一、背景
公司需要个简单的二维底图,加载点线面数据,为了省时间就用openlayers框架写一个简单的加载方法,openlayers核心包含Map对象、View视图、Layer图层、Source来源、Feature等特征
二、加载配置
在正常的vue系统的package.json文件中引入openlayer的包
"dependencies": {
...
"ol": "^6.6.1"
}
三、地图初始化
新建一个组件 map/index.vue文件,里面核心代码如下
import esb from '@/plugins/esb'
import 'ol/ol.css'
import Map from 'ol/Map'
import View from 'ol/View'
import { getWidth,getCenter} from 'ol/extent'
import TileLayer from 'ol/layer/Tile'
import WMTS from 'ol/source/WMTS'
import XYZ from 'ol/source/XYZ'
import WMTSTileGrid from 'ol/tilegrid/WMTS'
import Projection from 'ol/proj/Projection'
import VectorSource from 'ol/source/Vector'
import VectorLayer from 'ol/layer/Vector'
import Feature from 'ol/Feature'
// import Point from 'ol/geom/Point'
import { Draw } from 'ol/interaction'
import { Style, Circle, Stroke, Fill } from 'ol/src/style'
import WKT from 'ol/format/WKT'
import GeoJSON from 'ol/format/GeoJSON'
import { GEOJSON_BL } from '@/const/data/beilin.js'
地图初始化 方法:
init () { // 初始化地图
// 矢量图层
const projection = new Projection({
code: 'EPSG:4326',
extent: [-180, -90, 180, 90],
metersPerUnit: 2 * Math.PI * 6378137 / 360
})
/* const projectionExtent = projection.getExtent()
const size = getWidth(projectionExtent) / 256
const resolutions = new Array(18)
const matrixIds = new Array(18)
for (let z = 0; z < 18; ++z) {
resolutions[z] = size / Math.pow(2, z + 1)
matrixIds[z] = z + 1
}
// 省级节点提供的瓦片总共14级,从7到20
const resolutions2 = new Array(20)
const matrixIds2 = new Array(20)
for (let z = 0; z < 20; ++z) {
resolutions2[z] = size / Math.pow(2, z)
matrixIds2[z] = z
}
const orgin = [-180, 90]
*/
this.vectorLayer = new VectorLayer({
source: new VectorSource(),
style: new Style({
stroke: new Stroke({
width: 2,
color: [242, 114, 60, 1],
lineDash: [1, 2, 3, 4, 5, 6]
}),
fill: new Fill({
color: [242, 114, 60, 0.2]
}),
image: new Circle({
// 点的颜色
fill: new Fill({
COLOR: '#f60404',
color: [246, 4, 4, 1]
}),
stroke: new Stroke({
color: [242, 114, 60, 1]
}),
// 圆形半径
radius: 5
})
})
})
this.GGLWLayer =new TileLayer({
source:new XYZ({
url:"http://t4.tianditu.com/DataServer?T=vec_c&x={x}&y={y}&l={z}&tk=d0cf74b31931aab68af181d23fa23d8d"
}),
visible: false
});
this.GGLayer =new TileLayer({
source:new XYZ({
//url:"http://t4.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=d0cf74b31931aab68af181d23fa23d8d"
url:"http://globescenetiles.frp.trmap.cn/L8/{z}/{x}/{y}.png"
}),
visible: false
});
this.TDTLayer =new TileLayer({
source:new XYZ({
url:"http://t4.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=d0cf74b31931aab68af181d23fa23d8d"
})
});
this.zjLayer =new TileLayer({
source:new XYZ({
url:"http://t4.tianditu.com/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=d0cf74b31931aab68af181d23fa23d8d"
})
});
this.map = new Map({
layers: [this.TDTLayer,
this.GGLayer,
// this.GGLWLayer,
this.vectorLayer,
this.zjLayer
],
target: 'map',
view: new View({
center: [106.814931000, 34.945231000, 16000],
projection: projection,
minZoom: 4,
maxZoom: 21,
// extent : maxExtent, //限定到地图视图拖动范围
zoom: 5 // 地图初始化的缩放级别
})
})
},
四、加载geojson
import { GEOJSON_BL } from '@/const/data/beilin.js'
// 添加GeoJOSN
addGeoJSON () {
this.clearGeoJSON()
let source = new VectorSource({
// 准备好的GeoJSON
features: new GeoJSON().readFeatures(GEOJSON_BL)
})
debugger
this.geoLayer = new VectorLayer({
source: source,
// 设置样式,边框和填充
style: new Style({
stroke: new Stroke({
color: 'green',
width: 5
}),
fill: new Fill({
color: 'rgba(255, 255, 0, 0.5)'
})
})
})
// 添加GeoJSON到map上
this.map.addLayer(this.geoLayer)
// 地图视图缩小一点
const view = this.map.getView()
view.setZoom(9)
},
// 清除GeoJSON
clearGeoJSON () {
if (this.geoLayer) {
this.map.removeLayer(this.geoLayer)
this.geoLayer = null
}
// 清除之后将地图中心还原
const view = this.map.getView()
view.setCenter([106.814931000, 34.945231000, 16000])
view.setZoom(12)
}
五、底图切换demo
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>openlayers</title>
<script src="js/ol.js"></script>
<link rel="stylesheet" type="text/css" href="css/ol.css"/>
</head>
<body>
<button type="button" onclick="changeMap(1)">卫星地图</button>
<button type="button" onclick="changeMap(2)">路网地图</button>
<div id="map" class="map" style="width: 100%;"></div>
</body>
<script type="text/javascript">
// 改为天地图地图
var mlayer1 = new ol.layer.Tile({
source: new ol.source.XYZ({
url:"http://t4.tianditu.com/DataServer?T=vec_c&x={x}&y={y}&l={z}&tk=d0cf74b31931aab68af181d23fa23d8d"
}),
visible: true
});
// 改为天地图影像
var mlayer2 = new ol.layer.Tile({
source: new ol.source.XYZ({
url:"http://t4.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=d0cf74b31931aab68af181d23fa23d8d"
}),
visible: false
});
var map = new ol.Map({
layers: [mlayer1,mlayer2],
target: 'map',
controls: ol.control.defaults({
attributionOptions: {
collapsible: false
}
}),
view: new ol.View({
center: [0, 0],
zoom: 2
})
});
// 切换地图
function changeMap(data){
if(data==1){
mlayer1.setVisible(true);
mlayer2.setVisible(false);
}else {
mlayer1.setVisible(false);
mlayer2.setVisible(true);
}
}
</script>
</html>
六、绘制、修改点线面
point () {
const m = this
m.isShow = false
m.type = 'Point'
m.drawMap(m.type)
},
polygon () {
const m = this
m.isShow = false
m.type = 'Polygon'
this.drawMap(m.type)
},
drawMap (type) {
document.oncontextmenu = function () {
return false
}
const m = this
m.map.removeInteraction(this.draw)
this.vectorLayer.getSource().clear()
m.draw = new Draw({
source: this.vectorLayer.getSource(),
type: type,
stopClick: true, // 绘制时禁用点击事件
style: this.getDrawingStyle()
})
m.draw.on('drawend', e => {
// let coordinates = e.feature.getGeometry().getCoordinates();
m.feature = e.feature
var a = new WKT()
m.wkt = a.writeGeometry(m.feature.getGeometry())
m.map.removeInteraction(m.draw)
})
this.map.addInteraction(m.draw)
},
getDrawingStyle () {
return new Style({
stroke: new Stroke({
width: 2,
color: [239, 176, 19, 1],
lineDash: [1, 2, 3, 4, 5, 6]
}),
fill: new Fill({
color: [239, 176, 19, 0.2]
}),
image: new Circle({
// 点的颜色
fill: new Fill({
color: [246, 4, 4, 1]
}),
stroke: new Stroke({
color: [239, 176, 19, 1]
}),
// 圆形半径
radius: 5
})
})
},
七、完善各种功能后,完整代码如下:
map/index.vue
<template>
<div style="width:100%; height: 100%; position: relative;" v-if="areaShow">
<div id="map" style="width:100%; height: 100%;" />
<span @click="getGeoHandle()" />
<div v-if="isButton" style="position: absolute;right:12px;top:12px">
<p v-if="isShow" style="margin-top: 10px">
<Button type="primary" size="small" @click="point()">绘制点</Button>
<Button type="primary" size="small" @click="polygon()">绘制面</Button>
<Button type="warning" size="small" @click="clearDraw()">重置</Button>
</p>
<p v-else style="margin-top: 10px">
<Button type="primary" size="small" @click="submit">确定</Button>
<Button type="warning" size="small" @click="reset()">重置</Button>
</p>
</div>
</div>
</template>
<script>
import esb from '@/plugins/esb'
import 'ol/ol.css'
import Map from 'ol/Map'
import View from 'ol/View'
import TileLayer from 'ol/layer/Tile'
import XYZ from 'ol/source/XYZ'
import VectorSource from 'ol/source/Vector'
import VectorLayer from 'ol/layer/Vector'
import Feature from 'ol/Feature'
import { Draw } from 'ol/interaction'
import { Style, Circle, Stroke, Fill,Text} from 'ol/src/style'
import WKT from 'ol/format/WKT'
import GeoJSON from 'ol/format/GeoJSON'
export default {
name: 'Map',
props: ['areaShow', 'geometrys', 'isButton','layerid','geojsonurl','hightFeature'],
data () {
return {
fullscreen: true,
lon: '',
lat: '',
map: null,
type: null,
style: null,
feature: null,
wkt: null,
isShow: true
}
},
watch: {
areaShow (v) {
if (v) {
this.$nextTick(() => {
if (!this.map) {
this.init()
}
})
}
},
layerid(v){
if(v){
var m =this;
m.changeMap(v);
}
},
geojsonurl:{
immediate:true,
handler(v){
var m =this;
if(v){
m.addGeoJSON(v)
}else{
m.clearGeoJSON()
}
}
},
geometrys(v){
if(v && v.length>0){
this.loadGeometrys(v)
}else{
this.clearDraw()
}
},
hightFeature(v){
if(v){
this.addHeighLight(v)
}else{
this.clearHeighLight()
}
}
},
mounted () {
if (this.areaShow) {
if (!this.map) {
this.init()
}
};
},
methods: {
init () { // 初始化地图
// 矢量图层
this.vectorLayer = new VectorLayer({
source: new VectorSource(),
style: new Style({
stroke: new Stroke({
width: 2,
color: [242, 114, 60, 1],
lineDash: [1, 2, 3, 4, 5, 6]
}),
fill: new Fill({
color: [242, 114, 60, 0.2]
}),
image: new Circle({
// 点的颜色
fill: new Fill({
COLOR: '#f60404',
color: [246, 4, 4, 1]
}),
stroke: new Stroke({
color: [242, 114, 60, 1]
}),
// 圆形半径
radius: 5
})
})
})
//高亮图层
this.highlightLayer = new VectorLayer({
source: new VectorSource(),
style: this.getHeightStyle()
})
this.yxLayer =new TileLayer({
source:new XYZ({
//url:"http://t4.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=d0cf74b31931aab68af181d23fa23d8d"
url: this.$root.YXLAYERURL ||"http://globescenetiles.frp.trmap.cn/L8/{z}/{x}/{y}.png"
}),
visible: false
});
this.slLayer =new TileLayer({
source:new XYZ({
// url:"http://t4.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=d0cf74b31931aab68af181d23fa23d8d"
url: this.$root.SLLAYERURL || "http://globescenetiles.frp.trmap.cn/L8vec/{z}/{x}/{y}.png"
})
});
this.zjLayer =new TileLayer({
source:new XYZ({
url:"http://t4.tianditu.com/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=d0cf74b31931aab68af181d23fa23d8d"
}),
visible: this.$root.SHOWZJLAYER==true || false
});
this.map = new Map({
layers: [this.yxLayer,
this.slLayer,
this.vectorLayer,
this.zjLayer,
this.highlightLayer
],
target: 'map',
view: new View({
center: [106.814931000, 34.945231000, 16000],
projection: 'EPSG:4326',
minZoom: 4,
maxZoom: 21,
// extent : maxExtent, //限定到地图视图拖动范围
zoom: 5 // 地图初始化的缩放级别
})
})
},
loadGeometrys(features){
this.clearDraw();
var gemos = JSON.parse(JSON.stringify(features));
if(gemos.length>1){
gemos.forEach(e=>{
var feature = new GeoJSON().readFeature(e, {
featureProjection: "EPSG:4326" //规定要素以哪种坐标显示
});
var style = this.getlightStyle();
feature.setStyle(style);
this.vectorLayer.getSource().addFeature(feature)
})
}
const view = this.map.getView()
const extent = this.vectorLayer .getSource().getExtent();
//定位范围
view.fit(extent);
},
submit () {
if (this.wkt == null) {
this.$message({
type: 'error',
message: '不能保存空位置'
})
}
document.oncontextmenu = function () {
return true
}
return esb.$emit('MapHandle', { geometry: this.wkt })
},
reset () {
this.isShow = true
this.vectorLayer.getSource().clear()
},
point () {
const m = this
m.isShow = false
m.type = 'Point'
m.drawMap(m.type)
},
polygon () {
const m = this
m.isShow = false
m.type = 'Polygon'
this.drawMap(m.type)
},
edit (gemo) {
const feature = new Feature({
geometry: (new WKT()).readGeometryFromText(gemo)
})
// 保存到原始数据图层
this.vectorLayer.getSource().addFeature(feature)
//this.map.view.setCenter(getCenter(feature.getGeometry().getExtent()));
// let extent = vectorSource.getExtent()
// this.view.fit(extent)
},
drawMap (type) {
document.oncontextmenu = function () {
return false
}
const m = this
m.map.removeInteraction(this.draw)
this.vectorLayer.getSource().clear()
m.draw = new Draw({
source: this.vectorLayer.getSource(),
type: type,
stopClick: true, // 绘制时禁用点击事件
style: this.getDrawingStyle()
})
m.draw.on('drawend', e => {
// let coordinates = e.feature.getGeometry().getCoordinates();
m.feature = e.feature
var a = new WKT()
m.wkt = a.writeGeometry(m.feature.getGeometry())
m.map.removeInteraction(m.draw)
})
this.map.addInteraction(m.draw)
},
getDrawingStyle () {
return new Style({
stroke: new Stroke({
width: 2,
color: [239, 176, 19, 1],
lineDash: [1, 2, 3, 4, 5, 6]
}),
fill: new Fill({
color: [239, 176, 19, 0.2]
}),
image: new Circle({
// 点的颜色
fill: new Fill({
color: [246, 4, 4, 1]
}),
stroke: new Stroke({
color: [239, 176, 19, 1]
}),
// 圆形半径
radius: 5
})
})
},
//点击高亮样式
getHeightStyle(){
var highlightStyle = new Style({
fill: new Fill({
color:"#890c08", //高亮区域填充颜色,可用rgba值
}),
stroke: new Stroke({
color:"#0c0005", //高亮区域的边界线颜色
width:2
}),
image: new Circle({
// 点的颜色
fill: new Fill({
color: "rgb(231,6,18,1)"
}),
stroke: new Stroke({
color: "rgb(12,0,5,1)"
}),
// 圆形半径
radius: 10
})
});
return highlightStyle;
},
//高亮样式
getlightStyle(){
var lightStyle = new Style({
stroke: new Stroke({
width: 2,
color: "3e84cf",
lineDash: [1, 2, 3, 4, 5, 6]
}),
fill: new Fill({
color: [239, 176, 19, 0.1]
}),
image: new Circle({
// 点的颜色
fill: new Fill({
color: [246, 4, 4, 1]
}),
stroke: new Stroke({
color: [239, 176, 19, 1]
}),
// 圆形半径
radius: 5
})
})
return lightStyle;
},
clearDraw () {
if(this.vectorLayer){
this.vectorLayer.getSource().clear()
}
},
getGeoHandle () {
esb.$emit('MapHandle', { geometry: this.wkt })
},
close () {
document.oncontextmenu = function () {
return true
}
this.vectorLayer.getSource().clear()
return esb.$emit('MapHandle', { geometry: this.wkt })
},
// 清除
resets () {
this.vectorLayer.getSource().clear()
this.highlightLayer.getSource().clear()
if (this.geoLayer) {
console.log("clear geoLayer")
this.map.removeLayer(this.geoLayer)
this.geoLayer = null
}
},
// 切换地图
changeMap(id){
if(id=="YX"){
this.yxLayer.setVisible(true);
//console.log(111, this.$root.SHOWZJLAYER)
this.zjLayer.setVisible(this.$root.SHOWZJLAYER==true);
this.slLayer.setVisible(false);
}else if(id=="SL") {
this.yxLayer.setVisible(false);
this.zjLayer.setVisible(false);
this.slLayer.setVisible(true);
}
},
// 添加GeoJOSN
addGeoJSON (url) {
var m =this;
m.clearGeoJSON()
m.geoLayer = new VectorLayer({
name: "tj",
source: new VectorSource({
features: new GeoJSON().readFeatures(url),
}),
style: function (feature) {
var style = ""
if (feature.get("style")?.fill.length > 0) {
style = new Style({
fill: new Fill({
color: feature.get("style").fill || "rgb(0,0,0,0)"
}),
stroke: new Stroke({
color: feature.get("style").stroke || "rgb(0,0,0,1)",
width: 0.5,
}),
text: new Text({
textAlign: "center",
textBaseline: "middle",
font: "bold 15px 微软雅黑",
text: `${feature.get("name")}`,
fill: new Fill({color: "#8efff7"}),
stroke: new Stroke({color: "#353535", width: 1}),
}),
});
} else {
style = m.getStyle(feature)
}
return style;
}
})
m.geoLayer.setOpacity(0.7)
// 添加GeoJSON到map上
m.$nextTick(()=>{
m.map.addLayer(this.geoLayer)
})
},
getStyle(feature){
return new Style({
stroke: new Stroke({
color: 'rgba(0, 0, 0, 1)',//通过要素拿到具体的值
width: 0.5,
opacity: 1
}),
fill: new Fill({
color: 'rgba(255, 255, 0, 0.5)',
// opacity: feature.opacity
}),
image: new Circle({
// 点的颜色
fill: new Fill({
color: 'rgba(255, 255, 0, 0.5)',
}),
stroke: new Stroke({
color: 'rgba(0, 0, 0, 1)',
}),
// 圆形半径
radius: 5
})
})
},
// 清除GeoJSON
clearGeoJSON () {
if (this.geoLayer) {
this.map.removeLayer(this.geoLayer)
this.geoLayer = null
}
},
//添加高亮
addHeighLight(feature){
if(feature?.geometry?.type?.length>0){
this.clearHeighLight()
var fea = new GeoJSON().readFeature(feature, {
featureProjection: "EPSG:4326" //规定要素以哪种坐标显示
});
var style = this.getHeightStyle();
fea.setStyle(style);
this.highlightLayer.getSource().addFeature(fea)
let extent = fea.getGeometry().getExtent();
const view = this.map.getView()
view.setCenter(extent)
}
},
//清空高亮
clearHeighLight(){
if (this.highlightLayer) {
this.highlightLayer.getSource().clear()
}
}
}
}
</script>
<style scoped>
</style>
调用
<template lang="pug">
.homeview
.map
<Map :is-button="isButton" :area-show="areaShow" :geometrys="geometrys" :layerid="layerid" :geojsonurl="geojsonurl" :hightFeature="hightFeature"/>
.bth
img(src='../assets/1.png' @click="changeMap(1)" v-if="lightmap == 2")
img(src='../assets/2.png' @click="changeMap(2)" v-if="lightmap == 1")
</template>
<script>
import Map from '@/components/Map'
import QG from '@/const/data/qg.json'
import {list1,list2} from "@/views/Echars/index";
import {list} from "@/views/Echars/image";
import _ from 'lodash'
import colors from "@/const/colors";
export default {
name: 'HomeView',
data() {
return {
areaShow: true,
isButton: false,
hightFeature:"",
geometrys: [],
treeshow: true,
layerid: "TDT",
geojsonurl:"",
row:{},
lightmap:1,
whereCompany:''
}
},
components:{Map},
created() {
},
watch: {
'$route.path':{
immediate:true,
handler(v) {
if(v=='/Company'){
this.whereCompany = "first"
this.hightFeature =""
this.geometrys=[]
}else if(v=='/Image'){
this.whereCompany = "Image"
this.hightFeature =""
this.geometrys=[]
}else{
this.whereCompany = ""
this.hightFeature =""
this.geometrys=[]
this.geojsonurl="";
}
}
}
},
methods:{
// 切换地图
changeMap(data){
var m =this;
m.lightmap = data;
if(m.lightmap==1){
m.layerid="SL"
}else {
m.layerid="YX"
}
},
//加载统计数据
addGeoJson(url,data){
var m =this;
if(url=="QG"){
m.geojsonurl="";
var temp = JSON.parse(JSON.stringify(QG));
temp.features?.forEach(item=>{
var style={fill:"rgb(0,0,0,0.1)",opacity:"0.5",stroke:"rgb(0 0 0)"}
var index = _.findIndex(data, el=>el.name==item.properties.name);
let col =colors || []
if(index>=0 && index <=col.length){
style.fill=col[index];
}
item.properties.style=style;
})
m.geojsonurl=temp;
}else{
this.geojsonurl=url;
}
},
//表格查询,地图加载点线面
companyDataChange(e){
//console.log(99,e);
var m =this;
m.geometrys=[]
m.geojsonurl=""
m.hightFeature=""
var temp = JSON.parse(JSON.stringify(e));
//企业结果
if(temp.data?.length>0){
let data = m.object2Geojson(temp.data);
let data1=[];
if(temp.data1?.length>0){
data1 = m.object2Geojson(temp.data1)
}
var other = _.concat(data.features,data1.features);
let geojson={"type":"FeatureCollection","features":[]}
geojson.features=other;
m.geojsonurl=geojson;
}
if(temp.nowdata?.length>0){
let nowdata = m.object2Geojson(temp.nowdata);
let nowdata1=[];
if(temp.nowdata1?.length>0){
nowdata1= m.object2Geojson(temp.nowdata1)
}
var other1 = _.concat(nowdata.features,nowdata1.features);
m.geometrys=other1;
}
//影像查询结果
if(temp.data3?.length>0){
let geojson={"type":"FeatureCollection","features":[]}
geojson.features=temp.data3
m.geojsonurl=geojson;
}
if(temp.nowdata3?.length>0){
m.geometrys=temp.nowdata3;
}
},
//json转geojson
object2Geojson(data){
var featureCollection = {"type": "FeatureCollection"};
var features = new Array();
for (let i = 0; i < data.length; i++) {
var feature = {"type": "Feature"};
feature.properties = data[i];
var geometry = {"type": "Point"};
geometry.coordinates = [data[i].lon, data[i].lat];
feature.geometry = geometry;
features.push(feature);
}
featureCollection.features = features;
return featureCollection;
}
}
}
</script>
<style lang=less rel="stylesheet/less" scoped>
.homeview{
height: calc(100vh - 74px);
display: flex;
.map{
flex:1;
position: relative;
>.bth{
position: absolute;
right: 12px;
bottom:12px;
z-index: 999;
border:1px #265df7 solid;
width: 86px;
height: 60px;
overflow: hidden;
img:first-child{
cursor: pointer;
}
}
}
.Search,.company{
width: 450px;
margin-left: 2px;
overflow: hidden;
height: 100%;
}
}
</style>
七、参考网址
参考资料:
openlayers官网:https://openlayers.org/
geojson下载网站:https://datav.aliyun.com/portal/school/atlas/area_selector
地图坐标拾取网站:https://api.map.baidu.com/lbsapi/getpoint/index.html