diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 90a61d9b..38d70b49 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -24,6 +24,7 @@ "@pinetwork-js/sdk": "^0.7.0", "axios": "^1.6.7", "leaflet": "^1.9.4", + "leaflet-routing-machine": "^3.2.12", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3" @@ -3318,6 +3319,22 @@ "node": ">= 0.4" } }, + "node_modules/@mapbox/corslite": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@mapbox/corslite/-/corslite-0.0.7.tgz", + "integrity": "sha512-w/uS474VFjmqQ7fFWIMZINQM1BAQxDLuoJaZZIPES1BmeYpCtlh9MtbFxKGGDAsfvut8/HircIsVvEYRjQ+iMg==" + }, + "node_modules/@mapbox/polyline": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@mapbox/polyline/-/polyline-0.2.0.tgz", + "integrity": "sha512-GCddO0iw6AzOQqZgBmjEQI9Pgo40/yRgkTkikGctE01kNBN0ThWYuAnTD+hRWrAWMV6QJ0rNm4m8DAsaAXE7Pg==", + "bin": { + "polyline": "bin/polyline.bin.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/@material/animation": { "version": "15.0.0-canary.7f224ddd4.0", "resolved": "https://registry.npmjs.org/@material/animation/-/animation-15.0.0-canary.7f224ddd4.0.tgz", @@ -10451,6 +10468,16 @@ "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==" }, + "node_modules/leaflet-routing-machine": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/leaflet-routing-machine/-/leaflet-routing-machine-3.2.12.tgz", + "integrity": "sha512-HLde58G1YtD9xSIzZavJ6BPABZaV1hHeGst8ouhzuxmSC3s32NVtADT+njbIUMW1maHRCrsgTk/E4hz5QH7FrA==", + "dependencies": { + "@mapbox/corslite": "0.0.7", + "@mapbox/polyline": "^0.2.0", + "osrm-text-instructions": "^0.13.2" + } + }, "node_modules/less": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz", @@ -12209,6 +12236,11 @@ "node": ">=0.10.0" } }, + "node_modules/osrm-text-instructions": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/osrm-text-instructions/-/osrm-text-instructions-0.13.4.tgz", + "integrity": "sha512-ge4ZTIetMQKAHKq2MwWf83ntzdJN20ndRKRaVNoZ3SkDkBNO99Qddz7r6+hrVx38I+ih6Rk5T1yslczAB6Q9Pg==" + }, "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index bf8b5e52..bea2403b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -32,6 +32,7 @@ "@pinetwork-js/sdk": "^0.7.0", "axios": "^1.6.7", "leaflet": "^1.9.4", + "leaflet-routing-machine": "^3.2.12", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3" diff --git a/frontend/src/app/core/service/geolocation.service.ts b/frontend/src/app/core/service/geolocation.service.ts index 27a1194c..6b3bd54c 100644 --- a/frontend/src/app/core/service/geolocation.service.ts +++ b/frontend/src/app/core/service/geolocation.service.ts @@ -7,7 +7,7 @@ import axios from 'axios'; providedIn: 'root', }) export class GeolocationService { - private readonly initZoomLevel = 15; + private readonly initZoomLevel = 18; initCoords: number[] = []; private readonly geolocationTrigger = new Subject(); private readonly maxZoomLevel = 18; @@ -74,6 +74,18 @@ export class GeolocationService { shadowUrl: 'assets/marker-shadow.png', }); } + getMiddleIcon(): Icon { + return icon({ + ...Icon.Default.prototype.options, + iconUrl: 'assets/yellow.png', + iconRetinaUrl: 'assets/marker-icon.png', + shadowUrl: 'assets/marker-shadow.png', + iconSize: [41, 41], + iconAnchor: [12, 41], + popupAnchor: [1, -34], + shadowSize: [41, 41], + }); + } async getUserPositionManualy() { const response = await axios.get('https://ipapi.co/json/'); diff --git a/frontend/src/app/dialogs/social/social.component.html b/frontend/src/app/dialogs/social/social.component.html index 073f5e11..94d3ff0f 100644 --- a/frontend/src/app/dialogs/social/social.component.html +++ b/frontend/src/app/dialogs/social/social.component.html @@ -1,10 +1,5 @@

Map Of Pi Invitation - X

@@ -27,7 +22,12 @@

