diff --git a/demos/01_animatePoint.html b/demos/01_animatePoint.html
index e1ee4ae1f6..65c681a953 100644
--- a/demos/01_animatePoint.html
+++ b/demos/01_animatePoint.html
@@ -76,7 +76,7 @@ scene.on('loaded', () => {
       circleLayer.setData(pointOnCircle(timestamp / 1000));
       requestAnimationFrame(animateMarker);
   }
-   animateMarker(0);
+   //animateMarker(0);
    
    /**
    const layerText =  scene.PointLayer({
diff --git a/demos/tile.html b/demos/tile.html
index cd97a2c849..dea137e423 100644
--- a/demos/tile.html
+++ b/demos/tile.html
@@ -35,15 +35,15 @@ const scene = new L7.Scene({
 window.scene = scene;
 scene.on('loaded', () => {
   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();
 
    $.getJSON('https://gw.alipayobjects.com/os/rmsportal/JToMOWvicvJOISZFCkEI.json', city => {
     const citylayer = scene.PolygonLayer(
       {
-        zIndex:4
+        zIndex:0
       }
     )
     .source(city)
diff --git a/demos/vectorTile.html b/demos/vectorTile.html
index f3275875df..db74c5daf0 100644
--- a/demos/vectorTile.html
+++ b/demos/vectorTile.html
@@ -34,6 +34,7 @@ const scene = new L7.Scene({
 });
 window.scene = scene;
 scene.on('loaded', () => {
+
   const layer = scene.VectorTileLayer({
     zIndex:0,
     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
   // 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:{
       type: 'mvt',
       sourceLayer:'layer',
-      //idField:'OBJECTID',   
-      maxZoom: 17,
+     // idField:'adcode',   
+      maxZoom: 14,
+      minZoom: 13,
     }
   })
   .scale({
     total:{
       min:0,
-      max:1000000,
+      max:100000,
       type:'log'
     }
   })
-  .shape('hexagon')
-  .size(2)
+  .shape('circle')
+  .size(5)
   .active({fill:'red'})
   .color('total',  ['#d53e4f','#f46d43','#fdae61','#fee08b','#ffffbf','#e6f598','#abdda4','#66c2a5','#3288bd'].reverse())
   //.color('#0D408C')
   .style({
-    opacity:1.0
+     stroke: 'rgba(255,255,255,0.8)',
+     strokeWidth: 1,
+     strokeOpacity:0.6,
+     opacity: 1
   })
   .render(
   );
diff --git a/demos/vectorTilepolygon.html b/demos/vectorTilepolygon.html
index 344499e719..799ec23705 100644
--- a/demos/vectorTilepolygon.html
+++ b/demos/vectorTilepolygon.html
@@ -30,7 +30,7 @@ const scene = new L7.Scene({
   center: [116.5909,39.9225 ],
   pitch: 0,
   hash:true,
-  zoom: 14,
+  zoom: 4,
 
 });
 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
   // 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:{
       type: 'mvt',
       sourceLayer:'layer',
-      idField:'adcode',   
+      idField:'code',   
       maxZoom: 17,
     }
   })
-  .filter('province',name =>{
-    return name =='山东省'
+  .scale({
+    total:{
+      type:'linear',
+      min:0,
+      max:5000
+    }
   })
   .shape('fill')
   .size(2)
-  .active({fill:'red'})
-  .color('name',name => {
-    //var colorHash = new ColorHash();
-    return colorHash.hex(name)
-  })
+  .active(false)
+  .color('total', ['#ffffe5','#fff7bc','#fee391','#fec44f','#fe9929','#ec7014','#cc4c02','#993404','#662506'])
   .style({
     opacity:1.0
   })
-  .render(
-  );
+  .render();
   layer.on('mousemove',(feature)=>{
    console.log(feature);
   })
diff --git a/src/core/controller/buffer.js b/src/core/controller/buffer.js
new file mode 100644
index 0000000000..1f6b7081c7
--- /dev/null
+++ b/src/core/controller/buffer.js
@@ -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);
+  }
+}
diff --git a/src/core/controller/event.js b/src/core/controller/event.js
index debd66dc14..18fa5828cd 100644
--- a/src/core/controller/event.js
+++ b/src/core/controller/event.js
@@ -3,4 +3,32 @@ export default class EventContoller {
   constructor(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() {
+
+  }
 }
diff --git a/src/core/controller/index.js b/src/core/controller/index.js
index 7882f9b281..abbb44acd6 100644
--- a/src/core/controller/index.js
+++ b/src/core/controller/index.js
@@ -2,9 +2,13 @@ import Scale from './scale';
 import Mapping from './mapping';
 import Picking from './pick';
 import Interaction from './interaction';
+import Event from './event';
+import Buffer from './buffer';
 export default {
   Scale,
   Mapping,
   Picking,
-  Interaction
+  Interaction,
+  Event,
+  Buffer
 };
diff --git a/src/core/controller/mapping.js b/src/core/controller/mapping.js
index 00e845e72c..d5054d1053 100644
--- a/src/core/controller/mapping.js
+++ b/src/core/controller/mapping.js
@@ -20,13 +20,15 @@ export default class Mapping {
     this._mapping();
   }
   update() {
+    this.mesh.set('scales', {});
+    this._initTileAttrs();
     this._updateMaping();
   }
   _initControllers() {
-    const scales = this.layer.get('scaleOptions');
+    const scalesOption = this.layer.get('scaleOptions');
     const scaleController = new ScaleController({
       defs: {
-        ...scales
+        ...scalesOption
       }
     });
     this.mesh.set('scaleController', scaleController);
@@ -101,7 +103,10 @@ export default class Mapping {
     // 通过透明度过滤数据
     if (attrs.hasOwnProperty('filter')) {
       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;
diff --git a/src/core/controller/pick.js b/src/core/controller/pick.js
index 83bc22e37b..a27b79cde4 100644
--- a/src/core/controller/pick.js
+++ b/src/core/controller/pick.js
@@ -1,7 +1,7 @@
 import Util from '../../util';
 import * as THREE from '../three';
 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 {
   constructor(cfg) {
     Util.assign(this, cfg);
@@ -19,18 +19,32 @@ export default class PickContoller {
     this.layer.scene._engine._picking.remove(object);
   }
   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) {
     const pickmaterial = mesh.material.clone();
     pickmaterial.fragmentShader = pickingFragmentShader;
     const pickingMesh = new THREE[mesh.type](mesh.geometry, pickmaterial);
-    pickingMesh.name = this.layerId;
+    pickingMesh.name = mesh.name;
     pickingMesh.onBeforeRender = () => {
       const zoom = this.layer.scene.getZoom();
       updateObjecteUniform(pickingMesh, { u_zoom: zoom });
     };
     this.pickObject3D.add(pickingMesh);
-
   }
 }
diff --git a/src/core/layer.js b/src/core/layer.js
index b743bd07fe..26fb89dc19 100644
--- a/src/core/layer.js
+++ b/src/core/layer.js
@@ -107,7 +107,6 @@ export default class Layer extends Base {
     this._object3D.add(object);
     if (type === 'fill') {
       this.get('pickingController').addPickMesh(object);
-      // this._addPickMesh(object);// 不对边界线进行拾取
     }
     setTimeout(() => this.scene._engine.update(), 500);
   }
@@ -315,7 +314,8 @@ export default class Layer extends Base {
   // 重绘 度量, 映射,顶点构建
   repaint() {
     this.set('scales', {});
-    this._initControllers();
+    const mappingCtr = new Controller.Mapping({ layer: this });
+    this.set('mappingController', mappingCtr);
     // this._initAttrs();
     // this._mapping();
     this.redraw();
@@ -467,7 +467,6 @@ export default class Layer extends Base {
       // TODO 瓦片图层获取选中数据信息
       const lnglat = this.scene.containerToLngLat(point2d);
       const { feature, style } = this.getSelectFeature(featureId, lnglat);
-      // const style = this.layerData[featureId - 1];
       const target = {
         featureId,
         feature,
@@ -533,7 +532,7 @@ export default class Layer extends Base {
       offset = 5;
       this.shapeType = 'text' && (offset = 10);
 
-    } else if (this.type === 'polyline') {
+    } else if (this.type === 'polyline' || this.type === 'line') {
       offset = 2;
     } else if (this.type === 'polygon') {
       offset = 1;
@@ -551,7 +550,7 @@ export default class Layer extends Base {
     this._object3D.children.forEach(child => {
       this._object3D.remove(child);
     });
-    this.removeFromPicking(this._pickingMesh);
+    this.get('pickingController').removeAllMesh();
     this.draw();
   }
   // 更新mesh
diff --git a/src/core/source.js b/src/core/source.js
index 8b8fd97a92..a529d460e9 100644
--- a/src/core/source.js
+++ b/src/core/source.js
@@ -20,7 +20,7 @@ export default class Source extends Base {
     super(cfg);
     const transform = this.get('transforms');
     this._transforms = transform || [];
-    const mapType = this.get('mapType');
+    const mapType = this.get('mapType') || 'AMap';
     this.projectFlat = getMap(mapType).project;
     // 数据解析
     this._init();
@@ -56,10 +56,13 @@ export default class Source extends Base {
     const { type = 'geojson' } = parser;
     const data = this.get('data');
     this.originData = getParser(type)(data, parser);
-    this.data = {
-      dataArray: clone(this.originData.dataArray)
-    };
-    this.data.extent = extent(this.data.dataArray);
+    // this.data = {
+    //   dataArray: clone(this.originData.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();
   }
   _projectCoords() {
+    if (this.data === null) {
+      return;
+    }
     this.data.dataArray.forEach(data => {
       // data.coordinates = this._coordProject(data.coordinates);
       data.coordinates = tranfrormCoord(data.coordinates, this._coorConvert.bind(this));
diff --git a/src/geom/buffer/point/circleBuffer.js b/src/geom/buffer/point/circleBuffer.js
index 094f0c522f..165a655e89 100644
--- a/src/geom/buffer/point/circleBuffer.js
+++ b/src/geom/buffer/point/circleBuffer.js
@@ -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) {
   const index = [];
-  const aExtrude = [];
-  const aRadius = [];
-  const aColor = [];
-  const aPickingId = [];
   const aPosition = [];
+  const aPackedData = [];
   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
-    aExtrude.push(-1, -1, 1, -1, 1, 1, -1, 1);
-    aRadius.push(size, size, size, size);
-    aColor.push(...color, ...color, ...color, ...color);
-    aPickingId.push(id, id, id, id);
+    [
+      [ -1, -1 ],
+      [ 1, -1 ],
+      [ 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);
     index.push(...[ 0, 1, 2, 0, 2, 3 ].map(n => n + i * 4));
   });
   return {
-    aExtrude,
-    aRadius,
-    aColor,
-    aPickingId,
     aPosition,
-    index
+    index,
+    aPackedData
   };
 }
diff --git a/src/geom/shader/circle_vert.glsl b/src/geom/shader/circle_vert.glsl
index 8783f6bbc9..e6320e7f7e 100644
--- a/src/geom/shader/circle_vert.glsl
+++ b/src/geom/shader/circle_vert.glsl
@@ -1,6 +1,4 @@
-attribute float a_radius;
-attribute vec2 a_shape;
-attribute vec4 a_color;
+attribute vec4 a_packed_data;
 
 uniform float u_zoom : 1;
 uniform float u_stroke_width : 2;
@@ -11,24 +9,44 @@ varying vec3 v_data;
 varying vec4 v_color;
 varying float v_radius;
 
-void main() {
-  v_color = a_color;
-  v_radius = a_radius;
+#pragma include "decode"
+
+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);
-  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);
 
   // anti-alias
-  float antialiasblur = 1.0 / (a_radius + u_stroke_width);
+  float antialiasblur = 1.0 / (radius + u_stroke_width);
 
   // construct point coords
-  v_data = vec3(a_shape, antialiasblur);
+  v_data = vec3(extrude, antialiasblur);
 
   // picking
-  if(pickingId == u_activeId) {
+  if(picking_id == u_activeId) {
     v_color = u_activeColor;
   }
-  worldId = id_toPickColor(pickingId);
+  worldId = id_toPickColor(picking_id);
 }
\ No newline at end of file
diff --git a/src/geom/shader/index.js b/src/geom/shader/index.js
index e74c00b898..a23f341538 100644
--- a/src/geom/shader/index.js
+++ b/src/geom/shader/index.js
@@ -51,9 +51,12 @@ import mask_quard_frag from '../shader/tile/mask_quard_frag.glsl';
 import common from './common.glsl';
 import { registerModule } from '../../util/shaderModule';
 import pick_color from './shaderChunks/pick_color.glsl';
+import decode from './shaderChunks/decode.glsl';
+
 export function compileBuiltinModules() {
   registerModule('point', { vs: point_vert, fs: point_frag });
   registerModule('common', { vs: common, fs: common });
+  registerModule('decode', { vs: decode, fs: '' });
   registerModule('pick_color', { vs: pick_color, fs: pick_color });
   registerModule('circle', { vs: circle_vert, fs: circle_frag });
   registerModule('polygon', { vs: polygon_vert, fs: polygon_frag });
diff --git a/src/geom/shader/line_vert.glsl b/src/geom/shader/line_vert.glsl
index c54c2ac07f..4916b86390 100644
--- a/src/geom/shader/line_vert.glsl
+++ b/src/geom/shader/line_vert.glsl
@@ -19,5 +19,6 @@ void main() {
     //  vTime = 1.0- (28800. + mod(u_time* 10.,28800.)- position.z / 1000.) / 100.;
   #endif
   gl_Position = matModelViewProjection * vec4(position.xy, 0., 1.0);
+  gl_Position.z += 2. * gl_Position.w;
   worldId = id_toPickColor(pickingId);
 }
\ No newline at end of file
diff --git a/src/geom/shader/meshline_vert.glsl b/src/geom/shader/meshline_vert.glsl
index 2bf13b88ea..65b64f5d98 100644
--- a/src/geom/shader/meshline_vert.glsl
+++ b/src/geom/shader/meshline_vert.glsl
@@ -33,6 +33,7 @@ void main() {
   float extrude_scale = pow(2.0, 20.0 - u_zoom);
   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.z -=0.8 * gl_Position.w;
 
   #ifdef ANIMATE
     float alpha =1.0 - fract( mod(1.0- a_distance,u_interval)* (1.0/u_interval) + u_time / u_duration);
diff --git a/src/geom/shader/polygon_vert.glsl b/src/geom/shader/polygon_vert.glsl
index 815244fb97..741222b3a8 100644
--- a/src/geom/shader/polygon_vert.glsl
+++ b/src/geom/shader/polygon_vert.glsl
@@ -56,6 +56,7 @@ void main() {
      v_color = u_activeColor;
    }
   gl_Position =  matModelViewProjection * vec4(newposition, 1.0);
+  // gl_Position.z +=1.0 * gl_Position.w;
   
 
 }
\ No newline at end of file
diff --git a/src/geom/shader/shaderChunks/decode.glsl b/src/geom/shader/shaderChunks/decode.glsl
new file mode 100644
index 0000000000..afa198e6e4
--- /dev/null
+++ b/src/geom/shader/shaderChunks/decode.glsl
@@ -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
+  );
+}
\ No newline at end of file
diff --git a/src/layer/pointLayer.js b/src/layer/pointLayer.js
index 8439ccaf79..b42ed3902b 100644
--- a/src/layer/pointLayer.js
+++ b/src/layer/pointLayer.js
@@ -29,15 +29,11 @@ export default class PointLayer extends Layer {
       }
     }
 
-    // 2D circle 特殊处理
-    if (shape === 'circle') {
-      return 'circle';
-    }
     if (
       pointShape['2d'].indexOf(shape) !== -1 ||
       pointShape['3d'].indexOf(shape) !== -1
     ) {
-      return 'fill';
+      return shape === 'circle' ? 'circle' : 'fill';
     } else if (this.scene.image.imagesIds.indexOf(shape) !== -1) {
       return 'image';
     }
@@ -45,11 +41,15 @@ export default class PointLayer extends Layer {
   }
   zoomchange(ev) {
     super.zoomchange(ev);
-    this._updateData();
+    requestAnimationFrame(() => {
+      this._updateData();
+    });
   }
   dragend(ev) {
     super.dragend(ev);
-    this._updateData();
+    requestAnimationFrame(() => {
+      this._updateData();
+    });
 
   }
   _updateData() {
diff --git a/src/layer/render/point/drawCircle.js b/src/layer/render/point/drawCircle.js
index 0de9223ef2..5e9969c997 100644
--- a/src/layer/render/point/drawCircle.js
+++ b/src/layer/render/point/drawCircle.js
@@ -9,14 +9,11 @@ export default function drawCircle(layerData, layer) {
   const style = layer.get('styleOptions');
   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();
   geometry.setIndex(index);
   geometry.addAttribute('position', new THREE.Float32BufferAttribute(aPosition, 3));
-  geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(aColor, 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));
+  geometry.addAttribute('a_packed_data', new THREE.Float32BufferAttribute(aPackedData, 4));
 
   const material = new CircleMaterial({
     u_opacity: style.opacity,
@@ -28,5 +25,8 @@ export default function drawCircle(layerData, layer) {
   });
   material.depthTest = false;
   const fillMesh = new THREE.Mesh(geometry, material);
+  aPosition.length = 0;
+  aPackedData.length = 0;
+  index.length = 0;
   return fillMesh;
 }
diff --git a/src/layer/tile/imageTile.js b/src/layer/tile/imageTile.js
index 1a16b19c7d..ea655a0aca 100644
--- a/src/layer/tile/imageTile.js
+++ b/src/layer/tile/imageTile.js
@@ -16,7 +16,6 @@ export default class ImageTile extends Tile {
     const image = this._createDebugMesh();
     this._createMesh(image);
     this.emit('tileLoaded');
-    // return;
     // const urlParams = {
     //   x: this._tile[0],
     //   y: this._tile[1],
@@ -29,12 +28,10 @@ export default class ImageTile extends Tile {
     // image.addEventListener('load', () => {
     //   this._isLoaded = true;
     //   this._createMesh(image);
+    //   this.emit('tileLoaded');
     //   this._ready = true;
     // }, false);
 
-    // // image.addEventListener('progress', event => {}, false);
-    // // image.addEventListener('error', event => {}, false);
-
     // image.crossOrigin = '';
 
     // // Load image
@@ -83,6 +80,9 @@ export default class ImageTile extends Tile {
     }
 
     this._image.src = '';
+  }
+  updateColor() {
+
   }
   getSelectFeature() {
     return {};
diff --git a/src/layer/tile/imageTileLayer.js b/src/layer/tile/imageTileLayer.js
index 5417854b73..04adabee6d 100644
--- a/src/layer/tile/imageTileLayer.js
+++ b/src/layer/tile/imageTileLayer.js
@@ -2,6 +2,10 @@ import TileLayer from './tileLayer';
 import ImageTile from './imageTile';
 
 export default class ImageTileLayer extends TileLayer {
+  constructor(scene, cfg) {
+    super(scene, cfg);
+    this.type = 'image';
+  }
   _createTile(key, layer) {
     return new ImageTile(key, this.url, layer);
   }
diff --git a/src/layer/tile/tile.js b/src/layer/tile/tile.js
index f2be93bb7c..183725977c 100644
--- a/src/layer/tile/tile.js
+++ b/src/layer/tile/tile.js
@@ -21,7 +21,9 @@ export default class Tile extends Base {
     this._center = this._tileBounds.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._isLoaded = false;
@@ -29,14 +31,28 @@ export default class Tile extends Base {
   }
   _init(data) {
     this._creatSource(data);
+    if (this.layerSource.data === null) {
+      return;
+    }
+    this._initControllers();
+    this._createMesh();
+  }
+  repaint() {
     this._initControllers();
     this._createMesh();
   }
   _initControllers() {
-    this.mapping = new Controller.Mapping({
+    const mappingCtr = new Controller.Mapping({
       layer: this.layer,
       mesh: this
     });
+    const bufferCtr = new Controller.Buffer({
+      layer: this.layer,
+      mesh: this
+    });
+    this.set('mappingController', mappingCtr);
+    this.set('bufferController', bufferCtr);
+
   }
   _createMesh() {}
   _getTileURL(urlParams) {
@@ -98,10 +114,16 @@ export default class Tile extends Base {
 
     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() {
     super.destroy();
diff --git a/src/layer/tile/tileLayer.js b/src/layer/tile/tileLayer.js
index ec6df9e22d..1d64174acb 100644
--- a/src/layer/tile/tileLayer.js
+++ b/src/layer/tile/tileLayer.js
@@ -1,11 +1,13 @@
 import Layer from '../../core/layer';
+import Util from '../../util';
+import diff from '../../util/diff';
 import source from '../../core/source';
 import * as THREE from '../../core/three';
 import Controller from '../../core/controller/index';
 import Global from '../../global';
 const { pointShape } = Global;
 import TileCache from './tileCache';
-import { throttle, deepMix } from '@antv/util';
+import { deepMix } from '@antv/util';
 import { toLngLat, Bounds, Point } from '@antv/geo-coord';
 import { wrapNum } from '@antv/geo-coord/lib/util/index';
 import { epsg3857 } from '@antv/geo-coord/lib/geo/crs/crs-epsg3857';
@@ -13,17 +15,17 @@ export default class TileLayer extends Layer {
   constructor(scene, cfg) {
     super(scene, {
       ...cfg,
+      minSourceZoom: 0,
+      maxSOurceZoom: 18,
       keepBuffer: 2
     });
-    this._tileCache = new TileCache(100, this._destroyTile);
+    this._tileCache = new TileCache(50, this._destroyTile);
     this._crs = epsg3857;
     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._tileKeys = [];
     this.tileList = {};
+    this.type = this.get('layerType');
   }
   shape(field, values) {
     const layerType = this.get('layerType');
@@ -37,6 +39,8 @@ export default class TileLayer extends Layer {
     this.url = url;
     this.sourceCfg = cfg;
     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;
   }
   tileSource(data, cfg) {
@@ -57,31 +61,39 @@ export default class TileLayer extends Layer {
     this.set('interacionController', interactionCtr);
   }
   render() {
-    this._initControllers();
-    this._initMapEvent();
-    // this._initAttrs();
-    this._initInteraction();
-    this.draw();
+
+    if (this.type !== 'image') {
+      this._initControllers();
+    }
+    this._visibleWithZoom();
+    this._updateDraw();
+    this.scene._engine.update();
     return this;
   }
   draw() {
     this._object3D.add(this._tiles);
     this._calculateLOD();
   }
-
   drawTile() {
 
   }
 
   zoomchange(ev) {
     super.zoomchange(ev);
-    throttle(this._calculateLOD, 200);
+    this._visibleWithZoom();
+    requestAnimationFrame(() => {
+      this._calculateLOD();
+    });
+    // throttle(this._calculateLOD, 200);
     this._calculateLOD();
   }
 
   dragend(ev) {
     super.dragend(ev);
-    this._calculateLOD();
+    requestAnimationFrame(() => {
+      this._calculateLOD();
+    });
+    // this._calculateLOD();
 
   }
   _calculateLOD() {
@@ -90,17 +102,22 @@ export default class TileLayer extends Layer {
      * 需要显示 current
      * 是否保留 retain
      */
+    const zoom = Math.floor(this.scene.getZoom()) - 1;
     const minZoom = this.get('minZoom');
     const maxZoom = this.get('maxZoom');
+    const minSourceZoom = this.get('minSourceZoom');
+    const maxSourceZoom = this.get('maxSourceZoom');
     const currentZoom = this.scene.getZoom();
-    if (currentZoom < minZoom || currentZoom > maxZoom) {
-      this._removeOutTiles();
+    this.tileZoom = zoom > maxSourceZoom ? maxSourceZoom : zoom;
+    if (currentZoom < minZoom || currentZoom > maxZoom || currentZoom < minSourceZoom) {
+      this._removeTiles();
+      this.hide();
       return;
     }
+    this.show();
     this.updateTileList = [];
-    const zoom = Math.round(this.scene.getZoom()) - 1;
     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 pixelBounds = this._getPixelBounds();
     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'); }
     for (let j = tileRange.min.y; j <= tileRange.max.y; j++) {
       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('_')];
         if (tile) {
           tile.current = true;
@@ -157,7 +174,7 @@ export default class TileLayer extends Layer {
       pointShape['2d'].indexOf(shape) !== -1 ||
       pointShape['3d'].indexOf(shape) !== -1
     ) {
-      return 'fill';
+      return shape === 'circle' ? 'circle' : 'fill';
     } else if (this.scene.image.imagesIds.indexOf(shape) !== -1) {
       return 'image';
     }
@@ -211,6 +228,7 @@ export default class TileLayer extends Layer {
         this._pruneTiles();
         return;
       }
+      tile.updateColor();
       this._tiles.add(tile.getMesh());
       t.active = true;
       this._addPickTile(tile.getMesh());
@@ -220,13 +238,17 @@ export default class TileLayer extends Layer {
     }
   }
   _addPickTile(meshobj) {
+    if (this.type === 'image') {
+      return;
+    }
     const pickCtr = this.get('pickingController');
     const mesh = meshobj.children[0];
+    mesh.name = meshobj.name;
     pickCtr.addPickMesh(mesh);
   }
   // 根据距离优先级查找
   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 tileXY = tilePoint.divideBy(256).round();
     const key = [ tileXY.x, tileXY.y, zoom ].join('_');
@@ -236,7 +258,7 @@ export default class TileLayer extends Layer {
   }
   _pruneTiles() {
     let tile;
-    const zoom = Math.round(this.scene.getZoom()) - 1;
+    const zoom = this.tileZoom;
     for (const key in this.tileList) {
       const c = this.tileList[key].coords;
       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);
         if (tileObj) {
           tileObj._abortRequest();
+          const pickCtr = this.get('pickingController');
+          pickCtr && pickCtr.removePickMeshByName(tileObj.getMesh().name);
           this._tiles.remove(tileObj.getMesh());
         }
         if (tileObj && tileObj.getMesh().type === 'composer') {
@@ -321,6 +345,7 @@ export default class TileLayer extends Layer {
         }
       });
     } // 移除 空的geom
+
     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--) {
       this._tiles.remove(this._tiles.children[i]);
     }
