mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
Use the expat parser for the WFS provider because of smaller memory needs and possibility to incrementally parse document
git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@6434 c8812cc2-4d05-0410-92ff-de0c093fc19c
This commit is contained in:
parent
7903a9dbe7
commit
1d8725d6d7
@ -394,7 +394,7 @@ QgsDataProvider* QgsProviderRegistry::getProvider( QString const & providerKey,
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsLogger::warning( "Failed to load ../providers/libproviders.so" );
|
||||
QgsLogger::warning( "Failed to load " + lib );
|
||||
delete myLib;
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
|
||||
SUBDIRS (copyright_label delimited_text grid_maker navigation north_arrow scale_bar wfs)
|
||||
SUBDIRS (copyright_label delimited_text grid_maker navigation north_arrow scale_bar)
|
||||
|
||||
IF (POSTGRES_FOUND)
|
||||
SUBDIRS (geoprocessing spit)
|
||||
ENDIF (POSTGRES_FOUND)
|
||||
|
||||
IF (EXPAT_FOUND)
|
||||
SUBDIRS (gps_importer)
|
||||
SUBDIRS (gps_importer wfs)
|
||||
ENDIF (EXPAT_FOUND)
|
||||
|
||||
IF (GSL_FOUND)
|
||||
|
@ -35,7 +35,6 @@ INCLUDE_DIRECTORIES(
|
||||
../../core ../../core/raster ../../core/renderer ../../core/symbology
|
||||
../../gui
|
||||
..
|
||||
../../providers/wfs
|
||||
../../app # for QgsNewHttpConnection
|
||||
${GEOS_INCLUDE_DIR}
|
||||
${GEOS_INCLUDE_DIR}/geos
|
||||
|
@ -21,7 +21,6 @@
|
||||
#include "qgssinglesymbolrenderer.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgswfsplugin.h"
|
||||
#include "qgswfsprovider.h"
|
||||
|
||||
#include "mIconAddWfsLayer.xpm"
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
|
||||
SUBDIRS (ogr wms delimitedtext wfs)
|
||||
SUBDIRS (ogr wms delimitedtext)
|
||||
|
||||
IF (POSTGRES_FOUND)
|
||||
SUBDIRS (postgres)
|
||||
ENDIF (POSTGRES_FOUND)
|
||||
|
||||
IF (EXPAT_FOUND)
|
||||
SUBDIRS (gpx)
|
||||
SUBDIRS (gpx wfs)
|
||||
ENDIF (EXPAT_FOUND)
|
||||
|
||||
IF (GRASS_FOUND)
|
||||
|
@ -2,8 +2,11 @@
|
||||
########################################################
|
||||
# Files
|
||||
|
||||
SET(WFS_SRCS qgswfsprovider.cpp)
|
||||
SET(WFS_SRCS qgswfsprovider.cpp qgswfsdata.cpp)
|
||||
|
||||
SET (WFS_MOC_HDRS
|
||||
qgswfsdata.h
|
||||
)
|
||||
|
||||
########################################################
|
||||
# Build
|
||||
@ -14,12 +17,15 @@ INCLUDE_DIRECTORIES (
|
||||
../../core
|
||||
${GEOS_INCLUDE_DIR}
|
||||
${GEOS_INCLUDE_DIR}/geos
|
||||
${EXPAT_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
ADD_LIBRARY (wfsprovider MODULE ${WFS_SRCS})
|
||||
ADD_LIBRARY (wfsprovider MODULE ${WFS_SRCS} ${WFS_MOC_SRCS})
|
||||
|
||||
TARGET_LINK_LIBRARIES (wfsprovider
|
||||
${QT_LIBRARIES}
|
||||
${GEOS_LIBRARY}
|
||||
${EXPAT_LIBRARY}
|
||||
qgis_core
|
||||
)
|
||||
|
||||
|
677
src/providers/wfs/qgswfsdata.cpp
Normal file
677
src/providers/wfs/qgswfsdata.cpp
Normal file
@ -0,0 +1,677 @@
|
||||
#include "qgswfsdata.h"
|
||||
#include "qgsfeatureattribute.h"
|
||||
#include "qgsrect.h"
|
||||
#include "qgsspatialrefsys.h"
|
||||
#include <QBuffer>
|
||||
#include <QUrl>
|
||||
|
||||
//just for a test
|
||||
//#include <QProgressDialog>
|
||||
|
||||
const char NS_SEPARATOR = '?';
|
||||
const QString GML_NAMESPACE = "http://www.opengis.net/gml";
|
||||
|
||||
QgsWFSData::QgsWFSData(const QString& uri, QgsRect* extent, QgsSpatialRefSys* srs, std::list<QgsFeature*>* features, const QString& geometryAttribute, const std::set<QString>& thematicAttributes, QGis::WKBTYPE* wkbType): QObject(), mUri(uri), mExtent(extent), mSrs(srs), mFeatures(features), mGeometryAttribute(geometryAttribute), mThematicAttributes(thematicAttributes), mWkbType(wkbType), mFinished(false), mFeatureCount(0)
|
||||
{
|
||||
//qWarning("Name of the geometry attribute is:");
|
||||
//qWarning(mGeometryAttribute.toLocal8Bit().data());
|
||||
|
||||
//find out mTypeName from uri
|
||||
QStringList arguments = uri.split("&");
|
||||
QStringList::const_iterator it;
|
||||
for(it = arguments.constBegin(); it != arguments.constEnd(); ++it)
|
||||
{
|
||||
if(it->startsWith("TYPENAME", Qt::CaseInsensitive))
|
||||
{
|
||||
mTypeName = it->section("=", 1, 1);
|
||||
qWarning("mTypeName is:");
|
||||
qWarning(mTypeName.toLocal8Bit().data());
|
||||
}
|
||||
}
|
||||
|
||||
mEndian = QgsApplication::endian();
|
||||
QObject::connect(&mHttp, SIGNAL(done(bool)), this, SLOT(setFinished(bool)));
|
||||
}
|
||||
|
||||
QgsWFSData::~QgsWFSData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QgsWFSData::QgsWFSData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int QgsWFSData::getWFSData()
|
||||
{
|
||||
XML_Parser p = XML_ParserCreateNS(NULL, NS_SEPARATOR);
|
||||
XML_SetUserData(p, this);
|
||||
XML_SetElementHandler(p, QgsWFSData::start, QgsWFSData::end);
|
||||
XML_SetCharacterDataHandler(p, QgsWFSData::chars);
|
||||
|
||||
|
||||
//separate host from query string
|
||||
QUrl requestUrl(mUri);
|
||||
mHttp.setHost(requestUrl.host());
|
||||
mHttp.get(mUri);
|
||||
|
||||
//just for a test
|
||||
//QProgressDialog pd;
|
||||
//pd.setWindowModality(Qt::WindowModal);
|
||||
//pd.setMaximum(150000000); //shows busy indocator
|
||||
//QObject::connect(&mHttp, SIGNAL(dataReadProgress(int,int)), &pd, SLOT(setValue(int)));
|
||||
//pd.show();
|
||||
|
||||
//loop to read the data
|
||||
QByteArray readData;
|
||||
int atEnd = 0;
|
||||
qWarning("Entering loop");
|
||||
while(!mFinished || mHttp.bytesAvailable() > 0)
|
||||
{
|
||||
if(mFinished)
|
||||
{
|
||||
atEnd = 1;
|
||||
}
|
||||
if(mHttp.bytesAvailable() != 0)
|
||||
{
|
||||
readData = mHttp.readAll();
|
||||
XML_Parse(p, readData.data(), readData.size(), atEnd);
|
||||
}
|
||||
qApp->processEvents();
|
||||
}
|
||||
qWarning("Left loop");
|
||||
return 0; //soon
|
||||
}
|
||||
|
||||
void QgsWFSData::setFinished(bool error)
|
||||
{
|
||||
if(error)
|
||||
{
|
||||
//qWarning("Finished with error");
|
||||
//qWarning(mHttp.errorString().toLocal8Bit().data());
|
||||
}
|
||||
else
|
||||
{
|
||||
//qWarning("Finished without error");
|
||||
}
|
||||
mFinished = true;
|
||||
}
|
||||
|
||||
void QgsWFSData::startElement(const XML_Char* el, const XML_Char** attr)
|
||||
{
|
||||
QString elementName(el);
|
||||
QString localName = elementName.section(NS_SEPARATOR, 1, 1);
|
||||
if(elementName == GML_NAMESPACE + NS_SEPARATOR + "coordinates")
|
||||
{
|
||||
mParseModeStack.push(QgsWFSData::coordinate);
|
||||
mStringCash.clear();
|
||||
}
|
||||
else if(localName == mGeometryAttribute)
|
||||
{
|
||||
mParseModeStack.push(QgsWFSData::geometry);
|
||||
}
|
||||
else if(mParseModeStack.size() == 0 && elementName == GML_NAMESPACE + NS_SEPARATOR + "boundedBy")
|
||||
{
|
||||
mParseModeStack.push(QgsWFSData::boundingBox);
|
||||
}
|
||||
else if(elementName == GML_NAMESPACE + NS_SEPARATOR + "featureMember")
|
||||
{
|
||||
mCurrentFeature = new QgsFeature(mFeatureCount);
|
||||
++mFeatureCount;
|
||||
mAttributeIndex = 0;
|
||||
mParseModeStack.push(QgsWFSData::featureMember);
|
||||
}
|
||||
else if(elementName == GML_NAMESPACE + NS_SEPARATOR + "Box" && mParseModeStack.top() == QgsWFSData::boundingBox)
|
||||
{
|
||||
//read attribute srsName="EPSG:26910"
|
||||
int epsgNr;
|
||||
if(readEpsgFromAttribute(epsgNr, attr) != 0)
|
||||
{
|
||||
qWarning("error, could not get epsg id");
|
||||
}
|
||||
//qWarning(("epsg id is: " + QString::number(epsgNr)).toLocal8Bit().data());
|
||||
if(mSrs)
|
||||
{
|
||||
if(!mSrs->createFromEpsg(epsgNr))
|
||||
{
|
||||
qWarning("Creation of srs from epsg failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(elementName == GML_NAMESPACE + NS_SEPARATOR + "Polygon")
|
||||
{
|
||||
std::list<unsigned char*> wkbList;
|
||||
std::list<int> wkbSizeList;
|
||||
mCurrentWKBFragments.push_back(wkbList);
|
||||
mCurrentWKBFragmentSizes.push_back(wkbSizeList);
|
||||
}
|
||||
else if(elementName == GML_NAMESPACE + NS_SEPARATOR + "MultiPoint")
|
||||
{
|
||||
mParseModeStack.push(QgsWFSData::multiPoint);
|
||||
//we need one nested list for intermediate WKB
|
||||
std::list<unsigned char*> wkbList;
|
||||
std::list<int> wkbSizeList;
|
||||
mCurrentWKBFragments.push_back(wkbList);
|
||||
mCurrentWKBFragmentSizes.push_back(wkbSizeList);
|
||||
}
|
||||
else if(elementName == GML_NAMESPACE + NS_SEPARATOR + "MultiLineString")
|
||||
{
|
||||
mParseModeStack.push(QgsWFSData::multiLine);
|
||||
//we need one nested list for intermediate WKB
|
||||
std::list<unsigned char*> wkbList;
|
||||
std::list<int> wkbSizeList;
|
||||
mCurrentWKBFragments.push_back(wkbList);
|
||||
mCurrentWKBFragmentSizes.push_back(wkbSizeList);
|
||||
}
|
||||
else if(elementName == GML_NAMESPACE + NS_SEPARATOR + "MultiPolygon")
|
||||
{
|
||||
mParseModeStack.push(QgsWFSData::multiPolygon);
|
||||
}
|
||||
|
||||
else if(mParseModeStack.size() == 1 && mParseModeStack.top() == QgsWFSData::featureMember && mThematicAttributes.find(localName) != mThematicAttributes.end())
|
||||
{
|
||||
mParseModeStack.push(QgsWFSData::attribute);
|
||||
mAttributeName = localName;
|
||||
mStringCash.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void QgsWFSData::endElement(const XML_Char* el)
|
||||
{
|
||||
QString elementName(el);
|
||||
QString localName = elementName.section(NS_SEPARATOR, 1, 1);
|
||||
if(elementName == GML_NAMESPACE + NS_SEPARATOR + "coordinates")
|
||||
{
|
||||
//qWarning("Poping coordinate from the stack");
|
||||
mParseModeStack.pop();
|
||||
}
|
||||
else if(localName == mAttributeName)
|
||||
{
|
||||
mParseModeStack.pop();
|
||||
//qWarning("adding attribute");
|
||||
//qWarning(mAttributeName.toLocal8Bit().data());
|
||||
//qWarning(mStringCash.toLocal8Bit().data());
|
||||
//mCurrentFeature->addAttribute(mAttributeName, mStringCash);
|
||||
mCurrentFeature->addAttribute(mAttributeIndex, QgsFeatureAttribute(mAttributeName, mStringCash));
|
||||
++mAttributeIndex;
|
||||
}
|
||||
else if(localName == mGeometryAttribute)
|
||||
{
|
||||
//qWarning("Poping geometry from the stack");
|
||||
mParseModeStack.pop();
|
||||
}
|
||||
else if(elementName == GML_NAMESPACE + NS_SEPARATOR + "boundedBy" && mParseModeStack.top() == QgsWFSData::boundingBox)
|
||||
{
|
||||
//create bounding box from mStringCash
|
||||
if(createBBoxFromCoordinateString(mExtent, mStringCash) != 0)
|
||||
{
|
||||
qWarning("creation of bounding box failed");
|
||||
}
|
||||
//qWarning("bounding box");
|
||||
//qWarning(mExtent->stringRep().toLocal8Bit().data());
|
||||
//qWarning("Removing boundingBox from stack");
|
||||
mParseModeStack.pop();
|
||||
}
|
||||
else if(elementName == GML_NAMESPACE + NS_SEPARATOR + "featureMember")
|
||||
{
|
||||
mCurrentFeature->setGeometryAndOwnership(mCurrentWKB, mCurrentWKBSize);
|
||||
mFeatures->push_back(mCurrentFeature);
|
||||
++mFeatureCount;
|
||||
//qWarning("Removing featureMember from stack");
|
||||
mParseModeStack.pop();
|
||||
}
|
||||
else if(elementName == GML_NAMESPACE + NS_SEPARATOR + "Point")
|
||||
{
|
||||
std::list<QgsPoint> pointList;
|
||||
if(pointsFromCoordinateString(pointList, mStringCash) != 0)
|
||||
{
|
||||
//error
|
||||
}
|
||||
|
||||
if(mParseModeStack.top() != QgsWFSData::multiPoint)
|
||||
{
|
||||
//directly add WKB point to the feature
|
||||
if(getPointWKB(&mCurrentWKB, &mCurrentWKBSize, *(pointList.begin())) != 0)
|
||||
{
|
||||
//error
|
||||
}
|
||||
*mWkbType = QGis::WKBPoint;
|
||||
}
|
||||
else //multipoint, add WKB as fragment
|
||||
{
|
||||
unsigned char* wkb = 0;
|
||||
int wkbSize = 0;
|
||||
std::list<unsigned char*> wkbList;
|
||||
std::list<int> wkbSizeList;
|
||||
if(getPointWKB(&wkb, &wkbSize, *(pointList.begin())) != 0)
|
||||
{
|
||||
//error
|
||||
}
|
||||
mCurrentWKBFragments.rbegin()->push_back(wkb);
|
||||
mCurrentWKBFragmentSizes.rbegin()->push_back(wkbSize);
|
||||
//wkbList.push_back(wkb);
|
||||
//wkbSizeList.push_back(wkbSize);
|
||||
//mCurrentWKBFragments.push_back(wkbList);
|
||||
//mCurrentWKBFragmentSizes.push_back(wkbSizeList);
|
||||
}
|
||||
}
|
||||
else if(elementName == GML_NAMESPACE + NS_SEPARATOR + "LineString")
|
||||
{
|
||||
//add WKB point to the feature
|
||||
|
||||
std::list<QgsPoint> pointList;
|
||||
if(pointsFromCoordinateString(pointList, mStringCash) != 0)
|
||||
{
|
||||
//error
|
||||
}
|
||||
if(mParseModeStack.top() != QgsWFSData::multiLine)
|
||||
{
|
||||
if(getLineWKB(&mCurrentWKB, &mCurrentWKBSize, pointList) != 0)
|
||||
{
|
||||
//error
|
||||
}
|
||||
*mWkbType = QGis::WKBLineString;
|
||||
}
|
||||
else //multiline, add WKB as fragment
|
||||
{
|
||||
unsigned char* wkb = 0;
|
||||
int wkbSize = 0;
|
||||
std::list<unsigned char*> wkbList;
|
||||
std::list<int> wkbSizeList;
|
||||
if(getLineWKB(&wkb, &wkbSize, pointList) != 0)
|
||||
{
|
||||
//error
|
||||
}
|
||||
mCurrentWKBFragments.rbegin()->push_back(wkb);
|
||||
mCurrentWKBFragmentSizes.rbegin()->push_back(wkbSize);
|
||||
//wkbList.push_back(wkb);
|
||||
//wkbSizeList.push_back(wkbSize);
|
||||
//mCurrentWKBFragments.push_back(wkbList);
|
||||
//mCurrentWKBFragmentSizes.push_back(wkbSizeList);
|
||||
}
|
||||
}
|
||||
else if(elementName == GML_NAMESPACE + NS_SEPARATOR + "LinearRing")
|
||||
{
|
||||
std::list<QgsPoint> pointList;
|
||||
if(pointsFromCoordinateString(pointList, mStringCash) != 0)
|
||||
{
|
||||
//error
|
||||
}
|
||||
unsigned char* wkb;
|
||||
int wkbSize;
|
||||
if(getRingWKB(&wkb, &wkbSize, pointList) != 0)
|
||||
{
|
||||
//error
|
||||
}
|
||||
mCurrentWKBFragments.rbegin()->push_back(wkb);
|
||||
mCurrentWKBFragmentSizes.rbegin()->push_back(wkbSize);
|
||||
}
|
||||
else if(elementName == GML_NAMESPACE + NS_SEPARATOR + "Polygon")
|
||||
{
|
||||
*mWkbType = QGis::WKBPolygon;
|
||||
if(mParseModeStack.top() != QgsWFSData::multiPolygon)
|
||||
{
|
||||
createPolygonFromFragments();
|
||||
}
|
||||
}
|
||||
else if(elementName == GML_NAMESPACE + NS_SEPARATOR + "MultiPoint")
|
||||
{
|
||||
mParseModeStack.pop();
|
||||
createMultiPointFromFragments();
|
||||
}
|
||||
else if(elementName == GML_NAMESPACE + NS_SEPARATOR + "MultiLineString")
|
||||
{
|
||||
mParseModeStack.pop();
|
||||
createMultiLineFromFragments();
|
||||
}
|
||||
else if(elementName == GML_NAMESPACE + NS_SEPARATOR + "MultiPolygon")
|
||||
{
|
||||
mParseModeStack.pop();
|
||||
createMultiPolygonFromFragments();
|
||||
}
|
||||
}
|
||||
|
||||
void QgsWFSData::characters(const XML_Char* chars, int len)
|
||||
{
|
||||
//save chars in mStringCash attribute mode or coordinate mode
|
||||
if(mParseModeStack.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QgsWFSData::parseMode theParseMode = mParseModeStack.top();
|
||||
if(theParseMode == QgsWFSData::attribute || theParseMode == QgsWFSData::coordinate)
|
||||
{
|
||||
//mStringCash = QString::fromUtf8(chars, len);
|
||||
mStringCash.append(QString::fromUtf8(chars, len));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int QgsWFSData::readEpsgFromAttribute(int& epsgNr, const XML_Char** attr) const
|
||||
{
|
||||
int i = 0;
|
||||
while(attr[i] != NULL)
|
||||
{
|
||||
if(strcmp(attr[i], "srsName") == 0)
|
||||
{
|
||||
QString epsgString(attr[i+1]);
|
||||
QString epsgNrString = epsgString.section(":", 1, 1);
|
||||
bool conversionOk;
|
||||
int eNr = epsgNrString.toInt(&conversionOk);
|
||||
if(!conversionOk)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
epsgNr = eNr;
|
||||
return 0;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
int QgsWFSData::createBBoxFromCoordinateString(QgsRect* bb, const QString& coordString) const
|
||||
{
|
||||
if(!bb)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::list<QgsPoint> points;
|
||||
//qWarning("string is: ");
|
||||
//qWarning(coordString.toLocal8Bit().data());
|
||||
if(pointsFromCoordinateString(points, coordString) != 0)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
if(points.size() < 2)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
std::list<QgsPoint>::const_iterator firstPointIt = points.begin();
|
||||
std::list<QgsPoint>::const_iterator secondPointIt = points.begin();
|
||||
++secondPointIt;
|
||||
bb->set(*firstPointIt, *secondPointIt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QgsWFSData::pointsFromCoordinateString(std::list<QgsPoint>& points, const QString& coordString) const
|
||||
{
|
||||
//tuples are separated by space, x/y by ','
|
||||
QStringList tuples = coordString.split(" ", QString::SkipEmptyParts);
|
||||
QStringList tuples_coordinates;
|
||||
double x, y;
|
||||
bool conversionSuccess;
|
||||
|
||||
QStringList::const_iterator tupleIterator;
|
||||
for(tupleIterator = tuples.constBegin(); tupleIterator != tuples.constEnd(); ++tupleIterator)
|
||||
{
|
||||
tuples_coordinates = tupleIterator->split(",", QString::SkipEmptyParts);
|
||||
if(tuples_coordinates.size() < 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
x = tuples_coordinates.at(0).toDouble(&conversionSuccess);
|
||||
if(!conversionSuccess)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
y = tuples_coordinates.at(1).toDouble(&conversionSuccess);
|
||||
if(!conversionSuccess)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//qWarning("adding point");
|
||||
//qWarning(QString::number(x).toLocal8Bit().data());
|
||||
//qWarning(QString::number(y).toLocal8Bit().data());
|
||||
points.push_back(QgsPoint(x, y));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QgsWFSData::getPointWKB(unsigned char** wkb, int* size, const QgsPoint& point) const
|
||||
{
|
||||
int wkbSize = 1 + sizeof(int) + 2 * sizeof(double);
|
||||
*size = wkbSize;
|
||||
*wkb = new unsigned char[wkbSize];
|
||||
QGis::WKBTYPE type = QGis::WKBPoint;
|
||||
double x = point.x();
|
||||
double y = point.y();
|
||||
int wkbPosition = 0; //current offset from wkb beginning (in bytes)
|
||||
|
||||
memcpy(&(*wkb)[wkbPosition], &mEndian, 1);
|
||||
wkbPosition += 1;
|
||||
memcpy(&(*wkb)[wkbPosition], &type, sizeof(int));
|
||||
wkbPosition += sizeof(int);
|
||||
memcpy(&(*wkb)[wkbPosition], &x, sizeof(double));
|
||||
wkbPosition += sizeof(double);
|
||||
memcpy(&(*wkb)[wkbPosition], &y, sizeof(double));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QgsWFSData::getLineWKB(unsigned char** wkb, int* size, const std::list<QgsPoint>& lineCoordinates) const
|
||||
{
|
||||
int wkbSize = 1 + 2 * sizeof(int) + lineCoordinates.size() * 2* sizeof(double);
|
||||
*size = wkbSize;
|
||||
*wkb = new unsigned char[wkbSize];
|
||||
QGis::WKBTYPE type = QGis::WKBLineString;
|
||||
int wkbPosition = 0; //current offset from wkb beginning (in bytes)
|
||||
double x, y;
|
||||
int nPoints = lineCoordinates.size();
|
||||
|
||||
//fill the contents into *wkb
|
||||
memcpy(&(*wkb)[wkbPosition], &mEndian, 1);
|
||||
wkbPosition += 1;
|
||||
memcpy(&(*wkb)[wkbPosition], &type, sizeof(int));
|
||||
wkbPosition += sizeof(int);
|
||||
memcpy(&(*wkb)[wkbPosition], &nPoints, sizeof(int));
|
||||
wkbPosition += sizeof(int);
|
||||
|
||||
std::list<QgsPoint>::const_iterator iter;
|
||||
for(iter = lineCoordinates.begin(); iter != lineCoordinates.end(); ++iter)
|
||||
{
|
||||
x = iter->x();
|
||||
y = iter->y();
|
||||
memcpy(&(*wkb)[wkbPosition], &x, sizeof(double));
|
||||
wkbPosition += sizeof(double);
|
||||
memcpy(&(*wkb)[wkbPosition], &y, sizeof(double));
|
||||
wkbPosition += sizeof(double);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QgsWFSData::getRingWKB(unsigned char** wkb, int* size, const std::list<QgsPoint>& ringCoordinates) const
|
||||
{
|
||||
int wkbSize = sizeof(int) + ringCoordinates.size() * 2* sizeof(double);
|
||||
*size = wkbSize;
|
||||
*wkb = new unsigned char[wkbSize];
|
||||
int wkbPosition = 0; //current offset from wkb beginning (in bytes)
|
||||
double x, y;
|
||||
int nPoints = ringCoordinates.size();
|
||||
memcpy(&(*wkb)[wkbPosition], &nPoints, sizeof(int));
|
||||
wkbPosition += sizeof(int);
|
||||
|
||||
std::list<QgsPoint>::const_iterator iter;
|
||||
for(iter = ringCoordinates.begin(); iter != ringCoordinates.end(); ++iter)
|
||||
{
|
||||
x = iter->x();
|
||||
y = iter->y();
|
||||
memcpy(&(*wkb)[wkbPosition], &x, sizeof(double));
|
||||
wkbPosition += sizeof(double);
|
||||
memcpy(&(*wkb)[wkbPosition], &y, sizeof(double));
|
||||
wkbPosition += sizeof(double);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QgsWFSData::createMultiLineFromFragments()
|
||||
{
|
||||
mCurrentWKBSize = 0;
|
||||
mCurrentWKBSize += 1 + 2*sizeof(int);
|
||||
mCurrentWKBSize += totalWKBFragmentSize();
|
||||
|
||||
mCurrentWKB = new unsigned char[mCurrentWKBSize];
|
||||
int pos = 0;
|
||||
QGis::WKBTYPE type = QGis::WKBMultiLineString;
|
||||
int numLines = mCurrentWKBFragments.begin()->size();
|
||||
//add endian
|
||||
memcpy(&(mCurrentWKB[pos]), &mEndian, 1);
|
||||
pos += 1;
|
||||
memcpy(&(mCurrentWKB[pos]), &type, sizeof(int));
|
||||
pos += sizeof(int);
|
||||
memcpy(&(mCurrentWKB[pos]), &numLines, sizeof(int));
|
||||
pos += sizeof(int);
|
||||
std::list<unsigned char*>::iterator wkbIt = mCurrentWKBFragments.begin()->begin();
|
||||
std::list<int>::iterator sizeIt = mCurrentWKBFragmentSizes.begin()->begin();
|
||||
|
||||
//copy (and delete) all the wkb fragments
|
||||
for(; wkbIt != mCurrentWKBFragments.begin()->end(); ++wkbIt, ++sizeIt)
|
||||
{
|
||||
memcpy(&(mCurrentWKB[pos]), *wkbIt, *sizeIt);
|
||||
pos += *sizeIt;
|
||||
delete[] *wkbIt;
|
||||
}
|
||||
|
||||
mCurrentWKBFragments.clear();
|
||||
mCurrentWKBFragmentSizes.clear();
|
||||
*mWkbType = QGis::WKBMultiLineString;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QgsWFSData::createMultiPointFromFragments()
|
||||
{
|
||||
mCurrentWKBSize = 0;
|
||||
mCurrentWKBSize += 1 + 2*sizeof(int);
|
||||
mCurrentWKBSize += totalWKBFragmentSize();
|
||||
|
||||
int pos = 0;
|
||||
QGis::WKBTYPE type = QGis::WKBMultiPoint;
|
||||
int numPoints = mCurrentWKBFragments.begin()->size();
|
||||
|
||||
memcpy(&(mCurrentWKB[pos]), &mEndian, 1);
|
||||
pos += 1;
|
||||
memcpy(&(mCurrentWKB[pos]), &type, sizeof(int));
|
||||
pos += sizeof(int);
|
||||
memcpy(&(mCurrentWKB[pos]), &numPoints, sizeof(int));
|
||||
pos += sizeof(int);
|
||||
|
||||
std::list<unsigned char*>::iterator wkbIt = mCurrentWKBFragments.begin()->begin();
|
||||
std::list<int>::iterator sizeIt = mCurrentWKBFragmentSizes.begin()->begin();
|
||||
|
||||
for(; wkbIt != mCurrentWKBFragments.begin()->end(); ++wkbIt, ++sizeIt)
|
||||
{
|
||||
memcpy(&(mCurrentWKB[pos]), *wkbIt, *sizeIt);
|
||||
pos += *sizeIt;
|
||||
delete[] *wkbIt;
|
||||
}
|
||||
|
||||
mCurrentWKBFragments.clear();
|
||||
mCurrentWKBFragmentSizes.clear();
|
||||
*mWkbType = QGis::WKBMultiPoint;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int QgsWFSData::createPolygonFromFragments()
|
||||
{
|
||||
mCurrentWKBSize = 0;
|
||||
mCurrentWKBSize += 1 + 2*sizeof(int);
|
||||
mCurrentWKBSize += totalWKBFragmentSize();
|
||||
|
||||
mCurrentWKB = new unsigned char[mCurrentWKBSize];
|
||||
int pos = 0;
|
||||
QGis::WKBTYPE type = QGis::WKBPolygon;
|
||||
int numRings = mCurrentWKBFragments.begin()->size();
|
||||
memcpy(&(mCurrentWKB[pos]), &mEndian, 1);
|
||||
pos += 1;
|
||||
memcpy(&(mCurrentWKB[pos]), &type, sizeof(int));
|
||||
pos += sizeof(int);
|
||||
memcpy(&(mCurrentWKB[pos]), &numRings, sizeof(int));
|
||||
pos += sizeof(int);
|
||||
|
||||
std::list<unsigned char*>::iterator wkbIt = mCurrentWKBFragments.begin()->begin();
|
||||
std::list<int>::iterator sizeIt = mCurrentWKBFragmentSizes.begin()->begin();
|
||||
for(;wkbIt != mCurrentWKBFragments.begin()->end(); ++wkbIt, ++sizeIt)
|
||||
{
|
||||
memcpy(&(mCurrentWKB[pos]), *wkbIt, *sizeIt);
|
||||
pos += *sizeIt;
|
||||
delete[] *wkbIt;
|
||||
}
|
||||
|
||||
mCurrentWKBFragments.clear();
|
||||
mCurrentWKBFragmentSizes.clear();
|
||||
*mWkbType = QGis::WKBPolygon;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QgsWFSData::createMultiPolygonFromFragments()
|
||||
{
|
||||
mCurrentWKBSize = 0;
|
||||
mCurrentWKBSize += 1 + 2*sizeof(int);
|
||||
mCurrentWKBSize += totalWKBFragmentSize();
|
||||
mCurrentWKBSize += mCurrentWKBFragments.size() * (1 + 2 * sizeof(int)); //fragments are just the rings
|
||||
|
||||
mCurrentWKB = new unsigned char[mCurrentWKBSize];
|
||||
int pos = 0;
|
||||
QGis::WKBTYPE type = QGis::WKBMultiPolygon;
|
||||
QGis::WKBTYPE polygonType = QGis::WKBPolygon;
|
||||
int numPolys = mCurrentWKBFragments.size();
|
||||
int numRings;
|
||||
memcpy(&(mCurrentWKB[pos]), &mEndian, 1);
|
||||
pos += 1;
|
||||
memcpy(&(mCurrentWKB[pos]), &type, sizeof(int));
|
||||
pos += sizeof(int);
|
||||
memcpy(&(mCurrentWKB[pos]), &numPolys, sizeof(int));
|
||||
pos += sizeof(int);
|
||||
|
||||
//have outer and inner iterators
|
||||
std::list<std::list<unsigned char*> >::iterator outerWkbIt;
|
||||
std::list<std::list<int> >::iterator outerSizeIt;
|
||||
std::list<unsigned char*>::iterator innerWkbIt;
|
||||
std::list<int>::iterator innerSizeIt;
|
||||
|
||||
outerWkbIt = mCurrentWKBFragments.begin();
|
||||
outerSizeIt = mCurrentWKBFragmentSizes.begin();
|
||||
|
||||
for(; outerWkbIt != mCurrentWKBFragments.end(); ++outerWkbIt, ++outerSizeIt)
|
||||
{
|
||||
//new polygon
|
||||
memcpy(&(mCurrentWKB[pos]), &mEndian, 1);
|
||||
pos += 1;
|
||||
memcpy(&(mCurrentWKB[pos]), &polygonType, sizeof(int));
|
||||
pos += sizeof(int);
|
||||
numRings = outerWkbIt->size();
|
||||
memcpy(&(mCurrentWKB[pos]), &numRings, sizeof(int));
|
||||
pos += sizeof(int);
|
||||
|
||||
innerWkbIt = outerWkbIt->begin();
|
||||
innerSizeIt = outerSizeIt->begin();
|
||||
for(; innerWkbIt != outerWkbIt->end(); ++innerWkbIt, ++innerSizeIt)
|
||||
{
|
||||
memcpy(&(mCurrentWKB[pos]), *innerWkbIt, *innerSizeIt);
|
||||
pos += *innerSizeIt;
|
||||
delete[] *innerWkbIt;
|
||||
}
|
||||
}
|
||||
|
||||
mCurrentWKBFragments.clear();
|
||||
mCurrentWKBFragmentSizes.clear();
|
||||
*mWkbType = QGis::WKBMultiPolygon;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QgsWFSData::totalWKBFragmentSize() const
|
||||
{
|
||||
int result=0;
|
||||
for(std::list<std::list<int> >::const_iterator it = mCurrentWKBFragmentSizes.begin(); it != mCurrentWKBFragmentSizes.end(); ++it)
|
||||
{
|
||||
for(std::list<int>::const_iterator iter = it->begin(); iter != it->end(); ++iter)
|
||||
{
|
||||
result += *iter;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
133
src/providers/wfs/qgswfsdata.h
Normal file
133
src/providers/wfs/qgswfsdata.h
Normal file
@ -0,0 +1,133 @@
|
||||
#ifndef QGSWFSDATA_H
|
||||
#define QGSWFSDATA_H
|
||||
|
||||
#include <QHttp>
|
||||
#include <expat.h>
|
||||
#include "qgis.h"
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsdataprovider.h"
|
||||
#include "qgsfeature.h"
|
||||
#include "qgspoint.h"
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
class QgsRect;
|
||||
class QgsSpatialRefSys;
|
||||
|
||||
|
||||
/**This class reads data from a WFS server or alternatively from a GML file. It uses the expat XML parser and an event based model to keep performance high. The parsing starts when the first data arrives, it does not wait until the request is finished*/
|
||||
class QgsWFSData: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QgsWFSData(const QString& uri, QgsRect* extent, QgsSpatialRefSys* srs, std::list<QgsFeature*>* features, const QString& geometryAttribute, const std::set<QString>& thematicAttributes, QGis::WKBTYPE* wkbType);
|
||||
~QgsWFSData();
|
||||
|
||||
/**Does the Http GET request to the wfs server
|
||||
@param query string (to define the requested typename)
|
||||
@param extent the extent of the WFS layer
|
||||
@param srs the reference system of the layer
|
||||
@param features the features of the layer
|
||||
@return 0 in case of success*/
|
||||
int getWFSData();
|
||||
|
||||
private slots:
|
||||
void setFinished(bool error);
|
||||
|
||||
private:
|
||||
|
||||
enum parseMode
|
||||
{
|
||||
boundingBox,
|
||||
featureMember,
|
||||
attribute,
|
||||
geometry,
|
||||
coordinate,
|
||||
point,
|
||||
line,
|
||||
polygon,
|
||||
multiPoint,
|
||||
multiLine,
|
||||
multiPolygon
|
||||
};
|
||||
|
||||
QgsWFSData();
|
||||
|
||||
/**XML handler methods*/
|
||||
void startElement(const XML_Char* el, const XML_Char** attr);
|
||||
void endElement(const XML_Char* el);
|
||||
void characters(const XML_Char* chars, int len);
|
||||
static void start(void* data, const XML_Char* el, const XML_Char** attr)
|
||||
{
|
||||
static_cast<QgsWFSData*>(data)->startElement(el, attr);
|
||||
}
|
||||
static void end(void* data, const XML_Char* el)
|
||||
{
|
||||
static_cast<QgsWFSData*>(data)->endElement(el);
|
||||
}
|
||||
static void chars(void* data, const XML_Char* chars, int len)
|
||||
{
|
||||
static_cast<QgsWFSData*>(data)->characters(chars, len);
|
||||
}
|
||||
|
||||
//helper routines
|
||||
/**Reads attribute srsName="EPSG:..."
|
||||
@param epsgNr result
|
||||
@param attr attribute strings
|
||||
@return 0 in case of success*/
|
||||
int readEpsgFromAttribute(int& epsgNr, const XML_Char** attr) const;
|
||||
/**Creates a rectangle from a coordinate string.
|
||||
@return 0 in case of success*/
|
||||
int createBBoxFromCoordinateString(QgsRect* bb, const QString& coordString) const;
|
||||
/**Creates a set of points from a coordinate string.
|
||||
@return 0 in case of success*/
|
||||
int pointsFromCoordinateString(std::list<QgsPoint>& points, const QString& coordString) const;
|
||||
int getPointWKB(unsigned char** wkb, int* size, const QgsPoint&) const;
|
||||
int getLineWKB(unsigned char** wkb, int* size, const std::list<QgsPoint>& lineCoordinates) const;
|
||||
int getRingWKB(unsigned char** wkb, int* size, const std::list<QgsPoint>& ringCoordinates) const;
|
||||
/**Creates a multiline from the information in mCurrentWKBFragments and mCurrentWKBFragmentSizes. Assign the result. The multiline is in mCurrentWKB and mCurrentWKBSize. The function deletes the memory in mCurrentWKBFragments. Returns 0 in case of success.*/
|
||||
int createMultiLineFromFragments();
|
||||
int createMultiPointFromFragments();
|
||||
int createPolygonFromFragments();
|
||||
int createMultiPolygonFromFragments();
|
||||
/**Adds all the integers contained in mCurrentWKBFragmentSizes*/
|
||||
int totalWKBFragmentSize() const;
|
||||
|
||||
QString mUri;
|
||||
//results are members such that handler routines are able to manipulate them
|
||||
/**Bounding box of the layer*/
|
||||
QgsRect* mExtent;
|
||||
/**Source srs of the layer*/
|
||||
QgsSpatialRefSys* mSrs;
|
||||
/**The features of the layer*/
|
||||
std::list<QgsFeature*>* mFeatures;
|
||||
/**Name of geometry attribute*/
|
||||
QString mGeometryAttribute;
|
||||
std::set<QString> mThematicAttributes;
|
||||
QGis::WKBTYPE* mWkbType;
|
||||
/**True if the request is finished*/
|
||||
bool mFinished;
|
||||
/**The HTTP client object*/
|
||||
QHttp mHttp;
|
||||
/**Keep track about the most important nested elements*/
|
||||
std::stack<parseMode> mParseModeStack;
|
||||
/**This contains the character data if an important element has been encountered*/
|
||||
QString mStringCash;
|
||||
QgsFeature* mCurrentFeature;
|
||||
int mFeatureCount;
|
||||
/**The total WKB for a feature*/
|
||||
unsigned char* mCurrentWKB;
|
||||
/**The total WKB size for a feature*/
|
||||
int mCurrentWKBSize;
|
||||
/**WKB intermediate storage during parsing. For points and lines, no intermediate WKB is stored at all. For multipoins and multilines and polygons, only one nested list is used. For multipolygons, both nested lists are used*/
|
||||
std::list< std::list<unsigned char*> > mCurrentWKBFragments;
|
||||
/**Similar to mCurrentWKB, but only the size*/
|
||||
std::list< std::list<int> > mCurrentWKBFragmentSizes;
|
||||
QString mAttributeName;
|
||||
/**Index where the current attribute should be inserted*/
|
||||
int mAttributeIndex;
|
||||
QString mTypeName;
|
||||
QgsApplication::endian_t mEndian;
|
||||
};
|
||||
|
||||
#endif
|
@ -22,6 +22,7 @@
|
||||
#include "qgsgeometry.h"
|
||||
#include "qgshttptransaction.h"
|
||||
#include "qgsspatialrefsys.h"
|
||||
#include "qgswfsdata.h"
|
||||
#include "qgswfsprovider.h"
|
||||
#include "qgslogger.h"
|
||||
#include <QDomDocument>
|
||||
@ -306,6 +307,7 @@ int QgsWFSProvider::describeFeatureType(const QString& uri, QString& geometryAtt
|
||||
|
||||
int QgsWFSProvider::getFeatureGET(const QString& uri, const QString& geometryAttribute)
|
||||
{
|
||||
#if 0
|
||||
//assemble request string
|
||||
QString request = uri /*+ "&OUTPUTFORMAT=gml3"*/; //use gml2 as it is supported by most wfs servers
|
||||
QByteArray result;
|
||||
@ -334,6 +336,41 @@ int QgsWFSProvider::getFeatureGET(const QString& uri, const QString& geometryAtt
|
||||
}
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
//try the new and faster method with the expat parser
|
||||
std::list<QgsFeature*> dataFeatures;
|
||||
std::set<QString> thematicAttributes;
|
||||
for(QgsFieldMap::const_iterator it = mFields.begin(); it != mFields.end(); ++it)
|
||||
{
|
||||
thematicAttributes.insert(it->name());
|
||||
}
|
||||
|
||||
QgsWFSData dataReader(uri, &mExtent, mSourceSRS, &dataFeatures, geometryAttribute, thematicAttributes, &mWKBType);
|
||||
if(dataReader.getWFSData() != 0)
|
||||
{
|
||||
qWarning("getWFSData returned with error");
|
||||
return 1;
|
||||
}
|
||||
|
||||
qWarning("feature count after request is:");
|
||||
qWarning(QString::number(dataFeatures.size()).toLocal8Bit().data());
|
||||
qWarning("mExtent after request is:");
|
||||
qWarning(mExtent.stringRep().toLocal8Bit().data());
|
||||
|
||||
mFeatureCount = 0;
|
||||
|
||||
QgsRect featureBBox;
|
||||
GEOS_GEOM::Envelope* geosBBox;
|
||||
for(std::list<QgsFeature*>::const_iterator it = dataFeatures.begin(); it != dataFeatures.end(); ++it)
|
||||
{
|
||||
featureBBox = (*it)->boundingBox();
|
||||
geosBBox = new GEOS_GEOM::Envelope(featureBBox.xMin(), featureBBox.xMax(), featureBBox.yMin(), featureBBox.yMax());
|
||||
mSpatialIndex.insert(geosBBox, (void*)(*it));
|
||||
mEnvelopesAndFeatures.push_back(std::make_pair(geosBBox, (*it)));
|
||||
++mFeatureCount;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QgsWFSProvider::getFeaturePOST(const QString& uri, const QString& geometryAttribute)
|
||||
|
Loading…
x
Reference in New Issue
Block a user