Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
1fe1409
feat(plugin): initialization of autorefresh's plugin
endorria Jan 22, 2026
f3139ac
Merge branch 'master' into plugin-autorefresh
endorria Apr 29, 2026
20c3c65
feat(AutoRefresh): components and logic
endorria Apr 30, 2026
3341c9c
feat(AutoRefresh): Layers should detect the change on attribute autor…
endorria Apr 30, 2026
9102be3
feat(autorefresh): WFS, WMS updated when autorefresh on / off
endorria May 1, 2026
e2608ec
feat(autorefresh): add hasAutoRefreshCapability helper in LayerUtils
endorria May 5, 2026
2abb5ac
feat(autorefresh): update Store to handle 3D switch, add / remove Lay…
endorria May 5, 2026
58b4779
feat(autorefresh): simplify plugin container
endorria May 5, 2026
34fde65
feat(autorefresh): creation of refresh function for openlayers Layers
endorria May 5, 2026
0cc1c85
feat(autorefresh): add prop autorefreshTicks to Layer in map plugin
endorria May 5, 2026
c08b8c6
fix: refresh layer only if visible on Cesium Leaflet Openlayers
endorria May 27, 2026
e316135
fix: Autorefresh do not save local store on export
endorria May 27, 2026
8af7255
feat: handle autorefresh on wpsCounter widget
endorria May 27, 2026
780c998
Merge branch 'master' into plugin-autorefresh
endorria May 27, 2026
ca57dad
fix: update UI to add last updated
endorria May 27, 2026
f304fdf
Merge branch 'master' into plugin-autorefresh
endorria Jun 3, 2026
d3b6604
fix: old code to remove for openlayers
endorria Jun 3, 2026
ac03640
chore: some file are missing header comment and comments are missing
endorria Jun 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion web/client/components/map/cesium/Layer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import React from 'react';

import Layers from '../../../utils/cesium/Layers';
import PropTypes from 'prop-types';
import { round, isNil, castArray, isFunction } from 'lodash';
import { round, isNil, castArray, isFunction, isNumber } from 'lodash';
import { getResolutions } from '../../../utils/MapUtils';
import axios from '../../../libs/ajax';
import { getProxyCacheByUrl } from '../../../utils/ProxyUtils';
Expand Down Expand Up @@ -39,6 +39,7 @@ class CesiumLayer extends React.Component {
// in particular for detached layers (eg. Vector, WFS, 3D Tiles, ...)
const visibility = this.getVisibilityOption(this.props);
this._isMounted = true;
this.autorefreshTick = -1;
this.createLayer(this.props.type, { ...this.props.options, visibility }, this.props.position, this.props.map, this.props.securityToken);
if (this.props.options && this.layer && visibility) {
this.addLayer(this.props);
Expand Down Expand Up @@ -82,6 +83,10 @@ class CesiumLayer extends React.Component {
}
}
this.updateLayer(newProps, this.props);

if (newProps.autorefreshTicks) {
this.tryAutorefresh(newProps);
}
}

componentWillUnmount() {
Expand Down Expand Up @@ -473,6 +478,17 @@ class CesiumLayer extends React.Component {
}
this.props.map.scene.requestRender();
};

tryAutorefresh = (newProps) => {
const layerId = newProps.options.id;
const autorefreshTicks = newProps.autorefreshTicks;
const visible = this.getVisibilityOption(newProps);

if (visible && isNumber(autorefreshTicks[layerId]) && this.autorefreshTick < autorefreshTicks[layerId]) {
this.autorefreshTick = autorefreshTicks[layerId];
Layers.refreshLayer(this.props.type, this.layer);
}
};
}

export default CesiumLayer;
17 changes: 17 additions & 0 deletions web/client/components/map/leaflet/Layer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import PropTypes from 'prop-types';
import Layers from '../../../utils/leaflet/Layers';
import isEqual from 'lodash/isEqual';
import isNil from 'lodash/isNil';
import { isNumber } from 'lodash';
class LeafletLayer extends React.Component {
static propTypes = {
map: PropTypes.object,
Expand All @@ -32,6 +33,7 @@ class LeafletLayer extends React.Component {

componentDidMount() {
this.valid = true;
this.autorefreshTick = -1;
this.createLayer(
this.props.type,
this.props.options,
Expand All @@ -55,6 +57,10 @@ class LeafletLayer extends React.Component {
this.updateZIndex(newProps.position);
}
this.updateLayer(newProps, this.props);

if (newProps.autorefreshTicks) {
this.tryAutorefresh(newProps);
}
}

shouldComponentUpdate(newProps) {
Expand Down Expand Up @@ -221,6 +227,17 @@ class LeafletLayer extends React.Component {
}
return false;
};

tryAutorefresh = (newProps) => {
const layerId = newProps.options.id;
const autorefreshTicks = newProps.autorefreshTicks;
const visible = this.getVisibilityOption(newProps);

if (visible && isNumber(autorefreshTicks[layerId]) && this.autorefreshTick < autorefreshTicks[layerId]) {
this.autorefreshTick = autorefreshTicks[layerId];
Layers.refreshLayer(this.props.type, this.layer);
}
};
}

export default LeafletLayer;
23 changes: 13 additions & 10 deletions web/client/components/map/openlayers/Layer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@ export default class OpenlayersLayer extends React.Component {
onCreationError: () => {},
onWarning: () => {},
srs: "EPSG:3857"

};

componentDidMount() {
this.valid = true;
this.tilestoload = 0;
this.imagestoload = 0;
this.autorefreshTick = -1;
this.createLayer(
this.props.type,
this.props.options,
Expand All @@ -75,6 +77,9 @@ export default class OpenlayersLayer extends React.Component {
if (this.props.options) {
this.updateLayer(newProps, this.props);
}
if (newProps.autorefreshTicks) {
this.tryAutorefresh(newProps.options.id, newProps.autorefreshTicks);
}
}

componentWillUnmount() {
Expand All @@ -93,9 +98,7 @@ export default class OpenlayersLayer extends React.Component {
this.props.map.removeLayer(this.layer);
}
}
if (this.refreshTimer) {
clearInterval(this.refreshTimer);
}

Layers.removeLayer(this.props.type, this.props.options, this.props.map, this.props.mapId, this.layer);
}

Expand Down Expand Up @@ -344,13 +347,6 @@ export default class OpenlayersLayer extends React.Component {
this.props.onLayerLoad(options.id, {error: true});
}
});

if (options.refresh) {
let counter = 0;
this.refreshTimer = setInterval(() => {
this.layer.getSource().updateParams(Object.assign({}, options.params, {_refreshCounter: counter++}));
}, options.refresh);
}
}
};

Expand All @@ -359,4 +355,11 @@ export default class OpenlayersLayer extends React.Component {
this.valid = valid;
return valid;
};

tryAutorefresh = (layerId, autorefreshTicks) => {
if (this.layer?.getVisible() && isNumber(autorefreshTicks[layerId]) && this.autorefreshTick < autorefreshTicks[layerId]) {
this.autorefreshTick = autorefreshTicks[layerId];
Layers.refreshLayer(this.props.type, this.layer);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ Layers.registerType('arcgis-feature', {
layer.setMaxResolution(options.maxResolution === undefined ? Infinity : options.maxResolution);
}
},
refresh: (layer) => {
const source = layer.getSource();
if (source) {
source.refresh();
}
},
render: () => {
return null;
}
Expand Down
10 changes: 10 additions & 0 deletions web/client/components/map/openlayers/plugins/ArcGISLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ registerType('arcgis', {
layer.getSource().setTileLoadFunction(tileLoadFunction(newOptions));
}
},
refresh: (layer) => {
const source = layer.getSource();
if (source) {
source.updateParams(
Object.assign({}, source.getParams(), {
_refreshCounter: Date.now()
})
);
}
},
render: () => {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,5 +241,11 @@ Layers.registerType(FGB_LAYER_TYPE, {
}

return null;
},
refresh: (layer) => {
const source = layer.getSource();
if (source) {
source.refresh();
}
}
});
10 changes: 10 additions & 0 deletions web/client/components/map/openlayers/plugins/TMSLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,16 @@ Layers.registerType('tms', {
|| !isEqual(oldOptions.requestRuleRefreshHash, newOptions.requestRuleRefreshHash)) {
layer.getSource().setTileLoadFunction(tileLoadFunction(newOptions));
}
},
refresh: (layer) => {
const source = layer.getSource();
if (source) {
// Looks like the cache of the browser is not cleared with source.clear() or source.refresh().
// So, if the map is static, the same tiles are requested,
// hence the browser doesn't request new tiles to the server (no https request made).
// if you disable caching in the browser's dev tools, the new tiles are requested to the server.
source.refresh();
}
}
});

