Fieldra Desktop · Docs

Scripting with the fieldra library

Automate work in Fieldra Desktop with Python or JavaScript. The fieldra library reads and writes the project’s layers and runs every toolbox tool, so a series of steps you repeat each week becomes one script.

Getting started

  1. Open a project and go to Analysis › Script.
  2. Choose Python or JavaScript. Python can be the built-in one (works offline) or one installed on the computer.
  3. Write the script, then press Run (or Ctrl/⌘ Enter). What it prints appears below the editor; layers it adds appear on the map.
  4. Save it to use again: scripts are kept with the app, and the list also has examples to start from.

Every call into Fieldra is awaited (await fieldra.layers()). await works at the top of the script in both languages, so no async def main() is needed.

Python
import fieldra

for l in await fieldra.layers():
    print(l['name'], '·', l['count'], 'features')

zones = await fieldra.run('buffer', input='Water points', meters=500)
print('Added', zones['name'], 'with', zones['count'], 'zones')
JavaScript
const layers = await fieldra.layers();
for (const l of layers) print(l.name, '·', l.count, 'features');

const zones = await fieldra.run('buffer', { input: 'Water points', meters: 500 });
print('Added', zones.name, 'with', zones.count, 'zones');

Runtimes

RuntimeOfflineLibraries
Built-in PythonYesPython 3 with the standard library. numpy, pandas, shapely, scipy and others download on first import and are then kept; enter them under Packages for offline use to get them in advance.
Python on this computerYesAny Python 3.8 or newer found on the computer, including the one that comes with QGIS (with GDAL, rasterio and the rest). It uses its own installed packages and can read files. Set FIELDRA_PYTHON to the interpreter’s path to choose one.
JavaScriptYesModern JavaScript with turf (geometry: area, length, buffers, distances…) ready to use. print() and console.log() write to the output.

API reference

The same six calls in both languages; Python names use snake_case. In Python, import fieldra first; in JavaScript, fieldra is already there.

py await fieldra.layers()js await fieldra.layers()

Every layer of the open project: survey categories, files you added and results of tools. editable is true for your own layers (the ones a script may change).

Returns list of { id, name, count, editable }

py await fieldra.layer(name)js await fieldra.layer(nameOrId)

A layer’s features with their attributes in properties, in WGS 84 longitude/latitude. Give the name shown in the Layers panel or the id; with two layers of the same name, the newest is used.

Returns GeoJSON FeatureCollection

py await fieldra.add_layer(name, data)js await fieldra.addLayer(name, data)

Adds a layer to the map from a FeatureCollection or a plain list of features. It is one of your layers, so it can be styled, edited and exported like any other.

Returns the new layer’s id

py await fieldra.update_layer(name, data)js await fieldra.updateLayer(nameOrId, data)

Replaces the features of one of your layers. It is one undo step (Ctrl/⌘ Z undoes the whole script’s change). Survey layers can’t be changed: add a new layer instead.

Returns true

py await fieldra.run(tool, **params)js await fieldra.run(tool, params)

Runs a toolbox tool, exactly as the Toolbox does, and adds its result to the project. Layers and images may be given by name or id. In Python, parameters are keywords (or a dict as the second argument).

Returns { id, name, count } or { id, name, image, width, height }

py await fieldra.tools()js await fieldra.tools()

The tools available in this version and their parameters, e.g. “meters (number)”. Use it to check a tool id from inside a script.

Returns list of { id, label, params }

In Python, run takes keywords (run('buffer', input='Wells', meters=100)) or a dict (run('buffer', {'input': 'Wells', 'meters': 100})). A call that fails raises an exception (Python) or rejects (JavaScript) with the reason, e.g. No layer “Wels”; catch it with try/except or try/catch.

What calls return

Layers are GeoJSON: dicts and lists in Python, plain objects in JavaScript. Coordinates are WGS 84 longitude, latitude (and height when there is one).

