SecInterp - Detailed Project Architecture

Comprehensive Technical Documentation for the SecInterp QGIS Plugin Version 3.11.0 | Last Updated: 2026-10-24


πŸ“‘ Table of Contents

  1. Overview

  2. Directory Structure

  3. System Architecture

  4. GUI Layer - User Interface

  5. Core Layer - Business Logic

  6. Exporters Layer - Data Export

  7. Design Principles

  8. Extensibility

  9. Project Metrics

  10. Quality Assurance & Security Scanning


🎯 Overview

SecInterp (Section Interpreter) is a QGIS plugin designed for extracting and visualizing geological data in cross-sections. The plugin allows geologists to generate topographic profiles, project geological outcrops, and analyze structural data in a unified 2D view.

Key Features

  • βœ… Interactive Preview System with real-time rendering and adaptive LOD (Level of Detail) based on zoom.

  • βœ… Parallel Processing for complex geological intersections.

  • βœ… DEM & Profile Insights: DEM band statistics and profile-vs-DEM min/max/mean, plus gradient/single-color profile styling.

  • βœ… CRS Safety: mixed-CRS warnings and a blocking check for mislabelled layer CRS.

  • βœ… Adaptive Vertical Exaggeration (auto, clamped 0.5×–20Γ—) with manual override.

  • βœ… Optional Smoothed Topography profile (distance-window moving average) with matching smoothed geology.

  • βœ… Measurement Tools with automatic snapping.

  • βœ… Drillhole Support with 3Dβ†’2D trajectory projection.

  • βœ… Multi-format Export (SHP, GPKG, DXF, CSV, PDF, SVG, PNG).


πŸ“‚ Directory Structure

The project organization follows a highly modular architecture based on the Separation of Concerns (SoC) principle, decoupling the interface, business logic, and export formats. Each layer has a distinct responsibility and communicates through well-defined interfaces.

