UNPKG

kepler.gl.geoiq

Version:

kepler.gl is a webgl based application to visualize large scale location data in the browser

426 lines (389 loc) 15.1 kB
// Copyright (c) 2023 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 PropTypes, {string} from 'prop-types'; import {createSelector} from 'reselect'; import styled from 'styled-components'; import get from 'lodash.get'; import PanelHeaderAction from 'components/side-panel/panel-header-action'; import FieldSelector from 'components/common/field-selector'; import {Trash, Clock} from 'components/common/icons'; import SourceDataSelectorFactory from 'components/side-panel/common/source-data-selector'; import {StyledPanelHeader} from 'components/common/styled-components'; import FilterPanelHeaderFactory from 'components/side-panel/filter-panel/filter-panel-header'; import NewFilterPanelFactory from 'components/filters/filter-panels/new-filter-panel'; import TimeRangeFilterPanelFactory from 'components/filters/filter-panels/time-range-filter-panel'; import SingleSelectFilterPanelFactory from 'components/filters/filter-panels/single-select-filter-panel'; import MultiSelectFilterPanelFactory from 'components/filters/filter-panels/multi-select-filter-panel'; import RangeFilterPanelFactory from 'components/filters/filter-panels/range-filter-panel'; import PolygonFilterPanelFactory from 'components/filters/filter-panels/polygon-filter-panel'; // import PolygonFilterPanelFactory from 'components/filters/polygon-filter'; import {FILTER_TYPES, FILTER_COMPONENTS} from 'utils/filter-utils'; import {ALL_FIELD_TYPES, ON_PREMESIS_URL} from 'constants/default-settings'; import axios from 'axios'; import {histogram} from 'd3-array'; const StyledFilterPanel = styled.div` margin-bottom: 12px; border-radius: 1px; .filter-panel__filter { margin-top: 24px; } `; const StyledFilterHeader = styled(StyledPanelHeader)` cursor: pointer; padding: 10px 12px; .field-selector { width: calc(100% - 58px); } `; const StyledFilterContent = styled.div` background-color: ${props => props.theme.panelBackground}; padding: 12px; `; FilterPanelFactory.deps = [ NewFilterPanelFactory, TimeRangeFilterPanelFactory, SingleSelectFilterPanelFactory, MultiSelectFilterPanelFactory, RangeFilterPanelFactory, PolygonFilterPanelFactory ]; function FilterPanelFactory( NewFilterPanel, TimeRangeFilterPanel, SingleSelectFilterPanel, MultiSelectFilterPanel, RangeFilterPanel, PolygonFilterPanel ) { const FilterPanelComponents = { default: NewFilterPanel, [FILTER_TYPES.timeRange]: TimeRangeFilterPanel, [FILTER_TYPES.select]: SingleSelectFilterPanel, [FILTER_TYPES.multiSelect]: MultiSelectFilterPanel, [FILTER_TYPES.range]: RangeFilterPanel, [FILTER_TYPES.polygon]: PolygonFilterPanel }; return class FilterPanel extends Component { static propTypes = { idx: PropTypes.number, filters: PropTypes.arrayOf(PropTypes.any).isRequired, filter: PropTypes.object.isRequired, layers: PropTypes.arrayOf(PropTypes.any).isRequired, setFilter: PropTypes.func.isRequired, removeFilter: PropTypes.func.isRequired, enlargeFilter: PropTypes.func.isRequired, toggleAnimation: PropTypes.func.isRequired, datasets: PropTypes.object, showDatasetTable: PropTypes.func, isAnyFilterAnimating: PropTypes.bool, mapState: PropTypes.object, auth: PropTypes.object.isRequired, project: PropTypes.object.isRequired }; /* selectors */ // fieldsSelector = props => // (props.filter.dataId && props.datasets[props.filter.dataId].fields) || []; fieldsSelector = props => { const datasetId = props.filter.dataId[0]; if (!datasetId) { return []; } return get(props, ['datasets', datasetId, 'fields'], []); }; filterSelector = props => props.filters; nameSelector = props => props.filter.name; dataIdSelector = props => props.filter.dataId[0]; // only show current field and field that's not already been used as a filter availableFieldsSelector = createSelector( this.fieldsSelector, this.filterSelector, this.nameSelector, this.dataIdSelector, (fields, filters, name, dataId) => fields.filter( f => f.type && f.type !== ALL_FIELD_TYPES.geojson && (f.name[0] === name[0] || !filters.find( d => d.name[0] === f.name && d.dataId[0] === dataId )) ) ); filterApi(idx, value, dataId, datasets, auth, project, setFilterAPI) { const fieldName = value.name; var {fields, indexName} = datasets[dataId]; const fieldIdx = fields.findIndex(f => f.name === fieldName); const fieldType = fieldName && fields[fieldIdx].type === 'string' ? 'string' : (fieldName && fields[fieldIdx].type === 'integer') || fields[fieldIdx].type === 'real' ? 'numeric' : 'datetime'; // const userId = 'uid'; const {uid} = auth; const {isEdit} = project; // const {setFilterAPI} = this.props; // if fieldType is string inside filter then send string else send numeric const filterAPIData = { fieldName, userId: uid, permissionType: isEdit, fieldType, indexName }; const config = { headers: { 'Content-Type': 'application/json' } }; axios .post( `${ON_PREMESIS_URL}/geoiqutilities/filter/v1.0/fetch`, filterAPIData, config ) .then(result => { var domain; var histogram; const data = result.data.data; // this is written for real and integer fields const domainKeys = Object.keys(data); if (fieldType !== 'string') { histogram = domainKeys.reduce((accu, range) => { const count = data[range]; const bin = { count, x0: Number(range.split('&&')[0]), x1: Number(range.split('&&')[1]) }; accu.push(bin); return accu; }, []); histogram = histogram.sort((a, b) => a.x1 - b.x1); domain = [histogram[0].x0, histogram[histogram.length - 1].x1]; } else { domain = domainKeys; } // write data conversion to domain for string type fields setFilterAPI(idx, 'name', value.name, 0, dataId, domain, histogram); }); } render() { const {filter, setFilterAPI} = this.props; const {type, dataId} = filter; const FilterFilterComponent = (type && FilterPanelComponents[type]) || FilterPanelComponents.default; const allAvailableFields = this.availableFieldsSelector(this.props); return ( <StyledFilterPanel className="filter-panel"> <FilterFilterComponent allAvailableFields={allAvailableFields} dataId={dataId} filterApi={this.filterApi} setFilterAPI={setFilterAPI} {...this.props} /> </StyledFilterPanel> ); } }; // render() { // const { // datasets, // enlargeFilter, // filter, // idx, // isAnyFilterAnimating, // removeFilter, // setFilter, // toggleAnimation, // mapState, // auth, // project // } = this.props; // const {name, enlarged, type, dataId} = filter; // const FilterComponent = type && FilterComponents[FILTER_COMPONENTS[type]]; // const allAvailableFields = this.availableFieldsSelector(this.props); // return ( // <StyledFilterPanel className="filter-panel"> // <StyledFilterHeader // className="filter-panel__header" // labelRCGColorValues={datasets[dataId].color} // > // <FieldSelector // inputTheme="secondary" // fields={allAvailableFields} // value={Array.isArray(name) ? name[0] : name} // erasable={false} // onSelect={value => // this.filterApi(idx, value, dataId, datasets, auth, project) // } // /> // <PanelHeaderAction // id={filter.id} // tooltip="delete" // tooltipType="error" // onClick={removeFilter} // hoverColor={'errorColor'} // IconComponent={Trash} // /> // {type === FILTER_TYPES.timeRange && ( // <PanelHeaderAction // id={filter.id} // onClick={enlargeFilter} // tooltip="Time Playback" // IconComponent={Clock} // active={enlarged} // /> // )} // </StyledFilterHeader> // <StyledFilterContent className="filter-panel__content"> // {Object.keys(datasets).length > 1 && ( // <SourceDataSelector // inputTheme="secondary" // datasets={datasets} // disabled={filter.freeze} // dataId={filter.dataId} // onSelect={value => setFilter(idx, 'dataId', value)} // /> // )} // {type && !enlarged && ( // <div className="filter-panel__filter"> // <FilterComponent // filter={filter} // idx={idx} // isAnyFilterAnimating={isAnyFilterAnimating} // toggleAnimation={toggleAnimation} // setFilter={value => // setFilter(idx, 'value', value, 0, mapState, auth, project) // } // /> // </div> // )} // </StyledFilterContent> // </StyledFilterPanel> // ); // } // }; } export default FilterPanelFactory; // import React, { Component } from "react"; // import PropTypes from "prop-types"; // import { createSelector } from "reselect"; // import styled from "styled-components"; // import { FILTER_TYPES } from "utils/filter-utils"; // import { ALL_FIELD_TYPES } from "constants/default-settings"; // import FilterPanelHeaderFactory from "components/side-panel/filter-panel/filter-panel-header"; // import NewFilterPanelFactory from "components/filters/filter-panels/new-filter-panel"; // import TimeRangeFilterPanelFactory from "components/filters/filter-panels/time-range-filter-panel"; // import SingleSelectFilterPanelFactory from "components/filters/filter-panels/single-select-filter-panel"; // import MultiSelectFilterPanelFactory from "components/filters/filter-panels/multi-select-filter-panel"; // import RangeFilterPanelFactory from "components/filters/filter-panels/range-filter-panel"; // import PolygonFilterPanelFactory from "components/filters/filter-panels/polygon-filter-panel"; // const StyledFilterPanel = styled.div` // margin-bottom: 12px; // border-radius: 1px; // .filter-panel__filter { // margin-top: 24px; // } // `; // FilterPanelFactory.deps = [ // FilterPanelHeaderFactory, // NewFilterPanelFactory, // TimeRangeFilterPanelFactory, // SingleSelectFilterPanelFactory, // MultiSelectFilterPanelFactory, // RangeFilterPanelFactory, // PolygonFilterPanelFactory // ]; // function FilterPanelFactory( // FilterPanelHeader, // NewFilterPanel, // TimeRangeFilterPanel, // SingleSelectFilterPanel, // MultiSelectFilterPanel, // RangeFilterPanel, // PolygonFilterPanel // ) { // const FilterPanelComponents = { // default: NewFilterPanel, // [FILTER_TYPES.timeRange]: TimeRangeFilterPanel, // [FILTER_TYPES.select]: SingleSelectFilterPanel, // [FILTER_TYPES.multiSelect]: MultiSelectFilterPanel, // [FILTER_TYPES.range]: RangeFilterPanel, // [FILTER_TYPES.polygon]: PolygonFilterPanel // }; // return class FilterPanel extends Component { // static propTypes = { // idx: PropTypes.number, // filters: PropTypes.arrayOf(PropTypes.any).isRequired, // filter: PropTypes.object.isRequired, // setFilter: PropTypes.func.isRequired, // removeFilter: PropTypes.func.isRequired, // enlargeFilter: PropTypes.func.isRequired, // toggleAnimation: PropTypes.func.isRequired, // datasets: PropTypes.object, // showDatasetTable: PropTypes.func, // isAnyFilterAnimating: PropTypes.bool // }; // /* selectors */ // fieldsSelector = props => // (props.filter.dataId[0] && // props.datasets[props.filter.dataId[0]].fields) || // []; // filterSelector = props => props.filters; // nameSelector = props => props.filter.name; // dataIdSelector = props => props.filter.dataId[0]; // // only show current field and field that's not already been used as a filter // availableFieldsSelector = createSelector( // this.fieldsSelector, // this.filterSelector, // this.nameSelector, // this.dataIdSelector, // (fields, filters, name, dataId) => // fields.filter( // f => // f.type && // f.type !== ALL_FIELD_TYPES.geojson && // (f.name === name || // !filters.find(d => d.name === f.name && d.dataId === dataId)) // ) // ); // render() { // const { filter } = this.props; // const { type } = filter; // const FilterFilterComponent = // (type && FilterPanelComponents[type]) || FilterPanelComponents.default; // const allAvailableFields = this.availableFieldsSelector(this.props); // return ( // <StyledFilterPanel className="filter-panel"> // <FilterFilterComponent // allAvailableFields={allAvailableFields} // {...this.props} // /> // </StyledFilterPanel> // ); // } // }; // } // export default FilterPanelFactory;