From 5208523f9161f964dcb2552569ed1f311d6687ba Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 5 Jun 2024 17:27:59 +0200 Subject: [PATCH] OGRSpatialReference: workaround bug of PROJ < 9.5 regarding wrong conversion id for UTM south Complement fix of /~https://github.com/OSGeo/PROJ/pull/4166 for PROJ < 9.5 Fixes https://gis.stackexchange.com/questions/482037/what-is-epsg17056 --- autotest/osr/osr_basic.py | 16 +++++ ogr/ogrspatialreference.cpp | 134 ++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) diff --git a/autotest/osr/osr_basic.py b/autotest/osr/osr_basic.py index 0129bcbb5b02..14e13bbd38d8 100755 --- a/autotest/osr/osr_basic.py +++ b/autotest/osr/osr_basic.py @@ -31,6 +31,7 @@ # DEALINGS IN THE SOFTWARE. ############################################################################### +import json import os import subprocess import sys @@ -2478,3 +2479,18 @@ def test_osr_basic_has_point_motion_operation(): srs = osr.SpatialReference() srs.ImportFromEPSG(8255) # NAD83(CSRS)v7 assert srs.HasPointMotionOperation() + + +############################################################################### + + +# Test workaround for /~https://github.com/OSGeo/PROJ/pull/4166 +def test_osr_basic_export_wkt_utm_south(): + + srs = osr.SpatialReference() + srs.SetFromUserInput("+proj=utm +zone=1 +south +datum=WGS84") + + assert 'ID["EPSG",16101]' in srs.ExportToWkt(["FORMAT=WKT2_2019"]) + + j = json.loads(srs.ExportToPROJJSON()) + assert j["conversion"]["id"]["code"] == 16101 diff --git a/ogr/ogrspatialreference.cpp b/ogr/ogrspatialreference.cpp index fad5d53c090c..f269cc81c470 100644 --- a/ogr/ogrspatialreference.cpp +++ b/ogr/ogrspatialreference.cpp @@ -1751,6 +1751,71 @@ OGRErr OGRSpatialReference::exportToWkt(char **ppszResult, } *ppszResult = CPLStrdup(pszWKT); + +#if !(PROJ_AT_LEAST_VERSION(9, 5, 0)) + if (wktFormat == PJ_WKT2_2018) + { + // Works around bug fixed per /~https://github.com/OSGeo/PROJ/pull/4166 + // related to a wrong EPSG code assigned to UTM South conversions + char *pszPtr = strstr(*ppszResult, "CONVERSION[\"UTM zone "); + if (pszPtr) + { + pszPtr += strlen("CONVERSION[\"UTM zone "); + const int nZone = atoi(pszPtr); + while (*pszPtr >= '0' && *pszPtr <= '9') + ++pszPtr; + if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' && + pszPtr[1] == '"' && pszPtr[2] == ',') + { + pszPtr += 3; + int nLevel = 0; + bool bInString = false; + // Find the ID node corresponding to this CONVERSION node + while (*pszPtr) + { + if (bInString) + { + if (*pszPtr == '"' && pszPtr[1] == '"') + { + ++pszPtr; + } + else if (*pszPtr == '"') + { + bInString = false; + } + } + else if (nLevel == 0 && STARTS_WITH_CI(pszPtr, "ID[")) + { + if (STARTS_WITH_CI(pszPtr, CPLSPrintf("ID[\"EPSG\",%d]", + 17000 + nZone))) + { + CPLAssert(pszPtr[11] == '7'); + CPLAssert(pszPtr[12] == '0'); + pszPtr[11] = '6'; + pszPtr[12] = '1'; + } + break; + } + else if (*pszPtr == '"') + { + bInString = true; + } + else if (*pszPtr == '[') + { + ++nLevel; + } + else if (*pszPtr == ']') + { + --nLevel; + } + + ++pszPtr; + } + } + } + } +#endif + proj_destroy(boundCRS); return OGRERR_NONE; } @@ -1901,6 +1966,75 @@ OGRErr OGRSpatialReference::exportToPROJJSON( } *ppszResult = CPLStrdup(pszPROJJSON); + +#if !(PROJ_AT_LEAST_VERSION(9, 5, 0)) + { + // Works around bug fixed per /~https://github.com/OSGeo/PROJ/pull/4166 + // related to a wrong EPSG code assigned to UTM South conversions + char *pszPtr = strstr(*ppszResult, "\"name\": \"UTM zone "); + if (pszPtr) + { + pszPtr += strlen("\"name\": \"UTM zone "); + const int nZone = atoi(pszPtr); + while (*pszPtr >= '0' && *pszPtr <= '9') + ++pszPtr; + if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' && pszPtr[1] == '"') + { + pszPtr += 2; + int nLevel = 0; + bool bInString = false; + // Find the id node corresponding to this conversion node + while (*pszPtr) + { + if (bInString) + { + if (*pszPtr == '\\') + { + ++pszPtr; + } + else if (*pszPtr == '"') + { + bInString = false; + } + } + else if (nLevel == 0 && STARTS_WITH(pszPtr, "\"id\": {")) + { + const char *pszNextEndCurl = strchr(pszPtr, '}'); + const char *pszAuthEPSG = + strstr(pszPtr, "\"authority\": \"EPSG\""); + char *pszCode = strstr( + pszPtr, CPLSPrintf("\"code\": %d", 17000 + nZone)); + if (pszAuthEPSG && pszCode && pszNextEndCurl && + pszNextEndCurl - pszAuthEPSG > 0 && + pszNextEndCurl - pszCode > 0) + { + CPLAssert(pszCode[9] == '7'); + CPLAssert(pszCode[10] == '0'); + pszCode[9] = '6'; + pszCode[10] = '1'; + } + break; + } + else if (*pszPtr == '"') + { + bInString = true; + } + else if (*pszPtr == '{' || *pszPtr == '[') + { + ++nLevel; + } + else if (*pszPtr == '}' || *pszPtr == ']') + { + --nLevel; + } + + ++pszPtr; + } + } + } + } +#endif + return OGRERR_NONE; }