Weeknotes 2026 week 12
What did I do?
End of term this week, so my tutorial interviews kept me quite busy for some of the week. Then Paul-Elliot has returned from his sojourn working on Slipshow, so I had a useful few hours working with him to try and begin the process of handing over the dune odoc PR.
I mentioned last week that the TESSERA reprojection was causing issues with the overlay alignment. Now the original loading of the patches was taking ages, so I switched to zarr to be more efficient. Also, the PCA was slow, so I switched that over to tensorflow.js to use the GPU.
With these two optimisations in place, it was now a lot quicker to see if the misalignment was still there. It seemed likely that the issue was translating between the UTM grid that TESSERA uses and the WGS84 coordinate system that Leaflet.js is using. It was quite quick to whip up a conversion routine (click on 'source' to see the gory details!). With that in place, the overlay now matches up precisely as you can see on the map below. Be patient while it runs, you'll see the overlay on the map in a few seconds!
#require "tessera-zarr-jsoo";;
#require "tessera-viz-jsoo";;
#require "tessera-tfjs";;
#require "js_top_worker-widget-leaflet";;
open Widget_leaflet;;
register ();;
(* Load fzstd (Zstd decompressor) and TensorFlow.js *)
let () =
let open Js_of_ocaml in
let import url : unit = Js.Unsafe.fun_call
(Js.Unsafe.get Js.Unsafe.global (Js.string "importScripts"))
[| Js.Unsafe.inject (Js.string url) |] in
import "https://cdn.jsdelivr.net/npm/fzstd@0.1.1/umd/index.js";
import "https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@4/dist/tf.min.js"Reprojection test
Create the map, fetch embeddings from the Zarr store, run PCA via TensorFlow.js, and overlay — all in one cell so the async pipeline completes before the overlay is drawn:
let status_view text =
let open Widget.View in
Element { tag = "div"; attrs = [Style ("padding", "8px"); Style ("font-family", "monospace")];
children = [Text text] }
let () = Widget.display ~id:"status" ~handlers:[] (status_view "Initialising...")
let map = Leaflet_map.create
~center:(52.30690, -0.03296) ~zoom:14 ~height:"500px"
()
let bbox = Geotessera.{
min_lat = 52.29924; min_lon = -0.05845;
max_lat = 52.31745; max_lon = -0.00755;
}
let downsample mat ~h ~w ~max_pixels =
let n = h * w in
if n <= max_pixels then (mat, h, w)
else
let stride = int_of_float (ceil (sqrt (float_of_int n /. float_of_int max_pixels))) in
let h' = (h + stride - 1) / stride in
let w' = (w + stride - 1) / stride in
let out = Linalg.create_mat ~rows:(h' * w') ~cols:mat.Linalg.cols in
for i = 0 to h' - 1 do
for j = 0 to w' - 1 do
let si = min (i * stride) (h - 1) in
let sj = min (j * stride) (w - 1) in
for f = 0 to mat.Linalg.cols - 1 do
Linalg.mat_set out (i * w' + j) f
(Linalg.mat_get mat (si * w + sj) f)
done
done
done;
(out, h', w')
let () =
Widget.update ~id:"status" (status_view "Opening Zarr store...");
Lwt.async (fun () ->
let open Lwt.Syntax in
let* store = Tessera_zarr_jsoo.open_store () in
let progress msg = Widget.update ~id:"status" (status_view msg) in
let* (mat_full, h_full, w_full, geo_bounds) =
Tessera_zarr.fetch_region ~progress ~store bbox in
Widget.update ~id:"status"
(status_view (Printf.sprintf "Fetched %d×%d. Downsampling..." h_full w_full));
let (mat, h, w) = downsample mat_full ~h:h_full ~w:w_full ~max_pixels:500_000 in
let bounds = Leaflet_map.{
south = geo_bounds.Geotessera.min_lat;
north = geo_bounds.Geotessera.max_lat;
west = geo_bounds.Geotessera.min_lon;
east = geo_bounds.Geotessera.max_lon;
} in
Widget.update ~id:"status"
(status_view (Printf.sprintf "Computing PCA on %d×%d mosaic..." h w));
let proj = Tfjs.pca mat ~n_components:3 in
let img = Viz.pca_to_rgba ~width:w ~height:h proj in
let url = Viz_jsoo.to_data_url img in
Leaflet_map.add_image_overlay map ~url ~bounds ~opacity:0.7 ();
Widget.update ~id:"status"
(status_view (Printf.sprintf
"Done. Input bbox: S%.5f W%.5f N%.5f E%.5f | Overlay bounds: S%.5f W%.5f N%.5f E%.5f"
bbox.min_lat bbox.min_lon bbox.max_lat bbox.max_lon
bounds.south bounds.west bounds.north bounds.east));
Lwt.return_unit)