var tl_data = []; var intersections_geojson = {}; const intersectionsgeojsonloader = new GeojsonLoader(); const intersections_layer_def = { 'id': 'traffic-lights', 'type': 'circle', 'source': 'traffic-lights', 'filter': [ 'all', ['!', ['coalesce', ['get', 'is_radar'], false]] ], 'paint': { 'circle-radius': [ 'interpolate', ['linear'], ['zoom'], 10, 2, 13, 4, 16, 7, 19, 10 ], 'circle-color': [ 'case', ['boolean', ['feature-state', 'highlighted'], false], '#ffeb3bbf', // Highlight color '#0094ff' // Default color ], 'circle-opacity': 0.7 } }; const intersections_radar_outline_layer_def = { 'id': 'traffic-lights-radar-outline', 'type': 'symbol', 'source': 'traffic-lights', 'filter': ['coalesce', ['get', 'is_radar'], false], 'layout': { 'icon-image': 'cross-sdf-border', 'icon-size': ['interpolate', ['linear'], ['zoom'], 10, 0.22, 13, 0.35, 16, 0.6, 19, 0.85], 'icon-allow-overlap': true, 'icon-ignore-placement': true, }, 'paint': { 'icon-color': [ 'case', ['boolean', ['feature-state', 'highlighted'], false], '#ffeb3b', '#ffffff' ] } }; const intersections_radar_layer_def = { 'id': 'traffic-lights-radar', 'type': 'symbol', 'source': 'traffic-lights', 'filter': ['coalesce', ['get', 'is_radar'], false], 'layout': { 'icon-image': 'cross-sdf', 'icon-size': ['interpolate', ['linear'], ['zoom'], 10, 0.22, 13, 0.35, 16, 0.6, 19, 0.85], 'icon-allow-overlap': true, 'icon-ignore-placement': true, }, 'paint': { 'icon-color': [ 'case', ['boolean', ['feature-state', 'highlighted'], false], '#ffeb3bbf', // Highlight color '#0094ff' // Default color ] } }; const intersections_layers = [intersections_layer_def, intersections_radar_outline_layer_def, intersections_radar_layer_def]; handleGeojsonMapLoading(init=true, intersectionsgeojsonloader, 'traffic-lights', intersections_layers); // Reset table if error occurs when loading GeoJSON intersectionsgeojsonloader.addEventListener("geojsonError", event => { loadDataIntoIntersectionsTable([]); }); // Load data into table when GeoJSON is loaded intersectionsgeojsonloader.addEventListener("geojsonLoaded", event => { var geojson_data = event.detail; loadDataIntoIntersectionsTable(geojson_data.features); intersections_geojson = geojson_data; }); var assets_geojson = {}; const assetsgeojsonloader = new GeojsonLoader(); const assets_layer_def = { 'id': 'traffic-lights-assets', 'type': 'circle', 'source': 'traffic-lights-assets', 'paint': { 'circle-radius': [ 'interpolate', ['linear'], ['zoom'], 13, 1, 22, 10 ], 'circle-color': [ 'case', ['boolean', ['feature-state', 'highlighted'], false], '#ffeb3bbf', // Highlight color '#f24141' // Default color ], 'circle-opacity': 0.7 }, minzoom: 15 } handleGeojsonMapLoading(init=true, assetsgeojsonloader, 'traffic-lights-assets', [assets_layer_def]); assetsgeojsonloader.addEventListener("geojsonError", event => { loadDataIntoAssetsTable([]); }); assetsgeojsonloader.addEventListener("geojsonLoaded", event => { var geojson_data = event.detail; loadDataIntoAssetsTable(geojson_data.features); assets_geojson = geojson_data; }); // Load Map maploader.loadMap("map"); // Map layers events registerLayerMouseEnter(map, 'traffic-lights', popup, 'code', 13); registerLayerMouseLeave(map, 'traffic-lights', popup); registerLayerMouseEnter(map, 'traffic-lights-radar', popup, 'code', 13); registerLayerMouseLeave(map, 'traffic-lights-radar', popup); registerLayerMouseEnter(map, 'traffic-lights-assets', popup, 'name', 17); registerLayerMouseLeave(map, 'traffic-lights-assets', popup); var intersectionshighlightState = { previousId: null }; // Track the previously highlighted feature const handleIntersectionClick = (e) => { const featureId = e.features[0].id; const currentZoom = map.getZoom(); if (currentZoom > 13) { // Highlight the corresponding row in the Bootstrap table highlightRow('#intersections-table', 'id', featureId); // Update feature highlight on the map highlightMapFeature(map, 'traffic-lights', featureId, intersectionshighlightState); highlightMapFeature(map, 'traffic-lights-radar', featureId, intersectionshighlightState); // Load assets GeoJSON for the selected intersection const intersectionId = featureId; const intersectionAssetsGeojsonUrl = `${assetsgeojsonUrl}?intersection=${intersectionId}`; assetsgeojsonloader.loadGeojson(intersectionAssetsGeojsonUrl); } }; map.on('click', 'traffic-lights', handleIntersectionClick); map.on('click', 'traffic-lights-radar', handleIntersectionClick); var assetshighlightState = { previousId: null }; // Track the previously highlighted feature map.on('click', 'traffic-lights-assets', (e) => { const featureId = e.features[0].id; const currentZoom = map.getZoom(); if (currentZoom > 13) { //window.open(trafficlightintersectionsurl+e.features[0].id+"/","_self"); // Highlight the corresponding row in the Bootstrap table highlightRow('#assets-table', 'id', featureId); // Update feature highlight on the map highlightMapFeature(map, 'traffic-lights-assets', featureId, assetshighlightState); } }); // Charger les données initiales const params = new URLSearchParams(window.location.search).toString(); intersectionsgeojsonloader.loadGeojson(intersectionsgeojsonUrl+`?${params}`); assetsgeojsonloader.loadGeojson(assetsgeojsonUrl); function actionsFormatter(value, row, index) { resp = ``; if (row.lon) { resp = resp + ``; } if (preventive_draft_id && preventive_draft_id !== "None") { // If we are in the context of a preventive draft, we add the button to select the intersection if (selected_intersections_set.has(row.id)) { icon_select = 'bi-check-square-fill'; } else { icon_select = 'bi-square'; } resp = resp + ``; } return resp; } function actionsFormatterAssets(value, row, index) { resp = ``; if (row.lon) { resp = resp + ``; } if (intervention_id) { resp = resp + ``; } return resp; } // Loads features into the intersections table function loadDataIntoIntersectionsTable(features) { const tableData = features.map((feature) => { return { actions: '', // Column for the icons, manipulated by the formatter id: feature.id, code: feature.properties.code, name: feature.properties.name, status: '', lon: feature.geometry && feature.geometry.coordinates ? feature.geometry.coordinates[0] : null, // Longitude lat: feature.geometry && feature.geometry.coordinates ? feature.geometry.coordinates[1] : null // Latitude }; }); $('#intersections-spinner').hide(); $('#intersections-table-container').show(); $('#intersections-table').bootstrapTable('load', tableData); } // Loads features into the assets table function loadDataIntoAssetsTable(features) { const tableData = features.map((feature) => { return { actions: '', // Colonne pour l'icône, manipulée par le formatter id: feature.id, asset_type: feature.properties.asset_type, code: feature.properties.code, name: feature.properties.name, model: feature.properties.model || '', status: '', lon: feature.geometry && feature.geometry.coordinates ? feature.geometry.coordinates[0] : null, // Longitude lat: feature.geometry && feature.geometry.coordinates ? feature.geometry.coordinates[1] : null // Latitude }; }); $('#assets-spinner').hide(); $('#assets-table-container').show(); $('#assets-table').bootstrapTable('load', tableData); } function redirectToThematic() { const select = document.getElementById('theme-select'); const selectedValue = select.value; if (selectedValue) { // Rediriger vers l'URL sélectionnée window.location.href = selectedValue; } } $( document ).ready(function() { function calculateTableHeight() { const windowHeight = $(window).height(); // Total window height const offsetTop = 285; // Y position of the table const footerHeight = $('.footer').outerHeight() || 0; return windowHeight - offsetTop - footerHeight - 20; } $('#intersections-table').bootstrapTable({ columns: [ { field: 'actions', title: '', formatter:'actionsFormatter', width:'130' }, { field: 'code', title: 'Code' }, { field: 'name', title: 'Nom' }, { field: 'status', title: 'Statut', align: 'center' } ], height: calculateTableHeight(), // Apply computed height onClickRow: function (row, $element) { // Remove highlight from all previously highlighted cells $('#intersections-table').find('tbody td').removeClass('rowhighlight'); // Apply the highlight class to all `` cells in the row $element.find('td').addClass('rowhighlight'); // Update feature highlight on the map highlightMapFeature(map, 'traffic-lights', row.id, intersectionshighlightState); // Load assets GeoJSON for the selected intersection const intersectionId = row.id; const intersectionAssetsGeojsonUrl = `${assetsgeojsonUrl}?intersection=${intersectionId}`; assetsgeojsonloader.loadGeojson(intersectionAssetsGeojsonUrl); } }); $('#assets-table').bootstrapTable({ columns: [ { field: 'actions', title: '', formatter:'actionsFormatterAssets', width:'120' }, { field: 'code', title: 'Code' }, { field: 'name', title: 'Nom' }, { field: 'model', title: 'Modèle' }, { field: 'status', title: 'Statut', align: 'center' } ], height: calculateTableHeight(), // Apply computed height onClickRow: function (row, $element) { // Remove highlight from all previously highlighted cells $('#assets-table').find('tbody td').removeClass('rowhighlight'); // Apply the highlight class to all `` cells in the row $element.find('td').addClass('rowhighlight'); // Update feature highlight on the map highlightMapFeature(map, 'traffic-lights-assets', row.id, assetshighlightState); } }); // Apply the correct width to the dropdown-menu const parent_filter_dropdown = $('#filter-dropdown').parent().parent()[0]; const parentWidth = parent_filter_dropdown.offsetWidth - 20; $('#filter-dropdown').width(`${parentWidth}px`); $('#theme-select').on('change', function () { redirectToThematic(); }); // reset view when tab is clicked (in case of window resize) $('#locations-table-tab').on('click', function () { $('#locations -table').bootstrapTable('resetView'); }); $('#assets-table-tab').on('click', function () { $('#assets-table').bootstrapTable('resetView'); }); // Apply filters document.getElementById('apply-filters').addEventListener('click', () => { const form = document.getElementById('filter-form'); const formData = new FormData(form); const params = new URLSearchParams(); for (const [key, value] of formData.entries()) { if (value.trim() !== '') { // Vérifie que le champ n'est pas vide params.append(key, value); } } // Update URL const newUrl = `${window.location.pathname}?${params.toString()}`; window.history.pushState({}, '', newUrl); // Load filtered data handleGeojsonMapLoading(init=false, intersectionsgeojsonloader, 'traffic-lights', intersections_layers); intersectionsgeojsonloader.loadGeojson(intersectionsgeojsonUrl+`?${params.toString()}`); }); // Reset filters document.getElementById('reset-filters').addEventListener('click', () => { const form = document.getElementById('filter-form'); form.reset(); // Reset form fields // Explicitly clear date fields document.getElementById('begin_date').value = ''; // Remove URL parameters window.history.pushState({}, '', window.location.pathname); // Reload all data without filters handleGeojsonMapLoading(init=false, intersectionsgeojsonloader, 'traffic-lights', intersections_layers); intersectionsgeojsonloader.loadGeojson(intersectionsgeojsonUrl); }); // Filter tables by map bounds $("#filterzone").on( "click", function(e) { e.preventDefault(); filterTableByMapBounds(map, intersections_geojson, loadDataIntoIntersectionsTable); filterTableByMapBounds(map, assets_geojson, loadDataIntoAssetsTable); }); // Register detail action to the tables registerDetailAction('#intersections-table', '.detail-btn', trafficlightsintersectionsurl); registerDetailAction('#assets-table', '.detail-btn', trafficlightsassetsurl); // Register zoom action to the tables registerZoomAction('#intersections-table', '.zoom-btn', map, 17); registerZoomAction('#assets-table', '.zoom-btn', map, 18); // Register intervention action to table assets registerInterventionAction('#assets-table', '.intervention-btn', intervention_id); // Register intersection selection action to table intersections registerSelectIntersectionAction('#intersections-table', '.intersection-btn-toggle'); }); function registerInterventionAction(tableId, buttonClass, intervention_id) { $(tableId).on('click', buttonClass, function () { const $button = $(this); const assetId = $(this).data('id'); // Retrieve the ID from the button's data attribute const assetType = $(this).data('asset_type'); // Retrieve the type from the button's data attribute if (assetId) { linkAssetToIntervention(intervention_id, assetId, assetType, $button); } }); } function linkAssetToIntervention(interventionId, assetId, assetType, $button) { $.ajax({ url: `/interventions/${interventionId}/link-asset/`, method: 'POST', headers: {'X-CSRFToken': getCookie('csrftoken')}, data: { intervention_id: interventionId, asset_id: assetId, model: assetType }, success: function(response) { // Mettre le bouton en vert et désactiver $button .removeClass('btn-light') .addClass('btn-success') .prop('disabled', true); }, error: function(error) { alert('Erreur lors de la liaison'); } }); } function registerSelectIntersectionAction(tableId, buttonClass) { $(tableId).on('click', buttonClass, function () { const $button = $(this); const intersectionId = $(this).data('id'); // Retrieve the ID from the button's data attribute if (intersectionId) { toggleSelectedIntersection(intersectionId, $button); } }); } function toggleSelectedIntersection(intersectionId, $button) { if (selected_intersections_set.has(intersectionId)) { selected_intersections_set.delete(intersectionId); $button .find('i').removeClass('bi-check-square-fill').addClass('bi-square'); } else { selected_intersections_set.add(intersectionId); $button .find('i').removeClass('bi-square').addClass('bi-check-square-fill'); } updateInputAndStatus(); } function updateInputAndStatus() { const inputField = document.getElementById("selected-intersections-ids"); const statusBar = document.getElementById("selection-status"); const ids = Array.from(selected_intersections_set); inputField.value = ids.join(","); statusBar.innerText = `${ids.length} intersection${ids.length !== 1 ? 's' : ''} sélectionnée${ids.length !== 1 ? 's' : ''}`; } // Selection of intersections for preventive draft if (preventive_draft_id && preventive_draft_id !== "None") { document.addEventListener("DOMContentLoaded", function () { document.getElementById("select-all").addEventListener("click", () => { const allRows = $('#intersections-table').bootstrapTable('getData'); allRows.forEach(row => { selected_intersections_set.add(parseInt(row.id)); const btn = document.querySelector(`.intersection-btn-toggle[data-id="${row.id}"]`); if (btn) { $(btn).find('i').removeClass('bi-square').addClass('bi-check-square-fill'); } }); updateInputAndStatus(); }); document.getElementById("deselect-all").addEventListener("click", () => { selected_intersections_set.clear(); const allRows = $('#intersections-table').bootstrapTable('getData'); allRows.forEach(row => { const btn = document.querySelector(`.intersection-btn-toggle[data-id="${row.id}"]`); if (btn) { $(btn).find('i').removeClass('bi-check-square-fill').addClass('bi-square'); } }); updateInputAndStatus(); }); // Initial state updateInputAndStatus(); }); } function getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== '') { const cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].trim(); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } const csrftoken = getCookie('csrftoken');