diff --git a/demos/01_point_circle.html b/demos/01_point_circle.html index 57c8ef8dc7..f63a295ae3 100644 --- a/demos/01_point_circle.html +++ b/demos/01_point_circle.html @@ -59,11 +59,11 @@ scene.on('loaded', () => { .source(data,{ isCluster:true }) - .shape('hexagon') + .shape('circle') .size('point_count', [ 5, 40]) // default 1 //.size('value', [ 10, 300]) // default 1 .active(true) - .color('point_count',colorObj.blue) + .color('point_count',["#002466","#105CB3","#2894E0","#CFF6FF","#FFF5B8","#FFAB5C","#F27049","#730D1C"]) .style({ stroke: 'rgb(255,255,255)', strokeWidth: 1, diff --git a/demos/tile.html b/demos/tile.html index f0867c42a7..b3ac0aaae2 100644 --- a/demos/tile.html +++ b/demos/tile.html @@ -25,50 +25,36 @@ const scene = new L7.Scene({ id: 'map', - mapStyle: 'dark', // 样式URL + mapStyle: 'light', // 样式URL center: [104.838088,34.075889 ], pitch: 0, - zoom: 4.5, + hash:true, + zoom: 3, }); window.scene = scene; scene.on('loaded', () => { - // https://gw.alipayobjects.com/os/basement_prod/24883cde-3352-4e53-af52-f6e59d4fe2c8.json - //tile https://gw.alipayobjects.com/os/basement_prod/c400bd4e-5b46-4769-b969-c1f09feaf908.json - $.getJSON('https://gw.alipayobjects.com/os/basement_prod/c400bd4e-5b46-4769-b969-c1f09feaf908.json', city => { - city.type = "FeatureCollection"; - city.features = city.features.map((item)=>{ - return { - type: "Feature", - properties:item.tags, - "geometry":{ - "type": "Polygon", - "coordinates":item.geometry - } - } - }) - - console.log(city.features[0]); - const citylayer = scene.PolygonLayer() + scene.ImageTileLayer({ + zIndex:0 + }) + .source('http://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}') + .render(); + + $.getJSON('https://gw.alipayobjects.com/os/rmsportal/JToMOWvicvJOISZFCkEI.json', city => { + const citylayer = scene.PolygonLayer( + { + zIndex:4 + } + ) .source(city) - .color('Code',["#FFF5B8","#FFDC7D","#FFAB5C","#F27049","#D42F31","#730D1C"]) + .color('pm2_5_24h',["#FFF5B8","#FFDC7D","#FFAB5C","#F27049","#D42F31","#730D1C"]) .shape('fill') - .active(true) .style({ - opacity: 1 + opacity: 1.0 }) .render(); - console.log(citylayer); - /** - const citylayer2 = scene.PolygonLayer() - .source(city) - .shape('line') - .color('#fff') - .style({ - opacity: 1.0 - }) - .render(); - **/ + + }); }); diff --git a/package.json b/package.json index dcca835f42..43486efeb6 100755 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "prepublishOnly": "npm run build-lib && npm run dist", "screenshot": "node ./bin/screenshot.js", "start": "npm run dev", - "test": "torch --compile-opts ./.torch.compile.opts.js --compile --renderer --recursive test/unit", + "test": "torch --compile-opts ./.torch.compile.opts.js --compile --renderer --recursive test/unit", "test-all": "npm run test && npm run test-bugs", "test-bugs": "torch --compile --renderer --recursive test/bugs", "test-bugs-live": "torch --compile --interactive --watch --recursive test/bugs", @@ -96,6 +96,7 @@ }, "dependencies": { "@antv/g": "^3.1.3", + "@antv/geo-coord": "^1.0.8", "@antv/util": "~2.0.1", "@mapbox/tiny-sdf": "^1.1.0", "@turf/bbox": "^6.0.1", diff --git a/src/core/engine/picking/picking.js b/src/core/engine/picking/picking.js index 6d6ba60de1..3d61d86427 100755 --- a/src/core/engine/picking/picking.js +++ b/src/core/engine/picking/picking.js @@ -87,21 +87,11 @@ class Picking { id = -999; // return; } - this._raycaster.setFromCamera(normalisedPoint, this._camera); - - const intersects = this._raycaster.intersectObjects(this._pickingScene.children, true); const _point2d = { x: point.x, y: point.y }; - - let _point3d; - if (intersects.length > 0) { - _point3d = intersects[0].point; - } const item = { layerId, featureId: id, - point2d: _point2d, - point3d: _point3d, - intersects + point2d: _point2d }; return item; diff --git a/src/core/layer.js b/src/core/layer.js index 507729b617..9523aa21eb 100644 --- a/src/core/layer.js +++ b/src/core/layer.js @@ -679,6 +679,8 @@ export default class Layer extends Base { } else if (this.type === 'polyline') { offset = 2; + } else if (this.type === 'polygon') { + offset = 1; } this._object3D.position && (this._object3D.position.z = offset * Math.pow(2, 20 - zoom)); if (zoom < minZoom || zoom > maxZoom) { diff --git a/src/geo/buffer.js b/src/geo/lnglat.js similarity index 100% rename from src/geo/buffer.js rename to src/geo/lnglat.js diff --git a/src/geo/point.js b/src/geo/point.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/geom/buffer/image.js b/src/geom/buffer/image.js index 262151b149..5c69a4abf5 100644 --- a/src/geom/buffer/image.js +++ b/src/geom/buffer/image.js @@ -23,7 +23,7 @@ export default class ImageBuffer extends BufferBase { const uv = [ 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0 ]; const texture = new THREE.Texture(image); texture.magFilter = THREE.LinearFilter; - texture.minFilter = THREE.LinearFilter; + texture.minFilter = THREE.LinearMipMapLinearFilter; texture.needsUpdate = true; const attributes = { vertices: new Float32Array(positions), diff --git a/src/geom/extrude.js b/src/geom/extrude.js index 3b3375b798..8ae4aef5c1 100644 --- a/src/geom/extrude.js +++ b/src/geom/extrude.js @@ -19,18 +19,18 @@ export default function extrudePolygon(points, extrude) { const flattengeo = earcut.flatten(points); const positions = []; let cells = []; - + const { dimensions } = flattengeo; const triangles = earcut(flattengeo.vertices, flattengeo.holes, flattengeo.dimensions); cells = triangles; - const pointCount = flattengeo.vertices.length / 3; + const pointCount = flattengeo.vertices.length / dimensions; const { vertices } = flattengeo; extrude ? full() : flat(); function flat() { for (let i = 0; i < pointCount; i++) { - positions.push([ vertices[ i * 3 ], vertices[i * 3 + 1 ], 0 ]); + positions.push([ vertices[ i * dimensions ], vertices[i * dimensions + 1 ], 0 ]); } } function full() { @@ -41,10 +41,10 @@ export default function extrudePolygon(points, extrude) { // 顶部坐标 for (let i = 0; i < pointCount; i++) { - positions.push([ vertices[ i * 3 ], vertices[i * 3 + 1 ], 1 ]); + positions.push([ vertices[ i * dimensions ], vertices[i * dimensions + 1 ], 1 ]); } for (let i = 0; i < pointCount; i++) { - positions.push([ vertices[ i * 3 ], vertices[i * 3 + 1 ], 0 ]); + positions.push([ vertices[ i * dimensions ], vertices[i * dimensions + 1 ], 0 ]); } for (let i = 0; i < n; i++) { if (i === (n - 1)) { diff --git a/src/geom/material/imageMaterial.js b/src/geom/material/imageMaterial.js index d193576d7b..ecf950f498 100644 --- a/src/geom/material/imageMaterial.js +++ b/src/geom/material/imageMaterial.js @@ -9,7 +9,8 @@ export default function ImageMaterial(options) { }, vertexShader: vs, fragmentShader: fs, - transparent: true + transparent: true, + depthTest: false }); return material; } diff --git a/src/geom/shader/index.js b/src/geom/shader/index.js index c884b87690..0962214754 100644 --- a/src/geom/shader/index.js +++ b/src/geom/shader/index.js @@ -41,6 +41,10 @@ import image_frag from '../shader/image_frag.glsl'; import raster_vert from '../shader/raster_vert.glsl'; import raster_frag from '../shader/raster_frag.glsl'; +// tile +import tile_polygon_vert from '../shader/tile/polygon_vert.glsl'; +import tile_polygon_frag from '../shader/tile/polygon_frag.glsl'; + import common from './common.glsl'; import { registerModule } from '../../util/shaderModule'; import pick_color from './shaderChunks/pick_color.glsl'; @@ -60,5 +64,6 @@ export function compileBuiltinModules() { registerModule('text', { vs: text_vert, fs: text_frag }); registerModule('image', { vs: image_vert, fs: image_frag }); registerModule('raster', { vs: raster_vert, fs: raster_frag }); + registerModule('tilepolygon', { vs: tile_polygon_vert, fs: tile_polygon_frag }); } diff --git a/src/geom/shader/polygon_frag.glsl b/src/geom/shader/polygon_frag.glsl index bad3913f6b..545721cd04 100644 --- a/src/geom/shader/polygon_frag.glsl +++ b/src/geom/shader/polygon_frag.glsl @@ -41,7 +41,12 @@ float sdRect(vec2 p, vec2 sz) { float inside = min(max(d.x, d.y), 0.); return outside + inside; } - +float circle(in vec2 _st, in float _radius){ + vec2 dist = _st-vec2(0.5); + return 1.-smoothstep(_radius-(_radius*0.01), + _radius+(_radius*0.01), + dot(dist,dist)*4.0); +} void main() { if(v_color.w == 0.0) { @@ -106,6 +111,12 @@ void main() { gl_FragColor = vec4(foggedColor,1.0); } #else + // #ifdef SHAPE + // vec2 st = gl_FragCoord.xy / v_size ; + // vec3 color = vec3(circle(st,0.5)); + // gl_FragColor = vec4(color, 1.0 ); + // return; + // #endif gl_FragColor = vec4(v_color.xyz , v_color.w); #endif diff --git a/src/geom/shader/polygon_vert.glsl b/src/geom/shader/polygon_vert.glsl index 2015bee5f5..815244fb97 100644 --- a/src/geom/shader/polygon_vert.glsl +++ b/src/geom/shader/polygon_vert.glsl @@ -30,6 +30,7 @@ void main() { if(pickingId == u_activeId) { v_color = u_activeColor; } + v_size = a_size.x * scale; gl_Position = matModelViewProjection * vec4(newposition, 1.0); return; } @@ -49,7 +50,7 @@ void main() { float lightWeight = ambientRatio + diffuseRatio * lambert + specularRatio * specular; v_texCoord = faceUv; v_lightWeight = lightWeight; - // v_size = a_size; + v_color =vec4(a_color.rgb*lightWeight, a_color.w); if(pickingId == u_activeId) { v_color = u_activeColor; diff --git a/src/geom/shape/path.js b/src/geom/shape/path.js index a693484d9c..82bd51d68e 100644 --- a/src/geom/shape/path.js +++ b/src/geom/shape/path.js @@ -16,7 +16,7 @@ function triangle() { return polygonPath(3); } function hexagon() { - return polygonPath(6); + return polygonPath(6, 1); } export { circle, @@ -29,11 +29,11 @@ export { square as squareColumn }; -export function polygonPath(pointCount) { +export function polygonPath(pointCount, start = 0) { const step = Math.PI * 2 / pointCount; const line = []; for (let i = 0; i < pointCount; i++) { - line.push(step * i - Math.PI / 12); + line.push(step * i - start * Math.PI / 12); } const path = line.map(t => { const x = Math.sin(t + Math.PI / 4), diff --git a/src/layer/index.js b/src/layer/index.js index 02e72edceb..8b33d8534e 100644 --- a/src/layer/index.js +++ b/src/layer/index.js @@ -5,6 +5,8 @@ import LineLayer from './lineLayer'; import ImageLayer from './imageLayer'; import RasterLayer from './rasterLayer'; import HeatmapLayer from './heatmapLayer'; +import TileLayer from './tile/tileLayer'; +import ImageTileLayer from './tile/imageTileLayer'; registerLayer('PolygonLayer', PolygonLayer); registerLayer('PointLayer', PointLayer); @@ -12,6 +14,8 @@ registerLayer('LineLayer', LineLayer); registerLayer('ImageLayer', ImageLayer); registerLayer('RasterLayer', RasterLayer); registerLayer('HeatmapLayer', HeatmapLayer); +registerLayer('TileLayer', TileLayer); +registerLayer('ImageTileLayer', ImageTileLayer); export { LAYER_MAP } from './factory'; export { registerLayer }; diff --git a/src/layer/render/image/drawImage.js b/src/layer/render/image/drawImage.js new file mode 100644 index 0000000000..93330df82f --- /dev/null +++ b/src/layer/render/image/drawImage.js @@ -0,0 +1,13 @@ +import * as THREE from '../../../core/three'; +import ImageMaterial from '../../../geom/material/imageMaterial'; +export default function DrawImage(attributes, style) { + const geometry = new THREE.BufferGeometry(); + geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.vertices, 3)); + geometry.addAttribute('uv', new THREE.Float32BufferAttribute(attributes.uvs, 2)); + const { opacity } = style; + const material = new ImageMaterial({ + u_texture: attributes.texture, + u_opacity: opacity + }); + return new THREE.Mesh(geometry, material); +} diff --git a/src/layer/render/polygon/drawFill.js b/src/layer/render/polygon/drawFill.js index 8c5e5beb65..d9981cbdbb 100644 --- a/src/layer/render/polygon/drawFill.js +++ b/src/layer/render/polygon/drawFill.js @@ -1,5 +1,7 @@ import * as THREE from '../../../core/three'; import PolygonMaterial from '../../../geom/material/polygonMaterial'; +// import TileMaterial from '../../../geom/material/tile/polygon'; + export default function DrawPolygonFill(attributes, style) { const { opacity, activeColor } = style; const geometry = new THREE.BufferGeometry(); @@ -7,6 +9,12 @@ export default function DrawPolygonFill(attributes, style) { geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4)); geometry.addAttribute('pickingId', new THREE.Float32BufferAttribute(attributes.pickingIds, 1)); geometry.addAttribute('normal', new THREE.Float32BufferAttribute(attributes.normals, 3)); + // const material = new PolygonMaterial({ + // u_opacity: opacity, + // u_activeColor: activeColor + // }, { + // SHAPE: false + // }); const material = new PolygonMaterial({ u_opacity: opacity, u_activeColor: activeColor diff --git a/src/layer/tile/imageTile.js b/src/layer/tile/imageTile.js index c14a052e01..d30e336385 100644 --- a/src/layer/tile/imageTile.js +++ b/src/layer/tile/imageTile.js @@ -1,137 +1,82 @@ -// import * as THREE from '../../core/three'; -// import Tile from './tile'; -// export default class ImageTile extends Tile { -// constructor(layer, z, x, y) { -// } -// requestTileAsync() { -// // Making this asynchronous really speeds up the LOD framerate -// setTimeout(() => { -// if (!this._mesh) { -// this._mesh = this._createMesh(); -// this._requestTile(); -// } -// }, 0); -// } -// _requestTile() { -// const urlParams = { -// x: this._tile[0], -// y: this._tile[1], -// z: this._tile[2] -// }; +import Tile from './tile'; +import ImageBuffer from '../../geom/buffer/image'; +import DrawImage from '../render/image/drawImage'; +export default class ImageTile extends Tile { + requestTileAsync() { + // Making this asynchronous really speeds up the LOD framerate + setTimeout(() => { + if (!this._mesh) { + // this._mesh = this._createMesh(); + this._requestTile(); + } + }, 0); + } + _requestTile() { + const urlParams = { + x: this._tile[0], + y: this._tile[1], + z: this._tile[2] + }; -// const url = this._getTileURL(urlParams); + const url = this._getTileURL(urlParams); + const image = document.createElement('img'); -// const image = document.createElement('img'); + image.addEventListener('load', () => { -// image.addEventListener('load', event => { -// const texture = new THREE.Texture(); + this._createMesh(image); + this._ready = true; + }, false); -// texture.image = image; -// texture.needsUpdate = true; + // image.addEventListener('progress', event => {}, false); + // image.addEventListener('error', event => {}, false); -// // Silky smooth images when tilted -// texture.magFilter = THREE.LinearFilter; -// texture.minFilter = THREE.LinearMipMapLinearFilter; + image.crossOrigin = ''; -// // TODO: Set this to renderer.getMaxAnisotropy() / 4 -// texture.anisotropy = 4; + // Load image + image.src = url; -// texture.needsUpdate = true; + this._image = image; + } + _getBufferData(images) { + const NW = this._tileBounds.getTopLeft(); + const SE = this._tileBounds.getBottomRight(); + const coordinates = [[ NW.x, NW.y, 0 ], [ SE.x, SE.y, 0 ]]; + return [{ + coordinates, + images + }]; + } + _createMesh(image) { + if (!this._center) { + return; + } + this._layerData = this._getBufferData(image); + const buffer = new ImageBuffer({ + layerData: this._layerData + }); + buffer.attributes.texture = buffer.texture; + const style = this.layer.get('styleOptions'); + const mesh = DrawImage(buffer.attributes, style); + this.Object3D.add(mesh); + return this.Object3D; + } + _abortRequest() { + if (!this._image) { + return; + } -// // Something went wrong and the tile or its material is missing -// // -// // Possibly removed by the cache before the image loaded -// if (!this._mesh || !this._mesh.children[0] || !this._mesh.children[0].material) { -// return; -// } + this._image.src = ''; + } -// this._mesh.children[0].material.map = texture; -// this._mesh.children[0].material.needsUpdate = true; + destroy() { + // Cancel any pending requests + this._abortRequest(); -// this._texture = texture; -// this._ready = true; -// }, false); + // Clear image reference + this._image = null; -// // image.addEventListener('progress', event => {}, false); -// // image.addEventListener('error', event => {}, false); + super.destroy(); + } -// image.crossOrigin = ''; - -// // Load image -// image.src = url; - -// this._image = image; -// } - -// _createMesh() { -// // Something went wrong and the tile -// // -// // Possibly removed by the cache before loaded -// if (!this._center) { -// return; -// } - -// const mesh = new THREE.Object3D(); -// const geom = new THREE.PlaneBufferGeometry(this._side, this._side, 1); - -// let material; -// if (!this._world._environment._skybox) { -// material = new THREE.MeshBasicMaterial({ -// depthWrite: false -// }); - -// // const material = new THREE.MeshPhongMaterial({ -// // depthWrite: false -// // }); -// } else { -// // Other MeshStandardMaterial settings -// // -// // material.envMapIntensity will change the amount of colour reflected(?) -// // from the environment map–can be greater than 1 for more intensity - -// material = new THREE.MeshStandardMaterial({ -// depthWrite: false -// }); -// material.roughness = 1; -// material.metalness = 0.1; -// material.envMap = this._world._environment._skybox.getRenderTarget(); -// } - -// const localMesh = new THREE.Mesh(geom, material); -// localMesh.rotation.x = -90 * Math.PI / 180; - -// localMesh.receiveShadow = true; - -// mesh.add(localMesh); -// mesh.renderOrder = 0.1; - -// mesh.position.x = this._center[0]; -// mesh.position.z = this._center[1]; - -// // const box = new BoxHelper(localMesh); -// // mesh.add(box); -// // -// // mesh.add(this._createDebugMesh()); - -// return mesh; -// } -// _abortRequest() { -// if (!this._image) { -// return; -// } - -// this._image.src = ''; -// } - -// destroy() { -// // Cancel any pending requests -// this._abortRequest(); - -// // Clear image reference -// this._image = null; - -// super.destroy(); -// } - -// } +} diff --git a/src/layer/tile/imageTileLayer.js b/src/layer/tile/imageTileLayer.js new file mode 100644 index 0000000000..5417854b73 --- /dev/null +++ b/src/layer/tile/imageTileLayer.js @@ -0,0 +1,8 @@ +import TileLayer from './tileLayer'; +import ImageTile from './imageTile'; + +export default class ImageTileLayer extends TileLayer { + _createTile(key, layer) { + return new ImageTile(key, this.url, layer); + } +} diff --git a/src/layer/tile/tile-cache.js b/src/layer/tile/tile-cache.js deleted file mode 100644 index cd42e03cd1..0000000000 --- a/src/layer/tile/tile-cache.js +++ /dev/null @@ -1,22 +0,0 @@ -import LRUCache from '../../util/lru-cache'; -export default class TileCache { - constructor(limit = 50) { - this._cache = new LRUCache(limit); - } - - getTile(z, x, y) { - const key = this._generateKey(z, x, y); - return this._cache.get(key); - } - - setTile(tile, z, x, y) { - const key = this._generateKey(z, x, y); - this._cache.set(key, tile); - } - _generateKey(z, x, y) { - return [ z, x, y ].join('_'); - } - destory() { - this._cache.clear(); - } -} diff --git a/src/layer/tile/tile.js b/src/layer/tile/tile.js index 8ca5d10b48..261800150c 100644 --- a/src/layer/tile/tile.js +++ b/src/layer/tile/tile.js @@ -1,59 +1,90 @@ -// const r2d = 180 / Math.PI; -// const tileURLRegex = /\{([zxy])\}/g; -// export class Tile { -// constructor(layer, z, x, y) { -// this.layer = layer; -// this._tile = [ z, x, y ]; +import * as THREE from '../../core/three'; +import { toLngLatBounds, toBounds } from '@antv/geo-coord'; +const r2d = 180 / Math.PI; +const tileURLRegex = /\{([zxy])\}/g; -// } -// _createMesh() {} -// _createDebugMesh() {} +export default class Tile { + constructor(key, url, layer) { + this.layer = layer; + this._tile = key.split('_').map(v => v * 1); + this._path = url; + this._tileLnglatBounds = this._tileLnglatBounds(this._tile); -// _getTileURL(urlParams) { -// if (!urlParams.s) { -// // Default to a random choice of a, b or c -// urlParams.s = String.fromCharCode(97 + Math.floor(Math.random() * 3)); -// } + this._tileBounds = this._tileBounds(this._tileLnglatBounds); -// tileURLRegex.lastIndex = 0; -// return this._path.replace(tileURLRegex, function(value, key) { -// // Replace with paramter, otherwise keep existing value -// return urlParams[key]; -// }); -// } + this._center = this._tileBounds.getCenter(); -// _tileBoundsFromWGS84(boundsWGS84) { -// const sw = this._layer._world.latLonToPoint(LatLon(boundsWGS84[1], boundsWGS84[0])); -// const ne = this._layer._world.latLonToPoint(LatLon(boundsWGS84[3], boundsWGS84[2])); + this._centerLnglat = this._tileLnglatBounds.getCenter(); + this.Object3D = new THREE.Object3D(); + this.requestTileAsync(); -// return [sw.x, sw.y, ne.x, ne.y]; -// } -// // Get tile bounds in WGS84 coordinates -// _tileBoundsWGS84(tile) { -// const e = this._tile2lon(tile[0] + 1, tile[2]); -// const w = this._tile2lon(tile[0], tile[2]); -// const s = this._tile2lat(tile[1] + 1, tile[2]); -// const n = this._tile2lat(tile[1], tile[2]); -// return [ w, s, e, n ]; -// } + } + _createMesh() {} + _getTileURL(urlParams) { + if (!urlParams.s) { + // Default to a random choice of a, b or c + urlParams.s = String.fromCharCode(97 + Math.floor(Math.random() * 3)); + } -// _tile2lon(x, z) { -// return x / Math.pow(2, z) * 360 - 180; -// } + tileURLRegex.lastIndex = 0; + return this._path.replace(tileURLRegex, function(value, key) { + return urlParams[key]; + }); + } + // 经纬度范围转瓦片范围 + _tileBounds(lnglatBound) { + const ne = this.layer.scene.project([ lnglatBound.getNorthWest().lng, lnglatBound.getNorthEast().lat ]); + const sw = this.layer.scene.project([ lnglatBound.getSouthEast().lng, lnglatBound.getSouthWest().lat ]); + return toBounds(sw, ne); + } + getMesh() { + return this.Object3D; + } -// _tile2lat(y, z) { -// const n = Math.PI - 2 * Math.PI * y / Math.pow(2, z); -// return r2d * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))); -// } -// _boundsToCenter(bounds) { -// const x = bounds[0] + (bounds[2] - bounds[0]) / 2; -// const y = bounds[1] + (bounds[3] - bounds[1]) / 2; + // Get tile bounds in WGS84 coordinates + _tileLnglatBounds(tile) { + const e = this._tile2lng(tile[0] + 1, tile[2]); + const w = this._tile2lng(tile[0], tile[2]); + const s = this._tile2lat(tile[1] + 1, tile[2]); + const n = this._tile2lat(tile[1], tile[2]); + return toLngLatBounds([ w, n ], [ e, s ]); + } -// return [ x, y ]; -// } -// destory() { + _tile2lng(x, z) { + return x / Math.pow(2, z) * 360 - 180; + } -// } -// } + _tile2lat(y, z) { + const n = Math.PI - 2 * Math.PI * y / Math.pow(2, z); + return r2d * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))); + } + destroy() { + if (this._object3D && this._object3D.children) { + let child; + for (let i = 0; i < this._object3D.children.length; i++) { + child = this._object3D.children[i]; + if (!child) { + continue; + } + this.remove(child); + if (child.geometry) { + // child.geometry.dispose(); + child.geometry = null; + } + if (child.material) { + if (child.material.map) { + child.material.map.dispose(); + child.material.map = null; + } + + child.material.dispose(); + child.material = null; + } + child = null; + } + } + this._object3D = null; + } +} diff --git a/src/layer/tile/tileCache.js b/src/layer/tile/tileCache.js new file mode 100644 index 0000000000..9681b739f5 --- /dev/null +++ b/src/layer/tile/tileCache.js @@ -0,0 +1,17 @@ +import LRUCache from '../../util/lru-cache'; +export default class TileCache { + constructor(limit = 50, tileDestroy) { + this._cache = new LRUCache(limit, tileDestroy); + } + + getTile(key) { + return this._cache.get(key); + } + + setTile(tile, key) { + this._cache.set(key, tile); + } + destory() { + this._cache.clear(); + } +} diff --git a/src/layer/tile/tileLayer.js b/src/layer/tile/tileLayer.js new file mode 100644 index 0000000000..a3bc44afc9 --- /dev/null +++ b/src/layer/tile/tileLayer.js @@ -0,0 +1,138 @@ +import Layer from '../../core/layer'; +import * as THREE from '../../core/three'; +import TileCache from './tileCache'; +import { throttle } from '@antv/util'; +import { toLngLat } from '@antv/geo-coord'; +import { epsg3857 } from '@antv/geo-coord/lib/geo/crs/crs-epsg3857'; +export default class TileLayer extends Layer { + constructor(scene, cfg) { + super(scene, cfg); + this._tileCache = new TileCache(50, this._destroyTile); + this._crs = epsg3857; + this._tiles = new THREE.Object3D(); + this._tileKeys = []; + this.tileList = []; + + + } + source(url) { + this.url = url; + return this; + } + render() { + this._initMapEvent(); + this.draw(); + } + draw() { + this._object3D.add(this._tiles); + this._calculateLOD(); + } + drawTile() { + + } + zoomchange(ev) { + super.zoomchange(ev); + throttle(this._calculateLOD, 200); + this._calculateLOD(); + } + dragend(ev) { + super.dragend(ev); + this._calculateLOD(); + + } + _calculateLOD() { + const viewPort = this.scene.getBounds().toBounds(); + const SE = viewPort.getSouthEast(); + const NW = viewPort.getNorthWest(); + const zoom = Math.round(this.scene.getZoom()) - 1; + const tileCount = Math.pow(2, zoom); + const center = this.scene.getCenter(); + const NWPoint = this._crs.lngLatToPoint(toLngLat(NW.lng, NW.lat), zoom); + const SEPoint = this._crs.lngLatToPoint(toLngLat(SE.lng, SE.lat), zoom); + const centerPoint = this._crs.lngLatToPoint(toLngLat(center.lng, center.lat), zoom); + const centerXY = centerPoint.divideBy(256).round(); + const minXY = NWPoint.divideBy(256).round(); + const maxXY = SEPoint.divideBy(256).round(); + // console.log(NW.lng, NW.lat, SE.lng, SE.lat, NWPonint, SEPonint); + let updateTileList = []; + this.tileList = []; + const halfx = Math.floor((maxXY.x - minXY.x) / 2) + 1; + const halfy = Math.floor((maxXY.y - minXY.y) / 2) + 1; + if (!(centerPoint.x > NWPoint.x && centerPoint.x < SEPoint.x)) { // 地图循环的问题 + for (let i = 0; i < minXY.x; i++) { + for (let j = Math.min(0, minXY.y - halfy); j < Math.max(maxXY.y + halfy, tileCount); j++) { + this._updateTileList(updateTileList, i, j, zoom); + } + } + for (let i = maxXY.x; i < tileCount; i++) { + for (let j = Math.min(0, minXY.y - halfy); j < Math.max(maxXY.y + halfy, tileCount); j++) { + this._updateTileList(updateTileList, i, j, zoom); + } + } + } + for (let i = Math.max(0, minXY.x - halfx); i < Math.min(maxXY.x + halfx, tileCount); i++) { + for (let j = Math.max(0, minXY.y - halfy); j < Math.min(maxXY.y + halfy, tileCount); j++) { + this._updateTileList(updateTileList, i, j, zoom); + } + } + // 过滤掉已经存在的 + // tileList = tileList.filter(tile => { + // }) + updateTileList = updateTileList.sort((a, b) => { + const tile1 = a.split('_'); + const tile2 = b.split('_'); + const d1 = Math.pow((tile1[0] * 1 - centerXY.x), 2) + Math.pow((tile1[1] * 1 - centerXY.y), 2); + const d2 = Math.pow((tile2[0] * 1 - centerXY.x), 2) + Math.pow((tile2[1] * 1 - centerXY.y), 2); + return d1 - d2; + }); + updateTileList.forEach(key => { + this._requestTile(key, this); + }); + this._removeOutTiles(); + } + _updateTileList(updateTileList, x, y, z) { + const key = [ x, y, z ].join('_'); + this.tileList.push(key); + if (this._tileKeys.indexOf(key) === -1) { + updateTileList.push(key); + } + } + _requestTile(key, layer) { + let tile = this._tileCache.getTile(key); + if (!tile) { + tile = this._createTile(key, layer); + const mesh = tile.getMesh(); + mesh.name = key; + this._tileCache.setTile(tile, key); + this._tileKeys.push(key); + // this.scene._engine.update(); + } + this._tiles.add(tile.getMesh()); + this._tileKeys.push(key); + } + // 移除视野外的tile + _removeOutTiles() { + for (let i = this._tiles.children.length - 1; i >= 0; i--) { + const tile = this._tiles.children[i]; + const key = tile.name; + if (this.tileList.indexOf(key) === -1) { + this._tiles.remove(tile); + } + this._tileKeys = [].concat(this.tileList); + } + } + _removeTiles() { + if (!this._tiles || !this._tiles.children) { + return; + } + + for (let i = this._tiles.children.length - 1; i >= 0; i--) { + this._tiles.remove(this._tiles.children[i]); + } + } + _destroyTile(tile) { + tile.destroy(); + } + desttroy() { + } +} diff --git a/src/map/AMap.js b/src/map/AMap.js index 8ac662f838..d5bf7aae89 100644 --- a/src/map/AMap.js +++ b/src/map/AMap.js @@ -103,6 +103,7 @@ export default class GaodeMap extends Base { } mixMap(scene) { const map = this.map; + scene.project = GaodeMap.project; scene.getZoom = () => { return map.getZoom(); }; diff --git a/src/source/parser/geojson.js b/src/source/parser/geojson.js index 791dd2de0d..0e7d4de3f1 100644 --- a/src/source/parser/geojson.js +++ b/src/source/parser/geojson.js @@ -3,6 +3,10 @@ import { getCoords } from '@turf/invariant'; export default function geoJSON(data) { const resultData = []; + data.features = data.features.filter(item => { + return item != null && item.geometry && item.geometry.type && item.geometry.coordinates && item.geometry.coordinates.length > 0; + }); + // 数据为空时处理 turfMeta.flattenEach(data, (currentFeature, featureIndex) => { // 多个polygon 拆成一个 const coord = getCoords(currentFeature); diff --git a/src/util/lru-cache.js b/src/util/lru-cache.js index 8b417e53e2..ab7a78339b 100644 --- a/src/util/lru-cache.js +++ b/src/util/lru-cache.js @@ -6,9 +6,9 @@ */ export default class LRUCache { - constructor(limit = 5) { + constructor(limit = 50, destroy = () => {}) { this.limit = limit; - + this.destroy = destroy; this.clear(); } @@ -48,6 +48,7 @@ export default class LRUCache { delete(key) { const value = this._cache[key]; + this.destroy(value); if (value) { this._deleteCache(key); this._deleteOrder(key); diff --git a/test/unit/layer/tile/tile-layer-spec.js b/test/unit/layer/tile/tile-layer-spec.js new file mode 100644 index 0000000000..060127bb11 --- /dev/null +++ b/test/unit/layer/tile/tile-layer-spec.js @@ -0,0 +1,25 @@ +// import { expect } from 'chai'; +// import {Scene} from '../../../../src/core/scene'; +// import TileLayer from '../../../../src/layer/tile/tileLayer'; + +// describe('tile layer', function() { + +// const amapscript = document.createElement('script'); +// amapscript.type = 'text/javascript'; +// amapscript.src = 'https://webapi.amap.com/maps?v=1.4.8&key=15cd8a57710d40c9b7c0e3cc120f1200&plugin=Map3D'; +// document.body.appendChild(amapscript); +// const div = document.createElement('div'); +// div.id = 'map'; +// div.style.cssText = 'width:500px;height:500px;position:absolute'; +// document.body.appendChild(div); +// const scene = new Scene({ +// id: 'map', +// mapStyle: 'light', // 样式URL +// center: [ 120.19382669582967, 30.258134 ], +// pitch: 0, +// zoom: 2, +// maxZoom: 20, +// minZoom: 0 +// }); +// // const TileLayer = new TileLayer(null, {}); +// });