ldc-lionor/resources/ts/fo/components/GeocodageComponent.vue
2025-04-15 12:48:19 +02:00

344 lines
11 KiB
Vue

<template>
<div class="col-12">
<div id="map" />
</div>
</template>
<script lang="ts">
import axios from "axios";
import parseLaravelErrorMessages from "./../../modules/parseLaravelErrorMessages";
import route from "./../../modules/route";
import toast from "./../../modules/toast";
import trans from "./../../modules/trans";
import L from "leaflet";
import { defineComponent } from "vue";
export default defineComponent({
mixins: [route, trans, toast, parseLaravelErrorMessages],
props: {
signsStickers: {
type: Array,
default: null,
},
signsLabels: {
type: Array,
default: null,
},
formId: {
type: String,
default: null,
},
},
data(): {
formAddress: HTMLFormElement;
addressInput: string;
address: string;
fullAddress: Array<string>;
searchedAddress: any;
shops: Array<{
address: string;
city: string;
distance: number;
latitude: number;
longitude: number;
name: string;
postcode: number;
signs: any;
}>;
userPos: { latitude?: number; longitude?: number };
map: any;
centerPos: { latitude?: number; longitude?: number };
stickers: Record<string, string>;
labels: Array<string>;
loading: boolean;
currentPage: number;
} {
return {
formAddress: document.getElementById(this.formId) as HTMLFormElement,
addressInput: "",
address: "",
fullAddress: [],
searchedAddress: null,
userPos: {},
map: null,
centerPos: {},
shops: [],
stickers: {},
labels: [],
loading: false,
currentPage: 1,
};
},
mounted() {
const json = String(this.$attrs.json ?? "{}");
if (json.length) {
const data = JSON.parse(json);
if (data.signsStickers) {
this.stickers = data.signsStickers;
}
if (data.signsLabels) {
this.labels = data.signsLabels;
}
if (data.formId) {
this.formAddress = document.getElementById(
data.formId
) as HTMLFormElement;
this.formAddress?.addEventListener("submit", this.searchAddress);
}
}
const getAllShopsRoute = this.route("shops.index");
if (!getAllShopsRoute) {
throw Error("Undefined route 'shops.index'");
}
this.loading = true;
axios
.get(getAllShopsRoute)
.then((response) => {
this.shops = response.data;
this.initMap();
})
.then(() => (this.loading = false))
.catch((e) => {
console.log(e);
let message = this.__("http-statuses.0");
if (e.response.status === 422) {
message =
this.parseValidationErrors(e.response?.data?.errors ?? {}) ||
message;
if (window.vueDebug) {
console.warn(e.response.data.errors);
}
} else if (e.response.status === 419) {
// * CSRF TOKEN MISMATCH (On ne pourrais plus faire d'appels AJAX sans recharger la page)
setTimeout(() => window.location.reload(), 2000);
} else if (e.response.status === 403) {
// * Access denied
setTimeout(() => window.location.reload(), 2000);
} else {
if (window.vueDebug) {
console.error(e);
}
}
this.errorNotify(this.__(`http-statuses.${e.response.status}`));
this.errorNotify(message);
});
},
methods: {
searchAddress(event?: Event) {
event?.preventDefault();
const formInput = this.formAddress.firstElementChild as HTMLInputElement;
if (formInput.value) {
this.address = formInput.value.split(" ").join("+");
}
var instance = axios.create();
delete instance.defaults.headers.common["X-CSRF-TOKEN"];
instance
.get("https://api-adresse.data.gouv.fr/search/?q=" + this.address)
.then((response) => {
this.userPos = {};
this.searchedAddress = response.data.features[0];
this.calcPosition();
})
.then(() => (this.loading = false))
.catch((e) => {
console.log(e);
let message = this.__("http-statuses.0");
if (e.response.status === 422) {
message =
this.parseValidationErrors(e.response?.data?.errors ?? {}) ||
message;
if (window.vueDebug) {
console.warn(e.response.data.errors);
}
} else if (e.response.status === 419) {
// * CSRF TOKEN MISMATCH (On ne pourrais plus faire d'appels AJAX sans recharger la page)
setTimeout(() => window.location.reload(), 2000);
} else if (e.response.status === 403) {
// * Access denied
setTimeout(() => window.location.reload(), 2000);
} else {
if (window.vueDebug) {
console.error(e);
}
}
this.errorNotify(this.__(`http-statuses.${e.response.status}`));
this.errorNotify(message);
});
},
findNearest(userLat: number, userLon: number) {
const getShopsRoute = this.route("shops.nearest", {
latitude: userLat,
longitude: userLon
});
if (!getShopsRoute) {
throw Error("Undefined route 'shops.nearest'");
}
this.loading = true;
axios
.get(getShopsRoute)
.then((response) => {
this.shops = response.data;
this.displayShopsList();
this.displayMarkers(userLat, userLon);
})
.then(() => (this.loading = false))
.catch((e) => {
console.log(e);
let message = this.__("http-statuses.0");
if (e.response.status === 422) {
message =
this.parseValidationErrors(e.response?.data?.errors ?? {}) ||
message;
if (window.vueDebug) {
console.warn(e.response.data.errors);
}
} else if (e.response.status === 419) {
// * CSRF TOKEN MISMATCH (On ne pourrais plus faire d'appels AJAX sans recharger la page)
setTimeout(() => window.location.reload(), 2000);
} else if (e.response.status === 403) {
// * Access denied
setTimeout(() => window.location.reload(), 2000);
} else {
if (window.vueDebug) {
console.error(e);
}
}
this.errorNotify(this.__(`http-statuses.${e.response.status}`));
this.errorNotify(message);
});
},
setPosition() {
this.userPos = {};
this.searchAddress();
},
calcPosition() {
let userLon = null;
let userLat = null;
if (this.userPos.latitude == null) {
userLon = this.searchedAddress.geometry.coordinates[0];
userLat = this.searchedAddress.geometry.coordinates[1];
} else {
userLon = this.userPos.longitude;
userLat = this.userPos.latitude;
}
this.findNearest(userLat, userLon);
},
setCurrentPosition() {
navigator.geolocation.getCurrentPosition(this.savePosition);
},
savePosition(pos: any) {
this.userPos = pos.coords;
this.calcPosition();
},
displayMarkers(userLat: number, userLon: number) {
let map = this.map;
map.eachLayer(function (layer: L.Layer) {
map.removeLayer(layer);
});
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
maxZoom: 16,
attribution: "&copy; <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a>"
}).addTo(this.map);
this.map.setView([userLat, userLon], 8);
for (let shop of this.shops) {
L.marker([shop.latitude, shop.longitude])
.bindPopup(shop.name)
.addTo(map);
}
},
defineZoom(object: any) {
this.centerPos.latitude = object.latitude;
this.centerPos.longitude = object.longitude;
return this.zoom();
},
zoom() {
this.map.setView([this.centerPos.latitude, this.centerPos.longitude], 13);
},
// Initialize map with markers for all shops in database
initMap() {
// View set with coords and zoom for France.
this.map = L.map("map", {attributionControl: false}).setView([47, 2], 6);
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
maxZoom: 16,
attribution: "&copy; <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a>"
}).addTo(this.map);
// Remove Ukrainian flag.
const myAttrControl = L.control.attribution().addTo(this.map);
myAttrControl.setPrefix("<a href=\"https://leafletjs.com/\">Leaflet</a>");
for (let shop of this.shops) {
L.marker([shop.latitude, shop.longitude])
.bindPopup(shop.name)
.addTo(this.map);
}
},
displayShopsList() {
const list = document.getElementById("shops-list") as HTMLElement;
const baseItem = document.getElementById("shop-list-item") as HTMLElement;
list.replaceChildren(baseItem);
let i = 1;
for (let shop of this.shops) {
let shopListElement = baseItem?.cloneNode(true) as HTMLElement;
// Set the shop sign logo
const imgTag = shopListElement.childNodes[1]
.childNodes[1] as HTMLElement;
imgTag.setAttribute("src", this.stickers[shop.signs]);
imgTag.setAttribute("alt", "Logo " + this.labels[shop.signs]);
// Display the name and address
shopListElement.childNodes[1].childNodes[3].childNodes[1].textContent =
shop.name;
shopListElement.childNodes[1].childNodes[3].childNodes[3].textContent =
shop.address + ", " + shop.postcode + " " + shop.city;
// Display the distance
let unit = " km";
if (shop.distance === undefined) {
unit = " mètres";
shop.distance = shop.distance * 1000;
}
shopListElement.childNodes[1].childNodes[3].childNodes[5].textContent =
shop.distance?.toString() + unit;
// Link to get the journey on Google Maps
let aTag = shopListElement.childNodes[3] as HTMLElement;
aTag.setAttribute(
"href",
"https://www.google.com/maps/dir/?api=1&origin=" +
this.searchedAddress.geometry.coordinates[1] +
"," +
this.searchedAddress.geometry.coordinates[0] +
"&destination=" +
shop.latitude +
"," +
shop.longitude
);
aTag.setAttribute("target", "_blank");
shopListElement.classList.remove("d-none");
shopListElement.setAttribute("id", "shop-list-item-" + i);
shopListElement.addEventListener("click", () => {
this.defineZoom(shop);
});
list.appendChild(shopListElement);
i++;
}
},
},
watch: {
addressInput() {
let addressInputField = document.getElementById(
"address"
) as HTMLInputElement;
this.addressInput = addressInputField.value;
this.fullAddress = this.addressInput.split(",");
this.address = this.fullAddress[0].split(" ").join("+");
},
},
});
</script>
<style>
#map {
height: 500px;
}
</style>