code wiki / _hdl_build / nx_geo_webmerc.nx

nx_geo_webmerc.nx source

↩ module page · 52 lines · 2530 B

1// nx_geo_webmerc.nx -- LIB: GEO-019b WEB-MERCATOR (EPSG:3857) slippy z/x/y tiles -- the web de-facto 2// standard (OSM/Google/Mapbox tile numbering). Hardening of GEO-019 (which is the geodetic grid). 3// 4// xtile = floor((lon+180) / 360 * 2^z) (linear, same as geodetic) 5// ytile = floor((1 - asinh(tan(lat)) / pi) / 2 * 2^z) (the Mercator projection) 6// with asinh(tan(lat)) = ln(tan(pi/4 + lat/2)) computed sovereignly via fx.nx CORDIC sin/cos + fx_log2 7// (ln x = log2 x * ln2). All Q16.16 fixed point -> deterministic; no float, no proj4, no API. 8// 9// This is last-mile interop (matching the worldwide web tile standard) per the ecosystem-only law -- 10// the projection itself is sovereign. Valid for |lat| < ~85.05 deg (the Mercator cutoff); beyond that 11// tan -> inf and we clamp. Convention: lat/lon microdegrees; out2 = [xtile, ytile]. license_tier: ORIGINAL 12import "fx.nx" 13import "nx_syscalls.nx" 14const MERC_MAGIC_180000000: i64 = 180000000 15const MERC_MAGIC_360000000: i64 = 360000000 16 17const MERC_LN2: i64 = 45426 // round(ln(2) * 65536) -- converts log2 -> ln in Q16.16 18 19func geo_webmerc_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) microdeg -> Web-Mercator tile [xtile, ytile] in out2, clamped to [0, 2^z-1]. 22func geo_webmerc_tile(z: i64, lat: i64, lon: i64, out2: *i64) -> i64 { 23 let n: i64 = geo_webmerc_pow2(z) 24 25 // x: linear in longitude. 26 var tx: i64 = (lon + MERC_MAGIC_180000000) * n / MERC_MAGIC_360000000 27 if tx < 0 { tx = 0 } 28 if tx >= n { tx = n - 1 } 29 30 // y: Mercator. phi (rad, Q16.16) = lat_microdeg * 2pi / 360e6 ; arg = pi/4 + phi/2. 31 let phi_q: i64 = lat * FX_TWO_PI / MERC_MAGIC_360000000 32 let arg: i64 = FX_PI / 4 + phi_q / 2 33 let s: i64 = fx_sin(arg) 34 let c: i64 = fx_cos(arg) 35 var ty: i64 = 0 36 if c == 0 { 37 ty = 0 // at the pole-ward cutoff; clamp below 38 } else { 39 var tanv: i64 = s * FX_ONE / c // tan in Q16.16 40 if tanv <= 0 { tanv = 1 } // log domain guard 41 let psi: i64 = fx_mul(fx_log2(tanv), MERC_LN2) // ln(tan(arg)) in Q16.16 radians 42 let psi_over_pi: i64 = psi * FX_ONE / FX_PI // Q16.16 43 let frac_y: i64 = (FX_ONE - psi_over_pi) / 2 // Q16.16 in [0,1] 44 ty = frac_y * n / FX_ONE 45 } 46 if ty < 0 { ty = 0 } 47 if ty >= n { ty = n - 1 } 48 49 out2[0] = tx 50 out2[1] = ty 51 return 0 52}