Phase 2.2: performance optimizations, debug tools, app close fix
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user