Skip to content

Instantly share code, notes, and snippets.

@donpdonp
Last active March 22, 2022 17:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save donpdonp/90f77b35b5165e499c791f9e3e67cc6c to your computer and use it in GitHub Desktop.
Save donpdonp/90f77b35b5165e499c791f9e3e67cc6c to your computer and use it in GitHub Desktop.
gluon osm openstreetmap lookups
(function() {
// descriptor
return {name:"osm"}
})
// osm json
//{"bearing":345.9410965462545,"distance":355.0966239302687,
// "node":{"id":1963188104,"lat":45.5233261,"lon":-122.6584695,"tags":{...
function go(msg) {
if (msg.method == "irc.privmsg") {
var cmd = /^!osm(\s+(.*))?/.exec(msg.params.message)
if(cmd) {
if(cmd[2]) {
bot.say(bot.admin_channel, "osm: "+msg.params.nick+' '+cmd[2])
track(msg.params.nick, function(track){
if (track) {
//bot.say(msg.params.channel, msg.params.nick+' '+JSON.stringify(track.location))
var data = osmtag(cmd[2], track.location.latitude, track.location.longitude, 0.01)
bot.say(msg.params.channel, 'search +/-0.01 deg lat/lng of ['+
track.location.latitude.toFixed(4)+','+track.location.longitude.toFixed(4)+'] nodes found: '
+data.elements.length)
if(data.elements.length == 0) { // try again, a bit wider
data = osmtag(cmd[2], track.location.latitude, track.location.longitude, 0.1)
bot.say(msg.params.channel, 'search +/-0.1 deg lat/lng of ['+
track.location.latitude.toFixed(4)+','+track.location.longitude.toFixed(4)+'] nodes found: '
+data.elements.length)
}
if(data.elements.length > 0) {
var winner = closest(track.location, data.elements)
var name = winner.node.tags['name'] ? winner.node.tags['name'] : "[no name]"
var direction = headingDirection(winner.bearing) + "(" + winner.bearing.toFixed(0) + "°)"
var info = name+" is "+winner.distance.toFixed(0)+"m "+direction+" "+
" https://www.openstreetmap.org/node/"+winner.node.id
bot.say(msg.params.channel, msg.params.nick+': '+info)
var route = 'https://maps.openrouteservice.org/directions?c=0&a='
route = route + [track.location.latitude, track.location.longitude, winner.node.lat, winner.node.lon].join(',')
//bot.say(msg.params.channel, msg.params.nick+': route '+route)
} else {
bot.say(msg.params.channel, msg.params.nick+' no '+cmd[2]+' in your vicinity.')
}
} else {
bot.say(msg.params.channel, 'no icecondor track for '+msg.params.nick)
}
})
} else {
bot.say(msg.params.channel, "!osm <tag_name>=<tag_value>")
}
}
var barcmd = /^!nearestbar/.exec(msg.params.message)
if(barcmd) {
bot.say(bot.admin_channel, "osm: "+msg.params.nick+' !nearestbar')
track(msg.params.nick, function(track){
if (track) {
var barfind = 'amenity~"^bar$|^pub$"'
var data = osmtag(barfind, track.location.latitude, track.location.longitude, 0.02)
if(data.elements.length > 0) {
var winner = closest(track.location, data.elements)
var name = winner.node.tags['name'] ? winner.node.tags['name'] : ""
var direction = winner.distance.toFixed(0) + ' meters ' + headingDirection(winner.bearing)
bot.say(msg.params.channel, msg.params.nick+': '+name+'. '+direction)
} else {
bot.say(msg.params.channel, msg.params.nick+": nothing within 0.02deg of you.")
}
}
})
}
var nearest = /^!nearest(\s+(.*))/.exec(msg.params.message)
if(nearest) {
track(msg.params.nick, function(track){
if (track) {
var query = 'amenity~"'+nearest[2]+'"'
bot.say(msg.params.channel, msg.params.nick+": "+query)
var data = osmtag(query, track.location.latitude, track.location.longitude, 0.02)
if(data.elements.length > 0) {
var winner = closest(track.location, data.elements)
var name = winner.node.tags['name'] ? winner.node.tags['name'] : ""
var direction = winner.distance.toFixed(0) + ' meters ' + headingDirection(winner.bearing)
var node_url = "https://www.openstreetmap.org/node/"+winner.node.id
bot.say(msg.params.channel, msg.params.nick+': '+name+'. '+direction+' '+node_url)
} else {
bot.say(msg.params.channel, msg.params.nick+": nothing within 0.02deg of you.")
}
}
})
}
}
}
function closest(me, nodes) {
var winner = {node: null, distance: null}
nodes.forEach(function(node){
var meDistance = pointDistance(me, {latitude: node.lat, longitude: node.lon})
if(!winner.node || meDistance < winner.distance) {
winner.node = node
winner.distance = meDistance
winner.bearing = segmentBearing(me, {latitude: node.lat, longitude: node.lon})
}
})
return winner
}
function osmtag(tag, lat, lng, delta) {
var url = 'http://overpass-api.de/api/interpreter'
var coords = [lat-delta, lng-delta, lat+delta, lng+delta]
var tagquery
if(tag.indexOf('=') > 0 || tag.indexOf('~') > 0) {
tagquery = tag
} else {
tagquery = "\"name\"~\""+tag+"\",i"
}
var overpass = '[out:json];' +
//'node('+coords.join(',')+')['+tagquery+'];'+
'(node('+coords.join(',')+')['+tagquery+'];'+
'way('+coords.join(',')+')['+tagquery+'];);'+
'(._;>;);'+
'out;'
//(node(45.5016018,-122.6960055,45.6216018,-122.57600549999999) [aeroway=aerodrome];
// way(45.5016018,-122.6960055,46.5216018,-122.57600549999999) [aeroway=aerodrome];);
//(._;>;);
bot.say(bot.admin_channel, overpass)
var resp = http.get({url:url+'?data='+encodeURIComponent(overpass)})
bot.say(bot.admin_channel, url+'?data='+encodeURIComponent(overpass))
if(resp.status != 200) {
bot.say(bot.admin_channel, "osm http "+resp.status)
}
var data = JSON.parse(resp.body)
var filtered = data.elements.filter(function(n){return n.tags})
bot.say(bot.admin_channel, "osm found "+data.elements.length+" nodes and "+filtered.length+" nodes with tags")
data.elements = filtered
return data
}
function track(username, cb) {
db.get('icecondor:user'+':'+username+':track', function(trackJson){
var track
if (trackJson) {
cb(JSON.parse(trackJson))
} else { cb() }
})
}
// 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
}
function headingDirection(degrees) {
var directions = ["north", "northeast", "east", "southeast", "south", "southwest", "west", "northwest"]
var anglewidth = 360 / directions.length
var halfwidth = anglewidth/2
for(var i=0; i < directions.length; i++) {
var j = i == 0 ? directions.length-1 : i-1
var min = (anglewidth*j)+halfwidth
var max = (anglewidth*i)+halfwidth
var inside
if(i==0) {
inside = degrees <= max || degrees > min
} else {
inside = degrees <= max && degrees > min
}
if(inside) return directions[i]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment