Delivery app, real estate site, store locator: sooner or later, a project in Tunisia needs a base map. And not just any map. People expect to see their neighbourhood, their street names and the road numbers they actually drive on.
In this post we'll put a map from TMaps, the maps API built for Tunisia, in Leaflet. It takes about ten lines of JavaScript. Then we'll go through the small details that tend to eat an afternoon: the API key, authorized domains, zoom limits and caching.
You can see the finished result here: examples.tmaps.tn/01-raster-leaflet
Or play with it right here, and fork it on CodePen to change the code:
What are raster tiles?
A raster tile is a 256 × 256 PNG image showing a small square of the map. Leaflet asks for the tiles it needs to fill the screen, using the standard XYZ scheme:
https://tiles.tmaps.tn/r-tile/{z}/{x}/{y}?api_key=YOUR_API_KEY
z is the zoom level (0 to 18), and x and y are the column and row of the tile at that zoom. You never compute them yourself: you give Leaflet the template and it fills in the blanks.
1. Get an API key
- Create an account in the TMaps console.
- Create an API key. It looks like
ak_followed by 32 hexadecimal characters. - In Authorized Domains, add every domain that will show the map, for example
www.mysite.tn,app.mysite.tn, andlocalhostwhile you develop.
Two things that catch people out. Wildcards aren't accepted, so *.mysite.tn won't work and you have to list each subdomain. And requests must use HTTPS: plain HTTP gets a 403.
Yes, the key will be visible in your page source. That's normal for a map in the browser. The authorized domains list is what stops someone from copying it onto their own site.
2. The complete example
Save this as index.html, replace YOUR_API_KEY, and serve the folder over HTTP (npx serve . works fine):
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Map of Tunisia</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css">
<script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js"></script>
<style>
html, body, #map { height: 100%; margin: 0; }
</style>
</head>
<body>
<div id="map"></div>
<script>
const API_KEY = 'YOUR_API_KEY';
const map = L.map('map').setView([36.8002, 10.1815], 12); // Tunis
L.tileLayer(`https://tiles.tmaps.tn/r-tile/{z}/{x}/{y}?api_key=${API_KEY}`, {
maxZoom: 18,
attribution: '© <a href="https://www.tmaps.tn">TMaps</a>',
}).addTo(map);
</script>
</body>
</html>
That's really all there is to it. Leaflet takes care of panning, zooming, touch gestures and loading tiles.
Zoom in on the Medina and you'll get down to individual streets, with names in French and Arabic:
3. A few details worth getting right
Set maxZoom: 18
Tiles stop at zoom 18. If you keep Leaflet's default or go higher, anyone who zooms in past 18 ends up staring at an empty grey grid. Setting maxZoom: 18 keeps the zoom buttons in line with the data.
Say something when tiles fail
With a wrong key or a domain that isn't authorized, tiles fail quietly and you just get a grey map. A one-off tileerror listener saves a lot of head scratching:
L.tileLayer(url, { maxZoom: 18 })
.once('tileerror', () => {
console.error('TMaps tiles could not be loaded: check your API key and its authorized domains.');
})
.addTo(map);
In the network tab, a 401 means the key is missing or revoked, and a 403 means the domain isn't on the list.
Caching
Tiles come with long Cache-Control headers, so the browser keeps them between visits. On mobile, turn on your map library's disk cache and your users will thank you for the data they save.
High-density screens
There are no Retina (@2x) raster tiles yet. If the map has to stay crisp on high-density screens, or you want to change colours, rotate or tilt it, vector tiles are the better choice. That's the topic of the third post in this series.
The same tiles work everywhere
Because the URL follows the XYZ standard, you can drop it unchanged into OpenLayers, MapLibre GL, or a mobile SDK that supports XYZ layers (MapLibre Native, react-native-maps with UrlTile…). The next post does exactly that with OpenLayers, and adds a view you can share by URL (live demo).
Going further
- About TMaps: Tunisia maps, geocoding and routing API
- Live demos of every TMaps API: examples.tmaps.tn
- Source code, MIT licensed: github.com/tmaps-tn/tmaps-snippets
- Try the API without writing code in the TMaps Postman collection
- Raster tiles documentation: tmaps.tn/documentation
Questions or feedback? Leave a comment below or write to support@tmaps.tn.


Top comments (0)