[!tip] En Obsidian este Γ‘rbol es navegable: cada [slug](https://github.com/geociencio/sec_interp/blob/main/docs/code_walkthrough_v2/slug.md) abre la nota de la bΓ³veda y alimenta el Graph View. En GitHub/Sphinx usa el link [doc](https://github.com/geociencio/sec_interp/blob/main/docs/code_walkthrough_v2/slug.md) relativo. Los espejos en docs/code_walkthrough*/ARCHITECTURE_EN.md se re-sincronizan con bash scripts/sync_vault_mirrors.sh.


πŸ—οΈ System Architecture

Complete Architecture Diagram

        graph TD
    classDef module fill:#f9f,stroke:#333,stroke-width:2px;
    classDef manager fill:#e1f5fe,stroke:#0277bd,stroke-width:2px;
    classDef service fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px;
    classDef interface fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,stroke-dasharray: 5 5;
    classDef exporter fill:#fce4ec,stroke:#c2185b,stroke-width:2px;
    classDef task fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px;
    classDef renderer fill:#e0f2f1,stroke:#00695c,stroke-width:2px;

    %% ========== ENTRY POINT ==========
    QGIS[QGIS Application]
    PLUGIN[sec_interp_plugin.py<br/>SecInterp Class<br/>Plugin Root]

    %% ========== GUI LAYER ==========
    subgraph GUI["πŸ–₯️ GUI Layer - User Interface"]
        direction TB

        MAIN[main_dialog.py<br/>SecInterpDialog]

        subgraph MANAGERS["Managers (Orchestration)"]
            direction TB
            SIGNALS_MGR[dialog_signal_manager.py<br/>DialogSignalManager]:::manager
            INPUT_MGR[dialog_input_manager.py<br/>DialogInputManager]:::manager
            PREVIEW_MGR[dialog_preview_manager.py<br/>PreviewManager]:::manager
            EXPORT_MGR[dialog_export_manager.py<br/>ExportManager]:::manager
            INTERP_MGR[dialog_interpretation_manager.py<br/>InterpretationManager]:::manager
            STATE_MGR[dialog_state_manager.py<br/>DialogStateManager]:::manager
            SETTINGS_MGR[dialog_settings_persistence.py<br/>SettingsPersistence]:::manager
            TOOL_MGR[dialog_tool_manager.py<br/>ToolManager]:::manager
            LAYER_NOTIF[layer_notification_manager.py<br/>LayerNotificationManager]:::manager
            UI_STATUS[ui_status_manager.py<br/>UIStatusManager]:::manager
        end

        subgraph RENDERING["Rendering Engine"]
            direction TB
            LAYER_FACTORY[preview_layer_factory.py<br/>PreviewLayerFactory]
            AXES_MGR[preview_axes_manager.py<br/>PreviewAxesManager]
            RENDERER[preview_renderer.py<br/>PreviewRenderer]
            REPORTER[preview_reporter.py<br/>PreviewReporter]
            PARAM_HASH[preview_param_hasher.py<br/>PreviewParamHasher]
            PREVIEW_STATE[preview_state.py<br/>PreviewState]
        end

        subgraph RENDERERS["Specialized Renderers"]
            direction TB
            BASE_RENDERER[renderers/base_renderer.py<br/>BaseRenderer]:::renderer
            TOPO_RENDERER[renderers/topo_renderer.py<br/>TopoRenderer]:::renderer
            GEOLOGY_RENDERER[renderers/geology_renderer.py<br/>GeologyRenderer]:::renderer
            DRILLHOLE_RENDERER[renderers/drillhole_renderer.py<br/>DrillholeRenderer]:::renderer
            STRUCT_RENDERER[renderers/structure_renderer.py<br/>StructureRenderer]:::renderer
            INTERP_RENDERER[renderers/interpretation_renderer.py<br/>InterpretationRenderer]:::renderer
            COLOR_MGR[renderers/color_manager.py<br/>ColorManager]:::renderer
        end

        subgraph WORKERS["Async Tasks (QgsTask)"]
            direction TB
            TASK_ORCH[preview_task_orchestrator.py<br/>PreviewTaskOrchestrator]:::task
            GEOLOGY_TASK[tasks/geology_task.py<br/>GeologyTask]:::task
            DRILL_TASK[tasks/drillhole_task.py<br/>DrillholeTask]:::task
        end

        subgraph TOOLS["Map Tools"]
            direction TB
            MAP_TOOLS[tools/<br/>QgsMapTool Implementations]
        end

        subgraph UI_PAGES["UI Components"]
            direction TB
            PAGES[ui/pages/<br/>Tab-based Pages]
            ADAPTERS[adapters/<br/>UI Adapters]
            SIDE_PANEL[preview_side_panel.py<br/>Preview Side Panel]
            SYMBOLOGY[symbology_tab.py<br/>Live Symbology]
        end

        MAIN --> MANAGERS
        MAIN --> RENDERING
        MAIN --> RENDERERS
        MAIN --> WORKERS
        MAIN --> TOOLS
        MAIN --> UI_PAGES
        MAIN --> SIDE_PANEL
        MAIN --> SYMBOLOGY

        MANAGERS --> RENDERING
        MANAGERS --> WORKERS
        MANAGERS --> UI_PAGES

        WORKERS --> TASK_ORCH
        RENDERING --> RENDERERS
    end

    %% ========== CORE LAYER ==========
    subgraph CORE["βš™οΈ Core Layer - Business Logic"]
        direction TB

        CONTROLLER[controller.py<br/>ProfileController]

        subgraph INTERFACES["Abstractions (DI) - interfaces/"]
            direction TB
            I_GEOL[interfaces/geology_interface.py<br/>IGeologyService]:::interface
            I_DRILL[interfaces/drillhole_interface.py<br/>IDrillholeService]:::interface
            I_STRUCT[interfaces/structure_interface.py<br/>IStructureService]:::interface
            I_PREVIEW[interfaces/preview_interface.py<br/>IPreviewService]:::interface
            I_CACHE[interfaces/cache_interface.py<br/>ICacheService]:::interface
            I_RENDER3D[interfaces/i_renderer_3d.py<br/>IRenderer3D]:::interface
        end

        subgraph SERVICES["Concrete Services - services/"]
            direction TB
            GEOL_SVC[services/geology_service.py<br/>GeologyService]:::service
            STRUCT_SVC[services/structure_service.py<br/>StructureService]:::service
            DRILL_SVC[services/drillhole_service.py<br/>DrillholeService]:::service
            EXPORT_SVC[services/export_service.py<br/>ExportService]:::service
            PREVIEW_SVC[services/preview_service.py<br/>PreviewService]:::service
            VE_SVC[services/vertical_exaggeration_service.py<br/>VerticalExaggerationService]:::service
            ACCESS_SVC[services/access_control_service.py<br/>AccessControlService]:::service

            subgraph DRILLHOLE_PKG["Drillhole Sub-system"]
                direction TB
                COLLAR_PROC[drillhole/collar_processor.py<br/>CollarProcessor]
                SURVEY_PROC[drillhole/survey_processor.py<br/>SurveyProcessor]
                INTERVAL_PROC[drillhole/interval_processor.py<br/>IntervalProcessor]
                PROJ_ENGINE[drillhole/projection_engine.py<br/>ProjectionEngine]
                TRAJ_ENGINE[drillhole/trajectory_engine.py<br/>TrajectoryEngine]

                DRILL_SVC --> COLLAR_PROC
                DRILL_SVC --> SURVEY_PROC
                DRILL_SVC --> INTERVAL_PROC
                DRILL_SVC --> TRAJ_ENGINE
                COLLAR_PROC --> PROJ_ENGINE
                SURVEY_PROC --> TRAJ_ENGINE
                TRAJ_ENGINE --> PROJ_ENGINE
            end
        end

        subgraph MODELS["Domain Models - models/"]
            direction TB
            SETTINGS_MODEL[models/settings_model.py<br/>SettingsModel]
        end

        subgraph DOMAIN["Domain Layer - domain/"]
            direction TB
            ENTITIES[Entities & DTOs<br/>ProfileData, GeologySegment,<br/>DrillholeData, etc.]
        end

        subgraph VALIDATION["Validation Pipeline - validation/"]
            direction TB
            BASE_VAL[base_validator.py<br/>BaseValidator]
            PIPELINE[pipeline.py<br/>ValidationPipeline]
            LAYER_VAL[layer_validator.py<br/>LayerValidator]
            FIELD_VAL[field_validator.py<br/>FieldValidator]
            PATH_VAL[path_validator.py<br/>PathValidator]
            PROJECT_VAL[project_validator.py<br/>ProjectValidator]
            PROJECT_VALS[project_validators.py<br/>ProjectValidators]
            HELPERS[validation_helpers.py<br/>ValidationHelpers]
            VALIDATORS[validators.py<br/>Validators]
        end

        subgraph UTILS["Utilities - utils/"]
            direction TB
            DRILL_UTIL[drillhole.py<br/>Drillhole Utils]
            GEOL_UTIL[geology.py<br/>Geology Utils]
            GEOM_UTIL[geometry_utils/<br/>Geometry Utilities]
            I18N_UTIL[i18n.py<br/>i18n Utils]
            IO_UTIL[io.py<br/>IO Utils]
            META_UTIL[metadata_reader.py<br/>Metadata Reader]
            PARSE_UTIL[parsing.py<br/>Parsing Utils]
            RENDER_UTIL[rendering.py<br/>Rendering Utils]
            SAFE_LOADER[safe_loader.py<br/>Safe Loader]
            SAMPLE_UTIL[sampling.py<br/>Sampling Utils]
            SPATIAL_UTIL[spatial.py<br/>Spatial Utils]
        end

        CONTROLLER --> INTERFACES
        INTERFACES -.-> SERVICES
        SERVICES --> MODELS
        SERVICES --> DOMAIN
        SERVICES --> VALIDATION
        SERVICES --> UTILS
        CONTROLLER --> VALIDATION
    end

    %% ========== EXPORTERS LAYER ==========
    subgraph EXPORTERS["πŸ“€ Exporters Layer"]
        direction TB

        BASE_EXPORT[exporters/base_exporter.py<br/>BaseExporter]:::exporter

        subgraph VECTOR["Vector Exporters"]
            direction TB
            VEC_EXPORT[exporters/vector_exporter.py<br/>VectorExporter<br/>GPKG/SHP/DXF]:::exporter
            DXF_EXPORT[exporters/dxf_exporter.py<br/>DxfExporter]:::exporter
        end

        subgraph PROFILE["Profile Exporters"]
            direction TB
            PROF_EXPORT[exporters/profile_exporters.py<br/>ProfileExporters]:::exporter
            CSV_EXPORT[exporters/csv_exporter.py<br/>CsvExporter]:::exporter
        end

        subgraph INTERP_3D["3D Geologic Export"]
            direction TB
            INTERP_3D[exporters/interpretation_3d_exporter.py<br/>Interpretation3DExporter]:::exporter
            INTERP_EXP[exporters/interpretation_exporters.py<br/>InterpretationExporters]:::exporter
        end

        subgraph DRILL_3D["3D Drillhole Export"]
            direction TB
            DRILL_3D[exporters/drillhole_3d_exporter.py<br/>Drillhole3DExporter]:::exporter
            DRILL_EXP[exporters/drillhole_exporters.py<br/>DrillholeExporters]:::exporter
        end

        subgraph DOCUMENT["Document Exporters"]
            direction TB
            PDF_EXPORT[exporters/pdf_exporter.py<br/>PdfExporter]:::exporter
            SVG_EXPORT[exporters/svg_exporter.py<br/>SvgExporter]:::exporter
            IMG_EXPORT[exporters/image_exporter.py<br/>ImageExporter]:::exporter
        end

        BASE_EXPORT --> VEC_EXPORT
        BASE_EXPORT --> PROF_EXPORT
        BASE_EXPORT --> INTERP_3D
        BASE_EXPORT --> DRILL_3D
        BASE_EXPORT --> PDF_EXPORT
        BASE_EXPORT --> SVG_EXPORT
        BASE_EXPORT --> IMG_EXPORT
        VEC_EXPORT --> DXF_EXPORT
        PROF_EXPORT --> CSV_EXPORT
        INTERP_3D --> INTERP_EXP
        DRILL_3D --> DRILL_EXP
    end

    %% ========== CONNECTIONS ==========
    QGIS --> PLUGIN
    PLUGIN --> MAIN

    %% GUI -> Core
    PREVIEW_MGR --> PREVIEW_SVC
    EXPORT_MGR --> EXPORT_SVC
    INTERP_MGR --> DRILL_SVC
    INTERP_MGR --> GEOL_SVC
    TOOL_MGR --> CONTROLLER

    %% Workers -> Controller
    TASK_ORCH --> CONTROLLER
    GEOLOGY_TASK --> CONTROLLER
    DRILL_TASK --> CONTROLLER

    %% Core -> Exporters
    EXPORT_SVC --> EXPORTERS

    %% Styles
    classDef plugin fill:#ff6b6b,stroke:#c92a2a,stroke-width:2px,color:#fff
    class PLUGIN plugin
    

πŸ–₯️ GUI Layer - User Interface

1. Manager-Based Orchestration (main_dialog.py)

Main Class: SecInterpDialog Responsibility: The main dialog no longer contains business logic. It coordinates specialized Managers that handle specific lifecycle events and UI state.

Key Managers (10 Specialized Managers)

Manager

File

Responsibility

DialogSignalManager

dialog_signal_manager.py β†’ dialog_signal_manager

Centralizes all signal/slot connections to avoid spaghetti code.

DialogInputManager

dialog_input_manager.py β†’ dialog_input_manager

Manages input layer selection, schema validation, and layer compatibility.

PreviewManager

dialog_preview_manager.py β†’ dialog_preview_manager

Coordinates the preview canvas, axes, LOD calculation, and render triggering.

ExportManager

dialog_export_manager.py β†’ dialog_export_manager

Maps UI selections to the ExportService in the Core layer.

InterpretationManager

dialog_interpretation_manager.py β†’ dialog_interpretation_manager

Handles 2D/3D geological interpretation state and user interactions.

DialogStateManager

dialog_state_manager.py β†’ dialog_state_manager

Manages session persistence and UI defaults restoration.

SettingsPersistence

dialog_settings_persistence.py β†’ dialog_state_manager

Handles QSettings-based persistence for plugin configuration.

ToolManager

dialog_tool_manager.py β†’ dialog_tool_manager

Manages QgsMapTool lifecycle (pan, measure, interpret tools).

LayerNotificationManager

layer_notification_manager.py β†’ layer_notification_manager

Listens to QGIS layer tree changes and updates UI accordingly.

UIStatusManager

ui_status_manager.py β†’ ui_status_manager

Manages status bar messages, progress indicators, and user notifications.

2. Rendering Engine

Responsibility: Handles all preview canvas rendering with adaptive LOD and specialized renderers.

Core Rendering Components

Component

File

Responsibility

PreviewRenderer

preview_renderer.py β†’ preview_renderer

Main canvas renderer orchestrating specialized renderers.

PreviewLayerFactory

preview_layer_factory.py β†’ preview_layer_factory

Creates temporary memory layers for preview visualization.

PreviewAxesManager

preview_axes_manager.py β†’ preview_axes_manager

Manages coordinate axes, grids, and scale annotations.

PreviewReporter

preview_reporter.py β†’ preview_state

Generates textual/tabular reports from preview data.

PreviewParamHasher

preview_param_hasher.py β†’ dialog_preview_manager

Hashes render parameters for LOD cache invalidation.

PreviewState

preview_state.py β†’ preview_state

Encapsulates preview viewport, scale, and visibility state.

Specialized Renderers (Renderer Pattern)

Renderer

File

Responsibility

BaseRenderer

renderers/base_renderer.py β†’ gui_renderers

Abstract base with common rendering utilities.

TopoRenderer

renderers/topo_renderer.py β†’ gui_renderers

Renders topography profiles with elevation data.

GeologyRenderer

renderers/geology_renderer.py β†’ gui_renderers

Renders geological units, contacts, and structures.

DrillholeRenderer

renderers/drillhole_renderer.py β†’ gui_renderers

Renders drillhole traces, intervals, and projections.

StructureRenderer

renderers/structure_renderer.py β†’ gui_renderers

Renders structural measurements (strike/dip, foliations).

InterpretationRenderer

renderers/interpretation_renderer.py β†’ gui_renderers

Renders user-drawn geological interpretations.

ColorManager

renderers/color_manager.py β†’ gui_renderers

Centralized color palette and legend management.

LOD Optimization Methods (in PreviewRenderer):

Method

Purpose

Algorithm

_decimate_line_data()

Line simplification

Douglas-Peucker

_calculate_curvature()

Local curvature calculation

Angle between segments

_adaptive_sample()

Adaptive sampling

Curvature-based density

3. Async Task System (QgsTask)

Responsibility: Offloads heavy computation to background threads keeping UI responsive.

Task

File

Responsibility

PreviewTaskOrchestrator

preview_task_orchestrator.py β†’ gui_tasks

Queues, prioritizes, and monitors background tasks.

GeologyTask

tasks/geology_task.py β†’ gui_tasks

Background geological intersection calculations.

DrillholeTask

tasks/drillhole_task.py β†’ gui_tasks

Background drillhole trajectory processing.

4. Map Tools & UI Components

  • Map Tools (tools/): QgsMapTool implementations for interactive editing (pan, measure, interpretation drawing).

  • UI Pages (ui/pages/): Page components for each plugin module; Interpretation Settings is hosted as a tab in SettingsPage, not a sidebar page.

  • Section feature selector (ui/pages/section_feature_selector.py): only single-part lines with exactly two vertices; shows fid β€” label, supports Auto or an attribute label field, and forwards section_feature_id to preview/export/LOD.

  • Interpretation settings tab (ui/pages/settings/interpretation_tab.py): storage source (project JSON vs vector layer), custom fields and attribute inheritance; MainWindow.page_interpretation aliases the tab for compatibility.

  • UI Adapters (adapters/): Adapters bridging QGIS widgets to internal models.

  • PreviewSidePanel (preview_side_panel.py): Collapsible legend beside the preview, synchronized with the renderers; per-unit hide/color/alias, drillhole lithologies and the interpretations list.

  • Symbology tab (ui/pages/settings/symbology_tab.py, symbology_units.py): Live per-layer styles (topography, structures, drillholes, interpretations), the shared per-unit editor and the legend layout (position, font size, max items).

βš™οΈ Core Layer - Business Logic

1. Interface-Driven Design (interfaces/)

Pattern: Dependency Injection (DI) via Abstract Base Classes (ABCs) Responsibility: All services are defined as interfaces. The ProfileController consumes interfaces, allowing for easy mocking during testing and replacement of logic without affecting the GUI.

class IGeologyService(ABC):
    @abstractmethod
    def build_segments(self, context: GeologyContext, feedback: Any | None = None) -> Any:
        """Build geological segments from a detached context."""

Core Interfaces (6 Interfaces)

Interface

File

Purpose

IGeologyService

interfaces/geology_interface.py β†’ layer_core_interfaces

build_segments() β€” geological segments from a detached context.

IDrillholeService

interfaces/drillhole_interface.py β†’ layer_core_interfaces

process_context() — drillhole data processing, 3D→2D projection.

IStructureService

interfaces/structure_interface.py β†’ layer_core_interfaces

project_structures() β€” structural data validation and projection.

IPreviewService

interfaces/preview_interface.py β†’ layer_core_interfaces

generate_all() β€” preview orchestration and render data prep.

ICacheService

interfaces/cache_interface.py β†’ layer_core_interfaces

get/set/invalidate/clear β€” caching strategy (typing Protocol).

IRenderer3D

interfaces/i_renderer_3d.py β†’ layer_core_interfaces

render_3d()/clear() β€” 3D visualization abstraction.

2. ProfileController (controller.py)

Responsibility: Central orchestrator coordinating all services through their interfaces.

Concrete Services (7 Services)

Service

File

Responsibility

GeologyService

services/geology_service.py β†’ geology_service

Core intersection algorithms (outcrops, polygons, units).

StructureService

services/structure_service.py β†’ structure_service

Structural data processing, validation, stereonets.

DrillholeService

services/drillhole_service.py β†’ drillhole_service

3D trajectory calculation, 2D section projection, interval management.

ExportService

services/export_service.py β†’ core_services

Central orchestrator for Exporters layer, format registry.

PreviewService

services/preview_service.py β†’ preview_service

Preview data preparation, LOD computation, render DTOs.

VerticalExaggerationService

services/vertical_exaggeration_service.py β†’ vertical_exaggeration_service

Adaptive vertical exaggeration from profile aspect ratio and structural density.

AccessControlService

services/access_control_service.py β†’ core_services

License validation, feature gating, user permissions.

Drillhole Sub-system (5 Components)

Component

File

Responsibility

CollarProcessor

drillhole/collar_processor.py β†’ collar_processor

Collar data validation, coordinate transformation.

SurveyProcessor

drillhole/survey_processor.py β†’ core_services_drillhole

Survey data (azimuth/dip) processing, interpolation.

IntervalProcessor

drillhole/interval_processor.py β†’ interval_processor

Lithology/assay interval management, merging.

TrajectoryEngine

drillhole/trajectory_engine.py β†’ trajectory_engine

3D trajectory calculation (minimum curvature, etc.).

ProjectionEngine

drillhole/projection_engine.py β†’ core_services_drillhole

3D→2D projection onto section plane.

Geology Sub-system

  • Geology processing is consolidated in services/geology_service.py (the former services/geology/ package was removed).

3. Domain Layer

  • Domain Models (models/settings_model.py): SettingsModel for typed configuration.

  • Entities & DTOs (domain/): Immutable data transfer objects (ProfileData, GeologySegment, DrillholeData, etc.) for layer communication.

4. Validation Pipeline (validation/)

Modular validation framework with 11 validators coordinated by ValidationPipeline:

Validator

File

Scope

BaseValidator

base_validator.py β†’ core_validation

Abstract base with common validation utilities.

ValidationPipeline

pipeline.py β†’ core_validation

Orchestrates validators, aggregates results.

LayerValidator

layer_validator.py β†’ core_validation

QGIS layer structure, CRS, geometry type validation.

FieldValidator

field_validator.py β†’ core_validation

Attribute field names, types, required fields.

PathValidator

path_validator.py β†’ core_validation

File/directory paths, permissions, existence.

ProjectValidator

project_validator.py β†’ core_validation

Project-level consistency (layers, settings).

ProjectValidators

project_validators.py β†’ core_validation

Composite project validation rules.

ValidationHelpers

validation_helpers.py β†’ core_validation

Shared validation utilities, error formatting.

Validators

validators.py β†’ core_validation

Convenience functions for common validations.

LayerMetadata

layer_metadata.py β†’ layer_metadata

QGIS-agnostic layer metadata DTO consumed by the pipeline.

implausible_crs_reason()

crs_plausibility.py β†’ crs_plausibility

Best-effort mislabelled-CRS detection from extent plausibility.

5. Utilities (utils/)

11 specialized utility modules:

Utility

File

Purpose

Drillhole Utils

drillhole.py β†’ layer_core_utils

Drillhole-specific calculations, transformations.

Geology Utils

geology.py β†’ layer_core_utils

Geological computations, unit conversions.

Geometry Utils

geometry_utils/ β†’ layer_core_utils_geometry_utils

Advanced geometry operations (intersections, buffers).

i18n Utils

i18n.py β†’ core_utils

Translation helpers, locale management.

QGIS I/O

exporters/qgis_io.py β†’ layer_exporters

Vector writer adapter (Shapefile/GPKG/DXF).

Metadata Reader

metadata_reader.py β†’ layer_core_utils

QGIS layer metadata extraction.

Parsing Utils

parsing.py β†’ layer_core_utils

Text/CSV/XML parsing with error recovery.

Rendering Utils

rendering.py β†’ layer_core_utils

Render-ready data preparation.

Safe Loader

safe_loader.py β†’ safe_loader

Safe YAML/JSON loading with schema validation.

Sampling Utils

sampling.py β†’ layer_core_utils

Statistical sampling, profile point generation.

Spatial Utils

spatial.py β†’ layer_core_utils

Spatial queries, indexing, CRS operations.

πŸ“€ Exporters Layer

The Exporters layer implements the Factory Pattern via BaseExporter abstract class. All exporters consume QGIS-agnostic DTOs from core/domain.

Export Hierarchy

BaseExporter (ABC)
β”œβ”€β”€ VectorExporter β†’ DxfExporter
β”‚   β”œβ”€β”€ GPKG, SHP, DXF output
β”‚   └── Layer-based feature export
β”œβ”€β”€ ProfileExporters β†’ CsvExporter
β”‚   β”œβ”€β”€ Profile data (CSV, Excel)
β”‚   └── Sampling results export
β”œβ”€β”€ Interpretation3DExporter β†’ InterpretationExporters
β”‚   β”œβ”€β”€ 3D geological interpretations (Z-aware)
β”‚   └── GeoPackage 3D, CityJSON
β”œβ”€β”€ Drillhole3DExporter β†’ DrillholeExporters
β”‚   β”œβ”€β”€ 3D drillhole traces (Z-aware)
β”‚   └── Interval data as 3D cylinders
β”œβ”€β”€ PdfExporter
β”‚   β”œβ”€β”€ Professional layouts with legends
β”‚   └── Multi-page section reports
β”œβ”€β”€ SvgExporter
β”‚   β”œβ”€β”€ Vector graphics for publications
β”‚   └── Scalable section diagrams
└── ImageExporter
    β”œβ”€β”€ PNG/JPG raster output
    └── High-DPI for presentations

Key Exporters

Exporter

File

Formats

Purpose

VectorExporter

vector_exporter.py β†’ vector_exporter

GPKG, SHP, DXF

Unified vector export for sections.

DxfExporter

dxf_exporter.py β†’ dxf_exporter

DXF

CAD-compatible export.

ProfileExporters

profile_exporters.py β†’ profile_exporters

CSV, XLSX

Profile data tables, sampling results.

CsvExporter

csv_exporter.py β†’ csv_exporter

CSV

Raw data export.

Interpretation3DExporter

interpretation_3d_exporter.py β†’ interpretation_3d_exporter

GPKG 3D, CityJSON

3D geological interpretations.

InterpretationExporters

interpretation_exporters.py β†’ interpretation_exporters

Multiple

Interpretation-specific formats.

Drillhole3DExporter

drillhole_3d_exporter.py β†’ drillhole_3d_exporter

GPKG 3D, WellKnown Text

3D drillhole traces & intervals.

DrillholeExporters

drillhole_exporters.py β†’ drillhole_exporters

Multiple

Drillhole-specific formats.

PdfExporter

pdf_exporter.py β†’ pdf_exporter

PDF

Professional section layouts.

SvgExporter

svg_exporter.py β†’ svg_exporter

SVG

Vector graphics for reports.

ImageExporter

image_exporter.py β†’ image_exporter

PNG, JPG

Raster images for presentations.

Format Decoupling

All exporters implement:

  • export(dto: ExportDTO, output_path: Path) -> ExportResult

  • Consume only core/domain DTOs (no QGIS dependencies)

  • Return structured ExportResult with metadata (files created, features exported, warnings)

🎨 Design Principles

SecInterp v3.9.1 follows industry-standard architectural patterns to ensure high quality and testability.

Core Architectural Patterns

  • Manager Pattern (GUI): Decouples the main window from individual feature logic (Export, Preview, etc.).

  • Dependency Injection (Core): Uses core/interfaces to decouple the ProfileController from concrete service implementations.

  • Factory Pattern (Exporters): The ExportService dynamically selects the appropriate BaseExporter subclass.

  • Observer Pattern (Signals): Extensive use of qgis.PyQt.QtCore.pyqtSignal for asynchronous communication between Core and GUI.

  • DTO Pattern (Data Transfer Objects): All data passing between layers is encapsulated in immutable DTOs from core/domain.

SOLID & Clean Code

  • Interface Segregation: Clients only depend on the specific service interfaces they need.

  • Single Responsibility: Each Manager and Service handles a unique, atomic part of the plugin workflow.

  • Thread Safety: Long-running operations only use QgsTask to avoid blocking the QGIS main thread.


πŸš€ Extensibility

Adding a New Service

  1. Define the interface in core/interfaces/ (inheriting from abc.ABC).

  2. Implement the concrete service in core/services/.

  3. Register the service in controller.py and inject it into the ProfileController.

Adding a New Export Format

  1. Inherit from BaseExporter in exporters/.

  2. Implement the export() method using QGIS-agnostic logic.

  3. Update the ExportService to include the new format in its registry.


πŸ“Š Project Metrics

Metric

Value

Python Modules

200

Source Lines of Code (SLOC)

~24,400

Core Layer

~34%

GUI Layer

~54%

Exporters Layer

~9%

Plugin Layer

~2%


πŸ”’ Quality Assurance & Security Scanning

SecInterp implements a multi-layered quality assurance strategy that replicates and extends the QGIS Plugin Portal’s automated scanning pipeline.

1. Local Security Scanning (Portal-Compatible)

The project includes a custom security scanner (scripts/security_scan.py) that runs the exact same tools used by QGIS Plugin Portal:

Tool

Severity

Purpose

Configuration

Bandit

CRITICAL (Blocking)

Static analysis for Python security vulnerabilities (shell injection, pickle, eval, hardcoded secrets, weak crypto, SQL injection)

.bandit (excludes tests/scripts, 72 tests enabled)

detect-secrets

CRITICAL (Blocking)

Scans for hardcoded credentials, API keys, private keys, high-entropy strings

.secrets.baseline (27 detectors, heuristic filters, excludes tests/venv)

Flake8

INFO (Non-blocking)

Code quality, PEP 8 style, syntax errors, undefined names

.flake8 (max-line-length=100, max-complexity=15, JSON output)

Run locally:

make security-scan          # Full portal-compatible scan
make pylint                 # Additional static analysis
make pep8                   # Ruff-based style check (faster)

2. QGIS Plugin Analyzer (Deep Auditing)

For architectural and deep code quality audits beyond Portal scanning:

# Security (Bandit + detect-secrets)
uv run qgis-analyzer security .

# Internationalization coverage
uv run qgis-analyzer analyze i18n .

# Performance (UI blocks, expensive loops, signal leaks)
uv run qgis-analyzer analyze performance .

# Architecture (Core/GUI separation, QGIS API usage, circular deps)
uv run qgis-analyzer analyze architecture .

# Full HTML report
uv run qgis-analyzer analyze . --report

Workflow: /audit-plugin (agent @auditor with skills: project-context, coding-standards, i18n-standards)

3. Qt6 / QGIS 4 Migration Readiness

Automated compatibility checking using the same tool QGIS Portal runs on upload:

make qt6-check              # Dry-run check (pyqgis4-checker via Docker)
make qt6-fix                # Auto-migrate enums to scoped Qt6 form (edits files)

4. Pre-Release Validation Pipeline

Complete validation before any release candidate:

make pre-release            # Runs: qt6-check + security-scan + docker-test

docker-test executes the full suite (763 tests, including 76 integration tests) in a clean QGIS Docker environment.

5. CI/CD Integration

All tools are configured to run in CI pipelines with:

  • Zero critical Bandit findings required for merge

  • Zero detect-secrets findings (above baseline) required for merge

  • Flake8 warnings reported but non-blocking

  • Full test suite (unit + integration) must pass

6. Configuration Files (Committed to Repo)

File

Purpose

Portal Compatible

.bandit

Bandit test selection, exclude dirs

βœ…

.secrets.baseline

detect-secrets baseline, filters, excludes

βœ…

.flake8

Flake8 rules, complexity, output format

βœ…

pyproject.toml

Ruff, pytest, mypy config

⚠️ (dev only)


πŸ“ Final Notes

This document provides a detailed overview of the SecInterp plugin architecture. For development information, please refer to [README_DEV.md](file:///home/jmbernales/qgispluginsdev/sec_interp/README_DEV.md).

Last Updated: 2026-10-24 Plugin Version: 3.11.0 Author: Juan M. Bernales