geotrellis使用(三十五)Cesium加载geotrellis TMS瓦片
前言
做任何事情都不是想象中的那么简单。好久没有更新技术博客了,跟最近瞎忙有很大关系,虽说是瞎忙也抽空研究了些技术。
主要是前端渲染,像原生的WebGL和Cesium。WebGL写了几篇博客,自我感觉还可以。Cesium是一个封装好的WEB端3D Earth框架,有了WebGL的基础之后切换到Cesium按理说一切应该是顺理成章,简单的测试了几个功能之后发现确实非常好,简单的几行代码就可以实现Google Earth的功能,当然Google Earth重要的绝对不是他的渲染框架。
前期做了很多Geotrellis的工作,那么我就想着能不能把Geotrellis发布的TMS加载到Cesium中来,本来这是很简单的嘛,以前是在leaft-let中显示,现在就是换一个地方显示而已,并且Cesium已经调通。说干就干,结果怎么着,前天晚上整到四点,昨天折腾了几个小时居然一直不出图,所以我说任何看似简单的事情其实都不简单,下面就让我娓娓道来。
一、Cesium
1.1 简介
介绍之前还是来简单介绍一下Cesium,当然如果后面继续对此框架进行研究的话可能也会多写几篇关于此框架的博客。
官网地址:https://cesiumjs.org/,Github地址:https://github.com/AnalyticalGraphicsInc/cesium。
其功能简单明了,当然也很强大,基础教程可以参考http://blog.csdn.net/UmGsoil/article/category/7005304,当然官方文档更好。
1.2 简单使用
无需考虑这么复杂,从简单里说Cesium就是一个前端地图渲染引擎,与leaft-let、OpenLayer相同,只是Cesium做成了3D的。所以从基础功能都是相似的。
首先在html页面加载Cesium,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello 3D Earth</title>
<script src="CesiumUnminified/Cesium.js"></script>
<style>
@import url(CesiumUnminified/Widgets/widgets.css);
html, body, #cesiumContainer {
width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;
}
</style>
</head>
<body>
<div id="cesiumContainer"></div>
<script src="my_js.js"></script>
</body>
</html>
其中CesiumUnminified存储了相关文件,从Github中下载即可。my_js.js是我们自己要写的js文件。my_js.js最简单的情况只需要一句话即可:
var viewer = new Cesium.Viewer("cesiumContainer");
这样浏览器就会渲染出一个3维地球并自动加载微软的影像地图。那么如何更改或者添加图层呢?
var viewer = new Cesium.Viewer("cesiumContainer", {
animation: true, //是否显示动画控件(左下方那个)
baseLayerPicker: false, //是否显示图层选择控件
geocoder: true, //是否显示地名查找控件
timeline: true, //是否显示时间线控件
sceneModePicker: true, //是否显示投影方式控件
navigationHelpButton: false, //是否显示帮助信息控件
infoBox: true, //是否显示点击要素之后显示的信息
imageryProvider : new Cesium.WebMapTileServiceImageryProvider({
url: "http://t0.tianditu.com/vec_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=vec&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles",
layer: "tdtVecBasicLayer",
style: "default",
format: "image/jpeg",
tileMatrixSetID: "GoogleMapsCompatible",
show: false
}),
geocoder: false // no default bing maps
});
//全球影像中文注记服务
viewer.imageryLayers.addImageryProvider(new Cesium.WebMapTileServiceImageryProvider({
url: "http://t0.tianditu.com/cia_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=cia&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default.jpg",
layer: "tdtAnnoLayer",
style: "default",
format: "image/jpeg",
tileMatrixSetID: "GoogleMapsCompatible",
show: false
}));
这段代码就会自动在3维地球中加载天地图的线划图并添加注记。所以剩下的事情就很简单了,只需要再添加我自己的TMS即可。
1.3 问题来了
在上述代码下方添加如下代码:
var layers = viewer.scene.imageryLayers; //所有图层(非基本图层)
var layer = layers.addImageryProvider(
new Cesium.UrlTemplateImageryProvider({
url : 'http://xxxx/modis/ndvi/{z}/{x}/{y}',
format: "image/png"
})
);
//50%透明度
layer.alpha = 0.5;
//两倍亮度
layer.brightness = 2.0;
很简单的代码,获取图层对象,然后添加一层,url为我们自己的瓦片请求格式,这是我用Geotrellis发布的modis数据ndvi服务。并设置该图层透明度和增加亮度防止盖住上面的注记层。本来应该是点击一下刷新就出来效果的事情,结果足足折腾到我崩溃。
无论怎么刷新就是出不来那层瓦片,其他两层数据正常显示,打开浏览器的调试模式,能够看到对ndvi瓦片的请求返回的都是200 OK,也能在调试中看到单个瓦片应有的效果。然后变换各种添加图层的格式(UrlTemplateImageryProvider、WebMapTileServiceImageryProvider、Cesium.createTileMapServiceImageryProvider这些不是本文重点,在后续文章详细介绍)均显示不出瓦片,而后又去掉其他两层瓦片只保留NDVI,最后又添加Geotrellis发布的其他TMS服务,但是无论怎么折腾,只要是我自己Geotrellis发布的TMS均无法显示,折腾到四点多,始终没有出来,在stackoverflow和github上提了问,等了半天也无人回复,只好闷闷不乐的去睡了。
二、解决方案
2.1 转角遇到答案
今天中午小睡片刻,起床后收到一封邮件,赶紧打开看了一下,是Github的回复邮件,喜出望外,结果一看内容原来是告诉我不要在Issue中发布提问,告诉了我Google的提问列表(https://groups.google.com/forum/#!msg/cesium-dev/RfAlZZkPBaM/xGOK01trAwAJ;context-place=forum/cesium-dev),整个人当时就不好了,既然这样只有上去瞅瞅,打开简单一搜索,居然有现成的,问题描述跟我的一模一样,解决方案是添加CORS。
其实我之前折腾到四点多的时候脑子里就有这个意识,一定是我发布的TMS缺少了某个东西(或者是某个东西与Cesium的要求不一致),导致Cesium无法正常显示我的瓦片,所以一看到这个我就亢奋了,程序员的直觉告诉我这肯定就是我要找的东西。
2.2 解决
所以问题就来了,看样子我要在Geotrellis中折腾CORS了。Geotrellis采用Scala语言开发,所以我也是拿Scala写的,发布网络服务用的是Akka,Akka是开源的网络服务框架,于是就搜索了一下Akka CORS,很快就有了答案。
关于CORS的介绍,看这篇文章就够了:http://www.ruanyifeng.com/blog/2016/04/cors.html。CORS简单来说就是跨域资源共享,当跨域进行Ajax请求的时候进行权限验证等操作。其实细细想来倒是这么回事,Cesium请求瓦片一定用的是XMLHttpRequest,而我的TMS又未使用CORS,于是怎么折腾都出不来结果,当然对这块不太熟悉是导致问题发生的直接原因。
找到问题解决就很容易了,Github中有现成的解决方案。首先添加一个CorsSupport特质,如下:
import akka.http.scaladsl.model.HttpHeader
import akka.http.scaladsl.model.HttpMethods._
import akka.http.scaladsl.model.HttpResponse
import akka.http.scaladsl.model.headers._
import akka.http.scaladsl.model.headers.Origin
import akka.http.scaladsl.server.Directive0
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.MethodRejection
import akka.http.scaladsl.server.RejectionHandler
trait CorsSupport {
protected def corsAllowOrigins: List[String]
protected def corsAllowedHeaders: List[String]
protected def corsAllowCredentials: Boolean
protected def optionsCorsHeaders: List[HttpHeader]
protected def corsRejectionHandler(allowOrigin: `Access-Control-Allow-Origin`) =
RejectionHandler
.newBuilder().handle {
case MethodRejection(supported) =>
complete(HttpResponse().withHeaders(
`Access-Control-Allow-Methods`(OPTIONS, supported) ::
allowOrigin ::
optionsCorsHeaders
))
}
.result()
private def originToAllowOrigin(origin: Origin): Option[`Access-Control-Allow-Origin`] =
if (corsAllowOrigins.contains("*") || corsAllowOrigins.contains(origin.value))
origin.origins.headOption.map(`Access-Control-Allow-Origin`.apply)
else
None
def cors[T]: Directive0 = mapInnerRoute { route => context =>
((context.request.method, context.request.header[Origin].flatMap(originToAllowOrigin)) match {
case (OPTIONS, Some(allowOrigin)) =>
handleRejections(corsRejectionHandler(allowOrigin)) {
respondWithHeaders(allowOrigin, `Access-Control-Allow-Credentials`(corsAllowCredentials)) {
route
}
}
case (_, Some(allowOrigin)) =>
respondWithHeaders(allowOrigin, `Access-Control-Allow-Credentials`(corsAllowCredentials)) {
route
}
case (_, _) =>
route
})(context)
}
}
尔后在发布TMS服务的类中实现该特质:重写虚方法,并在原先发布TMS服务的地方将原结果传入cors方法:
override val corsAllowOrigins: List[String] = List("*")
override val corsAllowedHeaders: List[String] = List("Origin", "X-Requested-With", "Content-Type", "Accept", "Accept-Encoding", "Accept-Language", "Host", "Referer", "User-Agent")
override val corsAllowCredentials: Boolean = true
override val optionsCorsHeaders: List[HttpHeader] = List[HttpHeader](
`Access-Control-Allow-Headers`(corsAllowedHeaders.mkString(", ")),
`Access-Control-Max-Age`(60 * 60 * 24 * 20), // cache pre-flight response for 20 days
`Access-Control-Allow-Credentials`(corsAllowCredentials)
)
def service = cors {
pathPrefix("map") {
...
}
}
注意此处的cors方法,其本身是一个无参数方法,此处传入的是Directive0的apply方法的参数,所以返回的仍然是Route类型。
上述两段代码实现的就是将TMS服务实现CORS服务。请求的域为*,即任何域都可;请求头为"Origin", "X-Requested-With", "Content-Type", "Accept", "Accept-Encoding", "Accept-Language", "Host", "Referer", "User-Agent";并支持发送cookie等认证。完成上述改造后重新编译运行geotrellis程序,刷新浏览器即可看到我们想要的结果,效果如下:

三、总结
本文简单记录了将Cesium和Geotrellis结合中碰到的一个小问题,只是刚开始,后续估计问题会更多,无他法,只能咬着牙往下走。结果很简单,折腾的时间却很长,但是不折腾肯定是不会有结果的,只能是想办法加快折腾的速度。当然有些东西一定会记得你的折腾,比如腰椎颈椎当然还有大脑,在折腾中你会对整体框架更加熟悉。
Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html
geotrellis使用(三十五)Cesium加载geotrellis TMS瓦片的更多相关文章
- WebGL简易教程(十五):加载gltf模型
目录 1. 概述 2. 实例 2.1. 数据 2.2. 程序 2.2.1. 文件读取 2.2.2. glTF格式解析 2.2.3. 初始化顶点缓冲区 2.2.4. 其他 3. 结果 4. 参考 5. ...
- cocos2d-x游戏开发(十五)游戏加载动画loading界面
个人原创,欢迎转载:http://blog.csdn.net/dawn_moon/article/details/11478885 这个资源加载的loading界面demo是在玩客网做逆转三国的时候随 ...
- FreeSql (三十五)CodeFirst 自定义特性
比如项目内已经使用了其它 orm,如 efcore,这样意味着实体中可能存在 [Key],但它与 FreeSql [Column(IsPrimary = true] 不同. Q: FreeSql 实体 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (22) -----第五章 加载实体和导航属性之延迟加载
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第五章 加载实体和导航属性 实体框架提供了非常棒的建模环境,它允许开发人员可视化地使 ...
- webpack入坑之旅(五)加载vue单文件组件
这是一系列文章,此系列所有的练习都存在了我的github仓库中vue-webpack,在本人有了新的理解与认识之后,会对文章有不定时的更正与更新.下面是目前完成的列表: webpack入坑之旅(一)不 ...
- NeHe OpenGL教程 第三十五课:播放AVI
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- centos shell脚本编程1 正则 shell脚本结构 read命令 date命令的用法 shell中的逻辑判断 if 判断文件、目录属性 shell数组简单用法 $( ) 和${ } 和$(( )) 与 sh -n sh -x sh -v 第三十五节课
centos shell脚本编程1 正则 shell脚本结构 read命令 date命令的用法 shell中的逻辑判断 if 判断文件.目录属性 shell数组简单用法 $( ) 和$ ...
- cesium加载gltf模型
cesium加载gltf模型 一.采用vue-cesium:在项目里加载依赖包.命令如下: npm i --save vue-cesium 在main.js中加入如下代码: https://www.n ...
- 程序员与年龄:四十岁普通开发、三十五岁首席架构、三十岁基层Leader
最近,有一个词儿特别热门--躺平.有没有人跟你说过:"躺平说起来容易,做起来更容易." 和躺平相对的是另外一个词--内卷,群聊的时候,已经很多次看过草卷起来了.jpg表情包.某些节 ...
随机推荐
- HTML5之appcache语法理解/HTML5应用程序缓存/manifest缓存文件官方用法翻译
习惯性的贴几个参考链接: W3School-HTML 5 应用程序缓存 官方 MDN window.applicationCache 接口文档 官方 MDN 用法示例 看所有的教程不如直接看最原始的官 ...
- Linux系统fdisk分区
以下操作全部基于win7 64位系统上的Linux虚拟机(CentOS6.6). 当Linux虚拟机的硬盘空间不够用时,可以手动添加硬盘块,流程如下: 右键虚拟机,点击“Add”按钮: 选择“Hard ...
- 基于容器微服务的PaaS云平台设计(二)通过kubernetes实现微服务容器管理
版权声明:本文为博主原创文章,欢迎转载,转载请注明作者.原文超链接 ,博主地址:http://www.cnblogs.com/SuperXJ/ 上一章描述了基于spring cloud的微服务实例(实 ...
- RestServer 2.0 正式版发布
RestServer 2.0 正式版发布 使用许可&版权说明 在保持本软件完整的情况下可以将本软件用于任何商业用途. 本软件可以自由传播,但是请保持软件相关文件和说明文档完整. 未经许可不得将 ...
- 异步API
整合到一起,作为参考: //2 5 3 4 1 //2 5 3 1 4 var img = new Image(); img.src = '.'; img.onerror = function() { ...
- Crazy Calendar (阶梯博弈变形)
2011 was a crazy year. Many people all over the world proposed on 11-11-11, married on 11-11-11, som ...
- ssh分发秘钥时出现错误“Permission denied (publickey,gssapi-keyex,gssapi-with-mic)”
因为公司的服务器连接是通过xshell公钥和密码连接的,今天在ssh分发秘钥的时候出现了,下面的错误: [root@iZ2ze97cumk8opqm28h8Z .ssh]# ssh-copy-id - ...
- 数组删除操作 splice
原理通过设置 函数的 length 属性 var a = [1, 2, 3, 4]; a.length = 3 ; 结果 : a = [1,2,3]
- 移动端车牌识别sdk开发包(可下载)
移动端车牌识别是一项基于OCR识别的应用技术.移动端车牌识别过程主要包含五个步骤,其中包括图像采集.图像预处理.车牌定位.字符分割.字符识别.输出结果等一系列计算机算法运算, 第一步[图像采集]:此步 ...
- ⒁bootstrap组件 工具提示框 弹出框 警告框 基础案例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...