I will start to show the result: In the log of the device: According to the website: "It leverages enthusiast receivers to capture ADS-B (Automatic Dependent Surveillance-Broadcast) and MLAT (Multilateration) data, providing real-time information about aircraft in your area." On the site you can see a live map of the the current flights in the airspace above you.
There is also an API with which you can request the data. No api-key is needed. Shortest poll frequency interval is 1 second.
"Access does not currently require a feeder. That might change in the future."
At the moment I am not a feeder, but that might change in the future

The reason:
After routes have been shifted, I increasingly hear planes flying low south of Amsterdam (20 km from Schiphol airport). Sometimes I can count the windows.
I don't want to complain, but every once in a while I think it's worth reporting. I do not do it because I would not have registered the details. Sometimes I am just curious what the heigt is, when I see or hear an airplane.
The solution:
With the script I can request the data for a desired max. radius from my location.
With a frequency of 1 minute I sometimes caught an airplane several times. At different distances, because it is passing with high speed. I just want it one time AND with its coordinates as close as possible. Therefore I have now set the poll frequency every 15 seconds. I found this smart solution from waaren in https://www.domoticz.com/forum/viewtopi ... ds#p220418
This works very well.
So what it does:
- I store the result in a global table and overwrite its row content when the next one is closer.
- I write it to a text device when it is no longer reported within the specified radius.
- I am not interested in planes passing very high and therefore I filter on a configurable max. altitude and skip them.
- In the text device it provides the flight code (plus hyperlink to its flight on the live map) with details of the aircraft, altitude, speed and distance. The flight direction and the speed of ascent or descent are also displayed, as well as the time of the observation.
Code: Select all
--===============================================================
Every 5 minutes you see in the log that the OpenURL is scheduled (to run every 15 seconds)
2024-05-29 09:55:00.663 Status: dzVents: Debug: Airplanes-: OpenURL: url = https://api.airplanes.live/v2/point/52.14313121973369/4.672619005171448/1.86411357
2024-05-29 09:55:00.663 Status: dzVents: Debug: Airplanes-: OpenURL: method = GET
2024-05-29 09:55:00.663 Status: dzVents: Debug: Airplanes-: OpenURL: post data = nil
2024-05-29 09:55:00.663 Status: dzVents: Debug: Airplanes-: OpenURL: headers = nil
2024-05-29 09:55:00.663 Status: dzVents: Debug: Airplanes-: OpenURL: callback = airplanes
2024-05-29 09:55:00.663 Status: dzVents: Info: Airplanes-: ------ Finished t-Airplanes
--===============================================================
--Then it runs every 15 seconds. Several times nothing is found. Here an example.
2024-05-29 09:56:16.011 Status: dzVents: Info: Airplanes-: ------ Start internal script: t-Airplanes: HTTPResponse: "airplanes"
2024-05-29 09:56:16.011 Status: dzVents: Debug: Airplanes-: Item and JSON - OK
2024-05-29 09:56:16.012 Status: dzVents: Debug: Airplanes-: result_table: type = table
2024-05-29 09:56:16.012 Status: dzVents: Debug: Airplanes-: Total flights found in response = 0.
2024-05-29 09:56:16.012 Status: dzVents: Debug: Airplanes-: Nothing left in global _d.ap_table to write to device.
2024-05-29 09:56:16.012 Status: dzVents: Info: Airplanes-: ------ Finished t-Airplanes
--===============================================================
-- Example of an airplane passing higher that the max height that was set.
2024-05-29 09:56:31.051 Status: dzVents: Info: Airplanes-: ------ Start internal script: t-Airplanes: HTTPResponse: "airplanes"
2024-05-29 09:56:31.054 Status: dzVents: Debug: Airplanes-: Item and JSON - OK
2024-05-29 09:56:31.054 Status: dzVents: Debug: Airplanes-: result_table: type = table
2024-05-29 09:56:31.054 Status: dzVents: Debug: Airplanes-: Lat en Long: 52.116486 - 4.678879
2024-05-29 09:56:31.055 Status: dzVents: Debug: Airplanes-: Skip flight that is passing at 10729m. is higher then the set 3500 m.
2024-05-29 09:56:31.055 Status: dzVents: Info: Airplanes-: ------ Finished t-Airplanes
--===============================================================
-- In the next run it sees a airplane at 2.5 km.
2024-05-29 09:57:46.073 Status: dzVents: Info: Airplanes-: ------ Start internal script: t-Airplanes: HTTPResponse: "airplanes"
2024-05-29 09:57:46.075 Status: dzVents: Debug: Airplanes-: Item and JSON - OK
2024-05-29 09:57:46.075 Status: dzVents: Debug: Airplanes-: result_table: type = table
2024-05-29 09:57:46.075 Status: dzVents: Debug: Airplanes-: Lat en Long: 52.156615 - 4.702698
2024-05-29 09:57:46.076 Status: dzVents: Debug: Airplanes-: I see: 34654b with distance = 2.5 km.
2024-05-29 09:57:46.076 Status: dzVents: Debug: Airplanes-: 34654b is not found in _d.ap_table
2024-05-29 09:57:46.076 Status: dzVents: Debug: Airplanes-: New row created in global _d.ap_table with distance 2.5 km.
2024-05-29 09:57:46.076 Status: dzVents: Info: Airplanes-: ------ Finished t-Airplanes
--===============================================================
-- In the next run it sees the same airplane. >50% closer at 1.2 km. Find it in the table and update
2024-05-29 09:58:01.056 Status: dzVents: Info: Airplanes-: ------ Start internal script: t-Airplanes: HTTPResponse: "airplanes"
2024-05-29 09:58:01.059 Status: dzVents: Debug: Airplanes-: Item and JSON - OK
2024-05-29 09:58:01.059 Status: dzVents: Debug: Airplanes-: result_table: type = table
2024-05-29 09:58:01.059 Status: dzVents: Debug: Airplanes-: Lat en Long: 52.141067 - 4.690456
2024-05-29 09:58:01.059 Status: dzVents: Debug: Airplanes-: I see: 34654b with distance = 1.2 km.
2024-05-29 09:58:01.059 Status: dzVents: Debug: Airplanes-: 34654b is found in _d.ap_table
2024-05-29 09:58:01.059 Status: dzVents: Debug: Airplanes-: Global _d.ap_table exist with 1 rows.
2024-05-29 09:58:01.059 Status: dzVents: Debug: Airplanes-: Update row with closer distance 1.2 km.
2024-05-29 09:58:01.060 Status: dzVents: Info: Airplanes-: ------ Finished t-Airplanes
--===============================================================
-- In the next run it sees the same airplane again at 2.0 km. Find it in the table, but does not update, because it is further away.
2024-05-29 09:58:16.041 Status: dzVents: Info: Airplanes-: ------ Start internal script: t-Airplanes: HTTPResponse: "airplanes"
2024-05-29 09:58:16.045 Status: dzVents: Debug: Airplanes-: Item and JSON - OK
2024-05-29 09:58:16.046 Status: dzVents: Debug: Airplanes-: result_table: type = table
2024-05-29 09:58:16.047 Status: dzVents: Debug: Airplanes-: Lat en Long: 52.125229 - 4.67804
2024-05-29 09:58:16.047 Status: dzVents: Debug: Airplanes-: I see: 34654b with distance = 2.0 km.
2024-05-29 09:58:16.047 Status: dzVents: Debug: Airplanes-: 34654b is found in _d.ap_table
2024-05-29 09:58:16.047 Status: dzVents: Debug: Airplanes-: Global _d.ap_table exist with 1 rows.
2024-05-29 09:58:16.048 Status: dzVents: Info: Airplanes-: ------ Finished t-Airplanes
--===============================================================
-- In the next run it sees no airplane. Now time to check and write the row that is created to the device.
2024-05-29 09:58:31.032 Status: dzVents: Info: Airplanes-: ------ Start internal script: t-Airplanes: HTTPResponse: "airplanes"
2024-05-29 09:58:31.032 Status: dzVents: Debug: Airplanes-: Item and JSON - OK
2024-05-29 09:58:31.032 Status: dzVents: Debug: Airplanes-: result_table: type = table
2024-05-29 09:58:31.032 Status: dzVents: Debug: Airplanes-: Total flights found in response = 0.
2024-05-29 09:58:31.033 Status: dzVents: Debug: Airplanes-: Write and clear from global _d.ap_table, now no more flights are seen.
2024-05-29 09:58:31.045 Status: dzVents: Debug: Airplanes-: Processing device-adapter for Airplanes.Live: Text device
2024-05-29 09:58:31.045 Status: dzVents: Debug: Airplanes-: Airplane = <a href="https://globe.airplanes.live/?icao=34654b" target="_blank"><span style="color: Blue">KLM29M </span></a> - EC-NLJ - AIRBUS A-321<br>Hoogte: 2271 m. Snelheid: 471 km/u. Afstand: 1.2 km.<br>Richting: ZZW Stijgt 956 m/min. Tijd: 09:58:01
2024-05-29 09:58:31.045 Status: dzVents: Debug: Airplanes-: Saving row with distance 1.2 km.
2024-05-29 09:58:31.045 Status: dzVents: Debug: Airplanes-: Removed index 1 from global _d.ap_table.
2024-05-29 09:58:31.045 Status: dzVents: Info: Airplanes-: ------ Finished t-Airplanes
--===============================================================
-- Finaly it sees nothing and has nothing left to do.
2024-05-29 09:58:46.053 Status: dzVents: Info: Airplanes-: ------ Start internal script: t-Airplanes: HTTPResponse: "airplanes"
2024-05-29 09:58:46.054 Status: dzVents: Debug: Airplanes-: Item and JSON - OK
2024-05-29 09:58:46.054 Status: dzVents: Debug: Airplanes-: result_table: type = table
2024-05-29 09:58:46.054 Status: dzVents: Debug: Airplanes-: Total flights found in response = 0.
2024-05-29 09:58:46.054 Status: dzVents: Debug: Airplanes-: Nothing left in global _d.ap_table to write to device.
2024-05-29 09:58:46.054 Status: dzVents: Info: Airplanes-: ------ Finished t-Airplanes
--===============================================================
Code: Select all
-- 24-05-2024 script by Jan Peppink, https://ict.peppink.nl
-- Goal: Aircraft 'noise pollution detector'.
-- https://api.airplanes.live/v2/point/<lat>/<long>/<distanceKM>
-- Get data of airplaines that pass close by with lower altitudes.
-- When you hear them, they are probably logged. :-)
-- 27-05-2024 Call every 15 seconds. Store playe in global data table.
-- Update this when same plane is closer with call
-- When no planes are catched write the table (if not empty) to device.
--- Your settings ----------------------------------------------------------------
-- First set the used device index numbers and variables you might want to change.
---#################################################################
local ap_text_idx = 99999 --idx of the custom Text device
local ap_maxRadiusKm = 3 -- Query max radius in KM around your coordinates.
local ap_maxAltitudeM = 3500 -- Save only < max ap_maxAltitude in Meters.
----------------------------------------------------------------------------------
return {
on = {
timer = {
'every 5 minutes', -- Below in a loop it is made to 15 seconds!
},
httpResponses = {
'airplanes' -- matches callback string below
},
},
logging = {
-- Level can be domoticz.LOG_INFO, domoicz.LOG_MODULE_EXEC_INFO, domoticz.LOG_DEBUG, domoticz.LOG_ERROR or domoticz.LOG_FORCE
--level = domoticz.LOG_INFO,
--level = domoticz.LOG_DEBUG,
marker = 'Airplanes-',
},
execute = function(dz, triggeredItem)
-- Set Local environment=================
local _u = dz.utils -- Holds subset of handy utilities.
local _h = dz.helpers -- Holds the global functions
local _d = dz.globalData -- Holds the global data
--_d.initialize('ap_table') -- Re-initialize the table
-- Get location coordinates
local lat = dz.settings.location.latitude
local long = dz.settings.location.longitude
local ap_maxRadiusMiles = ap_maxRadiusKm * 0.62137119 -- Calculate given km in sdettings to miles.
-- Use some color variables
-- local cGreen = '#008000;'
-- local cYellow = '#ffff00;'
-- local cOrange = '#ffa500;'
-- local cRed = '#ff0000;'
-- local cWhite = '#FFFFFF;'
-- local cBlue = '#0000FF;'
-- local cGrey = '#8d8d8d;'
-- local htmlColor = '#3b3bc4;'
-- --Adjust lineheight
-- local lineHeight = 1.2
-- Local Functions go here =============
local function airplanesExist( testValue )
-- check if this plane is already in _d.ap_table
if type( _d.ap_table ) == "table" then
for i = 1, #_d.ap_table do
if _d.ap_table[i].ap_hex == testValue then
-- Found in table
dz.log( testValue .. ' is found in _d.ap_table', dz.LOG_DEBUG )
return true
end
end
-- Not found in table
dz.log( testValue .. ' is not found in _d.ap_table', dz.LOG_DEBUG )
return false
else
-- Table not found.
dz.log( 'Did not find _d.ap_table - Re-initialize.', dz.LOG_DEBUG )
_d.initialize('ap_table') -- Re-initialize the table
return false
end
end
-- Now start to do something ============
-- Get the data.
if (triggeredItem.isTimer) then
-- Retrieve the data every 15 second per 5 minutes.
for seconds = 15, 285, 15 do -- start with 15 step 15 until 285
dz.openURL({
url = 'https://api.airplanes.live/v2/point/' .. lat .. '/' .. long ..'/' .. ap_maxRadiusMiles,
method = 'GET',
callback = 'airplanes'
}).afterSec(seconds)
end
end
if (triggeredItem.isHTTPResponse) then
-- Process the obtained data.
-- Check the response and process the data.
if (triggeredItem.ok and triggeredItem.isJSON) then
dz.log( 'Item and JSON - OK', dz.LOG_DEBUG )
-- We have some result. Store in table.
local result_table = triggeredItem.json.ac
if type(result_table) == "table" then
dz.log( 'result_table: type = ' .. type(result_table), dz.LOG_DEBUG )
-- The data we get is:
--{
-- "ac": [
-- {
-- "hex": "485064",
-- "type": "adsb_icao",
-- "flight": "KLM66C ",
-- "r": "PH-EZY",
-- "t": "E190",
-- "desc": "EMBRAER ERJ-190-100",
-- "ownOp": "Klm Cityhopper",
-- "alt_baro": 1875,
-- "alt_geom": 2200,
-- "gs": 254.5,
-- "ias": 248,
-- "tas": 256,
-- "mach": 0.388,
-- "wd": 19,
-- "ws": 6,
-- "oat": 6,
-- "tat": 15,
-- "track": 93.6,
-- "track_rate": 0.09,
-- "roll": -0.18,
-- "mag_heading": 90,
-- "true_heading": 92.4,
-- "baro_rate": -192,
-- "geom_rate": -128,
-- "squawk": "7631",
-- "emergency": "none",
-- "category": "A3",
-- "nav_qnh": 1018.4,
-- "nav_altitude_mcp": 2016,
-- "nav_altitude_fms": 2000,
-- "nav_modes": [
-- "autopilot",
-- "tcas"
-- ],
-- "lat": 52.166748,
-- "lon": 4.638977,
-- "nic": 8,
-- "rc": 186,
-- "seen_pos": 0.179,
-- "recentReceiverIds": [
-- "8a6d5aed-e6d2-4a79",
-- "462a41f7-9808-5db2",
-- "b29d8c19-8849-41bb",
-- "8a4d3496-8f73-11ea",
-- "c21ace72-6613-4a53",
-- "4284e70f-e716-e5ab",
-- "01122182-d7a1-11ec",
-- "40d9183e-8c3f-47a1",
-- "6293f6c0-1ecf-4ad2",
-- "e74ec3c3-c566-e5e2",
-- "cdf69800-c853-47b0",
-- "f786031b-56b9-4b97",
-- "7715abf6-175d-4d44"
-- ],
-- "version": 2,
-- "nic_baro": 1,
-- "nac_p": 10,
-- "nac_v": 2,
-- "sil": 3,
-- "sil_type": "perhour",
-- "gva": 2,
-- "sda": 2,
-- "alert": 0,
-- "spi": 0,
-- "mlat": [],
-- "tisb": [],
-- "messages": 119867,
-- "seen": 0.2,
-- "rssi": -6.5,
-- "dst": 1.883,
-- "dir": 318.9
-- }
-- ],
-- "msg": "No error",
-- "now": 1716574591594,
-- "total": 1,
-- "ctime": 1716574591594,
-- "ptime": 0
--}
-- Now loop through the resul_table
if triggeredItem.json.total > 0 then
-- We see at least one plane. Retreive its data.
local tc = #result_table
for i = 1, tc do
local ap_hex = result_table[i].hex
local ap_flight = result_table[i].flight
local ap_r = result_table[i].r
local ap_t = result_table[i].t
local ap_desc = result_table[i].desc
local ap_gs = _u.round( result_table[i].gs * 1.851999278976, 0 ) -- Calculate groundspeed knots to km.
local ap_track = result_table[i].track -- direction in degree
local ap_direction = _h.getDirectionfromDegree( ap_track )
-- Translate abbreviation into Dutch abbreviation. E(ast) becomes O(ost) S(outh) becomes Z(uid).
ap_direction = string.gsub( ap_direction, "E", "O", 2 )
ap_direction = string.gsub( ap_direction, "S", "Z", 2 )
local ap_lat = result_table[i].lat
local ap_long = result_table[i].lon
dz.log( 'Lat en Long: ' .. ap_lat .. ' - ' .. ap_long, dz.LOG_DEBUG )
local ap_distance = _u.round( _h.calculateDistance( lat, long, ap_lat, ap_long ), 1 )
local ap_geom_rateString = ''
local ap_geom_rate = result_table[i].geom_rate
if ap_geom_rate ~= nil and ap_geom_rate > 0 then
-- stijgend
ap_geom_rateString = ' Stijgt ' .. _u.round ( ap_geom_rate * 0.3048 , 0 ) .. ' m/min.'
elseif ap_geom_rate ~= nil and ap_geom_rate < 0 then
--dalend
ap_geom_rateString = ' Daalt ' .. _u.round ( math.abs( ap_geom_rate ) * 0.3048 , 0 ) .. ' m/min.'
end
local ap_alt_geom = _u.round ( result_table[i].alt_geom * 0.3048 , 0 ) --Calculate height feet to meters.
if ap_alt_geom <= ap_maxAltitudeM then
-- It is within the max. height.
dz.log( 'I see: ' .. ap_hex .. ' with distance = ' .. ap_distance .. ' km.', dz.LOG_DEBUG )
if airplanesExist( ap_hex ) == true then
-- This plane exist already in our table. check and update when needed.
local tc = #_d.ap_table
dz.log( 'Global _d.ap_table exist with ' .. tc .. ' rows.', dz.LOG_DEBUG )
for i = 1, tc do
if _d.ap_table[i].ap_hex == ap_hex then
--We got the same airplane
if ap_distance < _d.ap_table[i].ap_distance then
dz.log( 'Update row with closer distance ' .. ap_distance .. ' km.', dz.LOG_DEBUG )
-- Update/Overwrite with the closest.
_d.ap_table[i].ap_time = dz.time.rawTime
_d.ap_table[i].ap_hex = ap_hex
_d.ap_table[i].ap_flight = ap_flight
_d.ap_table[i].ap_r = ap_r
_d.ap_table[i].ap_t = ap_t
_d.ap_table[i].ap_desc = ap_desc
_d.ap_table[i].ap_gs = ap_gs
_d.ap_table[i].ap_track = ap_track
_d.ap_table[i].ap_direction = ap_direction
_d.ap_table[i].ap_lat = ap_lat
_d.ap_table[i].ap_long = ap_long
_d.ap_table[i].ap_distance = ap_distance
_d.ap_table[i].ap_geom_rate = ap_geom_rate
_d.ap_table[i].ap_geom_rateString = ap_geom_rateString
_d.ap_table[i].ap_alt_geom = ap_alt_geom
end
end
end
else
--Not in the table. So insert this new airplane
dz.log( 'New row created in global _d.ap_table with distance ' .. ap_distance .. ' km.', dz.LOG_DEBUG )
table.insert( _d.ap_table, {
ap_time = dz.time.rawTime,
ap_hex = ap_hex,
ap_flight = ap_flight,
ap_r = ap_r,
ap_t = ap_t,
ap_desc = ap_desc,
ap_gs = ap_gs,
ap_track = ap_track,
ap_direction = ap_direction,
ap_lat = ap_lat,
ap_long = ap_long,
ap_distance = ap_distance,
ap_geom_rate = ap_geom_rate,
ap_geom_rateString = ap_geom_rateString,
ap_alt_geom = ap_alt_geom
});
end
else
dz.log( 'Skip flight that is passing at ' .. ap_alt_geom .. 'm. is higher then the set ' .. ap_maxAltitudeM .. ' m.', dz.LOG_DEBUG )
end
end
else
dz.log( 'Total flights found in response = 0.', dz.LOG_DEBUG )
-- See if we have something in the table to write to device.
if next( _d.ap_table ) ~= nil then
-- the Table exist
-- Store previous data from table if exist.
dz.log( 'Write and clear from global _d.ap_table, now no more flights are seen.', dz.LOG_DEBUG )
local linetoRemove = 0
if type( _d.ap_table ) == "table" then
--for eacht table row
tr = #_d.ap_table
for i = 1, tr do
--Get the previous stored data from device to compare
local ap_lastdeviceText = dz.devices(ap_text_idx).text -- Holds string of the previous round.
--Data we have in this row
local airplaneText = '<a href="https://globe.airplanes.live/?icao=' .. _d.ap_table[i].ap_hex .. '" target="_blank"><span style="color: Blue">' .. _d.ap_table[i].ap_flight .. '</span></a>' .. ' - ' .. _d.ap_table[i].ap_r .. ' - ' .. _d.ap_table[i].ap_desc ..
'<br>Hoogte: ' .. _d.ap_table[i].ap_alt_geom .. ' m. Snelheid: ' .. _d.ap_table[i].ap_gs .. ' km/u. Afstand: ' .. _d.ap_table[i].ap_distance .. ' km.' ..
'<br>Richting: ' .. _d.ap_table[i].ap_direction .. _d.ap_table[i].ap_geom_rateString .. ' Tijd: ' .. _d.ap_table[i].ap_time
dz.log( 'Airplane = ' .. airplaneText, dz.LOG_DEBUG )
if airplaneText ~= ap_lastdeviceText then
dz.log( 'Saving row with distance ' .. _d.ap_table[i].ap_distance .. ' km.', dz.LOG_DEBUG )
-- Data is changed, so update the device.
dz.devices(ap_text_idx).updateText( airplaneText )
end
linetoRemove = i
break
end
if linetoRemove > 0 then
-- Remove the row.
table.remove( _d.ap_table, linetoRemove )
dz.log( 'Removed index ' .. linetoRemove .. ' from global _d.ap_table.', dz.LOG_DEBUG )
end
end
else
dz.log( 'Nothing left in global _d.ap_table to write to device.', dz.LOG_DEBUG )
end
end
else
dz.log( 'No result_table found', dz.LOG_ERROR )
end
else
dz.log( 'Item or JSON - NOT OK', dz.LOG_DEBUG )
end
end
end
}
-- That's All --------------------------------------------------
The script uses a table that is stored in 'global_data' under data:
Code: Select all
-- Used in t-Airplanes
ap_table = { initial = {} },
Code: Select all
-- Used in dt-OpenMeteo, t-Weerlive, t-Airplanes
getDirectionfromDegree = function( degrees )
-- To calculate the degrees.
local directionString = ''
-- When string is not in Enlish, the icon does not appear!
if degrees >= 0 and degrees < 11.25 then directionString = 'N' end
if degrees >= 11.25 and degrees < 33.75 then directionString = 'NNE' end
if degrees >= 33.75 and degrees < 56.25 then directionString = 'NE' end
if degrees >= 56.25 and degrees < 78.75 then directionString = 'ENE' end
if degrees >= 78.75 and degrees < 101.25 then directionString = 'E' end
if degrees >= 101.25 and degrees < 123.75 then directionString = 'ESE' end
if degrees >= 123.75 and degrees < 146.25 then directionString = 'SE' end
if degrees >= 146.25 and degrees < 168.75 then directionString = 'SSE' end
if degrees >= 168.75 and degrees < 191.25 then directionString = 'S' end
if degrees >= 191.25 and degrees < 213.75 then directionString = 'SSW' end
if degrees >= 213.75 and degrees < 236.25 then directionString = 'SW' end
if degrees >= 236.25 and degrees < 258.75 then directionString = 'WSW' end
if degrees >= 258.75 and degrees < 281.25 then directionString = 'W' end
if degrees >= 281.25 and degrees < 303.75 then directionString = 'WNW' end
if degrees >= 303.75 and degrees < 326.25 then directionString = 'NW' end
if degrees >= 326.25 and degrees < 348.75 then directionString = 'NNW' end
if degrees >= 348.75 and degrees <= 360 then directionString = 'N' end
return directionString
end,
Code: Select all
-- Used in t-Earthquake-KNMI, t-Airplanes
calculateDistance = function( lat1, lon1, lat2, lon2)
-- Calculate distance using Haversine formula
local R = 6371 -- Earth radius in kilometers
local dLat = math.rad(lat2 - lat1)
local dLon = math.rad(lon2 - lon1)
local a = math.sin(dLat / 2) * math.sin(dLat / 2) + math.cos(math.rad(lat1)) * math.cos(math.rad(lat2)) * math.sin(dLon / 2) * math.sin(dLon / 2)
local c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
local distance = R * c
return distance
end,