前言

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

研究 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. Python之格式化输出,初始编码以及运算符

    一.题型 1.使用while循环输入 1 2 3 4 5 6  8 9 10 count = 0 while count < 10: count += 1   #count = count + ...

  2. c语言文件中关于while(!feof(fp)) 循环多输出一次的问题

      文件中关于while(!feof(fp)) 循环多输出一次的问题   feof(fp)有两个返回值:如果遇到文件结束,函数feof(fp)的值为1,否则为0.   当读到文件末尾时,文件指针并没有 ...

  3. 【Web安全】DoS及其家族

    不久前我分享过的Web安全概述获得了大家的广泛关注,说明大家对Web安全这一块还是很关心的,因此木可大大将陆续推出目前常见的Web攻击手段和对应的防范策略.本期向大家介绍的是DoS和它的家族. DoS ...

  4. 项目版本与分支管理之阿里AoneFlow模式分析

    前言 在我前期的项目管理的经验中,一个项目需要维护多个产品及多个版本,这给版本与分支的管理增加了难度.前期没有重视,使得分支太多太乱,版本也没记录好,引发了很多的问题.在多种分支与版本的管理模式下,最 ...

  5. 学习react系列(八)—— mixins迁移

    先来介绍一下mixins(混入) 先来看一段代码: const mixin = function(obj, mixins) { const newObj = obj; newObj.prototype ...

  6. .Net Core 通过依赖注入和动态加载程序集实现宿程序和接口实现类库完全解构

    网上很多.Net Core依赖注入的例子代码,例如再宿主程序中要这样写: services.AddTransient<Interface1, Class1>(); 其中Interface1 ...

  7. [LeetCode] Valid Parenthesis String 验证括号字符串

    Given a string containing only three types of characters: '(', ')' and '*', write a function to chec ...

  8. [USACO 04OPEN]MooFest

    Description 约翰的N 头奶牛每年都会参加“哞哞大会”.哞哞大会是奶牛界的盛事.集会上的活动很多,比如堆干草,跨栅栏,摸牛仔的屁股等等.它们参加活动时会聚在一起,第i 头奶牛的坐标为Xi,没 ...

  9. ●BZOJ 2839 集合计数

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2839 题解: 容斥原理 真的是神题!!! 定义 f[k] 表示交集大小至少为 k时的方案数怎 ...

  10. SPOJ NSUBSTR

    You are given a string S which consists of 250000 lowercase latin letters at most. We define F(x) as ...