区块链技术博客
www.b2bchain.cn

Cesium 加载矢量切片(MapBox Vector Tile)求职学习资料

本文介绍了Cesium 加载矢量切片(MapBox Vector Tile)求职学习资料,有助于帮助完成毕业设计以及求职,是一篇很好的资料。

对技术面试,学习经验等有一些体会,在此分享。

矢量切片(vector tile)是当前 WebGIS 较热技术,国内的高德、百度等在线地图都使用了矢量切片技术。相较于传统栅格切片,矢量切片好处很多。简单几点就是:轻量、客户端渲染、还可加密(栅格切片容易被爬取)。矢量切片格式一般有 GeoJSON、TopoJSON 、MVT (MapBox Vector Tile)、PBF。

下面是 Cesium 加载 MVT 矢量切片的代码,由于样式渲染使用的了 openlayers,所以首先需要安装 openlayers。

一、安装 openlayers

npm install openlayers

二、MVT 加载核心代码

“`javascript
// mvtProvider.js
import * as Cesium from ‘cesium’
import ol from ‘openlayers/dist/ol-debug’

export function MVTProvider (options) {
options = Cesium.defaultValue(options, Cesium.defaultValue.EMPTY_OBJECT)
this._tilingScheme = Cesium.defined(options.tilingScheme) ? options.tilingScheme : new Cesium.WebMercatorTilingScheme({ ellipsoid: options.ellipsoid })
this._tileWidth = Cesium.defaultValue(options.tileWidth, 512)
this._tileHeight = Cesium.defaultValue(options.tileHeight, 512)
this._readyPromise = Cesium.when.resolve(true)
this._ol = ol
this._mvtParser = new this._ol.format.MVT()
this._styleFun = Cesium.defined(options.styleFun) ? options.styleFun : createMapboxStreetsV6Style
this._key = Cesium.defaultValue(options.key, ”)
this._url = Cesium.defaultValue(options.url, ‘https://a.tiles.mapbox.com/v4/mapbox.mapbox-streets-v6/{z}/{x}/{y}.vector.pbf?access_token={k}’)

var sw = this._tilingScheme._rectangleSouthwestInMeters
var ne = this._tilingScheme._rectangleNortheastInMeters
var mapExtent = [sw.x, sw.y, ne.x, ne.y]
this._resolutions = ol.tilegrid.resolutionsFromExtent(mapExtent, 22, this._tileWidth)
this._pixelRatio = 1
this._transform = [0.125, 0, 0, 0.125, 0, 0]
this._replays = [‘Default’, ‘Image’, ‘Polygon’, ‘LineString’, ‘Text’]

this._tileQueue = new Cesium.TileReplacementQueue()
this._cacheSize = 1000
}

Object.defineProperties(MVTProvider.prototype, {
proxy: {
get () {
return undefined
}
},

tileWidth: {
get () {
return this._tileWidth
}
},

tileHeight: {
get () {
return this._tileHeight
}
},

maximumLevel: {
get () {
return undefined
}
},

minimumLevel: {
get () {
return undefined
}
},

tilingScheme: {
get () {
return this._tilingScheme
}
},

rectangle: {
get () {
return this._tilingScheme.rectangle
}
},

tileDiscardPolicy: {
get () {
return undefined
}
},

errorEvent: {
get () {
return this._errorEvent
}
},

ready: {
get () {
return true
}
},

readyPromise: {
get () {
return this._readyPromise
}
},

credit: {
get () {
return undefined
}
},

hasAlphaChannel: {
get () {
return true
}
}
})

MVTProvider.prototype.getTileCredits = function () {
return undefined
}

function findTileInQueue (x, y, level, tileQueue) {
var item = tileQueue.head
while (item !== undefined && !(item.xMvt === x && item.yMvt === y && item.zMvt === level)) {
item = item.replacementNext
}
return item
}

function remove (tileReplacementQueue, item) {
var previous = item.replacementPrevious
var next = item.replacementNext

if (item === tileReplacementQueue._lastBeforeStartOfFrame) {
tileReplacementQueue._lastBeforeStartOfFrame = next
}

if (item === tileReplacementQueue.head) {
tileReplacementQueue.head = next
} else {
previous.replacementNext = next
}

if (item === tileReplacementQueue.tail) {
tileReplacementQueue.tail = previous
} else {
next.replacementPrevious = previous
}

item.replacementPrevious = undefined
item.replacementNext = undefined

–tileReplacementQueue.count
}

function trimTiles (tileQueue, maximumTiles) {
var tileToTrim = tileQueue.tail
while (tileQueue.count > maximumTiles &&
Cesium.defined(tileToTrim)) {
var previous = tileToTrim.replacementPrevious
remove(tileQueue, tileToTrim)
tileToTrim = null
tileToTrim = previous
}
}

MVTProvider.prototype.requestImage = function (x, y, level) {
var cacheTile = findTileInQueue(x, y, level, this._tileQueue)
if (cacheTile !== undefined) {
return cacheTile
}

var that = this
var url = this._url
url = url.replace(‘{x}’, x).replace(‘{y}’, y).replace(‘{z}’, level).replace(‘{k}’, this._key);
(function (x, y, z) {
var resource = Cesium.Resource.createIfNeeded(url)
return resource.fetchArrayBuffer().then((arrayBuffer) => {
var canvas = document.createElement(‘canvas’)
canvas.width = 512
canvas.height = 512
var vectorContext = canvas.getContext(‘2d’)

  var features = that._mvtParser.readFeatures(arrayBuffer)    var styleFun = that._styleFun()    var extent = [0, 0, 4096, 4096]   var _replayGroup = new ol.render.canvas.ReplayGroup(0, extent, 8, true, 100)   for (var i = 0; i < features.length; i++) {     var feature = features[i]     var styles = styleFun(features[i], that._resolutions[level])     for (var j = 0; j < styles.length; j++) {       ol.renderer.vector.renderFeature_(_replayGroup, feature, styles[j], 16)     }   }   _replayGroup.finish()    _replayGroup.replay(vectorContext, that._pixelRatio, that._transform, 0, {}, that._replays, true)   if (that._tileQueue.count > that._cacheSize) {     trimTiles(that._tileQueue, that._cacheSize / 2)   }    canvas.xMvt = x   canvas.yMvt = y   canvas.zMvt = z   that._tileQueue.markTileRendered(canvas)   _replayGroup = null   return canvas }).otherwise(() => { })

}(x, y, level))
}

MVTProvider.prototype.pickFeatures = function () {
return undefined
}

function createMapboxStreetsV6Style () {
var fill = new ol.style.Fill({ color: ” })
var stroke = new ol.style.Stroke({ color: ”, width: 1 })
var polygon = new ol.style.Style({ fill })
var strokedPolygon = new ol.style.Style({ fill, stroke })
var line = new ol.style.Style({ stroke })
var text = new ol.style.Style({
text: new ol.style.Text({
text: ”, fill, stroke
})
})
var iconCache = {}
function getIcon (iconName) {
var icon = iconCache[iconName]
if (!icon) {
icon = new ol.style.Style({
image: new ol.style.Icon({
src: https://cdn.rawgit.com/mapbox/maki/master/icons/${iconName}-15.svg,
imgSize: [15, 15]
})
})
iconCache[iconName] = icon
}
return icon
}
var styles = []
return function (feature, resolution) {
var length = 0
var layer = feature.get(‘layer’)
var cls = feature.get(‘class’)
var type = feature.get(‘type’)
var scalerank = feature.get(‘scalerank’)
var labelrank = feature.get(‘labelrank’)
var adminLevel = feature.get(‘admin_level’)
var maritime = feature.get(‘maritime’)
var disputed = feature.get(‘disputed’)
var maki = feature.get(‘maki’)
var geom = feature.getGeometry().getType()
if (layer === ‘landuse’ && cls === ‘park’) {
fill.setColor(‘#d8e8c8’)
styles[length++] = polygon
} else if (layer === ‘landuse’ && cls === ‘cemetery’) {
fill.setColor(‘#e0e4dd’)
styles[length++] = polygon
} else if (layer === ‘landuse’ && cls === ‘hospital’) {
fill.setColor(‘#fde’)
styles[length++] = polygon
} else if (layer === ‘landuse’ && cls === ‘school’) {
fill.setColor(‘#f0e8f8’)
styles[length++] = polygon
} else if (layer === ‘landuse’ && cls === ‘wood’) {
fill.setColor(‘rgb(233,238,223)’)
styles[length++] = polygon
} else if (layer === ‘waterway’ &&
cls !== ‘river’ && cls !== ‘stream’ && cls !== ‘canal’) {
stroke.setColor(‘#a0c8f0’)

矢量切片(vector tile)是当前 WebGIS 较热技术,国内的高德、百度等在线地图都使用了矢量切片技术。相较于传统栅格切片,矢量切片好处很多。简单几点就是:轻量、客户端渲染、还可加密(栅格切片容易被爬取)。矢量切片格式一般有 GeoJSON、TopoJSON 、MVT (MapBox Vector Tile)、PBF。

下面是 Cesium 加载 MVT 矢量切片的代码,由于样式渲染使用的了 openlayers,所以首先需要安装 openlayers。

一、安装 openlayers

npm install openlayers

二、MVT 加载核心代码

“`javascript
// mvtProvider.js
import * as Cesium from ‘cesium’
import ol from ‘openlayers/dist/ol-debug’

