Creating custom labels with Polygons has always been a challenging thing for JavaScript developers. The fact that the polygons API doesn’t provide any option to show custom labels directly leave developers with no easy options.
There are plenty of workarounds. Like using an InfoWindow and mounting it over the polygon shape. But it looks weird. InfoWindows should show more detailed information about a place, marker or shape.
I’ve had my share of luck with different experiments and was able to use a marker as a label. Let’s have a look at how I did it:
I had the following project structure:
–App.js
–components
—-PolygonLabel.vue
—-PolygonWithLabel.vue
–constants.js
–main.js
package.json
I’ve mounted the app using App.js as follows:
<template>
<div id="app">
<PolygonWithLabel />
</div>
</template>
<script>
import PolygonWithLabel from "./components/PolygonWithLabel";
export default {
name: "App",
components: {
PolygonWithLabel,
},
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
</style>
Code language: HTML, XML (xml)
Then in the PolygonWithLabel.vue file:
<template>
<div>
<div class="header-margins">
Google Maps Polygon With Label
<a target="_blank" href="nightprogrammer.com">nightprogrammer.com</a>
</div>
<div id="polygon-label-map" class="map-margins"></div>
</div>
</template>
<script>
import loadGoogleMapsApi from "load-google-maps-api";
import PolygonLabelComponent from "./PolygonLabel";
import { gMapsApiKey } from "./../constants";
import Vue from "vue";
const PolygonLabelConstructor = Vue.extend(PolygonLabelComponent);
export default {
name: "AnimatedMarkerMap",
data() {
return {
map: null,
};
},
mounted() {
loadGoogleMapsApi({
key: gMapsApiKey,
libraries: ["places", "drawing", "geometry"],
}).then(async () => {
const mapZoom = 12;
const { google } = window;
const mapOptions = {
zoom: mapZoom,
mapTypeId: google.maps.MapTypeId.HYBRID,
center: new google.maps.LatLng({ lat: 23, lng: 57 }),
mapTypeControl: true,
streetViewControl: false,
mapTypeControlOptions: {
position: google.maps.ControlPosition.BOTTOM_LEFT,
},
};
this.map = new google.maps.Map(
document.getElementById("polygon-label-map"),
mapOptions
);
const polygonLabelComponent = new PolygonLabelConstructor({
propsData: { fieldName: "Bermuda" },
});
polygonLabelComponent.$mount();
const polygonLabelString = new XMLSerializer().serializeToString(
polygonLabelComponent.$el
);
const polygonLabelUrl =
"data:image/svg+xml;charset=UTF-8;base64," + btoa(polygonLabelString);
const polygonCoords = [
{ lat: 25.774, lng: -80.19 },
{ lat: 18.466, lng: -66.118 },
{ lat: 32.321, lng: -64.757 },
{ lat: 25.774, lng: -80.19 },
];
const tempBounds = new google.maps.LatLngBounds();
for (let j = 0; j < polygonCoords.length; j++) {
const x = {
lat: polygonCoords[j].lat,
lng: polygonCoords[j].lng,
};
const BoundLatLng = new google.maps.LatLng({
lat: parseFloat(x.lat),
lng: parseFloat(x.lng),
});
tempBounds.extend(BoundLatLng);
}
const centroid = tempBounds.getCenter();
const markerLabel = new google.maps.Marker({
position: centroid,
icon: polygonLabelUrl,
});
const PolygonShape = new google.maps.Polygon({
paths: polygonCoords,
strokeColor: "#ffffff",
map: this.map,
strokeWeight: 3,
fillColor: "#00ff00",
fillOpacity: 0.2,
zIndex: 99999,
});
markerLabel.setMap(this.map);
PolygonShape.setMap(this.map);
this.map.setCenter(centroid);
this.map.setZoom(4);
});
},
};
</script>
<style scoped>
.header-margins {
margin-left: 40px;
margin-top: 20px;
}
.map-margins {
height: 400px;
width: 600px;
margin: 30px 40px;
}
</style>
Code language: HTML, XML (xml)
You’d notice that on line 17, I’ve created a polygonLabel constructor from the polygonLabel component. Then on 48, I’ve created the component with some props data to show on the label. I’ve then mounted the label on line 52. On line 54, the serialised the component to a string. I’ve then converted it into a dataUrl on line 58.
Finally I’ve used the mounted polygon label as a marker on line 83. And polygon shape on line 88.
And the PolygonLabel.vue file as follows:
<template>
<svg
width="400"
height="30"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
>
<g class="layer">
<title>Polygon</title>
<text
style="
font-family: Arial, Helvetica, sans-serif;
font-weight: 600;
text-shadow: -1px -1px 0 #ffffff, 1px -1px 0 #ffffff,
-1px 1px 0 #ffffff, 1px 1px 0 #ffffff;
font-size: 14px;
"
fill="#ffffff"
class="pltext"
text-anchor="middle"
transform="matrix(0.9827 0 0 1 3.23004 0)"
x="195.9824"
xml:space="preserve"
y="21"
>
{{ fieldName }}
</text>
</g>
</svg>
</template>
<script>
export default {
props: ["fieldName"],
};
</script>
Code language: HTML, XML (xml)
You can find a working solution of the above code from my repos here: