Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:35:40

0001 // Boost.Geometry - gis-projections (based on PROJ4)
0002 
0003 // Copyright (c) 2008-2015 Barend Gehrels, Amsterdam, the Netherlands.
0004 
0005 // This file was modified by Oracle on 2017-2020.
0006 // Modifications copyright (c) 2017-2020, Oracle and/or its affiliates.
0007 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle.
0008 
0009 // Use, modification and distribution is subject to the Boost Software License,
0010 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
0011 // http://www.boost.org/LICENSE_1_0.txt)
0012 
0013 // This file is converted from PROJ4, http://trac.osgeo.org/proj
0014 // PROJ4 is originally written by Gerald Evenden (then of the USGS)
0015 // PROJ4 is maintained by Frank Warmerdam
0016 // PROJ4 is converted to Boost.Geometry by Barend Gehrels
0017 
0018 // Last updated version of proj: 5.0.0
0019 
0020 // Original copyright notice:
0021 
0022 // Purpose:  Implementation of the aeqd (Azimuthal Equidistant) projection.
0023 // Author:   Gerald Evenden
0024 // Copyright (c) 1995, Gerald Evenden
0025 
0026 // Permission is hereby granted, free of charge, to any person obtaining a
0027 // copy of this software and associated documentation files (the "Software"),
0028 // to deal in the Software without restriction, including without limitation
0029 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
0030 // and/or sell copies of the Software, and to permit persons to whom the
0031 // Software is furnished to do so, subject to the following conditions:
0032 
0033 // The above copyright notice and this permission notice shall be included
0034 // in all copies or substantial portions of the Software.
0035 
0036 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
0037 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0038 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
0039 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0040 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
0041 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
0042 // DEALINGS IN THE SOFTWARE.
0043 
0044 #ifndef BOOST_GEOMETRY_PROJECTIONS_AEQD_HPP
0045 #define BOOST_GEOMETRY_PROJECTIONS_AEQD_HPP
0046 
0047 
0048 #include <type_traits>
0049 
0050 #include <boost/config.hpp>
0051 
0052 #include <boost/geometry/formulas/vincenty_direct.hpp>
0053 #include <boost/geometry/formulas/vincenty_inverse.hpp>
0054 
0055 #include <boost/geometry/srs/projections/impl/aasincos.hpp>
0056 #include <boost/geometry/srs/projections/impl/base_static.hpp>
0057 #include <boost/geometry/srs/projections/impl/base_dynamic.hpp>
0058 #include <boost/geometry/srs/projections/impl/factory_entry.hpp>
0059 #include <boost/geometry/srs/projections/impl/pj_mlfn.hpp>
0060 #include <boost/geometry/srs/projections/impl/pj_param.hpp>
0061 #include <boost/geometry/srs/projections/impl/projects.hpp>
0062 
0063 #include <boost/geometry/util/math.hpp>
0064 
0065 #include <boost/math/special_functions/hypot.hpp>
0066 
0067 
0068 namespace boost { namespace geometry
0069 {
0070 
0071 namespace projections
0072 {
0073     #ifndef DOXYGEN_NO_DETAIL
0074     namespace detail { namespace aeqd
0075     {
0076 
0077             static const double epsilon10 = 1.e-10;
0078             static const double tolerance = 1.e-14;
0079             enum mode_type {
0080                 n_pole = 0,
0081                 s_pole = 1,
0082                 equit  = 2,
0083                 obliq  = 3
0084             };
0085 
0086             template <typename T>
0087             struct par_aeqd
0088             {
0089                 T    sinph0;
0090                 T    cosph0;
0091                 detail::en<T> en;
0092                 T    M1;
0093                 //T    N1;
0094                 T    Mp;
0095                 //T    He;
0096                 //T    G;
0097                 T    b;
0098                 mode_type mode;
0099             };
0100 
0101             template <typename T, typename Par, typename ProjParm>
0102             inline void e_forward(T const& lp_lon, T const& lp_lat, T& xy_x, T& xy_y, Par const& par, ProjParm const& proj_parm)
0103             {
0104                 T coslam, cosphi, sinphi, rho;
0105                 //T azi1, s12;
0106                 //T lam1, phi1, lam2, phi2;
0107 
0108                 coslam = cos(lp_lon);
0109                 cosphi = cos(lp_lat);
0110                 sinphi = sin(lp_lat);
0111                 switch (proj_parm.mode) {
0112                 case n_pole:
0113                     coslam = - coslam;
0114                     BOOST_FALLTHROUGH;
0115                 case s_pole:
0116                     xy_x = (rho = fabs(proj_parm.Mp - pj_mlfn(lp_lat, sinphi, cosphi, proj_parm.en))) *
0117                         sin(lp_lon);
0118                     xy_y = rho * coslam;
0119                     break;
0120                 case equit:
0121                 case obliq:
0122                     if (fabs(lp_lon) < epsilon10 && fabs(lp_lat - par.phi0) < epsilon10) {
0123                         xy_x = xy_y = 0.;
0124                         break;
0125                     }
0126 
0127                     //phi1 = par.phi0; lam1 = par.lam0;
0128                     //phi2 = lp_lat;  lam2 = lp_lon + par.lam0;
0129 
0130                     formula::result_inverse<T> const inv =
0131                         formula::vincenty_inverse
0132                             <
0133                                 T, true, true
0134                             >::apply(par.lam0, par.phi0, lp_lon + par.lam0, lp_lat, srs::spheroid<T>(par.a, proj_parm.b));
0135                     //azi1 = inv.azimuth; s12 = inv.distance;
0136                     xy_x = inv.distance * sin(inv.azimuth) / par.a;
0137                     xy_y = inv.distance * cos(inv.azimuth) / par.a;
0138                     break;
0139                 }
0140             }
0141 
0142             template <typename T, typename Par, typename ProjParm>
0143             inline void e_inverse(T const& xy_x, T const& xy_y, T& lp_lon, T& lp_lat, Par const& par, ProjParm const& proj_parm)
0144             {
0145                 T c;
0146 
0147                 if ((c = boost::math::hypot(xy_x, xy_y)) < epsilon10) {
0148                     lp_lat = par.phi0;
0149                     lp_lon = 0.;
0150                         return;
0151                 }
0152                 if (proj_parm.mode == obliq || proj_parm.mode == equit) {
0153                     T const x2 = xy_x * par.a;
0154                     T const y2 = xy_y * par.a;
0155                     //T const lat1 = par.phi0;
0156                     //T const lon1 = par.lam0;
0157                     T const azi1 = atan2(x2, y2);
0158                     T const s12 = sqrt(x2 * x2 + y2 * y2);
0159                     formula::result_direct<T> const dir =
0160                         formula::vincenty_direct
0161                             <
0162                                 T, true
0163                             >::apply(par.lam0, par.phi0, s12, azi1, srs::spheroid<T>(par.a, proj_parm.b));
0164                     lp_lat = dir.lat2;
0165                     lp_lon = dir.lon2;
0166                     lp_lon -= par.lam0;
0167                 } else { /* Polar */
0168                     lp_lat = pj_inv_mlfn(proj_parm.mode == n_pole ? proj_parm.Mp - c : proj_parm.Mp + c,
0169                         par.es, proj_parm.en);
0170                     lp_lon = atan2(xy_x, proj_parm.mode == n_pole ? -xy_y : xy_y);
0171                 }
0172             }
0173 
0174             template <typename T, typename Par, typename ProjParm>
0175             inline void e_guam_fwd(T const& lp_lon, T const& lp_lat, T& xy_x, T& xy_y, Par const& par, ProjParm const& proj_parm)
0176             {
0177                 T cosphi, sinphi, t;
0178 
0179                 cosphi = cos(lp_lat);
0180                 sinphi = sin(lp_lat);
0181                 t = 1. / sqrt(1. - par.es * sinphi * sinphi);
0182                 xy_x = lp_lon * cosphi * t;
0183                 xy_y = pj_mlfn(lp_lat, sinphi, cosphi, proj_parm.en) - proj_parm.M1 +
0184                     .5 * lp_lon * lp_lon * cosphi * sinphi * t;
0185             }
0186 
0187             template <typename T, typename Par, typename ProjParm>
0188             inline void e_guam_inv(T const& xy_x, T const& xy_y, T& lp_lon, T& lp_lat, Par const& par, ProjParm const& proj_parm)
0189             {
0190                 T x2, t = 0.0;
0191                 int i;
0192 
0193                 x2 = 0.5 * xy_x * xy_x;
0194                 lp_lat = par.phi0;
0195                 for (i = 0; i < 3; ++i) {
0196                     t = par.e * sin(lp_lat);
0197                     lp_lat = pj_inv_mlfn(proj_parm.M1 + xy_y -
0198                         x2 * tan(lp_lat) * (t = sqrt(1. - t * t)), par.es, proj_parm.en);
0199                 }
0200                 lp_lon = xy_x * t / cos(lp_lat);
0201             }
0202 
0203             template <typename T, typename Par, typename ProjParm>
0204             inline void s_forward(T const& lp_lon, T lp_lat, T& xy_x, T& xy_y, Par const& /*par*/, ProjParm const& proj_parm)
0205             {
0206                 static const T half_pi = detail::half_pi<T>();
0207 
0208                 T coslam, cosphi, sinphi;
0209 
0210                 sinphi = sin(lp_lat);
0211                 cosphi = cos(lp_lat);
0212                 coslam = cos(lp_lon);
0213                 switch (proj_parm.mode) {
0214                 case equit:
0215                     xy_y = cosphi * coslam;
0216                     goto oblcon;
0217                 case obliq:
0218                     xy_y = proj_parm.sinph0 * sinphi + proj_parm.cosph0 * cosphi * coslam;
0219             oblcon:
0220                     if (fabs(fabs(xy_y) - 1.) < tolerance)
0221                         if (xy_y < 0.)
0222                             BOOST_THROW_EXCEPTION( projection_exception(error_tolerance_condition) );
0223                         else
0224                             xy_x = xy_y = 0.;
0225                     else {
0226                         xy_y = acos(xy_y);
0227                         xy_y /= sin(xy_y);
0228                         xy_x = xy_y * cosphi * sin(lp_lon);
0229                         xy_y *= (proj_parm.mode == equit) ? sinphi :
0230                                 proj_parm.cosph0 * sinphi - proj_parm.sinph0 * cosphi * coslam;
0231                     }
0232                     break;
0233                 case n_pole:
0234                     lp_lat = -lp_lat;
0235                     coslam = -coslam;
0236                     BOOST_FALLTHROUGH;
0237                 case s_pole:
0238                     if (fabs(lp_lat - half_pi) < epsilon10)
0239                         BOOST_THROW_EXCEPTION( projection_exception(error_tolerance_condition) );
0240                     xy_x = (xy_y = (half_pi + lp_lat)) * sin(lp_lon);
0241                     xy_y *= coslam;
0242                     break;
0243                 }
0244             }
0245 
0246             template <typename T, typename Par, typename ProjParm>
0247             inline void s_inverse(T xy_x, T xy_y, T& lp_lon, T& lp_lat, Par const& par, ProjParm const& proj_parm)
0248             {
0249                 static const T pi = detail::pi<T>();
0250                 static const T half_pi = detail::half_pi<T>();
0251 
0252                 T cosc, c_rh, sinc;
0253 
0254                 if ((c_rh = boost::math::hypot(xy_x, xy_y)) > pi) {
0255                     if (c_rh - epsilon10 > pi)
0256                         BOOST_THROW_EXCEPTION( projection_exception(error_tolerance_condition) );
0257                     c_rh = pi;
0258                 } else if (c_rh < epsilon10) {
0259                     lp_lat = par.phi0;
0260                     lp_lon = 0.;
0261                         return;
0262                 }
0263                 if (proj_parm.mode == obliq || proj_parm.mode == equit) {
0264                     sinc = sin(c_rh);
0265                     cosc = cos(c_rh);
0266                     if (proj_parm.mode == equit) {
0267                                     lp_lat = aasin(xy_y * sinc / c_rh);
0268                         xy_x *= sinc;
0269                         xy_y = cosc * c_rh;
0270                     } else {
0271                         lp_lat = aasin(cosc * proj_parm.sinph0 + xy_y * sinc * proj_parm.cosph0 /
0272                             c_rh);
0273                         xy_y = (cosc - proj_parm.sinph0 * sin(lp_lat)) * c_rh;
0274                         xy_x *= sinc * proj_parm.cosph0;
0275                     }
0276                     lp_lon = xy_y == 0. ? 0. : atan2(xy_x, xy_y);
0277                 } else if (proj_parm.mode == n_pole) {
0278                     lp_lat = half_pi - c_rh;
0279                     lp_lon = atan2(xy_x, -xy_y);
0280                 } else {
0281                     lp_lat = c_rh - half_pi;
0282                     lp_lon = atan2(xy_x, xy_y);
0283                 }
0284             }
0285 
0286             // Azimuthal Equidistant
0287             template <typename Params, typename Parameters, typename T>
0288             inline void setup_aeqd(Params const& params, Parameters& par, par_aeqd<T>& proj_parm, bool is_sphere, bool is_guam)
0289             {
0290                 static const T half_pi = detail::half_pi<T>();
0291 
0292                 par.phi0 = pj_get_param_r<T, srs::spar::lat_0>(params, "lat_0", srs::dpar::lat_0);
0293                 if (fabs(fabs(par.phi0) - half_pi) < epsilon10) {
0294                     proj_parm.mode = par.phi0 < 0. ? s_pole : n_pole;
0295                     proj_parm.sinph0 = par.phi0 < 0. ? -1. : 1.;
0296                     proj_parm.cosph0 = 0.;
0297                 } else if (fabs(par.phi0) < epsilon10) {
0298                     proj_parm.mode = equit;
0299                     proj_parm.sinph0 = 0.;
0300                     proj_parm.cosph0 = 1.;
0301                 } else {
0302                     proj_parm.mode = obliq;
0303                     proj_parm.sinph0 = sin(par.phi0);
0304                     proj_parm.cosph0 = cos(par.phi0);
0305                 }
0306                 if (is_sphere) {
0307                     /* empty */
0308                 } else {
0309                     proj_parm.en = pj_enfn<T>(par.es);
0310                     if (is_guam) {
0311                         proj_parm.M1 = pj_mlfn(par.phi0, proj_parm.sinph0, proj_parm.cosph0, proj_parm.en);
0312                     } else {
0313                         switch (proj_parm.mode) {
0314                         case n_pole:
0315                             proj_parm.Mp = pj_mlfn<T>(half_pi, 1., 0., proj_parm.en);
0316                             break;
0317                         case s_pole:
0318                             proj_parm.Mp = pj_mlfn<T>(-half_pi, -1., 0., proj_parm.en);
0319                             break;
0320                         case equit:
0321                         case obliq:
0322                             //proj_parm.N1 = 1. / sqrt(1. - par.es * proj_parm.sinph0 * proj_parm.sinph0);
0323                             //proj_parm.G = proj_parm.sinph0 * (proj_parm.He = par.e / sqrt(par.one_es));
0324                             //proj_parm.He *= proj_parm.cosph0;
0325                             break;
0326                         }
0327                         // Boost.Geometry specific, in proj4 geodesic is initialized at the beginning
0328                         proj_parm.b = math::sqrt(math::sqr(par.a) * (1. - par.es));
0329                     }
0330                 }
0331             }
0332 
0333             template <typename T, typename Parameters>
0334             struct base_aeqd_e
0335             {
0336                 par_aeqd<T> m_proj_parm;
0337 
0338                 // FORWARD(e_forward)  elliptical
0339                 // Project coordinates from geographic (lon, lat) to cartesian (x, y)
0340                 inline void fwd(Parameters const& par, T const& lp_lon, T const& lp_lat, T& xy_x, T& xy_y) const
0341                 {
0342                     e_forward(lp_lon, lp_lat, xy_x, xy_y, par, this->m_proj_parm);
0343                 }
0344 
0345                 // INVERSE(e_inverse)  elliptical
0346                 // Project coordinates from cartesian (x, y) to geographic (lon, lat)
0347                 inline void inv(Parameters const& par, T const& xy_x, T const& xy_y, T& lp_lon, T& lp_lat) const
0348                 {
0349                     e_inverse(xy_x, xy_y, lp_lon, lp_lat, par, this->m_proj_parm);
0350                 }
0351 
0352                 static inline std::string get_name()
0353                 {
0354                     return "aeqd_e";
0355                 }
0356 
0357             };
0358 
0359             template <typename T, typename Parameters>
0360             struct base_aeqd_e_guam
0361             {
0362                 par_aeqd<T> m_proj_parm;
0363 
0364                 // FORWARD(e_guam_fwd)  Guam elliptical
0365                 // Project coordinates from geographic (lon, lat) to cartesian (x, y)
0366                 inline void fwd(Parameters const& par, T const& lp_lon, T const& lp_lat, T& xy_x, T& xy_y) const
0367                 {
0368                     e_guam_fwd(lp_lon, lp_lat, xy_x, xy_y, par, this->m_proj_parm);
0369                 }
0370 
0371                 // INVERSE(e_guam_inv)  Guam elliptical
0372                 // Project coordinates from cartesian (x, y) to geographic (lon, lat)
0373                 inline void inv(Parameters const& par, T const& xy_x, T const& xy_y, T& lp_lon, T& lp_lat) const
0374                 {
0375                     e_guam_inv(xy_x, xy_y, lp_lon, lp_lat, par, this->m_proj_parm);
0376                 }
0377 
0378                 static inline std::string get_name()
0379                 {
0380                     return "aeqd_e_guam";
0381                 }
0382 
0383             };
0384 
0385             template <typename T, typename Parameters>
0386             struct base_aeqd_s
0387             {
0388                 par_aeqd<T> m_proj_parm;
0389 
0390                 // FORWARD(s_forward)  spherical
0391                 // Project coordinates from geographic (lon, lat) to cartesian (x, y)
0392                 inline void fwd(Parameters const& par, T const& lp_lon, T const& lp_lat, T& xy_x, T& xy_y) const
0393                 {
0394                     s_forward(lp_lon, lp_lat, xy_x, xy_y, par, this->m_proj_parm);
0395                 }
0396 
0397                 // INVERSE(s_inverse)  spherical
0398                 // Project coordinates from cartesian (x, y) to geographic (lon, lat)
0399                 inline void inv(Parameters const& par, T const& xy_x, T const& xy_y, T& lp_lon, T& lp_lat) const
0400                 {
0401                     s_inverse(xy_x, xy_y, lp_lon, lp_lat, par, this->m_proj_parm);
0402                 }
0403 
0404                 static inline std::string get_name()
0405                 {
0406                     return "aeqd_s";
0407                 }
0408 
0409             };
0410 
0411     }} // namespace detail::aeqd
0412     #endif // doxygen
0413 
0414     /*!
0415         \brief Azimuthal Equidistant projection
0416         \ingroup projections
0417         \tparam Geographic latlong point type
0418         \tparam Cartesian xy point type
0419         \tparam Parameters parameter type
0420         \par Projection characteristics
0421          - Azimuthal
0422          - Spheroid
0423          - Ellipsoid
0424         \par Projection parameters
0425          - lat_0: Latitude of origin (degrees)
0426          - guam (boolean)
0427         \par Example
0428         \image html ex_aeqd.gif
0429     */
0430     template <typename T, typename Parameters>
0431     struct aeqd_e : public detail::aeqd::base_aeqd_e<T, Parameters>
0432     {
0433         template <typename Params>
0434         inline aeqd_e(Params const& params, Parameters & par)
0435         {
0436             detail::aeqd::setup_aeqd(params, par, this->m_proj_parm, false, false);
0437         }
0438     };
0439 
0440     /*!
0441         \brief Azimuthal Equidistant projection
0442         \ingroup projections
0443         \tparam Geographic latlong point type
0444         \tparam Cartesian xy point type
0445         \tparam Parameters parameter type
0446         \par Projection characteristics
0447          - Azimuthal
0448          - Spheroid
0449          - Ellipsoid
0450         \par Projection parameters
0451          - lat_0: Latitude of origin (degrees)
0452          - guam (boolean)
0453         \par Example
0454         \image html ex_aeqd.gif
0455     */
0456     template <typename T, typename Parameters>
0457     struct aeqd_e_guam : public detail::aeqd::base_aeqd_e_guam<T, Parameters>
0458     {
0459         template <typename Params>
0460         inline aeqd_e_guam(Params const& params, Parameters & par)
0461         {
0462             detail::aeqd::setup_aeqd(params, par, this->m_proj_parm, false, true);
0463         }
0464     };
0465 
0466     /*!
0467         \brief Azimuthal Equidistant projection
0468         \ingroup projections
0469         \tparam Geographic latlong point type
0470         \tparam Cartesian xy point type
0471         \tparam Parameters parameter type
0472         \par Projection characteristics
0473          - Azimuthal
0474          - Spheroid
0475          - Ellipsoid
0476         \par Projection parameters
0477          - lat_0: Latitude of origin (degrees)
0478          - guam (boolean)
0479         \par Example
0480         \image html ex_aeqd.gif
0481     */
0482     template <typename T, typename Parameters>
0483     struct aeqd_s : public detail::aeqd::base_aeqd_s<T, Parameters>
0484     {
0485         template <typename Params>
0486         inline aeqd_s(Params const& params, Parameters & par)
0487         {
0488             detail::aeqd::setup_aeqd(params, par, this->m_proj_parm, true, false);
0489         }
0490     };
0491 
0492     #ifndef DOXYGEN_NO_DETAIL
0493     namespace detail
0494     {
0495 
0496         // Static projection
0497         template <typename BGP, typename CT, typename P>
0498         struct static_projection_type<srs::spar::proj_aeqd, srs_sphere_tag, BGP, CT, P>
0499         {
0500             typedef static_wrapper_fi<aeqd_s<CT, P>, P> type;
0501         };
0502         template <typename BGP, typename CT, typename P>
0503         struct static_projection_type<srs::spar::proj_aeqd, srs_spheroid_tag, BGP, CT, P>
0504         {
0505             typedef static_wrapper_fi
0506                 <
0507                     std::conditional_t
0508                         <
0509                             std::is_void
0510                                 <
0511                                     typename geometry::tuples::find_if
0512                                         <
0513                                             BGP,
0514                                             //srs::par4::detail::is_guam
0515                                             srs::spar::detail::is_param<srs::spar::guam>::pred
0516                                         >::type
0517                                 >::value,
0518                             aeqd_e<CT, P>,
0519                             aeqd_e_guam<CT, P>
0520                         >
0521                     , P
0522                 > type;
0523         };
0524 
0525         BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_ENTRY_BEGIN(aeqd_entry)
0526         {
0527             bool const guam = pj_get_param_b<srs::spar::guam>(params, "guam", srs::dpar::guam);
0528 
0529             if (parameters.es && ! guam)
0530                 return new dynamic_wrapper_fi<aeqd_e<T, Parameters>, T, Parameters>(params, parameters);
0531             else if (parameters.es && guam)
0532                 return new dynamic_wrapper_fi<aeqd_e_guam<T, Parameters>, T, Parameters>(params, parameters);
0533             else
0534                 return new dynamic_wrapper_fi<aeqd_s<T, Parameters>, T, Parameters>(params, parameters);
0535         }
0536         BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_ENTRY_END
0537 
0538         BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_INIT_BEGIN(aeqd_init)
0539         {
0540             BOOST_GEOMETRY_PROJECTIONS_DETAIL_FACTORY_INIT_ENTRY(aeqd, aeqd_entry)
0541         }
0542 
0543     } // namespace detail
0544     #endif // doxygen
0545 
0546 } // namespace projections
0547 
0548 }} // namespace boost::geometry
0549 
0550 #endif // BOOST_GEOMETRY_PROJECTIONS_AEQD_HPP
0551