// Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2017-2020, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_GEOMETRY_SRS_TRANSFORMATION_HPP #define BOOST_GEOMETRY_SRS_TRANSFORMATION_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace geometry { namespace projections { namespace detail { template inline bool same_object(T1 const& , T2 const& ) { return false; } template inline bool same_object(T const& o1, T const& o2) { return boost::addressof(o1) == boost::addressof(o2); } template < typename PtIn, typename PtOut, bool SameUnits = std::is_same < typename geometry::detail::cs_angular_units::type, typename geometry::detail::cs_angular_units::type >::value > struct transform_geometry_point_coordinates { static inline void apply(PtIn const& in, PtOut & out, bool /*enable_angles*/) { geometry::set<0>(out, geometry::get<0>(in)); geometry::set<1>(out, geometry::get<1>(in)); } }; template struct transform_geometry_point_coordinates { static inline void apply(PtIn const& in, PtOut & out, bool enable_angles) { if (enable_angles) { geometry::set_from_radian<0>(out, geometry::get_as_radian<0>(in)); geometry::set_from_radian<1>(out, geometry::get_as_radian<1>(in)); } else { geometry::set<0>(out, geometry::get<0>(in)); geometry::set<1>(out, geometry::get<1>(in)); } } }; template struct transform_geometry_point { typedef typename geometry::point_type::type point_type; typedef geometry::model::point < typename select_most_precise < typename geometry::coordinate_type::type, CT >::type, geometry::dimension::type::value, typename geometry::coordinate_system::type > type; template static inline void apply(PtIn const& in, PtOut & out, bool enable_angles) { transform_geometry_point_coordinates::apply(in, out, enable_angles); projections::detail::copy_higher_dimensions<2>(in, out); } }; template struct transform_geometry_range_base { struct convert_strategy { convert_strategy(bool enable_angles) : m_enable_angles(enable_angles) {} template void apply(PtIn const& in, PtOut & out) { transform_geometry_point::apply(in, out, m_enable_angles); } bool m_enable_angles; }; template static inline void apply(In const& in, Out & out, bool enable_angles) { // Change the order and/or closure if needed // In - arbitrary geometry // Out - either Geometry or std::vector // So the order and closure of In and Geometry shoudl be compared // std::vector's order is assumed to be the same as of Geometry geometry::detail::conversion::range_to_range < In, Out, geometry::point_order::value != geometry::point_order::value >::apply(in, out, convert_strategy(enable_angles)); } }; template < typename Geometry, typename CT, typename Tag = typename geometry::tag::type > struct transform_geometry {}; template struct transform_geometry : transform_geometry_point {}; template struct transform_geometry { typedef geometry::model::segment < typename transform_geometry_point::type > type; template static inline void apply(In const& in, Out & out, bool enable_angles) { apply<0>(in, out, enable_angles); apply<1>(in, out, enable_angles); } private: template static inline void apply(In const& in, Out & out, bool enable_angles) { geometry::detail::indexed_point_view in_pt(in); geometry::detail::indexed_point_view out_pt(out); transform_geometry_point::apply(in_pt, out_pt, enable_angles); } }; template struct transform_geometry : transform_geometry_range_base { typedef model::multi_point < typename transform_geometry_point::type > type; }; template struct transform_geometry : transform_geometry_range_base { typedef model::linestring < typename transform_geometry_point::type > type; }; template struct transform_geometry : transform_geometry_range_base { typedef model::ring < typename transform_geometry_point::type, geometry::point_order::value == clockwise, geometry::closure::value == closed > type; }; template < typename OutGeometry, typename CT, bool EnableTemporary = ! std::is_same < typename select_most_precise < typename geometry::coordinate_type::type, CT >::type, typename geometry::coordinate_type::type >::value > struct transform_geometry_wrapper { typedef transform_geometry transform; typedef typename transform::type type; template transform_geometry_wrapper(InGeometry const& in, OutGeometry & out, bool input_angles) : m_out(out) { transform::apply(in, m_temp, input_angles); } type & get() { return m_temp; } void finish() { geometry::convert(m_temp, m_out); } // this is always copy 1:1 without changing the order or closure private: type m_temp; OutGeometry & m_out; }; template < typename OutGeometry, typename CT > struct transform_geometry_wrapper { typedef transform_geometry transform; typedef OutGeometry type; transform_geometry_wrapper(OutGeometry const& in, OutGeometry & out, bool input_angles) : m_out(out) { if (! same_object(in, out)) transform::apply(in, out, input_angles); } template transform_geometry_wrapper(InGeometry const& in, OutGeometry & out, bool input_angles) : m_out(out) { transform::apply(in, out, input_angles); } OutGeometry & get() { return m_out; } void finish() {} private: OutGeometry & m_out; }; template struct transform_range { template < typename Proj1, typename Par1, typename Proj2, typename Par2, typename RangeIn, typename RangeOut, typename Grids > static inline bool apply(Proj1 const& proj1, Par1 const& par1, Proj2 const& proj2, Par2 const& par2, RangeIn const& in, RangeOut & out, Grids const& grids1, Grids const& grids2) { // NOTE: this has to be consistent with pj_transform() bool const input_angles = !par1.is_geocent && par1.is_latlong; transform_geometry_wrapper wrapper(in, out, input_angles); bool res = true; try { res = pj_transform(proj1, par1, proj2, par2, wrapper.get(), grids1, grids2); } catch (projection_exception const&) { res = false; } catch(...) { BOOST_RETHROW } wrapper.finish(); return res; } }; template struct transform_multi { template < typename Proj1, typename Par1, typename Proj2, typename Par2, typename MultiIn, typename MultiOut, typename Grids > static inline bool apply(Proj1 const& proj1, Par1 const& par1, Proj2 const& proj2, Par2 const& par2, MultiIn const& in, MultiOut & out, Grids const& grids1, Grids const& grids2) { if (! same_object(in, out)) range::resize(out, boost::size(in)); return apply(proj1, par1, proj2, par2, boost::begin(in), boost::end(in), boost::begin(out), grids1, grids2); } private: template < typename Proj1, typename Par1, typename Proj2, typename Par2, typename InIt, typename OutIt, typename Grids > static inline bool apply(Proj1 const& proj1, Par1 const& par1, Proj2 const& proj2, Par2 const& par2, InIt in_first, InIt in_last, OutIt out_first, Grids const& grids1, Grids const& grids2) { bool res = true; for ( ; in_first != in_last ; ++in_first, ++out_first ) { if ( ! Policy::apply(proj1, par1, proj2, par2, *in_first, *out_first, grids1, grids2) ) { res = false; } } return res; } }; template < typename Geometry, typename CT, typename Tag = typename geometry::tag::type > struct transform : not_implemented {}; template struct transform { template < typename Proj1, typename Par1, typename Proj2, typename Par2, typename PointIn, typename PointOut, typename Grids > static inline bool apply(Proj1 const& proj1, Par1 const& par1, Proj2 const& proj2, Par2 const& par2, PointIn const& in, PointOut & out, Grids const& grids1, Grids const& grids2) { // NOTE: this has to be consistent with pj_transform() bool const input_angles = !par1.is_geocent && par1.is_latlong; transform_geometry_wrapper wrapper(in, out, input_angles); typedef typename transform_geometry_wrapper::type point_type; point_type * ptr = boost::addressof(wrapper.get()); std::pair range = std::make_pair(ptr, ptr + 1); bool res = true; try { res = pj_transform(proj1, par1, proj2, par2, range, grids1, grids2); } catch (projection_exception const&) { res = false; } catch(...) { BOOST_RETHROW } wrapper.finish(); return res; } }; template struct transform : transform_range {}; template struct transform { template < typename Proj1, typename Par1, typename Proj2, typename Par2, typename SegmentIn, typename SegmentOut, typename Grids > static inline bool apply(Proj1 const& proj1, Par1 const& par1, Proj2 const& proj2, Par2 const& par2, SegmentIn const& in, SegmentOut & out, Grids const& grids1, Grids const& grids2) { // NOTE: this has to be consistent with pj_transform() bool const input_angles = !par1.is_geocent && par1.is_latlong; transform_geometry_wrapper wrapper(in, out, input_angles); typedef typename geometry::point_type < typename transform_geometry_wrapper::type >::type point_type; point_type points[2]; geometry::detail::assign_point_from_index<0>(wrapper.get(), points[0]); geometry::detail::assign_point_from_index<1>(wrapper.get(), points[1]); std::pair range = std::make_pair(points, points + 2); bool res = true; try { res = pj_transform(proj1, par1, proj2, par2, range, grids1, grids2); } catch (projection_exception const&) { res = false; } catch(...) { BOOST_RETHROW } geometry::detail::assign_point_to_index<0>(points[0], wrapper.get()); geometry::detail::assign_point_to_index<1>(points[1], wrapper.get()); wrapper.finish(); return res; } }; template struct transform : transform_range {}; template struct transform : transform_multi > {}; template struct transform : transform_range {}; template struct transform { template < typename Proj1, typename Par1, typename Proj2, typename Par2, typename PolygonIn, typename PolygonOut, typename Grids > static inline bool apply(Proj1 const& proj1, Par1 const& par1, Proj2 const& proj2, Par2 const& par2, PolygonIn const& in, PolygonOut & out, Grids const& grids1, Grids const& grids2) { bool r1 = transform_range < CT >::apply(proj1, par1, proj2, par2, geometry::exterior_ring(in), geometry::exterior_ring(out), grids1, grids2); bool r2 = transform_multi < transform_range >::apply(proj1, par1, proj2, par2, geometry::interior_rings(in), geometry::interior_rings(out), grids1, grids2); return r1 && r2; } }; template struct transform : transform_multi < transform < typename boost::range_value::type, CT, polygon_tag > > {}; }} // namespace projections::detail namespace srs { /*! \brief Representation of projection \details Either dynamic or static projection representation \ingroup projection \tparam Proj1 default_dynamic or static projection parameters \tparam Proj2 default_dynamic or static projection parameters \tparam CT calculation type used internally */ template < typename Proj1 = srs::dynamic, typename Proj2 = srs::dynamic, typename CT = double > class transformation { typedef typename projections::detail::promote_to_double::type calc_t; public: // Both static and default constructed transformation() {} // First dynamic, second static and default constructed template < typename Parameters1, std::enable_if_t < std::is_same::value && projections::dynamic_parameters::is_specialized, int > = 0 > explicit transformation(Parameters1 const& parameters1) : m_proj1(parameters1) {} // First static, second static and default constructed explicit transformation(Proj1 const& parameters1) : m_proj1(parameters1) {} // Both dynamic template < typename Parameters1, typename Parameters2, std::enable_if_t < std::is_same::value && std::is_same::value && projections::dynamic_parameters::is_specialized && projections::dynamic_parameters::is_specialized, int > = 0 > transformation(Parameters1 const& parameters1, Parameters2 const& parameters2) : m_proj1(parameters1) , m_proj2(parameters2) {} // First dynamic, second static template < typename Parameters1, std::enable_if_t < std::is_same::value && projections::dynamic_parameters::is_specialized, int > = 0 > transformation(Parameters1 const& parameters1, Proj2 const& parameters2) : m_proj1(parameters1) , m_proj2(parameters2) {} // First static, second dynamic template < typename Parameters2, std::enable_if_t < std::is_same::value && projections::dynamic_parameters::is_specialized, int > = 0 > transformation(Proj1 const& parameters1, Parameters2 const& parameters2) : m_proj1(parameters1) , m_proj2(parameters2) {} // Both static transformation(Proj1 const& parameters1, Proj2 const& parameters2) : m_proj1(parameters1) , m_proj2(parameters2) {} template bool forward(GeometryIn const& in, GeometryOut & out) const { return forward(in, out, transformation_grids()); } template bool inverse(GeometryIn const& in, GeometryOut & out) const { return inverse(in, out, transformation_grids()); } template bool forward(GeometryIn const& in, GeometryOut & out, transformation_grids const& grids) const { BOOST_GEOMETRY_STATIC_ASSERT( (projections::detail::same_tags::value), "Not supported combination of Geometries.", GeometryIn, GeometryOut); return projections::detail::transform < GeometryOut, calc_t >::apply(m_proj1.proj(), m_proj1.proj().params(), m_proj2.proj(), m_proj2.proj().params(), in, out, grids.src_grids, grids.dst_grids); } template bool inverse(GeometryIn const& in, GeometryOut & out, transformation_grids const& grids) const { BOOST_GEOMETRY_STATIC_ASSERT( (projections::detail::same_tags::value), "Not supported combination of Geometries.", GeometryIn, GeometryOut); return projections::detail::transform < GeometryOut, calc_t >::apply(m_proj2.proj(), m_proj2.proj().params(), m_proj1.proj(), m_proj1.proj().params(), in, out, grids.dst_grids, grids.src_grids); } template inline transformation_grids initialize_grids(GridsStorage & grids_storage) const { transformation_grids result(grids_storage); using namespace projections::detail; pj_gridlist_from_nadgrids(m_proj1.proj().params(), result.src_grids); pj_gridlist_from_nadgrids(m_proj2.proj().params(), result.dst_grids); return result; } private: projections::proj_wrapper m_proj1; projections::proj_wrapper m_proj2; }; } // namespace srs }} // namespace boost::geometry #endif // BOOST_GEOMETRY_SRS_TRANSFORMATION_HPP