fix(mvt): source

This commit is contained in:
thinkinggis 2019-06-10 11:54:11 +08:00
commit 6a4af99c29
29 changed files with 437 additions and 118 deletions

View File

@ -76,7 +76,7 @@ scene.on('loaded', () => {
circleLayer.setData(pointOnCircle(timestamp / 1000)); circleLayer.setData(pointOnCircle(timestamp / 1000));
requestAnimationFrame(animateMarker); requestAnimationFrame(animateMarker);
} }
animateMarker(0); //animateMarker(0);
/** /**
const layerText = scene.PointLayer({ const layerText = scene.PointLayer({

View File

@ -35,15 +35,15 @@ const scene = new L7.Scene({
window.scene = scene; window.scene = scene;
scene.on('loaded', () => { scene.on('loaded', () => {
scene.ImageTileLayer({ scene.ImageTileLayer({
zIndex:0 zIndex:4
}) })
.source('http://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}') .source('http://t1.tianditu.com/DataServer?T=cva_w&X={x}&Y={y}&L={z}&tk=174705aebfe31b79b3587279e211cb9a')
.render(); .render();
$.getJSON('https://gw.alipayobjects.com/os/rmsportal/JToMOWvicvJOISZFCkEI.json', city => { $.getJSON('https://gw.alipayobjects.com/os/rmsportal/JToMOWvicvJOISZFCkEI.json', city => {
const citylayer = scene.PolygonLayer( const citylayer = scene.PolygonLayer(
{ {
zIndex:4 zIndex:0
} }
) )
.source(city) .source(city)

View File

@ -34,6 +34,7 @@ const scene = new L7.Scene({
}); });
window.scene = scene; window.scene = scene;
scene.on('loaded', () => { scene.on('loaded', () => {
const layer = scene.VectorTileLayer({ const layer = scene.VectorTileLayer({
zIndex:0, zIndex:0,
layerType:'point' layerType:'point'
@ -42,28 +43,32 @@ scene.on('loaded', () => {
// http://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/thinkgis/tile/point/{z}/{x}/{y}.pbf // http://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/thinkgis/tile/point/{z}/{x}/{y}.pbf
// https://mvt.amap.com/district/CHN2/8/203/105/4096?key= // https://mvt.amap.com/district/CHN2/8/203/105/4096?key=
.source('http://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/thinkgis/tile/point2/{z}/{x}/{y}.pbf',{ .source('http://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/thinkgis/tile/china/all_point/{z}/{x}/{y}.pbf',{
parser:{ parser:{
type: 'mvt', type: 'mvt',
sourceLayer:'layer', sourceLayer:'layer',
//idField:'OBJECTID', // idField:'adcode',
maxZoom: 17, maxZoom: 14,
minZoom: 13,
} }
}) })
.scale({ .scale({
total:{ total:{
min:0, min:0,
max:1000000, max:100000,
type:'log' type:'log'
} }
}) })
.shape('hexagon') .shape('circle')
.size(2) .size(5)
.active({fill:'red'}) .active({fill:'red'})
.color('total', ['#d53e4f','#f46d43','#fdae61','#fee08b','#ffffbf','#e6f598','#abdda4','#66c2a5','#3288bd'].reverse()) .color('total', ['#d53e4f','#f46d43','#fdae61','#fee08b','#ffffbf','#e6f598','#abdda4','#66c2a5','#3288bd'].reverse())
//.color('#0D408C') //.color('#0D408C')
.style({ .style({
opacity:1.0 stroke: 'rgba(255,255,255,0.8)',
strokeWidth: 1,
strokeOpacity:0.6,
opacity: 1
}) })
.render( .render(
); );

View File

@ -30,7 +30,7 @@ const scene = new L7.Scene({
center: [116.5909,39.9225 ], center: [116.5909,39.9225 ],
pitch: 0, pitch: 0,
hash:true, hash:true,
zoom: 14, zoom: 4,
}); });
window.scene = scene; window.scene = scene;
@ -44,29 +44,29 @@ scene.on('loaded', () => {
// http://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/thinkgis/tile/point/{z}/{x}/{y}.pbf // http://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/thinkgis/tile/point/{z}/{x}/{y}.pbf
// https://mvt.amap.com/district/CHN2/8/203/105/4096?key= // https://mvt.amap.com/district/CHN2/8/203/105/4096?key=
.source('http://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/thinkgis/tile/china/{z}/{x}/{y}.pbf',{ .source('http://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/thinkgis/tile/china/village/{z}/{x}/{y}.pbf',{
parser:{ parser:{
type: 'mvt', type: 'mvt',
sourceLayer:'layer', sourceLayer:'layer',
idField:'adcode', idField:'code',
maxZoom: 17, maxZoom: 17,
} }
}) })
.filter('province',name =>{ .scale({
return name =='山东省' total:{
type:'linear',
min:0,
max:5000
}
}) })
.shape('fill') .shape('fill')
.size(2) .size(2)
.active({fill:'red'}) .active(false)
.color('name',name => { .color('total', ['#ffffe5','#fff7bc','#fee391','#fec44f','#fe9929','#ec7014','#cc4c02','#993404','#662506'])
//var colorHash = new ColorHash();
return colorHash.hex(name)
})
.style({ .style({
opacity:1.0 opacity:1.0
}) })
.render( .render();
);
layer.on('mousemove',(feature)=>{ layer.on('mousemove',(feature)=>{
console.log(feature); console.log(feature);
}) })

View File

@ -0,0 +1,49 @@
import Util from '../../util';
import { updateObjecteUniform } from '../../util/object3d-util';
export default class BufferController {
constructor(cfg) {
// defs 列定义
Util.assign(this, cfg);
if (!this.mesh) this.mesh = this.layer;
}
_updateColorAttributes() {
const filterData = this.mesh.layerData;
const colorKey = {};
for (let e = 0; e < filterData.length; e++) {
const item = filterData[e];
colorKey[item.id] = item.color;
}
this.layer._activeIds = null; // 清空选中元素xwxw
const colorAttr = this.mesh.mesh.geometry.attributes.a_color;
const pickAttr = this.mesh.mesh.geometry.attributes.pickingId;
pickAttr.array.forEach((id, index) => {
id = Math.abs(id);
const color = colorKey[id];
id = Math.abs(id);
const item = filterData[id - 1];
if (item.hasOwnProperty('filter') && item.filter === false) {
colorAttr.array[index * 4 + 0] = 0;
colorAttr.array[index * 4 + 1] = 0;
colorAttr.array[index * 4 + 2] = 0;
colorAttr.array[index * 4 + 3] = 0;
pickAttr.array[index] = -id; // 通过Id数据过滤 id<0 不显示
} else {
colorAttr.array[index * 4 + 0] = color[0];
colorAttr.array[index * 4 + 1] = color[1];
colorAttr.array[index * 4 + 2] = color[2];
colorAttr.array[index * 4 + 3] = color[3];
pickAttr.array[index] = id;
}
});
colorAttr.needsUpdate = true;
pickAttr.needsUpdate = true;
}
_updateStyle(option) {
const newOption = { };
for (const key in option) {
newOption['u_' + key] = option[key];
}
updateObjecteUniform(this.mesh._object3D, newOption);
}
}

View File

@ -3,4 +3,32 @@ export default class EventContoller {
constructor(cfg) { constructor(cfg) {
Util.assign(this, cfg); Util.assign(this, cfg);
} }
_init() {
this.layer.scene.on('pick-' + this.layer.layerId, e => {
let { featureId, point2d, type } = e;
if (featureId < 0 && this._activeIds !== null) {
type = 'mouseleave';
}
this._activeIds = featureId;
// TODO 瓦片图层获取选中数据信息
const lnglat = this.layer.scene.containerToLngLat(point2d);
const { feature, style } = this.layer.getSelectFeature(featureId, lnglat);
// const style = this.layerData[featureId - 1];
const target = {
featureId,
feature,
style,
pixel: point2d,
type,
lnglat: { lng: lnglat.lng, lat: lnglat.lat }
};
if (featureId >= 0 || this._activeIds >= 0) { // 拾取到元素,或者离开元素
this.layer.emit(type, target);
}
});
}
_initMapEvent() {
}
} }

View File

@ -2,9 +2,13 @@ import Scale from './scale';
import Mapping from './mapping'; import Mapping from './mapping';
import Picking from './pick'; import Picking from './pick';
import Interaction from './interaction'; import Interaction from './interaction';
import Event from './event';
import Buffer from './buffer';
export default { export default {
Scale, Scale,
Mapping, Mapping,
Picking, Picking,
Interaction Interaction,
Event,
Buffer
}; };

View File

@ -20,13 +20,15 @@ export default class Mapping {
this._mapping(); this._mapping();
} }
update() { update() {
this.mesh.set('scales', {});
this._initTileAttrs();
this._updateMaping(); this._updateMaping();
} }
_initControllers() { _initControllers() {
const scales = this.layer.get('scaleOptions'); const scalesOption = this.layer.get('scaleOptions');
const scaleController = new ScaleController({ const scaleController = new ScaleController({
defs: { defs: {
...scales ...scalesOption
} }
}); });
this.mesh.set('scaleController', scaleController); this.mesh.set('scaleController', scaleController);
@ -101,7 +103,10 @@ export default class Mapping {
// 通过透明度过滤数据 // 通过透明度过滤数据
if (attrs.hasOwnProperty('filter')) { if (attrs.hasOwnProperty('filter')) {
mappedData.forEach(item => { mappedData.forEach(item => {
item.filter === false && (item.color[3] = 0); if (item.filter === false) {
(item.color[3] = 0);
item.id = -item.id;
}
}); });
} }
this.mesh.layerData = mappedData; this.mesh.layerData = mappedData;

View File

@ -1,7 +1,7 @@
import Util from '../../util'; import Util from '../../util';
import * as THREE from '../three'; import * as THREE from '../three';
import pickingFragmentShader from '../engine/picking/picking_frag.glsl'; import pickingFragmentShader from '../engine/picking/picking_frag.glsl';
import { updateObjecteUniform } from '../../util/object3d-util'; import { updateObjecteUniform, destoryObject } from '../../util/object3d-util';
export default class PickContoller { export default class PickContoller {
constructor(cfg) { constructor(cfg) {
Util.assign(this, cfg); Util.assign(this, cfg);
@ -19,18 +19,32 @@ export default class PickContoller {
this.layer.scene._engine._picking.remove(object); this.layer.scene._engine._picking.remove(object);
} }
removePickingMesh(mesh) { removePickingMesh(mesh) {
this.Object3D.remove(mesh); this.pickObject3D.remove(mesh);
destoryObject(mesh);
}
removePickMeshByName(name) {
for (let i = 0; i < this.pickObject3D.children.length; i++) {
if (this.pickObject3D.children[i].name === name) {
this.removePickingMesh(this.pickObject3D.children[i]);
}
}
}
removeAllMesh() {
this.pickObject3D.children.forEach(element => {
this.pickObject3D.remove(element);
destoryObject(element);
});
} }
addPickMesh(mesh) { addPickMesh(mesh) {
const pickmaterial = mesh.material.clone(); const pickmaterial = mesh.material.clone();
pickmaterial.fragmentShader = pickingFragmentShader; pickmaterial.fragmentShader = pickingFragmentShader;
const pickingMesh = new THREE[mesh.type](mesh.geometry, pickmaterial); const pickingMesh = new THREE[mesh.type](mesh.geometry, pickmaterial);
pickingMesh.name = this.layerId; pickingMesh.name = mesh.name;
pickingMesh.onBeforeRender = () => { pickingMesh.onBeforeRender = () => {
const zoom = this.layer.scene.getZoom(); const zoom = this.layer.scene.getZoom();
updateObjecteUniform(pickingMesh, { u_zoom: zoom }); updateObjecteUniform(pickingMesh, { u_zoom: zoom });
}; };
this.pickObject3D.add(pickingMesh); this.pickObject3D.add(pickingMesh);
} }
} }

View File

@ -107,7 +107,6 @@ export default class Layer extends Base {
this._object3D.add(object); this._object3D.add(object);
if (type === 'fill') { if (type === 'fill') {
this.get('pickingController').addPickMesh(object); this.get('pickingController').addPickMesh(object);
// this._addPickMesh(object);// 不对边界线进行拾取
} }
setTimeout(() => this.scene._engine.update(), 500); setTimeout(() => this.scene._engine.update(), 500);
} }
@ -315,7 +314,8 @@ export default class Layer extends Base {
// 重绘 度量, 映射,顶点构建 // 重绘 度量, 映射,顶点构建
repaint() { repaint() {
this.set('scales', {}); this.set('scales', {});
this._initControllers(); const mappingCtr = new Controller.Mapping({ layer: this });
this.set('mappingController', mappingCtr);
// this._initAttrs(); // this._initAttrs();
// this._mapping(); // this._mapping();
this.redraw(); this.redraw();
@ -467,7 +467,6 @@ export default class Layer extends Base {
// TODO 瓦片图层获取选中数据信息 // TODO 瓦片图层获取选中数据信息
const lnglat = this.scene.containerToLngLat(point2d); const lnglat = this.scene.containerToLngLat(point2d);
const { feature, style } = this.getSelectFeature(featureId, lnglat); const { feature, style } = this.getSelectFeature(featureId, lnglat);
// const style = this.layerData[featureId - 1];
const target = { const target = {
featureId, featureId,
feature, feature,
@ -533,7 +532,7 @@ export default class Layer extends Base {
offset = 5; offset = 5;
this.shapeType = 'text' && (offset = 10); this.shapeType = 'text' && (offset = 10);
} else if (this.type === 'polyline') { } else if (this.type === 'polyline' || this.type === 'line') {
offset = 2; offset = 2;
} else if (this.type === 'polygon') { } else if (this.type === 'polygon') {
offset = 1; offset = 1;
@ -551,7 +550,7 @@ export default class Layer extends Base {
this._object3D.children.forEach(child => { this._object3D.children.forEach(child => {
this._object3D.remove(child); this._object3D.remove(child);
}); });
this.removeFromPicking(this._pickingMesh); this.get('pickingController').removeAllMesh();
this.draw(); this.draw();
} }
// 更新mesh // 更新mesh

View File

@ -20,7 +20,7 @@ export default class Source extends Base {
super(cfg); super(cfg);
const transform = this.get('transforms'); const transform = this.get('transforms');
this._transforms = transform || []; this._transforms = transform || [];
const mapType = this.get('mapType'); const mapType = this.get('mapType') || 'AMap';
this.projectFlat = getMap(mapType).project; this.projectFlat = getMap(mapType).project;
// 数据解析 // 数据解析
this._init(); this._init();
@ -56,10 +56,13 @@ export default class Source extends Base {
const { type = 'geojson' } = parser; const { type = 'geojson' } = parser;
const data = this.get('data'); const data = this.get('data');
this.originData = getParser(type)(data, parser); this.originData = getParser(type)(data, parser);
this.data = { // this.data = {
dataArray: clone(this.originData.dataArray) // dataArray: clone(this.originData.dataArray)
}; // };
this.data.extent = extent(this.data.dataArray); this.data = this.originData;
if (this.data !== null) {
this.data.extent = extent(this.data.dataArray);
}
} }
/** /**
* 数据统计 * 数据统计
@ -96,6 +99,9 @@ export default class Source extends Base {
this._projectCoords(); this._projectCoords();
} }
_projectCoords() { _projectCoords() {
if (this.data === null) {
return;
}
this.data.dataArray.forEach(data => { this.data.dataArray.forEach(data => {
// data.coordinates = this._coordProject(data.coordinates); // data.coordinates = this._coordProject(data.coordinates);
data.coordinates = tranfrormCoord(data.coordinates, this._coorConvert.bind(this)); data.coordinates = tranfrormCoord(data.coordinates, this._coorConvert.bind(this));

View File

@ -1,25 +1,46 @@
import { packUint8ToFloat } from '../../../util/vertex-compress';
const LEFT_SHIFT18 = 262144.0;
const LEFT_SHIFT20 = 1048576.0;
export default function circleBuffer(layerData) { export default function circleBuffer(layerData) {
const index = []; const index = [];
const aExtrude = [];
const aRadius = [];
const aColor = [];
const aPickingId = [];
const aPosition = []; const aPosition = [];
const aPackedData = [];
layerData.forEach(({ size = 0, color, id, coordinates }, i) => { layerData.forEach(({ size = 0, color, id, coordinates }, i) => {
if (isNaN(size)) {
size = 0;
}
// pack color(vec4) into vec2
const packedColor = [
packUint8ToFloat(color[0] * 255, color[1] * 255),
packUint8ToFloat(color[2] * 255, color[3] * 255)
];
// construct point coords // construct point coords
aExtrude.push(-1, -1, 1, -1, 1, 1, -1, 1); [
aRadius.push(size, size, size, size); [ -1, -1 ],
aColor.push(...color, ...color, ...color, ...color); [ 1, -1 ],
aPickingId.push(id, id, id, id); [ 1, 1 ],
[ -1, 1 ]
].forEach(extrude => {
// vec4(color, color, (4-bit extrude, 16-bit size), id)
aPackedData.push(
...packedColor,
(extrude[0] + 1) * LEFT_SHIFT20 + (extrude[1] + 1) * LEFT_SHIFT18 + size,
id
);
});
// TODO如果使用相对瓦片坐标还可以进一步压缩
aPosition.push(...coordinates, ...coordinates, ...coordinates, ...coordinates); aPosition.push(...coordinates, ...coordinates, ...coordinates, ...coordinates);
index.push(...[ 0, 1, 2, 0, 2, 3 ].map(n => n + i * 4)); index.push(...[ 0, 1, 2, 0, 2, 3 ].map(n => n + i * 4));
}); });
return { return {
aExtrude,
aRadius,
aColor,
aPickingId,
aPosition, aPosition,
index index,
aPackedData
}; };
} }

View File

@ -1,6 +1,4 @@
attribute float a_radius; attribute vec4 a_packed_data;
attribute vec2 a_shape;
attribute vec4 a_color;
uniform float u_zoom : 1; uniform float u_zoom : 1;
uniform float u_stroke_width : 2; uniform float u_stroke_width : 2;
@ -11,24 +9,44 @@ varying vec3 v_data;
varying vec4 v_color; varying vec4 v_color;
varying float v_radius; varying float v_radius;
void main() { #pragma include "decode"
v_color = a_color;
v_radius = a_radius; void main() {
// unpack color(vec2)
v_color = decode_color(a_packed_data.xy);
// unpack picking_id
float picking_id = a_packed_data.w;
// unpack data(extrude(4-bit), radius(16-bit))
float compressed = a_packed_data.z;
// extrude(4-bit)
vec2 extrude;
extrude.x = floor(compressed * SHIFT_RIGHT20);
compressed -= extrude.x * SHIFT_LEFT20;
extrude.x = extrude.x - 1.;
extrude.y = floor(compressed * SHIFT_RIGHT18);
compressed -= extrude.y * SHIFT_LEFT18;
extrude.y = extrude.y - 1.;
// radius(16-bit)
float radius = compressed;
v_radius = radius;
// extrude
float zoom_scale = pow(2., 20. - u_zoom); float zoom_scale = pow(2., 20. - u_zoom);
vec2 offset = a_shape * (a_radius + u_stroke_width) * zoom_scale; vec2 offset = extrude * (radius + u_stroke_width) * zoom_scale;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position.xy + offset, 0.0, 1.0); gl_Position = projectionMatrix * modelViewMatrix * vec4(position.xy + offset, 0.0, 1.0);
// anti-alias // anti-alias
float antialiasblur = 1.0 / (a_radius + u_stroke_width); float antialiasblur = 1.0 / (radius + u_stroke_width);
// construct point coords // construct point coords
v_data = vec3(a_shape, antialiasblur); v_data = vec3(extrude, antialiasblur);
// picking // picking
if(pickingId == u_activeId) { if(picking_id == u_activeId) {
v_color = u_activeColor; v_color = u_activeColor;
} }
worldId = id_toPickColor(pickingId); worldId = id_toPickColor(picking_id);
} }

View File

@ -51,9 +51,12 @@ import mask_quard_frag from '../shader/tile/mask_quard_frag.glsl';
import common from './common.glsl'; import common from './common.glsl';
import { registerModule } from '../../util/shaderModule'; import { registerModule } from '../../util/shaderModule';
import pick_color from './shaderChunks/pick_color.glsl'; import pick_color from './shaderChunks/pick_color.glsl';
import decode from './shaderChunks/decode.glsl';
export function compileBuiltinModules() { export function compileBuiltinModules() {
registerModule('point', { vs: point_vert, fs: point_frag }); registerModule('point', { vs: point_vert, fs: point_frag });
registerModule('common', { vs: common, fs: common }); registerModule('common', { vs: common, fs: common });
registerModule('decode', { vs: decode, fs: '' });
registerModule('pick_color', { vs: pick_color, fs: pick_color }); registerModule('pick_color', { vs: pick_color, fs: pick_color });
registerModule('circle', { vs: circle_vert, fs: circle_frag }); registerModule('circle', { vs: circle_vert, fs: circle_frag });
registerModule('polygon', { vs: polygon_vert, fs: polygon_frag }); registerModule('polygon', { vs: polygon_vert, fs: polygon_frag });

View File

@ -19,5 +19,6 @@ void main() {
// vTime = 1.0- (28800. + mod(u_time* 10.,28800.)- position.z / 1000.) / 100.; // vTime = 1.0- (28800. + mod(u_time* 10.,28800.)- position.z / 1000.) / 100.;
#endif #endif
gl_Position = matModelViewProjection * vec4(position.xy, 0., 1.0); gl_Position = matModelViewProjection * vec4(position.xy, 0., 1.0);
gl_Position.z += 2. * gl_Position.w;
worldId = id_toPickColor(pickingId); worldId = id_toPickColor(pickingId);
} }

View File

@ -33,6 +33,7 @@ void main() {
float extrude_scale = pow(2.0, 20.0 - u_zoom); float extrude_scale = pow(2.0, 20.0 - u_zoom);
vec3 offset = vec3(normal * a_size * extrude_scale / 2.0 * a_miter); vec3 offset = vec3(normal * a_size * extrude_scale / 2.0 * a_miter);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position.xy + offset.xy, 0., 1.0); gl_Position = projectionMatrix * modelViewMatrix * vec4(position.xy + offset.xy, 0., 1.0);
// gl_Position.z -=0.8 * gl_Position.w;
#ifdef ANIMATE #ifdef ANIMATE
float alpha =1.0 - fract( mod(1.0- a_distance,u_interval)* (1.0/u_interval) + u_time / u_duration); float alpha =1.0 - fract( mod(1.0- a_distance,u_interval)* (1.0/u_interval) + u_time / u_duration);

View File

@ -56,6 +56,7 @@ void main() {
v_color = u_activeColor; v_color = u_activeColor;
} }
gl_Position = matModelViewProjection * vec4(newposition, 1.0); gl_Position = matModelViewProjection * vec4(newposition, 1.0);
// gl_Position.z +=1.0 * gl_Position.w;
} }

View File

@ -0,0 +1,17 @@
#define SHIFT_RIGHT18 1.0 / 262144.0
#define SHIFT_RIGHT20 1.0 / 1048576.0
#define SHIFT_LEFT18 262144.0
#define SHIFT_LEFT20 1048576.0
vec2 unpack_float(const float packedValue) {
int packedIntValue = int(packedValue);
int v0 = packedIntValue / 256;
return vec2(v0, packedIntValue - v0 * 256);
}
vec4 decode_color(const vec2 encodedColor) {
return vec4(
unpack_float(encodedColor[0]) / 255.0,
unpack_float(encodedColor[1]) / 255.0
);
}

View File

@ -29,15 +29,11 @@ export default class PointLayer extends Layer {
} }
} }
// 2D circle 特殊处理
if (shape === 'circle') {
return 'circle';
}
if ( if (
pointShape['2d'].indexOf(shape) !== -1 || pointShape['2d'].indexOf(shape) !== -1 ||
pointShape['3d'].indexOf(shape) !== -1 pointShape['3d'].indexOf(shape) !== -1
) { ) {
return 'fill'; return shape === 'circle' ? 'circle' : 'fill';
} else if (this.scene.image.imagesIds.indexOf(shape) !== -1) { } else if (this.scene.image.imagesIds.indexOf(shape) !== -1) {
return 'image'; return 'image';
} }
@ -45,11 +41,15 @@ export default class PointLayer extends Layer {
} }
zoomchange(ev) { zoomchange(ev) {
super.zoomchange(ev); super.zoomchange(ev);
this._updateData(); requestAnimationFrame(() => {
this._updateData();
});
} }
dragend(ev) { dragend(ev) {
super.dragend(ev); super.dragend(ev);
this._updateData(); requestAnimationFrame(() => {
this._updateData();
});
} }
_updateData() { _updateData() {

View File

@ -9,14 +9,11 @@ export default function drawCircle(layerData, layer) {
const style = layer.get('styleOptions'); const style = layer.get('styleOptions');
const activeOption = layer.get('activedOptions'); const activeOption = layer.get('activedOptions');
const { aColor, aExtrude, aPickingId, aPosition, index, aRadius } = PointBuffer.CircleBuffer(layerData, style); const { aPosition, aPackedData, index } = PointBuffer.CircleBuffer(layerData, style);
const geometry = new THREE.BufferGeometry(); const geometry = new THREE.BufferGeometry();
geometry.setIndex(index); geometry.setIndex(index);
geometry.addAttribute('position', new THREE.Float32BufferAttribute(aPosition, 3)); geometry.addAttribute('position', new THREE.Float32BufferAttribute(aPosition, 3));
geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(aColor, 4)); geometry.addAttribute('a_packed_data', new THREE.Float32BufferAttribute(aPackedData, 4));
geometry.addAttribute('pickingId', new THREE.Float32BufferAttribute(aPickingId, 1));
geometry.addAttribute('a_shape', new THREE.Float32BufferAttribute(aExtrude, 2));
geometry.addAttribute('a_radius', new THREE.Float32BufferAttribute(aRadius, 1));
const material = new CircleMaterial({ const material = new CircleMaterial({
u_opacity: style.opacity, u_opacity: style.opacity,
@ -28,5 +25,8 @@ export default function drawCircle(layerData, layer) {
}); });
material.depthTest = false; material.depthTest = false;
const fillMesh = new THREE.Mesh(geometry, material); const fillMesh = new THREE.Mesh(geometry, material);
aPosition.length = 0;
aPackedData.length = 0;
index.length = 0;
return fillMesh; return fillMesh;
} }

View File

@ -16,7 +16,6 @@ export default class ImageTile extends Tile {
const image = this._createDebugMesh(); const image = this._createDebugMesh();
this._createMesh(image); this._createMesh(image);
this.emit('tileLoaded'); this.emit('tileLoaded');
// return;
// const urlParams = { // const urlParams = {
// x: this._tile[0], // x: this._tile[0],
// y: this._tile[1], // y: this._tile[1],
@ -29,12 +28,10 @@ export default class ImageTile extends Tile {
// image.addEventListener('load', () => { // image.addEventListener('load', () => {
// this._isLoaded = true; // this._isLoaded = true;
// this._createMesh(image); // this._createMesh(image);
// this.emit('tileLoaded');
// this._ready = true; // this._ready = true;
// }, false); // }, false);
// // image.addEventListener('progress', event => {}, false);
// // image.addEventListener('error', event => {}, false);
// image.crossOrigin = ''; // image.crossOrigin = '';
// // Load image // // Load image
@ -83,6 +80,9 @@ export default class ImageTile extends Tile {
} }
this._image.src = ''; this._image.src = '';
}
updateColor() {
} }
getSelectFeature() { getSelectFeature() {
return {}; return {};

View File

@ -2,6 +2,10 @@ import TileLayer from './tileLayer';
import ImageTile from './imageTile'; import ImageTile from './imageTile';
export default class ImageTileLayer extends TileLayer { export default class ImageTileLayer extends TileLayer {
constructor(scene, cfg) {
super(scene, cfg);
this.type = 'image';
}
_createTile(key, layer) { _createTile(key, layer) {
return new ImageTile(key, this.url, layer); return new ImageTile(key, this.url, layer);
} }

View File

@ -21,7 +21,9 @@ export default class Tile extends Base {
this._center = this._tileBounds.getCenter(); this._center = this._tileBounds.getCenter();
this._centerLnglat = this._tileLnglatBounds.getCenter(); this._centerLnglat = this._tileLnglatBounds.getCenter();
this._object3D = new THREE.Object3D(); this._object3D = new THREE.Object3D({ name: key });
this._object3D.frustumCulled = false;
// this._object3D.name = key;
this._object3D.onBeforeRender = () => { this._object3D.onBeforeRender = () => {
}; };
this._isLoaded = false; this._isLoaded = false;
@ -29,14 +31,28 @@ export default class Tile extends Base {
} }
_init(data) { _init(data) {
this._creatSource(data); this._creatSource(data);
if (this.layerSource.data === null) {
return;
}
this._initControllers();
this._createMesh();
}
repaint() {
this._initControllers(); this._initControllers();
this._createMesh(); this._createMesh();
} }
_initControllers() { _initControllers() {
this.mapping = new Controller.Mapping({ const mappingCtr = new Controller.Mapping({
layer: this.layer, layer: this.layer,
mesh: this mesh: this
}); });
const bufferCtr = new Controller.Buffer({
layer: this.layer,
mesh: this
});
this.set('mappingController', mappingCtr);
this.set('bufferController', bufferCtr);
} }
_createMesh() {} _createMesh() {}
_getTileURL(urlParams) { _getTileURL(urlParams) {
@ -98,10 +114,16 @@ export default class Tile extends Base {
return false; return false;
} }
_preRender() { updateColor() {
const bufferCtr = this.get('bufferController');
this.get('mappingController').update();
bufferCtr._updateColorAttributes(this.getMesh().children[0]);
} }
repaint() { updateStyle() {
const bufferCtr = this.get('bufferController');
bufferCtr._updateStyle(this.getMesh().children[0]);
}
_preRender() {
} }
destroy() { destroy() {
super.destroy(); super.destroy();

View File

@ -1,11 +1,13 @@
import Layer from '../../core/layer'; import Layer from '../../core/layer';
import Util from '../../util';
import diff from '../../util/diff';
import source from '../../core/source'; import source from '../../core/source';
import * as THREE from '../../core/three'; import * as THREE from '../../core/three';
import Controller from '../../core/controller/index'; import Controller from '../../core/controller/index';
import Global from '../../global'; import Global from '../../global';
const { pointShape } = Global; const { pointShape } = Global;
import TileCache from './tileCache'; import TileCache from './tileCache';
import { throttle, deepMix } from '@antv/util'; import { deepMix } from '@antv/util';
import { toLngLat, Bounds, Point } from '@antv/geo-coord'; import { toLngLat, Bounds, Point } from '@antv/geo-coord';
import { wrapNum } from '@antv/geo-coord/lib/util/index'; import { wrapNum } from '@antv/geo-coord/lib/util/index';
import { epsg3857 } from '@antv/geo-coord/lib/geo/crs/crs-epsg3857'; import { epsg3857 } from '@antv/geo-coord/lib/geo/crs/crs-epsg3857';
@ -13,17 +15,17 @@ export default class TileLayer extends Layer {
constructor(scene, cfg) { constructor(scene, cfg) {
super(scene, { super(scene, {
...cfg, ...cfg,
minSourceZoom: 0,
maxSOurceZoom: 18,
keepBuffer: 2 keepBuffer: 2
}); });
this._tileCache = new TileCache(100, this._destroyTile); this._tileCache = new TileCache(50, this._destroyTile);
this._crs = epsg3857; this._crs = epsg3857;
this._tiles = new THREE.Object3D(); this._tiles = new THREE.Object3D();
// this._pickTiles = new THREE.Object3D();
// this._pickTiles.name = this.layerId;
// this.scene._engine._picking.add(this._pickTiles);
this._tiles.frustumCulled = false; this._tiles.frustumCulled = false;
this._tileKeys = []; this._tileKeys = [];
this.tileList = {}; this.tileList = {};
this.type = this.get('layerType');
} }
shape(field, values) { shape(field, values) {
const layerType = this.get('layerType'); const layerType = this.get('layerType');
@ -37,6 +39,8 @@ export default class TileLayer extends Layer {
this.url = url; this.url = url;
this.sourceCfg = cfg; this.sourceCfg = cfg;
this.sourceCfg.mapType = this.scene.mapType; this.sourceCfg.mapType = this.scene.mapType;
this.set('minSourceZoom', this.sourceCfg.parser && this.sourceCfg.parser.minZoom || 0);
this.set('maxSourceZoom', this.sourceCfg.parser && this.sourceCfg.parser.maxZoom || 18);
return this; return this;
} }
tileSource(data, cfg) { tileSource(data, cfg) {
@ -57,31 +61,39 @@ export default class TileLayer extends Layer {
this.set('interacionController', interactionCtr); this.set('interacionController', interactionCtr);
} }
render() { render() {
this._initControllers();
this._initMapEvent(); if (this.type !== 'image') {
// this._initAttrs(); this._initControllers();
this._initInteraction(); }
this.draw(); this._visibleWithZoom();
this._updateDraw();
this.scene._engine.update();
return this; return this;
} }
draw() { draw() {
this._object3D.add(this._tiles); this._object3D.add(this._tiles);
this._calculateLOD(); this._calculateLOD();
} }
drawTile() { drawTile() {
} }
zoomchange(ev) { zoomchange(ev) {
super.zoomchange(ev); super.zoomchange(ev);
throttle(this._calculateLOD, 200); this._visibleWithZoom();
requestAnimationFrame(() => {
this._calculateLOD();
});
// throttle(this._calculateLOD, 200);
this._calculateLOD(); this._calculateLOD();
} }
dragend(ev) { dragend(ev) {
super.dragend(ev); super.dragend(ev);
this._calculateLOD(); requestAnimationFrame(() => {
this._calculateLOD();
});
// this._calculateLOD();
} }
_calculateLOD() { _calculateLOD() {
@ -90,17 +102,22 @@ export default class TileLayer extends Layer {
* 需要显示 current * 需要显示 current
* 是否保留 retain * 是否保留 retain
*/ */
const zoom = Math.floor(this.scene.getZoom()) - 1;
const minZoom = this.get('minZoom'); const minZoom = this.get('minZoom');
const maxZoom = this.get('maxZoom'); const maxZoom = this.get('maxZoom');
const minSourceZoom = this.get('minSourceZoom');
const maxSourceZoom = this.get('maxSourceZoom');
const currentZoom = this.scene.getZoom(); const currentZoom = this.scene.getZoom();
if (currentZoom < minZoom || currentZoom > maxZoom) { this.tileZoom = zoom > maxSourceZoom ? maxSourceZoom : zoom;
this._removeOutTiles(); if (currentZoom < minZoom || currentZoom > maxZoom || currentZoom < minSourceZoom) {
this._removeTiles();
this.hide();
return; return;
} }
this.show();
this.updateTileList = []; this.updateTileList = [];
const zoom = Math.round(this.scene.getZoom()) - 1;
const center = this.scene.getCenter(); const center = this.scene.getCenter();
const centerPoint = this._crs.lngLatToPoint(toLngLat(center.lng, center.lat), zoom); const centerPoint = this._crs.lngLatToPoint(toLngLat(center.lng, center.lat), this.tileZoom);
const centerXY = centerPoint.divideBy(256).round(); const centerXY = centerPoint.divideBy(256).round();
const pixelBounds = this._getPixelBounds(); const pixelBounds = this._getPixelBounds();
const tileRange = this._pxBoundsToTileRange(pixelBounds); const tileRange = this._pxBoundsToTileRange(pixelBounds);
@ -113,7 +130,7 @@ export default class TileLayer extends Layer {
isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); } isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
for (let j = tileRange.min.y; j <= tileRange.max.y; j++) { for (let j = tileRange.min.y; j <= tileRange.max.y; j++) {
for (let i = tileRange.min.x; i <= tileRange.max.x; i++) { for (let i = tileRange.min.x; i <= tileRange.max.x; i++) {
const coords = [ i, j, zoom ]; const coords = [ i, j, this.tileZoom ];
const tile = this.tileList[coords.join('_')]; const tile = this.tileList[coords.join('_')];
if (tile) { if (tile) {
tile.current = true; tile.current = true;
@ -157,7 +174,7 @@ export default class TileLayer extends Layer {
pointShape['2d'].indexOf(shape) !== -1 || pointShape['2d'].indexOf(shape) !== -1 ||
pointShape['3d'].indexOf(shape) !== -1 pointShape['3d'].indexOf(shape) !== -1
) { ) {
return 'fill'; return shape === 'circle' ? 'circle' : 'fill';
} else if (this.scene.image.imagesIds.indexOf(shape) !== -1) { } else if (this.scene.image.imagesIds.indexOf(shape) !== -1) {
return 'image'; return 'image';
} }
@ -211,6 +228,7 @@ export default class TileLayer extends Layer {
this._pruneTiles(); this._pruneTiles();
return; return;
} }
tile.updateColor();
this._tiles.add(tile.getMesh()); this._tiles.add(tile.getMesh());
t.active = true; t.active = true;
this._addPickTile(tile.getMesh()); this._addPickTile(tile.getMesh());
@ -220,13 +238,17 @@ export default class TileLayer extends Layer {
} }
} }
_addPickTile(meshobj) { _addPickTile(meshobj) {
if (this.type === 'image') {
return;
}
const pickCtr = this.get('pickingController'); const pickCtr = this.get('pickingController');
const mesh = meshobj.children[0]; const mesh = meshobj.children[0];
mesh.name = meshobj.name;
pickCtr.addPickMesh(mesh); pickCtr.addPickMesh(mesh);
} }
// 根据距离优先级查找 // 根据距离优先级查找
getSelectFeature(id, lnglat) { getSelectFeature(id, lnglat) {
const zoom = Math.round(this.scene.getZoom()) - 1; const zoom = this.tileZoom;
const tilePoint = this._crs.lngLatToPoint(toLngLat(lnglat.lng, lnglat.lat), zoom); const tilePoint = this._crs.lngLatToPoint(toLngLat(lnglat.lng, lnglat.lat), zoom);
const tileXY = tilePoint.divideBy(256).round(); const tileXY = tilePoint.divideBy(256).round();
const key = [ tileXY.x, tileXY.y, zoom ].join('_'); const key = [ tileXY.x, tileXY.y, zoom ].join('_');
@ -236,7 +258,7 @@ export default class TileLayer extends Layer {
} }
_pruneTiles() { _pruneTiles() {
let tile; let tile;
const zoom = Math.round(this.scene.getZoom()) - 1; const zoom = this.tileZoom;
for (const key in this.tileList) { for (const key in this.tileList) {
const c = this.tileList[key].coords; const c = this.tileList[key].coords;
if (c[2] !== zoom || !this.noPruneRange.contains(new Point(c[0], c[1]))) { if (c[2] !== zoom || !this.noPruneRange.contains(new Point(c[0], c[1]))) {
@ -303,6 +325,8 @@ export default class TileLayer extends Layer {
const tileObj = this._tileCache.getTile(key); const tileObj = this._tileCache.getTile(key);
if (tileObj) { if (tileObj) {
tileObj._abortRequest(); tileObj._abortRequest();
const pickCtr = this.get('pickingController');
pickCtr && pickCtr.removePickMeshByName(tileObj.getMesh().name);
this._tiles.remove(tileObj.getMesh()); this._tiles.remove(tileObj.getMesh());
} }
if (tileObj && tileObj.getMesh().type === 'composer') { if (tileObj && tileObj.getMesh().type === 'composer') {
@ -321,6 +345,7 @@ export default class TileLayer extends Layer {
} }
}); });
} // 移除 空的geom } // 移除 空的geom
this.scene._engine.update(); this.scene._engine.update();
} }
@ -333,12 +358,17 @@ export default class TileLayer extends Layer {
for (let i = this._tiles.children.length - 1; i >= 0; i--) { for (let i = this._tiles.children.length - 1; i >= 0; i--) {
this._tiles.remove(this._tiles.children[i]); this._tiles.remove(this._tiles.children[i]);
} }
this.tileList = [];
this._tileKeys = [];
this._tileCache.destory();
const pickCtr = this.get('pickingController');
pickCtr.removeAllMesh();
} }
_getPixelBounds() { _getPixelBounds() {
const viewPort = this.scene.getBounds().toBounds(); const viewPort = this.scene.getBounds().toBounds();
const NE = viewPort.getNorthEast(); const NE = viewPort.getNorthEast();
const SW = viewPort.getSouthWest(); const SW = viewPort.getSouthWest();
const zoom = Math.round(this.scene.getZoom()) - 1; const zoom = this.tileZoom;
const center = this.scene.getCenter(); const center = this.scene.getCenter();
const NEPoint = this._crs.lngLatToPoint(toLngLat(NE.lng, NE.lat), zoom); const NEPoint = this._crs.lngLatToPoint(toLngLat(NE.lng, NE.lat), zoom);
const SWPoint = this._crs.lngLatToPoint(toLngLat(SW.lng, SW.lat), zoom); const SWPoint = this._crs.lngLatToPoint(toLngLat(SW.lng, SW.lat), zoom);
@ -379,9 +409,62 @@ export default class TileLayer extends Layer {
tile.destroy(); tile.destroy();
tile = null; tile = null;
} }
_updateAttributes() { _updateDraw() {
// 更新mapping const preAttrs = this.get('preAttrOptions');
// 更新attribute const nextAttrs = this.get('attrOptions');
const preStyle = this.get('preStyleOption');
const nextStyle = this.get('styleOptions');
if (preAttrs === undefined && preStyle === undefined) { // 首次渲染
// this._mapping();
this._setPreOption();
this._scaleByZoom();
// this._initInteraction();
this._initMapEvent();
this.draw();
this._setPreOption();
return;
}
if (!this._tiles.children.length > 0) {
this._setPreOption();
return;
}
if (!Util.isEqual(preAttrs.color, nextAttrs.color)) {
this._tiles.children.forEach(tile => {
this._tileCache.getTile(tile.name).updateColor();
this.scene._engine.update();
});
}
// 更新数据过滤 filter
if (!Util.isEqual(preAttrs.filter, nextAttrs.filter)) {
// 更新color
this._tiles.children(tile => {
this._tileCache.get(tile.name).updateColor();
});
}
// 更新Size
if (!Util.isEqual(preAttrs.size, nextAttrs.size)) {
// this._tiles.children(tile => {
// this._tileCache.get(tile.name).updateSize();
// });
}
// 更新形状
if (!Util.isEqual(preAttrs.shape, nextAttrs.shape)) {
// this._tiles.children(tile => {
// this._tileCache.get(tile.name).updateShape();
// });
}
if (!Util.isEqual(preStyle, nextStyle)) {
// 判断新增,修改,删除
const newStyle = {};
Util.each(diff(preStyle, nextStyle), ({ type, key, value }) => {
(type !== 'remove') && (newStyle[key] = value);
// newStyle[key] = type === 'remove' ? null : value;
});
this._tiles.children(tile => {
this._tileCache.get(tile.name).updateStyle();
});
}
this._setPreOption();
} }
destroy() { destroy() {
} }

View File

@ -32,6 +32,7 @@ export default class VectorTile extends Tile {
}); });
} }
_creatSource(data) { _creatSource(data) {
if (!data) return null;
this.layerSource = this.layer.tileSource(data, { this.layerSource = this.layer.tileSource(data, {
parser: { parser: {
tile: this._tile tile: this._tile
@ -50,6 +51,7 @@ export default class VectorTile extends Tile {
}; };
this.mesh.onAfterRender = renderer => { this.mesh.onAfterRender = renderer => {
const context = renderer.context; const context = renderer.context;
context.clear(context.STENCIL_BUFFER_BIT);
context.disable(context.STENCIL_TEST); context.disable(context.STENCIL_TEST);
}; };
this._object3D.add(this.mesh); this._object3D.add(this.mesh);
@ -66,9 +68,9 @@ export default class VectorTile extends Tile {
u_time: this.layer.scene._engine.clock.getElapsedTime(), u_time: this.layer.scene._engine.clock.getElapsedTime(),
u_zoom: zoom u_zoom: zoom
}); });
if (this.layer.get('layerType') === 'point') { // 点图层目前不需要mask // if (this.layer.get('layerType') === 'point') { // 点图层目前不需要mask
return; // return;
} // }
const maskScene = new THREE.Scene(); const maskScene = new THREE.Scene();
this.maskScene = maskScene; this.maskScene = maskScene;
const tileMesh = this._tileMaskMesh(); const tileMesh = this._tileMaskMesh();

View File

@ -177,7 +177,8 @@ class Linear extends Base {
return 0; return 0;
} }
const percent = (value - min) / (max - min); const percent = Math.min(1, Math.max(0, (value - min) / (max - min))); // 数据控制到 0-1 范围
// const percent = (value - min) / (max - min);
const rangeMin = this.rangeMin(); const rangeMin = this.rangeMin();
const rangeMax = this.rangeMax(); const rangeMax = this.rangeMax();
return rangeMin + percent * (rangeMax - rangeMin); return rangeMin + percent * (rangeMax - rangeMin);

View File

@ -7,13 +7,31 @@ export default function mvt(data, cfg) {
const layerName = cfg.sourceLayer; const layerName = cfg.sourceLayer;
const features = []; const features = [];
const vectorLayer = tile.layers[layerName]; const vectorLayer = tile.layers[layerName];
if (vectorLayer === undefined) {
return null;
}
for (let i = 0; i < vectorLayer.length; i++) { for (let i = 0; i < vectorLayer.length; i++) {
const feature = vectorLayer.feature(i); const feature = vectorLayer.feature(i);
features.push(feature.toGeoJSON(cfg.tile[0], cfg.tile[1], cfg.tile[2])); const geofeature = feature.toGeoJSON(cfg.tile[0], cfg.tile[1], cfg.tile[2]);
if (geofeature.geometry.type === 'Polygon' && geofeature.geometry.coordinates[0].length < 20) {
continue;
}
const newfc = {
geometry: geofeature.geometry,
type: 'Feature',
properties: {
total: geofeature.properties.total,
province: geofeature.properties.province,
bc_grade: geofeature.properties.bc_grade
}
};
features.push(newfc);
} }
const geodata = { const geodata = {
type: 'FeatureCollection', type: 'FeatureCollection',
features features
}; };
return geojson(geodata, cfg); return features.length === 0 ? null : geojson(geodata, cfg);
} }

View File

@ -9,10 +9,14 @@ export default class LRUCache {
constructor(limit = 50, destroy = () => {}) { constructor(limit = 50, destroy = () => {}) {
this.limit = limit; this.limit = limit;
this.destroy = destroy; this.destroy = destroy;
this._order = [];
this.clear(); this.clear();
} }
clear() { clear() {
this._order.forEach(key => {
this.delete(key);
});
this._cache = {}; this._cache = {};
// access/update order, first item is oldest, last item is newest // access/update order, first item is oldest, last item is newest
this._order = []; this._order = [];

View File

@ -0,0 +1,13 @@
import clamp from '@antv/util/lib/clamp';
/**
* encode 2 8-bit unsigned int into a 16-bit float
* @param {number} a 8-bit int
* @param {number} b 8-bit int
* @return {number} float
*/
export function packUint8ToFloat(a, b) {
a = clamp(Math.floor(a), 0, 255);
b = clamp(Math.floor(b), 0, 255);
return 256 * a + b;
}