Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/atlas-soil-map-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@svenvw/fdm-app": minor
---

Integrated the BRO "Bodemkaart" (Soil Map) into the Atlas
46 changes: 43 additions & 3 deletions fdm-app/app/components/blocks/atlas/atlas-controls.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { 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"
Expand All @@ -21,6 +21,8 @@ type ControlsProps = {
onToggleFields?: () => void
showElevation?: boolean
onToggleElevation?: () => void
showSoil?: boolean
onToggleSoil?: () => void
}

export function Controls(props: ControlsProps) {
Expand All @@ -44,6 +46,12 @@ export function Controls(props: ControlsProps) {
onToggle={props.onToggleElevation}
/>
)}
{props.showSoil !== undefined && props.onToggleSoil && (
<SoilControl
showSoil={props.showSoil}
onToggle={props.onToggleSoil}
/>
)}
<GeolocateControl
positionOptions={{ enableHighAccuracy: true }}
trackUserLocation={true}
Expand Down Expand Up @@ -152,7 +160,7 @@ function FieldsControl({
onToggle,
labelActive: "Verberg percelen",
labelInactive: "Toon percelen",
Icon: Layers,
Icon: PanelsRightBottom,
}),
CONTROL_OPTIONS,
)
Expand All @@ -163,7 +171,7 @@ function FieldsControl({
onToggle,
labelActive: "Verberg percelen",
labelInactive: "Toon percelen",
Icon: Layers,
Icon: PanelsRightBottom,
})
}, [control, showFields, onToggle])

Expand Down Expand Up @@ -201,3 +209,35 @@ function ElevationControl({

return null
}

function SoilControl({
showSoil,
onToggle,
}: {
showSoil: boolean
onToggle: () => void
}) {
const control = useControl<CustomControl>(
() =>
new CustomControl({
active: showSoil,
onToggle,
labelActive: "Verberg bodemkaart",
labelInactive: "Toon bodemkaart",
Icon: Layers,
}),
CONTROL_OPTIONS,
)

useEffect(() => {
control.updateProps({
active: showSoil,
onToggle,
labelActive: "Verberg bodemkaart",
labelInactive: "Toon bodemkaart",
Icon: Layers,
})
}, [control, showSoil, onToggle])

return null
}
14 changes: 13 additions & 1 deletion fdm-app/app/components/blocks/header/atlas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ export function HeaderAtlas({ b_id_farm }: { b_id_farm: string | undefined }) {
const location = useLocation()

const isElevation = location.pathname.includes("/elevation")
const currentName = isElevation ? "Hoogtekaart" : "Gewaspercelen"
const isSoil = location.pathname.includes("/soil")
const currentName = isElevation
? "Hoogtekaart"
: isSoil
? "Bodemkaart"
: "Gewaspercelen"

return (
<>
Expand Down Expand Up @@ -50,6 +55,13 @@ export function HeaderAtlas({ b_id_farm }: { b_id_farm: string | undefined }) {
Hoogtekaart
</NavLink>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<NavLink
to={`/farm/${b_id_farm}/${calendar}/atlas/soil`}
>
Bodemkaart
</NavLink>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</BreadcrumbItem>
Expand Down
18 changes: 18 additions & 0 deletions fdm-app/app/components/blocks/sidebar/apps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,22 @@ export function SidebarApps() {
let atlasLink: string | undefined
let atlasFieldsLink: string | undefined
let atlasElevationLink: string | undefined
let atlasSoilLink: string | undefined
if (isCreateFarmWizard) {
atlasLink = undefined
atlasFieldsLink = undefined
atlasElevationLink = undefined
atlasSoilLink = undefined
} else if (farmId) {
atlasLink = `/farm/${farmId}/${selectedCalendar}/atlas`
atlasFieldsLink = `/farm/${farmId}/${selectedCalendar}/atlas/fields`
atlasElevationLink = `/farm/${farmId}/${selectedCalendar}/atlas/elevation`
atlasSoilLink = `/farm/${farmId}/${selectedCalendar}/atlas/soil`
} else {
atlasLink = `/farm/undefined/${selectedCalendar}/atlas`
atlasFieldsLink = `/farm/undefined/${selectedCalendar}/atlas/fields`
atlasElevationLink = `/farm/undefined/${selectedCalendar}/atlas/elevation`
atlasSoilLink = `/farm/undefined/${selectedCalendar}/atlas/soil`
}

let nitrogenBalanceLink: string | undefined
Expand Down Expand Up @@ -158,6 +162,20 @@ export function SidebarApps() {
</SidebarMenuSubButton>
) : null}
</SidebarMenuSubItem>
<SidebarMenuSubItem>
{atlasSoilLink ? (
<SidebarMenuSubButton
asChild
isActive={location.pathname.includes(
atlasSoilLink,
)}
>
<NavLink to={atlasSoilLink}>
<span>Bodemkaart</span>
</NavLink>
</SidebarMenuSubButton>
) : null}
</SidebarMenuSubItem>
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
Expand Down
27 changes: 13 additions & 14 deletions fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.elevation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,30 +190,30 @@
const initialViewState = getViewState(fields)
const [viewState, setViewState] = useState<ViewState>(() => {
if (typeof window !== "undefined") {
const savedViewState = sessionStorage.getItem("mapViewState")
if (savedViewState) {
try {
try {
const savedViewState = sessionStorage.getItem("mapViewState")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why within try catch? can this generate an error?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if you are in private mode of your browser

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)
}, [])

// 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))
Comment thread
SvenVw marked this conversation as resolved.
Dismissed
} catch {
// ignore storage errors (e.g., private mode)
}
}
sessionStorage.setItem("mapViewState", JSON.stringify(viewState))
}, [viewState])

// Fetch COG Index once
Expand All @@ -231,8 +231,7 @@
// 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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,18 +127,17 @@
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<ViewState>(() => {
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
Expand All @@ -150,15 +149,14 @@
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))
Comment thread
SvenVw marked this conversation as resolved.
Dismissed
} catch {
// ignore storage errors (e.g., private mode)
}
}
sessionStorage.setItem("mapViewState", JSON.stringify(viewState))
}, [viewState])

const layerLayout = { visibility: showFields ? "visible" : "none" } as const
Expand Down
Loading