1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 
3 // Copyright (c) 2008-2012 Barend Gehrels, Amsterdam, the Netherlands.
4 
5 // This file was modified by Oracle on 2017-2020.
6 // Modifications copyright (c) 2017-2020, Oracle and/or its affiliates.
7 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
8 
9 // Use, modification and distribution is subject to the Boost Software License,
10 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
11 // http://www.boost.org/LICENSE_1_0.txt)
12 
13 #ifndef BOOST_GEOMETRY_SRS_PROJECTION_HPP
14 #define BOOST_GEOMETRY_SRS_PROJECTION_HPP
15 
16 
17 #include <string>
18 #include <type_traits>
19 
20 #include <boost/smart_ptr/shared_ptr.hpp>
21 #include <boost/throw_exception.hpp>
22 
23 #include <boost/geometry/algorithms/convert.hpp>
24 #include <boost/geometry/algorithms/detail/convert_point_to_point.hpp>
25 
26 #include <boost/geometry/core/coordinate_dimension.hpp>
27 #include <boost/geometry/core/static_assert.hpp>
28 
29 #include <boost/geometry/srs/projections/dpar.hpp>
30 #include <boost/geometry/srs/projections/exception.hpp>
31 #include <boost/geometry/srs/projections/factory.hpp>
32 #include <boost/geometry/srs/projections/impl/base_dynamic.hpp>
33 #include <boost/geometry/srs/projections/impl/base_static.hpp>
34 #include <boost/geometry/srs/projections/impl/pj_init.hpp>
35 #include <boost/geometry/srs/projections/invalid_point.hpp>
36 #include <boost/geometry/srs/projections/proj4.hpp>
37 #include <boost/geometry/srs/projections/spar.hpp>
38 
39 #include <boost/geometry/views/detail/indexed_point_view.hpp>
40 
41 
42 namespace boost { namespace geometry
43 {
44 
45 namespace projections
46 {
47 
48 #ifndef DOXYGEN_NO_DETAIL
49 namespace detail
50 {
51 
52 template <typename G1, typename G2>
53 struct same_tags
54     : std::is_same
55         <
56             typename geometry::tag<G1>::type,
57             typename geometry::tag<G2>::type
58         >
59 {};
60 
61 template <typename CT>
62 struct promote_to_double
63 {
64     typedef std::conditional_t
65         <
66             std::is_integral<CT>::value || std::is_same<CT, float>::value,
67             double, CT
68         > type;
69 };
70 
71 // Copy coordinates of dimensions >= MinDim
72 template <std::size_t MinDim, typename Point1, typename Point2>
copy_higher_dimensions(Point1 const & point1,Point2 & point2)73 inline void copy_higher_dimensions(Point1 const& point1, Point2 & point2)
74 {
75     static const std::size_t dim1 = geometry::dimension<Point1>::value;
76     static const std::size_t dim2 = geometry::dimension<Point2>::value;
77     static const std::size_t lesser_dim = dim1 < dim2 ? dim1 : dim2;
78     BOOST_GEOMETRY_STATIC_ASSERT((lesser_dim >= MinDim),
79         "The dimension of Point1 or Point2 is too small.",
80         Point1, Point2);
81 
82     geometry::detail::conversion::point_to_point
83         <
84             Point1, Point2, MinDim, lesser_dim
85         > ::apply(point1, point2);
86 
87     // TODO: fill point2 with zeros if dim1 < dim2 ?
88     // currently no need because equal dimensions are checked
89 }
90 
91 
92 struct forward_point_projection_policy
93 {
94     template <typename LL, typename XY, typename Proj>
applyboost::geometry::projections::detail::forward_point_projection_policy95     static inline bool apply(LL const& ll, XY & xy, Proj const& proj)
96     {
97         return proj.forward(ll, xy);
98     }
99 };
100 
101 struct inverse_point_projection_policy
102 {
103     template <typename XY, typename LL, typename Proj>
applyboost::geometry::projections::detail::inverse_point_projection_policy104     static inline bool apply(XY const& xy, LL & ll, Proj const& proj)
105     {
106         return proj.inverse(xy, ll);
107     }
108 };
109 
110 template <typename PointPolicy>
111 struct project_point
112 {
113     template <typename P1, typename P2, typename Proj>
applyboost::geometry::projections::detail::project_point114     static inline bool apply(P1 const& p1, P2 & p2, Proj const& proj)
115     {
116         // (Geographic -> Cartesian) will be projected, rest will be copied.
117         // So first copy third or higher dimensions
118         projections::detail::copy_higher_dimensions<2>(p1, p2);
119 
120         if (! PointPolicy::apply(p1, p2, proj))
121         {
122             // For consistency with transformation
123             set_invalid_point(p2);
124             return false;
125         }
126 
127         return true;
128     }
129 };
130 
131 template <typename PointPolicy>
132 struct project_range
133 {
134     template <typename Proj>
135     struct convert_policy
136     {
convert_policyboost::geometry::projections::detail::project_range::convert_policy137         explicit convert_policy(Proj const& proj)
138             : m_proj(proj)
139             , m_result(true)
140         {}
141 
142         template <typename Point1, typename Point2>
applyboost::geometry::projections::detail::project_range::convert_policy143         inline void apply(Point1 const& point1, Point2 & point2)
144         {
145             if (! project_point<PointPolicy>::apply(point1, point2, m_proj) )
146                 m_result = false;
147         }
148 
resultboost::geometry::projections::detail::project_range::convert_policy149         bool result() const
150         {
151             return m_result;
152         }
153 
154     private:
155         Proj const& m_proj;
156         bool m_result;
157     };
158 
159     template <typename R1, typename R2, typename Proj>
applyboost::geometry::projections::detail::project_range160     static inline bool apply(R1 const& r1, R2 & r2, Proj const& proj)
161     {
162         return geometry::detail::conversion::range_to_range
163             <
164                 R1, R2,
165                 geometry::point_order<R1>::value != geometry::point_order<R2>::value
166             >::apply(r1, r2, convert_policy<Proj>(proj)).result();
167     }
168 };
169 
170 template <typename Policy>
171 struct project_multi
172 {
173     template <typename G1, typename G2, typename Proj>
applyboost::geometry::projections::detail::project_multi174     static inline bool apply(G1 const& g1, G2 & g2, Proj const& proj)
175     {
176         range::resize(g2, boost::size(g1));
177         return apply(boost::begin(g1), boost::end(g1),
178                      boost::begin(g2),
179                      proj);
180     }
181 
182 private:
183     template <typename It1, typename It2, typename Proj>
applyboost::geometry::projections::detail::project_multi184     static inline bool apply(It1 g1_first, It1 g1_last, It2 g2_first, Proj const& proj)
185     {
186         bool result = true;
187         for ( ; g1_first != g1_last ; ++g1_first, ++g2_first )
188         {
189             if (! Policy::apply(*g1_first, *g2_first, proj))
190             {
191                 result = false;
192             }
193         }
194         return result;
195     }
196 };
197 
198 template
199 <
200     typename Geometry,
201     typename PointPolicy,
202     typename Tag = typename geometry::tag<Geometry>::type
203 >
204 struct project_geometry
205 {};
206 
207 template <typename Geometry, typename PointPolicy>
208 struct project_geometry<Geometry, PointPolicy, point_tag>
209     : project_point<PointPolicy>
210 {};
211 
212 template <typename Geometry, typename PointPolicy>
213 struct project_geometry<Geometry, PointPolicy, multi_point_tag>
214     : project_range<PointPolicy>
215 {};
216 
217 template <typename Geometry, typename PointPolicy>
218 struct project_geometry<Geometry, PointPolicy, segment_tag>
219 {
220     template <typename G1, typename G2, typename Proj>
applyboost::geometry::projections::detail::project_geometry221     static inline bool apply(G1 const& g1, G2 & g2, Proj const& proj)
222     {
223         bool r1 = apply<0>(g1, g2, proj);
224         bool r2 = apply<1>(g1, g2, proj);
225         return r1 && r2;
226     }
227 
228 private:
229     template <std::size_t Index, typename G1, typename G2, typename Proj>
applyboost::geometry::projections::detail::project_geometry230     static inline bool apply(G1 const& g1, G2 & g2, Proj const& proj)
231     {
232         geometry::detail::indexed_point_view<G1 const, Index> pt1(g1);
233         geometry::detail::indexed_point_view<G2, Index> pt2(g2);
234         return project_point<PointPolicy>::apply(pt1, pt2, proj);
235     }
236 };
237 
238 template <typename Geometry, typename PointPolicy>
239 struct project_geometry<Geometry, PointPolicy, linestring_tag>
240     : project_range<PointPolicy>
241 {};
242 
243 template <typename Geometry, typename PointPolicy>
244 struct project_geometry<Geometry, PointPolicy, multi_linestring_tag>
245     : project_multi< project_range<PointPolicy> >
246 {};
247 
248 template <typename Geometry, typename PointPolicy>
249 struct project_geometry<Geometry, PointPolicy, ring_tag>
250     : project_range<PointPolicy>
251 {};
252 
253 template <typename Geometry, typename PointPolicy>
254 struct project_geometry<Geometry, PointPolicy, polygon_tag>
255 {
256     template <typename G1, typename G2, typename Proj>
applyboost::geometry::projections::detail::project_geometry257     static inline bool apply(G1 const& g1, G2 & g2, Proj const& proj)
258     {
259         bool r1 = project_range
260                     <
261                         PointPolicy
262                     >::apply(geometry::exterior_ring(g1),
263                              geometry::exterior_ring(g2),
264                              proj);
265         bool r2 = project_multi
266                     <
267                         project_range<PointPolicy>
268                     >::apply(geometry::interior_rings(g1),
269                              geometry::interior_rings(g2),
270                              proj);
271         return r1 && r2;
272     }
273 };
274 
275 template <typename MultiPolygon, typename PointPolicy>
276 struct project_geometry<MultiPolygon, PointPolicy, multi_polygon_tag>
277     : project_multi
278         <
279             project_geometry
280             <
281                 typename boost::range_value<MultiPolygon>::type,
282                 PointPolicy,
283                 polygon_tag
284             >
285         >
286 {};
287 
288 
289 } // namespace detail
290 #endif // DOXYGEN_NO_DETAIL
291 
292 
293 template <typename Params>
294 struct dynamic_parameters
295 {
296     static const bool is_specialized = false;
297 };
298 
299 template <>
300 struct dynamic_parameters<srs::proj4>
301 {
302     static const bool is_specialized = true;
applyboost::geometry::projections::dynamic_parameters303     static inline srs::detail::proj4_parameters apply(srs::proj4 const& params)
304     {
305         return srs::detail::proj4_parameters(params.str());
306     }
307 };
308 
309 template <typename T>
310 struct dynamic_parameters<srs::dpar::parameters<T> >
311 {
312     static const bool is_specialized = true;
applyboost::geometry::projections::dynamic_parameters313     static inline srs::dpar::parameters<T> const& apply(srs::dpar::parameters<T> const& params)
314     {
315         return params;
316     }
317 };
318 
319 
320 // proj_wrapper class and its specializations wrapps the internal projection
321 // representation and implements transparent creation of projection object
322 template <typename Proj, typename CT>
323 class proj_wrapper
324 {
325     BOOST_GEOMETRY_STATIC_ASSERT_FALSE(
326         "Unknown projection definition.",
327         Proj);
328 };
329 
330 template <typename CT>
331 class proj_wrapper<srs::dynamic, CT>
332 {
333     // Some projections do not work with float -> wrong results
334     // select <double> from int/float/double and else selects T
335     typedef typename projections::detail::promote_to_double<CT>::type calc_t;
336 
337     typedef projections::parameters<calc_t> parameters_type;
338     typedef projections::detail::dynamic_wrapper_b<calc_t, parameters_type> vprj_t;
339 
340 public:
341     template
342     <
343         typename Params,
344         std::enable_if_t
345             <
346                 dynamic_parameters<Params>::is_specialized,
347                 int
348             > = 0
349     >
proj_wrapper(Params const & params)350     proj_wrapper(Params const& params)
351         : m_ptr(create(dynamic_parameters<Params>::apply(params)))
352     {}
353 
proj() const354     vprj_t const& proj() const { return *m_ptr; }
mutable_proj()355     vprj_t & mutable_proj() { return *m_ptr; }
356 
357 private:
358     template <typename Params>
create(Params const & params)359     static vprj_t* create(Params const& params)
360     {
361         parameters_type parameters = projections::detail::pj_init<calc_t>(params);
362 
363         vprj_t* result = projections::detail::create_new(params, parameters);
364 
365         if (result == NULL)
366         {
367             if (parameters.id.is_unknown())
368             {
369                 BOOST_THROW_EXCEPTION(projection_not_named_exception());
370             }
371             else
372             {
373                 // TODO: handle non-string projection id
374                 BOOST_THROW_EXCEPTION(projection_unknown_id_exception());
375             }
376         }
377 
378         return result;
379     }
380 
381     boost::shared_ptr<vprj_t> m_ptr;
382 };
383 
384 template <typename StaticParameters, typename CT>
385 class static_proj_wrapper_base
386 {
387     typedef typename projections::detail::promote_to_double<CT>::type calc_t;
388 
389     typedef projections::parameters<calc_t> parameters_type;
390 
391     typedef typename srs::spar::detail::pick_proj_tag
392         <
393             StaticParameters
394         >::type proj_tag;
395 
396     typedef typename projections::detail::static_projection_type
397         <
398             proj_tag,
399             typename projections::detail::static_srs_tag<StaticParameters>::type,
400             StaticParameters,
401             calc_t,
402             parameters_type
403         >::type projection_type;
404 
405 public:
proj() const406     projection_type const& proj() const { return m_proj; }
mutable_proj()407     projection_type & mutable_proj() { return m_proj; }
408 
409 protected:
static_proj_wrapper_base(StaticParameters const & s_params)410     explicit static_proj_wrapper_base(StaticParameters const& s_params)
411         : m_proj(s_params,
412                  projections::detail::pj_init<calc_t>(s_params))
413     {}
414 
415 private:
416     projection_type m_proj;
417 };
418 
419 template <typename ...Ps, typename CT>
420 class proj_wrapper<srs::spar::parameters<Ps...>, CT>
421     : public static_proj_wrapper_base<srs::spar::parameters<Ps...>, CT>
422 {
423     typedef srs::spar::parameters<Ps...>
424         static_parameters_type;
425     typedef static_proj_wrapper_base
426         <
427             static_parameters_type,
428             CT
429         > base_t;
430 
431 public:
proj_wrapper()432     proj_wrapper()
433         : base_t(static_parameters_type())
434     {}
435 
proj_wrapper(static_parameters_type const & s_params)436     proj_wrapper(static_parameters_type const& s_params)
437         : base_t(s_params)
438     {}
439 };
440 
441 
442 // projection class implements transparent forward/inverse projection interface
443 template <typename Proj, typename CT>
444 class projection
445     : private proj_wrapper<Proj, CT>
446 {
447     typedef proj_wrapper<Proj, CT> base_t;
448 
449 public:
projection()450     projection()
451     {}
452 
453     template <typename Params>
projection(Params const & params)454     explicit projection(Params const& params)
455         : base_t(params)
456     {}
457 
458     /// Forward projection, from Latitude-Longitude to Cartesian
459     template <typename LL, typename XY>
forward(LL const & ll,XY & xy) const460     inline bool forward(LL const& ll, XY& xy) const
461     {
462         BOOST_GEOMETRY_STATIC_ASSERT(
463             (projections::detail::same_tags<LL, XY>::value),
464             "Not supported combination of Geometries.",
465             LL, XY);
466 
467         concepts::check_concepts_and_equal_dimensions<LL const, XY>();
468 
469         return projections::detail::project_geometry
470                 <
471                     LL,
472                     projections::detail::forward_point_projection_policy
473                 >::apply(ll, xy, base_t::proj());
474     }
475 
476     /// Inverse projection, from Cartesian to Latitude-Longitude
477     template <typename XY, typename LL>
inverse(XY const & xy,LL & ll) const478     inline bool inverse(XY const& xy, LL& ll) const
479     {
480         BOOST_GEOMETRY_STATIC_ASSERT(
481             (projections::detail::same_tags<XY, LL>::value),
482             "Not supported combination of Geometries.",
483             XY, LL);
484 
485         concepts::check_concepts_and_equal_dimensions<XY const, LL>();
486 
487         return projections::detail::project_geometry
488                 <
489                     XY,
490                     projections::detail::inverse_point_projection_policy
491                 >::apply(xy, ll, base_t::proj());
492     }
493 };
494 
495 } // namespace projections
496 
497 
498 namespace srs
499 {
500 
501 
502 /*!
503     \brief Representation of projection
504     \details Either dynamic or static projection representation
505     \ingroup projection
506     \tparam Parameters default dynamic tag or static projection parameters
507     \tparam CT calculation type used internally
508 */
509 template
510 <
511     typename Parameters = srs::dynamic,
512     typename CT = double
513 >
514 class projection
515     : public projections::projection<Parameters, CT>
516 {
517     typedef projections::projection<Parameters, CT> base_t;
518 
519 public:
projection()520     projection()
521     {}
522 
projection(Parameters const & parameters)523     projection(Parameters const& parameters)
524         : base_t(parameters)
525     {}
526 
527     /*!
528     \ingroup projection
529     \brief Initializes a projection as a string, using the format with + and =
530     \details The projection can be initialized with a string (with the same format as the PROJ4 package) for
531       convenient initialization from, for example, the command line
532     \par Example
533         <tt>srs::proj4("+proj=labrd +ellps=intl +lon_0=46d26'13.95E +lat_0=18d54S +azi=18d54 +k_0=.9995 +x_0=400000 +y_0=800000")</tt>
534         for the Madagascar projection.
535     */
536     template
537     <
538         typename DynamicParameters,
539         std::enable_if_t
540             <
541                 projections::dynamic_parameters<DynamicParameters>::is_specialized,
542                 int
543             > = 0
544     >
projection(DynamicParameters const & dynamic_parameters)545     projection(DynamicParameters const& dynamic_parameters)
546         : base_t(dynamic_parameters)
547     {}
548 };
549 
550 
551 } // namespace srs
552 
553 
554 }} // namespace boost::geometry
555 
556 
557 #endif // BOOST_GEOMETRY_SRS_PROJECTION_HPP
558