refactor: restructure map layers for assets and locations, add SRID 3812 projection, and update UI thematic selection logic.
This commit is contained in:
parent
41aa78e382
commit
f41b10bff8
5 changed files with 301 additions and 97 deletions
|
|
@ -97,71 +97,200 @@
|
|||
}
|
||||
|
||||
map.on('load', function() {
|
||||
// Source GeoJSON pour les assets
|
||||
// Source GeoJSON pour les assets et localisations
|
||||
map.addSource('assets-source', {
|
||||
type: 'geojson',
|
||||
data: { type: 'FeatureCollection', features: [] }
|
||||
});
|
||||
|
||||
// Couche Polygones (ex: ParkingSpot polygon)
|
||||
// 1. Couche Localisations (Zones GIEP, etc.) - EN DESSOUS DES ASSETS
|
||||
map.addLayer({
|
||||
id: 'locations-polygons',
|
||||
type: 'fill',
|
||||
source: 'assets-source',
|
||||
filter: [
|
||||
'all',
|
||||
['==', ['coalesce', ['get', 'is_location'], false], true],
|
||||
['match', ['geometry-type'], ['Polygon', 'MultiPolygon'], true, false]
|
||||
],
|
||||
paint: {
|
||||
'fill-color': '#0094ff',
|
||||
'fill-opacity': 0.18
|
||||
}
|
||||
});
|
||||
|
||||
map.addLayer({
|
||||
id: 'locations-lines',
|
||||
type: 'line',
|
||||
source: 'assets-source',
|
||||
filter: [
|
||||
'all',
|
||||
['==', ['coalesce', ['get', 'is_location'], false], true],
|
||||
['match', ['geometry-type'], ['LineString', 'MultiLineString', 'Polygon', 'MultiPolygon'], true, false]
|
||||
],
|
||||
paint: {
|
||||
'line-color': '#0094ff',
|
||||
'line-width': 2,
|
||||
'line-dasharray': [2, 2]
|
||||
}
|
||||
});
|
||||
|
||||
// 2. Couche Polygones d'Assets (Noue, EVC, OED, TA, etc.) - AU-DESSUS DES LOCALISATIONS
|
||||
map.addLayer({
|
||||
id: 'assets-polygons',
|
||||
type: 'fill',
|
||||
source: 'assets-source',
|
||||
filter: ['==', '$type', 'Polygon'],
|
||||
filter: [
|
||||
'all',
|
||||
['!', ['coalesce', ['get', 'is_location'], false]],
|
||||
['!=', ['coalesce', ['get', 'model_name'], ''], 'parkingspot'],
|
||||
['match', ['geometry-type'], ['Polygon', 'MultiPolygon'], true, false]
|
||||
],
|
||||
paint: {
|
||||
'fill-color': '#0d6efd',
|
||||
'fill-opacity': 0.35,
|
||||
'fill-outline-color': '#0d6efd'
|
||||
'fill-color': [
|
||||
'match', ['coalesce', ['get', 'structure_type'], ''],
|
||||
['massif drainant', 'tranchee drainante', 'collecteur en beton poreux', 'empierrement sedimentation', 'oed'], '#8d6e63',
|
||||
['jardin de pluie', 'noue', 'toiture verte', 'bande filtrante', 'espace vert sur dalle', 'evc'], '#009688',
|
||||
['bassin sec/infiltration', 'bassin en eau', 'bassin orage', 'citerne', 'toiture stockante', 'SAUL-vide', 'SAUL-terre', 'ta'], '#0277bd',
|
||||
'#0288d1'
|
||||
],
|
||||
'fill-opacity': 0.55
|
||||
}
|
||||
});
|
||||
|
||||
// Couche Lignes pour le contour des polygones
|
||||
// 3. Couche Polygones de Stationnement (Affichés à partir du Zoom 20 et plus)
|
||||
map.addLayer({
|
||||
id: 'parking-polygons',
|
||||
type: 'fill',
|
||||
source: 'assets-source',
|
||||
minzoom: 20,
|
||||
filter: [
|
||||
'all',
|
||||
['!', ['coalesce', ['get', 'is_location'], false]],
|
||||
['==', ['coalesce', ['get', 'model_name'], ''], 'parkingspot'],
|
||||
['match', ['geometry-type'], ['Polygon', 'MultiPolygon'], true, false]
|
||||
],
|
||||
paint: {
|
||||
'fill-color': '#0d6efd',
|
||||
'fill-opacity': 0.55,
|
||||
'fill-outline-color': '#0a58ca'
|
||||
}
|
||||
});
|
||||
|
||||
// 4. Couche Lignes d'Assets (LineStrings & contours de polygones d'assets)
|
||||
map.addLayer({
|
||||
id: 'assets-lines',
|
||||
type: 'line',
|
||||
source: 'assets-source',
|
||||
filter: ['==', '$type', 'Polygon'],
|
||||
filter: [
|
||||
'all',
|
||||
['!', ['coalesce', ['get', 'is_location'], false]],
|
||||
['match', ['geometry-type'], ['LineString', 'MultiLineString', 'Polygon', 'MultiPolygon'], true, false]
|
||||
],
|
||||
paint: {
|
||||
'line-color': '#0d6efd',
|
||||
'line-width': 2
|
||||
'line-color': [
|
||||
'match', ['coalesce', ['get', 'structure_type'], ''],
|
||||
['massif drainant', 'tranchee drainante', 'drain', 'collecteur en beton poreux', 'reseau egout separatif', 'oed'], '#8d6e63',
|
||||
['noue', 'jardin de pluie', 'bande filtrante', 'evc'], '#009688',
|
||||
['filet eau', 'caniveau infiltrant', 'gargouille de trottoir', 'avaloir de rail', 'trop plein', 'ta'], '#0277bd',
|
||||
'#0288d1'
|
||||
],
|
||||
'line-width': 2.5
|
||||
}
|
||||
});
|
||||
|
||||
// Couche Points (ex: Poteaux, Armoires)
|
||||
// 5. Couche Points de Stationnement (1 seul point au centroïde pour Zoom < 20)
|
||||
map.addLayer({
|
||||
id: 'parking-points',
|
||||
type: 'circle',
|
||||
source: 'assets-source',
|
||||
maxzoom: 20,
|
||||
filter: [
|
||||
'all',
|
||||
['!', ['coalesce', ['get', 'is_location'], false]],
|
||||
['==', ['coalesce', ['get', 'model_name'], ''], 'parkingspot'],
|
||||
['match', ['geometry-type'], ['Point', 'MultiPoint'], true, false]
|
||||
],
|
||||
paint: {
|
||||
'circle-radius': 6,
|
||||
'circle-color': '#0d6efd',
|
||||
'circle-stroke-width': 1.5,
|
||||
'circle-stroke-color': '#ffffff'
|
||||
}
|
||||
});
|
||||
|
||||
// 6. Couche Points d'Assets généraux (Poteaux, Avaloirs, Regards, etc.)
|
||||
map.addLayer({
|
||||
id: 'assets-points',
|
||||
type: 'circle',
|
||||
source: 'assets-source',
|
||||
filter: ['==', '$type', 'Point'],
|
||||
filter: [
|
||||
'all',
|
||||
['!', ['coalesce', ['get', 'is_location'], false]],
|
||||
['!=', ['coalesce', ['get', 'model_name'], ''], 'parkingspot'],
|
||||
['match', ['geometry-type'], ['Point', 'MultiPoint'], true, false]
|
||||
],
|
||||
paint: {
|
||||
'circle-radius': 8,
|
||||
'circle-color': '#0d6efd',
|
||||
'circle-radius': 7,
|
||||
'circle-color': [
|
||||
'match', ['coalesce', ['get', 'structure_type'], ''],
|
||||
['massif drainant', 'tranchee drainante', 'oed'], '#8d6e63',
|
||||
['jardin de pluie', 'noue', 'evc'], '#009688',
|
||||
['bassin orage', 'citerne', 'ta'], '#0277bd',
|
||||
'#0288d1'
|
||||
],
|
||||
'circle-stroke-width': 2,
|
||||
'circle-stroke-color': '#ffffff'
|
||||
}
|
||||
});
|
||||
|
||||
// Clic sur un asset
|
||||
map.on('click', ['assets-polygons', 'assets-points'], function(e) {
|
||||
// Si un marker d'inspection est présent à cet endroit, ne pas ouvrir la modale directement
|
||||
const interactiveAssetLayers = [
|
||||
'assets-points', 'parking-points', 'assets-lines', 'assets-polygons', 'parking-polygons', 'locations-polygons'
|
||||
];
|
||||
|
||||
// Clic Priorisé sur un Asset / Localisation
|
||||
map.on('click', interactiveAssetLayers, function(e) {
|
||||
if (map.getLayer('inspections-history-points')) {
|
||||
const inspFeatures = map.queryRenderedFeatures(e.point, { layers: ['inspections-history-points'] });
|
||||
if (inspFeatures && inspFeatures.length > 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (e.features && e.features.length > 0) {
|
||||
const feature = e.features[0];
|
||||
handleAssetClick(feature);
|
||||
}
|
||||
|
||||
const renderedFeatures = map.queryRenderedFeatures(e.point, { layers: interactiveAssetLayers });
|
||||
if (!renderedFeatures || renderedFeatures.length === 0) return;
|
||||
|
||||
// Priorisation : Point (1) > Line (2) > Asset Polygon (3) > Location Polygon (4)
|
||||
const priorityOrder = {
|
||||
'Point': 1,
|
||||
'LineString': 2,
|
||||
'MultiLineString': 2,
|
||||
'Polygon': 3,
|
||||
'MultiPolygon': 3
|
||||
};
|
||||
|
||||
renderedFeatures.sort((a, b) => {
|
||||
const aIsLoc = a.properties && (a.properties.is_location === true || a.properties.is_location === 'true');
|
||||
const bIsLoc = b.properties && (b.properties.is_location === true || b.properties.is_location === 'true');
|
||||
|
||||
if (!aIsLoc && bIsLoc) return -1;
|
||||
if (aIsLoc && !bIsLoc) return 1;
|
||||
|
||||
const typeA = a.geometry ? a.geometry.type : '';
|
||||
const typeB = b.geometry ? b.geometry.type : '';
|
||||
const prioA = priorityOrder[typeA] || 9;
|
||||
const prioB = priorityOrder[typeB] || 9;
|
||||
return prioA - prioB;
|
||||
});
|
||||
|
||||
handleAssetClick(renderedFeatures[0]);
|
||||
});
|
||||
|
||||
// Survol curseur
|
||||
map.on('mouseenter', ['assets-polygons', 'assets-points'], function() {
|
||||
map.on('mouseenter', interactiveAssetLayers, function() {
|
||||
map.getCanvas().style.cursor = 'pointer';
|
||||
});
|
||||
map.on('mouseleave', ['assets-polygons', 'assets-points'], function() {
|
||||
map.on('mouseleave', interactiveAssetLayers, function() {
|
||||
map.getCanvas().style.cursor = '';
|
||||
});
|
||||
|
||||
|
|
@ -173,8 +302,8 @@
|
|||
|
||||
// Placer la couche d'inspections EN DESSOUS des couches d'assets
|
||||
let beforeLayerId = undefined;
|
||||
if (map.getLayer('assets-polygons')) {
|
||||
beforeLayerId = 'assets-polygons';
|
||||
if (map.getLayer('locations-polygons')) {
|
||||
beforeLayerId = 'locations-polygons';
|
||||
} else if (map.getLayer('assets-points')) {
|
||||
beforeLayerId = 'assets-points';
|
||||
}
|
||||
|
|
@ -401,7 +530,7 @@
|
|||
[sw.lng, sw.lat], [ne.lng, sw.lat],
|
||||
[ne.lng, ne.lat], [sw.lng, ne.lat],
|
||||
[sw.lng, sw.lat],
|
||||
]],
|
||||
]]
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -530,7 +659,9 @@
|
|||
}
|
||||
|
||||
function initFilterStorage() {
|
||||
const savedThematic = localStorage.getItem(STORAGE_THEMATIC_KEY) || 'parking';
|
||||
const firstOpt = document.querySelector('#filter-thematic option:not([value=""])');
|
||||
const defaultThematic = firstOpt ? firstOpt.value : 'parking';
|
||||
const savedThematic = localStorage.getItem(STORAGE_THEMATIC_KEY) || defaultThematic;
|
||||
const savedCategory = localStorage.getItem(STORAGE_CATEGORY_KEY) || '';
|
||||
|
||||
updateUIElements(savedThematic, savedCategory);
|
||||
|
|
@ -691,7 +822,7 @@
|
|||
.catch(err => console.warn('Erreur chargement GeoJSON inspections:', err));
|
||||
}
|
||||
|
||||
const MIN_ZOOM_FOR_GEOJSON = 16;
|
||||
const MIN_ZOOM_FOR_GEOJSON = 13;
|
||||
|
||||
function loadAssets() {
|
||||
if (!map) return;
|
||||
|
|
@ -710,18 +841,21 @@
|
|||
}
|
||||
|
||||
const thematicSelect = document.getElementById('filter-thematic');
|
||||
const thematic = thematicSelect ? thematicSelect.value : '';
|
||||
let thematic = thematicSelect ? thematicSelect.value : '';
|
||||
if (!thematic) {
|
||||
const firstOpt = document.querySelector('#filter-thematic option:not([value=""])');
|
||||
if (firstOpt) thematic = firstOpt.value;
|
||||
}
|
||||
|
||||
const categorySelect = document.getElementById('filter-category');
|
||||
const categoryId = categorySelect ? categorySelect.value : '';
|
||||
|
||||
let fetchUrl = '/assets/parking/assets/geojson/';
|
||||
if (thematic === 'parking') {
|
||||
fetchUrl = '/assets/parking/assets/geojson/';
|
||||
} else if (thematic === 'trafficlights') {
|
||||
fetchUrl = '/assets/trafficlights/assets/geojson/';
|
||||
} else if (thematic === 'publiclighting') {
|
||||
fetchUrl = '/assets/publiclighting/assets/geojson/';
|
||||
let fetchUrls = [`/assets/${thematic || 'parking'}/assets/geojson/`];
|
||||
if (thematic === 'water' || thematic === 'giep') {
|
||||
fetchUrls = [
|
||||
'/assets/water/assets/geojson/',
|
||||
'/assets/water/locations/geojson/'
|
||||
];
|
||||
}
|
||||
|
||||
const payload = {};
|
||||
|
|
@ -734,7 +868,8 @@
|
|||
payload.filter_geom = { type: 'Feature', geometry: bbox };
|
||||
}
|
||||
|
||||
fetch(fetchUrl, {
|
||||
const fetchPromises = fetchUrls.map(url =>
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(payload),
|
||||
headers: {
|
||||
|
|
@ -742,37 +877,71 @@
|
|||
'X-CSRFToken': getCsrfToken()
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
if (res.status === 413) {
|
||||
if (warningAlert) warningAlert.classList.remove('d-none');
|
||||
.then(res => (res.ok && res.status !== 413) ? res.json() : null)
|
||||
.catch(err => {
|
||||
console.warn('Erreur chargement assets depuis ' + url, err);
|
||||
return null;
|
||||
}
|
||||
if (!res.ok) {
|
||||
if (warningAlert) warningAlert.classList.remove('d-none');
|
||||
return null;
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (!data) return;
|
||||
);
|
||||
|
||||
if (data.error) {
|
||||
if (warningAlert) warningAlert.classList.remove('d-none');
|
||||
return;
|
||||
Promise.all(fetchPromises).then(results => {
|
||||
let combinedFeatures = [];
|
||||
results.forEach(res => {
|
||||
if (res && res.features && Array.isArray(res.features)) {
|
||||
combinedFeatures = combinedFeatures.concat(res.features);
|
||||
}
|
||||
});
|
||||
|
||||
// Pour les parkings : générer 1 seul point centroïde pour l'affichage sous le zoom 20
|
||||
let processedFeatures = [];
|
||||
combinedFeatures.forEach(f => {
|
||||
processedFeatures.push(f);
|
||||
if (f.properties && (f.properties.model_name === 'parkingspot' || f.properties.thematic === 'parking')) {
|
||||
if (f.geometry && (f.geometry.type === 'Polygon' || f.geometry.type === 'MultiPolygon')) {
|
||||
let lon = parseFloat(f.properties.lon);
|
||||
let lat = parseFloat(f.properties.lat);
|
||||
|
||||
if ((isNaN(lon) || isNaN(lat) || lon === 0 || lat === 0) && f.geometry.coordinates) {
|
||||
try {
|
||||
const ring = f.geometry.type === 'Polygon' ? f.geometry.coordinates[0] : (f.geometry.coordinates[0] ? f.geometry.coordinates[0][0] : null);
|
||||
if (ring && ring.length > 0) {
|
||||
let sumLon = 0, sumLat = 0;
|
||||
ring.forEach(pt => { sumLon += pt[0]; sumLat += pt[1]; });
|
||||
lon = sumLon / ring.length;
|
||||
lat = sumLat / ring.length;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Erreur calcul centroïde parking:', e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isNaN(lon) && !isNaN(lat) && lon !== 0 && lat !== 0) {
|
||||
processedFeatures.push({
|
||||
type: 'Feature',
|
||||
id: (f.id || f.properties.id) + '_point',
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: [lon, lat]
|
||||
},
|
||||
properties: Object.assign({}, f.properties, { is_centroid: true })
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (warningAlert) warningAlert.classList.add('d-none');
|
||||
|
||||
if (data.features) {
|
||||
loadedAssets = data.features;
|
||||
loadedAssets = processedFeatures;
|
||||
const source = map.getSource('assets-source');
|
||||
if (source) {
|
||||
source.setData(data);
|
||||
source.setData({
|
||||
type: 'FeatureCollection',
|
||||
features: processedFeatures
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Error loading asset GeoJSON:', err);
|
||||
}).catch(err => {
|
||||
console.error('Erreur globale loadAssets:', err);
|
||||
if (warningAlert) warningAlert.classList.remove('d-none');
|
||||
});
|
||||
}
|
||||
|
|
@ -992,20 +1161,30 @@
|
|||
document.getElementById('insp_content_type_id').value = ctId || '';
|
||||
document.getElementById('insp_object_id').value = objId;
|
||||
|
||||
document.getElementById('insp_target_type').textContent = props.asset_type_display || props.model_name || 'Asset';
|
||||
document.getElementById('insp_target_code').textContent = props.target_display || props.code || (objId ? ('#' + objId) : '-');
|
||||
let modelName = props.model_name || props.location_type || '';
|
||||
if (modelName === 'NatureRWIZ' || modelName === 'NatureRWIAsset' || modelName === 'naturerwiasset' || props.thematic === 'water' || props.location_type === 'NatureRWIZ') {
|
||||
modelName = 'naturerwiz';
|
||||
}
|
||||
|
||||
document.getElementById('insp_target_type').textContent = props.element_type || props.asset_type_display || props.model_name || 'Asset';
|
||||
document.getElementById('insp_target_code').textContent = props.code || props.target_display || (objId ? ('#' + objId) : '-');
|
||||
document.getElementById('insp_target_name').textContent = props.name || props.name_fr || props.target_display || '-';
|
||||
document.getElementById('insp_target_status').textContent = props.latest_result_status_display || props.status_display || props.status || '-';
|
||||
|
||||
const container = document.getElementById('assetSpecificFieldsContainer');
|
||||
if (container) {
|
||||
container.innerHTML = '';
|
||||
const modelName = props.model_name || '';
|
||||
const url = `/inspections/api/partial-form/?content_type_id=${encodeURIComponent(ctId || '')}&object_id=${encodeURIComponent(objId)}&model_name=${encodeURIComponent(modelName)}`;
|
||||
fetch(url)
|
||||
.then(res => res.text())
|
||||
.then(html => {
|
||||
container.innerHTML = html;
|
||||
Array.from(container.querySelectorAll('script')).forEach(oldScript => {
|
||||
const newScript = document.createElement('script');
|
||||
Array.from(oldScript.attributes).forEach(attr => newScript.setAttribute(attr.name, attr.value));
|
||||
newScript.appendChild(document.createTextNode(oldScript.innerHTML));
|
||||
oldScript.parentNode.replaceChild(newScript, oldScript);
|
||||
});
|
||||
})
|
||||
.catch(err => console.error('Error loading inspection fields partial:', err));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,9 +62,10 @@ def parking_assets_geojson(request):
|
|||
status = data.get('status')
|
||||
|
||||
filter_geom_raw = data.get("filter_geom")
|
||||
if filter_geom_raw:
|
||||
if filter_geom_raw and isinstance(filter_geom_raw, dict):
|
||||
try:
|
||||
filter_geom = GEOSGeometry(json.dumps(filter_geom_raw['geometry']), srid=4326)
|
||||
geom_json = filter_geom_raw.get('geometry', filter_geom_raw) if filter_geom_raw.get('type') == 'Feature' else filter_geom_raw
|
||||
filter_geom = GEOSGeometry(json.dumps(geom_json), srid=4326)
|
||||
filter_geom.transform(3812)
|
||||
except Exception as e:
|
||||
return JsonResponse({"error": f"Invalid geometry: {str(e)}"}, status=400)
|
||||
|
|
|
|||
|
|
@ -99,27 +99,32 @@ def water_locations_geojson(request):
|
|||
|
||||
# Récupération et parsing de la géométrie
|
||||
filter_geom_raw = data.get("filter_geom")
|
||||
if filter_geom_raw:
|
||||
if filter_geom_raw and isinstance(filter_geom_raw, dict):
|
||||
try:
|
||||
filter_geom = GEOSGeometry(json.dumps(filter_geom_raw['geometry']), srid=4326)
|
||||
filter_geom.transform(3812)
|
||||
geom_json = filter_geom_raw.get('geometry', filter_geom_raw) if filter_geom_raw.get('type') == 'Feature' else filter_geom_raw
|
||||
filter_geom = GEOSGeometry(json.dumps(geom_json), srid=4326)
|
||||
filter_geom_3812 = filter_geom.clone()
|
||||
filter_geom_3812.transform(3812)
|
||||
except Exception as e:
|
||||
return JsonResponse({"error": f"Invalid geometry: {str(e)}"}, status=400)
|
||||
else:
|
||||
filter_geom = None
|
||||
filter_geom_3812 = None
|
||||
else:
|
||||
filter_geom = None
|
||||
filter_geom_3812 = None
|
||||
|
||||
rwiz_locations = NatureRWIZ.objects.all()
|
||||
|
||||
if filter_geom:
|
||||
rwiz_locations = rwiz_locations.filter(geom__intersects=filter_geom)
|
||||
if filter_geom_3812:
|
||||
rwiz_locations = rwiz_locations.filter(geom__intersects=filter_geom_3812)
|
||||
|
||||
# Recherche plein texte
|
||||
if request.method == 'POST':
|
||||
from assets.views.core import apply_search
|
||||
rwiz_locations = apply_search(rwiz_locations, data)
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
rwiz_ct_id = ContentType.objects.get_for_model(NatureRWIZ).id
|
||||
|
||||
from assets.views.core import get_element_type_label
|
||||
def obj_to_feature(obj):
|
||||
geometry = json.loads(obj.geojson) if obj.geojson else None
|
||||
|
|
@ -128,6 +133,9 @@ def water_locations_geojson(request):
|
|||
"id": obj.id,
|
||||
"geometry": geometry,
|
||||
"properties": {
|
||||
"object_id": obj.id,
|
||||
"content_type_id": rwiz_ct_id,
|
||||
"model_name": "naturerwiz",
|
||||
"code": obj.code,
|
||||
"name": obj.get_name(),
|
||||
"location_type": obj.__class__.__name__,
|
||||
|
|
@ -168,22 +176,25 @@ def water_assets_geojson(request):
|
|||
|
||||
# Récupération et parsing de la géométrie
|
||||
filter_geom_raw = data.get("filter_geom")
|
||||
if filter_geom_raw:
|
||||
if filter_geom_raw and isinstance(filter_geom_raw, dict):
|
||||
try:
|
||||
filter_geom = GEOSGeometry(json.dumps(filter_geom_raw['geometry']), srid=4326)
|
||||
geom_json = filter_geom_raw.get('geometry', filter_geom_raw) if filter_geom_raw.get('type') == 'Feature' else filter_geom_raw
|
||||
filter_geom = GEOSGeometry(json.dumps(geom_json), srid=4326)
|
||||
filter_geom_3812 = filter_geom.clone()
|
||||
filter_geom_3812.transform(3812)
|
||||
except Exception as e:
|
||||
return JsonResponse({"error": f"Invalid geometry: {str(e)}"}, status=400)
|
||||
else:
|
||||
filter_geom = None
|
||||
filter_geom_3812 = None
|
||||
else:
|
||||
section_id = request.GET.get('location')
|
||||
sections_ids = [section_id] if section_id else []
|
||||
statuses = request.GET.getlist('asset_status')
|
||||
categories = request.GET.getlist('category', [])
|
||||
filter_geom = None
|
||||
filter_geom_3812 = None
|
||||
|
||||
_search_q = (data.get('q') or '').strip() if request.method == 'POST' else ''
|
||||
if not request.GET.get('unified') and not sections_ids and not categories and not filter_geom and not _search_q:
|
||||
if not request.GET.get('unified') and not sections_ids and not categories and not filter_geom_3812 and not _search_q:
|
||||
return JsonResponse({
|
||||
"type": "FeatureCollection",
|
||||
"features": [],
|
||||
|
|
@ -197,9 +208,12 @@ def water_assets_geojson(request):
|
|||
if categories:
|
||||
naturerwiasset_qs = naturerwiasset_qs.filter(category__id__in=categories)
|
||||
|
||||
if filter_geom:
|
||||
filter_geom_3812 = filter_geom.transform(3812, clone=True)
|
||||
naturerwiasset_qs = naturerwiasset_qs.filter(geom__intersects=filter_geom_3812)
|
||||
if filter_geom_3812:
|
||||
from django.db.models import Q
|
||||
naturerwiasset_qs = naturerwiasset_qs.filter(
|
||||
Q(geom__intersects=filter_geom_3812) |
|
||||
Q(geom__isnull=True, location__geom__intersects=filter_geom_3812)
|
||||
)
|
||||
|
||||
if statuses:
|
||||
naturerwiasset_qs = naturerwiasset_qs.filter(status__in=statuses)
|
||||
|
|
@ -214,7 +228,7 @@ def water_assets_geojson(request):
|
|||
if naturerwiasset_qs.count() > MAX_ASSET_FEATURES:
|
||||
return JsonResponse({"error": f"{_("Trop d'objets à afficher")} (> {MAX_ASSET_FEATURES})"}, status=413)
|
||||
|
||||
from django.db.models import FloatField, Value, CharField
|
||||
from django.db.models import FloatField, Value, CharField, F
|
||||
from assets.views.core import get_element_type_label
|
||||
|
||||
naturerwiasset_qs = (naturerwiasset_qs.annotate(
|
||||
|
|
@ -222,8 +236,8 @@ def water_assets_geojson(request):
|
|||
category_name=F(f'category__name_{lang}'),
|
||||
model_name=Value(None, output_field=CharField()),
|
||||
color=Value(None, output_field=CharField()),
|
||||
location_lon=Value(None, output_field=FloatField()),
|
||||
location_lat=Value(None, output_field=FloatField()),
|
||||
location_lon=F('location__lon'),
|
||||
location_lat=F('location__lat'),
|
||||
)
|
||||
.values(
|
||||
'id', 'code', 'name', 'status', 'validation_status', 'lon', 'lat', 'surface_area',
|
||||
|
|
@ -241,8 +255,8 @@ def water_assets_geojson(request):
|
|||
except (TypeError, ValueError, json.JSONDecodeError):
|
||||
geometry = None
|
||||
|
||||
lon = obj.get('lon')
|
||||
lat = obj.get('lat')
|
||||
lon = obj.get('lon') if obj.get('lon') is not None else obj.get('location_lon')
|
||||
lat = obj.get('lat') if obj.get('lat') is not None else obj.get('location_lat')
|
||||
|
||||
if geometry is None and lon is not None and lat is not None:
|
||||
geometry = {
|
||||
|
|
@ -251,6 +265,8 @@ def water_assets_geojson(request):
|
|||
}
|
||||
return geometry, lon, lat
|
||||
|
||||
asset_ct_id = ContentType.objects.get_for_model(NatureRWIAsset).id
|
||||
|
||||
def obj_to_feature(obj, asset_type):
|
||||
geometry, lon, lat = build_geometry_and_coords(obj)
|
||||
detail_url = reverse('assets:water_assets_detail', kwargs={'asset_model': asset_type, 'asset_id': obj['id']})
|
||||
|
|
@ -259,6 +275,9 @@ def water_assets_geojson(request):
|
|||
"id": obj['id'],
|
||||
"geometry": geometry,
|
||||
"properties": {
|
||||
"object_id": obj['id'],
|
||||
"content_type_id": asset_ct_id,
|
||||
"model_name": "naturerwiasset",
|
||||
"code": obj['code'],
|
||||
"name": obj['name'],
|
||||
"category": obj['category_name'],
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@
|
|||
<select id="filter-thematic" class="d-none">
|
||||
<option value="">{% translate "-- Thématique --" %}</option>
|
||||
{% for t in thematics %}
|
||||
<option value="{{ t.code }}" data-icon="{{ t.icon|default:'bi-folder2' }}" data-name="{{ t.name }}" {% if t.code == 'parking' %}selected{% endif %}>{{ t.name }}</option>
|
||||
<option value="{{ t.code }}" data-icon="{{ t.icon|default:'bi-folder2' }}" data-name="{{ t.name }}" {% if forloop.first %}selected{% endif %}>{{ t.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
|
|
@ -256,7 +256,7 @@
|
|||
<div class="row g-2" id="thematic-tiles-container">
|
||||
{% for t in thematics %}
|
||||
<div class="col-12 col-sm-6">
|
||||
<div class="thematic-tile d-flex align-items-center gap-3 {% if t.code == 'parking' %}active{% endif %}" data-code="{{ t.code }}" data-icon="{{ t.icon|default:'bi-folder2' }}" data-name="{{ t.name }}" data-bs-dismiss="modal">
|
||||
<div class="thematic-tile d-flex align-items-center gap-3 {% if forloop.first %}active{% endif %}" data-code="{{ t.code }}" data-icon="{{ t.icon|default:'bi-folder2' }}" data-name="{{ t.name }}" data-bs-dismiss="modal">
|
||||
<div class="thematic-tile-icon" style="background-color: {{ t.color|default:'#0d6efd' }}15; color: {{ t.color|default:'#0d6efd' }};">
|
||||
<i class="bi {{ t.icon|default:'bi-folder2' }}"></i>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -478,6 +478,10 @@ def inspections_geojson(request):
|
|||
if category_id:
|
||||
categories = categories.filter(id=category_id)
|
||||
ct_ids = {c for c in categories.values_list('allowed_models', flat=True).distinct() if c}
|
||||
if thematic_code == 'water':
|
||||
from assets.models.nature import NatureRWIZ, NatureRWIAsset
|
||||
ct_ids.add(ContentType.objects.get_for_model(NatureRWIZ).id)
|
||||
ct_ids.add(ContentType.objects.get_for_model(NatureRWIAsset).id)
|
||||
except Thematic.DoesNotExist:
|
||||
pass
|
||||
|
||||
|
|
@ -497,7 +501,8 @@ def inspections_geojson(request):
|
|||
|
||||
asset_qs = ModelClass.objects.filter(geom__isnull=False)
|
||||
if bbox_geom:
|
||||
asset_qs = asset_qs.filter(geom__intersects=bbox_geom)
|
||||
bbox_geom_3812 = bbox_geom.transform(3812, clone=True)
|
||||
asset_qs = asset_qs.filter(geom__intersects=bbox_geom_3812)
|
||||
|
||||
asset_qs = asset_qs.annotate(
|
||||
wgs84_json=AsGeoJSON(Transform(Centroid('geom'), 4326))
|
||||
|
|
|
|||
Loading…
Reference in a new issue