Build

Embed a Static Map and Update Geolocation with Mapbox

7 min read Michael Carroll on Jul 28, 2019

Mapbox is a flexible platform that provides a ton of awesome

and geolocation APIs. At PubNub, we love Mapbox because you can now build beautiful and interactive geolocation and tracking apps by integrating real-time functionality. With that, we’ve launched a number of Mapbox BLOCKS in our BLOCKS Catalog to power your real-time mapping functionality.

In this tutorial, we’ll show you how to embed a simple static map for web and mobile apps and update current geolocation. You’d want to use a static map for apps that don’t require real-time geolocation tracking, but still want to display an updated location to a user at a custom time, or based on a custom action (think Find My Friends, not Lyft).

In the end, your app will look like this:

Mapbox In Action

Before we jump into the tutorial, let’s take a look at the underlying Mapbox API.

Mapbox Static Map API

Mapping services are enormously challenging to build and operate on your own; they require substantial effort and engineering resources to create/populate/curate location data sets, manage tons of satellite and vector image data, implement walking/biking/driving directions, handle abusive changes, and more. Fortunately, the Mapbox APIs make it easy to enable your applications with location-aware features.

Looking closer at the APIs, static maps are just the beginning. There are a lot of API methods available for things like geocoding, custom maps, providing directions, embedding in native mobile apps, and more. It really is a powerful tool for building in location awareness across a wide range of services. In this article though, we’ll keep it simple and implement a basic static map integration that shows a map of the user’s general vicinity.

Getting Started with Mapbox

The first thing you’ll need to get started with Mapbox services is a Mapbox developer account to take advantage of the mapping APIs.

  1. Go to the Mapbox signup form.
  2. Go to the Mapbox studio page and configure your services.
  3. Look at the “Access token” pane of the dashboard and make note of the credential to update the BLOCK in our next step.

Overall, it’s a pretty quick process. And free to get started, which is a bonus!

Setting up the BLOCK

Next, we’ll get set up with BLOCKS, which enables you to run functions on your data in motion. This is how we’ll power the real-time in our app.

Create BLOCK
  • Create a new BLOCK.
Create Event Handler
  • Paste in the BLOCK code from the next section and update the client token with the Mapbox client token from the previous steps above.
BLOCK Code
  • Start the BLOCK, and test it using the “publish message” button and payload on the left-hand side of the screen.

That’s all it takes to create your serverless code running in the cloud!

Diving into the Code – the BLOCK

You’ll want to grab the 24 lines of BLOCK JavaScript and save them to a file called  pubnub_mapbox_static_block.js. It’s available as a Gist on GitHub for your convenience.

First up, we declare our dependency on xhr and query (for HTTP requests) and create a function to handle incoming messages.

export default request => {
    let xhr = require('xhr');
    let query = require('codec/query_string');

Next, we set up variables for accessing the service (the API url and client token).

    let clientToken = 'YOUR_CLIENT_TOKEN';
    let apiUrl = 'https://api.mapbox.com/styles/v1/mapbox/streets-v8/static';

Next, we set up the HTTP params for the mapping API URL (note: in this demo, the client browser requests the URL, we are just constructing that URL in the BLOCK).

We use a GET request for the URL. We use the JSON content of the message as the source of most important parameters, and hard-code the resolution (for now).

    let zoom = request.message.zoom;
    let resolution = '300x200';
    let lat = request.message.lat;
    let lng = request.message.lng;
    let queryParams = {
        access_token: clientToken,
    };

Finally, we construct the static map URL with the given data and decorate the message with a map attribute containing the static map URL. Pretty easy!

    apiUrl += '/' + lng + ',' + lat + ',' + zoom + '/' + resolution;
    let url = apiUrl + '?' + query.stringify(queryParams);
    request.message.map = url;
    return request.ok()
};

All in all, it doesn’t take a lot of code to embed static maps to our application. OK, let’s move on to the UI!

Diving into the Code – the User Interface

You’ll want to grab these 95 lines of HTML & JavaScript and save them to a file called pubnub_mapbox_static_ui.html.

The first thing you should do after saving the code is to replace two values in the JavaScript:

  • YOUR_PUB_KEY: with the PubNub publish key mentioned above.
  • YOUR_SUB_KEY: with the PubNub subscribe key mentioned above.

If you don’t, the UI will not be able to communicate with anything and probably clutter your console log with entirely too many errors.

For your convenience, this code is also available as a Gist on GitHub, and a Codepen as well. Enjoy!

Dependencies

First up, we have the JavaScript code & CSS dependencies of our application.

<!DOCTYPE html>
<html>
  <head>
    <title>Angular 2</title>
    <script src="https://unpkg.com/core-js@2.4.1/client/shim.min.js"></script>
    <script src="https://unpkg.com/zone.js@0.7.2/dist/zone.js"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.9/Reflect.js"></script>
    <script src="https://unpkg.com/rxjs@5.0.1/bundles/Rx.js"></script>
    <script src="https://unpkg.com/@angular/core/bundles/core.umd.js"></script>
    <script src="https://unpkg.com/@angular/common/bundles/common.umd.js"></script>
    <script src="https://unpkg.com/@angular/compiler/bundles/compiler.umd.js"></script>
    <script src="https://unpkg.com/@angular/platform-browser/bundles/platform-browser.umd.js"></script>
    <script src="https://unpkg.com/@angular/forms/bundles/forms.umd.js"></script>
    <script src="https://unpkg.com/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js"></script>
    <script src="https://unpkg.com/pubnub@4.3.3/dist/web/pubnub.js"></script>
    <script src="https://unpkg.com/pubnub-angular2@1.0.0-beta.8/dist/pubnub-angular2.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" />
  </head>

