Skip to content

Commit

Permalink
[feat] duckdb plugin (#2798)
Browse files Browse the repository at this point in the history
- optional duckdb plugin support in demo-app

Signed-off-by: Ihor Dykhta <[email protected]>
  • Loading branch information
ilyabo authored Jan 27, 2025
1 parent 029bcc5 commit 4031451
Show file tree
Hide file tree
Showing 23 changed files with 876 additions and 252 deletions.
20 changes: 11 additions & 9 deletions examples/demo-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@
"@auth0/auth0-spa-js": "^2.1.2",
"@carto/toolkit": "0.0.1-rc.18",
"@emotion/is-prop-valid": "^1.2.1",
"@kepler.gl/actions": "^3.1.0-alpha.5",
"@kepler.gl/cloud-providers": "^3.1.0-alpha.5",
"@kepler.gl/components": "^3.1.0-alpha.5",
"@kepler.gl/constants": "^3.1.0-alpha.5",
"@kepler.gl/processors": "^3.1.0-alpha.5",
"@kepler.gl/reducers": "^3.1.0-alpha.5",
"@kepler.gl/schemas": "^3.1.0-alpha.5",
"@kepler.gl/styles": "^3.1.0-alpha.5",
"@kepler.gl/utils": "^3.1.0-alpha.5",
"@kepler.gl/actions": "^3.1.0-alpha.6",
"@kepler.gl/cloud-providers": "^3.1.0-alpha.6",
"@kepler.gl/components": "^3.1.0-alpha.6",
"@kepler.gl/constants": "^3.1.0-alpha.6",
"@kepler.gl/duckdb": "^3.1.0-alpha.6",
"@kepler.gl/processors": "^3.1.0-alpha.6",
"@kepler.gl/reducers": "^3.1.0-alpha.6",
"@kepler.gl/schemas": "^3.1.0-alpha.6",
"@kepler.gl/styles": "^3.1.0-alpha.6",
"@kepler.gl/utils": "^3.1.0-alpha.6",
"@loaders.gl/arrow": "^4.3.2",
"@loaders.gl/core": "^4.3.2",
"@loaders.gl/csv": "^4.3.2",
Expand All @@ -46,6 +47,7 @@
"react-intl": "^6.3.0",
"react-markdown": "^6.0.3",
"react-redux": "^8.0.5",
"react-resizable-panels": "^2.1.7",
"react-router": "3.2.5",
"react-router-redux": "^4.0.8",
"react-virtualized": "^9.21.0",
Expand Down
94 changes: 68 additions & 26 deletions examples/demo-app/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ import {
setStartScreenCapture,
setScreenCaptured
} from '@kepler.gl/ai-assistant';
import {theme} from '@kepler.gl/styles';
import {panelBorderColor, theme} from '@kepler.gl/styles';
import {useSelector} from 'react-redux';
import {ParsedConfig} from '@kepler.gl/types';
import {getApplicationConfig} from '@kepler.gl/utils';
import {SqlPanel} from '@kepler.gl/duckdb';
import Banner from './components/banner';
import Announcement, {FormLink} from './components/announcement';
import {replaceLoadDataModal} from './factories/load-data-modal';
Expand All @@ -32,8 +36,15 @@ import {
onLoadCloudMapSuccess
} from './actions';

import {loadCloudMap, addDataToMap, replaceDataInMap} from '@kepler.gl/actions';
import {
loadCloudMap,
addDataToMap,
replaceDataInMap,
toggleMapControl,
toggleModal
} from '@kepler.gl/actions';
import {CLOUD_PROVIDERS} from './cloud-providers';
import {Panel, PanelGroup, PanelResizeHandle} from 'react-resizable-panels';

const KeplerGl = require('@kepler.gl/components').injectComponents([
replaceLoadDataModal(),
Expand All @@ -60,6 +71,7 @@ import sampleIconCsv from './data/sample-icon-csv';
import sampleGpsData from './data/sample-gps-data';
import sampleRowData, {config as rowDataConfig} from './data/sample-row-data';
import {processCsvData, processGeojson, processRowObject} from '@kepler.gl/processors';

/* eslint-enable no-unused-vars */

// This implements the default behavior from styled-components v5
Expand Down Expand Up @@ -113,14 +125,31 @@ const CONTAINER_STYLE = {
left: 0,
top: 0,
display: 'flex',
flexDirection: 'row'
flexDirection: 'column',
backgroundColor: '#333'
};

const StyledResizeHandle = styled(PanelResizeHandle)`
background-color: ${panelBorderColor};
&:hover {
background-color: #555;
}
width: 100%;
height: 5px;
cursor: row-resize;
`;

const App = props => {
const [showBanner, toggleShowBanner] = useState(false);
const {params: {id, provider} = {}, location: {query = {}} = {}} = props;
const dispatch = useDispatch();

// TODO find another way to check for existence of duckDb plugin
const duckDbPluginEnabled = (getApplicationConfig().plugins || []).some(p => p.name === 'duckdb');

const isSqlPanelOpen = useSelector(
state => duckDbPluginEnabled && state?.demo?.keplerGl?.map?.uiState.mapControls.sqlPanel?.active
);
const prevQueryRef = useRef<number>(null);

useEffect(() => {
Expand Down Expand Up @@ -155,6 +184,11 @@ const App = props => {
dispatch(loadRemoteMap({dataUrl: query.mapUrl}));
}

if (duckDbPluginEnabled && query.sql) {
dispatch(toggleMapControl('sqlPanel', 0));
dispatch(toggleModal(null));
}

// delay zs to show the banner
// if (!window.localStorage.getItem(BannerKey)) {
// window.setTimeout(_showBanner, 3000);
Expand Down Expand Up @@ -405,7 +439,7 @@ const App = props => {
options: {
keepExistingConfig: true
},
config: sampleGeojsonConfig
config: sampleGeojsonConfig as ParsedConfig
})
);
}, [dispatch]);
Expand Down Expand Up @@ -503,7 +537,7 @@ const App = props => {
data: processCsvData(sampleS2Data)
}
],
config: s2MapConfig,
config: s2MapConfig as ParsedConfig,
options: {
keepExistingConfig: true
}
Expand Down Expand Up @@ -594,27 +628,35 @@ const App = props => {
<Announcement onDisable={_disableBanner} />
</Banner>
<div style={CONTAINER_STYLE}>
<div style={{flexGrow: 1}}>
<AutoSizer>
{({height, width}) => (
<KeplerGl
mapboxApiAccessToken={CLOUD_PROVIDERS_CONFIGURATION.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={combinedMessages}
onExportToCloudSuccess={onExportFileSuccess}
onLoadCloudMapSuccess={onLoadCloudMapSuccess}
featureFlags={DEFAULT_FEATURE_FLAGS}
/>
)}
</AutoSizer>
</div>
<PanelGroup direction="vertical">
<Panel defaultSize={isSqlPanelOpen ? 60 : 100}>
<AutoSizer>
{({height, width}) => (
<KeplerGl
mapboxApiAccessToken={CLOUD_PROVIDERS_CONFIGURATION.MAPBOX_TOKEN}
id="map"
getState={keplerGlGetState}
width={width}
height={height}
cloudProviders={CLOUD_PROVIDERS}
localeMessages={combinedMessages}
onExportToCloudSuccess={onExportFileSuccess}
onLoadCloudMapSuccess={onLoadCloudMapSuccess}
featureFlags={DEFAULT_FEATURE_FLAGS}
/>
)}
</AutoSizer>
</Panel>

{isSqlPanelOpen && (
<>
<StyledResizeHandle />
<Panel defaultSize={40} minSize={20}>
<SqlPanel initialSql={query.sql || ''} />
</Panel>
</>
)}
</PanelGroup>
</div>
</ScreenshotWrapper>
</GlobalStyle>
Expand Down
56 changes: 56 additions & 0 deletions examples/demo-app/src/components/map-control/sql-panel-control.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: MIT
// Copyright contributors to the kepler.gl project

import React, {useCallback, ComponentType} from 'react';

import {MapControlButton, MapControlTooltipFactory} from '@kepler.gl/components';
import {MapControls} from '@kepler.gl/types';

interface SQLControlIcons {
sqlPanelIcon: ComponentType<any>;
}

export type SqlPanelControlProps = {
mapControls: MapControls;
onToggleMapControl: (control: string) => void;
actionIcons: SQLControlIcons;
};

SqlPanelControlFactory.deps = [MapControlTooltipFactory];

export default function SqlPanelControlFactory(
MapControlTooltip: ReturnType<typeof MapControlTooltipFactory>
): React.FC<SqlPanelControlProps> {
const SqlPanelControl = ({mapControls, onToggleMapControl}) => {
const onClick = useCallback(
event => {
event.preventDefault();
onToggleMapControl('sqlPanel');
},
[onToggleMapControl]
);

const showControl = mapControls?.sqlPanel?.show;
if (!showControl) {
return null;
}

const active = mapControls?.sqlPanel?.active;
return (
<MapControlTooltip
id="show-sql-panel"
message={active ? 'tooltip.hideSQLPanel' : 'tooltip.showSQLPanel'}
>
<MapControlButton
className="map-control-button toggle-sql-panel"
onClick={onClick}
active={active}
>
SQL
</MapControlButton>
</MapControlTooltip>
);
};

return SqlPanelControl;
}
4 changes: 3 additions & 1 deletion examples/demo-app/src/constants/localization.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ const en = {
'* CORS policy must be defined on your custom url domain in order to be accessible. For more info ',
'loadRemoteMap.clickHere':
'<a rel="noopener noreferrer" target="_blank" href="{corsLink}">click here</a>',
'loadRemoteMap.fetch': 'Fetch'
'loadRemoteMap.fetch': 'Fetch',
'tooltip.hideSQLPanel': 'Hide SQL Panel',
'tooltip.showSQLPanel': 'Show SQL Panel'
};

export const messages = {
Expand Down
6 changes: 5 additions & 1 deletion examples/demo-app/src/factories/map-control.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from '@kepler.gl/components';
import {AiAssistantControlFactory, AiAssistantManagerFactory} from '@kepler.gl/ai-assistant';
import {SampleMapPanel} from '../components/map-control/map-control';
import SqlPanelControlFactory from '../components/map-control/sql-panel-control';

const StyledMapControlPanel = styled.div`
position: relative;
Expand Down Expand Up @@ -57,20 +58,23 @@ CustomMapControlFactory.deps = [
EffectManagerFactory,
AiAssistantControlFactory,
AiAssistantManagerFactory,
SqlPanelControlFactory,
...MapControlFactory.deps
];
function CustomMapControlFactory(
EffectControl,
EffectManager,
AiAssistantControl,
AiAssistantManager,
SqlPanelControl,
...deps
) {
const MapControl = MapControlFactory(...deps);
const actionComponents = [
...(MapControl.defaultActionComponents ?? []),
EffectControl,
AiAssistantControl
AiAssistantControl,
SqlPanelControl
];

const CustomMapControl = props => {
Expand Down
25 changes: 24 additions & 1 deletion examples/demo-app/src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {processGeojson, processRowObject, processArrowTable} from '@kepler.gl/pr
import keplerGlReducer, {combinedUpdaters, uiStateUpdaters} from '@kepler.gl/reducers';
import KeplerGlSchema from '@kepler.gl/schemas';
import {KeplerTable} from '@kepler.gl/table';
import {getApplicationConfig} from '@kepler.gl/utils';
import {getApplicationConfig, initApplicationConfig} from '@kepler.gl/utils';
// import keplerGlDuckdbPlugin, {KeplerGlDuckDbTable} from '@kepler.gl/duckdb';

import {
INIT,
Expand All @@ -26,6 +27,14 @@ import {
import {CLOUD_PROVIDERS_CONFIGURATION} from '../constants/default-settings';
import {generateHashId} from '../utils/strings';

// initialize kepler application with duckdb plugin
initApplicationConfig({
// plugins: [keplerGlDuckdbPlugin],
// table: KeplerGlDuckDbTable
});

const {DEFAULT_MAP_CONTROLS} = uiStateUpdaters;

// INITIAL_APP_STATE
const initialAppState = {
appName: 'example',
Expand Down Expand Up @@ -75,6 +84,20 @@ const demoReducer = combineReducers({
...DEFAULT_EXPORT_MAP[[EXPORT_MAP_FORMATS.HTML]],
exportMapboxAccessToken: CLOUD_PROVIDERS_CONFIGURATION.EXPORT_MAPBOX_TOKEN
}
},
mapControls: {
...DEFAULT_MAP_CONTROLS,
// TODO find a better way not to add extra controls optionally - from plugin?
...((getApplicationConfig().plugins || []).some(p => p.name === 'duckdb')
? {
sqlPanel: {
active: false,
activeMapIndex: 0,
disableClose: false,
show: true
}
}
: {})
}
},
visState: {
Expand Down
4 changes: 3 additions & 1 deletion examples/demo-app/src/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import {combineReducers, createStore, applyMiddleware, compose} from 'redux';
import {routerReducer, routerMiddleware} from 'react-router-redux';
import {browserHistory} from 'react-router';
import {enhanceReduxMiddleware} from '@kepler.gl/reducers';
import {createLogger} from 'redux-logger';
import thunk from 'redux-thunk';

import {enhanceReduxMiddleware} from '@kepler.gl/reducers';

// eslint-disable-next-line no-unused-vars
import Window from 'global/window';

Expand Down
Loading

0 comments on commit 4031451

Please sign in to comment.