transformation.hpp 22 KB

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