maps::MapView lays the host's tile for each zoom, column and row in Web Mercator, the nearest cut scaled past the zooms it serves, on whole pixels. gpui packs pictures in its atlas without padding and samples them linearly, so each tile draws with a one-pixel rim copied from its own edge, one of its pixels larger each side and clipped, and no seam shows. A drag, the wheel or the arrows pan; Command-wheel, + and - or its buttons zoom about the pointer or the center; the owner keeps the viewport. MapMarker pins take a press and Enter when the map hears them; MapPopup opens toward the map's larger half and stays inside it, closing by its button or Escape. LocationPicker picks the place under a center crosshair; CoordinateDisplay writes degrees or degrees, minutes and seconds with tabular figures and a copy button. The gallery's tiles are Natural Earth's countries, cut z0 to z3 by scripts/tiles.py.
56 lines
2.0 KiB
Python
56 lines
2.0 KiB
Python
"""Writes the gallery's map tiles, z0 to z3, from Natural Earth's 110m countries.
|
|
|
|
Usage: python3 scripts/tiles.py <ne_110m_admin_0_countries.geojson>
|
|
Land is gray with alpha, so one set reads on light and dark maps.
|
|
"""
|
|
|
|
import json
|
|
import math
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from PIL import Image, ImageDraw
|
|
|
|
TILE, GRAIN, ZOOMS = 256, 4, range(4)
|
|
LAND, BORDER = (128, 72), (128, 150)
|
|
OUT = Path(__file__).resolve().parent.parent / "examples/gallery/assets/tiles"
|
|
|
|
|
|
def project(lon, lat):
|
|
lat = max(-85.05112878, min(85.05112878, lat))
|
|
phi = math.radians(lat)
|
|
return (lon + 180) / 360, (1 - math.log(math.tan(phi) + 1 / math.cos(phi)) / math.pi) / 2
|
|
|
|
|
|
def rings(geometry):
|
|
kind, coordinates = geometry["type"], geometry["coordinates"]
|
|
polygons = [coordinates] if kind == "Polygon" else coordinates
|
|
assert kind in ("Polygon", "MultiPolygon"), kind
|
|
return [ring for polygon in polygons for ring in polygon]
|
|
|
|
|
|
def main(source):
|
|
shapes = [rings(feature["geometry"]) for feature in json.load(open(source))["features"]]
|
|
for z in ZOOMS:
|
|
count = 2**z
|
|
side = TILE * GRAIN
|
|
for x in range(count):
|
|
for y in range(count):
|
|
image = Image.new("LA", (side, side), (0, 0))
|
|
draw = ImageDraw.Draw(image)
|
|
for shape in shapes:
|
|
for ring in shape:
|
|
points = [
|
|
((px * count - x) * side, (py * count - y) * side)
|
|
for px, py in (project(lon, lat) for lon, lat in ring)
|
|
]
|
|
draw.polygon(points, fill=LAND, outline=BORDER, width=GRAIN)
|
|
path = OUT / str(z) / str(x) / f"{y}.png"
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
image.resize((TILE, TILE), Image.LANCZOS).save(path, optimize=True)
|
|
print(f"wrote {sum(4**z for z in ZOOMS)} tiles to {OUT}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main(sys.argv[1])
|