RFC 6:作为OGR特殊字段的几何图形和要素样式

作者:Tamas Szekeres

联系方式:szekerest@gmail.com

状态:通过

总结

这个提议解决了很久以前就发现的问题,而OGR至今还没有提供等效的解决方案。

一些受支持的格式,如Mapinfo.tab可能包含多种几何图形类型和样式信息。为了正确地处理这类数据源,需要支持按几何图形类型或样式信息选择图层。有关更多详细信息,请参阅本文档后面与MapServer相关的以下错误。

所有提议的更改都可以在本文档后面引用的RFC的跟踪错误中找到。

主要概念

The most reasonable way to support this feature is to extend the currently existing 'special field' approach to allow specifying more than one fields. Along with the already defined 'FID' field we will add the following ones:

  • “OGR_GEOMETRY”包含“POINT”或“POLYGON”等几何类型。

  • 包含样式字符串的“OGR_STYLE”。

  • “OGR_GEOM_WKT”包含几何图形的完整WKT。

通过提供上述字段,例如可以进行以下选择:

  • 选择FID,OGR_GEOMETRY,OGR_STYLE,OGR_GEOM_WKT, * 从MyTable,其中OGR_GEOMETRY='POINT'或OGR_GEOMETRY='POLYGON'

  • 选择FID,OGR_GEOMETRY,OGR_STYLE,OGR_GEOM_WKT, * 从MyTable,其中OGR_样式类似于'%BRUSH%'

  • 选择FID,OGR_GEOMETRY,OGR_STYLE,OGR_GEOM_WKT, * 从我的表中,OGR_GEOM_WKT喜欢“POLYGON%”

  • 从MyTable order by OGR_GEOMETRY desc中选择不同的OGR_GEOMETRY

实施

在这两个不同的领域中,这个特性发挥了作用

  • 在ogrfeaturequery.cpp上实现的功能查询

  • 基于SQL的选择在ogr_gensql.cpp和ogrdatasource.cpp上实现

要指定任意数量的特殊字段,我们将ogrfeaturequery.cpp中的字段名和类型的数组声明为

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};

为了使这个数组可以被其他文件访问,下面的内容将被添加到ogr_p.h中

CPL_C_START
#include "ogr_swq.h"
CPL_C_END

#define SPF_FID 0
#define SPF_OGR_GEOMETRY 1
#define SPF_OGR_STYLE 2
#define SPF_OGR_GEOM_WKT 3
#define SPECIAL_FIELD_COUNT 4

extern char* SpecialFieldNames[SPECIAL_FIELD_COUNT];
extern swq_field_type SpecialFieldTypes[SPECIAL_FIELD_COUNT];

在ogrfeature.cpp中,字段访问器函数(GetFieldAsString、GetFieldAsInteger、GetFieldAsDouble)将被修改,通过字段索引提供特殊字段的值

以下代码将添加到OGRFeature::GetFieldAsInteger的开头:

int iSpecialField = iField - poDefn->GetFieldCount();
if (iSpecialField >= 0)
{
// special field value accessors
    switch (iSpecialField)
    {
    case SPF_FID:
        return GetFID();
    default:
        return 0;
    }
}

以下代码将添加到OGRFeature::GetFieldAsDouble的开头:

int iSpecialField = iField - poDefn->GetFieldCount();
if (iSpecialField >= 0)
{
// special field value accessors
    switch (iSpecialField)
    {
    case SPF_FID:
        return GetFID();
    default:
        return 0.0;
    }
}

以下代码将添加到OGRFeature::GetFieldAsString的开头:

int iSpecialField = iField - poDefn->GetFieldCount();
if (iSpecialField >= 0)
{
// special field value accessors
    switch (iSpecialField)
    {
    case SPF_FID:
        sprintf( szTempBuffer, "%d", GetFID() );
        return m_pszTmpFieldValue = CPLStrdup( szTempBuffer );
    case SPF_OGR_GEOMETRY:
        return poGeometry->getGeometryName();
    case SPF_OGR_STYLE:
        return GetStyleString();
    case SPF_OGR_GEOM_WKT:
        {
            if (poGeometry->exportToWkt( &m_pszTmpFieldValue ) == OGRERR_NONE )
                return m_pszTmpFieldValue;
            else
                return "";
        }
    default:
        return "";
    }
}

OGRFeature::GetFieldAsString的当前实现使用静态字符串保存const字符 * 返回值,该值是高度可避免的,并且使代码线程不安全。在这方面,静态char szTempBuffer [80] '将更改为非静态,并将一个新成员添加到OGRFeature.h中的OGRFeature:

char * m_pszTmpFieldValue;

此成员将在构造函数中初始化为空,并在OGRFeature的析构函数中使用CPLFree()释放。

在OGRFeature::GetFieldAsString中,所有出现的'return szTempBuffer;'将更改为'return m_pszTmpFieldValue=CPLStrdup(szTempBuffer);'

OGRFeature::GetFieldAsString负责在函数开头销毁m_pszTmpFieldValue的旧值:

CPLFree(m_pszTmpFieldValue);
m_pszTmpFieldValue = NULL;

在ogrfeaturequery.cpp中,我们应该更改ogrfeaturequery::Compile以添加特殊字段,如:

iField = 0;
while (iField < SPECIAL_FIELD_COUNT)
{
    papszFieldNames[poDefn->GetFieldCount() + iField] = SpecialFieldNames[iField];
    paeFieldTypes[poDefn->GetFieldCount() + iField] = SpecialFieldTypes[iField];
    ++iField;
}

在ogrfeaturequery.cpp中,OGRFeatureQueryEvaluator()应根据字段特定的操作进行修改,如

