Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 4 additions & 1 deletion src/components/OpenLayersMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import { ToggleSwitch } from './ToggleSwitch';
import { CenterMapFeature } from './CenterMapFeature';
import { AperturesLayer } from './layers/AperturesLayer';
import { LoadingOverlay } from './LoadingOverlay';
import { useTileLoading } from '../hooks/useTileLoading';

export type MapProps = {
mapGroups: MapGroupResponse[];
Expand Down Expand Up @@ -110,6 +111,8 @@ export function OpenLayersMap({
const [isNewBoxDrawn, setIsNewBoxDrawn] = useState(false);
const [isMapInitialized, setIsMapInitialized] = useState(false);

const isLoadingTiles = useTileLoading(mapRef);

const { activeBaselayer, internalBaselayers } = baselayersState;

const tileLayers = useMemo(() => {
Expand Down Expand Up @@ -484,7 +487,7 @@ export function OpenLayersMap({
externalSearchMarkerRef={externalSearchMarkerRef}
isMapInitialized={isMapInitialized}
/>
<LoadingOverlay isLoading={isPending} />
<LoadingOverlay isLoading={isPending || isLoadingTiles} />
</div>
);
}
69 changes: 69 additions & 0 deletions src/hooks/useTileLoading.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { useEffect, useState } from 'react';
import { Map } from 'ol';
import TileLayer from 'ol/layer/Tile';
import XYZ from 'ol/source/XYZ';

export function useTileLoading(mapRef: React.RefObject<Map | null>) {
const [isLoadingTiles, setIsLoadingTiles] = useState(false);

useEffect(() => {
const map = mapRef.current;
if (!map) return;

let pendingTiles = 0;

function onLoadStart() {
pendingTiles++;
setIsLoadingTiles(true);
}

function onLoadEnd() {
pendingTiles = Math.max(0, pendingTiles - 1);
if (pendingTiles === 0) setIsLoadingTiles(false);
}

// Attach to all current and future tile sources
function bindSource(layer: TileLayer<XYZ>) {
const source = layer.getSource();
if (!source) return;
source.on('tileloadstart', onLoadStart);
source.on('tileloadend', onLoadEnd);
source.on('tileloaderror', onLoadEnd); // don't hang on error
}

function unbindSource(layer: TileLayer<XYZ>) {
const source = layer.getSource();
if (!source) return;
source.un('tileloadstart', onLoadStart);
source.un('tileloadend', onLoadEnd);
source.un('tileloaderror', onLoadEnd);
}

// Bind existing layers
map.getLayers().forEach((layer) => {
if (layer instanceof TileLayer) bindSource(layer as TileLayer<XYZ>);
});

// Bind layers added after mount
const layerCollection = map.getLayers();
layerCollection.on('add', (e) => {
if (e.element instanceof TileLayer)
bindSource(e.element as TileLayer<XYZ>);
});
layerCollection.on('remove', (e) => {
if (e.element instanceof TileLayer)
unbindSource(e.element as TileLayer<XYZ>);
pendingTiles = 0;
setIsLoadingTiles(false);
});

return () => {
map.getLayers().forEach((layer) => {
if (layer instanceof TileLayer) unbindSource(layer as TileLayer<XYZ>);
});
setIsLoadingTiles(false);
};
}, [mapRef]);

return isLoadingTiles;
}
Loading