This document provides comprehensive API reference and configuration details for all GeoView core packages.
Description: A time slider package that provides temporal navigation and filtering capabilities for layers with time dimensions (WMS and ESRI Image layers). Supports both discrete and continuous temporal modes.
Version: 2.0.x
Author: Damon Ulmi
Repository: packages/geoview-time-slider/
Features:
Dependencies:
geoview-core: ^2.0.0react: ^18.3.1zustand: ~5.0.0The time slider is typically included in the GeoView build. To use it:
Via Configuration:
{
"footerBar": {
"tabs": {
"core": ["time-slider"]
},
"selectedTab": "time-slider",
"selectedTimeSliderLayerPath": "layerPath"
}
}
"corePackagesConfig": [
{
"time-slider": {
"sliders": [
{
"layerPaths": ["layer-path"],
"timeDimension": {
"nearestValues": "discrete", // or "continuous"
"rangeItems": {
"range": ["2020-01-01T00:00:00Z", "2021-01-01T00:00:00Z"]
}
}
}
]
}
}
]
Programmatic:
// Access time slider state via store getter functions
import { isStoreTimeSliderInitialized, getStoreTimeSliderLayers, getStoreTimeSliderFilters } from 'geoview-core';
// Check if time slider is initialized
if (isStoreTimeSliderInitialized('mapId')) {
// Get time-enabled layers
const timeLayers = getStoreTimeSliderLayers('mapId');
// Get current filters
const filters = getStoreTimeSliderFilters('mapId');
}
The time slider supports two distinct modes for handling temporal data:
nearestValues: 'discrete')Use When:
Behavior:
range arrayExample Configuration:
{
"time-slider": {
"sliders": [
{
"layerPaths": ["census-data"],
"title": "Census Years",
"timeDimension": {
"field": "Year",
"nearestValues": "discrete",
"singleHandle": true,
"rangeItems": {
"type": "discrete",
"range": [
"2000-01-01T00:00:00Z",
"2005-01-01T00:00:00Z",
"2010-01-01T00:00:00Z",
"2015-01-01T00:00:00Z",
"2020-01-01T00:00:00Z"
]
}
}
}
]
}
}
Generated Filter:
Year >= date '2010-01-01T00:00:00Z' AND Year < date '2015-01-01T00:00:00Z'
nearestValues: 'continuous')Use When:
Behavior:
Example Configuration:
{
"time-slider": {
"sliders": [
{
"layerPaths": ["weather-data/temperature"],
"title": "Hourly Temperature",
"timeDimension": {
"field": "timestamp",
"nearestValues": "continuous",
"singleHandle": true,
"rangeItems": {
"type": "relative",
"range": ["2024-01-01T00:00:00Z", "2024-01-31T23:59:59Z"]
}
}
}
]
}
}
Generated Filter (with 1-hour step):
timestamp >= date '2024-01-15T14:30:00Z' AND timestamp < date '2024-01-15T15:30:00Z'
Step Options (Continuous Mode Only):
3600000 ms)86400000 ms)604800000 ms)2592000000 ms)31536000000 ms)Example 1: Discrete Mode - Yearly Satellite Imagery
{
"map": {
"listOfGeoviewLayerConfig": [
{
"geoviewLayerId": "satellite-yearly",
"geoviewLayerType": "esriImage",
"metadataAccessPath": "https://example.com/arcgis/rest/services/Satellite",
"listOfLayerEntryConfig": [
{
"layerId": "0",
"layerName": "Annual Imagery"
}
]
}
]
},
"footerBar": {
"tabs": {
"core": ["time-slider"]
},
"selectedTab": "time-slider"
},
"corePackagesConfig": [
{
"time-slider": {
"sliders": [
{
"layerPaths": ["satellite-yearly/0"],
"title": "Satellite Imagery by Year",
"description": "Annual satellite imagery from 2015-2024",
"delay": 2000,
"filtering": true,
"timeDimension": {
"field": "AcquisitionDate",
"nearestValues": "discrete",
"singleHandle": true,
"displayPattern": [{ "datePrecision": "year" }],
"rangeItems": {
"type": "discrete",
"range": [
"2015-01-01T00:00:00Z",
"2016-01-01T00:00:00Z",
"2017-01-01T00:00:00Z",
"2018-01-01T00:00:00Z",
"2019-01-01T00:00:00Z",
"2020-01-01T00:00:00Z",
"2021-01-01T00:00:00Z",
"2022-01-01T00:00:00Z",
"2023-01-01T00:00:00Z",
"2024-01-01T00:00:00Z"
]
}
}
}
]
}
}
]
}
Example 2: Continuous Mode - Hourly Weather Data
{
"map": {
"listOfGeoviewLayerConfig": [
{
"geoviewLayerId": "weather-wms",
"geoviewLayerType": "ogcWms",
"metadataAccessPath": "https://example.com/wms",
"listOfLayerEntryConfig": [
{
"layerId": "temperature",
"layerName": "Temperature",
"source": {
"featureInfo": {
"queryable": true
}
}
}
]
}
]
},
"footerBar": {
"tabs": {
"core": ["time-slider"]
},
"selectedTab": "time-slider"
},
"corePackagesConfig": [
{
"time-slider": {
"sliders": [
{
"layerPaths": ["weather-wms/temperature"],
"title": "Hourly Temperature Forecast",
"description": "7-day temperature forecast with hourly resolution",
"delay": 500,
"filtering": true,
"timeDimension": {
"field": "time",
"nearestValues": "continuous",
"singleHandle": true,
"displayPattern": [{ "datePrecision": "day" }, { "timePrecision": "hour" }],
"rangeItems": {
"type": "relative",
"range": ["2024-01-01T00:00:00Z", "2024-01-07T23:00:00Z"]
}
}
}
]
}
}
]
}
Example 3: Multiple Layers with Range Handles (Discrete)
{
"corePackagesConfig": [
{
"time-slider": {
"sliders": [
{
"layerPaths": ["temperature-layer/data", "precipitation-layer/data"],
"fields": ["timestamp", "date"],
"title": "Climate Data Time Range",
"description": "Select a time range for temperature and precipitation",
"delay": 1000,
"filtering": true,
"locked": false,
"reversed": false,
"timeDimension": {
"nearestValues": "discrete",
"singleHandle": false,
"displayPattern": [{ "datePrecision": "month" }],
"rangeItems": {
"type": "discrete",
"range": [
"2023-01-01T00:00:00Z",
"2023-02-01T00:00:00Z",
"2023-03-01T00:00:00Z",
"2023-04-01T00:00:00Z",
"2023-05-01T00:00:00Z",
"2023-06-01T00:00:00Z",
"2023-07-01T00:00:00Z",
"2023-08-01T00:00:00Z",
"2023-09-01T00:00:00Z",
"2023-10-01T00:00:00Z",
"2023-11-01T00:00:00Z",
"2023-12-01T00:00:00Z"
]
}
}
}
]
}
}
]
}
The time slider state is managed through the TimeSliderController and Zustand store hooks. Configuration is the primary way to set up time slider behavior:
const mapViewer = cgpv.api.getMapViewer('mapId');
// Access time slider controller
const timeSliderController = mapViewer.controllers.timeSliderController;
// Time slider filtering is managed automatically by the UI
// Configuration-driven setup is the recommended approach
interface TypeTimeSliderValues {
// Configuration
additionalLayerpaths?: string[]; // Linked layers
delay: number; // Animation delay (ms)
description?: string; // Display description
discreteValues: boolean; // true = discrete mode, false = continuous mode
step?: number; // Step size for continuous mode (ms)
field: string; // Field name for filtering
fieldAlias: string; // Display name for field
filtering: boolean; // Is filtering active
isMainLayerPath: boolean; // Is this the primary layer
locked?: boolean; // Are handles locked together
reversed?: boolean; // Is animation reversed
singleHandle: boolean; // Single vs range selection (auto-detected from WMS metadata, overridable via config)
title?: string; // Display title
// Data
minAndMax: number[]; // [min, max] timestamps (ms)
range: string[]; // Array of ISO date strings
values: number[]; // Current position(s) (ms)
displayPattern: [DatePrecision, TimePrecision]; // Display format
}
Choosing the Right Mode:
| Data Characteristic | Recommended Mode |
|---|---|
| Yearly, quarterly, monthly data | Discrete |
| Specific collection dates | Discrete |
| Model outputs at scheduled intervals | Discrete |
| Hourly, minute-level data | Continuous |
| Real-time/streaming data | Continuous |
| Dense temporal coverage | Continuous |
| Sparse, irregular intervals | Discrete |
Performance Considerations:
delay based on data density (slower for many points)UI/UX Tips:
title and description for user contextdisplayPattern for your data’s temporal resolutiondelay based on desired animation speed (500ms-5000ms)locked handles for synchronized comparison in range modereversed animation for countdown scenariosDescription: A charting package that visualizes layer data in interactive charts using the geochart library.
Version: 2.0.x
Author: Alexandre Roy
Repository: packages/geoview-geochart/
Features:
Dependencies:
geoview-core: ^2.0.0geochart: github:Canadian-Geospatial-Platform/geochart#developreact: ^18.3.1zustand: ~5.0.0Via Configuration:
{
"footerBar": {
"tabs": {
"core": ["geochart"]
}
}
}
Programmatic:
const mapViewer = cgpv.api.getMapViewer('mapId');
const geochartPlugin = mapViewer.plugins['geochart'];
if (geochartPlugin) {
// Geochart state is managed via Zustand store
// Configure via corePackagesConfig in map config
}
{
"footerBar": {
"tabs": {
"core": ["geochart"]
},
"selectedTab": "geochart"
}
}
"corePackagesConfig": [
{
"geochart": {
"charts": [
{
...
}]
}
}]
const mapViewer = cgpv.api.getMapViewer('mapId');
const geochartPlugin = mapViewer.plugins['geochart'];
// Geochart state is managed through Zustand store hooks
// Configure charts via corePackagesConfig in map configuration
See Also: Controllers API
Description: A layer comparison tool that allows users to swipe between layers to compare them side-by-side.
Version: 2.0.x
Author: Saleh Yassin
Repository: packages/geoview-swiper/
Features:
interactive flag)Dependencies:
geoview-core: ^2.0.0react-draggable: ^4.4.6lodash: ^4.17.21ol: 10.5.0react: ^18.3.1zustand: ~5.0.0Based on:
Via Configuration:
{
"corePackages": ["swiper"]
}
Programmatic:
const mapViewer = cgpv.api.getMapViewer('mapId');
const swiperPlugin = mapViewer.plugins['swiper'];
if (swiperPlugin) {
swiperPlugin.activateForLayer('layerPath', 'right');
swiperPlugin.setOrientation('vertical');
}
{
"map": {
"listOfGeoviewLayerConfig": [
{
"geoviewLayerId": "layer2020",
"geoviewLayerName": "2020 Imagery",
"geoviewLayerType": "xyzTiles",
"metadataAccessPath": "https://tiles.example.com/2020/{z}/{x}/{y}.png"
},
{
"geoviewLayerId": "layer2024",
"geoviewLayerName": "2024 Imagery",
"geoviewLayerType": "xyzTiles",
"metadataAccessPath": "https://tiles.example.com/2024/{z}/{x}/{y}.png"
}
]
},
"corePackages": ["swiper"],
"corePackagesConfig": [
{
"swiper": {
"orientation": "horizontal",
"keyboardOffset": 10,
"interactive": true,
"layers": [{ "layerPath": "esriFeatureLYR4/0", "side": "up" }]
}
}
]
}
Interactive mode: When
interactiveistrue, a Swiper section appears in each layer’s right panel (settings gear). Users can toggle a layer in/out of the swiper and pick its visible side. The side options follow the current orientation (left/right for vertical, up/down for horizontal). Wheninteractiveisfalse(default) the swiper stays static and author-defined. Eachlayersentry is an object{ layerPath, side };sidenames the visible side of the divider.
const mapViewer = cgpv.api.getMapViewer('mapId');
const swiperPlugin = mapViewer.plugins['swiper'];
if (swiperPlugin) {
// Activate swiper for a layer on the right side
swiperPlugin.activateForLayer('layerPath', 'right');
// Deactivate for a layer
swiperPlugin.deActivateForLayer('layerPath');
// Change the visible side for an active layer
swiperPlugin.setLayerSide('layerPath', 'left');
// Set orientation
swiperPlugin.setOrientation('vertical');
// Deactivate all
swiperPlugin.deActivateAll();
}
The swiper controller (accessible on the map’s controller registry as
swiperController) also exposessetLayers(entries),addLayerPath(layerPath, side?),setLayerSide(layerPath, side), andsetInteractive(interactive)for finer-grained control, including per-layer side placement.
Swiper behavior spans the package UI and core controllers; it is not owned by MapController alone.
prerender /
postrender, never renderer-container CSS clip-path (OL containers/canvases can be shared).
Use getRenderPixel for canvas coordinates and restore WebGL scissor state. Regressions must
prove non-target layers have no handlers.Promise.all; reconcile removed paths without clearing already clipped layers while slower
descendants load.LayerSetController.#handleMapPointerStopped
with the SwiperController pixel hit test; clear hover results over tight divider and larger
centered-handle bands. React stopPropagation can run after OL’s listener and cannot reliably
suppress queries.DragPan
and viewport pointer-events; restore both on stop and unmount. An isDragging ref guards resize
remounts so diagonal drag streams are not destroyed. MapController supplies generic map operations;
rendered components must not access MapViewer or the raw OpenLayers map directly.See Also: Controllers API
Description: A drawing and geometry editing package that allows users to create, modify, and manage geometries on the map.
Version: 1.0.0
Repository: packages/geoview-drawer/
Features:
Dependencies:
geoview-core: ^2.0.0react: ^18.3.1zustand: ~5.0.0Via Configuration:
{
"navBar": ["drawer"]
}
Programmatic:
const mapViewer = cgpv.api.getMapViewer('mapId');
// The drawer plugin wraps the DrawerController
// Drawing operations are managed via the controller
{
"navBar": ["drawer"],
"corePackagesConfig": [
{
"drawer": {
"style": {
"fillColor": "rgba(252, 241, 0, 0.3)",
"strokeColor": "#000000",
"strokeWidth": 1.3
},
"activeGeom": "LineString",
"geomTypes": ["Point", "Text", "LineString", "Polygon", "Rectangle", "Circle", "Star"],
"hideMeasurements": false
}
}
]
}
The Drawer package provides comprehensive keyboard shortcuts for efficient drawing and editing. These shortcuts (except undo/redo/escape) can be toggled on/off via the shortcuts button in the drawer toolbar, with the backtick key `, or programmatically via DrawerController.setShortcutsEnabled().
Note: Undo (Ctrl+Z), Redo (Ctrl+Y / Ctrl+Shift+Z), and Escape remain active at all times, regardless of the shortcuts toggle state.
| Shortcut | Action |
|---|---|
| D | Toggle Drawing mode |
| E | Toggle Editing mode |
| G | Cycle Geometry Type (forward) |
| Shift+G | Cycle Geometry Type (backward) |
| S | Open Style Menu |
| M | Toggle Measurements visibility |
| N | Toggle Snapping |
| Ctrl+Z | Undo last action |
| Ctrl+Y / Ctrl+Shift+Z | Redo action |
| Shift+S | Save / Download drawings (GeoJSON) |
| Shift+O | Open / Upload drawings |
| Shift+C | Clear all drawings |
| Escape | Clear selection / Exit edit mode |
When keyboard navigation is enabled (crosshair mode), the Drawer tool provides enhanced interaction capabilities:
While in drawing or editing mode with crosshairs active:
The drawer functionality is managed through the DrawerController. The drawer plugin wraps the controller and provides the UI.
// Drawing operations are managed internally by the DrawerController
// The controller provides methods like:
// - startDrawing(geomType)
// - stopDrawing()
// - toggleDrawing()
// - startEditing() / stopEditing()
// - undo() / redo()
// - clearDrawings()
// - downloadDrawings() (exports as GeoJSON)
// - uploadDrawings(file) (imports GeoJSON)
See Also: Controllers API
Description: An Area of Interest (AOI) selection panel that allows users to define and manage areas of interest on the map.
Version: 2.0.x
Repository: packages/geoview-aoi-panel/
Features:
Dependencies:
geoview-core: ^2.0.0react: ^18.3.1zustand: ~5.0.0Via Configuration:
{
"appBar": {
"tabs": {
"core": ["aoi-panel"]
}
}
}
{
"appBar": {
"tabs": {
"core": ["aoi-panel"]
}
},
"corePackagesConfig": [
{
"aoi-panel": {
"isOpen": true,
"aoiList": [
{
"imageUrl": "https://encrypted-tbn1.gstatic.com/licensed-image?q=tbn:ANd9GcSbleN5tjC2Dilx77SCBJD9f3CxlnDEEGx5qY786BpVlu4JLzUd1ixjIOfO1WX5mJjUQLmSSg4JFuNWgqGZJZDV7LBH8y3QBz3KrjuHdg",
"aoiTitle": "CN Tower",
"extent": [-79.3881, 43.6416, -79.3861, 43.6436]
},
{
"imageUrl": "https://encrypted-tbn0.gstatic.com/licensed-image?q=tbn:ANd9GcTCSU8D4pV4fY9MfYa6NZvpcMrCDhxE-ySOSzbxqSCC67_loNeJ9WI-2Ki7zCfU36M0Iwt7-4aw0y3_Vg8t_8sxo86xS6HVewQdYjOOXA",
"aoiTitle": "Parliament Hill",
"extent": [-75.7019, 45.4226, -75.6999, 45.4246]
}
],
"version": "1.0"
}
}
]
}
Description: A customizable legend panel that allows users to create highly customized legend layouts with headers, groups, and standard layer legends organized in a specific order.
Version: 2.0.x
Repository: packages/geoview-custom-legend/
Features:
Dependencies:
geoview-core: ^2.0.0react: ^18.3.1zustand: ~5.0.0Via Configuration:
{
"appBar": {
"tabs": {
"core": ["custom-legend"]
}
}
}
interface CustomLegendConfig {
// Optional
isOpen?: boolean;
title?: string;
legendList?: Array<TypeLegendItem>;
version?: string;
}
type TypeLegendItem = TypeLegendLayer | TypeHeaderLayer | TypeGroupLayer;
interface TypeLegendLayer {
type: 'layer';
layerPath: string;
}
interface TypeHeaderLayer {
type: 'header';
text: string;
description?: TypeDescription;
fontSize?: number;
fontWeight?: 'normal' | 'bold';
}
interface TypeGroupLayer {
type: 'group';
text: string;
description?: TypeDescription;
collapsed?: boolean;
children: Array<TypeLegendItem>;
}
interface TypeDescription {
text: string;
collapsed?: boolean;
}
Root Configuration:
Legend Item Types:
"layer""header""group"Basic Custom Legend:
{
"appBar": {
"tabs": {
"core": ["custom-legend"]
}
},
"corePackagesConfig": [
{
"custom-legend": {
"isOpen": false,
"title": "Map Layers",
"legendList": [
{
"type": "layer",
"layerPath": "weather-layer/temperature"
},
{
"type": "layer",
"layerPath": "weather-layer/precipitation"
}
]
}
}
]
}
With Headers and Groups:
{
"appBar": {
"tabs": {
"core": ["custom-legend"]
}
},
"corePackagesConfig": [
{
"custom-legend": {
"isOpen": true,
"title": "Environmental Data",
"legendList": [
{
"type": "header",
"text": "Weather Layers",
"description": {
"text": "Current weather conditions and forecasts",
"collapsed": false
},
"fontSize": 18,
"fontWeight": "bold"
},
{
"type": "group",
"text": "Temperature Data",
"description": {
"text": "Temperature forecasts and historical data",
"collapsed": true
},
"collapsed": false,
"children": [
{
"type": "layer",
"layerPath": "weather/current-temp"
},
{
"type": "layer",
"layerPath": "weather/forecast-temp"
}
]
},
{
"type": "header",
"text": "Administrative Boundaries",
"fontSize": 16
},
{
"type": "layer",
"layerPath": "boundaries/provinces"
}
]
}
}
]
}
Nested Groups:
{
"appBar": {
"tabs": {
"core": ["custom-legend"]
}
},
"corePackagesConfig": [
{
"custom-legend": {
"title": "Advanced Legend",
"legendList": [
{
"type": "group",
"text": "Environmental",
"collapsed": false,
"children": [
{
"type": "group",
"text": "Weather",
"description": {
"text": "Real-time weather data and forecasts",
"collapsed": false
},
"collapsed": true,
"children": [
{
"type": "layer",
"layerPath": "weather/temperature"
},
{
"type": "layer",
"layerPath": "weather/precipitation"
}
]
},
{
"type": "group",
"text": "Air Quality",
"description": {
"text": "Air quality monitoring stations and measurements",
"collapsed": true
},
"collapsed": true,
"children": [
{
"type": "layer",
"layerPath": "air-quality/pm25"
},
{
"type": "layer",
"layerPath": "air-quality/ozone"
}
]
}
]
}
]
}
}
]
}
legendList array1. Organizing by Data Type:
{
"legendList": [
{
"type": "header",
"text": "Satellite Imagery"
},
{
"type": "layer",
"layerPath": "satellite/2024"
},
{
"type": "layer",
"layerPath": "satellite/2020"
},
{
"type": "header",
"text": "Reference Layers"
},
{
"type": "layer",
"layerPath": "boundaries/provinces"
}
]
}
2. Thematic Grouping:
{
"legendList": [
{
"type": "group",
"text": "Transportation",
"children": [
{
"type": "layer",
"layerPath": "transport/roads"
},
{
"type": "layer",
"layerPath": "transport/railways"
},
{
"type": "layer",
"layerPath": "transport/airports"
}
]
}
]
}
3. Multi-Level Organization:
{
"legendList": [
{
"type": "group",
"text": "Base Maps",
"collapsed": false,
"children": [
{
"type": "group",
"text": "Topographic",
"children": [
{
"type": "layer",
"layerPath": "basemap/topo-light"
},
{
"type": "layer",
"layerPath": "basemap/topo-dark"
}
]
},
{
"type": "group",
"text": "Satellite",
"children": [
{
"type": "layer",
"layerPath": "basemap/satellite"
}
]
}
]
}
]
}
See Also:
Description: An information panel package that displays content about the map or application. Supports multiple content formats including Markdown documents, custom content arrays, or structured information with title, logo, description, and links.
Version: 2.0.x
Repository: packages/geoview-about-panel/
Features:
Dependencies:
geoview-core: ^2.0.0markdown-to-jsx: ^7.5.0react: ^18.3.1zustand: ~5.0.0Via Configuration:
{
"appBar": {
"tabs": {
"core": ["about-panel"]
}
}
}
Programmatic:
// About Panel is automatically loaded when included in appBar.tabs.core
// Access via AppBar tabs
const appBarApi = cgpv.api.maps['mapId'].appBar;
The About Panel supports three content formats with the following priority:
mdPath) - Highest priority, displays content from a Markdown documentmdContent) - Displays multiple Markdown strings as separate sectionstitle, logoPath, description, link) - Lowest priority, displays structured contentIf no content is configured, an empty panel will be displayed.
interface AboutPanelConfig {
// Optional
isOpen?: boolean;
aboutTitle?: string;
iconPath?: string;
mdPath?: string;
mdContent?: string[];
title?: string;
logoPath?: string;
description?: string;
link?: string;
version?: string;
}
Example 1: Markdown File
{
"map": {
"interaction": "dynamic",
"viewSettings": {
"projection": 3978
}
},
"appBar": {
"tabs": {
"core": ["about-panel"]
}
},
"corePackagesConfig": [
{
"about-panel": {
"isOpen": true,
"aboutTitle": "About Atlas of Canada",
"iconPath": "./img/canada-icon.png",
"mdPath": "./img/about-atlas.md"
}
}
]
}
Example 2: Markdown Content Array
{
"appBar": {
"tabs": {
"core": ["about-panel"]
}
},
"corePackagesConfig": [
{
"about-panel": {
"isOpen": false,
"mdContent": [
"# Atlas of Canada\n\n",
"## Discover Canada through geography\n\nThe Atlas of Canada provides interactive and static maps of Canada.",
"## Key Features\n\n- **Interactive Maps** - Dynamic maps with multiple layers\n- **Historical Archive** - Digitized atlas editions from 1906\n- **Educational Resources** - Teaching materials and data",
"## Learn More\n\n[Visit the Atlas of Canada →](https://natural-resources.canada.ca/maps-tools-publications/maps/atlas-canada)"
]
}
}
]
}
Example 3: Default Content (Structured)
{
"appBar": {
"tabs": {
"core": ["about-panel"]
}
},
"corePackagesConfig": [
{
"about-panel": {
"isOpen": false,
"title": "GeoView Mapping Application",
"logoPath": "./img/logo.png",
"description": "A powerful and flexible geospatial viewer for the Canadian Geospatial Platform. Explore maps, analyze data, and discover geographic insights.",
"link": "https://canadian-geospatial-platform.github.io/geoview/"
}
}
]
}
Example 4: Minimal Configuration
{
"appBar": {
"tabs": {
"core": ["about-panel"]
}
},
"corePackagesConfig": [
{
"about-panel": {
"title": "About This Map"
}
}
]
}
When using mdPath or mdContent, the About Panel supports standard Markdown syntax including:
# H1, ## H2, etc.**bold**, *italic*[text](url)- item or 1. item`inline` or ` block `\n for new lines in stringsMarkdown Example File:
# Atlas of Canada

## Discover Canada through geography
The Atlas of Canada provides interactive and static maps of Canada, from past to present.
## Key Features
- **Interactive Maps** - Explore dynamic maps with layers covering physical geography, demographics, economy, and more
- **Historical Archive** - Access digitized versions of atlas editions dating back to 1906
- **Thematic Collections** - Browse curated map collections on topics like climate and natural resources
## Learn More
Visit the full Atlas of Canada and discover the stories told through maps:
[Visit the Atlas of Canada →](https://natural-resources.canada.ca/maps-tools-publications/maps/atlas-canada)
---
_Mapping Canada's past, present, and future since 1906_
1. Simple About Page:
{
"about-panel": {
"title": "My Mapping Application",
"description": "A custom GeoView application for environmental monitoring.",
"link": "https://example.com/help"
}
}
2. Documentation from Markdown File:
{
"about-panel": {
"isOpen": false,
"aboutTitle": "User Guide",
"mdPath": "./docs/user-guide.md"
}
}
3. Multi-Section Content:
{
"about-panel": {
"isOpen": true,
"mdContent": [
"# About This Map\n\nWelcome to our interactive mapping application.",
"## Data Sources\n\n- Natural Resources Canada\n- Statistics Canada",
"## Terms of Use\n\nThis application is provided as-is..."
]
}
}
4. Atlas of Canada Example:
{
"about-panel": {
"isOpen": true,
"title": "Atlas of Canada",
"logoPath": "../../../img/atlas_banner.png",
"description": "The Atlas of Canada provides interactive and static maps of Canada, from past to present. Find, explore and download a variety of maps and discover other sources of Canadian geographic information.",
"link": "https://natural-resources.canada.ca/maps-tools-publications/maps/atlas-canada"
}
}
The About Panel includes:
aboutTitle propertyThe panel gracefully handles:
mdPath fails to loadSee Also:
Description: A customizable filter panel package that provides layer attribute filtering capabilities. Supports multiple filter types including select, multiselect, range, and date filters for querying and displaying map layer data based on attribute values.
Version: 2.0.x
Repository: packages/geoview-filter-panel/
Features:
Dependencies:
geoview-core: ^2.0.0react: ^18.3.1zustand: ~5.0.0Via Configuration:
{
"appBar": {
"tabs": {
"core": ["filter-panel"]
}
}
}
Programmatic:
// Filter Panel is automatically loaded when included in appBar.tabs.core
// Access via AppBar tabs
const appBarApi = cgpv.api.maps['mapId'].appBar;
The Filter Panel supports four filter types:
The schema uses a discriminated union based on filterType. Each filter type has specific properties:
interface FilterPanelConfig {
isOpen?: boolean;
version?: string;
title?: string;
layers?: Array<{
layerPath: string;
filterName?: string;
enabled?: boolean;
defaultCollapsed?: boolean;
attributes?: Array<SelectFilterAttribute | MultiselectFilterAttribute | RangeFilterAttribute | DateFilterAttribute>;
}>;
}
type SelectFilterAttribute = {
fieldName: string;
displayLabel: string;
filterType: 'select';
enabled?: boolean;
defaultValues?: string | number | null;
domain?: Array<{ value: string | number; label: string }>;
filterMissingDomainValues?: boolean;
};
type MultiselectFilterAttribute = {
fieldName: string;
displayLabel: string;
filterType: 'multiselect';
enabled?: boolean;
defaultValues?: Array<string | number> | null;
domain?: Array<{ value: string | number; label: string }>;
filterMissingDomainValues?: boolean;
searchable?: boolean;
};
type RangeFilterAttribute = {
fieldName: string;
displayLabel: string;
filterType: 'range';
enabled?: boolean;
rangeStep?: number;
numberFormat?: 'number' | 'currency';
currency?: string;
defaultValues?: { min: number | null; max: number | null } | null;
};
type DateFilterAttribute = {
fieldName: string;
displayLabel: string;
filterType: 'date';
enabled?: boolean;
dateStep?: 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year';
defaultValues?: { start: string | null; end: string | null } | null;
};
Top-level properties:
Layer properties:
Attribute properties (common to all types):
"select", "multiselect", "range", "date"Select filter properties:
| defaultValues (string | number | null): Initial single value |
| value (string | number, required): The raw value from the layer |
Multiselect filter properties:
| defaultValues (array | null): Initial array of selected values (e.g., ["value1", "value2"]) |
Range filter properties:
| defaultValues (object | null): Initial range with min and max properties (e.g., { "min": 0, "max": 100 }) |
"number" shows plain numbers grouped with thousand separators; "currency" adds a currency symbol and two decimal places. Formatting is locale-aware based on the viewer’s display language (e.g., 1,234.56 in English vs 1 234,56 in French).numberFormat is "currency" (e.g., "CAD", "USD", "JPY", "EUR").Date filter properties:
| defaultValues (object | null): Initial date range with start and end properties in YYYY-MM-DD format (e.g., { "start": "2020-01-01", "end": "2020-12-31" }) |
"second" - Second-level precision"minute" - Minute-level precision"hour" - Hourly increments"day" - Daily increments (default)"week" - Weekly increments"month" - Monthly increments (calendar-aware, handles 28-31 day months)"year" - Yearly increments (calendar-aware, handles leap years)Example 1: Basic Multiselect Filter
{
"appBar": {
"tabs": {
"core": ["filter-panel"]
}
},
"corePackagesConfig": [
{
"filter-panel": {
"isOpen": false,
"title": "Filter Layers",
"layers": [
{
"layerPath": "cities-layer",
"filterName": "Canadian Cities",
"enabled": true,
"defaultCollapsed": false,
"attributes": [
{
"fieldName": "province",
"displayLabel": "Province",
"filterType": "multiselect",
"enabled": true,
"defaultValues": []
}
]
}
]
}
}
]
}
Example 2: Date Filter with Custom Step
{
"appBar": {
"tabs": {
"core": ["filter-panel"]
}
},
"corePackagesConfig": [
{
"filter-panel": {
"isOpen": true,
"title": "Historical Events",
"layers": [
{
"layerPath": "historical-data/0",
"filterName": "Historical Events",
"enabled": true,
"attributes": [
{
"fieldName": "event_date",
"displayLabel": "Event Date",
"filterType": "date",
"enabled": true,
"dateStep": "year",
"defaultValues": {
"start": "1800-01-01",
"end": "2000-12-31"
}
}
]
}
]
}
}
]
}
Example 3: Multiple Filter Types
{
"appBar": {
"tabs": {
"core": ["filter-panel"]
}
},
"corePackagesConfig": [
{
"filter-panel": {
"isOpen": true,
"title": "Population Filters",
"layers": [
{
"layerPath": "population-data",
"filterName": "Population Data",
"enabled": true,
"defaultCollapsed": false,
"attributes": [
{
"fieldName": "city_name",
"displayLabel": "City",
"filterType": "select",
"enabled": true
},
{
"fieldName": "population",
"displayLabel": "Population Range",
"filterType": "range",
"enabled": true,
"rangeStep": 10000,
"defaultValues": { "min": null, "max": null }
},
{
"fieldName": "census_date",
"displayLabel": "Census Date",
"filterType": "date",
"enabled": true,
"defaultValues": { "start": null, "end": null }
}
]
}
]
}
}
]
}
Example 3: Custom Settings
{
"appBar": {
"tabs": {
"core": ["filter-panel"]
}
},
"corePackagesConfig": [
{
"filter-panel": {
"isOpen": false,
"title": "Environmental Filters",
"layers": [
{
"layerPath": "environmental-data",
"filterName": "Environmental Monitoring",
"enabled": true,
"attributes": [
{
"fieldName": "pollutant_type",
"displayLabel": "Pollutant Type",
"filterType": "multiselect"
},
{
"fieldName": "concentration",
"displayLabel": "Concentration (ppm)",
"filterType": "range",
"rangeStep": 0.1
}
]
}
]
}
}
]
}
Example 4: Multiple Layers
{
"appBar": {
"tabs": {
"core": ["filter-panel"]
}
},
"corePackagesConfig": [
{
"filter-panel": {
"layers": [
{
"layerPath": "weather-stations",
"filterName": "Weather Stations",
"attributes": [
{
"fieldName": "station_type",
"displayLabel": "Station Type",
"filterType": "select"
},
{
"fieldName": "elevation",
"displayLabel": "Elevation (m)",
"filterType": "range"
}
]
},
{
"layerPath": "climate-data",
"filterName": "Climate Data",
"attributes": [
{
"fieldName": "temperature",
"displayLabel": "Temperature (°C)",
"filterType": "range"
},
{
"fieldName": "observation_date",
"displayLabel": "Observation Date",
"filterType": "date"
}
]
}
]
}
}
]
}
Example 5: Domain Mapping with Custom Labels
Domain mapping allows you to display user-friendly labels for coded values in select and multiselect filters. This is particularly useful when your layer data contains abbreviations or codes.
{
"appBar": {
"tabs": {
"core": ["filter-panel"]
}
},
"corePackagesConfig": [
{
"filter-panel": {
"enabled": true,
"layers": [
{
"layerPath": "land-use",
"filterName": "Land Use Classification",
"attributes": [
{
"fieldName": "use_code",
"displayLabel": "Land Use Type",
"filterType": "multiselect",
"domain": [
{ "value": "RES", "label": "Residential" },
{ "value": "COM", "label": "Commercial" },
{ "value": "IND", "label": "Industrial" },
{ "value": "AGR", "label": "Agricultural" },
{ "value": "FOR", "label": "Forest" },
{ "value": "WAT", "label": "Water Body" }
]
}
]
}
]
}
}
]
}
In this example, the layer contains land use codes like “RES”, “COM”, “IND”, but users see “Residential”, “Commercial”, “Industrial” in the filter dropdown.
Example 6: Domain Filtering with filterMissingDomainValues
When filterMissingDomainValues is true, only features with values in the domain are shown. This is useful for cleaning up data with unexpected or invalid values.
{
"appBar": {
"tabs": {
"core": ["filter-panel"]
}
},
"corePackagesConfig": [
{
"filter-panel": {
"enabled": true,
"layers": [
{
"layerPath": "infrastructure",
"filterName": "Infrastructure Assets",
"attributes": [
{
"fieldName": "asset_status",
"displayLabel": "Asset Status",
"filterType": "select",
"domain": [
{ "value": "A", "label": "Active" },
{ "value": "P", "label": "Planned" },
{ "value": "R", "label": "Retired" },
{ "value": "M", "label": "Maintenance" }
],
"filterMissingDomainValues": true
},
{
"fieldName": "priority",
"displayLabel": "Priority Level",
"filterType": "multiselect",
"domain": [
{ "value": 1, "label": "Critical" },
{ "value": 2, "label": "High" },
{ "value": 3, "label": "Medium" },
{ "value": 4, "label": "Low" }
],
"filterMissingDomainValues": true
}
]
}
]
}
}
]
}
In this example:
filterMissingDomainValues: true ensures only features with status codes A, P, R, or M are displayedDomain Mapping Notes:
"select" and "multiselect" filter typesfilterMissingDomainValues: false (default), values not in the domain appear with their raw valuefilterMissingDomainValues: true, features with values outside the domain are hidden from the mapSelect Filter:
{
"fieldName": "status",
"displayLabel": "Status",
"filterType": "select"
}
Multiselect Filter:
searchable: true) to filter the checkbox list, useful for long value lists{
"fieldName": "category",
"displayLabel": "Category",
"filterType": "multiselect",
"defaultValues": [],
"searchable": true
}
Range Filter:
{
"fieldName": "population",
"displayLabel": "Population",
"filterType": "range",
"defaultValues": { "min": null, "max": null }
}
Date Filter:
{
"fieldName": "date_created",
"displayLabel": "Date Created",
"filterType": "date",
"defaultValues": { "start": null, "end": null }
}
1. City Explorer:
{
"filter-panel": {
"layers": [
{
"layerPath": "canadian-cities",
"filterName": "Canadian Cities",
"attributes": [
{
"fieldName": "province",
"displayLabel": "Province",
"filterType": "multiselect"
},
{
"fieldName": "population",
"displayLabel": "Population",
"filterType": "range"
}
]
}
]
}
}
2. Environmental Monitoring:
{
"filter-panel": {
"layers": [
{
"layerPath": "air-quality",
"filterName": "Air Quality Stations",
"attributes": [
{
"fieldName": "pollutant",
"displayLabel": "Pollutant Type",
"filterType": "select"
},
{
"fieldName": "concentration",
"displayLabel": "Concentration (ppm)",
"filterType": "range"
},
{
"fieldName": "measurement_date",
"displayLabel": "Date",
"filterType": "date"
}
]
}
]
}
}
3. Real Estate Search:
{
"filter-panel": {
"layers": [
{
"layerPath": "properties",
"filterName": "Properties",
"attributes": [
{
"fieldName": "property_type",
"displayLabel": "Type",
"filterType": "multiselect"
},
{
"fieldName": "price",
"displayLabel": "Price Range",
"filterType": "range"
},
{
"fieldName": "bedrooms",
"displayLabel": "Bedrooms",
"filterType": "range"
},
{
"fieldName": "listing_date",
"displayLabel": "Listed",
"filterType": "date"
}
]
}
]
}
}
The Filter Panel includes:
The panel gracefully handles:
See Also:
To create a custom GeoView package:
packages/
+-- geoview-my-package/
+-- package.json
+-- tsconfig.json
+-- README.md
+-- src/
│ +-- index.tsx
│ +-- my-package-panel.tsx
│ +-- my-package-style.ts
│ +-- my-package-types.ts
+-- default-config-my-package.json
Note: Controllers live in
geoview-core/src/core/controllers/, not inside the plugin package. If your package needs a controller, create it atpackages/geoview-core/src/core/controllers/my-package-controller.tsand register it in theControllerRegistry.
{
"name": "geoview-my-package",
"version": "1.0.0",
"description": "My custom GeoView package",
"main": "src/index.tsx",
"peerDependencies": {
"geoview-core": "workspace:^2.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"zustand": "~5.0.0"
},
"devDependencies": {
"geoview-core": "workspace:^2.0.0",
"typescript": "^5.6.3"
}
}
Create a React component for your package panel:
// my-package-panel.tsx
import { useEffect } from 'react';
export function MyPackagePanel({ mapId }: { mapId: string }): JSX.Element {
useEffect(() => {
console.log('Package initialized for map:', mapId);
}, [mapId]);
return (
<div className="my-package-panel">
<h2>My Package</h2>
{/* Package UI */}
</div>
);
}
For app bar packages, extend AppBarPlugin. For nav bar packages, extend NavBarPlugin:
// index.tsx
import { AppBarPlugin } from 'geoview-core/api/plugin/appbar-plugin';
import { MyPackagePanel } from './my-package-panel';
import schema from '../schema.json';
import defaultConfig from '../default-config-my-package.json';
class MyPackagePlugin extends AppBarPlugin {
override schema(): unknown {
return schema;
}
override defaultConfig(): unknown {
return defaultConfig;
}
override defaultTranslations(): Record<string, unknown> {
return {
en: { MyPackage: { title: 'My Package' } },
fr: { MyPackage: { title: 'Mon Paquet' } },
};
}
}
export default MyPackagePlugin;
See Core Packages Guide for the full steps to register in webpack.common.js, rush.json, schema.json, and map-schema-types.ts.
# Build all packages
rush build
# Build specific package
cd packages/geoview-time-slider
npm run build
# Install all dependencies
rush install
# Update dependencies
rush update
# Add to specific package
cd packages/geoview-my-package
rush add -p lodash --caret