Skip to content

leaflet-geoman 图形绘制与编辑

leaflet-geoman 是一个商业化的插件,文档很详细,同时提供免费版本。

免费版本的功能同样强大,支持图形的绘制、编辑、拖动、剪切、分割、固定、跟踪、测量、缩放、旋转等操作。

示例

  • 绘制图形
  • 清除图形
  • 清除所有

安装依赖

shell
pnpm i @geoman-io/leaflet-geoman-free

在项目中引入

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

or

html

<script src="https://unpkg.com/@geoman-io/leaflet-geoman-free@latest/dist/leaflet-geoman.min.js"></script>
<link rel="stylesheet"
      href="https://unpkg.com/@geoman-io/leaflet-geoman-free@latest/dist/leaflet-geoman.css"
/>

国际化

js
// 文档地址 https://geoman.io/docs/customize/language
mapObj.value.pm.setLang("zh");

代码实现

vue
<script setup>
import { ref, defineAsyncComponent } from 'vue';
// todo 项目中使用请放开注释
// import "@geoman-io/leaflet-geoman-free";
import '@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css';

const InitMap = defineAsyncComponent(() =>
  import('../../components/InitMapTianditu.vue')
);

const mapObj = ref();

const addControls = () => {
  mapObj.value.pm.addControls({
    position: 'topright',
    drawCircleMarker: false,
    rotateMode: false
  });
};

// 自定义绘制样式
const customDrawingStyle = () => {
  mapObj.value.pm.setPathOptions(
    {
      color: 'orange', // 线的颜色
      fillColor: 'green', // 填充的颜色
      fillOpacity: 0.4 // 填充的透明度
    },
    {
      ignoreShapes: ['Circle'] // 忽略某些图形的更改
    }
  );
};

// 绘制完成事件
const pmCreate = (event) => {
  const layer = event.layer;
  const shapeType = layer.pm.getShape(); // 获取绘制图形的类型

  let coordinates = [];

  // 根据绘制图形的类型获取坐标集合
  switch (shapeType) {
    case 'Polygon':
      coordinates = layer.getLatLngs()[0];
      break;
    case 'Rectangle':
      coordinates = layer.getLatLngs()[0];
      break;
    case 'Circle':
      const center = layer.getLatLng();
      const radius = layer.getRadius();
      console.log('圆形', center, radius);
      // 这里可以根据需要将圆形转换为多边形,以便获得更多点的坐标
      // coordinates = convertCircleToPolygon(center, radius, numPoints);
      break;
    default:
      break;
  }

  console.log('绘制图形点位', coordinates);
};

// 图形删除事件
const pmRemove = (e) => {
  // 关闭全局删除模式
  mapObj.value.pm.disableGlobalRemovalMode();
};

const mapLoad = (map) => {
  mapObj.value = map;

  // 设置语言为中文
  mapObj.value.pm.setLang('zh');

  addControls();
  customDrawingStyle();

  // 监听绘制完成事件
  mapObj.value.on('pm:create', pmCreate);

  // 监听图形删除事件
  mapObj.value?.on('pm:remove', pmRemove);
};

/**
 * 手动调用 api 实现绘制、删除
 * https://geoman.io/docs/modes/draw-mode
 */

// 手动绘制多边形
const drawGraphics = () => {
  // 启用绘制模式
  mapObj.value.pm.enableDraw('Polygon', {
    snappable: true,
    snapDistance: 20
  });
};

// 手动清除绘制图形
const clearDrawGraphics = () => {
  // 开启全局删除模式
  mapObj.value.pm.enableGlobalRemovalMode();
};

// 清除所有绘制图层
const clearAllDrawGraphics = () => {
  mapObj.value.pm.disableGlobalRemovalMode();
  const allLayers = mapObj.value.pm.getGeomanDrawLayers();
  allLayers.forEach((layer) => {
    mapObj.value.removeLayer(layer);
  });
};
</script>

<template>
  <init-map style="height: 50vh" @map-load="mapLoad"></init-map>
  <ul>
    <li class="c-button" @click="drawGraphics">绘制图形</li>
    <li class="c-button" @click="clearDrawGraphics">清除图形</li>
    <li class="c-button" @click="clearAllDrawGraphics">清除所有</li>
  </ul>
</template>

<style scoped></style>
vue
<script setup>
import { onMounted, onUnmounted, ref } from 'vue';
// todo 项目使用请放开 leaflet 引入
// import L from 'leaflet';

const props = defineProps({
  center: {
    type: Array,
    default: [32.0237855, 118.8075675]
  }
})

const emit = defineEmits(['mapLoad']);

const mapRef = ref();

const initMap = () => {
  const map = L.map(mapRef.value, {
    center: props.center,
    zoom: 11,
    minZoom: 6,
    maxZoom: 20
  });

  const mapType = 'vec';
  L.tileLayer(
    'https://t{s}.tianditu.gov.cn/' +
      mapType +
      '_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=' +
      mapType +
      '&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL={x}&TILEROW={y}&TILEMATRIX={z}&tk=b72aa81ac2b3cae941d1eb213499e15e',
    {
      subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
      attribution:
        '&copy; <a href="http://lbs.tianditu.gov.cn/home.html">天地图 GS(2022)3124号 - 甲测资字1100471</a>'
    }
  ).addTo(map);

  const mapLabelType = 'cva';
  L.tileLayer(
    'https://t{s}.tianditu.gov.cn/' +
      mapLabelType +
      '_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=' +
      mapLabelType +
      '&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL={x}&TILEROW={y}&TILEMATRIX={z}&tk=b72aa81ac2b3cae941d1eb213499e15e',
    {
      subdomains: ['0', '1', '2', '3', '4', '5', '6', '7']
    }
  ).addTo(map);

  // 地图初始化完成发送事件
  emit('mapLoad', map);

  return map;
};

const mapObj = ref();

// 在 onMounted 中初始化地图
onMounted(() => {
  mapObj.value = initMap();
});

const removeMap = () => {
  if (mapObj.value) {
    mapObj.value.remove();
  }
};

// 在组件卸载时删除地图
onUnmounted(() => {
  removeMap();
});
</script>

<template>
  <div ref="mapRef" class="map"></div>
</template>

<style scoped>
.map {
  height: 40vh;
  z-index: 0;
}
</style>

贡献者

Released under the MIT License.