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