Creating a Scratch Map using JavaScript & OpenLayers 5

Hello, dear reader!

I have been working full time with OpenLayers for the last three months and, given the lack of up-to-date tutorials on how to use this incredible library, I felt that it was my moment to give back to the community.

This tutorial will walk you through the creation of a scratch map using OpenLayers v5.1.3 and only 64 lines of JavaScript!

Final result

You can see the final result of this tutorial here and access the repository here.

Note: To follow this guide, you should have a JavaScript package manager like npm or yarn installed. I’ll be using npm during this tutorial.


Starting Point

First, we’ll use a bundler so that we can enjoy recent JavaScript features. I’ll be using Webpack, but you can use your favorite one. I have prepared a start baseline project so you don’t have to worry about that. It was based on webpack-start-basic so I wouldn’t have to mess with webpack configuration.

OpenLayers Scratch Map Tutorial Repository

Our starting point will be the start branch, so make sure you git checkout start. For the sake of efficiency, install the dependencies by running npm install while you read the next section.


Project Structure

The most important files are index.html and src/index.js. The first contains the entry point of the application and webpack will inject the JavaScript file as a script tag for us.


index.html

The index.html file is pretty basic, but there are some things we should focus on:

  • The height: 100%; style applied to the head, body and div tags that will allow our map to take the whole screen.
  • The inclusion of the OpenLayers stylesheet using the link tag.
  • The id="map" in the div tag. This will act as the element on which the map will be drawn.

Understanding OpenLayers

OpenLayers provides an extensive API for map and data management and charting. Since this is a beginner tutorial, we’ll only scratch the surface of all its capabilities and more information and examples can be found in the OpenLayers website.

The most important part of our Scratch Map is — unexpectedly — the Map . The OpenLayers Map is the core component of the library and its attributes of most use to us are target, view and layers:

  1. The target is the DOM element in which the map will be drawn. This is the reason why we created a div with id="map" in index.html.
  2. The view instructs how we will see our map.
  3. layers defines the order and content of what should be drawn on the map.

A layer is crucial to displaying something interesting to the user. There are several types of layers, e.g. VectorLayer, ImageLayer, TileLayer.

In this tutorial, we’ll focus on tile and vector layers. The first allows us to display a map of the world, while the second gives us the ability to draw single and independent entities — called features — on top of it.

Every layer also has a source, which is where the data being displayed comes from. We’ll be using OpenStreetMap for the world map and a GeoJSON with countries coordinates, obtained from an OpenLayers example.


Running the Project

Now that you have a basic understanding of how OpenLayers works, we are ready to start coding!

In order to run the project, simply execute the following command inside the project’s root directory — where your package.json file is located.

npm start

If you cloned the repository and are starting from the start branch, you should head to localhost:8080 and see a blank screen.


Creating a Map

First of all, we need to create a Map. In order to do that, we’ll need a target— the div we created — , a view — centered on (0, 0) with a default zoom level — and, finally, some layers.

Creating a new map, without layers — for now.

By now, you should see a white map with some controls appear, like zoom in and zoom out buttons.

Map without layers

Creating the World Map Layer

In order to create the world map layer, we first need to select the type of layer to instantiate. We’ll use a TileLayer because the world map will be rendered in tiles (otherwise we’d have to download the map of the whole world before using it!).

Next, we have to choose a source. As said before, we’ll be using OpenStreetMap, as OpenLayers already provides a source that implements it and it’s very simple to use.

To achieve this, we only have to add two new imports TileLayer and OSM and instantiate the classes like shown below.

Added TileLayer and OpenStreetMap to show the world map

By now, your map should look like this:

World Map being rendered

Adding the Scratching layer

Great, we have a world map! Now we’ll need to focus on adding the “scratching” layer. To achieve that, we’ll use the countries.geojson from the OpenLayers example. It is available there as a link or in the root of the repository.

In order to obtain the layer, we’ll use a VectorLayer with a VectorSource and the url being one that points to the countries.geojson file.

Vector layers are used to draw vector features on a map. These are one of the most useful abstractions in OpenLayers as they give you access to interactions like drawing, modifying and selecting.

Add VectorLayer with VectorSource and countries.geojson

Now, you should see something like this:

World map with countries on top

You now see that there is a layer on top of the world map. Unfortunately, the countries.geojson coordinates do not match the OpenStreetMap coordinates perfectly, so we have some mismatches. But for the purpose of this tutorial, that’s acceptable.


Styling the Scratch Map

If you’ve ever seen a scratch map, almost all of them have a gold layer on top that you scratch. And that’s what we’re here to do! OpenLayers provides styling options through its Style class. You can, then, add that style to a VectorLayer in order to style all the countries!

In this example, we’ll use #D4AF37 as our gold color. The only thing we need to do is to instantiate a new Style with an object containing the fill property which is, in turn, an instance of Fill with our gold color specified.

This will style all the features in the VectorLayer by filling them with the given color.

After this step, we should witness something like this:

World map with undiscovered maps painted gold

Painting only visited places

This part is clearly the mostly complicated. In order to simplify it a bit, we’ll first paint all countries and then remove the ones we’ve visited. At the end of this part, I’ll show the whole index.js .

First of all, we must select the places we want to remove, I’ve chosen the following but you are free to choose any others.

Define the visited places. Order is [longitude, latitude], not the other way around.

Afterwards, we’ll need to extract the VectorSource we defined above into its own variable, as we’ll need to check if it’s done loading before deleting the visited places.

Create variable for the countries’ VectorSource

Now that we have everything ready for the actual deletion of countries, let’s get started!

As said above, we’ll have to until the countriesSource has finished loading, which is done by waiting on the addfeature event to fire. Afterwards, we’ll iterate through each visited place and convert its longitude and latitude into map coordinates, using the fromLonLat function.

Having the visited place represented in map coordinates, we can use the getFeaturesAtCoordinate function to return an array of all the features — in this case countries — that contain that coordinate. Finally, for each Feature found, we’ll remove it from the countriesSource, which will make them disappear from the map!

Delete countries (features) from the countries source

Your whole index.js should now look like this:

Final index.js

If everything went well, you should now be able to see something like this:

Final result

The end

Congratulations! You’ve made it until the end! Hopefully, you’ve learned something new and valuable to you and have understood how powerful OpenLayers is.

If you liked the tutorial, share it with your friends. You can see the final result here and access the repository here.

If you’d like to know more about me, you can visit my website belchior.me.

Thank you for reading!