Places

Places

Distances on the globe, bounding boxes for near-me queries in SQL, and geohashes to group points.

Version 1.0.0 · 5 functions · 1 crate

#maps#distance#geohash#search

Functions

What use geo.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use distance_km, bounding_box from geo (Quillo 0.6).

distance_km(lat1: float, lon1: float, lat2: float, lon2: float) → float

How far apart two points are, in kilometres, along the globe (haversine).

bounding_box(lat: float, lon: float, radius_km: float) → any

{ min_lat, max_lat, min_lon, max_lon } around a point: where lat between ? and ? and lon between ? and ? finds the candidates, distance_km the exact ones.

geohash_encode(lat: float, lon: float, precision: int) → stror an error

The geohash of a point, precision characters long (5 is about 5 km, 7 about 150 m).

geohash_decode(hash: str) → anyor an error

The centre of a geohash's cell: { lat, lon }.

geohash_neighbors(hash: str) → listor an error

The eight cells around a geohash's, to search a cell and its neighbours.

Get it

$ quillo install geo               # geo.qwer into this folder$ quillo install geo --example     # and its example, geo.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/geo.qwer$ curl -O https://quillolang.org/plugins/files/geo.qwe

Then, in your program, all of it or only what you call:

main.qwe
use geo.qweruse distance_km, bounding_box from geo      // Quillo 0.6: only these

It uses this crate, which cargo fetches the first time the plugin is built:

[dependencies]
geohash = "0.13"

Example

geo.qwe
// Shops near a visitor, nearest first.////   quillo run geo.qwe      then open http://localhost:8080/near?lat=52.37&lon=4.89use "geo.qwer"let shops = [    { name: "Centraal", lat: 52.3791, lon: 4.9003 },    { name: "Zuid", lat: 52.3389, lon: 4.8723 },    { name: "Utrecht", lat: 52.0907, lon: 5.1214 },]route GET /near ? { lat: float, lon: float } {    let box = bounding_box(lat, lon, 10.0)    shops        | filter((s) => s.lat >= box.min_lat && s.lat <= box.max_lat && s.lon >= box.min_lon && s.lon <= box.max_lon)        | map((s) => { name: s.name, km: round(distance_km(lat, lon, s.lat, s.lon), 2), cell: geohash_encode(s.lat, s.lon, 6)? })        | sort_by((s) => s.km)}
$ quillo run geo.qwe$ curl 'localhost:8080/near?lat=52.37&lon=4.89'

Source

geo.qwer
//! Places: distances on the globe, bounding boxes for "near me" queries in SQL, and geohashes for grouping points.//! version: 1.0.0//!//! [dependencies]//! geohash = "0.13"use serde_json::{json, Value};const EARTH_KM: f64 = 6371.0088;/// How far apart two points are, in kilometres, along the globe (haversine).pub fn distance_km(lat1: f64, lon1: f64, lat2: f64, lon2: f64) -> f64 {    let (p1, p2) = (lat1.to_radians(), lat2.to_radians());    let (dp, dl) = ((lat2 - lat1).to_radians(), (lon2 - lon1).to_radians());    let a = (dp / 2.0).sin().powi(2) + p1.cos() * p2.cos() * (dl / 2.0).sin().powi(2);    2.0 * EARTH_KM * a.sqrt().asin()}/// `{ min_lat, max_lat, min_lon, max_lon }` around a point: `where lat between ? and ? and lon between ? and ?` finds the candidates, `distance_km` the exact ones.pub fn bounding_box(lat: f64, lon: f64, radius_km: f64) -> Value {    let dlat = (radius_km / EARTH_KM).to_degrees();    let dlon = (radius_km / (EARTH_KM * lat.to_radians().cos().abs().max(1e-6))).to_degrees();    json!({ "min_lat": (lat - dlat).max(-90.0), "max_lat": (lat + dlat).min(90.0), "min_lon": (lon - dlon).max(-180.0), "max_lon": (lon + dlon).min(180.0) })}/// The geohash of a point, `precision` characters long (5 is about 5 km, 7 about 150 m).pub fn geohash_encode(lat: f64, lon: f64, precision: i64) -> Result<String, String> {    geohash::encode(geohash::Coord { x: lon, y: lat }, precision.clamp(1, 12) as usize).map_err(|e| e.to_string())}/// The centre of a geohash's cell: `{ lat, lon }`.pub fn geohash_decode(hash: &str) -> Result<Value, String> {    let (c, _, _) = geohash::decode(hash).map_err(|e| e.to_string())?;    Ok(json!({ "lat": c.y, "lon": c.x }))}/// The eight cells around a geohash's, to search a cell and its neighbours.pub fn geohash_neighbors(hash: &str) -> Result<Vec<String>, String> {    let n = geohash::neighbors(hash).map_err(|e| e.to_string())?;    Ok(vec![n.n, n.ne, n.e, n.se, n.s, n.sw, n.w, n.nw])}
Thread
79Useful? Like it, or add to its thread below.

Thread 0 posts

On Places: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.