<?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>Forem: Alfiya Tarasenko</title>
    <description>The latest articles on Forem by Alfiya Tarasenko (@geoapify).</description>
    <link>https://forem.com/geoapify</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F672541%2Fb8f414ef-ff17-4230-ae03-49c5f7c04e1c.png</url>
      <title>Forem: Alfiya Tarasenko</title>
      <link>https://forem.com/geoapify</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/geoapify"/>
    <language>en</language>
    <item>
      <title>Got coordinates but need a city or ZIP code?
This guide shows how to convert lat/lon into address data using reverse geocoding — with real examples, API calls, and a live demo.</title>
      <dc:creator>Alfiya Tarasenko</dc:creator>
      <pubDate>Tue, 31 Mar 2026 11:47:09 +0000</pubDate>
      <link>https://forem.com/geoapify/got-coordinates-but-need-a-city-or-zip-code-this-guide-shows-how-to-convert-latlon-into-address-n9o</link>
      <guid>https://forem.com/geoapify/got-coordinates-but-need-a-city-or-zip-code-this-guide-shows-how-to-convert-latlon-into-address-n9o</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/geoapify-maps-api/how-to-get-city-postal-code-address-from-latitude-and-longitude-1b4" class="crayons-story__hidden-navigation-link"&gt;How to Get City, Postal Code, Address from Latitude and Longitude&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;
          &lt;a class="crayons-logo crayons-logo--l" href="/geoapify-maps-api"&gt;
            &lt;img alt="Geoapify Maps API logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F11485%2F7ba45fe8-d6bb-450f-971e-d78a7bb3cec3.png" class="crayons-logo__image" width="200" height="200"&gt;
          &lt;/a&gt;

          &lt;a href="/geoapify" class="crayons-avatar  crayons-avatar--s absolute -right-2 -bottom-2 border-solid border-2 border-base-inverted  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F672541%2Fb8f414ef-ff17-4230-ae03-49c5f7c04e1c.png" alt="geoapify profile" class="crayons-avatar__image" width="800" height="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/geoapify" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Alfiya Tarasenko
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Alfiya Tarasenko
                
              
              &lt;div id="story-author-preview-content-3435442" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/geoapify" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F672541%2Fb8f414ef-ff17-4230-ae03-49c5f7c04e1c.png" class="crayons-avatar__image" alt="" width="800" height="800"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Alfiya Tarasenko&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

            &lt;span&gt;
              &lt;span class="crayons-story__tertiary fw-normal"&gt; for &lt;/span&gt;&lt;a href="/geoapify-maps-api" class="crayons-story__secondary fw-medium"&gt;Geoapify Maps API&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;a href="https://dev.to/geoapify-maps-api/how-to-get-city-postal-code-address-from-latitude-and-longitude-1b4" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 31&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/geoapify-maps-api/how-to-get-city-postal-code-address-from-latitude-and-longitude-1b4" id="article-link-3435442"&gt;
          How to Get City, Postal Code, Address from Latitude and Longitude
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/codenewbie"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;codenewbie&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/geoapify-maps-api/how-to-get-city-postal-code-address-from-latitude-and-longitude-1b4#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            7 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>codenewbie</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to Get City, Postal Code, Address from Latitude and Longitude</title>
      <dc:creator>Alfiya Tarasenko</dc:creator>
      <pubDate>Tue, 31 Mar 2026 11:45:23 +0000</pubDate>
      <link>https://forem.com/geoapify-maps-api/how-to-get-city-postal-code-address-from-latitude-and-longitude-1b4</link>
      <guid>https://forem.com/geoapify-maps-api/how-to-get-city-postal-code-address-from-latitude-and-longitude-1b4</guid>
      <description>&lt;p&gt;In many applications, you don’t start with an address — you start with coordinates.&lt;/p&gt;

&lt;p&gt;A user clicks on a map, shares their GPS location, or your system receives latitude and longitude from a device. But raw coordinates like &lt;code&gt;40.748817, -73.985428&lt;/code&gt; aren’t very useful on their own. Most applications need human-readable data: a city name, a postal code, or a full address.&lt;/p&gt;

&lt;p&gt;This is where &lt;a href="https://www.geoapify.com/reverse-geocoding-api/" rel="noopener noreferrer"&gt;reverse geocoding&lt;/a&gt; comes in.&lt;/p&gt;

&lt;p&gt;In this article, we’ll look at &lt;strong&gt;how to convert latitude and longitude into structured address data&lt;/strong&gt; — including city, postal code, and formatted address — and, more importantly, what developers should be aware of when working with real-world location data.&lt;/p&gt;

&lt;p&gt;To make things more practical, we’ll also use a simple interactive demo where you can click on a map and instantly get the corresponding address details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://codepen.io/team/geoapify/pen/raMdrar" rel="noopener noreferrer"&gt;CodePen Live Demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Coordinates vs Address: Two Ways to Describe a Location
&lt;/h2&gt;

&lt;p&gt;Latitude and longitude coordinates describe an exact point on Earth using a global grid system.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;latitude defines how far north or south a location is
&lt;/li&gt;
&lt;li&gt;longitude defines how far east or west it is
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, they pinpoint a precise location anywhere on the planet:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhkfbci0c4iduw0veam7m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhkfbci0c4iduw0veam7m.png" alt="Latitude and longitude grid" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, the coordinates &lt;code&gt;40.748817, -73.985428&lt;/code&gt; represent a specific point in New York.&lt;/p&gt;

&lt;p&gt;However, while coordinates are extremely precise, they are not very intuitive for humans. People naturally think of locations in terms of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cities
&lt;/li&gt;
&lt;li&gt;streets
&lt;/li&gt;
&lt;li&gt;postal codes
&lt;/li&gt;
&lt;li&gt;full addresses
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates a gap between how machines represent locations and how humans understand them.&lt;/p&gt;

&lt;p&gt;So how do you convert coordinates into a readable address?&lt;/p&gt;

&lt;p&gt;To do that, you need a process that maps latitude and longitude to real-world location data — such as city names and postal codes. This process is called &lt;strong&gt;reverse geocoding&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reverse Geocoding: Convert Coordinates to City, Postal Code, and Address
&lt;/h2&gt;

&lt;p&gt;Reverse geocoding is the process of converting geographic coordinates (latitude and longitude) into a human-readable address.&lt;/p&gt;

&lt;p&gt;In practice, reverse geocoding is not something you implement locally. It requires access to large, constantly updated geographic datasets that map coordinates to real-world address data — including administrative boundaries, streets, buildings, and postal codes.&lt;/p&gt;

&lt;p&gt;Because of this, reverse geocoding is typically provided as a service via APIs.&lt;/p&gt;

&lt;p&gt;One example is the &lt;a href="https://www.geoapify.com/reverse-geocoding-api/" rel="noopener noreferrer"&gt;Geoapify Reverse Geocoding API&lt;/a&gt;, which allows you to send coordinates and receive structured address data in response.&lt;/p&gt;

&lt;p&gt;Here is an example API Request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;https://api.geoapify.com/v1/geocode/reverse?lat=40.748817&amp;amp;lon=-73.985428&amp;amp;format=json&amp;amp;apiKey=YOUR_API_KEY
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response contains structured fields such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;city&lt;/code&gt;, &lt;code&gt;town&lt;/code&gt;, &lt;code&gt;village&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;postcode&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;street&lt;/code&gt;, &lt;code&gt;housenumber&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;formatted&lt;/code&gt; (full address)
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also control the level of detail using the &lt;code&gt;type&lt;/code&gt; parameter, which helps filter results by address level (for example: city, postcode, street, etc.).&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting city from coordinates
&lt;/h3&gt;

&lt;p&gt;To get the city from coordinates, you can use reverse geocoding with the &lt;code&gt;type=city&lt;/code&gt; parameter. This tells the API to return results at the city level.&lt;/p&gt;

&lt;p&gt;For example:&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="err"&gt;https://api.geoapify.com/v&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;/geocode/reverse?lat=&lt;/span&gt;&lt;span class="mf"&gt;40.719726903683636&lt;/span&gt;&lt;span class="err"&gt;&amp;amp;lon=&lt;/span&gt;&lt;span class="mf"&gt;-74.04103305850015&lt;/span&gt;&lt;span class="err"&gt;&amp;amp;format=json&amp;amp;type=city&amp;amp;apiKey=YOUR_API_KEY&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A typical response looks like this:&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;"results"&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="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;"United States"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New Jersey"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jersey City"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"lon"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-74.047455&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"lat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;40.7215682&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"result_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;"city"&lt;/span&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;"Jersey City, NJ, United States of America"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"address_line1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jersey City, NJ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"address_line2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"United States of America"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;type=city&lt;/code&gt; ensures that the response focuses on the city-level administrative area, rather than returning street-level or building-level details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting postcode (ZIP code) from coordinates
&lt;/h3&gt;

&lt;p&gt;To get the postal code from coordinates, you can use reverse geocoding with the &lt;code&gt;type=postcode&lt;/code&gt; parameter. This ensures the API returns results at the postcode level.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.geoapify.com/v1/geocode/reverse?lat=40.739501996228256&amp;amp;lon=-74.18542863164123&amp;amp;type=postcode&amp;amp;format=json&amp;amp;apiKey=YOUR_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A typical response looks like this:&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;"results"&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="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;"United States"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New Jersey"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Newark"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"postcode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"07103"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"result_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;"postcode"&lt;/span&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;"Newark, NJ 07103, United States of America"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"address_line1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Newark, NJ 07103"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"address_line2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"United States of America"&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key field here is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;postcode&lt;/code&gt; — contains the postal (ZIP) code for the given coordinates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additional useful fields include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;city&lt;/code&gt; — the associated city&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;formatted&lt;/code&gt; — full human-readable address&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using type=postcode ensures that the response focuses specifically on postcode-level data.&lt;/p&gt;

&lt;p&gt;Keep in mind that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;not all locations have postal codes&lt;/li&gt;
&lt;li&gt;postal codes may represent areas rather than exact points&lt;/li&gt;
&lt;li&gt;formats vary depending on the country&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Getting address from coordinates
&lt;/h3&gt;

&lt;p&gt;To get a full address from coordinates, you can use reverse geocoding without specifying a &lt;code&gt;type&lt;/code&gt;, which returns the most relevant place for the given location (for example, a building, POI, or street address).&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.geoapify.com/v1/geocode/reverse?lat=40.68818115914695&amp;amp;lon=-73.99558678565592&amp;amp;format=json&amp;amp;apiKey=YOUR_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A typical response looks like this:&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;"results"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Citi Bike - Congress St &amp;amp; Clinton St"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"housenumber"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"183"&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;"Congress 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;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New York"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"postcode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"11201"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New York"&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;"United States"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"result_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;"amenity"&lt;/span&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;"Citi Bike - Congress St &amp;amp; Clinton St, 183 Congress Street, New York, NY 11201, United States of America"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"address_line1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Citi Bike - Congress St &amp;amp; Clinton St"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"address_line2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"183 Congress Street, New York, NY 11201, United States of America"&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most useful field for display is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;formatted&lt;/code&gt; — a ready-to-use human-readable address&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also access individual components such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; — place or POI name (if available)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;housenumber&lt;/code&gt; and &lt;code&gt;street&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;city&lt;/code&gt;, &lt;code&gt;postcode&lt;/code&gt;, &lt;code&gt;state&lt;/code&gt;, &lt;code&gt;country&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By default, reverse geocoding returns the most relevant result for the given coordinates, which may be a specific place (like a business or landmark), not just a street or city.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Pitfalls When Converting Coordinates to Address
&lt;/h2&gt;

&lt;p&gt;Reverse geocoding is powerful, but working with real-world location data comes with a few important caveats.&lt;/p&gt;

&lt;p&gt;Understanding these will help you avoid common mistakes and build more reliable applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  City is not always available
&lt;/h3&gt;

&lt;p&gt;Not every location has a &lt;code&gt;city&lt;/code&gt; field. Depending on where the coordinates point, the response may include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;city&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;town&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;village&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;or no settlement at all
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To handle this correctly, always implement a fallback strategy instead of relying on a single field.&lt;/p&gt;

&lt;h3&gt;
  
  
  The result depends on the exact point
&lt;/h3&gt;

&lt;p&gt;Small changes in coordinates can lead to different results.&lt;/p&gt;

&lt;p&gt;For example, clicking on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a building → returns a specific address
&lt;/li&gt;
&lt;li&gt;a road → returns street-level data
&lt;/li&gt;
&lt;li&gt;a park → may return only city or district
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is expected behavior, not an error.&lt;/p&gt;

&lt;h3&gt;
  
  
  The closest result may not match expectations
&lt;/h3&gt;

&lt;p&gt;Reverse geocoding returns the &lt;strong&gt;nearest known object&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This could be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a building
&lt;/li&gt;
&lt;li&gt;a business (POI)
&lt;/li&gt;
&lt;li&gt;a street
&lt;/li&gt;
&lt;li&gt;an administrative area
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need a specific level (e.g., city or postcode), use the &lt;code&gt;type&lt;/code&gt; parameter to control the result.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coordinates may need normalization
&lt;/h3&gt;

&lt;p&gt;Coordinates should always be valid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;latitude must be between -90 and 90
&lt;/li&gt;
&lt;li&gt;longitude must be between -180 and 180
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It Yourself: Interactive Reverse Geocoding Demo
&lt;/h2&gt;

&lt;p&gt;To better understand how reverse geocoding works in practice, try this interactive demo:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/team/geoapify/embed/raMdrar?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://codepen.io/team/geoapify/pen/raMdrar" rel="noopener noreferrer"&gt;Live Demo on CodePen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The demo also shows how different clicks can return different types of results depending on the location — for example, a building, a street, or an administrative area.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Converting latitude and longitude into a city, postal code, or full address is a common requirement in many applications — from delivery services to map-based interfaces.&lt;/p&gt;

&lt;p&gt;In this article, we explored how reverse geocoding works and how to use it to extract meaningful location data from coordinates.&lt;/p&gt;

&lt;p&gt;Key takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;coordinates describe precise locations, but are not human-friendly
&lt;/li&gt;
&lt;li&gt;reverse geocoding bridges the gap between coordinates and real-world addresses
&lt;/li&gt;
&lt;li&gt;you can extract specific data like city and postcode using the &lt;code&gt;type&lt;/code&gt; parameter
&lt;/li&gt;
&lt;li&gt;not all locations have complete address data, so fallback logic is important
&lt;/li&gt;
&lt;li&gt;results may vary depending on the exact coordinates and context
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By understanding these concepts and handling edge cases properly, you can build reliable features that convert coordinates into usable address information.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://apidocs.geoapify.com/docs/geocoding/reverse-geocoding/" rel="noopener noreferrer"&gt;Geoapify Reverse Geocoding API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://maplibre.org/maplibre-gl-js/docs/" rel="noopener noreferrer"&gt;MapLibre GL JS Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codepen.io/team/geoapify/pen/raMdrar" rel="noopener noreferrer"&gt;Live Demo on CodePen&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is reverse geocoding free?
&lt;/h3&gt;

&lt;p&gt;Most reverse geocoding services offer a free tier with usage limits.&lt;/p&gt;

&lt;p&gt;For example, &lt;a href="https://www.geoapify.com/" rel="noopener noreferrer"&gt;Geoapify&lt;/a&gt; provides a free plan with 3000 requests per day and 5RPS. This is usually enough for testing and small applications. For production use or higher volumes, paid plans are available.&lt;/p&gt;




&lt;h3&gt;
  
  
  How do I get an API key?
&lt;/h3&gt;

&lt;p&gt;To use a reverse geocoding service like Geoapify, you need an API key.&lt;/p&gt;

&lt;p&gt;You can get one by registering at &lt;a href="https://www.geoapify.com/" rel="noopener noreferrer"&gt;the website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After creating a project, you’ll receive an API key that you can use in your requests.&lt;/p&gt;




&lt;h3&gt;
  
  
  How do I get the timezone from coordinates?
&lt;/h3&gt;

&lt;p&gt;Reverse geocoding responses often include timezone information.&lt;/p&gt;

&lt;p&gt;For example, Geoapify returns a &lt;code&gt;timezone&lt;/code&gt; object with:&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"America/New_York"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"offset_STD"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-05:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"offset_STD_seconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-18000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"offset_DST"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-04:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"offset_DST_seconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-14400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"abbreviation_STD"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"abbreviation_DST"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EDT"&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;You can extract this directly from the response without making a separate API call.&lt;/p&gt;




&lt;h3&gt;
  
  
  How do I get the state or region?
&lt;/h3&gt;

&lt;p&gt;To get the state or region from coordinates, you can use reverse geocoding with the &lt;code&gt;type=state&lt;/code&gt; parameter. This ensures the API returns results at the state level.&lt;/p&gt;




&lt;h3&gt;
  
  
  How can I understand the quality of the result?
