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:
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:
- The Vue CLI must be installed and configured.
- 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.