Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
calculate_functors [2026/07/03 05:12]
hermann
calculate_functors [2026/07/23 03:55] (current)
hermann
Line 35: Line 35:
 | Null Value | Null Value Type | No | Based on Cell Type | Sentinel value representing "no data" in the output. | | Null Value | Null Value Type | No | Based on Cell Type | Sentinel value representing "no data" in the output. |
 | Result Is Sparse | Boolean Value Type | No | False | If ''​True'',​ only non-null cells are stored — saves memory but slows random access. | | Result Is Sparse | Boolean Value Type | No | False | If ''​True'',​ only non-null cells are stored — saves memory but slows random access. |
-| Result Format | Map Type | No | None | Optional reference map whose spatial format (extent, resolution, projection) is applied to the output. Category information in the reference map is ignored. |+| Result Format | Map Type | No | None | Optional reference map whose spatial format (extent, resolution, projection) is applied to the output. Category information in the reference map is ignored. Prefer this port over a ''​NumberMap''​ hook when the map is needed only for format purposes — a hook defines an identifier in the expression namespace and blocks abbreviated syntax if that identifier is unused in the expression. |
  
 ==== Output ==== ==== Output ====
Line 61: Line 61:
 | Null Value | Null Value Type | No | Based on Cell Type | Sentinel value representing "no data" in the output. | | Null Value | Null Value Type | No | Based on Cell Type | Sentinel value representing "no data" in the output. |
 | Result Is Sparse | Boolean Value Type | No | False | If ''​True'',​ only non-null cells are stored. | | Result Is Sparse | Boolean Value Type | No | False | If ''​True'',​ only non-null cells are stored. |
-| Result Format | Map Type | No | None | Optional reference map whose spatial format is applied to the output. |+| Result Format | Map Type | No | None | Optional reference map whose spatial format is applied to the output. Prefer this port over a ''​NumberMap''​ hook when the map is needed only for format purposes — a hook defines an identifier in the expression namespace and blocks abbreviated syntax if that identifier is unused in the expression. |
  
 ==== Output ==== ==== Output ====
Line 110: Line 110:
 | Key Column Name | String | **Yes** | — | Name of the key column in the output lookup table. | | Key Column Name | String | **Yes** | — | Name of the key column in the output lookup table. |
 | Value Column Name | String | **Yes** | — | Name of the value column in the output lookup table. | | Value Column Name | String | **Yes** | — | Name of the value column in the output lookup table. |
-| Base Lookup Table | Lookup Table Type | No | ''​.none''​ | A lookup table whose key set drives iteration. When ''​.none'',​ the lowest-indexed connected ​''​NumberTable''​ (verbose form) or the first lookup table named in the expression (shorthand form) is used instead ​— not a union of all connected tables. |+| Base Lookup Table | Lookup Table Type | No | ''​.none''​ | A lookup table whose key set drives iteration. When ''​.none'',​ the ''​NumberTable'' ​hook with the lowest list index that is connected to a **lookup table** (not a regular table) is used as the row source ​(verbose form)or the first lookup table named in the expression (shorthand form) — not a union of all connected tables. Prefer this port over a ''​NumberTable''​ hook when the table is needed only to define the row set and is not referenced in the expression, to keep abbreviated syntax available. |
  
 ==== Output ==== ==== Output ====
Line 140: Line 140:
 | Key Column Name | String | **Yes** | — | Name of the key column in the output lookup table. | | Key Column Name | String | **Yes** | — | Name of the key column in the output lookup table. |
 | Value Column Name | String | **Yes** | — | Name of the value column in the output lookup table. | | Value Column Name | String | **Yes** | — | Name of the value column in the output lookup table. |
-| Base Lookup Table | Lookup Table Type | No | ''​.none''​ | A lookup table whose row set drives iteration. |+| Base Lookup Table | Lookup Table Type | No | ''​.none''​ | A lookup table whose row set drives iteration. Prefer this port over a ''​NumberTable''​ hook when the table is needed only to define the row set and is not referenced in the expression, to keep abbreviated syntax available. |
  
 ==== Output ==== ==== Output ====
Line 205: Line 205:
 | 6 | **Divide** | ''​EXPR / EXPR''​ |   | ''​i1 / i2''​ | | 6 | **Divide** | ''​EXPR / EXPR''​ |   | ''​i1 / i2''​ |
 | 6 | **Modulo** | ''​EXPR % EXPR''​ | Remainder after integer division | ''​i1 % 100''​ | | 6 | **Modulo** | ''​EXPR % EXPR''​ | Remainder after integer division | ''​i1 % 100''​ |
