Loading

openlayers基本使用(街景+标注+绘制)

卫星影像

<template>
  <div class="app">
    <div class="map-container" ref="map_ref"></div>
  </div>
</template>

<script>
import Map from "ol/Map.js";
import TileLayer from "ol/layer/Tile.js";
import View from "ol/View.js";
import XYZ from "ol/source/XYZ.js";

export default {
  data() {
    return {};
  },
  mounted() {
    this.initMap();
  },
  methods: {
    initMap() {
      // const map = new Map({
      //   target: this.$refs.map_ref,
      //   layers: [
      //     new TileLayer({
      //       source: new XYZ({
      //         url: "https://webst0{1-4}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
      //       }),
      //     }),
      //   ],
      //   view: new View({
      //     center: [0, 0],
      //     zoom: 2,
      //   }),
      // });

      // 创建卫星图层
      const satelliteLayer = new TileLayer({
        source: new XYZ({
          url: "https://webst0{1-4}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
          maxZoom: 18,
        }),
        opacity: 0.9,
        zIndex: 1,
      });

      // 创建街道图层作为备用
      const streetLayer = new TileLayer({
        source: new XYZ({
          url: "https://webst0{1-4}.is.autonavi.com/appmaptile?style=7&x={x}&y={y}&z={z}",
          maxZoom: 18,
        }),
        zIndex: 0,
        visible: false,
      });
      const map = new Map({
        target: this.$refs.map_ref,
        layers: [satelliteLayer, streetLayer],
        view: new View({
          center: [0, 0],
          zoom: 2,
        }),
      });
    },
  },
};
</script>

<style lang="less" scoped>
.app {
  padding: 10px;
}
.map-container {
  height: calc(100vh - 50px);
  // background-color: orange;
}
</style>

增加标注

基础示例

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <title>OpenLayers 街景与地名标注</title>
    <!-- 引入 OpenLayers CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@7.5.1/dist/ol.css" />
    <!-- 引入 OpenLayers JavaScript -->
    <script src="https://cdn.jsdelivr.net/npm/ol@7.5.1/dist/ol.js"></script>
    <style>
      #map {
        width: 100%;
        height: 800px;
      }
    </style>
  </head>
  <body>
    <div id="map"></div>
    <script>
      // 1.创建地图=============
      var map = new ol.Map({
        target: "map", // 地图渲染的目标 DOM 元素 ID
        layers: [
          // 底图图层,这里使用 OpenStreetMap
          new ol.layer.Tile({
            // source: new ol.source.OSM(),
            source: new ol.source.XYZ({
              url: "https://webst0{1-4}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
            }),
          }),
        ],
        view: new ol.View({
          center: ol.proj.fromLonLat([108, 28]), // 地图初始中心点,这里以西南地区大致经纬度为例
          zoom: 8, // 初始缩放级别
        }),
      });

      // 2.1 加载地名 GeoJSON 数据=======================
      var vectorSource = new ol.source.Vector({
        url: "./places.json", // 地名数据文件路径
        format: new ol.format.GeoJSON(),
      });

      // 2.2 创建矢量图层,用于显示地名标注
      var vectorLayer = new ol.layer.Vector({
        source: vectorSource,
        style: function (feature) {
          // 为每个地名要素设置样式,包括点样式和文字标签
          var textStyle = new ol.style.Text({
            text: feature.get("name"), // 获取地名属性
            font: "12px Arial",
            fill: new ol.style.Fill({
              color: "#000",
            }),
            stroke: new ol.style.Stroke({
              color: "#fff",
              width: 2,
            }),
            offsetY: -15, // 文字相对于点的垂直偏移
          });

          var pointStyle = new ol.style.Style({
            image: new ol.style.Circle({
              radius: 5,
              fill: new ol.style.Fill({
                color: "#ff0000",
              }),
            }),
            text: textStyle,
          });

          return pointStyle;
        },
      });

      // 2.3 将地名标注图层添加到地图
      map.addLayer(vectorLayer);

      // 3. 给地图添加点击事件,模拟街景加载
      map.on("click", function (evt) {
        var coordinate = evt.coordinate;
        // 这里只是模拟,实际需根据点击位置获取街景资源并展示
        alert("点击位置经纬度:" + ol.proj.toLonLat(coordinate));
        // 实际项目中,可在此处调用街景库的 API 加载对应位置的街景
      });

      // 获取地图的缩放级别=================start
      // 获取地图视图对象
      const view = map.getView();
      // 监听change:resolution事件
      view.on("change:resolution", function () {
        const zoom = view.getZoom(); // 获取当前缩放级别
        console.log("zoom=========>", zoom);
      });
      // 获取地图的缩放级别=================end
    </script>
  </body>
