/*************************************************************************** qgsogrprovider.cpp Data provider for OGR supported formats Formerly known as qgsshapefileprovider.cpp begin : Oct 29, 2003 copyright : (C) 2003 by Gary E.Sherman email : sherman at mrcc.com ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ /* $Id$ */ #include "qgsogrprovider.h" //Added by qt3to4: #include #ifndef WIN32 #include #endif #include #include #include #include // to collect version information #include #include #include #include #include "ogr_api.h"//only for a test #include #include #include #include #include //TODO Following ifndef can be removed once WIN32 GEOS support // is fixed #ifndef NOWIN32GEOSXXX //XXX GEOS support on windows is broken until we can get VC++ to // tolerate geos.h without throwing a bunch of type errors. It // appears that the windows version of GEOS may be compiled with // MINGW rather than VC++. #endif #include "../../src/qgssearchtreenode.h" #include "../../src/qgsdataprovider.h" #include "../../src/qgsfeature.h" #include "../../src/qgsfield.h" #include "../../src/qgis.h" #ifdef WIN32 #define QGISEXTERN extern "C" __declspec( dllexport ) #else #define QGISEXTERN extern "C" #endif static const QString TEXT_PROVIDER_KEY = "ogr"; static const QString TEXT_PROVIDER_DESCRIPTION = QString("OGR data provider") + " (compiled against GDAL/OGR library version " + GDAL_RELEASE_NAME + ", running against GDAL/OGR library version " + GDALVersionInfo("RELEASE_NAME") + ")"; QgsOgrProvider::QgsOgrProvider(QString const & uri) : QgsVectorDataProvider(uri), ogrDataSource(0), extent_(0), ogrLayer(0), ogrDriver(0), minmaxcachedirty(true) { OGRRegisterAll(); // set the selection rectangle pointer to 0 mSelectionRectangle = 0; // make connection to the data source #ifdef QGISDEBUG std::cerr << "Data source uri is " << uri.toLocal8Bit().data() << std::endl; #endif // try to open for update ogrDataSource = OGRSFDriverRegistrar::Open((const char *) uri.toLocal8Bit().data(), TRUE, &ogrDriver); if(ogrDataSource == NULL) { // try to open read-only ogrDataSource = OGRSFDriverRegistrar::Open((const char *) uri.toLocal8Bit().data(),FALSE, &ogrDriver); //TODO Need to set a flag or something to indicate that the layer //TODO is in read-only mode, otherwise edit ops will fail // TODO: capabilities() should now reflect this; need to test. } if (ogrDataSource != NULL) { #ifdef QGISDEBUG std::cerr << "Data source is valid" << std::endl; std::cerr << "OGR Driver was " << ogrDriver->GetName() << std::endl; #endif valid = true; ogrDriverName = ogrDriver->GetName(); ogrLayer = ogrDataSource->GetLayer(0); // get the extent_ (envelope) of the layer #ifdef QGISDEBUG std::cerr << "Starting get extent\n"; #endif extent_ = new OGREnvelope(); ogrLayer->GetExtent(extent_); #ifdef QGISDEBUG std::cerr << "Finished get extent\n"; #endif // getting the total number of features in the layer numberFeatures = ogrLayer->GetFeatureCount(); // check the validity of the layer #ifdef QGISDEBUG std::cerr << "checking validity\n"; #endif loadFields(); #ifdef QGISDEBUG std::cerr << "Done checking validity\n"; #endif } else { std::cerr << "Data source is invalid" << std::endl; #ifdef QGISDEBUG const char *er = CPLGetLastErrorMsg(); std::cerr << er << std::endl; #endif valid = false; } //resize the cache matrix minmaxcache=new double*[fieldCount()]; for(int i=0;iGetLayerDefn(); if(fdef) { geomType = fdef->GetGeomType(); for(int i=0;iGetFieldCount();++i) { OGRFieldDefn *fldDef = fdef->GetFieldDefn(i); OGRFieldType type = type = fldDef->GetType(); bool numeric = (type == OFTInteger || type == OFTReal); attributeFields.push_back(QgsField( mEncoding->toUnicode(fldDef->GetNameRef()), mEncoding->toUnicode(fldDef->GetFieldTypeName(type)), fldDef->GetWidth(), fldDef->GetPrecision(), numeric)); } } } QString QgsOgrProvider::getProjectionWKT() { #ifdef QGISDEBUG std::cerr << "QgsOgrProvider::getProjectionWKT()" << std::endl; #endif OGRSpatialReference * mySpatialRefSys = ogrLayer->GetSpatialRef(); if (mySpatialRefSys == NULL) { #ifdef QGISDEBUG std::cerr << "QgsOgrProvider::getProjectionWKT() --- no wkt found..returning null" << std::endl; #endif return NULL; } else { // if appropriate, morph the projection from ESRI form QString fileName = ogrDataSource->GetName(); #ifdef QGISDEBUG std::cerr << "Data source file name is : " << fileName.toLocal8Bit().data() << std::endl; #endif if(fileName.contains(".shp")) { #ifdef QGISDEBUG std::cerr << "Morphing " << fileName.toLocal8Bit().data() << " WKT from ESRI" << std::endl; #endif // morph it mySpatialRefSys->morphFromESRI(); } // get the proj4 text char * ppszProj4; mySpatialRefSys->exportToProj4 ( &ppszProj4 ); std::cout << "vvvvvvvvvvvvvvvvv PROJ4 TEXT vvvvvvvvvvvvvvv" << std::endl; std::cout << ppszProj4 << std::endl; std::cout << "^^^^^^^^^^^^^^^^^ PROJ4 TEXT ^^^^^^^^^^^^^^^" << std::endl; char *pszWKT = NULL; mySpatialRefSys->exportToWkt( &pszWKT ); QString myWKTString = QString(pszWKT); OGRFree(pszWKT); return myWKTString; } } QString QgsOgrProvider::storageType() { // Delegate to the driver loaded in by OGR return ogrDriverName; } /** * Get the first feature resutling from a select operation * @return QgsFeature */ QgsFeature * QgsOgrProvider::getFirstFeature(bool fetchAttributes) { QgsFeature *f = 0; if(valid) { #ifdef QGISDEBUG std::cerr << "getting first feature\n"; #endif ogrLayer->ResetReading(); OGRFeature * feat = ogrLayer->GetNextFeature(); Q_CHECK_PTR( feat ); if(feat) { #ifdef QGISDEBUG std::cerr << "First feature is not null\n"; #endif } else { #ifdef QGISDEBUG std::cerr << "First feature is null\n"; #endif return 0x0; // so return a null feature indicating that we got a null feature } // get the feature type name, if any OGRFeatureDefn * featureDefinition = feat->GetDefnRef(); QString featureTypeName = featureDefinition ? QString(featureDefinition->GetName()) : QString(""); f = new QgsFeature(feat->GetFID(), featureTypeName ); Q_CHECK_PTR( f ); if ( ! f ) // return null if we can't get a new QgsFeature { delete feat; return 0x0; } size_t geometry_size = feat->GetGeometryRef()->WkbSize(); f->setGeometryAndOwnership(getGeometryPointer(feat), geometry_size); if(fetchAttributes) { getFeatureAttributes(feat, f); } delete feat; } return f; } // QgsOgrProvider::getFirstFeature() bool QgsOgrProvider::getNextFeature(QgsFeature &f, bool fetchAttributes) { bool returnValue; if(valid){ //std::cerr << "getting next feature\n"; // skip features without geometry OGRFeature *fet; while ((fet = ogrLayer->GetNextFeature()) != NULL) { if (fet->GetGeometryRef()) break; } if(fet){ OGRGeometry *geom = fet->GetGeometryRef(); // get the wkb representation unsigned char *feature = new unsigned char[geom->WkbSize()]; geom->exportToWkb((OGRwkbByteOrder) endian(), feature); f.setFeatureId(fet->GetFID()); f.setGeometryAndOwnership(feature, geom->WkbSize()); OGRFeatureDefn * featureDefinition = fet->GetDefnRef(); QString featureTypeName = featureDefinition ? QString(featureDefinition->GetName()) : QString(""); f.typeName( featureTypeName ); if(fetchAttributes){ getFeatureAttributes(fet, &f); } /* char *wkt = new char[2 * geom->WkbSize()]; geom->exportToWkt(&wkt); f->setWellKnownText(wkt); delete[] wkt; */ delete fet; returnValue = true; }else{ #ifdef QGISDEBUG std::cerr << "Feature is null\n"; f.setValid(false); returnValue = false; #endif // probably should reset reading here ogrLayer->ResetReading(); } }else{ #ifdef QGISDEBUG std::cerr << "Read attempt on an invalid shapefile data source\n"; #endif } return returnValue; } /** * Get the next feature resutling from a select operation * Return 0 if there are no features in the selection set * @return QgsFeature */ QgsFeature *QgsOgrProvider::getNextFeature(bool fetchAttributes) { if(!valid) { std::cerr << "Read attempt on an invalid shapefile data source\n"; return 0; } OGRFeature* fet; OGRGeometry* geom; QgsFeature *f = 0; while((fet = ogrLayer->GetNextFeature()) != NULL) { if (fet->GetGeometryRef()) { geom = fet->GetGeometryRef(); // get the wkb representation unsigned char *feature = new unsigned char[geom->WkbSize()]; geom->exportToWkb((OGRwkbByteOrder) endian(), feature); OGRFeatureDefn * featureDefinition = fet->GetDefnRef(); QString featureTypeName = featureDefinition ? QString(featureDefinition->GetName()) : QString(""); f = new QgsFeature(fet->GetFID(), featureTypeName); f->setGeometryAndOwnership(feature, geom->WkbSize()); if(fetchAttributes /*|| !mAttributeFilter.isEmpty()*/) { getFeatureAttributes(fet, f); // filtering by attribute // TODO: improve, speed up! /* if (!mAttributeFilter.isEmpty() && !mAttributeFilter.tree()->checkAgainst(f->attributeMap())) { delete fet; continue; } */ } if(mUseIntersect) { geos::Geometry *geosGeom = 0; geosGeom=f->geosGeometry(); assert(geosGeom != 0); char *sWkt = new char[2 * mSelectionRectangle->WkbSize()]; mSelectionRectangle->exportToWkt(&sWkt); geos::Geometry *geosRect = wktReader->read(sWkt); assert(geosRect != 0); if(geosGeom->intersects(geosRect)) { #ifdef QGISDEBUG qWarning("intersection found"); #endif delete[] sWkt; delete geosGeom; break; } else { #ifdef QGISDEBUG qWarning("no intersection found"); #endif delete[] sWkt; delete geosGeom; delete f; f=0; } } else { break; } } delete fet; } return f; } /** * Get the next feature resutling from a select operation * Return 0 if there are no features in the selection set * @return QgsFeature */ /*QgsFeature *QgsOgrProvider::getNextFeature(bool fetchAttributes) { QgsFeature *f = 0; if(valid){ OGRFeature *fet; OGRGeometry *geom; while ((fet = ogrLayer->GetNextFeature()) != NULL) { if (fet->GetGeometryRef()) { if(mUseIntersect) { geom = fet->GetGeometryRef(); char *wkt = new char[2 * geom->WkbSize()]; geom->exportToWkt(&wkt); geos::Geometry *geosGeom = wktReader->read(wkt); assert(geosGeom != 0); std::cerr << "Geometry type of geos object is : " << geosGeom->getGeometryType() << std::endl; // get the selection rectangle and create a geos geometry from it char *sWkt = new char[2 * mSelectionRectangle->WkbSize()]; mSelectionRectangle->exportToWkt(&sWkt); std::cerr << "Passing " << sWkt << " to goes\n"; geos::Geometry *geosRect = wktReader->read(sWkt); assert(geosRect != 0); std::cerr << "About to apply intersects function\n"; // test the geometry if(geosGeom->intersects(geosRect)) { std::cerr << "Intersection found\n"; break; } //XXX For some reason deleting these on win32 causes segfault //XXX Someday I'll figure out why... //delete[] wkt; //delete[] sWkt; } else { break; } } } if(fet){ geom = fet->GetGeometryRef(); // get the wkb representation unsigned char *feature = new unsigned char[geom->WkbSize()]; geom->exportToWkb((OGRwkbByteOrder) endian(), feature); OGRFeatureDefn * featureDefinition = fet->GetDefnRef(); QString featureTypeName = featureDefinition ? QString(featureDefinition->GetName()) : QString(""); f = new QgsFeature(fet->GetFID(), featureTypeName); f->setGeometryAndOwnership(feature, geom->WkbSize()); if(fetchAttributes){ getFeatureAttributes(fet, f); } delete fet; }else{ #ifdef QGISDEBUG std::cerr << "Feature is null\n"; #endif // probably should reset reading here ogrLayer->ResetReading(); } }else{ #ifdef QGISDEBUG std::cerr << "Read attempt on an invalid shapefile data source\n"; #endif } return f; }*/ QgsFeature *QgsOgrProvider::getNextFeature(std::list const& attlist, int featureQueueSize) { QgsFeature *f = 0; if(valid) { // skip features without geometry OGRFeature *fet; while ((fet = ogrLayer->GetNextFeature()) != NULL) { if (fet->GetGeometryRef()) { if(mUseIntersect) { // test this geometry to see if it should be // returned #ifdef QGISDEBUG2 std::cerr << "Testing geometry using intersect" << std::endl; #endif } else { #ifdef QGISDEBUG2 std::cerr << "Testing geometry using mbr" << std::endl; #endif break; } } } if(fet) { OGRGeometry *geom = fet->GetGeometryRef(); // get the wkb representation unsigned char *feature = new unsigned char[geom->WkbSize()]; geom->exportToWkb((OGRwkbByteOrder) endian(), feature); OGRFeatureDefn * featureDefinition = fet->GetDefnRef(); QString featureTypeName = featureDefinition ? QString(featureDefinition->GetName()) : QString(""); f = new QgsFeature(fet->GetFID(), featureTypeName); f->setGeometryAndOwnership(feature, geom->WkbSize()); for(std::list::const_iterator it=attlist.begin();it!=attlist.end();++it) { getFeatureAttribute(fet,f,*it); } delete fet; //delete [] feature; } else { #ifdef QGISDEBUG std::cerr << "Feature is null\n"; #endif // probably should reset reading here ogrLayer->ResetReading(); } } else { #ifdef QGISDEBUG std::cerr << "Read attempt on an invalid shapefile data source\n"; #endif } return f; } /** * Select features based on a bounding rectangle. Features can be retrieved * with calls to getFirstFeature and getNextFeature. * @param mbr QgsRect containing the extent to use in selecting features * @param useIntersect If true, an intersect test will be used in selecting * features. In OGR, this is a two pass affair. The mUseIntersect value is * stored. If true, a secondary filter (using GEOS) is applied to each * feature in the getNextFeature function. */ void QgsOgrProvider::select(QgsRect *rect, bool useIntersect) { mUseIntersect = useIntersect; // spatial query to select features std::cerr << "Selection rectangle is " << *rect << std::endl; OGRGeometry *filter = 0; filter = new OGRPolygon(); QString wktExtent = QString("POLYGON ((%1))").arg(rect->asPolygon()); const char *wktText = (const char *)wktExtent; if(useIntersect) { // store the selection rectangle for use in filtering features during // an identify and display attributes delete mSelectionRectangle; mSelectionRectangle = new OGRPolygon(); mSelectionRectangle->importFromWkt((char **)&wktText); } // reset the extent for the ogr filter wktExtent = QString("POLYGON ((%1))").arg(rect->asPolygon()); wktText = (const char *)wktExtent; OGRErr result = ((OGRPolygon *) filter)->importFromWkt((char **)&wktText); //TODO - detect an error in setting the filter and figure out what to //TODO about it. If setting the filter fails, all records will be returned if (result == OGRERR_NONE) { std::cerr << "Setting spatial filter using " << wktExtent.toLocal8Bit().data() << std::endl; ogrLayer->SetSpatialFilter(filter); //ogrLayer->SetSpatialFilterRect(rect->xMin(), rect->yMin(), rect->xMax(), rect->yMax()); }else{ #ifdef QGISDEBUG std::cerr << "Setting spatial filter failed!" << std::endl; assert(result==OGRERR_NONE); #endif } delete filter; } // QgsOgrProvider::select /** * Identify features within the search radius specified by rect * @param rect Bounding rectangle of search radius * @return std::vector containing QgsFeature objects that intersect rect */ std::vector& QgsOgrProvider::identify(QgsRect * rect) { // select the features select(rect); #ifdef WIN32 //TODO fix this later for win32 std::vector feat; return feat; #endif } unsigned char * QgsOgrProvider::getGeometryPointer(OGRFeature *fet){ OGRGeometry *geom = fet->GetGeometryRef(); unsigned char *gPtr=0; // get the wkb representation gPtr = new unsigned char[geom->WkbSize()]; geom->exportToWkb((OGRwkbByteOrder) endian(), gPtr); return gPtr; } QgsRect *QgsOgrProvider::extent() { mExtentRect.set(extent_->MinX, extent_->MinY, extent_->MaxX, extent_->MaxY); return &mExtentRect; } size_t QgsOgrProvider::layerCount() const { return ogrDataSource->GetLayerCount(); } // QgsOgrProvider::layerCount() /** * Return the feature type */ int QgsOgrProvider::geometryType() const { return geomType; } /** * Return the feature type */ long QgsOgrProvider::featureCount() const { return numberFeatures; } /** * Return the number of fields */ int QgsOgrProvider::fieldCount() const { return attributeFields.size(); } void QgsOgrProvider::getFeatureAttribute(OGRFeature * ogrFet, QgsFeature * f, int attindex) { OGRFieldDefn *fldDef = ogrFet->GetFieldDefnRef(attindex); if ( ! fldDef ) { qDebug( "%s:%d ogrFet->GetFieldDefnRef(attindex) returns NULL", __FILE__, __LINE__ ); return; } QString fld = fldDef->GetNameRef(); Q3CString cstr(ogrFet->GetFieldAsString(attindex)); bool numeric = attributeFields[attindex].isNumeric(); f->addAttribute(fld, mEncoding->toUnicode(cstr), numeric); } /** * Fetch attributes for a selected feature */ void QgsOgrProvider::getFeatureAttributes(OGRFeature *ogrFet, QgsFeature *f){ for (int i = 0; i < ogrFet->GetFieldCount(); i++) { getFeatureAttribute(ogrFet,f,i); // add the feature attributes to the tree /*OGRFieldDefn *fldDef = ogrFet->GetFieldDefnRef(i); QString fld = fldDef->GetNameRef(); // OGRFieldType fldType = fldDef->GetType(); QString val; val = ogrFet->GetFieldAsString(i); f->addAttribute(fld, val);*/ } } std::vector const & QgsOgrProvider::fields() const { return attributeFields; } void QgsOgrProvider::reset() { // TODO: check whether it supports normal SQL or only that "restricted_sql" if (mAttributeFilter.isEmpty()) ogrLayer->SetAttributeFilter(NULL); else ogrLayer->SetAttributeFilter(mAttributeFilter.string()); ogrLayer->SetSpatialFilter(0); ogrLayer->ResetReading(); // Reset the use intersect flag on a provider reset, otherwise only the last // selected feature(s) will be displayed when the attribute table // is opened. //XXX In a future release, this "feature" can be used to implement // the display of only selected records in the attribute table. mUseIntersect = false; } QString QgsOgrProvider::minValue(int position) { if(position>=fieldCount()) { std::cerr << "Warning: access requested to invalid position in QgsOgrProvider::minValue(..)" << std::endl; } if(minmaxcachedirty) { fillMinMaxCash(); } return QString::number(minmaxcache[position][0],'f',2); } QString QgsOgrProvider::maxValue(int position) { if(position>=fieldCount()) { std::cerr << "Warning: access requested to invalid position in QgsOgrProvider::maxValue(..)" << std::endl; } if(minmaxcachedirty) { fillMinMaxCash(); } return QString::number(minmaxcache[position][1],'f',2); } void QgsOgrProvider::fillMinMaxCash() { for(int i=0;iattributeMap())[i].fieldValue().toDouble(); if(valueminmaxcache[i][1]) { minmaxcache[i][1]=value; } } delete f; }while(f=getNextFeature(true)); minmaxcachedirty=false; } //TODO - add sanity check for shape file layers, to include cheking to // see if the .shp, .dbf, .shx files are all present and the layer // actually has features bool QgsOgrProvider::isValid() { return valid; } bool QgsOgrProvider::addFeature(QgsFeature* f) { #ifdef QGISDEBUG qWarning("in add Feature"); #endif bool returnValue = true; OGRFeatureDefn* fdef=ogrLayer->GetLayerDefn(); OGRFeature* feature=new OGRFeature(fdef); QGis::WKBTYPE ftype; memcpy(&ftype, (f->getGeometry()+1), sizeof(int)); switch(ftype) { case QGis::WKBPoint: { OGRPoint* p=new OGRPoint(); p->importFromWkb(f->getGeometry(),1+sizeof(int)+2*sizeof(double)); OGRErr err = feature->SetGeometry(p); break; } case QGis::WKBLineString: { OGRLineString* l=new OGRLineString(); int length; memcpy(&length,f->getGeometry()+1+sizeof(int),sizeof(int)); l->importFromWkb(f->getGeometry(),1+2*sizeof(int)+2*length*sizeof(double)); feature->SetGeometry(l); break; } case QGis::WKBPolygon: { OGRPolygon* pol=new OGRPolygon(); int numrings; int totalnumpoints=0; int numpoints;//number of points in one ring unsigned char* ptr=f->getGeometry()+1+sizeof(int); memcpy(&numrings,ptr,sizeof(int)); ptr+=sizeof(int); for(int i=0;iimportFromWkb(f->getGeometry(),1+2*sizeof(int)+numrings*sizeof(int)+totalnumpoints*2*sizeof(double)); feature->SetGeometry(pol); break; } case QGis::WKBMultiPoint: { OGRMultiPoint* multip= new OGRMultiPoint(); int count; //determine how many points memcpy(&count,f->getGeometry()+1+sizeof(int),sizeof(int)); multip->importFromWkb(f->getGeometry(),1+2*sizeof(int)+count*2*sizeof(double)); feature->SetGeometry(multip); break; } case QGis::WKBMultiLineString: { OGRMultiLineString* multil=new OGRMultiLineString(); int numlines; memcpy(&numlines,f->getGeometry()+1+sizeof(int),sizeof(int)); int totalpoints=0; int numpoints;//number of point in one line unsigned char* ptr=f->getGeometry()+9; for(int i=0;iimportFromWkb(f->getGeometry(),size); feature->SetGeometry(multil); break; } case QGis::WKBMultiPolygon: { OGRMultiPolygon* multipol=new OGRMultiPolygon(); int numpolys; memcpy(&numpolys,f->getGeometry()+1+sizeof(int),sizeof(int)); int numrings;//number of rings in one polygon int totalrings=0; int totalpoints=0; int numpoints;//number of points in one ring unsigned char* ptr=f->getGeometry()+9; for(int i=0;iimportFromWkb(f->getGeometry(),size); feature->SetGeometry(multipol); break; } } //add possible attribute information #ifdef QGISDEBUG qWarning("before attribute commit section"); #endif for(int i=0;iattributeMap().size();++i) { QString s=(f->attributeMap())[i].fieldValue(); #ifdef QGISDEBUG qWarning("adding attribute: "+s); #endif if(!s.isEmpty()) { if(fdef->GetFieldDefn(i)->GetType()==OFTInteger) { feature->SetField(i,s.toInt()); #ifdef QGISDEBUG qWarning("OFTInteger, attribute value: "+s.toInt()); #endif } else if(fdef->GetFieldDefn(i)->GetType()==OFTReal) { feature->SetField(i,s.toDouble()); #ifdef QGISDEBUG qWarning("OFTReal, attribute value: "+QString::number(s.toDouble(),'f',3)); #endif } else if(fdef->GetFieldDefn(i)->GetType()==OFTString) { feature->SetField(i,s.ascii()); #ifdef QGISDEBUG qWarning("OFTString, attribute value: "+QString(s.ascii())); #endif } else { #ifdef QGISDEBUG qWarning("no type found"); #endif } } } if(ogrLayer->CreateFeature(feature)!=OGRERR_NONE) { //writing failed QMessageBox::warning (0, "Warning", "Writing of the feature failed", QMessageBox::Ok, Qt::NoButton, Qt::NoButton ); returnValue = false; } ++numberFeatures; delete feature; ogrLayer->SyncToDisk(); return returnValue; } bool QgsOgrProvider::addFeatures(std::list const flist) { bool returnvalue=true; for(std::list::const_iterator it=flist.begin();it!=flist.end();++it) { if(!addFeature(*it)) { returnvalue=false; } } return returnvalue; } bool QgsOgrProvider::addAttributes(std::map const & name) { bool returnvalue=true; for(std::map::const_iterator iter=name.begin();iter!=name.end();++iter) { if(iter->second=="OFTInteger") { OGRFieldDefn fielddefn(iter->first,OFTInteger); if(ogrLayer->CreateField(&fielddefn)!=OGRERR_NONE) { #ifdef QGISDEBUG qWarning("QgsOgrProvider.cpp: writing of the field failed"); #endif returnvalue=false; } } else if(iter->second=="OFTReal") { OGRFieldDefn fielddefn(iter->first,OFTReal); if(ogrLayer->CreateField(&fielddefn)!=OGRERR_NONE) { #ifdef QGISDEBUG qWarning("QgsOgrProvider.cpp: writing of the field failed"); #endif returnvalue=false; } } else if(iter->second=="OFTString") { OGRFieldDefn fielddefn(iter->first,OFTString); if(ogrLayer->CreateField(&fielddefn)!=OGRERR_NONE) { #ifdef QGISDEBUG qWarning("QgsOgrProvider.cpp: writing of the field failed"); #endif returnvalue=false; } } else { #ifdef QGISDEBUG qWarning("QgsOgrProvider.cpp: type not found"); #endif returnvalue=false; } } return returnvalue; } bool QgsOgrProvider::changeAttributeValues(std::map > const & attr_map) { #ifdef QGISDEBUG std::cerr << "QgsOgrProvider::changeAttributeValues()" << std::endl; #endif std::map > am = attr_map; // stupid, but I don't know other way to convince gcc to compile for( std::map >::iterator it=am.begin();it!=am.end();++it) { long fid = (long) (*it).first; OGRFeature *of = ogrLayer->GetFeature ( fid ); if ( !of ) { QMessageBox::warning (0, "Warning", "Cannot read feature, cannot change attributes" ); return false; } std::map attr = (*it).second; for( std::map::iterator it2 = attr.begin(); it2!=attr.end(); ++it2 ) { QString name = (*it2).first; QString value = (*it2).second; int fc = of->GetFieldCount(); for ( int f = 0; f < fc; f++ ) { OGRFieldDefn *fd = of->GetFieldDefnRef ( f ); if ( name.compare( fd->GetNameRef() ) == 0 ) { OGRFieldType type = fd->GetType(); #ifdef QGISDEBUG std::cerr << "set field " << f << " : " << name.toLocal8Bit().data() << " to " << value.toLocal8Bit().data() << std::endl; #endif switch ( type ) { case OFTInteger: of->SetField ( f, value.toInt() ); break; case OFTReal: of->SetField ( f, value.toDouble() ); break; case OFTString: of->SetField ( f, value.ascii() ); break; default: QMessageBox::warning (0, "Warning", "Unknown field type, cannot change attribute" ); break; } continue; } } ogrLayer->SetFeature ( of ); } } ogrLayer->SyncToDisk(); return true; } bool QgsOgrProvider::createSpatialIndex() { //experimental, try to create a spatial index QString filename=getDataSourceUri().section('/',-1,-1);//todo: find out the filename from the uri QString layername=filename.section('.',0,0); QString sql="CREATE SPATIAL INDEX ON "+layername; ogrDataSource->ExecuteSQL (sql.ascii(), ogrLayer->GetSpatialFilter(),""); //todo: find out, if the .qix file is there QString indexname = getDataSourceUri(); indexname.truncate(getDataSourceUri().length()-filename.length()); indexname=indexname+layername+".qix"; QFile indexfile(indexname); if(indexfile.exists()) { return true; } else { return false; } } int QgsOgrProvider::capabilities() const { int ability = NoCapabilities; // collect abilities reported by OGR if (ogrLayer) { // Whilst the OGR documentation (e.g. at // http://www.gdal.org/ogr/classOGRLayer.html#a17) states "The capability // codes that can be tested are represented as strings, but #defined // constants exists to ensure correct spelling", we always use strings // here. This is because older versions of OGR don't always have all // the #defines we want to test for here. if (ogrLayer->TestCapability("RandomRead")) // TRUE if the GetFeature() method works for this layer. { // TODO: Perhaps influence if QGIS caches into memory (vs read from disk every time) based on this setting. } if (ogrLayer->TestCapability("SequentialWrite")) // TRUE if the CreateFeature() method works for this layer. { ability |= QgsVectorDataProvider::AddFeatures; } if (ogrLayer->TestCapability("RandomWrite")) // TRUE if the SetFeature() method is operational on this layer. { ability |= QgsVectorDataProvider::ChangeAttributeValues; // TODO According to http://shapelib.maptools.org/ (Shapefile C Library V1.2) // TODO "You can't modify the vertices of existing structures". // TODO Need to work out versions of shapelib vs versions of GDAL/OGR // TODO And test appropriately. // This provider can't change geometries yet anyway (cf. Postgres provider) // ability |= QgsVectorDataProvider::ChangeGeometries; } if (ogrLayer->TestCapability("FastSpatialFilter")) // TRUE if this layer implements spatial filtering efficiently. // Layers that effectively read all features, and test them with the // OGRFeature intersection methods should return FALSE. // This can be used as a clue by the application whether it should build // and maintain it's own spatial index for features in this layer. { // TODO: Perhaps use as a clue by QGIS whether it should build and maintain it's own spatial index for features in this layer. } if (ogrLayer->TestCapability("FastFeatureCount")) // TRUE if this layer can return a feature count // (via OGRLayer::GetFeatureCount()) efficiently ... ie. without counting // the features. In some cases this will return TRUE until a spatial // filter is installed after which it will return FALSE. { // TODO: Perhaps use as a clue by QGIS whether it should spawn a thread to count features. } if (ogrLayer->TestCapability("FastGetExtent")) // TRUE if this layer can return its data extent // (via OGRLayer::GetExtent()) efficiently ... ie. without scanning // all the features. In some cases this will return TRUE until a // spatial filter is installed after which it will return FALSE. { // TODO: Perhaps use as a clue by QGIS whether it should spawn a thread to calculate extent. } if (ogrLayer->TestCapability("FastSetNextByIndex")) // TRUE if this layer can perform the SetNextByIndex() call efficiently. { // No use required for this QGIS release. } #ifdef QGISDEBUG std::cout << "QgsOgrProvider::capabilities: GDAL Version Num is 'GDAL_VERSION_NUM'." << std::endl; #endif if (1) { // Ideally this should test for Shapefile type and GDAL >= 1.2.6 // In reality, createSpatialIndex() looks after itself. ability |= QgsVectorDataProvider::CreateSpatialIndex; } } return ability; /* return (QgsVectorDataProvider::AddFeatures | QgsVectorDataProvider::ChangeAttributeValues | QgsVectorDataProvider::CreateSpatialIndex); */ } QString QgsOgrProvider::name() const { return TEXT_PROVIDER_KEY; } // QgsOgrProvider::name() QString QgsOgrProvider::description() const { return TEXT_PROVIDER_DESCRIPTION; } // QgsOgrProvider::description() /** Convenience function for readily creating file filters. Given a long name for a file filter and a regular expression, return a file filter string suitable for use in a QFileDialog::OpenFiles() call. The regular express, glob, will have both all lower and upper case versions added. @note Copied from qgisapp.cpp. @todo XXX This should probably be generalized and moved to a standard utility type thingy. */ static QString createFileFilter_(QString const &longName, QString const &glob) { return "[OGR] " + longName + " (" + glob.lower() + " " + glob.upper() + ");;"; } // createFileFilter_ QGISEXTERN QString fileVectorFilters() { static QString myFileFilters; // if we've already built the supported vector string, just return what // we've already built if ( ! ( myFileFilters.isEmpty() || myFileFilters.isNull() ) ) { return myFileFilters; } // first get the GDAL driver manager OGRSFDriverRegistrar *driverRegistrar = OGRSFDriverRegistrar::GetRegistrar(); if (!driverRegistrar) { QMessageBox::warning(0,"OGR Driver Manager","unable to get OGRDriverManager"); return ""; // XXX good place to throw exception if we } // XXX decide to do exceptions // then iterate through all of the supported drivers, adding the // corresponding file filter OGRSFDriver *driver; // current driver QString driverName; // current driver name // Grind through all the drivers and their respective metadata. // We'll add a file filter for those drivers that have a file // extension defined for them; the others, welll, even though // theoreticaly we can open those files because there exists a // driver for them, the user will have to use the "All Files" to // open datasets with no explicitly defined file name extension. #ifdef QGISDEBUG std::cerr << "Driver count: " << driverRegistrar->GetDriverCount() << std::endl; #endif for (int i = 0; i < driverRegistrar->GetDriverCount(); ++i) { driver = driverRegistrar->GetDriver(i); Q_CHECK_PTR(driver); if (!driver) { qWarning("unable to get driver %d", i); continue; } driverName = driver->GetName(); if (driverName.startsWith("ESRI")) { myFileFilters += createFileFilter_("ESRI Shapefiles", "*.shp"); } else if (driverName.startsWith("UK")) { // XXX needs file filter extension } else if (driverName.startsWith("SDTS")) { myFileFilters += createFileFilter_( "Spatial Data Transfer Standard", "*catd.ddf" ); } else if (driverName.startsWith("TIGER")) { // XXX needs file filter extension } else if (driverName.startsWith("S57")) { // XXX needs file filter extension } else if (driverName.startsWith("MapInfo")) { myFileFilters += createFileFilter_("MapInfo", "*.mif *.tab"); // XXX needs file filter extension } else if (driverName.startsWith("DGN")) { // XXX needs file filter extension } else if (driverName.startsWith("VRT")) { // XXX needs file filter extension } else if (driverName.startsWith("AVCBin")) { // XXX needs file filter extension } else if (driverName.startsWith("REC")) { // XXX needs file filter extension } else if (driverName.startsWith("Memory")) { // XXX needs file filter extension } else if (driverName.startsWith("Jis")) { // XXX needs file filter extension } else if (driverName.startsWith("GML")) { // XXX not yet supported; post 0.1 release task myFileFilters += createFileFilter_( "Geography Markup Language", "*.gml" ); } else { // NOP, we don't know anything about the current driver // with regards to a proper file filter string qDebug( "%s:%d unknown driver %s", __FILE__, __LINE__, (const char *)driverName.local8Bit() ); } } // each loaded GDAL driver // can't forget the default case myFileFilters += "All files (*.*)"; qDebug() << myFileFilters; return myFileFilters; } // fileVectorFilters() const QString QgsOgrProvider::fileVectorFilters() const { return fileVectorFilters(); } // QgsOgrProvider::fileVectorFilters() const /** * Class factory to return a pointer to a newly created * QgsOgrProvider object */ QGISEXTERN QgsOgrProvider * classFactory(const QString *uri) { return new QgsOgrProvider(*uri); } /** Required key function (used to map the plugin to a data store type) */ QGISEXTERN QString providerKey() { return TEXT_PROVIDER_KEY; } /** * Required description function */ QGISEXTERN QString description() { return TEXT_PROVIDER_DESCRIPTION; } /** * Required isProvider function. Used to determine if this shared library * is a data provider plugin */ QGISEXTERN bool isProvider() { return true; } /**Creates an empty data source @param uri location to store the file(s) @param format data format (e.g. "ESRI Shapefile" @param vectortype point/line/polygon or multitypes @param attributes a list of name/type pairs for the initial attributes @return true in case of success*/ QGISEXTERN bool createEmptyDataSource(const QString& uri,const QString& format, QGis::WKBTYPE vectortype, \ const std::list >& attributes) { OGRSFDriver* driver; OGRRegisterAll(); driver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(format); if(driver == NULL) { return false; } OGRDataSource* dataSource; dataSource = driver->CreateDataSource(uri, NULL); if(dataSource == NULL) { return false; } //consider spatial reference system OGRSpatialReference* reference = NULL; QgsSpatialRefSys mySpatialRefSys; mySpatialRefSys.validate(); char* WKT; QString myWKT = NULL; if(mySpatialRefSys.toOgrSrs().exportToWkt(&WKT)==OGRERR_NONE) { myWKT=WKT; } else { #ifdef QGISDEBUG qWarning("createEmptyDataSource: export of srs to wkt failed"); #endif } if( !myWKT.isNull() && myWKT.length() != 0 ) { reference = new OGRSpatialReference(myWKT.toLocal8Bit().data()); } OGRLayer* layer; layer = dataSource->CreateLayer(NULL, reference, (OGRwkbGeometryType)vectortype, NULL); if(layer == NULL) { return false; } //create the attribute fields for(std::list >::const_iterator it= attributes.begin(); it != attributes.end(); ++it) { if(it->second == "Real") { OGRFieldDefn field(it->first, OFTReal); if(layer->CreateField(&field) != OGRERR_NONE) { #ifdef QGISDEBUG qWarning("creation of OFTReal field failed"); #endif } } else if(it->second == "Integer") { OGRFieldDefn field(it->first, OFTInteger); if(layer->CreateField(&field) != OGRERR_NONE) { #ifdef QGISDEBUG qWarning("creation of OFTInteger field failed"); #endif } } else if(it->second == "String") { OGRFieldDefn field(it->first, OFTString); if(layer->CreateField(&field) != OGRERR_NONE) { #ifdef QGISDEBUG qWarning("creation of OFTString field failed"); #endif } } } OGRDataSource::DestroyDataSource(dataSource); qWarning("GDAL Version number is: "+QString::number(GDAL_VERSION_NUM)); #if GDAL_VERSION_NUM >= 1310 if(reference) { reference->Release(); } #endif //GDAL_VERSION_NUM return true; }