geoview

Core Packages Reference

This document provides comprehensive API reference and configuration details for all GeoView core packages.

Table of Contents

  1. geoview-time-slider
  2. geoview-geochart
  3. geoview-swiper
  4. geoview-drawer
  5. geoview-aoi-panel
  6. geoview-custom-legend
  7. geoview-about-panel
  8. geoview-filter-panel

1. geoview-time-slider

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:

Installation

The 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');
}

Temporal Modes

The time slider supports two distinct modes for handling temporal data:

Discrete Mode (nearestValues: 'discrete')

Use When:

Behavior:

Example 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'

Continuous Mode (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):


Configuration Examples

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"
                ]
              }
            }
          }
        ]
      }
    }
  ]
}

API Methods

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

TypeTimeSliderValues Interface

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
}

Best Practices

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:

UI/UX Tips:


2. geoview-geochart

Description: 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:

Installation

Via 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
}

Configuration Example

{
  "footerBar": {
    "tabs": {
      "core": ["geochart"]
    },
    "selectedTab": "geochart"
  }
}
"corePackagesConfig": [
        {
            "geochart": {
                "charts": [
                    {
                      ...
                    }]
            }
        }]

API Methods

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


3. geoview-swiper

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:

Dependencies:

Based on:

Installation

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');
}

Configuration Example

{
  "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 interactive is true, 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). When interactive is false (default) the swiper stays static and author-defined. Each layers entry is an object { layerPath, side }; side names the visible side of the divider.

API Methods

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 exposes setLayers(entries), addLayerPath(layerPath, side?), setLayerSide(layerPath, side), and setInteractive(interactive) for finer-grained control, including per-layer side placement.

Swiper runtime integration

Swiper behavior spans the package UI and core controllers; it is not owned by MapController alone.

See Also: Controllers API


4. geoview-drawer

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:

Installation

Via Configuration:

{
  "navBar": ["drawer"]
}

Programmatic:

const mapViewer = cgpv.api.getMapViewer('mapId');
// The drawer plugin wraps the DrawerController
// Drawing operations are managed via the controller

Configuration Example

{
  "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
      }
    }
  ]
}

Keyboard Shortcuts

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

Crosshair Integration

When keyboard navigation is enabled (crosshair mode), the Drawer tool provides enhanced interaction capabilities:

Drawing with Crosshairs

Editing with Crosshairs

Zoom Control with Crosshairs

While in drawing or editing mode with crosshairs active:

API Methods

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


5. geoview-aoi-panel

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:

Installation

Via Configuration:

{
  "appBar": {
    "tabs": {
      "core": ["aoi-panel"]
    }
  }
}

Configuration Example

{
  "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"
      }
    }
  ]
}

6. geoview-custom-legend

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:

Installation

Via Configuration:

{
  "appBar": {
    "tabs": {
      "core": ["custom-legend"]
    }
  }
}

Configuration Schema

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;
}

Configuration Properties

Root Configuration:

Legend Item Types:

  1. TypeLegendLayer - Display a standard GeoView layer legend:
    • type (required): Must be "layer"
    • layerPath (required): Layer path identifying the layer (e.g., “layerId/sublayerId”)
  2. TypeHeaderLayer - Display a text header for organizing sections:
    • type (required): Must be "header"
    • text (required): Header text to display
    • description: Optional description object with:
      • text (required): Descriptive text to display below header
      • collapsed (optional): Whether description starts collapsed (default: false)
    • fontSize (number, range: 8-32, default: 16): Font size in pixels
    • fontWeight (string, default: “bold”): Font weight (“normal” or “bold”)
  3. TypeGroupLayer - Display a collapsible group of legend items:
    • type (required): Must be "group"
    • text (required): Group title text
    • description: Optional description object with:
      • text (required): Descriptive text to display below group title
      • collapsed (optional): Whether description starts collapsed (default: false)
    • collapsed (boolean, default: false): Initial collapsed state of the group itself
    • children (required): Array of child legend items (minimum 1 item)

Configuration Examples

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"
                  }
                ]
              }
            ]
          }
        ]
      }
    }
  ]
}

Usage Notes

Common Use Cases

1. 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:



7. geoview-about-panel

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:

Installation

Via 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;

Content Formats

The About Panel supports three content formats with the following priority:

  1. Markdown File Path (mdPath) - Highest priority, displays content from a Markdown document
  2. Markdown Content Array (mdContent) - Displays multiple Markdown strings as separate sections
  3. Default Content (title, logoPath, description, link) - Lowest priority, displays structured content

If no content is configured, an empty panel will be displayed.

Configuration Schema

interface AboutPanelConfig {
  // Optional
  isOpen?: boolean;
  aboutTitle?: string;
  iconPath?: string;
  mdPath?: string;
  mdContent?: string[];
  title?: string;
  logoPath?: string;
  description?: string;
  link?: string;
  version?: string;
}

Configuration Properties

Configuration Examples

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![Atlas Banner](./img/atlas_banner.png)",
          "## 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"
      }
    }
  ]
}

Markdown Support

When using mdPath or mdContent, the About Panel supports standard Markdown syntax including:

Markdown Example File:

# Atlas of Canada

![Atlas Banner](../img/atlas_banner.png)

## 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_

Usage Notes

Common Use Cases

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"
  }
}

Accessibility

The About Panel includes:

Performance Considerations

Error Handling

The panel gracefully handles:

See Also:


8. geoview-filter-panel

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:

Installation

Via 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;

Filter Types

The Filter Panel supports four filter types:

  1. Select - Single-value dropdown selection
  2. Multiselect - Multiple-value checkbox list with “All” option
  3. Range - Numeric min/max range with slider
  4. Date - Date range picker with start/end dates and calendar-aware keyboard stepping

Configuration Schema

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;
};

Configuration Properties

Top-level properties:

Layer properties:

Attribute properties (common to all types):

Select filter properties:

Multiselect filter properties:

Range filter properties:

Date filter properties:

Configuration Examples

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:

Domain Mapping Notes:

Filter Type Details

Select Filter:

{
  "fieldName": "status",
  "displayLabel": "Status",
  "filterType": "select"
}

Multiselect Filter:

{
  "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 }
}

Usage Notes

Common Use Cases

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"
          }
        ]
      }
    ]
  }
}

Accessibility

The Filter Panel includes:

Performance Considerations

Error Handling

The panel gracefully handles:

See Also:


Package Development

Creating a Custom Package

To create a custom GeoView package:

  1. Create Package Structure:
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 at packages/geoview-core/src/core/controllers/my-package-controller.ts and register it in the ControllerRegistry.

  1. package.json:
{
  "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"
  }
}
  1. Create UI Component:

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>
  );
}
  1. Create Plugin Entry Point:

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;
  1. Register in webpack and configuration:

See Core Packages Guide for the full steps to register in webpack.common.js, rush.json, schema.json, and map-schema-types.ts.

Package Best Practices

  1. Use Controllers: State management should go through controllers
  2. Type Safety: Leverage TypeScript for all interfaces and types
  3. Accessibility: Ensure package UI is accessible (WCAG 2.1 AA)
  4. Internationalization: Support both English and French
  5. Error Handling: Gracefully handle errors and provide user feedback
  6. Documentation: Document all public APIs and configuration options
  7. Testing: Write tests for controllers and integration tests for UI

Package Management

Building Packages

# Build all packages
rush build

# Build specific package
cd packages/geoview-time-slider
npm run build

Installing Dependencies

# Install all dependencies
rush install

# Update dependencies
rush update

Adding Dependencies

# Add to specific package
cd packages/geoview-my-package
rush add -p lodash --caret

See Also