GeoTrellis 整体介绍

GeoTrellis

介绍

GeoTrellis是一个基于Apache spark 的用于处理栅格数据的scala库和框架

  • 可以高效的读/写和操作栅格,实现了地图运算和矢栅转换工具
  • 可以将栅格数据渲染成PNG图片,元数据转换成JSON

GeoTrellis解决了三个核心的问题

  • 创建可扩展的,高性能的地理信息处理WEB服务
  • 创建分布式的地理信息处理服务,用来处理海量数据集
  • 完成并行化地理信息处理操作,以利用多核架构的优势

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

GeoTrellis是针对大数据量栅格数据进行分布式空间计算的框架,所以无论采取何种操作,都是先将大块的数据切割成一定大小的小数据(瓦片),这是分治的思想,也是分布式计算的精髓。GeoTrellis的第一步就是要将数据切片(无论是存储在内存还是持久化),然而即使能力再大,在实际工作中也难以处理以下几种需求:

  1. 全球(大范围) 高分辨率遥感影像数据,数据量在TB级

  2. 局部地区数据更新

  3. 不同时间数据融合

    可行的方案是执行更新操作或者分批处理,GeoTrellis框架中提供了数据的ETL接口,但是只能进行write操作,不能进行update操作,write操作会覆盖此图层中已有数据,边处数据无法凭借,导致数据缺失,所以只能分批处理写到不同的图层。

概念

Catalog

DataSource

TileSetRasterLayer

AccumuloAttributeStore / AccumuloValueReader // 读取Accumulo 瓦片数据

RasterExtent

GeoTiffReader

SinglebandGeoTiff // 读取单波段

MultibandGeoTiff // 读取多波段

LayerReader // 读取集群中整层的瓦片信息

GeoTiff

SpatialKey //每幅瓦片在Accumulo中对应的瓦片Key值,可以通过Key值获取到对应的瓦片

​ // tileReader.readerSpatialKey, Tile.read(k)

SparkUtils

TileLayerMetadata

HadoopGeoTiffRDD //读取Tiff文件类

Reproject : 重投影

数据输入

1. 栅格

直接导入raster数据,通过ETL类生成金字塔,保存到Accumulo

单波段Tiff数据导入:

implicit val sc = SparkUtils.createSparkContext("ETL SinglebandIngest", new SparkConf(true)) Etl.ingest[ProjectedExtent, SpatialKey, Tile](args, ZCurveKeyIndexMethod) sc.stop() 

多波段Tiff数据导入:

implicit val sc = SparkUtils.createSparkContext("ETL MultibandIngest", new SparkConf(true)) Etl.ingest[ProjectedExtent, SpatialKey, MultibandTile](args, ZCurveKeyIndexMethod) sc.stop() 

GeoTrellis读取Tiff文件

HadoopGeoTiffRDD

2. 矢量

读取矢量文件 -> 矢量栅格化 -> 走栅格流程

  1. ShapeFileReader / ShapefileDataStore
geotrellis.shapefile.ShapeFileReader.readSimpleFeatures(path)
  1. Geometry数据栅格化

得到 RasterExtent

Rasterizer.rasterizeWithValue(features, re, 100) 
Rasterizer.foreachCellByGeometry(feature.geometry, re)

数据读取

参考: https://www.cnblogs.com/shoufengwei/p/5422844.html

在application.conf 中配置一个catolog.json文件,其中记录DataSource信息,通过此信息获取数据

--layoutScheme : tms/floating

​ floating切瓦片的时候只有0层 , 相当于用floating处理的就是原始数据只将数据切割成256*256的块,层为0(具体x、y编号不需要操心,geotrellis会自动计算)

​ tms会建立金字塔 ,用tms会将数据从最大层(此最大层根据数据的分辨率计算得出)切到第一层,调用的时候直接根据层进行调用

--pyramid : 加上此参数在 layoutScheme = tms的时候会建立金字塔

-I path=file:/.. : 果此处的路径为文件,则单独导入此文件,如果为文件夹,则一次将整个路径导入,并且会自动拼接,瓦片不会有缝隙。geotrellis不但能够分布式瓦片切割,还能自动拼接 。

--layer : 此参数用于区分不同的数据,取数据的时候根据此项区分不同的数据

参考: https://www.cnblogs.com/shoufengwei/p/5468068.html

LayerReader : 读取整层瓦片信息,然后根据偏移得到点值

  val r = reader.read[SpatialKey, Tile, TileLayerMetadata[SpatialKey]](layerId)
    val mapTransform = r.metadata.mapTransform
    val key = r.metadata.mapTransform(point)
    val dataValues: Seq[Double] = r.asRasters().lookup(key).map(_.getDoubleValueAtPoint(point))
    val value = if(dataValues == null || dataValues.length <= 0) 0 else dataValues.head

ValueReader : 首先找到对应瓦片,然后偏移得到此点

val key = attributeStore.readMetadata[TileLayerMetadata[SpatialKey]](layerId).mapTransform(point)
    val (col, row) = attributeStore.readMetadata[TileLayerMetadata[SpatialKey]](layerId).toRasterExtent().mapToGrid(point)
    
    //读取瓦片
    val tile: Tile =  tileReader.reader[SpatialKey, Tile](layerId).read(key)
    
    val tileCol = col - key.col * tile.cols
    val tileRow = row - key.row * tile.rows
    println(s"tileCol=${tileCol}   tileRow = ${tileRow}")
    tile.get(tileCol, tileRow)

多波段瓦片读取

val multiTile = tileReader.reader[SpatialKey, MultibandTile](LayerId(name, zoom)).read(key) 

从多波段中获取单个波段

MultibandTile  
multiTile.bands(bandNum) 
  1. 直接读取整层数据

    reader.read[SpatialKey, Tile, TileLayerMetadata[SpatialKey]](layerId) 
    
  2. 为read方法添加一个LayerQuery对象

    reader.read[SpatialKey, Tile, TileLayerMetadata[SpatialKey]](layerId, new LayerQuery[SpatialKey, TileLayerMetadata[SpatialKey]].where(Intersects(polygon))) 
    
  3. 第二种方式的语法汤

    reader.query[SpatialKey, Tile, TileLayerMetadata[SpatialKey]](layerId).where(Intersects(polygon)).result 
    

数据输出

HBase

Accumulo

数据发布

参考: https://www.cnblogs.com/shoufengwei/p/5422844.html

IO(Http) ! Http.Bind(service, host, port)

需要使用以下语句系统遍自动的在host和相应的port上发起服务。

具体路由信息需要在service类中定义。service类需要继承Actor方法,并覆盖父类的receive方法。

ETL工具

参考:https://www.cnblogs.com/shoufengwei/p/5856323.html

geotrellis.spart.etl //处理ETL数据处理

ETL工作就是将数据切割成瓦片并镜像持久化,GeoTrellis支持数据放在内存中,或者放在Accumulo,HABSE等分布式数据库或者HDFS和普通文件系统中

geotrellis.Ingest 是调用Geotrellis内部数据导入的类,就是调用了ETL类进行数据自动上传

EtlConf是GeoTrellis中导入数据的配置类,需要创建EtlConf的实例,然后交给ETL即可完成数据导入,依赖Inputjson,output.json和 backend-profiles.json文件

输入:input.json //ETL输入

输出:output.json //ETL输出

保存:backend.json //数据保存

PIPELINE 流水线

处理能力

瓦片切割

SHP转换JSON

矢量栅格化

矢量瓦片

生成金字塔

渲染图片

基于瓦片:

​ 渲染为JPG:renderJpg

​ 渲染为PNG: renderPng

​ 颜色表: ColorMap

​ Options[classBoundaryType noDataColor fallbackColor strict ]

ColorMap.fromQuantileBreaks

获取瓦片/合并瓦片

