Skip to content

feat(Text Layer): Separate text from geometry layer and add UI to toggle it's visibility#3352

Open
MatthewMuehlhauserNRCan wants to merge 8 commits intoCanadian-Geospatial-Platform:developfrom
MatthewMuehlhauserNRCan:3036-text-layer
Open

feat(Text Layer): Separate text from geometry layer and add UI to toggle it's visibility#3352
MatthewMuehlhauserNRCan wants to merge 8 commits intoCanadian-Geospatial-Platform:developfrom
MatthewMuehlhauserNRCan:3036-text-layer

Conversation

@MatthewMuehlhauserNRCan
Copy link
Member

@MatthewMuehlhauserNRCan MatthewMuehlhauserNRCan commented Mar 16, 2026

Description

Separated the text layer from the geometry layer when labels are configured so that the declutter mode will work properly. Also allowed to create a button to toggle the labels in the UI.

Fixes #3227

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Host Updated 2026-03-16 @ 3:55PM

Tried toggling the text in the UI and different combinations of the layer being turned on/off and text on/off. Also, the different layers have different declutterModes, so the results of decluttering can be seen.

Layer Text Demo Navigator Page

Checklist:

  • I have build (rush build) and deploy (rush host) my PR
  • I have connected the issues(s) to this PR
  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • I have created new issue(s) related to the outcome of this PR is needed
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

This change is Reviewable

Copy link
Member

@jolevesq jolevesq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jolevesq reviewed 11 files and all commit messages, and made 4 comments.
Reviewable status: all files reviewed, 4 unresolved discussions (waiting on MatthewMuehlhauserNRCan).


packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts line 429 at r1 (raw file):

  /**
   * Gets the OpenLayers text layer if one exists.
   * @returns {VectorLayer<VectorSource> | undefined} The text layer or undefined if no text layer exists.

Blank line and no need to set param or return type (for other functions below as well)


packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts line 503 at r1 (raw file):

  /**
   * Emits an event to all handlers.
   * @param {TextVisibleChangedEvent} event - The event to emit

No need for private


packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts line 562 at r1 (raw file):

  /**
   * Calculates a text-only style (no geometry) for the given feature.

Blank, param type and static


packages/geoview-core/src/geo/utils/renderer/geoview-text-renderer.ts line 3 at r1 (raw file):

import { Stroke, Fill, Text } from 'ol/style';
import type { FeatureLike } from 'ol/Feature';
import type Feature from 'ol/Feature';

Nice split to remove complexity inside geoview-renderer!

Copy link
Member Author

@MatthewMuehlhauserNRCan MatthewMuehlhauserNRCan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MatthewMuehlhauserNRCan made 3 comments and resolved 1 discussion.
Reviewable status: all files reviewed, 3 unresolved discussions (waiting on jolevesq).


packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts line 429 at r1 (raw file):

Previously, jolevesq (Johann Levesque) wrote…

Blank line and no need to set param or return type (for other functions below as well)

Done.


packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts line 503 at r1 (raw file):

Previously, jolevesq (Johann Levesque) wrote…

No need for private

Done.


packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts line 562 at r1 (raw file):

Previously, jolevesq (Johann Levesque) wrote…

Blank, param type and static

Done.

Copy link
Member Author

@MatthewMuehlhauserNRCan MatthewMuehlhauserNRCan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MatthewMuehlhauserNRCan reviewed 7 files and all commit messages, and made 1 comment.
Reviewable status: all files reviewed, 4 unresolved discussions (waiting on jolevesq).


packages/geoview-core/public/locales/fr/translation.json line 119 at r2 (raw file):

    "subLayersCount": "{count} sous-couches",
    "itemsCount": "{count} sur {totalCount} classes",
    "legendInstructions": "Instructions de la légende",

Seemed like it wasn't translated. Did a quick search for context to see where it was used and didn't see any hits ...

@Alex-NRCan Alex-NRCan requested a review from jolevesq March 17, 2026 14:56
Copy link
Member

@Alex-NRCan Alex-NRCan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Alex-NRCan reviewed 13 files and all commit messages, and made 2 comments.
Reviewable status: all files reviewed, 6 unresolved discussions (waiting on jolevesq and MatthewMuehlhauserNRCan).


packages/geoview-core/src/api/event-processors/event-processor-children/map-event-processor.ts line 1440 at r2 (raw file):

        // Make sure text layer z-index above other vector layers
        const gvLayer = this.getMapViewerLayerAPI(mapId).getGeoviewLayerIfExists(orderedLayerInfo.layerPath);

Instead of re-getting the gvLayer again with the orderedLayerInfo.layerPath, you could get it originally instead of getting the olLayer above. You wouldn't have to call get..LayerIfExists() twice.

Suggestion: Also, more importantly, similar to how you've done it with setVisible, we should probably have a setZIndex on our AbstractGVLayer base class which would call an overridable method onSetZIndex() which would do this.getOLLayer().setZIndex() in the base mother class. And then, in AbstractGVVector we would override onSetZIndex() to call the parent and then do the this.getTextOLLayer()?.setZIndex();

That way the logic of the text layer following the zIndex of the geometry layer is handled internally inside AbstractGVVector instead of through the map-event-processor only.

(Note, setVisible should call a onSetVisible and it's that function that should be overridable to make it even more clean, but it's also ok to override setVisible directly for now)


packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts line 173 at r2 (raw file):

    // Sync text layer visibility if it exists
    // Text layer visibility = layerVisibility && textVisible
    if (this.#textOLLayer) {

Very minor: what you have works, and this works too on a 1-liner: this.#textOLLayer?.setVisible(...); here and couple places

@Alex-NRCan
Copy link
Member

Nicely done overall! ! 👍

@jolevesq jolevesq requested a review from DamonU2 March 17, 2026 15:30
Copy link
Member Author

@MatthewMuehlhauserNRCan MatthewMuehlhauserNRCan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MatthewMuehlhauserNRCan made 2 comments.
Reviewable status: 11 of 14 files reviewed, 6 unresolved discussions (waiting on Alex-NRCan, DamonU2, and jolevesq).


packages/geoview-core/src/api/event-processors/event-processor-children/map-event-processor.ts line 1440 at r2 (raw file):

Previously, Alex-NRCan (Alex) wrote…

Instead of re-getting the gvLayer again with the orderedLayerInfo.layerPath, you could get it originally instead of getting the olLayer above. You wouldn't have to call get..LayerIfExists() twice.

Suggestion: Also, more importantly, similar to how you've done it with setVisible, we should probably have a setZIndex on our AbstractGVLayer base class which would call an overridable method onSetZIndex() which would do this.getOLLayer().setZIndex() in the base mother class. And then, in AbstractGVVector we would override onSetZIndex() to call the parent and then do the this.getTextOLLayer()?.setZIndex();

That way the logic of the text layer following the zIndex of the geometry layer is handled internally inside AbstractGVVector instead of through the map-event-processor only.

(Note, setVisible should call a onSetVisible and it's that function that should be overridable to make it even more clean, but it's also ok to override setVisible directly for now)

Done I think I got everything.


packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts line 173 at r2 (raw file):

Previously, Alex-NRCan (Alex) wrote…

Very minor: what you have works, and this works too on a 1-liner: this.#textOLLayer?.setVisible(...); here and couple places

Nice, I've added those changes.

Copy link
Member

@DamonU2 DamonU2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DamonU2 reviewed 14 files and all commit messages, and made 8 comments.
Reviewable status: all files reviewed, 14 unresolved discussions (waiting on Alex-NRCan, jolevesq, and MatthewMuehlhauserNRCan).


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 418 at r3 (raw file):

   * Sets the visibility of the layer (true or false).
   *
   * @param layerVisibility The visibility of the layer.

Needs a dash between layerVisibility and the description


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 419 at r3 (raw file):

   *
   * @param layerVisibility The visibility of the layer.
   * @param emitVisibleChanged Optional, whether to emit a visible changed event after updating the visibility. Defaults to true.

same here


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 430 at r3 (raw file):

   *
   * @param zIndex The z-index of the layer.
   * @param emitZIndexChanged Optional, whether to emit a z-index changed event after updating the z-index. Defaults to true.

Both of these too


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 568 at r3 (raw file):

  /**
   * Emits a z-index changed event.

Blank line after


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 569 at r3 (raw file):

  /**
   * Emits a z-index changed event.
   * @param {LayerZIndexChangedEvent} event - The event to emit

No type needed anymore


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 579 at r3 (raw file):

  /**
   * Registers a z-index changed event handler.
   * @param {LayerZIndexChangedDelegate} callback - The callback to be executed whenever the event is emitted

Same here


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 588 at r3 (raw file):

  /**
   * Unregisters a z-index changed event handler.
   * @param {LayerZIndexChangedDelegate} callback - The callback to stop being called whenever the event is emitted

And here


packages/geoview-core/src/geo/utils/renderer/geoview-text-renderer.ts line 20 at r3 (raw file):

   * This method returns true if a style config has a text configuration
   * @param {TypeLayerStyleConfig} layerStyle - The layer style
   * @returns {boolean} If the style has a text config

All functions in this file are still the old style of JSDocs

Copy link
Member Author

@MatthewMuehlhauserNRCan MatthewMuehlhauserNRCan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MatthewMuehlhauserNRCan made 8 comments.
Reviewable status: 12 of 14 files reviewed, 14 unresolved discussions (waiting on Alex-NRCan, DamonU2, and jolevesq).


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 418 at r3 (raw file):

Previously, DamonU2 (Damon Ulmi) wrote…

Needs a dash between layerVisibility and the description

Done.


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 419 at r3 (raw file):

Previously, DamonU2 (Damon Ulmi) wrote…

same here

Done.


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 430 at r3 (raw file):

Previously, DamonU2 (Damon Ulmi) wrote…

Both of these too

Done.


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 568 at r3 (raw file):

Previously, DamonU2 (Damon Ulmi) wrote…

Blank line after

Done.


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 569 at r3 (raw file):

Previously, DamonU2 (Damon Ulmi) wrote…

No type needed anymore

Done.


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 579 at r3 (raw file):

Previously, DamonU2 (Damon Ulmi) wrote…

Same here

Done.


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 588 at r3 (raw file):

Previously, DamonU2 (Damon Ulmi) wrote…

And here

Done.


packages/geoview-core/src/geo/utils/renderer/geoview-text-renderer.ts line 20 at r3 (raw file):

Previously, DamonU2 (Damon Ulmi) wrote…

All functions in this file are still the old style of JSDocs

Done.

Copy link
Member

@DamonU2 DamonU2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DamonU2 reviewed 2 files and all commit messages, and resolved 8 discussions.
Reviewable status: all files reviewed, 6 unresolved discussions (waiting on Alex-NRCan and jolevesq).

Copy link
Member

@jolevesq jolevesq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jolevesq reviewed 9 files and all commit messages, made 1 comment, and resolved 3 discussions.
Reviewable status: all files reviewed, 3 unresolved discussions (waiting on Alex-NRCan and MatthewMuehlhauserNRCan).


packages/geoview-core/public/locales/fr/translation.json line 119 at r2 (raw file):

Previously, MatthewMuehlhauserNRCan wrote…

Seemed like it wasn't translated. Did a quick search for context to see where it was used and didn't see any hits ...

Seems unused, we can even remove....

Copy link
Member Author

@MatthewMuehlhauserNRCan MatthewMuehlhauserNRCan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MatthewMuehlhauserNRCan made 1 comment.
Reviewable status: 12 of 14 files reviewed, 3 unresolved discussions (waiting on Alex-NRCan, DamonU2, and jolevesq).


packages/geoview-core/public/locales/fr/translation.json line 119 at r2 (raw file):

Previously, jolevesq (Johann Levesque) wrote…

Seems unused, we can even remove....

Removed

Copy link
Member Author

@MatthewMuehlhauserNRCan MatthewMuehlhauserNRCan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MatthewMuehlhauserNRCan resolved 1 discussion.
Reviewable status: 12 of 14 files reviewed, 2 unresolved discussions (waiting on Alex-NRCan, DamonU2, and jolevesq).

Copy link
Member

@jolevesq jolevesq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jolevesq reviewed 2 files and all commit messages.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on Alex-NRCan).

Copy link
Member

@Alex-NRCan Alex-NRCan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Alex-NRCan reviewed 6 files and all commit messages, made 4 comments, and resolved 2 discussions.
Reviewable status: all files reviewed, 4 unresolved discussions (waiting on MatthewMuehlhauserNRCan).


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 127 at r6 (raw file):

   * @param emitZIndexChanged - Optional, whether to emit a z-index changed event after updating the z-index. Defaults to true.
   */
  protected onSetZIndex(zIndex: number, emitZIndexChanged: boolean = true): void {

Love the implementation of this and onSetVisible.


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 128 at r6 (raw file):

   */
  protected onSetZIndex(zIndex: number, emitZIndexChanged: boolean = true): void {
    this.getOLLayer().setZIndex(zIndex);

Can you add line comments here Internally set the z-index on the actual OL object ?


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 442 at r6 (raw file):

  setVisible(layerVisibility: boolean, emitVisibleChanged: boolean = true): void {
    // Redirect
    this.onSetVisible(layerVisibility, emitVisibleChanged);

Originally, the setVisible was only raising the visibleChanged event if the actual visibility had changed on the layer, now it's whether the parameter tells it to. I'm not against it, just making sure it's an intended change of behavior. It'll probably be raising more visibleChanged event that before? (e.g. when a layer is already visible and we're calling setVisible(true) and vice-versa). Curious if there are some impacts of this. If all tested, all good. Test with large groups of layers just to make sure too. Otherwise, maybe we only call the this.#emitVisibleChanged() (in onSetVisible) if both the emitVisibleChanged parameter is true AND there's an actual visibility change happening?


packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts line 168 at r6 (raw file):

   * @param emitVisibleChanged - Optional, whether to emit a visible changed event after updating the visibility. Defaults to true.
   */
  override onSetVisible(layerVisibility: boolean, emitVisibleChanged: boolean = true): void {

add 'protected' scope on this child class please

Copy link
Member Author

@MatthewMuehlhauserNRCan MatthewMuehlhauserNRCan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MatthewMuehlhauserNRCan made 3 comments and resolved 1 discussion.
Reviewable status: 12 of 14 files reviewed, 3 unresolved discussions (waiting on Alex-NRCan, DamonU2, and jolevesq).


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 128 at r6 (raw file):

Previously, Alex-NRCan (Alex) wrote…

Can you add line comments here Internally set the z-index on the actual OL object ?

Done.


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 442 at r6 (raw file):

Previously, Alex-NRCan (Alex) wrote…

Originally, the setVisible was only raising the visibleChanged event if the actual visibility had changed on the layer, now it's whether the parameter tells it to. I'm not against it, just making sure it's an intended change of behavior. It'll probably be raising more visibleChanged event that before? (e.g. when a layer is already visible and we're calling setVisible(true) and vice-versa). Curious if there are some impacts of this. If all tested, all good. Test with large groups of layers just to make sure too. Otherwise, maybe we only call the this.#emitVisibleChanged() (in onSetVisible) if both the emitVisibleChanged parameter is true AND there's an actual visibility change happening?

Done Although, I'm curious with checking if the visibility has changed at this level if we could remove some of that logic from the layer.ts > setOrToggleLayerVisibility ... most of the time the return isn't used, but there is some that travels to the state, so maybe not.


packages/geoview-core/src/geo/layer/gv-layers/vector/abstract-gv-vector.ts line 168 at r6 (raw file):

Previously, Alex-NRCan (Alex) wrote…

add 'protected' scope on this child class please

Done.

Copy link
Member Author

@MatthewMuehlhauserNRCan MatthewMuehlhauserNRCan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MatthewMuehlhauserNRCan made 1 comment.
Reviewable status: 12 of 14 files reviewed, 3 unresolved discussions (waiting on Alex-NRCan, DamonU2, and jolevesq).


packages/geoview-core/src/geo/layer/gv-layers/abstract-base-layer.ts line 442 at r6 (raw file):

Previously, MatthewMuehlhauserNRCan wrote…

Done Although, I'm curious with checking if the visibility has changed at this level if we could remove some of that logic from the layer.ts > setOrToggleLayerVisibility ... most of the time the return isn't used, but there is some that travels to the state, so maybe not.

Nope, that needs to stay as is since it's setting or toggling

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] DeclutterMode for layers with text does not work

4 participants