From 888acfdb8f1ec4b6bb537663522ed9dcd777c2e3 Mon Sep 17 00:00:00 2001 From: ychoquet Date: Wed, 8 Mar 2023 09:01:23 -0500 Subject: [PATCH] 572-Document best practice to work with events --- docs/README.md | 3 +- .../FeatureInfoLayerSet-event-managment.md | 58 + .../draw.io/FeatureInfo-class.drawio.svg | 296 +++++ .../FeatureInfo-state-diagram.drawio.svg | 916 ++++++++++++++ .../LayerSet/LayerSet-event-managment.md | 23 + .../draw.io/LayerSet-class.drawio.svg | 175 +++ .../draw.io/LayerSet-state-diagram.drawio.svg | 608 ++++++++++ .../LegendsLayerSet-event-managment.md | 47 + .../draw.io/LegendsLayerSet-class.drawio.svg | 299 +++++ .../LegendsLayerSet-state-diagram.drawio.svg | 1069 +++++++++++++++++ docs/app/event/README.md | 13 + docs/app/event/event-payloads.md | 152 ++- docs/app/event/events-api.md | 103 +- .../geoview-core/public/templates/layers.html | 67 +- .../geoview-core/public/templates/test.html | 8 +- .../public/templates/ui-components.html | 8 +- packages/geoview-core/src/api/api.ts | 2 +- packages/geoview-core/src/api/events/event.ts | 149 +-- .../api/events/payloads/footer-tab-payload.ts | 2 +- .../payloads/get-feature-info-payload.ts | 3 +- ...lat-long-payload.ts => lng-lat-payload.ts} | 0 .../payloads/marker-definition-payload.ts | 6 +- .../src/core/components/app-bar/app-bar.tsx | 11 +- .../components/attribution/attribution.tsx | 5 +- .../components/click-marker/click-marker.tsx | 18 +- .../core/components/crosshair/crosshair.tsx | 6 +- .../footer-bar/footer-bar-fixnorth-switch.tsx | 6 +- .../footer-bar/footer-bar-rotation-button.tsx | 2 +- .../components/footer-tabs/footer-tabs-api.ts | 8 +- .../components/footer-tabs/footer-tabs.tsx | 40 +- .../src/core/components/legend/legend.tsx | 5 +- .../src/core/components/map/map.tsx | 104 +- .../src/core/components/nav-bar/nav-bar.tsx | 8 +- .../components/north-arrow/north-arrow.tsx | 12 +- .../components/overview-map/overview-map.tsx | 114 +- .../src/core/components/scale/scale.tsx | 4 +- .../src/core/containers/focus-trap.tsx | 4 +- .../src/core/containers/shell.tsx | 33 +- .../geoview-core/src/core/types/cgpv-types.ts | 4 +- .../geoview-layers/abstract-geoview-layers.ts | 93 +- packages/geoview-core/src/geo/layer/layer.ts | 143 ++- packages/geoview-core/src/geo/map/map.ts | 3 +- .../src/geo/projection/projection.ts | 26 +- .../src/geo/utils/feature-info-layer-set.ts | 11 +- .../geoview-core/src/geo/utils/layer-set.ts | 34 +- ...gend-layer-set.ts => legends-layer-set.ts} | 16 +- .../geoview-core/src/ui/drawer/drawer.tsx | 4 +- packages/geoview-core/src/ui/modal/modal.tsx | 6 +- .../geoview-core/src/ui/panel/panel-api.ts | 33 +- packages/geoview-core/src/ui/panel/panel.tsx | 48 +- .../geoview-core/src/ui/slider/slider.tsx | 6 +- .../src/details-item.tsx | 16 +- .../geoview-footer-panel/src/details-item.tsx | 23 +- packages/geoview-footer-panel/src/index.tsx | 3 +- .../geoview-footer-panel/src/legend-item.tsx | 5 +- .../src/layer-stepper.tsx | 3 +- .../src/panel-content.tsx | 10 +- 57 files changed, 4147 insertions(+), 727 deletions(-) create mode 100644 docs/app/event/FeatureInfoLayerSet/FeatureInfoLayerSet-event-managment.md create mode 100644 docs/app/event/FeatureInfoLayerSet/draw.io/FeatureInfo-class.drawio.svg create mode 100644 docs/app/event/FeatureInfoLayerSet/draw.io/FeatureInfo-state-diagram.drawio.svg create mode 100644 docs/app/event/LayerSet/LayerSet-event-managment.md create mode 100644 docs/app/event/LayerSet/draw.io/LayerSet-class.drawio.svg create mode 100644 docs/app/event/LayerSet/draw.io/LayerSet-state-diagram.drawio.svg create mode 100644 docs/app/event/LegendsLayerSet/LegendsLayerSet-event-managment.md create mode 100644 docs/app/event/LegendsLayerSet/draw.io/LegendsLayerSet-class.drawio.svg create mode 100644 docs/app/event/LegendsLayerSet/draw.io/LegendsLayerSet-state-diagram.drawio.svg create mode 100644 docs/app/event/README.md rename packages/geoview-core/src/api/events/payloads/{lat-long-payload.ts => lng-lat-payload.ts} (100%) rename packages/geoview-core/src/geo/utils/{legend-layer-set.ts => legends-layer-set.ts} (91%) diff --git a/docs/README.md b/docs/README.md index 96105c97d26..5b85e704298 100644 --- a/docs/README.md +++ b/docs/README.md @@ -19,8 +19,7 @@ Once the project matures, these docs can be the basis of official public docs, h ## How the application works - [Theme](./app/ui/theming.md) -- [Events API](./app/event/events-api.md) -- [Events and Payloads](./app/event/event-payloads.md) +- [Events documentation](./app/event/README.md) - [Components vs Core Packages vs External Packages](./app/components-packages.md) - [Packages](./app/packages.md) - [Load maps](./app/loading-maps.md) \ No newline at end of file diff --git a/docs/app/event/FeatureInfoLayerSet/FeatureInfoLayerSet-event-managment.md b/docs/app/event/FeatureInfoLayerSet/FeatureInfoLayerSet-event-managment.md new file mode 100644 index 00000000000..ae4739cf743 --- /dev/null +++ b/docs/app/event/FeatureInfoLayerSet/FeatureInfoLayerSet-event-managment.md @@ -0,0 +1,58 @@ +# FeatureInfoLayerSet Class + +The `FeatureInfoLayerSet` class is used to create objects that will keep query results associated with layer paths. It uses internally an instance of the `LayerSet` class to keep track of the layers loaded on the map. The property list of the LayerSet remains synchronized with all layer paths on the map at all times. However, the `LayerSet` has a registration condition function that filter out all layers that are not queryable as defined in their metadata or configuration. If you delete or add a layer to the map, the `LayerSet` will be updated. The feature information is stored in the `resultSets` property and has a `TypeFeatureInfoResultSets` type which is an object whose properties are layer paths and the values are of one of the following types: `null`, `undefined` or `TypeArrayOfFeatureInfoEntries`. A `null`value means the `getLgetFeatureInfo` call did not get the expected data due to an error. The `undefined`value is used to identify the layer paths that need to return there result set. A value of `TypeArrayOfFeatureInfoEntries` is the layer path result set. The structure of `TypeArrayOfFeatureInfoEntries` is shown below: + +``` js +export type TypeArrayOfFeatureInfoEntries = TypeFeatureInfoEntry[]; + +export type TypeFeatureInfoEntry = { + featureKey: number; + geoviewLayerType: TypeGeoviewLayerType; + extent: Extent; + geometry: FeatureLike | null; + featureIcon: HTMLCanvasElement; + fieldInfo: Partial>; +}; + +export type TypeFieldEntry = { + fieldKey: number; + value: unknown; + dataType: 'string' | 'date' | 'number'; + alias: string; + domain: null | codedValueType | rangeDomainType; +}; +``` + +The `featureKey` property is a sequence number assigned to the row in the array. The `geoviewLayerType` property identifies the of GeoView layer type that returned the information. The `extent` property is the bounding box for the feature stored in the array row. The `geometry` is the feature object used to draw on the layer. The `featureIcon` is a snippet representation of the feature. Finally, the `fieldInfo` is an object whose properties are the field name as used internally and whose values are records of type `TypeFieldEntry`. + +In a field entries, the `fieldKey` property is a sequence number assigned to the field. The `value` property is the actual value assigned to the field. The `dataType` property is the type of the value property. The `alias` is an alternative name for the field. Finally, `domain` is an object used by ESRI layers to describe the domain of values that can be assigned to the field value. When the value of `geoviewLayerType` is `esriDynamic` or `esriFeature`, you can use the `domain` property. Otherwise, you cannot. +

 

+

+ +

+ +The class can be instantiated using the constructor or the create method. At creation time, the constructor instantiates a `LayerSet` object which will send a `LAYER_SET.REQUEST_LAYER_INVENTORY` event using a `mapId/LayerSetId` handler in order to get the list of all the layer paths already placed on the map. Throughout its existence, the `FeatureInfoLayerSet` instance listens, through its LayerSet property, to the `LAYER_SET.LAYER_REGISTRATION` events that are emitted when a layer is created/destroyed on the map or in response to the inventory request to update its `ResultSet` property. It also listens to the `GET_FEATURE_INFO.QUERY_RESULT` event. This listener receives the feature information returned by the layer's `getFeatureInfo` calls and store it in the `LayerSet`. If all the registered layers have their feature information, a `GET_FEATURE_INFO.ALL_QUERIES_DONE` event is triggered with a `mapId/LayerSetId` handler. + +When created, the `FeatureInfoLayerSet` start listening for `MAP.EVENT_MAP_SINGLE_CLICK` events. When the user click on the map, a `GET_FEATURE_INFO.QUERY_LAYER` event is thrown to the map with the click position as a payload. This event will trigger the get feature info on the listening layers and when the query result is returned, it is relayed to the `FeatureInfoLayerSet` using a `GET_FEATURE_INFO.QUERY_RESULT` event. + +To see how you can use the `FeatureInfoLayerSet`, you can analyse the code of the following files: +- the constructor of the `DetailsAPI` class defined in [packages/geoview-core/src/core/components/details/details/details-api.ts](../../../../packages/geoview-core/src/core/components/details/details-api.ts#L25) and its `createDetails` function; +- the `GET_FEATURE_INFO.ALL_QUERIES_DONE` listener in the `DetailsItem` JSX.Element defined in [packages\geoview-details-panel\src\details-item.tsx](../../../../packages/geoview-details-panel/src/details-item.tsx#L43) and the `createDetails` API call near the end of the file; +- the `GET_FEATURE_INFO.ALL_QUERIES_DONE` listener in the `DetailsItem` JSX.Element defined in [packages\geoview-footer-panel\src\details-item.tsx](../../../../packages/geoview-footer-panel/src/details-item.tsx#L40) and the `createDetails` API call near the end of the file. + +# FeatureInfoLayerSet State Diagram + +The life cycle of the `FeatureInfoLayerSet` starts with the creation of a `LayerSet` object. This means that all the state transitions explained in the [`LayerSet` state diagram](../LayerSet/LayerSet-event-managment.md#layerset-state-diagram) are performed at creation time. To summarize what happens at this time, we must consider two cases: + +- The `FeatureInfoLayerSet` is instantiated before the associated map has created its layers and the layers will be added at the time of their creation. +- The `FeatureInfoLayerSet` is instantiated when the associated map already has layers enabled and these will be added as a result of the inventory request made by the `LegendsLayerSet` when it is created. + +All map layers added to the `FeatureInfoLayerSet` will fetch their feature information on user map click. + +Let's follow the thread of events for the first case. We create a `FeatureInfoLayerSet`. As a result, a `LayerSet` is instanciated to associate each layer path with its feature info result set array. The `REQUEST_LAYER_INVENTORY` event that is thrown at this point in time is done for nothing, because the map does not contain a layer. However, each time a layer is added to the map, a `LAYER_REGISTRATION` event is emited to add its layer path to the `LayerSet`. This action will trigger a `LAYER_SET.UPDATED` event to tell the `FeatureInfoLayerSet` instance that its `LayerSet` has been modified. It is the code of the layer path of the GeoView layer instance that will request the feature information when the users issue a click on the map and when the query result is obtained, a `QUERY_RESULT` event will be emitted for the layer path of the map. This will update the `LayerSet` and if all feature info array are fetched, an `ALL_QUERIES_DONE` event is emited to signal to all listening object that all feature info layer sets are received. + +The second case differs from the previous one only in the way the `REQUEST_LAYER_INVENTORY` event is handled. Since layers already exist on the map, they will identify themselves for registration. The rest of the logic is the same. +

 

+

+ +

diff --git a/docs/app/event/FeatureInfoLayerSet/draw.io/FeatureInfo-class.drawio.svg b/docs/app/event/FeatureInfoLayerSet/draw.io/FeatureInfo-class.drawio.svg new file mode 100644 index 00000000000..1602c9aa9a8 --- /dev/null +++ b/docs/app/event/FeatureInfoLayerSet/draw.io/FeatureInfo-class.drawio.svg @@ -0,0 +1,296 @@ + + + + + + + + + +
+
+
+

+ + FeatureInfoLayerSet + +

+
+
+
+
+ + FeatureInfoLayerSet + +
+
+ + + + +
+
+
+
+ + + + constructor + + + + ( + + + mapId, + + + layerSetId + + + + + ) + +
+
+ +
+
+
+
+ + + + create + + + + + + ( + + + mapId, + + + layerSetId + + + + + ) + +
+
+
+
+
+ + + constructor (mapId, layerSetId)... + +
+
+ + + + +
+
+
+

+ + FeatureInfoLayerSet class + +

+
+
+
+
+ + FeatureInfoLayerSet class + +
+
+ + + + + + +
+
+
+
+ + + mapId: string + +
+
+ + + layerSet:LayerSet + +
+
+ + + ResultSets: TypeFeatureInfoResultSet + +
+
+
+
+
+ + + mapId: string... + +
+
+ + + + +
+
+
+

+ + LayerSet + +

