Note: This is an old article I published in my personal blog onAugust 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 providefree, continuously updated geographic dataof the world.
So can we use their maps? and how? Let's have a look.
OpenStreetMap usage requirements
OpenStreetMap data is licensedOpen Data Commons Open Database License (ODbL),
and the map itself is licensed underCC BY-SA 2.0.That means...
To be able to use OpenStreetMap in your websites, you need adhere by two perfectly reasonable requirements
- You mustdisplay a proper attribution text in/around your map. Since we're using the map and the data, we'll add© OpenStreetMap contributorstext inside the map (more on that later), and link it to thecopyright page.
- When expecting high traffic (in our case we are),you mustavoid flooding OpenStreetMap servers. You can read more about the tile server usage policyhere.
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 thislibrarythat can help us with that.
Let's install everything we'll be needing
npminstall--savetilestrata tilestrata-proxy tilestrata-disk tilestrata-headers
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',
},
};
and now the server code:
vartilestrata=require('tilestrata');
varheaders=require('tilestrata-headers');
varproxy=require('tilestrata-proxy');
vardisk=require('tilestrata-disk');
varconfig=require('./config');
varutil=require('util');
varstrata=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);
Let's review the highlighted lines
- We called our map layer
osm
,you can call this whatever you want. - 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
andy
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.
-
- Then we setup client side caching, using the
Cache-Control
andExpires
header.
Now that our map server code is done, let's start it and use our map!
mkdirmap_tiles_cache# for caching files
node mapserver.js
Let's create a simple page that displays our map. We'll be usingLeaflet JSfor that.
<html>
<head>
<title>OpenStreenMap Example</title>
<metacharset="utf-8">
<linkrel="stylesheet"type="text/css"href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.css">
<scriptsrc='https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.js'></script>
</head>
<body>
<divid="map"style="height: 700px; width: 100%;"></div>
<script>
varmap=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:'© <a href= "https://www.openstreetmap.org/copyright" >OpenStreetMap contributors</a>'
}).addTo(map);
</script>
</body>
And we're done! We have ourselves a free, flexible map to use in our website.
Checkout theLeaflet websitefor 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 around30GB(You can calculate your own area with thiscalculator toolfromGeofabrik).
You have two options to fix that
- Delete the cache folder periodically.
- Setup map boundaries for the map.
The first option is self-explanatory, so let's look at the second one.
From thedocs,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 Algeriahere(you can get bounds for any country).
// The bounds object
// representing a fixed rectangular area
varbounds=L.bounds(
L.point(-8.704895,18.92874),
L.point(12.03598,37.77284)
);
// Set the max bounds
map.setMaxBounds(bounds);
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!