For folks who have done front-end implementation with Angular2 before, these should be the usual suspects:

  • CoreJS ES6 Shim, Zone.JS, Metadata Reflection, and RxJS : Dependencies of Angular2.
  • Angular2 : core, common, compiler, platform-browser, forms, and dynamic platform browser modules.
  • PubNub JavaScript client: to connect to our data stream integration channel.
  • PubNub Angular2 JavaScript client: provides PubNub services in Angular2 quite nicely indeed.

In addition, we bring in the CSS features:

  • Bootstrap: in this app, we use it just for vanilla UI presentation.

Overall, we were pretty pleased that we could build a nifty UI with so few dependencies. And with that… on to the UI!

The User Interface

Here’s what we intend the UI to look like:

pubnub_mapbox_static_overview

The UI is pretty straightforward – everything is inside a main-component tag that is managed by a single component that we’ll set up in the Angular2 code.

<body>
  <main-component>
    Loading...
  </main-component>

Let’s skip forward and show that Angular2 component template. We provide a simple button to perform the publish() action which publishes our coordinates as a message that will be decorated by the BLOCK.

<div class="container">
    <pre>
    NOTE: make sure to update the PubNub keys below with your keys,
    and ensure that the BLOCK settings are configured properly!
    </pre>
    <h3>MyApp Static Maps Integration</h3>
    <hr/>
    <p>Current location (requires location permission): <b>lat</b>={{lat}}, <b>lng</b>={{lng}})</p>
    <button class="btn btn-primary" (click)="publish()">Get Map!</button>
    <br/>
    <br/>
    <ul class="list-unstyled">
      <li *ngFor="let item of messages.slice().reverse()">
        <img [src]="item.message.map" />
      </li>
    </ul>
</div>

The component UI consists of a simple list of messages (in our case, the map images). We iterate over the messages in the controller scope using a trusty ngFor. Each message includes the static map image URL in the map attribute.

And that’s it – a functioning real-time UI in just a handful of code (thanks, Angular2)!

The Angular2 Code

Right on! Now we’re ready to dive into the Angular2 code. It’s not a ton of JavaScript, so this should hopefully be pretty straightforward.

The first lines we encounter set up our application (with a necessary dependency on the PubNub AngularJS service) and a single component (which we dub main-component).

<script>
var app = window.app = {};
app.main_component = ng.core.Component({
    selector: 'main-component',
    template: `...see previous...` 

The component has a constructor that takes care of initializing the PubNub service, and configuring the channel name and client lat/lng values. NOTE: make sure the channel matches the channel specified by your BLOCK configuration and the BLOCK itself!

    }).Class({
        constructor: [PubNubAngular, function(pubnubService){
            var self = this;
            self.pubnubService = pubnubService;
            self.channelName = 'mapbox-channel';
            self.lat = self.lng = "loading...";

Early on, we initialize the pubnubService with our credentials.

            pubnubService.init({
                publishKey:   'YOUR_PUB_KEY',
                subscribeKey: 'YOUR_SUB_KEY',
                uuid: PubNub.generateUUID(),
                ssl:true
            });

We subscribe to the relevant channel and create a dynamic attribute for the messages collection.

            pubnubService.subscribe({channels: [self.channelName], triggerEvents: true});
            self.messages = pubnubService.getMessage(this.channelName,function(msg){
              // no handler necessary, dynamic collection of msg objects
            });

Also, since Angular2 doesn’t have an $interval component, we use RxJS Observable to set up an interval timer for updating the client browser’s location. Every 3 seconds, we update the Angular2 component with the client’s latitude/longitude.

            Rx.Observable.interval(3000).subscribe(()=>(navigator.geolocation.getCurrentPosition((position)=>{
              self.lat = position.coords.latitude;
              self.lng = position.coords.longitude;
            })));
        }],

We also create a publish() event handler that performs the action of publishing the new message to the PubNub channel including location and zoom data.

        publish: function(){
            this.pubnubService.publish({channel: this.channelName, message:{lat:this.lat,lng:this.lng,zoom:8}});
        }
    });

Now that we have a new component, we can create a main module for the Angular2 app that uses it. This is pretty standard boilerplate that configures dependencies on the Browser and Forms modules and the PubNubAngular service.

app.main_module = ng.core.NgModule({
    imports: [ng.platformBrowser.BrowserModule, ng.forms.FormsModule],
    declarations: [app.main_component],
    providers: [PubNubAngular],
    bootstrap: [app.main_component]
}).Class({
    constructor: function(){}
});

Finally, we bind the application bootstrap initialization to the browser DOM content loaded event.

document.addEventListener('DOMContentLoaded', function(){
    ng.platformBrowserDynamic.platformBrowserDynamic().bootstrapModule(app.main_module);
});

We mustn’t forget close out the HTML tags accordingly.

});
</script>
</body>
</html>

Not too shabby for about 95 lines of HTML & JavaScript!

Additional Features

There are a couple other endpoints worth mentioning in the Mapbox API.

You can find detailed API documentation here and here.

  • Custom Maps: ability to customize themes and other map attributes, such as markers.
  • Geocoding: translating location names to lat/lng (and vice versa).
  • Directions: navigating from location to location.

All in all, we found it pretty easy to get started with static maps using the API, and we look forward to using more of the deeper integration features!

Conclusion

Thank you so much for joining us in the Static Maps article of our BLOCKS and web services series! Hopefully it’s been a useful experience learning about location-aware technologies. In future articles, we’ll dive deeper into additional web service APIs and use cases for other nifty services in real time web applications.

Stay tuned, and please reach out anytime if you feel especially inspired or need any help!

0