Last active
December 10, 2021 17:56
-
-
Save donpdonp/7a494613103e1c4b30a74222fd68375d to your computer and use it in GitHub Desktop.
gluon airport announcer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
// setup | |
setup() | |
// descriptor | |
return {name:"airport"} | |
}) | |
var airports // airport data | |
var land_cache = {} | |
var alert_channel = '#pdxbots' | |
function setup() { | |
var url = "https://raw.githubusercontent.com/jpatokal/openflights/master/data/airports.dat" | |
bot.say(bot.admin_channel, 'Airport data loading from '+url) | |
cache = http.get(url) | |
if(cache.length > 0) { | |
bot.say(bot.admin_channel, 'Airport data loaded '+(cache.length/1024).toFixed(1)+' kibibytes. parsing...') | |
} else { | |
bot.say(bot.admin_channel, 'Airport data load failed') | |
} | |
airports = cache.split("\n").map(function(row){return deCsv(row)}) | |
bot.say('#zrobo', 'Airport data cache loaded with '+airports.length+' airports') | |
} | |
function go(msg) { | |
if (msg.method == "irc.privmsg") { | |
var cmd_match = /^!airport(\s+(\w+))?/.exec(msg.params.message) | |
if(cmd_match) { | |
user_get_airport(cmd_match[2], function(last_winner) { | |
var say | |
if (last_winner) { | |
var username = cmd_match[2] | |
db.get('icecondor:user'+':'+username+':track', function(trackJson){ | |
if(trackJson) { | |
var track = JSON.parse(trackJson) | |
var old_min = (new Date() - new Date(track.location.date))/1000/60 | |
bot.say(msg.params.channel, 'airport: '+username+' icecondor track '+old_min.toFixed(1)+' minutes old. ') | |
bot.say(msg.params.channel, 'searching '+airports.length+' airports for '+track.location.latitude.toFixed(4)+','+track.location.longitude.toFixed(4)) | |
var winner = airport_closest(track.location) | |
var land_acres = airport_land(winner.airport.code) | |
var acre_trigger = Math.sqrt(land_acres*4047)*0.6 // 4047 sq m per acre | |
var trigger = land_acres ? (acre_trigger/1000).toFixed(1)+' km trigger from '+land_acres+' acres' : "missing acreage data for "+winner.airport.code+" at airport-data.com" | |
bot.say(msg.params.channel, 'airport: '+username+' closest is '+winner.airport.code+' '+ | |
(winner.distance/1000).toFixed(0)+' km away ('+ | |
trigger+')') | |
} else { | |
} | |
}) | |
say = 'airport: '+cmd_match[2]+' last recorded airport '+ | |
last_winner.airport.code+' on '+last_winner.date.substr(0,10) | |
} else { | |
var code = cmd_match[2].toUpperCase() | |
var winners = airport_find(code) | |
if (winners.length > 0) { | |
var airport = winners[0] | |
var land_acres = airport_land(code) | |
var land = land_acres ? "("+land_acres+" acres. airport-data.com)" : "" | |
say = airport.code+'/'+airport.ecode+": "+[airport.name, airport.city, airport.country].join(', ')+ | |
' '+land+' '+airport.latitude.toFixed(4)+','+airport.longitude.toFixed(4) | |
} else { | |
say = "no airports found for "+code+" from database of "+airports.length+" airports" | |
} | |
} | |
bot.say(msg.params.channel, say) | |
}) | |
} | |
} | |
if(msg.method == "icecondor.location.off") { | |
var location = msg.params | |
var start = new Date() | |
var winner = airport_closest(location) | |
winner.date = start.toISOString() | |
winner.location = location | |
var duration = (new Date() - start)/1000 | |
user_get_airport(location.username, function(last_winner) { | |
var say | |
if(last_winner) { | |
var last_delay_hours = (new Date() - new Date(last_winner.date)) / 1000/60/60 | |
if (last_winner.airport.code == winner.airport.code && last_delay_hours < 24) { | |
//say = location.username+" is still near "+winner.airport.code+" ("+(winner.distance/1000).toFixed(1)+" km)"+ | |
// " for "+last_delay_hours+" hours" | |
} else { | |
var land_acres = airport_land(winner.airport.code) | |
var acre_trigger = Math.sqrt(land_acres*4047) // 4047 sq m per acre | |
if(winner.distance < acre_trigger) { | |
user_set_airport(location.username, winner) | |
say = location.username+" is "+(winner.distance/1000).toFixed(1)+"km from "+ | |
winner.airport.name+", "+winner.airport.country+" ("+ | |
airports.length+" airports checked in "+ | |
duration.toFixed(1)+" seconds "+(airports.length/duration).toFixed(0)+" airports/sec) "+ | |
winner.location.id | |
var land = "("+(winner.distance/1000).toFixed(1)+"km < "+(acre_trigger/1000).toFixed(1)+"km trigger)" | |
var lastport = last_winner.airport.code+"-"+winner.airport.code | |
bot.say(alert_channel, location.username+" is inside "+winner.airport.code+" "+winner.airport.name+" "+ | |
land+" "+lastport) | |
} else { | |
//bot.say(bot.admin_channel, location.username+" is not inside "+winner.airport.code+" "+ | |
// winner.airport.name+" "+"("+winner.airport.city+", "+winner.airport.country+")") | |
} | |
} | |
} else { | |
user_set_airport(location.username, winner) | |
say = "setting airport for "+location.username+" to "+winner.airport.code | |
} | |
if (say) { | |
bot.say(bot.admin_channel, "airport: "+say) | |
} | |
}) | |
} | |
} | |
function airport_land(code) { | |
if (code in land_cache) { | |
return land_cache[code] | |
} else { | |
var airportdata = http.get("http://www.airport-data.com/airport/"+code).replace(/\n/g,"").replace(/\r/g,"") | |
var land_match = /Land:.*?(\d+) acres/.exec(airportdata) | |
if(land_match) { | |
var acres = parseInt(land_match[1]) | |
land_cache[code] = acres | |
bot.say(bot.admin_channel, "airport: caching "+code+" acres "+acres) | |
return acres | |
} | |
} | |
} | |
function user_get_airport(username, cb) { | |
db.get('airport:'+username, function(json){ | |
var obj | |
try { | |
obj = JSON.parse(json) | |
} catch(e) { | |
bot.say(bot.admin_channel, "airport: ignoring old airport data for "+username+" of "+json) | |
} | |
cb(obj) | |
}) | |
} | |
function user_set_airport(username, obj) { | |
var json = JSON.stringify(obj) | |
bot.say(bot.admin_channel, "airport: setting "+username+" "+json) | |
db.set('airport:'+username, json) | |
} | |
function airport_closest(location) { | |
var winner, winner_distance, airport_distance | |
var nearest = airports.map(function(airport){ | |
airport_distance = pointDistance(location, airport) | |
if(!winner) { | |
winner = airport; winner_distance = airport_distance | |
} else { | |
if(airport_distance < winner_distance) { | |
winner = airport; winner_distance = airport_distance | |
} | |
} | |
return airport | |
}) //.sort(function(a){return a.distance}) | |
return {airport: winner, distance: winner_distance} | |
} | |
function airport_find(location) { | |
var winners = airports.filter(function(airport){ | |
return airport.code == location || airport.ecode == location | |
}) | |
return winners | |
} | |
function deCsv(row) { | |
var cols = row.split(',') | |
cols = cols.map(function(c){ | |
if(c[0] == '"' && c[c.length-1] == '"') { | |
c = c.substr(1, c.length-2) | |
} | |
return c | |
}) | |
return { | |
name: cols[1], | |
city: cols[2], | |
country: cols[3], | |
code: cols[4], | |
ecode: cols[5], | |
latitude: parseFloat(cols[6]), | |
longitude: parseFloat(cols[7]), | |
altitude: cols[8] | |
} | |
} | |
// Geofunctions | |
// from http://www.movable-type.co.uk/scripts/latlong.html | |
function pointDistance(la, lb) { | |
var lat1 = la.latitude, lon1 = la.longitude | |
var lat2 = lb.latitude, lon2 = lb.longitude | |
var dLat = numberToRadius(lat2 - lat1), | |
dLon = numberToRadius(lon2 - lon1), | |
a = Math.pow(Math.sin(dLat / 2), 2) + Math.cos(numberToRadius(lat1)) | |
* Math.cos(numberToRadius(lat2)) * Math.pow(Math.sin(dLon / 2), 2), | |
c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); | |
return (6371 * c) * 1000; // returns meters | |
} | |
function numberToRadius(number) { | |
return number * Math.PI / 180; | |
} | |
function numberToDegree(number) { | |
return number * 180 / Math.PI | |
} | |
function segmentBearing(loc1, loc2) { | |
var λ1 = numberToRadius(loc1.longitude) | |
var λ2 = numberToRadius(loc2.longitude) | |
var φ1 = numberToRadius(loc1.latitude) | |
var φ2 = numberToRadius(loc2.latitude) | |
var y = Math.sin(λ2-λ1) * Math.cos(φ2); | |
var x = Math.cos(φ1)*Math.sin(φ2) - | |
Math.sin(φ1)*Math.cos(φ2)*Math.cos(λ2-λ1); | |
var brng = Math.atan2(y, x)// .toDegrees() | |
var bearingDegrees = numberToDegree(brng) | |
var offset = (bearingDegrees < 0) ? 360 : 0 | |
return offset + bearingDegrees | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment