geoman-io / leaflet-geoman

🍂🗺️ The most powerful leaflet plugin for drawing and editing geometry layers
https://geoman.io
MIT License
2.21k stars 433 forks source link

Vue integration #580

Closed totosugito closed 4 years ago

totosugito commented 4 years ago

Hi, are you have an example how to integrate this library with Vue ? Thank you. Best regards,

Toto

DazDotOne commented 4 years ago

I used vue-cli to get up and running.

Uing this library lets you get leaflet and Vue running quickly.

import the ES6 modules for geoman:

import "@geoman-io/leaflet-geoman-free";
import "@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css";

I think that's all.

Then you can access your map using something like:

let mapComponent = this.$refs.map; 

everything else is depending on your project setup.

your <l-map> component will be able to listen to pm events, for example:

<l-map
    ref="map"
    :zoom="zoom"
    :center="center"
    @pm:drawstart="drawStart"
    @pm:create="drawCreate"
    @click="mapClicked"
>

and features will too:

<l-feature-group
    @pm:cut="handleCutGeo"
    @pm:edit="handleEditGeo"
>
codeofsumit commented 4 years ago

Hope @DazDotOne s comment answered the question. Let us know if you have any problems.

mkalus commented 4 years ago

To get something similar to the editor on https://geoman.io/geojson-editor, you can create a map component like this:

<template>
  <div style="display: none;">
    <slot v-if="ready"></slot>
  </div>
</template>

<script>
/**
 * Geoman for Vue - created by Max Kalus
 *
 * Install using:
 * npm i @geoman-io/leaflet-geoman-free uuid
 */
import '@geoman-io/leaflet-geoman-free';
import L from 'leaflet';
import { v4 as uuidv4 } from 'uuid';

export default {
  name: 'vue2-leaflet-geoman',
  props: {
    map: {
      type: Object,
      required: true
    },
    locale: {
      type: String,
      default: 'en'
    },
    // initial geoJSON data
    geoJson: {
      type: Object
    }
  },
  data: () => ({
    ready: false
  }),
  created () {
    if (this.map) {
      // set global options
      this.map.pm.setGlobalOptions({ allowSelfIntersection: false });

      // set language
      this.map.pm.setLang(this.locale);

      // add controls
      this.map.pm.addControls({
        position: 'topleft'
      });

      // listen to events
      this.map.on('pm:create', this.mapUpdated);
      this.map.on('pm:remove', this.mapUpdated);
      this.map.on('pm:cut', this.mapUpdated);

      // add initial data
      this.importGeoJSON(this.geoJson);
    }
  },
  methods: {
    mapUpdated (event) {
      // add listeners on creation and delete on removal
      if (event.type === 'pm:create') {
        event.layer.on('pm:edit', this.mapUpdated);

        // add data
        event.layer.properties = {
          shape: event.shape
        };

        // radius for circles
        if (event.shape === 'Circle') {
          event.layer.properties.radius = event.layer.getRadius();
        }

        event.layer.internalId = uuidv4();
      }
      if (event.type === 'pm:remove') {
        event.layer.off(); // remove all event listeners
      }

      // emit event
      this.$emit('change', this.getDataAsGeoJSON());
    },
    // export data as GeoJSON object
    getDataAsGeoJSON () {
      // create FeatureCollection
      const geoJSON = {
        type: 'FeatureCollection',
        features: []
      };

      // export each layer
      this.map.eachLayer(function (layer) {
        if (layer.internalId && (layer instanceof L.Path || layer instanceof L.Marker)) {
          const geoJSONShape = layer.toGeoJSON(16); // to precise geo shape!
          geoJSONShape.properties = layer.properties;
          geoJSONShape.id = layer.internalId;
          geoJSON.features.push(geoJSONShape);

          // normalize coordinates (> 180/>90)
          // TODO
        }
      });

      return geoJSON;
    },
    // inport data from GeoJSON
    importGeoJSON (geoJSON) {
      if (geoJSON && geoJSON.type === 'FeatureCollection' && geoJSON.features && geoJSON.features.length) {
        geoJSON.features.forEach(feature => {
          const shape = feature.properties && feature.properties.shape;
          const coordinates = feature.geometry && feature.geometry.coordinates;
          let layer; // define shape for later use
          switch (shape) {
            case 'Circle':
              layer = new L.Circle([coordinates[1], coordinates[0]], feature.properties.radius);
              break;
            case 'CircleMarker':
              layer = new L.CircleMarker([coordinates[1], coordinates[0]]);
              break;
            case 'Marker':
              layer = new L.Marker([coordinates[1], coordinates[0]]);
              break;
            case 'Polyline':
              layer = new L.Polyline(this.switchCoordinates(coordinates));
              break;
            case 'Polygon':
              layer = new L.Polygon(this.switchCoordinates(coordinates));
              break;
            case 'Rectangle':
              layer = new L.Rectangle(this.switchCoordinates(coordinates));
              break;
          }

          if (layer) {
            layer.addTo(this.map);

            layer.internalId = feature.id;
            layer.properties = feature.properties;

            // add event listener
            layer.on('pm:edit', this.mapUpdated);
          }
        });
      }
    },
    // switch coordinates -> geoJSON to Leaflet
    switchCoordinates (coordinates) {
      return [coordinates[0].map(pair => [pair[1], pair[0]])];
    }
  }
};
</script>

<style lang="sass">
@import '~@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css'
</style>

Then add the component to your map:

<vue2-leaflet-geoman :lang="locale" :map="mapObject" :geo-json="initialData" v-if="mapObject" @change="shapesChanged" />

mapObject should be your map object and you have to set it after init, so listen to the ready event of your l-map.

totosugito commented 4 years ago

Thank you very much for the sample source code