<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tmaps Tunisia</title>
    <description>The latest articles on DEV Community by Tmaps Tunisia (@tmaps).</description>
    <link>https://dev.to/tmaps</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4202269%2F44391087-f85c-4be2-a89b-02896e262456.png</url>
      <title>DEV Community: Tmaps Tunisia</title>
      <link>https://dev.to/tmaps</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tmaps"/>
    <language>en</language>
    <item>
      <title>Leaflet Tutorial: Add a Map of Tunisia with TMaps Tiles</title>
      <dc:creator>Tmaps Tunisia</dc:creator>
      <pubDate>Sun, 08 Nov 2026 13:07:22 +0000</pubDate>
      <link>https://dev.to/tmaps/leaflet-tutorial-add-a-map-of-tunisia-with-tmaps-tiles-1ac5</link>
      <guid>https://dev.to/tmaps/leaflet-tutorial-add-a-map-of-tunisia-with-tmaps-tiles-1ac5</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;In this post we'll put a map from &lt;a href="https://www.tmaps.tn/en/cartes" rel="noopener noreferrer"&gt;TMaps, the maps API built for Tunisia&lt;/a&gt;, in &lt;a href="https://leafletjs.com" rel="noopener noreferrer"&gt;Leaflet&lt;/a&gt;. 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.&lt;/p&gt;

&lt;p&gt;You can see the finished result here: &lt;a href="https://examples.tmaps.tn/01-raster-leaflet/" rel="noopener noreferrer"&gt;examples.tmaps.tn/01-raster-leaflet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or play with it right here, and fork it on CodePen to change the code:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/tmaps/embed/KwWBabZ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F54w5rhqi0hhxekgyyayz.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F54w5rhqi0hhxekgyyayz.webp" alt="Leaflet map of Greater Tunis with TMaps raster tiles" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What are raster tiles?
&lt;/h2&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://tiles.tmaps.tn/r-tile/{z}/{x}/{y}?api_key=YOUR_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;z&lt;/code&gt; is the zoom level (0 to 18), and &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Get an API key
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create an account in the &lt;a href="https://app.tmaps.tn" rel="noopener noreferrer"&gt;TMaps console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Create an API key. It looks like &lt;code&gt;ak_&lt;/code&gt; followed by 32 hexadecimal characters.&lt;/li&gt;
&lt;li&gt;In Authorized Domains, add every domain that will show the map, for example &lt;code&gt;www.mysite.tn&lt;/code&gt;, &lt;code&gt;app.mysite.tn&lt;/code&gt;, and &lt;code&gt;localhost&lt;/code&gt; while you develop.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Two things that catch people out. Wildcards aren't accepted, so &lt;code&gt;*.mysite.tn&lt;/code&gt; won't work and you have to list each subdomain. And requests must use HTTPS: plain HTTP gets a &lt;code&gt;403&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The complete example
&lt;/h2&gt;

&lt;p&gt;Save this as &lt;code&gt;index.html&lt;/code&gt;, replace &lt;code&gt;YOUR_API_KEY&lt;/code&gt;, and serve the folder over HTTP (&lt;code&gt;npx serve .&lt;/code&gt; works fine):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Map of Tunisia&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;#map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"map"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setView&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;36.8002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;10.1815&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Tunis&lt;/span&gt;

    &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tileLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://tiles.tmaps.tn/r-tile/{z}/{x}/{y}?api_key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;maxZoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;attribution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;© &amp;lt;a href="https://www.tmaps.tn"&amp;gt;TMaps&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;addTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's really all there is to it. Leaflet takes care of panning, zooming, touch gestures and loading tiles.&lt;/p&gt;

&lt;p&gt;Zoom in on the Medina and you'll get down to individual streets, with names in French and Arabic:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F60x3c6l5srbbumu4ysvu.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F60x3c6l5srbbumu4ysvu.webp" alt="Medina of Tunis at zoom 16 with TMaps raster tiles" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. A few details worth getting right
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Set &lt;code&gt;maxZoom: 18&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;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 &lt;code&gt;maxZoom: 18&lt;/code&gt; keeps the zoom buttons in line with the data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Say something when tiles fail
&lt;/h3&gt;

&lt;p&gt;With a wrong key or a domain that isn't authorized, tiles fail quietly and you just get a grey map. A one-off &lt;code&gt;tileerror&lt;/code&gt; listener saves a lot of head scratching:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tileLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;maxZoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tileerror&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TMaps tiles could not be loaded: check your API key and its authorized domains.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the network tab, a &lt;code&gt;401&lt;/code&gt; means the key is missing or revoked, and a &lt;code&gt;403&lt;/code&gt; means the domain isn't on the list.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching
&lt;/h3&gt;

&lt;p&gt;Tiles come with long &lt;code&gt;Cache-Control&lt;/code&gt; 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.&lt;/p&gt;

&lt;h3&gt;
  
  
  High-density screens
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same tiles work everywhere
&lt;/h2&gt;

&lt;p&gt;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 &lt;code&gt;UrlTile&lt;/code&gt;…). The next post does exactly that with OpenLayers, and adds a view you can share by URL (&lt;a href="https://examples.tmaps.tn/02-raster-openlayers/" rel="noopener noreferrer"&gt;live demo&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Going further
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;About TMaps: &lt;a href="https://www.tmaps.tn/en" rel="noopener noreferrer"&gt;Tunisia maps, geocoding and routing API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live demos of every TMaps API: &lt;a href="https://examples.tmaps.tn" rel="noopener noreferrer"&gt;examples.tmaps.tn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source code, MIT licensed: &lt;a href="https://github.com/tmaps-tn/tmaps-snippets" rel="noopener noreferrer"&gt;github.com/tmaps-tn/tmaps-snippets&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Try the API without writing code in the &lt;a href="https://www.postman.com/tmaps-tn/tmaps-api" rel="noopener noreferrer"&gt;TMaps Postman collection&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Raster tiles documentation: &lt;a href="https://www.tmaps.tn/documentation/api/raster-tiles" rel="noopener noreferrer"&gt;tmaps.tn/documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions or feedback? Leave a comment below or write to &lt;a href="mailto:support@tmaps.tn"&gt;support@tmaps.tn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>leaflet</category>
    </item>
    <item>
      <title>Autocomplétion d'adresse en Tunisie : arabe et arabizi</title>
      <dc:creator>Tmaps Tunisia</dc:creator>
      <pubDate>Sun, 08 Nov 2026 13:07:00 +0000</pubDate>
      <link>https://dev.to/tmaps/autocompletion-dadresse-en-tunisie-arabe-et-arabizi-4e3k</link>
      <guid>https://dev.to/tmaps/autocompletion-dadresse-en-tunisie-arabe-et-arabizi-4e3k</guid>
      <description>&lt;p&gt;Demandez à dix personnes à Tunis de taper la même adresse, et vous verrez passer trois alphabets :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;en français : &lt;code&gt;Rue de Marseille, Tunis&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;en arabe : &lt;code&gt;نهج مرسيليا&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;en arabizi, c'est-à-dire le tunisien écrit en lettres latines avec des chiffres pour les sons que le français n'a pas : &lt;code&gt;nahj marseille&lt;/code&gt;, &lt;code&gt;zan9et el bacha&lt;/code&gt;, &lt;code&gt;9asr said&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Un champ d'adresse qui ne comprend que la première façon d'écrire fait perdre des clients au moment de la commande. Dans cet article, on construit une autocomplétion avec l'&lt;a href="https://www.tmaps.tn/geocoding" rel="noopener noreferrer"&gt;API de géocodage TMaps&lt;/a&gt; qui gère les trois, et qui affiche correctement les résultats en arabe, de droite à gauche.&lt;/p&gt;

&lt;p&gt;À tester en ligne : &lt;a href="https://examples.tmaps.tn/04-geocoding-search/" rel="noopener noreferrer"&gt;examples.tmaps.tn/04-geocoding-search&lt;/a&gt;. Passez la langue en arabe, ou tapez &lt;code&gt;9asr said&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ou testez-la directement ici, et copiez-la sur CodePen (bouton Fork) pour modifier le code :&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/tmaps/embed/vExagbN?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy6hd1x4s67twcph362fn.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy6hd1x4s67twcph362fn.webp" alt="Recherche en arabe avec des adresses affichées de droite à gauche" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  L'arabizi en 30 secondes
&lt;/h2&gt;

&lt;p&gt;L'arabizi remplace certaines lettres arabes par des chiffres qui leur ressemblent :&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Chiffre&lt;/th&gt;
&lt;th&gt;Arabe&lt;/th&gt;
&lt;th&gt;Son&lt;/th&gt;
&lt;th&gt;Exemple&lt;/th&gt;
&lt;th&gt;Orthographe française habituelle&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ع&lt;/td&gt;
&lt;td&gt;« aïn »&lt;/td&gt;
&lt;td&gt;&lt;code&gt;3in&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;خ&lt;/td&gt;
&lt;td&gt;kh&lt;/td&gt;
&lt;td&gt;&lt;code&gt;5ayreddine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Khayreddine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;7&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ح&lt;/td&gt;
&lt;td&gt;h aspiré&lt;/td&gt;
&lt;td&gt;&lt;code&gt;7ammamet&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hammamet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;9&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ق&lt;/td&gt;
&lt;td&gt;q&lt;/td&gt;
&lt;td&gt;&lt;code&gt;9asr said&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ksar Said&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Ajoutez les mots du dialecte pour les types de voies, &lt;code&gt;nahj&lt;/code&gt; (rue), &lt;code&gt;zan9a&lt;/code&gt; (impasse), &lt;code&gt;chare3&lt;/code&gt; (avenue) et &lt;code&gt;7ay&lt;/code&gt; (cité), et vous avez la façon dont beaucoup de gens écrivent vraiment leur adresse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Étape 1 : une autocomplétion de base solide
&lt;/h2&gt;

&lt;p&gt;Au fond, l'autocomplétion appelle &lt;code&gt;GET /geocoding/forward&lt;/code&gt; pendant que l'utilisateur tape. Trois détails font toute la différence :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// on attend que l'utilisateur fasse une pause&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;geocode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.tmaps.tn/geocoding/forward&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                 &lt;span class="c1"&gt;// Tunisie uniquement&lt;/span&gt;
    &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;languageSelect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// fr, en ou ar&lt;/span&gt;
    &lt;span class="na"&gt;proximity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;36.8065,10.1815&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// favorise les résultats proches de l'utilisateur&lt;/span&gt;
    &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({}));&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Un délai de 300 ms avant d'envoyer, pour faire une requête quand l'utilisateur s'arrête de taper, et pas une par lettre.&lt;/li&gt;
&lt;li&gt;Un &lt;code&gt;AbortController&lt;/code&gt; : quand une nouvelle recherche démarre, on annule la précédente. Sinon, une vieille réponse un peu lente peut arriver après une plus récente et l'écraser.&lt;/li&gt;
&lt;li&gt;Le paramètre &lt;code&gt;proximity&lt;/code&gt; : il y a une &lt;em&gt;Rue de Marseille&lt;/em&gt; dans beaucoup de villes tunisiennes, et favoriser la position de l'utilisateur (ou le centre de la carte) fait remonter la bonne.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Chaque résultat contient le point dans &lt;code&gt;geometry.location&lt;/code&gt; (&lt;code&gt;lat&lt;/code&gt;, &lt;code&gt;lng&lt;/code&gt;), l'adresse complète dans &lt;code&gt;formatted&lt;/code&gt;, un score &lt;code&gt;confidence&lt;/code&gt;, et &lt;code&gt;components&lt;/code&gt; avec la commune, la délégation et le gouvernorat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Étape 2 : des résultats en arabe, de droite à gauche
&lt;/h2&gt;

&lt;p&gt;Avec &lt;code&gt;lang=ar&lt;/code&gt;, toute l'adresse revient en arabe :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"formatted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"نهج مرسيليا, حبيب ثامر, باب بحر, تونس, تونس"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"street"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;L'arabe doit s'afficher de droite à gauche. Comme une liste de suggestions mélange souvent les écritures (une adresse en arabe à côté d'un nom de commune en français, un numéro de route en chiffres latins…), ne fixez pas le sens d'écriture en dur. Laissez le navigateur décider pour chaque élément :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;li&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// le premier caractère fort décide : arabe = droite à gauche, latin = gauche à droite&lt;/span&gt;
&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formatted&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"address"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt; &lt;span class="na"&gt;dir=&lt;/span&gt;&lt;span class="s"&gt;"auto"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mettre aussi &lt;code&gt;dir="auto"&lt;/code&gt; sur le champ de saisie fait basculer le curseur et l'alignement dès qu'on commence à taper en arabe. Sans ça, la ponctuation et les chiffres se retrouvent du mauvais côté.&lt;/p&gt;

&lt;h2&gt;
  
  
  Étape 3 : ce que l'API comprend déjà
&lt;/h2&gt;

