拖拽添加覆盖物
TIP
拖拽下方的图标到地图指定位置可以生成覆盖物。
对着地图上的覆盖物单击右键可以移除。
代码如下:
点我查看代码
vue
<template>
<div class="top">
<img
v-for="(item, index) in imageList"
:key="index"
:src="item"
draggable="true"
@dragstart="handleDragStart"
/>
</div>
<div id="map" @drop="handleDrop" @dragover="handleDragover">
<div class="menu" ref="menu">
<div class="menu-item" @click="handleDelete">删除</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref, Ref, onBeforeUnmount } from "vue";
import { Map, View, Overlay, Feature } 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 { MAPURL, ATTRIBUTIONS } from "../../../constants";
import { defaults as defaultInteractions } from "ol/interaction";
import { Geometry } from "ol/geom";
import { addVectorLabel } from "./tools";
import Drag from "./drag";
let map: Map | null = null,
menu_overlay: Overlay;
const menu: Ref<HTMLDivElement | null> = ref(null);
const selectIcon: Ref<Feature<Geometry> | null> = ref(null);
const imageList = ["/image/chips.png", "/image/ice_cream.png"];
const raster = new TileLayer({
source: new XYZ({
attributions: ATTRIBUTIONS,
url: MAPURL,
maxZoom: 20,
}),
});
//矢量标注的数据源
const vectorSource: VectorSource = new VectorSource();
//矢量标注图层
const vectorLayer = new VectorLayer({
source: vectorSource,
});
const initMap = () => {
map = new Map({
//初始化map
target: "map",
//地图容器中加载的图层
interactions: defaultInteractions().extend([new Drag()]),
layers: [
//加载瓦片图层数据
raster,
vectorLayer,
],
view: new View({
projection: "EPSG:4326", // 坐标系,有EPSG:4326和EPSG:3 857
center: [0, 0], // 深圳坐标
//地图初始显示级别
zoom: 5,
}),
//加载控件到地图容器中
controls: defaults().extend([
new FullScreen(), //加载全屏显示控件(目前支持非IE内核浏览器)
]),
});
menu_overlay = new Overlay({
element: menu.value || undefined,
positioning: "center-center",
});
map.addOverlay(menu_overlay);
};
const handleDragStart = (event) => {
const src = event.target.src;
event.dataTransfer.setData("src", src);
};
const handleDragover = (event: DragEvent) => {
event.preventDefault();
};
const handleDrop = (event) => {
if (map === null) return;
const imageUrl: string = event.dataTransfer?.getData("src");
const { layerX, layerY }: { layerX: number; layerY: number } = event;
const coordinate = map.getCoordinateFromPixel([layerX, layerY]);
addVectorLabel({
coordinate,
vectorSource,
imageUrl,
});
event.stopPropagation();
};
//右键函数
/*params: map(创建的map对象)*/
const contextmenu = (map: Map) => {
map.getViewport().oncontextmenu = function (event) {
event.preventDefault();
const pixel = map.getEventPixel(event);
const feature = map.forEachFeatureAtPixel(pixel, (feature) => feature);
if (feature) {
const coordinate = map.getEventCoordinate(event);
selectIcon.value = feature as Feature<Geometry>;
menu_overlay.setPosition(coordinate);
}
};
};
const handleDelete = () => {
const source = vectorLayer.getSource();
if (source === null || selectIcon.value === null) return;
source.removeFeature(selectIcon.value);
selectIcon.value = null;
menu_overlay.setPosition(undefined);
};
onMounted(() => {
const mapDom = document.getElementById("map");
//首先禁用document自带的右键功能
mapDom && (mapDom.oncontextmenu = () => false);
initMap();
if (map) {
contextmenu(map);
map.on("singleclick", () => {
menu_overlay.setPosition(undefined);
});
}
});
onBeforeUnmount(() => {
if (map) {
map.dispose();
map = null;
}
});
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
#map {
position: relative;
height: 650px;
}
.top {
height: 50px;
text-align: center;
line-height: 50px;
}
img {
display: inline-block;
width: 20px;
cursor: pointer;
margin: 0 20px;
}
.menu {
position: absolute;
left: 10px;
top: 10px;
width: 60px;
background-color: #f9f9f9;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 99;
border: 1px solid #ccc;
border-bottom: none;
}
.menu-item {
text-align: center;
height: 30px;
line-height: 30px;
border-bottom: 1px solid #ccc;
cursor: pointer;
color: #000;
}
</style>
tools 代码如下:
点我查看代码
ts
import { Style, Icon } from 'ol/style'
import { Feature } from 'ol'
import { Point } from 'ol/geom'
import { VectorLabelOptions } from '../../../@types'
/**
* 创建矢量标注样式函数,设置image为图标ol.style.Icon
* @param {ol.Feature} feature 要素
*/
export const createLabelStyle = (imageUrl: string | undefined): Style =>
new Style({
image: new Icon({
crossOrigin: 'anonymous',
//图标的url
src: imageUrl,
scale: 0.1,
}),
})
/**
* 添加一个新的标注(矢量要素)
* @param {ol.Coordinate} coordinate 坐标点
* @param {ol.source.Vector} vectorSource 矢量标注的数据源
* @param {string} imageUrl 图片地址
* @param {string} name 标注名
*/
export const addVectorLabel = ({
coordinate,
vectorSource,
imageUrl,
name = '标注点',
}: VectorLabelOptions) => {
if (vectorSource === null) return
//新建一个要素 ol.Feature
const newFeature = new Feature({
//几何信息
geometry: new Point(coordinate),
//名称属性
name,
})
//设置要素的样式
newFeature.setStyle(createLabelStyle(imageUrl))
//将新要素添加到数据源中
vectorSource.addFeature(newFeature)
}
drag 代码如下:
点我查看代码
ts
import { FeatureLike } from 'ol/Feature'
import { Pointer as PointerInteraction } from 'ol/interaction'
import { Point } from 'ol/geom'
import { Coordinate } from 'ol/coordinate';
import MapBrowserEvent from 'ol/MapBrowserEvent'
export default class Drag extends PointerInteraction {
coordinate_: Coordinate | null;
cursor_: string;
feature_: FeatureLike | null
previousCursor_: string | undefined
constructor() {
super()
/**
* @type {import("../src/ol/coordinate.js").Coordinate}
* @private
*/
this.coordinate_ = null
/**
* @type {string|undefined}
* @private
*/
this.cursor_ = 'pointer'
/**
* @type {Feature}
* @private
*/
this.feature_ = null
/**
* @type {string|undefined}
* @private
*/
this.previousCursor_ = undefined
}
/**
* @param {import("../src/ol/MapBrowserEvent.js").default} evt Map browser event.
* @return {boolean} `true` to start the drag sequence.
*/
handleDownEvent(evt: MapBrowserEvent<UIEvent>) {
const map = evt.map
const feature = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
return feature
})
if (feature) {
this.coordinate_ = evt.coordinate
this.feature_ = feature
}
return !!feature
}
/**
* @param {import("../src/ol/MapBrowserEvent.js").default} evt Map browser event.
*/
handleDragEvent(evt: MapBrowserEvent<UIEvent>) {
if (this.coordinate_ === null || this.feature_ === null) return
const deltaX = evt.coordinate[0] - this.coordinate_[0]
const deltaY = evt.coordinate[1] - this.coordinate_[1]
const geometry = this.feature_.getGeometry() as Point
geometry.translate(deltaX, deltaY)
this.coordinate_[0] = evt.coordinate[0]
this.coordinate_[1] = evt.coordinate[1]
}
/**
* @param {import("../src/ol/MapBrowserEvent.js").default} evt Event.
*/
handleMoveEvent(evt: MapBrowserEvent<UIEvent>) {
if (this.cursor_) {
const map = evt.map
const feature = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
return feature
})
const element = evt.map.getTargetElement()
if (feature) {
if (element.style.cursor != this.cursor_) {
this.previousCursor_ = element.style.cursor
element.style.cursor = this.cursor_
}
} else if (this.previousCursor_ !== undefined) {
element.style.cursor = this.previousCursor_
this.previousCursor_ = undefined
}
}
}
/**
* @return {boolean} `false` to stop the drag sequence.
*/
handleUpEvent(): boolean {
this.coordinate_ = null
this.feature_ = null
return false
}
}