OSM provider ported to new API

This commit is contained in:
Martin Dobias 2013-01-27 03:08:35 +01:00
parent b16c68ecd8
commit 600baca9a4
7 changed files with 608 additions and 642 deletions

View File

@ -323,7 +323,6 @@ class OsmDatabaseManager:
lay=self.pointLayers[self.currentKey]
lay.select([],area,True,True)
result=lay.nextFeature(feat)
lay.dataProvider().rewind()
if result:
return (feat,'Point')
@ -332,7 +331,6 @@ class OsmDatabaseManager:
lay=self.lineLayers[self.currentKey]
lay.select([],area,True,True)
result=lay.nextFeature(feat)
lay.dataProvider().rewind()
if result:
# line vertices
@ -341,7 +339,7 @@ class OsmDatabaseManager:
,{"minLat":area.yMinimum(),"maxLat":area.yMaximum(),"minLon":area.xMinimum(),"maxLon":area.xMaximum(),"lineId":str(feat.id())})
for rec in c:
feat2=QgsFeature(rec[0],"Point")
feat2=QgsFeature(rec[0])
feat2.setGeometry(QgsGeometry.fromPoint(QgsPoint(rec[2],rec[1])))
# without features' attributes here! we don't need them...
c.close()
@ -354,7 +352,6 @@ class OsmDatabaseManager:
lay=self.polygonLayers[self.currentKey]
lay.select([],area,True,True)
result=lay.nextFeature(feat)
lay.dataProvider().rewind()
if result:
# polygon vertices
@ -363,7 +360,7 @@ class OsmDatabaseManager:
,{"minLat":area.yMinimum(),"maxLat":area.yMaximum(),"minLon":area.xMinimum(),"maxLon":area.xMaximum(),"polygonId":str(feat.id())})
for rec in c:
feat2=QgsFeature(rec[0],"Point")
feat2=QgsFeature(rec[0])
feat2.setGeometry(QgsGeometry.fromPoint(QgsPoint(rec[2],rec[1])))
# without features' attributes here! we don't need them...
c.close()
@ -416,7 +413,7 @@ class OsmDatabaseManager:
,{"minLat":area.yMinimum(),"maxLat":area.yMaximum(),"minLon":area.xMinimum(),"maxLon":area.xMaximum(),"lineId":str(feat.id())})
for rec in c:
feat2=QgsFeature(rec[0],"Point")
feat2=QgsFeature(rec[0])
feat2.setGeometry(QgsGeometry.fromPoint(QgsPoint(rec[2],rec[1])))
# without features' attributes here! we don't need them...
featMap[feat2.id()]=feat2
@ -439,7 +436,7 @@ class OsmDatabaseManager:
,{"minLat":area.yMinimum(),"maxLat":area.yMaximum(),"minLon":area.xMinimum(),"maxLon":area.xMaximum(),"polygonId":str(feat.id())})
for rec in c:
feat2=QgsFeature(rec[0],"Point")
feat2=QgsFeature(rec[0])
feat2.setGeometry(QgsGeometry.fromPoint(QgsPoint(rec[2],rec[1])))
# without features' attributes here! we don't need them...
featMap[feat2.id()]=feat2
@ -471,7 +468,7 @@ class OsmDatabaseManager:
nodeId=self.__getFreeFeatureId()
affected=set()
feat=QgsFeature(nodeId,"Point")
feat=QgsFeature(nodeId)
feat.setGeometry(QgsGeometry.fromPoint(QgsPoint(mapPoint.x(),mapPoint.y())))
# should snapping be done? if not, everything's easy
@ -612,7 +609,7 @@ class OsmDatabaseManager:
# finishing...
c.close()
feat=QgsFeature(lineId,"Line")
feat=QgsFeature(lineId)
feat.setGeometry(QgsGeometry.fromPolyline(pline))
if doCommit:
@ -691,7 +688,7 @@ class OsmDatabaseManager:
# finish
c.close()
feat=QgsFeature(polygonId,"Polygon")
feat=QgsFeature(polygonId)
polygon=[]
polygon.append(pline)
feat.setGeometry(QgsGeometry.fromPolygon(polygon))

View File

@ -6,7 +6,7 @@ ADD_SUBDIRECTORY(memory)
ADD_SUBDIRECTORY(ogr)
ADD_SUBDIRECTORY(wms)
ADD_SUBDIRECTORY(delimitedtext)
#ADD_SUBDIRECTORY(osm) # TODO: enable when migrated to new api
ADD_SUBDIRECTORY(osm)
#ADD_SUBDIRECTORY(sqlanywhere) # TODO: enable when migrated to new api
ADD_SUBDIRECTORY(gdal)
#ADD_SUBDIRECTORY(mssql) # TODO: enable when migrated to new api

View File

@ -3,6 +3,7 @@
# Files
SET(OSM_SRCS
osmfeatureiterator.cpp
osmhandler.cpp
osmprovider.cpp
osmrenderer.cpp

View File

@ -0,0 +1,472 @@
#include "osmfeatureiterator.h"
#include "osmprovider.h"
#include "qgsapplication.h"
#include "qgsgeometry.h"
#include "qgslogger.h"
QgsOSMFeatureIterator::QgsOSMFeatureIterator( QgsOSMDataProvider* p, const QgsFeatureRequest& request )
: QgsAbstractFeatureIterator( request ), P( p )
, mRectGeom(0)
{
// make sure that only one iterator is active
if ( P->mActiveIterator )
P->mActiveIterator->close();
P->mActiveIterator = this;
bool hasFilterFid = mRequest.filterType() == QgsFeatureRequest::FilterFid;
bool hasFilterRect = mRequest.filterType() == QgsFeatureRequest::FilterRect;
QgsRectangle rect = mRequest.filterRect();
mRectGeom = QgsGeometry::fromRect( rect );
if ( P->mFeatureType == QgsOSMDataProvider::PointType )
{
const char* sqlSelectNode = "SELECT id, lat, lon, timestamp, user FROM node WHERE id=? AND usage=0 AND status<>'R' AND u=1";
const char* sqlSelectPoints = "SELECT id, lat, lon, timestamp, user FROM node WHERE usage=0 AND status<>'R' AND u=1";
const char* sqlSelectPointsIn = "SELECT id, lat, lon, timestamp, user FROM node WHERE usage=0 AND status<>'R' AND u=1 "
"AND lat>=? AND lat<=? AND lon>=? AND lon<=?";
const char* sql = hasFilterFid ? sqlSelectNode : (hasFilterRect ? sqlSelectPointsIn : sqlSelectPoints);
if ( sqlite3_prepare_v2( P->mDatabase, sql, -1, &mSelectStmt, 0 ) != SQLITE_OK )
{
QgsDebugMsg( "sqlite3 statement for points retrieval - prepare failed." );
return;
}
if ( hasFilterFid )
{
sqlite3_bind_int64( mSelectStmt, 1, mRequest.filterFid() );
}
else if ( hasFilterRect )
{
// binding variables (boundary) for points selection!
sqlite3_bind_double( mSelectStmt, 1, rect.yMinimum() );
sqlite3_bind_double( mSelectStmt, 2, rect.yMaximum() );
sqlite3_bind_double( mSelectStmt, 3, rect.xMinimum() );
sqlite3_bind_double( mSelectStmt, 4, rect.xMaximum() );
}
}
else if ( P->mFeatureType == QgsOSMDataProvider::LineType )
{
const char* sqlSelectWay = "SELECT id, wkb, timestamp, user FROM way WHERE id=? AND status<>'R' AND u=1";
const char* sqlSelectLines = "SELECT w.id, w.wkb, w.timestamp, w.user FROM way w WHERE w.closed=0 AND w.status<>'R' AND w.u=1";
const char* sqlSelectLinesIn = "SELECT w.id, w.wkb, w.timestamp, w.user FROM way w WHERE w.closed=0 AND w.status<>'R' AND w.u=1 "
"AND (((w.max_lat between ? AND ?) OR (w.min_lat between ? AND ?) OR (w.min_lat<? AND w.max_lat>?)) "
"AND ((w.max_lon between ? AND ?) OR (w.min_lon between ? AND ?) OR (w.min_lon<? AND w.max_lon>?)))";
const char* sql = hasFilterFid ? sqlSelectWay : ( hasFilterRect ? sqlSelectLinesIn : sqlSelectLines );
if ( sqlite3_prepare_v2( P->mDatabase, sql, -1, &mSelectStmt, 0 ) != SQLITE_OK )
{
QgsDebugMsg( "sqlite3 statement for lines retrieval - prepare failed." );
return;
}
if ( hasFilterFid )
{
sqlite3_bind_int64( mSelectStmt, 1, mRequest.filterFid() );
}
else if ( hasFilterRect )
{
// binding variables (boundary) for lines selection!
sqlite3_bind_double( mSelectStmt, 1, rect.yMinimum() );
sqlite3_bind_double( mSelectStmt, 2, rect.yMaximum() );
sqlite3_bind_double( mSelectStmt, 3, rect.yMinimum() );
sqlite3_bind_double( mSelectStmt, 4, rect.yMaximum() );
sqlite3_bind_double( mSelectStmt, 5, rect.yMinimum() );
sqlite3_bind_double( mSelectStmt, 6, rect.yMaximum() );
sqlite3_bind_double( mSelectStmt, 7, rect.xMinimum() );
sqlite3_bind_double( mSelectStmt, 8, rect.xMaximum() );
sqlite3_bind_double( mSelectStmt, 9, rect.xMinimum() );
sqlite3_bind_double( mSelectStmt, 10, rect.xMaximum() );
sqlite3_bind_double( mSelectStmt, 11, rect.xMinimum() );
sqlite3_bind_double( mSelectStmt, 12, rect.xMaximum() );
}
}
else // mFeatureType == PolygonType
{
const char* sqlSelectWay = "SELECT id, wkb, timestamp, user FROM way WHERE id=? AND status<>'R' AND u=1";
const char* sqlSelectPolys = "SELECT w.id, w.wkb, w.timestamp, w.user FROM way w WHERE w.closed=1 AND w.status<>'R' AND w.u=1";
const char* sqlSelectPolysIn = "SELECT w.id, w.wkb, w.timestamp, w.user FROM way w WHERE w.closed=1 AND w.status<>'R' AND w.u=1 "
"AND (((w.max_lat between ? AND ?) OR (w.min_lat between ? AND ?) OR (w.min_lat<? AND w.max_lat>?)) "
"AND ((w.max_lon between ? AND ?) OR (w.min_lon between ? AND ?) OR (w.min_lon<? AND w.max_lon>?)))";
const char* sql = hasFilterFid ? sqlSelectWay : ( hasFilterRect ? sqlSelectPolysIn : sqlSelectPolys );
if ( sqlite3_prepare_v2( P->mDatabase, sql, -1, &mSelectStmt, 0 ) != SQLITE_OK )
{
QgsDebugMsg( "sqlite3 statement for polygons retrieval - prepare failed." );
return;
}
if ( hasFilterFid )
{
sqlite3_bind_int64( mSelectStmt, 1, mRequest.filterFid() );
}
else if ( hasFilterRect )
{
// binding variables (boundary) for polygons selection!
sqlite3_bind_double( mSelectStmt, 1, rect.yMinimum() );
sqlite3_bind_double( mSelectStmt, 2, rect.yMaximum() );
sqlite3_bind_double( mSelectStmt, 3, rect.yMinimum() );
sqlite3_bind_double( mSelectStmt, 4, rect.yMaximum() );
sqlite3_bind_double( mSelectStmt, 5, rect.yMinimum() );
sqlite3_bind_double( mSelectStmt, 6, rect.yMaximum() );
sqlite3_bind_double( mSelectStmt, 7, rect.xMinimum() );
sqlite3_bind_double( mSelectStmt, 8, rect.xMaximum() );
sqlite3_bind_double( mSelectStmt, 9, rect.xMinimum() );
sqlite3_bind_double( mSelectStmt, 10, rect.xMaximum() );
sqlite3_bind_double( mSelectStmt, 11, rect.xMinimum() );
sqlite3_bind_double( mSelectStmt, 12, rect.xMaximum() );
}
}
// prepare statement for tag retrieval
const char* sqlSelectTags = "SELECT key, val FROM tag WHERE object_id=? AND object_type=?";
int rc = sqlite3_prepare_v2( P->mDatabase, sqlSelectTags, -1, &mTagsStmt, 0 );
if ( rc != SQLITE_OK )
{
QgsDebugMsg( "sqlite3 statement for feature tags selection - prepare failed." );
return;
}
const char* sqlSelectTagValue = "SELECT val FROM tag WHERE object_id=? AND object_type=? AND key=?";
rc = sqlite3_prepare_v2( P->mDatabase, sqlSelectTagValue, -1, &mCustomTagsStmt, 0 );
if ( rc != SQLITE_OK )
{
QgsDebugMsg( "sqlite3 statement for tag value selection - prepare failed." );
return;
}
}
QgsOSMFeatureIterator::~QgsOSMFeatureIterator()
{
close();
}
bool QgsOSMFeatureIterator::nextFeature( QgsFeature& feature )
{
if ( mClosed )
return false;
// load next requested feature from sqlite3 database
switch ( sqlite3_step( mSelectStmt ) )
{
case SQLITE_DONE: // no more features to return
feature.setValid( false );
close();
return false;
case SQLITE_ROW: // another feature to return
if ( P->mFeatureType == QgsOSMDataProvider::PointType )
return fetchNode( feature );
else if ( P->mFeatureType == QgsOSMDataProvider::LineType )
return fetchWay( feature );
else if ( P->mFeatureType == QgsOSMDataProvider::PolygonType )
return fetchWay( feature );
}
if ( P->mFeatureType == QgsOSMDataProvider::PointType )
{
QgsDebugMsg( "Getting next feature of type <point> failed." );
}
else if ( P->mFeatureType == QgsOSMDataProvider::LineType )
{
QgsDebugMsg( "Getting next feature of type <line> failed." );
}
else if ( P->mFeatureType == QgsOSMDataProvider::PolygonType )
{
QgsDebugMsg( "Getting next feature of type <polygon> failed." );
}
feature.setValid( false );
close();
return false;
}
bool QgsOSMFeatureIterator::rewind()
{
if ( mClosed )
return false;
// we have to reset precompiled database statement; thanx to this action the first feature
// (returned by the query) will be selected again with the next calling of sqlite3_step(mStmt)
if ( mSelectStmt )
sqlite3_reset( mSelectStmt );
return false;
}
bool QgsOSMFeatureIterator::close()
{
if ( mClosed )
return false;
// destruct selected geometry
delete mRectGeom;
mRectGeom = 0;
sqlite3_finalize( mSelectStmt );
// finalize all created sqlite3 statements
sqlite3_finalize( mTagsStmt );
sqlite3_finalize( mCustomTagsStmt );
// tell provider that this iterator is not active anymore
P->mActiveIterator = 0;
mClosed = true;
return true;
}
bool QgsOSMFeatureIterator::fetchNode( QgsFeature& feature )
{
sqlite3_stmt* stmt = mSelectStmt;
bool fetchGeometry = !( mRequest.flags() & QgsFeatureRequest::NoGeometry );
int selId = sqlite3_column_int( stmt, 0 );
double selLat = sqlite3_column_double( stmt, 1 );
double selLon = sqlite3_column_double( stmt, 2 );
feature.setFeatureId( selId );
feature.setValid( true );
feature.initAttributes( P->mAttributeFields.count() );
feature.setFields( &P->mAttributeFields ); // allow name-based attribute lookups
// fetch feature's geometry
if ( fetchGeometry )
{
char* geo = new char[21];
memset( geo, 0, 21 );
geo[0] = QgsApplication::endian();
geo[geo[0] == QgsApplication::NDR ? 1 : 4] = QGis::WKBPoint;
memcpy( geo + 5, &selLon, sizeof( double ) );
memcpy( geo + 13, &selLat, sizeof( double ) );
feature.setGeometryAndOwnership(( unsigned char * )geo, 24 ); // 24 is size of wkb point structure!
}
// fetch attributes
fetchAttributes( feature, true );
return true;
}
bool QgsOSMFeatureIterator::fetchWay( QgsFeature& feature )
{
sqlite3_stmt* stmt = mSelectStmt;
bool fetchGeometry = !( mRequest.flags() & QgsFeatureRequest::NoGeometry );
bool useIntersect = mRequest.flags() & QgsFeatureRequest::ExactIntersect;
int selId;
QgsGeometry *theGeometry = NULL;
bool fetchMoreRows = true;
int rc = -1;
do
{
selId = sqlite3_column_int( stmt, 0 );
unsigned char *pzBlob = 0;
int pnBlob = 0;
if ( fetchGeometry || useIntersect || mRequest.filterType() == QgsFeatureRequest::FilterRect )
{
pnBlob = sqlite3_column_bytes( stmt, 1 );
pzBlob = new unsigned char[pnBlob];
memcpy( pzBlob, sqlite3_column_blob( stmt, 1 ), pnBlob );
// create geometry
theGeometry = new QgsGeometry();
theGeometry->fromWkb(( unsigned char * ) pzBlob, pnBlob );
}
if ( theGeometry && ( theGeometry->type() == 3 ) && ( selId != 0 ) )
{
// line/polygon geometry is not cached!
char *geo;
int geolen;
P->updateWayWKB( selId, ( P->mFeatureType == QgsOSMDataProvider::LineType ) ? 0 : 1, &geo, &geolen );
theGeometry->fromWkb(( unsigned char * ) geo, ( size_t ) geolen );
}
if ( mRequest.filterType() == QgsFeatureRequest::FilterRect )
{
if ( useIntersect )
{
// when using intersect, some features might be ignored if they don't intersect the selection rect
// intersect is a costly operation, use rectangle converted to geos for less conversions
// (this is usually used during identification of an object)
if ( theGeometry->intersects( mRectGeom ) )
fetchMoreRows = false;
}
else
{
// when using selection rectangle but without exact intersection, check only overlap of bounding box
// (usually used when drawing)
if ( mRequest.filterRect().intersects( theGeometry->boundingBox() ) )
fetchMoreRows = false;
}
}
else
{
// no filter => always accept the new feature
// (used in attribute table)
fetchMoreRows = false;
}
// delete the geometry (if any) in case we're not going to use it anyway
if ( fetchMoreRows )
delete theGeometry;
}
while ( fetchMoreRows && (( rc = sqlite3_step( stmt ) ) == SQLITE_ROW ) );
// no more features to return
if ( rc == SQLITE_DONE )
{
sqlite3_exec( P->mDatabase, "COMMIT;", 0, 0, 0 );
feature.setValid( false );
return false;
}
// fetch feature's geometry
if ( fetchGeometry )
{
feature.setGeometry( theGeometry );
}
else
{
delete theGeometry; // make sure it's deleted
}
feature.setFeatureId( selId );
feature.setValid( true );
// fetch attributes
fetchAttributes( feature, false );
return true;
}
void QgsOSMFeatureIterator::fetchAttributes( QgsFeature& feature, bool isNode )
{
feature.initAttributes( P->mAttributeFields.count() );
feature.setFields( &P->mAttributeFields ); // allow name-based attribute lookups
// node
const char* selTimestamp = ( const char* ) sqlite3_column_text( mSelectStmt, isNode ? 3 : 2 );
const char* selUser = ( const char* ) sqlite3_column_text( mSelectStmt, isNode ? 4 : 3 );
int selId = sqlite3_column_int( mSelectStmt, 0 );
// TODO[MD]: subset of attributes
//QgsAttributeList::const_iterator iter;
//for ( iter = fetchAttrs.begin(); iter != fetchAttrs.end(); ++iter )
for ( int i = 0; i < P->mAttributeFields.count(); ++i )
{
switch ( i )
{
case QgsOSMDataProvider::TimestampAttr:
feature.setAttribute( QgsOSMDataProvider::TimestampAttr, QString::fromUtf8( selTimestamp ) );
break;
case QgsOSMDataProvider::UserAttr:
feature.setAttribute( QgsOSMDataProvider::UserAttr, QString::fromUtf8( selUser ) );
break;
case QgsOSMDataProvider::TagAttr:
feature.setAttribute( QgsOSMDataProvider::TagAttr, tagsForObject( isNode, selId ) );
break;
default: // suppose it's a custom tag
if ( i >= QgsOSMDataProvider::CustomTagAttr && i < QgsOSMDataProvider::CustomTagAttr + P->mCustomTagsList.count() )
{
feature.setAttribute( i, tagForObject( isNode, selId, P->mCustomTagsList[i - QgsOSMDataProvider::CustomTagAttr] ) );
}
}
}
}
QString QgsOSMFeatureIterator::tagForObject( bool isNode, int id, QString tagKey )
{
sqlite3_bind_int( mCustomTagsStmt, 1, id );
sqlite3_bind_text( mCustomTagsStmt, 2, isNode ? "node" : "way", -1, 0 );
QByteArray tag = tagKey.toUtf8(); // must keep the byte array until the query is run
sqlite3_bind_text( mCustomTagsStmt, 3, tag.data(), -1, 0 );
QString value;
int rc;
if (( rc = sqlite3_step( mCustomTagsStmt ) ) == SQLITE_ROW )
{
const char* tagVal = ( const char* ) sqlite3_column_text( mCustomTagsStmt, 0 );
value = QString::fromUtf8( tagVal );
}
else
{
// tag wasn't found
sqlite3_reset( mCustomTagsStmt ); // make ready for next retrieval
return "";
}
sqlite3_reset( mCustomTagsStmt ); // make ready for next retrieval
return value;
}
QString QgsOSMFeatureIterator::tagsForObject( bool isNode, int id )
{
sqlite3_bind_int( mTagsStmt, 1, id );
sqlite3_bind_text( mTagsStmt, 2, isNode ? "node" : "way", -1, 0 );
QString tags;
int rc;
while (( rc = sqlite3_step( mTagsStmt ) ) == SQLITE_ROW )
{
const char* tagKey = ( const char* ) sqlite3_column_text( mTagsStmt, 0 );
const char* tagVal = ( const char* ) sqlite3_column_text( mTagsStmt, 1 );
QString key = QString::fromUtf8( tagKey );
QString val = QString::fromUtf8( tagVal );
// we concatenate tags this way: "key1"="val1","key2"="val2","key3"="val3"
// -all ; in keyX and valX are replaced by ;;
// -all , in keyX and valX are replaced by ;
// -all - in keyX and valX are replaced by --
// -all = in keyX and valX are replaced by -
key = key.replace( ';', ";;" );
val = val.replace( ';', ";;" );
key = key.replace( ',', ";" );
val = val.replace( ',', ";" );
key = key.replace( '-', "--" );
val = val.replace( '-', "--" );
key = key.replace( '=', "-" );
val = val.replace( '=', "-" );
if ( tags.count() > 0 )
tags += ",";
tags += QString( "\"%1\"=\"%2\"" ).arg( key ).arg( val );
}
if ( rc != SQLITE_DONE )
{
// no tags for object
//QgsDebugMsg(QString("tags for object failed: type %1 id %2").arg(type).arg(id));
}
sqlite3_reset( mTagsStmt ); // make ready for next retrieval
return tags;
}

View File

@ -0,0 +1,87 @@
#ifndef OSMFEATUREITERATOR_H
#define OSMFEATUREITERATOR_H
#include "qgsfeatureiterator.h"
#include <sqlite3.h>
class QgsOSMDataProvider;
class QgsOSMFeatureIterator : public QgsAbstractFeatureIterator
{
public:
QgsOSMFeatureIterator( QgsOSMDataProvider* p, const QgsFeatureRequest& request );
~QgsOSMFeatureIterator();
//! fetch next feature, return true on success
virtual bool nextFeature( QgsFeature& feature );
//! reset the iterator to the starting position
virtual bool rewind();
//! end of iterating: free the resources / lock
virtual bool close();
protected:
/**
* Function fetches one node from current sqlite3 statement.
* @param feature output; feature representing fetched node
* @return success of failure flag (true/false)
*/
bool fetchNode( QgsFeature& feature );
/**
* Function fetches one way from current sqlite3 statement.
* @param feature output; feature representing fetched way
* @return success of failure flag (true/false)
*/
bool fetchWay( QgsFeature& feature );
/**
* Function returns string of concatenated tags of specified feature.
* @param isNode true for node, false for way
* @param id feature identifier
* @return string of tags concatenation
*/
QString tagsForObject( bool isNode, int id );
/**
* Function returns one tag value of specified feature and specified key.
* @param isNode true for node, false for way
* @param id feature identifier
* @param tagKey tag key
* @return tag value
*/
QString tagForObject( bool isNode, int id, QString tagKey );
/**
* Add attributes to the feature from the current row
* @param feature
* @param isNode true for node, false for way
*/
void fetchAttributes( QgsFeature& feature, bool isNode );
protected:
QgsOSMDataProvider* P;
//! sqlite3 database statement for selection
sqlite3_stmt *mSelectStmt;
//! sqlite3 database statement ready to select all feature tags
sqlite3_stmt *mTagsStmt;
//! sqlite3 database statement ready to select concrete feature tag
sqlite3_stmt *mCustomTagsStmt;
//! geometry for exact intersection test
QgsGeometry* mRectGeom;
};
#endif // OSMFEATUREITERATOR_H

View File

@ -15,6 +15,7 @@
#include "osmprovider.h"
#include "osmhandler.h"
#include "osmrenderer.h"
#include "osmfeatureiterator.h"
#include "qgsfeature.h"
#include "qgsfield.h"
@ -40,24 +41,19 @@ const char* QgsOSMDataProvider::attr[] = { "timestamp", "user", "tags" };
QgsOSMDataProvider::QgsOSMDataProvider( QString uri )
: QgsVectorDataProvider( uri )
, mActiveIterator( 0 )
{
QgsDebugMsg( "Initializing provider: " + uri );
mDatabaseStmt = NULL;
mValid = false;
// set the selection rectangle to null
mSelectionRectangle = 0;
mSelectionRectangleGeom = NULL;
mDatabase = NULL;
mInitObserver = NULL;
mFeatureType = PointType; // default feature type ~ point
// set default boundaries
xMin = -DEFAULT_EXTENT;
xMax = DEFAULT_EXTENT;
yMin = -DEFAULT_EXTENT;
yMax = DEFAULT_EXTENT;
mExtent = QgsRectangle( -DEFAULT_EXTENT, -DEFAULT_EXTENT, DEFAULT_EXTENT, DEFAULT_EXTENT );
// get the filename and other parameters from the URI
int fileNameEnd = uri.indexOf( '?' );
@ -137,14 +133,14 @@ QgsOSMDataProvider::QgsOSMDataProvider( QString uri )
}
// set up attributes depending on the feature type - same attributes for both point and way type so far
mAttributeFields[TimestampAttr] = QgsField( attr[TimestampAttr], QVariant::String, "string" );
mAttributeFields[UserAttr] = QgsField( attr[UserAttr], QVariant::String, "string" );
mAttributeFields[TagAttr] = QgsField( attr[TagAttr], QVariant::String, "string" );
mAttributeFields.append( QgsField( attr[TimestampAttr], QVariant::String, "string" ) );
mAttributeFields.append( QgsField( attr[UserAttr], QVariant::String, "string" ) );
mAttributeFields.append( QgsField( attr[TagAttr], QVariant::String, "string" ) );
// add custom attributes - these were chosen by user through OSM plugin
for ( int tagId = 0; tagId < mCustomTagsList.count(); ++tagId )
{
mAttributeFields[CustomTagAttr+tagId] = QgsField( mCustomTagsList[tagId], QVariant::String, "string" );
mAttributeFields.append( QgsField( mCustomTagsList[tagId], QVariant::String, "string" ) );
}
// get source file name and database file name
@ -226,10 +222,10 @@ QgsOSMDataProvider::QgsOSMDataProvider( QString uri )
QStringList parts = boundaries.split( QChar( ':' ) );
if ( parts.count() == 4 )
{
xMin = parts[0].toDouble();
yMin = parts[1].toDouble();
xMax = parts[2].toDouble();
yMax = parts[3].toDouble();
mExtent.setXMinimum( parts[0].toDouble() );
mExtent.setYMinimum( parts[1].toDouble() );
mExtent.setXMaximum( parts[2].toDouble() );
mExtent.setYMaximum( parts[3].toDouble() );
}
else
{
@ -243,94 +239,6 @@ QgsOSMDataProvider::QgsOSMDataProvider( QString uri )
}
// prepare statement for tag retrieval
char sqlSelectTags[] = "SELECT key, val FROM tag WHERE object_id=? AND object_type=?";
int rc = sqlite3_prepare_v2( mDatabase, sqlSelectTags, sizeof( sqlSelectTags ), &mTagsStmt, 0 );
if ( rc != SQLITE_OK )
{
QgsDebugMsg( "sqlite3 statement for feature tags selection - prepare failed." );
return;
}
char sqlSelectTagValue[] = "SELECT val FROM tag WHERE object_id=? AND object_type=? AND key=?";
rc = sqlite3_prepare_v2( mDatabase, sqlSelectTagValue, sizeof( sqlSelectTagValue ), &mCustomTagsStmt, 0 );
if ( rc != SQLITE_OK )
{
QgsDebugMsg( "sqlite3 statement for tag value selection - prepare failed." );
return;
}
// prepare statements for feature retrieval
char sqlSelectWay[] = "SELECT id, wkb, timestamp, user FROM way WHERE id=? AND status<>'R' AND u=1";
rc = sqlite3_prepare_v2( mDatabase, sqlSelectWay, sizeof( sqlSelectWay ), &mWayStmt, 0 );
if ( rc != SQLITE_OK )
{
QgsDebugMsg( "sqlite3 statement for way retrieval - prepare failed." );
return;
}
char sqlSelectNode[] = "SELECT id, lat, lon, timestamp, user FROM node WHERE id=? AND usage=0 AND status<>'R' AND u=1";
rc = sqlite3_prepare_v2( mDatabase, sqlSelectNode, sizeof( sqlSelectNode ), &mNodeStmt, 0 );
if ( rc != SQLITE_OK )
{
QgsDebugMsg( "sqlite3 statement for node retrieval - prepare failed." );
return;
}
if ( mFeatureType == PointType )
{
char sqlSelectPoints[] = "SELECT id, lat, lon, timestamp, user FROM node WHERE usage=0 AND status<>'R' AND u=1";
char sqlSelectPointsIn[] = "SELECT id, lat, lon, timestamp, user FROM node WHERE usage=0 AND status<>'R' AND u=1 \
AND lat>=? AND lat<=? AND lon>=? AND lon<=?";
if ( sqlite3_prepare_v2( mDatabase, sqlSelectPoints, sizeof( sqlSelectPoints ), &mSelectFeatsStmt, 0 ) != SQLITE_OK )
{
QgsDebugMsg( "sqlite3 statement for points retrieval - prepare failed." );
return;
}
if ( sqlite3_prepare_v2( mDatabase, sqlSelectPointsIn, sizeof( sqlSelectPointsIn ), &mSelectFeatsInStmt, 0 ) != SQLITE_OK )
{
QgsDebugMsg( "sqlite3 statement for points in boundary retrieval - prepare failed." );
return;
}
}
else if ( mFeatureType == LineType )
{
char sqlSelectLines[] = "SELECT w.id, w.wkb, w.timestamp, w.user FROM way w WHERE w.closed=0 AND w.status<>'R' AND w.u=1";
char sqlSelectLinesIn[] = "SELECT w.id, w.wkb, w.timestamp, w.user FROM way w WHERE w.closed=0 AND w.status<>'R' AND w.u=1 \
AND (((w.max_lat between ? AND ?) OR (w.min_lat between ? AND ?) OR (w.min_lat<? AND w.max_lat>?)) \
AND ((w.max_lon between ? AND ?) OR (w.min_lon between ? AND ?) OR (w.min_lon<? AND w.max_lon>?)))";
if ( sqlite3_prepare_v2( mDatabase, sqlSelectLines, sizeof( sqlSelectLines ), &mSelectFeatsStmt, 0 ) != SQLITE_OK )
{
QgsDebugMsg( "sqlite3 statement for lines retrieval - prepare failed." );
return;
}
if ( sqlite3_prepare_v2( mDatabase, sqlSelectLinesIn, sizeof( sqlSelectLinesIn ), &mSelectFeatsInStmt, 0 ) != SQLITE_OK )
{
QgsDebugMsg( "sqlite3 statement for lines in boundary retrieval - prepare failed." );
return;
}
}
else // mFeatureType == PolygonType
{
char sqlSelectPolys[] = "SELECT w.id, w.wkb, w.timestamp, w.user FROM way w WHERE w.closed=1 AND w.status<>'R' AND w.u=1";
char sqlSelectPolysIn[] = "SELECT w.id, w.wkb, w.timestamp, w.user FROM way w WHERE w.closed=1 AND w.status<>'R' AND w.u=1 \
AND (((w.max_lat between ? AND ?) OR (w.min_lat between ? AND ?) OR (w.min_lat<? AND w.max_lat>?)) \
AND ((w.max_lon between ? AND ?) OR (w.min_lon between ? AND ?) OR (w.min_lon<? AND w.max_lon>?)))";
if ( sqlite3_prepare_v2( mDatabase, sqlSelectPolys, sizeof( sqlSelectPolys ), &mSelectFeatsStmt, 0 ) != SQLITE_OK )
{
QgsDebugMsg( "sqlite3 statement for polygons retrieval - prepare failed." );
return;
}
if ( sqlite3_prepare_v2( mDatabase, sqlSelectPolysIn, sizeof( sqlSelectPolysIn ), &mSelectFeatsInStmt, 0 ) != SQLITE_OK )
{
QgsDebugMsg( "sqlite3 statement for polygons in boundary retrieval - prepare failed." );
return;
}
}
// finally OSM provider is initialized and considered to be valid!
mValid = true;
}
@ -338,16 +246,8 @@ QgsOSMDataProvider::QgsOSMDataProvider( QString uri )
QgsOSMDataProvider::~QgsOSMDataProvider()
{
// destruct selected geometry
delete mSelectionRectangleGeom;
// finalize all created sqlite3 statements
sqlite3_finalize( mTagsStmt );
sqlite3_finalize( mCustomTagsStmt );
sqlite3_finalize( mWayStmt );
sqlite3_finalize( mNodeStmt );
sqlite3_finalize( mSelectFeatsStmt );
sqlite3_finalize( mSelectFeatsInStmt );
if ( mActiveIterator )
mActiveIterator->close();
// close opened sqlite3 database
if ( mDatabase )
@ -422,82 +322,6 @@ QString QgsOSMDataProvider::storageType() const
}
void QgsOSMDataProvider::select( QgsAttributeList fetchAttributes,
QgsRectangle rect,
bool fetchGeometry,
bool useIntersect )
{
// re-initialization
delete mSelectionRectangleGeom;
if ( mDatabaseStmt )
// we must reset sqlite3 statement after recent selection - make it ready for next features selection
sqlite3_reset( mDatabaseStmt );
// store list of attributes to fetch, rectangle of area, geometry, etc.
mSelectionRectangle = rect;
mSelectionRectangleGeom = QgsGeometry::fromRect( rect );
mAttributesToFetch = fetchAttributes;
// set flags
mFetchGeom = fetchGeometry;
mSelectUseIntersect = useIntersect;
if ( mSelectionRectangle.isEmpty() )
{
// we want to select all features from OSM data; we will use mSelectFeatsStmt
// sqlite3 statement that is well prepared for this purpose
mDatabaseStmt = mSelectFeatsStmt;
return;
}
// we want to select features from specified boundary; we will use mSelectFeatsInStmt
// sqlite3 statement that is well prepared for this purpose
mDatabaseStmt = mSelectFeatsInStmt;
if ( mFeatureType == PointType )
{
// binding variables (boundary) for points selection!
sqlite3_bind_double( mDatabaseStmt, 1, mSelectionRectangle.yMinimum() );
sqlite3_bind_double( mDatabaseStmt, 2, mSelectionRectangle.yMaximum() );
sqlite3_bind_double( mDatabaseStmt, 3, mSelectionRectangle.xMinimum() );
sqlite3_bind_double( mDatabaseStmt, 4, mSelectionRectangle.xMaximum() );
}
else if ( mFeatureType == LineType )
{
// binding variables (boundary) for lines selection!
sqlite3_bind_double( mDatabaseStmt, 1, mSelectionRectangle.yMinimum() );
sqlite3_bind_double( mDatabaseStmt, 2, mSelectionRectangle.yMaximum() );
sqlite3_bind_double( mDatabaseStmt, 3, mSelectionRectangle.yMinimum() );
sqlite3_bind_double( mDatabaseStmt, 4, mSelectionRectangle.yMaximum() );
sqlite3_bind_double( mDatabaseStmt, 5, mSelectionRectangle.yMinimum() );
sqlite3_bind_double( mDatabaseStmt, 6, mSelectionRectangle.yMaximum() );
sqlite3_bind_double( mDatabaseStmt, 7, mSelectionRectangle.xMinimum() );
sqlite3_bind_double( mDatabaseStmt, 8, mSelectionRectangle.xMaximum() );
sqlite3_bind_double( mDatabaseStmt, 9, mSelectionRectangle.xMinimum() );
sqlite3_bind_double( mDatabaseStmt, 10, mSelectionRectangle.xMaximum() );
sqlite3_bind_double( mDatabaseStmt, 11, mSelectionRectangle.xMinimum() );
sqlite3_bind_double( mDatabaseStmt, 12, mSelectionRectangle.xMaximum() );
}
else // mFeatureType == PolygonType
{
// binding variables (boundary) for polygons selection!
sqlite3_bind_double( mDatabaseStmt, 1, mSelectionRectangle.yMinimum() );
sqlite3_bind_double( mDatabaseStmt, 2, mSelectionRectangle.yMaximum() );
sqlite3_bind_double( mDatabaseStmt, 3, mSelectionRectangle.yMinimum() );
sqlite3_bind_double( mDatabaseStmt, 4, mSelectionRectangle.yMaximum() );
sqlite3_bind_double( mDatabaseStmt, 5, mSelectionRectangle.yMinimum() );
sqlite3_bind_double( mDatabaseStmt, 6, mSelectionRectangle.yMaximum() );
sqlite3_bind_double( mDatabaseStmt, 7, mSelectionRectangle.xMinimum() );
sqlite3_bind_double( mDatabaseStmt, 8, mSelectionRectangle.xMaximum() );
sqlite3_bind_double( mDatabaseStmt, 9, mSelectionRectangle.xMinimum() );
sqlite3_bind_double( mDatabaseStmt, 10, mSelectionRectangle.xMaximum() );
sqlite3_bind_double( mDatabaseStmt, 11, mSelectionRectangle.xMinimum() );
sqlite3_bind_double( mDatabaseStmt, 12, mSelectionRectangle.xMaximum() );
}
}
int QgsOSMDataProvider::wayMemberCount( int wayId )
{
@ -530,322 +354,9 @@ int QgsOSMDataProvider::wayMemberCount( int wayId )
}
bool QgsOSMDataProvider::nextFeature( QgsFeature& feature )
{
// load next requested feature from sqlite3 database
switch ( sqlite3_step( mDatabaseStmt ) )
{
case SQLITE_DONE: // no more features to return
feature.setValid( false );
return false;
case SQLITE_ROW: // another feature to return
if ( mFeatureType == PointType )
return fetchNode( feature, mDatabaseStmt, mFetchGeom, mAttributesToFetch );
else if ( mFeatureType == LineType )
return fetchWay( feature, mDatabaseStmt, mFetchGeom, mAttributesToFetch );
else if ( mFeatureType == PolygonType )
return fetchWay( feature, mDatabaseStmt, mFetchGeom, mAttributesToFetch );
default:
if ( mFeatureType == PointType )
{
QgsDebugMsg( "Getting next feature of type <point> failed." );
}
else if ( mFeatureType == LineType )
{
QgsDebugMsg( "Getting next feature of type <line> failed." );
}
else if ( mFeatureType == PolygonType )
{
QgsDebugMsg( "Getting next feature of type <polygon> failed." );
}
feature.setValid( false );
return false;
}
}
bool QgsOSMDataProvider::featureAtId( QgsFeatureId featureId,
QgsFeature& feature,
bool fetchGeometry,
QgsAttributeList fetchAttributes )
{
// load exact feature from sqlite3 database
if ( mFeatureType == PointType )
{
sqlite3_bind_int64( mNodeStmt, 1, featureId );
if ( sqlite3_step( mNodeStmt ) != SQLITE_ROW )
{
QgsDebugMsg( QString( "Getting information about point with id=%1 failed." ).arg( featureId ) );
sqlite3_reset( mNodeStmt );
return false;
}
fetchNode( feature, mNodeStmt, fetchGeometry, fetchAttributes );
// prepare statement for next call
sqlite3_reset( mNodeStmt );
}
else if (( mFeatureType == LineType ) || ( mFeatureType == PolygonType ) )
{
sqlite3_bind_int64( mWayStmt, 1, featureId );
if ( sqlite3_step( mWayStmt ) != SQLITE_ROW )
{
QgsDebugMsg( QString( "Getting information about way with id=%1 failed." ).arg( featureId ) );
sqlite3_reset( mWayStmt );
return false;
}
fetchWay( feature, mWayStmt, fetchGeometry, fetchAttributes );
// prepare statement for next call
sqlite3_reset( mWayStmt );
}
return true;
}
bool QgsOSMDataProvider::fetchNode( QgsFeature& feature, sqlite3_stmt* stmt, bool fetchGeometry, QgsAttributeList& fetchAttrs )
{
int selId = sqlite3_column_int( stmt, 0 );
double selLat = sqlite3_column_double( stmt, 1 );
double selLon = sqlite3_column_double( stmt, 2 );
const char* selTimestamp = ( const char* ) sqlite3_column_text( stmt, 3 );
const char* selUser = ( const char* ) sqlite3_column_text( stmt, 4 );
// fetch feature's geometry
if ( fetchGeometry )
{
char* geo = new char[21];
memset( geo, 0, 21 );
geo[0] = QgsApplication::endian();
geo[geo[0] == QgsApplication::NDR ? 1 : 4] = QGis::WKBPoint;
memcpy( geo + 5, &selLon, sizeof( double ) );
memcpy( geo + 13, &selLat, sizeof( double ) );
feature.setGeometryAndOwnership(( unsigned char * )geo, 24 ); // 24 is size of wkb point structure!
}
// fetch attributes
QgsAttributeList::const_iterator iter;
for ( iter = fetchAttrs.begin(); iter != fetchAttrs.end(); ++iter )
{
switch ( *iter )
{
case TimestampAttr:
feature.addAttribute( TimestampAttr, QString::fromUtf8( selTimestamp ) ); break;
case UserAttr:
feature.addAttribute( UserAttr, QString::fromUtf8( selUser ) ); break;
case TagAttr:
feature.addAttribute( TagAttr, tagsForObject( "node", selId ) ); break;
default: // suppose it's a custom tag
if ( *iter >= CustomTagAttr && *iter < CustomTagAttr + mCustomTagsList.count() )
{
feature.addAttribute( *iter, tagForObject( "node", selId, mCustomTagsList[*iter-CustomTagAttr] ) );
}
}
}
feature.setFeatureId( selId );
feature.setValid( true );
feature.setFieldMap( &mAttributeFields ); // allow name-based attribute lookups
return true;
}
bool QgsOSMDataProvider::fetchWay( QgsFeature& feature, sqlite3_stmt* stmt, bool fetchGeometry, QgsAttributeList& fetchAttrs )
{
int selId;
const char* selTimestamp;
const char* selUser;
QgsGeometry *theGeometry = NULL;
bool fetchMoreRows = true;
int rc = -1;
do
{
selId = sqlite3_column_int( stmt, 0 );
selTimestamp = ( const char* ) sqlite3_column_text( stmt, 2 );
selUser = ( const char* ) sqlite3_column_text( stmt, 3 );
unsigned char *pzBlob = 0;
int pnBlob = 0;
if ( fetchGeometry || mSelectUseIntersect || !mSelectionRectangle.isEmpty() )
{
pnBlob = sqlite3_column_bytes( stmt, 1 );
pzBlob = new unsigned char[pnBlob];
memcpy( pzBlob, sqlite3_column_blob( stmt, 1 ), pnBlob );
// create geometry
theGeometry = new QgsGeometry();
theGeometry->fromWkb(( unsigned char * ) pzBlob, pnBlob );
}
if ( theGeometry && ( theGeometry->type() == 3 ) && ( selId != 0 ) )
{
// line/polygon geometry is not cached!
char *geo;
int geolen;
updateWayWKB( selId, ( mFeatureType == LineType ) ? 0 : 1, &geo, &geolen );
theGeometry->fromWkb(( unsigned char * ) geo, ( size_t ) geolen );
}
if ( mSelectUseIntersect )
{
// when using intersect, some features might be ignored if they don't intersect the selection rect
// intersect is a costly operation, use rectangle converted to geos for less conversions
// (this is usually used during identification of an object)
if ( theGeometry->intersects( mSelectionRectangleGeom ) )
fetchMoreRows = false;
}
else if ( !mSelectionRectangle.isEmpty() )
{
// when using selection rectangle but without exact intersection, check only overlap of bounding box
// (usually used when drawing)
if ( mSelectionRectangle.intersects( theGeometry->boundingBox() ) )
fetchMoreRows = false;
}
else
{
// no filter => always accept the new feature
// (used in attribute table)
fetchMoreRows = false;
}
// delete the geometry (if any) in case we're not going to use it anyway
if ( fetchMoreRows )
delete theGeometry;
}
while ( fetchMoreRows && (( rc = sqlite3_step( stmt ) ) == SQLITE_ROW ) );
// no more features to return
if ( rc == SQLITE_DONE )
{
sqlite3_exec( mDatabase, "COMMIT;", 0, 0, 0 );
feature.setValid( false );
return false;
}
// fetch feature's geometry
if ( fetchGeometry )
{
feature.setGeometry( theGeometry );
}
else
{
delete theGeometry; // make sure it's deleted
}
// fetch attributes
QgsAttributeList::const_iterator iter;
for ( iter = fetchAttrs.begin(); iter != fetchAttrs.end(); ++iter )
{
switch ( *iter )
{
case TimestampAttr:
feature.addAttribute( TimestampAttr, QString::fromUtf8( selTimestamp ) );
break;
case UserAttr:
feature.addAttribute( UserAttr, QString::fromUtf8( selUser ) );
break;
case TagAttr:
feature.addAttribute( TagAttr, tagsForObject( "way", selId ) );
break;
default: // suppose it's a custom tag
if ( *iter >= CustomTagAttr && *iter < CustomTagAttr + mCustomTagsList.count() )
{
feature.addAttribute( *iter, tagForObject( "way", selId, mCustomTagsList[*iter-CustomTagAttr] ) );
}
}
}
feature.setFeatureId( selId );
feature.setValid( true );
feature.setFieldMap( &mAttributeFields ); // allow name-based attribute lookups
return true;
}
QString QgsOSMDataProvider::tagForObject( const char* type, int id, QString tagKey )
{
sqlite3_bind_int( mCustomTagsStmt, 1, id );
sqlite3_bind_text( mCustomTagsStmt, 2, type, -1, 0 );
QByteArray tag = tagKey.toUtf8(); // must keep the byte array until the query is run
sqlite3_bind_text( mCustomTagsStmt, 3, tag.data(), -1, 0 );
QString value;
int rc;
if (( rc = sqlite3_step( mCustomTagsStmt ) ) == SQLITE_ROW )
{
const char* tagVal = ( const char* ) sqlite3_column_text( mCustomTagsStmt, 0 );
value = QString::fromUtf8( tagVal );
}
else
{
// tag wasn't found
sqlite3_reset( mCustomTagsStmt ); // make ready for next retrieval
return "";
}
sqlite3_reset( mCustomTagsStmt ); // make ready for next retrieval
return value;
}
QString QgsOSMDataProvider::tagsForObject( const char* type, int id )
{
sqlite3_bind_int( mTagsStmt, 1, id );
sqlite3_bind_text( mTagsStmt, 2, type, -1, 0 );
QString tags;
int rc;
while (( rc = sqlite3_step( mTagsStmt ) ) == SQLITE_ROW )
{
const char* tagKey = ( const char* ) sqlite3_column_text( mTagsStmt, 0 );
const char* tagVal = ( const char* ) sqlite3_column_text( mTagsStmt, 1 );
QString key = QString::fromUtf8( tagKey );
QString val = QString::fromUtf8( tagVal );
// we concatenate tags this way: "key1"="val1","key2"="val2","key3"="val3"
// -all ; in keyX and valX are replaced by ;;
// -all , in keyX and valX are replaced by ;
// -all - in keyX and valX are replaced by --
// -all = in keyX and valX are replaced by -
key = key.replace( ';', ";;" );
val = val.replace( ';', ";;" );
key = key.replace( ',', ";" );
val = val.replace( ',', ";" );
key = key.replace( '-', "--" );
val = val.replace( '-', "--" );
key = key.replace( '=', "-" );
val = val.replace( '=', "-" );
if ( tags.count() > 0 )
tags += ",";
tags += QString( "\"%1\"=\"%2\"" ).arg( key ).arg( val );
}
if ( rc != SQLITE_DONE )
{
// no tags for object
//QgsDebugMsg(QString("tags for object failed: type %1 id %2").arg(type).arg(id));
}
sqlite3_reset( mTagsStmt ); // make ready for next retrieval
return tags;
}
QgsRectangle QgsOSMDataProvider::extent()
{
return QgsRectangle( xMin, yMin, xMax, yMax );
return mExtent;
}
@ -883,6 +394,12 @@ long QgsOSMDataProvider::featureCount() const
}
QgsFeatureIterator QgsOSMDataProvider::getFeatures( const QgsFeatureRequest& request )
{
return QgsFeatureIterator( new QgsOSMFeatureIterator( this, request ) );
}
bool QgsOSMDataProvider::isValid()
{
return mValid;
@ -940,7 +457,7 @@ QGISEXTERN bool isProvider()
}
const QgsFieldMap & QgsOSMDataProvider::fields() const
const QgsFields & QgsOSMDataProvider::fields() const
{
return mAttributeFields;
}
@ -952,15 +469,6 @@ QgsCoordinateReferenceSystem QgsOSMDataProvider::crs()
}
void QgsOSMDataProvider::rewind()
{
// we have to reset precompiled database statement; thanx to this action the first feature
// (returned by the query) will be selected again with the next calling of sqlite3_step(mDatabaseStmt)
if ( mDatabaseStmt )
sqlite3_reset( mDatabaseStmt );
}
int QgsOSMDataProvider::freeFeatureId()
{
// todo: optimalization - wouldn't be better to keep minimum id in meta table?
@ -1395,14 +903,13 @@ bool QgsOSMDataProvider::loadOsmFile( QString osm_filename )
}
// store information got with handler into provider member variables
xMin = handler->xMin; // boundaries defining the area of all features
xMax = handler->xMax;
yMin = handler->yMin;
yMax = handler->yMax;
// boundaries defining the area of all features
mExtent = QgsRectangle( handler->xMin, handler->yMin, handler->xMax, handler->yMax );
// storing boundary information into database
QString cmd3 = QString( "INSERT INTO meta ( key, val ) VALUES ('default-area-boundaries','%1:%2:%3:%4');" )
.arg( xMin, 0, 'f', 10 ).arg( yMin, 0, 'f', 10 ).arg( xMax, 0, 'f', 10 ).arg( yMax, 0, 'f', 10 );
.arg( mExtent.xMinimum(), 0, 'f', 10 ).arg( mExtent.yMinimum(), 0, 'f', 10 )
.arg( mExtent.xMaximum(), 0, 'f', 10 ).arg( mExtent.yMaximum(), 0, 'f', 10 );
QByteArray cmd_bytes3 = cmd3.toAscii();
const char *ptr3 = cmd_bytes3.data();

