uniapp使用webview在app端集成maptalk利用离线瓦片展示离线地图

1,038 阅读6分钟

maptalk介绍

maptalks 是一个开源的地图框架,可以自定义地图资源,支持多种插件和功能

maptalks.org/examples/cn…

官方网站案例,尽管有时候地图可能由于网络问题加载不出来,但是这个是最为详细的了。

地图在uniapp的展示

首先需要在hybrid下的html新建html文档,以maptalk网站提供的案例为例

<!DOCTYPE html>
<html>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>瓦片图层与地理投影 - WMS瓦片图层</title>
  <style type="text/css">
    html,body{margin:0px;height:100%;width:100%}
    .container{width:100%;height:100%}
  </style>
  <link rel="stylesheet" href="https://unpkg.com/maptalks/dist/maptalks.css">
  <script type="text/javascript" src="https://unpkg.com/maptalks/dist/maptalks.min.js"></script>
  <body>
    <div id="map" class="container"></div>

    <script>
      var map = new maptalks.Map('map', {
        center: [-0.113049, 51.498568],
        zoom: 6,
        spatialReference: {
          projection: 'EPSG:4326'
        },
        baseLayer: new maptalks.WMSTileLayer('wms', {
          'tileSystem': [1, -1, -180, 90],
          'urlTemplate': 'https://ows.terrestris.de/osm/service',
          'crs': 'EPSG:4326',
          'layers': 'OSM-WMS',
          'styles': '',
          'version': '1.3.0',
          'format': 'image/png',
          'transparent': true,
          'uppercase': true
        }),
        attribution: {
          content: '&copy ows.terrestris.de'
        }
      });

    </script>
  </body>
</html>

接着在view下面需要展示该内容的页面或者组件,先以页面为例

		<view
			style="background-color: aqua;height: 100vh;">
			<web-view id="webview" @message="onMessage"
			:update-title="false"
			:fullscreen="false"
			 :webview-styles="webviewStyle"
			 src="/hybrid/html/map11.html?latitude=117.690828&longitude=39.016343&height=300px"></web-view>
		</view>

其中webviewStyle决定了初始样式,这在h5页面可能是可以正常展示的,通过该样式调节在app存在异常,比如

在这个案例中

webviewStyle:{
					height: 350,
					width: 200,
					left:50,
					top:100,
					// position:"relative"
				},

但结果是

MapNotMoveFromTop.jpg

可以看到并没有上下进行移动,但进行了左右的移动。

事实上,官网提供的方法中只存在宽高设定,不存在top和left,但这个字段确实是有用的,可以用来调整嵌入位置,但得通过元素进行操作。

uniappWebViewInfo.png

onReady() {
			// // this.locate()
			// const self = this;
			// // #ifdef APP-PLUS
			// self.currentWebview = this.$scope.$getAppWebview()
			// // #endif
			this.latitude = '115.5'
			this.longitude = '35.5'
			// self.currentWebview = self.$scope.$getAppWebview().children()[0]
			console.log("wozai on readay");
			const self = this;
			// #ifdef APP-PLUS
			self.currentWebview = this.$scope.$getAppWebview()
			setTimeout(function() {
				const wv = self.currentWebview.children()[0]
				console.log("wv", wv, "这是wv");
				// wv.setStyle({top:150,height:300})
				wv.setStyle(self.webviewStyle)
			}, 800)
			// #endif

		},

MapMoveFromTop.png

上处案例之中,我们通过onReady后天地改变了弹窗大小,使得他向下向右移动。

闪屏问题

不过此时存在一个问题,就是onReady的速度是较慢的,webview的渲染时间是在onReady之前的,所以会出现webview突然出现在屏幕中,然后突然大小发生变化,闪屏的体验极差。因此需要进行处置。

webviewStyle: {
					height: 1,
					width: 1,
				}

设置最开始的页面大小只占一个像素,让他在最左侧不被注意,这样的显示就是正常的了。

组件中引用webview调整大小

通过以上步骤我们完成了一个基础地图的展示。不过当运用到组件的时候就是另一回事了,因为OnReady是页面的生命周期,在组件中无法生效。如果你尝试在组件使用mounted,这个时候会出现错误

MountedError.png

