geotrellis使用(十七)使用缓冲区分析的方式解决单瓦片计算边缘值问题

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

目录

  1. 前言
  2. 需求分析
  3. 实现方案
  4. 总结

一、前言

       最近真的是日理千机,但是再忙也要抽出时间进行总结。上一篇文章讲了使用缓冲区分析的方式解决投影变换中边缘数据值计算的问题(见geotrellis使用(十六)使用缓冲区分析的方式解决投影变换中边缘数据值计算的问题)。实际中往往还有一种需求就是对单个瓦片进行操作,比如求坡度等,如果这时候直接计算,同样会出现边缘值计算的问题,这种情况也可以使用上一篇文章中讲到的方法进行处理。

二、需求分析

       假如我们想在前台地图中实时显示坡度图像,有两种方式:第一种是在DEM数据导入Accumulo之前先求坡度(可以使用传统的GDAl、也可以使用Geotrellis),然后再导入;第二种是直接将DEM数据导入Accumulo中,后台实时计算每幅瓦片的坡度,然后渲染到前台。两种方式各有各的好处,采用第一种方式,整体处理简单,显示的速度快,但是Accumulo中要存两份数据。第二种方式,实现起来稍显复杂,显示的速度稍慢,但是Accumulo中只存了一份数据。由于Geotrellis基于Spark集群,所以如果集群足够优秀,处理速度不是很重要的问题,但是如果我们需要对同一个数据进行多种操作,或者根据用户的需求来进行操作,那么就没有办法完成数据的预处理工作,只能进行实时计算,如果计算只针对瓦片中的单一像素则还不涉及到边缘值的问题,而如果需要进行插值采样等操作(如求坡度、山影等),这时候就会出现上文中讲到的瓦片边缘值计算的问题。本文就为大家讲解如何使用缓冲区分析的方式解决单瓦片计算边缘值问题。

三、实现方案

       至于求坡度等的具体算法不在这里介绍,都是很成熟的算法,并且Geotrellis中也已经实现了一些算法,只需调用相应的函数即可。有关缓冲区分析等也在之前的文章介绍过多次,不在这里赘述。

1.数据读取

       此处读的数据为没有进行过任何处理的原始DEM数据,具体读写数据也在之前的文章中介绍过,详情见geotrellis使用(三)geotrellis数据处理过程分析等文章。

       但是此处不同的是我们为了完成边缘值计算,就需要将单幅瓦片周围的八幅瓦片同时读入,即需要读9幅瓦片,这个我们只需要根据当前瓦片的key值算出周围瓦片key值,然后逐一读取即可。获取9幅瓦片key值的代码如下:

代码语言:javascript
复制
val keys = 
for (i <- -1 to 1; j <- -1 to 1)
    yield {
        val col = key.col + i
        val row = key.row + j
        SpatialKey(col, row)
      }

       逐一读取瓦片代码如下:

代码语言:javascript
复制
val tiles =
keys.map { k =>
  tileReader.reader[SpatialKey, Tile](layerId).read(k)
}

       其中tileReader为AccumuloValueReader实例,layerId包含当前数据存放layer以及层级zoom。

       将9幅瓦片拼接成1幅瓦片代码如下:

代码语言:javascript
复制
val pairs = keys zip tiles
val pieces = pairs.map { case (key, tile) => tile ->((key.col - mincol) * 256, (key.row - minrow) * 256) }
implicitly[Stitcher[Tile]].stitch(pieces, 256 * 3, 256 * 3)

       其中mincol为keys中的col最小值,minrow为keys中的row最小值,循环遍历keys即可求出。这样就实现了将9幅瓦片拼成1幅,完成数据读取工作。

2.瓦片处理

       上一步得到了拼接好的“大瓦片”,这里在Geotrellis中与之前的“小瓦片”一样的都是Tile实例,采用与之前数据处理相同的处理方式即可,唯一需要注意的是瓦片不在是256*256,而变成了原来的3倍。处理完之后原来边缘值计算有问题的地方,这样就被巧妙的避开了。

3.裁剪结果

       数据处理完之后下一步要做的就是将瓦片重新裁剪成256*256。实现代码如下:

代码语言:javascript
复制
val startcol = tile.cols / 2 - 256 / 2
val startrow = tile.rows / 2 - 256 / 2
val endcol = tile.cols / 2 + 256 / 2 - 1
val endrow = tile.rows / 2 + 256 / 2 - 1
tile.crop(startcol, startrow, endcol, endrow)

       因为要从“大瓦片”的中间取出256*256像素,所以需要按照上面的公式求出开始以及结束的像素偏移。这样就得到了边缘值没有问题的瓦片。

四、总结

       以上就是通过使用缓冲区分析的方式解决单瓦片计算边缘值问题。有些地方还可以优化,比如取的时候不要取9幅瓦片,只取比当前瓦片稍微向外扩展几个像素值等,具体由读者自行思考。