diff --git a/resources/context_help/QgsDelimitedTextSourceSelect-en_US b/resources/context_help/QgsDelimitedTextSourceSelect-en_US index 743e78c71682..c3e6e0ff8f57 100644 --- a/resources/context_help/QgsDelimitedTextSourceSelect-en_US +++ b/resources/context_help/QgsDelimitedTextSourceSelect-en_US @@ -270,6 +270,8 @@ The following options can be added
  • xField=fieldname specifies the name or number (starting at 1) of the field the X coordinate (only applies if wktField is not defined)
  • yField=fieldname specifies the name or number (starting at 1) of the field the Y coordinate (only applies if wktField is not defined)
  • geomType=(auto|point|line|polygon|none) specifies type of geometry for wkt fields, or none to load the file as an attribute-only table. The default is auto.
  • +
  • subset=expression specifies an expression used to identify a subset of the records that will be + used.
  • crs=... specifies the coordinate system to use for the vector layer, in a format accepted by QgsCoordinateReferenceSystem.createFromString (for example "EPSG:4167"). If this is not specified then a dialog box may request this information from the user when the layer is loaded (depending on QGIS CRS settings).
  • diff --git a/src/core/qgsvectorlayer.h b/src/core/qgsvectorlayer.h index 3ee77a5e36cc..770825b726c6 100644 --- a/src/core/qgsvectorlayer.h +++ b/src/core/qgsvectorlayer.h @@ -366,6 +366,10 @@ struct CORE_EXPORT QgsVectorJoinInfo * geomType can also be set to none, in which case the layer is loaded without * geometries. * + * - subset=expression + * + * Defines an expression that will identify a subset of records to display + * * - crs=crsstring * * Defines the coordinate reference system used for the layer. This can be diff --git a/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp b/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp index de17a0625238..a38930d322fa 100644 --- a/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp +++ b/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp @@ -879,205 +879,6 @@ void QgsDelimitedTextProvider::fetchAttribute( QgsFeature& feature, int fieldIdx feature.setAttribute( fieldIdx, val ); } -void QgsDelimitedTextProvider::resetDataSummary() -{ - QgsFeatureIterator fi = getFeatures(QgsFeatureRequest()); - mNumberFeatures = 0; - mExtent = QgsRectangle(); - QgsFeature f; - while( fi.nextFeature(f)) - { - if( mGeometryType != QGis::NoGeometry ) - { - if( mNumberFeatures == 0 ) - { - mExtent = f.geometry()->boundingBox(); - } - else - { - QgsRectangle bbox( f.geometry()->boundingBox()); - mExtent.combineExtentWith( &bbox); - } - } - mNumberFeatures++; - } -} - - -bool QgsDelimitedTextProvider::nextFeature( QgsFeature& feature, QgsDelimitedTextFile *file, const QgsFeatureRequest& request ) -{ - QStringList tokens; - while ( true ) - { - // before we do anything else, assume that there's something wrong with - // the feature - feature.setValid( false ); - QgsDelimitedTextFile::Status status = file->nextRecord( tokens ); - if ( status == QgsDelimitedTextFile::RecordEOF ) break; - if ( status != QgsDelimitedTextFile::RecordOk ) continue; - - int fid = file->recordLineNumber(); - if( request.filterType() == QgsFeatureRequest::FilterFid && fid != request.filterFid()) continue; - if ( recordIsEmpty( tokens ) ) continue; - - while ( tokens.size() < mFieldCount ) - tokens.append( QString::null ); - - QgsGeometry *geom = 0; - - if ( mWktFieldIndex >= 0 ) - { - geom = loadGeometryWkt( tokens, request ); - } - else if ( mXFieldIndex >= 0 && mYFieldIndex >= 0 ) - { - geom = loadGeometryXY( tokens,request ); - } - - if ( !geom && mWkbType != QGis::WKBNoGeometry ) - { - // Already dealt with invalid lines in provider - no need to repeat - // removed code (CC 2013-04-13) ... - // mInvalidLines << line; - // In any case it may be a valid line that is excluded because of - // bounds check... - continue; - } - - // At this point the current feature values are valid - - feature.setValid( true ); - feature.setFields( &attributeFields ); // allow name-based attribute lookups - feature.setFeatureId( fid ); - feature.initAttributes( attributeFields.count() ); - - if ( geom ) - feature.setGeometry( geom ); - - if ( request.flags() & QgsFeatureRequest::SubsetOfAttributes ) - { - const QgsAttributeList& attrs = request.subsetOfAttributes(); - for ( QgsAttributeList::const_iterator i = attrs.begin(); i != attrs.end(); ++i ) - { - int fieldIdx = *i; - fetchAttribute( feature, fieldIdx, tokens ); - } - } - else - { - for ( int idx = 0; idx < attributeFields.count(); ++idx ) - fetchAttribute( feature, idx, tokens ); - } - - // We have a good line, so return - return true; - - } // !mStream->atEnd() - - return false; -} - - -QgsGeometry* QgsDelimitedTextProvider::loadGeometryWkt( const QStringList& tokens, const QgsFeatureRequest& request ) -{ - QgsGeometry* geom = 0; - QString sWkt = tokens[mWktFieldIndex]; - - geom = geomFromWkt( sWkt ); - - if ( geom && geom->type() != mGeometryType ) - { - delete geom; - geom = 0; - } - if ( geom && !boundsCheck( geom, request ) ) - { - delete geom; - geom = 0; - } - return geom; -} - - -QgsGeometry* QgsDelimitedTextProvider::loadGeometryXY( const QStringList& tokens, const QgsFeatureRequest& request ) -{ - QString sX = tokens[mXFieldIndex]; - QString sY = tokens[mYFieldIndex]; - QgsPoint pt; - bool ok = pointFromXY( sX, sY, pt ); - - if ( ok && boundsCheck( pt, request ) ) - { - return QgsGeometry::fromPoint( pt ); - } - return 0; -} - -/** - * Check to see if the point is within the selection rectangle - */ -bool QgsDelimitedTextProvider::boundsCheck( const QgsPoint &pt, const QgsFeatureRequest& request ) -{ - // no selection rectangle or geometry => always in the bounds - if ( request.filterType() != QgsFeatureRequest::FilterRect || ( request.flags() & QgsFeatureRequest::NoGeometry ) ) - return true; - - return request.filterRect().contains( pt ); -} - -/** - * Check to see if the geometry is within the selection rectangle - */ -bool QgsDelimitedTextProvider::boundsCheck( QgsGeometry *geom, const QgsFeatureRequest& request ) -{ - // no selection rectangle or geometry => always in the bounds - if ( request.filterType() != QgsFeatureRequest::FilterRect || ( request.flags() & QgsFeatureRequest::NoGeometry ) ) - return true; - - if ( request.flags() & QgsFeatureRequest::ExactIntersect ) - return geom->intersects( request.filterRect() ); - else - return geom->boundingBox().intersects( request.filterRect() ); -} - - -void QgsDelimitedTextProvider::fetchAttribute( QgsFeature& feature, int fieldIdx, const QStringList& tokens ) -{ - if( fieldIdx < 0 || fieldIdx >= attributeColumns.count()) return; - int column = attributeColumns[fieldIdx]; - if( column < 0 || column >= tokens.count()) return; - const QString &value = tokens[column]; - QVariant val; - switch ( attributeFields[fieldIdx].type() ) - { - case QVariant::Int: - if ( value.isEmpty() ) - val = QVariant( attributeFields[fieldIdx].type() ); - else - val = QVariant( value ); - break; - case QVariant::Double: - if ( value.isEmpty() ) - { - val = QVariant( attributeFields[fieldIdx].type() ); - } - else if ( mDecimalPoint.isEmpty() ) - { - val = QVariant( value.toDouble() ); - } - else - { - val = QVariant( QString( value ).replace( mDecimalPoint, "." ).toDouble() ); - } - break; - default: - val = QVariant( value ); - break; - } - feature.setAttribute( fieldIdx, val ); -} - - // Return the extent of the layer QgsRectangle QgsDelimitedTextProvider::extent() { diff --git a/tests/src/python/test_qgsdelimitedtextprovider.py b/tests/src/python/test_qgsdelimitedtextprovider.py index 98480616c7c9..782d72f5dea2 100644 --- a/tests/src/python/test_qgsdelimitedtextprovider.py +++ b/tests/src/python/test_qgsdelimitedtextprovider.py @@ -1692,6 +1692,49 @@ def test_033_filter_attributes(self): ] runTest(description,wanted,filename,requests,**params) + def test_034_substring_test(self): + description='CSV file parsing' + filename='test.csv' + params={'geomType': 'none', 'subset': 'id % 2 = 1', 'type': 'csv'} + requests=None + if rebuildTests: + createTest(description,filename,requests,**params) + assert False,"Set rebuildTests to False to run delimited text tests" + wanted={} + wanted['uri']=u'file://file?geomType=none&type=csv&subset=id%20%25%202%20%3D%201' + wanted['data']={ + 2L: { + 'id': u'1', + 'description': u'Basic unquoted record', + 'data': u'Some data', + 'info': u'Some info', + 'field_5': u'', + '#fid': 2L, + '#geometry': 'None', + }, + 4L: { + 'id': u'3', + 'description': u'Escaped quotes', + 'data': u'Quoted "citation" data', + 'info': u'Unquoted', + 'field_5': u'', + '#fid': 4L, + '#geometry': 'None', + }, + 9L: { + 'id': u'5', + 'description': u'Extra fields', + 'data': u'data', + 'info': u'info', + 'field_5': u'message', + '#fid': 9L, + '#geometry': 'None', + }, + } + wanted['log']=[ + ] + runTest(description,wanted,filename,requests,**params) + #END if __name__ == '__main__':