Index: src/plugins/delimited_text/qgsdelimitedtextplugin.cpp =================================================================== --- src/plugins/delimited_text/qgsdelimitedtextplugin.cpp (revision 14778) +++ src/plugins/delimited_text/qgsdelimitedtextplugin.cpp (working copy) @@ -102,7 +102,7 @@ setCurrentTheme( "" ); myQActionPointer->setWhatsThis( tr( "Add a delimited text file as a map layer. " "The file must have a header row containing the field names. " - "X and Y fields are required and must contain coordinates in decimal units." ) ); + "The file must either contain X and Y fields with coordinates in decimal units or a WKT field." ) ); // Connect the action to the run connect( myQActionPointer, SIGNAL( triggered() ), this, SLOT( run() ) ); // Add the icon to the toolbar Index: src/plugins/delimited_text/qgsdelimitedtextplugingui.cpp =================================================================== --- src/plugins/delimited_text/qgsdelimitedtextplugingui.cpp (revision 14778) +++ src/plugins/delimited_text/qgsdelimitedtextplugingui.cpp (working copy) @@ -33,7 +33,7 @@ setupUi( this ); pbnOK = buttonBox->button( QDialogButtonBox::Ok ); - enableAccept(); + updateFieldsAndEnable(); // at startup, fetch the last used delimiter and directory from // settings @@ -58,22 +58,23 @@ cmbXField->setDisabled( true ); cmbYField->setDisabled( true ); + cmbWktField->setDisabled( true ); - connect( txtFilePath, SIGNAL( textChanged( QString ) ), this, SLOT( enableAccept() ) ); + connect( txtFilePath, SIGNAL( textChanged( QString ) ), this, SLOT( updateFieldsAndEnable() ) ); - connect( delimiterSelection, SIGNAL( toggled( bool ) ), this, SLOT( enableAccept() ) ); - connect( delimiterPlain, SIGNAL( toggled( bool ) ), this, SLOT( enableAccept() ) ); - connect( delimiterRegexp, SIGNAL( toggled( bool ) ), this, SLOT( enableAccept() ) ); + connect( delimiterSelection, SIGNAL( toggled( bool ) ), this, SLOT( updateFieldsAndEnable() ) ); + connect( delimiterPlain, SIGNAL( toggled( bool ) ), this, SLOT( updateFieldsAndEnable() ) ); + connect( delimiterRegexp, SIGNAL( toggled( bool ) ), this, SLOT( updateFieldsAndEnable() ) ); - connect( cbxDelimSpace, SIGNAL( stateChanged( int ) ), this, SLOT( enableAccept() ) ); - connect( cbxDelimTab, SIGNAL( stateChanged( int ) ), this, SLOT( enableAccept() ) ); - connect( cbxDelimSemicolon, SIGNAL( stateChanged( int ) ), this, SLOT( enableAccept() ) ); - connect( cbxDelimComma, SIGNAL( stateChanged( int ) ), this, SLOT( enableAccept() ) ); - connect( cbxDelimColon, SIGNAL( stateChanged( int ) ), this, SLOT( enableAccept() ) ); + connect( cbxDelimSpace, SIGNAL( stateChanged( int ) ), this, SLOT( updateFieldsAndEnable() ) ); + connect( cbxDelimTab, SIGNAL( stateChanged( int ) ), this, SLOT( updateFieldsAndEnable() ) ); + connect( cbxDelimSemicolon, SIGNAL( stateChanged( int ) ), this, SLOT( updateFieldsAndEnable() ) ); + connect( cbxDelimComma, SIGNAL( stateChanged( int ) ), this, SLOT( updateFieldsAndEnable() ) ); + connect( cbxDelimColon, SIGNAL( stateChanged( int ) ), this, SLOT( updateFieldsAndEnable() ) ); - connect( txtDelimiter, SIGNAL( editingFinished() ), this, SLOT( enableAccept() ) ); + connect( txtDelimiter, SIGNAL( editingFinished() ), this, SLOT( updateFieldsAndEnable() ) ); - connect( rowCounter, SIGNAL( valueChanged( int ) ), this, SLOT( enableAccept() ) ); + connect( rowCounter, SIGNAL( valueChanged( int ) ), this, SLOT( updateFieldsAndEnable() ) ); } QgsDelimitedTextPluginGui::~QgsDelimitedTextPluginGui() @@ -103,12 +104,23 @@ .arg( txtDelimiter->text() ) .arg( delimiterType ); - if ( !cmbXField->currentText().isEmpty() && !cmbYField->currentText().isEmpty() ) - { - uri += QString( "&xField=%1&yField=%2" ) - .arg( cmbXField->currentText() ) - .arg( cmbYField->currentText() ); - } + if( geomTypeXY->isChecked()) + { + if ( !cmbXField->currentText().isEmpty() && !cmbYField->currentText().isEmpty() ) + { + uri += QString( "&xField=%1&yField=%2" ) + .arg( cmbXField->currentText() ) + .arg( cmbYField->currentText() ); + } + } + else + { + if( ! cmbWktField->currentText().isEmpty() ) + { + uri += QString( "&wktField=%1" ) + .arg( cmbWktField->currentText() ); + } + } int skipLines = rowCounter->value(); if ( skipLines > 0 ) @@ -177,6 +189,31 @@ return fieldList; } +bool QgsDelimitedTextPluginGui::haveValidFileAndDelimiters() +{ + + bool valid = true; + // If there is no valid file or field delimiters than cannot determine fields + if ( txtFilePath->text().isEmpty() || !QFile( txtFilePath->text() ).exists() ) + { + valid = false; + } + else if ( delimiterSelection->isChecked() ) + { + valid = + cbxDelimSpace->isChecked() || + cbxDelimTab->isChecked() || + cbxDelimSemicolon->isChecked() || + cbxDelimComma->isChecked() || + cbxDelimColon->isChecked(); + } + else + { + valid = !txtDelimiter->text().isEmpty(); + } + return valid; +} + void QgsDelimitedTextPluginGui::updateFieldLists() { // Update the x and y field dropdown boxes @@ -184,17 +221,28 @@ disconnect( cmbXField, SIGNAL( currentIndexChanged( int ) ), this, SLOT( enableAccept() ) ); disconnect( cmbYField, SIGNAL( currentIndexChanged( int ) ), this, SLOT( enableAccept() ) ); + disconnect( cmbWktField, SIGNAL( currentIndexChanged( int ) ), this, SLOT( enableAccept() ) ); + disconnect(geomTypeXY, SIGNAL(toggled(bool)), cmbXField, SLOT(setEnabled(bool))); + disconnect(geomTypeXY, SIGNAL(toggled(bool)), cmbYField, SLOT(setEnabled(bool))); + disconnect(geomTypeXY, SIGNAL(toggled(bool)), cmbWktField, SLOT(setDisabled(bool))); QString columnX = cmbXField->currentText(); QString columnY = cmbYField->currentText(); + QString columnWkt = cmbWktField->currentText(); // clear the field lists cmbXField->clear(); cmbYField->clear(); + cmbWktField->clear(); + geomTypeXY->setEnabled( false ); + geomTypeWKT->setEnabled( false ); cmbXField->setEnabled( false ); cmbYField->setEnabled( false ); + cmbWktField->setEnabled( false ); + if( ! haveValidFileAndDelimiters()) return; + QFile file( txtFilePath->text() ); if ( !file.open( QIODevice::ReadOnly ) ) return; @@ -220,6 +268,8 @@ // // We don't know anything about a text based field other // than its name. All fields are assumed to be text + bool haveFields = false; + foreach( QString field, fieldList ) { if (( field.left( 1 ) == "'" || field.left( 1 ) == "\"" ) && @@ -233,10 +283,28 @@ cmbXField->addItem( field ); cmbYField->addItem( field ); + cmbWktField->addItem( field ); + haveFields = true; } - cmbXField->setEnabled( cmbXField->count() > 0 ); - cmbYField->setEnabled( cmbYField->count() > 0 ); + int indexWkt = -1; + if( ! columnWkt.isEmpty() ) + { + indexWkt = cmbWktField->findText( columnWkt ); + } + if( indexWkt < 0 ) + { + indexWkt = cmbWktField->findText("wkt", Qt::MatchContains ); + } + if( indexWkt < 0 ) + { + indexWkt = cmbWktField->findText("geometry", Qt::MatchContains ); + } + if( indexWkt < 0 ) + { + indexWkt = cmbWktField->findText("shape", Qt::MatchContains ); + } + cmbWktField->setCurrentIndex( indexWkt); int indexX = -1; if ( !columnX.isEmpty() ) @@ -274,9 +342,29 @@ cmbYField->setCurrentIndex( indexY ); - connect( cmbXField, SIGNAL( currentIndexChanged( int ) ), this, SLOT( enableAccept() ) ); - connect( cmbYField, SIGNAL( currentIndexChanged( int ) ), this, SLOT( enableAccept() ) ); + bool isXY = (geomTypeXY->isChecked() && indexX >= 0 && indexY >= 0) || indexWkt < 0; + + geomTypeXY->setChecked( isXY ); + geomTypeWKT->setChecked( ! isXY ); + + if( haveFields ) + { + geomTypeXY->setEnabled(true); + geomTypeWKT->setEnabled(true); + cmbXField->setEnabled( isXY ); + cmbYField->setEnabled( isXY ); + cmbWktField->setEnabled( ! isXY ); + + + connect( cmbXField, SIGNAL( currentIndexChanged( int ) ), this, SLOT( enableAccept() ) ); + connect( cmbYField, SIGNAL( currentIndexChanged( int ) ), this, SLOT( enableAccept() ) ); + connect( cmbWktField, SIGNAL( currentIndexChanged( int ) ), this, SLOT( enableAccept() ) ); + connect(geomTypeXY, SIGNAL(toggled(bool)), cmbXField, SLOT(setEnabled(bool))); + connect(geomTypeXY, SIGNAL(toggled(bool)), cmbYField, SLOT(setEnabled(bool))); + connect(geomTypeXY, SIGNAL(toggled(bool)), cmbWktField, SLOT(setDisabled(bool))); + } + // clear the sample text box tblSample->clear(); @@ -324,35 +412,30 @@ txtFilePath->setText( s ); } +void QgsDelimitedTextPluginGui::updateFieldsAndEnable() +{ + updateFieldLists(); + enableAccept(); +} + void QgsDelimitedTextPluginGui::enableAccept() { - bool enabled = false; - if ( txtFilePath->text().isEmpty() || !QFile( txtFilePath->text() ).exists() ) - { - enabled = false; - } - else if ( delimiterSelection->isChecked() ) - { - enabled = - cbxDelimSpace->isChecked() || - cbxDelimTab->isChecked() || - cbxDelimSemicolon->isChecked() || - cbxDelimComma->isChecked() || - cbxDelimColon->isChecked(); - } - else - { - enabled = !txtDelimiter->text().isEmpty(); - } + // If the geometry type field is enabled then there must be + // a valid file, and it must be + bool enabled = haveValidFileAndDelimiters(); - if ( enabled ) { - updateFieldLists(); - enabled = ( cmbXField->currentText().isEmpty() && cmbYField->currentText().isEmpty() ) - || ( !cmbXField->currentText().isEmpty() && !cmbYField->currentText().isEmpty() && cmbXField->currentText() != cmbYField->currentText() ); + if( geomTypeXY->isChecked() ) + { + enabled = !( cmbXField->currentText().isEmpty() || cmbYField->currentText().isEmpty() || cmbXField->currentText() == cmbYField->currentText() ); + } + else + { + enabled = !cmbWktField->currentText().isEmpty(); + } } pbnOK->setEnabled( enabled ); Index: src/plugins/delimited_text/qgsdelimitedtextplugingui.h =================================================================== --- src/plugins/delimited_text/qgsdelimitedtextplugingui.h (revision 14778) +++ src/plugins/delimited_text/qgsdelimitedtextplugingui.h (working copy) @@ -34,6 +34,7 @@ QStringList splitLine( QString line ); private: + bool haveValidFileAndDelimiters(); void updateFieldLists(); void getOpenFileName(); @@ -47,6 +48,7 @@ void on_btnBrowseForFile_clicked(); public slots: + void updateFieldsAndEnable(); void enableAccept(); signals: Index: src/plugins/delimited_text/qgsdelimitedtextpluginguibase.ui =================================================================== --- src/plugins/delimited_text/qgsdelimitedtextpluginguibase.ui (revision 14778) +++ src/plugins/delimited_text/qgsdelimitedtextpluginguibase.ui (working copy) @@ -6,8 +6,8 @@ 0 0 - 532 - 513 + 589 + 522 @@ -110,7 +110,7 @@ true - + @@ -121,70 +121,7 @@ - - - - Semicolon - - - - - - - Tab - - - - - - - Space - - - true - - - - - - - Comma - - - - - - - Colon - - - - - - - false - - - - 0 - 0 - - - - - 32767 - 32767 - - - - Delimiter to use when splitting fields in the text file. The delimiter can be more than one character. - - - Delimiter to use when splitting fields in the delimited text file. The delimiter can be 1 or more characters in length. - - - - + @@ -222,6 +159,73 @@ + + + + + + Tab + + + + + + + Space + + + true + + + + + + + Comma + + + + + + + Semicolon + + + + + + + Colon + + + + + + + + + false + + + + 0 + 0 + + + + + 32767 + 32767 + + + + Delimiter to use when splitting fields in the text file. The delimiter can be more than one character. + + + Delimiter to use when splitting fields in the delimited text file. The delimiter can be 1 or more characters in length. + + + @@ -236,7 +240,7 @@ 0 - + @@ -276,17 +280,84 @@ - - - + + + + 9 + + + + + false + + + The file contains a well known text geometry field + + + WKT field + + + + <p align="right">X field</p> - - + + + + + + false + + + + 0 + 0 + + + + + 120 + 0 + + + + Name of the field containing x values + + + Name of the field containing x values. Choose a field from the list. The list is generated by parsing the header row of the delimited text file. + + + true + + + + + + + + + false + + + The file contains X and Y coordinate columns + + + X Y fields + + + true + + + + + + + false + 0 @@ -300,25 +371,28 @@ - Name of the field containing x values + Name of the field containing y values - Name of the field containing x values. Choose a field from the list. The list is generated by parsing the header row of the delimited text file. + Name of the field containing y values. Choose a field from the list. The list is generated by parsing the header row of the delimited text file. true - + <p align="right">Y field</p> - - + + + + false + 0 @@ -376,8 +450,25 @@ + txtFilePath + btnBrowseForFile + txtLayerName + delimiterSelection + cbxDelimTab + cbxDelimSpace + cbxDelimComma + cbxDelimSemicolon + cbxDelimColon + delimiterPlain + delimiterRegexp + txtDelimiter + rowCounter + geomTypeXY cmbXField cmbYField + geomTypeWKT + cmbWktField + tblSample buttonBox @@ -395,8 +486,8 @@ 121 - 282 - 115 + 445 + 113 @@ -411,8 +502,8 @@ 110 - 277 - 149 + 286 + 113 @@ -427,8 +518,8 @@ 109 - 281 - 175 + 445 + 138 @@ -443,8 +534,8 @@ 114 - 293 - 203 + 286 + 138 @@ -459,8 +550,8 @@ 114 - 118 - 218 + 397 + 165 @@ -475,8 +566,8 @@ 113 - 301 - 225 + 531 + 138 Index: src/providers/delimitedtext/qgsdelimitedtextprovider.cpp =================================================================== --- src/providers/delimitedtext/qgsdelimitedtextprovider.cpp (revision 14778) +++ src/providers/delimitedtext/qgsdelimitedtextprovider.cpp (working copy) @@ -34,6 +34,7 @@ #include "qgsdataprovider.h" #include "qgsfeature.h" #include "qgsfield.h" +#include "qgsgeometry.h" #include "qgslogger.h" #include "qgsmessageoutput.h" #include "qgsrectangle.h" @@ -132,8 +133,12 @@ QgsDelimitedTextProvider::QgsDelimitedTextProvider( QString uri ) : QgsVectorDataProvider( uri ), - mXFieldIndex( -1 ), mYFieldIndex( -1 ), - mShowInvalidLines( true ), mWkbType( QGis::WKBPoint ) + mHasWktField( false ), mFieldCount(0), + mXFieldIndex( -1 ), mYFieldIndex( -1 ), + mWktFieldIndex( -1 ), mWktHasZM( false), + mWktZMRegexp("\\s+(?:z|m|zm)(?=\\s*\\()",Qt::CaseInsensitive), + mWktCrdRegexp("(\\-?\\d+(?:\\.\\d*)?\\s+\\-?\\d+(?:\\.\\d*)?)\\s[\\s\\d\\.\\-]+"), + mShowInvalidLines( true ), mWkbType( QGis::WKBNoGeometry ) { // Get the file name and mDelimiter out of the uri mFileName = uri.left( uri.indexOf( "?" ) ); @@ -147,6 +152,8 @@ mDelimiter = temp.size() ? temp[0].mid( temp[0].indexOf( "=" ) + 1 ) : ""; temp = parameters.filter( "delimiterType=" ); mDelimiterType = temp.size() ? temp[0].mid( temp[0].indexOf( "=" ) + 1 ) : ""; + temp = parameters.filter( "wktField=" ); + QString wktField = temp.size() ? temp[0].mid( temp[0].indexOf( "=" ) + 1 ) : ""; temp = parameters.filter( "xField=" ); QString xField = temp.size() ? temp[0].mid( temp[0].indexOf( "=" ) + 1 ) : ""; temp = parameters.filter( "yField=" ); @@ -157,8 +164,12 @@ mFileName = QUrl::fromPercentEncoding( mFileName.toUtf8() ); mDelimiter = QUrl::fromPercentEncoding( mDelimiter.toUtf8() ); mDelimiterType = QUrl::fromPercentEncoding( mDelimiterType.toUtf8() ); + wktField = QUrl::fromPercentEncoding( wktField.toUtf8() ); xField = QUrl::fromPercentEncoding( xField.toUtf8() ); yField = QUrl::fromPercentEncoding( yField.toUtf8() ); + + mHasWktField = wktField != ""; + skipLines = QUrl::fromPercentEncoding( skipLines.toUtf8() ); mSkipLines = skipLines.toInt(); @@ -167,6 +178,7 @@ QgsDebugMsg( "Delimited text file is: " + mFileName ); QgsDebugMsg( "Delimiter is: " + mDelimiter ); QgsDebugMsg( "Delimiter type is: " + mDelimiterType ); + QgsDebugMsg( "wktField is: " + xField ); QgsDebugMsg( "xField is: " + xField ); QgsDebugMsg( "yField is: " + yField ); QgsDebugMsg( "skipLines is: " + QString::number( mSkipLines ) ); @@ -218,7 +230,6 @@ QString line; mNumberFeatures = 0; int lineNumber = 0; - bool firstPoint = true; bool hasFields = false; while ( !mStream->atEnd() ) { @@ -236,101 +247,153 @@ // fields vector QStringList fieldList = splitLine( line ); + mFieldCount = fieldList.count(); + // We don't know anything about a text based field other // than its name. All fields are assumed to be text int fieldPos = 0; - for ( QStringList::Iterator it = fieldList.begin(); it != fieldList.end(); ++it ) - { - QString field = *it; + for( int column = 0; column < mFieldCount; column++ ) + { + QString field = fieldList[column]; if ( field.length() > 0 ) { - // for now, let's set field type as text - attributeFields[fieldPos] = QgsField( *it, QVariant::String, "Text" ); // check to see if this field matches either the x or y field - if ( xField == *it ) + if ( wktField == field ) { - QgsDebugMsg( "Found x field: " + ( *it ) ); - mXFieldIndex = fieldPos; + QgsDebugMsg( "Found wkt field: " + ( field ) ); + mWktFieldIndex = column; } - else if ( yField == *it ) + else if ( xField == field ) { - QgsDebugMsg( "Found y field: " + ( *it ) ); - mYFieldIndex = fieldPos; + QgsDebugMsg( "Found x field: " + ( field ) ); + mXFieldIndex = column; } + else if ( yField == field ) + { + QgsDebugMsg( "Found y field: " + ( field ) ); + mYFieldIndex = column; + } - QgsDebugMsg( "Adding field: " + ( *it ) ); + // WKT geometry field won't be displayed in attribute tables + if( column == mWktFieldIndex ) continue; + + QgsDebugMsg( "Adding field: " + ( field ) ); // assume that the field could be integer or double + // for now, let's set field type as text + attributeColumns.append(column); + attributeFields[fieldPos] = QgsField( field, QVariant::String, "Text" ); couldBeInt.insert( fieldPos, true ); couldBeDouble.insert( fieldPos, true ); fieldPos++; } } + if( mWktFieldIndex >= 0 ) { mXFieldIndex = -1; mYFieldIndex=-1; } QgsDebugMsg( "Field count for the delimited text file is " + QString::number( attributeFields.size() ) ); hasFields = true; } - else //field names already read + else // hasFields == true - field names already read { - mNumberFeatures++; // split the line on the delimiter QStringList parts = splitLine( line ); // Skip malformed lines silently. Report line number with nextFeature() - if ( attributeFields.size() != parts.size() ) + if ( parts.size() != mFieldCount ) { continue; } - // Get the x and y values, first checking to make sure they - // aren't null. - QString sX, sY; - if ( mXFieldIndex >= 0 && mYFieldIndex >= 0 ) - { - sX = parts[mXFieldIndex]; - sY = parts[mYFieldIndex]; - } + if( mHasWktField && mWktFieldIndex >= 0 ) + { + // Get the wkt - confirm it is valid, get the type, and + // if compatible with the rest of file, add to the extents - bool xOk = true; - bool yOk = true; - double x = sX.toDouble( &xOk ); - double y = sY.toDouble( &yOk ); + QString sWkt = parts[mWktFieldIndex]; + QgsGeometry *geom = 0; + try + { + if( ! mWktHasZM && sWkt.indexOf(mWktZMRegexp) >= 0 ) mWktHasZM = true; + if( mWktHasZM ) + { + sWkt.remove(mWktZMRegexp).replace(mWktCrdRegexp,"\\1"); + } + geom = QgsGeometry::fromWkt(sWkt); + } + catch(...) + { + geom = 0; + } - if ( xOk && yOk ) - { - if ( !firstPoint ) - { - mExtent.combineExtentWith( x, y ); - } - else - { - // Extent for the first point is just the first point - mExtent.set( x, y, x, y ); - firstPoint = false; - } - } + if( geom ) + { + QGis::WkbType type = geom->wkbType(); + if( type != QGis::WKBNoGeometry ) + { + if( mNumberFeatures == 0 ) + { + mNumberFeatures++; + mWkbType = type; + mExtent = geom->boundingBox(); + } + else if( type == mWkbType ) + { + mNumberFeatures++; + QgsRectangle bbox( geom->boundingBox()); + mExtent.combineExtentWith( &bbox ); + } + } + delete geom; + } + } - int i = 0; - for ( QStringList::iterator it = parts.begin(); it != parts.end(); ++it, ++i ) - { + else if( ! mHasWktField && mXFieldIndex >= 0 && mYFieldIndex >= 0 ) + { + + // Get the x and y values, first checking to make sure they + // aren't null. + + QString sX = parts[mXFieldIndex]; + QString sY = parts[mYFieldIndex]; + + bool xOk = true; + bool yOk = true; + double x = sX.toDouble( &xOk ); + double y = sY.toDouble( &yOk ); + + if ( xOk && yOk ) + { + if ( mNumberFeatures > 0 ) + { + mExtent.combineExtentWith( x, y ); + } + else + { + // Extent for the first point is just the first point + mExtent.set( x, y, x, y ); + mWkbType = QGis::WKBPoint; + } + mNumberFeatures++; + } + } + + for( int i = 0; i < attributeFields.size(); i++ ) + { + QString &value = parts[attributeColumns[i]]; + if( value.isEmpty()) continue; // try to convert attribute values to integer and double - if ( couldBeInt[i] && !it->isEmpty() ) + if ( couldBeInt[i] ) { - it->toInt( &couldBeInt[i] ); + value.toInt( &couldBeInt[i] ); } - if ( couldBeDouble[i] && !it->isEmpty() ) + if ( couldBeDouble[i] ) { - it->toDouble( &couldBeDouble[i] ); + value.toDouble( &couldBeDouble[i] ); } } } } - if ( mXFieldIndex < 0 || mYFieldIndex < 0 ) - { - mWkbType = QGis::WKBNoGeometry; - } - // now it's time to decide the types for the fields for ( QgsFieldMap::iterator it = attributeFields.begin(); it != attributeFields.end(); ++it ) { @@ -379,94 +442,86 @@ // lex the tokens from the current data line QStringList tokens = splitLine( line ); - bool xOk = false; - bool yOk = false; - bool geometryOk = false; + QgsGeometry *geom = 0; - // Skip indexing malformed lines. - if ( mXFieldIndex < 0 || mYFieldIndex < 0 ) - { - geometryOk = false; - } - else if ( attributeFields.size() == tokens.size() ) - { - x = tokens[mXFieldIndex].toDouble( &xOk ); - y = tokens[mYFieldIndex].toDouble( &yOk ); - geometryOk = ( xOk && yOk ); - } + if( mHasWktField && mWktFieldIndex >= 0 ) + { + try + { + QString &sWkt = tokens[mWktFieldIndex]; + if( mWktHasZM ) + { + sWkt.remove(mWktZMRegexp).replace(mWktCrdRegexp,"\\1"); + } - // Give every valid line in the file an id, even if it's not - // in the current extent or bounds. - ++mFid; // increment to next feature ID + geom = QgsGeometry::fromWkt(sWkt); + } + catch(...) + { + geom = 0; + } - // skip the feature if it's out of current bounds - if ( ! boundsCheck( x, y ) ) - continue; + if( geom && geom->wkbType() != mWkbType ) + { + delete geom; + geom = 0; + } + mFid++; + if( ! boundsCheck(geom)) + { + delete geom; + geom = 0; + } + } + else if( ! mHasWktField && mXFieldIndex >= 0 && mYFieldIndex >= 0 ) + { + bool xOk, yOk; + double x = tokens[mXFieldIndex].toDouble(&xOk); + double y = tokens[mYFieldIndex].toDouble(&yOk); + if( xOk && yOk ) + { + mFid++; + if( boundsCheck(x,y) ) + { + geom = QgsGeometry::fromPoint(QgsPoint(x,y)); + } + } + } - // at this point, one way or another, the current feature values - // are valid - feature.setValid( true ); + // If no valid geometry skip to the next line - feature.setFeatureId( mFid ); + if( ! geom ) continue; - QByteArray buffer; - QDataStream s( &buffer, static_cast( QIODevice::WriteOnly ) ); // open on buffers's data + // At this point the current feature values are valid - switch ( QgsApplication::endian() ) - { - case QgsApplication::NDR : - // we're on a little-endian platform, so tell the data - // stream to use that - s.setByteOrder( QDataStream::LittleEndian ); - s << ( quint8 )1; // 1 is for little-endian - break; - case QgsApplication::XDR : - // don't change byte order since QDataStream is big endian by default - s << ( quint8 )0; // 0 is for big-endian - break; - default : - QgsDebugMsg( "unknown endian" ); - //delete [] geometry; - return false; - } + feature.setValid( true ); - s << ( quint32 )QGis::WKBPoint; - s << x; - s << y; + feature.setFeatureId( mFid ); - unsigned char* geometry = 0; - if ( geometryOk ) - { - geometry = new unsigned char[buffer.size()]; - memcpy( geometry, buffer.data(), buffer.size() ); - feature.setGeometryAndOwnership( geometry, sizeof( wkbPoint ) ); - } - else - { - feature.setGeometryAndOwnership( 0, 0 ); - } + feature.setGeometry( geom ); for ( QgsAttributeList::const_iterator i = mAttributesToFetch.begin(); i != mAttributesToFetch.end(); ++i ) { + QString &value = tokens[attributeColumns[*i]]; QVariant val; switch ( attributeFields[*i].type() ) { case QVariant::Int: - if ( !tokens[*i].isEmpty() ) - val = QVariant( tokens[*i].toInt() ); + if ( !value.isEmpty() ) + val = QVariant( value ); else val = QVariant( attributeFields[*i].type() ); break; case QVariant::Double: - if ( !tokens[*i].isEmpty() ) - val = QVariant( tokens[*i].toDouble() ); + if ( !value.isEmpty() ) + val = QVariant( value.toDouble() ); else val = QVariant( attributeFields[*i].type() ); break; default: - val = QVariant( tokens[*i] ); + val = QVariant( value ); break; } feature.addAttribute( *i, val ); @@ -589,10 +644,20 @@ if ( mSelectionRectangle.isEmpty() || !mFetchGeom ) return true; - return ( x <= mSelectionRectangle.xMaximum() ) && ( x >= mSelectionRectangle.xMinimum() ) && - ( y <= mSelectionRectangle.yMaximum() ) && ( y >= mSelectionRectangle.yMinimum() ); + return mSelectionRectangle.contains( QgsPoint(x,y) ); } +/** + * Check to see if the geometry is within the selection rectangle + */ +bool QgsDelimitedTextProvider::boundsCheck( QgsGeometry *geom ) +{ + // no selection rectangle or geometry => always in the bounds + if ( mSelectionRectangle.isEmpty() || !mFetchGeom ) + return true; + return geom->boundingBox().intersects( mSelectionRectangle ); +} + int QgsDelimitedTextProvider::capabilities() const { return NoCapabilities; @@ -606,8 +671,6 @@ } - - QString QgsDelimitedTextProvider::name() const { return TEXT_PROVIDER_KEY; Index: src/providers/delimitedtext/qgsdelimitedtextprovider.h =================================================================== --- src/providers/delimitedtext/qgsdelimitedtextprovider.h (revision 14778) +++ src/providers/delimitedtext/qgsdelimitedtextprovider.h (working copy) @@ -166,12 +166,19 @@ bool boundsCheck( double x, double y ); + /** + * Check to see if a geometry overlaps the selection + * rectangle + * @param geom geometry to test against bounds + * @param y Y value of point + * @return True if point is within the rectangle + */ + bool boundsCheck( QgsGeometry *geom ); - - private: //! Fields + QList attributeColumns; QgsFieldMap attributeFields; QgsAttributeList mAttributesToFetch; @@ -181,9 +188,21 @@ QRegExp mDelimiterRegexp; QString mDelimiterType; + bool mHasWktField; + int mFieldCount; // Note: this includes field count for wkt field int mXFieldIndex; int mYFieldIndex; + int mWktFieldIndex; + // Handling of WKT types with .. Z, .. M, and .. ZM geometries (ie + // Z values and/or measures). mWktZMRegexp is used to test for and + // remove the Z or M fields, and mWktCrdRegexp is used to remove the + // extra coordinate values. + + bool mWktHasZM; + QRegExp mWktZMRegexp; + QRegExp mWktCrdRegexp; + //! Layer extent QgsRectangle mExtent; @@ -220,7 +239,7 @@ }; wkbPoint mWKBpt; - QGis::WkbType mWkbType; //can be WKBPoint or NoGeometry + QGis::WkbType mWkbType; QString readLine( QTextStream *stream ); QStringList splitLine( QString line );