export function MVTProvider (options) {
options = Cesium.defaultValue(options, Cesium.defaultValue.EMPTY_OBJECT)
this._tilingScheme = Cesium.defined(options.tilingScheme) ? options.tilingScheme : new Cesium.WebMercatorTilingScheme({ ellipsoid: options.ellipsoid })
this._tileWidth = Cesium.defaultValue(options.tileWidth, 512)
this._tileHeight = Cesium.defaultValue(options.tileHeight, 512)
this._readyPromise = Cesium.when.resolve(true)
this._ol = ol
this._mvtParser = new this._ol.format.MVT()
this._styleFun = Cesium.defined(options.styleFun) ? options.styleFun : createMapboxStreetsV6Style
this._key = Cesium.defaultValue(options.key, ”)
this._url = Cesium.defaultValue(options.url, ‘https://a.tiles.mapbox.com/v4/mapbox.mapbox-streets-v6/{z}/{x}/{y}.vector.pbf?access_token={k}’)

var sw = this._tilingScheme._rectangleSouthwestInMeters
var ne = this._tilingScheme._rectangleNortheastInMeters
var mapExtent = [sw.x, sw.y, ne.x, ne.y]
this._resolutions = ol.tilegrid.resolutionsFromExtent(mapExtent, 22, this._tileWidth)
this._pixelRatio = 1
this._transform = [0.125, 0, 0, 0.125, 0, 0]
this._replays = [‘Default’, ‘Image’, ‘Polygon’, ‘LineString’, ‘Text’]

this._tileQueue = new Cesium.TileReplacementQueue()
this._cacheSize = 1000
}

Object.defineProperties(MVTProvider.prototype, {
proxy: {
get () {
return undefined
}
},

tileWidth: {
get () {
return this._tileWidth
}
},

tileHeight: {
get () {
return this._tileHeight
}
},

maximumLevel: {
get () {
return undefined
}
},

minimumLevel: {
get () {
return undefined
}
},

tilingScheme: {
get () {
return this._tilingScheme
}
},

rectangle: {
get () {
return this._tilingScheme.rectangle
}
},

tileDiscardPolicy: {
get () {
return undefined
}
},

errorEvent: {
get () {
return this._errorEvent
}
},

ready: {
get () {
return true
}
},

readyPromise: {
get () {
return this._readyPromise
}
},

credit: {
get () {
return undefined
}
},

hasAlphaChannel: {
get () {
return true
}
}
})

MVTProvider.prototype.getTileCredits = function () {
return undefined
}

function findTileInQueue (x, y, level, tileQueue) {
var item = tileQueue.head
while (item !== undefined && !(item.xMvt === x && item.yMvt === y && item.zMvt === level)) {
item = item.replacementNext
}
return item
}

function remove (tileReplacementQueue, item) {
var previous = item.replacementPrevious
var next = item.replacementNext

if (item === tileReplacementQueue._lastBeforeStartOfFrame) {
tileReplacementQueue._lastBeforeStartOfFrame = next
}

if (item === tileReplacementQueue.head) {
tileReplacementQueue.head = next
} else {
previous.replacementNext = next
}

if (item === tileReplacementQueue.tail) {
tileReplacementQueue.tail = previous
} else {
next.replacementPrevious = previous
}

item.replacementPrevious = undefined
item.replacementNext = undefined

–tileReplacementQueue.count
}

function trimTiles (tileQueue, maximumTiles) {
var tileToTrim = tileQueue.tail
while (tileQueue.count > maximumTiles &&
Cesium.defined(tileToTrim)) {
var previous = tileToTrim.replacementPrevious
remove(tileQueue, tileToTrim)
tileToTrim = null
tileToTrim = previous
}
}

MVTProvider.prototype.requestImage = function (x, y, level) {
var cacheTile = findTileInQueue(x, y, level, this._tileQueue)
if (cacheTile !== undefined) {
return cacheTile
}

var that = this
var url = this._url
url = url.replace(‘{x}’, x).replace(‘{y}’, y).replace(‘{z}’, level).replace(‘{k}’, this._key);
(function (x, y, z) {
var resource = Cesium.Resource.createIfNeeded(url)
return resource.fetchArrayBuffer().then((arrayBuffer) => {
var canvas = document.createElement(‘canvas’)
canvas.width = 512
canvas.height = 512
var vectorContext = canvas.getContext(‘2d’)

  var features = that._mvtParser.readFeatures(arrayBuffer)    var styleFun = that._styleFun()    var extent = [0, 0, 4096, 4096]   var _replayGroup = new ol.render.canvas.ReplayGroup(0, extent, 8, true, 100)   for (var i = 0; i < features.length; i++) {     var feature = features[i]     var styles = styleFun(features[i], that._resolutions[level])     for (var j = 0; j < styles.length; j++) {       ol.renderer.vector.renderFeature_(_replayGroup, feature, styles[j], 16)     }   }   _replayGroup.finish()    _replayGroup.replay(vectorContext, that._pixelRatio, that._transform, 0, {}, that._replays, true)   if (that._tileQueue.count > that._cacheSize) {     trimTiles(that._tileQueue, that._cacheSize / 2)   }    canvas.xMvt = x   canvas.yMvt = y   canvas.zMvt = z   that._tileQueue.markTileRendered(canvas)   _replayGroup = null   return canvas }).otherwise(() => { })

}(x, y, level))
}

MVTProvider.prototype.pickFeatures = function () {
return undefined
}

function createMapboxStreetsV6Style () {
var fill = new ol.style.Fill({ color: ” })
var stroke = new ol.style.Stroke({ color: ”, width: 1 })
var polygon = new ol.style.Style({ fill })
var strokedPolygon = new ol.style.Style({ fill, stroke })
var line = new ol.style.Style({ stroke })
var text = new ol.style.Style({
text: new ol.style.Text({
text: ”, fill, stroke
})
})
var iconCache = {}
function getIcon (iconName) {
var icon = iconCache[iconName]
if (!icon) {
icon = new ol.style.Style({
image: new ol.style.Icon({
src: https://cdn.rawgit.com/mapbox/maki/master/icons/${iconName}-15.svg,
imgSize: [15, 15]
})
})
iconCache[iconName] = icon
}
return icon
}
var styles = []
return function (feature, resolution) {
var length = 0
var layer = feature.get(‘layer’)
var cls = feature.get(‘class’)
var type = feature.get(‘type’)
var scalerank = feature.get(‘scalerank’)
var labelrank = feature.get(‘labelrank’)
var adminLevel = feature.get(‘admin_level’)
var maritime = feature.get(‘maritime’)
var disputed = feature.get(‘disputed’)
var maki = feature.get(‘maki’)
var geom = feature.getGeometry().getType()
if (layer === ‘landuse’ && cls === ‘park’) {
fill.setColor(‘#d8e8c8’)
styles[length++] = polygon
} else if (layer === ‘landuse’ && cls === ‘cemetery’) {
fill.setColor(‘#e0e4dd’)
styles[length++] = polygon
} else if (layer === ‘landuse’ && cls === ‘hospital’) {
fill.setColor(‘#fde’)
styles[length++] = polygon
} else if (layer === ‘landuse’ && cls === ‘school’) {
fill.setColor(‘#f0e8f8’)
styles[length++] = polygon
} else if (layer === ‘landuse’ && cls === ‘wood’) {
fill.setColor(‘rgb(233,238,223)’)
styles[length++] = polygon
} else if (layer === ‘waterway’ &&
cls !== ‘river’ && cls !== ‘stream’ && cls !== ‘canal’) {
stroke.setColor(‘#a0c8f0’)

矢量切片(vector tile)是当前 WebGIS 较热技术,国内的高德、百度等在线地图都使用了矢量切片技术。相较于传统栅格切片,矢量切片好处很多。简单几点就是:轻量、客户端渲染、还可加密(栅格切片容易被爬取)。矢量切片格式一般有 GeoJSON、TopoJSON 、MVT (MapBox Vector Tile)、PBF。

下面是 Cesium 加载 MVT 矢量切片的代码,由于样式渲染使用的了 openlayers,所以首先需要安装 openlayers。

一、安装 openlayers

npm install openlayers

二、MVT 加载核心代码

“`javascript
// mvtProvider.js
import * as Cesium from ‘cesium’
import ol from ‘openlayers/dist/ol-debug’

export function MVTProvider (options) {
options = Cesium.defaultValue(options, Cesium.defaultValue.EMPTY_OBJECT)
this._tilingScheme = Cesium.defined(options.tilingScheme) ? options.tilingScheme : new Cesium.WebMercatorTilingScheme({ ellipsoid: options.ellipsoid })
this._tileWidth = Cesium.defaultValue(options.tileWidth, 512)
this._tileHeight = Cesium.defaultValue(options.tileHeight, 512)
this._readyPromise = Cesium.when.resolve(true)
this._ol = ol
this._mvtParser = new this._ol.format.MVT()
this._styleFun = Cesium.defined(options.styleFun) ? options.styleFun : createMapboxStreetsV6Style
this._key = Cesium.defaultValue(options.key, ”)
this._url = Cesium.defaultValue(options.url, ‘https://a.tiles.mapbox.com/v4/mapbox.mapbox-streets-v6/{z}/{x}/{y}.vector.pbf?access_token={k}’)

var sw = this._tilingScheme._rectangleSouthwestInMeters
var ne = this._tilingScheme._rectangleNortheastInMeters
var mapExtent = [sw.x, sw.y, ne.x, ne.y]
this._resolutions = ol.tilegrid.resolutionsFromExtent(mapExtent, 22, this._tileWidth)
this._pixelRatio = 1
this._transform = [0.125, 0, 0, 0.125, 0, 0]
this._replays = [‘Default’, ‘Image’, ‘Polygon’, ‘LineString’, ‘Text’]

this._tileQueue = new Cesium.TileReplacementQueue()
this._cacheSize = 1000
}

Object.defineProperties(MVTProvider.prototype, {
proxy: {
get () {
return undefined
}
},

tileWidth: {
get () {
return this._tileWidth
}
},

tileHeight: {
get () {
return this._tileHeight
}
},

maximumLevel: {
get () {
return undefined
}
},

minimumLevel: {
get () {
return undefined
}
},

tilingScheme: {
get () {
return this._tilingScheme
}
},

rectangle: {
get () {
return this._tilingScheme.rectangle
}
},

tileDiscardPolicy: {
get () {
return undefined
}
},

errorEvent: {
get () {
return this._errorEvent
}
},

ready: {
get () {
return true
}
},

readyPromise: {
get () {
return this._readyPromise
}
},

credit: {
get () {
return undefined
}
},

hasAlphaChannel: {
get () {
return true
}
}
})

MVTProvider.prototype.getTileCredits = function () {
return undefined
}

function findTileInQueue (x, y, level, tileQueue) {
var item = tileQueue.head
while (item !== undefined && !(item.xMvt === x && item.yMvt === y && item.zMvt === level)) {
item = item.replacementNext
}
return item
}

function remove (tileReplacementQueue, item) {
var previous = item.replacementPrevious
var next = item.replacementNext

if (item === tileReplacementQueue._lastBeforeStartOfFrame) {
tileReplacementQueue._lastBeforeStartOfFrame = next
}

if (item === tileReplacementQueue.head) {
tileReplacementQueue.head = next
} else {
previous.replacementNext = next
}

if (item === tileReplacementQueue.tail) {
tileReplacementQueue.tail = previous
} else {
next.replacementPrevious = previous
}

item.replacementPrevious = undefined
item.replacementNext = undefined

–tileReplacementQueue.count
}

function trimTiles (tileQueue, maximumTiles) {
var tileToTrim = tileQueue.tail
while (tileQueue.count > maximumTiles &&
Cesium.defined(tileToTrim)) {
var previous = tileToTrim.replacementPrevious
remove(tileQueue, tileToTrim)
tileToTrim = null
tileToTrim = previous
}
}

MVTProvider.prototype.requestImage = function (x, y, level) {
var cacheTile = findTileInQueue(x, y, level, this._tileQueue)
if (cacheTile !== undefined) {
return cacheTile
}

var that = this
var url = this._url
url = url.replace(‘{x}’, x).replace(‘{y}’, y).replace(‘{z}’, level).replace(‘{k}’, this._key);
(function (x, y, z) {
var resource = Cesium.Resource.createIfNeeded(url)
return resource.fetchArrayBuffer().then((arrayBuffer) => {
var canvas = document.createElement(‘canvas’)
canvas.width = 512
canvas.height = 512
var vectorContext = canvas.getContext(‘2d’)

  var features = that._mvtParser.readFeatures(arrayBuffer)    var styleFun = that._styleFun()    var extent = [0, 0, 4096, 4096]   var _replayGroup = new ol.render.canvas.ReplayGroup(0, extent, 8, true, 100)   for (var i = 0; i < features.length; i++) {     var feature = features[i]     var styles = styleFun(features[i], that._resolutions[level])     for (var j = 0; j < styles.length; j++) {       ol.renderer.vector.renderFeature_(_replayGroup, feature, styles[j], 16)     }   }   _replayGroup.finish()    _replayGroup.replay(vectorContext, that._pixelRatio, that._transform, 0, {}, that._replays, true)   if (that._tileQueue.count > that._cacheSize) {     trimTiles(that._tileQueue, that._cacheSize / 2)   }    canvas.xMvt = x   canvas.yMvt = y   canvas.zMvt = z   that._tileQueue.markTileRendered(canvas)   _replayGroup = null   return canvas }).otherwise(() => { })

}(x, y, level))
}

MVTProvider.prototype.pickFeatures = function () {
return undefined
}

function createMapboxStreetsV6Style () {
var fill = new ol.style.Fill({ color: ” })
var stroke = new ol.style.Stroke({ color: ”, width: 1 })
var polygon = new ol.style.Style({ fill })
var strokedPolygon = new ol.style.Style({ fill, stroke })
var line = new ol.style.Style({ stroke })
var text = new ol.style.Style({
text: new ol.style.Text({
text: ”, fill, stroke
})
})
var iconCache = {}
function getIcon (iconName) {
var icon = iconCache[iconName]
if (!icon) {
icon = new ol.style.Style({
image: new ol.style.Icon({
src: https://cdn.rawgit.com/mapbox/maki/master/icons/${iconName}-15.svg,
imgSize: [15, 15]
})
})
iconCache[iconName] = icon
}
return icon
}
var styles = []
return function (feature, resolution) {
var length = 0
var layer = feature.get(‘layer’)
var cls = feature.get(‘class’)
var type = feature.get(‘type’)
var scalerank = feature.get(‘scalerank’)
var labelrank = feature.get(‘labelrank’)
var adminLevel = feature.get(‘admin_level’)
var maritime = feature.get(‘maritime’)
var disputed = feature.get(‘disputed’)
var maki = feature.get(‘maki’)
var geom = feature.getGeometry().getType()
if (layer === ‘landuse’ && cls === ‘park’) {
fill.setColor(‘#d8e8c8’)
styles[length++] = polygon
} else if (layer === ‘landuse’ && cls === ‘cemetery’) {
fill.setColor(‘#e0e4dd’)
styles[length++] = polygon
} else if (layer === ‘landuse’ && cls === ‘hospital’) {
fill.setColor(‘#fde’)
styles[length++] = polygon
} else if (layer === ‘landuse’ && cls === ‘school’) {
fill.setColor(‘#f0e8f8’)
styles[length++] = polygon
} else if (layer === ‘landuse’ && cls === ‘wood’) {
fill.setColor(‘rgb(233,238,223)’)
styles[length++] = polygon
} else if (layer === ‘waterway’ &&
cls !== ‘river’ && cls !== ‘stream’ && cls !== ‘canal’) {
stroke.setColor(‘#a0c8f0’)

部分转自互联网,侵权删除联系

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » Cesium 加载矢量切片(MapBox Vector Tile)求职学习资料
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们