How Raster and COG Layers Added via Plugins Integrate with the Style Panel's Paint Controls
Raster and COG layers added via plugins integrate with GeoLibre's Style panel through metadata flags—specifically sourceKind and paintMode—which determine whether MapLibre-GL paint editors appear, get suppressed entirely, or show only a bridged opacity control.
GeoLibre's Style panel automatically adapts its paint controls based on how a raster or Cloud-Optimized GeoTIFF (COG) layer enters the map. Layers registered through plugins carry special metadata that the panel inspects at runtime, distinguishing between MapLibre-native raster sources and plugin-rendered WebGL layers. This architecture lets plugins opt out of standard paint controls when they handle pixel rendering themselves, while still offering selective UI integration through opacity bridges.
The Source Metadata That Flags Plugin Raster Layers
When a plugin registers a raster or COG layer, it populates a sourceKind field that the Style panel uses for initial categorization. According to the source code in apps/geolibre-desktop/src/components/panels/StylePanel.tsx (lines 1736-1748), recognized values include:
cog-urlgeotiff-urlmaplibre-gl-rasterstac-search-cog
These values trigger the isDeckRasterLayer flag, grouping the layer under "Deck raster layers" in the panel's internal logic.
// From StylePanel.tsx – layer classification based on sourceKind
const isDeckRasterLayer = [
'cog-url',
'geotiff-url',
'maplibre-gl-raster',
'stac-search-cog'
].includes(layer.sourceKind);
This classification alone does not determine paint control visibility—it merely identifies the layer's origin as plugin-mediated.
The paintMode Flag: Controlling Paint Editor Visibility
The critical decision point for paint control integration lives in the paintMode property. Defined in packages/plugins/src/types.ts (lines 61-66), this optional field accepts "plugin" as a value to indicate the plugin renders pixels independently.
The helper function pluginOwnsPaint, exported from @geolibre/core and implemented in packages/core/src/external-native-paint.ts (lines 10-16), performs the runtime check:
// packages/core/src/external-native-paint.ts
export function pluginOwnsPaint(metadata: LayerMetadata): boolean {
return metadata.paintMode === 'plugin';
}
When pluginOwnsPaint returns true, the Style panel suppresses all MapLibre-GL paint editors because the plugin's WebGL layer occupies no MapLibre paint slot. The source commentary in StylePanel.tsx (lines 1752-1756) explicitly notes this behavior: no MapLibre paint properties exist to edit when the plugin owns rendering.
Bridged Opacity: Selective UI Integration
Plugins that suppress standard paint controls can still expose limited styling through a paint bridge. If a plugin registers an opacity handler in its paintBridge configuration, the Style panel detects this via the hasBridgedOpacity flag (lines 1758-1760) and renders a single Opacity slider.
// Example: COG layer registration with paintMode and opacity bridge
// packages/plugins/src/plugins/cog-layer.ts
import { registration } from "@geolibre/core";
export const cogLayer = registration({
id: "my-cog-layer",
source: {
type: "cog-url",
url: "https://example.com/data/my.tif",
},
paintMode: "plugin", // Suppresses MapLibre paint editors
paintBridge: {
opacity: (value: number) => {
raster.setOpacity(value); // Enables Opacity slider only
},
},
});
This bridged approach satisfies common use cases—users expect opacity control for overlay rasters—without requiring full MapLibre paint property implementation.
Conditions for Raster-Specific Controls
The full suite of raster paint editors—brightness, contrast, saturation, and hue-rotate—appears only when a layer satisfies the compound condition in StylePanel.tsx (lines 1776-1779):
!isPluginPaintedLayer && (
isRasterPaintLayer(layer.type) ||
isRasterTileLayer ||
isDeckRasterLayer
)
This logic creates three distinct UI outcomes:
| Scenario | paintMode |
paintBridge.opacity |
Visible Controls |
|---|---|---|---|
| Plugin-rendered COG, no bridge | "plugin" |
absent | None |
| Plugin-rendered COG with opacity bridge | "plugin" |
present | Opacity slider only |
| Native raster source (no plugin) | undefined or omitted | N/A | Full raster paint editors (opacity, brightness, contrast, saturation, hue-rotate) |
Implementation Files and Verification
The integration behavior is enforced across several key files:
-
apps/geolibre-desktop/src/components/panels/StylePanel.tsx— Main UI logic for paint control rendering; containsisDeckRasterLayer,isPluginPaintedLayer, andhasBridgedOpacityevaluations. -
packages/core/src/external-native-paint.ts— ExportspluginOwnsPaintfor checkingmetadata.paintMode. -
packages/plugins/src/types.ts— Type definitions including the optionalpaintModeproperty for plugin registrations. -
tests/plugin-owned-paint.test.ts— Test suite confirmingpaintModeis correctly stored and interpreted in layer metadata.
Summary
- Plugin raster layers are recognized by
sourceKindvalues likecog-urland flagged as deck raster layers. - The
paintMode: "plugin"flag suppresses all MapLibre-GL paint editors when plugins render pixels independently. - Opacity bridges (
paintBridge.opacity) restore limited UI control, showing only the Opacity slider. - Full raster controls require native MapLibre sources or plugins that omit
paintMode: "plugin". - The
pluginOwnsPainthelper in@geolibre/corecentralizes the runtime ownership check.
Frequently Asked Questions
What happens if a plugin doesn't set paintMode?
The Style panel treats the layer as MapLibre-native, rendering the full set of raster paint editors. This may cause runtime errors if the plugin actually handles its own WebGL rendering, since no valid MapLibre paint properties exist for the layer type.
Can plugins expose controls beyond opacity through paint bridges?
The current architecture in GeoLibre supports only opacity bridging for plugin-painted layers. Additional paint properties would require extending the paintBridge interface or implementing custom panel components registered by the plugin itself.
How do I test whether my plugin's paint mode is correctly detected?
Import and invoke pluginOwnsPaint from @geolibre/core in your test suite, passing the layer metadata object. The test file tests/plugin-owned-paint.test.ts demonstrates the expected behavior: paintMode: "plugin" returns true, while absent or other values return false.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →