# 02Multimap

Multi-panel synchronised map canvases for comparative spatial analysis • v0.2.0

## 1. Overview

02Multimap is a QGIS workspace plugin that creates a multi-panel synchronised map viewer. It provides 2, 3, 4, 6, or 8 independent map canvases in a coordinated grid layout, with real-time navigation syncing, a laser crosshair cursor tracking across all panels, three per-panel render modes, print layout export, and an offline self-contained HTML dashboard export. It targets urban planners, environmental scientists, and demographers who need to compare spatial patterns side-by-side. The plugin is toolbar-only (no QGIS menu entry), has zero external Python dependencies, and is compatible with PyQt5 and PyQt6 via qgis.PyQt compatibility APIs.

## 2. Feature Documentation

### 2.1 Dynamic Layout Grids

#### Six Panel Configurations

A combobox offers 2 Panels (1×2), 2 Panels (2×1), 3 Panels (1×3), 4 Panels (2×2), 6 Panels (2×3), and 8 Panels (2×4). Single-row layouts use a single horizontal `QSplitter`; multi-row layouts nest two horizontal splitters inside a vertical splitter, initialised with equal sizing. Changing the grid destroys and recreates all panels, each inheriting the main canvas CRS and extent.

### 2.2 Bi-Directional Navigation Sync

#### Synchronised Panning and Zooming

Sync Navigation (default on): When any panel's extent changes, all other panels copy that extent. An internal `_is_syncing` boolean lock prevents cascade loops.

Sync with QGIS Canvas (default on): Panel extents push to the main QGIS canvas and vice versa. Both checkboxes must be on for main-canvas-to-panel propagation.

### 2.3 Laser Crosshair Tracking

#### Coordinated Cursor Across All Views

When moving the mouse over any panel, a green neon `QgsVertexMarker` (cross, 24 px) appears at the matching geographic coordinate on all other panels. A red marker appears on the main QGIS canvas. Coordinates display in a status bar readout. The markers hide when the mouse leaves the panel. Toggleable via the Laser Crosshair checkbox.

### 2.4 Smart Auto-Fill

#### "Choose for Me — Current Extent"

The teal-accented auto-fill button automatically assigns a different spatial layer to each panel. The algorithm collects all valid spatial layers from the project, orders them by canvas layer order, splits into two groups (layers intersecting the current canvas extent prioritised), and assigns the first N=panels layers. Remaining panels are set to "Follow Main Map". CRS-aware: transforms layer bounding boxes to canvas CRS before intersection testing.

### 2.5 Manual Alignment Buttons

#### Scale, Extent, and Full-Extent Alignment

- Match Scale: Copies the active panel's zoom scale to all panels, preserving each centre.
- Match Extent: Copies the active panel's full extent (centre + scale) to all panels.
- Fit All Panels: Calls `zoomToFullExtent()` on every panel.

## 3. Panel Render Modes

Each panel has a per-panel mode combobox with three options:

Mode | Behaviour
--- | ---
Follow Main Map | Copies layers directly from the main QGIS canvas
Compare One Layer | Shows one selected layer from the panel's layer combo, optionally stacked above a shared global background layer (from the top toolbar's base layer combo)
Use Map Theme | Applies a QGIS Map Theme's visible layers via `mapThemeCollection().mapThemeVisibleLayers()`

## 4. Print Layout and HTML Dashboard Export

#### Print Layout Export

Opens a configuration dialog for: map title (free text), page size (A4/A3/Letter), orientation (Landscape/Portrait), north arrow style (5 options from QGIS built-in SVG arrows), scalebar style (5 options), and export format (PNG, JPEG, PDF, SVG, or Interactive HTML Dashboard). Builds an in-memory `QgsLayout` with a title label, grid of `QgsLayoutItemMap` items, north arrow, and scalebar. Exports via `QgsLayoutExporter`.

#### Offline HTML Dashboard

When "Interactive HTML Dashboard" is selected, the export pipeline renders each panel's canvas to PNG at up to 2× resolution (capped at 1600×1200 px) and assembles a single self-contained HTML file with: CSS + vanilla JavaScript (zero external dependencies), grid layout matching the plugin's row/col configuration, synchronised pan via pointer events, synchronised wheel zoom (anchor-based, scale 1×–12×), double-click reset to 1×, laser cursor tracking via `pointermove` across all panels, coordinate readout with CRS label, and single-column responsive collapse below 760 px. XSS-safe: HTML-escapes titles, JSON-encodes panel data with `</` sanitisation via Unicode escaping.

## 5. Controls Summary

Control | Type | Description
--- | --- | ---
Panels | Combo | 6 grid configurations (2/3/4/6/8 panels)
Sync Navigation | QCheckBox (default on) | Bi-directional pan/zoom sync between panels
Sync with QGIS Canvas | QCheckBox (default on) | Two-way sync between panels and main canvas
Laser Crosshair | QCheckBox (default on) | Coordinated cursor marker across all views
Optional background | QgsMapLayerComboBox | Global base layer for Compare One Layer panels
Choose for Me | QPushButton | Auto-assigns layers to panels from current extent
Match Scale / Extent / Fit All | QPushButton (3) | Manual alignment between panels
Refresh / Export / Quick Guide | QPushButton (3) | Canvas refresh, print layout, help dialog

## 6. Workflow Guide

1. Load several spatial layers into a QGIS project and set up at least one map theme (optional, for theme-mode panels).
2. Click the 02Multimap toolbar icon to open the floating dialog.
3. Choose a panel count from the combobox (e.g., 4 Panels 2×2).
4. Optionally click Choose for Me to auto-assign layers intersecting the current extent.
5. Set each panel's mode: Follow Main Map, Compare One Layer (pick a layer from the combo), or Use Map Theme.
6. Optionally select a global background layer from the top toolbar.
7. Toggle Sync Navigation, canvas sync, and laser crosshair as needed.
8. Pan and zoom in any panel; all others follow. The laser crosshair tracks cursor position across all views.
9. Use Match Scale/Match Extent/Fit All for alignment. Export to print layout or HTML dashboard for sharing.

## 7. Technical Notes

- Layer identification: Layer combos store data by stable QGIS layer ID to avoid problems with duplicate layer names.
- Project signal integration: The dialog connects to `layerWasAdded`, `layerRemoved`, `mapThemeCollection().mapThemesChanged`, `iface.mapCanvas().layersChanged`, and `extentsChanged` to stay in sync with the project state.
- PyQt5/PyQt6 compatibility: 8 patterns for dynamic attribute access on enums (`Qt.WindowType`, `QEvent.Type`, `Qt.AlignmentFlag`, `QFont.Weight`, `QIODevice.OpenModeFlag`, `Qt.Orientation`, `QFrame.Shape`/`Shadow`) and event position extraction.
- Print layout fidelity: Theme-mode panels use `setFollowVisibilityPresetName()` / `setFollowVisibilityPreset(True)` for accuracy. Layer-mode panels use `layerStyleOverrides()`.
- Atomic file writes: HTML export uses `QSaveFile` for safe atomic commit.
- QGIS compatibility: 3.40 LTR through 4.x. Zero external Python dependencies.

## 8. Literature

1. Roberts, J. C. (2007). State of the art: Coordinated & multiple views in exploratory visualization. Proceedings of the Fifth International Conference on Coordinated and Multiple Views in Exploratory Visualization, 61–71. DOI: 10.1109/CMV.2007.20
2. Dykes, J., MacEachren, A. M., & Kraak, M. J. (Eds.). (2005). Exploring Geovisualization. Elsevier.
3. Elmqvist, N. & Tsigas, P. (2008). A taxonomy of 3D occlusion management for visualization. IEEE Transactions on Visualization and Computer Graphics, 14(5), 1095–1109. DOI: 10.1109/TVCG.2008.59
4. Andrienko, G., Andrienko, N., Dykes, J., Fabrikant, S. I., & Wachowicz, M. (2008). Geovisualization of dynamics, movement and change: key issues and developing approaches. The Cartographic Journal, 45(3), 185–200. DOI: 10.1179/000870408X311428