layers()
[
  { "id": "u-3f2a", "name": "Water points", "count": 128, "editable": true },
  { "id": "survey:water", "name": "Water quality", "count": 54, "editable": false }
]
layer(name)
{
  "type": "FeatureCollection",
  "features": [{
    "type": "Feature",
    "properties": { "name": "BH-12", "depth": 34.5 },
    "geometry": { "type": "Point", "coordinates": [-13.234, 8.484] }
  }]
}
run() — a layer result
{ "id": "u-91bc", "name": "Water points buffer", "count": 128 }
run() — an image result
{ "id": "a-77d0", "name": "DSM slope", "image": true,
  "width": 4096, "height": 3072 }

Expressions

Parameters of kind expression (select’s where, calculate’s expression) use the same language as Select by attributes and the field calculator. Fields are written [name] (or bare, without spaces), text in single quotes.

[depth] > 30 AND [village] LIKE 'Kis%'
[status] IN ('dry', 'broken') OR [yield] IS NULL
round($area / 10000, 2)                       -- hectares
if([count] >= 10, 'large', 'small')
upper([species]) + ' (' + [count] + ')'

Geometry values: $area (m²), $length and $perimeter (m), $x and $y (longitude, latitude), $id. Functions: round, abs, floor, ceil, sqrt, log, min, max, upper, lower, trim, len, concat, substr, replace, coalesce, if, number, text, year, month, day. The raster calculator’s expression uses A and B for the two images, e.g. (A - B) / (A + B) or if(A > 100, 1, 0).

Toolbox tools

Every tool fieldra.run can call, with its id and parameters. Parameters with a default may be left out; optional ones too.

layer
A layer: its name as shown in the Layers panel, or its id (from layers(), add_layer() or an earlier run()).
raster
An image (orthomosaic, elevation model, satellite scene or a raster tool’s result): its name in the Images list, or its id.
field
The name of a field (attribute) of the layer named in brackets, e.g. depth.
number
A number. Distances are metres, areas square metres, cell sizes metres.
text
Text, in the format the description gives (e.g. reclassify rules 0-5=1; 5-15=2).
expression
An expression in Fieldra’s expression language (see Expressions).
choice
One of the listed values, as text.
bool
True or false (True/False in Python, true/false in JavaScript).

Proximity

Bufferbuffer

A zone of a set distance around each feature.

  • inputlayerInput
  • metersnumberDistance (m) · default 50
  • dissolveboolDissolve all into one · default false

Overlay

Clipclip

The input inside the overlay areas.

  • inputlayerInput
  • overlaylayerOverlay layer
Eraseerase

The input outside the overlay areas.

  • inputlayerInput
  • overlaylayerOverlay layer
Intersectintersect

Where the input and the overlay areas overlap, with both attributes.

  • inputlayerInput
  • overlaylayerOverlay layer
Unionunion

Two area layers combined into every piece.

  • inputlayerInput
  • overlaylayerOverlay layer
Identityidentity

The input cut by the overlay areas; pieces inside get both attributes.

  • inputlayerInput
  • overlaylayerOverlay layer
Symmetrical differencesymDiff

Where only one of two area layers is.

  • inputlayerInput
  • overlaylayerOverlay layer
Updateupdate

The input with the overlay areas put in place of what they cover.

  • inputlayerInput
  • overlaylayerOverlay layer
Tabulate intersectiontabulate

Area (or length, count) of each overlay class in each input area.

  • inputlayerInput
  • overlaylayerOverlay layer
  • nameFieldfield of overlayClasses from · optional
Spatial joinspatialJoin

Attributes of the overlay feature each input feature touches.

  • inputlayerInput
  • overlaylayerOverlay layer
  • nearestMnumberOr the nearest within (m) · default 0
Summarize withinsummarize

Count (and total a field of) the overlay features in each input area.

  • inputlayerInput
  • overlaylayerOverlay layer
  • statFieldfield of overlayField to total · optional
Nearnear

Distance to the nearest overlay feature.

  • inputlayerInput
  • overlaylayerOverlay layer
  • nameFieldfield of overlayName it by · optional
Distance matrixdistanceMatrix

