Begin sharing code with search page

This includes code for geocoding, building a map, and getting the user's
location.
This commit is contained in:
Eli Ribble 2026-01-09 23:32:39 +00:00
parent 28e4e88794
commit acaeb2129e
No known key found for this signature in database
6 changed files with 216 additions and 163 deletions

View file

@ -3,10 +3,13 @@ package publicreport
import (
"net/http"
"github.com/Gleipnir-Technology/nidus-sync/config"
"github.com/Gleipnir-Technology/nidus-sync/htmlpage"
)
type ContextSearch struct{}
type ContextSearch struct {
MapboxToken string
}
var (
Search = buildTemplate("search", "base")
@ -16,6 +19,8 @@ func getSearch(w http.ResponseWriter, r *http.Request) {
htmlpage.RenderOrError(
w,
Search,
ContextSearch{},
ContextSearch{
MapboxToken: config.MapboxToken,
},
)
}

View file

@ -0,0 +1,13 @@
async function geocodeReverse(MAPBOX_ACCESS_TOKEN, lngLat) {
const url = `https://api.mapbox.com/search/geocode/v6/reverse?longitude=${lngLat.lng}&latitude=${lngLat.lat}&access_token=${MAPBOX_ACCESS_TOKEN}`
const response = await fetch(url);
const data = await response.json();
console.log("reverse geocoded to", data);
if (data.features.length == 0) {
console.warn("No results for reverse geocode");
return;
}
const match = data.features[0];
displaySelectedLocation(match);
setLocationInputs(match);
}

View file

@ -0,0 +1,23 @@
function getGeolocation(options) {
return new Promise((resolve, reject) => {
// Check if geolocation is supported by the browser
if (!navigator.geolocation) {
reject(new Error("Geolocation is not supported by your browser"));
return;
}
// Default options if none provided
const geolocationOptions = options || {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
// Call the geolocation API
navigator.geolocation.getCurrentPosition(
position => resolve(position),
error => reject(error),
geolocationOptions
);
});
}

View file

@ -0,0 +1,62 @@
var map = null;
var markers = [];
function mapAddMarker(coords) {
const mapContainer = document.getElementById("map-container");
const marker = new mapboxgl.Marker({
color: "#FF0000",
draggable: true
}).setLngLat(coords).addTo(map);
marker.on('dragend', function(e) {
const markerDraggedEvent = new CustomEvent("markerdragend", {
detail: {
marker: marker
}
});
mapContainer.dispatchEvent(markerDraggedEvent);
});
markers.push(marker);
}
function mapLoad(MAPBOX_ACCESS_TOKEN) {
return new Promise((resolve, reject) => {
console.log("Setting up the map...");
mapboxgl.accessToken = MAPBOX_ACCESS_TOKEN;
map = new mapboxgl.Map({
container: "map",
center: {
lat: 36.2,
lng: -119.2
},
style: 'mapbox://styles/mapbox/streets-v12', // style URL
zoom: 15,
});
map.addControl(new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true
},
trackUserLocation: true,
showUserHeading: true
}));
map.addControl(new mapboxgl.NavigationControl());
map.on("load", function() {
console.log("Map loaded.");
resolve(map);
});
});
}
function mapJumpTo(args) {
map.jumpTo(args);
}
function mapSetMarker(coords) {
console.log("Setting map marker", coords);
map.jumpTo({
center: coords,
zoom: 14,
});
markers.forEach((marker) => marker.remove());
mapAddMarker(coords);
}

View file

