Hands on

Searching for Points of Interest with the HERE Places API in a Vue.js Application

By Nic Raboy | 18 October 2018

When I’m looking at a map, I’m typically using it for two reasons. I’m either trying to navigate between two points or I’m trying to find a certain point of interest on the map. HERE provides an API for finding nearby places using REST and JavaScript, but what if you wanted to use a modern framework? Previously I had written a tutorial titled, Displaying Places on a HERE Map in an Angular Web Application which focused on Angular, but this time around we’re going to focus on Vue.js.

In this tutorial, we’re going to see how to search for nearby places using the HERE Places API for JavaScript and display those points of interest on a map using the Vue.js JavaScript framework.

The following image demonstrates what we hope to accomplish in this tutorial:

here-places-vuejs

We’re going to display a map with a search box. The user will be able to search for a place and all matches will be displayed on the map as markers. When a marker is clicked, the address information will be displayed as an information bubble.

The Requirements

To build an application with the HERE Places API, a few prerequisites must be met:

  1. The Vue CLI must be installed and configured.
  2. A HERE developer account must be available for the appropriate application tokens.

While we won’t be doing anything intense in this tutorial when it comes to Vue.js, and the Vue CLI isn’t a requirement for creating Vue.js applications, it will make our lives easier.

When working with the HERE Location Services (HLS), an app id and app code is required. These can be obtained after creating a free developer account.

Bootstrapping a Vue Application with a Mapping Component

This isn’t the first time that I wrote about Vue.js and HERE. In an effort to save me from re-explaining things that I’ve done in other tutorials, we’re going to bootstrap our application with the essentials and move beyond. If you’d like to see how to create a map component in Vue.js, I suggest checking out the tutorial I wrote titled, Showing a HERE Map with the Vue.js JavaScript Framework. If you want to learn about adding markers to your map, you can check out my tutorial titled, Dropping Markers on Geocoded Locations within a Vue.js Web Application.

To get us up to speed, lets quickly create a project and add the code from those tutorials. From the Vue CLI, execute the following:

vue create here-places-project

The above command will use the Vue CLI wizard to create a project. Because this project is simple, we’re going to use the defaults to the questions asked by the wizard. This means we won’t be using Vuex or a Vue Router.

The next step is to include the HERE JavaScript libraries. Open the project’s public/index.html file and include the following HTML markup:

<!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>HERE Maps with Vue</title>
        <link rel="stylesheet" type="text/css" href="https://js.api.here.com/v3/3.0/mapsjs-ui.css?dp-version=1533195059" />
    </head>
    <body>
        <noscript>
            <strong>We're sorry but vue-map 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://js.api.here.com/v3/3.0/mapsjs-core.js" type="text/javascript" charset="utf-8"></script>
        <script src="https://js.api.here.com/v3/3.0/mapsjs-service.js" type="text/javascript" charset="utf-8"></script>
        <script src="https://js.api.here.com/v3/3.0/mapsjs-places.js" type="text/javascript" charset="utf-8"></script>
        <script src="https://js.api.here.com/v3/3.0/mapsjs-mapevents.js" type="text/javascript" charset="utf-8"></script>
        <script src="https://js.api.here.com/v3/3.0/mapsjs-ui.js" type="text/javascript" charset="utf-8"></script>
    </body>
</html>

Essentially, we’ve included a bunch of scripts and a stylesheet. This will give us access to maps, core APIs, and an interactive map user interface. With the libraries in place, let’s get our map component ready. Create a src/components/HereMap.vue file within your project that contains the following:

<template>
    <div class="here-map">
        <div ref="map" v-bind:style="{ width: width, height: height }"></div>
    </div>
</template>

<script>
    export default {
        name: "HereMap",
        data() {
            return {
                map: {},
                platform: {},
            }
        },
        props: {
            appId: String,
            appCode: String,
            lat: String,
            lng: String,
            width: String,
            height: String
        },
        created() {
            this.platform = new H.service.Platform({
                "app_id": this.appId,
                "app_code": this.appCode
            });
        },
        mounted() {
            var defaultLayers = this.platform.createDefaultLayers();
            this.map = new H.Map(
                this.$refs.map,
                defaultLayers.normal.map,
                {
                    zoom: 10,
                    center: { lng: this.lng, lat: this.lat }
                }
            );
        },
        methods: { }
    }
</script>

<style scoped></style>

The above code gets us up to speed when it comes to our component in relation to the other tutorials. We create a placeholder UI component, initialize a few variables, define a few possible component properties, initialize HERE, and center the map.

To use the component, the project’s src/App.vue file should look like the following:

<template>
    <div id="app">
        <HereMap ref="map" appId="APP-ID-HERE" appCode="APP-CODE-HERE" lat="37.7397" lng="-121.4252" width="100%" height="800px" />
    </div>
</template>

<script>
    import HereMap from "./components/HereMap.vue"

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

<style>
    #app {
        font-family: 'Avenir', Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: left;
        color: #2c3e50;
    }
</style>

