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
orhooks
folder. - Create a main TypeScript file for the layer (e.g.,
NewLayer.tsx
). - Inside
components
create aNewLayerLeftToolbar.tsx
,NewLayerAttributeEditForm.tsx
and aNewLayerRightToolbar.tsx
. - Inside the
newLayer
folder create a file actions.ts.
- If your layer requires API interactions, create an
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 theTrackedMapStore.ts
.
- Define a hook for your layer (e.g.,
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 useNewLayerAttributeEditForm.tsx
to show/edit element attributes. - In
NewLayerAttributeEditForm.tsx
:- Add fields for all attributes of the element.
- Use
BasicAttributeEditForm.tsx
(frommap_planning/components/toolbar/
) foraddDate
,removeDate
, andnotes
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.
- Set up
- For selecting an element to be placed add a function to
UntrackedMapStore.ts
and a state toMapStoreTypes.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 actionexecute
: commits the changes over the apireverse
: undoes the actionapply
: applies the given tracked state
- Create a class inside
- Use the actions within
NewLayer.tsx
in listeners or hooks. - Add an entry for every action type to switch statement in
convertToAction
ofmap_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
fromfrontend/src/features/map_planning/api/areaApi.ts
. - Use
Area.tsx
andBrush.tsx
frommap_planning/components/brush/
inNewLayer.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.
- Write a function
- 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!