oedb-backend/frontend/src/app/pages/unlocated-events/unlocated-events.ts

407 lines
13 KiB
TypeScript

import { Component, inject, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { OedbApi } from '../../services/oedb-api';
import { OsmAuth } from '../../services/osm-auth';
import { ActivatedRoute } from '@angular/router';
interface NominatimResult {
place_id: number;
display_name: string;
lat: string;
lon: string;
type: string;
importance: number;
address?: {
house_number?: string;
road?: string;
postcode?: string;
city?: string;
state?: string;
country?: string;
};
}
@Component({
selector: 'app-unlocated-events-page',
standalone: true,
imports: [CommonModule, FormsModule],
templateUrl: './unlocated-events.html',
styleUrl: './unlocated-events.scss'
})
export class UnlocatedEventsPage implements OnInit {
OedbApi = inject(OedbApi);
private osmAuth = inject(OsmAuth);
private route = inject(ActivatedRoute);
events: Array<any> = [];
unlocatedEvents: Array<any> = [];
isLoading = false;
selectedEvent: any = null;
isEditing = false;
newKey = '';
newValue = '';
// Géolocalisation
searchQuery = '';
nominatimResults: NominatimResult[] = [];
isSearchingLocation = false;
selectedLocation: NominatimResult | null = null;
ngOnInit() {
this.route.queryParamMap.subscribe(map => {
const id = (map.get('id') || '').trim();
const what = (map.get('what') || '').trim();
const limitParam = map.get('limit');
const limit = limitParam ? Number(limitParam) : null;
if (id) {
this.loadSingleEvent(id);
} else {
this.loadEvents({ what: what || undefined, limit: limit || undefined });
}
});
}
loadEvents(overrides: { what?: string; limit?: number } = {}) {
this.isLoading = true;
const today = new Date();
const endDate = new Date(today);
endDate.setDate(today.getDate() + 30); // Charger 30 jours pour avoir plus d'événements
const params: any = {
start: today.toISOString().split('T')[0],
end: endDate.toISOString().split('T')[0],
limit: overrides.limit ?? 1000
};
if (overrides.what) params.what = overrides.what;
this.OedbApi.getEvents(params).subscribe((events: any) => {
this.events = Array.isArray(events?.features) ? events.features : [];
this.filterUnlocatedEvents();
this.isLoading = false;
});
}
loadSingleEvent(id: string | number) {
this.isLoading = true;
this.OedbApi.getEventById(id).subscribe({
next: (feature: any) => {
const f = (feature && (feature as any).type === 'Feature') ? feature : (feature?.feature || null);
this.events = f ? [f] : [];
this.filterUnlocatedEvents();
this.isLoading = false;
},
error: () => {
this.events = [];
this.unlocatedEvents = [];
this.isLoading = false;
}
});
}
filterUnlocatedEvents() {
this.unlocatedEvents = (this.events || []).filter(ev => {
// Vérifie si la géométrie est un point
if (!ev.geometry || ev.geometry.type !== 'Point') return false;
const coords = ev.geometry.coordinates;
// Vérifie si les coordonnées sont valides
if (!Array.isArray(coords) || coords.length !== 2) return true;
// Si les coordonnées sont [0,0], on considère comme non localisé
if (coords[0] === 0 && coords[1] === 0) return true;
// Si l'une des coordonnées est manquante ou nulle
if (coords[0] == null || coords[1] == null) return true;
return false;
});
}
selectEvent(event: any) {
this.selectedEvent = { ...event };
this.isEditing = true; // Ouvrir directement le formulaire d'édition
this.searchQuery = event?.properties?.where || '';
this.nominatimResults = [];
this.selectedLocation = null;
// S'assurer que l'événement a une géométrie valide
if (!this.selectedEvent.geometry) {
this.selectedEvent.geometry = {
type: 'Point',
coordinates: [0, 0]
};
}
// Si l'événement a une propriété 'where', proposer automatiquement une recherche
if (event?.properties?.where) {
this.searchLocation();
}
}
startEditing() {
this.isEditing = true;
}
cancelEditing() {
this.isEditing = false;
this.selectedEvent = null;
}
searchLocation() {
if (!this.searchQuery.trim()) {
this.nominatimResults = [];
return;
}
this.isSearchingLocation = true;
this.nominatimResults = [];
// Utiliser la propriété 'where' de l'événement si disponible, sinon utiliser la recherche manuelle
const searchTerm = this.selectedEvent?.properties?.where || this.searchQuery;
const url = `https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(searchTerm)}&limit=10&addressdetails=1&countrycodes=fr&extratags=1&accept-language=fr`;
fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(`Erreur HTTP: ${response.status}`);
}
return response.json();
})
.then((data: NominatimResult[]) => {
this.nominatimResults = data;
this.isSearchingLocation = false;
console.log('Résultats Nominatim:', data);
})
.catch(error => {
console.error('Erreur lors de la recherche Nominatim:', error);
this.isSearchingLocation = false;
// Afficher un message d'erreur à l'utilisateur
this.nominatimResults = [];
});
}
selectLocation(location: NominatimResult) {
this.selectedLocation = location;
if (this.selectedEvent) {
// Mettre à jour la géométrie
this.selectedEvent.geometry = {
type: 'Point',
coordinates: [parseFloat(location.lon), parseFloat(location.lat)]
};
// Mettre à jour les propriétés de l'événement
if (!this.selectedEvent.properties) {
this.selectedEvent.properties = {};
}
// Mettre à jour la propriété 'where' avec le nom du lieu
this.selectedEvent.properties.where = location.display_name;
// Ajouter d'autres propriétés utiles si elles n'existent pas
if (!this.selectedEvent.properties.label && !this.selectedEvent.properties.name) {
this.selectedEvent.properties.label = location.display_name;
}
// Ajouter des informations géographiques détaillées
this.selectedEvent.properties.lat = location.lat;
this.selectedEvent.properties.lon = location.lon;
// Ajouter des informations détaillées de Nominatim
if (location.address) {
if (location.address.house_number) this.selectedEvent.properties.housenumber = location.address.house_number;
if (location.address.road) this.selectedEvent.properties.street = location.address.road;
if (location.address.postcode) this.selectedEvent.properties.postcode = location.address.postcode;
if (location.address.city) this.selectedEvent.properties.city = location.address.city;
if (location.address.state) this.selectedEvent.properties.region = location.address.state;
if (location.address.country) this.selectedEvent.properties.country = location.address.country;
}
if (location.type) this.selectedEvent.properties.place_type = location.type;
if (location.importance) this.selectedEvent.properties.place_importance = location.importance.toString();
// Ajouter une note sur la source de géolocalisation
this.selectedEvent.properties.geocoding_source = 'Nominatim';
this.selectedEvent.properties.geocoding_date = new Date().toISOString();
// S'assurer que les coordonnées sont bien mises à jour dans le formulaire
this.updateCoordinates();
}
}
clearSearch() {
this.searchQuery = '';
this.nominatimResults = [];
this.selectedLocation = null;
this.isSearchingLocation = false;
}
updateCoordinates() {
// Cette méthode est appelée quand les coordonnées sont modifiées dans le formulaire
// Elle s'assure que la géométrie est correctement mise à jour
if (this.selectedEvent && this.selectedEvent.geometry) {
const lat = parseFloat(this.selectedEvent.geometry.coordinates[1]);
const lon = parseFloat(this.selectedEvent.geometry.coordinates[0]);
if (!isNaN(lat) && !isNaN(lon)) {
this.selectedEvent.geometry.coordinates = [lon, lat];
}
}
}
clearCoordinates() {
if (this.selectedEvent) {
this.selectedEvent.geometry = {
type: 'Point',
coordinates: [0, 0]
};
this.selectedLocation = null;
// Remettre à zéro les propriétés de localisation
if (this.selectedEvent.properties) {
this.selectedEvent.properties.where = '';
// Ne pas effacer le label/name s'ils existent déjà
}
}
}
validateCoordinates() {
if (this.selectedEvent && this.selectedEvent.geometry) {
const lat = this.selectedEvent.geometry.coordinates[1];
const lon = this.selectedEvent.geometry.coordinates[0];
if (this.areCoordinatesValid()) {
console.log('Coordonnées validées:', { lat, lon });
this.selectedEvent.geometry.coordinates = [lon, lat];
this.updateCoordinates();
// Ici on pourrait ajouter une validation supplémentaire ou une notification
}
}
}
areCoordinatesValid(): boolean {
if (!this.selectedEvent || !this.selectedEvent.geometry) return false;
const lat = this.selectedEvent.geometry.coordinates[1];
const lon = this.selectedEvent.geometry.coordinates[0];
// Vérifier que les coordonnées sont des nombres valides
if (isNaN(lat) || isNaN(lon)) return false;
// Vérifier que les coordonnées sont dans des plages valides
if (lat < -90 || lat > 90) return false;
if (lon < -180 || lon > 180) return false;
// Vérifier que ce ne sont pas les coordonnées par défaut (0,0)
if (lat === 0 && lon === 0) return false;
return true;
}
addProperty() {
if (this.newKey.trim() && this.newValue.trim()) {
if (!this.selectedEvent.properties) {
this.selectedEvent.properties = {};
}
this.selectedEvent.properties[this.newKey.trim()] = this.newValue.trim();
this.newKey = '';
this.newValue = '';
}
}
removeProperty(key: string) {
if (this.selectedEvent?.properties) {
delete this.selectedEvent.properties[key];
}
}
updateEvent() {
if (!this.selectedEvent) return;
this.isLoading = true;
const eventId = this.selectedEvent.id || this.selectedEvent.properties?.id;
if (eventId) {
// Mettre à jour un événement existant
this.OedbApi.updateEvent(eventId, this.selectedEvent).subscribe({
next: (response) => {
console.log('Événement mis à jour:', response);
this.loadEvents();
this.selectedEvent = null;
this.isEditing = false;
this.isLoading = false;
},
error: (error) => {
console.error('Erreur lors de la mise à jour:', error);
this.isLoading = false;
}
});
} else {
// Créer un nouvel événement
const osmUsername = this.osmAuth.getUsername();
if (osmUsername) {
this.selectedEvent.properties.last_modified_by = osmUsername;
}
this.OedbApi.createEvent(this.selectedEvent).subscribe({
next: (response) => {
console.log('Événement créé:', response);
this.loadEvents();
this.selectedEvent = null;
this.isEditing = false;
this.isLoading = false;
},
error: (error) => {
console.error('Erreur lors de la création:', error);
this.isLoading = false;
}
});
}
}
deleteEvent() {
if (!this.selectedEvent) return;
const eventId = this.selectedEvent.id || this.selectedEvent.properties?.id;
if (!eventId) return;
if (confirm('Êtes-vous sûr de vouloir supprimer cet événement ?')) {
this.isLoading = true;
this.OedbApi.deleteEvent(eventId).subscribe({
next: (response) => {
console.log('Événement supprimé:', response);
this.loadEvents();
this.selectedEvent = null;
this.isEditing = false;
this.isLoading = false;
},
error: (error) => {
console.error('Erreur lors de la suppression:', error);
this.isLoading = false;
}
});
}
}
getEventTitle(event: any): string {
return event?.properties?.what ||
event?.properties?.label ||
event?.properties?.name ||
'Événement sans nom';
}
getEventDescription(event: any): string {
return event?.properties?.description ||
event?.properties?.where ||
'Aucune description';
}
getObjectKeys(obj: any): string[] {
return Object.keys(obj || {});
}
isGeocodingProperty(prop: string): boolean {
const geocodingProps = [
'lat', 'lon', 'place_type', 'place_importance', 'housenumber', 'street',
'postcode', 'city', 'region', 'country', 'geocoding_source', 'geocoding_date'
];
return geocodingProps.includes(prop);
}
}