Skip to content

MapMarkers Plugin

MapMarkers is a minimal plugin that demonstrates the mapitemview hook. It draws two custom markers — a static pin icon and an animated "pulse" component — onto AMC's maps. The same file is loaded into both the Plan view map and the Fly view map. There is no data singleton, no settings panel, and no menu integration — just the map items.

The MapMarkers pin and pulse markers on the AMC map

Requires AMC 2.1.0+

The mapitemview hook was added in AMC 2.1.0. Earlier versions ignore the file and nothing is drawn.

Where to look

The markers are placed at a fixed coordinate (Zurich, 47.397, 8.546) so they appear at the same spot on both the Fly and Plan view maps — pan or zoom either map there to see them. The two maps are not centered together at load, which is why the example uses a fixed coordinate rather than map.center.

What it does

  • Adds two markers to both maps, driven by a single ListModel, at a fixed demo coordinate (Zurich) so they appear at the same spot on both maps:
    • an SVG pin icon, and
    • an animated pulsing circle (a sibling QML component) just beside it.
  • Renders on both maps by default (no view-targeting overrides).
  • Uses one delegate for both markers — each row's type selects which visual shows.

File structure

MapMarkers/
├── plugin.json
├── build.sh
├── src/
│   ├── MapMarkersMapItemView.qml   ← mapitemview hook
│   └── PulseCircle.qml             ← animated component rendered as sourceItem
└── img/
    └── pin.svg                     ← marker icon

plugin.json

{
    "name": "MapMarkers",
    "description": "Minimal example of the mapitemview hook — draws custom markers on the Plan and Fly view maps",
    "author": "Auterion",
    "copyright": "(c) 2026 Auterion AG",
    "license": "MIT",
    "type": "qml"
}

MapMarkersMapItemView.qml

AMC detects the mapitemview keyword in the filename and attaches the file to both maps.

Key patterns demonstrated:

  • root component PluginMapItemView (from QGroundControl.PluginInterface) — a QtLocation MapItemView with the live map already attached, so the file never sets map
  • an inline ListModel with author-defined roles (lat, lon, type, icon)
  • a single delegate — a MapQuickItem anchored at a coordinate, whose sourceItem holds both visuals and shows one based on the row's type
  • an icon row (an Image showing pin.svg) and an animated row (PulseCircle.qml) sharing the same model and delegate — note a sourceItem must be a QML item, so the icon is an Image, not a raw .svg
  • a fixed demo coordinate rather than map.center: the Fly and Plan maps aren't centered together at load, so map.center isn't a stable anchor in Component.onCompleted (it's still readable — bind to it or map.onCenterChanged to follow the live center)
  • one consistent type per ListModel role (icon is always a string), resolved with Qt.resolvedUrl(...) in the delegate — a ListModel locks each role's type from the first row and drops later rows of a different type

See the full source: MapMarkersMapItemView.qml.

import QtQuick
import QtLocation
import QtPositioning
import QGroundControl.PluginInterface   // PluginMapItemView — a MapItemView with `map` preset

PluginMapItemView {
    // Do NOT set `map`; AMC injects it. Renders on both maps by default; set
    // showInFlyView / showInPlanView to false to restrict to one (load-time constants).
    model: ListModel { id: markers }

    delegate: MapQuickItem {
        coordinate:    QtPositioning.coordinate(model.lat, model.lon)
        anchorPoint.x: content.width  / 2   // center the content on the coordinate
        anchorPoint.y: content.height / 2

        // sourceItem must be a QML item — hold both visuals, show one by model.type.
        sourceItem: Item {
            id:     content
            width:  Math.max(icon.width,  pulse.width)
            height: Math.max(icon.height, pulse.height)

            Image {
                id: icon
                visible:    model.type === "icon"
                width:  32; height: 32
                source:     model.icon ? Qt.resolvedUrl(model.icon) : ""
                sourceSize: Qt.size(width, height)
                fillMode:   Image.PreserveAspectFit
            }
            PulseCircle { id: pulse; visible: model.type === "pulse" }
        }
    }

    Component.onCompleted: {
        // Fixed demo coordinate so both markers land at the same spot on both maps
        // (map.center isn't synced across the Fly/Plan maps at load). `icon` is a string
        // in both rows — a ListModel locks each role's type from the first row.
        const lat = 47.397, lon = 8.546   // Zurich
        markers.append({ lat: lat,          lon: lon,          type: "icon",  icon: "../img/pin.svg" })
        markers.append({ lat: lat + 0.0015, lon: lon + 0.0025, type: "pulse", icon: "" })
    }
}

PulseCircle.qml

The animated marker, kept in its own file and pointed at by the second model row.

It is screen content, not a map item

Because PulseCircle.qml is loaded as the sourceItem of a MapQuickItem, it is screen content anchored at a coordinate, not a geographic map item. It must therefore be a plain Item/Rectangle. Do not make it a MapCircle/MapPolyline/MapPolygon — those are map items and will not render inside a sourceItem. To draw a true geographic shape, make the delegate's root that map item type directly instead of wrapping it in a MapQuickItem.

See the full source: PulseCircle.qml.

import QtQuick

Item {
    width:  40
    height: 40

    Rectangle {
        anchors.centerIn: parent
        width:        parent.width
        height:       parent.height
        radius:       width / 2
        color:        "transparent"
        border.color: qgcPal.colorRed   // qgcPal is globally available — no import needed
        border.width: 2

        SequentialAnimation on scale {
            loops: Animation.Infinite; running: true
            NumberAnimation { from: 0.4; to: 1.0; duration: 1000; easing.type: Easing.OutQuad }
            NumberAnimation { from: 1.0; to: 0.4; duration: 0 }
        }
        // ... matching opacity animation ...
    }

    Rectangle {
        anchors.centerIn: parent
        width: 10; height: 10; radius: width / 2
        color: qgcPal.colorRed
    }
}

Building and installing

cd examples/v2/MapMarkers
./build.sh

This produces MapMarkers.plugin. Copy it to the AMC Plugins folder:

~/Documents/Auterion Mission Control/Plugins/

AMC loads plugins at startup and watches the folder for changes — no restart needed when adding or removing a plugin file.

Next steps

  • Drive the model from a Data singleton — append or remove markers at runtime in response to telemetry.
  • Restrict the view to one map with showInFlyView / showInPlanView.
  • See the MapItemView hook reference for the full contract, and the linked Qt docs for QtLocation map items.