text: (function(global, factory) {
      typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
          typeof define === 'function' && define.amd ? define(['exports'], factory) :
          (factory((global.mapv = global.mapv || {})));
  }(this, (function(exports) {
      'use strict';
  
      var version = "2.0.13";
  
      /**
       * @author kyle / http://nikai.us/
       */
  
      var clear = function(context) {
          context && context.clearRect && context.clearRect(0, 0, context.canvas.width, context.canvas.height);
          //context.canvas.width = context.canvas.width;
          //context.canvas.height = context.canvas.height;
      };
  
      /**
       * @author kyle / http://nikai.us/
       */
  
      var resolutionScale$1 = function(context) {
          var devicePixelRatio = window.devicePixelRatio;
          context.canvas.width = context.canvas.width * devicePixelRatio;
          context.canvas.height = context.canvas.height * devicePixelRatio;
          context.canvas.style.width = context.canvas.width / devicePixelRatio + 'px';
          context.canvas.style.height = context.canvas.height / devicePixelRatio + 'px';
          context.scale(devicePixelRatio, devicePixelRatio);
      };
  
      function Event() {
          this._subscribers = {}; // event subscribers
      }
  
      /**
       * Subscribe to an event, add an event listener
       * @param {String} event        Event name. Available events: 'put', 'update',
       *                              'remove'
       * @param {function} callback   Callback method. Called with three parameters:
       *                                  {String} event
       *                                  {Object | null} params
       *                                  {String | Number} senderId
       */
      Event.prototype.on = function(event, callback) {
          var subscribers = this._subscribers[event];
          if (!subscribers) {
              subscribers = [];
              this._subscribers[event] = subscribers;
          }
  
          subscribers.push({
              callback: callback
          });
      };
  
      /**
       * Unsubscribe from an event, remove an event listener
       * @param {String} event
       * @param {function} callback
       */
      Event.prototype.off = function(event, callback) {
          var subscribers = this._subscribers[event];
          if (subscribers) {
              //this._subscribers[event] = subscribers.filter(listener => listener.callback != callback);
              for (var i = 0; i < subscribers.length; i++) {
                  if (subscribers[i].callback == callback) {
                      subscribers.splice(i, 1);
                      i--;
                  }
              }
          }
      };
  
      /**
       * Trigger an event
       * @param {String} event
       * @param {Object | null} params
       * @param {String} [senderId]       Optional id of the sender.
       * @private
       */
      Event.prototype._trigger = function(event, params, senderId) {
          if (event == '*') {
              throw new Error('Cannot trigger event *');
          }
  
          var subscribers = [];
          if (event in this._subscribers) {
              subscribers = subscribers.concat(this._subscribers[event]);
          }
          if ('*' in this._subscribers) {
              subscribers = subscribers.concat(this._subscribers['*']);
          }
  
          for (var i = 0, len = subscribers.length; i < len; i++) {
              var subscriber = subscribers[i];
              if (subscriber.callback) {
                  subscriber.callback(event, params, senderId || null);
              }
          }
      };
  
      var classCallCheck = function(instance, Constructor) {
          if (!(instance instanceof Constructor)) {
              throw new TypeError("Cannot call a class as a function");
          }
      };
  
      var createClass = function() {
          function defineProperties(target, props) {
              for (var i = 0; i < props.length; i++) {
                  var descriptor = props[i];
                  descriptor.enumerable = descriptor.enumerable || false;
                  descriptor.configurable = true;
                  if ("value" in descriptor) descriptor.writable = true;
                  Object.defineProperty(target, descriptor.key, descriptor);
              }
          }
  
          return function(Constructor, protoProps, staticProps) {
              if (protoProps) defineProperties(Constructor.prototype, protoProps);
              if (staticProps) defineProperties(Constructor, staticProps);
              return Constructor;
          };
      }();
  
  
  
      var get = function get(object, property, receiver) {
          if (object === null) object = Function.prototype;
          var desc = Object.getOwnPropertyDescriptor(object, property);
  
          if (desc === undefined) {
              var parent = Object.getPrototypeOf(object);
  
              if (parent === null) {
                  return undefined;
              } else {
                  return get(parent, property, receiver);
              }
          } else if ("value" in desc) {
              return desc.value;
          } else {
              var getter = desc.get;
  
              if (getter === undefined) {
                  return undefined;
              }
  
              return getter.call(receiver);
          }
      };
  
      var inherits = function(subClass, superClass) {
          if (typeof superClass !== "function" && superClass !== null) {
              throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
          }
  
          subClass.prototype = Object.create(superClass && superClass.prototype, {
              constructor: {
                  value: subClass,
                  enumerable: false,
                  writable: true,
                  configurable: true
              }
          });
          if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
      };
  
  
  
      var possibleConstructorReturn = function(self, call) {
          if (!self) {
              throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
          }
  
          return call && (typeof call === "object" || typeof call === "function") ? call : self;
      };
  
      /**
       * @author kyle / http://nikai.us/
       */
  
      /**
       * DataSet
       *
       * A data set can:
       * - add/remove/update data
       * - gives triggers upon changes in the data
       * - can  import/export data in various data formats
       * @param {Array} [data]    Optional array with initial data
       * the field geometry is like geojson, it can be:
       * {
       *     "type": "Point",
       *     "coordinates": [125.6, 10.1]
       * }
       * {
       *     "type": "LineString",
       *     "coordinates": [
       *         [102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]
       *     ]
       * }
       * {
       *     "type": "Polygon",
       *     "coordinates": [
       *         [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],
       *           [100.0, 1.0], [100.0, 0.0] ]
       *     ]
       * }
       * @param {Object} [options]   Available options:
       * 
       */
      function DataSet(data, options) {
  
          this._options = options || {};
          this._data = []; // map with data indexed by id
  
          // add initial data when provided
          if (data) {
              this.add(data);
          }
      }
  
      DataSet.prototype = new Event();
  
      /**
       * Add data.
       */
      DataSet.prototype.add = function(data, senderId) {
          if (Array.isArray(data)) {
              // Array
              for (var i = 0, len = data.length; i < len; i++) {
                  if (data[i].time && data[i].time.length == 14 && data[i].time.substr(0, 2) == '20') {
                      var time = data[i].time;
                      data[i].time = new Date(time.substr(0, 4) + '-' + time.substr(4, 2) + '-' + time.substr(6, 2) + ' ' + time.substr(8, 2) + ':' + time.substr(10, 2) + ':' + time.substr(12, 2)).getTime();
                  }
                  this._data.push(data[i]);
              }
          } else if (data instanceof Object) {
              // Single item
              this._data.push(data);
          } else {
              throw new Error('Unknown dataType');
          }
      };
  
      /**
       * get data.
       */
      DataSet.prototype.get = function(args) {
          args = args || {};
  
          //console.time('copy data time')
          var start = new Date();
          // TODO: 不修改原始数据，在数据上挂载新的名称，每次修改数据直接修改新名称下的数据，可以省去deepCopy
          // var data = deepCopy(this._data);
          var data = this._data;
  
          // console.timeEnd('copy data time')
  
          // console.time('transferCoordinate time')
  
          var start = new Date();
  
          if (args.filter) {
              var newData = [];
              for (var i = 0; i < data.length; i++) {
                  if (args.filter(data[i])) {
                      newData.push(data[i]);
                  }
              }
              data = newData;
          }
  
          if (args.transferCoordinate) {
              data = this.transferCoordinate(data, args.transferCoordinate, args.fromColumn, args.toColumn);
          }
  
          // console.timeEnd('transferCoordinate time')
  
          return data;
      };
  
      /**
       * set data.
       */
      DataSet.prototype.set = function(data) {
          this._set(data);
          this._trigger('change');
      };
  
      /**
       * set data.
       */
      DataSet.prototype._set = function(data) {
          this.clear();
          this.add(data);
      };
  
      /**
       * clear data.
       */
      DataSet.prototype.clear = function(args) {
          this._data = []; // map with data indexed by id
      };
  
      /**
       * remove data.
       */
      DataSet.prototype.remove = function(args) {};
  
      /**
       * update data.
       */
      DataSet.prototype.update = function(args) {};
  
      /**
       * transfer coordinate.
       */
      DataSet.prototype.transferCoordinate = function(data, transferFn, fromColumn, toColumnName) {
  
          toColumnName = toColumnName || '_coordinates';
          fromColumn = fromColumn || 'coordinates';
  
          for (var i = 0; i < data.length; i++) {
  
              var geometry = data[i].geometry;
              var coordinates = geometry[fromColumn];
              switch (geometry.type) {
                  case 'Point':
                      geometry[toColumnName] = transferFn(coordinates);
                      break;
                  case 'LineString':
                      var newCoordinates = [];
                      for (var j = 0; j < coordinates.length; j++) {
                          newCoordinates.push(transferFn(coordinates[j]));
                      }
                      geometry[toColumnName] = newCoordinates;
                      break;
                  case 'Polygon':
                      var newCoordinates = getPolygon(coordinates);
                      geometry[toColumnName] = newCoordinates;
                      break;
                  case 'MultiPolygon':
                      var newCoordinates = [];
                      for (var c = 0; c < coordinates.length; c++) {
                          var polygon = coordinates[c];
                          var polygon = getPolygon(polygon);
                          newCoordinates.push(polygon);
                      }
  
                      geometry[toColumnName] = newCoordinates;
                      break;
              }
          }
  
          function getPolygon(coordinates) {
              var newCoordinates = [];
              for (var c = 0; c < coordinates.length; c++) {
                  var coordinate = coordinates[c];
                  var newcoordinate = [];
                  for (var j = 0; j < coordinate.length; j++) {
                      newcoordinate.push(transferFn(coordinate[j]));
                  }
                  newCoordinates.push(newcoordinate);
              }
              return newCoordinates;
          }
  
          return data;
      };
  
      DataSet.prototype.initGeometry = function(transferFn) {
  
          if (transferFn) {
  
              this._data.forEach(function(item) {
                  item.geometry = transferFn(item);
              });
          } else {
  
              this._data.forEach(function(item) {
                  if (!item.geometry && item.lng && item.lat) {
                      item.geometry = {
                          type: 'Point',
                          coordinates: [item.lng, item.lat]
                      };
                  }
              });
          }
      };
  
      /**
       * 获取当前列的最大值
       */
      DataSet.prototype.getMax = function(columnName) {
          var data = this._data;
  
          if (!data || data.length <= 0) {
              return;
          }
  
          var max = parseFloat(data[0][columnName]);
  
          for (var i = 1; i < data.length; i++) {
              var value = parseFloat(data[i][columnName]);
              if (value > max) {
                  max = value;
              }
          }
  
          return max;
      };
  
      /**
       * 获取当前列的总和
       */
      DataSet.prototype.getSum = function(columnName) {
          var data = this._data;
  
          if (!data || data.length <= 0) {
              return;
          }
  
          var sum = 0;
  
          for (var i = 0; i < data.length; i++) {
              if (data[i][columnName]) {
                  sum += parseFloat(data[i][columnName]);
              }
          }
  
          return sum;
      };
  
      /**
       * 获取当前列的最小值
       */
      DataSet.prototype.getMin = function(columnName) {
          var data = this._data;
  
          if (!data || data.length <= 0) {
              return;
          }
  
          var min = parseFloat(data[0][columnName]);
  
          for (var i = 1; i < data.length; i++) {
              var value = parseFloat(data[i][columnName]);
              if (value < min) {
                  min = value;
              }
          }
  
          return min;
      };
  
      /**
       * @author kyle / http://nikai.us/
       */
  
      var pathSimple = {
          drawDataSet: function drawDataSet(context, dataSet, options) {
  
              var data = dataSet instanceof DataSet ? dataSet.get() : dataSet;
  
              for (var i = 0, len = data.length; i < len; i++) {
                  var item = data[i];
                  this.draw(context, item, options);
              }
          },
          draw: function draw(context, data, options) {
              var type = data.geometry.type;
              var coordinates = data.geometry._coordinates || data.geometry.coordinates;
              var symbol = options.symbol || 'circle';
              switch (type) {
                  case 'Point':
                      var size = data._size || data.size || options._size || options.size || 5;
                      if (options.symbol === 'rect') {
                          context.rect(coordinates[0] - size / 2, coordinates[1] - size / 2, size, size);
                      } else {
                          if (options.bigData === 'Point') {
                              context.moveTo(coordinates[0], coordinates[1]);
                          }
                          context.arc(coordinates[0], coordinates[1], size, 0, Math.PI * 2);
                      }
                      break;
                  case 'LineString':
                      for (var j = 0; j < coordinates.length; j++) {
                          var x = coordinates[j][0];
                          var y = coordinates[j][1];
                          if (j == 0) {
                              context.moveTo(x, y);
                          } else {
                              context.lineTo(x, y);
                          }
                      }
                      break;
                  case 'Polygon':
                      this.drawPolygon(context, coordinates);
                      break;
                  case 'MultiPolygon':
                      for (var i = 0; i < coordinates.length; i++) {
                          var polygon = coordinates[i];
                          this.drawPolygon(context, polygon);
                      }
                      context.closePath();
                      break;
                  default:
                      console.log('type' + type + 'is not support now!');
                      break;
              }
          },
  
          drawPolygon: function drawPolygon(context, coordinates) {
  
              for (var i = 0; i < coordinates.length; i++) {
  
                  var coordinate = coordinates[i];
  
                  context.moveTo(coordinate[0][0], coordinate[0][1]);
                  for (var j = 1; j < coordinate.length; j++) {
                      context.lineTo(coordinate[j][0], coordinate[j][1]);
                  }
                  context.lineTo(coordinate[0][0], coordinate[0][1]);
              }
          }
  
      };
  
      /**
       * @author kyle / http://nikai.us/
       */
  
      var drawSimple = {
          draw: function draw(context, dataSet, options) {
  
              var data = dataSet instanceof DataSet ? dataSet.get() : dataSet;
  
              // console.log('xxxx',options)
              context.save();
  
              for (var key in options) {
                  context[key] = options[key];
              }
  
              // console.log(data);
              if (options.bigData) {
                  context.save();
                  context.beginPath();
  
                  for (var i = 0, len = data.length; i < len; i++) {
  
                      var item = data[i];
  
                      pathSimple.draw(context, item, options);
                  }
  
                  var type = options.bigData;
  
                  if (type == 'Point' || type == 'Polygon' || type == 'MultiPolygon') {
  
                      context.fill();
  
                      if ((item.strokeStyle || options.strokeStyle) && options.lineWidth) {
                          context.stroke();
                      }
                  } else if (type == 'LineString') {
                      context.stroke();
                  }
  
                  context.restore();
              } else {
                  for (var i = 0, len = data.length; i < len; i++) {
  
                      var item = data[i];
  
                      context.save();
  
                      if (item.fillStyle) {
                          context.fillStyle = item.fillStyle;
                      }
  
                      if (item.strokeStyle) {
                          context.strokeStyle = item.strokeStyle;
                      }
  
                      var type = item.geometry.type;
  
                      context.beginPath();
  
                      pathSimple.draw(context, item, options);
  
                      if (type == 'Point' || type == 'Polygon' || type == 'MultiPolygon') {
  
                          context.fill();
  
                          if ((item.strokeStyle || options.strokeStyle) && options.lineWidth) {
                              context.stroke();
                          }
                      } else if (type == 'LineString') {
                          context.stroke();
                      }
  
                      context.restore();
                  }
              }
  
              context.restore();
          }
      };
  
      function Canvas(width, height) {
  
          var canvas;
  
          if (typeof document === 'undefined') {
  
              var Canvas = require('canvas');
              canvas = new Canvas(width, height);
          } else {
  
              var canvas = document.createElement('canvas');
  
              if (width) {
                  canvas.width = width;
              }
  
              if (height) {
                  canvas.height = height;
              }
          }
  
          return canvas;
      }
  
      /**
       * @author kyle / http://nikai.us/
       */
  
      /**
       * Category
       * @param {Object} [options]   Available options:
       *                             {Object} gradient: { 0.25: "rgb(0,0,255)", 0.55: "rgb(0,255,0)", 0.85: "yellow", 1.0: "rgb(255,0,0)"}
       */
      function Intensity(options) {
  
          options = options || {};
          this.gradient = options.gradient || {
              0.25: "rgba(0, 0, 255, 1)",
              0.55: "rgba(0, 255, 0, 1)",
              0.85: "rgba(255, 255, 0, 1)",
              1.0: "rgba(255, 0, 0, 1)"
          };
          this.maxSize = options.maxSize || 35;
          this.minSize = options.minSize || 0;
          this.max = options.max || 100;
          this.min = options.min || 0;
          this.initPalette();
      }
  
      Intensity.prototype.setMax = function(value) {
          this.max = value || 100;
      };
  
      Intensity.prototype.setMin = function(value) {
          this.min = value || 0;
      };
  
      Intensity.prototype.setMaxSize = function(maxSize) {
          this.maxSize = maxSize || 35;
      };
  
      Intensity.prototype.setMinSize = function(minSize) {
          this.minSize = minSize || 0;
      };
  
      Intensity.prototype.initPalette = function() {
  
          var gradient = this.gradient;
  
          var canvas = new Canvas(256, 1);
  
          var paletteCtx = this.paletteCtx = canvas.getContext('2d');
  
          var lineGradient = paletteCtx.createLinearGradient(0, 0, 256, 1);
  
          for (var key in gradient) {
              lineGradient.addColorStop(parseFloat(key), gradient[key]);
          }
  
          paletteCtx.fillStyle = lineGradient;
          paletteCtx.fillRect(0, 0, 256, 1);
      };
  
      Intensity.prototype.getColor = function(value) {
  
          var imageData = this.getImageData(value);
  
          return "rgba(" + imageData[0] + ", " + imageData[1] + ", " + imageData[2] + ", " + imageData[3] / 256 + ")";
      };
  
      Intensity.prototype.getImageData = function(value) {
  
          var imageData = this.paletteCtx.getImageData(0, 0, 256, 1).data;
  
          if (value === undefined) {
              return imageData;
          }
  
          var max = this.max;
          var min = this.min;
  
          if (value > max) {
              value = max;
          }
  
          if (value < min) {
              value = min;
          }
  
          var index = Math.floor((value - min) / (max - min) * (256 - 1)) * 4;
  
          return [imageData[index], imageData[index + 1], imageData[index + 2], imageData[index + 3]];
      };
  
      /**
       * @param Number value 
       * @param Number max of value
       * @param Number max of size
       * @param Object other options
       */
      Intensity.prototype.getSize = function(value) {
  
          var size = 0;
          var max = this.max;
          var min = this.min;
          var maxSize = this.maxSize;
          var minSize = this.minSize;
  
          if (value > max) {
              value = max;
          }
  
          if (value < min) {
              value = min;
          }
  
          size = minSize + (value - min) / (max - min) * (maxSize - minSize);
  
          return size;
      };
  
      Intensity.prototype.getLegend = function(options) {
          var gradient = this.gradient;
  
          var width = options.width || 20;
          var height = options.height || 180;
  
          var canvas = new Canvas(width, height);
  
          var paletteCtx = canvas.getContext('2d');
  
          var lineGradient = paletteCtx.createLinearGradient(0, height, 0, 0);
  
          for (var key in gradient) {
              lineGradient.addColorStop(parseFloat(key), gradient[key]);
          }
  
          paletteCtx.fillStyle = lineGradient;
          paletteCtx.fillRect(0, 0, width, height);
  
          return canvas;
      };
  
      var global$1 = typeof window === 'undefined' ? {} : window;
  
      var devicePixelRatio = global$1.devicePixelRatio;
  
      /**
       * @author kyle / http://nikai.us/
       */
  
      function createCircle(size) {
  
          var shadowBlur = size / 2;
          var r2 = size + shadowBlur;
          var offsetDistance = 10000;
  
          var circle = new Canvas(r2 * 2, r2 * 2);
          var context = circle.getContext('2d');
  
          context.shadowBlur = shadowBlur;
          context.shadowColor = 'black';
          context.shadowOffsetX = context.shadowOffsetY = offsetDistance;
  
          context.beginPath();
          context.arc(r2 - offsetDistance, r2 - offsetDistance, size, 0, Math.PI * 2, true);
          context.closePath();
          context.fill();
          return circle;
      }
  
      function colorize(pixels, gradient, options) {
  
          var maxOpacity = options.maxOpacity || 0.8;
          for (var i = 3, len = pixels.length, j; i < len; i += 4) {
              j = pixels[i] * 4; // get gradient color from opacity value
  
              if (pixels[i] / 256 > maxOpacity) {
                  pixels[i] = 256 * maxOpacity;
              }
  
              pixels[i - 3] = gradient[j];
              pixels[i - 2] = gradient[j + 1];
              pixels[i - 1] = gradient[j + 2];
          }
      }
  
      function drawGray(context, dataSet, options) {
  
          var max = options.max || 100;
          var min = options.min || 0;
          // console.log(max)
          var size = options._size;
          if (size == undefined) {
              size = options.size;
              if (size == undefined) {
                  size = 13;
              }
          }
  
          var intensity = new Intensity({
              gradient: options.gradient,
              max: max,
              min: min
          });
  
          var circle = createCircle(size);
          var circleHalfWidth = circle.width / 2;
          var circleHalfHeight = circle.height / 2;
  
          var data = dataSet;
  
          var dataOrderByAlpha = {};
  
          data.forEach(function(item, index) {
              var count = item.count === undefined ? 1 : item.count;
              var alpha = Math.min(1, count / max).toFixed(2);
              dataOrderByAlpha[alpha] = dataOrderByAlpha[alpha] || [];
              dataOrderByAlpha[alpha].push(item);
          });
  
          for (var i in dataOrderByAlpha) {
              if (isNaN(i)) continue;
              var _data = dataOrderByAlpha[i];
              context.beginPath();
              if (!options.withoutAlpha) {
                  context.globalAlpha = i;
              }
              context.strokeStyle = intensity.getColor(i * max);
              _data.forEach(function(item, index) {
                  if (!item.geometry) {
                      return;
                  }
  
                  var coordinates = item.geometry._coordinates || item.geometry.coordinates;
                  var type = item.geometry.type;
                  if (type === 'Point') {
                      var count = item.count === undefined ? 1 : item.count;
                      context.globalAlpha = count / max;
                      context.drawImage(circle, coordinates[0] - circleHalfWidth, coordinates[1] - circleHalfHeight);
                  } else if (type === 'LineString') {
                      var count = item.count === undefined ? 1 : item.count;
                      context.globalAlpha = count / max;
                      context.beginPath();
                      pathSimple.draw(context, item, options);
                      context.stroke();
                  } else if (type === 'Polygon') {}
              });
          }
      }
  
      function draw(context, dataSet, options) {
          var strength = options.strength || 0.3;
          context.strokeStyle = 'rgba(0,0,0,' + strength + ')';
          var shadowCanvas = new Canvas(context.canvas.width, context.canvas.height);
          var shadowContext = shadowCanvas.getContext('2d');
          shadowContext.scale(devicePixelRatio, devicePixelRatio);
  
          options = options || {};
  
          var data = dataSet instanceof DataSet ? dataSet.get() : dataSet;
  
          context.save();
  
          var intensity = new Intensity({
              gradient: options.gradient
          });
  
          //console.time('drawGray')
          drawGray(shadowContext, data, options);
  
          //console.timeEnd('drawGray');
          // return false;
          if (!options.absolute) {
              //console.time('changeColor');
              var colored = shadowContext.getImageData(0, 0, context.canvas.width, context.canvas.height);
              colorize(colored.data, intensity.getImageData(), options);
              //console.timeEnd('changeColor');
              context.putImageData(colored, 0, 0);
  
              context.restore();
          }
  
          intensity = null;
          shadowCanvas = null;
      }
  
      var drawHeatmap = {
          draw: draw
      };
  
      /**
       * @author kyle / http://nikai.us/
       */
  
      var drawGrid = {
          draw: function draw(context, dataSet, options) {
  
              context.save();
  
              var data = dataSet instanceof DataSet ? dataSet.get() : dataSet;
  
              var grids = {};
  
              var gridIndexData = {};
  
              var size = options._size || options.size || 50;
  
              var offset = options.offset || {
                  x: 0,
                  y: 0
              };
  
              for (var i = 0; i < data.length; i++) {
                  var coordinates = data[i].geometry._coordinates || data[i].geometry.coordinates;
                  var gridKey = Math.floor((coordinates[0] - offset.x) / size) + "," + Math.floor((coordinates[1] - offset.y) / size);
                  if (!grids[gridKey]) {
                      grids[gridKey] = 0;
                      gridIndexData[gridKey] = [];
                  }
                  var count = ~~(data[i].count || 1)
                  grids[gridKey] += count;
                  gridIndexData[gridKey].push(data[i]);
              };
  
              var intensity = new Intensity({
                  max: options.max || 100,
                  gradient: options.gradient
              });
  
              for (var gridKey in grids) {
                  gridKey = gridKey.split(",");
  
                  context.beginPath();
                  context.rect(gridKey[0] * size + .5 + offset.x, gridKey[1] * size + .5 + offset.y, size, size);
                  var text = grids[gridKey];
                  if (options.label.average) {
                      text = parseInt(grids[gridKey] / gridIndexData[gridKey].length)
                  }
                  
                  context.fillStyle = intensity.getColor(text);
                  context.fill();
                  if (options.strokeStyle && options.lineWidth) {
                      context.stroke();
                  }
              }
  
              if (options.label && options.label.show !== false) {
  
                  context.fillStyle = options.label.fillStyle || 'white';
  
                  if (options.label.font) {
                      context.font = options.label.font;
                  }
  
                  if (options.label.shadowColor) {
                      context.shadowColor = options.label.shadowColor;
                  }
  
                  if (options.label.shadowBlur) {
                      context.shadowBlur = options.label.shadowBlur;
                  }
  
                  for (var gridKey in grids) {
                      gridKey = gridKey.split(",");
                      var text = grids[gridKey];
                      if (options.label.average) {
                          text = parseInt(text / gridIndexData[gridKey].length)
                      }
                      var textWidth = context.measureText(text).width;
                      context.fillText(text, gridKey[0] * size + .5 + offset.x + size / 2 - textWidth / 2, gridKey[1] * size + .5 + offset.y + size / 2 + 5);
                  }
              }
  
              context.restore();
          }
      };
  
      /**
       * @author kyle / http://nikai.us/
       */
  
      function hex_corner(center, size, i) {
          var angle_deg = 60 * i + 30;
          var angle_rad = Math.PI / 180 * angle_deg;
          return [center.x + size * Math.cos(angle_rad), center.y + size * Math.sin(angle_rad)];
      }
  
      var drawHoneycomb = {
          draw: function draw(context, dataSet, options) {
  
              context.save();
  
              var data = dataSet instanceof DataSet ? dataSet.get() : dataSet;
  
              for (var key in options) {
                  context[key] = options[key];
              }
  
              var grids = {};
  
              var offset = options.offset || {
                  x: 10,
                  y: 10
              };
  
              //
              var r = options._size || options.size || 40;
              r = r / 2 / Math.sin(Math.PI / 3);
              var dx = r * 2 * Math.sin(Math.PI / 3);
              var dy = r * 1.5;
  
              var binsById = {};
  
              for (var i = 0; i < data.length; i++) {
                  var coordinates = data[i].geometry._coordinates || data[i].geometry.coordinates;
                  var py = (coordinates[1] - offset.y) / dy,
                      pj = Math.round(py),
                      px = (coordinates[0] - offset.x) / dx - (pj & 1 ? .5 : 0),
                      pi = Math.round(px),
                      py1 = py - pj;
  
                  if (Math.abs(py1) * 3 > 1) {
                      var px1 = px - pi,
                          pi2 = pi + (px < pi ? -1 : 1) / 2,
                          pj2 = pj + (py < pj ? -1 : 1),
                          px2 = px - pi2,
                          py2 = py - pj2;
                      if (px1 * px1 + py1 * py1 > px2 * px2 + py2 * py2) pi = pi2 + (pj & 1 ? 1 : -1) / 2, pj = pj2;
                  }
  
                  var id = pi + "-" + pj,
                      bin = binsById[id];
                  if (bin) {
                      bin.push(data[i]);
                  } else {
                      bin = binsById[id] = [data[i]];
                      bin.i = pi;
                      bin.j = pj;
                      bin.x = (pi + (pj & 1 ? 1 / 2 : 0)) * dx;
                      bin.y = pj * dy;
                  }
              }
  
              var intensity = new Intensity({
                  max: options.max || 100,
                  maxSize: r,
                  gradient: options.gradient
              });
  
              for (var key in binsById) {
  
                  var item = binsById[key];
  
                  context.beginPath();
  
                  for (var j = 0; j < 6; j++) {
  
                      var radius = r;
  
                      var result = hex_corner({
                          x: item.x + offset.x,
                          y: item.y + offset.y
                      }, radius, j);
                      context.lineTo(result[0], result[1]);
                  }
                  context.closePath();
  
                  var count = 0;
                  for (var i = 0; i < item.length; i++) {
                      count += item[i].count || 1;
                  }
                  item.count = count;
  
                  context.fillStyle = intensity.getColor(count);
                  context.fill();
                  if (options.strokeStyle && options.lineWidth) {
                      context.stroke();
                  }
              }
  
              if (options.label && options.label.show !== false) {
  
                  context.fillStyle = options.label.fillStyle || 'white';
  
                  if (options.label.font) {
                      context.font = options.label.font;
                  }
  
                  if (options.label.shadowColor) {
                      context.shadowColor = options.label.shadowColor;
                  }
  
                  if (options.label.shadowBlur) {
                      context.shadowBlur = options.label.shadowBlur;
                  }
  
                  for (var key in binsById) {
                      var item = binsById[key];
                      var text = item.count;
                      if (text < 0) {
                          text = text.toFixed(2);
                      } else {
                          text = ~~text;
                      }
                      var textWidth = context.measureText(text).width;
                      context.fillText(text, item.x + offset.x - textWidth / 2, item.y + offset.y + 5);
                  }
              }
  
              context.restore();
          }
      };
  
      function createShader(gl, src, type) {
          var shader = gl.createShader(type);
          gl.shaderSource(shader, src);
          gl.compileShader(shader);
          return shader;
      }
  
      function initShaders(gl, vs_source, fs_source) {
  
          var vertexShader = createShader(gl, vs_source, gl.VERTEX_SHADER);
          var fragmentShader = createShader(gl, fs_source, gl.FRAGMENT_SHADER);
  
          var glProgram = gl.createProgram();
  
          gl.attachShader(glProgram, vertexShader);
          gl.attachShader(glProgram, fragmentShader);
          gl.linkProgram(glProgram);
  
          gl.useProgram(glProgram);
  
          return glProgram;
      }
  
      function getColorData(color) {
          var tmpCanvas = document.createElement('canvas');
          var tmpCtx = tmpCanvas.getContext('2d');
          tmpCanvas.width = 1;
          tmpCanvas.height = 1;
          tmpCtx.fillStyle = color;
          tmpCtx.fillRect(0, 0, 1, 1);
          return tmpCtx.getImageData(0, 0, 1, 1).data;
      }
  
      var vs_s = ['attribute vec4 a_Position;', 'void main() {', 'gl_Position = a_Position;', 'gl_PointSize = 30.0;', '}'].join('');
  
      var fs_s = ['precision mediump float;', 'uniform vec4 u_FragColor;', 'void main() {', 'gl_FragColor = u_FragColor;', '}'].join('');
  
      function draw$1(gl, data, options) {
  
          if (!data) {
              return;
          }
  
          var program = initShaders(gl, vs_s, fs_s);
  
          gl.enable(gl.BLEND);
          gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
  
          //gl.clearColor(0.0, 0.0, 1.0, 1.0);
          gl.clear(gl.COLOR_BUFFER_BIT);
  
          var halfCanvasWidth = gl.canvas.width / 2;
          var halfCanvasHeight = gl.canvas.height / 2;
  
          // Create a buffer object
          var vertexBuffer = gl.createBuffer();
          // Bind the buffer object to target
          gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
  
          var a_Position = gl.getAttribLocation(program, 'a_Position');
          // Assign the buffer object to a_Position variable
          gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
  
          // Enable the assignment to a_Position variable
          gl.enableVertexAttribArray(a_Position);
  
          var uFragColor = gl.getUniformLocation(program, 'u_FragColor');
  
          var colored = getColorData(options.strokeStyle || 'red');
  
          gl.uniform4f(uFragColor, colored[0] / 255, colored[1] / 255, colored[2] / 255, colored[3] / 255);
  
          gl.lineWidth(options.lineWidth || 1);
  
          for (var i = 0, len = data.length; i < len; i++) {
              var _geometry = data[i].geometry._coordinates;
  
              var verticesData = [];
  
              for (var j = 0; j < _geometry.length; j++) {
                  var item = _geometry[j];
  
                  var x = (item[0] - halfCanvasWidth) / halfCanvasWidth;
                  var y = (halfCanvasHeight - item[1]) / halfCanvasHeight;
                  verticesData.push(x, y);
              }
  
              var vertices = new Float32Array(verticesData);
              // Write date into the buffer object
              gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
              gl.drawArrays(gl.LINE_STRIP, 0, _geometry.length);
          }
      }
  
      var line = {
          draw: draw$1
      };
  
      var vs_s$1 = ['attribute vec4 a_Position;', 'attribute float a_PointSize;', 'void main() {', 'gl_Position = a_Position;', 'gl_PointSize = a_PointSize;', '}'].join('');
  
      var fs_s$1 = ['precision mediump float;', 'uniform vec4 u_FragColor;', 'void main() {', 'gl_FragColor = u_FragColor;', '}'].join('');
  
      function draw$2(gl, data, options) {
  
          if (!data) {
              return;
          }
  
          var program = initShaders(gl, vs_s$1, fs_s$1);
  
          var a_Position = gl.getAttribLocation(program, 'a_Position');
  
          var a_PointSize = gl.getAttribLocation(program, 'a_PointSize');
  
          var uFragColor = gl.getUniformLocation(program, 'u_FragColor');
  
          //gl.clearColor(0.0, 0.0, 1.0, 1.0);
          gl.clear(gl.COLOR_BUFFER_BIT);
  
          var halfCanvasWidth = gl.canvas.width / 2;
          var halfCanvasHeight = gl.canvas.height / 2;
  
          var verticesData = [];
          var count = 0;
          for (var i = 0; i < data.length; i++) {
              var item = data[i].geometry._coordinates;
  
              var x = (item[0] - halfCanvasWidth) / halfCanvasWidth;
              var y = (halfCanvasHeight - item[1]) / halfCanvasHeight;
  
              if (x < -1 || x > 1 || y < -1 || y > 1) {
                  continue;
              }
              verticesData.push(x, y);
              count++;
          }
  
          var vertices = new Float32Array(verticesData);
          var n = count; // The number of vertices
  
          // Create a buffer object
          var vertexBuffer = gl.createBuffer();
  
          // Bind the buffer object to target
          gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
          // Write date into the buffer object
          gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
  
          // Assign the buffer object to a_Position variable
          gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
  
          // Enable the assignment to a_Position variable
          gl.enableVertexAttribArray(a_Position);
  
          gl.vertexAttrib1f(a_PointSize, options._size);
  
          var colored = getColorData(options.fillStyle || 'red');
  
          gl.uniform4f(uFragColor, colored[0] / 255, colored[1] / 255, colored[2] / 255, colored[3] / 255);
          gl.drawArrays(gl.POINTS, 0, n);
      }
  
      var point = {
          draw: draw$2
      };
  
      function earcut(data, holeIndices, dim) {
  
          dim = dim || 2;
  
          var hasHoles = holeIndices && holeIndices.length,
              outerLen = hasHoles ? holeIndices[0] * dim : data.length,
              outerNode = linkedList(data, 0, outerLen, dim, true),
              triangles = [];
  
          if (!outerNode) return triangles;
  
          var minX, minY, maxX, maxY, x, y, size;
  
          if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
  
          // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
          if (data.length > 80 * dim) {
              minX = maxX = data[0];
              minY = maxY = data[1];
  
              for (var i = dim; i < outerLen; i += dim) {
                  x = data[i];
                  y = data[i + 1];
                  if (x < minX) minX = x;
                  if (y < minY) minY = y;
                  if (x > maxX) maxX = x;
                  if (y > maxY) maxY = y;
              }
  
              // minX, minY and size are later used to transform coords into integers for z-order calculation
              size = Math.max(maxX - minX, maxY - minY);
          }
  
          earcutLinked(outerNode, triangles, dim, minX, minY, size);
  
          return triangles;
      }
  
      // create a circular doubly linked list from polygon points in the specified winding order
      function linkedList(data, start, end, dim, clockwise) {
          var i, last;
  
          if (clockwise === signedArea(data, start, end, dim) > 0) {
              for (i = start; i < end; i += dim) {
                  last = insertNode(i, data[i], data[i + 1], last);
              }
          } else {
              for (i = end - dim; i >= start; i -= dim) {
                  last = insertNode(i, data[i], data[i + 1], last);
              }
          }
  
          if (last && equals(last, last.next)) {
              removeNode(last);
              last = last.next;
          }
  
          return last;
      }
  
      // eliminate colinear or duplicate points
      function filterPoints(start, end) {
          if (!start) return start;
          if (!end) end = start;
  
          var p = start,
              again;
          do {
              again = false;
  
              if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
                  removeNode(p);
                  p = end = p.prev;
                  if (p === p.next) return null;
                  again = true;
              } else {
                  p = p.next;
              }
          } while (again || p !== end);
  
          return end;
      }
  
      // main ear slicing loop which triangulates a polygon (given as a linked list)
      function earcutLinked(ear, triangles, dim, minX, minY, size, pass) {
          if (!ear) return;
  
          // interlink polygon nodes in z-order
          if (!pass && size) indexCurve(ear, minX, minY, size);
  
          var stop = ear,
              prev,
              next;
  
          // iterate through ears, slicing them one by one
          while (ear.prev !== ear.next) {
              prev = ear.prev;
              next = ear.next;
  
              if (size ? isEarHashed(ear, minX, minY, size) : isEar(ear)) {
                  // cut off the triangle
                  triangles.push(prev.i / dim);
                  triangles.push(ear.i / dim);
                  triangles.push(next.i / dim);
  
                  removeNode(ear);
  
                  // skipping the next vertice leads to less sliver triangles
                  ear = next.next;
                  stop = next.next;
  
                  continue;
              }
  
              ear = next;
  
              // if we looped through the whole remaining polygon and can't find any more ears
              if (ear === stop) {
                  // try filtering points and slicing again
                  if (!pass) {
                      earcutLinked(filterPoints(ear), triangles, dim, minX, minY, size, 1);
  
                      // if this didn't work, try curing all small self-intersections locally
                  } else if (pass === 1) {
                      ear = cureLocalIntersections(ear, triangles, dim);
                      earcutLinked(ear, triangles, dim, minX, minY, size, 2);
  
                      // as a last resort, try splitting the remaining polygon into two
                  } else if (pass === 2) {
                      splitEarcut(ear, triangles, dim, minX, minY, size);
                  }
  
                  break;
              }
          }
      }
  
      // check whether a polygon node forms a valid ear with adjacent nodes
      function isEar(ear) {
          var a = ear.prev,
              b = ear,
              c = ear.next;
  
          if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
  
          // now make sure we don't have other points inside the potential ear
          var p = ear.next.next;
  
          while (p !== ear.prev) {
              if (pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
              p = p.next;
          }
  
          return true;
      }
  
      function isEarHashed(ear, minX, minY, size) {
          var a = ear.prev,
              b = ear,
              c = ear.next;
  
          if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
  
          // triangle bbox; min & max are calculated like this for speed
          var minTX = a.x < b.x ? a.x < c.x ? a.x : c.x : b.x < c.x ? b.x : c.x,
              minTY = a.y < b.y ? a.y < c.y ? a.y : c.y : b.y < c.y ? b.y : c.y,
              maxTX = a.x > b.x ? a.x > c.x ? a.x : c.x : b.x > c.x ? b.x : c.x,
              maxTY = a.y > b.y ? a.y > c.y ? a.y : c.y : b.y > c.y ? b.y : c.y;
  
          // z-order range for the current triangle bbox;
          var minZ = zOrder(minTX, minTY, minX, minY, size),
              maxZ = zOrder(maxTX, maxTY, minX, minY, size);
  
          // first look for points inside the triangle in increasing z-order
          var p = ear.nextZ;
  
          while (p && p.z <= maxZ) {
              if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
              p = p.nextZ;
          }
  
          // then look for points in decreasing z-order
          p = ear.prevZ;
  
          while (p && p.z >= minZ) {
              if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
              p = p.prevZ;
          }
  
          return true;
      }
  
      // go through all polygon nodes and cure small local self-intersections
      function cureLocalIntersections(start, triangles, dim) {
          var p = start;
          do {
              var a = p.prev,
                  b = p.next.next;
  
              if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
  
                  triangles.push(a.i / dim);
                  triangles.push(p.i / dim);
                  triangles.push(b.i / dim);
  
                  // remove two nodes involved
                  removeNode(p);
                  removeNode(p.next);
  
                  p = start = b;
              }
              p = p.next;
          } while (p !== start);
  
          return p;
      }
  
      // try splitting polygon into two and triangulate them independently
      function splitEarcut(start, triangles, dim, minX, minY, size) {
          // look for a valid diagonal that divides the polygon into two
          var a = start;
          do {
              var b = a.next.next;
              while (b !== a.prev) {
                  if (a.i !== b.i && isValidDiagonal(a, b)) {
                      // split the polygon in two by the diagonal
                      var c = splitPolygon(a, b);
  
                      // filter colinear points around the cuts
                      a = filterPoints(a, a.next);
                      c = filterPoints(c, c.next);
  
                      // run earcut on each half
                      earcutLinked(a, triangles, dim, minX, minY, size);
                      earcutLinked(c, triangles, dim, minX, minY, size);
                      return;
                  }
                  b = b.next;
              }
              a = a.next;
          } while (a !== start);
      }
  
      // link every hole into the outer loop, producing a single-ring polygon without holes
      function eliminateHoles(data, holeIndices, outerNode, dim) {
          var queue = [],
              i,
              len,
              start,
              end,
              list;
  
          for (i = 0, len = holeIndices.length; i < len; i++) {
              start = holeIndices[i] * dim;
              end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
              list = linkedList(data, start, end, dim, false);
              if (list === list.next) list.steiner = true;
              queue.push(getLeftmost(list));
          }
  
          queue.sort(compareX);
  
          // process holes from left to right
          for (i = 0; i < queue.length; i++) {
              eliminateHole(queue[i], outerNode);
              outerNode = filterPoints(outerNode, outerNode.next);
          }
  
          return outerNode;
      }
  
      function compareX(a, b) {
          return a.x - b.x;
      }
  
      // find a bridge between vertices that connects hole with an outer ring and and link it
      function eliminateHole(hole, outerNode) {
          outerNode = findHoleBridge(hole, outerNode);
          if (outerNode) {
              var b = splitPolygon(outerNode, hole);
              filterPoints(b, b.next);
          }
      }
  
      // David Eberly's algorithm for finding a bridge between hole and outer polygon
      function findHoleBridge(hole, outerNode) {
          var p = outerNode,
              hx = hole.x,
              hy = hole.y,
              qx = -Infinity,
              m;
  
          // find a segment intersected by a ray from the hole's leftmost point to the left;
          // segment's endpoint with lesser x will be potential connection point
          do {
              if (hy <= p.y && hy >= p.next.y) {
                  var x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
                  if (x <= hx && x > qx) {
                      qx = x;
                      if (x === hx) {
                          if (hy === p.y) return p;
                          if (hy === p.next.y) return p.next;
                      }
                      m = p.x < p.next.x ? p : p.next;
                  }
              }
              p = p.next;
          } while (p !== outerNode);
  
          if (!m) return null;
  
          if (hx === qx) return m.prev; // hole touches outer segment; pick lower endpoint
  
          // look for points inside the triangle of hole point, segment intersection and endpoint;
          // if there are no points found, we have a valid connection;
          // otherwise choose the point of the minimum angle with the ray as connection point
  
          var stop = m,
              mx = m.x,
              my = m.y,
              tanMin = Infinity,
              tan;
  
          p = m.next;
  
          while (p !== stop) {
              if (hx >= p.x && p.x >= mx && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {
  
                  tan = Math.abs(hy - p.y) / (hx - p.x); // tangential
  
                  if ((tan < tanMin || tan === tanMin && p.x > m.x) && locallyInside(p, hole)) {
                      m = p;
                      tanMin = tan;
                  }
              }
  
              p = p.next;
          }
  
          return m;
      }
  
      // interlink polygon nodes in z-order
      function indexCurve(start, minX, minY, size) {
          var p = start;
          do {
              if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, size);
              p.prevZ = p.prev;
              p.nextZ = p.next;
              p = p.next;
          } while (p !== start);
  
          p.prevZ.nextZ = null;
          p.prevZ = null;
  
          sortLinked(p);
      }
  
      // Simon Tatham's linked list merge sort algorithm
      // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
      function sortLinked(list) {
          var i,
              p,
              q,
              e,
              tail,
              numMerges,
              pSize,
              qSize,
              inSize = 1;
  
          do {
              p = list;
              list = null;
              tail = null;
              numMerges = 0;
  
              while (p) {
                  numMerges++;
                  q = p;
                  pSize = 0;
                  for (i = 0; i < inSize; i++) {
                      pSize++;
                      q = q.nextZ;
                      if (!q) break;
                  }
  
                  qSize = inSize;
  
                  while (pSize > 0 || qSize > 0 && q) {
  
                      if (pSize === 0) {
                          e = q;
                          q = q.nextZ;
                          qSize--;
                      } else if (qSize === 0 || !q) {
                          e = p;
                          p = p.nextZ;
                          pSize--;
                      } else if (p.z <= q.z) {
                          e = p;
                          p = p.nextZ;
                          pSize--;
                      } else {
                          e = q;
                          q = q.nextZ;
                          qSize--;
                      }
  
                      if (tail) tail.nextZ = e;
                      else list = e;
  
                      e.prevZ = tail;
                      tail = e;
                  }
  
                  p = q;
              }
  
              tail.nextZ = null;
              inSize *= 2;
          } while (numMerges > 1);
  
          return list;
      }
  
      // z-order of a point given coords and size of the data bounding box
      function zOrder(x, y, minX, minY, size) {
          // coords are transformed into non-negative 15-bit integer range
          x = 32767 * (x - minX) / size;
          y = 32767 * (y - minY) / size;
  
          x = (x | x << 8) & 0x00FF00FF;
          x = (x | x << 4) & 0x0F0F0F0F;
          x = (x | x << 2) & 0x33333333;
          x = (x | x << 1) & 0x55555555;
  
          y = (y | y << 8) & 0x00FF00FF;
          y = (y | y << 4) & 0x0F0F0F0F;
          y = (y | y << 2) & 0x33333333;
          y = (y | y << 1) & 0x55555555;
  
          return x | y << 1;
      }
  
      // find the leftmost node of a polygon ring
      function getLeftmost(start) {
          var p = start,
              leftmost = start;
          do {
              if (p.x < leftmost.x) leftmost = p;
              p = p.next;
          } while (p !== start);
  
          return leftmost;
      }
  
      // check if a point lies within a convex triangle
      function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
          return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 && (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 && (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0;
      }
  
      // check if a diagonal between two polygon nodes is valid (lies in polygon interior)
      function isValidDiagonal(a, b) {
          return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b);
      }
  
      // signed area of a triangle
      function area(p, q, r) {
          return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
      }
  
      // check if two points are equal
      function equals(p1, p2) {
          return p1.x === p2.x && p1.y === p2.y;
      }
  
      // check if two segments intersect
      function intersects(p1, q1, p2, q2) {
          if (equals(p1, q1) && equals(p2, q2) || equals(p1, q2) && equals(p2, q1)) return true;
          return area(p1, q1, p2) > 0 !== area(p1, q1, q2) > 0 && area(p2, q2, p1) > 0 !== area(p2, q2, q1) > 0;
      }
  
      // check if a polygon diagonal intersects any polygon segments
      function intersectsPolygon(a, b) {
          var p = a;
          do {
              if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b)) return true;
              p = p.next;
          } while (p !== a);
  
          return false;
      }
  
      // check if a polygon diagonal is locally inside the polygon
      function locallyInside(a, b) {
          return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;
      }
  
      // check if the middle point of a polygon diagonal is inside the polygon
      function middleInside(a, b) {
          var p = a,
              inside = false,
              px = (a.x + b.x) / 2,
              py = (a.y + b.y) / 2;
          do {
              if (p.y > py !== p.next.y > py && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x) inside = !inside;
              p = p.next;
          } while (p !== a);
  
          return inside;
      }
  
      // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;
      // if one belongs to the outer ring and another to a hole, it merges it into a single ring
      function splitPolygon(a, b) {
          var a2 = new Node(a.i, a.x, a.y),
              b2 = new Node(b.i, b.x, b.y),
              an = a.next,
              bp = b.prev;
  
          a.next = b;
          b.prev = a;
  
          a2.next = an;
          an.prev = a2;
  
          b2.next = a2;
          a2.prev = b2;
  
          bp.next = b2;
          b2.prev = bp;
  
          return b2;
      }
  
      // create a node and optionally link it with previous one (in a circular doubly linked list)
      function insertNode(i, x, y, last) {
          var p = new Node(i, x, y);
  
          if (!last) {
              p.prev = p;
              p.next = p;
          } else {
              p.next = last.next;
              p.prev = last;
              last.next.prev = p;
              last.next = p;
          }
          return p;
      }
  
      function removeNode(p) {
          p.next.prev = p.prev;
          p.prev.next = p.next;
  
          if (p.prevZ) p.prevZ.nextZ = p.nextZ;
          if (p.nextZ) p.nextZ.prevZ = p.prevZ;
      }
  
      function Node(i, x, y) {
          // vertice index in coordinates array
          this.i = i;
  
          // vertex coordinates
          this.x = x;
          this.y = y;
  
          // previous and next vertice nodes in a polygon ring
          this.prev = null;
          this.next = null;
  
          // z-order curve value
          this.z = null;
  
          // previous and next nodes in z-order
          this.prevZ = null;
          this.nextZ = null;
  
          // indicates whether this is a steiner point
          this.steiner = false;
      }
  
      // return a percentage difference between the polygon area and its triangulation area;
      // used to verify correctness of triangulation
      earcut.deviation = function(data, holeIndices, dim, triangles) {
          var hasHoles = holeIndices && holeIndices.length;
          var outerLen = hasHoles ? holeIndices[0] * dim : data.length;
  
          var polygonArea = Math.abs(signedArea(data, 0, outerLen, dim));
          if (hasHoles) {
              for (var i = 0, len = holeIndices.length; i < len; i++) {
                  var start = holeIndices[i] * dim;
                  var end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
                  polygonArea -= Math.abs(signedArea(data, start, end, dim));
              }
          }
  
          var trianglesArea = 0;
          for (i = 0; i < triangles.length; i += 3) {
              var a = triangles[i] * dim;
              var b = triangles[i + 1] * dim;
              var c = triangles[i + 2] * dim;
              trianglesArea += Math.abs((data[a] - data[c]) * (data[b + 1] - data[a + 1]) - (data[a] - data[b]) * (data[c + 1] - data[a + 1]));
          }
  
          return polygonArea === 0 && trianglesArea === 0 ? 0 : Math.abs((trianglesArea - polygonArea) / polygonArea);
      };
  
      function signedArea(data, start, end, dim) {
          var sum = 0;
          for (var i = start, j = end - dim; i < end; i += dim) {
              sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
              j = i;
          }
          return sum;
      }
  
      // turn a polygon in a multi-dimensional array form (e.g. as in GeoJSON) into a form Earcut accepts
      earcut.flatten = function(data) {
          var dim = data[0][0].length,
              result = {
                  vertices: [],
                  holes: [],
                  dimensions: dim
              },
              holeIndex = 0;
  
          for (var i = 0; i < data.length; i++) {
              for (var j = 0; j < data[i].length; j++) {
                  for (var d = 0; d < dim; d++) {
                      result.vertices.push(data[i][j][d]);
                  }
              }
              if (i > 0) {
                  holeIndex += data[i - 1].length;
                  result.holes.push(holeIndex);
              }
          }
          return result;
      };
  
      var vs_s$2 = ['attribute vec4 a_Position;', 'void main() {', 'gl_Position = a_Position;', 'gl_PointSize = 30.0;', '}'].join('');
  
      var fs_s$2 = ['precision mediump float;', 'uniform vec4 u_FragColor;', 'void main() {', 'gl_FragColor = u_FragColor;', '}'].join('');
  
      function draw$3(gl, data, options) {
  
          if (!data) {
              return;
          }
  
          // gl.clearColor(0.0, 0.0, 0.0, 1.0);
          gl.clear(gl.COLOR_BUFFER_BIT);
          gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  
          var program = initShaders(gl, vs_s$2, fs_s$2);
  
          gl.enable(gl.BLEND);
          gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
  
          var halfCanvasWidth = gl.canvas.width / 2;
          var halfCanvasHeight = gl.canvas.height / 2;
  
          // Bind the buffer object to target
          gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
          gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer());
  
          var a_Position = gl.getAttribLocation(program, 'a_Position');
          // Assign the buffer object to a_Position variable
          gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
  
          // Enable the assignment to a_Position variable
          gl.enableVertexAttribArray(a_Position);
  
          var uFragColor = gl.getUniformLocation(program, 'u_FragColor');
  
          var colored = getColorData(options.fillStyle || 'red');
  
          gl.uniform4f(uFragColor, colored[0] / 255, colored[1] / 255, colored[2] / 255, colored[3] / 255);
  
          gl.lineWidth(options.lineWidth || 1);
  
          var verticesArr = [];
          var trianglesArr = [];
  
          var maxSize = 65536;
          var indexOffset = 0;
  
          for (var i = 0, len = data.length; i < len; i++) {
  
              var flatten = earcut.flatten(data[i].geometry._coordinates || data[i].geometry.coordinates);
              var vertices = flatten.vertices;
              indexOffset = verticesArr.length / 2;
              for (var j = 0; j < vertices.length; j += 2) {
                  vertices[j] = (vertices[j] - halfCanvasWidth) / halfCanvasWidth;
                  vertices[j + 1] = (halfCanvasHeight - vertices[j + 1]) / halfCanvasHeight;
              }
  
              if ((verticesArr.length + vertices.length) / 2 > maxSize) {
                  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verticesArr), gl.STATIC_DRAW);
                  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(trianglesArr), gl.STATIC_DRAW);
                  gl.drawElements(gl.TRIANGLES, trianglesArr.length, gl.UNSIGNED_SHORT, 0);
                  verticesArr.length = 0;
                  trianglesArr.length = 0;
                  indexOffset = 0;
              }
  
              for (var j = 0; j < vertices.length; j++) {
                  verticesArr.push(vertices[j]);
              }
  
              var triangles = earcut(vertices, flatten.holes, flatten.dimensions);
              for (var j = 0; j < triangles.length; j++) {
                  trianglesArr.push(triangles[j] + indexOffset);
              }
          }
  
          gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verticesArr), gl.STATIC_DRAW);
          gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(trianglesArr), gl.STATIC_DRAW);
          gl.drawElements(gl.TRIANGLES, trianglesArr.length, gl.UNSIGNED_SHORT, 0);
  
          gl.bindBuffer(gl.ARRAY_BUFFER, null);
          gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
      }
  
      var polygon = {
          draw: draw$3
      };
  
      /**
       * @author kyle / http://nikai.us/
       */
      var webglDrawSimple = {
          draw: function draw(gl, dataSet, options) {
              var data = dataSet instanceof DataSet ? dataSet.get() : dataSet;
              if (data.length > 0) {
                  if (data[0].geometry.type == "LineString") {
                      line.draw(gl, data, options);
                  } else if (data[0].geometry.type == "Polygon" || data[0].geometry.type == "MultiPolygon") {
                      polygon.draw(gl, data, options);
                  } else {
                      point.draw(gl, data, options);
                  }
              }
          }
      };
  
      /**
       * get the center by the city name
       * @author kyle / http://nikai.us/
       */
  
      var citycenter = {
          municipalities: [{
              n: "北京",
              g: "116.395645,39.929986|12"
          }, {
              n: "上海",
              g: "121.487899,31.249162|12"
          }, {
              n: "天津",
              g: "117.210813,39.14393|12"
          }, {
              n: "重庆",
              g: "106.530635,29.544606|12"
          }],
          provinces: [{
              n: "安徽",
              g: "117.216005,31.859252|8",
              cities: [{
                  n: "合肥",
                  g: "117.282699,31.866942|12"
              }, {
                  n: "安庆",
                  g: "117.058739,30.537898|13"
              }, {
                  n: "蚌埠",
                  g: "117.35708,32.929499|13"
              }, {
                  n: "亳州",
                  g: "115.787928,33.871211|13"
              }, {
                  n: "巢湖",
                  g: "117.88049,31.608733|13"
              }, {
                  n: "池州",
                  g: "117.494477,30.660019|14"
              }, {
                  n: "滁州",
                  g: "118.32457,32.317351|13"
              }, {
                  n: "阜阳",
                  g: "115.820932,32.901211|13"
              }, {
                  n: "淮北",
                  g: "116.791447,33.960023|13"
              }, {
                  n: "淮南",
                  g: "117.018639,32.642812|13"
              }, {
                  n: "黄山",
                  g: "118.29357,29.734435|13"
              }, {
                  n: "六安",
                  g: "116.505253,31.755558|13"
              }, {
                  n: "马鞍山",
                  g: "118.515882,31.688528|13"
              }, {
                  n: "宿州",
                  g: "116.988692,33.636772|13"
              }, {
                  n: "铜陵",
                  g: "117.819429,30.94093|14"
              }, {
                  n: "芜湖",
                  g: "118.384108,31.36602|12"
              }, {
                  n: "宣城",
                  g: "118.752096,30.951642|13"
              }]
          }, {
              n: "福建",
              g: "117.984943,26.050118|8",
              cities: [{
                  n: "福州",
                  g: "119.330221,26.047125|12"
              }, {
                  n: "龙岩",
                  g: "117.017997,25.078685|13"
              }, {
                  n: "南平",
                  g: "118.181883,26.643626|13"
              }, {
                  n: "宁德",
                  g: "119.542082,26.656527|14"
              }, {
                  n: "莆田",
                  g: "119.077731,25.44845|13"
              }, {
                  n: "泉州",
                  g: "118.600362,24.901652|12"
              }, {
                  n: "三明",
                  g: "117.642194,26.270835|14"
              }, {
                  n: "厦门",
                  g: "118.103886,24.489231|12"
              }, {
                  n: "漳州",
                  g: "117.676205,24.517065|12"
              }]
          }, {
              n: "甘肃",
              g: "102.457625,38.103267|6",
              cities: [{
                  n: "兰州",
                  g: "103.823305,36.064226|12"
              }, {
                  n: "白银",
                  g: "104.171241,36.546682|13"
              }, {
                  n: "定西",
                  g: "104.626638,35.586056|13"
              }, {
                  n: "甘南州",
                  g: "102.917442,34.992211|14"
              }, {
                  n: "嘉峪关",
                  g: "98.281635,39.802397|13"
              }, {
                  n: "金昌",
                  g: "102.208126,38.516072|13"
              }, {
                  n: "酒泉",
                  g: "98.508415,39.741474|13"
              }, {
                  n: "临夏州",
                  g: "103.215249,35.598514|13"
              }, {
                  n: "陇南",
                  g: "104.934573,33.39448|14"
              }, {
                  n: "平凉",
                  g: "106.688911,35.55011|13"
              }, {
                  n: "庆阳",
                  g: "107.644227,35.726801|13"
              }, {
                  n: "天水",
                  g: "105.736932,34.584319|13"
              }, {
                  n: "武威",
                  g: "102.640147,37.933172|13"
              }, {
                  n: "张掖",
                  g: "100.459892,38.93932|13"
              }]
          }, {
              n: "广东",
              g: "113.394818,23.408004|8",
              cities: [{
                  n: "广州",
                  g: "113.30765,23.120049|12"
              }, {
                  n: "潮州",
                  g: "116.630076,23.661812|13"
              }, {
                  n: "东莞",
                  g: "113.763434,23.043024|12"
              }, {
                  n: "佛山",
                  g: "113.134026,23.035095|13"
              }, {
                  n: "河源",
                  g: "114.713721,23.757251|12"
              }, {
                  n: "惠州",
                  g: "114.410658,23.11354|12"
              }, {
                  n: "江门",
                  g: "113.078125,22.575117|13"
              }, {
                  n: "揭阳",
                  g: "116.379501,23.547999|13"
              }, {
                  n: "茂名",
                  g: "110.931245,21.668226|13"
              }, {
                  n: "梅州",
                  g: "116.126403,24.304571|13"
              }, {
                  n: "清远",
                  g: "113.040773,23.698469|13"
              }, {
                  n: "汕头",
                  g: "116.72865,23.383908|13"
              }, {
                  n: "汕尾",
                  g: "115.372924,22.778731|14"
              }, {
                  n: "韶关",
                  g: "113.594461,24.80296|13"
              }, {
                  n: "深圳",
                  g: "114.025974,22.546054|12"
              }, {
                  n: "阳江",
                  g: "111.97701,21.871517|14"
              }, {
                  n: "云浮",
                  g: "112.050946,22.937976|13"
              }, {
                  n: "湛江",
                  g: "110.365067,21.257463|13"
              }, {
                  n: "肇庆",
                  g: "112.479653,23.078663|13"
              }, {
                  n: "中山",
                  g: "113.42206,22.545178|12"
              }, {
                  n: "珠海",
                  g: "113.562447,22.256915|13"
              }]
          }, {
              n: "广西",
              g: "108.924274,23.552255|7",
              cities: [{
                  n: "南宁",
                  g: "108.297234,22.806493|12"
              }, {
                  n: "百色",
                  g: "106.631821,23.901512|13"
              }, {
                  n: "北海",
                  g: "109.122628,21.472718|13"
              }, {
                  n: "崇左",
                  g: "107.357322,22.415455|14"
              }, {
                  n: "防城港",
                  g: "108.351791,21.617398|15"
              }, {
                  n: "桂林",
                  g: "110.26092,25.262901|12"
              }, {
                  n: "贵港",
                  g: "109.613708,23.103373|13"
              }, {
                  n: "河池",
                  g: "108.069948,24.699521|14"
              }, {
                  n: "贺州",
                  g: "111.552594,24.411054|14"
              }, {
                  n: "来宾",
                  g: "109.231817,23.741166|14"
              }, {
                  n: "柳州",
                  g: "109.422402,24.329053|12"
              }, {
                  n: "钦州",
                  g: "108.638798,21.97335|13"
              }, {
                  n: "梧州",
                  g: "111.305472,23.485395|13"
              }, {
                  n: "玉林",
                  g: "110.151676,22.643974|14"
              }]
          }, {
              n: "贵州",
              g: "106.734996,26.902826|8",
              cities: [{
                  n: "贵阳",
                  g: "106.709177,26.629907|12"
              }, {
                  n: "安顺",
                  g: "105.92827,26.228595|13"
              }, {
                  n: "毕节地区",
                  g: "105.300492,27.302612|14"
              }, {
                  n: "六盘水",
                  g: "104.852087,26.591866|13"
              }, {
                  n: "铜仁地区",
                  g: "109.196161,27.726271|14"
              }, {
                  n: "遵义",
                  g: "106.93126,27.699961|13"
              }, {
                  n: "黔西南州",
                  g: "104.900558,25.095148|11"
              }, {
                  n: "黔东南州",
                  g: "107.985353,26.583992|11"
              }, {
                  n: "黔南州",
                  g: "107.523205,26.264536|11"
              }]
          }, {
              n: "海南",
              g: "109.733755,19.180501|9",
              cities: [{
                  n: "海口",
                  g: "110.330802,20.022071|13"
              }, {
                  n: "白沙",
                  g: "109.358586,19.216056|12"
              }, {
                  n: "保亭",
                  g: "109.656113,18.597592|12"
              }, {
                  n: "昌江",
                  g: "109.0113,19.222483|12"
              }, {
                  n: "儋州",
                  g: "109.413973,19.571153|13"
              }, {
                  n: "澄迈",
                  g: "109.996736,19.693135|13"
              }, {
                  n: "东方",
                  g: "108.85101,18.998161|13"
              }, {
                  n: "定安",
                  g: "110.32009,19.490991|13"
              }, {
                  n: "琼海",
                  g: "110.414359,19.21483|13"
              }, {
                  n: "琼中",
                  g: "109.861849,19.039771|12"
              }, {
                  n: "乐东",
                  g: "109.062698,18.658614|12"
              }, {
                  n: "临高",
                  g: "109.724101,19.805922|13"
              }, {
                  n: "陵水",
                  g: "109.948661,18.575985|12"
              }, {
                  n: "三亚",
                  g: "109.522771,18.257776|12"
              }, {
                  n: "屯昌",
                  g: "110.063364,19.347749|13"
              }, {
                  n: "万宁",
                  g: "110.292505,18.839886|13"
              }, {
                  n: "文昌",
                  g: "110.780909,19.750947|13"
              }, {
                  n: "五指山",
                  g: "109.51775,18.831306|13"
              }]
          }, {
              n: "河北",
              g: "115.661434,38.61384|7",
              cities: [{
                  n: "石家庄",
                  g: "114.522082,38.048958|12"
              }, {
                  n: "保定",
                  g: "115.49481,38.886565|13"
              }, {
                  n: "沧州",
                  g: "116.863806,38.297615|13"
              }, {
                  n: "承德",
                  g: "117.933822,40.992521|14"
              }, {
                  n: "邯郸",
                  g: "114.482694,36.609308|13"
              }, {
                  n: "衡水",
                  g: "115.686229,37.746929|13"
              }, {
                  n: "廊坊",
                  g: "116.703602,39.518611|13"
              }, {
                  n: "秦皇岛",
                  g: "119.604368,39.945462|12"
              }, {
                  n: "唐山",
                  g: "118.183451,39.650531|13"
              }, {
                  n: "邢台",
                  g: "114.520487,37.069531|13"
              }, {
                  n: "张家口",
                  g: "114.893782,40.811188|13"
              }]
          }, {
              n: "河南",
              g: "113.486804,34.157184|7",
              cities: [{
                  n: "郑州",
                  g: "113.649644,34.75661|12"
              }, {
                  n: "安阳",
                  g: "114.351807,36.110267|12"
              }, {
                  n: "鹤壁",
                  g: "114.29777,35.755426|13"
              }, {
                  n: "焦作",
                  g: "113.211836,35.234608|13"
              }, {
                  n: "开封",
                  g: "114.351642,34.801854|13"
              }, {
                  n: "洛阳",
                  g: "112.447525,34.657368|12"
              }, {
                  n: "漯河",
                  g: "114.046061,33.576279|13"
              }, {
                  n: "南阳",
                  g: "112.542842,33.01142|13"
              }, {
                  n: "平顶山",
                  g: "113.300849,33.745301|13"
              }, {
                  n: "濮阳",
                  g: "115.026627,35.753298|12"
              }, {
                  n: "三门峡",
                  g: "111.181262,34.78332|13"
              }, {
                  n: "商丘",
                  g: "115.641886,34.438589|13"
              }, {
                  n: "新乡",
                  g: "113.91269,35.307258|13"
              }, {
                  n: "信阳",
                  g: "114.085491,32.128582|13"
              }, {
                  n: "许昌",
                  g: "113.835312,34.02674|13"
              }, {
                  n: "周口",
                  g: "114.654102,33.623741|13"
              }, {
                  n: "驻马店",
                  g: "114.049154,32.983158|13"
              }]
          }, {
              n: "黑龙江",
              g: "128.047414,47.356592|6",
              cities: [{
                  n: "哈尔滨",
                  g: "126.657717,45.773225|12"
              }, {
                  n: "大庆",
                  g: "125.02184,46.596709|12"
              }, {
                  n: "大兴安岭地区",
                  g: "124.196104,51.991789|10"
              }, {
                  n: "鹤岗",
                  g: "130.292472,47.338666|13"
              }, {
                  n: "黑河",
                  g: "127.50083,50.25069|14"
              }, {
                  n: "鸡西",
                  g: "130.941767,45.32154|13"
              }, {
                  n: "佳木斯",
                  g: "130.284735,46.81378|12"
              }, {
                  n: "牡丹江",
                  g: "129.608035,44.588521|13"
              }, {
                  n: "七台河",
                  g: "131.019048,45.775005|14"
              }, {
                  n: "齐齐哈尔",
                  g: "123.987289,47.3477|13"
              }, {
                  n: "双鸭山",
                  g: "131.171402,46.655102|13"
              }, {
                  n: "绥化",
                  g: "126.989095,46.646064|13"
              }, {
                  n: "伊春",
                  g: "128.910766,47.734685|14"
              }]
          }, {
              n: "湖北",
              g: "112.410562,31.209316|8",
              cities: [{
                  n: "武汉",
                  g: "114.3162,30.581084|12"
              }, {
                  n: "鄂州",
                  g: "114.895594,30.384439|14"
              }, {
                  n: "恩施",
                  g: "109.517433,30.308978|14"
              }, {
                  n: "黄冈",
                  g: "114.906618,30.446109|14"
              }, {
                  n: "黄石",
                  g: "115.050683,30.216127|13"
              }, {
                  n: "荆门",
                  g: "112.21733,31.042611|13"
              }, {
                  n: "荆州",
                  g: "112.241866,30.332591|12"
              }, {
                  n: "潜江",
                  g: "112.768768,30.343116|13"
              }, {
                  n: "神农架林区",
                  g: "110.487231,31.595768|13"
              }, {
                  n: "十堰",
                  g: "110.801229,32.636994|13"
              }, {
                  n: "随州",
                  g: "113.379358,31.717858|13"
              }, {
                  n: "天门",
                  g: "113.12623,30.649047|13"
              }, {
                  n: "仙桃",
                  g: "113.387448,30.293966|13"
              }, {
                  n: "咸宁",
                  g: "114.300061,29.880657|13"
              }, {
                  n: "襄阳",
                  g: "112.176326,32.094934|12"
              }, {
                  n: "孝感",
                  g: "113.935734,30.927955|13"
              }, {
                  n: "宜昌",
                  g: "111.310981,30.732758|13"
              }]
          }, {
              n: "湖南",
              g: "111.720664,27.695864|7",
              cities: [{
                  n: "长沙",
                  g: "112.979353,28.213478|12"
              }, {
                  n: "常德",
                  g: "111.653718,29.012149|12"
              }, {
                  n: "郴州",
                  g: "113.037704,25.782264|13"
              }, {
                  n: "衡阳",
                  g: "112.583819,26.898164|13"
              }, {
                  n: "怀化",
                  g: "109.986959,27.557483|13"
              }, {
                  n: "娄底",
                  g: "111.996396,27.741073|13"
              }, {
                  n: "邵阳",
                  g: "111.461525,27.236811|13"
              }, {
                  n: "湘潭",
                  g: "112.935556,27.835095|13"
              }, {
                  n: "湘西州",
                  g: "109.745746,28.317951|14"
              }, {
                  n: "益阳",
                  g: "112.366547,28.588088|13"
              }, {
                  n: "永州",
                  g: "111.614648,26.435972|13"
              }, {
                  n: "岳阳",
                  g: "113.146196,29.378007|13"
              }, {
                  n: "张家界",
                  g: "110.48162,29.124889|13"
              }, {
                  n: "株洲",
                  g: "113.131695,27.827433|13"
              }]
          }, {
              n: "江苏",
              g: "119.368489,33.013797|8",
              cities: [{
                  n: "南京",
                  g: "118.778074,32.057236|12"
              }, {
                  n: "常州",
                  g: "119.981861,31.771397|12"
              }, {
                  n: "淮安",
                  g: "119.030186,33.606513|12"
              }, {
                  n: "连云港",
                  g: "119.173872,34.601549|12"
              }, {
                  n: "南通",
                  g: "120.873801,32.014665|12"
              }, {
                  n: "苏州",
                  g: "120.619907,31.317987|12"
              }, {
                  n: "宿迁",
                  g: "118.296893,33.95205|13"
              }, {
                  n: "泰州",
                  g: "119.919606,32.476053|13"
              }, {
                  n: "无锡",
                  g: "120.305456,31.570037|12"
              }, {
                  n: "徐州",
                  g: "117.188107,34.271553|12"
              }, {
                  n: "盐城",
                  g: "120.148872,33.379862|12"
              }, {
                  n: "扬州",
                  g: "119.427778,32.408505|13"
              }, {
                  n: "镇江",
                  g: "119.455835,32.204409|13"
              }]
          }, {
              n: "江西",
              g: "115.676082,27.757258|7",
              cities: [{
                  n: "南昌",
                  g: "115.893528,28.689578|12"
              }, {
                  n: "抚州",
                  g: "116.360919,27.954545|13"
              }, {
                  n: "赣州",
                  g: "114.935909,25.845296|13"
              }, {
                  n: "吉安",
                  g: "114.992039,27.113848|13"
              }, {
                  n: "景德镇",
                  g: "117.186523,29.303563|12"
              }, {
                  n: "九江",
                  g: "115.999848,29.71964|13"
              }, {
                  n: "萍乡",
                  g: "113.859917,27.639544|13"
              }, {
                  n: "上饶",
                  g: "117.955464,28.457623|13"
              }, {
                  n: "新余",
                  g: "114.947117,27.822322|13"
              }, {
                  n: "宜春",
                  g: "114.400039,27.81113|13"
              }, {
                  n: "鹰潭",
                  g: "117.03545,28.24131|13"
              }]
          }, {
              n: "吉林",
              g: "126.262876,43.678846|7",
              cities: [{
                  n: "长春",
                  g: "125.313642,43.898338|12"
              }, {
                  n: "白城",
                  g: "122.840777,45.621086|13"
              }, {
                  n: "白山",
                  g: "126.435798,41.945859|13"
              }, {
                  n: "吉林市",
                  g: "126.564544,43.871988|12"
              }, {
                  n: "辽源",
                  g: "125.133686,42.923303|13"
              }, {
                  n: "四平",
                  g: "124.391382,43.175525|12"
              }, {
                  n: "松原",
                  g: "124.832995,45.136049|13"
              }, {
                  n: "通化",
                  g: "125.94265,41.736397|13"
              }, {
                  n: "延边",
                  g: "129.485902,42.896414|13"
              }]
          }, {
              n: "辽宁",
              g: "122.753592,41.6216|8",
              cities: [{
                  n: "沈阳",
                  g: "123.432791,41.808645|12"
              }, {
                  n: "鞍山",
                  g: "123.007763,41.118744|13"
              }, {
                  n: "本溪",
                  g: "123.778062,41.325838|12"
              }, {
                  n: "朝阳",
                  g: "120.446163,41.571828|13"
              }, {
                  n: "大连",
                  g: "121.593478,38.94871|12"
              }, {
                  n: "丹东",
                  g: "124.338543,40.129023|12"
              }, {
                  n: "抚顺",
                  g: "123.92982,41.877304|12"
              }, {
                  n: "阜新",
                  g: "121.660822,42.01925|14"
              }, {
                  n: "葫芦岛",
                  g: "120.860758,40.74303|13"
              }, {
                  n: "锦州",
                  g: "121.147749,41.130879|13"
              }, {
                  n: "辽阳",
                  g: "123.172451,41.273339|14"
              }, {
                  n: "盘锦",
                  g: "122.073228,41.141248|13"
              }, {
                  n: "铁岭",
                  g: "123.85485,42.299757|13"
              }, {
                  n: "营口",
                  g: "122.233391,40.668651|13"
              }]
          }, {
              n: "内蒙古",
              g: "114.415868,43.468238|5",
              cities: [{
                  n: "呼和浩特",
                  g: "111.660351,40.828319|12"
              }, {
                  n: "阿拉善盟",
                  g: "105.695683,38.843075|14"
              }, {
                  n: "包头",
                  g: "109.846239,40.647119|12"
              }, {
                  n: "巴彦淖尔",
                  g: "107.423807,40.76918|12"
              }, {
                  n: "赤峰",
                  g: "118.930761,42.297112|12"
              }, {
                  n: "鄂尔多斯",
                  g: "109.993706,39.81649|12"
              }, {
                  n: "呼伦贝尔",
                  g: "119.760822,49.201636|12"
              }, {
                  n: "通辽",
                  g: "122.260363,43.633756|12"
              }, {
                  n: "乌海",
                  g: "106.831999,39.683177|13"
              }, {
                  n: "乌兰察布",
                  g: "113.112846,41.022363|12"
              }, {
                  n: "锡林郭勒盟",
                  g: "116.02734,43.939705|11"
              }, {
                  n: "兴安盟",
                  g: "122.048167,46.083757|11"
              }]
          }, {
              n: "宁夏",
              g: "106.155481,37.321323|8",
              cities: [{
                  n: "银川",
                  g: "106.206479,38.502621|12"
              }, {
                  n: "固原",
                  g: "106.285268,36.021523|13"
              }, {
                  n: "石嘴山",
                  g: "106.379337,39.020223|13"
              }, {
                  n: "吴忠",
                  g: "106.208254,37.993561|14"
              }, {
                  n: "中卫",
                  g: "105.196754,37.521124|14"
              }]
          }, {
              n: "青海",
              g: "96.202544,35.499761|7",
              cities: [{
                  n: "西宁",
                  g: "101.767921,36.640739|12"
              }, {
                  n: "果洛州",
                  g: "100.223723,34.480485|11"
              }, {
                  n: "海东地区",
                  g: "102.085207,36.51761|11"
              }, {
                  n: "海北州",
                  g: "100.879802,36.960654|11"
              }, {
                  n: "海南州",
                  g: "100.624066,36.284364|11"
              }, {
                  n: "海西州",
                  g: "97.342625,37.373799|11"
              }, {
                  n: "黄南州",
                  g: "102.0076,35.522852|11"
              }, {
                  n: "玉树州",
                  g: "97.013316,33.00624|14"
              }]
          }, {
              n: "山东",
              g: "118.527663,36.09929|8",
              cities: [{
                  n: "济南",
                  g: "117.024967,36.682785|12"
              }, {
                  n: "滨州",
                  g: "117.968292,37.405314|12"
              }, {
                  n: "东营",
                  g: "118.583926,37.487121|12"
              }, {
                  n: "德州",
                  g: "116.328161,37.460826|12"
              }, {
                  n: "菏泽",
                  g: "115.46336,35.26244|13"
              }, {
                  n: "济宁",
                  g: "116.600798,35.402122|13"
              }, {
                  n: "莱芜",
                  g: "117.684667,36.233654|13"
              }, {
                  n: "聊城",
                  g: "115.986869,36.455829|12"
              }, {
                  n: "临沂",
                  g: "118.340768,35.072409|12"
              }, {
                  n: "青岛",
                  g: "120.384428,36.105215|12"
              }, {
                  n: "日照",
                  g: "119.50718,35.420225|12"
              }, {
                  n: "泰安",
                  g: "117.089415,36.188078|13"
              }, {
                  n: "威海",
                  g: "122.093958,37.528787|13"
              }, {
                  n: "潍坊",
                  g: "119.142634,36.716115|12"
              }, {
                  n: "烟台",
                  g: "121.309555,37.536562|12"
              }, {
                  n: "枣庄",
                  g: "117.279305,34.807883|13"
              }, {
                  n: "淄博",
                  g: "118.059134,36.804685|12"
              }]
          }, {
              n: "山西",
              g: "112.515496,37.866566|7",
              cities: [{
                  n: "太原",
                  g: "112.550864,37.890277|12"
              }, {
                  n: "长治",
                  g: "113.120292,36.201664|12"
              }, {
                  n: "大同",
                  g: "113.290509,40.113744|12"
              }, {
                  n: "晋城",
                  g: "112.867333,35.499834|13"
              }, {
                  n: "晋中",
                  g: "112.738514,37.693362|13"
              }, {
                  n: "临汾",
                  g: "111.538788,36.099745|13"
              }, {
                  n: "吕梁",
                  g: "111.143157,37.527316|14"
              }, {
                  n: "朔州",
                  g: "112.479928,39.337672|13"
              }, {
                  n: "忻州",
                  g: "112.727939,38.461031|12"
              }, {
                  n: "阳泉",
                  g: "113.569238,37.869529|13"
              }, {
                  n: "运城",
                  g: "111.006854,35.038859|13"
              }]
          }, {
              n: "陕西",
              g: "109.503789,35.860026|7",
              cities: [{
                  n: "西安",
                  g: "108.953098,34.2778|12"
              }, {
                  n: "安康",
                  g: "109.038045,32.70437|13"
              }, {
                  n: "宝鸡",
                  g: "107.170645,34.364081|12"
              }, {
                  n: "汉中",
                  g: "107.045478,33.081569|13"
              }, {
                  n: "商洛",
                  g: "109.934208,33.873907|13"
              }, {
                  n: "铜川",
                  g: "108.968067,34.908368|13"
              }, {
                  n: "渭南",
                  g: "109.483933,34.502358|13"
              }, {
                  n: "咸阳",
                  g: "108.707509,34.345373|13"
              }, {
                  n: "延安",
                  g: "109.50051,36.60332|13"
              }, {
                  n: "榆林",
                  g: "109.745926,38.279439|12"
              }]
          }, {
              n: "四川",
              g: "102.89916,30.367481|7",
              cities: [{
                  n: "成都",
                  g: "104.067923,30.679943|12"
              }, {
                  n: "阿坝州",
                  g: "102.228565,31.905763|15"
              }, {
                  n: "巴中",
                  g: "106.757916,31.869189|14"
              }, {
                  n: "达州",
                  g: "107.494973,31.214199|14"
              }, {
                  n: "德阳",
                  g: "104.402398,31.13114|13"
              }, {
                  n: "甘孜州",
                  g: "101.969232,30.055144|15"
              }, {
                  n: "广安",
                  g: "106.63572,30.463984|13"
              }, {
                  n: "广元",
                  g: "105.819687,32.44104|13"
              }, {
                  n: "乐山",
                  g: "103.760824,29.600958|13"
              }, {
                  n: "凉山州",
                  g: "102.259591,27.892393|14"
              }, {
                  n: "泸州",
                  g: "105.44397,28.89593|14"
              }, {
                  n: "南充",
                  g: "106.105554,30.800965|13"
              }, {
                  n: "眉山",
                  g: "103.84143,30.061115|13"
              }, {
                  n: "绵阳",
                  g: "104.705519,31.504701|12"
              }, {
                  n: "内江",
                  g: "105.073056,29.599462|13"
              }, {
                  n: "攀枝花",
                  g: "101.722423,26.587571|14"
              }, {
                  n: "遂宁",
                  g: "105.564888,30.557491|12"
              }, {
                  n: "雅安",
                  g: "103.009356,29.999716|13"
              }, {
                  n: "宜宾",
                  g: "104.633019,28.769675|13"
              }, {
                  n: "资阳",
                  g: "104.63593,30.132191|13"
              }, {
                  n: "自贡",
                  g: "104.776071,29.359157|13"
              }]
          }, {
              n: "西藏",
              g: "89.137982,31.367315|6",
              cities: [{
                  n: "拉萨",
                  g: "91.111891,29.662557|13"
              }, {
                  n: "阿里地区",
                  g: "81.107669,30.404557|11"
              }, {
                  n: "昌都地区",
                  g: "97.185582,31.140576|15"
              }, {
                  n: "林芝地区",
                  g: "94.349985,29.666941|11"
              }, {
                  n: "那曲地区",
                  g: "92.067018,31.48068|14"
              }, {
                  n: "日喀则地区",
                  g: "88.891486,29.269023|14"
              }, {
                  n: "山南地区",
                  g: "91.750644,29.229027|11"
              }]
          }, {
              n: "新疆",
              g: "85.614899,42.127001|6",
              cities: [{
                  n: "乌鲁木齐",
                  g: "87.564988,43.84038|12"
              }, {
                  n: "阿拉尔",
                  g: "81.291737,40.61568|13"
              }, {
                  n: "阿克苏地区",
                  g: "80.269846,41.171731|12"
              }, {
                  n: "阿勒泰地区",
                  g: "88.137915,47.839744|13"
              }, {
                  n: "巴音郭楞",
                  g: "86.121688,41.771362|12"
              }, {
                  n: "博尔塔拉州",
                  g: "82.052436,44.913651|11"
              }, {
                  n: "昌吉州",
                  g: "87.296038,44.007058|13"
              }, {
                  n: "哈密地区",
                  g: "93.528355,42.858596|13"
              }, {
                  n: "和田地区",
                  g: "79.930239,37.116774|13"
              }, {
                  n: "喀什地区",
                  g: "75.992973,39.470627|12"
              }, {
                  n: "克拉玛依",
                  g: "84.88118,45.594331|13"
              }, {
                  n: "克孜勒苏州",
                  g: "76.137564,39.750346|11"
              }, {
                  n: "石河子",
                  g: "86.041865,44.308259|13"
              }, {
                  n: "塔城地区",
                  g: "82.974881,46.758684|12"
              }, {
                  n: "图木舒克",
                  g: "79.198155,39.889223|13"
              }, {
                  n: "吐鲁番地区",
                  g: "89.181595,42.96047|13"
              }, {
                  n: "五家渠",
                  g: "87.565449,44.368899|13"
              }, {
                  n: "伊犁州",
                  g: "81.297854,43.922248|11"
              }]
          }, {
              n: "云南",
              g: "101.592952,24.864213|7",
              cities: [{
                  n: "昆明",
                  g: "102.714601,25.049153|12"
              }, {
                  n: "保山",
                  g: "99.177996,25.120489|13"
              }, {
                  n: "楚雄州",
                  g: "101.529382,25.066356|13"
              }, {
                  n: "大理州",
                  g: "100.223675,25.5969|14"
              }, {
                  n: "德宏州",
                  g: "98.589434,24.44124|14"
              }, {
                  n: "迪庆州",
                  g: "99.713682,27.831029|14"
              }, {
                  n: "红河州",
                  g: "103.384065,23.367718|11"
              }, {
                  n: "丽江",
                  g: "100.229628,26.875351|13"
              }, {
                  n: "临沧",
                  g: "100.092613,23.887806|14"
              }, {
                  n: "怒江州",
                  g: "98.859932,25.860677|14"
              }, {
                  n: "普洱",
                  g: "100.980058,22.788778|14"
              }, {
                  n: "曲靖",
                  g: "103.782539,25.520758|12"
              }, {
                  n: "昭通",
                  g: "103.725021,27.340633|13"
              }, {
                  n: "文山",
                  g: "104.089112,23.401781|14"
              }, {
                  n: "西双版纳",
                  g: "100.803038,22.009433|13"
              }, {
                  n: "玉溪",
                  g: "102.545068,24.370447|13"
              }]
          }, {
              n: "浙江",
              g: "119.957202,29.159494|8",
              cities: [{
                  n: "杭州",
                  g: "120.219375,30.259244|12"
              }, {
                  n: "湖州",
                  g: "120.137243,30.877925|12"
              }, {
                  n: "嘉兴",
                  g: "120.760428,30.773992|13"
              }, {
                  n: "金华",
                  g: "119.652576,29.102899|12"
              }, {
                  n: "丽水",
                  g: "119.929576,28.4563|13"
              }, {
                  n: "宁波",
                  g: "121.579006,29.885259|12"
              }, {
                  n: "衢州",
                  g: "118.875842,28.95691|12"
              }, {
                  n: "绍兴",
                  g: "120.592467,30.002365|13"
              }, {
                  n: "台州",
                  g: "121.440613,28.668283|13"
              }, {
                  n: "温州",
                  g: "120.690635,28.002838|12"
              }, {
                  n: "舟山",
                  g: "122.169872,30.03601|13"
              }]
          }],
          other: [{
              n: "香港",
              g: "114.186124,22.293586|11"
          }, {
              n: "澳门",
              g: "113.557519,22.204118|13"
          }, {
              n: "台湾",
              g: "120.961454,23.80406|8"
          }]
      };
  
      function getCenter(g) {
          var item = g.split("|");
          item[0] = item[0].split(",");
          return {
              lng: parseFloat(item[0][0]),
              lat: parseFloat(item[0][1])
          };
      }
  
      var cityCenter = {
          getCenterByCityName: function getCenterByCityName(name) {
              for (var i = 0; i < citycenter.municipalities.length; i++) {
                  if (citycenter.municipalities[i].n == name) {
                      return getCenter(citycenter.municipalities[i].g);
                  }
              }
  
              var provinces = citycenter.provinces;
              for (var i = 0; i < provinces.length; i++) {
                  if (provinces[i].n == name) {
                      return getCenter(provinces[i].g);
                  }
                  var cities = provinces[i].cities;
                  for (var j = 0; j < cities.length; j++) {
                      if (cities[j].n == name) {
                          return getCenter(cities[j].g);
                      }
                  }
              }
              return null;
          }
      };
  
      /**
       * 根据弧线的坐标节点数组
       */
      function getCurvePoints(points) {
          var curvePoints = [];
          for (var i = 0; i < points.length - 1; i++) {
              var p = getCurveByTwoPoints(points[i], points[i + 1]);
              if (p && p.length > 0) {
                  curvePoints = curvePoints.concat(p);
              }
          }
          return curvePoints;
      }
  
      /**
       * 根据两点获取曲线坐标点数组
       * @param Point 起点
       * @param Point 终点
       */
      function getCurveByTwoPoints(obj1, obj2) {
          if (!obj1 || !obj2) {
              return null;
          }
  
          var B1 = function B1(x) {
              return 1 - 2 * x + x * x;
          };
          var B2 = function B2(x) {
              return 2 * x - 2 * x * x;
          };
          var B3 = function B3(x) {
              return x * x;
          };
  
          var curveCoordinates = [];
  
          var count = 40; // 曲线是由一些小的线段组成的，这个表示这个曲线所有到的折线的个数
          var isFuture = false;
          var t, h, h2, lat3, lng3, j, t2;
          var LnArray = [];
          var i = 0;
          var inc = 0;
  
          if (typeof obj2 == "undefined") {
              if (typeof curveCoordinates != "undefined") {
                  curveCoordinates = [];
              }
              return;
          }
  
          var lat1 = parseFloat(obj1.lat);
          var lat2 = parseFloat(obj2.lat);
          var lng1 = parseFloat(obj1.lng);
          var lng2 = parseFloat(obj2.lng);
  
          // 计算曲线角度的方法
          if (lng2 > lng1) {
              if (parseFloat(lng2 - lng1) > 180) {
                  if (lng1 < 0) {
                      lng1 = parseFloat(180 + 180 + lng1);
                  }
              }
          }
  
          if (lng1 > lng2) {
              if (parseFloat(lng1 - lng2) > 180) {
                  if (lng2 < 0) {
                      lng2 = parseFloat(180 + 180 + lng2);
                  }
              }
          }
          j = 0;
          t2 = 0;
          if (lat2 == lat1) {
              t = 0;
              h = lng1 - lng2;
          } else if (lng2 == lng1) {
              t = Math.PI / 2;
              h = lat1 - lat2;
          } else {
              t = Math.atan((lat2 - lat1) / (lng2 - lng1));
              h = (lat2 - lat1) / Math.sin(t);
          }
          if (t2 == 0) {
              t2 = t + Math.PI / 5;
          }
          h2 = h / 2;
          lng3 = h2 * Math.cos(t2) + lng1;
          lat3 = h2 * Math.sin(t2) + lat1;
  
          for (i = 0; i < count + 1; i++) {
              curveCoordinates.push([lng1 * B1(inc) + lng3 * B2(inc) + lng2 * B3(inc), lat1 * B1(inc) + lat3 * B2(inc) + lat2 * B3(inc)]);
              inc = inc + 1 / count;
          }
          return curveCoordinates;
      }
  
      var curve = {
          getPoints: getCurvePoints
      };
  
      /* 
      FDEB algorithm implementation [www.win.tue.nl/~dholten/papers/forcebundles_eurovis.pdf].
  
      Author:  (github.com/upphiminn)
      2013
  
      */
  
      var ForceEdgeBundling = function ForceEdgeBundling() {
          var data_nodes = {},
              // {'nodeid':{'x':,'y':},..}
              data_edges = [],
              // [{'source':'nodeid1', 'target':'nodeid2'},..]
              compatibility_list_for_edge = [],
              subdivision_points_for_edge = [],
              K = 0.1,
              // global bundling constant controling edge stiffness
              S_initial = 0.1,
              // init. distance to move points
              P_initial = 1,
              // init. subdivision number
              P_rate = 2,
              // subdivision rate increase
              C = 6,
              // number of cycles to perform
              I_initial = 70,
              // init. number of iterations for cycle
              I_rate = 0.6666667,
              // rate at which iteration number decreases i.e. 2/3
              compatibility_threshold = 0.6,
              invers_quadratic_mode = false,
              eps = 1e-8;
  
          /*** Geometry Helper Methods ***/
          function vector_dot_product(p, q) {
              return p.x * q.x + p.y * q.y;
          }
  
          function edge_as_vector(P) {
              return {
                  'x': data_nodes[P.target].x - data_nodes[P.source].x,
                  'y': data_nodes[P.target].y - data_nodes[P.source].y
              };
          }
  
          function edge_length(e) {
              return Math.sqrt(Math.pow(data_nodes[e.source].x - data_nodes[e.target].x, 2) + Math.pow(data_nodes[e.source].y - data_nodes[e.target].y, 2));
          }
  
          function custom_edge_length(e) {
              return Math.sqrt(Math.pow(e.source.x - e.target.x, 2) + Math.pow(e.source.y - e.target.y, 2));
          }
  
          function edge_midpoint(e) {
              var middle_x = (data_nodes[e.source].x + data_nodes[e.target].x) / 2.0;
              var middle_y = (data_nodes[e.source].y + data_nodes[e.target].y) / 2.0;
              return {
                  'x': middle_x,
                  'y': middle_y
              };
          }
  
          function compute_divided_edge_length(e_idx) {
              var length = 0;
              for (var i = 1; i < subdivision_points_for_edge[e_idx].length; i++) {
                  var segment_length = euclidean_distance(subdivision_points_for_edge[e_idx][i], subdivision_points_for_edge[e_idx][i - 1]);
                  length += segment_length;
              }
              return length;
          }
  
          function euclidean_distance(p, q) {
              return Math.sqrt(Math.pow(p.x - q.x, 2) + Math.pow(p.y - q.y, 2));
          }
  
          function project_point_on_line(p, Q) {
              var L = Math.sqrt((Q.target.x - Q.source.x) * (Q.target.x - Q.source.x) + (Q.target.y - Q.source.y) * (Q.target.y - Q.source.y));
              var r = ((Q.source.y - p.y) * (Q.source.y - Q.target.y) - (Q.source.x - p.x) * (Q.target.x - Q.source.x)) / (L * L);
  
              return {
                  'x': Q.source.x + r * (Q.target.x - Q.source.x),
                  'y': Q.source.y + r * (Q.target.y - Q.source.y)
              };
          }
  
          /*** ********************** ***/
  
          /*** Initialization Methods ***/
          function initialize_edge_subdivisions() {
              for (var i = 0; i < data_edges.length; i++) {
                  if (P_initial == 1) subdivision_points_for_edge[i] = []; //0 subdivisions
                  else {
                      subdivision_points_for_edge[i] = [];
                      subdivision_points_for_edge[i].push(data_nodes[data_edges[i].source]);
                      subdivision_points_for_edge[i].push(data_nodes[data_edges[i].target]);
                  }
              }
          }
  
          function initialize_compatibility_lists() {
              for (var i = 0; i < data_edges.length; i++) {
                  compatibility_list_for_edge[i] = [];
              } //0 compatible edges.
          }
  
          function filter_self_loops(edgelist) {
              var filtered_edge_list = [];
              for (var e = 0; e < edgelist.length; e++) {
                  if (data_nodes[edgelist[e].source].x != data_nodes[edgelist[e].target].x && data_nodes[edgelist[e].source].y != data_nodes[edgelist[e].target].y) {
                      //or smaller than eps
                      filtered_edge_list.push(edgelist[e]);
                  }
              }
  
              return filtered_edge_list;
          }
          /*** ********************** ***/
  
          /*** Force Calculation Methods ***/
          function apply_spring_force(e_idx, i, kP) {
  
              var prev = subdivision_points_for_edge[e_idx][i - 1];
              var next = subdivision_points_for_edge[e_idx][i + 1];
              var crnt = subdivision_points_for_edge[e_idx][i];
  
              var x = prev.x - crnt.x + next.x - crnt.x;
              var y = prev.y - crnt.y + next.y - crnt.y;
  
              x *= kP;
              y *= kP;
  
              return {
                  'x': x,
                  'y': y
              };
          }
  
          function apply_electrostatic_force(e_idx, i, S) {
              var sum_of_forces = {
                  'x': 0,
                  'y': 0
              };
              var compatible_edges_list = compatibility_list_for_edge[e_idx];
  
              for (var oe = 0; oe < compatible_edges_list.length; oe++) {
                  var force = {
                      'x': subdivision_points_for_edge[compatible_edges_list[oe]][i].x - subdivision_points_for_edge[e_idx][i].x,
                      'y': subdivision_points_for_edge[compatible_edges_list[oe]][i].y - subdivision_points_for_edge[e_idx][i].y
                  };
  
                  if (Math.abs(force.x) > eps || Math.abs(force.y) > eps) {
  
                      var diff = 1 / Math.pow(custom_edge_length({
                          'source': subdivision_points_for_edge[compatible_edges_list[oe]][i],
                          'target': subdivision_points_for_edge[e_idx][i]
                      }), 1);
  
                      sum_of_forces.x += force.x * diff;
                      sum_of_forces.y += force.y * diff;
                  }
              }
              return sum_of_forces;
          }
  
          function apply_resulting_forces_on_subdivision_points(e_idx, P, S) {
              var kP = K / (edge_length(data_edges[e_idx]) * (P + 1)); // kP=K/|P|(number of segments), where |P| is the initial length of edge P.
              // (length * (num of sub division pts - 1))
              var resulting_forces_for_subdivision_points = [{
                  'x': 0,
                  'y': 0
              }];
              for (var i = 1; i < P + 1; i++) {
                  // exclude initial end points of the edge 0 and P+1
                  var resulting_force = {
                      'x': 0,
                      'y': 0
                  };
  
                  var spring_force = apply_spring_force(e_idx, i, kP);
                  var electrostatic_force = apply_electrostatic_force(e_idx, i, S);
  
                  resulting_force.x = S * (spring_force.x + electrostatic_force.x);
                  resulting_force.y = S * (spring_force.y + electrostatic_force.y);
  
                  resulting_forces_for_subdivision_points.push(resulting_force);
              }
              resulting_forces_for_subdivision_points.push({
                  'x': 0,
                  'y': 0
              });
              return resulting_forces_for_subdivision_points;
          }
          /*** ********************** ***/
  
          /*** Edge Division Calculation Methods ***/
          function update_edge_divisions(P) {
              for (var e_idx = 0; e_idx < data_edges.length; e_idx++) {
  
                  if (P == 1) {
                      subdivision_points_for_edge[e_idx].push(data_nodes[data_edges[e_idx].source]); // source
                      subdivision_points_for_edge[e_idx].push(edge_midpoint(data_edges[e_idx])); // mid point
                      subdivision_points_for_edge[e_idx].push(data_nodes[data_edges[e_idx].target]); // target
                  } else {
  
                      var divided_edge_length = compute_divided_edge_length(e_idx);
                      var segment_length = divided_edge_length / (P + 1);
                      var current_segment_length = segment_length;
                      var new_subdivision_points = [];
                      new_subdivision_points.push(data_nodes[data_edges[e_idx].source]); //source
  
                      for (var i = 1; i < subdivision_points_for_edge[e_idx].length; i++) {
                          var old_segment_length = euclidean_distance(subdivision_points_for_edge[e_idx][i], subdivision_points_for_edge[e_idx][i - 1]);
  
                          while (old_segment_length > current_segment_length) {
                              var percent_position = current_segment_length / old_segment_length;
                              var new_subdivision_point_x = subdivision_points_for_edge[e_idx][i - 1].x;
                              var new_subdivision_point_y = subdivision_points_for_edge[e_idx][i - 1].y;
  
                              new_subdivision_point_x += percent_position * (subdivision_points_for_edge[e_idx][i].x - subdivision_points_for_edge[e_idx][i - 1].x);
                              new_subdivision_point_y += percent_position * (subdivision_points_for_edge[e_idx][i].y - subdivision_points_for_edge[e_idx][i - 1].y);
                              new_subdivision_points.push({
                                  'x': new_subdivision_point_x,
                                  'y': new_subdivision_point_y
                              });
  
                              old_segment_length -= current_segment_length;
                              current_segment_length = segment_length;
                          }
                          current_segment_length -= old_segment_length;
                      }
                      new_subdivision_points.push(data_nodes[data_edges[e_idx].target]); //target
                      subdivision_points_for_edge[e_idx] = new_subdivision_points;
                  }
              }
          }
          /*** ********************** ***/
  
          /*** Edge compatibility measures ***/
          function angle_compatibility(P, Q) {
              var result = Math.abs(vector_dot_product(edge_as_vector(P), edge_as_vector(Q)) / (edge_length(P) * edge_length(Q)));
              return result;
          }
  
          function scale_compatibility(P, Q) {
              var lavg = (edge_length(P) + edge_length(Q)) / 2.0;
              var result = 2.0 / (lavg / Math.min(edge_length(P), edge_length(Q)) + Math.max(edge_length(P), edge_length(Q)) / lavg);
              return result;
          }
  
          function position_compatibility(P, Q) {
              var lavg = (edge_length(P) + edge_length(Q)) / 2.0;
              var midP = {
                  'x': (data_nodes[P.source].x + data_nodes[P.target].x) / 2.0,
                  'y': (data_nodes[P.source].y + data_nodes[P.target].y) / 2.0
              };
              var midQ = {
                  'x': (data_nodes[Q.source].x + data_nodes[Q.target].x) / 2.0,
                  'y': (data_nodes[Q.source].y + data_nodes[Q.target].y) / 2.0
              };
              var result = lavg / (lavg + euclidean_distance(midP, midQ));
              return result;
          }
  
          function edge_visibility(P, Q) {
              var I0 = project_point_on_line(data_nodes[Q.source], {
                  'source': data_nodes[P.source],
                  'target': data_nodes[P.target]
              });
              var I1 = project_point_on_line(data_nodes[Q.target], {
                  'source': data_nodes[P.source],
                  'target': data_nodes[P.target]
              }); //send acutal edge points positions
              var midI = {
                  'x': (I0.x + I1.x) / 2.0,
                  'y': (I0.y + I1.y) / 2.0
              };
              var midP = {
                  'x': (data_nodes[P.source].x + data_nodes[P.target].x) / 2.0,
                  'y': (data_nodes[P.source].y + data_nodes[P.target].y) / 2.0
              };
              var result = Math.max(0, 1 - 2 * euclidean_distance(midP, midI) / euclidean_distance(I0, I1));
              return result;
          }
  
          function visibility_compatibility(P, Q) {
              return Math.min(edge_visibility(P, Q), edge_visibility(Q, P));
          }
  
          function compatibility_score(P, Q) {
              var result = angle_compatibility(P, Q) * scale_compatibility(P, Q) * position_compatibility(P, Q) * visibility_compatibility(P, Q);
  
              return result;
          }
  
          function are_compatible(P, Q) {
              // console.log('compatibility ' + P.source +' - '+ P.target + ' and ' + Q.source +' '+ Q.target);
              return compatibility_score(P, Q) >= compatibility_threshold;
          }
  
          function compute_compatibility_lists() {
              for (var e = 0; e < data_edges.length - 1; e++) {
                  for (var oe = e + 1; oe < data_edges.length; oe++) {
                      // don't want any duplicates
                      if (e == oe) continue;
                      else {
                          if (are_compatible(data_edges[e], data_edges[oe])) {
                              compatibility_list_for_edge[e].push(oe);
                              compatibility_list_for_edge[oe].push(e);
                          }
                      }
                  }
              }
          }
  
          /*** ************************ ***/
  
          /*** Main Bundling Loop Methods ***/
          var forcebundle = function forcebundle() {
              var S = S_initial;
              var I = I_initial;
              var P = P_initial;
  
              initialize_edge_subdivisions();
              initialize_compatibility_lists();
              update_edge_divisions(P);
              compute_compatibility_lists();
              for (var cycle = 0; cycle < C; cycle++) {
                  for (var iteration = 0; iteration < I; iteration++) {
                      var forces = [];
                      for (var edge = 0; edge < data_edges.length; edge++) {
                          forces[edge] = apply_resulting_forces_on_subdivision_points(edge, P, S);
                      }
                      for (var e = 0; e < data_edges.length; e++) {
                          for (var i = 0; i < P + 1; i++) {
                              subdivision_points_for_edge[e][i].x += forces[e][i].x;
                              subdivision_points_for_edge[e][i].y += forces[e][i].y;
                          }
                      }
                  }
                  //prepare for next cycle
                  S = S / 2;
                  P = P * 2;
                  I = I_rate * I;
  
                  update_edge_divisions(P);
                  // console.log('C' + cycle);
                  // console.log('P' + P);
                  // console.log('S' + S);
              }
              return subdivision_points_for_edge;
          };
          /*** ************************ ***/
  
          /*** Getters/Setters Methods ***/
          forcebundle.nodes = function(nl) {
              if (arguments.length == 0) {
                  return data_nodes;
              } else {
                  data_nodes = nl;
              }
              return forcebundle;
          };
  
          forcebundle.edges = function(ll) {
              if (arguments.length == 0) {
                  return data_edges;
              } else {
                  data_edges = filter_self_loops(ll); //remove edges to from to the same point
              }
              return forcebundle;
          };
  
          forcebundle.bundling_stiffness = function(k) {
              if (arguments.length == 0) {
                  return K;
              } else {
                  K = k;
              }
              return forcebundle;
          };
  
          forcebundle.step_size = function(step) {
              if (arguments.length == 0) {
                  return S_initial;
              } else {
                  S_initial = step;
              }
              return forcebundle;
          };
  
          forcebundle.cycles = function(c) {
              if (arguments.length == 0) {
                  return C;
              } else {
                  C = c;
              }
              return forcebundle;
          };
  
          forcebundle.iterations = function(i) {
              if (arguments.length == 0) {
                  return I_initial;
              } else {
                  I_initial = i;
              }
              return forcebundle;
          };
  
          forcebundle.iterations_rate = function(i) {
              if (arguments.length == 0) {
                  return I_rate;
              } else {
                  I_rate = i;
              }
              return forcebundle;
          };
  
          forcebundle.subdivision_points_seed = function(p) {
              if (arguments.length == 0) {
                  return P;
              } else {
                  P = p;
              }
              return forcebundle;
          };
  
          forcebundle.subdivision_rate = function(r) {
              if (arguments.length == 0) {
                  return P_rate;
              } else {
                  P_rate = r;
              }
              return forcebundle;
          };
  
          forcebundle.compatbility_threshold = function(t) {
              if (arguments.length == 0) {
                  return compatbility_threshold;
              } else {
                  compatibility_threshold = t;
              }
              return forcebundle;
          };
  
          /*** ************************ ***/
  
          return forcebundle;
      };
  
      /**
       * @author kyle / http://nikai.us/
       */
  
      /**
       * Category
       * @param {Object} splitList:
       *   { 
       *       other: 1,
       *       1: 2,
       *       2: 3,
       *       3: 4,
       *       4: 5,
       *       5: 6,
       *       6: 7
       *   }
       */
      function Category(splitList) {
          this.splitList = splitList || {
              other: 1
          };
      }
  
      Category.prototype.get = function(count) {
  
          var splitList = this.splitList;
  
          var value = splitList['other'];
  
          for (var i in splitList) {
              if (count == i) {
                  value = splitList[i];
                  break;
              }
          }
  
          return value;
      };
  
      /**
       * 根据DataSet自动生成对应的splitList
       */
      Category.prototype.generateByDataSet = function(dataSet) {
          var colors = ['rgba(255, 255, 0, 0.8)', 'rgba(253, 98, 104, 0.8)', 'rgba(255, 146, 149, 0.8)', 'rgba(255, 241, 193, 0.8)', 'rgba(110, 176, 253, 0.8)', 'rgba(52, 139, 251, 0.8)', 'rgba(17, 102, 252, 0.8)'];
          var data = dataSet.get();
          this.splitList = {};
          var count = 0;
          for (var i = 0; i < data.length; i++) {
              if (this.splitList[data[i].count] === undefined) {
                  this.splitList[data[i].count] = colors[count];
                  count++;
              }
              if (count >= colors.length - 1) {
                  break;
              }
          }
  
          this.splitList['other'] = colors[colors.length - 1];
      };
  
      /**
       * @author kyle / http://nikai.us/
       */
  
      /**
       * Choropleth
       * @param {Object} splitList:
       *       [
       *           {
       *               start: 0,
       *               end: 2,
       *               value: randomColor()
       *           },{
       *               start: 2,
       *               end: 4,
       *               value: randomColor()
       *           },{
       *               start: 4,
       *               value: randomColor()
       *           }
       *       ];
       *
       */
      function Choropleth(splitList) {
          this.splitList = splitList || [{
              start: 0,
              value: 'red'
          }];
      }
  
      Choropleth.prototype.get = function(count) {
          var splitList = this.splitList;
  
          var value = false;
  
          for (var i = 0; i < splitList.length; i++) {
              if ((splitList[i].start === undefined || splitList[i].start !== undefined && count >= splitList[i].start) && (splitList[i].end === undefined || splitList[i].end !== undefined && count < splitList[i].end)) {
                  value = splitList[i].value;
                  break;
              }
          }
  
          return value;
      };
  
      /**
       * 根据DataSet自动生成对应的splitList
       */
      Choropleth.prototype.generateByDataSet = function(dataSet) {
  
          var min = dataSet.getMin('count');
          var max = dataSet.getMax('count');
  
          this.generateByMinMax(min, max);
      };
  
      /**
       * 根据DataSet自动生成对应的splitList
       */
      Choropleth.prototype.generateByMinMax = function(min, max) {
          var colors = ['rgba(255, 255, 0, 0.8)', 'rgba(253, 98, 104, 0.8)', 'rgba(255, 146, 149, 0.8)', 'rgba(255, 241, 193, 0.8)', 'rgba(110, 176, 253, 0.8)', 'rgba(52, 139, 251, 0.8)', 'rgba(17, 102, 252, 0.8)'];
          var splitNum = (max - min) / 7;
          var index = min;
          this.splitList = [];
          var count = 0;
          while (index < max) {
              this.splitList.push({
                  start: index,
                  end: index + splitNum,
                  value: colors[count]
              });
              count++;
              index += splitNum;
          }
      };
  
      /**
       * @author Mofei<http://www.zhuwenlong.com>
       */
  
      var MapHelper = function() {
          function MapHelper(id, type, opt) {
              classCallCheck(this, MapHelper);
  
              if (!id || !type) {
                  console.warn('id 和 type 为必填项');
                  return false;
              }
  
              if (type == 'baidu') {
                  if (!BMap) {
                      console.warn('请先引入百度地图JS API');
                      return false;
                  }
              } else {
                  console.warn('暂不支持你的地图类型');
              }
              this.type = type;
              var center = opt && opt.center ? opt.center : [106.962497, 38.208726];
              var zoom = opt && opt.zoom ? opt.zoom : 5;
              var map = this.map = new BMap.Map(id, {
                  enableMapClick: false
              });
              map.centerAndZoom(new BMap.Point(center[0], center[1]), zoom);
              map.enableScrollWheelZoom(true);
  
              map.setMapStyle({
                  style: 'light'
              });
          }
  
          createClass(MapHelper, [{
              key: 'addLayer',
              value: function addLayer(datas, options) {
                  if (this.type == 'baidu') {
                      return new mapv.baiduMapLayer(this.map, dataSet, options);
                  }
              }
          }, {
              key: 'getMap',
              value: function getMap() {
                  return this.map;
              }
          }]);
          return MapHelper;
      }();
  
      /**
       * 一直覆盖在当前地图视野的Canvas对象
       *
       * @author nikai (@胖嘟嘟的骨头, nikai@baidu.com)
       *
       * @param 
       * {
       *     map 地图实例对象
       * }
       */
  
      function CanvasLayer(options) {
          this.options = options || {};
          this.paneName = this.options.paneName || 'mapPane';
          this.context = this.options.context || '2d';
          this.zIndex = this.options.zIndex || 0;
          this.mixBlendMode = this.options.mixBlendMode || null;
          this.enableMassClear = this.options.enableMassClear;
          this._map = options.map;
          this._lastDrawTime = null;
          this.show();
      }
  
      var global$3 = typeof window === 'undefined' ? {} : window;
  
      if (global$3.BMap) {
  
          CanvasLayer.prototype = new BMap.Overlay();
  
          CanvasLayer.prototype.initialize = function(map) {
              this._map = map;
              var canvas = this.canvas = document.createElement("canvas");
              canvas.style.cssText = "position:absolute;" + "left:0;" + "top:0;" + "z-index:" + this.zIndex + ";user-select:none;";
              canvas.style.mixBlendMode = this.mixBlendMode;
              this.adjustSize();
              map.getPanes()[this.paneName].appendChild(canvas);
              var that = this;
              map.addEventListener('resize', function() {
                  that.adjustSize();
                  that._draw();
              });
              return this.canvas;
          };
  
          CanvasLayer.prototype.adjustSize = function() {
              var size = this._map.getSize();
              var canvas = this.canvas;
  
              var devicePixelRatio = this.devicePixelRatio = global$3.devicePixelRatio;
  
              canvas.width = size.width * devicePixelRatio;
              canvas.height = size.height * devicePixelRatio;
              if (this.context == '2d') {
                  canvas.getContext(this.context).scale(devicePixelRatio, devicePixelRatio);
              }
  
              canvas.style.width = size.width + "px";
              canvas.style.height = size.height + "px";
          };
  
          CanvasLayer.prototype.draw = function() {
              var self = this;
              clearTimeout(self.timeoutID);
              self.timeoutID = setTimeout(function() {
                  self._draw();
              }, 15);
          };
  
          CanvasLayer.prototype._draw = function() {
              var map = this._map;
              var size = map.getSize();
              var center = map.getCenter();
              if (center) {
                  var pixel = map.pointToOverlayPixel(center);
                  this.canvas.style.left = pixel.x - size.width / 2 + 'px';
                  this.canvas.style.top = pixel.y - size.height / 2 + 'px';
                  this.dispatchEvent('draw');
                  this.options.update && this.options.update.call(this);
              }
          };
  
          CanvasLayer.prototype.getContainer = function() {
              return this.canvas;
          };
  
          CanvasLayer.prototype.show = function() {
              if (!this.canvas) {
                  this._map.addOverlay(this);
              }
              this.canvas.style.display = "block";
          };
  
          CanvasLayer.prototype.hide = function() {
              this.canvas.style.display = "none";
              //this._map.removeOverlay(this);
          };
  
          CanvasLayer.prototype.setZIndex = function(zIndex) {
              this.canvas.style.zIndex = zIndex;
          };
  
          CanvasLayer.prototype.getZIndex = function() {
              return this.zIndex;
          };
      }
  
      /**
       * Tween.js - Licensed under the MIT license
       * https://github.com/tweenjs/tween.js
       * ----------------------------------------------
       *
       * See https://github.com/tweenjs/tween.js/graphs/contributors for the full list of contributors.
       * Thank you all, you're awesome!
       */
  
      var TWEEN = TWEEN || function() {
  
          var _tweens = [];
  
          return {
  
              getAll: function getAll() {
  
                  return _tweens;
              },
  
              removeAll: function removeAll() {
  
                  _tweens = [];
              },
  
              add: function add(tween) {
  
                  _tweens.push(tween);
              },
  
              remove: function remove(tween) {
  
                  var i = _tweens.indexOf(tween);
  
                  if (i !== -1) {
                      _tweens.splice(i, 1);
                  }
              },
  
              update: function update(time, preserve) {
  
                  if (_tweens.length === 0) {
                      return false;
                  }
  
                  var i = 0;
  
                  time = time !== undefined ? time : TWEEN.now();
  
                  while (i < _tweens.length) {
  
                      if (_tweens[i].update(time) || preserve) {
                          i++;
                      } else {
                          _tweens.splice(i, 1);
                      }
                  }
  
                  return true;
              }
          };
      }();
  
      // Include a performance.now polyfill.
      // In node.js, use process.hrtime.
      if (typeof window === 'undefined' && typeof process !== 'undefined') {
          TWEEN.now = function() {
              var time = process.hrtime();
  
              // Convert [seconds, nanoseconds] to milliseconds.
              return time[0] * 1000 + time[1] / 1000000;
          };
      }
      // In a browser, use window.performance.now if it is available.
      else if (typeof window !== 'undefined' && window.performance !== undefined && window.performance.now !== undefined) {
          // This must be bound, because directly assigning this function
          // leads to an invocation exception in Chrome.
          TWEEN.now = window.performance.now.bind(window.performance);
      }
      // Use Date.now if it is available.
      else if (Date.now !== undefined) {
          TWEEN.now = Date.now;
      }
      // Otherwise, use 'new Date().getTime()'.
      else {
          TWEEN.now = function() {
              return new Date().getTime();
          };
      }
  
      TWEEN.Tween = function(object) {
  
          var _object = object;
          var _valuesStart = {};
          var _valuesEnd = {};
          var _valuesStartRepeat = {};
          var _duration = 1000;
          var _repeat = 0;
          var _repeatDelayTime;
          var _yoyo = false;
          var _isPlaying = false;
          var _reversed = false;
          var _delayTime = 0;
          var _startTime = null;
          var _easingFunction = TWEEN.Easing.Linear.None;
          var _interpolationFunction = TWEEN.Interpolation.Linear;
          var _chainedTweens = [];
          var _onStartCallback = null;
          var _onStartCallbackFired = false;
          var _onUpdateCallback = null;
          var _onCompleteCallback = null;
          var _onStopCallback = null;
  
          this.to = function(properties, duration) {
  
              _valuesEnd = properties;
  
              if (duration !== undefined) {
                  _duration = duration;
              }
  
              return this;
          };
  
          this.start = function(time) {
  
              TWEEN.add(this);
  
              _isPlaying = true;
  
              _onStartCallbackFired = false;
  
              _startTime = time !== undefined ? time : TWEEN.now();
              _startTime += _delayTime;
  
              for (var property in _valuesEnd) {
  
                  // Check if an Array was provided as property value
                  if (_valuesEnd[property] instanceof Array) {
  
                      if (_valuesEnd[property].length === 0) {
                          continue;
                      }
  
                      // Create a local copy of the Array with the start value at the front
                      _valuesEnd[property] = [_object[property]].concat(_valuesEnd[property]);
                  }
  
                  // If `to()` specifies a property that doesn't exist in the source object,
                  // we should not set that property in the object
                  if (_object[property] === undefined) {
                      continue;
                  }
  
                  // Save the starting value.
                  _valuesStart[property] = _object[property];
  
                  if (_valuesStart[property] instanceof Array === false) {
                      _valuesStart[property] *= 1.0; // Ensures we're using numbers, not strings
                  }
  
                  _valuesStartRepeat[property] = _valuesStart[property] || 0;
              }
  
              return this;
          };
  
          this.stop = function() {
  
              if (!_isPlaying) {
                  return this;
              }
  
              TWEEN.remove(this);
              _isPlaying = false;
  
              if (_onStopCallback !== null) {
                  _onStopCallback.call(_object, _object);
              }
  
              this.stopChainedTweens();
              return this;
          };
  
          this.end = function() {
  
              this.update(_startTime + _duration);
              return this;
          };
  
          this.stopChainedTweens = function() {
  
              for (var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++) {
                  _chainedTweens[i].stop();
              }
          };
  
          this.delay = function(amount) {
  
              _delayTime = amount;
              return this;
          };
  
          this.repeat = function(times) {
  
              _repeat = times;
              return this;
          };
  
          this.repeatDelay = function(amount) {
  
              _repeatDelayTime = amount;
              return this;
          };
  
          this.yoyo = function(yoyo) {
  
              _yoyo = yoyo;
              return this;
          };
  
          this.easing = function(easing) {
  
              _easingFunction = easing;
              return this;
          };
  
          this.interpolation = function(interpolation) {
  
              _interpolationFunction = interpolation;
              return this;
          };
  
          this.chain = function() {
  
              _chainedTweens = arguments;
              return this;
          };
  
          this.onStart = function(callback) {
  
              _onStartCallback = callback;
              return this;
          };
  
          this.onUpdate = function(callback) {
  
              _onUpdateCallback = callback;
              return this;
          };
  
          this.onComplete = function(callback) {
  
              _onCompleteCallback = callback;
              return this;
          };
  
          this.onStop = function(callback) {
  
              _onStopCallback = callback;
              return this;
          };
  
          this.update = function(time) {
  
              var property;
              var elapsed;
              var value;
  
              if (time < _startTime) {
                  return true;
              }
  
              if (_onStartCallbackFired === false) {
  
                  if (_onStartCallback !== null) {
                      _onStartCallback.call(_object, _object);
                  }
  
                  _onStartCallbackFired = true;
              }
  
              elapsed = (time - _startTime) / _duration;
              elapsed = elapsed > 1 ? 1 : elapsed;
  
              value = _easingFunction(elapsed);
  
              for (property in _valuesEnd) {
  
                  // Don't update properties that do not exist in the source object
                  if (_valuesStart[property] === undefined) {
                      continue;
                  }
  
                  var start = _valuesStart[property] || 0;
                  var end = _valuesEnd[property];
  
                  if (end instanceof Array) {
  
                      _object[property] = _interpolationFunction(end, value);
                  } else {
  
                      // Parses relative end values with start as base (e.g.: +10, -3)
                      if (typeof end === 'string') {
  
                          if (end.charAt(0) === '+' || end.charAt(0) === '-') {
                              end = start + parseFloat(end);
                          } else {
                              end = parseFloat(end);
                          }
                      }
  
                      // Protect against non numeric properties.
                      if (typeof end === 'number') {
                          _object[property] = start + (end - start) * value;
                      }
                  }
              }
  
              if (_onUpdateCallback !== null) {
                  _onUpdateCallback.call(_object, value);
              }
  
              if (elapsed === 1) {
  
                  if (_repeat > 0) {
  
                      if (isFinite(_repeat)) {
                          _repeat--;
                      }
  
                      // Reassign starting values, restart by making startTime = now
                      for (property in _valuesStartRepeat) {
  
                          if (typeof _valuesEnd[property] === 'string') {
                              _valuesStartRepeat[property] = _valuesStartRepeat[property] + parseFloat(_valuesEnd[property]);
                          }
  
                          if (_yoyo) {
                              var tmp = _valuesStartRepeat[property];
  
                              _valuesStartRepeat[property] = _valuesEnd[property];
                              _valuesEnd[property] = tmp;
                          }
  
                          _valuesStart[property] = _valuesStartRepeat[property];
                      }
  
                      if (_yoyo) {
                          _reversed = !_reversed;
                      }
  
                      if (_repeatDelayTime !== undefined) {
                          _startTime = time + _repeatDelayTime;
                      } else {
                          _startTime = time + _delayTime;
                      }
  
                      return true;
                  } else {
  
                      if (_onCompleteCallback !== null) {
  
                          _onCompleteCallback.call(_object, _object);
                      }
  
                      for (var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++) {
                          // Make the chained tweens start exactly at the time they should,
                          // even if the `update()` method was called way past the duration of the tween
                          _chainedTweens[i].start(_startTime + _duration);
                      }
  
                      return false;
                  }
              }
  
              return true;
          };
      };
  
      TWEEN.Easing = {
  
          Linear: {
  
              None: function None(k) {
  
                  return k;
              }
  
          },
  
          Quadratic: {
  
              In: function In(k) {
  
                  return k * k;
              },
  
              Out: function Out(k) {
  
                  return k * (2 - k);
              },
  
              InOut: function InOut(k) {
  
                  if ((k *= 2) < 1) {
                      return 0.5 * k * k;
                  }
  
                  return -0.5 * (--k * (k - 2) - 1);
              }
  
          },
  
          Cubic: {
  
              In: function In(k) {
  
                  return k * k * k;
              },
  
              Out: function Out(k) {
  
                  return --k * k * k + 1;
              },
  
              InOut: function InOut(k) {
  
                  if ((k *= 2) < 1) {
                      return 0.5 * k * k * k;
                  }
  
                  return 0.5 * ((k -= 2) * k * k + 2);
              }
  
          },
  
          Quartic: {
  
              In: function In(k) {
  
                  return k * k * k * k;
              },
  
              Out: function Out(k) {
  
                  return 1 - --k * k * k * k;
              },
  
              InOut: function InOut(k) {
  
                  if ((k *= 2) < 1) {
                      return 0.5 * k * k * k * k;
                  }
  
                  return -0.5 * ((k -= 2) * k * k * k - 2);
              }
  
          },
  
          Quintic: {
  
              In: function In(k) {
  
                  return k * k * k * k * k;
              },
  
              Out: function Out(k) {
  
                  return --k * k * k * k * k + 1;
              },
  
              InOut: function InOut(k) {
  
                  if ((k *= 2) < 1) {
                      return 0.5 * k * k * k * k * k;
                  }
  
                  return 0.5 * ((k -= 2) * k * k * k * k + 2);
              }
  
          },
  
          Sinusoidal: {
  
              In: function In(k) {
  
                  return 1 - Math.cos(k * Math.PI / 2);
              },
  
              Out: function Out(k) {
  
                  return Math.sin(k * Math.PI / 2);
              },
  
              InOut: function InOut(k) {
  
                  return 0.5 * (1 - Math.cos(Math.PI * k));
              }
  
          },
  
          Exponential: {
  
              In: function In(k) {
  
                  return k === 0 ? 0 : Math.pow(1024, k - 1);
              },
  
              Out: function Out(k) {
  
                  return k === 1 ? 1 : 1 - Math.pow(2, -10 * k);
              },
  
              InOut: function InOut(k) {
  
                  if (k === 0) {
                      return 0;
                  }
  
                  if (k === 1) {
                      return 1;
                  }
  
                  if ((k *= 2) < 1) {
                      return 0.5 * Math.pow(1024, k - 1);
                  }
  
                  return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2);
              }
  
          },
  
          Circular: {
  
              In: function In(k) {
  
                  return 1 - Math.sqrt(1 - k * k);
              },
  
              Out: function Out(k) {
  
                  return Math.sqrt(1 - --k * k);
              },
  
              InOut: function InOut(k) {
  
                  if ((k *= 2) < 1) {
                      return -0.5 * (Math.sqrt(1 - k * k) - 1);
                  }
  
                  return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);
              }
  
          },
  
          Elastic: {
  
              In: function In(k) {
  
                  if (k === 0) {
                      return 0;
                  }
  
                  if (k === 1) {
                      return 1;
                  }
  
                  return -Math.pow(2, 10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI);
              },
  
              Out: function Out(k) {
  
                  if (k === 0) {
                      return 0;
                  }
  
                  if (k === 1) {
                      return 1;
                  }
  
                  return Math.pow(2, -10 * k) * Math.sin((k - 0.1) * 5 * Math.PI) + 1;
              },
  
              InOut: function InOut(k) {
  
                  if (k === 0) {
                      return 0;
                  }
  
                  if (k === 1) {
                      return 1;
                  }
  
                  k *= 2;
  
                  if (k < 1) {
                      return -0.5 * Math.pow(2, 10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI);
                  }
  
                  return 0.5 * Math.pow(2, -10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI) + 1;
              }
  
          },
  
          Back: {
  
              In: function In(k) {
  
                  var s = 1.70158;
  
                  return k * k * ((s + 1) * k - s);
              },
  
              Out: function Out(k) {
  
                  var s = 1.70158;
  
                  return --k * k * ((s + 1) * k + s) + 1;
              },
  
              InOut: function InOut(k) {
  
                  var s = 1.70158 * 1.525;
  
                  if ((k *= 2) < 1) {
                      return 0.5 * (k * k * ((s + 1) * k - s));
                  }
  
                  return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);
              }
  
          },
  
          Bounce: {
  
              In: function In(k) {
  
                  return 1 - TWEEN.Easing.Bounce.Out(1 - k);
              },
  
              Out: function Out(k) {
  
                  if (k < 1 / 2.75) {
                      return 7.5625 * k * k;
                  } else if (k < 2 / 2.75) {
                      return 7.5625 * (k -= 1.5 / 2.75) * k + 0.75;
                  } else if (k < 2.5 / 2.75) {
                      return 7.5625 * (k -= 2.25 / 2.75) * k + 0.9375;
                  } else {
                      return 7.5625 * (k -= 2.625 / 2.75) * k + 0.984375;
                  }
              },
  
              InOut: function InOut(k) {
  
                  if (k < 0.5) {
                      return TWEEN.Easing.Bounce.In(k * 2) * 0.5;
                  }
  
                  return TWEEN.Easing.Bounce.Out(k * 2 - 1) * 0.5 + 0.5;
              }
  
          }
  
      };
  
      TWEEN.Interpolation = {
  
          Linear: function Linear(v, k) {
  
              var m = v.length - 1;
              var f = m * k;
              var i = Math.floor(f);
              var fn = TWEEN.Interpolation.Utils.Linear;
  
              if (k < 0) {
                  return fn(v[0], v[1], f);
              }
  
              if (k > 1) {
                  return fn(v[m], v[m - 1], m - f);
              }
  
              return fn(v[i], v[i + 1 > m ? m : i + 1], f - i);
          },
  
          Bezier: function Bezier(v, k) {
  
              var b = 0;
              var n = v.length - 1;
              var pw = Math.pow;
              var bn = TWEEN.Interpolation.Utils.Bernstein;
  
              for (var i = 0; i <= n; i++) {
                  b += pw(1 - k, n - i) * pw(k, i) * v[i] * bn(n, i);
              }
  
              return b;
          },
  
          CatmullRom: function CatmullRom(v, k) {
  
              var m = v.length - 1;
              var f = m * k;
              var i = Math.floor(f);
              var fn = TWEEN.Interpolation.Utils.CatmullRom;
  
              if (v[0] === v[m]) {
  
                  if (k < 0) {
                      i = Math.floor(f = m * (1 + k));
                  }
  
                  return fn(v[(i - 1 + m) % m], v[i], v[(i + 1) % m], v[(i + 2) % m], f - i);
              } else {
  
                  if (k < 0) {
                      return v[0] - (fn(v[0], v[0], v[1], v[1], -f) - v[0]);
                  }
  
                  if (k > 1) {
                      return v[m] - (fn(v[m], v[m], v[m - 1], v[m - 1], f - m) - v[m]);
                  }
  
                  return fn(v[i ? i - 1 : 0], v[i], v[m < i + 1 ? m : i + 1], v[m < i + 2 ? m : i + 2], f - i);
              }
          },
  
          Utils: {
  
              Linear: function Linear(p0, p1, t) {
  
                  return (p1 - p0) * t + p0;
              },
  
              Bernstein: function Bernstein(n, i) {
  
                  var fc = TWEEN.Interpolation.Utils.Factorial;
  
                  return fc(n) / fc(i) / fc(n - i);
              },
  
              Factorial: function() {
  
                  var a = [1];
  
                  return function(n) {
  
                      var s = 1;
  
                      if (a[n]) {
                          return a[n];
                      }
  
                      for (var i = n; i > 1; i--) {
                          s *= i;
                      }
  
                      a[n] = s;
                      return s;
                  };
              }(),
  
              CatmullRom: function CatmullRom(p0, p1, p2, p3, t) {
  
                  var v0 = (p2 - p0) * 0.5;
                  var v1 = (p3 - p1) * 0.5;
                  var t2 = t * t;
                  var t3 = t * t2;
  
                  return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1;
              }
  
          }
  
      };
  
      /**
       * @author Mofei Zhu<mapv@zhuwenlong.com>
       * This file is to draw text
       */
  
      var drawText = {
          draw: function draw(context, dataSet, options) {
  
              var data = dataSet instanceof DataSet ? dataSet.get() : dataSet;
              context.save();
  
              // set from options
              for (var key in options) {
                  context[key] = options[key];
              }
  
              var offset = options.offset || {
                  x: 0,
                  y: 0
              };
  
              var rects = [];
  
              var size = options._size || options.size;
              if (size) {
                  context.font = "bold " + size + "px Arial";
              } else {
                  size = 12;
              }
  
              var textKey = options.textKey || 'text';
  
              context.textAlign = 'center';
              context.textBaseline = 'middle';
  
              if (options.avoid) {
                  // 标注避让
                  for (var i = 0, len = data.length; i < len; i++) {
                      var coordinates = data[i].geometry._coordinates || data[i].geometry.coordinates;
                      var x = coordinates[0] + offset.x;
                      var y = coordinates[1] + offset.y;
                      var text = data[i][textKey];
                      var textWidth = context.measureText(text).width;
  
                      // 根据文本宽度和高度调整x，y位置，使得绘制文本时候坐标点在文本中心点，这个计算出的是左上角坐标
                      var px = x - textWidth / 2;
                      var py = y - size / 2;
  
                      var rect = {
                          sw: {
                              x: px,
                              y: py + size
                          },
                          ne: {
                              x: px + textWidth,
                              y: py
                          }
                      };
  
                      if (!hasOverlay(rects, rect)) {
                          rects.push(rect);
                          px = px + textWidth / 2;
                          py = py + size / 2;
                          context.fillText(text, px, py);
                      }
                  }
              } else {
                  for (var i = 0, len = data.length; i < len; i++) {
                      var coordinates = data[i].geometry._coordinates || data[i].geometry.coordinates;
                      var x = coordinates[0] + offset.x;
                      var y = coordinates[1] + offset.y;
                      var text = data[i][textKey];
                      context.fillText(text, x, y);
                  }
              }
  
              context.restore();
          }
  
      };
  
      /*
       *  当前文字区域和已有的文字区域是否有重叠部分
       */
      function hasOverlay(rects, overlay) {
          for (var i = 0; i < rects.length; i++) {
              if (isRectOverlay(rects[i], overlay)) {
                  return true;
              }
          }
          return false;
      }
  
      //判断2个矩形是否有重叠部分
      function isRectOverlay(rect1, rect2) {
          //minx、miny 2个矩形右下角最小的x和y
          //maxx、maxy 2个矩形左上角最大的x和y
          var minx = Math.min(rect1.ne.x, rect2.ne.x);
          var miny = Math.min(rect1.sw.y, rect2.sw.y);
          var maxx = Math.max(rect1.sw.x, rect2.sw.x);
          var maxy = Math.max(rect1.ne.y, rect2.ne.y);
          if (minx > maxx && miny > maxy) {
              return true;
          }
          return false;
      }
  
      /**
       * @author Mofei Zhu<mapv@zhuwenlong.com>
       * This file is to draw text
       */
  
      var drawIcon = {
          draw: function draw(context, dataSet, options) {
              var data = dataSet instanceof DataSet ? dataSet.get() : dataSet;
  
              context.fillStyle = 'white';
              context.textAlign = 'center';
              context.textBaseline = 'middle';
  
              var offset = options.offset || {
                  x: 0,
                  y: 0
              };
  
              // set from options
              // for (var key in options) {
              //     context[key] = options[key];
              // }
              // console.log(data)
              for (var i = 0, len = data.length; i < len; i++) {
  
                  if (data[i].geometry) {
                      var deg = data[i].deg || options.deg;
                      var icon = data[i].icon || options.icon;
                      var coordinates = data[i].geometry._coordinates || data[i].geometry.coordinates;
                      var x = coordinates[0];
                      var y = coordinates[1];
                      if (deg) {
                          context.save();
                          context.translate(x, y);
                          context.rotate(deg * Math.PI / 180);
                          context.translate(-x, -y);
                      }
                      var width = options._width || options.width || icon.width;
                      var height = options._height || options.height || icon.height;
                      x = x - width / 2 + offset.x;
                      y = y - height / 2 + offset.y;
                      if (options.sx && options.sy && options.swidth && options.sheight && options.width && options.height) {
                          context.drawImage(icon, options.sx, options.sy, options.swidth, options.sheight, x, y, width, height);
                      } else if (options.width && options.height) {
                          context.drawImage(icon, x, y, width, height);
                      } else {
                          context.drawImage(icon, x, y);
                      }
  
                      if (deg) {
                          context.restore();
                      }
                  }
              }
          }
      };
  
      /**
       * @author kyle / http://nikai.us/
       */
  
      if (typeof window !== 'undefined') {
          requestAnimationFrame(animate);
      }
  
      function animate(time) {
          requestAnimationFrame(animate);
          TWEEN.update(time);
      }
  
      var BaseLayer = function() {
          function BaseLayer(map, dataSet, options) {
              classCallCheck(this, BaseLayer);
  
              if (!(dataSet instanceof DataSet)) {
                  dataSet = new DataSet(dataSet);
              }
  
              this.dataSet = dataSet;
              this.map = map;
          }
  
          createClass(BaseLayer, [{
              key: "getDefaultContextConfig",
              value: function getDefaultContextConfig() {
                  return {
                      globalAlpha: 1,
                      globalCompositeOperation: 'source-over',
                      imageSmoothingEnabled: true,
                      strokeStyle: '#000000',
                      fillStyle: '#000000',
                      shadowOffsetX: 0,
                      shadowOffsetY: 0,
                      shadowBlur: 0,
                      shadowColor: 'rgba(0, 0, 0, 0)',
                      lineWidth: 1,
                      lineCap: 'butt',
                      lineJoin: 'miter',
                      miterLimit: 10,
                      lineDashOffset: 0,
                      font: '10px sans-serif',
                      textAlign: 'start',
                      textBaseline: 'alphabetic'
                  };
              }
          }, {
              key: "initDataRange",
              value: function initDataRange(options) {
                  var self = this;
  
                  self.intensity = new Intensity({
                      maxSize: self.options.maxSize,
                      minSize: self.options.minSize,
                      gradient: self.options.gradient,
                      max: self.options.max || this.dataSet.getMax('count')
                  });
  
                  self.category = new Category(self.options.splitList);
                  self.choropleth = new Choropleth(self.options.splitList);
                  if (self.options.splitList === undefined) {
                      self.category.generateByDataSet(this.dataSet);
                  }
  
                  if (self.options.splitList === undefined) {
                      var min = self.options.min || this.dataSet.getMin('count');
                      var max = self.options.max || this.dataSet.getMax('count');
                      self.choropleth.generateByMinMax(min, max);
                  }
              }
          }, {
              key: "getLegend",
              value: function getLegend(options) {
                  var draw = this.options.draw;
                  var legend = null;
                  if (self.options.draw == 'intensity' || self.options.draw == 'heatmap') {
                      return this.intensity.getLegend(options);
                  }
              }
          }, {
              key: "processData",
              value: function processData(data) {
                  var self = this;
                  var draw = self.options.draw;
                  if (draw == 'bubble' || draw == 'intensity' || draw == 'category' || draw == 'choropleth' || draw == 'simple') {
  
                      for (var i = 0; i < data.length; i++) {
                          var item = data[i];
  
                          if (self.options.draw == 'bubble') {
                              data[i]._size = self.intensity.getSize(item.count);
                          } else {
                              data[i]._size = undefined;
                          }
  
                          var styleType = 'fillStyle';
  
                          if (data[i].geometry.type === 'LineString' || self.options.styleType === 'stroke') {
                              styleType = 'strokeStyle';
                          }
  
                          if (self.options.draw == 'intensity') {
                              data[i][styleType] = self.intensity.getColor(item.count);
                          } else if (self.options.draw == 'category') {
                              data[i][styleType] = self.category.get(item.count);
                          } else if (self.options.draw == 'choropleth') {
                              data[i][styleType] = self.choropleth.get(item.count);
                          }
                      }
                  }
              }
          }, {
              key: "isEnabledTime",
              value: function isEnabledTime() {
  
                  var animationOptions = this.options.animation;
  
                  var flag = animationOptions && !(animationOptions.enabled === false);
  
                  return flag;
              }
          }, {
              key: "argCheck",
              value: function argCheck(options) {
                  if (options.draw == 'heatmap') {
                      if (options.strokeStyle) {
                          console.warn('[heatmap] options.strokeStyle is discard, pleause use options.strength [eg: options.strength = 0.1]');
                      }
                  }
              }
          }, {
              key: "drawContext",
              value: function drawContext(context, dataSet, options, nwPixel) {
                  var self = this;
                  switch (self.options.draw) {
                      case 'heatmap':
                          drawHeatmap.draw(context, dataSet, self.options);
                          break;
                      case 'grid':
                      case 'honeycomb':
                          self.options.offset = {
                              x: nwPixel.x,
                              y: nwPixel.y
                          };
                          if (self.options.draw == 'grid') {
                              drawGrid.draw(context, dataSet, self.options);
                          } else {
                              drawHoneycomb.draw(context, dataSet, self.options);
                          }
                          break;
                      case 'text':
                          drawText.draw(context, dataSet, self.options);
                          break;
                      case 'icon':
                          drawIcon.draw(context, dataSet, self.options);
                          break;
                      case 'clip':
                          context.save();
                          context.fillStyle = self.options.fillStyle || 'rgba(0, 0, 0, 0.5)';
                          context.fillRect(0, 0, context.canvas.width, context.canvas.height);
                          drawSimple.draw(context, dataSet, self.options);
                          context.beginPath();
                          pathSimple.drawDataSet(context, dataSet, self.options);
                          context.clip();
                          clear(context);
                          context.restore();
                          break;
                      default:
                          if (self.options.context == "webgl") {
                              webglDrawSimple.draw(self.canvasLayer.canvas.getContext('webgl'), dataSet, self.options);
                          } else {
                              drawSimple.draw(context, dataSet, self.options);
                          }
                  }
              }
          }, {
              key: "isPointInPath",
              value: function isPointInPath(context, pixel) {
                  var context = this.canvasLayer.canvas.getContext(this.context);
                  var data = this.dataSet.get();
                  for (var i = 0; i < data.length; i++) {
                      context.beginPath();
                      pathSimple.draw(context, data[i], this.options);
                      if (context.isPointInPath(pixel.x * this.canvasLayer.devicePixelRatio, pixel.y * this.canvasLayer.devicePixelRatio)) {
                          return data[i];
                      }
                  }
              }
          }, {
              key: "clickEvent",
              value: function clickEvent(pixel, e) {
  
                  var dataItem = this.isPointInPath(this.getContext(), pixel);
  
                  if (dataItem) {
                      this.options.methods.click(dataItem, e);
                  } else {
                      this.options.methods.click(null, e);
                  }
              }
          }, {
              key: "mousemoveEvent",
              value: function mousemoveEvent(pixel, e) {
                  var dataItem = this.isPointInPath(this.getContext(), pixel);
                  if (dataItem) {
                      this.options.methods.mousemove(dataItem, e);
                  } else {
                      this.options.methods.mousemove(null, e);
                  }
              }
  
              /**
               * obj.options
               */
  
          }, {
              key: "update",
              value: function update(obj, isDraw) {
                  var self = this;
                  var _options = obj.options;
                  var options = self.options;
                  for (var i in _options) {
                      options[i] = _options[i];
                  }
                  self.init(options);
                  if (isDraw !== false) {
                      self.draw();
                  }
              }
          }, {
              key: "setOptions",
              value: function setOptions(options) {
                  var self = this;
                  self.init(options);
                  self.draw();
              }
          }, {
              key: "set",
              value: function set$$1(obj) {
                  var self = this;
                  var ctx = this.getContext();
                  var conf = this.getDefaultContextConfig();
                  for (var i in conf) {
                      ctx[i] = conf[i];
                  }
                  self.init(obj.options);
                  self.draw();
              }
          }, {
              key: "destroy",
              value: function destroy() {
                  this.unbindEvent();
                  this.hide();
              }
          }, {
              key: "initAnimator",
              value: function initAnimator() {
                  var self = this;
                  var animationOptions = self.options.animation;
  
                  if (self.options.draw == 'time' || self.isEnabledTime()) {
  
                      if (!animationOptions.stepsRange) {
                          animationOptions.stepsRange = {
                              start: this.dataSet.getMin('time') || 0,
                              end: this.dataSet.getMax('time') || 0
                          };
                      }
  
                      this.steps = {
                          step: animationOptions.stepsRange.start
                      };
                      self.animator = new TWEEN.Tween(this.steps).onUpdate(function() {
                          self._canvasUpdate(this.step);
                      }).repeat(Infinity);
  
                      this.addAnimatorEvent();
  
                      var duration = animationOptions.duration * 1000 || 5000;
  
                      self.animator.to({
                          step: animationOptions.stepsRange.end
                      }, duration);
                      self.animator.start();
                  } else {
                      self.animator && self.animator.stop();
                  }
              }
          }, {
              key: "addAnimatorEvent",
              value: function addAnimatorEvent() {}
          }, {
              key: "animatorMovestartEvent",
              value: function animatorMovestartEvent() {
                  var animationOptions = this.options.animation;
                  if (this.isEnabledTime() && this.animator) {
                      this.steps.step = animationOptions.stepsRange.start;
                      this.animator.stop();
                  }
              }
          }, {
              key: "animatorMoveendEvent",
              value: function animatorMoveendEvent() {
                  if (this.isEnabledTime() && this.animator) {
                      this.animator.start();
                  }
              }
          }]);
          return BaseLayer;
      }();
  
      var AnimationLayer = function(_BaseLayer) {
          inherits(AnimationLayer, _BaseLayer);
  
          function AnimationLayer(map, dataSet, options) {
              classCallCheck(this, AnimationLayer);
  
              var _this = possibleConstructorReturn(this, (AnimationLayer.__proto__ || Object.getPrototypeOf(AnimationLayer)).call(this, map, dataSet, options));
  
              _this.map = map;
              _this.options = options || {};
              _this.dataSet = dataSet;
  
              _this.init(options);
  
              var canvasLayer = new CanvasLayer({
                  map: map,
                  update: _this._canvasUpdate.bind(_this)
              });
              _this.transferToMercator();
              var self = _this;
              dataSet.on('change', function() {
                  self.transferToMercator();
                  canvasLayer.draw();
              });
              _this.ctx = canvasLayer.canvas.getContext('2d');
  
              _this.start();
              return _this;
          }
  
          createClass(AnimationLayer, [{
              key: "init",
              value: function init(options) {
  
                  var self = this;
                  self.options = options;
                  this.initDataRange(options);
                  this.context = self.options.context || '2d';
  
                  if (self.options.zIndex) {
                      this.canvasLayer && this.canvasLayer.setZIndex(self.options.zIndex);
                  }
  
                  if (self.options.max) {
                      this.intensity.setMax(self.options.max);
                  }
  
                  if (self.options.min) {
                      this.intensity.setMin(self.options.min);
                  }
  
                  this.initAnimator();
              }
  
              // 经纬度左边转换为墨卡托坐标
  
          }, {
              key: "transferToMercator",
              value: function transferToMercator() {
                  var projection = this.map.getMapType().getProjection();
  
                  if (this.options.coordType !== 'bd09mc') {
                      var data = this.dataSet.get();
                      data = this.dataSet.transferCoordinate(data, function(coordinates) {
                          var pixel = projection.lngLatToPoint({
                              lng: coordinates[0],
                              lat: coordinates[1]
                          });
                          return [pixel.x, pixel.y];
                      }, 'coordinates', 'coordinates_mercator');
                      this.dataSet._set(data);
                  }
              }
          }, {
              key: "_canvasUpdate",
              value: function _canvasUpdate() {
                  var ctx = this.ctx;
                  if (!ctx) {
                      return;
                  }
                  //clear(ctx);
                  var map = this.map;
                  var zoomUnit = Math.pow(2, 18 - map.getZoom());
                  var projection = map.getMapType().getProjection();
  
                  var mcCenter = projection.lngLatToPoint(map.getCenter());
                  var nwMc = new BMap.Pixel(mcCenter.x - map.getSize().width / 2 * zoomUnit, mcCenter.y + map.getSize().height / 2 * zoomUnit); //左上角墨卡托坐标
  
                  clear(ctx);
  
                  var dataGetOptions = {
                      fromColumn: this.options.coordType == 'bd09mc' ? 'coordinates' : 'coordinates_mercator',
                      transferCoordinate: function transferCoordinate(coordinate) {
                          if (!coordinate) {
                              return;
                          }
                          var x = (coordinate[0] - nwMc.x) / zoomUnit;
                          var y = (nwMc.y - coordinate[1]) / zoomUnit;
                          return [x, y];
                      }
                  };
  
                  this.data = this.dataSet.get(dataGetOptions);
  
                  this.processData(this.data);
  
                  this.drawAnimation();
              }
          }, {
              key: "drawAnimation",
              value: function drawAnimation() {
                  var ctx = this.ctx;
                  var data = this.data;
                  if (!data) {
                      return;
                  }
  
                  ctx.save();
                  ctx.globalCompositeOperation = 'destination-out';
                  ctx.fillStyle = 'rgba(0, 0, 0, .1)';
                  ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
                  ctx.restore();
  
                  ctx.save();
                  if (this.options.shadowColor) {
                      ctx.shadowColor = this.options.shadowColor;
                  }
  
                  if (this.options.shadowBlur) {
                      ctx.shadowBlur = this.options.shadowBlur;
                  }
  
                  if (this.options.globalAlpha) {
                      ctx.globalAlpha = this.options.globalAlpha;
                  }
  
                  if (this.options.globalCompositeOperation) {
                      ctx.globalCompositeOperation = this.options.globalCompositeOperation;
                  }
  
                  var options = this.options;
                  for (var i = 0; i < data.length; i++) {
                      if (data[i].geometry.type === 'Point') {
                          ctx.beginPath();
                          var maxSize = data[i].size || this.options.size;
                          var minSize = data[i].minSize || this.options.minSize || 0;
                          if (data[i]._size === undefined) {
                              data[i]._size = minSize;
                          }
                          ctx.arc(data[i].geometry._coordinates[0], data[i].geometry._coordinates[1], data[i]._size, 0, Math.PI * 2, true);
                          ctx.closePath();
  
                          data[i]._size++;
  
                          if (data[i]._size > maxSize) {
                              data[i]._size = minSize;
                          }
                          ctx.lineWidth = 1;
                          ctx.strokeStyle = data[i].strokeStyle || options.strokeStyle || 'yellow';
                          ctx.stroke();
                          var fillStyle = data[i].fillStyle || options.fillStyle;
                          if (fillStyle) {
                              ctx.fillStyle = fillStyle;
                              ctx.fill();
                          }
                      } else if (data[i].geometry.type === 'LineString') {
                          ctx.beginPath();
                          var size = data[i].size || this.options.size || 5;
                          var minSize = data[i].minSize || this.options.minSize || 0;
                          if (data[i]._index === undefined) {
                              data[i]._index = 0;
                          }
                          var index = data[i]._index;
                          ctx.arc(data[i].geometry._coordinates[index][0], data[i].geometry._coordinates[index][1], size, 0, Math.PI * 2, true);
                          ctx.closePath();
  
                          data[i]._index++;
  
                          if (data[i]._index >= data[i].geometry._coordinates.length) {
                              data[i]._index = 0;
                          }
  
                          ctx.lineWidth = options.lineWidth || 1;
                          var strokeStyle = data[i].strokeStyle || options.strokeStyle;
                          var fillStyle = data[i].fillStyle || options.fillStyle || 'yellow';
                          ctx.fillStyle = fillStyle;
                          ctx.fill();
                          if (strokeStyle) {
                              ctx.strokeStyle = strokeStyle;
                              ctx.stroke();
                          }
                      }
                  }
                  ctx.restore();
              }
          }, {
              key: "animate",
              value: function animate() {
                  this.drawAnimation();
                  var animateTime = this.options.animateTime || 100;
                  this.timeout = setTimeout(this.animate.bind(this), animateTime);
              }
          }, {
              key: "start",
              value: function start() {
                  this.stop();
                  this.animate();
              }
          }, {
              key: "stop",
              value: function stop() {
                  clearTimeout(this.timeout);
              }
          }]);
          return AnimationLayer;
      }(BaseLayer);
  
      /**
       * @author kyle / http://nikai.us/
       */
  
      var Layer = function(_BaseLayer) {
          inherits(Layer, _BaseLayer);
  
          function Layer(map, dataSet, options) {
              classCallCheck(this, Layer);
  
              var _this = possibleConstructorReturn(this, (Layer.__proto__ || Object.getPrototypeOf(Layer)).call(this, map, dataSet, options));
  
              var self = _this;
              var data = null;
              options = options || {};
  
              self.init(options);
              self.argCheck(options);
              self.transferToMercator();
  
              var canvasLayer = _this.canvasLayer = new CanvasLayer({
                  map: map,
                  context: _this.context,
                  paneName: options.paneName,
                  mixBlendMode: options.mixBlendMode,
                  enableMassClear: options.enableMassClear,
                  zIndex: options.zIndex,
                  update: function update() {
                      self._canvasUpdate();
                  }
              });
  
              dataSet.on('change', function() {
                  self.transferToMercator();
                  canvasLayer.draw();
              });
  
              _this.clickEvent = _this.clickEvent.bind(_this);
              _this.mousemoveEvent = _this.mousemoveEvent.bind(_this);
              _this.bindEvent();
  
              return _this;
          }
  
          createClass(Layer, [{
              key: "clickEvent",
              value: function clickEvent(e) {
                  var pixel = e.pixel;
                  get(Layer.prototype.__proto__ || Object.getPrototypeOf(Layer.prototype), "clickEvent", this).call(this, pixel, e);
              }
          }, {
              key: "mousemoveEvent",
              value: function mousemoveEvent(e) {
                  var pixel = e.pixel;
                  get(Layer.prototype.__proto__ || Object.getPrototypeOf(Layer.prototype), "mousemoveEvent", this).call(this, pixel, e);
              }
          }, {
              key: "bindEvent",
              value: function bindEvent(e) {
                  var map = this.map;
  
                  if (this.options.methods) {
                      if (this.options.methods.click) {
                          map.setDefaultCursor("default");
                          map.addEventListener('click', this.clickEvent);
                      }
                      if (this.options.methods.mousemove) {
                          map.addEventListener('mousemove', this.mousemoveEvent);
                      }
                  }
              }
          }, {
              key: "unbindEvent",
              value: function unbindEvent(e) {
                  var map = this.map;
  
                  if (this.options.methods) {
                      if (this.options.methods.click) {
                          map.removeEventListener('click', this.clickEvent);
                      }
                      if (this.options.methods.mousemove) {
                          map.removeEventListener('mousemove', this.mousemoveEvent);
                      }
                  }
              }
  
              // 经纬度左边转换为墨卡托坐标
  
          }, {
              key: "transferToMercator",
              value: function transferToMercator() {
                  var projection = this.map.getMapType().getProjection();
  
                  if (this.options.coordType !== 'bd09mc') {
                      var data = this.dataSet.get();
                      data = this.dataSet.transferCoordinate(data, function(coordinates) {
                          var pixel = projection.lngLatToPoint({
                              lng: coordinates[0],
                              lat: coordinates[1]
                          });
                          return [pixel.x, pixel.y];
                      }, 'coordinates', 'coordinates_mercator');
                      this.dataSet._set(data);
                  }
              }
          }, {
              key: "getContext",
              value: function getContext() {
                  return this.canvasLayer.canvas.getContext(this.context);
              }
          }, {
              key: "_canvasUpdate",
              value: function _canvasUpdate(time) {
                  if (!this.canvasLayer) {
                      return;
                  }
  
                  var self = this;
  
                  var animationOptions = self.options.animation;
  
                  var map = this.canvasLayer._map;
  
                  var zoomUnit = Math.pow(2, 18 - map.getZoom());
                  var projection = map.getMapType().getProjection();
  
                  var mcCenter = projection.lngLatToPoint(map.getCenter());
                  var nwMc = new BMap.Pixel(mcCenter.x - map.getSize().width / 2 * zoomUnit, mcCenter.y + map.getSize().height / 2 * zoomUnit); //左上角墨卡托坐标
  
                  var context = this.getContext();
  
                  if (self.isEnabledTime()) {
                      if (time === undefined) {
                          clear(context);
                          return;
                      }
                      if (this.context == '2d') {
                          context.save();
                          context.globalCompositeOperation = 'destination-out';
                          context.fillStyle = 'rgba(0, 0, 0, .1)';
                          context.fillRect(0, 0, context.canvas.width, context.canvas.height);
                          context.restore();
                      }
                  } else {
                      clear(context);
                  }
  
                  if (this.context == '2d') {
                      for (var key in self.options) {
                          context[key] = self.options[key];
                      }
                  } else {
                      context.clear(context.COLOR_BUFFER_BIT);
                  }
  
                  if (self.options.minZoom && map.getZoom() < self.options.minZoom || self.options.maxZoom && map.getZoom() > self.options.maxZoom) {
                      return;
                  }
  
                  var scale = 1;
                  if (this.context != '2d') {
                      scale = this.canvasLayer.devicePixelRatio;
                  }
  
                  var dataGetOptions = {
                      fromColumn: self.options.coordType == 'bd09mc' ? 'coordinates' : 'coordinates_mercator',
                      transferCoordinate: function transferCoordinate(coordinate) {
                          var x = (coordinate[0] - nwMc.x) / zoomUnit * scale;
                          var y = (nwMc.y - coordinate[1]) / zoomUnit * scale;
                          return [x, y];
                      }
                  };
  
                  if (time !== undefined) {
                      dataGetOptions.filter = function(item) {
                          var trails = animationOptions.trails || 10;
                          if (time && item.time > time - trails && item.time < time) {
                              return true;
                          } else {
                              return false;
                          }
                      };
                  }
  
                  // get data from data set
                  var data = self.dataSet.get(dataGetOptions);
  
                  this.processData(data);
  
                  var nwPixel = map.pointToPixel(new BMap.Point(0, 0));
  
                  if (self.options.unit == 'm') {
                      if (self.options.size) {
                          self.options._size = self.options.size / zoomUnit;
                      }
                      if (self.options.width) {
                          self.options._width = self.options.width / zoomUnit;
                      }
                      if (self.options.height) {
                          self.options._height = self.options.height / zoomUnit;
                      }
                  } else {
                      self.options._size = self.options.size;
                      self.options._height = self.options.height;
                      self.options._width = self.options.width;
                  }
  
                  this.drawContext(context, data, self.options, nwPixel);
  
                  //console.timeEnd('draw');
  
                  //console.timeEnd('update')
                  self.options.updateCallback && self.options.updateCallback(time);
              }
          }, {
              key: "init",
              value: function init(options) {
  
                  var self = this;
                  self.options = options;
                  this.initDataRange(options);
                  this.context = self.options.context || '2d';
  
                  if (self.options.zIndex) {
                      this.canvasLayer && this.canvasLayer.setZIndex(self.options.zIndex);
                  }
  
                  if (self.options.max) {
                      this.intensity.setMax(self.options.max);
                  }
  
                  if (self.options.min) {
                      this.intensity.setMin(self.options.min);
                  }
  
                  this.initAnimator();
              }
          }, {
              key: "addAnimatorEvent",
              value: function addAnimatorEvent() {
                  this.map.addEventListener('movestart', this.animatorMovestartEvent.bind(this));
                  this.map.addEventListener('moveend', this.animatorMoveendEvent.bind(this));
              }
          }, {
              key: "show",
              value: function show() {
                  this.map.addOverlay(this.canvasLayer);
              }
          }, {
              key: "hide",
              value: function hide() {
                  this.map.removeOverlay(this.canvasLayer);
              }
          }, {
              key: "draw",
              value: function draw() {
                  this.canvasLayer.draw();
              }
          }]);
          return Layer;
      }(BaseLayer);
  
      /**
       * Copyright 2012 Google Inc. All Rights Reserved.
       *
       * Licensed under the Apache License, Version 2.0 (the "License");
       * you may not use this file except in compliance with the License.
       * You may obtain a copy of the License at
       *
       *     http://www.apache.org/licenses/LICENSE-2.0
       *
       * Unless required by applicable law or agreed to in writing, software
       * distributed under the License is distributed on an "AS IS" BASIS,
       * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       * See the License for the specific language governing permissions and
       * limitations under the License.
       */
  
      /**
       * @fileoverview Extends OverlayView to provide a canvas "Layer".
       * @author Brendan Kenny
       */
  
      /**
       * A map layer that provides a canvas over the slippy map and a callback
       * system for efficient animation. Requires canvas and CSS 2D transform
       * support.
       * @constructor
       * @extends google.maps.OverlayView
       * @param {CanvasLayerOptions=} opt_options Options to set in this CanvasLayer.
       */
      function CanvasLayer$2(opt_options) {
          /**
           * If true, canvas is in a map pane and the OverlayView is fully functional.
           * See google.maps.OverlayView.onAdd for more information.
           * @type {boolean}
           * @private
           */
          this.isAdded_ = false;
  
          /**
           * If true, each update will immediately schedule the next.
           * @type {boolean}
           * @private
           */
          this.isAnimated_ = false;
  
          /**
           * The name of the MapPane in which this layer will be displayed.
           * @type {string}
           * @private
           */
          this.paneName_ = CanvasLayer$2.DEFAULT_PANE_NAME_;
  
          /**
           * A user-supplied function called whenever an update is required. Null or
           * undefined if a callback is not provided.
           * @type {?function=}
           * @private
           */
          this.updateHandler_ = null;
  
          /**
           * A user-supplied function called whenever an update is required and the
           * map has been resized since the last update. Null or undefined if a
           * callback is not provided.
           * @type {?function}
           * @private
           */
          this.resizeHandler_ = null;
  
          /**
           * The LatLng coordinate of the top left of the current view of the map. Will
           * be null when this.isAdded_ is false.
           * @type {google.maps.LatLng}
           * @private
           */
          this.topLeft_ = null;
  
          /**
           * The map-pan event listener. Will be null when this.isAdded_ is false. Will
           * be null when this.isAdded_ is false.
           * @type {?function}
           * @private
           */
          this.centerListener_ = null;
  
          /**
           * The map-resize event listener. Will be null when this.isAdded_ is false.
           * @type {?function}
           * @private
           */
          this.resizeListener_ = null;
  
          /**
           * If true, the map size has changed and this.resizeHandler_ must be called
           * on the next update.
           * @type {boolean}
           * @private
           */
          this.needsResize_ = true;
  
          /**
           * A browser-defined id for the currently requested callback. Null when no
           * callback is queued.
           * @type {?number}
           * @private
           */
          this.requestAnimationFrameId_ = null;
  
          var canvas = document.createElement('canvas');
          canvas.style.position = 'absolute';
          canvas.style.top = 0;
          canvas.style.left = 0;
          canvas.style.pointerEvents = 'none';
  
          /**
           * The canvas element.
           * @type {!HTMLCanvasElement}
           */
          this.canvas = canvas;
  
          /**
           * The CSS width of the canvas, which may be different than the width of the
           * backing store.
           * @private {number}
           */
          this.canvasCssWidth_ = 300;
  
          /**
           * The CSS height of the canvas, which may be different than the height of
           * the backing store.
           * @private {number}
           */
          this.canvasCssHeight_ = 150;
  
          /**
           * A value for scaling the CanvasLayer resolution relative to the CanvasLayer
           * display size.
           * @private {number}
           */
          this.resolutionScale_ = 1;
  
          /**
           * Simple bind for functions with no args for bind-less browsers (Safari).
           * @param {Object} thisArg The this value used for the target function.
           * @param {function} func The function to be bound.
           */
          function simpleBindShim(thisArg, func) {
              return function() {
                  func.apply(thisArg);
              };
          }
  
          /**
           * A reference to this.repositionCanvas_ with this bound as its this value.
           * @type {function}
           * @private
           */
          this.repositionFunction_ = simpleBindShim(this, this.repositionCanvas_);
  
          /**
           * A reference to this.resize_ with this bound as its this value.
           * @type {function}
           * @private
           */
          this.resizeFunction_ = simpleBindShim(this, this.resize_);
  
          /**
           * A reference to this.update_ with this bound as its this value.
           * @type {function}
           * @private
           */
          this.requestUpdateFunction_ = simpleBindShim(this, this.update_);
  
          // set provided options, if any
          if (opt_options) {
              this.setOptions(opt_options);
          }
      }
  
      var global$4 = typeof window === 'undefined' ? {} : window;
  
      if (global$4.google && global$4.google.maps) {
  
          CanvasLayer$2.prototype = new google.maps.OverlayView();
  
          /**
           * The default MapPane to contain the canvas.
           * @type {string}
           * @const
           * @private
           */
          CanvasLayer$2.DEFAULT_PANE_NAME_ = 'overlayLayer';
  
          /**
           * Transform CSS property name, with vendor prefix if required. If browser
           * does not support transforms, property will be ignored.
           * @type {string}
           * @const
           * @private
           */
          CanvasLayer$2.CSS_TRANSFORM_ = function() {
              var div = document.createElement('div');
              var transformProps = ['transform', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform'];
              for (var i = 0; i < transformProps.length; i++) {
                  var prop = transformProps[i];
                  if (div.style[prop] !== undefined) {
                      return prop;
                  }
              }
  
              // return unprefixed version by default
              return transformProps[0];
          }();
  
          /**
           * The requestAnimationFrame function, with vendor-prefixed or setTimeout-based
           * fallbacks. MUST be called with window as thisArg.
           * @type {function}
           * @param {function} callback The function to add to the frame request queue.
           * @return {number} The browser-defined id for the requested callback.
           * @private
           */
          CanvasLayer$2.prototype.requestAnimFrame_ = global$4.requestAnimationFrame || global$4.webkitRequestAnimationFrame || global$4.mozRequestAnimationFrame || global$4.oRequestAnimationFrame || global$4.msRequestAnimationFrame || function(callback) {
              return global$4.setTimeout(callback, 1000 / 60);
          };
  
          /**
           * The cancelAnimationFrame function, with vendor-prefixed fallback. Does not
           * fall back to clearTimeout as some platforms implement requestAnimationFrame
           * but not cancelAnimationFrame, and the cost is an extra frame on onRemove.
           * MUST be called with window as thisArg.
           * @type {function}
           * @param {number=} requestId The id of the frame request to cancel.
           * @private
           */
          CanvasLayer$2.prototype.cancelAnimFrame_ = global$4.cancelAnimationFrame || global$4.webkitCancelAnimationFrame || global$4.mozCancelAnimationFrame || global$4.oCancelAnimationFrame || global$4.msCancelAnimationFrame || function(requestId) {};
  
          /**
           * Sets any options provided. See CanvasLayerOptions for more information.
           * @param {CanvasLayerOptions} options The options to set.
           */
          CanvasLayer$2.prototype.setOptions = function(options) {
              if (options.animate !== undefined) {
                  this.setAnimate(options.animate);
              }
  
              if (options.paneName !== undefined) {
                  this.setPaneName(options.paneName);
              }
  
              if (options.updateHandler !== undefined) {
                  this.setUpdateHandler(options.updateHandler);
              }
  
              if (options.resizeHandler !== undefined) {
                  this.setResizeHandler(options.resizeHandler);
              }
  
              if (options.resolutionScale !== undefined) {
                  this.setResolutionScale(options.resolutionScale);
              }
  
              if (options.map !== undefined) {
                  this.setMap(options.map);
              }
          };
  
          /**
           * Set the animated state of the layer. If true, updateHandler will be called
           * repeatedly, once per frame. If false, updateHandler will only be called when
           * a map property changes that could require the canvas content to be redrawn.
           * @param {boolean} animate Whether the canvas is animated.
           */
          CanvasLayer$2.prototype.setAnimate = function(animate) {
              this.isAnimated_ = !!animate;
  
              if (this.isAnimated_) {
                  this.scheduleUpdate();
              }
          };
  
          /**
           * @return {boolean} Whether the canvas is animated.
           */
          CanvasLayer$2.prototype.isAnimated = function() {
              return this.isAnimated_;
          };
  
          /**
           * Set the MapPane in which this layer will be displayed, by name. See
           * {@code google.maps.MapPanes} for the panes available.
           * @param {string} paneName The name of the desired MapPane.
           */
          CanvasLayer$2.prototype.setPaneName = function(paneName) {
              this.paneName_ = paneName;
  
              this.setPane_();
          };
  
          /**
           * @return {string} The name of the current container pane.
           */
          CanvasLayer$2.prototype.getPaneName = function() {
              return this.paneName_;
          };
  
          /**
           * Adds the canvas to the specified container pane. Since this is guaranteed to
           * execute only after onAdd is called, this is when paneName's existence is
           * checked (and an error is thrown if it doesn't exist).
           * @private
           */
          CanvasLayer$2.prototype.setPane_ = function() {
              if (!this.isAdded_) {
                  return;
              }
  
              // onAdd has been called, so panes can be used
              var panes = this.getPanes();
              if (!panes[this.paneName_]) {
                  throw new Error('"' + this.paneName_ + '" is not a valid MapPane name.');
              }
  
              panes[this.paneName_].appendChild(this.canvas);
          };
  
          /**
           * Set a function that will be called whenever the parent map and the overlay's
           * canvas have been resized. If opt_resizeHandler is null or unspecified, any
           * existing callback is removed.
           * @param {?function=} opt_resizeHandler The resize callback function.
           */
          CanvasLayer$2.prototype.setResizeHandler = function(opt_resizeHandler) {
              this.resizeHandler_ = opt_resizeHandler;
          };
  
          /**
           * Sets a value for scaling the canvas resolution relative to the canvas
           * display size. This can be used to save computation by scaling the backing
           * buffer down, or to support high DPI devices by scaling it up (by e.g.
           * window.devicePixelRatio).
           * @param {number} scale
           */
          CanvasLayer$2.prototype.setResolutionScale = function(scale) {
              if (typeof scale === 'number') {
                  this.resolutionScale_ = scale;
                  this.resize_();
              }
          };
  
          /**
           * Set a function that will be called when a repaint of the canvas is required.
           * If opt_updateHandler is null or unspecified, any existing callback is
           * removed.
           * @param {?function=} opt_updateHandler The update callback function.
           */
          CanvasLayer$2.prototype.setUpdateHandler = function(opt_updateHandler) {
              this.updateHandler_ = opt_updateHandler;
          };
  
          /**
           * @inheritDoc
           */
          CanvasLayer$2.prototype.onAdd = function() {
              if (this.isAdded_) {
                  return;
              }
  
              this.isAdded_ = true;
              this.setPane_();
  
              this.resizeListener_ = google.maps.event.addListener(this.getMap(), 'resize', this.resizeFunction_);
              this.centerListener_ = google.maps.event.addListener(this.getMap(), 'center_changed', this.repositionFunction_);
  
              this.resize_();
              this.repositionCanvas_();
          };
  
          /**
           * @inheritDoc
           */
          CanvasLayer$2.prototype.onRemove = function() {
              if (!this.isAdded_) {
                  return;
              }
  
              this.isAdded_ = false;
              this.topLeft_ = null;
  
              // remove canvas and listeners for pan and resize from map
              this.canvas.parentElement.removeChild(this.canvas);
              if (this.centerListener_) {
                  google.maps.event.removeListener(this.centerListener_);
                  this.centerListener_ = null;
              }
              if (this.resizeListener_) {
                  google.maps.event.removeListener(this.resizeListener_);
                  this.resizeListener_ = null;
              }
  
              // cease canvas update callbacks
              if (this.requestAnimationFrameId_) {
                  this.cancelAnimFrame_.call(global$4, this.requestAnimationFrameId_);
                  this.requestAnimationFrameId_ = null;
              }
          };
  
          /**
           * The internal callback for resize events that resizes the canvas to keep the
           * map properly covered.
           * @private
           */
          CanvasLayer$2.prototype.resize_ = function() {
              if (!this.isAdded_) {
                  return;
              }
  
              var map = this.getMap();
              var mapWidth = map.getDiv().offsetWidth;
              var mapHeight = map.getDiv().offsetHeight;
  
              var newWidth = mapWidth * this.resolutionScale_;
              var newHeight = mapHeight * this.resolutionScale_;
              var oldWidth = this.canvas.width;
              var oldHeight = this.canvas.height;
  
              // resizing may allocate a new back buffer, so do so conservatively
              if (oldWidth !== newWidth || oldHeight !== newHeight) {
                  this.canvas.width = newWidth;
                  this.canvas.height = newHeight;
  
                  this.needsResize_ = true;
                  this.scheduleUpdate();
              }
  
              // reset styling if new sizes don't match; resize of data not needed
              if (this.canvasCssWidth_ !== mapWidth || this.canvasCssHeight_ !== mapHeight) {
                  this.canvasCssWidth_ = mapWidth;
                  this.canvasCssHeight_ = mapHeight;
                  this.canvas.style.width = mapWidth + 'px';
                  this.canvas.style.height = mapHeight + 'px';
              }
          };
  
          /**
           * @inheritDoc
           */
          CanvasLayer$2.prototype.draw = function() {
              this.repositionCanvas_();
          };
  
          /**
           * Internal callback for map view changes. Since the Maps API moves the overlay
           * along with the map, this function calculates the opposite translation to
           * keep the canvas in place.
           * @private
           */
          CanvasLayer$2.prototype.repositionCanvas_ = function() {
              // TODO(bckenny): *should* only be executed on RAF, but in current browsers
              //     this causes noticeable hitches in map and overlay relative
              //     positioning.
  
              var map = this.getMap();
  
              // topLeft can't be calculated from map.getBounds(), because bounds are
              // clamped to -180 and 180 when completely zoomed out. Instead, calculate
              // left as an offset from the center, which is an unwrapped LatLng.
              var top = map.getBounds().getNorthEast().lat();
              var center = map.getCenter();
              var scale = Math.pow(2, map.getZoom());
              var left = center.lng() - this.canvasCssWidth_ * 180 / (256 * scale);
              this.topLeft_ = new google.maps.LatLng(top, left);
  
              // Canvas position relative to draggable map's container depends on
              // overlayView's projection, not the map's. Have to use the center of the
              // map for this, not the top left, for the same reason as above.
              var projection = this.getProjection();
              var divCenter = projection.fromLatLngToDivPixel(center);
              var offsetX = -Math.round(this.canvasCssWidth_ / 2 - divCenter.x);
              var offsetY = -Math.round(this.canvasCssHeight_ / 2 - divCenter.y);
              this.canvas.style[CanvasLayer$2.CSS_TRANSFORM_] = 'translate(' + offsetX + 'px,' + offsetY + 'px)';
  
              this.scheduleUpdate();
          };
  
          /**
           * Internal callback that serves as main animation scheduler via
           * requestAnimationFrame. Calls resize and update callbacks if set, and
           * schedules the next frame if overlay is animated.
           * @private
           */
          CanvasLayer$2.prototype.update_ = function() {
              this.requestAnimationFrameId_ = null;
  
              if (!this.isAdded_) {
                  return;
              }
  
              if (this.isAnimated_) {
                  this.scheduleUpdate();
              }
  
              if (this.needsResize_ && this.resizeHandler_) {
                  this.needsResize_ = false;
                  this.resizeHandler_();
              }
  
              if (this.updateHandler_) {
                  this.updateHandler_();
              }
          };
  
          /**
           * A convenience method to get the current LatLng coordinate of the top left of
           * the current view of the map.
           * @return {google.maps.LatLng} The top left coordinate.
           */
          CanvasLayer$2.prototype.getTopLeft = function() {
              return this.topLeft_;
          };
  
          /**
           * Schedule a requestAnimationFrame callback to updateHandler. If one is
           * already scheduled, there is no effect.
           */
          CanvasLayer$2.prototype.scheduleUpdate = function() {
              if (this.isAdded_ && !this.requestAnimationFrameId_) {
                  this.requestAnimationFrameId_ = this.requestAnimFrame_.call(global$4, this.requestUpdateFunction_);
              }
          };
      }
  
      /**
       * @author kyle / http://nikai.us/
       */
  
      var Layer$2 = function(_BaseLayer) {
          inherits(Layer, _BaseLayer);
  
          function Layer(map, dataSet, options) {
              classCallCheck(this, Layer);
  
              var _this = possibleConstructorReturn(this, (Layer.__proto__ || Object.getPrototypeOf(Layer)).call(this, map, dataSet, options));
  
              var self = _this;
              var data = null;
              options = options || {};
  
              self.init(options);
              self.argCheck(options);
  
              var canvasLayerOptions = {
                  map: map,
                  animate: false,
                  updateHandler: function updateHandler() {
                      self._canvasUpdate();
                  },
                  resolutionScale: resolutionScale
              };
  
              var canvasLayer = _this.canvasLayer = new CanvasLayer$2(canvasLayerOptions);
  
              _this.clickEvent = _this.clickEvent.bind(_this);
              _this.mousemoveEvent = _this.mousemoveEvent.bind(_this);
              _this.bindEvent();
              return _this;
          }
  
          createClass(Layer, [{
              key: "clickEvent",
              value: function clickEvent(e) {
                  var pixel = e.pixel;
                  get(Layer.prototype.__proto__ || Object.getPrototypeOf(Layer.prototype), "clickEvent", this).call(this, pixel, e);
              }
          }, {
              key: "mousemoveEvent",
              value: function mousemoveEvent(e) {
                  var pixel = e.pixel;
                  get(Layer.prototype.__proto__ || Object.getPrototypeOf(Layer.prototype), "mousemoveEvent", this).call(this, pixel, e);
              }
          }, {
              key: "bindEvent",
              value: function bindEvent(e) {
                  var map = this.map;
  
                  if (this.options.methods) {
                      if (this.options.methods.click) {
                          map.setDefaultCursor("default");
                          map.addListener('click', this.clickEvent);
                      }
                      if (this.options.methods.mousemove) {
                          map.addListener('mousemove', this.mousemoveEvent);
                      }
                  }
              }
          }, {
              key: "unbindEvent",
              value: function unbindEvent(e) {
                  var map = this.map;
  
                  if (this.options.methods) {
                      if (this.options.methods.click) {
                          map.removeListener('click', this.clickEvent);
                      }
                      if (this.options.methods.mousemove) {
                          map.removeListener('mousemove', this.mousemoveEvent);
                      }
                  }
              }
          }, {
              key: "getContext",
              value: function getContext() {
                  return this.canvasLayer.canvas.getContext(this.context);
              }
          }, {
              key: "_canvasUpdate",
              value: function _canvasUpdate(time) {
                  if (!this.canvasLayer) {
                      return;
                  }
  
                  var self = this;
  
                  var animationOptions = self.options.animation;
  
                  var context = this.getContext();
  
                  if (self.isEnabledTime()) {
                      if (time === undefined) {
                          clear(context);
                          return;
                      }
                      if (this.context == '2d') {
                          context.save();
                          context.globalCompositeOperation = 'destination-out';
                          context.fillStyle = 'rgba(0, 0, 0, .1)';
                          context.fillRect(0, 0, context.canvas.width, context.canvas.height);
                          context.restore();
                      }
                  } else {
                      clear(context);
                  }
  
                  if (this.context == '2d') {
                      for (var key in self.options) {
                          context[key] = self.options[key];
                      }
                  } else {
                      context.clear(context.COLOR_BUFFER_BIT);
                  }
  
                  if (self.options.minZoom && map.getZoom() < self.options.minZoom || self.options.maxZoom && map.getZoom() > self.options.maxZoom) {
                      return;
                  }
  
                  var scale = 1;
                  if (this.context != '2d') {
                      scale = this.canvasLayer.devicePixelRatio;
                  }
  
                  var map = this.map;
                  var mapProjection = map.getProjection();
                  var scale = Math.pow(2, map.zoom) * resolutionScale;
                  var offset = mapProjection.fromLatLngToPoint(this.canvasLayer.getTopLeft());
                  var dataGetOptions = {
                      //fromColumn: self.options.coordType == 'bd09mc' ? 'coordinates' : 'coordinates_mercator',
                      transferCoordinate: function transferCoordinate(coordinate) {
                          var latLng = new google.maps.LatLng(coordinate[1], coordinate[0]);
                          var worldPoint = mapProjection.fromLatLngToPoint(latLng);
                          var pixel = {
                              x: (worldPoint.x - offset.x) * scale,
                              y: (worldPoint.y - offset.y) * scale
                          };
                          return [pixel.x, pixel.y];
                      }
                  };
  
                  if (time !== undefined) {
                      dataGetOptions.filter = function(item) {
                          var trails = animationOptions.trails || 10;
                          if (time && item.time > time - trails && item.time < time) {
                              return true;
                          } else {
                              return false;
                          }
                      };
                  }
  
                  // get data from data set
                  var data = self.dataSet.get(dataGetOptions);
  
                  this.processData(data);
  
                  var latLng = new google.maps.LatLng(0, 0);
                  var worldPoint = mapProjection.fromLatLngToPoint(latLng);
                  var pixel = {
                      x: (worldPoint.x - offset.x) * scale,
                      y: (worldPoint.y - offset.y) * scale
                  };
  
                  if (self.options.unit == 'm' && self.options.size) {
                      self.options._size = self.options.size / zoomUnit;
                  } else {
                      self.options._size = self.options.size;
                  }
  
                  this.drawContext(context, new DataSet(data), self.options, pixel);
  
                  //console.timeEnd('draw');
  
                  //console.timeEnd('update')
                  self.options.updateCallback && self.options.updateCallback(time);
              }
          }, {
              key: "init",
              value: function init(options) {
  
                  var self = this;
  
                  self.options = options;
  
                  this.initDataRange(options);
  
                  this.context = self.options.context || '2d';
  
                  if (self.options.zIndex) {
                      this.canvasLayer && this.canvasLayer.setZIndex(self.options.zIndex);
                  }
  
                  this.initAnimator();
              }
          }, {
              key: "addAnimatorEvent",
              value: function addAnimatorEvent() {
                  this.map.addListener('movestart', this.animatorMovestartEvent.bind(this));
                  this.map.addListener('moveend', this.animatorMoveendEvent.bind(this));
              }
          }, {
              key: "show",
              value: function show() {
                  this.map.addOverlay(this.canvasLayer);
              }
          }, {
              key: "hide",
              value: function hide() {
                  this.map.removeOverlay(this.canvasLayer);
              }
          }, {
              key: "draw",
              value: function draw() {
                  self.canvasLayer.draw();
              }
          }]);
          return Layer;
      }(BaseLayer);
  
      /**
       * @author kyle / http://nikai.us/
       */
  
      var geojson = {
          getDataSet: function getDataSet(geoJson) {
  
              var data = [];
              var features = geoJson.features;
              for (var i = 0; i < features.length; i++) {
                  var feature = features[i];
                  var geometry = feature.geometry;
                  var properties = feature.properties;
                  var item = {};
                  for (var key in properties) {
                      item[key] = properties[key];
                  }
                  item.geometry = geometry;
                  data.push(item);
              }
              return new DataSet(data);
          }
      };
  
      /**
       * @author kyle / http://nikai.us/
       */
  
      var csv = {
          CSVToArray: function CSVToArray(strData, strDelimiter) {
              // Check to see if the delimiter is defined. If not,
              // then default to comma.
              strDelimiter = strDelimiter || ",";
  
              // Create a regular expression to parse the CSV values.
              var objPattern = new RegExp(
                  // Delimiters.
                  "(\\" + strDelimiter + "|\\r?\\n|\\r|^)" +
  
                  // Quoted fields.
                  "(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +
  
                  // Standard fields.
                  "([^\"\\" + strDelimiter + "\\r\\n]*))", "gi");
  
              // Create an array to hold our data. Give the array
              // a default empty first row.
              var arrData = [
                  []
              ];
  
              // Create an array to hold our individual pattern
              // matching groups.
              var arrMatches = null;
  
              // Keep looping over the regular expression matches
              // until we can no longer find a match.
              while (arrMatches = objPattern.exec(strData)) {
  
                  // Get the delimiter that was found.
                  var strMatchedDelimiter = arrMatches[1];
  
                  // Check to see if the given delimiter has a length
                  // (is not the start of string) and if it matches
                  // field delimiter. If id does not, then we know
                  // that this delimiter is a row delimiter.
                  if (strMatchedDelimiter.length && strMatchedDelimiter !== strDelimiter) {
  
                      // Since we have reached a new row of data,
                      // add an empty row to our data array.
                      arrData.push([]);
                  }
  
                  var strMatchedValue;
  
                  // Now that we have our delimiter out of the way,
                  // let's check to see which kind of value we
                  // captured (quoted or unquoted).
                  if (arrMatches[2]) {
  
                      // We found a quoted value. When we capture
                      // this value, unescape any double quotes.
                      strMatchedValue = arrMatches[2].replace(new RegExp("\"\"", "g"), "\"");
                  } else {
  
                      // We found a non-quoted value.
                      strMatchedValue = arrMatches[3];
                  }
  
                  // Now that we have our value string, let's add
                  // it to the data array.
                  arrData[arrData.length - 1].push(strMatchedValue);
              }
  
              // Return the parsed data.
              return arrData;
          },
  
          getDataSet: function getDataSet(csvStr) {
  
              var arr = this.CSVToArray(csvStr, ',');
  
              var data = [];
  
              var header = arr[0];
  
              for (var i = 1; i < arr.length - 1; i++) {
                  var line = arr[i];
                  var item = {};
                  for (var j = 0; j < line.length; j++) {
                      var value = line[j];
                      if (header[j] == 'geometry') {
                          value = JSON.parse(value);
                      }
                      item[header[j]] = value;
                  }
                  data.push(item);
              }
  
              return new DataSet(data);
          }
      };
  
      exports.version = version;
      exports.canvasClear = clear;
      exports.canvasResolutionScale = resolutionScale$1;
      exports.canvasDrawSimple = drawSimple;
      exports.canvasDrawHeatmap = drawHeatmap;
      exports.canvasDrawGrid = drawGrid;
      exports.canvasDrawHoneycomb = drawHoneycomb;
      exports.webglDrawSimple = webglDrawSimple;
      exports.webglDrawPoint = point;
      exports.webglDrawLine = line;
      exports.webglDrawPolygon = polygon;
      exports.utilCityCenter = cityCenter;
      exports.utilCurve = curve;
      exports.utilForceEdgeBundling = ForceEdgeBundling;
      exports.utilDataRangeIntensity = Intensity;
      exports.utilDataRangeCategory = Category;
      exports.utilDataRangeChoropleth = Choropleth;
      exports.Map = MapHelper;
      exports.baiduMapCanvasLayer = CanvasLayer;
      exports.baiduMapAnimationLayer = AnimationLayer;
      exports.baiduMapLayer = Layer;
      exports.googleMapCanvasLayer = CanvasLayer$2;
      exports.googleMapLayer = Layer$2;
      exports.DataSet = DataSet;
      exports.geojson = geojson;
      exports.csv = csv;
  
      Object.defineProperty(exports, '__esModule', {
          value: true
      });
  
  })));