From 3146b28f3e8756e4b4d37a08a02acc8b3e592d29 Mon Sep 17 00:00:00 2001 From: Alexis Feron Date: Sat, 3 May 2025 23:42:27 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20pin=20filters?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../leaflet-map/leaflet-map.component.html | 38 +++++ .../leaflet-map/leaflet-map.component.ts | 156 +++++++++++++----- .../components/navbar/navbar.component.html | 23 ++- 3 files changed, 173 insertions(+), 44 deletions(-) diff --git a/src/app/components/leaflet-map/leaflet-map.component.html b/src/app/components/leaflet-map/leaflet-map.component.html index 2d422b6..ab8a41d 100644 --- a/src/app/components/leaflet-map/leaflet-map.component.html +++ b/src/app/components/leaflet-map/leaflet-map.component.html @@ -1,3 +1,41 @@
+ +
+
+ + + + + +
+
diff --git a/src/app/components/leaflet-map/leaflet-map.component.ts b/src/app/components/leaflet-map/leaflet-map.component.ts index 380f18a..189f1b6 100644 --- a/src/app/components/leaflet-map/leaflet-map.component.ts +++ b/src/app/components/leaflet-map/leaflet-map.component.ts @@ -1,22 +1,35 @@ +import { NgFor } from '@angular/common'; import { Component, OnInit, ViewContainerRef } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import * as L from 'leaflet'; import 'leaflet/dist/leaflet.css'; import { Pin } from '../../model/Pin'; +import { AutocompleteService } from '../../services/auto-complete/auto-complete.service'; import { PinService } from '../../services/pin/pin.service'; import { PinMarkerComponent } from '../pin-marker/pin-marker.component'; @Component({ selector: 'app-leaflet-map', templateUrl: './leaflet-map.component.html', + standalone: true, + imports: [NgFor, FormsModule], }) export class LeafletMapComponent implements OnInit { private map!: L.Map; private markersMap: { [key: string]: L.Marker } = {}; + private allPins: Pin[] = []; + private pinCountries: { [pinId: string]: string } = {}; + + availableCountries: string[] = []; + availablePersons: string[] = []; + selectedCountry: string = ''; + selectedPerson: string = ''; constructor( private viewContainerRef: ViewContainerRef, private pinsService: PinService, + private autocompleteService: AutocompleteService, private route: ActivatedRoute, private router: Router ) {} @@ -26,67 +39,128 @@ export class LeafletMapComponent implements OnInit { } private initializeMap(): void { - // Initialize the map this.map = L.map('map', { - maxBounds: L.latLngBounds( - L.latLng(-90, -180), // South-West - L.latLng(90, 180) // North-East - ), - maxBoundsViscosity: 1.0, // Prevent dragging the map out of bounds - minZoom: 2, // Prevent zooming out too much + maxBounds: L.latLngBounds(L.latLng(-90, -180), L.latLng(90, 180)), + maxBoundsViscosity: 1.0, + minZoom: 2, }).setView([46.603354, 1.888334], 6); - // Add OpenStreetMap tiles L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '', }).addTo(this.map); this.map.attributionControl.setPrefix(''); - // Define custom icons + this.pinsService.getPins().subscribe((pins: Pin[]) => { + this.allPins = pins; + this.extractPersons(pins); + + const countrySet = new Set(); + const requests = pins.map((pin) => + this.autocompleteService + .getAddressFromCoordinates(pin.location[0], pin.location[1]) + .toPromise() + .then((res: any) => { + const address = res?.address; + const country = + address?.country || + this.extractLastFromDisplayName(res?.display_name); + if (country) { + this.pinCountries[pin.id] = country; + countrySet.add(country); + } + }) + .catch((err: any) => { + console.error( + 'Erreur lors de la récupération du pays pour le pin', + pin.id, + err + ); + }) + ); + + Promise.all(requests).then(() => { + this.availableCountries = Array.from(countrySet).sort(); + this.renderPins(); + }); + }); + } + + private extractLastFromDisplayName(displayName: string): string { + if (!displayName) return ''; + const parts = displayName.split(','); + return parts[parts.length - 1].trim(); + } + + private extractPersons(pins: Pin[]): void { + const personsSet = new Set(); + const regex = /@(\w+)/g; + pins.forEach((pin) => { + const desc = pin.description || ''; + let match; + while ((match = regex.exec(desc)) !== null) { + personsSet.add(match[1]); + } + }); + this.availablePersons = Array.from(personsSet).sort(); + } + + onCountryChange(country: string) { + this.selectedCountry = country; + this.renderPins(); + } + + onPersonChange(person: string) { + this.selectedPerson = person; + this.renderPins(); + } + + private renderPins(): void { + // Remove existing markers + Object.values(this.markersMap).forEach((marker) => + this.map.removeLayer(marker) + ); + this.markersMap = {}; + const visitedIcon = this.createDivIcon(` `); - const notVisitedIcon = this.createDivIcon(` - - - - `); - - this.pinsService.getPins().subscribe((pins: Pin[]) => { - // Add markers - pins.forEach((pin: Pin) => { - //const icon = pin.visited ? visitedIcon : notVisitedIcon; - const icon = visitedIcon; - - const marker = L.marker(pin.location as [number, number], { - icon, - }).addTo(this.map); + const filteredPins = this.allPins.filter((pin) => { + const pinCountry = this.pinCountries[pin.id]; + const matchesCountry = this.selectedCountry + ? pinCountry === this.selectedCountry + : true; + const matchesPerson = this.selectedPerson + ? pin.description?.includes(`@${this.selectedPerson}`) + : true; + return matchesCountry && matchesPerson; + }); - marker.on('popupclose', () => { - this.router.navigate(['/map']); - }); + filteredPins.forEach((pin) => { + const marker = L.marker(pin.location as [number, number], { + icon: visitedIcon, + }).addTo(this.map); - marker.on('popupopen', () => { - this.router.navigate(['/map'], { queryParams: { pin: pin.id } }); - }); + marker.on('popupclose', () => { + this.router.navigate(['/map']); + }); - // Dynamically create Angular component and attach it to popup - const popupDiv = document.createElement('div'); - const componentRef = - this.viewContainerRef.createComponent(PinMarkerComponent); + marker.on('popupopen', () => { + this.router.navigate(['/map'], { queryParams: { pin: pin.id } }); + }); - componentRef.instance.pin = pin; - componentRef.instance.marker = marker; - popupDiv.appendChild(componentRef.location.nativeElement); + const popupDiv = document.createElement('div'); + const componentRef = + this.viewContainerRef.createComponent(PinMarkerComponent); + componentRef.instance.pin = pin; + componentRef.instance.marker = marker; + popupDiv.appendChild(componentRef.location.nativeElement); - marker.bindPopup(popupDiv, { closeButton: false, minWidth: 150 }); + marker.bindPopup(popupDiv, { closeButton: false, minWidth: 150 }); - // Stocker les marqueurs par ID - this.markersMap[pin.id] = marker; - }); + this.markersMap[pin.id] = marker; // Ouvrir automatiquement la pop-up si un ID est passé dans l'URL this.route.queryParams.subscribe((params) => { diff --git a/src/app/components/navbar/navbar.component.html b/src/app/components/navbar/navbar.component.html index 271ece8..7c3eebe 100644 --- a/src/app/components/navbar/navbar.component.html +++ b/src/app/components/navbar/navbar.component.html @@ -5,7 +5,7 @@ Memory Map Logo Memory Map @@ -136,9 +136,26 @@