+    this.tileList = [];
+    this._tileKeys = [];
+    this._tileCache.destory();
+    const pickCtr = this.get('pickingController');
+    pickCtr.removeAllMesh();
   }
   _getPixelBounds() {
     const viewPort = this.scene.getBounds().toBounds();
     const NE = viewPort.getNorthEast();
     const SW = viewPort.getSouthWest();
-    const zoom = Math.round(this.scene.getZoom()) - 1;
+    const zoom = this.tileZoom;
     const center = this.scene.getCenter();
     const NEPoint = this._crs.lngLatToPoint(toLngLat(NE.lng, NE.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 = null;
   }
-  _updateAttributes() {
-      // 更新mapping
-      // 更新attribute
+  _updateDraw() {
+    const preAttrs = this.get('preAttrOptions');
+    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() {
   }
diff --git a/src/layer/tile/vectorTile.js b/src/layer/tile/vectorTile.js
index 984a377ba3..571dc32a02 100644
--- a/src/layer/tile/vectorTile.js
+++ b/src/layer/tile/vectorTile.js
@@ -32,6 +32,7 @@ export default class VectorTile extends Tile {
     });
   }
   _creatSource(data) {
+    if (!data) return null;
     this.layerSource = this.layer.tileSource(data, {
       parser: {
         tile: this._tile
@@ -50,6 +51,7 @@ export default class VectorTile extends Tile {
       };
       this.mesh.onAfterRender = renderer => {
         const context = renderer.context;
+        context.clear(context.STENCIL_BUFFER_BIT);
         context.disable(context.STENCIL_TEST);
       };
       this._object3D.add(this.mesh);
@@ -66,9 +68,9 @@ export default class VectorTile extends Tile {
       u_time: this.layer.scene._engine.clock.getElapsedTime(),
       u_zoom: zoom
     });
-    if (this.layer.get('layerType') === 'point') { // 点图层目前不需要mask
-      return;
-    }
+    // if (this.layer.get('layerType') === 'point') { // 点图层目前不需要mask
+    //   return;
+    // }
     const maskScene = new THREE.Scene();
     this.maskScene = maskScene;
     const tileMesh = this._tileMaskMesh();
diff --git a/src/scale/linear.js b/src/scale/linear.js
index a80e5f732f..9de8dcdbb9 100644
--- a/src/scale/linear.js
+++ b/src/scale/linear.js
@@ -177,7 +177,8 @@ class Linear extends Base {
       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 rangeMax = this.rangeMax();
     return rangeMin + percent * (rangeMax - rangeMin);
diff --git a/src/source/parser/mvt.js b/src/source/parser/mvt.js
index 7e5165488b..1d77d9592e 100644
--- a/src/source/parser/mvt.js
+++ b/src/source/parser/mvt.js
@@ -7,13 +7,31 @@ export default function mvt(data, cfg) {
   const layerName = cfg.sourceLayer;
   const features = [];
   const vectorLayer = tile.layers[layerName];
+  if (vectorLayer === undefined) {
+    return null;
+  }
   for (let i = 0; i < vectorLayer.length; 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 = {
     type: 'FeatureCollection',
     features
   };
-  return geojson(geodata, cfg);
+  return features.length === 0 ? null : geojson(geodata, cfg);
 }
diff --git a/src/util/lru-cache.js b/src/util/lru-cache.js
index d44c2626dc..686cf2fb9d 100644
--- a/src/util/lru-cache.js
+++ b/src/util/lru-cache.js
@@ -9,10 +9,14 @@ export default class LRUCache {
   constructor(limit = 50, destroy = () => {}) {
     this.limit = limit;
     this.destroy = destroy;
+    this._order = [];
     this.clear();
   }
 
   clear() {
+    this._order.forEach(key => {
+      this.delete(key);
+    });
     this._cache = {};
     // access/update order, first item is oldest, last item is newest
     this._order = [];
diff --git a/src/util/vertex-compress.js b/src/util/vertex-compress.js
new file mode 100644
index 0000000000..075020fb78
--- /dev/null
+++ b/src/util/vertex-compress.js
@@ -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;
+}