Optimalizace planovace, ikony pro letiste
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {computed} from "vue";
|
import {computed} from "vue";
|
||||||
|
import {useAltitudeUtils} from "src/composables/airspaces";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
lowerLimit: {
|
lowerLimit: {
|
||||||
@@ -20,13 +21,10 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const getLimitFormatted = (limit, unit) => {
|
const { altitudeLimitFormatted } = useAltitudeUtils();
|
||||||
if (parseInt(limit) === 0) return 'GND';
|
|
||||||
return `${limit} ${unit}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const upper = computed(() => getLimitFormatted(props.upperLimit, props.upperLimitUnit));
|
const upper = computed(() => altitudeLimitFormatted(props.upperLimit, props.upperLimitUnit));
|
||||||
const lower = computed(() => getLimitFormatted(props.lowerLimit, props.lowerLimitUnit));
|
const lower = computed(() => altitudeLimitFormatted(props.lowerLimit, props.lowerLimitUnit));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -40,10 +40,6 @@ const handleMarkerClick = (marker) => {
|
|||||||
mapWrap.value.openInfoWindow({marker});
|
mapWrap.value.openInfoWindow({marker});
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePoiClick = (poi) => {
|
|
||||||
mapWrap.value.openInfoWindow({poi});
|
|
||||||
}
|
|
||||||
|
|
||||||
const markers = ref([]);
|
const markers = ref([]);
|
||||||
watch(() => props.flightPlan.track, (newVal) => {
|
watch(() => props.flightPlan.track, (newVal) => {
|
||||||
markers.value = getMarkersFrom(props.flightPlan);
|
markers.value = getMarkersFrom(props.flightPlan);
|
||||||
@@ -56,14 +52,17 @@ const addMarker = async (point) => {
|
|||||||
if (!point) return;
|
if (!point) return;
|
||||||
|
|
||||||
const insertAt = Math.min(markers.value.length, props.insertPosition);
|
const insertAt = Math.min(markers.value.length, props.insertPosition);
|
||||||
|
|
||||||
markers.value.splice(insertAt, 0, markerObject({...point}));
|
markers.value.splice(insertAt, 0, markerObject({...point}));
|
||||||
|
|
||||||
emits("markers-changed", markers.value);
|
emits("markers-changed", markers.value);
|
||||||
emits("change-insert-position", insertAt + 1);
|
emits("change-insert-position", insertAt + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeMarker = (idx) => {
|
const removeMarker = (infoWindow) => {
|
||||||
|
const idx = markers.value.findIndex(i => i.gpsLatitude === infoWindow.position.lat && i.gpsLongitude === infoWindow.position.lng);
|
||||||
|
|
||||||
|
if (!idx) return;
|
||||||
|
|
||||||
markers.value.splice(idx, 1);
|
markers.value.splice(idx, 1);
|
||||||
emits("markers-changed", markers.value);
|
emits("markers-changed", markers.value);
|
||||||
}
|
}
|
||||||
@@ -85,6 +84,8 @@ const handleMarkerMove = async (idx, event) => {
|
|||||||
style="width: 100%; height: 100%; min-height: 500px"
|
style="width: 100%; height: 100%; min-height: 500px"
|
||||||
:center="center"
|
:center="center"
|
||||||
:zoom="8"
|
:zoom="8"
|
||||||
|
:track="{path: track, geodesic: true, strokeColor: '#ff00ff', strokeOpacity: 1, strokeWeight: 3}"
|
||||||
|
:points-of-interest="pointsOfInterest"
|
||||||
:airspaces-data="airspaceData"
|
:airspaces-data="airspaceData"
|
||||||
:airports="airports"
|
:airports="airports"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
@@ -94,19 +95,7 @@ const handleMarkerMove = async (idx, event) => {
|
|||||||
:allow-add-marker="true"
|
:allow-add-marker="true"
|
||||||
@add-to-route="addMarker"
|
@add-to-route="addMarker"
|
||||||
@remove-from-route="removeMarker"
|
@remove-from-route="removeMarker"
|
||||||
|
|
||||||
>
|
>
|
||||||
<template v-for="poi in pointsOfInterest" :key="poi.name">
|
|
||||||
<CustomMarker
|
|
||||||
v-if="poi.gpsLatitude && poi.gpsLongitude"
|
|
||||||
:options="{position: {lat: poi.gpsLatitude, lng: poi.gpsLongitude}, anchorPoint: 'BOTTOM_CENTER'}"
|
|
||||||
@click.stop="handlePoiClick(poi)"
|
|
||||||
>
|
|
||||||
<q-icon name="place" style="color: orange; font-size: 30px"/>
|
|
||||||
</CustomMarker>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
|
|
||||||
<template v-for="(marker, i) in markers" :key="marker.name">
|
<template v-for="(marker, i) in markers" :key="marker.name">
|
||||||
<Marker
|
<Marker
|
||||||
v-if="editableNameTypes.includes(marker.type)"
|
v-if="editableNameTypes.includes(marker.type)"
|
||||||
@@ -115,6 +104,5 @@ const handleMarkerMove = async (idx, event) => {
|
|||||||
@dragend="handleMarkerMove(i, $event.latLng)"
|
@dragend="handleMarkerMove(i, $event.latLng)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<Polyline :options="{path: track, geodesic: true, strokeColor: '#ff00ff', strokeOpacity: 1, strokeWeight: 3}"/>
|
|
||||||
</MapWrapper>
|
</MapWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ const totalTime = computed(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-list bordered class="q-ml-md-md q-pt-md">
|
<q-list bordered class="q-ml-md-md q-pt-md">
|
||||||
|
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<div style="display: flex; justify-content: space-between">
|
<div style="display: flex; justify-content: space-between">
|
||||||
@@ -66,8 +65,6 @@ const totalTime = computed(() => {
|
|||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
|
||||||
{{ insertPosition }} {{ flightPlan.track.length }}
|
|
||||||
|
|
||||||
<q-scroll-area style="height: 50vh">
|
<q-scroll-area style="height: 50vh">
|
||||||
<template v-for="(segment, i) in props.flightPlan.track" :key="segment">
|
<template v-for="(segment, i) in props.flightPlan.track" :key="segment">
|
||||||
<q-item dense>
|
<q-item dense>
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ const props = defineProps({
|
|||||||
required: false,
|
required: false,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
|
isInTrack: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
showActions: {
|
showActions: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
@@ -34,7 +39,7 @@ watch(() => props.infoWindow.position, async () => {
|
|||||||
if (props.infoWindow.title) {
|
if (props.infoWindow.title) {
|
||||||
title.value = props.infoWindow.title
|
title.value = props.infoWindow.title
|
||||||
} else {
|
} else {
|
||||||
title.value = await searchPlaceName(props.infoWindow.position.lat, props.infoWindow.position.lng);
|
searchPlaceName(props.infoWindow.position.lat, props.infoWindow.position.lng).then(data => title.value = data);
|
||||||
}
|
}
|
||||||
}, {immediate: true, deep: true});
|
}, {immediate: true, deep: true});
|
||||||
|
|
||||||
@@ -55,8 +60,8 @@ const addPoint = () => {
|
|||||||
emits("close");
|
emits("close");
|
||||||
}
|
}
|
||||||
|
|
||||||
const removePoint = (marker) => {
|
const removePoint = () => {
|
||||||
emits("remove-from-route", marker);
|
emits("remove-from-route", props.infoWindow);
|
||||||
emits("close");
|
emits("close");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,10 +137,8 @@ const openedTab = ref("info");
|
|||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="infoWindow.marker">
|
<div v-if="infoWindow.marker">
|
||||||
@@ -183,16 +186,15 @@ const openedTab = ref("info");
|
|||||||
|
|
||||||
<template v-if="showActions">
|
<template v-if="showActions">
|
||||||
<q-btn
|
<q-btn
|
||||||
v-if="infoWindow.marker"
|
v-if="isInTrack"
|
||||||
class="q-mt-lg full-width"
|
class="q-mr-sm q-mt-lg"
|
||||||
color="warning"
|
color="warning"
|
||||||
@click="removePoint(infoWindow.marker.position)"
|
@click="removePoint()"
|
||||||
>
|
>
|
||||||
Odebrat z trasy
|
Odebrat z trasy
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<q-btn
|
<q-btn
|
||||||
v-else
|
:class="{'q-mt-lg': true, 'full-width': !isInTrack}"
|
||||||
class="full-width q-mt-lg"
|
|
||||||
color="secondary"
|
color="secondary"
|
||||||
@click="addPoint()"
|
@click="addPoint()"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {GoogleMap, CustomControl, CustomMarker, Polygon} from "vue3-google-map";
|
import {CustomControl, CustomMarker, GoogleMap, Polygon, Polyline} from "vue3-google-map";
|
||||||
import {computed, ref, watch} from "vue";
|
import {computed, ref, watch} from "vue";
|
||||||
import {useVectorUtilities} from "src/graphql/fragments/vectorUtilities";
|
import {useVectorUtilities} from "src/graphql/fragments/vectorUtilities";
|
||||||
import {useAirspaceColors} from "src/composables/airspaces";
|
import {useAirspaceColors, useAltitudeUtils} from "src/composables/airspaces";
|
||||||
import AirspaceInfoWindow from "components/map/AirspaceInfoWindow.vue";
|
import AirspaceInfoWindow from "components/map/AirspaceInfoWindow.vue";
|
||||||
import {useFlightPlanObjects} from "src/composables/flightPlan";
|
import {useFlightPlanObjects} from "src/composables/flightPlan";
|
||||||
|
import {debounce} from "quasar";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
bounds: {
|
bounds: {
|
||||||
@@ -21,6 +22,15 @@ const props = defineProps({
|
|||||||
required: false,
|
required: false,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
|
pointsOfInterest: {
|
||||||
|
type: Array,
|
||||||
|
required: false,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
track: {
|
||||||
|
type: Object,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
showInfoWindow: {
|
showInfoWindow: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
@@ -42,8 +52,9 @@ const emits = defineEmits(["map-click", "add-to-route", "remove-from-route"]);
|
|||||||
|
|
||||||
const key = process.env.GOOGLE_MAPS_API_KEY;
|
const key = process.env.GOOGLE_MAPS_API_KEY;
|
||||||
|
|
||||||
const zoomLevel = ref(null);
|
|
||||||
const mapRef = ref(null);
|
const mapRef = ref(null);
|
||||||
|
const zoomLevel = ref(null);
|
||||||
|
const visibleBoundsObject = ref(null);
|
||||||
watch(
|
watch(
|
||||||
() => mapRef.value?.ready,
|
() => mapRef.value?.ready,
|
||||||
(ready) => {
|
(ready) => {
|
||||||
@@ -58,36 +69,55 @@ watch(
|
|||||||
}
|
}
|
||||||
zoomLevel.value = mapRef.value.map.getZoom();
|
zoomLevel.value = mapRef.value.map.getZoom();
|
||||||
|
|
||||||
mapRef.value.map.addListener("zoom_changed", () => {
|
mapRef.value.map.addListener("zoom_changed", debounce(() => {
|
||||||
console.log("zoom changed");
|
|
||||||
zoomLevel.value = mapRef.value.map.getZoom();
|
zoomLevel.value = mapRef.value.map.getZoom();
|
||||||
})
|
}, 100));
|
||||||
|
|
||||||
|
mapRef.value.map.addListener("bounds_changed", debounce(() => {
|
||||||
|
visibleBoundsObject.value = mapRef.value.map.getBounds();
|
||||||
|
}, 100));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const airspaceLayers = ref({
|
||||||
|
CTR: {displayed: true, color: "", minZoom: 7},
|
||||||
|
TMA: {displayed: true, color: "", minZoom: 7},
|
||||||
|
RMZ: {displayed: false, color: "", minZoom: 7},
|
||||||
|
DANGER: {label: "Nebezpečné prostory", displayed: false, color: "", minZoom: 7},
|
||||||
|
PROHIBIT: {label: "Zakázané prostory", displayed: true, color: "", minZoom: 7},
|
||||||
|
RESTRICT: {label: "TRA", displayed: true, color: "", minZoom: 7},
|
||||||
|
TRAGA: {displayed: true, color: "", minZoom: 8},
|
||||||
|
ATZ: {displayed: true, color: "", minZoom: 8},
|
||||||
|
AIRPORTS: {label: "Letiště", displayed: true, color: "", minZoom: 10},
|
||||||
|
FIELDSTRIP: {label: "SLZ plochy", displayed: false, color: "", minZoom: 11},
|
||||||
|
})
|
||||||
|
|
||||||
const displayAirspaceLayers = ref({
|
const isAirspaceVisible = (airspace, bounds) => {
|
||||||
CTR: true,
|
if (!bounds) return false;
|
||||||
TMA: true,
|
|
||||||
RMZ: false,
|
if (airspace.mapBounds) {
|
||||||
ATZ: true,
|
return airspace.mapBounds.some(b => bounds.contains(b));
|
||||||
PARA: false,
|
}
|
||||||
DANGER: false,
|
|
||||||
PROHIBIT: true,
|
if (airspace.gpsLatitude && airspace.gpsLongitude) {
|
||||||
RESTRICT: true,
|
return bounds.contains({lat: airspace.gpsLatitude, lng: airspace.gpsLongitude});
|
||||||
TRAGA: true,
|
}
|
||||||
AIRPORTS: true,
|
|
||||||
FIELDSTRIP: false,
|
return true;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
const maxAltitude = ref(5000);
|
||||||
|
const {altitudeLimitFormatted, altitudeToFt} = useAltitudeUtils();
|
||||||
|
|
||||||
const airspaces = computed(() => {
|
const airspaces = computed(() => {
|
||||||
const colorsMap = useAirspaceColors().colors;
|
const colorsMap = useAirspaceColors().colors;
|
||||||
|
|
||||||
return props.airspacesData
|
return props.airspacesData
|
||||||
.filter(airspace => !!colorsMap[airspace.type])
|
.filter(airspace => airspaceLayers.value[airspace.type] && airspaceLayers.value[airspace.type].displayed)
|
||||||
.filter(airspace => displayAirspaceLayers.value[airspace.type])
|
.filter(airspace => airspaceLayers.value[airspace.type].minZoom <= zoomLevel.value)
|
||||||
|
.filter(airspace => isAirspaceVisible(airspace, visibleBoundsObject.value))
|
||||||
|
.filter(airspace => altitudeToFt(airspace.lowerLimit, airspace.lowerAltUnit) < maxAltitude.value)
|
||||||
.map(airspace => {
|
.map(airspace => {
|
||||||
|
|
||||||
let fillOpacity = 0;
|
let fillOpacity = 0;
|
||||||
let strokeWeight = 1;
|
let strokeWeight = 1;
|
||||||
if (["PROHIBIT", "RESTRICT"].includes(airspace.type))
|
if (["PROHIBIT", "RESTRICT"].includes(airspace.type))
|
||||||
@@ -115,9 +145,15 @@ const airspaces = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const airportsOnMap = computed(() => {
|
const airportsOnMap = computed(() => {
|
||||||
if (!displayAirspaceLayers.value.AIRPORTS && !displayAirspaceLayers.value.FIELDSTRIP) return [];
|
const displayAirport = airspaceLayers.value.AIRPORTS.displayed;
|
||||||
|
const displayFieldstrip = airspaceLayers.value.FIELDSTRIP.displayed;
|
||||||
|
if (!displayAirport && !displayFieldstrip) return [];
|
||||||
|
|
||||||
return props.airports.filter(a => a.gpsLatitude && a.gpsLongitude).filter(a => a.icaoCode.length === 6 ? displayAirspaceLayers.value.FIELDSTRIP : displayAirspaceLayers.value.AIRPORTS);
|
return props.airports
|
||||||
|
.filter(a => a.gpsLatitude && a.gpsLongitude)
|
||||||
|
.filter(a => isAirspaceVisible(a, visibleBoundsObject.value))
|
||||||
|
.filter(a => zoomLevel.value >= (a.icaoCode.length === 6 ? airspaceLayers.value.FIELDSTRIP.minZoom : airspaceLayers.value.AIRPORTS.minZoom))
|
||||||
|
.filter(a => a.icaoCode.length === 6 ? displayFieldstrip : displayAirport)
|
||||||
});
|
});
|
||||||
|
|
||||||
const {isPointInPolygon} = useVectorUtilities();
|
const {isPointInPolygon} = useVectorUtilities();
|
||||||
@@ -154,7 +190,7 @@ const highlightAirspace = (airspaceName) => {
|
|||||||
|
|
||||||
if (airspace) {
|
if (airspace) {
|
||||||
highlightedAirspaces.value = [airspaceName];
|
highlightedAirspaces.value = [airspaceName];
|
||||||
displayAirspaceLayers.value[airspace.type] = true;
|
airspaceLayers.value[airspace.type].displayed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,6 +220,21 @@ const handleAirportClick = (airport) => {
|
|||||||
|
|
||||||
emitMapClick({position, airport, airspaces: airspacesUnderClick})
|
emitMapClick({position, airport, airspaces: airspacesUnderClick})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handlePoiClick = (poi) => {
|
||||||
|
const position = {lat: poi.gpsLatitude, lng: poi.gpsLongitude};
|
||||||
|
|
||||||
|
const airspacesUnderClick = getAirspacesAtCoordinates(position);
|
||||||
|
highlightedAirspaces.value = [];
|
||||||
|
|
||||||
|
emitMapClick({poi, position, airspaces: airspacesUnderClick});
|
||||||
|
}
|
||||||
|
|
||||||
|
const isDetailInTrack = computed(() => {
|
||||||
|
return !!props.track.path.find(i => i.lat === infoWindow.value.position.lat && i.lng === infoWindow.value.position.lng);
|
||||||
|
});
|
||||||
|
|
||||||
|
const isControlsOpened = ref(false);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -193,6 +244,7 @@ const handleAirportClick = (airport) => {
|
|||||||
:street-view-control="false"
|
:street-view-control="false"
|
||||||
@click="handleMapClick"
|
@click="handleMapClick"
|
||||||
map-type-id="roadmap"
|
map-type-id="roadmap"
|
||||||
|
gesture-handling="greedy"
|
||||||
:max-zoom="13"
|
:max-zoom="13"
|
||||||
ref="mapRef"
|
ref="mapRef"
|
||||||
>
|
>
|
||||||
@@ -201,6 +253,7 @@ const handleAirportClick = (airport) => {
|
|||||||
:info-window="infoWindow"
|
:info-window="infoWindow"
|
||||||
:show-actions="allowAddMarker"
|
:show-actions="allowAddMarker"
|
||||||
:highlighted-airspaces="highlightedAirspaces"
|
:highlighted-airspaces="highlightedAirspaces"
|
||||||
|
:is-in-track="isDetailInTrack"
|
||||||
@close="infoWindow.active=false"
|
@close="infoWindow.active=false"
|
||||||
@add-to-route="emits('add-to-route', $event)"
|
@add-to-route="emits('add-to-route', $event)"
|
||||||
@remove-from-route="emits('remove-from-route', $event)"
|
@remove-from-route="emits('remove-from-route', $event)"
|
||||||
@@ -218,37 +271,78 @@ const handleAirportClick = (airport) => {
|
|||||||
:options="{position: {lat: airport.gpsLatitude, lng: airport.gpsLongitude}, anchorPoint: 'CENTER'}"
|
:options="{position: {lat: airport.gpsLatitude, lng: airport.gpsLongitude}, anchorPoint: 'CENTER'}"
|
||||||
@click.stop="handleAirportClick(airport)"
|
@click.stop="handleAirportClick(airport)"
|
||||||
>
|
>
|
||||||
<q-chip size="xs">{{ airport.icaoCode }}</q-chip>
|
<img
|
||||||
|
:title="airport.icaoCode"
|
||||||
|
:src="airport.iconUrl"
|
||||||
|
:alt="airport.icaoCode"
|
||||||
|
/>
|
||||||
</CustomMarker>
|
</CustomMarker>
|
||||||
|
|
||||||
<template v-if="displayAirspaceLayers.CTR">
|
<template v-if="airspaceLayers.CTR.displayed">
|
||||||
<CustomMarker
|
<CustomMarker
|
||||||
v-for="reportingPoint in airport.reportingPoints" :key="reportingPoint.id"
|
v-for="reportingPoint in airport.reportingPoints" :key="reportingPoint.id"
|
||||||
:options="{position: {lat: reportingPoint.gpsLatitude, lng: reportingPoint.gpsLongitude, anchorPoint: 'CENTER'}}"
|
:options="{position: {lat: reportingPoint.gpsLatitude, lng: reportingPoint.gpsLongitude, anchorPoint: 'CENTER'}}"
|
||||||
@click.stop="handleReportingPointClick(airport, reportingPoint)"
|
@click.stop="handleReportingPointClick(airport, reportingPoint)"
|
||||||
:title="`${airport.icaoCode}: ${reportingPoint.name}`"
|
:title="`${airport.icaoCode}: ${reportingPoint.name}`"
|
||||||
|
style="font-size: 20px"
|
||||||
>
|
>
|
||||||
<q-icon name="mdi-triangle"/>
|
▲
|
||||||
</CustomMarker>
|
</CustomMarker>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template v-for="poi in pointsOfInterest" :key="poi.name">
|
||||||
|
<CustomMarker
|
||||||
|
v-if="poi.gpsLatitude && poi.gpsLongitude"
|
||||||
|
:options="{position: {lat: poi.gpsLatitude, lng: poi.gpsLongitude}, anchorPoint: 'BOTTOM_CENTER'}"
|
||||||
|
@click.stop="handlePoiClick(poi)"
|
||||||
|
>
|
||||||
|
<div style="padding: 5px; border-radius: 15px; background: #8bdfff">
|
||||||
|
<img style="width: 20px" src="/public/mapIcons/photo-32x32.png" alt="">
|
||||||
|
</div>
|
||||||
|
</CustomMarker>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<Polyline v-if="track" :options="track"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<CustomControl position="BOTTOM_CENTER" class="text-center" v-if="showLayerControls">
|
<CustomControl position="BOTTOM_LEFT" v-if="showLayerControls" style="width: 30%">
|
||||||
<q-card class="q-my-md q-mx-auto q-px-lg" style="max-width: 98%;">
|
<q-btn
|
||||||
<q-checkbox v-model="displayAirspaceLayers.CTR" label="CTR"/>
|
@click="isControlsOpened = !isControlsOpened"
|
||||||
<q-checkbox v-model="displayAirspaceLayers.TMA" label="TMA"/>
|
:icon="isControlsOpened ? 'layers_clear' : 'layers'"
|
||||||
<q-checkbox v-model="displayAirspaceLayers.RMZ" label="RMZ"/>
|
color="grey-2"
|
||||||
<q-checkbox v-model="displayAirspaceLayers.ATZ" label="ATZ"/>
|
text-color="grey-9"
|
||||||
<q-checkbox v-model="displayAirspaceLayers.DANGER" label="Nebezpečné prostory"/>
|
round
|
||||||
<q-checkbox v-model="displayAirspaceLayers.PROHIBIT" label="Zakázané prostory"/>
|
size="sm"
|
||||||
<q-checkbox v-model="displayAirspaceLayers.RESTRICT" label="TRA"/>
|
:class="{'q-mb-lg': !isControlsOpened}"
|
||||||
<q-checkbox v-model="displayAirspaceLayers.TRAGA" label="TRAGA"/>
|
>
|
||||||
<q-checkbox v-model="displayAirspaceLayers.PARA" label="PARA"/>
|
</q-btn>
|
||||||
<q-checkbox v-model="displayAirspaceLayers.AIRPORTS" label="Letiště"/>
|
<q-card
|
||||||
<q-checkbox v-model="displayAirspaceLayers.FIELDSTRIP" label="SLZ plochy"/>
|
v-if="isControlsOpened"
|
||||||
|
class="q-my-md q-mx-auto q-px-lg"
|
||||||
|
>
|
||||||
|
|
||||||
|
<template v-for="(airspaceLayer, key) in airspaceLayers" :key="airspaceLayer">
|
||||||
|
<q-checkbox
|
||||||
|
v-model="airspaceLayers[key].displayed"
|
||||||
|
v-if="airspaceLayers[key].minZoom <= zoomLevel"
|
||||||
|
:label="airspaceLayer.label || key"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<q-slider
|
||||||
|
class="q-my-md"
|
||||||
|
v-model.number="maxAltitude"
|
||||||
|
:min="1000"
|
||||||
|
:max="15000"
|
||||||
|
label
|
||||||
|
label-always
|
||||||
|
:label-value="altitudeLimitFormatted(maxAltitude, 'ft')"
|
||||||
|
:step="1000"
|
||||||
|
/>
|
||||||
</q-card>
|
</q-card>
|
||||||
</CustomControl>
|
</CustomControl>
|
||||||
|
|
||||||
</GoogleMap>
|
</GoogleMap>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -18,3 +18,21 @@ export const useAirspaceColors = () => {
|
|||||||
|
|
||||||
return {colors, getColorByType}
|
return {colors, getColorByType}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const useAltitudeUtils = () => {
|
||||||
|
const altitudeToFt = (limit, unit) => {
|
||||||
|
if (unit === 'FL') return parseInt(limit) * 100;
|
||||||
|
return parseInt(limit);
|
||||||
|
}
|
||||||
|
const altitudeLimitFormatted = (limit, unit) => {
|
||||||
|
if (parseInt(limit) === 0) return 'GND';
|
||||||
|
else if (unit === 'FL') return `FL${limit}`;
|
||||||
|
else {
|
||||||
|
if (parseInt(limit) >= 5000) return `FL${limit / 100}`;
|
||||||
|
return `${limit} ft`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {altitudeLimitFormatted, altitudeToFt}
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export const useFlightPlanObjects = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const infoWindowObject = ({active, position, marker, poi, airport, reportingPoint, airspaces}) => {
|
const infoWindowObject = ({active, position, marker, poi, airport, reportingPoint, airspaces, isInTrack}) => {
|
||||||
if (!position) position = {};
|
if (!position) position = {};
|
||||||
if (!position.lat) position.lat = 0;
|
if (!position.lat) position.lat = 0;
|
||||||
if (!position.lng) position.lng = 0;
|
if (!position.lng) position.lng = 0;
|
||||||
@@ -83,7 +83,7 @@ export const useFlightPlanObjects = () => {
|
|||||||
position.lng = poi.gpsLongitude;
|
position.lng = poi.gpsLongitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {title, active, position, airspaces, poi, marker, airport, reportingPoint};
|
return {title, active, position, airspaces, poi, marker, airport, reportingPoint, isInTrack};
|
||||||
};
|
};
|
||||||
|
|
||||||
return {markerObject, googleMapsMarkerObject, googleMapsCustomMarkerObject, infoWindowObject, editableNameTypes}
|
return {markerObject, googleMapsMarkerObject, googleMapsCustomMarkerObject, infoWindowObject, editableNameTypes}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export const FRAGMENT_AIRPORT_INFO = gql`
|
|||||||
gpsLatitude
|
gpsLatitude
|
||||||
gpsLongitude
|
gpsLongitude
|
||||||
elevation
|
elevation
|
||||||
|
iconUrl
|
||||||
reportingPoints {
|
reportingPoints {
|
||||||
id
|
id
|
||||||
gpsLatitude
|
gpsLatitude
|
||||||
|
|||||||
Reference in New Issue
Block a user