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表情包.某些节 ...
随机推荐
- canvas画布标签
最近良师益友整理一些canvas的资料,加强学习了解! 当你创建一个<canvas>元素后,就拥有了它的绘图上下文. 一.简单图形 1.getContext()方法 为了在canvas上绘 ...
- Linux系列教程(六)——Linux文件搜索命令
前一篇博客我们讲解了Linux链接命令和权限管理命令, 通过 ln -s 链接名 表示创建软链接,不加-s表示创建硬链接:还有三个更改权限的命令,chmod命令可以更改文件或目录权限,chown命令 ...
- IIS部署网站时常见问题解决
首先服务器上安装IIS和Framework\v4.0 一.打开iis服务管理器 左侧目录中选择网站右键,选择添加网站 填写网站名称.选择项目存放的路径.ip地址和端口 VS用的是4.0,iis中网站也 ...
- javascript算法(一)
1.实现一个函数,运算结果可以满足如下预期结果: add(1)(2) // 3 add(1, 2, 3)(10) // 16 add(1)(2)(3)(4)(5) // 15 实现: function ...
- 28款GitHub最流行的开源机器学习项目
现在机器学习逐渐成为行业热门,经过二十几年的发展,机器学习目前也有了十分广泛的应用,如:数据挖掘.计算机视觉.自然语言处理.生物特征识别.搜索引擎.医学诊断.DNA序列测序.语音和手写识别.战略游戏和 ...
- javascript 之执行环境-08
概念 执行环境(Execution context,简称EC)或执行上下文对象(后面统一用执行上下文表示),它定义了变量或者函数有权访问的其他数据,决定了他们各自的行为.是不是有点不好理解,那我先简单 ...
- TCP建立连接和断开连接图解
参考博客: http://blog.csdn.net/whuslei/article/details/6667471 http://www.2cto.com/net/201310/251896.htm ...
- HDU 5242 Game(三个贪心)
Game Time Limit: 3000/1500 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- Cactus
Cactus Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submis ...
- Be the Winner
Be the Winner Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total ...