geotrellis使用(二十)geotrellis1.0版本新功能及变化介绍

2018-04-28 17:07:47 浏览数 (1)

目录

  1. 前言
  2. 变化情况介绍
  3. 总结

一、前言

       之前版本是0.9或者0.10.1、0.10.2,最近发现更新成为1.0.0-2077839。1.0应该也能称之为正式版了吧。发现其中有很多变化,在这里为大家简单介绍。

二、变化情况介绍

2.1 数据导入变化

       之前数据导入参数基本都要写在命令行,刚查看之前写的博客发现没有介绍数据导入的,只有一个老版的调用本地数据的,本文就在这里简单介绍Geotrellis的数据导入。

       Geotrellis可以将数据(Tiff)从本地、HDFS、S3中导入到本地、HDFS、Accumulo、HBASE、CASSANDRA、S3等,可选方式很多,而且是通过Spark集群并行处理,其实相当于Geotrellis已经实现了分布式的瓦片切割。老版的命令如下:

代码语言:javascript复制
spark-submit --class geotrellis.Ingest --driver-memory=2G jarpath
--input hadoop --format geotiff --cache NONE -I path=filepath 
--output accumulo -O instance=accumuloinstance table=tablename user=username 
password=password zookeeper=zookeeper --layer layername --crs EPSG:3857 --layoutScheme floating

       其中geotrellis.Ingest是一个调用Geotrellis内部数据导入的类,就是调用了ETL类进行数据自动上传。代码如下:

代码语言:javascript复制
implicit val sc = SparkUtils.createSparkContext("Ingest", new SparkConf(true))
Etl.ingest[ProjectedExtent, SpatialKey, Tile](args, ZCurveKeyIndexMethod)
sc.stop()

       如果是多波段数据将Tile换成MultibandTile即可。接着说上面的脚本,input表示数据输入方式,如果是本地和HDFS就写hadoop,如果是S3就写s3。format是数据类型,单波段tiff为geotiff,多波段tiff为multiband-geotiff。path为数据存放路径。output指定输出存放位置。后面是该位置的一些配置。具体非常复杂,可以参考https://github.com/pomadchin/geotrellis/blob/master/docs/spark-etl/spark-etl-intro.md。

       上面的数据导入配置看上去是不是很乱,并且完全没有组织,1.0版进行了很大的改进,将配置信息基本都写在了json文件里。1.0版数据导入命令如下:

代码语言:javascript复制
spark-submit 
  --class geotrellis.dataimport.DataIngest --driver-memory=2G $JAR 
  --input "file:///input.json" 
  --output "file://output.json" 
  --backend-profiles "file://backend-profiles.json"

       看上去是不是很清爽,将配置信息写在了三个文件里,下面逐一介绍这三个文件。

       input表示输入信息的配置,其json文件如下:

代码语言:javascript复制
[
  {
    "name": "landsat",
    "format": "geotiff",
    "backend": {
      "type": "hadoop",
      "path": "file:///datapath/"
    },
    "cache": "NONE"
  }
]

       这是一个json数组可以写多个。name相当于旧版的layername,format不变,type相当于旧版的input,path不变。

       output表示输出信息的配置,其json文件如下:

代码语言:javascript复制
{
  "backend": {
    "type": "accumulo",
    "path": "through",
    "profile": "accumulo-201"
  },
  "reprojectMethod": "buffered",
  "cellSize": {
    "width": 256.0,
    "height": 256.0
  },
  "tileSize": 256,
  "pyramid": true,
  "resampleMethod": "nearest-neighbor",
  "keyIndexMethod": {
    "type": "zorder"
  },
  "layoutScheme": "zoomed",
  "cellType":"int8",
  "crs": "EPSG:3857"
}

       大部分意思与旧版相同,主要是backend中的信息,type相当于旧版的output,path相当于table,profile表示accumulo或其他输出方式的配置,具体写在backend-profiles.json文件中。

       backend-profiles中存放数据库等配置信息,其json文件如下:

