mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
- City engine: Esri ground, OSM buildings that rise with the scan, road ribbons carrying modelled traffic coloured by congestion, NYC DOT cameras, real ADS-B, MBTA GTFS lines + realtime vehicles, LAX runways with approach strobes, Fort Bragg restricted volumes, Everest terrain with contours. - Indexing keeps accelerating into a 260-capture global sweep while the planet whips a full turn. - The eye opens: aperture blades, sensor array waking, optics calibration, radar sweep, orbiting coordinate ring. - Camera wall with YOLOv8n detections and a city-pulse trace. - Ends on GOD'S EYE VIEW / OMNISCIENCE. - audio/sfx.py: sound design only, built to sit under a music track. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01KqP857pippaUs6konSDfe8
108 lines
4.1 KiB
Python
108 lines
4.1 KiB
Python
"""Esri World Imagery mosaic -> local ENU square, 4096^2 + 2048^2 JPEG.
|
|
|
|
usage: python3 -I tools/city/fetch_imagery.py nyc boston [--zoom 17]
|
|
"""
|
|
import sys, os, math, io, time
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
import numpy as np
|
|
import requests
|
|
from PIL import Image
|
|
from scipy.ndimage import map_coordinates
|
|
from geo import CITIES, OUT, CACHE, Frame
|
|
|
|
URL = 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'
|
|
TS = 256
|
|
N = 4096
|
|
SS = 2 # supersample factor before Lanczos downsample
|
|
|
|
|
|
def merc_px(lat, lon, z):
|
|
s = TS * 2 ** z
|
|
x = (lon + 180.0) / 360.0 * s
|
|
lr = np.radians(lat)
|
|
y = (1.0 - np.log(np.tan(lr) + 1.0 / np.cos(lr)) / math.pi) / 2.0 * s
|
|
return x, y
|
|
|
|
|
|
sess = requests.Session()
|
|
sess.headers['User-Agent'] = 'gods-eye-view-intro/1.0 (film asset build)'
|
|
|
|
|
|
def get_tile(z, x, y):
|
|
d = os.path.join(CACHE, 'tiles', str(z), str(x))
|
|
os.makedirs(d, exist_ok=True)
|
|
p = os.path.join(d, f'{y}.jpg')
|
|
if os.path.exists(p) and os.path.getsize(p) > 0:
|
|
return p, 'cache'
|
|
last = None
|
|
for attempt in range(5):
|
|
try:
|
|
r = sess.get(URL.format(z=z, x=x, y=y), timeout=60)
|
|
if r.status_code == 200 and r.content[:2] in (b'\xff\xd8', b'\x89P'):
|
|
with open(p, 'wb') as f:
|
|
f.write(r.content)
|
|
return p, 'net'
|
|
last = f'HTTP {r.status_code} {r.headers.get("content-type")}'
|
|
except Exception as e: # noqa
|
|
last = str(e)[:120]
|
|
time.sleep(1.5 * (attempt + 1))
|
|
raise RuntimeError(f'tile {z}/{x}/{y} failed: {last}')
|
|
|
|
|
|
def build(slug, z):
|
|
c = CITIES[slug]
|
|
F = Frame(**c)
|
|
h = F.half
|
|
s, w, n, e = F.bbox(h * 1.02)
|
|
x0, y0 = merc_px(n, w, z)
|
|
x1, y1 = merc_px(s, e, z)
|
|
tx0, ty0 = int(x0 // TS), int(y0 // TS)
|
|
tx1, ty1 = int(x1 // TS), int(y1 // TS)
|
|
tiles = [(tx, ty) for ty in range(ty0, ty1 + 1) for tx in range(tx0, tx1 + 1)]
|
|
print(f'== {slug} z{z}: {tx1-tx0+1}x{ty1-ty0+1} = {len(tiles)} tiles')
|
|
with ThreadPoolExecutor(8) as ex:
|
|
res = list(ex.map(lambda t: get_tile(z, t[0], t[1]), tiles))
|
|
print(f' net {sum(1 for r in res if r[1]=="net")} cache {sum(1 for r in res if r[1]=="cache")}')
|
|
W, H = (tx1 - tx0 + 1) * TS, (ty1 - ty0 + 1) * TS
|
|
mos = np.zeros((H, W, 3), np.uint8)
|
|
for (tx, ty), (p, _) in zip(tiles, res):
|
|
im = np.asarray(Image.open(p).convert('RGB'))
|
|
mos[(ty - ty0) * TS:(ty - ty0 + 1) * TS, (tx - tx0) * TS:(tx - tx0 + 1) * TS] = im
|
|
M = N * SS
|
|
t = (np.arange(M) + 0.5) / M
|
|
xs = -h + 2 * h * t # east
|
|
ys = h - 2 * h * t # north, row 0 at top
|
|
lon = F.lon0 + xs / F.kx
|
|
lat = F.lat0 + ys / F.ky
|
|
px, _ = merc_px(np.full_like(lon, F.lat0), lon, z)
|
|
_, py = merc_px(lat, np.full_like(lat, F.lon0), z)
|
|
px = px - tx0 * TS - 0.5 # pixel centers
|
|
py = py - ty0 * TS - 0.5
|
|
PX, PY = np.meshgrid(px, py)
|
|
out = np.empty((M, M, 3), np.uint8)
|
|
for ch in range(3):
|
|
out[..., ch] = np.clip(map_coordinates(mos[..., ch].astype(np.float32), [PY, PX], order=1,
|
|
mode='nearest'), 0, 255).astype(np.uint8)
|
|
del PX, PY
|
|
img = Image.fromarray(out)
|
|
if SS != 1:
|
|
img = img.resize((N, N), Image.LANCZOS)
|
|
d = os.path.join(OUT, slug)
|
|
os.makedirs(d, exist_ok=True)
|
|
img.save(os.path.join(d, 'ground.jpg'), quality=90, optimize=True)
|
|
img.resize((2048, 2048), Image.LANCZOS).save(os.path.join(d, 'ground_2k.jpg'), quality=90, optimize=True)
|
|
img.resize((768, 768), Image.LANCZOS).save(os.path.join(CACHE, f'{slug}_ground_preview.jpg'), quality=85)
|
|
mpp_src = 156543.03392 * math.cos(math.radians(F.lat0)) / 2 ** z
|
|
print(f' wrote ground.jpg {os.path.getsize(os.path.join(d, "ground.jpg"))/1e6:.1f}MB; '
|
|
f'source {mpp_src:.2f} m/px, output {2*h/N:.2f} m/px')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
args = [a for a in sys.argv[1:] if not a.startswith('--')]
|
|
z = 17
|
|
if '--zoom' in sys.argv:
|
|
z = int(sys.argv[sys.argv.index('--zoom') + 1])
|
|
for slug in args or list(CITIES):
|
|
build(slug, z)
|