diff --git a/frontend/src/app/home/action-row/action-row.component.scss b/frontend/src/app/home/action-row/action-row.component.scss index a4287999..126f4847 100644 --- a/frontend/src/app/home/action-row/action-row.component.scss +++ b/frontend/src/app/home/action-row/action-row.component.scss @@ -1,11 +1,10 @@ .action-row-container { - position: fixed; - top: 70%; /* Adjust this value as needed */ - width: 100%; - min-width: 80px; - margin-bottom: 2px; - display: flex; - justify-content: center; - z-index: 5; + position: fixed; + top: 80%; /* Adjust this value as needed */ + width: 100%; + min-width: 80px; + margin-bottom: 2px; + display: flex; + justify-content: center; + z-index: 5; } - \ No newline at end of file diff --git a/frontend/src/app/home/map/map.component.scss b/frontend/src/app/home/map/map.component.scss index d04a310c..4f2ed031 100644 --- a/frontend/src/app/home/map/map.component.scss +++ b/frontend/src/app/home/map/map.component.scss @@ -2,3 +2,57 @@ height: calc(100vh - var(--mat-toolbar-mobile-height)); z-index: 0; } + +/* Customize the route popup */ +.leaflet-routing-container { + background-color: white; /* Change background color */ + border: 2px solid #ca1010; /* Add border */ + border-radius: 5px; /* Add border radius */ + padding: 10px; /* Add padding */ + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* Add shadow */ +} + +.leaflet-routing-container .leaflet-routing-instructions { + margin-bottom: 5px; /* Adjust margin */ +} + +.leaflet-routing-container .leaflet-routing-instructions ul { + list-style: none; /* Remove list styles */ + padding: 0; /* Remove padding */ + margin: 0; /* Remove margin */ +} + +.leaflet-routing-container .leaflet-routing-instructions ul li { + padding: 5px 0; /* Add padding */ +} + +.leaflet-routing-container + .leaflet-routing-instructions + ul + li + span.leaflet-routing-text { + font-weight: bold; /* Make text bold */ +} + +.leaflet-routing-container + .leaflet-routing-instructions + ul + li + span.leaflet-routing-icon { + margin-right: 5px; /* Adjust margin */ +} + +/* Customize the close button */ +.leaflet-routing-container .leaflet-routing-popup .leaflet-popup-close-button { + color: #333; /* Change color */ + font-size: 16px; /* Adjust font size */ + top: 5px; /* Adjust top position */ + right: 5px; /* Adjust right position */ +} + +/* Hover effect for close button */ +.leaflet-routing-container + .leaflet-routing-popup + .leaflet-popup-close-button:hover { + color: #000; /* Change color on hover */ +} diff --git a/frontend/src/app/home/map/map.component.ts b/frontend/src/app/home/map/map.component.ts index 8f567e02..43403011 100644 --- a/frontend/src/app/home/map/map.component.ts +++ b/frontend/src/app/home/map/map.component.ts @@ -3,11 +3,12 @@ import { LeafletModule } from '@asymmetrik/ngx-leaflet'; import { Map, marker, Layer } from 'leaflet'; import { GeolocationService } from '../../core/service/geolocation.service'; import { SnackService } from '../../core/service/snack.service'; -import { take } from 'rxjs'; import { dummyCoordinates } from '../../core/model/business'; import { Router, RouterModule } from '@angular/router'; import { ShopService } from '../../core/service/shop.service'; import axios from 'axios'; +import 'leaflet-routing-machine'; +import * as L from 'leaflet'; @Component({ selector: 'app-map', @@ -49,34 +50,10 @@ export class MapComponent implements OnInit { } locateMe(): void { - this.geolocationService - .getCurrentPosition() - .pipe(take(1)) - .subscribe({ - next: (coords: GeolocationCoordinates) => { - this.layer = marker([coords.latitude, coords.longitude], { - icon: this.geolocationService.getUserMarkerIcon(), - }) - .bindPopup('A pretty CSS popup.
Easily customizable.') - .openPopup(); - - this.map.addLayer(this.layer); - this.map.flyTo([coords.latitude, coords.longitude], 15); - - // Add all other coordinates to the map - this.addAllCoordinatesToMap(); - - this.cdr.detectChanges(); - }, - error: (error: string) => { - this.snackService.showError(`Geolocation error: ${error}`); - }, - }); + this.track(); } async track() { - this.snackService.showMessage(`Locating your position`); - console.log('Coordinates from shop in track: ', this.shopService.getUserPosition()); const location = await axios.get('https://ipapi.co/json/'); @@ -89,7 +66,7 @@ export class MapComponent implements OnInit { const userMarker = marker([coord[0], coord[1]], { icon: this.geolocationService.getUserMarkerIcon(), }) - .bindPopup('User location') + .bindPopup(`
You're Here
`) .openPopup(); this.map.addLayer(userMarker); @@ -150,8 +127,89 @@ export class MapComponent implements OnInit { .addEventListener('click', (e) => { const commingBtn = document.querySelectorAll('#comming'); commingBtn.forEach((btn) => { - btn.addEventListener('click', () => { - this.snackService.showMessage('This feature is in development'); + btn.addEventListener('click', async () => { + const location = await axios.get('https://ipapi.co/json/'); + + const { data } = location; + + const userLocation = [[data.latitude, data.longitude]]; + + const shopLocation = [shop.coordinates[0], shop.coordinates[1]]; + + this.map.closePopup(); + + const creatingMarkers = (i: number, waypoints: any, n: number) => { + let icon; + + if (i === 0) { + icon = this.geolocationService.getUserMarkerIcon(); + } else if (i === n - 1) { + icon = this.geolocationService.getMarkerIcon(); + } else { + icon = this.geolocationService.getMiddleIcon(); + } + + const newMarker = L.marker(waypoints.latLng, { + icon: icon, + }).addTo(this.map); + + newMarker.getElement()?.setAttribute('data-custom-type', i === 0 ? 'user' : i === n - 1 ? 'shop' : 'middle'); + + newMarker.addEventListener('click', (e) => { + const customType = e.target.getElement()?.getAttribute('data-custom-type'); + + switch (customType) { + case 'user': + this.snackService.showMessage(`Dear Soleil00 You"re located her`); + break; + case 'shop': + newMarker.bindPopup(` +
+
${shop.name}
+
${shop.name} is located here and you are about to take routes towards it. It will approximately take you 23 min by car.
+ +
+ `); + + // Wait for the popup to open before attaching the event listener + newMarker.on('popupopen', () => { + const cancelBtn = document.getElementById('cancelBtn'); + if (cancelBtn) { + cancelBtn.addEventListener('click', () => { + // routeControl.removeFrom(this.map); + this.map.removeControl(routeControl); + newMarker.removeFrom(this.map); + console.log('Button clicked'); + }); + } + }); + + break; + case 'middle': + this.snackService.showMessage('Middle clicked'); + break; + default: + this.snackService.showMessage('Unknown marker clicked'); + } + }); + }; + + const routeControl = (window as any).L.Routing.control({ + waypoints: [(window as any).L.latLng(userLocation[0], userLocation[1]), (window as any).L.latLng(shopLocation[0], shopLocation[1])], + showAlternatives: true, + createMarker: creatingMarkers, + altLineOptions: { + styles: [ + { color: 'black', opacity: 0.15, weight: 9 }, + { color: 'white', opacity: 0.8, weight: 6 }, + { color: 'blue', opacity: 0.5, weight: 2 }, + ], + }, + }); + + console.log('from routing : ', { ...routeControl }); + console.log('type of routes : ', typeof routeControl); + routeControl.addTo(this.map); }); }); diff --git a/frontend/src/assets/yellow.png b/frontend/src/assets/yellow.png new file mode 100644 index 00000000..77acefd3 Binary files /dev/null and b/frontend/src/assets/yellow.png differ diff --git a/frontend/src/index.html b/frontend/src/index.html index cf4f7af8..751b6826 100644 --- a/frontend/src/index.html +++ b/frontend/src/index.html @@ -1,32 +1,64 @@ + + + Map of Pi + + + + + + + + + + + + + + + + + + + - - - Map of Pi - - - - - - - - - - - - - - - - - - - - - - - + + + + + + diff --git a/package-lock.json b/package-lock.json index 7b3bb33a..caad2c2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,41 @@ "": { "name": "map-of-pi", "version": "0.0.3", - "hasInstallScript": true + "hasInstallScript": true, + "dependencies": { + "leaflet-routing-machine": "^3.2.12" + } + }, + "node_modules/@mapbox/corslite": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@mapbox/corslite/-/corslite-0.0.7.tgz", + "integrity": "sha512-w/uS474VFjmqQ7fFWIMZINQM1BAQxDLuoJaZZIPES1BmeYpCtlh9MtbFxKGGDAsfvut8/HircIsVvEYRjQ+iMg==" + }, + "node_modules/@mapbox/polyline": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@mapbox/polyline/-/polyline-0.2.0.tgz", + "integrity": "sha512-GCddO0iw6AzOQqZgBmjEQI9Pgo40/yRgkTkikGctE01kNBN0ThWYuAnTD+hRWrAWMV6QJ0rNm4m8DAsaAXE7Pg==", + "bin": { + "polyline": "bin/polyline.bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/leaflet-routing-machine": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/leaflet-routing-machine/-/leaflet-routing-machine-3.2.12.tgz", + "integrity": "sha512-HLde58G1YtD9xSIzZavJ6BPABZaV1hHeGst8ouhzuxmSC3s32NVtADT+njbIUMW1maHRCrsgTk/E4hz5QH7FrA==", + "dependencies": { + "@mapbox/corslite": "0.0.7", + "@mapbox/polyline": "^0.2.0", + "osrm-text-instructions": "^0.13.2" + } + }, + "node_modules/osrm-text-instructions": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/osrm-text-instructions/-/osrm-text-instructions-0.13.4.tgz", + "integrity": "sha512-ge4ZTIetMQKAHKq2MwWf83ntzdJN20ndRKRaVNoZ3SkDkBNO99Qddz7r6+hrVx38I+ih6Rk5T1yslczAB6Q9Pg==" } } } diff --git a/package.json b/package.json index 80d91ae7..e0032d4a 100644 --- a/package.json +++ b/package.json @@ -5,5 +5,8 @@ "scripts": { "install": "npm install --prefix frontend && npm install --prefix backend", "build": "npm run -C frontend build" + }, + "dependencies": { + "leaflet-routing-machine": "^3.2.12" } }