Adding TomTom Maps to a Django App: Building a Front End with a Map
In part one of this series, we created a Django project, added a store locator app and a database model for storing locations. Now we’ll add a TomTom map to our locator app.
In part one of this series, we created a Django project, added a store locator app and a database model for storing locations. We have even filled our database with store locations. Now it’s time to add a TomTom map to our locator app.
SETTING UP DJANGO URLS
First, we must configure Django to assign a URL to the locator app. In the tomtom_django sub-folder, open urls.py and edit its contents to:
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('locator/', include('locator.urls')),
path('admin/', admin.site.urls),
]
Then, in the locator sub-folder, create a urls.py file and enter the following:
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
With these two changes, we’ve told Django that the /locator URL should direct requests to the locator app and the index view that we’re about to create.
CREATING VIEWS
Next, in the locator sub-folder, open views.py and add:
from django.shortcuts import render
import json
from .models import Location
def index(request):
location_list = list(Location.objects.order_by('name').values())
location_json = json.dumps(location_list)
context = {'locations': location_json}
return render(request, 'locator/index.html', context)
This creates a view named index. It loads the list of stores from the database, converts them to JSON, and adds them to a context object so we’ll be able to use them when we render the index view template, we create next.
Inside the locator app folder, create a templates folder, and then inside the templates folder, create a locator folder. The folder hierarchy from the top level of the project should look like:
│ tomtom_django/
├── locator/
├── templates/
├── locator/
It might seem strange that we have another locator folder inside the templates, but we need it because of the way Django searches for templates. For example, if another Django app installed in our project has a template name (index.html) the same as one of the locator app’s templates, Django won’t know which one to load and throws an exception. The final locator sub-folder stops this from happening.
Now, create two files inside of the templates/locator called base.html and index.html. You can copy the contents of base.html from the GitHub repository. It contains boilerplate code to create our HTML page and add a title bar. The code that does all the hard work lives in index.html:
{% extends "locator/base.html" %}
{% block content %}
<style>
#map {
height: 500px;
width: 100%;
}
.mapboxgl-marker {
cursor: pointer;
}
.locator-popup {
font-size: 14px;
}
</style>
<h1>Store Locations</h1>
<h5>Click a location to see store details</h5>
<div id='map' class='map'></div>
<!-- load TomTom Maps Web SDK from CDN -->
<link rel='stylesheet' type='text/css' href='https://api.tomtom.com/maps-sdk-for-web/cdn/6.x/6.13.0/maps/maps.css'/>
<script src='https://api.tomtom.com/maps-sdk-for-web/cdn/6.x/6.13.0/maps/maps-web.min.js'></script>
<script>
// create the map
tt.setProductInfo('TomTom Maps Django Demo', '1.0');
let map = tt.map({
key: 'YOUR API KEY GOES HERE',
container: 'map'
});
// add store markers
let bounds = []
let storeLocations = JSON.parse("{{ locations|escapejs }}");
for (let storeLocation of storeLocations) {
let coordinates = [storeLocation.longitude, storeLocation.latitude];
bounds.push(coordinates);
// create popup to display store information when the marker is clicked
let popup = new tt.Popup().setHTML(`
<div class="locator-popup">
<h6>Store Name</h6>
<p>${storeLocation.name}</p>
<h6>Address</h6>
<p>${storeLocation.address}</p>
</div>
`);
let marker = new tt.Marker()
.setLngLat(coordinates)
.setPopup(popup)
.addTo(map);
}
// zoom the map to fit all markers
map.on('load', () => {
map.fitBounds(bounds, {
padding: { top: 50, bottom:50, left: 50, right: 50 }
});
})
</script>
{% endblock %}
UNDERSTANDING OUR MAPPING CODE
Let’s go through the code one step at a time. We start by importing the base template to add a nice-looking title bar to our page. We then add a basic stylesheet that adds styles for our map container, maps markers (which we’ll use to represent store locations), and the popup displayed when you click on a store.
Next, add a couple of headers, add a div that will hold our map, and then add link and script elements to load the TomTom Maps Web SDK from TomTom’s CDN. If we were using the SDK with a framework like React, we should install it from NPM. But, if it’s being embedded in a web page (like this tutorial is), it’s best to load it from the CDN.
Finally, we have a script block containing all the code needed to make our map work. In a few lines of code, we can add a map to our page:
tt.setProductInfo('TomTom Maps Django Demo', '1.0');
let map = tt.map({
key: ‘YOUR API KEY GOES HERE’,
container: ‘map’
});
Edit the key property to insert our API key from the TomTom developer portal. If you don’t have an API key yet, it only takes a minute to register and get one.
Next, we add markers to the map. Each marker will show up as an icon representing the store location:
let bounds = []
let storeLocations = JSON.parse("{{ locations|escapejs }}");
for (let storeLocation of storeLocations) {
let coordinates = [storeLocation.longitude, storeLocation.latitude];
bounds.push(coordinates);
// create popup to display store information when the marker is clicked
let popup = new tt.Popup().setHTML(`
<div class="locator-popup">
<h6>Store Name</h6>
<p>${storeLocation.name}</p>
<h6>Address</h6>
<p>${storeLocation.address}</p>
</div>
`);
let marker = new tt.Marker()
.setLngLat(coordinates)
.setPopup(popup)
.addTo(map);
}
We start with a bounds array that will store the longitude and latitude coordinates of each marker. We’ll need this later to set the map’s zoom and center features automatically. Then, we parse the JSON store locations we passed into the template from Django.
Next, we loop through each store location. We’ll create an array from its coordinates with longitude first and latitude second since this is the format in which the TomTom market expects to receive the marker location. We then add it to the bounds array.
We then create a simple HTML popup that appears when the user clicks on the marker for this store. It shows the store location’s name and address.
Finally, we add the marker to the map. There’s just one final step:
map.on('load', () => {
map.fitBounds(bounds, {
padding: { top: 50, bottom:50, left: 50, right: 50 }
});
})
This code adds a function that’s called when the map finishes loading. It automatically zooms and re-centers the map to display all the store markers we just added. Now, we add a bit of padding to ensure the map does not cut off any markers.
Here’s how it looks in action:
And if we click one of the store markers, we’ll see its information:
And we’re done! We’ve created our very own Django store locator app.
NEXT STEPS
If we’d like to go a step further, there are a couple of next steps to try. We could start by adding more information about each store — perhaps hours, phone number, and e-mail address. To do this, we can edit the Location model, add the fields, and then create and run a migration.
We may also consider adding a page that automatically retrieves the longitude and latitude of an address from the TomTom Search API’s Geocode service. This way, users can easily add new stores without needing to look up and enter the coordinates themselves.
If you haven’t started yet, why wait? Sign up for a TomTom developer account and create your excellent mapping app!
This article was originally published at https://developer.tomtom.com/blog.