I’m writing a program to use OSM for imagery and while verifying my tile to/from lon/lat conversion code it seems like my y-axis values are sometimes off by 1. I’ve pulled the code from multiple languages off the slippy map tilename wiki page and converted them for my application as well as grabbing the python code from the svn repo and still the xyz → lonlat → xyz don’t seem to match.

Here’s the output from checking zoom levels 0-3. The left is the original x,y,z, the center is the converted lon,lat, and the right is the converted x,y,z from the converted lon,lat.

```
not equal: 0, 1, 2 -> -180.0, 66.51326044311186 -> [0, 0, 2]
not equal: 1, 1, 2 -> -90.0, 66.51326044311186 -> [1, 0, 2]
not equal: 2, 1, 2 -> 0.0, 66.51326044311186 -> [2, 0, 2]
not equal: 3, 1, 2 -> 90.0, 66.51326044311186 -> [3, 0, 2]
not equal: 0, 2, 3 -> -180.0, 66.51326044311186 -> [0, 1, 3]
not equal: 1, 2, 3 -> -135.0, 66.51326044311186 -> [1, 1, 3]
not equal: 2, 2, 3 -> -90.0, 66.51326044311186 -> [2, 1, 3]
not equal: 3, 2, 3 -> -45.0, 66.51326044311186 -> [3, 1, 3]
not equal: 4, 2, 3 -> 0.0, 66.51326044311186 -> [4, 1, 3]
not equal: 5, 2, 3 -> 45.0, 66.51326044311186 -> [5, 1, 3]
not equal: 6, 2, 3 -> 90.0, 66.51326044311186 -> [6, 1, 3]
not equal: 7, 2, 3 -> 135.0, 66.51326044311186 -> [7, 1, 3]
```

And here’s my conversion code I used to generate the above (my MathUtils.sec(rads) returns 1/cos; the rest are built in functions from Java)

```
public static void main(final String[] args) {
for(int z = 3; z < 4; z++) {
for(int x = 0; x < Math.pow(2, z); x++) {
for(int y = 0; y < Math.pow(2, z); y++) {
final double[] lonLat = xy2lonlat(x, y, z);
final int[] tile = tileXYZ(lonLat[0], lonLat[1], z);
if(!Arrays.equals(tile, new int[] { x, y, z })) {
System.err.println("not equal: " + x + ", " + y + ", " + z + " -> " + lonLat[0] + ", " + lonLat[1] + " -> " + Arrays.toString(tile));
}
}
}
}
}
private static int numTiles(final int zoom) {
return (int) Math.pow(2, zoom);
}
private static double[] lonlat2relativeXY(final double lon, final double lat) {
final double x = (lon + 180) / 360;
final double y = (1 - Math.log(Math.tan(Math.toRadians(lat)) + MathUtils.sec(Math.toRadians(lat))) / Math.PI) / 2;
return new double[] { x, y };
}
private static double[] lonlat2xy(final double lon, final double lat, final int z) {
final double n = numTiles(z);
final double[] xy = lonlat2relativeXY(lon, lat);
xy[0] *= n;
xy[1] *= n;
return xy;
}
private static int[] tileXYZ(final double lon, final double lat, final int z) {
final double[] xy = lonlat2xy(lon,lat,z);
return new int[] { (int) xy[0] , (int) xy[1], z };
}
private static double[] xy2lonlat(final int x, final int y, final int z) {
final double n = numTiles(z);
final double relY = y / n;
final double lat = mercatorToLat(Math.PI * (1 - 2 * relY));
final double lon = -180.0 + 360.0 * x / n;
return new double[] { lon, lat };
}
private static double mercatorToLat(final double mercatorY) {
return Math.toDegrees(Math.atan(Math.sinh(mercatorY)));
}
```

Any help or insight would be appreciated; thanks in advance!

Stephen