&lt;/h3&gt;

&lt;p&gt;You can estimate how accurate the result is using the &lt;code&gt;distance&lt;/code&gt; field.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;distance&lt;/code&gt; represents how far (in meters) the returned result is from the input coordinates
&lt;/li&gt;
&lt;li&gt;smaller values indicate higher accuracy
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;0–10 meters → very precise (building-level)
&lt;/li&gt;
&lt;li&gt;10–100 meters → good accuracy
&lt;/li&gt;
&lt;li&gt;100+ meters → approximate result
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Why do I sometimes get a place (POI) instead of an address?
&lt;/h3&gt;

&lt;p&gt;By default, reverse geocoding returns the most relevant nearby object.&lt;/p&gt;

&lt;p&gt;This could be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a business or landmark (POI)
&lt;/li&gt;
&lt;li&gt;a building
&lt;/li&gt;
&lt;li&gt;a street
&lt;/li&gt;
&lt;li&gt;an administrative area
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need a specific level (e.g., city or postcode), use the &lt;code&gt;type&lt;/code&gt; parameter to control the result.&lt;/p&gt;




&lt;h3&gt;
  
  
  Can I use reverse geocoding offline?
&lt;/h3&gt;

&lt;p&gt;In most cases, no.&lt;/p&gt;

&lt;p&gt;Reverse geocoding requires large and frequently updated datasets, making it impractical to run locally. That’s why it is typically provided as an online API service.&lt;/p&gt;




&lt;h3&gt;
  
  
  What happens if there are no results?
&lt;/h3&gt;

&lt;p&gt;In some cases, reverse geocoding may return no results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;remote areas
&lt;/li&gt;
&lt;li&gt;oceans or lakes
&lt;/li&gt;
&lt;li&gt;incomplete data coverage
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your application should handle this gracefully and provide fallback behavior.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>codenewbie</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Loading hundreds of places on a map? Learn how to make it fast and smooth!
This tutorial shows how to use the Geoapify Places API with Leaflet, split map areas into chunks, and apply rate limiting for better performance and UX.</title>
      <dc:creator>Alfiya Tarasenko</dc:creator>
      <pubDate>Fri, 07 Nov 2025 14:19:03 +0000</pubDate>
      <link>https://forem.com/geoapify/loading-hundreds-of-places-on-a-map-learn-how-to-make-it-fast-and-smooth-this-tutorial-shows-how-56j2</link>
      <guid>https://forem.com/geoapify/loading-hundreds-of-places-on-a-map-learn-how-to-make-it-fast-and-smooth-this-tutorial-shows-how-56j2</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/geoapify-maps-api/handle-large-place-searches-chunked-and-rate-limited-search-in-leaflet-g73" class="crayons-story__hidden-navigation-link"&gt;Handle Large Place Searches: Chunked and Rate-Limited Search in Leaflet&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;
          &lt;a class="crayons-logo crayons-logo--l" href="/geoapify-maps-api"&gt;
            &lt;img alt="Geoapify Maps API logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F11485%2F7ba45fe8-d6bb-450f-971e-d78a7bb3cec3.png" class="crayons-logo__image"&gt;
          &lt;/a&gt;

          &lt;a href="/geoapify" class="crayons-avatar  crayons-avatar--s absolute -right-2 -bottom-2 border-solid border-2 border-base-inverted  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F672541%2Fb8f414ef-ff17-4230-ae03-49c5f7c04e1c.png" alt="geoapify profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/geoapify" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Alfiya Tarasenko
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Alfiya Tarasenko
                
              
              &lt;div id="story-author-preview-content-3001116" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/geoapify" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F672541%2Fb8f414ef-ff17-4230-ae03-49c5f7c04e1c.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Alfiya Tarasenko&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

            &lt;span&gt;
              &lt;span class="crayons-story__tertiary fw-normal"&gt; for &lt;/span&gt;&lt;a href="/geoapify-maps-api" class="crayons-story__secondary fw-medium"&gt;Geoapify Maps API&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;a href="https://dev.to/geoapify-maps-api/handle-large-place-searches-chunked-and-rate-limited-search-in-leaflet-g73" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Nov 7 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/geoapify-maps-api/handle-large-place-searches-chunked-and-rate-limited-search-in-leaflet-g73" id="article-link-3001116"&gt;
          Handle Large Place Searches: Chunked and Rate-Limited Search in Leaflet
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/leaflet"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;leaflet&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/api"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;api&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/geoapify-maps-api/handle-large-place-searches-chunked-and-rate-limited-search-in-leaflet-g73" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/geoapify-maps-api/handle-large-place-searches-chunked-and-rate-limited-search-in-leaflet-g73#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            6 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;




</description>
      <category>webdev</category>
      <category>leaflet</category>
      <category>api</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Handle Large Place Searches: Chunked and Rate-Limited Search in Leaflet</title>
      <dc:creator>Alfiya Tarasenko</dc:creator>
      <pubDate>Fri, 07 Nov 2025 14:18:23 +0000</pubDate>
      <link>https://forem.com/geoapify-maps-api/handle-large-place-searches-chunked-and-rate-limited-search-in-leaflet-g73</link>
      <guid>https://forem.com/geoapify-maps-api/handle-large-place-searches-chunked-and-rate-limited-search-in-leaflet-g73</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/geoapify-maps-api/how-to-query-and-display-nearby-places-on-a-leaflet-map-with-the-geoapify-places-api-43p8"&gt;previous tutorial&lt;/a&gt;, we used the &lt;a href="https://www.geoapify.com/places-api/" rel="noopener noreferrer"&gt;&lt;strong&gt;Geoapify Places API&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://leafletjs.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Leaflet&lt;/strong&gt;&lt;/a&gt; to display nearby places — like parks, museums, and cinemas — on a map.&lt;/p&gt;

&lt;p&gt;That simple approach works well for small areas, but loading &lt;strong&gt;hundreds of places at once&lt;/strong&gt; can slow the map or exceed API rate limits.&lt;/p&gt;

&lt;p&gt;In this part, you’ll learn how to make place searches more efficient by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Splitting the map area into &lt;strong&gt;chunks&lt;/strong&gt; for smaller, faster requests&lt;/li&gt;
&lt;li&gt;Applying &lt;a href="https://www.npmjs.com/package/@geoapify/request-rate-limiter" rel="noopener noreferrer"&gt;rate limiting&lt;/a&gt; to control request flow&lt;/li&gt;
&lt;li&gt;Rendering results &lt;strong&gt;progressively&lt;/strong&gt; for smoother UX&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try the full example:&lt;br&gt;
➡️ &lt;a href="https://codepen.io/geoapify/pen/MYKRMBr" rel="noopener noreferrer"&gt;&lt;strong&gt;View on CodePen&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sure! Here’s a clean, Dev.to–ready &lt;strong&gt;Table of Contents&lt;/strong&gt; for your article.&lt;br&gt;
It matches your section headings exactly and uses the same anchor style Dev.to automatically generates.&lt;/p&gt;
&lt;h2&gt;
  
  
  🧭 Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Why large place searches can be slow&lt;/li&gt;
&lt;li&gt;A better approach&lt;/li&gt;
&lt;li&gt;Split the map bounds into smaller fragments&lt;/li&gt;
&lt;li&gt;Run fragment requests with rate limiting&lt;/li&gt;
&lt;li&gt;See the full implementation&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Why large place searches can be slow
&lt;/h2&gt;

&lt;p&gt;When your map covers a large area — for example, an entire city — the &lt;a href="https://www.geoapify.com/places-api/" rel="noopener noreferrer"&gt;&lt;strong&gt;Geoapify Places API&lt;/strong&gt;&lt;/a&gt; may need to process &lt;strong&gt;thousands of locations&lt;/strong&gt; at once.&lt;br&gt;
If you request all of them in a single query:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;API call can take longer&lt;/strong&gt; to complete.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;response size becomes large&lt;/strong&gt;, increasing data transfer time.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;map may freeze&lt;/strong&gt; while parsing or rendering the results.&lt;/li&gt;
&lt;li&gt;Users experience &lt;strong&gt;visible lag&lt;/strong&gt; or delayed updates.&lt;/li&gt;
&lt;li&gt;It might even require &lt;strong&gt;querying multiple pages&lt;/strong&gt; of results to retrieve all places in that area.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  A better approach
&lt;/h2&gt;

&lt;p&gt;To keep your app &lt;strong&gt;fast and user-friendly&lt;/strong&gt;, it’s better to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Split the map area into smaller parts (chunks)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Each chunk is a smaller rectangle inside the visible map bounds.&lt;/li&gt;
&lt;li&gt;The app sends several lightweight queries instead of one large request.&lt;/li&gt;
&lt;li&gt;Results load progressively — users start seeing places immediately while others continue loading in the background.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6c38vg8pl5x0qjz0e85v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6c38vg8pl5x0qjz0e85v.png" alt="Splitting the map area into smaller queries" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Description: A map view divided into several rectangles, each labeled “API request,” showing places being loaded one fragment at a time. Arrows can indicate parallel or sequential loading.&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Apply rate limiting&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you send multiple requests in parallel — for example, one per map fragment — it’s important to control how often they are executed.&lt;/p&gt;

&lt;p&gt;Without rate limiting, dozens of simultaneous API calls can overload the network or exceed the API’s allowed request rate, leading to &lt;strong&gt;&lt;a href="https://www.geoapify.com/how-to-avoid-429-too-many-requests-with-api-rate-limiting/" rel="noopener noreferrer"&gt;HTTP 429 (Too Many Requests)&lt;/a&gt;&lt;/strong&gt; errors.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://www.npmjs.com/package/@geoapify/request-rate-limiter" rel="noopener noreferrer"&gt;Geoapify Request Rate Limiter&lt;/a&gt; automatically spaces out requests over time.
&lt;/li&gt;
&lt;li&gt;It ensures you stay within the API’s limits, keeps the map responsive, and provides a smooth, continuous loading experience for users.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2rv6r6wc04yz62mwk11a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2rv6r6wc04yz62mwk11a.png" alt="Too Many Requests — When Rate Limiting Is Missing" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Description: If rate limits are not respected, some API requests return 429 (Too Many Requests) errors.&lt;br&gt;
In this example, the API allows 5 requests per second, but 9 are sent simultaneously — so 4 fail. The map shows those failed fragments highlighted in red.&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;Now that we understand why large queries can cause slow responses or 429 errors, let’s look at how to &lt;strong&gt;split the visible map area into smaller fragments&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Split the map bounds into smaller fragments
&lt;/h2&gt;

&lt;p&gt;To make large-area searches more efficient, we’ll divide the &lt;strong&gt;visible map area&lt;/strong&gt; into several smaller rectangles, called &lt;em&gt;fragments&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For example, the following helper function decides how many pieces the map should be split into depending on the &lt;strong&gt;zoom level&lt;/strong&gt; (fewer at high zoom, more at low zoom):&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;function&lt;/span&gt; &lt;span class="nf"&gt;getAxisChunksForZoom&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&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;17&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;z&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;delta&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// At zoom ≥ 16, one request is enough&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// At lower zooms, split the area into more parts&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The function takes the current &lt;strong&gt;map zoom level&lt;/strong&gt; as input.&lt;/li&gt;
&lt;li&gt;When you’re &lt;strong&gt;zoomed in close&lt;/strong&gt; (zoom ≥ 16), the visible area is small, so only &lt;strong&gt;one fragment&lt;/strong&gt; (one request) is needed.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;As you &lt;strong&gt;zoom out&lt;/strong&gt;, the visible area grows. The function increases the number of fragments exponentially — for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At zoom 15 → 2 x 2 fragments per axis&lt;/li&gt;
&lt;li&gt;At zoom 14 → 3 x 3 fragments&lt;/li&gt;
&lt;li&gt;At zoom 13 → 4 x 4 fragments&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;This ensures that large views (like an entire city) are split into multiple smaller bounding boxes, each of which can be queried quickly and displayed independently.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Next, we’ll use this helper to actually &lt;strong&gt;divide the map bounds into fragments&lt;/strong&gt; — small rectangular areas that together cover the visible region.&lt;/p&gt;

&lt;p&gt;The following function, &lt;code&gt;getFragments(bounds, zoom)&lt;/code&gt;, takes the current map bounds and zoom level, then returns an array of rectangles. Each rectangle defines a &lt;strong&gt;mini viewbox&lt;/strong&gt; that we’ll later use to query the &lt;a href="https://www.geoapify.com/places-api/" rel="noopener noreferrer"&gt;Geoapify Places API&lt;/a&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;function&lt;/span&gt; &lt;span class="nf"&gt;getFragments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bounds&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="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;bounds&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAxisChunksForZoom&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSouthWest&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;ne&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getNorthEast&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// If only one chunk, return the full map bounds&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;chunks&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;minLng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sw&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="na"&gt;minLat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sw&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="na"&gt;maxLng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ne&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="na"&gt;maxLat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ne&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="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;latStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ne&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="nx"&gt;sw&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="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;chunks&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;lngStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ne&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="nx"&gt;sw&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;chunks&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;rects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="c1"&gt;// Loop through rows and columns to build smaller rectangles&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;let&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&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;row&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;minLat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sw&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="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;latStep&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;maxLat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ne&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;minLat&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;latStep&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;let&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&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;col&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;minLng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sw&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="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;lngStep&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;maxLng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ne&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;minLng&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;lngStep&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;rects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;minLng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;minLat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxLng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxLat&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;rects&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;h3&gt;
  
  
  What this does
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Calculates how many pieces (&lt;code&gt;chunks&lt;/code&gt;) the area should be split into based on zoom level.&lt;/li&gt;
&lt;li&gt;Divides the visible map into a &lt;strong&gt;grid of rectangles&lt;/strong&gt;, using latitude and longitude steps.&lt;/li&gt;
&lt;li&gt;Returns an array of bounding boxes — each one ready to be used in a request like:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  filter=rect:minLon,minLat,maxLon,maxLat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a result, instead of sending one large, heavy API query, we can make multiple smaller requests — one per fragment — and load results &lt;strong&gt;progressively and smoothly&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run fragment requests with rate limiting
&lt;/h2&gt;

