// 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 <string> #include <type_traits> #include <boost/smart_ptr/shared_ptr.hpp> #include <boost/throw_exception.hpp> #include <boost/geometry/algorithms/convert.hpp> #include <boost/geometry/algorithms/detail/convert_point_to_point.hpp> #include <boost/geometry/core/coordinate_dimension.hpp> #include <boost/geometry/core/static_assert.hpp> #include <boost/geometry/srs/projections/dpar.hpp> #include <boost/geometry/srs/projections/exception.hpp> #include <boost/geometry/srs/projections/factory.hpp> #include <boost/geometry/srs/projections/impl/base_dynamic.hpp> #include <boost/geometry/srs/projections/impl/base_static.hpp> #include <boost/geometry/srs/projections/impl/pj_init.hpp> #include <boost/geometry/srs/projections/invalid_point.hpp> #include <boost/geometry/srs/projections/proj4.hpp> #include <boost/geometry/srs/projections/spar.hpp> #include <boost/geometry/views/detail/indexed_point_view.hpp> namespace boost { namespace geometry { namespace projections { #ifndef DOXYGEN_NO_DETAIL namespace detail { template <typename G1, typename G2> struct same_tags : std::is_same < typename geometry::tag<G1>::type, typename geometry::tag<G2>::type > {}; template <typename CT> struct promote_to_double { typedef std::conditional_t < std::is_integral<CT>::value || std::is_same<CT, float>::value, double, CT > type; }; // Copy coordinates of dimensions >= MinDim template <std::size_t MinDim, typename Point1, typename Point2> inline void copy_higher_dimensions(Point1 const& point1, Point2 & point2) { static const std::size_t dim1 = geometry::dimension<Point1>::value; static const std::size_t dim2 = geometry::dimension<Point2>::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 <typename LL, typename XY, typename Proj> static inline bool apply(LL const& ll, XY & xy, Proj const& proj) { return proj.forward(ll, xy); } }; struct inverse_point_projection_policy { template <typename XY, typename LL, typename Proj> static inline bool apply(XY const& xy, LL & ll, Proj const& proj) { return proj.inverse(xy, ll); } }; template <typename PointPolicy> struct project_point { template <typename P1, typename P2, typename Proj> 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 <typename PointPolicy> struct project_range { template <typename Proj> struct convert_policy { explicit convert_policy(Proj const& proj) : m_proj(proj) , m_result(true) {} template <typename Point1, typename Point2> inline void apply(Point1 const& point1, Point2 & point2) { if (! project_point<PointPolicy>::apply(point1, point2, m_proj) ) m_result = false; } bool result() const { return m_result; } private: Proj const& m_proj; bool m_result; }; template <typename R1, typename R2, typename Proj> static inline bool apply(R1 const& r1, R2 & r2, Proj const& proj) { return geometry::detail::conversion::range_to_range < R1, R2, geometry::point_order<R1>::value != geometry::point_order<R2>::value >::apply(r1, r2, convert_policy<Proj>(proj)).result(); } }; template <typename Policy> struct project_multi { template <typename G1, typename G2, typename Proj> 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 <typename It1, typename It2, typename Proj> 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<Geometry>::type > struct project_geometry {}; template <typename Geometry, typename PointPolicy> struct project_geometry<Geometry, PointPolicy, point_tag> : project_point<PointPolicy> {}; template <typename Geometry, typename PointPolicy> struct project_geometry<Geometry, PointPolicy, multi_point_tag> : project_range<PointPolicy> {}; template <typename Geometry, typename PointPolicy> struct project_geometry<Geometry, PointPolicy, segment_tag> { template <typename G1, typename G2, typename Proj> 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 <std::size_t Index, typename G1, typename G2, typename Proj> static inline bool apply(G1 const& g1, G2 & g2, Proj const& proj) { geometry::detail::indexed_point_view<G1 const, Index> pt1(g1); geometry::detail::indexed_point_view<G2, Index> pt2(g2); return project_point<PointPolicy>::apply(pt1, pt2, proj); } }; template <typename Geometry, typename PointPolicy> struct project_geometry<Geometry, PointPolicy, linestring_tag> : project_range<PointPolicy> {}; template <typename Geometry, typename PointPolicy> struct project_geometry<Geometry, PointPolicy, multi_linestring_tag> : project_multi< project_range<PointPolicy> > {}; template <typename Geometry, typename PointPolicy> struct project_geometry<Geometry, PointPolicy, ring_tag> : project_range<PointPolicy> {}; template <typename Geometry, typename PointPolicy> struct project_geometry<Geometry, PointPolicy, polygon_tag> { template <typename G1, typename G2, typename Proj> 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<PointPolicy> >::apply(geometry::interior_rings(g1), geometry::interior_rings(g2), proj); return r1 && r2; } }; template <typename MultiPolygon, typename PointPolicy> struct project_geometry<MultiPolygon, PointPolicy, multi_polygon_tag> : project_multi < project_geometry < typename boost::range_value<MultiPolygon>::type, PointPolicy, polygon_tag > > {}; } // namespace detail #endif // DOXYGEN_NO_DETAIL template <typename Params> struct dynamic_parameters { static const bool is_specialized = false; }; template <> struct dynamic_parameters<srs::proj4> { 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 <typename T> struct dynamic_parameters<srs::dpar::parameters<T> > { static const bool is_specialized = true; static inline srs::dpar::parameters<T> const& apply(srs::dpar::parameters<T> const& params) { return params; } }; // proj_wrapper class and its specializations wrapps the internal projection // representation and implements transparent creation of projection object template <typename Proj, typename CT> class proj_wrapper { BOOST_GEOMETRY_STATIC_ASSERT_FALSE( "Unknown projection definition.", Proj); }; template <typename CT> class proj_wrapper<srs::dynamic, CT> { // Some projections do not work with float -> wrong results // select <double> from int/float/double and else selects T typedef typename projections::detail::promote_to_double<CT>::type calc_t; typedef projections::parameters<calc_t> parameters_type; typedef projections::detail::dynamic_wrapper_b<calc_t, parameters_type> vprj_t; public: template < typename Params, std::enable_if_t < dynamic_parameters<Params>::is_specialized, int > = 0 > proj_wrapper(Params const& params) : m_ptr(create(dynamic_parameters<Params>::apply(params))) {} vprj_t const& proj() const { return *m_ptr; } vprj_t & mutable_proj() { return *m_ptr; } private: template <typename Params> static vprj_t* create(Params const& params) { parameters_type parameters = projections::detail::pj_init<calc_t>(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<vprj_t> m_ptr; }; template <typename StaticParameters, typename CT> class static_proj_wrapper_base { typedef typename projections::detail::promote_to_double<CT>::type calc_t; typedef projections::parameters<calc_t> 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<StaticParameters>::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<calc_t>(s_params)) {} private: projection_type m_proj; }; template <typename ...Ps, typename CT> class proj_wrapper<srs::spar::parameters<Ps...>, CT> : public static_proj_wrapper_base<srs::spar::parameters<Ps...>, CT> { typedef srs::spar::parameters<Ps...> 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 <typename Proj, typename CT> class projection : private proj_wrapper<Proj, CT> { typedef proj_wrapper<Proj, CT> base_t; public: projection() {} template <typename Params> explicit projection(Params const& params) : base_t(params) {} /// Forward projection, from Latitude-Longitude to Cartesian template <typename LL, typename XY> inline bool forward(LL const& ll, XY& xy) const { BOOST_GEOMETRY_STATIC_ASSERT( (projections::detail::same_tags<LL, XY>::value), "Not supported combination of Geometries.", LL, XY); concepts::check_concepts_and_equal_dimensions<LL const, XY>(); 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 <typename XY, typename LL> inline bool inverse(XY const& xy, LL& ll) const { BOOST_GEOMETRY_STATIC_ASSERT( (projections::detail::same_tags<XY, LL>::value), "Not supported combination of Geometries.", XY, LL); concepts::check_concepts_and_equal_dimensions<XY const, LL>(); 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<Parameters, CT> { typedef projections::projection<Parameters, CT> 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 <tt>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")</tt> for the Madagascar projection. */ template < typename DynamicParameters, std::enable_if_t < projections::dynamic_parameters<DynamicParameters>::is_specialized, int > = 0 > projection(DynamicParameters const& dynamic_parameters) : base_t(dynamic_parameters) {} }; } // namespace srs }} // namespace boost::geometry #endif // BOOST_GEOMETRY_SRS_PROJECTION_HPP