A map of the United States that drills down to congressional districts: click a state to zoom in and load the map of its districts.
Detailed maps are big files, so this one starts with the states and loads a state’s district map only when someone clicks it. The first view loads quickly, and the detail is a click away. The same approach works for countries and their regions, or any two levels of geography.
Built with amCharts 5, a JavaScript library. The code includes your theme and renderer (canvas or SVG), but not the changes you make with the controls.
npm install @amcharts/amcharts5 and @amcharts/amcharts5-geodata. See Getting started.
The same chart as a JSON config. Map Maker reads and writes this format, the library's JSON parser turns it into a chart, and the <am5-chart> element puts it on a page with no code."type" is the class to create and "settings" what you would pass to its new() in code; "properties" fill in its parts, lists such as series and axes or templates such as columns.template; "refs" holds objects used in more than one place ("#series-0" stands for the one named series-0); a string starting with "@" takes a value from another object, such as "@series.get('fill')".
A complete page. Save it as an .html file and open it in a browser.
Paste this into an AI coding assistant to build the chart from scratch. More in amCharts & AI.
// Create root element
// https://www.amcharts.com/docs/v5/getting-started/#Root_element
var root = am5.Root.new("chartdiv");
// Set themes
// https://www.amcharts.com/docs/v5/concepts/themes/
root.setThemes([
am5themes_Animated.new(root),
am5themes_Responsive.new(root)
]);
// Hover colors come from the theme's palette
var colors = am5.ColorSet.new(root, {});
// Create the map chart
// https://www.amcharts.com/docs/v5/charts/map-chart/
var chart = root.container.children.push(am5map.MapChart.new(root, {
minZoomLevel: 0.5, // zoom out to half the fitted size at most
// go to the home view once the map is fitted
autoHome: true,
panX: "translateX", // dragging moves the map sideways (Albers USA can't rotate)
panY: "translateY", // ...and up and down
projection: am5map.geoAlbersUsa(), // a US projection, with Alaska and Hawaii in insets at the lower left
// room for the title above the map
paddingTop: 40
}));
// Zoom control
// https://www.amcharts.com/docs/v5/charts/map-chart/map-pan-zoom/#Zoom_control
var zoomControl = chart.set("zoomControl", am5map.ZoomControl.new(root, {}));
// the home button is hidden by default
zoomControl.homeButton.set("visible", true);
// Create polygon series for the states
// https://www.amcharts.com/docs/v5/charts/map-chart/map-polygon-series/
var usaSeries = chart.series.push(am5map.MapPolygonSeries.new(root, {
geoJSON: am5geodata_usaLow
}));
usaSeries.mapPolygons.template.setAll({
tooltipText: "{name}", // the state's name on hover
interactive: true, // reacts to hover and clicks
templateField: "polygonSettings" // styles from a polygonSettings field in the data, if a state has one
});
// a state turns a palette color under the pointer
usaSeries.mapPolygons.template.states.create("hover", {
fill: colors.getIndex(9)
});
// a click zooms in on the state and loads its congressional districts
usaSeries.mapPolygons.template.events.on("click", (ev) => {
var dataItem = ev.target.dataItem;
// "US-CA" becomes "ca", the state's part of the district map's file name
var id = dataItem.get("id").toLowerCase().split("-").pop();
var name = dataItem.dataContext.name;
var zoomAnimation = usaSeries.zoomToDataItem(dataItem); // zoom in to fit the clicked state
// once the zoom has ended and the districts have loaded, swap the states for the districts
Promise.all([
zoomAnimation.waitForStop(),
am5.net.load("https://cdn.amcharts.com/lib/5/geodata/json/region/usa/congressional120/" + id + "Low.json", chart)
]).then(function(results) {
var geodata = am5.JSONParser.parse(results[1].response);
stateSeries.setAll({
geoJSON: geodata
});
stateSeries.show();
usaSeries.hide(100); // hide the states in 0.1 seconds
backContainer.show();
title.set("text", name);
});
});
// Create polygon series for the districts of the clicked state
// https://www.amcharts.com/docs/v5/charts/map-chart/map-polygon-series/
var stateSeries = chart.series.push(am5map.MapPolygonSeries.new(root, {
visible: false // hidden until a state is clicked
}));
stateSeries.mapPolygons.template.setAll({
tooltipText: "{name}", // the district's name on hover
interactive: true // reacts to hover
});
stateSeries.mapPolygons.template.states.create("hover", {
fill: colors.getIndex(9)
});
// Add button to go back to the whole country
var backContainer = chart.children.push(am5.Container.new(root, {
x: am5.p100, // at the chart's right edge...
centerX: am5.p100, // ...aligned by its own right edge
dx: -10, // 10px in from the edge
paddingTop: 5, // space around the label and the arrow
paddingRight: 10,
paddingBottom: 5,
y: 30, // a little below the top
// the whole box takes the click, not the label and icon inside it
interactiveChildren: false,
layout: root.horizontalLayout, // the label and the arrow side by side
cursorOverStyle: "pointer", // a hand pointer over the button
background: am5.RoundedRectangle.new(root, {
fill: root.interfaceColors.get("background"), // a box in the chart's background color...
fillOpacity: 0.7 // ...slightly see-through
}),
visible: false // hidden until a state is clicked
}));
var backLabel = backContainer.children.push(am5.Label.new(root, {
text: "Back",
centerY: am5.p50 // the label and the arrow line up on their middles
}));
var backButton = backContainer.children.push(am5.Graphics.new(root, {
width: 32, // a 32px icon
height: 32,
centerY: am5.p50,
fill: root.interfaceColors.get("text"), // in the text color, so it shows on light and dark backgrounds
// a back arrow, as SVG path data
svgPath: "M12 9.059V6.5a1.001 1.001 0 0 0-1.707-.708L4 12l6.293 6.207a.997.997 0 0 0 1.414 0A.999.999 0 0 0 12 17.5v-2.489c2.75.068 5.755.566 8 3.989v-1c0-4.633-3.5-8.443-8-8.941z"
}));
// back to the whole country: zoom out and swap the districts for the states
function goBack() {
chart.goHome();
usaSeries.show();
stateSeries.hide();
backContainer.hide();
title.set("text", "United States");
}
backContainer.events.on("click", goBack);
// the zoom control's home button goes back to the whole country too
zoomControl.homeButton.events.on("click", goBack);
// the title sits in the room above the map ("absolute" places it from the chart's top edge, not inside the padding)
var title = chart.children.push(am5.Label.new(root, {
text: "United States",
position: "absolute",
x: am5.p50, // centered across the chart...
centerX: am5.p50, // ...by its own middle
y: 5, // 5px from the top
fontSize: 20, // 20px text
textAlign: "center"
}));
