diff --git a/public/app.js b/public/app.js
index b3a53b1..d110b19 100644
--- a/public/app.js
+++ b/public/app.js
@@ -1,269 +1,49 @@
-// ------------------------------
-// MAP SETUP
-// ------------------------------
-var streets = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
- attribution: '© OpenStreetMap contributors'
-});
+const map = L.map("map").setView([40.7608, -111.8910], 12);
-var satellite = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/' +
- 'World_Imagery/MapServer/tile/{z}/{y}/{x}', {
- attribution: 'Tiles © Esri'
-});
+L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
+ attribution: '© OpenStreetMap',
+}).addTo(map);
-var map = L.map('map', {
- center: [40.7608, -111.8910],
- zoom: 13,
- layers: [streets]
-});
-
-var baseMaps = { "Streets": streets, "Satellite": satellite };
-L.control.layers(baseMaps).addTo(map);
-
-const MJR_LINES = ["701", "703", "704", "720", "750"];
-const LRT_LINES = ["701", "703", "704", "720"];
-const FRONTRUNNER = ["750"];
const API_URL = "http://localhost:7653/api/v0/";
-// ------------------------------
-// ROUTE STYLES
-// ------------------------------
-const ROUTE_STYLES = {
- "701": { color: "#0074D9", train: "🔵", stop: "🔹" },
- "704": { color: "#2ECC40", train: "🟢", stop: "🟩" },
- "703": { color: "#FF4136", train: "🔴", stop: "🔺" },
- "750": { color: "#B10DC9", train: "🟣", stop: "🔮" },
- "720": { color: "#000000", train: "⚪", stop: "🔷" }
-};
+const trainEmojiIcon = L.divIcon({ html: "🔵", className: "", iconSize: [64,64], iconAnchor: [16,32], popupAnchor: [0,-32] });
+const stopsEmojiIcon = L.divIcon({ html: "🫃", className: "", iconSize: [64,64], iconAnchor: [16,32], popupAnchor: [0,-32] });
-// ------------------------------
-// ICON BUILDER
-// ------------------------------
-function buildIcon(emoji, bearing) {
- const rotate = bearing ? `transform: rotate(${bearing}deg); transform-origin: center;` : '';
- const svg = `
- `;
-
- return L.divIcon({
- html: svg,
- className: "emoji-icon",
- iconSize: [32, 32],
- iconAnchor: [16, 16],
- popupAnchor: [0, -16]
- });
-}
-
-// ------------------------------
-// MARKER HELPER
-// ------------------------------
function addMarker(lat, lon, content, icon) {
if (!isNaN(lat) && !isNaN(lon)) {
- const marker = L.marker([lat, lon], { icon });
+ const marker = L.marker([lat, lon], { icon: icon }).addTo(map);
if (content) marker.bindPopup(content);
- return marker;
+ } else {
+ console.warn("Invalid coordinates:", latitude, longitude);
}
- console.warn("Invalid coordinates:", lat, lon);
}
-// ------------------------------
-// ROUTE LINES
-// ------------------------------
-function drawPolyLine(polylinePoints, color) {
- L.polyline(polylinePoints, { color, weight: 4, opacity: 0.8 }).addTo(map);
-}
-
-function drawLine(route) {
- fetch(API_URL + "routepaths/" + route)
+function getTrainsByRoute(route) {
+ fetch(API_URL + 'vehicles')
.then(res => res.json())
.then(data => {
- const points = data.data;
- if (!points || points.length === 0) return;
- const color = ROUTE_STYLES[route].color;
-
- let polylinePoints = [];
- let currentShape = points[0].shape_id;
-
- points.forEach(p => {
- if (p.shape_id === currentShape) {
- polylinePoints.push([p.lat, p.lng]);
- } else {
- drawPolyLine(polylinePoints, color);
- polylinePoints = [[p.lat, p.lng]];
- currentShape = p.shape_id;
- }
+ const trains = data.data;
+ const filtered = route ? trains.filter(t => t.routeId == route) : trains;
+ filtered.forEach(t => {
+ addMarker(t.location.latitude, t.location.longitude, t.routeName + ": Vehicle " + t.vehicleId, trainEmojiIcon);
});
-
- if (polylinePoints.length > 0) drawPolyLine(polylinePoints, color);
})
- .catch(err => console.error("Error drawing line:", err));
+ .catch(err => console.error("Error fetching trains:", err));
}
-function drawLines() {
- MJR_LINES.forEach(drawLine);
-}
-
-// ------------------------------
-// STOPS
-// ------------------------------
-let stopMarkers = {};
function getStopsByRoute(route) {
- if (stopMarkers[route]) return;
- stopMarkers[route] = [];
- const stopIcon = buildIcon(ROUTE_STYLES[route].stop);
-
- fetch(API_URL + "stops/" + route)
+ fetch(API_URL + 'stops/' + route)
.then(res => res.json())
.then(data => {
- data.data.forEach(s => {
- const lat = parseFloat(s.location.latitude);
- const lon = parseFloat(s.location.longitude);
- const marker = addMarker(
- lat, lon,
- `${s.stop_name}
${s.stop_desc}`,
- stopIcon
- ).addTo(map);
- stopMarkers[route].push(marker);
+ const stops = data.data;
+ stops.forEach(s => {
+ const lat = parseFloat(s.stop_lat);
+ const lon = parseFloat(s.stop_lon);
+ addMarker(lat,lon, s.stop_name + " - " + s.stop_desc, stopsEmojiIcon);
});
})
.catch(err => console.error("Error fetching stops:", err));
}
-// ------------------------------
-// VEHICLES
-// ------------------------------
-let trainMarkers = {};
-let filterType = "all";
-
-const filterSelect = document.getElementById("filterSelect");
-if (filterSelect) {
- filterSelect.addEventListener("change", (e) => {
- filterType = e.target.value;
- });
-}
-
-function refreshVehicles() {
- fetch(API_URL + "vehicles")
- .then(res => res.json())
- .then(data => {
- let vehicles = data.data;
-
- // Apply filter
- if (filterType === "lrt") {
- vehicles = vehicles.filter(v => LRT_LINES.includes(v.routeNum) || FRONTRUNNER.includes(v.routeNum));
- }
-
- vehicles = vehicles.filter(v => MJR_LINES.includes(v.routeNum));
-
- // Remove vehicles no longer active
- Object.keys(trainMarkers).forEach(id => {
- if (!vehicles.find(v => v.vehicleId == id)) {
- map.removeLayer(trainMarkers[id]);
- delete trainMarkers[id];
- }
- });
-
- vehicles.forEach(v => {
- const { vehicleId, routeNum, routeName, speed, bearing } = v;
- const lat = v.location.latitude;
- const lon = v.location.longitude;
- const icon = buildIcon(ROUTE_STYLES[routeNum].train, bearing);
-
- if (!trainMarkers[vehicleId]) {
- const marker = L.marker([lat, lon], { icon })
- .bindPopup(`${routeName}
Vehicle ${vehicleId}
Speed: ${(speed*2.23694).toFixed(1)} mph`)
- .addTo(map);
- marker.vehicleData = v;
- trainMarkers[vehicleId] = marker;
- } else {
- if (trainMarkers[vehicleId].slideTo) {
- trainMarkers[vehicleId].slideTo([lat, lon], { duration: 1000 });
- } else {
- trainMarkers[vehicleId].setLatLng([lat, lon]);
- }
- trainMarkers[vehicleId].setIcon(icon);
- trainMarkers[vehicleId].vehicleData = v;
- }
- });
- })
- .catch(err => console.error("Error refreshing vehicles:", err));
-}
-
-// ------------------------------
-// USER LOCATION & TRAIN DETECTION
-// ------------------------------
-let userMarker, accuracyCircle;
-const ON_BOARD_RADIUS = 50; // meters
-
-if (navigator.geolocation) {
- navigator.geolocation.getCurrentPosition(
- (position) => {
- const lat = position.coords.latitude;
- const lon = position.coords.longitude;
-
- userMarker = L.circleMarker([lat, lon], {
- radius: 8,
- fillColor: "#007AFF",
- color: "#fff",
- weight: 2,
- opacity: 1,
- fillOpacity: 0.9
- }).addTo(map);
- userMarker.bindPopup("You are here").openPopup();
-
- accuracyCircle = L.circle([lat, lon], {
- radius: position.coords.accuracy,
- color: "#007AFF",
- fillColor: "#007AFF",
- fillOpacity: 0.2
- }).addTo(map);
-
- map.setView([lat, lon], 13);
- },
- (err) => console.warn("Geolocation error:", err.message),
- { enableHighAccuracy: true, timeout: 500, maximumAge: 0 }
- );
-
- navigator.geolocation.watchPosition(
- (position) => {
- const userLat = position.coords.latitude;
- const userLon = position.coords.longitude;
-
- if (userMarker) userMarker.setLatLng([userLat, userLon]);
- if (accuracyCircle) accuracyCircle.setLatLng([userLat, userLon]).setRadius(position.coords.accuracy);
-
- // Detect if user is on a train
- const vehicles = Object.values(trainMarkers).map(m => m.vehicleData);
- let closest = null, minDistance = Infinity;
-
- vehicles.forEach(v => {
- const distance = map.distance([userLat, userLon], [v.location.latitude, v.location.longitude]);
- if (distance < minDistance) {
- minDistance = distance;
- closest = v;
- }
- });
-
- if (closest && minDistance <= ON_BOARD_RADIUS) {
- console.log(`User is on vehicle ${closest.vehicleId} (${closest.routeNum})`);
- }
- },
- (err) => console.warn("Geolocation watch error:", err.message),
- { enableHighAccuracy: true, maximumAge: 0 }
- );
-} else {
- console.warn("Geolocation not supported by this browser.");
-}
-
-// ------------------------------
-// INITIAL LOAD
-// ------------------------------
-drawLines();
-MJR_LINES.forEach(r => getStopsByRoute(r));
-refreshVehicles();
-setInterval(refreshVehicles, 1000);
+getStopsByRoute("701");
+getTrainsByRoute();
diff --git a/public/index.html b/public/index.html
index 62de5ee..d9ce6b5 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,9 +1,9 @@