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