If you're a budding developer or data enthusiast eager to explore map creation, you're in the right place. In this tutorial, I'll guide you through how to easily build an interactive point map using JavaScript.
To make things even more interesting, I’ll use data on the number of millionaires in cities as an example. By the end of this tutorial, you'll have made a visually stunning JS point map, ready to tell the story of the world’s wealthiest cities and a solid understanding of applying these skills to any data and scenarios. And stick around for a bonus at the end — I'll also show you how to transform your point map into abubble map where the size of the markers conveys additional information.
Ready to master the JS point mapping technique? Let's get started, and don't forget to join me in the bonus section where size matters!
As we delve into the realm of building an interactive JavaScript-based point map, it's essential to grasp the concept of point maps and their multifaceted role. A point map is a form of data visualization that represents data on a map using markers (dots), also known as a marker map and a dot map, offering a concise and potent method for showcasing spatial information. The purpose of point maps lies in their ability to reveal spatial patterns, provide an intuitive understanding of data distribution, facilitate comparisons, and uncover localized insights.
Beyond these core purposes, point maps find applications in various fields. They serve various purposes, from analyzing population density in urban planning to studying disease spread in epidemiology, revealing demographic trends in sociology, mapping environmental concentrations in environmental science, to visualizing business analytics data such as customer distribution or market trends.
Building a point map with JS might seem intimidating at first, especially envisioning cities lighting up based on their millionaire count. But fear not! Whether you're a coding novice or a seasoned developer, this guide breaks down the process into manageable steps:
Start by laying the foundation by crafting a basic HTML page for your JS point map. There, add an HTML block-level element into the body, which will be the home for your point map. Assign a unique ID to this element, which will be referenced later when bringing the map to life with JavaScript. Feel free to infuse your creativity with some CSS styling to ensure the map looks exactly how you envision it.
Below is an example of how your HTML page might look. The<div>
element, with an id of "container," ensures the map gracefully spans the entire screen, with its height and width set to 100%. Remember, this is your canvas, and you're encouraged to modify the styling to reflect your personal touch or match the theme of your project.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JavaScript Point Map</title>
<style type="text/css">
html,
body,
#container {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="container"></div>
</body>
</html>
Now that your HTML structure is in place, it’s time to infuse it with the prowess of JavaScript. Include any JS files you are going to use.
For example, think of JavaScript charting libraries as secret ingredients that can help you transform raw data into visually delightful creations with little effort. In this tutorial, I’ll illustrate using one of those listed in thetop JS mapping libraries — AnyChart.
You have two options for adding JavaScript files: downloading them for local use or linking them via CDN. In the code below, the latter is used, embedding links to the required scripts directly in the <head> section of the HTML page. The scripts being included are the necessary modules of the JavaScript charting library, map geodata, and the open-source Proj4js component that converts point coordinates.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JavaScript Point Map</title>
<style type="text/css">
html,
body,
#container {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
</style>
<script src="https://cdn.anychart.com/releases/8.12.0/js/anychart-core.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.12.0/js/anychart-map.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.12.0/geodata/custom/world/world.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.3.15/proj4.js"></script>
</head>
<body>
<div id="container"></div>
</body>
</html>
Ready to illuminate your point map? Fuel it with data! For tutorial illustration purposes, I decided to map data from the World’s Wealthiest Cities Report 2023 by Henley Global, representing millionaire populations of 97 cities across Africa, Australasia, CIS, East Asia, Europe, the Middle East, North America, South Asia, and Southeast Asia. The data is neatly organized in JSON format and readily available on my GitHub Gist.
Delving into the data, it unfolds as a collection of objects, each signifying a city. Within each city object, you'll discover details like the city's name, country, coordinates, and the count of millionaires, centimillionaires, and billionaires.
If you use a script to help you load the data, don’t forget to include it in the<head>
section in addition to the others. Here, I’ll load my JSON using the data adapter script:
<script src="https://cdn.anychart.com/releases/8.12.0/js/anychart-data-adapter.min.js"></script>
Then here’s how the data is loaded:
anychart.data.loadJsonFile("https://gist.githubusercontent.com/awanshrestha/9e91dbd3ac4626446cd84f8be97016ae/raw/160d5ab2da59a5264e716dcbe3239ce7ac9104b6/Wealthiest%2520Cities%25202023.json");
With this, the groundwork is complete! The stage is set for the next exciting phase, where a touch of JavaScript will bring your point map to life.
Before delving into the JS point map charting code, ensure it runs only after the entire web page is loaded. The anychart.onDocumentReady()
function serves this purpose.
<script>
anychart.onDocumentReady(function () {
// The point map data and code will be in this section
});
</script>
After confirming the page load, the first task is to import the data prepared in Step 3.
anychart.onDocumentReady(function() {
anychart.data.loadJsonFile("https://gist.githubusercontent.com/awanshrestha/9e91dbd3ac4626446cd84f8be97016ae/raw/160d5ab2da59a5264e716dcbe3239ce7ac9104b6/Wealthiest%2520Cities%25202023.json",
function(data) {",
// The rest of our code will go here
}
);
});
Now, embark on the map creation.
var map = anychart.map();
After creating a basic map instance, set the geographical data. For this visualization, the world map is used.
map.geoData("anychart.maps.world");
After setting the geodata, map your data using a marker series.
var mapSeries = map.marker(data);
With the data plotted, place your map inside the <div>
container defined in Step 1 and draw the result.
map.container("container");
map.draw();
There you have it! With these lines of JavaScript, a visually appealing point map is crafted, illustrating the world's wealthiest cities based on their millionaire count. Find the interactive version of this map here, where you can experiment with its code further. The entire block of HTML/CSS/JS code constructing this point map is also presented below for your reference.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JavaScript Point Map</title>
<style type="text/css">
html,
body,
#container {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
</style>
<script src="https://cdn.anychart.com/releases/8.12.0/js/anychart-core.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.12.0/js/anychart-map.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.12.0/geodata/custom/world/world.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.3.15/proj4.js"></script>
<script src="https://cdn.anychart.com/releases/8.12.0/js/anychart-data-adapter.min.js"></script>
</head>
<body>
<div id="container"></div>
<script>
anychart.onDocumentReady(function() {
// load data from json
anychart.data.loadJsonFile("https://gist.githubusercontent.com/awanshrestha/9e91dbd3ac4626446cd84f8be97016ae/raw/160d5ab2da59a5264e716dcbe3239ce7ac9104b6/Wealthiest%2520Cities%25202023.json",
function(data) {
// create a map chart
var map = anychart.map();
// set the global map geodata
map.geoData("anychart.maps.world");
// create a marker map series
var series = map.marker(data);
// specify a container
map.container("container");
// draw the resulting map
map.draw();
}
);
});
</script>
</body>
</html>
Now that your JavaScript point map is ready, let me demonstrate a few ways you can refine it to augment its visuals and functionalities, offering users a more immersive experience.
Add a dynamic and visually appealing title to the point map. This step enhances the user experience by providing context and additional information about the map, contributing to a more informative and engaging visual experience.
map.title()
.enabled(true)
.useHtml(true)
.text(
"Wealthiest Cities<br/>" +
"<span style='color: #929292; font-size: 12px;'>" +
"Based on the number of millionaires</span>"
);
To enhance the user's navigation experience, incorporate dedicated zoom controls into the point map. This addition makes it easier for users to navigate large or intricate maps, allowing them to zoom in or out and focus on specific areas effortlessly. Don’t forget to include the necessary scripts/files in the <head>
section if required by your JavaScript charting library.
<script src="https://cdn.anychart.com/releases/8.12.0/js/anychart-ui.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.anychart.com/releases/8.12.0/css/anychart-ui.min.css"/>
<link rel="stylesheet" type="text/css" href="https://cdn.anychart.com/releases/8.12.0/fonts/css/anychart-font.min.css"/>
var zoomController = anychart.ui.zoom();
zoomController.render(map);
Improve the visual appeal of your point map by tweaking default settings and adjusting colors to make the data pop out. Consider changing the color of the land area and background to ensure that the main features of the map remain easily distinguishable.
map.unboundRegions()
.enabled(true)
.fill("#E1E1E1")
.stroke("#D2D2D2”);
map.background()
.fill({
color: "#daedea",
opacity: 0.5
});
Customize the point map’s tooltip to make it more informative and user-friendly. In this case, by default, the tooltip displays the latitude and longitude of a city, which may not be very helpful. The following code example demonstrates how to make the tooltip display the name of the city and country, along with the count of millionaires, centimillionaires, and billionaires.
map.tooltip()
.useHtml(true)
.titleFormat(function () {
return this.getData("name") +
" (" +
this.getData("Country") +
")";
})
.format(function () {
return (
"<span style='color: #bfbfbf'>Millionaires: </span>" +
this.getData("Millionaires") +
"<br/>" +
"<span style='color: #bfbfbf'>Centimillionaires: </span>" +
this.getData("Centimillionaires") +
"<br/>" +
"<span style='color: #bfbfbf'>Billionaires: </span>" +
this.getData("Billionaires")
);
});
This is going to be a very interesting customization, where the dots in your point map can be colored according to the associated values. When it comes to the data being visualized in this tutorial, cities will be colored based on the number of millionaires they harbor. This necessitates adjustments to the existing code, particularly in how datasets and series are handled.
Begin by creating a dataset.
var citiesDataSet = anychart.data.set(data).mapAs();
Next, create a helper function that takes in the name, data, color, and size and creates a series based on those parameters. The function below initially creates a series based on the provided data and then proceeds to configure the series settings for visuals.
var createSeries = function (name, data, color) {
// set the marker series
var series = map.marker(data);
// configure the series settings
series
.name(name)
.type("circle")
.fill(color)
.stroke(anychart.color.darken(color, 0.4))
.labels(false);
// configure the series legend items
series
.legendItem()
.iconType("circle")
.iconFill(color)
.iconStroke(anychart.color.darken(color, 0.4))
};
Now, you may wonder what needs to be passed as data in this context. To address this, introduce a new helper, and a data filtering function. In the code below, the filterFunction()
is a higher-order function that returns another function based on its input parameters.
function filterFunction(val1, val2) {
if (val2) {
return function (fieldVal) {
return val1 <= fieldVal && fieldVal < val2;
};
}
return function (fieldVal) {
return val1 <= fieldVal;
};
}
Now, create the series, filtering the data by value.
Below, each series is determined with three parameters. The first is the name; in the first series, for example, the name “Over 100,000” signifies that this group of series includes cities with millionaire numbers over 100,000. The second is the data, where the filter function is employed; before that, “Millionaires” is passed to ensure data filtering for millionaire numbers, considering other parameters in the dataset; the filter function returns the cities with the respective millionaire numbers. Finally, the third parameter is the color.
createSeries(
"Over 100,000",
citiesDataSet.filter("Millionaires", filterFunction(100000, 1000000)),
"#D1FAE9"
);
createSeries(
"50,000–100,000",
citiesDataSet.filter("Millionaires", filterFunction(50000, 100000)),
"#9CE0E5"
);
createSeries(
"10,000–50,000",
citiesDataSet.filter("Millionaires", filterFunction(10000, 50000)),
"#00ACC3"
);
createSeries(
"1,000–10,000",
citiesDataSet.filter("Millionaires", filterFunction(1000, 10000)),
"#355CB1"
);
createSeries(
"Up to 1,000",
citiesDataSet.filter("Millionaires", filterFunction(0, 1000)),
"#002D79"
);
And don’t forget to add a legend, allowing users to understand the significance of each color on the point map.
map.legend(true);
The result is a captivating JavaScript-based point map visualization, providing a unique perspective on the distribution of millionaires. Access the interactive version with the code here, where you can feel free to explore and experiment with the various settings to observe their impact on the visualization. For reference, the complete code for the finalized point map is also provided below.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JavaScript Point Map</title>
<style type="text/css">
html,
body,
#container {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
</style>
<script src="https://cdn.anychart.com/releases/8.12.0/js/anychart-core.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.12.0/js/anychart-map.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.12.0/geodata/custom/world/world.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.3.15/proj4.js"></script>
<script src="https://cdn.anychart.com/releases/8.12.0/js/anychart-data-adapter.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.12.0/js/anychart-ui.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.anychart.com/releases/8.12.0/css/anychart-ui.min.css"/>
<link rel="stylesheet" type="text/css" href="https://cdn.anychart.com/releases/8.12.0/fonts/css/anychart-font.min.css"/>
</head>
<body>
<div id="container"></div>
<script>
anychart.onDocumentReady(function() {
// load data from a json file
anychart.data.loadJsonFile("https://gist.githubusercontent.com/awanshrestha/9e91dbd3ac4626446cd84f8be97016ae/raw/160d5ab2da59a5264e716dcbe3239ce7ac9104b6/Wealthiest%2520Cities%25202023.json",
function(data) {
// create a map chart
var map = anychart.map();
// set the global map geodata
map.geoData("anychart.maps.world");
// add zoom ui controls
var zoomController = anychart.ui.zoom();
zoomController.render(map);
// set the land area color
map.unboundRegions()
.enabled(true)
.fill('#E1E1E1')
.stroke('#D2D2D2');
// set the background color
map.background()
.fill({
color: '#daedea',
opacity: 0.5
});
// create a dataset from data
var citiesDataSet = anychart.data.set(data).mapAs();
// helper function to create several series
var createSeries = function (name, data, color) {
// set the marker series
var series = map.marker(data);
// configure the series settings
series
.name(name)
.type("circle")
.fill(color)
.stroke(anychart.color.darken(color, 0.4))
.labels(false);
// configure the series legend items
series
.legendItem()
.iconType("circle")
.iconFill(color)
.iconStroke(anychart.color.darken(color, 0.4))
};
// create 5 series, filtering the data by number of millionaires
createSeries(
"Over 100,000",
citiesDataSet.filter("Millionaires", filterFunction(100000, 1000000)),
"#D1FAE9"
);
createSeries(
"50,000–100,000",
citiesDataSet.filter("Millionaires", filterFunction(50000, 100000)),
"#9CE0E5"
);
createSeries(
"10,000–50,000",
citiesDataSet.filter("Millionaires", filterFunction(10000, 50000)),
"#00ACC3"
);
createSeries(
"1,000–10,000",
citiesDataSet.filter("Millionaires", filterFunction(1000, 10000)),
"#355CB1"
);
createSeries(
"Up to 1,000",
citiesDataSet.filter("Millionaires", filterFunction(0, 1000)),
"#002D79"
);
// add a legend
map.legend(true);
// configure the map tooltip
map.tooltip()
.useHtml(true)
.titleFormat(function () {
return this.getData("name") +
" (" +
this.getData("Country") +
")";
})
.format(function () {
return (
"<span style='color: #bfbfbf'>Millionaires: </span>" +
this.getData("Millionaires") +
"<br/>" +
"<span style='color: #bfbfbf'>Centimillionaires: </span>" +
this.getData("Centimillionaires") +
"<br/>" +
"<span style='color: #bfbfbf'>Billionaires: </span>" +
this.getData("Billionaires")
);
});
// set a title
map.title()
.enabled(true)
.useHtml(true)
.text(
"Wealthiest Cities<br/>" +
"<span style='color: #929292; font-size: 12px;'>" +
"Based on the number of millionaires</span>"
);
// specify a container
map.container("container");
// draw the resulting map
map.draw();
}
);
});
// helper filter function
function filterFunction(val1, val2) {
if (val2) {
return function (fieldVal) {
return val1 <= fieldVal && fieldVal < val2;
};
}
return function (fieldVal) {
return val1 <= fieldVal;
};
}
</script>
</body>
</html>
As alluded to earlier, I have an exciting bonus for you. I’ll show you how you can swiftly transform your JavaScript point map into a JavaScript bubble map, using the size of the bubbles to visualize the count of billionaires in each city with the same data. Firstly, make changes to the dataset to set the bubble size (below, it’s based on billionaires).
var citiesDataSet = anychart.data.set(data).mapAs({size: "Billionaires"});
Then, change the marker series to bubble.
var series = map.bubble(data);
Finally, set the maximum and minimum bubble size for a neat visualization, if needed. For instance, like this:
map.maxBubbleSize(20);
map.minBubbleSize(3);
There you go! Check out the resulting JavaScript-based bubble map, encoding millionaires in color and billionaires in size. Feel free to refer to the comprehensive code for the completed point map below and explore the interactive version with (editable) code here.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JavaScript Point Map</title>
<style type="text/css">
html,
body,
#container {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
</style>
<script src="https://cdn.anychart.com/releases/8.12.0/js/anychart-core.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.12.0/js/anychart-map.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.12.0/geodata/custom/world/world.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.3.15/proj4.js"></script>
<script src="https://cdn.anychart.com/releases/8.12.0/js/anychart-data-adapter.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.12.0/js/anychart-ui.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.anychart.com/releases/8.12.0/css/anychart-ui.min.css"/>
<link rel="stylesheet" type="text/css" href="https://cdn.anychart.com/releases/8.12.0/fonts/css/anychart-font.min.css"/>
</head>
<body>
<div id="container"></div>
<script>
anychart.onDocumentReady(function() {
// load data from a json file
anychart.data.loadJsonFile("https://gist.githubusercontent.com/awanshrestha/9e91dbd3ac4626446cd84f8be97016ae/raw/160d5ab2da59a5264e716dcbe3239ce7ac9104b6/Wealthiest%2520Cities%25202023.json",
function(data) {
// create a map chart
var map = anychart.map();
// set the global map geodata
map.geoData("anychart.maps.world");
// add zoom ui controls
var zoomController = anychart.ui.zoom();
zoomController.render(map);
// set the land area color
map.unboundRegions()
.enabled(true)
.fill('#E1E1E1')
.stroke('#D2D2D2');
// set the background color
map.background()
.fill({
color: '#daedea',
opacity: 0.5
});
// create a dataset from data
var citiesDataSet = anychart.data.set(data).mapAs({size: "Billionaires"});
// helper function to create several series
var createSeries = function (name, data, color) {
// set the bubble series
var series = map.bubble(data);
// set the max and min bubble sizes
map.maxBubbleSize(20);
map.minBubbleSize(3);
// configure the series settings
series
.name(name)
.type("circle")
.fill(color)
.stroke(anychart.color.darken(color, 0.4))
.labels(false);
// configure the series legend items
series
.legendItem()
.iconType("circle")
.iconFill(color)
.iconStroke(anychart.color.darken(color, 0.4))
};
// create 5 series, filtering the data by number of millionaires
createSeries(
"Over 100,000",
citiesDataSet.filter("Millionaires", filterFunction(100000, 1000000)),
"#D1FAE9"
);
createSeries(
"50,000–100,000",
citiesDataSet.filter("Millionaires", filterFunction(50000, 100000)),
"#9CE0E5"
);
createSeries(
"10,000–50,000",
citiesDataSet.filter("Millionaires", filterFunction(10000, 50000)),
"#00ACC3"
);
createSeries(
"1,000–10,000",
citiesDataSet.filter("Millionaires", filterFunction(1000, 10000)),
"#355CB1"
);
createSeries(
"Up to 1,000",
citiesDataSet.filter("Millionaires", filterFunction(0, 1000)),
"#002D79"
);
// add a legend
map.legend(true);
// configure the map tooltip
map.tooltip()
.useHtml(true)
.titleFormat(function () {
return this.getData("name") +
" (" +
this.getData("Country") +
")";
})
.format(function () {
return (
"<span style='color: #bfbfbf'>Millionaires: </span>" +
this.getData("Millionaires") +
"<br/>" +
"<span style='color: #bfbfbf'>Centimillionaires: </span>" +
this.getData("Centimillionaires") +
"<br/>" +
"<span style='color: #bfbfbf'>Billionaires: </span>" +
this.getData("Billionaires")
);
});
// set a title
map.title()
.enabled(true)
.useHtml(true)
.text(
"Wealthiest Cities<br/>" +
"<span style='color: #929292; font-size: 12px;'>" +
"Based on the number of millionaires</span>"
);
// specify a container
map.container("container");
// draw the resulting map
map.draw();
}
);
});
// helper filter function
function filterFunction(val1, val2) {
if (val2) {
return function (fieldVal) {
return val1 <= fieldVal && fieldVal < val2;
};
}
return function (fieldVal) {
return val1 <= fieldVal;
};
}
</script>
</body>
</html>
Congratulations on completing this exciting journey from raw data to a captivating interactive point (and bubble) map! I trust you found the process as enjoyable as I did guiding you through it.
Now, it's your turn to take the reins. Explore the myriad ways you can customize and tailor JavaScript point maps to align with your unique data narratives. Should you ever feel puzzled or seek guidance, feel free to reach out. My virtual door is always open to assist you on your web data viz journey.
Prepare to captivate with your point maps. Happy JS mapping!