代码语言:javascript复制
{
  "backend-profiles": [
    {
      "name": "accumulo-201",
      "type": "accumulo",
      "zookeepers": "zookeeper",
      "instance": "accumulo-instance",
      "user": "username",
      "password": "password"
    },
    {
      "name": "cassandra-local",
      "type": "cassandra",
      "allowRemoteDCsForLocalConsistencyLevel": false,
      "localDc": "datacenter1",
      "usedHostsPerRemoteDc": 0,
      "hosts": "localhost",
      "replicationStrategy": "SimpleStrategy",
      "replicationFactor": 1,
      "user": "",
      "password": ""
    }
  ]
}

       backend-profiles节点下可以存放多个数据库配置信息,其中name就是output.json文件中的backend.profile。

2.2 性能提升

       1.0版本明显做了很多优化,代码也变的更整洁清晰,带来的结果是性能明显提升。比如数据导入之前导入数据比较费时,且经常失败,1.0版更加稳定,并且速度明显提升。数据读取以及处理的速度也有所提升,我的系统中原来需要90ms处理的数据,现在可能只需要60ms左右,原来需要600ms处理的现在也只需要300ms左右。其实下面要讲的更是一个性能方面的提升。

2.3 LayerReader读取整层数据的变化

       比如我们希望能够实现用户选择任意区域数据(以SRTM为例)并能够自动拼接、下载该区域的SRTM数据,首先我们需要将全球的SRTM数据导入Geotrellis中,然后当有用户请求的时候读出SRTM的数据,进行拼接等操作。旧版的时候我们就需要将整层数据读出,然后根据用户输入的范围调用mask方法进行掩码操作。而新版大大改进了这一点,我们可以直接取出用户输入范围内的数据。下面我为大家介绍使用LayerReader读取整层数据的三种实现方式。

也有可能是旧版就有直接取出用户输入范围内的数据的方法我没有发现,在这里不做深究,将三种方式都简单介绍,仅供参考。

       第一种方式直接读取整层数据。代码如下:

代码语言:javascript复制
reader.read[SpatialKey, Tile, TileLayerMetadata[SpatialKey]](layerId)

       其中reader是FilteringLayerReader[LayerId]对象,下同,从名字就能看出应该是1.0版新加的带有过滤的层读取类(旧版为AccumuloLayerReader类),layerId为读取的层的信息,下同。适用该方式就会将该layerId的整层数据读出。

       第二种方式为read方法添加一个LayerQuery对象。实现代码如下:

代码语言:javascript复制
reader.read[SpatialKey, Tile, TileLayerMetadata[SpatialKey]](layerId, new LayerQuery[SpatialKey, TileLayerMetadata[SpatialKey]].where(Intersects(polygon)))

       其实就是用where语句加了一个过滤条件,Intersects(polygon)表示条件是与polygon相交,polygon是用户选择的范围,并且需要跟原始数据采用同一投影,此处有个小bug,就是仅支持MultiPolygon,如果是Polygon对象需要使用MultiPolygon(polygon)进行简单封装,下同。这样就能实现只读取该层中的与polygon相交的数据。

       第三种方式就是第二种方式的语法糖,写起来更加简单方法。代码如下:

代码语言:javascript复制
reader.query[SpatialKey, Tile, TileLayerMetadata[SpatialKey]](layerId).where(Intersects(polygon)).result

       以上就是实现整层数据读取的三种方式,如果需要处理的上述业务需求,最好采用后两种方式,进行实际测试,效率提高10倍左右。但是后两种方式有个小bug:如果polygon与层中的数据相交的瓦片(源数据在Accumulo等数据库中存放的方式是256*256的瓦片)是较小的区域,可能该瓦片不会被取出,即会被过滤掉,Geotrellis毕竟是一个新的框架,我们应该包容其中的BUG,寻找合适的方式绕过BUG实现我们的需求。

三、总结

       本文简单介绍了1.0版Geotrellis中的变化,不难看出Geotrellis正在快速的向前推进,我相信假以时日,一定会变的更加完善、更加好用,我对Geotrellis的未来充满信心。

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

0 人点赞