Skip to content

图文标注

TIP

选择标注类型后用鼠标在地图上点击添加标注。

代码如下:

点我查看代码
vue
<template>
  <div id="top">
    <el-radio-group v-model="state.type">
      <el-radio label="vector" size="large">Vector Labels</el-radio>
      <el-radio label="overlay" size="large">Overlay Labels</el-radio>
    </el-radio-group>
  </div>
  <div id="label" style="display: none">
    <div id="marker" class="marker" title="Marker">
      <a
        class="address"
        id="address"
        target="_blank"
        href="https://juejin.cn/user/588993965598407"
      >
        标注点
      </a>
    </div>
  </div>
  <div id="map"></div>
</template>

<script lang="ts" setup>
import { onMounted, reactive, onBeforeUnmount } from "vue";
import { Map, View, Feature, Overlay } from "ol";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer";
import { defaults, FullScreen } from "ol/control";
import { XYZ, Vector as VectorSource } from "ol/source";
import { Point } from "ol/geom";
import { ATTRIBUTIONS, SHENZHEN, MAPURL, NANCHANG } from "../../../constants";
import { ElMessage } from "element-plus";
import { createLabelStyle, addVectorLabel, addOverlayLabel } from "./tools";

interface State {
  map: Map | null;
  vectorSource: VectorSource<Point> | null;
  type: string;
}
const state: State = reactive({
  map: null,
  vectorSource: null,
  type: "",
});

const raster = new TileLayer({
  source: new XYZ({
    attributions: ATTRIBUTIONS,
    url: MAPURL,
    maxZoom: 20,
  }),
});

const initMap = () => {
  state.map = new Map({
    //初始化map
    target: "map",
    //地图容器中加载的图层
    layers: [
      //加载瓦片图层数据
      raster,
    ],
    view: new View({
      projection: "EPSG:4326", // 坐标系,有EPSG:4326和EPSG:3 857
      center: SHENZHEN, // 深圳坐标
      //地图初始显示级别
      zoom: 5,
    }),
    //加载控件到地图容器中
    controls: defaults().extend([
      new FullScreen(), //加载全屏显示控件(目前支持非IE内核浏览器)
    ]),
  });
  //实例化Vector要素,通过矢量图层添加到地图容器中
  const iconFeature = new Feature({
    geometry: new Point(SHENZHEN),
    //名称属性
    name: "深圳市",
  });

  iconFeature.setStyle(createLabelStyle(iconFeature));
  //矢量标注的数据源
  state.vectorSource = new VectorSource({
    features: [iconFeature],
  });
  //矢量标注图层
  const vectorLayer = new VectorLayer({
    source: state.vectorSource,
  });
  state.map.addLayer(vectorLayer);

  // 实例化overlay标注,添加到地图容器中
  const marker = new Overlay({
    position: NANCHANG,
    positioning: "center-center",
    element: document.getElementById("marker") || undefined,
    stopEvent: false,
  });
  state.map.addOverlay(marker);
  marker.getElement()!.title = "南昌市";
  const text = new Overlay({
    position: NANCHANG,
    element: document.getElementById("address") || undefined,
  });
  state.map.addOverlay(text);
  text.getElement()!.innerText = marker.getElement()!.title;
};

onMounted(() => {
  initMap();
  if (state.map) {
    state.map.on("click", (evt) => {
      if (!state.type) {
        ElMessage({
          message: "请先选择标注类型后用鼠标在地图上点击添加标注",
          type: "warning",
        });
        return;
      }
      //鼠标单击点坐标
      const coordinate = evt.coordinate;
      if (state.type === "vector") {
        //添加一个新的标注(矢量要素)
        addVectorLabel({
          coordinate,
          vectorSource: state.vectorSource,
        });
      }
      if (state.type === "overlay") {
        //添加新的图文标注(overlay标注)
        addOverlayLabel({ coordinate, map: state.map });
      }
    });
  }
});

onBeforeUnmount(() => {
  if (state.map) {
    state.map.dispose();
    state.map = null;
  }
});
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
#map {
  height: 650px;
}
#top {
  text-align: center;
  height: 50px;
  line-height: 50px;
}
:deep(.marker) {
  width: 20px;
  height: 20px;
  border: 1px solid #088;
  border-radius: 10px;
  background-color: #0ff;
  opacity: 0.5;
}

