GDAL源码剖析(十一)之OGR投影说明

摘要:
OGRSpaceReference类和OGRCoordinatieTransformation类主要用于提供定义坐标系(投影和标高)和转换坐标。这两个类基于OpenGIS的坐标转换描述,并使用WellKnownText格式表示坐标系。关于OpenGIS坐标系统和空间参考坐标的抽象模型文件的一些信息可以在OGC(开放地理空间联盟)的网站上找到。

一、简介

本文参考英文地址:http://www.gdal.org/ogr/osr_tutorial.html

OGRSpatialReference类和OGRCoordinateTransformation类主要用来提供定义坐标系统(投影和水准面)和转换坐标。这两个类都基于OpenGIS的坐标转换说明,并且使用Well Known Text格式来进行表述坐标系统。

一些关于OpenGIS坐标系统的资料,以及空间参考坐标抽象模型文件可以在OGC(Open Geospatial Consortium)的网站上找到。GeoTIFF投影转换列表(GeoTIFF Projections Transform List)网页可以更好的帮助你理解WKT的规则,同时EPSG的网站也是很有用的资料。

二、定义地理坐标系统

坐标系统使用OGRSpatialReference类来进行封装。这里提供了数种初始化OGRSpatialReference类的方式。这里有两类主要的坐标系统,第一种是地理坐标系统(位置信息使用经纬度来表示的),第二种是投影坐标系统(比如UTM-通用横轴墨卡托投影,位置信息使用米或者英尺来表示)。

一个地理坐标系统包含的信息有一个大地基准(里面含有一个使用长半轴和扁率的倒数来表示的托球体),一个中央经线(通常是本初子午线,也就是0度经线Greenwich), 此外还有一个角度的度量单位,使用度而不是弧度。如果含有这些信息,就可以构造一个有效的地理坐标系统。

OGRSpatialReference oSRS;
oSRS.SetGeogCS( "Mygeographic coordinate system",
               "WGS_1984",
               "My WGS84 Spheroid",
               SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING,
               "Greenwich", 0.0,
                "degree", SRS_UA_DEGREE_CONV );

在上面的代码中,名称为“My geographic coordinate system”,“My WGS84 Spheroid”,“Greenwich”和“degree”的并不是关键词,这些主要是用来给用户进行说明的。然而,“WGS_1984”是一个定义大地基准的关键词,注意:这里的大地基准必须是一个有效的大地基准!(这句话的意思,前面的那些字符串就是随便指定的,用来显示的,后面的WGS_1984这个位置的字符串,必须是一个有效的,不能随便命名,具体后面会说到)。

OGRSpatialReference可以使用一些常用的字符串来进行建立一个常用的坐标系统,这些字符串包括“NAD27”、“NAD83”,“WGS72”和“WGS84”等,使用的函数是SetWellKnownGeogCS(),使用方式见下面。

oSRS.SetWellKnownGeogCS( "WGS84" );

而且,还可以使用EPSG数据库定义的GCS代码来定义一个地理坐标系统,如:

oSRS.SetWellKnownGeogCS( "EPSG:4326" );

为了方便的和其他库进行关联,这里可以转换为OpenGIS的WKT格式。同样OGRSpatialReference可以使用一个WKT来进行初始化,或者将里面的信息导出为WKT格式。

char    *pszWKT =NULL;
SRS.SetWellKnownGeogCS( "WGS84" );
oSRS.exportToWkt( &pszWKT );
printf( "%s\n", pszWKT );

上面的语句会输出下面的内容:

GEOGCS["WGS84",DATUM["WGS_1984",SPHEROID["WGS84",6378137,298.257223563,AUTHORITY["EPSG",7030]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG",6326]],PRIMEM["Greenwich",0,AUTHORITY["EPSG",8901]],UNIT["DMSH",0.0174532925199433,AUTHORITY["EPSG",9108]],AXIS["Lat",NORTH],AXIS["Long",EAST],AUTHORITY["EPSG",4326]]

将上面的字符串格式调整成更好看的样子:

GEOGCS["WGS 84",
   DATUM["WGS_1984",
       SPHEROID["WGS 84",6378137,298.257223563,
           AUTHORITY["EPSG",7030]],
       TOWGS84[0,0,0,0,0,0,0],
       AUTHORITY["EPSG",6326]],
    PRIMEM["Greenwich",0,AUTHORITY["EPSG",8901]],
   UNIT["DMSH",0.0174532925199433,AUTHORITY["EPSG",9108]],
   AXIS["Lat",NORTH],
   AXIS["Long",EAST],
   AUTHORITY["EPSG",4326]]

函数OGRSpatialReference::importFromWkt()可以从一个WKT定义的坐标系统来构造一个OGRSpatialReference类对象。

三、定义投影坐标系统

一个投影坐标系统(比如UTM,兰伯特等角圆锥投影等)需要建立在一个地理坐标系统之上,在投影坐标系统中,坐标点使用米或者英尺等长度单位来表示,同时也可以用经纬度的角度坐标来表示。下面将定义一个UTM的第17带的投影坐标系统,基于WGS84的大地基准椭球体。

OGRSpatialReference    oSRS;
oSRS.SetProjCS( "UTM 17(WGS84) in northern hemisphere." );
oSRS.SetWellKnownGeogCS("WGS84" );
oSRS.SetUTM( 17, TRUE );

首先调用SetProjCS()函数设置投影坐标系统的名称,然后使用函数SetWellKnownGeogCS()指定地理坐标系统,最后调用函数SetUTM()设置投影转换参数信息。完成这些工作之后就定义了一个有效的投影坐标系统。这里必须要注意定义OGRSpatialReference的顺序!

上面定义的投影使用WKT表示的形式如下,注意UTM17会使用横轴墨卡托的分带投影参数来表示。

PROJCS["UTM 17 (WGS84) in northernhemisphere.",
   GEOGCS["WGS 84",
       DATUM["WGS_1984",
           SPHEROID["WGS 84",6378137,298.257223563,
               AUTHORITY["EPSG",7030]],
           TOWGS84[0,0,0,0,0,0,0],
           AUTHORITY["EPSG",6326]],
        PRIMEM["Greenwich",0,AUTHORITY["EPSG",8901]],
       UNIT["DMSH",0.0174532925199433,AUTHORITY["EPSG",9108]],
       AXIS["Lat",NORTH],
       AXIS["Long",EAST],
       AUTHORITY["EPSG",4326]],
   PROJECTION["Transverse_Mercator"],
   PARAMETER["latitude_of_origin",0],
   PARAMETER["central_meridian",-81],
   PARAMETER["scale_factor",0.9996],
   PARAMETER["false_easting",500000],
   PARAMETER["false_northing",0]]

这里提供了很多设置投影坐标的方法,包括SetTM()(横轴墨卡托投影), SetLCC()(兰伯特等角圆锥投影)和SetMercator()。

四、获取坐标系统信息

一旦一个OGRSpatialReference对象进行创建,那么就可以获取里面的各种信息,比如可以使用OGRSpatialReference::IsProjected()OGRSpatialReference::IsGeographic()方法来判断坐标系统是地理坐标系统还是投影坐标系统。使用函数OGRSpatialReference::GetSemiMajor()OGRSpatialReference::GetSemiMinor()OGRSpatialReference::GetInvFlattening()方法可以用来获取椭球体信息,分别是椭球体的长半轴,短半轴以及扁率的倒数。使用OGRSpatialReference::GetAttrValue()方法可以用来获取PROJCS、GEOGCS、DATUM、SPHEROID和PROJECTION的名称字符串。使用OGRSpatialReference::GetProjParm()方法可以获取投影参数信息。使用OGRSpatialReference::GetLinearUnits()方法可以获取长度单位类型,并将其转换为米。

下面的代码是一个获取坐标系统信息的示例,摘自ogr_srs_proj4.cpp文件中。使用GetAttrValue()获取投影名称,使用GetProjParm()获取投影参数信息。GetAttrValue()函数的第一个值节点就是WKT字符串的描述信息。投影参数的宏定义(比如SRS_PP_CENTRAL_MERIDIAN)和函数GetProjParm()一起使用,可以用来获取投影的参数。更多的使用方式可以参考文件ogrspatialreference.cpp中的相关代码。

const char *pszProjection = poSRS->GetAttrValue("PROJECTION");
if( pszProjection == NULL )
{
    if( poSRS->IsGeographic() )
        sprintf(szProj4+strlen(szProj4), "+proj=longlat " );
    else
        sprintf(szProj4+strlen(szProj4), "unknown " );
}
else if(EQUAL(pszProjection,SRS_PT_CYLINDRICAL_EQUAL_AREA) )
{
    sprintf(szProj4+strlen(szProj4),
      "+proj=cea +lon_0=%.9f +lat_ts=%.9f +x_0=%.3f +y_0=%.3f ",
           poSRS->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0),
           poSRS->GetProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0),
           poSRS->GetProjParm(SRS_PP_FALSE_EASTING,0.0),
           poSRS->GetProjParm(SRS_PP_FALSE_NORTHING,0.0));
}
...
const char *pszProjection = poSRS->GetAttrValue("PROJECTION");
if( pszProjection == NULL )
{
    if( poSRS->IsGeographic() )
        sprintf(szProj4+strlen(szProj4), "+proj=longlat " );
    else
        sprintf(szProj4+strlen(szProj4), "unknown " );
}
else if(EQUAL(pszProjection,SRS_PT_CYLINDRICAL_EQUAL_AREA) )
{
    sprintf(szProj4+strlen(szProj4),
      "+proj=cea +lon_0=%.9f +lat_ts=%.9f +x_0=%.3f +y_0=%.3f ",
           poSRS->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0),
           poSRS->GetProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0),
           poSRS->GetProjParm(SRS_PP_FALSE_EASTING,0.0),
           poSRS->GetProjParm(SRS_PP_FALSE_NORTHING,0.0));
}
...

