Using OpenStreetMap For Your Web Projects

breda

Bouchaala Reda

Posted on August 18, 2022

Using OpenStreetMap For Your Web Projects

Note: This is an old article I published in my personal blog on August 2019. I now publish articles on dev.to so I stopped my blog and moved this article here.


What's wrong with Google Maps?

Nothing. But... let's consider this scenario:
You're working on a web project, say an e-commerce platform for a real-estate company based in Algeria (where I currently live). One of the project's requirements is to show a map of the product's location, on its details page.

You could go with Google Maps, but since they changed to a pay-as-you-go pricing model, you should probably think twice before just jumping in.

Using Google Maps could be the perfect solution in your case but for me it wasn't. I had to look for an alternative.

OpenStreetMap to the rescue

First of all, what is OpenStreetMap ?
OpenStreetMap is a collaborative project, that aims to provide free, continuously updated geographic data of the world.

So can we use their maps? and how? Let's have a look.

OpenStreetMap usage requirements

OpenStreetMap data is licensed Open Data Commons Open Database License (ODbL),
and the map itself is licensed under CC BY-SA 2.0. That means...

To be able to use OpenStreetMap in your websites, you need adhere by two perfectly reasonable requirements

  1. You must display a proper attribution text in/around your map. Since we're using the map and the data, we'll add © OpenStreetMap contributors text inside the map (more on that later), and link it to the copyright page.
  2. When expecting high traffic (in our case we are), you must avoid flooding OpenStreetMap servers. You can read more about the tile server usage policy here.

Show me the code

Enough talk, we will now setup a map, with respect to the requirements mentioned above.

I'm going to use NodeJS to setup a map tile server proxy, I found this library that can help us with that.

Let's install everything we'll be needing

npm install --save tilestrata tilestrata-proxy tilestrata-disk tilestrata-headers
Enter fullscreen mode Exit fullscreen mode

Let's review what we installed:

  • Tilestrata is the main server code.
  • TileStrata Proxy will help us proxy requests to OpenStreetMap servers.
  • TileStrata Disk, will help us cache the map tiles, to avoid flooding the proxied servers.
  • TileStrata Headers to help us with client-side caching of map tiles.

Let's first setup a config file

module.exports = {
    // Host:Port to listen on
    host: '127.0.0.1',
    port: 12345,

    cache: {
        lifetime: 3600 * 24 * 7 * 4, // Cache for a month
        refresh: 3600 * 24 * 14, // 14 days refresh

        // The map-server script needs to be lunched from the project root
        dir: './map_tiles_cache',
    },
};
Enter fullscreen mode Exit fullscreen mode

and now the server code:

var tilestrata = require('tilestrata');
var headers    = require('tilestrata-headers');
var proxy      = require('tilestrata-proxy');
var disk       = require('tilestrata-disk');
var config     = require('./config');
var util       = require('util');

var strata = tilestrata();
strata.layer('osm') // highlight-line
    .route('*.png')
    .use(disk.cache({
        dir: config.cache.dir,
        refreshage: config.cache.refresh,
        maxage: config.cache.lifetime,
    }))
    .use(proxy({
        uri: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', // highlight-line
        subdomains: ['a', 'b', 'c'],
    }))
    .use(headers({ // highlight-line
        'Cache-Control': 'public, max-age=' + config.cache.lifetime, // highlight-line
        'Expires': config.cache.lifetime, // highlight-line
    }));

process.stdout.write(util.format(
    "Map server listening on http://%s:%s\nPress Ctrl-C to quit.\n",
    config.host, config.port
));
strata.listen(config.port, config.host);
Enter fullscreen mode Exit fullscreen mode

Let's review the highlighted lines

  1. We called our map layer osm, you can call this whatever you want.
  2. We proxy the request to OpenStreetMap tile servers, the variables are
    • s means sub-domain, and it's picked at random from the arrow bellow the highlighted line. Note that this is a required behavior.
    • z is the zoom level, x and y and numbers indicating the column and row the map. You don't have to worry about all this parameters, they will be passed automatically by our client map library. We'll still be using latitudes and longitudes like we're used to.
  3. Then we setup client side caching, using the Cache-Control and Expires header.

Now that our map server code is done, let's start it and use our map!

mkdir map_tiles_cache # for caching files
node mapserver.js
Enter fullscreen mode Exit fullscreen mode

Let's create a simple page that displays our map. We'll be using Leaflet JS for that.

<html>
<head>
    <title> OpenStreenMap Example </title>
    <meta charset="utf-8">
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.css">
    <script src='https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.js'></script>
</head>

<body>
    <div id="map" style="height: 700px; width: 100%;"></div>

    <script>
        var map = L.map('map', {
            center: [36.7597907,3.0665139], // The capital of Algeria
            zoom: 9, // default zoom level
        });

         L.tileLayer('http://127.0.0.1:12345/osm/{z}/{x}/{y}.png', {
             attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
        }).addTo(map);
    </script>
</body>
Enter fullscreen mode Exit fullscreen mode

And we're done! We have ourselves a free, flexible map to use in our website.
Checkout the Leaflet website for more documentation on how to ineract with your new map.

One more thing...

Depending on the use case, the cache disk space can grow very large.

As an example, The north side of Algeria, viewed from from zoom level 6 all the way up to zoom level 18 (which is the maximum zoom level supported by OpenStreetMap) will be around 30GB (You can calculate your own area with this calculator tool from Geofabrik).

You have two options to fix that

  1. Delete the cache folder periodically.
  2. Setup map boundaries for the map.

The first option is self-explanatory, so let's look at the second one.

From the docs, LeafletJS has map boundaries,
which restricts the view to the given geographical bounds,
bouncing the user back if they try to pan outside the view.

Let's set that up. I got the bounds for Algeria here (you can get bounds for any country).

// The bounds object
// representing a fixed rectangular area
var bounds = L.bounds(
    L.point(-8.704895, 18.92874), 
    L.point(12.03598, 37.77284)
);

// Set the max bounds
map.setMaxBounds(bounds);
Enter fullscreen mode Exit fullscreen mode

And we're done!

Of course everything has its pros and cons, and you should decide what's better suited for you own needs.

But as you can see, it was pretty easy to setup a map server based on OpenStreetMap and get started with it, so it's good to have it in your arsenal.

And that's about it! I hope this post was of help to you.
Make sure to drop a comment if you have any question or feedback. Thanks!

💖 💪 🙅 🚩
breda
Bouchaala Reda

Posted on August 18, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Using OpenStreetMap For Your Web Projects