Show or Hide Markers Based on Zoom Level

FdR
The Startup
Published in
5 min readDec 30, 2020

--

Image by TheAndrasBarta from Pixabay

The task is pretty much all explained in the title.

I structured the code with a parent component (the view) which holds the child component (the map).

Here is the map component:

Let’s break our work into little steps so it will be easier:

1.init the map
2.detect when the zoom level changes
3.get the data for the markers
4.create the google maps markers
5.create function to show the markers
6.create function to hide them

We then need to use them correctly in the vue.js hooks.

1.Init Map:

initMap() {  const myLatLng = new window.google.maps.LatLng(39.228458, 9.1167473);  this.gMap = new window.google.maps.Map(this.$refs.map, {    zoom: 13,    center: myLatLng,    gestureHandling: ‘greedy’,    streetViewControl: false,  });  // get stops from api and create markers — wait for showing them  this.emitGetMarkers();}

Nothing special in this block of code, especially if you’re familiar with google maps api.
We just set same basic options for the map. The most relevant for us are:

  • center for centering the map: choose whatever coordinates you may need, important is to create a LatLng google maps class before using them -const myLatLng = new window.google.maps.LatLng(39.228458, 9.1167473);.
    Also notice I had to used window. before google otherwise vue complained about google being undefined. I’m sure there’s another way to go over this issue, this time I chose this one.
  • zoom for setting an initial zoom level. It is set to 13, we’ll want to show the markers when it’s higher than 15 and hide them when it’s below it (so no markers when we first see the map).
  • In the last line the function emitGetStops is called. I got the data at this point, as soon as the component is mounted (it can be different though); the data can be obtained in many different ways. I personally emitted a call to get the list of the markers from the parent component. We’ll see this later.

We call the function initMap() inside the mounted hook.

2.Detect zoom change

detectZoomChange() {  this.gMap.addListener(‘zoom_changed’, () => {    this.showOrHideMarkers();  });},

The wrapper of this function is given by google maps. I’ m using it for calling another function showOrHideMarkers whenever the zoom level changes. We keep following our steps, we’ll look into this later.

3.get the data for the markers

There’s many ways of getting the data.
For the sake of following, it’s important just to know the structure of the data: reduced to a minimum, we need an array of objects with this structure:

{
"id": 161848,
"lat": 39.22048,
"lon": 9.10544,
"stop_id": "stop_123",
"code": "0123",
"name": "Dev Shop",
"distance": 82.47313714,
"vehicle_type": 3,
"accessibility": 0
},

Let’s push into the array few more objects like this one, with closed enough coordinates to be able to see them in the map.

The following lines are just for inexperienced developers, like me, who may find it useful.

My way of getting the data:
I am using an api which is called in the parent component: the child component, where the map is held, just tells the parent when to make the call — we saw it, in the mounted hook with this.emitGetMarkers() .

This api needs the latitude and longitude of the center of the map, and the radius (in meters) in order to give me back the list of markers for that area of the map. So here it is the function:

emitGetMarkers()   const payload = {    lat: this.gMap.getCenter().lat();,    lon: this.gMap.getCenter().lng();,    radius: 1000,  };  this.$emit(‘getStops’, payload);},

this.gMap.getCenter().lat() .lng are google maps built-in functions just for that: obtain latitude and longitude of the center of the map.

At this point we have the array of the markers, with the properties we need to make google maps Markers out of them — remember the objects pushed into the array above. The strictly necessary ones are lat and lon and probably the name, so to show it in the tooltip when the marker is hovered.

One step back to understand the component data:

data() {
return {
markers: [],
gMap: null,
gMapMarkers: [],
}
}

gMap refers to the google maps map, I declared it globally for an easy access throughout the code, probably not the best choice, but that’s it.

markers is the array of the markers (got from the api, our data).

gMapMarkers is the array of the google maps markers.

Yes, they are two different things!

Now, we can move on.

4.create the google maps markers

createGMarkers() {  this.gMapMarkers = [];  let gMapMarker = null;
this.stops.forEach(marker => { const markerLatLng = { lat: marker.lat, lng: marker.lon }; gMapMarker = new window.google.maps.Marker({ position: markerLatLng, map: this.gMap, title: marker.name, icon: this.stopIcon, draggable: true, }); this.gMapMarkers.push(gMapMarker); });},

The first line this.gMapMarkers = []; makes sure that whenever we call this function, aka whenever we create an array of google maps markers, we start from a fresh empty array, to avoid duplicating markers.

So, we are going through each element of the markers array, setting a pair of latitude and longitude for each one of them — their position — and creating a gMapMarker (google maps marker) for each one of them, then push them, one by one, to the gMapMarkers array.

This is how to create the markers.

We ‘created’ them, but they are still not visible in the map.

Now, I really broke the code in little parts, it helped me a lot to achieve what I wanted.

First I created a general function to set the map on all markers in the array.

setMapOnAll(map) {
if (this.gMapMarkers.length > 0) {
this.gMapMarkers.forEach(marker => marker.setMap(map));
}
}

So we can call this same function for both showing and hiding the markers.

5.create function to show the markers

showMarkers(map) {this.setMapOnAll(map);},

6.create function to hide the markers (but keep them in the array)

clearMarkers(map) {this.setMapOnAll(map);},

I understand, it’s only one line function, I will ‘cleanup’ my code myself, but for the sake of understanding I found it much clearer like this.

We decided to show the markers only when the zoom level is greater than 15, and hide them when it’s below it.
So here comes the last function:

showOrHideMarkers() {  if (this.gMap) {    if (this.gMap.getZoom() >= 15) {      this.showMarkers(this.gMap);    }  if (this.gMap.getZoom() < 15) {    this.clearMarkers(null);  }},

Pretty easy this one: just an initial check if the gMap exists, then a call on the functions according to the zoom level. Passing null to setMap inside of setMapOnAll is the google maps way for removing the markers from the map.

Ok, now we have all the building blocks. Only one last thing is missing.

We call this function showOrHideMarkers anytime the zoom changes (it’s called inside the detectZoomChange function). But we also need it anytime the markers array changes. So, let’s add a watcher on it:

watch: {  markers: {   immediate: true,   deep: true,   handler(value) {    // array of stops got from api    if (value) this.markers = value;    this.createGMarkers();    // call api if prop changes    this.showOrHideMarkers();
}
},

As you can see, whenever the markers array changes, we also create a new gMapMarkers array.

Remember I am getting markers as a prop from the parent component.

props: {  markers: {    type: Array,    required: true,  },},

I think we reach the end!

I know everything could have been done in much less lines (both the code and this story), but I preferred to explain as much as possible to help even the most junior devs.

If any of you would like to suggest a better way of doing it (in terms of performance and elegance), please take the time to leave a comment below!

Thank you,

Sabrina

--

--