You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenLayers 7实现多边形/多多边形选点分割及测量展示

OpenLayers 7 多边形分割功能实现

关键前置准备

确保已引入OpenLayers 7和JSTS库(用于多边形拓扑分割):

<script src="https://cdn.jsdelivr.net/npm/openlayers@7.4.0/dist/ol.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/openlayers@7.4.0/dist/ol.css">
<script src="https://cdn.jsdelivr.net/npm/jsts@2.0.0/dist/jsts.min.js"></script>

完整功能代码

// 全局变量定义
let map;
let vectorLayer;
let splitLineLayer;
let selectInteraction;
let pDrawInteraction;
let selectedFeature;
let sketch;
let measureTooltip;
let measureTooltipElement;
const regularStyle = new ol.style.Style({
  stroke: new ol.style.Stroke({ color: '#3399CC', width: 1.5 }),
  fill: new ol.style.Fill({ color: 'rgba(0, 153, 204, 0.1)' })
});
const highlightStyle = new ol.style.Style({
  stroke: new ol.style.Stroke({ color: '#FF0000', width: 2 }),
  fill: new ol.style.Fill({ color: 'rgba(255, 0, 0, 0.2)' })
});
const lineStyle = new ol.style.Style({
  stroke: new ol.style.Stroke({
    color: 'navy',
    width: 3,
    lineDash: [4, 8],
    lineDashOffset: 6
  }),
  image: new ol.style.Circle({
    fill: new ol.style.Fill({ color: '#69cfba' }),
    radius: 5,
    stroke: new ol.style.Stroke({ color: 'black', width: 2 })
  })
});

// 初始化地图
function initMap() {
  vectorLayer = new ol.layer.Vector({
    source: new ol.source.Vector(),
    style: regularStyle
  });
  splitLineLayer = new ol.layer.Vector({ source: new ol.source.Vector() });

  map = new ol.Map({
    target: 'map',
    layers: [
      new ol.layer.Tile({ source: new ol.source.OSM() }),
      vectorLayer,
      splitLineLayer
    ],
    view: new ol.View({ center: ol.proj.fromLonLat([0, 0]), zoom: 10 })
  });

  // 添加要素选择交互
  addSelectInteraction();
}

// 添加选择交互:点击选中多边形
function addSelectInteraction() {
  selectInteraction = new ol.interaction.Select({
    style: highlightStyle,
    filter: function(feature) {
      const geomType = feature.getGeometry().getType();
      return geomType === 'Polygon' || geomType === 'MultiPolygon';
    }
  });
  map.addInteraction(selectInteraction);

  // 监听选中事件,准备启动分割线绘制
  selectInteraction.on('select', function(evt) {
    selectedFeature = evt.selected.length > 0 ? evt.selected[0] : null;
    if (selectedFeature) {
      // 移除之前的绘制交互
      if (pDrawInteraction) map.removeInteraction(pDrawInteraction);
      // 启动分割线绘制
      startSubDivLineDrawInteraction('LineString', splitLineLayer.getSource());
    }
  });
}

// 启动分割线绘制交互
function startSubDivLineDrawInteraction(gtype, source) {
  pDrawInteraction = new ol.interaction.Draw({
    source: source,
    type: gtype,
    style: lineStyle,
    // 限制起点必须在选中要素内
    condition: function(event) {
      if (!selectedFeature) return false;
      const coord = event.coordinate;
      return selectedFeature.getGeometry().intersectsCoordinate(coord);
    }
  });
  map.addInteraction(pDrawInteraction);

  createMeasureTooltip(map);
  let listener;

  // 绘制开始:实时监听分割线变化,展示长度
  pDrawInteraction.on('drawstart', function(evt) {
    sketch = evt.feature;
    listener = sketch.getGeometry().on('change', function(geomEvt) {
      const line = geomEvt.target;
      const length = line.getLength();
      // 更新长度提示框
      const midPoint = line.getFlatMidpoint();
      measureTooltip.setPosition(midPoint);
      measureTooltipElement.innerHTML = `<b>${formatLength(line)}</b>`;
      // 实时尝试分割(可选,也可在drawend时处理)
      trySplitPolygon(line);
    });
  });

  // 绘制结束:完成分割并清理
  pDrawInteraction.on('drawend', function(e) {
    map.removeInteraction(pDrawInteraction);
    ol.Observable.unByKey(listener);
    // 最终分割处理
    trySplitPolygon(e.feature.getGeometry());
  });
}