&lt;p&gt;Now that we can split the view into fragments and build one request per fragment, let’s &lt;strong&gt;execute them safely&lt;/strong&gt;.&lt;br&gt;
We’ll use &lt;code&gt;[@geoapify/request-rate-limiter](https://www.npmjs.com/package/@geoapify/request-rate-limiter)&lt;/code&gt; to pace requests so the UI stays smooth and we avoid bursts.&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;// Build one async function per fragment (from previous step)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestFns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fragments&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;minLng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;minLat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxLng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxLat&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;rect&lt;/span&gt; &lt;span class="o"&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;minLng&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;minLat&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;maxLng&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;maxLat&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="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="s2"&gt;`https://api.geoapify.com/v2/places?categories=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;filter=rect:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;limit=200&amp;amp;apiKey=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;yourAPIKey&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="k"&gt;async &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;resp&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="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;resp&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;`HTTP &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;resp&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="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;resp&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="nf"&gt;renderResults&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="c1"&gt;// progressive rendering: draw as each fragment returns&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="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Run with rate limiting: 5 requests per 1000 ms window&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;RequestRateLimiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rateLimitedRequests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;requestFns&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="c1"&gt;// max requests per window&lt;/span&gt;
  &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// window duration (ms)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;onProgress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;completedRequests&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;totalRequests&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="c1"&gt;// optional: update a small status label&lt;/span&gt;
      &lt;span class="c1"&gt;// e.g., setStatus(`Loaded ${completedRequests}/${totalRequests} fragments…`);&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;ul&gt;
&lt;li&gt;
&lt;strong&gt;Paces outgoing requests&lt;/strong&gt; to avoid spikes and keep the app responsive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Renders progressively&lt;/strong&gt;: users see places appear fragment-by-fragment, not all at once.&lt;/li&gt;
&lt;li&gt;Keeps your code simple: pass an array of tasks and let the limiter schedule them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  See the full implementation
&lt;/h2&gt;

&lt;p&gt;You can explore the complete working version in the CodePen example:&lt;br&gt;
➡️ &lt;a href="https://codepen.io/geoapify/pen/MYKRMBr" rel="noopener noreferrer"&gt;&lt;strong&gt;View on CodePen&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code sample demonstrates everything described in this tutorial — including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rate-limited API requests&lt;/strong&gt; to keep the app smooth and avoid 429 errors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canceling outdated searches&lt;/strong&gt; when the user moves or zooms the map again&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Progressive visualization&lt;/strong&gt; of results as each fragment loads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, these techniques make large place searches fast, stable, and user-friendly — even when displaying thousands of points across a wide area.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we improved the performance of large-area searches in Leaflet using the &lt;a href="https://www.geoapify.com/places-api/" rel="noopener noreferrer"&gt;&lt;strong&gt;Geoapify Places API&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You learned how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Split the visible map area into &lt;strong&gt;smaller fragments&lt;/strong&gt; for faster, lighter API calls&lt;/li&gt;
&lt;li&gt;Apply &lt;strong&gt;rate limiting&lt;/strong&gt; with the &lt;a href="https://www.npmjs.com/package/@geoapify/request-rate-limiter" rel="noopener noreferrer"&gt;&lt;strong&gt;Geoapify Request Rate Limiter&lt;/strong&gt;&lt;/a&gt; to avoid 429 errors&lt;/li&gt;
&lt;li&gt;Load and render places &lt;strong&gt;progressively&lt;/strong&gt; for a smoother, more responsive user experience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see the complete implementation — including canceling outdated searches and visualizing data — in the live CodePen example:&lt;br&gt;
➡️ &lt;a href="https://codepen.io/geoapify/pen/MYKRMBr" rel="noopener noreferrer"&gt;&lt;strong&gt;View the complete example&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>leaflet</category>
      <category>api</category>
      <category>javascript</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Alfiya Tarasenko</dc:creator>
      <pubDate>Wed, 05 Nov 2025 14:31:05 +0000</pubDate>
      <link>https://forem.com/geoapify/-2cio</link>
      <guid>https://forem.com/geoapify/-2cio</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/geoapify-maps-api" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F11485%2F7ba45fe8-d6bb-450f-971e-d78a7bb3cec3.png" alt="Geoapify Maps API" width="200" height="200"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F672541%2Fb8f414ef-ff17-4230-ae03-49c5f7c04e1c.png" alt="" width="800" height="800"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/geoapify-maps-api/how-to-query-and-display-nearby-places-on-a-leaflet-map-with-the-geoapify-places-api-43p8" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;How to Query and Display Nearby Places on a Leaflet Map with the Geoapify Places API&lt;/h2&gt;
      &lt;h3&gt;Alfiya Tarasenko for Geoapify Maps API ・ Nov 5&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#codepen&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#leaflet&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#geoapify&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>codepen</category>
      <category>leaflet</category>
      <category>webdev</category>
      <category>geoapify</category>
    </item>
    <item>
      <title>How to Query and Display Nearby Places on a Leaflet Map with the Geoapify Places API</title>
      <dc:creator>Alfiya Tarasenko</dc:creator>
      <pubDate>Wed, 05 Nov 2025 13:46:21 +0000</pubDate>
      <link>https://forem.com/geoapify-maps-api/how-to-query-and-display-nearby-places-on-a-leaflet-map-with-the-geoapify-places-api-43p8</link>
      <guid>https://forem.com/geoapify-maps-api/how-to-query-and-display-nearby-places-on-a-leaflet-map-with-the-geoapify-places-api-43p8</guid>
      <description>&lt;p&gt;In this tutorial, you’ll learn how to use the &lt;a href="https://www.geoapify.com/places-api/" rel="noopener noreferrer"&gt;&lt;strong&gt;Geoapify Places API&lt;/strong&gt;&lt;/a&gt; with &lt;a href="https://leafletjs.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Leaflet&lt;/strong&gt;&lt;/a&gt; to display nearby places — such as parks, museums, and cinemas — on an interactive map.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try the live example&lt;/strong&gt;:&lt;br&gt;
➡️ &lt;a href="https://codepen.io/geoapify/pen/MYKRMBr" rel="noopener noreferrer"&gt;View on CodePen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this part, we’ll focus on the basics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Querying the Places API by category&lt;/li&gt;
&lt;li&gt;Parsing and displaying GeoJSON results&lt;/li&gt;
&lt;li&gt;Styling map markers with the &lt;a href="https://www.geoapify.com/map-marker-icon-api/" rel="noopener noreferrer"&gt;Geoapify Marker Icon API&lt;/a&gt; and showing popups in Leaflet&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🧭 Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Step 1 – Set up the Leaflet Map&lt;/li&gt;
&lt;li&gt;Step 2 – Query Places by Category Using the Geoapify Places API&lt;/li&gt;
&lt;li&gt;Step 3 – Display Places as Custom Markers (Marker Icon API)&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  Step 1 – Set up the Leaflet Map
&lt;/h2&gt;

&lt;p&gt;Let’s start by setting up a basic &lt;strong&gt;Leaflet map&lt;/strong&gt; with &lt;a href="https://www.geoapify.com/map-tiles/" rel="noopener noreferrer"&gt;Geoapify basemap tiles&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: You don’t need to build it from scratch — use this ready-to-edit CodePen template:&lt;br&gt;
➡️ &lt;a href="https://codepen.io/pen?template=gbadJzb" rel="noopener noreferrer"&gt;Open Leaflet Map Template&lt;/a&gt;&lt;br&gt;
It will create a new Codepen with a Leaflet map. Please replace the template Geoapify API key with your API key. Register and get your Free API key &lt;a href="https://www.geoapify.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here are main steps to create a map:&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;yourAPIKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR_GEOAPIFY_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;// Create the map and center it on London&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="s2"&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;51.5074&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.1278&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="c1"&gt;// Detect high-resolution displays for crisper tiles&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isRetina&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="nx"&gt;devicePixelRatio&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Use Geoapify tiles — regular or @2x for retina&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://maps.geoapify.com/v1/tile/klokantech-basic/{z}/{x}/{y}.png?apiKey=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;yourAPIKey&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;retinaUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://maps.geoapify.com/v1/tile/klokantech-basic/{z}/{x}/{y}@2x.png?apiKey=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;yourAPIKey&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="c1"&gt;// Add the tile layer&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="nx"&gt;isRetina&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;retinaUrl&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;,&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;Powered by &amp;lt;a href="https://www.geoapify.com/" target="_blank"&amp;gt;Geoapify&amp;lt;/a&amp;gt; | &amp;lt;a href="https://openmaptiles.org/" target="_blank"&amp;gt;© OpenMapTiles&amp;lt;/a&amp;gt; &amp;lt;a href="https://www.openstreetmap.org/copyright" target="_blank"&amp;gt;© OpenStreetMap&amp;lt;/a&amp;gt; contributors&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;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;osm-bright&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;yourAPIKey&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;This creates a Leaflet map centered on London using the &lt;strong&gt;Geoapify “klokantech-basic”&lt;/strong&gt; tile style.&lt;br&gt;
The &lt;code&gt;isRetina&lt;/code&gt; check ensures that users on high-DPI displays see sharp, high-resolution tiles.&lt;/p&gt;

&lt;p&gt;Next, we’ll connect the &lt;strong&gt;Geoapify Places API&lt;/strong&gt; to query and show real places on the map.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2 – Query Places by Category Using the Geoapify Places API
&lt;/h2&gt;

&lt;p&gt;Now that the map is ready, let’s make it &lt;strong&gt;interactive&lt;/strong&gt; by automatically loading places whenever the user pans or zooms.&lt;/p&gt;

&lt;p&gt;We’ll connect to the &lt;a href="https://www.geoapify.com/places-api/" rel="noopener noreferrer"&gt;&lt;strong&gt;Geoapify Places API&lt;/strong&gt;&lt;/a&gt; and fetch real locations — such as museums, parks, or cinemas — based on the &lt;strong&gt;current map view&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Listen to map movement events
&lt;/h3&gt;

&lt;p&gt;We want to detect when the user moves or zooms the map, then trigger a new API request.&lt;br&gt;
Leaflet makes this easy with &lt;code&gt;moveend&lt;/code&gt; and &lt;code&gt;zoomend&lt;/code&gt; events:&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="s2"&gt;moveend&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loadPlaces&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;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zoomend&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loadPlaces&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whenever the map position changes, the &lt;code&gt;loadPlaces()&lt;/code&gt; function will run and update the visible results.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Load places from the Geoapify Places API
&lt;/h3&gt;

&lt;p&gt;Inside &lt;code&gt;loadPlaces()&lt;/code&gt;, we:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get the map’s current bounds.&lt;/li&gt;
&lt;li&gt;Define which categories we want to search (for example, museums, cinemas, or parks).&lt;/li&gt;
&lt;li&gt;Build and send the API request.
&lt;/li&gt;
&lt;/ol&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;loadPlaces&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;bounds&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;getBounds&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;rect&lt;/span&gt; &lt;span class="o"&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;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getWest&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;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSouth&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;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEast&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;bounds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getNorth&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;categories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;entertainment.museum,entertainment.cinema,leisure.park&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://api.geoapify.com/v2/places?categories=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;filter=rect:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;limit=100&amp;amp;apiKey=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;yourAPIKey&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&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="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;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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Places loaded:&lt;/span&gt;&lt;span class="dl"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Tip: The key part is &lt;code&gt;filter=rect:${rect}&lt;/code&gt; — it limits the search to the &lt;strong&gt;current map bounds&lt;/strong&gt;, so the API only returns places visible on the map. This makes the search faster, more relevant, and efficient.&lt;/p&gt;

&lt;p&gt;You can explore all available categories in the &lt;a href="https://apidocs.geoapify.com/docs/places/#categories" rel="noopener noreferrer"&gt;Places API docs&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the &lt;strong&gt;simplest version&lt;/strong&gt; of a map-based search — it calls the API every time the user stops moving the map and retrieves up to 100 places in the visible area.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚙️ In real-world apps
&lt;/h3&gt;

&lt;p&gt;For better performance and smoother user experience, you’ll want to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add a short delay (debounce)&lt;/strong&gt; between map movement and the API request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split large map areas into smaller fragments&lt;/strong&gt; to avoid long or heavy queries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limit requests by zoom level&lt;/strong&gt; (e.g., only start searching when &lt;code&gt;zoom &amp;gt;= 13&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prevent duplicate results&lt;/strong&gt; if multiple requests overlap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate-limit API calls&lt;/strong&gt; to stay within usage limits.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All of these optimizations are implemented in the &lt;strong&gt;full CodePen example&lt;/strong&gt;:&lt;br&gt;
➡️ &lt;a href="https://codepen.io/geoapify/pen/MYKRMBr" rel="noopener noreferrer"&gt;View the complete version on CodePen&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3 – Display places as custom markers (Marker Icon API)
&lt;/h2&gt;

&lt;p&gt;We’ll parse the GeoJSON from the Places API and render each &lt;code&gt;Point&lt;/code&gt; as a Leaflet marker.&lt;br&gt;
To make markers visually meaningful, we’ll generate icon URLs on the fly with the &lt;a href="https://apidocs.geoapify.com/playground/icon/" rel="noopener noreferrer"&gt;Geoapify Marker Icon API&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  1) Create a marker icon helper
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;markerIconUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iconName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;map-marker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#37a961&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;icon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iconName&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;tint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`https://api.geoapify.com/v2/icon/?type=awesome&amp;amp;icon=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;color=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tint&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;size=50&amp;amp;contentSize=20&amp;amp;scaleFactor=2&amp;amp;apiKey=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;yourAPIKey&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;This will create a custom marker icon sized about &lt;strong&gt;38×55 px&lt;/strong&gt; — the visible icon itself is 38×50 px, and the subtle drop shadow adds roughly 5 px for depth.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;scaleFactor=2&lt;/code&gt; makes icons sharp on retina screens by rendering them at double resolution.&lt;/p&gt;
&lt;h3&gt;
  
  
  2) Render features as markers with popups
&lt;/h3&gt;

&lt;p&gt;Add this after &lt;code&gt;loadPlaces()&lt;/code&gt; fetches the data:&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;function&lt;/span&gt; &lt;span class="nf"&gt;renderPlaces&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;geojson&lt;/span&gt;&lt;span class="p"&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;geojson&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;geojson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;features&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="c1"&gt;// Optional: group for easy clear/redraw on next search&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resultsLayer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resultsLayer&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;layerGroup&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resultsLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clearLayers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;geojson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;features&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;f&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="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;properties&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;f&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;geometry&lt;/span&gt; &lt;span class="o"&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;type&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Point&lt;/span&gt;&lt;span class="dl"&gt;"&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="kd"&gt;const&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="o"&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;coordinates&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Pick an icon based on categories (fallback to a generic one)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iconName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;map-marker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// you can map categories -&amp;gt; icon names if you like&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;icon&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;icon&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;iconUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;markerIconUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iconName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#22c55e&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;iconSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Icon image size in pixels (width x height)&lt;/span&gt;
      &lt;span class="na"&gt;iconAnchor&lt;/span&gt;&lt;span class="p"&gt;:&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="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// The point of the icon (relative to its top-left corner) that will correspond to the marker’s geographical location&lt;/span&gt;
      &lt;span class="na"&gt;popupAnchor&lt;/span&gt;&lt;span class="p"&gt;:&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="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// Offset of the popup relative to the icon anchor. Moves the popup slightly above the icon tip&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="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;address_line1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unnamed place&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;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;address_line2&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;formatted&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&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="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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;icon&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="s2"&gt;`&amp;lt;strong&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;escapeHtml&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;&amp;lt;/strong&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;escapeHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&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="nf"&gt;addTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resultsLayer&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="c1"&gt;// Small helper to avoid HTML injection in popups&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;escapeHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&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="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;&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="s2"&gt;&amp;amp;amp;&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;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;&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="s2"&gt;&amp;amp;lt;&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;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&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="s2"&gt;&amp;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;replaceAll&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;quot;&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;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'&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="s2"&gt;&amp;amp;#039;&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;Call &lt;code&gt;renderPlaces(data)&lt;/code&gt; at the end of &lt;code&gt;loadPlaces()&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;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;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="nf"&gt;renderPlaces&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What you get
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Custom icons&lt;/strong&gt; generated by the Marker Icon API (fast, no image hosting needed).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Informative popups&lt;/strong&gt; with place names and addresses.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;clean layer group&lt;/strong&gt; so the map updates neatly on each search.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: For even better UX, map specific categories (e.g., &lt;code&gt;entertainment.museum&lt;/code&gt;, &lt;code&gt;leisure.park&lt;/code&gt;) to different icon names or colors so users can visually distinguish them at a glance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we built an interactive &lt;strong&gt;Leaflet map&lt;/strong&gt; powered by the &lt;a href="https://www.geoapify.com/places-api/" rel="noopener noreferrer"&gt;&lt;strong&gt;Geoapify Places API&lt;/strong&gt;&lt;/a&gt;.&lt;br&gt;
You learned how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up a map with &lt;strong&gt;Geoapify basemap tiles&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Query the &lt;strong&gt;Places API&lt;/strong&gt; by category and map bounds&lt;/li&gt;
&lt;li&gt;Parse &lt;strong&gt;GeoJSON&lt;/strong&gt; results&lt;/li&gt;
&lt;li&gt;Display results as &lt;strong&gt;custom markers&lt;/strong&gt; using the &lt;a href="https://www.geoapify.com/map-marker-icon-api/" rel="noopener noreferrer"&gt;&lt;strong&gt;Geoapify Marker Icon API&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is a map that dynamically shows real places — like museums, cinemas, or parks — with meaningful icons and popups.&lt;/p&gt;

&lt;p&gt;For smoother performance and a more responsive UX, check out the &lt;strong&gt;advanced version&lt;/strong&gt; of this project:&lt;br&gt;
➡️ &lt;a href="https://codepen.io/geoapify/pen/MYKRMBr" rel="noopener noreferrer"&gt;Full CodePen example&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;next tutorial&lt;/strong&gt;, we’ll dive deeper into performance optimizations — including &lt;strong&gt;chunked API requests&lt;/strong&gt;, &lt;strong&gt;rate limiting&lt;/strong&gt;, and &lt;strong&gt;progressive loading&lt;/strong&gt; for large map areas.&lt;/p&gt;

