]>
Commit | Line | Data |
---|---|---|
92f5a8d4 TL |
1 | // Boost.Geometry |
2 | // This file is manually converted from PROJ4 | |
3 | ||
4 | // This file was modified by Oracle on 2018, 2019. | |
5 | // Modifications copyright (c) 2018-2019, Oracle and/or its affiliates. | |
6 | // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle | |
7 | ||
8 | // Use, modification and distribution is subject to the Boost Software License, | |
9 | // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at | |
10 | // http://www.boost.org/LICENSE_1_0.txt) | |
11 | ||
12 | // This file is converted from PROJ4, http://trac.osgeo.org/proj | |
13 | // PROJ4 is originally written by Gerald Evenden (then of the USGS) | |
14 | // PROJ4 is maintained by Frank Warmerdam | |
15 | // This file was converted to Geometry Library by Adam Wulkiewicz | |
16 | ||
17 | // Original copyright notice: | |
18 | // Author: Frank Warmerdam, warmerdam@pobox.com | |
19 | ||
20 | // Copyright (c) 2000, Frank Warmerdam | |
21 | ||
22 | // Permission is hereby granted, free of charge, to any person obtaining a | |
23 | // copy of this software and associated documentation files (the "Software"), | |
24 | // to deal in the Software without restriction, including without limitation | |
25 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
26 | // and/or sell copies of the Software, and to permit persons to whom the | |
27 | // Software is furnished to do so, subject to the following conditions: | |
28 | ||
29 | // The above copyright notice and this permission notice shall be included | |
30 | // in all copies or substantial portions of the Software. | |
31 | ||
32 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
33 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
34 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
35 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
36 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
37 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
38 | // DEALINGS IN THE SOFTWARE. | |
39 | ||
40 | #ifndef BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_APPLY_GRIDSHIFT_HPP | |
41 | #define BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_APPLY_GRIDSHIFT_HPP | |
42 | ||
43 | ||
44 | #include <boost/geometry/core/assert.hpp> | |
45 | #include <boost/geometry/core/radian_access.hpp> | |
46 | ||
47 | #include <boost/geometry/srs/projections/impl/adjlon.hpp> | |
48 | #include <boost/geometry/srs/projections/impl/function_overloads.hpp> | |
49 | #include <boost/geometry/srs/projections/impl/pj_gridlist.hpp> | |
50 | ||
51 | #include <boost/geometry/util/range.hpp> | |
52 | ||
53 | ||
54 | namespace boost { namespace geometry { namespace projections | |
55 | { | |
56 | ||
57 | namespace detail | |
58 | { | |
59 | ||
60 | // Originally implemented in nad_intr.c | |
61 | template <typename CalcT> | |
62 | inline void nad_intr(CalcT in_lon, CalcT in_lat, | |
63 | CalcT & out_lon, CalcT & out_lat, | |
64 | pj_ctable const& ct) | |
65 | { | |
66 | pj_ctable::lp_t frct; | |
67 | pj_ctable::ilp_t indx; | |
68 | boost::int32_t in; | |
69 | ||
70 | indx.lam = int_floor(in_lon /= ct.del.lam); | |
71 | indx.phi = int_floor(in_lat /= ct.del.phi); | |
72 | frct.lam = in_lon - indx.lam; | |
73 | frct.phi = in_lat - indx.phi; | |
74 | // TODO: implement differently | |
75 | out_lon = out_lat = HUGE_VAL; | |
76 | if (indx.lam < 0) { | |
77 | if (indx.lam == -1 && frct.lam > 0.99999999999) { | |
78 | ++indx.lam; | |
79 | frct.lam = 0.; | |
80 | } else | |
81 | return; | |
82 | } else if ((in = indx.lam + 1) >= ct.lim.lam) { | |
83 | if (in == ct.lim.lam && frct.lam < 1e-11) { | |
84 | --indx.lam; | |
85 | frct.lam = 1.; | |
86 | } else | |
87 | return; | |
88 | } | |
89 | if (indx.phi < 0) { | |
90 | if (indx.phi == -1 && frct.phi > 0.99999999999) { | |
91 | ++indx.phi; | |
92 | frct.phi = 0.; | |
93 | } else | |
94 | return; | |
95 | } else if ((in = indx.phi + 1) >= ct.lim.phi) { | |
96 | if (in == ct.lim.phi && frct.phi < 1e-11) { | |
97 | --indx.phi; | |
98 | frct.phi = 1.; | |
99 | } else | |
100 | return; | |
101 | } | |
102 | boost::int32_t index = indx.phi * ct.lim.lam + indx.lam; | |
103 | pj_ctable::flp_t const& f00 = ct.cvs[index++]; | |
104 | pj_ctable::flp_t const& f10 = ct.cvs[index]; | |
105 | index += ct.lim.lam; | |
106 | pj_ctable::flp_t const& f11 = ct.cvs[index--]; | |
107 | pj_ctable::flp_t const& f01 = ct.cvs[index]; | |
108 | CalcT m00, m10, m01, m11; | |
109 | m11 = m10 = frct.lam; | |
110 | m00 = m01 = 1. - frct.lam; | |
111 | m11 *= frct.phi; | |
112 | m01 *= frct.phi; | |
113 | frct.phi = 1. - frct.phi; | |
114 | m00 *= frct.phi; | |
115 | m10 *= frct.phi; | |
116 | out_lon = m00 * f00.lam + m10 * f10.lam + | |
117 | m01 * f01.lam + m11 * f11.lam; | |
118 | out_lat = m00 * f00.phi + m10 * f10.phi + | |
119 | m01 * f01.phi + m11 * f11.phi; | |
120 | } | |
121 | ||
122 | // Originally implemented in nad_cvt.c | |
123 | template <bool Inverse, typename CalcT> | |
124 | inline void nad_cvt(CalcT const& in_lon, CalcT const& in_lat, | |
125 | CalcT & out_lon, CalcT & out_lat, | |
126 | pj_gi const& gi) | |
127 | { | |
128 | static const int max_iterations = 10; | |
129 | static const CalcT tol = 1e-12; | |
130 | static const CalcT toltol = tol * tol; | |
131 | static const CalcT pi = math::pi<CalcT>(); | |
132 | ||
133 | // horizontal grid expected | |
134 | BOOST_GEOMETRY_ASSERT_MSG(gi.format != pj_gi::gtx, | |
135 | "Vertical grid cannot be used in horizontal shift."); | |
136 | ||
137 | pj_ctable const& ct = gi.ct; | |
138 | ||
139 | // TODO: implement differently | |
140 | if (in_lon == HUGE_VAL) | |
141 | { | |
142 | out_lon = HUGE_VAL; | |
143 | out_lat = HUGE_VAL; | |
144 | return; | |
145 | } | |
146 | ||
147 | // normalize input to ll origin | |
148 | pj_ctable::lp_t tb; | |
149 | tb.lam = in_lon - ct.ll.lam; | |
150 | tb.phi = in_lat - ct.ll.phi; | |
151 | tb.lam = adjlon (tb.lam - pi) + pi; | |
152 | ||
153 | pj_ctable::lp_t t; | |
154 | nad_intr(tb.lam, tb.phi, t.lam, t.phi, ct); | |
155 | if (t.lam == HUGE_VAL) | |
156 | { | |
157 | out_lon = HUGE_VAL; | |
158 | out_lat = HUGE_VAL; | |
159 | return; | |
160 | } | |
161 | ||
162 | if (! Inverse) | |
163 | { | |
164 | out_lon = in_lon - t.lam; | |
165 | out_lat = in_lat - t.phi; | |
166 | return; | |
167 | } | |
168 | ||
169 | t.lam = tb.lam + t.lam; | |
170 | t.phi = tb.phi - t.phi; | |
171 | ||
172 | int i = max_iterations; | |
173 | pj_ctable::lp_t del, dif; | |
174 | do | |
175 | { | |
176 | nad_intr(t.lam, t.phi, del.lam, del.phi, ct); | |
177 | ||
178 | // This case used to return failure, but I have | |
179 | // changed it to return the first order approximation | |
180 | // of the inverse shift. This avoids cases where the | |
181 | // grid shift *into* this grid came from another grid. | |
182 | // While we aren't returning optimally correct results | |
183 | // I feel a close result in this case is better than | |
184 | // no result. NFW | |
185 | // To demonstrate use -112.5839956 49.4914451 against | |
186 | // the NTv2 grid shift file from Canada. | |
187 | if (del.lam == HUGE_VAL) | |
188 | { | |
189 | // Inverse grid shift iteration failed, presumably at grid edge. Using first approximation. | |
190 | break; | |
191 | } | |
192 | ||
193 | dif.lam = t.lam - del.lam - tb.lam; | |
194 | dif.phi = t.phi + del.phi - tb.phi; | |
195 | t.lam -= dif.lam; | |
196 | t.phi -= dif.phi; | |
197 | ||
198 | } | |
199 | while (--i && (dif.lam*dif.lam + dif.phi*dif.phi > toltol)); // prob. slightly faster than hypot() | |
200 | ||
201 | if (i==0) | |
202 | { | |
203 | // Inverse grid shift iterator failed to converge. | |
204 | out_lon = HUGE_VAL; | |
205 | out_lat = HUGE_VAL; | |
206 | return; | |
207 | } | |
208 | ||
209 | out_lon = adjlon (t.lam + ct.ll.lam); | |
210 | out_lat = t.phi + ct.ll.phi; | |
211 | } | |
212 | ||
213 | ||
214 | /************************************************************************/ | |
215 | /* find_grid() */ | |
216 | /* */ | |
217 | /* Determine which grid is the correct given an input coordinate. */ | |
218 | /************************************************************************/ | |
219 | ||
220 | // Originally find_ctable() | |
221 | // here divided into grid_disjoint(), find_grid() and load_grid() | |
222 | ||
223 | template <typename T> | |
224 | inline bool grid_disjoint(T const& lam, T const& phi, | |
225 | pj_ctable const& ct) | |
226 | { | |
227 | double epsilon = (fabs(ct.del.phi)+fabs(ct.del.lam))/10000.0; | |
228 | return ct.ll.phi - epsilon > phi | |
229 | || ct.ll.lam - epsilon > lam | |
230 | || (ct.ll.phi + (ct.lim.phi-1) * ct.del.phi + epsilon < phi) | |
231 | || (ct.ll.lam + (ct.lim.lam-1) * ct.del.lam + epsilon < lam); | |
232 | } | |
233 | ||
234 | template <typename T> | |
235 | inline pj_gi * find_grid(T const& lam, | |
236 | T const& phi, | |
237 | std::vector<pj_gi>::iterator first, | |
238 | std::vector<pj_gi>::iterator last) | |
239 | { | |
240 | pj_gi * gip = NULL; | |
241 | ||
242 | for( ; first != last ; ++first ) | |
243 | { | |
244 | // skip tables that don't match our point at all. | |
245 | if (! grid_disjoint(lam, phi, first->ct)) | |
246 | { | |
247 | // skip vertical grids | |
248 | if (first->format != pj_gi::gtx) | |
249 | { | |
250 | gip = boost::addressof(*first); | |
251 | break; | |
252 | } | |
253 | } | |
254 | } | |
255 | ||
256 | // If we didn't find a child then nothing more to do | |
257 | if( gip == NULL ) | |
258 | return gip; | |
259 | ||
260 | // Otherwise use the child, first checking it's children | |
261 | pj_gi * child = find_grid(lam, phi, first->children.begin(), first->children.end()); | |
262 | if (child != NULL) | |
263 | gip = child; | |
264 | ||
265 | return gip; | |
266 | } | |
267 | ||
268 | template <typename T> | |
269 | inline pj_gi * find_grid(T const& lam, | |
270 | T const& phi, | |
271 | pj_gridinfo & grids, | |
272 | std::vector<std::size_t> const& gridindexes) | |
273 | { | |
274 | pj_gi * gip = NULL; | |
275 | ||
276 | // keep trying till we find a table that works | |
277 | for (std::size_t i = 0 ; i < gridindexes.size() ; ++i) | |
278 | { | |
279 | pj_gi & gi = grids[gridindexes[i]]; | |
280 | ||
281 | // skip tables that don't match our point at all. | |
282 | if (! grid_disjoint(lam, phi, gi.ct)) | |
283 | { | |
284 | // skip vertical grids | |
285 | if (gi.format != pj_gi::gtx) | |
286 | { | |
287 | gip = boost::addressof(gi); | |
288 | break; | |
289 | } | |
290 | } | |
291 | } | |
292 | ||
293 | if (gip == NULL) | |
294 | return gip; | |
295 | ||
296 | // If we have child nodes, check to see if any of them apply. | |
297 | pj_gi * child = find_grid(lam, phi, gip->children.begin(), gip->children.end()); | |
298 | if (child != NULL) | |
299 | gip = child; | |
300 | ||
301 | // if we get this far we have found a suitable grid | |
302 | return gip; | |
303 | } | |
304 | ||
305 | ||
306 | template <typename StreamPolicy> | |
307 | inline bool load_grid(StreamPolicy const& stream_policy, pj_gi_load & gi) | |
308 | { | |
309 | // load the grid shift info if we don't have it. | |
310 | if (gi.ct.cvs.empty()) | |
311 | { | |
312 | typename StreamPolicy::stream_type is; | |
313 | stream_policy.open(is, gi.gridname); | |
314 | ||
315 | if (! pj_gridinfo_load(is, gi)) | |
316 | { | |
317 | //pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); | |
318 | return false; | |
319 | } | |
320 | } | |
321 | ||
322 | return true; | |
323 | } | |
324 | ||
325 | ||
326 | /************************************************************************/ | |
327 | /* pj_apply_gridshift_3() */ | |
328 | /* */ | |
329 | /* This is the real workhorse, given a gridlist. */ | |
330 | /************************************************************************/ | |
331 | ||
332 | // Generic stream policy and standard grids | |
333 | template <bool Inverse, typename CalcT, typename StreamPolicy, typename Range, typename Grids> | |
334 | inline bool pj_apply_gridshift_3(StreamPolicy const& stream_policy, | |
335 | Range & range, | |
336 | Grids & grids, | |
337 | std::vector<std::size_t> const& gridindexes, | |
338 | grids_tag) | |
339 | { | |
340 | typedef typename boost::range_size<Range>::type size_type; | |
341 | ||
342 | // If the grids are empty the indexes are as well | |
343 | if (gridindexes.empty()) | |
344 | { | |
345 | //pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID); | |
346 | //return PJD_ERR_FAILED_TO_LOAD_GRID; | |
347 | return false; | |
348 | } | |
349 | ||
350 | size_type point_count = boost::size(range); | |
351 | ||
352 | for (size_type i = 0 ; i < point_count ; ++i) | |
353 | { | |
354 | typename boost::range_reference<Range>::type | |
355 | point = range::at(range, i); | |
356 | ||
357 | CalcT in_lon = geometry::get_as_radian<0>(point); | |
358 | CalcT in_lat = geometry::get_as_radian<1>(point); | |
359 | ||
360 | pj_gi * gip = find_grid(in_lon, in_lat, grids.gridinfo, gridindexes); | |
361 | ||
362 | if ( gip != NULL ) | |
363 | { | |
364 | // load the grid shift info if we don't have it. | |
365 | if (! gip->ct.cvs.empty() || load_grid(stream_policy, *gip)) | |
366 | { | |
367 | // TODO: use set_invalid_point() or similar mechanism | |
368 | CalcT out_lon = HUGE_VAL; | |
369 | CalcT out_lat = HUGE_VAL; | |
370 | ||
371 | nad_cvt<Inverse>(in_lon, in_lat, out_lon, out_lat, *gip); | |
372 | ||
373 | // TODO: check differently | |
374 | if ( out_lon != HUGE_VAL ) | |
375 | { | |
376 | geometry::set_from_radian<0>(point, out_lon); | |
377 | geometry::set_from_radian<1>(point, out_lat); | |
378 | } | |
379 | } | |
380 | } | |
381 | } | |
382 | ||
383 | return true; | |
384 | } | |
385 | ||
386 | // Generic stream policy and shared grids | |
387 | template <bool Inverse, typename CalcT, typename StreamPolicy, typename Range, typename SharedGrids> | |
388 | inline bool pj_apply_gridshift_3(StreamPolicy const& stream_policy, | |
389 | Range & range, | |
390 | SharedGrids & grids, | |
391 | std::vector<std::size_t> const& gridindexes, | |
392 | shared_grids_tag) | |
393 | { | |
394 | typedef typename boost::range_size<Range>::type size_type; | |
395 | ||
396 | // If the grids are empty the indexes are as well | |
397 | if (gridindexes.empty()) | |
398 | { | |
399 | //pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID); | |
400 | //return PJD_ERR_FAILED_TO_LOAD_GRID; | |
401 | return false; | |
402 | } | |
403 | ||
404 | size_type point_count = boost::size(range); | |
405 | ||
406 | // local storage | |
407 | pj_gi_load local_gi; | |
408 | ||
409 | for (size_type i = 0 ; i < point_count ; ) | |
410 | { | |
411 | bool load_needed = false; | |
412 | ||
413 | CalcT in_lon = 0; | |
414 | CalcT in_lat = 0; | |
415 | ||
416 | { | |
417 | typename SharedGrids::read_locked lck_grids(grids); | |
418 | ||
419 | for ( ; i < point_count ; ++i ) | |
420 | { | |
421 | typename boost::range_reference<Range>::type | |
422 | point = range::at(range, i); | |
423 | ||
424 | in_lon = geometry::get_as_radian<0>(point); | |
425 | in_lat = geometry::get_as_radian<1>(point); | |
426 | ||
427 | pj_gi * gip = find_grid(in_lon, in_lat, lck_grids.gridinfo, gridindexes); | |
428 | ||
429 | if (gip == NULL) | |
430 | { | |
431 | // do nothing | |
432 | } | |
433 | else if (! gip->ct.cvs.empty()) | |
434 | { | |
435 | // TODO: use set_invalid_point() or similar mechanism | |
436 | CalcT out_lon = HUGE_VAL; | |
437 | CalcT out_lat = HUGE_VAL; | |
438 | ||
439 | nad_cvt<Inverse>(in_lon, in_lat, out_lon, out_lat, *gip); | |
440 | ||
441 | // TODO: check differently | |
442 | if (out_lon != HUGE_VAL) | |
443 | { | |
444 | geometry::set_from_radian<0>(point, out_lon); | |
445 | geometry::set_from_radian<1>(point, out_lat); | |
446 | } | |
447 | } | |
448 | else | |
449 | { | |
450 | // loading is needed | |
451 | local_gi = *gip; | |
452 | load_needed = true; | |
453 | break; | |
454 | } | |
455 | } | |
456 | } | |
457 | ||
458 | if (load_needed) | |
459 | { | |
460 | if (load_grid(stream_policy, local_gi)) | |
461 | { | |
462 | typename SharedGrids::write_locked lck_grids(grids); | |
463 | ||
464 | // check again in case other thread already loaded the grid. | |
465 | pj_gi * gip = find_grid(in_lon, in_lat, lck_grids.gridinfo, gridindexes); | |
466 | ||
467 | if (gip != NULL && gip->ct.cvs.empty()) | |
468 | { | |
469 | // swap loaded local storage with empty grid | |
470 | local_gi.swap(*gip); | |
471 | } | |
472 | } | |
473 | else | |
474 | { | |
475 | ++i; | |
476 | } | |
477 | } | |
478 | } | |
479 | ||
480 | return true; | |
481 | } | |
482 | ||
483 | ||
484 | /************************************************************************/ | |
485 | /* pj_apply_gridshift_2() */ | |
486 | /* */ | |
487 | /* This implementation uses the gridlist from a coordinate */ | |
488 | /* system definition. If the gridlist has not yet been */ | |
489 | /* populated in the coordinate system definition we set it up */ | |
490 | /* now. */ | |
491 | /************************************************************************/ | |
492 | ||
493 | template <bool Inverse, typename Par, typename Range, typename ProjGrids> | |
494 | inline bool pj_apply_gridshift_2(Par const& /*defn*/, Range & range, ProjGrids const& proj_grids) | |
495 | { | |
496 | /*if( defn->catalog_name != NULL ) | |
497 | return pj_gc_apply_gridshift( defn, inverse, point_count, point_offset, | |
498 | x, y, z );*/ | |
499 | ||
500 | /*std::vector<std::size_t> gridindexes; | |
501 | pj_gridlist_from_nadgrids(pj_get_param_s(defn.params, "nadgrids"), | |
502 | grids.storage_ptr->stream_policy, | |
503 | grids.storage_ptr->grids, | |
504 | gridindexes);*/ | |
505 | ||
506 | // At this point the grids should be initialized | |
507 | if (proj_grids.hindexes.empty()) | |
508 | return false; | |
509 | ||
510 | return pj_apply_gridshift_3 | |
511 | < | |
512 | Inverse, typename Par::type | |
513 | >(proj_grids.grids_storage().stream_policy, | |
514 | range, | |
515 | proj_grids.grids_storage().hgrids, | |
516 | proj_grids.hindexes, | |
517 | typename ProjGrids::grids_storage_type::grids_type::tag()); | |
518 | } | |
519 | ||
520 | template <bool Inverse, typename Par, typename Range> | |
521 | inline bool pj_apply_gridshift_2(Par const& , Range & , srs::detail::empty_projection_grids const& ) | |
522 | { | |
523 | return false; | |
524 | } | |
525 | ||
526 | ||
527 | } // namespace detail | |
528 | ||
529 | }}} // namespace boost::geometry::projections | |
530 | ||
531 | #endif // BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_APPLY_GRIDSHIFT_HPP |