> ## Documentation Index
> Fetch the complete documentation index at: https://developers.hubspot.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

---
id: 509ef9ca-b84d-4698-9251-8433f5b7f62a
---

# Build location-based pages with HubDB

> Learn how to build location-based website pages using HubDB.

HubDB supports locations as a field type, which enables you to build location-based pages on HubSpot's CMS.

In this tutorial, learn how to create a page with a map on HubSpot’s CMS using HubDB. You can reference more detailed HubDB documentation [here](/cms/start-building/features/storage/hubdb).

You’ll need:

* Approximately one hour.
* Some prior knowledge of HubSpot's CMS, HTML and CSS will be needed to customize your page.

## What's a location?

When you create a column on a HubDB table with the *Location* field type, you're designating this column as a place to store coordinates that point to a specific location in the world.

A location is often represented as *coordinates* (a pair of decimal numbers) such as 42.36, -71.11. The first number is the latitude, or the degrees North or South of the equator. The second number is the longitude, or the degrees from the Prime Meridian. The Prime Meridian is an imaginary line starting at the North Pole, running through Greenwich, England, France, Spain, Algeria, Ghana and ending at the South Pole. Longitudes east of that line are positive, west is negative. At 180 degrees, the positive and negative degree values meet.

<Steps>
  <Step title="Find the visitor">
    With the [Global Positioning System](https://en.wikipedia.org/wiki/Global_Positioning_System) (GPS), you can determine the location of a visitor to your site. Mobile phone devices have GPS chips that can receive GPS signals. These devices can also use nearby Wi-Fi networks or mobile network towers to determine their location. Laptop and desktop computers typically use Wi-Fi to determine their location.

    Within a page loaded in the web browser, you can use JavaScript to request a device's location.

    ```html theme={null}
    <script>
      navigator.geolocation.getCurrentPosition(function (position) {
        console.log(
          "I'm at " + position.coords.latitude + ', ' + position.coords.longitude
        );
      });
    </script>
    ```

    This will first ask the visitor if it's OK for the page to get the visitor's location, and then respond with the coordinates. The response may appear like the following:

    ```html theme={null}
    I'm at 42.370354299999995, -71.0765916
    ```
  </Step>

  <Step title="Build a database">
    Once you know the visitor's location, you can help them find different locations in their general area.

    Create a new table in HubDB and add two columns: 'location' (type: Location) an 'cuisine' (type: Text).

    It should look like this:

    <Frame>
      <img src="https://www.hubspot.com/hubfs/location-example.png" alt="location-example" />
    </Frame>

    You can build your own database of local eateries or you can import data for lunch spots near HubSpot in Cambridge, Massachusetts. Here's a sample file you can use: [lunch-spots.csv](https://cdn2.hubspot.net/hubfs/327485/lunch-spots.csv). Upload the file to HubDB, and map the columns as shown.

    <Frame>
      <img src="https://www.hubspot.com/hubfs/Knowledge_Base_2021/Developer/map-fields-location-table.png" alt="map-fields-location-table" />
    </Frame>

    Once you finish the import, you should have a list of eateries that looks like this:

    <Frame>
      <img src="https://www.hubspot.com/hubfs/Knowledge_Base_2021/Developer/list-of-cambridge-eateries.png" alt="list-of-cambridge-eateries" />
    </Frame>

    Publish your table, make a note of the table ID (the last number in the URL) and you're ready to start using your data.
  </Step>

  <Step title="Create a simple listing page">
    Create a simple list for this data using HubL and the HubDB functions.

    In the design manager, create a new template using the code editor. You can name it something like "lunchspots.html".

    Add this code just after `<body>` tag, using the table ID you noted above.

    ```hubl theme={null}
    {% set table_id = YOUR_TABLE_ID %}

         <table>
            {% for row in hubdb_table_rows(table_id) %}
              <tr>
                  <td>{{ row.name }}</td>
                  <td>{{ row.cuisine }}</td>
              </tr>
            {% endfor %}
        </table>
    ```

    When you preview the template, a simple list of restaurants and a description of their cuisine should appear. If nothing appears, double-check that your table ID is correct and that the table is published.
  </Step>

  <Step title="Filter to a single listing">
    Your listing page can also double as a detail page. You can link the restaurant name to a page specific to that restaurant. Make sure to leave in the `set table_id...` line here and in all other code samples on this page.

    ```hubl theme={null}
    <table>
         {% for row in hubdb_table_rows(table_id) %}
           <tr>
               <td><a href="https://developers.hubspot.com/docs?{{ request.query }}&row_id={{ row.hs_id }}">{{ row.name }}</a></td>
               <td>{{ row.cuisine }}</td>
           </tr>
         {% endfor %}
    </table>
    ```

    Each row will now be linked back to the same page with a query string parameter in `row_id` which is the unique ID of this row. Note the use of `request.query` to make sure any existing query string parameters are preserved on the current URL.

    Now we'll add the details:

    ```hubl theme={null}
    {% if request.query_dict['row_id'] %}
       {% set row = hubdb_table_row(table_id, request.query_dict['row_id']) %}
       <h1>{{ row.name }}</h1>
       <h3>{{ row.cuisine }}</h3>


    {{ row.location['lat'] }}, {{ row.location['lon'] }}
    {% else %}
       <table>
          {% for row in hubdb_table_rows(table_id) %}
            <tr>
                <td><a href="https://developers.hubspot.com/docs?{{ request.query }}&row_id={{ row.hs_id }}">{{ row.name }}</a></td>
                <td>{{ row.cuisine }}</td>
            </tr>
          {% endfor %}
     </table>
    {% endif %}
    ```

    The if statement (`{% if request.query_dict['row_id'] %}`) determines whether to show the details or skip down to the block to show the listing. It will look something like this:

    <Frame>
      <img src="https://www.hubspot.com/hubfs/Knowledge_Base_2021/Developer/restaurant-example.png" alt="restaurant-example" />
    </Frame>
  </Step>

  <Step title="Determine the visitor">
    You can also ask for the visitor's location, so you can show them how far away each restaurant is. Change your code to the following.

    ```hubl theme={null}
    {% if request.query_dict['row_id'] %}
      {% set row = hubdb_table_row(table_id, request.query_dict['row_id']) %}
      <h1>{{ row.name }}</h1>
      <h3>{{ row.cuisine }}</h3>


    {{ row.location['lat'] }}, {{ row.location['lon'] }}
    {% elif request.query_dict['lat'] %}
          <table>
          {% for row in hubdb_table_rows(table_id) %}
            <tr>
                <td><a href="https://developers.hubspot.com/docs?{{ request.query }}&row_id={{ row.hs_id }}">{{ row.name }}</a></td>
                <td>{{ row.cuisine }}</td>
                <td>{{ row.location|geo_distance(request.query_dict['lat'], request.query_dict['lon'], "mi")|round(3) }} mi away</td>
            </tr>
          {% endfor %}
          </table>
    {% else %}
      Please allow us to read your location so we can show you the closest lunch spots.
      <script>
        navigator.geolocation.getCurrentPosition(function(position) {
          window.location = window.location.href.split('?')[0] + "?lat=" + position.coords.latitude + "&lon=" + position.coords.longitude;
        });
      </script>
    {% endif %}
    ```

    This code introduces an `if` statement. In the default case, it asks the visitor to share their location. If they accept, it redirects the page with `lat` and `lon` query parameters. The second time the page loads, it shows the list, now with a third column that is the calculated distance from the provided location.

    Since `row.location` just returns the restaurant's coordinates, HubSpot uses the `geo_distance` filter to calculate the distance. It takes the visitor's latitude, longitude and the units of the distance. The units default to meters ("M"), but also accept kilometers ("KM"), miles ("MI"), and feet ("FT"). Lastly, HubSpot round the number a bit to make it more readable.
  </Step>

  <Step title="Sort the list">
    If the list got much bigger, it would be helpful to show the closest restaurants first. You can do so by adding sorting options to the `hubdb_table_rows` function. Change this section as follows:

    ```hubl theme={null}
    {% elif request.query_dict['lat'] %}
          {% set params = "orderBy=geo_distance(location," ~ request.query_dict['lat'] ~ "," ~ request.query_dict['lon'] ~ ")" %}
          <table>
          {% for row in hubdb_table_rows(table_id, params) %}
            <tr>
    ```

    Now the list will be sorted by the distance between the location column and the visitor's coordinates. The key is building the `geo_distance` ordering function which ends up looking like `orderBy=geo_distance(42.37,-71.076)`. To sort the list in reverse order, with the restaurants the furthest away first, you can prepend a `-` (minus sign) to `geo_distance`.
  </Step>

  <Step title="Filter by distance">
    You can filter the list by distance as well. To find all restaurants less than one mile away, use `geo_distance` as a query filter.

    ```hubl theme={null}
    {% elif request.query_dict['lat'] %}
          {% set params = "orderBy=geo_distance(location," ~ request.query_dict['lat'] ~ "," ~ request.query_dict['lon'] ~ ")" %}
          {% set params = "geo_distance(location," ~ request.query_dict['lat'] ~ "," ~ request.query_dict['lon'] ~ ",mi)__lt=1.0&" ~ params %}

          <table>
          {% for row in hubdb_table_rows(table_id, params) %}
            <tr>
    ```

    This constructs another parameter to the HubDB query which looks like `geo_distance(location,42.37,-71.076,mi)__lt=1.0`

    The `geo_distance` query filter takes four arguments. First is the location column that you want to filter by. The next three parameters are latitude, longitude, and distance units. After the `geo_distance` operator, specify the filter operator, "less than" or "lt". Finally, after the equal sign is the value to compare with the result of the function.
  </Step>

  <Step title="Add a map">
    You can [Google Maps APIs](https://developers.google.com/maps/documentation/javascript/) to create a visual map of the list. Replace the block of code inside the second if condition (`elif request.query_dict['lat']`) with this:

    ```hubl theme={null}
    <style>
      #map {
        width: 100%;
        height: 1200px;
      }
    </style>
    <div id="map" height="500"></div>
    <script>
        var map;
        function initMap() {
          map = new google.maps.Map(document.getElementById('map'), {
            center: {lat: 42.3665171, lng: -71.0820328}, zoom: 16
          });

        var marker_origin = new google.maps.Marker({
           map: map,
           title: "you are here",
           icon: { scaledSize: {width:50, height:50}, url: "http://maps.google.com/mapfiles/kml/paddle/blu-blank.png"},
           position: {lat: {{ request.query_dict['lat'] }}, lng: {{ request.query_dict['lon'] }}}
        });

      {% for row in hubdb_table_rows(table_id) %}
        var marker_{{ row.hs_id }} = new google.maps.Marker({
            map: map,
            position: {lat: {{ row.location["lat"] }}, lng: {{ row.location["lon"] }}},
            title: '{{ row.name }}',
            icon: { scaledSize: {width:40, height:40}, url: "http://maps.google.com/mapfiles/kml/shapes/dining.png"}
        });

        marker_{{ row.hs_id }}.addListener('click', function() {
          new google.maps.InfoWindow({
              content: '<div> {{ row.name }} is {{ row.location|geo_distance(request.query_dict['lat'], request.query_dict['lon'], "mi")|round(3) }} miles away</div>'
          }).open(map, marker_{{ row.hs_id }});
        });
      {% endfor %}
        }
    </script>
    <script
      src="https://maps.googleapis.com/maps/api/js?key=YOUR_GOOGLE_API_KEY&callback=initMap"
      async
      defer
    ></script>
    ```

    Add in your table ID and your [Google API key](https://developers.google.com/maps/documentation/javascript/get-api-key) and you should now see an interactive map in place of the listing. Your map should look like this (with your blue origin marker wherever you are).

    <Frame>
      <img src="https://www.hubspot.com/hubfs/Knowledge_Base_2021/Developer/sample-hubdb-map.png" alt="sample-hubdb-map" />
    </Frame>

    That's it! You've built a page that uses a visitor's location to find a local place to eat. The key is the `geo_distance` function which can be used to filter, order, and calculate the distance. This simple page could be enhanced by adding a toggle between the listing and map view, a map and more info on the details page, and perhaps a link to each restaurant's website.

    If the visitor's browser cannot determine their location, or they want to look for places in a different area, you can allow them to search for a location using the [Google Maps Geocoding API](https://developers.google.com/maps/documentation/geocoding/start).
  </Step>
</Steps>
