polygon
Description
This component helps you to create a polygon on Google Maps API.
For more information read the Google Maps documentation for polygons.
It is exported with the name GmapPolygon.
Variables
This component save the original polygon object provided by Google Maps in a property called $polygonObject, as the
example below.
this.$polygonObject = new google.maps.Polygon(...);
Source code
Click to se the source code of 
polygon.vue component<script>
import MapElementMixin from '../mixins/map-element';
import { getPropsValues, bindEvents, bindProps } from '../utils/helpers';
import { polygonMappedProps } from '../utils/mapped-props-by-map-element';
export default {
  mixins: [MapElementMixin],
  props: {
    deepWatch: {
      type: Boolean,
      default: false,
    },
    clickable: {
      type: Boolean,
      default: false,
    },
    draggable: {
      type: Boolean,
      default: false,
    },
    editable: {
      type: Boolean,
      default: false,
    },
    fillColor: {
      type: String,
      default: '',
    },
    fillOpacity: {
      type: Number,
      default: 1,
    },
    strokeColor: {
      type: String,
      default: '',
    },
    strokeOpacity: {
      type: Number,
      default: 1,
    },
    strokePosition: {
      type: Number,
      default: 0,
    },
    strokeWeight: {
      type: Number,
      default: 1,
    },
    visible: {
      type: Boolean,
      default: true,
    },
    options: {
      type: Object,
    },
    path: {
      type: Array,
      noBind: true,
    },
    paths: {
      type: Array,
      noBind: true,
    },
  },
  provide() {
    const events = [
      'click',
      'dblclick',
      'drag',
      'dragend',
      'dragstart',
      'mousedown',
      'mousemove',
      'mouseout',
      'mouseover',
      'mouseup',
      'rightclick',
    ];
    const promise = this.$mapPromise
      .then((map) => {
        this.$map = map;
        const initialOptions = {
          ...this.options,
          map,
          ...getPropsValues(this, polygonMappedProps),
        };
        const {
          options: extraOptions,
          path: optionPath,
          paths: optionPaths,
          ...finalOptions
        } = initialOptions;
        this.$polygonObject = new google.maps.Polygon(finalOptions);
        bindProps(this, this.$polygonObject, polygonMappedProps);
        bindEvents(this, this.$polygonObject, events);
        let clearEvents = () => {};
        this.$watch(
          'paths',
          (paths) => {
            if (paths) {
              clearEvents();
              this.$polygonObject.setPaths(paths);
              const updatePaths = () => {
                this.$emit('paths_changed', this.$polygonObject.getPaths());
              };
              const eventListeners = [];
              const mvcArray = this.$polygonObject.getPaths();
              for (let i = 0; i < mvcArray.getLength(); i += 1) {
                const mvcPath = mvcArray.getAt(i);
                eventListeners.push([
                  mvcPath,
                  mvcPath.addListener('insert_at', updatePaths),
                ]);
                eventListeners.push([
                  mvcPath,
                  mvcPath.addListener('remove_at', updatePaths),
                ]);
                eventListeners.push([
                  mvcPath,
                  mvcPath.addListener('set_at', updatePaths),
                ]);
              }
              eventListeners.push([
                mvcArray,
                mvcArray.addListener('insert_at', updatePaths),
              ]);
              eventListeners.push([
                mvcArray,
                mvcArray.addListener('remove_at', updatePaths),
              ]);
              eventListeners.push([
                mvcArray,
                mvcArray.addListener('set_at', updatePaths),
              ]);
              clearEvents = () => {
                eventListeners.forEach(([, listenerHandle]) => {
                  google.maps.event.removeListener(listenerHandle);
                });
              };
            }
          },
          {
            deep: this.deepWatch,
            immediate: true,
          }
        );
        this.$watch(
          'path',
          (path) => {
            if (path) {
              clearEvents();
              this.$polygonObject.setPaths(path);
              const mvcPath = this.$polygonObject.getPath();
              const eventListeners = [];
              const updatePaths = () => {
                this.$emit('path_changed', this.$polygonObject.getPath());
              };
              eventListeners.push([
                mvcPath,
                mvcPath.addListener('insert_at', updatePaths),
              ]);
              eventListeners.push([
                mvcPath,
                mvcPath.addListener('remove_at', updatePaths),
              ]);
              eventListeners.push([
                mvcPath,
                mvcPath.addListener('set_at', updatePaths),
              ]);
              clearEvents = () => {
                eventListeners.forEach(([, listenerHandle]) => {
                  google.maps.event.removeListener(listenerHandle);
                });
              };
            }
          },
          {
            deep: this.deepWatch,
            immediate: true,
          }
        );
        return this.$polygonObject;
      })
      .catch((error) => {
        throw error;
      });
    this.$polygonPromise = promise;
    return { $polygonPromise: promise };
  },
};
</script>
If you need to know what are mappedProps please read the general concepts of this
application here.
Mapped Props of 
GmapPolygon componentexport const polygonMappedProps = {
  clickable: {
    type: Boolean,
    noBind: true,
  },
  draggable: {
    type: Boolean,
  },
  editable: {
    type: Boolean,
  },
  fillColor: {
    type: String,
    noBind: true,
  },
  fillOpacity: {
    type: Number,
    noBind: true,
  },
  strokeColor: {
    type: String,
    noBind: true,
  },
  strokeOpacity: {
    type: Number,
    noBind: true,
  },
  strokePosition: {
    type: Number,
    noBind: true,
  },
  strokeWeight: {
    type: Number,
    noBind: true,
  },
  visible: {
    type: Boolean,
  },
  options: {
    type: Object,
  },
  path: {
    type: Array,
    twoWay: true,
    noBind: true,
  },
  paths: {
    type: Array,
    twoWay: true,
    noBind: true,
  },
};
Events bound with to way on 
GmapPolygonconst events = [
  'click',
  'dblclick',
  'drag',
  'dragend',
  'dragstart',
  'mousedown',
  'mousemove',
  'mouseout',
  'mouseover',
  'mouseup',
  'rightclick',
];
How to use it
<template>
  <gmap-map
    :center="{lat: 1.38, lng: 103.8}"
    :zoom="12"
    style="width: 100%; height: 500px">
    <gmap-polygon
      :paths="paths"
      :editable="true"
      @paths_changed="updateEdited($event)">
    </gmap-polygon>
  </gmap-map>
