测距组件:
import { useEffect, useState } from 'react';
import VectorSource from 'ol/source/Vector';
import { Draw, Modify } from 'ol/interaction';
import {
Stroke,
Style,
Fill,
Circle as CircleStyle,
RegularShape,
Text,
} from 'ol/style';
import { useMap } from '&/core/hooks';
import { Vector as VectorLayer } from 'ol/layer';
import { getArea, getLength } from 'ol/sphere';
import { LineString, Point } from 'ol/geom';
const style = new Style({
fill: new Fill({
color: '#ffcc33',
}),
stroke: new Stroke({
color: '#ffcc33',
width: 2,
}),
image: new CircleStyle({
radius: 5,
stroke: new Stroke({
color: 'rgba(0, 0, 0, 0.7)',
}),
fill: new Fill({
color: 'rgba(255, 255, 255, 0.2)',
}),
}),
});
// 标签样式
const labelStyle = new Style({
text: new Text({
font: '14px Calibri,sans-serif',
fill: new Fill({
color: '#ffcc33',
}),
backgroundFill: new Fill({
color: 'red',
}),
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 Calibri,sans-serif',
fill: new Fill({
color: 'green',
}),
backgroundFill: new Fill({
color: '#fff',
}),
padding: [2, 2, 2, 2],
textAlign: 'left',
offsetX: 15,
}),
});
const modifyStyle = new Style({
image: new CircleStyle({
radius: 5,
stroke: new Stroke({
color: 'blue',
}),
fill: new Fill({
color: 'blue',
}),
}),
text: new Text({
text: 'Drag to modify',
font: '12px Calibri,sans-serif',
fill: new Fill({
color: 'rgba(255, 255, 255, 1)',
}),
backgroundFill: new Fill({
color: 'rgba(0, 0, 0, 0.7)',
}),
padding: [2, 2, 2, 2],
textAlign: 'left',
offsetX: 15,
}),
});
const segmentStyle = new Style({
text: new Text({
font: '12px Calibri,sans-serif',
fill: new Fill({
color: 'purple',
}),
backgroundFill: new Fill({
color: '#fff',
}),
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)',
}),
}),
});
const formatLength = function (line) {
const length = getLength(line);
let output;
if (length > 100) {
output = Math.round((length / 1000) * 100) / 100 + ' km';
} else {
output = Math.round(length * 100) / 100 + ' m';
}
return output;
};
const formatArea = function (polygon) {
const area = getArea(polygon);
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 source = new VectorSource();
const modify = new Modify({ source: source, style: modifyStyle });
let tipPoint;
function styleFunction(feature, segments, drawType, tip) {
const styles = [style];
const geometry = feature.getGeometry();
const type = geometry.getType();
let point, label, line;
if (!drawType || drawType === type) {
if (type === 'Polygon') {
point = geometry.getInteriorPoint();
label = formatArea(geometry);
line = new LineString(geometry.getCoordinates()[0]);
} else if (type === 'LineString') {
point = new Point(geometry.getLastCoordinate());
label = formatLength(geometry);
line = geometry;
}
}
if (segments && line) {
let count = 0;
line.forEachSegment(function (a, b) {
const segment = new LineString([a, b]);
const label = formatLength(segment);
if (segmentStyles.length - 1 < count) {
segmentStyles.push(segmentStyle.clone());
}
const segmentPoint = new Point(segment.getCoordinateAt(0.5));
segmentStyles[count].setGeometry(segmentPoint);
segmentStyles[count].getText().setText(label);
styles.push(segmentStyles[count]);
count++;
});
}
if (label) {
labelStyle.setGeometry(point);
labelStyle.getText().setText(label);
styles.push(labelStyle);
}
if (
tip &&
type === 'Point' &&
!modify.getOverlay().getSource().getFeatures().length
) {
tipPoint = geometry;
tipStyle.getText() && tipStyle.getText().setText(tip);
styles.push(tipStyle);
}
return styles;
}
const vector = new VectorLayer({
source: source,
style: function (feature) {
return styleFunction(feature, true);
},
});
function addInteraction(map, setDraw) {
const drawType = 'LineString';
const activeTip = '点击开始绘制线';
const idleTip = '点击开始测量';
let tip = idleTip;
const draw = new Draw({
source: source,
type: drawType,
style: function (feature) {
return styleFunction(feature, true, drawType, tip);
},
});
setDraw(draw);
draw.on('drawstart', function () {
// 每次先清除
source.clear();
modify.setActive(false);
tip = activeTip;
});
draw.on('drawend', function () {
modifyStyle.setGeometry(tipPoint);
modify.setActive(true);
map.once('pointermove', function () {
modifyStyle.setGeometry();
});
tip = idleTip;
});
modify.setActive(true);
map.addInteraction(draw);
}
const segmentStyles = [segmentStyle];
const useDrawLine = (visible) => {
const [draw, setDraw] = useState(null);
const map = useMap();
useEffect(() => {
if (visible) {
map.addLayer(vector);
map.addInteraction(modify);
addInteraction(map, setDraw);
} else {
map.removeInteraction(draw);
map.removeLayer(vector);
source.clear();
}
}, [visible]);
};
export default useDrawLine;
DrawLine:通过visible来控制开始测量或者结束测量
import React, { useEffect, useState } from 'react';
import useDrawLine from './useDrawLine';
export default (props) => {
const [visible, setVisible] = useState(false);
useDrawLine(visible);
return (
<div className="drawLine_btn">
<span role="button" tabIndex={0} onClick={() => setVisible(!visible)}>
{!visible ? '开始测量' : '结束测量'}
</span>
</div>
);
};
把组件引入<Map><DrawLine /></Map>即可实现
效果图: