Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision | |||
|
calculate_functors [2026/07/29 13:34] hermann |
calculate_functors [2026/07/29 13:42] (current) hermann |
||
|---|---|---|---|
| Line 529: | Line 529: | ||
| If the computed real value exceeds the range of the chosen Cell Type, that cell is also written as null rather than wrapping or clipping. This range check applies to [[Calculate Map]] and [[Calculate Categorical Map]] only — the only two functors that accept a Cell Type parameter and write to a typed cell grid. | If the computed real value exceeds the range of the chosen Cell Type, that cell is also written as null rather than wrapping or clipping. This range check applies to [[Calculate Map]] and [[Calculate Categorical Map]] only — the only two functors that accept a Cell Type parameter and write to a typed cell grid. | ||
| - | ===== Guarding a conditional's test against null ===== | + | ===== When a conditional's test needs guarding against null ===== |
| - | A conditional whose test doesn't explicitly check for null still behaves correctly as long as the same operand being tested is also what gets returned. ''if i1 = 2 then 10 else i1'' is internally equivalent to ''if isNull(i1) then null else if i1 = 2 then 10 else i1'' — when ''i1'' is null, the whole expression is null, which is exactly the desired behaviour when reclassifying a single map in place. | + | A conditional's test poisoning the whole result is only a problem when the tested operand's nullness shouldn't determine whether the entire expression is invalid. Reclassifying ''i1'' in place — ''if i1 = 2 then 10 else i1'' — needs no explicit guard, since a null ''i1'' making the reclassified result null for that cell is exactly the desired behaviour: the expression is internally equivalent to ''if isNull(i1) then null else if i1 = 2 then 10 else i1''. |
| - | The same pattern breaks when the null being tested belongs to a //different// operand than the one being preserved. Combining a landscape map (''i1'') with a road map (''i2'', where 1 marks a road and null marks everything else) might suggest ''if i2 = 1 then 1 else i1'' — but this is internally equivalent to ''if isNull(i2) then null else if i2 = 1 then 1 else i1''. Since most cells are null in a sparse road map, most of the output becomes null too, discarding ''i1'''s landscape values everywhere a road isn't present. | + | The same mechanism causes a real bug when the operand being tested isn't the one the null-ness should actually be judged against. Combining a landscape map (''i1'') with a road map (''i2'', where 1 marks a road and null marks everything else) might suggest ''if i2 = 1 then 1 else i1'' — but this is internally equivalent to ''if isNull(i2) then null else if i2 = 1 then 1 else i1''. Since most cells are null in a sparse road map, most of the output becomes null too, discarding ''i1'''s landscape values everywhere a road isn't present — even though ''i2'' being null says nothing about whether ''i1'''s value is valid. |
| Guarding the test with ''isNull'' fixes it: ''if not isNull(i2) and i2 = 1 then 1 else i1'' only tests ''i2'' once it's confirmed non-null, so a null road cell falls through to the landscape value in ''i1'' instead of nulling the result. | Guarding the test with ''isNull'' fixes it: ''if not isNull(i2) and i2 = 1 then 1 else i1'' only tests ''i2'' once it's confirmed non-null, so a null road cell falls through to the landscape value in ''i1'' instead of nulling the result. | ||