osm-restaurants: implement
This commit is contained in:
106
osm-restaurants/osm-restaurants.js
Executable file
106
osm-restaurants/osm-restaurants.js
Executable file
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env node
|
||||
const crypto = require("crypto");
|
||||
const fs = require("fs");
|
||||
const os = require("os");
|
||||
const path = require("path");
|
||||
|
||||
const axios = require("axios");
|
||||
const openingHours = require("opening_hours");
|
||||
const yargs = require("yargs");
|
||||
|
||||
const CACHE_AGE = 12 * 60 * 60 * 1000;
|
||||
|
||||
const md5 = (text) => crypto.createHash("md5").update(text).digest("hex");
|
||||
|
||||
const getCachePath = (query) => {
|
||||
const cacheDirectory =
|
||||
process.env.CACHE_DIR ||
|
||||
path.join(os.homedir(), ".cache", "osm-restaurants");
|
||||
if (!fs.existsSync(cacheDirectory)) fs.mkdirSync(cacheDirectory);
|
||||
return path.join(cacheDirectory, md5(query) + ".json");
|
||||
};
|
||||
|
||||
const randomElement = (array) =>
|
||||
array[Math.floor(array.length * Math.random())];
|
||||
|
||||
const argv = yargs
|
||||
.option("checkOpen", {
|
||||
alias: "o",
|
||||
default: true,
|
||||
type: "boolean",
|
||||
description: "Only show restaurants that are open",
|
||||
})
|
||||
.option("radius", {
|
||||
alias: "r",
|
||||
description: "Radius around the location (metres)",
|
||||
type: "number",
|
||||
default: 500,
|
||||
})
|
||||
.option("center", {
|
||||
alias: "c",
|
||||
description: "The OSM ID to search around",
|
||||
type: "number",
|
||||
demandOption: true,
|
||||
})
|
||||
.option("endpoint", {
|
||||
description: "The Overpass API endpoint",
|
||||
type: "string",
|
||||
default: "http://overpass-api.de/api/interpreter",
|
||||
})
|
||||
.option("country", {
|
||||
description: "The country you are in (two letter code)",
|
||||
type: "string",
|
||||
default: "de",
|
||||
})
|
||||
.help()
|
||||
.alias("help", "h").argv;
|
||||
|
||||
const overpassQuery = `
|
||||
[out:json];
|
||||
node(id:${argv.center})->.center;
|
||||
(
|
||||
node(around.center:'${argv.radius}')[amenity=fast_food];
|
||||
node(around.center:'${argv.radius}')[amenity=restaurant];
|
||||
);
|
||||
out;
|
||||
`;
|
||||
|
||||
const restaurantOpen = (restaurant) =>
|
||||
"opening_hours" in restaurant.tags
|
||||
? new openingHours(restaurant.tags.opening_hours, {
|
||||
lat: restaurant.lat,
|
||||
lon: restaurant.lon,
|
||||
address: {
|
||||
country_code:
|
||||
"addr:country" in restaurant.tags
|
||||
? restaurant.tags["addr:country"].toLowerCase()
|
||||
: argv.country,
|
||||
},
|
||||
}).getState()
|
||||
: true;
|
||||
|
||||
const withRestaurants = (callback) => {
|
||||
const cachePath = getCachePath(overpassQuery);
|
||||
if (
|
||||
fs.existsSync(cachePath) &&
|
||||
new Date() - fs.statSync(cachePath).mtime > CACHE_AGE
|
||||
)
|
||||
return JSON.parse(fs.readFileSync(cachePath));
|
||||
else
|
||||
return axios
|
||||
.post(argv.endpoint, overpassQuery)
|
||||
.then((response) => {
|
||||
const restaurants = response.data.elements;
|
||||
fs.writeFileSync(cachePath, JSON.stringify(restaurants));
|
||||
callback(restaurants);
|
||||
})
|
||||
.catch(console.error);
|
||||
};
|
||||
|
||||
withRestaurants((restaurants) => {
|
||||
const randomOpenRestaurant = randomElement(
|
||||
argv.checkOpen ? restaurants.filter(restaurantOpen) : restaurants
|
||||
);
|
||||
|
||||
console.log(JSON.stringify(randomOpenRestaurant));
|
||||
});
|
||||
Reference in New Issue
Block a user