Aircraft Orientation Indicators#

from pathlib import Path
from dessia_drawing.core import Drawing
from drawing_tools.config.default_language_configs import DEFAULT_ENGLISH_CONFIG, DEFAULT_FRENCH_CONFIG
from drawing_tools.featured_drawing import FeaturedDrawing

_DATA_DIR = Path("data/json")
_LANGUAGE_CONFIGS = [DEFAULT_FRENCH_CONFIG, DEFAULT_ENGLISH_CONFIG]

formation_drawing = Drawing.from_json(
    str(_DATA_DIR / "condor/condor_TEMP_332P641283_--A_DRW01_FORMATION_LIGHT_ASSY.json")
)
formation = FeaturedDrawing(formation_drawing, language_configs=_LANGUAGE_CONFIGS)
featured_sheet = formation.sheets[4]
featured_view = featured_sheet.views[6]
featured_view.plot_data_aircraft_orientation().plot()

An aircraft orientation indicator tells the reader which way the aircraft lies on a view — where its front is, or where its top is — so the part depicted can be positioned on the aircraft. It is the repère appareil of the positioning rule of the drawing standard (structural elements helping the reader position the part): an arrow drawn together with a bilingual caption, the French line above the English one.

See also

  • Aircraft Reference Lines — the other element of the positioning rule, detected in the same view/annotations/aircraft/ subpackage.

  • The script scripts/detect_aircraft_features.py in the repository, which reports this detection together with the reference lines.

What is an Aircraft Orientation Indicator?#

The element lives in the view/annotations/aircraft/ subpackage, alongside the aircraft reference lines it shares the positioning rule with: that package groups what a drawing carries to position the assembly in the aircraft, by the question those elements answer rather than by the shape they take on the sheet.

Two things drawn together, and both are needed:

  • the caption — one text line per language, which is what carries the meaning. Two directions are named on the reference drawings: the front, AVANT APPAREIL / FRONT AIRCRAFT, and the top, HAUT APPAREIL / TOP AIRCRAFT (also written UPPER AIRCRAFT, the mixed TOP APPAREIL, or bare HAUT / TOP);

  • the arrow — a chevron head, a two-line shaft and a small tail feather, pointing towards the named direction of the aircraft, which is what makes the caption a positioning element rather than a plain note.

The arrow is drawn along whatever direction the front (or the top) takes in that view: upward on a top view, sideways on a section, along the isometric axis on an isometric view — and the caption is rotated with it. Nothing in the detection depends on that direction.

Accessing the Indicators#

Indicators are detected on each FeaturedView and aggregated at sheet and drawing level, like every other detection of the package:

for view in featured_sheet.views:
    if view.aircraft_orientation_indicators:
        print(f"{view.view.name}: {len(view.aircraft_orientation_indicators)} indicator(s)")

print(f"{len(featured_sheet.aircraft_orientation_indicators)} indicator(s) on the sheet")

The list is the whole API: the positioning rule asks “does this view carry one” view by view, and an empty list is that answer. No has_ or _count property duplicates it.

What the caption says, per language#

Each language of the configured language configurations that recognized the caption maps to the line that matched it:

indicator = featured_view.aircraft_orientation_indicators[0]

print(indicator.languages)                        # ['english', 'french']
print(indicator.text_for_language("french"))       # AVANT APPAREIL
print(indicator.text_for_language("english"))      # FRONT AIRCRAFT
print(indicator.text_by_language)

A language whose patterns match nothing is simply absent from text_by_language, and text_for_language returns None for it — so a single-language caption stays detectable and states which language it is in.

OrientationIndicator Properties#

Property

Description

text_by_language

Language name → the caption line that matched it, e.g. {"french": "AVANT APPAREIL", "english": "FRONT AIRCRAFT"}.

languages

Names of the languages the caption was recognized in, alphabetically.

text_for_language(name)

The caption line for one language, None when that language matched none.

text_content

The whole caption as exported, every language line joined.

arrow_edges

The arrow strokes, as exported (the reference drawings draw 10 edges for 8 distinct strokes). Redrawn by the overlay.

