webgis —— 为瓦片构建缓存
前言
了解 webgis 或接触过 webgis 相关应用的童鞋应该知道,webgis 应用有个特点,加载影像地图或者矢量地图是刚需。
相信对于国内的 gis 行业的从业者来说,天地图应该不是一个陌生的存在吧。
对比其他商业地图(高德、百度、腾讯地图等)而言,天地图有几个明显的优点:
- 它是官方出品的地图,权威性较高。
- 它更新比较及时,每年都会对部分区域的影像进行更新。
- 国内热门地方,影像精度较高,达到亚米级别。
- 坐标系采用 wgs84 坐标系,无偏差,无加密。
至于缺点嘛,这里就不做过多的介绍了,用过的童鞋自然会明白。
当然,其实这篇文章的出发点,解决的就是天地图的一大缺点——服务不稳定。
这不,最近很长一段时间,天地图的服务都不给力,经常加载极其缓慢,还动不动会出现响应超时的情况。
如果只是自己用用倒也没啥,但是被老板和客户碰到,就闹心了,接受到过很多次反馈。
虽然,这问题有外部的原因,也并不全是我们自己能解决的,但是有没有什么方法,可以作出一定程度的优化呢?
不然,这个问题,极其的影响 webgis 应用的使用体验。
毕竟,公司的业务都或多或少和 webgis 相关。
思考
对于这种问题,其实业界通常的做法,就是加缓存。
就是,经典的——用空间换时间。
当然,对于我们遇到的这个问题来说,其实有两种解决的思路。
一种是,后端做缓存;一种是,前端做缓存。
当然这两种方案,其实都是针对当前问题所作出的妥协下的无奈。
我们知道,一般而言,无论是使用 cesium 还是 openlayers 等 webgis 框架,直接目的都是为了能够在网页上直接使用地理空间数据。
而地理空间数据有个特点,就是空间范围大,时间跨度长,这一特点导致地理空间数据的数据量量普遍都很大。
所以,虽然公司是专门做 gis 方面应用的,也会定期制作发布一些区域的影像图,但是像天地图这种全球性底图,我们也很难做到自己发布一份来使用。
至于其中原因,说起来很简单,因为做这个事情的成本和收益不对等。
况且天地图服务本身质量不错,又可以免费使用,为什么要吃力不讨好的另起炉灶呢?
后端缓存
如果要采用后端缓存的方案,直接用 nginx 做转发即可。
我们知道,对于天地图而言,这是一个瓦片的请求地址(替换成自己的 token 即可):
https://t2.tianditu.gov.cn/img_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix=12&TileRow=1665&TileCol=3413&style=default&format=tiles&tk=你的token
对于不同的瓦片而言,请求变化的只是请求地址里面的几个参数:
- TileMatrix——层级数
- TileRow——行号
- TileCol——列号
所以,了解了这些以后,再稍微研究下 nginx 的缓存策略,想要实现后端对天地图的缓存就很简单了。
在这里,我只做一下简单的介绍,不作详细的赘述,有兴趣的童鞋可以自行研究一下。
前端缓存
有的同学可能会说,有了后端缓存的方案,为什么要研究前端缓存呢?
不要着急,让我们先缕一缕,做个分析对比。
后端缓存,实质上是把天地图服务器上的瓦片资源,缓存到我们自己的服务器上,会增加我们自己服务器的存储和带宽;但是它有个优点是,一旦有了缓存,以后高频访问的地方,就可以不依赖天地图的服务了,并且自己所有的项目都能用。
但是前端缓存也有自己的优势,不占用自己服务器的存储和带宽;一旦有了缓存以后,能支持离线加载,速度更快;每个用户都有自己的小型缓存服务,不会相互影像。
当然前端缓存,也不是没有缺点的,其中一个重要的影响就是,会增加代码的复杂度,对开发人员的要求较高;效果比不上浏览器的默认缓存,稍微会影响一些加载效率;对用户电脑的要求较高。
我们姑且不评价采用哪种方式更好,单纯从解决问题的角度出发,来探讨下如何增加一层前端缓存。
我们知道,用 Window.localStorage - Web APIs | MDN 或者 Window.sessionStorage - Web APIs | MDN 肯定是不行,因为它们的容量都有限制,达不到我们的使用要求。
Web SQL 基本已经处于废弃状态,因此,基本上可以确定,我们能选用的方案就是 Using IndexedDB - Web APIs | MDN。
当然 IndexedDB 原生的 api 写起来比较复杂,可以用已经封装好的框架,帮助我们开发,可以参考 mdn 页面的推荐:Using IndexedDB - Web APIs | MDN。
当然,这篇文章,不太会专注于怎么把数据存储在 IndexedDB 里面,如果你不了解而刚好又感兴趣的话,可以翻翻博主以前的文章,相信会有一些启发。
既然是与 webgis 相关的文章,那么我们当然要从 cesium、openlayers 这些 webgis 常用的框架入手,介绍,如何在这些框架里面实现瓦片的前端缓存。
openlayers
对于 openlayers 来说,想要实现该功能很简单。
加载 wmts 瓦片地图的时候 ,有一个选项 tileLoadFunction
,支持传入一个自定义的加载函数。
OpenLayers v7.1.0 API - Class: WMTS
我们先来看看,该函数的默认值:
function(imageTile, src) {imageTile.getImage().src = src;
};
分析可知,其主要干的事情就是给目标瓦片对象赋予 src
值。
而对于某个瓦片的链接来说,我们可以直接从 url 参数中分析出来所有我们需要的信息,因此我们直接在链接上下文章即可。
比如我们在使用 openlayers 加载天地图时候,该函数的第二个参数 src
可能是下面的值:
https://t2.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL=49&TILEROW=28&TILEMATRIX=6&tk=你的token
那么我们就可以直接用正则或者 URLSearchParams - Web API 接口参考 | MDN 对 url 进行处理,得到我们想要的层级、行列号、图层名等信息,作为存储的唯一标识。
接下来,我们需要通过 ajax 获取到图像的二进制数据,然后通过 URL.createObjectURL() - Web APIs | MDN 将二进制文件转成一个链接,直接赋值给 imageTile.getImage().src
即可。
关键代码如下:
const tileLoadFunction = async(imageTile, src) => {// 获取 keyconst cname = this._getKeyName(src);// 查询下缓存内是否有该数据const data = await getOneByName(cname);// 拿到目标 image 对象const image = imageTile.getImage();image.onload = function onload() {// 图片加载完,释放一个URL资源.window.URL.revokeObjectURL(this.src);};// 如果存在缓存,用缓存数据if (data) {image.src = URL.createObjectURL(data.blob);} else {// 如果不存在缓存,通过 fetch 加载数据const res = await fetch(src);const newBlob = await res.blob();image.src = URL.createObjectURL(newBlob);// 加入缓存addOneImage({name: cname,blob: newBlob});}
}
cesium
要想在 cesium 中使用前端缓存,实质上和在 openlayers 实际上大同小异。
但是比较棘手的是,cesium 并未直接提供改写瓦片请求的接口,因此我们需要通过重载源码的方式,来实现我们的需求。
我们知道,在 cesium 的架构中,ImageryProvider
是所有图层的容器对象,每个 layer 必定会对应着一个该对象的实例。
分析 cesium 的源码可知, ImageryProvider
对象的 loadImage
方法,是图层中加载每个瓦片的方法。
因此,我们在 loadImage
上下手即可:
// 将 ImageryProvider 对象上的 loadImage 方法重命名
ImageryProvider.loadImage2 = ImageryProvider.loadImage;
// 重载 loadImage 方法
ImageryProvider.loadImage = function loadImage(imageryProvider, url) {// 符合我们需求的,调用重载的方法if (imageryProvider instanceof WebMapTileServiceImageryProvider) {// 返回 promisereturn new Promise((resolve) => {// 获取唯一的 keyconst {layer,tilecol,tilematrix,tilerow,} = url.queryParameters;const cname = `${layer} ${tilematrix} ${tilerow} ${tilecol}`;// 从缓存里获取数据getOneByName(cname).then(async (data) => {// 存在数据if (data) {const imgUrl = URL.createObjectURL(data.blob);const img = new Image();img.src = imgUrl;img.crossOrigin = 'Anonymous';img.onload = () => {// 直接将图片返回resolve(img);};} else {// 不存在数据const resource = Resource.createIfNeeded(url);const newBlob = await resource.fetchImage({preferBlob: true,preferImageBitmap: false,flipY: true,});// 加入缓存addOneImage({name: cname,blob: newBlob.blob});// 将 fetchImage 获取到的对象返回resolve(newBlob);}});});}// 调用默认的方法return ImageryProvider.loadImage2.call(this, imageryProvider, url);
};
后记
就像文章前面所说的那样,无论是前端缓存还是后端缓存,都只是一种辅助措施,一种多方考量下的无奈之举,两者都有自己的缺点和优势。
不过话又说回来,web 应用作手动缓存,给人一种多此一举的感觉。
毕竟很多情况下,浏览器为了加快网页的访问速度,已经尽可能的做了很多事情。
而通常情况下,效率最高的方式,肯定是去遵循浏览器的默认缓存策略,从而使得我们的应用达到最好的使用效果。
奇淫巧计有一定的帮助,但是终究作用还是有限,解决问题的同时,势必会引发新的问题。
webgis —— 为瓦片构建缓存相关推荐
- 矢量切片_数据粒度均衡的二维矢量瓦片构建方法
作 者 信 息 应 申1,2,王子豪1,杜志强3,丁火平4, 李翔翔4 (1. 武汉大学 资源与环境科学学院,湖北 武汉 430079:2. 自然资源部城市国土资源监测与仿真重点实验室,广东 深圳 5 ...
- react中使用构建缓存_如何在React中构建热图
react中使用构建缓存 Heat maps are a great way of visualizing correlations among two data sets. With colors ...
- react中使用构建缓存_通过构建海滩度假胜地网站,了解如何使用React,Contentful和Netlify...
react中使用构建缓存 In this full course from John Smilga you will learn React by building a beach resort we ...
- 【高并发】高并发环境下构建缓存服务需要注意哪些问题?我和阿里P9聊了很久!...
写在前面 周末,跟阿里的一个朋友(去年晋升为P9了)聊了很久,聊的内容几乎全是技术,当然了,两个技术男聊得最多的话题当然就是技术了.从基础到架构,从算法到AI,无所不谈.中间又穿插着不少天马行空的想象 ...
- android使用软引用构建缓存
转自:http://www.devdiv.com/Android-%E4%BD%BF%E7%94%A8%E8%BD%AF%E5%BC%95%E7%94%A8%E6%9E%84%E5%BB%BA%E7% ...
- react中使用构建缓存_使用React和Netlify从头开始构建电子商务网站
react中使用构建缓存 In this step-by-step, 6-hour tutorial from Coding Addict, you will learn to build an e- ...
- react中使用构建缓存_完整的React课程:如何使用React构建聊天室应用
react中使用构建缓存 In this video course, you'll learn React by building a chat room app. 在本视频课程中,您将通过构建聊天室 ...
- react中使用构建缓存_通过在React中构建Tic Tac Toe来学习ReasonML
react中使用构建缓存 3. 7. 2018: UPDATED to ReasonReact v0.4.2 3. 7. 2018:更新为ReasonReact v0.4.2 You may have ...
- 在Kotlin中使用Gradle构建缓存
Kotlin 1.2.21允许Kotlin项目使用构建缓存.通过Gradle的构建缓存功能,可以提升Kotlin代码的编译速度,加快开发周期.本文将结合实际例子,介绍利用Gradle加速Kotlin代 ...
最新文章
- usaco Big Barn
- 使用Spinner和setDropDownViewResource
- static关键字_聊聊static关键字
- sql注入问题-视图-事物-以及存储过程(可视化工具)
- python中lxml模块的使用
- 网页排版规则:你需要知道的
- 华师大数据科学考研_华东师范大学数据科学与工程需要复习哪些内容?
- GTK+图形化应用程序开发学习笔记(七)—标签构件.事件盒构件
- ArcGIS 栅格函数在线调用详解
- 《优柔有情人》读后感6000字
- 三国杀移动版武将台词大全
- 网络协议—三要素与五层网络协议
- 【Android组件化】javaPoet的使用
- 图解组策略配置禁止修改IE主页
- 香港室内设计公司【Junee】申请纳斯达克IPO上市,募资2500 万美元
- 大战AV终结者(一)……AV简介
- 正确的学习思路(上)、
- C语言输出ASCII码表1/2
- UI设计师常用的8款工具介绍,建议收藏!
- 软件项目管理 7.5.项目进度模型(SPSP)