Changes between Initial Version and Version 1 of rfc6_sqlgeom


Ignore:
Timestamp:
Apr 28, 2007, 12:01:14 PM (17 years ago)
Author:
warmerdam
Comment:

Ported from doxygen.

Legend:

Unmodified
Added
Removed
Modified
  • rfc6_sqlgeom

    v1 v1  
     1= RFC 6: Geometry and Feature Style as OGR Special Fields =
     2
     3Author: Tamas Szekeres[[BR]]
     4Contact: szekerest@gmail.com[[BR]]
     5Status: Adopted
     6
     7== Summary ==
     8
     9This proposal addresses and issue have been discovered long ago, and OGR provides
     10no equivalent solution so far.
     11
     12Some of the supported formats like Mapinfo.tab may contain multiple geometry types
     13and style information. In order to hanlde this kind of data sources properly a
     14support for selecting the layers by geometry type or by the style info would be
     15highly required. For more details see the following MapServer related bugs later
     16in this document.
     17
     18All of the proposed changes can be found at the tracking bug of this RFC
     19referenced later in this document.
     20
     21== Main concepts ==
     22
     23The most reasonable way to support this feature is to extend the currently
     24existing 'special field' approach to allow specifying more than one fields.
     25Along with the already definied 'FID' field we will add the following ones:
     26
     27 * 'OGR_GEOMETRY' containing the geometry type like 'POINT' or 'POLYGON'.
     28 * 'OGR_STYLE' containing the style string.
     29 * 'OGR_GEOM_WKT' containing the full WKT of the geometry.
     30
     31By providing the aforementioned fields one can make for example
     32the following selections:
     33
     34 * select FID, OGR_GEOMETRY, OGR_STYLE, OGR_GEOM_WKT, * from MyTable where OGR_GEOMETRY='POINT' OR OGR_GEOMETRY='POLYGON'
     35 * select FID, OGR_GEOMETRY, OGR_STYLE, OGR_GEOM_WKT, * from MyTable where OGR_STYLE LIKE '%BRUSH%'
     36 * select FID, OGR_GEOMETRY, OGR_STYLE, OGR_GEOM_WKT, * from MyTable where OGR_GEOM_WKT LIKE 'POLYGON%'
     37 * select distinct OGR_GEOMETRY from MyTable order by OGR_GEOMETRY desc
     38
     39== Implementation ==
     40
     41There are two distinct areas where this feature plays a role
     42
     43 * Feature query implemented at ogrfeaturequery.cpp
     44
     45 * SQL based selection implemented at ogr_gensql.cpp and ogrdatasource.cpp
     46
     47To specify arbitrary number of special fields we will declare an
     48array for the field names and types in ogrfeaturequery.cpp as
     49
     50{{{
     51char* SpecialFieldNames[SPECIAL_FIELD_COUNT]
     52    = {"FID", "OGR_GEOMETRY", "OGR_STYLE", "OGR_GEOM_WKT"};
     53swq_field_type SpecialFieldTypes[SPECIAL_FIELD_COUNT]
     54    = {SWQ_INTEGER, SWQ_STRING, SWQ_STRING, SWQ_STRING};
     55}}}
     56
     57So as to make this array accessible to the other files the followings
     58will be added to ogr_p.h
     59
     60{{{
     61CPL_C_START
     62#include "swq.h"
     63CPL_C_END
     64
     65#define SPF_FID 0
     66#define SPF_OGR_GEOMETRY 1
     67#define SPF_OGR_STYLE 2
     68#define SPF_OGR_GEOM_WKT 3
     69#define SPECIAL_FIELD_COUNT 4
     70
     71extern char* SpecialFieldNames[SPECIAL_FIELD_COUNT];
     72extern swq_field_type SpecialFieldTypes[SPECIAL_FIELD_COUNT];
     73}}}
     74
     75In ogrfeature.cpp the field accessor functions (GetFieldAsString,
     76GetFieldAsInteger, GetFieldAsDouble) will be modified providing
     77the values of the special fields by the field index
     78
     79The following code will be added to the beginning of OGRFeature::GetFieldAsInteger:
     80
     81{{{
     82int iSpecialField = iField - poDefn->GetFieldCount();
     83if (iSpecialField >= 0)
     84{
     85// special field value accessors
     86    switch (iSpecialField)
     87    {
     88    case SPF_FID:
     89        return GetFID();
     90    default:
     91        return 0;
     92    }
     93}
     94}}}
     95
     96The following code will be added to the beginning of OGRFeature::GetFieldAsDouble:
     97
     98{{{
     99int iSpecialField = iField - poDefn->GetFieldCount();
     100if (iSpecialField >= 0)
     101{
     102// special field value accessors
     103    switch (iSpecialField)
     104    {
     105    case SPF_FID:
     106        return GetFID();
     107    default:
     108        return 0.0;
     109    }
     110}
     111}}}
     112
     113The following code will be added to the beginning of OGRFeature::GetFieldAsString:
     114
     115{{{
     116int iSpecialField = iField - poDefn->GetFieldCount();
     117if (iSpecialField >= 0)
     118{
     119// special field value accessors
     120    switch (iSpecialField)
     121    {
     122    case SPF_FID:
     123        sprintf( szTempBuffer, "%d", GetFID() );
     124        return m_pszTmpFieldValue = CPLStrdup( szTempBuffer );
     125    case SPF_OGR_GEOMETRY:
     126        return poGeometry->getGeometryName();
     127    case SPF_OGR_STYLE:
     128        return GetStyleString();
     129    case SPF_OGR_GEOM_WKT:
     130        {
     131            if (poGeometry->exportToWkt( &m_pszTmpFieldValue ) == OGRERR_NONE )
     132                return m_pszTmpFieldValue;
     133            else
     134                return "";
     135        }
     136    default:
     137        return "";
     138    }
     139}
     140}}}
     141
     142The current implementation of OGRFeature::GetFieldAsString uses a static string to
     143hold the const char* return value that is highly avoidable and makes the code
     144thread unsafe. In this regard the 'static char szTempBuffer[80]' will be changed to
     145non static and a new member will be added to OGRFeature in ogrfeature.h as:
     146
     147{{{
     148char * m_pszTmpFieldValue;
     149}}}
     150
     151This member will be initialized to NULL at the constructor, and will be freed using
     152CPLFree() at the destructor of OGRFeature.
     153
     154In OGRFeature::GetFieldAsString all of the occurrences of 'return szTempBuffer;'
     155will be changed to 'return m_pszTmpFieldValue = CPLStrdup( szTempBuffer );'
     156
     157OGRFeature::GetFieldAsString is responsible to destroy the old value of
     158m_pszTmpFieldValue at the beginning of the function:
     159
     160{{{
     161CPLFree(m_pszTmpFieldValue);
     162m_pszTmpFieldValue = NULL;
     163}}}
     164
     165In ogrfeaturequery.cpp we should change OGRFeatureQuery::Compile to add the
     166special fields like:
     167
     168{{{
     169iField = 0;
     170while (iField < SPECIAL_FIELD_COUNT)
     171{
     172    papszFieldNames[poDefn->GetFieldCount() + iField] = SpecialFieldNames[iField];
     173    paeFieldTypes[poDefn->GetFieldCount() + iField] = SpecialFieldTypes[iField];
     174    ++iField;
     175}
     176}}}
     177
     178In ogrfeaturequery.cpp OGRFeatureQueryEvaluator() should be modifyed according to
     179the field specific actions like
     180
     181{{{
     182int iSpecialField = op->field_index - poFeature->GetDefnRef()->GetFieldCount();
     183if( iSpecialField >= 0 )
     184{
     185    if ( iSpecialField < SPECIAL_FIELD_COUNT )
     186    {
     187        switch ( SpecialFieldTypes[iSpecialField] )
     188        {
     189        case SWQ_INTEGER:
     190            sField.Integer = poFeature->GetFieldAsInteger( op->field_index );
     191        case SWQ_STRING:
     192            sField.String = (char*) poFeature->GetFieldAsString( op->field_index );
     193        }     
     194    }
     195    else
     196    {
     197        CPLDebug( "OGRFeatureQuery", "Illegal special field index.");
     198        return FALSE;
     199    }
     200    psField = &sField;
     201}
     202else
     203    psField = poFeature->GetRawFieldRef( op->field_index );
     204}}}
     205
     206In ogrfeaturequery.cpp OGRFeatureQuery::FieldCollector should be modifyed to
     207add the field names like:
     208
     209{{{
     210if( op->field_index >= poTargetDefn->GetFieldCount()
     211        && op->field_index < poTargetDefn->GetFieldCount() + SPECIAL_FIELD_COUNT)
     212        pszFieldName = SpecialFieldNames[op->field_index];
     213}}}
     214
     215In ogrdatasource.cpp ExecuteSQL() will allocate the arrays according to the
     216number of the special fields:
     217
     218{{{
     219sFieldList.names = (char **)
     220        CPLMalloc( sizeof(char *) * (nFieldCount+SPECIAL_FIELD_COUNT) );
     221sFieldList.types = (swq_field_type *) 
     222        CPLMalloc( sizeof(swq_field_type) * (nFieldCount+SPECIAL_FIELD_COUNT) );
     223sFieldList.table_ids = (int *)
     224        CPLMalloc( sizeof(int) * (nFieldCount+SPECIAL_FIELD_COUNT) );
     225sFieldList.ids = (int *)
     226        CPLMalloc( sizeof(int) * (nFieldCount+SPECIAL_FIELD_COUNT) );
     227}}}
     228
     229And the fields will be added as
     230
     231{{{
     232for (iField = 0; iField < SPECIAL_FIELD_COUNT; iField++)
     233{
     234    sFieldList.names[sFieldList.count] = SpecialFieldNames[iField];
     235    sFieldList.types[sFieldList.count] = SpecialFieldTypes[iField];
     236    sFieldList.table_ids[sFieldList.count] = 0;
     237    sFieldList.ids[sFieldList.count] = nFIDIndex + iField;
     238    sFieldList.count++;
     239}
     240}}}
     241
     242For supporting the SQL based queries we should also modify the constructor of
     243OGRGenSQLResultsLayer in ogr_gensql.cpp and set the field type properly:
     244
     245{{{
     246else if ( psColDef->field_index >= iFIDFieldIndex )
     247{
     248    switch ( SpecialFieldTypes[psColDef->field_index - iFIDFieldIndex] )
     249    {
     250    case SWQ_INTEGER:
     251        oFDefn.SetType( OFTInteger );
     252        break;
     253    case SWQ_STRING:
     254        oFDefn.SetType( OFTString );
     255        break;
     256    case SWQ_FLOAT:
     257        oFDefn.SetType( OFTReal );
     258        break;
     259    }
     260}
     261
     262}}}
     263
     264Some of the queries will require to modify OGRGenSQLResultsLayer::PrepareSummary
     265in ogr_gensql.cpp will be simplified
     266(GetFieldAsString will be used in all cases to access the field values):
     267
     268{{{
     269pszError = swq_select_summarize( psSelectInfo, iField,
     270poSrcFeature->GetFieldAsString( psColDef->field_index ) );
     271}}}
     272
     273OGRGenSQLResultsLayer::TranslateFeature should also be modifyed when
     274copying the fields from primary record to the destination feature
     275
     276{{{
     277 if ( psColDef->field_index >= iFIDFieldIndex &&
     278            psColDef->field_index < iFIDFieldIndex + SPECIAL_FIELD_COUNT )
     279{
     280    switch (SpecialFieldTypes[psColDef->field_index - iFIDFieldIndex])
     281    {
     282    case SWQ_INTEGER:
     283        poDstFeat->SetField( iField, poSrcFeat->GetFieldAsInteger(psColDef->field_index) );
     284    case SWQ_STRING:
     285        poDstFeat->SetField( iField, poSrcFeat->GetFieldAsString(psColDef->field_index) );
     286    }
     287}
     288
     289}}}
     290
     291For supporting the 'order by' queries we should also modify
     292OGRGenSQLResultsLayer::CreateOrderByIndex() as:
     293
     294{{{
     295
     296if ( psKeyDef->field_index >= iFIDFieldIndex)
     297{
     298    if ( psKeyDef->field_index < iFIDFieldIndex + SPECIAL_FIELD_COUNT )
     299    {
     300        switch (SpecialFieldTypes[psKeyDef->field_index - iFIDFieldIndex])
     301        {
     302        case SWQ_INTEGER:
     303            psDstField->Integer = poSrcFeat->GetFieldAsInteger(psKeyDef->field_index);
     304        case SWQ_STRING:
     305            psDstField->String = CPLStrdup( poSrcFeat->GetFieldAsString(psKeyDef->field_index) );
     306        }
     307    }
     308    continue;
     309}
     310
     311}}}
     312
     313All of the strings allocated previously should be deallocated later in
     314the same function as:
     315
     316{{{
     317
     318if ( psKeyDef->field_index >= iFIDFieldIndex )
     319{
     320    /* warning: only special fields of type string should be deallocated */
     321    if (SpecialFieldTypes[psKeyDef->field_index - iFIDFieldIndex] == SWQ_STRING)
     322    {
     323        for( i = 0; i < nIndexSize; i++ )
     324        {
     325            OGRField *psField = pasIndexFields + iKey + i * nOrderItems;
     326            CPLFree( psField->String );
     327        }
     328    }
     329    continue;
     330}
     331
     332}}}
     333
     334When ordering by the field values the OGRGenSQLResultsLayer::Compare
     335should also be modifyed:
     336
     337{{{
     338if( psKeyDef->field_index >= iFIDFieldIndex )
     339    poFDefn = NULL;
     340else
     341    poFDefn = poSrcLayer->GetLayerDefn()->GetFieldDefn(
     342        psKeyDef->field_index );
     343
     344if( (pasFirstTuple[iKey].Set.nMarker1 == OGRUnsetMarker
     345        && pasFirstTuple[iKey].Set.nMarker2 == OGRUnsetMarker)
     346    || (pasSecondTuple[iKey].Set.nMarker1 == OGRUnsetMarker
     347        && pasSecondTuple[iKey].Set.nMarker2 == OGRUnsetMarker) )
     348    nResult = 0;
     349else if ( poFDefn == NULL )
     350{
     351    switch (SpecialFieldTypes[psKeyDef->field_index - iFIDFieldIndex])
     352    {
     353    case SWQ_INTEGER:
     354        if( pasFirstTuple[iKey].Integer < pasSecondTuple[iKey].Integer )
     355            nResult = -1;
     356        else if( pasFirstTuple[iKey].Integer > pasSecondTuple[iKey].Integer )
     357            nResult = 1;
     358        break;
     359    case SWQ_STRING:
     360        nResult = strcmp(pasFirstTuple[iKey].String,
     361                        pasSecondTuple[iKey].String);
     362        break;
     363    }
     364}
     365}}}
     366
     367== Adding New Special Fields ==
     368
     369Adding a new special field in a subsequent development phase is fairly
     370straightforward and the following steps should be made:
     371
     372 1. In ogr_p.h a new constant should be added with the value of the SPECIAL_FIELD_COUNT and SPECIAL_FIELD_COUNT should be incremented by one.
     373
     374 2. In ogrfeaturequery.cpp the special field string and the type should be added to SpecialFieldNames and SpecialFieldTypes respectively
     375
     376 3. The field value accessors (OGRFeature::GetFieldAsString, OGRFeature::GetFieldAsInteger, OGRFeature::GetFieldAsDouble) should be modifyed to provide the value of the new special field. All of these functions provide const return values so GetFieldAsString should retain the value in the m_pszTmpFieldValue member.   
     377
     378 4. When adding a new value with a type other than SWQ_INTEGER and SWQ_STRING the following functions might also be modified accordingly:
     379  * OGRGenSQLResultsLayer::OGRGenSQLResultsLayer
     380  * OGRGenSQLResultsLayer::TranslateFeature
     381  * OGRGenSQLResultsLayer::CreateOrderByIndex
     382  * OGRGenSQLResultsLayer::Compare
     383  * OGRFeatureQueryEvaluator
     384
     385== Backward Compatibility ==
     386
     387In most cases the backward compatibility of the OGR library will be retained.
     388However the special fields will potentially conflict with regard fields with
     389the given names. When accessing the field values the special fields will
     390take pecedence over the other fields with the same names.
     391
     392When using OGRFeature::GetFieldAsString the returned value will be stored as
     393a member variable instead of a static variable. The string will be deallocated
     394and will no longer be usable after the destruction of the feature.
     395
     396== Regression Testing ==
     397
     398A new gdalautotest/ogr/ogr_sqlspecials.py script to test support for all
     399special fields in the ExecuteSQL() call and with WHERE clauses.
     400
     401== Documentation ==
     402
     403The OGR SQL document will be updated to reflect the support for special fields.
     404
     405== Implementation Staffing ==
     406
     407Tamas Szekeres will implement the bulk of the RFC in time for GDAL/OGR 1.4.0.
     408
     409Frank Warmerdam will consider how the backward compatibility issues
     410(with special regard to the modified lifespan of the GetFieldAsString returned value)
     411will affect the other parts of the OGR project and will write the
     412Python regression testing script.
     413
     414== References ==
     415
     416 * Tracking bug for this feature (containing all of the proposed code changes): #1333
     417
     418 * MapServer related bugs:
     419   * [http://trac.osgeo.org/mapserver/ticket/1129 1129]
     420   * [http://trac.osgeo.org/mapserver/ticket/1438 1438]
     421
     422== Voting History ==
     423
     424Frank Warmerdam +1[[BR]]
     425Daniel Morissette +1[[BR]]
     426Howard Butler +0[[BR]]
     427Andrey Kiselev +1[[BR]]