Runtime API
The API endpoint is /proxy/api/runtime
. This endpoint allows arbitrary
Javascript scripts to run within the runtime of the Go code that queries the
Open Street Map data.
The runtime is a sandboxed custom environment, with the following restrictions:
- No network access
- No file access
- No npm packages -- no
import
orrequire
- Timeout 10 seconds
It supports the following:
- Typescript, but no type verification is done.
- Standard ECMAScript 5.1 features -- i.e. Regex, Date, JSON, etc.
Functions
This is the list of functions that are available to use in the javascript runtime.
declare const assert: Assert;
declare const colors: Colors;
declare const geo: Geo;
declare const params: Params;
declare const query: Query;
interface Properties {
[key: string]: string | number | boolean;
}
interface Feature {
type: string;
properties: Properties;
geometry: unknown;
}
interface Assert {
geoJSON(payload: unknown): void;
eq(value: boolean, message: string): void;
}
interface Colors {
pick(number): string;
}
interface Bound {
extend(number): Bound;
center(): Point;
intersects(bound: Bound): boolean;
}
interface BoundArray extends Array<Bound> {
asFeature(properties?: Properties): Feature;
asBound(): Bound;
}
interface Point {
asFeature(properties?: Properties): Feature;
asBound(): Bound;
lat(): number;
lon(): number;
}
interface Prefix {
name: string;
fullName: string;
minLat: number;
minLon: number;
maxLat: number;
maxLon: number;
}
interface Geo {
asPoint(lat: number, lon: number): Point;
asResults(...results: Result[]): ResultArray;
asBounds(...bounds: Bound[]): BoundArray;
rtree(): Tree;
}
interface Result {
asFeature(properties?: Properties): Feature;
name: string;
id: number;
minLat: number;
minLon: number;
maxLat: number;
maxLon: number;
tags: { [key: string]: string };
bound(): Bound;
}
interface Tree {
nearby(Bound, number): ResultArray;
within(Bound): ResultArray;
insert(Result): void;
}
interface ResultArray extends Array<Result> {
asTree(depth: number): Tree;
cluster(number): ResultArray;
overlap(
results: ResultArray,
originRadius: number,
neighborRadius: number,
count: number,
): ResultArray[];
}
interface Query {
union(...string): ResultArray;
execute(string): ResultArray;
areas(): Prefix[];
fromAddress(address: string): ResultArray;
}
interface Params {
[key: string]: string;
}
Examples
List of counties (via curl
)
The following script will return all the counties, their state, and center point of a the bounding box.
/// <reference path="../global.d.ts" />
const areas = query.areas();
const counties = areas.flatMap((area) => {
const results = query.execute(
`nwr[admin_level=6][boundary=administrative][name](area=${area.name})`,
);
return results.map((county) => {
const center = county.bound().center();
return {
name: county.tags.name,
lat: center.lat(),
lon: center.lon(),
state: area.name,
};
});
});
export { counties as payload };
This script can be applied via two methods a source
parameter in the query
string or the request body. If using the query string, Cloudflare will cache it
for 25 minutes (1500 seconds).
The above script can be used in a curl
command doing the following:
# create `script.js` of the above javascript
curl -X GET --data-urlencode "[email protected]" 'https://knowhere.live/proxy/api/runtime'
This will return a JSON payload in the format of:
This entire payload could be used for an auto complete client side. Rather that off loading all auto complete filtering to server side.
Find universities nearby each other
Let's find unique university campuses near each other.
/// <reference path="../global.d.ts" />
const areas = query.areas();
// find all the universities country wide
const allUnis = geo.asResults(
...areas.flatMap((area) => {
return query.execute(
`wr[amenity=university][name](area=${area.name})`,
);
}),
);
const radius = 500; // meters
const overlap = 3000; // meters
// find multiple marked universities near each other
// helpful when there are lots of buildings on the campus
// `cluster` always returns the largest area
const clustered = allUnis.cluster(radius);
// find clustered items that overlap each other
// this should find campuses that are near each other
const grouped = clustered.overlap(clustered, overlap, 0, 3);
// lets generate the GeoJSON
const payload = {
type: "FeatureCollection",
features: grouped.flatMap((entries, index) => {
const features = entries.flatMap((entry) => {
const feature = entry.asFeature({
"marker-color": colors.pick(index),
index: index,
});
return feature;
});
const bounds = geo.asBounds(
...entries.map((entry) => entry.bound().extend(overlap)),
);
return features.concat(
[
bounds.asFeature({
"fill": colors.pick(index),
"fill-opacity": 0.2,
}),
],
);
}),
};
export { payload };
Find neighborhood areas near areas
This finds neighborhoods within Colorado that are within driving distance of Costco and walking distance from a highschool and coffee shop.
/// <reference path="../global.d.ts" />
const keywords = [
{
radius: 5000,
results: query.execute(`nwr[name=~Costco](area=colorado)`),
},
{
radius: 1000,
results: query.execute(
`nwr[amenity=cafe][name][name!~Starbucks](area=colorado)`,
),
},
{
radius: 5000,
results: query.execute(`nwr[amenity=school][name](area=colorado)`),
},
];
keywords.sort((a, b) => a.results.length - b.results.length);
const neighbors = new Map<number, Map<number, Result>>();
const cluster = keywords[0].results.cluster(500);
cluster.forEach((entry) => {
neighbors.set(entry.id, new Map());
});
const expectedNeighbors = 2;
keywords.slice(1).forEach((keyword) => {
const grouped = cluster.overlap(
keyword.results,
keywords[0].radius,
keyword.radius,
expectedNeighbors - 1,
);
grouped.forEach((values) => {
values.forEach((value) =>
neighbors.get(values[0].id)?.set(value.id, value)
);
});
});
const payload = {
type: "FeatureCollection",
features: [...neighbors.values()].flatMap((set, index) => {
const entries = [...set.values()];
if (entries.length !== keywords.length) {
return;
}
const features = entries.flatMap((entry, index) => {
const color = colors.pick(index);
const feature = entry.asFeature({
"marker-color": color,
index: index,
});
return feature;
});
const bounds = geo.asBounds(
...entries.map((entry, index) =>
entry.bound().extend(keywords[index].radius)
),
);
return features.concat(
[
bounds.asFeature({
"fill": colors.pick(index),
"fill-opacity": 0.5,
}),
],
);
}).filter(Boolean),
};
assert.geoJSON(payload);
export { payload };