Skip to content

Commit cd04495

Browse files
author
morb_au
committedJul 14, 2006
Copying selected features, and starting editing, no longer force all geometries of the copied-from layer to be cached, which was an (in my opinion) unfortunate side-effect of r5489.
Instead, for copying, the geometry and attributes are now retrieved from the provider in a random-access fashion. For starting editing, the behaviour is regressed to the pre-r5489 idiom of geometry being cached as part of the screen redraw in editing mode. This returns to the much better response when copying from large layers, with the additional benefit of copying selected features that had been panned off the canvas (previously a bug). However, only Postgres and OGR providers have been updated to suit. Hopefully this is enough for a 0.8 release. Bonus feature: The layer properties now shows what access method is used to access individual geometries (random or sequential) Since this is a large change it would be appreciated if others can double checking my testing. (I have tested against PostGIS and MapInfo tables, and many different geometry adding/vertex editing combinations.) git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@5595 c8812cc2-4d05-0410-92ff-de0c093fc19c

File tree

8 files changed

+288
-60
lines changed

8 files changed

+288
-60
lines changed
 

‎src/core/qgsvectordataprovider.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ QString QgsVectorDataProvider::capabilitiesString() const
172172
if (abilities & QgsVectorDataProvider::SelectAtId)
173173
{
174174
// Not really meaningful to the user.
175-
// abilitiesList = "Select at ID";
175+
// abilitiesList += "Select at ID";
176176
#ifdef QGISDEBUG
177177
std::cerr << "QgsVectorDataProvider::capabilitiesString "
178178
<< "Select at ID" << std::endl;
@@ -188,6 +188,35 @@ QString QgsVectorDataProvider::capabilitiesString() const
188188
#endif
189189
}
190190

191+
if (abilities & QgsVectorDataProvider::SelectGeometryAtId)
192+
{
193+
194+
if (abilities & QgsVectorDataProvider::RandomSelectGeometryAtId)
195+
{
196+
abilitiesList += "Select Geometries by ID (random access)";
197+
#ifdef QGISDEBUG
198+
std::cerr << "QgsVectorDataProvider::capabilitiesString "
199+
<< "Select Geometries by ID (random access)" << std::endl;
200+
#endif
201+
}
202+
else if (abilities & QgsVectorDataProvider::SequentialSelectGeometryAtId)
203+
{
204+
abilitiesList += "Select Geometries by ID (sequential access)";
205+
#ifdef QGISDEBUG
206+
std::cerr << "QgsVectorDataProvider::capabilitiesString "
207+
<< "Select Geometries by ID (sequential access)" << std::endl;
208+
#endif
209+
}
210+
else
211+
{
212+
abilitiesList += "Select Geometries by ID (unknown access method)";
213+
#ifdef QGISDEBUG
214+
std::cerr << "QgsVectorDataProvider::capabilitiesString "
215+
<< "Select Geometries by ID (unknown access method)" << std::endl;
216+
#endif
217+
}
218+
}
219+
191220
return abilitiesList.join(", ");
192221

193222
}

‎src/core/qgsvectordataprovider.h

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,19 @@ class QgsVectorDataProvider : public QgsDataProvider
4343
// If you add to this, please also add to capabilitiesString()
4444
enum Capability
4545
{
46-
NoCapabilities = 0,
47-
AddFeatures = 1,
48-
DeleteFeatures = 1 << 1,
49-
ChangeAttributeValues = 1 << 2,
50-
AddAttributes = 1 << 3, // TODO: what is this exactly?
51-
DeleteAttributes = 1 << 4,
52-
SaveAsShapefile = 1 << 5,
53-
CreateSpatialIndex = 1 << 6,
54-
SelectAtId = 1 << 7,
55-
ChangeGeometries = 1 << 8
46+
NoCapabilities = 0,
47+
AddFeatures = 1,
48+
DeleteFeatures = 1 << 1,
49+
ChangeAttributeValues = 1 << 2,
50+
AddAttributes = 1 << 3, // TODO: what is this exactly?
51+
DeleteAttributes = 1 << 4,
52+
SaveAsShapefile = 1 << 5,
53+
CreateSpatialIndex = 1 << 6,
54+
SelectAtId = 1 << 7,
55+
ChangeGeometries = 1 << 8,
56+
SelectGeometryAtId = 1 << 9,
57+
RandomSelectGeometryAtId = 1 << 10,
58+
SequentialSelectGeometryAtId = 1 << 11
5659
};
5760

