Places

H3 hexagons

H3 hexagon cells: the cell of a point at a resolution, its neighbours within k rings, its outline for a map, and distances between cells.

Version 1.0.0 · 6 functions · 1 crate

#geo#maps#hexagons#search#delivery

Functions

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

h3_cell(lat: float, lon: float, resolution: int) → stror an error

The id of the cell holding a point, as 15 hex digits (8a1969053247fff), at resolution 0 to 15: 7 is about 5 km², 9 about 0.1 km², 12 about 300 m².

h3_info(id: str) → anyor an error

About a cell: { id, resolution, lat, lon, area_km2, pentagon, parent }, the centre's coordinates, and its parent one resolution up (null at 0).

h3_disk(id: str, k: int) → listor an error

The cell and every cell within k steps of it (k 0 to 50): 1 gives the 7 of a ring, 2 gives 19. For "within about this far" searches with where cell in (...).

h3_boundary(id: str) → listor an error

The cell's outline, as [[lat, lon], ...] (6 corners, 5 for a pentagon, a few more where edges cross faces), for drawing on a map.

h3_distance(a: str, b: str) → intor an error

How many cell steps apart two cells of the same resolution are, or an error when they are too far apart to count (on another face of the grid).

h3_parent(id: str, resolution: int) → stror an error

The cell holding id at a coarser resolution (for rolling up counts from street to district level).

Get it

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

Or with curl:

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

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

main.qwe
use h3.qweruse h3_cell, h3_info from h3      // Quillo 0.6: only these

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

[dependencies]
h3o = "0.6"

Example

h3.qwe
// Couriers near an order, by H3 cell: each courier's cell is kept (in the database in a real app),// and an order looks in its cell's neighbourhood; plus a heat map of orders by district.////   quillo run h3.qwe      then open http://localhost:8080/near?lat=52.3702&lon=4.8952use "h3.qwer"let couriers = [    { name: "Ayla", lat: 52.3731, lon: 4.8922 }, { name: "Bram", lat: 52.3600, lon: 4.8850 },    { name: "Cas", lat: 52.3080, lon: 4.7600 },] | map((c) => merge(c, { cell: h3_cell(c.lat, c.lon, 9)? }))route GET /near ? { lat: float, lon: float } {    let here = h3_cell(lat, lon, 9)?    let nearby = h3_disk(here, 3)?    { cell: here, area_km2: round(h3_info(here)?.area_km2, 3), couriers: couriers | filter((c) => contains(nearby, c.cell)) | map((c) => { name: c.name, steps: h3_distance(here, c.cell)? }) }}route GET /heat {    let orders = [[52.3702, 4.8952], [52.3712, 4.8960], [52.3600, 4.8850], [52.3080, 4.7600], [52.3705, 4.8949]]    orders | map((o) => h3_parent(h3_cell(o[0], o[1], 9)?, 6)?) | group_by((c) => c) | values | map((cs) => len(cs)) | sort | reverse}route GET /shape = { corners: len(h3_boundary(h3_cell(52.3702, 4.8952, 9)?)?), bad: h3_cell(52.37, 4.89, 16).error }
$ quillo run h3.qwe$ curl 'localhost:8080/near?lat=52.3702&lon=4.8952'$ curl 'localhost:8080/heat'$ curl 'localhost:8080/shape'

Source

h3.qwer
//! H3 hexagons (Uber's global grid): the cell of a point at a resolution, its neighbours within k rings, its outline for a map, and the distance between cells. For heat maps, delivery zones, and "near me" by cell id in SQL.//! version: 1.0.0//!//! [dependencies]//! h3o = "0.6"use h3o::{CellIndex, LatLng, Resolution};use serde_json::{json, Value};use std::str::FromStr;fn res(r: i64) -> Result<Resolution, String> {    u8::try_from(r).ok().and_then(|r| Resolution::try_from(r).ok()).ok_or(format!("{} is not a resolution: 0 (continents) to 15 (about 1 m²)", r))}fn cell(id: &str) -> Result<CellIndex, String> {    CellIndex::from_str(id.trim()).map_err(|_| format!("`{}` is not an H3 cell id", id))}fn point(lat: f64, lon: f64) -> Result<LatLng, String> {    LatLng::new(lat, lon).map_err(|_| format!("{}, {} is not a place", lat, lon))}/// The id of the cell holding a point, as 15 hex digits (`8a1969053247fff`), at `resolution` 0 to 15: 7 is about 5 km², 9 about 0.1 km², 12 about 300 m².pub fn h3_cell(lat: f64, lon: f64, resolution: i64) -> Result<String, String> {    Ok(point(lat, lon)?.to_cell(res(resolution)?).to_string())}/// About a cell: `{ id, resolution, lat, lon, area_km2, pentagon, parent }`, the centre's coordinates, and its parent one resolution up (null at 0).pub fn h3_info(id: &str) -> Result<Value, String> {    let c = cell(id)?;    let ll = LatLng::from(c);    let parent = c.resolution().pred().and_then(|r| c.parent(r)).map(|p| p.to_string());    Ok(json!({        "id": c.to_string(), "resolution": u8::from(c.resolution()), "lat": ll.lat(), "lon": ll.lng(),        "area_km2": c.area_km2(), "pentagon": c.is_pentagon(), "parent": parent,    }))}/// The cell and every cell within `k` steps of it (k 0 to 50): 1 gives the 7 of a ring, 2 gives 19. For "within about this far" searches with `where cell in (...)`.pub fn h3_disk(id: &str, k: i64) -> Result<Vec<String>, String> {    let c = cell(id)?;    let k = u32::try_from(k.clamp(0, 50)).unwrap_or(0);    let cells: Vec<CellIndex> = c.grid_disk(k);    Ok(cells.into_iter().map(|c| c.to_string()).collect())}/// The cell's outline, as `[[lat, lon], ...]` (6 corners, 5 for a pentagon, a few more where edges cross faces), for drawing on a map.pub fn h3_boundary(id: &str) -> Result<Vec<Vec<f64>>, String> {    Ok(cell(id)?.boundary().iter().map(|p| vec![p.lat(), p.lng()]).collect())}/// How many cell steps apart two cells of the same resolution are, or an error when they are too far apart to count (on another face of the grid).pub fn h3_distance(a: &str, b: &str) -> Result<i64, String> {    let (a, b) = (cell(a)?, cell(b)?);    if a.resolution() != b.resolution() {        return Err("the cells are of different resolutions".into());    }    a.grid_distance(b).map(i64::from).map_err(|e| e.to_string())}/// The cell holding `id` at a coarser `resolution` (for rolling up counts from street to district level).pub fn h3_parent(id: &str, resolution: i64) -> Result<String, String> {    let c = cell(id)?;    c.parent(res(resolution)?).map(|p| p.to_string()).ok_or(format!("resolution {} is not coarser than the cell's", resolution))}
Thread
78Useful? Like it, or add to its thread below.

Thread 0 posts

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

No posts yet. The first one opens the thread.