</description>
      <category>codepen</category>
      <category>leaflet</category>
      <category>webdev</category>
      <category>geoapify</category>
    </item>
    <item>
      <title>Turn your GeoJSON data into interactive maps — visualize Points, Routes, and Isolines in Leaflet with Geoapify.</title>
      <dc:creator>Alfiya Tarasenko</dc:creator>
      <pubDate>Tue, 28 Oct 2025 10:07:06 +0000</pubDate>
      <link>https://forem.com/geoapify/turn-your-geojson-data-into-interactive-maps-visualize-points-routes-and-isolines-in-leaflet-5dmc</link>
      <guid>https://forem.com/geoapify/turn-your-geojson-data-into-interactive-maps-visualize-points-routes-and-isolines-in-leaflet-5dmc</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/geoapify-maps-api" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F11485%2F7ba45fe8-d6bb-450f-971e-d78a7bb3cec3.png" alt="Geoapify Maps API" width="200" height="200"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F672541%2Fb8f414ef-ff17-4230-ae03-49c5f7c04e1c.png" alt="" width="800" height="800"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/geoapify-maps-api/how-to-visualize-geojson-points-lines-and-polygons-on-a-leaflet-map-1p9a" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;How to Visualize GeoJSON Points, Lines, and Polygons on a Leaflet Map&lt;/h2&gt;
      &lt;h3&gt;Alfiya Tarasenko for Geoapify Maps API ・ Oct 28&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#leaflet&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#geojson&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#programming&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>leaflet</category>
      <category>geojson</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Visualize GeoJSON Points, Lines, and Polygons on a Leaflet Map</title>
      <dc:creator>Alfiya Tarasenko</dc:creator>
      <pubDate>Tue, 28 Oct 2025 10:05:36 +0000</pubDate>
      <link>https://forem.com/geoapify-maps-api/how-to-visualize-geojson-points-lines-and-polygons-on-a-leaflet-map-1p9a</link>
      <guid>https://forem.com/geoapify-maps-api/how-to-visualize-geojson-points-lines-and-polygons-on-a-leaflet-map-1p9a</guid>
      <description>&lt;p&gt;&lt;strong&gt;GeoJSON&lt;/strong&gt; is a lightweight format for encoding geographic data structures such as &lt;strong&gt;Points&lt;/strong&gt;, &lt;strong&gt;LineStrings&lt;/strong&gt;, and &lt;strong&gt;Polygons&lt;/strong&gt;. It’s built on top of JSON and is widely supported across mapping libraries and APIs.&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll explore how to &lt;strong&gt;render GeoJSON geometry types on a Leaflet map&lt;/strong&gt; — including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Point features&lt;/strong&gt; representing real-world locations from the &lt;a href="https://www.geoapify.com/places-api/" rel="noopener noreferrer"&gt;Geoapify Places API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LineString features&lt;/strong&gt; representing travel routes from the &lt;a href="https://www.geoapify.com/routing-api/" rel="noopener noreferrer"&gt;Geoapify Routing API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polygon features&lt;/strong&gt; representing reachable areas (isochrones or isodistances) from the &lt;a href="https://www.geoapify.com/isoline-api/" rel="noopener noreferrer"&gt;Geoapify Isoline API&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each Geoapify API returns data as a &lt;strong&gt;FeatureCollection&lt;/strong&gt;, where each &lt;strong&gt;Feature&lt;/strong&gt; includes a &lt;code&gt;geometry&lt;/code&gt; object and &lt;code&gt;properties&lt;/code&gt;. Leaflet provides native support for GeoJSON via the &lt;code&gt;L.geoJSON()&lt;/code&gt; method, making it easy to visualize these geometries directly on a map.&lt;/p&gt;

&lt;p&gt;By the end of this tutorial, you’ll have an interactive Leaflet map that displays Points, LineStrings, and Polygons — all styled and layered together using real Geoapify API data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;About Leaflet Functions for Working with GeoJSON&lt;/li&gt;
&lt;li&gt;Visualizing GeoJSON Points (Places API)&lt;/li&gt;
&lt;li&gt;Visualizing GeoJSON Lines (Routing API)&lt;/li&gt;
&lt;li&gt;Visualizing GeoJSON Polygons (Isoline API)&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;li&gt;FAQ&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  About Leaflet Functions for Working with GeoJSON
&lt;/h2&gt;

&lt;p&gt;Leaflet provides native support for the &lt;strong&gt;GeoJSON&lt;/strong&gt; format through the &lt;a href="https://leafletjs.com/reference.html#geojson" rel="noopener noreferrer"&gt;&lt;code&gt;L.geoJSON()&lt;/code&gt;&lt;/a&gt; method. It can load any valid GeoJSON &lt;strong&gt;Feature&lt;/strong&gt;, &lt;strong&gt;FeatureCollection&lt;/strong&gt;, or &lt;strong&gt;Geometry&lt;/strong&gt;, automatically creating the correct map layer type — a &lt;strong&gt;Marker&lt;/strong&gt; for &lt;code&gt;Point&lt;/code&gt;, a &lt;strong&gt;Polyline&lt;/strong&gt; for &lt;code&gt;LineString&lt;/code&gt;, or a &lt;strong&gt;Polygon&lt;/strong&gt; for &lt;code&gt;Polygon&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;L.geoJSON(data, options)&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;L.geoJSON()&lt;/code&gt; factory function takes two arguments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;data&lt;/code&gt; — a valid GeoJSON object (Feature or FeatureCollection)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;options&lt;/code&gt; — an optional configuration object that lets you customize how each geometry type is displayed and how features behave&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&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;geoJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;geojsonData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&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;The &lt;code&gt;options&lt;/code&gt; parameter supports several useful callbacks and style settings:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;style&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Applies visual styling (color, weight, fill, opacity, etc.) to &lt;strong&gt;LineString&lt;/strong&gt; and &lt;strong&gt;Polygon&lt;/strong&gt; geometries.&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;lineLayer&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;geoJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routeData&lt;/span&gt;&lt;span class="p"&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="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feature&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#1976d2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.9&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;Docs: &lt;a href="https://leafletjs.com/reference.html#path-option" rel="noopener noreferrer"&gt;Path options&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;pointToLayer&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Controls how &lt;strong&gt;Point&lt;/strong&gt; features are rendered — for example, as custom markers, circle markers, or icons.&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;pointsLayer&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;geoJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;placeData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;pointToLayer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;latlng&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;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;circleMarker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latlng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;radius&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="na"&gt;fillColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ff5722&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#fff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;weight&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="na"&gt;fillOpacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.9&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;Docs: &lt;a href="https://leafletjs.com/reference.html#geojson-option" rel="noopener noreferrer"&gt;GeoJSON options&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;onEachFeature&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Adds interactivity to each feature — for example, binding popups or click handlers.&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;interactiveLayer&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;geoJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;geojsonData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;onEachFeature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feature&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="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;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&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="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="nf"&gt;bindPopup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&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="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;Docs: &lt;a href="https://leafletjs.com/reference.html#popup" rel="noopener noreferrer"&gt;Popup&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By combining these options, you can display and style GeoJSON data returned by any of the &lt;strong&gt;Geoapify APIs&lt;/strong&gt; — whether that’s Points from the Places API, LineStrings from the Routing API, or Polygons from the Isoline API.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Visualizing GeoJSON Points (Places API)
&lt;/h2&gt;

&lt;p&gt;To start, we’ll visualize &lt;strong&gt;GeoJSON Point features&lt;/strong&gt; returned by the &lt;a href="https://www.geoapify.com/places-api/" rel="noopener noreferrer"&gt;Geoapify Places API&lt;/a&gt;.&lt;br&gt;
Each Point represents a real-world place — in this example, &lt;strong&gt;schools&lt;/strong&gt; within a specific area.&lt;/p&gt;



&lt;p&gt;Live demo on CodePen: &lt;a href="https://codepen.io/geoapify/pen/zxraMEp" rel="noopener noreferrer"&gt;https://codepen.io/geoapify/pen/zxraMEp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;

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


&lt;/p&gt;

&lt;p&gt;In this code sample, each school from the &lt;a href="https://www.geoapify.com/places-api/" rel="noopener noreferrer"&gt;Places API&lt;/a&gt; is shown as a stylized marker with a popup showing its name and address.&lt;/p&gt;

&lt;p&gt;This demonstrates how to directly visualize API responses that use GeoJSON &lt;strong&gt;Point&lt;/strong&gt; geometries in Leaflet.&lt;/p&gt;




&lt;h3&gt;
  
  
  GeoJSON Points: Fetching GeoJSON data from the Places API
&lt;/h3&gt;

