前言

前面介绍过了如何在 ETL 的时候更新 Layer,使得能够在大数据量的时候完成 ETL 操作,同时前两篇文章也介绍了 COG 以及如何在 Geotrellis 中实现 COG 的读取。本文介绍如何在进行 COG 方式 ETL 的时候实现 Layer 的更新。

一、实现

1.1 原理分析

其实实现 COG 方式的 Layer 更新就是把上述两种方式结合起来,唯一的区别在于普通的 ETL 操作更新的时候需要合并的是同一个 Layer 下编号相同的瓦片,而 COG 方式的 ETL 更新的时候需要合并的是同一个 Layer 下编号相同的 GeoTiff 文件,明白了这一点实现起来就很容易了。

1.2 实现方案

上一篇文章中讲了如何实现 COG 的数据写入,执行写入操作的是最后一行代码:

writer.writeCOGLayer(layerName, cogLayer, keyIndexes)

其中 writer 是 FileCOGLayerWriter 实例或者其他 COGLayerWriter 实例,layerName 表示写入的层,cogLayer 为需要写入的数据。

所以理论上实现方式为首先判断此 Layer 是否存在,如果存在则更新之,否则执行上述 writeCOGLayer 方法。

其实 writeCOGLayer 方法已经帮我们实现了这一步,只需要传入一个 GeoTiff 的 merge 方法即可。merge 的类型为 (GeoTiff[V], GeoTiff[V]) => GeoTiff[V],V 为 Tile 或者 MultibandTile 类型,其实就是如何将两个 GeoTiff 合并成一个 GeoTiff。

这就很简单了,只需要写一个此方法即可,如下:

def merge(v1: GeoTiff[V], v2: GeoTiff[V]) = {
val tile: V = v2.tile merge v1.tile
val extent = v2.extent combine v1.extent
val crs = v2.crs
GeoTiffBuilder[V].makeGeoTiff(
tile, extent, crs, Tags(Map(), Nil), GeoTiffOptions.DEFAULT
)
}

只需要将此方法传入即可,在 writeCOGLayer 方法中的下述方法会自动完成 update 操作:

case Some(merge) if uriExists(path) =>
val old = GeoTiffReader[V].read(path, decompress = false, streaming = true)
val merged = merge(cog, old)
merged.write(path, true)
// collect VRT metadata
(0 until merged.bandCount)
.map { b =>
val idx = Index.encode(keyIndex.toIndex(key), maxWidth)
(idx.toLong, vrt.simpleSource(s"$idx.$Extension", b + 1, merged.cols, merged.rows, merged.extent))
}
.foreach(samplesAccumulator.add)

这也正与我们的分析一样,此方法将两个 tiff 合并成一个写入。

1.3 效果

编译执行两次数据 COG 方式导入,可以看到两个数据完美的拼接在一起,继续放大,然而居然出问题了,中间有些 zoom 下的结合处瓦片不翼而飞了,这是什么原因?为什么仅仅是有些 zoom 下的丢失了?

其实静下心来分析就不难知道,存在的问题一定在我们自己写的 merge 方法中,并且是合并后的 Tiff 文件未实现 COG 造成的,因为没有实现 COG 导致有些 zoom 下无法读取,所以取不到数据。

1.4 优化

明白了这一点优化起来就很容易了,只需要看一下 Geotrellis 是如何生成 COG 方式的 Tiff 的,我们也按照此方式生成合并后的 Tiff 即可。

