Skip to content

Instantly share code, notes, and snippets.

@donpdonp
Last active December 10, 2021 17:56
Show Gist options
  • Save donpdonp/7a494613103e1c4b30a74222fd68375d to your computer and use it in GitHub Desktop.
Save donpdonp/7a494613103e1c4b30a74222fd68375d to your computer and use it in GitHub Desktop.
gluon airport announcer
(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