Per-field norms errors, GeoTIFF refactor, derogatievrije zones support#280
Conversation
…as no variety provided
…snorm calculation
…or 1st and later years
…an error message on the card for that field, but do show a general error for the whole page and show still the fields that were able to calculate
🦋 Changeset detectedLatest commit: fb778ac The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
WalkthroughAdds per-field norm error handling and UI indicators; centralizes public-data URL and GeoTIFF helpers; replaces vector lookups with GeoTIFF queries for multiple norm/area checks; restructures stikstof norm data to use per-sub-type entries; updates deposition logic, tests, and TypeScript module resolution. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Route as Norms route (loader)
participant Calc as Norms calculator
participant Geo as GeoTIFF utils
participant Storage as Public Data Storage
Route->>Route: Build per-field promises (try/catch)
par For each field
Route->>Calc: request field norms
Calc->>Geo: getGeoTiffValue(url, lon, lat) for NV/region/areas
Geo->>Storage: fetch TIFF (deduped/cache)
Storage-->>Geo: TIFF bytes
Geo-->>Calc: raster value or null
alt success
Calc-->>Route: norms object
else error
Calc-->>Route: throws -> Route captures field.errorMessage
end
end
Route->>Route: Promise.allSettled -> assemble fieldNorms[]
Route->>Route: aggregate hasFieldNormErrors & fieldErrorMessages
Route->>Route: compute farm totals from valid fields
Route-->>UI: render FieldNorms (inline errors) and FarmNorms (tooltip)
sequenceDiagram
autonumber
participant C as getNL2025StikstofGebruiksNorm
participant D as determineSubTypeOmschrijving
participant G as getNormsForCultivation
participant Geo as GeoTIFF utils
C->>Geo: getGeoTiffValue(...grondsoortenkaart) -> region code
Geo-->>C: region
C->>Geo: getGeoTiffValue(...nv_gebied) -> nv flag
Geo-->>C: nv flag
C->>D: derive subTypeOmschrijving (cultivation/history/variety)
D-->>C: subTypeOmschrijving?
C->>G: getNormsForCultivation(standard, date, subTypeOmschrijving)
G-->>C: NormsByRegion (selected sub_type/norms)
C-->>Caller: composed normValue and normSource (may include sub-type text)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~70 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## development #280 +/- ##
===============================================
+ Coverage 92.93% 93.20% +0.26%
===============================================
Files 79 81 +2
Lines 12952 12901 -51
Branches 1268 1305 +37
===============================================
- Hits 12037 12024 -13
+ Misses 913 875 -38
Partials 2 2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx (1)
127-171: Fix undefined variable: fieldOptions is not defined in loaderThe catch block uses fieldOptions, but it’s only defined later in the client component, not in the loader scope. This will throw at runtime.
Apply this diff:
- const fieldName = - fieldOptions.find((opt) => opt.b_id === field.b_id) - ?.b_name || `Perceel ${field.b_id}` + const fieldName = + fields.find((f) => f.b_id === field.b_id)?.b_name || + `Perceel ${field.b_id}`
🧹 Nitpick comments (24)
.changeset/kind-singers-arrive.md (1)
5-5: Polish the changeset description for clarity.Suggest rewording and punctuation for readability.
-At norms replace vector lookup of remote datasets with raster query to improve performance and reliability +In norms, replace vector lookups of remote datasets with raster queries to improve performance and reliability..changeset/tame-cars-kneel.md (1)
5-5: Minor wording improvement.Small grammar tweak for clarity.
-Add support for cultivation with different stikstofgebruiksnormen for 1st and later years +Add support for cultivations with different stikstofgebruiksnormen for first and subsequent years..changeset/every-shoes-boil.md (1)
5-5: Fix grammar.Use “when calculating” instead of “at calculation of”.
-Fixes exception at calculation of stikstofgebruiksnorm for cultivations with sub_types +Fix exception when calculating stikstofgebruiksnorm for cultivations with sub_types..changeset/crazy-onions-poke.md (1)
5-5: Fix typos and improve readability."calculationg" typo and phrasing makes the sentence hard to parse.
-At norms when a field has an error for calculationg norms show an error message on the card for that field, but do show a general error for the whole page and show still the fields that were able to calculate +In norms, when a field has an error during calculation, show an error message on that field’s card; also show a general error for the whole page; still render the fields that were calculated successfully.fdm-calculator/src/balance/nitrogen/supply/deposition.ts (3)
27-33: Avoid hardcoding year/region; derive when possible.Minor: consider deriving year from timeFrame and guarding unsupported years. Keep the TODO but reduce future touchpoints.
-// Currently, only the year 2022 is available. -// TODO: Add support for multiple years when data becomes available. -const year = "2022" -const region = "nl" +// TODO: Add support for multiple years when data becomes available. +const requestedYear = timeFrame.start.getUTCFullYear().toString() +const year = ["2022"].includes(requestedYear) ? requestedYear : "2022" +const region = "nl"
34-34: Comment step numbering is inconsistent.You're using “Step 1”, “Step 3”, and “Step 4”. Renumber or simplify to avoid confusion.
-// Step 1: Create an array of promises to calculate deposition for each field concurrently. +// Create an array of promises to calculate deposition for each field concurrently.
49-52: Skip GeoTIFF lookup when fraction is zero.Small perf win: avoid I/O when timeframe has no overlap.
- // Get the deposition value from the GeoTIFF using the new getTiffValue function. - const [longitude, latitude] = field.field.b_centroid - const value = await getGeoTiffValue(url, longitude, latitude) + // Skip lookup if timeframe has no overlap. + if (fraction.isZero()) { + return { + fieldId: field.field.b_id, + deposition: { total: new Decimal(0) }, + } + } + // Get the deposition value from the GeoTIFF. + const [longitude, latitude] = field.field.b_centroid + const value = await getGeoTiffValue(url, longitude, latitude)fdm-calculator/src/shared/public-data-url.ts (1)
1-3: Make base URL configurable and normalize trailing slashAllow overriding via env and ensure a single trailing slash to avoid accidental
//joins.Apply:
-export function getFdmPublicDataUrl(): string { - return "https://storage.googleapis.com/fdm-public-data/" -} +export function getFdmPublicDataUrl(): string { + const fallback = "https://storage.googleapis.com/fdm-public-data/" + // process may be undefined in browsers – guard accordingly + const fromEnv = + typeof process !== "undefined" && process.env?.FDM_PUBLIC_DATA_URL + ? process.env.FDM_PUBLIC_DATA_URL + : undefined + const base = fromEnv ?? fallback + return base.endsWith("/") ? base : `${base}/` +}fdm-calculator/src/norms/nl/2025/dierlijke-mest-gebruiksnorm.test.ts (1)
116-134: Add an integration test: derogatie-vrije zone should suppress derogationAdd a case where farm.is_derogatie_bedrijf = true but the centroid is inside the derogatie-vrije zone; expect the non-derogation cap (170) and the appropriate source label.
fdm-app/app/components/blocks/norms/field-norms.tsx (1)
66-156: Guard against missing norms, add a11y to error, and standardize unit labelAvoid rendering “undefined” when norms are absent, mark the error container as an alert, and normalize “kg P2O5 / ha”.
Apply:
- {field.errorMessage ? ( - <div className="p-3 bg-red-50 rounded-lg border border-red-100 text-red-700"> + {field.errorMessage ? ( + <div + className="p-3 bg-red-50 rounded-lg border border-red-100 text-red-700" + role="alert" + aria-live="polite" + > ... - ) : ( + ) : field.norms ? ( ... - <span className="text-sm font-normal text-gray-600"> - kg P2O5 /ha - </span> + <span className="text-sm font-normal text-gray-600"> + kg P2O5 / ha + </span> ... - )} + ) : ( + <div className="p-3 bg-yellow-50 rounded-lg border border-yellow-100 text-gray-700"> + Nog geen normen beschikbaar voor dit perceel. + </div> + )}fdm-calculator/src/balance/nitrogen/index.ts (2)
309-335: Confirm rounding to integer output is intended.convertDecimalToNumberRecursive rounds all values to integers. If fractional kg/ha are needed (UI or API), consider toDecimalPlaces with a defined precision or passing a rounding mode.
Example change:
- return data.round().toNumber() + return data.toDecimalPlaces(1, Decimal.ROUND_HALF_UP).toNumber()
351-356: Avoid mutating input when sorting.combineSoilAnalyses sorts soilAnalyses in place. If callers rely on original ordering elsewhere, make a shallow copy before sorting.
- soilAnalyses.sort((a, b) => { + const analyses = [...soilAnalyses] + analyses.sort((a, b) => { return ( new Date(b.b_sampling_date).getTime() - new Date(a.b_sampling_date).getTime() ) }) ... - for (const prop of propertiesToExtract) { - soilAnalysis[prop] = - soilAnalyses.find( + for (const prop of propertiesToExtract) { + soilAnalysis[prop] = + analyses.find( (x) => x[prop] !== null && x[prop] !== undefined, )?.[prop] || null }fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm-data.ts (1)
920-920: Fix typos and label consistency.Minor text issues that can confuse users or tests relying on exact labels:
- Line 920: duplicated “Akkerbouwgewassen,”
- Line 971: “Baldgewassen” → “Bladgewassen”
- Line 742: missing space after comma
- Line 2033: missing space after comma
- Line 2439: trailing comma in label
- cultivation_rvo_table2: "Akkerbouwgewassen, Akkerbouwgewassen, overig", + cultivation_rvo_table2: "Akkerbouwgewassen, overig", - cultivation_rvo_table2: "Baldgewassen, Spinazie volgteelt", + cultivation_rvo_table2: "Bladgewassen, Spinazie volgteelt", - cultivation_rvo_table2: "Akkerbouwgewassen, Graszaad,Westerwolds", + cultivation_rvo_table2: "Akkerbouwgewassen, Graszaad, Westerwolds", - cultivation_rvo_table2: "Bloembollengewassen,Lelie", + cultivation_rvo_table2: "Bloembollengewassen, Lelie", - cultivation_rvo_table2: "Boomkwekerijgewassen, Vruchtbomen, overig,", + cultivation_rvo_table2: "Boomkwekerijgewassen, Vruchtbomen, overig",Also applies to: 971-971, 742-742, 2033-2033, 2439-2439
fdm-calculator/src/norms/nl/2025/dierlijke-mest-gebruiksnorm.ts (3)
24-39: Use 2025 layers (if available) and improve error context.
- Paths point to 2024 datasets; confirm correct vintage for 2025 norms.
- Error messages omit coordinates; include them for diagnostics.
- const url = `${fdmPublicDataUrl}norms/nl/2024/gwbg.tiff` + const url = `${fdmPublicDataUrl}norms/nl/2025/gwbg.tiff` ... - default: { - throw new Error(`Unknown GWBG code: ${gwbgCode} for coordinates , `) + default: { + throw new Error( + `Unknown GWBG code: ${gwbgCode} for coordinates ${longitude}, ${latitude}`, + )
57-76: Same as GWBG: dataset year and error details.Align to 2025 (if correct source) and include coordinates in error message.
- const url = `${fdmPublicDataUrl}norms/nl/2024/natura2000.tiff` + const url = `${fdmPublicDataUrl}norms/nl/2025/natura2000.tiff` ... - `Unknown Natura2000 code: ${natura2000Code} for coordinates , `, + `Unknown Natura2000 code: ${natura2000Code} for coordinates ${longitude}, ${latitude}`,
89-115: Polish: error message typo and details.Fix message grammar and add coordinates. Also confirm GeoTIFF value domain is strictly {0,1}; treat null as 0 if NoData outside NL is expected.
- `Unknown derogatieVrijeZoneCodes code: ${derogatieVrijeZoneCode} for coordinates , `, + `Unknown derogatie-vrije zone code: ${derogatieVrijeZoneCode} for coordinates ${longitude}, ${latitude}`,Optional: handle null as false
- switch (derogatieVrijeZoneCode) { + switch (derogatieVrijeZoneCode ?? 0) {fdm-calculator/src/shared/geotiff.ts (1)
56-102: Add optional AbortSignal and document CRS assumption.
- readRasters supports AbortController; accept an optional signal to allow upstream cancellation.
- This math assumes the TIFF CRS matches the longitude/latitude passed (likely EPSG:4326). If sources vary, consider asserting/transforming.
-export async function getGeoTiffValue( - url: string, - longitude: number, - latitude: number, -): Promise<number | null> { +export async function getGeoTiffValue( + url: string, + longitude: number, + latitude: number, + options?: { signal?: AbortSignal }, +): Promise<number | null> { ... - const rasterData = await image.readRasters({ window }) + const rasterData = await image.readRasters({ window, signal: options?.signal })Optionally export a clearCache for tests/long-lived processes:
export function clearGeoTiffCaches() { tiffCache.clear() tiffPromiseCache.clear() }fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts (6)
30-47: Improve NV-gebied error handling and message clarity
- Include coordinates in the error to aid debugging.
- Consider handling null (outside TIFF/NoData) explicitly before the switch for clearer failures.
Apply this diff:
const NVGebiedCode = await getGeoTiffValue(url, longitude, latitude) + if (NVGebiedCode === null) { + throw new Error( + `No NV-gebied value for coordinates ${longitude}, ${latitude} (outside coverage or NoData)`, + ) + } + switch (NVGebiedCode) { case 1: { return true } case 0: { return false } default: { throw new Error( - `Unknown NVGebied code: ${NVGebiedCode} for coordinates , `, + `Unknown NV-gebied code: ${NVGebiedCode} for coordinates ${longitude}, ${latitude}`, ) } }
72-100: Handle null region values, fix typo, and verify dataset year
- Add an explicit null check (outside TIFF/NoData).
- Correct variable name typo and include coordinates in the error.
- The URL references 2024 data in a 2025 module. Verify that this is intentional and that the 2024 grondsoorten.tiff is valid for 2025.
Apply this diff:
- const grondoortCode = await getGeoTiffValue(url, longitude, latitude) + const grondsoortCode = await getGeoTiffValue(url, longitude, latitude) + + if (grondsoortCode === null) { + throw new Error( + `No grondsoorten value for coordinates ${longitude}, ${latitude} (outside coverage or NoData)`, + ) + } - switch (grondoortCode) { + switch (grondsoortCode) { case 1: { return "klei" } case 2: { return "loess" } case 3: { return "veen" } case 4: { return "zand_nwc" } case 5: { return "zand_zuid" } default: { throw new Error( - `Unknown region code: ${grondoortCode} for coordinates , `, + `Unknown region code: ${grondsoortCode} for coordinates ${longitude}, ${latitude}`, ) } }Also confirm that:
- norms/nl/2024/grondsoorten.tiff is the correct authoritative source for 2025, or update to a 2025 path/file if available.
251-294: Unify previous-year checks with a single YEAR constantMultiple branches hardcode 2024. Consider using a single YEAR (2025) and derive PREV_YEAR to avoid drift and ease future maintenance.
Example:
const YEAR = 2025 const PREV_YEAR = YEAR - 1 // then replace <= 2024 with <= PREV_YEARBased on learnings.
312-318: Volgteelt logic is still TODOIssue #276 mentions selecting "1e teelt" vs "volgteelt". You only implemented "1e teelt". Track or implement "volgteelt" selection (e.g., any additional same-year bladgewassen after hoofdteelt).
I can propose a minimal, testable volgteelt heuristic based on sow dates and hoofdteelt order if desired.
576-582: Selecting the most specific standard is fragileCurrent heuristic picks the first standard that happens to have any sub_types with omschrijving or varieties. This may not correlate with the actual sub-type/variety for the cultivation.
Refactor to evaluate each candidate:
- For each ns in matchingStandards, compute subTypeOmschrijving via determineSubTypeOmschrijving(cultivation, ns, is_derogatie_bedrijf, cultivations).
- Pass that to getNormsForCultivation(ns, b_lu_end, subTypeOmschrijving).
- Choose the first with a defined NormsByRegion; otherwise fall back.
This avoids picking the wrong standard when multiple entries exist.
630-634: Keep source and korting separate (optional API polish)Appending kortingDescription to normSource mixes concerns. Consider returning kortingDescription as a separate field for clearer UI rendering and i18n.
If changing API isn’t feasible now, keep as is.
fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx (1)
36-45: Avoid duplicating FieldNorm typeThis duplicates the FieldNorm shape from components/blocks/norms/field-norms.tsx. Prefer importing the type to keep props in sync and reduce drift.
For example:
- export type FieldNorm from the component module and import it here.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (22)
.changeset/crazy-onions-poke.md(1 hunks).changeset/every-shoes-boil.md(1 hunks).changeset/kind-singers-arrive.md(1 hunks).changeset/light-rockets-fly.md(1 hunks).changeset/seven-crabs-cross.md(1 hunks).changeset/tame-cars-kneel.md(1 hunks)fdm-app/app/components/blocks/norms/farm-norms.tsx(1 hunks)fdm-app/app/components/blocks/norms/field-norms.tsx(2 hunks)fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx(8 hunks)fdm-calculator/src/balance/nitrogen/index.test.ts(0 hunks)fdm-calculator/src/balance/nitrogen/index.ts(1 hunks)fdm-calculator/src/balance/nitrogen/supply/deposition.test.ts(1 hunks)fdm-calculator/src/balance/nitrogen/supply/deposition.ts(3 hunks)fdm-calculator/src/norms/nl/2025/dierlijke-mest-gebruiksnorm.test.ts(2 hunks)fdm-calculator/src/norms/nl/2025/dierlijke-mest-gebruiksnorm.ts(3 hunks)fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm-data.ts(3 hunks)fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.test.ts(2 hunks)fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts(7 hunks)fdm-calculator/src/norms/nl/2025/types.d.ts(1 hunks)fdm-calculator/src/shared/geotiff.ts(1 hunks)fdm-calculator/src/shared/public-data-url.ts(1 hunks)fdm-calculator/tsconfig.json(1 hunks)
💤 Files with no reviewable changes (1)
- fdm-calculator/src/balance/nitrogen/index.test.ts
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2025-09-23T10:02:32.123Z
Learnt from: BoraIneviNMI
PR: SvenVw/fdm#272
File: fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fertilizers.$b_lu_catalogue.manage.$p_id.tsx:151-164
Timestamp: 2025-09-23T10:02:32.123Z
Learning: The getFertilizer function from svenvw/fdm-core throws an exception if the fertilizer doesn't exist, rather than returning null or undefined.
Applied to files:
.changeset/light-rockets-fly.md.changeset/every-shoes-boil.md
📚 Learning: 2025-08-13T10:33:05.313Z
Learnt from: SvenVw
PR: SvenVw/fdm#0
File: :0-0
Timestamp: 2025-08-13T10:33:05.313Z
Learning: In the fdm project, fdm-calculator integration for new features like b_lu_variety is handled in separate updates from the core data model changes. When fdm-core functions are updated to support new fields, fdm-calculator can consume these enhanced APIs without requiring changes in the same PR that introduces the core functionality.
Applied to files:
.changeset/tame-cars-kneel.md.changeset/seven-crabs-cross.md
📚 Learning: 2025-07-21T12:06:07.070Z
Learnt from: SvenVw
PR: SvenVw/fdm#156
File: fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts:295-303
Timestamp: 2025-07-21T12:06:07.070Z
Learning: Functions in the fdm-calculator with "NL2025" in their names are specifically designed for Netherlands 2025 agricultural norms calculation and hardcoded 2025 dates are appropriate in this context, as different years would have separate calculation modules.
Applied to files:
.changeset/tame-cars-kneel.md.changeset/seven-crabs-cross.mdfdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.test.tsfdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts
📚 Learning: 2024-12-11T12:09:35.540Z
Learnt from: SvenVw
PR: SvenVw/fdm#20
File: fdm-app/tsconfig.json:8-9
Timestamp: 2024-12-11T12:09:35.540Z
Learning: In the `fdm-app/tsconfig.json` file, the include path `.react-router/types/**/*` refers to a build-time generated directory which is intentionally not included in the repository.
Applied to files:
fdm-calculator/tsconfig.json
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
PR: SvenVw/fdm#42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A shared layout component `FarmLayoutBase` has been created in `components/custom/farm-layout-base.tsx` to maintain consistency across farm-related pages. The component handles farm selection dropdown, breadcrumb navigation, and provides a common layout structure.
Applied to files:
fdm-app/app/components/blocks/norms/farm-norms.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
PR: SvenVw/fdm#42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: The `FarmLayout` component in `components/custom/farm-layout.tsx` provides a reusable layout structure for farm-related pages, with support for farm selection dropdown, customizable breadcrumb titles, and flexible content rendering through either children or Outlet components.
Applied to files:
fdm-app/app/components/blocks/norms/farm-norms.tsx
📚 Learning: 2025-09-23T12:37:58.700Z
Learnt from: SvenVw
PR: SvenVw/fdm#274
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx:113-148
Timestamp: 2025-09-23T12:37:58.700Z
Learning: In the FDM application, the current field data fetching implementation using Promise.all with individual API calls (getCultivations, getFertilizerApplications, getCurrentSoilData) performs acceptably even with farms containing 90+ fields. No performance issues have been observed in practice with this approach.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx
📚 Learning: 2025-08-14T14:31:55.384Z
Learnt from: SvenVw
PR: SvenVw/fdm#236
File: fdm-calculator/src/balance/nitrogen/index.ts:173-0
Timestamp: 2025-08-14T14:31:55.384Z
Learning: In nitrogen balance calculations for agricultural systems, the balance should only include ammonia emissions (emission.ammonia.total) and should not include nitrate leaching from the emission calculation. The nitrate component (emission.nitrate) should be excluded from the balance formula.
Applied to files:
fdm-calculator/src/balance/nitrogen/index.ts
🧬 Code graph analysis (6)
fdm-calculator/src/balance/nitrogen/supply/deposition.ts (1)
fdm-calculator/src/shared/geotiff.ts (1)
getGeoTiffValue(56-102)
fdm-calculator/src/norms/nl/2025/dierlijke-mest-gebruiksnorm.test.ts (1)
fdm-calculator/src/norms/nl/2025/dierlijke-mest-gebruiksnorm.ts (1)
isFieldInDerogatieVrijeZone(89-115)
fdm-calculator/src/norms/nl/2025/dierlijke-mest-gebruiksnorm.ts (3)
fdm-calculator/src/shared/public-data-url.ts (1)
getFdmPublicDataUrl(1-3)fdm-calculator/src/shared/geotiff.ts (1)
getGeoTiffValue(56-102)fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts (1)
isFieldInNVGebied(27-49)
fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.test.ts (2)
fdm-calculator/src/norms/nl/2025/types.d.ts (2)
NL2025NormsInput(14-25)NL2025NormsInputForCultivation(6-9)fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts (1)
getNL2025StikstofGebruiksNorm(525-635)
fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts (5)
fdm-calculator/src/shared/public-data-url.ts (1)
getFdmPublicDataUrl(1-3)fdm-calculator/src/shared/geotiff.ts (1)
getGeoTiffValue(56-102)fdm-calculator/src/norms/nl/2025/types.d.ts (4)
RegionKey(146-146)NormsByRegion(151-153)NL2025NormsInputForCultivation(6-9)NitrogenStandard(80-141)fdm-calculator/src/norms/nl/2025/hoofdteelt.ts (1)
determineNL2025Hoofdteelt(19-73)fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm-data.ts (1)
nitrogenStandardsData(1-2625)
fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx (5)
fdm-app/app/components/blocks/norms/field-norms.tsx (1)
FieldNorm(15-24)fdm-calculator/src/norms/nl/2025/types.d.ts (1)
GebruiksnormResult(159-169)fdm-calculator/src/norms/farm.ts (1)
AggregatedNormsToFarmLevel(31-44)fdm-calculator/src/norms/index.ts (1)
createFunctionsForNorms(7-21)fdm-app/app/components/blocks/norms/farm-norms.tsx (1)
FarmNorms(20-93)
🪛 Biome (2.1.2)
fdm-app/app/components/blocks/norms/farm-norms.tsx
[error] 41-41: Avoid using the index of an array as key property in an element.
This is the source of the key value.
The order of the items may change, and this also affects performances and component state.
Check the React documentation.
(lint/suspicious/noArrayIndexKey)
fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts
[error] 153-153: This variable implicitly has the any type.
Variable declarations without type annotation and initialization implicitly have the any type. Declare a type or initialize the variable with some value.
(lint/suspicious/noImplicitAnyLet)
🔇 Additional comments (22)
fdm-calculator/src/balance/nitrogen/supply/deposition.ts (1)
50-51: Confirm GeoTIFF CRS matches lon/lat. Ensurentot_2022.tiffis in EPSG:4326 (usegdalinfoorimage.getGeoKeys()); if it’s in a projected CRS (e.g. EPSG:28992), reprojectb_centroidto that CRS or serve a WGS84‐aligned TIFF.fdm-calculator/tsconfig.json (1)
22-22: No local non-relative imports in fdm-calculator — baseUrl is safe hereScanned fdm-calculator/src: imports are external/scoped packages (e.g. @svenvw/fdm-core, decimal.js); no bare non-relative local imports that would require TS baseUrl at runtime were found. If you later introduce non-relative internal imports, ensure your bundler or a runtime resolver (tsconfig-paths / plugin) rewrites them in the build output.
.changeset/seven-crabs-cross.md (1)
2-5: LGTM on changeset entryMinor bump and concise description look good.
.changeset/light-rockets-fly.md (1)
2-5: LGTM on changeset entryPatch bump and message are clear.
fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.test.ts (2)
80-82: Verify label consistency: “Akkerbouwgewas” vs “Akkerbouwgewassen”Ensure the source string for pootaardappelen matches the dataset’s
cultivation_rvo_table2naming convention used elsewhere (most tests expect “Akkerbouwgewassen, …”).
291-568: Add tests for rapeseed winter/summer and bulb variety sub-types; postpone spinach/lettuce volgteelt
- Rapeseed (nl_1922 vs nl_1923): verify “winter” vs “zomer” sub-type in normSource and normValue.
- Flower bulbs: verify “grofbollig” vs “fijnbollig” variety-based omschrijving.
- Spinach/Lettuce volgteelt: logic still TODO—implement before testing.
fdm-calculator/src/norms/nl/2025/dierlijke-mest-gebruiksnorm.test.ts (1)
2-5: Public API import looks goodExposing and testing isFieldInDerogatieVrijeZone is consistent with the calculator module changes.
fdm-app/app/components/blocks/norms/field-norms.tsx (1)
15-24: Exported FieldNorm shape looks goodOptional norms and errorMessage enable the new UI states without breaking existing usage.
fdm-calculator/src/balance/nitrogen/supply/deposition.test.ts (1)
4-4: All imports of getFdmPublicDataUrl updated
No remaining imports reference a non-shared path; every usage now imports fromshared/public-data-url.fdm-calculator/src/balance/nitrogen/index.ts (2)
24-24: Good move: centralized public data URL.Importing getFdmPublicDataUrl avoids hardcoded URLs and keeps sources consistent.
208-214: Balance uses ammonia only (correct).Summing emission.ammonia.total (excluding nitrate) matches the intended balance definition.
Based on learnings
Also applies to: 268-269
fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm-data.ts (3)
186-223: Potato (consumption) refactor to sub_types looks solid.Consolidating high/low/overig under sub_types with varieties lists improves lookup clarity. Please ensure variety names are deduplicated and consistently cased to avoid ambiguous matches.
246-283: Potato (seed) sub_types structure LGTM.Same note on de-duplication and consistent casing in varieties.
473-494: Mais derogatie/non-derogatie split is clear. Verify caller sub-type selection.New sub_types require the caller to pass a matching omschrijving. Confirm getNormsForCultivation and getNL2025StikstofGebruiksNorm set subTypeOmschrijving accordingly (per PR objective).
Would you like a small helper that picks sub_types by omschrijving with a tolerant comparator (case/diacritics)?
fdm-calculator/src/norms/nl/2025/dierlijke-mest-gebruiksnorm.ts (2)
153-163: Parallel checks via Promise.all are appropriate.Good use of concurrency and shared TIFF caching to keep latency down.
168-178: Logic addition for derogatie-vrije zone is in the right precedence.Applying 170 cap before NV aligns with the rule intent.
fdm-calculator/src/shared/geotiff.ts (1)
21-46: Solid, deduplicated TIFF fetch with in-memory cache.Prevents duplicate downloads and reduces CPU; error cleanup is correct.
fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx (3)
202-216: Farm aggregation guard looks goodFiltering to validFieldNorms before aggregation prevents runtime errors when some fields fail. Nice.
285-292: Async data destructuring is correctuse(loaderData.asyncData) shape matches the loader return, including new error aggregation fields.
358-362: FarmNorms receives new error-state propsProp wiring for hasFieldNormErrors and fieldErrorMessages aligns with the new UI behavior.
fdm-calculator/src/norms/nl/2025/types.d.ts (2)
104-111: Top-level norms still present; confirm data alignmentThe NitrogenStandard interface still exposes optional top-level norms. Ensure the data file matches this shape for standards without sub_types so getNormsForCultivation’s default path remains valid.
118-140: Varieties on sub_types is a good fit for potato handlingAdding varieties?: string[] under sub_types matches the new selection logic using determineSubTypeOmschrijving.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (4)
fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts (3)
233-294: Factor out a single norm-year constant.All the “previous year” lookups now compare against a hard-coded
2024. That literal shows up in multiple branches (lucerne, grass for industrial processing, graszaad, roodzwenkgras) and will be easy to miss next time we roll the module forward. Please extract a shared constant (e.g.const NORM_YEAR = 2025) at the top of the file and derive thepreviousYearfrom it so future updates stay consistent.
44-46: Include coordinates in the error message.If the GeoTIFF returns an unexpected code (including
null), the thrown error currently renders asUnknown NVGebied code: … for coordinates ,. Please interpolate the longitude & latitude so we can investigate bad tiles or out-of-bounds centroids more easily.
96-97: Add coordinates to the region error as well.Same issue here—the fallback error omits the centroid values, which makes debugging harder. Please include both numbers in the message.
fdm-calculator/src/balance/nitrogen/supply/deposition.ts (1)
45-61: Short-circuit GeoTIFF fetch when fraction is zero.When the effective timeframe doesn’t overlap the balance period,
fractionis zero but we still read the raster. We can return immediately and avoid an unnecessary GeoTIFF lookup for those fields.const days = differenceInCalendarDays(effectiveEnd, effectiveStart) const fraction = days >= 0 ? new Decimal(days).add(1).dividedBy(365) : new Decimal(0) - // Get the deposition value from the GeoTIFF using the new getTiffValue function. + if (fraction.isZero()) { + return { + fieldId: field.field.b_id, + deposition: { total: new Decimal(0) }, + } + } + + // Get the deposition value from the GeoTIFF using the new getGeoTiffValue function. const [longitude, latitude] = field.field.b_centroid const value = await getGeoTiffValue(url, longitude, latitude)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
fdm-app/app/components/blocks/norms/farm-norms.tsx(1 hunks)fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx(9 hunks)fdm-calculator/src/balance/nitrogen/supply/deposition.ts(3 hunks)fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts(7 hunks)
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: SvenVw
PR: SvenVw/fdm#156
File: fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts:295-303
Timestamp: 2025-07-21T12:06:07.070Z
Learning: Functions in the fdm-calculator with "NL2025" in their names are specifically designed for Netherlands 2025 agricultural norms calculation and hardcoded 2025 dates are appropriate in this context, as different years would have separate calculation modules.
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
PR: SvenVw/fdm#42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A shared layout component `FarmLayoutBase` has been created in `components/custom/farm-layout-base.tsx` to maintain consistency across farm-related pages. The component handles farm selection dropdown, breadcrumb navigation, and provides a common layout structure.
Applied to files:
fdm-app/app/components/blocks/norms/farm-norms.tsx
📚 Learning: 2025-08-11T12:24:32.200Z
Learnt from: SvenVw
PR: SvenVw/fdm#233
File: fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx:53-53
Timestamp: 2025-08-11T12:24:32.200Z
Learning: In `fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx`, the NMI API for cultivations guarantees that each year will be unique in the cultivation history data, so using `cultivation.year` as a React list key is safe and won't cause duplicate key warnings.
Applied to files:
fdm-app/app/components/blocks/norms/farm-norms.tsxfdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
PR: SvenVw/fdm#42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: The `FarmLayout` component in `components/custom/farm-layout.tsx` provides a reusable layout structure for farm-related pages, with support for farm selection dropdown, customizable breadcrumb titles, and flexible content rendering through either children or Outlet components.
Applied to files:
fdm-app/app/components/blocks/norms/farm-norms.tsx
📚 Learning: 2025-09-23T12:37:58.700Z
Learnt from: SvenVw
PR: SvenVw/fdm#274
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx:113-148
Timestamp: 2025-09-23T12:37:58.700Z
Learning: In the FDM application, the current field data fetching implementation using Promise.all with individual API calls (getCultivations, getFertilizerApplications, getCurrentSoilData) performs acceptably even with farms containing 90+ fields. No performance issues have been observed in practice with this approach.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx
📚 Learning: 2025-07-21T12:06:07.070Z
Learnt from: SvenVw
PR: SvenVw/fdm#156
File: fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts:295-303
Timestamp: 2025-07-21T12:06:07.070Z
Learning: Functions in the fdm-calculator with "NL2025" in their names are specifically designed for Netherlands 2025 agricultural norms calculation and hardcoded 2025 dates are appropriate in this context, as different years would have separate calculation modules.
Applied to files:
fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts
📚 Learning: 2025-08-11T11:55:26.053Z
Learnt from: SvenVw
PR: SvenVw/fdm#233
File: fdm-app/app/integrations/nmi.ts:54-0
Timestamp: 2025-08-11T11:55:26.053Z
Learning: The NMI API Estimates endpoint (`https://api.nmi-agro.nl/estimates`) always returns the fields `b_gwl_ghg`, `b_gwl_glg`, and `cultivations` according to its specification. These fields should be kept as required (not optional) in the TypeScript return type and Zod validation schema in `fdm-app/app/integrations/nmi.ts`.
Applied to files:
fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts
🧬 Code graph analysis (3)
fdm-calculator/src/balance/nitrogen/supply/deposition.ts (1)
fdm-calculator/src/shared/geotiff.ts (1)
getGeoTiffValue(56-102)
fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx (6)
fdm-app/app/components/blocks/norms/field-norms.tsx (1)
FieldNorm(15-24)fdm-calculator/src/norms/nl/2025/types.d.ts (1)
GebruiksnormResult(159-169)fdm-calculator/src/index.ts (3)
GebruiksnormResult(36-36)AggregatedNormsToFarmLevel(26-26)createFunctionsForNorms(24-24)fdm-calculator/src/norms/farm.ts (1)
AggregatedNormsToFarmLevel(31-44)fdm-calculator/src/norms/index.ts (1)
createFunctionsForNorms(7-21)fdm-app/app/components/blocks/norms/farm-norms.tsx (1)
FarmNorms(20-102)
fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts (4)
fdm-calculator/src/shared/public-data-url.ts (1)
getFdmPublicDataUrl(1-3)fdm-calculator/src/shared/geotiff.ts (1)
getGeoTiffValue(56-102)fdm-calculator/src/norms/nl/2025/types.d.ts (3)
RegionKey(146-146)NormsByRegion(151-153)NL2025NormsInputForCultivation(6-9)fdm-calculator/src/norms/nl/2025/hoofdteelt.ts (1)
determineNL2025Hoofdteelt(19-73)
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx (1)
36-45: Reuse the sharedFieldNormtype instead of duplicating it.We already export this interface from
field-norms.tsx; redefining it here increases the chance the shapes drift apart (for example if a new property is added later). Please import the shared type and drop this duplicate declaration.fdm-calculator/src/norms/nl/2025/dierlijke-mest-gebruiksnorm.ts (3)
24-26: GWBG: verify dataset year (2024 vs 2025) and handlenullexplicitly
- Year: This NL2025 module references norms/nl/2024/gwbg.tiff. If that’s intentional (no 2025 update), add a short comment. If a 2025 raster exists, switch to it. Based on learnings.
- Nulls: getGeoTiffValue can return null (OOB/NoData). Handling it explicitly yields clearer errors.
- const url = `${fdmPublicDataUrl}norms/nl/2024/gwbg.tiff` + const url = `${fdmPublicDataUrl}norms/nl/2024/gwbg.tiff` // NOTE: keep if 2024 is authoritative for 2025; otherwise switch to 2025. - switch (gwbgCode) { + switch (gwbgCode) { case 1: { return true } case 0: { return false } + case null: { + throw new Error( + `GWBG NoData/outside raster extent for coordinates ${longitude}, ${latitude}`, + ) + } default: { throw new Error( `Unknown GWBG code: ${gwbgCode} for coordinates ${longitude}, ${latitude}`, ) } }Also applies to: 30-41
59-61: Natura2000: same as GWBG — confirm 2024 raster and addnullhandlingMirror the above: document 2024 usage (or update to 2025) and treat null explicitly.
- const url = `${fdmPublicDataUrl}norms/nl/2024/natura2000.tiff` + const url = `${fdmPublicDataUrl}norms/nl/2024/natura2000.tiff` // NOTE: confirm year. - switch (natura2000Code) { + switch (natura2000Code) { case 1: { return true } case 0: { return false } + case null: { + throw new Error( + `Natura2000 NoData/outside raster extent for coordinates ${longitude}, ${latitude}`, + ) + } default: { throw new Error( `Unknown Natura2000 code: ${natura2000Code} for coordinates ${longitude}, ${latitude}`, ) } }Also applies to: 65-78
155-165: Good parallelization; decide on fail-fast vs per-layer error collectionPromise.all fails on the first rejection. If the UI should show per-layer errors while still proceeding with available signals, consider Promise.allSettled with explicit aggregation. If fail-fast is desired for data integrity, keep as-is.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
.changeset/crazy-onions-poke.md(1 hunks).changeset/every-shoes-boil.md(1 hunks).changeset/kind-singers-arrive.md(1 hunks).changeset/tame-cars-kneel.md(1 hunks)fdm-app/app/components/blocks/norms/farm-norms.tsx(1 hunks)fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx(9 hunks)fdm-calculator/src/norms/nl/2025/dierlijke-mest-gebruiksnorm.ts(3 hunks)fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts(7 hunks)
✅ Files skipped from review due to trivial changes (1)
- .changeset/kind-singers-arrive.md
🚧 Files skipped from review as they are similar to previous changes (2)
- .changeset/crazy-onions-poke.md
- .changeset/every-shoes-boil.md
🧰 Additional context used
🧠 Learnings (9)
📚 Learning: 2025-08-13T10:33:05.313Z
Learnt from: SvenVw
PR: SvenVw/fdm#0
File: :0-0
Timestamp: 2025-08-13T10:33:05.313Z
Learning: In the fdm project, fdm-calculator integration for new features like b_lu_variety is handled in separate updates from the core data model changes. When fdm-core functions are updated to support new fields, fdm-calculator can consume these enhanced APIs without requiring changes in the same PR that introduces the core functionality.
Applied to files:
.changeset/tame-cars-kneel.md
📚 Learning: 2025-07-21T12:06:07.070Z
Learnt from: SvenVw
PR: SvenVw/fdm#156
File: fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts:295-303
Timestamp: 2025-07-21T12:06:07.070Z
Learning: Functions in the fdm-calculator with "NL2025" in their names are specifically designed for Netherlands 2025 agricultural norms calculation and hardcoded 2025 dates are appropriate in this context, as different years would have separate calculation modules.
Applied to files:
.changeset/tame-cars-kneel.mdfdm-calculator/src/norms/nl/2025/dierlijke-mest-gebruiksnorm.tsfdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts
📚 Learning: 2025-09-23T12:37:58.700Z
Learnt from: SvenVw
PR: SvenVw/fdm#274
File: fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx:113-148
Timestamp: 2025-09-23T12:37:58.700Z
Learning: In the FDM application, the current field data fetching implementation using Promise.all with individual API calls (getCultivations, getFertilizerApplications, getCurrentSoilData) performs acceptably even with farms containing 90+ fields. No performance issues have been observed in practice with this approach.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx
📚 Learning: 2025-01-31T15:05:14.310Z
Learnt from: SvenVw
PR: SvenVw/fdm#67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:05:14.310Z
Learning: The `updateField` function in fdm-core has optional parameters after `fdm` and `b_id`. The TypeScript definitions might show 8 required parameters due to a potential version mismatch.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx
📚 Learning: 2025-01-31T15:34:20.850Z
Learnt from: SvenVw
PR: SvenVw/fdm#67
File: fdm-app/app/routes/farm.create.$b_id_farm.fields.$b_id.tsx:601-610
Timestamp: 2025-01-31T15:34:20.850Z
Learning: The `updateField` function in fdm-core has optional parameters that don't need to be passed as undefined. Only `fdm` and `b_id` are required.
Applied to files:
fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx
📚 Learning: 2025-08-11T11:55:26.053Z
Learnt from: SvenVw
PR: SvenVw/fdm#233
File: fdm-app/app/integrations/nmi.ts:54-0
Timestamp: 2025-08-11T11:55:26.053Z
Learning: The NMI API Estimates endpoint (`https://api.nmi-agro.nl/estimates`) always returns the fields `b_gwl_ghg`, `b_gwl_glg`, and `cultivations` according to its specification. These fields should be kept as required (not optional) in the TypeScript return type and Zod validation schema in `fdm-app/app/integrations/nmi.ts`.
Applied to files:
fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts
📚 Learning: 2025-08-11T12:24:32.200Z
Learnt from: SvenVw
PR: SvenVw/fdm#233
File: fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx:53-53
Timestamp: 2025-08-11T12:24:32.200Z
Learning: In `fdm-app/app/components/blocks/atlas-fields/cultivation-history.tsx`, the NMI API for cultivations guarantees that each year will be unique in the cultivation history data, so using `cultivation.year` as a React list key is safe and won't cause duplicate key warnings.
Applied to files:
fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.tsfdm-app/app/components/blocks/norms/farm-norms.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
PR: SvenVw/fdm#42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: A shared layout component `FarmLayoutBase` has been created in `components/custom/farm-layout-base.tsx` to maintain consistency across farm-related pages. The component handles farm selection dropdown, breadcrumb navigation, and provides a common layout structure.
Applied to files:
fdm-app/app/components/blocks/norms/farm-norms.tsx
📚 Learning: 2025-01-09T16:03:37.764Z
Learnt from: SvenVw
PR: SvenVw/fdm#42
File: fdm-app/app/routes/farm/_b_id_farm/layout.tsx:46-95
Timestamp: 2025-01-09T16:03:37.764Z
Learning: The `FarmLayout` component in `components/custom/farm-layout.tsx` provides a reusable layout structure for farm-related pages, with support for farm selection dropdown, customizable breadcrumb titles, and flexible content rendering through either children or Outlet components.
Applied to files:
fdm-app/app/components/blocks/norms/farm-norms.tsx
🧬 Code graph analysis (3)
fdm-calculator/src/norms/nl/2025/dierlijke-mest-gebruiksnorm.ts (3)
fdm-calculator/src/shared/public-data-url.ts (1)
getFdmPublicDataUrl(1-3)fdm-calculator/src/shared/geotiff.ts (1)
getGeoTiffValue(56-102)fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts (1)
isFieldInNVGebied(27-49)
fdm-app/app/routes/farm.$b_id_farm.$calendar.norms.tsx (5)
fdm-app/app/components/blocks/norms/field-norms.tsx (1)
FieldNorm(15-24)fdm-calculator/src/norms/nl/2025/types.d.ts (1)
GebruiksnormResult(159-169)fdm-calculator/src/norms/farm.ts (1)
AggregatedNormsToFarmLevel(31-44)fdm-calculator/src/norms/index.ts (1)
createFunctionsForNorms(7-21)fdm-app/app/components/blocks/norms/farm-norms.tsx (1)
FarmNorms(20-102)
fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm.ts (5)
fdm-calculator/src/shared/public-data-url.ts (1)
getFdmPublicDataUrl(1-3)fdm-calculator/src/shared/geotiff.ts (1)
getGeoTiffValue(56-102)fdm-calculator/src/norms/nl/2025/types.d.ts (4)
RegionKey(146-146)NormsByRegion(151-153)NitrogenStandard(80-141)NL2025NormsInputForCultivation(6-9)fdm-calculator/src/norms/nl/2025/hoofdteelt.ts (1)
determineNL2025Hoofdteelt(19-73)fdm-calculator/src/norms/nl/2025/stikstofgebruiksnorm-data.ts (1)
nitrogenStandardsData(1-2625)
🔇 Additional comments (3)
fdm-calculator/src/norms/nl/2025/dierlijke-mest-gebruiksnorm.ts (3)
7-8: Centralized helpers — LGTMMoving to shared GeoTIFF + public-data URL utilities is the right direction.
12-20: Clarify CRS assumption in docs (WGS84 vs RD/EPSG:28992)These lookups assume the centroid coordinates are in the same CRS as the GeoTIFFs. Please confirm TIFFs are EPSG:4326 (lon/lat). If they’re RD New (EPSG:28992) you’ll need a transform before sampling.
177-179: Precedence includes Derogatie‑vrije zone before NV — looks rightThis ordering matches typical rule priority. Please confirm with the policy source for 2025 to avoid a precedence regression.
Summary by CodeRabbit
New Features
Bug Fixes
Refactor
Tests
Chore
Closes #276