Rev 3955 | Blame | Compare with Previous | Last modification | View Log | RSS feed
import {NOMINATIM_OSM_URL,TbPlaces} from "./modules/Locality.js";
const tileLayers = {
googleHybrid: [
'https://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}',
{
attribution: '<a href="https://www.google.com" target="_blank">\xa9 Google Maps</a>',
subdomains: ["mt0","mt1","mt2","mt3"],
maxZoom: 20
}
],
osm: [
'https://b.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png',
//'https://osm.tela-botanica.org/tuiles/osmfr/{z}/{x}/{y}.png',
{
attribution: 'Data © <a href="http://osm.org/copyright">OpenStreetMap</a>',
maxZoom: 18
}
]
};
const defaultPosition = {
lat: 47.0504,
lng: 2.2347
};
const defaultCityZoom = 12;
const defaultZoom = 4;
const geometryFilterDefault = 'point';
const defaultMapIdAttr = 'tb-geolocation';
const markerImgBaseUrl = URL_BASE +'js/tb-geoloc/img/';
const markerIcon = L.Icon.extend({
options: {
shadowUrl: markerImgBaseUrl +'marker-shadow.png',
iconAnchor: new L.Point(12, 40),//correctly replaces the dot of the pointer
iconSize: new L.Point(24,40),
iconUrl: markerImgBaseUrl +'marker-icon.png',
}
});
const defaultPolylineOptions = {
shapeOptions: {
weight: 5
},
showLength: true,
repeatMode: false
};
const drawLocale = {
edit: {
handlers: {
edit: {
tooltip: {
subtext: 'Cliquer sur annuler pour supprimer les changements',
text: 'Déplacez les marqueurs pour modifier leur position'
}
},
remove: {
tooltip: {
text: 'cliquer sur une ligne pour supprimer'
}
}
},
toolbar: {
actions: {
cancel: {
text: 'Annuler',
title: 'Annuler'
},
clearAll: {
text: 'Effacer',
title: 'Tout effacer'
},
save: {
text: 'Sauvegarder',
title: 'Sauvegarder les changements'
}
},
buttons: {
edit: 'Editer',
editDisabled: 'Rien à editer',
remove: 'Supprimer',
removeDisabled: 'Rien à supprimer'
}
}
},
draw : {
toolbar: {
actions: {
text: 'Annuler',
title: 'Annuler'
},
buttons: {
polyline: 'Dessiner une ligne',
marker: 'Pointer une position'
},
finish: {
text: 'Terminer',
title: 'Terminer'
},
undo: {
text: 'Supprimer',
title: 'Supprimer le dernier point'
}
},
handlers: {
polyline: {
tooltip: {
start: 'Cliquer sur la carte pour placer le début de la ligne',
cont: 'Positionner le prochain point et cliquer',
end: 'Re-cliquer sur le dernier point pour finir la ligne'
}
},
marker: {
tooltip: {
start: 'Cliquer sur la carte pour placer le marqueur'
}
},
rectangle: {tooltip: {}},
simpleshape: {tooltip: {}},
circle: {tooltip: {}},
circlemarker: {tooltip: {}}
}
}
};
/***************************************************/
export function Geoloc() {
this.map = {};
this.coordinates = defaultPosition;
this.geojson = {};
this.editableLayers = {};
this.defaultDrawControlOptions = {};
this.drawControl = {};
this.defaultEditOptions = false;
this.layer = {};
};
Geoloc.prototype.init = function(suffix = '') {
this.mapIdAttr = defaultMapIdAttr + suffix;
this.geolocLabel = document.getElementById('geoloc-label' + suffix);
this.initForm();
this.initEvts();
};
Geoloc.prototype.initForm = function() {
this.mapEl = document.getElementById(this.mapIdAttr);
this.zoom = this.mapEl.dataset.zoom || defaultZoom;
this.geometryFilter = this.mapEl.dataset.typeLocalisation || geometryFilterDefault;
const formSuffix = this.mapEl.dataset.formSuffix;
this.latitudeEl = document.getElementById('latitude' + formSuffix);
this.longitudeEl = document.getElementById('longitude' + formSuffix);
};
Geoloc.prototype.initEvts = function() {
this.initMap();
this.initMapCoordinates();
this.initSearchLocality();
};
Geoloc.prototype.initMap = function() {
this.map = this.createLocationMap();
// interactions with map
this.map.on(L.Draw.Event.CREATED, evt => {
this.layer = evt.layer;
// created marker or polyline with drawControl
// no more need this drawcontrol: remove
// (polyline: another one will be added with only edit toolbar)
this.map.removeControl(this.drawControl);
if ('marker' === evt.layerType) {
this.onMarkerCreated();
} else if ('polyline' === evt.layerType) {
this.handlePolylineEvents();
}
});
this.map.on(L.Draw.Event.EDITED, evt => {
const layers = evt.layers;
this.map.removeLayer(this.layer);
layers.eachLayer(layer => {
this.layer = layer;
this.handlePolylineEvents(false);
});
});
this.map.on(L.Draw.Event.DELETED, evt => {
this.reSetDrawControl();
});
};
Geoloc.prototype.initMapCoordinates = function() {
if (!!this.latitudeEl.value && !!this.longitudeEl.value) {
this.setMapCoordinates({
'lat': this.latitudeEl.value,
'lng': this.longitudeEl.value
});
}
this.onCoordinates();
};
Geoloc.prototype.reSetDrawControl = function() {
this.map.removeLayer(this.layer);
this.map.removeControl(this.drawControl);
this.setDrawControl(this.map);
};
Geoloc.prototype.createLocationMap = function() {
const lthis = this,
map = L.map(this.mapIdAttr, {zoomControl: true, gestureHandling: true}).setView([this.coordinates.lat, this.coordinates.lng], this.zoom),
tileLayer = this.mapEl.dataset.layer || 'osm';
L.tileLayer(...tileLayers[tileLayer]).addTo(map);
this.editableLayers = new L.FeatureGroup();
map.addLayer(this.editableLayers);
this.setDrawControl(map);
return map;
};
Geoloc.prototype.setDrawControl = function(map) {
this.defaultDrawControlOptions = this.generateDrawControlOptions();
this.drawControl = this.generateDrawControl(...Object.values(this.defaultDrawControlOptions));
map.addControl(this.drawControl);
};
Geoloc.prototype.generateDrawControlOptions = function() {
const options = {
editOptions: false,
markerOptions: false,
polylineOptions: false
};
this.defaultEditOptions = {
featureGroup: this.editableLayers,// REQUIRED!!
remove: true
}
switch (this.geometryFilter) {
case 'point':
options.markerOptions = {
icon: new markerIcon(),
repeatMode: false
};
break;
case 'rue':
options.polylineOptions = defaultPolylineOptions;
break;
default:
break;
}
return options;
};
Geoloc.prototype.generateDrawControl = function(
editOptions,
markerOptions = false,
polylineOptions = false
) {
L.drawLocal = drawLocale;
return new L.Control.Draw({
position: 'topleft',
draw: {
polyline: polylineOptions,
polygon: false,
circle: false,
rectangle: false,
marker: markerOptions,
circlemarker: false
},
edit: editOptions
});
};
Geoloc.prototype.onMarkerCreated = function() {
const coordinates = this.layer.getLatLng();
this.handleNewLocation(coordinates);
};
Geoloc.prototype.handleMarkerEvents = function() {
if(this.map) {
const lthis = this;
// move marker on click
$(this.map).off('click').on('click', function(evt) {
lthis.handleNewLocation(evt.originalEvent.latlng);
});
// move marker on drag
$(this.map.marker).off('dragend').on('dragend', function() {
lthis.handleNewLocation(lthis.map.marker.getLatLng());
});
}
};
Geoloc.prototype.handlePolylineEvents = function(requiresNewEditDrawControl = true) {
const latLngs = this.layer.getLatLngs(),
polyline = this.formatPolyline(latLngs),
coordinates = this.layer.getBounds().getCenter();
if (requiresNewEditDrawControl) {
this.drawControl = this.generateDrawControl(this.defaultEditOptions);
this.map.addControl(this.drawControl);
}
// make polyline removable
this.editableLayers.addLayer(L.polyline(latLngs));
this.map.addLayer(this.layer);
this.handleNewLocation(coordinates, polyline);
};
/**
* triggers location event
* @param coordinates.
* @param coordinates.lat latitude.
* @param coordinates.lng longitude.
* @param {array||null} polyline array of coordinates object
*/
Geoloc.prototype.handleNewLocation = function (coordinates, polyline = []) {
coordinates = this.formatCoordinates(coordinates);
if(!!coordinates && !!coordinates.lat && coordinates.lng) {
this.zoom = 20;
this.setMapCoordinates(coordinates);
if('point' === this.geometryFilter) {
this.createDraggableMarker();
this.handleMarkerEvents();
}
if (
('rue' === this.geometryFilter && 0 < polyline.length) ||
('point' === this.geometryFilter && 0 === polyline.length)
) {
this.getLocationInfo(coordinates, polyline);
}
}
};
Geoloc.prototype.formatCoordinates = function (coordinates) {
coordinates.lat = Number.parseFloat(coordinates.lat);
coordinates.lng = Number.parseFloat(coordinates.lng);
if(Number.isNaN(coordinates.lat) || Number.isNaN(coordinates.lng)) {
return null;
}
return coordinates;
};
Geoloc.prototype.formatPolyline = function (latLngs) {
const polyline = [];
latLngs.forEach(coordinates => polyline.push(Object.values(coordinates).reverse()));
return polyline;
};
Geoloc.prototype.setMapCoordinates = function (coordinates) {
this.coordinates = coordinates;
// updates map
this.map.setView(coordinates,this.zoom);
};
Geoloc.prototype.createDraggableMarker = function() {
if (undefined === this.map.marker) {
// after many attempts, did not manage
// to make marker from draw control draggable
// solution: replace it
if(this.layer) {
this.map.removeLayer(this.layer);
}
this.map.marker = new L.Marker(this.coordinates, {
draggable: true,
icon: new markerIcon()
});
this.layer = this.map.marker;
this.map.addLayer(this.map.marker);
if(this.drawControl) {
this.map.removeControl(this.drawControl);
}
}
this.map.marker.setLatLng(this.coordinates, {draggable: 'true'});
};
Geoloc.prototype.getLocationInfo = function(coordinates, polyline) {
const lthis = this,
url = NOMINATIM_OSM_URL+'reverse',
params = {
'format': 'json',
'lat': coordinates.lat,
'lon': coordinates.lng
};
this.geolocLabel.classList.add('loading');
$.ajax({
method: "GET",
url: url,
data: params,
success: function(locationData) {
locationData.centroid = lthis.formatPointTypeCoordinates(coordinates);
locationData.geojson = 'rue' === lthis.geometryFilter ? {type: 'LineString', coordinates: polyline} : locationData.centroid;
lthis.loadGeolocation(coordinates,locationData);
},
error: (err) => {
console.warn(err);
lthis.geolocLabel.classList.remove('loading');
}
});
};
Geoloc.prototype.formatPointTypeCoordinates = function(coordinates) {
return {type : 'Point', coordinates: Object.values(coordinates).reverse()};
};
Geoloc.prototype.loadGeolocation = function(coordinates,locationData) {
const lthis = this,
query = {
'lat': coordinates.lat,
'lon': coordinates.lng
};
$.ajax({
method: "GET",
url: URL_GEOLOC_SERVICE,
data: query,
success: function (geoLocationData) {
lthis.triggerLocationEvent(
lthis.formatLocationEventObject(locationData,geoLocationData)
);
lthis.geolocLabel.classList.remove('loading');
},
error: (err) => {
console.warn(err);
lthis.geolocLabel.classList.remove('loading');
}
});
};
Geoloc.prototype.triggerLocationEvent = function(locationDataObject) {
const locationEvent = new CustomEvent('location', {detail: locationDataObject});
this.mapEl.dispatchEvent(locationEvent);
};
Geoloc.prototype.formatLocationEventObject = function(locationData,geoLocationData) {
const detail = {
centroid: locationData.centroid,
geometry: locationData.geojson,
elevation: geoLocationData.altitude,
inseeData: {
code: 'FR' === geoLocationData.code_pays ? geoLocationData.code_zone : '',
nom: geoLocationData.nom
},
osmCountry: locationData.address.country,
osmCountryCode: geoLocationData.code_pays,
osmCounty: locationData.address.county,
osmPostcode: locationData.address.postcode,
locality: this.getLocalityFromData(locationData),
locality: geoLocationData.nom,
osmRoad: locationData.address.road,
osmState: locationData.address.state,
osmId: locationData.osm_id,
osmPlaceId: locationData.place_id
};
return detail;
};
Geoloc.prototype.handleCoordinates = function() {
if (!!this.latitudeEl.value && !!this.longitudeEl.value) {
this.handleNewLocation({
'lat': this.latitudeEl.value,
'lng': this.longitudeEl.value
});
}
};
Geoloc.prototype.onCoordinates = function() {
[this.latitudeEl,this.longitudeEl].forEach(coordinate =>
coordinate.addEventListener('blur', function() {
this.handleCoordinates();
}.bind(this))
);
};
Geoloc.prototype.initSearchLocality = function() {
const placesZone = document.getElementById('tb-places-zone');
if(placesZone) {
placesZone.classList.remove('hidden');
this.tbPlaces = new TbPlaces(this.tbPlacesCallback.bind(this));
this.tbPlaces.init();
}
};
Geoloc.prototype.tbPlacesCallback = function(localityData) {
const locality = this.getLocalityFromData(localityData);
if(!!locality) {
const coordinates = this.formatCoordinates({
'lat' : localityData.lat,
'lng' : localityData.lon
});
if(!!coordinates && !!coordinates.lat && !!coordinates.lng) {
this.zoom = 20;
this.setMapCoordinates(coordinates);
if('point' === this.geometryFilter) {
this.map.removeControl(this.drawControl);
this.createDraggableMarker();
this.handleMarkerEvents();
}
localityData.centroid = this.formatPointTypeCoordinates(coordinates);
if('rue' !== this.geometryFilter && 'Polygon' === localityData.geojson.type ) {
localityData.geojson = localityData.centroid;
}
this.loadGeolocation(coordinates,localityData);
}
}
};
Geoloc.prototype.getLocalityFromData = function(localityData) {
const addressData = localityData.address,
locationNameType = ['village', 'city', 'locality', 'municipality', 'county', 'state'].find(locationNameType => addressData[locationNameType] !== undefined);
if (!locationNameType) {
return;
}
return addressData[locationNameType];
};
Geoloc.prototype.closeMap = function () {
// reset map
this.map = L.DomUtil.get(this.mapIdAttr);
if (this.map != null) {
this.map._leaflet_id = null;
}
};