@mytec: feat: Phase 3.0 Architecture Refactor

Major refactoring of RFCP backend:
- Modular propagation models (8 models)
- SharedMemoryManager for terrain data
- ProcessPoolExecutor parallel processing
- WebSocket progress streaming
- Building filtering pipeline (351k → 15k)
- 82 unit tests

Performance: Standard preset 38s → 5s (7.6x speedup)

Known issue: Detailed preset timeout (fix in 3.1.0)
This commit is contained in:
2026-02-01 23:12:26 +02:00
parent 1dde56705a
commit defa3ad440
71 changed files with 7134 additions and 256 deletions

View File

@@ -1,5 +1,6 @@
import os
import re
import asyncio
import httpx
import json
from typing import List, Optional
@@ -90,7 +91,10 @@ class BuildingsService:
OpenStreetMap buildings via Overpass API with local caching.
"""
OVERPASS_URL = "https://overpass-api.de/api/interpreter"
OVERPASS_URLS = [
"https://overpass-api.de/api/interpreter",
"https://overpass.kumi.systems/api/interpreter",
]
DEFAULT_LEVEL_HEIGHT = 3.0 # meters per floor
DEFAULT_BUILDING_HEIGHT = 9.0 # 3 floors if unknown
@@ -152,7 +156,7 @@ class BuildingsService:
self._memory_cache[bbox_key] = buildings
return buildings
# Fetch from Overpass API
# Fetch from Overpass API with retry
print(f"[Buildings] Fetching from Overpass API...")
query = f"""
@@ -166,17 +170,26 @@ class BuildingsService:
out skel qt;
"""
try:
async with httpx.AsyncClient(timeout=60.0) as client:
response = await client.post(
self.OVERPASS_URL,
data={"data": query}
)
response.raise_for_status()
data = response.json()
except Exception as e:
print(f"[Buildings] Overpass API error: {e}")
return []
data = None
max_retries = 3
for attempt in range(max_retries):
url = self.OVERPASS_URLS[attempt % len(self.OVERPASS_URLS)]
try:
timeout = 60.0 * (attempt + 1) # 60s, 120s, 180s
async with httpx.AsyncClient(timeout=timeout) as client:
response = await client.post(url, data={"data": query})
response.raise_for_status()
data = response.json()
break
except Exception as e:
print(f"[Buildings] Overpass attempt {attempt + 1}/{max_retries} failed ({url}): {e}")
if attempt < max_retries - 1:
wait_time = 2 ** attempt # 1s, 2s
print(f"[Buildings] Retrying in {wait_time}s...")
await asyncio.sleep(wait_time)
else:
print(f"[Buildings] All {max_retries} attempts failed")
return []
buildings = self._parse_overpass_response(data)