地理坐标系4326
效果图:
首先下载turf.js:
cnpm i -S @turf/turf
全局引入turf.js
//引入turf.js
import * as turf from '@turf/turf'
Vue.prototype.$turf = turf
使用:
<template>
<div>
<div id="map" style="width: 100vw; height: 100vh"></div>
<div style="position: fixed; top: 20vh; left: 20vw">
<el-button @click="isMeasure = !isMeasure">测量面板</el-button>
<div v-if="isMeasure">
<LjMeasureDraw4326 :map="map"></LjMeasureDraw4326>
</div>
</div>
</div>
</template>
<script>
import "ol/ol.css";
import { Map, View } from "ol";
import { OSM } from "ol/source";
import { Tile as TileLayer } from "ol/layer";
import LjMeasureDraw4326 from "@/components/LjMeasureDraw4326/index.vue";
export default {
components: { LjMeasureDraw4326 },
data() {
return {
map: {},
isMeasure: false,
};
},
created() {},
mounted() {
this.initMap();
},
computed: {},
methods: {
initMap() {
this.map = new Map({
target: "map",
layers: [
new TileLayer({
source: new OSM(),
}),
],
view: new View({
projection: "EPSG:4326",
center: [104.06335863843803, 30.659840919243138], //成都
zoom: 18,
}),
});
},
},
};
</script>
组件 LjMeasureDraw4326/index.vue:
<!-- LjMeasureDraw4326 -->
<template>
<div>
<el-radio-group v-model="isMeasure" @change="clearDrawLayer">
<el-radio :label="true">测量</el-radio>
<el-radio :label="false">绘制</el-radio>
</el-radio-group>
<div style="margin: 20px 0"></div>
<LjButton size="mini">
<select
v-model="currentDrawFeature"
style="
background-color: rgb(66, 66, 66);
color: white;
padding: 2px 7px;
outline: none;
"
@change="drawFeature()"
>
<option value="">请选择...</option>
<option value="Point" label="画点" v-if="!isMeasure"></option>
<option value="LineString">画线</option>
<option value="Polygon">画面</option>
<option value="Circle" v-if="!isMeasure">画圆</option>
</select>
</LjButton>
<LjButton size="mini"
><el-checkbox v-model="enableFreeHand">手绘</el-checkbox></LjButton
>
<LjButton size="mini" @click="draw.removeLastPoint()">撤回</LjButton>
<LjButton size="mini" @click="cancelDraw()">取消</LjButton>
<LjButton size="mini" @click="clearDrawLayer()">清除</LjButton>
</div>
</template>
<script>
import "ol/ol.css";
import { Vector as VectorLayer } from "ol/layer";
import { OSM, Vector as VectorSource } from "ol/source";
import Draw from "ol/interaction/Draw";
import { Fill, Stroke, Style, Text, Circle } from "ol/style";
import LjButton from "@/components/LjButton/index.vue";
export default {
name: "LjMeasureDraw4326",
components: { LjButton },
props: {
map: {
type: Object,
default: () => {},
required: true,
},
},
data() {
return {
draw_source: new VectorSource(),
draw_vector: {},
draw: {},
currentDrawFeature: "", //当前正在绘制的要素类型
enableFreeHand: false, //是否允许手绘
isMeasure: true, //是否开启测量
};
},
mounted() {
this.addDrawLayer();
},
methods: {
formatLength(length) {
// length单位默认为km
if (length >= 1) {
return length.toFixed(2) + " km ";
} else {
return (length * 1000).toFixed(2) + " m ";
}
},
formatArea(area) {
// area默认为㎡
if (area > 10000) {
return (area / 1000000).toFixed(2) + " k㎡ ";
} else {
return area.toFixed(2) + " ㎡ ";
}
},
//添加绘制点线面图层
addDrawLayer() {
this.draw_vector = new VectorLayer({
source: this.draw_source,
//绘制好后,在地图上呈现的样式
style: (feature) => {
if (this.isMeasure) {
return new Style({
fill: new Fill({
color: "rgba(255, 255, 255, 0.2)",
}),
stroke: new Stroke({
//边界样式
color: "#ffcc33",
width: 3,
}),
//点样式继承image
image: new Circle({
radius: 7,
fill: new Fill({
color: "#ffcc33",
}),
}),
text: new Text({
// 位置
textAlign: "center",
// 基准线
textBaseline: "middle",
// 文字样式
font: "bold 18px 微软雅黑",
// 文本内容
text: `${
feature.getGeometry().getType() == "Polygon"
? this.formatArea(feature.get("area"))
: this.formatLength(feature.get("length"))
}`,
// 文字颜色
fill: new Fill({
color: "black",
}),
// 文字背景
stroke: new Stroke({ color: "yellow", width: 10 }),
}),
});
} else {
return new Style({
fill: new Fill({
color: "rgba(255, 255, 255, 0.2)",
}),
stroke: new Stroke({
//边界样式
color: "#ffcc33",
width: 3,
}),
//点样式继承image
image: new Circle({
radius: 7,
fill: new Fill({
color: "#ffcc33",
}),
}),
});
}
},
});
this.map.addLayer(this.draw_vector);
},
//取消绘制
cancelDraw() {
this.map.removeInteraction(this.draw); //移除交互
this.currentDrawFeature = ""; //取消选中要素!!
},
//清空绘制图层
clearDrawLayer() {
this.map.removeInteraction(this.draw); //移除交互
this.draw_vector.getSource().clear(); //清除图层上的所有要素
this.currentDrawFeature = ""; //取消选中要素!!
},
//绘制点线面
drawFeature() {
this.map.removeInteraction(this.draw); //移除交互
if (!this.currentDrawFeature) return; //这里一定要判断
this.draw = new Draw({
source: this.draw_source,
type: this.currentDrawFeature,
//绘制时,在地图上呈现的样式
style: new Style({
fill: new Fill({
color: "rgba(255, 255, 255, 0.2)",
}),
stroke: new Stroke({
color: "#ffcc33",
width: 2,
}),
image: new Circle({
radius: 7,
fill: new Fill({
color: "#ffcc33",
}),
}),
}),
freehand: this.enableFreeHand, //手绘
});
if (this.isMeasure) {
this.draw.on("drawend", (e) => {
if (e.feature.getGeometry().getType() == "Polygon") {
//面的折点坐标数组,如:[[[108,32],[109,33],[110,34],[108,32]]]为三角形
let coordinates_polygon = e.feature.getGeometry().getCoordinates();
// 面积计算,单位为平方米
let area = this.$turf.area(this.$turf.polygon(coordinates_polygon));
e.feature.set("area", area);
}
if (e.feature.getGeometry().getType() == "LineString") {
let coordinates_lineString = e.feature
.getGeometry()
.getCoordinates();
// 长度计算,单位为千米,单位还可以设置为degrees, radians, miles, or kilometers
let length = this.$turf.length(
this.$turf.lineString(coordinates_lineString),
{
units: "kilometers",
}
);
e.feature.set("length", length);
}
});
}
this.map.addInteraction(this.draw);
},
},
destroyed() {
this.clearDrawLayer();
},
watch: {
enableFreeHand: {
handler() {
this.drawFeature();
},
},
},
};
</script>
<style lang="scss" scoped>
//单选框
::v-deep .el-radio__label {
color: white;
}
//多选框
::v-deep .el-checkbox__label {
color: white;
}
</style>
高级封装:
实现代码:
<template>
<div>
<div id="map" style="width: 100vw; height: 100vh"></div>
<div
style="
position: fixed;
top: 29vh;
right: 30vw;
z-index: 999;
width: 50px;
height: 50px;
"
>
<!-- 测绘按钮 -->
<div
@click="showDrawPanel = !showDrawPanel"
style="
display: inline-block;
width: 100%;
height: 100%;
display: flex;
flex-flow: column nowrap;
align-items: center;
justify-content: space-around;
background-color: #08305d;
cursor: pointer;
color: white;
"
>
<i class="el-icon-edit" style="font-size: 24px"></i>
<span style="font-size: 12px">测绘</span>
</div>
<!-- 绘制点线面面板 -->
<LjMeasureDraw4326
:map="map"
v-if="showDrawPanel"
style="
position: absolute;
top: 0px;
left: -345px;
z-index: 99999;
width: 335px;
height: 80px;
background-color: rgba(0, 0, 0, 0.48);
padding: 10px;
"
></LjMeasureDraw4326>
</div>
</div>
</template>
<script>
import "ol/ol.css";
import { Map, View } from "ol";
import { OSM } from "ol/source";
import { Tile as TileLayer } from "ol/layer";
import LjMeasureDraw4326 from "@/components/LjMeasureDraw4326/index.vue";
export default {
components: { LjMeasureDraw4326 },
data() {
return {
map: {},
showDrawPanel: false, //是否显示绘制点线面面板
};
},
mounted() {
this.initMap();
},
methods: {
// 初始化地图
initMap() {
this.map = new Map({
target: "map",
layers: [
new TileLayer({
source: new OSM(),
}),
],
view: new View({
projection: "EPSG:4326",
center: [104.06334237328053, 30.65985125961902],
zoom: 18,
}),
});
},
},
};
</script>
投影坐标系3857
使用:
<template>
<div>
<div id="map" style="width: 100vw; height: 100vh"></div>
<div style="position: fixed; top: 20vh; left: 20vw">
<el-button @click="isMeasure = !isMeasure">测量面板</el-button>
<div v-if="isMeasure">
<LjMeasureDraw3857 :map="map"></LjMeasureDraw3857>
</div>
</div>
</div>
</template>
<script>
import "ol/ol.css";
import { Map, View } from "ol";
import { OSM } from "ol/source";
import { Tile as TileLayer } from "ol/layer";
import LjMeasureDraw3857 from "@/components/LjMeasureDraw3857/index.vue";
export default {
components: { LjMeasureDraw3857 },
data() {
return {
map: {},
isMeasure: false,
};
},
created() {},
mounted() {
this.initMap();
},
computed: {},
methods: {
initMap() {
this.map = new Map({
target: "map",
layers: [
new TileLayer({
source: new OSM(),
}),
],
view: new View({
projection: "EPSG:3857",//默认3857
center: [11584282.38, 3588664.32], //成都
zoom: 18,
}),
});
},
},
};
</script>
组件:LjMeasureDraw3857/index.vue
<template>
<div>
<el-radio-group v-model="isMeasure" @change="clearDrawLayer">
<el-radio :label="true">测量</el-radio>
<el-radio :label="false">绘制</el-radio>
</el-radio-group>
<div style="margin: 20px 0"></div>
<el-button size="mini">
<select
v-model="currentDrawFeature"
style="
background-color: rgb(66, 66, 66);
color: white;
padding: 2px 7px;
outline: none;
"
@change="drawFeature()"
>
<option value="">请选择...</option>
<option value="Point" label="画点" v-if="!isMeasure"></option>
<option value="LineString">画线</option>
<option value="Polygon">画面</option>
<option value="Circle" v-if="!isMeasure">画圆</option>
</select>
</el-button>
<el-button size="mini"
><el-checkbox v-model="enableFreeHand">手绘</el-checkbox></el-button
>
<el-button size="mini" @click="draw.removeLastPoint()">撤回</el-button>
<el-button size="mini" @click="cancelDraw()">取消</el-button>
<el-button size="mini" @click="clearDrawLayer()">清除</el-button>
</div>
</template>
<script>
import "ol/ol.css";
import { Vector as VectorLayer } from "ol/layer";
import { OSM, Vector as VectorSource } from "ol/source";
import Draw from "ol/interaction/Draw";
import { Fill, Stroke, Style, Text, Circle } from "ol/style";
import { getArea, getLength } from "ol/sphere";
export default {
name: "LjMeasureDraw3857",
components: {},
props: {
map: {
type: Object,
default: () => {},
required: true,
},
},
data() {
return {
draw_source: new VectorSource(),
draw_vector: {},
draw: {},
currentDrawFeature: "", //当前正在绘制的要素类型
enableFreeHand: false, //是否允许手绘
isMeasure: true, //是否开启测量
};
},
mounted() {
this.addDrawLayer();
},
methods: {
formatLength(length) {
// length单位默认为m
if (length < 999) {
return length.toFixed(2) + " m ";
} else {
return (length / 1000).toFixed(2) + " km ";
}
},
formatArea(area) {
// area默认为㎡
if (area > 10000) {
return (area / 1000000).toFixed(2) + " k㎡ ";
} else {
return area.toFixed(2) + " ㎡ ";
}
},
//添加绘制点线面图层
addDrawLayer() {
this.draw_vector = new VectorLayer({
source: this.draw_source,
//绘制好后,在地图上呈现的样式
style: (feature) => {
if (this.isMeasure) {
return new Style({
fill: new Fill({
color: "rgba(255, 255, 255, 0.2)",
}),
stroke: new Stroke({
//边界样式
color: "#ffcc33",
width: 3,
}),
//点样式继承image
image: new Circle({
radius: 7,
fill: new Fill({
color: "#ffcc33",
}),
}),
text: new Text({
// 位置
textAlign: "center",
// 基准线
textBaseline: "middle",
// 文字样式
font: "bold 18px 微软雅黑",
// 文本内容
text: `${
feature.getGeometry().getType() == "Polygon"
? this.formatArea(feature.get("area"))
: this.formatLength(feature.get("length"))
}`,
// 文字颜色
fill: new Fill({
color: "black",
}),
// 文字背景
stroke: new Stroke({ color: "yellow", width: 10 }),
}),
});
} else {
return new Style({
fill: new Fill({
color: "rgba(255, 255, 255, 0.2)",
}),
stroke: new Stroke({
//边界样式
color: "#ffcc33",
width: 3,
}),
//点样式继承image
image: new Circle({
radius: 7,
fill: new Fill({
color: "#ffcc33",
}),
}),
});
}
},
});
this.map.addLayer(this.draw_vector);
},
//取消绘制
cancelDraw() {
this.map.removeInteraction(this.draw); //移除交互
this.currentDrawFeature = ""; //取消选中要素!!
},
//清空绘制图层
clearDrawLayer() {
this.map.removeInteraction(this.draw); //移除交互
this.draw_vector.getSource().clear(); //清除图层上的所有要素
this.currentDrawFeature = ""; //取消选中要素!!
},
//绘制点线面
drawFeature() {
this.map.removeInteraction(this.draw); //移除交互
if (!this.currentDrawFeature) return; //这里一定要判断
this.draw = new Draw({
source: this.draw_source,
type: this.currentDrawFeature,
//绘制时,在地图上呈现的样式
style: new Style({
fill: new Fill({
color: "rgba(255, 255, 255, 0.2)",
}),
stroke: new Stroke({
color: "#ffcc33",
width: 2,
}),
image: new Circle({
radius: 7,
fill: new Fill({
color: "#ffcc33",
}),
}),
}),
freehand: this.enableFreeHand, //手绘
});
if (this.isMeasure) {
this.draw.on("drawend", (e) => {
if (e.feature.getGeometry().getType() == "Polygon") {
let area = getArea(e.feature.getGeometry());
e.feature.set("area", area);
}
if (e.feature.getGeometry().getType() == "LineString") {
let length = getLength(e.feature.getGeometry());
e.feature.set("length", length);
}
});
}
this.map.addInteraction(this.draw);
},
},
destroyed() {
this.clearDrawLayer();
},
watch: {
enableFreeHand: {
handler() {
this.drawFeature();
},
},
},
};
</script>
<style lang="scss" scoped>
//单选框
::v-deep .el-radio__label {
color: white;
}
//多选框
::v-deep .el-checkbox__label {
color: white;
}
</style>
封装测距测面
效果1:
MeasureTool2D.js
import "ol/ol.css";
import { Map, View, Feature } from "ol";
import { OSM, Vector as VectorSource, XYZ } from "ol/source";
import { Vector as VectorLayer, Tile as TileLayer } from "ol/layer";
import { Fill, Stroke, Style, Text, Circle } from "ol/style";
import Draw from "ol/interaction/Draw";
//引入turf.js
import * as turf from '@turf/turf'
import { getArea, getLength } from "ol/sphere"; //适用于墨卡托投影
export default class MeasureTool2D {
constructor({ domId_map, proj = "wgs84" }) {
this.domId_map = domId_map
this.divDom = document.getElementById(domId_map)
this.map = null
this.source = new VectorSource()
this.vector = null
this.draw = {}
this.proj = proj //wgs84,mct
}
initMap() {
if (this.proj == 'wgs84') {
this.map = new Map({
target: this.domId_map,
layers: [
new TileLayer({
source: new XYZ({
url:
"http://t3.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=5a257cd2df1b3311723bd77b0de14baf",
}),
visible: true,
// name: "OSM",
}),
],
view: new View({
projection: "EPSG:4326",
center: [115, 39],
zoom: 4,
}),
})
}
if (this.proj == 'mct') {
this.map = new Map({
target: this.domId_map,
layers: [
new TileLayer({
source: new XYZ({
url:
"http://t3.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=5a257cd2df1b3311723bd77b0de14baf",
}),
visible: true,
// name: "OSM",
}),
],
view: new View({
projection: "EPSG:3857",
center: [11584282.38, 3588664.32],
zoom: 4,
}),
})
}
}
//添加绘制图层
addDrawLayer() {
if (this.vector) this.map.removeLayer(this.vector)
this.vector = new VectorLayer({
source: this.source,
//绘制好后,在地图上呈现的样式
style: (feature) => {
return new Style({
fill: new Fill({
color: "rgba(255, 255, 255, 0.2)",
}),
stroke: new Stroke({
//边界样式
color: "#ffcc33",
width: 3,
}),
//点样式继承image
image: new Circle({
radius: 7,
fill: new Fill({
color: "#ffcc33",
}),
}),
text: new Text({
// 位置
textAlign: "center",
// 基准线
textBaseline: "middle",
// 文字样式
font: "bold 18px 微软雅黑",
// 文本内容
text: `${feature.getGeometry().getType() == "Polygon"
? this.formatArea(feature.get("area"))
: this.formatLength(feature.get("length"))
}`,
// 文字颜色
fill: new Fill({
color: "black",
}),
// 文字背景
stroke: new Stroke({ color: "yellow", width: 10 }),
}),
});
},
});
this.map.addLayer(this.vector);
}
formatLength(length) {
// length单位默认为km
if (length >= 1) {
return length.toFixed(2) + " km ";
} else {
return (length * 1000).toFixed(2) + " m ";
}
}
formatArea(area) {
// area默认为㎡
if (area > 10000) {
return (area / 1000000).toFixed(2) + " k㎡ ";
} else {
return area.toFixed(2) + " ㎡ ";
}
}
// 测距
measureDistance() {
if (this.draw) this.map.removeInteraction(this.draw); //移除绘制交互
this.divDom.style.cursor = 'crosshair'
this.draw = new Draw({
source: this.source,
type: 'LineString',
//绘制时,在地图上呈现的样式
style: new Style({
fill: new Fill({
color: "rgba(255, 255, 255, 0.2)",
}),
stroke: new Stroke({
color: "#ffcc33",
width: 2,
}),
image: new Circle({
radius: 3,
fill: new Fill({
color: "#ffcc33",
}),
}),
}),
});
this.map.addInteraction(this.draw);
this.draw.on("drawend", (e) => {
let coordinates_lineString = e.feature
.getGeometry()
.getCoordinates();
// 长度计算,单位为千米,单位还可以设置为degrees, radians, miles, or kilometers
let length = ''
if (this.proj == 'wgs84') {
length = turf.length(
turf.lineString(coordinates_lineString),
{
units: "kilometers",
}
);
}
if (this.proj == 'mct') {
length = getLength(e.feature.getGeometry()) * 0.001
}
e.feature.set("length", length);
});
}
// 测面
measureArea() {
if (this.draw) this.map.removeInteraction(this.draw); //移除绘制交互
this.divDom.style.cursor = 'crosshair'
this.draw = new Draw({
source: this.source,
type: 'Polygon',
//绘制时,在地图上呈现的样式
style: new Style({
fill: new Fill({
color: "rgba(255, 255, 255, 0.2)",
}),
stroke: new Stroke({
color: "#ffcc33",
width: 2,
}),
image: new Circle({
radius: 2,
fill: new Fill({
color: "#ffcc33",
}),
}),
}),
});
this.map.addInteraction(this.draw);
this.draw.on("drawend", (e) => {
//面的折点坐标数组,如:[[[108,32],[109,33],[110,34],[108,32]]]为三角形
let coordinates_polygon = e.feature.getGeometry().getCoordinates();
// 面积计算,单位为平方米
let area = ""
if (this.proj == 'wgs84') {
area = turf.area(turf.polygon(coordinates_polygon));
}
if (this.proj == 'mct') {
area = getArea(e.feature.getGeometry());
}
e.feature.set("area", area);
});
}
// 取消测量
cancelMeasure() {
if (this.draw) this.map.removeInteraction(this.draw); //移除绘制交互
this.divDom.style.cursor = 'default'
}
// 清除
clearMeasure() {
this.map.removeInteraction(this.draw); //移除交互
this.source.clear(); //清除图层上的所有要素
this.divDom.style.cursor = 'default'
}
}
测试:
<template>
<div class="main">
<div style="position: absolute;z-index:99;left:30%;top:20px">
<div>
<el-button @click="state.measure.measureDistance()">测距</el-button>
<el-button @click="state.measure.measureArea()">测面</el-button>
<el-button @click="state.measure.cancelMeasure()">取消测量</el-button>
<el-button @click="state.measure.clearMeasure()">清除</el-button>
</div>
</div>
<div id="olContainer" style="height: 100vh; width: 100vw"></div>
</div>
</template>
<script setup>
import { reactive } from 'vue';
import MeasureTool2D from './MeasureTool2D'
const state = reactive({
measure: null
})
onMounted(() => {
let measure = new MeasureTool2D({ domId_map: 'olContainer', proj: "wgs84" })
measure.initMap()
measure.addDrawLayer()
state.measure = measure
})
</script>
<style lang="scss" scoped>
.main {
position: relative;
height: 100vh;
width: 100vw;
#olContainer {
position: absolute;
height: 100vh;
width: 100vw;
}
}
</style>
效果2:
MeasureTool2D.js:
import { Map, View } from "ol";
import Feature from "ol/Feature";
import Draw from 'ol/interaction/Draw';
import Overlay from 'ol/Overlay';
import { Circle as CircleStyle, Fill, Stroke, Style } from 'ol/style';
import { LineString, Polygon } from 'ol/geom';
import { Vector as VectorSource, XYZ } from 'ol/source';
import { Vector as VectorLayer, Tile as TileLayer } from 'ol/layer';
import { getArea, getLength } from 'ol/sphere';
import { unByKey } from 'ol/Observable';
/**
* 二维测量工具类
*/
export default class MeasureTool2D {
constructor({ map }) {
this.map = map
this.source = new VectorSource();
this.vector = new VectorLayer({
source: this.source,
style: {
'fill-color': 'rgba(255, 255, 255, 0.2)',
'stroke-color': '#ffff00',
'stroke-width': 2,
'circle-radius': 7,
'circle-fill-color': '#ffff00',
},
});
this.map.addLayer(this.vector)
this.currentDrawFeature = null;//当前绘制要素
this.helpTooltipElement = null;
this.helpTooltip;
this.measureTooltipElement;
this.measureTooltip;
this.continuePolygonMsg = '单击绘制多边形,双击结束';
this.continueLineMsg = '单击绘制画线,双击结束';
this.vector.setZIndex(999)
this.draw;
this.overlayGroups = []
this.typeOptions = {
'area': 'Polygon',
'length': 'LineString'
}
this.geometry = null;
}
pointerMoveHandler(evt) {
if (evt.dragging) {
return;
}
let helpMsg = '单击开始绘制';
if (this.currentDrawFeature) {
const geom = this.currentDrawFeature.getGeometry();
if (geom instanceof Polygon) {
helpMsg = this.continuePolygonMsg;
} else if (geom instanceof LineString) {
helpMsg = this.continueLineMsg;
}
}
if (this.helpTooltipElement) {
this.helpTooltipElement.innerHTML = helpMsg;
this.helpTooltip.setPosition(evt.coordinate);
this.helpTooltipElement.classList.remove('hidden');
}
}
formatLength(line) {
const length = getLength(line);
let output;
if (length > 100) {
output = Math.round((length / 1000) * 100) / 100 + ' ' + '公里';
} else {
output = Math.round(length * 100) / 100 + ' ' + '米';
}
return output;
}
formatArea(polygon) {
const area = getArea(polygon);
let output;
if (area > 10000) {
output = Math.round((area / 1000000) * 100) / 100 + ' ' + '平方公里';
} else {
output = Math.round(area * 100) / 100 + ' ' + '平方米';
}
return output;
}
addInteraction(typeSelect) {
if (this.draw) this.destory()
const type = this.typeOptions[typeSelect];
if (!type) {
return;
}
this.draw = new Draw({
source: this.source,
type: type,
style: new Style({
fill: new Fill({
color: 'rgba(255, 255, 255, 0.2)',
}),
stroke: new Stroke({
color: 'rgba(0, 142, 255, 0.5)',
lineDash: [10, 10],
width: 2,
}),
image: new CircleStyle({
radius: 5,
stroke: new Stroke({
color: 'rgba(0, 142, 255, 0.7)',
}),
fill: new Fill({
color: 'rgba(255, 255, 255, 0.2)',
}),
}),
}),
});
this.map.addInteraction(this.draw);
this.createMeasureTooltip();
this.createHelpTooltip();
this.map.on('pointermove', (evt) => this.pointerMoveHandler(evt));
this.map.getViewport().addEventListener('mouseout', () => {
this.helpTooltipElement.classList.add('hidden');
});
let listener;
this.draw.on('drawstart', (evt) => {
this.currentDrawFeature = evt.feature;
evt.feature.type = typeSelect
let tooltipCoord = evt.coordinate;
listener = this.currentDrawFeature.getGeometry().on('change', (evt) => {
const geom = evt.target;
const p = geom.clone();
const circleIn4326 = p
.transform(
this.map.getView().getProjection(),
"EPSG:3857"
);
let output;
if (geom instanceof Polygon) {
output = this.formatArea(circleIn4326);
tooltipCoord = geom.getInteriorPoint().getCoordinates();
} else if (geom instanceof LineString) {
output = this.formatLength(circleIn4326);
tooltipCoord = geom.getLastCoordinate();
}
this.measureTooltipElement.innerHTML = output;
this.measureTooltip.setPosition(tooltipCoord);
this.geometry = geom
});
});
this.map.on('singleclick', () => {
if (typeSelect !== "length") return;
const element = document.createElement('div');
element.className = 'ol-tooltip ol-tooltip-static';
if (this.geometry.getCoordinates() && this.geometry.getCoordinates().length < 3) element.innerHTML = '开始'
else element.innerHTML = this.measureTooltipElement.innerHTML
const lastTwo = this.geometry.getCoordinates()[this.geometry.getCoordinates().length - 2]
const tooltip = new Overlay({
element: element,
offset: [0, -15],
positioning: 'bottom-center',
stopEvent: false,
insertFirst: false,
});
tooltip.setPosition([lastTwo[0], lastTwo[1]])
this.map.addOverlay(tooltip);
this.overlayGroups.push(tooltip)
})
this.draw.on('drawend', (evt) => {
this.currentDrawFeature = null;
this.measureTooltipElement = null;
this.createMeasureTooltip();
unByKey(listener);
setTimeout(() => {
this.destory()
}, 300);
});
}
createHelpTooltip() {
if (this.helpTooltipElement) {
this.helpTooltipElement.parentNode.removeChild(this.helpTooltipElement);
}
this.helpTooltipElement = document.createElement('div');
this.helpTooltipElement.className = 'ol-tooltip hidden';
this.helpTooltip = new Overlay({
element: this.helpTooltipElement,
offset: [15, 0],
positioning: 'center-left',
});
this.map.addOverlay(this.helpTooltip);
}
createMeasureTooltip() {
if (this.measureTooltipElement) {
this.measureTooltipElement.parentNode.removeChild(this.measureTooltipElement);
}
this.measureTooltipElement = document.createElement('div');
this.measureTooltipElement.className = 'ol-tooltip ol-tooltip-measure';
this.measureTooltip = new Overlay({
element: this.measureTooltipElement,
offset: [0, -15],
positioning: 'bottom-center',
stopEvent: false,
insertFirst: false,
});
this.map.addOverlay(this.measureTooltip);
this.overlayGroups.push(this.measureTooltip)
}
clearMeasure() {
if (this.overlayGroups.length > 1) {
this.overlayGroups.forEach((el) => {
this.map.removeOverlay(el);
})
}
this.vector.getSource().clear()
this.overlayGroups = []
}
destory() {
this.map.un('pointermove', this.pointerMoveHandler);
this.map.removeOverlay(this.helpTooltip);
this.map.removeOverlay(this.measureTooltip);
this.map.removeInteraction(this.draw);
this.draw = null
}
}
测试:
<template>
<div class="main">
<div style="position: absolute;z-index:99;left:30%;top:20px">
<div>
<el-button @click="state.measure.addInteraction('length')">测距</el-button>
<el-button @click="state.measure.addInteraction('area')">测面</el-button>
<!-- <el-button @click="state.measure.cancelMeasure()">取消测量</el-button> -->
<el-button @click="state.measure.clearMeasure()">清除</el-button>
</div>
</div>
<div id="olContainer" style="height: 100vh; width: 100vw"></div>
</div>
</template>
<script setup>
import { reactive } from 'vue';
import MeasureTool2D from './MeasureTool2D'
import { Map, View } from "ol";
import { Vector as VectorSource, XYZ } from 'ol/source';
import { Vector as VectorLayer, Tile as TileLayer } from 'ol/layer';
const state = reactive({
measure: null
})
onMounted(() => {
const map = new Map({
target: "olContainer",
layers: [
new TileLayer({
source: new XYZ({
url:
"http://t3.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=5a257cd2df1b3311723bd77b0de14baf",
}),
visible: true,
}),
],
view: new View({
projection: "EPSG:4326",
center: [115, 39],
zoom: 4,
}),
})
state.measure = new MeasureTool2D({ map })
})
</script>
<style lang="scss" scoped>
.main {
position: relative;
height: 100vh;
width: 100vw;
#olContainer {
position: absolute;
height: 100vh;
width: 100vw;
}
}
</style>