fix: restrict map route domain to Chennai region and implement numbered stop markers
This commit is contained in:
@@ -16,23 +16,34 @@ L.Icon.Default.mergeOptions({
|
||||
shadowUrl: markerShadow,
|
||||
});
|
||||
|
||||
// Domain Boundary validation (Chennai and surrounding route regions)
|
||||
const MIN_LAT = 12.7;
|
||||
const MAX_LAT = 13.4;
|
||||
const MIN_LNG = 79.6;
|
||||
const MAX_LNG = 80.4;
|
||||
|
||||
const isValidCoordinate = (lat?: number, lng?: number): boolean => {
|
||||
if (lat === undefined || lng === undefined) return false;
|
||||
return lat >= MIN_LAT && lat <= MAX_LAT && lng >= MIN_LNG && lng <= MAX_LNG;
|
||||
};
|
||||
|
||||
// Custom Premium DivIcons using Tailwind CSS
|
||||
const startIcon = L.divIcon({
|
||||
html: `<div class="flex items-center justify-center w-6 h-6 rounded-full bg-emerald-500 border-2 border-white shadow-md text-white text-[10px] font-bold">A</div>`,
|
||||
const getStartIcon = (label: string = "1") => L.divIcon({
|
||||
html: `<div class="flex items-center justify-center w-6 h-6 rounded-full bg-emerald-500 border-2 border-white shadow-md text-white text-[10px] font-extrabold hover:scale-110 transition-transform">${label}</div>`,
|
||||
className: '',
|
||||
iconSize: [24, 24],
|
||||
iconAnchor: [12, 12],
|
||||
});
|
||||
|
||||
const stopIcon = L.divIcon({
|
||||
html: `<div class="w-3.5 h-3.5 rounded-full bg-blue-500 border-2 border-white shadow-sm hover:scale-125 transition-transform"></div>`,
|
||||
const getStopIcon = (num: number) => L.divIcon({
|
||||
html: `<div class="flex items-center justify-center w-5.5 h-5.5 rounded-full bg-blue-600 border-2 border-white shadow-md text-white text-[10px] font-bold hover:scale-125 hover:bg-blue-700 transition-all">${num}</div>`,
|
||||
className: '',
|
||||
iconSize: [14, 14],
|
||||
iconAnchor: [7, 7],
|
||||
iconSize: [22, 22],
|
||||
iconAnchor: [11, 11],
|
||||
});
|
||||
|
||||
const campusIcon = L.divIcon({
|
||||
html: `<div class="flex items-center justify-center w-9 h-9 rounded-full bg-gradient-to-tr from-orange-500 to-amber-500 border-2 border-white shadow-lg text-white text-sm font-bold animate-pulse">🏫</div>`,
|
||||
html: `<div class="flex items-center justify-center w-9 h-9 rounded-full bg-gradient-to-tr from-orange-500 to-amber-500 border-2 border-white shadow-lg text-white text-sm font-bold animate-pulse hover:scale-110 transition-transform">🏫</div>`,
|
||||
className: '',
|
||||
iconSize: [36, 36],
|
||||
iconAnchor: [18, 18],
|
||||
@@ -57,27 +68,27 @@ interface BusRouteMapProps {
|
||||
const DEFAULT_CENTER = [13.0118, 80.0214]; // RIT Campus default
|
||||
|
||||
export default function BusRouteMap({ selectedRoute, allRoutes }: BusRouteMapProps) {
|
||||
// Collect coordinates for the polyline path
|
||||
// Collect coordinates for the polyline path, filtering out any invalid outliers
|
||||
const pathCoordinates = useMemo(() => {
|
||||
if (!selectedRoute) return [];
|
||||
|
||||
const coords: [number, number][] = [];
|
||||
|
||||
// Add start stop coords if present
|
||||
if (selectedRoute.from_lat && selectedRoute.from_lng) {
|
||||
coords.push([selectedRoute.from_lat, selectedRoute.from_lng]);
|
||||
// Add start stop coords if valid
|
||||
if (isValidCoordinate(selectedRoute.from_lat, selectedRoute.from_lng)) {
|
||||
coords.push([selectedRoute.from_lat!, selectedRoute.from_lng!]);
|
||||
}
|
||||
|
||||
// Add all intermediary stop coords
|
||||
// Add all intermediary stop coords that are valid
|
||||
selectedRoute.stops.forEach(stop => {
|
||||
if (stop.lat && stop.lng) {
|
||||
coords.push([stop.lat, stop.lng]);
|
||||
if (isValidCoordinate(stop.lat, stop.lng)) {
|
||||
coords.push([stop.lat!, stop.lng!]);
|
||||
}
|
||||
});
|
||||
|
||||
// Add end stop coords if present
|
||||
if (selectedRoute.to_lat && selectedRoute.to_lng) {
|
||||
coords.push([selectedRoute.to_lat, selectedRoute.to_lng]);
|
||||
// Add end stop coords if valid
|
||||
if (isValidCoordinate(selectedRoute.to_lat, selectedRoute.to_lng)) {
|
||||
coords.push([selectedRoute.to_lat!, selectedRoute.to_lng!]);
|
||||
}
|
||||
|
||||
return coords;
|
||||
@@ -89,32 +100,38 @@ export default function BusRouteMap({ selectedRoute, allRoutes }: BusRouteMapPro
|
||||
return pathCoordinates as L.LatLngBoundsExpression;
|
||||
}, [pathCoordinates]);
|
||||
|
||||
// Determine which routes to display (either the selected one, or all route start pins as overview)
|
||||
// Determine which markers to display
|
||||
const renderMarkers = () => {
|
||||
if (selectedRoute) {
|
||||
// Filter out stops that do not have valid coordinates
|
||||
const validStops = selectedRoute.stops.filter(stop => isValidCoordinate(stop.lat, stop.lng));
|
||||
|
||||
return (
|
||||
<>
|
||||
{selectedRoute.stops.map((stop, index) => {
|
||||
{validStops.map((stop, index) => {
|
||||
const isStart = index === 0;
|
||||
const isEnd = index === selectedRoute.stops.length - 1;
|
||||
const isEnd = index === validStops.length - 1;
|
||||
const isRit = stop.name.toLowerCase().includes("rit");
|
||||
|
||||
let currentIcon = stopIcon;
|
||||
if (isStart) currentIcon = startIcon;
|
||||
let currentIcon = getStopIcon(index + 1);
|
||||
if (isStart) currentIcon = getStartIcon("Start");
|
||||
if (isRit || isEnd) currentIcon = campusIcon;
|
||||
|
||||
if (!stop.lat || !stop.lng) return null;
|
||||
|
||||
return (
|
||||
<Marker
|
||||
key={`${stop.name}-${index}`}
|
||||
position={[stop.lat, stop.lng]}
|
||||
position={[stop.lat!, stop.lng!]}
|
||||
icon={currentIcon}
|
||||
>
|
||||
<Popup>
|
||||
<div className="p-1 font-sans">
|
||||
<h4 className="font-bold text-slate-800 text-xs">{stop.name}</h4>
|
||||
<p className="text-[10px] text-orange-500 font-semibold mt-0.5">Estimated: {stop.time}</p>
|
||||
<div className="flex items-center gap-1.5 mb-0.5">
|
||||
<span className="text-[10px] bg-slate-100 text-slate-700 px-1.5 py-0.5 rounded font-bold">
|
||||
Stop #{index + 1}
|
||||
</span>
|
||||
<h4 className="font-bold text-slate-800 text-xs">{stop.name}</h4>
|
||||
</div>
|
||||
<p className="text-[10px] text-orange-500 font-semibold mt-0.5">Estimated Arrival: {stop.time}</p>
|
||||
{isStart && <span className="inline-block text-[9px] bg-emerald-100 text-emerald-700 px-1.5 py-0.5 rounded-full mt-1 font-bold">Departure Stop</span>}
|
||||
{isRit && <span className="inline-block text-[9px] bg-orange-100 text-orange-700 px-1.5 py-0.5 rounded-full mt-1 font-bold">RIT Campus</span>}
|
||||
</div>
|
||||
@@ -126,7 +143,7 @@ export default function BusRouteMap({ selectedRoute, allRoutes }: BusRouteMapPro
|
||||
);
|
||||
}
|
||||
|
||||
// Default view: Draw RIT Campus pin + Start pins for all routes
|
||||
// Default view: Draw RIT Campus pin + Start pins for all routes (only if coordinates are valid)
|
||||
return (
|
||||
<>
|
||||
<Marker position={[13.0118, 80.0214]} icon={campusIcon}>
|
||||
@@ -138,12 +155,12 @@ export default function BusRouteMap({ selectedRoute, allRoutes }: BusRouteMapPro
|
||||
</Popup>
|
||||
</Marker>
|
||||
{allRoutes.map((r, i) => {
|
||||
if (!r.from_lat || !r.from_lng) return null;
|
||||
if (!isValidCoordinate(r.from_lat, r.from_lng)) return null;
|
||||
return (
|
||||
<Marker
|
||||
key={`start-${r.number}-${i}`}
|
||||
position={[r.from_lat, r.from_lng]}
|
||||
icon={startIcon}
|
||||
position={[r.from_lat!, r.from_lng!]}
|
||||
icon={getStartIcon(r.number)}
|
||||
>
|
||||
<Popup>
|
||||
<div className="p-1 font-sans">
|
||||
|
||||
Reference in New Issue
Block a user