feat: enhance basemaps palette with Google Hybrid and Esri Clarity, remove carto, instant road approve update

This commit is contained in:
Hamza-Ayed
2026-08-16 13:53:38 +03:00
parent 8061f3e7a3
commit 126761004c
2 changed files with 36 additions and 21 deletions
+12 -9
View File
@@ -21,16 +21,19 @@ 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-sat' | 'esri-sat' | 'esri-streets' | 'osm' | 'here-sat' | 'here-streets' | 'carto-voyager';
type RefKey = 'google-hybrid' | 'google-sat' | 'google-streets' | 'esri-sat' | 'esri-clarity' | 'esri-streets' | 'esri-topo' | 'osm' | 'here-sat' | 'here-streets';
const getRefs = (hereKey: string): Record<RefKey, { label: string; tiles: string; attribution: string; maxzoom: number }> => ({
'google-sat': { label: '🛰️ Google Satellite', tiles: 'https://mt1.google.com/vt/lyrs=s&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-streets': { label: '🗺️ Esri Streets', tiles: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_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 },
'carto-voyager': { label: '🎨 Carto Voyager', tiles: 'https://basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}@2x.png', attribution: '© CARTO, © OpenStreetMap', maxzoom: 19 },
'osm': { label: '🧭 OSM Standard', tiles: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', attribution: '© OpenStreetMap', maxzoom: 19 },
'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 rasterStyle = (ref: { tiles: string; attribution: string; maxzoom: number }): any => ({
@@ -52,7 +55,7 @@ const CompareView: React.FC = () => {
const [showKeyModal, setShowKeyModal] = useState(false);
const [tempKey, setTempKey] = useState('');
const [refKey, setRefKey] = useState<RefKey>('esri-sat');
const [refKey, setRefKey] = useState<RefKey>('google-hybrid');
const [center, setCenter] = useState<{ lng: number; lat: number; zoom: number }>({ lng: 35.91, lat: 31.95, zoom: 15 });
const REFS = getRefs(hereApiKey);
+24 -12
View File
@@ -5,13 +5,20 @@ 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-sat' | 'esri-sat' | 'esri-streets' | 'osm';
const REFS: Record<RefKey, { label: string; tiles: string; attribution: string; maxzoom: number }> = {
'google-sat': { label: '🛰️ Google Satellite', tiles: 'https://mt1.google.com/vt/lyrs=s&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-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 },
};
type RefKey = 'google-hybrid' | 'google-sat' | 'google-streets' | 'esri-sat' | 'esri-clarity' | 'esri-streets' | 'esri-topo' | 'osm' | 'here-sat' | 'here-streets';
const getRefs = (hereKey: string): Record<RefKey, { label: string; tiles: string; attribution: string; maxzoom: number }> => ({
'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 API = (import.meta as any).env.VITE_API_URL || '/api';
@@ -19,7 +26,7 @@ const cc = (c: number) => c >= 0.8 ? '#22c55e' : c >= 0.6 ? '#eab308' : '#f97316
const rasterStyle = (ref: any): any => ({
version: 8,
sources: { ref: { type: 'raster', tiles: [ref.tiles], tileSize: 256, maxzoom: ref.maxzoom } },
sources: { ref: { type: 'raster', tiles: [ref.tiles], tileSize: 256, maxzoom: ref.maxzoom, attribution: ref.attribution } },
layers: [{ id: 'ref', type: 'raster', source: 'ref' }],
});
@@ -32,10 +39,11 @@ 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 [sel, setSel] = useState<string | null>(null);
const [hereApiKey, setHereApiKey] = useState(localStorage.getItem('here_api_key') || '');
const REFS = getRefs(hereApiKey);
// Right Map State
const [refKey, setRefKey] = useState<keyof typeof REFS>('google-sat');
const [refKey, setRefKey] = useState<RefKey>('google-hybrid');
const leftMapRef = useRef<maplibregl.Map | null>(null);
const rightMapRef = useRef<maplibregl.Map | null>(null);
@@ -301,8 +309,12 @@ const IntelligenceDashboard: React.FC = () => {
if (a === 'approve') {
const d = await res.json().catch(() => ({} as any));
setMsg(d.connected ? '✅ Approved & wired into the network.' : '✅ Approved — drawn on the map.');
candsRef.current = candsRef.current.map(c => c.id === id ? { ...c, status: 'approved' } : c);
pushMapData();
} else {
setMsg('🚫 Rejected.');
candsRef.current = candsRef.current.filter(c => c.id !== id);
pushMapData();
}
setSel(null);
(leftMapRef.current?.getSource('hl') as maplibregl.GeoJSONSource)?.setData({ type: 'FeatureCollection', features: [] });
@@ -575,9 +587,9 @@ const IntelligenceDashboard: React.FC = () => {
<div style={{ flex: 1, position: 'relative' }}>
<div ref={rightDiv} style={{ width: '100%', height: '100%' }} />
<div style={{ position: 'absolute', top: 10, left: 10, zIndex: 2, display: 'flex', gap: '6px' }}>
<div style={{ position: 'absolute', top: 10, left: 10, right: 10, zIndex: 2, display: 'flex', gap: '6px', overflowX: 'auto', paddingBottom: '4px', maxWidth: 'calc(100% - 20px)' }}>
{(Object.keys(REFS) as Array<keyof typeof REFS>).map(k => (
<button key={k} onClick={() => setRefKey(k)} style={{ background: refKey === k ? 'rgba(99,102,241,0.9)' : 'rgba(15,23,42,0.85)', border: '1px solid #334155', color: refKey === k ? 'white' : '#e2e8f0', padding: '4px 10px', borderRadius: '20px', fontSize: '0.72rem', fontWeight: 600, cursor: 'pointer' }}>
<button key={k} onClick={() => setRefKey(k)} style={{ background: refKey === k ? 'rgba(99,102,241,0.95)' : 'rgba(15,23,42,0.85)', border: '1px solid #334155', color: refKey === k ? 'white' : '#e2e8f0', padding: '4px 10px', borderRadius: '20px', fontSize: '0.72rem', fontWeight: 600, cursor: 'pointer', whiteSpace: 'nowrap', flexShrink: 0 }}>
{REFS[k].label}
</button>
))}