+
+
+
+
+ + LayerSet + +
+
+ + + + +
+
+
+
+ + + mapId: string + +
+
+ + + layerSetId:string + +
+
+ + + ResultSets: Record<string, any> + +
+
+ + + registrationConditionFunction: (string) => boolean + +
+
+
+
+
+ + + mapId: string... + +
+
+ + + + +
+
+
+
+ + + + constructor + + + + ( + + + mapId, + + + layerSetId, + + + ResultSets, + + + +
+
+ + + registrationConditionFunction) + + +
+
+ +
+
+
+
+ + + + create + + + + + + ( + + + mapId, + + + layerSetId, + + + ResultSets, + +
+
+
+
+
+ + + registrationConditionFunction) + + +
+
+
+
+
+ + + constructor (mapId, layerSetId, ResultSets,... + +
+
+ + + + +
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/docs/app/event/FeatureInfoLayerSet/draw.io/FeatureInfo-state-diagram.drawio.svg b/docs/app/event/FeatureInfoLayerSet/draw.io/FeatureInfo-state-diagram.drawio.svg new file mode 100644 index 00000000000..7c398407513 --- /dev/null +++ b/docs/app/event/FeatureInfoLayerSet/draw.io/FeatureInfo-state-diagram.drawio.svg @@ -0,0 +1,916 @@ + + + + + + + +
+
+
+

+ + FeatureInfoLayerSet + +

+
+
+
+
+ + FeatureInfoLayerSet + +
+
+ + + + +
+
+
+

+ + LayerSet + +

+

+ + constructor( + + mapId, layerSetId, … + + ) + +

+
+
+
+
+ + LayerSet... + +
+
+ + + + + + +
+
+
+

+ + On + +

+

+ + + LayerSet + + +

+

+ + + Layer_registration + +
+
+

+

+ +
+ (mapId) +
+

+
+
+
+
+ + On... + +
+
+ + + + + + + + +
+
+
+

+ + + Emit + + +

+

+ + LayerSet + +

+

+ + Layer_registration + +

+

+ + (mapId, layerPath, 'add', layerSetId) + +

+

+
+

+
+
+
+
+ + Emit... + +
+
+ + + + +
+
+
+

+ + AbstractGeoViewLayer + +

+

+ + createGeoViewLayers() + +

+
+
+
+
+ + AbstractGeoViewLayer... + +
+
+ + + + +
+
+
+

+ + + Emit + + +

+

+ + LayerSet + +

+

+ + Layer_registration + +

+

+ + (mapId, layerPath, 'add') + +

+

+ +
+
+

+
+
+
+
+ + Emit... + +
+
+ + + + +
+
+
+

+ + + Emit + + +

+

+ + LayerSet + +

+

+ + + Request_layer_inventory + + +

+

+ + + (mapId, layerSetId) + + +

+
+
+
+
+ + Emit... + +
+
+ + + + +
+
+
+

+ + On + +

+

+ + LayerSet + +

+

+ + + Request_layer_inventory + +
+
+

+

+ + (mapId) + +

+
+
+
+
+ + On... + +
+
+ + + + + + +
+
+
+

+ + On + +

+

+ + + GetLegends + + +

+

+ + + Query_legend + +
+
+

+

+ + (mapId, layerPath) + +

+
+
+
+
+ + On... + +
+
+ + + + + + +
+
+
+

+ + On + +

+

+ + + GetFeatureInfo + + +

+

+ + Query_layer +
+
+

+

+ + (mapId) + +

+
+
+
+
+ + On... + +
+
+ + + + +
+
+
+

+ + FeatureInfoLayerSet state diagram + +

+
+
+
+
+ + FeatureInfoLayerSet state diagram + +
+
+ + + + + + + + + + + + + + +
+
+
+

+ + On + +

+

+ + + GetFeatureInfo + + +

+

+ + + Query_result + +
+
+

+

+ +
+ (mapId) +
+

+
+
+
+
+ + On... + +
+
+ + + + + + + + +
+
+
+ Yes +
+
+
+
+ + Yes + +
+
+ + + + +
+
+
+ No +
+
+
+
+ + No + +
+
+ + + + + + +
+
+
+

+ + + Emit + + +

+

+ + GetFeatureIndo + +

+

+ + All_queries_done + +

+

+ + (mapId, layerSetId) + +

+

+
+

+
+
+
+
+ + Emit... + +
+
+ + + + + + +
+
+
+

+ + On + +

+

+ + Map + +

+

+ + Event_map_single_click + +

+

+ +
+ (mapId) +
+

+
+
+
+
+ + On... + +
+
+ + + + +
+
+
+ + All done? + +
+
+
+
+ + All done? + +
+
+ + + + +
+
+
+

+ + UserObject + +

+
+
+
+
+ + UserObject + +
+
+ + + + +
+
+
+

+ + GetFeatureInfo + +

+

+ + On + +

+

+ + All_query_done + +

+

+
+ + (mapId, layerSetId) + +

+
+
+
+
+ + GetFeatureInfo... + +
+
+ + + + + + +
+
+
+ + Wait for other +
+ Query_result +
+ events +
+
+
+
+
+
+ + Wait for other... + +
+
+ + + + + + +
+
+
+

+ + + Emit + + +

+

+ + + GetFeatureInfo + + +

+

+ + Query_layer +
+
+

+

+ + (mapId) + +

+
+
+
+
+ + Emit... + +
+
+ + + + +
+
+
+

+ + + Emit + + +

+

+ + GetFeatureInfo + +

+

+ + Query_result + +

+

+ + (mapId) + +

+

+ +
+
+

+
+
+
+
+ + Emit... + +
+
+ + + + + + +
+
+
+ + Execute feature +
+ info query and +
+ get the result +
+
+
+
+
+
+ + Execute feature... + +
+
+ + + + + + +
+
+
+ + Save the query +
+ result in the +
+ LayerSet +
+
+
+
+
+
+ + Save the query... + +
+
+ + + + + + +
+
+
+

+ Layer +

+

+ removeLayerUsingPath(path) +

+
+
+
+
+ + Layer... + +
+
+ + + + + + +
+
+
+

+ + + Emit + + +

+

+ + LayerSet + +

+

+ + Layer_registration + +

+

+ + (mapId, layerPath, 'remove') + +

+

+ +
+
+

+
+
+
+
+ + Emit... + +
+
+ + + + +
+
+
+ + update +
+ LayerSet +
+ (add/remove) +
+
+
+
+
+
+ + update... + +
+
+ + +
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/docs/app/event/LayerSet/LayerSet-event-managment.md b/docs/app/event/LayerSet/LayerSet-event-managment.md new file mode 100644 index 00000000000..1ade2088d00 --- /dev/null +++ b/docs/app/event/LayerSet/LayerSet-event-managment.md @@ -0,0 +1,23 @@ +# LayerSet class + +The `layerSet` class has four properties. The first is the identifier of the map to which it is attached. The second is the `layerSet` identifier used to differentiate the `layerSet` when we have several of them. Then we have the resultSets which will have as many properties as there are layer entries (sometimes named layer path) on the attached map. Finally, there is a registration condition function that will filter out layer entries that we don't want registered in the layerSet. + +The class can be instantiated using the constructor or the create method. At creation time, the instance sends a `LAYER_SET.REQUEST_LAYER_INVENTORY` event in order to get the list of all the layerPaths existing on the map. This list is filtered with the registration condition function to keep only the layerPaths of interest. Throughout its existence, the instance listens to the `LAYER_SET.LAYER_REGISTRATION` events which are emitted when a layer is created/destroyed on the map or in response to an inventory request. +

 

+

+ +

+ +# LayerSet State Diagram + +The `LayerSet` class allows to create objects whose properties correspond to the layer paths loaded on the map. The list of properties of a `LayerSet` remain synchronized at all times with all the layer paths of the map. If you delete or add a layer to the map, the `LayerSet` will be updated. The value associated to the layer path varies according to the usage. The Geoview viewer has two classes that use `layerSets` and can be examined to see how they can be used. These classes are [LegendsLayerSet](../LegendsLayerSet/LegendsLayerSet-event-managment.md) and [FeatureInfoLayerSet](../FeatureInfoLayerSet/FeatureInfoLayerSet-event-managment.md). + +When we create a geoview layer, three listeners are attached to each layer entry that compose it. The first one is used to keep track of all the layer paths associated with the layer sets of the map, the second one is for the legend and the last one for the get feature info mechanism. Next, the geoview layer emit a layer registration event for each layer path added to the map. This event identifies the map and the layer path to be added to all layer sets already instantiated for the specified map. Here, the layer registration does not specify the `layerSetId` because we want to register to all `layerSet` that listen to the map. + +In some cases, it is the layer set that is created when several layers already exist on the map. This situation is taken care of by sending a layer inventory request event when the layer set is created. In response to this signal, all existing layer path listeners will again issue a layer registration event to register their existence in the layer set. This time, the layer registration specify the `layerSetId` because we know whose layer set is requesting the inventory. + +Each time a layer set is updated, a `LayerSet` updated event is issued to signal the change. This event is used to trigger the process that own the layer set. +

 

+

+ +

diff --git a/docs/app/event/LayerSet/draw.io/LayerSet-class.drawio.svg b/docs/app/event/LayerSet/draw.io/LayerSet-class.drawio.svg new file mode 100644 index 00000000000..48b3fe7a30c --- /dev/null +++ b/docs/app/event/LayerSet/draw.io/LayerSet-class.drawio.svg @@ -0,0 +1,175 @@ + + + + + + + + + +
+
+
+

+ + LayerSet + +

+
+
+
+
+ + LayerSet + +
+
+ + + + +
+
+
+
+ + + mapId: string + +
+
+ + + layerSetId:string + +
+
+ + + ResultSets: Record<string, any> + +
+
+ + + registrationConditionFunction: (string) => boolean + +
+
+
+
+
+ + + mapId: string... + +
+
+ + + + +
+
+
+
+ + + + constructor + + + + ( + + + mapId, + + + layerSetId, + + + ResultSets, + + + +
+
+ + + registrationConditionFunction) + + +
+
+ +
+
+
+
+ + + + create + + + + + + ( + + + mapId, + + + layerSetId, + + + ResultSets, + +
+
+
+
+
+ + + registrationConditionFunction) + + +
+
+
+
+
+ + + constructor (mapId, layerSetId, ResultSets,... + +
+
+ + + + +
+
+
+

+ + LayerSet class + +

+
+
+
+
+ + LayerSet class + +
+
+ + +
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/docs/app/event/LayerSet/draw.io/LayerSet-state-diagram.drawio.svg b/docs/app/event/LayerSet/draw.io/LayerSet-state-diagram.drawio.svg new file mode 100644 index 00000000000..336a1bcb138 --- /dev/null +++ b/docs/app/event/LayerSet/draw.io/LayerSet-state-diagram.drawio.svg @@ -0,0 +1,608 @@ + + + + + + + + + + + +
+
+
+ + update +
+ LayerSet +
+ (add/remove) +
+
+
+
+
+
+ + update... + +
+
+ + + + +
+
+
+

+ + AbstractGeoViewLayer + +

+

+ + createGeoViewLayers() + +

+
+
+
+
+ + AbstractGeoViewLayer... + +
+
+ + + + + + +
+
+
+

+ + + Emit + + +

+

+ + Layer_set + +

+

+ + Updated + +

+

+ + (mapId, layerSetId) + +

+
+
+
+
+ + Emit... + +
+
+ + + + +
+
+
+

+ + + Emit + + +

+

+ + + LayerSet + + +

+

+ + Layer_registration + +

+

+ + (mapId, layerPath, 'add') + +

+
+
+
+
+ + Emit... + +
+
+ + + + +
+
+
+

+ + + Emit + + +

+

+ + + LayerSet + + +

+

+ + Layer_registration + +

+

+ + (mapId, layerPath, 'add', layerSetId) + +

+
+
+
+
+ + Emit... + +
+
+ + + + +
+
+
+

+ + + Emit + + +

+

+ + + LayerSet + + +

+

+ + + Request_layer_inventory + + +

+

+ + + (mapId, layerSetId) + + +

+
+
+
+
+ + Emit... + +
+
+ + + + +
+
+
+

+ + On + +

+

+ + LayerSet + +

+

+ + + Request_layer_inventory + +
+
+

+

+ + (mapId) + +

+
+
+
+
+ + On... + +
+
+ + + + + + +
+
+
+

+ + On + +

+

+ + + GetLegends + + +

+

+ + + Query_legend + +
+
+

+

+ + (mapId, layerPath) + +

+
+
+
+
+ + On... + +
+
+ + + + +
+
+
+

+ + On + +

+

+ + + GetFeatureInfo + + +

+

+ + Query_layer +
+
+

+

+ + (mapId) + +

+
+ +
+
+
+
+
+
+
+ + On... + +
+
+ + + + +
+
+
+

+ + LayerSet state diagram + +

+
+
+
+
+ + LayerSet state diagram + +
+
+ + + + + + +
+
+
+

+ + LayerSet + +

+

+ + constructor( + + mapId, layerSetId, … + + ) + +

+
+
+
+
+ + LayerSet... + +
+
+ + + + +
+
+
+

+ + On + +

+

+ + + LayerSet + + +

+

+ + + layer_registration + +
+
+

+

+ +
+ (mapId) +
+

+

+ +
+
+

+
+
+
+
+ + On... + +
+
+ + + + + + + + + + + + + + +
+
+
+

+ + UserObject + +

+
+
+
+
+ + UserObject + +
+
+ + + + +
+
+
+

+ + On + +

+

+ + + LayerSet + + +

+

+ + + Layer_set.updated + +
+
+

+

+ +
+ (mapId) +
+

+

+ +
+
+

+
+
+
+
+ + On... + +
+
+ + + + + + +
+
+
+

+ + Layer + +

+

+ + removeLayerUsingPath(path) + +

+
+
+
+
+ + Layer... + +
+
+ + + + + + +
+
+
+

+ + + Emit + + +

+

+ + LayerSet + +

+

+ + Layer_registration + +

+

+ + (mapId, layerPath, 'remove') + +

+

+ +
+
+

+
+
+
+
+ + Emit... + +
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/docs/app/event/LegendsLayerSet/LegendsLayerSet-event-managment.md b/docs/app/event/LegendsLayerSet/LegendsLayerSet-event-managment.md new file mode 100644 index 00000000000..d3fb2c36b81 --- /dev/null +++ b/docs/app/event/LegendsLayerSet/LegendsLayerSet-event-managment.md @@ -0,0 +1,47 @@ +# LegendsLayerSet Class + +The `LegendsLayerSet` class is used to create objects that will keep legends associated with layer paths. It uses internally an instance of the `LayerSet` class to keep track of the layers loaded on the map. The property list of the LayerSet remains synchronized with all layer paths on the map at all times. However, the `LayerSet` has a registration condition function that filter out XYZ layers since they do not have an associated legend. If you delete or add a layer to the map, the LayerSet will be updated accordingly. The values associated with layer paths are of one of the following types: `null`, `undefined` or `TypeLegend`. A `null`value means the `getLegend` call did not get the expected legend due to an error. The `undefined`value is used to identify the layer paths that need to return there legend. A value of `TypeLegend` is the layer path legend. The structure of `TypeLegend` is shown below: + +``` js +export type TypeLegend = { + layerPath: string; + layerName?: TypeLocalizedString; + type: TypeGeoviewLayerType; + styleConfig?: TypeStyleConfig; + legend: TypeLayerStyles | HTMLCanvasElement | null; +}; +``` + +The `layerPath` parameter is used to link the legend to the layer entry configuration in the map. The `layerName` is a bilingual string for display information. The `type` tells us how to handle the legend. The `styleConfig` parameter contains the configuration settings that describe the style of the legend. This can be a simple, a unique value, or a class break configuration. Finally, we have the `legend`, whose null value indicates that it is impossible to get a legend for the layer. When the type is `ogcWms`, the legend is a `HTMLCanvasElement`. Otherwise, it is a `TypeLayerStyles`. +

 

+

+ +

+ +The class can be instantiated using the constructor or the create method. At creation time, the constructor instantiates a `LayerSet` object which will send a `LAYER_SET.REQUEST_LAYER_INVENTORY` event using a `mapId/LayerSetId` handler in order to get the list of all the layerPaths existing on the map. Throughout its existence, the `LegendsLayerSet` instance listens, through its `LayerSet` property, to the `LAYER_SET.LAYER_REGISTRATION` events that are emitted when a layer is created/destroyed on the map or in response to the inventory request to update its `ResultSet` property. It also listens to the `GET_LEGENDS.LEGEND_INFO` event. This listener receives the legend information returned by the layer's `getLegend` call and store it in the `LayerSet`. If all the registered layers have their legend information, a `GET_LEGENDS.ALL_LEGENDS_DONE` event is triggered with a `mapId/LayerSetId` handler. + +When created, the `LegendsLayerSet` is in a idle state. To start it, the instance listens to a `GET_LEGENDS.TRIGGER` event. This event is only listen once and when it is received, a `LAYER_SET.UPDATED` listener is attached to the instance to wait for `LayerSet` modifications. Then, a `GET_LEGENDS.QUERY_LEGEND` event will be emited to all undefined legend of the `LayerSet` to obtain the legends. + +The `LAYER_SET.UPDATED` listener will catch layer add/remove applied to the map. If a layer is added, a `GET_LEGENDS.QUERY_LEGEND` event will be emited for it and when all the registered layers have received their legend information, a `GET_LEGENDS.ALL_LEGENDS_DONE` event is emited using the `mapId/LayerSetId` as handler. The `GET_LEGENDS.ALL_LEGENDS_DONE` event is also emited when a layer is removed from the map to signal that a legend has been removed. + +To see how you can use the `FeatureInfoLayerSet`, you can analyse the code of the following files: +- the constructor of the `DetailsAPI` class defined in [packages/geoview-core/src/core/components/details/details/details-api.ts](../../../../packages/geoview-core/src/core/components/details/details-api.ts#L25) and its `createDetails` function; +- the `GET_FEATURE_INFO.ALL_QUERIES_DONE` listener in the `DetailsItem` JSX.Element defined in [packages\geoview-details-panel\src\details-item.tsx](../../../../packages/geoview-details-panel/src/details-item.tsx#L43) and the `createDetails` API call near the end of the file; +- the `GET_FEATURE_INFO.ALL_QUERIES_DONE` listener in the `DetailsItem` JSX.Element defined in [packages\geoview-footer-panel\src\details-item.tsx](../../../../packages/geoview-footer-panel/src/details-item.tsx#L40) and the `createDetails` API call near the end of the file. + +# LegendsLayerSet State Diagram + +The life cycle of the `LegendsLayerSet` starts with the creation of a `LayerSet` object. This means that all the state transitions explained in the [`LayerSet` state diagram](../LayerSet/LayerSet-event-managment.md#layerset-state-diagram) are performed at creation time. To summarize what happens at this time, we must consider two cases: + +- The `LegendsLayerSet` is instantiated before the associated map has created its layers and the layers will be added at the time of their creation. +- The `LegendsLayerSet` is instantiated when the associated map already has layers enabled and these will be added as a result of the inventory request made by the `LegendsLayerSet` when it is created. + +All map layers added to the `LegendsLayerSet` will fetch their legend, but only when the `LegendsLayerSet` process has been triggered. As long as the `TRIGGER` event is not emitted, the collection of legends is not done. + +Let's follow the thread of events for the first case. We create a `LegendsLayerSet`. As a result, a `LayerSet` is instanciated to associate each layer path with its legend. The `REQUEST_LAYER_INVENTORY` event that is thrown at this point in time is done for nothing, because the map does not contain a layer. However, each time a layer is added to the map, a `LAYER_REGISTRATION` event is emited to add its layer path to the `LayerSet`. This action will trigger a `LAYER_SET.UPDATED` event to tell the `LegendsLayerSet` instance that its `LayerSet` has been modified. If the `LegendsLayerSet` has received its `TRIGGER` event, It will react to the Layer_set.updated and request the legend for the newly added layer path. It is the code of the layer path of the GeoView layer instance that will request the legend and when it is obtained, a `LEGEND_INFO` event will be emitted for the layer path of the map. This will update the `LayerSet` and if all legends are fetched, an `ALL_LEGENDS_DONE` event is emited to signal to all listening object that the legends has changed. + +The second case differs from the previous one only in the way the `REQUEST_LAYER_INVENTORY` event is handled. Since layers already exist on the map, they will identify themselves for registration. The rest of the logic is the same. +

 

+

+ +

diff --git a/docs/app/event/LegendsLayerSet/draw.io/LegendsLayerSet-class.drawio.svg b/docs/app/event/LegendsLayerSet/draw.io/LegendsLayerSet-class.drawio.svg new file mode 100644 index 00000000000..24800e60ffc --- /dev/null +++ b/docs/app/event/LegendsLayerSet/draw.io/LegendsLayerSet-class.drawio.svg @@ -0,0 +1,299 @@ + + + + + + + + + +
+
+
+

+ + Legends + + + LayerSet + +

+
+
+
+
+ + LegendsLayerSet + +
+
+ + + + +
+
+
+
+ + + + constructor + + + + ( + + + mapId, + + + layerSetId + + + + + ) + +
+
+ +
+
+
+
+ + + + create + + + + + + ( + + + mapId, + + + layerSetId + + + + + ) + +
+
+
+
+
+ + + constructor (mapId, layerSetId)... + +
+
+ + + + +
+
+
+

+ + LegendsLayerSet class + +

+
+
+
+
+ + LegendsLayerSet class + +
+
+ + + + + + +
+
+
+
+ + + mapId: string + +
+
+ + + layerSet:LayerSet + +
+
+ + + ResultSets: TypeLegendResultSet + +
+
+
+
+
+ + + mapId: string... + +
+
+ + + + +
+
+
+

+ + LayerSet + +

+
+
+
+
+ + LayerSet + +
+
+ + + + +
+
+
+
+ + + mapId: string + +
+
+ + + layerSetId:string + +
+
+ + + ResultSets: Record<string, any> + +
+
+ + + registrationConditionFunction: (string) => boolean + +
+
+
+
+
+ + + mapId: string... + +
+
+ + + + +
+
+
+
+ + + + constructor + + + + ( + + + mapId, + + + layerSetId, + + + ResultSets, + + + +
+
+ + + registrationConditionFunction) + + +
+
+ +
+
+
+
+ + + + create + + + + + + ( + + + mapId, + + + layerSetId, + + + ResultSets, + +
+
+
+
+
+ + + registrationConditionFunction) + + +
+
+
+
+
+ + + constructor (mapId, layerSetId, ResultSets,... + +
+
+ + + + +
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/docs/app/event/LegendsLayerSet/draw.io/LegendsLayerSet-state-diagram.drawio.svg b/docs/app/event/LegendsLayerSet/draw.io/LegendsLayerSet-state-diagram.drawio.svg new file mode 100644 index 00000000000..3c8ccfd522f --- /dev/null +++ b/docs/app/event/LegendsLayerSet/draw.io/LegendsLayerSet-state-diagram.drawio.svg @@ -0,0 +1,1069 @@ + + + + + + + +
+
+
+

+ + Once + +

+

+ + GetLegends + +

+

+ + Trigger + +

+

+ +
+ (mapId, layerSetId) +
+

+
+
+
+
+ + Once... + +
+
+ + + + + + + + + + +
+
+
+

+ + LegendsLayerSet + +

+
+
+
+
+ + LegendsLayerSet + +
+
+ + + + +
+
+
+

+ + LayerSet + +

+

+ + constructor( + + mapId, layerSetId, … + + ) + +

+
+
+
+
+ + LayerSet... + +
+
+ + + + +
+
+
+

+ + On + +

+

+ + + LayerSet + + +

+

+ + + Layer_registration + +
+
+

+

+ +
+ (mapId) +
+

+
+
+
+
+ + On... + +
+
+ + + + +
+
+
+

+ + + Emit + + +

+

+ + LayerSet + +

+

+ + Layer_registration + +

+

+ + (mapId, layerPath, 'add', layerSetId) + +

+

+
+

+
+
+
+
+ + Emit... + +
+
+ + + + +
+
+
+

+ + AbstractGeoViewLayer + +

+

+ + createGeoViewLayers() + +

+
+
+
+
+ + AbstractGeoViewLayer... + +
+
+ + + + + + +
+
+
+

+ + + Emit + + +

+

+ + + LayerSet + + +

+

+ + Updated + +

+

+ + (mapId, layerSetId) + +

+

+
+

+
+
+
+
+ + Emit... + +
+
+ + + + +
+
+
+

+ + + Emit + + +

+

+ + LayerSet + +

+

+ + Layer_registration + +

+

+ + (mapId, layerPath, 'add') + +

+

+ +
+
+

+
+
+
+
+ + Emit... + +
+
+ + + + +
+
+
+

+ + + Emit + + +

+

+ + LayerSet + +

+

+ + + Request_layer_inventory + + +

+

+ + + (mapId, layerSetId) + + +

+
+
+
+
+ + Emit... + +
+
+ + + + +
+
+
+

+ + On + +

+

+ + LayerSet + +

+

+ + + Request_layer_inventory + +
+
+

+

+ + (mapId) + +

+
+
+
+
+ + On... + +
+
+ + + + + + + + +
+
+
+

+ + On + +

+

+ + + GetLegends + + +

+

+ + + Query_legend + +
+
+

+

+ + (mapId, layerPath) + +

+
+
+
+
+ + On... + +
+
+ + + + +
+
+
+

+ + On + +

+

+ + + GetFeatureInfo + + +

+

+ + Query_layer +
+
+

+

+ + (mapId) + +

+

+ +
+
+

+
+
+
+
+ + On... + +
+
+ + + + +
+
+
+

+ + LegendsLayerSet state diagram + +

+
+
+
+
+ + LegendsLayerSet state diagram + +
+
+ + + + + + + + + + + + + + + + +
+
+
+

+ + On + +

+

+ + + LayerSet + + +

+

+ + + Updated + +
+
+

+

+ +
+ (mapId) +
+

+
+
+
+
+ + On... + +
+
+ + + + + + + + + + +
+
+
+ Yes +
+
+
+
+ + Yes + +
+
+ + + + +
+
+
+ No +
+
+
+
+ + No + +
+
+ + + + + + +
+
+
+

+ + + Emit + + +

+

+ + GetLegends + +

+

+ + All_legends_done + +

+

+ + (mapId, layerSetId) + +

+

+
+

+
+
+
+
+ + Emit... + +
+
+ + + + +
+
+
+

+ + + Emit + + +

+

+ + GetLegends + +

+

+ + Query_legend + +

+

+ + (mapId, layerSetId) + +

+

+
+

+
+
+
+
+ + Emit... + +
+
+ + + + +
+
+
+ + All done? + +
+
+
+
+ + All done? + +
+
+ + + + +
+
+
+

+ + + Emit + + +

+

+ + GetLegends + +

+

+ + Legend_info + +

+

+ + (mapId, layerPath, Legend) + +

+

+
+

+
+
+
+
+ + Emit... + +
+
+ + + + + + +
+
+
+

+ + On + +

+

+ + GetLegends + +

+

+ + Legend_info + +

+

+ +
+ (mapId) +
+

+
+
+
+
+ + On... + +
+
+ + + + +
+
+
+

+ + UserObject + +

+
+
+
+
+ + UserObject + +
+
+ + + + +
+
+
+

+ + + GetLegends + + +

+

+ + + On + + +

+

+ + + All_legend_done + + +

+

+ +
+ (mapId, layerSetId) +
+

+
+
+
+
+ + GetLegends... + +
+
+ + + + + + +
+
+
+ + update +
+ LayerSet +
+ (add/remove) +
+
+
+
+
+
+ + update... + +
+
+ + + + + + + + +
+
+
+ + Trigger +
+ has run? +
+
+
+
+
+
+ + Trigger... + +
+
+ + + + + + +
+
+
+ + Wait  for +
+ Trigger event +
+
+
+
+
+
+ + Wait  for... + +
+
+ + + + +
+
+
+ No +
+
+
+
+ + No + +
+
+ + + + +
+
+
+ Yes +
+
+
+
+ + Yes + +
+
+ + + + + + +
+
+
+ + Wait  for +
+ Legend_info +
+ event +
+
+
+
+
+
+ + Wait  for... + +
+
+ + + + + + +
+
+
+ + Save +
+ Legend_info +
+ in LayerSet +
+
+
+
+
+
+ + Save... + +
+
+ + + + + + +
+
+
+

+ Layer +

+

+ removeLayerUsingPath(path) +

+
+
+
+
+ + Layer... + +
+
+ + + + + + +
+
+
+

+ + + Emit + + +

+

+ + LayerSet + +

+

+ + Layer_registration + +

+

+ + (mapId, layerPath, 'remove') + +

+

+ +
+
+

+
+
+
+
+ + Emit... + +
+
+ + +
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/docs/app/event/README.md b/docs/app/event/README.md new file mode 100644 index 00000000000..02910cdd907 --- /dev/null +++ b/docs/app/event/README.md @@ -0,0 +1,13 @@ +# Events related documentation + +- [Events API](./events-api.md) +- [Events and Payloads](./event-payloads.md) +- [LayerSet event managment](./LayerSet/LayerSet-event-managment.md) + - [LayerSet class description](./LayerSet/LayerSet-event-managment.md#layerset-class) + - [LayerSet state diagram](./LayerSet/LayerSet-event-managment.md#layerset-state-diagram) +- [LegendsLayerSet event managment](./LegendsLayerSet/LegendsLayerSet-event-managment.md) + - [LegendsLayerSet class description](./LegendsLayerSet/LegendsLayerSet-event-managment.md#legendslayerset-class) + - [LegendsLayerSet state diagram](./LegendsLayerSet/LegendsLayerSet-event-managment.md#legendslayerset-state-diagram) +- [FeatureInfoLayerSet event managment](./FeatureInfoLayerSet/FeatureInfoLayerSet-event-managment.md) + - [FeatureInfoLayerSet class description](./FeatureInfoLayerSet/FeatureInfoLayerSet-event-managment.md#FeatureInfoLayerSet-class) + - [FeatureInfoLayerSet state diagram](./FeatureInfoLayerSet/FeatureInfoLayerSet-event-managment.md#FeatureInfoLayerSet-state-diagram) diff --git a/docs/app/event/event-payloads.md b/docs/app/event/event-payloads.md index c3d14ac7f71..f05a92376f6 100644 --- a/docs/app/event/event-payloads.md +++ b/docs/app/event/event-payloads.md @@ -5,122 +5,144 @@ Below you will find a list of some available functions and payload types/interfa ## Event payload validation functions ## Event object creation functions ## Event payloads classes - -## Event payloads interfaces - - \ No newline at end of file diff --git a/docs/app/event/events-api.md b/docs/app/event/events-api.md index 836a099cd9e..da210803eaa 100644 --- a/docs/app/event/events-api.md +++ b/docs/app/event/events-api.md @@ -1,6 +1,6 @@ # Events API -The events API provides an interface to manage events and event handlers on the GeoView core viewer cgpv. +The Event API provides an interface for managing events and event handlers on the GeoView core cgpv viewer. In the following sections, it is stated in some places that the event handler name must be a string concatenation separated by forward slashes. Although not mandatory, it is worth mentioning that this strategy is intended to allow bulk deactivation of event lists using a handler prefix, usually the map identifier. We will discuss that in the `offAll` section. The event API will allow you to listen to events that are emitted from the viewer, create your own event listeners, emit events that are being listened from the viewer and emit your own created events. @@ -24,7 +24,7 @@ To listen to existing events, make a note of the event name that you want to lis You can add an event listener by calling the `cgpv.api.event.on()` function. -The `on()` function takes two required parameters and a list of optional parameters. +The `on()` function takes two required parameters and one optional parameter. #### First parameter @@ -36,8 +36,6 @@ This will return `map/moveend` as string value. You can enter `map/moveend` manu Event names are named by the component this event will interact with such as `map` followed by a forward slash, followed by the name of the event that will execute. -If a **handler** is added while emitting this event then another forward slash will follow with that handler name such as `map/moveend/mapOne/anotherHandler/anotherHandler` this will be explained later with the emit function. - So far we have `cgpv.api.event.on('map/moveend',...,...)` @@ -64,29 +62,26 @@ The viewer provides a function to **validate** each payload if it contains the c Validation functions starts with `payloadIs...` You can call `cgpv.types.payloadIs...` to access the validation functions for each event. -For our example, a map move end event sends a `latlng` object with the payload containing the latitude and longtitude position. Therefore we have a validator function called `payloadIsALatLng`. The validation function will verify if the correct event was received and the payload contains the latlng object and then convert it's type to LatLngPayload which will give you auto completion to access the payload content. +For our example, a map move end event sends a `lnglat` object with the payload containing the latitude and longtitude position. Therefore we have a validator function called `payloadIsALngLat`. The validation function will verify if the correct event was received and the payload contains the lnglat object and then convert it's type to LngLatPayload which will give you auto completion to access the payload content. You can use this validation function as follow: ```js cgpv.api.event.on('map/moveend', function(payload) { // before the validation, the type of payload is PayloadBaseClass - if(cgpv.types.payloadIsALatLng(payload)) { - // after the validation, the type of payload in this case is LatLngPayload which will give access to the latlng object - console.log(payload.latlng); + if(cgpv.types.payloadIsALngLat(payload)) { + // after the validation, the type of payload in this case is + // LngLatPayload which will give access to the lnglat object + console.log(payload.lnglat); } }, ...); ``` -### Third+ - list of optional parameters - -The optional parameters, are parameters for listening on specefic handlers such as a specefic map, specefic panel in a map etc... - -Some events emit only on one handler, other events emit to all maps so a handler is not required and others could emit on specefic panel on a map hence the need of a list of parameters. +#### Third optional parameter -Handler names will automatically get added to the event names so for example, passing `mapOne` as the third parameter in the `on()` function will make the event name `map/moveend/mapOne`. +The optional parameter is a handler name. It is used for listening on specefic handlers such as a specefic map, specefic panel in a map etc... -_The emitter function will have to emit the same handlers if you want to listen to it, will give an example on the emit function documentations._ +Some events emit only on one handler, other events emit to all maps so a handler is not required and others could emit on specefic panel on a map hence the need of a slash separated list of handlers. If a **handler** is used while emitting an event, then there must be a listener on the same event and handler if we want the callback function to be called. Handler names will automatically get added to the event names so for example, passing `mapOne` as the third parameter in the `on()` function will make the event name `map/moveend/mapOne`. In our example, the emit function emits a map move end with the map id as the handler. So you can listen to a map move end event on that specefic map when you pass the map id handler to the third parameter of the `on()` function @@ -97,18 +92,26 @@ cgpv.api.event.on( "map/moveend", function (payload) { // before the validation, the type of payload is PayloadBaseClass - if (cgpv.types.payloadIsALatLng(payload)) { - // after the validation, the type of payload in this case is LatLngPayload which will give access to the latlng object - console.log(payload.latlng); + if (cgpv.types.payloadIsALngLat(payload)) { + // after the validation, the type of payload in this case is LngLatPayload which will give access to the lnglat object + console.log(payload.lnglat); } }, "mapOne" ); ``` +The callback function does not need to test that the handler name in the payload is the same as the name in the third parameter, because the callback function is only called if it is. + ### Listening to events created by you -Listening to events you create is not difference than listening to existing events. You should be able to use the same documentation without the validation function. +Listening for events you create is no different than listening for existing events. You should be able to use the same documentation without the validation function. And if you want to leverage your use of typescript, there's nothing stopping you from writing your own type protection functions. + +### Listening to many events having the same event name and handler name + +In some cases, we have to set listeners on the same event name and handler name. If we need to turn off only one of these listeners, we must keep a reference to the handler function that is used by the targetted handler. We will see how to tur off a single handler in the `off` section. + +The handler function is returned by the `on` and `once` method of the Event class. It is the same as the callback function passed in as their second parameter. ## Emitting Events @@ -120,22 +123,24 @@ Many of the existing events are being listened by the viewer so you can emit eve Say you are trying to open a snackbar, the viewer listens to an event that opens the snackbar with a custom message. -To emit an event you need to call the `cgpv.api.event.emit()` function. The function takes 1 required parameter and a list of optional parameters +To emit an event you need to call the `cgpv.api.event.emit()` function. The function takes 1 required parameter. -#### The first parameter +#### The emit parameter -The first parameter is an object that contains the **event name** to emit to, a **handler name**, and the **payload data**. +The emit parameter is an object that contains the **event name** to emit to, a **handler name**, and the **payload data**. Just like validation functions and event names, the viewer provides a function that creates the object for you. [Click here](event-payloads.md) to view a list of exported functions that creates the object for each event. Just like validation functions, the functions that creates the object are exported under `cgpv.types`. The first parameter in the object creating function is required. To emit an event you must provide the event name to emit. Just like the `.on()` function, you can access the existing event names from `cgpv.api.eventNames`. -The second parameter is an optional parameter for the handler name, usually its the map id. This can be set to null if you are emitting to all maps or if you want to set handler names in the second parameter of the `.emit()` function. +The second parameter is an optional parameter for the handler name, usually the map id. It can be set to null if you are emitting to all maps. If you want to target a specific map element, for example a panel, its value is a list of handler names separated by slashes. The third parameter is payload data, this can be any types. For **existing** events you need to provide certain payload data. To list a list of payload data [click here](event-payloads.md) ```js -// here you will notice the second parameter for the snackbarMessagePayload function is mapOne. This can be null and you can provide the handler id in the second paramter of the emit function. In here the event name is snackbar/open. Providing the handler name it will automatically become snackbar/open/mapOne. Here we omitted the second parameter for the emit function. +// here you will notice the second parameter for the snackbarMessagePayload function is mapOne. This can be null if you +// target all maps. In here the event name is snackbar/open. Providing the handler name it will automatically become +// snackbar/open/mapOne. cgpv.api.event.emit( cgpv.types.snackbarMessagePayload( cgpv.api.eventNames.SNACKBAR.EVENT_SNACKBAR_OPEN, @@ -148,26 +153,30 @@ cgpv.api.event.emit( ); ``` -#### The second+ list of optional parameters +#### The handler name parameters -Just like the `.on()` function, the `.emit()` function gives the user the option to provide a list of handler names. This is optional because a user has the opportunity to provide a handler name (mainly the map id) in the first parameter. If you add handler names they will automatically be appended to the event name for example +Just like the `.on()` function, the `.emit()` function gives the user the option to provide a list of handler names. If you add handler names, you can use a slash +separated list of strings. The resulting handler names will automatically be appended to the event name, for example: ```js -// you will notice that for the second paramter of the snackbarMessagePayload we set it to null. And for the second paramter of the emit function we provided the handler name. The event name will become snackbar/open/mapOne just like above. The reason we have this is because sometimes you might have multiple handlers such as opening a specefic panel in a specefic map, an example panel/open/mapOne/panelOne. +// you will notice that the second parameter of snackbarMessagePayload is mapOne/panelOne. The event name will become +// snackbar/open/mapOne/panelOne. This way, we can provide multiple handler names to do things like open a specific panel +// in a specific map. cgpv.api.event.emit( cgpv.types.snackbarMessagePayload( cgpv.api.eventNames.SNACKBAR.EVENT_SNACKBAR_OPEN, - null, + "mapOne/panelOne", { type: "string", value: "Hello, World!", } - ), - "mapOne" /* pass here another handler if needed */ + ) ); ``` +The use of a list of names separated by forward slashes is not mandatory. The important thing to remember is that listeners only react to emitters when the event name and handler string is the same. + ### Creating your own event emitter You can create your own event emitter in the same way as above except you will want to pass an object directly in the first parameter of the emit function instead of using a function that will create the object for you. Here is an example to emit the same function above to open a snackbar without using a converter function. @@ -176,26 +185,46 @@ You can create your own event emitter in the same way as above except you will w cgpv.api.event.emit( { event: "snackbar/open", - handlerName: null, + handlerName: "mapOne/panelOne", message: { type: "string", value: "Hello, World!", }, - }, - "mapOne" + } ); ``` ## Turning off an event listener -It's always recommended to turn off the event listeners for clean up. +It's always recommended to turn off the event listeners for clean up. However, you must be careful when disabling events, because if you disable an event associated with a viewer feature, you may interrupt the normal response of the viewer. -To turn off an event listener use the `cgpv.api.event.off()` function. The function takes 1 required parameter and a list of optional parameters. Like the `emit` and `on` functions. The first parameter is the event name and the second parameter is a list of optional handler names. +To turn off an event listener use the `cgpv.api.event.off()` function. The function takes one required parameter and two optional parameter. The first parameter is the event name and the second parameter is an optional handler name string. The last parameter is the optionnal handler function that is used to turn off a single event handler. The following line shows how to deactivate event `snackbar/open` on the map having the identifier `mapOne`. ```js cgpv.api.event.off("snackbar/open", "mapOne"); ``` -## Other available functions +The following lines show how to activate and deactivate a single event `snackbar/open` on the map having the identifier `mapOne`. -`cgpv.api.event.offAll()` takes 1 parameter with a handler name. You can turn off all listeners that includes the handler name. +```js +const callbackFunction = (payload) => { + if (payloadIsRequestLayerInventory(payload)) { + const { layerSetId } = payload; + api.event.emit(LayerSetPayload.createLayerRegistrationPayload(this.mapId, layerPath, 'add', layerSetId)); + } +}; + +api.event.on(EVENT_NAMES.LAYER_SET.REQUEST_LAYER_INVENTORY, callbackFunction, 'mapOne'); + +... + +api.event.off(EVENT_NAMES.LAYER_SET.REQUEST_LAYER_INVENTORY, 'mapOne', callbackFunction); +``` + +## Bulk deactivation of listeners + +The `offAll()` method takes a string parameter. All listeners whose handler name starts with the string assigned to the parameter will be disabled. You must be careful when disabling events, because if you disable an event associated with a viewer feature, you may interrupt the normal response of the viewer. + +```js +cgpv.api.event.offAll("mapOne"); +``` diff --git a/packages/geoview-core/public/templates/layers.html b/packages/geoview-core/public/templates/layers.html index 93a936b3d03..fb8ceeaa3b8 100644 --- a/packages/geoview-core/public/templates/layers.html +++ b/packages/geoview-core/public/templates/layers.html @@ -1612,14 +1612,14 @@

9. GeoPackage Layer

//create snippets createCodeSnippet(); createConfigSnippet(); - cgpv.api.event.emit({ handlerName: 'LYR1', event: cgpv.api.eventNames.GET_LEGENDS.TRIGGER }, 'wmsLegendsId'); - cgpv.api.event.emit({ handlerName: 'LYR3', event: cgpv.api.eventNames.GET_LEGENDS.TRIGGER }, 'esriDynamicLegendsId'); - cgpv.api.event.emit({ handlerName: 'LYR4', event: cgpv.api.eventNames.GET_LEGENDS.TRIGGER }, 'esriFeatureLegendsId'); - cgpv.api.event.emit({ handlerName: 'LYR5', event: cgpv.api.eventNames.GET_LEGENDS.TRIGGER }, 'geojsonLegendsId'); - cgpv.api.event.emit({ handlerName: 'LYR6', event: cgpv.api.eventNames.GET_LEGENDS.TRIGGER }, 'wfsLegendsId'); - cgpv.api.event.emit({ handlerName: 'LYR7', event: cgpv.api.eventNames.GET_LEGENDS.TRIGGER }, 'ogcApiLegendsId'); - cgpv.api.event.emit({ handlerName: 'LYR8', event: cgpv.api.eventNames.GET_LEGENDS.TRIGGER }, 'geocoreLegendsId'); - cgpv.api.event.emit({ handlerName: 'LYR9', event: cgpv.api.eventNames.GET_LEGENDS.TRIGGER }, 'geopackageLegendsId'); + cgpv.api.event.emit({ handlerName: 'LYR1/wmsLegendsId', event: cgpv.api.eventNames.GET_LEGENDS.TRIGGER }); + cgpv.api.event.emit({ handlerName: 'LYR3/esriDynamicLegendsId', event: cgpv.api.eventNames.GET_LEGENDS.TRIGGER }); + cgpv.api.event.emit({ handlerName: 'LYR4/esriFeatureLegendsId', event: cgpv.api.eventNames.GET_LEGENDS.TRIGGER }); + cgpv.api.event.emit({ handlerName: 'LYR5/geojsonLegendsId', event: cgpv.api.eventNames.GET_LEGENDS.TRIGGER }); + cgpv.api.event.emit({ handlerName: 'LYR6/wfsLegendsId', event: cgpv.api.eventNames.GET_LEGENDS.TRIGGER }); + cgpv.api.event.emit({ handlerName: 'LYR7/ogcApiLegendsId', event: cgpv.api.eventNames.GET_LEGENDS.TRIGGER }); + cgpv.api.event.emit({ handlerName: 'LYR8/geocoreLegendsId', event: cgpv.api.eventNames.GET_LEGENDS.TRIGGER }); + cgpv.api.event.emit({ handlerName: 'LYR9/geopackageLegendsId', event: cgpv.api.eventNames.GET_LEGENDS.TRIGGER }); } }); @@ -1664,8 +1664,7 @@

9. GeoPackage Layer

(payload) => { createTableOfFilter('LYR5'); }, - 'LYR5', - 'geoJsonSample' + 'LYR5/geoJsonSample' ); }); @@ -1691,8 +1690,7 @@

