Reverse geocoding
The nearest town or city to a point, offline, from about 150,000 places with their region and country code.
Version 1.0.0 · 3 functions · 1 crate
Functions
What use reverse_geocode.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use reverse_geocode, reverse_geocode_many from reverse_geocode (Quillo 0.6).
reverse_geocode(lat: float, lon: float) → anyor an errorThe nearest place to a point: { city, region, district, country_code, lat, lon, km }, km how far the point is from it. Far out at sea it is still the nearest one: check km.
reverse_geocode_many(points: list) → listor an errorThe nearest place to each of many points [[lat, lon], ...], in order (at most 100,000).
reverse_geocode_label(lat: float, lon: float, near_km: float) → stror an errorA short label for a point, "Utrecht, Utrecht, NL", or "near Utrecht, NL" when it is more than near_km away from the place.
Get it
$ quillo install reverse_geocode # reverse_geocode.qwer into this folder$ quillo install reverse_geocode --example # and its example, reverse_geocode.qweOr with curl:
$ curl -O https://quillolang.org/plugins/files/reverse_geocode.qwer$ curl -O https://quillolang.org/plugins/files/reverse_geocode.qweThen, in your program, all of it or only what you call:
use reverse_geocode.qweruse reverse_geocode, reverse_geocode_many from reverse_geocode // Quillo 0.6: only theseIt uses this crate, which cargo fetches the first time the plugin is built:
reverse_geocoder = "4"Example
// Check-ins labelled with the town they were made in, from the phone's coordinates.//// quillo run reverse_geocode.qwe then open http://localhost:8080/where?lat=52.0907&lon=5.1214use "reverse_geocode.qwer"route GET /where ? { lat: float, lon: float } = reverse_geocode(lat, lon)?route GET /checkins { let checkins = [{ user: "ada", at: [48.8584, 2.2945] }, { user: "bas", at: [35.6595, 139.7005] }, { user: "cleo", at: [-33.8568, 151.2153] }] let places = reverse_geocode_many(checkins | map((c) => c.at))? zip(checkins, places) | map((p) => `${p[0].user} in ${p[1].city}, ${p[1].country_code}`)}route GET /label = { city: reverse_geocode_label(52.0907, 5.1214, 10.0)?, sea: reverse_geocode_label(54.0, 3.0, 10.0)? }$ quillo run reverse_geocode.qwe$ curl 'localhost:8080/where?lat=52.0907&lon=5.1214'$ curl 'localhost:8080/checkins'$ curl 'localhost:8080/label'Source
//! The nearest town or city to a point, offline: about 150,000 places of 1,000 people or more (GeoNames), with their region and country code. For "posted from Utrecht, NL", a photo's place, or a form's default country.//! version: 1.0.0//!//! [dependencies]//! reverse_geocoder = "4"use serde_json::{json, Value};use std::sync::OnceLock;/// The places, loaded once (the first call takes a moment; they stay in memory, about 40 MB).fn geocoder() -> &'static reverse_geocoder::ReverseGeocoder { static G: OnceLock<reverse_geocoder::ReverseGeocoder> = OnceLock::new(); G.get_or_init(reverse_geocoder::ReverseGeocoder::new)}fn 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 * 6371.0088 * a.sqrt().asin()}fn near(lat: f64, lon: f64) -> Result<Value, String> { if !(-90.0..=90.0).contains(&lat) || !(-180.0..=180.0).contains(&lon) { return Err(format!("{}, {} is not a place", lat, lon)); } let r = geocoder().search((lat, lon)).record; Ok(json!({ "city": r.name, "region": r.admin1, "district": r.admin2, "country_code": r.cc, "lat": r.lat, "lon": r.lon, "km": (km(lat, lon, r.lat, r.lon) * 100.0).round() / 100.0, }))}/// The nearest place to a point: `{ city, region, district, country_code, lat, lon, km }`, `km` how far the point is from it. Far out at sea it is still the nearest one: check `km`.pub fn reverse_geocode(lat: f64, lon: f64) -> Result<Value, String> { near(lat, lon)}/// The nearest place to each of many points `[[lat, lon], ...]`, in order (at most 100,000).pub fn reverse_geocode_many(points: Vec<Vec<f64>>) -> Result<Vec<Value>, String> { if points.len() > 100_000 { return Err("at most 100,000 points at once".into()); } points.iter().map(|p| if p.len() == 2 { near(p[0], p[1]) } else { Err("each point is [lat, lon]".into()) }).collect()}/// A short label for a point, "Utrecht, Utrecht, NL", or "near Utrecht, NL" when it is more than `near_km` away from the place.pub fn reverse_geocode_label(lat: f64, lon: f64, near_km: f64) -> Result<String, String> { let p = near(lat, lon)?; let city = p["city"].as_str().unwrap_or(""); let region = p["region"].as_str().unwrap_or(""); let cc = p["country_code"].as_str().unwrap_or(""); let base = if region.is_empty() || region == city { format!("{}, {}", city, cc) } else { format!("{}, {}, {}", city, region, cc) }; Ok(if p["km"].as_f64().unwrap_or(0.0) > near_km { format!("near {}, {}", city, cc) } else { base })}Related
maidenhead.qwer · 1.0.0Maidenhead grid locators as radio amateurs use them (FN31pr, JN58td): from a latitude and longitude at 2 to 10 characters, back to the square's centre and corners, and the distance and bearing between two.7 views · 8 downloadsSpatial index spatial_index.qwer · 1.0.0An R-tree of places in memory: the nearest ones to a point, all within a distance, those in a map's view, and markers clustered for it.6 views · 8 downloadsCoordinates coordinates.qwer · 1.0.0Coordinates as people write them, such as 52°22'N 4°53'E or N 52° 22.2', read as one, written as degrees-minutes-seconds, and converted to and from UTM.7 views · 8 downloadsH3 hexagons h3.qwer · 1.0.0H3 hexagon cells: the cell of a point at a resolution, its neighbours within k rings, its outline for a map, and distances between cells.7 views · 8 downloadsKML and KMZ kml.qwer · 1.0.0KML and KMZ files from Google Earth and My Maps read to GeoJSON, placemarks with their names, descriptions, folders and extended data, and GeoJSON written back as KML or KMZ.7 views · 8 downloadsMap clusters map_clusters.qwer · 1.0.0Map markers clustered by zoom as Supercluster does: thousands of points loaded once, then for each view and zoom the clusters with their counts and the single points, a cluster's points, and the zoom where it opens.7 views · 7 downloadsThread 0 posts
On Reverse geocoding: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.