From dc95c2252ee3b5107cebf5eeee44afedac10a67e Mon Sep 17 00:00:00 2001 From: Daniel Barlow Date: Tue, 27 May 2025 23:48:32 +0100 Subject: [PATCH] convert lat/lon to tile x/y and back --- pkgs/maps/assert.fnl | 10 +++++++++- pkgs/maps/tiles.fnl | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/pkgs/maps/assert.fnl b/pkgs/maps/assert.fnl index 06a2a70..d44ee99 100644 --- a/pkgs/maps/assert.fnl +++ b/pkgs/maps/assert.fnl @@ -17,8 +17,16 @@ (.. "\nexpected " ve# "\ngot " va#) )))) +(fn expect-near [actual expected] + `(let [delta# (math.abs (- ,actual ,expected)) + e# ,expected + a# ,actual] + (assert (< delta# 0.00000001) + (.. "expected " e# " to be approximately " a#)))) + + (fn define-tests [& body] (when _G.RUNNING_TESTS `(do ,(unpack body)))) -{ : define-tests : expect : expect= } +{ : define-tests : expect : expect= : expect-near } diff --git a/pkgs/maps/tiles.fnl b/pkgs/maps/tiles.fnl index fe50eda..5325e85 100644 --- a/pkgs/maps/tiles.fnl +++ b/pkgs/maps/tiles.fnl @@ -2,6 +2,7 @@ (local { : dict_to_query } (require :http.util)) (local json (require :json)) +(import-macros { : define-tests : expect : expect= : expect-near } :assert) (local { : view } (require :fennel)) @@ -33,3 +34,38 @@ (print (view headers)) (print (view (json.decode (stream:get_body_as_string)))))) +(fn sinh [x] (/ (- 1 (math.exp (* -2 x))) (* 2 (math.exp (- x))))) + +(expect (< (math.abs (- (sinh 2) 3.626860407847)) 0.001)) + +(fn tile->latlon [xtile ytile zoom] + (let [n (^ 2 zoom) + lon-deg (- (/ (* xtile 360) n) 180.0) + lat-rad (math.atan + (sinh (* math.pi (- 1 (/ (* 2 ytile) n)))) + )] + + (values (/ (* lat-rad 180) math.pi) lon-deg))) + +(let [(lat lon) (tile->latlon 0 0 0)] + (expect= lon -180) + (expect-near lat 85.05112877) + ) + +(let [(lat lon) (tile->latlon 232798 103246 18)] + (expect-near lon 139.699401855) + (expect-near lat 35.6595278648) + ) + +(fn latlon->tile [lat lon zoom] + (let [n (^ 2 zoom) + x (* n (/ (+ lon 180) 360)) + t1 (/ (* lat math.pi) 180) + t (math.log (+ (math.tan t1) (/ 1 (math.cos t1)))) + y (* (- 1 (/ t math.pi)) (/ n 2))] + (values x y))) + +(let [(x y) (latlon->tile 52.1234 -0.53 17)] + (expect= (math.floor x) 65343) + (expect= (math.floor y) 43221)) +