1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2
3 // Copyright (c) 2017-2020, Oracle and/or its affiliates.
4 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
5
6 // Use, modification and distribution is subject to the Boost Software License,
7 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
8 // http://www.boost.org/LICENSE_1_0.txt)
9
10 #ifndef BOOST_GEOMETRY_SRS_TRANSFORMATION_HPP
11 #define BOOST_GEOMETRY_SRS_TRANSFORMATION_HPP
12
13
14 #include <string>
15 #include <type_traits>
16
17 #include <boost/throw_exception.hpp>
18
19 #include <boost/geometry/algorithms/convert.hpp>
20
21 #include <boost/geometry/core/coordinate_dimension.hpp>
22 #include <boost/geometry/core/static_assert.hpp>
23
24 #include <boost/geometry/geometries/point.hpp>
25 #include <boost/geometry/geometries/multi_point.hpp>
26 #include <boost/geometry/geometries/linestring.hpp>
27 #include <boost/geometry/geometries/ring.hpp>
28 #include <boost/geometry/geometries/segment.hpp>
29
30 #include <boost/geometry/srs/projection.hpp>
31 #include <boost/geometry/srs/projections/grids.hpp>
32 #include <boost/geometry/srs/projections/impl/pj_transform.hpp>
33
34 #include <boost/geometry/views/detail/indexed_point_view.hpp>
35
36
37 namespace boost { namespace geometry
38 {
39
40 namespace projections { namespace detail
41 {
42
43 template <typename T1, typename T2>
same_object(T1 const &,T2 const &)44 inline bool same_object(T1 const& , T2 const& )
45 {
46 return false;
47 }
48
49 template <typename T>
same_object(T const & o1,T const & o2)50 inline bool same_object(T const& o1, T const& o2)
51 {
52 return boost::addressof(o1) == boost::addressof(o2);
53 }
54
55 template
56 <
57 typename PtIn,
58 typename PtOut,
59 bool SameUnits = std::is_same
60 <
61 typename geometry::detail::cs_angular_units<PtIn>::type,
62 typename geometry::detail::cs_angular_units<PtOut>::type
63 >::value
64 >
65 struct transform_geometry_point_coordinates
66 {
applyboost::geometry::projections::detail::transform_geometry_point_coordinates67 static inline void apply(PtIn const& in, PtOut & out, bool /*enable_angles*/)
68 {
69 geometry::set<0>(out, geometry::get<0>(in));
70 geometry::set<1>(out, geometry::get<1>(in));
71 }
72 };
73
74 template <typename PtIn, typename PtOut>
75 struct transform_geometry_point_coordinates<PtIn, PtOut, false>
76 {
applyboost::geometry::projections::detail::transform_geometry_point_coordinates77 static inline void apply(PtIn const& in, PtOut & out, bool enable_angles)
78 {
79 if (enable_angles)
80 {
81 geometry::set_from_radian<0>(out, geometry::get_as_radian<0>(in));
82 geometry::set_from_radian<1>(out, geometry::get_as_radian<1>(in));
83 }
84 else
85 {
86 geometry::set<0>(out, geometry::get<0>(in));
87 geometry::set<1>(out, geometry::get<1>(in));
88 }
89 }
90 };
91
92 template <typename Geometry, typename CT>
93 struct transform_geometry_point
94 {
95 typedef typename geometry::point_type<Geometry>::type point_type;
96
97 typedef geometry::model::point
98 <
99 typename select_most_precise
100 <
101 typename geometry::coordinate_type<point_type>::type,
102 CT
103 >::type,
104 geometry::dimension<point_type>::type::value,
105 typename geometry::coordinate_system<point_type>::type
106 > type;
107
108 template <typename PtIn, typename PtOut>
applyboost::geometry::projections::detail::transform_geometry_point109 static inline void apply(PtIn const& in, PtOut & out, bool enable_angles)
110 {
111 transform_geometry_point_coordinates<PtIn, PtOut>::apply(in, out, enable_angles);
112 projections::detail::copy_higher_dimensions<2>(in, out);
113 }
114 };
115
116 template <typename Geometry, typename CT>
117 struct transform_geometry_range_base
118 {
119 struct convert_strategy
120 {
convert_strategyboost::geometry::projections::detail::transform_geometry_range_base::convert_strategy121 convert_strategy(bool enable_angles)
122 : m_enable_angles(enable_angles)
123 {}
124
125 template <typename PtIn, typename PtOut>
applyboost::geometry::projections::detail::transform_geometry_range_base::convert_strategy126 void apply(PtIn const& in, PtOut & out)
127 {
128 transform_geometry_point<Geometry, CT>::apply(in, out, m_enable_angles);
129 }
130
131 bool m_enable_angles;
132 };
133
134 template <typename In, typename Out>
applyboost::geometry::projections::detail::transform_geometry_range_base135 static inline void apply(In const& in, Out & out, bool enable_angles)
136 {
137 // Change the order and/or closure if needed
138 // In - arbitrary geometry
139 // Out - either Geometry or std::vector
140 // So the order and closure of In and Geometry shoudl be compared
141 // std::vector's order is assumed to be the same as of Geometry
142 geometry::detail::conversion::range_to_range
143 <
144 In,
145 Out,
146 geometry::point_order<In>::value != geometry::point_order<Out>::value
147 >::apply(in, out, convert_strategy(enable_angles));
148 }
149 };
150
151 template
152 <
153 typename Geometry,
154 typename CT,
155 typename Tag = typename geometry::tag<Geometry>::type
156 >
157 struct transform_geometry
158 {};
159
160 template <typename Point, typename CT>
161 struct transform_geometry<Point, CT, point_tag>
162 : transform_geometry_point<Point, CT>
163 {};
164
165 template <typename Segment, typename CT>
166 struct transform_geometry<Segment, CT, segment_tag>
167 {
168 typedef geometry::model::segment
169 <
170 typename transform_geometry_point<Segment, CT>::type
171 > type;
172
173 template <typename In, typename Out>
applyboost::geometry::projections::detail::transform_geometry174 static inline void apply(In const& in, Out & out, bool enable_angles)
175 {
176 apply<0>(in, out, enable_angles);
177 apply<1>(in, out, enable_angles);
178 }
179
180 private:
181 template <std::size_t Index, typename In, typename Out>
applyboost::geometry::projections::detail::transform_geometry182 static inline void apply(In const& in, Out & out, bool enable_angles)
183 {
184 geometry::detail::indexed_point_view<In const, Index> in_pt(in);
185 geometry::detail::indexed_point_view<Out, Index> out_pt(out);
186 transform_geometry_point<Segment, CT>::apply(in_pt, out_pt, enable_angles);
187 }
188 };
189
190 template <typename MultiPoint, typename CT>
191 struct transform_geometry<MultiPoint, CT, multi_point_tag>
192 : transform_geometry_range_base<MultiPoint, CT>
193 {
194 typedef model::multi_point
195 <
196 typename transform_geometry_point<MultiPoint, CT>::type
197 > type;
198 };
199
200 template <typename LineString, typename CT>
201 struct transform_geometry<LineString, CT, linestring_tag>
202 : transform_geometry_range_base<LineString, CT>
203 {
204 typedef model::linestring
205 <
206 typename transform_geometry_point<LineString, CT>::type
207 > type;
208 };
209
210 template <typename Ring, typename CT>
211 struct transform_geometry<Ring, CT, ring_tag>
212 : transform_geometry_range_base<Ring, CT>
213 {
214 typedef model::ring
215 <
216 typename transform_geometry_point<Ring, CT>::type,
217 geometry::point_order<Ring>::value == clockwise,
218 geometry::closure<Ring>::value == closed
219 > type;
220 };
221
222
223 template
224 <
225 typename OutGeometry,
226 typename CT,
227 bool EnableTemporary = ! std::is_same
228 <
229 typename select_most_precise
230 <
231 typename geometry::coordinate_type<OutGeometry>::type,
232 CT
233 >::type,
234 typename geometry::coordinate_type<OutGeometry>::type
235 >::value
236 >
237 struct transform_geometry_wrapper
238 {
239 typedef transform_geometry<OutGeometry, CT> transform;
240 typedef typename transform::type type;
241
242 template <typename InGeometry>
transform_geometry_wrapperboost::geometry::projections::detail::transform_geometry_wrapper243 transform_geometry_wrapper(InGeometry const& in, OutGeometry & out, bool input_angles)
244 : m_out(out)
245 {
246 transform::apply(in, m_temp, input_angles);
247 }
248
getboost::geometry::projections::detail::transform_geometry_wrapper249 type & get() { return m_temp; }
finishboost::geometry::projections::detail::transform_geometry_wrapper250 void finish() { geometry::convert(m_temp, m_out); } // this is always copy 1:1 without changing the order or closure
251
252 private:
253 type m_temp;
254 OutGeometry & m_out;
255 };
256
257 template
258 <
259 typename OutGeometry,
260 typename CT
261 >
262 struct transform_geometry_wrapper<OutGeometry, CT, false>
263 {
264 typedef transform_geometry<OutGeometry, CT> transform;
265 typedef OutGeometry type;
266
transform_geometry_wrapperboost::geometry::projections::detail::transform_geometry_wrapper267 transform_geometry_wrapper(OutGeometry const& in, OutGeometry & out, bool input_angles)
268 : m_out(out)
269 {
270 if (! same_object(in, out))
271 transform::apply(in, out, input_angles);
272 }
273
274 template <typename InGeometry>
transform_geometry_wrapperboost::geometry::projections::detail::transform_geometry_wrapper275 transform_geometry_wrapper(InGeometry const& in, OutGeometry & out, bool input_angles)
276 : m_out(out)
277 {
278 transform::apply(in, out, input_angles);
279 }
280
getboost::geometry::projections::detail::transform_geometry_wrapper281 OutGeometry & get() { return m_out; }
finishboost::geometry::projections::detail::transform_geometry_wrapper282 void finish() {}
283
284 private:
285 OutGeometry & m_out;
286 };
287
288 template <typename CT>
289 struct transform_range
290 {
291 template
292 <
293 typename Proj1, typename Par1,
294 typename Proj2, typename Par2,
295 typename RangeIn, typename RangeOut,
296 typename Grids
297 >
applyboost::geometry::projections::detail::transform_range298 static inline bool apply(Proj1 const& proj1, Par1 const& par1,
299 Proj2 const& proj2, Par2 const& par2,
300 RangeIn const& in, RangeOut & out,
301 Grids const& grids1, Grids const& grids2)
302 {
303 // NOTE: this has to be consistent with pj_transform()
304 bool const input_angles = !par1.is_geocent && par1.is_latlong;
305
306 transform_geometry_wrapper<RangeOut, CT> wrapper(in, out, input_angles);
307
308 bool res = true;
309 try
310 {
311 res = pj_transform(proj1, par1, proj2, par2, wrapper.get(), grids1, grids2);
312 }
313 catch (projection_exception const&)
314 {
315 res = false;
316 }
317 catch(...)
318 {
319 BOOST_RETHROW
320 }
321
322 wrapper.finish();
323
324 return res;
325 }
326 };
327
328 template <typename Policy>
329 struct transform_multi
330 {
331 template
332 <
333 typename Proj1, typename Par1,
334 typename Proj2, typename Par2,
335 typename MultiIn, typename MultiOut,
336 typename Grids
337 >
applyboost::geometry::projections::detail::transform_multi338 static inline bool apply(Proj1 const& proj1, Par1 const& par1,
339 Proj2 const& proj2, Par2 const& par2,
340 MultiIn const& in, MultiOut & out,
341 Grids const& grids1, Grids const& grids2)
342 {
343 if (! same_object(in, out))
344 range::resize(out, boost::size(in));
345
346 return apply(proj1, par1, proj2, par2,
347 boost::begin(in), boost::end(in),
348 boost::begin(out),
349 grids1, grids2);
350 }
351
352 private:
353 template
354 <
355 typename Proj1, typename Par1,
356 typename Proj2, typename Par2,
357 typename InIt, typename OutIt,
358 typename Grids
359 >
applyboost::geometry::projections::detail::transform_multi360 static inline bool apply(Proj1 const& proj1, Par1 const& par1,
361 Proj2 const& proj2, Par2 const& par2,
362 InIt in_first, InIt in_last, OutIt out_first,
363 Grids const& grids1, Grids const& grids2)
364 {
365 bool res = true;
366 for ( ; in_first != in_last ; ++in_first, ++out_first )
367 {
368 if ( ! Policy::apply(proj1, par1, proj2, par2, *in_first, *out_first, grids1, grids2) )
369 {
370 res = false;
371 }
372 }
373 return res;
374 }
375 };
376
377 template
378 <
379 typename Geometry,
380 typename CT,
381 typename Tag = typename geometry::tag<Geometry>::type
382 >
383 struct transform
384 : not_implemented<Tag>
385 {};
386
387 template <typename Point, typename CT>
388 struct transform<Point, CT, point_tag>
389 {
390 template
391 <
392 typename Proj1, typename Par1,
393 typename Proj2, typename Par2,
394 typename PointIn, typename PointOut,
395 typename Grids
396 >
applyboost::geometry::projections::detail::transform397 static inline bool apply(Proj1 const& proj1, Par1 const& par1,
398 Proj2 const& proj2, Par2 const& par2,
399 PointIn const& in, PointOut & out,
400 Grids const& grids1, Grids const& grids2)
401 {
402 // NOTE: this has to be consistent with pj_transform()
403 bool const input_angles = !par1.is_geocent && par1.is_latlong;
404
405 transform_geometry_wrapper<PointOut, CT> wrapper(in, out, input_angles);
406
407 typedef typename transform_geometry_wrapper<PointOut, CT>::type point_type;
408 point_type * ptr = boost::addressof(wrapper.get());
409
410 std::pair<point_type *, point_type *> range = std::make_pair(ptr, ptr + 1);
411
412 bool res = true;
413 try
414 {
415 res = pj_transform(proj1, par1, proj2, par2, range, grids1, grids2);
416 }
417 catch (projection_exception const&)
418 {
419 res = false;
420 }
421 catch(...)
422 {
423 BOOST_RETHROW
424 }
425
426 wrapper.finish();
427
428 return res;
429 }
430 };
431
432 template <typename MultiPoint, typename CT>
433 struct transform<MultiPoint, CT, multi_point_tag>
434 : transform_range<CT>
435 {};
436
437 template <typename Segment, typename CT>
438 struct transform<Segment, CT, segment_tag>
439 {
440 template
441 <
442 typename Proj1, typename Par1,
443 typename Proj2, typename Par2,
444 typename SegmentIn, typename SegmentOut,
445 typename Grids
446 >
applyboost::geometry::projections::detail::transform447 static inline bool apply(Proj1 const& proj1, Par1 const& par1,
448 Proj2 const& proj2, Par2 const& par2,
449 SegmentIn const& in, SegmentOut & out,
450 Grids const& grids1, Grids const& grids2)
451 {
452 // NOTE: this has to be consistent with pj_transform()
453 bool const input_angles = !par1.is_geocent && par1.is_latlong;
454
455 transform_geometry_wrapper<SegmentOut, CT> wrapper(in, out, input_angles);
456
457 typedef typename geometry::point_type
458 <
459 typename transform_geometry_wrapper<SegmentOut, CT>::type
460 >::type point_type;
461
462 point_type points[2];
463
464 geometry::detail::assign_point_from_index<0>(wrapper.get(), points[0]);
465 geometry::detail::assign_point_from_index<1>(wrapper.get(), points[1]);
466
467 std::pair<point_type*, point_type*> range = std::make_pair(points, points + 2);
468
469 bool res = true;
470 try
471 {
472 res = pj_transform(proj1, par1, proj2, par2, range, grids1, grids2);
473 }
474 catch (projection_exception const&)
475 {
476 res = false;
477 }
478 catch(...)
479 {
480 BOOST_RETHROW
481 }
482
483 geometry::detail::assign_point_to_index<0>(points[0], wrapper.get());
484 geometry::detail::assign_point_to_index<1>(points[1], wrapper.get());
485
486 wrapper.finish();
487
488 return res;
489 }
490 };
491
492 template <typename Linestring, typename CT>
493 struct transform<Linestring, CT, linestring_tag>
494 : transform_range<CT>
495 {};
496
497 template <typename MultiLinestring, typename CT>
498 struct transform<MultiLinestring, CT, multi_linestring_tag>
499 : transform_multi<transform_range<CT> >
500 {};
501
502 template <typename Ring, typename CT>
503 struct transform<Ring, CT, ring_tag>
504 : transform_range<CT>
505 {};
506
507 template <typename Polygon, typename CT>
508 struct transform<Polygon, CT, polygon_tag>
509 {
510 template
511 <
512 typename Proj1, typename Par1,
513 typename Proj2, typename Par2,
514 typename PolygonIn, typename PolygonOut,
515 typename Grids
516 >
applyboost::geometry::projections::detail::transform517 static inline bool apply(Proj1 const& proj1, Par1 const& par1,
518 Proj2 const& proj2, Par2 const& par2,
519 PolygonIn const& in, PolygonOut & out,
520 Grids const& grids1, Grids const& grids2)
521 {
522 bool r1 = transform_range
523 <
524 CT
525 >::apply(proj1, par1, proj2, par2,
526 geometry::exterior_ring(in),
527 geometry::exterior_ring(out),
528 grids1, grids2);
529 bool r2 = transform_multi
530 <
531 transform_range<CT>
532 >::apply(proj1, par1, proj2, par2,
533 geometry::interior_rings(in),
534 geometry::interior_rings(out),
535 grids1, grids2);
536 return r1 && r2;
537 }
538 };
539
540 template <typename MultiPolygon, typename CT>
541 struct transform<MultiPolygon, CT, multi_polygon_tag>
542 : transform_multi
543 <
544 transform
545 <
546 typename boost::range_value<MultiPolygon>::type,
547 CT,
548 polygon_tag
549 >
550 >
551 {};
552
553
554 }} // namespace projections::detail
555
556 namespace srs
557 {
558
559
560 /*!
561 \brief Representation of projection
562 \details Either dynamic or static projection representation
563 \ingroup projection
564 \tparam Proj1 default_dynamic or static projection parameters
565 \tparam Proj2 default_dynamic or static projection parameters
566 \tparam CT calculation type used internally
567 */
568 template
569 <
570 typename Proj1 = srs::dynamic,
571 typename Proj2 = srs::dynamic,
572 typename CT = double
573 >
574 class transformation
575 {
576 typedef typename projections::detail::promote_to_double<CT>::type calc_t;
577
578 public:
579 // Both static and default constructed
transformation()580 transformation()
581 {}
582
583 // First dynamic, second static and default constructed
584 template
585 <
586 typename Parameters1,
587 std::enable_if_t
588 <
589 std::is_same<Proj1, srs::dynamic>::value
590 && projections::dynamic_parameters<Parameters1>::is_specialized,
591 int
592 > = 0
593 >
transformation(Parameters1 const & parameters1)594 explicit transformation(Parameters1 const& parameters1)
595 : m_proj1(parameters1)
596 {}
597
598 // First static, second static and default constructed
transformation(Proj1 const & parameters1)599 explicit transformation(Proj1 const& parameters1)
600 : m_proj1(parameters1)
601 {}
602
603 // Both dynamic
604 template
605 <
606 typename Parameters1, typename Parameters2,
607 std::enable_if_t
608 <
609 std::is_same<Proj1, srs::dynamic>::value
610 && std::is_same<Proj2, srs::dynamic>::value
611 && projections::dynamic_parameters<Parameters1>::is_specialized
612 && projections::dynamic_parameters<Parameters2>::is_specialized,
613 int
614 > = 0
615 >
transformation(Parameters1 const & parameters1,Parameters2 const & parameters2)616 transformation(Parameters1 const& parameters1,
617 Parameters2 const& parameters2)
618 : m_proj1(parameters1)
619 , m_proj2(parameters2)
620 {}
621
622 // First dynamic, second static
623 template
624 <
625 typename Parameters1,
626 std::enable_if_t
627 <
628 std::is_same<Proj1, srs::dynamic>::value
629 && projections::dynamic_parameters<Parameters1>::is_specialized,
630 int
631 > = 0
632 >
transformation(Parameters1 const & parameters1,Proj2 const & parameters2)633 transformation(Parameters1 const& parameters1,
634 Proj2 const& parameters2)
635 : m_proj1(parameters1)
636 , m_proj2(parameters2)
637 {}
638
639 // First static, second dynamic
640 template
641 <
642 typename Parameters2,
643 std::enable_if_t
644 <
645 std::is_same<Proj2, srs::dynamic>::value
646 && projections::dynamic_parameters<Parameters2>::is_specialized,
647 int
648 > = 0
649 >
transformation(Proj1 const & parameters1,Parameters2 const & parameters2)650 transformation(Proj1 const& parameters1,
651 Parameters2 const& parameters2)
652 : m_proj1(parameters1)
653 , m_proj2(parameters2)
654 {}
655
656 // Both static
transformation(Proj1 const & parameters1,Proj2 const & parameters2)657 transformation(Proj1 const& parameters1,
658 Proj2 const& parameters2)
659 : m_proj1(parameters1)
660 , m_proj2(parameters2)
661 {}
662
663 template <typename GeometryIn, typename GeometryOut>
forward(GeometryIn const & in,GeometryOut & out) const664 bool forward(GeometryIn const& in, GeometryOut & out) const
665 {
666 return forward(in, out, transformation_grids<detail::empty_grids_storage>());
667 }
668
669 template <typename GeometryIn, typename GeometryOut>
inverse(GeometryIn const & in,GeometryOut & out) const670 bool inverse(GeometryIn const& in, GeometryOut & out) const
671 {
672 return inverse(in, out, transformation_grids<detail::empty_grids_storage>());
673 }
674
675 template <typename GeometryIn, typename GeometryOut, typename GridsStorage>
forward(GeometryIn const & in,GeometryOut & out,transformation_grids<GridsStorage> const & grids) const676 bool forward(GeometryIn const& in, GeometryOut & out,
677 transformation_grids<GridsStorage> const& grids) const
678 {
679 BOOST_GEOMETRY_STATIC_ASSERT(
680 (projections::detail::same_tags<GeometryIn, GeometryOut>::value),
681 "Not supported combination of Geometries.",
682 GeometryIn, GeometryOut);
683
684 return projections::detail::transform
685 <
686 GeometryOut,
687 calc_t
688 >::apply(m_proj1.proj(), m_proj1.proj().params(),
689 m_proj2.proj(), m_proj2.proj().params(),
690 in, out,
691 grids.src_grids,
692 grids.dst_grids);
693 }
694
695 template <typename GeometryIn, typename GeometryOut, typename GridsStorage>
inverse(GeometryIn const & in,GeometryOut & out,transformation_grids<GridsStorage> const & grids) const696 bool inverse(GeometryIn const& in, GeometryOut & out,
697 transformation_grids<GridsStorage> const& grids) const
698 {
699 BOOST_GEOMETRY_STATIC_ASSERT(
700 (projections::detail::same_tags<GeometryIn, GeometryOut>::value),
701 "Not supported combination of Geometries.",
702 GeometryIn, GeometryOut);
703
704 return projections::detail::transform
705 <
706 GeometryOut,
707 calc_t
708 >::apply(m_proj2.proj(), m_proj2.proj().params(),
709 m_proj1.proj(), m_proj1.proj().params(),
710 in, out,
711 grids.dst_grids,
712 grids.src_grids);
713 }
714
715 template <typename GridsStorage>
initialize_grids(GridsStorage & grids_storage) const716 inline transformation_grids<GridsStorage> initialize_grids(GridsStorage & grids_storage) const
717 {
718 transformation_grids<GridsStorage> result(grids_storage);
719
720 using namespace projections::detail;
721 pj_gridlist_from_nadgrids(m_proj1.proj().params(),
722 result.src_grids);
723 pj_gridlist_from_nadgrids(m_proj2.proj().params(),
724 result.dst_grids);
725
726 return result;
727 }
728
729 private:
730 projections::proj_wrapper<Proj1, CT> m_proj1;
731 projections::proj_wrapper<Proj2, CT> m_proj2;
732 };
733
734
735 } // namespace srs
736
737
738 }} // namespace boost::geometry
739
740
741 #endif // BOOST_GEOMETRY_SRS_TRANSFORMATION_HPP
742