caption_symbol

The nested text entity carrying the caption.

source_entity

The CompositeEntity the indicator was detected on — also where bounding_rectangle and drawing_address come from.

overlay_lines / tooltip

The lines the overlay draws and shows on hover.

Configuring the caption#

The captions to recognize are not in the detector: they are aircraft_orientation_patterns on each LanguageConfig, so a new wording is added by configuration rather than by code:

from drawing_tools.config.default_language_configs import (
    DEFAULT_FRENCH_CONFIG,
    ENGLISH_AIRCRAFT_ORIENTATION_PATTERNS,
    FRENCH_AIRCRAFT_ORIENTATION_PATTERNS,
)

print(FRENCH_AIRCRAFT_ORIENTATION_PATTERNS)   # ['AVANT\\s+APPAREIL', 'HAUT\\s+APPAREIL', '^HAUT$']
print(ENGLISH_AIRCRAFT_ORIENTATION_PATTERNS)  # ['FRONT\\s+AIRCRAFT', 'TOP\\s+AIRCRAFT', ..., '^TOP$']
print(DEFAULT_FRENCH_CONFIG.aircraft_orientation_patterns)

The defaults name the two directions attested on the reference drawings — the front (AVANT APPAREIL / FRONT AIRCRAFT) and the top (HAUT APPAREIL, whose English line is TOP AIRCRAFT, UPPER AIRCRAFT or the mixed TOP APPAREIL depending on the plan, and the bare HAUT / TOP). Each pattern is searched in every line of the caption, so the two bare words are anchored to the whole line: TOP claims neither STOP nor a TOP VIEW title. Both directions are detected the same way, as the same OrientationIndicator: what distinguishes a top indicator from a front one is what text_for_language returns. The language name is what keys the detected texts, so a configuration named "french" yields text_by_language["french"].

How the detection works#

One CompositeEntity is one indicator, recognized by two criteria in that order:

  1. the caption — one of the composite’s own nested texts matches the orientation patterns of at least one language configuration. Measured on the reference drawings, this criterion alone selects the 16 indicators out of 72 composites, with no false positive; the CAD grouping is authoritative, so the caption needs no proximity search.

  2. the arrow — the composite draws at least min_arrow_edge_count graphic edges (two, the fewest an arrow head can be drawn with). Every indicator of the reference drawings draws 10, so this bound rejects none of them: what it rejects is a caption with no arrow at all, which the rule does not accept as a positioning element. Such a case is logged as a warning rather than silently dropped.

from drawing_tools.view.annotations.aircraft.view_orientation_detector import (
    OrientationDetectionConfig,
    ViewOrientationDetector,
)

# The value below is the default: passing no config detects every indicator of the
# reference drawings. Build one only to tune the detection on a new export.
config = OrientationDetectionConfig(min_arrow_edge_count=2)
detector = ViewOrientationDetector(
    featured_view.view, language_configs=_LANGUAGE_CONFIGS, config=config
)
print(f"{len(detector.indicators)} indicator(s) detected")

# Same result, with the detector's own French and English defaults:
print(len(ViewOrientationDetector(featured_view.view).indicators))

Note

Unlike the view title, this detection does not require language configurations: without them the detector falls back to its French and English defaults, so a language-free FeaturedView still reports its indicators.

To see the code behind these two criteria, see Going Further at the end of this page.

The arrow’s direction is deliberately not computed. The rule asks whether the view carries the element and what it says; turning the strokes into a front direction would be a separate feature, and no rule requires it yet.

Visualization#

Every detected indicator is drawn by plot_data_aircraft_orientation on view, sheet and drawing level (and stacked into plot_data_all_overlays): one palette color per indicator, a rectangle around the whole element, its arrow strokes redrawn in that color, a caption — also the hover tooltip — stating what the element says in each language, and the containing view outlined in that same color (outline only, no fill). Reading the same color on the element and on the view frame is what confirms the element was detected in that view, which a sheet-level overlay could not otherwise show. The frame is built by overlays.featured.build_detection_view_frame_primitive, reusable by any other detection.