-| 7 | **Power** | ''​EXPR ^ EXPR''​ | Exponentiation | ''​i1 ^ 3''​ | +| 7 | **Power** | ''​EXPR ​<​nowiki>​^</​nowiki> ​EXPR''​ | Exponentiation | ''​i1 ​<​nowiki>​^</​nowiki> ​3''​ | 
-| 8 | **Catch Error (''?''​)** | ''​EXPR ? EXPR''​ | If the left expression produces an algebraic error (division by zero, key not found, etc.), return the right expression instead. | ''​(i1 / i2) ? (i1 - i2)''​ |+| 8 | **Catch Error (''?''​)** | ''​EXPR ? EXPR''​ | If the left expression produces an algebraic error (division by zero, key not found, etc.), ​evaluate and return the **fallback ​expression** instead. The fallback can be any expression, including another calculation,​ a conditional,​ or a nested ''?''​. | ''​(i1 / i2) ? (i1 - i2)''​ |
 | 9 | **Boolean Not** | ''​not EXPR''​ / ''​! EXPR''​ | Inverts a boolean value. | ''​not isNull(i1)''​ | | 9 | **Boolean Not** | ''​not EXPR''​ / ''​! EXPR''​ | Inverts a boolean value. | ''​not isNull(i1)''​ |
 | 9 | **Negate** | ''​-EXPR''​ | Arithmetic negation. | ''​-ceil(i1 + i2)''​ | | 9 | **Negate** | ''​-EXPR''​ | Arithmetic negation. | ''​-ceil(i1 + i2)''​ |