View File

@ -1,3 +1,6 @@
#ifndef OSMPROVIDER_H
#define OSMPROVIDER_H
/***************************************************************************
osmprovider.h - provider for OSM; stores OSM data in sqlite3 DB
------------------
@ -19,6 +22,8 @@
class QgsVectorLayer;
class QgsOSMFeatureIterator;
/**
* Quantum GIS provider for OpenStreetMap data.
*/
@ -29,7 +34,8 @@ class QgsOSMDataProvider: public QgsVectorDataProvider
private:
//! provider manages features with one of three geometry types; variable determines feature type of this provider
enum { PointType, LineType, PolygonType } mFeatureType;
enum OSMType { PointType, LineType, PolygonType };
OSMType mFeatureType;
//! supported feature attributes
enum Attribute { TimestampAttr = 0, UserAttr = 1, TagAttr, CustomTagAttr };
@ -53,7 +59,7 @@ class QgsOSMDataProvider: public QgsVectorDataProvider
QObject* mInitObserver;
//! boundary of all OSM data that provider manages
double xMin, xMax, yMin, yMax;
QgsRectangle mExtent;
//! list of feature tags for which feature attributes are created
QStringList mCustomTagsList;
@ -72,44 +78,11 @@ class QgsOSMDataProvider: public QgsVectorDataProvider
//! pointer to sqlite3 database that keeps OSM data
sqlite3 *mDatabase;
//! pointer to main sqlite3 database statement object; this statement serves to select OSM data
sqlite3_stmt *mDatabaseStmt;
//! pointer to main sqlite3 database statement object; this statement serves to select OSM data
sqlite3_stmt *mSelectFeatsStmt;
//! pointer to main sqlite3 db stmt object; this stmt serves to select OSM data from some boundary
sqlite3_stmt *mSelectFeatsInStmt;
//! sqlite3 database statement ready to select all feature tags
sqlite3_stmt *mTagsStmt;
//! sqlite3 database statement ready to select concrete feature tag
sqlite3_stmt *mCustomTagsStmt;
//! sqlite3 database statement for exact way selection
sqlite3_stmt *mWayStmt;
//! sqlite3 database statement for exact node selection
sqlite3_stmt *mNodeStmt;
// variables used to select OSM data; used mainly in select(), nextFeature() functions:
//! list of supported attribute fields
QgsFieldMap mAttributeFields;
//! which attributes should be fetched after calling of select() function
QgsAttributeList mAttributesToFetch;
//! features from which area should be fetched after calling of select() function?
QgsRectangle mSelectionRectangle;
//! geometry object of area from which features should be fetched after calling of select() function
QgsGeometry* mSelectionRectangleGeom;
//! determines if intersect should be used while selecting OSM data
bool mSelectUseIntersect;
QgsFields mAttributeFields;
friend class QgsOSMFeatureIterator;
QgsOSMFeatureIterator* mActiveIterator; //!< pointer to currently active iterator (0 if none)
public:
@ -133,38 +106,6 @@ class QgsOSMDataProvider: public QgsVectorDataProvider
*/
virtual QString storageType() const;
/** Select features based on a bounding rectangle. Features can be retrieved with calls to getNextFeature.
* @param fetchAttributes list of attributes which should be fetched
* @param rect spatial filter
* @param fetchGeometry true if the feature geometry should be fetched
* @param useIntersect true if an accurate intersection test should be used,
* false if a test based on bounding box is sufficient
*/
virtual void select( QgsAttributeList fetchAttributes = QgsAttributeList(),
QgsRectangle rect = QgsRectangle(),
bool fetchGeometry = true,
bool useIntersect = false );
/**
* Get the next feature resulting from a select operation.
* @param feature feature which will receive data from the provider
* @return true when there was a feature to fetch, false when end was hit
*/
virtual bool nextFeature( QgsFeature& feature );
/**
* Gets the feature at the given feature ID.
* @param featureId id of the feature
* @param feature feature which will receive the data
* @param fetchGeometry if true, geometry will be fetched from the provider
* @param fetchAttributes a list containing the indexes of the attribute fields to copy
* @return True when feature was found, otherwise false
*/
virtual bool featureAtId( QgsFeatureId featureId,
QgsFeature& feature,
bool fetchGeometry = true,
QgsAttributeList fetchAttributes = QgsAttributeList() );
/**
* Get feature type.
* @return int representing the feature type
@ -181,12 +122,7 @@ class QgsOSMDataProvider: public QgsVectorDataProvider
* Return a map of indexes with field names for this layer
* @return map of fields
*/
virtual const QgsFieldMap & fields() const;
/**
* Restart reading features from previous select operation.
*/
virtual void rewind();
virtual const QgsFields & fields() const;
/**
* Returns a bitmask containing the supported capabilities
@ -196,6 +132,8 @@ class QgsOSMDataProvider: public QgsVectorDataProvider
*/
virtual int capabilities() const;
virtual QgsFeatureIterator getFeatures( const QgsFeatureRequest& request );
// Implementation of QgsDataProvider functions
@ -330,42 +268,6 @@ class QgsOSMDataProvider: public QgsVectorDataProvider
* @return number of way members
*/
int wayMemberCount( int wayId );
/**
* Function fetches one node from current sqlite3 statement.
* @param feature output; feature representing fetched node
* @param stmt database statement to fetch node from
* @param fetchGeometry determines if node geometry should be fetched also
* @param fetchAttrs list of attributes to be fetched with node
* @return success of failure flag (true/false)
*/
bool fetchNode( QgsFeature& feature, sqlite3_stmt* stmt, bool fetchGeometry, QgsAttributeList& fetchAttrs );
/**
* Function fetches one way from current sqlite3 statement.
* @param feature output; feature representing fetched way
* @param stmt database statement to fetch way from
* @param fetchGeometry determines if way geometry should be fetched also
* @param fetchAttrs list of attributes to be fetched with way
* @return success of failure flag (true/false)
*/
bool fetchWay( QgsFeature& feature, sqlite3_stmt* stmt, bool fetchGeometry, QgsAttributeList& fetchAttrs );
/**
* Function returns string of concatenated tags of specified feature.
* @param type type of feature (one of "node","way","relation")
* @param id feature identifier
* @return string of tags concatenation
*/
QString tagsForObject( const char* type, int id );
/**
* Function returns one tag value of specified feature and specified key.
* @param type type of feature (one of "node","way","relation")
* @param id feature identifier
* @param tagKey tag key
* @return tag value
*/
QString tagForObject( const char* type, int id, QString tagKey );
};
#endif