UNPKG

terriajs

Version:

Geospatial data visualization platform.

158 lines (134 loc) 5.98 kB
'use strict'; /* global Float32Array */ /* eslint new-parens: 0 */ import React from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import debounce from 'lodash.debounce'; import defined from 'terriajs-cesium/Source/Core/defined'; import when from 'terriajs-cesium/Source/ThirdParty/when'; import DataUri from '../../../Core/DataUri'; import ObserveModelMixin from '../../ObserveModelMixin'; import VarType from '../../../Map/VarType'; import Icon from "../../Icon.jsx"; import Styles from './chart-panel-download-button.scss'; const RUN_WORKER_DEBOUNCE = 100; // Wait 100ms for initial setup changes to be completed. const TIME_COLUMN_DEFAULT_NAME = 'date'; const ChartPanelDownloadButton = createReactClass({ displayName: 'ChartPanelDownloadButton', mixins: [ObserveModelMixin], propTypes: { chartableItems: PropTypes.array.isRequired, errorEvent: PropTypes.object.isRequired }, _subscription: undefined, getInitialState: function() { return {href: undefined}; }, /* eslint-disable-next-line camelcase */ UNSAFE_componentWillMount() { // Changes to the graph item's catalog item results in new props being passed to this component 5 times on load... // a debounce is a simple way to ensure it only gets run once for every batch of real changes. this.debouncedRunWorker = debounce(this.runWorker, RUN_WORKER_DEBOUNCE); }, componentDidMount() { this.debouncedRunWorker(this.props.chartableItems); }, /* eslint-disable-next-line camelcase */ UNSAFE_componentWillReceiveProps(newProps) { this.debouncedRunWorker(newProps.chartableItems); }, runWorker(newValue) { const that = this; if (window.Worker && (typeof Float32Array !== 'undefined')) { that.setState({href: undefined}); const loadingPromises = newValue.map(item => item.load()); when.all(loadingPromises).then(() => { const synthesized = that.synthesizeNameAndValueArrays(newValue); // Could implement this using TaskProcessor, but requires webpack magic. const HrefWorker = require('worker-loader!./downloadHrefWorker'); const worker = new HrefWorker; // console.log('names and value arrays', synthesized.names, synthesized.values); if (synthesized.values && synthesized.values.length > 0) { worker.postMessage(synthesized); worker.onmessage = function(event) { // console.log('got worker message', event.data.slice(0, 60), '...'); that.setState({href: event.data}); }; } }); } // Currently no fallback for IE9-10 - just can't download. }, componentWillUnmount() { if (defined(this._subscription)) { this._subscription.dispose(); } }, synthesizeNameAndValueArrays(chartableItems) { const valueArrays = []; const names = []; // We will add the catalog item name back into the csv column name. for (let i = chartableItems.length - 1; i >= 0; i--) { const item = chartableItems[i]; const xColumn = getXColumn(item); if (!names.length) { names.push(getXColumnName(item, xColumn)); } let columns = [xColumn]; if (item.isEnabled && defined(item.tableStructure)) { if (!defined(columns[0])) { continue; } const yColumns = item.tableStructure.columnsByType[VarType.SCALAR].filter(column=>column.isActive); if (yColumns.length > 0) { columns = columns.concat(yColumns); // Use typed array if possible so we can pass by pointer to the web worker. // Create a new array otherwise because if values are a knockout observable, they cannot be serialised for the web worker. valueArrays.push(columns.map(column => (column.type === VarType.SCALAR ? new Float32Array(column.values) : Array.prototype.slice.call(column.values)))); yColumns.forEach(column => { names.push(item.name + ' ' + column.name); }); } } } return {values: valueArrays, names: names}; }, render() { if (this.state.href) { if (DataUri.checkCompatibility()) { return ( <a className={Styles.btnDownload} download='chart data.csv' href={this.state.href}> <Icon glyph={Icon.GLYPHS.download}/>Download</a> ); } else { return ( <span className={Styles.btnDownload} onClick={DataUri.checkCompatibility.bind(null, this.props.errorEvent, this.state.href)}> <Icon glyph={Icon.GLYPHS.download}/>Download</span> ); } } return null; }, }); /** * Gets the name for the x column - this will be 'date' if it's a time column otherwise it'll be the column's name. */ function getXColumnName(item, column) { if (item.timeColumn) { return TIME_COLUMN_DEFAULT_NAME; } else { return column.name; } } /** * Gets the column that will be used for the X axis of the chart. * * @returns {TableColumn} */ function getXColumn(item) { return item.timeColumn || (item.tableStructure && item.tableStructure.columnsByType[VarType.SCALAR][0]); } module.exports = ChartPanelDownloadButton;