Hands On

Helping Cyclists Stay Safe Using HERE Studio

By Olaf Wysocki | 07 February 2020

Try HERE Maps

Create a free API key to build location-aware apps and services.

Get Started

As part of their young developer outreach, HERE conducts workshops in collaboration with the GIS departments of many universities across Europe. One such workshop took place in my uni, the Technical University of Munich where the we learned how to use HERE Location Services and HERE Studio. Below I am going to describe what my team built using the HERE APIs and how it could help cyclists be extra vigilant while cycling in Berlin.

Besides being a very popular means of transportation across EU, cycling comes with its risks. In the EU there are more than 2000 people who become victim of fatal accidents involving bikes. One of the most dangerous cities in Europe is Berlin where 5191 cyclists were involved in accidents and 11 of them were killed in 2018. I calculated these numbers using a GIS software based on "Unfallatlas" data which is maintained by The Federal Statistical Office.

To showcase how you can use GIS tools to raise awareness about accident prone areas in your city, I have taken Berlin as an example because of the density of accidents, available open data, actions of authorities & citizens. We will look into following analyses:

  • The most dangerous zones in Berlin
  • Spots where accidents have happened at night/twilight/dusk, not in the range of streetlights
  • Accidents in winter on a slippery surface
  • Accidents with vehicles on roads with speed limit >30km/h
  • Accidents per road junction without traffic bike signs and traffic lights

Now let me show you how to share these results to increase awareness among commuters and aid public authorities in their actions using a similar map.


Screen Shot of the final application

Start here: Get your credentials

Yeah, I know this is always this most boring part of the creation of a web map. However, HERE made it quite simple. Just register at developer.here.com and go to your account and generate your JavaScript API key and your REST API key. Copy that to your IDE and… you can forget about it and start your job!


const here = {
   apikey: "Your JS API key",
   apiKey: "Your REST API key"
};

Get access to a freemium data storage solution


First of all, our data has to be accessible via the Internet to make it available for a wide audience. HERE provides a very handy solution for that – HERE Studio Data Hub. Here you can store lots of data and query it for free (to some, very wide, extend). Now let us go to the Here Studio webpage, set up an account and go to Data Hub.Screen grab of the data hub page

Now you can simply create new space and drag and drop your data. For web browser, GeoJSON files are most suitable, although you can upload shapefiles or CSV as well.For more on GeoJSON, visit Raymond Camden posts.
Our data is stored but now we would like to have access from external environments to those files. How to do that? Go to: xyz.api.here.com/token-ui/index.html There you can generate tokens, which could be understood as keys to access each feature and the whole project itself.Screen grab of token manager page

You can tick all access rights or just select some of them. Do not forget to click generate token for the whole project (it will appear in green bar).

Start building your web map


