Skip to content

Commit cc8ac96

Browse files
committed
Marianas Map Markers added and finalised
1 parent 5d90d1c commit cc8ac96

5 files changed

Lines changed: 147 additions & 18 deletions

File tree

0 Bytes
Binary file not shown.

scripts/MapDatabaseTools/add_marianas_markers.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ def marianas_samples():
4848
iata="GUM",
4949
elevation_m=91.0,
5050
runways=[
51-
Runway(name="06L/24R", length_m=3255, width_m=45, heading=63.0, surface="asphalt", ils=True),
52-
Runway(name="06R/24L", length_m=2743, width_m=45, heading=63.0, surface="asphalt", ils=False)
51+
Runway(name="06L/24R", length_m=3255, width_m=45, heading=65.0, surface="asphalt", ils=True),
52+
Runway(name="06R/24L", length_m=2743, width_m=45, heading=65.0, surface="asphalt", ils=False)
5353
],
5454
frequencies={"tower": 110.30, "ground": 121.9, "approach": 118.7, "atis": 127.25},
5555
country="United States (Guam)",
@@ -66,8 +66,8 @@ def marianas_samples():
6666
iata="UAM",
6767
elevation_m=184.0,
6868
runways=[
69-
Runway(name="06L/24R", length_m=3399, width_m=61, heading=63.0, surface="concrete", ils=True),
70-
Runway(name="06R/24L", length_m=3048, width_m=46, heading=63.0, surface="concrete", ils=False)
69+
Runway(name="06L/24R", length_m=3399, width_m=61, heading=65.0, surface="concrete", ils=True),
70+
Runway(name="06R/24L", length_m=3048, width_m=46, heading=65.0, surface="concrete", ils=False)
7171
],
7272
frequencies={"tower": 126.2, "ground": 275.8, "approach": 327.0, "departure": 363.275},
7373
country="United States (Guam)",
@@ -77,8 +77,8 @@ def marianas_samples():
7777
),
7878
Location(
7979
name="OLF Orote (Naval Base Guam)",
80-
latitude=13.447222,
81-
longitude=144.637778,
80+
latitude=13.438336,
81+
longitude=144.642391,
8282
marker_type=MarkerType.AIRPORT.value,
8383
icao="PGRO",
8484
elevation_m=116.0,
@@ -92,7 +92,7 @@ def marianas_samples():
9292
map="Marianas"
9393
),
9494