</template>
If you need to know the API of this component please read it here.
HTML examples
Simple polygon example
<body>
  <div id="root">
    <gmap-map :center="{lat: 1.38, lng: 103.8}" :zoom="12" style="width: 100%; height: 500px">
      <gmap-polygon :paths="paths" :editable="true" @paths_changed="updateEdited($event)">
      </gmap-polygon>
    </gmap-map>
    <ul v-if="edited" @click="edited = null">
      <li v-for="path in edited">
        <ol>
          <li v-for="point in path">
            {{point.lat}}, {{point.lng}}
          </li>
        </ol>
      </li>
    </ul>
  </div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/gmap-vue@1.2.2/dist/gmap-vue.min.js"></script>
  <script>
    Vue.use(GmapVue, {
      load: {
        key: 'AIzaSyDf43lPdwlF98RCBsJOFNKOkoEjkwxb5Sc'
      },
    });
    document.addEventListener('DOMContentLoaded', function() {
      window.XXX = new Vue({
        el: '#root',
        data: {
          edited: null,
          paths: [
            [ {lat: 1.380, lng: 103.800}, {lat:1.380, lng: 103.810}, {lat: 1.390, lng: 103.810}, {lat: 1.390, lng: 103.800} ],
            [ {lat: 1.382, lng: 103.802}, {lat:1.382, lng: 103.808}, {lat: 1.388, lng: 103.808}, {lat: 1.388, lng: 103.802} ],
          ]
        },
        methods: {
          updateEdited(mvcArray) {
            let paths = [];
            for (let i=0; i<mvcArray.getLength(); i++) {
              let path = [];
              for (let j=0; j<mvcArray.getAt(i).getLength(); j++) {
                let point = mvcArray.getAt(i).getAt(j);
                path.push({lat: point.lat(), lng: point.lng()});
              }
              paths.push(path);
            }
            this.edited = paths;
          }
        }
      });
    });
  </script>