五、坐标转换

OGRCoordinateTransformation类可以用来在不同的坐标系统中进行坐标转换。可以使用函数OGRCreateCoordinateTransformation()创建一个新的坐标转换对象,然后使用OGRCoordinateTransformation::Transform()方法来进行坐标转换。

OGRSpatialReference oSourceSRS,oTargetSRS;
OGRCoordinateTransformation *poCT;
double                 x, y;
   
oSourceSRS.importFromEPSG(atoi(papszArgv[i+1]) );
oTargetSRS.importFromEPSG(atoi(papszArgv[i+2]) );
   
poCT = OGRCreateCoordinateTransformation(&oSourceSRS,
                                         &oTargetSRS );
x = atof( papszArgv[i+3] );
y = atof( papszArgv[i+4] );
   
if( poCT == NULL || !poCT->Transform( 1, &x,&y ) )
    printf("Transformation failed.\n" );
else
    printf("(%f,%f) -> (%f,%f)\n",
            atof(papszArgv[i+3] ),
            atof(papszArgv[i+4] ),
            x, y );

一对点转换失败的原因有,首先,OGRCreateCoordinateTransformation()函数执行失败,通常的原因是不知道指定的两个投影之间的转换关系。这个可能是试用了PROJ.4库不支持的投影,不同的椭球体之间的转换关系没有定义,或者是其中的一个坐标系统没有定义完全。如果函数OGRCreateCoordinateTransformation() 执行失败,那么其返回值将是NULL。

OGRCoordinateTransformation::Transform() 方法本身页可能执行失败。这个可能的原因也有上面的问题,或者是转换的点里面有一个以上没有定义的数字。函数Transform()执行成功是返回TRUE,如果有一个点转换失败都会返回FALSE。

坐标转换也可以支持三维点的坐标转换,会自动根据不同的椭球地的基准面调整高程值。这个可以用来在不同的垂直基准面进行坐标转换。如果没有Z值,系统会认为所有的点都是在水准面上。