private def generateGeoTiffRDD[
K: SpatialComponent: Ordering: JsonFormat: ClassTag,
V <: CellGrid: ClassTag: ? => TileMergeMethods[V]: ? => TilePrototypeMethods[V]: ? => TileCropMethods[V]: GeoTiffBuilder
](
rdd: RDD[(K, V)],
zoomRange: ZoomRange ,
layoutScheme: ZoomedLayoutScheme,
cellType: CellType,
compression: Compression
): RDD[(K, GeoTiff[V])] = {
val kwFomat = KryoWrapper(implicitly[JsonFormat[K]])
val crs = layoutScheme.crs val minZoomLayout = layoutScheme.levelForZoom(zoomRange.minZoom).layout
val maxZoomLayout = layoutScheme.levelForZoom(zoomRange.maxZoom).layout val options: GeoTiffOptions =
GeoTiffOptions(
storageMethod = Tiled(maxZoomLayout.tileCols, maxZoomLayout.tileRows),
compression = compression
) rdd.
mapPartitions { partition =>
partition.map { case (key, tile) =>
val extent: Extent = key.getComponent[SpatialKey].extent(maxZoomLayout)
val minZoomSpatialKey = minZoomLayout.mapTransform(extent.center) (key.setComponent(minZoomSpatialKey), (key, tile))
}
}.
groupByKey(new HashPartitioner(rdd.partitions.length)).
mapPartitions { partition =>
val keyFormat = kwFomat.value
partition.map { case (key, tiles) =>
val cogExtent = key.getComponent[SpatialKey].extent(minZoomLayout)
val centerToCenter: Extent = {
val h = maxZoomLayout.cellheight / 2
val w = maxZoomLayout.cellwidth / 2
Extent(
xmin = cogExtent.xmin + w,
ymin = cogExtent.ymin + h,
xmax = cogExtent.xmax - w,
ymax = cogExtent.ymax - h)
}
val cogTileBounds: GridBounds = maxZoomLayout.mapTransform.extentToBounds(centerToCenter)
val cogLayout: TileLayout = maxZoomLayout.layoutForBounds(cogTileBounds).tileLayout val segments = tiles.map { case (key, value) =>
val SpatialKey(col, row) = key.getComponent[SpatialKey]
(SpatialKey(col - cogTileBounds.colMin, row - cogTileBounds.rowMin), value)
} val cogTile = GeoTiffBuilder[V].makeTile(
segments.iterator,
cogLayout,
cellType,
Tiled(cogLayout.tileCols, cogLayout.tileRows),
compression) val cogTiff = GeoTiffBuilder[V].makeGeoTiff(
cogTile, cogExtent, crs,
Tags(Map("GT_KEY" -> keyFormat.write(key).prettyPrint), Nil),
options
).withOverviews(NearestNeighbor) (key, cogTiff)
}
}
}

这是 Geotrellis 中的 COG Tiff 生成代码,重点在于最下面的 GeoTiffBuilder[V].makeGeoTiff 方法,可以看到与我们上面的方式稍微有些不同,只需要按照其修改即可。如下:

def merge(v1: GeoTiff[V], v2: GeoTiff[V]) = {
val tile: V = v2.tile merge v1.tile
val extent = v2.extent combine v1.extent
val crs = v2.crs
GeoTiffBuilder[V].makeGeoTiff(
tile, extent, crs, v2.tags, v2.options), GeoTiffOptions.DEFAULT
).withOverviews(NearestNeighbor)
}

主要变化在于 Tiff 的 tag 使用已有 Tiff 的 tag,这样会添加 GT_KEY 标签,添加了已有 Tiff 的options,并添加了 withOverviews 方法,这样就能满足 COG 的要求,生成符合 COG 格式的 Geotrellis 下的 Tiff 文件。

三、总结

本文介绍了如何实现 COG 模式下 ETL 的 Layer 更新操作,只要想明白原理,其实代码本就不复杂,这也是我对待码农工作的个人感悟:重要的在于编程思维、解决问题能力的培养,而不是具体的代码。

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

我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=3tczwqg3smw44

