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:
parent
28e4e88794
commit
acaeb2129e
6 changed files with 216 additions and 163 deletions
|
|
@ -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,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
13
public-report/static/js/geocode.js
Normal file
13
public-report/static/js/geocode.js
Normal 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);
|
||||
}
|
||||
23
public-report/static/js/location.js
Normal file
23
public-report/static/js/location.js
Normal 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
|
||||
);
|
||||
});
|
||||
}
|
||||
62
public-report/static/js/map.js
Normal file
62
public-report/static/js/map.js
Normal 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);
|
||||
}
|
||||
|
||||
|
|
@ -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"/>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue