geom icon indicating copy to clipboard operation
geom copied to clipboard

Grid: Unpredictable interactions between ToNative and FromNative

Open jchamberlain opened this issue 1 year ago • 2 comments

Expected

Grid.ToNative() should return the top-left point of the given tile:

grid, _ := slippy.NewGrid(3857)
tile := slippy.NewTile(5, 0, 0)
point, _ := grid.ToNative(tile)
fmt.Println(point) // [-2.003750834e+07 2.003750834e+07]

Grid.FromNative() should return the tile corresponding to a top-left point:

grid, _ := slippy.NewGrid(3857)
tile, _ := grid.FromNative(5, geom.Point{-2.003750834e7, 2.003750834e7})
fmt.Println(tile) // &{5 0 0}

FromNative() and ToNative() should be able to reverse each other predictably. That is, FromNative(ToNative(x)) == x.

Problem

The above does not hold for all coordinates. In the following example, 5,3,5 becomes 5,3,4:

grid, _ := NewGrid(3857)
tile1 := NewTile(5, 3, 5)
point, _ := grid.ToNative(tile1)
tile2, _ := grid.FromNative(5, point)
fmt.Println(tile2) // &{5 3 4}

x=3 and y=5 seems to be buggy at multiple zooms (try 6,3,5, 7,3,5, and 8,3,5)

Impact

Whatever is causing this problem seems to make slippy.RangeFamilyAt() unpredictable as well, as it relies on ToNative() and FromNative(). When updating Tegola to use the latest geom (https://github.com/go-spatial/tegola/pull/952), I couldn't get RangeFamilyAt() to work and had to copy in the old version which does not make use of these functions.

Note: it's entirely possible this is related to me using an M1 Mac. I see slightly smaller floats generated on a regular basis, so maybe someone could try this on amd64?

jchamberlain avatar Sep 12 '23 17:09 jchamberlain

Converting to/from an SRID is a lossy action; it is not expected to be a perfect transformation. Usually, you want to reduce the number of transforms you do.

gdey avatar Sep 12 '23 17:09 gdey

That makes sense. In which case, RangeFamilyAt() should be completely reverted as it relies on converting back and forth between zoom ranges, and is thus unpredictable.

I'm also not 100% clear if converting to/from an SRID is what's happening here. My understanding was that it's converting between slippy tile names and Web Mercator units. Shouldn't you be able to go back and forth between those predictably? Put another way, I assume this would only happen if ToNative() is returning a point that FromNative() considers to be outside of the tile. That feels intuitively like something which should be able to be reconciled.

jchamberlain avatar Sep 12 '23 18:09 jchamberlain