记一次 CesiumJS 中 WMTS 数据的加载
CesiumJS 能用的 WMTS 目前只支持两种切片方案(TilingScheme):
GeographicTilingSchemeWebMercatorTilingScheme光说很抽象,上图:


0 级瓦片有 2 个的投影,是直接以经纬度数值展平成平面,众所周知:
所以 GeographicTilingScheme 的样子就是一个一比二的 矩形,刚好就在 0 级瓦片时有两个,后续就按常规的四叉树切分即可。而 WebMercator 投影后的坐标系 xy 值域是 \([-20037508.34,20037508.34]^2\),是一个 正方形,所以可以按单个 0 级瓦片进行四叉树切分。
以上,是技术前提,CesiumJS 只能支持这两种切分方案,也就是说,我国常用的其他投影方法,例如高斯投影、兰伯特投影等是不支持的,主要是形状不太满足构造四叉树瓦片。
需求是这样的,这一份 WMTS 的起切等级并不是 0 级,通过观察能力文档,我可以得出如下几个结论:
GeographicTilingScheme 方案的形状,是 1:2 的矩形EPSG:4326 的 0 级瓦片不同EPSG:4326 的 9 级瓦片相同EPSG:4326 是 2 列 × 1 行,是 4326 的 \(512^2\) 倍如果读者足够敏锐,可以得知这个 WMTS 的起切瓦片实际上几乎就是 4326 的第 9 级瓦片:
<TileMatrixLimits>
<TileMatrix>EPSG:4490:0</TileMatrix>
<MinTileRow>190</MinTileRow>
<MaxTileRow>192</MaxTileRow>
<MinTileCol>835</MinTileCol>
<MaxTileCol>838</MaxTileCol>
</TileMatrixLimits>eMatrix>
比对广东省省界数据的 4326 坐标系第 9 级瓦片阵(TileMatrix)定义:
<TileMatrixLimits>
<TileMatrix>EPSG:4326:9</TileMatrix>
<MinTileRow>183</MinTileRow>
<MaxTileRow>198</MaxTileRow>
<MinTileCol>823</MinTileCol>
<MaxTileCol>845</MaxTileCol>
</TileMatrixLimits>
最大最小行列号略有差别,是因为数据所跨的范围略有不同,前者范围较后者(广东省省界)小。
我只能说比较庆幸,这样的 WMTS 基本还是可以满足加载要求的,现有 API 可以满足加载调整。
先给代码,然后解释:
const provider = new WebMapTileServiceImageryProvider({
url: new Resource({
url: 'http://127.0.0.1/server/wmts', // 简写 url,会意即可
headers: {
'tk': 'aaaaaaaaa', // 数据保密,需要传递 token
}
}),
tileMatrixSetID: 'EPSG:4490',
format: 'image/png',
tileMatrixLabels: Object.keys(new Array(11).fill(0)).map(v => `EPSG:4490:${v}`),
layer: 'demo',
rectangle: Rectangle.fromDegrees(113.75549, 22.383494, 114.662777, 22.888641),
style: "",
tilingScheme: new GeographicTilingScheme({
numberOfLevelZeroTilesX: 1024,
numberOfLevelZeroTilesY: 512,
})
})
viewer.imageryLayers.addImageryProvider(provider)
WebMapTileServiceImageryProvider 的 url 可以是两种类型的值:string 或 Resource,这个 WMTS 数据需要在所有请求头中加上访问令牌(token),所以我选择创建一个 Resource 对象来传递 token。
这里设为 EPSG:4490,指的是能力文档中该图层(layer: 'demo')下的 <TileMatrixSetLink></TileMatrixSetLink> 的瓦片阵集ID(TileMatrixSetID):
<TileMatrixSetLink>
<TileMatrixSet>EPSG:4490</TileMatrixSet>
<TileMatrixSetLimits><!-- ... --></TileMatrixSetLimits>
</TileMatrixSetLink>
这个没什么好说的,就是每一层瓦片阵的访问标签,我这里使用 JavaScript 的数组语法糖快速生成了 11 级(0到10,共11层)瓦片阵的 ID,即:
Object.keys(new Array(11).fill(0)).map(v => `EPSG:4490:${v}`)
// ["EPSG:4490:0", "EPSG:4490:1", ..., "EPSG:4490:10"]
这样,网络请求瓦片的链接:
http://127.0.0.1/server/wmts?tilematrix=EPSG%3A4490%3A1&layer=ZT%3ATDYT&style=&tilerow=382&tilecol=1671&tilematrixset=EPSG%3A4490&format=image%2Fpng&service=WMTS&version=1.0.0&request=GetTile
中的 tilematrix 参数的值:EPSG:4490:1 就是正确的了。tileMatrixLabels 数组默认是数字 0 ~ 最大等级,如果图层在能力文档中定义的 tilematrix 的 ID 不是 0 ~ 最大等级的数字的话,就需要这样构造一个数组来告诉 CesiumJS,要以什么样的 tilematrix 发出请求。
加上这个参数,就可以最大优化 WMTS 的请求性能,如果不加这个参数限制请求范围,就会取相机视角下的所有筛选到的瓦片,这对这个例子十分有效,主要还是要加上最后一个参数:
上文已经提及,这个 WMTS 的切片方案与 4326 的是几乎一致的,只不过其 0 级相当于 4326 的 9 级。
那么,当 CesiumJS 发出第 0 级瓦片请求时,默认的 TilingScheme 是只有 1 行 2 列的瓦片的,但是在能力文档中,我注意到了一个定义:
<Contents>
<TileMatrixSet>
<ows:Identifier>EPSG:4490</ows:Identifier>
<ows:SupportedCRS>urn:ogc:def:crs:EPSG::4490</ows:SupportedCRS>
<TileMatrix>
<ows:Identifier>EPSG:4490:0</ows:Identifier>
<ScaleDenominator>545978.7734655447</ScaleDenominator>
<TopLeftCorner>90.0 -180.0</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>1024</MatrixWidth>
<MatrixHeight>512</MatrixHeight>
</TileMatrix>
<!-- ... -->
<TileMatrixSet>
</Contents>
这是这个 WMTS 服务下的 EPSG:4490 的瓦片阵集(TileMatrixSet)的第 0 个瓦片阵(TileMatrix)的定义,这里定义了几个比较重要的参数:
Identifier:瓦片阵的 IDScaleDenominator:比例(分母)TileWidth / TileHeight:该瓦片阵的瓦片的像素宽高MatrixWidth / MatrixHeight:该瓦片阵的瓦片行列数要用于前端的就是最后一个,行列数:
new GeographicTilingScheme({
numberOfLevelZeroTilesX: 1024,
numberOfLevelZeroTilesY: 512,
})
这样就能在 CesiumJS 发出第 0 级瓦片请求时,行列号能与 WMTS 的瓦片行列号对应上了,TilingScheme API 的这两个参数就是这么个用法,前提是切片的形状满足 CesiumJS 支持的这两个:GeographicTilingScheme、WebMercatorTilingScheme
通过这次实践,我又进一步学习了老旧但是又不得不用的 WMTS 规范,以及这种非标准 4326、3857 切片数据的加载细节,那就是精确地根据能力文档中各项参数(瓦片阵集的选择、瓦片阵的范围等),配合 JsAPI 控制发出准确的请求。
如果真遇上那种不满足 CesiumJS 这俩切片方案的,估计就难搞了,水平有限。
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳
我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co
我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01 客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02 数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit