// Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2008-2012 Barend Gehrels, Amsterdam, the Netherlands. // This file was modified by Oracle on 2017-2020. // Modifications 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_PROJECTION_HPP #define BOOST_GEOMETRY_SRS_PROJECTION_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace geometry { namespace projections { #ifndef DOXYGEN_NO_DETAIL namespace detail { template struct same_tags : std::is_same < typename geometry::tag::type, typename geometry::tag::type > {}; template struct promote_to_double { typedef std::conditional_t < std::is_integral::value || std::is_same::value, double, CT > type; }; // Copy coordinates of dimensions >= MinDim template inline void copy_higher_dimensions(Point1 const& point1, Point2 & point2) { static const std::size_t dim1 = geometry::dimension::value; static const std::size_t dim2 = geometry::dimension::value; static const std::size_t lesser_dim = dim1 < dim2 ? dim1 : dim2; BOOST_GEOMETRY_STATIC_ASSERT((lesser_dim >= MinDim), "The dimension of Point1 or Point2 is too small.", Point1, Point2); geometry::detail::conversion::point_to_point < Point1, Point2, MinDim, lesser_dim > ::apply(point1, point2); // TODO: fill point2 with zeros if dim1 < dim2 ? // currently no need because equal dimensions are checked } struct forward_point_projection_policy { template static inline bool apply(LL const& ll, XY & xy, Proj const& proj) { return proj.forward(ll, xy); } }; struct inverse_point_projection_policy { template static inline bool apply(XY const& xy, LL & ll, Proj const& proj) { return proj.inverse(xy, ll); } }; template struct project_point { template static inline bool apply(P1 const& p1, P2 & p2, Proj const& proj) { // (Geographic -> Cartesian) will be projected, rest will be copied. // So first copy third or higher dimensions projections::detail::copy_higher_dimensions<2>(p1, p2); if (! PointPolicy::apply(p1, p2, proj)) { // For consistency with transformation set_invalid_point(p2); return false; } return true; } }; template struct project_range { template struct convert_policy { explicit convert_policy(Proj const& proj) : m_proj(proj) , m_result(true) {} template inline void apply(Point1 const& point1, Point2 & point2) { if (! project_point::apply(point1, point2, m_proj) ) m_result = false; } bool result() const { return m_result; } private: Proj const& m_proj; bool m_result; }; template static inline bool apply(R1 const& r1, R2 & r2, Proj const& proj) { return geometry::detail::conversion::range_to_range < R1, R2, geometry::point_order::value != geometry::point_order::value >::apply(r1, r2, convert_policy(proj)).result(); } }; template struct project_multi { template static inline bool apply(G1 const& g1, G2 & g2, Proj const& proj) { range::resize(g2, boost::size(g1)); return apply(boost::begin(g1), boost::end(g1), boost::begin(g2), proj); } private: template static inline bool apply(It1 g1_first, It1 g1_last, It2 g2_first, Proj const& proj) { bool result = true; for ( ; g1_first != g1_last ; ++g1_first, ++g2_first ) { if (! Policy::apply(*g1_first, *g2_first, proj)) { result = false; } } return result; } }; template < typename Geometry, typename PointPolicy, typename Tag = typename geometry::tag::type > struct project_geometry {}; template struct project_geometry : project_point {}; template struct project_geometry : project_range {}; template struct project_geometry { template static inline bool apply(G1 const& g1, G2 & g2, Proj const& proj) { bool r1 = apply<0>(g1, g2, proj); bool r2 = apply<1>(g1, g2, proj); return r1 && r2; } private: template static inline bool apply(G1 const& g1, G2 & g2, Proj const& proj) { geometry::detail::indexed_point_view pt1(g1); geometry::detail::indexed_point_view pt2(g2); return project_point::apply(pt1, pt2, proj); } }; template struct project_geometry : project_range {}; template struct project_geometry : project_multi< project_range > {}; template struct project_geometry : project_range {}; template struct project_geometry { template static inline bool apply(G1 const& g1, G2 & g2, Proj const& proj) { bool r1 = project_range < PointPolicy >::apply(geometry::exterior_ring(g1), geometry::exterior_ring(g2), proj); bool r2 = project_multi < project_range >::apply(geometry::interior_rings(g1), geometry::interior_rings(g2), proj); return r1 && r2; } }; template struct project_geometry : project_multi < project_geometry < typename boost::range_value::type, PointPolicy, polygon_tag > > {}; } // namespace detail #endif // DOXYGEN_NO_DETAIL template struct dynamic_parameters { static const bool is_specialized = false; }; template <> struct dynamic_parameters { static const bool is_specialized = true; static inline srs::detail::proj4_parameters apply(srs::proj4 const& params) { return srs::detail::proj4_parameters(params.str()); } }; template struct dynamic_parameters > { static const bool is_specialized = true; static inline srs::dpar::parameters const& apply(srs::dpar::parameters const& params) { return params; } }; // proj_wrapper class and its specializations wrapps the internal projection // representation and implements transparent creation of projection object template class proj_wrapper { BOOST_GEOMETRY_STATIC_ASSERT_FALSE( "Unknown projection definition.", Proj); }; template class proj_wrapper { // Some projections do not work with float -> wrong results // select from int/float/double and else selects T typedef typename projections::detail::promote_to_double::type calc_t; typedef projections::parameters parameters_type; typedef projections::detail::dynamic_wrapper_b vprj_t; public: template < typename Params, std::enable_if_t < dynamic_parameters::is_specialized, int > = 0 > proj_wrapper(Params const& params) : m_ptr(create(dynamic_parameters::apply(params))) {} vprj_t const& proj() const { return *m_ptr; } vprj_t & mutable_proj() { return *m_ptr; } private: template static vprj_t* create(Params const& params) { parameters_type parameters = projections::detail::pj_init(params); vprj_t* result = projections::detail::create_new(params, parameters); if (result == NULL) { if (parameters.id.is_unknown()) { BOOST_THROW_EXCEPTION(projection_not_named_exception()); } else { // TODO: handle non-string projection id BOOST_THROW_EXCEPTION(projection_unknown_id_exception()); } } return result; } boost::shared_ptr m_ptr; }; template class static_proj_wrapper_base { typedef typename projections::detail::promote_to_double::type calc_t; typedef projections::parameters parameters_type; typedef typename srs::spar::detail::pick_proj_tag < StaticParameters >::type proj_tag; typedef typename projections::detail::static_projection_type < proj_tag, typename projections::detail::static_srs_tag::type, StaticParameters, calc_t, parameters_type >::type projection_type; public: projection_type const& proj() const { return m_proj; } projection_type & mutable_proj() { return m_proj; } protected: explicit static_proj_wrapper_base(StaticParameters const& s_params) : m_proj(s_params, projections::detail::pj_init(s_params)) {} private: projection_type m_proj; }; template class proj_wrapper, CT> : public static_proj_wrapper_base, CT> { typedef srs::spar::parameters static_parameters_type; typedef static_proj_wrapper_base < static_parameters_type, CT > base_t; public: proj_wrapper() : base_t(static_parameters_type()) {} proj_wrapper(static_parameters_type const& s_params) : base_t(s_params) {} }; // projection class implements transparent forward/inverse projection interface template class projection : private proj_wrapper { typedef proj_wrapper base_t; public: projection() {} template explicit projection(Params const& params) : base_t(params) {} /// Forward projection, from Latitude-Longitude to Cartesian template inline bool forward(LL const& ll, XY& xy) const { BOOST_GEOMETRY_STATIC_ASSERT( (projections::detail::same_tags::value), "Not supported combination of Geometries.", LL, XY); concepts::check_concepts_and_equal_dimensions(); return projections::detail::project_geometry < LL, projections::detail::forward_point_projection_policy >::apply(ll, xy, base_t::proj()); } /// Inverse projection, from Cartesian to Latitude-Longitude template inline bool inverse(XY const& xy, LL& ll) const { BOOST_GEOMETRY_STATIC_ASSERT( (projections::detail::same_tags::value), "Not supported combination of Geometries.", XY, LL); concepts::check_concepts_and_equal_dimensions(); return projections::detail::project_geometry < XY, projections::detail::inverse_point_projection_policy >::apply(xy, ll, base_t::proj()); } }; } // namespace projections namespace srs { /*! \brief Representation of projection \details Either dynamic or static projection representation \ingroup projection \tparam Parameters default dynamic tag or static projection parameters \tparam CT calculation type used internally */ template < typename Parameters = srs::dynamic, typename CT = double > class projection : public projections::projection { typedef projections::projection base_t; public: projection() {} projection(Parameters const& parameters) : base_t(parameters) {} /*! \ingroup projection \brief Initializes a projection as a string, using the format with + and = \details The projection can be initialized with a string (with the same format as the PROJ4 package) for convenient initialization from, for example, the command line \par Example srs::proj4("+proj=labrd +ellps=intl +lon_0=46d26'13.95E +lat_0=18d54S +azi=18d54 +k_0=.9995 +x_0=400000 +y_0=800000") for the Madagascar projection. */ template < typename DynamicParameters, std::enable_if_t < projections::dynamic_parameters::is_specialized, int > = 0 > projection(DynamicParameters const& dynamic_parameters) : base_t(dynamic_parameters) {} }; } // namespace srs }} // namespace boost::geometry #endif // BOOST_GEOMETRY_SRS_PROJECTION_HPP