9. GeoPackage Layer

const { layerSetId, resultSets } = payload; createInfoTable('LYR1', 'wmsResultSetId', resultSets); }, - 'LYR1', - 'wmsResultSetId' + 'LYR1/wmsResultSetId' ); const LegendsWmsLayerSet = cgpv.api.createLegendsLayerSet('LYR1', 'wmsLegendsId'); @@ -1702,8 +1700,7 @@

9. GeoPackage Layer

const { resultSets } = payload; displayLegend('wmsLegendsId', resultSets); }, - 'LYR1', - 'wmsLegendsId' + 'LYR1/wmsLegendsId' ); // ESRI Dynamic ============================================================================================================= @@ -1714,8 +1711,7 @@

9. GeoPackage Layer

const { layerSetId, resultSets } = payload; createInfoTable('LYR3', 'esriDynamicResultSetId', resultSets); }, - 'LYR3', - 'esriDynamicResultSetId' + 'LYR3/esriDynamicResultSetId' ); const LegendsEsriDynamicLayerSet = cgpv.api.createLegendsLayerSet('LYR3', 'esriDynamicLegendsId'); @@ -1725,8 +1721,7 @@

9. GeoPackage Layer

const { resultSets } = payload; displayLegend('esriDynamicLegendsId', resultSets); }, - 'LYR3', - 'esriDynamicLegendsId' + 'LYR3/esriDynamicLegendsId' ); // ESRI Feature ============================================================================================================= @@ -1737,8 +1732,7 @@

