✨ Create/Update Place GPX, beta: show GPX on Map
This commit is contained in:
parent
b0cbd0efb5
commit
3005fe9caf
@ -2,7 +2,8 @@
|
||||
|
||||
@if (selectedPlace) {
|
||||
<app-place-box [selectedPlace]="selectedPlace" (deleteEmitter)="deletePlace()" (editEmitter)="editPlace()"
|
||||
(favoriteEmitter)="favoritePlace()" (visitEmitter)="visitPlace()" (closeEmitter)="closePlaceBox()"></app-place-box>
|
||||
(favoriteEmitter)="favoritePlace()" (gpxEmitter)="getPlaceGPX()" (visitEmitter)="visitPlace()"
|
||||
(closeEmitter)="closePlaceBox()"></app-place-box>
|
||||
}
|
||||
|
||||
<div class="absolute z-30 top-2 right-2 p-2 bg-white shadow rounded">
|
||||
|
||||
@ -25,7 +25,12 @@ import { FloatLabelModule } from "primeng/floatlabel";
|
||||
import { BatchCreateModalComponent } from "../../modals/batch-create-modal/batch-create-modal.component";
|
||||
import { UtilsService } from "../../services/utils.service";
|
||||
import { Info } from "../../types/info";
|
||||
import { createMap, placeToMarker, createClusterGroup } from "../../shared/map";
|
||||
import {
|
||||
createMap,
|
||||
placeToMarker,
|
||||
createClusterGroup,
|
||||
gpxToPolyline,
|
||||
} from "../../shared/map";
|
||||
import { Router } from "@angular/router";
|
||||
import { SelectModule } from "primeng/select";
|
||||
import { MultiSelectModule } from "primeng/multiselect";
|
||||
@ -82,6 +87,7 @@ export class DashboardComponent implements AfterViewInit {
|
||||
hoveredElements: HTMLElement[] = [];
|
||||
|
||||
map: any;
|
||||
mapDisplayedTrace: L.Polyline[] = [];
|
||||
settings: Settings | undefined;
|
||||
currencySigns: { c: string; s: string }[] = [];
|
||||
doNotDisplayOptions: SelectItemGroup[] = [];
|
||||
@ -499,6 +505,42 @@ export class DashboardComponent implements AfterViewInit {
|
||||
});
|
||||
}
|
||||
|
||||
displayGPXOnMap(gpx: string) {
|
||||
try {
|
||||
// HINT: For now, delete traces everytime we display a GPX
|
||||
// TODO: Handle multiple polygons and handle Click events
|
||||
this.mapDisplayedTrace.forEach((p) => this.map.removeLayer(p));
|
||||
this.mapDisplayedTrace = [];
|
||||
|
||||
const gpxPolyline = gpxToPolyline(gpx).addTo(this.map);
|
||||
gpxPolyline.on("click", () => {
|
||||
this.map.removeLayer(gpxPolyline);
|
||||
});
|
||||
|
||||
this.mapDisplayedTrace.push(gpxPolyline);
|
||||
} catch {
|
||||
this.utilsService.toast("error", "Error", "Couldn't parse GPX data");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
getPlaceGPX() {
|
||||
if (!this.selectedPlace) return;
|
||||
this.apiService.getPlaceGPX(this.selectedPlace.id).subscribe({
|
||||
next: (p) => {
|
||||
if (!p.gpx) {
|
||||
this.utilsService.toast(
|
||||
"error",
|
||||
"Error",
|
||||
"Couldn't retrieve GPX data",
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.displayGPXOnMap(p.gpx);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
toggleSettings() {
|
||||
this.viewSettings = !this.viewSettings;
|
||||
if (this.viewSettings && this.settings) {
|
||||
|
||||
@ -43,14 +43,25 @@
|
||||
<label for="price">Price</label>
|
||||
</p-floatlabel>
|
||||
|
||||
<div class="flex justify-center items-center">
|
||||
<p-checkbox formControlName="allowdog" [binary]="true" inputId="allowdog" />
|
||||
<label for="allowdog" class="ml-2">Allow 🐶</label>
|
||||
</div>
|
||||
<div class="col-span-2 grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||
<div class="flex justify-center items-center">
|
||||
<p-checkbox formControlName="allowdog" [binary]="true" inputId="allowdog" />
|
||||
<label for="allowdog" class="ml-2">Allow 🐶</label>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center items-center">
|
||||
<p-checkbox formControlName="visited" [binary]="true" inputId="visited" />
|
||||
<label for="visited" class="ml-2">Visited</label>
|
||||
<div class="flex justify-center items-center">
|
||||
<p-checkbox formControlName="visited" [binary]="true" inputId="visited" />
|
||||
<label for="visited" class="ml-2">Visited</label>
|
||||
</div>
|
||||
|
||||
<div class="col-span-2 md:col-span-1 flex justify-center items-center">
|
||||
@if (placeForm.get('gpx')?.value) {
|
||||
<p-button text icon="pi pi-times" label="GPX" severity="danger" (click)="clearGPX()" />
|
||||
} @else {
|
||||
<p-button text icon="pi pi-paperclip" label="GPX" (click)="gpxInput.click()" />
|
||||
}
|
||||
<input type="file" accept=".gpx" #gpxInput class="hidden" (change)="onGPXSelected($event)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid col-span-full md:grid-cols-4">
|
||||
@ -61,7 +72,7 @@
|
||||
|
||||
<div class="mt-4 md:mt-0 grid place-items-center col-span-full md:col-span-1">
|
||||
@if (placeForm.get("image_id")?.value) {
|
||||
<div class="w-2/3 relative group cursor-pointer" (click)="fileInput.click()">
|
||||
<div class="w-2/3 relative group cursor-pointer" (click)="imageInput.click()">
|
||||
<img [src]="placeForm.get('image')?.value"
|
||||
class="w-full max-h-20 object-cover rounded-full shadow-lg transition-transform duration-300" />
|
||||
<div
|
||||
@ -69,7 +80,6 @@
|
||||
<span class="text-sm text-gray-300">Click to edit</span><i class="pi pi-upload text-white"></i>
|
||||
</div>
|
||||
</div>
|
||||
<input type="file" accept="image/*" #fileInput class="hidden" (change)="onFileSelected($event)" />
|
||||
} @else {
|
||||
@if (placeForm.get("image")?.value) {
|
||||
<div class="w-2/3 relative group cursor-pointer" (click)="clearImage()">
|
||||
@ -81,7 +91,7 @@
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="w-2/3 relative group cursor-pointer" (click)="fileInput.click()">
|
||||
<div class="w-2/3 relative group cursor-pointer" (click)="imageInput.click()">
|
||||
<img src="/favicon.png"
|
||||
class="w-full max-h-20 object-cover rounded-full shadow-lg transition-transform duration-300" />
|
||||
<div
|
||||
@ -89,9 +99,9 @@
|
||||
<span class="text-sm text-gray-300">Click to edit</span><i class="pi pi-upload text-white"></i>
|
||||
</div>
|
||||
</div>
|
||||
<input type="file" accept="image/*" #fileInput class="hidden" (change)="onFileSelected($event)" />
|
||||
}
|
||||
}
|
||||
<input type="file" accept="image/*" #imageInput class="hidden" (change)="onImageSelected($event)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -14,8 +14,3 @@
|
||||
.p-floatlabel:has(.p-inputwrapper-focus) input::placeholder {
|
||||
color: var(--p-inputtext-placeholder-color) !important;
|
||||
}
|
||||
|
||||
.p-tooltip > .p-tooltip-text {
|
||||
width: 350px !important;
|
||||
background: red;
|
||||
}
|
||||
|
||||
@ -91,8 +91,9 @@ export class PlaceCreateModalComponent {
|
||||
price: "",
|
||||
allowdog: false,
|
||||
visited: false,
|
||||
image: "",
|
||||
image: null,
|
||||
image_id: null,
|
||||
gpx: null,
|
||||
});
|
||||
|
||||
if (this.config.data) {
|
||||
@ -172,7 +173,7 @@ export class PlaceCreateModalComponent {
|
||||
this.placeForm.get("name")?.setValue(place);
|
||||
}
|
||||
|
||||
onFileSelected(event: Event) {
|
||||
onImageSelected(event: Event) {
|
||||
const input = event.target as HTMLInputElement;
|
||||
if (input.files && input.files.length > 0) {
|
||||
const file = input.files[0];
|
||||
@ -201,4 +202,24 @@ export class PlaceCreateModalComponent {
|
||||
this.placeForm.get("image")?.setValue(this.previous_image);
|
||||
}
|
||||
}
|
||||
|
||||
onGPXSelected(event: Event) {
|
||||
const input = event.target as HTMLInputElement;
|
||||
if (input.files && input.files.length > 0) {
|
||||
const file = input.files[0];
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = (e) => {
|
||||
this.placeForm.get("gpx")?.setValue(e.target?.result as string);
|
||||
this.placeForm.get("gpx")?.markAsDirty();
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
}
|
||||
}
|
||||
|
||||
clearGPX() {
|
||||
this.placeForm.get("gpx")?.setValue(null);
|
||||
this.placeForm.get("gpx")?.markAsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,6 +164,12 @@ export class ApiService {
|
||||
);
|
||||
}
|
||||
|
||||
getPlaceGPX(place_id: number): Observable<Place> {
|
||||
return this.httpClient
|
||||
.get<Place>(`${this.apiBaseUrl}/places/${place_id}`)
|
||||
.pipe(map((p) => this._normalizePlaceImage(p)));
|
||||
}
|
||||
|
||||
getTrips(): Observable<TripBase[]> {
|
||||
return this.httpClient.get<TripBase[]>(`${this.apiBaseUrl}/trips`).pipe(
|
||||
map((resp) => {
|
||||
|
||||
@ -99,3 +99,18 @@ export function placeToMarker(place: Place): L.Marker {
|
||||
}
|
||||
return marker;
|
||||
}
|
||||
|
||||
export function gpxToPolyline(gpx: string): L.Polyline {
|
||||
const parser = new DOMParser();
|
||||
const gpxDoc = parser.parseFromString(gpx, "application/xml");
|
||||
|
||||
const trkpts = Array.from(gpxDoc.querySelectorAll("trkpt"));
|
||||
const latlngs = trkpts.map((pt) => {
|
||||
return [
|
||||
parseFloat(pt.getAttribute("lat")!),
|
||||
parseFloat(pt.getAttribute("lon")!),
|
||||
] as [number, number];
|
||||
});
|
||||
|
||||
return L.polyline(latlngs, { color: "blue" });
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user