前言

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

研究 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. 测试驱动开发实践4————testSave之新增文档分类

    [内容指引] 1.确定"新增文档分类"的流程及所需的参数 2.根据业务规则设计测试用例 3.为测试用例赋值并驱动开发 一.确定"新增文档分类"的流程及所需的参数 ...

  2. python基础二(基础数据类型)

    一. 引子 1. 什么是数据 x=10,10是我们要存储的数据 2. 为何数据要分不同的类型 数据是用来表示状态的,不同的状态就应该用不同的类型的数据去表示 3.数据类型 数字 字符串 列表 元组 字 ...

  3. Django之auth模块(用户认证)

    auth模块简介 auth模块是对登录认证方法的一种封装,之前我们获取用户输入的用户名及密码后需要自己从user表里查询有没有用户名和密码符合的对象, 而有了auth模块之后就可以很轻松的去验证用户的 ...

  4. POJ-1679 The Unique MST---判断最小生成树是否唯一

    题目链接: https://vjudge.net/problem/POJ-1679 题目大意: 给定一个无向连通网,判断最小生成树是否唯一. 思路: (1)对图中的每条边,扫描其他边,如果存在相同权值 ...

  5. hdu1051 Wooden Sticks---贪心

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1051 题目大意:给你n根木棍的长度和重量.根据要求求出制作该木棍的最短时间.建立第一个木棍需要1分钟 ...

  6. git的理论基础

    GIT是目前世界上最先进最牛逼的分布式版本控制系统git维护的三棵树分别是工作区域.暂存区域.git仓库工作区域:就是你平时存放项目代码的地方暂存区域:用于临时存放你的改动,事实上它只是一个文件,保存 ...

  7. 基于 MySQL 的数据库实践(自然连接)

    在基本查询一节的示例中,我们有从 instructor 和 teaches 表组合信息,匹配条件是 instructor.ID 等于 teaches.ID 的查询,ID 属性是两个表中具有相同名称的所 ...

  8. Vue还有这种操作?浅析几个新手常常忽略的API

    一:实现子组件与父组件双向绑定的"sync": 一般来说,我们实现父子组件值的传递通常使用的是[props]和自定义事件[$emit].父组件通过[props]将值传给子组件,子组 ...

  9. SQL SERVER 索引名前缀代表的意思

    PK - Primary Key IX - Non-Unique Index AK - Unique Index (AX should have been AK (Alternate Key)) CK ...

  10. 开发一款APP所需要的时间

    "要多少钱""要多少时间"这应该是一个企业在打算开发一款APP时问到最多的问题了.的确,现在的人不管做什么事情都讲究计划,更何况在这个时间就是金钱的时代,企业如 ...