Skip to content

MIP - AutoRefresh Plugin - Automatic Layer Refresh for MapStore #11887 - #12454

Draft
endorria wants to merge 20 commits into
geosolutions-it:masterfrom
endorria:plugin-autorefresh
Draft

endorria wants to merge 20 commits into
geosolutions-it:masterfrom
endorria:plugin-autorefresh

Conversation

@endorria

@endorria endorria commented Jun 3, 2026

Copy link
Copy Markdown

Description

This is an early stage version of the plugin to validate the approach.
Mainly works with Openlayers on few layer types (the ones who implements the refresh's method).

Footer Plugin next to the CRS Plugin.

Please check if the PR fulfills these requirements

What kind of change does this PR introduce? (check one with "x", remove the others)

  • Feature

Issue

What is the current behavior?

Feature not available

What is the new behavior?

#11887

Breaking change

Does this PR introduce a breaking change? (check one with "x", remove the other)

  • Yes, and I documented them in migration notes
  • No

@cla-bot

cla-bot Bot commented Jun 3, 2026

Copy link
Copy Markdown

In order to contribute to the MapStore project, the CLA (Contributor License agreement) should be sent signed to GeoSolutions. Please consult contributing rules at: https://github.com/geosolutions-it/MapStore2/wiki/Contributing-to-MapStore#contributing-code

@tdipisa

tdipisa commented Jun 10, 2026

Copy link
Copy Markdown
Member

Thank you very much for your contribution to the MapStore codebase. We will review it as soon as possible. Many thanks.

@tdipisa

tdipisa commented Jun 10, 2026

Copy link
Copy Markdown
Member

Thank you so much for this!
As expected by Community Bylaws, we need the CLA signed to accept your contribution. Can you please provide it? Thank you again.

@endorria

Copy link
Copy Markdown
Author

Thank you so much for this! As expected by Community Bylaws, we need the CLA signed to accept your contribution. Can you please provide it? Thank you again.

I work for the company (my GH's profile is up to date), arx iT, we already send the CLA. We can update it if necessary.

@jlo-arxit

Copy link
Copy Markdown
Contributor

Hi @tdipisa
I confirm Pull Request author (Marc M.) is listed in the CLA signed by arx iT
Is it ok for you ?

@tdipisa

tdipisa commented Jul 1, 2026

Copy link
Copy Markdown
Member

Hi @tdipisa I confirm Pull Request author (Marc M.) is listed in the CLA signed by arx iT Is it ok for you ?

Yes sure. I've seen it. I'm sorry, it seems we missed to include it. Pending PR here.

@tdipisa
tdipisa requested a review from offtherailz July 6, 2026 15:08
@tdipisa tdipisa linked an issue Jul 14, 2026 that may be closed by this pull request
5 tasks
@tdipisa
tdipisa requested review from MV88 and removed request for offtherailz July 14, 2026 13:18

@MV88 MV88 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

First of all thanks for this contribution Here a few feedbacks about this PR:

Handling the auto-refresh

We noticed that you are introducing a lot of logic in order to handle the refresh inside the core components like:

  • web/client/components/map/openlayers/* and other mapping library
  • web/client/utils/openlayers/* and other mapping library

we think that this might be too much and we propose a different direction you can take:

  • there is already for WFS and WMS a mechanism to trigger an update which is called refreshLayerVersion

export function refreshLayerVersion(layer, version) {
return {
type: CHANGE_LAYER_PROPERTIES,
layer,
newProperties: {
_v_: version || new Date().getTime()
}
};
}

that changes a param _v_ dedicated to "refresh" recreate the layer instance hence updating it. So it is enough to trigger this action instead of the AUTOREFRESH_TICK one. This is also a more abstracted way to handle refresh since layers doesn't need to be aware of ticks and if another plugin needs to trigger a refresh we can reuse refreshLayerVersion logic, so instead of having a refresh method inside layers you can reuse the logic and add the missing logic on _v_ parameter in the update method, therefore a cleanup here is needed, implementing this logic in the remaining layers.

All the ticks data and logic probably can be removed using derived data from layers object, triggering the action in the epic itself.

Manage changes and different intervals

In order to manage visibility or param changes and activate/deactivate the epic you need to recollect the possible trigger actions that require a reset,
We suggest a set of TRIGGERS actions, and a set of timers that triggers the refresh action on each timeout.
When one Trigger changes one of the intervals, or one of the layers arrives/goes out, the timer is added/recreated/destroyed.

Here a sample code:

const TRIGGERS = [
    CHANGE_LAYER_PROPERTIES,   // visibility toggle
    CHANGE_LAYER_PARAMS,
    UPDATE_NODE,               // autoRefreshInterval update
    ADD_LAYER,
    REMOVE_NODE,
    MAP_CONFIG_LOADED          // init
    AUTOREFRESH_START, // initiate the flow
    AUTOREFRESH_STOP, // stops the flow
    AUTOREFRESH_PAUSE // ADD PAUSE ACTION THAT blocks execution if when feature editor is in edit mode, this action will be triggered by the refresh plugin itself that will listen for changes to the featuregrid.mode property and trigger it when that the mode value changes like a toggle action, the pause value can be stored inside autorefresh reducer  
];

const layersAutoRefreshEpic = (action$, store) => {
    const getLayersMap = () =>
        isPaused(store.getState()) || !isActiveRefresh(store.getState()) ? {} : refreshableLayers(store.getState())
            .reduce((acc, l) => ({ ...acc, [l.id]: l.interval }), {});
    // this stream should emit refreshable layers map on every possible change, but not when paused or refesh is not active (toggled on)
    const active$ = action$.ofType(...TRIGGERS)
        .map(getLayersMap)
        .startWith(getLayersMap())
        .distinctUntilChanged(isEqual) // if some of the layer interval changed (or list change) triggers
        .publishReplay(1).refCount(); // activate multicast

    return active$
        .mergeMap(m => Object.keys(m).map(id => ({ id, interval: m[id] })))
        .groupBy(d => d.id)
        // this merges N stream actions, 1 for each layer that needs a refresh, each one with its own timer
        .mergeMap(group$ => {
            const id = group$.key;
            const removed$ = active$.filter(m => !m[id]);   // this intercepts when a layer is not in the map anymore
            return group$
                .map(d => d.interval)
                .distinctUntilChanged()                     // if change interval, rebuild the timer
                .switchMap(interval =>
                    Rx.Observable.timer(interval, interval)
                        .map(() => refreshLayerVersion(id, Date().getTime()))
                )
                .takeUntil(removed$);                        // teardown of the single timer
        });
};

refreshableLayersSelector will exclude layers being edited from attribute table or from widget wizard

State handling

Considering that autorefreshInterval is stored within layers object, you don't need availableLayers: {}, or activeLayers: {} in reducer. with a dedicated selector you can fetch layers with autorefreshInterval defined from the layers state and then filter the array of layers with only the one with visibility set to true, so they can trigger the refreshLayerVersion action. Try to reuse the layers selectors

User Interface

It is not clear from how the settings panel should look like, for example how to handle multiple layers inside a single widget? Probably not all layers inside a widget must refresh? When a layer is selected to be refreshed, (in layers state) it will receive the autorefreshInterval property. Considering that widgets have their own state, then also this state should receive the autorefreshInterval param for all the layers instances. If unselected in all places (widgets & layers state) it will remove the autorefreshInterval param from layer object. it would be nice to visually know if layer is coming from the map or from which widget

Map Widgets

The refreshLayerVersion action trigger an update of _v_ and by keeping track of this change we can retrigger the update on widgets. By monitoring changes to _v_ param in this part we can trigger a refresh on the widget

Dashboards

Regarding dashboards we have some concerns on how to handle connections between widgets(dependencies managemente) even after introducing the logic to listen for changes on _v_ param it might not be enough. Therefore we suggest to create a dedicated issue to better investigate this part after the others are done.

import Rx from 'rxjs';
import { isNumber } from 'lodash';

import propsStreamFactory from '../../misc/enhancers/propsStreamFactory';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

avoid using recompose for the new development as it is going to be removed at all in the future


const AutoRefreshForm = ({
defaultRefreshInterval,
minRefreshInterval,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this is not named minimumRefreshInterval

@cla-bot cla-bot Bot added the CLA Ready label Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AutoRefresh Plugin

5 participants