Index: python/core/qgscoordinatereferencesystem.sip =================================================================== --- python/core/qgscoordinatereferencesystem.sip (revision 15071) +++ python/core/qgscoordinatereferencesystem.sip (working copy) @@ -23,11 +23,12 @@ ~QgsCoordinateReferenceSystem(); - /*! - * Constructs a CRS object from a Wkt string - * @param theWkt A String containing a valid Wkt def + /*! + * Constructs a CRS object from a string definition as defined in the createFromString + * member function (by default a WKT definition). + * @param theDefinition A String containing a coordinate reference system definition. */ - explicit QgsCoordinateReferenceSystem(QString theWkt); + explicit QgsCoordinateReferenceSystem( QString theDefinition ); /*! Use this constructor when you want to create a CRS object using * a postgis SRID, an Epsg Id id or a QGIS CRS_ID. @@ -38,7 +39,7 @@ // Misc helper functions ----------------------- - void createFromId(const long theId, CrsType theType=PostgisCrsId); + bool createFromId(const long theId, CrsType theType=PostgisCrsId); /** * \brief Set up this CRS from the given OGC CRS @@ -70,6 +71,15 @@ * @return bool TRUE if sucess else false */ bool createFromWkt(const QString theWkt); + + /*! Set up this srs from a string definition, by default a WKT definition. Otherwise + * the string defines a authority, followed by a colon, followed by the definition. + * The authority can be one of "epsg", "postgis", "internal" for integer definitions, + * and "wkt" or "proj4" for string definitions. The implementation of each authority + * uses the corresponding createFrom... function. + * @param theDefinition A String containing a coordinate reference system definition. + */ + bool createFromString( const QString theDefinition ); /*! Set up this srs by fetching the appropriate information from the * sqlite backend. First the system level read only srs.db will be checked Index: src/core/qgscoordinatereferencesystem.cpp =================================================================== --- src/core/qgscoordinatereferencesystem.cpp (revision 15071) +++ src/core/qgscoordinatereferencesystem.cpp (working copy) @@ -51,13 +51,13 @@ mCRS = OSRNewSpatialReference( NULL ); } -QgsCoordinateReferenceSystem::QgsCoordinateReferenceSystem( QString theWkt ) +QgsCoordinateReferenceSystem::QgsCoordinateReferenceSystem( QString theDefinition ) : mMapUnits( QGis::UnknownUnit ) , mIsValidFlag( 0 ) , mValidationHint( "" ) { mCRS = OSRNewSpatialReference( NULL ); - createFromWkt( theWkt ); + createFromString( theDefinition ); } @@ -75,25 +75,58 @@ OSRDestroySpatialReference( mCRS ); } -void QgsCoordinateReferenceSystem::createFromId( const long theId, CrsType theType ) +bool QgsCoordinateReferenceSystem::createFromId( const long theId, CrsType theType ) { + bool result = false; switch ( theType ) { case InternalCrsId: - createFromSrsId( theId ); + result = createFromSrsId( theId ); break; case PostgisCrsId: - createFromSrid( theId ); + result = createFromSrid( theId ); break; case EpsgCrsId: - createFromEpsg( theId ); + result = createFromEpsg( theId ); break; default: //THIS IS BAD...THIS PART OF CODE SHOULD NEVER BE REACHED... QgsDebugMsg( "Unexpected case reached!" ); }; + return result; } +bool QgsCoordinateReferenceSystem::createFromString( const QString theDefinition ) +{ + bool result = false; + QRegExp reCrsId("^(epsg|postgis|internal)\\:(\\d+)$",Qt::CaseInsensitive); + if( reCrsId.indexIn(theDefinition) == 0) + { + QString authName = reCrsId.cap(1).toLower(); + CrsType type = InternalCrsId; + if( authName == "epsg" ) type = EpsgCrsId; + if( authName == "postgis" ) type = PostgisCrsId; + long id = reCrsId.cap(2).toLong(); + result = createFromId(id,type); + } + else + { + QRegExp reCrsStr("^(?:(wkt|proj4)\\:)?(.+)$",Qt::CaseInsensitive); + if( reCrsStr.indexIn(theDefinition) == 0 ) + { + if( reCrsStr.cap(1).toLower() == "proj4" ) + { + result = createFromProj4(reCrsStr.cap(2)); + } + else + { + result = createFromWkt(reCrsStr.cap(2)); + } + } + } + return result; +} + bool QgsCoordinateReferenceSystem::createFromOgcWmsCrs( QString theCrs ) { if ( loadFromDb( QgsApplication::srsDbFilePath(), "lower(auth_name||':'||auth_id)", theCrs.toLower() ) ) Index: src/core/qgscoordinatereferencesystem.h =================================================================== --- src/core/qgscoordinatereferencesystem.h (revision 15071) +++ src/core/qgscoordinatereferencesystem.h (working copy) @@ -57,10 +57,11 @@ ~QgsCoordinateReferenceSystem(); /*! - * Constructs a CRS object from a Wkt string - * @param theWkt A String containing a valid Wkt def + * Constructs a CRS object from a string definition as defined in the createFromString + * member function (by default a WKT definition). + * @param theDefinition A String containing a coordinate reference system definition. */ - explicit QgsCoordinateReferenceSystem( QString theWkt ); + explicit QgsCoordinateReferenceSystem( QString theDefinition ); /*! Use this constructor when you want to create a CRS object using * a postgis SRID, an EpsgCrsId id or a QGIS CRS_ID. @@ -79,7 +80,7 @@ // Misc helper functions ----------------------- - void createFromId( const long theId, CrsType theType = PostgisCrsId ); + bool createFromId( const long theId, CrsType theType = PostgisCrsId ); /** * \brief Set up this CRS from the given OGC CRS @@ -161,6 +162,15 @@ * @return bool TRUE if sucess else false */ bool createFromProj4( const QString theProjString ); + + /*! Set up this srs from a string definition, by default a WKT definition. Otherwise + * the string defines a authority, followed by a colon, followed by the definition. + * The authority can be one of "epsg", "postgis", "internal" for integer definitions, + * and "wkt" or "proj4" for string definitions. The implementation of each authority + * uses the corresponding createFrom... function. + * @param theDefinition A String containing a coordinate reference system definition. + */ + bool createFromString( const QString theDefinition ); /*! Find out whether this CRS is correctly initialised and usable */ bool isValid() const; Index: src/plugins/delimited_text/qgsdelimitedtextplugingui.cpp =================================================================== --- src/plugins/delimited_text/qgsdelimitedtextplugingui.cpp (revision 15071) +++ src/plugins/delimited_text/qgsdelimitedtextplugingui.cpp (working copy) @@ -25,6 +25,7 @@ #include #include #include +#include #include "qgslogger.h" QgsDelimitedTextPluginGui::QgsDelimitedTextPluginGui( QgisInterface * _qI, QWidget * parent, Qt::WFlags fl ) @@ -56,6 +57,13 @@ delimiterRegexp->setChecked( true ); } + QString delimiterChars = settings.value( key + "/delimiterChars", " " ).toString(); + cbxDelimSpace->setChecked( delimiterChars.contains(" ")); + cbxDelimTab->setChecked( delimiterChars.contains("\\t")); + cbxDelimColon->setChecked( delimiterChars.contains(":")); + cbxDelimSemicolon->setChecked( delimiterChars.contains(":")); + cbxDelimComma->setChecked( delimiterChars.contains(",")); + cmbXField->setDisabled( true ); cmbYField->setDisabled( true ); cmbWktField->setDisabled( true ); @@ -99,34 +107,33 @@ else if ( delimiterRegexp->isChecked() ) delimiterType = "regexp"; - QString uri = QString( "%1?delimiter=%2&delimiterType=%3" ) - .arg( txtFilePath->text() ) - .arg( txtDelimiter->text() ) - .arg( delimiterType ); + QUrl url(txtFilePath->text()); + url.addQueryItem("delimiter",txtDelimiter->text()); + url.addQueryItem("delimiterType",delimiterType); if ( geomTypeXY->isChecked() ) { if ( !cmbXField->currentText().isEmpty() && !cmbYField->currentText().isEmpty() ) { - uri += QString( "&xField=%1&yField=%2" ) - .arg( cmbXField->currentText() ) - .arg( cmbYField->currentText() ); + url.addQueryItem("xField",cmbXField->currentText()); + url.addQueryItem("yField",cmbYField->currentText()); } } else { if ( ! cmbWktField->currentText().isEmpty() ) { - uri += QString( "&wktField=%1" ) - .arg( cmbWktField->currentText() ); + url.addQueryItem("wktField",cmbWktField->currentText()); } } int skipLines = rowCounter->value(); if ( skipLines > 0 ) - uri += QString( "&skipLines=%1" ).arg( skipLines ); + url.addQueryItem("skipLines",QString( "%1" ).arg( skipLines )); // add the layer to the map + + QString uri(url.toEncoded()); emit drawVectorLayer( uri, txtLayerName->text(), "delimitedtext" ); // store the settings @@ -138,10 +145,11 @@ if ( delimiterSelection->isChecked() ) settings.setValue( key + "/delimiterType", "selection" ); - if ( delimiterPlain->isChecked() ) + else if ( delimiterPlain->isChecked() ) settings.setValue( key + "/delimiterType", "plain" ); else settings.setValue( key + "/delimiterType", "regexp" ); + settings.setValue( key + "/delimiterChars", selectedChars()); accept(); } @@ -156,6 +164,17 @@ reject(); } +QString QgsDelimitedTextPluginGui::selectedChars() +{ + QString chars = ""; + if ( cbxDelimSpace->isChecked() ) chars += " "; + if ( cbxDelimTab->isChecked() ) chars += "\\t"; + if ( cbxDelimSemicolon->isChecked() ) chars += ";"; + if ( cbxDelimComma->isChecked() ) chars += ","; + if ( cbxDelimColon->isChecked() ) chars += ":"; + return chars; +} + QStringList QgsDelimitedTextPluginGui::splitLine( QString line ) { QStringList fieldList; @@ -171,11 +190,7 @@ else if ( delimiterSelection->isChecked() ) { delimiter = "["; - if ( cbxDelimSpace->isChecked() ) delimiter += " "; - if ( cbxDelimTab->isChecked() ) delimiter += "\t"; - if ( cbxDelimSemicolon->isChecked() ) delimiter += ";"; - if ( cbxDelimComma->isChecked() ) delimiter += ","; - if ( cbxDelimColon->isChecked() ) delimiter += ":"; + delimiter += selectedChars(); delimiter += "]"; txtDelimiter->setText( delimiter ); fieldList = line.split( QRegExp( delimiter ) ); Index: src/plugins/delimited_text/qgsdelimitedtextplugingui.h =================================================================== --- src/plugins/delimited_text/qgsdelimitedtextplugingui.h (revision 15071) +++ src/plugins/delimited_text/qgsdelimitedtextplugingui.h (working copy) @@ -37,6 +37,7 @@ bool haveValidFileAndDelimiters(); void updateFieldLists(); void getOpenFileName(); + QString selectedChars(); QgisInterface * qI; QAbstractButton *pbnOK; Index: src/plugins/delimited_text/qgsdelimitedtextpluginguibase.ui =================================================================== --- src/plugins/delimited_text/qgsdelimitedtextpluginguibase.ui (revision 15071) +++ src/plugins/delimited_text/qgsdelimitedtextpluginguibase.ui (working copy) @@ -331,7 +331,7 @@ 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 @@ -366,7 +366,7 @@ 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 + false @@ -409,7 +409,7 @@ 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 + false Index: src/providers/delimitedtext/qgsdelimitedtextprovider.cpp =================================================================== --- src/providers/delimitedtext/qgsdelimitedtextprovider.cpp (revision 15071) +++ src/providers/delimitedtext/qgsdelimitedtextprovider.cpp (working copy) @@ -18,7 +18,6 @@ #include "qgsdelimitedtextprovider.h" - #include #include #include @@ -135,49 +134,42 @@ : QgsVectorDataProvider( uri ) , mHasWktField( false ) , mFieldCount( 0 ) - , mXFieldIndex( -1 ), mYFieldIndex( -1 ) + , mXFieldIndex( -1 ) + , mYFieldIndex( -1 ) , mWktFieldIndex( -1 ) + , mDelimiterType( "plain" ) + , mDelimiter( "," ) + , mDelimiterRegexp() , mWktHasZM( false ) , mWktZMRegexp( "\\s+(?:z|m|zm)(?=\\s*\\()", Qt::CaseInsensitive ) , mWktCrdRegexp( "(\\-?\\d+(?:\\.\\d*)?\\s+\\-?\\d+(?:\\.\\d*)?)\\s[\\s\\d\\.\\-]+" ) + , mSkipLines(0) , mFirstDataLine( 0 ) , mShowInvalidLines( true ) , mWkbType( QGis::WKBNoGeometry ) + , mCrs() { - // Get the file name and mDelimiter out of the uri - mFileName = uri.left( uri.indexOf( "?" ) ); - // split the string up on & to get the individual parameters - QStringList parameters = uri.mid( uri.indexOf( "?" ) ).split( "&", QString::SkipEmptyParts ); - QgsDebugMsg( "Parameter count after split on &" + QString::number( parameters.size() ) ); + QUrl url = QUrl::fromEncoded(uri.toUtf8()); - // get the individual parameters and assign values - QStringList temp = parameters.filter( "delimiter=" ); - 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=" ); - QString yField = temp.size() ? temp[0].mid( temp[0].indexOf( "=" ) + 1 ) : ""; - temp = parameters.filter( "skipLines=" ); - QString skipLines = temp.size() ? temp[0].mid( temp[0].indexOf( "=" ) + 1 ) : "0"; - // Decode the parts of the uri. Good if someone entered '=' as a delimiter, for instance. - 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() ); + // Extract the provider definition from the url - mHasWktField = wktField != ""; + mFileName = url.path(); - skipLines = QUrl::fromPercentEncoding( skipLines.toUtf8() ); + QString wktField(""); + QString xField(""); + QString yField(""); - mSkipLines = skipLines.toInt(); + if( url.hasQueryItem("delimiter")) mDelimiter = url.queryItemValue("delimiter"); + if( url.hasQueryItem("delimiterType")) mDelimiterType = url.queryItemValue("delimiterType"); + if( url.hasQueryItem("wktField")) wktField = url.queryItemValue("wktField"); + if( url.hasQueryItem("xField")) xField = url.queryItemValue("xField"); + if( url.hasQueryItem("yField")) yField = url.queryItemValue("yField"); + if( url.hasQueryItem("skipLines")) mSkipLines = url.queryItemValue("skipLines").toInt(); + if( url.hasQueryItem("crs")) mCrs.createFromString( url.queryItemValue("crs")); + mHasWktField = wktField != ""; + QgsDebugMsg( "Data source uri is " + uri ); QgsDebugMsg( "Delimited text file is: " + mFileName ); QgsDebugMsg( "Delimiter is: " + mDelimiter ); @@ -481,7 +473,7 @@ geom = 0; } mFid++; - if ( !boundsCheck( geom ) ) + if ( geom && !boundsCheck( geom ) ) { delete geom; geom = 0; @@ -682,8 +674,7 @@ QgsCoordinateReferenceSystem QgsDelimitedTextProvider::crs() { - // TODO: make provider projection-aware - return QgsCoordinateReferenceSystem(); // return default CRS + return mCrs; } Index: src/providers/delimitedtext/qgsdelimitedtextprovider.h =================================================================== --- src/providers/delimitedtext/qgsdelimitedtextprovider.h (revision 15071) +++ src/providers/delimitedtext/qgsdelimitedtextprovider.h (working copy) @@ -241,6 +241,9 @@ }; wkbPoint mWKBpt; + // Coordinate reference sytem + QgsCoordinateReferenceSystem mCrs; + QGis::WkbType mWkbType; QString readLine( QTextStream *stream ); Index: src/providers/memory/qgsmemoryprovider.cpp =================================================================== --- src/providers/memory/qgsmemoryprovider.cpp (revision 15071) +++ src/providers/memory/qgsmemoryprovider.cpp (working copy) @@ -22,7 +22,10 @@ #include "qgsspatialindex.h" #include "qgscoordinatereferencesystem.h" +#include +#include + static const QString TEXT_PROVIDER_KEY = "memory"; static const QString TEXT_PROVIDER_DESCRIPTION = "Memory provider"; @@ -31,21 +34,41 @@ mSelectRectGeom( NULL ), mSpatialIndex( NULL ) { - if ( uri == "Point" ) + // Initiallize the geometry with the uri to support old style uri's + // (ie, just 'point', 'line', 'polygon') + QUrl url = QUrl::fromEncoded(uri.toUtf8()); + QString geometry; + if( url.hasQueryItem("geometry")) + { + geometry = url.queryItemValue("geometry"); + } + else + { + geometry = url.path(); + } + + geometry = geometry.toLower(); + if ( geometry == "point" ) mWkbType = QGis::WKBPoint; - else if ( uri == "LineString" ) + else if ( geometry == "linestring" ) mWkbType = QGis::WKBLineString; - else if ( uri == "Polygon" ) + else if ( geometry == "polygon" ) mWkbType = QGis::WKBPolygon; - else if ( uri == "MultiPoint" ) + else if ( geometry == "multipoint" ) mWkbType = QGis::WKBMultiPoint; - else if ( uri == "MultiLineString" ) + else if ( geometry == "multilinestring" ) mWkbType = QGis::WKBMultiLineString; - else if ( uri == "MultiPolygon" ) + else if ( geometry == "multipolygon" ) mWkbType = QGis::WKBMultiPolygon; else mWkbType = QGis::WKBUnknown; + if( url.hasQueryItem("crs")) + { + QString crsDef = url.queryItemValue("crs"); + mCrs.createFromString(crsDef); + } + mNextFeatureId = 1; mNativeTypes @@ -53,6 +76,63 @@ << QgsVectorDataProvider::NativeType( tr( "Decimal number (real)" ), "double", QVariant::Double, 1, 20, 0, 5 ) << QgsVectorDataProvider::NativeType( tr( "Text (string)" ), "string", QVariant::String, 1, 255 ) ; + + if( url.hasQueryItem("field")) + { + QList attributes; + QRegExp reFieldDef("\\:" + "(int|integer|real|double|string)" // type + "(?:\\((\\d+)" // length + "(?:\\,(\\d+))?" // precision + "\\))?" + "$", Qt::CaseInsensitive); + QStringList fields = url.allQueryItemValues("field"); + for( int i = 0; i < fields.size(); i++ ) + { + QString name = fields.at(i); + QVariant::Type type = QVariant::String; + QString typeName("string"); + int length = 255; + int precision = 0; + + int pos = reFieldDef.indexIn(name); + if( pos >= 0 ) + { + name = name.mid(0,pos); + typeName = reFieldDef.cap(1).toLower(); + if( typeName == "int" || typeName == "integer" ) + { + type = QVariant::Int; + typeName = "integer"; + length = 10; + } + else if( typeName == "real" || typeName == "double" ) + { + type = QVariant::Double; + typeName = "double"; + length=20; + precision = 5; + } + + if( reFieldDef.cap(2) != "" ) + { + length = reFieldDef.cap(2).toInt(); + } + if( reFieldDef.cap(3) != "" ) + { + precision = reFieldDef.cap(3).toInt(); + } + } + if( name != "" ) attributes.append(QgsField(name,type,typeName,length,precision)); + } + addAttributes(attributes); + } + + if( url.hasQueryItem("index") && url.queryItemValue("index") == "yes" ) + { + createSpatialIndex(); + } + } QgsMemoryProvider::~QgsMemoryProvider() @@ -61,6 +141,69 @@ delete mSelectRectGeom; } +QString QgsMemoryProvider::dataSourceUri() const +{ + QUrl uri("memory"); + QString geometry(""); + switch(mWkbType) + { + case QGis::WKBPoint : + geometry="Point"; + break; + case QGis::WKBLineString : + geometry="LineString"; + break; + case QGis::WKBPolygon : + geometry="Polygon"; + break; + case QGis::WKBMultiPoint : + geometry="MultiPoint"; + break; + case QGis::WKBMultiLineString : + geometry="MultiLineString"; + break; + case QGis::WKBMultiPolygon : + geometry="MultiPolygon"; + break; + } + uri.addQueryItem("geometry",geometry); + + if( mCrs.isValid()) + { + QString crsDef(""); + long srid = mCrs.epsg(); + if( srid ) + { + crsDef = QString("epsg:%1").arg(srid); + } + else if( srid=mCrs.postgisSrid() ) + { + crsDef = QString("postgis:%1").arg(srid); + } + else + { + crsDef = QString("wkt:%1").arg(mCrs.toWkt()); + } + uri.addQueryItem("crs",crsDef); + } + if( mSpatialIndex ) + { + uri.addQueryItem("index","yes"); + } + + QgsAttributeList attrs = const_cast(this)->attributeIndexes(); + for( int i = 0; i < attrs.size(); i++ ) + { + QgsField field = mFields[attrs[i]]; + QString fieldDef = field.name(); + fieldDef.append(QString(":%2(%3,%4)").arg(field.typeName()).arg(field.length()).arg(field.precision())); + uri.addQueryItem("field",fieldDef); + } + + return QString(uri.toEncoded()); + +} + QString QgsMemoryProvider::storageType() const { return "Memory storage"; @@ -231,7 +374,7 @@ QgsCoordinateReferenceSystem QgsMemoryProvider::crs() { // TODO: make provider projection-aware - return QgsCoordinateReferenceSystem(); // return default CRS + return mCrs; // return default CRS } Index: src/providers/memory/qgsmemoryprovider.h =================================================================== --- src/providers/memory/qgsmemoryprovider.h (revision 15071) +++ src/providers/memory/qgsmemoryprovider.h (working copy) @@ -34,6 +34,12 @@ /** * Returns the permanent storage type for this layer as a friendly name. */ + + virtual QString dataSourceUri() const; + + /** + * Returns the permanent storage type for this layer as a friendly name. + */ virtual QString storageType() const; /** Select features based on a bounding rectangle. Features can be retrieved with calls to nextFeature. @@ -185,6 +191,9 @@ void updateExtent(); private: + // Coordinate reference system + QgsCoordinateReferenceSystem mCrs; + // fields QgsFieldMap mFields; QGis::WkbType mWkbType;