MS RFC 126:端口到PROJ 6 API¶
- 日期
2019-10-01
- 作者
甚至鲁奥
- 联系方式
- 状态
采用
- 最后更新
2019-10-10
- 版本
MapServer 8.0版
概述¶
自2019年3月1日起, PROJ 6.0 已经释放。此版本不支持proj_API.h中提供的“传统”API,而支持proj.h中提供的新API。目前,通过定义ACCEPT_USE_OF_DEPRECATED_proj_API_h宏,传统API仍然可用,但从proj 7.0开始,proj_API.h将不再可用。
因此,这个RFC是关于MapServer中使用proj.h新API所需的更改。当MapServer针对proj 4.x或proj 5.x构建时,对传统的proj_api.h的支持目前仍然保留。
建议的解决方案¶
与PROJ的大部分接口都包含在mapproject.c中,因此大部分更改都在那里完成,projectionObj对象是在代码库中一致使用的抽象,用于对坐标参考系建模。只有一些对pj_is_latlong()的直接调用被一个新的msprojisgographiccrs()函数抽象出来。
然而,在最初实现的测试中,很快就发现需要进行其他更改来保持MapServer的性能。PROJ 6计算坐标操作的新机制非常强大,使用“后期绑定”方法试图找到源坐标系和目标坐标系之间最直接的转换,而不是使用众所周知的+to WGS84=或+nadgrids=PROJ关键字系统地通过WGS84。穿过WGS84枢轴可能会导致不良结果,例如当在两个固定板CRS(如GDA94和GDA2020)之间转换时,两者相差1.8米,但其中每个CRS与WGS84之间的转换是零移位转换。
然而,这种新功能有一个计算代价。而对于PROJ 4.x,pj_transform()可以毫无问题地用于转换单个坐标元组,天真地使用等效的调用PROJ_create_crs_to_crs()+PROJ_trans()将是一个主要的性能杀手,因为PROJ_create_crs_to_crs()在最复杂的情况下可能需要100毫秒的时间。因此,需要缓存由proj_create_crs_to_crs()产生的PJ对象,并在相同对象(源crs,目标crs)之间转换时重用它。
因此,解决方案是通过projectonContext不透明结构拥有一个全局投影上下文池,在PROJ 6的情况下,该结构包含PJ_上下文以及多个(源_crs,目标_crs)对之间PJ*转换对象的缓存。将mapObj结构扩展为存储projectionContext,并从loadMap()中的全局池中获取一个。它将其分配给mapObj.projection成员,并且在MapServer中创建projectionObj的所有调用站点都将被修改为继承投影上下文。当mapObj被释放时,它的投影上下文被返回到全局池,因此它可以被另一个请求重用(通常是FastCGI或MapScript循环)。
避免研究现有的PJ * object for a (source_crs, target_crs) in the cache of the current projection context, which has a price by itself, a variant API for a number of functions like msProjectPoint(), msProjectShape() or msProjectLine() have has been created to directly take a reprojectionObj* 指针而不是(projectionObj 在,projectionObj 出)元组。那次谴责 基本上是PJ的持有人 坐标变换对象。这对于PROJ 4.x也有帮助,因为msProjectPoint()必须对每个坐标转换的CRS的性质进行多次检查,而reprojectonobj可以缓存它们。
综上所述,新的功能是:
projectionContext* msProjectionContextGetFromPool(void);
void msProjectionContextReleaseToPool(projectionContext* ctx);
void msProjectionContextPoolCleanup(void);
reprojectionObj* msProjectCreateReprojector(projectionObj* in, projectionObj* out);
void msProjectDestroyReprojector(reprojectionObj* reprojector);
int msProjectPointEx(reprojectionObj* reprojector, pointObj *point);
int msProjectShapeEx(reprojectionObj* reprojector, shapeObj *shape);
int msProjectLineEx(reprojectionObj* reprojector, lineObj *line);
void msProjectionInheritContextFrom(projectionObj *pDst, projectionObj* pSrc);
void msProjectionSetContext(projectionObj *p, projectionContext* ctx);
持续集成更改¶
PHP 7.3配置被修改为使用PROJ 6 API,而其他目标仍然使用PROJ 4.xapi。为了简化测试套件的维护,所有Travis配置都被修改为使用PROJ 6.1.1。对于不使用PROJ 6 API的配置,只需删除PROJ.h文件,因此MapServer将使用PROJ_API.h。将同一PROJ版本上的所有配置对齐的原因是,与所使用的API无关,PROJ中的更改使坐标重投影的结果略有不同,这影响了许多预期结果(这主要是由于PROJ 6默认使用增强的横轴墨卡托法,而不是以前使用的传统算法)。这样,当使用proj_api.h或proj.h时,预期的结果是相同的(除了在WCS GeoTIFF geokeys中存在微米级差异的单个情况,因为proj_api.h和proj.h使用的地理到地心转换算法略有不同)
Vagrant也被修改为使用PROJ 6.1.1,它创建了两个构建:针对PROJ 4.x API构建Vagrant,针对PROJ 6 API构建Vagrant。
实施细节¶
将修改以下文件:
.travis.yml:更改PHP7.3配置的名称
CMakeLists.txt:考虑删除mapprojhack.c
c:创建一个reprojectionObj对象,并将其与msprojectshapex一起使用
mapchart.c:同上
mapcluster.c:同上
mapcopy.c:msCopyProjectionExtended()中的共享上下文
mapdraw.c:使用reprojectonobj对象和msprojectshapex()。使用msprojisgographiccrs()
mapfile.c:将msInitProjection()、msFreeProjection()和msProcessAutoProjection()移动到mapproject.c。在initMap()和initLayer()中共享上下文
mapgml.c:使用reprojectionObj对象和msprojectshapex()
mapgradicule.c:使用reprojectonobj对象和msProjectShapeEx()。使用msprojisgographiccrs()
mapkmlrenderer.cpp:上下文共享。使用msprojisgographiccrs()
c:使用reprojectionObj对象和msProjectShapeEx()
mapmvt.c:同上
mapobject.c:将投影上下文释放到msFreeMap()中的投影池
mapogcfilter.c:上下文共享
mapogcfilter.h:修改fltdoaxiswappingifnecessage()的原型,以接受用于上下文共享的mapObj*
mapogcfiltercommon.c:上下文共享
mapogcsos.c:使用reprojectionObj对象和msprojectshapex()
mapogr.cpp:上下文共享,当CRS是EPSG代码时,避免通过msOGRSpatialRef2ProjectionObj()中的PROJ字符串执行有损步骤
mapogroutput.c:使用reprojectionObj对象和msprojectshapex()
mapows.c:上下文共享。使用msprojisgographiccrs()
mapproject.c:很多变化!
mapproject.h:新功能和结构
mapquery.c:使用reprojectionObj对象和msprojectshapex()
mapresample.c:一些PROJ 6特定的代码路径,因为有对PROJ API的直接调用
mapserver.h:添加到mapObj的projContext成员,以及存储在layerObj中的2个reprojector对象
mapservutil.c:使用msprojisgographiccrs()
c:使用reprojectionObj对象和msProjectShapeEx()
mapshape.h:在mstiled shplayerinfo中存储一个重新投影程序对象
maptemplate.c:使用reprojectionObj对象和msprojectshapex()
mapunion.c:同上
maputil.c:在msCleanup()中调用msprojectonContextPoolCleanup()
mapwcs.c:上下文共享。使用msprojisgographiccrs()
mapwcs11.c:上下文共享。
mapwcs20.c:上下文共享。使用msprojisgographiccrs()
mapwfs.c:上下文共享
mapwms.c:上下文共享
Vagrantfile:调用proj6.sh脚本
ci/travis/before_install.sh:安装卷边
ci/travis/install.sh:下载并构建项目6。修改PHP 7.3目标以使用它
cmake/FindProj.cmake:检测项目6 API
msautotest/pymod/testlib.py:测试替代预期结果的逻辑
msautotest/wxs/expected/:由于PROJ 6增强的横轴墨卡托而更改了许多文件
scripts/vagrant/mapserver.sh:PROJ 6相关的构建更改
scripts/vagrant/packages.sh:安装curl和sqlite3
将添加以下文件:
scripts/vagrant/proj6.sh:下载并构建proj6
将删除以下文件:
mapprojhack.c:合并到mapproject.c中
向后兼容性问题¶
映射文件语法上没有。由于项目的变化,坐标重投影可能会有轻微的变化。
安全影响¶
没有。
性能影响¶
使用PROJ 6 API比较慢,特别是对于MapServer CGI二进制文件的单次调用。这显示在msautotest的运行时,它包含许多这样的单次调用。Travis CI show上显示的典型运行时,PROJ 4api构建大约9分钟,PROJ 6api构建大约15分钟30秒。对此我们无能为力。
对于涉及使用MapScript的FastCGI或WSGI的用例,其中流程的生命周期较长,投影上下文池策略有助于大大降低成本。
我们使用了以下简单的MapScript python代码和mapfile来测试性能。
def test():
map = mapscript.mapObj('bench.map')
mapscript.msIO_installStdoutToBuffer()
request = mapscript.OWSRequest()
request.loadParamsFromURL('service=WMS&version=1.1.1&request=GetMap&layers=grey&srs=EPSG:4269&bbox=-180,-90,180,90&format=image/png&width=80&height=40')
status = map.OWSDispatch(request)
return mapscript.msIO_getStdoutBufferBytes()
MAP
NAME TEST
STATUS ON
SIZE 80 40
EXTENT -180 -90 180 90
PROJECTION
"init=epsg:4326"
END
OUTPUTFORMAT
NAME png24_t
DRIVER "GDAL/PNG"
IMAGEMODE RGBA
END
WEB
METADATA
"ows_enable_request" "*"
"wms_srs" "EPSG:4326 EPSG:4269"
"ows_http_max_age" "86400"
END
END
LAYER
NAME grey
TYPE raster
STATUS default
DATA ../gdal/data/grey.tif
END
END
这涉及到从WGS84到NAD83的重新投影,使用相同的GDAL 2.4和PROJ master(7.0dev)构建,在test()上循环500次,使用PROJ 4 API每次迭代的运行时间为0.9ms,而使用proj6api的运行时间为1.1ms。
如果在热运行时使用PROJ 4 API进行单次迭代,则运行时为19ms,使用proj6api时为30ms。
这种情况有点极端,因为渲染速度非常快,因此坐标操作设置是不可忽略的。对于请求时间较长的用例,预计时间差会成比例地降低。
MapScript含义¶
没有。
文件需求¶
理论上没有,因为没有语法变化。但是,将在https://mapserver.org/mapfile/projection.html中添加一些单词,建议使用init=epsg:XXXX语法,而不是PROJ 4 CRS字符串,以便从更精确的后期绑定坐标转换中获益。
票证ID和参考号¶
投票历史¶
+来自PSC的成员包括鲁奥、米克尔·史密斯、塞思·吉尔文、丹尼尔·莫里塞特和朱卡·拉科宁。
信用¶
感谢法国国防部的资助。