from argparse import ArgumentParser import logging from pathlib import Path import urllib.error import urllib.request from flask import Flask, send_file def main(): parser = ArgumentParser() parser.add_argument('--host', default='0.0.0.0', help='Server host (default: 0.0.0.0)') parser.add_argument('--port', type=int, default=8000, help='Server port (default: 8000)') parser.add_argument('--debug', action='store_true', help='Run in debug mode (auto-reload on change)') arguments = parser.parse_args() host: str = arguments.host port: int = arguments.port debug_mode: bool = arguments.debug del arguments app = Flask('map_server', static_folder='public', static_url_path='/') osm_timeout = 3 http_code_ok = 200 if not debug_mode: log = logging.getLogger('werkzeug') log.setLevel(logging.WARNING) @app.route('/') def _index(): return app.send_static_file('index.html') @app.route('///.png') def _tile(zoom: int, x: int, y: int): tile_path = Path('data', str(zoom), str(x), f'{y}.png') if tile_path.exists(): return send_file(tile_path) if not tile_path.parent.exists(): tile_path.parent.mkdir(parents=True, exist_ok=True) request = urllib.request.Request(f'https://tile.openstreetmap.org/{zoom}/{x}/{y}.png') request.add_header('User-Agent', 'Mozilla/5.0 (X11; Linux x86_64; rv:138.0) Gecko/20100101 Firefox/138.0') request.add_header('Accept', 'image/avif,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5') request.add_header('Accept-Language', 'en-US,en;q=0.5') request.add_header('Accept-Encoding', 'gzip, deflate, br, zstd') try: with urllib.request.urlopen(request, timeout=osm_timeout) as response: if response.status != http_code_ok: return (f'tile.openstreetmap.org answer is not OK (status: {response.status}) : {response.read()}', 501) tile_path.write_bytes(response.read()) return send_file(tile_path) except urllib.error.URLError as error: return f'Cannot retrieve tile: {error}', 501 app.run(host=host, port=port, debug=debug_mode) if __name__ == '__main__': main()