准备工作,需要先下载好离线地图的瓦片文件
本文参考了:https://juejin.cn/post/7020970548666564639#heading-2,https://juejin.cn/post/7017301189406490655#comment
安装openlayers地图:npm install ol
瓦片下载工具:全能瓦片下载工具
openlayers 官方api:官方api英文文档
地图的行政区划json名为“canton.json”,下载需要的行政区json:json下载地址
本地的地图瓦片需要跑一个服务才能使用,本地运行时地图瓦片文件夹使用http-server创建服务
安装:http-server
全局安装后去到需要瓦片文件夹里执行:
此时就已经可以在浏览器查看对应的http://192.168.100.90:8081,成功如下所示。
项目中加入一个vue文件,我原本的自定义内容比较多,删掉了一大部分自定义的信息窗内容,样式也基本删掉,但下面的代码几乎是离线地图标题中的功能了
<template>
<div style="width: 100%;height: 100%;position: relative;">
<div class="map"
id="map"></div>
<div id="people_info">
<div class="tipContainer"></div>
</div>
</div>
</template>
<script>
import 'ol/ol.css';
import Map from 'ol/Map';
import Feature from 'ol/Feature';
import VectorSource from 'ol/source/Vector';
import Overlay from 'ol/Overlay';
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer';
import View from 'ol/View';
import { transform } from 'ol/proj';
import XYZ from 'ol/source/XYZ'
import Point from 'ol/geom/Point';
import GeoJSON from 'ol/format/GeoJSON';
import { Fill, Stroke, Icon, Style } from 'ol/style'
import markerImg from '@/assets/big-screen/icon-build.png'//图标资源
import { LineString, Polygon } from 'ol/geom'
export default {
name: "openlayersMap",
data () {
return {
mapObj: null,
mapDom: null,
mapPointList: [],
pointLayerSource: null,
pointLayer: null,
markerIcon: markerImg,
}
},
mounted () {
this.initMap()
},
methods: {
// 清除地图 某些情况 地图容器会存在两个 导致地图无法正常显示 这个问题折腾了我半天。
// 找了半天官方貌似也没有提供 对应的 api,自己动手了。
mapClear () {
if (this.mapDom) {
this.mapDom.innerHTML = ''
this.mapDom = null
}
},
// 初始化地图
initMap () {
// 先尝试清除
this.mapClear()
// 获取地图容器
this.mapDom = document.getElementById('map')
// 初始化地图配置
this.mapObj = new Map({
target: this.mapDom, // 地图容器
view: new View({
center: [107.975994, 26.602712], // 地图中心点
zoom: 9, // 缩放
projection: 'EPSG:4326' // 坐标系
})
})
// 添加一个使用离线瓦片地图的层
const offlineMapLayer = new TileLayer({
source: new XYZ({
url: 'http://192.168.100.90:8081' + '/{z}/{x}/{y}.png' // 设置本地离线瓦片所在路径
// url: 'http://83.218.0.40:812/map' + '/{z}/{x}/{y}.png' // 设置内网离线瓦片所在路径
})
})
// 将图层添加到地图
this.mapObj.addLayer(offlineMapLayer)
// 地图点击事件
this.mapOnClick()
//地图hover事件
this.mapOnenterMove()
//画出行政区划边界
this.addAreaPolygon()
// 加载地理坐标,放在后面则为上层
this.addPoint()
//用于hover的点坐标,mark参数需要自己传
this.addPointPeople()
},
// 地图点击事件
mapOnClick () {
const self = this
// 地图点击事件
this.mapObj.on('click', function (evt) {
console.log(evt)
// 获取点击位置的数据
var feature = self.mapObj.forEachFeatureAtPixel(evt.pixel, (feature) => {
return feature
})
// 判断数据,
if (feature) {
//此处可以去开启处理自定义的信息窗
self.getBuildInfo(feature.values_.data)
}
})
},
mapOnenterMove () {
const self = this
const tipDom = document.getElementById('people_info')
// 创建 tip
const tip = new Overlay({
element: tipDom,
positioning: 'bottom-center',
stopEvent: false
})
// // 加载到地图
this.mapObj.addOverlay(tip)
// 地图鼠标移入事件
this.mapObj.on('pointermove', function (evt) {
// 获取点击位置的数据
var feature = self.mapObj.forEachFeatureAtPixel(evt.pixel, (feature) => {
return feature
})
// 根据 点击元素 className 判断是否点击在自定义popup上
const isMoveTip = evt.originalEvent.path.map(item => item.className).includes('people_info')
if (!isMoveTip) {
tipDom.style.display = 'none'
}
// 官方示例 采用 jq + bootstrap弹窗,但是我觉得没有必要 如果大量使用bootstrap 组件可以考虑引入。
const tipContainer = document.getElementsByClassName('tipContainer')[0]
// 判断数据
if (feature) {
// feature.values_.data ,data字段是在 addPoint 函数创建point时添加 可以自定义
if (feature.values_.data) {
const pointData = feature.values_.data
// console.log(pointData)
tip.setPosition(evt.coordinate)
if (!pointData.name) return
var infoShow = pointData.realname ? `${pointData.name}--${pointData.realname}` : 'not info'
tipContainer.innerHTML = `<div>${infoShow}</div>`
tipDom.style.display = 'block'
}
}
})
},
// 添加地理坐标
addPoint (mark) {
this.delPointAll()
// 地理坐标数组
var defaultData = [
// { longitude: 108.042078, latitude: 26.549856, nickname: '李大壮' },
// { longitude: 107.956612, latitude: 26.56404, nickname: '李大壮2' },
]
var pointData = (mark !== undefined) ? mark : defaultData
pointData.map(item => {
console.log('point_item', item)
// 创建点
const point = new Feature({
geometry: new Point([Number(item.longitude), Number(item.latitude)]),
data: item
})
// 点的样式
const iconStyle = new Style({
image: new Icon({
color: '#ffffff',
crossOrigin: 'anonymous',
src: this.markerIcon2,
scale: 0.5,//放大缩小
}),
})
// 设置样式
point.setStyle(iconStyle)
// 保存到数据 方便删除
this.mapPointList.push(point)
})
// 创建geojson据源
this.pointLayerSource = new VectorSource({ features: this.mapPointList })
// 创建图层 并加载数据
this.pointLayer = new VectorLayer({ source: this.pointLayerSource })
// 将图层添加地图上
this.mapObj.addLayer(this.pointLayer)
},
//hover的坐标
addPointPeople (mark) {
var defaultData = [
{ longitude: 108.242078, latitude: 26.749856, name: '李大壮' },
{ longitude: 107.756612, latitude: 26.36404, name: '李大壮2' },
]
// var pointData = defaultData
var pointData = mark ? mark : defaultData
pointData.map(item => {
// console.log('point_item', item)
// 创建点
const point = new Feature({
geometry: new Point([Number(item.longitude), Number(item.latitude)]),
data: item
})
// 点的样式
const iconStyle = new Style({
image: new Icon({
color: '#ffffff',
crossOrigin: 'anonymous',
src: this.markerIcon3,
scale: 0.5,//放大缩小
}),
})
// 设置样式
point.setStyle(iconStyle)
// 保存到数据 方便删除
this.mapPointList.push(point)
})
// 创建geojson据源
this.pointLayerSource = new VectorSource({ features: this.mapPointList })
// 创建图层 并加载数据
this.pointLayer = new VectorLayer({ source: this.pointLayerSource })
// 将图层添加地图上
this.mapObj.addLayer(this.pointLayer)
},
addAreaPolygon () {
let geoJson = require('@/utils/canton.json')
console.log('geoJson', geoJson.features)
var borderList = []
geoJson.features.map((item, index) => {
console.log(item)
var border_item = {}
border_item.ad_name = item.properties.name
border_item.border = item.geometry.coordinates[0]
borderList.push(border_item)
})
//多个区域遍历画出边界
borderList.map((item, idx) => {
let polygonFeature = new Feature({
type: 'polygon',
name: item.ad_name,
geometry: new Polygon(item.border)
})
polygonFeature.setStyle(new Style({
stroke: new Stroke({
width: 2,
color: [0, 0, 205, 0.8]
}),
fill: new Fill({
color: [0, 206, 209, 0.2]
}),
// text: new Text({
// text: item.name
// })
}))
let polygonLayer = new VectorLayer({
source: new VectorSource({
features: [polygonFeature]
})
})
this.mapObj.addLayer(polygonLayer)
})
// var arr = geoJson.features[0].geometry.coordinates[0]
},
// 地理点位删除
delPointAll () {
// 判断 删除的数据源是否存在
if (this.pointLayerSource) {
// 遍历删除
this.mapPointList.map(item => {
this.pointLayerSource.removeFeature(item)
})
// 删除图层 重置数据
this.mapObj.removeLayer(this.pointLayer)
this.pointLayerSource = null
this.pointLayer = null
this.mapPointList = []
}
},
//重置地图中心点位置,按自己需求地图需要移动时传入参数即可
restMapCenetr (mk) {
console.log(mk, this.mapObj)
if (mk) {
this.mapObj.getView().animate({ // 只设置需要的属性即可
center: [mk.longitude, mk.latitude], // 中心点
zoom: 12, // 缩放级别
rotation: undefined, // 缩放完成view视图旋转弧度
duration: 1000 // 缩放持续时间,默认不需要设置
})
}
},
},
beforeDestroy () {
this.mapClear()
}
}
</script>
<style lang="scss" scoped>
.map {
width: 100%;
height: 100%;
}
.tipContainer {
background-color: #fff;
padding: 8px;
border-radius: 4px;
}
.popup {
//自定义的样式
position: absolute;
z-index: 998 !important;
width: 120%;
height: 100%;
border: 2px solid #02d3ea;
left: -10%;
top: -10%;
display: none;
}
</style>