5861
QgsVectorDataProvider();
@@ -137,12 +140,20 @@ class QgsVectorDataProvider : public QgsDataProvider
137140
virtual long featureCount() const = 0;
138141

139142
/**
140-
* Get the attributes associated with a feature
141-
* TODO: Get rid of "row" and set up provider-internal caching instead
142-
*/
143-
virtual void getFeatureAttributes(int oid, int& row, QgsFeature *f) {};
144-
145-
/**
143+
* Get the attributes associated with a feature
144+
* TODO: Get rid of "row" and set up provider-internal caching instead
145+
*/
146+
virtual void getFeatureAttributes(int key, int& row, QgsFeature *f) {};
147+
148+
/**
149+
* Fetch geometry for a particular feature with id "key",
150+
* modifies "f" in-place.
151+
*
152+
* This function is enabled if capabilities() returns "SelectGeometryAtId".
153+
*/
154+
virtual void getFeatureGeometry(int key, QgsFeature *f) {};
155+
156+
/**
146157
* Number of attribute fields for a feature in the layer
147158
*/
148159
virtual int fieldCount() const = 0;

‎src/gui/qgsvectorlayer.cpp

Lines changed: 83 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -174,14 +174,8 @@ QgsVectorLayer::~QgsVectorLayer()
174174
delete myLib;
175175
delete mLabel;
176176

177-
// Destroy and cached geometries and clear the references to them
178-
for (std::map<int, QgsGeometry*>::iterator it = mCachedGeometries.begin();
179-
it != mCachedGeometries.end();
180-
++it )
181-
{
182-
delete (*it).second;
183-
}
184-
mCachedGeometries.clear();
177+
// Destroy any cached geometries and clear the references to them
178+
deleteCachedGeometries();
185179
}
186180

