<bmx-map>
A map drawn by the library, with no tile server and nothing fetched: the world's countries are built in (Natural Earth, public domain; boundaries as they are on the ground), or give shapes of your own as GeoJSON or TopoJSON.
32 properties · 3 events · 5 methods · 7 parts
Example
Hover or tab to the map, then use the arrow keys. Click countries to select them; click a group of cities to zoom in.
Change either side of zero, with names
A diverging scale colours growth and decline differently; labels="auto" names the countries there is room for.
Shapes of your own
Give any GeoJSON or TopoJSON - here, made-up sales territories - and colour them by category.
Show markup
<bmx-map id="ex-map" scale="quantile" steps="6" format="decimal:1" unit=" m" legend-title="Population (millions, approximate)" label="World population" cluster selectable="multiple"></bmx-map>
<p class="note" id="ex-map-out" role="status">Hover or tab to the map, then use the arrow keys. Click countries to select them; click a group of cities to zoom in.</p>
<div class="row" style="gap: 0.75rem; flex-wrap: wrap; align-items: center; margin-block: 0.5rem 1.5rem">
<label>Projection
<select id="ex-map-proj">
<option value="equal-earth" selected>Equal Earth</option>
<option value="natural-earth">Natural Earth</option>
<option value="mercator">Mercator</option>
<option value="equirectangular">Equirectangular</option>
</select>
</label>
<label><input type="checkbox" id="ex-map-grat"> Graticule</label>
<bmx-button variant="outline" tone="neutral" id="ex-map-europe">Zoom to Europe</bmx-button>
<bmx-button variant="outline" tone="neutral" id="ex-map-svg">Copy as SVG</bmx-button>
</div>
<h3>Change either side of zero, with names</h3>
<p class="note">A diverging scale colours growth and decline differently; <code>labels="auto"</code> names the countries there is room for.</p>
<bmx-map id="ex-map-growth" scale="diverging" steps="6" format="decimal:1" unit="%" fit-to="DEU,FRA,ESP,POL,SWE,ITA,GBR" labels="auto" legend-title="Population change, 2013-2023 (approximate)" label="Population change in Europe" style="--bmx-map-height: 26rem"></bmx-map>
<h3>Shapes of your own</h3>
<p class="note">Give any GeoJSON or TopoJSON - here, made-up sales territories - and colour them by category.</p>
<bmx-map id="ex-map-own" projection="equirectangular" scale="category" label="Sales territories" labels="all" style="--bmx-map-height: 18rem"></bmx-map>
<script type="module">
await customElements.whenDefined('bmx-map');
const pop = {
CHN: 1410, IND: 1428, USA: 335, IDN: 277, PAK: 240, NGA: 224, BRA: 216, BGD: 173, RUS: 144, MEX: 128, ETH: 126, JPN: 124, PHL: 117,
EGY: 113, COD: 102, VNM: 99, IRN: 89, TUR: 85, DEU: 84, THA: 72, GBR: 68, FRA: 68, TZA: 67, ZAF: 60, ITA: 59, KEN: 55, MMR: 54,
COL: 52, KOR: 52, UGA: 48, SDN: 48, ESP: 48, ARG: 46, DZA: 46, IRQ: 45, AFG: 42, POL: 37, CAN: 39, MAR: 37, SAU: 37, UKR: 37,
AGO: 36, UZB: 35, PER: 34, MYS: 34, MOZ: 34, GHA: 34, YEM: 34, NPL: 31, VEN: 28, MDG: 30, CMR: 28, AUS: 27, PRK: 26, NER: 27,
LKA: 22, MLI: 23, CHL: 20, KAZ: 20, ROU: 19, NLD: 18, SEN: 18, ECU: 18, GTM: 18, SWE: 10.5, NOR: 5.5, FIN: 5.6, IRL: 5.3,
NZL: 5.2, PRT: 10.4, GRC: 10.4, CZE: 10.9, HUN: 9.6, BLR: 9.2, AUT: 9.1, CHE: 8.8, ISR: 9.8, BOL: 12.4, CUB: 11.2, TUN: 12.5,
BEL: 11.7, DNK: 5.9, MNG: 3.4, GRL: 0.06, ISL: 0.38,
};
const map = document.getElementById('ex-map');
map.data = pop;
map.markers = [
{ label: 'London', lon: -0.13, lat: 51.51, value: 9 },
{ label: 'Paris', lon: 2.35, lat: 48.86, value: 11 },
{ label: 'Brussels', lon: 4.35, lat: 50.85, value: 2.1 },
{ label: 'Amsterdam', lon: 4.9, lat: 52.37, value: 2.5 },
{ label: 'New York', lon: -74.0, lat: 40.71, value: 19 },
{ label: 'Tokyo', lon: 139.69, lat: 35.69, value: 37 },
{ label: 'Mumbai', lon: 72.88, lat: 19.08, value: 21 },
{ label: 'São Paulo', lon: -46.63, lat: -23.55, value: 22 },
{ label: 'Lagos', lon: 3.38, lat: 6.52, value: 15 },
{ label: 'Sydney', lon: 151.21, lat: -33.87, value: 5.3 },
];
map.lines = [
{ from: [-0.13, 51.51], to: [-74.0, 40.71] },
{ from: [-0.13, 51.51], to: [139.69, 35.69] },
{ from: [-0.13, 51.51], to: [151.21, -33.87] },
];
const out = document.getElementById('ex-map-out');
map.addEventListener('bmxMapSelect', e => (out.textContent = e.detail.ids.length ? `Selected: ${e.detail.ids.join(', ')}` : 'Nothing selected.'));
map.addEventListener('bmxMapItemClick', e => {
if (e.detail.kind === 'marker') out.textContent = `${e.detail.name}: about ${e.detail.value} million people in the city.`;
});
document.getElementById('ex-map-proj').addEventListener('change', e => (map.projection = e.target.value));
document.getElementById('ex-map-grat').addEventListener('change', e => (map.graticule = e.target.checked));
document.getElementById('ex-map-europe').addEventListener('click', () => map.zoomTo(['FRA', 'DEU', 'POL', 'ESP', 'ITA', 'GBR', 'SWE']));
document.getElementById('ex-map-svg').addEventListener('click', async () => {
const svg = await map.toSvg();
try {
await navigator.clipboard.writeText(svg);
out.textContent = `Copied ${Math.round(svg.length / 1024)} KB of SVG.`;
} catch {
out.textContent = 'The clipboard was not available.';
}
});
document.getElementById('ex-map-growth').data = {
DEU: 4.1, FRA: 3.6, GBR: 6.9, ESP: 3.5, ITA: -1.6, POL: -1.3, ROU: -4.9, BGR: -11.2, GRC: -5.9, PRT: -0.5, IRL: 13.7, NLD: 6.3,
BEL: 6.2, SWE: 8.9, NOR: 9.1, FIN: 2.9, DNK: 5.4, AUT: 7.4, CHE: 10.6, CZE: 2.1, SVK: 0.4, HUN: -2.2, SRB: -5.1, HRV: -9.4,
LTU: -7.6, LVA: -7.9, EST: 1.6, UKR: -14.0, BLR: -2.6, BIH: -8.0, ALB: -2.4, MKD: -1.1, SVN: 2.0, MDA: -8.5,
};
const box = (x, y, w, h) => [[[x, y], [x + w, y], [x + w, y + h], [x, y + h], [x, y]]];
const territories = {
type: 'FeatureCollection',
features: [
{ type: 'Feature', id: 'N1', properties: { name: 'North 1' }, geometry: { type: 'Polygon', coordinates: box(0, 20, 20, 10) } },
{ type: 'Feature', id: 'N2', properties: { name: 'North 2' }, geometry: { type: 'Polygon', coordinates: box(20, 20, 25, 10) } },
{ type: 'Feature', id: 'C1', properties: { name: 'Central' }, geometry: { type: 'Polygon', coordinates: box(0, 8, 45, 12) } },
{ type: 'Feature', id: 'S1', properties: { name: 'South West' }, geometry: { type: 'Polygon', coordinates: box(0, 0, 15, 8) } },
{ type: 'Feature', id: 'S2', properties: { name: 'South East' }, geometry: { type: 'Polygon', coordinates: box(15, 0, 30, 8) } },
],
};
const own = document.getElementById('ex-map-own');
own.regions = territories;
own.data = [
{ id: 'N1', value: 'Ahead', label: 'Owner: Priya' },
{ id: 'N2', value: 'On track', label: 'Owner: Sam' },
{ id: 'C1', value: 'On track', label: 'Owner: Leo' },
{ id: 'S1', value: 'Behind', label: 'Owner: Ana' },
{ id: 'S2', value: 'Ahead', label: 'Owner: Kofi' },
];
</script>
Colour regions by value (a choropleth: equal steps, quantiles, your own breaks, diverging either side of a middle, or categories) with a legend; put markers on places, sized by value and grouped when they crowd; draw routes along great circles. Pan and zoom with the pointer, the wheel, a pinch or the keyboard; select regions; read every value as a table.
Keyboard: the map is one stop. The arrow keys move from region to region (and marker) in that direction, saying each name and value; Enter or Space selects; Shift and the arrows pan; + and - zoom; 0 shows the whole map.
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
antarctica |
antarctica |
boolean |
false |
Draw Antarctica (the built-in world). |
cluster |
cluster |
boolean |
false |
Group markers that crowd together; a group opens when it is chosen. |
data |
data |
BmxMapDatum[] | Record<string, unknown> | string |
— | Values for regions: [{ id, value, label, color }], or { "GBR": 67.7, "FR": 68.2 }, as a property or JSON. |
fitTo |
fit-to |
string |
'all' |
What the view starts on: all (default), data (the regions with values and the markers), or ids: "GBR,FRA,DEU". |
format |
format |
BmxChartNumberFormat | string |
— | How values are written: percent, currency:EUR, decimal:1, compact, or the object. |
graticule |
graticule |
boolean |
false |
Draw lines of latitude and longitude. |
idField |
id-field |
string |
'id' |
The field of a record holding the region's id. |
idProperty |
id-property |
string |
— | The property holding a region's id in your shapes. |
label |
label |
string |
— | What the map shows: its accessible name. |
labels |
labels |
'none' | 'auto' | 'all' |
'none' |
Names on the map: none (default), auto (regions large enough at the zoom, and markers), or all. |
legend |
legend |
'bottom' | 'none' |
'bottom' |
Where the legend goes: bottom (default) or none. |
legendTitle |
legend-title |
string |
— | A heading for the legend. |
lines |
lines |
BmxMapLine[] | string |
— | Routes: [{ from, to, label }], each end [longitude, latitude] or an id. |
locale |
locale |
string |
— | A BCP 47 locale for numbers. Default: the page's lang. |
markers |
markers |
BmxMapMarker[] | string |
— | Places: [{ lon, lat, value, label, color }], as a property or JSON. |
maxZoom |
max-zoom |
number |
12 |
How far in the reader can zoom. |
mid |
mid |
number |
0 |
The middle of a diverging scale. |
nameProperty |
name-property |
string |
— | The property holding a region's name in your shapes. |
projection |
projection |
BmxMapProjection |
'equal-earth' |
equal-earth (default), natural-earth, mercator or equirectangular. |
regions |
property only | unknown |
— | Shapes of your own: GeoJSON or TopoJSON, as a property or JSON. The world's countries, by default. |
scale |
scale |
BmxMapScaleKind |
'quantize' |
How values become colours: quantize (equal steps, default), quantile, threshold, diverging or category. |
selectable |
selectable |
'none' | 'single' | 'multiple' |
'none' |
Regions the reader can select: none (default), single or multiple. |
selected |
property only | string[] |
[] |
The selected regions' ids. Changes write back here. |
src |
src |
string |
— | A URL to read shapes from (GeoJSON or TopoJSON), on the page's own server. |
steps |
steps |
number |
5 |
How many classes, for quantize, quantile and diverging. |
strings |
strings |
Partial<BmxMapStrings> | string |
— | Replacements for the map's wording, as a property or JSON. |
tableView |
table-view |
boolean |
true |
Offer the table view. |
thresholds |
thresholds |
number[] | string |
— | The breaks of a threshold scale: [10, 50, 100] or "10,50,100". |
topologyObject |
topology-object |
string |
— | The TopoJSON object to draw, when there are several. |
unit |
unit |
string |
— | Written after every value: t, %. |
valueField |
value-field |
string |
'value' |
The field of a record holding its value. |
zoomable |
zoomable |
boolean |
true |
The reader can pan and zoom. |
Events
| Event | Detail | Description |
|---|---|---|
bmxMapItemClick |
BmxMapItemDetail |
A region, marker or group was clicked or chosen with the keyboard. |
bmxMapSelect |
BmxMapSelectDetail |
Regions were selected or unselected. |
bmxMapViewChange |
BmxMapViewDetail |
The view was panned or zoomed. |
Methods
| Method | Signature | Description |
|---|---|---|
centerOn |
centerOn(center: BmxMapPoint, zoom?: number) => Promise<void> |
Centres the view on a place, at a zoom (the current one by default). |
clearSelection |
clearSelection() => Promise<void> |
Unselects every region. |
getView |
getView() => Promise<BmxMapViewDetail> |
The view: how far in, and the middle as [longitude, latitude]. |
toSvg |
toSvg() => Promise<string> |
The map as a standalone SVG document, its colours written in. |
zoomTo |
zoomTo(target: string[] | "data" | null) => Promise<void> |
Shows regions (ids), the regions with data and the markers (data), or the whole map (null). |
CSS shadow parts
| Part | Description |
|---|---|
controls |
the zoom and table buttons. |
legend |
the legend. |
map |
the drawing. |
marker |
a marker. |
region |
a region's shape. |
table |
the table view. |
tooltip |
the name and value under the pointer. |
CSS custom properties
| Property | Description |
|---|---|
--bmx-map-border |
The lines between regions. |
--bmx-map-color-0 |
The first category. Colours 0 to 9 are used in turn. |
--bmx-map-color-1 |
Category 2. |
--bmx-map-color-2 |
Category 3. |
--bmx-map-color-3 |
Category 4. |
--bmx-map-color-4 |
Category 5. |
--bmx-map-color-5 |
Category 6. |
--bmx-map-color-6 |
Category 7. |
--bmx-map-color-7 |
Category 8. |
--bmx-map-color-8 |
Category 9. |
--bmx-map-color-9 |
Category 10. |
--bmx-map-height |
How tall the map is. Default: as tall as the projection needs at the width. |
--bmx-map-high |
The highest class. |
--bmx-map-land |
A region with no value. |
--bmx-map-low |
The lowest class of a choropleth. |
--bmx-map-marker |
A marker. |
--bmx-map-mid |
The middle of a diverging scale. |
--bmx-map-negative |
The low end of a diverging scale. |
--bmx-map-ocean |
Behind the land (with graticule, inside the map's outline). |
--bmx-map-route |
A route. |
--bmx-map-selected |
A selected region's outline. |