</html>

缩放到一定程度后再显示标注

效果图:

image

示例代码:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <title>OpenLayers 街景与地名标注</title>
    <!-- 引入 OpenLayers CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@7.5.1/dist/ol.css" />
    <!-- 引入 OpenLayers JavaScript -->
    <script src="https://cdn.jsdelivr.net/npm/ol@7.5.1/dist/ol.js"></script>
    <style>
      #map {
        width: 100%;
        height: 800px;
      }
    </style>
  </head>
  <body>
    <div class="info">
      当前缩放级别: <span id="zoom-level">0</span><br />
      标注将在缩放级别 ≥ 8 时显示
    </div>
    <div id="map"></div>
    <script>
      // 1.创建地图=============
      var map = new ol.Map({
        target: "map", // 地图渲染的目标 DOM 元素 ID
        layers: [
          // 底图图层,这里使用 OpenStreetMap
          new ol.layer.Tile({
            // source: new ol.source.OSM(),
            source: new ol.source.XYZ({
              url: "https://webst0{1-4}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
            }),
          }),
        ],
        view: new ol.View({
          center: ol.proj.fromLonLat([108, 28]), // 地图初始中心点,这里以西南地区大致经纬度为例
          zoom: 8, // 初始缩放级别
        }),
      });

      // 2.1 加载地名 GeoJSON 数据=======================
      var vectorSource = new ol.source.Vector({
        url: "./places.json", // 地名数据文件路径
        format: new ol.format.GeoJSON(),
      });

      // 2.2 创建矢量图层,用于显示地名标注
      var vectorLayer = new ol.layer.Vector({
        source: vectorSource,
        style: function (feature) {
          // 为每个地名要素设置样式,包括点样式和文字标签
          var textStyle = new ol.style.Text({
            text: feature.get("name"), // 获取地名属性
            font: "12px Arial",
            fill: new ol.style.Fill({
              color: "#000",
            }),
            stroke: new ol.style.Stroke({
              color: "#fff",
              width: 2,
            }),
            offsetY: -15, // 文字相对于点的垂直偏移
          });

          var pointStyle = new ol.style.Style({
            image: new ol.style.Circle({
              radius: 5,
              fill: new ol.style.Fill({
                color: "#ff0000",
              }),
            }),
            text: textStyle,
          });

          return pointStyle;
        },
      });

      // 2.3 将地名标注图层添加到地图
      map.addLayer(vectorLayer);

      // 3. 需求:获取地图的经纬度      (给地图添加点击事件)
      map.on("click", function (evt) {
        var coordinate = evt.coordinate;
        // 这里只是模拟,实际需根据点击位置获取街景资源并展示
        alert("点击位置经纬度:" + ol.proj.toLonLat(coordinate));
        // 实际项目中,可在此处调用街景库的 API 加载对应位置的街景
      });

      // 4. 需求:缩放到一定的程度后再显示标注图层============start
      // 缩放级别显示元素
      var zoomLevelElement = document.getElementById("zoom-level");
      // 控制标注显示的最小缩放级别
      var MIN_ZOOM_FOR_LABELS = 8;
      // 更新标注可见性的函数
      function updateLabelsVisibility() {
        var currentZoom = map.getView().getZoom();
        zoomLevelElement.textContent = currentZoom.toFixed(1);

        // 当缩放级别大于等于设定值时显示标注,否则隐藏
        vectorLayer.setVisible(currentZoom >= MIN_ZOOM_FOR_LABELS);
      }
      // 初始调用一次,设置初始状态
      updateLabelsVisibility();

      // 监听地图的移动结束事件(包括缩放和拖动)
      map.on("moveend", updateLabelsVisibility);
      // 4. 需求:缩放到一定的程度后再显示标注图层============end
    </script>
  </body>
</html>

多边形绘制和标记

基础示例

效果图:

image

