Hands On

Mapping Your Data with the GPU using Harp.gl and Vue

By Nic Raboy | 20 August 2019

I recently wrote about using harp.gl along with the Angular framework to visualize potentially large amounts location data on a map. In case you were unfamiliar, harp.gl is a new open source 3D map renderer that leverages the GPU through WebGL, three.js, and TypeScript.

Per popular request, I decided to reproduce the tutorial that I had previously written, but this time with Vue.js, another popular framework.

To get an idea of what we want to accomplish, take a look at the following animated image:

vue-harp-example

The goal is to render our map tiles, and show points at certain locations. It may sound very familiar to you if you’ve ever worked with the standard HERE interactive map APIs, or Leaflet, or something similar, but behind the scenes harp.gl is using a much more powerful engine that can be incredibly useful in certain scenarios.

Create a New Project with the Vue CLI

To be successful with this project you’ll need the Vue CLI. It isn’t a requirement to use Vue, but it will add the boilerplate code necessary to be successful with this particular example.

From the CLI, execute the following:

vue create harp-vue-project

The above command may ask several questions in regards to the project configuration. Since this is a simple example, the defaults work fine.

With the project created, we need to import the harp.gl dependencies. For this example, we’ll use the browser libraries and explore other options in a future tutorial. Open the project’s public/index.html file and include the following:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <link rel="icon" href="<%= BASE_URL %>favicon.ico">
        <title>harp</title>
    </head>
    <body>
        <noscript>
            <strong>We're sorry but harp doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
        </noscript>
        <div id="app"></div>
        <!-- built files will be auto injected -->
        <script src="https://unpkg.com/three/build/three.min.js"></script>
        <script src="https://unpkg.com/@here/harp.gl/dist/harp.js"></script>
    </body>
</html>

Notice that we’ve included two different <script> tags towards the bottom of our HTML markup. There is one more dependency that we’ll be using, and while it isn’t a requirement, it will make our lives a little bit easier.

From the CLI, execute the following:

npm install geojson --save

When the time comes to visualize points on the map, we’ll need to be using GeoJSON formatted point data. This can be created manually since GeoJSON is just JSON that follows a certain specification, or we can use a library to do this for us. We’re choosing to use a library that will do this for us.

Creating a Harp.gl Map Component for the Project

When working with Vue, it is a good idea to create separate components for the functionality within the application. This keeps the code clean and allows for the components to be reused. We’re going to create a component for all of our map functionality.

Create a src/components/HarpMap.vue file within the project and add the following boilerplate code:

<template>
    <div>
        <canvas ref="map" style="width: 100vw; height: 100vh"></canvas>
    </div>
</template>

<script>
    import GeoJSON from "geojson";
    export default {
        name: "HarpMap",
        props: {
            token: String,
            lat: String,
            lng: String
        },
        mounted() { },
        methods: {
            createPoints(positions) { },
            dropPoints(name, positions) { }
        }
    }
</script>

<style scoped></style>

Let’s break down what’s happening in the above code before we continue.

Inside the <template> block we have a <canvas> tag with style information that is set to take up the full space of the viewport. What’s most important to us is the ref attribute because it is how we can gain access to the component in Vue.

Inside the <script> block, we define our class information.

We import the GeoJSON package, define a few methods, and define our possible tag properties. The tag properties are the attributes that are allowed when using the <HarpMap> tag. We’ll allow the user to provide token information, and center point information.

Now let’s take a look at displaying a map when this component is used. We can do that within the mounted method which is a Vue life-cycle event that triggers after the application has rendered.

mounted() {
    this.map = new harp.MapView({
        canvas: this.$refs.map,
        theme: "https://unpkg.com/@here/harp-map-theme@latest/resources/berlin_tilezen_night_reduced.json",
    });
    const controls = new harp.MapControls(this.map);
    const omvDataSource = new harp.OmvDataSource({
        baseUrl: "https://xyz.api.here.com/tiles/herebase.02",
        apiFormat: harp.APIFormat.XYZOMV,
        styleSetName: "tilezen",
        authenticationCode: this.token,
    });
    this.map.addDataSource(omvDataSource);
    this.map.setCameraGeolocationAndZoom(new harp.GeoCoordinates(Number(this.lat), Number(this.lng)), 12);
},

In the mounted method, the reference to the <canvas> tag is obtained and used on a new map. A new data source is also defined, and it uses a HERE XYZ access token. This token can be obtained through the HERE XYZ CLI or through the HERE XYZ Token Manager.

Finally, the data source is added to the map and the map is centered on the latitude and longitude coordinates that were passed by the user. During the map configuration, the theme of the map can be defined as well as the tiles source.

Using the Harp.gl Vue Component

At this point in time the Vue component is ready for use, even if we’re not quite done with it.

Open the project’s src/App.vue file and include the following:

<template>
    <div id="app">
        <HarpMap 
            token="XYZ_ACCESS_TOKEN" 
            lat="37.7397" 
            lng="-121.4252" />
    </div>
</template>

<script>
    import HarpMap from './components/HarpMap.vue'

    export default {
        name: 'app',
        components: {
            HarpMap
        }
    }
</script>

<style>
    body {
        margin: 0;
    }
</style>

In the above markup, the HarpMap component was imported and it was used as a tag in the <template> block. Running the application, assuming the access token is provided, should show an interactive harp.gl map. As of right now there are no map objects.

Including Visual Points on the Harp.gl Map

When it comes to harp.gl, markers are known as points. The process of adding a point is similar to adding the initial data source which is the map tiles. We’ll be providing the data source, which is point data, and then styling each element in that data.

We’re going to break this down into two functions, starting with the createPoints function:

createPoints(positions) {
    return GeoJSON.parse(positions, { Point: ["lat", "lng"] });
},

Remember that GeoJSON package? We’re using it to take an array of latitude and longitude positions and convert them into a GeoJSON feature set.

The next step is to draw those points to the map. We can look at the dropPoints method for this:

dropPoints(name, positions) {
    const geoJsonDataProvider = new harp.GeoJsonDataProvider(name, this.createPoints(positions));
    const geoJsonDataSource = new harp.OmvDataSource({
        dataProvider: geoJsonDataProvider,
        name: name
    });
    this.map.addDataSource(geoJsonDataSource).then(() => {
        const styles = [{
            when: "$geometryType == 'point'",
            technique: "circles",
            renderOrder: 1,
            attr: {
                color: "#7ED321",
                size: 15
            }
        }];
        geoJsonDataSource.setStyleSet(styles);
        this.map.update();
    });
}

A name and GeoJSON feature set is provided as the data source. This acts as a layer on the map. When adding the data source to the map, style information is defined which says that each item in the data source should be rendered as a green circle.

This method can be used in the mounted method if we want to show points when the map loads:

this.dropPoints("example-points", [{ lat: 37.7497, lng: -121.4252 }, { lat: 37.7597, lng: -121.4352 }]);

Of course we could be providing many points or we could be designing the application to show the data differently. However, what we did is a good starting point to becoming familiar with the harp.gl process.

Conclusion

You just saw how to use harp.gl in a Vue web application. Like previously mentioned, harp.gl can be a powerful tool if you need to visualize a lot of data or complex data because it leverages the GPU of the client. If you’re more of an Angular fan, check out my previous tutorial which uses Angular instead.