95-
# Northern Mariana Islands (US Commonwealth)
95+
# Northern Mariana Islands (US Commonwealth)
9696
Location(
9797
name="Saipan International Airport",
9898
latitude=15.119444,
@@ -129,13 +129,13 @@ def marianas_samples():
129129
),
130130
Location(
131131
name="North West Field (Tinian)",
132-
latitude=15.087778,
133-
longitude=145.641389,
132+
latitude=15.077842,
133+
longitude=145.639744,
134134
marker_type=MarkerType.AIRPORT.value,
135135
icao="TN01",
136136
elevation_m=187.0,
137137
runways=[
138-
Runway(name="08/26", length_m=2438, width_m=46, heading=80.0, surface="asphalt", ils=False)
138+
Runway(name="06/24", length_m=2438, width_m=46, heading=62.0, surface="asphalt", ils=False)
139139
],
140140
frequencies={"tower": 118.5},
141141
country="United States (Northern Mariana Islands)",
@@ -162,13 +162,13 @@ def marianas_samples():
162162
),
163163
Location(
164164
name="Pagan Airstrip",
165-
latitude=18.139722,
166-
longitude=145.774167,
165+
latitude=18.123207,
166+
longitude=145.763147,
167167
marker_type=MarkerType.AIRPORT.value,
168168
icao="PGPA",
169169
elevation_m=167.0,
170170
runways=[
171-
Runway(name="03/21", length_m=914, width_m=18, heading=30.0, surface="coral", ils=False)
171+
Runway(name="12/30", length_m=914, width_m=18, heading=119.0, surface="coral", ils=False)
172172
],
173173
frequencies={},
174174
country="United States (Northern Mariana Islands)",

scripts/MapDatabaseTools/gui/assets_manager.py

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,44 @@ def refresh_list(self):
9191
markers_header.setForeground(QColor("#666"))
9292
self.list_widget.addItem(markers_header)
9393

94-
markers = self.db.get_all_locations()
95-
for m in markers:
96-
if cat in ("All", "Markers") and (not q or q in (m.name or "").lower()):
97-
item = QListWidgetItem(f"📍 {m.name} ({m.latitude:.4f}, {m.longitude:.4f})")
98-
item.setData(Qt.ItemDataRole.UserRole, ("marker", m.id))
94+
# Group markers by DCS map (show subheaders per map)
95+
if cat in ("All", "Markers"):
96+
markers = self.db.get_all_locations()
97+
groups: dict = {}
98+
for m in markers:
99+
# Apply name filter
100+
if q and q not in (m.name or "").lower():
101+
continue
102+
map_name = (m.map or "").strip()
103+
if not map_name:
104+
map_key = "No Map"
105+
else:
106+
map_key = map_name
107+
groups.setdefault(map_key, []).append(m)
108+
109+
if not groups:
110+
item = QListWidgetItem("No markers found")
111+
item.setFlags(Qt.ItemFlag.NoItemFlags)
112+
item.setForeground(QColor("gray"))
99113
self.list_widget.addItem(item)
114+
else:
115+
# Sort maps alphabetically but put "No Map" last
116+
def map_sort_key(s):
117+
return (s == "No Map", s.lower())
118+
119+
for map_key in sorted(groups.keys(), key=map_sort_key):
120+
# Subheader for this map
121+
sub_label = f" — {map_key} —"
122+
sub_item = QListWidgetItem(sub_label)
123+
sub_item.setFlags(Qt.ItemFlag.NoItemFlags)
124+
sub_item.setForeground(QColor("#888"))
125+
self.list_widget.addItem(sub_item)
126+
127+
# Add markers in this map (sorted by name)
128+
for m in sorted(groups[map_key], key=lambda x: (x.name or "").lower()):
129+
item = QListWidgetItem(f" 📍 {m.name} ({m.latitude:.4f}, {m.longitude:.4f})")
130+
item.setData(Qt.ItemDataRole.UserRole, ("marker", m.id))
131+
self.list_widget.addItem(item)
100132

101133
# Borders
102134
borders_header = QListWidgetItem("— BORDERS —")

scripts/MapDatabaseTools/gui/datapad_gui.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,12 @@ def on_location_selected(self, location: Location):
722722
js = f"centerMap({location.latitude:.6f}, {location.longitude:.6f});"
723723
try:
724724
self._call_js_when_ready('centerMap', js)
725+
# Also draw runway headings if present for this location
726+
try:
727+
self._call_js_when_ready('clearRunwayLines', "clearRunwayLines();")
728+
self._call_js_when_ready('showRunwayHeadings', f"showRunwayHeadings({location.id});")
729+
except Exception:
730+
pass
725731
except Exception:
726732
pass
727733

@@ -731,7 +737,16 @@ def on_asset_selected(self, asset_tuple):
731737
return
732738
typ, obj = asset_tuple
733739
if typ == "marker":
740+
# Center on selected location and show runway heading lines (if available)
734741
self.on_location_selected(obj)
742+
try:
743+
# Clear previous runway lines
744+
if self.webview:
745+
self._call_js_when_ready('clearRunwayLines', "clearRunwayLines();")
746+
# Draw for this marker id
747+
self._call_js_when_ready('showRunwayHeadings', f"showRunwayHeadings({obj.id});")
748+
except Exception:
749+
pass
735750
elif typ == "border":
736751
self.on_border_selected(obj)
737752

@@ -783,6 +798,12 @@ def on_location_changed(self, location):
783798
def on_border_selected(self, border: Border):
784799
"""Handle border selection - zoom to border on map"""
785800
if self.webview and border and border.points:
801+
# Clear any runway lines (we are switching to border)
802+
try:
803+
self._call_js_when_ready('clearRunwayLines', "clearRunwayLines();")
804+
except Exception:
805+
pass
806+
786807
# Calculate center of border
787808
lats = [p[0] for p in border.points]
788809
lons = [p[1] for p in border.points]
@@ -1286,6 +1307,14 @@ def refresh_map_markers(self):
12861307
marker_info['icao'] = loc.icao
12871308
if loc.runways:
12881309
marker_info['runways'] = len(loc.runways)
1310+
# Add runway heading details for visualization (heading in degrees, length in meters)
1311+
try:
1312+
marker_info['runway_headings'] = [
1313+
{ 'heading': float(rw.heading), 'length_m': float(rw.length_m) if rw.length_m else None }
1314+
for rw in loc.runways
1315+
]
1316+
except Exception:
1317+
marker_info['runway_headings'] = []
12891318
if loc.threat_level:
12901319
marker_info['threat'] = loc.threat_level
12911320

scripts/MapDatabaseTools/map.html

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,9 @@
254254
var bordersLayer = L.layerGroup().addTo(map);
255255
var borderPolygons = {};
256256
var mgrsGridLayer = null;
257+
258+
// Layer for drawing selected marker runway centerlines
259+
var runwayLinesLayer = L.layerGroup().addTo(map);
257260

258261
// Border drawing state
259262
var isDrawingBorder = false;
@@ -391,13 +394,78 @@
391394
marker.bindPopup(popupContent);
392395

393396
markersLayer.addLayer(marker);
397+
// Attach runway headings data (if any) to marker for later visualization
398+
try { marker.runway_headings = m.runway_headings || []; marker.options.runway_headings = m.runway_headings || []; } catch (e) {}
394399
locationMarkers[m.id] = marker;
395400

396401
} catch (e) {
397402
console.error('Error adding marker:', e, m);
398403
}
399404
});
400405

