Skip to content

Commit

Permalink
Aerodrome and bay APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
Kahn committed Nov 13, 2021
1 parent bec3533 commit ecb4d57
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 56 deletions.
85 changes: 82 additions & 3 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,35 @@
## TODO

### Feedback
* Map the on the ground users.
* Make major AD's permanently shown.
* Join AFV freqs with sectors to show online.
* Fix the on the ground altitude maths
* Make light theme AD parts darker
* Markers and labels are too small on the ground.
* Query OSM via Overpass API for aeroway=parking_position
// Get aerodrome polygon
// if aeroway=parking_position not undefined
// bay = aeroway=parking_position ref
Nah - Use https://overpass-turbo.eu/# to pull the features geojson into a mapbox tileset for a quick and dirty option.
Expand client.js to enrich pilots API

// Boarding - On aerodrome, on apron poly. 0 GS
// Departing - On aerodrome
// Enroute - Off aerodrome
// Arriving - On aerodrome
// Arrived - On aerodrome, on apron poly. 0 GS


* Add FL and GS to labels aka datatag
Ideas

Airspace map - sector map for Zach.
https://vatpac.org/controllers/airspace/

Add CID search to local

### Feedback

* Add FL and GS to labels aka datatag to labels

* Instrument API response times and request times from VATSIM to track down loading delays

Expand All @@ -13,6 +39,22 @@

## Future

### OSM mapping

Aerodromes missing aeroway=parking_positions:

Major
* YPDN - Done
* YBCS
* YPAD
* YPPH

Metro
* YBAF
* YSBK
* YMAV
* YPPF

### Features

* Theme switch light / dark
Expand All @@ -31,16 +73,41 @@
* Use nav API for progressive taxi or draw on ground map routes
* Use GS and HDG to animate markers between refresh

Query tilesets to get "in poly" for ATC on aerodromes.https://docs.mapbox.com/help/tutorials/find-elevations-with-tilequery-api/
https://docs.mapbox.com/mapbox-gl-js/api/map/#map#querysourcefeatures
https://docs.mapbox.com/mapbox-gl-js/example/filter-features-within-map-view/
https://docs.mapbox.com/mapbox-gl-js/example/query-similar-features/

Stretch goal enrich the pilot locations API for aerodrome reporting board with where the acft is
WAT499 YSSY YMML Boarding gate 54
WAT499 YSSY YMML Taxiing on C GS 15
WAT499 YSSY YMML Departed

* Use mapbox 3D elements for dynamic ATC poly visualizations https://blog.mapbox.com/dive-into-large-datasets-with-3d-shapes-in-mapbox-gl-c89023ef291


### Tech debt

* What is going wrong with test imports?
* Retest the xmlToPoly client with a single Line XML file
* Retest text and icon scaling on low res PC, mobile
* Add GS in hundreds via API for display
* Expose alt format via API for display
* Add instrumentation to capture iteration cost https://dev.to/typescripttv/measure-execution-times-in-browsers-node-js-js-ts-1kik
* Implement URL discovery via https://status.vatsim.net/status.json for https://github.com/vatsimnetwork/developer-info/wiki/Data-Feeds

### Feedback

Possible to see C steps. Import LL labels markers.
TMA splits

### Current stats
In FIR now
Top types
Arr / dep counts

### Stored stats
Is using a DB going to be worth it? Start tracking what needs it. Eg ENR time and track. Persist objects in memory for a few hours?
Traffic heatmap (DB needed)

## Credits
Expand All @@ -50,6 +117,9 @@ https://commons.wikimedia.org/wiki/File:Plane_font_awesome.svg
public/flaticon.com/ga-*.png
<div>Icons made by <a href="https://www.freepik.com" title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a></div>

Openstreet map data
The data included in this document is from www.openstreetmap.org. The data is made available under ODbL

## Theme

Green 33cc99 A10
Expand All @@ -69,4 +139,13 @@ https://docs.mapbox.com/mapbox-gl-js/example/measure/
https://docs.mapbox.com/mapbox-gl-js/example/set-popup/

Change markers on zoom level. Would require iterating markers
https://docs.mapbox.com/mapbox-gl-js/example/updating-choropleth/
https://docs.mapbox.com/mapbox-gl-js/example/updating-choropleth/

