Skip to main content

Agriculture — precision farming

The problem. A farm is a patchwork of fields, each with soil sensors streaming moisture and temperature, and each periodically photographed from a drone or satellite to spot disease and growth variation. The agronomist needs the sensor trends and the imagery in one place, tied to the field they belong to, so a dry-soil reading and a stressed-crop image line up.

This scenario is the one where files matter as much as numbers: attach imagery and scouting reports to the field, alongside its sensor series.

Set up demo data​

New workspace? Run this once (Python) to create the field's soil series with two days of readings that dry out toward the irrigation threshold. (The drone-scan upload in step 2 needs a local image file — any file works.) Safe to re-run.

import intellistream_datahub_sdk, numpy as np, pandas as pd

client = intellistream_datahub_sdk.DataHubClient.from_env()

for s, u in [("field_north_40_soil_moisture", "pct"), ("field_north_40_soil_temp_c", "deg_c")]:
client.timeseries.create([intellistream_datahub_sdk.TimeSeries(external_id=s, name=s, unit=u)])
idx = pd.date_range(end=pd.Timestamp.now(tz="UTC"), periods=48, freq="1h")
moisture = 35 - np.linspace(0, 15, 48) + np.random.normal(0, 1, 48) # drying out
client.timeseries.insert_from_lists(timestamps=idx, values=moisture, ts="field_north_40_soil_moisture")

1. Model fields and stream soil sensors​

A farm contains fields; each field's sensors are series.

var moisture = Timeseries.of("field_north_40_soil_moisture").name("North 40 soil moisture");
moisture.setUnit("pct");
var soilTemp = Timeseries.of("field_north_40_soil_temp_c").name("North 40 soil temperature");
soilTemp.setUnit("deg_c");
client.timeseries().create(moisture, soilTemp);

client.timeseries().ingest(Map.of(
"field_north_40_soil_moisture", moistureReadings)); // List<Datapoint>

2. Attach the drone imagery to the field​

Upload each scan into the field's folder, tagged so it's discoverable next to the field's sensor data. See Attach files to assets for the upload details.

byte[] scan = Files.readAllBytes(Path.of("north_40_2026_06_28.tif"));

client.files().upload(
FileUploadRequest.builder()
.path("fields/north_40/scans/2026_06_28.tif")
.content(scan)
.contentType("image/tiff")
.externalId("scan_north_40_2026_06_28")
.description("Drone NDVI scan — North 40")
.source("drone_survey")
.build());

Later, list a field's folder to pull up its scan history alongside the soil trend:

client.files().list("/fields/north_40/scans")
.getItems().forEach(node -> System.out.println(node.getName()));

3. Trigger irrigation on a dry trend​

When soil moisture stays below target through the heat of the day, raise an irrigation_needed event the irrigation controller acts on.

See the result​

Chart the soil moisture — the downward trend is what triggers irrigation:

import matplotlib.pyplot as plt

rf = intellistream_datahub_sdk.RetrieveFilter(ts="field_north_40_soil_moisture",
start=pd.Timestamp.now(tz="UTC") - pd.Timedelta(days=2), end=pd.Timestamp.now(tz="UTC"))
s = pd.Series({pd.to_datetime(p.timestamp): float(p.value)
for p in client.timeseries.retrieve_datapoints(rf)[0].get_datapoints()}).sort_index()
s.plot(title="North 40 soil moisture (%)"); plt.show()

See also​