406+
// Function to compute destination point given bearing and distance (meters)
407+
function destinationPoint(lat, lon, bearing, distance_m) {
408+
var R = 6371000; // Earth radius in meters
409+
var br = bearing * Math.PI / 180;
410+
var lat1 = lat * Math.PI / 180;
411+
var lon1 = lon * Math.PI / 180;
412+
var d = distance_m / R;
413+
var lat2 = Math.asin(Math.sin(lat1) * Math.cos(d) + Math.cos(lat1) * Math.sin(d) * Math.cos(br));
414+
var lon2 = lon1 + Math.atan2(Math.sin(br) * Math.sin(d) * Math.cos(lat1), Math.cos(d) - Math.sin(lat1) * Math.sin(lat2));
415+
return [lat2 * 180 / Math.PI, lon2 * 180 / Math.PI];
416+
}
417+
418+
// Clear existing runway lines
419+
window.clearRunwayLines = function() {
420+
try {
421+
runwayLinesLayer.clearLayers();
422+
} catch (e) { console.error('clearRunwayLines', e); }
423+
};
424+
425+
// Draw runway centerlines for a given marker id using supplied runway_headings in marker data
426+
window.showRunwayHeadings = function(markerId) {
427+
try {
428+
runwayLinesLayer.clearLayers();
429+
if (!locationMarkers[markerId]) return;
430+
var marker = locationMarkers[markerId];
431+
var lat = marker.getLatLng().lat;
432+
var lon = marker.getLatLng().lng;
433+
var mdata = null;
434+
// Try to find the original data object; it's not stored directly, so
435+
// we rely on markers array closure in updateMarkers - workaround: markersLayer.eachLayer
436+
markersLayer.eachLayer(function(layer) {
437+
if (layer && layer.getLatLng && layer.getLatLng().lat === lat && layer.getLatLng().lng === lon) {
438+
// try to get stored marker options or bind to popup content hint
439+
}
440+
});
441+
// Instead, request runway headings from window._lastMarkerRunways if available
442+
// To keep things simple, we also store runway data on the marker object itself in updateMarkers
443+
var runways = marker.options.runway_headings || marker.runway_headings || [];
444+
445+
if (!runways || runways.length === 0) {
446+
// Nothing to draw
447+
return;
448+
}
449+
450+
runways.forEach(function(r) {
451+
var heading = Number(r.heading) || 0;
452+
var length_m = r.length_m && Number(r.length_m) > 0 ? Number(r.length_m) : 1000; // default 1 km
453+
var half = length_m / 2.0;
454+
455+
var pt1 = destinationPoint(lat, lon, heading, half);
456+
var pt2 = destinationPoint(lat, lon, (heading + 180) % 360, half);
457+
458+
var line = L.polyline([pt1, pt2], { color: '#FF8800', weight: 3, opacity: 0.9, dashArray: '8,6' });
459+
line.addTo(runwayLinesLayer);
460+
});
461+
462+
// Optionally zoom slightly to show the lines
463+
// Here we pan to marker to ensure visibility
464+
map.panTo([lat, lon]);
465+
466+
} catch (e) { console.error('showRunwayHeadings error', e); }
467+
};
468+
401469
// Provide getters for Python to poll for marker interactions
402470
window.getClickedMarker = function() {
403471
try {

0 commit comments

Comments
 (0)