geotrellis使用(十二)再记录一次惨痛的伪BUG调试经历(数据导入以及读取瓦片)
Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html
目录
一、前言
最近做一项实验,简单的说就是读取已经存入Accumulo中的瓦片,然后对瓦片进行简单的Map操作然后RenderPng生成瓦片,前台显示。看上去是个很简单的操作,但是中间一直存在一个问题,就是明明数据值范围在[0-10] (除了某些地方无值),但是处理完后某些地方会出现数值严重偏差的情况,在100以上(处理逻辑也不应该出现这么大的值),具体效果就是瓦片中某些地方是空白的(因为用了ColorMap,超过10的没有定义,所以是空白的),百思不得其解,辗转反侧,最后终于顿悟,遂记录之。
二、BUG还原
首先准备一个8位有符号类型的tiff,然后使用ingest导入Accumulo,然后读取tile并进行简单的逻辑处理,然后渲染发送到前台显示,这时候你就可以看到很多诡异的事情。
三、查找BUG
此BUG查找过程颇为闹心,前前后后折腾了好几天,猜测并实验了各种原因,最后才发现真正的问题。
3.1 怀疑处理逻辑
因为我的处理为11-value,因为原始范围是[0, 10],所以此处相当于将数值反了个个,这个地方会有什么问题呢,怎么结果会大于100多呢,通过各种调试生成tiff等,发现原始数据有-100多的,所以此处就变成了正的100多,那么就先判断value是否小于0,大于0的才处理,成功解决问题,显示OK。但是真的解决问题了吗?(当然没解决,解决了就不会有这篇文章了,哈哈)为什么会出现值为负的情况呢,我原始数据范围可是[0, 10]啊?
3.2 怀疑Byte类型
然后以为是Byte类型造成的,在Scala中,Byte的范围是[-128, 127],而在C#等有些语言中,Byte的范围是[0, 255]。在Geotrellis中ByteArrayTile对应基础数据类型为Byte,UByteArrayTile对应基础数据类型为UByte,二者同时对应tiff中的Byte类型,ByteArrayTile类型生成的tiff多了一项PIXELTYPE=SIGNEDBYTE。所以刚开始一直以为是数据类型的问题,想当然的认为tiff文件所支持的Byte类型的范围也是[0, 255],其实这时候根本没有发现问题的本质,并且也没有对tiff进行认真研究,认为使用UByteArrayTile就能解决问题,但是考虑到万一将来数据有负值的情况怎么办呢?所以又苦思冥想半天没有结果,折腾了半天BUG也没有解决。
3.3 真相浮出水面
将从Accumulo读出来的数据直接生成tiff,会发现一个很诡异的问题,NODATA这一项居然没有了,我原来可是正儿八经写的-128,在又咨询了圈内人士之后大概明白了为什么会出BUG。
因为在瓦片切割的过程中会进行重采样,这样肯定是读的数据不包含NODATA值,所以在进行重采样的时候有些点自然就变成了负值,因为0到10之间的数与-128作用自然就是负的(比如内插法的线性)。
但是问题又来了,为什么切瓦片之前读TIFF的时候没有读入TIFF的NODATA呢,之前为了解决切瓦片采样方式的问题,重写了ETL类,但是大部分地方都一样,只有在投影和建立金字塔的时候添加了其他采样方法,所以刚好可以在这里进行打印调试,一试果然与猜测一致,输出cellType,全是int8raw,表示读入的是没有NODATA值的Byte类型,怎么会这样,明明原始数据是有NODATA值的,这时候看到输入参数,可以指定cellType,于是数据导入的时候加了一项参数--cellType int8,测试,发现问题解决,导入的时候打印信息全部正常。
幸福来的太突然,让人不知所措,难道问题就这么解决了(了解国产电视剧的观众都知道:没有,哈哈)。这时候再看从Accumulo中读出来的Tile,发现数据类型居然变成了int8ud0,这是什么鬼,查了一下源码发现是byte类型的用户自定义NODATA,并且NODATA值为0的这么一种类型。
为什么会出现这么一种类型,只能再看读取瓦片的源代码,主要代码如下:
def read(key: K): V = {
val scanner = instance.connector.createScanner(header.tileTable, new Authorizations())
scanner.setRange(new ARange(rowId(keyIndex.toIndex(key))))
scanner.fetchColumnFamily(columnFamily(layerId))
val tiles = scanner.iterator
.map { entry =>
AvroEncoder.fromBinary(writerSchema, entry.getValue.get)(codec)
}
.flatMap { pairs: Vector[(K, V)] =>
pairs.filter(pair => pair._1 == key)
}
.toVector
if (tiles.isEmpty) {
throw new TileNotFoundError(key, layerId)
} else if (tiles.size > 1) {
throw new LayerIOError(s"Multiple tiles found for $key for layer $layerId")
} else {
tiles.head._2
}
}
其实上述代码最关键的就是AvroEncoder.fromBinary(writerSchema, entry.getValue.get)(codec),意思就是将二进制数据读成Tile,没看出有什么问题,好吧,请教原作者,只告诉我采用新版本可以,于是我更新新版本Geotrellis,发现这块读取确实好了,但是悲剧的是前面的采样造成的负值的问题又出来了。
又折腾了数次,问题还是没有解决,想到刚开始在数据导入的时候为了实现Tiff边界拼接的问题,路径输入的是文件夹,这样相当于同时导入一个文件夹下的所有Tiff,现在是不是这个地方变了呢。一试果然如此,导入单个Tiff,采样没有问题,同时导入一个文件夹则会出问题。那么这显然又是Geotrellis的一个BUG。
四、解决方案
解决方案就三点:
- 导入数据的时候添加
--cellType int8即添加指定的类型,可以解决导入的时候无数据值的问题,并能够解决瓦片切割重采样时候造成的无效值。至于为何需要添加此配置项,为什么Geotrellis不能自动读出Tiff的NODATA值还需下一步进一步研究。 - 针对不能再导入文件夹下所有Tiff的问题,有又三种解决方案。第一,如果不需要考虑重采样负值带来的影响可以继续使用文件夹作为输入;第二,可以事先将Tiff拼接起来,当然Tiff不能太大;第三,不考虑Tiff边界处缝隙带来的影响。貌似三种都不是最好的解决方案,下一步要继续研究数据导入这块的源代码,看看有没有办法从根本上解决。
- 从Accumulo读取瓦片cellType的问题在升级到0.10.1后自动解决。
五、总结
本次BUG调试,历经数天,折腾无数次,总结出以下几点:
- 对采样、切瓦片等基础地理信息知识掌握的不够全面。
- 研读源码不够细致。
- 不要完全相信Geotrellis,其也是有不完善以及BUG的————尽信书则不如无书。
- 发现问题远比解决问题重要。
- 做事情要执着。
六、后记
恰逢今日高中群里原先语文老师孩子参加高考,咨询志愿情况,有人建议先选城市、有人建议先选学校、有人建议学计算机、有人建议学会计。。。众说纷纭,各种出世入世。
我说我建议学哲学,其实我觉得其他任何专业都只是工具,只有思想上去了,你干任何事情都能做好。当然有人要说,很多人没学哲学思想也很有深度,做事也很成功。其实还是那句话,一个人一辈子做好一件事情足以,将一件事情能做到至善至美,那么你的思想自然而然的也就上去了,这时候你再去做任何事情,岂有不成的道理。但是一般人深受花花世界的吸引,不能耐得住这份寂寞去做一件事,唯有哲学,能教你从方法论等角度去思考世界、探索世界,自然你的思想也就慢慢得到升华。
对待写程序同样如此,只有拥有一颗执着的心,遇到问题能够刨根问底,你的思想自然也就上去了,对待任何问题你都将如履平地。如果只是一味的为写程序而写程序,那么你终究是一个代码的搬运工。
思想高于一切!
geotrellis使用(十二)再记录一次惨痛的伪BUG调试经历(数据导入以及读取瓦片)的更多相关文章
- geotrellis使用(七)记录一次惨痛的bug调试经历以及求DEM坡度实践
眼看就要端午节了,屌丝还在写代码,话说过节也不给轻松,折腾了一天终于解决了一个BUG,并完成了老板安排的求DEM坡度的任务,那么就分两段来表. 一.BUG调试 首先记录一天的BUG调试,简单copy了 ...
- 使用Typescript重构axios(十二)——增加参数
0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...
- 使用Typescript重构axios(二十二)——请求取消功能:收尾
0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...
- 使用Typescript重构axios(三十二)——写在最后面(总结)
0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...
- 还需要学习的十二种CSS选择器
在前面的文章中,我们在介绍了<五种你必须彻底了解的CSS选择器>,现在向大家介绍,还需要学习的另外十二种CSS选择器.如果你还没有用过,就好好学习一下,如果你已经熟知了就当是温习. 一.X ...
- geotrellis使用(二十五)将Geotrellis移植到spark2.0
目录 前言 升级spark到2.0 将geotrellis最新版部署到spark2.0(CDH) 总结 一.前言 事情总是变化这么快,前面刚写了一篇博客介绍如何将geotrellis移植 ...
- geotrellis使用(二十二)实时获取点状目标对应的栅格数据值
目录 前言 实现方法 总结 一.前言 其实这个功能之前已经实现,今天将其采用1.0版的方式进行了重构与完善,现将该内容进行总结. 其实这个功能很常见,比如google地球上 ...
- geotrellis使用(二十八)栅格数据色彩渲染(多波段真彩色)
目录 前言 实现过程 总结 一.前言 上一篇文章介绍了如何使用Geotrellis渲染单波段的栅格数据,已然很是头疼,这几天不懈努力之后工作又进了一步,整清楚了如何使用Geotrelli ...
- geotrellis使用(二十六)实现海量空间数据的搜索处理查看
目录 前言 前台实现 后台实现 总结 一.前言 看到这个题目有人肯定会说这有什么可写的,最简单的我只要用文件系统一个个查找.打开就可以实现,再高级一点我可以提取出所有数据的元数据,做个元 ...
随机推荐
- 安卓手机USB网络共享,电脑卡顿、反应慢
1.首先需要把手机连接到电脑,在手机上打开USB网络共享. 2.打开设备管理器 3.在网络适配器中,找到Remote NDIS based Internet Sharing Device,右键更新驱动 ...
- matlab环境配置
一.环境变量设置 AMD处理器:右键单击我的电脑 属性 — >高级 —> 环境变量 —> 系统变量 —> 新建 变量名:BLAS_VERSION,值为安装目录\atlas_At ...
- ntko office在线编辑控件问题记录
ntko office在线预览插件 http://www.ntko.com/ 问题:火狐或谷歌下保存报[没有打开的文档]错误,ie正常 原因:火狐.谷歌.ie的各方法执行文字不同,ie嵌在页面,而火狐 ...
- input jquery 操作
本文章主要为了总结开发常用的input等常见html的jquery操作,不是为了展示自己多么菜,只为了积累知识,勿喷!!!不断更新中 $(function () { $("input[nam ...
- 再探.NET的PE文件结构(安全篇)
一.开篇 首先写在前面,这篇文章源于个人的研究和探索,由于.NET有自己的反射机制,可以清楚的将源码反射出来,这样你的软件就很容易被破解,当然这篇文章不会说怎么样保护你的软件不被破解,相反是借用一个软 ...
- Go语言实战 - 创业进行时之创业伊始
在工作了10年之后,我于32岁的年纪在两个月前辞职创业了. 简单介绍一下之前的整个职业生涯,挺典型的,工程师 –> 资深工程师 –> 架构师 –> 项目经理 –> 部门经理,可 ...
- TODO:Node.js pm2使用方法
TODO:Node.js pm2使用方法 pm2 是一个带有负载均衡功能的Node应用的进程管理器. 当你要把你的独立代码利用全部的服务器上的所有CPU,并保证进程永远都活着,0秒的重载, PM2是完 ...
- Jmeter 使用Jmeter与Badboy进行压力测试
1. 介绍 Badboy是一个录制请求的工具,这里用它来生成文件给JMeter用. JMeter是一个用java写的开源的性能测试工具,用于模拟在服务器.网络或者其他对象上附加高负载以测试他们提供服务 ...
- 使用sklearn做单机特征工程
目录 1 特征工程是什么?2 数据预处理 2.1 无量纲化 2.1.1 标准化 2.1.2 区间缩放法 2.1.3 标准化与归一化的区别 2.2 对定量特征二值化 2.3 对定性特征哑编码 2.4 缺 ...
- Java 对象引用方式 —— 强引用、软引用、弱引用和虚引用
Java中负责内存回收的是JVM.通过JVM回收内存,我们不需要像使用C语音开发那样操心内存的使用,但是正因为不用操心内存的时候,也会导致在内存回收方面存在不够灵活的问题.为了解决内存操作不灵活的问题 ...