Created
February 9, 2019 15:28
-
-
Save olegam/901654d0a85f495c53647f1b1bfadc0f to your computer and use it in GitHub Desktop.
Scriptable app script to find closest scooter in Copenhagen
This file contains 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
let googleApiKey = '' // Insert your own key for Google Maps + Directions JS SDK here | |
let copenhagenBounds = {minLat: 55.5, minLong: 12.5, maxLat: 55.8, maxLong: 12.7} | |
let copenhagenCenter = {lat: copenhagenBounds.maxLat - copenhagenBounds.minLat, lng: copenhagenBounds.maxLong - copenhagenBounds.minLong} | |
let getLocation = Location.current() | |
// const getLocation = new Promise(resolve => resolve({latitude: 55.7, longitude: 12.5})) // hardcoded location resolver for airplane debug use | |
const [allTIERScooters, allVOIScooters, myLocation] = await Promise.all([getTIERScooters(copenhagenBounds), getVOIScooters(copenhagenBounds), getLocation]) | |
console.log('My location: ' + JSON.stringify(myLocation)) | |
if (allTIERScooters.length + allVOIScooters.length <= 0) { | |
let message = 'No scooters available right now' | |
console.log(message) | |
if (config.runsWithSiri) { | |
Speech.speak(message) | |
} | |
return | |
} | |
let tierScooters = sortScootersByDistance(allTIERScooters, myLocation) | |
let voiScooters = sortScootersByDistance(allVOIScooters, myLocation) | |
console.log('Closest TIER scooter: ' + JSON.stringify(tierScooters[0])) | |
console.log('Closest VOI scooter: ' + JSON.stringify(voiScooters[0])) | |
let allScootersByDistance = [...tierScooters, ...voiScooters].sort((a, b) => a.distance - b.distance) | |
let closest = allScootersByDistance[0] | |
let addressMessagePart = closest.address ? ` on ${closest.address}` : '' | |
let resultMessage = `The closest scooter is from ${closest.brand} and is ${Math.round(closest.distance)} m away${addressMessagePart}.` | |
console.log(resultMessage) | |
if (config.runsWithSiri) { | |
Speech.speak(resultMessage) | |
} | |
showMap(allScootersByDistance.slice(0, 10), myLocation) | |
async function getTIERScooters(boundingBox) { | |
let url = `https://tier.frontend.fleetbird.eu/api/prod/v1.06/map/cars/?lat1=${boundingBox.minLat}&lat2=${boundingBox.maxLat}&lon1=${boundingBox.minLong}&lon2=${boundingBox.maxLong}` | |
let request = new Request(url) | |
let scooters = await request.loadJSON() | |
console.log('Number of available TIER scooters: ' + scooters.length) | |
return scooters.map(s => {s.brand = 'TIER'; return s}) | |
} | |
async function getVOIScooters(boundingBox) { | |
let url = `https://api.voiapp.io/v1/vehicle/status/ready?la=${boundingBox.minLat}&lo=${boundingBox.minLong}` | |
let request = new Request(url) | |
let worldwideVOIScooters = await request.loadJSON() | |
console.log('Number of worldwide VOI scooters: ' + worldwideVOIScooters.length) | |
let localScooters = worldwideVOIScooters.filter(s => withinBoundingBox(boundingBox, s.location[0], s.location[1])) | |
console.log('Number of local VOI scooters: ' + localScooters.length) | |
return localScooters.map(s => { | |
s.lat = s.location[0] // store location in same format as TIER | |
s.lon = s.location[1] | |
s.brand = 'VOI' | |
return s | |
}) | |
} | |
function sortScootersByDistance(scooters, toLocation) { | |
return scooters.map((s) => {s.distance = 1000 * distance(s.lat, s.lon, toLocation.latitude, toLocation.longitude, 'K'); return s}).sort((a, b) => a.distance - b.distance) | |
} | |
function withinBoundingBox(boundingBox, latitude, longitude) { | |
return latitude > boundingBox.minLat && latitude < boundingBox.maxLat && longitude > boundingBox.minLong && longitude < boundingBox.maxLong | |
} | |
function showMap(nearbyScooters, myLocation) { | |
let closest = nearbyScooters[0] | |
let html = ` | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta name="viewport" content="initial-scale=1.0"> | |
<meta charset="utf-8"> | |
<style> | |
#map { | |
height: 100%; | |
} | |
html, | |
body { | |
height: 100%; | |
margin: 0; | |
padding: 0; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="map"></div> | |
<script> | |
var map; | |
function initMap() { | |
var myLocation = { | |
lat: ${myLocation.latitude}, | |
lng: ${myLocation.longitude} | |
} | |
var closestLocation = { | |
lat: ${closest.lat}, | |
lng: ${closest.lon} | |
} | |
map = new google.maps.Map(document.getElementById('map'), { | |
center: myLocation, | |
zoom: 14, | |
disableDefaultUI: true | |
}); | |
var voiScale = 0.3 | |
var voiIcon = { | |
url: "https://s3-eu-west-1.amazonaws.com/shape-website-images/scriptable/voi-marker%402x.png", | |
scaledSize: new google.maps.Size(132 * voiScale, 160 * voiScale), | |
origin: new google.maps.Point(0, 0), | |
anchor: new google.maps.Point(66 * voiScale, 152 * voiScale) | |
}; | |
var tierIcon = { | |
url: "https://s3-eu-west-1.amazonaws.com/shape-website-images/scriptable/tier-marker%402x.png", | |
scaledSize: new google.maps.Size(50, 50), | |
origin: new google.maps.Point(0, 0), | |
anchor: new google.maps.Point(25, 48) | |
}; | |
new google.maps.Marker({ | |
map: map, | |
position: myLocation, | |
icon: 'http://www.robotwoods.com/dev/misc/bluecircle.png' | |
}); | |
var nearbyScooters = ${JSON.stringify(nearbyScooters)} | |
nearbyScooters.forEach(s => new google.maps.Marker({ | |
map: map, | |
position: { | |
lat: s.lat, | |
lng: s.lon | |
}, | |
icon: s.brand === 'VOI' ? voiIcon : tierIcon | |
})) | |
var directionsService = new google.maps.DirectionsService; | |
var directionsDisplay = new google.maps.DirectionsRenderer({ | |
suppressMarkers: true | |
}); | |
directionsDisplay.setMap(map); | |
directionsService.route({ | |
origin: myLocation, | |
destination: closestLocation, | |
travelMode: 'WALKING' | |
}, function (response, status) { | |
if (status === 'OK') { | |
directionsDisplay.setDirections(response); | |
} | |
}); | |
} | |
</script> | |
<script src="https://maps.googleapis.com/maps/api/js?key=${googleApiKey}&callback=initMap" async defer></script> | |
</body> | |
</html> | |
` | |
let fm = FileManager.iCloud() | |
let dir = fm.documentsDirectory() | |
let path = fm.joinPath(dir, 'scriptable_closest_scooter_map.html') | |
fm.writeString(path, html) | |
WebView.loadFile(path, new Size(400, 400)) | |
} | |
// copied from somewhere on interwebs | |
function distance(lat1, lon1, lat2, lon2, unit) { | |
if ((lat1 == lat2) && (lon1 == lon2)) { | |
return 0; | |
} | |
else { | |
var radlat1 = Math.PI * lat1/180; | |
var radlat2 = Math.PI * lat2/180; | |
var theta = lon1-lon2; | |
var radtheta = Math.PI * theta/180; | |
var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta); | |
if (dist > 1) { | |
dist = 1; | |
} | |
dist = Math.acos(dist); | |
dist = dist * 180/Math.PI; | |
dist = dist * 60 * 1.1515; | |
if (unit=="K") { dist = dist * 1.609344 } | |
if (unit=="N") { dist = dist * 0.8684 } | |
return dist; | |
} | |
} |
Can you run this script in a normal browser?
Could you post a working URL for the tier api with only gps coordinates?(not your variables)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Demo video https://twitter.com/olegam/status/1094249610304995329