Known issues and workarounds¶
This page lists every place where the package works around a defect or a gap in PROJ, pyproj, the EPSG dataset, OSDU or ESRI WKT. Each entry gives the symptom, the cause, what the package does, where the code is, and when the workaround can be removed. An upstream issue is linked only where the code cites one. “None referenced” means no upstream report is recorded in this repository, not that the problem is unknown upstream.
Versions checked: PROJ 9.9.0 with EPSG v13.102, pyproj 3.8.0, proj-data 1.25.
Overview¶
Each issue links to its full entry below.
PROJ and pyproj
Issue |
What the package does |
|---|---|
Detects the leftover swap and transposes the values first |
|
Adds the missing axis swap to the pipeline |
|
PROJ reads a Similarity transformation’s ordinates in declared axis order |
Picks the reading that lands inside the area of use |
Rebuilds the bound CRS from its stored definition |
|
Collapses a chain of Helmert steps into one, checked to 1 mm |
|
A parts-per-billion scale is exported unconverted in a bound CRS |
Restates every scale in ppm before embedding |
Detects inverted steps and returns no export for them |
|
Restates the series as PROJ’s |
|
Checks the grid files the compiled pipeline will read |
|
Parses and writes ESRI WKT itself |
|
Reads the codes from pyproj’s sub-objects instead |
|
Builds PROJ and pyproj from source so the pinned database is used |
PROJ data
Issue |
What the package does |
|---|---|
Adds the mapping to a copy of |
OSDU catalogue
Issue |
What the package does |
|---|---|
Assembles each bound CRS with pyproj from its parts |
|
Records the extent under the importing authority |
ESRI WKT
Issue |
What the package does |
|---|---|
Maps both methods to EPSG method 9607 |
Known issues not worked around
Issue |
What to do |
|---|---|
Projected CRSs with south or west axes keep their declared order |
Read |
A Similarity transformation into a northing-first engineering CRS |
State the operation out of the grid instead |
Check points against |
|
Name a deprecated operation by its code |
PROJ and pyproj¶
always_xy leaves an axis swap at a vertical end¶
- Symptom
With
always_xy=True, a pipeline between a geographic 3D CRS and a vertical CRS reads or writes the horizontal position latitude-first.EPSG:4937toEPSG:5776returns(58.5, 5.5, 57.087)for input(5.5, 58.5, 100). With a vertical source, a geoid grid is interpolated at the transposed position, giving a plausible wrong height if that position is inside the grid.- Cause
always_xynormalises each end against that end’s declared horizontal axes. A vertical CRS has none, so theaxisswapbelonging to the operation’s own internal geographic CRS is never stripped.- What the package does
Before running a pipeline from a vertical source, it checks whether the entry step swaps the horizontal pair. If so, it transposes the values first, and adds the swap to the reported
pipelineso that replaying it reproduces the result.- Code
_entry_step,_swaps_horizontal,_order_horizontal_for_pipelineand_report_horizontal_swapingeodetic_engine/geodesy/transformation.py, and_Pipeline.reads_declared_horizontal. The call is inTransformation.transform.- Upstream
None referenced.
- Remove when
PROJ strips the leftover
axisswapitself. The block of functions above can then be deleted outright, together with its call inTransformation.transform. Nothing else depends on it.test_vertical_source_reads_its_position_in_xy_orderintests/geodesy/test_axis_order.pycovers the behaviour. PROJ still leaves the swap, and the example above still returns(58.5, 5.5, 57.087).
always_xy leaves an engineering CRS in declared order¶
- Symptom
PROJ’s
always_xynever normalises an engineering CRS, whatever its axes. A plant grid declared (northing, easting) is read and written in that order while every other end of the pipeline is east-first, so a point given in this package’sxyorder lands hundreds of metres off. Nothing raises; the first point of a symmetric test set (7000, 7000) even agrees.- Cause
PROJ’s
mustAxisOrderBeSwitchedForVisualizationconsiders geographic, projected and derived projected CRSs only.- What the package does
For an engineering end declared northing-first and used by anything other than Cartesian Grid Offsets, it adds the
axisswapPROJ omits to the core of the pipeline and rebuilds it. Cartesian Grid Offsets (EPSG:9656) is left alone because PROJ renders it as an east-first affine and does not adapt an engineering end to it, so thexyvalues already are what the step consumes. A grid with no east/north pair at all (EPSG:5800declares north and west) keeps its declared order, which is whatvalue_axis_orderreports for it. The rebuilt pipeline is what runs and whatpipelinereports, so replaying it reproduces the result from the caller’s own values.- Code
_correct_engineering_axesand_add_engineering_swapsingeodetic_engine/geodesy/transformation.py; the block comment above them is the full account.- Upstream
None referenced.
- Remove when
PROJ normalises engineering CRSs under
always_xy.test_proj_still_leaves_engineering_axes_aloneintests/geodesy/test_engineering_axes.pyfails the day it does, at which point the swap must be removed rather than adapted, or it is applied twice. The test still passes.
PROJ reads a Similarity transformation’s ordinates in declared axis order¶
- Symptom
EPSG:1035(Astra Minas Grid to Campo Inchauspe / Argentina 2) returns(4915661.95, 2590394.63)for the grid point(10000, 20000): northing first, under anxylabel. Chained into the geographic CRS the same point lands in Paraguay, 1 500 km from the site. The same happens to plant grids in registers that state their origin easting-first.- Cause
PROJ takes “Ordinate 1/2 of evaluation point in target CRS” (method 9621) and A0/B0 (method 9624) as the target CRS’s first and second declared axes, then appends an
axisswapto normalise a northing-first target. EPSG’s own data forEPSG:1035is authored the other way: read in declared order the stated origin(2610200.48, 4905282.73)lies in Antarctica; read easting-first it lies at Comodoro Rivadavia, the operation’s area of use. The two conventions coexist in published data.- What the package does
For a Similarity or Affine parametric transformation whose evaluation point is stated in a northing-first projected CRS, it unprojects the point under both readings and keeps the one that lands inside the operation’s area of use (the CRS’s when the operation has none, allowing 1° of slack). When the ordinates turn out east-first, PROJ’s appended
axisswap– which then transposes a result that was alreadyxy– is removed from the pipeline before it is rebuilt. When neither or both readings are plausible, the operation is refused withOperationNotAvailableErrorrather than run under a guess. An evaluation point stated in an engineering CRS cannot be placed on the map, so PROJ’s reading is kept there.- Code
_drop_spurious_ordinate_swapand_ordinate_conventioningeodetic_engine/geodesy/transformation.py.- Upstream
None referenced. The convention question itself (whether EPSG methods 9621/9624 refer to declared axes or to an easting-first frame) is open between EPSG’s guidance and its data.
- Remove when
EPSG and PROJ agree on the convention and
EPSG:1035is restated or read accordingly.test_epsg_1035_states_its_evaluation_point_easting_firstintests/geodesy/test_engineering_axes.pyfails the day EPSG restates it. The origin is still stated easting-first and PROJ still reads it in declared order.
A bound CRS looked up by code comes back unbound¶
- Symptom
CRS.from_authorityon a CRS stored asBOUNDCRSWKT returns the base CRS withis_bound == Falseand no transformation. Naming it would be refused as an ambiguous datum change, even though the CRS defines its operation.- Cause
PROJ discards the
BOUNDCRSwrapper when building a CRS from a code. It still uses the binding for operation selection.- What the package does
Reads the stored
text_definitionback out ofproj.dband rebuilds the bound CRS from it, by code first and then by name. OnlyBOUNDCRSrows are read, once per PROJ data directory. This is the only direct read ofproj.dbat runtime.- Code
_reboundingeodetic_engine/geodesy/crs.py;bound_definitionandbound_definition_by_nameingeodetic_engine/geodesy/database.py.- Upstream
None referenced.
- Remove when
tests/geodesy/test_bound_from_database.pyfails because PROJ keeps the wrapper. It still discards it.
A bound CRS cannot carry a concatenated operation¶
- Symptom
A register’s bound CRS over a chain, such as
EPSG:8047(two Helmerts through ED87), cannot be written as aBOUNDCRS.- Cause
A
BOUNDCRSholds exactly oneABRIDGEDTRANSFORMATION. This is a limit of the WKT2 model, not a PROJ bug.- What the package does
Composes the Helmert steps into one (
collapse_concatenated()), then checks the result against PROJ’s evaluation of the chain over the area of use to 1 mm. A chain that does not compose is skipped with a reason, never approximated. See Bound CRSs.- Code
geodetic_engine/geodesy/utils/helmert.py, used bygeodetic_engine/projdb/bound.py,geodetic_engine/osdudb/bound.pyandgeodetic_engine/persistablereference/reference.py.- Upstream
Not a defect. None referenced.
- Remove when
Permanent, unless the bound CRS model gains support for chains. A
BoundCRSoverEPSG:8047is still refused.
A parts-per-billion scale is exported unconverted in a bound CRS¶
- Symptom
A bound CRS whose transformation gives its scale difference in ppb (most recent ITRF/ETRF transformations) is exported with the literal value. On reading it back,
0.33ppb becomes a scale factor, and positions are kilometres out.- Cause
PROJ converts to the abridged transformation’s fixed units only from the units it expects. A ppb scale is written unchanged.
- What the package does
Restates every scale difference in ppm before embedding (
scale_in_parts_per_million()).- Code
geodetic_engine/geodesy/utils/abridged.py.- Upstream
None referenced.
- Remove when
A
BoundCRSbuilt overEPSG:10586exports and re-reads with the correct scale. The scale is still written as2.25.
Exporting an inverted datum step reverses it silently¶
- Symptom
When PROJ applies a Helmert backwards, it marks the step with an authority of
INVERSE(EPSG). WKT2 and PROJJSON cannot express that, so re-reading the export gives the forward operation: the datum shift with its sign reversed.- Cause
No “apply backwards” flag exists in the export formats. PROJ keeps the forward parameters.
- What the package does
Detects inverted datum steps (
has_inverted_stepingeodetic_engine/geodesy/operation.py). In that caseAppliedOperation.to_wktandOperationCandidate.to_wktreturn None,to_persistable_referencerefuses, and chain collapse refuses. When reading ESRI chains, a reversed Helmert is written out as the forward Helmert of its exact inverse.- Code
geodetic_engine/geodesy/operation.py,geodetic_engine/persistablereference/emit.py,_reversedingeodetic_engine/persistablereference/reference.py.- Upstream
- Remove when
PROJ exports an inverted step in a form that reads back inverted. The issue’s own example,
EPSG:4837, still re-imports from PROJJSON about 176 m off.
EPSG’s reversible polynomial is not implemented¶
- Symptom
EPSG method 9651, “Reversible polynomial of degree 4”, used for example by ED50 to ED87 (1),
EPSG:15753, cannot be built by PROJ.- Cause
PROJ implements no EPSG polynomial method.
- What the package does
When reading an ESRI
Reversible_polynomial_of_degree_4, it states the series as PROJ’shorneroperation. This reproduces EPSG’s worked example.- Code
_polynomial_methodingeodetic_engine/persistablereference/reference.py.- Upstream
- Remove when
PROJ implements method 9651.
CoordinateOperation.from_epsg(15753)still fails with “coordinate operation not found”.
A grid is reported missing when PROJ reads a renamed copy¶
- Symptom
EPSG:15851citesconus.las/conus.los, which PROJ no longer ships. PROJ readsus_noaa_conus.tifinstead, but reports the published names as unavailable. Believing that would refuse a transformation that runs correctly. The opposite failure exists too: PROJ keeps opened grids in memory, so a pipeline compiled whileus_noaa_conus.tifwas present still compiles once it is gone and fails at the first coordinate; and an operation stated outright (an OSDU payload) whose grid is absent fails to build with “Bad step definition”, which says nothing about a grid.- Cause
PROJ’s grid availability is reported for the name the authority published, not for the file
grid_alternativessubstitutes, and compiling a pipeline is not proof that its files are on disk.- What the package does
Reads the
grids=of the compiled pipeline – the complete statement of what will be read – and checks each file on disk, across PROJ’s search path and the user-writable directory. Every file present: the registry’s “missing” names are taken as satisfied. Any file absent: it is reported under PROJ’s own name inMissingGridError, at construction. A stated operation PROJ cannot build for want of a file is refused the same way, naming the grids its definition cites. With PROJ’s network access enabled a file may be fetched on demand, so its absence is then not held against it.- Code
_confirm_installed,_installed_gridand_stated_transformeringeodetic_engine/geodesy/transformation.py.- Upstream
None referenced.
- Remove when
PROJ reports availability for the substituted file and refuses to compile a step whose file is gone.
test_a_superseded_grid_filename_is_not_reported_missingand the three*_names_its_missing_gridtests intests/geodesy/test_rules.pycover both directions.conus.lasandconus.losare still reported unavailable, and a compiled pipeline still outlives its grid file.
PROJ reads no ESRI GEOGTRAN, and writes none¶
- Symptom
PROJ parses ESRI WKT for CRSs, but not the
GEOGTRANelement that states a transformation. It will not write a transformation as ESRI WKT, and for a bound CRS its ESRI WKT output silently drops the datum shift.- What the package does
Parses and writes ESRI WKT itself, translating method and parameter names and units through fixed tables. Anything that cannot be stated exactly is refused (Persistable references (OSDU)).
- Code
geodetic_engine/persistablereference/esriwkt.py,geodetic_engine/persistablereference/methods.py,geodetic_engine/persistablereference/emit.py.- Upstream
Not a defect. None referenced.
- Remove when
Permanent. PROJ still neither reads nor writes a
GEOGTRAN.
PROJJSON drops nested identifiers and unit codes¶
- Symptom
A CRS’s PROJJSON export omits the codes of its datum, ellipsoid and prime meridian, and gives units by name and factor with no code.
- What the package does
When importing OSDU records, reads nested codes from the pyproj sub-objects, and matches units against the
unit_of_measuretable. A unit that cannot be matched is refused, not guessed.- Code
geodetic_engine/osdudb/definition.py(identifier_of,UnitResolver).- Upstream
None referenced.
- Remove when
PROJJSON export includes these identifiers. They are still omitted.
Build configuration¶
-DEMBED_RESOURCE_FILES=OFFWith
proj.dbembedded inlibproj, PROJ can ignore a custom database on disk..devcontainer/install-proj.shbuilds PROJ without embedding.- pyproj built from source
PyPI wheels bundle their own PROJ, which would replace the pinned version silently.
pyproject.tomlsetsno-binary-package = ["pyproj"], and the image setsPROJ_WHEEL=false.tests/test_environment.pychecks this.
PROJ data¶
Missing grid_alternatives mapping for the 1′ EGM2008 geoid¶
- Symptom
EPSG:3859/EPSG:9618(WGS 84 to EGM2008 height, 1′ grid) report their grid as missing even whenus_nga_egm2008_1.tifis installed.- Cause
PROJ’s
proj.dbhas agrid_alternativesrow for the 2.5′ variant only.- What the package does
scripts/patch-grid-alternatives.shadds the rowUnd_min1x1_egm2008_isw=82_WGS84_TideFree → us_nga_egm2008_1.tif(and its.gztwin, plusNNTrans2018B.gtx → no_kv_HREF2018B_NN54_NN2000.tif) to a database under the repository: it runs as the last step ofscripts/build-projdb.shonbuild/proj.db, and.devcontainer/link-local-grids.sh --patched-copywrites a patched copy of the stock database tolocal/proj-data/proj.dbfor the developer to put first onPROJ_DATAby hand. The installedproj.dbis never modified;tests/test_environment.pyfails if it is. The patch is idempotent and leaves an existing row untouched.- Upstream
None referenced.
- Remove when
SELECT * FROM grid_alternatives WHERE original_grid_name='Und_min1x1_egm2008_isw=82_WGS84_TideFree'returns a row in a stock PROJ database. Each entry in the script states why it is still needed. None of the three rows is there yet, and every grid name is still cited by a current operation.
OSDU catalogue¶
No WKT for bound CRSs¶
OSDU bound CRSs are assembled with pyproj from their parts, so the embedded transformation is one this package has checked (Custom database from OSDU).
Bound CRS extent has no code¶
OSDU gives the extent as the intersection of the CRS and transformation extents, with no code. It is recorded under the importing authority, not dropped.
ESRI WKT¶
Bursa_Wolf read as Coordinate Frame¶
ESRI documents the Coordinate Frame and Bursa-Wolf methods as the same in its Projection Engine (ArcSDE 10.0 SDK), so both map to EPSG method 9607.
Known issues not worked around¶
Projected CRSs with south or west axes keep their declared order¶
EPSG:5513 (S-JTSK / Krovak) and EPSG:2065 declare axes pointing south
and west, and the engineering EPSG:5800 declares north and west. PROJ’s
always_xy leaves such axes alone, and so does this package: values go in
and come out in the declared order, while coordinate_order still says
"xy" – which this package defines as easting-first wherever there is an
easting to put first. value_axis_abbreviations reports the order actually
used (('X', 'Y')), so read it for these CRSs.
A Similarity transformation into a northing-first engineering CRS¶
The ordinate-convention check above needs to place the evaluation point on the map, which an engineering CRS cannot do. An operation defined into a northing-first plant grid (rare: EPSG’s are all defined out of the grid) is read as PROJ reads it, ordinates in declared order. State such an operation out of the grid instead, or with the grid declared east-first.
Area of use is not enforced¶
An operation is applied wherever PROJ can compute it. Check points against the
operation’s area_of_use yourself
(Choosing an operation).
Deprecated operations cannot be listed¶
PROJ’s operation search always excludes deprecated EPSG operations, so
available_operations() has no option to include
them. A deprecated operation can still be named explicitly by code.