#!/usr/bin/env python3 """ Sovereign Satellite Imagery Tile Seeder for Jordan (سيرفر سيادي لتخزين صور الأقمار الصناعية للأردن) Downloads high-resolution photographic satellite imagery tiles for Jordan's National Envelope and stores them into an offline sovereign MBTiles SQLite database. Bounding Box (Jordan): West: 34.8°, South: 29.1°, East: 39.3°, North: 33.4° Zoom Levels: 0 to 15 (Strategic & Tactical military coverage) """ import os import sys import math import sqlite3 import argparse import time import urllib.request import urllib.error from concurrent.futures import ThreadPoolExecutor, as_completed JORDAN_BBOX = { 'min_lon': 34.8, 'min_lat': 29.1, 'max_lon': 39.3, 'max_lat': 33.4 } # Arcgis World Imagery Tile Template SATELLITE_URL_TEMPLATE = "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}" def deg2num(lat_deg, lon_deg, zoom): lat_rad = math.radians(lat_deg) n = 2.0 ** zoom xtile = int((lon_deg + 180.0) / 360.0 * n) ytile = int((1.0 - math.asinh(math.tan(lat_rad)) / math.pi) / 2.0 * n) return (xtile, ytile) def init_mbtiles(db_path, min_zoom, max_zoom): conn = sqlite3.connect(db_path) cur = conn.cursor() cur.execute("PRAGMA synchronous = NORMAL") cur.execute("PRAGMA journal_mode = WAL") cur.execute(""" CREATE TABLE IF NOT EXISTS metadata ( name TEXT PRIMARY KEY, value TEXT ); """) cur.execute(""" CREATE TABLE IF NOT EXISTS tiles ( zoom_level INTEGER, tile_column INTEGER, tile_row INTEGER, tile_data BLOB, PRIMARY KEY (zoom_level, tile_column, tile_row) ); """) meta = [ ('name', 'Jordan Sovereign Satellite Imagery (الأقمار الصناعية السيادية للأردن)'), ('type', 'baselayer'), ('version', '1.0'), ('description', 'High-resolution Sovereign Photographic Satellite Imagery for the Hashemite Kingdom of Jordan'), ('format', 'jpg'), ('bounds', f"{JORDAN_BBOX['min_lon']},{JORDAN_BBOX['min_lat']},{JORDAN_BBOX['max_lon']},{JORDAN_BBOX['max_lat']}"), ('minzoom', str(min_zoom)), ('maxzoom', str(max_zoom)), ('attribution', '© Intaleq Sovereign Earth Observation Engine') ] for k, v in meta: cur.execute("INSERT OR REPLACE INTO metadata (name, value) VALUES (?, ?)", (k, v)) conn.commit() return conn def download_tile(z, x, y, retries=3): url = SATELLITE_URL_TEMPLATE.format(z=z, x=x, y=y) req = urllib.request.Request( url, headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) IntaleqSovereignEngine/1.0', 'Referer': 'https://map-saas.intaleqapp.com/' } ) for attempt in range(retries): try: with urllib.request.urlopen(req, timeout=12) as response: if response.status == 200: data = response.read() return (z, x, y, data, None) except Exception as e: if attempt == retries - 1: return (z, x, y, None, str(e)) time.sleep(0.5 * (attempt + 1)) return (z, x, y, None, "Timeout") def main(): parser = argparse.ArgumentParser(description="Jordan Sovereign Satellite Imagery Tile Seeder") parser.add_argument("--min-zoom", type=int, default=0, help="Minimum zoom level (default: 0)") parser.add_argument("--max-zoom", type=int, default=14, help="Maximum zoom level (default: 14)") parser.add_argument("--output-mbtiles", type=str, default="data/jordan-satellite.mbtiles", help="Output MBTiles path") parser.add_argument("--output-dir", type=str, default="", help="Optional output directory for raw {z}/{x}/{y}.jpg files") parser.add_argument("--workers", type=int, default=16, help="Concurrent worker threads (default: 16)") args = parser.parse_args() os.makedirs(os.path.dirname(os.path.abspath(args.output_mbtiles)), exist_ok=True) if args.output_dir: os.makedirs(args.output_dir, exist_ok=True) print(f"🛰️ Initializing Sovereign Satellite Imagery Seeder for Jordan...") print(f"📍 Geographic Bounding Box: {JORDAN_BBOX}") print(f"🔍 Zoom range: {args.min_zoom} -> {args.max_zoom}") print(f"💾 Target MBTiles: {args.output_mbtiles}") conn = init_mbtiles(args.output_mbtiles, args.min_zoom, args.max_zoom) cursor = conn.cursor() tiles_to_download = [] for z in range(args.min_zoom, args.max_zoom + 1): x1, y2 = deg2num(JORDAN_BBOX['min_lat'], JORDAN_BBOX['min_lon'], z) x2, y1 = deg2num(JORDAN_BBOX['max_lat'], JORDAN_BBOX['max_lon'], z) min_x, max_x = min(x1, x2), max(x1, x2) min_y, max_y = min(y1, y2), max(y1, y2) for x in range(min_x, max_x + 1): for y in range(min_y, max_y + 1): tms_y = (1 << z) - 1 - y cursor.execute("SELECT 1 FROM tiles WHERE zoom_level=? AND tile_column=? AND tile_row=?", (z, x, tms_y)) if not cursor.fetchone(): tiles_to_download.append((z, x, y)) total = len(tiles_to_download) print(f"📦 Total new satellite tiles to fetch: {total:,}") if total == 0: print("✅ All satellite tiles are already cached and present in the sovereign database!") conn.close() return downloaded = 0 errors = 0 batch = [] start_time = time.time() with ThreadPoolExecutor(max_workers=args.workers) as executor: futures = {executor.submit(download_tile, z, x, y): (z, x, y) for z, x, y in tiles_to_download} for future in as_completed(futures): z, x, y, data, err = future.result() if data: tms_y = (1 << z) - 1 - y batch.append((z, x, tms_y, data)) downloaded += 1 if args.output_dir: tile_file_dir = os.path.join(args.output_dir, str(z), str(x)) os.makedirs(tile_file_dir, exist_ok=True) with open(os.path.join(tile_file_dir, f"{y}.jpg"), "wb") as f: f.write(data) else: errors += 1 if len(batch) >= 200: cursor.executemany("INSERT OR REPLACE INTO tiles (zoom_level, tile_column, tile_row, tile_data) VALUES (?, ?, ?, ?)", batch) conn.commit() batch.clear() elapsed = time.time() - start_time speed = downloaded / elapsed if elapsed > 0 else 0 pct = (downloaded + errors) / total * 100 print(f"⏳ Progress: {downloaded:,}/{total:,} ({pct:.1f}%) | Speed: {speed:.1f} tiles/s | Errors: {errors}") if batch: cursor.executemany("INSERT OR REPLACE INTO tiles (zoom_level, tile_column, tile_row, tile_data) VALUES (?, ?, ?, ?)", batch) conn.commit() conn.close() elapsed = time.time() - start_time print(f"\n🎉 Finished! Downloaded: {downloaded:,} satellite tiles in {elapsed:.1f}s. Errors: {errors}.") print(f"🛡️ Sovereign Satellite MBTiles stored at: {args.output_mbtiles}") if __name__ == "__main__": main()