Hands On

Displaying Public Transport Stations in the Entire World

By Shruti Kuber | 29 July 2020

Try HERE Maps

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

Get Started

In the previous blog post about HERE Advanced Map Data Sets, we talked about what they are and where to find them. In this post, let's explore how to use them.

One of the easiest ways to start using these data sets is to display the data on your map. The JS API 3.1 has a wrapper which can help you do just that. But where do you start?

Select a layer

As a refresher from the last blog post, we will now get a list of all the layers that are available through the Advanced Map Data Sets API.


  GET  https://s.fleet.ls.hereapi.com/1/doc/layers.json?apiKey={{YOUR APIKEY}}

On sending this call, we will get a long list of available layers and their attributes. What you want to look at are the objects name, type and featureMapping.


    ...
    {
        "name": "PUBLIC_TRANSPORT_POI",
        "type": "geom",
        "tileLevel": 13,
        "attributes": [
            "PLACE_ID",
            "NAMES",
            "LINK_ID",
            "CAT_ID",
            "CAT_NAME",
            "DISPLAY_LAT",
            "DISPLAY_LON",
            "SIDE_OF_STREET",
            "PERCENT_FROM_REFNODE",
            "POI_ID",
            "LAT",
            "LON"
        ],
        "featureMapping": "PDE-Base"
    },
    {
        "name": "LOADING_ZONE_POI",
        "type": "geom",
        "tileLevel": 13,
        "attributes": [
            "POI_ID",
            "CAT_ID",
            "NAME",
            "CHAIN_ID",
            "LINK_ID",
            "SIDE",
            "LAT",
            "LON"
        ],
        "featureMapping": "PDE-Premium-Truck-POI"
    },
    {
        "name": "ROAD_NAME_FC1",
        "type": "attr",
        "tileLevel": 9,
        "attributes": [
            "LINK_ID",
            "NAMES"
        ],
        "featureMapping": "PDE-Base"
    },
    ...

The name will give you the layer name to use in the next step. The type will either be geom- Geometry or attr- Attributes. With the JS wrapper, you should use the geometry layers. The featureMapping object tells you if the layer is available with the freemium account. I have selected the PUBLIC_TRANSPORT_POI layer for this example.

Get layer details

This and the previous step should be implemented only once. You won't need them in your main application, however I wanted to give you an idea of what information these datasets can give you. Once you have selected your layer, you need to get a few more details about it. To get this information, we will query the layer with the layer name.


    GET https://s.fleet.ls.hereapi.com/1/doc/layer.json?layer=PUBLIC_TRANSPORT_POI&apiKey={{YOUR APIKEY}}