187181
QString QgsVectorLayer::storageType() const
@@ -830,13 +824,10 @@ void QgsVectorLayer::draw(QPainter * p,
830824
/*Scale factor of the marker image*/
831825
double markerScaleFactor=1.;
832826

833-
// Destroy all cached geometries and clear the references to them
834-
835827
if(mEditable)
836828
{
837829
// Destroy all cached geometries and clear the references to them
838830
deleteCachedGeometries();
839-
840831
}
841832

842833
dataProvider->reset();
@@ -988,7 +979,10 @@ void QgsVectorLayer::cacheGeometries()
988979
{
989980
if(dataProvider)
990981
{
991-
QgsFeature* f = 0;
982+
QgsFeature* f;
983+
984+
dataProvider->reset();
985+
992986
while(f = dataProvider->getNextFeature(false))
993987
{
994988
mCachedGeometries.insert(std::make_pair(f->featureId(), f->geometryAndOwnership()));
@@ -999,6 +993,8 @@ void QgsVectorLayer::cacheGeometries()
999993

1000994
void QgsVectorLayer::deleteCachedGeometries()
1001995
{
996+
// Destroy any cached geometries and clear the references to them
997+
1002998
for (std::map<int, QgsGeometry*>::iterator it = mCachedGeometries.begin(); it != mCachedGeometries.end(); ++it )
1003999
{
10041000
delete (*it).second;
@@ -1918,7 +1914,11 @@ void QgsVectorLayer::startEditing()
19181914
}
19191915
else
19201916
{
1921-
cacheGeometries();
1917+
// No longer need to cache all geometries
1918+
// instead, mCachedGeometries is refreshed every time the
1919+
// screen is redrawn.
1920+
//cacheGeometries();
1921+
19221922
mEditable=true;
19231923
if(isValid())
19241924
{
@@ -2604,6 +2604,13 @@ bool QgsVectorLayer::rollBack()
26042604
return true;
26052605
}
26062606

2607+
2608+
int QgsVectorLayer::selectedFeatureCount()
2609+
{
2610+
return mSelectedFeatureIds.size();
2611+
}
2612+
2613+
26072614
std::vector<QgsFeature>* QgsVectorLayer::selectedFeatures()
26082615
{
26092616
if (!dataProvider)
@@ -2612,40 +2619,80 @@ std::vector<QgsFeature>* QgsVectorLayer::selectedFeatures()
26122619
}
26132620

26142621
std::vector<QgsFeature>* features = new std::vector<QgsFeature>;
2615-
if(mSelectedFeatureIds.size() == 0)
2616-
{
2617-
return features;
2618-
}
26192622

2620-
//we need to cache all the features first (which has already been done if a layer is editable)
2621-
if(!mEditable)
2622-
{
2623-
deleteCachedGeometries();
2624-
cacheGeometries();
2625-
}
2623+
if (mSelectedFeatureIds.size() == 0)
2624+
{
2625+
// short cut
2626+
return features;
2627+
}
2628+
2629+
// we don't need to cache features ... it just adds unnecessary time
2630+
// as we don't need to pull *everything* from disk
2631+
2632+
// Go through each selected feature ID and determine
2633+
// its current geometry and attributes
26262634

26272635
for (std::set<int>::iterator it = mSelectedFeatureIds.begin(); it != mSelectedFeatureIds.end(); ++it)
26282636
{
2629-
// Check this selected item against the committed or cached features
2630-
if ( mCachedGeometries.find(*it) != mCachedGeometries.end() )
2631-
{
2632-
QgsFeature* f = new QgsFeature();
2633-
f->setGeometry(*mCachedGeometries[*it]);//makes a deep copy of the geometry
2634-
features->push_back(*f);
2635-
continue;
2636-
}
2637-
2637+
QgsFeature* initialFeature;
2638+
2639+
// Pull the original version of the feature from disk or memory
2640+
// as appropriate
2641+
26382642
// Check this selected item against the uncommitted added features
2639-
/*for (std::vector<QgsFeature*>::iterator iter = mAddedFeatures.begin();
2643+
bool selectionIsAddedFeature = FALSE;
2644+
2645+
for (std::vector<QgsFeature*>::iterator iter = mAddedFeatures.begin();
26402646
iter != mAddedFeatures.end();
26412647
++iter)
26422648
{
26432649
if ( (*it) == (*iter)->featureId() )
26442650
{
2645-
features->push_back( **iter ); //shouldn't we make a deep copy here?
2646-
break;
2651+
#ifdef QGISDEBUG
2652+
std::cout << "QgsVectorLayer::selectedFeatures: found an added geometry: "
2653+
<< std::endl;
2654+
#endif
2655+
initialFeature = new QgsFeature(**iter);
2656+
selectionIsAddedFeature = TRUE;
26472657
}
2648-
}*/
2658+
}
2659+
2660+
if (!selectionIsAddedFeature)
2661+
{
2662+
// pull committed version from disk
2663+
initialFeature = new QgsFeature(*it);
2664+
2665+
int row = 0; //TODO: Get rid of this, but getFeatureAttributes()
2666+
// needs it for some reason
2667+
dataProvider->getFeatureAttributes(*it, row, initialFeature);
2668+
2669+
if ( mChangedGeometries.find(*it) == mChangedGeometries.end() )
2670+
{
2671+
// also pull committed geometry from disk as we will
2672+
// not need to overwrite it later
2673+
2674+
if (dataProvider->capabilities() & QgsVectorDataProvider::SelectGeometryAtId)
2675+
{
2676+
dataProvider->getFeatureGeometry(*it, initialFeature);
2677+
}
2678+
else
2679+
{
2680+
QMessageBox::information(0, tr("Cannot retrieve features"),
2681+
tr("The provider for the current layer cannot retrieve geometry for the selected features. This version of the provider does not have this capability."));
2682+
}
2683+
}
2684+
2685+
}
2686+
2687+
// Transform the feature to the "current" in-memory version
2688+
QgsFeature finalFeature =
2689+
QgsFeature(*initialFeature,
2690+
mChangedAttributes,
2691+
mChangedGeometries);
2692+
2693+
delete initialFeature;
2694+
2695+
features->push_back(finalFeature);
26492696

26502697
} // for each selected
26512698

‎src/gui/qgsvectorlayer.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ const QString displayField() const { return fieldIndex; }
147147

148148
QgsAttributeAction* actions() { return &mActions; }
149149

150+
//! The number of features that are selected in this layer
151+
int selectedFeatureCount();
152+
150153
/**
151154
Get a copy of the user-selected features
152155
*/
@@ -476,7 +479,7 @@ public slots:
476479
/**Pointer to the table display object if there is one, else a pointer to 0*/
477480
QgsAttributeTableDisplay * tabledisplay;
478481

479-
/** cache of the committed geometries retrieved for the current display */
482+
/** cache of the committed geometries retrieved *for the current display* */
480483
std::map<int, QgsGeometry*> mCachedGeometries;
481484

482485
/** Set holding the feature IDs that are activated. Note that if a feature
@@ -630,7 +633,7 @@ protected slots:
630633
};
631634
private: // Private methods
632635

633-
/**Caches all the (commited) geometries to mCachedFeatures, e.g. when entering editing mode*/
636+
/**Caches all the (commited) geometries to mCachedGeometries - somewhat out of date as mCachedGeometries should only contain geometries currently visible on the canvas */
634637
void cacheGeometries();
635638
/**Deletes the geometries in mCachedGeometries*/
636639
void deleteCachedGeometries();

‎src/providers/ogr/qgsogrprovider.cpp

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,56 @@ void QgsOgrProvider::getFeatureAttributes(OGRFeature *ogrFet, QgsFeature *f){
719719
}
720720
}
721721

722+
void QgsOgrProvider::getFeatureAttributes(int key, int &row, QgsFeature *f)
723+
{
724+
if(!valid)
725+
{
726+
QgsLogger::critical("Read attempt on an invalid shapefile data source");
727+
return;
728+
}
729+
730+
OGRFeature* fet;
731+
OGRGeometry* geom;
732+
733+
if ((fet = ogrLayer->GetFeature(key)) != NULL)
734+
{
735+
getFeatureAttributes(fet, f);
736+
737+
delete fet;
738+
}
739+
}
740+
741+
void QgsOgrProvider::getFeatureGeometry(int key, QgsFeature *f)
742+
{
743+
if(!valid)
744+
{
745+
QgsLogger::critical("Read attempt on an invalid shapefile data source");
746+
return;
747+
}
748+
749+
OGRFeature* fet;
750+
OGRGeometry* geom;
751+
752+
if ((fet = ogrLayer->GetFeature(key)) != NULL)
753+
{
754+
if (geom = fet->GetGeometryRef())
755+
{
756+
geom = fet->GetGeometryRef();
757+
// get the wkb representation
758+
unsigned char *feature = new unsigned char[geom->WkbSize()];
759+
geom->exportToWkb((OGRwkbByteOrder) endian(), feature);
760+
OGRFeatureDefn * featureDefinition = fet->GetDefnRef();
761+
QString featureTypeName = featureDefinition ?
762+
QString(featureDefinition->GetName()) :
763+
QString("");
764+
765+
f->setGeometryAndOwnership(feature, geom->WkbSize());
766+
767+
}
768+
delete fet;
769+
}
770+
}
771+
722772
std::vector<QgsField> const & QgsOgrProvider::fields() const
723773
{
724774
return attributeFields;
@@ -1155,10 +1205,18 @@ int QgsOgrProvider::capabilities() const
11551205
// the #defines we want to test for here.
11561206

11571207
if (ogrLayer->TestCapability("RandomRead"))
1158-
// TRUE if the GetFeature() method works for this layer.
1208+
// TRUE if the GetFeature() method works *efficiently* for this layer.
1209+
// TODO: Perhaps influence if QGIS caches into memory
1210+
// (vs read from disk every time) based on this setting.
11591211
{
1160-
// TODO: Perhaps influence if QGIS caches into memory (vs read from disk every time) based on this setting.
1212+
ability |= QgsVectorDataProvider::RandomSelectGeometryAtId;
11611213
}
1214+
else
1215+
{
1216+
ability |= QgsVectorDataProvider::SequentialSelectGeometryAtId;
1217+
}
1218+
ability |= QgsVectorDataProvider::SelectGeometryAtId;
1219+
11621220

11631221
if (ogrLayer->TestCapability("SequentialWrite"))
11641222
// TRUE if the CreateFeature() method works for this layer.

‎src/providers/ogr/qgsogrprovider.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,28 @@ class QgsOgrProvider:public QgsVectorDataProvider
116116
/** Return the extent for this data layer
117117
*/
118118
virtual QgsRect *extent();
119+
119120
/**Get an attribute associated with a feature*/
120121
void getFeatureAttribute(OGRFeature * ogrFet, QgsFeature * f, int attindex);
122+
121123
/**
122124
* Get the attributes associated with a feature
123125
*/
124126
void getFeatureAttributes(OGRFeature * ogrFet, QgsFeature * f);
127+
128+
/**
129+
* Get the attributes associated with a feature
130+
* TODO: Get rid of "row" and set up provider-internal caching instead
131+
* ("row" was only ever used in the PostgreSQL provider context anyway)
132+
*/
133+
void getFeatureAttributes(int key, int& row, QgsFeature *f);
134+
135+
/**
136+
* Fetch geometry for a particular feature with id "key",
137+
* modifies "f" in-place.
138+
*/
139+
void getFeatureGeometry(int key, QgsFeature *f);
140+
125141
/**
126142
* Get the field information for the layer
127143
*/

‎src/providers/postgres/qgspostgresprovider.cpp

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,63 @@ void QgsPostgresProvider::getFeatureAttributes(int key, int &row,
777777
}
778778
}
779779

780+
781+
void QgsPostgresProvider::getFeatureGeometry(int key, QgsFeature *f)
782+
{
783+
if (!valid)
784+
{
785+
return;
786+
}
787+
788+
QString cursor = QString("declare qgisf binary cursor for "
789+
"select asbinary(%1,'%2') from %3 where %4 = %5")
790+
.arg(geometryColumn)
791+
.arg(endianString())
792+
.arg(mSchemaTableName)
793+
.arg(primaryKey)
794+
.arg(key);
795+
796+
#ifdef QGISDEBUG
797+
std::cerr << "QgsPostgresProvider::getFeatureGeometry using: " << cursor.toLocal8Bit().data() << std::endl;
798+
#endif
799+
800+
PQexec(connection, "begin work");
801+
PQexec(connection, (const char *)(cursor.utf8()));
802+
803+
QString fetch = "fetch forward 1 from qgisf";
804+
PGresult *geomResult = PQexec(connection, (const char *)fetch);
805+
806+
if (PQntuples(geomResult) == 0)
807+
{
808+
// Nothing found - therefore nothing to change
809+
PQexec(connection,"end work");
810+
PQclear(geomResult);
811+
return;
812+
}
813+
814+
int row = 0;
815+
816+
int returnedLength = PQgetlength(geomResult, row, 0);
817+
818+
if(returnedLength > 0)
819+
{
820+
unsigned char *wkbgeom = new unsigned char[returnedLength + 1];
821+
memset(wkbgeom, '\0', returnedLength + 1);
822+
memcpy(wkbgeom, PQgetvalue(geomResult, row, 0), returnedLength);
823+
f->setGeometryAndOwnership(wkbgeom, returnedLength + 1);
824+
}
825+
else
826+
{
827+
//--std::cout <<"Couldn't get the feature geometry in binary form" << std::endl;
828+
}
829+
830+
PQclear(geomResult);
831+
832+
PQexec(connection,"end work");
833+
834+
}
835+
836+
780837
std::vector<QgsField> const & QgsPostgresProvider::fields() const
781838
{
782839
return attributeFields;
@@ -2257,7 +2314,8 @@ int QgsPostgresProvider::capabilities() const
22572314
QgsVectorDataProvider::ChangeAttributeValues |
22582315
QgsVectorDataProvider::AddAttributes |
22592316
QgsVectorDataProvider::DeleteAttributes |
2260-
QgsVectorDataProvider::ChangeGeometries
2317+
QgsVectorDataProvider::ChangeGeometries |
2318+
QgsVectorDataProvider::SelectGeometryAtId
22612319
);
22622320
}
22632321

‎src/providers/postgres/qgspostgresprovider.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,16 @@ class QgsPostgresProvider:public QgsVectorDataProvider
174174
/**
175175
* Get the attributes associated with a feature
176176
*/
177-
virtual void getFeatureAttributes(int oid, int& row, QgsFeature *f);
177+
virtual void getFeatureAttributes(int key, int& row, QgsFeature *f);
178178

179179
/**Get the attributes with indices contained in attlist*/
180-
void getFeatureAttributes(int oid, int& row, QgsFeature *f, std::list<int> const& attlist);
180+
void getFeatureAttributes(int key, int& row, QgsFeature *f, std::list<int> const& attlist);
181+
182+
/**
183+
* Fetch geometry for a particular feature with id "key",
184+
* modifies "f" in-place.
185+
*/
186+
void getFeatureGeometry(int key, QgsFeature *f);
181187

182188
/** * Get the name of the primary key for the layer
183189
*/

0 commit comments

Comments
 (0)
Please sign in to comment.