feat: add military bases map layer for Western Pacific

Add 18 US military bases (Japan, Guam, South Korea, Hawaii, Diego Garcia)
as a toggleable map layer. Follows the existing data center layer pattern:
static JSON → backend fetcher → slow-tier API → frontend GeoJSON layer.

Includes red circle markers with labels, click popups showing operator
and branch info, and a toggle in the left panel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
adust09
2026-03-16 00:33:35 +09:00
co-authored by Claude Opus 4.6
parent 130287bb49
commit 05de14af9d
11 changed files with 359 additions and 4 deletions
+2 -1
View File
@@ -40,7 +40,7 @@ from services.fetchers.earth_observation import ( # noqa: F401
fetch_earthquakes, fetch_firms_fires, fetch_space_weather, fetch_weather,
)
from services.fetchers.infrastructure import ( # noqa: F401
fetch_internet_outages, fetch_datacenters, fetch_cctv, fetch_kiwisdr,
fetch_internet_outages, fetch_datacenters, fetch_military_bases, fetch_cctv, fetch_kiwisdr,
)
from services.fetchers.geo import ( # noqa: F401
fetch_ships, fetch_airports, find_nearest_airport, cached_airports,
@@ -85,6 +85,7 @@ def update_slow_data():
fetch_frontlines,
fetch_gdelt,
fetch_datacenters,
fetch_military_bases,
]
with concurrent.futures.ThreadPoolExecutor(max_workers=len(slow_funcs)) as executor:
futures = [executor.submit(func) for func in slow_funcs]
+2 -1
View File
@@ -30,7 +30,8 @@ latest_data = {
"space_weather": None,
"internet_outages": [],
"firms_fires": [],
"datacenters": []
"datacenters": [],
"military_bases": []
}
# Per-source freshness timestamps
@@ -143,6 +143,43 @@ def fetch_datacenters():
_mark_fresh("datacenters")
# ---------------------------------------------------------------------------
# Military Bases (static JSON — Western Pacific)
# ---------------------------------------------------------------------------
_MILITARY_BASES_PATH = Path(__file__).parent.parent.parent / "data" / "military_bases.json"
def fetch_military_bases():
"""Load static military base locations (Western Pacific focus)."""
bases = []
try:
if not _MILITARY_BASES_PATH.exists():
logger.warning(f"Military bases file not found: {_MILITARY_BASES_PATH}")
return
raw = json.loads(_MILITARY_BASES_PATH.read_text(encoding="utf-8"))
for entry in raw:
lat = entry.get("lat")
lng = entry.get("lng")
if lat is None or lng is None:
continue
if not (-90 <= lat <= 90 and -180 <= lng <= 180):
continue
bases.append({
"name": entry.get("name", "Unknown"),
"country": entry.get("country", ""),
"operator": entry.get("operator", ""),
"branch": entry.get("branch", ""),
"lat": lat, "lng": lng,
})
logger.info(f"Military bases: {len(bases)} locations loaded")
except Exception as e:
logger.error(f"Error loading military bases: {e}")
with _data_lock:
latest_data["military_bases"] = bases
if bases:
_mark_fresh("military_bases")
# ---------------------------------------------------------------------------
# CCTV Cameras
# ---------------------------------------------------------------------------