Skip to content

Commit 3aa97a6

Browse files
committed
proj_factors(): make it work with projected CRS with non-metre unit and/or northing/easting axis order (fixes OSGeo#3824)
1 parent fad9ad9 commit 3aa97a6

File tree

2 files changed

+84
-4
lines changed

2 files changed

+84
-4
lines changed

src/4D_api.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2894,6 +2894,9 @@ PJ_FACTORS proj_factors(PJ *P, PJ_COORD lp) {
28942894
// using the same datum as the one of the projected CRS, and with
28952895
// input coordinates being in longitude, latitude order in radian,
28962896
// to be consistent with the expectations of the lp input parameter.
2897+
// We also need to create a modified projected CRS with a normalized
2898+
// easting/northing axis order in metre, so the resulting operation is
2899+
// just a single step pipeline with no axisswap or unitconvert steps.
28972900

28982901
auto ctx = P->ctx;
28992902
auto geodetic_crs = proj_get_source_crs(ctx, P);
@@ -2902,16 +2905,27 @@ PJ_FACTORS proj_factors(PJ *P, PJ_COORD lp) {
29022905
auto datum_ensemble = proj_crs_get_datum_ensemble(ctx, geodetic_crs);
29032906
auto cs = proj_create_ellipsoidal_2D_cs(
29042907
ctx, PJ_ELLPS2D_LONGITUDE_LATITUDE, "Radian", 1.0);
2905-
auto temp = proj_create_geographic_crs_from_datum(
2908+
auto geogCRSNormalized = proj_create_geographic_crs_from_datum(
29062909
ctx, "unnamed crs", datum ? datum : datum_ensemble, cs);
29072910
proj_destroy(datum);
29082911
proj_destroy(datum_ensemble);
29092912
proj_destroy(cs);
2913+
auto conversion = proj_crs_get_coordoperation(ctx, P);
2914+
auto projCS = proj_create_cartesian_2D_cs(
2915+
ctx, PJ_CART2D_EASTING_NORTHING, "metre", 1.0);
2916+
auto projCRSNormalized = proj_create_projected_crs(
2917+
ctx, nullptr, geodetic_crs, conversion, projCS);
2918+
assert(projCRSNormalized);
29102919
proj_destroy(geodetic_crs);
2911-
auto newOp =
2912-
proj_create_crs_to_crs_from_pj(ctx, temp, P, nullptr, nullptr);
2913-
proj_destroy(temp);
2920+
proj_destroy(conversion);
2921+
proj_destroy(projCS);
2922+
auto newOp = proj_create_crs_to_crs_from_pj(
2923+
ctx, geogCRSNormalized, projCRSNormalized, nullptr, nullptr);
2924+
proj_destroy(geogCRSNormalized);
2925+
proj_destroy(projCRSNormalized);
29142926
assert(newOp);
2927+
// For debugging:
2928+
// printf("%s\n", proj_as_proj_string(ctx, newOp, PJ_PROJ_5, nullptr));
29152929
auto ret = proj_factors(newOp, lp);
29162930
proj_destroy(newOp);
29172931
return ret;

test/unit/gie_self_tests.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,72 @@ TEST(gie, info_functions) {
522522
proj_destroy(P);
523523
}
524524

525+
// Test with a projected CRS with feet unit
526+
{
527+
PJ_COORD c;
528+
c.lp.lam = proj_torad(-110);
529+
c.lp.phi = proj_torad(30);
530+
531+
P = proj_create(PJ_DEFAULT_CTX,
532+
"+proj=tmerc +lat_0=31 +lon_0=-110.166666666667 "
533+
"+k=0.9999 +x_0=213360 +y_0=0 +ellps=GRS80 +units=ft");
534+
factors = proj_factors(P, c);
535+
EXPECT_NEAR(factors.meridional_scale, 0.99990319, 1e-8)
536+
<< factors.meridional_scale;
537+
EXPECT_NEAR(factors.parallel_scale, 0.99990319, 1e-8)
538+
<< factors.parallel_scale;
539+
EXPECT_NEAR(factors.angular_distortion, 0, 1e-7)
540+
<< factors.angular_distortion;
541+
EXPECT_NEAR(factors.meridian_parallel_angle, M_PI_2, 1e-7)
542+
<< factors.meridian_parallel_angle;
543+
proj_destroy(P);
544+
545+
P = proj_create(PJ_DEFAULT_CTX, "EPSG:2222");
546+
547+
const auto factors2 = proj_factors(P, c);
548+
549+
EXPECT_NEAR(factors.meridional_scale, factors2.meridional_scale, 1e-10);
550+
EXPECT_NEAR(factors.parallel_scale, factors2.parallel_scale, 1e-10);
551+
EXPECT_NEAR(factors.angular_distortion, factors2.angular_distortion,
552+
1e-10);
553+
EXPECT_NEAR(factors.meridian_parallel_angle,
554+
factors2.meridian_parallel_angle, 1e-109);
555+
556+
proj_destroy(P);
557+
}
558+
559+
// Test with a projected CRS with northing, easting axis order
560+
{
561+
PJ_COORD c;
562+
c.lp.lam = proj_torad(9);
563+
c.lp.phi = proj_torad(0);
564+
565+
P = proj_create(PJ_DEFAULT_CTX, "+proj=utm +zone=32 +ellps=GRS80");
566+
factors = proj_factors(P, c);
567+
EXPECT_NEAR(factors.meridional_scale, 0.9996, 1e-8)
568+
<< factors.meridional_scale;
569+
EXPECT_NEAR(factors.parallel_scale, 0.9996, 1e-8)
570+
<< factors.parallel_scale;
571+
EXPECT_NEAR(factors.angular_distortion, 0, 1e-7)
572+
<< factors.angular_distortion;
573+
EXPECT_NEAR(factors.meridian_parallel_angle, M_PI_2, 1e-7)
574+
<< factors.meridian_parallel_angle;
575+
proj_destroy(P);
576+
577+
P = proj_create(PJ_DEFAULT_CTX, "EPSG:3044");
578+
579+
const auto factors2 = proj_factors(P, c);
580+
581+
EXPECT_NEAR(factors.meridional_scale, factors2.meridional_scale, 1e-10);
582+
EXPECT_NEAR(factors.parallel_scale, factors2.parallel_scale, 1e-10);
583+
EXPECT_NEAR(factors.angular_distortion, factors2.angular_distortion,
584+
1e-10);
585+
EXPECT_NEAR(factors.meridian_parallel_angle,
586+
factors2.meridian_parallel_angle, 1e-109);
587+
588+
proj_destroy(P);
589+
}
590+
525591
// Test with a geographic CRS --> error
526592
{
527593
P = proj_create(PJ_DEFAULT_CTX, "EPSG:4326");

0 commit comments

Comments
 (0)