</body>
Advanced polygon example
<body>
  <div id="root">
    <label>
      <strong>Start at:</strong>
      <gmap-autocomplete @place_changed="updateCenter($event)" />
    </label>
    <br>
    <br>
    <gmap-map :center="center" :zoom="12" style="width: 100%; height: 500px" ref="map">
      <gmap-polygon v-if="paths.length > 0" :paths="paths" :editable="true" @paths_changed="updateEdited($event)"
          @rightclick="handleClickForDelete"
          ref="polygon">
      </gmap-polygon>
    </gmap-map>
    <div>
      <button @click="addPath()">Add Path</button>
      <button @click="removePath()">Remove Path</button>
    </div>
    <br>
    <div>
      <label for="geojson">
        <strong>Paste your geojson here:</strong>
      </label>
      <textarea id="geojson" :value="polygonGeojson" style="width: 100%; height: 100px"
        @input="readGeojson">
      </textarea>
      <div v-if="errorMessage">{{errorMessage}}</div>
    </div>
  </div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/gmap-vue@1.2.2/dist/gmap-vue.min.js"></script>
  <script>
    Vue.use(GmapVue, {
      load: {
        key: 'AIzaSyDf43lPdwlF98RCBsJOFNKOkoEjkwxb5Sc',
        libraries: 'places',
      },
    });
    function closeLoop (path) {
      return path.concat(path.slice(0, 1))
    }
    document.addEventListener('DOMContentLoaded', function() {
      window.XXX = new Vue({
        el: '#root',
        data: {
          center: {lat: 1.38, lng: 103.8},
          edited: null,
          paths: [
          ],
          mvcPaths: null,
          errorMessage: null,
          polygonGeojson: '',
        },
        watch: {
          polygonPaths: _.throttle(function (paths) {
            if (paths) {
              this.paths = paths
              this.polygonGeojson = JSON.stringify({
                type: 'Polygon',
                coordinates: this.paths.map(path => closeLoop(path.map(({lat, lng}) => [lng, lat])))
              }, null, 2)
            }
          }, 1000)
        },
        computed: {
          polygonPaths: function () {
            if (!this.mvcPaths) return null
            let paths = [];
            for (let i=0; i < this.mvcPaths.getLength(); i++) {
              let path = [];
              for (let j=0; j<this.mvcPaths.getAt(i).getLength(); j++) {
                let point = this.mvcPaths.getAt(i).getAt(j);
                path.push({lat: point.lat(), lng: point.lng()});
              }
              paths.push(path);
            }
            return paths
          },
        },
        methods: {
          updateCenter: function (place) {
            this.center = {
              lat: place.geometry.location.lat(),
              lng: place.geometry.location.lng(),
            }
          },
          updateEdited: function (mvcPaths) {
            this.mvcPaths = mvcPaths
          },
          addPath: function () {
            // obtain the bounds, so we can guess how big the polygon should be
            var bounds = this.$refs.map.$mapObject.getBounds()
            var northEast = bounds.getNorthEast()
            var southWest = bounds.getSouthWest()
            var center = bounds.getCenter()
            var degree = this.paths.length + 1;
            var f = Math.pow(0.66, degree)
            // Draw a triangle. Use f to control the size of the triangle.
            // i.e., every time we add a path, we reduce the size of the triangle
            var path = [
              { lng: center.lng(), lat: (1-f) * center.lat() + (f) * northEast.lat() },
              { lng: (1-f) * center.lng() + (f) * southWest.lng(), lat: (1-f) * center.lat() + (f) * southWest.lat() },
              { lng: (1-f) * center.lng() + (f) * northEast.lng(), lat: (1-f) * center.lat() + (f) * southWest.lat() },
            ]
            this.paths.push(path)
          },
          removePath: function () {
            this.paths.splice(this.paths.length - 1, 1)
          },
          handleClickForDelete($event) {
            if ($event.vertex) {
              this.$refs.polygon.$polygonObject.getPaths()
                .getAt($event.path)
                .removeAt($event.vertex)
            }
          },
          readGeojson: function ($event) {
            try {
              this.polygonGeojson = $event.target.value
              var v = JSON.parse($event.target.value);
              this.paths = v.coordinates.map(linearRing =>
                linearRing.slice(0, linearRing.length - 1)
                .map(([lng, lat]) => ({lat, lng}))
              )
              this.errorMessage = null
            } catch (err) {
              this.errorMessage = err.message
            }
          }
        }
      });
    });
  </script>
</body>
Test the component
Simple polygon
Advanced polygon