uniapp 整合 OpenLayers - 加载本地瓦片地图服务

一、下载离线瓦片资源
链接: https://pan.baidu.com/s/125q3_yJgiN5bpH8GxJQ_MQ 提取码: kagy

下载后解压,之间运行imaps.exe

 

要下载高德地图!!! 下载别的地图 我试了 报错(移动端直接不显示)

 

二、发布离线瓦片数据

要把自己下载的地图资源放在服务器上才行
自己本地测试的话,本地起一个服务就好

上面这是刚刚下载的瓦片地图资源,在这个目录下 

2.1http-server

安装http-server(前提电脑配置了node.js):

npm i http-server -g

运行http-server :

http-server --cors

 

为什么是 http-server --cors 为什么要加 --cors
因为我们的地图资源问卷有跨域的限制
虽然百度了说设置img.setAttribute(‘crossOrigin’, ‘anonymous’)这个有用,但是经过实验其实没什么用

访问结果如下:

2.2 nginx

下载一个nginx

nginx.conf配置:

 

访问结果如下: 

三、页面渲染(uniapp-vue3)

安装 ol并配置环境:

看这篇:uniapp 整合 OpenLayer3_uniapp导入openlayers-CSDN博客

直接上代码:

<template>
	<!-- 监听变量 operation 的变化,operation 发生改变时,调用 openlayers 模块的 loadOperation 方法 -->
	<view :operation="valueChangeSign" :change:operation="ol.valueChange" type="default"></view>
	<view class="map" id="map">
		<view class="bottom-horizontal-button">
			<button @click="location()" class="btn" type="primary">定位</button>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				valueChangeSign:{
					flag:false,
					latitude:null,//纬度
					longitude:null//经度
				},
			}
		},
		onLoad() {

		},
		methods: {
			/**
			 * 点击事件-定位
			 * */
			 location(){
				 
				// 获取经纬度
				uni.getLocation({
					type: 'wgs84 ',
					success: (res) => {
						//console.log("res",res)
						this.valueChangeSign.flag = !this.valueChangeSign.flag;
						this.valueChangeSign.latitude = res.latitude//接收纬度值
						this.valueChangeSign.longitude = res.longitude//接收经度值
					}
				});  
				
			 },
		}
	}
</script>
<script module="ol" lang="renderjs" type="module">
import Map from 'ol/Map';
import View from 'ol/View';
import TileLayer from 'ol/layer/Tile.js'// OpenLayers的瓦片图层类  
import { Vector as VectorLayer } from 'ol/layer.js'  // OpenLayers的矢量图层类,用于显示矢量数据
import { Vector as VectorSource } from 'ol/source.js'  // OpenLayers的矢量数据源类,用于管理和提供矢量数据       
import { ScaleLine, defaults as defaultControls, MousePosition } from 'ol/control.js'// OpenLayers的控件类,包括默认的控件集合和特定的全屏、鼠标位置、比例尺控件
import {addCoordinateTransforms, addProjection, Projection, transform} from 'ol/proj.js'
import XYZ from 'ol/source/XYZ.js'
import Feature from 'ol/Feature.js'  
import Point from 'ol/geom/Point.js' 
import { Circle as CircleStyle, Style, Stroke, Fill, Icon } from "ol/style.js"

	export default {
		data(){
			return {
				map:null,
				view:null,
				locationLayer:null,// 定位点图层
			}
		},
		mounted(){
			this.initMap();
		},
		methods:{
			/**
			 * @param {*} newValue 新的值或状态
			 * @param {*} oldValue 旧的值或状态
			 * @param {*} ownerInstance 拥有该数据或组件的实例
			 * @param {*} instance 当前操作的具体实例
			 */
			valueChange(newValue, oldValue, ownerInstance, instance){
				console.log(newValue, oldValue, ownerInstance, instance);
				
				const olLongitude = newValue.longitude;//当前位置的经度
				const olLatitude = newValue.latitude;//当前位置的纬度
// 
				// 调用定位按钮
				this.lxLocation(olLongitude,olLatitude);
			},
			initMap(){
				// 添加一个使用离线瓦片地图的层
				var offlineMapLayer = new TileLayer({
					source: new XYZ({
						// http-server 地址
						//url: 'http://192.168.1.167:8080/{z}/{x}/{y}.png'
						// nginx 地址
						url:'http://192.168.1.167:8081/gaode/{z}/{x}/{y}.png'
					})
				});
				
				this.map = new Map({
				  target: 'map',
				  layers:[offlineMapLayer],
				  view: new View({
					projection:'EPSG:3857',
					center: transform([125.33,43.90], 'EPSG:4326', 'EPSG:3857'),
					zoom: 9,
				    minZoom: 1,// 最小缩放级别
				    maxZoom: 12, //最大缩放级别
				    constrainResolution: true,// 因为存在非整数的缩放级别,所以设置该参数为true来让每次缩放结束后自动缩放到距离最近的一个整数级别,这个必须要设置,当缩放在非整数级别时地图会糊
					enableRotation: false,// 禁止地图旋转
				  }),
				  controls:defaultControls({
					  zoom:false,//不显示放大放小按钮
					  rotate:false,// 不显示指北针控件
					  attribution:false//不显示右下角的地图信息控件
				  }).extend([
					// 比例尺
					new ScaleLine({
						//设置比例尺单位,degrees、imperial、us、nautical、metric(度量单位)
						units: "metric"
					})
				  ])
				})
				
			},
			lxLocation(longitude,latitude){// 定位
				const geom = transform([longitude,latitude], 'EPSG:4326', 'EPSG:3857');
				
				// 设置中心点定位-直接定位
				//this.map.getView().setCenter(geom);
				// 设置层级
				//this.map.getView().setZoom(18);
				
				// 设置中心点-过渡动画
				this.map.getView().animate({
					center:geom,// 中心点
					zoom:12,// 层级
					duration:1000// 持续时间
				});
				
				
				// 绘制点
				const locationPoint = new Point(geom);
				// 清除绘制点图层
				this.map.removeLayer(this.locationLayer); 
				
				// 绘制定位点
				// 设置点特征(Feature)
				const pointFeature = new Feature({
					title:"point",
					geometry:locationPoint
				});
				
				// 设置特征样式(style)
				pointFeature.setStyle(
					new Style({
						 // 使用 CircleStyle 创建一个圆形的点
						 image:new CircleStyle({
							 // 点样式
							 fill:new Fill({
								 //color:"red",// 颜色
								 color: 'rgba(255,0,0,0.4)',
							 }),
							 // 点周边样式
							 stroke:new Stroke({
								color: '#3399CC',
								width: 1.25, 
							 }),
							 radius:7,// 半径
						 }),
					})
				);
				// 创建和添加特征到源(Source)
				// VectorSource表示一个矢量要素源,它用于存储和显示地理数据。
				const source = new VectorSource();
				source.addFeature(pointFeature);
				// 创建图层并设置源(Layer)
				// VectorLayer表示一个矢量图层,它由一系列矢量要素(Feature)组成,用于在地图上显示地理数据。
				this.locationLayer = new VectorLayer();
				this.locationLayer.setSource(source);
				this.map.addLayer(this.locationLayer);
			},
			
			
		}
		
	}