示例代码:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <title>OpenLayers 街景与地名标注</title>
    <!-- 引入 OpenLayers CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@7.5.1/dist/ol.css" />
    <!-- 引入 OpenLayers JavaScript -->
    <script src="https://cdn.jsdelivr.net/npm/ol@7.5.1/dist/ol.js"></script>
    <style>
      #map {
        width: 100%;
        height: 800px;
      }

      .info {
        /* position: absolute;
        top: 10px;
        left: 10px; */
        background: white;
        padding: 10px;
        border-radius: 5px;
        box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
        /* max-width: 250px; */
      }

      /* 绘制工具 */
      .toolbar {
        /* position: absolute;
        top: 10px;
        right: 10px; */
        background: white;
        padding: 10px;
        border-radius: 5px;
        box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
      }
      .toolbar button {
        /* display: block;
        width: 100%; */
        padding: 8px 12px;
        margin-bottom: 5px;
        background: #f5f5f5;
        border: 1px solid #ddd;
        border-radius: 3px;
        cursor: pointer;
        transition: all 0.2s;
      }
      .toolbar button:hover {
        background: #e0e0e0;
      }
      .toolbar button.active {
        background: #4285f4;
        color: white;
        border-color: #4285f4;
      }

      .status-panel {
        background: white;
        padding: 10px;
        border-radius: 5px;
        box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
        max-height: 200px;
        overflow-y: auto;
      }
      .feature-count {
        color: #666;
        font-size: 14px;
        margin-bottom: 10px;
      }
    </style>
  </head>
  <body>
    <div class="info">
      当前缩放级别: <span id="zoom-level">0</span><br />
      标注将在缩放级别 ≥ 8 时显示
    </div>
    <div class="toolbar">
      <button id="draw-point">绘制点标记</button>
      <button id="draw-line-string">绘制线</button>
      <button id="draw-polygon">绘制多边形</button>
      <button id="clear">清除所有绘制</button>
    </div>

    <div class="data-panel">
      <h4>绘制数据信息:</h4>
      <pre id="data-display"></pre>
    </div>

    <!-- <div class="data-panel">
      <h4>绘制数据信息:</h4>
      <pre id="latest-data"></pre>
      <div class="history-list">
        <h5>历史绘制记录:</h5>
        <ul id="history-data"></ul>
      </div>
    </div> -->
    <div class="status-panel">
      <div class="feature-count">当前已绘制: <span id="count">0</span> 个要素</div>
      <div>
        <h4>最近绘制数据:</h4>
        <pre id="last-feature-data"></pre>
      </div>
    </div>

    <div id="map"></div>
    <script>
      var map = new ol.Map({
        target: "map",
        layers: [
          // 底图图层,这里使用 OpenStreetMap
          new ol.layer.Tile({
            // source: new ol.source.OSM(),
            source: new ol.source.XYZ({
              url: "https://webst0{1-4}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
            }),
          }),
        ],
        view: new ol.View({
          center: ol.proj.fromLonLat([108, 28]),
          zoom: 8,
        }),
      });

      var vectorSource = new ol.source.Vector({
        url: "./places.json", // 地名数据文件路径
        format: new ol.format.GeoJSON(),
      });

      var vectorLayer = new ol.layer.Vector({
        source: vectorSource,
        style: function (feature) {
          var textStyle = new ol.style.Text({
            text: feature.get("name"), // 获取地名属性
            font: "12px Arial",
            fill: new ol.style.Fill({
              color: "#000",
            }),
            stroke: new ol.style.Stroke({
              color: "#fff",
              width: 2,
            }),
            offsetY: -15, // 文字相对于点的垂直偏移
          });

          var pointStyle = new ol.style.Style({
            image: new ol.style.Circle({
              radius: 5,
              fill: new ol.style.Fill({
                color: "#ff0000",
              }),
            }),
            text: textStyle,
          });

          return pointStyle;
        },
      });

      map.addLayer(vectorLayer);

      map.on("click", function (evt) {
        var coordinate = evt.coordinate;
        console.log("点击位置经纬度:" + ol.proj.toLonLat(coordinate));
      });

      var zoomLevelElement = document.getElementById("zoom-level");
      var MIN_ZOOM_FOR_LABELS = 8;
      function updateLabelsVisibility() {
        var currentZoom = map.getView().getZoom();
        zoomLevelElement.textContent = currentZoom.toFixed(1);
        vectorLayer.setVisible(currentZoom >= MIN_ZOOM_FOR_LABELS);
      }
      updateLabelsVisibility();
      map.on("moveend", updateLabelsVisibility);

      // 绘制
      // 绘制交互变量
      let draw;
      // 创建矢量数据源和图层
      const vector_source = new ol.source.Vector();
      // 创建矢量图层并关联数据源
      const vector_layer = new ol.layer.Vector({
        source: vector_source,
        style: new ol.style.Style({
          fill: new ol.style.Fill({ color: "rgba(0, 153, 255, 0.2)" }),
          stroke: new ol.style.Stroke({ color: "#0099ff", width: 2 }),
          image: new ol.style.Circle({
            radius: 6,
            fill: new ol.style.Fill({ color: "#0099ff" }),
            stroke: new ol.style.Stroke({ color: "white", width: 1 }),
          }),
        }),
      });
      map.addLayer(vector_layer);

      const dataDisplay = document.getElementById("data-display");

      const countElement = document.getElementById("count");
      const lastFeatureElement = document.getElementById("last-feature-data");

      // 更新要素计数显示
      function updateFeatureCount() {
        const count = vector_source.getFeatures().length;
        countElement.textContent = count;
      }

      // 激活绘制工具的函数
      function activateDraw(type, id) {
        // 移除现有的绘制交互
        if (draw) {
          map.removeInteraction(draw);
        }

        // 清除按钮的active状态
        document.querySelectorAll(".toolbar button").forEach((btn) => {
          btn.classList.remove("active");
        });

        // 如果不是清除操作,则激活相应的绘制工具
        if (type !== "clear") {
          document.getElementById(id).classList.add("active");

          // 创建新的绘制交互
          draw = new ol.interaction.Draw({
            // 绘制的内容会添加到这个数据源
            source: vector_source,
            type: type,
          });
          map.addInteraction(draw);

          // 监听绘制结束事件
          draw.on("drawend", function (event) {
            // 获取绘制的要素
            const feature = event.feature;
            // 获取几何图形
            const geometry = feature.getGeometry();

            // 根据几何类型处理数据
            let data, type;
            switch (geometry.getType()) {
              case "Point":
                type = "点";
                // OpenLayers 默认使用 Web Mercator 投影坐标(米为单位),转换为经纬度坐标(度为单位)
                const coords = ol.proj.toLonLat(geometry.getCoordinates());
                data = `坐标: [${coords[0].toFixed(6)}, ${coords[1].toFixed(6)}]`;
                break;

              case "LineString":
                type = "线";
                // 线的坐标数组
                const lineCoords = geometry
                  .getCoordinates()
                  .map((coord) => ol.proj.toLonLat(coord))
                  .map((coord) => `[${coord[0].toFixed(6)}, ${coord[1].toFixed(6)}]`);
                data = `路径点: [\n  ${lineCoords.join(",\n  ")}\n]`;
                break;

              case "Polygon":
                type = "多边形";
                // 多边形的坐标数组(外层数组是多边形环,取第一个环)
                const polygonCoords = geometry
                  .getCoordinates()[0]
                  .map((coord) => ol.proj.toLonLat(coord))
                  .map((coord) => `[${coord[0].toFixed(6)}, ${coord[1].toFixed(6)}]`);
                data = `顶点: [\n  ${polygonCoords.join(",\n  ")}\n]`;
                break;
            }

            // 显示数据
            dataDisplay.textContent = `绘制了${type}:\n${data}\n\n历史数据可通过vectorSource获取`;
            lastFeatureElement.textContent = data;
            // 更新要素计数
            // 确保添加完成后再更新计数
            setTimeout(() => {
              updateFeatureCount();
            }, 200);
          });
        }
      }

      // 绑定按钮事件
      document.getElementById("draw-point").addEventListener("click", () => {
        activateDraw("Point", "draw-point");
      });

      document.getElementById("draw-line-string").addEventListener("click", () => {
        activateDraw("LineString", "draw-line-string");
      });

      document.getElementById("draw-polygon").addEventListener("click", () => {
        activateDraw("Polygon", "draw-polygon");
      });

      document.getElementById("clear").addEventListener("click", () => {
        // 清除所有绘制的要素
        vector_source.clear();
        // 移除绘制交互
        if (draw) {
          map.removeInteraction(draw);
          draw = null;
        }
        // 清除按钮的active状态
        document.querySelectorAll(".toolbar button").forEach((btn) => {
          btn.classList.remove("active");
          dataDisplay.textContent = "所有绘制已清除";

          // 更新显示
          lastFeatureElement.textContent = "所有要素已清除";
          updateFeatureCount();
        });
      });
    </script>
  </body>
