diff --git a/libs/shared/lib/components/colorComponents/colorPicker/index.tsx b/libs/shared/lib/components/colorComponents/colorPicker/index.tsx
index 0fb827637df25845552e368cb45db145a3f6e8ae..bd9a01d16778937875963bab89eb4d10e26ee834 100644
--- a/libs/shared/lib/components/colorComponents/colorPicker/index.tsx
+++ b/libs/shared/lib/components/colorComponents/colorPicker/index.tsx
@@ -4,7 +4,7 @@ import { useFloating, autoUpdate, offset, flip, shift, useInteractions, useClick
 
 type Props = {
   value: any;
-  updateValue: any;
+  updateValue: (val: [number, number, number]) => void;
 };
 
 export default function ColorPicker({ value, updateValue }: Props) {
@@ -52,9 +52,10 @@ export default function ColorPicker({ value, updateValue }: Props) {
             <TwitterPicker
               triangle="top-right"
               color={{ r: value[0], g: value[1], b: value[2] }}
-              onChangeComplete={(color: any) => {
+              onChangeComplete={(color) => {
+                console.log(color);
                 const rgb = color.rgb;
-                const newValue = [rgb.r, rgb.g, rgb.b];
+                const newValue: [number, number, number] = [rgb.r, rgb.g, rgb.b];
                 updateValue(newValue);
                 setOpen(false);
               }}
diff --git a/libs/shared/lib/vis/visualizations/mapvis/MapSettings.tsx b/libs/shared/lib/vis/visualizations/mapvis/MapSettings.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..a5682a798b9b00f9a9b39f3cbf7292737411d0e1
--- /dev/null
+++ b/libs/shared/lib/vis/visualizations/mapvis/MapSettings.tsx
@@ -0,0 +1,103 @@
+import React, { useEffect, useMemo } from 'react';
+import { SettingsContainer } from '../../components/config';
+import { layerSettings, layerTypes } from './layers';
+import { Input } from '../../..';
+import { VisualizationSettingsPropTypes } from '../../common';
+import { MapEdgeData, MapNodeData, MapNodeOrEdgeData, MapProps } from './mapvis';
+
+export const MapSettings = ({ settings, graphMetadata, updateSettings }: VisualizationSettingsPropTypes<MapProps>) => {
+  const DataLayerSettings = settings.layer && layerSettings?.[settings.layer];
+
+  const spatialAttributes = useMemo(() => {
+    return Object.fromEntries(
+      graphMetadata.nodes.labels.map((node) => [
+        node,
+        Object.entries(graphMetadata.nodes.types[node].attributes)
+          .filter(([, value]) => value.dimension === 'numerical')
+          .map(([key]) => key),
+      ]),
+    );
+  }, [graphMetadata]);
+
+  useEffect(() => {
+    const nodes = Object.fromEntries(
+      graphMetadata.nodes.labels.map(
+        (node) =>
+          [
+            node,
+            {
+              color: [252, 185, 0],
+              hidden: false,
+              fixed: true,
+              min: 0,
+              max: 10,
+              radius: 1,
+              lat: undefined,
+              lon: undefined,
+              collapsed: false,
+              shape: 'circle',
+              size: 10,
+              ...(settings?.nodes && node in settings.nodes ? settings.nodes[node] : {}),
+            },
+          ] as [string, MapNodeData],
+      ),
+    );
+
+    const edges = Object.fromEntries(
+      graphMetadata.edges.labels.map(
+        (edge) =>
+          [
+            edge,
+            {
+              color: [171, 184, 195],
+              hidden: false,
+              fixed: true,
+              min: 0,
+              max: 10,
+              radius: 1,
+              sizeAttribute: undefined,
+              collapsed: false,
+              size: 10,
+              ...(settings?.edges && edge in settings.edges ? settings.edges[edge] : {}),
+            },
+          ] as [string, MapEdgeData],
+      ),
+    );
+
+    updateSettings({ nodes: nodes, edges: edges });
+  }, [graphMetadata]);
+
+  useEffect(() => {
+    // Autodetect a lat or lon attribute if not already set
+    Object.keys(settings.nodes).forEach((node) => {
+      if ((!settings.nodes[node].lat || !settings.nodes[node].lon) && node in spatialAttributes) {
+        const lat = spatialAttributes[node].find((attr) => attr.includes('latitude'));
+        const lon = spatialAttributes[node].find((attr) => attr.includes('longitude'));
+        if (lat && lon) {
+          updateSettings({ nodes: { ...settings.nodes, [node]: { ...settings.nodes[node], lat, lon } } });
+        }
+      }
+    });
+  }, [spatialAttributes, settings]);
+
+  return (
+    <SettingsContainer>
+      <Input
+        label="Data layer"
+        type="dropdown"
+        inline
+        value={settings.layer}
+        options={Object.keys(layerTypes)}
+        onChange={(val) => updateSettings({ layer: val as string })}
+      />
+      {DataLayerSettings && !!spatialAttributes && (
+        <DataLayerSettings
+          settings={settings}
+          graphMetadata={graphMetadata}
+          updateSettings={updateSettings}
+          spatialAttributes={spatialAttributes}
+        />
+      )}
+    </SettingsContainer>
+  );
+};
diff --git a/libs/shared/lib/vis/visualizations/mapvis/search.tsx b/libs/shared/lib/vis/visualizations/mapvis/SearchBar.tsx
similarity index 90%
rename from libs/shared/lib/vis/visualizations/mapvis/search.tsx
rename to libs/shared/lib/vis/visualizations/mapvis/SearchBar.tsx
index f781ba7981b1ab6e870ab027c205da61cdb28b7c..d6d37127e623dfd4b31ece91e4ea2bd57753ccc1 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/search.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/SearchBar.tsx
@@ -4,10 +4,10 @@ import { addError } from '@graphpolaris/shared/lib/data-access/store/configSlice
 import React, { useState } from 'react';
 
 interface SearchBarProps {
-  onSearch: (boundingbox: [number, number, number, number]) => void;
+  onSearch: (boundingBox: [number, number, number, number]) => void;
 }
 
-const SearchBar: React.FC<SearchBarProps> = ({ onSearch }) => {
+export const SearchBar: React.FC<SearchBarProps> = ({ onSearch }) => {
   const dispatch = useAppDispatch();
   const [query, setQuery] = useState('');
   const [isLoading, setIsLoading] = useState(false);
@@ -42,5 +42,3 @@ const SearchBar: React.FC<SearchBarProps> = ({ onSearch }) => {
     </div>
   );
 };
-
-export default SearchBar;
diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/choropleth-layer/ChoroplethOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/choropleth-layer/ChoroplethOptions.tsx
index fea160aa1dad5b964ffce4938057a4b12ff9bdc6..a05d7e7bb27c3c425d7061d924f971acc1aa8acd 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/layers/choropleth-layer/ChoroplethOptions.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/layers/choropleth-layer/ChoroplethOptions.tsx
@@ -4,7 +4,7 @@ import { VisualizationSettingsPropTypes } from '@graphpolaris/shared/lib/vis/com
 import { MapProps } from '../../mapvis';
 import { EntityPill, Icon, Input, RelationPill } from '@graphpolaris/shared/lib/components';
 
-export default function ChoroplethOptions({ settings, graphMetadata, updateSettings }: VisualizationSettingsPropTypes<MapProps>) {
+export function ChoroplethOptions({ settings, graphMetadata, updateSettings }: VisualizationSettingsPropTypes<MapProps>) {
   const [collapsed, setCollapsed] = useState<Record<string, boolean>>({});
 
   useEffect(() => {
@@ -59,22 +59,21 @@ export default function ChoroplethOptions({ settings, graphMetadata, updateSetti
             <div>
               <Input
                 inline
-                label="Longitude"
+                label="Latitude"
                 type="dropdown"
-                value={settings?.[nodeType]?.lon}
+                value={settings?.[nodeType]?.lat}
                 options={[...spatialAttributes[nodeType]]}
                 disabled={!settings.node || spatialAttributes[nodeType].length < 1}
-                onChange={(val) => updateSettings({ [nodeType]: { ...settings?.[nodeType], lon: val as string } })}
+                onChange={(val) => updateSettings({ [nodeType]: { ...settings?.[nodeType], lat: val as string } })}
               />
-
               <Input
                 inline
-                label="Latitude"
+                label="Longitude"
                 type="dropdown"
-                value={settings?.[nodeType]?.lat}
+                value={settings?.[nodeType]?.lon}
                 options={[...spatialAttributes[nodeType]]}
                 disabled={!settings.node || spatialAttributes[nodeType].length < 1}
-                onChange={(val) => updateSettings({ [nodeType]: { ...settings?.[nodeType], lat: val as string } })}
+                onChange={(val) => updateSettings({ [nodeType]: { ...settings?.[nodeType], lon: val as string } })}
               />
             </div>
           )}
diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/heatmap-layer/HeatLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/heatmap-layer/HeatLayer.tsx
index 5ad4670fb2e4ff7b0774be551e5665efc3e27331..4453d6dbc22f71f29964e37a2822ecc01836044c 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/layers/heatmap-layer/HeatLayer.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/layers/heatmap-layer/HeatLayer.tsx
@@ -2,10 +2,10 @@ import React from 'react';
 import { CompositeLayer, HeatmapLayer } from 'deck.gl';
 import * as d3 from 'd3';
 import { getDistance } from '../../utlis';
-import { Edge, LayerProps } from '../../mapvis.types';
+import { CompositeLayerType, Edge, LayerProps } from '../../mapvis.types';
 import { Node } from '@graphpolaris/shared/lib/data-access';
 
-export class HeatLayer extends CompositeLayer<LayerProps> {
+export class HeatLayer extends CompositeLayer<CompositeLayerType> {
   static type = 'Heatmap';
 
   updateState({ changeFlags }: { changeFlags: any }) {
@@ -16,7 +16,7 @@ export class HeatLayer extends CompositeLayer<LayerProps> {
     // Generates a path between source and target nodes
     return edges.map((edge: Edge, index) => {
       const length = getDistance(edge.path[0], edge.path[1]);
-      const nSegments = length * this.props.config.nSegments;
+      const nSegments = length * this.props.settings.nSegments;
 
       let xscale = d3
         .scaleLinear()
@@ -44,22 +44,24 @@ export class HeatLayer extends CompositeLayer<LayerProps> {
   }
 
   renderLayers() {
-    const { graph, config, getNodeLocation, setLayerIds, metaData } = this.props;
+    const { data, settings, getNodeLocation, selected, setLayerIds, graphMetadata } = this.props;
 
     const layers: any[] = [];
     const layerIds: string[] = [];
 
-    metaData.nodes.labels.forEach((label: string) => {
-      const layerId = `${label}-nodes-iconlayer`;
+    graphMetadata.nodes.labels.forEach((label: string) => {
+      const layerId = `${label}-nodes-heatmaplayer`;
       layerIds.push(layerId);
 
+      console.log(settings.nodes[label].size);
       layers.push(
-        new HeatmapLayer({
-          id: `${label}-nodes-iconlayer`,
-          data: graph.nodes.filter((node: Node) => node.label === label),
-          visible: !config[label]?.hidden,
+        new HeatmapLayer<Node>({
+          id: layerId,
+          data: data.nodes.filter((node: Node) => node.label === label),
+          visible: !settings.nodes[label].hidden,
           getPosition: (d: any) => getNodeLocation(d.id),
-          getWeight: (d) => 1,
+          getWeight: (d: any) => settings.nodes[label].size,
+          radiusPixels: settings.nodes[label].size,
           aggregation: 'SUM',
         }),
       );
@@ -68,77 +70,6 @@ export class HeatLayer extends CompositeLayer<LayerProps> {
     setLayerIds(layerIds);
 
     return layers;
-
-    // const layers = [];
-
-    // if (config.type === 'location') {
-    //   layers.push(
-    //     new HeatmapLayer(
-    //       this.getSubLayerProps({
-    //         data:
-    //           config.location === 'source'
-    //             ? graph.getEdges().map((edge: Edge) => graph.getNode(edge.from))
-    //             : graph.getEdges().map((edge: Edge) => graph.getNode(edge.to)),
-    //         getPosition: (d: any) => [d.attributes.long, d.attributes.lat],
-    //         aggregation: 'SUM',
-    //       }),
-    //     ),
-    //   );
-    // } else if (config.type === 'distance') {
-    //   layers.push(
-    //     new HeatmapLayer(
-    //       this.getSubLayerProps({
-    //         data: graph.getEdges().map((edge: Edge) => {
-    //           const from = graph.getNode(edge.from);
-    //           const from_coords: [number, number] = [from.attributes.long, from.attributes.lat];
-    //           const to = graph.getNode(edge.to);
-    //           const to_coords: [number, number] = [to.attributes.long, to.attributes.lat];
-    //           const dist = getDistance(from_coords, to_coords);
-    //           const node = config.location === 'source' ? from : to;
-    //           return { ...node, distance: dist };
-    //         }),
-    //         threshold: 0.5,
-    //         getPosition: (d: any) => [d.attributes.long, d.attributes.lat],
-    //         getWeight: (d: any) => d.distance,
-    //         aggregation: 'MEAN',
-    //       }),
-    //     ),
-    //   );
-    // } else if (config.type === 'attribute') {
-    //   console.log('attribute');
-    //   layers.push(
-    //     new HeatmapLayer(
-    //       this.getSubLayerProps({
-    //         data: graph.getEdges().map((edge: Edge) => graph.getNode(edge.from)),
-    //         getPosition: (d: any) => [d.attributes.long, d.attributes.lat],
-    //         getWeight: (d: any) => {
-    //           console.log(d, d.attributes[config.attribute]);
-    //           return 1;
-    //         },
-    //         aggregation: 'SUM',
-    //       }),
-    //     ),
-    //   );
-    // } else if (config.type === 'path') {
-    //   layers.push(
-    //     new HeatmapLayer(
-    //       this.getSubLayerProps({
-    //         data: this.createSegments(
-    //           graph.getEdges().map((edge: Edge) => {
-    //             return {
-    //               ...edge,
-    //               path: [this.props.graph.getNodeLocation(edge.from), this.props.graph.getNodeLocation(edge.to)],
-    //             };
-    //           }),
-    //         ).flatMap((edge) => edge.path),
-    //         getPosition: (d: any) => d,
-    //         aggregation: 'SUM',
-    //       }),
-    //     ),
-    //   );
-    // }
-
-    // return [...layers];
   }
 }
 
diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/heatmap-layer/HeatLayerOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/heatmap-layer/HeatLayerOptions.tsx
index cae0eb34ac26b97a0daf9a2ddb61ed22b1ac6e63..8b0ce65c20586fd5e727f00c19d474182b43cd84 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/layers/heatmap-layer/HeatLayerOptions.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/layers/heatmap-layer/HeatLayerOptions.tsx
@@ -2,87 +2,76 @@ import React, { useState, useMemo, useEffect } from 'react';
 import { VisualizationSettingsPropTypes } from '@graphpolaris/shared/lib/vis/common';
 import { MapProps } from '../../mapvis';
 import { Button, EntityPill, Input } from '@graphpolaris/shared/lib/components';
+import { MapLayerSettingsPropTypes } from '..';
 
-export default function HeatLayerOptions({ settings, graphMetadata, updateSettings }: VisualizationSettingsPropTypes<MapProps>) {
-  const [collapsed, setCollapsed] = useState<Record<string, boolean>>({});
-
-  useEffect(() => {
-    graphMetadata.nodes.labels.forEach((node) => {
-      updateSettings({
-        [node]: {
-          color: [0, 0, 0],
-          hidden: false,
-          fixed: true,
-          min: 0,
-          max: 10,
-          radius: 1,
-          sizeAttribute: '',
-          lon: '',
-          lat: '',
-          ...settings?.[node],
-        },
-      });
-    });
-  }, [graphMetadata]);
-
-  const spatialAttributes: { [id: string]: string[] } = {};
-  graphMetadata.nodes.labels.forEach((node) => {
-    spatialAttributes[node] = Object.entries(graphMetadata.nodes.types[node].attributes)
-      .filter(([, value]) => value.dimension === 'numerical')
-      .map(([key]) => key);
-  });
-
+export function HeatLayerOptions({ settings, graphMetadata, updateSettings, spatialAttributes }: MapLayerSettingsPropTypes) {
   const handleCollapseToggle = (nodeType: string) => {
-    setCollapsed((prevCollapsed) => ({
-      ...prevCollapsed,
-      [nodeType]: !prevCollapsed[nodeType],
-    }));
+    settings.nodes[nodeType].collapsed = !settings.nodes[nodeType].collapsed;
+    updateSettings({ nodes: settings.nodes });
   };
 
   return (
     <div>
-      {graphMetadata.nodes.labels.map((nodeType) => (
-        <div className="mt-2" key={nodeType}>
-          <div className="flex items-center">
-            <div className="w-3/4 mr-6 cursor-pointer" onClick={() => handleCollapseToggle(nodeType)}>
-              <EntityPill title={nodeType} />
-            </div>
-            <div className="w-1/2">
-              <Button
-                iconComponent={settings?.[nodeType].hidden ? 'icon-[ic--baseline-visibility-off]' : 'icon-[ic--baseline-visibility]'}
-                variant="ghost"
-                onClick={() => {
-                  updateSettings({ [nodeType]: { ...settings?.[nodeType], hidden: !settings?.[nodeType].hidden as boolean } });
-                }}
-              />
-            </div>
-          </div>
-
-          {!collapsed[nodeType] && (
-            <div>
-              <Input
-                inline
-                label="Longitude"
-                type="dropdown"
-                value={settings?.[nodeType]?.lon}
-                options={[...spatialAttributes[nodeType]]}
-                disabled={!settings.node || spatialAttributes[nodeType].length < 1}
-                onChange={(val) => updateSettings({ [nodeType]: { ...settings?.[nodeType], lon: val as string } })}
-              />
+      {settings?.nodes &&
+        Object.keys(settings.nodes).map((nodeType) => {
+          const nodeSettings = settings.nodes[nodeType];
+          return (
+            <div className="mt-2" key={nodeType}>
+              <div className="flex items-center">
+                <div className="w-3/4 mr-6 cursor-pointer" onClick={() => handleCollapseToggle(nodeType)}>
+                  <EntityPill title={nodeType} />
+                </div>
+                <div className="w-1/2">
+                  <Button
+                    iconComponent={nodeSettings.hidden ? 'icon-[ic--baseline-visibility-off]' : 'icon-[ic--baseline-visibility]'}
+                    variant="ghost"
+                    onClick={() =>
+                      updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, hidden: !nodeSettings.hidden } } })
+                    }
+                  />
+                </div>
+              </div>
 
-              <Input
-                inline
-                label="Latitude"
-                type="dropdown"
-                value={settings?.[nodeType]?.lat}
-                options={[...spatialAttributes[nodeType]]}
-                disabled={!settings.node || spatialAttributes[nodeType].length < 1}
-                onChange={(val) => updateSettings({ [nodeType]: { ...settings?.[nodeType], lat: val as string } })}
-              />
+              {!nodeSettings.collapsed && (
+                <div>
+                  <Input
+                    inline
+                    label="Latitude"
+                    type="dropdown"
+                    value={nodeSettings.lat}
+                    options={[...(spatialAttributes[nodeType] || [])]}
+                    disabled={!settings.node || spatialAttributes[nodeType].length < 1}
+                    onChange={(val) => {
+                      updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, lat: String(val) } } });
+                    }}
+                  />
+                  <Input
+                    inline
+                    label="Longitude"
+                    type="dropdown"
+                    value={nodeSettings.lon}
+                    options={[...(spatialAttributes[nodeType] || [])]}
+                    disabled={!settings.node || spatialAttributes[nodeType].length < 1}
+                    onChange={(val) => {
+                      updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, lon: String(val) } } });
+                    }}
+                  />
+                  <Input
+                    label="Size"
+                    type="slider"
+                    min={0}
+                    max={40}
+                    step={1}
+                    value={nodeSettings.size}
+                    onChange={(val) => {
+                      updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, size: Number(val) } } });
+                    }}
+                  />
+                </div>
+              )}
             </div>
-          )}
-        </div>
-      ))}
+          );
+        })}
     </div>
   );
 }
diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/icon-layer/IconLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/icon-layer/IconLayer.tsx
index aea4bf5b208a316579897e272868f1e6267a0690..e4a3e1e09765baa15391b61469ae35b5d1f378ab 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/layers/icon-layer/IconLayer.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/layers/icon-layer/IconLayer.tsx
@@ -1,14 +1,14 @@
 import React from 'react';
 import { CompositeLayer } from 'deck.gl';
 import { IconLayer } from '@deck.gl/layers';
-import { LayerProps } from '../../mapvis.types';
+import { CompositeLayerType, LayerProps } from '../../mapvis.types';
 import { Node } from '@graphpolaris/shared/lib/data-access';
 
 const ICON_MAPPING = {
   marker: { x: 0, y: 0, width: 128, height: 128, mask: false },
 };
 
-export class NodeIconLayer extends CompositeLayer<LayerProps> {
+export class NodeIconLayer extends CompositeLayer<CompositeLayerType> {
   static type = 'Icon';
 
   updateState({ changeFlags }: { changeFlags: any }) {
@@ -16,26 +16,26 @@ export class NodeIconLayer extends CompositeLayer<LayerProps> {
   }
 
   renderLayers() {
-    const { graph, config, getNodeLocation, setLayerIds, metaData } = this.props;
+    const { data, settings, getNodeLocation, setLayerIds, graphMetadata } = this.props;
 
     const layers: any[] = [];
     const layerIds: string[] = [];
 
-    metaData.nodes.labels.forEach((label: string) => {
+    graphMetadata.nodes.labels.forEach((label: string) => {
       const layerId = `${label}-nodes-iconlayer`;
       layerIds.push(layerId);
 
       layers.push(
         new IconLayer({
           id: layerId,
-          data: graph.nodes.filter((node: Node) => node.label === label),
-          visible: !config[label].hidden,
+          data: data.nodes.filter((node: Node) => node.label === label),
+          visible: !settings.nodes[label].hidden,
           iconAtlas: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/icon-atlas.png',
           iconMapping: ICON_MAPPING,
           sizeScale: 10,
           pickable: true,
           getIcon: (d: any) => 'marker',
-          getColor: (d: any) => config[label].color,
+          getColor: (d: any) => settings.nodes[label].color,
           getPosition: (d: any) => getNodeLocation(d._id),
           getSize: (d: any) => 3,
         }),
diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/icon-layer/IconOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/icon-layer/IconOptions.tsx
index 878dfd78738c60642ed2511519aa235fe8e57121..3643d74b3a80b36e6ec38683bcfd45c35f014c29 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/layers/icon-layer/IconOptions.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/layers/icon-layer/IconOptions.tsx
@@ -4,7 +4,7 @@ import { VisualizationSettingsPropTypes } from '@graphpolaris/shared/lib/vis/com
 import { MapProps } from '../../mapvis';
 import { EntityPill, Icon, Input, RelationPill } from '@graphpolaris/shared/lib/components';
 
-export default function IconOptions({ settings, graphMetadata, updateSettings }: VisualizationSettingsPropTypes<MapProps>) {
+export function IconOptions({ settings, graphMetadata, updateSettings }: VisualizationSettingsPropTypes<MapProps>) {
   const [collapsed, setCollapsed] = useState<Record<string, boolean>>({});
 
   useEffect(() => {
@@ -59,23 +59,23 @@ export default function IconOptions({ settings, graphMetadata, updateSettings }:
             <div>
               <Input
                 inline
-                label="Longitude"
+                label="Latitude"
                 type="dropdown"
-                value={settings?.[nodeType]?.lon}
+                value={settings?.[nodeType]?.lat}
                 options={[...spatialAttributes[nodeType]]}
                 disabled={!settings.node || spatialAttributes[nodeType].length < 1}
-                onChange={(val) => updateSettings({ [nodeType]: { ...settings?.[nodeType], lon: val as string } })}
+                onChange={(val) => updateSettings({ [nodeType]: { ...settings?.[nodeType], lat: val as string } })}
               />
-
               <Input
                 inline
-                label="Latitude"
+                label="Longitude"
                 type="dropdown"
-                value={settings?.[nodeType]?.lat}
+                value={settings?.[nodeType]?.lon}
                 options={[...spatialAttributes[nodeType]]}
                 disabled={!settings.node || spatialAttributes[nodeType].length < 1}
-                onChange={(val) => updateSettings({ [nodeType]: { ...settings?.[nodeType], lat: val as string } })}
+                onChange={(val) => updateSettings({ [nodeType]: { ...settings?.[nodeType], lon: val as string } })}
               />
+
               <div className="flex items-center gap-1">
                 <Icon component="icon-[ic--baseline-subdirectory-arrow-right]" size={16} color="text-secondary-300" />
                 <Input
diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/index.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/index.tsx
index fc09070cc0043dde9c39f5386b87c0a0bd191164..cd7d9fe0d03d3bc2a24fbf2fc505d796cd5d0f7e 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/layers/index.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/layers/index.tsx
@@ -3,12 +3,13 @@ import { HeatLayer } from './heatmap-layer/HeatLayer';
 import { NodeLinkLayer } from './nodelink-layer/NodeLinkLayer';
 import { NodeLayer } from './node-layer/NodeLayer';
 import { NodeIconLayer } from './icon-layer/IconLayer';
-import NodeOptions from './node-layer/NodeOptions';
-import NodeLinkOptions from './nodelink-layer/NodeLinkOptions';
-import IconOptions from './icon-layer/IconOptions';
-import HeatLayerOptions from './heatmap-layer/HeatLayerOptions';
-import ChoroplethOptions from './choropleth-layer/ChoroplethOptions';
+import { NodeLinkOptions } from './nodelink-layer/NodeLinkOptions';
+import { IconOptions } from './icon-layer/IconOptions';
+import { HeatLayerOptions } from './heatmap-layer/HeatLayerOptions';
+import { ChoroplethOptions } from './choropleth-layer/ChoroplethOptions';
 import { TileLayer, BitmapLayer } from 'deck.gl';
+import { VisualizationSettingsPropTypes } from '../../../common';
+import { MapProps } from '../mapvis';
 
 export const layerTypes: Record<string, any> = {
   // node: NodeLayer,
@@ -18,7 +19,11 @@ export const layerTypes: Record<string, any> = {
   // choropleth: ChoroplethLayer,
 };
 
-export const layerSettings: Record<string, any> = {
+export type MapLayerSettingsPropTypes = VisualizationSettingsPropTypes<MapProps> & {
+  spatialAttributes: { [id: string]: string[] };
+};
+
+export const layerSettings: Record<string, React.FC<MapLayerSettingsPropTypes>> = {
   nodelink: NodeLinkOptions,
   heatmap: HeatLayerOptions,
   // node: NodeOptions,
diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/node-layer/NodeLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/node-layer/NodeLayer.tsx
index a3e027bb9d61255e291d3fc392638d966c1a65c8..82f4a3a47885bf86a2755d5a466cecc483fe72fb 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/layers/node-layer/NodeLayer.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/layers/node-layer/NodeLayer.tsx
@@ -1,10 +1,10 @@
 import React from 'react';
 import { CompositeLayer } from 'deck.gl';
 import { ScatterplotLayer } from '@deck.gl/layers';
-import { LayerProps } from '../../mapvis.types';
+import { CompositeLayerType, LayerProps } from '../../mapvis.types';
 import { Node } from '@graphpolaris/shared/lib/data-access';
 
-export class NodeLayer extends CompositeLayer<LayerProps> {
+export class NodeLayer extends CompositeLayer<CompositeLayerType> {
   static type = 'Node';
 
   updateState({ changeFlags }: { changeFlags: any }) {
@@ -28,20 +28,20 @@ export class NodeLayer extends CompositeLayer<LayerProps> {
   }
 
   renderLayers() {
-    const { graph, config, getNodeLocation, setLayerIds, metaData } = this.props;
+    const { data, settings, getNodeLocation, selected, setLayerIds, graphMetadata } = this.props;
 
     const layers: any[] = [];
     const layerIds: any[] = [];
 
-    metaData.nodes.labels.forEach((label: string) => {
+    graphMetadata.nodes.labels.forEach((label: string) => {
       const layerId = `${label}-nodes-scatterplot`;
       layerIds.push(layerId);
 
       layers.push(
         new ScatterplotLayer({
           id: layerId,
-          visible: !config[label].hidden,
-          data: graph.nodes.filter((node: Node) => node.label === label),
+          visible: !settings.nodes[label].hidden,
+          data: data.nodes.filter((node: Node) => node.label === label),
           pickable: true,
           filled: true,
           radiusScale: 6,
@@ -49,8 +49,8 @@ export class NodeLayer extends CompositeLayer<LayerProps> {
           radiusMaxPixels: 100,
           lineWidthMinPixels: 1,
           getPosition: (d: any) => getNodeLocation(d._id),
-          getFillColor: (d: any) => config[label].color,
-          getRadius: (d: any) => this.getRadius(d, config),
+          getFillColor: (d: any) => settings.nodes[label].color,
+          getRadius: (d: any) => settings.nodes[label].radius,
         }),
       );
     });
diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/node-layer/NodeOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/node-layer/NodeOptions.tsx
index 0054a2f7dce4cd53eada0d3875d211564f2825b1..2210c29f65f41b1852a8d1c5ccf54e5c35c1cfd1 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/layers/node-layer/NodeOptions.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/layers/node-layer/NodeOptions.tsx
@@ -1,153 +1,144 @@
 import React, { useEffect, useMemo, useState } from 'react';
 import ColorPicker from '@graphpolaris/shared/lib/components/colorComponents/colorPicker';
-import { MapProps } from '../../mapvis';
+import { MapNodeOrEdgeData, MapProps } from '../../mapvis';
 import { VisualizationSettingsPropTypes } from '@graphpolaris/shared/lib/vis/common';
 import { EntityPill, Icon, Input } from '@graphpolaris/shared/lib/components';
+import { MapLayerSettingsPropTypes } from '..';
 
-export default function NodeOptions({ settings, graphMetadata, updateSettings }: VisualizationSettingsPropTypes<MapProps>) {
-  const [collapsed, setCollapsed] = useState<Record<string, boolean>>({});
-
-  useEffect(() => {
-    graphMetadata.nodes.labels.forEach((node) => {
-      updateSettings({
-        [node]: {
-          color: [0, 0, 0],
-          hidden: false,
-          fixed: true,
-          min: 0,
-          max: 10,
-          radius: 1,
-          sizeAttribute: '',
-          lon: '',
-          lat: '',
-          ...settings?.[node],
-        },
-      });
-    });
-  }, [graphMetadata]);
-
-  const spatialAttributes: { [id: string]: string[] } = {};
-  graphMetadata.nodes.labels.forEach((node) => {
-    spatialAttributes[node] = Object.entries(graphMetadata.nodes.types[node].attributes)
-      .filter(([, value]) => value.dimension === 'numerical')
-      .map(([key]) => key);
-  });
-
+export default function NodeOptions({ settings, graphMetadata, updateSettings, spatialAttributes }: MapLayerSettingsPropTypes) {
   const handleCollapseToggle = (nodeType: string) => {
-    setCollapsed((prevCollapsed) => ({
-      ...prevCollapsed,
-      [nodeType]: !prevCollapsed[nodeType],
-    }));
+    settings.nodes[nodeType].collapsed = !settings.nodes[nodeType].collapsed;
+    updateSettings({ nodes: settings.nodes });
   };
 
   return (
     <div>
-      {graphMetadata.nodes.labels.map((nodeType) => (
-        <div className="mt-2" key={nodeType}>
-          <div className="flex items-center">
-            <div className="w-3/4 mr-6 cursor-pointer" onClick={() => handleCollapseToggle(nodeType)}>
-              <EntityPill title={nodeType} />
-            </div>
-            <div className="w-1/2">
-              <ColorPicker
-                value={settings?.[nodeType]?.['color'] ? settings?.[nodeType]?.['color'] : [0, 0, 0]}
-                updateValue={(val: number[]) => updateSettings({ [nodeType]: { ...settings?.[nodeType], color: val } })}
-              />
-            </div>
-          </div>
-
-          {!collapsed[nodeType] && (
-            <div>
-              <Input
-                inline
-                label="Longitude"
-                type="dropdown"
-                value={settings?.[nodeType]?.lon}
-                options={[...spatialAttributes[nodeType]]}
-                disabled={!settings.node || spatialAttributes[nodeType].length < 1}
-                onChange={(val) => updateSettings({ [nodeType]: { ...settings?.[nodeType], lon: val as string } })}
-              />
-
-              <Input
-                inline
-                label="Latitude"
-                type="dropdown"
-                value={settings?.[nodeType]?.lat}
-                options={[...spatialAttributes[nodeType]]}
-                disabled={!settings.node || spatialAttributes[nodeType].length < 1}
-                onChange={(val) => updateSettings({ [nodeType]: { ...settings?.[nodeType], lat: val as string } })}
-              />
-
-              <div className="ml-2">
-                <div className="flex items-center gap-1">
-                  <Icon component="icon-[ic--baseline-subdirectory-arrow-right]" size={16} color="text-secondary-300" />
-                  <Input
-                    label="Hidden"
-                    type="boolean"
-                    value={settings?.[nodeType]?.hidden ?? false}
-                    onChange={(val: boolean) => updateSettings({ [nodeType]: { ...settings?.[nodeType], hidden: val } })}
+      {settings?.nodes &&
+        Object.keys(settings.nodes).map((nodeType) => {
+          const nodeSettings = settings.nodes[nodeType];
+          return (
+            <div className="mt-2" key={nodeType}>
+              <div className="flex items-center">
+                <div className="w-3/4 mr-6 cursor-pointer" onClick={() => handleCollapseToggle(nodeType)}>
+                  <EntityPill title={nodeType} />
+                </div>
+                <div className="w-1/2">
+                  <ColorPicker
+                    value={nodeSettings.color}
+                    updateValue={(val) => {
+                      updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, color: val } } });
+                    }}
                   />
                 </div>
+              </div>
 
+              {!nodeSettings.collapsed && (
                 <div>
-                  <div className="flex items-center gap-1">
-                    <Icon component="icon-[ic--baseline-subdirectory-arrow-right]" size={16} color="text-secondary-300" />
-                    <span>Radius</span>
-                  </div>
                   <Input
-                    label="Fixed"
-                    type="boolean"
-                    value={settings?.[nodeType]?.fixed ?? false}
-                    onChange={(val: boolean) => updateSettings({ [nodeType]: { ...settings?.[nodeType], fixed: val } })}
+                    inline
+                    label="Latitude"
+                    type="dropdown"
+                    value={nodeSettings.lat}
+                    options={[...(spatialAttributes[nodeType] || [])]}
+                    disabled={!settings.node || spatialAttributes[nodeType].length < 1}
+                    onChange={(val) => {
+                      updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, lat: String(val) } } });
+                    }}
                   />
-                  {!settings?.[nodeType]?.fixed ? (
-                    <div>
+                  <Input
+                    inline
+                    label="Longitude"
+                    type="dropdown"
+                    value={nodeSettings.lon}
+                    options={[...(spatialAttributes[nodeType] || [])]}
+                    disabled={!settings.node || spatialAttributes[nodeType].length < 1}
+                    onChange={(val) => {
+                      updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, lon: String(val) } } });
+                    }}
+                  />
+
+                  <div className="ml-2">
+                    <div className="flex items-center gap-1">
+                      <Icon component="icon-[ic--baseline-subdirectory-arrow-right]" size={16} color="text-secondary-300" />
+
                       <Input
-                        label="Based on"
-                        type="dropdown"
-                        size="xs"
-                        options={Object.keys(graphMetadata.nodes.types[nodeType].attributes).filter(
-                          (key) => graphMetadata.nodes.types[nodeType].attributes[key].dimension === 'numerical',
-                        )}
-                        value={settings?.[nodeType]?.sizeAttribute ?? ''}
-                        onChange={(val: string | number) => updateSettings({ [nodeType]: { ...settings?.[nodeType], sizeAttribute: val } })}
+                        label="Hidden"
+                        type="boolean"
+                        value={nodeSettings.hidden}
+                        onChange={(val) => {
+                          updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, hidden: val } } });
+                        }}
                       />
-                      <div className="flex">
-                        <Input
-                          type="number"
-                          label="min"
-                          size="xs"
-                          value={settings?.[nodeType]?.min ?? 0}
-                          onChange={(val: number) => updateSettings({ [nodeType]: { ...settings?.[nodeType], min: val } })}
-                        />
-                        <Input
-                          type="number"
-                          label="max"
-                          size="xs"
-                          value={settings?.[nodeType]?.max ?? 10}
-                          onChange={(val: number) => updateSettings({ [nodeType]: { ...settings?.[nodeType], max: val } })}
-                        />
-                      </div>
                     </div>
-                  ) : (
+
                     <div>
+                      <div className="flex items-center gap-1">
+                        <Icon component="icon-[ic--baseline-subdirectory-arrow-right]" size={16} color="text-secondary-300" />
+                        <span>Radius</span>
+                      </div>
                       <Input
-                        type="slider"
-                        label="Width"
-                        min={0}
-                        max={10}
-                        step={0.5}
-                        value={settings?.[nodeType]?.radius ?? 1}
-                        onChange={(val: number) => updateSettings({ [nodeType]: { ...settings?.[nodeType], radius: val } })}
+                        label="Fixed"
+                        type="boolean"
+                        value={nodeSettings.fixed}
+                        onChange={(val) => {
+                          updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, fixed: val } } });
+                        }}
                       />
+                      {!settings?.[nodeType]?.fixed ? (
+                        <div>
+                          <Input
+                            label="Based on"
+                            type="dropdown"
+                            size="xs"
+                            options={spatialAttributes[nodeType]}
+                            value={nodeSettings.sizeAttribute}
+                            onChange={(val) => {
+                              updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, sizeAttribute: String(val) } } });
+                            }}
+                          />
+                          <div className="flex">
+                            <Input
+                              type="number"
+                              label="min"
+                              size="xs"
+                              value={nodeSettings.min}
+                              onChange={(val) => {
+                                updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, min: Number(val) } } });
+                              }}
+                            />
+                            <Input
+                              type="number"
+                              label="max"
+                              size="xs"
+                              value={nodeSettings.max}
+                              onChange={(val) => {
+                                updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, max: Number(val) } } });
+                              }}
+                            />
+                          </div>
+                        </div>
+                      ) : (
+                        <div>
+                          <Input
+                            type="slider"
+                            label="Width"
+                            min={0}
+                            max={10}
+                            step={0.5}
+                            value={nodeSettings.radius}
+                            onChange={(val) => {
+                              updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, radius: Number(val) } } });
+                            }}
+                          />
+                        </div>
+                      )}
                     </div>
-                  )}
+                  </div>
                 </div>
-              </div>
+              )}
             </div>
-          )}
-        </div>
-      ))}
+          );
+        })}
     </div>
   );
 }
diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx
index bb7e24aca952d7ad1aa7b43c6fc98a915fb4677e..56004039f0a519784302e744f9294cf398faf5d3 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkLayer.tsx
@@ -1,103 +1,89 @@
 import React from 'react';
-import { CompositeLayer } from 'deck.gl';
+import { CompositeLayer, Layer } from 'deck.gl';
 import { IconLayer, LineLayer, ScatterplotLayer, TextLayer } from '@deck.gl/layers';
-import { LayerProps } from '../../mapvis.types';
+import { CompositeLayerType, LayerProps } from '../../mapvis.types';
 import { BrushingExtension, CollisionFilterExtension } from '@deck.gl/extensions';
 import { Edge, Node } from '@graphpolaris/shared/lib/data-access';
 import { createIcon } from './shapeFactory';
 
-export class NodeLinkLayer extends CompositeLayer<LayerProps> {
+export class NodeLinkLayer extends CompositeLayer<CompositeLayerType> {
   static type = 'NodeLink';
+  private _layers: Record<string, Layer> = {};
+
+  constructor(props: LayerProps) {
+    super(props);
+  }
 
   updateState({ changeFlags }: { changeFlags: any }) {
     return changeFlags.propsOrDataChanged || changeFlags.somethingChanged;
   }
 
   renderLayers() {
-    const { graph, config, getNodeLocation, selected, setLayerIds, metaData } = this.props;
-
-    const layers = [];
-    const layerIds = [];
+    const { data, settings, getNodeLocation, selected, setLayerIds, graphMetadata } = this.props;
 
     const brushingExtension = new BrushingExtension();
     const collisionFilter = new CollisionFilterExtension();
 
-    metaData.nodes.labels.forEach((label: string) => {
-      const layerId = `${label}-nodes-scatterplot`;
-      layerIds.push(layerId);
-
-      layers.push(
-        new IconLayer({
-          id: layerId,
-          visible: !config[label].hidden,
-          data: graph.nodes.filter((node: Node) => node.label === label),
-          pickable: true,
-          getColor: (d) => [200, 140, 0],
-          getSize: (d: any) => config[label].size,
-          getPosition: (d: any) => getNodeLocation(d._id),
-          getIcon: (d: any) => {
-            return {
-              url: createIcon(config[label].shape ?? 'circle', config[label].color),
-              width: 24,
-              height: 24,
-            };
-          },
-          mask: true,
-        }),
-      );
-    });
-
-    metaData.edges.labels.forEach((label: string) => {
+    graphMetadata.edges.labels.forEach((label: string) => {
       const layerId = `${label}-edges-line`;
-      layerIds.push(layerId);
-
       const edgeData =
-        selected.length > 0 ? graph.edges.filter((edge: Edge) => selected.includes(edge.from) || selected.includes(edge.to)) : graph.edges;
+        selected.length > 0 ? data.edges.filter((edge: Edge) => selected.includes(edge.from) || selected.includes(edge.to)) : data.edges;
 
-      layers.push(
-        new LineLayer({
-          id: layerId,
-          data: edgeData,
-          visible: !config[label]?.hidden,
-          pickable: true,
-          getWidth: (d: any) => config[label].width,
-          getSourcePosition: (d: any) => getNodeLocation(d.from),
-          getTargetPosition: (d: any) => getNodeLocation(d.to),
-          getColor: (d: any) => config[d.label].color,
-          radiusScale: 3000,
-          brushingEnabled: config.enableBrushing,
-          extensions: [brushingExtension],
-        }),
-      );
+      this._layers[layerId] = new LineLayer<Edge>({
+        id: layerId,
+        data: edgeData,
+        visible: !settings.edges[label].hidden,
+        pickable: true,
+        getWidth: settings.edges[label].width,
+        getSourcePosition: (d) => getNodeLocation(d.from),
+        getTargetPosition: (d) => getNodeLocation(d.to),
+        getColor: (d) => settings.edges[d.label].color,
+        extensions: [brushingExtension],
+      });
     });
+    graphMetadata.nodes.labels.forEach((label: string) => {
+      const layerId = `${label}-nodes-scatterplot`;
 
-    const textLayerId = 'label-target';
-    layerIds.push(textLayerId);
+      this._layers[layerId] = new IconLayer<Node>({
+        id: layerId,
+        visible: !settings.nodes[label].hidden,
+        data: data.nodes.filter((node: Node) => node.label === label),
+        pickable: true,
+        getColor: (d) => [200, 140, 0],
+        getSize: (d) => settings.nodes[label].size,
+        getPosition: (d) => getNodeLocation(d._id),
+        getIcon: (d: any) => {
+          return {
+            url: createIcon(settings.nodes[label].shape, settings.nodes[label].color),
+            width: 24,
+            height: 24,
+          };
+        },
+      });
+    });
 
-    layers.push(
-      new TextLayer({
-        id: textLayerId,
-        data: graph.nodes,
-        getPosition: (d: any) => getNodeLocation(d._id),
-        getText: (d: any) => d.id,
-        getSize: 15,
-        visible: true,
-        getAlignmentBaseline: 'top',
-        background: true,
-        getPixelOffset: [10, 10],
-        extensions: [collisionFilter],
-        collisionEnabled: true,
-        getCollisionPriority: (d: any) => d.id,
-        collisionTestProps: { sizeScale: 10 },
-        getRadius: 10,
-        radiusUnits: 'pixels',
-        collisionGroup: 'text',
-      }),
-    );
+    const textLayerId = 'label-target';
 
-    setLayerIds(layerIds);
+    this._layers[textLayerId] = new TextLayer({
+      id: textLayerId,
+      data: data.nodes,
+      getPosition: (d: any) => getNodeLocation(d._id),
+      getText: (d: any) => d.id,
+      getSize: 15,
+      visible: true,
+      getAlignmentBaseline: 'top',
+      background: true,
+      getPixelOffset: [10, 10],
+      extensions: [collisionFilter],
+      collisionEnabled: true,
+      getCollisionPriority: (d: any) => d.id,
+      collisionTestProps: { sizeScale: 10 },
+      getRadius: 10,
+      radiusUnits: 'pixels',
+      collisionGroup: 'text',
+    });
 
-    return layers;
+    return Object.values(this._layers);
   }
 }
 
diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx
index 1de263ca59e0991ca8812d74ebb6c100362b801c..6c530cf3741393dae5f5981f9606d73cbf492ab5 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx
@@ -1,222 +1,206 @@
 import React, { useState, useEffect } from 'react';
 import ColorPicker from '@graphpolaris/shared/lib/components/colorComponents/colorPicker';
-import { VisualizationSettingsPropTypes } from '@graphpolaris/shared/lib/vis/common';
-import { MapProps } from '../../mapvis';
 import { Button, EntityPill, Icon, Input, RelationPill } from '@graphpolaris/shared/lib/components';
+import { MapLayerSettingsPropTypes } from '..';
 
-export default function NodeLinkOptions({ settings, graphMetadata, updateSettings }: VisualizationSettingsPropTypes<MapProps>) {
-  const [collapsed, setCollapsed] = useState<Record<string, boolean>>({});
+export function NodeLinkOptions({ settings, graphMetadata, updateSettings, spatialAttributes }: MapLayerSettingsPropTypes) {
+  const handleCollapseToggle = (nodeType: string) => {
+    settings.nodes[nodeType].collapsed = !settings.nodes[nodeType].collapsed;
+    updateSettings({ nodes: settings.nodes });
+  };
 
   useEffect(() => {
-    graphMetadata.nodes.labels.forEach((node) => {
-      updateSettings({
-        [node]: {
-          color: [0, 0, 0],
-          hidden: false,
-          shape: '',
-          size: 10,
-          fixed: true,
-          min: 0,
-          max: 10,
-          sizeAttribute: '',
-          lon: '',
-          lat: '',
-          ...settings?.[node],
-        },
-      });
-    });
-
-    graphMetadata.edges.labels.forEach((edge) => {
-      updateSettings({
-        [edge]: {
-          color: [0, 0, 0],
-          hidden: false,
-          fixed: true,
-          min: 0,
-          max: 10,
-          width: 1,
-          widthAttribute: '',
-          ...settings?.[edge],
-        },
-      });
+    graphMetadata.nodes.labels.map((nodeType) => {
+      if (settings?.[nodeType]?.lat) {
+      }
     });
-  }, [graphMetadata]);
-
-  const spatialAttributes: { [id: string]: string[] } = {};
-  graphMetadata.nodes.labels.forEach((node) => {
-    spatialAttributes[node] = Object.entries(graphMetadata.nodes.types[node].attributes)
-      .filter(([, value]) => value.dimension === 'numerical')
-      .map(([key]) => key);
-  });
-
-  const handleCollapseToggle = (nodeType: string) => {
-    setCollapsed((prevCollapsed) => ({
-      ...prevCollapsed,
-      [nodeType]: !prevCollapsed[nodeType],
-    }));
-  };
+  }, [settings.node, graphMetadata]);
 
   return (
     <div>
-      {graphMetadata.nodes.labels.map((nodeType) => (
-        <div className="mt-2" key={nodeType}>
-          <div className="flex items-center">
-            <div className="flex flex-grow mr-2 cursor-pointer" onClick={() => handleCollapseToggle(nodeType)}>
-              <EntityPill title={nodeType} />
-            </div>
-            <div className="flex items-center space-x-2">
-              <ColorPicker
-                value={settings?.[nodeType]?.['color'] ? settings?.[nodeType]?.['color'] : [0, 0, 0]}
-                updateValue={(val: number[]) => updateSettings({ [nodeType]: { ...settings?.[nodeType], color: val } })}
-              />
-              <Button
-                iconComponent={settings?.[nodeType]?.hidden ? 'icon-[ic--baseline-visibility-off]' : 'icon-[ic--baseline-visibility]'}
-                variant="ghost"
-                onClick={() => {
-                  updateSettings({ [nodeType]: { ...settings?.[nodeType], hidden: !settings?.[nodeType]?.hidden as boolean } });
-                }}
-              />
-            </div>
-          </div>
-
-          {!collapsed[nodeType] && (
-            <div>
-              <Input
-                inline
-                label="Longitude"
-                type="dropdown"
-                value={settings?.[nodeType]?.lon}
-                options={[...spatialAttributes[nodeType]]}
-                disabled={!settings.node || spatialAttributes[nodeType].length < 1}
-                onChange={(val) => updateSettings({ [nodeType]: { ...settings?.[nodeType], lon: val as string } })}
-              />
-
-              <Input
-                inline
-                label="Latitude"
-                type="dropdown"
-                value={settings?.[nodeType]?.lat}
-                options={[...spatialAttributes[nodeType]]}
-                disabled={!settings.node || spatialAttributes[nodeType].length < 1}
-                onChange={(val) => updateSettings({ [nodeType]: { ...settings?.[nodeType], lat: val as string } })}
-              />
-              <Input
-                inline
-                label="Shape"
-                type="dropdown"
-                value={settings?.[nodeType]?.shape}
-                options={['circle', 'square', 'triangle', 'diamond', 'location', 'star']}
-                disabled={!settings.shape}
-                onChange={(val) => updateSettings({ [nodeType]: { ...settings?.[nodeType], shape: val as string } })}
-              />
-              <Input
-                label="Size"
-                type="slider"
-                min={0}
-                max={20}
-                step={1}
-                value={settings?.[nodeType]?.size}
-                onChange={(val) => updateSettings({ [nodeType]: { ...settings?.[nodeType], size: val as number } })}
-              />
-            </div>
-          )}
-        </div>
-      ))}
+      {settings?.nodes &&
+        Object.keys(settings.nodes).map((nodeType) => {
+          const nodeSettings = settings.nodes[nodeType];
+          return (
+            <div className="mt-2" key={nodeType}>
+              <div className="flex items-center">
+                <div className="flex flex-grow mr-2 cursor-pointer" onClick={() => handleCollapseToggle(nodeType)}>
+                  <EntityPill title={nodeType} />
+                </div>
+                <div className="flex items-center space-x-2">
+                  <ColorPicker
+                    value={nodeSettings.color}
+                    updateValue={(val) => {
+                      updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, color: val } } });
+                    }}
+                  />
+                  <Button
+                    iconComponent={nodeSettings.hidden ? 'icon-[ic--baseline-visibility-off]' : 'icon-[ic--baseline-visibility]'}
+                    variant="ghost"
+                    onClick={() => {
+                      updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, hidden: !nodeSettings.hidden } } });
+                    }}
+                  />
+                </div>
+              </div>
 
-      {graphMetadata.edges.labels.map((edgeType) => (
-        <div className="mt-2" key={edgeType}>
-          <div className="flex items-center">
-            <div className="w-3/4 mr-6 cursor-pointer" onClick={() => handleCollapseToggle(edgeType)}>
-              <RelationPill title={edgeType} />
-            </div>
-            <div className="w-1/2 flex">
-              <ColorPicker
-                value={settings?.[edgeType]?.['color'] ? settings?.[edgeType]?.['color'] : [0, 0, 0]}
-                updateValue={(val: number[]) => updateSettings({ [edgeType]: { ...settings?.[edgeType], color: val } })}
-              />
-              <Button
-                iconComponent={settings?.[edgeType]?.hidden ? 'icon-[ic--baseline-visibility-off]' : 'icon-[ic--baseline-visibility]'}
-                variant="ghost"
-                onClick={() => {
-                  updateSettings({ [edgeType]: { ...settings?.[edgeType], hidden: !settings?.[edgeType]?.hidden as boolean } });
-                }}
-              />
+              {!nodeSettings.collapsed && (
+                <div>
+                  <Input
+                    inline
+                    label="Latitude"
+                    type="dropdown"
+                    value={nodeSettings.lat}
+                    options={[...(spatialAttributes[nodeType] || [])]}
+                    disabled={!settings.node || spatialAttributes[nodeType].length < 1}
+                    onChange={(val) => {
+                      updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, lat: String(val) } } });
+                    }}
+                  />
+                  <Input
+                    inline
+                    label="Longitude"
+                    type="dropdown"
+                    value={nodeSettings.lon}
+                    options={[...(spatialAttributes[nodeType] || [])]}
+                    disabled={!settings.node || spatialAttributes[nodeType].length < 1}
+                    onChange={(val) => {
+                      updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, lon: String(val) } } });
+                    }}
+                  />
+                  <Input
+                    inline
+                    label="Shape"
+                    type="dropdown"
+                    value={nodeSettings.shape}
+                    options={['circle', 'square', 'triangle', 'diamond', 'location', 'star']}
+                    disabled={!settings.shape}
+                    onChange={(val) => {
+                      updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, shape: String(val) as any } } });
+                    }}
+                  />
+                  <Input
+                    label="Size"
+                    type="slider"
+                    min={0}
+                    max={40}
+                    step={1}
+                    value={nodeSettings.size}
+                    onChange={(val) => {
+                      updateSettings({ nodes: { ...settings.nodes, [nodeType]: { ...nodeSettings, size: Number(val) } } });
+                    }}
+                  />
+                </div>
+              )}
             </div>
-          </div>
+          );
+        })}
 
-          {!collapsed[edgeType] && (
-            <div>
-              <Input
-                label="Enable brushing"
-                type="boolean"
-                value={settings.enableBrushing}
-                onChange={(val) => {
-                  updateSettings({ enableBrushing: val as boolean });
-                }}
-              />
+      {settings?.edges &&
+        Object.keys(settings.edges).map((edgeType) => {
+          const edgeSettings = settings.edges[edgeType];
 
-              <div>
-                <div className="flex items-center gap-1">
-                  <Icon component="icon-[ic--baseline-subdirectory-arrow-right]" size={16} color="text-secondary-300" />
-                  <span>Width</span>
+          return (
+            <div className="mt-2" key={edgeType}>
+              <div className="flex items-center">
+                <div className="w-3/4 mr-6 cursor-pointer" onClick={() => handleCollapseToggle(edgeType)}>
+                  <RelationPill title={edgeType} />
                 </div>
-                <Input
-                  label="Fixed"
-                  type="boolean"
-                  value={settings?.[edgeType]?.fixed ?? false}
-                  onChange={(val: boolean) => updateSettings({ [edgeType]: { ...settings?.[edgeType], fixed: val } })}
-                />
-                {!settings?.[edgeType]?.fixed ? (
+                <div className="w-1/2 flex">
+                  <ColorPicker
+                    value={settings.edges[edgeType].color}
+                    updateValue={(val) => updateSettings({ edges: { ...settings.edges, [edgeType]: { ...edgeSettings, color: val } } })}
+                  />
+                  <Button
+                    iconComponent={
+                      settings.edges[edgeType].hidden ? 'icon-[ic--baseline-visibility-off]' : 'icon-[ic--baseline-visibility]'
+                    }
+                    variant="ghost"
+                    onClick={() =>
+                      updateSettings({ edges: { ...settings.edges, [edgeType]: { ...edgeSettings, hidden: !edgeSettings.hidden } } })
+                    }
+                  />
+                </div>
+              </div>
+
+              {!edgeSettings.collapsed && (
+                <div>
+                  <Input
+                    label="Enable brushing"
+                    type="boolean"
+                    value={settings.enableBrushing}
+                    onChange={(val) => {
+                      updateSettings({ enableBrushing: val as boolean });
+                    }}
+                  />
+
                   <div>
-                    <Input
-                      label="Based on"
-                      type="dropdown"
-                      size="xs"
-                      options={
-                        graphMetadata.nodes.types[edgeType]?.attributes
-                          ? Object.keys(graphMetadata.nodes.types[edgeType].attributes).filter(
-                              (key) => graphMetadata.nodes.types[edgeType].attributes[key].dimension === 'numerical',
-                            )
-                          : []
-                      }
-                      value={settings?.[edgeType]?.sizeAttribute ?? ''}
-                      onChange={(val: string | number) => updateSettings({ [edgeType]: { ...settings?.[edgeType], sizeAttribute: val } })}
-                    />
-                    <div className="flex">
-                      <Input
-                        type="number"
-                        label="min"
-                        size="xs"
-                        value={settings?.[edgeType]?.min ?? 0}
-                        onChange={(val: number) => updateSettings({ [edgeType]: { ...settings?.[edgeType], min: val } })}
-                      />
-                      <Input
-                        type="number"
-                        label="max"
-                        size="xs"
-                        value={settings?.[edgeType]?.max ?? 10}
-                        onChange={(val: number) => updateSettings({ [edgeType]: { ...settings?.[edgeType], max: val } })}
-                      />
+                    <div className="flex items-center gap-1">
+                      <Icon component="icon-[ic--baseline-subdirectory-arrow-right]" size={16} color="text-secondary-300" />
+                      <span>Width</span>
                     </div>
-                  </div>
-                ) : (
-                  <div>
                     <Input
-                      type="slider"
-                      label="Width"
-                      min={0}
-                      max={10}
-                      step={0.5}
-                      value={settings?.[edgeType]?.width ?? 1}
-                      onChange={(val: number) => updateSettings({ [edgeType]: { ...settings?.[edgeType], width: val } })}
+                      label="Fixed"
+                      type="boolean"
+                      value={edgeSettings.fixed}
+                      onChange={(val) => updateSettings({ edges: { ...settings.edges, [edgeType]: { ...edgeSettings, fixed: val } } })}
                     />
+                    {!edgeSettings.fixed ? (
+                      <div>
+                        <Input
+                          label="Based on"
+                          type="dropdown"
+                          size="xs"
+                          options={
+                            graphMetadata.nodes.types[edgeType]?.attributes
+                              ? Object.keys(graphMetadata.nodes.types[edgeType].attributes).filter(
+                                  (key) => graphMetadata.nodes.types[edgeType].attributes[key].dimension === 'numerical',
+                                )
+                              : []
+                          }
+                          value={edgeSettings.sizeAttribute ?? ''}
+                          onChange={(val) =>
+                            updateSettings({ edges: { ...settings.edges, [edgeType]: { ...edgeSettings, sizeAttribute: String(val) } } })
+                          }
+                        />
+                        <div className="flex">
+                          <Input
+                            type="number"
+                            label="min"
+                            size="xs"
+                            value={edgeSettings.min}
+                            onChange={(val) => updateSettings({ edges: { ...settings.edges, [edgeType]: { ...edgeSettings, min: val } } })}
+                          />
+                          <Input
+                            type="number"
+                            label="max"
+                            size="xs"
+                            value={edgeSettings.max}
+                            onChange={(val) => updateSettings({ edges: { ...settings.edges, [edgeType]: { ...edgeSettings, max: val } } })}
+                          />
+                        </div>
+                      </div>
+                    ) : (
+                      <div>
+                        <Input
+                          type="slider"
+                          label="Width"
+                          min={0}
+                          max={10}
+                          step={0.2}
+                          value={settings.edges[edgeType].width}
+                          onChange={(val) =>
+                            updateSettings({ edges: { ...settings.edges, [edgeType]: { ...edgeSettings, width: Number(val) } } })
+                          }
+                        />
+                      </div>
+                    )}
                   </div>
-                )}
-              </div>
+                </div>
+              )}
             </div>
-          )}
-        </div>
-      ))}
+          );
+        })}
     </div>
   );
 }
