前言

要说清楚这个题目对我来说可能都不是一件简单的事情,我简单尝试。

研究 GIS 的人应该都清楚在 GIS 中最常用的技术是瓦片技术,无论是传统的栅格瓦片还是比较新颖的矢量瓦片,一旦将数据切好瓦片就会造成其层级固定,假如说 0 - 11 级,请求此层级范围内数据的时候能够正常响应,但是当用户请求超过最高级(假如为 12 )的时候该如何处理呢?传统方式只能返回 404 ,即显示空白数据,然而有没有更好的方式呢,能够使得用户在请求超过最高级数据的时候能够优雅的并且正确的返回数据而不是直接 404。这个问题可以用手动挡汽车和自动挡汽车进行类比,传统方式就像手动挡,最大只能到 5 档,而现在的需求是希望挡位能变的更高些。但愿我已经清晰的说明了此问题,本文对此原理和实现方案进行简单探讨。

一、实现

1.1 原理分析

这个解决方案倒是很容易想象,当超过最大层级(以下简称 zoom)的时候(> 11 级)我们只需要读出最大 zoom(11 级)的此范围内数据对应的瓦片,然后将此瓦片根据此范围进行切割并重新采样到 256 * 256 即可。

这里面涉及到了瓦片的金字塔体系的一些常用概念。首先层级越大表示分辨率越高,即显示出来的数据越清晰,每提高一层数据量增加4倍,即一个低层级的瓦片包含了比他高一层级的四个瓦片,整个看下来便像一个金字塔一样;而常用的每个瓦片的大小为 256 * 256,直白的说就是一个 256 * 256 的 PNG 或者 JPG 图片,当然也可以是其他尺寸,每个瓦片对应一个 x、y、z 编号,x、y 代表瓦片的行列号,z 代表瓦片的 zoom,屏幕范围内数据所有瓦片按照 x、y、z 正常排列显示出来便得到了整个地图(或者其他数据,如遥感等),就像房顶的瓦片一样,所以称为瓦片技术。有关具体技术和描述可以百度之。

1.2 实现方案

有了上面的分析,其实这件事情应该已经不困难了。

1.2.1 层级

首先获取当前数据的最大层级并判断当前请求是否大于此层级。

def getMaxZoom(layerName: String) =
attributeStore.layerIds
.groupBy(_.name)(layerName)
.map(_.zoom)
.max def exist(layerId: LayerId) =
attributeStore.layerExists(layerId)

第一个函数取到当前 layerName 数据的最大层级,其中 attributeStore 为你当前 backend 的后台,可以参考此前文章。

exist 函数判断当前 layerId 是否存在, layerId 包含 name 信息和 zoom 信息。当然此处你可以直接判断此 layerId 的 zoom 是否大于第一个函数取到的 maxZoom,但是此处我这么写也是埋下一个伏笔,会在后文介绍。

1.2.2 取到请求瓦片的范围

想要取到最大层的数据首先要取到瓦片包含数据的范围,这个范围我们只能根据所请求瓦片的 z、y、z 获得,如下:

val layerId: LayerId = LayerId(name, maxZoom)
val rmd = attributeStore.read[TileLayerMetadata[SpatialKey]](layerId, Fields.metadata)
val layoutLevel = ZoomedLayoutScheme(rmd.crs).levelForZoom(rmd.extent, z)
val mapTransform = MapKeyTransform(rmd.crs, layoutLevel.layout.layoutCols, layoutLevel.layout.layoutRows)
val targetExtent = mapTransform(x, y)

首先取到 maxZoom 层的元数据,根据投影(rmd.crs)、范围(rmd.extent)及 zoom 信息,获取到当前 z 层的 layout,这个具体细节涉及到金字塔理论,大意是根据投影、范围和层级就可以取到瓦片的编号和范围情况,最终也正是根据 x、y 计算出瓦片数据范围 targetExtent。

1.2.3 取到最大层级对应瓦片

有了瓦片的范围,我们就可以在最大曾中取出此瓦片,如下:

val GridBounds(nx, ny, _, _) = rmd.mapTransform(targetExtent)
val sourceExtent = rmd.mapTransform(nx, ny)
val maxZoomTile = tileReader.reader[SpatialKey, Tile](layerId).read(SpatialKey(nx, ny))

tileReader 是 ValueReader 对象,同样与所采用的 backend 有关。其中 nx、ny 正是 maxZoom 层对应的瓦片编号,此处同样用到金字塔理论,高层级的瓦片必然包含在比他层级低的某一个瓦片里,即 sourceExtent 必然能够完全覆盖 targetExtent。

1.2.4 将瓦片重采样到所请求的 zoom

现在只需要我们对获取到的瓦片进行裁切并重采样到 256 * 256 即可,如下:

val targetTile = sourceTile.resample(sourceExtent, RasterExtent(targetExtent, 256, 256))

这样就获取到了最终的请求瓦片,将此瓦片返回浏览器等其他请求源即可。

1.3 效果

展示一下最终效果:

11 级瓦片是正常取得的瓦片,12 级瓦片即为通过此种方式由 11 级瓦片重采样得到的。

二、进一步思考

做产品和做项目有着本质的区别,一个项目可能只需要考虑到通用情况即可,而产品则必须考虑到方方面面,还记得我在上面留的伏笔吗,在那里我没有采用判断所请求 zoom 是否大于 maxZoom 的方式,而是直接判断 exist,这里面有个逻辑问题。假如切瓦片的时候不是每一层都有切到,必然我切了 0 - 5、7 - 11,而没有切第 6 层,那么采用这种方式肯定是有问题的,并且出现这种情况的时候整个逻辑都需要重新修改,因为第 6 层的某个瓦片肯定包含了2 ^ (2 ^ 5) 个 11 层(maxZoom)的瓦片,这样我们就不能简单的只取出一个,而应该将其全部取出并进行拼接然后再重采样。

