Anatomy of a Map Chart
This article will explain how to implement feature-rich maps with amCharts 4.
Creating a Map
Section titled “Creating a Map”As with any other chart type, Map Chart requires an instance object to be able to do anything. To create a chart, we’ll use MapChart class.
A Map to be useful will at least need to have these elements:
- Map chart instance
- Map definition (in GeoJSON format);
- At least one map series.
We’ll walk you through these basics before jumping on to more advanced topics.
Importing modules/scripts
Section titled “Importing modules/scripts”Needless to say, before you can use amcharts4 module/object, you need to make sure all required modules (in TypeScript or ES6), or files (in JavaScript) are imported. Please refer to our Getting started articles for more details:
For a Map chart, we’ll need to import core (main module), maps module, and a map file in GeoJSON format.
import * as am4core from "@amcharts/amcharts4/core";import * as am4maps from "@amcharts/amcharts4/maps";import am4geodata_worldLow from "@amcharts/amcharts4-geodata/worldLow";<script src="//cdn.amcharts.com/lib/4/core.js"></script><script src="//cdn.amcharts.com/lib/4/maps.js"></script><script src="//cdn.amcharts.com/lib/4/geodata/worldLow.js"></script>We’ll be importing and referring these as am4core and am4maps in the course of this article, and overally in our demos and tutorials.
As for map geodata, we’ll be using am4geodata_[map name] syntax.
Map data
Section titled “Map data”In amCharts 4, actual map data (map files) are located separately from the actual libraries.
The reason for this is that map files tend to be big, and there are a lot of them. We don’t want you to have to download tens of megabytes of map data, if all you want to do is to draw a Pie chart, do we?
TypeScript/ES6
Section titled “TypeScript/ES6”If you are using TypeScript/ES, import files from a separate package: @amcharts/amcharts4-geodata.
JavaScript
Section titled “JavaScript”If you are using amCharts CDN to load the files, you’re all set.
If you are installing all files on your own server, please make sure to download a separate ZIP file containing all the map data files.
Creating map instance
Section titled “Creating map instance”To create an instance we’ll use am4core.create() function. (or am4core.createFromConfig() if you are using JSON-based config approach)
let map = am4core.create("chartdiv", am4maps.MapChart);var map = am4core.create("chartdiv", am4maps.MapChart);var map = am4core.createFromConfig({ // ... map config}, "chartdiv", am4maps.MapChart);Map definition
Section titled “Map definition”There’s an important distinction between “Map chart” and “Map definition”.
The first is an instance, an object, like any other chart type, which we use to configure our map control.
The latter is a data file, which contains actual geographical data, used to draw map, like contours of countries, etc. amCharts 4 accepts map definitions in GeoJSON format.
About GeoJSON
Section titled “About GeoJSON”“GeoJSON is a format for encoding a variety of geographic data structures.” - that’s how its creators describe it.
We chose it to be used in our library because
- It’s open (no corporation behind it);
- Has a lot of sources for free, ready-made maps;
- Is widely adopted in the industry;
- There is a lot of geo software tools that can be used to create and edit maps.
Using map definition
Section titled “Using map definition”To make the Map chart use specific map definition, simply set map instance’s geodata property.
If you are using TypeScript or ES6 modules, the loaded definition will be in whatever name you specified during import. For plain JavaScript all bundled map definitions end up in am4geodata_[map name] object, and are accessible via map name as key.
map.geodata = am4geodata_worldLow;map.geodata = am4geodata_worldLow;var map = am4core.createFromConfig({ "geodata": am4geodata_worldLow, // Alternatively we could do this: // "geodata": "worldLow", // ... map config}, "chartdiv", am4maps.MapChart);NOTE ABOUT JSON In JSON-based setup, it’s possible to specify the map by its name: geodata: "worldLow". If your map is loaded via <script> tag, it will automatically replace the contents of geodata with proper JSON.
Regional and county maps
Section titled “Regional and county maps”Since version 4.0.12 geodata package contains county maps for the U.S.
They are located in region/usa subdirectory, and are accessed somewhat differently than regular maps.
Please read this tutorial for further information.
Loading external maps
Section titled “Loading external maps”You’re not limited to just using bundled maps. You can use any map definition in GeoJSON format you can lay your hands on.
To make your Map chart automatically load such external file, use geodataSource.url setting instead of geodata.
Here’s how it would look:
map.geodataSource.url = "/path/to/myCustomMap.json";map.geodataSource.url = "/path/to/myCustomMap.json";{ // ... "geodataSource": { "url": "/path/to/myCustomMap.json" }}For the purpose of this article we are going to be using bundled maps and geodata. But if you’d rather go with geodataSource approach, everything in this article will still apply and work for you.
Setting projection
Section titled “Setting projection”There’s no proper way to “flatten out” a globe into a 2-dimensional sheet without severely distorting it.
Mapping community has been dealing with this issue since the dawn of maps. There are numerous ways and techniques to do that, and they are called “projections”.
In amCharts 4, by default all maps are displayed in Equirectangular projection.
To display a map in some other projection, use maps’ projection property. In amCharts 4 all projection class definitions are accessible via am4maps.projections variable. We just need to instantiate a proper one as object, and assign to projection property.
At this moment we support following projections:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Let’s set our map to use “Miller” projection:
map.projection = new am4maps.projections.Miller();map.projection = new am4maps.projections.Miller();var map = am4core.createFromConfig({ "geodata": am4geodata_worldLow, "projection": "Miller", // ... map config}, "chartdiv", am4maps.MapChart);Bundled maps
Section titled “Bundled maps”Most of maps, bundled with amCharts 4 come in two flavors: high detail and low detail. The modules/files are indicated with “High” and “Low” suffixes respectively.
High-detail versions provides finer border and coastal lines, but they are significantly larger.
Low-detail ones are much smaller in size, but at the expense on details. Since map lines are generalized, using low-detail maps may plot some coastal markers off-shore.
It’s up to you to decide which one of the trade-offs suit your requirements best.
Low vs high-detail
Section titled “Low vs high-detail”The choice between low or high detail map version comes down to your personal preferences and requirements.
Do you need it to load super fast and don’t need a lot of detail? Low-detail version might seem a better choice.
That said, low-detail version has lines simplified to the point where small hooks, creeks, and even smaller islands might become unrecognizable or even vanish altogether.
If that seems to be a problem, opt for the high-detail version of the map.
Below is a side-by-side comparison of two versions of the map of France:
Please note, that while both maps might seem relatively fine when fully zoomed out, the difference become more and more prominent when zooming in:

Ultra-high detail
Section titled “Ultra-high detail”Since version 4.0.28 of geodata package, most world and region maps come also in “ultra high” detail. File names for those have *Ultra suffix, e.g. worldUltra.
Please note that higher-detail comes with extra file sizes, so make sure you really need all that detail before jumping in.
One final note: Ultra-detail on world/region maps are generated using same detail level as High-detail versions of country maps, so they are a good match for pairing, as it is described in “Overlaying multiple maps” section of this article.
Finding more maps
Section titled “Finding more maps”@todo Information being prepared.
Creating custom maps
Section titled “Creating custom maps”Please refer to this article on creating custom maps.
Map series
Section titled “Map series”The map differs from other chart types in such regard that it does not use standalone data. All of its data is passed in using various types of MapSeries, like MapPolygonSeries for configuring map geographical areas, such as countries.
We add series to map by instantiating an object out of the proper series class, then pushing them into map’s series list.
Polygon (area) series
Section titled “Polygon (area) series”Creating polygon series
Section titled “Creating polygon series”Map areas (countries, states, etc.) are represented by objects MapPolygonSeries.
Unless you like empty maps, a map has to have one or more polygon series, so it knows what to draw. Let’s create one:
let polygonSeries = new am4maps.MapPolygonSeries();map.series.push(polygonSeries);var polygonSeries = new am4maps.MapPolygonSeries();map.series.push(polygonSeries);var map = am4core.createFromConfig({ // ... map config "series": [{ "type": "MapPolygonSeries" }]}, "chartdiv", am4maps.MapChart);Loading series data
Section titled “Loading series data”Now, for the series to be really useful, we need data for it. The data can either come from the map definition (GeoJSON), be set directly to series via its data property, or, like you will see in a moment, combined from the two sources.
Obviously, for polygon series we need the geographical data defining the curvature of their outlines. While you can add that directly to data it would be super inconvenient.
For this job we have a handy series’ setting: useGeodata. Set it to true and it will load data for all polygons available in map definition we have already loaded earlier in this article.
let polygonSeries = new am4maps.MapPolygonSeries();polygonSeries.useGeodata = true;map.series.push(polygonSeries);var polygonSeries = new am4maps.MapPolygonSeries();polygonSeries.useGeodata = true;map.series.push(polygonSeries);var map = am4core.createFromConfig({ // ... map config "series": [{ "type": "MapPolygonSeries", "useGeodata": true }]}, "chartdiv", am4maps.MapChart);Let’s see how this looks like:
Configuring series
Section titled “Configuring series”Like with most series in amCharts 4, we configure appearance and behavior of its items by accessing templates.
Template for polygon services resides in its mapPolygons property. The series’ mapPolygons.template is a “template” object of type MapPolygon. You can set any property there and it will be automatically applied to all map polygons in the series.
To make the map more interactive let’s set tooltipText so that it would display a tooltip, as well as fill to exchange bland gray into something more lively, and finally, add a slightly darker shade for hovered polygons by adding “hover” state:
// Configure serieslet polygonTemplate = polygonSeries.mapPolygons.template;polygonTemplate.tooltipText = "{name}";polygonTemplate.fill = am4core.color("#74B266");
// Create hover state and set alternative fill colorlet hs = polygonTemplate.states.create("hover");hs.properties.fill = am4core.color("#367B25");// Configure seriesvar polygonTemplate = polygonSeries.mapPolygons.template;polygonTemplate.tooltipText = "{name}";polygonTemplate.fill = am4core.color("#74B266");
// Create hover state and set alternative fill colorvar hs = polygonTemplate.states.create("hover");hs.properties.fill = am4core.color("#367B25");{ // ... map config "series": [{ "type": "MapPolygonSeries", "useGeodata": true, "mapPolygons": { "tooltipText": "{name}", "fill": "#74B266", "states": { "hover": { "properties": { "fill": "#367B25" } } } } }]}Let’s see how it turned out:
Object identifiers
Section titled “Object identifiers”Before we go any further, let’s stop for a moment to talk about object identifiers.
Most of amCharts maps use ISO2 standard for ID’ing countries and their states.
For multi-country maps, this means all polygons will have a two letter code, e.g. “US” for United States, “FR” for France, and so on.
Country subdivisions use the following identification principle: [ISO2 country code]-[ISO state code]. E.g. “US-NY” for New York state.
Including and excluding objects
Section titled “Including and excluding objects”OK, but what if I need only few objects out of the whole map? Or I want to get rid of a few items?
For that series have include and exclude arrays. If you add any object ids to include the series will only load those objects and ignore anything else.
If you add some ids to exclude, the series will not load data for those specific objects from map definition.
To illustrate the point, let’s get rid of Antarctica from our map. (Antarctica’s ISO2 code is “AQ” by the way)
polygonSeries.exclude = ["AQ"];polygonSeries.exclude = ["AQ"];{ // ... map config "series": [{ "type": "MapPolygonSeries", // ... "exclude": ["AQ"] }]}Similarly, if I wanted to include only certain countries, I could use include instead:
polygonSeries.include = ["PT", "ES", "FR", "DE", "BE", "NL", "IT", "AT", "GB", "IE", "CH", "LU"];polygonSeries.include = ["PT", "ES", "FR", "DE", "BE", "NL", "IT", "AT", "GB", "IE", "CH", "LU"];{ // ... map config "series": [{ "type": "MapPolygonSeries", // ... "include": ["PT", "ES", "FR", "DE", "BE", "NL", "IT", "AT", "GB", "IE", "CH", "LU"] }]}The above will automatically include countries from the Western Europe, and will position/zoom map to accommodate that:
In-line data
Section titled “In-line data”Nice, so why do we need data at all?
If all we require is to display a map like the one above, we don’t. However, most often we’ll need to attach some additional data to polygons that is not available in the GeoJSON.
For example, we can add values to countries for a heat map. Or, override a country name. Or, set a different rollover information, color, you name it.
That’s where data comes in.
Just like with regular charts, map series’ data is an array of objects. Each object represents a map object of some type - as dictated by series type.
[{ "id": "US", "name": "United States", "value": 100}, { "id": "FR", "name": "France", "value": 50}]With a data like above the series will now associate value 100 with the U.S. and 50 with France.
Now, since I have additional data attached to those countries, I can use it in a number of ways, like display it in tooltip:
polygonTemplate.tooltipText = "{name}: {value}";polygonTemplate.tooltipText = "{name}: {value}";{ // ... map config "series": [{ "type": "MapPolygonSeries", // ... "mapPolygons": { "tooltipText": "{name}: {value}", // ... } }]}
Binding properties to data
Section titled “Binding properties to data”Series’ data can be used in a number of ways, not just displaying additional data in a tooltip.
Among such advanced usage is the capability to bind object properties to fields in data.
For that we can use template’s propertyFields object. Basically, every entry in that object will tell: look for value in related data item using this key and apply to this property.
Say we want to enhance our previous demo to apply different fill colors to the U.S. and France.
polygonSeries.data = [{ "id": "US", "name": "United States", "value": 100, "fill": am4core.color("#F05C5C")}, { "id": "FR", "name": "France", "value": 50, "fill": am4core.color("#5C5CFF")}];
polygonTemplate.propertyFields.fill = "fill";polygonSeries.data = [{ "id": "US", "name": "United States", "value": 100, "fill": am4core.color("#F05C5C")}, { "id": "FR", "name": "France", "value": 50, "fill": am4core.color("#5C5CFF")}];
polygonTemplate.propertyFields.fill = "fill";{ // ... map config "series": [{ "type": "MapPolygonSeries", "data": [{ "id": "US", "name": "United States", "value": 100, "fill": am4core.color("#F05C5C") }, { "id": "FR", "name": "France", "value": 50, "fill": am4core.color("#5C5CFF") }], // ... "mapPolygons": { "tooltipText": "{name}: {value}", // ... "propertyFields": { "fill": "fill" } } }]}Custom polygon series
Section titled “Custom polygon series”It is possible to create completely custom shapes on the map by providing inline data for their corners. For more info and examples, please refer to “Custom polygons on Map Chart” tutorial.
Note about data mutation
Section titled “Note about data mutation”MapPolygonSeries is unique in that it will modify the array assigned to its data. So if you’re using that array elsewhere in your app, you’ll want to assign a copy of the array. Here’s a simple way of doing this if there are no custom objects (like am4core.color) in your array:
polygonSeries.data = JSON.parse(JSON.stringify(dataArray));polygonSeries.data = JSON.parse(JSON.stringify(dataArray));{ // ... "data": JSON.parse(JSON.stringify(dataArray))}Multiple polygon series
Section titled “Multiple polygon series”When creating a map, you are not limited to using just one polygon series. Depending on your data structure and target functionality, you may add as many series as you need.
Each series, can reuse the same geodata, but use different include.
The following example shows how we can build a functional map of regions of Europe:
Overlaying multiple maps
Section titled “Overlaying multiple maps”It is possible to have several actual maps (geodata) to be shown on a single map instance.
Up until now, we have been using single geodata (or geodataSource) on the chart object.
It is also possible to set geodata (or geodataSource) directly on the Map series. Since we can have multiple Polygon series, we can have multiple maps nicely overlaid over each other, allowing the creation of nested setups.
As an example, let’s create a World map with a US state map overlaid over it, creating a World map with US states.
/** * Series for the World map * It will use `chart.geodata` */let worldSeries = chart.series.push(new am4maps.MapPolygonSeries());worldSeries.exclude = ["AQ"];worldSeries.useGeodata = true;
/** * Series for the U.S. map * It will use its own `geodata` */let usaSeries = chart.series.push(new am4maps.MapPolygonSeries());usaSeries.geodata = am4geodata_usaLow/** * Series for the World map * It will use `chart.geodata` */var worldSeries = chart.series.push(new am4maps.MapPolygonSeries());worldSeries.exclude = ["AQ"];worldSeries.useGeodata = true;
/** * Series for the U.S. map * It will use its own `geodata` */var usaSeries = chart.series.push(new am4maps.MapPolygonSeries());usaSeries.geodata = am4geodata_usaLow{ // ... map config "series": [{ "type": "MapPolygonSeries", "useGeodata": true, // ... }, { "type": "MapPolygonSeries", "geodata": "usaLow" // ... }]}Here’s how it turned out:
Sorting polygons
Section titled “Sorting polygons”By default, polygons in the series are automatically sorted by size. This is done to ensure that smaller polygons are not obstructed by larger ones.
This also affects their selection when focusing polygons using TAB key (accessibility feature). In this case ordering by size might seem pretty random to user.
Please refer to the tutorial “Ordering map polygons” for further information.
Image series
Section titled “Image series”Image series is used to add any visual object (let’s call them markers) anywhere on the map. And by “any object” we literally mean anything.
It could be something as simple as a circle, a label, or image. Or it could be a full-fledged chart. As long as the object is a descendant of Sprite class (which describes almost any visual element in amCharts 4), it can be used as a marker in maps.
Technically, GeoJSON format allows incorporating images and can be loaded using useGeodata. However, it’s not very common, so, for the purpose of this article, we’ll be using just data.
Image object
Section titled “Image object”As we briefly mentioned in the intro to this section, a map marker can be anything. It’s represented by a MapImage class (which essentially is an empty container you can add anything to). Map images are collected into MapImageSeries.
Creating image series
Section titled “Creating image series”Image series is created like any other series, by instantiating of a proper class, in this case MapImageSeries.
let imageSeries = chart.series.push(new am4maps.MapImageSeries());var imageSeries = chart.series.push(new am4maps.MapImageSeries());{ // ... map config "series": [{ "type": "MapPolygonSeries", // ... }, { "type": "MapImageSeries", // ... }]}Configuring image series
Section titled “Configuring image series”Once the series is created, we can configure it and assign data.
There are two important configuration aspects in images series: data fields and appearance.
Just like with other map series, we configure using “template” object. As we mentioned earlier, it holds a MapImage object, which, essentially, is an empty container, you can add anything in it. To start off simple, let’s add a basic Circle to it.
let imageSeriesTemplate = imageSeries.mapImages.template;let circle = imageSeriesTemplate.createChild(am4core.Circle);circle.radius = 4;circle.fill = am4core.color("#B27799");circle.stroke = am4core.color("#FFFFFF");circle.strokeWidth = 2;circle.nonScaling = true;circle.tooltipText = "{title}";var imageSeriesTemplate = imageSeries.mapImages.template;var circle = imageSeriesTemplate.createChild(am4core.Circle);circle.radius = 4;circle.fill = am4core.color("#B27799");circle.stroke = am4core.color("#FFFFFF");circle.strokeWidth = 2;circle.nonScaling = true;circle.tooltipText = "{title}";{ // ... map config "series": [{ "type": "MapPolygonSeries", // ... }, { "type": "MapImageSeries", "mapImages": { "children": [{ "type": "Circle", "radius": 4, "stroke" : "#B27799", "strokeWidth": 2, "nonScaling": true, "tooltipText": "{title}" }] } }]}Note, that radius, stroke, and other properties are Circle properties. Other objects will have their own properties.
Now, whenever a series is creating a new marker, it will create purple circle with a white outline.
Binding marker properties to data
Section titled “Binding marker properties to data”What we also need to do, is to bind specific images properties to data.
As we saw with polygon series, we bind series’ items’ properties to data using propertyFields.
So, for example, if we want to bind latitude and longitude properties to “latitude” and “longitude” keys in data, we’ll do something like this:
imageSeriesTemplate.propertyFields.latitude = "latitude";imageSeriesTemplate.propertyFields.longitude = "longitude";imageSeriesTemplate.propertyFields.latitude = "latitude";imageSeriesTemplate.propertyFields.longitude = "longitude";{ // ... map config "series": [{ "type": "MapPolygonSeries", // ... }, { "type": "MapImageSeries", // ... "mapImages": { "propertyFields": { "latitude": "latitude", "longitude": "longitude" } } }]}Image data
Section titled “Image data”Now, all we need to do is to top it off with actual data.
Let’s add a marker for Paris (48.856614, 2.352222) to New York City (40.712775, -74.005973) to Vancouver (49.282729, -123.120738).
imageSeries.data = [{ "latitude": 48.856614, "longitude": 2.352222, "title": "Paris"}, { "latitude": 40.712775, "longitude": -74.005973, "title": "New York"}, { "latitude": 49.282729, "longitude": -123.120738, "title": "Vancouver"}];imageSeries.data = [{ "latitude": 48.856614, "longitude": 2.352222, "title": "Paris"}, { "latitude": 40.712775, "longitude": -74.005973, "title": "New York"}, { "latitude": 49.282729, "longitude": -123.120738, "title": "Vancouver"}];{ // ... map config "series": [{ "type": "MapPolygonSeries", // ... }, { "type": "MapImageSeries", // ... "data": [{ "latitude": 48.856614, "longitude": 2.352222, "title": "Paris" }, { "latitude": 40.712775, "longitude": -74.005973, "title": "New York" }, { "latitude": 49.282729, "longitude": -123.120738, "title": "Vancouver" }] }]}And, let’s see how it went:
Using actual images
Section titled “Using actual images”Please refer to “Using actual images in MapImageSeries” tutorial for info and examples on how you can use various images for map markers.
Line series
Section titled “Line series”The map can draw a number of lines connecting two or more points. They are assembled into line series.
Lines can be straight (MapLineSeries), arched (MapArcSeries), or smoothely curved (MapSplineSeries; please note for lines to be curved they must pass through at least 3 points).
As with polygons and images, lines can also be loaded from map definition (GeoJSON) using useGeodata, although is much less common. In the course of this article we’ll be adding lines using just in-line data.
Line object
Section titled “Line object”Probably the most common way to draw a line is to fill its multiGeoLine array with “geo point” coordinates. A geo point is a simple object with two properties: latitude and longitude.
Each geo point in array represents a line point. There should be at least two. If you have more than two, the series will draw multi-segment line.
Let’s draw a line connecting our three cities from the images - Paris, New York City, and Vancouver.
[{ "multiGeoLine": [ [ { "latitude": 48.856614, "longitude": 2.352222 }, { "latitude": 40.712775, "longitude": -74.005973 }, { "latitude": 49.282729, "longitude": -123.120738 } ] ]}]The above defines data for map line series. It contains one data for one line.
Notice, how the multiGeoLine is an array of array. This is not a mistake but rather clever way (inherited from GeoJSON standard) to define multiple clusters of multi-segmented lines.
This can be used if you want to introduce a break in the multi-segmented line, i.e. some points to not be connected by an actual line:
[{ "multiGeoLine": [ [ // points for the first cluster of segments ], [ // points for the second cluster of segments ], [ // points for the third cluster of segments ] ]}]Creating line series
Section titled “Creating line series”Line series is created just like polygon series:
let lineSeries = chart.series.push(new am4maps.MapLineSeries());lineSeries.data = [{ "multiGeoLine": [ [ { "latitude": 48.856614, "longitude": 2.352222 }, { "latitude": 40.712775, "longitude": -74.005973 }, { "latitude": 49.282729, "longitude": -123.120738 } ] ]}];var lineSeries = chart.series.push(new am4maps.MapLineSeries());lineSeries.data = [{ "multiGeoLine": [ [ { "latitude": 48.856614, "longitude": 2.352222 }, { "latitude": 40.712775, "longitude": -74.005973 }, { "latitude": 49.282729, "longitude": -123.120738 } ] ]}];{ // ... map config "series": [{ "type": "MapPolygonSeries", // ... }, { "type": "MapLineSeries", "data": [{ "multiGeoLine": [ [ { "latitude": 48.856614, "longitude": 2.352222 }, { "latitude": 40.712775, "longitude": -74.005973 }, { "latitude": 49.282729, "longitude": -123.120738 } ] ] }] }]}Let’s see if we have a line connecting our three awesome cities:
Configuring line appearance
Section titled “Configuring line appearance”As with other map object series, Line series elements are configured via series’ templates.
In this case it’s the mapLines.template.line object.
Notice we are using line property of the template. Regardless of what type of line series we have, the template will contain a lineproperty, which will have an actual line element of the relative type, we can use to configure appearance of.
For MapLineSeries it’ll be an object of type Polyline. For MapArcSeries it’ll be a Polyarc, etc.
You can set properties like stroke (line color), strokeWidth (line thickness in pixels), strokeOpacity (line opacity), strokeDasharray (to make line dashed), and some other settings. E.g.:
lineSeries.mapLines.template.line.stroke = am4core.color("#5C5CFF");lineSeries.mapLines.template.line.strokeOpacity = 0.5;lineSeries.mapLines.template.line.strokeWidth = 4;lineSeries.mapLines.template.line.strokeDasharray = "3,3";lineSeries.mapLines.template.line.stroke = am4core.color("#5C5CFF");lineSeries.mapLines.template.line.strokeOpacity = 0.5;lineSeries.mapLines.template.line.strokeWidth = 4;lineSeries.mapLines.template.line.strokeDasharray = "3,3";{ // ... map config "series": [{ "type": "MapPolygonSeries", // ... }, { "type": "MapLineSeries", // ... "mapLines": { "line": { "stroke": "#5C5CFF", "strokeOpacity": 0.5, "strokeWidth": 4, "strokeDasharray": "3,3" } } }]}Curved vs. straight lines
Section titled “Curved vs. straight lines”By now, you might be wondering why our “lines” are not straight, but rather slightly curved.
The answer is simple - they follow the curvature of the Earth. While they might look like they are taking longer route between point A and point B on screen, in reality they plot the shortest course between the two points.
The distortion comes in because the actual flattened map introduces certain modifications.
If you’d like your lines look straight on screen, simply set line’s (or MapLineSeries template’s) shortestDistance = false.

Using images as line points
Section titled “Using images as line points”If your lines are connecting actual markers (map images), you can skip the multiGeoLine property, and use imagesToConnect instead.
This property is an array of references to MapImage objects. E.g.:
let paris = imageSeries.mapImages.create();paris.latitude = 48.856614;paris.longitude = 2.352222;
let nyc = imageSeries.mapImages.create();nyc.latitude = 40.712776;nyc.longitude = -74.005973;
let paris_nyc = lineSeries.mapLines.create();paris_nyc.imagesToConnect = [paris, nyc];var paris = imageSeries.mapImages.create();paris.latitude = 48.856614;paris.longitude = 2.352222;
var nyc = imageSeries.mapImages.create();nyc.latitude = 40.712776;nyc.longitude = -74.005973;
var paris_nyc = lineSeries.mapLines.create();paris_nyc.imagesToConnect = [paris, nyc];Line series types
Section titled “Line series types”Built-in separator lines
Section titled “Built-in separator lines”Some maps have separator line information built-in. Please refer to “Maps with separator lines” tutorial for more info.
Additional controls
Section titled “Additional controls”As with any chart type, Map chart allows adding some specific controls.
Zoom control
Section titled “Zoom control”Adding zoom control, is as simple as instantiating an object of ZoomControl class and assigning it to map’s zoomControl property:
chart.zoomControl = new am4maps.ZoomControl();chart.zoomControl = new am4maps.ZoomControl();{ // ... map config "zoomControl": { // No need to set `type` here. // There's only one type of zoom control, so the map // will be able to figure it out by itself. // ... }}Please refer to the reference of ZoomControl for available configuration options.
Normally zoom control would show only zoom in and out buttons.
It can also show a neat slider in-between. To enable it simply set its height:
chart.zoomControl.slider.height = 100;chart.zoomControl.slider.height = 100;{ // ... map config "zoomControl": { "slider": { "height": 100 } }}Small map
Section titled “Small map”Same is with a small map - a tiny control that will display a mini version of the full map as well as highlight your current view port.
However, before it can be useful, we need to configure it, by providing what to show in it. Since it is essentially a small version of the map, we need to assign map series to small map to show.
If we wanted, we could create and show any new series in the small map. For simplicity, let’s just replicate our polygon series we already have on the map.
chart.smallMap = new am4maps.SmallMap();chart.smallMap.series.push(polygonSeries);chart.smallMap = new am4maps.SmallMap();chart.smallMap.series.push(polygonSeries);{ // ... map config "series": [{ "type": "MapPolygonSeries" "id": "s1", // setting id so we can reference it in `smallMap` // ... }], "smallMap": { "series": ["s1"] }}Please refer to the reference of SmallMap for available configuration options.
Full map demo
Section titled “Full map demo”Geographical heat-maps
Section titled “Geographical heat-maps”The map can automatically set filll color (and any other quantitative value for that matter) based on some arbitrary value, which allows us creating geographical heat-maps.
The functionality, which is not limited to just maps, is called “heat rules”, or (heatRules) and can be applied to any series, including series for map objects.
REQUIRED READING There’s a special section “Heat-maps” in our Series tutorial. Make sure you read it, because we’ll spare you the details here.
We’re going to assume you did read the Series article section linked above. Now, let’s put this into the context of maps.
Creating heat rules
Section titled “Creating heat rules”Creating heat rules for map series is identical to creating heat rules for any series: you just specify start and end values, as well as the property, and what object, you need to be controlled by the rule:
polygonSeries.heatRules.push({ "property": "fill", "target": polygonSeries.mapPolygons.template, "min": am4core.color("#ffffff"), "max": am4core.color("#AAAA00")});polygonSeries.heatRules.push({ "property": "fill", "target": polygonSeries.mapPolygons.template, "min": am4core.color("#ffffff"), "max": am4core.color("#AAAA00")});{ // ... "series": [{ "type": "MapPolygonSeries", // ... "heatRules": [{ "target": "mapPolygons.template", "property": "fill", "min": "#ffffff", "max": "#AAAA00" }] }]}This basically instructs the series to “check "value" field in data for each polygon, and color it between white and greenish based on where that value stands in the spectrum of the whole range of values in the series”.
Here’s how it turned out:
Logarithmic rules
Section titled “Logarithmic rules”Regular heat rules might work when you have values of comparable scale. However, should there be even one value that is significantly larger than other values, it would skew the whole heat map, since all other values would look very small in comparison, and thus would use basically the same color.
That’s where logarithmic rules come in.
If logarithmic: true is set for the rule, the value’s “mass” in the whole spectrum would be calculated not linear but logarithmic scale.
polygonSeries.heatRules.push({ "property": "fill", "target": polygonSeries.mapPolygons.template, "min": am4core.color("#ffffff"), "max": am4core.color("#AAAA00"), "logarithmic": true});polygonSeries.heatRules.push({ "property": "fill", "target": polygonSeries.mapPolygons.template, "min": am4core.color("#ffffff"), "max": am4core.color("#AAAA00"), "logarithmic": true});{ // ... "series": [{ "type": "MapPolygonSeries", // ... "heatRules": [{ "target": "mapPolygons.template", "property": "fill", "min": "#ffffff", "max": "#AAAA00", "logarithmic": true }] }]}

As you can see in the first example above, just a few large-value polygons, makes difference between the rest of them nearly indistinguishable.
Enabling logarithmic scale fixes that.
Live example:
Adding a heat legend
Section titled “Adding a heat legend”You know what goes well with a heat-map? Yes, a Heat Legend.
It’s a special kind of Legend, that instead of showing individual items, shows a spectrum of colors, available in some selected series.
REQUIRED READING Again, we’ll have to drop some required reading on you, as we have a dedicated “Heat Legend”.
There’s absolutely nothing special with adding Head Legend to a map: you create an instance of HeatLegend, you add it to one of the chart containers as a child, you hook the legend up with your map series, so that it knows where to get its data from:
let heatLegend = chart.createChild(am4maps.HeatLegend);heatLegend.series = polygonSeries;heatLegend.width = am4core.percent(100);var heatLegend = chart.createChild(am4maps.HeatLegend);heatLegend.series = polygonSeries;heatLegend.width = am4core.percent(100);{ // ... "series": [{ "id": "s1", // ... }], // ... "children": [{ "type": "HeatLegend", "series": "s1", "width": "100%" }]}And, if we’re especially generous with our users, we might also add some interactivity, so that when user rolls over some polygon, we show where in the overall spectrum of values it stands:
polygonSeries.mapPolygons.template.events.on("over", function(ev) { if (!isNaN(ev.target.dataItem.value)) { heatLegend.valueAxis.showTooltipAt(ev.target.dataItem.value) } else { heatLegend.valueAxis.hideTooltip(); }});
polygonSeries.mapPolygons.template.events.on("out", function(ev) { heatLegend.valueAxis.hideTooltip();});polygonSeries.mapPolygons.template.events.on("over", function(ev) { if (!isNaN(ev.target.dataItem.value)) { heatLegend.valueAxis.showTooltipAt(ev.target.dataItem.value) } else { heatLegend.valueAxis.hideTooltip(); }});
polygonSeries.mapPolygons.template.events.on("out", function(ev) { heatLegend.valueAxis.hideTooltip();});Let’s see how that went:
Adding a custom legend
Section titled “Adding a custom legend”You can also add a Legend with custom items in it. For more details and a working demo, check out “Adding custom Items” section in our “Legend” article.
Using objects
Section titled “Using objects”So far we have shown you how to use map series’ data property to add map objects like polygons, lines and markers.
It’s a convenient way to add a lot of data to map. However, there’s another way - creating objects of appropriate type, then setting their properties directly.
Since you’re setting properties directly on the object, there’s no need to map fields like “latitude” and “longitude” to data. That’s the most valuable advantage of using objects over data.
Creating series object
Section titled “Creating series object”The most common way to create an object in the series is to use its create() method:
let france = polygonSeries.mapPolygons.create();france.id = "FR";
let paris = imageSeries.mapImages.create();paris.latitude = 48.856614;paris.longitude = 2.352222;
let nyc = imageSeries.mapImages.create();nyc.latitude = 40.712776;nyc.longitude = -74.005973;
let paris_nyc = lineSeries.mapLines.create();paris_nyc.imagesToConnect = [paris, nyc];var france = polygonSeries.mapPolygons.create();france.id = "FR";
var paris = imageSeries.mapImages.create();paris.latitude = 48.856614;paris.longitude = 2.352222;
var nyc = imageSeries.mapImages.create();nyc.latitude = 40.712776;nyc.longitude = -74.005973;There is no need to push() newly created objects into series - create() will take care of that.
Calling create() will create a new object out of the series template, like we’ve shown you earlier. So you might want to configure templates before starting creating objects.
Specifying map line points directly
Section titled “Specifying map line points directly”If you didn’t specify line series points via series data, or do not have images which you can use in imagesToConnect, you can set line’s multiGeoLine property directly:
let line = lineSeries.mapLines.create();
line.multiGeoLine = [[ { "latitude": 48.856614, "longitude": 2.352222 }, { "latitude": 40.712775, "longitude": -74.005973 }, { "latitude": 49.282729, "longitude": -123.120738 }]];var line = lineSeries.mapLines.create();
line.multiGeoLine = [[ { "latitude": 48.856614, "longitude": 2.352222 }, { "latitude": 40.712775, "longitude": -74.005973 }, { "latitude": 49.282729, "longitude": -123.120738 }]];{ // ... "series": [{ "type": "MapLineSeries", "mapLines": [{ "multiGeoLine": [[ { "latitude": 48.856614, "longitude": 2.352222 }, { "latitude": 40.712775, "longitude": -74.005973 }, { "latitude": 49.282729, "longitude": -123.120738 } ]] }] }]}Controlling scaling of map objects
Section titled “Controlling scaling of map objects”By default all objects on the map - polygons, images, and lines - will scale in size when you zoom in the map.
To disable such scaling we do have to configure relative templates.
To prevent map images from scaling we have to set nonScaling = true on the template of the map images.
Similarly, to prevent map lines to getting thicker with zoom, we can set nonScalingStroke = true on their respective templates.
imageSeries.mapImages.template.nonScaling = true;// ...lineSeries.mapImages.template.nonScalingStroke = true;imageSeries.mapLines.template.nonScaling = true;// ...lineSeries.mapLines.template.nonScalingStroke = true;{ // ... "series": [{ "type": "MapImageSeries", // ... "mapImages": { // ... "nonScaling": true } }, { "type": "MapLineSeries", // ... "mapLines": { // ... "nonScalingStroke": true } }]}Map background
Section titled “Map background”In some cases, you might want to to give your map a background.
Static fill
Section titled “Static fill”Sure, you can give your whole Map Chart a fill background using its background property:
chart.background.fill = am4core.color("#aadaff");chart.background.fillOpacity = 1;chart.background.fill = am4core.color("#aadaff");chart.background.fillOpacity = 1;{ // ... "background": { "fill": "#aadaff", "fillOpacity": 1 }}However, that would fill the whole area of the map, which in some projections could look a bit off:

Background series
Section titled “Background series”The solution is to use “background series”, that are readily accessible via map’s backgroundSeries property.
It holds a Series of MapPolygonSeries so you can configure its appearance just like a regular Polygon series.
The difference is that it will automatically add a polygon, shaped exactly as the map, so you don’t have to add any data into it - just configure its polygon template:
chart.backgroundSeries.mapPolygons.template.polygon.fill = am4core.color("#aadaff");chart.backgroundSeries.mapPolygons.template.polygon.fillOpacity = 1;chart.backgroundSeries.mapPolygons.template.polygon.fill = am4core.color("#aadaff");chart.backgroundSeries.mapPolygons.template.polygon.fillOpacity = 1;{ // ... "backgroundSeries": { "mapPolygons": { "polygon": { "fill": "#aadaff", "fillOpacity": 1 } } }}
Better, isn’t it?
Map rotation
Section titled “Map rotation”Since version 4.3.0 Map Chart supports rotation around any of its axes: vertical (Y), horizontal (X), and perpendicular (Z).
While the map starts off centered perfectly - like you are used to seeing the maps - it can be rotated in any of the directions around any of its axes using deltaLongitude, deltaLatitude, and deltaGamma properties respectively.



Setting map center
Section titled “Setting map center”Want to create a Pacific-centered map? Or just about any-longitude-centered?
You can use MapChart’s deltaLongitude property to set longitude offset when centering the map.
Basically, in layman’s terms, it means “move the map center by X degrees”.
If longitude zero is center by default, and we set deltaLongitude = -160 the longitude 20 will be our new center, creating a Pacific-centered map:
chart.deltaLongitude = -160;chart.deltaLongitude = -160;{ // ... map config "deltaLongitude": -160}Rotating latitude
Section titled “Rotating latitude”Want to show a map of Antarctica? Rotate it along it’s horizontal axis using deltaLatitude:
chart.deltaLatitude = 90;chart.deltaLatitude = 90;{ // ... map config "deltaLatitude": 90}Map grid
Section titled “Map grid”Since version 4.3.0 Map Chart supports automatic grid.
The grid consists of a vertical and horizontal grid lines - let’s call it “graticules” - drawn every X latitude and longitude.
They also follow map projection, so they give the whole map “shape”.
Enabling grid
Section titled “Enabling grid”To enable grid, simply add an additional series of type GraticuleSeries, just like you would add any other series.
let grid = chart.series.push(new am4maps.GraticuleSeries());grid.toBack();var grid = chart.series.push(new am4maps.GraticuleSeries());grid.toBack();{ // ... "series": [{ // ... }, { "type": "GraticuleSeries", "zIndex": -1 }]}This will cause a simple gray lines drawn every 10 longitude and latitude.
Configuring grid
Section titled “Configuring grid”If you’re happy with how default grid looks, you can leave it at that. But if you’d like to give it some personal touch, there’s plenty of options.
GraticuleSeries is in a nutshell a version of MapLineSeries.
This means that we can configure the appearance of the lines just like we would on a regular Map line series: via its mapLines.template property.
GraticuleSeries automatically creates the lines for us, so we don’t need to add any data to it.
grid.mapLines.template.line.stroke = am4core.color("#e33");grid.mapLines.template.line.strokeOpacity = 0.2;grid.mapLines.template.line.stroke = am4core.color("#e33");grid.mapLines.template.line.strokeOpacity = 0.2;{ // ... "series": [{ // ... }, { "type": "GraticuleSeries", "zIndex": -1, "mapLines": { "line": { "stroke": "#e33", "strokeOpacity": 0.2 } } }]}Besides regular Map line series settings, you also have specific settings that are relevant only to the grid. Such as longitudeStep and latitudeStep.
Those, as the name suggests, define density of the grid, e.g. draw a graticule every X longitude or latitude.
grid.longitudeStep = 20;grid.latitudeStep = 20;grid.longitudeStep = 20;grid.latitudeStep = 20;{ // ... "series": [{ // ... }, { "type": "GraticuleSeries", // ... "longitudeStep": 20, "latitudeStep": 20 }]}Extent of the grid
Section titled “Extent of the grid”By default, map grid (or GraticuleSeries) will automatically cover the area occupied by other Polygon series.
If we’d rather span it whole area of the world, we can disable this “cropping” using its fitExtent property:
grid.fitExtent = false;grid.fitExtent = false;{ // ... "series": [{ // ... }, { "type": "GraticuleSeries", // ... "fotExtent": false }]}Interacting with map
Section titled “Interacting with map”By default, when you drag the map by mouse or touch, it will just move the whole of it as it is.
Using Map Chart’s panBehavior setting we can modify this functionality according to our needs.
Available settings are as follows:
"move"(default) - moves the whole of the map without modifying it."rotateLong"- rotates the map using its vertical axis, same as changingdeltaLongitude."rotateLat"- rotates the map using its horizontal axis, same as changingdeltaLatitude."rotateLongLat"- rotates the map freely around both of its axes, same as changingdeltaLongitudeanddeltaLatitude.




So, if we’d like to allow our users to rotate globe in any direction, we’d just do this:
chart.panBehavior = "rotateLongLat";chart.panBehavior = "rotateLongLat";{ "panBehavior": "rotateLongLat"}Related content
Section titled “Related content”Zooming
Section titled “Zooming”- Setting initial zoom and position of a Map chart
- Zooming to map area on click
- Disabling zoom and pan on a Map Chart
- Resetting Map Position/Zoom
- Pre-zooming Map to a Country
Interacting
Section titled “Interacting”- Retrieving information of a clicked Map Object
- Retrieving latitude/longitude of map click
- Consistent outlines of Map Polygons on hover
Projection-related
Section titled “Projection-related”Other topics
Section titled “Other topics”- Configuring Small Map
- Maps with separator lines
- Adding “Home” Button to Map Chart
- Creating custom maps
- Automatic labels over map polygons
- Using map country name translations
- All map-related tutorials
- County maps
- Combining map polygon series to create grouped outlines
- Using U.S. congressional districts maps
- Exporing MapChart data
- Country-specific world maps










