218 lines
8.6 KiB
HTML
218 lines
8.6 KiB
HTML
{% extends "documents/base_documents.html" %}
|
|
{% load i18n static documents_tags %}
|
|
|
|
{% block title %}
|
|
{% translate "Plan géolocalisé" %} - {{ document.title }}
|
|
{% endblock title %}
|
|
|
|
{% block head %}
|
|
{{ block.super }}
|
|
<style>
|
|
#map .maplibregl-ctrl-top-right,
|
|
#map .mapboxgl-ctrl-top-right {
|
|
top: 10px !important;
|
|
}
|
|
.map-header {
|
|
background: linear-gradient(135deg, #0284c7, #0369a1);
|
|
color: #fff;
|
|
padding: 1rem 1.5rem;
|
|
border-radius: 8px;
|
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
.map-container {
|
|
height: 680px;
|
|
width: 100%;
|
|
border-radius: 8px;
|
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
|
position: relative;
|
|
}
|
|
.map-control-card {
|
|
position: absolute;
|
|
top: 20px;
|
|
right: 20px;
|
|
z-index: 10;
|
|
background: rgba(255, 255, 255, 0.95);
|
|
backdrop-filter: blur(8px);
|
|
border-radius: 8px;
|
|
width: 250px;
|
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
|
}
|
|
</style>
|
|
{% endblock head %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid py-4">
|
|
<!-- Header -->
|
|
<div class="map-header d-flex justify-content-between align-items-center flex-wrap gap-3">
|
|
<div>
|
|
<nav aria-label="breadcrumb">
|
|
<ol class="breadcrumb mb-1">
|
|
<li class="breadcrumb-item"><a href="{% url 'documents:list' %}" class="text-white-50">{% translate "Documents" %}</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'documents:detail' document.pk %}" class="text-white-50">{{ document.title }}</a></li>
|
|
<li class="breadcrumb-item active text-white" aria-current="page">{% translate "Visualisation" %}</li>
|
|
</ol>
|
|
</nav>
|
|
<h3 class="mb-0 fw-bold">
|
|
<i class="bi bi-map-fill"></i> {% translate "Visualisation sur la carte" %}
|
|
</h3>
|
|
</div>
|
|
<div>
|
|
<a href="{% url 'documents:detail' document.pk %}" class="btn btn-outline-light btn-sm px-4">
|
|
<i class="bi bi-arrow-left"></i> {% translate "Retour au document" %}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Map Area -->
|
|
<div class="card border-0 mb-4 shadow-sm">
|
|
<div class="card-body p-0 position-relative">
|
|
<div id="map" class="map-container"></div>
|
|
|
|
<!-- Map Controls Panel -->
|
|
<div class="map-control-card p-3 border">
|
|
<h6 class="fw-bold mb-2 text-dark"><i class="bi bi-sliders me-2 text-primary"></i>{% translate "Contrôles" %}</h6>
|
|
|
|
<div class="mb-2">
|
|
<label for="opacityRange" class="form-label small fw-bold text-secondary">{% translate "Opacité du plan" %}</label>
|
|
<input type="range" class="form-range" id="opacityRange" min="0" max="1" step="0.05" value="0.7">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock content %}
|
|
|
|
{% block script %}
|
|
{{ block.super }}
|
|
<script src="{% static 'common/layer-switcher-control.js' %}"></script>
|
|
<script src="{% static 'common/address_search_control.js' %}"></script>
|
|
<script>
|
|
document.addEventListener("DOMContentLoaded", function() {
|
|
const pageImageUrl = "{% url 'documents:page_image' document.pk document.georeference_page %}";
|
|
const coords = {{ document.georeference_coords|safe }};
|
|
|
|
let mapStyle = window.MAP_STYLE_LIGHT;
|
|
if (!mapStyle) {
|
|
mapStyle = "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json";
|
|
}
|
|
|
|
// Initialize Map
|
|
const map = new maplibregl.Map({
|
|
container: 'map',
|
|
style: mapStyle,
|
|
center: [coords[0][0], coords[0][1]],
|
|
zoom: 16
|
|
});
|
|
|
|
map.addControl(new maplibregl.NavigationControl(), 'top-right');
|
|
|
|
// Keep overlay layer always on top of base layers and orthophotos
|
|
map.on('styledata', () => {
|
|
if (map.getLayer('overlay-lyr')) {
|
|
const layers = map.getStyle().layers;
|
|
if (layers && layers.length > 0 && layers[layers.length - 1].id !== 'overlay-lyr') {
|
|
map.moveLayer('overlay-lyr');
|
|
}
|
|
}
|
|
});
|
|
|
|
// Add Geocoding/Address search control
|
|
if (typeof AddressSearchControl !== 'undefined') {
|
|
const searchCtrl = new AddressSearchControl({
|
|
autocompleteUrl: "{% url 'common:autocomplete_street' %}"
|
|
});
|
|
map.addControl(searchCtrl, 'top-left');
|
|
|
|
searchCtrl.addEventListener('address-selected', function(e) {
|
|
if (e.detail && e.detail.lon && e.detail.lat) {
|
|
map.flyTo({
|
|
center: [e.detail.lon, e.detail.lat],
|
|
zoom: 17
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
// Add Satellite Layer Switcher Control
|
|
if (typeof LayerSwitcherControl !== 'undefined') {
|
|
const layerSwitcher = new LayerSwitcherControl({
|
|
layers: [
|
|
{
|
|
id: 'ortho',
|
|
name: 'Orthophotos',
|
|
icon: 'satellite',
|
|
group: 'ortho',
|
|
layer: {
|
|
type: 'raster',
|
|
paint: { 'raster-opacity': 1 }
|
|
},
|
|
sublayers: [
|
|
{
|
|
id: 'urbis',
|
|
name: window.MAP_ORTHO_WMS_NAME || 'Urbis (Bruxelles)',
|
|
default: true,
|
|
source: {
|
|
type: 'raster',
|
|
tiles: [
|
|
window.MAP_ORTHO_WMS_URL || 'https://geoservices-urbis.irisnet.be/geoserver/urbisgrid/wms?service=WMS&version=1.3.0&request=GetMap&layers=Ortho&styles=&format=image/png&transparent=true&width=256&height=256&crs=EPSG:3857&bbox={bbox-epsg-3857}'
|
|
],
|
|
tileSize: 256,
|
|
attribution: window.MAP_ORTHO_WMS_ATTRIBUTION || '© Brussels UrbIS'
|
|
}
|
|
},
|
|
{
|
|
id: 'vlaanderen',
|
|
name: 'Vlaanderen 2023',
|
|
source: {
|
|
type: 'raster',
|
|
tiles: [
|
|
'https://geo.api.vlaanderen.be/OMW/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=omwrgb23vl&STYLE=&FORMAT=image/png&TILEMATRIXSET=GoogleMapsVL&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}'
|
|
],
|
|
tileSize: 256,
|
|
attribution: '© Digitaal Vlaanderen - Orthofoto 2023'
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]
|
|
});
|
|
map.addControl(layerSwitcher, 'top-left');
|
|
}
|
|
|
|
// Zoom map to the coordinates boundaries
|
|
const lngs = coords.map(c => c[0]);
|
|
const lats = coords.map(c => c[1]);
|
|
map.fitBounds([
|
|
[Math.min(...lngs), Math.min(...lats)],
|
|
[Math.max(...lngs), Math.max(...lats)]
|
|
], { padding: 80, maxZoom: 18, animate: false });
|
|
|
|
map.on('load', function() {
|
|
// Add Image source and layer
|
|
map.addSource('overlay-src', {
|
|
type: 'image',
|
|
url: pageImageUrl,
|
|
coordinates: coords
|
|
});
|
|
|
|
map.addLayer({
|
|
id: 'overlay-lyr',
|
|
type: 'raster',
|
|
source: 'overlay-src',
|
|
paint: {
|
|
'raster-opacity': 0.7
|
|
}
|
|
});
|
|
});
|
|
|
|
// Opacity control
|
|
const opacityRange = document.getElementById('opacityRange');
|
|
opacityRange.addEventListener('input', function() {
|
|
if (map.getLayer('overlay-lyr')) {
|
|
map.setPaintProperty('overlay-lyr', 'raster-opacity', parseFloat(this.value));
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock script %}
|