geotrellis使用(三十九)COG 写入更新的更多相关文章

  1. Gradle 1.12用户指南翻译——第三十九章. IDEA 插件

    本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  2. NeHe OpenGL教程 第三十九课:物理模拟

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  3. WPF,Silverlight与XAML读书笔记第三十九 - 可视化效果之3D图形

    原文:WPF,Silverlight与XAML读书笔记第三十九 - 可视化效果之3D图形 说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘> ...

  4. 《手把手教你》系列技巧篇(三十九)-java+ selenium自动化测试-JavaScript的调用执行-上篇(详解教程)

    1.简介 在做web自动化时,有些情况selenium的api无法完成,需要通过第三方手段比如js来完成实现,比如去改变某些元素对象的属性或者进行一些特殊的操作,本文将来讲解怎样来调用JavaScri ...

  5. Java进阶(三十九)Java集合类的排序,查找,替换操作

    Java进阶(三十九)Java集合类的排序,查找,替换操作 前言 在Java方向校招过程中,经常会遇到将输入转换为数组的情况,而我们通常使用ArrayList来表示动态数组.获取到ArrayList对 ...

  6. SQL注入之Sqli-labs系列第三十八关、第三十九关,第四十关(堆叠注入)

    0x1 堆叠注入讲解 (1)前言 国内有的称为堆查询注入,也有称之为堆叠注入.个人认为称之为堆叠注入更为准确.堆叠注入为攻击者提供了很多的攻击手段,通过添加一个新 的查询或者终止查询,可以达到修改数据 ...

  7. 第三百三十九节,Python分布式爬虫打造搜索引擎Scrapy精讲—Scrapy启动文件的配置—xpath表达式

    第三百三十九节,Python分布式爬虫打造搜索引擎Scrapy精讲—Scrapy启动文件的配置—xpath表达式 我们自定义一个main.py来作为启动文件 main.py #!/usr/bin/en ...

  8. centos shell编程5 LANMP一键安装脚本 lamp sed lnmp 变量和字符串比较不能用-eq cat > /usr/local/apache2/htdocs/index.php <<EOF重定向 shell的变量和函数命名不能有横杠 平台可以用arch命令,获取是i686还是x86_64 curl 下载 第三十九节课

    centos shell编程5  LANMP一键安装脚本 lamp  sed  lnmp  变量和字符串比较不能用-eq  cat > /usr/local/apache2/htdocs/ind ...

  9. “全栈2019”Java第三十九章:构造函数、构造方法、构造器

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  10. 微信小程序把玩(三十九)navigation API

    原文:微信小程序把玩(三十九)navigation API 演示效果也看到了小程序也就提供这几个处理导航控制.值得注意的是只能同时导航五个页面 主要属性: 导航条一些方法 wx.setNavigati ...

随机推荐

  1. js网页判断移动终端浏览器版本信息是安卓还是苹果ios,判断在微信浏览器跳转不同页面,生成二维码

    一个二维码,扫描进入网页,自动识别下载苹果和安卓客户端,判断网页如下,(只有苹果的微信不能自动跳转)所以加个微信判断. <!DOCTYPE html> <html> <h ...

  2. 利用1.1.1.1进行DNS网络加速,仅需2分钟让网络更快

    NEWS 近日,Cloudflare 和 APNIC联合推出了1.1.1.1DNS网络加速. Cloudflare 运行全球规模最大.速度最快的网络之一.APNIC 是一个非营利组织,管理着亚太和大洋 ...

  3. Beta阶段总结分析报告

    1 讨论照片 2 Postmortem结果 二手交易平台项目Postmortem结果 整理:程环宇 设想和目标 1.       我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有 ...

  4. Java Collections API和泛型

    Java Collections API和泛型 数据结构和算法 学会一门编程语言,你可以写出一些可以工作的代码用计算机来解决一些问题,然而想要优雅而高效的解决问题,就要学习数据结构和算法了.当然对数据 ...

  5. 基础篇 - SQL 的约束

    基础篇 - SQL 的约束       约束 一.实验简介 约束是一种限制,它通过对表的行或列的数据做出限制,来确保表的数据的完整性.唯一性.本节实验将在实践操作中熟悉 MySQL 中的几种约束. 二 ...

  6. 【iOS】字号问题

    一,ps和pt转换 px:相对长度单位.像素(Pixel).(PS字体) pt:绝对长度单位.点(Point).(iOS字体) 公式如下: pt=(px/96)*72. 二,字体间转换 1in = 2 ...

  7. 洛谷P2894 [USACO08FEB]酒店Hotel

    P2894 [USACO08FEB]酒店Hotel https://www.luogu.org/problem/show?pid=2894 题目描述 The cows are journeying n ...

  8. nyoj 邮票分你一半

    邮票分你一半 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述      小珂最近收集了些邮票,他想把其中的一些给他的好朋友小明.每张邮票上都有分值,他们想把这些邮票分 ...

  9. Java面试题合集(二)

    接下来几篇文章准备系统整理一下有关Java的面试题,分为基础篇,javaweb篇,框架篇,数据库篇,多线程篇,并发篇,算法篇等等,陆续更新中.其他方面如前端后端等等的面试题也在整理中,都会有的. 注: ...

  10. Centos6.7的在虚拟机virulBox下的lamp平台的搭建

    实验环境: linux:小甲鱼带你学C语言,带你飞的提供的体积比较小的centos6.7和virtualBox mysql,apahce,php是燕十八在Linux基础进阶中提供的安装方式: 结果,安 ...