Creating a New Layer in Frontend

This document gives guidelines on how to implement a new layer in the frontend.

Folder Structure

  • Navigate to the layers directory in your project (features/map_planning/layers).
  • Create a new folder named after the type of layer you want to add (e.g., new_layer).

Add Necessary Files and Folders

  • Inside the "newLayer" folder:
    • If your layer requires API interactions, create an api folder and place your .ts files in there or place them into the map_planning api folder depending on whether it's a special api that is part of the layer or it's just for initiating the layer in general.
    • For React components or custom hooks specific to this layer, add a components or hooks folder.
    • Create a main TypeScript file for the layer (e.g., NewLayer.tsx).
    • Inside components create a NewLayerLeftToolbar.tsx, NewLayerAttributeEditForm.tsx and a NewLayerRightToolbar.tsx.
    • Inside the newLayer folder create a file actions.ts.

Define the Layer Component

  • In NewLayer.tsx:
    • Import Konva and other dependencies.
    • Define a functional component that returns a Konva Layer.
  • In mapEditorHookApi.ts:
    • Define a hook for your layer (e.g., useNewLayer.tsx).
    • Add states (see next section) that need to be saved across components.
    • Add an init function (e.g., initNewLayer) which is then defined inside the TrackedMapStore.ts.

Implement Layer Logic

  • Use hooks (starting with use in the name) and the global MapStore (tracked & untracked) to manage state and interactions.
  • Ensure proper interaction with other layers and elements.
  • In EditorMap.getToolbarContent replace the empty divs for left and right with the toolbars for your layer.
  • Toolbars
    • Set up NewLayerLeftToolbar.tsx with change listeners as the component for manipulating attributes of the selected map element.
    • Inside NewLayerLeftToolbar.tsx, import and use NewLayerAttributeEditForm.tsx to show/edit element attributes.
    • In NewLayerAttributeEditForm.tsx:
      • Add fields for all attributes of the element.
      • Use BasicAttributeEditForm.tsx (from map_planning/components/toolbar/) for addDate, removeDate, and notes fields.
      • Add a delete button that deletes the element from the map (not only remove) like it has never existed.
    • Set up NewLayerRightToolbar.tsx as the component for selecting elements to be placed/drawn on the map.
      • Add buttons and components for selecting and drawing new elements directly in NewLayerRightToolbar.tsx.
  • For selecting an element to be placed add a function to UntrackedMapStore.ts and a state to MapStoreTypes.ts.
  • Write the client-side for the layer's API and put it into map_planning/api (except for brushing layers - see Brushing Layers below).
  • Actions include:
    • Create (placing an element on the map)
    • Delete
    • Separate Updates
      • Add Date
      • Remove Date
      • Notes
      • Type/Value
    • For some layers (except brushing layers)
      • Move
      • Transform
  • For every action do the following steps
    • Create a class inside actions.ts.
    • In every action the following functions are needed:
      • entityIds: entity ids that are affected by this action
      • execute: commits the changes over the api
      • reverse: undoes the action
      • apply: applies the given tracked state
  • Use the actions within NewLayer.tsx in listeners or hooks.
  • Add an entry for every action type to switch statement in convertToAction of map_planning/store/RemoteActions.ts.
    • This is the only thing needed for concurrent use to work (several browsers with same map).
  • For undo and redo no changes are needed.

Brushing Layers

These steps are additionally necessary only when implementing a brushing layer (like shade, hydrology, soiltexture).

  • Instead of writing an own API for each brushing layer use the generic areaApi.ts from frontend/src/features/map_planning/api/areaApi.ts.
  • Use Area.tsx and Brush.tsx from map_planning/components/brush/ in NewLayer.tsx.
    • Pass them the states for concavity and strokeWidth.
    • Write a callback function handlePlaceNewLayerArea that executes the corresponding action for creating the area.
  • In area-utils.ts (map_planning/utils/area-utils.ts)
    • Write a function fillColorFromNewLayerType that gives you the color for each type.
    • Write a function getHydrologyFromArea that extracts the area object itself.
  • In frontend/tailwind.config.ts add a color definition for each type (DEFAULT and dark).

Test Your Layer

  • Write tests for hooks (in the same folder as hooks) if feasible/useful (see plant/hooks) to verify functionality.
    • Especially if side effects or wrong behaviour can not be easily seen in manual tests!