Show marker dot on trip itinerary map

This commit is contained in:
itskovacs 2025-07-25 18:09:08 +02:00
parent 002555ce37
commit c9df0c6d33
2 changed files with 113 additions and 43 deletions

View File

@ -16,7 +16,12 @@ import {
TripStatus, TripStatus,
} from "../../types/trip"; } from "../../types/trip";
import { Place } from "../../types/poi"; import { Place } from "../../types/poi";
import { createMap, placeToMarker, createClusterGroup } from "../../shared/map"; import {
createMap,
placeToMarker,
createClusterGroup,
tripDayMarker,
} from "../../shared/map";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { DialogService, DynamicDialogRef } from "primeng/dynamicdialog"; import { DialogService, DynamicDialogRef } from "primeng/dynamicdialog";
import { TripPlaceSelectModalComponent } from "../../modals/trip-place-select-modal/trip-place-select-modal.component"; import { TripPlaceSelectModalComponent } from "../../modals/trip-place-select-modal/trip-place-select-modal.component";
@ -58,7 +63,7 @@ export class TripComponent implements AfterViewInit {
placesUsedInTable = new Set<number>(); placesUsedInTable = new Set<number>();
trip: Trip | undefined; trip: Trip | undefined;
tripMapAntLayer: undefined; tripMapAntLayer: L.LayerGroup<any> | undefined;
tripMapAntLayerDayID: number | undefined; tripMapAntLayerDayID: number | undefined;
isMapFullscreen: boolean = false; isMapFullscreen: boolean = false;
@ -294,6 +299,7 @@ export class TripComponent implements AfterViewInit {
toggleMapFullscreen() { toggleMapFullscreen() {
this.isMapFullscreen = !this.isMapFullscreen; this.isMapFullscreen = !this.isMapFullscreen;
document.body.classList.toggle("overflow-hidden");
setTimeout(() => { setTimeout(() => {
this.map.invalidateSize(); this.map.invalidateSize();
@ -365,28 +371,49 @@ export class TripComponent implements AfterViewInit {
if (!this.trip) return; if (!this.trip) return;
const items = this.trip.days.flatMap((day) => const items = this.trip.days
day.items.sort((a, b) => a.time.localeCompare(b.time)), .flatMap((day) => day.items.sort((a, b) => a.time.localeCompare(b.time)))
);
const coords = items
.map((item) => { .map((item) => {
if (item.lat && item.lng) return [item.lat, item.lng]; if (item.lat && item.lng)
if (item.place && item.place) return [item.place.lat, item.place.lng]; return { text: item.text, lat: item.lat, lng: item.lng };
if (item.place && item.place)
return { text: item.text, lat: item.place.lat, lng: item.place.lng };
return undefined; return undefined;
}) })
.filter((n): n is number[] => n !== undefined); .filter((n) => n !== undefined);
this.map.fitBounds(coords, { padding: [30, 30] });
const path = antPath(coords, { if (items.length < 2) {
delay: 600, this.utilsService.toast(
dashArray: [10, 20], "info",
weight: 5, "Info",
color: "#0000FF", "Not enough values to map an itinerary",
pulseColor: "#FFFFFF", );
paused: false, return;
reverse: false, }
hardwareAccelerated: true,
this.map.fitBounds(
items.map((c) => [c.lat, c.lng]),
{ padding: [30, 30] },
);
const path = antPath(
items.map((c) => [c.lat, c.lng]),
{
delay: 600,
dashArray: [10, 20],
weight: 5,
color: "#0000FF",
pulseColor: "#FFFFFF",
paused: false,
reverse: false,
hardwareAccelerated: true,
},
);
const layGroup = L.layerGroup();
layGroup.addLayer(path);
items.forEach((item) => {
layGroup.addLayer(tripDayMarker(item));
}); });
if (this.tripMapAntLayer) { if (this.tripMapAntLayer) {
@ -396,10 +423,10 @@ export class TripComponent implements AfterViewInit {
// UX // UX
setTimeout(() => { setTimeout(() => {
this.map.addLayer(path); layGroup.addTo(this.map);
}, 200); }, 200);
this.tripMapAntLayer = path; this.tripMapAntLayer = layGroup;
this.tripMapAntLayerDayID = -1; //Hardcoded value for global trace this.tripMapAntLayerDayID = -1; //Hardcoded value for global trace
} }
@ -416,7 +443,19 @@ export class TripComponent implements AfterViewInit {
if (!this.trip || index == -1) return; if (!this.trip || index == -1) return;
const data = this.trip.days[index as number].items; const data = this.trip.days[index as number].items;
if (data.length == 1) {
data.sort((a, b) => a.time.localeCompare(b.time));
const items = data
.map((item) => {
if (item.lat && item.lng)
return { text: item.text, lat: item.lat, lng: item.lng };
if (item.place && item.place)
return { text: item.text, lat: item.place.lat, lng: item.place.lng };
return undefined;
})
.filter((n) => n !== undefined);
if (items.length < 2) {
this.utilsService.toast( this.utilsService.toast(
"info", "info",
"Info", "Info",
@ -425,25 +464,29 @@ export class TripComponent implements AfterViewInit {
return; return;
} }
data.sort((a, b) => a.time.localeCompare(b.time)); this.map.fitBounds(
const coords = data items.map((c) => [c.lat, c.lng]),
.map((item) => { { padding: [30, 30] },
if (item.lat && item.lng) return [item.lat, item.lng]; );
if (item.place && item.place) return [item.place.lat, item.place.lng];
return undefined;
})
.filter((n): n is number[] => n !== undefined);
this.map.fitBounds(coords, { padding: [30, 30] });
const path = antPath(coords, { const path = antPath(
delay: 400, items.map((c) => [c.lat, c.lng]),
dashArray: [10, 20], {
weight: 5, delay: 400,
color: "#0000FF", dashArray: [10, 20],
pulseColor: "#FFFFFF", weight: 5,
paused: false, color: "#0000FF",
reverse: false, pulseColor: "#FFFFFF",
hardwareAccelerated: true, paused: false,
reverse: false,
hardwareAccelerated: true,
},
);
const layGroup = L.layerGroup();
layGroup.addLayer(path);
items.forEach((item) => {
layGroup.addLayer(tripDayMarker(item));
}); });
if (this.tripMapAntLayer) { if (this.tripMapAntLayer) {
@ -453,10 +496,10 @@ export class TripComponent implements AfterViewInit {
// UX // UX
setTimeout(() => { setTimeout(() => {
this.map.addLayer(path); layGroup.addTo(this.map);
}, 200); }, 200);
this.tripMapAntLayer = path; this.tripMapAntLayer = layGroup;
this.tripMapAntLayerDayID = day_id; this.tripMapAntLayerDayID = day_id;
} }

View File

@ -2,6 +2,7 @@ import * as L from "leaflet";
import "leaflet.markercluster"; import "leaflet.markercluster";
import "leaflet-contextmenu"; import "leaflet-contextmenu";
import { Place } from "../types/poi"; import { Place } from "../types/poi";
import { TripItem } from "../types/trip";
export interface ContextMenuItem { export interface ContextMenuItem {
text: string; text: string;
@ -69,6 +70,32 @@ export function createClusterGroup(): L.MarkerClusterGroup {
}); });
} }
export function tripDayMarker(item: {
text: string;
lat: number;
lng: number;
}): L.Marker {
const marker = new L.Marker([item.lat!, item.lng], {
icon: L.divIcon({
className: "bg-black rounded-full",
iconSize: [14, 14],
}),
});
let touchDevice = "ontouchstart" in window;
if (!touchDevice) {
marker.bindTooltip(
`<div class="font-semibold mb-1 truncate text-base">${item.text}</div>`,
{
direction: "right",
offset: [10, 0],
className: "class-tooltip",
},
);
}
return marker;
}
export function placeToMarker( export function placeToMarker(
place: Place, place: Place,
isLowNet: boolean = true, isLowNet: boolean = true,