Using the properties that were defined in the component, we can make use of the map. This sums up everything that was done in my tutorial titled, Showing a HERE Map with the Vue.js JavaScript Framework, but without the depth of explanations.

We can now focus on actually using the HERE Places API.

Searching for Points of Interest with the HERE Places API for JavaScript

Before we can start searching for places, we need to initialize it within our application. Open the project’s src/components/HereMap.vue and alter the project’s data and created methods:

data() {
    return {
        map: {},
        platform: {},
        ui: {},
        search: {}
    }
},
created() {
    this.platform = new H.service.Platform({
        "app_id": this.appId,
        "app_code": this.appCode
    });
    this.search = new H.places.Search(this.platform.getPlacesService());
},

In the data method we are initializing a ui variable and search variable. These will be used throughout our component. In the created method we are setting the search variable using the new places service.

It makes sense to create a places method now for searching. In the methods object, include the following:

methods: {
    places(query) {
        this.map.removeObjects(this.map.getObjects());
        this.search.request({ "q": query, "at": this.lat + "," + this.lng }, {}, data => {
            for(var i = 0; i < data.results.items.length; i++) {
                // Marker logic here
            }
        }, error => {
            console.error(error);
        });
    },
}

When the places method is called, we are passing a query string. To allow multiple searches, we are going to clear the map every time the method is called. When the search is performed, the query string is used along with the latitude and longitude of the centered map.

The response from the search should be coordinates which we’ll later use for placing markers.

Now that we’re able to search, we need to configure search in the parent component. Open the project’s src/App.vue file and make some changes, like so:

<template>
    <div id="app">
        <input type="text" v-model="query" />
        <button type="button" v-on:click="search()">Search</button>
        <HereMap ref="map" appId="APP-ID-HERE" appCode="APP-CODE-HERE" lat="37.7397" lng="-121.4252" width="100%" height="800px" />
    </div>
</template>

<script>
    import HereMap from "./components/HereMap.vue"

    export default {
        name: 'app',
        components: {
            HereMap
        },
        data() {
            return {
                query: "Starbucks"
            }
        },
        methods: {
            search() {
                this.$refs.map.places(this.query);
            }
        }
    }
</script>

<style>
    #app {
        font-family: 'Avenir', Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: left;
        color: #2c3e50;
    }
</style>

A few things have changed. First look in the <template> block. We have an input field bound to a class variable and a button bound to a method in the class. If we look into the class, we initialize our query variable that is bound and define our search method. There are numerous ways to interact with child components, but for this example we’ll use the ref attribute. Using the ref attribute we can call methods from our component, passing the query string.

If you were to print from the places method, you should see results appearing, but not on the map itself.

Interacting with Places using Markers and Information Bubbles

To bring our application to a close, we’re going to display markers for each of the places found. When clicking those places we’re going to see address information through information bubbles.

Go back into the project’s src/components/HereMap.vue file and change the mounted method:

mounted() {
    var defaultLayers = this.platform.createDefaultLayers();
    this.map = new H.Map(
        this.$refs.map,
        defaultLayers.normal.map,
        {
            zoom: 10,
            center: { lng: this.lng, lat: this.lat }
        }
    );
    var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(this.map));
    this.ui = H.ui.UI.createDefault(this.map, defaultLayers);
},

You’ll notice that we added two lines. The first line for behavior gives us the ability to click on markers. Without it, markers will show, but not display anything on click even if click logic is in place. The second line is for our UI objects which will come in the form of information bubbles.

Now we can set our markers and information bubbles through a method like so:

dropMarker(coordinates, data) {
    var marker = new H.map.Marker(coordinates);
    marker.setData("<p>" + data.title + "<br>" + data.vicinity + "</p>");
    marker.addEventListener('tap', event => {
        var bubble = new H.ui.InfoBubble(event.target.getPosition(), {
            content: event.target.getData()
        });
        this.ui.addBubble(bubble);
    }, false);
    this.map.addObject(marker);
}

The above dropMarker method will appear in the methods object. The coordinates will be a latitude and longitude and the data will be the address information to present. When creating a marker, we are also creating an event listener for click events. The information bubble will be set using the data that was added to the marker. Once the information bubble is created, the marker is added to the map.

With the dropMarker method in place, we can update the places method:

places(query) {
    this.map.removeObjects(this.map.getObjects());
    this.search.request({ "q": query, "at": this.lat + "," + this.lng }, {}, data => {
        for(var i = 0; i < data.results.items.length; i++) {
            this.dropMarker({ "lat": data.results.items[i].position[0], "lng": data.results.items[i].position[1] }, data.results.items[i]);
        }
    }, error => {
        console.error(error);
    });
},

If you do a search now, markers should show up on the map and those markers should have click events associated with them.

Conclusion

You just saw how to use the HERE Places API using JavaScript and the Vue.js framework. Most of the setup was around building a Vue.js component and not so much in the actual searching for points of interest which is great.

If you’re interested in seeing how to search for points of interest with Angular, check out a previous tutorial I wrote on the subject.