</script>

<style scoped lang="scss">
	/*去除顶部导航栏*/
	*{margin:0;padding:0}
	.map{
	  width:100vw;
	  height: 100vh;
	  position: relative;
	  z-index: 1;
	}
	
	.bottom-horizontal-button{
		  position: absolute;
		  bottom: 0;
		  right: 0;
		  margin-bottom: 30rpx;
		  width: 80rpx;
		  z-index: 10;
		.btn {
			width: auto;
			height: auto;
			margin: 5px; /* 按钮间距 */
			padding: 10px; /* 按钮内部填充 */
			width: 80%; /* 按钮宽度 */
			text-align: center; /* 按钮文字居中 */
		 }
	}
</style>

实际结果:

五、修改地图样式

参考文章:
https://www.cnblogs.com/m7777/p/16280817.html
https://blog.csdn.net/qq_32077521/article/details/123224974
https://blog.csdn.net/weixin_43239880/article/details/129247279
https://juejin.cn/post/7017301189406490655

利用openlayers中的tileLoadFunction 的函数回调进行变色,结合css的filter属性来进行变色

// 添加一个使用离线瓦片地图的层
const offlineMapLayer = new TileLayer({
  source: new XYZ({
    url: 'http://192.168.7.34:8081' + '/{z}/{x}/{y}.png', // 设置本地离线瓦片所在路径,前面的地址是你输入http-server之后的服务地址
    tileLoadFunction: (imageTile, src) => {
      console.log(imageTile, src)
      // 使用滤镜 将白色修改为深色
      const img = new Image()
      // img.crossOrigin = ''
      // 设置图片不从缓存取,从缓存取可能会出现跨域,导致加载失败
      img.setAttribute('crossOrigin', 'anonymous')
      img.onload = () => {
        const canvas = document.createElement('canvas')
        const w = img.width
        const h = img.height
        canvas.width = w
        canvas.height = h
        const context = canvas.getContext('2d')
        context.filter = 'grayscale(98%) invert(100%) sepia(20%) hue-rotate(180deg) saturate(1600%) brightness(80%) contrast(90%)'
        context.drawImage(img, 0, 0, w, h, 0, 0, w, h)
        imageTile.getImage().src = canvas.toDataURL('image/png')
      }
      img.onerror = () => {
        imageTile.getImage().src = require('@/assets/logo.png')
      }
      img.src = src
    }
  })
})

offlineMapLayer函数:在加载瓦片的时候进行修改

css的filter属性解释:大概就是改变你图片的色相,饱和度,黑白,通透性等等,来实现图片变色的效果。【缺点,不能让地图指定哪个颜色,只能调个大概的好看的颜色。】

效果:

为什么多了这些图片??
目的是解决加了这个函数后,有些瓦片不全时会加载404的瓦片图,缩放后就没了的问题。
所以添加img.onerror事件,
把imageTile.getImage().src = require(‘@/assets/logo.png’)设置为一个图片出错时的替换图片就ok。对于404的图片,大家也可以这样子设置,设置成需要替换的图片就行。