var map = L.map('map', { boxZoom: false });
var layer = Tangram.leafletLayer({
   scene: 'scene.yaml',

This is an initialization of a map. You can spot very mysterious names like Tangram, Leaflet. Long story short – those are open source libraries (cool!) for JavaScript tailored to modern cartographers' needs. I really recommend checking them and familiarize with their documentation – possibilities of those rendering libraries are just awesome.
In the last line, you can spot the scene: ‘scene.yaml’ which tells to computer ‘get the styling from this file’. Within a scene.yaml file one can prepare very sophisticated visualisations of data. I would like to focus on how we are able to combine our data in Here Studio (our superb warehouse) with a script for visualisation.


sources:
    xyz_osm:
        type: MVT
        url: https://xyz.api.here.com/tiles/osmbase/256/all/{z}/{x}/{y}.mvt
        max_zoom: 16
        url_params:
            access_token: 'ACCESS_TOKEN_TO_THE_PROJECT'

    mostDangerousRoadJunctions:
        url: https://xyz.api.here.com/hub/spaces/HERE_GOES_UNIQUE_FEATURE_ID/tile/web/{z}_{x}_{y}
        type: GeoJSON
        url_params:
            access_token: 'ACCESS_TOKEN_TO_THE_PROJECT'

    slowDown:
        url: https://xyz.api.here.com/hub/spaces/HERE_GOES_UNIQUE_FEATURE_ID /tile/web/{z}_{x}_{y} 
        type: GeoJSON
        url_params:
            access_token: 'ACCESS_TOKEN_TO_THE_PROJECT'

Take a look at HERE_GOES_UNIQUE_FEATURE_ID this is the place where you should put a unique ID from Token manager which we generated before. Besides, you have to provide a token to the project itself (ACCESS_TOKEN_TO_THE_PROJECT). Moreover, we can make use of OpenStreetMap data (again free!) provided by HERE (xyz_osm). Thanks to those operations we have prepared data for further styling like this:


slowDown:
        base: points
        blend: overlay
        blend_order: 362
        texture: img/slowDown.png 

and extend it:


_slowDown:
        data: {source: slowDown}
        filter: { $zoom: { min: 12 } }
        draw:
          points:
            style: slowDown
            size: 36px
            interactive: true  

Nice, we have our data accessible via the Internet. Now what?

The answer is – (almost) everything that you want! Though, we have to focus on one solution which can really help public organizations. Up to now, we presented spots and regions which can help increase awareness of hazards on which cyclists are exposed and probably give a better understanding of dangerous places for authorities. We can go even further and prepare an interactive map for public bodies to analyze the spatial distribution of ambulances in Berlin taking into account real-time traffic data and locations of ambulance stations. Seems interesting? So, take a deep breath and let us go through it!

Calculate ranges for ambulances in Berlin based on real-time traffic data

I really like the idea that some cities provide they data for free (otherwise, how would we go through this tutorial?) but even better is that private companies like HERE share their spatial information freely. That is exactly what we would like to do now – use HERE location services to get the location of ambulance stations within borders of the city.


function addAuthoritiesPlace(){
   let params = {
     "apiKey" :"YOUR REST API KEY",
     "in":  centerBerlin.lat + ',' + centerBerlin.lng +";r=100000",       // meters
     "name": "Ambulance Services",
     "size": "50000"
   }

We are now creating parameters for this query (you can take the apiKey from our first step or paste it here again). The parameter “in” says in what range we would like to search for “places”. In this case, we start in the center of Berlin and analyze radius of 10 000 m. On the other hand, we have also parameter “name” which selects a specific category in which we are interested (this case – Ambulance Services). HERE provides a truly long list of categories that you can check here. “Size” prevents accessing more than x (50 000 in this case) number of features. The complete query:


function addAuthoritiesPlace(){
   let params = {
     "apiKey" :"YOUR REST API KEY",
     "in":  centerBerlin.lat + ',' + centerBerlin.lng +";r=100000",       // meters
     "name": "Ambulance Services",
     "size": "50000"
   }

   let query = Object.keys(params)
              .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
              .join('&')
   let url = 'https://places.ls.hereapi.com/places/v1/browse?' + query
 
 
   fetch(url, {
     "method": "GET"
   })
   .then(response => response.json())
   .then(response => {
     console.log(response)
     for (i=0; i  < response.results.items.length; i++)
       {  

       newpos= {lat: response.results.items[i].position[0], lng: response.results.items[i].position[1]}

       addMarker(newpos)
       
       } 
   })
 }

You have probably spotted the addMarker function call – we are going to talk about definition of that function in a minute. We can also inherit the styling of our features inside our script file using Leaflet. Thanks to that I have my own icon inherited within my map.


var myIcon = L.icon({
   iconUrl: 'icons/ambulance.png',
   iconSize: [38, 38],
   iconAnchor: [22, 94],
   popupAnchor: [-3, -76],
   shadowSize: [68, 95],
   shadowAnchor: [22, 94]
});

What we would like to do as well is adding interactivity to our app. The idea behind is to add markers and on double mouse click fire the calculation of our range. So reachability is constrained to starting position placed in one of our ambulances services selected by user. That is exactly what is triggered by addMarker function. It defines way of adding Ambulance Services to the map with specific custom icon and allows interactivity (certain action) when one of point is double clicked. What kind of “interactivity” is that? Please, go further!


function addMarker(newpos, html){
   ev_marker = L.marker([newpos.lat, newpos.lng], {icon: myIcon})
   ev_marker.addTo(map).on('dblclick', onDblClick)
 }

At this point we have to use our JavaScript apikey. Routing service is one of many HERE APIs providing interactivity to our solution. In this case it allows us to check maximal range for ambulances from ambulances services points. First, we have to call the API.


var platform = new H.service.Platform({ apikey: here.apikey });
//
var router = platform.getRoutingService();

Now we can start constructing the request. Do you still remember? We wanted to have our range on double click but only calculating possible ranges from the ambulance’s locations. That is why we are using positions (lat, lng) from events (double click on our feature). We can set up mode, in other words – what type of route we would like to take (shortest/fastest) and what type of vehicle do we use (or maybe just walk?). So we say take the fastest road, use a car (because we want to help ASAP and we have an ambulance), but still, we have to watch out for traffic (traffic:enabled). The traffic is real-time data at the moment of firing the query(!). Moreover, we would like to say: “restrict the time of arrival to 15 min" because if it is not within this time range, we will take other ambulance” (‘range’: ’900’ means 15 min and ‘rangetype’ specifies ‘time’ as our restriction).


function onDblClick(e) {
   //if feature clicked twice - > fire routing
   //routing parameters
   
   var myStart = e.latlng.lat + ',' + e.latlng.lng
   var routingParams = {
   'mode': 'fastest;car;traffic:enabled',
   'start': myStart,
   'range': '900', // 15 (15x60secs) minutes of driving 
   'rangetype': 'time'
}

Based on that we will get the spatial range controlled by those parameters. So-called isoline routing (for more on Isoline Routing service) allows us to proceed with maximum ranges and we are able to draw it on the map. Take a look at the full part:


var onResult = function(result) {
   var center = new H.geo.Point(
     result.response.center.latitude,
     result.response.center.longitude),
   isolineCoords = result.response.isoline[0].component[0].shape,
   //isolinePolygon,
   isolineCenter
 

   // Create a polygon and a marker representing the isoline:
   //43.2323, 12.2312, 44.32112, 13.13213 --> [[43.2323, 12.2312], [44.32112, 13.13213],...]
   var list1 = isolineCoords.toString()
   var string = list1.split(',');

   // Create array of float for each pair of coordinate
   var a = string.length;
   for (i = 0; i < a; i++) {
      string[i] = parseFloat(string[i]);
   }

   // Initialize an array to store the new values
   var b = string.length / 2;
   var array = [];
   for (i = 0; i < b; i++) {
      array[i] = [0, 0];
   }

   // Create an array of array of coordinates
   var k = 0;
   for (i = 0; i < b; i++) {
      for (j = 0; j < 2; j++) {
         array[i][j] = string[k];
         k++;
      }
   }
   
   // Add the polygon to the map & remove when 1 already exist:
   
   if (isolinePolygon != undefined) {
      map.removeLayer(isolinePolygon);
   };

   isolinePolygon = L.polygon(array)
   isolinePolygon.addTo(map)

   map.fitBounds(isolinePolygon.getBounds());
   //map.setView([isolinePolygon.getCenter().lat, isolinePolygon.getCenter().lng], map.getZoom());
   
   var isolinePopup = L.popup({className: 'customIso', closeButton: false});
   isolinePopup

   .setLatLng([isolinePolygon.getCenter().lat, isolinePolygon.getCenter().lng])
   .setContent('</p ><p>How far can I get in 15 min?')
   .openOn(map); 
    
 }

 // Call the Routing API to calculate an isoline:

 function onDblClick(e) {
   //alert(e.latlng.lat + ',' + e.latlng.lng);
   //if feature clicked twice - > fire routing
   //routing parameters
   
   var myStart = e.latlng.lat + ',' + e.latlng.lng
   var routingParams = {
   'mode': 'fastest;car;traffic:enabled',
   'start': myStart,
   'range': '900', // 15 (15x60secs) minutes of driving 
   'rangetype': 'time'
}

   router.calculateIsoline(
      routingParams,
      onResult,
      function(error) {
      alert(error.message)
      }
    );

}

Conclusion

To recreate this for your city, check out also my GitHub repository and the cyclist related project.