下面的例子演示了如果从一个投影坐标系统中的点转换为该投影中的地理坐标系统中的点,将米表示的坐标转换为经纬度表示的坐标。

OGRSpatialReference   oUTM, *poLatLong;
OGRCoordinateTransformation *poTransform;
oUTM.SetProjCS("UTM 17 /WGS84");
oUTM.SetWellKnownGeogCS("WGS84" );
oUTM.SetUTM( 17 );
poLatLong = oUTM.CloneGeogCS();
 
poTransform = OGRCreateCoordinateTransformation( &oUTM,poLatLong );
if( poTransform == NULL )
{
    ...
}
 
...
if( !poTransform->Transform( nPoints, x,y, z ) )
...

六、其他语言接口

OGR的空间参考提供了一个C语言的接口,定义在ogr_srs_api.h文件中,Python的接口定义在osr.py文件中。所有的接口名称和C++的接口都很相似,但是C和Python中有些方法没有进行提供。

1、C语言接口

typedef void *OGRSpatialReferenceH;                              
typedef void *OGRCoordinateTransformationH;
OGRSpatialReferenceH OSRNewSpatialReference( const char *);
void   OSRDestroySpatialReference( OGRSpatialReferenceH );
int    OSRReference( OGRSpatialReferenceH );
int    OSRDereference( OGRSpatialReferenceH );
OGRErr OSRImportFromEPSG( OGRSpatialReferenceH, int );
OGRErr OSRImportFromWkt( OGRSpatialReferenceH, char ** );
OGRErr OSRExportToWkt( OGRSpatialReferenceH, char ** );
OGRErr OSRSetAttrValue( OGRSpatialReferenceH hSRS, const char * pszNodePath,
                         const char *pszNewNodeValue );
const char *OSRGetAttrValue( OGRSpatialReferenceH hSRS,
                             const char * pszName, intiChild);
OGRErr OSRSetLinearUnits( OGRSpatialReferenceH, const char *, double );
double OSRGetLinearUnits( OGRSpatialReferenceH, char ** );
int    OSRIsGeographic( OGRSpatialReferenceH );
int    OSRIsProjected( OGRSpatialReferenceH );
int    OSRIsSameGeogCS( OGRSpatialReferenceH, OGRSpatialReferenceH );
int    OSRIsSame( OGRSpatialReferenceH, OGRSpatialReferenceH );
OGRErr OSRSetProjCS( OGRSpatialReferenceH hSRS, const char * pszName );
OGRErr OSRSetWellKnownGeogCS( OGRSpatialReferenceH hSRS,
                               const char * pszName );
OGRErr OSRSetGeogCS( OGRSpatialReferenceH hSRS,
                      const char * pszGeogName,
                      const char *pszDatumName,
                      const char *pszEllipsoidName,
                      double dfSemiMajor,double dfInvFlattening,
                      const char * pszPMName ,
                     double dfPMOffset ,
                      const char * pszUnits,
                      double dfConvertToRadians);
double OSRGetSemiMajor( OGRSpatialReferenceH, OGRErr * );
double OSRGetSemiMinor( OGRSpatialReferenceH, OGRErr * );
double OSRGetInvFlattening( OGRSpatialReferenceH, OGRErr * );
OGRErr OSRSetAuthority( OGRSpatialReferenceH hSRS,
                         const char *pszTargetKey,
                         const char *pszAuthority,
                         int nCode );
OGRErr OSRSetProjParm( OGRSpatialReferenceH, const char *, double );
double OSRGetProjParm( OGRSpatialReferenceH hSRS,
                        const char *pszParmName,
                        double dfDefault,
                        OGRErr * );
OGRErr OSRSetUTM( OGRSpatialReferenceH hSRS, int nZone, int bNorth );
int    OSRGetUTMZone( OGRSpatialReferenceH hSRS, int *pbNorth );
OGRCoordinateTransformationH
OCTNewCoordinateTransformation( OGRSpatialReferenceH hSourceSRS,
                                OGRSpatialReferenceHhTargetSRS );
