Cabin — React & Redux Example App — Mapbox

This is the 8th post in our tutorial series created by getstream.io. The final result is your own feature-rich, scalable social network app built with React and Redux! Visit cabin.getstream.io for an overview of all 8 tutorials and a live demo. The full source code is available on GitHub.

Stay updated to upcoming posts in this React & Redux series:

Introduction

Foursquare, Evernote, Instacart, Pinterest, GitHub, and Etsy have something in common — their maps are powered by Mapbox.

header

Mapbox provides a number of incredible services that are easy to integrate with your application, from Maps and Directions to Geocoding, and even Satellite Imagery.

For Cabin we will be using two of their services: displaying custom maps and geocoding user provided locations. We really like using Mapbox because their dedication to making developer tools intuitive. In addition, Mapbox has Open Source Components, which makes them a great fit for our integration. After this tutorial you’ll have the following map up and running:

Screen Shot 2016-06-22 at 10.16.01 AM

Getting Started

Step 1:

Head over to the Mapbox Studio and sign up for a free account.

Next up, we’re going to be incorporating a map into our app. A style is a document that defines the visual appearance of a map. More here.

Step 2:

In Mapbox Studio, click on Styles.

Step 3:

Select your template option, and click “Create”.

Style URL

One more step: We need to get the Style URL, which we will use in our client-side code and API.

Step 1:

Click on your published style as shown below — and then click “Share & Use”.

Screen Shot 2016-06-20 at 2.11.39 PM

Step 2:

Get your Style URL, which you will use in your JavaScript:

Screen Shot 2016-06-20 at 2.13.41 PM

Step 3:

Go back to the Home page in Studio — and get your access token from the sidebar.

That should be enough work in the Mapbox Studio to get started! Now it’s time to implement Mapbox in our code…

Client Side

So, in our Cabin app, we have functionality to click on a post and visualize its location. This is where our the Mapbox JavaScript library comes into play:

Mapbox GL JS is a JavaScript library that uses WebGL to render interactive maps from vector tiles and Mapbox GL styles. It is part of the Mapbox GL ecosystem, which includes Mapbox Mobile, a compatible renderer written in C++ with bindings for desktop and mobile platforms.

Step 1:

Let’s add the library in our project in /app/views/index.ejs:

//api.tiles.mapbox.com/mapbox-gl-js/v0.17.0/mapbox-gl.js

Step 2:

Next, we can add our access token to our env.sh file:

From the Mapbox dashboard, head over to Account (bottom left), and then click on “Access Token” on the top right corner of your screen. Here you will be able to generate a new token for use.

Screen Shot 2016-06-23 at 9.29.44 AM

Now that we have our token, replace the default value in our env.sh file with the actual token:

export MAPBOX_ACCESS_TOKEN=VALUE

Restart the app and make sure to run Webpack:

cd /app
source ../env.sh; webpack --watch --progress

Note: The watch and progress flags are great for development. As you might guess, watch keeps an eye on the filesystem and automatically rebuilds your application when you make a change. Progress is a nice feature that keeps you up to date on the status of your build, which can be nice for apps that become very large (like Cabin).

Step 3:

Open up app/routes/Location/Location.js.

The loadMapboxMapmethod component is used to render our map. As can be seen below:

loadMapboxMap = (lng, lat, name) => {

mapboxgl.accessToken = config.mapbox.accessToken
let map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/stream/cinj7vvpfbbbib7mbzpyhkw39',
center: [lng, lat],
zoom: 9,
})

map.on('load', function () {
map.addSource('markers', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [lng, lat]
},
properties: {
title: name,
'marker-symbol': 'default_marker'
}
}]
}
})

map.addLayer({
id: 'markers',
type: 'symbol',
source: 'markers',
layout: {
'icon-image': 'default_marker',
'text-field': name,
'text-font': ['Open Sans Semibold', 'Arial Unicode MS Bold'],
'text-offset': [0, 0.6],
'text-anchor': 'top'
}
})
})

}

For the most part, our map functionality is handled solely by the following JavaScript — with our map container being the “map” id in our JSX inside of render():

render() {
return (
<div className="page">
<div className="location">
<div className="map" id="map"></div>
<div className="images">
<h1>{this.state.location}</h1>
<div className="grid">
{this.props.loc.map(item =>
<div className="grid-cell" key={`location-${item.id}`}>
<Link to={`/photos/${item.id}`}>
<img src={`${config.imgix.baseUrl}/${item.filename}?auto=enhance&w=200&h=200&fit=crop&fm=png&dpr=2`} />
</Link>
</div>
)}
</div>
</div>
</div>
</div>
)
}

Server Side

When we upload a glorious photo of a cabin to our app, we need a way to geocode the location the user has entered in the freeform text box. No worries, Mapbox has us covered.

Setting up the geocoding functionality is easy.

Let’s install the library via npm:

npm install --save mapbox-geocoding

Note: You’ll need to source and restart the API since we added an updated token to our env.sh file by hitting control + c (to stop the api), followed by npm start (to start the api).

Step 4:

Next, open up /api/routes/uploads.js and head down to around line ~269.

Here, we are creating a POST method to /uploads that we will use to upload images to our server.

Sounds like a lot, right? It’s actually simple.

Here’s the code:

// use mapbox to get latitude and longitude
function(cb) {

// initialize mapbox client
geo.setAccessToken(config.mapbox.accessToken);

// get location data
geo.geocode('mapbox.places', data.location, function (err, location) {

if (err) {
cb(err);
}

// if the location was found
if (location.features.length) {

// extract coordinates
var coords = location.features[0].geometry.coordinates;
if (coords.length) {

// assign to latitude and longitude in data object
data.longitude = coords[0];
data.latitude = coords[1];

}

}

cb(null)

});

}

Note: We’re using the popular NPM module, Async, to handle our waterfall approach of actions. You’re welcome to use any other promise style library; however, we felt that Async was the best for our scenario.

As you can see, we are passing a plain text string (data.location) to the .geocode method — and, in return, we are getting a longitude and latitude value.

More documentation on the Geocoding API can be found here.

And that’s it! (Really, it’s that easy to get Mapbox implemented in your application.)

Additional Features

Mapbox is the superior product when it comes to mapping and geocoding. They bolster some great features and services — all with open source libraries and SDKs that make their products easy to implement. While the Mapbox functions that we use for Cabin are all free, Mapbox also has an impressive offering of paid services. It would be well worth your time to check out a few of their other services, like:

Conclusion

We admire the way Mapbox has created a product that is both powerful AND intuitive. It is the perfect solution to handle custom themed maps and geo locations.

In the next post, we’ll cover how we’re using Digital Ocean to power the hosting for Cabin. Add your email on cabin.getstream.io or follow @getstream_io on Twitter to stay up to date about the latest Cabin tutorials.

This tutorial series is created by getstream.io. Try the 5 min interactive tutorial to learn how Stream works.


Originally published at The Stream Blog.