- 
-> **Note on null and equality:** Writing ''​i1 == null''​ does **not** test for null — because null participates in no arithmetic comparison, it always produces null itself, poisoning the expression. Always use ''​isNull(i1)''​ instead. See the [[#​null_value_handling|Null Value Handling]] section for full details. 
  
 ---- ----
Line 228: Line 226:
 | Asin | ''​asin(EXPR)''​ | Arcsine; output in radians | ''​asin(i1 + i2)''​ | | Asin | ''​asin(EXPR)''​ | Arcsine; output in radians | ''​asin(i1 + i2)''​ |
 | Atan | ''​atan(EXPR)''​ | Arctangent; output in radians | ''​atan(i1 + i2)''​ | | Atan | ''​atan(EXPR)''​ | Arctangent; output in radians | ''​atan(i1 + i2)''​ |
-| Exp | ''​exp(EXPR)''​ | Natural exponential (e^x) | ''​exp(i1 + i2)''​ |+| Exp | ''​exp(EXPR)''​ | Natural exponential (e<​nowiki>​^</​nowiki>​x) | ''​exp(i1 + i2)''​ |
 | Natural Logarithm | ''​ln(EXPR)''​ | log base e | ''​ln(i1 / i4)''​ | | Natural Logarithm | ''​ln(EXPR)''​ | log base e | ''​ln(i1 / i4)''​ |
 | Log base 10 | ''​log(EXPR)''​ |   | ''​log(i1 / i4)''​ | | Log base 10 | ''​log(EXPR)''​ |   | ''​log(i1 / i4)''​ |
Line 241: Line 239:
  
 ===== Clamping Functions ===== ===== Clamping Functions =====
 +
 +All clamping functions are available in all five calculator functors.
  
 ^ Function ^ Syntax ^ Description ^ Example ^ ^ Function ^ Syntax ^ Description ^ Example ^
Line 248: Line 248:
 ===== Random / Stochastic Functions ===== ===== Random / Stochastic Functions =====
  
-These functions generate a new random sample **for each evaluation unit** each time the expression is evaluated. In [[Calculate Map]] and [[Calculate Categorical Map]], this means one sample per cell, making them suitable for spatially stochastic models. In [[Calculate Lookup Table Values]] and [[Calculate Lookup Table Keys And Values]], one sample is drawn per table row. In [[Calculate Value]], a single sample is drawn for the whole expression.+All stochastic ​functions ​are available in all five calculator functors. They generate a new random sample **for each evaluation unit** each time the expression is evaluated. In [[Calculate Map]] and [[Calculate Categorical Map]], this means one sample per cell, making them suitable for spatially stochastic models. In [[Calculate Lookup Table Values]] and [[Calculate Lookup Table Keys And Values]], one sample is drawn per table row. In [[Calculate Value]], a single sample is drawn for the whole expression.
  
 ^ Function ^ Syntax ^ Description ^ Example ^ ^ Function ^ Syntax ^ Description ^ Example ^
Line 259: Line 259:
  
 ^ Function ^ Syntax ^ Description ^ Example ^ ^ Function ^ Syntax ^ Description ^ Example ^
-| Literal value | ''​2'',​ ''​-3.5e-2'',​ etc. | Any numeric literal, including scientific notation. | ''​2 + i1 / -3.5e-2''​ | +| Literal value | ''​2'',​ ''​-3.5e-2'',​ etc. | Any numeric literal, including scientific notation. Available in all five calculator functors. | ''​2 + i1 / -3.5e-2''​ | 
-| Abort | ''​abort''​ | Immediately halts model execution. Useful as a guard inside a conditional to catch unexpected states. | ''​if i1 > 0 then i1 * i2 + 4 else abort''​ |+| Abort | ''​abort''​ | Immediately halts model execution. Useful as a guard inside a conditional to catch unexpected states. Available in all five calculator functors. | ''​if i1 > 0 then i1 * i2 + 4 else abort''​ | 
 +| Is Null | ''​isNull(EXPR)''​ | Returns 1 if the expression is null, 0 otherwise. Immune to the null poisoning rule — always resolves to 0 or 1. Available in all five calculator functors. | ''​if not isNull(i1) then i1 else i2''​ | 
 +| Null | ''​null''​ | Makes the current evaluation unit null — the cell, row, or scalar result is marked as missing and any further arithmetic using it propagates null. This is an **abstract** null, not a numeric value; it cannot be used in arithmetic and has no numeric representation. Available in all five calculator functors. | ''​if i1 > 2 then i1 else null''​ | 
 + 
 +> **Note on null and equality:** Writing ''​i1 == null''​ does **not** test for null — because null participates in no arithmetic comparison, it always produces null itself, poisoning the expression. Always use ''​isNull(i1)''​ instead. See the [[#​null_value_handling|Null Value Handling]] section for full details.
  
 ---- ----
Line 281: Line 285:
 ^ Operator ^ Syntax ^ Description ^ Example ^ ^ Operator ^ Syntax ^ Description ^ Example ^
 | Get Image Value | ''​iX''​ | Returns the value of image X at the **current cell**. | ''​i2''​ | | Get Image Value | ''​iX''​ | Returns the value of image X at the **current cell**. | ''​i2''​ |
-| Get Image Value At Location | ''​iX[LINE_EXPR,​ COL_EXPR]''​ | Returns the value of image X at the **specified (line, column) coordinate**. If the target cell is null, **returns 0** (not null). | ''​i1[line - 1, column - 2]''​ | +| Get Image Value At Location | ''​iX[LINE_EXPR,​ COL_EXPR]''​ | Returns the value of image X at the **specified (line, column) coordinate**. Coordinates outside the image boundary are **mirrored** back into range. For a row coordinate ''​cellLine''​ in an image of N rows, ''​cellLine''​ is normally expected to be in the range [1, N]. When ''​cellLine''​ is outside this range, it is mirrored back: if ''​cellLine''​ ≤ 0, the effective row is ''​1 − cellLine''​ (for instance, ''​cellLine''​ = 0 → row 1, ''​cellLine''​ = −1 → row 2, ''​cellLine''​ = −2 → row 3); if ''​cellLine''​ > N, the effective row is ''​2N + 1 − cellLine''​ (for instance, ''​cellLine''​ = N+1 → row N, ''​cellLine''​ = N+2 → row N−1, ''​cellLine''​ = N+3 → row N−2). The same formula applies independently to the column coordinate ''​cellCol''​. If the target cell is null, **returns 0** (not null). | ''​i1[line - 1, column - 2]''​ | 
-| Get Image Null Value | ''​null''​ | Returns the null sentinel value for the current output ​image, ​making ​the current cell nullUsing ''​null''​ in arithmetic ​always poisons the result. | ''​if i1 > 2 then i1 else null''​ | +| Get Image Null Sentinel ​Value | ''​null(iX)''​ | Returns the **numeric ​sentinel value** stored in image X to represent missing data (e.g. −132767, or any other number defined when the image was created)Unlike the abstract ​''​null'' ​keyword, this is a real number that participates normally ​in arithmetic ​— but be aware that using it as if it were a regular data value may produce misleading results. | ''​if null(i2) > 2 then 1 else null''​ |
-| Get Image Null Value (specific) | ''​null(iX)''​ | Returns the null sentinel ​value defined for image X specifically. | ''​if null(i2) > 2 then 1 else null''​ | +
-| Is Null | ''​isNull(EXPR)''​ | Returns 1 if the expression result is null, 0 otherwise. The **correct** way to test for null. | ''​if not isNull(i1) then i1 else i2''​ |+
 | Get Line Number | ''​line''​ | In map calculators:​ returns the **row index** of the current cell, **counting from 1**. In lookup table calculators:​ returns the **key of the current row**. | ''​line + 1''​ | | Get Line Number | ''​line''​ | In map calculators:​ returns the **row index** of the current cell, **counting from 1**. In lookup table calculators:​ returns the **key of the current row**. | ''​line + 1''​ |
 | Get Column Number | ''​column''​ | In map calculators:​ returns the **column index** of the current cell being processed, **counting from 1**. In lookup table calculators:​ returns the **value associated with the key of the current row** in the base lookup table. | ''​if column / 2 > 50 then 1 else null''​ | | Get Column Number | ''​column''​ | In map calculators:​ returns the **column index** of the current cell being processed, **counting from 1**. In lookup table calculators:​ returns the **value associated with the key of the current row** in the base lookup table. | ''​if column / 2 > 50 then 1 else null''​ |
Line 312: Line 314:
 | Key existence test | ''​tX[?​ EXPR]''​ / ''​tX[=?​ EXPR]''​ / ''​tX[==?​ EXPR]''​ | Returns **1** if EXPR matches a key in the table, **0** otherwise. Does not retrieve a value. | ''​t2[?​ i2]''​ | | Key existence test | ''​tX[?​ EXPR]''​ / ''​tX[=?​ EXPR]''​ / ''​tX[==?​ EXPR]''​ | Returns **1** if EXPR matches a key in the table, **0** otherwise. Does not retrieve a value. | ''​t2[?​ i2]''​ |
 | Named attribute | ''​tX["​NAME"​]''​ | Returns the value bound to a **predefined attribute name** (e.g. ''"​cellArea"''​). Only valid for tables produced by [[Extract Map Attributes]] or [[Extract Lookup Table Attributes]]. | ''​t2["​cellArea"​]''​ | | Named attribute | ''​tX["​NAME"​]''​ | Returns the value bound to a **predefined attribute name** (e.g. ''"​cellArea"''​). Only valid for tables produced by [[Extract Map Attributes]] or [[Extract Lookup Table Attributes]]. | ''​t2["​cellArea"​]''​ |
 +
 +> **Note:** The syntax ''​tX{EXPR}''​ (curly braces) was a deprecated alias for ''​tX[<​= EXPR]''​ and is no longer supported.
  
 The bound operators (''<​='',​ ''<'',​ ''>​='',​ ''>'',​ ''><'',​ ''/''​) are especially useful for **range-based reclassification** and **interpolation**,​ where your table defines breakpoints rather than exact class codes. The bound operators (''<​='',​ ''<'',​ ''>​='',​ ''>'',​ ''><'',​ ''/''​) are especially useful for **range-based reclassification** and **interpolation**,​ where your table defines breakpoints rather than exact class codes.
Line 328: Line 332:
 | Default column | ''​tX[ [EXPR, EXPR, ...] ]''​ | Returns the value in the **first data column** matching the given set of keys. | ''​t2[ [i1 + 2, i2/2, t1[v2]] ]''​ | | Default column | ''​tX[ [EXPR, EXPR, ...] ]''​ | Returns the value in the **first data column** matching the given set of keys. | ''​t2[ [i1 + 2, i2/2, t1[v2]] ]''​ |
 | Named column | ''​tX[ [EXPR, EXPR, ...] ["​COL_NAME"​] ]''​ | Returns the value in the **named data column** matching the given set of keys. | ''​t2[ [i1 + 2, i2/2, t1[v2]] ["​Area_In_Hectares"​] ]''​ | | Named column | ''​tX[ [EXPR, EXPR, ...] ["​COL_NAME"​] ]''​ | Returns the value in the **named data column** matching the given set of keys. | ''​t2[ [i1 + 2, i2/2, t1[v2]] ["​Area_In_Hectares"​] ]''​ |
-| Indexed column | ''​tX[ [EXPR, EXPR, ...] [EXPR] ]''​ | Returns the value in the **column indexed by the second expression** matching the given set of keys. | ''​t2[ [i1 + 2, i2/2, t1[v2]] [v1+2] ]''​ |+| Indexed column | ''​tX[ [EXPR, EXPR, ...] [EXPR] ]''​ | Returns the value in the **column indexed by the second expression** matching the given set of keys. Column indices are **1-based**. | ''​t2[ [i1 + 2, i2/2, t1[v2]] [v1+2] ]''​ |
  
 These operators allow joining spatial data to rich tabular datasets at the cell level — for example, retrieving a transition probability from a multi-dimensional probability table keyed by land-use class and elevation zone. These operators allow joining spatial data to rich tabular datasets at the cell level — for example, retrieving a transition probability from a multi-dimensional probability table keyed by land-use class and elevation zone.
Line 351: Line 355:
 | ''​x''​ | Column coordinate to anchor the window centre (optional) | | ''​x''​ | Column coordinate to anchor the window centre (optional) |
  
-The window centre defaults to the current cell when ''​y''​ and ''​x''​ are omitted. For even-sized windows, the centre is displaced toward the top-left corner. **Null cells within the window are excluded from all calculations.**+The window centre defaults to the current cell when ''​y''​ and ''​x''​ are omitted. For even-sized windows, the centre is displaced toward the top-left corner. When any cell in the window falls outside the image boundary, its coordinate is resolved using the same mirror formula as ''​iX[LINE,​ COL]'',​ applied **independently to each out-of-bounds cell**: for a row coordinate ''​cellLine''​ in an image of N rows, if ''​cellLine''​ ≤ 0 the effective row is ''​1 − cellLine'',​ and if ''​cellLine''​ > N the effective row is ''​2N + 1 − cellLine'';​ the same applies to column coordinates. This applies to every cell in the window — not just the anchor position — so a window near the edge of the image will sample mirrored cells for all positions that extend beyond the boundary. **Null cells within the window are excluded from all calculations.**
  
 ^ Operator ^ Syntax ^ Description ^ Example ^ ^ Operator ^ Syntax ^ Description ^ Example ^
Line 362: Line 366:
 | Median | ''​nbMedian(...)''​ | Median of non-null cells. For an even count, the **greater** of the two central values is returned. | ''​nbMedian(i1,​ 5, 5)''​ | | Median | ''​nbMedian(...)''​ | Median of non-null cells. For an even count, the **greater** of the two central values is returned. | ''​nbMedian(i1,​ 5, 5)''​ |
 | Mode | ''​nbMode(...)''​ | Most frequent value among non-null cells. Returns null if no mode exists; returns the **lesser** value if multiple modes tie. | ''​nbMode(i1,​ 5, 5)''​ | | Mode | ''​nbMode(...)''​ | Most frequent value among non-null cells. Returns null if no mode exists; returns the **lesser** value if multiple modes tie. | ''​nbMode(i1,​ 5, 5)''​ |
-| Variance | ''​nbVar(...)''​ | Population variance of non-null cells: sum of (xi - mean)^2 / n | ''​nbVar(i4,​ 7, 7) / 25''​ |+| Variance | ''​nbVar(...)''​ | Population variance of non-null cells: sum of (xi - mean)<​nowiki>​^</​nowiki>​2 / n | ''​nbVar(i4,​ 7, 7) / 25''​ |
 | Standard Deviation | ''​nbStdDev(...)''​ | Population standard deviation of non-null cells: sqrt of variance | ''​nbStdDev(i2,​ 3, 3, line+1, column)''​ | | Standard Deviation | ''​nbStdDev(...)''​ | Population standard deviation of non-null cells: sqrt of variance | ''​nbStdDev(i2,​ 3, 3, line+1, column)''​ |
  
Line 371: Line 375:
 > **Availability:​** [[Calculate Map]] and [[Calculate Categorical Map]] only. > **Availability:​** [[Calculate Map]] and [[Calculate Categorical Map]] only.
  
-These operators compute a neighbourhood statistic and then use the result as an **index** to retrieve a value from either a lookup table or a second image.+These operators compute a neighbourhood statistic and then use the result as an **index** to retrieve a value from either a connected Lookup Table or a second image. ''​TABLE''​ below always refers to a **Lookup Table** specifically — see section 5 for the operators that query one directly. 
 + 
 +> If ''​TABLE''​ is bound to a multi-column Table (not a Lookup Table), an error is reported. See section 6 for multi-column tables.
  
 **Full syntax:** \\ **Full syntax:** \\
Line 381: Line 387:
 ''​nbN(IMAGE,​ h, w, IMAGE2)''​ ''​nbN(IMAGE,​ h, w, IMAGE2)''​
  
-When a **TABLE** is provided: the statistic is used as a key to query the lookup table. \\+When a **TABLE** ​(Lookup Table) ​is provided: the statistic is used as a key to query it, equivalent to ''​TABLE[statistic]''​ from section 5. \\
 When an **IMAGE2** is provided: the statistic is used as a pixel coordinate to sample ''​IMAGE2''​. When an **IMAGE2** is provided: the statistic is used as a pixel coordinate to sample ''​IMAGE2''​.
  
 ^ Operator ^ Syntax ^ Description ^ Example ^ ^ Operator ^ Syntax ^ Description ^ Example ^
-| MinRef | ''​nbMinRef(...)''​ | Finds the minimum in the window, then uses it as an index. | ''​nbMinRef(i4,​ 2, 3, t1, line, column)''​ | +| MinRef | ''​nbMinRef(...)''​ | Finds the minimum in the window, then uses it as an index into the Lookup Table (or a coordinate into IMAGE2). | ''​nbMinRef(i4,​ 2, 3, t1, line, column)''​ | 
-| MaxRef | ''​nbMaxRef(...)''​ | Finds the maximum in the window, then uses it as an index. | ''​nbMaxRef(i4,​ 2, 3, i1, line, column)''​ |+| MaxRef | ''​nbMaxRef(...)''​ | Finds the maximum in the window, then uses it as an index into the Lookup Table (or a coordinate into IMAGE2). | ''​nbMaxRef(i4,​ 2, 3, i1, line, column)''​ |
  
 ---- ----
Line 397: Line 403:
  
 **Syntax:** ''​nbConvol(IMAGE,​ TABLE, h, w)''​ / ''​nbConvol(IMAGE,​ TABLE, h, w, y, x)''​ **Syntax:** ''​nbConvol(IMAGE,​ TABLE, h, w)''​ / ''​nbConvol(IMAGE,​ TABLE, h, w, y, x)''​
 +
 +> If ''​TABLE''​ is bound to a multi-column Table (not a Lookup Table), an error is reported. See section 6 for multi-column tables.
  
 ^ Parameter ^ Meaning ^ ^ Parameter ^ Meaning ^
 | ''​IMAGE''​ | The image to convolve (''​iX''​) | | ''​IMAGE''​ | The image to convolve (''​iX''​) |
-| ''​TABLE''​ | A Table whose entries are the kernel coefficients,​ indexed 1 to h*w, mapped **left-to-right,​ top-to-bottom** |+| ''​TABLE''​ | A **Lookup ​Table** whose entries are the kernel coefficients,​ indexed 1 to h*w, mapped **left-to-right,​ top-to-bottom**|
 | ''​h''​ | Number of kernel rows | | ''​h''​ | Number of kernel rows |
 | ''​w''​ | Number of kernel columns | | ''​w''​ | Number of kernel columns |
 | ''​y'',​ ''​x''​ | Anchor position (optional; defaults to window centre) | | ''​y'',​ ''​x''​ | Anchor position (optional; defaults to window centre) |
  
-**Kernel index mapping for 3x3 kernel (h=3w=3):**+Each cell in the window is assigned ​unique integer key, starting at 1 and advancing left-to-right,​ then top-to-bottom. The ''​TABLE''​ argument must be a Lookup Table containing one row per window cell, where the key is that cell's index and the value is its convolution coefficient. The result of ''​nbConvol''​ is the weighted sum of all pixel values in the windoweach multiplied by the coefficient stored at the corresponding key in the table.
  
-Table key Window position ​+The grids below illustrate the index mapping for some common window sizes: 
-| 1 | top-left ​| + 
-| 2 | top-centre ​| +**3×3 window (h=3, w=3):** 
-| 3 | top-right ​+ 
-| 4 | mid-left ​+  col 1  ^  col 2  ^  col 3  ​
-centre ​+^ row 1    ​2  ​ ​3  ​| 
-mid-right ​+^ row 2 |  ​4  ​ ​5  ​ ​6 ​ | 
-bottom-left ​+^ row 3 |  ​7 ​ |  8  |  9  ​
-bottom-centre ​+ 
-| 9 | bottom-right ​|+**5×5 window (h=5, w=5):** 
 + 
 +^  ^  col 1  ^  col 2  ^  col 3  ^  col 4  ^  col 5  ^ 
 +^ row 1  ​1 ​ |  2  |  3  |  ​  ​5  ​
 +^ row 2  ​6  ​ ​7 ​ |  8  |  9  |  10  ​
 +^ row 3  ​11  ​ ​12 ​ |  13  |  14  |  15  ​
 +^ row 4  ​16  ​ ​17 ​ |  18  |  19  |  20  ​
 +^ row 5  ​21  ​ ​22 ​ |  23  |  24  |  25  | 
 + 
 +**3×5 window (h=3, w=5) — non-square example:​** 
 + 
 +^  ^  col 1  ^  col 2  ^  col 3  ^  col 4  ^  col 5  ^ 
 +^ row 1 |  1  |  2  |  3  |  4  |  5  ​
 +^ row 2  ​6 ​ |  7  |  8  |  ​  ​10  ​| 
 +^ row 3 |  11  |  12  |  13  |  14  |  15  | 
 + 
 +In all cases the total number of keys required in the table is h × w.
  
 **Example:​** Apply a Gaussian-blur kernel stored in ''​t1''​ over a 3x3 window on ''​i1'':​ **Example:​** Apply a Gaussian-blur kernel stored in ''​t1''​ over a 3x3 window on ''​i1'':​
Line 430: Line 454:
  
 A null value in any operand **poisons the entire expression**:​ the result becomes null regardless of what the rest of the expression does. This is not a special case — it is the default behaviour for every operator and every operand type, across all five calculator functors. A null value in any operand **poisons the entire expression**:​ the result becomes null regardless of what the rest of the expression does. This is not a special case — it is the default behaviour for every operator and every operand type, across all five calculator functors.
 +
 +> **Note:** The keyword ''​null''​ (available in all five functors) is an **abstract** sentinel — it carries no numeric value and cannot participate in arithmetic. It is distinct from ''​null(iX)''​ (available in map calculators only), which returns the **numeric** value that image X uses internally to mark missing cells. These two are not interchangeable:​ ''​null''​ poisons any expression that uses it; ''​null(iX)''​ is a regular number that happens to be the image'​s no-data sentinel.
  
 ^ Expression ^ Null behaviour ^ ^ Expression ^ Null behaviour ^
Line 435: Line 461:
 | ''​v1 / 0''​ | Always null — division by zero is null regardless of operand type | | ''​v1 / 0''​ | Always null — division by zero is null regardless of operand type |
 | ''​t1[v1]''​ | Null if the key ''​v1''​ is not found in lookup table ''​t1''​ | | ''​t1[v1]''​ | Null if the key ''​v1''​ is not found in lookup table ''​t1''​ |
-| ''​i1 + t1[line] / v1 ^ 3''​ | Null if the map cell ''​i1'',​ the table lookup ''​t1[line]'',​ or the value ''​v1''​ is null |+| ''​i1 + t1[line] / v1 <​nowiki>​^</​nowiki> ​3''​ | Null if the map cell ''​i1'',​ the table lookup ''​t1[line]'',​ or the value ''​v1''​ is null |
 | ''​if isNull(t1[v1]) then 1 else 2''​ | **Never null** — ''​isNull''​ itself always resolves to 0 or 1 | | ''​if isNull(t1[v1]) then 1 else 2''​ | **Never null** — ''​isNull''​ itself always resolves to 0 or 1 |
 | ''​if isNull(v1) then i2 else t1[line]''​ | May be null, depending on ''​i2''​ or the table lookup ''​t1[line]''​ | | ''​if isNull(v1) then i2 else t1[line]''​ | May be null, depending on ''​i2''​ or the table lookup ''​t1[line]''​ |
Line 472: Line 498:
 > **Note:** ''​isNull()''​ is immune to the poisoning rule — it always returns 0 or 1, never null, even when its argument is null. It is the only reliable way to branch on null. > **Note:** ''​isNull()''​ is immune to the poisoning rule — it always returns 0 or 1, never null, even when its argument is null. It is the only reliable way to branch on null.
  
-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 the map calculators ​only, since the other calculator ​functors ​do not 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.
  
 ===== Exceptions in map calculators ===== ===== Exceptions in map calculators =====
Line 478: Line 504:
 > **Availability:​** [[Calculate Map]] and [[Calculate Categorical Map]] only. The two exceptions below involve ''​iX''​ references and neighbourhood functions, which do not exist in the other three calculator functors. > **Availability:​** [[Calculate Map]] and [[Calculate Categorical Map]] only. The two exceptions below involve ''​iX''​ references and neighbourhood functions, which do not exist in the other three calculator functors.
  
-  * **Coordinate lookup** ''​iX[LINE_EXPR,​ COL_EXPR]''​ — accessing a map at a specific coordinate returns **0**, not null, when the target cell is null. This is the one case where null input does not poison ​the output. It can be a source of subtle bugs: an expression that tests ''​i1[line-1,​ column] > 0''​ will silently treat a null neighbour as zero rather than null. A table or lookup-table key lookup (''​tX[EXPR]''​) that finds no matching key still returns null as normal — this exception applies only to map coordinate lookups.+  * **Coordinate lookup** ''​iX[LINE_EXPR,​ COL_EXPR]''​ — accessing a map at a specific coordinate returns **0**, not null, when the target cell **is null**Coordinates that fall **outside ​the image boundary** are mirrored back into range rather than producing an error. For row coordinate ''​cellLine''​ in an image of N rows, ''​cellLine''​ is normally expected to be in the range [1, N]. When ''​cellLine''​ is outside this range, it is mirrored back: if ''​cellLine''​ ≤ 0, the effective row is ''​1 − cellLine''​ (for instance, ''​cellLine''​ = 0 → row 1, ''​cellLine''​ = −1 → row 2, ''​cellLine''​ = −2 → row 3); if ''​cellLine''​ > N, the effective row is ''​2N + 1 − cellLine''​ (for instance, ''​cellLine''​ = N+1 → row N, ''​cellLine''​ = N+2 → row N−1, ''​cellLine''​ = N+3 → row N−2). The same formula applies independently to the column coordinate ''​cellCol''​. It can be a source of subtle bugs: an expression that tests ''​i1[line-1,​ column] > 0''​ will silently treat a null neighbour as zero rather than null. A table or lookup-table key lookup (''​tX[EXPR]''​) that finds no matching key still returns null as normal — this exception applies only to map coordinate lookups.
   * **Neighbourhood functions** ''​nbN(iX,​ h, w)''​ — operators such as ''​nbSum'',​ ''​nbAverage'',​ ''​nbMax'',​ and so on aggregate over a window of cells and **exclude null cells from the calculation**. If ''​i1''​ is null at the current cell, the neighbourhood function simply ignores it and aggregates the remaining non-null cells in the window.   * **Neighbourhood functions** ''​nbN(iX,​ h, w)''​ — operators such as ''​nbSum'',​ ''​nbAverage'',​ ''​nbMax'',​ and so on aggregate over a window of cells and **exclude null cells from the calculation**. If ''​i1''​ is null at the current cell, the neighbourhood function simply ignores it and aggregates the remaining non-null cells in the window.
  
Line 844: Line 870:
   - **Connect your inputs first.** Connect all required tables ([[Number Table]]) and scalars ([[Number Value]]) to the operator'​s ports before opening the editor.   - **Connect your inputs first.** Connect all required tables ([[Number Table]]) and scalars ([[Number Value]]) to the operator'​s ports before opening the editor.
   - **Set the Key Column Name and Value Column Name** for the output lookup table.   - **Set the Key Column Name and Value Column Name** for the output lookup table.
-  - **Connect a Base Lookup Table**, or leave it as ''​.none'',​ in which case the row set instead comes from a single connected table — the lowest-indexed ​''​NumberTable''​ in verbose form, or the first lookup table referenced by name in the expression in shorthand form.+  - **Connect a Base Lookup Table**, or leave it as ''​.none'',​ in which case the row source is the ''​NumberTable'' ​hook with the lowest list index that is connected to a lookup table (not a regular table) ​in verbose form, or the first lookup table referenced by name in the expression in shorthand form.
   - **Open the expression editor.** All connected identifiers appear with their aliases at the top. [[Calculate Lookup Table Values]] has a single expression; [[Calculate Lookup Table Keys And Values]] has separate key and value expressions.   - **Open the expression editor.** All connected identifiers appear with their aliases at the top. [[Calculate Lookup Table Values]] has a single expression; [[Calculate Lookup Table Keys And Values]] has separate key and value expressions.
   - **Write the expression(s)**,​ using ''​line''​ for the current row's key and ''​column''​ for the current row's existing value where needed.   - **Write the expression(s)**,​ using ''​line''​ for the current row's key and ''​column''​ for the current row's existing value where needed.
Line 855: Line 881:
   * **''​line''​ and ''​column''​ are context-dependent.** In [[Calculate Map]] and [[Calculate Categorical Map]], ''​line''​ is the spatial row index and ''​column''​ is the spatial column index of the current pixel, both **starting at 1**. In [[Calculate Lookup Table Values]] and [[Calculate Lookup Table Keys And Values]], both are still available but take on row-oriented meanings: ''​line''​ is the key of the current table row, and ''​column''​ is the value already associated with that key in the base lookup table. A different table'​s value for that same key can be retrieved with ''​%someTable[line]''​. See [[#​4_image_operators_and_functions|section 4]] for full details.   * **''​line''​ and ''​column''​ are context-dependent.** In [[Calculate Map]] and [[Calculate Categorical Map]], ''​line''​ is the spatial row index and ''​column''​ is the spatial column index of the current pixel, both **starting at 1**. In [[Calculate Lookup Table Values]] and [[Calculate Lookup Table Keys And Values]], both are still available but take on row-oriented meanings: ''​line''​ is the key of the current table row, and ''​column''​ is the value already associated with that key in the base lookup table. A different table'​s value for that same key can be retrieved with ''​%someTable[line]''​. See [[#​4_image_operators_and_functions|section 4]] for full details.
   * **Native compilation.** Expression calculations across all five calculator functors can be compiled to native code automatically (requires the optional native expression support package), delivering near-C performance — no user action is needed beyond installing the package.   * **Native compilation.** Expression calculations across all five calculator functors can be compiled to native code automatically (requires the optional native expression support package), delivering near-C performance — no user action is needed beyond installing the package.
-  * **Overflow produces null.** If the result exceeds the Cell Type's range, the cell becomes null rather than wrapping. Applies to the map calculators ​only (the other functors ​return values or table entries that are not constrained to cell type in the same way).+  * **Overflow produces null.** If the result exceeds the Cell Type's range, the cell becomes null rather than wrapping. Applies to [[Calculate Map]] and [[Calculate Categorical Map]] only — the only two functors ​with Cell Type parameter.
   * **Neighbourhood functions ignore nulls.** All ''​nbN''​ operators ([[#​7_image_neighborhood_functions|section 7]]) skip null cells in their window; only non-null cells contribute to the statistic.   * **Neighbourhood functions ignore nulls.** All ''​nbN''​ operators ([[#​7_image_neighborhood_functions|section 7]]) skip null cells in their window; only non-null cells contribute to the statistic.
-  * **''​iX[LINE,​ COL]''​ returns 0, not null, for out-of-bounds ​or null cells.** This is the one exception to the general null-propagation rule. See [[#​4_image_operators_and_functions|section 4]] and [[#​null_value_handling|Null Value Handling]].+  * **''​iX[LINE,​ COL]''​ returns 0, not null, when the target cell is null.** Coordinates outside the image boundary are mirrored rather than treated as out-of-bounds. This is the one exception to the general null-propagation rule. See [[#​4_image_operators_and_functions|section 4]] and [[#​null_value_handling|Null Value Handling]].
  
 ---- ----
Line 869: Line 895:
 | Calculate Lookup Table Values | ''​CalculateLookupTableValues''​ | | Calculate Lookup Table Values | ''​CalculateLookupTableValues''​ |
 | Calculate Lookup Table Keys and Values | ''​CalculateLookupTableKeysAndValues''​ | | Calculate Lookup Table Keys and Values | ''​CalculateLookupTableKeysAndValues''​ |
- 
-