图文标注
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
}
}