9. GeoPackage Layer

const { layerSetId, resultSets } = payload; createInfoTable('LYR4', 'esriFeatureResultSetId', resultSets); }, - 'LYR4', - 'esriFeatureResultSetId' + 'LYR4/esriFeatureResultSetId' ); const LegendsEsriFeatureLayerSet = cgpv.api.createLegendsLayerSet('LYR4', 'esriFeatureLegendsId'); @@ -1748,8 +1742,7 @@

9. GeoPackage Layer

const { resultSets } = payload; displayLegend('esriFeatureLegendsId', resultSets); }, - 'LYR4', - 'esriFeatureLegendsId' + 'LYR4/esriFeatureLegendsId' ); // GeoJson ================================================================================================================== @@ -1760,8 +1753,7 @@

9. GeoPackage Layer

const { layerSetId, resultSets } = payload; createInfoTable('LYR5', 'geoJsonResultSetId', resultSets); }, - 'LYR5', - 'geoJsonResultSetId' + 'LYR5/geoJsonResultSetId' ); const LegendsGeoJsonLayerSet = cgpv.api.createLegendsLayerSet('LYR5', 'geojsonLegendsId'); @@ -1771,8 +1763,7 @@

9. GeoPackage Layer

const { resultSets } = payload; displayLegend('geojsonLegendsId', resultSets); }, - 'LYR5', - 'geojsonLegendsId' + 'LYR5/geojsonLegendsId' ); // WFS ====================================================================================================================== @@ -1783,8 +1774,7 @@

9. GeoPackage Layer

const { layerSetId, resultSets } = payload; createInfoTable('LYR6', 'wfsResultSetId', resultSets); }, - 'LYR6', - 'wfsResultSetId' + 'LYR6/wfsResultSetId' ); const LegendsWfsLayerSet = cgpv.api.createLegendsLayerSet('LYR6', 'wfsLegendsId'); @@ -1794,8 +1784,7 @@

9. GeoPackage Layer

const { resultSets } = payload; displayLegend('wfsLegendsId', resultSets); }, - 'LYR6', - 'wfsLegendsId' + 'LYR6/wfsLegendsId' ); // OGC Feature API ========================================================================================================== @@ -1806,8 +1795,7 @@

9. GeoPackage Layer

const { layerSetId, resultSets } = payload; createInfoTable('LYR7', 'ogcFeatureResultSetId', resultSets); }, - 'LYR7', - 'ogcFeatureResultSetId' + 'LYR7/ogcFeatureResultSetId' ); const LegendsOgcApiLayerSet = cgpv.api.createLegendsLayerSet('LYR7', 'ogcApiLegendsId'); @@ -1817,8 +1805,7 @@

9. GeoPackage Layer

const { resultSets } = payload; displayLegend('ogcApiLegendsId', resultSets); }, - 'LYR7', - 'ogcApiLegendsId' + 'LYR7/ogcApiLegendsId' ); // GeoCore ================================================================================================================== @@ -1829,8 +1816,7 @@

9. GeoPackage Layer

const { layerSetId, resultSets } = payload; createInfoTable('LYR8', 'geocoreResultSetId', resultSets); }, - 'LYR8', - 'geocoreResultSetId' + 'LYR8/geocoreResultSetId' ); const LegendsGeocoreLayerSet = cgpv.api.createLegendsLayerSet('LYR8', 'geocoreLegendsId'); @@ -1840,8 +1826,7 @@

9. GeoPackage Layer

const { resultSets } = payload; displayLegend('geocoreLegendsId', resultSets); }, - 'LYR8', - 'geocoreLegendsId' + 'LYR8/geocoreLegendsId' ); // GeoPackage ============================================================================================================= @@ -1852,8 +1837,7 @@

9. GeoPackage Layer

const { layerSetId, resultSets } = payload; createInfoTable('LYR9', 'geopackageResultSetId', resultSets); }, - 'LYR9', - 'geopackageResultSetId' + 'LYR9/geopackageResultSetId' ); const LegendsGeopackageLayerSet = cgpv.api.createLegendsLayerSet('LYR9', 'geopackageLegendsId'); @@ -1863,8 +1847,7 @@

9. GeoPackage Layer

const { resultSets } = payload; displayLegend('geopackageLegendsId', resultSets); }, - 'LYR9', - 'geopackageLegendsId' + 'LYR9/geopackageLegendsId' ); // ========================================================================================================================== diff --git a/packages/geoview-core/public/templates/test.html b/packages/geoview-core/public/templates/test.html index 52cec8a0219..adfaa5fb70c 100644 --- a/packages/geoview-core/public/templates/test.html +++ b/packages/geoview-core/public/templates/test.html @@ -209,8 +209,7 @@ const { layerSetId, resultSets } = payload; document.getElementById(layerSetId).textContent = JSON.stringify(resultSets, undefined, 2); }, - 'LYR1', - 'wmsResultSetId' + 'LYR1/wmsResultSetId' ); const LegendsWmsLayerSet = cgpv.api.createLegendsLayerSet('LYR1', 'wmsLegendsId'); @@ -220,13 +219,12 @@ const { resultSets } = payload; displayLegend('wmsLegendsId', resultSets); }, - 'LYR1', - 'wmsLegendsId' + 'LYR1/wmsLegendsId' ); var addGeoJsonLegendButton = document.getElementsByClassName('Get-Legend')[0]; addGeoJsonLegendButton.addEventListener('click', function (e) { - cgpv.api.event.emit({ handlerName: 'LYR1', event: cgpv.api.eventNames.GET_LEGENDS.TRIGGER }, 'wmsLegendsId'); + cgpv.api.event.emit({ handlerName: 'LYR1/wmsLegendsId', event: cgpv.api.eventNames.GET_LEGENDS.TRIGGER }); }); // ========================================================================================================================== diff --git a/packages/geoview-core/public/templates/ui-components.html b/packages/geoview-core/public/templates/ui-components.html index 7459521fd90..c4115fcecc6 100644 --- a/packages/geoview-core/public/templates/ui-components.html +++ b/packages/geoview-core/public/templates/ui-components.html @@ -130,9 +130,7 @@

Accessing slider value from outside of the core viewer using api event liste // set min max function setSliderMinMax() { cgpv.api.event.emit( - // null instead of mapId, "mySliderId" additional parameter to control a specefic slider - cgpv.types.sliderPayload(cgpv.api.eventNames.SLIDER.EVENT_SLIDER_SET_MINMAX, null, { min: 2, max: 16 }), - 'UI1-slider1' + cgpv.types.sliderPayload(cgpv.api.eventNames.SLIDER.EVENT_SLIDER_SET_MINMAX, 'UI1-slider1', { min: 2, max: 16 }) ); } document.getElementById('sliderminmax').addEventListener('click', setSliderMinMax, false); @@ -140,9 +138,7 @@

