Skip to content

Commit

Permalink
Add search query ability on map (#370) (#385)
Browse files Browse the repository at this point in the history
Signed-off-by: Junqiu Lei <junqiu@amazon.com>
(cherry picked from commit e6c0004)

Co-authored-by: Junqiu Lei <junqiu@amazon.com>
  • Loading branch information
opensearch-trigger-bot[bot] and junqiu-lei committed Apr 5, 2023
1 parent 1c2077b commit c24d7d1
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 129 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
* Add geo shape query filter ([#319](https://github.com/opensearch-project/dashboards-maps/pull/319))
* Add shape filter UI button ([#329](https://github.com/opensearch-project/dashboards-maps/pull/329))
* Add tooltip to draw shape filter ([#330](https://github.com/opensearch-project/dashboards-maps/pull/330))
* Add search query ability on map([#370](https://github.com/opensearch-project/dashboards-maps/pull/370))

### Enhancements
* Enhance layer visibility status display ([#299](https://github.com/opensearch-project/dashboards-maps/pull/299))
Expand Down
54 changes: 30 additions & 24 deletions public/components/map_container/map_container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,9 @@ import { GeoShapeRelation } from '@opensearch-project/opensearch/api/types';
import { LayerControlPanel } from '../layer_control_panel';
import './map_container.scss';
import { DrawFilterProperties, FILTER_DRAW_MODE, MAP_INITIAL_STATE } from '../../../common';
import { MapLayerSpecification } from '../../model/mapLayerType';
import { DataLayerSpecification, MapLayerSpecification } from '../../model/mapLayerType';
import { DrawFilterShape } from '../toolbar/spatial_filter/draw_filter_shape';
import {
Filter,
IndexPattern,
Query,
RefreshInterval,
TimeRange,
} from '../../../../../src/plugins/data/public';
import { IndexPattern } from '../../../../../src/plugins/data/public';
import { MapState } from '../../model/mapState';
import {
renderDataLayers,
Expand All @@ -38,6 +32,7 @@ import { TOOLTIP_STATE } from '../../../common';
import { SpatialFilterToolbar } from '../toolbar/spatial_filter/filter_toolbar';
import { DrawFilterShapeHelper } from '../toolbar/spatial_filter/display_draw_helper';
import { ShapeFilter } from '../../../../../src/plugins/data/common';
import { DashboardProps } from '../map_page/map_page';

interface MapContainerProps {
setLayers: (layers: MapLayerSpecification[]) => void;
Expand All @@ -48,10 +43,7 @@ interface MapContainerProps {
mapState: MapState;
mapConfig: ConfigSchema;
isReadOnlyMode: boolean;
timeRange?: TimeRange;
refreshConfig?: RefreshInterval;
filters?: Filter[];
query?: Query;
dashboardProps?: DashboardProps;
isUpdatingLayerRender: boolean;
setIsUpdatingLayerRender: (isUpdatingLayerRender: boolean) => void;
addSpatialFilter: (shape: ShapeFilter, label: string | null, relation: GeoShapeRelation) => void;
Expand All @@ -66,10 +58,7 @@ export const MapContainer = ({
mapState,
mapConfig,
isReadOnlyMode,
timeRange,
refreshConfig,
filters,
query,
dashboardProps,
isUpdatingLayerRender,
setIsUpdatingLayerRender,
addSpatialFilter,
Expand Down Expand Up @@ -140,7 +129,7 @@ export const MapContainer = ({
// Rerender layers with 200ms debounce to avoid calling the search API too frequently, especially when
// resizing the window, the "moveend" event could be fired constantly
const debouncedRenderLayers = debounce(() => {
renderDataLayers(layers, mapState, services, maplibreRef, timeRange, filters, query);
renderDataLayers(layers, mapState, services, maplibreRef, dashboardProps);
}, 200);

if (maplibreRef.current) {
Expand All @@ -157,17 +146,21 @@ export const MapContainer = ({
// Update data layers when state bar enable auto refresh
useEffect(() => {
let intervalId: NodeJS.Timeout | undefined;
if (refreshConfig && !refreshConfig.pause) {
if (dashboardProps && dashboardProps.refreshConfig && !dashboardProps.refreshConfig.pause) {
const { refreshConfig } = dashboardProps;
intervalId = setInterval(() => {
renderDataLayers(layers, mapState, services, maplibreRef, timeRange, filters, query);
renderDataLayers(layers, mapState, services, maplibreRef, dashboardProps);
}, refreshConfig.value);
}
return () => clearInterval(intervalId);
}, [refreshConfig]);
}, [dashboardProps?.refreshConfig]);

// Update data layers when global filter is updated
useEffect(() => {
renderDataLayers(layers, mapState, services, maplibreRef, timeRange, filters, query);
if (!mapState?.spatialMetaFilters) {
return;
}
renderDataLayers(layers, mapState, services, maplibreRef, dashboardProps);
}, [mapState.spatialMetaFilters]);

useEffect(() => {
Expand All @@ -183,10 +176,15 @@ export const MapContainer = ({
handleBaseLayerRender(selectedLayerConfig, maplibreRef);
} else {
updateIndexPatterns();
handleDataLayerRender(selectedLayerConfig, mapState, services, maplibreRef);
handleDataLayerRender(
selectedLayerConfig as DataLayerSpecification,
mapState,
services,
maplibreRef
);
}
} else {
renderDataLayers(layers, mapState, services, maplibreRef, timeRange, filters, query);
renderDataLayers(layers, mapState, services, maplibreRef, dashboardProps);
renderBaseLayers(layers, maplibreRef);
// Because of async layer rendering, layers order is not guaranteed, so we need to order layers
// after all layers are rendered.
Expand All @@ -197,7 +195,15 @@ export const MapContainer = ({
return () => {
maplibreRef.current!.off('idle', orderLayersAfterRenderLoaded);
};
}, [layers, mounted, timeRange, filters, query, mapState, isReadOnlyMode]);
}, [
layers,
mounted,
dashboardProps?.query,
dashboardProps?.timeRange,
dashboardProps?.filters,
mapState,
isReadOnlyMode,
]);

useEffect(() => {
const currentTooltipState: TOOLTIP_STATE =
Expand Down
43 changes: 20 additions & 23 deletions public/components/map_page/map_page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
import React, { useEffect, useRef, useState } from 'react';
import { useParams } from 'react-router-dom';
import { Map as Maplibre } from 'maplibre-gl';
import { SimpleSavedObject } from '../../../../../src/core/public';
import classNames from 'classnames';
import { GeoShapeRelation } from '@opensearch-project/opensearch/api/types';
import { SimpleSavedObject } from '../../../../../src/core/public';
import { MapContainer } from '../map_container';
import { MapTopNavMenu } from '../map_top_nav';
import { MapServices } from '../../types';
Expand All @@ -29,32 +30,31 @@ import {
} from '../../../../../src/plugins/data/public';
import { MapState } from '../../model/mapState';
import { ConfigSchema } from '../../../common/config';
import {GeoShapeFilterMeta, ShapeFilter} from "../../../../../src/plugins/data/common";
import {GeoShapeRelation} from "@opensearch-project/opensearch/api/types";
import {buildGeoShapeFilterMeta} from "../../model/geo/filter";
import {FilterBar} from "../filter_bar/filter_bar";
import { GeoShapeFilterMeta, ShapeFilter } from '../../../../../src/plugins/data/common';
import { buildGeoShapeFilterMeta } from '../../model/geo/filter';
import { FilterBar } from '../filter_bar/filter_bar';

interface MapPageProps {
mapConfig: ConfigSchema;
}

interface MapComponentProps {
mapConfig: ConfigSchema;
mapIdFromSavedObject: string;
export interface DashboardProps {
timeRange?: TimeRange;
isReadOnlyMode: boolean;
refreshConfig?: RefreshInterval;
filters?: Filter[];
query?: Query;
}

interface MapComponentProps {
mapConfig: ConfigSchema;
mapIdFromSavedObject: string;
dashboardProps?: DashboardProps;
}

export const MapComponent = ({
mapIdFromSavedObject,
mapConfig,
timeRange,
isReadOnlyMode,
refreshConfig,
filters,
query,
dashboardProps,
}: MapComponentProps) => {
const { services } = useOpenSearchDashboards<MapServices>();
const {
Expand All @@ -67,6 +67,7 @@ export const MapComponent = ({
const maplibreRef = useRef<Maplibre | null>(null);
const [mapState, setMapState] = useState<MapState>(getInitialMapState());
const [isUpdatingLayerRender, setIsUpdatingLayerRender] = useState(true);
const isReadOnlyMode = !!dashboardProps;

useEffect(() => {
if (mapIdFromSavedObject) {
Expand All @@ -87,8 +88,9 @@ export const MapComponent = ({
setLayersIndexPatterns(savedIndexPatterns);
});
} else {
const initialDefaultLayer: MapLayerSpecification =
getLayerConfigMap(mapConfig)[OPENSEARCH_MAP_LAYER.type];
const initialDefaultLayer: MapLayerSpecification = getLayerConfigMap(mapConfig)[
OPENSEARCH_MAP_LAYER.type
] as MapLayerSpecification;
initialDefaultLayer.name = MAP_LAYER_DEFAULT_NAME;
setLayers([initialDefaultLayer]);
}
Expand Down Expand Up @@ -125,8 +127,6 @@ export const MapComponent = ({
{isReadOnlyMode ? null : (
<MapTopNavMenu
mapIdFromUrl={mapIdFromSavedObject}
isReadOnlyMode={isReadOnlyMode}
timeRange={timeRange}
savedMapObject={savedMapObject}
layers={layers}
layersIndexPatterns={layersIndexPatterns}
Expand Down Expand Up @@ -156,10 +156,7 @@ export const MapComponent = ({
mapState={mapState}
mapConfig={mapConfig}
isReadOnlyMode={isReadOnlyMode}
timeRange={timeRange}
refreshConfig={refreshConfig}
filters={filters}
query={query}
dashboardProps={dashboardProps}
isUpdatingLayerRender={isUpdatingLayerRender}
setIsUpdatingLayerRender={setIsUpdatingLayerRender}
addSpatialFilter={addSpatialFilter}
Expand All @@ -170,5 +167,5 @@ export const MapComponent = ({

export const MapPage = ({ mapConfig }: MapPageProps) => {
const { id: mapId } = useParams<{ id: string }>();
return <MapComponent mapIdFromSavedObject={mapId} mapConfig={mapConfig} isReadOnlyMode={false} />;
return <MapComponent mapIdFromSavedObject={mapId} mapConfig={mapConfig} />;
};
32 changes: 12 additions & 20 deletions public/components/map_top_nav/top_nav_menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,13 @@ interface MapTopNavMenuProps {
maplibreRef: any;
mapState: MapState;
setMapState: (mapState: MapState) => void;
isReadOnlyMode: boolean;
timeRange?: TimeRange;
originatingApp?: string;
setIsUpdatingLayerRender: (isUpdatingLayerRender: boolean) => void;
}

export const MapTopNavMenu = ({
mapIdFromUrl,
savedMapObject,
isReadOnlyMode,
timeRange,
layers,
layersIndexPatterns,
maplibreRef,
Expand Down Expand Up @@ -99,26 +95,21 @@ export const MapTopNavMenu = ({

const handleQuerySubmit = ({ query, dateRange }: { query?: Query; dateRange: TimeRange }) => {
setIsUpdatingLayerRender(true);
if (query) {
setMapState({ ...mapState, query });
}
if (dateRange) {
setMapState({ ...mapState, timeRange: dateRange });
}
const updatedMapState = {
...mapState,
...(query && { query }),
...(dateRange && { timeRange: dateRange }),
};
setMapState(updatedMapState);
};

useEffect(() => {
if (!isReadOnlyMode) {
setDateFrom(mapState.timeRange.from);
setDateTo(mapState.timeRange.to);
} else {
setDateFrom(timeRange!.from);
setDateTo(timeRange!.to);
}
setDateFrom(mapState.timeRange.from);
setDateTo(mapState.timeRange.to);
setQueryConfig(mapState.query);
setIsRefreshPaused(mapState.refreshInterval.pause);
setRefreshIntervalValue(mapState.refreshInterval.value);
}, [mapState, timeRange]);
}, [mapState.timeRange, mapState.query, mapState.refreshInterval]);

const onRefreshChange = useCallback(
({ isPaused, refreshInterval }: { isPaused: boolean; refreshInterval: number }) => {
Expand All @@ -142,14 +133,15 @@ export const MapTopNavMenu = ({
}, [services, mapIdFromUrl, layers, title, description, mapState, originatingApp]);

return (
// @ts-ignore
<TopNavMenu
appName={MAPS_APP_ID}
config={config}
setMenuMountPoint={setHeaderActionMenu}
indexPatterns={layersIndexPatterns || []}
showSearchBar={!isReadOnlyMode}
showSearchBar={true}
showFilterBar={false}
showDatePicker={!isReadOnlyMode}
showDatePicker={true}
showQueryBar={true}
showSaveQuery={true}
showQueryInput={true}
Expand Down
14 changes: 9 additions & 5 deletions public/embeddable/map_component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { MapComponent } from '../components/map_page/';
import { OpenSearchDashboardsContextProvider } from '../../../../src/plugins/opensearch_dashboards_react/public';
import { MapServices } from '../types';
import { TimeRange } from '../../../../src/plugins/data/common';
import { DashboardProps } from '../components/map_page/map_page';

interface Props {
embeddable: MapEmbeddable;
Expand All @@ -35,16 +36,19 @@ export function MapEmbeddableComponentInner({ embeddable, input }: Props) {
setQuery(input.query);
}, [input.refreshConfig, input.timeRange, input.filters, input.query]);

const dashboardProps: DashboardProps = {
timeRange,
refreshConfig,
filters,
query,
};

return (
<OpenSearchDashboardsContextProvider services={services}>
<MapComponent
mapConfig={embeddable.getMapConfig()}
mapIdFromSavedObject={input.savedObjectId}
timeRange={timeRange}
isReadOnlyMode={true}
refreshConfig={refreshConfig}
filters={filters}
query={query}
dashboardProps={dashboardProps}
/>
</OpenSearchDashboardsContextProvider>
);
Expand Down
Loading

0 comments on commit c24d7d1

Please sign in to comment.