code wiki / _hdl_build / nx_geo_tiles.nx
nx_geo_tiles.nx source
↩ module page · 52 lines · 2511 B
1// nx_geo_tiles.nx -- LIB: GEO-019 VECTOR/RASTER TILE addressing (sovereign slippy z/x/y scheme).
2//
3// Uses the GEODETIC / plate-carree tiling (the OGC WMTS WGS84 grid): the world lon[-180,180] x
4// lat[-90,90] is split into 2^z x 2^z tiles, LINEARLY in lon/lat -- so the whole scheme is INTEGER:
5// tile_x = floor((lon+180e6) * 2^z / 360e6), tile_y = floor((90e6-lat) * 2^z / 180e6) (y south+)
6// Honest: this is the geodetic grid, NOT Web-Mercator (Mercator needs log/tan -- a later rung).
7//
8// THE EXCEED ANGLE (measured): tile boundaries are exact integer microdegree edges, so tiles tessellate
9// the plane with NO seam gaps or overlaps -- the round-trip "a point's tile's bbox contains the point"
10// holds exactly (for z<=8, where 2^z divides 360e6 and 180e6; larger z floors the edges). Float tile
11// math (Mapbox/Google JS) can produce sub-pixel seam gaps/overlaps at boundaries. license_tier: ORIGINAL
12import "nx_syscalls.nx"
13
14const TILE_LON_SPAN: i64 = 360000000
15const TILE_LAT_SPAN: i64 = 180000000
16const TILE_LON_MIN: i64 = 0 - 180000000
17const TILE_LAT_MAX: i64 = 90000000
18
19func geo_tile_pow2(z: i64) -> i64 { var n: i64 = 1; var t: i64 = 0; while t < z { n = n * 2; t = t + 1 } return n }
20
21// (z, lat, lon) -> tile (tx,ty) written to out2, clamped to [0, 2^z-1].
22func geo_point_tile(z: i64, lat: i64, lon: i64, out2: *i64) -> i64 {
23 let n: i64 = geo_tile_pow2(z)
24 var tx: i64 = (lon - TILE_LON_MIN) * n / TILE_LON_SPAN
25 var ty: i64 = (TILE_LAT_MAX - lat) * n / TILE_LAT_SPAN
26 if tx < 0 { tx = 0 }
27 if tx >= n { tx = n - 1 }
28 if ty < 0 { ty = 0 }
29 if ty >= n { ty = n - 1 }
30 out2[0] = tx
31 out2[1] = ty
32 return 0
33}
34
35// (z, tx, ty) -> bbox [minlat, minlon, maxlat, maxlon] written to out4 (microdegrees).
36func geo_tile_bbox(z: i64, tx: i64, ty: i64, out4: *i64) -> i64 {
37 let n: i64 = geo_tile_pow2(z)
38 out4[0] = TILE_LAT_MAX - (ty + 1) * TILE_LAT_SPAN / n // minlat (south edge)
39 out4[1] = TILE_LON_MIN + tx * TILE_LON_SPAN / n // minlon (west edge)
40 out4[2] = TILE_LAT_MAX - ty * TILE_LAT_SPAN / n // maxlat (north edge)
41 out4[3] = TILE_LON_MIN + (tx + 1) * TILE_LON_SPAN / n // maxlon (east edge)
42 return 0
43}
44
45// 1 iff (lat,lon) lies within the bbox out4 (inclusive). The round-trip containment predicate.
46func geo_bbox_contains(bbox: *i64, lat: i64, lon: i64) -> i64 {
47 if lat < bbox[0] { return 0 }
48 if lat > bbox[2] { return 0 }
49 if lon < bbox[1] { return 0 }
50 if lon > bbox[3] { return 0 }
51 return 1
52}