Hands On

HERE Studio and Maps API for JavaScript

By Raymond Camden | 06 March 2020

A few months ago we released support for working with HERE Studio and our client-side JavaScript maps library. While it was previously possible to view your HERE Studio data in any form you desired, we wanted to make it even easier for folks using our mapping library to load in HERE Studio features. In this post I'm going to share two simple examples of how this can be done in your own projects. For my testing, I'm using a large Studio space with a few hundred features based on the American National Park system. You can see a Studio version of the data right here.

Alright, so let's get started. I'm going to begin with a map centered on America with no other data. 

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Test XYZ</title>
    <meta name="description" content="" />
    <meta name="viewport" content="initial-scale=1.0, width=device-width" />
    <script src="https://js.api.here.com/v3/3.1/mapsjs-core.js" type="text/javascript" charset="utf-8"></script>
    <script src="https://js.api.here.com/v3/3.1/mapsjs-service.js" type="text/javascript" charset="utf-8"></script>
    <script src="https://js.api.here.com/v3/3.1/mapsjs-mapevents.js" type="text/javascript" charset="utf-8"></script>
    <script src="https://js.api.here.com/v3/3.1/mapsjs-ui.js" type="text/javascript" charset="utf-8"></script>
    <link rel="stylesheet" type="text/css" href="https://js.api.here.com/v3/3.1/mapsjs-ui.css" />
    <style>
      #mapContainer {
		width: 750px;
		height: 700px;
      }
    </style>
  </head>
  <body>
    <div id="mapContainer"></div>

    <script>
	const KEY = "c1LJuR0Bl2y02PefaQ2d8PvPnBKEN8KdhAOFYR_Bgmw";
	var platform = new H.service.Platform({
		apikey: KEY
	});

	// Obtain the default map types from the platform object:
	var defaultLayers = platform.createDefaultLayers();

	var map = new H.Map(
		document.getElementById('mapContainer'),
		defaultLayers.vector.normal.map,
		{
			zoom: 3,
			center: { lat: 38.7984, lng: -96.3944 },
			pixelRatio: window.devicePixelRatio || 1
		}
	);

	var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));

	// Create the default UI:
	var ui = H.ui.UI.createDefault(map, defaultLayers);

	</script>
  </body>
</html>

Taking it from the top, we begin by loading in libraries for core MapJS functionality as well as events, services, and UI support. A map is created centered on, roughly, the middle of America, and default UI and behavior is added. This is how we get things like click to pan and mouse wheel zooming. I'll be sharing links to all of the code at the end, but for now you can test this here: https://cfjedimaster.github.io/heredemos/mapsjs/test0_xyz.html

Alright, now let's start adding in our HERE Studio data. I began by using our docs (Display an HERE Studio space on the Map) and then added this code:


const XYZ_TOKEN = 'AFJj375eSbmD0w3reC4aGQA';
const service = platform.getXYZService({
	token: XYZ_TOKEN,
});

const XYZ_SPACE = '73KBVoD8';
const mySpaceProvider = new H.service.xyz.Provider(service, XYZ_SPACE, {
});

const mySpaceLayer = new H.map.layer.TileLayer(mySpaceProvider);
// add a layer to the map
map.addLayer(mySpaceLayer);

The token above was generated using our online token tool and set to allow read only access for my space.  I then define a new "XYZService", specify my space, and create the provider and layer. Finally, I just add it to my map. You can see the result of this here: https://cfjedimaster.github.io/heredemos/mapsjs/test1_xyz.html

This nicely shows my data, but doesn't actually tell me what the data represents! For that, let's modify the code to add a click event. Whenever the user clicks on a feature we'll display a bubble showing all of the feature data. For this update I want to thank my coworker Mujammil Ansari. Here's the modification. 

mySpaceProvider.getStyle().setInteractive(['xyz'], true);

let bubble;
// Add 'tap' event listener, that opens info bubble
mySpaceProvider.addEventListener('tap', function (evt) {
	let position = map.screenToGeo(evt.currentPointer.viewportX, evt.currentPointer.viewportY),
		data = evt.target.getData(),
		rows = Object.keys(data.properties)
		.map((key) => `${key}:${data.properties[key]}`);

	if (!bubble) {
		bubble = new H.ui.InfoBubble(position, { content: '' })
		ui.addBubble(bubble);
	}
	bubble.setContent(`
	<div style="max-height:300px; overflow:auto">
	<table id="bubbleInfo" style="font-size:10px">
	${rows.join('')}
	</table>
	</div>`);

	bubble.setPosition(position);
	bubble.open();

	map.setCenter(position, true);	
});

The modification begins by adding interactivity to the Studio information on the map. Then it adds a tap handler. The tap handler handles defining a bubble as well as an HTML block that gets all of the properties from the feature. Finally this bubble is opened and centered on the map. The result may be a bit different you expect:

As you can see, your features include properties that HERE Studio adds itself as part of it's data management process. Before we discuss how to fix this, you can view this version here: https://cfjedimaster.github.io/heredemos/mapsjs/test2_xyz.html

Alright, so how do we make the card a bit nicer? One way would be to write code to hide properties like "id" and any that include "@ns:com.here.xyz". While that would work, we still wouldn't have control over the HTML. Since we actually know our data and know it includes a Code and Name value, we can hard code the bubble and make it a bit nicer:

mySpaceProvider.addEventListener('tap', function (evt) {
	let position = map.screenToGeo(evt.currentPointer.viewportX, evt.currentPointer.viewportY),
		data = evt.target.getData();
				
	if (!bubble) {
		bubble = new H.ui.InfoBubble(position, { content: '' })
		ui.addBubble(bubble);
	}
	bubble.setContent(`
	<div style="max-height:300px; overflow:auto;width: 300px;">
	<h2>${data.properties['properties.Name']}</h2>
	Code: ${data.properties['properties.Code']}
	</div>`);

	bubble.setPosition(position);
	bubble.open();

	map.setCenter(position, true);	
});

The change here is primarily to the content of the bubble. I've set a width and hard coded it to assume properties.Name and Code. The result is a bit nicer:

You can test this version here: https://cfjedimaster.github.io/heredemos/mapsjs/test3_xyz.html

If you want to play with the code for these demos, you can find them (along with other simple demos) at my repository here: https://github.com/cfjedimaster/heredemos