'use strict' const qs = require('querystring') const {fetch} = require('fetch-ponyfill')({Promise: require('pinkie-promise')}) const parse = require('parse-jsonp') const isObj = o => 'object' === typeof o && o !== null && !Array.isArray(o) const endpoint = 'https://data-cloud.flightradar24.com/zones/fcgi/feed.js' const headers = { 'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0' } const defaults = { FAA: true, // use US/Canada radar data source FLARM: true, // use FLARM data source MLAT: true, // use MLAT data source ADSB: true, // use ADS-B data source inAir: true, // fetch airborne aircraft onGround: false, // fetch (active) aircraft on ground inactive: false, // fetch inactive aircraft (on ground) gliders: false, // fetch gliders estimatedPositions: false // if out of coverage } const request = (north, west, south, east, when, opt = {}) => { if ('number' !== typeof north) throw new Error('north must be a number') if ('number' !== typeof west) throw new Error('west must be a number') if ('number' !== typeof south) throw new Error('south must be a number') if ('number' !== typeof east) throw new Error('east must be a number') if (when && 'number' !== typeof when) throw new Error('when must be a number') opt = Object.assign({}, defaults, opt) const query = { bounds: [north, south, west, east].join(','), callback: 'jsonp', // options faa: opt.FAA ? '1' : '0', flarm: opt.FLARM ? '1' : '0', mlat: opt.MLAT ? '1' : '0', adsb: opt.ADSB ? '1' : '0', air: opt.inAir ? '1' : '0', gnd: opt.onGround ? '1' : '0', vehicles: opt.inactive ? '1' : '0', gliders: opt.gliders ? '1' : '0', estimated: opt.estimatedPositions ? '1' : '0' // todo: maxage, stats, history, prefetch } if (when) query.history = Math.round(when / 1000) const url = endpoint + '?' + qs.stringify(query) return fetch(url, { mode: 'cors', redirect: 'follow', headers, referrer: 'no-referrer', referrerPolicy: 'no-referrer' }) .then((res) => { if (!res.ok) { const err = new Error(res.statusText) err.statusCode = res.status throw err } return res.text() }) .then((jsonp) => { const data = parse('jsonp', jsonp) if (!isObj(data)) throw new Error('response data must be an object') const aircraft = [] for (let id in data) { const d = data[id] if (!Array.isArray(d)) continue aircraft.push({ id, registration: d[9] || null, flight: d[13] || null, callsign: d[16] || null, // ICAO ATC call signature origin: d[11] || null, // airport IATA code destination: d[12] || null, // airport IATA code latitude: d[1], longitude: d[2], altitude: d[4], // in feet bearing: d[3], // in degrees speed: d[5] || null, // in knots rateOfClimb: d[15], // ft/min isOnGround: !!d[14], squawkCode: d[6], // https://en.wikipedia.org/wiki/Transponder_(aeronautics) model: d[8] || null, // ICAO aircraft type designator modeSCode: d[0] || null, // ICAO aircraft registration number radar: d[7], // F24 "radar" data source ID isGlider: !!d[17], timestamp: d[10] || null }) } return aircraft }) } module.exports = request