本篇介绍一下使用vue3-openlayers添加tips,比如测量时,实时显示长度、面积等,两种方法:
主要是 vue3-openlayers 中 styleFunc 功能的使用
<template>
<ol-map
:loadTilesWhileAnimating="true"
:loadTilesWhileInteracting="true"
style="width: 100%; height: 100%"
ref="mapRef"
>
<ol-view
ref="view"
:center="center"
:rotation="rotation"
:zoom="zoom"
:projection="projection"
/>
<ol-vector-layer ref="vectorRef">
<ol-source-vector :projection="projection" :wrapX="false">
<ol-interaction-draw
ref="drawRef"
:type="'Polygon'"
:source="source"
@drawend="drawend"
@drawstart="drawstart"
>
<ol-style
:overrideStyleFunction="
feature => handleStyleFunction(feature, showLength, showArea, tips)
"
>
</ol-style>
</ol-interaction-draw>
<ol-interaction-modify
ref="modifyRef"
v-if="modifyFlag"
:features="selectedFeatures"
:pixelTolerance="10"
:insertVertexCondition="handleInsertVertexCondition"
@modifyend="handleModifyEnd"
>
<ol-style
:overrideStyleFunction="
feature => handleModifyStyleFunction(feature, showLength, showArea)
"
>
</ol-style>
</ol-interaction-modify>
<ol-interaction-snap :edge="false" />
</ol-source-vector>
<ol-style :overrideStyleFunction="styleFunction"> </ol-style>
</ol-vector-layer>
<ol-interaction-select
ref="selectRef"
:features="selectedFeatures"
@select="handleSelect"
:condition="selectCondition"
>
<ol-style :overrideStyleFunction="selectStyleFunc"> </ol-style>
</ol-interaction-select>
</ol-map>
<div class="toolbar">
<el-button type="primary" @click="handleClick">{{ drawFlag ? '结束' : '开始' }}绘制</el-button>
<el-button type="warning" :disabled="!drawFlag" @click="handleCancelClick">撤销上步</el-button>
<el-button type="success" @click="handleShowDataClick(true)"
>{{ showLength ? '隐藏' : '显示' }}边长</el-button
>
<el-button type="success" @click="handleShowDataClick(false)"
>{{ showArea ? '隐藏' : '显示' }}面积</el-button
>
</div>
</template>
<script setup lang="ts">
import { Collection } from 'ol';
import { FeatureLike } from 'ol/Feature';
import { LineString, Point } from 'ol/geom';
import { DrawEvent } from 'ol/interaction/Draw';
import { Circle, Fill, Stroke, Style, RegularShape, Text } from 'ol/style';
import { getArea, getLength } from 'ol/sphere.js';
import { start } from 'nprogress';
const center = ref([121, 31]);
const projection = ref('EPSG:4326');
const zoom = ref(5);
const rotation = ref(0);
const features = ref(new Collection()); //保存绘制的features
const selectedFeatures = ref(new Collection()); //选中的features
const source = ref([]);
const mapRef = ref();
const drawRef = ref();
const selectRef = ref();
const modifyRef = ref();
const vectorRef = ref();
const drawFlag = ref(false);
const modifyFlag = ref(false);
const selectConditions = inject('ol-selectconditions');
const showLength = ref(false);
const showArea = ref(false);
const tips = ref('');
const selectCondition = selectConditions.click;
const startTip = '点击鼠标定位第一个顶点';
const labelStyle = new Style({
text: new Text({
font: '14px Microsoft YaHei',
fill: new Fill({
color: 'rgba(255, 255, 255, 1)'
}),
backgroundFill: new Fill({
color: 'rgba(0, 0, 0, 0.7)'
}),
padding: [3, 3, 3, 3],
textBaseline: 'bottom',
offsetY: -15
}),
image: new RegularShape({
radius: 8,
points: 3,
angle: Math.PI,
displacement: [0, 10],
fill: new Fill({
color: 'rgba(0, 0, 0, 0.7)'
})
})
});
const tipStyle = new Style({
text: new Text({
font: '12px Microsoft YaHei',
fill: new Fill({
color: 'rgba(255, 255, 255, 1)'
}),
backgroundFill: new Fill({
color: 'rgba(0, 0, 0, 0.5)'
}),
padding: [2, 2, 2, 2],
textAlign: 'left',
offsetX: 15
})
});
const modifyStyle = new Style({
image: new Circle({
radius: 5,
stroke: new Stroke({
color: 'rgba(255, 255, 255, 0.4)'
}),
fill: new Fill({
color: 'rgba(255, 255, 255, 0.4)'
})
}),
text: new Text({
text: '拖动顶点进行修改',
font: '12px Microsoft YaHei',
fill: new Fill({
color: 'rgba(255, 255, 255, 1)'
}),
backgroundFill: new Fill({
color: 'rgba(0, 0, 0, 0.7)'
}),
padding: [5, 5, 5, 5],
textAlign: 'left',
offsetX: 15
})
});
const edgeStyle = new Style({
text: new Text({
font: '12px Microsoft YaHei',
fill: new Fill({
color: 'rgba(255, 255, 255, 1)'
}),
backgroundFill: new Fill({
color: 'rgba(0, 0, 0, 0.4)'
}),
padding: [2, 2, 2, 2],
textBaseline: 'bottom',
offsetY: -12
}),
image: new RegularShape({
radius: 6,
points: 3,
angle: Math.PI,
displacement: [0, 8],
fill: new Fill({
color: 'rgba(0, 0, 0, 0.4)'
})
})
});
onMounted(() => {
drawRef.value.draw.setActive(false);
// modifyRef.value.modify.setActive(false);
});
const drawstart = (event: Event) => {
console.log(event);
};
const drawend = (event: DrawEvent) => {
console.log(event.feature.getGeometry());
tips.value = startTip;
};
const handleClick = () => {
drawFlag.value = !drawFlag.value;
drawRef.value.draw.setActive(drawFlag.value);
modifyFlag.value = !drawFlag.value;
// modifyRef.value.modify.setActive(!drawFlag.value);
selectRef.value.select.setActive(!drawFlag.value);
selectRef.value.select.getFeatures().clear();
tips.value = startTip;
};
const handleCancelClick = () => {
drawRef.value.draw.removeLastPoint();
};
const handleShowDataClick = isLength => {
if (isLength) {
showLength.value = !showLength.value;
} else {
showArea.value = !showArea.value;
}
drawRef.value.draw.getOverlay().changed();
vectorRef.value.vectorLayer.changed();
};
const handleSelect = e => {
// modifyRef.value.modify.setActive(false);
modifyFlag.value = false;
if (!e.selected.length) {
selectedFeatures.value.clear();
selectRef.value.select.getFeatures().clear();
} else {
nextTick(() => {
modifyFlag.value = true;
// modifyRef.value?.modify.setActive(true);
selectedFeatures.value = e.target.getFeatures();
});
}
};
const handleStyleFunction = (feature: FeatureLike, showLength, showArea, tip) => {
const styles: Array<Style> = [];
const geometry = feature.getGeometry();
const type = geometry.getType();
const coord = geometry.getCoordinates();
const tStyle = tipStyle.clone();
if (type === 'LineString') {
for (let i = 0; i < coord.length - 1; i++) {
const line = new LineString([coord[i], coord[i + 1]]);
const label = formatLength(line);
const point = new Point(line.getCoordinateAt(0.5));
const style = edgeStyle.clone();
style.setGeometry(point);
style.getText().setText(label);
if (showLength) {
styles.push(style);
}
styles.push(
new Style({
geometry: line,
stroke: new Stroke({
color: 'orange',
lineDash: coord.length > 2 && i < coord.length - 2 ? [] : [10],
width: 2
})
})
);
}
const point = new Point(geometry.getLastCoordinate());
tStyle.setGeometry(point);
if (coord.length <= 3) {
tips.value = '继续定位顶点';
} else if (coord.length > 3) {
tips.value = '点击第一个点可结束绘制';
}
}
if (type === 'Polygon') {
if (showArea) {
const label = formatArea(geometry);
const point = geometry.getInteriorPoint();
const style = labelStyle.clone();
style.setGeometry(point);
style.getText().setText(label);
styles.push(style);
}
}
tStyle.getText().setText(tip);
styles.push(tStyle);
return styles;
};
const handleInsertVertexCondition = e => {
return false;
};
const handleModifyStyleFunction = (feature: FeatureLike, showLength, showArea) => {
const layer = mapRef.value.map.getLayers().item(0);
const features = layer.getSource().getFeatures();
const geometry = feature.getGeometry();
const coord = geometry.getCoordinates();
const type = geometry.getType();
const coords = features.map(feature => feature.getGeometry().getCoordinates()).flat(2);
let styles = [];
// 只有鼠标在顶点时才能触发编辑功能
if (coords.find(c => c.toString() === coord.toString())) {
styles.push(
new Style({
geometry: new Point(coord),
image: new Circle({
radius: 6,
fill: new Fill({
color: '#ffff'
}),
stroke: new Stroke({
color: 'red',
width: 2
})
})
}),
modifyStyle
);
}
if (type === 'Point') {
const feature = selectedFeatures.value.getArray()[0];
const geometry = feature.getGeometry();
const coord = feature.getGeometry().getCoordinates()[0];
if (showLength) {
for (let i = 0; i < coord.length - 1; i++) {
const line = new LineString([coord[i], coord[i + 1]]);
const label = formatLength(line);
const point = new Point(line.getCoordinateAt(0.5));
const style = edgeStyle.clone();
style.setGeometry(point);
style.getText().setText(label);
styles.push(style);
}
}
if (showArea) {
const label = formatArea(geometry);
const point = geometry.getInteriorPoint();
const style = labelStyle.clone();
style.setGeometry(point);
style.getText().setText(label);
styles.push(style);
}
}
return styles;
};
const handleModifyEnd = e => {
features.value.push(e.feature); //这里可以把编辑后的feature添加到layer绑定的features中
console.log('modifyend', e.features);
};
const styleFunction = (feature: FeatureLike, currentStyle: Style) => {
const styles = [];
styles.push(
new Style({
fill: new Fill({
color: [128, 128, 255, 0.5]
}),
stroke: new Stroke({
color: 'blue',
width: 2
})
})
);
return styles;
};
const selectStyleFunc = feature => {
const styles = [];
const coord = feature.getGeometry().getCoordinates().flat(1);
for (let i = 0; i < coord.length - 1; i++) {
styles.push(
new Style({
geometry: new Point(coord[i]),
image: new Circle({
radius: 4,
fill: new Fill({
color: '#ffff'
}),
stroke: new Stroke({
color: 'orange',
width: 2
})
})
})
);
}
styles.push(
new Style({
stroke: new Stroke({
color: 'orange',
width: 2
}),
fill: new Fill({
color: '#ffff'
})
})
);
return styles;
};
const formatArea = polygon => {
const area = getArea(polygon, { projection: 'EPSG:4326' });
let output; //根据面积大小实时换算成平方千米
if (area > 10000) {
//平方千米
output = Math.round((area / 1000000) * 100) / 100 + ' km\xB2';
} else {
//平方米
output = Math.round(area * 100) / 100 + ' m\xB2';
}
return output;
};
const formatLength = line => {
const length = getLength(line, { projection: 'EPSG:4326' });
let output; //根据面积大小实时换算成平方千米
if (length > 100) {
output = Math.round((length / 1000) * 100) / 100 + ' km';
} else {
output = Math.round(length * 100) / 100 + ' m';
}
return output;
};
</script>
<style scoped lang="scss">
.toolbar {
position: absolute;
top: 20px;
left: 100px;
}
</style>