Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vatglasses #387

Draft
wants to merge 38 commits into
base: next
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
661cedd
started work
FX5F Sep 25, 2024
cfab83c
Added watch for updated Positions. Disabled debug positions.
FX5F Oct 1, 2024
5df4d30
Fixed camelCase
FX5F Oct 1, 2024
0a35a66
Added missing watch
FX5F Oct 1, 2024
82aba30
Merge branch 'next' into vatglasses
FX5F Oct 18, 2024
1b57c73
Added server side sector calculation
FX5F Oct 24, 2024
b6090aa
Added Github download
FX5F Nov 1, 2024
833a824
Updated settings and updated combined data download
FX5F Nov 6, 2024
d69dcb3
Minor changes
FX5F Nov 6, 2024
c7a5954
Input Style changes
FX5F Nov 6, 2024
c84c464
few bug fixes
daniluk4000 Nov 9, 2024
a4a87f3
Some clean up
FX5F Nov 9, 2024
35ba9e6
Improvements based on comments
FX5F Nov 9, 2024
957f3aa
Renamed vars
FX5F Nov 9, 2024
461affc
Some clean up
FX5F Nov 10, 2024
2022328
Move api
FX5F Nov 11, 2024
a7f78a1
Use structuredClone
FX5F Nov 11, 2024
bcb346c
Added Type for properties and added atc to VatglassesActivePosition
FX5F Nov 12, 2024
3d14170
Minor sector fixes
FX5F Nov 12, 2024
29f1af9
Merge remote-tracking branch 'origin/next' into vatglasses
FX5F Nov 18, 2024
b33dddf
Upgraded to new cronjob function
FX5F Nov 18, 2024
3f133fb
Merge remote-tracking branch 'origin/next' into vatglasses
FX5F Nov 18, 2024
88d0fd8
yarn fix
FX5F Nov 19, 2024
43b2e36
Minor fixes
FX5F Nov 19, 2024
7ee0f4e
Merge branch 'next' into vatglasses
FX5F Nov 19, 2024
c15cce1
Type fix
FX5F Nov 21, 2024
0532641
Use attachMoveEnd
FX5F Nov 21, 2024
5e9fd49
Merge remote-tracking branch 'origin/vatglasses' into vatglasses
FX5F Nov 21, 2024
3a0278b
Border with
FX5F Nov 21, 2024
314f5dd
Fixed cronjob
FX5F Nov 21, 2024
ad77481
Minor fix
FX5F Nov 21, 2024
9c112de
Console log cleanup
FX5F Nov 21, 2024
73e081d
fix: ts
daniluk4000 Nov 30, 2024
b360790
Merge branch 'next' into vatglasses
daniluk4000 Nov 30, 2024
faaf0c8
chore: update lockfile
daniluk4000 Nov 30, 2024
6cd1ee3
fix: hexToColor bugging
daniluk4000 Nov 30, 2024
341ecae
Added vatspy as fallback
FX5F Nov 30, 2024
60d082d
Merge remote-tracking branch 'origin/vatglasses' into vatglasses
FX5F Nov 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@
"@influxdata/influxdb-client-apis": "^1.35.0",
"@pinia/nuxt": "^0.8.0",
"@prisma/client": "^6.0.0",
"@turf/difference": "^7.1.0",
"@turf/great-circle": "^7.1.0",
"@turf/helpers": "^7.1.0",
"@turf/intersect": "^7.1.0",
"@turf/meta": "^7.1.0",
"@turf/truncate": "^7.1.0",
"@turf/union": "^7.1.0",
FX5F marked this conversation as resolved.
Show resolved Hide resolved
"@vite-pwa/nuxt": "^0.10.6",
"adm-zip": "^0.5.16",
"aws-sdk": "^2.1692.0",
Expand All @@ -37,6 +43,7 @@
"jwks-rsa": "^3.1.0",
"kafkajs": "^2.2.4",
"marked": "^15.0.3",
"merge-ranges": "^1.0.2",
"metar-taf-parser": "^9.0.1",
"nuxt": "^3.14.1592",
"ol": "^10.2.1",
Expand All @@ -62,6 +69,7 @@
"@types/adm-zip": "^0.5.7",
"@types/geojson": "^7946.0.14",
"@types/jsonwebtoken": "^9.0.7",
"@types/merge-ranges": "^1",
"@types/node": "^22.10.1",
"@types/ws": "^8.5.13",
"eslint": "^9.16.0",
Expand Down
6 changes: 5 additions & 1 deletion src/components/common/basic/CommonInputText.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
v-bind="inputAttrs"
v-model="model"
:placeholder
type="text"
:type="inputType"
@blur="focused = false"
@change="$emit('change', $event)"
@focus="focused = true"
Expand All @@ -35,6 +35,10 @@ defineProps({
type: Object as PropType<Record<string, any>>,
default: () => {},
},
inputType: {
type: String,
default: 'text',
},
placeholder: {
type: String,
},
Expand Down
4 changes: 3 additions & 1 deletion src/components/map/airports/MapAirport.vue
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,9 @@ onMounted(async () => {
}
}

watch(dataStore.vatsim.updateTimestamp, () => initAndUpdateData(), {
const isVatglassesActive = computed(() => store.mapSettings.vatglasses?.active);
const vatglassesFallbacks = computed(() => dataStore.vatglassesActivePositions.value['fallback']);
watch([dataStore.vatsim.updateTimestamp, isVatglassesActive, vatglassesFallbacks], () => initAndUpdateData(), {
immediate: true,
});

Expand Down
4 changes: 4 additions & 0 deletions src/components/map/airports/MapAirportsList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,10 @@ const getAirportsList = computed(() => {
if (!airport) continue;

if (isArr) {
if (store.mapSettings.vatglasses?.active && dataStore.vatglassesActivePositions.value['fallback']) {
const fallbackPositions = Object.keys(dataStore.vatglassesActivePositions.value['fallback']);
if (!fallbackPositions.includes(atc.atc.callsign)) continue; // We don't add the current station if it is not in the fallback array, because it is shown with vatglasses sector. We need the tracon sectors as fallback for positions which are not defined in vatglasses.
}
airport.arrAtc.push(atc.atc);
airport.arrAtcInfo.push(atc);
continue;
Expand Down
1 change: 1 addition & 0 deletions src/components/map/filters/MapFilters.vue
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ import { isFetchError, MAX_MAP_PRESETS } from '~/utils/shared';
import CommonTooltip from '~/components/common/basic/CommonTooltip.vue';
import MapFiltersTraffic from '~/components/map/filters/MapFiltersTraffic.vue';


const store = useStore();

const isOpened = computed(() => store.localSettings.filters?.opened !== false);
Expand Down
65 changes: 65 additions & 0 deletions src/components/map/filters/settings/MapSettingsLayers.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,59 @@
@update:modelValue="setUserMapSettings({ aircraftScale: $event as number })"
/>
</div>

<common-block-title>
VATGlasses
</common-block-title>

<div class="__section-group __section-group--even">
<common-toggle
:model-value="!!store.mapSettings.vatglasses?.active"
@update:modelValue="setUserMapSettings({ vatglasses: { active: $event } })"
>
Enable VATGlasses
</common-toggle>
<common-toggle
:disabled="!store.mapSettings.vatglasses?.active"
:model-value="store.mapSettings.vatglasses?.combined"
@update:modelValue="setUserMapSettings({ vatglasses: { combined: $event } })"
>
Combined Mode

<template #description>
All sectors at once. Eats performance.
</template>
</common-toggle>
</div>

<div
v-if="store.mapSettings.vatglasses?.active && !store.mapSettings.vatglasses?.combined"
class="__grid-info-sections __grid-info-sections--large-title"
>
<div class="__grid-info-sections_title">
VATGlasses Level
</div>
<div class="__section-group">
<input
v-model="vatglassesLevel"
max="430"
min="0"
step="10"
type="range"
>
<common-input-text
v-model="vatglassesLevel"
class="vatglassesLevel-input"
:input-attrs="{
max: 430,
min: 0,
step: 10,
}"
input-type="number"
/>
</div>
</div>

<common-block-title>
Airports Counters
</common-block-title>
Expand Down Expand Up @@ -136,13 +189,25 @@ import CommonSelect from '~/components/common/basic/CommonSelect.vue';
import type { SelectItem } from '~/types/components/select';
import CommonBlockTitle from '~/components/common/blocks/CommonBlockTitle.vue';
import CommonButton from '~/components/common/basic/CommonButton.vue';
import CommonInputText from '~/components/common/basic/CommonInputText.vue';
import { backupMapSettings } from '~/composables/settings';
import { resetUserMapSettings } from '~/composables';

const store = useStore();

const resetActive = ref(false);

const vatglassesLevel = computed({
get() {
return store.localSettings.vatglassesLevel?.toString();
},
set(value) {
if (value !== undefined) {
setUserLocalSettings({ vatglassesLevel: Number(value) });
}
},
});

// For type safety
const countersOptions: Record<Required<IUserMapSettings['airportsCounters']>['departuresMode'], string> = {
total: 'Total departures',
Expand Down
19 changes: 14 additions & 5 deletions src/components/map/sectors/MapSector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ const hasScroll = useScrollExists(computed(() => {
}));

const mapStore = useMapStore();
const dataStore = useDataStore();
const vectorSource = inject<ShallowRef<VectorSource | null>>('vector-source')!;
const isHovered = ref(false);
let localFeature: Feature | undefined;
Expand All @@ -98,15 +99,23 @@ let rootFeature: Feature | undefined;
const store = useStore();

const locals = computed(() => {
const filtered = props.atc.filter(x => !x.icao && x.controller && x.firs.filter(x => x.boundaryId === props.fir.feature.id));
if (!dataStore.vatglassesActivePositions.value) return [];

return filtered.filter((x, index) => index <= filtered.findIndex(y => y.controller?.cid === x.controller!.cid));
let filtered = props.atc.filter(x => !x.icao && x.controller && x.firs.filter(x => x.boundaryId === props.fir.feature.id));
filtered = filtered.filter((x, index) => index <= filtered.findIndex(y => y.controller?.cid === x.controller!.cid));
if (!store.mapSettings.vatglasses?.active || !dataStore.vatglassesActivePositions.value['fallback']) return filtered;

const fallbackPositions = Object.keys(dataStore.vatglassesActivePositions.value['fallback']);
return filtered.filter(x => fallbackPositions.includes(x.controller?.callsign)); // We filter out all stations which are not in the fallback list, because they are shown with vatglasses sector. We need the vatspy sectors as fallback for positions which are not defined in vatglasses.
});

const globals = computed(() => {
const filtered = props.atc.filter(x => x.icao && x.controller);
let filtered = props.atc.filter(x => x.icao && x.controller);
filtered = filtered.filter((x, index) => index <= filtered.findIndex(y => y.controller?.cid === x.controller!.cid));
if (!store.mapSettings.vatglasses?.active || !dataStore.vatglassesActivePositions.value['fallback']) return filtered;

return filtered.filter((x, index) => index <= filtered.findIndex(y => y.controller?.cid === x.controller!.cid));
const fallbackPositions = Object.keys(dataStore.vatglassesActivePositions.value['fallback']);
return filtered.filter(x => fallbackPositions.includes(x.controller?.callsign)); // We filter out all stations which are not in the fallback list, because they are shown with vatglasses sector. We need the vatspy sectors as fallback for positions which are not defined in vatglasses.
});

const controllers = computed(() => {
Expand Down Expand Up @@ -178,7 +187,7 @@ const init = () => {

onMounted(init);

watch([() => props.atc, isHovered], init);
watch([() => props.atc, isHovered, locals], init);

onBeforeUnmount(() => {
if (localFeature) {
Expand Down
130 changes: 125 additions & 5 deletions src/components/map/sectors/MapSectorsList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,132 @@
:fir="sector.fir"
/>
</template>

<template v-if="!isHideAtcType('firs') && store.mapSettings.vatglasses?.active">
<template
v-for="(countryEntries, countryId) in dataStore.vatglassesActivePositions.value"
:key="countryId"
>
<map-vatglasses-position
v-for="(position, positionId) in countryEntries"
:key="countryId + '-' + positionId"
:position="position"
/>
</template>


<map-overlay
class="vatglasses-overlay"
:model-value="vatglassesPopupIsShown"
:settings="{
position: getCoordinates,
offset: [15, -15],
}"
:z-index="20"
>
<common-popup-block
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be awesome if you could make cursor pointer implementation when hovering features. In same way as it's done in other components

class="aircraft-hover"
>
<template #title>
Positions
FX5F marked this conversation as resolved.
Show resolved Hide resolved
</template>

<ul>
<li
v-for="(sector, index) in sectorsAtClick"
:key="index"
>
{{ sector.vatglassesPositionId }} ({{ sector.atc.callsign }}) {{ formatNumber(sector.min) }} - {{ formatNumber(sector.max) }}
</li>
</ul>

</common-popup-block>
</map-overlay>
</template>
</template>

<script setup lang="ts">
import VectorSource from 'ol/source/Vector';
import type { ShallowRef } from 'vue';
import type { Map } from 'ol';
import type { Map, MapBrowserEvent } from 'ol';
import { Fill, Stroke, Style } from 'ol/style';
import MapSector from '~/components/map/sectors/MapSector.vue';
import MapVatglassesPosition from '~/components/map/sectors/MapVatglassesPosition.vue';
import VectorImageLayer from 'ol/layer/VectorImage';
import { useStore } from '~/store';
import MapSector from '~/components/map/sectors/MapSector.vue';
import { attachMoveEnd } from '~/composables';

import { initVatglasses } from '~/utils/data/vatglasses';
import type { VatglassesSectorProperties } from '~/utils/data/vatglasses';

import type { Pixel } from 'ol/pixel';


let vectorLayer: VectorImageLayer<any>;
const vectorSource = shallowRef<VectorSource | null>(null);
provide('vector-source', vectorSource);
const map = inject<ShallowRef<Map | null>>('map')!;
const dataStore = useDataStore();
const store = useStore();


const firs = computed(() => {
const list = dataStore.vatspy.value!.data.firs;
const firs = list.map(fir => ({
fir,
atc: dataStore.vatsim.data.firs.value.filter(x => x.firs.some(x => x.boundaryId === fir.feature.id && (fir.icao === x.icao || (fir.callsign && fir.callsign === x.callsign)))) ?? [],
}));

return firs.filter((x, xIndex) => !firs.some((y, yIndex) => y.fir.icao === x.fir.icao && x.fir.feature.id === y.fir.feature.id && yIndex < xIndex));
});


const sectorsAtClick = shallowRef<VatglassesSectorProperties[]>([]);
const getCoordinates = ref([0, 0]);
const vatglassesPopupIsShown = ref(false);

let lastEventPixel: Pixel | null = null;
async function handleClick(e: MapBrowserEvent<any>) {
// TODO: don't show popup when clicked target has an aircraft
const eventPixel = map.value!.getPixelFromCoordinate(e.coordinate);

if (lastEventPixel && lastEventPixel[0] === eventPixel[0] && lastEventPixel[1] === eventPixel[1]) {
// same location, close popup
sectorsAtClick.value = [];
vatglassesPopupIsShown.value = false;
lastEventPixel = null;
return;
}
lastEventPixel = eventPixel;
const featureSectors = map.value!.getFeaturesAtPixel(eventPixel, {
hitTolerance: 0, // we use 6 instead of 5 because of the aircraft icons size, it is just for cosmetic reasons
layerFilter: layer => layer.getProperties().type === 'sectors',
});

const sectors: VatglassesSectorProperties[] = [];
featureSectors.map(feature => {
const properties = feature.getProperties() as VatglassesSectorProperties;
sectors.push(properties);
});

sectorsAtClick.value = sectors.filter(x => x.atc);

getCoordinates.value = e.coordinate;
vatglassesPopupIsShown.value = !!sectorsAtClick.value.length;
}


attachMoveEnd(() => {
// Change of map position
sectorsAtClick.value = [];
vatglassesPopupIsShown.value = false;
lastEventPixel = null;
});

function formatNumber(number: number) {
if (number === 0) return 'GND';
return 'FL' + number.toString().padStart(3, '0');
}

watch(map, val => {
if (!val) return;

Expand Down Expand Up @@ -107,15 +207,28 @@ watch(map, val => {
zIndex: 5,
});

const vatglassesStyle = (color: string, altMax: number = 1): Style => {
// console.log('color',color)
return new Style({

fill: new Fill({
color: `rgb(${ hexToRgb(color) }, 0.2)`,
}),
stroke: new Stroke({
color: `rgb(${ hexToRgb(color) }, 0.6)`,
width: 1,
}),
zIndex: altMax,
});
};

vectorLayer = new VectorImageLayer<any>({
source: vectorSource.value,
zIndex: 2,
properties: {
type: 'sectors',
},
style: function(feature) {
if (feature.getGeometry()?.getType() !== 'MultiPolygon') return;

const type = feature.getProperties().type;

switch (type) {
Expand All @@ -129,12 +242,19 @@ watch(map, val => {
return hoveredStyle;
case 'hovered-root':
return hoveredRootStyle;
case 'vatglasses':
// console.log(feature.getProperties());
return vatglassesStyle(feature.getProperties().colour, feature.getProperties().max);
default:
return localStyle;
}
},
});
}

val.addLayer(vectorLayer);
initVatglasses();
val.on('click', handleClick);
}, {
immediate: true,
});
Expand Down
Loading