From 844774bd1beb20e1b9e4f0a67e705eb3c1ff05c6 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Sun, 16 Aug 2026 14:09:02 +0300 Subject: [PATCH] feat: add Soviet Military Topo and Google Topo, remove HERE API dependency, relocate and restyle floating ViewNav --- apps/web/src/main.tsx | 41 ++++++- apps/web/src/pages/CompareView.tsx | 109 +++++-------------- apps/web/src/pages/IntelligenceDashboard.tsx | 42 ++++--- 3 files changed, 91 insertions(+), 101 deletions(-) diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index ad60575..8d66148 100644 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -15,21 +15,50 @@ maplibregl.setRTLTextPlugin( // Lightweight hash router (no dependency). '#compare' and '#review' are additive // views; anything else falls back to the existing map app, untouched. const NAV = [ - { hash: '#map', label: 'Map' }, - { hash: '#review', label: 'Review' }, - { hash: '#compare', label: 'Compare' }, + { hash: '#map', label: 'Map', icon: '🗺️' }, + { hash: '#review', label: 'Review', icon: '🔍' }, + { hash: '#compare', label: 'Compare', icon: '⚖️' }, ] function ViewNav({ hash }: { hash: string }) { const isMap = hash !== '#compare' && hash !== '#review' return ( -
+
{NAV.map(n => { const active = n.hash === '#map' ? isMap : hash === n.hash return ( - {n.label} + style={{ + textDecoration: 'none', + fontSize: 12, + fontWeight: 600, + color: active ? '#fff' : '#94a3b8', + background: active ? 'linear-gradient(135deg, #6366f1, #4f46e5)' : 'transparent', + boxShadow: active ? '0 2px 8px rgba(99, 102, 241, 0.4)' : 'none', + padding: '5px 13px', + borderRadius: 999, + display: 'flex', + alignItems: 'center', + gap: 5, + transition: 'all 0.15s ease' + }}> + {n.icon} + {n.label} ) })} diff --git a/apps/web/src/pages/CompareView.tsx b/apps/web/src/pages/CompareView.tsx index 32c1efb..4e81b7f 100644 --- a/apps/web/src/pages/CompareView.tsx +++ b/apps/web/src/pages/CompareView.tsx @@ -21,20 +21,32 @@ import { attachIconLoader } from '../utils/mapIcons'; // absolute — an empty default resolved to the app host, which serves no tiles. const TILES = (import.meta as any).env.VITE_TILES_URL || 'https://tiles.intaleqapp.com'; -type RefKey = 'google-hybrid' | 'google-sat' | 'google-streets' | 'esri-sat' | 'esri-clarity' | 'esri-streets' | 'esri-topo' | 'osm' | 'here-sat' | 'here-streets'; +type RefKey = + | 'google-hybrid' + | 'google-topo' + | 'soviet-military' + | 'opentopo' + | 'esri-clarity' + | 'google-sat' + | 'google-streets' + | 'esri-sat' + | 'esri-topo' + | 'esri-streets' + | 'osm'; -const getRefs = (hereKey: string): Record => ({ - 'google-hybrid': { label: '🛰️ Google Hybrid', tiles: 'https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, - 'google-sat': { label: '🛰️ Google Sat', tiles: 'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, - 'google-streets': { label: '🗺️ Google Streets', tiles: 'https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, - 'esri-sat': { label: '🛰️ Esri Satellite', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri, Maxar', maxzoom: 19 }, - 'esri-clarity': { label: '✨ Esri Clarity', tiles: 'https://clarity.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri', maxzoom: 19 }, - 'esri-streets': { label: '🗺️ Esri Streets', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri', maxzoom: 19 }, - 'esri-topo': { label: '🏔️ Esri Topo', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri', maxzoom: 19 }, - 'here-sat': { label: '🛰️ HERE Satellite', tiles: `https://maps.hereapi.com/v3/base/mc/{z}/{x}/{y}/jpeg?style=hybrid.day&apiKey=${hereKey || 'YOUR_HERE_API_KEY'}`, attribution: '© HERE Technologies', maxzoom: 19 }, - 'here-streets': { label: '🗺️ HERE Streets', tiles: `https://maps.hereapi.com/v3/base/mc/{z}/{x}/{y}/png8?style=explore.day&apiKey=${hereKey || 'YOUR_HERE_API_KEY'}`, attribution: '© HERE Technologies', maxzoom: 19 }, - 'osm': { label: '🧭 OSM Standard', tiles: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', attribution: '© OpenStreetMap', maxzoom: 19 }, -}); +const REFS: Record = { + 'google-hybrid': { label: '🛰️ Google Hybrid', tiles: 'https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, + 'google-topo': { label: '🏔️ Google Topo', tiles: 'https://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, + 'soviet-military': { label: '🇷🇺 Soviet Military Topo', tiles: 'https://maps.marshruty.ru/ml.ashx?x={x}&y={y}&z={z}&i=1&al=1', attribution: '© Soviet Military Topo 1:100k', maxzoom: 15 }, + 'opentopo': { label: '🧭 OpenTopoMap', tiles: 'https://tile.opentopomap.org/{z}/{x}/{y}.png', attribution: '© OpenTopoMap', maxzoom: 17 }, + 'esri-clarity': { label: '✨ Esri Clarity HD', tiles: 'https://clarity.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri', maxzoom: 19 }, + 'google-sat': { label: '🛰️ Google Sat', tiles: 'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, + 'google-streets': { label: '🗺️ Google Streets', tiles: 'https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, + 'esri-sat': { label: '🛰️ Esri Satellite', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri, Maxar', maxzoom: 19 }, + 'esri-topo': { label: '🏔️ Esri Topo', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri', maxzoom: 19 }, + 'esri-streets': { label: '🗺️ Esri Streets', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri', maxzoom: 19 }, + 'osm': { label: '🧭 OSM Standard', tiles: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', attribution: '© OpenStreetMap', maxzoom: 19 }, +}; const rasterStyle = (ref: { tiles: string; attribution: string; maxzoom: number }): any => ({ version: 8, @@ -49,12 +61,6 @@ const CompareView: React.FC = () => { const rightMap = useRef(null); const syncing = useRef(false); - const [hereApiKey, setHereApiKey] = useState(() => { - return localStorage.getItem('here_api_key') || (import.meta as any).env.VITE_HERE_API_KEY || ''; - }); - const [showKeyModal, setShowKeyModal] = useState(false); - const [tempKey, setTempKey] = useState(''); - const [refKey, setRefKey] = useState('google-hybrid'); const [center, setCenter] = useState<{ lng: number; lat: number; zoom: number }>({ lng: 35.91, lat: 31.95, zoom: 15 }); @@ -125,75 +131,20 @@ const CompareView: React.FC = () => { return (
{/* Toolbar */} -
- ← Map - Compare - Our map ⟷ reference — synced +
+ ⚖️ Compare + Our map ⟷ reference — synced -
+
{(Object.keys(REFS) as RefKey[]).map(k => ( ))} - {refKey.startsWith('here') && ( - - )}
- {/* HERE API Key Modal */} - {showKeyModal && ( -
-
-

🔑 HERE Platform API Key

-

- Enter your official HERE Raster Tile API v3 key to view official HERE Satellite & Streets imagery. -

- setTempKey(e.target.value)} - style={{ background: '#0f172a', border: '1px solid #475569', borderRadius: '6px', padding: '8px 12px', color: '#fff', fontSize: '0.85rem', width: '100%', boxSizing: 'border-box' }} - /> -
- - -
-
-
- )} - {/* Maps */}
diff --git a/apps/web/src/pages/IntelligenceDashboard.tsx b/apps/web/src/pages/IntelligenceDashboard.tsx index ef8d440..5659d5d 100644 --- a/apps/web/src/pages/IntelligenceDashboard.tsx +++ b/apps/web/src/pages/IntelligenceDashboard.tsx @@ -5,20 +5,32 @@ import { Activity, Map as MapIcon, Settings, ShieldAlert, CheckCircle2, XCircle, interface CandidateRoad { id: string; uniqueDriverCount: number; totalPoints: number; lengthMeters: number; confidence: number; status: string; source?: string; name?: string; highway?: string; geometry?: any; } interface Closure { segmentId: string; sampleCount: number; lastUpdated: string; } -type RefKey = 'google-hybrid' | 'google-sat' | 'google-streets' | 'esri-sat' | 'esri-clarity' | 'esri-streets' | 'esri-topo' | 'osm' | 'here-sat' | 'here-streets'; +type RefKey = + | 'google-hybrid' + | 'google-topo' + | 'soviet-military' + | 'opentopo' + | 'esri-clarity' + | 'google-sat' + | 'google-streets' + | 'esri-sat' + | 'esri-topo' + | 'esri-streets' + | 'osm'; -const getRefs = (hereKey: string): Record => ({ - 'google-hybrid': { label: '🛰️ Google Hybrid', tiles: 'https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, - 'google-sat': { label: '🛰️ Google Sat', tiles: 'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, - 'google-streets': { label: '🗺️ Google Streets', tiles: 'https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, - 'esri-sat': { label: '🛰️ Esri Satellite', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri, Maxar', maxzoom: 19 }, - 'esri-clarity': { label: '✨ Esri Clarity', tiles: 'https://clarity.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri', maxzoom: 19 }, - 'esri-streets': { label: '🗺️ Esri Streets', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri', maxzoom: 19 }, - 'esri-topo': { label: '🏔️ Esri Topo', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri', maxzoom: 19 }, - 'here-sat': { label: '🛰️ HERE Satellite', tiles: `https://maps.hereapi.com/v3/base/mc/{z}/{x}/{y}/jpeg?style=hybrid.day&apiKey=${hereKey || 'YOUR_HERE_API_KEY'}`, attribution: '© HERE', maxzoom: 19 }, - 'here-streets': { label: '🗺️ HERE Streets', tiles: `https://maps.hereapi.com/v3/base/mc/{z}/{x}/{y}/png8?style=explore.day&apiKey=${hereKey || 'YOUR_HERE_API_KEY'}`, attribution: '© HERE', maxzoom: 19 }, - 'osm': { label: '🧭 OSM Standard', tiles: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', attribution: '© OpenStreetMap', maxzoom: 19 }, -}); +const REFS: Record = { + 'google-hybrid': { label: '🛰️ Google Hybrid', tiles: 'https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, + 'google-topo': { label: '🏔️ Google Topo', tiles: 'https://mt1.google.com/vt/lyrs=p&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, + 'soviet-military': { label: '🇷🇺 Soviet Military Topo', tiles: 'https://maps.marshruty.ru/ml.ashx?x={x}&y={y}&z={z}&i=1&al=1', attribution: '© Soviet Military Topo 1:100k', maxzoom: 15 }, + 'opentopo': { label: '🧭 OpenTopoMap', tiles: 'https://tile.opentopomap.org/{z}/{x}/{y}.png', attribution: '© OpenTopoMap', maxzoom: 17 }, + 'esri-clarity': { label: '✨ Esri Clarity HD', tiles: 'https://clarity.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri', maxzoom: 19 }, + 'google-sat': { label: '🛰️ Google Sat', tiles: 'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, + 'google-streets': { label: '🗺️ Google Streets', tiles: 'https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', attribution: '© Google', maxzoom: 20 }, + 'esri-sat': { label: '🛰️ Esri Satellite', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri, Maxar', maxzoom: 19 }, + 'esri-topo': { label: '🏔️ Esri Topo', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri', maxzoom: 19 }, + 'esri-streets': { label: '🗺️ Esri Streets', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', attribution: '© Esri', maxzoom: 19 }, + 'osm': { label: '🧭 OSM Standard', tiles: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', attribution: '© OpenStreetMap', maxzoom: 19 }, +}; const API = (import.meta as any).env.VITE_API_URL || '/api'; @@ -39,8 +51,6 @@ const IntelligenceDashboard: React.FC = () => { const [loading, setLoading] = useState(true); const [apiKey, setApiKey] = useState(localStorage.getItem('map_admin_key') || ''); const [needsAuth, setNeedsAuth] = useState(!localStorage.getItem('map_admin_key')); - const [hereApiKey, setHereApiKey] = useState(localStorage.getItem('here_api_key') || ''); - const REFS = getRefs(hereApiKey); // Right Map State const [refKey, setRefKey] = useState('google-hybrid'); @@ -587,7 +597,7 @@ const IntelligenceDashboard: React.FC = () => {
-
+
{(Object.keys(REFS) as Array).map(k => (