transformation.hpp 22 KB

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