Hexbin map
Points binned into hexagons over projected map shapes, each hexagon shaded by how many points fall inside. Reads density where individual dots would pile up.
| Hexagon | Points | Total | Includes |
|---|---|---|---|
| Hexagon 1 | 1 | 3 | Aberdeen site 1 |
| Hexagon 2 | 1 | 2 | Aberdeen site 2 |
| Hexagon 3 | 1 | 6 | Aberdeen site 3 |
| Hexagon 4 | 1 | 5 | Glasgow site 10 |
| Hexagon 5 | 2 | 10 | Edinburgh site 3, Edinburgh site 4 |
| Hexagon 6 | 2 | 7 | Edinburgh site 5, Edinburgh site 8 |
| Hexagon 7 | 2 | 7 | Glasgow site 4, Glasgow site 9 |
| Hexagon 8 | 3 | 6 | Glasgow site 3, Glasgow site 5, Glasgow site 6 |
| Hexagon 9 | 3 | 16 | Glasgow site 1, Glasgow site 8, Glasgow site 12 |
| Hexagon 10 | 3 | 13 | Edinburgh site 2, Edinburgh site 6, Edinburgh site 9 |
| Hexagon 11 | 1 | 3 | Edinburgh site 7 |
| Hexagon 12 | 2 | 9 | Glasgow site 2, Glasgow site 11 |
| Hexagon 13 | 1 | 4 | Glasgow site 7 |
| Hexagon 14 | 1 | 3 | Edinburgh site 1 |
| Hexagon 15 | 1 | 5 | Newcastle site 2 |
| Hexagon 16 | 1 | 5 | Newcastle site 1 |
| Hexagon 17 | 1 | 1 | Newcastle site 3 |
| Hexagon 18 | 1 | 6 | Newcastle site 5 |
| Hexagon 19 | 1 | 7 | Newcastle site 6 |
| Hexagon 20 | 1 | 2 | Newcastle site 4 |
| Hexagon 21 | 2 | 10 | Belfast site 2, Belfast site 3 |
| Hexagon 22 | 1 | 2 | Belfast site 1 |
| Hexagon 23 | 1 | 6 | Belfast site 4 |
| Hexagon 24 | 1 | 3 | Belfast site 5 |
| Hexagon 25 | 2 | 6 | Leeds site 6, Leeds site 7 |
| Hexagon 26 | 1 | 1 | Leeds site 9 |
| Hexagon 27 | 3 | 8 | Manchester site 1, Manchester site 9, Manchester site 13 |
| Hexagon 28 | 5 | 26 | Manchester site 5, Manchester site 6, Manchester site 18, Leeds site 4, Leeds site 5 |
| Hexagon 29 | 2 | 9 | Leeds site 2, Leeds site 3 |
| Hexagon 30 | 3 | 11 | Leeds site 1, Leeds site 8, Leeds site 10 |
| Hexagon 31 | 1 | 4 | Manchester site 12 |
| Hexagon 32 | 2 | 7 | Manchester site 8, Manchester site 10 |
| Hexagon 33 | 4 | 19 | Manchester site 2, Manchester site 11, Manchester site 14, Manchester site 16 |
| Hexagon 34 | 2 | 4 | Manchester site 4, Manchester site 17 |
| Hexagon 35 | 3 | 14 | Manchester site 3, Manchester site 7, Manchester site 15 |
| Hexagon 36 | 2 | 6 | Birmingham site 5, Birmingham site 6 |
| Hexagon 37 | 1 | 3 | Birmingham site 1 |
| Hexagon 38 | 2 | 9 | Norwich site 2, Norwich site 4 |
| Hexagon 39 | 3 | 9 | Birmingham site 8, Birmingham site 10, Birmingham site 12 |
| Hexagon 40 | 3 | 14 | Birmingham site 2, Birmingham site 3, Birmingham site 4 |
| Hexagon 41 | 1 | 6 | Norwich site 1 |
| Hexagon 42 | 1 | 2 | Norwich site 3 |
| Hexagon 43 | 3 | 12 | Birmingham site 9, Birmingham site 13, Birmingham site 16 |
| Hexagon 44 | 3 | 12 | Birmingham site 7, Birmingham site 11, Birmingham site 15 |
| Hexagon 45 | 1 | 3 | Birmingham site 14 |
| Hexagon 46 | 2 | 8 | London site 19, London site 21 |
| Hexagon 47 | 2 | 12 | London site 10, London site 40 |
| Hexagon 48 | 1 | 4 | Cardiff site 2 |
| Hexagon 49 | 4 | 13 | Bristol site 1, Bristol site 2, Bristol site 5, Bristol site 9 |
| Hexagon 50 | 1 | 6 | Bristol site 7 |
| Hexagon 51 | 7 | 31 | London site 16, London site 24, London site 26, London site 28, London site 32, London site 38, London site 39 |
| Hexagon 52 | 13 | 56 | London site 2, London site 3, London site 5, London site 12, London site 15, London site 17, London site 18, London site 25, London site 27, London site 30, London site 33, London site 36, London site 37 |
| Hexagon 53 | 5 | 21 | London site 7, London site 13, London site 14, London site 23, London site 29 |
| Hexagon 54 | 2 | 8 | Cardiff site 1, Cardiff site 5 |
| Hexagon 55 | 2 | 8 | Bristol site 4, Bristol site 6 |
| Hexagon 56 | 3 | 19 | Bristol site 3, Cardiff site 3, Cardiff site 4 |
| Hexagon 57 | 1 | 5 | Bristol site 8 |
| Hexagon 58 | 1 | 3 | London site 6 |
| Hexagon 59 | 6 | 23 | London site 8, London site 9, London site 20, London site 22, London site 31, London site 35 |
| Hexagon 60 | 4 | 16 | London site 1, London site 4, London site 11, London site 34 |
Usage
use hydroxy_leptos::hexbin_map_chart::*;
use leptos::prelude::*;
/// A rough outline of Great Britain and Northern Ireland as (longitude, latitude).
fn uk() -> Vec<Region> {
let gb = [
(-5.7, 50.0),
(-3.0, 50.6),
(1.4, 51.2),
(1.7, 52.7),
(0.3, 53.4),
(-0.1, 54.5),
(-1.6, 55.6),
(-2.1, 57.7),
(-3.0, 58.6),
(-5.0, 58.6),
(-6.2, 56.8),
(-5.6, 55.3),
(-4.9, 54.8),
(-3.4, 54.9),
(-3.0, 53.4),
(-4.6, 53.3),
(-4.2, 52.3),
(-5.3, 51.7),
(-3.3, 51.4),
];
let ni = [(-8.2, 54.4), (-7.3, 55.3), (-6.0, 55.2), (-5.4, 54.4), (-6.3, 54.0), (-7.6, 54.1)];
vec![Region::new("gb", "Great Britain", f64::NAN, gb), Region::new("ni", "Northern Ireland", f64::NAN, ni)]
}
/// Charging sites scattered around towns: more around bigger towns, each with a few chargers.
fn sites() -> Vec<GeoPoint> {
let towns = [
("London", (-0.13, 51.51), 40),
("Birmingham", (-1.89, 52.49), 16),
("Manchester", (-2.24, 53.48), 18),
("Leeds", (-1.55, 53.80), 10),
("Glasgow", (-4.25, 55.86), 12),
("Edinburgh", (-3.19, 55.95), 9),
("Bristol", (-2.59, 51.45), 9),
("Cardiff", (-3.18, 51.48), 5),
("Newcastle", (-1.61, 54.97), 6),
("Norwich", (1.30, 52.63), 4),
("Aberdeen", (-2.09, 57.15), 3),
("Belfast", (-5.93, 54.60), 5),
];
// A fixed pseudo-random spread, so the demo looks the same every time.
let mut seed = 7_u32;
let mut next = move || {
seed = seed.wrapping_mul(1_103_515_245).wrapping_add(12_345);
f64::from(seed >> 8) / f64::from(1_u32 << 24) - 0.5
};
towns
.iter()
.flat_map(|&(town, (lon, lat), n)| {
(0..n)
.map(|k| {
GeoPoint::new(
format!("{town} site {}", k + 1),
(lon + next() * 1.2, lat + next() * 0.7),
(4.0 + next() * 6.0).round(),
)
})
.collect::<Vec<_>>()
})
.collect()
}
#[component]
pub fn Demo() -> impl IntoView {
view! {
<HexbinMapChart points=sites() regions=uk() label="Public charging sites" radius=14.0 projection=Projection::Mercator />
}
}use dioxus::prelude::*;
use hydroxy_dioxus::hexbin_map_chart::{GeoPoint, HexbinMapChart, Projection, Region};
/// A rough outline of Great Britain and Northern Ireland as (longitude, latitude).
fn uk() -> Vec<Region> {
let gb = [
(-5.7, 50.0),
(-3.0, 50.6),
(1.4, 51.2),
(1.7, 52.7),
(0.3, 53.4),
(-0.1, 54.5),
(-1.6, 55.6),
(-2.1, 57.7),
(-3.0, 58.6),
(-5.0, 58.6),
(-6.2, 56.8),
(-5.6, 55.3),
(-4.9, 54.8),
(-3.4, 54.9),
(-3.0, 53.4),
(-4.6, 53.3),
(-4.2, 52.3),
(-5.3, 51.7),
(-3.3, 51.4),
];
let ni = [(-8.2, 54.4), (-7.3, 55.3), (-6.0, 55.2), (-5.4, 54.4), (-6.3, 54.0), (-7.6, 54.1)];
vec![Region::new("gb", "Great Britain", f64::NAN, gb), Region::new("ni", "Northern Ireland", f64::NAN, ni)]
}
/// Charging sites scattered around towns: more around bigger towns, each with a few chargers.
fn sites() -> Vec<GeoPoint> {
let towns = [
("London", (-0.13, 51.51), 40),
("Birmingham", (-1.89, 52.49), 16),
("Manchester", (-2.24, 53.48), 18),
("Leeds", (-1.55, 53.80), 10),
("Glasgow", (-4.25, 55.86), 12),
("Edinburgh", (-3.19, 55.95), 9),
("Bristol", (-2.59, 51.45), 9),
("Cardiff", (-3.18, 51.48), 5),
("Newcastle", (-1.61, 54.97), 6),
("Norwich", (1.30, 52.63), 4),
("Aberdeen", (-2.09, 57.15), 3),
("Belfast", (-5.93, 54.60), 5),
];
// A fixed pseudo-random spread, so the demo looks the same every time.
let mut seed = 7_u32;
let mut next = move || {
seed = seed.wrapping_mul(1_103_515_245).wrapping_add(12_345);
f64::from(seed >> 8) / f64::from(1_u32 << 24) - 0.5
};
towns
.iter()
.flat_map(|&(town, (lon, lat), n)| {
(0..n)
.map(|k| {
GeoPoint::new(
format!("{town} site {}", k + 1),
(lon + next() * 1.2, lat + next() * 0.7),
(4.0 + next() * 6.0).round(),
)
})
.collect::<Vec<_>>()
})
.collect()
}
#[component]
pub fn HexbinMapChartDemo() -> Element {
rsx! {
HexbinMapChart {
points: sites(),
regions: uk(),
label: "Public charging sites",
radius: 14.0,
projection: Projection::Mercator,
}
}
}
Anatomy
| Component | data-part | Renders |
|---|---|---|
HexbinMapChart | root | figure; `data-active` while a hexagon is picked out |
| marks | Focusable SVG; arrow keys step through hexagons from the top left |
| land | The base shapes, drawn plain |
| hex | One occupied hexagon, shaded by its point count through `--t` |
| tooltip | Points inside, their count and their summed value |
| scale | Colour ramp from one point to the fullest hexagon |
| sr-table | Every hexagon with its count, total and the names inside |
Keyboard
| Key | Does |
|---|---|
Arrow keys | Step through hexagons |
Home / End | First or last hexagon |
Escape | Clear the active hexagon |
Props
| Prop | Type | Notes |
|---|---|---|
points | Vec<GeoPoint> | Required. GeoPoint::new(name, (lon, lat), value); values sum into each hexagon's total |
regions | Vec<Region> | Base shapes drawn under the hexagons; their values are ignored |
label | String | Required. What the map shows; the accessible name |
radius | f64 | Hexagon radius in viewBox units (640 wide). Default 16 |
projection | Projection | Equirectangular (default), Mercator or Custom(fn) |
format | Fn(f64) -> String | Formats each hexagon's total |
Events
| Prop | Type | Notes |
|---|---|---|
on_hover | Callback<Option<Datum>> | The mark under the pointer or keyboard focus changes, or is left. Fires only on a change |
on_select | Callback<Datum> | Click, tap or Enter on a mark |