正在加载...
正在搜索...
无匹配项
transverse_mercator.cuh
转到此文件的文档。
1/*
2 * 版权所有 (c) 2023, NVIDIA CORPORATION.
3 *
4 * 根据 Apache 许可证 2.0 版(“许可证”)获得许可;
5 * 除非符合许可证的规定,否则您不得使用此文件。
6 * 您可以在以下网址获得许可证副本:
7 *
8 * https://apache.ac.cn/licenses/LICENSE-2.0
9 *
10 * 除非适用法律要求或书面同意,否则软件
11 * 根据许可证分发,按“原样”提供,
12 * 不提供任何明示或默示的担保或条件。
13 * 请参阅许可证,了解管理权限和
14 * 限制的具体语言。
15 */
16
17// 此文件中的代码源自 [OSGeo/PROJ 项目](https://github.com/OSGeo/PROJ)
18// 并已修改为使用 CUDA 在 GPU 上运行。
19//
20// PROJ 许可证来自 https://github.com/OSGeo/PROJ/blob/9.2/COPYING:
21// 但请注意,此文件源自的文件没有版权声明。
22/*
23 版权信息可在源代码文件中找到。
24
25 --------------
26
27 特此授予任何获得此软件及其相关文档文件(“软件”)副本的人,
28 免费处理此软件的许可,不受任何限制,包括但不限于
29 使用、复制、修改、合并、发布、分发、再许可、
30 和/或销售软件副本的权利,并允许接受者按以下条件
31 使用本软件:
32 上述版权声明和本许可声明应包含在
33
34 软件的所有副本或实质部分中。
35 本软件按“原样”提供,不提供任何明示
36
37 或默示的任何形式的保证,包括但不限于适销性、
38 特定用途适用性和非侵权性的保证。在任何情况下,
39 作者或版权持有人均不对因软件或
40 使用或其他交易引起的任何索赔、损害或其他
41 责任,无论是合同行为、侵权行为或其他原因引起的,承担任何责任。
42
43 DEALINGS IN THE SOFTWARE.
44*/
45
46// 以下文本取自 PROJ 文件
47// [tmerc.cpp](https://github.com/OSGeo/PROJ/blob/9.2/src/projections/tmerc.cpp)
48// 有关原始 PROJ 许可证文本,请参阅 cuspatial 仓库根目录中的文件 LICENSE_PROJ
49// license text.
50/*****************************************************************************/
51//
52// 精确横轴墨卡托函数
53//
54//
55// 此文件中的代码主要基于以下程序:
56//
57// 作者:Knud Poder 和 Karsten Engsager
58//
59// 基于以下数学理论:R.Koenig 和 K.H. Weise,“Mathematische
60// Grundlagen der hoeheren Geodaesie und Kartographie,
61// Springer-Verlag, Berlin/Goettingen” 海德堡,1951年。
62//
63// 经丹麦哥本哈根 Kort og Matrikelstyrelsen (KMS) 参考网络部门许可在此修改和使用
64//
65//
66/*****************************************************************************/
67
68#pragma once
69
70#include <cuproj/detail/utility/cuda.hpp>
71#include <cuproj/detail/wrap_to_pi.hpp>
72#include <cuproj/ellipsoid.hpp>
75
76#include <thrust/iterator/transform_iterator.h>
77
78#include <assert.h>
79
80namespace cuproj {
81
86
87namespace detail {
88
100template <typename T>
101inline static CUPROJ_HOST_DEVICE T gatg(T const* p1, int len_p1, T B, T cos_2B, T sin_2B)
102{
103 T h = 0, h1, h2 = 0;
104
105 T const two_cos_2B = 2 * cos_2B;
106 T const* p = p1 + len_p1;
107 h1 = *--p;
108 while (p - p1) {
109 h = -h2 + two_cos_2B * h1 + *--p;
110 h2 = h1;
111 h1 = h;
112 }
113 return (B + h * sin_2B);
114}
115
132template <typename T>
133inline static CUPROJ_HOST_DEVICE T clenshaw_complex(
134 T const* a, int size, T sin_arg_r, T cos_arg_r, T sinh_arg_i, T cosh_arg_i, T* R, T* I)
135{
136 T r, i, hr, hr1, hr2, hi, hi1, hi2;
137
138 // 参数
139 T const* p = a + size;
140 r = 2 * cos_arg_r * cosh_arg_i;
141 i = -2 * sin_arg_r * sinh_arg_i;
142
143 // 求和循环
144 hi1 = hr1 = hi = 0;
145 hr = *--p;
146 for (; a - p;) {
147 hr2 = hr1;
148 hi2 = hi1;
149 hr1 = hr;
150 hi1 = hi;
151 hr = -hr2 + r * hr1 - i * hi1 + *--p;
152 hi = -hi2 + i * hr1 + r * hi1;
153 }
154
155 r = sin_arg_r * cosh_arg_i;
156 i = cos_arg_r * sinh_arg_i;
157 *R = r * hr - i * hi;
158 *I = r * hi + i * hr;
159 return *R;
160}
161
173template <typename T>
174static CUPROJ_HOST_DEVICE T clenshaw_real(T const* a, int size, T arg_r)
175{
176 T r, hr, hr1, hr2, cos_arg_r;
177
178 T const* p = a + size;
179 cos_arg_r = cos(arg_r);
180 r = 2 * cos_arg_r;
181
182 // 求和循环
183 hr1 = 0;
184 hr = *--p;
185 for (; a - p;) {
186 hr2 = hr1;
187 hr1 = hr;
188 hr = -hr2 + r * hr1 + *--p;
189 }
190 return sin(arg_r) * hr;
191}
192} // namespace detail
193
194template <typename Coordinate, typename T = typename Coordinate::value_type>
195class transverse_mercator : operation<Coordinate> {
196 public
197 static constexpr int ETMERC_ORDER = 6;
198
204 CUPROJ_HOST_DEVICE transverse_mercator(projection_parameters<T> const& params) : params_(params)
205 {
206 }
207
215 CUPROJ_HOST_DEVICE Coordinate operator()(Coordinate const& coord, direction dir) const
216 {
217 if (dir == direction::FORWARD)
218 return forward(coord);
219 else
220 return inverse(coord);
221 }
222
223 static constexpr T utm_central_meridian = T{500000}; // UTM 区域的假东值中心
224 // UTM 区域的假北值中心
225 static constexpr T utm_central_parallel(hemisphere h)
226 {
227 return (h == hemisphere::NORTH) ? T{0} : T{10000000};
228 }
229
237 {
238 params_ = input_params;
239
240 // 这样我们就不必在每个地方限定类名。
241 auto& params = params_;
242 auto& tmerc_params = params_.tmerc_params_;
243 auto& ellipsoid = params_.ellipsoid_;
244
245 assert(ellipsoid.es > 0);
246
247 params.x0 = utm_central_meridian;
248 params.y0 = utm_central_parallel(params.utm_hemisphere_);
249
250 if (params.utm_zone_ > 0 && params.utm_zone_ <= 60) {
251 --params.utm_zone_;
252 } else {
253 params.utm_zone_ = lround((floor((detail::wrap_to_pi(params.lam0_) + M_PI) * 30. / M_PI)));
254 params.utm_zone_ = std::clamp(params.utm_zone_, 0, 59);
255 }
256
257 params.lam0_ = (params.utm_zone_ + .5) * M_PI / 30. - M_PI;
258 params.k0 = T{0.9996};
259 params.phi0 = T{0};
260
261 // 第三扁率
262 T const n = ellipsoid.n;
263 T np = n;
264
265 // 三角级数系数 GEO <-> GAUSS
266 // cgb := 高斯坐标 -> 大地坐标, KW p190 - 191 (61) - (62)
267 // cbg := 大地坐标 -> 高斯坐标, KW p186 - 187 (51) - (52)
268 // ETMERC_ORDER = 6阶:Engsager 和 Poder:ICC2007
269
270 tmerc_params.cgb[0] =
271 n *
272 (2 + n * (-2 / 3.0 + n * (-2 + n * (116 / 45.0 + n * (26 / 45.0 + n * (-2854 / 675.0))))));
273 tmerc_params.cbg[0] =
274 n * (-2 + n * (2 / 3.0 +
275 n * (4 / 3.0 + n * (-82 / 45.0 + n * (32 / 45.0 + n * (4642 / 4725.0))))));
276 np *= n;
277 tmerc_params.cgb[1] =
278 np * (7 / 3.0 + n * (-8 / 5.0 + n * (-227 / 45.0 + n * (2704 / 315.0 + n * (2323 / 945.0)))));
279 tmerc_params.cbg[1] =
280 np * (5 / 3.0 + n * (-16 / 15.0 + n * (-13 / 9.0 + n * (904 / 315.0 + n * (-1522 / 945.0)))));
281 np *= n;
282 // n^5 系数从 1262/105 修正为 -1262/105
283 tmerc_params.cgb[2] =
284 np * (56 / 15.0 + n * (-136 / 35.0 + n * (-1262 / 105.0 + n * (73814 / 2835.0))));
285 tmerc_params.cbg[2] =
286 np * (-26 / 15.0 + n * (34 / 21.0 + n * (8 / 5.0 + n * (-12686 / 2835.0))));
287 np *= n;
288 // n^5 系数从 322/35 修正为 332/35
289 tmerc_params.cgb[3] = np * (4279 / 630.0 + n * (-332 / 35.0 + n * (-399572 / 14175.0)));
290 tmerc_params.cbg[3] = np * (1237 / 630.0 + n * (-12 / 5.0 + n * (-24832 / 14175.0)));
291 np *= n;
292 tmerc_params.cgb[4] = np * (4174 / 315.0 + n * (-144838 / 6237.0));
293 tmerc_params.cbg[4] = np * (-734 / 315.0 + n * (109598 / 31185.0));
294 np *= n;
295 tmerc_params.cgb[5] = np * (601676 / 22275.0);
296 tmerc_params.cbg[5] = np * (444337 / 155925.0);
297
298 // 投影常数
299 // 横轴墨卡托 (UTM, ITM 等)
300 np = n * n;
301 // 标准子午线象限, K&W p.50 (96), p.19 (38b), p.5 (2)
302 tmerc_params.Qn = params.k0 / (1 + n) * (1 + np * (1 / 4.0 + np * (1 / 64.0 + np / 256.0)));
303 // 三角级数系数
304 // utg := 椭球北、东 -> 球北、东, KW p194 (65)
305 // gtu := 球北、东 -> 椭球北、东, KW p196 (69)
306 tmerc_params.utg[0] =
307 n * (-0.5 +
308 n * (2 / 3.0 +
309 n * (-37 / 96.0 + n * (1 / 360.0 + n * (81 / 512.0 + n * (-96199 / 604800.0))))));
310 tmerc_params.gtu[0] =
311 n * (0.5 + n * (-2 / 3.0 + n * (5 / 16.0 + n * (41 / 180.0 +
312 n * (-127 / 288.0 + n * (7891 / 37800.0))))));
313 tmerc_params.utg[1] =
314 np * (-1 / 48.0 +
315 n * (-1 / 15.0 + n * (437 / 1440.0 + n * (-46 / 105.0 + n * (1118711 / 3870720.0)))));
316 tmerc_params.gtu[1] =
317 np * (13 / 48.0 +
318 n * (-3 / 5.0 + n * (557 / 1440.0 + n * (281 / 630.0 + n * (-1983433 / 1935360.0)))));
319 np *= n;
320 tmerc_params.utg[2] =
321 np * (-17 / 480.0 + n * (37 / 840.0 + n * (209 / 4480.0 + n * (-5569 / 90720.0))));
322 tmerc_params.gtu[2] =
323 np * (61 / 240.0 + n * (-103 / 140.0 + n * (15061 / 26880.0 + n * (167603 / 181440.0))));
324 np *= n;
325 tmerc_params.utg[3] = np * (-4397 / 161280.0 + n * (11 / 504.0 + n * (830251 / 7257600.0)));
326 tmerc_params.gtu[3] = np * (49561 / 161280.0 + n * (-179 / 168.0 + n * (6601661 / 7257600.0)));
327 np *= n;
328 tmerc_params.utg[4] = np * (-4583 / 161280.0 + n * (108847 / 3991680.0));
329 tmerc_params.gtu[4] = np * (34729 / 80640.0 + n * (-3418889 / 1995840.0));
330 np *= n;
331 tmerc_params.utg[5] = np * (-20648693 / 638668800.0);
332 tmerc_params.gtu[5] = np * (212378941 / 319334400.0);
333
334 // 原点纬度的高斯纬度值
335 T const Z = detail::gatg(
336 tmerc_params.cbg, ETMERC_ORDER, params.phi0, cos(2 * params.phi0), sin(2 * params.phi0));
337
338 // 原点北值减去原点纬度的真实北值
339 // 即 真实北值 = N - P->Zb
340 tmerc_params.Zb =
341 -tmerc_params.Qn * (Z + detail::clenshaw_real(tmerc_params.gtu, ETMERC_ORDER, 2 * Z));
342
343 return params;
344 }
345
346 private
353 CUPROJ_HOST_DEVICE Coordinate forward(Coordinate const& coord) const
354 {
355 // 这样我们就不必在每个地方限定类名。
356 auto& tmerc_params = this->params_.tmerc_params_;
357 auto& ellipsoid = this->params_.ellipsoid_;
358
359 // 椭球纬度、经度 -> 高斯纬度、经度
360 T Cn =
361 detail::gatg(tmerc_params.cbg, ETMERC_ORDER, coord.y, cos(2 * coord.y), sin(2 * coord.y));
362
363 // 高斯纬度、经度 -> 复球纬度
364 T const sin_Cn = sin(Cn);
365 T const cos_Cn = cos(Cn);
366 T const sin_Ce = sin(coord.x);
367 T const cos_Ce = cos(coord.x);
368
369 T const cos_Cn_cos_Ce = cos_Cn * cos_Ce;
370 Cn = atan2(sin_Cn, cos_Cn_cos_Ce);
371
372 T const inv_denom_tan_Ce = 1. / hypot(sin_Cn, cos_Cn_cos_Ce);
373 T const tan_Ce = sin_Ce * cos_Cn * inv_denom_tan_Ce;
374
375 // 上述变体:未发现有可测量的加速
376 // T const sin_Ce_cos_Cn = sin_Ce*cos_Cn;
377 // T const denom = sqrt(1 - sin_Ce_cos_Cn * sin_Ce_cos_Cn);
378 // T const tan_Ce = sin_Ce_cos_Cn / denom;
379
380 // 复球北、东 -> 椭球标准北、东
381 T Ce = asinh(tan_Ce); /* 替换为:Ce = log(tan(FORTPI + Ce*0.5)); */
382
383 // 未优化版本:
384 // T const sin_arg_r = sin(2*Cn);
385 // T const cos_arg_r = cos(2*Cn);
386 //
387 // 已知:
388 // sin(2 * Cn) = 2 sin(Cn) cos(Cn)
389 // sin(atan(y)) = y / sqrt(1 + y^2)
390 // cos(atan(y)) = 1 / sqrt(1 + y^2)
391 // ==> sin(2 * Cn) = 2 tan_Cn / (1 + tan_Cn^2)
392 //
393 // cos(2 * Cn) = 2cos^2(Cn) - 1
394 // = 2 / (1 + tan_Cn^2) - 1
395 //
396 T const two_inv_denom_tan_Ce = 2 * inv_denom_tan_Ce;
397 T const two_inv_denom_tan_Ce_square = two_inv_denom_tan_Ce * inv_denom_tan_Ce;
398 T const tmp_r = cos_Cn_cos_Ce * two_inv_denom_tan_Ce_square;
399 T const sin_arg_r = sin_Cn * tmp_r;
400 T const cos_arg_r = cos_Cn_cos_Ce * tmp_r - 1;
401
402 // 未优化版本:
403 // T const sinh_arg_i = sinh(2*Ce);
404 // T const cosh_arg_i = cosh(2*Ce);
405 //
406 // 已知
407 // sinh(2 * Ce) = 2 sinh(Ce) cosh(Ce)
408 // sinh(asinh(y)) = y
409 // cosh(asinh(y)) = sqrt(1 + y^2)
410 // ==> sinh(2 * Ce) = 2 tan_Ce sqrt(1 + tan_Ce^2)
411 //
412 // cosh(2 * Ce) = 2cosh^2(Ce) - 1
413 // = 2 * (1 + tan_Ce^2) - 1
414 //
415 // 且 1+tan_Ce^2 = 1 + sin_Ce^2 * cos_Cn^2 / (sin_Cn^2 + cos_Cn^2 *
416 // cos_Ce^2) = (sin_Cn^2 + cos_Cn^2 * cos_Ce^2 + sin_Ce^2 * cos_Cn^2) /
417 // (sin_Cn^2 + cos_Cn^2 * cos_Ce^2) = 1. / (sin_Cn^2 + cos_Cn^2 * cos_Ce^2)
418 // = inv_denom_tan_Ce^2
419
420 T const sinh_arg_i = tan_Ce * two_inv_denom_tan_Ce;
421 T const cosh_arg_i = two_inv_denom_tan_Ce_square - 1;
422
423 T dCn, dCe;
424 Cn += detail::clenshaw_complex(
425 tmerc_params.gtu, ETMERC_ORDER, sin_arg_r, cos_arg_r, sinh_arg_i, cosh_arg_i, &dCn, &dCe);
426
427 Ce += dCe;
428
429 CUPROJ_HOST_DEVICE_EXPECTS(fabs(Ce) <= 2.623395162778, // 值来自 PROJ
430 "坐标变换超出投影域");
431 Coordinate xy{0.0, 0.0};
432 xy.y = tmerc_params.Qn * Cn + tmerc_params.Zb; // 北值
433 xy.x = tmerc_params.Qn * Ce; // 东值
434
435 return xy;
436 }
437
443 CUPROJ_HOST_DEVICE Coordinate inverse(Coordinate const& coord) const
444 {
445 // 这样我们就不必在每个地方限定类名。
446 auto& tmerc_params = this->params_.tmerc_params_;
447 auto& ellipsoid = this->params_.ellipsoid_;
448
449 // 标准化北值、东值
450 T Cn = (coord.y - tmerc_params.Zb) / tmerc_params.Qn;
451 T Ce = coord.x / tmerc_params.Qn;
452
453 CUPROJ_HOST_DEVICE_EXPECTS(fabs(Ce) <= 2.623395162778, // 值来自 PROJ
454 "坐标变换超出投影域");
455
456 // 标准化北值、东值 -> 复球纬度、经度
457 T const sin_arg_r = sin(2 * Cn);
458 T const cos_arg_r = cos(2 * Cn);
459
460 // T const sinh_arg_i = sinh(2*Ce);
461 // T const cosh_arg_i = cosh(2*Ce);
462 T const exp_2_Ce = exp(2 * Ce);
463 T const half_inv_exp_2_Ce = T{0.5} / exp_2_Ce;
464 T const sinh_arg_i = T{0.5} * exp_2_Ce - half_inv_exp_2_Ce;
465 T const cosh_arg_i = T{0.5} * exp_2_Ce + half_inv_exp_2_Ce;
466
467 T dCn_ignored, dCe;
468 Cn += detail::clenshaw_complex(tmerc_params.utg,
470 sin_arg_r,
471 cos_arg_r,
472 sinh_arg_i,
473 cosh_arg_i,
474 &dCn_ignored,
475 &dCe);
476 Ce += dCe;
477
478 // 复球纬度 -> 高斯纬度、经度
479 T const sin_Cn = sin(Cn);
480 T const cos_Cn = cos(Cn);
481
482#if 0
483 // 未优化版本:
484 T sin_Ce, cos_Ce;
485 Ce = atan (sinh (Ce)); // 替换为:Ce = 2*(atan(exp(Ce)) - FORTPI);
486 sin_Ce = sin (Ce);
487 cos_Ce = cos (Ce);
488 Ce = atan2 (sin_Ce, cos_Ce*cos_Cn);
489 Cn = atan2 (sin_Cn*cos_Ce, hypot (sin_Ce, cos_Ce*cos_Cn));
490#else
491 // 可以将 Ce = atan2(...) 的两个参数都除以 cos_Ce,
492 // 得到:Ce = atan2 (tan_Ce, cos_Cn) = atan2(sinh(Ce), cos_Cn)
493 //
494 // Cn = atan2(...) 也类似
495 // Cn = atan2 (sin_Cn, hypot (sin_Ce, cos_Ce*cos_Cn)/cos_Ce)
496 // = atan2 (sin_Cn, hypot (sin_Ce/cos_Ce, cos_Cn))
497 // = atan2 (sin_Cn, hypot (tan_Ce, cos_Cn))
498 // = atan2 (sin_Cn, hypot (sinhCe, cos_Cn))
499 T const sinhCe = sinh(Ce);
500 Ce = atan2(sinhCe, cos_Cn);
501 T const modulus_Ce = hypot(sinhCe, cos_Cn);
502 Cn = atan2(sin_Cn, modulus_Ce);
503#endif
504
505 // 高斯纬度、经度 -> 椭球纬度、经度
506
507 // cos(2*Cn) 和 sin(2*Cn) 计算的优化
508 T const tmp = 2 * modulus_Ce / (sinhCe * sinhCe + 1);
509 T const sin_2_Cn = sin_Cn * tmp;
510 T const cos_2_Cn = tmp * modulus_Ce - 1.;
511 // T const cos_2_Cn = cos(2 * Cn);
512 // T const sin_2_Cn = sin(2 * Cn);
513
514 return Coordinate{Ce, detail::gatg(tmerc_params.cgb, ETMERC_ORDER, Cn, cos_2_Cn, sin_2_Cn)};
515 }
516
522 T lam0() const { return this->params_.lam0_; }
523
524 private
525 projection_parameters<T> params_{};
526};
527
531
532} // namespace cuproj
所有变换操作的基类。
CUPROJ_HOST_DEVICE Coordinate operator()(Coordinate const &coord, direction dir) const
对单个坐标执行 UTM 投影。
CUPROJ_HOST_DEVICE transverse_mercator(projection_parameters< T > const ¶ms)
构造一个横轴墨卡托投影
projection_parameters< T > setup(projection_parameters< T > const &input_params)
为横轴墨卡托投影设置投影参数。
static constexpr int ETMERC_ORDER
6阶级数展开
#define CUPROJ_HOST_DEVICE_EXPECTS(cond, reason)
用于检查(前置)条件并在条件不满足时抛出异常的宏。
direction
枚举变换操作的方向。
hemisphere
投影的半球标识符。
椭球参数。