// Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2017, 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 #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 = geometry::is_radian < typename geometry::coordinate_system::type >::type::value == geometry::is_radian < typename geometry::coordinate_system::type >::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 = ! boost::is_same < typename select_most_precise < typename geometry::coordinate_type::type, CT >::type, typename geometry::coordinate_type::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 > static inline bool apply(Proj1 const& proj1, Par1 const& par1, Proj2 const& proj2, Par2 const& par2, RangeIn const& in, RangeOut & out) { // 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()); } catch (projection_exception) { 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 > static inline bool apply(Proj1 const& proj1, Par1 const& par1, Proj2 const& proj2, Par2 const& par2, MultiIn const& in, MultiOut & out) { 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)); } private: template < typename Proj1, typename Par1, typename Proj2, typename Par2, typename InIt, typename OutIt > 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) { bool res = true; for ( ; in_first != in_last ; ++in_first, ++out_first ) { if ( ! Policy::apply(proj1, par1, proj2, par2, *in_first, *out_first) ) { 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 > static inline bool apply(Proj1 const& proj1, Par1 const& par1, Proj2 const& proj2, Par2 const& par2, PointIn const& in, PointOut & out) { // 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); } catch (projection_exception) { 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 > static inline bool apply(Proj1 const& proj1, Par1 const& par1, Proj2 const& proj2, Par2 const& par2, SegmentIn const& in, SegmentOut & out) { // 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); } catch (projection_exception) { 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 > static inline bool apply(Proj1 const& proj1, Par1 const& par1, Proj2 const& proj2, Par2 const& par2, PolygonIn const& in, PolygonOut & out) { bool r1 = transform_range < CT >::apply(proj1, par1, proj2, par2, geometry::exterior_ring(in), geometry::exterior_ring(out)); bool r2 = transform_multi < transform_range >::apply(proj1, par1, proj2, par2, geometry::interior_rings(in), geometry::interior_rings(out)); 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: transformation() {} template explicit transformation(Parameters1 const& parameters1) : m_proj1(parameters1) {} template transformation(Parameters1 const& parameters1, Parameters2 const& parameters2) : m_proj1(parameters1) , m_proj2(parameters2) {} template bool forward(GeometryIn const& in, GeometryOut & out) const { BOOST_MPL_ASSERT_MSG((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); } template bool inverse(GeometryIn const& in, GeometryOut & out) const { BOOST_MPL_ASSERT_MSG((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); } private: projections::proj_wrapper m_proj1; projections::proj_wrapper m_proj2; }; } // namespace srs }} // namespace boost::geometry #endif // BOOST_GEOMETRY_SRS_TRANSFORMATION_HPP