10 changes: 10 additions & 0 deletions web/client/components/map/openlayers/plugins/TileProviderLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ Layers.registerType('tileprovider', {
|| !isEqual(oldOptions.requestRuleRefreshHash, newOptions.requestRuleRefreshHash)) {
layer.getSource().setTileLoadFunction(tileLoadFunction(newOptions));
}
},
refresh: (layer) => {
const source = layer.getSource();
if (source) {
// Looks like the cache of the browser is not cleared with source.clear() or source.refresh().
// So, if the map is static, the same tiles are requested,
// hence the browser doesn't request new tiles to the server (no https request made).
// if you disable caching in the browser's dev tools, the new tiles are requested to the server.
source.refresh();
}
}
});

6 changes: 6 additions & 0 deletions web/client/components/map/openlayers/plugins/WFSLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,12 @@ Layers.registerType('wfs', {
layer.setMaxResolution(options.maxResolution === undefined ? Infinity : options.maxResolution);
}
},
refresh: (layer) => {
const source = layer.getSource();
if (source) {
source.refresh();
}
},
render: () => {
return null;
}
Expand Down
21 changes: 21 additions & 0 deletions web/client/components/map/openlayers/plugins/WMSLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -305,5 +305,26 @@ Layers.registerType('wms', {
}
}
return null;
},
refresh: (layer) => {
// *source.refresh() doesn't trigger an HTTPS request to reload tiles
// *source.updateParams() with a dummy parameter forces the source to reload tiles
const wmsSource = layer.get('wmsSource');
if (wmsSource) {
wmsSource.updateParams(
Object.assign({}, wmsSource.getParams(), {
_refreshCounter: Date.now()
})
);
}

const vectorSource = layer.getSource();
if (vectorSource) {
vectorSource.updateParams(
Object.assign({}, vectorSource.getParams(), {
_refreshCounter: Date.now()
})
);
}
}
});
13 changes: 12 additions & 1 deletion web/client/components/map/openlayers/plugins/WMTSLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,17 @@ const updateLayer = (layer, newOptions, oldOptions) => {
return null;
};

const refreshLayer = (layer) => {
const source = layer.getSource();
if (source) {
// Looks like the cache of the browser is not cleared with source.clear() or source.refresh().
// So, if the map is static, the same tiles are requested,
// hence the browser doesn't request new tiles to the server (no https request made).
// if you disable caching in the browser's dev tools, the new tiles are requested to the server.
source.refresh();
}
};

