前言

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

主要是前端渲染,像原生的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. Uva11582

    最近各种破事忙死了 终于开始做题了 紫薯第10章第一题,come on 设g(i)=f(i) mod n,当二元组(g(i).g(i+1))出现重复时,整个序列就开始重复(这一话怎么也不懂,请大神解释 ...

  2. 三、Spring的面向切面

    Spring的面向切面 在应用开发中,有很多类似日志.安全和事务管理的功能.这些功能都有一个共同点,那就是很多个对象都需要这些功能.复用这些通用的功能的最简单的方法就是继承或者委托.但是当应用规模达到 ...

  3. 【转】缓存淘汰算法系列之1——LRU类

    原文地址:http://www.360doc.com/content/13/0805/15/13247663_304901967.shtml 参考地址(一系列关于缓存的,后面几篇也都在这里有):htt ...

  4. [Python] 文科生零基础学编程系列一——对象、集合、属性、方法的基本定义

    1.编程语言: 1.1是什么: 编程语言(programming language),是用来定义计算机程序的形式语言.它是一种被标准化的交流技巧,用来向计算机发出指令. 一种计算机语言让程序员能够准确 ...

  5. JAVA基础知识总结:一

    一.软件开发的常识 1.什么是软件? 一系列按照特定顺序组织起来的计算机数据或者指令 常见的软件: 系统软件:Windows\Mac OS \Linux 应用软件:QQ,一系列的播放器(爱奇艺,乐视, ...

  6. VPS搭建离线下载服务器——后网盘时代

    动机 由于学习的需要,在国外某服务器厂商购买了vps服务(至于是哪个厂商就不说啦).但是呢,就算用作梯子,一个月1T的流量总是用不完.最经觉得自己营养充足,想找点电影看看. 无奈现在百度网盘的速度真的 ...

  7. angualr高级篇之elem.scope()、elem.isolateScope和$compile(elem)(scope)中scope的区别

    在angular的使用过程中我们经常用$rootScope.$new()为elem创建一个新的作用域scope,然后使用$compile(elem)(scope)编译这个含有指令的元素.那么这里传进去 ...

  8. sphinx实时索引和高亮显示

    sphinx实时索引和高亮显示 时间 2014-06-25 14:50:58  linux技术分享 -欧阳博客 原文  http://www.wantlearn.net/825 主题 Sphinx数据 ...

  9. How Many Answers Are Wrong

    How Many Answers Are Wrong Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/ ...

  10. Android 开发笔记___Application操作全局变量

    只要app在运行中,他就是一个application.因此可以用它来保存一些全局变量 package com.example.alimjan.hello_world; import android.a ...