diff --git a/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx b/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx
index e6d90b7e23ab64148e85344118dcbbe854ecea6d..43a4646a66f4ab38567f47cfa4cb577bc97d24c0 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx
@@ -1,18 +1,53 @@
-import React, { useEffect, useMemo, useCallback, useState } from 'react';
+import React, { useEffect, useMemo, useCallback, useState, useRef } from 'react';
 import DeckGL from '@deck.gl/react';
-import { FlyToInterpolator, WebMercatorViewport } from '@deck.gl/core';
+import { CompositeLayer, FlyToInterpolator, Position, WebMercatorViewport } from '@deck.gl/core';
 import { SelectionLayer } from '@deck.gl-community/editable-layers';
-import { Coordinate, Layer } from './mapvis.types';
+import { CompositeLayerType, Coordinate, Layer } from './mapvis.types';
 import { VISComponentType, VisualizationPropTypes } from '../../common';
-import { layerTypes, createBaseMap } from './layers';
-import { MapSettings } from './settings';
+import { layerTypes, createBaseMap, MapLayerSettingsPropTypes } from './layers';
+import { MapSettings } from './MapSettings';
 import { Node } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice';
-import SearchBar from './search';
+import { SearchBar } from './SearchBar';
 import { Icon } from '@graphpolaris/shared/lib/components';
 
