This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

tools.realwallpaper: replace mercator by plate carrée world projection

Mercator at 16:9 only showed about +-71 degrees, cutting off northern
Greenland and the Arctic. The new variant world is plate carrée, north up,
centred on the equator and the home longitude, showing -90..90 degrees
stretched vertically to fill the screen. It replaces mercator in the CLI
--projection choices and the server's variant parameter, without an alias
for the old name. lens and plain are unchanged.

author
Hermes
committer
lassulus
date (Oct 17, 2026, 12:30 AM CEST) commit 0d32dfeb parent 8cce4990
+28 -17
+24 -13
tools/realwallpaper/realwallpaper.py
··· 120 120 Projections (--projection): 121 121 - lens: the above plus the magnifying lens around home (default) 122 122 - plain: the above without the lens, no distortion beyond the cylinder 123 - - mercator: the classic map, north up, true Mercator centred on the 124 - equator and the home longitude; the poles are cut off (about +-71 125 - degrees fit 16:9) and there is no lens 123 + - world: the classic map, north up, plate carrée centred on the equator 124 + and the home longitude, the whole globe stretched to fill the screen 125 + (longitude scaled to the width, latitude to the height); no lens 126 126 127 127 The lens is focus + glue + context (Yamamoto et al. 2009; the same idea 128 128 as Snyder's 1987 "magnifying-glass" azimuthal projections): a lens ··· 171 171 self.height = height 172 172 self.m = magnification if projection == "lens" else 1.0 173 173 self.k = 2 * math.pi / width # radians of longitude per px 174 - if projection == "mercator": 174 + self.world = projection == "world" 175 + if self.world: 175 176 self.rot_lat = 0.0 # no rotation: the real equator is the axis 176 - self.a = 1.0 # true Mercator 177 + self.ky = math.pi / height # radians of latitude per px 177 178 else: 178 179 self.rot_lat = self.lat0 # home on the projection's equator 179 180 self.a = solve_cylinder(math.pi * height / width) ··· 235 236 wx = self.px + dx * scale 236 237 wy = self.py + dy * scale 237 238 lon_r = (wx - self.px) * self.k 238 - u = (self.py - wy) * self.k 239 - lat_r = (2 / self.a) * (np.arctan(np.exp(self.a * u)) - math.pi / 4) 239 + if self.world: 240 + lat_r = (self.py - wy) * self.ky 241 + else: 242 + u = (self.py - wy) * self.k 243 + lat_r = (2 / self.a) * (np.arctan(np.exp(self.a * u)) - math.pi / 4) 240 244 lat_r = np.clip(lat_r, -math.pi / 2, math.pi / 2) 241 245 lat, dlon = rotate_from_home(self.rot_lat, lat_r, lon_r) 242 246 lon = (self.lon0 + dlon + math.pi) % (2 * math.pi) - math.pi ··· 246 250 coarse = np.maximum(tangential, radial) 247 251 lens_f = np.where(inside, np.maximum(fine, coarse / 4), 1.0) 248 252 # cylinder: east-west px size R k cos(lat), north-south R k cos(a lat) 249 - base_f = np.maximum(np.cos(lat_r), np.cos(self.a * lat_r) / 4) 250 - q = R_EARTH * self.k * base_f * lens_f 253 + # (world: R ky) 254 + if self.world: 255 + base_q = np.maximum(self.k * np.cos(lat_r), self.ky / 4) 256 + else: 257 + base_q = self.k * np.maximum(np.cos(lat_r), np.cos(self.a * lat_r) / 4) 258 + q = R_EARTH * base_q * lens_f 251 259 return np.degrees(lat), np.degrees(lon), np.maximum(q, 1e-3) 252 260 253 261 def forward(self, lat_deg, lon_deg): 254 262 lat = np.radians(np.asarray(lat_deg, dtype=np.float64)) 255 263 dlon = np.radians(np.asarray(lon_deg, dtype=np.float64)) - self.lon0 256 264 lat_r, lon_r = rotate_to_home(self.rot_lat, lat, dlon) 257 - lat_r = np.clip(lat_r, -math.radians(89.5), math.radians(89.5)) 258 265 wx = self.px + lon_r / self.k 259 - wy = self.py - np.log(np.tan(math.pi / 4 + self.a * lat_r / 2)) / (self.a * self.k) 266 + if self.world: 267 + wy = self.py - lat_r / self.ky 268 + else: 269 + lat_r = np.clip(lat_r, -math.radians(89.5), math.radians(89.5)) 270 + wy = self.py - np.log(np.tan(math.pi / 4 + self.a * lat_r / 2)) / (self.a * self.k) 260 271 dx, dy = wx - self.px, wy - self.py 261 272 r = self.norm(dx, dy) 262 273 s = np.interp(np.minimum(r, 1.0), self._h, self._s) ··· 1330 1341 p.add_argument("--falloff", type=float, default=0.65, 1331 1342 help="where across the lens (0..1) the distortion peaks; higher keeps " 1332 1343 "the middle closer to a plain zoom (default 0.65)") 1333 - p.add_argument("--projection", choices=["lens", "plain", "mercator"], default="lens", 1344 + p.add_argument("--projection", choices=["lens", "plain", "world"], default="lens", 1334 1345 help="lens: centred on home with the zoom lens; plain: centred on home " 1335 - "without distortion; mercator: classic north-up map (default lens)") 1346 + "without distortion; world: classic north-up map (default lens)") 1336 1347 p.add_argument("--marker-url", default=None, 1337 1348 help="JSON with {name: {latitude, longitude}} for krebs markers") 1338 1349 p.add_argument("--time", type=dt.datetime.fromisoformat, default=None,
+1 -1
tools/realwallpaper/server-usage.kdl
··· 1 1 bin "realwallpaper-server" 2 - about "HTTP service rendering realwallpaper on demand: /?location=LAT,LON&variant=lens|plain|mercator&overlay=none|sky|krebs|stars&size=WxH" 2 + about "HTTP service rendering realwallpaper on demand: /?location=LAT,LON&variant=lens|plain|world&overlay=none|sky|krebs|stars&size=WxH" 3 3 4 4 flag "--state-dir" help="Renders and the shared data cache (required)" { 5 5 arg "<dir>"
+2 -2
tools/realwallpaper/server.py
··· 26 26 from dataclasses import dataclass, field 27 27 from pathlib import Path 28 28 29 - VARIANTS = ("lens", "plain", "mercator") 29 + VARIANTS = ("lens", "plain", "world") 30 30 OVERLAYS = { 31 31 "none": "realwallpaper.png", 32 32 "sky": "realwallpaper-marker.png", # sun, moon, planets ··· 43 43 44 44 location degrees, south/west negative, rounded to 0.01 (default Berlin) 45 45 variant lens (zoomed in on location), plain (no distortion), 46 - mercator (classic north-up map) (default lens) 46 + world (classic north-up map) (default lens) 47 47 overlay none, sky (sun, moon, planets), krebs (sky and krebs nodes), 48 48 stars (all of that plus constellations) (default sky) 49 49 size up to 3840x2160 (default 3840x2160)
+1 -1
tools/realwallpaper/usage.kdl
··· 31 31 flag "--falloff" help="Where across the lens (0..1) the distortion peaks; higher keeps the middle closer to a plain zoom (default 0.65)" { 32 32 arg "<position>" 33 33 } 34 - flag "--projection" help="lens: centred on home with the zoom lens; plain: centred on home without distortion; mercator: classic north-up map (default lens)" { 34 + flag "--projection" help="lens: centred on home with the zoom lens; plain: centred on home without distortion; world: classic north-up map (default lens)" { 35 35 arg "<mode>" 36 36 } 37 37 flag "--marker-url" help="JSON {name: {latitude, longitude}} for krebs markers" {