From 46cdd24ccb5e50f6717f19d0a04ff04a4378059d Mon Sep 17 00:00:00 2001 From: nikai Date: Thu, 15 Dec 2016 15:20:33 +0800 Subject: [PATCH] add Tween --- build/mapv.js | 8342 +++++++++++--------- build/mapv.min.js | 4 +- build/release/mapv.v2.0.3.js | 4475 +++++++++++ build/release/mapv.v2.0.3.min.js | 2 + build/version.js | 2 +- examples/baidu-map-forceEdgeBundling.html | 7 +- examples/baidu-map-point-heatmap-time.html | 2 +- examples/baidu-map-polyline-time.html | 11 +- examples/qianxi.html | 2 +- nodeCanvasExamples/line.png | Bin 167809 -> 109840 bytes nodeCanvasExamples/showLineCsvData.js | 2 +- package.json | 5 +- src/canvas/draw/heatmap.js | 8 +- src/map/baidu-map/Layer.js | 52 +- src/map/baidu-map/Layer.md | 3 + src/utils/animation/Animator.js | 191 +- src/utils/animation/Tween.js | 839 ++ 17 files changed, 9885 insertions(+), 4062 deletions(-) create mode 100644 build/release/mapv.v2.0.3.js create mode 100644 build/release/mapv.v2.0.3.min.js create mode 100644 src/utils/animation/Tween.js diff --git a/build/mapv.js b/build/mapv.js index f4716ac3..63081d4f 100644 --- a/build/mapv.js +++ b/build/mapv.js @@ -1,3877 +1,4475 @@ (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.2"; - -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 X = function () { - function X(dom, opt) { - classCallCheck(this, X); - - this.dom = dom; - this.opt = opt; - this.init(); - } - - createClass(X, [{ - key: 'init', - value: function init() { - var zoom = 1; - - var scene = new THREE.Scene(); - var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 10e7); - var renderer = new THREE.WebGLRenderer(); - - // add controls - var controls = new THREE.OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.dampingFactor = 0.25; - // controls.enableZoom = false; - renderer.setSize(this.dom.clientWidth, this.dom.clientHeight); - this.dom.appendChild(renderer.domElement); - - var geometry = new THREE.PlaneGeometry(80 * zoom, 50 * zoom, 10, 10); - var material = new THREE.MeshBasicMaterial({ - color: 0x585858, - wireframe: true - }); - var cube = window.cube = new THREE.Mesh(geometry, material); - cube.rotateX(-Math.PI / 2); - scene.add(cube); - camera.position.y = 50 * zoom; - camera.position.z = 50 * zoom; - camera.lookAt(new THREE.Vector3(0, 0, 0)); - - function render() { - requestAnimationFrame(render); - renderer.render(scene, camera); - controls.update(); - } - render(); - - var sizeZoom = this.opt.grid.size * zoom; - - var gradeData = {}; - var min = Infinity; - var max = -Infinity; - for (var i in data) { - var x = parseInt(data[i].lng * zoom / sizeZoom) * sizeZoom; - var y = parseInt(data[i].lat * zoom / sizeZoom) * sizeZoom; - gradeData[x + '_' + y] = gradeData[x + '_' + y] || 0; - gradeData[x + '_' + y]++; - max = Math.max(max, gradeData[x + '_' + y]); - min = Math.min(min, gradeData[x + '_' + y]); - } - - // color~ - var color = getColor(); - - var lines = new THREE.Object3D(); - for (var i in gradeData) { - var colorPersent = max == min ? 0 : (gradeData[i] - min) / (max - min); - var colorInedx = parseInt(colorPersent * (color.length / 4)) - 1; - colorInedx = colorInedx < 0 ? 0 : colorInedx; - var r = color[colorInedx * 4].toString(16); - r = r.length < 2 ? '0' + r : r; - var g = color[colorInedx * 4 + 1].toString(16); - g = g.length < 2 ? '0' + g : g; - var b = color[colorInedx * 4 + 2].toString(16); - b = b.length < 2 ? '0' + b : b; - - var height = gradeData[i] * 1.5; - var geometry = new THREE.BoxGeometry(sizeZoom * 0.9, height, sizeZoom * 0.9); - var material = new THREE.MeshBasicMaterial({ - color: '#' + r + g + b - }); - var cube = new THREE.Mesh(geometry, material); - var pos = i.split('_'); - cube.position.x = pos[0] - this.opt.center.lng * zoom; - cube.position.y = height / 2; - cube.position.z = this.opt.center.lat * zoom - pos[1]; - lines.add(cube); - } - scene.add(lines); - } - }]); - return X; -}(); - -function getColor() { - var canvas = document.createElement('canvas'); - var ctx = canvas.getContext('2d'); - var gradient = ctx.createLinearGradient(0, 0, 256, 0); - gradient.addColorStop(1, "#F00"); - gradient.addColorStop(0.6, "#FFFC00"); - gradient.addColorStop(0.3, "#00FF1D"); - gradient.addColorStop(0, "#000BFF"); - ctx.fillStyle = gradient; - ctx.fillRect(0, 0, 256, 1); - var data = ctx.getImageData(0, 0, 256, 1); - return data.data; -} - -/** - * @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.max = options.max || 100; - this.initPalette(); -} - -Intensity.prototype.initPalette = function () { - - var gradient = this.gradient; - - if (typeof document === 'undefined') { - var Canvas = require('canvas'); - var paletteCanvas = new Canvas(256, 1); - } else { - var paletteCanvas = document.createElement('canvas'); - } - - paletteCanvas.width = 256; - paletteCanvas.height = 1; - - var paletteCtx = this.paletteCtx = paletteCanvas.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 max = this.max; - - if (value > max) { - value = max; - } - - var index = Math.floor(value / max * (256 - 1)) * 4; - - var imageData = this.paletteCtx.getImageData(0, 0, 256, 1).data; - - return "rgba(" + imageData[index] + ", " + imageData[index + 1] + ", " + imageData[index + 2] + ", " + imageData[index + 3] / 256 + ")"; -}; - -/** - * @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 maxSize = this.maxSize; - - if (value > max) { - value = max; - } - - size = value / max * maxSize; - - return size; -}; - -function Flate(container) { - - this.container = container; - this.init(); - - var that = this; - - this.group = new THREE.Group(); - - this.center = [105, 33]; - - function animate(time) { - requestAnimationFrame(animate); - - //that.controls.update(); - - that.render(); - } - - requestAnimationFrame(animate); -} - -Flate.prototype.init = function () { - this.intensity = new Intensity({ - gradient: { - 0: '#006bab', - 1: '#002841' - }, - max: 100 - }); - - var WIDTH = this.container.offsetWidth; - var HEIGHT = this.container.offsetHeight; - var camera = this.camera = new THREE.PerspectiveCamera(40, WIDTH / HEIGHT, 0.01, 9000); - camera.position.x = 0; - camera.position.y = 0; - camera.position.z = 85; - camera.lookAt(new THREE.Vector3(0, 0, 0)); - - var scene = this.scene = new THREE.Scene(); - - var renderer = this.renderer = new THREE.WebGLRenderer({ - alpha: true - }); - - /* - var controls = this.controls = new THREE.OrbitControls( camera, renderer.domElement ); - controls.enableDamping = true; - controls.dampingFactor = 0.25; - controls.enableZoom = true; - */ - - // renderer.setClearColor('rgb(1, 11, 21)', 1); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(WIDTH, HEIGHT); - - this.container.appendChild(renderer.domElement); - - var floorTexture = THREE.ImageUtils.loadTexture('images/china.png'); - var floorMaterial = new THREE.MeshBasicMaterial({ - map: floorTexture, - transparent: true, - side: THREE.DoubleSide - }); - var floorGeometry = new THREE.PlaneGeometry(100, 100, 1, 1); - var floor = new THREE.Mesh(floorGeometry, floorMaterial); - floor.position.x = 0; - floor.position.y = 0; - floor.position.z = 0; - //scene.add(floor); - - // LIGHT - var light = new THREE.PointLight('rgb(50, 50, 250)'); - light.position.set(0, 0, 35); - scene.add(light); - - var SUBDIVISIONS = 20; - var geometry = new THREE.Geometry(); - var curve = new THREE.QuadraticBezierCurve3(); - curve.v0 = new THREE.Vector3(0, 0, 0); - curve.v1 = new THREE.Vector3(20, 20, 0); - curve.v2 = new THREE.Vector3(40, 40, 0); - for (var j = 0; j < SUBDIVISIONS; j++) { - geometry.vertices.push(curve.getPoint(j / SUBDIVISIONS)); - } - - var material = new THREE.LineBasicMaterial({ color: 0xff0000, linewidth: 95 }); - var line = this.line = new THREE.Line(geometry, material); - //scene.add(line); - - this.current = 0; -}; - -Flate.prototype.render = function () { - - var SUBDIVISIONS = 50; - var geometry = new THREE.Geometry(); - var curve = new THREE.QuadraticBezierCurve3(); - curve.v0 = new THREE.Vector3(0, 0, 0); - curve.v1 = new THREE.Vector3(25, 25, 50); - curve.v2 = new THREE.Vector3(50, 50, 0); - this.current += 0.01; - if (this.current > 1) { - this.current = 0; - } - - for (var j = 0; j < SUBDIVISIONS; j++) { - var percent = j / SUBDIVISIONS; - if (percent < this.current) { - geometry.vertices.push(curve.getPoint(percent)); - } - } - - //this.line.geometry = geometry; - - this.renderer.render(this.scene, this.camera); -}; - -Flate.prototype.setDataSet = function (dataSet) { - // create a canvas element - var canvas = document.createElement('canvas'); - var context = canvas.getContext('2d'); - canvas.width = 50; - canvas.height = 50; - context.fillStyle = "rgba(255,255,50,0.75)"; - //context.shadowColor = "rgba(255,255,255,0.95)"; - //context.shadowBlur = 0; - context.arc(25, 25, 10, 0, Math.PI * 2); - context.fill(); - - // canvas contents will be used for a texture - var texture = new THREE.Texture(canvas); - texture.needsUpdate = true; - - var material = new THREE.MeshBasicMaterial({ map: texture, side: THREE.DoubleSide }); - material.transparent = true; - - var rs = dataSet.get(); - var features = rs; - for (var i = 0; i < features.length; i++) { - var feature = features[i]; - if (feature.geometry.type == 'Polygon') { - var coords = this.getCoordinates(feature.geometry.coordinates[0]); - this.addShape(coords); - } else if (feature.geometry.type == 'MultiPolygon') { - for (var j = 0; j < feature.geometry.coordinates.length; j++) { - var coords = this.getCoordinates(feature.geometry.coordinates[j][0]); - this.addShape(coords); - } - } else if (feature.geometry.type == 'Point') { - - var size = canvas.width / 15 + Math.random() * 4; - var mesh = new THREE.Mesh(new THREE.PlaneGeometry(size, size), material); - mesh.position.set(feature.geometry.coordinates[0] - this.center[0], feature.geometry.coordinates[1] - this.center[1], 1); - this.scene.add(mesh); - } - - var cityname = feature.name; - var center = feature.cp; - } - this.scene.add(this.group); -}; - -Flate.prototype.getCoordinates = function (coordinates) { - var coords = []; - for (var j = 0; j < coordinates.length; j++) { - coords.push(new THREE.Vector2(coordinates[j][0] - this.center[0], coordinates[j][1] - this.center[1])); - } - return coords; -}; - -Flate.prototype.addShape = function (coords) { - var shape = new THREE.Shape(coords); - var geometry = new THREE.ShapeGeometry(shape); - - var color = 'rgb(' + ~~(Math.random() * 256) + ', ' + ~~(Math.random() * 256) + ', ' + ~~(Math.random() * 256) + ')'; - color = this.intensity.getColor(Math.random() * 100); - var mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: color, side: THREE.DoubleSide })); - mesh.position.set(0, 0, 0); - this.group.add(mesh); - - var points = shape.createPointsGeometry(); - var line = new THREE.Line(points, new THREE.LineBasicMaterial({ color: 'rgb(0, 137, 191)', linewidth: 1 })); - line.position.set(0, 0, 0.1); - this.group.add(line); -}; - -var flights = [[43.061306, 74.477556, 40.608989, 72.793269]]; - -var index = 100; -while (index--) { - flights.push([19.670399 + Math.random() * 35, 78.895343 + Math.random() * 50, 19.670399 + Math.random() * 35, 78.895343 + Math.random() * 50]); -} - -var positions; -var start_flight_idx = 0; -var end_flight_idx = flights.length; -var flight_path_splines = []; -var flight_distance = []; -var flight_path_lines; -var flight_track_opacity = 0.32; -var flight_point_cloud_geom; -var flight_point_start_time = []; -var flight_point_end_time = []; -var flight_point_speed_changed = false; -var flight_point_speed_scaling = 5.0; -var flight_point_speed_min_scaling = 1.0; -var flight_point_speed_max_scaling = 25.0; - -function Earth(container) { - - this.container = container; - this.init(); - - var that = this; - - function animate(time) { - requestAnimationFrame(animate); - that.render(); - } - - requestAnimationFrame(animate); -} - -Earth.prototype.init = function () { - var WIDTH = this.container.offsetWidth; - var HEIGHT = this.container.offsetHeight; - var camera = this.camera = new THREE.PerspectiveCamera(40, WIDTH / HEIGHT, 0.01, 9000); - camera.position.z = 1.0; - - var scene = this.scene = new THREE.Scene(); - - var renderer = this.renderer = new THREE.WebGLRenderer({ - alpha: true - }); - // renderer.setClearColor('rgb(1, 11, 21)', 1); - renderer.setPixelRatio(window.devicePixelRatio); - renderer.setSize(WIDTH, HEIGHT); - - this.container.appendChild(renderer.domElement); - - // LIGHT - /* - var light = new THREE.PointLight('rgb(50, 50, 250)'); - light.position.set(0, 0, 35); - scene.add(light); - */ - - scene.add(new THREE.AmbientLight(0x777777)); - - var light1 = new THREE.DirectionalLight(0xffffff, 0.2); - light1.position.set(5, 3, 5); - scene.add(light1); - - var light2 = new THREE.DirectionalLight(0xffffff, 0.2); - light2.position.set(5, 3, -5); - scene.add(light2); - - var earth_img, elevation_img, water_img; - var radius = 0.5; - var segments = 64; - earth_img = THREE.ImageUtils.loadTexture('images/earth_airports.png', THREE.UVMapping, function () { - elevation_img = THREE.ImageUtils.loadTexture('images/elevation.jpg', THREE.UVMapping, function () { - water_img = THREE.ImageUtils.loadTexture('images/water.png', THREE.UVMapping, function () { - var earth = new THREE.Mesh(new THREE.SphereGeometry(radius, segments, segments), new THREE.MeshPhongMaterial({ - map: earth_img, - bumpMap: elevation_img, - bumpScale: 0.01, - specularMap: water_img, - specular: new THREE.Color('grey') - })); - earth.rotation.y = 170 * (Math.PI / 180); - earth.rotation.x = 30 * (Math.PI / 180); - scene.add(earth); - - generateControlPoints(radius); - - flight_path_lines = flightPathLines(); - earth.add(flight_path_lines); - - earth.add(flightPointCloud()); - }); - }); - }); -}; - -Earth.prototype.render = function () { - update_flights(); - - this.renderer.render(this.scene, this.camera); -}; - -Earth.prototype.setDataSet = function (dataSet) { - console.log(dataSet.get()); -}; - -function generateControlPoints(radius) { - for (var f = start_flight_idx; f < end_flight_idx; ++f) { - - var start_lat = flights[f][0]; - var start_lng = flights[f][1]; - var end_lat = flights[f][2]; - var end_lng = flights[f][3]; - - var max_height = Math.random() * 0.04; - - var points = []; - var spline_control_points = 8; - for (var i = 0; i < spline_control_points + 1; i++) { - var arc_angle = i * 180.0 / spline_control_points; - var arc_radius = radius + Math.sin(arc_angle * Math.PI / 180.0) * max_height; - var latlng = latlngInterPoint(start_lat, start_lng, end_lat, end_lng, i / spline_control_points); - - var pos = xyzFromLatLng(latlng.lat, latlng.lng, arc_radius); - - points.push(new THREE.Vector3(pos.x, pos.y, pos.z)); - } - - var spline = new THREE.SplineCurve3(points); - - flight_path_splines.push(spline); - - var arc_length = spline.getLength(); - flight_distance.push(arc_length); - - setFlightTimes(f); - } -} - -function latlngInterPoint(lat1, lng1, lat2, lng2, offset) { - lat1 = lat1 * Math.PI / 180.0; - lng1 = lng1 * Math.PI / 180.0; - lat2 = lat2 * Math.PI / 180.0; - lng2 = lng2 * Math.PI / 180.0; - - var d = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin((lat1 - lat2) / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin((lng1 - lng2) / 2), 2))); - var A = Math.sin((1 - offset) * d) / Math.sin(d); - var B = Math.sin(offset * d) / Math.sin(d); - var x = A * Math.cos(lat1) * Math.cos(lng1) + B * Math.cos(lat2) * Math.cos(lng2); - var y = A * Math.cos(lat1) * Math.sin(lng1) + B * Math.cos(lat2) * Math.sin(lng2); - var z = A * Math.sin(lat1) + B * Math.sin(lat2); - var lat = Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))) * 180 / Math.PI; - var lng = Math.atan2(y, x) * 180 / Math.PI; - - return { - lat: lat, - lng: lng - }; -} - -function xyzFromLatLng(lat, lng, radius) { - var phi = (90 - lat) * Math.PI / 180; - var theta = (360 - lng) * Math.PI / 180; - - return { - x: radius * Math.sin(phi) * Math.cos(theta), - y: radius * Math.cos(phi), - z: radius * Math.sin(phi) * Math.sin(theta) - }; -} - -function flightPathLines() { - - var num_control_points = 32; - - var geometry = new THREE.BufferGeometry(); - var material = new THREE.LineBasicMaterial({ - color: 0xffff00, - vertexColors: THREE.VertexColors, - transparent: true, - opacity: flight_track_opacity, - depthTest: true, - depthWrite: false, - linewidth: 1.0001 - }); - var line_positions = new Float32Array(flights.length * 3 * 2 * num_control_points); - var colors = new Float32Array(flights.length * 3 * 2 * num_control_points); - - for (var i = start_flight_idx; i < end_flight_idx; ++i) { - - for (var j = 0; j < num_control_points - 1; ++j) { - - var start_pos = flight_path_splines[i].getPoint(j / (num_control_points - 1)); - var end_pos = flight_path_splines[i].getPoint((j + 1) / (num_control_points - 1)); - - line_positions[(i * num_control_points + j) * 6 + 0] = start_pos.x; - line_positions[(i * num_control_points + j) * 6 + 1] = start_pos.y; - line_positions[(i * num_control_points + j) * 6 + 2] = start_pos.z; - line_positions[(i * num_control_points + j) * 6 + 3] = end_pos.x; - line_positions[(i * num_control_points + j) * 6 + 4] = end_pos.y; - line_positions[(i * num_control_points + j) * 6 + 5] = end_pos.z; - - colors[(i * num_control_points + j) * 6 + 0] = 1.0; - colors[(i * num_control_points + j) * 6 + 1] = 0.4; - colors[(i * num_control_points + j) * 6 + 2] = 1.0; - colors[(i * num_control_points + j) * 6 + 3] = 1.0; - colors[(i * num_control_points + j) * 6 + 4] = 0.4; - colors[(i * num_control_points + j) * 6 + 5] = 1.0; - } - } - - geometry.addAttribute('position', new THREE.BufferAttribute(line_positions, 3)); - geometry.addAttribute('color', new THREE.BufferAttribute(colors, 3)); - - geometry.computeBoundingSphere(); - - return new THREE.Line(geometry, material, THREE.LinePieces); -} - -function flightPointCloud() { - flight_point_cloud_geom = new THREE.BufferGeometry(); - - var num_points = flights.length; - - positions = new Float32Array(num_points * 3); - var colors = new Float32Array(num_points * 3); - var sizes = new Float32Array(num_points); - - for (var i = 0; i < num_points; i++) { - positions[3 * i + 0] = 0; - positions[3 * i + 1] = 0; - positions[3 * i + 2] = 0; - - colors[3 * i + 0] = Math.random(); - colors[3 * i + 1] = Math.random(); - colors[3 * i + 2] = Math.random(); - - sizes[i] = 0.1; - } - - flight_point_cloud_geom.addAttribute('position', new THREE.BufferAttribute(positions, 3)); - flight_point_cloud_geom.addAttribute('customColor', new THREE.BufferAttribute(colors, 3)); - flight_point_cloud_geom.addAttribute('size', new THREE.BufferAttribute(sizes, 1)); - flight_point_cloud_geom.computeBoundingBox(); - - var attributes = { - size: { - type: 'f', - value: null - }, - customColor: { - type: 'c', - value: null - } - }; - - var uniforms = { - color: { - type: "c", - value: new THREE.Color(0xffffff) - }, - texture: { - type: "t", - value: THREE.ImageUtils.loadTexture("images/point.png") - } - }; - - var shaderMaterial = new THREE.ShaderMaterial({ - uniforms: uniforms, - //attributes: attributes, - vertexShader: document.getElementById('vertexshader').textContent, - fragmentShader: document.getElementById('fragmentshader').textContent, - blending: THREE.AdditiveBlending, - depthTest: true, - depthWrite: false, - transparent: true - }); - - return new THREE.Points(flight_point_cloud_geom, shaderMaterial); -} - -function update_flights() { - if (!flight_point_cloud_geom) { - return; - } - flight_point_cloud_geom.attributes.position.needsUpdate = true; - - for (var i = start_flight_idx; i < end_flight_idx; ++i) { - - if (Date.now() > flight_point_start_time[i]) { - var ease_val = easeOutQuadratic(Date.now() - flight_point_start_time[i], 0, 1, flight_point_end_time[i] - flight_point_start_time[i]); - - if (ease_val < 0 || flight_point_speed_changed) { - ease_val = 0; - setFlightTimes(i); - } - - var pos = flight_path_splines[i].getPoint(ease_val); - positions[3 * i + 0] = pos.x; - positions[3 * i + 1] = pos.y; - positions[3 * i + 2] = pos.z; - } - } -} - -function setFlightTimes(index) { - var scaling_factor = (flight_point_speed_scaling - flight_point_speed_min_scaling) / (flight_point_speed_max_scaling - flight_point_speed_min_scaling); - var duration = (1 - scaling_factor) * flight_distance[index] * 80000; - - var start_time = Date.now() + Math.random() * 5000; - flight_point_start_time[index] = start_time; - flight_point_end_time[index] = start_time + duration; -} - -function easeOutQuadratic(t, b, c, d) { - if ((t /= d / 2) < 1) return c / 2 * t * t + b; - return -c / 2 * (--t * (t - 2) - 1) + b; -} - -/** - * @author kyle / http://nikai.us/ - */ - -function clear (context) { - 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/ - */ - -function resolutionScale (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); - } - } -}; - -/** - * @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++) { - 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); - } - - //console.timeEnd('transferCoordinate time') - - return data; -}; - -/** - * set data. - */ -DataSet.prototype.set = function (data) { - this.clear(); - this.add(data); - this._trigger('change'); -}; - -/** - * 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) { - - for (var i = 0; i < data.length; i++) { - - var item = data[i]; - - if (data[i].geometry) { - - if (data[i].geometry.type === 'Point') { - var coordinates = data[i].geometry.coordinates; - data[i].geometry._coordinates = transferFn(coordinates); - } - - if (data[i].geometry.type === 'Polygon' || data[i].geometry.type === 'MultiPolygon') { - - var coordinates = data[i].geometry.coordinates; - - if (data[i].geometry.type === 'Polygon') { - - var newCoordinates = getPolygon(coordinates); - data[i].geometry._coordinates = newCoordinates; - } else if (data[i].geometry.type === 'MultiPolygon') { - var newCoordinates = []; - for (var c = 0; c < coordinates.length; c++) { - var polygon = coordinates[c]; - var polygon = getPolygon(polygon); - newCoordinates.push(polygon); - } - - data[i].geometry._coordinates = newCoordinates; - } - } - - if (data[i].geometry.type === 'LineString') { - var coordinates = data[i].geometry.coordinates; - var newCoordinates = []; - for (var j = 0; j < coordinates.length; j++) { - newCoordinates.push(transferFn(coordinates[j])); - } - data[i].geometry._coordinates = newCoordinates; - } - } - } - - 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 = data[0][columnName]; - - for (var i = 1; i < data.length; i++) { - if (data[i][columnName] > max) { - max = data[i][columnName]; - } - } - - 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 = data[0][columnName]; - - for (var i = 1; i < data.length; i++) { - if (data[i][columnName] < min) { - min = data[i][columnName]; - } - } - - 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 { - 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(); - }; - } + 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.3"; + + 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 X = function () { + function X(dom, opt) { + classCallCheck(this, X); + + this.dom = dom; + this.opt = opt; + this.init(); + } + + createClass(X, [{ + key: 'init', + value: function init() { + var zoom = 1; + + var scene = new THREE.Scene(); + var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 10e7); + var renderer = new THREE.WebGLRenderer(); + + // add controls + var controls = new THREE.OrbitControls(camera, renderer.domElement); + controls.enableDamping = true; + controls.dampingFactor = 0.25; + // controls.enableZoom = false; + renderer.setSize(this.dom.clientWidth, this.dom.clientHeight); + this.dom.appendChild(renderer.domElement); + + var geometry = new THREE.PlaneGeometry(80 * zoom, 50 * zoom, 10, 10); + var material = new THREE.MeshBasicMaterial({ + color: 0x585858, + wireframe: true + }); + var cube = window.cube = new THREE.Mesh(geometry, material); + cube.rotateX(-Math.PI / 2); + scene.add(cube); + camera.position.y = 50 * zoom; + camera.position.z = 50 * zoom; + camera.lookAt(new THREE.Vector3(0, 0, 0)); + + function render() { + requestAnimationFrame(render); + renderer.render(scene, camera); + controls.update(); + } + render(); + + var sizeZoom = this.opt.grid.size * zoom; + + var gradeData = {}; + var min = Infinity; + var max = -Infinity; + for (var i in data) { + var x = parseInt(data[i].lng * zoom / sizeZoom) * sizeZoom; + var y = parseInt(data[i].lat * zoom / sizeZoom) * sizeZoom; + gradeData[x + '_' + y] = gradeData[x + '_' + y] || 0; + gradeData[x + '_' + y]++; + max = Math.max(max, gradeData[x + '_' + y]); + min = Math.min(min, gradeData[x + '_' + y]); + } + + // color~ + var color = getColor(); + + var lines = new THREE.Object3D(); + for (var i in gradeData) { + var colorPersent = max == min ? 0 : (gradeData[i] - min) / (max - min); + var colorInedx = parseInt(colorPersent * (color.length / 4)) - 1; + colorInedx = colorInedx < 0 ? 0 : colorInedx; + var r = color[colorInedx * 4].toString(16); + r = r.length < 2 ? '0' + r : r; + var g = color[colorInedx * 4 + 1].toString(16); + g = g.length < 2 ? '0' + g : g; + var b = color[colorInedx * 4 + 2].toString(16); + b = b.length < 2 ? '0' + b : b; + + var height = gradeData[i] * 1.5; + var geometry = new THREE.BoxGeometry(sizeZoom * 0.9, height, sizeZoom * 0.9); + var material = new THREE.MeshBasicMaterial({ + color: '#' + r + g + b + }); + var cube = new THREE.Mesh(geometry, material); + var pos = i.split('_'); + cube.position.x = pos[0] - this.opt.center.lng * zoom; + cube.position.y = height / 2; + cube.position.z = this.opt.center.lat * zoom - pos[1]; + lines.add(cube); + } + scene.add(lines); + } + }]); + return X; + }(); + + function getColor() { + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + var gradient = ctx.createLinearGradient(0, 0, 256, 0); + gradient.addColorStop(1, "#F00"); + gradient.addColorStop(0.6, "#FFFC00"); + gradient.addColorStop(0.3, "#00FF1D"); + gradient.addColorStop(0, "#000BFF"); + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, 256, 1); + var data = ctx.getImageData(0, 0, 256, 1); + return data.data; + } + + /** + * @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.max = options.max || 100; + this.initPalette(); + } + + Intensity.prototype.initPalette = function () { + + var gradient = this.gradient; + + if (typeof document === 'undefined') { + var Canvas = require('canvas'); + var paletteCanvas = new Canvas(256, 1); + } else { + var paletteCanvas = document.createElement('canvas'); + } + + paletteCanvas.width = 256; + paletteCanvas.height = 1; + + var paletteCtx = this.paletteCtx = paletteCanvas.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 max = this.max; + + if (value > max) { + value = max; + } + + var index = Math.floor(value / max * (256 - 1)) * 4; + + var imageData = this.paletteCtx.getImageData(0, 0, 256, 1).data; + + return "rgba(" + imageData[index] + ", " + imageData[index + 1] + ", " + imageData[index + 2] + ", " + imageData[index + 3] / 256 + ")"; + }; + + /** + * @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 maxSize = this.maxSize; + + if (value > max) { + value = max; + } + + size = value / max * maxSize; + + return size; + }; + + function Flate(container) { + + this.container = container; + this.init(); + + var that = this; + + this.group = new THREE.Group(); + + this.center = [105, 33]; + + function animate(time) { + requestAnimationFrame(animate); + + //that.controls.update(); + + that.render(); + } + + requestAnimationFrame(animate); + } + + Flate.prototype.init = function () { + this.intensity = new Intensity({ + gradient: { + 0: '#006bab', + 1: '#002841' + }, + max: 100 + }); + + var WIDTH = this.container.offsetWidth; + var HEIGHT = this.container.offsetHeight; + var camera = this.camera = new THREE.PerspectiveCamera(40, WIDTH / HEIGHT, 0.01, 9000); + camera.position.x = 0; + camera.position.y = 0; + camera.position.z = 85; + camera.lookAt(new THREE.Vector3(0, 0, 0)); + + var scene = this.scene = new THREE.Scene(); + + var renderer = this.renderer = new THREE.WebGLRenderer({ + alpha: true + }); + + /* + var controls = this.controls = new THREE.OrbitControls( camera, renderer.domElement ); + controls.enableDamping = true; + controls.dampingFactor = 0.25; + controls.enableZoom = true; + */ + + // renderer.setClearColor('rgb(1, 11, 21)', 1); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(WIDTH, HEIGHT); + + this.container.appendChild(renderer.domElement); + + var floorTexture = THREE.ImageUtils.loadTexture('images/china.png'); + var floorMaterial = new THREE.MeshBasicMaterial({ + map: floorTexture, + transparent: true, + side: THREE.DoubleSide + }); + var floorGeometry = new THREE.PlaneGeometry(100, 100, 1, 1); + var floor = new THREE.Mesh(floorGeometry, floorMaterial); + floor.position.x = 0; + floor.position.y = 0; + floor.position.z = 0; + //scene.add(floor); + + // LIGHT + var light = new THREE.PointLight('rgb(50, 50, 250)'); + light.position.set(0, 0, 35); + scene.add(light); + + var SUBDIVISIONS = 20; + var geometry = new THREE.Geometry(); + var curve = new THREE.QuadraticBezierCurve3(); + curve.v0 = new THREE.Vector3(0, 0, 0); + curve.v1 = new THREE.Vector3(20, 20, 0); + curve.v2 = new THREE.Vector3(40, 40, 0); + for (var j = 0; j < SUBDIVISIONS; j++) { + geometry.vertices.push(curve.getPoint(j / SUBDIVISIONS)); + } + + var material = new THREE.LineBasicMaterial({ color: 0xff0000, linewidth: 95 }); + var line = this.line = new THREE.Line(geometry, material); + //scene.add(line); + + this.current = 0; + }; + + Flate.prototype.render = function () { + + var SUBDIVISIONS = 50; + var geometry = new THREE.Geometry(); + var curve = new THREE.QuadraticBezierCurve3(); + curve.v0 = new THREE.Vector3(0, 0, 0); + curve.v1 = new THREE.Vector3(25, 25, 50); + curve.v2 = new THREE.Vector3(50, 50, 0); + this.current += 0.01; + if (this.current > 1) { + this.current = 0; + } + + for (var j = 0; j < SUBDIVISIONS; j++) { + var percent = j / SUBDIVISIONS; + if (percent < this.current) { + geometry.vertices.push(curve.getPoint(percent)); + } + } + + //this.line.geometry = geometry; + + this.renderer.render(this.scene, this.camera); + }; + + Flate.prototype.setDataSet = function (dataSet) { + // create a canvas element + var canvas = document.createElement('canvas'); + var context = canvas.getContext('2d'); + canvas.width = 50; + canvas.height = 50; + context.fillStyle = "rgba(255,255,50,0.75)"; + //context.shadowColor = "rgba(255,255,255,0.95)"; + //context.shadowBlur = 0; + context.arc(25, 25, 10, 0, Math.PI * 2); + context.fill(); + + // canvas contents will be used for a texture + var texture = new THREE.Texture(canvas); + texture.needsUpdate = true; + + var material = new THREE.MeshBasicMaterial({ map: texture, side: THREE.DoubleSide }); + material.transparent = true; + + var rs = dataSet.get(); + var features = rs; + for (var i = 0; i < features.length; i++) { + var feature = features[i]; + if (feature.geometry.type == 'Polygon') { + var coords = this.getCoordinates(feature.geometry.coordinates[0]); + this.addShape(coords); + } else if (feature.geometry.type == 'MultiPolygon') { + for (var j = 0; j < feature.geometry.coordinates.length; j++) { + var coords = this.getCoordinates(feature.geometry.coordinates[j][0]); + this.addShape(coords); + } + } else if (feature.geometry.type == 'Point') { + + var size = canvas.width / 15 + Math.random() * 4; + var mesh = new THREE.Mesh(new THREE.PlaneGeometry(size, size), material); + mesh.position.set(feature.geometry.coordinates[0] - this.center[0], feature.geometry.coordinates[1] - this.center[1], 1); + this.scene.add(mesh); + } + + var cityname = feature.name; + var center = feature.cp; + } + this.scene.add(this.group); + }; + + Flate.prototype.getCoordinates = function (coordinates) { + var coords = []; + for (var j = 0; j < coordinates.length; j++) { + coords.push(new THREE.Vector2(coordinates[j][0] - this.center[0], coordinates[j][1] - this.center[1])); + } + return coords; + }; + + Flate.prototype.addShape = function (coords) { + var shape = new THREE.Shape(coords); + var geometry = new THREE.ShapeGeometry(shape); + + var color = 'rgb(' + ~~(Math.random() * 256) + ', ' + ~~(Math.random() * 256) + ', ' + ~~(Math.random() * 256) + ')'; + color = this.intensity.getColor(Math.random() * 100); + var mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: color, side: THREE.DoubleSide })); + mesh.position.set(0, 0, 0); + this.group.add(mesh); + + var points = shape.createPointsGeometry(); + var line = new THREE.Line(points, new THREE.LineBasicMaterial({ color: 'rgb(0, 137, 191)', linewidth: 1 })); + line.position.set(0, 0, 0.1); + this.group.add(line); + }; + + var flights = [[43.061306, 74.477556, 40.608989, 72.793269]]; + + var index = 100; + while (index--) { + flights.push([19.670399 + Math.random() * 35, 78.895343 + Math.random() * 50, 19.670399 + Math.random() * 35, 78.895343 + Math.random() * 50]); + } + + var positions; + var start_flight_idx = 0; + var end_flight_idx = flights.length; + var flight_path_splines = []; + var flight_distance = []; + var flight_path_lines; + var flight_track_opacity = 0.32; + var flight_point_cloud_geom; + var flight_point_start_time = []; + var flight_point_end_time = []; + var flight_point_speed_changed = false; + var flight_point_speed_scaling = 5.0; + var flight_point_speed_min_scaling = 1.0; + var flight_point_speed_max_scaling = 25.0; + + function Earth(container) { + + this.container = container; + this.init(); + + var that = this; + + function animate(time) { + requestAnimationFrame(animate); + that.render(); + } + + requestAnimationFrame(animate); + } + + Earth.prototype.init = function () { + var WIDTH = this.container.offsetWidth; + var HEIGHT = this.container.offsetHeight; + var camera = this.camera = new THREE.PerspectiveCamera(40, WIDTH / HEIGHT, 0.01, 9000); + camera.position.z = 1.0; + + var scene = this.scene = new THREE.Scene(); + + var renderer = this.renderer = new THREE.WebGLRenderer({ + alpha: true + }); + // renderer.setClearColor('rgb(1, 11, 21)', 1); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(WIDTH, HEIGHT); + + this.container.appendChild(renderer.domElement); + + // LIGHT + /* + var light = new THREE.PointLight('rgb(50, 50, 250)'); + light.position.set(0, 0, 35); + scene.add(light); + */ + + scene.add(new THREE.AmbientLight(0x777777)); + + var light1 = new THREE.DirectionalLight(0xffffff, 0.2); + light1.position.set(5, 3, 5); + scene.add(light1); + + var light2 = new THREE.DirectionalLight(0xffffff, 0.2); + light2.position.set(5, 3, -5); + scene.add(light2); + + var earth_img, elevation_img, water_img; + var radius = 0.5; + var segments = 64; + earth_img = THREE.ImageUtils.loadTexture('images/earth_airports.png', THREE.UVMapping, function () { + elevation_img = THREE.ImageUtils.loadTexture('images/elevation.jpg', THREE.UVMapping, function () { + water_img = THREE.ImageUtils.loadTexture('images/water.png', THREE.UVMapping, function () { + var earth = new THREE.Mesh(new THREE.SphereGeometry(radius, segments, segments), new THREE.MeshPhongMaterial({ + map: earth_img, + bumpMap: elevation_img, + bumpScale: 0.01, + specularMap: water_img, + specular: new THREE.Color('grey') + })); + earth.rotation.y = 170 * (Math.PI / 180); + earth.rotation.x = 30 * (Math.PI / 180); + scene.add(earth); + + generateControlPoints(radius); + + flight_path_lines = flightPathLines(); + earth.add(flight_path_lines); + + earth.add(flightPointCloud()); + }); + }); + }); + }; + + Earth.prototype.render = function () { + update_flights(); + + this.renderer.render(this.scene, this.camera); + }; + + Earth.prototype.setDataSet = function (dataSet) { + console.log(dataSet.get()); + }; + + function generateControlPoints(radius) { + for (var f = start_flight_idx; f < end_flight_idx; ++f) { + + var start_lat = flights[f][0]; + var start_lng = flights[f][1]; + var end_lat = flights[f][2]; + var end_lng = flights[f][3]; + + var max_height = Math.random() * 0.04; + + var points = []; + var spline_control_points = 8; + for (var i = 0; i < spline_control_points + 1; i++) { + var arc_angle = i * 180.0 / spline_control_points; + var arc_radius = radius + Math.sin(arc_angle * Math.PI / 180.0) * max_height; + var latlng = latlngInterPoint(start_lat, start_lng, end_lat, end_lng, i / spline_control_points); + + var pos = xyzFromLatLng(latlng.lat, latlng.lng, arc_radius); + + points.push(new THREE.Vector3(pos.x, pos.y, pos.z)); + } + + var spline = new THREE.SplineCurve3(points); + + flight_path_splines.push(spline); + + var arc_length = spline.getLength(); + flight_distance.push(arc_length); + + setFlightTimes(f); + } + } + + function latlngInterPoint(lat1, lng1, lat2, lng2, offset) { + lat1 = lat1 * Math.PI / 180.0; + lng1 = lng1 * Math.PI / 180.0; + lat2 = lat2 * Math.PI / 180.0; + lng2 = lng2 * Math.PI / 180.0; + + var d = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin((lat1 - lat2) / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin((lng1 - lng2) / 2), 2))); + var A = Math.sin((1 - offset) * d) / Math.sin(d); + var B = Math.sin(offset * d) / Math.sin(d); + var x = A * Math.cos(lat1) * Math.cos(lng1) + B * Math.cos(lat2) * Math.cos(lng2); + var y = A * Math.cos(lat1) * Math.sin(lng1) + B * Math.cos(lat2) * Math.sin(lng2); + var z = A * Math.sin(lat1) + B * Math.sin(lat2); + var lat = Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))) * 180 / Math.PI; + var lng = Math.atan2(y, x) * 180 / Math.PI; + + return { + lat: lat, + lng: lng + }; + } + + function xyzFromLatLng(lat, lng, radius) { + var phi = (90 - lat) * Math.PI / 180; + var theta = (360 - lng) * Math.PI / 180; + + return { + x: radius * Math.sin(phi) * Math.cos(theta), + y: radius * Math.cos(phi), + z: radius * Math.sin(phi) * Math.sin(theta) + }; + } + + function flightPathLines() { + + var num_control_points = 32; + + var geometry = new THREE.BufferGeometry(); + var material = new THREE.LineBasicMaterial({ + color: 0xffff00, + vertexColors: THREE.VertexColors, + transparent: true, + opacity: flight_track_opacity, + depthTest: true, + depthWrite: false, + linewidth: 1.0001 + }); + var line_positions = new Float32Array(flights.length * 3 * 2 * num_control_points); + var colors = new Float32Array(flights.length * 3 * 2 * num_control_points); + + for (var i = start_flight_idx; i < end_flight_idx; ++i) { + + for (var j = 0; j < num_control_points - 1; ++j) { + + var start_pos = flight_path_splines[i].getPoint(j / (num_control_points - 1)); + var end_pos = flight_path_splines[i].getPoint((j + 1) / (num_control_points - 1)); + + line_positions[(i * num_control_points + j) * 6 + 0] = start_pos.x; + line_positions[(i * num_control_points + j) * 6 + 1] = start_pos.y; + line_positions[(i * num_control_points + j) * 6 + 2] = start_pos.z; + line_positions[(i * num_control_points + j) * 6 + 3] = end_pos.x; + line_positions[(i * num_control_points + j) * 6 + 4] = end_pos.y; + line_positions[(i * num_control_points + j) * 6 + 5] = end_pos.z; + + colors[(i * num_control_points + j) * 6 + 0] = 1.0; + colors[(i * num_control_points + j) * 6 + 1] = 0.4; + colors[(i * num_control_points + j) * 6 + 2] = 1.0; + colors[(i * num_control_points + j) * 6 + 3] = 1.0; + colors[(i * num_control_points + j) * 6 + 4] = 0.4; + colors[(i * num_control_points + j) * 6 + 5] = 1.0; + } + } + + geometry.addAttribute('position', new THREE.BufferAttribute(line_positions, 3)); + geometry.addAttribute('color', new THREE.BufferAttribute(colors, 3)); + + geometry.computeBoundingSphere(); + + return new THREE.Line(geometry, material, THREE.LinePieces); + } + + function flightPointCloud() { + flight_point_cloud_geom = new THREE.BufferGeometry(); + + var num_points = flights.length; + + positions = new Float32Array(num_points * 3); + var colors = new Float32Array(num_points * 3); + var sizes = new Float32Array(num_points); + + for (var i = 0; i < num_points; i++) { + positions[3 * i + 0] = 0; + positions[3 * i + 1] = 0; + positions[3 * i + 2] = 0; + + colors[3 * i + 0] = Math.random(); + colors[3 * i + 1] = Math.random(); + colors[3 * i + 2] = Math.random(); + + sizes[i] = 0.1; + } + + flight_point_cloud_geom.addAttribute('position', new THREE.BufferAttribute(positions, 3)); + flight_point_cloud_geom.addAttribute('customColor', new THREE.BufferAttribute(colors, 3)); + flight_point_cloud_geom.addAttribute('size', new THREE.BufferAttribute(sizes, 1)); + flight_point_cloud_geom.computeBoundingBox(); + + var attributes = { + size: { + type: 'f', + value: null + }, + customColor: { + type: 'c', + value: null + } + }; + + var uniforms = { + color: { + type: "c", + value: new THREE.Color(0xffffff) + }, + texture: { + type: "t", + value: THREE.ImageUtils.loadTexture("images/point.png") + } + }; + + var shaderMaterial = new THREE.ShaderMaterial({ + uniforms: uniforms, + //attributes: attributes, + vertexShader: document.getElementById('vertexshader').textContent, + fragmentShader: document.getElementById('fragmentshader').textContent, + blending: THREE.AdditiveBlending, + depthTest: true, + depthWrite: false, + transparent: true + }); + + return new THREE.Points(flight_point_cloud_geom, shaderMaterial); + } + + function update_flights() { + if (!flight_point_cloud_geom) { + return; + } + flight_point_cloud_geom.attributes.position.needsUpdate = true; + + for (var i = start_flight_idx; i < end_flight_idx; ++i) { + + if (Date.now() > flight_point_start_time[i]) { + var ease_val = easeOutQuadratic(Date.now() - flight_point_start_time[i], 0, 1, flight_point_end_time[i] - flight_point_start_time[i]); + + if (ease_val < 0 || flight_point_speed_changed) { + ease_val = 0; + setFlightTimes(i); + } + + var pos = flight_path_splines[i].getPoint(ease_val); + positions[3 * i + 0] = pos.x; + positions[3 * i + 1] = pos.y; + positions[3 * i + 2] = pos.z; + } + } + } + + function setFlightTimes(index) { + var scaling_factor = (flight_point_speed_scaling - flight_point_speed_min_scaling) / (flight_point_speed_max_scaling - flight_point_speed_min_scaling); + var duration = (1 - scaling_factor) * flight_distance[index] * 80000; + + var start_time = Date.now() + Math.random() * 5000; + flight_point_start_time[index] = start_time; + flight_point_end_time[index] = start_time + duration; + } + + function easeOutQuadratic(t, b, c, d) { + if ((t /= d / 2) < 1) return c / 2 * t * t + b; + return -c / 2 * (--t * (t - 2) - 1) + b; + } + + /** + * @author kyle / http://nikai.us/ + */ + + function clear (context) { + 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/ + */ + + function resolutionScale (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); + } + } + }; + + /** + * 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++) { + 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); + } + + //console.timeEnd('transferCoordinate time') + + return data; + }; + + /** + * set data. + */ + DataSet.prototype.set = function (data) { + this.clear(); + this.add(data); + this._trigger('change'); + }; + + /** + * 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) { + + for (var i = 0; i < data.length; i++) { + + var item = data[i]; + + if (data[i].geometry) { + + if (data[i].geometry.type === 'Point') { + var coordinates = data[i].geometry.coordinates; + data[i].geometry._coordinates = transferFn(coordinates); + } + + if (data[i].geometry.type === 'Polygon' || data[i].geometry.type === 'MultiPolygon') { + + var coordinates = data[i].geometry.coordinates; + + if (data[i].geometry.type === 'Polygon') { + + var newCoordinates = getPolygon(coordinates); + data[i].geometry._coordinates = newCoordinates; + } else if (data[i].geometry.type === 'MultiPolygon') { + var newCoordinates = []; + for (var c = 0; c < coordinates.length; c++) { + var polygon = coordinates[c]; + var polygon = getPolygon(polygon); + newCoordinates.push(polygon); + } + + data[i].geometry._coordinates = newCoordinates; + } + } + + if (data[i].geometry.type === 'LineString') { + var coordinates = data[i].geometry.coordinates; + var newCoordinates = []; + for (var j = 0; j < coordinates.length; j++) { + newCoordinates.push(transferFn(coordinates[j])); + } + data[i].geometry._coordinates = newCoordinates; + } + } + } + + 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 = data[0][columnName]; + + for (var i = 1; i < data.length; i++) { + if (data[i][columnName] > max) { + max = data[i][columnName]; + } + } + + 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 = data[0][columnName]; + + for (var i = 1; i < data.length; i++) { + if (data[i][columnName] < min) { + min = data[i][columnName]; + } + } + + return min; + }; + + 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 { + 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]); + } + } + + }; + + 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(); - } -}; + context.restore(); + } + }; -/** - * @author kyle / http://nikai.us/ - */ + /** + * @author kyle / http://nikai.us/ + */ -var utilsColorPalette = { - getImageData: function getImageData(config) { - var gradientConfig = config.gradient || config.defaultGradient; - if (typeof document === 'undefined') { - var Canvas = require('canvas'); - var paletteCanvas = new Canvas(256, 1); - } else { - var paletteCanvas = document.createElement('canvas'); - } - var paletteCtx = paletteCanvas.getContext('2d'); - - paletteCanvas.width = 256; - paletteCanvas.height = 1; + var utilsColorPalette = { + getImageData: function getImageData(config) { + var gradientConfig = config.gradient || config.defaultGradient; + if (typeof document === 'undefined') { + var Canvas = require('canvas'); + var paletteCanvas = new Canvas(256, 1); + } else { + var paletteCanvas = document.createElement('canvas'); + } + var paletteCtx = paletteCanvas.getContext('2d'); + + paletteCanvas.width = 256; + paletteCanvas.height = 1; + + var gradient = paletteCtx.createLinearGradient(0, 0, 256, 1); + for (var key in gradientConfig) { + gradient.addColorStop(parseFloat(key), gradientConfig[key]); + } + + paletteCtx.fillStyle = gradient; + paletteCtx.fillRect(0, 0, 256, 1); + + return paletteCtx.getImageData(0, 0, 256, 1).data; + } + }; + + function createCircle(size) { + + if (typeof document === 'undefined') { + var Canvas = require('canvas'); + var circle = new Canvas(); + } else { + var circle = document.createElement('canvas'); + } + var context = circle.getContext('2d'); + var shadowBlur = size / 2; + var r2 = size + shadowBlur; + var offsetDistance = 10000; + + circle.width = circle.height = r2 * 2; + + 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; + // console.log(max) + var size = options._size; + if (size == undefined) { + size = options.size; + if (size == undefined) { + size = 13; + } + } + + var color = new Intensity({ + gradient: options.gradient, + max: max + }); + + var circle = createCircle(size); + + 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; + } + _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] - circle.width / 2, coordinates[1] - circle.height / 2); + } else if (type === 'LineString') { + pathSimple.draw(context, item, options); + } else if (type === 'Polygon') {} + }); + // console.warn(i, i * max, color.getColor(i * max)) + context.strokeStyle = color.getColor(i * max); + context.stroke(); + } + } + + function draw(context, dataSet, options) { + var strength = options.strength || 0.3; + context.strokeStyle = 'rgba(0,0,0,' + strength + ')'; + + options = options || {}; + + var data = dataSet.get(); + + context.save(); + //console.time('drawGray') + drawGray(context, data, options); + //console.timeEnd('drawGray'); + // return false; + if (!options.absolute) { + //console.time('changeColor'); + var colored = context.getImageData(0, 0, context.canvas.width, context.canvas.height); + colorize(colored.data, utilsColorPalette.getImageData({ + defaultGradient: 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)" + } + }), options); + //console.timeEnd('changeColor'); + context.putImageData(colored, 0, 0); + + context.restore(); + } + } + + var drawHeatmap = { + draw: draw + }; + + var drawGrid = { + draw: function draw(context, dataSet, options) { + + context.save(); + + var data = dataSet.get(); + + var grids = {}; + + 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; + } + grids[gridKey] += ~~(data[i].count || 1); + } + + for (var gridKey in grids) { + gridKey = gridKey.split(","); + + var intensity = new Intensity({ + max: options.max || 100, + gradient: options.gradient + }); + + context.beginPath(); + context.rect(gridKey[0] * size + .5 + offset.x, gridKey[1] * size + .5 + offset.y, size, size); + context.fillStyle = intensity.getColor(grids[gridKey]); + context.fill(); + if (options.showText) { + context.fillStyle = 'white'; + context.fillText(grids[gridKey], gridKey[0] * size + .5 + offset.x + size / 2, gridKey[1] * size + .5 + offset.y + size / 2); + } + if (options.strokeStyle && options.lineWidth) { + context.stroke(); + } + } + + context.restore(); + } + }; + + 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.get(); + + 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; + } + + context.fillStyle = intensity.getColor(count); + context.fill(); + if (options.strokeStyle && options.lineWidth) { + context.stroke(); + } + } + + context.restore(); + } + }; + + /** + * 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; + } + }; + + /** + * Timer + * @author kyle / http://nikai.us/ + */ + + var Timer = function () { + function Timer(callback, options) { + classCallCheck(this, Timer); + + this._call = callback; + this._runing = false; + this.start(); + } + + createClass(Timer, [{ + key: "start", + value: function start() { + this._runing = true; + requestAnimationFrame(this._launch.bind(this)); + } + }, { + key: "stop", + value: function stop() { + this._runing = false; + } + }, { + key: "_launch", + value: function _launch(timestamp) { + if (this._runing) { + this._call && this._call(timestamp); + requestAnimationFrame(this._launch.bind(this)); + } + } + }]); + return Timer; + }(); + + /** + * 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 + (function () { + // In a browser, use window.performance.now if it is available. + 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(); + }; + } + })(); - var gradient = paletteCtx.createLinearGradient(0, 0, 256, 1); - for (var key in gradientConfig) { - gradient.addColorStop(parseFloat(key), gradientConfig[key]); - } - - paletteCtx.fillStyle = gradient; - paletteCtx.fillRect(0, 0, 256, 1); - - return paletteCtx.getImageData(0, 0, 256, 1).data; - } -}; - -/** - * @author kyle / http://nikai.us/ - */ - -function createCircle(size) { - - if (typeof document === 'undefined') { - var Canvas = require('canvas'); - var circle = new Canvas(); - } else { - var circle = document.createElement('canvas'); - } - var context = circle.getContext('2d'); - var shadowBlur = size / 2; - var r2 = size + shadowBlur; - var offsetDistance = 10000; - - circle.width = circle.height = r2 * 2; - - 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; - // console.log(max) - var size = options._size; - if (size == undefined) { - size = options.size; - if (size == undefined) { - size = 13; - } - } - - var color = new Intensity({ - gradient: options.gradient, - max: max - }); - - var circle = createCircle(size); - - 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; - } - _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] - circle.width / 2, coordinates[1] - circle.height / 2); - } else if (type === 'LineString') { - pathSimple.draw(context, item, options); - } else if (type === 'Polygon') {} - }); - // console.warn(i, i * max, color.getColor(i * max)) - context.strokeStyle = color.getColor(i * max); - context.stroke(); - } -} - -function draw(context, dataSet, options) { - var strength = options.strength || 0.3; - context.strokeStyle = 'rgba(0,0,0,' + strength + ')'; - - options = options || {}; - - var data = dataSet.get(); - - context.save(); - console.time('drawGray'); - drawGray(context, data, options); - console.timeEnd('drawGray'); - // return false; - if (!options.absolute) { - console.time('changeColor'); - var colored = context.getImageData(0, 0, context.canvas.width, context.canvas.height); - colorize(colored.data, utilsColorPalette.getImageData({ - defaultGradient: 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)" - } - }), options); - console.timeEnd('changeColor'); - context.putImageData(colored, 0, 0); - - context.restore(); - } -} - -var drawHeatmap = { - draw: draw -}; - -/** - * @author kyle / http://nikai.us/ - */ - -var drawGrid = { - draw: function draw(context, dataSet, options) { - - context.save(); - - var data = dataSet.get(); - - var grids = {}; - - 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; - } - grids[gridKey] += ~~(data[i].count || 1); - } - - for (var gridKey in grids) { - gridKey = gridKey.split(","); - - var intensity = new Intensity({ - max: options.max || 100, - gradient: options.gradient - }); - - context.beginPath(); - context.rect(gridKey[0] * size + .5 + offset.x, gridKey[1] * size + .5 + offset.y, size, size); - context.fillStyle = intensity.getColor(grids[gridKey]); - context.fill(); - if (options.showText) { - context.fillStyle = 'white'; - context.fillText(grids[gridKey], gridKey[0] * size + .5 + offset.x + size / 2, gridKey[1] * size + .5 + offset.y + size / 2); - } - if (options.strokeStyle && options.lineWidth) { - context.stroke(); - } - } - - 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.get(); - - 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; - } - - context.fillStyle = intensity.getColor(count); - context.fill(); - if (options.strokeStyle && options.lineWidth) { - context.stroke(); - } - } - - context.restore(); - } -}; - -/** - * 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; - } -}; - -/** - * Timer - * @author kyle / http://nikai.us/ - */ - -var Timer = function () { - function Timer(callback, options) { - classCallCheck(this, Timer); - - this._call = callback; - this._runing = false; - this.start(); - } - - createClass(Timer, [{ - key: "start", - value: function start() { - this._runing = true; - requestAnimationFrame(this._launch.bind(this)); - } - }, { - key: "stop", - value: function stop() { - this._runing = false; - } - }, { - key: "_launch", - value: function _launch(timestamp) { - if (this._runing) { - this._call && this._call(timestamp); - requestAnimationFrame(this._launch.bind(this)); - } - } - }]); - return Timer; -}(); - -/** - * Abstract handler for animator steps - */ -var AnimatorStepsRange = function AnimatorStepsRange(start, end) { - if (start < 0) throw new Error('start must be a positive number'); - if (start >= end) throw new Error('start must be smaller than end'); - - this.start = start; - this.end = end; -}; - -AnimatorStepsRange.prototype = { - - diff: function diff() { - return this.end - this.start; - }, - - isLast: function isLast(step) { - // round step into an integer, to be able to compare number as expected (also converts bad input to 0) - return (step | 0) === this.end; - } -}; - -function clamp(a, b) { - return function (t) { - return Math.max(Math.min(t, b), a); - }; -} - -function invLinear(a, b) { - var c = clamp(0, 1.0); - return function (t) { - return c((t - a) / (b - a)); - }; -} - -function linear(a, b) { - var c = clamp(a, b); - function _linear(t) { - return c(a * (1.0 - t) + t * b); - } - - _linear.invert = function () { - return invLinear(a, b); - }; - - return _linear; -} - -var global$1 = typeof window === 'undefined' ? {} : window; - -var requestAnimationFrame$1 = global$1.requestAnimationFrame || global$1.mozRequestAnimationFrame || global$1.webkitRequestAnimationFrame || global$1.msRequestAnimationFrame || function (callback) { - return global$1.setTimeout(callback, 1000 / 60); -}; - -var cancelAnimationFrame = global$1.cancelAnimationFrame || global$1.mozCancelAnimationFrame || global$1.webkitCancelAnimationFrame || global$1.msCancelAnimationFrame || function (id) { - clearTimeout(id); -}; - -/** - * options: - * animationDuration in seconds - * animationDelay in seconds - */ -function Animator(callback, options) { - if (!options.steps) { - throw new Error("steps option missing"); - } - this.options = options; - this.running = false; - this._tick = this._tick.bind(this); - this._t0 = +new Date(); - this.callback = callback; - this._time = 0.0; - this.itemsReady = false; - - this.options.animationDelay = 0; - this.options.maxDelta = 0.2; - this.options.loop = options.loop === undefined ? true : options.loop; - - options.stepsRange = options.stepsRange || { - start: 0, - end: 100 - }; - - this.steps(options.stepsRange.start || 0, options.stepsRange.end || 100); - if (options.stepsRange && options.stepsRange.start !== undefined && options.stepsRange.end !== undefined) { - this.stepsRange(options.stepsRange.start, options.stepsRange.end); - } -} - -Animator.prototype = { - - start: function start() { - this.running = true; - requestAnimationFrame$1(this._tick); - this.options.onStart && this.options.onStart(); - if (this.stepsRange().diff() === 1) { - this.running = false; - } - }, - - isRunning: function isRunning() { - return this.running; - }, - - stop: function stop() { - this.pause(); - this.time(this.stepsRange().start); - this.options.onStop && this.options.onStop(); - }, - - // real animation time - time: function time(_) { - if (!arguments.length) return this._time; - this._time = _; - var t = this.range(this.domain(this._time)); - this.callback(t); - }, - - toggle: function toggle() { - if (this.running) { - this.pause(); - } else { - this.start(); - } - }, - - rescale: function rescale() { - this.domainInv = linear(this.options.animationDelay, this.options.animationDelay + this.options.animationDuration); - this.domain = this.domainInv.invert(); - this.range = linear(0, this._defaultStepsRange.end); - this.rangeInv = this.range.invert(); - this.time(this._time); - this.running ? this.start() : this.pause(); - return this; - }, - - duration: function duration(_) { - if (!arguments.length) return this.options.animationDuration; - this.options.animationDuration = _; - if (this.time() > _) { - this.time(0); - } - this.rescale(); - return this; - }, - - steps: function steps(start, end) { - this._defaultStepsRange = new AnimatorStepsRange(start, end); - return this.rescale(); - }, - - // Returns or sets a (custom) steps range - // Setting a steps range must be within the full range - stepsRange: function stepsRange(start, end) { - if (arguments.length === 2) { - if (start < this._defaultStepsRange.start) throw new Error('start must be within default steps range'); - if (end > this._defaultStepsRange.end) throw new Error('end must be within default steps range'); - - this._customStepsRange = new AnimatorStepsRange(start, end); - this.options.onStepsRange && this.options.onStepsRange(); - - // Change current step if it's outside the new custom range - var step = this.step() | 0; // round to an integer - if (step < start || step > end) { - this.step(start); - } - } - return this._customStepsRange || this._defaultStepsRange; - }, - - removeCustomStepsRange: function removeCustomStepsRange() { - this._customStepsRange = undefined; - this.options.onStepsRange && this.options.onStepsRange(); - }, - - step: function step(s) { - if (arguments.length === 0) return this.range(this.domain(this._time)); - this._time = this.domainInv(this.rangeInv(s)); - }, - - pause: function pause() { - this.running = false; - cancelAnimationFrame(this._tick); - this.options.onPause && this.options.onPause(); - }, - - _tick: function _tick() { - var t1 = +new Date(); - var delta = (t1 - this._t0) * 0.001; - // if delta is really big means the tab lost the focus - // at some point, so limit delta change - delta = Math.min(this.options.maxDelta, delta); - this._t0 = t1; - this._time += delta; - - var stepsRange = this.stepsRange(); - if (stepsRange.isLast(this.step())) { - if (!this.options.loop) { - // set time to max time - this.time(this.options.animationDuration); - this.pause(); - } else { - this.step(stepsRange.start); - } - } - if (this.running) { - this.time(this._time); - requestAnimationFrame$1(this._tick); - } - } - -}; - -/** - * @author Mofei - */ - -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 || 'labelPane'; - this.zIndex = this.options.zIndex || 0; - this.mixBlendMode = this.options.mixBlendMode || null; - this._map = options.map; - this._lastDrawTime = null; - this.show(); -} - -var global$2 = typeof window === 'undefined' ? {} : window; - -if (global$2.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 + ";"; - 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$2.devicePixelRatio; - - canvas.width = size.width * devicePixelRatio; - canvas.height = size.height * devicePixelRatio; - canvas.getContext('2d').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; - }; -} - -/** - * @author Mofei Zhu - * This file is to draw text - */ - -var drawText = { - draw: function draw(context, dataSet, options) { - var data = dataSet.get(); - context.fillStyle = 'white'; - context.textAlign = 'center'; - context.textBaseline = 'middle'; - - // set from options - for (var key in options) { - context[key] = options[key]; - } - - var offset = options.offset || { - x: 0, - y: 0 - }; - - var textKey = options.textKey || 'text'; - - for (var i = 0, len = data.length; i < len; i++) { - var coordinates = data[i].geometry._coordinates || data[i].geometry.coordinates; - context.fillText(data[i][textKey], coordinates[0] + offset.x, coordinates[1] + offset.y); - }; - } -}; - -/** - * @author Mofei Zhu - * 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'; - - // 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 icon = data[i].icon; - var coordinates = data[i].geometry._coordinates || data[i].geometry.coordinates; - context.drawImage(icon, coordinates[0] - icon.width / 2, coordinates[1] - icon.height / 2); - } - }; - } -}; - -/** - * @author kyle / http://nikai.us/ - */ - -function Layer(map, dataSet, options) { - if (!(dataSet instanceof DataSet)) { - dataSet = new DataSet(dataSet); - } - - this.dataSet = dataSet; - - var self = this; - var data = null; - options = options || {}; - - self.init(options); - self.argCheck(options); - - self.map = map; - - var canvasLayer = this.canvasLayer = new CanvasLayer({ - map: map, - paneName: options.paneName, - mixBlendMode: options.mixBlendMode, - zIndex: options.zIndex, - update: function update() { - self._canvasUpdate(); - } - }); - - dataSet.on('change', function () { - canvasLayer.draw(); - }); - - if (self.options.methods) { - if (self.options.methods.click) { - map.setDefaultCursor("default"); - map.addEventListener('click', function (e) { - var pixel = e.pixel; - var context = canvasLayer.canvas.getContext('2d'); - var data = dataSet.get(); - for (var i = 0; i < data.length; i++) { - context.beginPath(); - pathSimple.draw(context, data[i], self.options); - if (context.isPointInPath(pixel.x * canvasLayer.devicePixelRatio, pixel.y * canvasLayer.devicePixelRatio)) { - self.options.methods.click(data[i], e); - return; - } - } - }); - } - } -} - -Layer.prototype._canvasUpdate = function (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); //左上角墨卡托坐标 - - //console.time('update') - var context = this.canvasLayer.canvas.getContext("2d"); - - if (self.isEnabledTime()) { - if (time === undefined) { - return; - } - 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); - } - - for (var key in self.options) { - context[key] = self.options[key]; - } - - var dataGetOptions = { - transferCoordinate: function transferCoordinate(coordinate) { - - if (self.options.coordType == 'bd09mc') { - var x = (coordinate[0] - nwMc.x) / zoomUnit; - var y = (nwMc.y - coordinate[1]) / zoomUnit; - return [x, y]; - } - - var pixel = map.pointToPixel(new BMap.Point(coordinate[0], coordinate[1])); - return [pixel.x, pixel.y]; - } - }; - - if (time !== undefined) { - dataGetOptions.filter = function (item) { - var trails = animationOptions.trails || 5; - 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); - - // deal with data based on draw - - // TODO: 部分情况下可以不用循环,比如heatmap - //console.time('setstyle'); - - 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; - } - - if (self.options.draw == 'intensity') { - if (data[i].geometry.type === 'LineString') { - data[i].strokeStyle = self.intensity.getColor(item.count); - } else { - data[i].fillStyle = self.intensity.getColor(item.count); - } - } else if (self.options.draw == 'category') { - data[i].fillStyle = self.category.get(item.count); - } else if (self.options.draw == 'choropleth') { - data[i].fillStyle = self.choropleth.get(item.count); - } - } - } - - //console.timeEnd('setstyle'); - - if (self.options.minZoom && map.getZoom() < self.options.minZoom || self.options.maxZoom && map.getZoom() > self.options.maxZoom) { - return; - } - - //console.time('draw'); - // draw - - if (self.options.unit == 'm' && self.options.size) { - self.options._size = self.options.size / zoomUnit; - } else { - self.options._size = self.options.size; - } - - switch (self.options.draw) { - case 'heatmap': - drawHeatmap.draw(context, new DataSet(data), self.options); - break; - case 'grid': - case 'honeycomb': - /* - if (data.length <= 0) { - break; - } - var minx = data[0].geometry.coordinates[0]; - var maxy = data[0].geometry.coordinates[1]; - for (var i = 1; i < data.length; i++) { - minx = Math.min(data[i].geometry.coordinates[0], minx); - maxy = Math.max(data[i].geometry.coordinates[1], maxy); - } - var nwPixel = map.pointToPixel(new BMap.Point(minx, maxy)); - */ - var nwPixel = map.pointToPixel(new BMap.Point(0, 0)); - self.options.offset = { - x: nwPixel.x, - y: nwPixel.y - }; - if (self.options.draw == 'grid') { - drawGrid.draw(context, new DataSet(data), self.options); - } else { - drawHoneycomb.draw(context, new DataSet(data), self.options); - } - break; - case 'text': - drawText.draw(context, new DataSet(data), self.options); - break; - case 'icon': - drawIcon.draw(context, data, 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, data, self.options); - context.beginPath(); - pathSimple.drawDataSet(context, new DataSet(data), self.options); - context.clip(); - clear(context); - context.restore(); - break; - default: - drawSimple.draw(context, data, self.options); - } - //console.timeEnd('draw'); - - //console.timeEnd('update') - self.options.updateCallback && self.options.updateCallback(time); -}; - -Layer.prototype.isEnabledTime = function () { - - var animationOptions = this.options.animation; - - var flag = animationOptions && !(animationOptions.enabled === false); - - return flag; -}; - -Layer.prototype.argCheck = function (options) { - if (options.draw == 'heatmap') { - if (options.strokeStyle) { - console.warn('[heatmap] options.strokeStyle is discard, pleause use options.strength [eg: options.strength = 0.1]'); - } - } -}; - -Layer.prototype.init = function (options) { - var self = this; - - self.options = options; - - self.intensity = new Intensity({ - maxSize: self.options.maxSize, - 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.zIndex) { - this.canvasLayer && this.canvasLayer.setZIndex(self.options.zIndex); - } - - 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); - } - - var animationOptions = self.options.animation; - - if (self.options.draw == 'time' || self.isEnabledTime()) { - if (!self.animator) { - self.animator = new Animator(function (time) { - self._canvasUpdate(time); - }, { - steps: animationOptions.steps || 100, - stepsRange: animationOptions.stepsRange || 100, - animationDuration: animationOptions.duration || 10 - }); - - map.addEventListener('movestart', function () { - if (self.isEnabledTime() && self.animator) { - self.animator.pause(); - } - }); - - map.addEventListener('moveend', function () { - if (self.isEnabledTime() && self.animator) { - self.animator.start(); - } - }); - } - self.animator.start(); - } else { - self.animator && self.animator.pause(); - } -}; - -Layer.prototype.show = function () { - this.map.addOverlay(this.canvasLayer); -}; - -Layer.prototype.hide = function () { - this.map.removeOverlay(this.canvasLayer); -}; - -/** - * obj.options - */ -Layer.prototype.update = function (obj) { - var self = this; - var _options = obj.options; - var options = self.options; - for (var i in _options) { - options[i] = _options[i]; - } - self.init(options); - self.canvasLayer.draw(); -}; - -Layer.prototype.setOptions = function (options) { - var self = this; - self.init(options); - self.canvasLayer.draw(); -}; - -Layer.prototype.set = function (obj) { - var conf = { - 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' - }; - var self = this; - var ctx = self.canvasLayer.canvas.getContext("2d"); - for (var i in conf) { - ctx[i] = conf[i]; - } - self.init(obj.options); - self.canvasLayer.draw(); -}; - -/** - * 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$1(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$1.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$3 = typeof window === 'undefined' ? {} : window; - -if (global$3.google && global$3.google.maps) { - - CanvasLayer$1.prototype = new google.maps.OverlayView(); - - /** - * The default MapPane to contain the canvas. - * @type {string} - * @const - * @private - */ - CanvasLayer$1.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$1.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$1.prototype.requestAnimFrame_ = global$3.requestAnimationFrame || global$3.webkitRequestAnimationFrame || global$3.mozRequestAnimationFrame || global$3.oRequestAnimationFrame || global$3.msRequestAnimationFrame || function (callback) { - return global$3.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$1.prototype.cancelAnimFrame_ = global$3.cancelAnimationFrame || global$3.webkitCancelAnimationFrame || global$3.mozCancelAnimationFrame || global$3.oCancelAnimationFrame || global$3.msCancelAnimationFrame || function (requestId) {}; - - /** - * Sets any options provided. See CanvasLayerOptions for more information. - * @param {CanvasLayerOptions} options The options to set. - */ - CanvasLayer$1.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$1.prototype.setAnimate = function (animate) { - this.isAnimated_ = !!animate; - - if (this.isAnimated_) { - this.scheduleUpdate(); - } - }; - - /** - * @return {boolean} Whether the canvas is animated. - */ - CanvasLayer$1.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$1.prototype.setPaneName = function (paneName) { - this.paneName_ = paneName; - - this.setPane_(); - }; - - /** - * @return {string} The name of the current container pane. - */ - CanvasLayer$1.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$1.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$1.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$1.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$1.prototype.setUpdateHandler = function (opt_updateHandler) { - this.updateHandler_ = opt_updateHandler; - }; - - /** - * @inheritDoc - */ - CanvasLayer$1.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$1.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$3, this.requestAnimationFrameId_); - this.requestAnimationFrameId_ = null; - } - }; - - /** - * The internal callback for resize events that resizes the canvas to keep the - * map properly covered. - * @private - */ - CanvasLayer$1.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$1.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$1.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$1.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$1.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$1.prototype.getTopLeft = function () { - return this.topLeft_; - }; - - /** - * Schedule a requestAnimationFrame callback to updateHandler. If one is - * already scheduled, there is no effect. - */ - CanvasLayer$1.prototype.scheduleUpdate = function () { - if (this.isAdded_ && !this.requestAnimationFrameId_) { - this.requestAnimationFrameId_ = this.requestAnimFrame_.call(global$3, this.requestUpdateFunction_); - } - }; -} - -/** - * @author kyle / http://nikai.us/ - */ - -function Layer$1(map, dataSet, options) { - var intensity = new Intensity({ - maxSize: options.maxSize, - gradient: options.gradient, - max: options.max - }); - - var category = new Category(options.splitList); - - var choropleth = new Choropleth(options.splitList); - - var resolutionScale = window.devicePixelRatio || 1; - - // initialize the canvasLayer - var canvasLayerOptions = { - map: map, - animate: false, - updateHandler: update, - resolutionScale: resolutionScale - }; - - var canvasLayer = new CanvasLayer$1(canvasLayerOptions); - - function update() { - - var context = canvasLayer.canvas.getContext('2d'); - - clear(context); - - for (var key in options) { - context[key] = options[key]; - } - - var pointCount = 0; - var lineCount = 0; - var polygonCount = 0; - - /* We need to scale and translate the map for current view. - * see https://developers.google.com/maps/documentation/javascript/maptypes#MapCoordinates - */ - var mapProjection = map.getProjection(); - - // scale is just 2^zoom - // If canvasLayer is scaled (with resolutionScale), we need to scale by - // the same amount to account for the larger canvas. - var scale = Math.pow(2, map.zoom) * resolutionScale; - - var offset = mapProjection.fromLatLngToPoint(canvasLayer.getTopLeft()); - - var data = dataSet.get({ - 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]; - } - }); - - for (var i = 0; i < data.length; i++) { - var item = data[i]; - if (options.draw == 'bubble') { - data[i].size = intensity.getSize(item.count); - } else if (options.draw == 'intensity') { - if (data[i].geometry.type === 'LineString') { - data[i].strokeStyle = intensity.getColor(item.count); - } else { - data[i].fillStyle = intensity.getColor(item.count); - } - } else if (options.draw == 'category') { - data[i].fillStyle = category.get(item.count); - } else if (options.draw == 'choropleth') { - data[i].fillStyle = choropleth.get(item.count); - } - } - - var maxCount = Math.max(Math.max(pointCount, lineCount), polygonCount); - - if (options.draw == 'heatmap') { - drawHeatmap.draw(context, new DataSet(data), options); - } else if (options.draw == 'grid' || options.draw == 'honeycomb') { - var data1 = dataSet.get(); - var minx = data1[0].geometry.coordinates[0]; - var maxy = data1[0].geometry.coordinates[1]; - for (var i = 1; i < data1.length; i++) { - if (data1[i].geometry.coordinates[0] < minx) { - minx = data1[i].geometry.coordinates[0]; - } - if (data1[i].geometry.coordinates[1] > maxy) { - maxy = data1[i].geometry.coordinates[1]; - } - } - - var latLng = new google.maps.LatLng(minx, maxy); - var worldPoint = mapProjection.fromLatLngToPoint(latLng); - - options.offset = { - x: (worldPoint.x - offset.x) * scale, - y: (worldPoint.y - offset.y) * scale - }; - if (options.draw == 'grid') { - drawGrid.draw(context, new DataSet(data), options); - } else { - drawHoneycomb.draw(context, new DataSet(data), options); - } - } else { - console.log('hehe'); - drawSimple.draw(context, new DataSet(data), options); - } - } -} - -/** - * @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.x = X; -exports.X = X; -exports.Flate = Flate; -exports.Earth = Earth; -exports.canvasClear = clear; -exports.canvasResolutionScale = resolutionScale; -exports.canvasDrawSimple = drawSimple; -exports.canvasDrawHeatmap = drawHeatmap; -exports.canvasDrawGrid = drawGrid; -exports.canvasDrawHoneycomb = drawHoneycomb; -exports.utilCityCenter = cityCenter; -exports.utilCurve = curve; -exports.utilForceEdgeBundling = ForceEdgeBundling; -exports.utilDataRangeIntensity = Intensity; -exports.utilDataRangeCategory = Category; -exports.utilDataRangeChoropleth = Choropleth; -exports.Timer = Timer; -exports.Animator = Animator; -exports.Map = MapHelper; -exports.baiduMapCanvasLayer = CanvasLayer; -exports.baiduMapLayer = Layer; -exports.googleMapCanvasLayer = CanvasLayer$1; -exports.googleMapLayer = Layer$1; -exports.DataSet = DataSet; -exports.geojson = geojson; -exports.csv = csv; - -Object.defineProperty(exports, '__esModule', { value: true }); - -}))); \ No newline at end of file + TWEEN.Tween = function (object) { + + var _object = object; + var _valuesStart = {}; + var _valuesEnd = {}; + var _valuesStartRepeat = {}; + var _duration = 1000; + var _repeat = 0; + 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; + + // Set all starting values present on the target object + for (var field in object) { + _valuesStart[field] = parseFloat(object[field], 10); + } + + this.to = function (properties, duration) { + + if (duration !== undefined) { + _duration = duration; + } + + _valuesEnd = properties; + + 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 (_valuesStart[property] === undefined) { + continue; + } + + _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); + } + + this.stopChainedTweens(); + 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.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); + } + + _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, 10); + } else { + end = parseFloat(end, 10); + } + } + + // 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], 10); + } + + if (_yoyo) { + var tmp = _valuesStartRepeat[property]; + + _valuesStartRepeat[property] = _valuesEnd[property]; + _valuesEnd[property] = tmp; + } + + _valuesStart[property] = _valuesStartRepeat[property]; + } + + if (_yoyo) { + _reversed = !_reversed; + } + + _startTime = time + _delayTime; + + return true; + } else { + + if (_onCompleteCallback !== null) { + _onCompleteCallback.call(_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; + } + + } + + }; + + /** + * Abstract handler for animator steps + */ + + var global$1 = typeof window === 'undefined' ? {} : window; + + var requestAnimationFrame$1 = global$1.requestAnimationFrame || global$1.mozRequestAnimationFrame || global$1.webkitRequestAnimationFrame || global$1.msRequestAnimationFrame || function (callback) { + return global$1.setTimeout(callback, 1000 / 60); + }; + + var cancelAnimationFrame = global$1.cancelAnimationFrame || global$1.mozCancelAnimationFrame || global$1.webkitCancelAnimationFrame || global$1.msCancelAnimationFrame || function (id) { + clearTimeout(id); + }; + + /** + * options: + * duration in seconds + * delay in seconds + */ + function Animator(callback, options) { + + this.running = false; + this.callback = callback; + + this.setOptions(options); + + this._tick = this._tick.bind(this); + } + + Animator.prototype = { + + setOptions: function setOptions(options) { + this.options = options; + options.stepsRange = options.stepsRange || { + start: 0, + end: 100 + }; + + this.duration = options.duration || 10; // 单位秒 + + this.stepsRange = options.stepsRange; + this._add = (this.stepsRange.end - this.stepsRange.start) / (this.duration * 60); + this._time = this.stepsRange.start; + }, + + start: function start() { + + this.running = true; + requestAnimationFrame$1(this._tick); + this.options.onStart && this.options.onStart(); + }, + + _tick: function _tick() { + this._time += this._add; + if (this._time > this.stepsRange.end) { + this._time = this.stepsRange.start; + } + this.callback && this.callback(this._time); + if (this.running) { + requestAnimationFrame$1(this._tick); + } + }, + + isRunning: function isRunning() { + return this.running; + }, + + stop: function stop() { + this.pause(); + this._time = this.stepsRange.start; + this.options.onStop && this.options.onStop(); + }, + + toggle: function toggle() { + if (this.running) { + this.pause(); + } else { + this.start(); + } + }, + + pause: function pause() { + this.running = false; + cancelAnimationFrame(this._tick); + this.options.onPause && this.options.onPause(); + } + + }; + + /** + * @author Mofei + */ + + 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 || 'labelPane'; + this.zIndex = this.options.zIndex || 0; + this.mixBlendMode = this.options.mixBlendMode || null; + this._map = options.map; + this._lastDrawTime = null; + this.show(); + } + + var global$2 = typeof window === 'undefined' ? {} : window; + + if (global$2.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 + ";"; + 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$2.devicePixelRatio; + + canvas.width = size.width * devicePixelRatio; + canvas.height = size.height * devicePixelRatio; + canvas.getContext('2d').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; + }; + } + + var drawText = { + draw: function draw(context, dataSet, options) { + var data = dataSet.get(); + context.fillStyle = 'white'; + context.textAlign = 'center'; + context.textBaseline = 'middle'; + + // set from options + for (var key in options) { + context[key] = options[key]; + } + + var offset = options.offset || { + x: 0, + y: 0 + }; + + var textKey = options.textKey || 'text'; + + for (var i = 0, len = data.length; i < len; i++) { + var coordinates = data[i].geometry._coordinates || data[i].geometry.coordinates; + context.fillText(data[i][textKey], coordinates[0] + offset.x, coordinates[1] + offset.y); + }; + } + }; + + 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'; + + // 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 icon = data[i].icon; + var coordinates = data[i].geometry._coordinates || data[i].geometry.coordinates; + context.drawImage(icon, coordinates[0] - icon.width / 2, coordinates[1] - icon.height / 2); + } + }; + } + }; + + if (typeof window !== 'undefined') { + requestAnimationFrame(animate); + } + + function animate(time) { + requestAnimationFrame(animate); + TWEEN.update(time); + } + + function Layer(map, dataSet, options) { + if (!(dataSet instanceof DataSet)) { + dataSet = new DataSet(dataSet); + } + + this.dataSet = dataSet; + + var self = this; + var data = null; + options = options || {}; + + self.map = map; + + self.init(options); + self.argCheck(options); + + var canvasLayer = this.canvasLayer = new CanvasLayer({ + map: map, + paneName: options.paneName, + mixBlendMode: options.mixBlendMode, + zIndex: options.zIndex, + update: function update() { + self._canvasUpdate(); + } + }); + + dataSet.on('change', function () { + canvasLayer.draw(); + }); + + if (self.options.methods) { + if (self.options.methods.click) { + map.setDefaultCursor("default"); + map.addEventListener('click', function (e) { + var pixel = e.pixel; + var context = canvasLayer.canvas.getContext('2d'); + var data = dataSet.get(); + for (var i = 0; i < data.length; i++) { + context.beginPath(); + pathSimple.draw(context, data[i], self.options); + if (context.isPointInPath(pixel.x * canvasLayer.devicePixelRatio, pixel.y * canvasLayer.devicePixelRatio)) { + self.options.methods.click(data[i], e); + return; + } + } + }); + } + } + } + + Layer.prototype._canvasUpdate = function (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); //左上角墨卡托坐标 + + //console.time('update') + var context = this.canvasLayer.canvas.getContext("2d"); + + if (self.isEnabledTime()) { + if (time === undefined) { + clear(context); + return; + } + 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); + } + + for (var key in self.options) { + context[key] = self.options[key]; + } + + var dataGetOptions = { + transferCoordinate: function transferCoordinate(coordinate) { + + if (self.options.coordType == 'bd09mc') { + var x = (coordinate[0] - nwMc.x) / zoomUnit; + var y = (nwMc.y - coordinate[1]) / zoomUnit; + return [x, y]; + } + + var pixel = map.pointToPixel(new BMap.Point(coordinate[0], coordinate[1])); + return [pixel.x, pixel.y]; + } + }; + + if (time !== undefined) { + dataGetOptions.filter = function (item) { + var trails = animationOptions.trails || 5; + 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); + + // deal with data based on draw + + // TODO: 部分情况下可以不用循环,比如heatmap + //console.time('setstyle'); + + 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; + } + + if (self.options.draw == 'intensity') { + if (data[i].geometry.type === 'LineString') { + data[i].strokeStyle = self.intensity.getColor(item.count); + } else { + data[i].fillStyle = self.intensity.getColor(item.count); + } + } else if (self.options.draw == 'category') { + data[i].fillStyle = self.category.get(item.count); + } else if (self.options.draw == 'choropleth') { + data[i].fillStyle = self.choropleth.get(item.count); + } + } + } + + //console.timeEnd('setstyle'); + + if (self.options.minZoom && map.getZoom() < self.options.minZoom || self.options.maxZoom && map.getZoom() > self.options.maxZoom) { + return; + } + + //console.time('draw'); + // draw + + if (self.options.unit == 'm' && self.options.size) { + self.options._size = self.options.size / zoomUnit; + } else { + self.options._size = self.options.size; + } + + switch (self.options.draw) { + case 'heatmap': + drawHeatmap.draw(context, new DataSet(data), self.options); + break; + case 'grid': + case 'honeycomb': + /* + if (data.length <= 0) { + break; + } + var minx = data[0].geometry.coordinates[0]; + var maxy = data[0].geometry.coordinates[1]; + for (var i = 1; i < data.length; i++) { + minx = Math.min(data[i].geometry.coordinates[0], minx); + maxy = Math.max(data[i].geometry.coordinates[1], maxy); + } + var nwPixel = map.pointToPixel(new BMap.Point(minx, maxy)); + */ + var nwPixel = map.pointToPixel(new BMap.Point(0, 0)); + self.options.offset = { + x: nwPixel.x, + y: nwPixel.y + }; + if (self.options.draw == 'grid') { + drawGrid.draw(context, new DataSet(data), self.options); + } else { + drawHoneycomb.draw(context, new DataSet(data), self.options); + } + break; + case 'text': + drawText.draw(context, new DataSet(data), self.options); + break; + case 'icon': + drawIcon.draw(context, data, 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, data, self.options); + context.beginPath(); + pathSimple.drawDataSet(context, new DataSet(data), self.options); + context.clip(); + clear(context); + context.restore(); + break; + default: + drawSimple.draw(context, data, self.options); + } + //console.timeEnd('draw'); + + //console.timeEnd('update') + self.options.updateCallback && self.options.updateCallback(time); + }; + + Layer.prototype.isEnabledTime = function () { + + var animationOptions = this.options.animation; + + var flag = animationOptions && !(animationOptions.enabled === false); + + return flag; + }; + + Layer.prototype.argCheck = function (options) { + if (options.draw == 'heatmap') { + if (options.strokeStyle) { + console.warn('[heatmap] options.strokeStyle is discard, pleause use options.strength [eg: options.strength = 0.1]'); + } + } + }; + + Layer.prototype.init = function (options) { + var self = this; + + self.options = options; + + self.intensity = new Intensity({ + maxSize: self.options.maxSize, + 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.zIndex) { + this.canvasLayer && this.canvasLayer.setZIndex(self.options.zIndex); + } + + 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); + } + + var animationOptions = self.options.animation; + + if (self.options.draw == 'time' || self.isEnabledTime()) { + //if (!self.animator) { + if (!animationOptions.stepsRange) { + animationOptions.stepsRange = { + start: this.dataSet.getMin('time') || 0, + end: this.dataSet.getMax('time') || 0 + }; + } + + var steps = { step: animationOptions.stepsRange.start }; + self.animator = new TWEEN.Tween(steps).onUpdate(function () { + self._canvasUpdate(this.step); + }).repeat(Infinity); + + self.map.addEventListener('movestart', function () { + if (self.isEnabledTime() && self.animator) { + self.animator.stop(); + } + }); + + self.map.addEventListener('moveend', function () { + if (self.isEnabledTime() && self.animator) { + self.animator.start(); + } + }); + //} + + var duration = animationOptions.duration * 1000 || 5000; + + self.animator.to({ step: animationOptions.stepsRange.end }, duration); + self.animator.start(); + } else { + self.animator && self.animator.stop(); + } + }; + + Layer.prototype.show = function () { + this.map.addOverlay(this.canvasLayer); + }; + + Layer.prototype.hide = function () { + this.map.removeOverlay(this.canvasLayer); + }; + + /** + * obj.options + */ + Layer.prototype.update = function (obj) { + var self = this; + var _options = obj.options; + var options = self.options; + for (var i in _options) { + options[i] = _options[i]; + } + self.init(options); + self.canvasLayer.draw(); + }; + + Layer.prototype.setOptions = function (options) { + var self = this; + self.init(options); + self.canvasLayer.draw(); + }; + + Layer.prototype.set = function (obj) { + var conf = { + 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' + }; + var self = this; + var ctx = self.canvasLayer.canvas.getContext("2d"); + for (var i in conf) { + ctx[i] = conf[i]; + } + self.init(obj.options); + self.canvasLayer.draw(); + }; + + /** + * 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$1(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$1.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$3 = typeof window === 'undefined' ? {} : window; + + if (global$3.google && global$3.google.maps) { + + CanvasLayer$1.prototype = new google.maps.OverlayView(); + + /** + * The default MapPane to contain the canvas. + * @type {string} + * @const + * @private + */ + CanvasLayer$1.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$1.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$1.prototype.requestAnimFrame_ = global$3.requestAnimationFrame || global$3.webkitRequestAnimationFrame || global$3.mozRequestAnimationFrame || global$3.oRequestAnimationFrame || global$3.msRequestAnimationFrame || function (callback) { + return global$3.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$1.prototype.cancelAnimFrame_ = global$3.cancelAnimationFrame || global$3.webkitCancelAnimationFrame || global$3.mozCancelAnimationFrame || global$3.oCancelAnimationFrame || global$3.msCancelAnimationFrame || function (requestId) {}; + + /** + * Sets any options provided. See CanvasLayerOptions for more information. + * @param {CanvasLayerOptions} options The options to set. + */ + CanvasLayer$1.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$1.prototype.setAnimate = function (animate) { + this.isAnimated_ = !!animate; + + if (this.isAnimated_) { + this.scheduleUpdate(); + } + }; + + /** + * @return {boolean} Whether the canvas is animated. + */ + CanvasLayer$1.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$1.prototype.setPaneName = function (paneName) { + this.paneName_ = paneName; + + this.setPane_(); + }; + + /** + * @return {string} The name of the current container pane. + */ + CanvasLayer$1.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$1.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$1.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$1.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$1.prototype.setUpdateHandler = function (opt_updateHandler) { + this.updateHandler_ = opt_updateHandler; + }; + + /** + * @inheritDoc + */ + CanvasLayer$1.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$1.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$3, this.requestAnimationFrameId_); + this.requestAnimationFrameId_ = null; + } + }; + + /** + * The internal callback for resize events that resizes the canvas to keep the + * map properly covered. + * @private + */ + CanvasLayer$1.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$1.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$1.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$1.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$1.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$1.prototype.getTopLeft = function () { + return this.topLeft_; + }; + + /** + * Schedule a requestAnimationFrame callback to updateHandler. If one is + * already scheduled, there is no effect. + */ + CanvasLayer$1.prototype.scheduleUpdate = function () { + if (this.isAdded_ && !this.requestAnimationFrameId_) { + this.requestAnimationFrameId_ = this.requestAnimFrame_.call(global$3, this.requestUpdateFunction_); + } + }; + } + + function Layer$1(map, dataSet, options) { + var intensity = new Intensity({ + maxSize: options.maxSize, + gradient: options.gradient, + max: options.max + }); + + var category = new Category(options.splitList); + + var choropleth = new Choropleth(options.splitList); + + var resolutionScale = window.devicePixelRatio || 1; + + // initialize the canvasLayer + var canvasLayerOptions = { + map: map, + animate: false, + updateHandler: update, + resolutionScale: resolutionScale + }; + + var canvasLayer = new CanvasLayer$1(canvasLayerOptions); + + function update() { + + var context = canvasLayer.canvas.getContext('2d'); + + clear(context); + + for (var key in options) { + context[key] = options[key]; + } + + var pointCount = 0; + var lineCount = 0; + var polygonCount = 0; + + /* We need to scale and translate the map for current view. + * see https://developers.google.com/maps/documentation/javascript/maptypes#MapCoordinates + */ + var mapProjection = map.getProjection(); + + // scale is just 2^zoom + // If canvasLayer is scaled (with resolutionScale), we need to scale by + // the same amount to account for the larger canvas. + var scale = Math.pow(2, map.zoom) * resolutionScale; + + var offset = mapProjection.fromLatLngToPoint(canvasLayer.getTopLeft()); + + var data = dataSet.get({ + 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]; + } + }); + + for (var i = 0; i < data.length; i++) { + var item = data[i]; + if (options.draw == 'bubble') { + data[i].size = intensity.getSize(item.count); + } else if (options.draw == 'intensity') { + if (data[i].geometry.type === 'LineString') { + data[i].strokeStyle = intensity.getColor(item.count); + } else { + data[i].fillStyle = intensity.getColor(item.count); + } + } else if (options.draw == 'category') { + data[i].fillStyle = category.get(item.count); + } else if (options.draw == 'choropleth') { + data[i].fillStyle = choropleth.get(item.count); + } + } + + var maxCount = Math.max(Math.max(pointCount, lineCount), polygonCount); + + if (options.draw == 'heatmap') { + drawHeatmap.draw(context, new DataSet(data), options); + } else if (options.draw == 'grid' || options.draw == 'honeycomb') { + var data1 = dataSet.get(); + var minx = data1[0].geometry.coordinates[0]; + var maxy = data1[0].geometry.coordinates[1]; + for (var i = 1; i < data1.length; i++) { + if (data1[i].geometry.coordinates[0] < minx) { + minx = data1[i].geometry.coordinates[0]; + } + if (data1[i].geometry.coordinates[1] > maxy) { + maxy = data1[i].geometry.coordinates[1]; + } + } + + var latLng = new google.maps.LatLng(minx, maxy); + var worldPoint = mapProjection.fromLatLngToPoint(latLng); + + options.offset = { + x: (worldPoint.x - offset.x) * scale, + y: (worldPoint.y - offset.y) * scale + }; + if (options.draw == 'grid') { + drawGrid.draw(context, new DataSet(data), options); + } else { + drawHoneycomb.draw(context, new DataSet(data), options); + } + } else { + console.log('hehe'); + drawSimple.draw(context, new DataSet(data), options); + } + } + } + + 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); + } + }; + + 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.x = X; + exports.X = X; + exports.Flate = Flate; + exports.Earth = Earth; + exports.canvasClear = clear; + exports.canvasResolutionScale = resolutionScale; + exports.canvasDrawSimple = drawSimple; + exports.canvasDrawHeatmap = drawHeatmap; + exports.canvasDrawGrid = drawGrid; + exports.canvasDrawHoneycomb = drawHoneycomb; + exports.utilCityCenter = cityCenter; + exports.utilCurve = curve; + exports.utilForceEdgeBundling = ForceEdgeBundling; + exports.utilDataRangeIntensity = Intensity; + exports.utilDataRangeCategory = Category; + exports.utilDataRangeChoropleth = Choropleth; + exports.Timer = Timer; + exports.Animator = Animator; + exports.Map = MapHelper; + exports.baiduMapCanvasLayer = CanvasLayer; + exports.baiduMapLayer = Layer; + exports.googleMapCanvasLayer = CanvasLayer$1; + exports.googleMapLayer = Layer$1; + exports.DataSet = DataSet; + exports.geojson = geojson; + exports.csv = csv; + +})); \ No newline at end of file diff --git a/build/mapv.min.js b/build/mapv.min.js index 8f039171..9edbcd92 100644 --- a/build/mapv.min.js +++ b/build/mapv.min.js @@ -1,2 +1,2 @@ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t.mapv=t.mapv||{})}(this,function(t){"use strict";function e(){var t=document.createElement("canvas"),e=t.getContext("2d"),n=e.createLinearGradient(0,0,256,0);n.addColorStop(1,"#F00"),n.addColorStop(.6,"#FFFC00"),n.addColorStop(.3,"#00FF1D"),n.addColorStop(0,"#000BFF"),e.fillStyle=n,e.fillRect(0,0,256,1);var i=e.getImageData(0,0,256,1);return i.data}function n(t){t=t||{},this.gradient=t.gradient||{.25:"rgba(0, 0, 255, 1)",.55:"rgba(0, 255, 0, 1)",.85:"rgba(255, 255, 0, 1)",1:"rgba(255, 0, 0, 1)"},this.maxSize=t.maxSize||35,this.max=t.max||100,this.initPalette()}function i(t){function e(t){requestAnimationFrame(e),n.render()}this.container=t,this.init();var n=this;this.group=new THREE.Group,this.center=[105,33],requestAnimationFrame(e)}function a(t){function e(t){requestAnimationFrame(e),n.render()}this.container=t,this.init();var n=this;requestAnimationFrame(e)}function r(t){for(var e=V;eQ[t]){var e=u(Date.now()-Q[t],0,1,Y[t]-Q[t]);(e<0||K)&&(e=0,l(t));var n=Z[t].getPoint(e);O[3*t+0]=n.x,O[3*t+1]=n.y,O[3*t+2]=n.z}}}function l(t){var e=($-tt)/(et-tt),n=(1-e)*X[t]*8e4,i=Date.now()+5e3*Math.random();Q[t]=i,Y[t]=i+n}function u(t,e,n,i){return(t/=i/2)<1?n/2*t*t+e:-n/2*(--t*(t-2)-1)+e}function p(t){t.clearRect(0,0,t.canvas.width,t.canvas.height)}function d(t){var e=window.devicePixelRatio;t.canvas.width=t.canvas.width*e,t.canvas.height=t.canvas.height*e,t.canvas.style.width=t.canvas.width/e+"px",t.canvas.style.height=t.canvas.height/e+"px",t.scale(e,e)}function f(){this._subscribers={}}function v(t,e){this._options=e||{},this._data=[],t&&this.add(t)}function m(t){if("undefined"==typeof document)var e=require("canvas"),n=new e;else var n=document.createElement("canvas");var i=n.getContext("2d"),a=t/2,r=t+a,o=1e4;return n.width=n.height=2*r,i.shadowBlur=a,i.shadowColor="black",i.shadowOffsetX=i.shadowOffsetY=o,i.beginPath(),i.arc(r-o,r-o,t,0,2*Math.PI,!0),i.closePath(),i.fill(),n}function y(t,e,n){for(var i,a=n.maxOpacity||.8,r=3,o=t.length;ra&&(t[r]=256*a),t[r-3]=e[i],t[r-2]=e[i+1],t[r-1]=e[i+2]}function w(t,e,i){var a=i.max||100,r=i._size;void 0==r&&(r=i.size,void 0==r&&(r=13));var o=new n({gradient:i.gradient,max:a}),s=m(r),g=e,h={};g.forEach(function(t,e){var n=void 0===t.count?1:t.count,i=Math.min(1,n/a).toFixed(2);h[i]=h[i]||[],h[i].push(t)});for(var c in h)if(!isNaN(c)){var l=h[c];t.beginPath(),i.withoutAlpha||(t.globalAlpha=c),l.forEach(function(e,n){if(e.geometry){var r=e.geometry._coordinates||e.geometry.coordinates,o=e.geometry.type;if("Point"===o){var g=void 0===e.count?1:e.count;t.globalAlpha=g/a,t.drawImage(s,r[0]-s.width/2,r[1]-s.height/2)}else"LineString"===o&&nt.draw(t,e,i)}}),t.strokeStyle=o.getColor(c*a),t.stroke()}}function x(t,e,n){var i=n.strength||.3;t.strokeStyle="rgba(0,0,0,"+i+")",n=n||{};var a=e.get();if(t.save(),console.time("drawGray"),w(t,a,n),console.timeEnd("drawGray"),!n.absolute){console.time("changeColor");var r=t.getImageData(0,0,t.canvas.width,t.canvas.height);y(r.data,at.getImageData({defaultGradient:n.gradient||{.25:"rgba(0, 0, 255, 1)",.55:"rgba(0, 255, 0, 1)",.85:"rgba(255, 255, 0, 1)",1:"rgba(255, 0, 0, 1)"}}),n),console.timeEnd("changeColor"),t.putImageData(r,0,0),t.restore()}}function E(t,e,n){var i=60*n+30,a=Math.PI/180*i;return[t.x+e*Math.cos(a),t.y+e*Math.sin(a)]}function _(t){var e=t.split("|");return e[0]=e[0].split(","),{lng:parseFloat(e[0][0]),lat:parseFloat(e[0][1])}}function M(t){for(var e=[],n=0;n0&&(e=e.concat(i))}return e}function b(t,e){if(!t||!e)return null;var n,i,a,r,o,s,g,h=function(t){return 1-2*t+t*t},c=function(t){return 2*t-2*t*t},l=function(t){return t*t},u=[],p=40,d=0,f=0;if("undefined"==typeof e)return void("undefined"!=typeof u&&(u=[]));var v=parseFloat(t.lat),m=parseFloat(e.lat),y=parseFloat(t.lng),w=parseFloat(e.lng);for(w>y&&parseFloat(w-y)>180&&y<0&&(y=parseFloat(360+y)),y>w&&parseFloat(y-w)>180&&w<0&&(w=parseFloat(360+w)),s=0,g=0,m==v?(n=0,i=y-w):w==y?(n=Math.PI/2,i=v-m):(n=Math.atan((m-v)/(w-y)),i=(m-v)/Math.sin(n)),0==g&&(g=n+Math.PI/5),a=i/2,o=a*Math.cos(g)+y,r=a*Math.sin(g)+v,d=0;di-e&&t.timea.options.maxZoom)){switch("m"==a.options.unit&&a.options.size?a.options._size=a.options.size/o:a.options._size=a.options.size,a.options.draw){case"heatmap":rt.draw(u,new v(r),a.options);break;case"grid":case"honeycomb":var x=t.pointToPixel(new BMap.Point(0,0));a.options.offset={x:x.x,y:x.y},"grid"==a.options.draw?ot.draw(u,new v(r),a.options):st.draw(u,new v(r),a.options);break;case"text":wt.draw(u,new v(r),a.options);break;case"icon":xt.draw(u,r,a.options);break;case"clip":u.save(),u.fillStyle=n.fillStyle||"rgba(0, 0, 0, 0.5)",u.fillRect(0,0,u.canvas.width,u.canvas.height),it.draw(u,r,a.options),u.beginPath(),nt.drawDataSet(u,new v(r),a.options),u.clip(),p(u),u.restore();break;default:it.draw(u,r,a.options)}n.updateCallback&&n.updateCallback(i)}}e instanceof v||(e=new v(e)),this.dataSet=e;var a=this,r=null;n=n||{},a.init(n),a.argCheck(n),a.map=t;var o=this.canvasLayer=new z({map:t,paneName:n.paneName,mixBlendMode:n.mixBlendMode,zIndex:n.zIndex,update:i});e.on("change",function(){o.draw()});var s=a.options.animation,g=s&&!(s.enabled===!1);if("time"==a.options.draw||g){var h=new L(function(t){i.call(o,t)},{steps:s.steps||100,stepsRange:s.stepsRange||100,animationDuration:s.duration||10});h.start(),t.addEventListener("movestart",function(){h.pause()}),t.addEventListener("moveend",function(){h.start()})}a.options.methods&&a.options.methods.click&&(t.setDefaultCursor("default"),t.addEventListener("click",function(t){for(var n=t.pixel,i=o.canvas.getContext("2d"),r=e.get(),s=0;sM&&(M=E[w].geometry.coordinates[1]);var b=new google.maps.LatLng(_,M),R=d.fromLatLngToPoint(b);i.offset={x:(R.x-m.x)*f,y:(R.y-m.y)*f},"grid"==i.draw?ot.draw(n,new v(y),i):st.draw(n,new v(y),i)}else console.log("hehe"),it.draw(n,new v(y),i)}var r=new n({maxSize:i.maxSize,gradient:i.gradient,max:i.max}),o=new R(i.splitList),s=new S(i.splitList),g=window.devicePixelRatio||1,h={map:t,animate:!1,updateHandler:a,resolutionScale:g},c=new F(h)}var I="2.0.2",D=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},B=function(){function t(t,e){for(var n=0;ne&&(t=e);var n=4*Math.floor(t/e*255),i=this.paletteCtx.getImageData(0,0,256,1).data;return"rgba("+i[n]+", "+i[n+1]+", "+i[n+2]+", "+i[n+3]/256+")"},n.prototype.getSize=function(t){var e=0,n=this.max,i=this.maxSize;return t>n&&(t=n),e=t/n*i},i.prototype.init=function(){this.intensity=new n({gradient:{0:"#006bab",1:"#002841"},max:100});var t=this.container.offsetWidth,e=this.container.offsetHeight,i=this.camera=new THREE.PerspectiveCamera(40,t/e,.01,9e3);i.position.x=0,i.position.y=0,i.position.z=85,i.lookAt(new THREE.Vector3(0,0,0));var a=this.scene=new THREE.Scene,r=this.renderer=new THREE.WebGLRenderer({alpha:!0});r.setPixelRatio(window.devicePixelRatio),r.setSize(t,e),this.container.appendChild(r.domElement);var o=THREE.ImageUtils.loadTexture("images/china.png"),s=new THREE.MeshBasicMaterial({map:o,transparent:!0,side:THREE.DoubleSide}),g=new THREE.PlaneGeometry(100,100,1,1),h=new THREE.Mesh(g,s);h.position.x=0,h.position.y=0,h.position.z=0;var c=new THREE.PointLight("rgb(50, 50, 250)");c.position.set(0,0,35),a.add(c);var l=20,u=new THREE.Geometry,p=new THREE.QuadraticBezierCurve3;p.v0=new THREE.Vector3(0,0,0),p.v1=new THREE.Vector3(20,20,0),p.v2=new THREE.Vector3(40,40,0);for(var d=0;d1&&(this.current=0);for(var i=0;in&&(n=e[i][t]);return n}},v.prototype.getSum=function(t){var e=this._data;if(e&&!(e.length<=0)){for(var n=0,i=0;i1){var y=f-v,w=v+(f_*_+M*M&&(v=w+(1&d?1:-1)/2,d=x)}var b=v+"-"+d,R=c[b];R?R.push(a[l]):(R=c[b]=[a[l]],R.i=v,R.j=d,R.x=(v+(1&d?.5:0))*g,R.y=d*h)}var S=new n({max:i.max||100,maxSize:s,gradient:i.gradient});for(var r in c){var T=c[r];t.beginPath();for(var P=0;P<6;P++){var C=s,H=E({x:T.x+o.x,y:T.y+o.y},C,P);t.lineTo(H[0],H[1])}t.closePath();for(var L=0,l=0;lI||Math.abs(s.y)>I){var g=1/Math.pow(i({source:S[r[o]][e],target:S[t][e]}),1);a.x+=s.x*g,a.y+=s.y*g}}return a}function p(t,e,i){for(var a=T/(n(b[t])*(e+1)),r=[{x:0,y:0}],o=1;os;){var l=s/c,u=S[e][h-1].x,p=S[e][h-1].y;u+=l*(S[e][h].x-S[e][h-1].x),p+=l*(S[e][h].y-S[e][h-1].y),g.push({x:u,y:p}),c-=s,s=i}s-=c}g.push(M[b[e].target]),S[e]=g}}function f(i,a){var r=Math.abs(t(e(i),e(a))/(n(i)*n(a)));return r}function v(t,e){var i=(n(t)+n(e))/2,a=2/(i/Math.min(n(t),n(e))+Math.max(n(t),n(e))/i);return a}function m(t,e){var i=(n(t)+n(e))/2,a={x:(M[t.source].x+M[t.target].x)/2,y:(M[t.source].y+M[t.target].y)/2},r={x:(M[e.source].x+M[e.target].x)/2,y:(M[e.source].y+M[e.target].y)/2},s=i/(i+o(a,r));return s}function y(t,e){var n=s(M[e.source],{source:M[t.source],target:M[t.target]}),i=s(M[e.target],{source:M[t.source],target:M[t.target]}),a={x:(n.x+i.x)/2,y:(n.y+i.y)/2},r={x:(M[t.source].x+M[t.target].x)/2,y:(M[t.source].y+M[t.target].y)/2},g=Math.max(0,1-2*o(r,a)/o(n,i));return g}function w(t,e){return Math.min(y(t,e),y(e,t))}function x(t,e){var n=f(t,e)*v(t,e)*m(t,e)*w(t,e);return n}function E(t,e){return x(t,e)>=k}function _(){for(var t=0;t=e.length-1));a++);this.splitList.other=e[e.length-1]},S.prototype.get=function(t){for(var e=this.splitList,n=!1,i=0;i=e[i].start)&&(void 0===e[i].end||void 0!==e[i].end&&t=e)throw new Error("start must be smaller than end");this.start=t,this.end=e};pt.prototype={diff:function(){return this.end-this.start},isLast:function(t){return(0|t)===this.end}};var dt="undefined"==typeof window?{}:window,ft=dt.requestAnimationFrame||dt.mozRequestAnimationFrame||dt.webkitRequestAnimationFrame||dt.msRequestAnimationFrame||function(t){return dt.setTimeout(t,1e3/60)},vt=dt.cancelAnimationFrame||dt.mozCancelAnimationFrame||dt.webkitCancelAnimationFrame||dt.msCancelAnimationFrame||function(t){clearTimeout(t)};L.prototype={start:function(){this.running=!0,ft(this._tick),this.options.onStart&&this.options.onStart(),1===this.stepsRange().diff()&&(this.running=!1)},isRunning:function(){return this.running},stop:function(){this.pause(),this.time(this.stepsRange().start),this.options.onStop&&this.options.onStop()},time:function(t){if(!arguments.length)return this._time;this._time=t;var e=this.range(this.domain(this._time));this.callback(e)},toggle:function(){this.running?this.pause():this.start()},rescale:function(){return this.domainInv=H(this.options.animationDelay,this.options.animationDelay+this.options.animationDuration),this.domain=this.domainInv.invert(),this.range=H(0,this._defaultStepsRange.end),this.rangeInv=this.range.invert(),this.time(this._time),this.running?this.start():this.pause(),this},duration:function(t){return arguments.length?(this.options.animationDuration=t,this.time()>t&&this.time(0),this.rescale(),this):this.options.animationDuration},steps:function(t,e){return this._defaultStepsRange=new pt(t,e),this.rescale()},stepsRange:function(t,e){if(2===arguments.length){if(tthis._defaultStepsRange.end)throw new Error("end must be within default steps range");this._customStepsRange=new pt(t,e),this.options.onStepsRange&&this.options.onStepsRange();var n=0|this.step();(ne)&&this.step(t)}return this._customStepsRange||this._defaultStepsRange},removeCustomStepsRange:function(){this._customStepsRange=void 0,this.options.onStepsRange&&this.options.onStepsRange()},step:function(t){return 0===arguments.length?this.range(this.domain(this._time)):void(this._time=this.domainInv(this.rangeInv(t)))},pause:function(){this.running=!1,vt(this._tick),this.options.onPause&&this.options.onPause()},_tick:function(){var t=+new Date,e=.001*(t-this._t0);e=Math.min(this.options.maxDelta,e),this._t0=t,this._time+=e;var n=this.stepsRange();n.isLast(this.step())&&(this.options.loop?this.step(n.start):(this.time(this.options.animationDuration),this.pause())),this.running&&(this.time(this._time),ft(this._tick))}};var mt=function(){function t(e,n,i){if(D(this,t),!e||!n)return console.warn("id 和 type 为必填项"),!1;if("baidu"==n){if(!BMap)return console.warn("请先引入百度地图JS API"),!1}else console.warn("暂不支持你的地图类型");this.type=n;var a=i&&i.center?i.center:[106.962497,38.208726],r=i&&i.zoom?i.zoom:5,o=this.map=new BMap.Map(e,{enableMapClick:!1});o.centerAndZoom(new BMap.Point(a[0],a[1]),r),o.enableScrollWheelZoom(!0),o.setMapStyle({style:"light"})}return B(t,[{key:"addLayer",value:function(t,e){if("baidu"==this.type)return new mapv.baiduMapLayer(this.map,dataSet,e)}},{key:"getMap",value:function(){return this.map}}]),t}(),yt="undefined"==typeof window?{}:window;yt.BMap&&(z.prototype=new BMap.Overlay,z.prototype.initialize=function(t){this._map=t;var e=this.canvas=document.createElement("canvas");e.style.cssText="position:absolute;left:0;top:0;z-index:"+this.zIndex+";",e.style.mixBlendMode=this.mixBlendMode,this.adjustSize(),t.getPanes()[this.paneName].appendChild(e);var n=this;return t.addEventListener("resize",function(){n.adjustSize(),n._draw()}),this.canvas},z.prototype.adjustSize=function(){var t=this._map.getSize(),e=this.canvas,n=this.devicePixelRatio=yt.devicePixelRatio;e.width=t.width*n,e.height=t.height*n,e.getContext("2d").scale(n,n),e.style.width=t.width+"px",e.style.height=t.height+"px"},z.prototype.draw=function(){var t=this;clearTimeout(t.timeoutID),t.timeoutID=setTimeout(function(){t._draw()},15)},z.prototype._draw=function(){var t=this._map,e=t.getSize(),n=t.getCenter();if(n){var i=t.pointToOverlayPixel(n);this.canvas.style.left=i.x-e.width/2+"px",this.canvas.style.top=i.y-e.height/2+"px",this.dispatchEvent("draw"),this.options.update&&this.options.update.call(this)}},z.prototype.getContainer=function(){return this.canvas},z.prototype.show=function(){this.canvas||this._map.addOverlay(this),this.canvas.style.display="block"},z.prototype.hide=function(){this.canvas.style.display="none"},z.prototype.setZIndex=function(t){this.canvas.style.zIndex=t},z.prototype.getZIndex=function(){return this.zIndex});var wt={draw:function(t,e,n){var i=e.get();t.fillStyle="white",t.textAlign="center",t.textBaseline="middle";for(var a in n)t[a]=n[a];for(var r=n.offset||{x:0,y:0},o=n.textKey||"text",s=0,g=i.length;sQ[t]){var e=l(Date.now()-Q[t],0,1,X[t]-Q[t]);(e<0||J)&&(e=0,c(t));var n=V[t].getPoint(e);q[3*t+0]=n.x,q[3*t+1]=n.y,q[3*t+2]=n.z}}}function c(t){var e=(Y-K)/($-K),n=(1-e)*j[t]*8e4,i=Date.now()+5e3*Math.random();Q[t]=i,X[t]=i+n}function l(t,e,n,i){return(t/=i/2)<1?n/2*t*t+e:-n/2*(--t*(t-2)-1)+e}function p(t){t.clearRect(0,0,t.canvas.width,t.canvas.height)}function d(t){var e=window.devicePixelRatio;t.canvas.width=t.canvas.width*e,t.canvas.height=t.canvas.height*e,t.canvas.style.width=t.canvas.width/e+"px",t.canvas.style.height=t.canvas.height/e+"px",t.scale(e,e)}function f(){this._subscribers={}}function v(t,e){this._options=e||{},this._data=[],t&&this.add(t)}function m(t){if("undefined"==typeof document)var e=require("canvas"),n=new e;else var n=document.createElement("canvas");var i=n.getContext("2d"),a=t/2,r=t+a,o=1e4;return n.width=n.height=2*r,i.shadowBlur=a,i.shadowColor="black",i.shadowOffsetX=i.shadowOffsetY=o,i.beginPath(),i.arc(r-o,r-o,t,0,2*Math.PI,!0),i.closePath(),i.fill(),n}function y(t,e,n){for(var i,a=n.maxOpacity||.8,r=3,o=t.length;ra&&(t[r]=256*a),t[r-3]=e[i],t[r-2]=e[i+1],t[r-1]=e[i+2]}function w(t,e,i){var a=i.max||100,r=i._size;void 0==r&&(r=i.size,void 0==r&&(r=13));var o=new n({gradient:i.gradient,max:a}),s=m(r),g=e,h={};g.forEach(function(t,e){var n=void 0===t.count?1:t.count,i=Math.min(1,n/a).toFixed(2);h[i]=h[i]||[],h[i].push(t)});for(var u in h)if(!isNaN(u)){var c=h[u];t.beginPath(),i.withoutAlpha||(t.globalAlpha=u),c.forEach(function(e,n){if(e.geometry){var r=e.geometry._coordinates||e.geometry.coordinates,o=e.geometry.type;if("Point"===o){var g=void 0===e.count?1:e.count;t.globalAlpha=g/a,t.drawImage(s,r[0]-s.width/2,r[1]-s.height/2)}else"LineString"===o&&tt.draw(t,e,i)}}),t.strokeStyle=o.getColor(u*a),t.stroke()}}function x(t,e,n){var i=n.strength||.3;t.strokeStyle="rgba(0,0,0,"+i+")",n=n||{};var a=e.get();if(t.save(),w(t,a,n),!n.absolute){var r=t.getImageData(0,0,t.canvas.width,t.canvas.height);y(r.data,nt.getImageData({defaultGradient:n.gradient||{.25:"rgba(0, 0, 255, 1)",.55:"rgba(0, 255, 0, 1)",.85:"rgba(255, 255, 0, 1)",1:"rgba(255, 0, 0, 1)"}}),n),t.putImageData(r,0,0),t.restore()}}function M(t,e,n){var i=60*n+30,a=Math.PI/180*i;return[t.x+e*Math.cos(a),t.y+e*Math.sin(a)]}function E(t){var e=t.split("|");return e[0]=e[0].split(","),{lng:parseFloat(e[0][0]),lat:parseFloat(e[0][1])}}function _(t){for(var e=[],n=0;n0&&(e=e.concat(i))}return e}function b(t,e){if(!t||!e)return null;var n,i,a,r,o,s,g,h=function(t){return 1-2*t+t*t},u=function(t){return 2*t-2*t*t},c=function(t){return t*t},l=[],p=40,d=0,f=0;if("undefined"==typeof e)return void("undefined"!=typeof l&&(l=[]));var v=parseFloat(t.lat),m=parseFloat(e.lat),y=parseFloat(t.lng),w=parseFloat(e.lng);for(w>y&&parseFloat(w-y)>180&&y<0&&(y=parseFloat(360+y)),y>w&&parseFloat(y-w)>180&&w<0&&(w=parseFloat(360+w)),s=0,g=0,m==v?(n=0,i=y-w):w==y?(n=Math.PI/2,i=v-m):(n=Math.atan((m-v)/(w-y)),i=(m-v)/Math.sin(n)),0==g&&(g=n+Math.PI/5),a=i/2,o=a*Math.cos(g)+y,r=a*Math.sin(g)+v,d=0;d_&&(_=M[w].geometry.coordinates[1]);var b=new google.maps.LatLng(E,_),R=d.fromLatLngToPoint(b);i.offset={x:(R.x-m.x)*f,y:(R.y-m.y)*f},"grid"==i.draw?at.draw(n,new v(y),i):rt.draw(n,new v(y),i)}else console.log("hehe"),et.draw(n,new v(y),i)}var r=new n({maxSize:i.maxSize,gradient:i.gradient,max:i.max}),o=new R(i.splitList),s=new S(i.splitList),g=window.devicePixelRatio||1,h={map:t,animate:!1,updateHandler:a,resolutionScale:g},u=new A(h)}var I="2.0.3",F=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},k=function(){function t(t,e){for(var n=0;ne&&(t=e);var n=4*Math.floor(t/e*255),i=this.paletteCtx.getImageData(0,0,256,1).data;return"rgba("+i[n]+", "+i[n+1]+", "+i[n+2]+", "+i[n+3]/256+")"},n.prototype.getSize=function(t){var e=0,n=this.max,i=this.maxSize;return t>n&&(t=n),e=t/n*i},i.prototype.init=function(){this.intensity=new n({gradient:{0:"#006bab",1:"#002841"},max:100});var t=this.container.offsetWidth,e=this.container.offsetHeight,i=this.camera=new THREE.PerspectiveCamera(40,t/e,.01,9e3);i.position.x=0,i.position.y=0,i.position.z=85,i.lookAt(new THREE.Vector3(0,0,0));var a=this.scene=new THREE.Scene,r=this.renderer=new THREE.WebGLRenderer({alpha:!0});r.setPixelRatio(window.devicePixelRatio),r.setSize(t,e),this.container.appendChild(r.domElement);var o=THREE.ImageUtils.loadTexture("images/china.png"),s=new THREE.MeshBasicMaterial({map:o,transparent:!0,side:THREE.DoubleSide}),g=new THREE.PlaneGeometry(100,100,1,1),h=new THREE.Mesh(g,s);h.position.x=0,h.position.y=0,h.position.z=0;var u=new THREE.PointLight("rgb(50, 50, 250)");u.position.set(0,0,35),a.add(u);var c=20,l=new THREE.Geometry,p=new THREE.QuadraticBezierCurve3;p.v0=new THREE.Vector3(0,0,0),p.v1=new THREE.Vector3(20,20,0),p.v2=new THREE.Vector3(40,40,0);for(var d=0;d1&&(this.current=0);for(var i=0;in&&(n=e[i][t]);return n}},v.prototype.getSum=function(t){var e=this._data;if(e&&!(e.length<=0)){for(var n=0,i=0;i1){var y=f-v,w=v+(fE*E+_*_&&(v=w+(1&d?1:-1)/2,d=x)}var b=v+"-"+d,R=u[b];R?R.push(a[c]):(R=u[b]=[a[c]],R.i=v,R.j=d,R.x=(v+(1&d?.5:0))*g,R.y=d*h)}var S=new n({max:i.max||100,maxSize:s,gradient:i.gradient});for(var r in u){var T=u[r];t.beginPath();for(var P=0;P<6;P++){var C=s,H=M({x:T.x+o.x,y:T.y+o.y},C,P);t.lineTo(H[0],H[1])}t.closePath();for(var L=0,c=0;ck||Math.abs(s.y)>k){var g=1/Math.pow(i({source:S[r[o]][e],target:S[t][e]}),1);a.x+=s.x*g,a.y+=s.y*g}}return a}function p(t,e,i){for(var a=T/(n(b[t])*(e+1)),r=[{x:0,y:0}],o=1;os;){var c=s/u,l=S[e][h-1].x,p=S[e][h-1].y;l+=c*(S[e][h].x-S[e][h-1].x),p+=c*(S[e][h].y-S[e][h-1].y),g.push({x:l,y:p}),u-=s,s=i}s-=u}g.push(_[b[e].target]),S[e]=g}}function f(i,a){var r=Math.abs(t(e(i),e(a))/(n(i)*n(a)));return r}function v(t,e){var i=(n(t)+n(e))/2,a=2/(i/Math.min(n(t),n(e))+Math.max(n(t),n(e))/i);return a}function m(t,e){var i=(n(t)+n(e))/2,a={x:(_[t.source].x+_[t.target].x)/2,y:(_[t.source].y+_[t.target].y)/2},r={x:(_[e.source].x+_[e.target].x)/2,y:(_[e.source].y+_[e.target].y)/2},s=i/(i+o(a,r));return s}function y(t,e){var n=s(_[e.source],{source:_[t.source],target:_[t.target]}),i=s(_[e.target],{source:_[t.source],target:_[t.target]}),a={x:(n.x+i.x)/2,y:(n.y+i.y)/2},r={x:(_[t.source].x+_[t.target].x)/2,y:(_[t.source].y+_[t.target].y)/2},g=Math.max(0,1-2*o(r,a)/o(n,i));return g}function w(t,e){return Math.min(y(t,e),y(e,t))}function x(t,e){var n=f(t,e)*v(t,e)*m(t,e)*w(t,e);return n}function M(t,e){return x(t,e)>=F}function E(){for(var t=0;t=e.length-1));a++);this.splitList.other=e[e.length-1]},S.prototype.get=function(t){for(var e=this.splitList,n=!1,i=0;i=e[i].start)&&(void 0===e[i].end||void 0!==e[i].end&&t1?1:w,x=l(w);for(g in i)if(void 0!==n[g]){var M=n[g]||0,E=i[g];E instanceof Array?e[g]=p(E,x):("string"==typeof E&&(E="+"===E.charAt(0)||"-"===E.charAt(0)?M+parseFloat(E,10):parseFloat(E,10)),"number"==typeof E&&(e[g]=M+(E-M)*x))}if(null!==m&&m.call(e,x),1===w){if(o>0){isFinite(o)&&o--;for(g in a){if("string"==typeof i[g]&&(a[g]=a[g]+parseFloat(i[g],10)),s){var _=a[g];a[g]=i[g],i[g]=_}n[g]=a[g]}return s&&(h=!h),c=t+u,!0}null!==y&&y.call(e);for(var b=0,R=d.length;b1?r(t[n],t[n-1],n-i):r(t[a],t[a+1>n?n:a+1],i-a)},Bezier:function(t,e){for(var n=0,i=t.length-1,a=Math.pow,r=ct.Interpolation.Utils.Bernstein,o=0;o<=i;o++)n+=a(1-e,i-o)*a(e,o)*t[o]*r(i,o);return n},CatmullRom:function(t,e){var n=t.length-1,i=n*e,a=Math.floor(i),r=ct.Interpolation.Utils.CatmullRom;return t[0]===t[n]?(e<0&&(a=Math.floor(i=n*(1+e))),r(t[(a-1+n)%n],t[a],t[(a+1)%n],t[(a+2)%n],i-a)):e<0?t[0]-(r(t[0],t[0],t[1],t[1],-i)-t[0]):e>1?t[n]-(r(t[n],t[n],t[n-1],t[n-1],i-n)-t[n]):r(t[a?a-1:0],t[a],t[n1;i--)n*=i;return t[e]=n,n}}(),CatmullRom:function(t,e,n,i,a){var r=.5*(n-t),o=.5*(i-e),s=a*a,g=a*s;return(2*e-2*n+r+o)*g+(-3*e+3*n-2*r-o)*s+r*a+e}}};var lt="undefined"==typeof window?{}:window,pt=lt.requestAnimationFrame||lt.mozRequestAnimationFrame||lt.webkitRequestAnimationFrame||lt.msRequestAnimationFrame||function(t){return lt.setTimeout(t,1e3/60)},dt=lt.cancelAnimationFrame||lt.mozCancelAnimationFrame||lt.webkitCancelAnimationFrame||lt.msCancelAnimationFrame||function(t){clearTimeout(t)};T.prototype={setOptions:function(t){this.options=t,t.stepsRange=t.stepsRange||{start:0,end:100},this.duration=t.duration||10,this.stepsRange=t.stepsRange,this._add=(this.stepsRange.end-this.stepsRange.start)/(60*this.duration),this._time=this.stepsRange.start},start:function(){this.running=!0,pt(this._tick),this.options.onStart&&this.options.onStart()},_tick:function(){this._time+=this._add,this._time>this.stepsRange.end&&(this._time=this.stepsRange.start),this.callback&&this.callback(this._time),this.running&&pt(this._tick)},isRunning:function(){return this.running},stop:function(){this.pause(),this._time=this.stepsRange.start,this.options.onStop&&this.options.onStop()},toggle:function(){this.running?this.pause():this.start()},pause:function(){this.running=!1,dt(this._tick),this.options.onPause&&this.options.onPause()}};var ft=function(){function t(e,n,i){if(F(this,t),!e||!n)return console.warn("id 和 type 为必填项"),!1;if("baidu"==n){if(!BMap)return console.warn("请先引入百度地图JS API"),!1}else console.warn("暂不支持你的地图类型");this.type=n;var a=i&&i.center?i.center:[106.962497,38.208726],r=i&&i.zoom?i.zoom:5,o=this.map=new BMap.Map(e,{enableMapClick:!1});o.centerAndZoom(new BMap.Point(a[0],a[1]),r),o.enableScrollWheelZoom(!0),o.setMapStyle({style:"light"})}return k(t,[{key:"addLayer",value:function(t,e){if("baidu"==this.type)return new mapv.baiduMapLayer(this.map,dataSet,e)}},{key:"getMap",value:function(){return this.map}}]),t}(),vt="undefined"==typeof window?{}:window;vt.BMap&&(C.prototype=new BMap.Overlay,C.prototype.initialize=function(t){this._map=t;var e=this.canvas=document.createElement("canvas");e.style.cssText="position:absolute;left:0;top:0;z-index:"+this.zIndex+";",e.style.mixBlendMode=this.mixBlendMode,this.adjustSize(),t.getPanes()[this.paneName].appendChild(e);var n=this;return t.addEventListener("resize",function(){n.adjustSize(),n._draw()}),this.canvas},C.prototype.adjustSize=function(){var t=this._map.getSize(),e=this.canvas,n=this.devicePixelRatio=vt.devicePixelRatio;e.width=t.width*n,e.height=t.height*n,e.getContext("2d").scale(n,n),e.style.width=t.width+"px",e.style.height=t.height+"px"},C.prototype.draw=function(){var t=this;clearTimeout(t.timeoutID),t.timeoutID=setTimeout(function(){t._draw()},15)},C.prototype._draw=function(){var t=this._map,e=t.getSize(),n=t.getCenter();if(n){var i=t.pointToOverlayPixel(n);this.canvas.style.left=i.x-e.width/2+"px",this.canvas.style.top=i.y-e.height/2+"px",this.dispatchEvent("draw"),this.options.update&&this.options.update.call(this)}},C.prototype.getContainer=function(){return this.canvas},C.prototype.show=function(){this.canvas||this._map.addOverlay(this),this.canvas.style.display="block"},C.prototype.hide=function(){this.canvas.style.display="none"},C.prototype.setZIndex=function(t){this.canvas.style.zIndex=t},C.prototype.getZIndex=function(){return this.zIndex});var mt={draw:function(t,e,n){var i=e.get();t.fillStyle="white",t.textAlign="center",t.textBaseline="middle";for(var a in n)t[a]=n[a];for(var r=n.offset||{x:0,y:0},o=n.textKey||"text",s=0,g=i.length;st-i&&e.timee.options.maxZoom)){switch("m"==e.options.unit&&e.options.size?e.options._size=e.options.size/a:e.options._size=e.options.size,e.options.draw){case"heatmap":it.draw(g,new v(c),e.options);break;case"grid":case"honeycomb":var m=i.pointToPixel(new BMap.Point(0,0));e.options.offset={x:m.x,y:m.y},"grid"==e.options.draw?at.draw(g,new v(c),e.options):rt.draw(g,new v(c),e.options);break;case"text":mt.draw(g,new v(c),e.options);break;case"icon":yt.draw(g,c,e.options);break;case"clip":g.save(),g.fillStyle=e.options.fillStyle||"rgba(0, 0, 0, 0.5)",g.fillRect(0,0,g.canvas.width,g.canvas.height),et.draw(g,c,e.options),g.beginPath(),tt.drawDataSet(g,new v(c),e.options),g.clip(),p(g),g.restore();break;default:et.draw(g,c,e.options)}e.options.updateCallback&&e.options.updateCallback(t)}}},L.prototype.isEnabledTime=function(){var t=this.options.animation,e=t&&!(t.enabled===!1);return e},L.prototype.argCheck=function(t){"heatmap"==t.draw&&t.strokeStyle&&console.warn("[heatmap] options.strokeStyle is discard, pleause use options.strength [eg: options.strength = 0.1]")},L.prototype.init=function(t){var e=this;if(e.options=t,e.intensity=new n({maxSize:e.options.maxSize,gradient:e.options.gradient,max:e.options.max||this.dataSet.getMax("count")}),e.category=new R(e.options.splitList),e.choropleth=new S(e.options.splitList),void 0===e.options.splitList&&e.category.generateByDataSet(this.dataSet),e.options.zIndex&&this.canvasLayer&&this.canvasLayer.setZIndex(e.options.zIndex),void 0===e.options.splitList){var i=e.options.min||this.dataSet.getMin("count"),a=e.options.max||this.dataSet.getMax("count");e.choropleth.generateByMinMax(i,a)}var r=e.options.animation;if("time"==e.options.draw||e.isEnabledTime()){r.stepsRange||(r.stepsRange={start:this.dataSet.getMin("time")||0,end:this.dataSet.getMax("time")||0});var o={step:r.stepsRange.start};e.animator=new ct.Tween(o).onUpdate(function(){e._canvasUpdate(this.step)}).repeat(1/0),e.map.addEventListener("movestart",function(){e.isEnabledTime()&&e.animator&&e.animator.stop()}),e.map.addEventListener("moveend",function(){e.isEnabledTime()&&e.animator&&e.animator.start()});var s=1e3*r.duration||5e3;e.animator.to({step:r.stepsRange.end},s),e.animator.start()}else e.animator&&e.animator.stop()},L.prototype.show=function(){this.map.addOverlay(this.canvasLayer)},L.prototype.hide=function(){this.map.removeOverlay(this.canvasLayer)},L.prototype.update=function(t){var e=this,n=t.options,i=e.options;for(var a in n)i[a]=n[a];e.init(i),e.canvasLayer.draw()},L.prototype.setOptions=function(t){var e=this;e.init(t),e.canvasLayer.draw()},L.prototype.set=function(t){var e={globalAlpha:1,globalCompositeOperation:"source-over",imageSmoothingEnabled:!0,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"},n=this,i=n.canvasLayer.canvas.getContext("2d");for(var a in e)i[a]=e[a];n.init(t.options),n.canvasLayer.draw()};var wt="undefined"==typeof window?{}:window;wt.google&&wt.google.maps&&(A.prototype=new google.maps.OverlayView,A.DEFAULT_PANE_NAME_="overlayLayer",A.CSS_TRANSFORM_=function(){for(var t=document.createElement("div"),e=["transform","WebkitTransform","MozTransform","OTransform","msTransform"],n=0;n max) { + value = max; + } + + var index = Math.floor(value / max * (256 - 1)) * 4; + + var imageData = this.paletteCtx.getImageData(0, 0, 256, 1).data; + + return "rgba(" + imageData[index] + ", " + imageData[index + 1] + ", " + imageData[index + 2] + ", " + imageData[index + 3] / 256 + ")"; + }; + + /** + * @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 maxSize = this.maxSize; + + if (value > max) { + value = max; + } + + size = value / max * maxSize; + + return size; + }; + + function Flate(container) { + + this.container = container; + this.init(); + + var that = this; + + this.group = new THREE.Group(); + + this.center = [105, 33]; + + function animate(time) { + requestAnimationFrame(animate); + + //that.controls.update(); + + that.render(); + } + + requestAnimationFrame(animate); + } + + Flate.prototype.init = function () { + this.intensity = new Intensity({ + gradient: { + 0: '#006bab', + 1: '#002841' + }, + max: 100 + }); + + var WIDTH = this.container.offsetWidth; + var HEIGHT = this.container.offsetHeight; + var camera = this.camera = new THREE.PerspectiveCamera(40, WIDTH / HEIGHT, 0.01, 9000); + camera.position.x = 0; + camera.position.y = 0; + camera.position.z = 85; + camera.lookAt(new THREE.Vector3(0, 0, 0)); + + var scene = this.scene = new THREE.Scene(); + + var renderer = this.renderer = new THREE.WebGLRenderer({ + alpha: true + }); + + /* + var controls = this.controls = new THREE.OrbitControls( camera, renderer.domElement ); + controls.enableDamping = true; + controls.dampingFactor = 0.25; + controls.enableZoom = true; + */ + + // renderer.setClearColor('rgb(1, 11, 21)', 1); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(WIDTH, HEIGHT); + + this.container.appendChild(renderer.domElement); + + var floorTexture = THREE.ImageUtils.loadTexture('images/china.png'); + var floorMaterial = new THREE.MeshBasicMaterial({ + map: floorTexture, + transparent: true, + side: THREE.DoubleSide + }); + var floorGeometry = new THREE.PlaneGeometry(100, 100, 1, 1); + var floor = new THREE.Mesh(floorGeometry, floorMaterial); + floor.position.x = 0; + floor.position.y = 0; + floor.position.z = 0; + //scene.add(floor); + + // LIGHT + var light = new THREE.PointLight('rgb(50, 50, 250)'); + light.position.set(0, 0, 35); + scene.add(light); + + var SUBDIVISIONS = 20; + var geometry = new THREE.Geometry(); + var curve = new THREE.QuadraticBezierCurve3(); + curve.v0 = new THREE.Vector3(0, 0, 0); + curve.v1 = new THREE.Vector3(20, 20, 0); + curve.v2 = new THREE.Vector3(40, 40, 0); + for (var j = 0; j < SUBDIVISIONS; j++) { + geometry.vertices.push(curve.getPoint(j / SUBDIVISIONS)); + } + + var material = new THREE.LineBasicMaterial({ color: 0xff0000, linewidth: 95 }); + var line = this.line = new THREE.Line(geometry, material); + //scene.add(line); + + this.current = 0; + }; + + Flate.prototype.render = function () { + + var SUBDIVISIONS = 50; + var geometry = new THREE.Geometry(); + var curve = new THREE.QuadraticBezierCurve3(); + curve.v0 = new THREE.Vector3(0, 0, 0); + curve.v1 = new THREE.Vector3(25, 25, 50); + curve.v2 = new THREE.Vector3(50, 50, 0); + this.current += 0.01; + if (this.current > 1) { + this.current = 0; + } + + for (var j = 0; j < SUBDIVISIONS; j++) { + var percent = j / SUBDIVISIONS; + if (percent < this.current) { + geometry.vertices.push(curve.getPoint(percent)); + } + } + + //this.line.geometry = geometry; + + this.renderer.render(this.scene, this.camera); + }; + + Flate.prototype.setDataSet = function (dataSet) { + // create a canvas element + var canvas = document.createElement('canvas'); + var context = canvas.getContext('2d'); + canvas.width = 50; + canvas.height = 50; + context.fillStyle = "rgba(255,255,50,0.75)"; + //context.shadowColor = "rgba(255,255,255,0.95)"; + //context.shadowBlur = 0; + context.arc(25, 25, 10, 0, Math.PI * 2); + context.fill(); + + // canvas contents will be used for a texture + var texture = new THREE.Texture(canvas); + texture.needsUpdate = true; + + var material = new THREE.MeshBasicMaterial({ map: texture, side: THREE.DoubleSide }); + material.transparent = true; + + var rs = dataSet.get(); + var features = rs; + for (var i = 0; i < features.length; i++) { + var feature = features[i]; + if (feature.geometry.type == 'Polygon') { + var coords = this.getCoordinates(feature.geometry.coordinates[0]); + this.addShape(coords); + } else if (feature.geometry.type == 'MultiPolygon') { + for (var j = 0; j < feature.geometry.coordinates.length; j++) { + var coords = this.getCoordinates(feature.geometry.coordinates[j][0]); + this.addShape(coords); + } + } else if (feature.geometry.type == 'Point') { + + var size = canvas.width / 15 + Math.random() * 4; + var mesh = new THREE.Mesh(new THREE.PlaneGeometry(size, size), material); + mesh.position.set(feature.geometry.coordinates[0] - this.center[0], feature.geometry.coordinates[1] - this.center[1], 1); + this.scene.add(mesh); + } + + var cityname = feature.name; + var center = feature.cp; + } + this.scene.add(this.group); + }; + + Flate.prototype.getCoordinates = function (coordinates) { + var coords = []; + for (var j = 0; j < coordinates.length; j++) { + coords.push(new THREE.Vector2(coordinates[j][0] - this.center[0], coordinates[j][1] - this.center[1])); + } + return coords; + }; + + Flate.prototype.addShape = function (coords) { + var shape = new THREE.Shape(coords); + var geometry = new THREE.ShapeGeometry(shape); + + var color = 'rgb(' + ~~(Math.random() * 256) + ', ' + ~~(Math.random() * 256) + ', ' + ~~(Math.random() * 256) + ')'; + color = this.intensity.getColor(Math.random() * 100); + var mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: color, side: THREE.DoubleSide })); + mesh.position.set(0, 0, 0); + this.group.add(mesh); + + var points = shape.createPointsGeometry(); + var line = new THREE.Line(points, new THREE.LineBasicMaterial({ color: 'rgb(0, 137, 191)', linewidth: 1 })); + line.position.set(0, 0, 0.1); + this.group.add(line); + }; + + var flights = [[43.061306, 74.477556, 40.608989, 72.793269]]; + + var index = 100; + while (index--) { + flights.push([19.670399 + Math.random() * 35, 78.895343 + Math.random() * 50, 19.670399 + Math.random() * 35, 78.895343 + Math.random() * 50]); + } + + var positions; + var start_flight_idx = 0; + var end_flight_idx = flights.length; + var flight_path_splines = []; + var flight_distance = []; + var flight_path_lines; + var flight_track_opacity = 0.32; + var flight_point_cloud_geom; + var flight_point_start_time = []; + var flight_point_end_time = []; + var flight_point_speed_changed = false; + var flight_point_speed_scaling = 5.0; + var flight_point_speed_min_scaling = 1.0; + var flight_point_speed_max_scaling = 25.0; + + function Earth(container) { + + this.container = container; + this.init(); + + var that = this; + + function animate(time) { + requestAnimationFrame(animate); + that.render(); + } + + requestAnimationFrame(animate); + } + + Earth.prototype.init = function () { + var WIDTH = this.container.offsetWidth; + var HEIGHT = this.container.offsetHeight; + var camera = this.camera = new THREE.PerspectiveCamera(40, WIDTH / HEIGHT, 0.01, 9000); + camera.position.z = 1.0; + + var scene = this.scene = new THREE.Scene(); + + var renderer = this.renderer = new THREE.WebGLRenderer({ + alpha: true + }); + // renderer.setClearColor('rgb(1, 11, 21)', 1); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(WIDTH, HEIGHT); + + this.container.appendChild(renderer.domElement); + + // LIGHT + /* + var light = new THREE.PointLight('rgb(50, 50, 250)'); + light.position.set(0, 0, 35); + scene.add(light); + */ + + scene.add(new THREE.AmbientLight(0x777777)); + + var light1 = new THREE.DirectionalLight(0xffffff, 0.2); + light1.position.set(5, 3, 5); + scene.add(light1); + + var light2 = new THREE.DirectionalLight(0xffffff, 0.2); + light2.position.set(5, 3, -5); + scene.add(light2); + + var earth_img, elevation_img, water_img; + var radius = 0.5; + var segments = 64; + earth_img = THREE.ImageUtils.loadTexture('images/earth_airports.png', THREE.UVMapping, function () { + elevation_img = THREE.ImageUtils.loadTexture('images/elevation.jpg', THREE.UVMapping, function () { + water_img = THREE.ImageUtils.loadTexture('images/water.png', THREE.UVMapping, function () { + var earth = new THREE.Mesh(new THREE.SphereGeometry(radius, segments, segments), new THREE.MeshPhongMaterial({ + map: earth_img, + bumpMap: elevation_img, + bumpScale: 0.01, + specularMap: water_img, + specular: new THREE.Color('grey') + })); + earth.rotation.y = 170 * (Math.PI / 180); + earth.rotation.x = 30 * (Math.PI / 180); + scene.add(earth); + + generateControlPoints(radius); + + flight_path_lines = flightPathLines(); + earth.add(flight_path_lines); + + earth.add(flightPointCloud()); + }); + }); + }); + }; + + Earth.prototype.render = function () { + update_flights(); + + this.renderer.render(this.scene, this.camera); + }; + + Earth.prototype.setDataSet = function (dataSet) { + console.log(dataSet.get()); + }; + + function generateControlPoints(radius) { + for (var f = start_flight_idx; f < end_flight_idx; ++f) { + + var start_lat = flights[f][0]; + var start_lng = flights[f][1]; + var end_lat = flights[f][2]; + var end_lng = flights[f][3]; + + var max_height = Math.random() * 0.04; + + var points = []; + var spline_control_points = 8; + for (var i = 0; i < spline_control_points + 1; i++) { + var arc_angle = i * 180.0 / spline_control_points; + var arc_radius = radius + Math.sin(arc_angle * Math.PI / 180.0) * max_height; + var latlng = latlngInterPoint(start_lat, start_lng, end_lat, end_lng, i / spline_control_points); + + var pos = xyzFromLatLng(latlng.lat, latlng.lng, arc_radius); + + points.push(new THREE.Vector3(pos.x, pos.y, pos.z)); + } + + var spline = new THREE.SplineCurve3(points); + + flight_path_splines.push(spline); + + var arc_length = spline.getLength(); + flight_distance.push(arc_length); + + setFlightTimes(f); + } + } + + function latlngInterPoint(lat1, lng1, lat2, lng2, offset) { + lat1 = lat1 * Math.PI / 180.0; + lng1 = lng1 * Math.PI / 180.0; + lat2 = lat2 * Math.PI / 180.0; + lng2 = lng2 * Math.PI / 180.0; + + var d = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin((lat1 - lat2) / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin((lng1 - lng2) / 2), 2))); + var A = Math.sin((1 - offset) * d) / Math.sin(d); + var B = Math.sin(offset * d) / Math.sin(d); + var x = A * Math.cos(lat1) * Math.cos(lng1) + B * Math.cos(lat2) * Math.cos(lng2); + var y = A * Math.cos(lat1) * Math.sin(lng1) + B * Math.cos(lat2) * Math.sin(lng2); + var z = A * Math.sin(lat1) + B * Math.sin(lat2); + var lat = Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))) * 180 / Math.PI; + var lng = Math.atan2(y, x) * 180 / Math.PI; + + return { + lat: lat, + lng: lng + }; + } + + function xyzFromLatLng(lat, lng, radius) { + var phi = (90 - lat) * Math.PI / 180; + var theta = (360 - lng) * Math.PI / 180; + + return { + x: radius * Math.sin(phi) * Math.cos(theta), + y: radius * Math.cos(phi), + z: radius * Math.sin(phi) * Math.sin(theta) + }; + } + + function flightPathLines() { + + var num_control_points = 32; + + var geometry = new THREE.BufferGeometry(); + var material = new THREE.LineBasicMaterial({ + color: 0xffff00, + vertexColors: THREE.VertexColors, + transparent: true, + opacity: flight_track_opacity, + depthTest: true, + depthWrite: false, + linewidth: 1.0001 + }); + var line_positions = new Float32Array(flights.length * 3 * 2 * num_control_points); + var colors = new Float32Array(flights.length * 3 * 2 * num_control_points); + + for (var i = start_flight_idx; i < end_flight_idx; ++i) { + + for (var j = 0; j < num_control_points - 1; ++j) { + + var start_pos = flight_path_splines[i].getPoint(j / (num_control_points - 1)); + var end_pos = flight_path_splines[i].getPoint((j + 1) / (num_control_points - 1)); + + line_positions[(i * num_control_points + j) * 6 + 0] = start_pos.x; + line_positions[(i * num_control_points + j) * 6 + 1] = start_pos.y; + line_positions[(i * num_control_points + j) * 6 + 2] = start_pos.z; + line_positions[(i * num_control_points + j) * 6 + 3] = end_pos.x; + line_positions[(i * num_control_points + j) * 6 + 4] = end_pos.y; + line_positions[(i * num_control_points + j) * 6 + 5] = end_pos.z; + + colors[(i * num_control_points + j) * 6 + 0] = 1.0; + colors[(i * num_control_points + j) * 6 + 1] = 0.4; + colors[(i * num_control_points + j) * 6 + 2] = 1.0; + colors[(i * num_control_points + j) * 6 + 3] = 1.0; + colors[(i * num_control_points + j) * 6 + 4] = 0.4; + colors[(i * num_control_points + j) * 6 + 5] = 1.0; + } + } + + geometry.addAttribute('position', new THREE.BufferAttribute(line_positions, 3)); + geometry.addAttribute('color', new THREE.BufferAttribute(colors, 3)); + + geometry.computeBoundingSphere(); + + return new THREE.Line(geometry, material, THREE.LinePieces); + } + + function flightPointCloud() { + flight_point_cloud_geom = new THREE.BufferGeometry(); + + var num_points = flights.length; + + positions = new Float32Array(num_points * 3); + var colors = new Float32Array(num_points * 3); + var sizes = new Float32Array(num_points); + + for (var i = 0; i < num_points; i++) { + positions[3 * i + 0] = 0; + positions[3 * i + 1] = 0; + positions[3 * i + 2] = 0; + + colors[3 * i + 0] = Math.random(); + colors[3 * i + 1] = Math.random(); + colors[3 * i + 2] = Math.random(); + + sizes[i] = 0.1; + } + + flight_point_cloud_geom.addAttribute('position', new THREE.BufferAttribute(positions, 3)); + flight_point_cloud_geom.addAttribute('customColor', new THREE.BufferAttribute(colors, 3)); + flight_point_cloud_geom.addAttribute('size', new THREE.BufferAttribute(sizes, 1)); + flight_point_cloud_geom.computeBoundingBox(); + + var attributes = { + size: { + type: 'f', + value: null + }, + customColor: { + type: 'c', + value: null + } + }; + + var uniforms = { + color: { + type: "c", + value: new THREE.Color(0xffffff) + }, + texture: { + type: "t", + value: THREE.ImageUtils.loadTexture("images/point.png") + } + }; + + var shaderMaterial = new THREE.ShaderMaterial({ + uniforms: uniforms, + //attributes: attributes, + vertexShader: document.getElementById('vertexshader').textContent, + fragmentShader: document.getElementById('fragmentshader').textContent, + blending: THREE.AdditiveBlending, + depthTest: true, + depthWrite: false, + transparent: true + }); + + return new THREE.Points(flight_point_cloud_geom, shaderMaterial); + } + + function update_flights() { + if (!flight_point_cloud_geom) { + return; + } + flight_point_cloud_geom.attributes.position.needsUpdate = true; + + for (var i = start_flight_idx; i < end_flight_idx; ++i) { + + if (Date.now() > flight_point_start_time[i]) { + var ease_val = easeOutQuadratic(Date.now() - flight_point_start_time[i], 0, 1, flight_point_end_time[i] - flight_point_start_time[i]); + + if (ease_val < 0 || flight_point_speed_changed) { + ease_val = 0; + setFlightTimes(i); + } + + var pos = flight_path_splines[i].getPoint(ease_val); + positions[3 * i + 0] = pos.x; + positions[3 * i + 1] = pos.y; + positions[3 * i + 2] = pos.z; + } + } + } + + function setFlightTimes(index) { + var scaling_factor = (flight_point_speed_scaling - flight_point_speed_min_scaling) / (flight_point_speed_max_scaling - flight_point_speed_min_scaling); + var duration = (1 - scaling_factor) * flight_distance[index] * 80000; + + var start_time = Date.now() + Math.random() * 5000; + flight_point_start_time[index] = start_time; + flight_point_end_time[index] = start_time + duration; + } + + function easeOutQuadratic(t, b, c, d) { + if ((t /= d / 2) < 1) return c / 2 * t * t + b; + return -c / 2 * (--t * (t - 2) - 1) + b; + } + + /** + * @author kyle / http://nikai.us/ + */ + + function clear (context) { + 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/ + */ + + function resolutionScale (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); + } + } + }; + + /** + * 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++) { + 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); + } + + //console.timeEnd('transferCoordinate time') + + return data; + }; + + /** + * set data. + */ + DataSet.prototype.set = function (data) { + this.clear(); + this.add(data); + this._trigger('change'); + }; + + /** + * 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) { + + for (var i = 0; i < data.length; i++) { + + var item = data[i]; + + if (data[i].geometry) { + + if (data[i].geometry.type === 'Point') { + var coordinates = data[i].geometry.coordinates; + data[i].geometry._coordinates = transferFn(coordinates); + } + + if (data[i].geometry.type === 'Polygon' || data[i].geometry.type === 'MultiPolygon') { + + var coordinates = data[i].geometry.coordinates; + + if (data[i].geometry.type === 'Polygon') { + + var newCoordinates = getPolygon(coordinates); + data[i].geometry._coordinates = newCoordinates; + } else if (data[i].geometry.type === 'MultiPolygon') { + var newCoordinates = []; + for (var c = 0; c < coordinates.length; c++) { + var polygon = coordinates[c]; + var polygon = getPolygon(polygon); + newCoordinates.push(polygon); + } + + data[i].geometry._coordinates = newCoordinates; + } + } + + if (data[i].geometry.type === 'LineString') { + var coordinates = data[i].geometry.coordinates; + var newCoordinates = []; + for (var j = 0; j < coordinates.length; j++) { + newCoordinates.push(transferFn(coordinates[j])); + } + data[i].geometry._coordinates = newCoordinates; + } + } + } + + 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 = data[0][columnName]; + + for (var i = 1; i < data.length; i++) { + if (data[i][columnName] > max) { + max = data[i][columnName]; + } + } + + 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 = data[0][columnName]; + + for (var i = 1; i < data.length; i++) { + if (data[i][columnName] < min) { + min = data[i][columnName]; + } + } + + return min; + }; + + 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 { + 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]); + } + } + + }; + + 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(); + } + }; + + /** + * @author kyle / http://nikai.us/ + */ + + var utilsColorPalette = { + getImageData: function getImageData(config) { + var gradientConfig = config.gradient || config.defaultGradient; + if (typeof document === 'undefined') { + var Canvas = require('canvas'); + var paletteCanvas = new Canvas(256, 1); + } else { + var paletteCanvas = document.createElement('canvas'); + } + var paletteCtx = paletteCanvas.getContext('2d'); + + paletteCanvas.width = 256; + paletteCanvas.height = 1; + + var gradient = paletteCtx.createLinearGradient(0, 0, 256, 1); + for (var key in gradientConfig) { + gradient.addColorStop(parseFloat(key), gradientConfig[key]); + } + + paletteCtx.fillStyle = gradient; + paletteCtx.fillRect(0, 0, 256, 1); + + return paletteCtx.getImageData(0, 0, 256, 1).data; + } + }; + + function createCircle(size) { + + if (typeof document === 'undefined') { + var Canvas = require('canvas'); + var circle = new Canvas(); + } else { + var circle = document.createElement('canvas'); + } + var context = circle.getContext('2d'); + var shadowBlur = size / 2; + var r2 = size + shadowBlur; + var offsetDistance = 10000; + + circle.width = circle.height = r2 * 2; + + 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; + // console.log(max) + var size = options._size; + if (size == undefined) { + size = options.size; + if (size == undefined) { + size = 13; + } + } + + var color = new Intensity({ + gradient: options.gradient, + max: max + }); + + var circle = createCircle(size); + + 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; + } + _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] - circle.width / 2, coordinates[1] - circle.height / 2); + } else if (type === 'LineString') { + pathSimple.draw(context, item, options); + } else if (type === 'Polygon') {} + }); + // console.warn(i, i * max, color.getColor(i * max)) + context.strokeStyle = color.getColor(i * max); + context.stroke(); + } + } + + function draw(context, dataSet, options) { + var strength = options.strength || 0.3; + context.strokeStyle = 'rgba(0,0,0,' + strength + ')'; + + options = options || {}; + + var data = dataSet.get(); + + context.save(); + //console.time('drawGray') + drawGray(context, data, options); + //console.timeEnd('drawGray'); + // return false; + if (!options.absolute) { + //console.time('changeColor'); + var colored = context.getImageData(0, 0, context.canvas.width, context.canvas.height); + colorize(colored.data, utilsColorPalette.getImageData({ + defaultGradient: 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)" + } + }), options); + //console.timeEnd('changeColor'); + context.putImageData(colored, 0, 0); + + context.restore(); + } + } + + var drawHeatmap = { + draw: draw + }; + + var drawGrid = { + draw: function draw(context, dataSet, options) { + + context.save(); + + var data = dataSet.get(); + + var grids = {}; + + 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; + } + grids[gridKey] += ~~(data[i].count || 1); + } + + for (var gridKey in grids) { + gridKey = gridKey.split(","); + + var intensity = new Intensity({ + max: options.max || 100, + gradient: options.gradient + }); + + context.beginPath(); + context.rect(gridKey[0] * size + .5 + offset.x, gridKey[1] * size + .5 + offset.y, size, size); + context.fillStyle = intensity.getColor(grids[gridKey]); + context.fill(); + if (options.showText) { + context.fillStyle = 'white'; + context.fillText(grids[gridKey], gridKey[0] * size + .5 + offset.x + size / 2, gridKey[1] * size + .5 + offset.y + size / 2); + } + if (options.strokeStyle && options.lineWidth) { + context.stroke(); + } + } + + context.restore(); + } + }; + + 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.get(); + + 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; + } + + context.fillStyle = intensity.getColor(count); + context.fill(); + if (options.strokeStyle && options.lineWidth) { + context.stroke(); + } + } + + context.restore(); + } + }; + + /** + * 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; + } + }; + + /** + * Timer + * @author kyle / http://nikai.us/ + */ + + var Timer = function () { + function Timer(callback, options) { + classCallCheck(this, Timer); + + this._call = callback; + this._runing = false; + this.start(); + } + + createClass(Timer, [{ + key: "start", + value: function start() { + this._runing = true; + requestAnimationFrame(this._launch.bind(this)); + } + }, { + key: "stop", + value: function stop() { + this._runing = false; + } + }, { + key: "_launch", + value: function _launch(timestamp) { + if (this._runing) { + this._call && this._call(timestamp); + requestAnimationFrame(this._launch.bind(this)); + } + } + }]); + return Timer; + }(); + + /** + * 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 + (function () { + // In a browser, use window.performance.now if it is available. + 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 _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; + + // Set all starting values present on the target object + for (var field in object) { + _valuesStart[field] = parseFloat(object[field], 10); + } + + this.to = function (properties, duration) { + + if (duration !== undefined) { + _duration = duration; + } + + _valuesEnd = properties; + + 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 (_valuesStart[property] === undefined) { + continue; + } + + _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); + } + + this.stopChainedTweens(); + 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.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); + } + + _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, 10); + } else { + end = parseFloat(end, 10); + } + } + + // 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], 10); + } + + if (_yoyo) { + var tmp = _valuesStartRepeat[property]; + + _valuesStartRepeat[property] = _valuesEnd[property]; + _valuesEnd[property] = tmp; + } + + _valuesStart[property] = _valuesStartRepeat[property]; + } + + if (_yoyo) { + _reversed = !_reversed; + } + + _startTime = time + _delayTime; + + return true; + } else { + + if (_onCompleteCallback !== null) { + _onCompleteCallback.call(_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; + } + + } + + }; + + /** + * Abstract handler for animator steps + */ + + var global$1 = typeof window === 'undefined' ? {} : window; + + var requestAnimationFrame$1 = global$1.requestAnimationFrame || global$1.mozRequestAnimationFrame || global$1.webkitRequestAnimationFrame || global$1.msRequestAnimationFrame || function (callback) { + return global$1.setTimeout(callback, 1000 / 60); + }; + + var cancelAnimationFrame = global$1.cancelAnimationFrame || global$1.mozCancelAnimationFrame || global$1.webkitCancelAnimationFrame || global$1.msCancelAnimationFrame || function (id) { + clearTimeout(id); + }; + + /** + * options: + * duration in seconds + * delay in seconds + */ + function Animator(callback, options) { + + this.running = false; + this.callback = callback; + + this.setOptions(options); + + this._tick = this._tick.bind(this); + } + + Animator.prototype = { + + setOptions: function setOptions(options) { + this.options = options; + options.stepsRange = options.stepsRange || { + start: 0, + end: 100 + }; + + this.duration = options.duration || 10; // 单位秒 + + this.stepsRange = options.stepsRange; + this._add = (this.stepsRange.end - this.stepsRange.start) / (this.duration * 60); + this._time = this.stepsRange.start; + }, + + start: function start() { + + this.running = true; + requestAnimationFrame$1(this._tick); + this.options.onStart && this.options.onStart(); + }, + + _tick: function _tick() { + this._time += this._add; + if (this._time > this.stepsRange.end) { + this._time = this.stepsRange.start; + } + this.callback && this.callback(this._time); + if (this.running) { + requestAnimationFrame$1(this._tick); + } + }, + + isRunning: function isRunning() { + return this.running; + }, + + stop: function stop() { + this.pause(); + this._time = this.stepsRange.start; + this.options.onStop && this.options.onStop(); + }, + + toggle: function toggle() { + if (this.running) { + this.pause(); + } else { + this.start(); + } + }, + + pause: function pause() { + this.running = false; + cancelAnimationFrame(this._tick); + this.options.onPause && this.options.onPause(); + } + + }; + + /** + * @author Mofei + */ + + 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 || 'labelPane'; + this.zIndex = this.options.zIndex || 0; + this.mixBlendMode = this.options.mixBlendMode || null; + this._map = options.map; + this._lastDrawTime = null; + this.show(); + } + + var global$2 = typeof window === 'undefined' ? {} : window; + + if (global$2.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 + ";"; + 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$2.devicePixelRatio; + + canvas.width = size.width * devicePixelRatio; + canvas.height = size.height * devicePixelRatio; + canvas.getContext('2d').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; + }; + } + + var drawText = { + draw: function draw(context, dataSet, options) { + var data = dataSet.get(); + context.fillStyle = 'white'; + context.textAlign = 'center'; + context.textBaseline = 'middle'; + + // set from options + for (var key in options) { + context[key] = options[key]; + } + + var offset = options.offset || { + x: 0, + y: 0 + }; + + var textKey = options.textKey || 'text'; + + for (var i = 0, len = data.length; i < len; i++) { + var coordinates = data[i].geometry._coordinates || data[i].geometry.coordinates; + context.fillText(data[i][textKey], coordinates[0] + offset.x, coordinates[1] + offset.y); + }; + } + }; + + 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'; + + // 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 icon = data[i].icon; + var coordinates = data[i].geometry._coordinates || data[i].geometry.coordinates; + context.drawImage(icon, coordinates[0] - icon.width / 2, coordinates[1] - icon.height / 2); + } + }; + } + }; + + if (typeof window !== 'undefined') { + requestAnimationFrame(animate); + } + + function animate(time) { + requestAnimationFrame(animate); + TWEEN.update(time); + } + + function Layer(map, dataSet, options) { + if (!(dataSet instanceof DataSet)) { + dataSet = new DataSet(dataSet); + } + + this.dataSet = dataSet; + + var self = this; + var data = null; + options = options || {}; + + self.map = map; + + self.init(options); + self.argCheck(options); + + var canvasLayer = this.canvasLayer = new CanvasLayer({ + map: map, + paneName: options.paneName, + mixBlendMode: options.mixBlendMode, + zIndex: options.zIndex, + update: function update() { + self._canvasUpdate(); + } + }); + + dataSet.on('change', function () { + canvasLayer.draw(); + }); + + if (self.options.methods) { + if (self.options.methods.click) { + map.setDefaultCursor("default"); + map.addEventListener('click', function (e) { + var pixel = e.pixel; + var context = canvasLayer.canvas.getContext('2d'); + var data = dataSet.get(); + for (var i = 0; i < data.length; i++) { + context.beginPath(); + pathSimple.draw(context, data[i], self.options); + if (context.isPointInPath(pixel.x * canvasLayer.devicePixelRatio, pixel.y * canvasLayer.devicePixelRatio)) { + self.options.methods.click(data[i], e); + return; + } + } + }); + } + } + } + + Layer.prototype._canvasUpdate = function (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); //左上角墨卡托坐标 + + //console.time('update') + var context = this.canvasLayer.canvas.getContext("2d"); + + if (self.isEnabledTime()) { + if (time === undefined) { + clear(context); + return; + } + 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); + } + + for (var key in self.options) { + context[key] = self.options[key]; + } + + var dataGetOptions = { + transferCoordinate: function transferCoordinate(coordinate) { + + if (self.options.coordType == 'bd09mc') { + var x = (coordinate[0] - nwMc.x) / zoomUnit; + var y = (nwMc.y - coordinate[1]) / zoomUnit; + return [x, y]; + } + + var pixel = map.pointToPixel(new BMap.Point(coordinate[0], coordinate[1])); + return [pixel.x, pixel.y]; + } + }; + + if (time !== undefined) { + dataGetOptions.filter = function (item) { + var trails = animationOptions.trails || 5; + 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); + + // deal with data based on draw + + // TODO: 部分情况下可以不用循环,比如heatmap + //console.time('setstyle'); + + 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; + } + + if (self.options.draw == 'intensity') { + if (data[i].geometry.type === 'LineString') { + data[i].strokeStyle = self.intensity.getColor(item.count); + } else { + data[i].fillStyle = self.intensity.getColor(item.count); + } + } else if (self.options.draw == 'category') { + data[i].fillStyle = self.category.get(item.count); + } else if (self.options.draw == 'choropleth') { + data[i].fillStyle = self.choropleth.get(item.count); + } + } + } + + //console.timeEnd('setstyle'); + + if (self.options.minZoom && map.getZoom() < self.options.minZoom || self.options.maxZoom && map.getZoom() > self.options.maxZoom) { + return; + } + + //console.time('draw'); + // draw + + if (self.options.unit == 'm' && self.options.size) { + self.options._size = self.options.size / zoomUnit; + } else { + self.options._size = self.options.size; + } + + switch (self.options.draw) { + case 'heatmap': + drawHeatmap.draw(context, new DataSet(data), self.options); + break; + case 'grid': + case 'honeycomb': + /* + if (data.length <= 0) { + break; + } + var minx = data[0].geometry.coordinates[0]; + var maxy = data[0].geometry.coordinates[1]; + for (var i = 1; i < data.length; i++) { + minx = Math.min(data[i].geometry.coordinates[0], minx); + maxy = Math.max(data[i].geometry.coordinates[1], maxy); + } + var nwPixel = map.pointToPixel(new BMap.Point(minx, maxy)); + */ + var nwPixel = map.pointToPixel(new BMap.Point(0, 0)); + self.options.offset = { + x: nwPixel.x, + y: nwPixel.y + }; + if (self.options.draw == 'grid') { + drawGrid.draw(context, new DataSet(data), self.options); + } else { + drawHoneycomb.draw(context, new DataSet(data), self.options); + } + break; + case 'text': + drawText.draw(context, new DataSet(data), self.options); + break; + case 'icon': + drawIcon.draw(context, data, 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, data, self.options); + context.beginPath(); + pathSimple.drawDataSet(context, new DataSet(data), self.options); + context.clip(); + clear(context); + context.restore(); + break; + default: + drawSimple.draw(context, data, self.options); + } + //console.timeEnd('draw'); + + //console.timeEnd('update') + self.options.updateCallback && self.options.updateCallback(time); + }; + + Layer.prototype.isEnabledTime = function () { + + var animationOptions = this.options.animation; + + var flag = animationOptions && !(animationOptions.enabled === false); + + return flag; + }; + + Layer.prototype.argCheck = function (options) { + if (options.draw == 'heatmap') { + if (options.strokeStyle) { + console.warn('[heatmap] options.strokeStyle is discard, pleause use options.strength [eg: options.strength = 0.1]'); + } + } + }; + + Layer.prototype.init = function (options) { + var self = this; + + self.options = options; + + self.intensity = new Intensity({ + maxSize: self.options.maxSize, + 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.zIndex) { + this.canvasLayer && this.canvasLayer.setZIndex(self.options.zIndex); + } + + 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); + } + + var animationOptions = self.options.animation; + + if (self.options.draw == 'time' || self.isEnabledTime()) { + //if (!self.animator) { + if (!animationOptions.stepsRange) { + animationOptions.stepsRange = { + start: this.dataSet.getMin('time') || 0, + end: this.dataSet.getMax('time') || 0 + }; + } + + var steps = { step: animationOptions.stepsRange.start }; + self.animator = new TWEEN.Tween(steps).onUpdate(function () { + self._canvasUpdate(this.step); + }).repeat(Infinity); + + self.map.addEventListener('movestart', function () { + if (self.isEnabledTime() && self.animator) { + self.animator.stop(); + } + }); + + self.map.addEventListener('moveend', function () { + if (self.isEnabledTime() && self.animator) { + self.animator.start(); + } + }); + //} + + var duration = animationOptions.duration * 1000 || 5000; + + self.animator.to({ step: animationOptions.stepsRange.end }, duration); + self.animator.start(); + } else { + self.animator && self.animator.stop(); + } + }; + + Layer.prototype.show = function () { + this.map.addOverlay(this.canvasLayer); + }; + + Layer.prototype.hide = function () { + this.map.removeOverlay(this.canvasLayer); + }; + + /** + * obj.options + */ + Layer.prototype.update = function (obj) { + var self = this; + var _options = obj.options; + var options = self.options; + for (var i in _options) { + options[i] = _options[i]; + } + self.init(options); + self.canvasLayer.draw(); + }; + + Layer.prototype.setOptions = function (options) { + var self = this; + self.init(options); + self.canvasLayer.draw(); + }; + + Layer.prototype.set = function (obj) { + var conf = { + 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' + }; + var self = this; + var ctx = self.canvasLayer.canvas.getContext("2d"); + for (var i in conf) { + ctx[i] = conf[i]; + } + self.init(obj.options); + self.canvasLayer.draw(); + }; + + /** + * 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$1(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$1.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$3 = typeof window === 'undefined' ? {} : window; + + if (global$3.google && global$3.google.maps) { + + CanvasLayer$1.prototype = new google.maps.OverlayView(); + + /** + * The default MapPane to contain the canvas. + * @type {string} + * @const + * @private + */ + CanvasLayer$1.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$1.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$1.prototype.requestAnimFrame_ = global$3.requestAnimationFrame || global$3.webkitRequestAnimationFrame || global$3.mozRequestAnimationFrame || global$3.oRequestAnimationFrame || global$3.msRequestAnimationFrame || function (callback) { + return global$3.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$1.prototype.cancelAnimFrame_ = global$3.cancelAnimationFrame || global$3.webkitCancelAnimationFrame || global$3.mozCancelAnimationFrame || global$3.oCancelAnimationFrame || global$3.msCancelAnimationFrame || function (requestId) {}; + + /** + * Sets any options provided. See CanvasLayerOptions for more information. + * @param {CanvasLayerOptions} options The options to set. + */ + CanvasLayer$1.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$1.prototype.setAnimate = function (animate) { + this.isAnimated_ = !!animate; + + if (this.isAnimated_) { + this.scheduleUpdate(); + } + }; + + /** + * @return {boolean} Whether the canvas is animated. + */ + CanvasLayer$1.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$1.prototype.setPaneName = function (paneName) { + this.paneName_ = paneName; + + this.setPane_(); + }; + + /** + * @return {string} The name of the current container pane. + */ + CanvasLayer$1.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$1.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$1.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$1.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$1.prototype.setUpdateHandler = function (opt_updateHandler) { + this.updateHandler_ = opt_updateHandler; + }; + + /** + * @inheritDoc + */ + CanvasLayer$1.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$1.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$3, this.requestAnimationFrameId_); + this.requestAnimationFrameId_ = null; + } + }; + + /** + * The internal callback for resize events that resizes the canvas to keep the + * map properly covered. + * @private + */ + CanvasLayer$1.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$1.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$1.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$1.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$1.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$1.prototype.getTopLeft = function () { + return this.topLeft_; + }; + + /** + * Schedule a requestAnimationFrame callback to updateHandler. If one is + * already scheduled, there is no effect. + */ + CanvasLayer$1.prototype.scheduleUpdate = function () { + if (this.isAdded_ && !this.requestAnimationFrameId_) { + this.requestAnimationFrameId_ = this.requestAnimFrame_.call(global$3, this.requestUpdateFunction_); + } + }; + } + + function Layer$1(map, dataSet, options) { + var intensity = new Intensity({ + maxSize: options.maxSize, + gradient: options.gradient, + max: options.max + }); + + var category = new Category(options.splitList); + + var choropleth = new Choropleth(options.splitList); + + var resolutionScale = window.devicePixelRatio || 1; + + // initialize the canvasLayer + var canvasLayerOptions = { + map: map, + animate: false, + updateHandler: update, + resolutionScale: resolutionScale + }; + + var canvasLayer = new CanvasLayer$1(canvasLayerOptions); + + function update() { + + var context = canvasLayer.canvas.getContext('2d'); + + clear(context); + + for (var key in options) { + context[key] = options[key]; + } + + var pointCount = 0; + var lineCount = 0; + var polygonCount = 0; + + /* We need to scale and translate the map for current view. + * see https://developers.google.com/maps/documentation/javascript/maptypes#MapCoordinates + */ + var mapProjection = map.getProjection(); + + // scale is just 2^zoom + // If canvasLayer is scaled (with resolutionScale), we need to scale by + // the same amount to account for the larger canvas. + var scale = Math.pow(2, map.zoom) * resolutionScale; + + var offset = mapProjection.fromLatLngToPoint(canvasLayer.getTopLeft()); + + var data = dataSet.get({ + 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]; + } + }); + + for (var i = 0; i < data.length; i++) { + var item = data[i]; + if (options.draw == 'bubble') { + data[i].size = intensity.getSize(item.count); + } else if (options.draw == 'intensity') { + if (data[i].geometry.type === 'LineString') { + data[i].strokeStyle = intensity.getColor(item.count); + } else { + data[i].fillStyle = intensity.getColor(item.count); + } + } else if (options.draw == 'category') { + data[i].fillStyle = category.get(item.count); + } else if (options.draw == 'choropleth') { + data[i].fillStyle = choropleth.get(item.count); + } + } + + var maxCount = Math.max(Math.max(pointCount, lineCount), polygonCount); + + if (options.draw == 'heatmap') { + drawHeatmap.draw(context, new DataSet(data), options); + } else if (options.draw == 'grid' || options.draw == 'honeycomb') { + var data1 = dataSet.get(); + var minx = data1[0].geometry.coordinates[0]; + var maxy = data1[0].geometry.coordinates[1]; + for (var i = 1; i < data1.length; i++) { + if (data1[i].geometry.coordinates[0] < minx) { + minx = data1[i].geometry.coordinates[0]; + } + if (data1[i].geometry.coordinates[1] > maxy) { + maxy = data1[i].geometry.coordinates[1]; + } + } + + var latLng = new google.maps.LatLng(minx, maxy); + var worldPoint = mapProjection.fromLatLngToPoint(latLng); + + options.offset = { + x: (worldPoint.x - offset.x) * scale, + y: (worldPoint.y - offset.y) * scale + }; + if (options.draw == 'grid') { + drawGrid.draw(context, new DataSet(data), options); + } else { + drawHoneycomb.draw(context, new DataSet(data), options); + } + } else { + console.log('hehe'); + drawSimple.draw(context, new DataSet(data), options); + } + } + } + + 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); + } + }; + + 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.x = X; + exports.X = X; + exports.Flate = Flate; + exports.Earth = Earth; + exports.canvasClear = clear; + exports.canvasResolutionScale = resolutionScale; + exports.canvasDrawSimple = drawSimple; + exports.canvasDrawHeatmap = drawHeatmap; + exports.canvasDrawGrid = drawGrid; + exports.canvasDrawHoneycomb = drawHoneycomb; + exports.utilCityCenter = cityCenter; + exports.utilCurve = curve; + exports.utilForceEdgeBundling = ForceEdgeBundling; + exports.utilDataRangeIntensity = Intensity; + exports.utilDataRangeCategory = Category; + exports.utilDataRangeChoropleth = Choropleth; + exports.Timer = Timer; + exports.Animator = Animator; + exports.Map = MapHelper; + exports.baiduMapCanvasLayer = CanvasLayer; + exports.baiduMapLayer = Layer; + exports.googleMapCanvasLayer = CanvasLayer$1; + exports.googleMapLayer = Layer$1; + exports.DataSet = DataSet; + exports.geojson = geojson; + exports.csv = csv; + +})); \ No newline at end of file diff --git a/build/release/mapv.v2.0.3.min.js b/build/release/mapv.v2.0.3.min.js new file mode 100644 index 00000000..b4c0b8c9 --- /dev/null +++ b/build/release/mapv.v2.0.3.min.js @@ -0,0 +1,2 @@ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t.mapv=t.mapv||{})}(this,function(t){"use strict";function e(){var t=document.createElement("canvas"),e=t.getContext("2d"),n=e.createLinearGradient(0,0,256,0);n.addColorStop(1,"#F00"),n.addColorStop(.6,"#FFFC00"),n.addColorStop(.3,"#00FF1D"),n.addColorStop(0,"#000BFF"),e.fillStyle=n,e.fillRect(0,0,256,1);var i=e.getImageData(0,0,256,1);return i.data}function n(t){t=t||{},this.gradient=t.gradient||{.25:"rgba(0, 0, 255, 1)",.55:"rgba(0, 255, 0, 1)",.85:"rgba(255, 255, 0, 1)",1:"rgba(255, 0, 0, 1)"},this.maxSize=t.maxSize||35,this.max=t.max||100,this.initPalette()}function i(t){function e(t){requestAnimationFrame(e),n.render()}this.container=t,this.init();var n=this;this.group=new THREE.Group,this.center=[105,33],requestAnimationFrame(e)}function a(t){function e(t){requestAnimationFrame(e),n.render()}this.container=t,this.init();var n=this;requestAnimationFrame(e)}function r(t){for(var e=G;eQ[t]){var e=l(Date.now()-Q[t],0,1,X[t]-Q[t]);(e<0||J)&&(e=0,c(t));var n=V[t].getPoint(e);q[3*t+0]=n.x,q[3*t+1]=n.y,q[3*t+2]=n.z}}}function c(t){var e=(Y-K)/($-K),n=(1-e)*j[t]*8e4,i=Date.now()+5e3*Math.random();Q[t]=i,X[t]=i+n}function l(t,e,n,i){return(t/=i/2)<1?n/2*t*t+e:-n/2*(--t*(t-2)-1)+e}function p(t){t.clearRect(0,0,t.canvas.width,t.canvas.height)}function d(t){var e=window.devicePixelRatio;t.canvas.width=t.canvas.width*e,t.canvas.height=t.canvas.height*e,t.canvas.style.width=t.canvas.width/e+"px",t.canvas.style.height=t.canvas.height/e+"px",t.scale(e,e)}function f(){this._subscribers={}}function v(t,e){this._options=e||{},this._data=[],t&&this.add(t)}function m(t){if("undefined"==typeof document)var e=require("canvas"),n=new e;else var n=document.createElement("canvas");var i=n.getContext("2d"),a=t/2,r=t+a,o=1e4;return n.width=n.height=2*r,i.shadowBlur=a,i.shadowColor="black",i.shadowOffsetX=i.shadowOffsetY=o,i.beginPath(),i.arc(r-o,r-o,t,0,2*Math.PI,!0),i.closePath(),i.fill(),n}function y(t,e,n){for(var i,a=n.maxOpacity||.8,r=3,o=t.length;ra&&(t[r]=256*a),t[r-3]=e[i],t[r-2]=e[i+1],t[r-1]=e[i+2]}function w(t,e,i){var a=i.max||100,r=i._size;void 0==r&&(r=i.size,void 0==r&&(r=13));var o=new n({gradient:i.gradient,max:a}),s=m(r),g=e,h={};g.forEach(function(t,e){var n=void 0===t.count?1:t.count,i=Math.min(1,n/a).toFixed(2);h[i]=h[i]||[],h[i].push(t)});for(var u in h)if(!isNaN(u)){var c=h[u];t.beginPath(),i.withoutAlpha||(t.globalAlpha=u),c.forEach(function(e,n){if(e.geometry){var r=e.geometry._coordinates||e.geometry.coordinates,o=e.geometry.type;if("Point"===o){var g=void 0===e.count?1:e.count;t.globalAlpha=g/a,t.drawImage(s,r[0]-s.width/2,r[1]-s.height/2)}else"LineString"===o&&tt.draw(t,e,i)}}),t.strokeStyle=o.getColor(u*a),t.stroke()}}function x(t,e,n){var i=n.strength||.3;t.strokeStyle="rgba(0,0,0,"+i+")",n=n||{};var a=e.get();if(t.save(),w(t,a,n),!n.absolute){var r=t.getImageData(0,0,t.canvas.width,t.canvas.height);y(r.data,nt.getImageData({defaultGradient:n.gradient||{.25:"rgba(0, 0, 255, 1)",.55:"rgba(0, 255, 0, 1)",.85:"rgba(255, 255, 0, 1)",1:"rgba(255, 0, 0, 1)"}}),n),t.putImageData(r,0,0),t.restore()}}function M(t,e,n){var i=60*n+30,a=Math.PI/180*i;return[t.x+e*Math.cos(a),t.y+e*Math.sin(a)]}function E(t){var e=t.split("|");return e[0]=e[0].split(","),{lng:parseFloat(e[0][0]),lat:parseFloat(e[0][1])}}function _(t){for(var e=[],n=0;n0&&(e=e.concat(i))}return e}function b(t,e){if(!t||!e)return null;var n,i,a,r,o,s,g,h=function(t){return 1-2*t+t*t},u=function(t){return 2*t-2*t*t},c=function(t){return t*t},l=[],p=40,d=0,f=0;if("undefined"==typeof e)return void("undefined"!=typeof l&&(l=[]));var v=parseFloat(t.lat),m=parseFloat(e.lat),y=parseFloat(t.lng),w=parseFloat(e.lng);for(w>y&&parseFloat(w-y)>180&&y<0&&(y=parseFloat(360+y)),y>w&&parseFloat(y-w)>180&&w<0&&(w=parseFloat(360+w)),s=0,g=0,m==v?(n=0,i=y-w):w==y?(n=Math.PI/2,i=v-m):(n=Math.atan((m-v)/(w-y)),i=(m-v)/Math.sin(n)),0==g&&(g=n+Math.PI/5),a=i/2,o=a*Math.cos(g)+y,r=a*Math.sin(g)+v,d=0;d_&&(_=M[w].geometry.coordinates[1]);var b=new google.maps.LatLng(E,_),R=d.fromLatLngToPoint(b);i.offset={x:(R.x-m.x)*f,y:(R.y-m.y)*f},"grid"==i.draw?at.draw(n,new v(y),i):rt.draw(n,new v(y),i)}else et.draw(n,new v(y),i)}var r=new n({maxSize:i.maxSize,gradient:i.gradient,max:i.max}),o=new R(i.splitList),s=new S(i.splitList),g=window.devicePixelRatio||1,h={map:t,animate:!1,updateHandler:a,resolutionScale:g},u=new A(h)}var I="2.0.3",F=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},k=function(){function t(t,e){for(var n=0;ne&&(t=e);var n=4*Math.floor(t/e*255),i=this.paletteCtx.getImageData(0,0,256,1).data;return"rgba("+i[n]+", "+i[n+1]+", "+i[n+2]+", "+i[n+3]/256+")"},n.prototype.getSize=function(t){var e=0,n=this.max,i=this.maxSize;return t>n&&(t=n),e=t/n*i},i.prototype.init=function(){this.intensity=new n({gradient:{0:"#006bab",1:"#002841"},max:100});var t=this.container.offsetWidth,e=this.container.offsetHeight,i=this.camera=new THREE.PerspectiveCamera(40,t/e,.01,9e3);i.position.x=0,i.position.y=0,i.position.z=85,i.lookAt(new THREE.Vector3(0,0,0));var a=this.scene=new THREE.Scene,r=this.renderer=new THREE.WebGLRenderer({alpha:!0});r.setPixelRatio(window.devicePixelRatio),r.setSize(t,e),this.container.appendChild(r.domElement);var o=THREE.ImageUtils.loadTexture("images/china.png"),s=new THREE.MeshBasicMaterial({map:o,transparent:!0,side:THREE.DoubleSide}),g=new THREE.PlaneGeometry(100,100,1,1),h=new THREE.Mesh(g,s);h.position.x=0,h.position.y=0,h.position.z=0;var u=new THREE.PointLight("rgb(50, 50, 250)");u.position.set(0,0,35),a.add(u);var c=20,l=new THREE.Geometry,p=new THREE.QuadraticBezierCurve3;p.v0=new THREE.Vector3(0,0,0),p.v1=new THREE.Vector3(20,20,0),p.v2=new THREE.Vector3(40,40,0);for(var d=0;d1&&(this.current=0);for(var i=0;in&&(n=e[i][t]);return n}},v.prototype.getSum=function(t){var e=this._data;if(e&&!(e.length<=0)){for(var n=0,i=0;i1){var y=f-v,w=v+(fE*E+_*_&&(v=w+(1&d?1:-1)/2,d=x)}var b=v+"-"+d,R=u[b];R?R.push(a[c]):(R=u[b]=[a[c]],R.i=v,R.j=d,R.x=(v+(1&d?.5:0))*g,R.y=d*h)}var S=new n({max:i.max||100,maxSize:s,gradient:i.gradient});for(var r in u){var T=u[r];t.beginPath();for(var P=0;P<6;P++){var C=s,H=M({x:T.x+o.x,y:T.y+o.y},C,P);t.lineTo(H[0],H[1])}t.closePath();for(var L=0,c=0;ck||Math.abs(s.y)>k){var g=1/Math.pow(i({source:S[r[o]][e],target:S[t][e]}),1);a.x+=s.x*g,a.y+=s.y*g}}return a}function p(t,e,i){for(var a=T/(n(b[t])*(e+1)),r=[{x:0,y:0}],o=1;os;){var c=s/u,l=S[e][h-1].x,p=S[e][h-1].y;l+=c*(S[e][h].x-S[e][h-1].x),p+=c*(S[e][h].y-S[e][h-1].y),g.push({x:l,y:p}),u-=s,s=i}s-=u}g.push(_[b[e].target]),S[e]=g}}function f(i,a){var r=Math.abs(t(e(i),e(a))/(n(i)*n(a)));return r}function v(t,e){var i=(n(t)+n(e))/2,a=2/(i/Math.min(n(t),n(e))+Math.max(n(t),n(e))/i);return a}function m(t,e){var i=(n(t)+n(e))/2,a={x:(_[t.source].x+_[t.target].x)/2,y:(_[t.source].y+_[t.target].y)/2},r={x:(_[e.source].x+_[e.target].x)/2,y:(_[e.source].y+_[e.target].y)/2},s=i/(i+o(a,r));return s}function y(t,e){var n=s(_[e.source],{source:_[t.source],target:_[t.target]}),i=s(_[e.target],{source:_[t.source],target:_[t.target]}),a={x:(n.x+i.x)/2,y:(n.y+i.y)/2},r={x:(_[t.source].x+_[t.target].x)/2,y:(_[t.source].y+_[t.target].y)/2},g=Math.max(0,1-2*o(r,a)/o(n,i));return g}function w(t,e){return Math.min(y(t,e),y(e,t))}function x(t,e){var n=f(t,e)*v(t,e)*m(t,e)*w(t,e);return n}function M(t,e){return x(t,e)>=F}function E(){for(var t=0;t=e.length-1));a++);this.splitList.other=e[e.length-1]},S.prototype.get=function(t){for(var e=this.splitList,n=!1,i=0;i=e[i].start)&&(void 0===e[i].end||void 0!==e[i].end&&t1?1:w,x=l(w);for(g in i)if(void 0!==n[g]){var M=n[g]||0,E=i[g];E instanceof Array?e[g]=p(E,x):("string"==typeof E&&(E="+"===E.charAt(0)||"-"===E.charAt(0)?M+parseFloat(E,10):parseFloat(E,10)),"number"==typeof E&&(e[g]=M+(E-M)*x))}if(null!==m&&m.call(e,x),1===w){if(o>0){isFinite(o)&&o--;for(g in a){if("string"==typeof i[g]&&(a[g]=a[g]+parseFloat(i[g],10)),s){var _=a[g];a[g]=i[g],i[g]=_}n[g]=a[g]}return s&&(h=!h),c=t+u,!0}null!==y&&y.call(e);for(var b=0,R=d.length;b1?r(t[n],t[n-1],n-i):r(t[a],t[a+1>n?n:a+1],i-a)},Bezier:function(t,e){for(var n=0,i=t.length-1,a=Math.pow,r=ct.Interpolation.Utils.Bernstein,o=0;o<=i;o++)n+=a(1-e,i-o)*a(e,o)*t[o]*r(i,o);return n},CatmullRom:function(t,e){var n=t.length-1,i=n*e,a=Math.floor(i),r=ct.Interpolation.Utils.CatmullRom;return t[0]===t[n]?(e<0&&(a=Math.floor(i=n*(1+e))),r(t[(a-1+n)%n],t[a],t[(a+1)%n],t[(a+2)%n],i-a)):e<0?t[0]-(r(t[0],t[0],t[1],t[1],-i)-t[0]):e>1?t[n]-(r(t[n],t[n],t[n-1],t[n-1],i-n)-t[n]):r(t[a?a-1:0],t[a],t[n1;i--)n*=i;return t[e]=n,n}}(),CatmullRom:function(t,e,n,i,a){var r=.5*(n-t),o=.5*(i-e),s=a*a,g=a*s;return(2*e-2*n+r+o)*g+(-3*e+3*n-2*r-o)*s+r*a+e}}};var lt="undefined"==typeof window?{}:window,pt=lt.requestAnimationFrame||lt.mozRequestAnimationFrame||lt.webkitRequestAnimationFrame||lt.msRequestAnimationFrame||function(t){return lt.setTimeout(t,1e3/60)},dt=lt.cancelAnimationFrame||lt.mozCancelAnimationFrame||lt.webkitCancelAnimationFrame||lt.msCancelAnimationFrame||function(t){clearTimeout(t)};T.prototype={setOptions:function(t){this.options=t,t.stepsRange=t.stepsRange||{start:0,end:100},this.duration=t.duration||10,this.stepsRange=t.stepsRange,this._add=(this.stepsRange.end-this.stepsRange.start)/(60*this.duration),this._time=this.stepsRange.start},start:function(){this.running=!0,pt(this._tick),this.options.onStart&&this.options.onStart()},_tick:function(){this._time+=this._add,this._time>this.stepsRange.end&&(this._time=this.stepsRange.start),this.callback&&this.callback(this._time),this.running&&pt(this._tick)},isRunning:function(){return this.running},stop:function(){this.pause(),this._time=this.stepsRange.start,this.options.onStop&&this.options.onStop()},toggle:function(){this.running?this.pause():this.start()},pause:function(){this.running=!1,dt(this._tick),this.options.onPause&&this.options.onPause()}};var ft=function(){function t(e,n,i){if(F(this,t),!e||!n)return!1;if("baidu"==n&&!BMap)return!1;this.type=n;var a=i&&i.center?i.center:[106.962497,38.208726],r=i&&i.zoom?i.zoom:5,o=this.map=new BMap.Map(e,{enableMapClick:!1});o.centerAndZoom(new BMap.Point(a[0],a[1]),r),o.enableScrollWheelZoom(!0),o.setMapStyle({style:"light"})}return k(t,[{key:"addLayer",value:function(t,e){if("baidu"==this.type)return new mapv.baiduMapLayer(this.map,dataSet,e)}},{key:"getMap",value:function(){return this.map}}]),t}(),vt="undefined"==typeof window?{}:window;vt.BMap&&(C.prototype=new BMap.Overlay,C.prototype.initialize=function(t){this._map=t;var e=this.canvas=document.createElement("canvas");e.style.cssText="position:absolute;left:0;top:0;z-index:"+this.zIndex+";",e.style.mixBlendMode=this.mixBlendMode,this.adjustSize(),t.getPanes()[this.paneName].appendChild(e);var n=this;return t.addEventListener("resize",function(){n.adjustSize(),n._draw()}),this.canvas},C.prototype.adjustSize=function(){var t=this._map.getSize(),e=this.canvas,n=this.devicePixelRatio=vt.devicePixelRatio;e.width=t.width*n,e.height=t.height*n,e.getContext("2d").scale(n,n),e.style.width=t.width+"px",e.style.height=t.height+"px"},C.prototype.draw=function(){var t=this;clearTimeout(t.timeoutID),t.timeoutID=setTimeout(function(){t._draw()},15)},C.prototype._draw=function(){var t=this._map,e=t.getSize(),n=t.getCenter();if(n){var i=t.pointToOverlayPixel(n);this.canvas.style.left=i.x-e.width/2+"px",this.canvas.style.top=i.y-e.height/2+"px",this.dispatchEvent("draw"),this.options.update&&this.options.update.call(this)}},C.prototype.getContainer=function(){return this.canvas},C.prototype.show=function(){this.canvas||this._map.addOverlay(this),this.canvas.style.display="block"},C.prototype.hide=function(){this.canvas.style.display="none"},C.prototype.setZIndex=function(t){this.canvas.style.zIndex=t},C.prototype.getZIndex=function(){return this.zIndex});var mt={draw:function(t,e,n){var i=e.get();t.fillStyle="white",t.textAlign="center",t.textBaseline="middle";for(var a in n)t[a]=n[a];for(var r=n.offset||{x:0,y:0},o=n.textKey||"text",s=0,g=i.length;st-i&&e.timee.options.maxZoom)){switch("m"==e.options.unit&&e.options.size?e.options._size=e.options.size/a:e.options._size=e.options.size,e.options.draw){case"heatmap":it.draw(g,new v(c),e.options);break;case"grid":case"honeycomb":var m=i.pointToPixel(new BMap.Point(0,0));e.options.offset={x:m.x,y:m.y},"grid"==e.options.draw?at.draw(g,new v(c),e.options):rt.draw(g,new v(c),e.options);break;case"text":mt.draw(g,new v(c),e.options);break;case"icon":yt.draw(g,c,e.options);break;case"clip":g.save(),g.fillStyle=e.options.fillStyle||"rgba(0, 0, 0, 0.5)",g.fillRect(0,0,g.canvas.width,g.canvas.height),et.draw(g,c,e.options),g.beginPath(),tt.drawDataSet(g,new v(c),e.options),g.clip(),p(g),g.restore();break;default:et.draw(g,c,e.options)}e.options.updateCallback&&e.options.updateCallback(t)}}},L.prototype.isEnabledTime=function(){var t=this.options.animation,e=t&&!(t.enabled===!1);return e},L.prototype.argCheck=function(t){"heatmap"==t.draw&&t.strokeStyle},L.prototype.init=function(t){var e=this;if(e.options=t,e.intensity=new n({maxSize:e.options.maxSize,gradient:e.options.gradient,max:e.options.max||this.dataSet.getMax("count")}),e.category=new R(e.options.splitList),e.choropleth=new S(e.options.splitList),void 0===e.options.splitList&&e.category.generateByDataSet(this.dataSet),e.options.zIndex&&this.canvasLayer&&this.canvasLayer.setZIndex(e.options.zIndex),void 0===e.options.splitList){var i=e.options.min||this.dataSet.getMin("count"),a=e.options.max||this.dataSet.getMax("count");e.choropleth.generateByMinMax(i,a)}var r=e.options.animation;if("time"==e.options.draw||e.isEnabledTime()){r.stepsRange||(r.stepsRange={start:this.dataSet.getMin("time")||0,end:this.dataSet.getMax("time")||0});var o={step:r.stepsRange.start};e.animator=new ct.Tween(o).onUpdate(function(){e._canvasUpdate(this.step)}).repeat(1/0),e.map.addEventListener("movestart",function(){e.isEnabledTime()&&e.animator&&e.animator.stop()}),e.map.addEventListener("moveend",function(){e.isEnabledTime()&&e.animator&&e.animator.start()});var s=1e3*r.duration||5e3;e.animator.to({step:r.stepsRange.end},s),e.animator.start()}else e.animator&&e.animator.stop()},L.prototype.show=function(){this.map.addOverlay(this.canvasLayer)},L.prototype.hide=function(){this.map.removeOverlay(this.canvasLayer)},L.prototype.update=function(t){var e=this,n=t.options,i=e.options;for(var a in n)i[a]=n[a];e.init(i),e.canvasLayer.draw()},L.prototype.setOptions=function(t){var e=this;e.init(t),e.canvasLayer.draw()},L.prototype.set=function(t){var e={globalAlpha:1,globalCompositeOperation:"source-over",imageSmoothingEnabled:!0,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"},n=this,i=n.canvasLayer.canvas.getContext("2d");for(var a in e)i[a]=e[a];n.init(t.options),n.canvasLayer.draw()};var wt="undefined"==typeof window?{}:window;wt.google&&wt.google.maps&&(A.prototype=new google.maps.OverlayView,A.DEFAULT_PANE_NAME_="overlayLayer",A.CSS_TRANSFORM_=function(){for(var t=document.createElement("div"),e=["transform","WebkitTransform","MozTransform","OTransform","msTransform"],n=0;n maxLength) { + maxLength = item.length; + } for (j = 0; j < item.length; j += 2) { coordinates.push([item[j], item[j + 1]]); timeData.push({ @@ -202,8 +206,7 @@ geometry: { type: 'LineString', coordinates: coordinates - }, - count: 30 * Math.random() + } }); } @@ -233,9 +236,9 @@ animation: { stepsRange: { start: 0, - end: 100 + end: 100 }, - trails: 1, + trails: 3, duration: 5, }, draw: 'simple' diff --git a/examples/qianxi.html b/examples/qianxi.html index 5f962b24..d6faad08 100644 --- a/examples/qianxi.html +++ b/examples/qianxi.html @@ -366,7 +366,7 @@ end: 50 }, trails: 10, - duration: 3, + duration: 2, }, draw: 'simple' } diff --git a/nodeCanvasExamples/line.png b/nodeCanvasExamples/line.png index 16f2f787905ea98b82919497b54dd91484963a7e..9101c854188573b5d5bdb5882489a579e06d1299 100644 GIT binary patch literal 109840 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

(uh67J&K4{si2LrC*WciyZAGLmov(cm zeco3z|zv{?cKPCoe==q zcJtNu0~OInO|_Yx?4nmTW_1dKduyR95fCte=)rS*b=|n<6-4GG0Ao2}b@ty(WZ+9R zrgmpwc9wvNY^(k-?JAc)X$D;zT41IOqG|qP*4naM$oMvTWQQR(N%TpD)m&wA$Q)c# zq6uowBMMw(?>$Q-!u|IM@-k1Cl!eFim$5NU)VRx<3aU3n9lS*ZS)tSpq;=&i70%(zxs-g zngQ&+sP@gM#-8tV8ERh}QEf+S(C4glfJy98osAt!nmO>>?jRXjm!|T{heb+7ftr-~ zacbgc@bbnhBs8?UNHAF?@W&6}$pj#qOsQg*NzWHrqh!h~K7_%;sZ0c}_c+o#Gym$; zynCw~(5PQh-?w@K@nIrP(PLT@JETft?CKWJT;R}wrr%1_9sr}jI2@G|551oAx+x2t zvHAHd7WUYqJ=culaoyB_0Ir?e{;^`A_$g7*@7xrsHDWFXy^3`t5HY3N$6gz>&!O z8LuY8szh?aKJOQ&M@wk`k5>85_N2RKFta+#<{S7Lp)a;>qwU2~+l82hh97*78A0^T zXXX5G8gE} zAu0av?oog7?8r&jj?&BQ~|3^ehZiCcc`E=dr@)zbboE;7V&xw=w=6bzs+tIX)7K$lRagoKH!A}2}Ja<9(Aq(I< z6nK~|(Z>YJGnOSs-fT8$H2jA!MLKcp09@Y;4<3>AQILhoApSgP<|-BJ93J-U-y;5c zx^9S8FnDpROWAF2)lkm=p+LMk1{c9pHgfkPUkvDQw$H={qYZuIJid{nq+(84Ez6Pd zzWtK%#-f~S!QP3B@6r73T_CL0=inBqMenz7aQu@s5MstrFd2hOyC0qCoxQKgeS@nY z{vz*tqo||eZlmt7vE|>)e@#~WPsxf>n=9%Kx5I#vtjg|B`dH8Oe8Z3O)#hAK3CiJq zR%h~ti=BcfY~c?S2K5{#+9GX&*h-&H(WvmJP73#JrD5du%_%~zY}?-u6tVlfKaRvP z{C;S=u|W|!Aal`oXLs@aZo_p=?^e|A?xws^vv8p$!{-j*=hroIPf28J!Zz6K7v=K5 z$Vgi(y)O2dQ%hMWn+hZFK4F6&0Vf)5X8bRCfbi1 z=bN)adNnNN*J*a%gqyHdop=L!4GFj}f-l{x*qO;ke$q|4w_FI5&IJ{9(!8n~li~WC z9T`0TCy=u}VUwo*=ua%Ynv|gH`iPAFb#8fvwhi(O6cEP9`ktekJ_!IF;uSRS2=bgo z<7Ew`xeL44N7;zjCD}?T1|1UkkXLxu|5KgsL(@94qNj!f9|9PFI={XMxPS5>uzzJ|f#YXZ`*S2}+Ea*>wS?|bSfq$weS zDJS>A_pHf{d}sF1b9nR?O%n=dJ7^Zk`Em6(2hb%8A z*U<-Q>CyrXA$SwCdHYn~dRJ_ET118@TZ5ipMrk^lze?z8E-B$-wMH4r_b=|<=v#hk zx3OcXL|ky*ZC;AL0p;RFg`(pc8l9r-wPXD9sOi|LZN3YU`n`pV0=L3o2e<$OAW>30CYwPv+E0r)2p2Ev4hUV#U^Kc<~JWvqIZO zuvDwvS?iVGv;bqn`M?jlxyIAGLd};6;|iT2%*;RGXbQ9}c zmF~mNIMrb~LIroHT5XRTyVR<9ky19xM_+bdng_4D0`ewIrc8ucW-Rn!ieH~O_bWyP zvbHQBBwVED_`9|pFYY&Q&{YYO&dV^`T|Q*;J>Nyvz1unSW&23Q!q&M+Haz+@-xx{!*))novK!y-%lFm#%lEz4W3_e2*&R&aIipc0zIp zE%^Vl_Sez!OU;jU#S=Qgx<1wG>cW($xe~@hMVDM%>xu>QDNGzHv-8oXf<8}6Q-m*;Zt9(3W5ek1aNpq+ z*1}~TW}Ggp|D)~QB-Qds&NOucE zr+~nHUHspB?m4^X?Cx1T&hY!a?>C<3^ALyQItY3J@n?qmW83bH8raZY&ih&EDhN4h zbD?hGyG+R3lZ44+Rl3A+YYH%qrrJ5xfRb|YcPg>)(tbP8Ee$1O;PiNbdG6tsb9K#i z!rB;9+d~Y=eBXKmQg=g*kL-b_Q&$I-7^eQyp+Q8~TW9ZWC4_BhykT*-VQE6t@O!Q$ zO)qQXpTWxh`Y}1&JV=Kk-;ROxU=CKS{tx=Y<4rJ^*zJP0I!XhtD!8cPoDrQ+7{AWn z2xyo$H}c^>|K7g7^zDs-VLLwOj8bFFSs|Y!cMQZ4|LKJKVi^T2WkpfLwrH#h(Xs-0COw6K!@g=e(nvF|)gDs3Kkz4<<;GcGm% z@aB?#sZcYMvmo}W<`T7Ne8*!;t;Y7~hQkw`o^CPCebI=`7ib2XgxGc|#3RXkP{=={ zQ20qx@Qf5sa1F)w*^Vk2yLXW2?&)ytcS02wzUvHix4i3%VL5Y?!t!MltFCckI$os# zY2N)9Va3RfsV89SaCB{XUQdR{C1Q0ULM~=q++~0el`1%?dFi(jvo9P^DT7kM9}&-3 zP%~PTZsm20sCuB6eOr~fV1J4GO`KVEUAHs=KV(nNs;IfnkeE=iJ8k6nn?l;tWAzdO znKtF-cRd6u3>d>0;#jEv^ilt(h)7jMz+4C#8x!j~%gdi=Np`zZ#0%Zz*5j`bu2=Ud z()u`%t5{+TJwSroL{T{&+mug$LjW5Z@8L+;9Icz}|56&3^=L7(3Oi*g{|6%y7J;1D zv9w5PvVj-!y%(F{@uUxD@O=2UMK8ketTw7Hc=;3_x9Jo)y5|*Y?JQ(r0od9lde+tf z_fc}seZ|-<%=wrXw^!Fa<_nQ1qPnX(c)&5&YIDQOo&3XE9FL411EdaD?i#3i0DKL7 zLh?+nMl)weo|siY=jUg3hn=`TkX5iF+>EMt4Ap!N0ZqdrW9qP2AX~{C-CU`E=&^u0 z{Gnknb?+~bKglAD=Fe$OxC@5((R^Xh_;mM%8yP0qg;xC>V&a3?pe|+T_&OIreUiH8 z&~6-5y!ZN7?R4GVOnUcW`0rWY&1?1;b;`YS(X5N7!yah;vK=RHef0a<_}iF9nb~MT zzy&SS!-9g)6#fHT*_*nf*zlyiua563*WP>sDUX4gkP9+OWzm-K*LqI@ha;ws4X!=7 za@k?W5%0^QwX5&(hn0Y^1*kDKB!=}zv^ZW{_>U`b2G_?0H6FW@EygE4ZEcw|2$ookbUgtAel^l+P-pc>yb7 z%a^B{m<45MduiqV>E5^u4-PxTs8Z^r{bbnfHR?`x9TrLb&WrisI0{m_RO5`$8T->!EyS79=U4|_w3L<~$(vd0FdDJp@!$5BdY>J8Nv{SXMQS@By zC-Xk@{{1W4*z2jt1L=sDqmq*>}DzvwsqA`t)nrF!e9CBb5@EXQGw|1(9c$cUM@5r$D=)~N^N z*VJHY!H8F!tv)Rc=M+dtdTbV4q58wJ*zV8oNL)lgBDup5&3Dx@(-G?_^&IJv8o83Q z2Y|mFy~3zka7FeVu^?U)N0Bo^ih}-dvi>C2a_UenNf8`v@X_-}diD3marY->>?ID$KvZT&;wcP## zF@Un|(d}sXQD+btqGzc-w{hZltB;b{hJ(1H*@Z4Q-XsrQ{(a`+elz~aNE9^S8)AmBLj8y>a@ zkkbLf)%tOy&AoXEqNFs>SGFO4d1`S#1b5O+M!eOC5+Uu~{IRG_C$FKNA7zioKGX#4 zMPiPRAAKV-+qi?iupp6H8fbS;8`HO5v2#`huffIL%bmNH8);!{?VjA2?iiv&Z7^{i zRBWd1gx-aJ|Ewu&<6YuuCFDH!mRP0>S?tT#d_XLE-h%^8k!DP#VYDh~6V89{fYS5! zGv)hxO8UL!LOt~a!}VZWrE*MTOZ_EImwBx9kG)lRk=B5taX?<{{ih5US2oy^^eX1~ z2()N*EAk1J^3wh1kfLND+A!-~J5EYSn-5gIM7_tgk2nK`=73epQ!Xy@qH!@@=s^o; z<*{e~Hy-uF`}fs%j2iG4FEva{DYxHxLlEE`w{RJ1dmI*@Y7 z7>?$i*&nWoZS=nq9HJ-wg^PHS^$Qph+SQb=Y(#I)_ts8Ilmlx$&YU}@zw_5ZIk%795e0t}SLxkHIV-P;hF7|XCg348M zT5qqy)Cs*uNr`$75DKByHaCvn-kLCAo40i2tI@%K)oKhPG^1mZ}vXr*yWr+o-+(I7SDQwL^J^SSK1ak5QZ*cA96c(Q%~d zHlBl*MQKYS$cI)XAlM7)v&jF&068B2q-ox$j{fXzub{xcvrArUDGBlTFxwM%d5iFE+Ybq=2;t>!Z|iqy+^Jq*W)fbRq+}L z!gQh2Ruwh~j+(mJcQIQl^|?+Er1~R4C31G7KV~66bV!Jcc;w7T>FF@2flHroY^$Jl zJ&A(ik1#j!`kxIux}q)m8&2-SIc2%8R-vB>8`vv0#!mToEcEH3X40h@pWNb$y1Z}q zhQ3P}bhJWBW?$qg4f|q-=vuS7X^z1s1Q{hDO=fsUG%aRMN;c_*jYr#6444&Db#3Vqip<>+L5bbrs7K4(?d1^1v5d=C=6Okz%$rzMcM7RJUvC-WYY~vfE24c}7q}7w+^1<8#L<=h?Wk#d zf(f-&#fex&CyTR1g&qOsC*hwJ&F96l4+LViM)#5#`|oH~zXT9hJr`mGqk)?^B=gBT z%f!yYCY}4l;X~zKHxsyM`OrDxw3@`%OOJdODHi#qxF?WbQUk0i}hsLl|{lPx=iO} zY33fJ@dYE3ex{Gph|nRRb5zYYs&;j<1cW#4RCwVj)>X}Z<2Iz)HDg;=Ah989!7FKO z3}lv!=m}k(dQt!j6X^4aAvG%IR}lEdYexSSKUtqD?>h2Bex+ydegOgKz1-ZemoEr1 zwVT7)h5y_uxr_}W6OKVZ<-k^tyl&QEWH$qLd?`F5;qYI@W^9t4QWSvOCicClzJMhMNf@-+{4LJt|nS{*$-yf79P{KnrfB#6dx zaV@&u;2}8R>pQ3DAX@%eVe+#_qq#FYYlFHt!MP?I_aa5nS5}O@(ooz_Tl3Y#I)ZUq zKC80ytM6LMVVAERRBL3j=sA%JNzSCiOOu~-flEX5i1o?&pEQN~H)b`?-5J+UhR|#=XOY78CO3 zTcB#H#JeKpmdzte4HA=Nf+s)ZFU}lcBN~s;y4X{s^WRZ@ZSQX$SEe6F?xrge*u{yN zd^9*Ub9*WUWN-Kp2AxJsEtY7TYml2ezpxOYeoFx4#gKmUhq>-R3MPE)nXKyZq*#kS zBHlKIXrhQy@PfD6nrCDfs-XG18d>@FvNy^e!z@JA)app zfFS%_Twdv$W@;D00%#4+H!qY$&%JZD>k)~JT`#7pDm%2mhz!pk>;qW=Wlu66K>R|+ zJ$b8%gyJQRy-ABL41pO4H}kzD@^{#E)=c#kDhj6XORVF+-SqaY=XtXB-yM2KijFwy z{exb9rH4ZWKlYLjRfNcDw442n?VlhvY0y|||Fm!U)8aoL)O7>I3{*BtPOjuEZLz!S zasq7FKY*=zzfqtaKwfTyfoL$MPl~B|$LdYN*t2H>_IA~(o2ALdxa%7f#-FY=f>+jF zwaCwmmcDc6;o>TFE;%r~NEl_)q2H&O;83 zqQy{5Pll1L?ziF~V7quiG@OvC-5iy(d`^>*Te3TMFSuPW348tTbML;StG9kQQMBwz?V^*avxn8(VnXR;URCo0 zN%k_I4l>hI%NBRD>HH{pB?s4|gtU0|2rJ7G22jRl%AjmEB6s_>x8JLgG_oS%-VgbN z)S?R~l3aIF>y_yQbJ@kVr=!+hWN=VRrsmv?v~dGH1Jq6;3fmt-Vpe4}W4yj$uw47N z5d2OSq(Y~koc4IrH4DJvbZ;Y={u41mlQ#9YM<&t?p4EH&zW)?+4wduZHH|)NRA9cs z3xc!1N}S>0qE$QNrk(0f`hR}?lGoHuQM$ISD@0-b?-bZ?{P&pONf0$D*81)UvJM&p zdU`0npA8t!SdH$91A@Ip z<%+NajCK1x&)QO*laPImpfy+urq7t*V(fy^hsy{%Zhgd(wC{v+ZO%=JBN-U&2kEJ1 z6)<<5D8P>T?W{|-07dcRSoF_w(SafStVfdums4dcQXUJ9#zW-%E~QF%Z*5bIM<}?3 z_psrHqhH@O7VSm3@+=$jx(|p|Nc3+wtCqC*yIo0zbS;TYx3F0z@g={0n2_K?0TcxN zkL?_82l(;0w=K!r$fyTk79sbya(D^@4G0(d!vMFDSh+C?CPS2PONrUcfnwMS9tsX^ zgw|)X(+&Q`di*TYP0mjn%%FS#q#_*PJ%Phfz^g1&cja_cxR)$D`0)=FP>`})SfoFX z0K6o0xszy|W&X|B1o`M-a-h+1v;asSZELqTyjE%<{yvJGk_`A*h=NMUM-aqFGF8?g z%*XqPn%agQ!;1timw3EWv1A?>7`H@|g};$o^DX_42sBsX1)dWKUj!Z(>nwHR`Rv%B zsTqD=+HDm)`upw?!{8`e@Z#j6y*B?Se|tsR|L z-?6$}zqn}42-Sz$lq^GChE%Awm=VE;OjjNZ3}h5L1BJoK#Y|$ND_R#-IG*rswiv)9V@i=&E9iEc_zDaRJq6`Sz!~@gK#|r}S8QMa+l!gC>BlN@5p@QZ5 zx7sxie#=I7B0>Dm!z$wY*yIRf9EYMuKo*gG7gXP7Rn0z(4ZAo9MWys>wf23o)uZ(a z=N+G`OT>PoQH(h_u@Zk;joOjyq!_COb&MufP;sRGO!+I2=h@$%b^1fEW-;AQlpBN!1>FCWFt67yiPP0}XqF^la#CS`<1< ziLrs|W8iU8;CyfxzX7%)ulI`^7OyWqwk_)ivkQlhi+C&`j?4kOqib0QJdj`2!z991 z9PCN$Q;Q1|qNlAVF#ps>)d^4_RAdj!B(X?WelkwhjW(9(1STeaT>O2IuI$E>gf`MK zadBRJwteuiXG3h}DR(^fT3x@@MeUwVu8tSi$wePSeBhabQ!u}D_5owk02|HycRq$J z9gc^#SKP;h=A8Bat)AM~Zin}!ci9=A7x{W8l=blvXyU^euQ%{K^H=>>D1B_-{L`N z<5?ifux0hq$XMPb`6bgpzZD8>ayp2rwFe`eflJ^# zJ4P=5jiwlUe4bC7E`pY-Hy4i&2T6ZjpUA1{p1jlHyz7}Tfge4QRJq94f>z@C#B$m& zprUaZ-}VoGj2x@`>CegfF`2e)*Mt=A4s~;$-+wj~6YVQpg&AkYz`BDrJA1)EbjrJ| z3`D&AA%!3$R0t=mz%nlx`1)zIs);iB1<+=yJwLA?Za%iQ9eoeZ(u^^`e}Q%zwAd9t?wlEOA|gHDmE@ndaL=mO#-IW8SzemDCH?=$AR+8{>$2|Y^O3n6CB*5 zU6#5`ydB8ay8Ii+e&)vx`P<4%hA8QFt0FnWT|rGQ zmu(K%%h~ED)mGkn*VfeNm!4cz0vKv#R`N$$EY!^%G{Ut;eNHhx!NC-Z`%4R1Gm0qa z;bAd7YtOzG41tp>7qw#oc)8uvO=8q1SmJ! zifY<@pXOc(WupQ!um8iZ>Z(01Ggja|F}D4pD#bt8-x#^JCY_L2hmYWBw>96Q=Lf|^ zmLmn+jbRK79XnpU_(PP;N0M3V$P9L@$x+x9B|9?<68V+!YI&ak!z{B3Kjc$6oldzZ z-Hye0u1v|93)NbAzEpr}c*olE#S1iqyeErhCUS*elGom;A_sH-F4r1PJ3O z)UDa0W z_MQ~Ex&7)U>{OP}0|1BpXf&Fug@T)bKscAx)+X!hF;dcmB-QRRp*&5pvtTc2*x}`O z9YIw<+yxsF*Oiw^Zye^#Ilpdz0*ZtI?>kC5uH;`<0wLE=+E%E(s&|6iyk%Xj2)$W5 zNB19Lon8xYRER9s-VJ+_7EHwzmOtYet+ZhwD{zDk31l#$)lYdQN`v!1>HY0tWO--& z@!jw!mAhCt0V1tG!`JO}uaBIYbzI|pEpM7F#p(<9Yu{{Nk_PmNb{Hl|(v7>3OH{$XXpb2UNO>s(G%heK{zKK0mrun4_OF>3H1sLEi01|+oL}V19{gwAPUsgJd=l_aD2=PMVlq|cmn=!1aM>n; z^FJ8ykq@A5j#llVPFH@{Eh}GY^^Gmw_rDbrdh#1?*-p!;+V~vOCW*r>L;h%4W%D^j z>-!@yD{!pPO@W9Nqrx{oi7_dzpB}p@)2;PacM2&q$N%qXllT8`61T+E5yF=oQQkCz z2Vu6Ww!pNlQz(H{St;9*89Rp# zibgzC5FIDGad2gJcn&g124Cj1C0?BlN}uw)Ykqk5_|nR)3CzvzBuP`uea{p(n01VI zwZdylUcd;%+{qfdAk}jr{e>%8;c?z~3EFk0m6g(ZbJYlt(D1x|j{ZlzG%0}W>~R4; zg}+bTC*%onY;PZ!SW6>=_^q)X(DS?eP;hnna8wVbD&5#z$7=y3r=VIq>R{f@K^Vfb zRM)Q{;$I8{_hw5AG!Mh_`su*6@ z`Y&-T26%Hb64+!+xTi2v!++_H=m72W=?NDiNBzz*j*8n7jueszes9?&@za6=;W#n6 ze`<;*l?QD%zSvZVEOA-HUgVOi+VATJK=XgP>q*_`|K1#i3Ijv{FdYKOstwqQfs;P#XbG`qSTywIA14Xf`vxrTb{r^q0By_kgXm%nkUx7 zGtubyMKvYSRSf0nCm~rzDTcAdVEXH@4-L(ca{g^d;UO9Z-_MykSx!&Njpm`2kGhX9 zu_W^juCRO8;+I{sSM6ZnqxiR1;f1bw&vILEH!d6;2m(Uo_mPq0#MCQVQu=gMQ7z*e z?F$EEcWx=r=|~|pFw)4Lxl#j^%qs|{7yRS_{(tM!Nut}XyO^`l@T=4_1S1`7JQHKZ z_C1<>(bD`br47BxPaQ8kewvpk1k`Awy&lGV49X&W?HUOHYy?fybJU3P+TY2(m23X8 z*r~^*$ZYk%wKaEW3eZ89C`7yR_PEm0RFU@o#<3Uu;QU~L0%SuSTrmQ33-IW+HG3NW z3dV2OHGD5>qv~=ZNGhDLd*Wf%Q7EIWl?7+lXazEO;W7 z2jTJsClB44YE)(2xZW|ba~z2O6>iIUt713J-vxP0yLAYewiSeI8rNmj>tQ-(cadKw z(;&wl&F4OF1w(c~{@ph`^igo&S(vg+C_f&W|Hwx8fsl^Qsl3sH`qjPzRj+4^IsNhk zof+FBn8dhX#)6nk2_nOz2~xDePpj)VCxCmqv}UEfZ5#?P_^?B0jg7CL#P&XMNs~<^ z zURcI-uzyy(;fkl-79SqA3g!}gzrXW+N`Y9_vSlHs^=N+}2WTT>_ZxxH+6qK1(_TrA z8hmJpm_iJmY7-?%KhAMVLGD(1K6?qB@D&DT; z0se^dKaicO!s1UU4zGWAh1~Yz50?NpDG^fLmm3lZ$ z*LyHH0flMddxvM_K6Z7g3Y!m>JwDMV^ESCrf`1>8{KUs~GL3;A3<_qT4r$qMWXv*s zPHoSMLKlVRPZRrISv44(GGJrhH0v-*QhV)29jIFy`beSz9fM2}M#pHv_+Vs=RCB*0D%XySbq zZSAyK)A+tuO^>@$h!5H)vLos&)SZ*_#@I$C2i;QIe!=! zvRk$*tvJq^NB?YSM3aQcaH$&UF9)JT&Zs9Q7P@o19^TeKCh%kaa-yJS;cC(rS@WQb z+=(?+4F;#6v4k1ukp@P1ohzK=^tN}^1s$5t<`oz!MGu^npq58nu zu|6OspLYay#Gp7N0C~lG^T&|+214+|?`g|1V@4yR#}EEOCp1C-bchang$Po_%)ByPx*Pvw}0lR(?;z0t=)m?Xn9#~ zFsU^EMCxAZtw-yzO48BwbIG;!qIW{Yo>9i&+lj;+24@KXoq(0o-nEMGkamfhnt|z6 zZrcg6{)c(nPxU0$*~VwaCV{Ykca+m7+H5!zc~5=sBn@1FIQg8pW{M)vens;(CC)2T za3#c8-3>D3z78oXqacwv*xpc9XbfIkTcD0e9@0x4#j%GSV!Q_ym$gF?U&n6~3+>Cx zHuT`k#)JPeq_o3th3K~USujz{P3q#uLQa2@xybw06wH2&7fE#iFwxgZDc?N8)_I2P&a{IOMc(fHunQ_uMN!b=Y{uFKlUO@nx_no6>adQ{$2HfUF)#3a~Y7U6V8=-bv0(g??Tt^P>5Z@dBBVjLq z6l)X)iE&3vVPTee5Jl0q9h;+f<_z7OlOnj`4aPxA4Oedo2%2Gv$)}j0o3vvO(X}4L z$SMo7A=P9cQqjWjUH160hE_Qw9)Ct?F5>-w%Odcn-+=$c?EQ z<2NJ!FCyW#k=JPyMsm*VH?tg^o%!? zh@zVLj`^mZno+Z7N82xi_gg=XT7E(=5(gr9Fe6iCRIIO>0f7vDoY@|80f0mJG{AlWo$#wzoq;+gqB0=PEn zziS>HrhxIIK2()uxt2F(wcIf8-lZsBsMHqt8zP|eEm%hw5b4(g&C)l$JL{WLKZuntb_{qJ`kAG2s&1Dc|- z;)?5%#+86>dHHqCPO362GF@O`JgA3Rl6_K-@m;C5^{^SOGA37e-RG4U04;;RI_#H` zqV@{gmRAb{UH>Vt_Hj111Q%&7bU)#1{q^-6Z-#z27pLX=4nM&7AghVTcFCt}a-o`53q z`72vt*QW_TO|ap_pk3XYs@T|g$$Aw8ArTmuG`XO>|<%{&`b`VhDdb^j<41 zY7+Q`n45l36s`<}azu}EF>Sg!L#`^v75r5-eO@FoZoiSZSxl+slHsq3EwEyCa8~=7 z=@NevGfmVj`LC4Y-@wrbAoF|aA<2!frS91%Tx-+uH>w%rzQs9>pnj0QUn;aG8qP~bfk=F*!uqWh{|Y~b)SCfQ~x z`t;9zYPXfi<4TX;di*Z?_d5@Sp^Zu1oeWc6^B!C^oX3hQwPnTJXL79}#iVt!ow}5d zTYF#`j_H-$mzKT;SrRS?5LJS~96wranCcDNkq-D*=cpGi17ch3X^r&{^n z?Kwo;f}-M7qqpk+x98RNmM%-uqkSbeODuu`A2PqIa7pO+b870%hWPl*8BbDbLQBY2 zR|LMVDBTlNck+nEOM}{7hGwyl4MpIYz^u0RkHQ|!8$4Ay_JmRO#U+&>FdvuYcf#la zLE_p^YQJu#|(kE_y zxsfLFiytVZ^0A8C!83%}waf~*TZCX`C7J)dK6<$?7(di3%l7vL(+WT6t)gST6LP3> zTt2>65!4|fs0P&nCkw>$VO0`#FbuX1(SFbF6o3L^A#*Qs#nk{TY3=76 zolEWMW8?^{&ra@3ZeJkEu!8~7jp*^?mRF=DG$J>UoDMl|IWkf_$`iaio+KU&fvM6Y1 z|CWijS%mFxLt@!93xm)~WlLV`Av3(FUbZ|no7#+}`k*5-o`?OowpcdgS(|~;5Rrd7 zd2!PzkG3-z(8NsNf0b}`G_^ZJ9t_0*S#x4f1D|^NgZ5C4M*lv1JI2_45W{SG2pR?^ zb9$Mk^Y*kH!ZOl81IG5D#_m}Fyx`Q3Fxy9 z8ll2AF(4_`X}2*nRVY)3f*@!p?qnikGZQtLufzNPOz_INA$#aQ;|}A$86D+FeidC2 zKCI7*Sg!Bna!B2+9=k`$qS~x%IYUf8iMU|J)$y-2x#`jgEIz3Eb00sC^(%^-vSgEB z#*Fo$WUsjrEi6pY1t4~?G-Mc~ix6TMlR{c^@~>(K*Xu2<^PAn5qUN4BTW^@nq3y%U zR9C36kVssaG|Y<>cpCrOQWI*rwhAXLKGZIqNZJWc$o=5yg`37VI5~M%X0yP+#YIk>(1s!c3sz z^xb+AV?R(#qSVMe%rTR2Cm)B=9T&3OrTqE9Jd&1(^q*dn$W{+i#fAh{&TMkUqRU3M zl5Vjo{GLen1MFVXP-7rXt($@dhoGPYdnd<34N4@-;_2_Fk z?N_~M+Zj{+=cx`>yug|XMZe!>5FKPjT&CUZn<`{uJi)3)WnRXq!boj{N%}c2?LrZ_ z3cvzlyy;boXi-t;L-0voe3O{}PUK1i%f(I?2#DL&h`OQ=Wi&g99%yEkRnmA#&i&TN@KJ$h3q|zs8lP-ZW}6F;qN&aE0`Z_USvU;@WD!Key z4mNcABh}#hx84j;tnOkMqmCfd)aD*KzWiYstWJi?&W^RBc!%BsJKUzeox0O!dW zF|3ZJ!)?#5Y0Rpi-q_j-*=XFj zK4`W{RpSxK=FW5n!Zc-(vC$82J-==Jl)3nIdFct`&Z2EPD;@O)zC4SMmsmyPO$=31 zq&0uHKD@xR*KKv|2q70u=Qyg2`MF2C3MKVyOw*tZA?A;;>0DoT5hFv0ZUl|Jy%a&c z2iCQjWA%ZCk!y7{W^c%^?IBcY3JhDJNV=7Q;sWWE6C%$syo)eck9N1?#9UjN$i+PG zj`^AS?hMM1@DWPoOeB%)90_Bb>>wk2WFv~jXryL%!{hEy0RYfQ!R^i$gDR$)fY3^9 z2;>R=Er49gZYLFkT$UW6S@>B{Q}kJ4^R}7<>_+j55BuCa!1CGL;A>n^L6T!coG*I_Nf1 zXRW86Ah}WKBYujI_3~2Z&ozoPNK?M3CXZN2j#~dCL#6Vud`dGn?_*!_hf>eW;09#j z>gLH}qKWR!bxB$2{)~jmNP9|9XT?+mS+awEI{$8Y^Cv=Rp+CqAtG4+&G+w~ z_J1x;onkzGlPdUS5d_ySO3Pacsea4=>$$lGwk4m(8R{d_FwvpYy+9Q5-;_J2zU!nF zS<}5B7U*O4UddI3MX`G)G}8L)Ak*Ebf-qc0C6vpqX?XAUxxwPSZFoz&Wa!~iYHAts zY<4fv5cyvQbo`LHeInYzHtj**$sy5Q)Rz}Zc<2|`JwuMY9H}Eh2a}cDhPIXvbDDB` zY(AYW(Vi8WnlqG-^!G+`J1vr`5H5uPSM$9_zqlmt& zxuk(M2B;#XXq?nahNp0Xgu<)=KB3R9g4xwOm$PBN!q!dc8xq<>P}3S zXZlWpD@YhhI(r_vVU&r4T~Fg=#P6?@GB8609y;A|GchSc-Kl0J8{Sav4`oArW_55HPSl@d08Co0U>AUsDlp2O%2P9d z^?(c5KvWqF83{7q{ODpVE+82eQQq^abf^5_{M&uAvw&9QHAR46zUy97IvBrG|LrF& z*0qsP!LC$AVgyuyhz{}VQq8w1D+0#)`#{wz@H313h|s$tZ4mr(D{;NW^!O$s(3q_sdlY%2u0HH3 zE1gZBAs3xxinP=6rl*aX&-K;($v3a;I|v(Ulqf9dDpFrBt*st8+6l0jSZ2vHvN#;N zcCS_bD%0mCvweI|1oCaB0C)SVReh1G>$5aB^t4YGHKsKbv=Pboqg<>?tQdoZe#G4Z25nP)*E|Be! zdog^dMhOLz+|ToJ7s#7Hg=Q23kz`~(EGiTQ{A48tL8-#i;gaOnp{_!Yb`v*-`MTIK z<+>)1%l}o0RJYf#V(Tv*0lwJYBZYDX8+3{gsTe$HO3Ktj!UPq?r`=1JM#45B3dTY( zm|&GgiG_gkuq5HqWsVe;eJg9sv?S3MO(L0EmDvj!r&dO|*SvY#MSb(}6N`7OCJeFGnZv&kKUup*&q+suCD>?jD(LRM=We)fV|aNA z;fM9(h+O**)=}09vjEq-XDF2nHm*M1knD!XS{m ziJWBA=R!T_Ohhz&22Q&?oNc>`N-B8VckDKl@iR$a~HN2xr& z%g!>+o`pMz1IAb#MONonMk_PuYPT1+-7sC;?UmxEay#JK2wECJNyW2MGicq6TBHJ! z%%g9=ufdL%7S%~DzcIpY3Jw|L96GCNJn$4ThLm)`4>1r~ zNAh2IxF7a-8BG)?Kf+;6Z}}rY>Z|yAt8B}#QczZWOTtjl|9vMEJ*Kb3U}F&Kv@?>u zm!0Ky+*sqKB>!6In=oV=SMuUg@9T}_LyrRBNOc}I{Uf4R%gw)_^x{}R&4-l*=LEF^ z$;q%kd}E(ssndr#R1np(-FjwGyGsP*b&LeC*gJ@b*q|&rlagSq|Lz|>%$^3wJBJhZOkA!+lZPy<;Ah^BQuHQ7nMQ}q$TGblU>fr zV5Z3tP4A`4;YG+%{5Z@VmA$r%tXY^)HGyJBkP7qy%#F2_ycW27bFl(xv+%8xHED8( z8+IJz$~v~T5L4AV)oqs_i*!Kltt)As|H`pyPkH$F!RL%E0?*8bt%G#w+o(wHg9JGXUzEPUuY7ab9T1&mWREjghKg z8Elec6VJa3KebH@$G)iiT@}BYH}ztB`sBK87g1?hSu$q3HB!v}@BUT_xD_3|;7B>W zCnT7}p;%eX{fh6WF=s>11{*D`F6Q+TPOlNmBd1+IbDpTXn|}BhkphrH8B1PAI~!Nc z$1&D?jA^Cr==OC>945^Agms|JAYXHKbjV#2D|BA5Zf+x|6VV=X9) zU~X3w;5If-PP1Wdy~4&LQU&Tmc<~O8WJRw`m>sIKBoqwPLYmrGyQH#Zn{?Wt$FAVp z7z6=7xCm_M_PwYngGBOlWg4B4T#?>i&GIt|DIJa#AOLt}WdSicx!dL%P2f0S+&MdY z3GfTcu5JoqA<;fqo~-=TBU7*BC4@#&xX?P6Uy{1%7*XPcc4=@5xidC6wRLtrDtY$} zk2d0@j#sP%Q4&e!9wW7)1_Z!AxHe>lVxrz-lO!D`RH7<;%=6E>n$G=TwaoLu)`3oT zp6yc9q#}ONwP$1e?BwP%QS9&R^kE6qn!%B>TlS{qhuhcQ#M=-hg`8yINfLC2ypwWL zw(gHPpoTLN1daysLVxiGcaxBvR2d{~>Z1xx&suJgIYo+L0{Etv-x8G^Pg%pPgHRP*-Bq?2n)lRA$|6 zEABG0!ngV9`9ITP%S0ataP?vO)t+uh`A(IrS8~ISLSRPptZKU9k!(e9u=B7bkW)DV zcrBDP@ocQQCfi8BmMPP8&pt!XT-y&-NGA3l`5{r^~zT z`}~l5`1@B(@Db!>g~>*SK;2Yhy0tSP1HC3Kff0lVE;)S53Nus~u)#=LHu6S{HPXjg)vJ0mkRNr&)V$Ca>uZ;v#ouh`3_E`{r$c~m7 zx2V7*{Ob_?%zCmC!*tXJ?6Krf0jy{HIc!Qi-+^n)+`0pMirvrOUkEsNK4h78b$=!L za01y8q!Wjn)9pF_}$wHgTtEIE3; zau>wpNK5jh7OZfF(Kn6->Y^}ae+f^g*g-;1g50il3@hk&WuJ&&ok^9{fUPU{+%EVQ zh-9}k(&>5Ugc<|YZchZFzX6K?ITlcQx#&^hH7F+`@?8_??R$DAzVy(XO`1O7Xu2sK z4L%S?o#XHnQg&f5vb2Fh*e#PvBgI8mKSC0-6>Zazh z*y5Z7R%c7_v>w@JrJHj@)i?Y+kmc`XCL^A#fFNMT`%%6rZM%B5=`zr)>e#fA<+T7l z3I1h{Y@XrD`_LUYoDE240{xa$xTI!D!pbdDUI~skGK)tN1}GkH3_5dt3LU{o}Tpedpz$37F1Ftk$`47T;+!3$Cpf1Xw_NUU>mVM&vw*U?{1Psy>=rQ6k3*>_D5Q zJ%5)i8Fl8_wf5~PtX=|)1NTS~ecl^jwUqyz*+x*G(B?rso980qdh3-tZY z{PBlgmpj&e@(x%{&9y>R-GCGf&n`bYsP!H|>g&-Xes~qK%7}+<-4x`l6m}=9Vn=`D zJ^$Ok@jfRq%-Hb3!a_Ts9w`^lRKmJbV`C}7wgwdU=MO*8m?w{&_dz64u{-Qm9UDa; z3*1t+PeY2n4V@q#pxFB<_%9<xJc-w-J)j#=n!%#6mq?(r^P5C+9aDJVoKdbAW zf4Z}HEmn7_Z*i^d#@V5<51bUt6vf@?GG94t6}5sUOrko+%_>>Vtf3+tm=^G-NL$|675-B{96r;QwC7c z+XPE1yit#4YAwkuaYm%#IYa+oDDJ7NeX6B-M*OR`?c;?rL~gg+*;B8|uW9Yg1PiB$ zcV1-L`ufgJYq8Ju08YR@-586s7z5U(>!GX$g|%Nj#@~9NNQof~oNvzkt9@d|42W^;iO?(ZNmV#lB4Zw4FI%Y{%sHaUc#+FLF^@j6|8Aesj$oE z=b)%bvm!@XEtn>gr8QZ(S=W_A6SOJi0BKDZJ-f87IPggA(w3{LBh505u@5rB_X_8s zY#O-YVO-5;aV*XplC~J9e7>@)+_AC9ohq#a1WUnIgZS_)>EWJATD17uVU_KAf#!$l z(pUxL#*?-n(LnFVSu1^J&ZvJ9Lqz_&uVRhA+l_kRVWV7)Xt@_eiORr9>FvW+1_o~c zy(IlSti13=Dd zb?+$y&Wu&{`IZqdvlnc!{YW=V*-wG}uYK$b{#&p=(0?bR9|iS)B^`hWD|0~DvMKez zB7pW(e0EBuFUbP`8+yAKlt1Y)cmkjs1VEFtk7%!0u5Q70#jeb@*NzHr-JU&;oH?nTEBQg%r z=gY+W5$MOX?bGaBnt#&g<8!W0a@Ro>Ns`gl1HpX@jM_3a#wFy# z2OPx(7Y;lMA7ls+&PEyyv9X57P9=RG;9+Fr!V6B#@ux7}tp~Q>piySySp~jkuk)G@ zIAUbng)-)13BQ)?qK>&**S#&9ETCA8Vc_Wm8;`ca6jOH*+C$*WWl_`7{ww( zJYDd|rlgnNtEz^NfNtZtiZL5sr?E2|>n@|5A}zRNr>X8pN|*Fe-o#!|%EN&K*C|8i zvdQyUAcQEOu;IqSYWG}b^?2TUz=`l1!3cae&RfWw9QluPG>3uE@~QS)*&bzSz3zA4 z)tp|v&V>ykgr9SJYx|I7ND1}tv3XwY`zU1Ynzz`pQsapJMVTTQl>KF#xZ-V?<$#yC zZ=#o>Km#mXK}-CMyCkzr#VcPFY6Xj=)pA~I(n3SC7L z#@Hboe4j$^7A8HAG2!sKw~z)ZdvQS@N@kBKYwIQ*TJ%>U#ACHd%Aoj1^9DvhYNT0j0vY^D-99 zsVHsufs8NRn8y2Qv<0-e?&Cg`{{?(Z5mv3oE_PkiMToUL15fQ9u@32;Rk8jl2gWcf zXAIHFCcPXY*{nt7RQ$`(l-~&wP>K{mOU{ZDOO6BAqn3LRzHGqP*EzfNH7R!^%=$t% zMj=fta|%4qZxUy5#ouuJtvITa9lXwOvTq;z=Ov_EV%c|5?Fv&l^RjRqFPL#|vr&{7 zNkmZg$H~csFHlEMy^K*_cTD|tS|Nk_Xsb+Hs)AZ8S@o`r2@BJpKJ+>rFY}71ZOKSMW&uT?0-L%|%IFT}SONPm zJN&HaTsS%i1_18G>A@PVLKfe%bnafx>m_)2&U-j*?E=KM6^mR3JNGeU4ujwrAP0*x z##mr?fPF)yG&VF!<}YpjNhQr~sgERD$}nWN;a&IXc`uz(L1SuS;sj-@i^B z^mQ$^LqV|ANN8|bTH~-GD~d@#;{ec&ywvSDUN;1Oz@?U zqU9*rgK${}Y%BWR9Rt_jSOS9e^=M)33s1yDOcwOx#gJ$w?2_lh67iDHrng2%VY%yJ z{ecI`2NF?O`9Z2OS+EQN#8c>joDasgCsKc#R&Jl$Q??V;mR*=HC5K;RcB;a((A?WQ z(`kGlkbh!cwfL*~S~zF_JE2V_#n6@v|Jx-hMySL!mP=!3e0X==NMyBK1al5?f|W=G zztj(lp=2O{ddnJk1nH0j?O18%)-N|O>kd_3ecaw*FvB@FYbhBxa*h%o_p6Kc(9Dit ztp^yf4zCNy6BJ^1RMs0GxaFiZc?kpRt|_HgQdm@6T)CNC4uo+?x)JYhr1aazyxlJ{ zOFO#1c&;NY24os`E_qc{Zcg^->-PmrIy}9zvmPLUELB^zCyh|-YG)T8Y=;}Yldu~> z%H5q+foAr}lrGq-CuEJ-3@leD9+A-8Q;K-G&T697qSAiM0HPqm!m05^3;tO_El&;I#>MC z0M*&QsO&zc{@aV7ApT+qbd^EZ2V~6>$LFkWiaR5tc(mBUNnDLGLi>}Qn}&!z7j{qx z>!Wa6Qq>5^nDMgzlQXp;Zh}aYh1gX1WjqZ|Y`zjzaB1wY%GcEQ zjeEbBTI*D9_M9oRAjF1H+N3LOeD1&1$r=eV&xeGjEf*-}XuYbUAQdV+rXt}#F=*Mj zBU4jt{SyWwmW(R)G4-opJf3=iNcib=iRFOK5qmwZEk^v&Yc|GKk)BYHNPv zpnziqVuwgs<0`pgNdYsKQIfQ{PIcDN-lS}VDayUeCUQ!$;0ag4g$Tn5Con(}ph7?Q zD))e1v%mw`bI^s4h&=D87s6_VZ@5uDcE}w%;;%KMeh_FjUEM!X4VEU3vx6iFqk+XInEL0c<^un(- z&}V^4c%)?@n}U&(kqBsfQ@U-w-&*3<;2$7QN-;25SxK)%0OS}>7Euz*gTKNVK#%MO z8nIY!oH-ok`ui-)FBbYS5(+vd5hj=C9!K@2gTE|szz8Y=aGhrk&Ic0O=N*0bP2{Mv zhaOPd&5GsSDj~}L(Wxef-0DjhQo0>oa2{Fyan+D`q6LN2>Gl%bk#f~+z`zc zF!bsH9t;B+Kw;NzuKu|)A&`sVR)A`6X=dl-B&G(enx_at<^s(mqYH$~x*s=OiX;udOh9Z4unsMM-U+l6yfY}9VBCy)T zH?P>D4WBV#)^o3SW2iU5II3}>$uKEx_UBRzIMLY2(eSSwy#hB=v#Gh1rzR%efO9@g_GrqwHCoXd6Evwy z)v@%K{{k110XzAPMMOVNx$V$Wi|zqM6?eE@a%ZT?w1hUptA3Z7?=GvWb8Zh*ekz+H z`f=VZsVFuKbi(4K1O2I1%L)#zQ*obQcYwicVnxMgi#OMBrg&dq(hPtiPdX}h-5B_V zygcnTo^&u7OfGdsHZ8WGF33DF%dNH|cAc-&(D8x{znB~_9^bH+n}!J#{Tv;= zZktr^<7Z9@7jXQ8kJ5UoN`K^eW%-Y((K3EJw?*6~(WF{?s6Ek_NTZdaI!X=p2U-K# z$13m`wJ#|)dm>FtmCX}d^0SSXm%jHx#b~Y;z$`LDwddPWrA3*wa(BkUI#W-Nk9gKz zc=Mz~!4c>9#CDtAC&ri$`$On`Jit|RUG2}|in`{fXM8`8ov^ya@n01<6SA}0?zW*t zB^sDXHX2wf&_0bI8QUu!I)U`9=zxLBwoe4Nbt08vhm=^7s#l>0Etp@(X=XZED9g4b zCZ^@6jSR^!a7K_^rsWqc_$piNH(Ivs|AQty(IvY101ZMiRbFR4`Yy8gR5fqmhBmZ$ zgadp%*xx~P*t=)Mb5pVKFI94U<{+sO-tyM&k6O5qnXe1D?o5QrX>+j+ zHR5T-ijgslWlhybvv?xE($AXK4Tp54$3)=i)!Ml_EZwt7SJKE8V?&6Wrn#xE5W z9;?Sih$z`|_2k4Md#tEquHf?5{LW~C*KQ|kZ-x5K&Qj>c?f{PZvrv}xbz5737g%XM z2$2Y`^@ppVifY|obJcyezzon6&7-5lVA<1=7ax~uY`1$X&s*8E!g%%HX}xZUfle}dIXwweb+S=oY_bI;~oU3rLT6)FRRp253iX5c6TuT4>1m2w#rOX>_gpxbbG zs*w4PC1=S|<^`=&Nf)g5HCu_Y8~Vt|+dI%s9{b;L0FdX5m785-8_F2Ws8)oyXlS^7 z>FN^rfM*Tlk^pz@afclgj@VC4k>__zROrWD4FwG)QnT|XeOixWZ&Z&Xv&fUucvd=2 zXvS~N_>G?_E-hZim*gq^IATLBWMSWz4K#_emlyYA*PUTJ5r{W(T>gkKhuyaA z-+MhyZ1zqs3F~y#T|ZMwdK-wDg~AcRbd@!eT<3RX0nrjj6B*A`zWwzL zq6Aqt)?`8kH^R>2_VWFEuednDma{Yb-k_}5kcMWu^7)zsk~;p_JC2+untJO-($juc zTHEdMh;yPzj*P}XtTsuLy-8k9?@Fg+LE|z0iX5f8b$%PAi>x$&^TY90d?0um>)mo+lyc4ZvcIQY%*Yb05GuI z4=41x4$1|$vvUraI_Q8t^fmPKRF9o)sFJtm`~Nbi^X+NyFKH1EDYHLaM`mN4Gk)Jx z$=0?1RSosK+Jbb>z5*VeVAt9j5OEcVRWCPhqsom9`l*!9Eti8df`48%*(qO#7 z&Ed~%kjtxsjLbL<3>Fo;Opd`l$>geb9OIPy;Reo7;P3S<*3d!W5(yUXKxr~}32z21 z16MGeW z6wEA)JHo3SFzC>EVqQku1y?x#(JeSzBdgm#k zg1Nu5YS4Ax40l`bV8OgLWPCL_idl0Je4SWJoA(J&RT8ZHz595i!vQRfF0s2Z=@oSS>WDr09uc02W}lko#nx?dG=!4H zSSL)d%e0%&&C|uUab}=aaGU*)-*Z45a0=s|`@>HEuUTkWy#-lo#fWusD+pJ(Q-2Qq zNFsQlLRVo)B^w0irmFxHchche3sc<36JWCDtD4vL(Xk9Zc<3JKep&bDxG97Mrak_s zRlLlHi6)q6g**9G8E|IVxdZ|H=l+5E4+Ja0%gPR9-hwfJe>!W(FY-xLKrA*hi84HO z(qsh#^sm!O>I~Qp04E26gG5QSuc!CPn^JT6BsqTDU$j61-NM)~Vqb-(#df5E1oc?) zgm!)k5+T1`dmurj!9vvyp$*wqRL*Ni+1+J$xH)>O*+7viAM}_T9>tlWw zG#wT>lcQD?APa%3qI`Pf=@SRw2Tyvz_iMAX6cQL>km~kus|Mir!9(EtNvz{L}-Ombm`{>^z{A6o#(}1_&Q42g?#dL@Dj`T0F2%vA9sLSaVgdTBFX9T9R6$yzr)M(ncc!nw%r4@wYk05D6u=0fww zSns1%>SnjC3twa`bBi0-~LLNQ? zNZSrOzaktqCKW6yY_inq5-m9i{k>UX#E`Q;M$-%Ma~9T;u-)Bbpf?Sg2+6w3CkC#) zv%@cQ^R?*W^;oG?hnpA}v9%b%z#Nq*tcem0U;eBliO4~a^c@8id}r!aMuDam>gRgS z+Qq5xvNWcnHzRH(|>N~H0KG2qFNXnz`u8`6ph^iqr+)*BdD=-7pmvYcb_ zndp6e+G;AB>L&bfg7J3r42+;`el3MGeUy1nSo84AixCG-(0KO7^-ub>SQC&pMQ4w* zba6gg*-s)lCTXlJtx&B^>FTn1rujzSuCGUs)s8_{mT%QLjVyd~0SjStruGk{)Gu)R zz{I|Zhqi5r5Xb4QDx-JoK2*L76TwWLHo-z4ra*E&Oma6IqTw~&=g@d(2ZZg z%?VCP=0~_^MK&UtFl2rw#6e%N^h{2=s1$8a?|4lzy?M)MK(=#*yTL(av(X4dBCI-E#-RU&Q9hpT(Pv zoB0a2AmU=nQQD8uW;YaxQH4%hvgU%`qM`>GeUyctC7XmCZZUm*G4_ z9_ZtILkllPnE-k7uP69P?#{W>Q9ARaB!rI=-ins6bv|+ zv~Ca8)oX#lj+OJ?C?ra390G2U0-%P5di(ST!K`JdQstlLQ(IBsbfA3NG-j)N^l5I^ zF***KniH{5_?X)pQaT0nV-*QC)TzR~90apvWAlyYq$|!Ib}zqwSn73fSTeD`4m=P( zg6T?hH2WFdvrsVwqVa)k2c7?p+Oxk8jml%&(0d{@S z96raAVoOtU)S8J2>_+`K02lYFI0Tp%Sl6x+G1bYDHYuN-o!C{MTX!1k52Q|vnAN9N zR16H8d48eO1R#yOQyXo7w@nb+y@OaYHo5>`)5Dbh%xddM&CSi9y}twtli;wJ|7ui} z9O9c9rX_YhTK8)@?Y7BDJ^;cXYnWG&pmRpHe_$BZZnUr=D?4fCve+dZq559K?6`>9 zV{#aK*EoZMaYLd=GNNy5ybLNwnl-MWeF2L5E9Asp|KIx)p9l2h1+UpkH3a%+Ed?#Q zjjv+qG$nGqWkUUPsSvW#6xoDxDeC&}XI8=#-+*R37_xUsArSP4VG34h2n}Ve#=e(8 zcT(2|MSNYb$fCt#e?t2joDt_$*2AnU4-gY6&n|f=F+En2F;%cHEjg97hS^Gh2m&_J z49cfxqZZr0DrbI-TG*ZxH{w3lJn>p)(3}m(b`X!)X^I2${b7q&nRc}A-(QEWs}Yc1 zTdy5L(c9c=(>=W!+Q4{g$Z{SXR&D)ouCkH0I)cQb-yr&DrVsP~66V=$!lWUa^8dq4 zs96x0_~py*j1X_=*ccBUo_C0@4aUS~LW$zcbn>&H6b|X3_Wh?8#B=jJT@$o**AjXW zQFw7)OOr1tCqVfpjybf--?F!tM!ag4rV=xKZRW4yPP;UPr;vosaCtyrRpYt-x@>=1 zGYP!tZ~f_}3~bUl()XjBS1w+c$dtIUgS7Q|KryP1%?y>4nz5@^M_v0u4-jZaV^F39 z;?*d!k9|@ZST`a&VUNK6^i{F%>Z*&wjFo#)&Ky3G_|Bjx5OE--NNV^DMDC-zlB?ms zo(wcBABu>vuQNR;llq?WrgZ#qBuSsTpin9gQs&Gu*UG-1VP^Ly$jj&ab*cKlug{O& z_offzeB^wr1CmN{^@*fv9>9AT@*SR?YiN5$`TJvHiHGFI7=BpCvz_Z6q+C6iT3xD< zX+fMP9FXl`MR%1zLmd5&I_VoiphPiS|p?J2q1G6$l*RG^} z#)zAm$qUuUXd19v(ne4v;29rbE0{}kk*%Yvz{XikGfO3*EF67I>_rpI@hc!2^9J#{|IkTfM zDMwZ2$494 zu+srME+;&Ep9sq%!|vsx9MxK10Zp6u z)J9em80*|7E5EW^N27jLP7OS?DE+Aoe{I`l#gxw%_BvpYtL@FRW3PEr#6XnF`OS78 z$W6azDAn*AR-+`8f9OTBygz|Pr2-Mnv{R&i#LY{TTFx1fja?x&8gGAE0R%H*$@IU! z5$k04bfodUlJI|3@R?a8TxnM8^2erIj(8v*}W?oVNN0zU>Q~ zFMU2Vpk#_v#(z!s?${St5s`i!af<`|NhWslWH;1JJBYUL{)PE(1? z*yW?NGJU&?F_h8lqh23p zXjjcTVMl;#^TEo0^GQeW$?2E?s#bubMsVyKLiRZA7AI7&*$>~Czk)cx3V%fUyu&WqWg`h&7Ob|Omaz)1Mc`d&JYcv@`W>cpBV zRYZZ%0;|&K9hyUS+n;uOKf71mmHq)4UQOP)$!*D$`6%6s+z%)}$w+KbEt!~`Lt6tE zWFb|FX>Y5op@zGhpmOt7mR@MzGFIshPh}!ZEK6Jt+qsb1<|^moVi(Qa)1-?7!jZ3v zjm*#*7lbSt$Dj6gU6x6ZOb^FGK)O}Uv&mMQk$>{%JVyl@)=mWcX(Fs$(#%b_h$6qs ziYmkeYAr(!de_xC>u_?amx3oSq7r4Ar-=akBF)h2k||ma5(_n#IQA7OonT3Uc%~Oc zHl`^Tq;J<+r!HP*{u>IAC0qY}PsQhB2kN;9jYk|nyw(F5Tk1IwnlowAOyUl=O@5>H zR!9OsIxH%F|njbgzM>A6jb1pex%S2Wg==!5T=% zONx@9?Fgl!Yd#v9LfRzx;X*Zv>3v(fUGi|!>pKS3!Vs5-jD205TWW#>mU#|m-II)GdfN8RJL8Y&v?OI*l#-SPV|wliv26pxyx=BOfL*kIoM1T zwH|?~HS4Yq3%f(>FEzE_FPP?m7&2juMIvq#Nm3|WFnoj(@a!x4FD{TlrDX-_0Bh<{ zPc#FSI9(K4+PU(4YD3;gvyq_i=+@)+Hz!NCDbZ69>mzZxe})%7{>0&Z5r9oopj0Z+&HjA)_K()2;}Y1g zB(rD}J=f>KM{8Pf_j&gzuAfZ#f6)zc8{IjsET^%Wy4?SK#Hn83j&CmCQ zWQ~W($)SqOe4UXDp_m!Q26)RqN(dCJV66v9QU~(VGBi%oUy`2D%5v64gW1aWi7L2T z7iB2tryM2Uk)P}oyLE)bbyy3eOefL{Lg7Fb&gY}`v0cV@gZ@^ME;m=fWG$vAzqrM3 zHcAQY$5jAlzmlC)NI-AM<4FKqTEmp{Xs{+dDv^cW|7k+Tw!|L0fEF-&{e{*c8SLM1 z2|zNka(TvN05BJZCMy-f6gU&(B0ziV5%68fHD3A-2^})*xGS>6t@HCUQ`-|xp~`|O z#V|GmvV2c?blb7%cU4`{8c&9-;T?7Rv)T#YiV9ya0Rxh@R%V`pNev&|F9F-52xdoy zR#9CUv0VEEsg`v%H3X<5pk_@!&wZ9s@ph#*s#6Rcr1KG^W(>AO4c*;fE|!m^23!K=b_FPAWh zXkX-}G!|{bNGNSIw^qd+(ZO%W=F@S@8H7!YDtTTwhiA`3sh_jVR_9Y6u=VtiW{uSj zY)HHSGr5^T;AZayWSk{$F#a%_HatM_~3P-_Kl5IwEU!G+OtDfM)FS4Xr^*`8~MLaK;`xmK>JK$ zQ1Ona68aXkO?j2ZqJm%F9}|d)wNkPv9g(G`O^TW8w35<+T3@dx;`QD3mKNHejOEB(WiXE-RpUhVRn0)eg0O#jF$O3b zl(a!G$Km3!wX)JZE}-GV2;_o)m_Bx8i9o=)PezTVDqyc83H_b_lcIeuzP$ z*~kyDb5cHC0EP^VqLe#!`OD+e)AT?x2H5S9t{z8Veu+D*5O~YKG|9Sv8R(Z`Wvg)X zel2p6TkQj%D?el3@G%3Nn*Yi+vhd$Ae&tI3_c)~YaQ+zMDs?~{FV(7TXuquXmrr@d zvv(C;&V!1;ckZ3)hJg{Qf#-z|DD&32pWK4?t|(;qR%UsY#@=4-c8| z6%ozm0Nsz+*d`jRNDM7m;nlJGp8O(k?pj7)RYvaL)ojdRA~pL(cYU&Kvs)_+G@ zGk?Y_K#k#>`hnt;5SixTVM)11vJs20MBA&+8>5cNp9mcaS$Iy3bvdSyM={%E)xn_qN|+WK7FL&$4xdthk&RlnR|oXkkI zbpYL-GKGC!CoQAxtL^Ux^>&~1i5r_zJg?}j>v!Mu_OEGaCZ2Rsg7#CI&2GONX5c9# z9V>z5sykJ$SJN!z3F(-05Y5qUnq0oaoQJR(*2rZ?x!myGdiksHiBI`)IXRuU+?^S`12$82$DF^w!(4d8a+eFIh; zFBQU=13~xnq+@~SiG16V6?eZ)c6)quq{iGyzT`nt-vj%aN5UaL40oLYoYaJBta_@1 zvj4#ixGw^Ju1TXtztCAwwe?k3(OETs9QZJRn-XH1OeE-FESHjNY^(EHMk4LMEZ~1*@b1U{;c2i=j509i0>WSA#WloQ zq3M#C=PN=N)YOjNO9?B>vtJkM)a#+E<_)s!(mwHq$tY-&5>5LgQu!6%H(6ZjJls-^$r3n*R}cCX zZ^^(pNa)bRL!UOMr!vnu2g0TP<%db4YQ)(cq*vT}CD+XhqPd((qg25}Vng&wbi#};E>BZ&pk}3fd*c&n1Q(+>7@|bNy8b&F zU}th;Q_3G6ZDhYUr<%7iB)7(h77-oVYpv$^7?+J(0c#)+em(-mi~#-exo>N^o1rOi z#)dQwT7Wm}gb!YlkZ_ZY3r*z*rlj+Ivt#n3*FS*0+;X!+>GsT@nRk*4Ry?DXR@==^ zvfK*u7Xuh+1q;9bAuUl%F{N*MriUe&ewD)H^2xRHy`>Vai`BV~hFBAfB~~*{w9(Sv z2F`dv+I=BY0E&YYT#t0~H%Xxt=X_~*n4Vp504$B#`d!L++;BL$a&*n?J2?g2ucU1d zts>-(a4)i#ymeFkD#%HdqfzA_5N~$s!N%tcSwi}2S8A@=oxOA~s1j0Mp6&6R3lJdb z5E}ydM&F@=C+#uc+AMIw0c=oeBcmb?#=X|6a-T|(A(=M&sT=?(03(gj zVn65fc>t4(XP~bczv+Y+tg5gALg>bXbFiy`aGn_CSm}p&&ORMjJ$q|cnK?VH*e2K4 z6a>PNKq#!4Z3E^HNC!NuFAsf&Ew51R@_U)YlPqJJ%KkNci&Hn*9dvvqmiA0P?Mkby zK{?OPzP~9nFQD=uY^mmul!fpMwP z9e$fH*2mAIbdvwq<)PhzN?Xj^@k)&Qyj$)8J)~n25)JPOm#a&@9Ht*pGRM}^1zV|v z0|%4ssd**4?=0EdXL0!6|NKcL_49LIv@vwi;895QR=FQ&YtmOcxkd_;dz z=DdW;%K3ia!OBXFn>&-&+q|stif^^43)@vb7yY^PhrN??0?M*KuIf_}{L1Sw9B;Bl zz6|Fy#dmcz$(Kx`Z~Pd;>*=|+CT@lOJm0EBP-(PI7oTzki+oUh(HJ*0bo@H^Hz5H^ zvvS@c=iccSTH_#|Cl-Rp!_we2-l%noEMfTL*ls-l)Z+tEpv2aHyyF~-OYUL{qL}ty zph>eSM&^o74r@5S46>|pTIXGIF0dRbY#^9Upk+MwY_!|d$90aZwv z$>oCt986!!%*EGqE$#5Jz;B8Vmo7M{lXERVR|)qA4yu1<%BkNIlaszaFHrKA&OQCm z@FqAFSfMqYdZJ9x$8J-G*EJiI0Qa5ZTB5DDF6X<-173CG&B{j*U0#=R;?aN~F!CkW zAdyA7va;(SnFmYUy;+u=twyrg)wXxHnyyj7J~)7?Mk*>l%fQgL;fuw}zw zun3!}wff>6@h^R*JA&^k=C)q<%aFG@t7L&dU{wmkWf+6 zc}vv!0~~x31QCo!1NE?;MZFa-l&7JK(z}A~H53jGq*o)z1x2Ym+M*A`gABSCMqP*;0#YF)NBj~%I-{_n?Vq-)VwhYjP)B9V8(Ras zta_*fgUFM$dUF^JlUj8$we};;-U!dS7;{WowT5`2yN$%kz~(#hpVkWi25y}IO$d}qw8U+ z7{{6|mbF(fx~{mS9cxLVkBiR6z8?}Aq3B3bbiBBmB6JM~-+Dh2DEwQ5j6FRGj6@#H zkB$!59&ISXy$Fk|Uwqs+cl+2d(=;O-;(X50M-$UCrv-SU(^==V6yR5SO?Pu7-qLA# zN46$Bp=sZJ(ze7Xrw}j&oe!k{nU%HBQe#qkVwm#--XbTkt%Zi^L(6$NP`@LRz))R+ zweq;X&&$vGoT}gIm)EoHR_wSrbEg1ne?viZbRX#Mu2Fu`TMfNy=1~hwY;oV-e(x9= zYOimNGe5kJHm-pe3U*LX#$6`ula~R3K4G4MNV%DrnU#;42PV zR!(nw85}?tYZyi4XT9mHt_HlCh0=#y@4e^R6?_q}8W5!Mx!Pm#3oC0N#w8mZ&V?h+ z!l`Cpxq)15yNK7`zLlYlR@Ku2TV95PltY=X(^12zztf0BrCq(_W@5S!#%eHf2!>Og*T;r1_YBAomhS3-(7~*}~bY8b{zJ3PA%iyRu;68SVwtaGxEXy#*k;h*68WPK((7C<#?|K63>&HK=fFMYx~k=wmSLO$B9X(;MS0QV9}w`KQl0WtaBHnDkv zI(fKc5F871{vLAjUO0p3pbkNRQ{vNBqd8!LNLk{uX;2)_^gnjW*U2KL*fQ=pWH1et zp0N)kfbfq$!Y+Hj4txhie~b-e4Xn!cz3Tn(?CuL^C@A*3#EKI411u;yBC_BBZqJjn($c+i0vwS!_e<94%{Ip)BfLRf&0TX8 z1H1GxDpUPmPJn74i-zX+nl;JBoUwP{`Z>th7>Mza;Ewqnm+gl|@yrToBL<+KF)GpQCJ8s@SYscJW4YA*~kqmdR>@sZR=eQyc_Y9fkc|5HDk~!PX)R4vtX!$w?huY~}p$G)vxqW<&)!E(cBa>jY!{ zJN7kOR%OENJZ4pkvo+UX53J~wa_VjG#-tNo<|Mcj>y=V0M(YPNpmbq!`MkP{39Nd| zWKkXK&}kLDLp3*7+TY{{#dV;3aeKVyzt(Wjj1A z+nhbGkzw|@I4$pMnmHo9Qb1#6-_HvT^LL!%;r2L+1})5tW2#|T@u(zo(rx82y7z)& zc%rmB-;*dg2AWYE>1MHJN(ez)l(rE*IXrk6G$j&oc<2VMg*zmKG-ob#JJ(DlX@5TI z4X4RIt0=Keo>N>!MU%1X$=q*ya@9~H0!V%Q*x10gW?Sd;OGx&+V*!r~tNMZIj@IE= zwRI|>!nfJ`hGS(6V6RI}t(U!hFmA7-GVq=_R%9b?T#+Mwag2Kl%m7$%C8l_}AjZKR z3Y?PyX!ZvQ%I2E;!WS7bFshI4&wh*{G;|sw_7Oh5z>y$(%9Ff6#&R%aH|N*8?N#)r zWmfYf+r%Kp#>yFDqcFGEbYdmBpVE?ohkk{ z)3i%Ij-(vh!fAG#GCkv`^=Sy-Nx|ash2~{3#OQp z9Jn^WW@*5n)7bNZ1bi$DKrNo9lBw>Tdi><&TUC!6UZ`3wYKKX{xoH(HM6IhAC?r7@bzECIp zjuyYgIK)EJb(H#l#aLJv(ms&;b}_Q6Lua92OkK*9FG*H!dGnt)->AuF5q zv!+iWVGBe*AOZ>-C)H*`1x8x#ynQS(eKNrL~8v zK5T?a8&%*MsMLk;fp9Zd*F?dl<>omeh7+`93JS}?G*xH~;u>Wkz?3mkUz(L18WFUi z4yx)ufRh<8-zJI%L39_FfZnO80DwpOM>qNvw!=zoYJ%D7S=e&tBSja^fcZe7H6k5; zRB>5dEml$z082^1XCX%c_p|m74>NOrk*sZM?P@zD&KtMorzxAjBwX@*_u~iNV;(0L zOBA67*YIQpwGxNYrmF{_BTulq50_DI*7Ie^?$G;$Kz>UsyM~RYGUA!~19Pd^mc$J6p)5ut}?5il|Ne+1pf+}H>o z;ShjgW|M$;07yV(vSNW-K9QAhQr}f?@tVJ9^vyy_AQ$o-ou=oCNX2UT$*LgrYuBS@ za3kJ(1E~jVtxRh!Yga-bJ`n;j8H@6aiuzQYftlkU12VF=fq_AqZ1XA=XW%^g$VTUq ze_7Se;4+({)UD%q~H-B%=K!X=aifCzu(U>2A&0YH4! zY~xsvd-NCZ+wA^IWkSdaflFv; z6-Y-`*!2v)zV5YaE-e#;!*@|(bd>e6p%IswnaLYQ{TjLYv#-p|Vp;Cl4J7<*{PiXV zL_+CN(Q~rjWY!Qr4C@RntT!IjdhVMH+Yc=G@#1h)Tm_ zYrzfbT!HS$u_m-~dP}DyBv^tjY@WiF#c^B(-TG-HO!g8ZUa1T=WJF_cO|(TrKScMY$J1sFhn0RWv%a)vz1N9HJ{QvCHX%I~|u zb!>Frru4&I5SUXty!DRo;=3`YnTkUQQVNRG5&P>~XQIl_TmA})Uf3otY#0i9uX$@1 zJW?z2&*@np92Z;+ z8~#25@afy1O>Ga>j|gq4&l4nF=Jl?s?uykGq9Tixi-U`e=5|F-b02^ID@Yv4&5srt znK|m^MgoFUkm3iB#zu^@ZzsvhZtMvXNR-bbfep#z$?w8ZuW*3-UwjL$&`b8*{MGeM z`x<+V->LX1hDEvQv*My6={wT$W2)t_NlN?=cO&(37pJE31f7>JScqwpf~3QwK6{=Y znSfFo^bOng6%|G^Pk%0>a^#ZIUrIu2`XtHDtv{a#oi%B`dwH37(2h#XV|V^Vk^gPL z+BB(n1XWC>mQaldWm#GEJXqsjhwsULRl%kxinN|EYC(|Sn)A^w;?*nwN4i>qGI!1v zWj)U~EceCnbkl6|#@|%a${!LSO~AQ&<+O*S%<_4BmGGK3X&E+;*yYpD6ii$kzCeTK zyAXXDZ<(Gh^x9(tno;Uh`~E^~?#`rBeb8cAg@#sUehp3()6iJlRAjBj2S2N^}1pGUzF7FkxYfW>~n-%sm}sbPxWt zWm3)i0E@JDIY)T*@3u2SebE!21z-OQ@|8D?FXDB%_8Pr9v3|M>0!8`x>5_m+YV6Z?WA?0g+xdA=IxjlzKM&9O_9=__ zWl@v!;SYs{t_9ZU=nGFu&S()cPZvEO;}}P>Xd(^Qh6w}=iX-fkCuTQA+gm`rZ_-vM zFFQN)@6A%ix%7%Jwg8NxtQNr$IW|)wR||T>LP`4s9EnmE)3t@vC_*W?ad%J@B5V3nHDP*nfQVaNDc-OC(m9Zmr#TDxPn1Es@ z>UZz{%sHE&N(~-@_xUMnrj<6f+wnh#{N|i)n8nM?%B#;rJol*Z zB(-PuYzFrSG5nw`8XN!nQ|F4PpkP+b$qHj`Ke|SPt2x5Z9>zl6k^GoWldt~xJM?my zSE)z?!EX@M&dtsPx)g>lsUMs@00CGPr$PD$R1hh1_uJ(}ikI`NPx_JWr3&g~Pl`UT zrypsCfIUtjcKeHfP%OHiy;ZL@oT0|2`eRKz-)o1Q|FrdkS?i1grxK zIb-jO-ktt7$WMZFh9y~TJHa7XsiYL23O&(d8c*sgo0Qi`X5sWGF;MCh|5G0uk`s?Q*# z4Md??ob%~oC6yMbt#Vw14p&}-vS7f`i}n}YaXWwX_6Cq&S+PcjceBUejW|=$GNViy z$!}k9HD1I4MM-q8UpM7eunO9^iF@pt0GIzDt>9R*!AN!Cm+`p4H%pPH^pzv-Ygh0%^6?c7Nws@3#gJYir!*I<- zfgHeDIgyHrOhp1yXF@K++93U%OxK~vVi)9V{E@GLS-21$ZnP#mb~4;RjX*&u0ZsCf zQc})dqoEz7L(oe=U;$jjn5}~A`6u?Pe2m-NXc?i5kjw-96-{qiM9)h%E{k!w!bO%>Zjs?l2T|Yseyri^z>qr zIZZ%hM!(j72~_eyL&<5cvSu>Ng%&B>J06~%9_Ew`e-w~FBEh=*=ju5qgp6@{uhLUn z?1h}hC6ZPgAEjVe9kyWCi=8M^q@?nD1kz*ALqkKWH5t6|`j|lJWroX|f5`FifY}Zj zpsO<#CG0U1x4Lq*QVBX`3JYgip9{Rl?^@7aT5?=f2K^_68XOGsdTf!}e~zSSk`h89 z&N67rlqvDt5{t31DVP2%n5=vcG&dkE_QZRS1`-IQE-uYKJ3Ha#JI<gpjt8x0RNmS8=Y zv@}pK+_ABy86vjLy?2lW?e_mTy(2k?!R*d!iE7O0;2u3vFXYg+!|=6_A(y)9(=~$piacT>tyunSoO=%;ia%lG@iB zpoB~_c|GV1RxDs)M&Zr_iyP`PHy?0OuZjE2+g>Hcow>r zTY~n?U%iSlEH-$bTqR)A@Rw{r;yPQi0Nyd>1}uxU!x)++B10FrZT)(wc#ebahXGFu zNcuZ@X1v*>Vy?LnGlJ@T*u)r1cW*3P^B^{G+ery+$&{VTtfb8$fq`Y56ln#ca&cyoI-_0Ey+H~8N1cucsNwQe|-xRsgSKVzkTyO2{n)= zJSeAcrR)IA*B1>$kT;dK4h^__;ToJvhY#DsI}o4P2xWzRy@NS7I+t|~M0&QjXFn>^ zX>ugJG+){Q9N~ktPuBP)dH{)ozl)I9AXI4J2s^b)D*%Vdx-<)}@7ffk$5_f?5Rin1 zyINrBk151kfQVf50At3-;j3B{eeDu%a%w$_O@TE_3C{X|&tU_e+gMxMdd0Cgu!=(R zVbRw&w>j%xW=*Ba*0s)4?BR5h(Sjml?6x+Cm#%ye)6H zI-%Y1bsR2#zw#BY5(yhJs@i_C$qcMvoDD69Rhc`9&7<-%Cka(if_^d-fG0iM2!Jnj zMkp-S-od`LZ$D=Z88!kh%zWfqY`&j1&=AP-6cjLn$-e)nn0f4FdMnKbOGycD$&X#E zylC^vqNx}XZn&WhgwAA^$$v;aMQJA^Dr>spx36PG4_0$>CM3N-FI9BHota+Ce#>wxemGO~e1A(2of;q7moH}haGEcp!a=9}>3Lrjk!ujwC$IEpP=ORP0Y>`2Hv}QyFnKq_o_ihPS=PG% z!Z>0n5HPp?`qkIlr=<}8gtkCa6BGl2zixYEmYsfFS35U31O|>E-HD9s67=;9HY~2s z0oHchd3WZ{m_0rl$>D=E8VsTJ6~3qgrrGiG@|;vMi4vVoU7d7V;9Y z9Oz###M$$a?wDhfL&_GR{oju;wL&vBcr>Z_Q%%hME)9erh!0t4Zeed`7Z+y7Vg;*! z{?}}5H{cgB_TSHbN_7A(WeKD)gO88FnXqXb5_S?Y^7GgIaEa(syK1gC>Nl3rne#?f zj7V2O#?QG}o5sS0HyGCGD&5i2;;``VEHBXA`@&W|QtC#!HW23nb_oIQS~+Kb^qXTI z^7kH&>uLg;ln(*}18W=?T3>iNJi;WIEuQm%rV_UWaWTNpqSR5q)3>T0mlTOTi*HEZ z*l=VwH}$1FII97TzDM~qw`czqszEv~^D6IVOk!CSY_F%`EudMfR+^8=ZtDJiMO4m5+~X7;&@M|^^(oZ!wtG;C8J z{VyMke6f++5b4XoPoE}LON2d$z`|1H;r^q|#6%n2#v_7-g5u$_(28*V0_c=!Ns7Lk zHJS8Bs{E^%%~06v8`-XZ{}e()n^Gb$SrUG)rY=;K0H-gat1AdcgS}TutU`{h+g%;1 zhGTs5iyN8?8j$AdJGPZ8NimYHaPq?6f4qH9xi!^ z9O(2@Xqjw3aEL|)QpR>A-|1ZDVG83BB4+tHI!VqZY#C19_Rlw6I?Nt49K>gNrj9`Z z54hq2$(v!xvcOYLKGmC*xVS0L=#C8urxWDBm%SSW!WJ~{FX22LNBp7o6a{RZ%Zf*# zuxLk4p=g>%j?3Md{mLDNxb}6J)B*o)?VMN_&bA|j>C`m0Znlp*oS zjcac_=Vi2R48*iFG*d%HK>mR7p;&tg%24WrFa4=js|C^0DFAZ3;FR!_H&qpt`pm;> zLs^pQ#WT4HFzU6kD1$`Jc@*w>Gup`8sWLGP-vuDKy2_;5jp?m1mtelVP(=} zIth(vBaVdHCfo+SyyC&h(#L$RAAPAC(AU_2ch8>o&j*iwR_oqS0}xfXcEQi7siKCf zp9#o9wrKET6ZXs&5N@=Aj%EXI3Y3_!J)=H`JRApuT5uw~>JFQUS;C_6t|Re|%*+%H}%kghxyz1XHI zECwLCz|#a}s@^LPiK0%#E_oZFg_N@ru`5HI;lF>T8?Ii!322v4fQmmb^Z*9nf^@EC zU;ODIk%f=m=jbW`(dZSN1H-7_jW>&PSVoxF?(hpbnE{tCl}MM@A?K)gC0^0_qP^u5a4 zbw462!q=mE#PmP=%O&4cxtRix1Pb8#$%vxCc5O?(9rYtKNdmbdB~khrvNFpgY8U0( zB&1e(nTt$VSTBXth(m;6+1dEAxvmjv4Lzq?NTV7sA~6KAMe;O`g5zkPbf;grJwb(~ z45-3=3;XroTsoTu);mXlfvN{M%2rpD+>i$>a<+O$_~4&0F_A zAu$qtInK?i;Eta2ofoA2RlWyg%H^f0Ygkffm{;IdaWA^2>82ur<>ZrWD__ESsS}^Yi z43LDJpByRbL!$~t0aE;qz~M5)py^{Hj-d`=$;X#v{QTA!4T&TTsT$)U35dt_sTIDo zOL+etT`)jAErPywg|vI+OZXBUFs7QUNC4KIpaI!MdcW2?m}6s3_Bb};JFaL$IM)`F-Aemkg4;9PXVQX7Ae|T z7P&~)=5{DH0;TRDBYE&leH_b0h$a+$Myv(wcs>Njz<1Mbr1%imYQgWTnr+R%;daGr z1v*~q@-lk3d6gJ4&uGYbBOy7_!A=DESh2@cMk5{xfZ-+nps>Vh>Lg9>Q5f_%nc^tp zr_j6M)=P$fv86dHJ0H@x)~*JVrg1#=&6lveJ}5T>vRUz-P+?ZgC8qQ5m^ME zgQaeeN)OVy%ABi#ikw2D>{;BvESCl4>K!4t1gKVt6{JQlKUt&9ysK|F2rVo9Be?<` zFs2+ly)>JJ8R0SGS8m7RtE+RZaS!R#e0OZX`EK4Z?b~wk#E$k|+^}DyQ#zZn7q)<3#EyGJ@mi*`j~G+sUZMx%*Js zBtZ-)g=DJQ$^;Hc98{Q2R7sT{zPl!VhJ!kM*3G;;r)|Gz9=>Zl2kS}8`Efm ziZOGFU4<}cN6EYj;KiOPnxg~?wIR);Mjvg-LL3b)uWCUT|8qIxFS0xwpm9vClJ>TX zCTuMOoY+|s)Y;P{d;CJ=f3*Otq4zq9IQ!?OkCr$K?Y`VzMg#hQyH@~67zaSR7q8i! z9DB1r7=6qM&Ep~n8Smt%T4c_Xk7eYIk}tl1zv1TL0bJ(tp(%F5rq)6!Y5u5zo?&p+ zlZlD93_tYK+BELl)bD{yChG334M-QAXvHtnj}^xtPHn6Q$=-Dg-ka^zPi=A- zQS6#?xAysgtZ*zpz1jEYy4b`KSx(LfY0zQ^oK+RvPl8l0}%WO?lay2nDd~9|1=x^)k_L_nGl&9nF%RLr`0oG{~WI zkh^$gnmJ&kl#zlojg3hVGd!5rQomAvAn&Ca24=cV-cvIoQ0+%zjK(&>T<_O;F-YE6cTY)4tw^d7w~CxE zbzvYf(s6&WVnZq!C(R&x*0=+apmI^^lIb>ucus2r*Dgf{ne~un9tDPT6IM$SlSE_@ zc4FsbX0-rAZm|>kq`@JRO*U>z9S|9ugF)?1cg@*khPc)FK2kGgeU;%7kS_ZR8lA94 zJ-F4IILMt8By}5H0XeBxyTK&rNSn5Y^Po9{!%>+q86M`c_IvqFb%XNDqS@%aX4i%6QTo$f%1q8j)oW;-FUy-9sE5whp8VQonRQKu0X$)?T_NCwp%E z?AYFFFf?!@0N{pX zYXoYrYVu17sP#rGSqhL7t@1EoiXIb#hK~(@THzlUEUjt=Vi^uAr|EQ${86m%JB7ZU zLvBpQJ>!mSoEWnKOFT2G1t=vV!&G`Em29;yt`K9(|NFSwl>wc2d7I|RqUS9Zg03JkUKwI7;OIUp zQzpFyHR7lttvrgkI=q4y+1YfqX(^z}UuvDKYoS}}+4>cwh}%8!Qz5Wm*NXLipFr|q z=fn#@<|!?yDl-%H{&Ezr$yQLE(K5*yogw&{(XV200b8>sn(Xq0O8YCZ7+du(2E>F>IZH@-9204=2_=eXHVTRxhSn;1&w%u>pmg zR&Wvw8}BjMF%&+v1!asej=#UBnp9%b6{v~&|C0ktWfclE`g5U{aDj0{<$K}IWRO@e z(EpNc#hrdFtVkKd%jYUkGw)9Ul6}gm4mhMmIOdepp0}F*gxrO4N7`G&3XkRDIY8~` zoDSwF>|767Za=LWnAM%}LOI}Dq2to~=I6N@{zJB$7zu286lqnZb?2y-16Pe1oAq0{ zJ3^HdplF7!vvY2sy&b)}m+p{yZ2US3bpPzU2(1>4?0)iVT6k8ZF3ZE+T)gNNrTlab zC_Li17ZSAWJTl{Zc=5N|A&A1k*QvC&8V!Di4Gz{NSyvg$vy%ekEj5MK_;dmd=^D!Y zkV2c4++QD>f92}BC>wPC(&qCzCFpUJfpy=1M;M#9!sO?n7-z6o)Q>pM!u_TM>OR-8 z<>Oz3T=YH$q>o=Om<8IBnp4IQc6}he2Xb?AUb9m4aA>xPh<>du@+tH_v}Nm4rMZpRBdaPG;7V-VjW(m%~jG|HM4C`y2@BuV>pY*v7QW z+}}suhc7IIrB+VMrKDhHnpHPg*kbQCQG|xY@PM0Jc21<$wuF-|x0T#?)FJJb4^R&Q z>oa2X6QsEUr53)puELdSh{6bE&CM1)Ma7=!8eBYlQ`Lo0WH)YOH8!m>(PQEa=m@t^ z+ep8!x|DR$T|d?=yVqjCsn#XmHILSvn&w)ZI{grdf6|_d5%bxj-k)9{GAKO8E6hHz zgwW(9E+8jP(ztu)lSMP%3wZnpT{Ffl^@RU6)Sfg$_)n$F%A6ZjKdpPq169N#WTm*bH{-@_PNP^QNA+A>oYn{!Yd zwPx<#`%b$wi2(9F3PByEE(yaDt!?C1GlH~6;U)F&u{Qr8a6cP8{bd0KhxdHzlGgY|Qgk!U-sBm6C-!6GG%ZtXbL0Q92LEAj`V0N)_8tOR>h7U8 zcJT~dg6`OhWDQ|~x4}9u>DGt3P)MS19zYqcxB`Nm6Dla6iqm1cy;(Wk17>7hrQ&jZ zsp_;KB}mcr=lMJ;C_ho;5>O$=xf3$m(1sLC3=+u_Y+%_nfZRKnuvh#tvB{a@SCuI9 z?v>xq9oTXL%|h^5Hgi7%-3>PsYJ@@I@dj36X^sU*Bg@x|4J^f!XcUB0fC#~d3d)%e zad#pag(1=RP|I&D~X;OTKPn0IRY0bW;Y;vrrok> zOA!nmZz6R@pvVl7b$f6tSEuj-50FRw;qb)JHhrS9x(tY&99vp-6Yb?e&v*Im&ry(mG zPS|InGfrKg!hzBpO??_kp&4Q}IwR$*#Mw|{wj&$Ad?!Y9IBN*Qa*=GefoRb)UU2L` zkv;ZafuP!O5=Z|NNBeXjU~Qjd%M45);0^2Kj4K}Gsin{VeH&R(31+BKon}F_2FzCq zGaTD)K%F=`)&3m|d22GgelJoMNk~2=Tz8acwf{{7pW^_6Dswxmy{%tcU3vysmRT6DU(>>1WFL_19Z()FXdy)U@%1qe$zHT4 z2Ua$6!~G4Lzejef9K33w*M)-YCuq;)GV2%gfK35^&R)*QwA;DCU&dLG8v|AG)aHZ@ z4&dBOvfHZ1VKhvB{-=L+aDd#2n8ce_(T>|khjK{$2nJDP92F58(R)^iF|;t##$0wP z#{C5jAcftdCy3igxTJ(dEZvFCGsLw_3(^NDxXUN<>Ls$yfh!5(T-=p|dCpuHo`Nc0 zbJO4202`$m*q@<5pDb)f8VDBp@^qx?p|dIHCfL7+S=Xilbr5H<3#Bn2Tunj=;W$HR+L@I{BvR2R$xp-D2$+_1k?!H z+9t-uIdfYMhk|dHJ9H50Z6M_=T#!M0r#BNoJ;Nb<}LH8{AirZ>GL30*LPB|lq zQg;K8BKS`XxsxFi>={s35Slo4ejy^w@ZqWyHJFwnK{Be&2xwPsV+8R`K2E$76y(s} zAoIGm;h%#@&3%GNV+5ZvB@^&7E1e>NS*PIku>Ga$2Q~t4(K3+16;ZU|ErW`tpHPwb2s}X*G-3aN8P6HG0@H@OvT0-&&Ny#QlQkCKu%344AnciB)EY z8ggwBP#&%PA@*#(bkFziWc?xhZ3zjCq@>i03ZK<#F|C#w!5U7g=+z&B0w6&r(H6U! z5Zg2xD7zUbz2vbkq_Yeu4jK>Ti_-rH5P% z1JwnaNnQk#Ts-O@?UHGjqq`87Cyt#y6Ene<8D}VkMr{b;!MRZ%WF^`~BvN@6Zi8$m z8k%ur6^N89!GXCoC{!dxk_LKGshP;p@pKaNyar2V{(AK_6I1JhHE@5{XW${Lwt{g6 z-grQCP8^b@cqR9@@}Ts`_wNM=#{~~iD9(xGCI7B83dn;(1N!NXTV7dKZv~6-yV_>F zZ3I75iAbC=KvA&UuU#a^siO!Ypzdfu$6b(aZNbf54zK#VG~-z4McD5gkiDRXXq1=s zwV6JkSv;t3LbU<8{lI)AQ@5-L+4)I=$#{~1)z$kVd+hSWaq0J8DwDD#Mr6IjrGfUq zNM7csaKtIa*IA^6D-K0U!NuD1E3LXr@m*fAjWam))T};&kUiQ~FOf6lz5=oA{>)mhMagJb)oF?lpPe3q ztaEe|jp<8%%e|Dzw!D_Tf7e^rcQi+Fd2oNU>83q?q!6Do-X*CXKc^NN9&cYwXoVv7 zvwUEG3114AsNkNl{{s#3*kOzUB1VOHS5BCLA4)ufLS3BmXWc|UsdblaU4Q=3fcaYA z2l;PGaEULo!x$9ejU`ZI!DFSfQT2ld8H4Y_%=RTelRY%7J2ErM6l}Q%{*l25>*hPT zIV|499d~4hzW2z1N|Gq;gg}(kG(GR$T zpU%PyXaBsyo$_#ri&Q=`C=6HesDTe*zB^v6E=l<37d!o|E61%^vBRaf#8ln%(2&fL zLt-Ib#vI_3T2~{c+O0MBc7GhGlqCLC3rFF{B~ng1*u+3wxFNStVOd&uuQy5?M8)c> z?rI&gdF{1_Uob1iGi>NTU;!Ug0(=$5(5S}_M80aK&Fz(<%A?R6zP zW+qN$IB&{Jj986!T6OTh$9uqfF>J(k@AU6@<8^THtkRZw1xI8eCz=Y$WH>XnfG9Fc z8j_g1L_q<1N%u%Pb8P_f8(}x!7~Ftfd+EF1Fe&j2}LjY zG)Lcl3?+l+xZ#cb=xY>RK(Giyi(S^eF)~syJ?*$V6TsYQq(N?8UjZ%T&B*BRrLcf# z#iZ7q{(1U~Kl9vR{F<*LZ;eN09)g^J+?MwITj={bPcgt!Vfg(#AnWNlQ&uc(!quuW zs1cdBf05Bfa@~}^`y7Z%2RRwO(>ruv)#zOH^o5`DjD(e!-R7px#YD`WX$QOC6%xfh zc5(^Q!6Ht5;!{#Fpah1k{TNb*Pdu~lX!PDZdTf!Rqs}p!X=eFF zzf1{(3YH~ZtjQ7XBpFrTUi&;sfKrx&{o&!G?J?4v%D!VArhZ)Y6sp4ELTxdNIynrbN_}?X4z{8(b^W3xa0e{I zp6{B%Zc{1>iup;iY)VkO&u*YU&2BA9KYYMPvNM^TQ$Qdf+7ce(;JdVhji#!hRl=Rp zpcs{!!sS5S;=OM8V$#`Z*Vn@|d# zrTdvfhowjB=K)J(nz~3maQ&Vr0-Z1i3&adE*xHn8nHZ^oQ zJp6&CwFfxG_*VJUc{@Ej-T`eYVxqayFXEC1)-u`YIMfoAh&%4h?glkn?(8iTIfSTK z)&b|{z@tKA-uvR4PopnbfwS6?ALDr+2f||8I*jq=F6}3#&hu>T{Xdy^Q7>&z26USK zVKhtoRWw^I4ivr-P?BIlR`qcSGV|LidFty&DlWy{j!na|SWbf)97a(iIGyE_(ewGV z!i2cD)rpNh;X9pA#2}{QVIr!%zKU^L?JX&*I`+xo z^G3OD27`KaV`vCjpUYdzE4E!qma&+=ygjw^g1CczkWq(<|3%z_sey}wgggfb^a{za-JRy9-w%mXo)(`NCLh9kTmN>s=&q zhoV@@84K7%MPuY1v&VE`+eFjALqiutTvZ0pB7A)Q^zwoZ#m&8re{u5PeE8_DU~PyF zqk@@5kTJW~Pit!V?|n2C6z#Hd4gAOyJovElHvIY@Ta4HV{;Yk|YYRr5m%F>I2p5`f zrxw0N)%K9j#k<+yIs{7?6O5hDxeSvtSd~22={)y4UQ|ve$y>`PayloNAtaP@Ig(g= z9)4U=2kH(=6{vmxFnj%JX|P-vz0>~MMCR|97=Zx)?-?P_)s$;6yZc}Bfi0o$1SU1? z^X>I`4gY^*r}MYaG)KAUY*D1awch>k%SpMvEi}ES#1AQj?tohLfb4wZ7H-Iqy+=BF zzEd`hsqFmf6lLR;6iZ_ZLD6T;aFh;@W(v?#)FW>AExsbt2Mbdfj1_+hmtsZ3+)?4q zjS-2S(|`c`B>Q*mOro~$+E(ZLTnr3K5tvC_E#R86+gn@Ba$0udbc!bw zs(2@YB{u)hSfFBm%g4#zByo4YN-ptT$@3hqcb-1{@!`+kC5I(2p0F(4FZsqyo$20h zoRhM2vqrq$j^xb`1mUg1Bx=va!;`pHHrpVqb22e`MmaqlAQQFKUMr{QJSX z;S0)Xx<(} zLr>Y?kEXu#0YGpohp%vtbqs#7BW?g8npPyDQTr-y9hUDMGPKSC=vIFC`L8gqfBTfR zA-5#Xv;RbU6#mHzIJbx0iKZ5-65y8Z)s}b5~TiY+b2T{!eRQ5WUZh50@hCExshuEPBX!`1DZj@ z=vzT8A8Z{0Lh+r~t)~Gb8Gf%btbegy)PxCbpAy56Bn{kUjeBVl*KA5k4r{+yH&0d@ z9xeKvuE3fW9LedmwTzc|(u7b<{~1UTM{F*s~`okM6%f|Tp z>%>MHsIP$=?>z<0P^Lw%1ZP}So8I_QWq97{PYgBwA%wtv2ICjDTc-Y~ZL??hAJjV37RAw^; z1q7C1`7=@6_QIl!cvcUFP+=o36)4*I-hX>;y#;!A|Irn{$K2V={CiwR=Qz{pK)`d7 zH(*=^_2ez5SwpYq49NVw=KJZxpbbKiKQs?w;O37JGK#4eFFxVnY9Ugb)jW>}8Xpte zN_7zMNMVqaG@Xl7PUj(9Hw1uaxr>zfAYfTHbQ2|{Gs&d>dXeH0?i7?STrLr8;jqW9 z%v6HEWMTTf2Jg_mdXN8B)bd`Jzt$4gBVW|;3%lW(uG2UWsQCW(fYN5Ew}yQJGZLwU z-?>VqGlpljp9?xTe@$*SIZQ9HjRB@>_&pM;yRtk9QUO?%71n`b`QlAJ;hLDIO+J3A z*!{J$dii*UKJ=TYj2T}_0b}QKbZ1qPhmq_(Z6_To)fX`|yq?ooFz`!~nsYQ@UtAyF zJ$SXoPj|!uqo#AUs>#!ddREbXBKDZg2iRaBr*Iz<3!vY{^MIj zWVp>Lic8YknNYBi+Hte_!AV)!p==gXRv2U5?A`*WgBRZStKhYr9SPc8f|`L>mS7L| z5#0Kr3LH_wkAsS%HG?;or)J0KJkG96@`+R8*rVPzB!Y}5#s{B2#$+dzIgWo zIwqr|%yd88soRw$^8d}RuW|Avhmp3ncz3Im_(*UB=wfCNE?jlQYvg3MD*5YEae zqL?brNkHJ;`|`ix+`1=arA|!c-57QTBbBLL}57&nKVFl~TQx4Ymn;?*cT&M13baj15!F13nXkC zcdqBH#LVnFILN-R)OeSk&YWB=N`VXw&ykj+uZWvBHW$1gJ~!8wDJzJj5BRhBGa+2=*K|4sJ}7l@uMV{MPoa|Lw2xKQ?86rpyZ7OzhFE zK<%yeKufu7EHVZt8hyrl6V_qMSL%12z#|AuWr+A!!9Hg&y5TJliSn%Rz@hov3ch85 zncjbS{5|mw^dg2?mYZj?$AuMX=J36AIUGUnFE0EiNLLw1dS(gFM24sE8zoY7vseuN z*a_T8V*XD{skbXDr}$DexFkwQ9dLd4-vWP;a+1-qD{V|C>cBU3A^@77&Eg$A*rJAv z_TJq*>t~y;2Kk5KaI!GqNlPv(=)Qxn>kG?y@E{5{2^hNTe~P8g8*8Dg=mSCgeL*

^fIO-5wE-Iy7s74$aJb0IK45CNI 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 literal 167809 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

ESH-|PVQON_Vu4dOCiz#lkPIr;kec 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

RK1uxT7^ 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�G=m8M&68pV)!LBXMc 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

1<=KH%n+3f*LTs}3Q(|>R+oK55i8J3Wi zWBYl62$v?Eg_y*nizj~-@?+QR)}rCEUCrL(VFAE`$#|X(l)}@;q8d>ChQ|ga`dbP09Q~ELi-*U}ja3NP`)bz5z~_dh9;*17b;$n#fC)Y2 diff --git a/nodeCanvasExamples/showLineCsvData.js b/nodeCanvasExamples/showLineCsvData.js index 6fda5c95..f452e291 100644 --- a/nodeCanvasExamples/showLineCsvData.js +++ b/nodeCanvasExamples/showLineCsvData.js @@ -4,7 +4,7 @@ var fs = require("fs"); var width = 1300; var height = 800; var center = [121.489723, 31.248408]; -var zoom = 10; +var zoom = 8; //var center = [118.489723, 31.248408]; var Canvas = require('canvas') diff --git a/package.json b/package.json index 22daee5a..9c5b32a2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mapv", - "version": "2.0.2", + "version": "2.0.3", "description": "a library of geography visualization", "main": "build/mapv.js", "scripts": { @@ -36,5 +36,6 @@ "rollup": "^0.25.8", "rollup-plugin-babel": "^2.4.0", "uglify-js": "^2.6.2" - } + }, + "dependencies": {} } diff --git a/src/canvas/draw/heatmap.js b/src/canvas/draw/heatmap.js index ca9c2ccb..0f6ac135 100644 --- a/src/canvas/draw/heatmap.js +++ b/src/canvas/draw/heatmap.js @@ -119,12 +119,12 @@ function draw(context, dataSet, options) { var data = dataSet.get(); context.save(); - console.time('drawGray') + //console.time('drawGray') drawGray(context, data, options); - console.timeEnd('drawGray'); + //console.timeEnd('drawGray'); // return false; if (!options.absolute) { - console.time('changeColor'); + //console.time('changeColor'); var colored = context.getImageData(0, 0, context.canvas.width, context.canvas.height); colorize(colored.data, utilsColorPalette.getImageData({ defaultGradient: options.gradient || { @@ -134,7 +134,7 @@ function draw(context, dataSet, options) { 1.0: "rgba(255, 0, 0, 1)" } }), options); - console.timeEnd('changeColor'); + //console.timeEnd('changeColor'); context.putImageData(colored, 0, 0); context.restore(); diff --git a/src/map/baidu-map/Layer.js b/src/map/baidu-map/Layer.js index 2a072c22..c726d60a 100644 --- a/src/map/baidu-map/Layer.js +++ b/src/map/baidu-map/Layer.js @@ -15,8 +15,18 @@ import Intensity from "../../utils/data-range/Intensity"; import Category from "../../utils/data-range/Category"; import Choropleth from "../../utils/data-range/Choropleth"; import Animator from "../../utils/animation/Animator"; +import TWEEN from "../../utils/animation/Tween"; import pathSimple from "../../canvas/path/simple"; +if (typeof window !== 'undefined') { + requestAnimationFrame(animate); +} + +function animate(time) { + requestAnimationFrame(animate); + TWEEN.update(time); +} + function Layer(map, dataSet, options) { if (!(dataSet instanceof DataSet)) { dataSet = new DataSet(dataSet); @@ -28,11 +38,11 @@ function Layer(map, dataSet, options) { var data = null; options = options || {}; + self.map = map; + self.init(options); self.argCheck(options); - self.map = map; - var canvasLayer = this.canvasLayer = new CanvasLayer({ map: map, paneName: options.paneName, @@ -90,6 +100,7 @@ Layer.prototype._canvasUpdate = function(time) { if (self.isEnabledTime()) { if (time === undefined) { + clear(context); return; } context.save(); @@ -287,30 +298,41 @@ Layer.prototype.init = function(options) { var animationOptions = self.options.animation; if (self.options.draw == 'time' || self.isEnabledTime()) { - if (!self.animator) { - self.animator = new Animator(function(time) { - self._canvasUpdate(time); - }, { - steps: animationOptions.steps || 100, - stepsRange: animationOptions.stepsRange || 100, - animationDuration: animationOptions.duration || 10 - }); + //if (!self.animator) { + if (!animationOptions.stepsRange) { + animationOptions.stepsRange = { + start: this.dataSet.getMin('time') || 0, + end: this.dataSet.getMax('time') || 0 + } + } + + var steps = { step: animationOptions.stepsRange.start }; + self.animator = new TWEEN.Tween(steps) + .onUpdate(function() { + self._canvasUpdate(this.step); + }) + .repeat(Infinity); - map.addEventListener('movestart', function() { + self.map.addEventListener('movestart', function() { if (self.isEnabledTime() && self.animator) { - self.animator.pause(); + self.animator.stop(); } }); - map.addEventListener('moveend', function() { + self.map.addEventListener('moveend', function() { if (self.isEnabledTime() && self.animator) { self.animator.start(); } }); - } + //} + + var duration = animationOptions.duration * 1000 || 5000; + + self.animator.to({ step: animationOptions.stepsRange.end }, duration); self.animator.start(); + } else { - self.animator && self.animator.pause(); + self.animator && self.animator.stop(); } } diff --git a/src/map/baidu-map/Layer.md b/src/map/baidu-map/Layer.md index e942934f..5b7ca598 100644 --- a/src/map/baidu-map/Layer.md +++ b/src/map/baidu-map/Layer.md @@ -269,5 +269,8 @@ dataSet中添加字段 ### mapvLayer.update({ options: {} // 修改配置 }); +### mapvLayer.setOptions({ + size: 1 +}); // 重新设置配置 ### mapvLayer.show(); // 显示图层 ### mapvLayer.hide(); // 删除图层 diff --git a/src/utils/animation/Animator.js b/src/utils/animation/Animator.js index f3ebf038..2e411945 100644 --- a/src/utils/animation/Animator.js +++ b/src/utils/animation/Animator.js @@ -1,55 +1,11 @@ /** * Abstract handler for animator steps */ -var AnimatorStepsRange = function(start, end) { - if (start < 0) throw new Error('start must be a positive number'); - if (start >= end) throw new Error('start must be smaller than end'); - - this.start = start; - this.end = end; -}; - -AnimatorStepsRange.prototype = { - - diff: function() { - return this.end - this.start; - }, - - isLast: function(step) { - // round step into an integer, to be able to compare number as expected (also converts bad input to 0) - return (step | 0) === this.end; - } -}; - -function clamp(a, b) { - return function(t) { - return Math.max(Math.min(t, b), a); - }; -} - -function invLinear(a, b) { - var c = clamp(0, 1.0); - return function(t) { - return c((t - a)/(b - a)); - }; -} - -function linear(a, b) { - var c = clamp(a, b); - function _linear(t) { - return c(a*(1.0 - t) + t*b); - } - - _linear.invert = function() { - return invLinear(a, b); - }; - - return _linear; -} - var global = typeof window === 'undefined' ? {} : window; +import TWEEN from './Tween'; + var requestAnimationFrame = global.requestAnimationFrame || global.mozRequestAnimationFrame || global.webkitRequestAnimationFrame @@ -64,44 +20,52 @@ var cancelAnimationFrame = global.cancelAnimationFrame /** * options: - * animationDuration in seconds - * animationDelay in seconds + * duration in seconds + * delay in seconds */ function Animator(callback, options) { - if(!options.steps) { - throw new Error("steps option missing") - } - this.options = options; + this.running = false; - this._tick = this._tick.bind(this); - this._t0 = +new Date(); this.callback = callback; - this._time = 0.0; - this.itemsReady = false; - this.options.animationDelay = 0; - this.options.maxDelta = 0.2; - this.options.loop = options.loop === undefined ? true : options.loop; + this.setOptions(options); - options.stepsRange = options.stepsRange || { - start: 0, - end: 100 - }; + this._tick = this._tick.bind(this); - this.steps(options.stepsRange.start || 0, options.stepsRange.end || 100); - if (options.stepsRange && options.stepsRange.start !== undefined && options.stepsRange.end !== undefined) { - this.stepsRange(options.stepsRange.start, options.stepsRange.end); - } } Animator.prototype = { + setOptions: function(options) { + this.options = options; + options.stepsRange = options.stepsRange || { + start: 0, + end: 100 + }; + + this.duration = options.duration || 10; // 单位秒 + + this.stepsRange = options.stepsRange; + this._add = (this.stepsRange.end - this.stepsRange.start) / (this.duration * 60); + this._time = this.stepsRange.start; + }, + start: function() { + + this.running = true; requestAnimationFrame(this._tick); this.options.onStart && this.options.onStart(); - if (this.stepsRange().diff() === 1) { - this.running = false; + }, + + _tick: function () { + this._time += this._add; + if (this._time > this.stepsRange.end) { + this._time = this.stepsRange.start; + } + this.callback && this.callback(this._time); + if (this.running) { + requestAnimationFrame(this._tick); } }, @@ -111,18 +75,10 @@ Animator.prototype = { stop: function() { this.pause(); - this.time(this.stepsRange().start); + this._time = this.stepsRange.start; this.options.onStop && this.options.onStop(); }, - // real animation time - time: function(_) { - if (!arguments.length) return this._time; - this._time = _; - var t = this.range(this.domain(this._time)); - this.callback(t); - }, - toggle: function() { if (this.running) { this.pause() @@ -131,89 +87,10 @@ Animator.prototype = { } }, - rescale: function() { - this.domainInv = linear(this.options.animationDelay, this.options.animationDelay + this.options.animationDuration); - this.domain = this.domainInv.invert(); - this.range = linear(0, this._defaultStepsRange.end); - this.rangeInv = this.range.invert(); - this.time(this._time); - this.running? this.start(): this.pause(); - return this; - }, - - duration: function(_) { - if (!arguments.length) return this.options.animationDuration; - this.options.animationDuration = _; - if (this.time() > _) { - this.time(0); - } - this.rescale(); - return this; - }, - - steps: function(start, end) { - this._defaultStepsRange = new AnimatorStepsRange(start, end); - return this.rescale(); - }, - - // Returns or sets a (custom) steps range - // Setting a steps range must be within the full range - stepsRange: function(start, end) { - if (arguments.length === 2) { - if (start < this._defaultStepsRange.start) throw new Error('start must be within default steps range'); - if (end > this._defaultStepsRange.end) throw new Error('end must be within default steps range'); - - this._customStepsRange = new AnimatorStepsRange(start, end); - this.options.onStepsRange && this.options.onStepsRange(); - - // Change current step if it's outside the new custom range - var step = this.step() | 0; // round to an integer - if (step < start || step > end) { - this.step(start); - } - } - return this._customStepsRange || this._defaultStepsRange; - }, - - removeCustomStepsRange: function() { - this._customStepsRange = undefined; - this.options.onStepsRange && this.options.onStepsRange(); - }, - - step: function(s) { - if(arguments.length === 0) return this.range(this.domain(this._time)); - this._time = this.domainInv(this.rangeInv(s)); - }, - pause: function() { this.running = false; cancelAnimationFrame(this._tick); this.options.onPause && this.options.onPause(); - }, - - _tick: function() { - var t1 = +new Date(); - var delta = (t1 - this._t0)*0.001; - // if delta is really big means the tab lost the focus - // at some point, so limit delta change - delta = Math.min(this.options.maxDelta, delta); - this._t0 = t1; - this._time += delta; - - var stepsRange = this.stepsRange(); - if (stepsRange.isLast(this.step())) { - if(!this.options.loop){ - // set time to max time - this.time(this.options.animationDuration); - this.pause(); - } else { - this.step(stepsRange.start); - } - } - if(this.running) { - this.time(this._time); - requestAnimationFrame(this._tick); - } } }; diff --git a/src/utils/animation/Tween.js b/src/utils/animation/Tween.js new file mode 100644 index 00000000..9969b9fb --- /dev/null +++ b/src/utils/animation/Tween.js @@ -0,0 +1,839 @@ +/** + * 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 () { + + return _tweens; + + }, + + removeAll: function () { + + _tweens = []; + + }, + + add: function (tween) { + + _tweens.push(tween); + + }, + + remove: function (tween) { + + var i = _tweens.indexOf(tween); + + if (i !== -1) { + _tweens.splice(i, 1); + } + + }, + + update: function (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 +(function () { + // In a browser, use window.performance.now if it is available. + 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 _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; + + // Set all starting values present on the target object + for (var field in object) { + _valuesStart[field] = parseFloat(object[field], 10); + } + + this.to = function (properties, duration) { + + if (duration !== undefined) { + _duration = duration; + } + + _valuesEnd = properties; + + 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 (_valuesStart[property] === undefined) { + continue; + } + + _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); + } + + this.stopChainedTweens(); + 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.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); + } + + _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, 10); + } else { + end = parseFloat(end, 10); + } + } + + // 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], 10); + } + + if (_yoyo) { + var tmp = _valuesStartRepeat[property]; + + _valuesStartRepeat[property] = _valuesEnd[property]; + _valuesEnd[property] = tmp; + } + + _valuesStart[property] = _valuesStartRepeat[property]; + + } + + if (_yoyo) { + _reversed = !_reversed; + } + + _startTime = time + _delayTime; + + return true; + + } else { + + if (_onCompleteCallback !== null) { + _onCompleteCallback.call(_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 (k) { + + return k; + + } + + }, + + Quadratic: { + + In: function (k) { + + return k * k; + + }, + + Out: function (k) { + + return k * (2 - k); + + }, + + InOut: function (k) { + + if ((k *= 2) < 1) { + return 0.5 * k * k; + } + + return - 0.5 * (--k * (k - 2) - 1); + + } + + }, + + Cubic: { + + In: function (k) { + + return k * k * k; + + }, + + Out: function (k) { + + return --k * k * k + 1; + + }, + + InOut: function (k) { + + if ((k *= 2) < 1) { + return 0.5 * k * k * k; + } + + return 0.5 * ((k -= 2) * k * k + 2); + + } + + }, + + Quartic: { + + In: function (k) { + + return k * k * k * k; + + }, + + Out: function (k) { + + return 1 - (--k * k * k * k); + + }, + + InOut: function (k) { + + if ((k *= 2) < 1) { + return 0.5 * k * k * k * k; + } + + return - 0.5 * ((k -= 2) * k * k * k - 2); + + } + + }, + + Quintic: { + + In: function (k) { + + return k * k * k * k * k; + + }, + + Out: function (k) { + + return --k * k * k * k * k + 1; + + }, + + InOut: function (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 (k) { + + return 1 - Math.cos(k * Math.PI / 2); + + }, + + Out: function (k) { + + return Math.sin(k * Math.PI / 2); + + }, + + InOut: function (k) { + + return 0.5 * (1 - Math.cos(Math.PI * k)); + + } + + }, + + Exponential: { + + In: function (k) { + + return k === 0 ? 0 : Math.pow(1024, k - 1); + + }, + + Out: function (k) { + + return k === 1 ? 1 : 1 - Math.pow(2, - 10 * k); + + }, + + InOut: function (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 (k) { + + return 1 - Math.sqrt(1 - k * k); + + }, + + Out: function (k) { + + return Math.sqrt(1 - (--k * k)); + + }, + + InOut: function (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 (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 (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 (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 (k) { + + var s = 1.70158; + + return k * k * ((s + 1) * k - s); + + }, + + Out: function (k) { + + var s = 1.70158; + + return --k * k * ((s + 1) * k + s) + 1; + + }, + + InOut: function (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 (k) { + + return 1 - TWEEN.Easing.Bounce.Out(1 - k); + + }, + + Out: function (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 (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 (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 (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 (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 (p0, p1, t) { + + return (p1 - p0) * t + p0; + + }, + + Bernstein: function (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 (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; + + } + + } + +}; + +export default TWEEN;