-export type MapProps = { layer: string; enableBrushing: boolean };
+export type MapNodeOrEdgeData = MapNodeData | MapEdgeData;
+
+export type MapNodeData = {
+  color: [number, number, number];
+  hidden: boolean;
+  fixed: boolean;
+  min: number;
+  max: number;
+  radius: number;
+  collapsed: boolean;
+  lat?: string;
+  lon?: string;
+  shape: 'circle' | 'square' | 'triangle' | 'diamond' | 'location' | 'star';
+  size: number;
+  sizeAttribute?: string;
+};
+
+export type MapEdgeData = {
+  color: [number, number, number];
+  hidden: boolean;
+  fixed: boolean;
+  min: number;
+  max: number;
+  radius: number;
+  collapsed: boolean;
+  size: number;
+  width: number;
+  sizeAttribute?: string;
+};
+
+export type MapProps = {
+  layer: string;
+  enableBrushing: boolean;
+  nodes: Record<string, MapNodeData>;
+  edges: Record<string, MapEdgeData>;
+};
 
-const settings: MapProps = { layer: 'nodelink', enableBrushing: false };
+const settings: MapProps = { layer: 'nodelink', enableBrushing: false, nodes: {}, edges: {} };
 
 const INITIAL_VIEW_STATE = {
   latitude: 52.1006,
@@ -24,10 +59,8 @@ const INITIAL_VIEW_STATE = {
 
 const FLY_SPEED = 1000;
 
-const baseLayer = createBaseMap();
-
-export const MapVis = ({ data, settings, handleSelect, graphMetadata }: VisualizationPropTypes<MapProps>) => {
-  const [layer, setLayer] = useState<Layer | undefined>(undefined);
+export const MapVis = (props: VisualizationPropTypes<MapProps>) => {
+  const baseLayer = useRef(createBaseMap());
   const [viewport, setViewport] = useState<Record<string, any>>(INITIAL_VIEW_STATE);
   const [hoverObject, setHoverObject] = useState<Node | null>(null);
   const [selected, setSelected] = useState<any[]>([]);
@@ -64,20 +97,28 @@ export const MapVis = ({ data, settings, handleSelect, graphMetadata }: Visualiz
     [getFittedViewport],
   );
 
-  useEffect(() => {
-    setLayer({
-      type: settings.layer ? layerTypes?.[settings.layer] : layerTypes.nodelink,
-      config: settings,
-    });
-  }, [settings.layer]);
-
-  const dataLayer = useMemo(() => {
-    if (!layer || !settings.layer) return null;
+  const [dataLayer, setDataLayer] = useState<{ component: CompositeLayer<CompositeLayerType>; id: string } | null>(null);
 
-    const coordinateLookup: { [id: string]: Coordinate } = data.nodes.reduce(
+  useEffect(() => {
+    if (!props.settings.layer) {
+      setDataLayer(null);
+      return;
+    }
+
+    const layer = {
+      component: props.settings.layer ? layerTypes?.[props.settings.layer] : layerTypes.nodelink,
+      settings: props.settings,
+      id: props.settings.layer,
+    };
+
+    const coordinateLookup: { [id: string]: Position } = props.data.nodes.reduce(
       (acc, node) => {
-        const latitude = settings?.[node.label]?.lat ? (node?.attributes?.[settings[node.label].lat] as string) : undefined;
-        const longitude = settings?.[node.label]?.lon ? (node?.attributes?.[settings[node.label].lon] as string) : undefined;
+        const latitude = props.settings.nodes?.[node.label]?.lat
+          ? (node?.attributes?.[props.settings.nodes[node.label].lat as any] as string)
+          : undefined;
+        const longitude = props.settings.nodes?.[node.label]?.lon
+          ? (node?.attributes?.[props.settings.nodes[node.label].lon as any] as string)
+          : undefined;
 
         if (latitude !== undefined && longitude !== undefined) {
           acc[node._id] = [parseFloat(longitude), parseFloat(latitude)];
@@ -85,20 +126,26 @@ export const MapVis = ({ data, settings, handleSelect, graphMetadata }: Visualiz
 
         return acc;
       },
-      {} as { [id: string]: Coordinate },
+      {} as { [id: string]: Position },
     );
 
-    return new layer.type({
-      graph: data,
-      metaData: graphMetadata,
-      config: settings,
+    const layerProps: CompositeLayerType = {
+      ...props,
       selected: selected,
       hoverObject: hoverObject,
       getNodeLocation: (d: string) => coordinateLookup[d],
       flyToBoundingBox: flyToBoundingBox,
       setLayerIds: (val: string[]) => setLayerIds(val),
-    });
-  }, [layer, data, selected, hoverObject, settings]);
+    };
+
+    if (dataLayer && dataLayer.id === layer.id) {
+      // dataLayer.updateState;
+      setDataLayer({ component: dataLayer.component.clone(layerProps), id: props.settings.layer });
+    } else {
+      // @ts-ignore
+      setDataLayer({ component: new layer.component(layerProps), id: props.settings.layer });
+    }
+  }, [props.settings.layer, props.data, selected, hoverObject, props.settings]);
 
   const selectionLayer = useMemo(
     () =>
@@ -122,16 +169,16 @@ export const MapVis = ({ data, settings, handleSelect, graphMetadata }: Visualiz
               }
             }
             setSelected(nodes.map((node) => node._id));
-            handleSelect({ nodes, edges });
+            props.handleSelect({ nodes, edges });
           } else {
-            handleSelect();
+            props.handleSelect();
           }
           setSelectingRectangle(false);
         },
         layerIds: layerIds,
         getTentativeFillColor: () => [22, 37, 67, 100],
       }),
-    [selectingRectangle, layer],
+    [selectingRectangle],
   );
 
   return (
@@ -146,35 +193,35 @@ export const MapVis = ({ data, settings, handleSelect, graphMetadata }: Visualiz
       </div>
       {isSearching && (
         <SearchBar
-          onSearch={(boundingbox: [number, number, number, number]) => {
-            flyToBoundingBox(...boundingbox);
+          onSearch={(boundingBox: [number, number, number, number]) => {
+            flyToBoundingBox(...boundingBox);
             setIsSearching(false);
           }}
         />
       )}
       <DeckGL
-        layers={[baseLayer, dataLayer, selectionLayer]}
+        layers={[baseLayer.current, dataLayer?.component, selectionLayer]}
         controller={true}
         initialViewState={viewport}
         onViewStateChange={({ viewState }) => setViewport(viewState)}
         onClick={({ object }) => {
-          if (data) {
+          if (props.data) {
             if (!object) {
-              handleSelect();
+              props.handleSelect();
               setSelected([]);
               return;
             }
             if (object.hasOwnProperty('attributes') && object.hasOwnProperty('id') && object.hasOwnProperty('label')) {
-              handleSelect({ nodes: [object] });
+              props.handleSelect({ nodes: [object] });
               setSelected([object.id]);
             }
             if (object.type === 'Feature') {
               const ids = object.properties.nodes;
               if (ids.length > 0) {
-                const nodes = data.nodes.filter((node) => ids.includes((node as unknown as { id: string }).id));
-                handleSelect({ nodes: [...nodes] });
+                const nodes = props.data.nodes.filter((node) => ids.includes((node as unknown as { id: string }).id));
+                props.handleSelect({ nodes: [...nodes] });
               } else {
-                handleSelect();
+                props.handleSelect();
                 setSelected([]);
                 return;
               }
diff --git a/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts b/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts
index 8293fbbfcf2a9bb2af60d477fc35f1b6111abdc8..3dcad5c67ab35ba0fa1cd47f61685b8cbcdbb326 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts
+++ b/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts
@@ -1,12 +1,27 @@
+import { CompositeLayer, Position } from 'deck.gl';
+import { MapLayerSettingsPropTypes } from './layers';
+import { VisualizationPropTypes, VisualizationSettingsType } from '../../common';
+import { MapProps } from './mapvis';
+import { Node as QueryNode } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice';
+
 export type Coordinate = [number, number] | [];
 
 export interface LayerProps {
   [key: string]: any;
 }
 
+export type CompositeLayerType = VisualizationPropTypes<MapProps> & {
+  selected: any[];
+  hoverObject: QueryNode | null;
+  getNodeLocation: (d: string) => Position;
+  flyToBoundingBox: (minLat: number, maxLat: number, minLon: number, maxLon: number, options?: { [key: string]: any }) => void;
+  setLayerIds: (val: string[]) => void;
+};
+
 export type Layer = {
-  type: any;
-  config: any;
+  id: string;
+  component: CompositeLayer<CompositeLayerType>;
+  settings: MapProps & VisualizationSettingsType;
 };
 
 export type Node = {
diff --git a/libs/shared/lib/vis/visualizations/mapvis/settings.tsx b/libs/shared/lib/vis/visualizations/mapvis/settings.tsx
deleted file mode 100644
index 0d893868bd544ffa09dbb094f30315384700ccae..0000000000000000000000000000000000000000
--- a/libs/shared/lib/vis/visualizations/mapvis/settings.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import React from 'react';
-import { SettingsContainer } from '../../components/config';
-import { layerSettings, layerTypes } from './layers';
-import { Input } from '../../..';
-import { VisualizationSettingsPropTypes } from '../../common';
-import { MapProps } from './mapvis';
-
-export const MapSettings = ({ settings, graphMetadata, updateSettings }: VisualizationSettingsPropTypes<MapProps>) => {
-  const DataLayerSettings = settings.layer && layerSettings?.[settings.layer];
-
-  return (
-    <SettingsContainer>
-      <Input
-        label="Data layer"
-        type="dropdown"
-        inline
-        value={settings.layer}
-        options={Object.keys(layerTypes)}
-        onChange={(val) => updateSettings({ layer: val as string })}
-      />
-      {DataLayerSettings && <DataLayerSettings settings={settings} graphMetadata={graphMetadata} updateSettings={updateSettings} />}
-    </SettingsContainer>
-  );
-};