Created
June 10, 2026 20:46
-
-
Save chaosifier/ced25440e582a1970fd469bfb82b524f to your computer and use it in GitHub Desktop.
Geocode Bounding Box Visualizer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!doctype html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>Geocode Bounding Box Visualizer</title> | |
| <style> | |
| body { | |
| font-family: Arial, sans-serif; | |
| margin: 20px; | |
| background-color: #f4f4f9; | |
| } | |
| .container { | |
| max-width: 1000px; | |
| margin: 0 auto; | |
| } | |
| .form-panel { | |
| background: white; | |
| padding: 20px; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | |
| margin-bottom: 20px; | |
| } | |
| .input-group { | |
| display: grid; | |
| grid-template-columns: 2fr 1fr 1fr 1fr; | |
| gap: 10px; | |
| margin-bottom: 15px; | |
| } | |
| @media (max-width: 600px) { | |
| .input-group { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| input, | |
| button { | |
| padding: 10px; | |
| border: 1px solid #ccc; | |
| border-radius: 4px; | |
| font-size: 14px; | |
| } | |
| button { | |
| background-color: #1a73e8; | |
| color: white; | |
| border: none; | |
| cursor: pointer; | |
| font-weight: bold; | |
| } | |
| button:hover { | |
| background-color: #1557b0; | |
| } | |
| #map { | |
| width: 100%; | |
| height: 500px; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | |
| } | |
| .stats-panel { | |
| margin-top: 15px; | |
| background: #e8f0fe; | |
| padding: 15px; | |
| border-radius: 4px; | |
| display: none; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h2>Geocode Bounding Box Visualizer</h2> | |
| <div class="form-panel"> | |
| <div class="input-group"> | |
| <input | |
| type="text" | |
| id="street" | |
| placeholder="Street Address" | |
| value="1201 Hidden Ridge Dr" | |
| /> | |
| <input type="text" id="city" placeholder="City" value="Irving" /> | |
| <input | |
| type="text" | |
| id="state" | |
| placeholder="State (e.g. TX)" | |
| value="TX" | |
| /> | |
| <input type="text" id="zip" placeholder="ZIP Code" value="75038" /> | |
| </div> | |
| <button onclick="geocodeAddress()">Geocode & Map Bounds</button> | |
| </div> | |
| <div id="stats" class="stats-panel"></div> | |
| <div id="map"></div> | |
| </div> | |
| <script> | |
| let map; | |
| let geocoder; | |
| let currentMarker = null; | |
| let currentRectangle = null; | |
| let currentCircle = null; | |
| // Initialize the Map | |
| function initMap() { | |
| geocoder = new google.maps.Geocoder(); | |
| // Default map center (Dallas/Fort Worth area) | |
| map = new google.maps.Map(document.getElementById("map"), { | |
| zoom: 12, | |
| center: { lat: 32.848, lng: -97.241 }, | |
| }); | |
| } | |
| function geocodeAddress() { | |
| // 1. Check if the SDK is actually ready | |
| if (typeof google === "undefined" || !google.maps) { | |
| alert( | |
| "Google Maps SDK is still loading. Please wait a moment and try again.", | |
| ); | |
| return; | |
| } | |
| // 2. Instantly initialize the geocoder if it's missing | |
| if (!geocoder) { | |
| geocoder = new google.maps.Geocoder(); | |
| } | |
| const street = document.getElementById("street").value; | |
| const city = document.getElementById("city").value; | |
| const state = document.getElementById("state").value; | |
| const zip = document.getElementById("zip").value; | |
| const fullAddress = `${street}, ${city}, ${state} ${zip}`; | |
| // 3. Safe to call now! | |
| geocoder.geocode({ address: fullAddress }, (results, status) => { | |
| if (status === "OK" && results[0]) { | |
| const result = results[0]; | |
| const location = result.geometry.location; | |
| const viewport = result.geometry.viewport; | |
| const locType = result.geometry.location_type; | |
| const isPartial = result.partial_match ? "TRUE" : "FALSE"; | |
| // Clear previous drawings | |
| if (currentMarker) currentMarker.setMap(null); | |
| if (currentRectangle) currentRectangle.setMap(null); | |
| if (currentCircle) currentCircle.setMap(null); | |
| // 1. Center map and drop marker on the coordinates | |
| map.setCenter(location); | |
| currentMarker = new google.maps.Marker({ | |
| map: map, | |
| position: location, | |
| title: "Returned Coordinate", | |
| }); | |
| // 2. Draw the exact Viewport Bounding Box (Red Rectangle) | |
| currentRectangle = new google.maps.Rectangle({ | |
| strokeColor: "#FF0000", | |
| strokeOpacity: 0.8, | |
| strokeWeight: 2, | |
| fillColor: "#FF0000", | |
| fillOpacity: 0.1, | |
| map: map, | |
| bounds: viewport, | |
| }); | |
| // 3. Compute dynamic uncertainty radius (using previous math module) | |
| const radiusMeters = calculatePredictiveRadius(result); | |
| // 4. Draw predicted certainty radius (Blue Circle) | |
| currentCircle = new google.maps.Circle({ | |
| strokeColor: "#1A73E8", | |
| strokeOpacity: 0.7, | |
| strokeWeight: 1, | |
| fillColor: "#1A73E8", | |
| fillOpacity: 0.15, | |
| map: map, | |
| center: location, | |
| radius: radiusMeters, | |
| }); | |
| // 5. Fit map view to fully contain the bounding box bounds | |
| map.fitBounds(viewport); | |
| // Display tracking data panels | |
| const statsDiv = document.getElementById("stats"); | |
| statsDiv.style.display = "block"; | |
| statsDiv.innerHTML = ` | |
| <strong>Formatted Address Returned:</strong> ${result.formatted_address}<br> | |
| <strong>Location Type:</strong> <span style="color:#d93025; font-weight:bold;">${locType}</span><br> | |
| <strong>Partial Match Flag:</strong> ${isPartial}<br> | |
| <strong>Calculated Uncertainty Radius:</strong> ${radiusMeters.toFixed(2)} meters | |
| `; | |
| } else { | |
| alert("Geocode failed due to: " + status); | |
| } | |
| }); | |
| } | |
| // Helper math calculation module | |
| function calculatePredictiveRadius(result) { | |
| const lat1 = result.geometry.location.lat(); | |
| const lng1 = result.geometry.location.lng(); | |
| const lat2 = result.geometry.viewport.getNorthEast().lat(); | |
| const lng2 = result.geometry.viewport.getNorthEast().lng(); | |
| const R = 6371000; | |
| const dLat = ((lat2 - lat1) * Math.PI) / 180; | |
| const dLng = ((lng2 - lng1) * Math.PI) / 180; | |
| const a = | |
| Math.sin(dLat / 2) * Math.sin(dLat / 2) + | |
| Math.cos((lat1 * Math.PI) / 180) * | |
| Math.cos((lat2 * Math.PI) / 180) * | |
| Math.sin(dLng / 2) * | |
| Math.sin(dLng / 2); | |
| const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); | |
| let baseRadius = R * c; | |
| const locType = result.geometry.location_type; | |
| const isPartial = result.partial_match || false; | |
| if (locType === "ROOFTOP") { | |
| return isPartial ? 75 : 20; | |
| } else if (locType === "RANGE_INTERPOLATED") { | |
| return baseRadius; | |
| } else if (locType === "GEOMETRIC_CENTER") { | |
| return baseRadius * 1.5; | |
| } else if (locType === "APPROXIMATE") { | |
| return Math.max(baseRadius, 5000); | |
| } | |
| return baseRadius; | |
| } | |
| </script> | |
| <!-- Replace YOUR_API_KEY with your actual Google Maps API key --> | |
| <script | |
| src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap" | |
| async | |
| defer | |
| ></script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment