@mytec: iter3.4.0 ready for testing
This commit is contained in:
@@ -86,7 +86,12 @@ class TerrainService:
|
||||
return False
|
||||
|
||||
def _load_tile(self, tile_name: str) -> Optional[np.ndarray]:
|
||||
"""Load tile from disk into memory cache"""
|
||||
"""Load tile from disk into memory cache using memory-mapped I/O.
|
||||
|
||||
Uses np.memmap so the OS pages data from disk on demand — near-zero
|
||||
upfront RAM cost per tile (~25 MB savings each vs full load).
|
||||
Falls back to np.frombuffer if memmap fails.
|
||||
"""
|
||||
# Check memory cache first
|
||||
if tile_name in self._tile_cache:
|
||||
return self._tile_cache[tile_name]
|
||||
@@ -97,18 +102,26 @@ class TerrainService:
|
||||
return None
|
||||
|
||||
try:
|
||||
data = tile_path.read_bytes()
|
||||
file_size = tile_path.stat().st_size
|
||||
|
||||
# SRTM HGT format: big-endian signed 16-bit integers
|
||||
if len(data) == 3601 * 3601 * 2:
|
||||
if file_size == 3601 * 3601 * 2:
|
||||
size = 3601 # SRTM1 (30m)
|
||||
elif len(data) == 1201 * 1201 * 2:
|
||||
elif file_size == 1201 * 1201 * 2:
|
||||
size = 1201 # SRTM3 (90m)
|
||||
else:
|
||||
print(f"[Terrain] Unknown tile size: {len(data)} bytes for {tile_name}")
|
||||
print(f"[Terrain] Unknown tile size: {file_size} bytes for {tile_name}")
|
||||
return None
|
||||
|
||||
tile = np.frombuffer(data, dtype='>i2').reshape((size, size))
|
||||
# Memory-mapped loading — OS pages from disk, near-zero RAM
|
||||
try:
|
||||
tile = np.memmap(
|
||||
tile_path, dtype='>i2', mode='r', shape=(size, size),
|
||||
)
|
||||
except Exception:
|
||||
# Fallback: full load into RAM
|
||||
data = tile_path.read_bytes()
|
||||
tile = np.frombuffer(data, dtype='>i2').reshape((size, size))
|
||||
|
||||
# Manage memory cache with LRU eviction
|
||||
if len(self._tile_cache) >= self._max_cache_tiles:
|
||||
@@ -272,6 +285,38 @@ class TerrainService:
|
||||
total = sum(f.stat().st_size for f in self.terrain_path.glob("*.hgt"))
|
||||
return total / (1024 * 1024)
|
||||
|
||||
def evict_disk_cache(self, max_size_mb: float = 2048.0):
|
||||
"""LRU eviction of .hgt files when disk cache exceeds max_size_mb.
|
||||
|
||||
Deletes the oldest-accessed files until total size is under the limit.
|
||||
"""
|
||||
hgt_files = list(self.terrain_path.glob("*.hgt"))
|
||||
if not hgt_files:
|
||||
return
|
||||
|
||||
total = sum(f.stat().st_size for f in hgt_files)
|
||||
if total / (1024 * 1024) <= max_size_mb:
|
||||
return
|
||||
|
||||
# Sort by access time (oldest first)
|
||||
hgt_files.sort(key=lambda f: f.stat().st_atime)
|
||||
|
||||
evicted = 0
|
||||
for f in hgt_files:
|
||||
if total / (1024 * 1024) <= max_size_mb:
|
||||
break
|
||||
fsize = f.stat().st_size
|
||||
# Remove from memory cache if loaded
|
||||
stem = f.stem
|
||||
self._tile_cache.pop(stem, None)
|
||||
f.unlink()
|
||||
total -= fsize
|
||||
evicted += 1
|
||||
|
||||
if evicted:
|
||||
print(f"[Terrain] Evicted {evicted} tiles, "
|
||||
f"cache now {total / (1024 * 1024):.0f} MB")
|
||||
|
||||
@staticmethod
|
||||
def haversine_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
|
||||
"""Calculate distance between two points in meters"""
|
||||
|
||||
Reference in New Issue
Block a user