前言

做任何事情都不是想象中的那么简单。好久没有更新技术博客了,跟最近瞎忙有很大关系,虽说是瞎忙也抽空研究了些技术。

主要是前端渲染,像原生的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瓦片的更多相关文章

  1. WebGL简易教程(十五):加载gltf模型

    目录 1. 概述 2. 实例 2.1. 数据 2.2. 程序 2.2.1. 文件读取 2.2.2. glTF格式解析 2.2.3. 初始化顶点缓冲区 2.2.4. 其他 3. 结果 4. 参考 5. ...

  2. cocos2d-x游戏开发(十五)游戏加载动画loading界面

    个人原创,欢迎转载:http://blog.csdn.net/dawn_moon/article/details/11478885 这个资源加载的loading界面demo是在玩客网做逆转三国的时候随 ...

  3. FreeSql (三十五)CodeFirst 自定义特性

    比如项目内已经使用了其它 orm,如 efcore,这样意味着实体中可能存在 [Key],但它与 FreeSql [Column(IsPrimary = true] 不同. Q: FreeSql 实体 ...

  4. 《Entity Framework 6 Recipes》中文翻译系列 (22) -----第五章 加载实体和导航属性之延迟加载

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第五章 加载实体和导航属性 实体框架提供了非常棒的建模环境,它允许开发人员可视化地使 ...

  5. webpack入坑之旅(五)加载vue单文件组件

    这是一系列文章,此系列所有的练习都存在了我的github仓库中vue-webpack,在本人有了新的理解与认识之后,会对文章有不定时的更正与更新.下面是目前完成的列表: webpack入坑之旅(一)不 ...

  6. NeHe OpenGL教程 第三十五课:播放AVI

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

  7. centos shell脚本编程1 正则 shell脚本结构 read命令 date命令的用法 shell中的逻辑判断 if 判断文件、目录属性 shell数组简单用法 $( ) 和${ } 和$(( )) 与 sh -n sh -x sh -v 第三十五节课

    centos   shell脚本编程1 正则  shell脚本结构  read命令  date命令的用法  shell中的逻辑判断  if 判断文件.目录属性  shell数组简单用法 $( ) 和$ ...

  8. cesium加载gltf模型

    cesium加载gltf模型 一.采用vue-cesium:在项目里加载依赖包.命令如下: npm i --save vue-cesium 在main.js中加入如下代码: https://www.n ...

  9. 程序员与年龄:四十岁普通开发、三十五岁首席架构、三十岁基层Leader

    最近,有一个词儿特别热门--躺平.有没有人跟你说过:"躺平说起来容易,做起来更容易." 和躺平相对的是另外一个词--内卷,群聊的时候,已经很多次看过草卷起来了.jpg表情包.某些节 ...

随机推荐

  1. 一份传世典文:十年编程(Teach Yourself Programming in Ten Years)

    原文:Teach Yourself Programming in Ten Years作者:郭晓刚翻译:郭晓刚(foosleeper@163.net)最后修订日期:2004-3-192005-01-12 ...

  2. Javaweb配置最全的数据源配置

    DBCP DBCP是Apache推出的数据库连接池(Database Connection Pool). 操作步骤: 添加jar包: commons-dbcp-1.4.jar commons-pool ...

  3. Unity3d 2017

    Unity3d引擎的新纪元--Unity3d 2017 来源 http://blog.csdn.net/dark00800/article/details/75209544 Unity3d不久之前正式 ...

  4. zookeeper分布式搭建

    1下载并解压zookeeper安装包 2进入zookeeper配置文件目录,找到zoo_sample.cfg,执行cp zoo_sample.cfg  zoo.cfg 3打开zoo.cfg文件,修改d ...

  5. scanf和cin性能的比较

    我的实验机器配置是: 处理器:Intel(R) Core(TM) i3-7100U CPU @ 2.40GHz 2.40GHz 随机访问存储器:4.00GB 操作系统:Windows10 集成开发环境 ...

  6. IOS学习——iphone X的适配

    说实话,对于一个刚入门iOS两个月的新手而言,在拿到这个任务的时候整个人都是懵逼的,怎么做适配?哪些地方需要适配?该怎么做?一个个问题搞得头都大了. 首先,啥都不管,先在iPhone X上运行起来看看 ...

  7. Grunt打包之seajs项目【转】

    原文:http://www.cnblogs.com/accordion/p/4508154.html grunt与seajs grunt是前端流行的自定义任务的脚手架工具,我们可以使用grunt来为我 ...

  8. ligerUI---ligerGrid中treegrid(表格树)的使用

    写在前面: 表格树是在普通ligerGrid的基础上,做了一点改变,使数据以表格树的形式显示出来,适用于有级别的数据比如菜单(有父菜单,父菜单下面有子菜单).表格树的显示有两种方法,可以根据自己的项目 ...

  9. 入门到熟练-Eclipse开发工具

    1. 概述 本文用于Eclipse说明开发功能的各种配置.希望可以帮助到对于Eclipse工具设置不同熟练的朋友,快速上手Eclipse开发工具. 2. Eclipse的配置 2.1. 设置Eclip ...

  10. HTML5对音视频的处理

      前  言 现在网上有许多的框架和插件,能够满足程序猿的各种需求,慢慢的,就有些忽视最基础的东西. 比如,大多数视频是通过插件(比如 Flash)来显示的.然而,并非所有浏览器都拥有同样的插件. H ...