CINXE.COM

Working with map panes - Leaflet - a JavaScript library for interactive maps

<!DOCTYPE html> <html lang="en"> <head> <title>Working with map panes - Leaflet - a JavaScript library for interactive maps</title> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="shortcut icon" type="image/x-icon" href="../../docs/images/favicon.ico" /> <link href="https://leafletjs.com/atom.xml" type="application/atom+xml" rel="alternate" title="Leaflet Dev Blog Atom Feed" /> <link rel="stylesheet" href="../../docs/css/normalize.css" /> <link rel="stylesheet" href="../../docs/css/main.css" /> <link href='https://fonts.googleapis.com/css?family=Open+Sans:400,400italic,700,300' rel='stylesheet' type='text/css'> <script src="../../docs/highlight/highlight.pack.js"></script> <link rel="stylesheet" href="../../docs/highlight/styles/github-gist.css" /> <!-- Leaflet --> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/> <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script> <script> ACCESS_TOKEN = 'pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw'; MB_ATTR = 'Map data &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, ' + 'Imagery © <a href="https://www.mapbox.com/">Mapbox</a>'; MB_URL = 'https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=' + ACCESS_TOKEN; OSM_URL = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png'; OSM_ATTRIB = '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'; </script> </head> <body> <header> <h1><a href="https://leafletjs.com/"><img src="../../docs/images/logo.png" alt="Leaflet" width="300" /></a></h1> <p class="tagline">an open-source JavaScript library<br> for mobile-friendly interactive maps</p> </header> <nav> <ul class="nav"> <li> <a href="../../index.html">Overview</a> </li> <li> <a href="../../examples.html">Tutorials</a> </li> <li> <a href="../../reference.html">Docs</a> </li> <li> <a href="../../download.html">Download</a> </li> <li> <a href="../../plugins.html">Plugins</a> </li> <li> <a href="../../blog.html">Blog</a> </li> </ul> </nav> <main> <div class="container"> <p class="tutorials-back"><a href="../../examples.html">&larr; Tutorials</a></p> <h2 id="what-are-panes">What are panes?</h2> <p>In Leaflet, map panes group layers together implicitly, without the developer knowing about it. This grouping allows web browsers to work with several layers at once in a more efficient way than working with layers individually.</p> <p>Map panes use the <a href="https://developer.mozilla.org/docs/Web/CSS/z-index">z-index CSS property</a> to always show some layers on top of others. The <a href="/reference.html#map-pane">default order</a> is:</p> <ul> <li><code class="language-plaintext highlighter-rouge">TileLayer</code>s and <code class="language-plaintext highlighter-rouge">GridLayer</code>s</li> <li><code class="language-plaintext highlighter-rouge">Path</code>s, like lines, polylines, circles, or <code class="language-plaintext highlighter-rouge">GeoJSON</code> layers.</li> <li><code class="language-plaintext highlighter-rouge">Marker</code> shadows</li> <li><code class="language-plaintext highlighter-rouge">Marker</code> icons</li> <li><code class="language-plaintext highlighter-rouge">Popup</code>s</li> </ul> <p>This is why, in Leaflet maps, popups always show &#8220;on top&#8221; of other layers, markers always show on top of tile layers, etc.</p> <p>A new feature of <strong>Leaflet 1.0.0</strong> (not present in 0.7.x) is custom map panes, which allows for customization of this order.</p> <h2 id="the-default-is-not-always-right">The default is not always right</h2> <p>In some particular cases, the default order is not the right one for the map. We can demonstrate this with the <a href="https://carto.com/location-data-services/basemaps/">Carto basemaps</a> and labels:</p> <style> .tiles img { border: 1px solid #ccc; border-radius: 5px; } </style> <div class="tiles"> <div style="display: inline-block"> <img src="https://a.basemaps.cartocdn.com/light_nolabels/4/8/5.png" class="bordered-img" /><br /> Basemap tile with no labels </div> <div style="display: inline-block"> <img src="https://a.basemaps.cartocdn.com/light_only_labels/4/8/5.png" class="bordered-img" /><br /> Transparent labels-only tile </div> <div style="display: inline-block; position:relative;"> <img src="https://a.basemaps.cartocdn.com/light_nolabels/4/8/5.png" class="bordered-img" /> <img src="https://a.basemaps.cartocdn.com/light_only_labels/4/8/5.png" style="position:absolute; left:0; top:0;" /><br /> Labels on top of basemap </div> </div> <p>If we create a Leaflet map with these two tile layers, any marker or polygon will show on top of both, but having the labels on top <a href="http://blog.cartodb.com/let-your-labels-shine/">looks much nicer</a>. How can we do that?</p> <table role="presentation"> <tr><td style="text-align: center; border: none; padding: 0;"> <iframe src="example.html" width="600" height="400" style="max-width: 100%; max-height: 90vh; box-sizing: border-box;"></iframe> </td></tr> <tr><td style="text-align: center; border: none"> <small><a href="example.html">See this example stand-alone.</a></small> </td></tr></table> <h2 id="custom-pane">Custom pane</h2> <p>We can use the defaults for the basemap tiles and some overlays like GeoJSON layers, but we have to define a custom pane for the labels, so they show on top of the GeoJSON data.</p> <p>Custom map panes are created on a per-map basis, so first create an instance of <code class="language-plaintext highlighter-rouge">L.Map</code> and the pane:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var map = L.map('map'); map.createPane('labels'); </code></pre></div></div> <p>The next step is setting the z-index of the pane. Looking at the <a href="https://github.com/Leaflet/Leaflet/blob/v1.0.0/dist/leaflet.css#L87">defaults</a>, a value of 650 will make the <code class="language-plaintext highlighter-rouge">TileLayer</code> with the labels show on top of markers but below pop-ups. By using <code class="language-plaintext highlighter-rouge">getPane()</code>, we have a reference to the <a href="https://developer.mozilla.org/docs/Web/API/HTMLElement"><code class="language-plaintext highlighter-rouge">HTMLElement</code></a> representing the pane, and change its z-index:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>map.getPane('labels').style.zIndex = 650; </code></pre></div></div> <p>One of the problems of having image tiles on top of other map layers is that the tiles will capture clicks and touches. If a user clicks anywhere on the map, the web browser will assume she clicked on the labels tiles, and not on the GeoJSON or on the markers. This can be solved using <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events">the <code class="language-plaintext highlighter-rouge">pointer-events</code> CSS property</a>:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>map.getPane('labels').style.pointerEvents = 'none'; </code></pre></div></div> <p>With the pane now ready, we can add the layers, paying attention to use the <code class="language-plaintext highlighter-rouge">pane</code> option on the labels tiles:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var positron = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png', { attribution: '©OpenStreetMap, ©CartoDB' }).addTo(map); var positronLabels = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png', { attribution: '©OpenStreetMap, ©CartoDB', pane: 'labels' }).addTo(map); var geojson = L.geoJson(GeoJsonData, geoJsonOptions).addTo(map); </code></pre></div></div> <p>Finally, add some interaction to each feature on the GeoJSON layer:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>geojson.eachLayer(function (layer) { layer.bindPopup(layer.feature.properties.name); }); map.fitBounds(geojson.getBounds()); </code></pre></div></div> <p>Now the <a href="example.html">example map</a> is complete!</p> </div> </main> <footer class="container"> <div class="footer"> <p>&copy; 2010–2024 <a href="https://agafonkin.com">Volodymyr Agafonkin</a>. Maps &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors.</p> </div> <nav class="ext-links"> <a class="ext-link twitter" href="https://twitter.com/LeafletJS" title="Follow LeafletJS on X"><img alt="Follow LeafletJS on X" src="../../docs/images/x-round.svg" width="46" /></a> <a class="ext-link github" href="http://github.com/Leaflet/Leaflet" title="View Source on GitHub"><img alt="View Source on GitHub" src="../../docs/images/github-round.svg" width="46" /></a> <a class="ext-link forum" href="https://stackoverflow.com/questions/tagged/leaflet" title="Ask for help on Stack Overflow"><img alt="Leaflet questions on Stack Overflow" src="../../docs/images/forum-round.svg" width="46" /></a> </nav> <button id="back-to-top" title="Scroll back to top"> <svg viewBox="0 0 16 16"> <path d="M7.3,3.3c0.4-0.4,1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4s-1,0.4-1.4,0L8,5.4l-5.3,5.3c-0.4,0.4-1,0.4-1.4,0s-0.4-1,0-1.4L7.3,3.3L7.3,3.3z"/> </svg> </button> </footer> <script> var _gaq = _gaq || []; _gaq.push([ '_setAccount', 'UA-4147697-4' ]); _gaq.push([ '_trackPageview' ]); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script> <script src="../../dialog/dialog.js"></script> <script src="../../docs/js/docs.js"></script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10