Browse Source

add measure func

pull/214/head
lixin 1 year ago
parent
commit
4fdc6b00c7
5 changed files with 394 additions and 1 deletions
  1. +384
    -0
      src/components/Map/ToolBar.vue
  2. BIN
      src/components/Map/area.png
  3. BIN
      src/components/Map/clear.png
  4. BIN
      src/components/Map/distance.png
  5. +10
    -1
      src/views/dashboard/components/OneMap.vue

+ 384
- 0
src/components/Map/ToolBar.vue View File

@@ -0,0 +1,384 @@
<template>
<div class="map-toolbar">
<img class="box" src="./distance.png" @click="measure('LineString')">
<img class="box" src="./area.png" @click="measure('Polygon')">
<img class="box" src="./clear.png" @click="clearMeasure">
</div>
</template>
<script>
import { reactive, toRefs, watch } from 'vue'
import { Feature } from 'ol'
import VectorSource from 'ol/source/Vector'
import VectorLayer from 'ol/layer/Vector'
import { Stroke, Style, Fill } from 'ol/style'
import CircleStyle from 'ol/style/Circle'
import { Draw } from 'ol/interaction'
import { getArea, getLength } from 'ol/sphere'
import { unByKey } from 'ol/Observable'
import { Overlay } from 'ol'
import { LineString, Polygon } from 'ol/geom'
import { transform, fromLonLat } from 'ol/proj'
import { styleList } from '@/utils/style.js'

// turf 用于简单的空间计算
import * as turf from '@turf/turf'
export default {
props: {
map: {
type: Object,
default: () => {}
}
},
setup(props, { emit }) {
const data = reactive({
mapData: null,
mapMouseMove: null,
helpTooltipElement: null,
feature: null,
draw: null,
listener: null,
measureTooltipElement: null,
measureTooltip: null,
drawLayers: [],
drawElements: []
})

watch(() => props.map, (map) => {
data.mapData = map
cpRPA.setDistanceFn(distance)
cpRPA.setLatlng2PxFn(latlng2Px)
cpRPA.setPx2LatlngFn(px2Latlng)
})

function distance(p1, p2) {
const point1 = turf.point([parseFloat(p1.lng), parseFloat(p1.lat)])
const point2 = turf.point([parseFloat(p2.lng), parseFloat(p2.lat)])
return turf.distance(point1, point2, { units: 'meters' })
}

function latlng2Px(latlng) {
const pixel = data.mapData.getPixelFromCoordinate(fromLonLat([latlng.lng, latlng.lat]))
return {
x: pixel[0],
y: pixel[1]
}
}

function px2Latlng(px) {
const coord = transform(
data.mapData.getCoordinateFromPixel(px),
'EPSG:3857',
'EPSG:4326'
)
return {
lat: coord[1],
lng: coord[0]
}
}

/**
* 清除测量
*/
const clearMeasure = () => {
for (let i = 0; i < data.drawLayers.length; i++) {
data.mapData.removeLayer(data.drawLayers[i])
}
for (let i = 0; i < data.drawElements.length; i++) {
data.mapData.removeOverlay(data.drawElements[i])
}
data.drawLayers = []
data.drawElements = []
}

/**
* 测距
*/
const measure = (measureType) => {
// 创建一个数据源,用于放置绘制过程中和绘制结束后的线段
const source = new VectorSource()
// 添加图层,用来放置数据源
const layer = new VectorLayer({
source: source,
style: new Style({
fill: new Fill({
color: 'rgba(255, 255, 255, 0.2)'
}),
stroke: new Stroke({
color: '#ffcc33',
width: 4
}),
image: new CircleStyle({
radius: 7,
fill: new Fill({
color: '#ffcc33'
})
})
})
})
data.mapMouseMove = data.mapData.on('pointermove', (evt) => {
if (evt.dragging) {
return
}
let helpMsg = '单击开始测量'
if (data.feature) {
helpMsg = '双击结束测量'
}
data.helpTooltipElement.innerHTML = helpMsg
data.helpTooltip.setPosition(evt.coordinate)
data.helpTooltipElement.classList.remove('hidden')
})

data.mapData.getViewport().addEventListener('mouseout', () => {
data.helpTooltipElement.classList.add('hidden')
})

data.draw = new Draw({
source,
// 测量方式 polygon 或者 lineString
type: measureType,
style: new Style({
fill: new Fill({
color: 'rgba(255, 255, 255, 0.2)'
}),
stroke: new Stroke({
color: 'rgba(0, 0, 0, 0.5)',
lineDash: [10, 10],
width: 4
}),
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)'
})
})
})
})

// 开始进行绘制
data.draw.on('drawstart', (evt) => {
data.feature = evt.feature
let tooltipCoord = evt.coordinate
data.listener = data.feature.getGeometry().on('change', (evt) => {
const geom = evt.target
// let output
if (geom instanceof Polygon) {
// output = formatArea(geom)
tooltipCoord = geom.getInteriorPoint().getCoordinates()
} else if (geom instanceof LineString) {
// output = formatLength(geom)
tooltipCoord = geom.getLastCoordinate()
}

// data.measureTooltipElement.innerHTML = output
data.measureTooltip.setPosition(tooltipCoord)
})
})

// 绘制完成
data.draw.on('drawend', (evt) => {
data.measureTooltipElement.className = 'ol-tooltip ol-tooltip-static'
data.measureTooltip.setOffset([0, -7])
data.feature = null
data.measureTooltipElement = null
// createMeasureTooltip()
data.mapData.removeInteraction(data.draw)
unByKey(data.listener)
unByKey(data.mapMouseMove)

var feature = evt.feature
var geometry = feature.getGeometry()
var coordinate = geometry.getCoordinates()
if (geometry instanceof LineString) {
console.log(coordinate)
} else if (geometry instanceof Polygon) {
const lngLats = []
const len = coordinate[0].length
for (let i = 0; i < len - 1; i++) {
const coord = transform(
coordinate[0][i],
'EPSG:3857',
'EPSG:4326'
)
lngLats.push(
{
lat: coord[1],
lng: coord[0]
}
)
}

var polyline = cpRPA.setOptions({
polygon: lngLats,
rotate: 0,
space: 20
})

drawPolyline(formatPolyline(polyline))
}
})

createHelpTooltip()
createMeasureTooltip()
data.mapData.addLayer(layer)
data.drawLayers.push(layer)
data.mapData.addInteraction(data.draw)
}

const drawPolyline = (lngLats) => {
const route = new LineString(lngLats)
const routeFeature = new Feature({
type: 'route',
geometry: route
})
const vectorLayer = new VectorLayer({
source: new VectorSource({
features: [routeFeature]
}),
style: function(feature) {
return styleList[feature.get('type')]
}
})
data.mapData.addLayer(vectorLayer)
}

const formatPolyline = (coordinate) => {
const lngLats = []
coordinate.map((item) => {
lngLats.push(fromLonLat([parseFloat(item.lng), parseFloat(item.lat)]))
})
return lngLats
}

// 计算长度
const formatLength = (line) => {
// ol自带的计算线段长度方法
const length = getLength(line)
let output
// 长度大于100米单位为km, 否则为m
if (length > 100) {
output = Math.round((length / 1000) * 100) / 100 + ' ' + 'km'
} else {
output = Math.round(length * 100) / 100 + ' ' + 'm'
}
return output
}

// 计算面积
const formatArea = (polygon) => {
const proj = data.mapData.getView().getProjection()
const area = getArea(polygon, { projection: proj })
let output
if (area > 10000) {
output = Math.round((area / 1000000 * 100) / 100) + ' ' + 'km<sup>2</sup>'
} else {
output = Math.round(area * 100) / 100 + ' ' + 'm<sup>2</sup>'
}
return output
}

const createMeasureTooltip = () => {
if (data.measureTooltipElement) {
data.measureTooltipElement.parentNode.removeChild(data.measureTooltipElement)
}
data.measureTooltipElement = document.createElement('div')
data.measureTooltipElement.className = 'ol-tooltip ol-tooltip-measure'
data.measureTooltip = new Overlay({
element: data.measureTooltipElement,
offset: [0, -15],
positioning: 'bottom-center',
stopEvent: false,
insertFirst: false
})
data.drawElements.push(data.measureTooltip)
data.mapData.addOverlay(data.measureTooltip)
}

const createHelpTooltip = () => {
if (data.helpTooltipElement) {
data.helpTooltipElement.parentNode.removeChild(data.helpTooltipElement)
}
data.helpTooltipElement = document.createElement('div')
data.helpTooltipElement.className = 'ol-tooltip hidden'
data.helpTooltip = new Overlay({
element: data.helpTooltipElement,
offset: [15, 0],
positioning: 'center-left'
})
data.mapData.addOverlay(data.helpTooltip)
}

return {
...toRefs(data),
measure,
clearMeasure
}
}
}
</script>

<style scoped lang="scss">
.map-toolbar {
width: 32px;
height: 100px;
position: absolute;
right: 10px;
bottom: 10px;
background-color: #fff;
.box {
width: 32px;
height: 32px;
background-size: 100% 100%;
display: inline-block;
cursor: pointer;
}

}

::deep(.hidden) {
display: none;
}

::deep(.ol-tooltip) {
position: relative;
background: rgba(0, 0, 0, 0.5);
border-radius: 4px;
color: white;
padding: 4px 8px;
opacity: 0.7;
white-space: nowrap;
font-size: 12px;
cursor: default;
user-select: none;
}

::deep(.ol-tooltip-measure) {
opacity: 1;
font-weight: bold;
}

::deep(.ol-tooltip-static) {
background-color: #ffcc33;
color: black;
border: 1px solid white;
}

::deep(.ol-tooltip-measure:before),
::deep(.ol-tooltip-static:before) {
border-top: 6px solid rgba(0, 0, 0, 0.5);
border-right: 6px solid transparent;
border-left: 6px solid transparent;
content: "";
position: absolute;
bottom: -6px;
margin-left: -7px;
left: 50%;
}

::deep(.ol-tooltip-static:before) {
border-top-color: #ffcc33;
}

</style>

BIN
src/components/Map/area.png View File

Before After
Width: 32  |  Height: 32  |  Size: 444B

BIN
src/components/Map/clear.png View File

Before After
Width: 32  |  Height: 32  |  Size: 1023B

BIN
src/components/Map/distance.png View File

Before After
Width: 32  |  Height: 32  |  Size: 242B

+ 10
- 1
src/views/dashboard/components/OneMap.vue View File

@@ -35,6 +35,7 @@ import { Tile, Vector as VectorLayer } from 'ol/layer'
import { transform, fromLonLat } from 'ol/proj'
import { Style, Icon, Text, Fill, Circle } from 'ol/style'
import * as control from 'ol/control'
import * as interaction from 'ol/interaction'
import uav_icon from '@/assets/images/airport.png'
import warningIcon from '@/assets/gis/images/fire.png'
import warningSelectIcon from '@/assets/gis/images/fire_select.png'
@@ -53,10 +54,11 @@ import FireAlarm from './FireAlarm.vue'
import WarningDrawer from './WarningDrawer.vue'
import gcj02Mecator from '@/utils/map/gcj02Mecator.js'
import { useInspectionStore } from '@/store/modules/inspection.js'
import ToolBar from '@/components/Map/ToolBar.vue'

export default {
name: 'OneMap',
components: { AirInfo, ProblemInfo, SuppliesInfo, FireAlarm, WarningDrawer, MonitorVideo },
components: { AirInfo, ProblemInfo, SuppliesInfo, FireAlarm, WarningDrawer, MonitorVideo, ToolBar },
props: {
id: {
type: String,
@@ -111,6 +113,10 @@ export default {
}
})

const measureDistance = () => {

}

// 点击展示属性
const click2ShowInfo = (e) => {
// 防止冒泡
@@ -205,6 +211,9 @@ export default {
attribution: false,
rotate: false,
zoom: false
}),
interactions: interaction.defaults({
doubleClickZoom: false // 屏蔽双击放大事件
})
})
data.map.addLayer(wmsSource)

Loading…
Cancel
Save