Navigation Menu

Skip to content

Commit

Permalink
delimitedtext: add support for featureSource()
Browse files Browse the repository at this point in the history
  • Loading branch information
wonder-sk committed Jan 13, 2014
1 parent 6d3f1b8 commit 78841d9
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 80 deletions.
119 changes: 77 additions & 42 deletions src/providers/delimitedtext/qgsdelimitedtextfeatureiterator.cpp
Expand Up @@ -25,23 +25,21 @@
#include <QtAlgorithms>
#include <QTextStream>

QgsDelimitedTextFeatureIterator::QgsDelimitedTextFeatureIterator( QgsDelimitedTextProvider* p, const QgsFeatureRequest& request )
: QgsAbstractFeatureIterator( request )
, P( p )
QgsDelimitedTextFeatureIterator::QgsDelimitedTextFeatureIterator( QgsDelimitedTextFeatureSource* source, bool ownSource, const QgsFeatureRequest& request )
: QgsAbstractFeatureIteratorFromSource( source, ownSource, request )
{
P->mActiveIterators << this;

// Determine mode to use based on request...
QgsDebugMsg( "Setting up QgsDelimitedTextIterator" );

// Does the layer have geometry - will revise later to determine if we actually need to
// load it.
bool hasGeometry = P->mGeomRep != QgsDelimitedTextProvider::GeomNone;
bool hasGeometry = mSource->mGeomRep != QgsDelimitedTextProvider::GeomNone;

// Does the layer have an explicit or implicit subset (implicit subset is if we have geometry which can
// be invalid)

mTestSubset = P->mSubsetExpression;
mTestSubset = mSource->mSubsetExpression;
mTestGeometry = false;

mMode = FileScan;
Expand All @@ -65,20 +63,20 @@ QgsDelimitedTextFeatureIterator::QgsDelimitedTextFeatureIterator( QgsDelimitedTe
mTestGeometry = true;
// Exact intersection test only applies for WKT geometries
mTestGeometryExact = mRequest.flags() & QgsFeatureRequest::ExactIntersect
&& P->mGeomRep == QgsDelimitedTextProvider::GeomAsWkt;
&& mSource->mGeomRep == QgsDelimitedTextProvider::GeomAsWkt;

QgsRectangle rect = request.filterRect();

// If request doesn't overlap extents, then nothing to return
if ( ! rect.intersects( P->extent() ) )
if ( ! rect.intersects( mSource->mExtent ) )
{
QgsDebugMsg( "Rectangle outside layer extents - no features to return" );
mMode = FeatureIds;
}
// If the request extents include the entire layer, then revert to
// a file scan

else if ( rect.contains( P->extent() ) )
else if ( rect.contains( mSource->mExtent ) )
{
QgsDebugMsg( "Rectangle contains layer extents - bypass spatial filter" );
mTestGeometry = false;
Expand All @@ -87,9 +85,9 @@ QgsDelimitedTextFeatureIterator::QgsDelimitedTextFeatureIterator( QgsDelimitedTe
// for the subset. Also means we don't have to test geometries unless doing exact
// intersection

else if ( P->mUseSpatialIndex )
else if ( mSource->mUseSpatialIndex )
{
mFeatureIds = P->mSpatialIndex->intersects( rect );
mFeatureIds = mSource->mSpatialIndex->intersects( rect );
// Sort for efficient sequential retrieval
qSort( mFeatureIds.begin(), mFeatureIds.end() );
QgsDebugMsg( QString( "Layer has spatial index - selected %1 features from index" ).arg( mFeatureIds.size() ) );
Expand All @@ -100,9 +98,9 @@ QgsDelimitedTextFeatureIterator::QgsDelimitedTextFeatureIterator( QgsDelimitedTe
}

// If we have a subset index then use it..
if ( mMode == FileScan && P->mUseSubsetIndex )
if ( mMode == FileScan && mSource->mUseSubsetIndex )
{
QgsDebugMsg( QString( "Layer has subset index - use %1 items from subset index" ).arg( P->mSubsetIndex.size() ) );
QgsDebugMsg( QString( "Layer has subset index - use %1 items from subset index" ).arg( mSource->mSubsetIndex.size() ) );
mTestSubset = false;
mMode = SubsetIndex;
}
Expand All @@ -121,7 +119,7 @@ QgsDelimitedTextFeatureIterator::QgsDelimitedTextFeatureIterator( QgsDelimitedTe
&& (
!( mRequest.flags() & QgsFeatureRequest::NoGeometry )
|| mTestGeometry
|| ( mTestSubset && P->mSubsetExpression->needsGeometry() )
|| ( mTestSubset && mSource->mSubsetExpression->needsGeometry() )
)
)
{
Expand Down Expand Up @@ -172,9 +170,9 @@ bool QgsDelimitedTextFeatureIterator::fetchFeature( QgsFeature& feature )
fid = mFeatureIds[mNextId];
}
}
else if ( mNextId < P->mSubsetIndex.size() )
else if ( mNextId < mSource->mSubsetIndex.size() )
{
fid = P->mSubsetIndex[mNextId];
fid = mSource->mSubsetIndex[mNextId];
}
if ( fid < 0 ) break;
mNextId++;
Expand All @@ -200,7 +198,7 @@ bool QgsDelimitedTextFeatureIterator::rewind()
// Skip to first data record
if ( mMode == FileScan )
{
P->mFile->reset();
mSource->mFile->reset();
}
else
{
Expand All @@ -214,7 +212,7 @@ bool QgsDelimitedTextFeatureIterator::close()
if ( mClosed )
return false;

P->mActiveIterators.remove( this );
iteratorClosed();

mFeatureIds = QList<QgsFeatureId>();
mClosed = true;
Expand Down Expand Up @@ -252,7 +250,7 @@ bool QgsDelimitedTextFeatureIterator::nextFeatureInternal( QgsFeature& feature )
{
QStringList tokens;

QgsDelimitedTextFile *file = P->mFile;
QgsDelimitedTextFile *file = mSource->mFile;

// If the iterator is not scanning the file, then it will have requested a specific
// record, so only need to load that one.
Expand All @@ -269,7 +267,6 @@ bool QgsDelimitedTextFeatureIterator::nextFeatureInternal( QgsFeature& feature )
// feature.

feature.setValid( false );
if ( ! P->mValid ) break;

QgsDelimitedTextFile::Status status = file->nextRecord( tokens );
if ( status == QgsDelimitedTextFile::RecordEOF ) break;
Expand All @@ -281,7 +278,7 @@ bool QgsDelimitedTextFeatureIterator::nextFeatureInternal( QgsFeature& feature )

QgsFeatureId fid = file->recordId();

while ( tokens.size() < P->mFieldCount )
while ( tokens.size() < mSource->mFieldCount )
tokens.append( QString::null );

QgsGeometry *geom = 0;
Expand All @@ -290,11 +287,11 @@ bool QgsDelimitedTextFeatureIterator::nextFeatureInternal( QgsFeature& feature )

if ( mLoadGeometry )
{
if ( P->mGeomRep == QgsDelimitedTextProvider::GeomAsWkt )
if ( mSource->mGeomRep == QgsDelimitedTextProvider::GeomAsWkt )
{
geom = loadGeometryWkt( tokens );
}
else if ( P->mGeomRep == QgsDelimitedTextProvider::GeomAsXy )
else if ( mSource->mGeomRep == QgsDelimitedTextProvider::GeomAsXy )
{
geom = loadGeometryXY( tokens );
}
Expand All @@ -308,9 +305,9 @@ bool QgsDelimitedTextFeatureIterator::nextFeatureInternal( QgsFeature& feature )
// At this point the current feature values are valid

feature.setValid( true );
feature.setFields( &P->attributeFields ); // allow name-based attribute lookups
feature.setFields( &mSource->mFields ); // allow name-based attribute lookups
feature.setFeatureId( fid );
feature.initAttributes( P->attributeFields.count() );
feature.initAttributes( mSource->mFields.count() );

if ( geom )
feature.setGeometry( geom );
Expand All @@ -329,16 +326,16 @@ bool QgsDelimitedTextFeatureIterator::nextFeatureInternal( QgsFeature& feature )
}
else
{
for ( int idx = 0; idx < P->attributeFields.count(); ++idx )
for ( int idx = 0; idx < mSource->mFields.count(); ++idx )
fetchAttribute( feature, idx, tokens );
}

// If the iterator hasn't already filtered out the subset, then do it now

if ( mTestSubset )
{
QVariant isOk = P->mSubsetExpression->evaluate( &feature );
if ( P->mSubsetExpression->hasEvalError() ) continue;
QVariant isOk = mSource->mSubsetExpression->evaluate( &feature );
if ( mSource->mSubsetExpression->hasEvalError() ) continue;
if ( ! isOk.toBool() ) continue;
}

Expand All @@ -352,19 +349,19 @@ bool QgsDelimitedTextFeatureIterator::nextFeatureInternal( QgsFeature& feature )

bool QgsDelimitedTextFeatureIterator::setNextFeatureId( qint64 fid )
{
return P->mFile->setNextRecordId(( long ) fid );
return mSource->mFile->setNextRecordId(( long ) fid );
}



QgsGeometry* QgsDelimitedTextFeatureIterator::loadGeometryWkt( const QStringList& tokens )
{
QgsGeometry* geom = 0;
QString sWkt = tokens[P->mWktFieldIndex];
QString sWkt = tokens[mSource->mWktFieldIndex];

geom = P->geomFromWkt( sWkt );
geom = QgsDelimitedTextProvider::geomFromWkt( sWkt, mSource->mWktHasPrefix, mSource->mWktHasZM );

if ( geom && geom->type() != P->mGeometryType )
if ( geom && geom->type() != mSource->mGeometryType )
{
delete geom;
geom = 0;
Expand All @@ -379,10 +376,10 @@ QgsGeometry* QgsDelimitedTextFeatureIterator::loadGeometryWkt( const QStringList

QgsGeometry* QgsDelimitedTextFeatureIterator::loadGeometryXY( const QStringList& tokens )
{
QString sX = tokens[P->mXFieldIndex];
QString sY = tokens[P->mYFieldIndex];
QString sX = tokens[mSource->mXFieldIndex];
QString sY = tokens[mSource->mYFieldIndex];
QgsPoint pt;
bool ok = P->pointFromXY( sX, sY, pt );
bool ok = QgsDelimitedTextProvider::pointFromXY( sX, sY, pt, mSource->mDecimalPoint, mSource->mXyDms );

if ( ok && wantGeometry( pt ) )
{
Expand All @@ -395,12 +392,12 @@ QgsGeometry* QgsDelimitedTextFeatureIterator::loadGeometryXY( const QStringList&

void QgsDelimitedTextFeatureIterator::fetchAttribute( QgsFeature& feature, int fieldIdx, const QStringList& tokens )
{
if ( fieldIdx < 0 || fieldIdx >= P->attributeColumns.count() ) return;
int column = P->attributeColumns[fieldIdx];
if ( fieldIdx < 0 || fieldIdx >= mSource->attributeColumns.count() ) return;
int column = mSource->attributeColumns[fieldIdx];
if ( column < 0 || column >= tokens.count() ) return;
const QString &value = tokens[column];
QVariant val;
switch ( P->attributeFields[fieldIdx].type() )
switch ( mSource->mFields[fieldIdx].type() )
{
case QVariant::Int:
{
Expand All @@ -410,7 +407,7 @@ void QgsDelimitedTextFeatureIterator::fetchAttribute( QgsFeature& feature, int f
if ( ok )
val = QVariant( ivalue );
else
val = QVariant( P->attributeFields[fieldIdx].type() );
val = QVariant( mSource->mFields[fieldIdx].type() );
break;
}
case QVariant::Double:
Expand All @@ -419,13 +416,13 @@ void QgsDelimitedTextFeatureIterator::fetchAttribute( QgsFeature& feature, int f
bool ok = false;
if ( ! value.isEmpty() )
{
if ( P->mDecimalPoint.isEmpty() )
if ( mSource->mDecimalPoint.isEmpty() )
{
dvalue = value.toDouble( &ok );
}
else
{
dvalue = QString( value ).replace( P->mDecimalPoint, "." ).toDouble( &ok );
dvalue = QString( value ).replace( mSource->mDecimalPoint, "." ).toDouble( &ok );
}
}
if ( ok )
Expand All @@ -434,7 +431,7 @@ void QgsDelimitedTextFeatureIterator::fetchAttribute( QgsFeature& feature, int f
}
else
{
val = QVariant( P->attributeFields[fieldIdx].type() );
val = QVariant( mSource->mFields[fieldIdx].type() );
}
break;
}
Expand All @@ -444,3 +441,41 @@ void QgsDelimitedTextFeatureIterator::fetchAttribute( QgsFeature& feature, int f
}
feature.setAttribute( fieldIdx, val );
}

// ------------

QgsDelimitedTextFeatureSource::QgsDelimitedTextFeatureSource( const QgsDelimitedTextProvider* p )
: mGeomRep( p->mGeomRep )
, mSubsetExpression( p->mSubsetExpression )
, mExtent( p->mExtent )
, mUseSpatialIndex( p->mUseSpatialIndex )
, mSpatialIndex( p->mSpatialIndex ? new QgsSpatialIndex( *p->mSpatialIndex ) : 0 )
, mUseSubsetIndex( p->mUseSubsetIndex )
, mSubsetIndex( p->mSubsetIndex )
, mFile( 0 )
, mFields( p->attributeFields )
, mFieldCount( p->mFieldCount )
, mXFieldIndex( p->mXFieldIndex )
, mYFieldIndex( p->mYFieldIndex )
, mWktFieldIndex( p->mWktFieldIndex )
, mWktHasZM( p->mWktHasZM )
, mWktHasPrefix( p->mWktHasPrefix )
, mGeometryType( p->mGeometryType )
, mDecimalPoint( p->mDecimalPoint )
, mXyDms( p->mXyDms )
, attributeColumns( p->attributeColumns )
{
mFile = new QgsDelimitedTextFile();
mFile->setFromUrl( p->mFile->url() );
}

QgsDelimitedTextFeatureSource::~QgsDelimitedTextFeatureSource()
{
delete mSpatialIndex;
delete mFile;
}

QgsFeatureIterator QgsDelimitedTextFeatureSource::getFeatures( const QgsFeatureRequest& request )
{
return QgsFeatureIterator( new QgsDelimitedTextFeatureIterator( this, false, request ) );
}
40 changes: 36 additions & 4 deletions src/providers/delimitedtext/qgsdelimitedtextfeatureiterator.h
Expand Up @@ -19,9 +19,42 @@
#include "qgsfeatureiterator.h"
#include "qgsfeature.h"

class QgsDelimitedTextProvider;
#include "qgsdelimitedtextprovider.h"

class QgsDelimitedTextFeatureIterator : public QgsAbstractFeatureIterator
class QgsDelimitedTextFeatureSource : public QgsAbstractFeatureSource
{
public:
QgsDelimitedTextFeatureSource( const QgsDelimitedTextProvider* p );
~QgsDelimitedTextFeatureSource();

virtual QgsFeatureIterator getFeatures( const QgsFeatureRequest& request );

protected:
QgsDelimitedTextProvider::GeomRepresentationType mGeomRep;
QgsExpression *mSubsetExpression;
QgsRectangle mExtent;
bool mUseSpatialIndex;
QgsSpatialIndex *mSpatialIndex;
bool mUseSubsetIndex;
QList<quintptr> mSubsetIndex;
QgsDelimitedTextFile *mFile;
QgsFields mFields;
int mFieldCount; // Note: this includes field count for wkt field
int mXFieldIndex;
int mYFieldIndex;
int mWktFieldIndex;
bool mWktHasZM;
bool mWktHasPrefix;
QGis::GeometryType mGeometryType;
QString mDecimalPoint;
bool mXyDms;
QList<int> attributeColumns;

friend class QgsDelimitedTextFeatureIterator;
};


class QgsDelimitedTextFeatureIterator : public QgsAbstractFeatureIteratorFromSource<QgsDelimitedTextFeatureSource>
{
enum IteratorMode
{
Expand All @@ -30,7 +63,7 @@ class QgsDelimitedTextFeatureIterator : public QgsAbstractFeatureIterator
FeatureIds
};
public:
QgsDelimitedTextFeatureIterator( QgsDelimitedTextProvider* p, const QgsFeatureRequest& request );
QgsDelimitedTextFeatureIterator( QgsDelimitedTextFeatureSource* source, bool ownSource, const QgsFeatureRequest& request );

~QgsDelimitedTextFeatureIterator();

Expand All @@ -55,7 +88,6 @@ class QgsDelimitedTextFeatureIterator : public QgsAbstractFeatureIterator
QgsGeometry* loadGeometryXY( const QStringList& tokens );
void fetchAttribute( QgsFeature& feature, int fieldIdx, const QStringList& tokens );

QgsDelimitedTextProvider* P;
QList<QgsFeatureId> mFeatureIds;
IteratorMode mMode;
long mNextId;
Expand Down

0 comments on commit 78841d9

Please sign in to comment.