:deep(.address) {
  text-decoration: none;
  color: #aa3300;
  font-size: 14px;
  font-weight: bold;
  text-shadow: black 0.1em 0.1em 0.2em;
}
</style>

tools 代码如下:

点我查看代码
ts
import { Style, Icon, Text, Fill, Stroke } from 'ol/style'
import { Feature, Overlay, Map } from 'ol'
import { FeatureLike } from 'ol/Feature'
import { Point } from 'ol/geom'
import { Coordinate } from 'ol/coordinate'
import { VectorLabelOptions } from '../../../@types'
/**
 * 创建矢量标注样式函数,设置image为图标ol.style.Icon
 * @param {ol.Feature} feature 要素
 */
export const createLabelStyle = (feature: FeatureLike): Style =>
  new Style({
    image: new Icon(
      /** @type {olx.style.IconOptions} */
      ({
        //设置图标点
        anchor: [0.5, 60],
        //图标起点
        anchorOrigin: 'top-right',
        //指定x值为图标点的x值
        anchorXUnits: 'fraction',
        //指定Y值为像素的值
        anchorYUnits: 'pixels',
        //偏移
        offsetOrigin: 'top-right',
        // offset:[0,10],
        //图标缩放比例
        scale: 0.4,
        //透明度
        opacity: 0.75,
        //图标的url
        src: '/image//blue.png',
      }),
    ),
    text: new Text({
      //位置
      textAlign: 'center',
      //基准线
      textBaseline: 'middle',
      //文字样式
      font: 'normal 14px 微软雅黑',
      //文本内容
      text: feature.get('name'),
      //文本填充样式(即文字颜色)
      fill: new Fill({ color: '#aa3300' }),
      stroke: new Stroke({ color: '#ffcc33', width: 2 }),
    }),
  })

/**
 * 添加一个新的标注(矢量要素)
 * @param {ol.Coordinate} coordinate 坐标点
 * @param {ol.source.Vector} vectorSource 矢量标注的数据源
 */
export const addVectorLabel = ({ coordinate, vectorSource, name = '标注点' }: VectorLabelOptions) => {
  if (vectorSource === null) return
  //新建一个要素 ol.Feature
  const newFeature = new Feature({
    //几何信息
    geometry: new Point(coordinate),
    //名称属性
    name,
  })
  //设置要素的样式
  newFeature.setStyle(createLabelStyle(newFeature))
  //将新要素添加到数据源中
  vectorSource.addFeature(newFeature)
}

/**
 * 添加一个新的图文标注(overlay标注)
 * @param {ol.Coordinate} coordinate 坐标点
 * @param {ol.Map} map 地图实例
 */
export const addOverlayLabel = ({ coordinate, map }: { coordinate: Coordinate, map: Map | null }) => {
  if (map === null) return
  //新增div元素
  const elementDiv = document.createElement('div')
  elementDiv.className = 'marker'
  elementDiv.title = '标注点'
  // 获取id为label的元素
  const overlay = document.getElementById('label')
  if (overlay === null) return
  // 为ID为label的div层添加div子节点
  overlay.appendChild(elementDiv)
  //新增a元素
  const elementA = document.createElement('a')
  elementA.className = 'address'
  elementA.href = '#'
  //设置文本
  setInnerText(elementA, elementDiv.title)
  // 新建的div元素添加a子节点
  elementDiv.appendChild(elementA)
  //实例化图文标注(图形+文本),添加到地图容器中
  const newMarker = new Overlay({
    position: coordinate,
    positioning: 'center-center',
    element: elementDiv,
    stopEvent: false,
  })
  map.addOverlay(newMarker)
  const newText = new Overlay({
    position: coordinate,
    element: elementA,
  })
  map.addOverlay(newText)
}

/**
 * 动态设置元素文本内容(兼容)
 */
function setInnerText(element: HTMLElement, text: string) {
  if (typeof element.textContent == 'string') {
    element.textContent = text
  } else {
    element.innerText = text
  }
}

如有转载或 CV 的请标注本站原文地址