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
- Open a project and go to Analysis › Script.
- Choose Python or JavaScript. Python can be the built-in one (works offline) or one installed on the computer.
- Write the script, then press Run (or Ctrl/⌘ Enter). What it prints appears below the editor; layers it adds appear on the map.
- 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.
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')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
| Runtime | Offline | Libraries |
|---|---|---|
| Built-in Python | Yes | Python 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 computer | Yes | Any 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. |
| JavaScript | Yes | Modern 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.
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 }
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
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
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
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 }
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).
[
{ "id": "u-3f2a", "name": "Water points", "count": 128, "editable": true },
{ "id": "survey:water", "name": "Water quality", "count": 54, "editable": false }
]{
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": { "name": "BH-12", "depth": 34.5 },
"geometry": { "type": "Point", "coordinates": [-13.234, 8.484] }
}]
}{ "id": "u-91bc", "name": "Water points buffer", "count": 128 }{ "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
A zone of a set distance around each feature.
- inputlayerInput
- metersnumberDistance (m) · default 50
- dissolveboolDissolve all into one · default false
Overlay
The input inside the overlay areas.
- inputlayerInput
- overlaylayerOverlay layer
The input outside the overlay areas.
- inputlayerInput
- overlaylayerOverlay layer
Where the input and the overlay areas overlap, with both attributes.
- inputlayerInput
- overlaylayerOverlay layer
Two area layers combined into every piece.
- inputlayerInput
- overlaylayerOverlay layer
The input cut by the overlay areas; pieces inside get both attributes.
- inputlayerInput
- overlaylayerOverlay layer
Where only one of two area layers is.
- inputlayerInput
- overlaylayerOverlay layer
The input with the overlay areas put in place of what they cover.
- inputlayerInput
- overlaylayerOverlay layer
Area (or length, count) of each overlay class in each input area.
- inputlayerInput
- overlaylayerOverlay layer
- nameFieldfield of overlayClasses from · optional
Attributes of the overlay feature each input feature touches.
- inputlayerInput
- overlaylayerOverlay layer
- nearestMnumberOr the nearest within (m) · default 0
Count (and total a field of) the overlay features in each input area.
- inputlayerInput
- overlaylayerOverlay layer
- statFieldfield of overlayField to total · optional
Distance to the nearest overlay feature.
- inputlayerInput
- overlaylayerOverlay layer
- nameFieldfield of overlayName it by · optional
Lines to the nearest overlay features, with distances.
- inputlayerInput
- overlaylayerOverlay layer
- nearestCountnumberHow many nearest · default 1
- maxDistanceMnumberWithin (m, 0 = any) · default 0
Both layers’ features in one layer.
- inputlayerInput
- overlaylayerOverlay layer
Generalise
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
Merge areas smaller than a size into their neighbour.
- inputlayerInput
- minAreanumberSmaller than (m²) · default 100
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
The smallest area enclosing all features.
- inputlayerInput
A point at the centre of each feature.
- inputlayerInput
Triangles joining points.
- inputlayerPoints
Fields
The features matching an expression, as a new layer.
- inputlayerInput
- whereexpressionWhere
A copy with a field filled from an expression.
- inputlayerInput
- fieldtextField · default 'value'
- expressionexpressionExpression
Raster
Slope from an elevation model.
- rasterrasterElevation model
- bandnumberBand · default 1
Aspect from an elevation model.
- rasterrasterElevation model
- bandnumberBand · default 1
Hillshade from an elevation model.
- rasterrasterElevation model
- bandnumberBand · default 1
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'
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
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'
The image at another cell size.
- rasterrasterImage
- cellnumberCell size (m) · default 5
- methodchoiceMethod · one of 'bilinear', 'nearest' · default 'bilinear'
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'
Cut an image to the areas of a layer.
- rasterrasterImage
- areaslayerAreas
The mean, lowest, highest and total of an image in each area (added to the areas).
- areaslayerAreas
- rasterrasterImage
- bandnumberBand · default 1
Areas of equal whole-number value (gridcode).
- rasterrasterImage
- bandnumberBand · default 1
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
Accumulated cost from source points over a cost image.
- rasterrasterCost image
- bandnumberBand · default 1
- sourceslayerSource points
(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.
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.
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.
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.
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.
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.
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.
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.
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
Images are reached through tools
Scripts read and write layers directly; images (rasters) are used through the raster tools, whose results are new images. There is no call yet to read an image’s pixels into numpy.
Built-in Python packages
Pure-Python packages and those built for the browser’s Python (numpy, pandas, shapely, scipy, scikit-learn and many more) work. Packages with other native code (GDAL’s Python bindings, rasterio, geopandas’ file reading) need the computer’s own Python.
First import needs the internet
A package is downloaded the first time it is imported, then kept. Use Packages for offline use before going to the field.
Files
Built-in Python and JavaScript can’t open files on the computer: add them to the project first (Add data), then read them as layers. The computer’s own Python can open files as usual.
JavaScript imports
JavaScript scripts can’t import packages; turf is provided. Top-level await works.
Survey layers are read-only
update_layer changes only your own layers. To change survey data, add a layer with the new values, or use the attribute table.
One script at a time
A script runs apart from the map, so the app stays responsive, and Stop ends it. Run is available again once it finishes or is stopped.