I have a table of points in my database that I'm showing in OpenLayers in 2 different ways:
- Retrieving via API as GeoJSON and then display in OL using GeoJSON format (white point)
- Retrieving via GeoServer which serves a vector tile layer and displays in OL using MVT format (pink point)
This is the result I get for the same point:
The data in the database is in the CRS NZGD2000 (EPSG:4167) but with a shift in longitude of -160 degrees so a point has the coordinates POINT (10.9909915167 -45.0688542). --- (note the longitude of 10.9909915167 instead of 170.9909915167.)
To deal with the shifted longitudes we have defined a custom CRS (we named it EPSG:1) that basically copies the specs of EPSG:4167 (NZGD2000) and adds 160 to the Prime meridian, like so:
Definition of the CRS in OpenLayers using proj4:
proj4.defs("EPSG:1", "+proj=longlat +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +no_defs +pm=160");
Definition of the CRS in GeoServer (added to the bottom of users_projections/epsg.properties file):
1=GEOGCS["NZGD2000_160_Shift", DATUM["New_Zealand_Geodetic_Datum_2000_160_shift", SPHEROID["GRS 1980",6378137,298.257222101, AUTHORITY["EPSG","7019"]], TOWGS84[0,0,0,0,0,0,0], AUTHORITY["EPSG","6167"]], PRIMEM["Custom",160], UNIT["degree",0.0174532925199433, AUTHORITY["EPSG","9122"]]]
To display the GeoJSON data, we use the following code to tell OpenLayers the data is coming in EPSG:1 and to project it to EPSG:3857, so OL uses the proj4 definition we declared above to do the transformation.
const source = new VectorSource({
features: new GeoJSON().readFeatures(features, { dataProjection: "EPSG:1", featureProjection: "EPSG:3857" }),
});
layer.setSource(source);
To display the data coming from the GeoServer vector tile we use the following piece of code retrieving the data in EPSG:900913, so the transformation is done by GeoServer with the epsg we added to its projections file.
const source = new VectorTileSource({
url: `${geoserverURL}/geoserver/gwc/service/tms/1.0.0/myworkspace:marks@EPSG%3A900913@pbf/{z}/{x}/{-y}.pbf`,
format: new MVT(),
wrapX: true,
tileGrid: createXYZ({ maxZoom: 19 }),
});
These are the FlatCoordinates (in EPSG:3857) we get when clicking on the map and do getGeometry():
projected by OpenLayers (white point): [19034630.105876002, -5632367.691823326]
projected by GeoServer (pink point): [19034630.10393626, -5632367.698059561]
What makes us believe that the point projected by OpenLayers is the right one, is that when we transformed the coordinates of the database POINT (10.9909915167 -45.0688542) from EPSG:1 to EPSG:3857, this was the result 19034630.1059;-5632367.69182 (similar to the OpenLayers one).
For some reason, GeoServer is not transforming it properly!
Just one more evidence, when accessing the GeoServer tile that retrieves that point I get this geometry:
http:///geoserver/gwc/service/tms/1.0.0/myworkspace:marks@EPSG%3A900913@pbf/19/511167/188457.geojson
Not sure if the coordinates showing only 2 decimal places means the pbf that OpenLayers receives also has this number of decimal places...
I wonder what might be causing this offset of millimetres and if there is a good way to debug this issue.
I wish I could see the coordinates with more decimal places.

