-
-
- Helaas, de bodemkaart is nog niet beschikbaar :(
-
-
- We proberen de bodemkaart binnenkort toe te voegen. Hou de
- website in de gaten.
-
-
-
+
+
+
+ setViewState((currentViewState) => ({
+ ...currentViewState,
+ longitude,
+ latitude,
+ zoom,
+ }))
+ }
+ showFields={showFields}
+ onToggleFields={() => setShowFields(!showFields)}
+ showSoil={showSoil}
+ onToggleSoil={onToggleSoil}
+ />
+
+
+
+ {/* Soil WMS Layer */}
+ {showSoil && (
+
+
+
+ )}
+
+ {/* Selected Soil Feature Highlight */}
+ {showSoil && selectedSoilFeature && (
+
+
+
+ )}
+
+ {/* Fields Overlay */}
+ {fields && (
+
+
+
+
+ )}
+
+ {/* Popup */}
+ {showSoil && popupInfo && (
+ setPopupInfo(null)}
+ anchor="bottom"
+ maxWidth="350px"
+ >
+
+
+
+ {popupInfo.properties.first_soilname ||
+ popupInfo.properties
+ .normal_soilprofile_name ||
+ "Onbekende bodem"}
+
+ {popupInfo.properties.soilcode && (
+
+ {popupInfo.properties.soilcode}
+
+ )}
+
+
+
+ )}
+
+
+
)
}
diff --git a/fdm-app/app/tailwind.css b/fdm-app/app/tailwind.css
index c9b8a0c16..46a084c42 100644
--- a/fdm-app/app/tailwind.css
+++ b/fdm-app/app/tailwind.css
@@ -190,3 +190,22 @@
display: none;
}
+/* MapLibre Popup Close Button Fix */
+.maplibregl-popup-close-button {
+ font-size: 24px;
+ padding: 8px;
+ width: 32px;
+ height: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--color-foreground);
+ border-radius: 0 0.5rem 0 0.5rem;
+}
+.maplibregl-popup-close-button:hover {
+ background-color: var(--color-muted);
+}
+.maplibregl-popup-content {
+ border-radius: 0.5rem;
+ padding: 0;
+}
\ No newline at end of file
From e4d7d8c33e5602f4d26c2a2718b280b67aa54212 Mon Sep 17 00:00:00 2001
From: Sven Verweij <37927107+SvenVw@users.noreply.github.com>
Date: Thu, 22 Jan 2026 14:49:14 +0100
Subject: [PATCH 2/5] refactor: use better layer icons
---
fdm-app/app/components/blocks/atlas/atlas-controls.tsx | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/fdm-app/app/components/blocks/atlas/atlas-controls.tsx b/fdm-app/app/components/blocks/atlas/atlas-controls.tsx
index a451fffa7..2eb887dbc 100644
--- a/fdm-app/app/components/blocks/atlas/atlas-controls.tsx
+++ b/fdm-app/app/components/blocks/atlas/atlas-controls.tsx
@@ -1,4 +1,4 @@
-import { Earth, Layers, Mountain } from "lucide-react"
+import { Layers, Mountain, PanelsRightBottom } from "lucide-react"
import type { ControlPosition, Map as MapLibreMap } from "maplibre-gl"
import { useEffect } from "react"
import { createRoot, type Root } from "react-dom/client"
@@ -160,7 +160,7 @@ function FieldsControl({
onToggle,
labelActive: "Verberg percelen",
labelInactive: "Toon percelen",
- Icon: Layers,
+ Icon: PanelsRightBottom,
}),
CONTROL_OPTIONS,
)
@@ -171,7 +171,7 @@ function FieldsControl({
onToggle,
labelActive: "Verberg percelen",
labelInactive: "Toon percelen",
- Icon: Layers,
+ Icon: PanelsRightBottom,
})
}, [control, showFields, onToggle])
@@ -224,7 +224,7 @@ function SoilControl({
onToggle,
labelActive: "Verberg bodemkaart",
labelInactive: "Toon bodemkaart",
- Icon: Earth,
+ Icon: Layers,
}),
CONTROL_OPTIONS,
)
@@ -235,7 +235,7 @@ function SoilControl({
onToggle,
labelActive: "Verberg bodemkaart",
labelInactive: "Toon bodemkaart",
- Icon: Earth,
+ Icon: Layers,
})
}, [control, showSoil, onToggle])
From 386a29eeba5683d8e3d5ece2dc1ab80cb2d7c400 Mon Sep 17 00:00:00 2001
From: Sven Verweij <37927107+SvenVw@users.noreply.github.com>
Date: Thu, 22 Jan 2026 15:10:07 +0100
Subject: [PATCH 3/5] refactor: improvements
---
.../farm.$b_id_farm.$calendar.atlas.soil.tsx | 35 +++++++++++++------
1 file changed, 24 insertions(+), 11 deletions(-)
diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.soil.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.soil.tsx
index a7592356b..ebd39c9b9 100644
--- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.soil.tsx
+++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.soil.tsx
@@ -194,8 +194,8 @@ export default function FarmAtlasSoilBlock() {
bbox: bbox,
width: width.toString(),
height: height.toString(),
- i: Math.round(point.x).toString(),
- j: Math.round(point.y).toString(),
+ I: Math.round(point.x).toString(),
+ J: Math.round(point.y).toString(),
})
const url = `https://service.pdok.nl/bzk/bro-bodemkaart/wms/v1_0?${params.toString()}`
@@ -248,6 +248,26 @@ export default function FarmAtlasSoilBlock() {
})
}, [])
+ const onToggleFields = useCallback(() => {
+ setShowFields((prev) => !prev)
+ }, [])
+
+ const onControlsViewportChange = useCallback(
+ ({
+ longitude,
+ latitude,
+ zoom,
+ }: { longitude: number; latitude: number; zoom: number }) => {
+ setViewState((current) => ({
+ ...current,
+ longitude,
+ latitude,
+ zoom,
+ }))
+ },
+ [],
+ )
+
return (
- setViewState((currentViewState) => ({
- ...currentViewState,
- longitude,
- latitude,
- zoom,
- }))
- }
+ onViewportChange={onControlsViewportChange}
showFields={showFields}
- onToggleFields={() => setShowFields(!showFields)}
+ onToggleFields={onToggleFields}
showSoil={showSoil}
onToggleSoil={onToggleSoil}
/>
From 48879b29fbf7eb8c3a79432004e8cb0506b6d5af Mon Sep 17 00:00:00 2001
From: Sven Verweij <37927107+SvenVw@users.noreply.github.com>
Date: Thu, 22 Jan 2026 17:07:00 +0100
Subject: [PATCH 4/5] refactor: guard sessionStorage acces
---
...farm.$b_id_farm.$calendar.atlas.elevation.tsx | 15 +++++++--------
....$b_id_farm.$calendar.atlas.fields._index.tsx | 13 ++++++-------
.../farm.$b_id_farm.$calendar.atlas.soil.tsx | 16 ++++++++++------
3 files changed, 23 insertions(+), 21 deletions(-)
diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx
index 7c8939489..26ec9bff1 100644
--- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx
+++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx
@@ -190,18 +190,18 @@ export default function FarmAtlasElevationBlock() {
const initialViewState = getViewState(fields)
const [viewState, setViewState] = useState(() => {
if (typeof window !== "undefined") {
- const savedViewState = sessionStorage.getItem("mapViewState")
- if (savedViewState) {
- try {
+ try {
+ const savedViewState = sessionStorage.getItem("mapViewState")
+ if (savedViewState) {
return JSON.parse(savedViewState)
- } catch {
- sessionStorage.removeItem("mapViewState")
}
+ } catch {
+ // ignore storage errors (e.g., private mode)
}
}
return initialViewState as ViewState
})
-
+
const onViewportChange = useCallback((event: ViewStateChangeEvent) => {
setViewState(event.viewState)
}, [])
@@ -231,8 +231,7 @@ export default function FarmAtlasElevationBlock() {
// Cache for 24 hours and ensure data is valid
if (
data?.features?.length > 0 &&
- Date.now() - timestamp <
- 24 * 60 * 60 * 1000
+ Date.now() - timestamp < 24 * 60 * 60 * 1000
) {
setIndexData(data)
setNetworkStatus("idle")
diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.fields._index.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.fields._index.tsx
index 101d46098..85bfc1db0 100644
--- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.fields._index.tsx
+++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.fields._index.tsx
@@ -127,18 +127,17 @@ export default function FarmAtlasFieldsBlock() {
const fieldsAvailableId = "fieldsAvailable"
const fieldsAvailableStyle = getFieldsStyle(fieldsAvailableId)
const fieldsSavedOutlineStyle = getFieldsStyle("fieldsSavedOutline")
+ // ViewState logic
const initialViewState = getViewState(fields)
-
- // Create a sessionStorage to store the latest viewstate
const [viewState, setViewState] = useState(() => {
if (typeof window !== "undefined") {
- const savedViewState = sessionStorage.getItem("mapViewState")
- if (savedViewState) {
- try {
+ try {
+ const savedViewState = sessionStorage.getItem("mapViewState")
+ if (savedViewState) {
return JSON.parse(savedViewState)
- } catch {
- sessionStorage.removeItem("mapViewState")
}
+ } catch {
+ // ignore storage errors (e.g., private mode)
}
}
return initialViewState as ViewState
diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.soil.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.soil.tsx
index ebd39c9b9..874b36b79 100644
--- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.soil.tsx
+++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.soil.tsx
@@ -140,13 +140,13 @@ export default function FarmAtlasSoilBlock() {
const initialViewState = getViewState(fields)
const [viewState, setViewState] = useState(() => {
if (typeof window !== "undefined") {
- const savedViewState = sessionStorage.getItem("mapViewState")
- if (savedViewState) {
- try {
+ try {
+ const savedViewState = sessionStorage.getItem("mapViewState")
+ if (savedViewState) {
return JSON.parse(savedViewState)
- } catch {
- sessionStorage.removeItem("mapViewState")
}
+ } catch {
+ // ignore storage errors (e.g., private mode)
}
}
return initialViewState as ViewState
@@ -257,7 +257,11 @@ export default function FarmAtlasSoilBlock() {
longitude,
latitude,
zoom,
- }: { longitude: number; latitude: number; zoom: number }) => {
+ }: {
+ longitude: number
+ latitude: number
+ zoom: number
+ }) => {
setViewState((current) => ({
...current,
longitude,
From 52dd9049367ad3225b712a41af99d2aa7ff4f843 Mon Sep 17 00:00:00 2001
From: Sven Verweij <37927107+SvenVw@users.noreply.github.com>
Date: Thu, 22 Jan 2026 17:49:01 +0100
Subject: [PATCH 5/5] fix: persist viewstate
---
.../farm.$b_id_farm.$calendar.atlas.elevation.tsx | 12 ++++++------
...arm.$b_id_farm.$calendar.atlas.fields._index.tsx | 13 ++++++-------
.../routes/farm.$b_id_farm.$calendar.atlas.soil.tsx | 12 +++++++++++-
3 files changed, 23 insertions(+), 14 deletions(-)
diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx
index 26ec9bff1..3b54e0791 100644
--- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx
+++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx
@@ -206,14 +206,14 @@ export default function FarmAtlasElevationBlock() {
setViewState(event.viewState)
}, [])
- // Save viewState
- const isFirstRender = useRef(true)
useEffect(() => {
- if (isFirstRender.current) {
- isFirstRender.current = false
- return
+ if (typeof window !== "undefined") {
+ try {
+ sessionStorage.setItem("mapViewState", JSON.stringify(viewState))
+ } catch {
+ // ignore storage errors (e.g., private mode)
+ }
}
- sessionStorage.setItem("mapViewState", JSON.stringify(viewState))
}, [viewState])
// Fetch COG Index once
diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.fields._index.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.fields._index.tsx
index 85bfc1db0..65f90d3fa 100644
--- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.fields._index.tsx
+++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.fields._index.tsx
@@ -149,15 +149,14 @@ export default function FarmAtlasFieldsBlock() {
setViewState(event.viewState)
}, [])
- const isFirstRender = useRef(true)
-
- // If latest viewstate is available use that one
useEffect(() => {
- if (isFirstRender.current) {
- isFirstRender.current = false
- return
+ if (typeof window !== "undefined") {
+ try {
+ sessionStorage.setItem("mapViewState", JSON.stringify(viewState))
+ } catch {
+ // ignore storage errors (e.g., private mode)
+ }
}
- sessionStorage.setItem("mapViewState", JSON.stringify(viewState))
}, [viewState])
const layerLayout = { visibility: showFields ? "visible" : "none" } as const
diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.soil.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.soil.tsx
index 874b36b79..96caf590b 100644
--- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.soil.tsx
+++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.soil.tsx
@@ -3,7 +3,7 @@ import { simplify } from "@turf/turf"
import type { Feature, FeatureCollection, Geometry } from "geojson"
import maplibregl from "maplibre-gl"
import proj4 from "proj4"
-import { useCallback, useRef, useState } from "react"
+import { useCallback, useEffect, useRef, useState } from "react"
import {
Layer,
Map as MapGL,
@@ -152,6 +152,16 @@ export default function FarmAtlasSoilBlock() {
return initialViewState as ViewState
})
+ useEffect(() => {
+ if (typeof window !== "undefined") {
+ try {
+ sessionStorage.setItem("mapViewState", JSON.stringify(viewState))
+ } catch {
+ // ignore storage errors (e.g., private mode)
+ }
+ }
+ }, [viewState])
+
const onViewportChange = useCallback((event: ViewStateChangeEvent) => {
setViewState(event.viewState)
}, [])