From 8708a3422a0116e579cd969cacd7f8178e2b11fa Mon Sep 17 00:00:00 2001 From: Momtchil Momtchev Date: Sun, 2 Feb 2025 21:37:14 +0100 Subject: [PATCH] reduce OpenLayers API calls (#364) * avoid calling OpenLayers when possible * add more debug * ignore the size warnings --- examples/Spinner.tsx | 18 ++++++++++++------ src/REvent.tsx | 5 ++++- src/RMap.tsx | 18 ++++++++++++++++-- src/layer/RLayer.tsx | 9 +++++++-- src/layer/RLayerTile.tsx | 2 ++ webpack.config.ts | 9 +++++++-- 6 files changed, 48 insertions(+), 13 deletions(-) diff --git a/examples/Spinner.tsx b/examples/Spinner.tsx index 909d5422..e2bec6fc 100644 --- a/examples/Spinner.tsx +++ b/examples/Spinner.tsx @@ -5,13 +5,19 @@ import spinnerIcon from './svg/pacman.svg'; import 'ol/ol.css'; export default function Layers(): JSX.Element { - const [loading, setLoading] = React.useState(0); + const [loading, setLoading] = React.useState(true); + const showSpinner = React.useCallback(() => setLoading(true), [setLoading]); + const hideSpinner = React.useCallback(() => setLoading(false), [setLoading]); + const origin = React.useMemo(() => ({center: fromLonLat([2.364, 48.82]), zoom: 4}), []); return ( - - + <> + setLoading((loading) => loading + 1)} - onTileLoadEnd={() => setLoading((loading) => loading - 1)} url='https://{a-c}.tile.opentopomap.org/{z}/{x}/{y}.png' attributions='Kartendaten: © OpenStreetMap-Mitwirkende, SRTM | Kartendarstellung: © OpenTopoMap (CC-BY-SA)' /> @@ -20,6 +26,6 @@ export default function Layers(): JSX.Element { spinner {loading} Loading... - + ); } diff --git a/src/REvent.tsx b/src/REvent.tsx index dc2c269c..c2bbe5d1 100644 --- a/src/REvent.tsx +++ b/src/REvent.tsx @@ -71,7 +71,10 @@ export class RlayersBase extends React.PureComponent { debug('installing handler', this, p, newEvents[p]); const prop = this.getHandlerProp(p); if (!prop) throw new Error('Internal error'); - handlers[p] = (e: unknown) => this.props[prop].call(this, e); + handlers[p] = (e: unknown) => { + debug('handling event', e, this, this.props[prop]); + return this.props[prop].call(this, e); + }; for (const source of eventSources) source.on(p as OLEvent, handlers[p]); this.incrementHandlers(p); } diff --git a/src/RMap.tsx b/src/RMap.tsx index 6980251a..c85cd2e0 100644 --- a/src/RMap.tsx +++ b/src/RMap.tsx @@ -8,6 +8,7 @@ import {ProjectionLike} from 'ol/proj'; import {RContext} from './context'; import {RlayersBase} from './REvent'; +import debug from './debug'; /** Center and zoom level */ export type RView = { @@ -78,6 +79,12 @@ export interface RMapProps extends PropsWithChildren { onRenderComplete?: (this: RMap, e: RenderEvent) => boolean | void; /** Called on every change */ onChange?: (this: RMap, e: BaseEvent) => void; + /** Called when the map starts loading */ + onLoadStart?: (this: RMap, e: MapEvent) => void; + /** Called when the map has completely loaded */ + onLoadEnd?: (this: RMap, e: MapEvent) => void; + /** Generic error handled */ + onError?: (this: RMap, e: BaseEvent) => void; /** A set of properties that can be accessed later by .get()/.getProperties() */ properties?: Record; /** Extent of the map, cannot be dynamically modified @@ -151,7 +158,10 @@ export default class RMap extends RlayersBase> componentDidMount(): void { super.componentDidMount(); - this.ol.setTarget(this.target.current); + if (this.ol.getTarget() !== this.target.current) { + debug('Setting target', this, this.target.current); + this.ol.setTarget(this.target.current); + } } private updateView = (e: MapEvent): void => { @@ -169,9 +179,13 @@ export default class RMap extends RlayersBase> const view = this.ol.getView(); for (const p of ['minZoom', 'maxZoom', 'constrainResolution']) { const m = p.charAt(0).toUpperCase() + p.substring(1); - if (!prevProps || this.props[p] !== prevProps[p]) view['set' + m](this.props[p]); + if (this.props?.[p] !== prevProps?.[p]) { + debug('Setting', this, m, this.props[p]); + view['set' + m](this.props[p]); + } } if (this.props.view) { + debug('Setting view', this, this.props.view); view.setCenter(this.props.view[0].center); if (this.props.view[0].resolution === undefined) view.setZoom(this.props.view[0].zoom); diff --git a/src/layer/RLayer.tsx b/src/layer/RLayer.tsx index 9db3786d..5a1ba5c1 100644 --- a/src/layer/RLayer.tsx +++ b/src/layer/RLayer.tsx @@ -66,10 +66,15 @@ export default class RLayer

extends RlayersBase { protected refresh(prevProps?: RLayerTileProps): void { super.refresh(prevProps); if (prevProps?.tileGrid !== this.props.tileGrid || prevProps?.url !== this.props.url) { + debug('replacing source', this); this.createSource(); this.ol.setSource(this.source); this.attachOldEventHandlers(this.source); diff --git a/webpack.config.ts b/webpack.config.ts index a314e801..37030f5d 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -23,8 +23,13 @@ const webpackConfig = (env): webpack.Configuration => { path: path.join(__dirname, '/docs'), filename: 'bundle.js' }, - // https://github.com/TypeStrong/ts-loader/issues/751 - ignoreWarnings: [{message: /export .* was not found in/}], + ignoreWarnings: [ + // https://github.com/TypeStrong/ts-loader/issues/751 + {message: /export .* was not found in/}, + // OpenLayers + React + rlayers is simply big + {message: /asset size exceeds/}, + {message: /recommended size limit/} + ], module: { rules: [ {