Places map: city base map, cluster gallery, zoom + render polish

Base map / tiles:
- Switch the base map to the mapchart city-label world map; re-derive its
  Web-Mercator georeferencing (MAP_LON0/1, MAP_IMG_MY0/1) by least-squares
  against the pixel-identical graticule export.
- Add a 1.5x LANCZOS supersample LOD and bump tile quality 90->94 so city
  labels stay crisp when zoomed in (tools/png_to_tiles.py).
- Fill the map view background with the ocean colour instead of black.

Navigation / clustering:
- Snap the cluster grid to power-of-two zoom octaves so the bubbles stop
  jittering during a continuous zoom.
- Replace the loose zoom clamps with named MAP_SCALE_MIN/MAX (whole map fills
  the screen .. country/region level).
- Add a toggleable computed lat/lon graticule overlay (TRIANGLE).

Cluster gallery:
- Selecting a bubble now opens all its photos on the same justified grid as
  the timeline/search (a g_gallery_active filter). Members not already paged
  in are appended as hidden g_asset entries (kept out of the main timeline);
  a background metadata worker resolves each one's date/aspect so the grid
  reflows into month sections + true aspect ratios.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011uKJQMYDE9Nrq9ECsU3MxH
This commit is contained in:
Sads
2026-06-18 22:26:59 +02:00
co-authored by Claude Opus 4.8
parent 02f428a76d
commit 5a0eb06d3e
8 changed files with 408 additions and 59 deletions
+13 -9
View File
@@ -1,9 +1,10 @@
#!/usr/bin/env python3
"""Generate a Web-Mercator tile pyramid (LODs) from a MapChart world PNG and
"""Generate a Web-Mercator tile pyramid (LODs) from a world map image and
pack it into one file for the Vita map view.
The source PNG covers the same cropped-Mercator region as before: longitude
-180..180 across its full width, latitude band [MAP_IMG_MY0, MAP_IMG_MY1].
The source image covers a cropped-Mercator region: longitude spans its full
width, latitude band [MAP_IMG_MY0, MAP_IMG_MY1]. The default source carries a
baked-in 10° graticule, against which those bounds were fit (see map.c).
Each LOD level is the source scaled to a width, padded to whole 256px tiles,
and cut up; tiles are JPEG (decoded into pooled textures on device, the safe
path). Everything goes into assets/maptiles.pak:
@@ -18,12 +19,15 @@ offsets are from the start of the JPEG blob.
import sys, struct, io
from PIL import Image
SRC = sys.argv[1] if len(sys.argv) > 1 else '/home/sads/Desktop/MapChart_Map (3).png'
SRC = sys.argv[1] if len(sys.argv) > 1 else '/home/sads/Desktop/world map cities.jpeg'
OUT = sys.argv[2] if len(sys.argv) > 2 else '/home/sads/Code/vitaImmich/assets/maptiles.pak'
TILE = 256
QUALITY = 90
# level widths (px) for the full map; last is ~native. heights follow aspect.
WIDTHS = [512, 1024, 2048, 4096, 7000]
QUALITY = 94
# level widths (px). The last entry (10496 ~= 1.5x the 7000px source) is a
# LANCZOS supersample: it adds no true detail beyond native, but lets a zoomed-in
# view sample crisp pre-upscaled tiles instead of the GPU bilinear-magnifying the
# native level, so city labels read sharper. heights follow aspect.
WIDTHS = [512, 1024, 2048, 4096, 7000, 10496]
src = Image.open(SRC).convert('RGB')
SW, SH = src.size
@@ -31,8 +35,8 @@ print('source', SW, SH, 'aspect', round(SW/SH, 4))
levels = [] # (imgW,imgH,cols,rows,Image-padded)
for w in WIDTHS:
if w > SW:
w = SW
if w > 2 * SW: # allow a modest supersample, but cap runaway upscales
w = 2 * SW
h = round(w * SH / SW)
img = src.resize((w, h), Image.LANCZOS)
cols = (w + TILE - 1) // TILE