Accessing slider value from outside of the core viewer using api event liste // set values function setSliderValues() { cgpv.api.event.emit( - // null instead of mapId, "mySliderId" additional parameter to control a specefic slider - cgpv.types.sliderPayload(cgpv.api.eventNames.SLIDER.EVENT_SLIDER_SET_VALUES, null, { value: 8 }), - 'UI1-slider1' + cgpv.types.sliderPayload(cgpv.api.eventNames.SLIDER.EVENT_SLIDER_SET_VALUES, 'UI1-slider1', { value: 8 }) ); } document.getElementById('slidervalues').addEventListener('click', setSliderValues, false); diff --git a/packages/geoview-core/src/api/api.ts b/packages/geoview-core/src/api/api.ts index eb9ace79680..f33f2a53e75 100644 --- a/packages/geoview-core/src/api/api.ts +++ b/packages/geoview-core/src/api/api.ts @@ -13,7 +13,7 @@ import { CONST_LAYER_TYPES } from '../geo/layer/geoview-layers/abstract-geoview- import * as MarkerDefinitions from '../core/types/marker-definitions'; import { generateId, addUiComponent } from '../core/utils/utilities'; import { FeatureInfoLayerSet } from '../geo/utils/feature-info-layer-set'; -import { LegendsLayerSet } from '../geo/utils/legend-layer-set'; +import { LegendsLayerSet } from '../geo/utils/legends-layer-set'; import { GeoViewLayerPayload, payloadIsTestGeoViewLayers } from './events/payloads/geoview-layer-payload'; /** diff --git a/packages/geoview-core/src/api/events/event.ts b/packages/geoview-core/src/api/events/event.ts index 7567bc8ba8b..29e40d6e723 100644 --- a/packages/geoview-core/src/api/events/event.ts +++ b/packages/geoview-core/src/api/events/event.ts @@ -1,9 +1,10 @@ import EventEmitter from 'eventemitter3'; -import { generateId } from '../../core/utils/utilities'; import { EventStringId } from './event-types'; import { PayloadBaseClass } from './payloads/payload-base-class'; +export type TypeEventHandlerFunction = (payload: PayloadBaseClass) => void; + /** * Class used to handle event emitting and subscribing for the API * @@ -15,7 +16,7 @@ export class Event { eventEmitter: EventEmitter; // events object containing all registered events - events: Record> = {}; + events: Record = {}; /** * Initiate the event emitter @@ -30,34 +31,15 @@ export class Event { * @param {string} eventName the event name to listen to * @param {function} listener the callback function * @param {string} [handlerName] the handler name to return data from - * @param {string[]} args optional additional arguments + * + * @returns {TypeEventHandlerFunction} The event handler listener function associated to the event created. */ - on = (eventName: EventStringId, listener: (payload: PayloadBaseClass) => void, handlerName?: string, ...args: string[]): void => { - let eventNameId = eventName + (handlerName && handlerName.length > 0 ? `/${handlerName}` : ''); - - // check if args provided - for (let argIndex = 0; argIndex < args.length; argIndex++) { - eventNameId = `${eventNameId}/${args[argIndex]}`; - } - - /** - * Listen callback, sets the data that will be returned back - * @param payload payload being passed when emitted - */ - const listen = (payload: PayloadBaseClass) => { - let listenerPayload: PayloadBaseClass; + on = (eventName: EventStringId, listener: TypeEventHandlerFunction, handlerName?: string): TypeEventHandlerFunction => { + const eventNameId = `${eventName}${handlerName ? `/${handlerName}` : ''}`; + if (!this.events[eventNameId]) this.events[eventNameId] = handlerName || ''; - // if a handler name was specified, callback will return that data if found - if (handlerName && payload.handlerName === handlerName && this.events[eventNameId] && this.events[eventNameId][handlerName]) { - listenerPayload = this.events[eventNameId][handlerName]; - } else { - listenerPayload = payload; - } - - listener(listenerPayload); - }; - - this.eventEmitter.on(eventNameId, listen); + this.eventEmitter.on(eventNameId, listener); + return listener; }; /** @@ -66,34 +48,15 @@ export class Event { * @param {string} eventName the event name to listen to * @param {function} listener the callback function * @param {string} [handlerName] the handler name to return data from - * @param {string[]} args optional additional arguments + * + * @returns {TypeEventHandlerFunction} The event handler listener function associated to the event created. */ - once = (eventName: EventStringId, listener: (payload: PayloadBaseClass) => void, handlerName?: string, ...args: string[]): void => { - let eventNameId = eventName + (handlerName && handlerName.length > 0 ? `/${handlerName}` : ''); + once = (eventName: EventStringId, listener: TypeEventHandlerFunction, handlerName?: string): TypeEventHandlerFunction => { + const eventNameId = `${eventName}${handlerName ? `/${handlerName}` : ''}`; + if (!this.events[eventNameId]) this.events[eventNameId] = handlerName || ''; - // check if args provided - for (let argIndex = 0; argIndex < args.length; argIndex++) { - eventNameId = `${eventNameId}/${args[argIndex]}`; - } - - /** - * Listen callback, sets the data that will be returned back - * @param payload payload being passed when emitted - */ - const listen = (payload: PayloadBaseClass) => { - let listenerPayload: PayloadBaseClass; - - // if a handler name was specefieid, callback will return that data if found - if (handlerName && payload.handlerName === handlerName && this.events[eventNameId] && this.events[eventNameId][handlerName]) { - listenerPayload = this.events[eventNameId][handlerName]; - } else { - listenerPayload = payload; - } - - listener(listenerPayload); - }; - - this.eventEmitter.once(eventNameId, listen); + this.eventEmitter.once(eventNameId, listener); + return listener; }; /** @@ -101,30 +64,25 @@ export class Event { * * @param {string} eventName the event name of the event to be removed * @param {string} handlerName the name of the handler an event needs to be removed from - * @param {string[]} args optional additional arguments + * @param {TypeEventHandlerFunction} listener The event handler listener function associated to the event created. */ - off = (eventName: EventStringId, handlerName?: string, ...args: string[]): void => { - let eventNameId = eventName + (handlerName && handlerName.length > 0 ? `/${handlerName}` : ''); - - // check if args provided - for (let argIndex = 0; argIndex < args.length; argIndex++) { - eventNameId = `${eventNameId}/${args[argIndex]}`; - } + off = (eventName: EventStringId, handlerName?: string, listener?: TypeEventHandlerFunction): void => { + const eventNameId = `${eventName}${handlerName ? `/${handlerName}` : ''}`; - this.eventEmitter.off(eventNameId); + this.eventEmitter.off(eventNameId, listener); delete this.events[eventNameId]; }; /** - * Unsubscribe from all events on the map + * Unregister all events whose handler names start with the string passed in parameter. * - * @param {string} handlerName the id of the map to turn unsubscribe the event from + * @param {string} handlerNamePrefix the handler name prefix for which you need to unregister from the event */ - offAll = (handlerName: string): void => { - Object.keys(this.events).forEach((event) => { - if (event.includes(handlerName)) { - this.off(event as EventStringId); + offAll = (handlerNamePrefix: string): void => { + Object.keys(this.events).forEach((eventNameId) => { + if (this.events[eventNameId].startsWith(handlerNamePrefix)) { + this.off(eventNameId as EventStringId); } }); }; @@ -133,57 +91,10 @@ export class Event { * Will emit the event on the event name with the @payload * * @param {object} payload a payload (data) to be emitted for the event - * @param {string[]} args optional additional arguments */ - emit = (payload: PayloadBaseClass, ...args: string[]): void => { + emit = (payload: PayloadBaseClass): void => { const { handlerName, event } = payload; - - let customHandlerNames: string | undefined; - - // event name - let eventName = event + (handlerName && handlerName.length > 0 ? `/${handlerName}` : ''); - - // check if args provided - for (let argIndex = 0; argIndex < args.length; argIndex++) { - eventName = `${eventName}/${args[argIndex]}`; - - customHandlerNames = `${customHandlerNames ? `${customHandlerNames}/` : ''}${args[argIndex]}`; - } - - // handler name, registers a unique handler to be used when multiple events emit with same event name - const handlerNameId = generateId(handlerName || customHandlerNames); - - if (!this.events[eventName]) { - this.events[eventName] = {}; - } - - // store the emitted event to the events array - this.events[eventName][handlerNameId] = { - ...payload, - handlerName: handlerName || customHandlerNames, - } as PayloadBaseClass; - - this.eventEmitter.emit(eventName, { ...payload, handlerName: handlerName || customHandlerNames }, handlerName || customHandlerNames); - }; - - /** - * Get all the event handler names on a specified event - * @param eventName the event name to get all it's handler names - * @returns an array of all the event handler names - */ - getHandlerNames = (eventName: string): Array => { - if (this.events && this.events[eventName]) { - return Object.keys(this.events[eventName]); - } - - return []; - }; - - /** - * Get all events with their data and event handler names - * @returns all the events with their data and handler names - */ - getEvents = (): Record> => { - return this.events; + const eventName = `${event}${handlerName ? `/${handlerName}` : ''}`; + this.eventEmitter.emit(eventName, { ...payload, handlerName }, handlerName); }; } diff --git a/packages/geoview-core/src/api/events/payloads/footer-tab-payload.ts b/packages/geoview-core/src/api/events/payloads/footer-tab-payload.ts index 26dd215d470..4dc54368f5c 100644 --- a/packages/geoview-core/src/api/events/payloads/footer-tab-payload.ts +++ b/packages/geoview-core/src/api/events/payloads/footer-tab-payload.ts @@ -58,6 +58,6 @@ export class FooterTabPayload extends PayloadBaseClass { * * @returns {FooterTabPayload} the FooterTabPayload object created */ -export const tabPayload = (event: EventStringId, handlerName: string | null, tab: TypeTabs): FooterTabPayload => { +export const footerTabPayload = (event: EventStringId, handlerName: string | null, tab: TypeTabs): FooterTabPayload => { return new FooterTabPayload(event, handlerName, tab); }; diff --git a/packages/geoview-core/src/api/events/payloads/get-feature-info-payload.ts b/packages/geoview-core/src/api/events/payloads/get-feature-info-payload.ts index 2a02f8b11aa..421283ecf09 100644 --- a/packages/geoview-core/src/api/events/payloads/get-feature-info-payload.ts +++ b/packages/geoview-core/src/api/events/payloads/get-feature-info-payload.ts @@ -2,7 +2,6 @@ import { Extent } from 'ol/extent'; import { Coordinate } from 'ol/coordinate'; import { FeatureLike } from 'ol/Feature'; import { Pixel } from 'ol/pixel'; -import { Geometry } from 'ol/geom'; import { PayloadBaseClass } from './payload-base-class'; @@ -54,7 +53,7 @@ export type TypeFeatureInfoEntry = { }; export type TypeArrayOfFeatureInfoEntries = TypeFeatureInfoEntry[]; -export type TypeFeatureInfoResultSets = { [layerPath: string]: TypeArrayOfFeatureInfoEntries | undefined }; +export type TypeFeatureInfoResultSets = { [layerPath: string]: TypeArrayOfFeatureInfoEntries | undefined | null }; /** * type guard function that redefines a PayloadBaseClass as a TypeQueryLayerPayload diff --git a/packages/geoview-core/src/api/events/payloads/lat-long-payload.ts b/packages/geoview-core/src/api/events/payloads/lng-lat-payload.ts similarity index 100% rename from packages/geoview-core/src/api/events/payloads/lat-long-payload.ts rename to packages/geoview-core/src/api/events/payloads/lng-lat-payload.ts diff --git a/packages/geoview-core/src/api/events/payloads/marker-definition-payload.ts b/packages/geoview-core/src/api/events/payloads/marker-definition-payload.ts index 00f9f007d43..d5e758c3b72 100644 --- a/packages/geoview-core/src/api/events/payloads/marker-definition-payload.ts +++ b/packages/geoview-core/src/api/events/payloads/marker-definition-payload.ts @@ -41,11 +41,11 @@ export class MarkerDefinitionPayload extends PayloadBaseClass { * @param {Coordinate} lnglat the marker coordinate * @param {TypeJsonObject} symbology the marker symbology */ - constructor(event: EventStringId, handlerName: string | null, lnglat: Coordinate, symbology: TypeJsonObject) { + constructor(event: EventStringId, handlerName: string | null, lnglat: Coordinate, symbology?: TypeJsonObject) { if (!validEvents.includes(event)) throw new Error(`MarkerIconPayload can't be instanciated for event of type ${event}`); super(event, handlerName); this.lnglat = lnglat; - this.symbology = symbology; + this.symbology = symbology || ({} as TypeJsonObject); } } @@ -64,7 +64,7 @@ export const markerDefinitionPayload = ( event: EventStringId, handlerName: string | null, lnglat: Coordinate, - symbology: TypeJsonObject + symbology?: TypeJsonObject ): MarkerDefinitionPayload => { return new MarkerDefinitionPayload(event, handlerName, lnglat, symbology); }; diff --git a/packages/geoview-core/src/core/components/app-bar/app-bar.tsx b/packages/geoview-core/src/core/components/app-bar/app-bar.tsx index 5faf7b02858..eac74974edc 100644 --- a/packages/geoview-core/src/core/components/app-bar/app-bar.tsx +++ b/packages/geoview-core/src/core/components/app-bar/app-bar.tsx @@ -150,9 +150,7 @@ export function Appbar({ setActivetrap }: AppbarProps): JSX.Element { EVENT_NAMES.APPBAR.EVENT_APPBAR_PANEL_CREATE, (payload) => { if (payloadIsAButtonPanel(payload)) { - if (payload.handlerName && payload.handlerName === mapId) { - addButtonPanel(payload); - } + addButtonPanel(payload); } }, mapId @@ -163,9 +161,7 @@ export function Appbar({ setActivetrap }: AppbarProps): JSX.Element { EVENT_NAMES.APPBAR.EVENT_APPBAR_PANEL_REMOVE, (payload) => { if (payloadIsAButtonPanel(payload)) { - if (payload.handlerName && payload.handlerName === mapId) { - removeButtonPanel(payload); - } + removeButtonPanel(payload); } }, mapId @@ -176,8 +172,7 @@ export function Appbar({ setActivetrap }: AppbarProps): JSX.Element { () => { setSelectedAppbarButtonId(''); }, - mapId, - selectedAppBarButtonId + `${mapId}/${selectedAppBarButtonId}` ); return () => { diff --git a/packages/geoview-core/src/core/components/attribution/attribution.tsx b/packages/geoview-core/src/core/components/attribution/attribution.tsx index 9160b781bae..a168fbdcc0e 100644 --- a/packages/geoview-core/src/core/components/attribution/attribution.tsx +++ b/packages/geoview-core/src/core/components/attribution/attribution.tsx @@ -177,10 +177,9 @@ export function Attribution(): JSX.Element { } } } - if (payload.handlerName!.includes(mapId) && payload.status) { + if (payload.status) { attributionControl.formatAttribution(); - } - if (!payload.status) { + } else { attributionTextRef.current.length = 0; } setAttribtuionTextOpacity(payload.status); diff --git a/packages/geoview-core/src/core/components/click-marker/click-marker.tsx b/packages/geoview-core/src/core/components/click-marker/click-marker.tsx index 5a02e9fdcee..33cc7c38a24 100644 --- a/packages/geoview-core/src/core/components/click-marker/click-marker.tsx +++ b/packages/geoview-core/src/core/components/click-marker/click-marker.tsx @@ -70,9 +70,7 @@ export function ClickMarker(): JSX.Element { EVENT_NAMES.MAP.EVENT_MAP_SINGLE_CLICK, (payload) => { if (payloadIsAMapSingleClick(payload)) { - if (payload.handlerName!.includes(mapId)) { - showMarkerIcon(payload.coordinates.lnglat); - } + showMarkerIcon(payload.coordinates.lnglat); } }, mapId @@ -82,10 +80,8 @@ export function ClickMarker(): JSX.Element { EVENT_NAMES.MARKER_ICON.EVENT_MARKER_ICON_SHOW, (payload) => { if (payloadIsAMarkerDefinition(payload)) { - if (payload.handlerName!.includes(mapId)) { - // TODO: Also implement a symbology define by the payload for feature details item selection. - showMarkerIcon(payload.lnglat); - } + // TODO: Also implement a symbology define by the payload for feature details item selection. + showMarkerIcon(payload.lnglat); } }, mapId @@ -93,12 +89,8 @@ export function ClickMarker(): JSX.Element { api.event.on( EVENT_NAMES.MARKER_ICON.EVENT_MARKER_ICON_HIDE, - (payload) => { - // we do not need to verify the payload as no marker are pass - // we only need to validate if we have handler name (map id) - if (payload.handlerName!.includes(mapId)) { - setShowMarker(false); - } + () => { + setShowMarker(false); }, mapId ); diff --git a/packages/geoview-core/src/core/components/crosshair/crosshair.tsx b/packages/geoview-core/src/core/components/crosshair/crosshair.tsx index 9daff24099e..26068d0d700 100644 --- a/packages/geoview-core/src/core/components/crosshair/crosshair.tsx +++ b/packages/geoview-core/src/core/components/crosshair/crosshair.tsx @@ -12,7 +12,7 @@ import { EVENT_NAMES } from '../../../api/events/event-types'; import { CrosshairIcon } from './crosshair-icon'; import { Fade } from '../../../ui'; -import { lngLatPayload } from '../../../api/events/payloads/lat-long-payload'; +import { lngLatPayload } from '../../../api/events/payloads/lng-lat-payload'; import { booleanPayload } from '../../../api/events/payloads/boolean-payload'; import { payloadIsAInKeyfocus } from '../../../api/events/payloads/in-keyfocus-payload'; @@ -78,7 +78,7 @@ export function Crosshair(): JSX.Element { const lnglatPoint = map.getView().getCenter()!; if (isCrosshairsActiveValue.current) { - // emit an event with the latlng point + // emit an event with the lnglat point api.event.emit(lngLatPayload(EVENT_NAMES.DETAILS_PANEL.EVENT_DETAILS_PANEL_CROSSHAIR_ENTER, mapId, lnglatPoint)); } } @@ -108,7 +108,7 @@ export function Crosshair(): JSX.Element { EVENT_NAMES.MAP.EVENT_MAP_IN_KEYFOCUS, (payload) => { if (payloadIsAInKeyfocus(payload)) { - if (payload.handlerName!.includes(mapId) && interaction !== 'static') { + if (interaction !== 'static') { setCrosshairsActive(true); isCrosshairsActiveValue.current = true; api.event.emit(booleanPayload(EVENT_NAMES.MAP.EVENT_MAP_CROSSHAIR_ENABLE_DISABLE, mapId, true)); diff --git a/packages/geoview-core/src/core/components/footer-bar/footer-bar-fixnorth-switch.tsx b/packages/geoview-core/src/core/components/footer-bar/footer-bar-fixnorth-switch.tsx index 6aafc47ac64..cbe7ac6a847 100644 --- a/packages/geoview-core/src/core/components/footer-bar/footer-bar-fixnorth-switch.tsx +++ b/packages/geoview-core/src/core/components/footer-bar/footer-bar-fixnorth-switch.tsx @@ -52,9 +52,7 @@ export function FooterbarFixNorthSwitch(): JSX.Element { EVENT_NAMES.FOOTERBAR.EVENT_FOOTERBAR_EXPAND_COLLAPSE, (payload) => { if (payloadIsABoolean(payload)) { - if (payload.handlerName!.includes(mapId)) { - setExpanded(payload.status); - } + setExpanded(payload.status); } }, mapId @@ -64,7 +62,7 @@ export function FooterbarFixNorthSwitch(): JSX.Element { api.event.on( EVENT_NAMES.MAP.EVENT_MAP_VIEW_PROJECTION_CHANGE, (payload) => { - if (payload.handlerName === mapId && payloadIsAMapViewProjection(payload)) { + if (payloadIsAMapViewProjection(payload)) { setMapProjection(`EPSG:${payload.projection}`); // uncheck the control diff --git a/packages/geoview-core/src/core/components/footer-bar/footer-bar-rotation-button.tsx b/packages/geoview-core/src/core/components/footer-bar/footer-bar-rotation-button.tsx index 693762ca57f..81fcfab4f80 100644 --- a/packages/geoview-core/src/core/components/footer-bar/footer-bar-rotation-button.tsx +++ b/packages/geoview-core/src/core/components/footer-bar/footer-bar-rotation-button.tsx @@ -75,7 +75,7 @@ export function FooterbarRotationButton(): JSX.Element { api.event.on( EVENT_NAMES.MAP.EVENT_MAP_VIEW_PROJECTION_CHANGE, (payload) => { - if (payload.handlerName === mapId && payloadIsAMapViewProjection(payload)) { + if (payloadIsAMapViewProjection(payload)) { // reset icon rotation to 0 because the new view rotation is 0 // will be set again by proper function if needed (i.e. if fix north switch is checked) if (iconRef && iconRef.current) { diff --git a/packages/geoview-core/src/core/components/footer-tabs/footer-tabs-api.ts b/packages/geoview-core/src/core/components/footer-tabs/footer-tabs-api.ts index f93624a0ec8..ec13be44c7e 100644 --- a/packages/geoview-core/src/core/components/footer-tabs/footer-tabs-api.ts +++ b/packages/geoview-core/src/core/components/footer-tabs/footer-tabs-api.ts @@ -3,7 +3,7 @@ import { api } from '../../../app'; import { EVENT_NAMES } from '../../../api/events/event-types'; import { sanitizeHtmlContent } from '../../utils/utilities'; -import { tabPayload } from '../../../api/events/payloads/footer-tab-payload'; +import { footerTabPayload } from '../../../api/events/payloads/footer-tab-payload'; import { TypeTabs } from '../../../ui/tabs/tabs'; @@ -49,7 +49,7 @@ export class FooterTabsApi { this.tabs.push(tabProps); // trigger an event that a new tab has been created - api.event.emit(tabPayload(EVENT_NAMES.FOOTER_TABS.EVENT_FOOTER_TABS_TAB_CREATE, this.mapId, tabProps)); + api.event.emit(footerTabPayload(EVENT_NAMES.FOOTER_TABS.EVENT_FOOTER_TABS_TAB_CREATE, this.mapId, tabProps)); } } }; @@ -68,7 +68,7 @@ export class FooterTabsApi { this.tabs = this.tabs.filter((tab) => tab.value !== value); // trigger an event that a tab has been removed - api.event.emit(tabPayload(EVENT_NAMES.FOOTER_TABS.EVENT_FOOTER_TABS_TAB_REMOVE, this.mapId, tabToRemove)); + api.event.emit(footerTabPayload(EVENT_NAMES.FOOTER_TABS.EVENT_FOOTER_TABS_TAB_REMOVE, this.mapId, tabToRemove)); } }; @@ -82,7 +82,7 @@ export class FooterTabsApi { const tabToSelect = this.tabs.find((tab) => tab.value === value); if (tabToSelect) { // trigger an event to select the tab - api.event.emit(tabPayload(EVENT_NAMES.FOOTER_TABS.EVENT_FOOTER_TABS_TAB_SELECT, this.mapId, tabToSelect)); + api.event.emit(footerTabPayload(EVENT_NAMES.FOOTER_TABS.EVENT_FOOTER_TABS_TAB_SELECT, this.mapId, tabToSelect)); } }; } diff --git a/packages/geoview-core/src/core/components/footer-tabs/footer-tabs.tsx b/packages/geoview-core/src/core/components/footer-tabs/footer-tabs.tsx index b2c8b96c5c4..4f6bc1f76b0 100644 --- a/packages/geoview-core/src/core/components/footer-tabs/footer-tabs.tsx +++ b/packages/geoview-core/src/core/components/footer-tabs/footer-tabs.tsx @@ -122,19 +122,17 @@ export function FooterTabs(): JSX.Element | null { EVENT_NAMES.FOOTER_TABS.EVENT_FOOTER_TABS_TAB_CREATE, (payload) => { if (payloadIsAFooterTab(payload)) { - if (payload.handlerName && payload.handlerName === mapId) { - addTab(payload); - // Check if footer-panel is collapsed or not, and size accordingly - if (tabsContainerRef && tabsContainerRef.current) { - const tabsContainer = tabsContainerRef.current as HTMLDivElement; - const mapContainer = tabsContainer.previousElementSibling as HTMLDivElement; - if (mapContainer.style.height === 'calc(100% - 300px)') { - setIsCollapsed(false); - tabsContainer.style.height = '300px'; - } else { - setIsCollapsed(true); - tabsContainer.style.height = '55px'; - } + addTab(payload); + // Check if footer-panel is collapsed or not, and size accordingly + if (tabsContainerRef && tabsContainerRef.current) { + const tabsContainer = tabsContainerRef.current as HTMLDivElement; + const mapContainer = tabsContainer.previousElementSibling as HTMLDivElement; + if (mapContainer.style.height === 'calc(100% - 300px)') { + setIsCollapsed(false); + tabsContainer.style.height = '300px'; + } else { + setIsCollapsed(true); + tabsContainer.style.height = '55px'; } } } @@ -147,9 +145,7 @@ export function FooterTabs(): JSX.Element | null { EVENT_NAMES.FOOTER_TABS.EVENT_FOOTER_TABS_TAB_REMOVE, (payload) => { if (payloadIsAFooterTab(payload)) { - if (payload.handlerName && payload.handlerName === mapId) { - removeTab(payload); - } + removeTab(payload); } }, mapId @@ -160,14 +156,12 @@ export function FooterTabs(): JSX.Element | null { EVENT_NAMES.FOOTER_TABS.EVENT_FOOTER_TABS_TAB_SELECT, (payload) => { if (payloadIsAFooterTab(payload)) { - if (payload.handlerName && payload.handlerName === mapId) { - // for details tab, extand the tab - if (payload.tab.value === 1) { - handleCollapse(); - } - setSelectedTab(undefined); // this will always trigger the tab change, needed in case user changes selection - setSelectedTab(payload.tab.value); + // for details tab, extand the tab + if (payload.tab.value === 1) { + handleCollapse(); } + setSelectedTab(undefined); // this will always trigger the tab change, needed in case user changes selection + setSelectedTab(payload.tab.value); } }, mapId diff --git a/packages/geoview-core/src/core/components/legend/legend.tsx b/packages/geoview-core/src/core/components/legend/legend.tsx index 4c27c8f8abd..423e20d98ce 100644 --- a/packages/geoview-core/src/core/components/legend/legend.tsx +++ b/packages/geoview-core/src/core/components/legend/legend.tsx @@ -53,10 +53,9 @@ export function Legend(): JSX.Element | null { api.eventNames.LAYER.EVENT_LAYER_ADDED, () => { addLayer(payload.layerConfig.geoviewLayerId); - api.event.off(api.eventNames.LAYER.EVENT_LAYER_ADDED, mapId, payload.layerConfig.geoviewLayerId); + api.event.off(api.eventNames.LAYER.EVENT_LAYER_ADDED, `${mapId}/${payload.layerConfig.geoviewLayerId}`); }, - mapId, - payload.layerConfig.geoviewLayerId + `${mapId}/${payload.layerConfig.geoviewLayerId}` ); } }, diff --git a/packages/geoview-core/src/core/components/map/map.tsx b/packages/geoview-core/src/core/components/map/map.tsx index c8a62dd551b..9f1c42961b2 100644 --- a/packages/geoview-core/src/core/components/map/map.tsx +++ b/packages/geoview-core/src/core/components/map/map.tsx @@ -31,7 +31,7 @@ import { MapViewer } from '../../../geo/map/map'; import { payloadIsABasemapLayerArray } from '../../../api/events/payloads/basemap-layers-payload'; import { payloadIsAMapViewProjection } from '../../../api/events/payloads/map-view-projection-payload'; import { numberPayload } from '../../../api/events/payloads/number-payload'; -import { lngLatPayload } from '../../../api/events/payloads/lat-long-payload'; +import { lngLatPayload } from '../../../api/events/payloads/lng-lat-payload'; import { TypeMapFeaturesConfig } from '../../types/global-types'; import { TypeMapSingleClick, mapSingleClickPayload } from '../../../api/events/payloads/map-slingle-click-payload'; @@ -185,41 +185,39 @@ export function Map(mapFeaturesConfig: TypeMapFeaturesConfig): JSX.Element { EVENT_NAMES.BASEMAP.EVENT_BASEMAP_LAYERS_UPDATE, (payload) => { if (payloadIsABasemapLayerArray(payload)) { - if (payload.handlerName === mapId) { - // remove previous basemaps - const layers = api.map(mapId).map.getAllLayers(); + // remove previous basemaps + const layers = api.map(mapId).map.getAllLayers(); - // loop through all layers on the map - for (let layerIndex = 0; layerIndex < layers.length; layerIndex++) { - const layer = layers[layerIndex]; + // loop through all layers on the map + for (let layerIndex = 0; layerIndex < layers.length; layerIndex++) { + const layer = layers[layerIndex]; - // get group id that this layer belongs to - const layerId = layer.get('mapId'); + // get group id that this layer belongs to + const layerId = layer.get('mapId'); - // check if the group id matches basemap - if (layerId && layerId === 'basemap') { - // remove the basemap layer - api.map(mapId).map.removeLayer(layer); - } + // check if the group id matches basemap + if (layerId && layerId === 'basemap') { + // remove the basemap layer + api.map(mapId).map.removeLayer(layer); } + } - // add basemap layers - payload.layers.forEach((layer, index) => { - const basemapLayer = new TileLayer({ - opacity: layer.opacity, - source: layer.source, - }); + // add basemap layers + payload.layers.forEach((layer, index) => { + const basemapLayer = new TileLayer({ + opacity: layer.opacity, + source: layer.source, + }); - // set this basemap's group id to basemap - basemapLayer.set('mapId', 'basemap'); + // set this basemap's group id to basemap + basemapLayer.set('mapId', 'basemap'); - // add the basemap layer - api.map(mapId).map.getLayers().insertAt(index, basemapLayer); + // add the basemap layer + api.map(mapId).map.getLayers().insertAt(index, basemapLayer); - // render the layer - basemapLayer.changed(); - }); - } + // render the layer + basemapLayer.changed(); + }); } }, mapId @@ -230,33 +228,31 @@ export function Map(mapFeaturesConfig: TypeMapFeaturesConfig): JSX.Element { EVENT_NAMES.MAP.EVENT_MAP_VIEW_PROJECTION_CHANGE, (payload) => { if (payloadIsAMapViewProjection(payload)) { - if (payload.handlerName === mapId) { - // on map view projection change, layer source needs to be refreshed - const currentView = api.map(mapId).getView(); - const centerCoordinate = toLonLat(currentView.getCenter()!, currentView.getProjection()); - api.map(mapId).setView({ - projection: 3978, - zoom: currentView.getZoom()!, - center: [centerCoordinate[0], centerCoordinate[1]], - }); - const mapLayers = api.map(mapId).layer.geoviewLayers; - Object.entries(mapLayers).forEach((mapLayerEntry) => { - const refreshBaseLayer = (baseLayer: BaseLayer | null) => { - if (baseLayer) { - const layerGroup: Array | Collection | undefined = baseLayer.get('layers'); - if (layerGroup) { - layerGroup.forEach((baseLayerEntry) => { - refreshBaseLayer(baseLayerEntry); - }); - } else { - const layerSource: Source = baseLayer.get('source'); - layerSource.refresh(); - } + // on map view projection change, layer source needs to be refreshed + const currentView = api.map(mapId).getView(); + const centerCoordinate = toLonLat(currentView.getCenter()!, currentView.getProjection()); + api.map(mapId).setView({ + projection: 3978, + zoom: currentView.getZoom()!, + center: [centerCoordinate[0], centerCoordinate[1]], + }); + const mapLayers = api.map(mapId).layer.geoviewLayers; + Object.entries(mapLayers).forEach((mapLayerEntry) => { + const refreshBaseLayer = (baseLayer: BaseLayer | null) => { + if (baseLayer) { + const layerGroup: Array | Collection | undefined = baseLayer.get('layers'); + if (layerGroup) { + layerGroup.forEach((baseLayerEntry) => { + refreshBaseLayer(baseLayerEntry); + }); + } else { + const layerSource: Source = baseLayer.get('source'); + layerSource.refresh(); } - }; - refreshBaseLayer(mapLayerEntry[1].gvLayers); - }); - } + } + }; + refreshBaseLayer(mapLayerEntry[1].gvLayers); + }); } }, mapId diff --git a/packages/geoview-core/src/core/components/nav-bar/nav-bar.tsx b/packages/geoview-core/src/core/components/nav-bar/nav-bar.tsx index bb91b416014..f2022908f50 100644 --- a/packages/geoview-core/src/core/components/nav-bar/nav-bar.tsx +++ b/packages/geoview-core/src/core/components/nav-bar/nav-bar.tsx @@ -156,9 +156,7 @@ export function Navbar({ setActivetrap }: NavbarProps): JSX.Element { EVENT_NAMES.NAVBAR.EVENT_NAVBAR_BUTTON_PANEL_CREATE, (payload) => { if (payloadIsAButtonPanel(payload)) { - if (payload.handlerName && payload.handlerName === mapId) { - addButtonPanel(payload); - } + addButtonPanel(payload); } }, mapId @@ -169,9 +167,7 @@ export function Navbar({ setActivetrap }: NavbarProps): JSX.Element { EVENT_NAMES.NAVBAR.EVENT_NAVBAR_BUTTON_PANEL_REMOVE, (payload) => { if (payloadIsAButtonPanel(payload)) { - if (payload.handlerName && payload.handlerName === mapId) { - removeButtonPanel(payload); - } + removeButtonPanel(payload); } }, mapId diff --git a/packages/geoview-core/src/core/components/north-arrow/north-arrow.tsx b/packages/geoview-core/src/core/components/north-arrow/north-arrow.tsx index cd050b04692..34722d1c845 100644 --- a/packages/geoview-core/src/core/components/north-arrow/north-arrow.tsx +++ b/packages/geoview-core/src/core/components/north-arrow/north-arrow.tsx @@ -252,13 +252,11 @@ export function NorthArrow(props: NorthArrowProps): JSX.Element { EVENT_NAMES.MAP.EVENT_MAP_FIX_NORTH, (payload) => { if (payloadIsABoolean(payload)) { - if (payload.handlerName!.includes(mapId)) { - isNorthFixedValue.current = payload.status; + isNorthFixedValue.current = payload.status; - // if north is fix, trigger the map rotation - if (payload.status) { - manageArrow(api.map(mapId).map); - } + // if north is fix, trigger the map rotation + if (payload.status) { + manageArrow(api.map(mapId).map); } } }, @@ -328,7 +326,7 @@ export function NorthPoleFlag(props: NorthArrowProps): JSX.Element { api.event.on( EVENT_NAMES.MAP.EVENT_MAP_VIEW_PROJECTION_CHANGE, (payload) => { - if (payload.handlerName === mapId && payloadIsAMapViewProjection(payload)) { + if (payloadIsAMapViewProjection(payload)) { setMapProjection(`EPSG:${payload.projection}`); } }, diff --git a/packages/geoview-core/src/core/components/overview-map/overview-map.tsx b/packages/geoview-core/src/core/components/overview-map/overview-map.tsx index 4b9943d7c4a..d2611f24a06 100644 --- a/packages/geoview-core/src/core/components/overview-map/overview-map.tsx +++ b/packages/geoview-core/src/core/components/overview-map/overview-map.tsx @@ -113,49 +113,47 @@ export function OverviewMap(): JSX.Element { EVENT_NAMES.BASEMAP.EVENT_BASEMAP_LAYERS_UPDATE, (payload) => { if (payloadIsABasemapLayerArray(payload)) { - if (payload.handlerName === mapId) { - const overviewMap = api - .map(mapId) - .map.getControls() - .getArray() - .filter((item) => { - return item instanceof OLOverviewMap; - })[0] as OLOverviewMap; - - // remove previous basemaps - const layers = overviewMap.getOverviewMap().getAllLayers(); - - // loop through all layers on the map - for (let layerIndex = 0; layerIndex < layers.length; layerIndex++) { - const layer = layers[layerIndex]; - - // get group id that this layer belongs to - const layerMapId = layer.get('mapId'); - - // check if the group id matches basemap - if (layerMapId && layerMapId === 'basemap') { - // remove the basemap layer - overviewMap.getOverviewMap().removeLayer(layer); - } + const overviewMap = api + .map(mapId) + .map.getControls() + .getArray() + .filter((item) => { + return item instanceof OLOverviewMap; + })[0] as OLOverviewMap; + + // remove previous basemaps + const layers = overviewMap.getOverviewMap().getAllLayers(); + + // loop through all layers on the map + for (let layerIndex = 0; layerIndex < layers.length; layerIndex++) { + const layer = layers[layerIndex]; + + // get group id that this layer belongs to + const layerMapId = layer.get('mapId'); + + // check if the group id matches basemap + if (layerMapId && layerMapId === 'basemap') { + // remove the basemap layer + overviewMap.getOverviewMap().removeLayer(layer); } + } - // add basemap layers - payload.layers.forEach((layer) => { - const basemapLayer = new TileLayer({ - opacity: layer.opacity, - source: layer.source, - }); + // add basemap layers + payload.layers.forEach((layer) => { + const basemapLayer = new TileLayer({ + opacity: layer.opacity, + source: layer.source, + }); - // set this basemap's group id to basemap - basemapLayer.set('mapId', 'basemap'); + // set this basemap's group id to basemap + basemapLayer.set('mapId', 'basemap'); - // add the basemap layer - overviewMap.getOverviewMap().addLayer(basemapLayer); + // add the basemap layer + overviewMap.getOverviewMap().addLayer(basemapLayer); - // render the layer - basemapLayer.changed(); - }); - } + // render the layer + basemapLayer.changed(); + }); } }, mapId @@ -166,27 +164,25 @@ export function OverviewMap(): JSX.Element { EVENT_NAMES.MAP.EVENT_MAP_VIEW_PROJECTION_CHANGE, (payload) => { if (payloadIsAMapViewProjection(payload)) { - if (payload.handlerName === mapId) { - const overviewMap = api - .map(mapId) - .map.getControls() - .getArray() - .filter((item) => { - return item instanceof OLOverviewMap; - })[0] as OLOverviewMap; - - // collapse the overview map, if not projection throw an error - overviewMap.setCollapsed(true); - overviewMap.setMap(null); - - // wait for the view change then set the map and open the overview - // TODO: look for better options then Timeout - setTimeout(() => { - overviewMap.setMap(api.map(mapId).map); - - setTimeout(() => overviewMap.setCollapsed(false), 500); - }, 2000); - } + const overviewMap = api + .map(mapId) + .map.getControls() + .getArray() + .filter((item) => { + return item instanceof OLOverviewMap; + })[0] as OLOverviewMap; + + // collapse the overview map, if not projection throw an error + overviewMap.setCollapsed(true); + overviewMap.setMap(null); + + // wait for the view change then set the map and open the overview + // TODO: look for better options then Timeout + setTimeout(() => { + overviewMap.setMap(api.map(mapId).map); + + setTimeout(() => overviewMap.setCollapsed(false), 500); + }, 2000); } }, mapId diff --git a/packages/geoview-core/src/core/components/scale/scale.tsx b/packages/geoview-core/src/core/components/scale/scale.tsx index 81136f638df..bbb5eb88f8d 100644 --- a/packages/geoview-core/src/core/components/scale/scale.tsx +++ b/packages/geoview-core/src/core/components/scale/scale.tsx @@ -137,9 +137,7 @@ export function Scale(): JSX.Element { EVENT_NAMES.FOOTERBAR.EVENT_FOOTERBAR_EXPAND_COLLAPSE, (payload) => { if (payloadIsABoolean(payload)) { - if (payload.handlerName!.includes(mapId)) { - setExpanded(payload.status); - } + setExpanded(payload.status); } }, mapId diff --git a/packages/geoview-core/src/core/containers/focus-trap.tsx b/packages/geoview-core/src/core/containers/focus-trap.tsx index 5af4f4fed59..fe9ac21fa44 100644 --- a/packages/geoview-core/src/core/containers/focus-trap.tsx +++ b/packages/geoview-core/src/core/containers/focus-trap.tsx @@ -145,9 +145,7 @@ export function FocusTrapDialog(props: FocusTrapProps): JSX.Element { EVENT_NAMES.MAP.EVENT_MAP_IN_KEYFOCUS, (payload) => { if (payloadIsAInKeyfocus(payload)) { - if (payload.handlerName!.includes(focusTrapId)) { - setTimeout(() => document.getElementById(`map-${focusTrapId}`)?.focus(), 0); - } + setTimeout(() => document.getElementById(`map-${focusTrapId}`)?.focus(), 0); } }, focusTrapId diff --git a/packages/geoview-core/src/core/containers/shell.tsx b/packages/geoview-core/src/core/containers/shell.tsx index 88a11852465..93e5c13700f 100644 --- a/packages/geoview-core/src/core/containers/shell.tsx +++ b/packages/geoview-core/src/core/containers/shell.tsx @@ -121,11 +121,10 @@ export function Shell(props: ShellProps): JSX.Element { EVENT_NAMES.MAP.EVENT_MAP_ADD_COMPONENT, (payload) => { if (payloadIsAMapComponent(payload)) { - if (payload.handlerName === shellId) - setComponents((tempComponents) => ({ - ...tempComponents, - [payload.mapComponentId]: payload.component!, - })); + setComponents((tempComponents) => ({ + ...tempComponents, + [payload.mapComponentId]: payload.component!, + })); } }, shellId @@ -136,14 +135,12 @@ export function Shell(props: ShellProps): JSX.Element { EVENT_NAMES.MAP.EVENT_MAP_REMOVE_COMPONENT, (payload) => { if (payloadIsAMapComponent(payload)) { - if (payload.handlerName === shellId) { - const tempComponents: Record = { ...components }; - delete tempComponents[payload.mapComponentId]; - - setComponents(() => ({ - ...tempComponents, - })); - } + const tempComponents: Record = { ...components }; + delete tempComponents[payload.mapComponentId]; + + setComponents(() => ({ + ...tempComponents, + })); } }, shellId @@ -153,10 +150,8 @@ export function Shell(props: ShellProps): JSX.Element { EVENT_NAMES.MAP.EVENT_MAP_LOADED, (payload) => { if (payloadIsAMap(payload)) { - if (payload.handlerName!.includes(shellId)) { - // even if the map loads some layers (basemap) are not finish rendering. Same for north arrow - setIsLoaded(true); - } + // even if the map loads some layers (basemap) are not finish rendering. Same for north arrow + setIsLoaded(true); } }, shellId @@ -167,9 +162,7 @@ export function Shell(props: ShellProps): JSX.Element { EVENT_NAMES.MODAL.EVENT_MODAL_CREATE, (payload) => { if (payloadIsAModal(payload)) { - if (payload.handlerName === shellId) { - updateShell(); - } + updateShell(); } }, shellId diff --git a/packages/geoview-core/src/core/types/cgpv-types.ts b/packages/geoview-core/src/core/types/cgpv-types.ts index 009cab15987..287c489508e 100644 --- a/packages/geoview-core/src/core/types/cgpv-types.ts +++ b/packages/geoview-core/src/core/types/cgpv-types.ts @@ -72,7 +72,7 @@ export * from '../../api/events/payloads/geoview-layer-payload'; export * from '../../api/events/payloads/get-feature-info-payload'; export * from '../../api/events/payloads/get-legends-payload'; export * from '../../api/events/payloads/in-keyfocus-payload'; -export * from '../../api/events/payloads/lat-long-payload'; +export * from '../../api/events/payloads/lng-lat-payload'; export * from '../../api/events/payloads/layer-config-payload'; export * from '../../api/events/payloads/layer-set-payload'; export * from '../../api/events/payloads/map-component-payload'; @@ -118,7 +118,7 @@ export * from '../../geo/renderer/esri-renderer'; export * from '../../geo/renderer/geoview-renderer'; export * from '../../geo/utils/constant'; export * from '../../geo/utils/feature-info-layer-set'; -export * from '../../geo/utils/legend-layer-set'; +export * from '../../geo/utils/legends-layer-set'; export * from '../../geo/utils/utilities'; export * from '../../ui/autocomplete/autocomplete'; export * from '../../ui/button/button'; diff --git a/packages/geoview-core/src/geo/layer/geoview-layers/abstract-geoview-layers.ts b/packages/geoview-core/src/geo/layer/geoview-layers/abstract-geoview-layers.ts index c118a63e94c..2541a867438 100644 --- a/packages/geoview-core/src/geo/layer/geoview-layers/abstract-geoview-layers.ts +++ b/packages/geoview-core/src/geo/layer/geoview-layers/abstract-geoview-layers.ts @@ -39,6 +39,7 @@ import { Layer } from '../layer'; import { LayerSetPayload, payloadIsRequestLayerInventory } from '../../../api/events/payloads/layer-set-payload'; import { GetLegendsPayload, payloadIsQueryLegend } from '../../../api/events/payloads/get-legends-payload'; import { TimeDimension } from '../../../core/utils/date-mgt'; +import { TypeEventHandlerFunction } from '../../../api/events/event'; export type TypeLegend = { layerPath: string; @@ -142,6 +143,12 @@ export const CONST_LAYER_TYPES: Record = { WMS: 'ogcWms', }; +type TypeLayerSetHandlerFunctions = { + requestLayerInventory?: TypeEventHandlerFunction; + queryLegend?: TypeEventHandlerFunction; + queryLayer?: TypeEventHandlerFunction; +}; + // ****************************************************************************************************************************** // ****************************************************************************************************************************** /** ****************************************************************************************************************************** @@ -205,12 +212,15 @@ export abstract class AbstractGeoViewLayer { /** Layer metadata */ layerMetadata: Record = {}; - /** Layer temporal dimension */ + /** Layer temporal dimension indexed by layerPath. */ layerTemporalDimension: Record = {}; /** Attribution used in the OpenLayer source. */ attributions: string[] = []; + /** LayerSet handler functions indexed by layerPath. This property is used to deactivate (off) events attached to a layer. */ + registerToLayerSetListenerFunctions: Record = {}; + /** *************************************************************************************************************************** * The class constructor saves parameters and common configuration parameters in attributes. * @@ -561,53 +571,80 @@ export abstract class AbstractGeoViewLayer { */ protected registerToLayerSets(layerEntryConfig: TypeBaseLayerEntryConfig) { const layerPath = Layer.getLayerPath(layerEntryConfig); + if (!this.registerToLayerSetListenerFunctions[layerPath]) this.registerToLayerSetListenerFunctions[layerPath] = {}; // Listen to events that request a layer inventory and emit a register payload event. // This will register all existing layers to a newly created layer set. + this.registerToLayerSetListenerFunctions[layerPath].requestLayerInventory = (payload) => { + if (payloadIsRequestLayerInventory(payload)) { + const { layerSetId } = payload; + api.event.emit(LayerSetPayload.createLayerRegistrationPayload(this.mapId, layerPath, 'add', layerSetId)); + } + }; + api.event.on( EVENT_NAMES.LAYER_SET.REQUEST_LAYER_INVENTORY, - (payload) => { - if (payloadIsRequestLayerInventory(payload)) { - const { layerSetId } = payload; - api.event.emit(LayerSetPayload.createLayerRegistrationPayload(this.mapId, layerPath, 'add', layerSetId)); - } - }, + this.registerToLayerSetListenerFunctions[layerPath].requestLayerInventory!, this.mapId ); + this.registerToLayerSetListenerFunctions[layerPath].queryLegend = (payload) => { + if (payloadIsQueryLegend(payload)) { + this.getLegend(layerPath).then((queryResult) => { + api.event.emit(GetLegendsPayload.createLegendInfoPayload(this.mapId, layerPath, queryResult)); + }); + } + }; + api.event.on( EVENT_NAMES.GET_LEGENDS.QUERY_LEGEND, - (payload) => { - if (payloadIsQueryLegend(payload)) { - this.getLegend(layerPath).then((queryResult) => { - api.event.emit(GetLegendsPayload.createLegendInfoPayload(this.mapId, layerPath, queryResult)); - }); - } - }, - this.mapId, - layerPath + this.registerToLayerSetListenerFunctions[layerPath].queryLegend!, + `${this.mapId}/${layerPath}` ); if ('featureInfo' in layerEntryConfig.source! && layerEntryConfig.source.featureInfo?.queryable) { // Listen to events that request to query a layer and return the resultset to the requester. - api.event.on( - EVENT_NAMES.GET_FEATURE_INFO.QUERY_LAYER, - (payload) => { - if (payloadIsQueryLayer(payload)) { - const { queryType, location } = payload; - this.getFeatureInfo(location, layerPath, queryType).then((queryResult) => { - api.event.emit(GetFeatureInfoPayload.createQueryResultPayload(this.mapId, layerPath, queryResult)); - }); - } - }, - this.mapId - ); + this.registerToLayerSetListenerFunctions[layerPath].queryLayer = (payload) => { + if (payloadIsQueryLayer(payload)) { + const { queryType, location } = payload; + this.getFeatureInfo(location, layerPath, queryType).then((queryResult) => { + api.event.emit(GetFeatureInfoPayload.createQueryResultPayload(this.mapId, layerPath, queryResult)); + }); + } + }; + + api.event.on(EVENT_NAMES.GET_FEATURE_INFO.QUERY_LAYER, this.registerToLayerSetListenerFunctions[layerPath].queryLayer!, this.mapId); } // Register to layer sets that are already created. api.event.emit(LayerSetPayload.createLayerRegistrationPayload(this.mapId, layerPath, 'add')); } + /** *************************************************************************************************************************** + * This method unregisters the layer from the layer sets. + * + * @param {TypeBaseLayerEntryConfig} layerEntryConfig The layer entry to register. + */ + unregisterFromLayerSets(layerEntryConfig: TypeBaseLayerEntryConfig) { + const layerPath = Layer.getLayerPath(layerEntryConfig); + + api.event.off( + EVENT_NAMES.LAYER_SET.REQUEST_LAYER_INVENTORY, + this.mapId, + this.registerToLayerSetListenerFunctions[layerPath].requestLayerInventory + ); + + api.event.off( + EVENT_NAMES.GET_LEGENDS.QUERY_LEGEND, + `${this.mapId}/${layerPath}`, + this.registerToLayerSetListenerFunctions[layerPath].queryLegend + ); + + if ('featureInfo' in layerEntryConfig.source! && layerEntryConfig.source.featureInfo?.queryable) { + api.event.off(EVENT_NAMES.GET_FEATURE_INFO.QUERY_LAYER, this.mapId, this.registerToLayerSetListenerFunctions[layerPath].queryLayer); + } + } + /** *************************************************************************************************************************** * This method create a layer group. it uses the layer initial settings of the GeoView layer configuration. * diff --git a/packages/geoview-core/src/geo/layer/layer.ts b/packages/geoview-core/src/geo/layer/layer.ts index 7139b599bd1..2a0d9905c7c 100644 --- a/packages/geoview-core/src/geo/layer/layer.ts +++ b/packages/geoview-core/src/geo/layer/layer.ts @@ -14,6 +14,7 @@ import { GeoViewLayerPayload, payloadIsRemoveGeoViewLayer } from '../../api/even import { snackbarMessagePayload } from '../../api/events/payloads/snackbar-message-payload'; import { AbstractGeoViewLayer } from './geoview-layers/abstract-geoview-layers'; import { + TypeBaseLayerEntryConfig, TypeGeoviewLayerConfig, TypeLayerEntryConfig, TypeLayerGroupEntryConfig, @@ -84,62 +85,60 @@ export class Layer { EVENT_NAMES.LAYER.EVENT_ADD_LAYER, (payload) => { if (payloadIsALayerConfig(payload)) { - if (payload.handlerName!.includes(this.mapId)) { - const { layerConfig } = payload; - - if (layerConfigIsGeoCore(layerConfig)) { - const geoCore = new GeoCore(this.mapId); - geoCore.createLayers(layerConfig).then((arrayOfListOfGeoviewLayerConfig) => { - arrayOfListOfGeoviewLayerConfig.forEach((listOfGeoviewLayerConfig) => { - // the -1 applied is because each geocore UUID config count for one. We want to replace the geocore entry by - // the list of geoview layer entries. - api.maps[this.mapId].remainingLayersThatNeedToBeLoaded += listOfGeoviewLayerConfig.length - 1; - listOfGeoviewLayerConfig.forEach((geoviewLayerConfig) => { - this.addGeoviewLayer(geoviewLayerConfig); - }); + const { layerConfig } = payload; + + if (layerConfigIsGeoCore(layerConfig)) { + const geoCore = new GeoCore(this.mapId); + geoCore.createLayers(layerConfig).then((arrayOfListOfGeoviewLayerConfig) => { + arrayOfListOfGeoviewLayerConfig.forEach((listOfGeoviewLayerConfig) => { + // the -1 applied is because each geocore UUID config count for one. We want to replace the geocore entry by + // the list of geoview layer entries. + api.maps[this.mapId].remainingLayersThatNeedToBeLoaded += listOfGeoviewLayerConfig.length - 1; + listOfGeoviewLayerConfig.forEach((geoviewLayerConfig) => { + this.addGeoviewLayer(geoviewLayerConfig); }); }); - } else if (layerConfigIsGeoJSON(layerConfig)) { - const geoJSON = new GeoJSON(this.mapId, layerConfig); - geoJSON.createGeoViewLayers().then(() => { - this.addToMap(geoJSON); - }); - } else if (layerConfigIsGeoPackage(layerConfig)) { - const geoPackage = new GeoPackage(this.mapId, layerConfig); - geoPackage.createGeoViewLayers().then(() => { - this.addToMap(geoPackage); - }); - } else if (layerConfigIsWMS(layerConfig)) { - const wmsLayer = new WMS(this.mapId, layerConfig); - wmsLayer.createGeoViewLayers().then(() => { - this.addToMap(wmsLayer); - }); - } else if (layerConfigIsEsriDynamic(layerConfig)) { - const esriDynamic = new EsriDynamic(this.mapId, layerConfig); - esriDynamic.createGeoViewLayers().then(() => { - this.addToMap(esriDynamic); - }); - } else if (layerConfigIsEsriFeature(layerConfig)) { - const esriFeature = new EsriFeature(this.mapId, layerConfig); - esriFeature.createGeoViewLayers().then(() => { - this.addToMap(esriFeature); - }); - } else if (layerConfigIsWFS(layerConfig)) { - const wfsLayer = new WFS(this.mapId, layerConfig); - wfsLayer.createGeoViewLayers().then(() => { - this.addToMap(wfsLayer); - }); - } else if (layerConfigIsOgcFeature(layerConfig)) { - const ogcFeatureLayer = new OgcFeature(this.mapId, layerConfig); - ogcFeatureLayer.createGeoViewLayers().then(() => { - this.addToMap(ogcFeatureLayer); - }); - } else if (layerConfigIsXYZTiles(layerConfig)) { - const xyzTiles = new XYZTiles(this.mapId, layerConfig); - xyzTiles.createGeoViewLayers().then(() => { - this.addToMap(xyzTiles); - }); - } + }); + } else if (layerConfigIsGeoJSON(layerConfig)) { + const geoJSON = new GeoJSON(this.mapId, layerConfig); + geoJSON.createGeoViewLayers().then(() => { + this.addToMap(geoJSON); + }); + } else if (layerConfigIsGeoPackage(layerConfig)) { + const geoPackage = new GeoPackage(this.mapId, layerConfig); + geoPackage.createGeoViewLayers().then(() => { + this.addToMap(geoPackage); + }); + } else if (layerConfigIsWMS(layerConfig)) { + const wmsLayer = new WMS(this.mapId, layerConfig); + wmsLayer.createGeoViewLayers().then(() => { + this.addToMap(wmsLayer); + }); + } else if (layerConfigIsEsriDynamic(layerConfig)) { + const esriDynamic = new EsriDynamic(this.mapId, layerConfig); + esriDynamic.createGeoViewLayers().then(() => { + this.addToMap(esriDynamic); + }); + } else if (layerConfigIsEsriFeature(layerConfig)) { + const esriFeature = new EsriFeature(this.mapId, layerConfig); + esriFeature.createGeoViewLayers().then(() => { + this.addToMap(esriFeature); + }); + } else if (layerConfigIsWFS(layerConfig)) { + const wfsLayer = new WFS(this.mapId, layerConfig); + wfsLayer.createGeoViewLayers().then(() => { + this.addToMap(wfsLayer); + }); + } else if (layerConfigIsOgcFeature(layerConfig)) { + const ogcFeatureLayer = new OgcFeature(this.mapId, layerConfig); + ogcFeatureLayer.createGeoViewLayers().then(() => { + this.addToMap(ogcFeatureLayer); + }); + } else if (layerConfigIsXYZTiles(layerConfig)) { + const xyzTiles = new XYZTiles(this.mapId, layerConfig); + xyzTiles.createGeoViewLayers().then(() => { + this.addToMap(xyzTiles); + }); } } }, @@ -221,7 +220,7 @@ export class Layer { pathEnding = layerEntryConfig.layerPathEnding === undefined ? layerEntryConfig.layerId - : `${layerEntryConfig.layerId}/${layerEntryConfig.layerPathEnding}`; + : `${layerEntryConfig.layerId}.${layerEntryConfig.layerPathEnding}`; if (layerEntryConfig.geoviewRootLayer === layerEntryConfig.parentLayerConfig) return `${layerEntryConfig.geoviewRootLayer!.geoviewLayerId!}/${pathEnding}`; return this.getLayerPath( @@ -281,7 +280,9 @@ export class Layer { const layerInterval = setInterval(() => { if (this.geoviewLayers[geoviewLayer.geoviewLayerId]) { clearInterval(layerInterval); - api.event.emit(GeoViewLayerPayload.createGeoviewLayerAddedPayload(this.mapId, geoviewLayer), geoviewLayer.geoviewLayerId); + api.event.emit( + GeoViewLayerPayload.createGeoviewLayerAddedPayload(`${this.mapId}/${geoviewLayer.geoviewLayerId}`, geoviewLayer) + ); } }, 10); } @@ -298,17 +299,33 @@ export class Layer { * @param {string} partialLayerPath the path of the layer to be removed */ removeLayersUsingPath = (partialLayerPath: string): void => { + // A layer path is a slash seperated string made of the GeoView layer Id followed by the layer Ids + const partialLayerPathNodes = partialLayerPath.split('/'); + + // initialize these two constant now because we will delete the information use to get their values. + const indexToDelete = this.registeredLayers[partialLayerPath] + ? this.registeredLayers[partialLayerPath].parentLayerConfig?.listOfLayerEntryConfig.findIndex( + (layerEntryConfig) => layerEntryConfig === this.registeredLayers?.[partialLayerPath] + ) + : undefined; + const listOfLayerEntryConfigAffected = this.registeredLayers[partialLayerPath]?.parentLayerConfig?.listOfLayerEntryConfig; + Object.keys(this.registeredLayers).forEach((completeLayerPath) => { - if (completeLayerPath.startsWith(partialLayerPath)) { - this.registeredLayers[completeLayerPath]?.gvLayer!.dispose(); - const layerId = partialLayerPath.split('/').slice(-1)[0]; - const listOfLayerEntryConfig = this.registeredLayers[completeLayerPath].parentLayerConfig?.listOfLayerEntryConfig; - const layerIndex = listOfLayerEntryConfig!.findIndex((layerConfig) => layerConfig.layerId === layerId); - delete listOfLayerEntryConfig![layerIndex]; - api.event.emit(LayerSetPayload.createLayerRegistrationPayload(this.mapId, completeLayerPath, 'remove')); + const completeLayerPathNodes = completeLayerPath.split('/'); + const pathBeginningAreEqual = partialLayerPathNodes.reduce((areEqual, partialLayerPathNode, nodeIndex) => { + return areEqual && partialLayerPathNode === completeLayerPathNodes[nodeIndex]; + }, true); + if (pathBeginningAreEqual) { + const layerEntryConfigToRemove = this.registeredLayers[completeLayerPath]; + layerEntryConfigToRemove.gvLayer?.dispose(); + if (layerEntryConfigToRemove.entryType !== 'group') { + this.geoviewLayers[partialLayerPathNodes[0]].unregisterFromLayerSets(layerEntryConfigToRemove as TypeBaseLayerEntryConfig); + api.event.emit(LayerSetPayload.createLayerRegistrationPayload(this.mapId, completeLayerPath, 'remove')); + } delete this.registeredLayers[completeLayerPath]; } }); + if (listOfLayerEntryConfigAffected) listOfLayerEntryConfigAffected.splice(indexToDelete!, 1); if (this.geoviewLayers[partialLayerPath]) { // The clearTimeout is there for those rare extreme cases where you create a layer and then immediately destroy diff --git a/packages/geoview-core/src/geo/map/map.ts b/packages/geoview-core/src/geo/map/map.ts index 67dd33f7351..5ddf05ad893 100644 --- a/packages/geoview-core/src/geo/map/map.ts +++ b/packages/geoview-core/src/geo/map/map.ts @@ -225,8 +225,7 @@ export class MapViewer { } } }, - this.mapId, - geoviewLayerConfig.geoviewLayerId + `${this.mapId}/${geoviewLayerConfig.geoviewLayerId}` ); } }); diff --git a/packages/geoview-core/src/geo/projection/projection.ts b/packages/geoview-core/src/geo/projection/projection.ts index bb81edb63c8..12a5ee6f354 100644 --- a/packages/geoview-core/src/geo/projection/projection.ts +++ b/packages/geoview-core/src/geo/projection/projection.ts @@ -10,7 +10,7 @@ import { get as getOLProjection, Projection as OLProjection, getPointResolution export const PROJECTION_NAMES = { LCC: 'EPSG:3978', WM: 'EPSG:3857', - LATLNG: 'EPSG:4326', + LNGLAT: 'EPSG:4326', }; /** @@ -110,21 +110,21 @@ export class Projection { }; /** - * Convert points from LATLNG EPSG:4326 to LCC EPSG:3978 + * Convert points from LNGLAT EPSG:4326 to LCC EPSG:3978 * * @param {Array>} points array of passed in points to convert */ - latLngToLCC = (points: Array>): Array | number> => { - return this.transformPoints(points, PROJECTION_NAMES.LATLNG, PROJECTION_NAMES.LCC); + lngLatToLCC = (points: Array>): Array | number> => { + return this.transformPoints(points, PROJECTION_NAMES.LNGLAT, PROJECTION_NAMES.LCC); }; /** - * Convert points from LATLNG EPSG:4326 to WM EPSG:3857 + * Convert points from LNGLAT EPSG:4326 to WM EPSG:3857 * * @param {Array>} points array of passed in points to convert */ - latLngToWm = (points: Array>): Array | number> => { - return this.transformPoints(points, PROJECTION_NAMES.LATLNG, PROJECTION_NAMES.WM); + LngLatToWm = (points: Array>): Array | number> => { + return this.transformPoints(points, PROJECTION_NAMES.LNGLAT, PROJECTION_NAMES.WM); }; /** @@ -137,21 +137,21 @@ export class Projection { }; /** - * Convert points from LCC EPSG:3978 to LATLNG EPSG:4326 + * Convert points from LCC EPSG:3978 to LNGLAT EPSG:4326 * * @param {Array>} points array of passed in points to convert */ - lccToLatLng = (points: Array>): Array | number> => { - return this.transformPoints(points, PROJECTION_NAMES.LCC, PROJECTION_NAMES.LATLNG); + lccToLngLat = (points: Array>): Array | number> => { + return this.transformPoints(points, PROJECTION_NAMES.LCC, PROJECTION_NAMES.LNGLAT); }; /** - * Convert points from WM EPSG:3857 to LATLNG EPSG:4326 + * Convert points from WM EPSG:3857 to LNGLAT EPSG:4326 * * @param {Array>} points array of passed in points to convert */ - wmToLatLng = (points: Array>): Array | number> => { - return this.transformPoints(points, PROJECTION_NAMES.WM, PROJECTION_NAMES.LATLNG); + wmToLngLat = (points: Array>): Array | number> => { + return this.transformPoints(points, PROJECTION_NAMES.WM, PROJECTION_NAMES.LNGLAT); }; /** diff --git a/packages/geoview-core/src/geo/utils/feature-info-layer-set.ts b/packages/geoview-core/src/geo/utils/feature-info-layer-set.ts index 34c58ec709f..9be814532f2 100644 --- a/packages/geoview-core/src/geo/utils/feature-info-layer-set.ts +++ b/packages/geoview-core/src/geo/utils/feature-info-layer-set.ts @@ -17,9 +17,6 @@ export class FeatureInfoLayerSet { /** The map identifier the layer set belongs to. */ mapId: string; - /** The layer set identifier. */ - layerSetId: string; - /** The layer set object. */ layerSet: LayerSet; @@ -42,7 +39,6 @@ export class FeatureInfoLayerSet { return false; }; this.mapId = mapId; - this.layerSetId = layerSetId; this.layerSet = new LayerSet(mapId, layerSetId, this.resultSets, registrationConditionFunction); // Listen to map click and send a query layers event to queryable layers. These layers will return a result set if features @@ -73,8 +69,11 @@ export class FeatureInfoLayerSet { }, true); if (allDone) api.event.emit( - GetFeatureInfoPayload.createAllQueriesDonePayload(this.mapId, this.layerSetId, this.resultSets), - this.layerSetId + GetFeatureInfoPayload.createAllQueriesDonePayload( + `${this.mapId}/${this.layerSet.layerSetId}`, + this.layerSet.layerSetId, + this.resultSets + ) ); } }, diff --git a/packages/geoview-core/src/geo/utils/layer-set.ts b/packages/geoview-core/src/geo/utils/layer-set.ts index 0286f4436af..a04c8543718 100644 --- a/packages/geoview-core/src/geo/utils/layer-set.ts +++ b/packages/geoview-core/src/geo/utils/layer-set.ts @@ -4,11 +4,12 @@ import { LayerSetPayload, payloadIsLayerRegistration, TypeResultSets } from '../ import { api } from '../../app'; /** *************************************************************************************************************************** - * A class to hold a set of layers associated with an array of TypeArrayOfFeatureInfoEntries. When this class is instantiated, - * all layers already loaded on the specified map that are queryable will be added to the set. Layers added afterwards will be - * added to the set if they are queryable. Deleted layers will be removed from the set. + * A class to hold a set of layers associated with an value of any type. When this class is instantiated, all layers already + * loaded on the specified map that have a return value equal to true when the registrationConditionFunction is called using + * the layer path as a parameter will be added to the set. Layers added afterwards will be added to the set if the + * registrationConditionFunction returns true. Deleted layers will be removed from the set. * - * @class FeatureInfoLayerSet + * @class LayerSet */ export class LayerSet { /** The map identifier the layer set belongs to. */ @@ -51,20 +52,13 @@ export class LayerSet { // update the registration of all layer sets if !payload.layerSetId or update only the specified layer set if (!layerSetId || layerSetId === this.layerSetId) { if (this.registrationConditionFunction(layerPath) && action === 'add') { - api.event.once( - EVENT_NAMES.LAYER.EVENT_LAYER_ADDED, - () => { + const layerInterval = setInterval(() => { + if (api.maps[this.mapId].mapIsReady()) { this.resultSets[layerPath] = undefined; - const layerInterval = setInterval(() => { - if (api.maps[this.mapId].mapIsReady()) { - clearInterval(layerInterval); - api.event.emit(LayerSetPayload.createLayerSetUpdatedPayload(this.mapId, this.layerSetId)); - } - }, 250); - }, - this.mapId, - layerPath.split('/')[0] - ); + clearInterval(layerInterval); + api.event.emit(LayerSetPayload.createLayerSetUpdatedPayload(this.mapId, this.layerSetId)); + } else if (api.maps[this.mapId].layer.geoviewLayers[layerPath.split('/')[0]].loadError) clearInterval(layerInterval); + }, 250); } else { delete this.resultSets[layerPath]; api.event.emit(LayerSetPayload.createLayerSetUpdatedPayload(this.mapId, this.layerSetId)); @@ -80,15 +74,15 @@ export class LayerSet { } /** - * Helper function used to instanciate a FeatureInfoLayerSet object. This function - * avoids the "new FeatureInfoLayerSet" syntax. + * Helper function used to instanciate a LayerSet object. This function + * avoids the "new LayerSet" syntax. * * @param {string} mapId The map identifier the layer set belongs to. * @param {string} layerSetId The layer set identifier. * @param {TypeResultSets} resultSets An object that will contain the result sets indexed using the layer path. * @param {(layerPath: string) => boolean} registrationConditionFunction A function to decide if the layer can be added. * - * @returns {FeatureInfoLayerSet} the FeatureInfoLayerSet object created + * @returns {LayerSet} the LayerSet object created */ static create( mapId: string, diff --git a/packages/geoview-core/src/geo/utils/legend-layer-set.ts b/packages/geoview-core/src/geo/utils/legends-layer-set.ts similarity index 91% rename from packages/geoview-core/src/geo/utils/legend-layer-set.ts rename to packages/geoview-core/src/geo/utils/legends-layer-set.ts index 98c3a9f3a20..2293c896a43 100644 --- a/packages/geoview-core/src/geo/utils/legend-layer-set.ts +++ b/packages/geoview-core/src/geo/utils/legends-layer-set.ts @@ -49,7 +49,7 @@ export class LegendsLayerSet { this.layerSet = new LayerSet(mapId, layerSetId, this.resultSets, registrationConditionFunction); // This listener receives the legend information returned by the a layer's getLegend call and store it in the resultSets - // if all the registered layers has received their legend information, a EVENT_NAMES.GET_LEGENDS.ALL_LEGENDS_DONE event + // if all the registered layers has received their legend information, an EVENT_NAMES.GET_LEGENDS.ALL_LEGENDS_DONE event // is triggered. api.event.on( EVENT_NAMES.GET_LEGENDS.LEGEND_INFO, @@ -57,7 +57,7 @@ export class LegendsLayerSet { if (payloadIsLegendInfo(payload)) { const { layerPath, legendInfo } = payload; if (layerPath in this.resultSets) this.resultSets[layerPath] = legendInfo; - if (isAllDone()) api.event.emit(GetLegendsPayload.createAllQueriesDonePayload(this.mapId, this.resultSets), layerSetId); + if (isAllDone()) api.event.emit(GetLegendsPayload.createAllQueriesDonePayload(`${this.mapId}/${layerSetId}`, this.resultSets)); } }, this.mapId @@ -72,28 +72,28 @@ export class LegendsLayerSet { const queryUndefinedLegend = () => { Object.keys(this.resultSets).forEach((layerPath) => { if (this.resultSets[layerPath] === undefined) - api.event.emit(GetLegendsPayload.createQueryLegendPayload(this.mapId, layerPath), layerPath); + api.event.emit(GetLegendsPayload.createQueryLegendPayload(`${this.mapId}/${layerPath}`, layerPath)); }); }; - queryUndefinedLegend(); - api.event.on( EVENT_NAMES.LAYER_SET.UPDATED, (layerUpdatedPayload) => { if (payloadIsLayerSetUpdated(layerUpdatedPayload)) { if (layerUpdatedPayload.layerSetId === layerSetId) { - if (isAllDone()) api.event.emit(GetLegendsPayload.createAllQueriesDonePayload(this.mapId, this.resultSets), layerSetId); + if (isAllDone()) + api.event.emit(GetLegendsPayload.createAllQueriesDonePayload(`${this.mapId}/${layerSetId}`, this.resultSets)); else queryUndefinedLegend(); } } }, mapId ); + + queryUndefinedLegend(); } }, - mapId, - layerSetId + `${mapId}/${layerSetId}` ); } diff --git a/packages/geoview-core/src/ui/drawer/drawer.tsx b/packages/geoview-core/src/ui/drawer/drawer.tsx index fffb3a6cc54..f63c05877ce 100644 --- a/packages/geoview-core/src/ui/drawer/drawer.tsx +++ b/packages/geoview-core/src/ui/drawer/drawer.tsx @@ -99,9 +99,7 @@ export function Drawer(props: TypeDrawerProps): JSX.Element { EVENT_NAMES.DRAWER.EVENT_DRAWER_OPEN_CLOSE, (payload) => { if (payloadIsABoolean(payload)) { - if (payload.handlerName === mapId) { - setOpen(payload.status); - } + setOpen(payload.status); } }, mapId diff --git a/packages/geoview-core/src/ui/modal/modal.tsx b/packages/geoview-core/src/ui/modal/modal.tsx index 25176e4982d..a328fccdaa9 100644 --- a/packages/geoview-core/src/ui/modal/modal.tsx +++ b/packages/geoview-core/src/ui/modal/modal.tsx @@ -242,7 +242,7 @@ export function Modal(props: TypeDialogProps): JSX.Element { EVENT_NAMES.MODAL.EVENT_MODAL_OPEN, (payload) => { if (payloadIsAModal(payload)) { - if (modalId === payload.modalId && payload.handlerName === mapId) { + if (modalId === payload.modalId) { const modal = api.map(mapId).modal.modals[payload.modalId] as TypeModalProps; // eslint-disable-next-line react-hooks/exhaustive-deps openEvent = true; @@ -259,7 +259,7 @@ export function Modal(props: TypeDialogProps): JSX.Element { EVENT_NAMES.MODAL.EVENT_MODAL_UPDATE, (payload) => { if (payloadIsAModal(payload)) { - if (modalId === payload.modalId && payload.handlerName === mapId) { + if (modalId === payload.modalId) { const modal = api.map(mapId).modal.modals[payload.modalId] as TypeModalProps; setCreatedModal(ceatedModalJSXReturner(modal)); @@ -274,7 +274,7 @@ export function Modal(props: TypeDialogProps): JSX.Element { EVENT_NAMES.MODAL.EVENT_MODAL_CLOSE, (payload) => { if (payloadIsAModal(payload)) { - if (modalId === payload.modalId && payload.handlerName === mapId) { + if (modalId === payload.modalId) { if (!payload.open) openEvent = false; setCreatedModal(); } diff --git a/packages/geoview-core/src/ui/panel/panel-api.ts b/packages/geoview-core/src/ui/panel/panel-api.ts index a8885100533..71ba91079a9 100644 --- a/packages/geoview-core/src/ui/panel/panel-api.ts +++ b/packages/geoview-core/src/ui/panel/panel-api.ts @@ -74,8 +74,7 @@ export class PanelApi { this.closeAll(); api.event.emit( - PanelPayload.withButtonIdAndType(EVENT_NAMES.PANEL.EVENT_PANEL_OPEN, this.mapId, this.buttonId, this.type!), - this.buttonId + PanelPayload.withButtonIdAndType(EVENT_NAMES.PANEL.EVENT_PANEL_OPEN, `${this.mapId}/${this.buttonId}`, this.buttonId, this.type!) ); }; @@ -121,8 +120,7 @@ export class PanelApi { this.status = false; api.event.emit( - PanelPayload.withButtonIdAndType(EVENT_NAMES.PANEL.EVENT_PANEL_CLOSE, this.mapId, this.buttonId, this.type!), - this.buttonId + PanelPayload.withButtonIdAndType(EVENT_NAMES.PANEL.EVENT_PANEL_CLOSE, `${this.mapId}/${this.buttonId}`, this.buttonId, this.type!) ); }; @@ -130,8 +128,7 @@ export class PanelApi { this.status = false; api.event.emit( - PanelPayload.withButtonIdAndType(EVENT_NAMES.PANEL.EVENT_PANEL_CLOSE_ALL, this.mapId, this.buttonId, this.type!), - this.buttonId + PanelPayload.withButtonIdAndType(EVENT_NAMES.PANEL.EVENT_PANEL_CLOSE_ALL, `${this.mapId}/${this.buttonId}`, this.buttonId, this.type!) ); }; @@ -157,8 +154,12 @@ export class PanelApi { action, }; api.event.emit( - PanelPayload.withButtonIdAndActionButton(EVENT_NAMES.PANEL.EVENT_PANEL_ADD_ACTION, this.mapId, this.buttonId, actionButton), - this.buttonId + PanelPayload.withButtonIdAndActionButton( + EVENT_NAMES.PANEL.EVENT_PANEL_ADD_ACTION, + `${this.mapId}/${this.buttonId}`, + this.buttonId, + actionButton + ) ); return this; @@ -188,8 +189,12 @@ export class PanelApi { this.content = content; api.event.emit( - PanelPayload.withButtonIdAndContent(EVENT_NAMES.PANEL.EVENT_PANEL_CHANGE_CONTENT, this.mapId, this.buttonId, content), - this.buttonId + PanelPayload.withButtonIdAndContent( + EVENT_NAMES.PANEL.EVENT_PANEL_CHANGE_CONTENT, + `${this.mapId}/${this.buttonId}`, + this.buttonId, + content + ) ); return this; @@ -206,8 +211,12 @@ export class PanelApi { actionButtonId: `${this.buttonId}_${actionButtonId}`, }; api.event.emit( - PanelPayload.withButtonIdAndActionButton(EVENT_NAMES.PANEL.EVENT_PANEL_REMOVE_ACTION, this.mapId, this.buttonId, actionButton), - this.buttonId + PanelPayload.withButtonIdAndActionButton( + EVENT_NAMES.PANEL.EVENT_PANEL_REMOVE_ACTION, + `${this.mapId}/${this.buttonId}`, + this.buttonId, + actionButton + ) ); return this; diff --git a/packages/geoview-core/src/ui/panel/panel.tsx b/packages/geoview-core/src/ui/panel/panel.tsx index fd9445cbeb9..e8c3d12a0f2 100644 --- a/packages/geoview-core/src/ui/panel/panel.tsx +++ b/packages/geoview-core/src/ui/panel/panel.tsx @@ -165,8 +165,7 @@ export function Panel(props: TypePanelAppProps): JSX.Element { } } }, - mapId, - button.id! + `${mapId}/${button.id!}` ); useEffect(() => { @@ -181,44 +180,35 @@ export function Panel(props: TypePanelAppProps): JSX.Element { EVENT_NAMES.PANEL.EVENT_PANEL_OPEN, (payload) => { if (payloadHasAButtonIdAndType(payload)) { - if (payload.buttonId === button.id! && payload.handlerName === mapId) { - // set focus on close button on panel open - setPanelStatus(true); + // set focus on close button on panel open + setPanelStatus(true); - if (closeBtnRef && closeBtnRef.current) { - (closeBtnRef.current as HTMLElement).focus(); - } + if (closeBtnRef && closeBtnRef.current) { + (closeBtnRef.current as HTMLElement).focus(); } } }, - mapId, - button.id! + `${mapId}/${button.id!}` ); api.event.on( EVENT_NAMES.PANEL.EVENT_PANEL_CLOSE, (payload) => { if (payloadHasAButtonIdAndType(payload)) { - if (payload.buttonId === button.id! && payload.handlerName === mapId) { - closePanel(); - } + closePanel(); } }, - mapId, - button.id! + `${mapId}/${button.id!}` ); api.event.on( EVENT_NAMES.PANEL.EVENT_PANEL_CLOSE_ALL, (payload) => { if (payloadHasAButtonIdAndType(payload)) { - if (payload.handlerName === mapId) { - setPanelStatus(false); - } + setPanelStatus(false); } }, - mapId, - button.id! + `${mapId}/${button.id!}` ); // listen to add action button event @@ -255,8 +245,7 @@ export function Panel(props: TypePanelAppProps): JSX.Element { } } }, - mapId, - button.id! + `${mapId}/${button.id!}` ); // listen to remove action button event @@ -273,17 +262,16 @@ export function Panel(props: TypePanelAppProps): JSX.Element { } } }, - mapId, - button.id! + `${mapId}/${button.id!}` ); return () => { - api.event.off(EVENT_NAMES.PANEL.EVENT_PANEL_OPEN, mapId, button.id!); - api.event.off(EVENT_NAMES.PANEL.EVENT_PANEL_CLOSE, mapId, button.id!); - api.event.off(EVENT_NAMES.PANEL.EVENT_PANEL_CLOSE_ALL, mapId, button.id!); - api.event.off(EVENT_NAMES.PANEL.EVENT_PANEL_ADD_ACTION, mapId, button.id!); - api.event.off(EVENT_NAMES.PANEL.EVENT_PANEL_REMOVE_ACTION, mapId, button.id!); - api.event.off(EVENT_NAMES.PANEL.EVENT_PANEL_CHANGE_CONTENT, mapId, button.id!); + api.event.off(EVENT_NAMES.PANEL.EVENT_PANEL_OPEN, `${mapId}/${button.id!}`); + api.event.off(EVENT_NAMES.PANEL.EVENT_PANEL_CLOSE, `${mapId}/${button.id!}`); + api.event.off(EVENT_NAMES.PANEL.EVENT_PANEL_CLOSE_ALL, `${mapId}/${button.id!}`); + api.event.off(EVENT_NAMES.PANEL.EVENT_PANEL_ADD_ACTION, `${mapId}/${button.id!}`); + api.event.off(EVENT_NAMES.PANEL.EVENT_PANEL_REMOVE_ACTION, `${mapId}/${button.id!}`); + api.event.off(EVENT_NAMES.PANEL.EVENT_PANEL_CHANGE_CONTENT, `${mapId}/${button.id!}`); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/packages/geoview-core/src/ui/slider/slider.tsx b/packages/geoview-core/src/ui/slider/slider.tsx index 99cede6c654..c7a2c7ff03a 100644 --- a/packages/geoview-core/src/ui/slider/slider.tsx +++ b/packages/geoview-core/src/ui/slider/slider.tsx @@ -118,7 +118,7 @@ export function Slider(props: TypeSliderProps): JSX.Element { }; // emit the slider values change event to the api - api.event.emit(sliderPayload(EVENT_NAMES.SLIDER.EVENT_SLIDER_CHANGE, null, sliderValues), properties.sliderId); + api.event.emit(sliderPayload(EVENT_NAMES.SLIDER.EVENT_SLIDER_CHANGE, properties.sliderId, sliderValues)); }; // remove overlapping labels @@ -163,7 +163,7 @@ export function Slider(props: TypeSliderProps): JSX.Element { value, activeThumb, }; - api.event.emit(sliderPayload(EVENT_NAMES.SLIDER.EVENT_SLIDER_CHANGE, null, sliderValues), properties.sliderId); + api.event.emit(sliderPayload(EVENT_NAMES.SLIDER.EVENT_SLIDER_CHANGE, properties.sliderId, sliderValues)); } }, properties.id @@ -186,7 +186,7 @@ export function Slider(props: TypeSliderProps): JSX.Element { value: payload.sliderValues.value, activeThumb, }; - api.event.emit(sliderPayload(EVENT_NAMES.SLIDER.EVENT_SLIDER_CHANGE, null, sliderValues), properties.sliderId); + api.event.emit(sliderPayload(EVENT_NAMES.SLIDER.EVENT_SLIDER_CHANGE, properties.sliderId, sliderValues)); } }, properties.id diff --git a/packages/geoview-details-panel/src/details-item.tsx b/packages/geoview-details-panel/src/details-item.tsx index 0d6061819bf..01436c51462 100644 --- a/packages/geoview-details-panel/src/details-item.tsx +++ b/packages/geoview-details-panel/src/details-item.tsx @@ -7,7 +7,6 @@ import { markerDefinitionPayload, payloadIsAllQueriesDone, TypeArrayOfLayerData, - TypeJsonObject, getLocalizedValue, Coordinate, } from 'geoview-core'; @@ -33,7 +32,7 @@ export function DetailsItem({ mapId, buttonId }: Props): JSX.Element { const [details, setDetails] = useState([]); // eslint-disable-next-line @typescript-eslint/ban-types const [list, setList] = useState(); - const [latLng, setLatLng] = useState([]); + const [LngLat, setLngLat] = useState([]); const [handlerName, setHandlerName] = useState(null); const panel = api.map(mapId).appBarButtons.getAppBarButtonPanelById(buttonId === undefined ? '' : buttonId)?.panel; @@ -64,8 +63,7 @@ export function DetailsItem({ mapId, buttonId }: Props): JSX.Element { setDetails([]); } }, - mapId, - `${mapId}-DetailsAPI` + `${mapId}/${mapId}-DetailsAPI` ); // get click info. api.event.on( @@ -74,9 +72,9 @@ export function DetailsItem({ mapId, buttonId }: Props): JSX.Element { if (payloadIsAMapSingleClick(payload)) { const { coordinates } = payload; setHandlerName(payload.handlerName); - setLatLng(coordinates.lnglat); + setLngLat(coordinates.lnglat); } else { - setLatLng([]); + setLngLat([]); } }, mapId @@ -93,13 +91,13 @@ export function DetailsItem({ mapId, buttonId }: Props): JSX.Element { setList( api .map(mapId) - .details.createDetails(mapId, details, { mapId, location: latLng, backgroundStyle: 'dark', singleColumn: true, handlerName }) + .details.createDetails(mapId, details, { mapId, location: LngLat, backgroundStyle: 'dark', singleColumn: true, handlerName }) ); setTimeout(() => { - api.event.emit(markerDefinitionPayload(api.eventNames.MARKER_ICON.EVENT_MARKER_ICON_SHOW, handlerName, latLng, {} as TypeJsonObject)); + api.event.emit(markerDefinitionPayload(api.eventNames.MARKER_ICON.EVENT_MARKER_ICON_SHOW, handlerName, LngLat)); }, 800); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [details, latLng]); + }, [details, LngLat]); return
{list}
; } diff --git a/packages/geoview-footer-panel/src/details-item.tsx b/packages/geoview-footer-panel/src/details-item.tsx index b4afadca415..96dc4b05649 100644 --- a/packages/geoview-footer-panel/src/details-item.tsx +++ b/packages/geoview-footer-panel/src/details-item.tsx @@ -7,7 +7,6 @@ import { markerDefinitionPayload, payloadIsAllQueriesDone, TypeArrayOfLayerData, - TypeJsonObject, getLocalizedValue, Coordinate, } from 'geoview-core'; @@ -32,7 +31,7 @@ export function DetailsItem({ mapId }: Props): JSX.Element { const [details, setDetails] = useState([]); // eslint-disable-next-line @typescript-eslint/ban-types const [list, setList] = useState(); - const [latLng, setLatLng] = useState([]); + const [lngLat, setLngLat] = useState([]); const [handlerName, setHandlerName] = useState(null); useEffect(() => { @@ -59,8 +58,7 @@ export function DetailsItem({ mapId }: Props): JSX.Element { setDetails([]); } }, - mapId, - `${mapId}-DetailsAPI` + `${mapId}/${mapId}-DetailsAPI` ); api.event.on( api.eventNames.MAP.EVENT_MAP_SINGLE_CLICK, @@ -68,17 +66,12 @@ export function DetailsItem({ mapId }: Props): JSX.Element { if (payloadIsAMapSingleClick(payload)) { const { coordinates } = payload; setHandlerName(payload.handlerName); - setLatLng(coordinates.lnglat); + setLngLat(coordinates.lnglat); api.event.emit( - markerDefinitionPayload( - api.eventNames.MARKER_ICON.EVENT_MARKER_ICON_SHOW, - payload.handlerName, - coordinates.lnglat, - {} as TypeJsonObject - ) + markerDefinitionPayload(api.eventNames.MARKER_ICON.EVENT_MARKER_ICON_SHOW, payload.handlerName, coordinates.lnglat) ); } else { - setLatLng([]); + setLngLat([]); } }, mapId @@ -91,12 +84,12 @@ export function DetailsItem({ mapId }: Props): JSX.Element { }, []); useEffect(() => { - setList(api.map(mapId).details.createDetails(mapId, details, { mapId, location: latLng, handlerName })); + setList(api.map(mapId).details.createDetails(mapId, details, { mapId, location: lngLat, handlerName })); setTimeout(() => { - api.event.emit(markerDefinitionPayload(api.eventNames.MARKER_ICON.EVENT_MARKER_ICON_SHOW, handlerName, latLng, {} as TypeJsonObject)); + api.event.emit(markerDefinitionPayload(api.eventNames.MARKER_ICON.EVENT_MARKER_ICON_SHOW, handlerName, lngLat)); }, 1800); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [details, latLng]); + }, [details, lngLat]); return
{list}
; } diff --git a/packages/geoview-footer-panel/src/index.tsx b/packages/geoview-footer-panel/src/index.tsx index 964bc3d41fe..79f96652ca3 100644 --- a/packages/geoview-footer-panel/src/index.tsx +++ b/packages/geoview-footer-panel/src/index.tsx @@ -130,8 +130,7 @@ class FooterPanelPlugin extends AbstractPlugin { } } }, - mapId, - `${mapId}-DetailsAPI` + `${mapId}/${mapId}-DetailsAPI` ); } diff --git a/packages/geoview-footer-panel/src/legend-item.tsx b/packages/geoview-footer-panel/src/legend-item.tsx index d50f8a2a514..56bf062e3f9 100644 --- a/packages/geoview-footer-panel/src/legend-item.tsx +++ b/packages/geoview-footer-panel/src/legend-item.tsx @@ -54,10 +54,9 @@ export function LegendItem({ mapId }: Props): JSX.Element { api.eventNames.LAYER.EVENT_LAYER_ADDED, () => { addLayer(payload.layerConfig.geoviewLayerId); - api.event.off(api.eventNames.LAYER.EVENT_LAYER_ADDED, mapId, payload.layerConfig.geoviewLayerId); + api.event.off(api.eventNames.LAYER.EVENT_LAYER_ADDED, `${mapId}/${payload.layerConfig.geoviewLayerId}`); }, - mapId, - payload.layerConfig.geoviewLayerId + `${mapId}/${payload.layerConfig.geoviewLayerId}` ); } }, diff --git a/packages/geoview-layers-panel/src/layer-stepper.tsx b/packages/geoview-layers-panel/src/layer-stepper.tsx index 536c434a6ac..1431beff8e0 100644 --- a/packages/geoview-layers-panel/src/layer-stepper.tsx +++ b/packages/geoview-layers-panel/src/layer-stepper.tsx @@ -634,8 +634,7 @@ function LayerStepper({ mapId, setAddLayerVisible }: Props): JSX.Element { setIsLoading(false); setAddLayerVisible(false); }, - mapId, - geoviewLayerId + `${mapId}/${geoviewLayerId}` ); let valid = true; diff --git a/packages/geoview-layers-panel/src/panel-content.tsx b/packages/geoview-layers-panel/src/panel-content.tsx index 0df9e1dd05f..2e37eaa9154 100644 --- a/packages/geoview-layers-panel/src/panel-content.tsx +++ b/packages/geoview-layers-panel/src/panel-content.tsx @@ -128,10 +128,9 @@ function PanelContent(props: TypePanelContentProps): JSX.Element { api.eventNames.LAYER.EVENT_LAYER_ADDED, () => { addLayer(payload.layerConfig.geoviewLayerId); - api.event.off(api.eventNames.LAYER.EVENT_LAYER_ADDED, mapId, payload.layerConfig.geoviewLayerId); + api.event.off(api.eventNames.LAYER.EVENT_LAYER_ADDED, `${mapId}/${payload.layerConfig.geoviewLayerId}`); }, - mapId, - payload.layerConfig.geoviewLayerId + `${mapId}/${payload.layerConfig.geoviewLayerId}` ); } }, @@ -155,12 +154,11 @@ function PanelContent(props: TypePanelContentProps): JSX.Element { () => { setAddLayerVisible(false); }, - mapId, - buttonPanel.buttonPanelId + `${mapId}/${buttonPanel.buttonPanelId}` ); return () => { - api.event.off(api.eventNames.PANEL.EVENT_PANEL_CLOSE, mapId, buttonPanel.buttonPanelId); + api.event.off(api.eventNames.PANEL.EVENT_PANEL_CLOSE, `${mapId}/${buttonPanel.buttonPanelId}`); }; }, [api, buttonPanel.buttonPanelId, mapId]);