UNPKG

three-globe

Version:

Globe data visualization as a ThreeJS reusable 3D object

1,412 lines (1,218 loc) 122 kB
'use strict'; function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var three = require('three'); var Kapsule = _interopDefault(require('kapsule')); var TWEEN = _interopDefault(require('@tweenjs/tween.js')); var threeGeojsonGeometry = require('three-geojson-geometry'); var d3Geo = require('d3-geo'); var accessorFn = _interopDefault(require('accessor-fn')); var tinyColor = _interopDefault(require('tinycolor2')); var dataJoint = _interopDefault(require('data-joint')); var FrameTicker = _interopDefault(require('frame-ticker')); var d3Scale = require('d3-scale'); var threeConicPolygonGeometry = require('three-conic-polygon-geometry'); var indexBy = _interopDefault(require('index-array-by')); var h3Js = require('h3-js'); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread2(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } function _construct(Parent, args, Class) { if (isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); } function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } var materialDispose = function materialDispose(material) { if (material instanceof Array) { material.forEach(materialDispose); } else { if (material.map) { material.map.dispose(); } material.dispose(); } }; var deallocate = function deallocate(obj) { if (obj.geometry) { obj.geometry.dispose(); } if (obj.material) { materialDispose(obj.material); } if (obj.texture) { obj.texture.dispose(); } if (obj.children) { obj.children.forEach(deallocate); } }; var emptyObject = function emptyObject(obj) { while (obj.children.length) { var childObj = obj.children[0]; obj.remove(childObj); deallocate(childObj); } }; function linkKapsule (kapsulePropName, kapsuleType) { var dummyK = new kapsuleType(); // To extract defaults return { linkProp: function linkProp(prop) { // link property config return { "default": dummyK[prop](), onChange: function onChange(v, state) { state[kapsulePropName][prop](v); }, triggerUpdate: false }; }, linkMethod: function linkMethod(method) { // link method pass-through return function (state) { var kapsuleInstance = state[kapsulePropName]; for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } var returnVal = kapsuleInstance[method].apply(kapsuleInstance, args); return returnVal === kapsuleInstance ? this // chain based on the parent object, not the inner kapsule : returnVal; }; } }; } var GLOBE_RADIUS = 100; function polar2Cartesian(lat, lng) { var relAltitude = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var phi = (90 - lat) * Math.PI / 180; var theta = (90 - lng) * Math.PI / 180; var r = GLOBE_RADIUS * (1 + relAltitude); return { x: r * Math.sin(phi) * Math.cos(theta), y: r * Math.cos(phi), z: r * Math.sin(phi) * Math.sin(theta) }; } function cartesian2Polar(_ref) { var x = _ref.x, y = _ref.y, z = _ref.z; var r = Math.sqrt(x * x + y * y + z * z); var phi = Math.acos(y / r); var theta = Math.atan2(z, x); return { lat: 90 - phi * 180 / Math.PI, lng: 90 - theta * 180 / Math.PI, altitude: r / GLOBE_RADIUS - 1 }; } var THREE = window.THREE ? window.THREE // Prefer consumption from global THREE, if exists : { AdditiveBlending: three.AdditiveBlending, BackSide: three.BackSide, Color: three.Color, LineBasicMaterial: three.LineBasicMaterial, LineSegments: three.LineSegments, Mesh: three.Mesh, MeshPhongMaterial: three.MeshPhongMaterial, ShaderMaterial: three.ShaderMaterial, SphereGeometry: three.SphereGeometry, TextureLoader: three.TextureLoader }; var GlobeLayerKapsule = Kapsule({ props: { globeImageUrl: { onChange: function onChange(_, state) { state.globeNeedsUpdate = true; } }, bumpImageUrl: { onChange: function onChange(_, state) { state.globeNeedsUpdate = true; } }, showAtmosphere: { "default": true, onChange: function onChange(showAtmosphere, state) { state.atmosphereObj.visible = !!showAtmosphere; }, triggerUpdate: false }, showGraticules: { "default": false, onChange: function onChange(showGraticules, state) { state.graticulesObj.visible = !!showGraticules; }, triggerUpdate: false } }, stateInit: function stateInit() { // create globe var globeGeometry = new THREE.SphereGeometry(GLOBE_RADIUS, 75, 75); var globeObj = new THREE.Mesh(globeGeometry, new THREE.MeshPhongMaterial({ color: 0x000000 })); globeObj.rotation.y = -Math.PI / 2; // face prime meridian along Z axis globeObj.__globeObjType = 'globe'; // Add object type // create atmosphere var atmosphereObj; { var shaders = { uniforms: { coeficient: { value: 0.8 }, power: { value: 12 } }, vertexShader: "\n varying vec3 vNormal;\n void main() {\n vNormal = normalize(normalMatrix * normal);\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n }\n ", fragmentShader: "\n uniform float\tcoeficient;\n uniform float\tpower;\n\n varying vec3 vNormal;\n void main() {\n float intensity = pow(coeficient - dot(vNormal, vec3(0, 0, 1.0)), power);\n gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0) * intensity;\n }\n " }; var material = new THREE.ShaderMaterial(_objectSpread2({}, shaders, { side: THREE.BackSide, blending: THREE.AdditiveBlending, transparent: true, depthWrite: false })); atmosphereObj = new THREE.Mesh(globeGeometry, material); atmosphereObj.scale.set(1.1, 1.1, 1.1); atmosphereObj.__globeObjType = 'atmosphere'; // Add object type } // create graticules var graticulesObj = new THREE.LineSegments(new threeGeojsonGeometry.GeoJsonGeometry(d3Geo.geoGraticule10(), GLOBE_RADIUS, 2), new THREE.LineBasicMaterial({ color: 'lightgrey', transparent: true, opacity: 0.1 })); return { globeObj: globeObj, atmosphereObj: atmosphereObj, graticulesObj: graticulesObj }; }, init: function init(threeObj, state) { // Clear the scene emptyObject(threeObj); // Main three object to manipulate state.scene = threeObj; state.scene.add(state.globeObj); // add globe state.scene.add(state.atmosphereObj); // add atmosphere state.scene.add(state.graticulesObj); // add graticules }, update: function update(state) { var globeMaterial = state.globeObj.material; // Black globe if no image globeMaterial.color = new THREE.Color(0x000000); state.globeImageUrl && new THREE.TextureLoader().load(state.globeImageUrl, function (texture) { globeMaterial.map = texture; globeMaterial.color = null; globeMaterial.needsUpdate = true; }); state.bumpImageUrl && new THREE.TextureLoader().load(state.bumpImageUrl, function (texture) { globeMaterial.bumpMap = texture; globeMaterial.needsUpdate = true; }); } }); var colorStr2Hex = function colorStr2Hex(str) { return isNaN(str) ? parseInt(tinyColor(str).toHex(), 16) : str; }; var colorAlpha = function colorAlpha(str) { return isNaN(str) ? tinyColor(str).getAlpha() : 1; }; var color2ShaderArr = function color2ShaderArr(str) { var rgba = tinyColor(str).toRgb(); return [].concat(_toConsumableArray(['r', 'g', 'b'].map(function (d) { return rgba[d] / 255; })), [rgba.a]); }; function threeDigest(data, scene) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; return dataJoint(data, scene.children, function (obj) { return scene.add(obj); }, function (obj) { return scene.remove(obj); }, _objectSpread2({ objBindAttr: '__threeObj' }, options)); } var THREE$1 = window.THREE ? window.THREE // Prefer consumption from global THREE, if exists : { Color: three.Color, CylinderGeometry: three.CylinderGeometry, CylinderBufferGeometry: three.CylinderBufferGeometry, FaceColors: three.FaceColors, Geometry: three.Geometry, Matrix4: three.Matrix4, Mesh: three.Mesh, MeshBasicMaterial: three.MeshBasicMaterial, MeshLambertMaterial: three.MeshLambertMaterial, Object3D: three.Object3D }; var PointsLayerKapsule = Kapsule({ props: { pointsData: { "default": [] }, pointLat: { "default": 'lat' }, pointLng: { "default": 'lng' }, pointColor: { "default": function _default() { return '#ffffaa'; } }, pointAltitude: { "default": 0.1 }, // in units of globe radius pointRadius: { "default": 0.25 }, // in deg pointResolution: { "default": 12, triggerUpdate: false }, // how many slice segments in the cylinder's circumference pointsMerge: { "default": false }, // boolean. Whether to merge all points into a single mesh for rendering performance pointsTransitionDuration: { "default": 1000, triggerUpdate: false } // ms }, init: function init(threeObj, state) { // Clear the scene emptyObject(threeObj); // Main three object to manipulate state.scene = threeObj; }, update: function update(state) { // Data accessors var latAccessor = accessorFn(state.pointLat); var lngAccessor = accessorFn(state.pointLng); var altitudeAccessor = accessorFn(state.pointAltitude); var radiusAccessor = accessorFn(state.pointRadius); var colorAccessor = accessorFn(state.pointColor); // shared geometry var pointGeometry = new THREE$1[state.pointsMerge ? 'CylinderGeometry' : 'CylinderBufferGeometry'](1, 1, 1, state.pointResolution); pointGeometry.applyMatrix(new THREE$1.Matrix4().makeRotationX(Math.PI / 2)); pointGeometry.applyMatrix(new THREE$1.Matrix4().makeTranslation(0, 0, -0.5)); var pxPerDeg = 2 * Math.PI * GLOBE_RADIUS / 360; var pointMaterials = {}; // indexed by color var scene = state.pointsMerge ? new THREE$1.Object3D() : state.scene; // use fake scene if merging points threeDigest(state.pointsData, scene, { createObj: createObj, updateObj: updateObj, exitObj: emptyObject }); if (state.pointsMerge) { // merge points into a single mesh var pointsGeometry = new THREE$1.Geometry(); state.pointsData.forEach(function (d) { var obj = d.__threeObj; d.__threeObj = undefined; // unbind merged points // color faces var color = new THREE$1.Color(colorAccessor(d)); obj.geometry.faces.forEach(function (face) { return face.color = color; }); obj.updateMatrix(); pointsGeometry.merge(obj.geometry, obj.matrix); }); var points = new THREE$1.Mesh(pointsGeometry, new THREE$1.MeshBasicMaterial({ color: 0xffffff, vertexColors: THREE$1.FaceColors, morphTargets: false })); points.__globeObjType = 'points'; // Add object type points.__data = state.pointsData; // Attach obj data state.scene.add(points); } // function createObj() { var obj = new THREE$1.Mesh(pointGeometry); obj.__globeObjType = 'point'; // Add object type return obj; } function updateObj(obj, d) { var applyUpdate = function applyUpdate(td) { var _obj$__currentTargetD = obj.__currentTargetD = td, r = _obj$__currentTargetD.r, alt = _obj$__currentTargetD.alt, lat = _obj$__currentTargetD.lat, lng = _obj$__currentTargetD.lng; // position cylinder ground Object.assign(obj.position, polar2Cartesian(lat, lng)); // orientate outwards obj.lookAt(0, 0, 0); // scale radius and altitude obj.scale.x = obj.scale.y = Math.min(30, r) * pxPerDeg; obj.scale.z = Math.max(alt * GLOBE_RADIUS, 0.1); // avoid non-invertible matrix }; var targetD = { alt: +altitudeAccessor(d), r: +radiusAccessor(d), lat: +latAccessor(d), lng: +lngAccessor(d) }; var currentTargetD = obj.__currentTargetD || Object.assign({}, targetD, { alt: -1e-3 }); if (Object.keys(targetD).some(function (k) { return currentTargetD[k] !== targetD[k]; })) { if (state.pointsMerge || !state.pointsTransitionDuration || state.pointsTransitionDuration < 0) { // set final position applyUpdate(targetD); } else { // animate new TWEEN.Tween(currentTargetD).to(targetD, state.pointsTransitionDuration).easing(TWEEN.Easing.Quadratic.InOut).onUpdate(applyUpdate).start(); } } if (!state.pointsMerge) { // Update materials on individual points var color = colorAccessor(d); var opacity = colorAlpha(color); if (!pointMaterials.hasOwnProperty(color)) { pointMaterials[color] = new THREE$1.MeshLambertMaterial({ color: colorStr2Hex(color), transparent: opacity < 1, opacity: opacity }); } obj.material = pointMaterials[color]; } } } }); var THREE$2 = window.THREE ? window.THREE // Prefer consumption from global THREE, if exists : { AdditiveBlending: three.AdditiveBlending, BufferGeometry: three.BufferGeometry, CubicBezierCurve3: three.CubicBezierCurve3, Curve: three.Curve, Float32BufferAttribute: three.Float32BufferAttribute, Line: three.Line, Mesh: three.Mesh, QuadraticBezierCurve3: three.QuadraticBezierCurve3, ShaderMaterial: three.ShaderMaterial, TubeBufferGeometry: three.TubeBufferGeometry, Vector3: three.Vector3 }; var gradientShaders = { uniforms: { // dash param defaults, all relative to full length dashOffset: { value: 0 }, dashSize: { value: 1 }, gapSize: { value: 0 }, dashTranslate: { value: 0 } // used for animating the dash }, vertexShader: "\n uniform float dashTranslate; \n\n attribute vec4 vertexColor;\n varying vec4 vColor;\n \n attribute float vertexRelDistance;\n varying float vRelDistance;\n\n void main() {\n // pass through colors and distances\n vColor = vertexColor;\n vRelDistance = vertexRelDistance + dashTranslate;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n }\n ", fragmentShader: "\n uniform float dashOffset; \n uniform float dashSize;\n uniform float gapSize; \n \n varying vec4 vColor;\n varying float vRelDistance;\n \n void main() {\n // ignore pixels in the gap\n if (vRelDistance < dashOffset) discard;\n if (mod(vRelDistance - dashOffset, dashSize + gapSize) > dashSize) discard;\n \n // set px color: [r, g, b, a], interpolated between vertices \n gl_FragColor = vColor; \n }\n " }; var ArcsLayerKapsule = Kapsule({ props: { arcsData: { "default": [] }, arcStartLat: { "default": 'startLat' }, arcStartLng: { "default": 'startLng' }, arcEndLat: { "default": 'endLat' }, arcEndLng: { "default": 'endLng' }, arcColor: { "default": function _default() { return '#ffffaa'; } }, arcAltitude: {}, // in units of globe radius arcAltitudeAutoScale: { "default": 0.5 }, // scale altitude proportional to great-arc distance between the two points arcStroke: {}, // in deg arcCurveResolution: { "default": 64, triggerUpdate: false }, // how many straight segments in the curve arcCircularResolution: { "default": 6, triggerUpdate: false }, // how many slice segments in the tube's circumference arcDashLength: { "default": 1 }, // in units of line length arcDashGap: { "default": 0 }, arcDashInitialGap: { "default": 0 }, arcDashAnimateTime: { "default": 0 }, // ms arcsTransitionDuration: { "default": 1000, triggerUpdate: false } // ms }, init: function init(threeObj, state) { // Clear the scene emptyObject(threeObj); // Main three object to manipulate state.scene = threeObj; // Kick-off dash animations new FrameTicker().onTick.add(function (_, timeDelta) { state.arcsData.filter(function (d) { return d.__threeObj && d.__threeObj.material && d.__threeObj.__dashAnimateStep; }).forEach(function (d) { var obj = d.__threeObj; var step = obj.__dashAnimateStep * timeDelta; var curTranslate = obj.material.uniforms.dashTranslate.value % 1e9; // reset after 1B loops obj.material.uniforms.dashTranslate.value = curTranslate + step; }); }); }, update: function update(state) { // Data accessors var startLatAccessor = accessorFn(state.arcStartLat); var startLngAccessor = accessorFn(state.arcStartLng); var endLatAccessor = accessorFn(state.arcEndLat); var endLngAccessor = accessorFn(state.arcEndLng); var altitudeAccessor = accessorFn(state.arcAltitude); var altitudeAutoScaleAccessor = accessorFn(state.arcAltitudeAutoScale); var strokeAccessor = accessorFn(state.arcStroke); var colorAccessor = accessorFn(state.arcColor); var dashLengthAccessor = accessorFn(state.arcDashLength); var dashGapAccessor = accessorFn(state.arcDashGap); var dashInitialGapAccessor = accessorFn(state.arcDashInitialGap); var dashAnimateTimeAccessor = accessorFn(state.arcDashAnimateTime); var sharedMaterial = new THREE$2.ShaderMaterial(_objectSpread2({}, gradientShaders, { transparent: true, blending: THREE$2.AdditiveBlending })); threeDigest(state.arcsData, state.scene, { exitObj: emptyObject, createObj: function createObj(arc) { var stroke = strokeAccessor(arc); var useTube = stroke !== null && stroke !== undefined; var obj = useTube ? new THREE$2.Mesh() : new THREE$2.Line(new THREE$2.BufferGeometry()); obj.material = sharedMaterial.clone(); // Separate material instance per object to have dedicated uniforms (but shared shaders) obj.__globeObjType = 'arc'; // Add object type return obj; }, updateObj: function updateObj(obj, arc) { var stroke = strokeAccessor(arc); var useTube = stroke !== null && stroke !== undefined; // set dash uniforms Object.assign(obj.material.uniforms, { dashSize: { value: dashLengthAccessor(arc) }, gapSize: { value: dashGapAccessor(arc) }, dashOffset: { value: dashInitialGapAccessor(arc) } }); // set dash animation step var dashAnimateTime = dashAnimateTimeAccessor(arc); obj.__dashAnimateStep = dashAnimateTime > 0 ? 1000 / dashAnimateTime : 0; // per second // calculate vertex colors (to create gradient) var vertexColorArray = calcColorVertexArray(colorAccessor(arc), // single or array of colors state.arcCurveResolution, // numSegments useTube ? state.arcCircularResolution + 1 : 1 // num vertices per segment ); // calculate vertex relative distances (for dashed lines) var vertexRelDistanceArray = calcVertexRelDistances(state.arcCurveResolution, // numSegments useTube ? state.arcCircularResolution + 1 : 1, // num vertices per segment true // run from end to start, to animate in the correct direction ); obj.geometry.addAttribute('vertexColor', vertexColorArray); obj.geometry.addAttribute('vertexRelDistance', vertexRelDistanceArray); var applyUpdate = function applyUpdate(td) { var _arc$__currentTargetD = arc.__currentTargetD = td, stroke = _arc$__currentTargetD.stroke, curveD = _objectWithoutProperties(_arc$__currentTargetD, ["stroke"]); var curve = calcCurve(curveD); if (useTube) { obj.geometry && obj.geometry.dispose(); obj.geometry = new THREE$2.TubeBufferGeometry(curve, state.arcCurveResolution, stroke / 2, state.arcCircularResolution); obj.geometry.addAttribute('vertexColor', vertexColorArray); obj.geometry.addAttribute('vertexRelDistance', vertexRelDistanceArray); } else { obj.geometry.setFromPoints(curve.getPoints(state.arcCurveResolution)); } }; var targetD = { stroke: stroke, alt: altitudeAccessor(arc), altAutoScale: +altitudeAutoScaleAccessor(arc), startLat: +startLatAccessor(arc), startLng: +startLngAccessor(arc), endLat: +endLatAccessor(arc), endLng: +endLngAccessor(arc) }; var currentTargetD = arc.__currentTargetD || Object.assign({}, targetD, { altAutoScale: -1e-3 }); if (Object.keys(targetD).some(function (k) { return currentTargetD[k] !== targetD[k]; })) { if (!state.arcsTransitionDuration || state.arcsTransitionDuration < 0) { // set final position applyUpdate(targetD); } else { // animate new TWEEN.Tween(currentTargetD).to(targetD, state.arcsTransitionDuration).easing(TWEEN.Easing.Quadratic.InOut).onUpdate(applyUpdate).start(); } } } }); // function calcCurve(_ref) { var alt = _ref.alt, altAutoScale = _ref.altAutoScale, startLat = _ref.startLat, startLng = _ref.startLng, endLat = _ref.endLat, endLng = _ref.endLng; var getVec = function getVec(_ref2) { var _ref3 = _slicedToArray(_ref2, 3), lng = _ref3[0], lat = _ref3[1], alt = _ref3[2]; var _polar2Cartesian = polar2Cartesian(lat, lng, alt), x = _polar2Cartesian.x, y = _polar2Cartesian.y, z = _polar2Cartesian.z; return new THREE$2.Vector3(x, y, z); }; //calculate curve var startPnt = [startLng, startLat]; var endPnt = [endLng, endLat]; var altitude = alt; (altitude === null || altitude === undefined) && ( // by default set altitude proportional to the great-arc distance altitude = d3Geo.geoDistance(startPnt, endPnt) / 2 * altAutoScale); if (altitude) { var interpolate = d3Geo.geoInterpolate(startPnt, endPnt); var _map = [0.25, 0.75].map(function (t) { return [].concat(_toConsumableArray(interpolate(t)), [altitude * 1.5]); }), _map2 = _slicedToArray(_map, 2), m1Pnt = _map2[0], m2Pnt = _map2[1]; var curve = _construct(THREE$2.CubicBezierCurve3, _toConsumableArray([startPnt, m1Pnt, m2Pnt, endPnt].map(getVec))); //const mPnt = [...interpolate(0.5), altitude * 2]; //curve = new THREE.QuadraticBezierCurve3(...[startPnt, mPnt, endPnt].map(getVec)); return curve; } else { // ground line var _alt = 0.001; // slightly above the ground to prevent occlusion return calcSphereArc.apply(void 0, _toConsumableArray([[].concat(startPnt, [_alt]), [].concat(endPnt, [_alt])].map(getVec))); } // function calcSphereArc(startVec, endVec) { var angle = startVec.angleTo(endVec); var getGreatCirclePoint = function getGreatCirclePoint(t) { return new THREE$2.Vector3().addVectors(startVec.clone().multiplyScalar(Math.sin((1 - t) * angle)), endVec.clone().multiplyScalar(Math.sin(t * angle))).divideScalar(Math.sin(angle)); }; var sphereArc = new THREE$2.Curve(); sphereArc.getPoint = getGreatCirclePoint; return sphereArc; } } function calcColorVertexArray(colors, numSegments) { var numVerticesPerSegment = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1; var numVerticesGroup = numSegments + 1; // one between every two segments and two at the ends var getVertexColor; if (colors instanceof Array) { // array of colors, interpolate at each step var colorScale = d3Scale.scaleLinear().domain(colors.map(function (_, idx) { return idx / (colors.length - 1); })) // same number of stops as colors .range(colors); getVertexColor = function getVertexColor(t) { return color2ShaderArr(colorScale(t)); }; } else { // single color, use constant var vertexColor = color2ShaderArr(colors); getVertexColor = function getVertexColor() { return vertexColor; }; } var vertexColorArray = new THREE$2.Float32BufferAttribute(numVerticesGroup * 4 * numVerticesPerSegment, 4); for (var v = 0, l = numVerticesGroup; v < l; v++) { var _vertexColor = getVertexColor(v / (l - 1)); for (var s = 0; s < numVerticesPerSegment; s++) { vertexColorArray.set(_vertexColor, (v * numVerticesPerSegment + s) * 4); } } return vertexColorArray; } function calcVertexRelDistances(numSegments) { var numVerticesPerSegment = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; var invert = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var numVerticesGroup = numSegments + 1; // one between every two segments and two at the ends var arrLen = numVerticesGroup * numVerticesPerSegment; var vertexDistanceArray = new THREE$2.Float32BufferAttribute(arrLen, 1); for (var v = 0, l = numVerticesGroup; v < l; v++) { var relDistance = v / (l - 1); for (var s = 0; s < numVerticesPerSegment; s++) { var idx = v * numVerticesPerSegment + s; var pos = invert ? arrLen - 1 - idx : idx; vertexDistanceArray.setX(pos, relDistance); } } return vertexDistanceArray; } } }); var THREE$3 = window.THREE ? window.THREE // Prefer consumption from global THREE, if exists : { Color: three.Color, FaceColors: three.FaceColors, Geometry: three.Geometry, Mesh: three.Mesh, MeshBasicMaterial: three.MeshBasicMaterial, MeshLambertMaterial: three.MeshLambertMaterial, Object3D: three.Object3D }; var HexBinLayerKapsule = Kapsule({ props: { hexBinPointsData: { "default": [] }, hexBinPointLat: { "default": 'lat' }, hexBinPointLng: { "default": 'lng' }, hexBinPointWeight: { "default": 1 }, hexBinResolution: { "default": 4 }, // 0-15. Level 0 partitions the earth in 122 (mostly) hexagonal cells. Each subsequent level sub-divides the previous in roughly 7 hexagons. hexMargin: { "default": 0.2 }, // in fraction of diameter hexTopColor: { "default": function _default() { return '#ffffaa'; } }, hexSideColor: { "default": function _default() { return '#ffffaa'; } }, hexAltitude: { "default": function _default(_ref) { var sumWeight = _ref.sumWeight; return sumWeight * 0.01; } }, // in units of globe radius hexBinMerge: { "default": false }, // boolean. Whether to merge all hex geometries into a single mesh for rendering performance hexTransitionDuration: { "default": 1000, triggerUpdate: false } // ms }, init: function init(threeObj, state) { // Clear the scene emptyObject(threeObj); // Main three object to manipulate state.scene = threeObj; }, update: function update(state) { // Accessors var latAccessor = accessorFn(state.hexBinPointLat); var lngAccessor = accessorFn(state.hexBinPointLng); var weightAccessor = accessorFn(state.hexBinPointWeight); var altitudeAccessor = accessorFn(state.hexAltitude); var topColorAccessor = accessorFn(state.hexTopColor); var sideColorAccessor = accessorFn(state.hexSideColor); var marginAccessor = accessorFn(state.hexMargin); var byH3Idx = indexBy(state.hexBinPointsData.map(function (d) { return _objectSpread2({}, d, { h3Idx: h3Js.geoToH3(latAccessor(d), lngAccessor(d), state.hexBinResolution) }); }), 'h3Idx'); var hexBins = Object.entries(byH3Idx).map(function (_ref2) { var _ref3 = _slicedToArray(_ref2, 2), h3Idx = _ref3[0], points = _ref3[1]; return { h3Idx: h3Idx, points: points, sumWeight: points.reduce(function (agg, d) { return agg + +weightAccessor(d); }, 0) }; }); var hexMaterials = {}; // indexed by color var scene = state.hexBinMerge ? new THREE$3.Object3D() : state.scene; // use fake scene if merging hex points threeDigest(hexBins, scene, { createObj: createObj, updateObj: updateObj, exitObj: emptyObject, idAccessor: function idAccessor(d) { return d.h3Idx; } }); if (state.hexBinMerge) { // merge points into a single mesh var hexPointsGeometry = new THREE$3.Geometry(); hexBins.forEach(function (d) { var obj = d.__threeObj; d.__threeObj = undefined; // unbind merged points // color faces var topColor = new THREE$3.Color(topColorAccessor(d)); var sideColor = new THREE$3.Color(sideColorAccessor(d)); var topFaceIdx = obj.geometry.faces.length - 4; obj.geometry.faces.forEach(function (face, idx) { return face.color = idx >= topFaceIdx ? topColor : sideColor; }); obj.updateMatrix(); hexPointsGeometry.merge(obj.geometry, obj.matrix); }); var hexPoints = new THREE$3.Mesh(hexPointsGeometry, new THREE$3.MeshBasicMaterial({ color: 0xffffff, vertexColors: THREE$3.FaceColors, morphTargets: false })); hexPoints.__globeObjType = 'hexBinPoints'; // Add object type hexPoints.__data = hexBins; // Attach obj data state.scene.add(hexPoints); } // function createObj(d) { var obj = new THREE$3.Mesh(); obj.__hexCenter = h3Js.h3ToGeo(d.h3Idx); obj.__hexGeoJson = h3Js.h3ToGeoBoundary(d.h3Idx, true); // stitch longitudes at the anti-meridian var centerLng = obj.__hexCenter[1]; obj.__hexGeoJson.forEach(function (d) { var edgeLng = d[0]; if (Math.abs(centerLng - edgeLng) > 170) { // normalize large lng distances d[0] += centerLng > edgeLng ? 360 : -360; } }); obj.__globeObjType = 'hexbin'; // Add object type return obj; } function updateObj(obj, d) { var GeometryClass = state.hexBinMerge ? threeConicPolygonGeometry.ConicPolygonGeometry : threeConicPolygonGeometry.ConicPolygonGeometry; var applyUpdate = function applyUpdate(td) { var _obj$__currentTargetD = obj.__currentTargetD = td, alt = _obj$__currentTargetD.alt, margin = _obj$__currentTargetD.margin; // compute new geojson with relative margin var relNum = function relNum(st, end, rat) { return st - (st - end) * rat; }; var _obj$__hexCenter = _slicedToArray(obj.__hexCenter, 2), clat = _obj$__hexCenter[0], clng = _obj$__hexCenter[1]; var geoJson = margin === 0 ? obj.__hexGeoJson : obj.__hexGeoJson.map(function (_ref4) { var _ref5 = _slicedToArray(_ref4, 2), elng = _ref5[0], elat = _ref5[1]; return [[elng, clng], [elat, clat]].map(function (_ref6) { var _ref7 = _slicedToArray(_ref6, 2), st = _ref7[0], end = _ref7[1]; return relNum(st, end, margin); }); }); obj.geometry = new GeometryClass([geoJson], GLOBE_RADIUS, GLOBE_RADIUS * (1 + alt), false); }; var targetD = { alt: +altitudeAccessor(d), margin: Math.max(0, Math.min(1, +marginAccessor(d))) }; var currentTargetD = obj.__currentTargetD || Object.assign({}, targetD, { alt: -1e-3 }); if (Object.keys(targetD).some(function (k) { return currentTargetD[k] !== targetD[k]; })) { if (state.hexBinMerge || !state.hexTransitionDuration || state.hexTransitionDuration < 0) { // set final position applyUpdate(targetD); } else { // animate new TWEEN.Tween(currentTargetD).to(targetD, state.hexTransitionDuration).easing(TWEEN.Easing.Quadratic.InOut).onUpdate(applyUpdate).start(); } } if (!state.hexBinMerge) { // Update materials on individual hex points var sideColor = sideColorAccessor(d); var topColor = topColorAccessor(d); [sideColor, topColor].forEach(function (color) { if (!hexMaterials.hasOwnProperty(color)) { var opacity = colorAlpha(color); hexMaterials[color] = new THREE$3.MeshLambertMaterial({ color: colorStr2Hex(color), transparent: opacity < 1, opacity: opacity }); } }); obj.material = [sideColor, topColor].map(function (color) { return hexMaterials[color]; }); } } } }); var THREE$4 = window.THREE ? window.THREE // Prefer consumption from global THREE, if exists : { DoubleSide: three.DoubleSide, Group: three.Group, Line: three.Line, LineBasicMaterial: three.LineBasicMaterial, Mesh: three.Mesh, MeshLambertMaterial: three.MeshLambertMaterial }; var PolygonsLayerKapsule = Kapsule({ props: { polygonsData: { "default": [] }, polygonGeoJsonGeometry: { "default": 'geometry' }, polygonSideColor: { "default": function _default() { return '#ffffaa'; } }, polygonCapColor: { "default": function _default() { return '#ffffaa'; } }, polygonStrokeColor: {}, polygonAltitude: { "default": 0.1 }, // in units of globe radius polygonsTransitionDuration: { "default": 1000, triggerUpdate: false } // ms }, init: function init(threeObj, state) { // Clear the scene emptyObject(threeObj); // Main three object to manipulate state.scene = threeObj; }, update: function update(state) { // Data accessors var geoJsonAccessor = accessorFn(state.polygonGeoJsonGeometry); var altitudeAccessor = accessorFn(state.polygonAltitude); var capColorAccessor = accessorFn(state.polygonCapColor); var sideColorAccessor = accessorFn(state.polygonSideColor); var strokeColorAccessor = accessorFn(state.polygonStrokeColor); var singlePolygons = []; state.polygonsData.forEach(function (polygon) { var objAttrs = { data: polygon, capColor: capColorAccessor(polygon), sideColor: sideColorAccessor(polygon), strokeColor: strokeColorAccessor(polygon), altitude: +altitudeAccessor(polygon) }; var geoJson = geoJsonAccessor(polygon); var geoId = polygon.__id || "".concat(Math.round(Math.random() * 1e9)); // generate and stamp polygon ids to keep track in digest polygon.__id = geoId; if (geoJson.type === 'Polygon') { singlePolygons.push(_objectSpread2({ id: "".concat(geoId, "_0"), coords: geoJson.coordinates }, objAttrs)); } else if (geoJson.type === 'MultiPolygon') { singlePolygons.push.apply(singlePolygons, _toConsumableArray(geoJson.coordinates.map(function (coords, idx) { return _objectSpread2({ id: "".concat(geoId, "_").concat(idx), coords: coords }, objAttrs); }))); } else { console.warn("Unsupported GeoJson geometry type: ".concat(geoJson.type, ". Skipping geometry...")); } }); threeDigest(singlePolygons, state.scene, { idAccessor: function idAccessor(d) { return d.id; }, exitObj: emptyObject, createObj: function createObj() { var obj = new THREE$4.Group(); // conic geometry obj.add(new THREE$4.Mesh(undefined, [new THREE$4.MeshLambertMaterial({ side: THREE$4.DoubleSide, depthWrite: true }), // side material new THREE$4.MeshLambertMaterial({ side: THREE$4.DoubleSide, depthWrite: true }) // cap material ])); // polygon stroke obj.add(new THREE$4.Line(undefined, new THREE$4.LineBasicMaterial())); obj.__globeObjType = 'polygon'; // Add object type return obj; }, updateObj: function updateObj(obj, _ref) { var coords = _ref.coords, capColor = _ref.capColor, sideColor = _ref.sideColor, strokeColor = _ref.strokeColor, altitude = _ref.altitude; var _obj$children = _slicedToArray(obj.children, 2), conicObj = _obj$children[0], strokeObj = _obj$children[1]; // hide stroke if no color set var addStroke = !!strokeColor; strokeObj.visible = addStroke; // update materials [sideColor, capColor].forEach(function (color, materialIdx) { // conic object var material = conicObj.material[materialIdx]; var opacity = colorAlpha(color); material.color.set(colorStr2Hex(color)); material.transparent = opacity < 1; material.opacity = opacity; }); if (addStroke) { // stroke object var material = strokeObj.material; var opacity = colorAlpha(strokeColor); material.color.set(colorStr2Hex(strokeColor)); material.transparent = opacity < 1; material.opacity = opacity; } var geoJsonGeometry = { type: 'Polygon', coordinates: coords }; var applyUpdate = function applyUpdate(td) { var _obj$__currentTargetD = obj.__currentTargetD = td, alt = _obj$__currentTargetD.alt; conicObj.geometry = new threeConicPolygonGeometry.ConicPolygonBufferGeometry(coords, GLOBE_RADIUS, GLOBE_RADIUS * (1 + alt), false); addStroke && (strokeObj.geometry = new threeGeojsonGeometry.GeoJsonGeometry(geoJsonGeometry, GLOBE_RADIUS * (1 + alt + 1e-4))); // stroke slightly above the conic mesh }; var targetD = { alt: altitude }; var currentTargetD = obj.__currentTargetD || { alt: -1e-3 }; if (Object.keys(targetD).some(function (k) { return currentTargetD[k] !== targetD[k]; })) { if (!state.polygonsTransitionDuration || state.polygonsTransitionDuration < 0) { // set final position applyUpdate(targetD); } else { // animate new TWEEN.Tween(currentTargetD).to(targetD, state.polygonsTransitionDuration).easing(TWEEN.Easing.Quadratic.InOut).onUpdate(applyUpdate).start(); } } } }); } }); var glyphs={"0":{x_min:73,x_max:715,ha:792,o:"m 394 -29 q 153 129 242 -29 q 73 479 73 272 q 152 829 73 687 q 394 989 241 989 q 634 829 545 989 q 715 479 715 684 q 635 129 715 270 q 394 -29 546 -29 m 394 89 q 546 211 489 89 q 598 479 598 322 q 548 748 598 640 q 394 871 491 871 q 241 748 298 871 q 190 479 190 637 q 239 211 190 319 q 394 89 296 89 "},"1":{x_min:215.671875,x_max:574,ha:792,o:"m 574 0 l 442 0 l 442 697 l 215 697 l 215 796 q 386 833 330 796 q 475 986 447 875 l 574 986 l 574 0 "},"2":{x_min:59,x_max:731,ha:792,o:"m 731 0 l 59 0 q 197 314 59 188 q 457 487 199 315 q 598 691 598 580 q 543 819 598 772 q 411 867 488 867 q 272 811 328 867 q 209 630 209 747 l 81 630 q 182 901 81 805 q 408 986 271 986 q 629 909 536 986 q 731 694 731 826 q 613 449 731 541 q 378 316 495 383 q 201 122 235 234 l 731 122 l 731 0 "},"3":{x_min:54,x_max:737,ha:792,o:"m 737 284 q 635 55 737 141 q 399 -25 541 -25 q 156 52 248 -25 q 54 308 54 140 l 185 308 q 245 147 185 202 q 395 96 302 96 q 539 140 484 96 q 602 280 602 190 q 510 429 602 390 q 324 454 451 454 l 324 565 q 487 584 441 565 q 565 719 565 617 q 515 835 565 791 q 395 879 466 879 q 255 824 307 879 q 203 661 203 769 l 78 661 q 166 909 78 822 q 387 992 250 992 q 603 921 513 992 q 701 723 701 844 q 669 607 701 656 q 578 524 637 558 q 696 434 655 499 q 737 284 737 369 "},"4":{x_min:48,x_max:742.453125,ha:792,o:"m 742 243 l 602 243 l 602 0 l 476 0 l 476 243 l 48 243 l 48 368 l 476 958 l 602 958 l 602 354 l 742 354 l 742 243 m 476 354 l 476 792 l 162 354 l 476 354 "},"5":{x_min:54.171875,x_max:738,ha:792,o:"m 738 314 q 626 60 738 153 q 382 -23 526 -23 q 155 47 248 -23 q 54 256 54 125 l 183 256 q 259 132 204 174 q 382 91 314 91 q 533 149 471 91 q 602 314 602 213 q 538 469 602 411 q 386 528 475 528 q 284 506 332 528 q 197 439 237 484 l 81 439 l 159 958 l 684 958 l 684 840 l 254 840 l 214 579 q 306 627 258 612 q 407 643 354 643 q 636 552 540 643 q 738 314 738 457 "},"6":{x_min:53,x_max:739,ha:792,o:"m 739 312 q 633 62 739 162 q 400 -31 534 -31 q 162 78 257 -31 q 53 439 53 206 q 178 859 53 712 q 441 986 284 986 q 643 912 559 986 q 732 713 732 833 l 601 713 q 544 830 594 786 q 426 875 494 875 q 268 793 331 875 q 193 517 193 697 q 301 597 240 570 q 427 624 362 624 q 643 540 552 624 q 739 312 739 451 m 603 298 q 540 461 603 400 q 404 516 484 516 q 268 461 323 516 q 207 300 207 401 q 269 137 207 198 q 405 83 325 83 q 541 137 486 83 q 603 298 603 197 "},"7":{x_min:58.71875,x_max:730.953125,ha:792,o:"m 730 839 q 469 448 560 641 q 335 0 378 255 l 192 0 q 328 441 235 252 q 593 830 421 630 l 58 830 l 58 958 l 730 958 l 730 839 "},"8":{x_min:55,x_max:736,ha:792,o:"m 571 527 q 694 424 652 491 q 736 280 736 358 q 648 71 736 158 q 395 -26 551 -26 q 142 69 238 -26 q 55 279 55 157 q 96 425 55 359 q 220 527 138 491 q 120 615 153 562 q 88 726 88 668 q 171 904 88 827 q 395 986 261 986 q 618 905 529 986 q 702 727 702 830 q 670 616 702 667 q 571 527 638 565 m 394 565 q 519 610 475 565 q 563 717 563 655 q 521 823 563 781 q 392 872 474 872 q 265 824 312 872 q 224 720 224 783 q 265 613 224 656 q 394 565 312 565 m 395 91 q 545 150 488 91 q 597 280 597 204 q 546 408 597 355 q 395 465 492 465 q 244 408 299 465 q 194 280 194 356 q 244 150 194 203 q 395 91 299 91 "},"9":{x_min:53,x_max:739,ha:792,o:"m 739 524 q 619 94 739 241 q 362 -32 516 -32 q 150 47 242 -32 q 59 244 59 126 l 191 244 q 246 129 191 176 q 373 82 301 82 q 526 161 466 82 q 597 440 597 255 q 363 334 501 334 q 130 432 216 334 q 53 650 53 521 q 134 880 53 786 q 383 986 226 986 q 659 841 566 986 q 739 524 739 719 m 388 449 q 535 514 480 449 q 585 658 585 573 q 535 805 585 744 q 388 873 480 873 q 242 809 294 873 q 191 658 191 745 q 239 514 191 572 q 388 449 292 449 "},"ο":{x_min:0,x_max:712,ha:815,o:"m 356 -25 q 96 88 192 -25 q 0 368 0 201 q 92 642 0 533 q 356 761 192 761 q 617 644 517 761 q 712 368 712 533 q 619 91 712 201 q 356 -25 520 -25 m 356 85 q 527 175 465 85 q 583 369 583 255 q 528 562 583 484 q 356 651 466 651 q 189 560 250 651 q 135 369 135 481 q 187 177 135 257 q 356 85 250 85 "},S:{x_min:0,x_max:788,ha:890,o:"m 788 291 q 662 54 788 144 q 397 -26 550 -26 q 116 68 226 -26 q 0 337 0 168 l 131 337 q 200 152 131 220 q 384 85 269 85 q 557 129 479 85 q 650 270 650 183 q 490 429 650 379 q 194 513 341 470 q 33 739 33 584 q 142 964 33 881 q 388 1041 242 1041 q 644 957 543 1041 q 756 716 756 867 l 625 716 q 561 874 625 816 q 395 933 497 933 q 243 891 309 933 q 164 759 164 841 q 325 609 164 656 q 625 526 475 568 q 788 291 788 454 "},"¦":{x_min:343,x_max:449,ha:792,o:"m 449 462 l 343 462 l 343 986 l 449 986 l 449 462 m 449 -242 l 343 -242 l 343 280 l 449 280 l 449 -242 "},"/":{x_min:183.25,x_max:608.328125,ha:792,o:"m 608 1041 l 266 -129 l 183 -129 l 520 1041 l 608 1041 "},"Τ":{x_min:-0.4375,x_max:777.453125,ha:839,o:"m 777 893 l 458 893 l 458 0 l 319 0 l 319 892 l 0 892 l 0 1013 l 777 1013 l 777 893 "},y:{x_min:0,x_max:684.78125,ha:771,o:"m 684 738 l 388 -83 q 311