基于Google Map绘制多边形查询区域内车辆轨迹的实现方案咨询
Got it, let's walk through how to build this feature without sending your polygon data to PostgreSQL—perfect for keeping your security and data management concerns in check. Here's a practical, step-by-step approach tailored to your setup:
First, you'll use the Google Maps Drawing Library to let users draw polygons on your map. This gives you access to the polygon's coordinate data directly in the browser, no database upload needed.
// Initialize your Google Map let map; function initMap() { map = new google.maps.Map(document.getElementById("map"), { center: { lat: 37.7749, lng: -122.4194 }, // Replace with your default map center zoom: 12, }); // Set up the drawing manager for polygons const drawingManager = new google.maps.drawing.DrawingManager({ drawingMode: google.maps.drawing.OverlayType.POLYGON, drawingControl: true, drawingControlOptions: { position: google.maps.ControlPosition.TOP_CENTER, drawingModes: [google.maps.drawing.OverlayType.POLYGON], }, polygonOptions: { editable: true, fillColor: "#ff4444", fillOpacity: 0.3, strokeColor: "#cc0000", strokeWeight: 2, }, }); drawingManager.setMap(map); // Listen for when a polygon is finished being drawn google.maps.event.addListener(drawingManager, "polygoncomplete", (polygon) => { // Extract the polygon's coordinates as a plain JS array const polygonCoords = polygon.getPath().getArray().map(coord => ({ lat: coord.lat(), lng: coord.lng() })); // Trigger the vehicle query with these coordinates findVehiclesInPolygon(polygonCoords); }); }
Since you already have all 7 days of vehicle latitude/longitude data in the frontend, you'll use geometric algorithms to check which vehicles' paths intersect or pass through the polygon. We'll use the ray-casting algorithm for point-in-polygon checks, plus segment intersection logic for path crossings.
// Ray-casting algorithm to check if a single point is inside the polygon function pointInPolygon(point, polygon) { let isInside = false; const { lat: y, lng: x } = point; for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { const { lat: yi, lng: xi } = polygon[i]; const { lat: yj, lng: xj } = polygon[j]; const intersects = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); if (intersects) isInside = !isInside; } return isInside; } // Check if a line segment intersects any edge of the polygon function segmentIntersectsPolygon(start, end, polygon) { for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) { const polyStart = polygon[i]; const polyEnd = polygon[j]; if (doSegmentsIntersect(start, end, polyStart, polyEnd)) return true; } return false; } // Helper to check if two line segments intersect function doSegmentsIntersect(a1, a2, b1, b2) { const ccw = (A, B, C) => (B.lng - A.lng) * (C.lat - A.lat) - (B.lat - A.lat) * (C.lng - A.lng); const ccw1 = ccw(a1, a2, b1); const ccw2 = ccw(a1, a2, b2); const ccw3 = ccw(b1, b2, a1); const ccw4 = ccw(b1, b2, a2); // Check if segments cross each other if ((ccw1 * ccw2 < 0) && (ccw3 * ccw4 < 0)) return true; // Check if any endpoint lies on the other segment if (ccw1 === 0 && isPointOnSegment(b1, a1, a2)) return true; if (ccw2 === 0 && isPointOnSegment(b2, a1, a2)) return true; if (ccw3 === 0 && isPointOnSegment(a1, b1, b2)) return true; if (ccw4 === 0 && isPointOnSegment(a2, b1, b2)) return true; return false; } // Helper to check if a point lies on a line segment function isPointOnSegment(point, segStart, segEnd) { return ( Math.min(segStart.lng, segEnd.lng) <= point.lng && point.lng <= Math.max(segStart.lng, segEnd.lng) && Math.min(segStart.lat, segEnd.lat) <= point.lat && point.lat <= Math.max(segStart.lat, segEnd.lat) ); } // Check if a vehicle's entire track intersects the polygon function trackIntersectsPolygon(vehicleTrack, polygon) { // First check if any point in the track is inside the polygon for (const point of vehicleTrack.points) { if (pointInPolygon(point, polygon)) return true; } // Then check if any segment of the track crosses the polygon boundary for (let i = 0; i < vehicleTrack.points.length - 1; i++) { const startPoint = vehicleTrack.points[i]; const endPoint = vehicleTrack.points[i + 1]; if (segmentIntersectsPolygon(startPoint, endPoint, polygon)) return true; } return false; }
First, fetch all your vehicle track data from the backend on page load. Then, when a polygon is drawn, filter the tracks to find matches and display results.
// Store all vehicle tracks here (populated on page load) let allVehicleTracks = []; // Fetch vehicle data from your backend API async function loadVehicleTracks() { try { const response = await fetch("/api/vehicle-tracks"); // Replace with your actual API endpoint allVehicleTracks = await response.json(); console.log("Loaded vehicle tracks:", allVehicleTracks.length); } catch (err) { console.error("Failed to load vehicle tracks:", err); } } // Filter and display vehicles that passed through the polygon function findVehiclesInPolygon(polygonCoords) { const matchingVehicles = allVehicleTracks.filter(track => trackIntersectsPolygon(track, polygonCoords) ); // Update your UI with results displayResults(matchingVehicles); } // Render matching vehicles to the dashboard function displayResults(vehicles) { const resultsContainer = document.getElementById("vehicle-results"); resultsContainer.innerHTML = ""; if (vehicles.length === 0) { resultsContainer.innerHTML = "<p>No vehicles passed through the selected area.</p>"; return; } const resultsList = document.createElement("ul"); vehicles.forEach(vehicle => { const listItem = document.createElement("li"); listItem.textContent = `Vehicle ID: ${vehicle.vehicleId} | Total Points: ${vehicle.points.length}`; resultsList.appendChild(listItem); }); resultsContainer.appendChild(resultsList); // Optional: Highlight matching tracks on the map vehicles.forEach(vehicle => { const trackPath = new google.maps.Polyline({ path: vehicle.points, geodesic: true, strokeColor: "#00ff00", strokeOpacity: 1.0, strokeWeight: 2, }); trackPath.setMap(map); }); } // Load tracks when the page loads window.onload = () => { initMap(); loadVehicleTracks(); };
- Performance: If you have a huge dataset (thousands of tracks), use Web Workers to run the geometric checks in the background so your UI doesn't freeze. You can also pre-simplify track data by removing redundant points.
- Polygon Edits: Add listeners for polygon edits (
set_at,insert_atevents) to re-run the query when users adjust the polygon. - Coordinate Consistency: Ensure your vehicle data uses the WGS84 coordinate system (same as Google Maps) to avoid accuracy issues.
内容的提问来源于stack exchange,提问作者user2315860