</html>

增加信息窗

效果图:

image

示例代码:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <title>OpenLayers 街景与地名标注</title>
    <!-- 引入 OpenLayers CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@7.5.1/dist/ol.css" />
    <!-- 引入 OpenLayers JavaScript -->
    <script src="https://cdn.jsdelivr.net/npm/ol@7.5.1/dist/ol.js"></script>
    <style>
      #map {
        width: 100%;
        height: 800px;
      }

      .info {
        /* position: absolute;
        top: 10px;
        left: 10px; */
        background: white;
        padding: 10px;
        border-radius: 5px;
        box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
        /* max-width: 250px; */
      }

      /* 绘制工具 */
      .toolbar {
        /* position: absolute;
        top: 10px;
        right: 10px; */
        background: white;
        padding: 10px;
        border-radius: 5px;
        box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
      }
      .toolbar button {
        /* display: block;
        width: 100%; */
        padding: 8px 12px;
        margin-bottom: 5px;
        background: #f5f5f5;
        border: 1px solid #ddd;
        border-radius: 3px;
        cursor: pointer;
        transition: all 0.2s;
      }
      .toolbar button:hover {
        background: #e0e0e0;
      }
      .toolbar button.active {
        background: #4285f4;
        color: white;
        border-color: #4285f4;
      }

      .status-panel {
        background: white;
        padding: 10px;
        border-radius: 5px;
        box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
        max-height: 200px;
        overflow-y: auto;
      }
      .feature-count {
        color: #666;
        font-size: 14px;
        margin-bottom: 10px;
      }

      /* 信息弹窗样式 */
      .popup {
        position: absolute;
        background-color: white;
        padding: 10px;
        border: 1px solid #ccc;
        border-radius: 5px;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        pointer-events: auto;
        display: none;
      }
      .popup-content {
        min-width: 200px;
      }
      .popup-closer {
        position: absolute;
        top: 5px;
        right: 5px;
        cursor: pointer;
        font-size: 16px;
      }
    </style>
  </head>
  <body>
    <div class="info">
      当前缩放级别: <span id="zoom-level">0</span><br />
      标注将在缩放级别 ≥ 8 时显示
    </div>
    <div class="toolbar">
      <button id="draw-point">绘制点标记</button>
      <button id="draw-line-string">绘制线</button>
      <button id="draw-polygon">绘制多边形</button>
      <button id="clear">清除所有绘制</button>
      <button id="save">保存</button>
    </div>

    <div class="data-panel">
      <h4>绘制数据信息:</h4>
      <pre id="data-display"></pre>
    </div>

    <!-- <div class="data-panel">
      <h4>绘制数据信息:</h4>
      <pre id="latest-data"></pre>
      <div class="history-list">
        <h5>历史绘制记录:</h5>
        <ul id="history-data"></ul>
      </div>
    </div> -->
    <div class="status-panel">
      <div class="feature-count">当前已绘制: <span id="count">0</span> 个要素</div>
      <div>
        <h4>最近绘制数据:</h4>
        <pre id="last-feature-data"></pre>
      </div>
    </div>

    <!-- 信息弹窗 -->
    <div id="popup" class="popup">
      <a href="#" class="popup-closer" onclick="hidePopup()">&times;</a>
      <div id="popup-content" class="popup-content"></div>
    </div>

    <div id="map"></div>
    <script>
      var map = new ol.Map({
        target: "map",
        layers: [
          // 底图图层,这里使用 OpenStreetMap
          new ol.layer.Tile({
            // source: new ol.source.OSM(),
            source: new ol.source.XYZ({
              url: "https://webst0{1-4}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
            }),
          }),
        ],
        view: new ol.View({
          center: ol.proj.fromLonLat([108, 28]),
          zoom: 8,
        }),
      });

      var vectorSource = new ol.source.Vector({
        url: "./places.json", // 地名数据文件路径
        format: new ol.format.GeoJSON(),
      });

      var vectorLayer = new ol.layer.Vector({
        source: vectorSource,
        style: function (feature) {
          var textStyle = new ol.style.Text({
            text: feature.get("name"), // 获取地名属性
            font: "12px Arial",
            fill: new ol.style.Fill({
              color: "#000",
            }),
            stroke: new ol.style.Stroke({
              color: "#fff",
              width: 2,
            }),
            offsetY: -15, // 文字相对于点的垂直偏移
          });

          var pointStyle = new ol.style.Style({
            image: new ol.style.Circle({
              radius: 5,
              fill: new ol.style.Fill({
                color: "#ff0000",
              }),
            }),
            text: textStyle,
          });

          return pointStyle;
        },
      });

      map.addLayer(vectorLayer);

      map.on("click", function (evt) {
        var coordinate = evt.coordinate;
        console.log("点击位置经纬度:" + ol.proj.toLonLat(coordinate));

        // 检查点击位置是否有要素
        map.forEachFeatureAtPixel(evt.pixel, function (feature) {
          // 核心过滤逻辑:排除临时要素(只有带 isFinished: true 的才是正式要素)
          // 如果点击的是多边形
          // if (feature.get("isFinished") && feature.getGeometry() instanceof ol.geom.Polygon) {
          //   showPopup(evt.coordinate, feature);
          //   return true; // 停止遍历
          // }
          if (feature.get("isFinished")) {
            showPopup(evt.coordinate, feature);
            return true; // 停止遍历
          }
        });
      });

      var zoomLevelElement = document.getElementById("zoom-level");
      var MIN_ZOOM_FOR_LABELS = 8;
      function updateLabelsVisibility() {
        var currentZoom = map.getView().getZoom();
        zoomLevelElement.textContent = currentZoom.toFixed(1);
        vectorLayer.setVisible(currentZoom >= MIN_ZOOM_FOR_LABELS);
      }
      updateLabelsVisibility();
      map.on("moveend", updateLabelsVisibility);

      // 绘制
      // 绘制交互变量
      let draw;
      // 创建矢量数据源和图层
      const vector_source = new ol.source.Vector();
      // 创建矢量图层并关联数据源
      const vector_layer = new ol.layer.Vector({
        source: vector_source,
        style: new ol.style.Style({
          fill: new ol.style.Fill({ color: "rgba(0, 153, 255, 0.2)" }),
          stroke: new ol.style.Stroke({ color: "#0099ff", width: 2 }),
          image: new ol.style.Circle({
            radius: 6,
            fill: new ol.style.Fill({ color: "#0099ff" }),
            stroke: new ol.style.Stroke({ color: "white", width: 1 }),
          }),
        }),
      });
      map.addLayer(vector_layer);

      const dataDisplay = document.getElementById("data-display");

      const countElement = document.getElementById("count");
      const lastFeatureElement = document.getElementById("last-feature-data");

      const saveElement = document.getElementById("save");

      // 更新要素计数显示
      function updateFeatureCount() {
        const count = vector_source.getFeatures().length;
        countElement.textContent = count;
      }

      // 计算多边形面积(平方米)
      function calculateArea(geometry) {
        const area = geometry.getArea();
        // 转换为平方米(如果需要)
        return Math.round(area) + " 平方米";
      }

      // 激活绘制工具的函数
      function activateDraw(type, id) {
        // 移除现有的绘制交互
        if (draw) {
          map.removeInteraction(draw);
        }

        // 清除按钮的active状态
        document.querySelectorAll(".toolbar button").forEach((btn) => {
          btn.classList.remove("active");
        });

        // 如果不是清除操作,则激活相应的绘制工具
        if (type !== "clear") {
          document.getElementById(id).classList.add("active");

          // 创建新的绘制交互
          draw = new ol.interaction.Draw({
            // 绘制的内容会添加到这个数据源
            source: vector_source,
            type: type,
          });
          map.addInteraction(draw);

          // 监听绘制结束事件
          draw.on("drawend", function (event) {
            // event.stopPropagation();
            draw.setActive(false);

            // 获取绘制的要素
            const feature = event.feature;
            // 获取几何图形
            const geometry = feature.getGeometry();

            // 给正式要素添加标识(用于后续弹窗过滤)
            feature.set("isFinished", true);

            // 添加其他属性(如面积)
            // 多边形
            // feature.set("area", Math.round(geometry.getArea()) + " 平方米");
            // feature.set("name", "多边形" + vector_source.getFeatures().length);
            feature.set("test_data", {
              name: "zs",
              age: 36,
              friend: ["kobe", "lili"],
            });

            // 根据几何类型处理数据
            let data, type;
            switch (geometry.getType()) {
              case "Point":
                type = "点";
                // OpenLayers 默认使用 Web Mercator 投影坐标(米为单位),转换为经纬度坐标(度为单位)
                const coords = ol.proj.toLonLat(geometry.getCoordinates());
                data = `坐标: [${coords[0].toFixed(6)}, ${coords[1].toFixed(6)}]`;
                break;

              case "LineString":
                type = "线";
                // 线的坐标数组
                const lineCoords = geometry
                  .getCoordinates()
                  .map((coord) => ol.proj.toLonLat(coord))
                  .map((coord) => `[${coord[0].toFixed(6)}, ${coord[1].toFixed(6)}]`);
                data = `路径点: [\n  ${lineCoords.join(",\n  ")}\n]`;
                break;

              case "Polygon":
                type = "多边形";
                // 多边形的坐标数组(外层数组是多边形环,取第一个环)
                const polygonCoords = geometry
                  .getCoordinates()[0]
                  .map((coord) => ol.proj.toLonLat(coord))
                  .map((coord) => `[${coord[0].toFixed(6)}, ${coord[1].toFixed(6)}]`);
                data = `顶点: [\n  ${polygonCoords.join(",\n  ")}\n]`;
                break;
            }

            // 显示数据
            dataDisplay.textContent = `绘制了${type}:\n${data}\n\n历史数据可通过vectorSource获取`;
            lastFeatureElement.textContent = data;
            // 更新要素计数
            // 确保添加完成后再更新计数
            setTimeout(() => {
              updateFeatureCount();
            }, 200);
          });
        }
      }

      // 绑定按钮事件
      document.getElementById("draw-point").addEventListener("click", () => {
        activateDraw("Point", "draw-point");
      });

      document.getElementById("draw-line-string").addEventListener("click", () => {
        activateDraw("LineString", "draw-line-string");
      });

      document.getElementById("draw-polygon").addEventListener("click", () => {
        activateDraw("Polygon", "draw-polygon");
      });

      document.getElementById("clear").addEventListener("click", () => {
        // 清除所有绘制的要素
        vector_source.clear();
        // 移除绘制交互
        if (draw) {
          map.removeInteraction(draw);
          draw = null;
        }
        // 清除按钮的active状态
        document.querySelectorAll(".toolbar button").forEach((btn) => {
          btn.classList.remove("active");
          dataDisplay.textContent = "所有绘制已清除";

          // 更新显示
          lastFeatureElement.textContent = "所有要素已清除";
          updateFeatureCount();
        });
      });

      document.getElementById("save").addEventListener("click", () => {
        if (draw) {
          map.removeInteraction(draw);
        }
      });

      // 显示弹窗
      function showPopup(coordinate, feature) {
        const popup = document.getElementById("popup");
        const popupContent = document.getElementById("popup-content");

        // 设置弹窗内容
        // 示例:多边形
        // popupContent.innerHTML = `
        //         <h3>${feature.get("name")}</h3>
        //         <p>面积: ${feature.get("area")}</p>
        //         <p>ID: ${feature.getId()}</p>
        //     `;
        const test_data = feature.get("test_data");
        popupContent.innerHTML = `
                <h1>测试数据</h1>
                <h3>姓名:${test_data.name}</h3>
                <h3>年龄:${test_data.age}</h3>
                <h3>朋友:${JSON.stringify(test_data.friend)}</h3>
            `;
        console.log("dddddd==>", feature.get("test_data"));

        // 设置弹窗位置
        const overlay = new ol.Overlay({
          element: popup,
          positioning: "bottom-center",
          stopEvent: false,
          offset: [0, -10],
        });
        map.addOverlay(overlay);
        overlay.setPosition(coordinate);

        // 显示弹窗
        popup.style.display = "block";
      }

      // 隐藏弹窗
      function hidePopup() {
        const popup = document.getElementById("popup");
        popup.style.display = "none";
      }

      // 鼠标悬停时改变光标样式
      map.on("pointermove", function (e) {
        const pixel = map.getEventPixel(e.originalEvent);
        const hit = map.hasFeatureAtPixel(pixel);
        map.getTargetElement().style.cursor = hit ? "pointer" : "";
      });
    </script>
  </body>
</html>

更多

可参考:https://mp.weixin.qq.com/s/bwdGYOF4Oy2B-cNyX8JnDQ

posted @ 2025-10-11 08:08  ^Mao^  阅读(30)  评论(0)    收藏  举报