&lt;p&gt;The Places API endpoint returns a &lt;strong&gt;FeatureCollection&lt;/strong&gt; where each &lt;strong&gt;Feature&lt;/strong&gt; has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;geometry.type = "Point"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;geometry.coordinates = [longitude, latitude]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;properties&lt;/code&gt; describing the place (name, address, category, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example fetch request (schools in Jacksonville, US):&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;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://api.geoapify.com/v2/places?categories=education.school&amp;amp;filter=place:51bea59c2ff66954c059d1425dff09553e40f00101f901e0d0010000000000c00208&amp;amp;limit=500&amp;amp;apiKey=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiKey&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="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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="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;data&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;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;geoJSON&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;options&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GeoJSON Points: Displaying GeoJSON with &lt;code&gt;L.geoJSON()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Leaflet’s &lt;a href="https://leafletjs.com/reference.html#geojson" rel="noopener noreferrer"&gt;&lt;code&gt;L.geoJSON()&lt;/code&gt;&lt;/a&gt; method automatically recognizes each geometry and adds it to the map.&lt;br&gt;
To customize the rendering of Point features, we use the &lt;code&gt;pointToLayer&lt;/code&gt; and &lt;code&gt;onEachFeature&lt;/code&gt; options.&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;geoJSON&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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;pointToLayer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;latlng&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;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="nx"&gt;latlng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;schoolIcon&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;onEachFeature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feature&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="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="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unnamed school&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;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address_line2&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&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="nf"&gt;bindPopup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;strong&amp;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="s2"&gt;&amp;lt;/strong&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;address&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="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;h3&gt;
  
  
  GeoJSON Points: Adding custom marker icons
&lt;/h3&gt;

&lt;p&gt;Instead of default Leaflet pins, we use the &lt;a href="https://www.geoapify.com/map-marker-icon-api/" rel="noopener noreferrer"&gt;Geoapify Marker Icon API&lt;/a&gt;&lt;br&gt;
to create branded, retina-friendly icons dynamically by URL.&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;schoolIcon&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;icon&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;iconUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`https://api.geoapify.com/v2/icon/?type=awesome&amp;amp;color=%23e2b928&amp;amp;size=48&amp;amp;icon=school&amp;amp;iconType=lucide&amp;amp;contentSize=20&amp;amp;noWhiteCircle&amp;amp;scaleFactor=2&amp;amp;apiKey=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;yourAPIKey&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;iconSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;53&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// width, height in pixels&lt;/span&gt;
  &lt;span class="na"&gt;iconAnchor&lt;/span&gt;&lt;span class="p"&gt;:&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="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// point of the icon which corresponds to marker’s location, 5px is for shadow&lt;/span&gt;
  &lt;span class="na"&gt;popupAnchor&lt;/span&gt;&lt;span class="p"&gt;:&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="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// where popups open relative to the iconAnchor&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This icon configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sets the &lt;strong&gt;Lucide "school" icon&lt;/strong&gt; with a custom orange color (&lt;code&gt;#efa00b&lt;/code&gt;),&lt;/li&gt;
&lt;li&gt;ensures crisp display on Retina screens,&lt;/li&gt;
&lt;li&gt;includes a small decorative &lt;strong&gt;shadow&lt;/strong&gt; for better contrast on light map backgrounds.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Visualizing GeoJSON Lines (Routing API)
&lt;/h2&gt;

&lt;p&gt;We’ll render a route as a GeoJSON MultiLineString returned by the Geoapify Routing API and style it with a subtle shadow, rounded caps/joins, and waypoint markers. Turn-by-turn steps are shown both in a sidebar and as small white circle markers on the route.&lt;/p&gt;




&lt;p&gt;Live demo (CodePen): &lt;a href="https://codepen.io/geoapify/pen/ogbyJeN" rel="noopener noreferrer"&gt;https://codepen.io/geoapify/pen/ogbyJeN&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;

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


&lt;/p&gt;

&lt;p&gt;This code sample demonstrates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calculating route between three waypoints with &lt;a href="https://www.geoapify.com/routing-api/" rel="noopener noreferrer"&gt;Geoapify Routing API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Rendering a GeoJSON MultiLineString via L.geoJSON(data, options)&lt;/li&gt;
&lt;li&gt;Using the options parameter to style paths and layering the same geometry twice for a subtle shadow effect&lt;/li&gt;
&lt;li&gt;Reading waypoint and step metadata from properties to add markers and a turn-by-turn list&lt;/li&gt;
&lt;li&gt;Managing visual stacking with custom panes and zIndex&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Here are key parts of the example related to GeoJSON LineString visualization:&lt;/p&gt;

&lt;h3&gt;
  
  
  GeoJSON Lines: Fetch route as GeoJSON
&lt;/h3&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;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.geoapify.com/v1/routing?waypoints=...&amp;amp;mode=drive&amp;amp;apiKey=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;apiKey&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fc&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routeFeature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;features&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="c1"&gt;// geometry: MultiLineString&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GeoJSON Lines: Draw the route with a shadow underlay
&lt;/h3&gt;

&lt;p&gt;We render the same GeoJSON twice using L.geoJSON(data, options): first a thick semi-transparent shadow, then the colored line above it. Using panes keeps ordering predictable.&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;createPane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;route-shadow&lt;/span&gt;&lt;span class="dl"&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;getPane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;route-shadow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;399&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;createPane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;route-line&lt;/span&gt;&lt;span class="dl"&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;getPane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;route-line&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zIndex&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&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;shadow&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;geoJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routeFeature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;pane&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;route-shadow&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lineCap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;round&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lineJoin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;round&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;line&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;geoJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routeFeature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;pane&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;route-line&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#1976d2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;weight&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;lineCap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;round&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lineJoin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;round&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Leaflet uses &lt;em&gt;panes&lt;/em&gt; to control the &lt;strong&gt;drawing order&lt;/strong&gt; of map layers.&lt;br&gt;
Each pane acts like a separate &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; inside the map container, and layers added to panes with higher &lt;code&gt;zIndex&lt;/code&gt; values appear &lt;strong&gt;above&lt;/strong&gt; those in lower ones.&lt;/p&gt;

&lt;p&gt;In this example, we create two panes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;route-shadow&lt;/code&gt; (zIndex 399) — draws the soft black shadow beneath the line&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;route-line&lt;/code&gt; (zIndex 400) — renders the main colored route above the shadow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By separating the route into multiple panes, we achieve a clear visual hierarchy without merging styles or affecting interactivity.&lt;/p&gt;

&lt;p&gt;Learn more in the Leaflet documentation: &lt;a href="https://leafletjs.com/reference.html#map-pane" rel="noopener noreferrer"&gt;Map panes&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Visualizing GeoJSON Polygons (Isoline API)
&lt;/h2&gt;

&lt;p&gt;We’ll render Polygon/MultiPolygon features returned by the Geoapify Isoline API (drive-time areas) and style them with a soft shadow underlay, semi-transparent fills, and crisp outlines. The map also shows an origin marker and a simple legend for time ranges.&lt;/p&gt;



&lt;p&gt;Live demo (CodePen): &lt;a href="https://codepen.io/geoapify/pen/EaPROwQ" rel="noopener noreferrer"&gt;https://codepen.io/geoapify/pen/EaPROwQ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;

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


&lt;/p&gt;

&lt;p&gt;What happens in the example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requests a FeatureCollection from the &lt;a href="https://www.geoapify.com/isoline-api/" rel="noopener noreferrer"&gt;Isoline API&lt;/a&gt; where each feature has geometry.type = Polygon or MultiPolygon and a properties.range (seconds).&lt;/li&gt;
&lt;li&gt;Renders polygons via L.geoJSON(data, options) with range-based fillColor, semi-transparent, with outline.&lt;/li&gt;
&lt;li&gt;Creates custom panes and z-index so shadows stay beneath fills and markers sit on top.&lt;/li&gt;
&lt;li&gt;Adds an origin marker made with the Geoapify Marker Icon API.&lt;/li&gt;
&lt;li&gt;Builds a legend from properties.range values and labels them in minutes.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Here are the &lt;strong&gt;key code parts&lt;/strong&gt; used to render &lt;strong&gt;GeoJSON Polygons/MultiPolygons&lt;/strong&gt; from the Isoline API in Leaflet:&lt;/p&gt;

&lt;h3&gt;
  
  
  GeoJSON Polygons: Fetch Isoline GeoJSON
&lt;/h3&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;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;lat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;32.82950455&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;96.73469695515405&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="s2"&gt;`https://api.geoapify.com/v1/isoline?lat=&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="s2"&gt;&amp;amp;lon=&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="s2"&gt;&amp;amp;type=time&amp;amp;mode=drive&amp;amp;range=1200,1800,2400,3000&amp;amp;apiKey=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;apiKey&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="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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isolineData&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="c1"&gt;// FeatureCollection with Polygon/MultiPolygon&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GeoJSON Polygons: Palettes &amp;amp; range mapping (seconds → color)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Colorful palette (light → dark / inner → outer)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;palette&lt;/span&gt; &lt;span class="o"&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;#fff3bf&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;#b2f2bb&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;#a5d8ff&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;#d0bfff&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;#ffc9c9&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;#ffd8a8&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;ranges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&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="nx"&gt;isolineData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;features&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;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;range&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&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;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&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;colorByRange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="nx"&gt;ranges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&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;i&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;colorByRange&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;=&lt;/span&gt; &lt;span class="nx"&gt;palette&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;palette&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;-&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toMinutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GeoJSON Polygons: Filled polygons with outlines + popups + hover highlight
&lt;/h3&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;fillsLayer&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;geoJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isolineData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;pane&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;isoline-fill&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feature&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;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;range&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;fill&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;colorByRange&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;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#74c0fc&lt;/span&gt;&lt;span class="dl"&gt;'&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="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#1e3a8a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// outline&lt;/span&gt;
      &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;fillColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// per-range color&lt;/span&gt;
      &lt;span class="na"&gt;fillOpacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.35&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;onEachFeature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feature&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="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;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;range&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;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;r&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="nf"&gt;toMinutes&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="s2"&gt; min`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Isoline&lt;/span&gt;&lt;span class="dl"&gt;'&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="nf"&gt;bindPopup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Reachable within &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;label&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="c1"&gt;// subtle hover effect&lt;/span&gt;
    &lt;span class="nx"&gt;layer&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;mouseover&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;layer&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="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;opacity&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="na"&gt;fillOpacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.45&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="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;mouseout&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;layer&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="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fillOpacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.35&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;These snippets cover the &lt;strong&gt;GeoJSON-specific logic&lt;/strong&gt;: fetching Polygon/MultiPolygon features, mapping &lt;code&gt;properties.range&lt;/code&gt; to colors, drawing a &lt;strong&gt;shadow + fill&lt;/strong&gt; combo for visual depth, and providing &lt;strong&gt;popups/hover&lt;/strong&gt; for interactivity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we explored how to visualize &lt;strong&gt;different GeoJSON geometries&lt;/strong&gt; — &lt;strong&gt;Points&lt;/strong&gt;, &lt;strong&gt;LineStrings&lt;/strong&gt;, and &lt;strong&gt;Polygons&lt;/strong&gt; — using &lt;strong&gt;Leaflet&lt;/strong&gt; and the &lt;strong&gt;Geoapify APIs&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GeoJSON Points&lt;/strong&gt; were loaded from the &lt;a href="https://www.geoapify.com/places-api/" rel="noopener noreferrer"&gt;Geoapify Places API&lt;/a&gt; and displayed as custom markers styled via the &lt;a href="https://www.geoapify.com/map-marker-icon-api/" rel="noopener noreferrer"&gt;Geoapify Marker Icon API&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GeoJSON LineStrings&lt;/strong&gt; were fetched from the &lt;a href="https://www.geoapify.com/routing-api/" rel="noopener noreferrer"&gt;Geoapify Routing API&lt;/a&gt; and drawn as routes with shadows, colorful lines, and turn-by-turn markers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GeoJSON Polygons&lt;/strong&gt; (and MultiPolygons) came from the &lt;a href="https://www.geoapify.com/isoline-api/" rel="noopener noreferrer"&gt;Geoapify Isoline API&lt;/a&gt;, representing areas reachable within certain travel-time ranges.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each geometry type used Leaflet’s &lt;a href="https://leafletjs.com/reference.html#geojson" rel="noopener noreferrer"&gt;&lt;code&gt;L.geoJSON()&lt;/code&gt;&lt;/a&gt; with the &lt;strong&gt;options parameter&lt;/strong&gt; to control styling and interactivity — including &lt;code&gt;pointToLayer&lt;/code&gt;, &lt;code&gt;style&lt;/code&gt;, and &lt;code&gt;onEachFeature&lt;/code&gt;.&lt;br&gt;
Together, these techniques demonstrate how Geoapify APIs and Leaflet can be combined to create powerful, visually appealing map visualizations for a wide range of geospatial data.&lt;/p&gt;




&lt;h3&gt;
  
  
  FAQ
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Q: What is GeoJSON and why is it useful?&lt;/strong&gt;&lt;br&gt;
A: &lt;a href="https://geojson.org/" rel="noopener noreferrer"&gt;GeoJSON&lt;/a&gt; is a standard format for encoding geographic data structures such as Points, LineStrings, and Polygons. It’s easy to parse, visualize, and combine with APIs like Geoapify’s for web mapping.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Which Geoapify APIs provide GeoJSON data?&lt;/strong&gt;&lt;br&gt;
A:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.geoapify.com/places-api/" rel="noopener noreferrer"&gt;Places API&lt;/a&gt; — returns Points (POIs).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.geoapify.com/routing-api/" rel="noopener noreferrer"&gt;Routing API&lt;/a&gt; — returns LineStrings or MultiLineStrings (routes).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.geoapify.com/isoline-api/" rel="noopener noreferrer"&gt;Isoline API&lt;/a&gt; — returns Polygons or MultiPolygons (travel-time areas).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Q: How does Leaflet handle different geometry types?&lt;/strong&gt;&lt;br&gt;
A: Leaflet’s &lt;code&gt;L.geoJSON()&lt;/code&gt; automatically detects geometry types and renders them appropriately. You can use callbacks like &lt;code&gt;pointToLayer()&lt;/code&gt; for Points and &lt;code&gt;style()&lt;/code&gt; for LineStrings or Polygons to customize their appearance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can I use the same map to show Points, Lines, and Polygons together?&lt;/strong&gt;&lt;br&gt;
A: Yes. Leaflet allows multiple GeoJSON layers to be added on the same map. You can overlay POIs, routes, and isolines for richer visualization and interactivity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Why do GeoJSON coordinates sometimes appear flipped when displayed on a Leaflet map?&lt;/strong&gt;&lt;br&gt;
A: In GeoJSON, coordinates are always defined in &lt;code&gt;[longitude, latitude]&lt;/code&gt; order, while Leaflet expects &lt;code&gt;[latitude, longitude]&lt;/code&gt;. When you use &lt;code&gt;L.geoJSON()&lt;/code&gt; or pass &lt;code&gt;latlng&lt;/code&gt; inside &lt;code&gt;pointToLayer&lt;/code&gt; or &lt;code&gt;L.marker()&lt;/code&gt;, Leaflet automatically converts them correctly. Just make sure you don’t manually reverse the order unless you’re working directly with raw coordinate arrays.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: How can I customize the look of my map?&lt;/strong&gt;&lt;br&gt;
A: You can use the &lt;a href="https://www.geoapify.com/map-tiles/" rel="noopener noreferrer"&gt;Geoapify Map Tiles&lt;/a&gt; to apply different base styles (Bright, Dark, Satellite, etc.) and adjust colors, icons, or shadows for your data layers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What are some practical use cases for these visualizations?&lt;/strong&gt;&lt;br&gt;
A:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Points:&lt;/strong&gt; show places of interest, user locations, or search results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lines:&lt;/strong&gt; display routes, tracks, or transit paths.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polygons:&lt;/strong&gt; visualize travel-time zones, service areas, or delivery coverage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Q: How do I make my layers interactive?&lt;/strong&gt;&lt;br&gt;
A: Use &lt;code&gt;onEachFeature()&lt;/code&gt; in &lt;code&gt;L.geoJSON()&lt;/code&gt; to bind popups or highlight layers on hover. This makes maps engaging and helps users explore the data intuitively.&lt;/p&gt;

</description>
      <category>leaflet</category>
      <category>geojson</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>MapLibre GL Markers: Custom Icons, Popups &amp; Events with Geoapify</title>
      <dc:creator>Alfiya Tarasenko</dc:creator>
      <pubDate>Wed, 01 Oct 2025 13:45:33 +0000</pubDate>
      <link>https://forem.com/geoapify-maps-api/maplibre-gl-markers-custom-icons-popups-events-with-geoapify-gm8</link>
      <guid>https://forem.com/geoapify-maps-api/maplibre-gl-markers-custom-icons-popups-events-with-geoapify-gm8</guid>
      <description>&lt;p&gt;Markers are one of the most common elements in any interactive map. They help users identify important points, highlight search results, or drop a temporary pin on click.  &lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;MapLibre GL&lt;/strong&gt;, you can display points on the map in two main ways:  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Markers with &lt;code&gt;new maplibregl.Marker()&lt;/code&gt;&lt;/strong&gt; — these are DOM elements (HTML nodes) placed on top of the map at specific coordinates. They are highly customizable, can be interactive, and support drag &amp;amp; drop.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Symbol layers&lt;/strong&gt; — vector-based icons defined inside a GeoJSON source. They are more efficient for rendering thousands of points but offer less flexibility for custom HTML content.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this article, we’ll focus on the &lt;strong&gt;Marker() API&lt;/strong&gt;. Markers are ideal when you need interactive or custom pins — for example:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dropping a pin at a clicked location.
&lt;/li&gt;
&lt;li&gt;Highlighting search results or selected places.
&lt;/li&gt;
&lt;li&gt;Adding temporary overlays during editing or debugging.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to style your markers, you can use the &lt;a href="https://www.geoapify.com/map-marker-icon-api/" rel="noopener noreferrer"&gt;Geoapify Map Marker Icon API&lt;/a&gt;, which lets you generate dynamically PNG icons with custom colors, sizes, and symbols. These can be easily plugged into MapLibre markers.  &lt;/p&gt;

&lt;p&gt;To see everything in action, check out the interactive demo: &lt;a href="https://codepen.io/geoapify/pen/ZYQOdQw" rel="noopener noreferrer"&gt;MapLibre Markers with Geoapify on CodePen&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
Creating markers
&lt;/li&gt;
&lt;li&gt;
Custom markers
&lt;/li&gt;
&lt;li&gt;
Adding a popup to a marker
&lt;/li&gt;
&lt;li&gt;
Marker events
&lt;/li&gt;
&lt;li&gt;
Managing markers
&lt;/li&gt;
&lt;li&gt;
When not to use markers
&lt;/li&gt;
&lt;li&gt;
FAQ
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. Creating markers
&lt;/h2&gt;

&lt;p&gt;The easiest way to add a marker in MapLibre GL is by creating a new instance of &lt;a href="https://maplibre.org/maplibre-gl-js/docs/API/classes/Marker/" rel="noopener noreferrer"&gt;&lt;code&gt;maplibregl.Marker&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
The constructor can take an optional configuration object — &lt;a href="https://maplibre.org/maplibre-gl-js/docs/API/classes/Marker/#parameters" rel="noopener noreferrer"&gt;&lt;code&gt;MarkerOptions&lt;/code&gt;&lt;/a&gt; — that controls the marker’s &lt;strong&gt;appearance&lt;/strong&gt; (custom HTML, anchor, offset) and &lt;strong&gt;behavior&lt;/strong&gt; (draggable, rotation, alignment).  &lt;/p&gt;

&lt;p&gt;Once created, you place the marker on the map by setting its position with &lt;code&gt;.setLngLat([lon, lat])&lt;/code&gt; and finalizing it with &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="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="nx"&gt;markerOptions&lt;/span&gt;&lt;span class="p"&gt;)&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="nx"&gt;lon&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="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;This creates a default blue pin marker at the given coordinates:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwx52z2l64zxk30esrup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwx52z2l64zxk30esrup.png" alt="MapLibreGL: blue default marker"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  MarkerOptions: customizing behavior and appearance
&lt;/h3&gt;

&lt;p&gt;When creating a marker, you can pass an optional &lt;a href="https://maplibre.org/maplibre-gl-js/docs/API/classes/Marker/#parameters" rel="noopener noreferrer"&gt;&lt;code&gt;MarkerOptions&lt;/code&gt;&lt;/a&gt; object to control how it looks and behaves. This allows you to change its anchor position, make it draggable, or even replace it entirely with a custom HTML element.&lt;/p&gt;

&lt;p&gt;Here are some of the most commonly used options:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;anchor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Defines which part of the marker should be placed on the given coordinates. Options: &lt;code&gt;center&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;bottom&lt;/code&gt;, &lt;code&gt;left&lt;/code&gt;, &lt;code&gt;right&lt;/code&gt;, &lt;code&gt;top-left&lt;/code&gt;, &lt;code&gt;top-right&lt;/code&gt;, &lt;code&gt;bottom-left&lt;/code&gt;, &lt;code&gt;bottom-right&lt;/code&gt;. Default is &lt;code&gt;center&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;anchor: "bottom"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;className&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Space-separated CSS class names to add to the marker element.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;className: "my-marker highlight"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;draggable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Makes the marker draggable so users can move it on the map. Default is &lt;code&gt;false&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;draggable: true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;element&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A custom HTML element to use instead of the default blue pin.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;element: myDivElement&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;offset&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pixel offset from the anchor point. Useful to align custom markers with the correct "tip".&lt;/td&gt;
&lt;td&gt;&lt;code&gt;offset: [0, -10]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;offset&lt;/code&gt; option becomes especially important when you’re using &lt;strong&gt;custom marker icons&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
By default, a marker is placed with its center aligned to the given coordinates. However, many icons — like pin-shaped markers — need to be aligned at the &lt;strong&gt;bottom tip&lt;/strong&gt; rather than the center.  &lt;/p&gt;

&lt;p&gt;We’ll explore this in more detail later in the &lt;strong&gt;Custom markers&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;📖 There are more options available, such as &lt;code&gt;color&lt;/code&gt;, &lt;code&gt;scale&lt;/code&gt;, &lt;code&gt;rotation&lt;/code&gt;, &lt;code&gt;opacity&lt;/code&gt;, &lt;code&gt;pitchAlignment&lt;/code&gt;, and others.&lt;br&gt;&lt;br&gt;
See the full list in the &lt;a href="https://maplibre.org/maplibre-gl-js/docs/API/classes/Marker/#parameters" rel="noopener noreferrer"&gt;official MapLibre MarkerOptions documentation&lt;/a&gt;.  &lt;/p&gt;
&lt;h2&gt;
  
  
  2. Custom markers
&lt;/h2&gt;

&lt;p&gt;The most powerful option in &lt;a href="https://maplibre.org/maplibre-gl-js/docs/API/classes/Marker/#parameters" rel="noopener noreferrer"&gt;&lt;code&gt;MarkerOptions&lt;/code&gt;&lt;/a&gt; is &lt;code&gt;element&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
It allows you to replace the default blue pin with &lt;strong&gt;any DOM element&lt;/strong&gt; — for example, an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag, a styled &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, or an inline &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This makes it easy to integrate custom icons, badges, or branded styles.&lt;/p&gt;
&lt;h3&gt;
  
  
  Example: using Geoapify Marker Icon API
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://www.geoapify.com/map-marker-icon-api/" rel="noopener noreferrer"&gt;Geoapify Map Marker Icon API&lt;/a&gt; can generate PNG or SVG icons on the fly. You can pick an icon type, set a color, add a shadow, and adjust scaling — then plug the resulting image URL into a MapLibre marker.&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;// Generate a marker icon URL with Geoapify&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;geoapifyIconUrl&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;iconType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;material&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#8b5cf6&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;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="s2"&gt;https://api.geoapify.com/v2/icon/&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;searchParams&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="s2"&gt;type&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="s2"&gt;awesome&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;searchParams&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="s2"&gt;icon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;icon&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;searchParams&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="s2"&gt;iconType&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;iconType&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;searchParams&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="s2"&gt;color&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&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;searchParams&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="s2"&gt;shadow&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="s2"&gt;true&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;searchParams&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="s2"&gt;scaleFactor&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="s2"&gt;2&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;searchParams&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="s2"&gt;apiKey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GEOAPIFY_API_KEY&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;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Create an &amp;lt;img&amp;gt; element for the marker&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;img&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="s2"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Restaurant marker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;geoapifyIconUrl&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;restaurant&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Wrap it in a container div&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wrapper&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="s2"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;map-marker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Create the marker&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="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bottom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&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="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&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;setLngLat&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;lat&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;Here we use &lt;code&gt;anchor: "bottom"&lt;/code&gt; and a small negative &lt;code&gt;offset&lt;/code&gt; so that the &lt;strong&gt;tip of the pin icon&lt;/strong&gt; aligns exactly with the map coordinates.&lt;/p&gt;

&lt;p&gt;👉 Try this in the interactive demo:&lt;/p&gt;

&lt;p&gt;

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


&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: adjusting &lt;code&gt;offset&lt;/code&gt; for custom icons
&lt;/h3&gt;

&lt;p&gt;When you use a custom icon, the default anchor might not match the part of the icon you want to “touch” the map point. That’s where the &lt;code&gt;offset&lt;/code&gt; option becomes important.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Case 1: square icon (40×40px)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
If you have a round or square icon (e.g. 40×40px) and want it centered exactly on the latitude/longitude, no offset is needed:&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;icon40&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="s2"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;icon40&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;marker-40x40.png&lt;/span&gt;&lt;span class="dl"&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="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;icon40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// center aligns with lat/lon&lt;/span&gt;
  &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&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;0&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;setLngLat&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;lat&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;&lt;strong&gt;Case 2: pin icon (31×46px with shadow 4px)&lt;/strong&gt;&lt;br&gt;
For a pin-shaped icon (31×46px, with a shadow extending 4px below), the “tip” of the pin should touch the coordinates.&lt;br&gt;
Here we shift the marker upward so the bottom tip aligns with the point:&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;pin&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="s2"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;marker-31x46-shadow4.png&lt;/span&gt;&lt;span class="dl"&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="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bottom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// attach by bottom edge&lt;/span&gt;
  &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&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="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;         &lt;span class="c1"&gt;// shift up by shadow size&lt;/span&gt;
&lt;span class="p"&gt;})&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="nx"&gt;lon&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="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;👉 The &lt;code&gt;offset&lt;/code&gt; ensures that the &lt;strong&gt;visual tip of the marker&lt;/strong&gt; sits exactly on the coordinates, rather than floating above or overlapping incorrectly.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Adding a popup to a marker
&lt;/h2&gt;

