Campaign

A Campaign organises a study area, airspace data, flight lines, reusable patterns, and line groups into a single object that can be saved to and loaded from a plain folder.

Folder structure

my_campaign/
  campaign.json        # name, version, campaign_id, revision metadata
  domain.geojson       # study area polygon
  airspaces.json       # raw OpenAIP items (re-parsed on load)
  flight_lines/
    all_lines.geojson  # free-standing flight lines only
    groups.json        # logical groupings with generation params
  patterns/
    all_patterns.json  # serialized Pattern objects

What changed in the pattern-aware campaign model

  • Campaigns now manage both free-standing lines and first-class Pattern objects.

  • Line-based patterns receive stable campaign-global line_id values when added to a campaign.

  • Campaign mutation bumps revision and updated_at, which makes the object easier to synchronize with interactive clients.

  • patterns_to_geojson() exposes waypoint-based pattern geometry for map display, while flight_lines_to_geojson() continues to expose all line geometry.

Common workflow

  1. Build a pattern with a generator such as racetrack().

  2. Add it to a campaign with add_pattern().

  3. Edit the pattern or individual legs with replace_pattern(), replace_line_anywhere(), or replace_line().

  4. Save the campaign and use the stored IDs and revision metadata to track changes across planning sessions.

Campaign class

class Campaign[source]

Bases: object

A flight campaign with geographic domain, reference data, and flight lines.

Parameters:
  • name (str) – Human-readable campaign name.

  • bounds (Optional[Tuple[float, float, float, float]]) – (min_lon, min_lat, max_lon, max_lat) bounding box. Mutually exclusive with polygon.

  • polygon (Optional[Polygon]) – Shapely Polygon defining the domain boundary. Mutually exclusive with bounds.

  • country (Optional[str]) – Optional ISO 2-letter country code to filter API results.

Raises:

HyPlanValueError – If neither or both of bounds/polygon are given, or if coordinates are out of range.

__init__(name, bounds=None, polygon=None, country=None)[source]
Parameters:
property name: str
property polygon: Polygon
property bounds: Tuple[float, float, float, float]
property country: str | None
property airspaces: List[Airspace] | None
property is_fetched: bool
property flight_lines: List[FlightLine]
property flight_line_ids: List[str]
property groups: List[dict]
property patterns: List[Pattern]

All patterns attached to this campaign, in insertion order.

property pattern_ids: List[str]
property campaign_id: str
property revision: int
property updated_at: str
fetch_airspaces(api_key=None, force=False)[source]

Fetch airspaces from OpenAIP for this domain.

If data is already loaded (from a previous fetch or from load()), this is a no-op unless force=True.

Returns self for chaining.

Return type:

Campaign

Parameters:
  • api_key (str | None)

  • force (bool)

check_conflicts(flight_lines=None)[source]

Check flight lines against this campaign’s airspaces.

Parameters:

flight_lines – Lines to check. If None, checks this campaign’s own flight lines.

Raises:

HyPlanRuntimeError – If airspace data has not been fetched.

Return type:

List[AirspaceConflict]

add_flight_lines(lines, group_name=None, group_type='manual', generation_params=None)[source]

Add flight lines to the campaign and create a group.

Parameters:
  • lines (List[FlightLine]) – Flight lines to add.

  • group_name (Optional[str]) – Human-readable group name. Defaults to "group_NNN".

  • group_type (str) – Group type label ("flight_box", "pattern", "single_line", "manual").

  • generation_params (Optional[dict]) – Optional dict recording how the lines were generated (method, parameters) for reproducibility.

Return type:

str

Returns:

The group ID string.

remove_group(group_id)[source]

Remove a group and its flight lines from the campaign.

Return type:

None

Parameters:

group_id (str)

remove_flight_line(line_id)[source]

Remove a flight line by ID and update group memberships.

Removes line_id from the campaign-wide flight-line registry and from any groups that reference it. Groups left with no remaining lines are removed.

