('');
+
+ const onChangeCustomMapURL = (e: any) => {
+ setCustomMapURL(e.target.value);
+ setIsUpdateDisabled(false);
+ setSelectedLayerConfig({
+ ...selectedLayerConfig,
+ source: {
+ ...selectedLayerConfig?.source,
+ url: e.target.value,
+ },
+ });
+ };
+
+ const onChangeCustomMapAttribution = (e: any) => {
+ setCustomMapAttribution(e.target.value);
+ setIsUpdateDisabled(false);
+ setSelectedLayerConfig({
+ ...selectedLayerConfig,
+ source: {
+ ...selectedLayerConfig?.source,
+ attribution: e.target.value,
+ },
+ });
+ };
+
+ const isInvalidURL = (url: string): boolean => {
+ try {
+ new URL(url);
+ return false;
+ } catch (e) {
+ return true;
+ }
+ };
+
+ useEffect(() => {
+ setCustomMapURL(selectedLayerConfig.source.url);
+ }, [selectedLayerConfig.source.url]);
+
+ useEffect(() => {
+ setCustomMapAttribution(selectedLayerConfig.source.attribution);
+ }, [selectedLayerConfig.source.attribution]);
+
+ useEffect(() => {
+ const disableUpdate = isInvalidURL(customMapURL);
+ setIsUpdateDisabled(disableUpdate);
+ }, [customMapURL, setIsUpdateDisabled]);
+
+ return (
+
+
+
+
+
+ Raster Tile URL
+
+
+ {isInvalidURL(customMapURL) && (
+
+
+
+ )}
+
+
+ Raster Tile Attribution
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/maps_dashboards/public/components/layer_config/layer_config_panel.tsx b/maps_dashboards/public/components/layer_config/layer_config_panel.tsx
index eee3bef1..e9a15d7f 100644
--- a/maps_dashboards/public/components/layer_config/layer_config_panel.tsx
+++ b/maps_dashboards/public/components/layer_config/layer_config_panel.tsx
@@ -29,6 +29,7 @@ import { DASHBOARDS_MAPS_LAYER_TYPE } from '../../../common';
import { DocumentLayerConfigPanel } from './documents_config/document_layer_config_panel';
import { layersTypeIconMap } from '../../model/layersFunctions';
import { IndexPattern } from '../../../../../src/plugins/data/public';
+import { CustomMapConfigPanel } from './custom_map_config/custom_map_config_panel';
interface Props {
closeLayerConfigPanel: Function;
@@ -128,6 +129,14 @@ export const LayerConfigPanel = ({
isLayerExists={isLayerExists}
/>
)}
+ {selectedLayerConfig.type === DASHBOARDS_MAPS_LAYER_TYPE.CUSTOM_MAP && (
+
+ )}
diff --git a/maps_dashboards/public/components/layer_control_panel/layer_control_panel.tsx b/maps_dashboards/public/components/layer_control_panel/layer_control_panel.tsx
index 10c719f8..05cf4d28 100644
--- a/maps_dashboards/public/components/layer_control_panel/layer_control_panel.tsx
+++ b/maps_dashboards/public/components/layer_control_panel/layer_control_panel.tsx
@@ -33,7 +33,11 @@ import {
LAYER_PANEL_SHOW_LAYER_ICON,
LAYER_PANEL_HIDE_LAYER_ICON,
} from '../../../common';
-import { LayerActions, layersFunctionMap } from '../../model/layersFunctions';
+import {
+ LayerActions,
+ layersFunctionMap,
+ referenceLayerTypeLookup,
+} from '../../model/layersFunctions';
import { useOpenSearchDashboards } from '../../../../../src/plugins/opensearch_dashboards_react/public';
import { MapServices } from '../../types';
import {
@@ -95,7 +99,10 @@ export const LayerControlPanel = memo(
if (!selectedLayerConfig) {
return;
}
- if (selectedLayerConfig.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP) {
+ if (
+ selectedLayerConfig.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP ||
+ selectedLayerConfig.type === DASHBOARDS_MAPS_LAYER_TYPE.CUSTOM_MAP
+ ) {
handleReferenceLayerRender(selectedLayerConfig, maplibreRef, undefined);
} else {
updateIndexPatterns();
@@ -107,7 +114,7 @@ export const LayerControlPanel = memo(
} else {
layers.forEach((layer) => {
const beforeLayerId = getMapBeforeLayerId(layer);
- if (layer.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP) {
+ if (referenceLayerTypeLookup[layer.type]) {
handleReferenceLayerRender(layer, maplibreRef, beforeLayerId);
} else {
handleDataLayerRender(layer, mapState, services, maplibreRef, beforeLayerId);
@@ -231,10 +238,10 @@ export const LayerControlPanel = memo(
if (!selectedLayerConfig) {
return;
}
- if (selectedLayerConfig.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP) {
- return;
- }
- if (!selectedLayerConfig.source.indexPatternId) {
+ if (
+ selectedLayerConfig.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP ||
+ selectedLayerConfig.type === DASHBOARDS_MAPS_LAYER_TYPE.CUSTOM_MAP
+ ) {
return;
}
const findIndexPattern = layersIndexPatterns.find(
diff --git a/maps_dashboards/public/model/customLayerFunctions.ts b/maps_dashboards/public/model/customLayerFunctions.ts
new file mode 100644
index 00000000..dbfd60fc
--- /dev/null
+++ b/maps_dashboards/public/model/customLayerFunctions.ts
@@ -0,0 +1,115 @@
+import {
+ Map as Maplibre,
+ AttributionControl,
+ RasterSourceSpecification,
+} from 'maplibre-gl';
+import { CustomLayerSpecification, OSMLayerSpecification } from './mapLayerType';
+
+interface MaplibreRef {
+ current: Maplibre | null;
+}
+
+const getCurrentStyleLayers = (maplibreRef: MaplibreRef) => {
+ return maplibreRef.current?.getStyle().layers || [];
+};
+
+const layerExistInMbSource = (layerConfig: CustomLayerSpecification, maplibreRef: MaplibreRef) => {
+ const layers = getCurrentStyleLayers(maplibreRef);
+ for (const layer in layers) {
+ if (layers[layer].id.includes(layerConfig.id)) {
+ return true;
+ }
+ }
+ return false;
+};
+
+const updateLayerConfig = (layerConfig: CustomLayerSpecification, maplibreRef: MaplibreRef) => {
+ const maplibreInstance = maplibreRef.current;
+ if (maplibreInstance) {
+ const customLauer = maplibreInstance.getLayer(layerConfig.id);
+ if (customLauer) {
+ maplibreInstance.setPaintProperty(
+ layerConfig.id,
+ 'raster-opacity',
+ layerConfig.opacity / 100
+ );
+ maplibreInstance.setLayerZoomRange(
+ layerConfig.id,
+ layerConfig.zoomRange[0],
+ layerConfig.zoomRange[1]
+ );
+ const rasterLayerSource = maplibreInstance.getSource(
+ layerConfig.id
+ )! as RasterSourceSpecification;
+ if (rasterLayerSource.attribution !== layerConfig.source?.attribution) {
+ rasterLayerSource.attribution = layerConfig?.source?.attribution;
+ maplibreInstance._controls.forEach((control) => {
+ if (control instanceof AttributionControl) {
+ control._updateAttributions();
+ }
+ });
+ }
+ if (rasterLayerSource.tiles![0] !== layerConfig.source?.url) {
+ rasterLayerSource.tiles = [layerConfig?.source?.url];
+ maplibreInstance.style.sourceCaches[layerConfig.id].clearTiles();
+ maplibreInstance.style.sourceCaches[layerConfig.id].update(maplibreInstance.transform);
+ maplibreInstance.triggerRepaint();
+ }
+ }
+ }
+};
+
+const addNewLayer = (
+ layerConfig: CustomLayerSpecification,
+ maplibreRef: MaplibreRef,
+ beforeLayerId: string | undefined
+) => {
+ const maplibreInstance = maplibreRef.current;
+ if (maplibreInstance) {
+ const layerSource = layerConfig?.source;
+ maplibreInstance.addSource(layerConfig.id, {
+ type: 'raster',
+ tiles: [layerSource?.url],
+ tileSize: 256,
+ attribution: layerSource?.attribution,
+ });
+ maplibreInstance.addLayer(
+ {
+ id: layerConfig.id,
+ type: 'raster',
+ source: layerConfig.id,
+ },
+ beforeLayerId
+ );
+ }
+};
+
+export const CustomLayerFunctions = {
+ render: (
+ maplibreRef: MaplibreRef,
+ layerConfig: CustomLayerSpecification,
+ beforeLayerId: string | undefined
+ ) => {
+ if (layerExistInMbSource(layerConfig, maplibreRef)) {
+ updateLayerConfig(layerConfig, maplibreRef);
+ } else {
+ addNewLayer(layerConfig, maplibreRef, beforeLayerId);
+ }
+ },
+ remove: (maplibreRef: MaplibreRef, layerConfig: OSMLayerSpecification) => {
+ const layers = getCurrentStyleLayers(maplibreRef);
+ layers.forEach((mbLayer: { id: any }) => {
+ if (mbLayer.id.includes(layerConfig.id)) {
+ maplibreRef.current?.removeLayer(mbLayer.id);
+ }
+ });
+ },
+ hide: (maplibreRef: MaplibreRef, layerConfig: OSMLayerSpecification) => {
+ const layers = getCurrentStyleLayers(maplibreRef);
+ layers.forEach((mbLayer: { id: any }) => {
+ if (mbLayer.id.includes(layerConfig.id)) {
+ maplibreRef.current?.setLayoutProperty(mbLayer.id, 'visibility', layerConfig.visibility);
+ }
+ });
+ },
+};
diff --git a/maps_dashboards/public/model/layersFunctions.ts b/maps_dashboards/public/model/layersFunctions.ts
index 24355ae4..a4311b6a 100644
--- a/maps_dashboards/public/model/layersFunctions.ts
+++ b/maps_dashboards/public/model/layersFunctions.ts
@@ -12,6 +12,7 @@ import {
import { OSMLayerFunctions } from './OSMLayerFunctions';
import { DocumentLayerFunctions } from './documentLayerFunctions';
import { MapLayerSpecification } from './mapLayerType';
+import { CustomLayerFunctions } from './customLayerFunctions';
interface MaplibreRef {
current: Maplibre | null;
@@ -55,11 +56,13 @@ export const LayerActions = {
export const layersFunctionMap: { [key: string]: any } = {
[DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP]: OSMLayerFunctions,
[DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS]: DocumentLayerFunctions,
+ [DASHBOARDS_MAPS_LAYER_TYPE.CUSTOM_MAP]: CustomLayerFunctions,
};
export const layersTypeNameMap: { [key: string]: string } = {
[DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP]: DASHBOARDS_MAPS_LAYER_NAME.OPENSEARCH_MAP,
[DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS]: DASHBOARDS_MAPS_LAYER_NAME.DOCUMENTS,
+ [DASHBOARDS_MAPS_LAYER_TYPE.CUSTOM_MAP]: DASHBOARDS_MAPS_LAYER_NAME.CUSTOM_MAP,
};
const getCurrentStyleLayers = (maplibreRef: MaplibreRef) => {
@@ -84,3 +87,9 @@ export const layersTypeIconMap: { [key: string]: string } = {
[DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP]: DASHBOARDS_MAPS_LAYER_ICON.OPENSEARCH_MAP,
[DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS]: DASHBOARDS_MAPS_LAYER_ICON.DOCUMENTS,
};
+
+export const referenceLayerTypeLookup = {
+ [DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP]: true,
+ [DASHBOARDS_MAPS_LAYER_TYPE.CUSTOM_MAP]: true,
+ [DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS]: false,
+};
diff --git a/maps_dashboards/public/model/mapLayerType.ts b/maps_dashboards/public/model/mapLayerType.ts
index ec0e4de1..8484b26a 100644
--- a/maps_dashboards/public/model/mapLayerType.ts
+++ b/maps_dashboards/public/model/mapLayerType.ts
@@ -6,7 +6,10 @@
import { Filter } from '../../../../src/plugins/data/public';
/* eslint @typescript-eslint/consistent-type-definitions: ["error", "type"] */
-export type MapLayerSpecification = OSMLayerSpecification | DocumentLayerSpecification;
+export type MapLayerSpecification =
+ | OSMLayerSpecification
+ | DocumentLayerSpecification
+ | CustomLayerSpecification;
export type OSMLayerSpecification = {
name: string;
@@ -49,3 +52,17 @@ export type DocumentLayerSpecification = {
markerSize: number;
};
};
+
+export type CustomLayerSpecification = {
+ name: string;
+ id: string;
+ type: 'custom_map';
+ description: string;
+ zoomRange: number[];
+ opacity: number;
+ visibility: string;
+ source: {
+ url: string;
+ attribution: string;
+ };
+};
diff --git a/maps_dashboards/public/utils/getIntialConfig.ts b/maps_dashboards/public/utils/getIntialConfig.ts
index 114e20c9..919a6399 100644
--- a/maps_dashboards/public/utils/getIntialConfig.ts
+++ b/maps_dashboards/public/utils/getIntialConfig.ts
@@ -61,27 +61,17 @@ export const getLayerConfigMap = () => ({
markerSize: DOCUMENTS_DEFAULT_MARKER_SIZE,
},
},
- //TODO: update custom layer config
[CUSTOM_MAP.type]: {
name: '',
description: '',
type: CUSTOM_MAP.type,
id: uuidv4(),
zoomRange: [MAP_DEFAULT_MIN_ZOOM, MAP_DEFAULT_MAX_ZOOM],
- opacity: MAP_DATA_LAYER_DEFAULT_OPACITY,
+ opacity: MAP_REFERENCE_LAYER_DEFAULT_OPACITY,
visibility: LAYER_VISIBILITY.VISIBLE,
source: {
- indexPatternRefName: undefined,
- geoFieldType: undefined,
- geoFieldName: undefined,
- documentRequestNumber: DOCUMENTS_DEFAULT_REQUEST_NUMBER,
- tooltipFields: DOCUMENTS_DEFAULT_TOOLTIPS,
- showTooltips: DOCUMENTS_DEFAULT_SHOW_TOOLTIPS,
- },
- style: {
- ...getStyleColor(),
- borderThickness: MAP_LAYER_DEFAULT_BORDER_THICKNESS,
- markerSize: DOCUMENTS_DEFAULT_MARKER_SIZE,
+ url: '',
+ attribution: '',
},
},
});