for line in featured_view.aircraft_orientation_indicators[0].overlay_lines:
    print(line)

On a whole sheet#

Called on a FeaturedSheet, the overlay marks every indicator of the sheet and outlines each containing view in the indicator’s color. The element is a few units wide on a sheet a thousand units across: at this scale it is the view frames that show, at a glance, which views carry an indicator and which do not — the question the positioning rule asks. Here four of the six named views carry one; the auxiliary view VUE SUIVANT F1- and the section SECTION C-C TYP. do not.

Source: condor_TEMP_332P641283_–A_DRW01_FORMATION_LIGHT_ASSY.json, Sheet 1 (4 indicators)

formation.sheets[1].plot_data_aircraft_orientation().plot()

On a single view#

Called on a FeaturedView, the same overlay is drawn at the scale of that view: the arrow strokes redrawn in the indicator’s color, the caption stating what each language says under the element.

On a section view the arrow lies along the cut.

Source: condor_TEMP_332P641283_–A_DRW01_FORMATION_LIGHT_ASSY.json, Sheet 4, view COUPE E-E (1 indicator)

formation.sheets[4].views[5].plot_data_aircraft_orientation().plot()

On an isometric view both the arrow and its caption run along the isometric axis. The detection is unaffected, since it reads the caption text, never an angle.

Source: condor_TEMP_332P641283_–A_DRW01_FORMATION_LIGHT_ASSY.json, Sheet 4, view VUE ISOMETRIQUE (1 indicator)

formation.sheets[4].views[2].plot_data_aircraft_orientation().plot()

See also

Display & Report Methods shows the sheet-level overlay among every other FeaturedSheet rendering, with its download link.

Note

The caption is placed under the element, so in a dense area it can cross the drawing’s own texts (as every diagnostic overlay of the package does). The tooltip carries the same lines on hover when the drawing underneath matters.

Going Further: Inside the Detection#

For readers who want to see how the detector works

Two classes carry this feature, both in view/annotations/aircraft/:

  • the featureOrientationIndicator (orientation_indicators.py), the value object described above;

  • the detectorViewOrientationDetector (view_orientation_detector.py), which holds the recipe and its OrientationDetectionConfig. FeaturedView.aircraft_orientation_indicators builds one per view with the view’s language configurations; the sheet and drawing properties concatenate the results.

detect_all maps one method over the view’s CompositeEntity annotations (_composites); that method is the two criteria of How the detection works:

def _indicator_from_composite(self, composite: CompositeEntity) -> OrientationIndicator | None:
    """Build an indicator from a composite, or None when it fails one of the two criteria.

    The caption comes first: it is the discriminating criterion (measured on the reference
    drawings, it selects the 16 indicators out of 72 composites, with no false positive), and
    it is cheaper than reading the geometry -- ``geometries`` re-applies the entity
    transformation on every access.

    A caption drawn with no arrow is logged rather than silently dropped: the rule wants an
    arrow, so a bare caption is a case a human should look at.
    """
    caption_symbol, text_by_language = self._identify_caption(composite)
    if caption_symbol is None:
        return None
    arrow_edges = [geometry for geometry in composite.geometries if isinstance(geometry, Edge)]
    if len(arrow_edges) < self.config.min_arrow_edge_count:
        logger.warning(
            "Aircraft orientation caption %r found with %d graphic edge(s), no arrow drawn: %s",
            caption_symbol.get_text_content().strip(),
            len(arrow_edges),
            composite,
        )
        return None
    return OrientationIndicator(
        source_entity=composite,
        arrow_edges=arrow_edges,
        caption_symbol=caption_symbol,
        text_by_language=text_by_language,
    )
  • _identify_caption walks the composite’s own text entities and returns the first one that _match_text_by_language recognizes — that helper tries the aircraft_orientation_patterns of each LanguageConfig and records, per language, the caption line that matched;

  • the arrow check is inline: at least min_arrow_edge_count Edge geometries in the composite, a bare caption being logged rather than dropped.