Parameters:

line_id (str) – Stable campaign line ID, e.g. "line_001".

Raises:

HyPlanValueError – If line_id is not present in the campaign.

Return type:

None

replace_flight_line(line_id, line)[source]

Replace an existing flight line in place, preserving its ID.

Parameters:
Raises:
Return type:

None

flight_lines_to_geojson()[source]

Return all campaign flight lines as a GeoJSON FeatureCollection.

Each feature includes the stable line_id in both the feature id field and in properties.line_id. Includes free-standing lines as well as flight lines that belong to line-based patterns; pattern lines carry pattern_id and pattern_kind in their properties.

Return type:

dict

add_pattern(pattern)[source]

Add a pattern to the campaign.

Assigns a stable pattern_id and rekeys any contained flight lines with campaign-global line_id values. The input Pattern is mutated in place so the caller holds the live reference.

Parameters:

pattern (Pattern) – A Pattern returned by a generator.

Return type:

str

Returns:

The assigned pattern_id.

remove_pattern(pattern_id)[source]

Remove a pattern and all of its contained flight lines or waypoints.

Return type:

None

Parameters:

pattern_id (str)

replace_pattern(pattern_id, new_pattern)[source]

Replace a pattern in place, preserving its pattern_id.

Contained flight lines receive fresh campaign-global line_id values — callers that want to preserve individual line IDs should use Pattern.replace_line() instead.

Return type:

None

Parameters:
get_pattern(pattern_id)[source]

Return the Pattern with the given id (raises if not found).

Return type:

Pattern

Parameters:

pattern_id (str)

patterns_to_geojson()[source]

Return waypoint-based patterns as one GeoJSON FeatureCollection for display.

Only waypoint-based patterns (polygon, sawtooth, spiral) contribute features here; their geometry (Point waypoints + connecting track) is not available elsewhere. Line-based patterns (rosette, racetrack) are intentionally omitted because their legs are already exposed through flight_lines_to_geojson(); emitting them again would cause duplicate rendering in map clients.

Return type:

dict

get_line(line_id)[source]

Return the FlightLine with line_id from free-standing lines or any line-based pattern. Raises if not found.

Return type:

FlightLine

Parameters:

line_id (str)

find_pattern_for_line(line_id)[source]

Return the Pattern owning line_id, or None if the line is free-standing or does not exist.

Return type:

Optional[Pattern]

Parameters:

line_id (str)

all_flight_lines()[source]

Return all FlightLines in the campaign: free-standing plus pattern-owned lines, in a stable order.

Return type:

List[FlightLine]

all_flight_lines_dict()[source]

Return {line_id: FlightLine} for every flight line in the campaign — free-standing or pattern-owned.

Return type:

Dict[str, FlightLine]

replace_line_anywhere(line_id, line)[source]

Replace a flight line by id whether free-standing or pattern-owned.

Routes to replace_flight_line() for free-standing lines or Pattern.replace_line() on the owning pattern.

Return type:

None

Parameters:
remove_line_anywhere(line_id)[source]

Remove a flight line by id whether free-standing or pattern-owned.

Free-standing removal uses remove_flight_line() (deletes the line and trims empty groups). Pattern-owned removal drops the leg from the pattern and, if the pattern has no legs left, removes the pattern entirely.

Return type:

None

Parameters:

line_id (str)

save(path)[source]

Save the campaign to a folder.

Creates the directory structure and writes all files. Existing files are overwritten.

Parameters:

path (str) – Path to the campaign folder.

Return type:

None

classmethod load(path)[source]

Load a campaign from a previously saved folder.

No API calls are made. Raw airspace items are re-parsed.

Parameters:

path (str) – Path to the campaign folder.

Return type:

Campaign

Returns:

A fully populated Campaign instance.

summary()[source]

Return a human-readable summary of the campaign.

Return type:

str