void OCTDestroyCoordinateTransformation( OGRCoordinateTransformationH );
int OCTTransform(OGRCoordinateTransformationH hCT,
                  int nCount, double *x, double*y, double *z );

2、Python语言接口

class osr.SpatialReference
   def __init__(self,obj=None):
   def ImportFromWkt( self, wkt ):
   def ExportToWkt(self):
   def ImportFromEPSG(self,code):
   def IsGeographic(self):
   def IsProjected(self):
   def GetAttrValue(self, name, child = 0):
   def SetAttrValue(self, name, value):
   def SetWellKnownGeogCS(self, name):
   def SetProjCS(self, name = "unnamed" ):
   def IsSameGeogCS(self, other):
   def IsSame(self, other):
   def SetLinearUnits(self, units_name, to_meters ):
   def SetUTM(self, zone, is_north = 1):
 
class CoordinateTransformation:
   def __init__(self,source,target):
   def TransformPoint(self, x, y, z = 0):
   def TransformPoints(self, points):

七、内部实现

OGRCoordinateTransformation依赖于PROJ.4库。所以要使用坐标转换的内容,GDAL必须在编译的时候绑定PROJ4才可以用来进行坐标转换。如果要使用GDAL的坐标转换,重投影相关的算法,就必须要有PROJ4库的支持,否则会转换失败。关于PROJ4的编译和GDAL绑定PROJ4的内容,请参考之前的博文。

免责声明:文章转载自《GDAL源码剖析(十一)之OGR投影说明》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇haproxy 非常完整的配置FreeRTOS-Qemu 实现三任务同步通信机制以及API信息下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

安全威胁的分类

  (1)从威胁的来源看可分为内部威胁和外部威胁     造成网络安全的威胁的原因可能是多方面的,有来自外部,也有可能来自企业网络内部。     内部威胁 80%的计算机犯罪都和系统安全遭受损害的内部攻击有密切的关系。内部人员对机构的运作、结构熟悉,导致攻击不易被发觉,内部人员最容易接触敏感信息,危害的往往是机构最核心的数据、资源等。各机构的信息安全保护措...

[二] JavaIO之File详解 以及FileSystem WinNTFileSystem简介

File类 文件和目录路径名的抽象表示形式。 我们知道,对于不同的操作系统,文件路径的描述是不同的 比如 windows平台:用 linux平台:用/   File是Java为了这一概念提供的抽象描述,与系统无关的视图 抽象路径名有两个组件: 1.可选的与系统有关的前缀  字符串   比如盘符,"/" 表示 UNIX 中的根目录,...

CENTOS7静默安装ORACLE11G及数据泵迁移

2021年2月4日江苏淮安特钢 CENTOS7静默安装ORACLE11G及数据泵迁移 作者:查小广(北京红河谷时代信息技术有限公司) 检化验系统LIMS 数据库迁移 Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production lims数据库oracle:192.168.20....

VirtualBox虚拟机安装与上网配置

 一,安装VirtualBox     1.到https://www.virtualbox.org/下载安装包进行安装。 二,安装虚拟机        1.找到系统镜像     2.打开VirtualBox选择新建,创建虚拟硬盘。        3.添加虚拟光驱加载要装系统的镜像。        4.启动虚拟机。        5.完成安装 三,设...

推荐几款优秀的开源博客系统

1.OneBlog 一个简洁美观、功能强大并且自适应的Java博客。 项目地址:https://gitee.com/yadong.zhang/DBlog 2.halo Halo 可能是最好的 Java 博客系统。 项目路径:https://github.com/halo-dev/halo 3.mblog开源免费的博客系统 mblog开源免费的博客系统,...

KVM基本功能管理

 一、KVM基础功能管理 1、查看命令帮助 virsh -h 2、查看 KVM 的配置文件存放目录(CENTOS7.0.xml是虚拟系统实例的配置文件) ls /etc/libvirt/qemu                 //属性配置文件路径 ls /virtual/KVM/                     //虚拟机磁盘镜像文件路径 3、查...