Lines to the nearest overlay features, with distances.

  • inputlayerInput
  • overlaylayerOverlay layer
  • nearestCountnumberHow many nearest · default 1
  • maxDistanceMnumberWithin (m, 0 = any) · default 0
Mergemerge

Both layers’ features in one layer.

  • inputlayerInput
  • overlaylayerOverlay layer

Generalise

Dissolve by fielddissolveBy

One feature per value of a field, with a total.

  • inputlayerInput
  • fieldfield of inputBy field (empty: all in one) · optional
  • sumfield of inputTotal of · optional
Eliminateeliminate

Merge areas smaller than a size into their neighbour.

  • inputlayerInput
  • minAreanumberSmaller than (m²) · default 100
Minimum bounding geometrybounding

Rectangle, envelope, hull or circle around features.

  • inputlayerInput
  • kindchoiceShape · one of 'rectangle', 'envelope', 'convex', 'circle' · default 'rectangle'
  • groupfield of inputGroup by field (empty: each) · optional
Convex hull (all)convexAll

The smallest area enclosing all features.

  • inputlayerInput
Centroidscentroids

A point at the centre of each feature.

  • inputlayerInput
Delaunay trianglesdelaunay

Triangles joining points.

  • inputlayerPoints

Fields

Select (expression)select

The features matching an expression, as a new layer.

  • inputlayerInput
  • whereexpressionWhere
Calculate fieldcalculate

A copy with a field filled from an expression.

  • inputlayerInput
  • fieldtextField · default 'value'
  • expressionexpressionExpression

Raster

Slopeslope

Slope from an elevation model.

  • rasterrasterElevation model
  • bandnumberBand · default 1
Aspectaspect

Aspect from an elevation model.

  • rasterrasterElevation model
  • bandnumberBand · default 1
Hillshadehillshade

Hillshade from an elevation model.

  • rasterrasterElevation model
  • bandnumberBand · default 1
Reclassifyreclassify

Values into classes, e.g. slope 0-5=1; 5-15=2; 15-90=3.

  • rasterrasterImage
  • bandnumberBand · default 1
  • rulestextClasses (from-to=value; …) · default '0-5=1; 5-15=2; 15-90=3'
Focal statisticsfocalStats

Each cell from its neighbourhood.

  • rasterrasterImage
  • bandnumberBand · default 1
  • statchoiceStatistic · one of 'mean', 'min', 'max', 'range', 'std', 'sum', 'majority' · default 'mean'
  • radiusnumberRadius (cells) · default 2
Raster calculatorrasterCalc

A new image from an expression of A (and B), e.g. A - B or if(A > 100, 1, 0).

  • arasterImage A
  • brasterImage B · optional
  • expressionexpressionExpression · default 'A'
ResampleresampleRaster

The image at another cell size.

  • rasterrasterImage
  • cellnumberCell size (m) · default 5
  • methodchoiceMethod · one of 'bilinear', 'nearest' · default 'bilinear'
Pan-sharpenpanSharpen

Colour bands sharpened to a panchromatic band’s detail (Brovey or simple mean).

  • rasterrasterColour image
  • bandstextBands (e.g. 1,2,3) · default '1,2,3'
  • panrasterPanchromatic image
  • methodchoiceMethod · one of 'brovey', 'mean' · default 'brovey'
Clip image to areasclipRaster

Cut an image to the areas of a layer.

  • rasterrasterImage
  • areaslayerAreas
Zonal statisticszonalStats

The mean, lowest, highest and total of an image in each area (added to the areas).

  • areaslayerAreas
  • rasterrasterImage
  • bandnumberBand · default 1
Raster to polygonsrasterToPolygons

Areas of equal whole-number value (gridcode).

  • rasterrasterImage
  • bandnumberBand · default 1
Features to rasterfeaturesToRaster

Burn areas, lines or points into an image, valued by a field.

  • inputlayerLayer
  • fieldfield of inputValue from (empty: 1) · optional
  • cellnumberCell size (m) · default 5
Cost distancecostDistanceTool

Accumulated cost from source points over a cost image.

  • rasterrasterCost image
  • bandnumberBand · default 1
  • sourceslayerSource points
NDVIndviTool

(NIR − red) / (NIR + red) from a multispectral image.

  • rasterrasterImage
  • rednumberRed band · default 3
  • nirnumberNIR band · default 4

Examples

Replace the layer and image names with ones from your project.

Statistics of a field

Read a layer and use the standard library on its attributes.

Python
import statistics
import fieldra

points = await fieldra.layer('Water points')
depths = [f['properties']['depth'] for f in points['features']
          if f['properties'].get('depth') is not None]
print('n =', len(depths))
print('mean', round(statistics.mean(depths), 2),
      '· median', statistics.median(depths),
      '· sd', round(statistics.stdev(depths), 2))

Run a tool, then change its result

A tool’s result is one of your layers, so the script can add fields to it.

Python
import fieldra

zones = await fieldra.run('buffer', input='Water points', meters=500, dissolve=False)
data = await fieldra.layer(zones['id'])
for f in data['features']:
    deep = (f['properties'].get('depth') or 0) > 30
    f['properties']['priority'] = 'high' if deep else 'normal'
await fieldra.update_layer(zones['id'], data)
print('Flagged', len(data['features']), 'zones')

A new layer from your own geometry

Build features as plain dicts; coordinates are longitude, latitude.

Python
import fieldra

sites = await fieldra.layer('Sample sites')
lines = []
for a, b in zip(sites['features'], sites['features'][1:]):
    lines.append({
        'type': 'Feature',
        'properties': {'from': a['properties'].get('name'), 'to': b['properties'].get('name')},
        'geometry': {'type': 'LineString',
                     'coordinates': [a['geometry']['coordinates'][:2], b['geometry']['coordinates'][:2]]},
    })
await fieldra.add_layer('Walking order', lines)

numpy and pandas

Downloaded the first time they are imported (or beforehand, with Packages for offline use), then kept on the computer.

Python
import numpy as np
import pandas as pd
import fieldra

pts = await fieldra.layer('Water points')
df = pd.DataFrame([f['properties'] for f in pts['features']])
print(df.groupby('village')['depth'].agg(['count', 'mean', 'max']))

xy = np.array([f['geometry']['coordinates'][:2] for f in pts['features'] if f['geometry']])
print('centre', xy.mean(axis=0))

Deep boreholes, selected and buffered

Chain tools by passing one result’s id to the next.

JavaScript
const deep = await fieldra.run('select', { input: 'Water points', where: '[depth] > 30' });
const zones = await fieldra.run('buffer', { input: deep.id, meters: 500, dissolve: true });
print('Deep boreholes:', deep.count, '· buffer layer', zones.name);

Area of each parcel with turf

turf is ready to use in JavaScript scripts, with no import.

JavaScript
const parcels = await fieldra.layer('Parcels');
for (const f of parcels.features) f.properties.area_ha = +(turf.area(f) / 10000).toFixed(3);
await fieldra.updateLayer('Parcels', parcels);
print('Updated', parcels.features.length, 'parcels');

Slope classes of an elevation model

Raster tools take images by name and add their result to Images.

JavaScript
const slope = await fieldra.run('slope', { raster: 'DSM' });
const classes = await fieldra.run('reclassify', { raster: slope.id, rules: '0-5=1; 5-15=2; 15-90=3' });
const zones = await fieldra.run('rasterToPolygons', { raster: classes.id });
print('Slope classes:', zones.count, 'areas');

Mean height in each parcel

Zonal statistics adds zone_count, zone_mean, zone_min, zone_max and zone_sum to each area.

Python
import fieldra

stats = await fieldra.run('zonalStats', areas='Parcels', raster='DSM', band=1)
for f in (await fieldra.layer(stats['id']))['features']:
    p = f['properties']
    print(p.get('name'), 'mean', p['zone_mean'], '· min', p['zone_min'], '· max', p['zone_max'])

Limits