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