2088 lines
70 KiB
JavaScript
2088 lines
70 KiB
JavaScript
/**
|
|
* @file ouestmonbus.com main source file.
|
|
*
|
|
* @copyright Benoît Meunier & Karin Meunier - 2015-2023
|
|
*
|
|
* @license
|
|
* ouestmonbus.com is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* ouestmonbus.com is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with ouestmonbus.com. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
const STATION_CONTENT_REFRESH = 2 * 60 * 1000;
|
|
const STATION_LIST_REFRESH = 4 * 60 * 1000;
|
|
|
|
const version = '%VERSION%';
|
|
const date_version = '%DATEVERSION%';
|
|
|
|
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
const apikey = "3324de385f6b7b5bdfa1727d3e1a9f4bed5dd1accb288db6df5e8487";
|
|
|
|
log.setLevel(1);
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////// Classe OuestmonbusApp////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Classe principale de l'application.
|
|
*
|
|
* @constructor
|
|
*/
|
|
function OuestmonbusApp() {
|
|
|
|
this.localstoragePermitted = false;
|
|
try {
|
|
localStorage.setItem("localStorage", 1);
|
|
localStorage.removeItem("localStorage");
|
|
this.localstoragePermitted = true;
|
|
} catch (e) {}
|
|
|
|
// Carte Leaflet
|
|
this.map = null;
|
|
// Classe de gestion de la localisation
|
|
this.localisation = null;
|
|
// Classe de gestion des info traffic (dans ce fichier)
|
|
this.infostrafics = null;
|
|
|
|
this.needHelp = this.getLocalStorage("alreadyDisplayHelp") === null;
|
|
this.firstdisplayRetardHelp = this.getLocalStorage("alreadyDisplayRetardHelp") === null;
|
|
this.firstdisplayNoBusHelp = this.getLocalStorage("alreadyDisplayNoBusHelp") === null;
|
|
|
|
this.allStationsDataPath = this.getLocalStorage("allStationsDataPath");
|
|
this.allStationsDataCache = JSON.parse(this.getLocalStorage("allStationsDataCache"));
|
|
|
|
this.refreshAllStationsTimer = null;
|
|
this.refreshStationTimer = null;
|
|
|
|
this.firstStationDisplaySession = true;
|
|
this.stationFoundInCenter = null;
|
|
|
|
this.markerCurrentDisplay = null;
|
|
this.dialogCurrentDisplay = null;
|
|
|
|
this.lines_layers = [];
|
|
|
|
this.bus_group_layer = L.layerGroup();
|
|
this.stations_on_active_line_layer_group = L.layerGroup();
|
|
|
|
this.bus_station_origin = null;
|
|
|
|
this.last_lignes_selected = [];
|
|
this.last_ligne_bus_fetch = null;
|
|
|
|
this.waitSpinner = null;
|
|
this.waitSpinnerDisplayed = false;
|
|
|
|
this.progressbar = null;
|
|
this.hash_change_from_move = false;
|
|
|
|
this.md = new MobileDetect(window.navigator.userAgent);
|
|
}
|
|
|
|
OuestmonbusApp.prototype.getLocalStorage = function(key) {
|
|
|
|
if (this.localstoragePermitted) {
|
|
return localStorage.getItem(key);
|
|
} else {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
OuestmonbusApp.prototype.setLocalStorage = function(key, value) {
|
|
|
|
if (this.localstoragePermitted) {
|
|
try {
|
|
localStorage.setItem(key, value);
|
|
} catch (e) {}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Initialise l'application.
|
|
*/
|
|
OuestmonbusApp.prototype.init = function() {
|
|
|
|
var hpg = 0.5;
|
|
if (this.md.phone()) {
|
|
hpg = 2;
|
|
}
|
|
this.progressbar = new ProgressBar.Line("#progressbar", {
|
|
duration: STATION_CONTENT_REFRESH,
|
|
strokeWidth: hpg
|
|
});
|
|
this.progressbar.set(0);
|
|
|
|
$(window).resize(this.doOnResize.bind(this));
|
|
|
|
this.initTexts();
|
|
|
|
this.initMap();
|
|
|
|
this.localisation = new Localization(this, this.map);
|
|
|
|
this.infostrafics = new InfosTrafics(this);
|
|
|
|
this.initMenuButtons();
|
|
|
|
this.infostrafics.fetchLinesAndAlerts();
|
|
|
|
this.initPDFLinks();
|
|
|
|
window.onload = function() {
|
|
this.start();
|
|
}.bind(this);
|
|
|
|
window.onhashchange = function() {
|
|
this.onHashChange();
|
|
}.bind(this);
|
|
|
|
$(".app-container").show();
|
|
};
|
|
|
|
|
|
/**
|
|
* Point d'entrée, lorsque la fenêtre est chargé on exécute le programme.
|
|
*/
|
|
OuestmonbusApp.prototype.start = function() {
|
|
|
|
setTimeout(function() {
|
|
this.showHelp();
|
|
}.bind(this), 60000);
|
|
|
|
var display = function() {
|
|
this.getAllTodayStationsData(this.displayAllStations.bind(this));
|
|
};
|
|
|
|
var zoomLatLng = this.localisation.urlToZoomLatLng(window.location.hash);
|
|
if (zoomLatLng) {
|
|
this.moveMapTo(zoomLatLng[1], zoomLatLng[0], display.bind(this));
|
|
} else {
|
|
|
|
var last_zoom = this.getLocalStorage("last_zoom");
|
|
var last_lat = this.getLocalStorage("last_lat");
|
|
var last_lng = this.getLocalStorage("last_lng");
|
|
|
|
if (last_zoom !== null && last_lat !== null && last_lng !== null) {
|
|
this.moveMapTo(L.latLng(last_lat, last_lng), last_zoom, display.bind(this));
|
|
} else {
|
|
this.moveMapTo(this.map.DEFAULT_POSITION, this.map.DEFAULT_ZOOM, display.bind(this));
|
|
}
|
|
}
|
|
};
|
|
|
|
OuestmonbusApp.prototype.isMapHere = function(latlng, zoom) {
|
|
|
|
var res = false;
|
|
try {
|
|
res = this.map.getCenter().distanceTo(latlng) < this.map.DEFAULT_DISTANCE_RESOLUTION && this.map.getZoom() === zoom;
|
|
} catch (e) {}
|
|
return res;
|
|
};
|
|
|
|
OuestmonbusApp.prototype.moveMapTo = function(latlng, zoom, onFinish) {
|
|
|
|
if (typeof onFinish === "undefined") {
|
|
onFinish = function() {};
|
|
}
|
|
if (!this.isMapHere(latlng, zoom)) {
|
|
this.map.once("moveend", function() {
|
|
onFinish();
|
|
});
|
|
this.map.setView(latlng, zoom);
|
|
} else {
|
|
onFinish();
|
|
}
|
|
};
|
|
|
|
OuestmonbusApp.prototype.doOnResize = function() {
|
|
|
|
this.placeLegal();
|
|
};
|
|
|
|
/**
|
|
* Appelé quand l'URL change.
|
|
*/
|
|
OuestmonbusApp.prototype.onHashChange = function() {
|
|
|
|
if (!this.hash_change_from_move) {
|
|
var zoomLatLng = this.localisation.urlToZoomLatLng(window.location.hash);
|
|
|
|
if (zoomLatLng) {
|
|
this.moveMapTo(zoomLatLng[1], zoomLatLng[0]);
|
|
}
|
|
}
|
|
this.hash_change_from_move = false;
|
|
};
|
|
|
|
/**
|
|
Ajuste la position des éléments
|
|
Les mentions légal dans div.leaflet-bottom ne sont pas positionné en bas
|
|
sans un ajustement manuel.
|
|
*/
|
|
OuestmonbusApp.prototype.placeLegal = function() {
|
|
|
|
// hauteur des mentions légal
|
|
var legal_attribution_height = $("#legal_attribution").height();
|
|
|
|
// taille du menu
|
|
var appbar_height = $(".app-bar").height();
|
|
|
|
// taille de la barre de progression
|
|
var progress_height = $("#progressbar").height();
|
|
|
|
// taille de la fenêtre du navigateur
|
|
var window_height = $(window).height();
|
|
|
|
// position des mentions légale
|
|
var bottom_position = window_height - legal_attribution_height - progress_height;
|
|
$("div.leaflet-bottom").css("top", `${bottom_position}px`);
|
|
$("div.leaflet-top").css("top", `${appbar_height}px`);
|
|
|
|
// taille de la carte
|
|
var map_height = window_height - progress_height;
|
|
$("#map").css("height", `${map_height}px`);
|
|
};
|
|
|
|
/**
|
|
Ajuste la position des éléments
|
|
*/
|
|
OuestmonbusApp.prototype.addWaitCenterSpinner = function() {
|
|
|
|
if (this.waitSpinner !== null && !this.waitSpinnerDisplayed) {
|
|
this.waitSpinner.addTo(this.map);
|
|
this.waitSpinnerDisplayed = true;
|
|
this.waitSpinner.setPosition("topright");
|
|
var spinner_size_px = 52;
|
|
|
|
var padding_right = Math.round(Number($(window).width() / 2 - spinner_size_px / 2));
|
|
var padding_top = Math.round(Number($(window).height() / 2 - spinner_size_px / 2 - $(".app-bar").height()));
|
|
|
|
$("div.wait").css("padding-top", padding_top);
|
|
$("div.wait").css("padding-right", padding_right);
|
|
}
|
|
};
|
|
|
|
OuestmonbusApp.prototype.removeWaitCenterSpinner = function() {
|
|
|
|
if (this.waitSpinner !== null && this.waitSpinnerDisplayed) {
|
|
this.waitSpinner.remove(this.map);
|
|
this.waitSpinnerDisplayed = false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Ajuste la position des dialogues un peu plus en hauteur
|
|
*/
|
|
OuestmonbusApp.prototype.updateDialogPosition = function(element_id) {
|
|
|
|
$(element_id).css("top", $(".app-bar").height() + Math.ceil(($(window).height() - $(".app-bar").height()) * 0.05));
|
|
};
|
|
|
|
/**
|
|
* Initialise les textes
|
|
*/
|
|
OuestmonbusApp.prototype.initTexts = function() {
|
|
|
|
$("#version").html(`Version ${version} (${date_version})`);
|
|
|
|
$("#app_news").html(marked.parse(`
|
|
**03/10/2022:**
|
|
- Correction de bug: les lignes du métro sont maintenant visibles.
|
|
|
|
**28/09/20022:**
|
|
- Harmonisation des couleurs et du design.
|
|
- Ajout des nouveautés.
|
|
|
|
**27/09/2022:**
|
|
- Correction du problème avec le fuseau horaire de l'API Star.
|
|
- Suppression des widgets Twitter (privacy et performances).
|
|
|
|
**26/09/2022:**
|
|
- Hébergement: suppression du CDN cloudflare (privacy).
|
|
- Mise à jours de la carte OpenStreetMap.
|
|
|
|
`));
|
|
|
|
$("#text_usage").html(marked.parse(`[ouestmonbus.com](https://ouestmonbus.com) est une carte interactive qui vous permet de consulter les prochains passages des bus de Rennes Métropole à leurs arrêts, de visualiser les lignes et d'observer la position des bus en temps réel.`));
|
|
|
|
$("#text_who_develop").html(marked.parse(`
|
|
[ouestmonbus.com](https://ouestmonbus.com) est développé par [Benoît Meunier](mailto:contact@ouestmonbus.com) avec la participation de Karin Meunier (directrice graphisme et expérience utilisateur).
|
|
|
|
Ce service est fourni gratuitement sans garantie de fonctionnement.
|
|
Il est construit à l'aide des librairies [Leaflet](http://leafletjs.com/), [Metro UI CSS](https://metroui.org.ua/), [jQuery](https://jquery.com/), [mobile-detect.js](http://hgoebl.github.io/mobile-detect.js/), [underscore.js](https://underscorejs.org/) et [Moment.js](http://momentjs.com/).
|
|
|
|
L'application est basée sur des données ouvertes (Open Data). Elles proviennent des [contributeurs de OpenStreetMap](http://osm.org/copyright) et des informations du [service de Rennes Métropole et Keolis Rennes](https://data.explore.star.fr/).
|
|
|
|
Cette application web existe depuis 2015, c'est un logiciel libre sous [licence GPL v3](https://ouestmonbus.com/LICENSE), les sources sont [disponibles ici](https://ouestmonbus.com/ouestmonbus_src.zip).
|
|
`));
|
|
}
|
|
|
|
/**
|
|
* Affichage de la carte.
|
|
*/
|
|
OuestmonbusApp.prototype.initMap = function() {
|
|
|
|
// Création de la carte
|
|
var southWest = L.latLng(47.9690, -2.0316);
|
|
var northEast = L.latLng(48.3186, -1.3788);
|
|
var maxBounds = L.latLngBounds(southWest, northEast);
|
|
|
|
this.map = L.map("map", {
|
|
attributionControl: false,
|
|
maxBounds: maxBounds,
|
|
minZoom: 12,
|
|
zoomControl: true,
|
|
photonControl: true,
|
|
photonControlOptions: {
|
|
placeholder: "Entrez un lieu...",
|
|
position: "topleft",
|
|
noResultLabel: "Pas de résultat",
|
|
feedbackEmail: null,
|
|
onSelected: function(feature) {
|
|
this.map.setView([feature.geometry.coordinates[1], feature.geometry.coordinates[0]], 16);
|
|
$(".photon-input").hide();
|
|
}.bind(this)
|
|
}
|
|
});
|
|
this.map.setView([48.1178, -1.67036], 16);
|
|
this.map.zoomControl.setPosition('topright');
|
|
|
|
//#map=16/48.1113/-1.67566
|
|
this.map.DEFAULT_ZOOM = 17;
|
|
this.map.DEFAULT_DISTANCE_RESOLUTION = 1;
|
|
this.map.DEFAULT_POSITION = L.latLng(48.1178, -1.67036);
|
|
this.map.MAXBOUNDS = maxBounds;
|
|
|
|
// Ajout des tuiles
|
|
var tiles_layer = L.tileLayer("https://ouestmonbus.com/tiles/{z}/{x}/{y}.png", {
|
|
maxZoom: 18,
|
|
bounds: maxBounds,
|
|
opacity: 0.75
|
|
});
|
|
this.map.addLayer(tiles_layer);
|
|
|
|
// Spinner d'attente de chargement des données
|
|
this.waitSpinner = L.control();
|
|
|
|
this.waitSpinner.onAdd = function() {
|
|
this._div = L.DomUtil.create("div", "wait");
|
|
this.update();
|
|
return this._div;
|
|
};
|
|
this.waitSpinner.update = function() {
|
|
this._div.innerHTML = "<div data-role='preloader' data-type='ring' data-style='dark'></div>";
|
|
};
|
|
|
|
// Ajout des clusters de marker
|
|
this.map.cluster_markers = new L.markerClusterGroup({
|
|
disableClusteringAtZoom: this.map.DEFAULT_ZOOM,
|
|
spiderfyOnMaxZoom: false,
|
|
maxClusterRadius: 200
|
|
});
|
|
|
|
// ajout du layer de bus
|
|
this.bus_group_layer.addTo(this.map);
|
|
|
|
// ajout du layer des stations du parcours
|
|
this.stations_on_active_line_layer_group.addTo(this.map);
|
|
|
|
// Ajout des mentions légals
|
|
var legal = L.control.attribution({
|
|
prefix: "<div id='legal_attribution'>© 2015-2023 <a href='https://ouestmonbus.com'>ouestmonbus.com</a>"
|
|
}).addAttribution("<a href='http://osm.org/copyright'>OpenStreetMap</a> | <a href='https://data.explore.star.fr/page/home/'>Data Keolis Rennes Métropole</a></div>");
|
|
legal.setPosition("bottomright");
|
|
legal.addTo(this.map);
|
|
|
|
// Force un réalignement
|
|
this.placeLegal();
|
|
|
|
this.resetMapListener();
|
|
};
|
|
|
|
OuestmonbusApp.prototype.resetMapListener = function() {
|
|
|
|
this.map.on("moveend", this.onMapMoveEnd.bind(this));
|
|
this.map.on("zoomend", this.onMapZoomEnd.bind(this));
|
|
};
|
|
|
|
OuestmonbusApp.prototype.onMapZoomEnd = function() {
|
|
|
|
if (this.stations_on_active_line_layer_group.getLayers().length > 0) {
|
|
var currentZoom = this.map.getZoom();
|
|
var icon_size = 22;
|
|
var icon_anchor = 11;
|
|
|
|
if (currentZoom < 16) {
|
|
icon_size = currentZoom;
|
|
icon_anchor = Math.floor(currentZoom / 2);
|
|
}
|
|
|
|
var iconSize = [icon_size, icon_size];
|
|
var iconAnchor = [icon_anchor, icon_anchor];
|
|
|
|
this.stations_on_active_line_layer_group.eachLayer(function(layers) {
|
|
layers.bringToBack();
|
|
layers.eachLayer(function(layer) {
|
|
var icon_option = layer.getIcon();
|
|
icon_option["options"]["iconSize"] = icon_size;
|
|
icon_option["options"]["iconAnchor"] = icon_anchor;
|
|
layer.setIcon(icon_option);
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
|
|
OuestmonbusApp.prototype.onMapMoveEnd = function() {
|
|
|
|
this.hideDlg(true);
|
|
|
|
this.hash_change_from_move = true;
|
|
|
|
var pos = this.map.getCenter();
|
|
var lat = Number(pos.lat.toFixed(5));
|
|
var lng = Number(pos.lng.toFixed(5));
|
|
var z = this.map.getZoom();
|
|
window.location.hash = `map=${z}/${lat}/${lng}`;
|
|
this.setLocalStorage("last_zoom", this.map.getZoom());
|
|
this.setLocalStorage("last_lat", lat);
|
|
this.setLocalStorage("last_lng", lng);
|
|
};
|
|
|
|
OuestmonbusApp.prototype.imgPicto = function(ligne) {
|
|
|
|
return `/images/picto_${ligne}.png`;
|
|
};
|
|
|
|
/**
|
|
* Récupère les liens vers les fiches PDF des lignes
|
|
*/
|
|
OuestmonbusApp.prototype.initPDFLinks = function() {
|
|
|
|
this.allpdflinks = {};
|
|
|
|
var url_api = `https://data.explore.star.fr/api/records/1.0/search?apikey=${apikey}&dataset=mkt-information-documents-td&q=&rows=200&facet=nomtype&facet=datedebutvalidite&facet=datefinvalidite&facet=nomcourtligne&refine.nomtype=Fiche+horaire+de+ligne`
|
|
|
|
log.debug("call API mkt-information-documents-td");
|
|
$.getJSON(url_api,
|
|
function(data) {
|
|
var url_prefix = "https://data.explore.star.fr/explore/dataset/mkt-information-documents-td/files/"
|
|
_.each(data.records, function(e, i) {
|
|
var pdflink_id = e.fields.file.id;
|
|
var ligne_id = e.fields.idligne;
|
|
this.allpdflinks[ligne_id] = url_prefix + pdflink_id + '/download/';
|
|
}.bind(this));
|
|
}.bind(this)).fail(function() {
|
|
this.app.displayAPIError("Impossible de récupérer les url des PDF");
|
|
}.bind(this));
|
|
}
|
|
|
|
OuestmonbusApp.prototype.showOuestmonbusNewsDlg = function() {
|
|
|
|
this.hideDlg(true);
|
|
var dialog = $("#ouestmonbus_news_dlg").data("dialog");
|
|
this.dialogCurrentDisplay = dialog;
|
|
dialog.open();
|
|
this.updateDialogPosition("#ouestmonbus_news_dlg");
|
|
}
|
|
|
|
|
|
OuestmonbusApp.prototype.hideDlg = function(dlg) {
|
|
|
|
if (dlg) {
|
|
if (this.dialogCurrentDisplay) {
|
|
this.dialogCurrentDisplay.close();
|
|
this.dialogCurrentDisplay = null;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Initialise les boutons du menu.
|
|
*/
|
|
OuestmonbusApp.prototype.initMenuButtons = function() {
|
|
|
|
// Bouton News ouestmonbus
|
|
$("#ouestmonbus_btn").click(function() {
|
|
this.showOuestmonbusNewsDlg();
|
|
}.bind(this));
|
|
if (this.md.mobile()) {
|
|
$("#ouestmonbus_btn").unbind("mouseenter mouseleave");
|
|
}
|
|
|
|
// Bouton localisation
|
|
$("#localise_btn").click(function() {
|
|
this.hideDlg(true);
|
|
this.addWaitCenterSpinner();
|
|
this.localisation.autoLocate();
|
|
}.bind(this));
|
|
if (this.md.mobile()) {
|
|
$("#localise_btn").unbind("mouseenter mouseleave");
|
|
}
|
|
|
|
// photon search
|
|
$("#address_btn").click(function() {
|
|
this.hideDlg(true);
|
|
$(".photon-input").show();
|
|
$(".photon-input").focus();
|
|
}.bind(this));
|
|
if (this.md.mobile()) {
|
|
$("#address_btn").unbind("mouseenter mouseleave");
|
|
}
|
|
|
|
// Affichage des perturbations
|
|
$("#perturbations_btn").click(function() {
|
|
var h = Math.ceil(($(window).height() - $(".app-bar").height() - $("#progressbar").height() - $("#legal_attribution").height()) * 0.8);
|
|
$("#trafic_dlg_content").height(h);
|
|
this.hideDlg(true);
|
|
var dialog = $("#trafic_dlg").data("dialog");
|
|
this.dialogCurrentDisplay = dialog;
|
|
dialog.open();
|
|
this.updateDialogPosition("#trafic_dlg");
|
|
}.bind(this));
|
|
if (this.md.mobile()) {
|
|
$("#perturbations_btn").unbind("mouseenter mouseleave");
|
|
}
|
|
|
|
// Bouton config
|
|
$("#config_btn").click(function() {
|
|
var val = this.getLocalStorage("all_stations_mode") === 'true';
|
|
$("#all_stations_checkbox").prop('checked', val);
|
|
this.hideDlg(true);
|
|
var dialog = $("#config_dlg").data("dialog");
|
|
this.dialogCurrentDisplay = dialog;
|
|
dialog.open();
|
|
this.updateDialogPosition("#config_dlg");
|
|
}.bind(this));
|
|
if (this.md.mobile()) {
|
|
$("#config_btn").unbind("mouseenter mouseleave");
|
|
}
|
|
|
|
// Validation de la config
|
|
$("#config_valid_btn").click(function() {
|
|
var all_stations_mode = $("#all_stations_checkbox").is(":checked");
|
|
this.setLocalStorage("all_stations_mode", all_stations_mode);
|
|
location.reload();
|
|
}.bind(this));
|
|
if (this.md.mobile()) {
|
|
$("#config_valid_btn").unbind("mouseenter mouseleave");
|
|
}
|
|
|
|
// Bouton aide
|
|
$("#about_btn").click(function() {
|
|
this.hideDlg(true);
|
|
var dialog = $("#about_dlg").data("dialog");
|
|
this.dialogCurrentDisplay = dialog;
|
|
dialog.open();
|
|
this.updateDialogPosition("#about_dlg");
|
|
}.bind(this));
|
|
if (this.md.mobile()) {
|
|
$("#about_btn").unbind("mouseenter mouseleave");
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Affichage de la notice d'aide.
|
|
*/
|
|
OuestmonbusApp.prototype.showHelp = function() {
|
|
|
|
if (this.needHelp) {
|
|
$.Notify({
|
|
content: "Cliquez ou tapez sur les icônes " +
|
|
"<img src='./images/station.png' height='22' width='22' alt='station'> " +
|
|
"des arrêts de bus pour connaître les prochains passages en temps réel.",
|
|
icon: "<span class='mif-question'></span>",
|
|
type: "info",
|
|
keepOpen: true
|
|
});
|
|
}
|
|
this.setLocalStorage("alreadyDisplayHelp", true);
|
|
};
|
|
|
|
/**
|
|
* Affichage de la notification de légende retard.
|
|
*/
|
|
OuestmonbusApp.prototype.displayRetardHelp = function() {
|
|
|
|
if (this.firstdisplayRetardHelp) {
|
|
$.Notify({
|
|
content: "Le symbole <span class='mif-sync-problem mif-lg'></span> " +
|
|
"indique l'avance ou le retard par rapport à l'horaire initialement prévu",
|
|
type: "info",
|
|
keepOpen: true
|
|
});
|
|
}
|
|
this.firstdisplayRetardHelp = false;
|
|
this.setLocalStorage("alreadyDisplayRetardHelp", true);
|
|
};
|
|
|
|
/**
|
|
* Affichage de la notification de légende pas de bus.
|
|
*/
|
|
OuestmonbusApp.prototype.displayNoBusHelp = function() {
|
|
|
|
if (this.firstdisplayNoBusHelp) {
|
|
$.Notify({
|
|
content: "Le symbole <span class='mif-not'></span> " +
|
|
"indique qu'un bus est prévu avant la fin du service mais pas dans l'heure à venir",
|
|
type: "warning",
|
|
keepOpen: true
|
|
});
|
|
}
|
|
this.firstdisplayNoBusHelp = false;
|
|
this.setLocalStorage("alreadyDisplayNoBusHelp", true);
|
|
};
|
|
|
|
/**
|
|
* Récupère le chemin des données des stations du jour.
|
|
* @param {moment} now - maintenant
|
|
* @return {string} - chemin
|
|
*/
|
|
OuestmonbusApp.prototype.getDataStationPath = function(now) {
|
|
|
|
var first = moment(now);
|
|
first.hour(5);
|
|
first.minute(0);
|
|
first.second(0);
|
|
first.millisecond(0);
|
|
|
|
if (now.isBefore(first)) {
|
|
now = now.subtract(1, "days");
|
|
}
|
|
|
|
var datestr = now.format("YYYY_MM_DD");
|
|
return `./data/stations_${datestr}.geojson`;
|
|
};
|
|
|
|
/**
|
|
* Récupère la liste des station.
|
|
*
|
|
* @param {function} onSucess - fonction à executer après la récupération
|
|
*/
|
|
OuestmonbusApp.prototype.getAllTodayStationsData = function(onSuccess) {
|
|
|
|
var url_data = this.getDataStationPath(moment());
|
|
|
|
if (this.allStationsDataPath !== url_data || this.allStationsDataCache === null) {
|
|
|
|
this.addWaitCenterSpinner();
|
|
$.getJSON(url_data,
|
|
function(data) {
|
|
this.allStationsDataCache = data;
|
|
this.setLocalStorage("allStationsDataPath", url_data);
|
|
this.setLocalStorage("allStationsDataCache", JSON.stringify(data));
|
|
onSuccess(data);
|
|
}.bind(this)).fail(function() {
|
|
this.removeWaitCenterSpinner();
|
|
this.displayAPIError("Connexion impossible aux données ouestmonbus");
|
|
}.bind(this));
|
|
} else {
|
|
onSuccess(this.allStationsDataCache);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Filtre si il faut afficher la station ou non
|
|
* @param {moment} now
|
|
* @param {array} limits - [premier_passage,dernier_passage]
|
|
* @return {boolean} - true si à afficher
|
|
*/
|
|
OuestmonbusApp.prototype.stationTimeFilter = function(now, limits) {
|
|
|
|
// prevent error in GTFS data
|
|
if (limits === null || limits[0] === null || limits[1] === null) {
|
|
return false;
|
|
}
|
|
|
|
var now_ajusted = moment(now);
|
|
|
|
var morning_start = moment(now).startOf("day");
|
|
var morning_end = moment(morning_start).hours(5);
|
|
|
|
var hour_end_limit = Number(limits[1].slice(0, 2));
|
|
|
|
if (now.isBetween(morning_start, morning_end) && hour_end_limit >= 24) {
|
|
now_ajusted.add(1, "days");
|
|
}
|
|
|
|
var start = moment(now);
|
|
start.hours(Number(limits[0].slice(0, 2)) - 1);
|
|
start.minute(Number(limits[0].slice(3, 5)));
|
|
start.second(Number(limits[0].slice(6, 8)));
|
|
|
|
var end = moment(now);
|
|
end.hours(Number(limits[1].slice(0, 2)));
|
|
end.minute(Number(limits[1].slice(3, 5)));
|
|
end.second(Number(limits[1].slice(6, 8)));
|
|
end.add(10, "minutes");
|
|
|
|
if (now_ajusted.isBetween(start, end)) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
OuestmonbusApp.prototype.isMetroLine = function(lines) {
|
|
|
|
var line_id = Number(lines[0].split(",")[0]);
|
|
var res = _.size(lines) === 1 && (line_id === 1001 || line_id === 1002);
|
|
return res;
|
|
};
|
|
|
|
|
|
/***
|
|
*
|
|
* Affiche les stations sur la ligne active
|
|
*
|
|
*/
|
|
OuestmonbusApp.prototype.displayStationsOnActiveLine = function(line, sens) {
|
|
|
|
this.deleteAllSelectedStationsOnMap();
|
|
|
|
var line_sens = `${line},${sens}`;
|
|
var iconpath = "/images/station_selected.png";
|
|
if (this.isMetroLine([line_sens])) {
|
|
iconpath = "/images/station-metro_selected.png";
|
|
}
|
|
|
|
// récupère les stations sur la ligne
|
|
var stations = _.filter(this.allStationsDataCache, function(e) {
|
|
return _.contains(e.properties.lines, line + ',' + sens);
|
|
});
|
|
|
|
// Génère un layer pour ces stations
|
|
var geoJsonLayer = L.geoJson(stations, {
|
|
pointToLayer: function(feature, latlng) {
|
|
|
|
var stationIcon = L.icon({
|
|
iconUrl: iconpath,
|
|
shadowUrl: "",
|
|
iconSize: [22, 22],
|
|
shadowSize: [0, 0],
|
|
iconAnchor: [11, 11],
|
|
shadowAnchor: [0, 0],
|
|
popupAnchor: [0, 0]
|
|
});
|
|
|
|
return L.marker(latlng, {
|
|
icon: stationIcon
|
|
});
|
|
},
|
|
onEachFeature: function(feature, marker) {
|
|
marker.idarret = feature.properties.id;
|
|
marker.nomarret = feature.properties.name;
|
|
marker.lines = feature.properties.lines;
|
|
marker.terminus = feature.properties.terminus;
|
|
marker.parcours = feature.properties.parcours;
|
|
marker.line_selected = true;
|
|
|
|
this.onEachMakerAdd(marker);
|
|
}.bind(this)
|
|
});
|
|
|
|
this.stations_on_active_line_layer_group.addLayer(geoJsonLayer);
|
|
|
|
// ajust icon size
|
|
this.onMapZoomEnd();
|
|
}
|
|
|
|
/**
|
|
Callback lors d'un click sur une ligne
|
|
*/
|
|
OuestmonbusApp.prototype.displayLine = function(line, sens) {
|
|
|
|
this.displayStationsOnActiveLine(line, sens);
|
|
|
|
// met toutes les lignes en invisibles
|
|
_.each(this.lines_layers, function(e) {
|
|
var line_color = e.color;
|
|
var invisible = {
|
|
opacity: 0,
|
|
color: line_color
|
|
};
|
|
e.setStyle(invisible);
|
|
}.bind(this));
|
|
|
|
// recupère la ligne selectionnée
|
|
var line_layer = _.filter(this.lines_layers, function(e) {
|
|
return (e.idligne === line && e.sens == sens);
|
|
});
|
|
|
|
var param = [`${line},${sens}`];
|
|
this.displayLines(param, [], () => {});
|
|
};
|
|
|
|
/**
|
|
* Affichage des lignes sur la carte
|
|
* @param {strings} lines "idligne,sens"
|
|
* @param {array[string]} extra_lines "ajoute une ligne qui n'est pas dans les parcours de la station (bus exceptionnel stade,..)"
|
|
*/
|
|
OuestmonbusApp.prototype.displayLines = function(lines, extra_lines, fdone) {
|
|
|
|
log.debug(`displayLines: ${lines} | ${extra_lines}`);
|
|
|
|
// Efface les précédentes lignes en cache
|
|
this.deleteAllSelectedStationsOnMap();
|
|
_.each(this.lines_layers, function(layer) {
|
|
this.map.removeLayer(layer);
|
|
}.bind(this));
|
|
this.lines_layers = [];
|
|
|
|
var parcours_array = _.map(this.markerCurrentDisplay.parcours, function(item) {
|
|
return "id=" + item;
|
|
});
|
|
var q = parcours_array.join(" OR ");
|
|
|
|
if (extra_lines.length > 0) {
|
|
var extra_lines_array = _.map(extra_lines, function(e) {
|
|
return "idligne=" + e.split(",")[0];
|
|
});
|
|
q += " OR " + extra_lines_array.join(" OR ");
|
|
}
|
|
|
|
if (lines.length === 1) {
|
|
var line = lines[0].split(",")[0];
|
|
q += " OR idligne=" + line;
|
|
}
|
|
|
|
// Ajoute les ligne dans l'ordre pour que la première ligne soit en premier plan.
|
|
var first_line = lines[0].split(",")[0];
|
|
var lines_array = _.union(lines, extra_lines);
|
|
var args = [first_line, lines_array, fdone];
|
|
|
|
var q = encodeURIComponent(q);
|
|
if (this.isMetroLine(lines)) {
|
|
var url_api = `https://data.explore.star.fr/api/records/1.0/search?apikey=${apikey}&dataset=tco-metro-topologie-parcours-td&sort=nomcourtligne&facet=nomcourtligne&facet=senscommercial&facet=type&facet=nomarretdepart&facet=nomarretarrivee&facet=estaccessiblepmr&q=${q}&timezone=${tz}`;
|
|
log.debug("call API tco-metro-topologie-parcours-td");
|
|
} else {
|
|
var url_api = `https://data.explore.star.fr/api/records/1.0/search?apikey=${apikey}&dataset=tco-bus-topologie-parcours-td&facet=idligne&facet=nomcourtligne&facet=senscommercial&facet=type&facet=nomarretdepart&facet=nomarretarrivee&facet=estaccessiblepmr&q=${q}&timezone=${tz}`;
|
|
log.debug("call API tco-bus-topologie-parcours-td");
|
|
}
|
|
$.getJSON(url_api,
|
|
args,
|
|
function(data) {
|
|
var first_line = args[0];
|
|
var lines_array = args[1];
|
|
var fdone = args[2];
|
|
|
|
var data_sorted = _.sortBy(data.records, function(i) {
|
|
return i.fields.idligne === first_line;
|
|
});
|
|
|
|
|
|
_.each(data_sorted, function(trace, index, list) {
|
|
|
|
// Si première ligne on selectionne
|
|
if (index == (list.length - 1)) {
|
|
log.debug(`First line is ${trace.fields.idligne}`);
|
|
this.displayStationsOnActiveLine(trace.fields.idligne, trace.fields.sens);
|
|
}
|
|
|
|
// Bug de l'API: trace.fields.couleurtracetrace au lieu de trace.fields.couleurtrace quand métro.
|
|
var line_color = trace.fields.couleurtrace !== undefined ? trace.fields.couleurtrace : trace.fields.couleurtracetrace;
|
|
|
|
// filtre avec les parcours et montre la ligne uniquement si sur un des parcours de la station active
|
|
// (garde les données pour la position des bus mais n'affiche pas sinon)
|
|
var line_style_visible = {
|
|
opacity: 1,
|
|
color: line_color
|
|
};
|
|
var line_sens = `${trace.fields.idligne},${trace.fields.sens}`;
|
|
if (lines_array.includes(line_sens)) {
|
|
log.debug(`Add ${line_sens} to map`);
|
|
var layer = L.geoJson(trace.fields.parcours, {
|
|
style: line_style_visible
|
|
});
|
|
var sensstr = "";
|
|
if (Number(trace.fields.sens) === 0) {
|
|
sensstr = "aller";
|
|
} else {
|
|
sensstr = "retour";
|
|
}
|
|
layer.bindPopup(this.generatePopupLineContent(trace.fields.idligne, sensstr, trace.fields.couleurtrace, trace.fields.libellelong, this.allpdflinks[trace.fields.idligne]));
|
|
layer.on("click", function(e) {
|
|
$(".line_info").parent().css("margin", 0);
|
|
$(".leaflet-popup-tip-container").remove();
|
|
e.layer.bringToFront();
|
|
}.bind(this));
|
|
|
|
layer.idligne = trace.fields.idligne;
|
|
layer.sens = trace.fields.sens;
|
|
layer.dest = trace.fields.nomarretarrivee;
|
|
layer.color = line_color;
|
|
this.lines_layers.push(layer);
|
|
layer.addTo(this.map);
|
|
}
|
|
}.bind(this));
|
|
fdone();
|
|
}.bind(this));
|
|
};
|
|
|
|
/**
|
|
* Génération du contenu de la popup des lignes
|
|
*/
|
|
OuestmonbusApp.prototype.generatePopupLineContent = function(idligne, sens, couleur, nom, pdfurl) {
|
|
|
|
var img_picto = this.imgPicto(idligne);
|
|
var pdf_part = pdfurl ? `<br><a target='_blank' href='${pdfurl}'>Téléchargement de la fiche horaire</a>` : ``;
|
|
return `<div class='line_info' style='border:0.3rem solid #${couleur};'><img src='${img_picto}' height='22' width='22' alt='${idligne}'> ${nom} (sens ${sens})<br>${pdf_part}</div>`
|
|
};
|
|
|
|
/**
|
|
* Affiche les stations.
|
|
* Cette fonction devrait régulièrement être appelé pour mettre a jour
|
|
* la présence ou non des arrêts
|
|
*
|
|
* @param {geoJson} geoJsonData - stations du jour
|
|
*/
|
|
OuestmonbusApp.prototype.displayAllStations = function(geoJsonData) {
|
|
|
|
this.removeWaitCenterSpinner();
|
|
|
|
var geoJsonLayer = L.geoJson(geoJsonData, {
|
|
pointToLayer: function(feature, latlng) {
|
|
|
|
var busIcon = L.icon({
|
|
iconUrl: "./images/station.png",
|
|
shadowUrl: "",
|
|
iconSize: [22, 22],
|
|
shadowSize: [0, 0],
|
|
iconAnchor: [11, 11],
|
|
shadowAnchor: [0, 0],
|
|
popupAnchor: [0, 0]
|
|
});
|
|
|
|
var metroIcon = L.icon({
|
|
iconUrl: "./images/station-metro.png",
|
|
shadowUrl: "",
|
|
iconSize: [22, 22],
|
|
shadowSize: [0, 0],
|
|
iconAnchor: [11, 11],
|
|
shadowAnchor: [0, 0],
|
|
popupAnchor: [0, 0]
|
|
});
|
|
|
|
var icon = busIcon;
|
|
|
|
var line_id = Number(feature.properties.lines[0].split(",")[0]);
|
|
if (line_id === 1001 || line_id === 1002) {
|
|
icon = metroIcon;
|
|
}
|
|
|
|
return L.marker(latlng, {
|
|
icon: icon
|
|
});
|
|
}.bind(this),
|
|
filter: function(feature) {
|
|
var all_stations_mode = this.getLocalStorage("all_stations_mode") === 'true';
|
|
var time_ok = this.stationTimeFilter(moment(), feature.properties.limits);
|
|
return all_stations_mode || time_ok;
|
|
}.bind(this),
|
|
onEachFeature: function(feature, marker) {
|
|
marker.idarret = feature.properties.id;
|
|
marker.nomarret = feature.properties.name;
|
|
marker.lines = feature.properties.lines;
|
|
marker.terminus = feature.properties.terminus;
|
|
marker.parcours = feature.properties.parcours;
|
|
marker.line_selected = false;
|
|
|
|
this.onEachMakerAdd(marker);
|
|
if (this.firstStationDisplaySession) {
|
|
if (this.stationFoundInCenter === null) {
|
|
var lat = Number(marker.getLatLng().lat).toFixed(5);
|
|
var lng = Number(marker.getLatLng().lng).toFixed(5);
|
|
if (this.map.getCenter().distanceTo(L.latLng(lat, lng)) < this.map.DEFAULT_DISTANCE_RESOLUTION * 2) {
|
|
this.stationFoundInCenter = marker;
|
|
}
|
|
}
|
|
}
|
|
}.bind(this)
|
|
});
|
|
this.map.cluster_markers.clearLayers();
|
|
this.map.cluster_markers.addLayer(geoJsonLayer);
|
|
|
|
if (!this.map.hasLayer(this.map.cluster_markers)) {
|
|
this.map.cluster_markers.addTo(this.map);
|
|
}
|
|
|
|
if (this.stationFoundInCenter !== null) {
|
|
this.openStation(this.stationFoundInCenter);
|
|
} else {
|
|
if (this.firstStationDisplaySession && this.map.getZoom() >= this.map.DEFAULT_ZOOM) {
|
|
this.localisation.showLocation(this.map.getCenter(), false, false, 0);
|
|
}
|
|
}
|
|
|
|
this.firstStationDisplaySession = false;
|
|
this.delayRefreshAllStations();
|
|
};
|
|
|
|
|
|
/**
|
|
* Appelé à chaque ajout de marker
|
|
*
|
|
* @param {marker}
|
|
*/
|
|
OuestmonbusApp.prototype.onEachMakerAdd = function(marker) {
|
|
|
|
var popup = L.popup({
|
|
autoPan: true,
|
|
autoPanPaddingTopLeft: L.point(Math.ceil($(window).width() * 0.05), $(".app-bar").height() + Math.ceil(($(window).height() - $(".app-bar").height()) * 0.05)),
|
|
maxWidth: Math.ceil($(window).width() * 0.8),
|
|
maxHeight: Math.ceil(($(window).height() - $(".app-bar").height() - $("#progressbar").height() - $("#legal_attribution").height()) * 0.8)
|
|
});
|
|
popup.setContent("<div data-role='preloader' data-type='ring' data-style='dark'></div>");
|
|
marker.bindPopup(popup);
|
|
marker.on("click", function() {
|
|
this.openStation(marker);
|
|
}.bind(this));
|
|
};
|
|
|
|
/**
|
|
* Récupère les infos du prochain dépare à une station.
|
|
*
|
|
* @param {string} idarret
|
|
* @param {function} onSuccess - callback
|
|
*/
|
|
OuestmonbusApp.prototype.getNextDepartures = function(lines, idarret, onSuccess, onFailed) {
|
|
|
|
log.debug(`getNextDepartures: ${lines} ${idarret}`);
|
|
|
|
|
|
if (this.isMetroLine(lines)) {
|
|
var url = `https://data.explore.star.fr/api/records/1.0/search?apikey=${apikey}&dataset=tco-metro-circulation-passages-tr&sort=-depart&refine.idarret=${idarret}&timezone=${tz}`;
|
|
log.debug("call API tco-metro-circulation-passages-tr");
|
|
} else {
|
|
var url = `https://data.explore.star.fr/api/records/1.0/search?apikey=${apikey}&dataset=tco-bus-circulation-passages-tr&sort=-depart&refine.idarret=${idarret}&timezone=${tz}`;
|
|
log.debug("call API tco-bus-circulation-passages-tr");
|
|
}
|
|
|
|
$.getJSON(url,
|
|
function(data) {
|
|
if (data) {
|
|
|
|
// check server time
|
|
if (data.records.length > 0) {
|
|
var record_timestamp = moment(data.records[0].record_timestamp);
|
|
var diff_ms = moment().diff(record_timestamp);
|
|
var d = moment.duration(diff_ms);
|
|
// Feature ou bug de l'API, le champs .record_timestamp est à UTC ...
|
|
var utc_offset = moment().utcOffset();
|
|
|
|
if (d.asMinutes() > (30 + utc_offset)) {
|
|
var dialog = $("#api_time_failure_dlg").data("dialog");
|
|
this.dialogCurrentDisplay = dialog;
|
|
dialog.open();
|
|
}
|
|
onSuccess(data);
|
|
|
|
} else {
|
|
onSuccess(data);
|
|
}
|
|
} else {
|
|
this.displayAPIError("données invalides du service opendata STAR");
|
|
onFailed();
|
|
|
|
}
|
|
}.bind(this)).fail(function() {
|
|
this.displayAPIError("échec de connexion au service opendata STAR");
|
|
onFailed();
|
|
}.bind(this));
|
|
};
|
|
|
|
OuestmonbusApp.prototype.deleteAllBusOnMap = function() {
|
|
this.bus_group_layer.clearLayers();
|
|
};
|
|
|
|
OuestmonbusApp.prototype.deleteAllSelectedStationsOnMap = function() {
|
|
this.stations_on_active_line_layer_group.clearLayers();
|
|
};
|
|
|
|
OuestmonbusApp.prototype.generatePopupBusContent = function(idligne, destination) {
|
|
var img_picto = this.imgPicto(idligne);
|
|
return `<div class='bus_info'><img src='${img_picto}' height='22' width='22' alt='${idligne}'><br><span>Direction ${destination}</span></div>`;
|
|
};
|
|
|
|
OuestmonbusApp.prototype.getBusPositions = function(data, latlng) {
|
|
|
|
// prepare data
|
|
var all_records = _.reduce(data.records, function(seed, item) {
|
|
seed.push(item.fields);
|
|
return seed;
|
|
}, []);
|
|
|
|
var lignes_selected = _.groupBy(all_records, "idligne");
|
|
lignes_selected = _.map(lignes_selected, function(item) {
|
|
var first = _.first(item);
|
|
return [`${first.idligne},${first.sens}`];
|
|
});
|
|
lignes_selected = _.flatten(lignes_selected);
|
|
|
|
var request = _.map(lignes_selected, function(e) {
|
|
var idligne = e.split(",")[0];
|
|
var sens = e.split(",")[1];
|
|
return `idligne=${idligne} AND sens=${sens}`;
|
|
});
|
|
|
|
var q = encodeURIComponent(request.join(" OR "));
|
|
|
|
var url_api = `https://data.explore.star.fr/api/records/1.0/search?apikey=${apikey}&dataset=tco-bus-vehicules-position-tr&q=${q}&timezone=${tz}&refine.etat=En+ligne`;
|
|
|
|
|
|
log.debug("call API tco-bus-vehicules-position-tr");
|
|
$.getJSON(url_api,
|
|
function(data) {
|
|
if (data) {
|
|
// Ajoute la ligne comme clé de dict
|
|
var records = _.groupBy(data.records, function(e) {
|
|
return `${e.fields.idligne},${e.fields.sens}`;
|
|
});
|
|
|
|
// Position de la station
|
|
var station_l_latlng = this.normalizeLatLng(latlng);
|
|
// Résultat des positions classé par distance de la station et uniquement les ** 4 premiers bus **
|
|
var records_distance_filtred = _.mapObject(records, function(r) {
|
|
return _.first(_.sortBy(r, function(e) {
|
|
return this.normalizeLatLng(e.geometry.coordinates).distanceTo(station_l_latlng);
|
|
}.bind(this)), 4);
|
|
}.bind(this));
|
|
|
|
// Ligne avec idlgine comme clé
|
|
var lines_grouped = _.groupBy(this.lines_layers, function(e) {
|
|
return `${e.idligne},${e.sens}`;
|
|
});
|
|
var lines = _.mapObject(lines_grouped, function(e) {
|
|
return _.map(e, function(x) {
|
|
return x.getLayers()[0]._latlngs;
|
|
});
|
|
});
|
|
|
|
_.each(records_distance_filtred, function(value, key) {
|
|
this.genSegmentsTrajet(key, value, lines[key]);
|
|
}.bind(this));
|
|
}
|
|
}.bind(this)
|
|
).fail(function() {
|
|
// TODO add error message
|
|
}.bind(this));
|
|
return;
|
|
}
|
|
|
|
/*
|
|
Normalise les coordonnées. L'api peut répondre un array dans les deux sens.
|
|
*/
|
|
OuestmonbusApp.prototype.normalizeLatLng = function(a) {
|
|
|
|
if (a[0] > 47 && a[1] < 0) {
|
|
return L.latLng(a[0], a[1]);
|
|
}
|
|
if (a[1] > 47 && a[0] < 0) {
|
|
return L.latLng(a[1], a[0]);
|
|
}
|
|
if (a.lat > 47 && a.lng < 0) {
|
|
return L.latLng(a.lat, a.lng);
|
|
}
|
|
if (a.lat < 0 && a.lng > 47) {
|
|
return L.latLng(a.lng, a.lat);
|
|
}
|
|
// Ne devrait jamais se produire
|
|
log.debug(`Error: bad conversion: ${a}`);
|
|
}
|
|
|
|
OuestmonbusApp.prototype.genSegmentsTrajet = function(ligne_sens, bus_positions, lines) {
|
|
|
|
_.each(bus_positions, function(bus_position) {
|
|
var idbus = bus_position.fields.idbus;
|
|
|
|
var url_api = `https://data.explore.star.fr/api/records/1.0/search?apikey=${apikey}&sort=-depart&dataset=tco-bus-circulation-passages-tr&refine.idbus=${idbus}&timezone=${tz}`;
|
|
|
|
var idligne = ligne_sens.split(',')[0];
|
|
var sens = ligne_sens.split(',')[1];
|
|
|
|
log.debug("call API tco-bus-circulation-passages-tr");
|
|
$.getJSON(url_api,
|
|
function(data) {
|
|
if (data) {
|
|
// On ne tient pas compte des stations à moins de 10 secondes
|
|
var records_10s_filtred = _.filter(data.records, function(e) {
|
|
var now = moment();
|
|
var station_depart = moment(e.fields.depart);
|
|
var diff_second = station_depart.diff(now, 'seconds');
|
|
return diff_second >= 10;
|
|
});
|
|
// On utilise les 3 prochaines stations pour calculer le déplacement
|
|
var first_next_stations = _.first(records_10s_filtred, 3);
|
|
if (first_next_stations.length == 0) {
|
|
log.debug(`Pas de stations trouvé pour le bus ${idbus} - ${ligne_sens}`);
|
|
} else {
|
|
// Selection la ligne pour la trajectoire
|
|
var selected_line = this.getLignesForNextStation(first_next_stations, lines);
|
|
if (selected_line) {
|
|
var segments_time = this.getParcoursSegmentsTimes(bus_position, first_next_stations, selected_line);
|
|
if (!_.isUndefined(segments_time) && segments_time[0].length > 0) {
|
|
this.addBusIconOnMap(idbus, segments_time[0], segments_time[1], idligne, sens, bus_position.fields.destination);
|
|
return;
|
|
} else {
|
|
log.debug(`Impossible de calculer la trajectoire pour le bus ${idbus} - ${ligne_sens}`);
|
|
}
|
|
} else {
|
|
log.debug(`Pas de ligne trouvé pour la trajectoire du bus ${idbus} - ${ligne_sens}`);
|
|
}
|
|
|
|
}
|
|
|
|
log.debug("Ajout d'un bus sans animation")
|
|
var uniq_position = [bus_position.fields.coordonnees, bus_position.fields.coordonnees];
|
|
this.addBusIconOnMap(idbus, uniq_position, 0, idligne, sens, bus_position.fields.destination);
|
|
}
|
|
}.bind(this));
|
|
}.bind(this));
|
|
};
|
|
|
|
/*
|
|
Cette fonction prend en paramètre, la position du bus, les 3 prochaine stations et la ligne sur lequel le bus circule.
|
|
Elle retourne un tableau [0]: un array de L.latLng du déplacement du bus à venir, [1]: le temps estimé que va prendre le bus pour parcourir le trajet.
|
|
Le trajet est calculé en utilisant les prochaines station jusqu'à 2 minutes de trajet soit atteint.
|
|
*/
|
|
OuestmonbusApp.prototype.getParcoursSegmentsTimes = function(bus_position, first_next_stations, line) {
|
|
|
|
var parcour_points = [];
|
|
var parcour_duration = 0;
|
|
|
|
// calcul position du point de départ
|
|
var first_point = L.GeometryUtil.closest(this.map, line, this.normalizeLatLng(bus_position.fields.coordonnees), true);
|
|
log.debug(`Distance depuis le point de départ: ${first_point.distance}`);
|
|
if (first_point.distance > 250) {
|
|
log.debug("Le point de départ est hors parcours");
|
|
return;
|
|
}
|
|
// index dans la ligne du point de départ du bus
|
|
var first_index = _.findIndex(line, function(e) {
|
|
return e.lat == first_point.lat && e.lon == first_point.lon;
|
|
});
|
|
|
|
var begin_index = first_index;
|
|
_.each(first_next_stations, function(e) {
|
|
if (parcour_duration < STATION_CONTENT_REFRESH) {
|
|
var next_station_point = L.GeometryUtil.closest(this.map, line, this.normalizeLatLng(e.fields.coordonnees), true);
|
|
if (next_station_point.distance > 250) {
|
|
log.debug("Bus hors parcours");
|
|
return;
|
|
}
|
|
var next_station_index = _.findIndex(line, function(x) {
|
|
return x.lat == next_station_point.lat && x.lon == next_station_point.lon;
|
|
});
|
|
var part_line = line.slice(begin_index, next_station_index);
|
|
begin_index = next_station_index;
|
|
var part_duration = moment(e.fields.depart).diff(moment());
|
|
|
|
parcour_points = _.union(parcour_points, part_line);
|
|
parcour_duration += part_duration;
|
|
}
|
|
}.bind(this));
|
|
|
|
return [parcour_points, parcour_duration];
|
|
};
|
|
|
|
|
|
/*
|
|
Prend en paramètre les 3 prochaines stations d'un bus et les parcours connus du bus.
|
|
Retourne le parcours le plus proche des 3 prochaines stations.
|
|
Si > 2 km on retourn null, on considère que le parcours est inconnu.
|
|
*/
|
|
OuestmonbusApp.prototype.getLignesForNextStation = function(first_next_stations, lines) {
|
|
|
|
// Genere un array des positions des prochaines stations
|
|
var first_next_stations_array = _.map(first_next_stations, function(e) {
|
|
return e.geometry.coordinates;
|
|
});
|
|
|
|
// pour chaque ligne
|
|
var distances = _.map(lines, function(line_pos) {
|
|
var distance = 0;
|
|
// Pour chaque station
|
|
_.each(first_next_stations_array, function(station_pos) {
|
|
// On inverse lat et lon car API Star donne dans le sens inverse Leaflet
|
|
var pos_station = this.normalizeLatLng(station_pos);
|
|
// Le point le plus proche de la station sur tous les points de la ligne
|
|
var c = L.GeometryUtil.closest(this.map, line_pos, pos_station);
|
|
// On fait la somme des distances
|
|
distance += c.distance;
|
|
}.bind(this));
|
|
return [line_pos, distance];
|
|
}.bind(this));
|
|
|
|
var res = _.first(_.sortBy(distances, function(e) {
|
|
return e[1]
|
|
}));
|
|
if (_.isUndefined(res)) {
|
|
log.debug("Pas de ligne trouvé");
|
|
return;
|
|
}
|
|
if (res[1] > 2000) {
|
|
log.debug("Le bus est situé à plus de 2 km de son parcours");
|
|
return;
|
|
}
|
|
return res[0];
|
|
}
|
|
|
|
|
|
OuestmonbusApp.prototype.addBusIconOnMap = function(idbus, parcours, durations, idligne, sens, destination) {
|
|
|
|
log.debug(`Ajout de bus sur la carte idligne: ${idligne}`);
|
|
|
|
var busIcon = L.icon({
|
|
iconUrl: this.imgPicto(idligne),
|
|
shadowUrl: "./images/bus_shadow.png",
|
|
iconSize: [20, 20],
|
|
shadowSize: [32, 32],
|
|
iconAnchor: [10, 10],
|
|
shadowAnchor: [16, 16],
|
|
popupAnchor: [0, 0]
|
|
});
|
|
|
|
var marker = L.Marker.movingMarker(parcours, durations, {
|
|
autostart: true,
|
|
icon: busIcon,
|
|
zIndexOffset: -2
|
|
});
|
|
|
|
marker.idligne = idligne;
|
|
marker.sens = sens;
|
|
marker.idbus = idbus;
|
|
marker.last_refresh = moment();
|
|
|
|
marker.bindPopup(this.generatePopupBusContent(idligne, destination), {
|
|
closeButton: false,
|
|
className: "bus_popup"
|
|
});
|
|
marker.on("click", function() {
|
|
|
|
$(".bus_info").parent().css("margin", 0);
|
|
$(".leaflet-popup-tip-container").remove();
|
|
}.bind(this));
|
|
|
|
this.updateBusMarker(marker);
|
|
this.refreshBusCountHtml();
|
|
};
|
|
|
|
/*
|
|
Enleve l'ancien layer du bus et ajoute le nouveau.
|
|
*/
|
|
OuestmonbusApp.prototype.updateBusMarker = function(busMarker) {
|
|
|
|
var bus_already_on_map = false;
|
|
this.bus_group_layer.eachLayer(function(layer) {
|
|
if (layer.idbus === busMarker.idbus) {
|
|
this.bus_group_layer.removeLayer(layer);
|
|
this.bus_group_layer.addLayer(busMarker);
|
|
bus_already_on_map = true;
|
|
}
|
|
}.bind(this));
|
|
|
|
if (!bus_already_on_map) {
|
|
this.bus_group_layer.addLayer(busMarker);
|
|
}
|
|
};
|
|
|
|
|
|
OuestmonbusApp.prototype.refreshBusCountHtml = function() {
|
|
|
|
var bus_count_on_map = 0;
|
|
this.bus_group_layer.eachLayer(function(layer) {
|
|
if (!_.isUndefined(layer.idbus)) {
|
|
bus_count_on_map++;
|
|
}
|
|
});
|
|
|
|
var content = "";
|
|
if (bus_count_on_map === 0) {
|
|
content = "Pas de <img src='./images/bus_shadow.png' height='22' width='22' alt='bus'> sur la carte pour cet arrêt";
|
|
}
|
|
if (bus_count_on_map > 1) {
|
|
content = `Voir les <b>${bus_count_on_map}</b> <img src='./images/bus_shadow.png' height='22' width='22' alt='bus'> sur la carte`;
|
|
}
|
|
if (bus_count_on_map === 1) {
|
|
content = "Voir le <img src='./images/bus_shadow.png' height='22' width='22' alt='bus'> sur la carte";
|
|
}
|
|
$("#bus_count").html(content);
|
|
$("#bus_count").click(this.onBusCountClick.bind(this));
|
|
};
|
|
|
|
/*
|
|
Callback lors du click sur le bouton ce recentrage de la carte sur les bus.
|
|
*/
|
|
OuestmonbusApp.prototype.onBusCountClick = function() {
|
|
|
|
var bus_map_bounds = [this.markerCurrentDisplay.getLatLng()];
|
|
|
|
this.bus_group_layer.eachLayer(function(layer) {
|
|
if (!_.isUndefined(layer.idbus)) {
|
|
bus_map_bounds.push(layer.getLatLng());
|
|
}
|
|
}.bind(this));
|
|
|
|
this.map.fitBounds(bus_map_bounds, {
|
|
padding: L.point(100, 100)
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Ouvre la popup d'une station
|
|
*
|
|
* @param {L.marker} marker -
|
|
*/
|
|
OuestmonbusApp.prototype.openStation = function(marker) {
|
|
|
|
this.needHelp = false;
|
|
this.delayRefreshAllStations();
|
|
this.markerCurrentDisplay = marker;
|
|
|
|
this.getNextDepartures(marker.lines, marker.idarret, function(data) {
|
|
this.setPopupContent(marker, data);
|
|
marker.openPopup();
|
|
if (Number(data.nhits) > 0) {
|
|
this.refreshLinesBus(marker, data);
|
|
}
|
|
}.bind(this), function() {
|
|
this.setPopupContent(marker, null);
|
|
marker.openPopup();
|
|
}.bind(this));
|
|
|
|
this.refreshStation();
|
|
|
|
};
|
|
|
|
OuestmonbusApp.prototype.setOrigin = function(marker) {
|
|
|
|
this.localisation.deleteMarkerMe.call(this.localisation);
|
|
|
|
if (this.bus_station_origin !== null && this.map.hasLayer(this.bus_station_origin)) {
|
|
this.map.removeLayer(this.bus_station_origin);
|
|
}
|
|
this.bus_station_origin = L.marker(marker.getLatLng(), {
|
|
zIndexOffset: -1
|
|
});
|
|
|
|
this.bus_station_origin.on("click", function(e) {
|
|
this.moveMapTo(e.latlng, this.map.DEFAULT_ZOOM);
|
|
}.bind(this));
|
|
|
|
this.bus_station_origin.addTo(this.map);
|
|
};
|
|
|
|
/**
|
|
* Fonction apppellé lorsque l'utilisateur clique sur la station la plus
|
|
* proche après une localisation.
|
|
*/
|
|
OuestmonbusApp.prototype.followNearestStation = function() {
|
|
|
|
this.moveMapTo(this.localisation.nearestStation.marker.getLatLng(),
|
|
this.map.DEFAULT_ZOOM,
|
|
this.openStation.bind(this, this.localisation.nearestStation.marker)
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Mets à jour le contenue d'une popup de station
|
|
*/
|
|
OuestmonbusApp.prototype.setPopupContent = function(marker, data) {
|
|
|
|
var content = this.generatePopupContent(marker, data);
|
|
var popup = marker.getPopup();
|
|
popup.setContent(content);
|
|
//popup.update();
|
|
};
|
|
|
|
OuestmonbusApp.prototype.getStationUrl = function(marker) {
|
|
|
|
var z = this.map.DEFAULT_ZOOM;
|
|
var lat = Number(marker.getLatLng().lat).toFixed(5);
|
|
var lng = Number(marker.getLatLng().lng).toFixed(5);
|
|
|
|
var station_url = `#map=${z}/${lat}/${lng}`;
|
|
return station_url;
|
|
};
|
|
|
|
OuestmonbusApp.prototype.generateImgHtml = function(idligne, sens) {
|
|
var img_picto = this.imgPicto(idligne);
|
|
return `<a href='#' alt='afficher ligne' onclick='app.displayLine("${idligne}","${sens}");return false;'><img src='${img_picto}' height='22' width='22' alt='${idligne}'></a>`;
|
|
};
|
|
|
|
/**
|
|
* Généré le contenu d'une popup de station.
|
|
*
|
|
* @param {L.marker} marker - marker de la station
|
|
* @param {object} data - donnée reçu de l'API explore
|
|
*
|
|
* @return {string} - code html
|
|
*/
|
|
OuestmonbusApp.prototype.generatePopupContent = function(marker, data) {
|
|
|
|
var station_url = this.getStationUrl(marker);
|
|
|
|
var lines_header = [];
|
|
var lines_img = "";
|
|
|
|
var lines_array = marker.lines;
|
|
|
|
_.each(lines_array, function(item) {
|
|
var a = item.split(",");
|
|
if (_.indexOf(lines_header, a[0]) === -1) {
|
|
lines_img += this.generateImgHtml(a[0], a[1]);
|
|
lines_img += ' ';
|
|
lines_header.push(a[0]);
|
|
}
|
|
}.bind(this));
|
|
|
|
var full_content = "";
|
|
// si il y a moins de 4 icones on les mets à coté du nom de la station
|
|
if (_.size(lines_header) < 4) {
|
|
full_content = `<div class='popup_title_inline'><div class='list_lines_inline'>${lines_img}</div><div class='station_name'><a class='fg-white' href='${station_url}'>${marker.nomarret}</a></div></div><!-- idarret=${marker.idarret} --> <hr>`;
|
|
} else { // sinon les icones sont dessous
|
|
full_content = `<div class='popup_title'><div class='station_name'><a class='fg-white' href='${station_url}'>${marker.nomarret}</a></div><div class='list_lines'>${lines_img}</div></div><!-- idarret=${marker.idarret} --> <hr>`;
|
|
}
|
|
|
|
if (data !== null) {
|
|
|
|
var metrobus = this.isMetroLine(marker.lines) ? "métro" : "bus"
|
|
|
|
if (Number(data.nhits) > 0) {
|
|
|
|
// prepare data
|
|
var all_records = _.reduce(data.records, function(seed, item) {
|
|
seed.push(item.fields);
|
|
return seed;
|
|
}, []);
|
|
|
|
this.refreshBusCountHtml();
|
|
var records = _.groupBy(all_records, "idligne");
|
|
var pluriel = all_records.length > 1 ? "s" : "";
|
|
full_content += `<div class='header_station'><b>prochain${pluriel} passage${pluriel} de ${metrobus}:</b></div><div class='lines_content'`;
|
|
|
|
_.each(records, function(record_line) {
|
|
|
|
_.each(_.groupBy(record_line, "destination"), function(item) {
|
|
|
|
var lines_sens = _.first(item).idligne + "," + _.first(item).sens;
|
|
var is_terminus = (_.indexOf(marker.terminus, lines_sens) !== -1);
|
|
|
|
var content = this.generateLineContent(this.isMetroLine(marker.lines), is_terminus, item);
|
|
full_content += content;
|
|
}.bind(this));
|
|
|
|
}.bind(this));
|
|
|
|
} else {
|
|
this.displayNoBusHelp();
|
|
this.stopprogressBar();
|
|
|
|
full_content += "<div class='line_content'><span class='mif-not'></span> pas de passage dans l'heure</div>";
|
|
}
|
|
full_content += "</div><br>";
|
|
if (!this.isMetroLine(marker.lines)) {
|
|
full_content += "<button style='width:100%; text-align:center;' id='bus_count'>Pas de <img src='./images/bus_shadow.png' height='22' width='22' alt='bus'> sur la carte pour cet arrêt</button>";
|
|
}
|
|
} else {
|
|
full_content += "<div class='header_station' style='height: 1rem; width:9rem; display: inline-block;'></div>";
|
|
}
|
|
full_content += "</div>";
|
|
return full_content;
|
|
};
|
|
|
|
/**
|
|
* Génère le contenu de la ligne dans la popup de station.
|
|
*
|
|
* @param {object} records - champs de records des données de l'API explore
|
|
* @param {string} idligne - numéro de ligne
|
|
*
|
|
* @return {string} code html
|
|
*/
|
|
OuestmonbusApp.prototype.generateLineContent = function(isMetro, is_terminus, record) {
|
|
|
|
var content = "";
|
|
|
|
if (isMetro && is_terminus) {
|
|
return "";
|
|
}
|
|
|
|
_.each(_.first(record, 2), function(element, index) {
|
|
if (index === 0) {
|
|
var destination = element.destination;
|
|
|
|
if (_.isUndefined(destination)) {
|
|
destination = "Ligne " + element.idligne;
|
|
} else {
|
|
destination = destination.replace("- | ", "-");
|
|
destination = destination.replace(" | ", " ");
|
|
destination = `<b>► Direction ${destination}</b>`;
|
|
}
|
|
|
|
if (is_terminus) {
|
|
destination = "<b><font color='#787878 '> Terminus à cet arrêt</font></b>";
|
|
}
|
|
content += this.generateImgHtml(element.idligne, element.sens);
|
|
content += `<span>${destination}</span><br>`;
|
|
}
|
|
var accurate = (element.precision === "Temps réel") && (index === 0);
|
|
content += this.generateLineDeparture(
|
|
new Date(),
|
|
element.depart,
|
|
accurate,
|
|
element.departtheorique,
|
|
is_terminus,
|
|
isMetro);
|
|
}.bind(this));
|
|
|
|
return `<div class='line_content'>${content}</div>`;
|
|
};
|
|
|
|
|
|
/**
|
|
* Génère le code html de la ligne d'horaire (ex "12:56:00 dans 45min ! retard 5min")
|
|
*
|
|
* @param {string} server_localtime - heure courante du serveur d'API
|
|
* @param {string} time - heure de passage
|
|
* @param {Boolean} accurate - temps réel
|
|
* @param {string} expected - heure théorique
|
|
*
|
|
* @return {string} code html
|
|
*/
|
|
OuestmonbusApp.prototype.generateLineDeparture = function(localtime, time, accurate, expected, is_terminus, isMetro) {
|
|
|
|
var now = moment();
|
|
|
|
var departure = moment(time);
|
|
var wait_min = departure.diff(now, "minutes");
|
|
expected = moment(expected);
|
|
var retard_min = departure.diff(expected, "minutes");
|
|
|
|
var retard_min_prefix = "";
|
|
if (retard_min > 0) {
|
|
retard_min_prefix = "en retard de";
|
|
}
|
|
if (retard_min < 0) {
|
|
retard_min_prefix = "en avance de";
|
|
}
|
|
var retard_str = "";
|
|
|
|
var t = Math.abs(retard_min);
|
|
if (accurate && (retard_min < -1)) {
|
|
this.displayRetardHelp();
|
|
retard_str = ` <span class='mif-sync-problem mif-lg fg-red'></span> <b>${retard_min_prefix} ${t} min</b>`;
|
|
} else
|
|
if (accurate && (retard_min !== 0)) {
|
|
this.displayRetardHelp();
|
|
retard_str = ` <span class='mif-sync-problem mif-lg'></span> <b>${retard_min_prefix} ${t} min</b>`;
|
|
}
|
|
|
|
var line_h = "";
|
|
var wait_str = "";
|
|
if (wait_min < 1) {
|
|
if (is_terminus) {
|
|
wait_str = "<span><b> dans < 1 min</b></span>";
|
|
} else {
|
|
wait_str = "<span class= 'fg-red'><b> dans < 1 min</b></span>";
|
|
}
|
|
} else {
|
|
if (wait_min < 3 && !is_terminus && !isMetro) {
|
|
wait_str = `<span class= 'fg-red'>dans <b>${wait_min} min</b></span>`;
|
|
} else {
|
|
if (wait_min > 60) {
|
|
var hours = departure.diff(now, "hours");
|
|
var minutes = wait_min - (hours * 60);
|
|
wait_str = `<span>dans <b>${hours}h ${minutes} min</b></span>`;
|
|
} else {
|
|
wait_str = `<span>dans <b>${wait_min} min</b></span>`;
|
|
}
|
|
}
|
|
}
|
|
var t = departure.format("HH:mm:ss");
|
|
line_h = `<u>${t}</u> ${wait_str} ${retard_str}`;
|
|
|
|
return `<div class='popup_line'>${line_h}</div>`;
|
|
};
|
|
|
|
/**
|
|
* Informe l'utilisateur que l'API STAR est en rade..
|
|
*/
|
|
OuestmonbusApp.prototype.displayAPIError = function(err) {
|
|
|
|
if (this.markerCurrentDisplay) {
|
|
this.markerCurrentDisplay.getPopup().setContent(`<b>Erreur:</b> ${err}`);
|
|
}
|
|
|
|
$.Notify({
|
|
caption: "Erreur",
|
|
content: `Impossible d'interroger les données en temps réel (${err})`,
|
|
type: "error"
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Annule et relance un timer pour le rafraîchissement des stations.
|
|
*/
|
|
OuestmonbusApp.prototype.delayRefreshAllStations = function() {
|
|
|
|
if (this.refreshAllStationsTimer) {
|
|
clearTimeout(this.refreshAllStationsTimer);
|
|
}
|
|
this.refreshAllStationsTimer = setTimeout(function() {
|
|
this.getAllTodayStationsData(this.displayAllStations.bind(this));
|
|
}.bind(this), STATION_LIST_REFRESH);
|
|
};
|
|
|
|
|
|
OuestmonbusApp.prototype.refreshLinesBus = function(markerCurrentDisplay, data) {
|
|
|
|
// prepare data
|
|
var all_records = _.reduce(data.records, function(seed, item) {
|
|
seed.push(item.fields);
|
|
return seed;
|
|
}, []);
|
|
|
|
// Affichage des lignes
|
|
var lines_sens = _.map(all_records, function(item) {
|
|
return item.idligne + "," + item.sens;
|
|
});
|
|
lines_sens = _.uniq(lines_sens);
|
|
|
|
// extra lines : lignes annoncées au passage mais qui n'étaient pas prévu par les données GTFS
|
|
// cas observé une fois, à vérifier si pertinent de garder le code.
|
|
var extra_lines = _.reject(lines_sens, function(e) {
|
|
return _.contains(this, e);
|
|
}, this.markerCurrentDisplay.lines);
|
|
|
|
var lignes_selected = _.groupBy(all_records, "idligne");
|
|
lignes_selected = _.map(lignes_selected, function(item) {
|
|
var first = _.first(item);
|
|
return ["" + first.idligne + "," + first.sens];
|
|
});
|
|
lignes_selected = _.flatten(lignes_selected);
|
|
|
|
if ((lignes_selected.join() !== this.last_lignes_selected.join()) && markerCurrentDisplay.line_selected == false) {
|
|
this.displayLines(lines_sens, extra_lines, function() {
|
|
this.deleteAllBusOnMap();
|
|
this.getBusPositions(data, this.markerCurrentDisplay._latlng);
|
|
this.last_lignes_selected = lignes_selected;
|
|
this.last_ligne_bus_fetch = moment();
|
|
}.bind(this));
|
|
} else {
|
|
var next_fresh_duration = moment().diff(this.last_ligne_bus_fetch);
|
|
if (next_fresh_duration > STATION_CONTENT_REFRESH) {
|
|
this.getBusPositions(data, this.markerCurrentDisplay._latlng);
|
|
this.last_ligne_bus_fetch = moment();
|
|
} else {
|
|
var t = STATION_CONTENT_REFRESH - next_fresh_duration;
|
|
log.debug(`Prochain rafraichissement de la position des bus dans ${t} ms`);
|
|
this.refreshBusCountHtml();
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Rafraichie le contenue de la station actuellement ouverte
|
|
*/
|
|
OuestmonbusApp.prototype.refreshStation = function() {
|
|
|
|
this.startprogressBar();
|
|
if (this.refreshStationTimer) {
|
|
clearTimeout(this.refreshStationTimer);
|
|
}
|
|
this.refreshStationTimer = setTimeout(function() {
|
|
if (this.markerCurrentDisplay) {
|
|
this.getNextDepartures(this.markerCurrentDisplay.lines, this.markerCurrentDisplay.idarret, function(data) {
|
|
// Generation du contenue de la popup
|
|
this.setPopupContent(this.markerCurrentDisplay, data);
|
|
if (Number(data.nhits) > 0) {
|
|
// Update lines and bus
|
|
this.refreshLinesBus(this.markerCurrentDisplay, data);
|
|
}
|
|
}.bind(this), function() {
|
|
this.setPopupContent(this.markerCurrentDisplay, null);
|
|
}.bind(this));
|
|
this.refreshStation();
|
|
}
|
|
}.bind(this), STATION_CONTENT_REFRESH);
|
|
};
|
|
|
|
OuestmonbusApp.prototype.startprogressBar = function() {
|
|
|
|
this.stopprogressBar();
|
|
this.progressbar.animate(1);
|
|
};
|
|
|
|
OuestmonbusApp.prototype.stopprogressBar = function() {
|
|
|
|
this.progressbar.stop();
|
|
this.progressbar.set(0);
|
|
};
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////// Classe InfosTrafics /////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
function InfosTrafics(app) {
|
|
|
|
this.app = app;
|
|
this.html = "";
|
|
this.count = 0;
|
|
|
|
}
|
|
|
|
InfosTrafics.prototype.fetchLinesAndAlerts = function() {
|
|
|
|
var isoNow = moment().toISOString(false);
|
|
var q = encodeURIComponent(`debutvalidite < ${isoNow} AND finvalidite > ${isoNow}`);
|
|
|
|
var url_api = `https://data.explore.star.fr/api/records/1.0/search?apikey=${apikey}&dataset=tco-busmetro-trafic-alertes-tr&sort=idligne&rows=100&facet=idligne&q=${q}&timezone=${tz}`;
|
|
|
|
this.html += "<div class='accordion' data-role='accordion'>";
|
|
|
|
log.debug("call API tco-busmetro-trafic-alertes-tr");
|
|
$.getJSON(url_api,
|
|
function(data) {
|
|
_.each(data.records, function(info) {
|
|
var picto_html = this.getPictoHtml(info.fields.idligne);
|
|
var c = this.formatDetail(info.fields.description);
|
|
this.html += `<div class='frame'><div class='heading'>${info.fields.titre}<div class='place-right'>${picto_html}</div></div><div class='content'>${c}</div></div>`;
|
|
this.count += 1;
|
|
}.bind(this));
|
|
}.bind(this)).fail(function() {
|
|
this.app.displayAPIError("Impossible de récupérer les infos trafics (connect)");
|
|
}.bind(this)).done(function() {
|
|
this.html += "</div>";
|
|
$("#trafic_dlg_content").html(this.html);
|
|
$("#perturbations_btn").attr("data-hint", `Infos trafic| ${this.count} pertubations`);
|
|
}.bind(this));
|
|
};
|
|
|
|
InfosTrafics.prototype.getPictoHtml = function(ligne) {
|
|
|
|
var img_picto = this.app.imgPicto(ligne);
|
|
var html = `<img src='${img_picto}' height='22' width='22' alt='${ligne}' style='margin-right:2px;'>`;
|
|
return html;
|
|
};
|
|
|
|
InfosTrafics.prototype.formatDetail = function(input) {
|
|
|
|
return input.replace(/(?:\r\n|\r|\n)/g, "<br>");
|
|
};
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////// Classe Localization /////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Localisation de l'utilisateur.
|
|
* @constructor
|
|
*/
|
|
function Localization(app, map) {
|
|
|
|
this.app = app;
|
|
this.map = map;
|
|
|
|
this.marker_me = null; // position "vous êtes ici"
|
|
this.circle_me = null; // rayon ||
|
|
|
|
this.nearestStation = null;
|
|
|
|
this.last_lat = null;
|
|
this.last_lng = null;
|
|
|
|
this.last_localisation = null;
|
|
this.current_loc = null;
|
|
}
|
|
|
|
/**
|
|
* Localise l'utilisateur avec le navigateur.
|
|
*/
|
|
Localization.prototype.autoLocate = function() {
|
|
|
|
this.map.locate({
|
|
setView: true,
|
|
maxZoom: this.map.DEFAULT_ZOOM
|
|
});
|
|
this.map.once("locationfound", this.onLocationFound.bind(this));
|
|
this.map.once("locationerror", this.onLocationError.bind(this));
|
|
};
|
|
|
|
/**
|
|
* Appelé quand la localisation est trouvée.
|
|
*/
|
|
Localization.prototype.onLocationFound = function(e) {
|
|
|
|
this.app.removeWaitCenterSpinner();
|
|
this.showLocation(e.latlng, true, true, Math.round(e.accuracy / 2));
|
|
};
|
|
|
|
Localization.prototype.deleteMarkerMe = function() {
|
|
|
|
if (this.marker_me && this.map.hasLayer(this.marker_me)) {
|
|
this.map.removeLayer(this.marker_me);
|
|
}
|
|
if (this.circle_me && this.map.hasLayer(this.circle_me)) {
|
|
this.map.removeLayer(this.circle_me);
|
|
}
|
|
};
|
|
|
|
Localization.prototype.showLocation = function(latlng, youarehere, show_circle, radius) {
|
|
|
|
if (this.map.getBounds().contains(latlng)) {
|
|
|
|
if (radius > 1000) {
|
|
this.onLocationError();
|
|
} else {
|
|
|
|
this.deleteMarkerMe();
|
|
|
|
this.nearestStation = this.findNearestStation(latlng);
|
|
|
|
if (this.nearestStation.marker) {
|
|
if (this.nearestStation.distance < 50) {
|
|
this.app.moveMapTo(this.nearestStation.marker.getLatLng(), this.map.DEFAULT_ZOOM, this.app.openStation.bind(app, this.nearestStation.marker));
|
|
|
|
} else {
|
|
this.marker_me = L.marker(latlng);
|
|
|
|
var station_url = this.app.getStationUrl(this.nearestStation.marker);
|
|
var station_html = `<a class='fg-white' id='nearestStationLink' href='${station_url}' onclick='app.followNearestStation();return false;'><div class='station_name'>${this.nearestStation.marker.nomarret}</div></a>`;
|
|
|
|
var marker_str = "";
|
|
if (youarehere) {
|
|
marker_str = "<b>Vous êtes ici !</b><br>";
|
|
if (this.nearestStation.distance < 1000) {
|
|
var d = Math.round(this.nearestStation.distance);
|
|
marker_str += `L'arrêt de bus le plus proche est: <br>${station_html} à ${d} m.`;
|
|
}
|
|
|
|
this.marker_me.bindPopup(marker_str);
|
|
this.marker_me.addTo(this.map);
|
|
this.marker_me.openPopup();
|
|
} else {
|
|
if (this.nearestStation.distance < 1000) {
|
|
var d = Math.round(this.nearestStation.distance);
|
|
marker_str += `L'arrêt de bus le plus proche est: <br>${station_html} à ${d} m.`;
|
|
this.marker_me.bindPopup(marker_str);
|
|
this.marker_me.addTo(this.map);
|
|
this.marker_me.openPopup();
|
|
} else {
|
|
this.marker_me.addTo(this.map);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (show_circle) {
|
|
this.circle_me = L.circle(latlng, radius).addTo(this.map);
|
|
}
|
|
}
|
|
} else {
|
|
|
|
var dialog = $("#localisation_nothere_dlg").data("dialog");
|
|
this.app.dialogCurrentDisplay = dialog;
|
|
|
|
$("#localisation_nothere_dlg_close").click({
|
|
dlg: dialog
|
|
}, function(e) {
|
|
e.data.dlg.close();
|
|
$(".photon-input").show();
|
|
$(".photon-input").focus();
|
|
});
|
|
|
|
dialog.open();
|
|
this.app.updateDialogPosition("#localisation_nothere_dlg");
|
|
|
|
this.app.moveMapTo(this.map.DEFAULT_POSITION, this.map.DEFAULT_ZOOM);
|
|
}
|
|
};
|
|
|
|
|
|
Localization.prototype.findNearestStation = function(latlng) {
|
|
|
|
var nearestStation = {};
|
|
var res = L.GeometryUtil.closestLayer(this.map, this.map.cluster_markers.getLayers(), latlng);
|
|
|
|
if (res) {
|
|
nearestStation.marker = res.layer;
|
|
nearestStation.distance = res.distance;
|
|
}
|
|
|
|
return nearestStation;
|
|
};
|
|
|
|
|
|
/**
|
|
* Appelé quand la localisation n'est trouvée.
|
|
*/
|
|
Localization.prototype.onLocationError = function() {
|
|
|
|
this.app.removeWaitCenterSpinner();
|
|
var dialog = $("#localisation_failed_dlg").data("dialog");
|
|
this.app.dialogCurrentDisplay = dialog;
|
|
|
|
$("#localisation_failed_dlg_close").click({
|
|
dlg: dialog
|
|
}, function(e) {
|
|
e.data.dlg.close();
|
|
$(".photon-input").show();
|
|
$(".photon-input").focus();
|
|
});
|
|
|
|
dialog.open();
|
|
this.app.updateDialogPosition("#localisation_failed_dlg_close");
|
|
};
|
|
|
|
/**
|
|
* Converti le hash URL en LatLng.
|
|
*
|
|
* @param {string} url
|
|
*/
|
|
Localization.prototype.urlToZoomLatLng = function(url) {
|
|
|
|
var res = null;
|
|
try {
|
|
var map_pos = url.substring(url.indexOf("map"));
|
|
|
|
var zoom_str_x = map_pos.indexOf("=") + 1;
|
|
var lat_str_x = map_pos.indexOf("/") + 1;
|
|
var lng_str_x = map_pos.indexOf("/", lat_str_x);
|
|
|
|
var zoom = Number(map_pos.substring(zoom_str_x, lat_str_x - 1));
|
|
|
|
var lat = Number(map_pos.substring(lat_str_x, lng_str_x)).toFixed(5);
|
|
var lng = Number(map_pos.substring(lng_str_x + 1)).toFixed(5);
|
|
|
|
if (isNaN(lat) || isNaN(lng)) {
|
|
throw "Nan";
|
|
}
|
|
|
|
var latlng = L.latLng(lat, lng);
|
|
|
|
if (!this.map.MAXBOUNDS.contains(latlng)) {
|
|
throw "not in Map";
|
|
}
|
|
|
|
res = [zoom, latlng];
|
|
} catch (err) {
|
|
//log.debug("Err:"+err);
|
|
res = null;
|
|
}
|
|
return res;
|
|
};
|