const hasSRS = (srs, layer) => {
const { tileMatrixSetName, tileMatrixSet } = WMTSUtils.getTileMatrix(layer, srs);
if (tileMatrixSet) {
Expand All @@ -214,4 +225,4 @@ const compatibleLayer = layer =>
head(CoordinatesUtils.getEquivalentSRS(layer.srs || 'EPSG:3857').filter(srs => hasSRS(srs, layer))) ? true : false;


Layers.registerType('wmts', { create: createLayer, update: updateLayer, isCompatible: compatibleLayer });
Layers.registerType('wmts', { create: createLayer, update: updateLayer, refresh: refreshLayer, isCompatible: compatibleLayer });
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2026, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

import { compose, withProps } from 'recompose';
import Rx from 'rxjs';
import { isNumber } from 'lodash';

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

let currentTicks = {};

/**
* Set the property `shouldAutorefresh` to true if the widget should autorefresh based on the autorefreshTicks and layer.id.
* @param {object} props the widget props
* @param {{[prop: string]: number}} props.autorefreshTicks an object with the layer.id as key and the ticks as value
* @param {object} props.layer the layer object where to perform the requests, used to get the layer.id to check against the autorefreshTicks
* @returns {Rx.Observable<{shouldAutorefresh: boolean}>} an observable that emits an object with the property shouldAutorefresh (boolean)
* @example
* // in a widget component
* const MyWidget = ({shouldAutorefresh}) => {
* useEffect(() => {
* if (shouldAutorefresh) {
* // trigger data fetch
* }
* }, [shouldAutorefresh]);
*/
const dataStreamFactory = ($props) =>
$props
.switchMap(
({autorefreshTicks, layer}) => {
let shouldAutorefresh = false;

if (layer?.id && Object.keys(autorefreshTicks).length && isNumber(autorefreshTicks[layer.id]) &&
currentTicks[layer.id] !== autorefreshTicks[layer.id]) {
currentTicks[layer.id] = autorefreshTicks[layer.id];
shouldAutorefresh = true;
}

return Rx.Observable.of({
shouldAutorefresh
});
}
);

export default compose(
withProps( () => ({
dataStreamFactory
})),
propsStreamFactory
);
23 changes: 17 additions & 6 deletions web/client/components/widgets/enhancers/wpsCounter.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ const sameOptions = (o1 = {}, o2 = {}) =>
import { getWpsUrl } from '../../../utils/LayersUtils';
import { checkMapSyncWithWidgetOfMapType } from '../../../utils/WidgetsUtils';


/**
* Stream of props -> props to retrieve data from WPS aggregate process on params changes.
* Can be used with widgets and charts to auto-update data on property changes.
Expand All @@ -32,20 +31,32 @@ import { checkMapSyncWithWidgetOfMapType } from '../../../utils/WidgetsUtils';
*/
const dataStreamFactory = ($props) =>
$props
.filter(({layer = {}, options, dependencies, mapSync, dependenciesMap, widgets}) => {
.filter(({layer = {}, options, dependencies, mapSync, dependenciesMap, widgets, shouldAutorefresh }) => {
const isValid = layer.name && getWpsUrl(layer) && options && options.aggregateFunction && options.aggregationAttribute;

if (shouldAutorefresh) {
return isValid;
}

// Check if mapSync is enabled (true), dependencyMap has mapSync dependency to Map widget and dependencies.viewport is null or falsy
// If this condition is true, return false to filter out the event.
// This prevents an extra API call from being triggered when the viewport is not available.
if (mapSync && checkMapSyncWithWidgetOfMapType(widgets, dependenciesMap) && !dependencies?.viewport) {
return false;
}
return layer.name && getWpsUrl(layer) && options && options.aggregateFunction && options.aggregationAttribute;
return isValid;
})
.distinctUntilChanged(
({layer = {}, options = {}, filter}, newProps) =>
(newProps.layer && layer.name === newProps.layer.name && layer.loadingError === newProps.layer.loadingError)
({layer = {}, options = {}, filter }, newProps) => {
if (newProps.shouldAutorefresh) {
return false;
}

return (newProps.layer && layer.name === newProps.layer.name && layer.loadingError === newProps.layer.loadingError)
&& sameOptions(options, newProps.options)
&& sameFilter(filter, newProps.filter))
&& sameFilter(filter, newProps.filter);
}
)
.switchMap(
({layer = {}, options, filter, onLoad = () => {}, onLoadError = () => {}}) =>
wpsAggregate(
Expand Down
Loading