transformation.hpp 22 KB

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