&lt;p&gt;Markers in MapLibre can be paired with a &lt;a href="https://maplibre.org/maplibre-gl-js/docs/API/classes/Popup/" rel="noopener noreferrer"&gt;&lt;code&gt;Popup&lt;/code&gt;&lt;/a&gt; so that extra information appears when the user interacts with them.&lt;br&gt;&lt;br&gt;
Popups are not part of the marker itself, but you can &lt;strong&gt;attach&lt;/strong&gt; them using the &lt;code&gt;.setPopup()&lt;/code&gt; method.&lt;/p&gt;
&lt;h3&gt;
  
  
  Example: simple popup
&lt;/h3&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;marker&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;Marker&lt;/span&gt;&lt;span class="p"&gt;()&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="nx"&gt;lon&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setPopup&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;Popup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello from this location!&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;span class="c1"&gt;// Open the popup immediately&lt;/span&gt;
&lt;span class="nx"&gt;marker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;togglePopup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This creates a marker with a text popup that appears when clicked. In this case, we also call &lt;code&gt;togglePopup()&lt;/code&gt; so it opens by default.&lt;/p&gt;
&lt;h3&gt;
  
  
  Custom popup content
&lt;/h3&gt;

&lt;p&gt;Popups don’t have to be plain text — you can also provide &lt;strong&gt;custom HTML&lt;/strong&gt; using &lt;code&gt;.setHTML()&lt;/code&gt;. This makes it possible to render richer content, such as formatted addresses, buttons, or images.&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;popup&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;Popup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;div class="popup-content"&amp;gt;
      &amp;lt;h3&amp;gt;Restaurant&amp;lt;/h3&amp;gt;
      &amp;lt;p&amp;gt;Open daily 10:00–22:00&amp;lt;/p&amp;gt;
      &amp;lt;a href="https://example.com" target="_blank"&amp;gt;More info&amp;lt;/a&amp;gt;
    &amp;lt;/div&amp;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="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="nx"&gt;lon&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setPopup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;popup&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;h3&gt;
  
  
  Known issue: event propagation
&lt;/h3&gt;

&lt;p&gt;When you click a marker to open its popup, the &lt;strong&gt;click event also bubbles up to the map&lt;/strong&gt;, which may trigger &lt;code&gt;map.on("click", …)&lt;/code&gt; handlers.&lt;br&gt;
To avoid unwanted side effects, check whether the click originated from a marker element before running map-level logic:&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="s2"&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="nx"&gt;event&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="c1"&gt;// Ignore clicks on existing markers&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;closest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.map-marker&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="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// ... &lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, clicking on a marker will only open its popup, without triggering your map’s click handler.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Marker events
&lt;/h2&gt;

&lt;p&gt;Markers can respond to both &lt;strong&gt;DOM events&lt;/strong&gt; (like clicks or hovers) and &lt;strong&gt;drag events&lt;/strong&gt; if they are created with &lt;code&gt;draggable: true&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  DOM events
&lt;/h3&gt;

&lt;p&gt;Attach using &lt;a href="https://maplibre.org/maplibre-gl-js/docs/API/classes/Marker/#getelement" rel="noopener noreferrer"&gt;&lt;code&gt;marker.getElement()&lt;/code&gt;&lt;/a&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;marker&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;Marker&lt;/span&gt;&lt;span class="p"&gt;()&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="nx"&gt;lon&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="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;marker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElement&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="s2"&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="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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Marker clicked!&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;marker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElement&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="s2"&gt;mouseenter&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;marker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElement&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pointer&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;This way, you can make markers interactive beyond just showing a popup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Drag events
&lt;/h3&gt;

