Phase 2.2: performance optimizations, debug tools, app close fix

This commit is contained in:
2026-01-31 20:31:53 +02:00
parent fb2b55caff
commit 26f8067c94
18 changed files with 1006 additions and 167 deletions

View File

@@ -21,6 +21,7 @@ class SpatialIndex:
self.cell_size = cell_size
self._grid: Dict[Tuple[int, int], List[Building]] = defaultdict(list)
self._buildings: List[Building] = []
self._buildings_by_id: Dict[int, Building] = {}
def _cell_key(self, lat: float, lon: float) -> Tuple[int, int]:
"""Convert lat/lon to grid cell key"""
@@ -30,6 +31,7 @@ class SpatialIndex:
"""Build spatial index from buildings list"""
self._grid.clear()
self._buildings = buildings
self._buildings_by_id = {b.id: b for b in buildings}
for building in buildings:
# Get bounding box of building
@@ -63,9 +65,7 @@ class SpatialIndex:
for b in self._grid.get(key, []):
results.add(b.id)
# Return buildings by id (deduped)
id_set = results
return [b for b in self._buildings if b.id in id_set]
return [self._buildings_by_id[bid] for bid in results if bid in self._buildings_by_id]
def query_line(
self,
@@ -73,29 +73,37 @@ class SpatialIndex:
lat2: float, lon2: float,
buffer_cells: int = 1
) -> List[Building]:
"""Find buildings along a line (for LoS checks)"""
"""Find buildings along a line by walking the actual cells it passes through.
Samples points along the line at cell_size intervals and queries
a buffer around each sample — much faster than bounding-box scan
for long lines.
"""
if not self._grid:
return self._buildings
# Get bounding box cells of the line
min_lat = min(lat1, lat2)
max_lat = max(lat1, lat2)
min_lon = min(lon1, lon2)
max_lon = max(lon1, lon2)
# Walk the line in cell_size steps, collecting unique cells
dlat = lat2 - lat1
dlon = lon2 - lon1
length = max(abs(dlat), abs(dlon))
num_steps = max(1, int(length / self.cell_size) + 1)
min_clat = int(min_lat / self.cell_size) - buffer_cells
max_clat = int(max_lat / self.cell_size) + buffer_cells
min_clon = int(min_lon / self.cell_size) - buffer_cells
max_clon = int(max_lon / self.cell_size) + buffer_cells
visited_cells: set = set()
for s in range(num_steps + 1):
t = s / num_steps
lat = lat1 + t * dlat
lon = lon1 + t * dlon
center = self._cell_key(lat, lon)
for dy in range(-buffer_cells, buffer_cells + 1):
for dx in range(-buffer_cells, buffer_cells + 1):
visited_cells.add((center[0] + dy, center[1] + dx))
results = set()
for clat in range(min_clat, max_clat + 1):
for clon in range(min_clon, max_clon + 1):
for b in self._grid.get((clat, clon), []):
results.add(b.id)
for key in visited_cells:
for b in self._grid.get(key, []):
results.add(b.id)
id_set = results
return [b for b in self._buildings if b.id in id_set]
return [self._buildings_by_id[bid] for bid in results if bid in self._buildings_by_id]
def query_bbox(
self,
@@ -117,8 +125,7 @@ class SpatialIndex:
for b in self._grid.get((clat, clon), []):
results.add(b.id)
id_set = results
return [b for b in self._buildings if b.id in id_set]
return [self._buildings_by_id[bid] for bid in results if bid in self._buildings_by_id]
# Global cache of spatial indices