&lt;p&gt;Nous avons testé de vraies requêtes tunisiennes sur l'API (&lt;code&gt;lang=fr&lt;/code&gt;, &lt;code&gt;country=TN&lt;/code&gt;). Voici le premier résultat pour chacune :&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requête&lt;/th&gt;
&lt;th&gt;Premier résultat&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;نهج مرسيليا&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Rue de Marseille&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nahj marseille&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Rue de Marseille, Tunis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;9asr said&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Ksar Said, Le Bardo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;7ammamet&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Hammamet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;5ayreddine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Rue Khayreddine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;3in zaghouan&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌ aucun résultat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;zan9et el bacha&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;⚠️ Khaireddine Pacha (pas une impasse)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;L'arabe, &lt;code&gt;nahj&lt;/code&gt; et les chiffres &lt;code&gt;7&lt;/code&gt;, &lt;code&gt;9&lt;/code&gt; et &lt;code&gt;5&lt;/code&gt; fonctionnent donc déjà. Le chiffre &lt;code&gt;3&lt;/code&gt; et certains mots du dialecte ont besoin d'un coup de pouce, qu'on peut donner directement dans le navigateur.&lt;/p&gt;

&lt;h2&gt;
  
  
  Étape 4 : normaliser l'arabizi côté navigateur
&lt;/h2&gt;

&lt;p&gt;Une petite fonction réécrit les mots du dialecte et les chiffres de l'arabizi dans l'orthographe utilisée dans les adresses :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Mots du dialecte tunisien et chiffres de l'arabizi, réécrits comme dans les adresses&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DIALECT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;nahj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;nhej&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;zan9a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;impasse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;zan9et&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;impasse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;zanket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;impasse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;chare3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;avenue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;chara3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;avenue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cité&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;7ay&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cité&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ARABIZI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;kh&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;k&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;normalizeTunisian&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+/&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DIALECT&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;DIALECT&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="c1"&gt;// On ne touche ni aux nombres (4014) ni aux routes (rn9, c27)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-z&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-z&lt;/span&gt;&lt;span class="se"&gt;]{1,2}\d&lt;/span&gt;&lt;span class="sr"&gt;+$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/3a/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;3579&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;digit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ARABIZI&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;digit&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"3in zaghouan"     → "ain zaghouan"
"nahj 5ayreddine"  → "rue khayreddine"
"zan9et el bacha"  → "impasse el bacha"
"chare3 bourguiba" → "avenue bourguiba"
"Rue 4014"         → "rue 4014"     (inchangé)
"RN9"              → "rn9"          (inchangé)
"نهج مرسيليا"       → "نهج مرسيليا"  (inchangé)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ne sautez pas les deux protections. Les adresses tunisiennes sont pleines de numéros (&lt;code&gt;Rue 4014&lt;/code&gt;) et de codes de routes (&lt;code&gt;RN9&lt;/code&gt;, &lt;code&gt;C27&lt;/code&gt;), et réécrire ces chiffres casserait des recherches qui marchaient très bien.&lt;/p&gt;

&lt;h2&gt;
  
  
  Étape 5 : chercher les deux versions en parallèle
&lt;/h2&gt;

&lt;p&gt;La normalisation peut aussi faire moins bien : &lt;code&gt;9asr said&lt;/code&gt; fonctionne déjà tel quel. Plutôt que de remplacer ce que l'utilisateur a tapé, on cherche donc les deux versions et on fusionne les résultats :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalizeTunisian&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;geocode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;

  &lt;span class="c1"&gt;// Résultats normalisés d'abord, puis ceux de la saisie brute, sans doublons&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lists&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formatted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formatted&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un texte en français ou en arabe ressort de &lt;code&gt;normalizeTunisian&lt;/code&gt; sans changement : il ne coûte donc qu'une seule requête. Seule une saisie en arabizi ou en dialecte déclenche la deuxième.&lt;/p&gt;

&lt;p&gt;La différence se voit tout de suite : &lt;code&gt;3in zaghouan&lt;/code&gt; passe de zéro résultat à la &lt;em&gt;Rue Ain Zaghouane&lt;/em&gt; et aux villages « Ain » autour de Zaghouan.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0s8xx7ul7ya1r485j4uf.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0s8xx7ul7ya1r485j4uf.webp" alt="La requête arabizi « 3in zaghouan » renvoie des résultats" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Et &lt;code&gt;9asr said&lt;/code&gt; tombe bien sur Ksar Said, au Bardo :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpr4vb918bg68khegnutp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpr4vb918bg68khegnutp.webp" alt="La requête arabizi « 9asr said » renvoie Ksar Said" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Étape 6 : ne pas faire confiance à tous les résultats
&lt;/h2&gt;

&lt;p&gt;Pour une adresse de livraison, une mauvaise adresse coûte plus cher que pas d'adresse du tout. Le score &lt;code&gt;confidence&lt;/code&gt; aide à décider :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;à partir de 0,85, enregistrez les coordonnées ;&lt;/li&gt;
&lt;li&gt;en dessous, gardez la suggestion mais demandez au client de vérifier le pin sur la carte.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La &lt;a href="https://examples.tmaps.tn/12-checkout-address-form/" rel="noopener noreferrer"&gt;démo du formulaire de commande&lt;/a&gt; montre tout le parcours : le client choisit une suggestion, la commune, la délégation et le gouvernorat se remplissent tout seuls, puis il peut déplacer le pin, et l'adresse est affinée par géocodage inverse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Les limites actuelles
&lt;/h2&gt;

