route work

This commit is contained in:
2026-06-12 07:22:59 -04:00
parent 1b19ee9de9
commit fee7bba0ce
7 changed files with 220 additions and 114 deletions
+23 -2
View File
@@ -37,12 +37,16 @@ type CommandAck = {
message: string;
};
function setOpenedAppFab(fab: ControlCategoryKey) {
function setOpenedAppFab(fab: ControlCategoryKey | null) {
leafletStore.fabs.appControl.openedFab = fab;
}
function handleOpenedAppFabUpdate(val: boolean, key: ControlCategoryKey) {
if (val) setOpenedAppFab(key);
if (val) {
setOpenedAppFab(key);
} else {
setOpenedAppFab(null);
}
}
function formatUnknownError(commandLabel: string, error: unknown): NotificationResult {
@@ -138,6 +142,23 @@ async function runRoutingCommand(cmdAttr: ControlAction): Promise<NotificationRe
};
}
if (cmdAttr.cmd === 'set_route_start_to_current_location') {
if (simulationStore.currentLocation.loc_id) {
const currentLocationData = simulationStore.getLocationData(simulationStore.currentLocation.loc_id);
const result = leafletStore.setRouteStart(currentLocationData.latitude, currentLocationData.longitude);
return {
type: result.type,
message: result.message || `${cmdAttr.name} requested`,
};
} else {
const geoLocat = await leafletStore.getGeoLocationAsync()
const result = leafletStore.setRouteStart(geoLocat.lat, geoLocat.lng);
return {
type: result.type,
message: result.message || `${cmdAttr.name} requested`,
};
}
}
return {
type: 'negative',
message: `Unsupported routing command: ${cmdAttr.cmd}`,
+16 -59
View File
@@ -1,27 +1,15 @@
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
import { computed, onMounted } from 'vue';
import { useLeafletStore } from 'stores/leaflet';
import { LocateControl } from 'leaflet.locatecontrol';
import { storeToRefs } from 'pinia';
const leafletStore = useLeafletStore();
const locateControl = new LocateControl({
position: 'topleft',
flyTo: true,
keepCurrentZoomLevel: true,
returnToPrevBounds: true,
layer: leafletStore.layerGroups.locateControl ?? undefined,
});
const locationControlInit = ref(false);
const locateControlActive = ref(false);
const locateControlFollowing = ref(false);
const locationFound = ref(false);
const { locateControl } = storeToRefs(leafletStore);
const iconColor = computed(() =>
locateControlActive.value
? locationFound.value
? locateControlFollowing.value
locateControl.value.status.active
? locationControl.value.status.Found
? locateControl.value.status.Following
? '#FC8428FF'
: '#2074B6FF'
: '#FFFFFF'
@@ -29,7 +17,11 @@ const iconColor = computed(() =>
);
const iconClass = computed(() => {
if (locateControlActive.value && !locationFound.value && !locateControlFollowing.value) {
if (
locateControl.value.status.active &&
!locateControl.value.status.found &&
!locateControl.value.status.following
) {
return 'locate-control-icon-spinner';
} else {
return '';
@@ -38,53 +30,18 @@ const iconClass = computed(() => {
const handleClick = () => {
initialize();
if (locateControlActive.value) {
if (locateControl.value.status.active) {
console.log('Stopping locate control');
locateControl.stop();
leafletStore.stopLocate();
} else {
console.log('Starting locate control');
locateControl.start();
leafletStore.startLocate();
}
};
function initialize() {
if (!locationControlInit.value) {
if (leafletStore.mapObject) {
locationControlInit.value = true;
leafletStore.mapObject.addControl(locateControl);
leafletStore.mapObject?.on('locateactivate', function () {
console.log('Locate control activated');
locateControlActive.value = true;
});
leafletStore.mapObject?.on('locatedeactivate', function () {
console.log('Locate control deactivated');
locateControlActive.value = false;
leafletStore.layerGroups.locateControl?.clearLayers();
});
leafletStore.mapObject?.on('locationfound', function (e) {
console.log('Location Found: ', e);
console.log('Location found:', e.latlng);
console.log('Accuracy:', e.accuracy, 'meters');
locateControlFollowing.value = true;
locationFound.value = true;
});
leafletStore.mapObject?.on('moveend', function () {
if (locateControlFollowing.value) {
locateControlFollowing.value = false;
}
});
leafletStore.mapObject?.on('locationtimeout', function (e) {
const timeoutEvent = e as typeof e & { count?: number };
console.log('Location timeout count:', timeoutEvent.count);
locationFound.value = false;
leafletStore.layerGroups.locateControl?.clearLayers();
});
}
if (!locationControl.value.status.init) {
leafletStore.initLocateControl();
}
}
@@ -269,6 +269,7 @@ function zoomToCoords(arg: string) {
"
@item-clicked="zoomToCoords"
@contextmenu.prevent="onDrawerContextMenu($event, key)"
@touchstart.prevent="onDrawerContextMenu($event, key)"
active-class="text-orange"
/>
</VueDraggable>
+1
View File
@@ -52,6 +52,7 @@ export async function useAddLocation(
const simulationStore = useSimulationStore();
const locationStore = useLocationStore();
try {
console.log ('address: %s, getRevGeocode: %s', address, getRevGeocode);
const revGeo =
!address && getRevGeocode
/* ? await reverseGeocodeRateLimited(coords.lat, coords.lng) */
+12 -1
View File
@@ -306,6 +306,16 @@ const baseControls = {
iconColor: 'grey-10',
isVisible: true,
},
set_route_start_to_current_location: {
name: 'Set route start to current location',
cmd: 'set_route_start_to_current_location',
cmdClass: 'routing_cntrl_class',
icon: 'mdi-navigation',
cnfrm: false,
disabled: false,
iconColor: 'grey-10',
isVisible: true,
},
},
},
} satisfies Record<ControlCategoryKey, ControlCategoryDefinition>;
@@ -349,7 +359,8 @@ export function useControls() {
return true;
}),
routing: computed(() => {
return Boolean(leafletStore.routeSet.start.lng && leafletStore.routeSet.end.lng);
return true;
// return Boolean(leafletStore.routeSet.start.lng && leafletStore.routeSet.end.lng);
}),
} satisfies Record<ControlCategoryKey, ComputedRef<boolean>>;
+18 -2
View File
@@ -23,7 +23,13 @@ useMeta({
function layoutClickHandler(event: LeafletMouseEvent) {
// if (!leafletStore.locationDrawerSettings.opened && !leafletStore.controlDrawerSettings.opened && !leafletStore.statusBarSettings.opened) {
if (!leafletStore.locationDrawerSettings.opened && !leafletStore.fabs.statusBar) {
if (
!leafletStore.locationDrawerSettings.opened &&
!leafletStore.fabs.statusBar.opened &&
!leafletStore.fabs.layerControl.opened &&
!leafletStore.fabs.favorites.opened &&
!leafletStore.fabs.appControl.opened
) {
return;
}
if (
@@ -33,9 +39,19 @@ function layoutClickHandler(event: LeafletMouseEvent) {
) {
leafletStore.locationsOpenedHandler(false);
}
if (!event.target.closest('#status-bar') && leafletStore.fabs.statusBar) {
if (!event.target.closest('#status-bar') && leafletStore.fabs.statusBar.opened) {
leafletStore.fabs.statusBar.opened = false;
}
if (!event.target.closest('#app-ctrl') && leafletStore.fabs.appControl.opened) {
leafletStore.fabs.appControl.opened = false;
}
if (!event.target.closest('#favorite-ctrl') && leafletStore.fabs.favorites.opened) {
leafletStore.fabs.favorites.opened = false;
}
if (!event.target.closest('#layer-ctrl') && leafletStore.fabs.layerControl.opened) {
leafletStore.fabs.layerControl.opened = false;
}
/*
if (!event.target.closest('#control-drawer') && !leafletStore.controlDrawerSettings.pinned) {
leafletStore.locationsOpenedHandler(false);
+149 -50
View File
@@ -6,6 +6,7 @@ import * as L from 'leaflet';
import type { Map, LatLngExpression, LayerGroup, FeatureGroup, Marker } from 'leaflet';
import type { SearchResult } from 'components/models';
import type { IRouter } from 'leaflet-routing-machine';
import { LocateControl } from 'leaflet.locatecontrol';
import { customRouter } from 'functions/serviceURL';
import { startIcon, intermediateIcon, endIcon } from 'constants/icons';
import type {
@@ -66,7 +67,20 @@ interface RoutingOptions {
createMarker: (i: number, waypoint: RoutingWaypoint, n: number) => L.Marker | false;
}
interface State {
interface LcStatus {
active: boolean;
found: boolean;
following: boolean;
init: boolean;
}
interface LocateControlInstance {
lc: ReturnType<typeof shallowRef<LocateControl | null>>;
status: LcStatus;
latLng: L.LatLng | null;
}
interface StoreState {
zoom: number;
center: [number, number] | [null, null] | null;
mapObject: ReturnType<typeof shallowRef<Map | null>>;
@@ -85,6 +99,7 @@ interface State {
};
layerControl: LayerControl;
layerGroups: LayerGroups;
locateControl: LocateControlInstance;
routeSet: {
start: LatLng;
end: LatLng;
@@ -105,7 +120,7 @@ interface State {
}
export const useLeafletStore = defineStore('leafletStore', {
state: (): State => {
state: (): StoreState => {
return {
zoom: 10,
center: [favorites.home.coords.lat, favorites.home.coords.lng],
@@ -128,6 +143,16 @@ export const useLeafletStore = defineStore('leafletStore', {
findMy: shallowRef(null),
locateControl: shallowRef(null),
},
locateControl: {
lc: shallowRef(null),
status: {
active: false,
following: false,
init: false,
found: false,
},
latLng: null as L.LatLng | null,
},
leafletDrawerOpen: false,
locationDrawerSettings: {
opened: false,
@@ -171,45 +196,48 @@ export const useLeafletStore = defineStore('leafletStore', {
value: 1,
label: 'Open Street Map',
layer: shallowRef(
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
{
maxZoom: 19,
attribution: '© OpenStreetMap',
minZoom: 2,
noWrap: true,
}
))
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap',
minZoom: 2,
noWrap: true,
}),
),
},
{
value: 2,
label: 'Topography',
layer: shallowRef(L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {
minZoom: 2,
maxZoom: 17,
attribution: '© OpenTopoMap',
noWrap: true,
}))
},
{
value: 3,
label: 'Satellite',
layer: shallowRef(L.tileLayer(
'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
{
minZoom: 2,
maxZoom: 19,
attribution: '© ArcGIS',
noWrap: true,
}))
}
],
value: 2,
label: 'Topography',
layer: shallowRef(
L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {
minZoom: 2,
maxZoom: 17,
attribution: '© OpenTopoMap',
noWrap: true,
}),
),
},
{
value: 3,
label: 'Satellite',
layer: shallowRef(
L.tileLayer(
'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
{
minZoom: 2,
maxZoom: 19,
attribution: '© ArcGIS',
noWrap: true,
},
),
),
},
],
},
baseLayerWMS: {
selected: false,
label: 'OpenStreetMap Transparent',
layer: shallowRef(L.tileLayer.wms(
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
{
layer: shallowRef(
L.tileLayer.wms('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
minZoom: 2,
maxZoom: 19,
attribution: '© OpenStreetMap',
@@ -217,8 +245,8 @@ export const useLeafletStore = defineStore('leafletStore', {
transparent: true,
format: 'image/png',
opacity: 0.5,
}
)),
}),
),
},
routingOptions: {
waypoints: [],
@@ -259,6 +287,61 @@ export const useLeafletStore = defineStore('leafletStore', {
setMap(map: Map) {
this.mapObject = map;
},
initLocateControl() {
const lc = new LocateControl({
position: 'topleft',
flyTo: true,
keepCurrentZoomLevel: true,
returnToPrevBounds: true,
layer: this.layerGroups.locateControl ?? undefined,
});
this.locateControl.lc = lc;
this.locateControl.status.init = true;
if (this.mapObject) {
this.mapObject.addControl(lc);
this.mapObject.on('locationerror', () => {
this.locateControl.latLng = null;
});
this.mapObject.on('locateactivate', () => {
console.log('Locate control activated');
this.locateControl.status.active = true;
});
this.mapObject.on('locatedeactivate', () => {
console.log('Locate control deactivated');
this.locateControl.status.active = false;
this.layerGroups.locateControl?.clearLayers();
});
this.mapObject.on('locationfound', (e) => {
console.log('Location Found: ', e);
console.log('Location found:', e.latlng);
console.log('Accuracy:', e.accuracy, 'meters');
this.locateControl.latLng = e.latlng;
this.locateControl.status.following = true;
this.locateControl.status.found = true;
});
this.mapObject.on('moveend', () => {
if (this.locateControl.status.following) {
this.locateControl.status.following = false;
}
});
this.mapObject.on('locationtimeout', (e) => {
const timeoutEvent = e as typeof e & { count?: number };
console.log('Location timeout count:', timeoutEvent.count);
this.locateControl.status.found = false;
this.layerGroups.locateControl?.clearLayers();
});
}
},
startLocate() {
if (this.locateControl.lc) {
this.locateControl.lc.start();
}
},
stopLocate() {
if (this.locateControl.lc) {
this.locateControl.lc.stop();
}
},
/*
setLayerGroup(layerGroup: LayerGroup, name: string)
this.layerGroups[name] = layerGroup;
@@ -293,23 +376,23 @@ export const useLeafletStore = defineStore('leafletStore', {
this.zoom = zoom;
},
zoomIn() {
this.mapObject?.zoomIn()
this.mapObject?.zoomIn();
},
zoomOut() {
this.mapObject?.zoomOut()
this.mapObject?.zoomOut();
},
setBaseLayer(newVal?: number, oldVal?: number) {
if (newVal === undefined && this.baseLayer.selected === undefined) return;
if (oldVal && this.mapObject) {
const oldLayer = this.baseLayer.options.filter(u => u.value === oldVal)[0];
const oldLayer = this.baseLayer.options.filter((u) => u.value === oldVal)[0];
if (oldLayer) {
console.log("OldLayer name: ", oldLayer.label);
console.log('OldLayer name: ', oldLayer.label);
oldLayer.layer!.remove();
}
}
const bLayer = this.baseLayer.options.filter(u => u.value === this.baseLayer.selected)[0];
if(this.mapObject && bLayer) {
console.log("NewLayer name: ", bLayer.label);
const bLayer = this.baseLayer.options.filter((u) => u.value === this.baseLayer.selected)[0];
if (this.mapObject && bLayer) {
console.log('NewLayer name: ', bLayer.label);
bLayer.layer!.addTo(this.mapObject);
}
},
@@ -422,12 +505,28 @@ export const useLeafletStore = defineStore('leafletStore', {
}
return { type: result.notificationType, message: result.responseMessage };
},
setRouteStart(lat: number, lng: number) {
this.layerControl.routing = true;
this.routeSet.start = { lat: lat, lng: lng };
return { type: 'positive', message: 'Route start set.' };
},
getGeoLocationAsync() {
return new Promise((resolve, reject) => {
this.locateControl?.lc?.start();
this.mapObject?.once('locationfound', function (e) {
resolve(e.latlng);
});
this.mapObject?.once('locationerror', function (e) {
reject(new Error(e.message));
});
});
},
clearRoute() {
this.layerControl.routing = false;
this.clearRouteSegments();
this.routeSet.start = {lat: null, lng: null};
this.routeSet.end = {lat: null, lng: null};
return {type: 'positive', message: 'Route cleared successfully'};
this.routeSet.start = { lat: null, lng: null };
this.routeSet.end = { lat: null, lng: null };
return { type: 'positive', message: 'Route cleared successfully' };
},
moveToLocation(location: SearchResult) {
if (!location) return;
@@ -443,9 +542,9 @@ export const useLeafletStore = defineStore('leafletStore', {
// Otherwise, pan to center
this.mapObject?.setView([lat, lng], 14);
}
let searchMarker: Marker = new L.Marker([0,0]);
// Update/create a marker at the selected location
if (searchMarker) {
let searchMarker: Marker = new L.Marker([0, 0]);
// Update/create a marker at the selected location
if (searchMarker) {
searchMarker.setLatLng([lat, lng]);
} else {
searchMarker = L.marker([lat, lng]).addTo(this.mapObject!);