Examples
Copy-paste examples for common use cases. All examples use the current flat API with ixmaps.Map().
1. Simple bubble map (point data)
Points sized by a numeric value. The simplest useful map.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Bubble Map</title>
<style>
html, body { width: 100%; height: 100%; margin: 0; overflow: hidden; }
#map { width: 100%; height: 100%; }
</style>
<script src="https://cdn.jsdelivr.net/gh/gjrichter/ixmaps-flat@1/ixmaps.js"></script>
</head>
<body>
<div id="map"></div>
<script>
const DATA = [
{ city: "Rome", lat: 41.90, lon: 12.50, pop: 2873000 },
{ city: "Milan", lat: 45.46, lon: 9.19, pop: 1372000 },
{ city: "Naples", lat: 40.85, lon: 14.27, pop: 966000 },
{ city: "Turin", lat: 45.07, lon: 7.69, pop: 875000 },
{ city: "Palermo", lat: 38.12, lon: 13.36, pop: 657000 }
];
const myMap = ixmaps.Map("map", {
mapType: "VT_TONER_LITE",
mode: "info",
legend: "closed",
tools: true
})
.view({ center: { lat: 42.5, lng: 12.5 }, zoom: 6 })
.options({ basemapopacity: 0.6, flushChartDraw: 1000000 });
myMap.layer("cities")
.data({ obj: DATA, type: "json" })
.binding({ geo: "lat|lon", value: "pop", title: "city" })
.type("CHART|BUBBLE|SIZE|VALUES")
.style({
colorscheme: ["#0066cc"],
fillopacity: 0.7,
normalsizevalue: "3000000",
showdata: "true"
})
.meta({ tooltip: "<b>{{city}}</b><br>Population: {{pop}}" })
.title("Population")
.define();
</script>
</body>
</html>2. Categorical bubble map
Points coloured by category. values pins specific colors to specific categories.
const DATA = [
{ name: "Florence", lat: 43.77, lon: 11.25, type: "arts", size: 370000 },
{ name: "Venice", lat: 45.44, lon: 12.34, type: "tourism", size: 255000 },
{ name: "Bologna", lat: 44.49, lon: 11.34, type: "food", size: 400000 },
{ name: "Genoa", lat: 44.41, lon: 8.93, type: "port", size: 580000 },
{ name: "Bari", lat: 41.12, lon: 16.87, type: "port", size: 320000 }
];
myMap.layer("cities")
.data({ obj: DATA, type: "json" })
.binding({ geo: "lat|lon", value: "type", size: "size", title: "name" })
.type("CHART|BUBBLE|CATEGORICAL|GLOW")
.style({
colorscheme: ["#e76f51","#2a9d8f","#e9c46a","#264653"],
values: ["arts","tourism","food","port"], // strings, not numbers
normalsizevalue: "600000",
fillopacity: 0.8,
showdata: "true"
})
.meta({ tooltip: "<b>{{name}}</b><br>Type: {{type}}" })
.title("City type")
.define();3. World choropleth (countries)
Colour world countries by a CSV value. The overlay layer reuses the geometry base’s name.
const myMap = ixmaps.Map("map", {
mapType: "CartoDB - Positron",
mode: "info",
legend: "open"
})
.view({ center: { lat: 20, lng: 0 }, zoom: 2 })
.options({ basemapopacity: 0.5, flushChartDraw: 1000000 });
// 1 — geometry base
myMap.layer("countries")
.data({
url: "https://gisco-services.ec.europa.eu/distribution/v2/countries/topojson/CNTR_RG_60M_2020_4326.json",
type: "topojson"
})
.binding({ geo: "geometry", id: "CNTR_ID", title: "NAME_ENGL" })
.type("FEATURE")
.style({
colorscheme: ["#ddd"],
fillopacity: 0.3,
linecolor: "#aaa",
linewidth: 0.4,
showdata: "true"
})
.define();
// 2 — choropleth overlay (SAME name "countries")
myMap.layer("countries")
.data({ url: "https://example.com/gdp.csv", type: "csv" })
.binding({ lookup: "iso2", value: "gdp_per_capita" })
.type("CHOROPLETH|QUANTILE")
.style({
colorscheme: ["#ffffcc","#c7e9b4","#41b6c4","#2c7fb8","#253494"],
fillopacity: 0.8,
showdata: "true"
})
.meta({ tooltip: "<b>{{NAME_ENGL}}</b><br>GDP per capita: {{gdp_per_capita}}" })
.title("GDP per capita")
.define();4. Italy municipalities (choropleth + join)
Join ISTAT CSV data to municipality polygons. Note the pinned geometry URL for 2024-compatible codes.
const GEO_URL =
"https://raw.githubusercontent.com/gjrichter/geo/0153a0e14da5dae877b8c94d6deb11f210d4660a/italy/boundaries/italy_istat_municipalities_4326_500m.topojson";
const myMap = ixmaps.Map("map", {
mapType: "VT_TONER_LITE",
mode: "info",
legend: "closed"
})
.view({ center: { lat: 42.5, lng: 12.5 }, zoom: 6 })
.options({
basemapopacity: 0.4,
flushChartDraw: 1000000,
flushPaintShape: 1000000 // required for ~8000 polygons
});
myMap.layer("comuni")
.data({ url: GEO_URL, type: "topojson" })
.binding({ geo: "geometry", id: "com_istat_code_num", title: "name" })
.type("FEATURE")
.style({
colorscheme: ["#ccc"],
fillopacity: 0.2,
linecolor: "#888",
linewidth: 0.2,
showdata: "true"
})
.define();
myMap.layer("comuni")
.data({ url: "https://example.com/istat_data.csv", type: "csv" })
.binding({ lookup: "istat_code", value: "value" })
.type("CHOROPLETH|QUANTILE")
.style({
colorscheme: ["#fff7fb","#ece2f0","#a6bddb","#1c9099","#016450"],
fillopacity: 0.85,
showdata: "true"
})
.meta({ tooltip: "<b>{{name}}</b><br>{{value}}" })
.title("Value by municipality")
.define();5. Swappable themes (remove-then-define)
Switch between multiple visualisations of the same geometry without stacking layers.
let ACTIVE_THEME = null;
function setTheme(id, type, styleOverride) {
const themeName = "theme-" + id;
const prev = ACTIVE_THEME;
myMap.then(api => {
if (prev) { try { api.removeTheme(prev); } catch(e) {} }
myMap.layer("regions") // SAME name as FEATURE base
.data({ obj: DATA, type: "json", cache: "true" })
.binding({ lookup: "code", value: id })
.type(type)
.style(Object.assign({ showdata: "true" }, styleOverride))
.meta({ name: themeName, tooltip: "{{name}}: {{" + id + "}}" })
.define();
ACTIVE_THEME = themeName;
});
}
// Switch between indicators:
setTheme("population", "CHOROPLETH|QUANTILE",
{ colorscheme: ["#ffffcc","#253494"], fillopacity: 0.8 });
setTheme("income", "CHOROPLETH|NATURAL",
{ colorscheme: ["#fff5f0","#a50f15"], fillopacity: 0.8 });6. Animated time series
Show one year at a time, stepping through with a slider.
const yearData = {
"2020": [...],
"2021": [...],
"2022": [...],
"2023": [...]
};
let ACTIVE = null;
function showYear(year) {
const themeName = "year-" + year;
const prev = ACTIVE;
myMap.then(api => {
if (prev) { try { api.removeTheme(prev); } catch(e) {} }
myMap.layer("countries")
.data({ obj: yearData[year], type: "json" })
.binding({ geo: "lat|lon", value: "metric", title: "name" })
.type("CHART|BUBBLE|SIZE|VALUES")
.style({
colorscheme: ["#0066cc"],
fillopacity: 0.7,
normalsizevalue: "1000000",
showdata: "true"
})
.meta({ name: themeName, tooltip: "{{name}}: {{metric}}" })
.define();
ACTIVE = themeName;
});
}
// HTML: <input type="range" min="2020" max="2023" oninput="showYear(this.value)">
showYear("2020");7. Flow map (origin → destination)
Arrows from source to destination, with optional animation.
const flows = [
{ from_name: "Rome", from_lat: 41.90, from_lon: 12.50,
to_lat: 48.86, to_lon: 2.35, volume: 5000 },
{ from_name: "Milan", from_lat: 45.46, from_lon: 9.19,
to_lat: 51.51, to_lon: -0.13, volume: 8000 }
];
myMap.layer("flows")
.data({ obj: flows, type: "json" })
.binding({
geo: "from_lat|from_lon",
value: "volume",
title: "from_name",
geo2: "to_lat|to_lon"
})
.type("CHART|VECTOR|BEZIER|POINTER|DASH")
.style({
colorscheme: ["#0066cc"],
fillopacity: 0.7,
linewidth: 2,
showdata: "true"
})
.meta({ tooltip: "{{from_name}} → volume: {{volume}}" })
.define();8. Sparklines (time-series curves per location)
A sparkline curve at each data point showing multi-year trends.
const DATA = [
{ city: "Rome", lat: 41.90, lon: 12.50, v2020: 100, v2021: 108, v2022: 115, v2023: 122 },
{ city: "Milan", lat: 45.46, lon: 9.19, v2020: 90, v2021: 95, v2022: 99, v2023: 105 }
];
myMap.layer("trends")
.data({ obj: DATA, type: "json" })
.binding({ geo: "lat|lon", value: "v2020|v2021|v2022|v2023", title: "city" })
.type("CHART|SYMBOL|PLOT|LINES|AREA|FADE|LASTARROW|NOCLIP|GRIDSIZE|FIXSIZE")
.style({
gridwidth: "100px",
normalsizevalue: "30",
markersize: 2,
colorscheme: ["#00e5ff"],
fillopacity: 0.5,
showdata: "true"
})
.meta({ tooltip: "{{city}}<br>{{theme.item.chart}}" })
.define();9. Grid density heatmap
Aggregate thousands of points into a regular grid.
myMap.layer("density")
.data({ url: "https://example.com/points.csv", type: "csv" })
.binding({ geo: "lat|lon", value: "$item$" })
.type("CHART|BUBBLE|SIZE|AGGREGATE|COUNT|RECT")
.style({
colorscheme: ["#fee5d9","#fcae91","#fb6a4a","#de2d26","#a50f15"],
fillopacity: 0.7,
gridwidth: "5px",
showdata: "true"
})
.meta({ tooltip: "Count: {{$item$}}" })
.title("Point density")
.define();10. Equal Earth world map (projection)
A world map using the Equal Earth projection — no tile layer, SVG only.
const myMap = ixmaps.Map("map", {
mapType: "#0a1929", // dark ocean background
mapProjection: "equalearth",
mode: "info",
legend: "closed"
})
.view([0, 0], 1) // array syntax required for projections
.options({ basemapopacity: 0, flushChartDraw: 1000000 });
myMap.layer("countries")
.data({
url: "https://gisco-services.ec.europa.eu/distribution/v2/countries/topojson/CNTR_RG_60M_2020_4326.json",
type: "topojson"
})
.binding({ geo: "geometry", id: "CNTR_ID", title: "NAME_ENGL" })
.type("FEATURE")
.style({
colorscheme: ["#1a3a5c"],
fillopacity: 0.9,
linecolor: "#4a7fc1",
linewidth: 0.4,
showdata: "true"
})
.define();
// Add data overlay with external CSV
myMap.layer("countries")
.data({ url: "https://example.com/world_data.csv", type: "csv" })
.binding({ lookup: "iso2", value: "value" })
.type("CHOROPLETH|QUANTILE")
.style({
colorscheme: ["#08306b","#2171b5","#6baed6","#bdd7e7","#eff3ff"],
fillopacity: 0.85,
showdata: "true"
})
.meta({ tooltip: "<b>{{NAME_ENGL}}</b>: {{value}}" })
.define();11. External data via jsDelivr CDN
Best practice for production maps — keep data outside the HTML file.
// Data hosted at github.com/your-user/your-repo/data/cities.csv
// CDN URL pattern: cdn.jsdelivr.net/gh/<user>/<repo>@<branch>/<path>
myMap.layer("cities")
.data({
url: "https://cdn.jsdelivr.net/gh/gjrichter/data@main/cities/italy.csv",
type: "csv"
})
.binding({ geo: "lat|lon", value: "population", title: "name" })
.type("CHART|BUBBLE|SIZE|VALUES")
.style({
colorscheme: ["#0066cc"],
fillopacity: 0.7,
normalsizevalue: "3000000",
showdata: "true"
})
.meta({ tooltip: "<b>{{name}}</b><br>Population: {{population}}" })
.define();Benefits: small HTML (no inline data), separate data versioning, free CDN, CORS-enabled.