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

目录

  1. 前言
  2. BUG还原
  3. 查找BUG
  4. 解决方案
  5. 总结
  6. 后记

一、前言

       最近做一项实验,简单的说就是读取已经存入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。

四、解决方案

       解决方案就三点:

  1. 导入数据的时候添加--cellType int8即添加指定的类型,可以解决导入的时候无数据值的问题,并能够解决瓦片切割重采样时候造成的无效值。至于为何需要添加此配置项,为什么Geotrellis不能自动读出Tiff的NODATA值还需下一步进一步研究。
  2. 针对不能再导入文件夹下所有Tiff的问题,有又三种解决方案。第一,如果不需要考虑重采样负值带来的影响可以继续使用文件夹作为输入;第二,可以事先将Tiff拼接起来,当然Tiff不能太大;第三,不考虑Tiff边界处缝隙带来的影响。貌似三种都不是最好的解决方案,下一步要继续研究数据导入这块的源代码,看看有没有办法从根本上解决。
  3. 从Accumulo读取瓦片cellType的问题在升级到0.10.1后自动解决。

五、总结

       本次BUG调试,历经数天,折腾无数次,总结出以下几点:

  1. 对采样、切瓦片等基础地理信息知识掌握的不够全面。
  2. 研读源码不够细致。
  3. 不要完全相信Geotrellis,其也是有不完善以及BUG的————尽信书则不如无书。
  4. 发现问题远比解决问题重要。
  5. 做事情要执着。

六、后记

       恰逢今日高中群里原先语文老师孩子参加高考,咨询志愿情况,有人建议先选城市、有人建议先选学校、有人建议学计算机、有人建议学会计。。。众说纷纭,各种出世入世。

       我说我建议学哲学,其实我觉得其他任何专业都只是工具,只有思想上去了,你干任何事情都能做好。当然有人要说,很多人没学哲学思想也很有深度,做事也很成功。其实还是那句话,一个人一辈子做好一件事情足以,将一件事情能做到至善至美,那么你的思想自然而然的也就上去了,这时候你再去做任何事情,岂有不成的道理。但是一般人深受花花世界的吸引,不能耐得住这份寂寞去做一件事,唯有哲学,能教你从方法论等角度去思考世界、探索世界,自然你的思想也就慢慢得到升华。

       对待写程序同样如此,只有拥有一颗执着的心,遇到问题能够刨根问底,你的思想自然也就上去了,对待任何问题你都将如履平地。如果只是一味的为写程序而写程序,那么你终究是一个代码的搬运工。

       思想高于一切!

geotrellis使用(十二)再记录一次惨痛的伪BUG调试经历(数据导入以及读取瓦片)的更多相关文章

  1. geotrellis使用(七)记录一次惨痛的bug调试经历以及求DEM坡度实践

    眼看就要端午节了,屌丝还在写代码,话说过节也不给轻松,折腾了一天终于解决了一个BUG,并完成了老板安排的求DEM坡度的任务,那么就分两段来表. 一.BUG调试 首先记录一天的BUG调试,简单copy了 ...

  2. 使用Typescript重构axios(十二)——增加参数

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

  3. 使用Typescript重构axios(二十二)——请求取消功能:收尾

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

  4. 使用Typescript重构axios(三十二)——写在最后面(总结)

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

  5. 还需要学习的十二种CSS选择器

    在前面的文章中,我们在介绍了<五种你必须彻底了解的CSS选择器>,现在向大家介绍,还需要学习的另外十二种CSS选择器.如果你还没有用过,就好好学习一下,如果你已经熟知了就当是温习. 一.X ...

  6. geotrellis使用(二十五)将Geotrellis移植到spark2.0

    目录 前言 升级spark到2.0 将geotrellis最新版部署到spark2.0(CDH) 总结 一.前言        事情总是变化这么快,前面刚写了一篇博客介绍如何将geotrellis移植 ...

  7. geotrellis使用(二十二)实时获取点状目标对应的栅格数据值

    目录 前言 实现方法 总结 一.前言        其实这个功能之前已经实现,今天将其采用1.0版的方式进行了重构与完善,现将该内容进行总结.        其实这个功能很常见,比如google地球上 ...

  8. geotrellis使用(二十八)栅格数据色彩渲染(多波段真彩色)

    目录 前言 实现过程 总结 一.前言        上一篇文章介绍了如何使用Geotrellis渲染单波段的栅格数据,已然很是头疼,这几天不懈努力之后工作又进了一步,整清楚了如何使用Geotrelli ...

  9. geotrellis使用(二十六)实现海量空间数据的搜索处理查看

    目录 前言 前台实现 后台实现 总结 一.前言        看到这个题目有人肯定会说这有什么可写的,最简单的我只要用文件系统一个个查找.打开就可以实现,再高级一点我可以提取出所有数据的元数据,做个元 ...

随机推荐

  1. java使用websocket,并且获取HttpSession,源码分析

    转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...

  2. 辛巴学院-Unity-剑英陪你零基础学c#系列(二)顺序

    这不是草稿 辛巴学院:正大光明的不务正业.   上一次的教程写出来之后,反馈还是挺多的,有很多都做了修改,也有一些让人崩溃,不得不说上几句.有些人有些很奇怪的地方,你写篇东西,被看了以后不说他感觉怎么 ...

  3. 模仿JavaAppArguments.java示例,编写一个程序,此程序从命令行接收多个数 字,求和之后输出结果,写出其的设计思想、程序流程图、源程序代码。

    一 设计思想 首先现在file中建立一个类,并把任务名和类名写上(注意类名的大写):第二步则是参数的输入,并且定义求和变量:第三步则是对参数数据类型的要求,要把字符类型转化为整数类型并输出(这也是本道 ...

  4. linux环境下配置java WEB项目运行环境,jdk8+tomcat8+mysql5.7.11 新手向

    一:安装jdk 1.下载jdk  在oracle下载东西的时候因为oracle的一些验证机制,所以需要在链接前面添加一些参数 wget --no-check-certificate --no-cook ...

  5. T 泛型转换

    T为左值 result = (T)Convert.ChangeType(o,typeof(T));

  6. Javascript基础系列之(七)函数(定义和调运函数)

    函数是一个可以随时运行的语句,简单说,函数是完成某个功能的一组语句,它接受0或者多个参数. 函数的基本语法如下 function functionName([arg0,arg1,......argN] ...

  7. Java Performance - 优化和分析Garbage Collection/垃圾收集

    随着硬件的不断提升,Java Heap 越来越大,合理的垃圾收集调优变得愈发重要.下面介绍一些最佳实践: 注意: 下面不涉及 IBM AIX Java. 同时不介绍原理,仅仅是建议以及初始配置/最佳实 ...

  8. Behavioral模式之Observer模式

    1.意图 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,全部依赖于它的对象都得到通知并被自己主动更新. 2.别名 依赖(dependents).公布-订阅(Publish-Subscr ...

  9. RTX任务管理

        默认情况下用户创建的任务栈大小是由参数Task stack size决定的.     如果觉得每个任务都分配同样大小的栈空间不方便的话,可以采用自定义任务栈的方式创建任务.采用自定义方式更灵活 ...

  10. Codeforces 915F Imbalance Value of a Tree

    Imbalance Value of a Tree 感觉这种题没啥营养, 排个序算算贡献就好啦. #include<bits/stdc++.h> #define LL long long ...