examples/demo-app/src/app.js (313 lines of code) (raw):

// Copyright (c) 2020 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import React, {Component} from 'react'; import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer'; import styled, {ThemeProvider} from 'styled-components'; import window from 'global/window'; import {connect} from 'react-redux'; import {theme} from 'kepler.gl/styles'; import Banner from './components/banner'; import Announcement, {FormLink} from './components/announcement'; import {replaceLoadDataModal} from './factories/load-data-modal'; import {replaceMapControl} from './factories/map-control'; import {replacePanelHeader} from './factories/panel-header'; import {AUTH_TOKENS} from './constants/default-settings'; import {messages} from './constants/localization'; import { loadRemoteMap, loadSampleConfigurations, onExportFileSuccess, onLoadCloudMapSuccess } from './actions'; import {loadCloudMap} from 'kepler.gl/actions'; import {CLOUD_PROVIDERS} from './cloud-providers'; const KeplerGl = require('kepler.gl/components').injectComponents([ replaceLoadDataModal(), replaceMapControl(), replacePanelHeader() ]); // Sample data /* eslint-disable no-unused-vars */ import sampleTripData, {testCsvData, sampleTripDataConfig} from './data/sample-trip-data'; import sampleGeojson from './data/sample-small-geojson'; import sampleGeojsonPoints from './data/sample-geojson-points'; import sampleGeojsonConfig from './data/sample-geojson-config'; import sampleH3Data, {config as h3MapConfig} from './data/sample-hex-id-csv'; import sampleS2Data, {config as s2MapConfig, dataId as s2DataId} from './data/sample-s2-data'; import sampleAnimateTrip from './data/sample-animate-trip-data'; import sampleIconCsv, {config as savedMapConfig} from './data/sample-icon-csv'; import {addDataToMap, addNotification} from 'kepler.gl/actions'; import {processCsvData, processGeojson} from 'kepler.gl/processors'; /* eslint-enable no-unused-vars */ const BannerHeight = 48; const BannerKey = `banner-${FormLink}`; const keplerGlGetState = state => state.demo.keplerGl; const GlobalStyle = styled.div` font-family: ff-clan-web-pro, 'Helvetica Neue', Helvetica, sans-serif; font-weight: 400; font-size: 0.875em; line-height: 1.71429; *, *:before, *:after { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } ul { margin: 0; padding: 0; } li { margin: 0; } a { text-decoration: none; color: ${props => props.theme.labelColor}; } `; class App extends Component { state = { showBanner: false, width: window.innerWidth, height: window.innerHeight }; componentDidMount() { // if we pass an id as part of the url // we ry to fetch along map configurations const {params: {id, provider} = {}, location: {query = {}} = {}} = this.props; const cloudProvider = CLOUD_PROVIDERS.find(c => c.name === provider); if (cloudProvider) { this.props.dispatch( loadCloudMap({ loadParams: query, provider: cloudProvider, onSuccess: onLoadCloudMapSuccess }) ); return; } // Load sample using its id if (id) { this.props.dispatch(loadSampleConfigurations(id)); } // Load map using a custom if (query.mapUrl) { // TODO?: validate map url this.props.dispatch(loadRemoteMap({dataUrl: query.mapUrl})); } // delay zs to show the banner // if (!window.localStorage.getItem(BannerKey)) { // window.setTimeout(this._showBanner, 3000); // } // load sample data // this._loadSampleData(); // Notifications // this._loadMockNotifications(); } _showBanner = () => { this.setState({showBanner: true}); }; _hideBanner = () => { this.setState({showBanner: false}); }; _disableBanner = () => { this._hideBanner(); window.localStorage.setItem(BannerKey, 'true'); }; _loadMockNotifications = () => { const notifications = [ [{message: 'Welcome to Kepler.gl'}, 3000], [{message: 'Something is wrong', type: 'error'}, 1000], [{message: 'I am getting better', type: 'warning'}, 1000], [{message: 'Everything is fine', type: 'success'}, 1000] ]; this._addNotifications(notifications); }; _addNotifications(notifications) { if (notifications && notifications.length) { const [notification, timeout] = notifications[0]; window.setTimeout(() => { this.props.dispatch(addNotification(notification)); this._addNotifications(notifications.slice(1)); }, timeout); } } _loadSampleData() { this._loadPointData(); // this._loadGeojsonData(); // this._loadTripGeoJson(); // this._loadIconData(); // this._loadH3HexagonData(); // this._loadS2Data(); // this._loadScenegraphLayer(); } _loadPointData() { this.props.dispatch( addDataToMap({ datasets: { info: { label: 'Sample Taxi Trips in New York City', id: 'test_trip_data' }, data: sampleTripData }, options: { centerMap: true, readOnly: false }, config: sampleTripDataConfig }) ); } _loadScenegraphLayer() { this.props.dispatch( addDataToMap({ datasets: { info: { label: 'Sample Scenegraph Ducks', id: 'test_trip_data' }, data: processCsvData(testCsvData) }, config: { version: 'v1', config: { visState: { layers: [ { type: '3D', config: { dataId: 'test_trip_data', columns: { lat: 'gps_data.lat', lng: 'gps_data.lng' }, isVisible: true } } ] } } } }) ); } _loadIconData() { // load icon data and config and process csv file this.props.dispatch( addDataToMap({ datasets: [ { info: { label: 'Icon Data', id: 'test_icon_data' }, data: processCsvData(sampleIconCsv) } ] }) ); } _loadTripGeoJson() { this.props.dispatch( addDataToMap({ datasets: [ { info: {label: 'Trip animation'}, data: processGeojson(sampleAnimateTrip) } ] }) ); } _loadGeojsonData() { // load geojson this.props.dispatch( addDataToMap({ datasets: [ { info: {label: 'Bart Stops Geo', id: 'bart-stops-geo'}, data: processGeojson(sampleGeojsonPoints) }, { info: {label: 'SF Zip Geo', id: 'sf-zip-geo'}, data: processGeojson(sampleGeojson) } ], options: { keepExistingConfig: true }, config: sampleGeojsonConfig }) ); } _loadH3HexagonData() { // load h3 hexagon this.props.dispatch( addDataToMap({ datasets: [ { info: { label: 'H3 Hexagons V2', id: 'h3-hex-id' }, data: processCsvData(sampleH3Data) } ], config: h3MapConfig, options: { keepExistingConfig: true } }) ); } _loadS2Data() { // load s2 this.props.dispatch( addDataToMap({ datasets: [ { info: { label: 'S2 Data', id: s2DataId }, data: processCsvData(sampleS2Data) } ], config: s2MapConfig, options: { keepExistingConfig: true } }) ); } _toggleCloudModal = () => { // TODO: this lives only in the demo hence we use the state for now // REFCOTOR using redux this.setState({ cloudModalOpen: !this.state.cloudModalOpen }); }; _getMapboxRef = (mapbox, index) => { if (!mapbox) { // The ref has been unset. // https://reactjs.org/docs/refs-and-the-dom.html#callback-refs // console.log(`Map ${index} has closed`); } else { // We expect an InteractiveMap created by KeplerGl's MapContainer. // https://uber.github.io/react-map-gl/#/Documentation/api-reference/interactive-map const map = mapbox.getMap(); map.on('zoomend', e => { // console.log(`Map ${index} zoom level: ${e.target.style.z}`); }); } }; render() { return ( <ThemeProvider theme={theme}> <GlobalStyle // this is to apply the same modal style as kepler.gl core // because styled-components doesn't always return a node // https://github.com/styled-components/styled-components/issues/617 ref={node => { node ? (this.root = node) : null; }} > <Banner show={this.state.showBanner} height={BannerHeight} bgColor="#2E7CF6" onClose={this._hideBanner} > <Announcement onDisable={this._disableBanner} /> </Banner> <div style={{ transition: 'margin 1s, height 1s', position: 'absolute', width: '100%', height: '100%', left: 0, top: 0 }} > <AutoSizer> {({height, width}) => ( <KeplerGl mapboxApiAccessToken={AUTH_TOKENS.MAPBOX_TOKEN} id="map" /* * Specify path to keplerGl state, because it is not mount at the root */ getState={keplerGlGetState} width={width} height={height} cloudProviders={CLOUD_PROVIDERS} localeMessages={messages} onExportToCloudSuccess={onExportFileSuccess} onLoadCloudMapSuccess={onLoadCloudMapSuccess} /> )} </AutoSizer> </div> </GlobalStyle> </ThemeProvider> ); } } const mapStateToProps = state => state; const dispatchToProps = dispatch => ({dispatch}); export default connect(mapStateToProps, dispatchToProps)(App);