mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-13 07:40:59 +00:00
93727084ec
- Packing: a ring of what is in the bags, each bag against its weight limit, the items still missing in their bag's colour, a nudge when departure is two days or less away - World map: the world as dots from the Atlas country shapes (WorldGrid.swift, generated by WidgetPreviews/make-world-grid.py), the visited countries glowing, the latest one ringed, continents as bars and the latest countries with their flags; it needs no trip - Travel group: who comes along as avatars, the latest chat messages as bubbles and the open poll with its votes as bars - The to-dos widget is about to-dos only; the packing ring moved out The app sends the bags, the Atlas countries (an hour's cache) and the chat and poll with the snapshot; all optional, as before.
94 lines
3.2 KiB
Python
94 lines
3.2 KiB
Python
"""Writes client/ios/App/TREKWidgets/WorldGrid.swift: the world as a grid of dots,
|
|
each with the ISO code of the country under it, for the atlas widget.
|
|
|
|
Run from the repo root after the country shapes change:
|
|
python client/ios/WidgetPreviews/make-world-grid.py
|
|
Reads server/assets/atlas/admin0.geojson.gz, the same shapes the Atlas page draws.
|
|
"""
|
|
import gzip
|
|
import json
|
|
|
|
COLS, ROWS = 76, 34
|
|
LAT_TOP, LAT_BOTTOM = 80.0, -58.0 # Antarctica and the far north stay off the map
|
|
SOURCE = 'server/assets/atlas/admin0.geojson.gz'
|
|
TARGET = 'client/ios/App/TREKWidgets/WorldGrid.swift'
|
|
|
|
|
|
def rings(geometry):
|
|
if geometry['type'] == 'Polygon':
|
|
yield geometry['coordinates']
|
|
elif geometry['type'] == 'MultiPolygon':
|
|
yield from geometry['coordinates']
|
|
|
|
|
|
def inside(ring, x, y):
|
|
hit = False
|
|
j = len(ring) - 1
|
|
for i in range(len(ring)):
|
|
xi, yi = ring[i][0], ring[i][1]
|
|
xj, yj = ring[j][0], ring[j][1]
|
|
if (yi > y) != (yj > y) and x < (xj - xi) * (y - yi) / ((yj - yi) or 1e-12) + xi:
|
|
hit = not hit
|
|
j = i
|
|
return hit
|
|
|
|
|
|
def in_polygon(polygon, x, y):
|
|
return inside(polygon[0], x, y) and not any(inside(hole, x, y) for hole in polygon[1:])
|
|
|
|
|
|
features = []
|
|
for feature in json.load(gzip.open(SOURCE))['features']:
|
|
code = feature['properties'].get('ISO_A2') or ''
|
|
if len(code) != 2 or not code.isalpha():
|
|
continue
|
|
for polygon in rings(feature['geometry']):
|
|
xs = [p[0] for p in polygon[0]]
|
|
ys = [p[1] for p in polygon[0]]
|
|
features.append((code, (min(xs), min(ys), max(xs), max(ys)), polygon))
|
|
|
|
# Each cell is sampled at five points, so a narrow country still claims its dot.
|
|
offsets = [(0.5, 0.5), (0.25, 0.25), (0.75, 0.25), (0.25, 0.75), (0.75, 0.75)]
|
|
cell_w = 360.0 / COLS
|
|
cell_h = (LAT_TOP - LAT_BOTTOM) / ROWS
|
|
rows = []
|
|
for r in range(ROWS):
|
|
row = []
|
|
for c in range(COLS):
|
|
votes = {}
|
|
for ox, oy in offsets:
|
|
x = -180 + (c + ox) * cell_w
|
|
y = LAT_TOP - (r + oy) * cell_h
|
|
for code, (x0, y0, x1, y1), polygon in features:
|
|
if x0 <= x <= x1 and y0 <= y <= y1 and in_polygon(polygon, x, y):
|
|
votes[code] = votes.get(code, 0) + 1
|
|
break
|
|
row.append(max(votes, key=votes.get) if votes else '..')
|
|
rows.append(''.join(row))
|
|
|
|
land = sum(len(r) // 2 - r.count('..') for r in rows)
|
|
body = '\n'.join(f' "{r}",' for r in rows)
|
|
swift = f'''// Generated by client/ios/WidgetPreviews/make-world-grid.py from
|
|
// server/assets/atlas/admin0.geojson.gz; do not edit by hand.
|
|
import Foundation
|
|
|
|
/// The world as {COLS} by {ROWS} dots from {LAT_TOP:g}° north to {-LAT_BOTTOM:g}° south,
|
|
/// each the ISO code of the country under it, ".." for sea ({land} land dots).
|
|
enum WorldGrid {{
|
|
static let columns = {COLS}
|
|
static let rows: [String] = [
|
|
{body}
|
|
]
|
|
|
|
/// The country code of a dot, nil for sea.
|
|
static func code(row: Int, column: Int) -> String? {{
|
|
let line = rows[row]
|
|
let start = line.index(line.startIndex, offsetBy: column * 2)
|
|
let code = String(line[start..<line.index(start, offsetBy: 2)])
|
|
return code == ".." ? nil : code
|
|
}}
|
|
}}
|
|
'''
|
|
open(TARGET, 'w', encoding='utf8', newline='\n').write(swift)
|
|
print('land dots', land)
|