如果你使用OnReady,则什么都不会发生!!

如果你使用OnReady,则什么都不会发生!!

如果你使用OnReady,则什么都不会发生!!

这意味着我们在组件是无法正常去修改其大小的,必须要去页面上进行获取。这并不难解决。

但是这样并不能满足我想要的效果,我需要的是一个列表页面,点击列表中的每个内容都会弹框弹出一个地图,展示内容的位置。

这个需求的实现是这样,我要在弹窗打开的时候将v-if的webview放开,然后重新设定webview的大小,之后将经纬度传递给webview,此时webview根据传递的经纬度创建地图,添加图层上的点后移动到指定点的位置。

//处理地图创建于webview大小重置的函数,不处理弹窗打开
//页面.vue
refreshMap(dataObject){
				//动态获取当前页面的宽高
				let width = 0
				let height = 0
				uni.getSystemInfo({
					success: function (res) {
						width = res.windowWidth
						height = res.windowHeight
						console.log("宽度",res.windowWidth);
						console.log("高度",res.windowHeight);
					}
				}
				)
				let that = this
				//等待地图,在我的手机800ms大概可以正常的加载,如果太短可能对象还未生成			     
				//that.$scope.$getAppWebview().children()[0] 这个拿不到对的
				setTimeout(function(){
					const wv = that.$scope.$getAppWebview().children()[0]
					//这里让webview执行指定的js,传递参数 dataObject包含了我需要的经纬度
					wv.evalJS(`getParams(${JSON.stringify(
						dataObject
					)})`)
					console.log(height*0.6);
					console.log(height*0.5);
					console.log(width);
					wv.setStyle({
						height: height*0.3,
						width: width*0.805,
						left:width*0.1,
						top:height*0.73,
						// position:"relative"
					})
				}, 800)
			}

我的组件中

	<view class="content">
		<web-view
			class="webb"
			@message="onMessage"
			:update-title="false"
			:webview-styles="webviewStyle"
			:src="`/hybrid/html/map.html?latitude=${latitude}&longitude=${longitude}&height=${height+'vh'}&name=${name}`"></web-view>
	</view>

在webview界面(我在这个页面引入了vue,可以不用)

		<script>
			var url = window.location
			var wd = window
			var vm = new Vue({
				el: "#home",
				data() {
					return {
						name: '某任务',
						height: '100vh',
						dataShow: {},
						queryDic: {},
						latitude: null,
						longitude: null,
						pointLayer: null,
						map: {},
						// 地图引擎
						mapEngine: null,
						zoom: 12,
						// 轴承
						bearing: 0,
						// 屏幕坐标
						containerPoint: {
							x: null,
							y: null
						},
						// 地图坐标
						coordinatePoint: {
							x: null,
							y: null
						}
					};
				},
				methods: {
					//移动
					toOriginPlace() {
						this.map.flyTo({
							center: [this.latitude, this.longitude], // 中心点
							zoom: 12, // 缩放比例
							// pitch: 45 // 倾斜度
						})
					},
				},
				mounted() {
					let that = this
					const config = {
						tileBZ: "http://www.zyzcgis.xyz/tile/wg/pc/baidu/"
					}

					wd.getParams = (data) => {
						self.dataShow = data
						that.longitude = data.longitude
						that.latitude = data.latitude
						const fuyuan = data.fuyuan
						const centerCoordinate = [data.longitude,data.latitude ]
						that.$nextTick(() => {
							that.map = new maptalks.Map('map', {
								center: ['117.690828','39.016343'],
								zoom: 14,
								minZoom: 1,
								maxZoom: 19,
								attribution: false,
								// zoomControl: true, // add zoom control
								scaleControl: true, // add scale control
								baseLayer: new maptalks.TileLayer('base', {
									'urlTemplate' : 'http://{ip地址写你的}:38666/gis/{z}/{x}/{y}.png',
									'subdomains':[0, 1, 2, 3],
								}),
							});
							that.$nextTick(() => {
								if(!!fuyuan){
									new maptalks.control.Toolbar({
											'position': 'bottom-right',
											'reverseMenu': false,
											'items': [{
												item: '复原',
												click: function() {
													that.toOriginPlace();
												}
											}]
										})
										.addTo(that.map);
								}
								this.map.flyTo({
									center: centerCoordinate, // 中心点
									zoom: 12, // 缩放比例
									// pitch: 45 // 倾斜度
								})
								const centerPoint = centerCoordinate
								const placeName = data.name
								const point = new maptalks.Marker(
									centerPoint, {}
								);
								var marker2 = new maptalks.Marker(
									centerPoint, {
										'symbol': [{
												'markerFile': './static/dingwei01.png',
												'markerWidth': 20,
												'markerHeight': 24,
												'markerDx': 0,
												'markerDy': 0,
												'markerOpacity': 1
											},
											{
												'textFaceName': 'monospace',
												'textFill': '#34495e',
												// 'textHaloFill': '#fff',
												'textName': placeName,
												'textHaloRadius': 4,
												'textSize': 18,
												'textWeight': 'bold',
												// 'textVerticalAlignment': 'top',
												'textDy': 14

											}
										]
									}
								);
								const layer = new maptalks.VectorLayer('vector')
								layer.addGeometry(marker2)
								that.map.addLayer(layer)
								that.pointLayer = layer
							})

						})
					}
				},
				created() {},
			});
			</script>

这样就可以正常的展示内容了,效果如下:

mapShow.png

以上就是在uniapp集成maptalk展示地图的操作。

离线地图

代码的修改

关于map.html这个webview页面的内容,如果要离线,需要把所有的script引入的内容下载到本地离线。

		<link rel="stylesheet" href="./css/maptalks.css">
		<script src="./js/vue.js"></script>
		<script src="./js/hybrid_html_uni.webview.1.5.5.js"></script>
		<script src="./js/axios.min.js"></script>
		<script src="./elementui/index.js"></script>
		<script type="text/javascript" src="./js/maptalks.min.js"></script>

你可以在找到它们的h5引用,复制连接后手动添加到hybrid文件夹下进行引用。

瓦片下载

离线地图的展示,则需要下载瓦片,瓦片的下载工具我推荐github.com/atlasdatate…

这是一个多线程的瓦片下载工具,大概的使用方法如下

先去寻找geoJson来限制范围,可以从这个网站进行下载www.geojson.cn/

mapTiles.png

将待下载的指定的行政区划放在geojson之中,点击conf.toml修改配置

[app]
	version = "v 0.1.0"
	title = "MapCloud Tiler"
[output]
	#可以填写 mbtiles/file 我没用过mbtiles
	format ="file"
	#输出文件,默认为当前的output文件夹
	directory ="output"
[task]
	#number of fetchers
	#几个fetch一起下
	workers = 3
	#number of savers
	#几个写入
	savepipe = 1
	#min request interval, a speed limit, unit millisecond
	#最小请求间隔,限速 单位毫秒
	timedelay = 50

[tm]
	#下载的名称
	name = "gaode satelite"
	#设定地图的层级 最小是多少,最大是多少
	min = 0
	max = 18
	#可以是 pbf/png/jpg
	format = "png"
	#可以是 xyz/tm
	schema = "xyz"
	# 你下载地图的位置,我这里用的是高德的
	url = "https://webrd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}"
#lrs can set diff boundaries for diff levels
  [[lrs]]
  #设定地图的层级 最小是多少,最大是多少,改的话这里和上面都改一下,我不知道这个和上面的具体怎么填写
  	min = 0
  	max = 18
  	# 指定的geojson路径,填你下的文件即可
  	geojson = "./geojson/tianjin.geojson"

之后运行tiler.exe下载即可,瓦片可能比较多,消耗时间较长,不清楚有没有断点续传的操作,可以分开下载,1-16层下一次,17层下一次,18层下一次,不同地区分开下最后在进行合并。

[[lrs]]好像可以使用多个,但作者的文档我并没有看的很明白,这样使用可以下载,额外的更便利的操作我就不知道了。

瓦片下载完成后使用nginx进行代理,这个教程较多,可以参考这个blog.csdn.net/weixin_4102… ,然后将webview页面的修改即可。

baseLayer: new maptalks.TileLayer('base', {
									'urlTemplate' : 'http://{ip地址写你的}:38666/gis/{z}/{x}/{y}.png',
									'subdomains':[0, 1, 2, 3],
								}),