园林绿化
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

200 lines
6.8 KiB

  1. // import * as mars3d from "mars3d"
  2. // const Cesium = mars3d.Cesium
  3. //按mars3d规范,封装的pbf图层
  4. ;(function (window) {
  5. let BasicRenderer = window.mapboxRenderer.BasicRenderer
  6. class MVTImageryProvider {
  7. /**
  8. *
  9. * @param {Object} options
  10. * @param {Object} options.style - mapbox style object
  11. * @param {Function} [options.sourceFilter] - sourceFilter is used to filter which source participate in pickFeature process.
  12. * @param {Number} [options.maximumLevel] - if cesium zoom level exceeds maximumLevel, layer will be invisible.
  13. * @param {Number} [options.minimumLevel] - if cesium zoom level belows minimumLevel, layer will be invisible.
  14. * @param {Number} [options.tileSize=512] - can be 256 or 512.
  15. * @param {Boolean} [options.hasAlphaChannel] -
  16. * @param {String} [options.credit] -
  17. *
  18. */
  19. constructor(options) {
  20. this.mapboxRenderer = new BasicRenderer({ style: options.style })
  21. this.ready = false
  22. this.readyPromise = this.mapboxRenderer._style.loadedPromise.then(() => (this.ready = true))
  23. this.tilingScheme = new Cesium.WebMercatorTilingScheme()
  24. this.rectangle = this.tilingScheme.rectangle
  25. this.tileSize = this.tileWidth = this.tileHeight = options.tileSize || 512
  26. this.maximumLevel = options.maximumLevel || Number.MAX_SAFE_INTEGER
  27. this.minimumLevel = options.minimumLevel || 0
  28. this.tileDiscardPolicy = undefined
  29. this.errorEvent = new Cesium.Event()
  30. this.credit = new Cesium.Credit(options.credit || "", false)
  31. this.proxy = new Cesium.DefaultProxy("")
  32. this.hasAlphaChannel = options.hasAlphaChannel !== undefined ? options.hasAlphaChannel : true
  33. this.sourceFilter = options.sourceFilter
  34. }
  35. getTileCredits(x, y, level) {
  36. return []
  37. }
  38. createTile() {
  39. let canv = document.createElement("canvas")
  40. canv.width = this.tileSize
  41. canv.height = this.tileSize
  42. canv.style.imageRendering = "pixelated"
  43. canv.getContext("2d").globalCompositeOperation = "copy"
  44. return canv
  45. }
  46. _getTilesSpec(coord, source) {
  47. const { x, y, zoom } = coord
  48. const TILE_SIZE = this.tileSize
  49. // 3x3 grid of source tiles, where the region of interest is that corresponding to the central source tile
  50. let ret = []
  51. const maxTile = (1 << zoom) - 1
  52. for (let xx = -1; xx <= 1; xx++) {
  53. let newx = x + xx
  54. if (newx < 0) newx = maxTile
  55. if (newx > maxTile) newx = 0
  56. for (let yy = -1; yy <= 1; yy++) {
  57. let newy = y + yy
  58. if (newy < 0) continue
  59. if (newy > maxTile) continue
  60. ret.push({
  61. source: source,
  62. z: zoom,
  63. x: newx,
  64. y: newy,
  65. left: 0 + xx * TILE_SIZE,
  66. top: 0 + yy * TILE_SIZE,
  67. size: TILE_SIZE
  68. })
  69. }
  70. }
  71. return ret
  72. }
  73. requestImage(x, y, zoom, releaseTile = true) {
  74. if (zoom > this.maximumLevel || zoom < this.minimumLevel) return Promise.reject(undefined)
  75. this.mapboxRenderer.filterForZoom(zoom)
  76. const tilesSpec = this.mapboxRenderer.getVisibleSources().reduce((a, s) => a.concat(this._getTilesSpec({ x, y, zoom }, s)), [])
  77. return new Promise((resolve, reject) => {
  78. const canv = this.createTile()
  79. const ctx = canv.getContext("2d")
  80. const renderRef = this.mapboxRenderer.renderTiles(
  81. ctx,
  82. {
  83. srcLeft: 0,
  84. srcTop: 0,
  85. width: this.tileSize,
  86. height: this.tileSize,
  87. destLeft: 0,
  88. destTop: 0
  89. },
  90. tilesSpec,
  91. (err) => {
  92. /**
  93. * In case of err ends with 'tiles not available', the canvas will still be painted.
  94. * relate url: https://github.com/landtechnologies/Mapbox-vector-tiles-basic-js-renderer/blob/master/src/basic/renderer.js#L341-L405
  95. */
  96. if (typeof err === "string" && !err.endsWith("tiles not available")) {
  97. reject(undefined)
  98. } else if (releaseTile) {
  99. renderRef.consumer.ctx = undefined
  100. resolve(canv)
  101. // releaseTile默认为true,对应Cesium请求图像的情形
  102. this.mapboxRenderer.releaseRender(renderRef)
  103. } else {
  104. // releaseTile为false时在由pickFeature手动调用,在渲染完成之后在pickFeature里边手动释放tile
  105. resolve(renderRef)
  106. }
  107. }
  108. )
  109. })
  110. }
  111. pickFeatures(x, y, zoom, longitude, latitude) {
  112. return this.requestImage(x, y, zoom, false).then((renderRef) => {
  113. let targetSources = this.mapboxRenderer.getVisibleSources()
  114. targetSources = this.sourceFilter ? this.sourceFilter(targetSources) : targetSources
  115. const queryResult = []
  116. longitude = Cesium.Math.toDegrees(longitude)
  117. latitude = Cesium.Math.toDegrees(latitude)
  118. targetSources.forEach((s) => {
  119. let data = this.mapboxRenderer.queryRenderedFeatures({
  120. source: s,
  121. renderedZoom: zoom,
  122. lng: longitude,
  123. lat: latitude,
  124. tileZ: zoom
  125. })
  126. for (let key in data) {
  127. let item = data[key]
  128. for (let index = 0; index < item.length; index++) {
  129. const element = item[index]
  130. element.layer = key
  131. queryResult.push({ properties: element })
  132. }
  133. }
  134. })
  135. // release tile
  136. renderRef.consumer.ctx = undefined
  137. this.mapboxRenderer.releaseRender(renderRef)
  138. return queryResult
  139. })
  140. }
  141. destroy() {
  142. this.mapboxRenderer._cancelAllPendingRenders()
  143. Object.values(this.mapboxRenderer._style.sourceCaches).forEach((cache) => cache._tileCache.reset())
  144. this.mapboxRenderer._gl.getExtension("WEBGL_lose_context").loseContext()
  145. }
  146. }
  147. class PbfLayer extends mars3d.layer.BaseTileLayer {
  148. //构建ImageryProvider
  149. _createImageryProvider(options) {
  150. return createImageryProvider(options)
  151. }
  152. }
  153. function createImageryProvider(options) {
  154. return new Promise((resolve, reject) => {
  155. if (options.url) {
  156. Cesium.Resource.fetchJson(options).then((data) => {
  157. const provider = new MVTImageryProvider({ ...options, style: data })
  158. provider.readyPromise.then(() => {
  159. resolve(provider)
  160. })
  161. })
  162. } else {
  163. const provider = new MVTImageryProvider(options)
  164. provider.readyPromise.then(() => {
  165. resolve(provider)
  166. })
  167. }
  168. })
  169. }
  170. PbfLayer.createImageryProvider = createImageryProvider
  171. //注册下
  172. const layerType = "pbf" //图层类型
  173. mars3d.LayerUtil.register(layerType, PbfLayer)
  174. mars3d.LayerUtil.registerImageryProvider(layerType, createImageryProvider)
  175. // 对外接口
  176. window.mars3d.layer.PbfLayer = PbfLayer
  177. })(window)
  178. // export { PbfLayer }