// 创建测量提示框
function createMeasureTooltip(map) {
  if (measureTooltipElement) {
    measureTooltipElement.parentNode.removeChild(measureTooltipElement);
  }
  measureTooltipElement = document.createElement('div');
  measureTooltipElement.className = 'mtooltip mtooltip-measure';
  measureTooltip = new ol.Overlay({
    element: measureTooltipElement,
    offset: [0, -15],
    positioning: 'bottom-center'
  });
  map.addOverlay(measureTooltip);
  document.querySelector('.ol-overlay-container').style.zIndex = '1000';
}

// 格式化长度
function formatLength(line) {
  const length = line.getLength();
  return length >= 1000 ? `${(length / 1000).toFixed(3)} KM` : `${length.toFixed(3)} Meter`;
}

// 格式化面积
function formatArea(polygon) {
  const area = ol.sphere.getArea(polygon);
  if (area >= 10000) {
    return `${Math.round((area / 10000) * 100) / 100} HECT`;
  } else {
    return `${Math.round(area * 100) / 100} SQ M`;
  }
}

// 尝试分割多边形
function trySplitPolygon(lineGeom) {
  if (!selectedFeature) return;

  const parser = new jsts.io.OL3Parser();
  let selectedGeom = selectedFeature.getGeometry();
  let jstsSelectedGeom;

  // 处理MultiPolygon:分解为单个Polygon处理(示例取第一个,如需批量处理可扩展循环)
  if (selectedGeom.getType() === 'MultiPolygon') {
    const polygons = selectedGeom.getPolygons();
    jstsSelectedGeom = parser.read(polygons[0]);
  } else {
    jstsSelectedGeom = parser.read(selectedGeom);
  }

  const jstsLineGeom = parser.read(lineGeom);

  try {
    // 执行分割:用线切割多边形
    const splitter = new jsts.operation.split.SplitOp();
    const splitResult = splitter.split(jstsSelectedGeom, jstsLineGeom);
    const splitGeoms = splitResult.getGeometries();

    // 仅当分割出两个多边形时处理
    if (splitGeoms.length === 2) {
      // 清除原有要素和Overlay
      vectorLayer.getSource().removeFeature(selectedFeature);
      map.getOverlays().clear();

      // 添加分割后的多边形并展示面积
      splitGeoms.forEach(function(jstsGeom) {
        const olGeom = parser.write(jstsGeom);
        const newFeature = new ol.Feature({ geometry: olGeom });
        vectorLayer.getSource().addFeature(newFeature);

        // 创建面积Overlay
        const areaOverlay = new ol.Overlay({
          element: document.createElement('div'),
          offset: [0, -15],
          positioning: 'bottom-center'
        });
        const area = ol.sphere.getArea(olGeom);
        areaOverlay.getElement().innerHTML = `<b>${formatArea(olGeom)}</b>`;
        areaOverlay.setPosition(olGeom.getFlatInteriorPoint());
        map.addOverlay(areaOverlay);
      });

      // 添加分割线并展示长度
      const lineFeature = new ol.Feature({ geometry: lineGeom });
      splitLineLayer.getSource().addFeature(lineFeature);
      const lineOverlay = new ol.Overlay({
        element: document.createElement('div'),
        offset: [0, -15],
        positioning: 'bottom-center'
      });
      lineOverlay.getElement().innerHTML = `<b>${formatLength(lineGeom)}</b>`;
      lineOverlay.setPosition(lineGeom.getFlatMidpoint());
      map.addOverlay(lineOverlay);
    }
  } catch (e) {
    // 分割失败(如线未完全穿过多边形),清除临时Overlay
    map.getOverlays().clear();
  }
}

// 初始化地图
window.onload = initMap;

关键功能说明

  • 要素选择:通过ol.interaction.Select实现,仅允许选择Polygon/MultiPolygon类型要素,选中后自动启动分割线绘制。
  • 分割线绘制限制:通过condition配置,确保分割线起点必须在选中要素范围内,避免无效绘制。
  • 实时长度展示:在绘制过程中监听线几何变化,通过Overlay实时显示分割线长度。
  • 多边形分割:使用JSTS的SplitOp工具完成拓扑分割,支持Polygon和MultiPolygon(示例中处理MultiPolygon的第一个子多边形,如需批量处理可扩展循环逻辑)。
  • 面积与长度展示:分割完成后,为每个新多边形创建面积Overlay,为分割线创建长度Overlay,定位在几何中心点/中点。

内容的提问来源于stack exchange,提问作者Marudi Abhinaya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 12:07:12