&lt;p&gt;Available when the &lt;code&gt;draggable&lt;/code&gt;option is &lt;code&gt;true&lt;/code&gt;,, the marker can be moved around. You can then listen to the drag lifecycle:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event name&lt;/th&gt;
&lt;th&gt;When it fires&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dragstart&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;User starts dragging the marker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;drag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Marker is being dragged&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dragend&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;User releases the marker after dragging&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&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;marker&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;Marker&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;draggable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&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="nx"&gt;lon&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="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="c1"&gt;// Listen for drag events&lt;/span&gt;
&lt;span class="nx"&gt;marker&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="s2"&gt;dragstart&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Drag started&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;marker&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="s2"&gt;drag&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Dragging...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;marker&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="s2"&gt;dragend&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;lngLat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;marker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLngLat&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`New position: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lngLat&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="s2"&gt;, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lngLat&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="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;This is especially useful for “pick a location” UIs, where the user drags a pin to select an exact spot.&lt;/p&gt;

&lt;p&gt;📖 See details in the &lt;a href="https://maplibre.org/maplibre-gl-js/docs/API/classes/Marker/" rel="noopener noreferrer"&gt;MapLibre Marker docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Managing markers
&lt;/h2&gt;

&lt;p&gt;When working with more than one marker, it’s good practice to keep track of them so you can update or remove them later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep markers in an array
&lt;/h3&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;markers&lt;/span&gt; &lt;span class="o"&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;addMarker&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;lat&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;marker&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;Marker&lt;/span&gt;&lt;span class="p"&gt;()&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="nx"&gt;lon&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="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;markers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;marker&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;h3&gt;
  
  
  Remove a marker
&lt;/h3&gt;

&lt;p&gt;Each marker has a &lt;code&gt;.remove()&lt;/code&gt; method:&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;markers&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="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// removes the first marker from the map&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Remove all markers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;markers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;marker&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;marker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;markers&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;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// clear the array&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Replace markers dynamically
&lt;/h3&gt;

&lt;p&gt;This is useful when showing search results:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Remove old markers.&lt;/li&gt;
&lt;li&gt;Add new ones from the latest data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Keeping markers organized prevents memory leaks and avoids clutter when updating the map dynamically.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. When not to use markers
&lt;/h2&gt;

&lt;p&gt;Markers in MapLibre are &lt;strong&gt;&lt;a href="https://www.w3schools.com/jsref/dom_obj_all.asp" rel="noopener noreferrer"&gt;DOM elements&lt;/a&gt;&lt;/strong&gt; placed on top of the map. This makes them flexible and easy to customize, but also less efficient when you need to display &lt;strong&gt;hundreds or thousands&lt;/strong&gt; of points. Each marker adds a new node to the DOM, which can slow down rendering and interactions.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to avoid markers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Displaying large datasets (thousands of locations).
&lt;/li&gt;
&lt;li&gt;Real-time updates where many markers are added/removed frequently.
&lt;/li&gt;
&lt;li&gt;Use cases where performance and smooth zooming/panning are critical.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Alternative: &lt;a href="https://maplibre.org/maplibre-style-spec/layers/#symbol" rel="noopener noreferrer"&gt;Symbol layers&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;For bulk rendering, prefer a &lt;strong&gt;GeoJSON source + SymbolLayer&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
This approach draws icons directly on the map canvas, which is much more efficient.&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;addSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;places&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;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;geojson&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myGeoJson&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;addLayer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;places-layer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;symbol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;places&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;icon-image&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="s2"&gt;marker-15&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="s2"&gt;icon-size&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.5&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;h3&gt;
  
  
  Hybrid approach
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;symbol layers&lt;/strong&gt; for the full dataset.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;markers&lt;/strong&gt; for selected or interactive points (e.g., user’s chosen location, currently active place).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. How do I change marker size on high-DPI (retina) displays?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Use a higher-resolution image (2× or SVG) and scale it down with CSS to keep it sharp.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Can I drag markers? How do I read their updated coordinates?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Yes — set &lt;code&gt;draggable: true&lt;/code&gt; in &lt;code&gt;MarkerOptions&lt;/code&gt; and listen to the &lt;code&gt;dragend&lt;/code&gt; event. Call &lt;code&gt;marker.getLngLat()&lt;/code&gt; to get the new position.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. What’s the best way to show thousands of points?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Use a &lt;strong&gt;GeoJSON source + SymbolLayer&lt;/strong&gt;, not individual markers.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. How do I make markers clickable without interfering with the map?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Use &lt;code&gt;marker.getElement().addEventListener("click", …)&lt;/code&gt; or attach a popup. In map click handlers, ignore clicks that come from marker elements using &lt;code&gt;event.target.closest(".map-marker")&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. How do I automatically open a popup when a marker is created or added?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
After calling &lt;code&gt;.setPopup()&lt;/code&gt;, use &lt;code&gt;marker.togglePopup()&lt;/code&gt; to open it right away.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Can I use inline SVG as a marker?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Yes — pass an element containing your SVG to &lt;code&gt;MarkerOptions.element&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. How do I rotate a marker with heading or bearing?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Use the &lt;code&gt;rotation&lt;/code&gt; option. Combine with &lt;code&gt;rotationAlignment: "map"&lt;/code&gt; to keep it aligned to map bearing.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Can I animate markers (bounce, pulse, fade)?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Yes — since markers are DOM elements, you can animate them with CSS transitions, keyframes, or libraries like GSAP.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. How do I add text labels to markers?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Include a &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; inside your marker element and style it with CSS.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. How do I cluster many markers?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Don’t cluster DOM markers directly. Instead, cluster your data in GeoJSON and use a SymbolLayer for clustered icons.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;11. Can I change marker color dynamically after adding it?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Yes — update the marker’s DOM element (e.g., change an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; &lt;code&gt;src&lt;/code&gt; or CSS class).  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;12. How do I handle overlapping markers?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Use different offsets, z-index (&lt;code&gt;marker.getElement().style.zIndex&lt;/code&gt;), or clustering.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;13. How do I remove all markers at once?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Keep them in an array and loop through calling &lt;code&gt;.remove()&lt;/code&gt; on each.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;14. How do I make markers accessible?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Add &lt;code&gt;alt&lt;/code&gt; text for images or &lt;code&gt;aria-label&lt;/code&gt; for custom HTML. Use &lt;code&gt;cursor: pointer&lt;/code&gt; for clickable markers and keep touch targets large enough on mobile.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;Markers in MapLibre GL are a flexible way to place interactive elements on the map. With &lt;code&gt;MarkerOptions&lt;/code&gt;, you can fine-tune alignment, make them draggable, or replace the default pin with your own custom HTML or icons (for example, using the &lt;a href="https://www.geoapify.com/map-marker-icon-api/" rel="noopener noreferrer"&gt;Geoapify Map Marker Icon API&lt;/a&gt;).  &lt;/p&gt;

&lt;p&gt;Use markers when you need:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interactive pins that users can drag or click.
&lt;/li&gt;
&lt;li&gt;Custom icons, badges, or HTML content.
&lt;/li&gt;
&lt;li&gt;Highlighted or temporary overlays.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But remember:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For &lt;strong&gt;large datasets&lt;/strong&gt;, prefer &lt;strong&gt;SymbolLayers&lt;/strong&gt; with a GeoJSON source for performance.
&lt;/li&gt;
&lt;li&gt;Keep markers organized (arrays, cleanup) to avoid clutter and memory leaks.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the next article, we’ll take this further and focus on &lt;strong&gt;popups&lt;/strong&gt; — attaching them to markers, rendering custom HTML, and fetching live data (e.g., place details from Geoapify).  &lt;/p&gt;

</description>
      <category>maplibre</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Fix 401 Unauthorized Error When Calling the API</title>
      <dc:creator>Alfiya Tarasenko</dc:creator>
      <pubDate>Mon, 29 Sep 2025 09:49:11 +0000</pubDate>
      <link>https://forem.com/geoapify-maps-api/how-to-fix-401-unauthorized-error-when-calling-the-api-4kld</link>
      <guid>https://forem.com/geoapify-maps-api/how-to-fix-401-unauthorized-error-when-calling-the-api-4kld</guid>
      <description>&lt;p&gt;When working with APIs, running into an &lt;strong&gt;HTTP 401 Unauthorized&lt;/strong&gt; error can be frustrating. It usually means that the request wasn’t authenticated properly — most often because of an issue with your API key.&lt;/p&gt;

&lt;p&gt;If you’re using the &lt;a href="https://www.geoapify.com/" rel="noopener noreferrer"&gt;Geoapify APIs&lt;/a&gt;, a 401 error is one of the most common issues new users face. The good news is that it’s quick to fix once you know what to check.&lt;/p&gt;

&lt;p&gt;In this short FAQ, we’ll explain what a 401 error means, list the most common causes, and walk you through how to resolve it so you can get your project running smoothly again.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does “401 Unauthorized” mean?
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;401 Unauthorized&lt;/strong&gt; status code tells you that the request could not be completed because the server did not receive valid authentication credentials or API key. In other words, the API expected an API key (or another form of authentication), but it was missing, invalid, or rejected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "statusCode": 401,
  "error": "Unauthorized",
  "message": "Invalid apiKey"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s important to note that &lt;strong&gt;401 is different from 403&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;401 Unauthorized&lt;/strong&gt; → you are &lt;em&gt;not authenticated&lt;/em&gt; (the server doesn’t recognize your credentials).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;403 Forbidden&lt;/strong&gt; → you are &lt;em&gt;authenticated&lt;/em&gt; but &lt;em&gt;not allowed&lt;/em&gt; to access that resource.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Other common 4xx codes
&lt;/h3&gt;

&lt;p&gt;The 401 Unauthorized error is just one of several client-side HTTP status codes (4xx family). You can find the full list in the &lt;a href="https://en.wikipedia.org/wiki/List_of_HTTP_status_codes" rel="noopener noreferrer"&gt;Wikipedia page on HTTP status codes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are a few you might encounter most often when working with APIs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Status Code&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Typical Cause&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;400 Bad Request&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The server could not understand the request.&lt;/td&gt;
&lt;td&gt;Missing or invalid parameters, malformed URL.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;401 Unauthorized&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Authentication is missing or invalid.&lt;/td&gt;
&lt;td&gt;No API key, wrong key, or not sent correctly.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;403 Forbidden&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You are authenticated but not allowed to access the resource.&lt;/td&gt;
&lt;td&gt;Key doesn’t have permission, IP/domain restrictions, or access denied.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;404 Not Found&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The requested resource doesn’t exist.&lt;/td&gt;
&lt;td&gt;Wrong endpoint, typo in URL, or resource removed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;429 Too Many Requests&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You have exceeded the allowed request limit.&lt;/td&gt;
&lt;td&gt;Hitting rate limits or quota exhaustion.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Understanding these codes helps quickly identify whether the issue is with authentication (401), permissions (403), request formatting (400), missing resources (404), or usage limits (429).&lt;/p&gt;

&lt;h3&gt;
  
  
  Why 401 stands out
&lt;/h3&gt;

&lt;p&gt;Unlike the other 4xx errors, &lt;strong&gt;401 specifically indicates an authentication problem&lt;/strong&gt; or &lt;strong&gt;problem with API key&lt;/strong&gt;. It doesn’t mean your request is malformed (400), the resource is missing (404), or you’ve gone over quota (429). It means the API simply doesn’t recognize who you are — usually because the API key is missing, wrong, or invalid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common reasons for 401 errors
&lt;/h2&gt;

&lt;p&gt;Here are the most common reasons you might see a &lt;strong&gt;401 Unauthorized&lt;/strong&gt; when calling an API:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Missing API key&lt;/td&gt;
&lt;td&gt;The request does not include the required &lt;code&gt;apiKey&lt;/code&gt; parameter. &lt;br&gt; Example: &lt;code&gt;https://api.geoapify.com/v1/geocode/search?text=Berlin&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typo in API key&lt;/td&gt;
&lt;td&gt;The key is mistyped, cut off, or contains hidden characters (like spaces or line breaks from copy-paste). &lt;br&gt; Example: &lt;code&gt;apiKey=12345OOPS&lt;/code&gt; instead of &lt;code&gt;apiKey=12345OPS&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Not URL-encoded&lt;/td&gt;
&lt;td&gt;The key or query parameters are not properly &lt;strong&gt;&lt;a href="https://www.w3schools.com/tags/ref_urlencode.ASP" rel="noopener noreferrer"&gt;URL-encoded&lt;/a&gt;&lt;/strong&gt;, so special characters break the request. &lt;br&gt; Example: When sending an address to &lt;a href="https://www.geoapify.com/geocoding-api/" rel="noopener noreferrer"&gt;Geocoding API&lt;/a&gt; it must be URL encoded. In other words, it should be&lt;code&gt;29%20Buxton%20Road%2C%20London%2C%20E15%201QU%2C%20United%20Kingdom&lt;/code&gt; instead of just &lt;em&gt;29 Buxton Road, London, E15 1QU, United Kingdom&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key in the wrong place&lt;/td&gt;
&lt;td&gt;The API key is sent in the wrong part of the request (e.g., header instead of query parameter). See &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication" rel="noopener noreferrer"&gt;API authentication basics&lt;/a&gt;. &lt;br&gt; Example: sending &lt;code&gt;Authorization: apiKey&lt;/code&gt; instead of &lt;code&gt;?apiKey=...&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inactive key&lt;/td&gt;
&lt;td&gt;The key is expired, deleted, or disabled. Learn more about &lt;a href="https://cloud.google.com/docs/authentication/api-keys#securing_an_api_key" rel="noopener noreferrer"&gt;API key lifecycle&lt;/a&gt;. &lt;br&gt; Example: using an old key after rotation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restricted key&lt;/td&gt;
&lt;td&gt;The key has domain, IP, or referrer restrictions that block the request. &lt;br&gt; Example: request from &lt;code&gt;localhost&lt;/code&gt; when the key only allows &lt;code&gt;example.com&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment variable issue&lt;/td&gt;
&lt;td&gt;The app doesn’t load the key correctly, often leaving the value empty. &lt;br&gt; Example: request with &lt;code&gt;apiKey=&lt;/code&gt; and no value&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How to fix a 401 error
&lt;/h2&gt;

&lt;p&gt;Here’s a simple step-by-step approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test with curl&lt;/strong&gt;
Run the request directly with &lt;code&gt;curl&lt;/code&gt; to see if it works outside your code:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   curl &lt;span class="s2"&gt;"https://api.geoapify.com/v1/geocode/search?text=29%20Buxton%20Road%2C%20London%2C%20E15%201QU%2C%20United%20Kingdom&amp;amp;format=json&amp;amp;apiKey=YOUR_API_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;If it works here, the issue is in your code.&lt;/li&gt;
&lt;li&gt;If it fails, the problem is with your API key or request setup.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;If the issue is in your code&lt;/strong&gt;&lt;br&gt;
Debug and check what request your code actually sends. Compare it with the working &lt;code&gt;curl&lt;/code&gt; request to spot the difference.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;If the issue is with your API key&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Try again with a newly created API key.&lt;/li&gt;
&lt;li&gt;Check for domain, IP, or referrer restrictions that may block your request.&lt;/li&gt;
&lt;li&gt;If nothing helps, contact Support and share your request details. Geoapify users can email &lt;a href="//info@geoapify.com"&gt;info@geoapify.com&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;401 Unauthorized&lt;/strong&gt; error means your request didn’t pass authentication — most often because the API key is missing, wrong, or restricted.  &lt;/p&gt;

&lt;p&gt;To solve it, first test the request with &lt;code&gt;curl&lt;/code&gt; to see if the problem comes from your code or the API key.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If it’s your code, debug the request it generates.
&lt;/li&gt;
&lt;li&gt;If it’s the key, try a new one, check restrictions, and if needed, reach out to &lt;strong&gt;&lt;a href="mailto:info@geoapify.com"&gt;info@geoapify.com&lt;/a&gt;&lt;/strong&gt; for support.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also use the &lt;a href="https://apidocs.geoapify.com/playground" rel="noopener noreferrer"&gt;Geoapify API Playground&lt;/a&gt; to &lt;strong&gt;test requests interactively, generate ready-to-use links, and copy working examples&lt;/strong&gt;. This is the easiest way to verify your API key and request format.  &lt;/p&gt;

&lt;p&gt;With these steps, you can quickly identify the root cause and get your API calls working again.&lt;/p&gt;

</description>
      <category>api</category>
      <category>development</category>
      <category>programming</category>
      <category>http</category>
    </item>
    <item>
      <title>How to Calculate Pixel Width and Height of a Bounding Box at Different Map Zoom Levels</title>
      <dc:creator>Alfiya Tarasenko</dc:creator>
      <pubDate>Fri, 26 Sep 2025 07:37:05 +0000</pubDate>
      <link>https://forem.com/geoapify-maps-api/how-to-calculate-pixel-width-and-height-of-a-bounding-box-at-different-map-zoom-levels-1o34</link>
      <guid>https://forem.com/geoapify-maps-api/how-to-calculate-pixel-width-and-height-of-a-bounding-box-at-different-map-zoom-levels-1o34</guid>
      <description>&lt;p&gt;When creating &lt;strong&gt;print maps&lt;/strong&gt; or &lt;strong&gt;static map images&lt;/strong&gt;, one of the key challenges is to control the scale: &lt;em&gt;how many pixels wide and tall will my area of interest be?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Imagine you have a bounding box defined by two latitude/longitude corners. Depending on the zoom, its width and height in pixels will change. Sometimes you know the zoom and want to measure the pixel size; other times you know the desired image width (or height) and want to find the zoom that makes the box fit.&lt;/p&gt;

&lt;p&gt;This is exactly what you need when working with the &lt;a href="https://www.geoapify.com/static-maps-api/" rel="noopener noreferrer"&gt;Geoapify Static Maps API&lt;/a&gt; — for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;printing a map at a specific size,&lt;/li&gt;
&lt;li&gt;generating static images where labels stay readable,&lt;/li&gt;
&lt;li&gt;making sure the requested area fits perfectly into the image dimensions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, we’ll cover three scenarios with simple JavaScript helpers and a live demo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Calculate bbox width and height at a given zoom.&lt;/li&gt;
&lt;li&gt;Calculate width when the height is fixed.&lt;/li&gt;
&lt;li&gt;Calculate height when the width is fixed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Try it out in the demo: &lt;a href="https://codepen.io/geoapify/pen/dPYLYpZ" rel="noopener noreferrer"&gt;https://codepen.io/geoapify/pen/dPYLYpZ&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Background: Zoom Levels, Latitude/Longitude, and Pixels
&lt;/h2&gt;

&lt;p&gt;Most modern map libraries — &lt;a href="https://leafletjs.com/" rel="noopener noreferrer"&gt;Leaflet&lt;/a&gt;, &lt;a href="https://maplibre.org/" rel="noopener noreferrer"&gt;MapLibre GL&lt;/a&gt;, &lt;a href="https://developers.google.com/maps" rel="noopener noreferrer"&gt;Google Maps&lt;/a&gt;, &lt;a href="https://openlayers.org/" rel="noopener noreferrer"&gt;OpenLayers&lt;/a&gt; — rely on the &lt;strong&gt;Web Mercator projection&lt;/strong&gt; (&lt;a href="https://epsg.io/3857" rel="noopener noreferrer"&gt;EPSG:3857&lt;/a&gt;). This projection transforms geographic coordinates (latitude/longitude) into a flat, square map that can be tiled and displayed on screens.&lt;/p&gt;

&lt;p&gt;Here’s how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Longitude&lt;/strong&gt; maps linearly to the X axis. At zoom level 0, the full range −180° … +180° fits into one 256 px tile.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latitude&lt;/strong&gt; is projected using the &lt;a href="https://en.wikipedia.org/wiki/Mercator_projection#Mathematics" rel="noopener noreferrer"&gt;Mercator formula&lt;/a&gt;, which stretches distances near the poles. To avoid infinite values, the map is clipped to about ±85.05113° latitude.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;A relation between the Mercator projection and the actual relative size of each country. Author: Jakub Nowosad. &lt;a href="https://en.wikipedia.org/wiki/File:Worlds_animate.gif" rel="noopener noreferrer"&gt;Source&lt;/a&gt;. Licensed under &lt;a href="https://creativecommons.org/licenses/by-sa/4.0/deed.en" rel="noopener noreferrer"&gt;CC BY-SA 4.0&lt;/a&gt;:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnezi0x4wy79guisbuf2t.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnezi0x4wy79guisbuf2t.gif" alt="Animation showing Mercator projection distortion of country sizes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The result is a grid of &lt;strong&gt;tiles&lt;/strong&gt;, each typically 256×256 px:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At &lt;strong&gt;zoom 0&lt;/strong&gt;, the world is one tile (256 px wide).&lt;/li&gt;
&lt;li&gt;At &lt;strong&gt;zoom 1&lt;/strong&gt;, the world is 512 px wide (2 tiles × 256).&lt;/li&gt;
&lt;li&gt;At &lt;strong&gt;zoom 2&lt;/strong&gt;, 1024 px wide.&lt;/li&gt;
&lt;li&gt;In general:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;worldSizePx = 256 × 2^z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So every zoom step doubles the number of pixels available for both longitude and latitude.&lt;/p&gt;

&lt;p&gt;👉 See it live: &lt;a href="https://codepen.io/geoapify/pen/zxvgKNo" rel="noopener noreferrer"&gt;Zoom collage demo&lt;/a&gt; (toggle the grid to view tile boundaries).&lt;br&gt;
👉 Read more: &lt;a href="https://dev.to/geoapify-maps-api/understanding-map-zoom-levels-and-xyz-tile-coordinates-55da"&gt;Understanding map zoom levels and XYZ tile coordinates&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This transformation from lat/lon to pixels explains why the &lt;strong&gt;pixel width/height of a bounding box&lt;/strong&gt; depends on zoom. A 1° × 1° area near the equator may span only a few pixels at zoom 2, but thousands at zoom 12 — and at high latitudes, the Mercator projection exaggerates this effect even further.&lt;/p&gt;

&lt;p&gt;This foundation prepares us for the three practical scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Calculate pixel width/height of a bounding box at a given zoom.&lt;/li&gt;
&lt;li&gt;Given a fixed image height, find the corresponding width.&lt;/li&gt;
&lt;li&gt;Given a fixed image width, find the corresponding height.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Case 1: Width and Height at a Given Zoom
&lt;/h2&gt;

&lt;p&gt;The simplest case: you already know the zoom level and want to measure how many pixels wide and tall a geographic bounding box will be.&lt;/p&gt;

&lt;p&gt;This requires converting latitude/longitude to &lt;strong&gt;world pixel coordinates&lt;/strong&gt; in Web Mercator. The math looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Longitude → X&lt;/strong&gt; (linear):
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  x = (lon + 180) / 360 × worldSize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Latitude → Y&lt;/strong&gt; (Mercator projection):
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  y = (0.5 - log((1 + sinφ) / (1 - sinφ)) / (4π)) × worldSize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;where φ is the latitude expressed in radians (i.e., &lt;code&gt;φ = lat × π / 180&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Here’s a small utility function in JavaScript:&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;TILE_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Convert lon/lat to world pixel coordinates at zoom&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;lonLatToWorldPixels&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;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="nx"&gt;tileSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TILE_SIZE&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;scale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tileSize&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&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="nx"&gt;zoom&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;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lon&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;scale&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;sin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sin&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="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;180&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;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;scale&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;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Calculate bbox width/height in pixels at a given zoom&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;bboxSizePx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&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="nx"&gt;tileSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TILE_SIZE&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;minLon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;minLat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxLon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxLat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bbox&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;p1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lonLatToWorldPixels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;minLon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;minLat&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="nx"&gt;tileSize&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;p2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lonLatToWorldPixels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxLon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxLat&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="nx"&gt;tileSize&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="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&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;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;Let’s measure a box around Paris:&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;bboxParis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;2.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;48.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;49.0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// [minLon, minLat, maxLon, maxLat]&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;bboxSizePx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bboxParis&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="c1"&gt;// → { width: 9.10 px, height: 10.37 px }&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;bboxSizePx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bboxParis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// → { width: 291.27 px, height: 331.98 px }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At &lt;strong&gt;zoom 5&lt;/strong&gt;, the Paris bbox is just a few pixels across. At &lt;strong&gt;zoom 10&lt;/strong&gt;, it’s already several hundred pixels.&lt;/p&gt;

&lt;p&gt;This explains why, when printing or generating static images, choosing the right zoom is crucial: too low and your area looks tiny, too high and it won’t fit into the image dimensions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Case 2: Width from a Given Height
&lt;/h2&gt;

&lt;p&gt;Sometimes you don’t fix the zoom directly — instead, you know how tall the image should be in pixels. From that height, you can derive the corresponding width of the bounding box (or the zoom that gives you that height).&lt;/p&gt;

&lt;p&gt;The math works because in Web Mercator, both width and height scale proportionally with &lt;code&gt;2^z&lt;/code&gt;. If you know one dimension, you can compute the zoom and then derive the other.&lt;/p&gt;

&lt;h3&gt;
  
  
  JavaScript helper
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Precompute bbox deltas at zoom 0&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;bboxDeltaAtZoom0&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TILE_SIZE&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;minLon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;minLat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxLon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxLat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bbox&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;x0a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tileSize&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;minLon&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;360&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;x0b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tileSize&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxLon&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;360&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;dx0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x0b&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;x0a&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;mercY&lt;/span&gt; &lt;span class="o"&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="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;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sin&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="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;180&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="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;tileSize&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;dy0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;mercY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxLat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;mercY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;minLat&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;dx0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dy0&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Given a bbox and target height, find zoom&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;zoomForHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetHeightPx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TILE_SIZE&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;dy0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bboxDeltaAtZoom0&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetHeightPx&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;dy0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// From that zoom, calculate width&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;widthFromHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetHeightPx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TILE_SIZE&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;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;zoomForHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetHeightPx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileSize&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;width&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bboxSizePx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileSize&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="na"&gt;zoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&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;h3&gt;
  
  
  Example
&lt;/h3&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;bboxParis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;2.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;48.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;49.0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// Paris bbox&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;widthFromHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bboxParis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// → { zoom:  10.854, width: 526.42 px }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If we want the Paris bbox to be &lt;strong&gt;600 px tall&lt;/strong&gt;, we need roughly &lt;strong&gt;zoom 10.9&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;At that zoom, its width will be ~526 px.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why it’s useful
&lt;/h3&gt;

&lt;p&gt;This scenario is handy when you know the image height (e.g., &lt;strong&gt;fixed paper size&lt;/strong&gt;, or a &lt;strong&gt;portrait-oriented static map&lt;/strong&gt;) and want the width to scale accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Case 3: Height from a Given Width
&lt;/h2&gt;

&lt;p&gt;In many layouts the image width is fixed (website container, print column). From that width, you can solve the zoom first and then derive the height of your bounding box.&lt;/p&gt;

&lt;h3&gt;
  
  
  JavaScript helpers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Precompute bbox deltas at zoom 0 (reuse if already defined)&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;bboxDeltaAtZoom0&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;256&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;minLon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;minLat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxLon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxLat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bbox&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;x0a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tileSize&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;minLon&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;360&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;x0b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tileSize&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxLon&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;360&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;dx0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x0b&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;x0a&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;mercY&lt;/span&gt; &lt;span class="o"&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="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;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sin&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="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;180&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="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;tileSize&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;dy0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;mercY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxLat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;mercY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;minLat&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;dx0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dy0&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Solve zoom for a target pixel width&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;zoomForWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetWidthPx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;256&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;dx0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bboxDeltaAtZoom0&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetWidthPx&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;dx0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// From that zoom, calculate height&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;heightFromWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetWidthPx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;256&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;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;zoomForWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetWidthPx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileSize&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;height&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bboxSizePx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// reuse bboxSizePx from Case 1&lt;/span&gt;
  &lt;span class="k"&gt;return&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="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&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;h3&gt;
  
  
  Example
&lt;/h3&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;bboxParis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;2.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;48.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;49.0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// [minLon, minLat, maxLon, maxLat]&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;heightFromWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bboxParis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;800&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// → { zoom: 11.458, height: 911.81 px }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interpretation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To make the Paris bbox 800 px wide, you need zoom ≈ 11.5.&lt;/li&gt;
&lt;li&gt;At that zoom, the bbox will be about 912 px tall.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When this helps
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Fixed-width web containers (responsive pages where height can grow).&lt;/li&gt;
&lt;li&gt;Landscape static map images that must match a target width.&lt;/li&gt;
&lt;li&gt;Print layouts where column width is constrained and height can flow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo walkthrough
&lt;/h2&gt;

&lt;p&gt;To make the math more intuitive, we prepared a live demo you can explore in your browser:&lt;/p&gt;

&lt;p&gt;To make the math more intuitive, here’s a live demo you can try directly:&lt;br&gt;
&lt;iframe height="600" src="https://codepen.io/geoapify/embed/dPYLYpZ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;What it shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Case 1&lt;/strong&gt;: calculate width and height of a bounding box at a given zoom.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Case 2&lt;/strong&gt;: calculate width when a target height is specified.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Case 3&lt;/strong&gt;: calculate height when a target width is specified.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How to use it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enter or adjust the bounding box coordinates.&lt;/li&gt;
&lt;li&gt;Select a zoom level or provide a target width/height.&lt;/li&gt;
&lt;li&gt;See how the bbox dimensions in pixels update instantly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This interactive tool helps visualize the link between geographic coordinates, zoom levels, and pixel sizes — the same logic you can reuse in code when preparing static images or print maps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Working with bounding boxes in Web Mercator isn’t just about coordinates — it’s also about &lt;strong&gt;pixels&lt;/strong&gt;.&lt;br&gt;
In this article we covered three practical scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Width/height at a given zoom&lt;/strong&gt; → useful when you already know the zoom and want to measure how large your area will appear.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Width from a given height&lt;/strong&gt; → handy when your image or print layout has a fixed height.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Height from a given width&lt;/strong&gt; → perfect for fixed-width web containers or landscape images.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These calculations are especially important when &lt;strong&gt;printing maps&lt;/strong&gt; or generating &lt;strong&gt;static images&lt;/strong&gt; with the &lt;a href="https://www.geoapify.com/static-maps-api/" rel="noopener noreferrer"&gt;Geoapify Static Maps API&lt;/a&gt;. They let you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pick the right zoom to make your area fit,&lt;/li&gt;
&lt;li&gt;avoid cutting off important features,&lt;/li&gt;
&lt;li&gt;ensure labels and markers stay legible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And with the demo, you can experiment in real time to see how zoom, width, and height interact.&lt;/p&gt;

&lt;p&gt;👉 Explore more mapping tools and APIs at &lt;a href="https://www.geoapify.com/" rel="noopener noreferrer"&gt;Geoapify&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>programming</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Free OpenStreetMap Locality Dataset: Cities, Towns, Villages &amp; Hamlets in NDJSON</title>
      <dc:creator>Alfiya Tarasenko</dc:creator>
      <pubDate>Fri, 19 Sep 2025 09:22:48 +0000</pubDate>
      <link>https://forem.com/geoapify-maps-api/free-openstreetmap-locality-dataset-cities-towns-villages-hamlets-in-ndjson-6dh</link>
      <guid>https://forem.com/geoapify-maps-api/free-openstreetmap-locality-dataset-cities-towns-villages-hamlets-in-ndjson-6dh</guid>
      <description>&lt;p&gt;Keeping city, town, and village information up to date is harder than it sounds. The raw data inside &lt;a href="https://www.openstreetmap.org" rel="noopener noreferrer"&gt;OpenStreetMap&lt;/a&gt; is incredibly rich, yet developers still spend hours cleaning duplicates, merging boundaries, and normalizing metadata before it becomes production-ready. &lt;/p&gt;

&lt;p&gt;To make that easier, &lt;strong&gt;Geoapify&lt;/strong&gt; publishes a free OpenStreetMap locality dataset: NDJSON archives that bundle cities, towns, villages, and hamlets for every country, complete with administrative boundaries and multilingual labels. You can browse and download every country bundle from our data hub at &lt;a href="https://www.geoapify.com/download-all-the-cities-towns-villages/" rel="noopener noreferrer"&gt;Download all the cities, towns, villages&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What's inside the bundles&lt;/li&gt;
&lt;li&gt;Working with NDJSON&lt;/li&gt;
&lt;li&gt;What each record contains&lt;/li&gt;
&lt;li&gt;Enriching records with Geoapify Place Details&lt;/li&gt;
&lt;li&gt;Suggested applications&lt;/li&gt;
&lt;li&gt;Quality notes&lt;/li&gt;
&lt;li&gt;Attribution and licensing&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What's inside the bundles
&lt;/h2&gt;

&lt;p&gt;Every country is packaged as a zip file at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://www.geoapify.com/data-share/localities/&amp;lt;country_code&amp;gt;.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, &lt;code&gt;https://www.geoapify.com/data-share/localities/de.zip&lt;/code&gt; contains the German locality files.&lt;/p&gt;

&lt;p&gt;After unzipping you will always find four NDJSON files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;country_code&amp;gt;/
├── place_city.ndjson
├── place-town.ndjson
├── place-village.ndjson
└── place-hamlet.ndjson
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is an example of data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"name":"Aurora","other_names":{"name:en":"Aurora","name:ru":"Орора"},"display_name":"Aurora, Aurora Township, Kane County, Illinois, United States","address":{"city":"Aurora","municipality":"Aurora Township","county":"Kane County","state":"Illinois","ISO3166-2-lvl4":"US-IL","country":"United States","country_code":"us"},"population":180542,"osm_type":"node","osm_id":153812116,"type":"city","location":[-88.3147539,41.7571701],"bbox":[-88.4747539,41.5971701,-88.1547539,41.9171701],"border":{"name":"Aurora","other_names":{"name:en":"Aurora","name:ru":"Орора"},"display_name":"Aurora, Aurora Township, Kane County, Illinois, United States","address":{"city":"Aurora","municipality":"Aurora Township","county":"Kane County","state":"Illinois","ISO3166-2-lvl4":"US-IL","country":"United States","country_code":"us"},"population":180542,"osm_type":"relation","osm_id":124817,"type":"administrative","location":[-88.3147539,41.7571701],"bbox":[-88.4083712,41.679869,-88.204992,41.8221793]}}
{"name":"Austin","other_names":{"name:ar":"أوستن","name:be":"Остын","name:bn":"অস্টিন","name:de":"Austin","name:el":"Ώστιν","name:en":"Austin","name:eo":"Aŭstino","name:es":"Austin","name:fa":"آستین","name:fr":"Austin","name:he":"אוסטין","name:hi":"ऑस्टिन","name:it":"Austin","name:ja":"オースティン","name:ko":"오스틴","name:ku":"Austin","name:la":"Austinopolis","name:pl":"Austin","name:pt":"Austin","name:ru":"Остин","name:sr":"Остин","name:ta":"ஆஸ்டின்","name:te":"ఆస్టిన్","name:tr":"Austin","name:uk":"Остін","name:ur":"آسٹن","name:vi":"Austin","name:zh":"奥斯汀 / 柯士甸","name:azb":"آستین","name:grc":"Αὐγούστα","name:hak":"Austin","name:nan":"Austin","name:yue":"柯士甸","name:zh-Hans":"奥斯汀","name:zh-Hant":"奥斯汀","name:be-tarask":"Остын","name:zh-Hant-HK":"柯士甸"},"display_name":"Austin, Travis County, Texas, United States","address":{"city":"Austin","county":"Travis County","state":"Texas","ISO3166-2-lvl4":"US-TX","country":"United States","country_code":"us"},"population":974447,"osm_type":"node","osm_id":1801308037,"type":"city","location":[-97.7436995,30.2711286],"bbox":[-97.9036995,30.1111286,-97.5836995,30.4311286],"border":{"name":"Austin","other_names":{"name:ar":"أوستن","name:be":"Остын","name:de":"Austin","name:el":"Ώστιν","name:en":"Austin","name:es":"Austin","name:ru":"Остин","name:sr":"Остин","name:uk":"Остін","name:ur":"آسٹن","name:zh":"奥斯汀 / 柯士甸","name:azb":"آستین","name:grc":"Αὐγούστα","name:hak":"Austin","name:nan":"Austin","name:yue":"柯士甸","name:zh-Hans":"奥斯汀","name:zh-Hant":"奥斯汀","name:bn":"অস্টিন","name:eo":"Aŭstino","name:fa":"آستین","name:fr":"Austin","name:he":"אוסטין","name:hi":"ऑस्टिन","name:it":"Austin","name:ja":"オースティン","name:ko":"오스틴","name:ku":"Austin","name:la":"Austinopolis","name:pl":"Austin","name:pt":"Austin","name:ta":"ஆஸ்டின்","name:te":"ఆస్టిన్","name:tr":"Austin","name:vi":"Austin","name:be-tarask":"Остын","name:zh-Hant-HK":"柯士甸"},"display_name":"Austin, Travis County, Texas, United States","address":{"city":"Austin","county":"Travis County","state":"Texas","ISO3166-2-lvl4":"US-TX","country":"United States","country_code":"us"},"population":974447,"osm_type":"relation","osm_id":113314,"type":"administrative","location":[-97.7436995,30.2711286],"bbox":[-97.9367663,30.0985133,-97.5605288,30.5166255]}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure makes it easy to load only the locality types you need without preprocessing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Working with NDJSON
&lt;/h3&gt;

&lt;p&gt;Every file is a &lt;a href="https://docs.mulesoft.com/dataweave/latest/dataweave-formats-ndjson" rel="noopener noreferrer"&gt;newline-delimited JSON&lt;/a&gt; stream. Instead of a single giant JSON array, each line is a self-contained JSON object. That makes the dataset friendly for streaming, piping between tools, or chunked imports.&lt;/p&gt;

&lt;h4&gt;
  
  
  Stream with Node.js
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;readline&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;readline&lt;/span&gt;&lt;span class="dl"&gt;"&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;processFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&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;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&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;rl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;readline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createInterface&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &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;line&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;rl&lt;/span&gt;&lt;span class="p"&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;line&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;continue&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;place&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Work with the place object: console.log(place.name);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;processFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;de/place_city.ndjson&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Read with Python
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;de/place-town.ndjson&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;fh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;fh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="n"&gt;place&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Example usage: print(place["name"], place.get("population"))
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Filter with jq
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'select(.population and .population &amp;gt; 500000) | {name, population}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  de/place_city.ndjson
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the files are pure NDJSON you can plug them into data pipelines, message queues, or databases that accept JSON Lines (e.g. BigQuery, Elasticsearch, Snowflake, or PostgreSQL &lt;code&gt;COPY&lt;/code&gt; with &lt;code&gt;FORMAT json&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  OSM tags used to compile the bundle
&lt;/h2&gt;

&lt;p&gt;Every archive focuses on populated places captured by &lt;a href="https://wiki.openstreetmap.org/wiki/Key:place" rel="noopener noreferrer"&gt;&lt;code&gt;place=*&lt;/code&gt;&lt;/a&gt; tags in OpenStreetMap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://wiki.openstreetmap.org/wiki/Tag:place%3Dcity" rel="noopener noreferrer"&gt;&lt;code&gt;place=city&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.openstreetmap.org/wiki/Tag:place%3Dtown" rel="noopener noreferrer"&gt;&lt;code&gt;place=town&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.openstreetmap.org/wiki/Tag:place%3Dvillage" rel="noopener noreferrer"&gt;&lt;code&gt;place=village&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.openstreetmap.org/wiki/Tag:place%3Dhamlet" rel="noopener noreferrer"&gt;&lt;code&gt;place=hamlet&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to these nodes, OpenStreetMap stores many municipal boundaries as &lt;a href="https://wiki.openstreetmap.org/wiki/Tag:boundary%3Dadministrative" rel="noopener noreferrer"&gt;&lt;code&gt;boundary=administrative&lt;/code&gt;&lt;/a&gt; relations. When such a boundary references a locality (via matching names, &lt;code&gt;linked_place&lt;/code&gt;, or similar tags) we pair it with the corresponding place record and expose it under a &lt;code&gt;border&lt;/code&gt; property. Depending on how many matching relations we find, &lt;code&gt;border&lt;/code&gt; can be a single object or an array of administrative relations.&lt;/p&gt;

&lt;p&gt;Not every boundary has an explicit &lt;code&gt;place=*&lt;/code&gt; tag, but they are still valuable: in many regions administrative relations are the only dataset that hints at a settlement. If we detect one of those boundaries and can infer that it represents a city, town, village, or hamlet, we add it to the dataset with an &lt;code&gt;inferred_type&lt;/code&gt; field so you can decide whether to keep or discard it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What each record contains
&lt;/h2&gt;

&lt;p&gt;Every NDJSON line holds a single locality object. Key fields include:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;osm_type&lt;/code&gt;, &lt;code&gt;osm_id&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;OSM identifiers for the node/way/relation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The &lt;code&gt;place=*&lt;/code&gt; value (&lt;code&gt;city&lt;/code&gt;, &lt;code&gt;town&lt;/code&gt;, &lt;code&gt;village&lt;/code&gt;, &lt;code&gt;hamlet&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;name&lt;/code&gt;, &lt;code&gt;other_names&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Primary place name plus language-specific variants&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;display_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A full, human-readable label similar to Nominatim output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;address&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Structured address metadata (country code, region, admin levels)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;population&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reported population (when present in OSM)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;location&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;[lon, lat]&lt;/code&gt; point coordinate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bbox&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;[min_lon, min_lat, max_lon, max_lat]&lt;/code&gt; bounding box&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;border&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Administrative relation(s) that represent the area of the locality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;inferred_type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Present when the system had to infer the place type from address hints&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All files are newline-delimited JSON, which makes them easy to stream with tools like &lt;code&gt;jq&lt;/code&gt;, Python's &lt;code&gt;json&lt;/code&gt; module, Node.js streams, or any big-data ingestion pipeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sample record
&lt;/h3&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Aachen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"display_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Aachen, Städteregion Aachen, Nordrhein-Westfalen, Deutschland"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"address"&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;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Aachen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Nordrhein-Westfalen"&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;"Deutschland"&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_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"de"&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;"osm_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;"relation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"osm_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;62764&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;"city"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"location"&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="mf"&gt;6.08388&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;50.77535&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"population"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;249646&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bbox"&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="mf"&gt;6.00164&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;50.70452&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;6.21609&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;50.84047&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"border"&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;"osm_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;"relation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"osm_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;62765&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;"administrative"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&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="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;Feel free to trim the fields you do not need—each line is independent, so you can stream-filter the dataset using the tools you already know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enriching records with Geoapify Place Details
&lt;/h2&gt;

&lt;p&gt;The dataset already includes plenty of metadata, but you can dig deeper with Geoapify's Place Details API. Feed it the &lt;code&gt;osm_id&lt;/code&gt; and &lt;code&gt;osm_type&lt;/code&gt; from any record to retrieve enhanced attributes, opening hours, and geometry.&lt;/p&gt;

&lt;p&gt;Example call, for Paris, France:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.geoapify.com/v2/place-details?osm_id=7444&amp;amp;osm_type=r&amp;amp;apiKey=YOUR_API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the geometry for the place:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5uxi1wntotg78uo9fqe2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5uxi1wntotg78uo9fqe2.png" alt="Paris, France" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Replace &lt;code&gt;YOUR_API_KEY&lt;/code&gt; with a valid Geoapify key and customize the OSM identifiers to match the locality you care about. When a record already carries a &lt;code&gt;border&lt;/code&gt; object you can use the identifier inside the border to request full administrative polygons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Suggested applications
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quick visualization.&lt;/strong&gt; Drop any NDJSON file into a Leaflet or MapLibre map to display centroids and polygons.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spatial analytics.&lt;/strong&gt; Load the dataset into PostGIS or BigQuery to join with demographic or business data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Geofencing.&lt;/strong&gt; Use the embedded &lt;code&gt;border&lt;/code&gt; geometries to decide whether a user falls inside a particular locality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search and localization.&lt;/strong&gt; The &lt;code&gt;other_names&lt;/code&gt; field gives you multilingual labels and historical aliases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data quality monitoring.&lt;/strong&gt; Compare &lt;code&gt;population&lt;/code&gt; figures across snapshots to spot OSM changes or anomalies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline and edge deployments.&lt;/strong&gt; Because the data is NDJSON, you can ship only the countries you need to client devices without restructuring the format.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Quality notes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Administrative attachments rely on name matching plus geometry overlap; a handful of localities may not have a &lt;code&gt;border&lt;/code&gt; if the OSM relation is missing or mismatched.&lt;/li&gt;
&lt;li&gt;Population values are only as accurate as the OSM tags. Treat them as hints unless you verify against official statistics.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;inferred_type&lt;/code&gt; appears when the place lacked a definitive &lt;code&gt;place=*&lt;/code&gt; tag. You can filter these out if you only want explicit matches.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Attribution and licensing
&lt;/h2&gt;

&lt;p&gt;These extracts are derived from OpenStreetMap and distributed under the &lt;a href="https://www.openstreetmap.org/copyright" rel="noopener noreferrer"&gt;Open Database License (ODbL)&lt;/a&gt;. Always credit “© OpenStreetMap contributors” and share derivative databases under the same license when you publish them publicly.&lt;/p&gt;

&lt;p&gt;A minimal attribution example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;© [OpenStreetMap contributors](https://www.openstreetmap.org/copyright). Locality dataset processed by [Geoapify](https://www.geoapify.com). Licensed under [ODbL](https://opendatacommons.org/licenses/odbl/).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;The next articles in this series will explore practical workflows: loading the NDJSON into databases, visualizing boundaries, and combining the dataset with live &lt;a href="https://www.geoapify.com/" rel="noopener noreferrer"&gt;Geoapify APIs&lt;/a&gt;. Stay tuned!&lt;/p&gt;

</description>
      <category>openstreetmap</category>
      <category>opendata</category>
      <category>gis</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