What you get is the description of the attributes in this layer, tileRequestsLevel, tileX and tileY. The tile system is a way to divide the location data in the world into chunks. The lower the level, fewer the number of chunks, larger the area these chunks cover. To make it simple, the tileRequestsLevel will give you the minimum zoom level of the map at which the data is available. The tileX and tileY values are the extent of the data tile. You can read about this in detail here. Meanwhile, let's use these values to display the data set.


    {"description":"Layer contains Public Tranport POIs.","attributes":{"PLACE_ID":"The Place ID is an identifier for a Place record. For published (blended) records, the Place ID is a unique identifier. For source records, the Place ID may or may not be unique as it can be provided by various suppliers. The Place ID contains a number of alphanumeric characters and hyphens.
NULLABLE in case the POI has POI_ID","NAMES":"List of all names for this object, in all languages, latin1/pinyin/phonetic transliterations.
For convenience, non-exonym base names are listed first.
Format:
NAMES = NAME1 \u001D NAME2 \u001D NAME3 ...
NAME = NAME_TEXT \u001E TRANSLIT1 ; TRANSLIT2 ; ... \u001E PHONEME1 ; PHONEME2 ; ...
NAME_TEXT = LANGUAGE_CODE NAME_TYPE IS_EXONYM text
TRANSLIT = LANGUAGE_CODE text
PHONEME = LANGUAGE_CODE IS_PREFERRED text
LANGUAGE_CODE is a 3 character string
NAME_TYPE is one letter (A = abbreviation, B = base name, E = exonym, K = shortened name, S = synonym)
IS_EXONYM = Y if the name is a translation into another language
IS_PREFERRED = Y if this is the preferred phoneme.

Please note, the delimiters are:
\u001D between languages (NAMES level)
\u001E between name text, transliterations, and phonemes
';' between different transliterations and phonemes of the same name.
Language codes:
ALB : ALBANIAN
AMT : ARMENIAN TRANSCRIBED
...
WEL : WELSH
WEN : WORLD ENGLISH

Language codes of transliterations:
ARX : ARMENIAN TRANSLITERATION
ASX : ASSAMESE TRANSLITERATION
AZX : Azeri Transliteration
...
UKX : UKRAINIAN TRANSLITERATION
VIX : VIETNAMESE TRANSLITERATION
","LINK_ID":"Permanent link ID. Positive 64 bit Integer that globally identifies the road, carto or building footprint link, also across map releases. Link IDs are never reused.","CAT_ID":"Category ID of POI. Values:
4013 - Train Station
400-4000-4581 - Airport
...
400-4100-0348 - Bicycle Parking
800-8500-0179 - Park and Ride","CAT_NAME":"Category Name","DISPLAY_LAT":"Latitude of the display position for the POI. Only published if it is different to LAT.
NULLABLE.","DISPLAY_LON":"Longitude of the display position for the POI. Only published if it is different to LON.
NULLABLE.","SIDE_OF_STREET":"Identifies the side of the street on which the POI is located.
NULLABLE
Values:
L - LEFT
R - RIGHT
N - NEITHER","PERCENT_FROM_REFNODE":"The location of a POI on a link (in terms of percentage from the reference node). -1 indicates that a Percent From Reference Node is unavailable for the POI
NULLABLE","POI_ID":"A permanent identifier that is unique across all published Core POIs.
NULLABLE in case the POI has a PLACE_ID","LAT":"Latitude coordinates [10^-5 degree WGS84] for the POI Coordinate of the location.","LON":"Longitude coordinates [10^-5 degree WGS84] for the POI Coordinate of the location."},"referencedStaticContents":[],"tileRequestsLevel":13,"tileX":7481,"tileY":5358,"isStaticContent":false}

Render the basic map

To display the data layer on the map, we first need to render the map. Let's use the boilerplate JS code for rendering a web map. Do not forget to add your 'APIKEY'. Once we have the map set up, let's get our datasets fired up.

Displaying datasets

First things first. To use any of these layers, you need to get an instance of the HERE Platform Data Extension service. Next, we are going to create a Fleet Telematics Advanced Data Sets provider with the 'PUBLIC_TRANSPORT_POI' thematic layer, and render markers to display the layer. For doing this, we are going to use the layer name and the level which we noted in step 2.


    var service = platform.getPlatformDataService();

    const svg = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="21px" height="21px" viewBox="-0.5 -0.5 21 21"><defs/><g><ellipse cx="10" cy="10" rx="5" ry="5" fill="#FAB800" stroke="#000000" pointer-events="none"/></g></svg>`;
    const icon = new H.map.Icon(svg);

    
    // create tile provider and layer that displays all public transport station POIs 

    var poiProvider = new H.service.extension.platformData.TileProvider(service,
    {
        layer: 'PUBLIC_TRANSPORT_POI', 
        level: 13
    }, 
    {
        resultType: 
            H.service.extension.platformData.TileProvider.ResultType.MARKER,
            styleCallback(data) {
                return icon;
            }
    });
    var poiPoints = new H.map.layer.MarkerTileLayer(poiProvider);
    map.addLayer(poiPoints);

Now, we will add a tap event on these markers to display useful information. For this, I have selected the NAMES and CAT_NAME attributes of the layer as seen in step 2.


    // add events listener, that outputs data provided by the Platform Data Extension and
    // associated with the H.map.Marker
    poiProvider.addEventListener('tap', function(ev) {
        if(ev.target instanceof H.map.Marker){
            var markerData = ev.target.getData();
            // Create an info bubble object at a specific geographic location:
            // console.log(markerData)

            let names = markerData.getCell('NAMES').split('DEUBN');

            var bubble = new H.ui.InfoBubble(ev.target.getGeometry(), {
                content: 
                    `<div><h3>Name: </h3></div>`+
                    names[1]+
                    `<div><h3>Category: </h3></div>`+
                    markerData.getCell('CAT_NAME')
            });

            // Add info bubble to the UI:
            ui.addBubble(bubble);
        }
    });

In this way, you will have displayed ALL the public transport points of interest in the WORLD instead of querying 100 at a time with the HERE Geocoding and Search API. Of course, I wouldn't recommend displaying geometries for the entire map in every application. The recommended way to use the HERE Fleet Telematics Advanced Data Sets API is the tile interface, with caching on the client side. In the next post about Advanced Maps Data Sets, I will talk about selectively displaying datasets for certain areas. In the meantime, check out Sayna's post about improving your address data with HERE Geocoding.