版本4到6 API迁移¶
这是一个过渡指南,供希望将代码迁移到PROJ版本6的开发人员使用。
代码示例¶
这里用几个例子来说明新旧API之间的区别。下面我们用两个不同的API实现相同的程序。程序从命令行读取输入的经度和纬度,并用墨卡托投影将它们转换为投影坐标。
我们首先为项目4编写程序:
#include <proj_api.h>
main(int argc, char **argv) {
projPJ pj_merc, pj_longlat;
double x, y;
int p;
if (!(pj_longlat = pj_init_plus("+proj=longlat +ellps=clrk66")) )
return 1;
if (!(pj_merc = pj_init_plus("+proj=merc +datum=clrk66 +lat_ts=33")) )
return 1;
while (scanf("%lf %lf", &x, &y) == 2) {
x *= DEG_TO_RAD; /* longitude */
y *= DEG_TO_RAD; /* latitude */
p = pj_transform(pj_longlat, pj_merc, 1, 1, &x, &y, NULL);
printf("%.2f\t%.2f\n", x, y);
}
pj_free(pj_longlat);
pj_free(pj_merc);
return 0;
}
使用PROJ 6实现的相同程序:
#include <proj.h>
main(int argc, char **argv) {
PJ *P;
PJ_COORD c, c_out;
/* NOTE: the use of PROJ strings to describe CRS is strongly discouraged */
/* in PROJ 6, as PROJ strings are a poor way of describing a CRS, and */
/* more precise its geodetic datum. */
/* Use of codes provided by authorities (such as "EPSG:4326", etc...) */
/* or WKT strings will bring the full power of the "transformation */
/* engine" used by PROJ to determine the best transformation(s) between */
/* two CRS. */
P = proj_create_crs_to_crs(PJ_DEFAULT_CTX,
"+proj=longlat +ellps=clrs66",
"+proj=merc +ellps=clrk66 +lat_ts=33",
NULL);
if (P==0)
return 1;
{
/* For that particular use case, this is not needed. */
/* proj_normalize_for_visualization() ensures that the coordinate */
/* order expected and returned by proj_trans() will be longitude, */
/* latitude for geographic CRS, and easting, northing for projected */
/* CRS. If instead of using PROJ strings as above, "EPSG:XXXX" codes */
/* had been used, this might had been necessary. */
PJ* P_for_GIS = proj_normalize_for_visualization(PJ_DEFAULT_CTX, P);
if( 0 == P_for_GIS ) {
proj_destroy(P);
return 1;
}
proj_destroy(P);
P = P_for_GIS;
}
/* For reliable geographic <--> geocentric conversions, z shall not */
/* be some random value. Also t shall be initialized to HUGE_VAL to */
/* allow for proper selection of time-dependent operations if one of */
/* the CRS is dynamic. */
c.lpzt.z = 0.0;
c.lpzt.t = HUGE_VAL;
while (scanf("%lf %lf", &c.lpzt.lam, &c.lpzt.phi) == 2) {
/* No need to convert to radian */
c_out = proj_trans(P, PJ_FWD, c);
printf("%.2f\t%.2f\n", c_out.xy.x, c_out.xy.y);
}
proj_destroy(P);
return 0;
}
从旧API到新API的函数映射¶
旧API函数 |
新的API函数 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
无直接等价物 ,但可以通过链接完成 |
|
|
|
|
|
无等效物 |
|
无等效物 |
|
|
|
向后不兼容¶
访问 proj_api.h
仍然是可能的,但需要定义 ACCEPT_USE_OF_DEPRECATED_PROJ_API_H
宏命令。
仿效现在已不受欢迎的 +init=epsg:XXXX
PROJ 6中的语法与以前的版本不完全兼容。
尤其是与 pj_transform()
函数,无基准位移项 (towgs84
, nadgrids
, geoidgrids
)将在扩建期间添加 +init=epsg:XXXX
字符串到 +proj=YYYY ....
. 如果你还用 pj_transform()
如果要应用基准偏移,则需要提供一个具有适当 towgs84
, nadgrids
或 geoidgrids
条款至 pj_init()
.
使用 +init=epsg:XXXX
语法与 proj_create()
然后 proj_create_crs_to_crs()
, proj_context_use_proj4_init_rules(ctx, TRUE)
或 PROJ_USE_PROJ4_INIT_RULES=YES
必须事先设置环境变量。在这种情况下,将研究基准位移。然而,它们可能与PROJ 4或PROJ 5不同,因为将使用“后期绑定”方法(即尽可能多地寻找源数据和目标数据之间最直接的转换),而PROJ 4或PROJ 5使用“早期绑定”方法,包括始终执行EPSG:4326/第84页。
下游项目对项目6迁移的反馈¶
版本4到5 API迁移¶
这个PROJ版本是一个让开发者迁移代码的指南。
背景¶
在我们继续之前,需要一点背景知识。新API的世界观与旧API不同,因为它是获得高精度转换所必需的。旧API的构造方式是,两个坐标参考系之间的任何转换 must 通过定义不清的WGS84参考框架,将其用作中心。新的API消除了PROJ中转换的限制。仍然可以进行这种类型的转换,但在许多情况下,会有更好的替代方法。
如果您只关心米级精度,那么旧API所代表的世界视图就足够了—在许多情况下,它将提供比这更高的精度。但是“WGS84是 true 世界的基础,以及其他一切都可以从WGS84向本来面目转变,这是内在的缺陷。
首先也是最重要的是,每当提到WGS84时,你都应该问自己“我们在这里讨论的是六个WGS84实现中的哪一个?”.
第二,因为对于许多(特别是遗留的)系统来说,转换为WGS84可能并不简单(或者实际上是ITRF某物、ETRS某物或NAD某物,这在日常项目相关工作中似乎是术语WGS84的实际意义),而厘米级的精确转换可能存在于成对的旧系统之间。
hub参考框架(“datum”)的概念本身并不坏,但在许多情况下,您需要比旧API允许的更谨慎地处理和选择该基准。新API的主要目的就是允许这样做。要做到这一点,你必须意识到,世界本来就是四维的。在许多情况下,您可能会假设一个或多个坐标为常量,但基本上,要获得大地测量精度转换,您需要在4维中工作。
现在,在介绍了引入新API的背景之后,让我们来演示如何使用它。首先请注意,为了从系统A转到系统B,旧的API从执行 逆 从系统A到WGS84的转换,然后执行 向前地 从WGS84到系统B的转换。
使用 cs2cs 作为旧API的命令行界面,以及 cct 对于新事物来说,这两种世界观中做同样事情的例子应该会让我们了解其中的不同之处:
$ echo 300000 6100000 | cs2cs +proj=utm +zone=33 +ellps=GRS80 +to +proj=utm +zone=32 +ellps=GRS80
683687.87 6099299.66 0.00
$ echo 300000 6100000 0 0 | cct +proj=pipeline +step +inv +proj=utm +zone=33 +ellps=GRS80 +step +proj=utm +zone=32 +ellps=GRS80
683687.8667 6099299.6624 0.0000 0.0000
当心那辆车 +inv
一开始 +step
,表示逆变换。
代码示例¶
这里用几个例子来说明新旧API之间的区别。下面我们用两个不同的API实现相同的程序。程序从命令行读取输入的经度和纬度,并用墨卡托投影将它们转换为投影坐标。
我们从为项目4编写程序开始:
#include <proj_api.h>
main(int argc, char **argv) {
projPJ pj_merc, pj_longlat;
double x, y;
if (!(pj_longlat = pj_init_plus("+proj=longlat +ellps=clrk66")) )
return 1;
if (!(pj_merc = pj_init_plus("+proj=merc +ellps=clrk66 +lat_ts=33")) )
return 1;
while (scanf("%lf %lf", &x, &y) == 2) {
x *= DEG_TO_RAD; /* longitude */
y *= DEG_TO_RAD; /* latitude */
p = pj_transform(pj_longlat, pj_merc, 1, 1, &x, &y, NULL );
printf("%.2f\t%.2f\n", x, y);
}
pj_free(pj_longlat);
pj_free(pj_merc);
return 0;
}
使用PROJ v.5实施的相同程序:
#include <proj.h>
main(int argc, char **argv) {
PJ *P;
PJ_COORD c;
P = proj_create(PJ_DEFAULT_CTX, "+proj=merc +ellps=clrk66 +lat_ts=33");
if (P==0)
return 1;
while (scanf("%lf %lf", &c.lp.lam, &c.lp.phi) == 2) {
c.lp.lam = proj_torad(c.lp.lam);
c.lp.phi = proj_torad(c.lp.phi);
c = proj_trans(P, PJ_FWD, c);
printf("%.2f\t%.2f\n", c.xy.x, c.xy.y);
}
proj_destroy(P);
}
看看这两个不同的程序,有几个直接的区别,吸引眼球。首先,所包含的描述API的头文件已经从 proj_api.h
简单地说 proj.h
. 中的所有函数 proj.h
属于 proj_
命名空间。
新的API还带来了新的数据类型。E、 g.转换对象 projPJ
已更改为类型的指针 PJ
. 这样做是为了突出显示对象的实际性质,而不是将其隐藏在typedef后面。还引入了处理坐标的新数据类型。在上面的例子中,我们使用 PJ_COORD
,它是各种类型的联合体。这样做的好处是可以使用union中的各种结构来传递程序中不同点的数据所处的状态。例如,在上面的示例中,从STDIN读取坐标作为大地坐标,通过使用 c.lp
结构。在它被投影之后,我们通过访问 c.xy
以说明坐标现在在投影空间中。数据类型的前缀是 PJ_ .
最后,也许是最大的变化是PROJ中转换的基本概念现在在单个转换对象中处理 (PJ
)而不是像以前那样说明源系统和目标系统。当然,仍然可以这样做,但是transformation对象现在在一个窗口中捕获从源到目标的整个转换。在使用旧API的示例中,源系统描述为 +proj=latlon +ellps=clrk66
和目的地一样 +proj=merc +ellps=clrk66 +lat_ts=33
. 由于墨卡托投影接受大地坐标作为其输入,因此在这种情况下对震源的描述是多余的。我们在新的API中利用了这一点,并简单地声明了目的地。这看起来很简单,但实际上是一个很大的概念变化。我们现在关注的是两个系统之间的路径,而不是源系统和目标系统是什么。
从旧API到新API的函数映射¶
旧API函数 |
新的API函数 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
无等效物 |
|
无等效物 |
|
无等效物 |
|
无等效物 |
|
无等效物 |
|
|
|