Author: Tamas Szekeres
Contact: szekerest@gmail.com
Status: Proposed
Some of the supported formats like Mapinfo.tab may contain multiple geometry types and style information. In order to hanlde this kind of data sources properly a support for selecting the layers by geometry type or by the style info would be highly required. For more details see the following MapServer related bugs later in this document.
All of the proposed changes can be found at the tracking bug of this RFC referenced later in this document.
By providing the aforementioned fields one can make for example the following selections:
To specify arbitrary number of special fields we will declare an array for the field names and types in ogrfeaturequery.cpp as
char* SpecialFieldNames[SPECIAL_FIELD_COUNT] = {"FID", "OGR_GEOMETRY", "OGR_STYLE", "OGR_GEOM_WKT"}; swq_field_type SpecialFieldTypes[SPECIAL_FIELD_COUNT] = {SWQ_INTEGER, SWQ_STRING, SWQ_STRING, SWQ_STRING};
So as to make this array accessible to the other files the followings will be added to ogr_p.h
CPL_C_START include "swq.h" CPL_C_END
define SPECIAL_FIELD_COUNT 4 extern char* SpecialFieldNames[SPECIAL_FIELD_COUNT]; extern swq_field_type SpecialFieldTypes[SPECIAL_FIELD_COUNT];
In ogrfeaturequery.cpp we should change OGRFeatureQuery::Compile to add the special fields like:
iField = 0; while (iField < SPECIAL_FIELD_COUNT) { papszFieldNames[poDefn->GetFieldCount() + iField] = SpecialFieldNames[iField]; paeFieldTypes[poDefn->GetFieldCount() + iField] = SpecialFieldTypes[iField]; ++iField; }
In ogrfeaturequery.cpp OGRFeatureQueryEvaluator() should be modifyed according to the field specific actions like
switch (op->field_index - poFeature->GetDefnRef()->GetFieldCount()) { case 0: sField.Integer = poFeature->GetFID(); break; case 1: sField.String = (char*) poFeature->GetGeometryRef()->getGeometryName(); /* should not destroy string literals */ break; case 2: sField.String = (char*) poFeature->GetStyleString(); /* should not destroy the style string managed by OGRFeature */ break; case 3: if ( poFeature->GetGeometryRef()->exportToWkt( &sField.String ) != OGRERR_NONE ) return FALSE; /* should finalize */ finalizer.Add( sField.String ); break; default: CPLDebug( "OGRFeatureQuery", "Illegal special field index."); return FALSE; }
So as to deallocate the newly created wkt in the previous code we have introduced a helper class 'finalizer' as
class StringFinalizer { public: StringFinalizer( ) { _ptr = NULL; } ~StringFinalizer() { if ( _ptr ) CPLFree( _ptr ); } void Add( char* ptr) { _ptr = ptr; } private: char* _ptr; };
In ogrfeaturequery.cpp OGRFeatureQuery::FieldCollector should be modifyed to add the field names like:
if( op->field_index >= poTargetDefn->GetFieldCount() && op->field_index < poTargetDefn->GetFieldCount() + SPECIAL_FIELD_COUNT) pszFieldName = SpecialFieldNames[op->field_index];
In ogrdatasource.cpp ExecuteSQL() will allocate the arrays according to the number of the special fields:
sFieldList.names = (char **) CPLMalloc( sizeof(char *) * (nFieldCount+SPECIAL_FIELD_COUNT) ); sFieldList.types = (swq_field_type *) CPLMalloc( sizeof(swq_field_type) * (nFieldCount+SPECIAL_FIELD_COUNT) ); sFieldList.table_ids = (int *) CPLMalloc( sizeof(int) * (nFieldCount+SPECIAL_FIELD_COUNT) ); sFieldList.ids = (int *) CPLMalloc( sizeof(int) * (nFieldCount+SPECIAL_FIELD_COUNT) );
And the fields will be added as
for (iField = 0; iField < SPECIAL_FIELD_COUNT; iField++) { sFieldList.names[sFieldList.count] = SpecialFieldNames[iField]; sFieldList.types[sFieldList.count] = SpecialFieldTypes[iField]; sFieldList.table_ids[sFieldList.count] = 0; sFieldList.ids[sFieldList.count] = nFIDIndex + iField; sFieldList.count++; }
For supporting the SQL based queries we should also modify the constructor of OGRGenSQLResultsLayer in ogr_gensql.cpp and set the field type properly:
else if ( psColDef->field_index >= iFIDFieldIndex ) { switch ( SpecialFieldTypes[psColDef->field_index - iFIDFieldIndex] ) { case SWQ_INTEGER: oFDefn.SetType( OFTInteger ); break; case SWQ_STRING: oFDefn.SetType( OFTString ); break; } }
Some of the queries will require to modify OGRGenSQLResultsLayer::PrepareSummary in ogr_gensql.cpp:
if( psColDef->field_index == iFIDFieldIndex ) { // Special case where the column is "FID" char szBuffer[255]; sprintf( szBuffer, "%ld", poSrcFeature->GetFID() ); pszError = swq_select_summarize( psSelectInfo, iField, szBuffer); } else if( psColDef->field_index == iFIDFieldIndex + 1 ) { // Special case where the column is "OGR_GEOMETRY" pszError = swq_select_summarize( psSelectInfo, iField, poSrcFeature->GetGeometryRef()->getGeometryName()); } else if( psColDef->field_index == iFIDFieldIndex + 2 ) { // Special case where the column is "OGR_STYLE" pszError = swq_select_summarize( psSelectInfo, iField, poSrcFeature->GetStyleString()); } else if( psColDef->field_index == iFIDFieldIndex + 3 ) { // Special case where the column is "OGR_GEOMETRY_WKT" char *wkt = NULL; if ( poSrcFeature->GetGeometryRef()->exportToWkt( &wkt ) != OGRERR_NONE ) { pszError = swq_select_summarize( psSelectInfo, iField, wkt); } if (wkt) CPLFree(wkt); }
OGRGenSQLResultsLayer::TranslateFeature should also be modifyed when copying the fields from primary record to the destination feature
if( psColDef->field_index == iFIDFieldIndex ) poDstFeat->SetField( iField, (int) poSrcFeat->GetFID() ); else if( psColDef->field_index == iFIDFieldIndex + 1) poDstFeat->SetField( iField, poSrcFeat->GetGeometryRef()->getGeometryName() ); else if( psColDef->field_index == iFIDFieldIndex + 2) poDstFeat->SetField( iField, poSrcFeat->GetStyleString() ); else if( psColDef->field_index == iFIDFieldIndex + 3) { char *wkt = NULL; if ( poSrcFeat->GetGeometryRef()->exportToWkt( &wkt ) == OGRERR_NONE ) poDstFeat->SetField( iField, wkt ); if (wkt) CPLFree(wkt); }
For supporting the 'order by' queries we should also modify OGRGenSQLResultsLayer::CreateOrderByIndex() as:
if( psKeyDef->field_index == iFIDFieldIndex ) { psDstField->Integer = poSrcFeat->GetFID(); continue; } if( psKeyDef->field_index == iFIDFieldIndex + 1 ) { psDstField->String = CPLStrdup( poSrcFeat->GetGeometryRef()->getGeometryName() ); continue; } if( psKeyDef->field_index == iFIDFieldIndex + 2 ) { psDstField->String = CPLStrdup( poSrcFeat->GetStyleString() ); continue; } if( psKeyDef->field_index == iFIDFieldIndex + 3 ) { if ( poSrcFeat->GetGeometryRef()->exportToWkt( &psDstField->String ) == OGRERR_NONE ) continue; }
All of the strings ellocated previously should be deallocated later in the same function as:
if ( psKeyDef->field_index >= iFIDFieldIndex ) { /* warning: only special fields of type string should be deallocated */ if (SpecialFieldTypes[psKeyDef->field_index - iFIDFieldIndex] == SWQ_STRING) { for( i = 0; i < nIndexSize; i++ ) { OGRField *psField = pasIndexFields + iKey + i * nOrderItems; CPLFree( psField->String ); } } continue; }
When ordering by the field values the OGRGenSQLResultsLayer::Compare should also be modifyed:
if( psKeyDef->field_index >= iFIDFieldIndex ) poFDefn = NULL; else poFDefn = poSrcLayer->GetLayerDefn()->GetFieldDefn( psKeyDef->field_index );
if( (pasFirstTuple[iKey].Set.nMarker1 == OGRUnsetMarker && pasFirstTuple[iKey].Set.nMarker2 == OGRUnsetMarker) || (pasSecondTuple[iKey].Set.nMarker1 == OGRUnsetMarker && pasSecondTuple[iKey].Set.nMarker2 == OGRUnsetMarker) ) nResult = 0; else if ( poFDefn == NULL ) { switch (SpecialFieldTypes[psKeyDef->field_index - iFIDFieldIndex]) { case SWQ_INTEGER: if( pasFirstTuple[iKey].Integer < pasSecondTuple[iKey].Integer ) nResult = -1; else if( pasFirstTuple[iKey].Integer > pasSecondTuple[iKey].Integer ) nResult = 1; break; case SWQ_STRING: nResult = strcmp(pasFirstTuple[iKey].String, pasSecondTuple[iKey].String); break; } }
http://bugzilla.remotesensing.org/show_bug.cgi?id=1333
http://mapserver.gis.umn.edu/bugs/show_bug.cgi?id=1129
http://mapserver.gis.umn.edu/bugs/show_bug.cgi?id=1438