int iSpecialField = op->field_index - poFeature->GetDefnRef()->GetFieldCount();
if( iSpecialField >= 0 )
{
    if ( iSpecialField < SPECIAL_FIELD_COUNT )
    {
        switch ( SpecialFieldTypes[iSpecialField] )
        {
        case SWQ_INTEGER:
            sField.Integer = poFeature->GetFieldAsInteger( op->field_index );
        case SWQ_STRING:
            sField.String = (char*) poFeature->GetFieldAsString( op->field_index );
        }
    }
    else
    {
        CPLDebug( "OGRFeatureQuery", "Illegal special field index.");
        return FALSE;
    }
    psField = &sField;
}
else
    psField = poFeature->GetRawFieldRef( op->field_index );

在ogrfeaturequery.cpp中,应修改ogrfeaturequery::FieldCollector以添加以下字段名:

if( op->field_index >= poTargetDefn->GetFieldCount()
        && op->field_index < poTargetDefn->GetFieldCount() + SPECIAL_FIELD_COUNT)
        pszFieldName = SpecialFieldNames[op->field_index];

在ogrdatasource.cpp ExecuteSQL()中,将根据特殊字段的数量分配数组:

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) );

字段将添加为

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++;
}

为了支持基于SQL的查询,我们还应该修改ogr_gensql.cpp中OGRGenSQLResultsLayer的构造函数,并正确设置字段类型:

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;
    case SWQ_FLOAT:
        oFDefn.SetType( OFTReal );
        break;
    }
}

一些需要修改ogr_gensql.cpp中OGRGenSQLResultsLayer::PrepareSummary的查询将被简化(GetFieldAsString将在所有情况下用于访问字段值):

pszError = swq_select_summarize( psSelectInfo, iField,
poSrcFeature->GetFieldAsString( psColDef->field_index ) );

将字段从主记录复制到目标功能时,还应修改OGRGenSQLResultsLayer::TranslateFeature

 if ( psColDef->field_index >= iFIDFieldIndex &&
            psColDef->field_index < iFIDFieldIndex + SPECIAL_FIELD_COUNT )
{
    switch (SpecialFieldTypes[psColDef->field_index - iFIDFieldIndex])
    {
    case SWQ_INTEGER:
        poDstFeat->SetField( iField, poSrcFeat->GetFieldAsInteger(psColDef->field_index) );
    case SWQ_STRING:
        poDstFeat->SetField( iField, poSrcFeat->GetFieldAsString(psColDef->field_index) );
    }
}

为了支持“order by”查询,我们还应该将OGRGenSQLResultsLayer::CreateOrderByIndex()修改为:

if ( psKeyDef->field_index >= iFIDFieldIndex)
{
    if ( psKeyDef->field_index < iFIDFieldIndex + SPECIAL_FIELD_COUNT )
    {
        switch (SpecialFieldTypes[psKeyDef->field_index - iFIDFieldIndex])
        {
        case SWQ_INTEGER:
            psDstField->Integer = poSrcFeat->GetFieldAsInteger(psKeyDef->field_index);
        case SWQ_STRING:
            psDstField->String = CPLStrdup( poSrcFeat->GetFieldAsString(psKeyDef->field_index) );
        }
    }
    continue;
}

以前分配的所有字符串稍后都应在以下同一函数中解除分配:

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;
}

按字段值排序时,还应修改OGRGenSQLResultsLayer::Compare:

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;
    }
}

添加新的特殊字段

在随后的开发阶段添加一个新的特殊字段相当简单,应该执行以下步骤:

  1. 在ogr_p.h中,一个新的常数应该与特殊字段计数的值相加,并且特殊字段计数应该递增一。

  2. 在ogrfeaturequery.cpp中,特殊字段字符串和类型应分别添加到SpecialFieldNames和SpecialFieldTypes中

  3. 应修改字段值访问器(OGRFeature::GetFieldAsString、OGRFeature::GetFieldAsInteger、OGRFeature::GetFieldAsDouble)以提供新特殊字段的值。所有这些函数都提供常量返回值,因此GetFieldAsString应该在m_pszTmpFieldValue成员中保留该值。

  4. 使用SWQ_INTEGER和SWQ_STRING以外的类型添加新值时,还可以相应地修改以下函数:

  • OGRGenSQLResultsLayer::OGRGenSQLResultsLayer

  • OGRGenSQLResultsLayer::TranslateFeature

  • OGRGenSQLResultsLayer::CreateOrderByIndex

  • OGRGenSQLResultsLayer::比较

  • OGRFeatureQueryEvaluator

向后兼容性

在大多数情况下,OGR库的向后兼容性将被保留。但是,对于具有给定名称的字段,特殊字段可能会发生冲突。访问字段值时,特殊字段将优先于具有相同名称的其他字段。

使用OGRFeature::GetFieldAsString时,返回值将存储为成员变量,而不是静态变量。字符串将被释放,并且在功能被破坏后将不再可用。

回归检验

一个新的gdalautotest/ogr/ogr_sqlspecials.py脚本,用于测试对ExecuteSQL()调用中所有特殊字段和WHERE子句的支持。

文档

将更新OGR SQL文档以反映对特殊字段的支持。

实施人员配置

Tamas Szekeres将及时为GDAL/OGR 1.4.0实现大部分RFC。

Frank Warmerdam将考虑向后兼容性问题(特别是GetFieldAsString返回值的修改寿命)将如何影响OGR项目的其他部分,并将编写Python回归测试脚本。

工具书类

  • 跟踪此功能的错误(包含所有建议的代码更改):#1333

  • 与MapServer相关的错误:

投票历史

弗兰克·温特丹+1

丹尼尔·莫里塞特+1

霍华德·巴特勒+0

安德烈·基塞列夫+1