loko/streetup/assets/static/assets/trafficlights_index.js
2026-07-22 14:48:40 +02:00

542 lines
No EOL
20 KiB
JavaScript

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 = `<button class="btn btn-sm btn-light detail-btn" data-id="${row.id}" data-bs-toggle="tooltip" title="Detail">
<i class="bi bi-info-circle"></i>
</button>`;
if (row.lon) {
resp = resp +
`<button class="btn btn-sm btn-light zoom-btn" data-lon="${row.lon}" data-lat="${row.lat}" data-bs-toggle="tooltip" title="Zoom">
<i class="bi bi-geo-alt"></i>
</button>`;
}
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 +
`<button class="btn btn-sm btn-light intersection-btn-toggle" data-id="${row.id}" data-bs-toggle="tooltip" title="Désélectionner l'intersection">
<i class="bi ${icon_select}"></i>
</button>`;
}
return resp;
}
function actionsFormatterAssets(value, row, index) {
resp = `<button class="btn btn-sm btn-light detail-btn" data-id="${row.asset_type}/${row.id}" data-bs-toggle="tooltip" title="Detail">
<i class="bi bi-info-circle"></i>
</button>`;
if (row.lon) {
resp = resp +
`<button class="btn btn-sm btn-light zoom-btn" data-lon="${row.lon}" data-lat="${row.lat}" data-bs-toggle="tooltip" title="Zoom">
<i class="bi bi-geo-alt"></i>
</button>`;
}
if (intervention_id) {
resp = resp +
`<button class="btn btn-sm btn-light intervention-btn" data-id="${row.id}" data-asset_type="${row.asset_type}" data-bs-toggle="tooltip" title="Ajouter à l'intervention">
<i class="bi bi-check-square"></i>
</button>`;
}
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: '<i class="bi bi-circle-fill status_'+feature.properties.status+'"></i>',
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: '<i class="bi bi-circle-fill status_'+feature.properties.status+'"></i>',
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 `<td>` 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 `<td>` 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');