Files
maps-saas/scripts/inject_sovereign_navigation.py
T

238 lines
9.7 KiB
Python

#!/usr/bin/env python3
import re
def main():
src = 'apps/dashboard/tourism.html'
with open(src, 'r', encoding='utf-8') as f:
html = f.read()
# 1. Add HUD CSS
hud_css = """
/* Sovereign In-App Navigation HUD */
.sovereign-nav-hud {
position: absolute;
top: 80px;
left: 50%;
transform: translateX(-50%);
z-index: 70;
background: rgba(6, 29, 66, 0.96);
backdrop-filter: blur(25px);
-webkit-backdrop-filter: blur(25px);
border: 2px solid #06b6d4;
border-radius: 18px;
padding: 14px 20px;
color: #fff;
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.45);
width: 480px;
max-width: calc(100vw - 32px);
animation: fadeInDown 0.3s ease;
}
@keyframes fadeInDown { from { opacity: 0; transform: translate(-50%, -15px); } to { opacity: 1; transform: translate(-50%, 0); } }
.nav-hud-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
.nav-hud-title { display: flex; align-items: center; gap: 8px; font-weight: 800; font-size: 0.88rem; color: #38bdf8; }
.pulse-dot { width: 8px; height: 8px; border-radius: 50%; background: #22c55e; box-shadow: 0 0 10px #22c55e; display: inline-block; }
.nav-hud-close { background: rgba(239, 68, 68, 0.2); border: 1px solid rgba(239, 68, 68, 0.5); color: #fca5a5; padding: 4px 10px; border-radius: 8px; cursor: pointer; font-size: 0.74rem; font-weight: 700; }
.nav-hud-close:hover { background: rgba(239, 68, 68, 0.4); }
.nav-dest-info { font-size: 0.84rem; color: #f1f5f9; margin-bottom: 8px; }
.dest-label { color: var(--gold); font-weight: 700; margin-left: 4px; }
.nav-stats { display: flex; gap: 8px; margin-bottom: 10px; }
.stat-pill { background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.12); padding: 5px 12px; border-radius: 8px; display: flex; gap: 4px; align-items: baseline; }
.stat-val { font-weight: 800; font-size: 0.95rem; color: #facc15; }
.stat-lbl { font-size: 0.7rem; color: #94a3b8; }
.nav-step { font-size: 0.78rem; background: rgba(6, 182, 212, 0.12); border: 1px solid rgba(6, 182, 212, 0.3); color: #a5f3fc; padding: 7px 12px; border-radius: 8px; }
"""
if '.sovereign-nav-hud' not in html:
html = html.replace('</style>', hud_css + '\n </style>', 1)
# 2. Add HUD HTML
hud_html = """
<!-- Sovereign In-App Navigation HUD -->
<div id="sovereignNavHud" class="sovereign-nav-hud" style="display: none;">
<div class="nav-hud-header">
<div class="nav-hud-title">
<span class="pulse-dot"></span>
<span>🚗 مسار التوجيه الملاحي السيادي (محرك أوروك المستقل)</span>
</div>
<button class="nav-hud-close" id="stopNavBtn">✕ إنهاء التوجيه ومسح المسار</button>
</div>
<div class="nav-hud-body">
<div class="nav-dest-info">
<span class="dest-label">نقطة الوصول:</span>
<strong id="navDestName">بوابة العبور ومواقف الحافلات</strong>
</div>
<div class="nav-stats">
<div class="stat-pill"><span class="stat-val" id="navDistance">0</span> <span class="stat-lbl">كم</span></div>
<div class="stat-pill"><span class="stat-val" id="navEta">0</span> <span class="stat-lbl">دقيقة</span></div>
<div class="stat-pill"><span class="stat-val">محرك سيادي</span> <span class="stat-lbl">GraphHopper</span></div>
</div>
<div class="nav-step" id="navInstruction">🛣️ اسلك المسار المضاء مباشرة نحو بوابة العبور ومواقف الحافلات المخصصة</div>
</div>
</div>
"""
if 'id="sovereignNavHud"' not in html:
html = html.replace('<script>', hud_html + '\n <script>', 1)
# 3. Replace the Google Maps redirect with in-app sovereign navigation
pattern = r"document\.getElementById\('drawerNavBtn'\)\.onclick\s*=\s*\(\)\s*=>\s*\{[\s\S]*?window\.open\([^\)]*\);[\s\S]*?\};"
replacement = "document.getElementById('drawerNavBtn').onclick = () => {\n startSovereignNavigation(lm);\n };"
html = re.sub(pattern, replacement, html)
# 4. Add the sovereign navigation engine function
nav_code = """
// Sovereign In-App Routing Engine (Zero Google Maps Dependency)
function startSovereignNavigation(lm) {
document.getElementById('landmarkDrawer').style.display = 'none';
const [gateLng, gateLat] = lm.gate;
const defaultHubs = {
'العراق': [44.3855, 33.3283],
'الأردن': [35.9106, 31.9539],
'سوريا': [36.2765, 33.5138],
'مصر': [31.2357, 30.0444]
};
const startCoord = defaultHubs[lm.country] || [44.3855, 33.3283];
function computeAndDraw(startLng, startLat) {
const apiUrl = `/api/maps/route?fromLat=${startLat}&fromLng=${startLng}&toLat=${gateLat}&toLng=${gateLng}&profile=car&steps=true&locale=ar`;
fetch(apiUrl)
.then(r => {
if (!r.ok) throw new Error('Offline');
return r.json();
})
.then(data => {
if (data && data.routes && data.routes.length > 0) {
const r = data.routes[0];
const distKm = (r.distance / 1000).toFixed(1);
const etaMins = Math.round(r.duration / 60);
drawRouteOnMap(r.geometry.coordinates, distKm, etaMins, lm);
} else {
throw new Error('No route');
}
})
.catch(() => {
const lineCoords = generateSovereignCorridor(startLng, startLat, gateLng, gateLat);
const dist = calculateHaversineKm(startLat, startLng, gateLat, gateLng);
const eta = Math.round((dist / 75) * 60);
drawRouteOnMap(lineCoords, dist.toFixed(1), eta, lm);
});
}
if ('geolocation' in navigator) {
navigator.geolocation.getCurrentPosition(
pos => computeAndDraw(pos.coords.longitude, pos.coords.latitude),
() => computeAndDraw(startCoord[0], startCoord[1]),
{ timeout: 4000 }
);
} else {
computeAndDraw(startCoord[0], startCoord[1]);
}
}
function calculateHaversineKm(lat1, lon1, lat2, lon2) {
const R = 6371;
const dLat = (lat2 - lat1) * Math.PI / 180;
const dLon = (lon2 - lon1) * Math.PI / 180;
const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon/2) * Math.sin(dLon/2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
return R * c;
}
function generateSovereignCorridor(lng1, lat1, lng2, lat2) {
const points = [];
const steps = 12;
for (let i = 0; i <= steps; i++) {
const f = i / steps;
const curLng = lng1 + (lng2 - lng1) * f;
const curLat = lat1 + (lat2 - lat1) * f;
points.push([curLng, curLat]);
}
return points;
}
function drawRouteOnMap(coords, distKm, etaMins, lm) {
const geojson = {
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: coords
}
};
if (map.getSource('sovereign-route')) {
map.getSource('sovereign-route').setData(geojson);
} else {
map.addSource('sovereign-route', {
type: 'geojson',
data: geojson
});
map.addLayer({
id: 'sovereign-route-casing',
type: 'line',
source: 'sovereign-route',
layout: { 'line-join': 'round', 'line-cap': 'round' },
paint: {
'line-color': '#0369a1',
'line-width': 8,
'line-opacity': 0.8
}
});
map.addLayer({
id: 'sovereign-route-core',
type: 'line',
source: 'sovereign-route',
layout: { 'line-join': 'round', 'line-cap': 'round' },
paint: {
'line-color': '#38bdf8',
'line-width': 4.5
}
});
}
const bounds = coords.reduce((b, coord) => b.extend(coord), new maplibregl.LngLatBounds(coords[0], coords[0]));
map.fitBounds(bounds, { padding: 90, pitch: 45, bearing: 10, duration: 1600 });
document.getElementById('navDestName').innerText = `بوابة ${lm.name_ar} (نقطة العبور الخدمي ومواقف الحافلات)`;
document.getElementById('navDistance').innerText = distKm;
document.getElementById('navEta').innerText = etaMins;
document.getElementById('sovereignNavHud').style.display = 'block';
}
document.getElementById('stopNavBtn').addEventListener('click', () => {
document.getElementById('sovereignNavHud').style.display = 'none';
if (map.getSource('sovereign-route')) {
map.getSource('sovereign-route').setData({
type: 'Feature',
properties: {},
geometry: { type: 'LineString', coordinates: [] }
});
}
if (selectedLandmark) {
map.flyTo({ center: selectedLandmark.centroid, zoom: 14.5, pitch: 40 });
}
});
"""
if 'function startSovereignNavigation' not in html:
html = html.replace("map.on('load', () => {", nav_code + "\n map.on('load', () => {", 1)
# Save to all 4 destinations
destinations = [
'apps/dashboard/tourism.html',
'apps/dashboard/tourism/index.html',
'apps/web/public/tourism.html',
'apps/web/public/tourism/index.html'
]
for d in destinations:
with open(d, 'w', encoding='utf-8') as f:
f.write(html)
print(f'✅ Successfully updated {d} with 100% In-App Sovereign Routing!')
if __name__ == '__main__':
main()