mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-04-23 19:16:06 +02:00
Merge pull request #77 from adust09/feat/jsdf-bases-layer
feat: add 18 JSDF bases to military bases layer
This commit is contained in:
@@ -143,7 +143,150 @@
|
||||
"lat": -7.316,
|
||||
"lng": 72.411
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Naha Air Base",
|
||||
"country": "Japan",
|
||||
"operator": "ASDF 9th Air Wing",
|
||||
"branch": "asdf",
|
||||
"lat": 26.196,
|
||||
"lng": 127.646
|
||||
},
|
||||
{
|
||||
"name": "Misawa Air Base (ASDF)",
|
||||
"country": "Japan",
|
||||
"operator": "ASDF 3rd Air Wing",
|
||||
"branch": "asdf",
|
||||
"lat": 40.706,
|
||||
"lng": 141.371
|
||||
},
|
||||
{
|
||||
"name": "Chitose Air Base",
|
||||
"country": "Japan",
|
||||
"operator": "ASDF 2nd Air Wing",
|
||||
"branch": "asdf",
|
||||
"lat": 42.795,
|
||||
"lng": 141.666
|
||||
},
|
||||
{
|
||||
"name": "Nyutabaru Air Base",
|
||||
"country": "Japan",
|
||||
"operator": "ASDF 5th Air Wing",
|
||||
"branch": "asdf",
|
||||
"lat": 32.084,
|
||||
"lng": 131.451
|
||||
},
|
||||
{
|
||||
"name": "Tsuiki Air Base",
|
||||
"country": "Japan",
|
||||
"operator": "ASDF 8th Air Wing",
|
||||
"branch": "asdf",
|
||||
"lat": 33.685,
|
||||
"lng": 131.041
|
||||
},
|
||||
{
|
||||
"name": "Komatsu Air Base",
|
||||
"country": "Japan",
|
||||
"operator": "ASDF 6th Air Wing",
|
||||
"branch": "asdf",
|
||||
"lat": 36.395,
|
||||
"lng": 136.407
|
||||
},
|
||||
{
|
||||
"name": "Hamamatsu Air Base",
|
||||
"country": "Japan",
|
||||
"operator": "ASDF Air Defense Command",
|
||||
"branch": "asdf",
|
||||
"lat": 34.750,
|
||||
"lng": 137.703
|
||||
},
|
||||
{
|
||||
"name": "Iruma Air Base",
|
||||
"country": "Japan",
|
||||
"operator": "ASDF Central Air Command HQ",
|
||||
"branch": "asdf",
|
||||
"lat": 35.842,
|
||||
"lng": 139.411
|
||||
},
|
||||
{
|
||||
"name": "Yokosuka Naval Base (MSDF)",
|
||||
"country": "Japan",
|
||||
"operator": "MSDF Fleet HQ",
|
||||
"branch": "msdf",
|
||||
"lat": 35.290,
|
||||
"lng": 139.665
|
||||
},
|
||||
{
|
||||
"name": "Kure Naval Base",
|
||||
"country": "Japan",
|
||||
"operator": "MSDF Kure District",
|
||||
"branch": "msdf",
|
||||
"lat": 34.233,
|
||||
"lng": 132.566
|
||||
},
|
||||
{
|
||||
"name": "Sasebo Naval Base (MSDF)",
|
||||
"country": "Japan",
|
||||
"operator": "MSDF Sasebo District",
|
||||
"branch": "msdf",
|
||||
"lat": 33.165,
|
||||
"lng": 129.715
|
||||
},
|
||||
{
|
||||
"name": "Maizuru Naval Base",
|
||||
"country": "Japan",
|
||||
"operator": "MSDF Maizuru District",
|
||||
"branch": "msdf",
|
||||
"lat": 35.469,
|
||||
"lng": 135.383
|
||||
},
|
||||
{
|
||||
"name": "Kanoya Air Base (MSDF)",
|
||||
"country": "Japan",
|
||||
"operator": "MSDF Fleet Air Wing 1",
|
||||
"branch": "msdf",
|
||||
"lat": 31.367,
|
||||
"lng": 130.845
|
||||
},
|
||||
{
|
||||
"name": "Ominato Naval Base",
|
||||
"country": "Japan",
|
||||
"operator": "MSDF Ominato District",
|
||||
"branch": "msdf",
|
||||
"lat": 41.279,
|
||||
"lng": 141.135
|
||||
},
|
||||
{
|
||||
"name": "Camp Naha (GSDF)",
|
||||
"country": "Japan",
|
||||
"operator": "GSDF 15th Brigade",
|
||||
"branch": "gsdf",
|
||||
"lat": 26.334,
|
||||
"lng": 127.769
|
||||
},
|
||||
{
|
||||
"name": "Yonaguni Garrison",
|
||||
"country": "Japan",
|
||||
"operator": "GSDF Yonaguni Coastal Surveillance",
|
||||
"branch": "gsdf",
|
||||
"lat": 24.467,
|
||||
"lng": 122.978
|
||||
},
|
||||
{
|
||||
"name": "Miyako Island Garrison",
|
||||
"country": "Japan",
|
||||
"operator": "GSDF Miyako Garrison",
|
||||
"branch": "gsdf",
|
||||
"lat": 24.791,
|
||||
"lng": 125.281
|
||||
},
|
||||
{
|
||||
"name": "Amami Garrison",
|
||||
"country": "Japan",
|
||||
"operator": "GSDF Amami Garrison",
|
||||
"branch": "gsdf",
|
||||
"lat": 28.381,
|
||||
"lng": 129.494
|
||||
},
|
||||
{
|
||||
"name": "Fuzhou Changle Air Base",
|
||||
"country": "China",
|
||||
|
||||
@@ -35,7 +35,7 @@ class TestMilitaryBasesData:
|
||||
assert -180 <= entry["lng"] <= 180, f"{entry['name']} has invalid lng"
|
||||
|
||||
def test_branch_values_are_known(self):
|
||||
known_branches = {"air_force", "navy", "marines", "army", "missile", "nuclear"}
|
||||
known_branches = {"air_force", "navy", "marines", "army", "gsdf", "msdf", "asdf", "missile", "nuclear"}
|
||||
raw = json.loads(BASES_PATH.read_text(encoding="utf-8"))
|
||||
for entry in raw:
|
||||
assert entry["branch"] in known_branches, f"{entry['name']} has unknown branch: {entry['branch']}"
|
||||
@@ -71,3 +71,19 @@ class TestFetchMilitaryBases:
|
||||
assert "Kadena Air Base" in names
|
||||
assert "Fleet Activities Yokosuka" in names
|
||||
assert "Andersen Air Force Base" in names
|
||||
|
||||
def test_includes_jsdf_bases(self):
|
||||
from services.fetchers.infrastructure import fetch_military_bases
|
||||
fetch_military_bases()
|
||||
with _data_lock:
|
||||
names = {b["name"] for b in latest_data["military_bases"]}
|
||||
assert "Yonaguni Garrison" in names
|
||||
assert "Naha Air Base" in names
|
||||
assert "Kure Naval Base" in names
|
||||
|
||||
def test_colocated_bases_have_separate_entries(self):
|
||||
from services.fetchers.infrastructure import fetch_military_bases
|
||||
fetch_military_bases()
|
||||
with _data_lock:
|
||||
misawa_entries = [b for b in latest_data["military_bases"] if "Misawa" in b["name"]]
|
||||
assert len(misawa_entries) == 2, f"Expected 2 Misawa entries, got {len(misawa_entries)}"
|
||||
|
||||
@@ -2,9 +2,34 @@ import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
buildEarthquakesGeoJSON, buildJammingGeoJSON, buildCctvGeoJSON, buildKiwisdrGeoJSON,
|
||||
buildFirmsGeoJSON, buildInternetOutagesGeoJSON, buildDataCentersGeoJSON,
|
||||
buildGdeltGeoJSON, buildLiveuaGeoJSON, buildFrontlineGeoJSON
|
||||
buildGdeltGeoJSON, buildLiveuaGeoJSON, buildFrontlineGeoJSON, buildMilitaryBasesGeoJSON
|
||||
} from '@/components/map/geoJSONBuilders';
|
||||
import type { Earthquake, GPSJammingZone, FireHotspot, InternetOutage, DataCenter, GDELTIncident, LiveUAmapIncident, CCTVCamera, KiwiSDR } from '@/types/dashboard';
|
||||
import type { Earthquake, GPSJammingZone, FireHotspot, InternetOutage, DataCenter, GDELTIncident, LiveUAmapIncident, CCTVCamera, KiwiSDR, MilitaryBase } from '@/types/dashboard';
|
||||
|
||||
// ─── Military Bases ────────────────────────────────────────────────────────
|
||||
|
||||
describe('buildMilitaryBasesGeoJSON', () => {
|
||||
it('returns null for empty/undefined input', () => {
|
||||
expect(buildMilitaryBasesGeoJSON(undefined)).toBeNull();
|
||||
expect(buildMilitaryBasesGeoJSON([])).toBeNull();
|
||||
});
|
||||
|
||||
it('builds valid Feature for ASDF branch base', () => {
|
||||
const bases: MilitaryBase[] = [
|
||||
{ name: 'Naha Air Base', country: 'Japan', operator: 'ASDF 9th Air Wing', branch: 'asdf', lat: 26.196, lng: 127.646 },
|
||||
];
|
||||
const result = buildMilitaryBasesGeoJSON(bases);
|
||||
expect(result).not.toBeNull();
|
||||
expect(result!.type).toBe('FeatureCollection');
|
||||
expect(result!.features).toHaveLength(1);
|
||||
|
||||
const f = result!.features[0];
|
||||
expect(f.geometry).toEqual({ type: 'Point', coordinates: [127.646, 26.196] });
|
||||
expect(f.properties?.type).toBe('military_base');
|
||||
expect(f.properties?.branch).toBe('asdf');
|
||||
expect(f.properties?.name).toBe('Naha Air Base');
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Earthquakes ────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -1890,6 +1890,7 @@ const MaplibreViewer = ({ data, activeLayers, onEntityClick, flyToLocation, sele
|
||||
if (!base) return null;
|
||||
const branchLabel: Record<string, string> = {
|
||||
air_force: 'AIR FORCE', navy: 'NAVY', marines: 'MARINES', army: 'ARMY',
|
||||
gsdf: 'GSDF (陸自)', msdf: 'MSDF (海自)', asdf: 'ASDF (空自)',
|
||||
missile: 'MISSILE FORCES', nuclear: 'NUCLEAR FACILITY',
|
||||
};
|
||||
const isAdversary = ['China', 'Russia', 'North Korea'].includes(base.country);
|
||||
|
||||
Reference in New Issue
Block a user