Sectors.xml
Meta -> Volumes
Sector -> Volumes(VolumeName)

Volumes.xml
Meta -> Line
Volume(Name) -> Boundaries(Name)
Volume(ARA) -> Boundaries(ARAFURA)
26 changes: 21 additions & 5 deletions aerodrome.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getOSMAerodromeData } from './client.js';
import { getOSMAerodromeData, getOSMParkingPositionData } from './client.js';
import { point, featureCollection } from '@turf/helpers';
import booleanPointInPolygon from '@turf/boolean-point-in-polygon';
import bunyan from 'bunyan';
Expand All @@ -8,10 +8,10 @@ var log = bunyan.createLogger({name: config.get('app.name'), level: config.get('

export async function getAerodromes(){
try{
var aerodromes = await getOSMAerodromeData(config.get('data.osm.aerodromesArea'));
if(aerodromes){
if(aerodromes.type === "FeatureCollection"){
return aerodromes;
var data = await getOSMAerodromeData(config.get('data.osm.aerodromesArea'));
if(data){
if(data.type === "FeatureCollection"){
return data;
}else{
return false;
}
Expand All @@ -34,4 +34,20 @@ export async function getMajorAerodromes(){
}
});
});
}

export async function getAerodromeBays(){
try{
var data = await getOSMParkingPositionData(config.get('data.osm.aerodromesArea'));
if(data){
if(data.type === "FeatureCollection"){
return data;
}else{
return false;
}
};
}catch(err){
log.error(err)
return false;
}
}
64 changes: 50 additions & 14 deletions atc.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,24 @@ function uniq(a) {
return Array.from(new Set(a));
}

function getSectorByName(sectorName, sectors){
var sector = sectors.find(e => {
if(e.Name === sectorName){
return e;
};
});
return sector;
}

function getSectorByCallsign(sectorName, sectors){
var sector = sectors.find(e => {
if(e.Callsign === sectorName){
return e;
};
});
return sector;
}

function mergeSectors(sector, sectors, json){
let mergedSector;
// FSS don't have sector volumes, only responsible sectors.
Expand Down Expand Up @@ -237,36 +255,54 @@ export async function getOnlinePositions() {
// SY_APP 124.400 AFV 124400000
// iterate txvrs.element.transceivers.element frequency/1000000
stations.forEach(function(station, index){
var activePosition = {};
var activePosition = false;
// Keep only CTR, APP, and TWR
if(station.callsign.toUpperCase().includes("CTR") === false && station.callsign.toUpperCase().includes("APP") === false && station.callsign.toUpperCase().includes("TWR") === false){
delete stations[index];
}else{
var txvrs = [];
var frequency = [];
var activeFrequncies = [];
// Transform frequencies array
station.transceivers.forEach(function(element){
// Hertz to Megahurts
element.frequency = element.frequency/1000000;
txvrs.push(element);
activeFrequncies.push(element.frequency);
})
frequency = uniq(txvrs);
activeFrequncies = uniq(activeFrequncies);

// Join sectors by callsign
var sector = sectors.find(function cb(element){
if(element.Callsign === station.callsign){
return element;
// Check std sectors and load sub sectors.
if(element.standard_position === true){
var sectorWithSubsectors = mergeSectors(element, element.responsibleSectors,sectors);
return sectorWithSubsectors;
}else{
return element;
}
};
});
if(sector !== undefined){
onlineSectors.push(mergeBoundaries(sector));
activePosition = mergeBoundaries(sector);
}
// Join sectors by frequency
// TODO - Only if adjacent to match vatpac extending policy.
txvrs.forEach(function(element){
var adjacentSector = isAdjacentSector(element.frequency, activePosition, sectors);
if(adjacentSector !== false){
onlineSectors.push(mergeBoundaries(adjacentSector))
}
})

if(activePosition !== false){
// Join sectors by frequency
// TODO - How to incrementally add sectors working outwards from the logged on sector?
var extendedPoly = activePosition;
activeFrequncies.forEach(function(element){
var adjacentSector = isAdjacentSector(element, extendedPoly, sectors);
if(adjacentSector !== false){
extendedPoly = unionArray([extendedPoly, sectorWithSubsectors])
if(adjacentSector.standard_position === true){
var sectorWithSubsectors = mergeSectors(adjacentSector, adjacentSector.responsibleSectors,sectors);
onlineSectors.push(sectorWithSubsectors);
}else{
onlineSectors.push(mergeBoundaries(adjacentSector));
}
}
})
}
}
})

Expand Down
53 changes: 39 additions & 14 deletions client.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import config from 'config';
import { iso2dec } from './iso2dec.js';
import {Mutex, Semaphore, withTimeout} from 'async-mutex';
import uniqueRandomArray from 'unique-random-array';
import sha1 from 'sha1';

var log = bunyan.createLogger({name: config.get('app.name'), level: config.get('app.log_level')});

Expand Down Expand Up @@ -108,31 +109,55 @@ async function getVatsimServers(){

export async function getOSMAerodromeData (areaName) {
log.info(`getOSMAerodromeData`);
var data = await queryOverpass(
`area["name"="${areaName}"]->.boundaryarea;
(
nwr(area.boundaryarea)["aeroway"="aerodrome"];
);
out body;
>;
out skel qt;`
)
return data;
}

export async function getOSMParkingPositionData (areaName) {
log.info(`getOSMParkingPositionData`);
var data = await queryOverpass(
`area["name"="${areaName}"]->.boundaryarea;
(
nwr(area.boundaryarea)["aeroway"="parking_position"];
);
out body;
>;
out skel qt;`
)
return data;
}

export async function queryOverpass (query) {
var cachekey = sha1(query);
log.info(`queryOverpass ${cachekey}`);
log.info(query);
var data = await mutex.runExclusive(async () => {
log.info(`mutex locked`);
var ttlMs = cache.getTtl(areaName);
log.info(`mutex locked ${cachekey}`);
var ttlMs = cache.getTtl(cachekey);
let data;
if (ttlMs == undefined || ttlMs - Date.now() <= 120000) {
log.info(`Querying OSM`);
log.info(`Querying OSM ${cachekey}`);
try{
data = await query_overpass(
`area["name"="${areaName}"]->.boundaryarea;
(
nwr(area.boundaryarea)["aeroway"="aerodrome"];
);
out body;
>;
out skel qt;`,
query,
function(err, data){
if(err){
log.error(err);
}else{
log.info({
cache: 'set',
area: areaName,
cachekey: cachekey,
keys: Object.keys(data).length
})
cache.set(areaName, data, 86400);
cache.set(cachekey, data, 86400);
}
},
{ overpassUrl: config.get('data.osm.overpassUrl'), userAgent: `${config.get('app.name')}/${config.get('app.version')}` }
Expand All @@ -144,10 +169,10 @@ export async function getOSMAerodromeData (areaName) {
}
}else{
log.info(`Return cached OSM response`);
data = cache.get(areaName);
data = cache.get(cachekey);
log.info({
cache: 'get',
area: areaName,
cachekey: cachekey,
keys: Object.keys(data).length
})
}
Expand Down
3 changes: 1 addition & 2 deletions config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,9 @@
"BN-KEN_CTR",
"BN-ISA_CTR",
"BN-INL_CTR",
"BN-ARM_CTR",
"BN-ARL_CTR",
"ML-OLW_CTR",
"ML-ASP_CTR",
"ML-ARM_CTR",
"ML-TBD_CTR",
"ML-PIY_CTR",
"ML-BIK_CTR",
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"private": true,
"dependencies": {
"@adobe/node-fetch-retry": "^1.1.1",
"@mapbox/polylabel": "^1.0.2",
"@turf/boolean-intersects": "^6.5.0",
"@turf/turf": "^6.5.0",
"async-mutex": "^0.3.2",
"bunyan": "^1.8.15",
Expand All @@ -17,6 +19,7 @@
"node-fetch": "^2.6.1",
"query-overpass": "https://github.com/Kahn/query-overpass.git",
"rgb2hex": "^0.2.5",
"sha1": "^1.1.1",
"unique-random-array": "^3.0.0",
"xml-js": "^1.6.11"
},
Expand Down
Loading

0 comments on commit ecb4d57

Please sign in to comment.