@ -5,22 +5,15 @@
{{template "location-geocode-header" .}}
<script src='https://api.mapbox.com/mapbox-gl-js/v3.17.0-beta.1/mapbox-gl.js'></script>
<link href='https://api.mapbox.com/mapbox-gl-js/v3.17.0-beta.1/mapbox-gl.css' rel='stylesheet' />
<script src="/static/js/geocode.js"></script>
<script src="/static/js/location.js"></script>
<script src="/static/js/map.js"></script>
{{template "photo-upload-header"}}
<style>
.district-logo {
max-height: 80px;
width: auto;
}
.map-container {
height: 500px;
background-color: #e9ecef;
border-radius: 5px;
border: 1px solid #dee2e6;
display: flex;
align-items: center;
justify-content: center;
margin-top: 10px;
}
.form-section {
margin-bottom: 2.5rem;
padding-bottom: 2rem;
@ -97,6 +90,7 @@
}
</style>
<script>
const MAPBOX_ACCESS_TOKEN = '{{.MapboxToken}}';
function handlePhotoSelection() {
const photoInput = document.getElementById('photos');
const photoPreviewContainer = document.getElementById('photoPreviewContainer');
@ -163,6 +157,12 @@ function handlePhotoSelection() {
}
}
function onMapMarkerDragEnd(marker) {
const lngLat = marker.getLngLat();
//displaySelectedCoordinates(lngLat);
geocodeReverse(MAPBOX_ACCESS_TOKEN, lngLat);
}
document.addEventListener('DOMContentLoaded', function() {
// Elements
const photoInput = document.getElementById('photos');
@ -190,137 +190,51 @@ document.addEventListener('DOMContentLoaded', function() {
handleFiles(e.dataTransfer.files);
}
});
onLoadMap();
mapLoad(MAPBOX_ACCESS_TOKEN).then(() => {
getGeolocation({
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
}).then(position => {
mapJumpTo({
center: {
lng: position.coords.longitude,
lat: position.coords.latitude,
},
zoom: 14,
});
mapAddMarker([
position.coords.longitude,
position.coords.latitude,
]);
geocodeReverse(MAPBOX_ACCESS_TOKEN, {
lat: position.coords.latitude,
lng: position.coords.longitude,
});
}).catch(error => {
console.log("location error", error);
})
})
let mapZoom = document.getElementById('map-zoom');
map.on("zoomend", function(e) {
mapZoom.value = e.target.getZoom();
});
const mapContainer = document.getElementById("map-container");
mapContainer.addEventListener("markerdragend", (e) => {
onMapMarkerDragEnd(e.detail.marker);
});
const suggestionsContainer = document.getElementById('suggestions');
suggestionsContainer.addEventListener("locationselected", (e) => {
setMapMarker(e.detail.coordinates);
mapSetMarker(e.detail.coordinates);
});
});
var map = null;
var markers = [];
function setMapMarker(coords) {
console.log("Setting map marker", coords);
map.jumpTo({
center: coords,
zoom: 14,
});
markers.forEach((marker) => marker.remove());
addMarker(coords);
}
function addMarker(coords) {
const marker = new mapboxgl.Marker({
color: "#FF0000",
draggable: true
}).setLngLat(coords).addTo(map);
marker.on('dragend', onMapMarkerDragEnd(marker));
markers.push(marker);
}
function onMapMarkerDragEnd(marker) {
return function() {
const lngLat = marker.getLngLat();
//displaySelectedCoordinates(lngLat);
reverseGeocode(lngLat);
}
}
function displaySelectedCoordinates(lngLat) {
const gpsDisplay = document.getElementById("gps-display");
gpsDisplay.classList.remove('d-none');
longitude.textContent = lngLat.lng;
latitude.textContent = lngLat.lat;
}
async function reverseGeocode(lngLat) {
const MAPBOX_ACCESS_TOKEN = '{{.MapboxToken}}';
const url = `https://api.mapbox.com/search/geocode/v6/reverse?longitude=${lngLat.lng}&latitude=${lngLat.lat}&access_token=${MAPBOX_ACCESS_TOKEN}`
const response = await fetch(url);
const data = await response.json();
console.log("reverse geocoded to", data);
if (data.features.length == 0) {
console.warn("No results for reverse geocode");
return;
}
const match = data.features[0];
displaySelectedLocation(match);
setLocationInputs(match);
}
function onLoadMap() {
const MAPBOX_ACCESS_TOKEN = '{{.MapboxToken}}';
let mapZoom = document.getElementById('map-zoom');
console.log("Setting up the map...");
mapboxgl.accessToken = MAPBOX_ACCESS_TOKEN;
map = new mapboxgl.Map({
container: "map",
center: {
lat: 36.2,
lng: -119.2
},
style: 'mapbox://styles/mapbox/streets-v12', // style URL
zoom: 15,
});
map.addControl(new mapboxgl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true
},
trackUserLocation: true,
showUserHeading: true
}));
map.addControl(new mapboxgl.NavigationControl());
map.on("load", function() {
console.log("Map post-load...");
updateMapWithLocation(map);
console.log("Map post-load done.");
});
map.on("zoomend", function(e) {
mapZoom.value = e.target.getZoom();
});
console.log("Map init done.");
}
function updateMapWithLocation(map) {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
// on success
function(position) {
console.log("Got location", position);
map.jumpTo({
center: {
lng: position.coords.longitude,
lat: position.coords.latitude,
},
zoom: 14,
});
addMarker([
position.coords.longitude,
position.coords.latitude,
]);
reverseGeocode({
lat: position.coords.latitude,
lng: position.coords.longitude,
});
},
// on error
function(error) {
switch (error.code) {
case error.PERMISSION_DENIED:
console.log("permission denied");
break;
case error.POSITION_UNAVAILABLE:
console.log("location unavailable");
break;
case error.TIMEOUT:
console.log("request timed out");
break;
}
},
// options
{
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
}
);
} else {
console.log("location is not supported");
}
}
</script>
{{end}}
{{define "content"}}
@ -386,7 +300,7 @@ function updateMapWithLocation(map) {
</div>
<p class="small text-muted mb-2">You can also click on the map to mark the location precisely</p>
<div class="map-container">
<div id="map-container" class="map-container">
<div id="map"></div>
</div>
<input type="hidden" id="map-zoom" name="map-zoom"/>

View file

@ -2,35 +2,73 @@
{{define "title"}}Status{{end}}
{{define "extraheader"}}
<script src='https://api.mapbox.com/mapbox-gl-js/v3.17.0-beta.1/mapbox-gl.js'></script>
<link href='https://api.mapbox.com/mapbox-gl-js/v3.17.0-beta.1/mapbox-gl.css' rel='stylesheet' />
<script src="/static/js/geocode.js"></script>
<script src="/static/js/location.js"></script>
<script src="/static/js/map.js"></script>
<style>
.map-container {
height: 400px;
border-radius: 5px;
margin-bottom: 20px;
}
.clickable-row {
cursor: pointer;
transition: background-color 0.15s ease-in-out;
}
.clickable-row:hover {
background-color: rgba(13, 110, 253, 0.1);
}
.report-type-badge {
font-size: 0.85rem;
}
.search-box {
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
border-radius: 8px;
}
@media (max-width: 768px) {
.map-container {
height: 300px;
}
}
.clickable-row {
cursor: pointer;
transition: background-color 0.15s ease-in-out;
}
.clickable-row:hover {
background-color: rgba(13, 110, 253, 0.1);
}
.map-container {
background-color: #e9ecef;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
height: 500px;
display: flex;
align-items: center;
justify-content: center;
margin-top: 20px;
}
#map {
height: 500px;
width:100%;
margin-bottom: 10px;
}
#map img {
max-width: none;
min-width: 0px;
height: auto;
}
.report-type-badge {
font-size: 0.85rem;
}
.search-box {
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
border-radius: 8px;
}
@media (max-width: 768px) {
.map-container {
height: 300px;
}
}
</style>
<script>
const MAPBOX_ACCESS_TOKEN = '{{.MapboxToken}}';
var markers = [];
document.addEventListener('DOMContentLoaded', function() {
mapLoad(MAPBOX_ACCESS_TOKEN).then(() => {
getGeolocation({
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
}).then(position => {
mapJumpTo({
center: {
lng: position.coords.longitude,
lat: position.coords.latitude,
},
zoom: 13,
});
}).catch(error => {
console.error("Failed to get position", error);
});
});
});
</script>
{{end}}
@ -75,10 +113,8 @@ document.addEventListener('DOMContentLoaded', function() {
<h5 class="mb-0"><i class="fas fa-map-marked-alt me-2"></i>Reports Map</h5>
</div>
<div class="card-body p-0">
<div class="map-container">
<!-- Replace with actual map embed code -->
<iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d12000!2d-122.4194!3d37.7749!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x0%3A0x0!2zMzfCsDQ2JzI5LjYiTiAxMjLCsDI1JzEwLjAiVw!5e0!3m2!1sen!2sus!4v1627309456789!5m2!1sen!2sus"
width="100%" height="100%" style="border:0;" allowfullscreen="" loading="lazy"></iframe>
<div class="map-container" id="map-container">
<div id="map"></div>
</div>
</div>
</div>