再进一步思考,碰到这种方式的时候我们是不是可以取出第 5 层或者第 7 层的某个/些瓦片而不是非要 maxZoom 层的,因为接近的层数据更相似(此处牵扯到层级可视化表达的问题)。

所以这些都需要我们丰富和设计好逻辑,只有这些都考虑清楚才能设计出完美的产品。具体代码此处就不放出了,如果有需要可以探讨。

三、总结

本文介绍了如何在所请求的瓦片层级不存在的情况时通过取出最大层或者相近层的瓦片并进行重采样操作,从而优雅的返回瓦片数据。

Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html

geotrellis使用(四十)优雅的处理请求超过最大层级数据的更多相关文章

  1. geotrellis使用(十)缓冲区分析以及多种类型要素栅格化

    目录 前言 缓冲区分析 多种类型要素栅格化 总结 参考链接 一.前言        上两篇文章介绍了如何使用Geotrellis进行矢量数据栅格化以及栅格渲染,本文主要介绍栅格化过程中常用到的缓冲区分 ...

  2. geotrellis使用(十六)使用缓冲区分析的方式解决投影变换中边缘数据值计算的问题

    Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html 目录 前言 问题探索 采样说明 实现方案 总结 一.前言     ...

  3. 如何在发型不乱的前提下应对单日十亿计Web请求

    原文地址:http://developer.51cto.com/art/201502/464640.htm 就在不久之前,AppLovin移动广告平台的单一广告请求数量突破了200亿大关——相当于每一 ...

  4. Python之路(第四十六篇)多种方法实现python线程池(threadpool模块\multiprocessing.dummy模块\concurrent.futures模块)

    一.线程池 很久(python2.6)之前python没有官方的线程池模块,只有第三方的threadpool模块, 之后再python2.6加入了multiprocessing.dummy 作为可以使 ...

  5. 使用Typescript重构axios(二十八)——自定义序列化请求参数

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

  6. geotrellis使用(十二)再记录一次惨痛的伪BUG调试经历(数据导入以及读取瓦片)

    Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html 目录 前言 BUG还原 查找BUG 解决方案 总结 后记 一.前 ...

  7. 第四十四章 微服务CICD(6)- gitlab + jenkins + docker + k8s

    总体流程: 在开发机开发代码后提交到gitlab 之后通过webhook插件触发jenkins进行构建,jenkins将代码打成docker镜像,push到docker-registry 之后将在k8 ...

  8. Deep learning:四十六(DropConnect简单理解)

    和maxout(maxout简单理解)一样,DropConnect也是在ICML2013上发表的,同样也是为了提高Deep Network的泛化能力的,两者都号称是对Dropout(Dropout简单 ...

  9. [书]WALL·E、龙与地下铁、中国美丽的故事、故事新编、四十自述、书虫、人工智能、大话数据结构

    下午有时间,逛了逛了书城,看到了一些书.在这里总结一些自己的感受.   一.<龙与地下铁>     这本书是我首先看到的,就在靠前的新书区.是小说,我没看里面的内容,但是被书封皮的宣传文案 ...

随机推荐

  1. tomcat 热替换class

    需要在server.xml中做以下配置: 在host节点内加入<Context>标签,reloadable属性设置为true. <Host name="localhost& ...

  2. antd 主题色

    如果是自己配置的reac项目,而非官方推荐的creat-react-app或者dva-cli等阿里自己开发的脚手架去引入antd,会有两个问题 第一,用babel-plugin-import设置sty ...

  3. [Luogu1801] 黑匣子 - Treap

    Description Black Box是一种原始的数据库.它可以储存一个整数数组,还有一个特别的变量i.最开始的时候Black Box是空的.而i等于0.这个Black Box要处理一串命令. 命 ...

  4. nginx和nfs

    1.安装nginx #yum install epel-release -y #yum install nginx -y #vim /usr/local/nginx/conf/nginx.conf - ...

  5. 2018 php的flush和ob_flush不起作用 整理解决

    2018 php的flush和ob_flush不起作用 整理解决成功解决. 要点 : 使用函数 str_repeat2处配置:检查php.ini.Nginx 中有下面两个设置使用方式:echo str ...

  6. hdu1022 Train Problem I---模拟栈

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1022 题目大意: 车的进出站问题,先给出N个火车,再按序列一的方式进站,判断能否以序列二的方式出站,若 ...

  7. [转]Python UnicodeEncodeError: 'gbk' codec can't encode character 解决方法

    使用Python写文件的时候,或者将网络数据流写入到本地文件的时候,大部分情况下会遇到:UnicodeEncodeError: 'gbk' codec can't encode character ' ...

  8. Git Bash

    Git Bash是Git的命令行工具,可以执行Git的所有命令,但是当我们想把一个URL粘贴到Git Blash时,Ctrl+V或者右键粘贴不起作用了 方法1-使用快捷键"Insert&qu ...

  9. C#之转换两个不同类型但是成员相同的对象

    /// <summary> /// 转换两个不同类型但是成员相同的对象 /// </summary> /// <typeparam name="T"& ...

  10. [LeetCode] Cherry Pickup 捡樱桃

    In a N x N grid representing a field of cherries, each cell is one of three possible integers. 0 mea ...