We have encountered an issue with 2.27.5 / 33.5 where for some of our layers the envelope transformation is no longer working correctly.
Background: we have some data stores that have custom CRSs, which we define in eps.properties. And we have epsg_operations.properties that defines transformations for those.
There are many, many places in GeoServer that do Envelope.transform(targetCRS, lenient), especially from/to native CRS to declared CRS and/or request CRS.
Problem: Now with GS 2.27.5 / GT 33.5 the envelope transformation does not work properly anymore. The following figure compares the result of GS 2.27.3 and GS 2.27.5 when pressing the button “Compute from native bounds“.
In this snapshot we are right in Envelope.transform()near the end of the method where the following block is performed:
CoordinateOperationFactory coordinateOperationFactory = CRS.getCoordinateOperationFactory(lenient);
final CoordinateOperation operation = coordinateOperationFactory.createOperation(crs, targetCRS);
final GeneralBounds transformed = CRS.transform(operation, this);
transformed.setCoordinateReferenceSystem(targetCRS);
This example has an envelope in custom polar stereographic projection and the aim is to tranforsm to WGS84 to obtain the lat/lon bounding box.
The src envelope is this:
"ReferencedEnvelope[-523.4622 : 376.53779999999995, -4658.645 : -3758.6450000000004] DefaultProjectedCRS[EPSG:Radolan projection] AXIS["X", EAST] AXIS["Y", NORTH]".
When starting to look at the screenshot from the bottom, you can see that with GS 2.27.3 the target envelope is something like this:
GS 2.27.3 transformed bounds in WGS84:
"GeneralBounds[(2.071479739549674, 46.95257809439008), (15.720754942504271, 54.905353130084585)]"
That is a bounding box roughly covering Germany. Now looking at GS 2.27.5 the bounding box basically collapses:
GS 2.27.5 transformed bounds in WGS84:
"GeneralBounds[(0.002071479739549676, 0.08995480639774257), (0.015720754942504273, 0.08996376533827592)]"
This is very wrong!
Analysis
The reason seems to be that GT 33.3 and GT 33.5 are choosing different operations to perform the transform.
GT 33.3 chooses
Operation["1000001 ⇨ 4326",
SOURCE["Radolan projection", AUTHORITY["EPSG","1000001"]],
TARGET["WGS 84", AUTHORITY["EPSG","4326"]],
METHOD["Stereographic_North_Pole"]]
using the math transform:
CONCAT_MT[PARAM_MT["Affine",
PARAMETER["num_row", 3],
PARAMETER["num_col", 3],
PARAMETER["elt_0_0", 1000.0],
PARAMETER["elt_1_1", 1000.0]],
INVERSE_MT[PARAM_MT["Stereographic_North_Pole",
PARAMETER["semi_major", 6370040.0],
PARAMETER["semi_minor", 6370040.0],
PARAMETER["central_meridian", 10.0],
PARAMETER["Standard_Parallel_1", 60.0],
PARAMETER["scale_factor", 1.0],
PARAMETER["false_easting", 0.0],
PARAMETER["false_northing", 0.0]]]]
while GT 33.5 picks
Operation["GEOGCS["WGS84(DD)",
DATUM["WGS84",
SPHEROID["WGS84", 6378137.0, 298.257223563]],
PRIMEM["Greenwich", 0.0],
UNIT["degree", 0.017453292519943295],
AXIS["Geodetic longitude", EAST],
AXIS["Geodetic latitude", NORTH],
AUTHORITY["EPSG","4326"]] ⇨ PROJCS["Radolan projection",
GEOGCS["Radolan Coordinate System",
DATUM["Radolan Sphere",
SPHEROID["Radolan Sphere", 6370040.0, 0.0]],
PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
UNIT["degree", 0.017453292519943295],
AXIS["Longitude", EAST],
AXIS["Latitude", NORTH]],
PROJECTION["Stereographic_North_Pole"],
PARAMETER["central_meridian", 10.0],
PARAMETER["Standard_Parallel_1", 60.0],
PARAMETER["scale_factor", 1.0],
PARAMETER["false_easting", 0.0],
PARAMETER["false_northing", 0.0],
UNIT["km", 1000.0],
AXIS["X", EAST],
AXIS["Y", NORTH],
AUTHORITY["EPSG","1000001"]]",
SOURCE["Radolan projection", AUTHORITY["EPSG","1000001"]],
TARGET["WGS84(DD)", AUTHORITY["EPSG","4326"]],
METHOD["Stereographic_North_Pole"]]
using the math transform
CONCAT_MT[INVERSE_MT[PARAM_MT["Stereographic_North_Pole",
PARAMETER["semi_major", 6370040.0],
PARAMETER["semi_minor", 6370040.0],
PARAMETER["central_meridian", 10.0],
PARAMETER["Standard_Parallel_1", 60.0],
PARAMETER["scale_factor", 1.0],
PARAMETER["false_easting", 0.0],
PARAMETER["false_northing", 0.0]]],
PARAM_MT["Affine",
PARAMETER["num_row", 3],
PARAMETER["num_col", 3],
PARAMETER["elt_0_0", 0.001],
PARAMETER["elt_1_1", 0.001]]]
Both operations seem to be doing the inverse of “polar stereographic” but the scaling is flipped and inverted.
While I am not sure why there are two factories that try doing things differently, surely from the result we know that only what GT 33.3 was doing is correct.
it looks like GT 33.3 used the “AuthorityBackedFactory“ directly, now with https://osgeo-org.atlassian.net/browse/GEOT-7844 the “ManyCoordinateOperationFactory” maintains a pool and chooses the first factory that claims to be able to handle the operation.
In this example the pool of factory of the “ManyCoordinateOperationFactory” looks like this for us:
I haven’t been yet able to dig further, but my feeling is that with GT 33.5 a different factory is used and that one somehow doesn’t work well with our custom epsg.properties and epsg_operations.properties.
Is there any way we can fix this, so that our data-stores are working again?
Thanks!
Sören

