diff --git a/docs/plot/plotdefs.json b/docs/plot/plotdefs.json index a6d70273e1..f55f5e0ab0 100644 --- a/docs/plot/plotdefs.json +++ b/docs/plot/plotdefs.json @@ -1057,7 +1057,7 @@ "lonmax": 180, "lonmin": -180, "name": "peirce_q_square", - "projstring": "+proj=peirce_q +lon_0=25 +type=square", + "projstring": "+proj=peirce_q +lon_0=25 +shape=square", "res": "low", "type": "poly" }, @@ -1068,7 +1068,7 @@ "lonmax": 180, "lonmin": -180, "name": "peirce_q", - "projstring": "+proj=peirce_q +lon_0=25 +type=diamond", + "projstring": "+proj=peirce_q +lon_0=25 +shape=diamond", "res": "low", "type": "poly" }, @@ -1079,7 +1079,7 @@ "lonmax": 180, "lonmin": -180, "name": "peirce_q_horizontal", - "projstring": "+proj=peirce_q +lon_0=25 +type=horizontal", + "projstring": "+proj=peirce_q +lon_0=25 +shape=horizontal", "res": "low", "type": "line" }, @@ -1090,7 +1090,7 @@ "lonmax": 180, "lonmin": -180, "name": "grieger_triptychial", - "projstring": "+proj=pipeline +step +proj=ob_tran +o_proj=peirce_q +o_lat_p=-45 +o_lon_p=45 +type=horizontal +scrollx=-0.25 +step +proj=affine +s11=-1 +s12=0 +s21=0 +s22=-1", + "projstring": "+proj=pipeline +step +proj=ob_tran +o_proj=peirce_q +o_lat_p=-45 +o_lon_p=45 +shape=horizontal +scrollx=-0.25 +step +proj=affine +s11=-1 +s12=0 +s21=0 +s22=-1", "res": "low", "type": "poly" }, diff --git a/docs/source/operations/projections/peirce_q.rst b/docs/source/operations/projections/peirce_q.rst index cf97293280..57504032af 100644 --- a/docs/source/operations/projections/peirce_q.rst +++ b/docs/source/operations/projections/peirce_q.rst @@ -14,10 +14,10 @@ defaults to a central meridian of 0, it is more common to use a central meridian of around 25 to optimise the distortions. Peirce's original published map from 1879 used a central meridian of approx -70. The diamond and square versions can be produced using the -``+type=diamond`` and ``+type=square`` options respectively. +``+shape=diamond`` and ``+shape=square`` options respectively. This implementation includes an alternative lateral projection -which places hemispheres side-by-side (``+type=horizontal`` or -``+type=vertical``). Combined with a general oblique transformation, +which places hemispheres side-by-side (``+shape=horizontal`` or +``+shape=vertical``). Combined with a general oblique transformation, this can be used to produced a Grieger Triptychial projection (see example below). @@ -43,21 +43,21 @@ this can be used to produced a Grieger Triptychial projection :align: center :alt: Peirce Quincuncial (Square) - proj-string: ``+proj=peirce_q +lon_0=25 +type=square`` + proj-string: ``+proj=peirce_q +lon_0=25 +shape=square`` .. figure:: ./images/peirce_q_diamond.png :width: 500 px :align: center :alt: Peirce Quincuncial (Diamond) - proj-string: ``+proj=peirce_q +lon_0=25 +type=diamond`` + proj-string: ``+proj=peirce_q +lon_0=25 +shape=diamond`` .. figure:: ./images/peirce_q_horizontal.png :width: 500 px :align: center :alt: Peirce Quincuncial (Horizontal) - proj-string: ``+proj=peirce_q +lon_0=25 +type=horizontal`` + proj-string: ``+proj=peirce_q +lon_0=25 +shape=horizontal`` .. figure:: ./images/grieger_triptychial.png :width: 500 px @@ -73,16 +73,20 @@ Parameters .. include:: ../options/lon_0.rst -.. option:: +type=square/diamond/horizontal/vertical/nhemisphere/shemisphere +.. option:: +shape=square/diamond/horizontal/vertical/nhemisphere/shemisphere - .. versionadded:: 8.2.1 + .. versionadded:: 9.0 *Defaults to diamond.* - Indicates the type of transformation applied to the southern hemisphere: + .. warning:: This option was wrongly introduced introduced in 8.2.1 with the + ``type`` name, which was inappropriate as it conflicted with + the ``+type=crs`` general hint. + + Indicates the shape of transformation applied to the southern hemisphere: ``square`` and ``diamond`` represent the traditional quincuncial form suggested by Peirce with the southern hemisphere divided into 4 triangles and reflected - outward from the northern hemisphere. The ``square`` type is rotated by 45 + outward from the northern hemisphere. The ``square`` shape is rotated by 45 degrees to produce the conventional square presentation. The origin lies at the centre of the square or diamond. @@ -96,14 +100,14 @@ Parameters .. option:: +scrollx= - For ``horizontal`` type allows a scalar circular scroll of resulting x coordinates + For ``horizontal`` shape allows a scalar circular scroll of resulting x coordinates to shift sections of the projection to the other horizontal side of the map. *Defaults to 0.0. Must be a scale between -1.0 and 1.0.* .. option:: +scrolly= - For ``vertical`` type allows a scalar circular scroll of resulting y coordinates + For ``vertical`` shape allows a scalar circular scroll of resulting y coordinates to shift sections of the projection to the other vertical side of the map. *Defaults to 0.0. Must be a scale between -1.0 and 1.0.* diff --git a/scripts/build_esri_projection_mapping.py b/scripts/build_esri_projection_mapping.py index 30f4ad6c66..32920a599b 100644 --- a/scripts/build_esri_projection_mapping.py +++ b/scripts/build_esri_projection_mapping.py @@ -710,6 +710,24 @@ - False_Northing: EPSG_NAME_PARAMETER_FALSE_NORTHING - Central_Meridian: EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN +- Peirce_Quincuncial: + - WKT2_name: PROJ_WKT2_NAME_METHOD_PEIRCE_QUINCUNCIAL_SQUARE + Params: + - False_Easting: EPSG_NAME_PARAMETER_FALSE_EASTING + - False_Northing: EPSG_NAME_PARAMETER_FALSE_NORTHING + - Central_Meridian: EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN + - Scale_Factor: EPSG_NAME_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN + - Latitude_Of_Origin: EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN + - Option: 0.0 + + - WKT2_name: PROJ_WKT2_NAME_METHOD_PEIRCE_QUINCUNCIAL_DIAMOND + Params: + - False_Easting: EPSG_NAME_PARAMETER_FALSE_EASTING + - False_Northing: EPSG_NAME_PARAMETER_FALSE_NORTHING + - Central_Meridian: EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN + - Scale_Factor: EPSG_NAME_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN + - Latitude_Of_Origin: EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN + - Option: 1.0 # Missing/unclear mappings @@ -748,7 +766,6 @@ # Polar_Stereographic_Variant_C # Quartic_Authalic_Ellipsoidal # Adams_Square_II -# Peirce_Quincuncial """ diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index ca5a3d977b..fc92dbbdfe 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -10209,6 +10209,8 @@ PROJStringParser::Private::buildProjectedCRS(int iStep, } } else if (param->unit_type == UnitOfMeasure::Type::SCALE) { value = 1; + } else if (step.name == "peirce_q" && proj_name == "lat_0") { + value = 90; } PropertyMap propertiesParameter; diff --git a/src/iso19111/operation/conversion.cpp b/src/iso19111/operation/conversion.cpp index 77e703f360..6bccf18281 100644 --- a/src/iso19111/operation/conversion.cpp +++ b/src/iso19111/operation/conversion.cpp @@ -3839,6 +3839,30 @@ void Conversion::_exportToPROJString( formatter->addParam("x_0", falseEasting); formatter->addParam("y_0", falseNorthing); bConversionDone = true; + } else if (ci_equal(methodName, + PROJ_WKT2_NAME_METHOD_PEIRCE_QUINCUNCIAL_SQUARE) || + ci_equal(methodName, + PROJ_WKT2_NAME_METHOD_PEIRCE_QUINCUNCIAL_DIAMOND)) { + const auto &scaleFactor = parameterValueMeasure( + EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN); + if (scaleFactor.unit().type() != common::UnitOfMeasure::Type::UNKNOWN && + std::fabs(scaleFactor.getSIValue() - 1.0) > 1e-10) { + throw io::FormattingException( + "Only scale factor = 1 handled for Peirce Quincuncial"); + } + const double latitudeOfOriginDeg = + parameterValue(EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN) + ->value() + .unit() + .type() != common::UnitOfMeasure::Type::UNKNOWN + ? parameterValueNumeric( + EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, + common::UnitOfMeasure::DEGREE) + : 90.0; + if (std::fabs(latitudeOfOriginDeg - 90.0) > 1e-10) { + throw io::FormattingException("Only latitude of natural origin = " + "90 handled for Peirce Quincuncial"); + } } else if (formatter->convention() == io::PROJStringFormatter::Convention::PROJ_5 && isZUnitConversion) { diff --git a/src/iso19111/operation/esriparammappings.cpp b/src/iso19111/operation/esriparammappings.cpp index 8021b95fff..a6f6c6a120 100644 --- a/src/iso19111/operation/esriparammappings.cpp +++ b/src/iso19111/operation/esriparammappings.cpp @@ -947,6 +947,33 @@ static const ESRIParamMapping paramsESRI_Equal_Earth[] = { EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, "0.0", false}, {nullptr, nullptr, 0, "0.0", false}}; +static const ESRIParamMapping paramsESRI_Peirce_Quincuncial_alt1[] = { + {"False_Easting", EPSG_NAME_PARAMETER_FALSE_EASTING, + EPSG_CODE_PARAMETER_FALSE_EASTING, "0.0", false}, + {"False_Northing", EPSG_NAME_PARAMETER_FALSE_NORTHING, + EPSG_CODE_PARAMETER_FALSE_NORTHING, "0.0", false}, + {"Central_Meridian", EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, + EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, "0.0", false}, + {"Scale_Factor", EPSG_NAME_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN, + EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN, "0.0", false}, + {"Latitude_Of_Origin", EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, + EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, "0.0", false}, + {"Option", nullptr, 0, "0.0", false}, + {nullptr, nullptr, 0, "0.0", false}}; +static const ESRIParamMapping paramsESRI_Peirce_Quincuncial_alt2[] = { + {"False_Easting", EPSG_NAME_PARAMETER_FALSE_EASTING, + EPSG_CODE_PARAMETER_FALSE_EASTING, "0.0", false}, + {"False_Northing", EPSG_NAME_PARAMETER_FALSE_NORTHING, + EPSG_CODE_PARAMETER_FALSE_NORTHING, "0.0", false}, + {"Central_Meridian", EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, + EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, "0.0", false}, + {"Scale_Factor", EPSG_NAME_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN, + EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN, "0.0", false}, + {"Latitude_Of_Origin", EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, + EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, "0.0", false}, + {"Option", nullptr, 0, "1.0", false}, + {nullptr, nullptr, 0, "0.0", false}}; + static const ESRIMethodMapping esriMappings[] = { {"Plate_Carree", EPSG_NAME_METHOD_EQUIDISTANT_CYLINDRICAL, EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL, paramsESRI_Plate_Carree}, @@ -1115,6 +1142,10 @@ static const ESRIMethodMapping esriMappings[] = { EPSG_CODE_METHOD_COLOMBIA_URBAN, paramsESRI_IGAC_Plano_Cartesiano}, {"Equal_Earth", EPSG_NAME_METHOD_EQUAL_EARTH, EPSG_CODE_METHOD_EQUAL_EARTH, paramsESRI_Equal_Earth}, + {"Peirce_Quincuncial", PROJ_WKT2_NAME_METHOD_PEIRCE_QUINCUNCIAL_SQUARE, 0, + paramsESRI_Peirce_Quincuncial_alt1}, + {"Peirce_Quincuncial", PROJ_WKT2_NAME_METHOD_PEIRCE_QUINCUNCIAL_DIAMOND, 0, + paramsESRI_Peirce_Quincuncial_alt2}, }; // --------------------------------------------------------------------------- diff --git a/src/iso19111/operation/parammappings.cpp b/src/iso19111/operation/parammappings.cpp index af1309b3d4..dd94e26706 100644 --- a/src/iso19111/operation/parammappings.cpp +++ b/src/iso19111/operation/parammappings.cpp @@ -793,6 +793,12 @@ static const MethodMapping projectionMethodMappings[] = { {PROJ_WKT2_NAME_METHOD_ROBINSON, 0, "Robinson", "robin", nullptr, paramsLonNatOriginLongitudeCentre}, + {PROJ_WKT2_NAME_METHOD_PEIRCE_QUINCUNCIAL_SQUARE, 0, nullptr, "peirce_q", + "shape=square", paramsNatOriginScale}, + + {PROJ_WKT2_NAME_METHOD_PEIRCE_QUINCUNCIAL_DIAMOND, 0, nullptr, "peirce_q", + "shape=diamond", paramsNatOriginScale}, + {PROJ_WKT2_NAME_METHOD_SINUSOIDAL, 0, "Sinusoidal", "sinu", nullptr, paramsLonNatOriginLongitudeCentre}, diff --git a/src/proj_constants.h b/src/proj_constants.h index 815511fe9e..1da6347423 100644 --- a/src/proj_constants.h +++ b/src/proj_constants.h @@ -248,6 +248,9 @@ #define EPSG_CODE_METHOD_COLOMBIA_URBAN 1052 #define EPSG_NAME_METHOD_COLOMBIA_URBAN "Colombia Urban" +#define PROJ_WKT2_NAME_METHOD_PEIRCE_QUINCUNCIAL_SQUARE "Peirce Quincuncial (Square)" +#define PROJ_WKT2_NAME_METHOD_PEIRCE_QUINCUNCIAL_DIAMOND "Peirce Quincuncial (Diamond)" + /* ------------------------------------------------------------------------ */ /* Projection parameters */ diff --git a/src/projections/adams.cpp b/src/projections/adams.cpp index 4150fff2ee..b0c2849c4f 100644 --- a/src/projections/adams.cpp +++ b/src/projections/adams.cpp @@ -60,7 +60,7 @@ enum projection_type { ADAMS_WS2, }; -enum peirce_type { +enum peirce_shape { PEIRCE_Q_SQUARE, PEIRCE_Q_DIAMOND, PEIRCE_Q_NHEMISPHERE, @@ -71,7 +71,7 @@ enum peirce_type { struct pj_opaque { projection_type mode; - peirce_type pqtype; + peirce_shape pqshape; double scrollx = 0.0; double scrolly = 0.0; }; @@ -143,13 +143,13 @@ static PJ_XY adams_forward(PJ_LP lp, PJ *P) { break; case PEIRCE_Q: { /* lam0 - note that the original Peirce model used a central meridian of around -70deg, but the default within proj is +lon0=0 */ - if (Q->pqtype == PEIRCE_Q_NHEMISPHERE) { + if (Q->pqshape == PEIRCE_Q_NHEMISPHERE) { if( lp.phi < -TOL ) { proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); return proj_coord_error().xy; } } - if (Q->pqtype == PEIRCE_Q_SHEMISPHERE) { + if (Q->pqshape == PEIRCE_Q_SHEMISPHERE) { if( lp.phi > -TOL ) { proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); return proj_coord_error().xy; @@ -211,7 +211,7 @@ static PJ_XY adams_forward(PJ_LP lp, PJ *P) { constexpr double shd = 1.8540746773013719 * 2; /* For square and diamond Quincuncial projections, spin out southern hemisphere to triangular segments of quincunx (before rotation for square)*/ - if( Q->pqtype == PEIRCE_Q_SQUARE || ( Q->pqtype == PEIRCE_Q_DIAMOND )) { + if( Q->pqshape == PEIRCE_Q_SQUARE || ( Q->pqshape == PEIRCE_Q_DIAMOND )) { if (lp.phi < 0.) { /* fold out segments */ if (lp.lam < ( -0.75 * M_PI )) xy.y = shd - xy.y; /* top left segment, shift up and reflect y */ if ( (lp.lam < (-0.25 * M_PI)) && (lp.lam >= ( -0.75 * M_PI ))) xy.x = - shd - xy.x; /* left segment, shift left and reflect x */ @@ -222,20 +222,20 @@ static PJ_XY adams_forward(PJ_LP lp, PJ *P) { } /* For square types rotate xy by 45 deg */ - if( Q->pqtype == PEIRCE_Q_SQUARE ) { + if( Q->pqshape == PEIRCE_Q_SQUARE ) { const double temp = xy.x; xy.x = RSQRT2 * (xy.x - xy.y); xy.y = RSQRT2 * (temp + xy.y); } /* For rectangle Quincuncial projs, spin out southern hemisphere to east (horizontal) or north (vertical) after rotation */ - if( Q->pqtype == PEIRCE_Q_HORIZONTAL ) { + if( Q->pqshape == PEIRCE_Q_HORIZONTAL ) { if (lp.phi < 0.) { xy.x = shd - xy.x; /* reflect x to east */ } xy.x = xy.x - (shd / 2); /* shift everything so origin is in middle of two hemispheres */ } - if( Q->pqtype == PEIRCE_Q_VERTICAL ) { + if( Q->pqshape == PEIRCE_Q_VERTICAL ) { if (lp.phi < 0.) { xy.y = shd - xy.y; /* reflect y to north */ } @@ -243,7 +243,7 @@ static PJ_XY adams_forward(PJ_LP lp, PJ *P) { } //if o_scrollx param present, scroll x - if (!(Q->scrollx == 0.0) && (Q->pqtype == PEIRCE_Q_HORIZONTAL) ) { + if (!(Q->scrollx == 0.0) && (Q->pqshape == PEIRCE_Q_HORIZONTAL) ) { double xscale = 2.0; double xthresh = shd / 2; xy.x = xy.x + (Q->scrollx * (xthresh * 2 * xscale)); /*shift relative to proj width*/ @@ -256,7 +256,7 @@ static PJ_XY adams_forward(PJ_LP lp, PJ *P) { } //if o_scrolly param present, scroll y - if (!(Q->scrolly == 0.0) && (Q->pqtype == PEIRCE_Q_VERTICAL)) { + if (!(Q->scrolly == 0.0) && (Q->pqshape == PEIRCE_Q_VERTICAL)) { double yscale = 2.0; double ythresh = shd / 2; xy.y = xy.y + (Q->scrolly * (ythresh * 2 * yscale)); /*shift relative to proj height*/ @@ -299,6 +299,91 @@ static PJ_LP adams_inverse(PJ_XY xy, PJ *P) return pj_generic_inverse_2d(xy, P, lp); } +static PJ_LP peirce_q_square_inverse(PJ_XY xy, PJ *P) +{ + /* Heuristics based on trial and repeat process */ + PJ_LP lp; + lp.phi = 0; + if( xy.x == 0 && xy.y < 0 ) + { + lp.lam = -M_PI / 4; + if( fabs(xy.y) < 2.622057580396 ) + lp.phi = M_PI / 4; + } + else if( xy.x > 0 && fabs(xy.y) < 1e-7 ) + lp.lam = M_PI / 4; + else if( xy.x < 0 && fabs(xy.y) < 1e-7 ) + { + lp.lam = -3 * M_PI / 4; + lp.phi = M_PI / 2 / 2.622057574224 * xy.x + M_PI / 2; + } + else if( fabs(xy.x) < 1e-7 && xy.y > 0 ) + lp.lam = 3 * M_PI / 4; + else if( xy.x >= 0 && xy.y <= 0 ) + { + lp.lam = 0; + if( xy.x == 0 && xy.y == 0 ) + { + lp.phi = M_PI / 2; + return lp; + } + } + else if( xy.x >= 0 && xy.y >= 0 ) + lp.lam = M_PI / 2; + else if( xy.x <= 0 && xy.y >= 0 ) + { + if( fabs(xy.x) < fabs(xy.y) ) + lp.lam = M_PI * 0.9; + else + lp.lam = -M_PI * 0.9; + } + else /* if( xy.x <= 0 && xy.y <= 0 ) */ + lp.lam = -M_PI / 2; + return pj_generic_inverse_2d(xy, P, lp); +} + +static PJ_LP peirce_q_diamond_inverse(PJ_XY xy, PJ *P) +{ + /* Heuristics based on a trial and repeat process */ + PJ_LP lp; + lp.phi = 0; + if( xy.x >= 0 && xy.y <= 0 ) + { + lp.lam = M_PI / 4; + if( xy.x > 0 && xy.y == 0 ) + { + lp.lam = M_PI / 2; + lp.phi = 0; + } + else if( xy.x == 0 && xy.y == 0 ) + { + lp.lam = 0; + lp.phi = M_PI / 2; + return lp; + } + else if( xy.x == 0 && xy.y < 0 ) + { + lp.lam = 0; + lp.phi = M_PI / 4; + } + } + else if( xy.x >= 0 && xy.y >= 0 ) + lp.lam = 3 * M_PI / 4; + else if( xy.x <= 0 && xy.y >= 0 ) + { + lp.lam = -3 * M_PI / 4; + } + else /* if( xy.x <= 0 && xy.y <= 0 ) */ + lp.lam = -M_PI / 4; + + if( fabs(xy.x) > 1.8540746773013719 + 1e-3 || + fabs(xy.y) > 1.8540746773013719 + 1e-3 ) + { + lp.phi = -M_PI / 4; + } + + return pj_generic_inverse_2d(xy, P, lp); +} static PJ *setup(PJ *P, projection_type mode) { struct pj_opaque *Q = static_cast( @@ -316,25 +401,27 @@ static PJ *setup(PJ *P, projection_type mode) { P->inv = adams_inverse; if( mode == PEIRCE_Q) { - // Quincuncial projections type options: square, diamond, hemisphere, horizontal (rectangle) or vertical (rectangle) - const char* pqtype = pj_param (P->ctx, P->params, "stype").s; + // Quincuncial projections shape options: square, diamond, hemisphere, horizontal (rectangle) or vertical (rectangle) + const char* pqshape = pj_param (P->ctx, P->params, "sshape").s; - if (!pqtype) pqtype = "diamond"; /* default if type value not supplied */ + if (!pqshape) pqshape = "diamond"; /* default if shape value not supplied */ - if (strcmp(pqtype, "square") == 0) { - Q->pqtype = PEIRCE_Q_SQUARE; + if (strcmp(pqshape, "square") == 0) { + Q->pqshape = PEIRCE_Q_SQUARE; + P->inv = peirce_q_square_inverse; } - else if (strcmp(pqtype, "diamond") == 0) { - Q->pqtype = PEIRCE_Q_DIAMOND; + else if (strcmp(pqshape, "diamond") == 0) { + Q->pqshape = PEIRCE_Q_DIAMOND; + P->inv = peirce_q_diamond_inverse; } - else if (strcmp(pqtype, "nhemisphere") == 0) { - Q->pqtype = PEIRCE_Q_NHEMISPHERE; + else if (strcmp(pqshape, "nhemisphere") == 0) { + Q->pqshape = PEIRCE_Q_NHEMISPHERE; } - else if (strcmp(pqtype, "shemisphere") == 0) { - Q->pqtype = PEIRCE_Q_SHEMISPHERE; + else if (strcmp(pqshape, "shemisphere") == 0) { + Q->pqshape = PEIRCE_Q_SHEMISPHERE; } - else if (strcmp(pqtype, "horizontal") == 0) { - Q->pqtype = PEIRCE_Q_HORIZONTAL; + else if (strcmp(pqshape, "horizontal") == 0) { + Q->pqshape = PEIRCE_Q_HORIZONTAL; if (pj_param(P->ctx, P->params, "tscrollx").i) { double scrollx; scrollx = pj_param(P->ctx, P->params, "dscrollx").f; @@ -345,8 +432,8 @@ static PJ *setup(PJ *P, projection_type mode) { Q->scrollx = scrollx; } } - else if (strcmp(pqtype, "vertical") == 0) { - Q->pqtype = PEIRCE_Q_VERTICAL; + else if (strcmp(pqshape, "vertical") == 0) { + Q->pqshape = PEIRCE_Q_VERTICAL; if (pj_param(P->ctx, P->params, "tscrolly").i) { double scrolly; scrolly = pj_param(P->ctx, P->params, "dscrolly").f; @@ -358,7 +445,7 @@ static PJ *setup(PJ *P, projection_type mode) { } } else { - proj_log_error (P, _("peirce_q: invalid value for 'type' parameter")); + proj_log_error (P, _("peirce_q: invalid value for 'shape' parameter")); return pj_default_destructor (P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); } diff --git a/test/gie/peirce_q.gie b/test/gie/peirce_q.gie index 713d2581fd..777ce054bf 100644 --- a/test/gie/peirce_q.gie +++ b/test/gie/peirce_q.gie @@ -14,7 +14,7 @@ ------------------------------------------------------------ ------------------------------------------------------------ -operation +proj=peirce_q +R=6370997 +type=square +operation +proj=peirce_q +R=6370997 +shape=square tolerance 10 mm ------------------------------------------------------------ @@ -138,7 +138,7 @@ accept 170.3972098164 87.6222109978 expect -153172.25 215556.52 ------------------------------------------------------------ - operation +proj=peirce_q +R=6370997 +type=diamond + operation +proj=peirce_q +R=6370997 +shape=diamond tolerance 10 mm ------------------------------------------------------------ @@ -262,7 +262,7 @@ accept 170.3972098164 87.6222109978 expect 44112.34 260730.62 ------------------------------------------------------------ -operation +proj=peirce_q +R=6370997 +type=horizontal +operation +proj=peirce_q +R=6370997 +shape=horizontal tolerance 10 mm ------------------------------------------------------------ @@ -386,7 +386,7 @@ accept 170.3972098164 87.6222109978 expect -11768191.87 260730.62 ------------------------------------------------------------ - operation +proj=peirce_q +R=6370997 +type=horizontal +scrollx=0.75 + operation +proj=peirce_q +R=6370997 +shape=horizontal +scrollx=0.75 tolerance 10 mm ------------------------------------------------------------ @@ -510,7 +510,7 @@ accept 170.3972098164 87.6222109978 expect -23580496.07 260730.62 ------------------------------------------------------------ - operation +proj=peirce_q +R=6370997 +type=vertical + operation +proj=peirce_q +R=6370997 +shape=vertical tolerance 10 mm ------------------------------------------------------------ @@ -634,7 +634,7 @@ accept 170.3972098164 87.6222109978 expect 44112.34 -11551573.59 ------------------------------------------------------------ - operation +proj=peirce_q +R=6370997 +type=vertical +scrolly=-0.25 + operation +proj=peirce_q +R=6370997 +shape=vertical +scrolly=-0.25 tolerance 10 mm ------------------------------------------------------------ @@ -758,7 +758,7 @@ accept 170.3972098164 87.6222109978 expect 44112.34 -23363877.80 ------------------------------------------------------------ - operation +proj=peirce_q +R=6370997 +type=nhemisphere + operation +proj=peirce_q +R=6370997 +shape=nhemisphere tolerance 10 mm ------------------------------------------------------------ @@ -882,7 +882,7 @@ accept 170.3972098164 87.6222109978 expect 44112.34 260730.62 ------------------------------------------------------------ - operation +proj=peirce_q +R=6370997 +type=shemisphere + operation +proj=peirce_q +R=6370997 +shape=shemisphere tolerance 10 mm ------------------------------------------------------------ @@ -1005,4 +1005,330 @@ expect failure errno coord_transfm_outside_projection_domain accept 170.3972098164 87.6222109978 expect failure errno coord_transfm_outside_projection_domain +# Test inverse +------------------------------------------------------------ +operation +proj=peirce_q +shape=square +------------------------------------------------------------ + +#tolerance 1 mm +# has to bump to this for i386 +tolerance 150 mm + +accept 0 90 +expect 0 0 +roundtrip 1 + +accept 0 0 +expect 8361921.234827487729 -8361921.234827487729 +roundtrip 1 + +accept 0 -90 +expect 16723842.303160080686 -16723842.303160080686 +#tolerance 2 mm +roundtrip 1 +#tolerance 1 mm + +accept 0 45 +expect 3725360.212758612353 -3725360.212758612353 +roundtrip 1 + +accept 0 -45 +expect 12998482.090401465073 -12998482.090401465073 +roundtrip 1 + +accept 45 0 +tolerance 200 mm +expect 16723842.564696932212 -0.095041956369 +roundtrip 1 +tolerance 150 mm + +accept -45 0 +expect 0 -16723842.469654975459 +#roundtrip 1 + +accept 90 0 +expect 8361921.329869444482 8361921.329869444482 +roundtrip 1 + +accept -90 0 +expect -8361921.234827487729 -8361921.234827487729 +roundtrip 1 + +accept 135 0 +expect 0.095041956369 16723842.564696932212 +roundtrip 1 + +accept -135 0 +expect -16723842.430287310854 -0.039367665210 +#roundtrip 1 + +accept 179.99 0 +expect -8360808.039828131907 8363034.429826845415 +#roundtrip 1 + +accept -179.99 0 +expect -8363034.429826845415 8360808.039828131907 +#roundtrip 1 + +accept 45 45 +expect 5299570.257319082506 0 +roundtrip 1 + +accept -45 45 +expect 0 -5299570.257319079712 +roundtrip 1 + +accept 90 45 +expect 3725360.212758610491 3725360.212758610491 +roundtrip 1 + +accept -90 45 +expect -3725360.212758613285 -3725360.212758613285 +roundtrip 1 + +accept 135 45 +expect 0 5299570.257319079712 +roundtrip 1 + +accept -135 45 +expect -5299570.257319079712 0 +#roundtrip 1 + +accept 179.99 45 +expect -3724717.456456150394 3726002.863303491380 +roundtrip 1 + +accept -179.99 45 +expect -3726002.863303492777 3724717.456456151791 +roundtrip 1 + +accept 45 -45 +expect 16723842.303160080686 11424272.045840997249 +roundtrip 1 + +accept -45 -45 +expect 11424272.045840999112 -16723842.303160080686 +roundtrip 1 + +accept 90 -45 +expect 12998482.090401468799 12998482.090401468799 +roundtrip 1 + +accept -90 -45 +expect -12998482.090401465073 -12998482.090401465073 +roundtrip 1 + +accept 135 -45 +expect -11424272.045840999112 16723842.303160080686 +roundtrip 1 + +accept -135 -45 +expect -16723842.303160080686 -11424272.045840999112 +roundtrip 1 + +accept 179.99 -45 +expect -12997839.439856586978 12999124.846703927964 +roundtrip 1 + +accept -179.99 -45 +expect -12999124.846703927964 12997839.439856585115 +roundtrip 1 + +accept 45 -89.999 +expect 16723842.303160080686 16723730.983657168224 +#roundtrip 1 + +accept -45 -89.999 +expect 16723730.983657168224 -16723842.303160080686 +#roundtrip 1 + +accept 90 -89.999 +expect 16723763.588384689763 16723763.588384689763 +roundtrip 1 + +accept -90 -89.999 +expect -16723763.588384689763 -16723763.588384689763 +roundtrip 1 + +accept 135 -89.999 +expect -16723730.983657168224 16723842.303160080686 +#roundtrip 1 + +accept -135 -89.999 +expect -16723842.303160080686 -16723730.983657168224 +#roundtrip 1 + +accept 179.99 -89.999 +expect -16723763.588384689763 16723763.588384689763 +#roundtrip 1 + +accept -179.99 -89.999 +expect -16723763.588384689763 16723763.588384689763 +#roundtrip 1 + +# Test inverse +------------------------------------------------------------ +operation +proj=peirce_q +shape=diamond +------------------------------------------------------------ + +#tolerance 1 mm +# has to bump to this for i386 +tolerance 150 mm + +accept 0 90 +expect 0 0 +roundtrip 1 + +accept 0 -90 +#tolerance 10 mm +expect 0 -23651084.600117880851 +roundtrip 1 +#tolerance 1 mm + +accept 0 45 +expect 0 -5268454.937608348206 +roundtrip 1 + +accept 0 -45 +expect 0 -18382629.662509534508 +roundtrip 1 + +accept 45 0 +tolerance 200 mm +expect 11825542.417788611725 -11825542.552198234946 +roundtrip 1 +tolerance 150 mm + +accept -45 0 +expect -11825542.417788611725 -11825542.417788611725 +roundtrip 1 + +accept 90 0 +expect 11825542.552198234946 0.000000000000 +roundtrip 1 + +accept -90 0 +expect -11825542.417788611725 0.000000000000 +#tolerance 20 mm +#roundtrip 1 +#tolerance 1 mm + +accept 135 0 +expect 11825542.552198234946 11825542.417788611725 +roundtrip 1 + +accept -135 0 +expect -11825542.417788611725 11825542.362114325166 +roundtrip 1 + +accept 179.99 0 +expect 1574.295465656175 11825542.417788611725 +#tolerance 200 mm +#roundtrip 1 +#tolerance 1 mm + +accept -179.99 0 +expect -1574.295465656175 11825542.417788611725 +#tolerance 30 mm +#roundtrip 1 +#tolerance 1 mm + +accept 45 45 +expect 3747362.066324858926 -3747362.066324859392 +roundtrip 1 + +accept -45 45 +expect -3747362.066324857529 -3747362.066324857995 +roundtrip 1 + +accept 90 45 +expect 5268454.937608345412 0.000000000000 +#roundtrip 1 + +accept -90 45 +expect -5268454.937608350068 0.000000000000 +#roundtrip 1 + +accept 135 45 +expect 3747362.066324858926 3747362.066324857995 +roundtrip 1 + +accept -135 45 +expect -3747362.066324857529 3747362.066324857529 +roundtrip 1 + +accept 179.99 45 +expect 908.919898338959 5268454.862826444209 +roundtrip 1 + +accept -179.99 45 +expect -908.919898338959 5268454.862826446071 +roundtrip 1 + +accept 45 -45 +expect 19903722.533793020993 -3747362.066324859392 +roundtrip 1 + +accept -45 -45 +expect -3747362.066324857529 -19903722.533793020993 +roundtrip 1 + +accept 90 -45 +expect 18382629.662509534508 0.000000000000 +roundtrip 1 + +accept -90 -45 +expect -18382629.662509530783 0.000000000000 +#tolerance 3 mm +#roundtrip 1 +#tolerance 1 mm + +accept 135 -45 +expect 3747362.066324858926 19903722.533793020993 +roundtrip 1 + +accept -135 -45 +expect -19903722.533793020993 3747362.066324857529 +roundtrip 1 + +accept 179.99 -45 +expect 908.919898338959 18382629.737291436642 +#roundtrip 1 + +accept -179.99 -45 +expect -908.919898338959 18382629.737291432917 +roundtrip 1 + +accept 45 -89.999 +expect 23651005.885342493653 -78.714775386137 +#roundtrip 1 + +accept -45 -89.999 +expect -78.714775386137 -23651005.885342493653 +#roundtrip 1 + +accept 90 -89.999 +expect 23650973.280614964664 0.000000000000 +#roundtrip 1 + +accept -90 -89.999 +expect -23650973.280614964664 0.000000000000 +#roundtrip 1 + +accept 135 -89.999 +expect 78.714775386137 23651005.885342493653 +#roundtrip 1 + +accept -135 -89.999 +expect -23651005.885342493653 78.714775386137 +#roundtrip 1 + +accept 179.99 -89.999 +expect 0.000000000000 23650973.280614964664 +#roundtrip 1 + +accept -179.99 -89.999 +expect 0.000000000000 23650973.280614964664 +#roundtrip 1 + diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index 5ffa461b2d..1c7fc81296 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -6075,6 +6075,38 @@ static const struct { {"False northing", 2}, }}, + {"Peirce_Quincuncial", + {{"False_Easting", 1}, + {"False_Northing", 2}, + {"Central_Meridian", 3}, + {"Scale_Factor", 4}, + {"Latitude_Of_Origin", 5}, + {"Option", 0}}, + "Peirce Quincuncial (Square)", + { + {"Latitude of natural origin", 5}, + {"Longitude of natural origin", 3}, + {"Scale factor at natural origin", 4}, + {"False easting", 1}, + {"False northing", 2}, + }}, + + {"Peirce_Quincuncial", + {{"False_Easting", 1}, + {"False_Northing", 2}, + {"Central_Meridian", 3}, + {"Scale_Factor", 4}, + {"Latitude_Of_Origin", 5}, + {"Option", 1}}, + "Peirce Quincuncial (Diamond)", + { + {"Latitude of natural origin", 5}, + {"Longitude of natural origin", 3}, + {"Scale factor at natural origin", 4}, + {"False easting", 1}, + {"False northing", 2}, + }}, + { "Unknown_Method", {{"False_Easting", 1}, @@ -9931,6 +9963,49 @@ TEST(io, projparse_ortho_spherical_on_sphere) { // --------------------------------------------------------------------------- +TEST(io, projparse_peirce_q) { + std::string input("+proj=peirce_q +shape=square +type=crs"); + auto obj = PROJStringParser().createFromPROJString(input); + auto crs = nn_dynamic_pointer_cast(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ( + crs->exportToPROJString( + PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_4) + .get()), + "+proj=peirce_q +shape=square +lat_0=90 +lon_0=0 +k_0=1 +x_0=0 +y_0=0 " + "+datum=WGS84 +units=m +no_defs +type=crs"); +} + +// --------------------------------------------------------------------------- + +TEST(io, projparse_peirce_q_invalid_lat_0) { + std::string input("+proj=peirce_q +lat_0=0 +shape=square +type=crs"); + auto obj = PROJStringParser().createFromPROJString(input); + auto crs = nn_dynamic_pointer_cast(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_THROW( + crs->exportToPROJString( + PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_4) + .get()), + FormattingException); +} + +// --------------------------------------------------------------------------- + +TEST(io, projparse_peirce_q_invalid_k_0) { + std::string input("+proj=peirce_q +k_0=0.5 +shape=square +type=crs"); + auto obj = PROJStringParser().createFromPROJString(input); + auto crs = nn_dynamic_pointer_cast(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_THROW( + crs->exportToPROJString( + PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_4) + .get()), + FormattingException); +} + +// --------------------------------------------------------------------------- + TEST(io, projparse_axisswap_unitconvert_longlat_proj) { std::string input = "+type=crs +proj=pipeline +step +proj=axisswap +order=2,1 +step "