&lt;p&gt;Pour être transparents sur ce que cette approche ne règle pas encore :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Certains noms de ville tapés en arabe peuvent renvoyer un autre lieu au nom proche. Si vos utilisateurs tapent les villes en arabe, gardez &lt;code&gt;proximity&lt;/code&gt; et affichez la commune et le gouvernorat dans chaque suggestion, pour qu'ils puissent choisir le bon lieu.&lt;/li&gt;
&lt;li&gt;Quand plusieurs lieux portent le même nom dans des régions différentes (il y a plus d'une &lt;em&gt;El Aouina&lt;/em&gt;), le classement dépend beaucoup de &lt;code&gt;proximity&lt;/code&gt;. Envoyez la vraie position de l'utilisateur dès que vous l'avez.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  En résumé
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Un délai avant d'envoyer, l'annulation des requêtes dépassées, et &lt;code&gt;proximity&lt;/code&gt; pour orienter les résultats.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lang=ar&lt;/code&gt; pour des résultats en arabe, et &lt;code&gt;dir="auto"&lt;/code&gt; partout où vous les affichez.&lt;/li&gt;
&lt;li&gt;Normaliser le dialecte et l'arabizi dans le navigateur, sans toucher aux numéros ni aux routes.&lt;/li&gt;
&lt;li&gt;Chercher en parallèle la saisie brute et la version normalisée, puis fusionner sans doublons.&lt;/li&gt;
&lt;li&gt;N'enregistrer les coordonnées que si &lt;code&gt;confidence&lt;/code&gt; est suffisant.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Pour aller plus loin
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;TMaps pour la &lt;a href="https://www.tmaps.tn/use-cases/livraison-ecommerce" rel="noopener noreferrer"&gt;livraison et l'e-commerce&lt;/a&gt; : &lt;a href="https://www.tmaps.tn/" rel="noopener noreferrer"&gt;www.tmaps.tn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;La démo en ligne : &lt;a href="https://examples.tmaps.tn/04-geocoding-search/" rel="noopener noreferrer"&gt;examples.tmaps.tn/04-geocoding-search&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;La démo du formulaire de commande : &lt;a href="https://examples.tmaps.tn/12-checkout-address-form/" rel="noopener noreferrer"&gt;examples.tmaps.tn/12-checkout-address-form&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Le code source, sous licence MIT : &lt;a href="https://github.com/tmaps-tn/tmaps-snippets" rel="noopener noreferrer"&gt;github.com/tmaps-tn/tmaps-snippets&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;La collection Postman : &lt;a href="https://www.postman.com/tmaps-tn/tmaps-api" rel="noopener noreferrer"&gt;postman.com/tmaps-tn/tmaps-api&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;La documentation du géocodage : &lt;a href="https://www.tmaps.tn/documentation/api/geocoding/forward" rel="noopener noreferrer"&gt;tmaps.tn/documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et vous, comment écrivez-vous vos adresses ? Partagez votre requête tunisienne la plus compliquée en commentaire, ou écrivez-nous à support@tmott13 &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>i18n</category>
    </item>
    <item>
      <title>Carte de la Tunisie avec OpenLayers et les tuiles TMaps</title>
      <dc:creator>Tmaps Tunisia</dc:creator>
      <pubDate>Fri, 06 Nov 2026 10:38:48 +0000</pubDate>
      <link>https://dev.to/tmaps/carte-de-la-tunisie-avec-openlayers-et-les-tuiles-tmaps-48be</link>
      <guid>https://dev.to/tmaps/carte-de-la-tunisie-avec-openlayers-et-les-tuiles-tmaps-48be</guid>
      <description>&lt;p&gt;Dès qu'une carte doit faire plus qu'afficher quelques marqueurs (projections, couches WMS, édition de formes, vrai travail SIG), c'est souvent vers &lt;a href="https://openlayers.org" rel="noopener noreferrer"&gt;OpenLayers&lt;/a&gt; qu'on se tourne. Dans cet article, on branche les tuiles raster de &lt;a href="https://www.tmaps.tn/cartes" rel="noopener noreferrer"&gt;TMaps&lt;/a&gt; dans OpenLayers, on évite le piège classique de la projection, et on ajoute une petite fonction que les utilisateurs adorent : une URL qui se souvient de l'endroit où l'on se trouve sur la carte.&lt;/p&gt;

&lt;p&gt;La démo en ligne : &lt;a href="https://examples.tmaps.tn/02-raster-openlayers/" rel="noopener noreferrer"&gt;examples.tmaps.tn/02-raster-openlayers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ou testez-la directement ici, et copiez-la sur CodePen (bouton Fork) pour modifier le code :&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/tmaps/embed/zxZLNyy?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7z6q0l1aigpmyuigu0ti.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7z6q0l1aigpmyuigu0ti.webp" alt="Démo OpenLayers sur Sousse, avec la vue actuelle dans l'URL" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Avant de commencer
&lt;/h2&gt;

&lt;p&gt;Il vous faut une clé API créée sur la &lt;a href="https://app.tmaps.tn" rel="noopener noreferrer"&gt;console TMaps&lt;/a&gt;, avec votre domaine (et &lt;code&gt;localhost&lt;/code&gt; pour le développement) dans ses Authorized Domains. Les jokers ne sont pas acceptés, donc ajoutez chaque sous-domaine un par un.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. La couche de tuiles
&lt;/h2&gt;

&lt;p&gt;Les tuiles raster de TMaps suivent le schéma XYZ. La source est donc un simple &lt;code&gt;ol.source.XYZ&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;XYZ&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://tiles.tmaps.tn/r-tile/{z}/{x}/{y}?api_key=VOTRE_CLE_API&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;maxZoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;attributions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;© &amp;lt;a href="https://www.tmaps.tn"&amp;gt;TMaps&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Chaque tuile est une image PNG de 256 × 256 pixels, et le zoom va de 0 à 18.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. La projection, le piège classique
&lt;/h2&gt;

&lt;p&gt;C'est l'erreur qui envoie les marqueurs au milieu de la mer. Par défaut, OpenLayers travaille en Web Mercator (EPSG:3857), alors que les coordonnées GPS sont en longitude et latitude (EPSG:4326). Il faut donc les convertir avec &lt;code&gt;ol.proj.fromLonLat&lt;/code&gt;, en faisant attention à l'ordre : la longitude vient en premier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Tile&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;})],&lt;/span&gt;
  &lt;span class="na"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;View&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;proj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromLonLat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;10.637&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;35.828&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="c1"&gt;// Sousse, en [lng, lat]&lt;/span&gt;
    &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;maxZoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2o8l4t7pkka0i2rd6ea7.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2o8l4t7pkka0i2rd6ea7.webp" alt="Sousse affichée avec OpenLayers et les tuiles TMaps" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Garder la vue dans l'URL
&lt;/h2&gt;

&lt;p&gt;Un jour ou l'autre, quelqu'un voudra envoyer « cet endroit précis » à un collègue. Le plus simple est de garder la vue dans le hash de l'URL, sous la forme &lt;code&gt;#zoom,lat,lng&lt;/code&gt; : on le met à jour à chaque déplacement de la carte, et on le relit au chargement de la page.&lt;/p&gt;

&lt;p&gt;À la lecture, avec Tunis par défaut :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;zoom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;36.8002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;10.1815&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;À l'écriture, après chaque déplacement. &lt;code&gt;history.replaceState&lt;/code&gt; change l'adresse sans ajouter une entrée dans l'historique à chaque glissement, donc le bouton Retour continue de fonctionner normalement :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;moveend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getView&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;la&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;proj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLonLat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCenter&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`#&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getZoom&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;la&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Avec ça, un lien comme celui-ci ouvre directement la carte sur Djerba :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://examples.tmaps.tn/02-raster-openlayers/#11,33.80,10.88
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgsvhpw5iib40we3ypn50.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgsvhpw5iib40we3ypn50.webp" alt="Djerba ouverte depuis un lien partagé" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. L'exemple complet
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"fr"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Carte de la Tunisie à partager&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/ol@10.4.0/ol.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/ol@10.4.0/dist/ol.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;#map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"map"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;VOTRE_CLE_API&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Vue initiale lue dans l'URL : #zoom,lat,lng&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;zoom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;36.8002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;10.1815&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;XYZ&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`https://tiles.tmaps.tn/r-tile/{z}/{x}/{y}?api_key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;maxZoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;attributions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;© &amp;lt;a href="https://www.tmaps.tn"&amp;gt;TMaps&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tileloaderror&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tuiles TMaps indisponibles : vérifiez votre clé API et ses domaines autorisés.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Tile&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;})],&lt;/span&gt;
      &lt;span class="na"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;View&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;proj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromLonLat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="nx"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;maxZoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Partage de la vue actuelle via l'URL&lt;/span&gt;
    &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;moveend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getView&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;la&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;proj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLonLat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCenter&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`#&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getZoom&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;la&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Quelques conseils
&lt;/h2&gt;

&lt;p&gt;Si la carte reste vide, ouvrez l'onglet Réseau du navigateur. Un &lt;code&gt;401&lt;/code&gt; signifie que la clé est absente ou révoquée. Un &lt;code&gt;403&lt;/code&gt; signifie que le domaine de la page n'est pas dans les domaines autorisés de la clé, ou que la requête est partie en HTTP simple.&lt;/p&gt;

&lt;p&gt;Les tuiles sont envoyées avec des en-têtes de cache longs : revenir sur la carte est donc très rapide.&lt;/p&gt;

&lt;p&gt;Vous utilisez un bundler ? Le même code fonctionne avec &lt;code&gt;import { Map, View } from 'ol'&lt;/code&gt;, &lt;code&gt;TileLayer&lt;/code&gt; depuis &lt;code&gt;ol/layer/Tile&lt;/code&gt;, &lt;code&gt;XYZ&lt;/code&gt; depuis &lt;code&gt;ol/source/XYZ&lt;/code&gt; et &lt;code&gt;fromLonLat&lt;/code&gt; depuis &lt;code&gt;ol/proj&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pour aller plus loin
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;TMaps, une &lt;a href="https://www.tmaps.tn/google-maps-alternative" rel="noopener noreferrer"&gt;alternative à Google Maps pour la Tunisie&lt;/a&gt; : &lt;a href="https://www.tmaps.tn/" rel="noopener noreferrer"&gt;www.tmaps.tn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Toutes les démos en ligne : &lt;a href="https://examples.tmaps.tn" rel="noopener noreferrer"&gt;examples.tmaps.tn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Le code source, sous licence MIT : &lt;a href="https://github.com/tmaps-tn/tmaps-snippets" rel="noopener noreferrer"&gt;github.com/tmaps-tn/tmaps-snippets&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;La collection Postman : &lt;a href="https://www.postman.com/tmaps-tn/tmaps-api" rel="noopener noreferrer"&gt;postman.com/tmaps-tn/tmaps-api&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;La documentation des tuiles raster : &lt;a href="https://www.tmaps.tn/documentation/api/raster-tiles" rel="noopener noreferrer"&gt;tmaps.tn/documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dans le prochain article : les tuiles vectorielles avec MapLibre GL, quatre styles, le mode sombre et une vue en 3D.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>openlayers</category>
    </item>
    <item>
      <title>OpenLayers XYZ Tiles with TMaps: Shareable Map of Tunisia</title>
      <dc:creator>Tmaps Tunisia</dc:creator>
      <pubDate>Fri, 06 Nov 2026 10:38:01 +0000</pubDate>
      <link>https://dev.to/tmaps/openlayers-xyz-tiles-with-tmaps-shareable-map-of-tunisia-31b5</link>
      <guid>https://dev.to/tmaps/openlayers-xyz-tiles-with-tmaps-shareable-map-of-tunisia-31b5</guid>
      <description>&lt;p&gt;When a map has to do more than show a few markers (projections, WMS layers, editing shapes, real GIS work), &lt;a href="https://openlayers.org" rel="noopener noreferrer"&gt;OpenLayers&lt;/a&gt; is usually the library people reach for. In this post we'll plug &lt;a href="https://www.tmaps.tn/en/cartes" rel="noopener noreferrer"&gt;TMaps&lt;/a&gt; raster tiles into it, sidestep the classic projection trap, and add a small feature users really like: a URL that remembers where you are on the map.&lt;/p&gt;

&lt;p&gt;Live demo: &lt;a href="https://examples.tmaps.tn/02-raster-openlayers/" rel="noopener noreferrer"&gt;examples.tmaps.tn/02-raster-openlayers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or play with it right here, and fork it on CodePen to change the code:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/tmaps/embed/zxZLNyy?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7z6q0l1aigpmyuigu0ti.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7z6q0l1aigpmyuigu0ti.webp" alt="OpenLayers demo page showing Sousse with the current view in the URL" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before you start
&lt;/h2&gt;

&lt;p&gt;You'll need a TMaps API key from the &lt;a href="https://app.tmaps.tn" rel="noopener noreferrer"&gt;TMaps console&lt;/a&gt;, with your domain (and &lt;code&gt;localhost&lt;/code&gt; for development) in its Authorized Domains. Wildcards don't work there, so list each subdomain. The first post of this series walks through this step if you haven't done it yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The tile layer
&lt;/h2&gt;

&lt;p&gt;TMaps raster tiles follow the XYZ scheme, so the source is a plain &lt;code&gt;ol.source.XYZ&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;XYZ&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://tiles.tmaps.tn/r-tile/{z}/{x}/{y}?api_key=YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;maxZoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;attributions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;© &amp;lt;a href="https://www.tmaps.tn"&amp;gt;TMaps&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each tile is a 256 × 256 PNG, and zoom goes from 0 to 18.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Get the projection right
&lt;/h2&gt;

&lt;p&gt;This is the one that sends markers into the sea. By default OpenLayers works in Web Mercator (EPSG:3857), while GPS coordinates are longitude and latitude in EPSG:4326. Convert them with &lt;code&gt;ol.proj.fromLonLat&lt;/code&gt;, and watch the order, because longitude comes first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Tile&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;})],&lt;/span&gt;
  &lt;span class="na"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;View&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;proj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromLonLat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;10.637&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;35.828&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="c1"&gt;// Sousse, as [lng, lat]&lt;/span&gt;
    &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;maxZoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2o8l4t7pkka0i2rd6ea7.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2o8l4t7pkka0i2rd6ea7.webp" alt="Sousse rendered with OpenLayers and TMaps raster tiles" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Keep the view in the URL
&lt;/h2&gt;

&lt;p&gt;Sooner or later someone wants to send "this exact spot" to a colleague. The simplest way is to keep the view in the URL hash as &lt;code&gt;#zoom,lat,lng&lt;/code&gt;: update it whenever the map moves, and read it back when the page loads.&lt;/p&gt;

&lt;p&gt;Reading it on load, with Tunis as the fallback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;zoom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;36.8002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;10.1815&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing it after every move. &lt;code&gt;history.replaceState&lt;/code&gt; changes the address bar without adding one history entry per drag, so the back button still behaves:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;moveend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getView&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;la&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;proj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLonLat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCenter&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`#&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getZoom&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;la&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that in place, a link like this one opens straight on Djerba:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://examples.tmaps.tn/02-raster-openlayers/#11,33.80,10.88
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgsvhpw5iib40we3ypn50.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgsvhpw5iib40we3ypn50.webp" alt="Djerba opened from a shared URL" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The complete example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Shareable map of Tunisia&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/ol@10.4.0/ol.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/ol@10.4.0/dist/ol.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;#map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"map"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Initial view from the URL: #zoom,lat,lng&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;zoom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;36.8002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;10.1815&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;XYZ&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`https://tiles.tmaps.tn/r-tile/{z}/{x}/{y}?api_key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;maxZoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;attributions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;© &amp;lt;a href="https://www.tmaps.tn"&amp;gt;TMaps&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tileloaderror&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TMaps tiles could not be loaded: check your API key and its authorized domains.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Tile&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;})],&lt;/span&gt;
      &lt;span class="na"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;View&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;proj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromLonLat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="nx"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;maxZoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Share the current view through the URL&lt;/span&gt;
    &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;moveend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getView&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;la&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;proj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLonLat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCenter&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`#&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getZoom&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;la&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A few tips
&lt;/h2&gt;

&lt;p&gt;If the map stays empty, open the network tab. A &lt;code&gt;401&lt;/code&gt; means the key is missing or revoked. A &lt;code&gt;403&lt;/code&gt; means the page's domain isn't in the key's authorized domains, or the request went out over plain HTTP.&lt;/p&gt;

&lt;p&gt;Tiles are sent with long cache headers, so coming back to the map is fast.&lt;/p&gt;

&lt;p&gt;Using a bundler? The same code works with &lt;code&gt;import { Map, View } from 'ol'&lt;/code&gt;, &lt;code&gt;TileLayer&lt;/code&gt; from &lt;code&gt;ol/layer/Tile&lt;/code&gt;, &lt;code&gt;XYZ&lt;/code&gt; from &lt;code&gt;ol/source/XYZ&lt;/code&gt; and &lt;code&gt;fromLonLat&lt;/code&gt; from &lt;code&gt;ol/proj&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going further
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;TMaps, a &lt;a href="https://www.tmaps.tn/en/google-maps-alternative" rel="noopener noreferrer"&gt;Google Maps alternative for Tunisia&lt;/a&gt;: &lt;a href="https://www.tmaps.tn/en" rel="noopener noreferrer"&gt;www.tmaps.tn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live demos: &lt;a href="https://examples.tmaps.tn" rel="noopener noreferrer"&gt;examples.tmaps.tn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source code, MIT licensed: &lt;a href="https://github.com/tmaps-tn/tmaps-snippets" rel="noopener noreferrer"&gt;github.com/tmaps-tn/tmaps-snippets&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Postman collection: &lt;a href="https://www.postman.com/tmaps-tn/tmaps-api" rel="noopener noreferrer"&gt;postman.com/tmaps-tn/tmaps-api&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Raster tiles documentation: &lt;a href="https://www.tmaps.tn/documentation/api/raster-tiles" rel="noopener noreferrer"&gt;tmaps.tn/documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next up in the series: vector tiles with MapLibre GL, with four styles, dark mode and a 3D tilt.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>openlayers</category>
    </item>
    <item>
      <title>Carte de la Tunisie sur votre site web en 10 minutes</title>
      <dc:creator>Tmaps Tunisia</dc:creator>
      <pubDate>Mon, 02 Nov 2026 11:07:11 +0000</pubDate>
      <link>https://dev.to/tmaps/carte-de-la-tunisie-sur-votre-site-web-en-10-minutes-1m1b</link>
      <guid>https://dev.to/tmaps/carte-de-la-tunisie-sur-votre-site-web-en-10-minutes-1m1b</guid>
      <description>&lt;p&gt;Vous avez une boutique, un restaurant, une agence ou un cabinet, et vous aimeriez que vos clients voient où vous trouver directement sur votre site ? Ou vous développez une application qui a besoin d'une carte de la Tunisie à jour, avec les vrais noms de rues ?&lt;/p&gt;

&lt;p&gt;Dans ce tutoriel, on part d'une page vide pour arriver, en une dizaine de minutes, à une carte interactive avec un marqueur sur votre adresse. Pas de framework, rien à installer : un seul fichier HTML suffit.&lt;/p&gt;

&lt;p&gt;Le résultat est visible ici : &lt;a href="https://examples.tmaps.tn/01-raster-leaflet/" rel="noopener noreferrer"&gt;examples.tmaps.tn/01-raster-leaflet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ou testez-la directement ici, et copiez-la sur CodePen (bouton Fork) pour modifier le code :&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/tmaps/embed/KwWBabZ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F54w5rhqi0hhxekgyyayz.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F54w5rhqi0hhxekgyyayz.webp" alt="Carte du Grand Tunis avec TMaps" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Les outils
&lt;/h2&gt;

&lt;p&gt;On utilise &lt;a href="https://leafletjs.com" rel="noopener noreferrer"&gt;Leaflet&lt;/a&gt;, la bibliothèque de cartes JavaScript la plus répandue, légère et gratuite. Pour le fond de carte, on prend &lt;a href="https://www.tmaps.tn/carte-tunisie" rel="noopener noreferrer"&gt;TMaps&lt;/a&gt; : il couvre le monde entier, avec beaucoup plus de détails sur la Tunisie (rues, quartiers, routes nationales et régionales).&lt;/p&gt;

&lt;h2&gt;
  
  
  De 0 à 3 minutes : créer une clé API
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Créez un compte sur la &lt;a href="https://app.tmaps.tn" rel="noopener noreferrer"&gt;console TMaps&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Dans API Keys, cliquez sur Create API Key. Vous obtenez une clé qui commence par &lt;code&gt;ak_&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Dans Authorized Domains, ajoutez les domaines où la carte va s'afficher, par exemple &lt;code&gt;www.monsite.tn&lt;/code&gt;, et &lt;code&gt;localhost&lt;/code&gt; pour vos tests sur votre ordinateur.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Deux pièges à éviter. Les jokers ne sont pas acceptés : &lt;code&gt;*.monsite.tn&lt;/code&gt; ne marche pas, il faut ajouter chaque sous-domaine (&lt;code&gt;www.monsite.tn&lt;/code&gt;, &lt;code&gt;boutique.monsite.tn&lt;/code&gt;…). Et les requêtes doivent passer en HTTPS, le HTTP simple est refusé.&lt;/p&gt;

&lt;p&gt;Votre clé sera visible dans le code de la page. Pas d'inquiétude, c'est normal pour une carte affichée dans le navigateur : c'est la liste des domaines autorisés qui empêche quelqu'un d'autre de l'utiliser sur son propre site.&lt;/p&gt;

&lt;h2&gt;
  
  
  De 3 à 7 minutes : la carte
&lt;/h2&gt;

&lt;p&gt;Créez un fichier &lt;code&gt;index.html&lt;/code&gt;, collez ce code et remplacez &lt;code&gt;VOTRE_CLE_API&lt;/code&gt; par votre clé :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"fr"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Où nous trouver&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;#map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;450px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Où nous trouver&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"map"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;VOTRE_CLE_API&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Centre de la carte [latitude, longitude] et niveau de zoom&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setView&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;36.8002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;10.1815&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tileLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://tiles.tmaps.tn/r-tile/{z}/{x}/{y}?api_key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;maxZoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;attribution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;© &amp;lt;a href="https://www.tmaps.tn"&amp;gt;TMaps&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;addTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pour tester, ouvrez la page avec un petit serveur local plutôt qu'en double-cliquant sur le fichier. Dans le dossier du fichier :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx serve &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;# ou&lt;/span&gt;
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; http.server 8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Allez ensuite sur &lt;code&gt;http://localhost:3000&lt;/code&gt; (ou &lt;code&gt;:8000&lt;/code&gt;). La carte s'affiche, et vous pouvez zoomer jusqu'au niveau de la rue :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F60x3c6l5srbbumu4ysvu.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F60x3c6l5srbbumu4ysvu.webp" alt="Médina de Tunis au zoom 16" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  De 7 à 9 minutes : un marqueur sur votre adresse
&lt;/h2&gt;

&lt;p&gt;Ajoutez ces quelques lignes juste après &lt;code&gt;.addTo(map);&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;marker&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;36.8002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;10.1815&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bindPopup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;b&amp;gt;Ma Boutique&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;Avenue Habib Bourguiba, Tunis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openPopup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pour connaître les coordonnées de votre adresse, le plus simple est de la taper dans la &lt;a href="https://examples.tmaps.tn/04-geocoding-search/" rel="noopener noreferrer"&gt;démo de recherche d'adresse TMaps&lt;/a&gt;, qui affiche la latitude et la longitude. Un clic droit sur n'importe quelle carte en ligne fait aussi l'affaire.&lt;/p&gt;

&lt;p&gt;Attention à l'ordre : Leaflet attend &lt;code&gt;[latitude, longitude]&lt;/code&gt;. En Tunisie, la latitude tourne autour de 30 à 37 et la longitude autour de 7 à 11. Si votre marqueur atterrit dans la mer, vous avez très probablement inversé les deux.&lt;/p&gt;

&lt;h2&gt;
  
  
  À 10 minutes : la mise en ligne
&lt;/h2&gt;

&lt;p&gt;Envoyez &lt;code&gt;index.html&lt;/code&gt; sur votre hébergement, ou copiez le bloc &lt;code&gt;&amp;lt;div id="map"&amp;gt;&lt;/code&gt; et les deux &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; dans une page existante (WordPress, site vitrine…). Vérifiez juste que le domaine du site figure bien dans les Authorized Domains de votre clé.&lt;/p&gt;

&lt;p&gt;Si la carte reste grise, ouvrez la console du navigateur (F12) et regardez l'onglet Réseau. Un code &lt;code&gt;401&lt;/code&gt; veut dire que la clé est absente ou révoquée. Un &lt;code&gt;403&lt;/code&gt; veut dire que le domaine n'est pas autorisé, ou que la page est servie en HTTP au lieu de HTTPS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pour aller plus loin
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;TMaps : &lt;a href="https://www.tmaps.tn/" rel="noopener noreferrer"&gt;API de cartographie et de géocodage pour la Tunisie&lt;/a&gt;, une &lt;a href="https://www.tmaps.tn/google-maps-alternative" rel="noopener noreferrer"&gt;alternative à Google Maps&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Une carte vectorielle avec mode sombre, grâce à MapLibre GL : &lt;a href="https://examples.tmaps.tn/03-vector-maplibre-styles/" rel="noopener noreferrer"&gt;la démo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;La recherche d'adresse (géocodage) : &lt;a href="https://examples.tmaps.tn/04-geocoding-search/" rel="noopener noreferrer"&gt;la démo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Un formulaire de livraison avec saisie d'adresse et pin sur la carte : &lt;a href="https://examples.tmaps.tn/12-checkout-address-form/" rel="noopener noreferrer"&gt;la démo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Tout le code source, sous licence MIT : &lt;a href="https://github.com/tmaps-tn/tmaps-snippets" rel="noopener noreferrer"&gt;github.com/tmaps-tn/tmaps-snippets&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;La documentation : &lt;a href="https://www.tmaps.tn/documentation" rel="noopener noreferrer"&gt;tmaps.tn/documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Une question ? Laissez un commentaire ou écrivez-nous à &lt;a href="mailto:support@tmaps.tn"&gt;support@tmaps.tn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>leaflet</category>
    </item>
    <item>
      <title>Carte vectorielle de la Tunisie avec MapLibre et TMaps</title>
      <dc:creator>Tmaps Tunisia</dc:creator>
      <pubDate>Mon, 02 Nov 2026 11:06:25 +0000</pubDate>
      <link>https://dev.to/tmaps/carte-vectorielle-de-la-tunisie-avec-maplibre-et-tmaps-5gmg</link>
      <guid>https://dev.to/tmaps/carte-vectorielle-de-la-tunisie-avec-maplibre-et-tmaps-5gmg</guid>
      <description>&lt;p&gt;Les tuiles raster sont des images de la carte. C'est simple et ça marche partout, mais on ne peut pas y toucher. Les tuiles vectorielles, elles, envoient les données de la carte (routes, bâtiments, noms de lieux) et laissent le navigateur la dessiner. Résultat : une carte nette sur tous les écrans, un zoom fluide, la possibilité de la faire pivoter ou de l'incliner, et un changement de style instantané.&lt;/p&gt;

&lt;p&gt;Dans cet article, on utilise les tuiles vectorielles de &lt;a href="https://www.tmaps.tn/cartes" rel="noopener noreferrer"&gt;TMaps&lt;/a&gt; avec &lt;a href="https://maplibre.org" rel="noopener noreferrer"&gt;MapLibre GL JS&lt;/a&gt;, la bibliothèque open source de cartes en WebGL, et on construit un petit sélecteur de styles.&lt;/p&gt;

&lt;p&gt;La démo en ligne : &lt;a href="https://examples.tmaps.tn/03-vector-maplibre-styles/" rel="noopener noreferrer"&gt;examples.tmaps.tn/03-vector-maplibre-styles&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ou testez-la directement ici, et copiez-la sur CodePen (bouton Fork) pour modifier le code :&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/tmaps/embed/yyMqgGK?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk9w89jtskw8fti6myz14.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk9w89jtskw8fti6myz14.webp" alt="Démo MapLibre avec le sélecteur de styles" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Comment fonctionnent les tuiles vectorielles TMaps
&lt;/h2&gt;

&lt;p&gt;Vous ne demandez pas les tuiles vous-même. Chaque style est un document JSON qui suit la Mapbox Style Specification :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://tiles.tmaps.tn/v-tile/{style}.json?api_key=VOTRE_CLE_API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ce JSON décrit les sources de tuiles, les polices (glyphs), les icônes (sprites) et la façon de dessiner chaque couche. MapLibre le lit et va chercher tout le reste tout seul. Quatre styles sont disponibles :&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Style&lt;/th&gt;
&lt;th&gt;Pour quoi faire&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;streets&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Carte de référence, rues et points d'intérêt faciles à lire&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dark&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tableaux de bord et mode nuit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terrain&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Relief, cours d'eau et topographie&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sunny&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tourisme et commerce, couleurs plus chaudes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  1. Une première carte vectorielle
&lt;/h2&gt;

&lt;p&gt;Il vous faut une clé API créée sur la &lt;a href="https://app.tmaps.tn" rel="noopener noreferrer"&gt;console TMaps&lt;/a&gt;, avec votre domaine (et &lt;code&gt;localhost&lt;/code&gt;) dans ses Authorized Domains.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"fr"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Carte vectorielle de la Tunisie&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/maplibre-gl@5.6.0/dist/maplibre-gl.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/maplibre-gl@5.6.0/dist/maplibre-gl.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;#map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"map"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;VOTRE_CLE_API&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;maplibregl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`https://tiles.tmaps.tn/v-tile/streets.json?api_key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;10.1815&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;36.8002&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Tunis, en [lng, lat]&lt;/span&gt;
      &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;maxZoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;maplibregl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NavigationControl&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;top-right&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un point d'attention : MapLibre attend &lt;code&gt;[longitude, latitude]&lt;/code&gt;, l'inverse de Leaflet qui attend &lt;code&gt;[lat, lng]&lt;/code&gt;. C'est la cause la plus fréquente d'une carte qui s'ouvre en plein océan.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7q825hom30o3vyoqjqlz.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7q825hom30o3vyoqjqlz.webp" alt="Style streets de TMaps" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Changer de style sans recharger la carte
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;map.setStyle(url)&lt;/code&gt; remplace le style mais garde la carte, sa position et ses contrôles. Un sélecteur simple tient en quelques lignes :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;styleUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`https://tiles.tmaps.tn/v-tile/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.json?api_key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;streets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;terrain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sunny&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;styleUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Voici la même vue de Tunis dans les trois autres styles. D'abord &lt;code&gt;dark&lt;/code&gt;, idéal pour un tableau de bord :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F26wk8dw9gpk6abg3ajmc.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F26wk8dw9gpk6abg3ajmc.webp" alt="Style dark de TMaps" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Puis &lt;code&gt;terrain&lt;/code&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fti9d3x2v6g3ttouctq2i.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fti9d3x2v6g3ttouctq2i.webp" alt="Style terrain de TMaps" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Et enfin &lt;code&gt;sunny&lt;/code&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0q4ggt2h6vlz7sm4j4z8.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0q4ggt2h6vlz7sm4j4z8.webp" alt="Style sunny de TMaps" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Suivre le mode sombre de l'utilisateur
&lt;/h3&gt;

&lt;p&gt;Comme le changement est instantané, on peut tout simplement suivre le thème du système :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(prefers-color-scheme: dark)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;applyTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;styleUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;streets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nx"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;applyTheme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;applyTheme&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Incliner et faire pivoter
&lt;/h2&gt;

&lt;p&gt;La carte est dessinée en WebGL, on peut donc l'incliner et la faire pivoter, ce qui est impossible avec des tuiles raster :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jumpTo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;10.179&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;36.799&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Médina de Tunis&lt;/span&gt;
  &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;15.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;pitch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// inclinaison, en degrés&lt;/span&gt;
  &lt;span class="na"&gt;bearing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// rotation, en degrés&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh3w5yaffjyevkludi51x.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh3w5yaffjyevkludi51x.webp" alt="Médina de Tunis inclinée à 60 degrés" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vos utilisateurs peuvent faire la même chose eux-mêmes : clic droit maintenu sur ordinateur, ou deux doigts sur téléphone.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Personnaliser un style
&lt;/h2&gt;

&lt;p&gt;Le JSON du style est modifiable. Téléchargez-le, changez les couches (couleurs, polices, noms affichés selon le zoom), puis hébergez votre propre version. Gardez simplement deux règles en tête :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;les URL des tuiles dans &lt;code&gt;sources&lt;/code&gt; doivent toujours pointer vers &lt;code&gt;tiles.tmaps.tn&lt;/code&gt; avec votre &lt;code&gt;api_key&lt;/code&gt;, car c'est ce qui authentifie votre consommation ;&lt;/li&gt;
&lt;li&gt;vous pouvez modifier librement &lt;code&gt;layers&lt;/code&gt;, ainsi que &lt;code&gt;glyphs&lt;/code&gt; et &lt;code&gt;sprite&lt;/code&gt; si vous hébergez vos propres polices et icônes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Raster ou vectoriel ?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Raster&lt;/th&gt;
&lt;th&gt;Vectoriel&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bibliothèques&lt;/td&gt;
&lt;td&gt;Leaflet, OpenLayers, tout client XYZ&lt;/td&gt;
&lt;td&gt;MapLibre GL JS, MapLibre Native&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Écrans haute densité&lt;/td&gt;
&lt;td&gt;Flou (pas encore de @2x)&lt;/td&gt;
&lt;td&gt;Toujours net&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Changer de style&lt;/td&gt;
&lt;td&gt;Il faut d'autres tuiles&lt;/td&gt;
&lt;td&gt;Instantané, dans le navigateur&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rotation et inclinaison&lt;/td&gt;
&lt;td&gt;Non&lt;/td&gt;
&lt;td&gt;Oui&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Appareils anciens&lt;/td&gt;
&lt;td&gt;Très léger&lt;/td&gt;
&lt;td&gt;Nécessite WebGL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pour une carte simple intégrée dans une page, les tuiles raster avec Leaflet restent imbattables. Quand la carte est au cœur de votre produit, passez au vectoriel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pour aller plus loin
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;TMaps : &lt;a href="https://www.tmaps.tn/" rel="noopener noreferrer"&gt;API de cartographie pour la Tunisie&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Toutes les démos en ligne : &lt;a href="https://examples.tmaps.tn" rel="noopener noreferrer"&gt;examples.tmaps.tn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Le code source, sous licence MIT : &lt;a href="https://github.com/tmaps-tn/tmaps-snippets" rel="noopener noreferrer"&gt;github.com/tmaps-tn/tmaps-snippets&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;La collection Postman : &lt;a href="https://www.postman.com/tmaps-tn/tmaps-api" rel="noopener noreferrer"&gt;postman.com/tmaps-tn/tmaps-api&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;La documentation des tuiles vectorielles : &lt;a href="https://www.tmaps.tn/documentation/api/vector-tiles" rel="noopener noreferrer"&gt;tmaps.tn/documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Une question ou une remarque ? Laissez un commentaire ou écrivez-nous à &lt;a href="mailto:support@tmaps.tn"&gt;support@tmaps.tn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>maplibre</category>
    </item>
    <item>
      <title>TMaps API in Postman: Test Geocoding in 2 Minutes</title>
      <dc:creator>Tmaps Tunisia</dc:creator>
      <pubDate>Fri, 30 Oct 2026 09:47:39 +0000</pubDate>
      <link>https://dev.to/tmaps/tmaps-api-in-postman-test-geocoding-in-2-minutes-33oe</link>
      <guid>https://dev.to/tmaps/tmaps-api-in-postman-test-geocoding-in-2-minutes-33oe</guid>
      <description>&lt;p&gt;Before writing a single line of code against a new API, most of us want to poke at it first and see what comes back. That's what the &lt;a href="https://www.tmaps.tn/en" rel="noopener noreferrer"&gt;TMaps&lt;/a&gt; Postman collection is for: map tiles, address search, reverse geocoding and nearby places, each with documented parameters, example responses and tests.&lt;/p&gt;

&lt;p&gt;Here's how to get your first response in about two minutes, and then how to automate the whole thing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://god.gw.postman.com/run-collection/59164577-39c61a9a-5dec-4a19-bfb0-78e040babbe5?action=collection%2Ffork&amp;amp;source=rip_markdown&amp;amp;collection-url=entityId%3D59164577-39c61a9a-5dec-4a19-bfb0-78e040babbe5%26entityType%3Dcollection%26workspaceId%3Dd930590d-cd34-4179-bf0c-14ef875c22af" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frun.pstmn.io%2Fbutton.svg" alt="Run in Postman" width="128" height="32"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: get a key (30 seconds)
&lt;/h2&gt;

&lt;p&gt;Create an API key in the &lt;a href="https://app.tmaps.tn" rel="noopener noreferrer"&gt;TMaps console&lt;/a&gt;. It starts with &lt;code&gt;ak_&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Postman calls the API from a server context, without an &lt;code&gt;Origin&lt;/code&gt; header, so the key's authorized domains don't come into play here. Any valid key will do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: fork the collection (30 seconds)
&lt;/h2&gt;

&lt;p&gt;Click Run in Postman above, or open the &lt;a href="https://www.postman.com/tmaps-tn/tmaps-api" rel="noopener noreferrer"&gt;public workspace&lt;/a&gt;, and choose Fork Collection. You now have your own copy of the collection in your own workspace.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0rnry66odp41ba1ayiy3.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0rnry66odp41ba1ayiy3.webp" alt="Fork the TMaps collection into your workspace" width="799" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The workspace README gives a quick overview of what's inside:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh56kq1zzw49cc35jls1z.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh56kq1zzw49cc35jls1z.webp" alt="The TMaps API public workspace" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: set your key (30 seconds)
&lt;/h2&gt;

&lt;p&gt;Open your forked collection, go to its Variables tab and add a variable called &lt;code&gt;api_key&lt;/code&gt;, with your key as the Current value. If you prefer environments, you can also fork the TMaps environment from the public workspace and set &lt;code&gt;api_key&lt;/code&gt; there.&lt;/p&gt;

&lt;p&gt;That's the only setting. The collection adds the key to every request as an &lt;code&gt;api_key&lt;/code&gt; query parameter. It's configured once in the collection's Authorization tab and every request inherits it. TMaps doesn't use an &lt;code&gt;Authorization&lt;/code&gt; header.&lt;/p&gt;

&lt;p&gt;Keep the key in Current value: it stays on your machine and isn't shared with your workspace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: send your first request (30 seconds)
&lt;/h2&gt;

&lt;p&gt;Open Geocoding, then Forward geocoding, and hit Send.&lt;/p&gt;

&lt;p&gt;Each request comes with its own documentation: what it does, what it returns, and a link to the matching live demo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ub6rig3nz1sfxyzu87v.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ub6rig3nz1sfxyzu87v.webp" alt="Forward geocoding request documentation" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Params tab lists every parameter with a description. The optional ones (&lt;code&gt;proximity&lt;/code&gt;, &lt;code&gt;bbox&lt;/code&gt;, &lt;code&gt;type&lt;/code&gt;) are already there, just unchecked. Tick them to try them out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm1ausnc0qxjuot2lb0p1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm1ausnc0qxjuot2lb0p1.webp" alt="Documented query parameters" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the response you'll find the point in &lt;code&gt;geometry.location&lt;/code&gt;, a &lt;code&gt;formatted&lt;/code&gt; address, a &lt;code&gt;confidence&lt;/code&gt; score and the Tunisian administrative breakdown (&lt;code&gt;commune&lt;/code&gt;, &lt;code&gt;delegation&lt;/code&gt;, &lt;code&gt;governorate&lt;/code&gt;). No key yet? Open the saved example to see a real response:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F60o4mnftt8yrggk3yu13.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F60o4mnftt8yrggk3yu13.webp" alt="Example response of the forward geocoding request" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Chaining requests
&lt;/h2&gt;

&lt;p&gt;The test script of Forward geocoding saves the first result's coordinates in the &lt;code&gt;lat&lt;/code&gt; and &lt;code&gt;lng&lt;/code&gt; collection variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collectionVariables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collectionVariables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lng&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So Reverse geocoding and Nearby search work straight away on the address you just found. No copying coordinates around by hand.&lt;/p&gt;

&lt;p&gt;Every request also has a few tests (status code, response shape, results sorted by distance…). After sending, you'll see them in the Tests tab of the response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: run it from the command line with Newman
&lt;/h2&gt;

&lt;p&gt;The same collection runs outside Postman with &lt;a href="https://github.com/postmanlabs/newman" rel="noopener noreferrer"&gt;Newman&lt;/a&gt;. The files live in the &lt;a href="https://github.com/tmaps-tn/tmaps-snippets/tree/main/postman" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/tmaps-tn/tmaps-snippets.git
&lt;span class="nb"&gt;cd &lt;/span&gt;tmaps-snippets

npx newman run postman/TMaps-API.postman_collection.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; postman/TMaps.postman_environment.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env-var&lt;/span&gt; &lt;span class="s2"&gt;"api_key=&lt;/span&gt;&lt;span class="nv"&gt;$TMAPS_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what a run looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❏ Geocoding
↳ Forward geocoding
  GET https://api.tmaps.tn/geocoding/forward?q=Avenue Habib Bourguiba, Tunis&amp;amp;country=TN&amp;amp;limit=5&amp;amp;lang=fr&amp;amp;api_key=YOUR_API_KEY [200 OK, 3.64kB, 217ms]
  ✓  Status is 200
  ✓  Returns at least one result

↳ Reverse geocoding
  GET https://api.tmaps.tn/geocoding/reverse?lat=36.80060045491796&amp;amp;lng=10.191012134812192&amp;amp;lang=fr&amp;amp;api_key=YOUR_API_KEY [200 OK, 1.09kB, 29ms]
  ✓  Status is 200
  ✓  Returns an address

↳ Nearby search
  GET https://api.tmaps.tn/geocoding/nearby?lat=36.80060045491796&amp;amp;lng=10.191012134812192&amp;amp;radius=1000&amp;amp;categories=restaurant&amp;amp;limit=10&amp;amp;api_key=YOUR_API_KEY [200 OK, 2.38kB, 28ms]
  ✓  Status is 200
  ✓  Results are sorted by distance

│              assertions │                10 │                 0 │
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  In GitHub Actions
&lt;/h3&gt;

&lt;p&gt;Store your key as a repository secret named &lt;code&gt;TMAPS_API_KEY&lt;/code&gt;, then add a step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check the TMaps API&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;npx newman run postman/TMaps-API.postman_collection.json \&lt;/span&gt;
      &lt;span class="s"&gt;-e postman/TMaps.postman_environment.json \&lt;/span&gt;
      &lt;span class="s"&gt;--env-var "api_key=${{ secrets.TMAPS_API_KEY }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your pipeline fails as soon as an endpoint your app relies on stops answering the way it should.&lt;/p&gt;

&lt;p&gt;One word of caution: Newman prints full request URLs, key included. Don't run it with a real key in public CI logs, or mask the output first.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in the collection
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Folder&lt;/th&gt;
&lt;th&gt;Request&lt;/th&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Maps&lt;/td&gt;
&lt;td&gt;Raster tile (PNG)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET https://tiles.tmaps.tn/r-tile/{z}/{x}/{y}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maps&lt;/td&gt;
&lt;td&gt;Vector style (MapLibre)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET https://tiles.tmaps.tn/v-tile/{style}.json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Geocoding&lt;/td&gt;
&lt;td&gt;Forward geocoding&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET https://api.tmaps.tn/geocoding/forward&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Geocoding&lt;/td&gt;
&lt;td&gt;Reverse geocoding&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET https://api.tmaps.tn/geocoding/reverse&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Geocoding&lt;/td&gt;
&lt;td&gt;Nearby search&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET https://api.tmaps.tn/geocoding/nearby&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Going further
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Geocoding API for Tunisia: &lt;a href="https://www.tmaps.tn/en/geocoding" rel="noopener noreferrer"&gt;www.tmaps.tn/en/geocoding&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Public Postman workspace: &lt;a href="https://www.postman.com/tmaps-tn/tmaps-api" rel="noopener noreferrer"&gt;postman.com/tmaps-tn/tmaps-api&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live demos in the browser: &lt;a href="https://examples.tmaps.tn" rel="noopener noreferrer"&gt;examples.tmaps.tn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source code, MIT licensed: &lt;a href="https://github.com/tmaps-tn/tmaps-snippets" rel="noopener noreferrer"&gt;github.com/tmaps-tn/tmaps-snippets&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Documentation: &lt;a href="https://www.tmaps.tn/documentation" rel="noopener noreferrer"&gt;tmaps.tn/documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions or feedback? Leave a comment or write to &lt;a href="mailto:support@tmaps.tn"&gt;support@tmaps.tn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>postman</category>
      <category>api</category>
      <category>testing</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Tester l'API TMaps dans Postman en 2 minutes</title>
      <dc:creator>Tmaps Tunisia</dc:creator>
      <pubDate>Fri, 30 Oct 2026 09:47:30 +0000</pubDate>
      <link>https://dev.to/tmaps/tester-lapi-tmaps-dans-postman-en-2-minutes-2np0</link>
      <guid>https://dev.to/tmaps/tester-lapi-tmaps-dans-postman-en-2-minutes-2np0</guid>
      <description>&lt;p&gt;Avant d'écrire la moindre ligne de code avec une nouvelle API, on a tous envie de la tester un peu pour voir ce qu'elle renvoie. C'est exactement le rôle de la collection Postman de &lt;a href="https://www.tmaps.tn/" rel="noopener noreferrer"&gt;TMaps&lt;/a&gt; : tuiles de carte, recherche d'adresse, géocodage inverse et lieux à proximité, chacun avec ses paramètres documentés, des exemples de réponse et des tests.&lt;/p&gt;

&lt;p&gt;Voici comment obtenir votre première réponse en deux minutes environ, puis comment automatiser le tout.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://god.gw.postman.com/run-collection/59164577-39c61a9a-5dec-4a19-bfb0-78e040babbe5?action=collection%2Ffork&amp;amp;source=rip_markdown&amp;amp;collection-url=entityId%3D59164577-39c61a9a-5dec-4a19-bfb0-78e040babbe5%26entityType%3Dcollection%26workspaceId%3Dd930590d-cd34-4179-bf0c-14ef875c22af" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frun.pstmn.io%2Fbutton.svg" alt="Run in Postman" width="128" height="32"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Étape 1 : créer une clé (30 secondes)
&lt;/h2&gt;

&lt;p&gt;Créez une clé API sur la &lt;a href="https://app.tmaps.tn" rel="noopener noreferrer"&gt;console TMaps&lt;/a&gt;. Elle commence par &lt;code&gt;ak_&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Postman appelle l'API depuis un contexte serveur, sans en-tête &lt;code&gt;Origin&lt;/code&gt; : les domaines autorisés de la clé ne jouent donc aucun rôle ici. N'importe quelle clé valide fera l'affaire.&lt;/p&gt;

&lt;h2&gt;
  
  
  Étape 2 : copier la collection (30 secondes)
&lt;/h2&gt;

&lt;p&gt;Cliquez sur Run in Postman ci-dessus, ou ouvrez l'&lt;a href="https://www.postman.com/tmaps-tn/tmaps-api" rel="noopener noreferrer"&gt;espace de travail public&lt;/a&gt;, puis choisissez Fork Collection. Vous avez maintenant votre propre copie de la collection, dans votre espace de travail.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0rnry66odp41ba1ayiy3.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0rnry66odp41ba1ayiy3.webp" alt="Copier la collection TMaps dans votre espace de travail" width="799" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Le README de l'espace de travail résume ce qu'il contient :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh56kq1zzw49cc35jls1z.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh56kq1zzw49cc35jls1z.webp" alt="L'espace de travail public TMaps API" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Étape 3 : renseigner votre clé (30 secondes)
&lt;/h2&gt;

&lt;p&gt;Ouvrez votre copie de la collection, allez dans l'onglet Variables et ajoutez une variable &lt;code&gt;api_key&lt;/code&gt;, avec votre clé comme Current value. Si vous préférez les environnements, vous pouvez aussi copier l'environnement TMaps depuis l'espace de travail public et y renseigner &lt;code&gt;api_key&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;C'est le seul réglage. La collection ajoute la clé à chaque requête sous forme de paramètre &lt;code&gt;api_key&lt;/code&gt; dans l'URL. Elle est configurée une seule fois, dans l'onglet Authorization de la collection, et toutes les requêtes en héritent. TMaps n'utilise pas d'en-tête &lt;code&gt;Authorization&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Gardez bien la clé dans Current value : elle reste sur votre machine et n'est pas partagée avec votre espace de travail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Étape 4 : envoyer votre première requête (30 secondes)
&lt;/h2&gt;

&lt;p&gt;Ouvrez Geocoding, puis Forward geocoding, et cliquez sur Send.&lt;/p&gt;

&lt;p&gt;Chaque requête a sa propre documentation : ce qu'elle fait, ce qu'elle renvoie, et un lien vers la démo en ligne correspondante.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ub6rig3nz1sfxyzu87v.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ub6rig3nz1sfxyzu87v.webp" alt="Documentation de la requête de géocodage" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;L'onglet Params liste tous les paramètres avec leur description. Les paramètres facultatifs (&lt;code&gt;proximity&lt;/code&gt;, &lt;code&gt;bbox&lt;/code&gt;, &lt;code&gt;type&lt;/code&gt;) sont déjà là, simplement décochés. Cochez-les pour les essayer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm1ausnc0qxjuot2lb0p1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm1ausnc0qxjuot2lb0p1.webp" alt="Les paramètres documentés" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dans la réponse, vous trouverez le point dans &lt;code&gt;geometry.location&lt;/code&gt;, l'adresse complète dans &lt;code&gt;formatted&lt;/code&gt;, un score &lt;code&gt;confidence&lt;/code&gt; et le découpage administratif tunisien (&lt;code&gt;commune&lt;/code&gt;, &lt;code&gt;delegation&lt;/code&gt;, &lt;code&gt;governorate&lt;/code&gt;). Pas encore de clé ? Ouvrez l'exemple enregistré pour voir une vraie réponse :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F60o4mnftt8yrggk3yu13.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F60o4mnftt8yrggk3yu13.webp" alt="Exemple de réponse du géocodage" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Enchaîner les requêtes
&lt;/h2&gt;

&lt;p&gt;Le script de test de Forward geocoding enregistre les coordonnées du premier résultat dans les variables &lt;code&gt;lat&lt;/code&gt; et &lt;code&gt;lng&lt;/code&gt; de la collection :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collectionVariables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collectionVariables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lng&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reverse geocoding et Nearby search fonctionnent donc tout de suite sur l'adresse que vous venez de trouver, sans recopier les coordonnées à la main.&lt;/p&gt;

&lt;p&gt;Chaque requête a aussi quelques tests (code de statut, forme de la réponse, résultats triés par distance…). Après l'envoi, vous les retrouvez dans l'onglet Tests de la réponse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus : lancer la collection en ligne de commande avec Newman
&lt;/h2&gt;

&lt;p&gt;La même collection fonctionne en dehors de Postman grâce à &lt;a href="https://github.com/postmanlabs/newman" rel="noopener noreferrer"&gt;Newman&lt;/a&gt;. Les fichiers sont dans le &lt;a href="https://github.com/tmaps-tn/tmaps-snippets/tree/main/postman" rel="noopener noreferrer"&gt;dépôt GitHub&lt;/a&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/tmaps-tn/tmaps-snippets.git
&lt;span class="nb"&gt;cd &lt;/span&gt;tmaps-snippets

npx newman run postman/TMaps-API.postman_collection.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; postman/TMaps.postman_environment.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env-var&lt;/span&gt; &lt;span class="s2"&gt;"api_key=&lt;/span&gt;&lt;span class="nv"&gt;$TMAPS_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Voici à quoi ressemble une exécution :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❏ Geocoding
↳ Forward geocoding
  GET https://api.tmaps.tn/geocoding/forward?q=Avenue Habib Bourguiba, Tunis&amp;amp;country=TN&amp;amp;limit=5&amp;amp;lang=fr&amp;amp;api_key=VOTRE_CLE_API [200 OK, 3.64kB, 217ms]
  ✓  Status is 200
  ✓  Returns at least one result

↳ Reverse geocoding
  GET https://api.tmaps.tn/geocoding/reverse?lat=36.80060045491796&amp;amp;lng=10.191012134812192&amp;amp;lang=fr&amp;amp;api_key=VOTRE_CLE_API [200 OK, 1.09kB, 29ms]
  ✓  Status is 200
  ✓  Returns an address

↳ Nearby search
  GET https://api.tmaps.tn/geocoding/nearby?lat=36.80060045491796&amp;amp;lng=10.191012134812192&amp;amp;radius=1000&amp;amp;categories=restaurant&amp;amp;limit=10&amp;amp;api_key=VOTRE_CLE_API [200 OK, 2.38kB, 28ms]
  ✓  Status is 200
  ✓  Results are sorted by distance

│              assertions │                10 │                 0 │
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dans GitHub Actions
&lt;/h3&gt;

&lt;p&gt;Enregistrez votre clé comme secret du dépôt sous le nom &lt;code&gt;TMAPS_API_KEY&lt;/code&gt;, puis ajoutez une étape :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check the TMaps API&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;npx newman run postman/TMaps-API.postman_collection.json \&lt;/span&gt;
      &lt;span class="s"&gt;-e postman/TMaps.postman_environment.json \&lt;/span&gt;
      &lt;span class="s"&gt;--env-var "api_key=${{ secrets.TMAPS_API_KEY }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Votre pipeline échoue désormais dès qu'un endpoint dont votre application dépend ne répond plus comme prévu.&lt;/p&gt;

&lt;p&gt;Petite mise en garde : Newman affiche les URL complètes des requêtes, clé comprise. Ne l'utilisez pas avec une vraie clé dans des logs de CI publics, ou masquez la sortie avant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ce que contient la collection
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dossier&lt;/th&gt;
&lt;th&gt;Requête&lt;/th&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Maps&lt;/td&gt;
&lt;td&gt;Raster tile (PNG)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET https://tiles.tmaps.tn/r-tile/{z}/{x}/{y}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maps&lt;/td&gt;
&lt;td&gt;Vector style (MapLibre)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET https://tiles.tmaps.tn/v-tile/{style}.json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Geocoding&lt;/td&gt;
&lt;td&gt;Forward geocoding&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET https://api.tmaps.tn/geocoding/forward&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Geocoding&lt;/td&gt;
&lt;td&gt;Reverse geocoding&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET https://api.tmaps.tn/geocoding/reverse&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Geocoding&lt;/td&gt;
&lt;td&gt;Nearby search&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET https://api.tmaps.tn/geocoding/nearby&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Pour aller plus loin
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;L'API de géocodage pour la Tunisie : &lt;a href="https://www.tmaps.tn/geocoding" rel="noopener noreferrer"&gt;www.tmaps.tn/geocoding&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;L'espace de travail Postman public : &lt;a href="https://www.postman.com/tmaps-tn/tmaps-api" rel="noopener noreferrer"&gt;postman.com/tmaps-tn/tmaps-api&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Les démos dans le navigateur : &lt;a href="https://examples.tmaps.tn" rel="noopener noreferrer"&gt;examples.tmaps.tn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Le code source, sous licence MIT : &lt;a href="https://github.com/tmaps-tn/tmaps-snippets" rel="noopener noreferrer"&gt;github.com/tmaps-tn/tmaps-snippets&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;La documentation : &lt;a href="https://www.tmaps.tn/documentation" rel="noopener noreferrer"&gt;tmaps.tn/documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Une question ou une remarque ? Laissez un commentaire ou écrivez-nous à &lt;a href="mailto:support@tmaps.tn"&gt;support@tmaps.tn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>postman</category>
      <category>api</category>
      <category>testing</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Google Maps Alternative for Tunisia: TMaps Compared</title>
      <dc:creator>Tmaps Tunisia</dc:creator>
      <pubDate>Mon, 26 Oct 2026 13:42:17 +0000</pubDate>
      <link>https://dev.to/tmaps/google-maps-alternative-for-tunisia-tmaps-compared-3aoa</link>
      <guid>https://dev.to/tmaps/google-maps-alternative-for-tunisia-tmaps-compared-3aoa</guid>
      <description>&lt;p&gt;If you build an app for the Tunisian market, Google Maps is the default choice. Everyone knows it, it covers everything, and you've probably used it before. Then the first invoice arrives, in US dollars, charged to an international card, and you start wondering whether there's an alternative built for Tunisia.&lt;/p&gt;

&lt;p&gt;We built &lt;a href="https://www.tmaps.tn/en/google-maps-alternative" rel="noopener noreferrer"&gt;TMaps&lt;/a&gt; to answer that question. But instead of a sales pitch, this post compares the two the way a developer would: the code, the billing model, real prices on three scenarios, and the cases where Google is still the better pick.&lt;/p&gt;

&lt;p&gt;We'll stick to the two most common needs: &lt;strong&gt;showing a map&lt;/strong&gt; and &lt;strong&gt;finding an address&lt;/strong&gt; (geocoding). Routing deserves its own comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The code: what does a migration look like?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Showing a map
&lt;/h3&gt;

&lt;p&gt;With Google Maps, you load Google's JavaScript SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"map"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"height: 400px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initMap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;36.8002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;10.1815&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// Tunis&lt;/span&gt;
      &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;async&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://maps.googleapis.com/maps/api/js?key=YOUR_GOOGLE_KEY&amp;amp;callback=initMap"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With TMaps, you pick an open-source library (Leaflet here) and point it at the TMaps tiles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"map"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"height: 400px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setView&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;36.8002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;10.1815&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Tunis&lt;/span&gt;
  &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tileLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://tiles.tmaps.tn/r-tile/{z}/{x}/{y}?api_key=YOUR_TMAPS_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;maxZoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;attribution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;© TMaps&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;addTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The real difference isn't the line count. With Google, your code depends on Google's SDK. With TMaps, it depends on Leaflet, OpenLayers or MapLibre, which are open source: if you ever switch tile providers, you don't rewrite your map, you change a URL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding an address
&lt;/h3&gt;

&lt;p&gt;On the Google side, you call the Geocoding API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://maps.googleapis.com/maps/api/geocode/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rue de Marseille, Tunis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GOOGLE_KEY&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the TMaps side:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.tmaps.tn/geocoding/forward&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rue de Marseille, Tunis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TMAPS_KEY&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Good news if you're migrating: the coordinates live in the same place, &lt;code&gt;geometry.location&lt;/code&gt;. Where they really differ is the address breakdown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google returns a generic &lt;code&gt;address_components&lt;/code&gt; array where each item has &lt;code&gt;types&lt;/code&gt; (&lt;code&gt;locality&lt;/code&gt;, &lt;code&gt;administrative_area_level_1&lt;/code&gt;…). Mapping those types to Tunisian administrative levels is up to you.&lt;/li&gt;
&lt;li&gt;TMaps returns fields named after the Tunisian administrative structure:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"components"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"street"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rue de Marseille"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"commune"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Habib Thameur"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"delegation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bab El Bhar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"governorate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tunis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TN"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your delivery form asks for the delegation and the governorate, that's mapping code you don't have to write.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Billing: two different units
&lt;/h2&gt;

&lt;p&gt;This is the part you really need to understand before comparing prices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google&lt;/strong&gt; bills a map &lt;strong&gt;per load&lt;/strong&gt;: when a page displays the map, that's one billable event, no matter how many tiles it loads afterwards. Geocoding is billed per request. Each product has a monthly free cap: 10,000 map loads and 10,000 geocoding requests. Beyond that, you pay per thousand, in US dollars.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TMaps&lt;/strong&gt; counts every &lt;strong&gt;successful request&lt;/strong&gt; as one call: a map tile or a geocoding request. Plans are monthly bundles priced in Tunisian dinars (TND), with a number of calls included. Past your quota, every extra 1,000 calls costs 0.5 TND, and the service is never cut off. The Free plan includes 50,000 calls a month, commercial use included.&lt;/p&gt;

&lt;p&gt;So a TMaps map uses &lt;strong&gt;several calls per display&lt;/strong&gt;. We measured it on our demos, with a 1500 × 963 pixel map:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;with raster tiles (Leaflet), the first display loads &lt;strong&gt;35 tiles&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;with vector tiles (MapLibre), it loads &lt;strong&gt;12&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tiles already in the browser cache aren't requested again, but to stay on the safe side, we count every display as a first visit.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Prices on three scenarios
&lt;/h2&gt;

&lt;p&gt;Sources: the &lt;a href="https://developers.google.com/maps/billing-and-pricing/pricing" rel="noopener noreferrer"&gt;Google Maps Platform price list&lt;/a&gt; (updated October 21, 2026) and the &lt;a href="https://www.tmaps.tn/en/pricing" rel="noopener noreferrer"&gt;TMaps pricing page&lt;/a&gt;. To compare, we use about 3 TND per US dollar (Central Bank of Tunisia rate on October 8, 2026). Google prices exclude taxes and international payment fees.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 1: a small showcase site
&lt;/h3&gt;

&lt;p&gt;3,000 map displays and 1,000 address searches per month, commercial use.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google: $0.&lt;/strong&gt; Everything fits in the free caps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TMaps: 0 TND&lt;/strong&gt; (Free plan, commercial use allowed). With vector tiles, 3,000 displays × 12 tiles + 1,000 geocoding requests is about 37,000 calls, under the plan's 50,000.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A tie, then. The difference shows up as traffic grows: the TMaps Free plan covers about 4,000 vector map displays a month (or 1,400 with raster tiles), while Google stays free up to 10,000 map loads. In between, Google costs nothing and TMaps moves to the Starter plan at 79 TND a month. No point pretending otherwise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 2: a delivery app
&lt;/h3&gt;

&lt;p&gt;200,000 geocoding requests per month (entering and checking customer addresses), not much map display.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google: $850/month, about 2,550 TND.&lt;/strong&gt; The first 10,000 are free, then 90,000 at $5 per thousand and 100,000 at $4 per thousand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TMaps: 79 TND/month&lt;/strong&gt; (Starter plan, 200,000 calls included).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where the gap is widest: geocoding is a big line item on a Google invoice, and it's part of the bundle with TMaps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 3: an e-commerce site
&lt;/h3&gt;

&lt;p&gt;30,000 map displays and 20,000 address searches per month.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google: $190/month, about 570 TND.&lt;/strong&gt; 20,000 paid map loads at $7 per thousand, plus 10,000 paid geocoding requests at $5 per thousand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TMaps with vector tiles (MapLibre): 229 TND/month&lt;/strong&gt; (Growth plan). 30,000 × 12 tiles + 20,000 geocoding requests is about 380,000 calls, under the plan's 750,000.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TMaps with raster tiles (Leaflet): 599 TND/month&lt;/strong&gt; (Business plan). At 35 tiles per display, you reach about 1,070,000 calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This one shows how much the technical choice matters: with vector tiles, TMaps costs about 2.5 times less than Google; with raster tiles, it ends up slightly more expensive. If your site shows a lot of maps, go vector.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Google&lt;/th&gt;
&lt;th&gt;TMaps&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Small showcase site (3,000 maps a month)&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;0 TND&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mid-size site (8,000 maps a month)&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;79 TND&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delivery app&lt;/td&gt;
&lt;td&gt;≈ 2,550 TND ($850)&lt;/td&gt;
&lt;td&gt;79 TND&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E-commerce, vector maps&lt;/td&gt;
&lt;td&gt;≈ 570 TND ($190)&lt;/td&gt;
&lt;td&gt;229 TND&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E-commerce, raster maps&lt;/td&gt;
&lt;td&gt;≈ 570 TND ($190)&lt;/td&gt;
&lt;td&gt;599 TND&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  4. What the price list doesn't show
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The street reference.&lt;/strong&gt; This is probably the biggest difference, and no price list shows it. The TMaps database holds more than 120,000 roads, with a verified name for 72% of road segments on average. According to our coverage measurement, published and recomputed every week on the &lt;a href="https://www.tmaps.tn/couverture-cartographique" rel="noopener noreferrer"&gt;map coverage page&lt;/a&gt; (in French), TMaps shows on average &lt;strong&gt;twice as many named roads as Google Maps&lt;/strong&gt; across Tunisia's 24 governorates, especially in suburban areas and new neighbourhoods. Streets are named in French and Arabic, including the informal local names people actually use.&lt;/p&gt;

&lt;p&gt;For a delivery app, that's very concrete: a street with no name is an address your customer can't type, and a driver who has to call to ask for directions. This is our own measurement, not an independent audit, so check it on your area: the side-by-side pages show Google on the left and TMaps on the right, for example in &lt;a href="https://www.tmaps.tn/en/comparez-vous-meme/ben-arous" rel="noopener noreferrer"&gt;Ben Arous&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Currency.&lt;/strong&gt; Google bills in US dollars, through a billing account and an international payment method, so every move of the dinar changes your invoice. TMaps bills in dinars, with no exchange-rate exposure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overages.&lt;/strong&gt; With TMaps, a traffic spike never takes the service down: it's billed at the overage rate on the next invoice. With Google, pay-as-you-go has no ceiling by default, so set quotas and budget alerts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Arabic and Arabizi.&lt;/strong&gt; Tunisian addresses get typed in French, in Arabic script and in Arabizi (&lt;code&gt;nahj&lt;/code&gt;, &lt;code&gt;9asr said&lt;/code&gt;). Another post in this series shows how to build an address search that handles all three, and you can try it in the &lt;a href="https://examples.tmaps.tn/04-geocoding-search/" rel="noopener noreferrer"&gt;address search demo&lt;/a&gt;: type &lt;code&gt;9asr said&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Support.&lt;/strong&gt; A team based in Tunis, working Tunisian hours, on one side; the support desk of a global platform on the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. When to stay on Google Maps
&lt;/h2&gt;

&lt;p&gt;An honest comparison also has to say when not to switch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your traffic fits in Google's free caps but not in the TMaps Free plan&lt;/strong&gt; (roughly 4,000 to 10,000 maps a month): with Google, you won't pay anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your app covers several countries&lt;/strong&gt;, and Tunisia is just one market among others.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You rely on services TMaps doesn't offer&lt;/strong&gt;: Street View, Google's global places database with reviews and photos, or mobile SDKs your team already knows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the other hand, TMaps makes sense when Tunisia is your main market, when your customers live in new or suburban neighbourhoods where named streets make the difference, when you geocode a lot of addresses, when you want a fixed invoice in dinars, or when you'd rather build your map on open-source libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  See for yourself
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Compare both maps in &lt;a href="https://www.tmaps.tn/en/comparez-vous-meme/ben-arous" rel="noopener noreferrer"&gt;Ben Arous&lt;/a&gt;, &lt;a href="https://www.tmaps.tn/en/comparez-vous-meme/sfax" rel="noopener noreferrer"&gt;Sfax&lt;/a&gt; or &lt;a href="https://www.tmaps.tn/en/comparez-vous-meme/nabeul" rel="noopener noreferrer"&gt;Nabeul&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Road coverage, governorate by governorate: &lt;a href="https://www.tmaps.tn/couverture-cartographique" rel="noopener noreferrer"&gt;map coverage&lt;/a&gt; (in French)&lt;/li&gt;
&lt;li&gt;Try the API without writing code in the &lt;a href="https://www.postman.com/tmaps-tn/tmaps-api" rel="noopener noreferrer"&gt;Postman collection&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live demos and their code: &lt;a href="https://examples.tmaps.tn" rel="noopener noreferrer"&gt;examples.tmaps.tn&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prices quoted here are from October 2026 and can change: always check the official price lists before you decide. Questions about a migration? Leave a comment or write to &lt;a href="mailto:support@tmaps.tn"&gt;support@tmaps.tn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>googlemaps</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>api</category>
    </item>
    <item>
      <title>Migrer de Google Maps vers TMaps en Tunisie</title>
      <dc:creator>Tmaps Tunisia</dc:creator>
      <pubDate>Mon, 26 Oct 2026 10:33:40 +0000</pubDate>
      <link>https://dev.to/tmaps/migrer-de-google-maps-vers-tmaps-en-tunisie-2i51</link>
      <guid>https://dev.to/tmaps/migrer-de-google-maps-vers-tmaps-en-tunisie-2i51</guid>
      <description>&lt;p&gt;Vous avez une application tunisienne qui tourne sur Google Maps et vous envisagez de passer à &lt;a href="https://www.tmaps.tn/google-maps-alternative" rel="noopener noreferrer"&gt;TMaps&lt;/a&gt;. Bonne nouvelle : pour les deux usages les plus courants, afficher une carte et transformer une adresse en coordonnées, la migration tient en quelques fichiers. Cet article passe en revue ce qui change dans le code, ce qui ne change pas, et le piège de facturation à connaître avant de mettre en production.&lt;/p&gt;

&lt;p&gt;Ce guide ne parle pas d'itinéraires : on se limite à la carte et au géocodage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Étape 1 : remplacer le SDK Google par Leaflet
&lt;/h2&gt;

&lt;p&gt;Côté Google, une carte typique ressemble à ceci :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"map"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"height: 400px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initMap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;36.8002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;10.1815&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Marker&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;36.8002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;10.1815&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;async&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://maps.googleapis.com/maps/api/js?key=VOTRE_CLE_GOOGLE&amp;amp;callback=initMap"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TMaps ne fournit pas de SDK propriétaire. On garde une bibliothèque open source et on lui donne l'URL des tuiles TMaps. Avec Leaflet, l'équivalent s'écrit :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"map"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"height: 400px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;setView&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;36.8002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;10.1815&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tileLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://tiles.tmaps.tn/r-tile/{z}/{x}/{y}?api_key=VOTRE_CLE_TMAPS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;maxZoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;attribution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;© TMaps&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;addTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;marker&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;36.8002&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;10.1815&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;addTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quelques correspondances utiles pendant la réécriture :&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Google Maps JS&lt;/th&gt;
&lt;th&gt;Leaflet&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;new google.maps.Map(el, { center, zoom })&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;L.map(el).setView([lat, lng], zoom)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;new google.maps.Marker({ position, map })&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;L.marker([lat, lng]).addTo(map)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;{ lat: 36.8, lng: 10.18 }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[36.8, 10.18]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;chargement asynchrone et &lt;code&gt;callback=initMap&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;script classique, la carte existe tout de suite&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Plus de callback global, plus de clé Google dans le HTML. Et surtout, votre code ne dépend plus d'un fournisseur : changer de source de tuiles un jour reviendra à modifier une URL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F54w5rhqi0hhxekgyyayz.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F54w5rhqi0hhxekgyyayz.webp" alt="Carte Leaflet de Tunis avec les tuiles TMaps" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Étape 2 : passer au vectoriel (vraiment)
&lt;/h2&gt;

&lt;p&gt;Le raster avec Leaflet est la façon la plus directe de remplacer Google, mais ce n'est pas celle que nous conseillons pour la production. Avec MapLibre GL JS et les tuiles vectorielles, la migration reste aussi courte :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;maplibregl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://tiles.tmaps.tn/v-tile/streets.json?api_key=VOTRE_CLE_TMAPS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;10.1815&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;36.8002&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// attention : [lng, lat]&lt;/span&gt;
  &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;maplibregl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Marker&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;setLngLat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;10.1815&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;36.8002&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;addTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Piège classique en venant de Google : MapLibre attend &lt;code&gt;[longitude, latitude]&lt;/code&gt;. Si votre carte s'ouvre au milieu de l'océan, c'est presque toujours ça.&lt;/p&gt;

&lt;p&gt;La raison de ce conseil tient à la facturation, détaillée plus bas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Étape 3 : migrer le géocodage
&lt;/h2&gt;

&lt;p&gt;Ici, le changement est minime. Avant :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://maps.googleapis.com/maps/api/geocode/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rue de Marseille, Tunis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GOOGLE_KEY&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Après :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.tmaps.tn/geocoding/forward&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rue de Marseille, Tunis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TMAPS_KEY&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quatre paramètres renommés (&lt;code&gt;address&lt;/code&gt; devient &lt;code&gt;q&lt;/code&gt;, &lt;code&gt;region&lt;/code&gt; devient &lt;code&gt;country&lt;/code&gt;, &lt;code&gt;language&lt;/code&gt; devient &lt;code&gt;lang&lt;/code&gt;, &lt;code&gt;key&lt;/code&gt; devient &lt;code&gt;api_key&lt;/code&gt;) et c'est tout. La dernière ligne est strictement identique : les coordonnées sont dans &lt;code&gt;geometry.location&lt;/code&gt;, comme chez Google. Le code qui place un marqueur ou enregistre une position en base n'a pas à bouger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Étape 4 : simplifier le découpage de l'adresse
&lt;/h2&gt;

&lt;p&gt;C'est là que la migration fait gagner du code. Chez Google, l'adresse détaillée arrive sous forme de tableau générique &lt;code&gt;address_components&lt;/code&gt;, et il faut aller chercher chaque niveau par son type :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;address_components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;))?.&lt;/span&gt;&lt;span class="nx"&gt;long_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;governorate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;administrative_area_level_1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;locality&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// et la délégation ? À vérifier au cas par cas selon l'adresse...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ce code de correspondance est fragile : les types génériques ne collent pas toujours aux niveaux administratifs tunisiens, et on finit avec des cas particuliers. TMaps renvoie directement des champs nommés selon le découpage du pays :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"components"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"street"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rue de Marseille"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"commune"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Habib Thameur"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"delegation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bab El Bhar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"governorate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tunis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TN"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La fonction &lt;code&gt;pick&lt;/code&gt; disparaît :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;commune&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;delegation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;governorate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pour un formulaire de livraison qui calcule ses frais par délégation, c'est une source de bugs en moins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Le piège : l'unité de facturation n'est pas la même
&lt;/h2&gt;

&lt;p&gt;Avant de comparer des factures, il faut comprendre ce qu'on compte.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google&lt;/strong&gt; facture la carte &lt;strong&gt;au chargement&lt;/strong&gt; : une page qui affiche une carte compte pour un, quel que soit le nombre de tuiles téléchargées ensuite.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TMaps&lt;/strong&gt; compte &lt;strong&gt;chaque requête réussie&lt;/strong&gt; : une tuile, c'est un appel ; un géocodage, c'est un appel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Une carte TMaps consomme donc plusieurs appels par affichage. Mesuré sur nos démos avec une carte de 1500 × 963 pixels, le premier affichage charge environ &lt;strong&gt;35 tuiles en raster&lt;/strong&gt; (Leaflet) contre &lt;strong&gt;12 en vectoriel&lt;/strong&gt; (MapLibre). Les tuiles déjà en cache dans le navigateur ne sont pas redemandées, mais pour estimer votre consommation, comptez chaque affichage comme une première visite.&lt;/p&gt;

&lt;p&gt;D'où le conseil de l'étape 2 : à trafic égal, le vectoriel divise à peu près par trois le nombre d'appels liés à la carte. Si vous migrez une application qui affiche beaucoup de cartes, c'est le réglage qui pèse le plus sur la facture. Le géocodage, lui, reste à un appel par requête dans les deux cas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Et les prix ?
&lt;/h2&gt;

&lt;p&gt;On ne refait pas le calcul ici. Nous avons publié un &lt;a href="https://www.tmaps.tn/blog/alternative-google-maps-tunisie-comparatif" rel="noopener noreferrer"&gt;comparatif complet de Google Maps et TMaps&lt;/a&gt; avec les prix en dinars sur plusieurs scénarios (petite vitrine, livraison, e-commerce), le référentiel des rues, et les cas où il vaut mieux rester sur Google. Les grilles de TMaps sont sur la page &lt;a href="https://www.tmaps.tn/pricing" rel="noopener noreferrer"&gt;tarifs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Avant de migrer, regardez aussi la carte elle-même sur votre zone : les pages « Comparez vous-même » affichent Google et TMaps côte à côte, par exemple sur &lt;a href="https://www.tmaps.tn/comparez-vous-meme/ben-arous" rel="noopener noreferrer"&gt;Ben Arous&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checklist de migration
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Remplacer le script Google par Leaflet, ou mieux, MapLibre avec les tuiles vectorielles.&lt;/li&gt;
&lt;li&gt;Inverser l'ordre des coordonnées si vous passez à MapLibre (&lt;code&gt;[lng, lat]&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Renommer les paramètres du géocodage, garder la lecture de &lt;code&gt;geometry.location&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Supprimer le code de correspondance des &lt;code&gt;address_components&lt;/code&gt; au profit de &lt;code&gt;components&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Estimer la consommation en tuiles par affichage, pas en chargements de carte.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Les démos et leur code sont sur &lt;a href="https://examples.tmaps.tn" rel="noopener noreferrer"&gt;examples.tmaps.tn&lt;/a&gt;, et le dépôt &lt;a href="https://github.com/tmaps-tn/tmaps-snippets" rel="noopener noreferrer"&gt;tmaps-snippets&lt;/a&gt; contient les exemples de cette série. Une question sur votre migration ? Laissez un commentaire ou écrivez-nous à &lt;a href="mailto:support@tmaps.tn"&gt;support@tmaps.tn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>googlemaps</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>api</category>
    </item>
    <item>
      <title>MapLibre GL Vector Tiles with TMaps: Dark Mode and 3D</title>
      <dc:creator>Tmaps Tunisia</dc:creator>
      <pubDate>Fri, 23 Oct 2026 13:28:05 +0000</pubDate>
      <link>https://dev.to/tmaps/vector-maps-of-tunisia-with-maplibre-gl-4-styles-dark-mode-and-3d-tilt-59l1</link>
      <guid>https://dev.to/tmaps/vector-maps-of-tunisia-with-maplibre-gl-4-styles-dark-mode-and-3d-tilt-59l1</guid>
      <description>&lt;p&gt;Raster tiles are pictures of the map. They're simple and they work everywhere, but what you get is what you get. Vector tiles send the map data itself (roads, buildings, labels) and let the browser draw it. That means the map stays sharp on any screen, zooming is smooth, you can rotate and tilt it, and you can restyle it instantly.&lt;/p&gt;

&lt;p&gt;In this post we'll use &lt;a href="https://www.tmaps.tn/en/cartes" rel="noopener noreferrer"&gt;TMaps&lt;/a&gt; vector tiles with &lt;a href="https://maplibre.org" rel="noopener noreferrer"&gt;MapLibre GL JS&lt;/a&gt;, the open-source WebGL map library, and build a little style switcher with four styles.&lt;/p&gt;

&lt;p&gt;Live demo: &lt;a href="https://examples.tmaps.tn/03-vector-maplibre-styles/" rel="noopener noreferrer"&gt;examples.tmaps.tn/03-vector-maplibre-styles&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk9w89jtskw8fti6myz14.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk9w89jtskw8fti6myz14.webp" alt="MapLibre demo with the style switcher" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How TMaps vector tiles work
&lt;/h2&gt;

&lt;p&gt;You don't request the tiles yourself. Each style is a JSON document that follows the Mapbox Style Specification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://tiles.tmaps.tn/v-tile/{style}.json?api_key=YOUR_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That JSON lists the tile sources, the fonts (glyphs), the icons (sprites) and how every layer should be drawn. MapLibre reads it and fetches the rest on its own. There are four styles to choose from:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Style&lt;/th&gt;
&lt;th&gt;Good for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;streets&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Everyday reference map, easy to read streets and points of interest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dark&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dashboards and night mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;terrain&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Relief, rivers and topography&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sunny&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tourism and retail, warmer colours&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  1. A first vector map
&lt;/h2&gt;

&lt;p&gt;You'll need a TMaps API key from the &lt;a href="https://app.tmaps.tn" rel="noopener noreferrer"&gt;TMaps console&lt;/a&gt;, with your domain (and &lt;code&gt;localhost&lt;/code&gt;) in its Authorized Domains.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Vector map of Tunisia&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/maplibre-gl@5.6.0/dist/maplibre-gl.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/maplibre-gl@5.6.0/dist/maplibre-gl.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;#map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"map"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;maplibregl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`https://tiles.tmaps.tn/v-tile/streets.json?api_key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;10.1815&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;36.8002&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Tunis, as [lng, lat]&lt;/span&gt;
      &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;maxZoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;maplibregl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NavigationControl&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;top-right&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing to watch: MapLibre takes &lt;code&gt;[longitude, latitude]&lt;/code&gt;, the opposite of Leaflet's &lt;code&gt;[lat, lng]&lt;/code&gt;. Mixing them up is the most common reason a map opens in the middle of the ocean.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7q825hom30o3vyoqjqlz.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7q825hom30o3vyoqjqlz.webp" alt="TMaps streets style" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Switch styles without reloading the map
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;map.setStyle(url)&lt;/code&gt; swaps the style but keeps the map, its position and its controls. A basic switcher is only a few lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;styleUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`https://tiles.tmaps.tn/v-tile/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.json?api_key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;streets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;terrain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sunny&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;styleUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the same view of Tunis in the other three styles. First &lt;code&gt;dark&lt;/code&gt;, which works well on dashboards:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F26wk8dw9gpk6abg3ajmc.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F26wk8dw9gpk6abg3ajmc.webp" alt="TMaps dark style" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then &lt;code&gt;terrain&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fti9d3x2v6g3ttouctq2i.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fti9d3x2v6g3ttouctq2i.webp" alt="TMaps terrain style" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And &lt;code&gt;sunny&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0q4ggt2h6vlz7sm4j4z8.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0q4ggt2h6vlz7sm4j4z8.webp" alt="TMaps sunny style" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Follow the user's dark mode
&lt;/h3&gt;

&lt;p&gt;Since switching is instant, you can simply follow the operating system's theme:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(prefers-color-scheme: dark)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;applyTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;styleUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;streets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nx"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;applyTheme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;applyTheme&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Tilt and rotate
&lt;/h2&gt;

&lt;p&gt;The map is drawn with WebGL, so you can tilt and rotate it, which raster tiles simply can't do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jumpTo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;10.179&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;36.799&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Medina of Tunis&lt;/span&gt;
  &lt;span class="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;15.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;pitch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// tilt, in degrees&lt;/span&gt;
  &lt;span class="na"&gt;bearing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// rotation, in degrees&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh3w5yaffjyevkludi51x.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh3w5yaffjyevkludi51x.webp" alt="Medina of Tunis tilted at 60 degrees" width="799" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Users can do the same thing themselves with a right-click drag on desktop, or two fingers on a phone.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Customize a style
&lt;/h2&gt;

&lt;p&gt;The style JSON is yours to tweak. Download it, change the layers (colours, fonts, which labels show up at which zoom), and host your own copy. Just keep two things in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the tile URLs in &lt;code&gt;sources&lt;/code&gt; must still point to &lt;code&gt;tiles.tmaps.tn&lt;/code&gt; with your &lt;code&gt;api_key&lt;/code&gt;, because that's how your usage is authenticated;&lt;/li&gt;
&lt;li&gt;you're free to change &lt;code&gt;layers&lt;/code&gt;, and also &lt;code&gt;glyphs&lt;/code&gt; and &lt;code&gt;sprite&lt;/code&gt; if you host your own fonts and icons.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Raster or vector?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Raster&lt;/th&gt;
&lt;th&gt;Vector&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Libraries&lt;/td&gt;
&lt;td&gt;Leaflet, OpenLayers, any XYZ client&lt;/td&gt;
&lt;td&gt;MapLibre GL JS, MapLibre Native&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High-density screens&lt;/td&gt;
&lt;td&gt;Blurry (no @2x yet)&lt;/td&gt;
&lt;td&gt;Always sharp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Changing the style&lt;/td&gt;
&lt;td&gt;Needs new tiles&lt;/td&gt;
&lt;td&gt;Instant, in the browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rotate and tilt&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Older devices&lt;/td&gt;
&lt;td&gt;Very light&lt;/td&gt;
&lt;td&gt;Needs WebGL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For a simple map embedded in a page, raster tiles with Leaflet are hard to beat (that's the first post of this series). When the map is the heart of your product, go vector.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going further
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;About TMaps: &lt;a href="https://www.tmaps.tn/en" rel="noopener noreferrer"&gt;Tunisia maps, geocoding and routing API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live demos: &lt;a href="https://examples.tmaps.tn" rel="noopener noreferrer"&gt;examples.tmaps.tn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source code, MIT licensed: &lt;a href="https://github.com/tmaps-tn/tmaps-snippets" rel="noopener noreferrer"&gt;github.com/tmaps-tn/tmaps-snippets&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Postman collection: &lt;a href="https://www.postman.com/tmaps-tn/tmaps-api" rel="noopener noreferrer"&gt;postman.com/tmaps-tn/tmaps-api&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Vector tiles documentation: &lt;a href="https://www.tmaps.tn/documentation/api/vector-tiles" rel="noopener noreferrer"&gt;tmaps.tn/documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions or feedback? Leave a comment or write to &lt;a href="mailto:support@tmaps.tn"&gt;support@tmaps.tn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>maplibre</category>
    </item>
  </channel>
</rss>
