[PROJ] Obtaining possible transformation pipelines

Even Rouault even.rouault at spatialys.com
Fri May 24 03:48:00 PDT 2019


> > Storing the PROJ string would also be reasonable (PROJ strings for
> > coordinate operations are lossless for the purpose of transforming
> > coordinates), but the WKT2 2018 representation will give you a more
> > traceable way. That said, I will not guarantee at 100% that the WKT2 2018
> > representation of the whole pipeline can be roundtripped in all
> > situations, so I'd suggest perhaps:
> > 
> > 1) export the coord op as PROJ string
> > 2) export the coord op as WKT2_2018 and instanciate it as a PJ with
> > proj_create()
> > 3) export the PJ of 2) as PROJ string, and compare (string equality) with
> > 1). If they match you can use the WKT2_2018 just fine. Otherwise use the
> > PROJ string of 1) (and report such cases so that we can check if that can
> > be improved)
> 
> Reading this sounds like I'm best off just to use the proj strings and
> be done with it. But I was under the impression that not all CRSes
> could be represented as a proj string? (e.g. EPSG:2218). Is it still
> possible to represent operations from crses like EPS:2218 to proj
> strings with loss?

PROJ strings can accurately represent coordinate operations (even between CRS 
that can't be themselves losslessly represented as PROJ strings).

> I'm seeing something I can't explain here.
> 
> Using this code to list operations from EPSG:3111 to EPSG:7899, I see
> (as expected) 3 results, all using GDA94->GDA2020 as a pivot:
> 
>   PJ *crs1 = proj_create_from_database( pjContext, "EPSG", "3111",
> PJ_CATEGORY_CRS, false, nullptr );
>   PJ *crs2 = proj_create_from_database( pjContext, "EPSG", "7899",
> PJ_CATEGORY_CRS, false, nullptr );
>   PJ_OPERATION_FACTORY_CONTEXT *operationContext =
> proj_create_operation_factory_context( pjContext, nullptr );
>   proj_operation_factory_context_set_grid_availability_use( pjContext,
> operationContext, PROJ_GRID_AVAILABILITY_IGNORED );
>   PJ_OBJ_LIST *ops = proj_create_operations( pjContext, crs1, crs2,
> operationContext );
> 
> Results in:
> 
> Inverse of Vicgrid + GDA94 to GDA2020 (1) + Vicgrid
> +proj=pipeline +step +inv +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36
> +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80 +step +proj=push
> +v_3 +step +proj=cart +ellps=GRS80 +step +proj=helmert +x=0.06155
> +y=-0.01087 +z=-0.04019 +rx=-0.0394924 +ry=-0.0327221 +rz=-0.0328979
> +s=-0.009994 +convention=coordinate_frame +step +inv +proj=cart
> +ellps=GRS80 +step +proj=pop +v_3 +step +proj=lcc +lat_0=-37
> +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000
> +ellps=GRS80
> 
> Inverse of Vicgrid + GDA94 to GDA2020 (2) + Vicgrid
> +proj=pipeline +step +inv +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36
> +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80 +step
> +proj=hgridshift +grids=GDA94_GDA2020_conformal_and_distortion.gsb
> +step +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38
> +x_0=2500000 +y_0=2500000 +ellps=GRS80
> 
> Inverse of Vicgrid + GDA94 to GDA2020 (3) + Vicgrid
> +proj=pipeline +step +inv +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36
> +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80 +step
> +proj=hgridshift +grids=GDA94_GDA2020_conformal.gsb +step +proj=lcc
> +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000
> +ellps=GRS80
> 
> BUT... listing operations directly from GDA94 (EPSG:4283) to GDA2020
> (EPSG:7844) only gives one result:
> 
> GDA94 to GDA2020 (1)
> +proj=pipeline +step +proj=axisswap +order=2,1 +step +proj=unitconvert
> +xy_in=deg +xy_out=rad +step +proj=push +v_3 +step +proj=cart
> +ellps=GRS80 +step +proj=helmert +x=0.06155 +y=-0.01087 +z=-0.04019
> +rx=-0.0394924 +ry=-0.0327221 +rz=-0.0328979 +s=-0.009994
> +convention=coordinate_frame +step +inv +proj=cart +ellps=GRS80 +step
> +proj=pop +v_3 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step
> +proj=axisswap +order=2,1
> 
> Why aren't the options using using the
> GDA94_GDA2020_conformal_and_distoration.gsb and
> GDA94_GDA2020_conformal.gsb listed here?

Because you don't use 

proj_operation_factory_context_set_spatial_criterion(pjContext, 
operationContext,
PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION)

hint: if you had tried with projinfo, it would have advised you about that ;-)

The reason is that for EPSG:3111 to EPSG:7899, the area of use is "small", so
there are 3 possible datum shift operations whose area of use covers the area 
of use of the CRSs.
However, when using GDA94 -> GDA2020, the area of use of those CRS is much 
larger, and there's only the Helmert-based transformation that covers the 
whole area. If you specify PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION (or "--
spatial-test intersects" with projinfo), you ask PROJ to return coordinate 
operations whose area of use intersects at least partially with the one of the 
CRS, but do not necessarily cover them completely.

> 
> And as a follow up question -- when checking the accuracy of the 3
> pipelines given from EPSG:3111 to EPSG:7899,
> proj_coordoperation_get_accuracy reports that the transforms using the
> grid shift files only have an accuracy of 0.05m, vs 0.01m for the
> non-grid pipeline (counter to my expectations). How is this accuracy
> derived?

Directly from the EPSG dataset. Strangely enough, the accuracy advertized for 
those grids is 0.05m , whereas the one of the Helmert base transformation is 
0.01m. If you don't believe me, check EPSG:8048 and EPSG:8446 in
https://www.epsg-registry.org/


-- 
Spatialys - Geospatial professional services
http://www.spatialys.com


More information about the PROJ mailing list