ortofoto-arter / index.html
KennethTM's picture
Update index.html
6d01a0c verified
<!DOCTYPE html>
<html>
<head>
<title>Matchmaking for habitater og arter</title>
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css"/>
<style>
html, body, #map {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
#downloadButton {
position: absolute;
top: 140px; /* Positioned below the infoBox */
right: 10px;
z-index: 1000;
padding: 10px;
background-color: rgba(255, 255, 255, 0.8);
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
font-family: Arial, sans-serif;
cursor: pointer;
display: none;
}
#infoBox {
position: absolute;
top: 10px;
right: 10px;
background-color: rgba(255, 255, 255, 0.8);
padding: 10px;
border-radius: 5px;
z-index: 1000;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
font-family: Arial, sans-serif;
text-align: center;
max-width: 300px;
}
/* Loading spinner styles */
.spinner {
position: absolute;
width: 40px;
height: 40px;
margin: 0;
background-color: rgba(255, 255, 255, 0.8);
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #3498db;
border-bottom-color: #3498db;
animation: spin 2s linear infinite;
z-index: 1000;
}
/* Add this if not already present */
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.spinner-container {
background: none !important;
}
/* Make sure there's no Leaflet default icon background */
.leaflet-div-icon {
background: transparent;
border: none;
}
</style>
</head>
<body>
<div id="map"></div>
<button id="downloadButton">Download GeoJSON</button>
<div id="infoBox">
<h3 style="margin: 0 0 5px 0;">Matchmaking for habitater og arter</h3>
<p style="margin: 0;">Tegn et område på kortet og få en liste med de arter som bedst matcher habitatet.</p>
<p style="margin: 0;">Udviklet af: <a href="mailto:[email protected]" style="color: #3498db; text-decoration: none;">Kenneth Thorø Martinsen</a></p>
</div>
<script>
var map = L.map('map').setView([56.2, 10.3], 7);
L.tileLayer('https://services.datafordeler.dk/GeoDanmarkOrto/orto_foraar_webm/1.0.0/WMTS/orto_foraar_webm/default/DFD_GoogleMapsCompatible/{z}/{y}/{x}.jpg?username=BJSIGPGRVW&password=Panseryrtat*56klinge', {
attribution: 'CC BY 4.0, GeoDanmark, Forårsbilleder Ortofoto, dataforsyningen.dk',
maxZoom: 19
}).addTo(map);
var drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
var drawControl = new L.Control.Draw({
draw: {
polygon: true,
polyline: false,
circle: false,
rectangle: false,
marker: false,
circlemarker: false
},
edit: {
featureGroup: drawnItems
}
});
map.addControl(drawControl);
map.on('draw:created', function (e) {
var layer = e.layer;
drawnItems.addLayer(layer);
predictAndShow(layer);
});
map.on('draw:edited', function(e){
var layers = e.layers;
layers.eachLayer(function(layer) {
predictAndShow(layer);
});
});
map.on('draw:deleted', function(e){
updateDownloadButton();
});
function predictAndShow(layer) {
var geojson = layer.toGeoJSON();
// Get the center of the polygon for placing the spinner
var bounds = layer.getBounds();
var center = bounds.getCenter();
// Create a more visible spinner with custom HTML
var spinnerHtml = '<div class="spinner" style="width: 25px; height: 25px; ' +
'border: 5px solid #f3f3f3; border-top: 5px solid #3498db; ' +
'border-radius: 50%; animation: spin 2s linear infinite;"></div>';
var spinner = L.divIcon({
html: spinnerHtml,
className: 'spinner-container',
iconSize: [50, 50],
iconAnchor: [25, 25] // Center the spinner on the point
});
// Add the spinner to the map
var loadingMarker = L.marker(center, {
icon: spinner,
interactive: false,
zIndexOffset: 1000 // Ensure spinner appears above other elements
}).addTo(map);
// Change the polygon style to indicate loading
var originalStyle = {
color: layer.options.color || '#3388ff',
fillOpacity: layer.options.fillOpacity || 0.2
};
layer.setStyle({
fillOpacity: 0.1,
color: '#aaa'
});
fetch('/predict', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ geojson: geojson })
})
.then(response => response.json())
.then(data => {
// Remove the spinner and restore original style
map.removeLayer(loadingMarker);
layer.setStyle(originalStyle);
var predictions = data.predictions;
var popupContent = "<b>Arter:</b><br>" + data.predictions_formatted;
// Store the popup content in the layer for later use
layer.popupContent = popupContent;
// Only add click handler, no mouseover/hover effects
layer.on('click', function(e) {
if (!layer._popup || !map.hasLayer(layer._popup)) {
var popup = L.popup({
closeButton: true,
autoClose: false,
closeOnEscapeKey: false,
closeOnClick: false
})
.setLatLng(e.latlng)
.setContent(layer.popupContent);
layer.bindPopup(popup).openPopup();
} else {
layer.closePopup();
}
});
// Ensure the feature object exists before assigning to it
if (!layer.feature) {
layer.feature = {};
}
if (!layer.feature.properties) {
layer.feature.properties = {};
}
// Store both the raw predictions and formatted text
layer.feature.properties.arter = predictions;
updateDownloadButton();
})
.catch(error => {
// Remove the spinner and restore original style on error
map.removeLayer(loadingMarker);
layer.setStyle(originalStyle);
console.error('Error:', error);
alert('Prediction failed.');
});
}
document.getElementById('downloadButton').addEventListener('click', function() {
var features = [];
// Collect all drawn layers with their prediction data
drawnItems.eachLayer(function(layer){
// Get the GeoJSON representation of the layer
var featureGeoJSON = layer.toGeoJSON();
// Ensure type is explicitly set to "Feature"
featureGeoJSON.type = "Feature";
// Make sure we have properties object
if (!featureGeoJSON.properties) {
featureGeoJSON.properties = {};
}
// Ensure prediction data is included in properties
if (layer.feature && layer.feature.properties && layer.feature.properties.arter) {
featureGeoJSON.properties.arter = layer.feature.properties.arter;
}
features.push(featureGeoJSON);
});
// Create a proper GeoJSON FeatureCollection
var featureCollection = {
"type": "FeatureCollection",
"features": features
};
// Convert to a JSON string with pretty formatting
var geojsonString = JSON.stringify(featureCollection, null, 2);
// Create a Blob from the GeoJSON
var blob = new Blob([geojsonString], {type: 'application/geo+json'});
// Create download link
var url = window.URL.createObjectURL(blob);
var a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = 'polygoner.geojson';
document.body.appendChild(a);
a.click();
// Clean up
setTimeout(function() {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 100);
});
function updateDownloadButton(){
var geojsonData = [];
drawnItems.eachLayer(function(layer){
geojsonData.push(layer.toGeoJSON());
});
if(geojsonData.length > 0){
document.getElementById('downloadButton').style.display = "block";
} else {
document.getElementById('downloadButton').style.display = "none";
}
}
updateDownloadButton();
</script>
</body>
</html>