geotrellis使用(四十)优雅的处理请求超过最大层级数据
前言
要说清楚这个题目对我来说可能都不是一件简单的事情,我简单尝试。
研究 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使用(四十)优雅的处理请求超过最大层级数据的更多相关文章
- geotrellis使用(十)缓冲区分析以及多种类型要素栅格化
目录 前言 缓冲区分析 多种类型要素栅格化 总结 参考链接 一.前言 上两篇文章介绍了如何使用Geotrellis进行矢量数据栅格化以及栅格渲染,本文主要介绍栅格化过程中常用到的缓冲区分 ...
- geotrellis使用(十六)使用缓冲区分析的方式解决投影变换中边缘数据值计算的问题
Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html 目录 前言 问题探索 采样说明 实现方案 总结 一.前言 ...
- 如何在发型不乱的前提下应对单日十亿计Web请求
原文地址:http://developer.51cto.com/art/201502/464640.htm 就在不久之前,AppLovin移动广告平台的单一广告请求数量突破了200亿大关——相当于每一 ...
- Python之路(第四十六篇)多种方法实现python线程池(threadpool模块\multiprocessing.dummy模块\concurrent.futures模块)
一.线程池 很久(python2.6)之前python没有官方的线程池模块,只有第三方的threadpool模块, 之后再python2.6加入了multiprocessing.dummy 作为可以使 ...
- 使用Typescript重构axios(二十八)——自定义序列化请求参数
0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...
- geotrellis使用(十二)再记录一次惨痛的伪BUG调试经历(数据导入以及读取瓦片)
Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html 目录 前言 BUG还原 查找BUG 解决方案 总结 后记 一.前 ...
- 第四十四章 微服务CICD(6)- gitlab + jenkins + docker + k8s
总体流程: 在开发机开发代码后提交到gitlab 之后通过webhook插件触发jenkins进行构建,jenkins将代码打成docker镜像,push到docker-registry 之后将在k8 ...
- Deep learning:四十六(DropConnect简单理解)
和maxout(maxout简单理解)一样,DropConnect也是在ICML2013上发表的,同样也是为了提高Deep Network的泛化能力的,两者都号称是对Dropout(Dropout简单 ...
- [书]WALL·E、龙与地下铁、中国美丽的故事、故事新编、四十自述、书虫、人工智能、大话数据结构
下午有时间,逛了逛了书城,看到了一些书.在这里总结一些自己的感受. 一.<龙与地下铁> 这本书是我首先看到的,就在靠前的新书区.是小说,我没看里面的内容,但是被书封皮的宣传文案 ...
随机推荐
- 解析 Javascript - this
在函数中 this 到底取何值,是在函数真正被调用执行的时候确定下来的,函数定义的时候确定不了. 因为 this 的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境.当你在 ...
- 【webGL入门2】点线面的绘制
用js绘制webGL的点: THREE.Vector3 = function ( x, y, z ) { //用THREE声明的变量都是全局变量.this.x = x || 0;this.y = y ...
- geotrellis使用(三十七)COG 基础介绍
前言 Geotrellis 已经迭代到了 2.0 版本(截止写作此文时为 2.0.0-SNAPSHOT 版),2.0 版多了很多新的特性,其中最重要的应该就是 COG,COG 是什么鬼?刚看到时我也是 ...
- WPF经纬度控件
1.需求及思路 绘制一个控件,可以设置经纬度,经纬度用double类型表示,支持绑定数据,数据更改通知.错误验证,输入检校. 大致设计界面如下图: 2.主要代码 主要代码在于计算,double类型转换 ...
- 格式化输出prettify()
BeautifulSoup的格式化输出函数: print(soup.prettify())
- Python面向对象——重写与Super
1本文的意义 如果给已经存在的类添加新的行为,采用继承方案 如果改变已经存在类的行为,采用重写方案 2图解继承.重写与Super 注:上面代码层层关联.super()可以用到任何方法里进行调用,本文只 ...
- springboot集成mybatis(一)
MyBatis简介 MyBatis本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation迁移到了google code,并且改名为MyB ...
- winform中的数据绑定
1. 简单的数据绑定 例1 using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[& ...
- Hibernate注解开发详解
*****************关于注解的简单介绍 详细介绍请点击这里注解详细教程 package com.tomowork.pojo; import org.hibernate.annotatio ...
- pdf如何转换为word文档
我们经常会遇到需要将PDF转换为WORD文档,对于我来讲,有些PDF没有目录,看起来非常不方便,于是就特别想转成WORD,然后增加目录,想看某一节内容时,快速查找. 这里我总结了一些方法,后续也会不断 ...