Download assets/map.js from build-small-hackathon/site-intelligence-studio: direct link, hf CLI and curl.
- Browser
- Download file 8.87 kB
-
https://huggingface.co/spaces/build-small-hackathon/site-intelligence-studio/resolve/main/assets/map.js
- Command line
-
hf download hf://spaces/build-small-hackathon/site-intelligence-studio/assets/map.js
-
curl -L -o map.js https://huggingface.co/spaces/build-small-hackathon/site-intelligence-studio/resolve/main/assets/map.js
8.87 kB
| function initSiteMap() { | |
| const mapNode = document.getElementById("site-map"); | |
| if (!mapNode || mapNode.dataset.ready === "true" || typeof L === "undefined") return false; | |
| mapNode.dataset.ready = "true"; | |
| const map = L.map("site-map", { zoomControl: true }).setView([20.92, 70.36], 13); | |
| const tileLayer = L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { | |
| maxZoom: 19, | |
| attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' | |
| }).addTo(map); | |
| let mode = "pin"; | |
| let points = []; | |
| let markers = []; | |
| let shapeLayer = null; | |
| let pinMarker = null; | |
| let radiusCircle = null; | |
| const status = document.getElementById("map-status"); | |
| const radiusSelect = document.getElementById("radius-select"); | |
| const buttons = { | |
| polygon: document.getElementById("mode-polygon"), | |
| rectangle: document.getElementById("mode-rectangle"), | |
| pin: document.getElementById("mode-pin") | |
| }; | |
| function setStatus(text) { | |
| if (status) status.textContent = text; | |
| } | |
| let tileErrorCount = 0; | |
| let tileLoadCount = 0; | |
| tileLayer.on("tileload", () => { | |
| tileLoadCount += 1; | |
| }); | |
| tileLayer.on("tileerror", () => { | |
| tileErrorCount += 1; | |
| if (tileErrorCount === 3) { | |
| setStatus("Base map tiles are unavailable in this browser/network. Drawing still works; verify the boundary with CAD/KML/GeoJSON or coordinates."); | |
| } | |
| }); | |
| setTimeout(() => { | |
| if (tileLoadCount === 0) { | |
| setStatus("Base map tiles are unavailable in this browser/network. Drawing still works; verify the boundary with CAD/KML/GeoJSON or coordinates."); | |
| } | |
| }, 3500); | |
| function setMode(nextMode) { | |
| mode = nextMode; | |
| Object.entries(buttons).forEach(([key, btn]) => { | |
| if (btn) btn.classList.toggle("active", key === mode); | |
| }); | |
| if (mode === "polygon") setStatus("Polygon mode: click corners, then Finish."); | |
| if (mode === "rectangle") setStatus("Rectangle mode: click two opposite corners."); | |
| if (mode === "pin") setStatus("Pin mode: click site center and choose radius."); | |
| } | |
| function setGradioState(state) { | |
| const textarea = document.querySelector("#map_state textarea"); | |
| if (!textarea) return; | |
| textarea.value = JSON.stringify(state); | |
| textarea.dispatchEvent(new Event("input", { bubbles: true })); | |
| textarea.dispatchEvent(new Event("change", { bubbles: true })); | |
| } | |
| function clearDrawing(pushState = true) { | |
| markers.forEach((m) => map.removeLayer(m)); | |
| markers = []; | |
| points = []; | |
| if (shapeLayer) map.removeLayer(shapeLayer); | |
| if (pinMarker) map.removeLayer(pinMarker); | |
| if (radiusCircle) map.removeLayer(radiusCircle); | |
| shapeLayer = null; | |
| pinMarker = null; | |
| radiusCircle = null; | |
| if (pushState) setGradioState({}); | |
| setStatus("Selection cleared."); | |
| } | |
| function drawPolygonPreview(closeIt = false) { | |
| if (shapeLayer) map.removeLayer(shapeLayer); | |
| if (points.length < 2) return; | |
| const latLngs = closeIt ? [...points, points[0]] : points; | |
| shapeLayer = L.polyline(latLngs, { color: "#123b51", weight: 3 }).addTo(map); | |
| if (closeIt && points.length >= 3) { | |
| map.removeLayer(shapeLayer); | |
| shapeLayer = L.polygon(points, { color: "#123b51", fillColor: "#0f766e", fillOpacity: 0.22, weight: 3 }).addTo(map); | |
| } | |
| } | |
| function finishPolygon() { | |
| if (points.length < 3) { | |
| setStatus("A polygon needs at least three points."); | |
| return; | |
| } | |
| drawPolygonPreview(true); | |
| const ring = points.map((p) => [p.lng, p.lat]); | |
| ring.push([points[0].lng, points[0].lat]); | |
| setGradioState({ | |
| mode: "polygon", | |
| geometry: { type: "Polygon", coordinates: [ring] } | |
| }); | |
| setStatus(`Polygon finished with ${points.length} points. Drawn boundaries are approximate.`); | |
| } | |
| function finishRectangle(a, b) { | |
| clearDrawing(false); | |
| const corners = [ | |
| L.latLng(a.lat, a.lng), | |
| L.latLng(a.lat, b.lng), | |
| L.latLng(b.lat, b.lng), | |
| L.latLng(b.lat, a.lng) | |
| ]; | |
| points = corners; | |
| shapeLayer = L.polygon(corners, { color: "#123b51", fillColor: "#0f766e", fillOpacity: 0.22, weight: 3 }).addTo(map); | |
| const ring = corners.map((p) => [p.lng, p.lat]); | |
| ring.push([corners[0].lng, corners[0].lat]); | |
| setGradioState({ | |
| mode: "rectangle", | |
| geometry: { type: "Polygon", coordinates: [ring] } | |
| }); | |
| setStatus("Rectangle selection finished. Drawn boundaries are approximate."); | |
| } | |
| function placePin(latlng) { | |
| if (pinMarker) map.removeLayer(pinMarker); | |
| if (radiusCircle) map.removeLayer(radiusCircle); | |
| pinMarker = L.circleMarker(latlng, { | |
| radius: 7, | |
| color: "#123b51", | |
| fillColor: "#ffffff", | |
| fillOpacity: 1, | |
| weight: 3 | |
| }).addTo(map); | |
| const radius = Number(radiusSelect ? radiusSelect.value : 250); | |
| radiusCircle = L.circle(latlng, { radius, color: "#123b51", fillColor: "#0f766e", fillOpacity: 0.16 }).addTo(map); | |
| setGradioState({ | |
| mode: "pin_radius", | |
| lat: latlng.lat, | |
| lon: latlng.lng, | |
| radius_m: radius | |
| }); | |
| setStatus(`Pin set at ${latlng.lat.toFixed(6)}, ${latlng.lng.toFixed(6)} with ${radius} m radius.`); | |
| } | |
| map.on("click", (event) => { | |
| if (mode === "pin") { | |
| clearDrawing(false); | |
| placePin(event.latlng); | |
| return; | |
| } | |
| if (mode === "polygon") { | |
| const marker = L.circleMarker(event.latlng, { radius: 5, color: "#123b51", fillColor: "#c88920", fillOpacity: 1 }).addTo(map); | |
| markers.push(marker); | |
| points.push(event.latlng); | |
| drawPolygonPreview(false); | |
| setStatus(`Polygon point ${points.length}: ${event.latlng.lat.toFixed(6)}, ${event.latlng.lng.toFixed(6)}`); | |
| return; | |
| } | |
| if (mode === "rectangle") { | |
| points.push(event.latlng); | |
| markers.push(L.circleMarker(event.latlng, { radius: 5, color: "#123b51", fillColor: "#c88920", fillOpacity: 1 }).addTo(map)); | |
| if (points.length === 2) finishRectangle(points[0], points[1]); | |
| else setStatus("Rectangle mode: click opposite corner."); | |
| } | |
| }); | |
| if (buttons.polygon) buttons.polygon.addEventListener("click", () => { clearDrawing(); setMode("polygon"); }); | |
| if (buttons.rectangle) buttons.rectangle.addEventListener("click", () => { clearDrawing(); setMode("rectangle"); }); | |
| if (buttons.pin) buttons.pin.addEventListener("click", () => { clearDrawing(); setMode("pin"); }); | |
| const finishBtn = document.getElementById("finish-shape"); | |
| if (finishBtn) finishBtn.addEventListener("click", finishPolygon); | |
| const undoBtn = document.getElementById("undo-point"); | |
| if (undoBtn) undoBtn.addEventListener("click", () => { | |
| if (points.length > 0) { | |
| points.pop(); | |
| const marker = markers.pop(); | |
| if (marker) map.removeLayer(marker); | |
| drawPolygonPreview(false); | |
| setStatus(`Removed last point. ${points.length} points remain.`); | |
| } | |
| }); | |
| const clearBtn = document.getElementById("clear-map"); | |
| if (clearBtn) clearBtn.addEventListener("click", () => clearDrawing()); | |
| if (radiusSelect) radiusSelect.addEventListener("change", () => { | |
| if (pinMarker) placePin(pinMarker.getLatLng()); | |
| }); | |
| setMode("pin"); | |
| setTimeout(() => map.invalidateSize(), 250); | |
| return true; | |
| } | |
| let leafletFallbackRequested = false; | |
| function requestLeafletFallback() { | |
| if (leafletFallbackRequested) return; | |
| leafletFallbackRequested = true; | |
| const status = document.getElementById("map-status"); | |
| if (status) status.textContent = "Map library is taking longer than expected. Trying fallback map assets..."; | |
| const css = document.createElement("link"); | |
| css.rel = "stylesheet"; | |
| css.href = "https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css"; | |
| document.head.appendChild(css); | |
| const script = document.createElement("script"); | |
| script.src = "https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js"; | |
| script.onload = () => initSiteMap(); | |
| script.onerror = () => { | |
| if (status) { | |
| status.textContent = "Map library could not load. Use the lat/lon field or upload DXF/KML/GeoJSON, then generate the report."; | |
| } | |
| }; | |
| document.head.appendChild(script); | |
| } | |
| function waitForLeafletAndInit(attempt = 0) { | |
| const hasMapNode = Boolean(document.getElementById("site-map")); | |
| if (typeof L !== "undefined" && hasMapNode) { | |
| if (initSiteMap()) return; | |
| } | |
| if (typeof L !== "undefined" && !hasMapNode) { | |
| const status = document.getElementById("map-status"); | |
| if (status) status.textContent = "Map UI is loading..."; | |
| } | |
| if (attempt === 6 && typeof L === "undefined") requestLeafletFallback(); | |
| if (attempt > 80) { | |
| const status = document.getElementById("map-status"); | |
| if (status) { | |
| status.textContent = "Map could not initialize. Use the lat/lon field or upload DXF/KML/GeoJSON, then generate the report."; | |
| } | |
| return; | |
| } | |
| setTimeout(() => waitForLeafletAndInit(attempt + 1), 250); | |
| } | |
| waitForLeafletAndInit(); | |