参考:https://www.cnblogs.com/shoufengwei/p/5753753.html

获取polygon对应瓦片:val masked = raster.mask(polygon)

合并瓦片:val stitch = masked.stitch val tile = stitch.tile

重采样/投影/数据类型

参考:https://www.cnblogs.com/shoufengwei/p/5753753.html

val rep = tile.reproject(extent, srcCRS, dstCRS, method).tile val res = rep.convert(cellType) 

瓦片裁剪

tile.crop(startcol, startrow, endcol, endrow) 

栅格切片

COG

FileCOGLayerWriter COGLayer

COGLayerMetadata

FileCOGValueReader

时序数据处理

  1. 导入数据文件,需要添加时间头

通过gdal添加时间头

gdal_edit -mo TIFFTAG_DATETIME="time" yourtiff.tif 

通过GeoTrellis添加时间头信息

val tiff = SinglebandGeoTiff(path) tiff.tags.headTags + (Tags.TIFFTAG_DATETIME -> time) val newtiff = new SinglebandGeoTiff(tiff.tile, tiff.extent, tiff.crs, Tags(map, tiff.tags.bandTags), tiff.options) newtiff.write(newTiffPath) 
  1. 改变导入参数

    普通tiff数据导入的时候条用ETL类的方式:

    Etl.ingest[ProjectedExtent, SpatialKey, Tile](args) 
    

    时间序列数据导入时:

    Etl.ingest[TemporalProjectedExtent, SpaceTimeKey, Tile](args) 
    

    主要添加时间支持,ProjectedExtent 变为 TemporalProjectExtent,SpatialKey变为SpaceTimeKey,如果是多波段的需要将Tile替换为 MultibandTile。

  2. 改变导入参数

    修改input.json中只需要将format由geotiff改为temporal-geotiff;output.json中需要将keyIndexMethod中的内容改成如下方式:

    "keyIndexMethod":{   
    "type":"zorder", 
    "temporalResolution": 86400000, 
    "timeTag":"TIFFTAG_DATETIME", 
    "timeFormat":"yyyy:MM:dd HH:mm:ss" 
    } 
    

    其中temporalResolution表示时间精度,理论上来说,设置此值表示当你根据时间查询的时候在这个精度范围内的数据都应该能够查询出来

    到此,时间序列数据已经导入到accumulo中。

  3. 获取对应时间序列瓦片

    前台将请求时间,瓦片的x,y,z传入后台,根据这四个参数查询,相较普通查询,多添加了饿时间条件

    val dt = DateTimeFormat.forPattern("yyyy:MM:dd HH:mm:ss").parseDateTime(time)
    val key = SpaceTimeKey(x, y, dt)
    val layerId = LayerId(name, zoom)
    respondWithMediaType(MediaTypes.`image/png`) {
    val result = {
      val tile = tileReader.reader[SpaceTimeKey, Tile](layerId).read(key)
      tile.renderPng.bytes
    }
    complete(result)
    }
    

    name标示数据导入是存放的名字,tileReader为AccumuloValueReader实例

    这样就能将用户请求的时间以及x、y、z瓦片数据渲染之后发送到前台,这里还需要强调的是Geotrellis中时间处理采用joda开源框架 。

分析能力

数据分析

坡度:Slope 读取瓦片,调用tile.slope 即可实现坡度

缓冲区: Buffer

栅格化几何:

Rasterizer.foreachCellByGeometry(geom, rasterExtent)(f)  
ArrayTile(array, rasterExtent.cols, rasterExtent.rows) 

统计分析

高程信息:tile.histogram

tile.histogram.foreach { (key, value) => { map(key.toDouble) = map(key.toDouble) + value } 

参考

weishoufeng: https://blog.csdn.net/luoye4321/article/category/9294349
https://blog.csdn.net/qq_32432081?t=1
https://www.cnblogs.com/shoufengwei/p/5619419.html

你可能感兴趣的:(GIS-Spark)