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:
mhugent 2007-01-15 08:29:34 +00:00
parent 7903a9dbe7
commit 1d8725d6d7
9 changed files with 860 additions and 9 deletions

View File

@ -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;
}

View File

@ -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)

View File

@ -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

View File

@ -21,7 +21,6 @@
#include "qgssinglesymbolrenderer.h"
#include "qgsvectorlayer.h"
#include "qgswfsplugin.h"
#include "qgswfsprovider.h"
#include "mIconAddWfsLayer.xpm"

View File

@ -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)

View File

@ -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
)

View 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;
}

View 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

View File

@ -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)