Location Based Content With a MEAN Stack and Contentful


I am the lead developer at We Are Living - a company building an exciting new app and suit of supporting web projects. I oversee the development of our core projects built with MEAN stack technologies.


At We Are Living, we spent quite a bit of time playing with iBeacons, HTML geolocation and new mapping technologies. After discovering a generous selection of tools for working with location based data, we decided to build Leeds Living, a digital city guide with a focus on location and real-time data. So to demonstrate how we achieved some of the core functionality within Leeds Living, we will share a taster of the process behind the product. This article assumes you already have some knowledge of all technologies mentioned.

Choosing a CMS

By the end of 2015, we’re hoping to unveil a new app - essentially Leeds Living on steroids. The Living App will be a groundbreaking lifestyle app integrated with thousands of iBeacons installed across Leeds city centre, providing real-time anonymous marketing data to businesses and pushing offers and useful information to users when they are out and about in the city. So we needed a CMS that would not only deliver content to the Leeds Living site, but also future-proof us for when we need to access the same data from the app. We chose Contentful for this very reason. The guys that built it in Berlin call it an “API-first content management system for multi-device online publishing”.
When building content types within the system, we came across 2 fields that were simple yet invaluable to us. The ‘location’ field accepts a postcode or address and stores it as coordinates. The ‘object’ field accepts an arbitrary JSON structure; we used this to house more complex location information.

Content Delivery

We began entering local businesses into the system, each with location coordinates. The content delivery API then allowed us to access this content from our NodeJS server.
var contentful = require("contentful").createClient({
 accessToken: "xxxxxxxx",
 space: "xxxxxxxx"
});
app.get("/api/place/:slug", function(req, res){ 
 contentful.entries({ 
 "content_type": contentType.place,
 "fields.slug[in]": req.params.slug,
 "limit": 1
 }, function(err, entries){
 res.send(entry[0]);
 }); 
});
This API request is made by our Angular frontend, where we also apply the results to our scope.
app.controller("PlaceController", function($scope, $routeParams, $http){ 
 $http.get("/api/place/" + $routeParams.slug).then(function(res){
 $scope.place = res.data;
 });
});
The content can now be rendered in a browser as HTML. Easy as pie!
<div>{{place.fields.name}}</div>
<div>{{place.fields.about}}</div>
<a ng-href="mailto:{{place.fields.email}}">Make enquiry</a>
<a ng-href="{{place.fields.website}}" target="_blank">Website</a>

Dynamic Mapping

The beauty here is that we also have the coordinates for each place stored in Contentful. So we used the formidable mapping solution Mapbox to display a map on the page with a marker showing the location of the place. We simply add to our controller:
$http.get("/api/place/" + $routeParams.slug).then(function(res){
 $scope.place = res.data;
 buildMap();
});
var buildMap = function(){
 L.mapbox.accessToken = "xxxxxxxx";
 var map = L.mapbox.map("map", "xxxxxxxx" {
 minZoom: 11,
 zoomControl: false,
 attributionControl: false
 }).setView([53.80451586014786, -1.5477633476257324], 15, {
 pan: { animate: true },
 zoom: { animate: true } 
 });
 L.mapbox.featureLayer({
 type: "Feature",
 geometry: {
 type: "Point",
 coordinates: [$scope.place.fields.location.lon, $scope.place.fields.location.lat]
 },
 properties: { "title": $scope.place.fields.name }
 }).addTo(map);
}
We took this one step further and added a button to our map, which would allow the user to see their current location in relation to the place (if they’re in the city area). This is where HTML geolocation came in handy. Firstly, we ask the user to share their location when they visit the site.
if (navigator.geolocation) {
 navigator.geolocation.getCurrentPosition(function(position){ 
 $rootScope.geolocation = position;
 });
}
Then we add our HTML button to the map.
<a href="" ng-click="showMe()"></a>
And simply add another feature layer to the map when the button is clicked, showing a marker where the user is.
$scope.showMe = function(){
 L.mapbox.featureLayer({
 type: "Feature",
 geometry: {
 type: "Point",
 coordinates: [$rootScope.geolocation.coords.longitude, $rootScope.geolocation.coords.latitude]
 },
 properties: { "title": "You are here" }
 });
});
Of course there’s a bit more to it than that. We actually handled all map related processing in an Angular service and added some additional features such as zooming the map to fit both markers within the canvas when the ‘show me’ button is clicked.

Location Based Suggestions

During our R&D phase, we stumbled across a variety of city guide websites, all doing similar things. If you visit one of these sites looking for the best local burger place, you’ll most likely also find a ‘suggested places’ box filled with alternative burger places or relatively useless ‘author’s picks’. We thought we could do better. What if we suggested other nearby places? The user would look up the burger place and also get suggestions for shops and bars within a short walk. Now we’re talking! You could easily plan your whole afternoon or evening in the city.
This is where search parameters of the Contentful API had their time to shine. The clever boys and girls at Contentful built location based search into their content delivery API, with options to find ‘entries near me’, ‘search within bounding rectangle’ or ‘search within bounding circle’.
So to find nearby places, we simply add to our Angular controller:
$http.get("/api/place/" + $routeParams.slug).then(function(res){
 $scope.place = res.data;
 buildMap();
 nearbyPlaces();
});
var nearbyPlaces = function(){
 $http.post("/api/place/nearby", {
 lat: $scope.place.location.lat, 
 lon: $scope.place.location.lon
 }).then(function(res){ 
 $scope.nearbyPlaces = res.data;
 });
}
Then to request the search through our NodeJS server:
app("/api/place/nearby", function(req, res){
 contentful.entries({
 "content_type": "xxxxxxxx",
 "fields.location[near]": req.body.lat + "," + req.body.lon,
 "limit": 8
 }, function(err, entries){
 res.send(entries);
 });
});
Once again the results can be easily rendered in a browser using Angular. For groups of data like this, we often opted to use ngRepeat.
<a ng-repeat="nearbyPlace in nearbyPlaces" ng-href="/places/{{nearbyPlace.fields.slug}}">
 <span>{{nearbyPlace.fields.name}}</span>
 <span>{{nearbyPlace.fields.description}}</span>
</a>

That’s All Folks!

So there you have it! We still have a long road ahead for improving the functionality on Leeds Living and developing it further. We want to introduce more location features such as a ‘show places near me’ button and site filtering such as ‘show restaurants open near me’. And it’s not just places that use the location data. Some of our articles also use the map and show related places based on relevance or location.
Ultimately we’ve opened the flood gates here and the possibilities are endless. We think this is the future for city guides and with the help of well supported tools such as Contentful and Mapbox, we can make this possible.

Post a Comment

[disqus][blogger][facebook]

Afrogalaxy

Contact Form

Name

Email *

Message *

Powered by Blogger.
Javascript DisablePlease Enable Javascript To See All Widget