Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add cluster layer #423

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
21 changes: 21 additions & 0 deletions common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ export const MAP_SAVED_OBJECT_TYPE = 'map';
// TODO: Replace with actual app icon
export const MAPS_APP_ICON = 'gisApp';
export const MAPS_VISUALIZATION_DESCRIPTION = 'Create map visualization with multiple layers';
export const CLUSTER_DEFAULT_FILL_TYPE = 'gradient';
export const CLUSTER_DEFAULT_PALETTE = 'blue';
export const CLUSTER_MIN_BORDER_THICKNESS = 0;
export const CLUSTER_MAX_BORDER_THICKNESS = 100;
export const CLUSTER_DEFAULT_MARKER_BORDER_THICKNESS = 1;
//This is a valid value in geohex,geotile and geohash aggregation.
export const CLUSTER_DEFAULT_PRECISION = 2;
export const CLUSTER_DEFAULT_METRIC_AGG = 'count';
export const CLUSTER_DEFAULT_CLUSTER_AGG = 'geohash_grid';

// Starting position [lng, lat] and zoom
export const MAP_INITIAL_STATE = {
Expand All @@ -91,12 +100,14 @@ export enum DASHBOARDS_MAPS_LAYER_NAME {
OPENSEARCH_MAP = 'OpenSearch map',
DOCUMENTS = 'Documents',
CUSTOM_MAP = 'Custom map',
CLUSTER = 'Cluster',
}

export enum DASHBOARDS_MAPS_LAYER_TYPE {
OPENSEARCH_MAP = 'opensearch_vector_tile_map',
DOCUMENTS = 'documents',
CUSTOM_MAP = 'custom_map',
CLUSTER = 'cluster',
}

export enum DASHBOARDS_CUSTOM_MAPS_LAYER_TYPE {
Expand All @@ -108,12 +119,14 @@ export enum DASHBOARDS_MAPS_LAYER_ICON {
OPENSEARCH_MAP = 'globe',
DOCUMENTS = 'document',
CUSTOM_MAP = 'globe',
CLUSTER = 'heatmap',
}

export enum DASHBOARDS_MAPS_LAYER_DESCRIPTION {
OPENSEARCH_MAP = 'Use default OpenSearch basemaps.',
DOCUMENTS = 'View points, lines, and polygons on the map.',
CUSTOM_MAP = 'Configure maps to use a custom map source.',
CLUSTER = 'Geospatial data grouped to show density.',
}

export const DOCUMENTS = {
Expand All @@ -137,6 +150,13 @@ export const CUSTOM_MAP = {
description: DASHBOARDS_MAPS_LAYER_DESCRIPTION.CUSTOM_MAP,
};

export const CLUSTER = {
name: DASHBOARDS_MAPS_LAYER_NAME.CLUSTER,
type: DASHBOARDS_MAPS_LAYER_TYPE.CLUSTER,
icon: DASHBOARDS_MAPS_LAYER_ICON.CLUSTER,
description: DASHBOARDS_MAPS_LAYER_DESCRIPTION.CLUSTER,
};

export interface Layer {
name: DASHBOARDS_MAPS_LAYER_NAME;
type: DASHBOARDS_MAPS_LAYER_TYPE;
Expand All @@ -153,6 +173,7 @@ export const LAYER_ICON_TYPE_MAP: { [key: string]: string } = {
[DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP]: 'globe',
[DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS]: 'document',
[DASHBOARDS_MAPS_LAYER_TYPE.CUSTOM_MAP]: 'globe',
[DASHBOARDS_MAPS_LAYER_TYPE.CLUSTER]: 'heatmap',
};

// refer https://github.com/opensearch-project/i18n-plugin/blob/main/DEVELOPER_GUIDE.md#new-locale for OSD supported languages
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@types/wellknown": "^0.5.4",
"cypress-file-upload": "^5.0.8",
"geojson": "^0.5.0",
"h3-js": "^4.1.0",
"install": "^0.13.0",
"maplibre-gl": "^2.4.0",
"prettier": "^2.1.1",
Expand Down
3 changes: 2 additions & 1 deletion public/components/add_layer_panel/add_layer_panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
Layer,
NEW_MAP_LAYER_DEFAULT_PREFIX,
MAX_LAYER_LIMIT,
CLUSTER,
} from '../../../common';
import { getLayerConfigMap } from '../../utils/getIntialConfig';
import { ConfigSchema } from '../../../common/config';
Expand Down Expand Up @@ -67,7 +68,7 @@ export const AddLayerPanel = ({
addLayer(initLayerConfig);
}

const dataLayers = [DOCUMENTS];
const dataLayers = [CLUSTER, DOCUMENTS];
const dataLayerItems = Object.values(dataLayers).map((layerItem, index) => {
return (
<EuiKeyPadMenuItem
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { Fragment, useState } from 'react';
import { EuiSpacer, EuiTabbedContent } from '@elastic/eui';
import { ClusterLayerSpecification } from '../../../model/mapLayerType';
import { LayerBasicSettings } from '../layer_basic_settings';
import { ClusterLayerSource } from './cluster_layer_source';
import { ClusterLayerStyle } from './style';
import { IndexPattern } from '../../../../../../src/plugins/data/common';
import { useCallback } from 'react';

interface Props {
selectedLayerConfig: ClusterLayerSpecification;
setSelectedLayerConfig: Function;
setIsUpdateDisabled: Function;
isLayerExists: Function;
}

export const ClusterLayerConfigPanel = (props: Props) => {
const [indexPattern, setIndexPattern] = useState<IndexPattern | null | undefined>();
const [dataUpdateDisabled, setDataUpdateDisabled] = useState(true);

const setIsUpdateDisabled = useCallback(
(isUpdateDisabled: boolean, isFromDataPanel = false) => {
//we can't judge whether source is valid only by selectLayerConfig like documents. We need a state to record it.
if (isFromDataPanel) {
setDataUpdateDisabled(isUpdateDisabled);
props.setIsUpdateDisabled(isUpdateDisabled);
} else {
props.setIsUpdateDisabled(dataUpdateDisabled || isUpdateDisabled);
}
},
[dataUpdateDisabled]
);

const newProps = {
...props,
setIsUpdateDisabled,
indexPattern,
setIndexPattern,
};

const tabs = [
{
id: 'data-source--id',
name: 'Data',
content: (
<Fragment>
<EuiSpacer size="m" />
<ClusterLayerSource {...newProps} />
</Fragment>
),
testsubj: 'dataTab',
},
{
id: 'style--id',
name: 'Style',
content: (
<Fragment>
<EuiSpacer size="m" />
<ClusterLayerStyle {...newProps} />
</Fragment>
),
testsubj: 'styleTab',
},
{
id: 'settings--id',
name: 'Settings',
content: (
<Fragment>
<EuiSpacer size="m" />
<LayerBasicSettings {...newProps} />
</Fragment>
),
testsubj: 'settingsTab',
},
];
return <EuiTabbedContent tabs={tabs} initialSelectedTab={tabs[0]} />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useState, useEffect } from 'react';
import { EuiSpacer } from '@elastic/eui';
import { useOpenSearchDashboards } from '../../../../../../src/plugins/opensearch_dashboards_react/public';
import { TimeRange, IndexPattern } from '../../../../../../src/plugins/data/public';
import { ClusterLayerSpecification } from '../../../model/mapLayerType';
import { DataSourceSection } from './data_source_section';
import { MapServices } from '../../../types';
import { ClusterSection } from './cluster_section';
import { MetricSection } from './metric_section';
import { FilterSection } from './filter_section';

interface Props {
setSelectedLayerConfig: Function;
selectedLayerConfig: ClusterLayerSpecification;
setIsUpdateDisabled: Function;
indexPattern: IndexPattern | null | undefined;
setIndexPattern: Function;
timeRange?: TimeRange;
}

const defaultCanUpdateMap = {
index: false,
metric: false,
cluster: false,
};
export type CanUpdateMapType = typeof defaultCanUpdateMap;

export const ClusterLayerSource = ({
setIsUpdateDisabled,
setSelectedLayerConfig,
selectedLayerConfig,
indexPattern,
setIndexPattern,
}: Props) => {
const {
services: {
data: { indexPatterns },
},
} = useOpenSearchDashboards<MapServices>();

const [canUpdateMap, setCanUpdateMap] = useState(defaultCanUpdateMap);

useEffect(() => {
const keys = Object.keys(canUpdateMap);
const canUpdate = keys.every((key) => canUpdateMap[key as keyof CanUpdateMapType]);
setIsUpdateDisabled(!canUpdate, true);
}, [canUpdateMap]);

useEffect(() => {
const selectIndexPattern = async () => {
if (selectedLayerConfig.source.indexPatternId) {
const selectedIndexPattern = await indexPatterns.get(
selectedLayerConfig.source.indexPatternId
);
setIndexPattern(selectedIndexPattern);
}
};
selectIndexPattern();
}, [indexPatterns, selectedLayerConfig.source.indexPatternId]);

// Handle the side effects of index pattern change
useEffect(() => {
const source = { ...selectedLayerConfig.source };
// when index pattern changed, reset aggs
if (indexPattern && indexPattern.id !== selectedLayerConfig.source.indexPatternId) {
source.indexPatternId = indexPattern.id ?? '';
source.indexPatternRefName = indexPattern.title;
setSelectedLayerConfig({
...selectedLayerConfig,
source,
});
}
}, [indexPattern]);

const commonProps = {
selectedLayerConfig,
setSelectedLayerConfig,
indexPattern,
setCanUpdateMap,
};

return (
<>
<DataSourceSection
indexPattern={indexPattern}
setIndexPattern={setIndexPattern}
setCanUpdateMap={setCanUpdateMap}
/>
<EuiSpacer size="s" />
<ClusterSection {...commonProps} />

<EuiSpacer size="s" />
<MetricSection {...commonProps} />
<EuiSpacer size="s" />
<FilterSection {...commonProps} />
</>
);
};
Loading