<template>
<div class="container">
<h1>GeoJSON</h1>
<p>
<h2>添加元素状态</h2>
<ul style="padding-left: 20px">
<li>可添加点线面元素到页面</li>
<li>可获取GeoJSON</li>
</ul>
</p>
<p>
<h2>编辑原属状态</h2>
<ul style="padding-left: 20px">
<li>可删除元素</li>
<li>获取元素元素属性</li>
</ul>
</p>
<p>
<h2>展示状态</h2>
<ul style="padding-left: 20px">
<li>根据GeoJSON展示元素</li>
<li>获取元素元素属性</li>
</ul>
</p>
<div id="vue-openlayers"></div>
<!-- 状态 -->
<div>
状态:
<select v-model="status">
<option value="drawing">添加元素状态</option>
<option value="set-get-properties">编辑元素属性</option>
<option value="display">展示状态</option>
</select>
<select id="type" v-model="type" v-if="status === 'drawing'">
<option v-for="item in tools" :key="item.value" :value="item.value">画 - {{ item.label }}</option>
</select>
<button v-if="status === 'drawing'" @click="type='None'">停止作画</button>
</div>
<div v-if="status === 'drawing' || status === 'set-get-properties'">
<fieldset style="margin: 5px;padding:5px">
<legend>
<button @click="get_GeoJSON_fromMap"> 将画布内容保存到 textarea</button>
<button @click="downJsonStrFile">下载json</button>
</legend>
<textarea v-model="jsonStr"></textarea>
</fieldset>
</div>
<div v-if="status === 'display'">
<fieldset style="margin: 5px;padding:5px">
<legend>
<button @click="set_GeoJSON_toMap"> 将textarea内容 渲染到画布</button>
</legend>
<textarea v-model="jsonStr"></textarea>
<button @click="downJsonStrFile">下载json</button>
</fieldset>
</div>
<div style="margin-top:50px" v-if="activeFeature">
<fieldset style="margin: 5px;padding:5px">
<legend>
{{ activeFeature ? '有选中' : '无选中' }}
<button @click="getActiveFeatureProperties">将选中元素的属性 输入 到textarea</button>
<button @click="setActiveFeatureProperties">将textarea内容 渲染 到选中的元素</button>
<button @click="onDel">删除选中的元素</button>
</legend>
<textarea v-model="featurePropertiesStr"></textarea>
</fieldset>
</div>
</div>
</template>
<script>
import md5 from 'md5'
import 'ol/ol.css'
import {Map, View} from 'ol'
import Tile from 'ol/layer/Tile'
import OSM from 'ol/source/OSM'
import LayerVector from 'ol/layer/Vector'
import SourceVector from 'ol/source/Vector'
import Style from 'ol/style/Style'
import Fill from 'ol/style/Fill'
import Stroke from 'ol/style/Stroke'
import Circle from 'ol/style/Circle'
import GeoJSON from "ol/format/GeoJSON";
import {Select, Translate, Draw} from "ol/interaction";
import XYZ from "ol/source/XYZ";
import {fromLonLat} from "ol/proj";
export default {
data() {
return {
status: 'drawing', // drawing:编辑状态 display:展示状态
type: 'Point',
tools: [
{value: 'Point', label: '点'},
{value: 'LineString', label: '线'},
{value: 'Polygon', label: '多边形'},
{value: 'Circle', label: '圆'},
{value: 'None', label: '无'}
],
map: null, // 地图
vectorLayer: null, //矢量图层 也就是画布
vectorLayerSource: new SourceVector({
wrapX: false
}),
drawInteraction: null, // 画图交互
selectInteraction: null, // 选中交互
translateInteraction: null, // 拖拽交互
activeFeature: {},
jsonStr: '', // {"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[112.70586074886323,22.8627981812439]},"properties":null},{"type":"Feature","geometry":{"type":"Point","coordinates":[112.83436538276673,22.97987660474396]},"properties":null},{"type":"Feature","geometry":{"type":"Point","coordinates":[113.02682460842134,23.100152221389774]},"properties":null}]}
featurePropertiesStr: ''
}
},
watch: {
status: {
handler(val) {
this.selectInteraction?.getFeatures().clear()
this.activeFeature = null
this.$nextTick(() => {
if (this.drawInteraction) this.map.removeInteraction(this.drawInteraction)
if (this.selectInteraction) this.map.removeInteraction(this.selectInteraction)
if (this.translateInteraction) this.map.removeInteraction(this.selectInteraction)
if (val === 'display') {
this.addSelectInteraction()
}
if (val === 'drawing') {
this.addDrawInteraction()
}
if (val === 'set-get-properties') {
this.addSelectInteraction()
this.addTranslateInteraction();
}
})
},
immediate: true
},
type() {
this.addDrawInteraction()
}
},
mounted() {
this.initMap()
},
methods: {
initMap() {
let vectorLayer = new LayerVector({
source: this.vectorLayerSource,
// Vector层显示的样式
style: (feature) => {
return this.getFeatureStyle(feature, false)
}
});
this.vectorLayer = vectorLayer
this.map = new Map({
target: 'vue-openlayers',
layers: [
// new Tile({
// source: new OSM()
// }),
new Tile({
source: new XYZ({
url: 'http://wprd0{1-4}.is.auto' + 'navi.com/appmap' + 'tile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=7'
})
}),
vectorLayer
],
view: new View({
// projection: "EPSG:4326",
// center: [116, 40],
projection: "EPSG:3857",
center: fromLonLat([116, 40]),
zoom: 2.5,
minZoom:2.5,
maxZoom:20
}),
})
this.map.on('moveend',()=>{
let view = this.map.getView()
let zoom = view.getZoom()
console.log('zoom is '+ zoom)
})
},
downJsonStrFile(){
let jsonStr = this.jsonStr
// 创建一个 Blob 对象,该对象表示数据,并设置 MIME 类型为 application/json
const blob = new Blob([jsonStr], {type: "application/json"});
// 创建一个下载链接
const downloadLink = document.createElement("a");
downloadLink.href = URL.createObjectURL(blob);
downloadLink.download = "data.json"; // 设置文件名
downloadLink.click()
},
addDrawInteraction() {
if (this.drawInteraction !== null) {
this.map.removeInteraction(this.drawInteraction)
}
if (this.type !== 'None') {
this.drawInteraction = new Draw({
source: this.vectorLayerSource,
type: this.type,
style: (feature => {
return this.getFeatureStyle(feature, false)
})
})
this.drawInteraction.on('drawend', (event) => {
let drawnFeature = event.feature;
// 设置要素的属性
drawnFeature.setProperties({
'uuid': this.getUuid(),
});
});
this.map.addInteraction(this.drawInteraction)
}
},
get_GeoJSON_fromMap() {
let format = new GeoJSON();
this.jsonStr = format.writeFeatures(this.vectorLayerSource.getFeatures())
},
set_GeoJSON_toMap() {
let jsonData = JSON.parse(this.jsonStr)
this.vectorLayerSource = new SourceVector({
format: new GeoJSON(),
features: new GeoJSON().readFeatures(jsonData),
})
this.vectorLayer.setSource(this.vectorLayerSource)
},
addSelectInteraction() {
let selectInteraction = new Select({
layers: [this.vectorLayer],
style: (feature => {
return this.getFeatureStyle(feature, true)
})
});
// 监听select交互的select事件
selectInteraction.on('select', (event) => {
let selectedFeatures = event.selected;
// 输出选中的要素
selectedFeatures.forEach((feature) => {
console.log("Selected Features: ", feature);
this.activeFeature = feature;
});
if (selectedFeatures.length === 0) {
this.activeFeature = null
}
});
this.selectInteraction = selectInteraction
this.map.addInteraction(selectInteraction)
},
addTranslateInteraction() {
let translateInteraction = new Translate({
features: this.selectInteraction.getFeatures()
})
this.translateInteraction = translateInteraction
this.map.addInteraction(translateInteraction);
},
getUuid() {
return md5(Math.random() + (new Date()).toString());
},
onDel() {
let selectedFeatures = this.selectInteraction.getFeatures();
if (selectedFeatures.getLength() > 0) {
this.vectorLayer.getSource().removeFeature(selectedFeatures.item(0));
selectedFeatures.clear();
this.activeFeature = null
}
},
getFeatureStyle(feature, active) {
// 通过 feature 的几何类型设置不同的样式
let geometryType = feature.getGeometry().getType();
let color = feature.getProperties()?.color || '#00f'
let activeColor = '#f00'
let style;
if (geometryType === 'Point') {
style = new Style({
image: new Circle({
radius: 5,
fill: new Fill({
color: color
}),
stroke: new Stroke({
color: active ? activeColor : color,
width: 2
})
})
});
} else if (geometryType === 'LineString') {
style = [new Style({
stroke: new Stroke({
color: active ? activeColor : color,
width: 8
})
}), new Style({
stroke: new Stroke({
color: color,
width: 4
})
})];
} else if (geometryType === 'Polygon') {
style = new Style({
fill: new Fill({
color: color
}),
stroke: new Stroke({
color: color,
width: 2
})
});
}
return style;
},
getActiveFeatureProperties() {
if (this.activeFeature && this.activeFeature.getProperties) {
let {geometry: _, ...rest} = this.activeFeature.getProperties()
this.featurePropertiesStr = JSON.stringify(rest)
}
},
setActiveFeatureProperties() {
let data = null
try {
data = JSON.parse(this.featurePropertiesStr)
} catch (e) {
data = null
}
if (data) {
this.activeFeature.setProperties(data)
}
}
},
}
</script>
<style scoped>
.container {
width: 840px;
margin: 50px auto;
border: 1px solid #42B983;
}
#vue-openlayers {
width: 100%;
height: 400px;
border: 1px solid #42B983;
position: relative;
}
textarea {
width: 100%;
height: 80px;
}
</style>
网友评论