Maintain shape 'selection' following the reload of an OGR Layer
Shapefiles maintain a set of so-called 'visibility' flags for each individual Shape. This includes the 'selected' state and also a 'hidden' state, which can be user-specified. When the Shapefile is the in-memory version of an OGR Layer, and a call is made to ReloadOgrLayerFromSource, all Shapefile attributes (e.g. Selectable) and visibility flags are lost.
As it stands, the programmer would have to maintain an external list of the selected and hidden shapes, and attempt to restore those following the reload. This 'improvement' would build the functionality into the OCX.
In order to better support this feature, it was determined that it would be helpful to have a way to look up OGR-based Shapes by the OGR_FID. It is not practical to maintain a list of the ‘selected’ Shapes by their Shape index, for at least two reasons.
Shape indexes are assigned (0 to n-1) by simple iteration of the query results, but depending upon your query, a SQL result set is not guaranteed to return the rows in the same order from query to query. This problem can be mitigated, however…
If rows are added or removed by another user in a multi-user environment, the updated result-set will not have the same rows as before, and you would not know which Shape indexes were still valid, and which were invalid.
I had considered options to restructure the internal workings so that the Shape indices, at least while in-memory, would maintain fixed values as rows were added and removed. However, after some discussion, it was thought that such an internal restructuring would introduce a fair amount of risk and require extensive retesting of mostly stable code.
What I settled on was a simple mapping of the OGR_FID to the current Shape index. When the layer is reloaded, the mapping is rebuilt, and any saved references to existing FIDs (by an external program) can easily be resolved to their currently assigned row.
Two new methods have been added to the Shapefile interface:
boolean HasOgrFidMapping(), read-only property, returns true or false
Use this property to determine whether or not the mapping exists. If an OGR_FID column exists in the current table, the mapping is automatically built and maintained internally, and this property will return True.
long OgrFid2ShapeIndex(long OgrFid), returns a long integer, being the Shape Index that is currently associated with the specified OgrFid. If you know the OgrFid of a specific feature, you don’t need to know the currently assigned Shape Index. In high-level languages, this function can be used inline, such that for any methods that expect a Shape Index, you could instead do something like
result = sf.CellValue(fieldIndex, sf.OgrFid2ShapeIndex(OgrFid))
This mechanism was then used internally to help persist the Shapefile ‘visibility’ flags from one Shapefile to the next during a Reload of the OGR Layer. Internal helper methods are used to map the current flags to the FID values, then restore those values to any rows that still exist in the new Shapefile.
Currently, I am only restoring the ‘hidden’ flag and the ‘selectable’ flag, as these are the only ones that made sense to persist as the layer is reloaded. Additionally, I am restoring the ‘Selectable’ property to the Shapefile.
I should also note that this logic was also required when Reprojecting a Shapefile (when not reprojecting in-place), so that the resulting Shapefile maintained the same visibility attributes.