66/* eslint-disable @typescript-eslint/consistent-type-definitions */
77
88import { Dispatch } from 'redux' ;
9+ // @ts -ignore
10+ import turf from 'turf' ;
911import { FeatureCollection } from 'geojson' ;
1012import { MapStoreState } from '../reducers/store' ;
11- import { LAYER_TYPE , SOURCE_DATA_ID_ORIGIN } from '../../common/constants' ;
13+ import { LAYER_TYPE , SOURCE_DATA_REQUEST_ID } from '../../common/constants' ;
1214import {
1315 getDataFilters ,
1416 getDataRequestDescriptor ,
17+ getFittableLayers ,
1518 getLayerById ,
1619 getLayerList ,
1720} from '../selectors/map_selectors' ;
@@ -27,13 +30,15 @@ import {
2730 LAYER_DATA_LOAD_ENDED ,
2831 LAYER_DATA_LOAD_ERROR ,
2932 LAYER_DATA_LOAD_STARTED ,
33+ SET_GOTO ,
3034 SET_LAYER_ERROR_STATUS ,
3135 SET_LAYER_STYLE_META ,
3236 UPDATE_LAYER_PROP ,
3337 UPDATE_SOURCE_DATA_REQUEST ,
3438} from './map_action_constants' ;
3539import { ILayer } from '../classes/layers/layer' ;
36- import { DataMeta , MapFilters } from '../../common/descriptor_types' ;
40+ import { DataMeta , MapExtent , MapFilters } from '../../common/descriptor_types' ;
41+ import { DataRequestAbortError } from '../classes/util/data_request' ;
3742
3843export type DataRequestContext = {
3944 startLoading ( dataId : string , requestToken : symbol , meta : DataMeta ) : void ;
@@ -269,11 +274,107 @@ export function updateSourceDataRequest(layerId: string, newData: unknown) {
269274 return ( dispatch : Dispatch ) => {
270275 dispatch ( {
271276 type : UPDATE_SOURCE_DATA_REQUEST ,
272- dataId : SOURCE_DATA_ID_ORIGIN ,
277+ dataId : SOURCE_DATA_REQUEST_ID ,
273278 layerId,
274279 newData,
275280 } ) ;
276281
277282 dispatch < any > ( updateStyleMeta ( layerId ) ) ;
278283 } ;
279284}
285+
286+ export function fitToLayerExtent ( layerId : string ) {
287+ return async ( dispatch : Dispatch , getState : ( ) => MapStoreState ) => {
288+ const targetLayer = getLayerById ( layerId , getState ( ) ) ;
289+
290+ if ( targetLayer ) {
291+ try {
292+ const bounds = await targetLayer . getBounds (
293+ getDataRequestContext ( dispatch , getState , layerId )
294+ ) ;
295+ if ( bounds ) {
296+ await dispatch ( setGotoWithBounds ( bounds ) ) ;
297+ }
298+ } catch ( error ) {
299+ if ( ! ( error instanceof DataRequestAbortError ) ) {
300+ // eslint-disable-next-line no-console
301+ console . warn (
302+ 'Unhandled getBounds error for layer. Only DataRequestAbortError should be surfaced' ,
303+ error
304+ ) ;
305+ }
306+ // new fitToLayerExtent request has superseded this thread of execution. Results no longer needed.
307+ return ;
308+ }
309+ }
310+ } ;
311+ }
312+
313+ export function fitToDataBounds ( ) {
314+ return async ( dispatch : Dispatch , getState : ( ) => MapStoreState ) => {
315+ const layerList = getFittableLayers ( getState ( ) ) ;
316+
317+ if ( ! layerList . length ) {
318+ return ;
319+ }
320+
321+ const boundsPromises = layerList . map ( async ( layer : ILayer ) => {
322+ return layer . getBounds ( getDataRequestContext ( dispatch , getState , layer . getId ( ) ) ) ;
323+ } ) ;
324+
325+ let bounds ;
326+ try {
327+ bounds = await Promise . all ( boundsPromises ) ;
328+ } catch ( error ) {
329+ if ( ! ( error instanceof DataRequestAbortError ) ) {
330+ // eslint-disable-next-line no-console
331+ console . warn (
332+ 'Unhandled getBounds error for layer. Only DataRequestAbortError should be surfaced' ,
333+ error
334+ ) ;
335+ }
336+ // new fitToDataBounds request has superseded this thread of execution. Results no longer needed.
337+ return ;
338+ }
339+
340+ const corners = [ ] ;
341+ for ( let i = 0 ; i < bounds . length ; i ++ ) {
342+ const b = bounds [ i ] ;
343+
344+ // filter out undefined bounds (uses Infinity due to turf responses)
345+ if (
346+ b === null ||
347+ b . minLon === Infinity ||
348+ b . maxLon === Infinity ||
349+ b . minLat === - Infinity ||
350+ b . maxLat === - Infinity
351+ ) {
352+ continue ;
353+ }
354+
355+ corners . push ( [ b . minLon , b . minLat ] ) ;
356+ corners . push ( [ b . maxLon , b . maxLat ] ) ;
357+ }
358+
359+ if ( ! corners . length ) {
360+ return ;
361+ }
362+
363+ const turfUnionBbox = turf . bbox ( turf . multiPoint ( corners ) ) ;
364+ const dataBounds = {
365+ minLon : turfUnionBbox [ 0 ] ,
366+ minLat : turfUnionBbox [ 1 ] ,
367+ maxLon : turfUnionBbox [ 2 ] ,
368+ maxLat : turfUnionBbox [ 3 ] ,
369+ } ;
370+
371+ dispatch ( setGotoWithBounds ( dataBounds ) ) ;
372+ } ;
373+ }
374+
375+ function setGotoWithBounds ( bounds : MapExtent ) {
376+ return {
377+ type : SET_GOTO ,
378+ bounds,
379+ } ;
380+ }
0 commit comments