@jupytergis/base 0.10.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commands/BaseCommandIDs.d.ts +2 -0
- package/lib/commands/BaseCommandIDs.js +3 -0
- package/lib/commands/index.js +66 -0
- package/lib/constants.js +4 -0
- package/lib/dialogs/symbology/hooks/useGetBandInfo.d.ts +0 -6
- package/lib/dialogs/symbology/hooks/useGetBandInfo.js +2 -2
- package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.js +4 -4
- package/lib/dialogs/symbology/vector_layer/types/Categorized.js +1 -5
- package/lib/formbuilder/formselectors.js +5 -1
- package/lib/formbuilder/objectform/StoryEditorForm.d.ts +9 -0
- package/lib/formbuilder/objectform/StoryEditorForm.js +16 -0
- package/lib/formbuilder/objectform/components/StorySegmentReset.d.ts +8 -0
- package/lib/formbuilder/objectform/components/StorySegmentReset.js +24 -0
- package/lib/formbuilder/objectform/layer/index.d.ts +1 -0
- package/lib/formbuilder/objectform/layer/index.js +1 -0
- package/lib/formbuilder/objectform/layer/storySegmentLayerForm.d.ts +5 -0
- package/lib/formbuilder/objectform/layer/storySegmentLayerForm.js +32 -0
- package/lib/mainview/mainView.d.ts +18 -0
- package/lib/mainview/mainView.js +293 -14
- package/lib/panelview/components/layers.d.ts +2 -1
- package/lib/panelview/components/layers.js +31 -23
- package/lib/panelview/{components/filter-panel → filter-panel}/Filter.js +1 -1
- package/lib/panelview/leftpanel.js +89 -7
- package/lib/panelview/rightpanel.d.ts +2 -0
- package/lib/panelview/rightpanel.js +41 -4
- package/lib/panelview/story-maps/PreviewModeSwitch.d.ts +7 -0
- package/lib/panelview/story-maps/PreviewModeSwitch.js +13 -0
- package/lib/panelview/story-maps/StoryEditorPanel.d.ts +9 -0
- package/lib/panelview/story-maps/StoryEditorPanel.js +34 -0
- package/lib/panelview/story-maps/StoryNavBar.d.ts +10 -0
- package/lib/panelview/story-maps/StoryNavBar.js +11 -0
- package/lib/panelview/story-maps/StoryViewerPanel.d.ts +13 -0
- package/lib/panelview/story-maps/StoryViewerPanel.js +179 -0
- package/lib/panelview/story-maps/components/StoryContentSection.d.ts +6 -0
- package/lib/panelview/story-maps/components/StoryContentSection.js +10 -0
- package/lib/panelview/story-maps/components/StoryImageSection.d.ts +15 -0
- package/lib/panelview/story-maps/components/StoryImageSection.js +13 -0
- package/lib/panelview/story-maps/components/StorySubtitleSection.d.ts +11 -0
- package/lib/panelview/story-maps/components/StorySubtitleSection.js +9 -0
- package/lib/panelview/story-maps/components/StoryTitleSection.d.ts +12 -0
- package/lib/panelview/story-maps/components/StoryTitleSection.js +8 -0
- package/lib/shared/components/Calendar.d.ts +1 -1
- package/lib/shared/components/Combobox.d.ts +21 -0
- package/lib/shared/components/Combobox.js +32 -0
- package/lib/shared/components/Command.d.ts +18 -0
- package/lib/shared/components/Command.js +60 -0
- package/lib/shared/components/Dialog.d.ts +15 -0
- package/lib/shared/components/Dialog.js +62 -0
- package/lib/shared/components/Input.d.ts +3 -0
- package/lib/shared/components/Input.js +18 -0
- package/lib/shared/components/Pagination.js +3 -2
- package/lib/shared/components/RadioGroup.d.ts +5 -0
- package/lib/shared/components/RadioGroup.js +26 -0
- package/lib/shared/components/Select.d.ts +19 -0
- package/lib/shared/components/Select.js +28 -0
- package/lib/shared/components/SingleDatePicker.d.ts +11 -0
- package/lib/shared/components/SingleDatePicker.js +16 -0
- package/lib/shared/components/Switch.d.ts +4 -0
- package/lib/shared/components/Switch.js +20 -0
- package/lib/stacBrowser/components/StacPanel.d.ts +9 -1
- package/lib/stacBrowser/components/StacPanel.js +53 -9
- package/lib/stacBrowser/components/filter-extension/QueryableComboBox.d.ts +9 -0
- package/lib/stacBrowser/components/filter-extension/QueryableComboBox.js +179 -0
- package/lib/stacBrowser/components/filter-extension/QueryableRow.d.ts +16 -0
- package/lib/stacBrowser/components/filter-extension/QueryableRow.js +16 -0
- package/lib/stacBrowser/components/filter-extension/StacFilterExtensionPanel.d.ts +7 -0
- package/lib/stacBrowser/components/filter-extension/StacFilterExtensionPanel.js +49 -0
- package/lib/stacBrowser/components/filter-extension/StacQueryableFilters.d.ts +11 -0
- package/lib/stacBrowser/components/filter-extension/StacQueryableFilters.js +19 -0
- package/lib/stacBrowser/components/{StacFilterSection.d.ts → geodes/StacFilterSection.d.ts} +1 -1
- package/lib/stacBrowser/components/{StacFilterSection.js → geodes/StacFilterSection.js} +3 -3
- package/lib/stacBrowser/components/geodes/StacGeodesFilterPanel.d.ts +7 -0
- package/lib/stacBrowser/components/geodes/StacGeodesFilterPanel.js +69 -0
- package/lib/stacBrowser/components/shared/StacPanelResults.d.ts +3 -0
- package/lib/stacBrowser/components/shared/StacPanelResults.js +68 -0
- package/lib/stacBrowser/components/shared/StacSpatialExtent.d.ts +8 -0
- package/lib/stacBrowser/components/shared/StacSpatialExtent.js +10 -0
- package/lib/stacBrowser/components/shared/StacTemporalExtent.d.ts +9 -0
- package/lib/stacBrowser/components/shared/StacTemporalExtent.js +9 -0
- package/lib/stacBrowser/context/StacResultsContext.d.ts +33 -0
- package/lib/stacBrowser/context/StacResultsContext.js +269 -0
- package/lib/stacBrowser/hooks/useGeodesSearch.d.ts +24 -0
- package/lib/stacBrowser/hooks/useGeodesSearch.js +178 -0
- package/lib/stacBrowser/hooks/useStacFilterExtension.d.ts +30 -0
- package/lib/stacBrowser/hooks/useStacFilterExtension.js +262 -0
- package/lib/stacBrowser/hooks/useStacSearch.d.ts +5 -16
- package/lib/stacBrowser/hooks/useStacSearch.js +30 -184
- package/lib/stacBrowser/types/types.d.ts +86 -3
- package/lib/toolbar/widget.d.ts +15 -0
- package/lib/toolbar/widget.js +70 -0
- package/lib/tools.d.ts +0 -7
- package/lib/tools.js +56 -15
- package/package.json +8 -3
- package/style/base.css +42 -3
- package/style/leftPanel.css +18 -0
- package/style/shared/button.css +6 -5
- package/style/shared/calendar.css +7 -1
- package/style/shared/combobox.css +75 -0
- package/style/shared/command.css +178 -0
- package/style/shared/dialog.css +177 -0
- package/style/shared/input.css +59 -0
- package/style/shared/pagination.css +1 -1
- package/style/shared/popover.css +1 -0
- package/style/shared/radioGroup.css +55 -0
- package/style/shared/switch.css +63 -0
- package/style/shared/tabs.css +4 -3
- package/style/shared/toggle.css +1 -1
- package/style/stacBrowser.css +169 -16
- package/style/statusBar.css +1 -0
- package/style/storyPanel.css +185 -0
- package/style/tabPanel.css +1 -88
- package/lib/stacBrowser/components/StacPanelFilters.d.ts +0 -14
- package/lib/stacBrowser/components/StacPanelFilters.js +0 -81
- package/lib/stacBrowser/components/StacPanelResults.d.ts +0 -13
- package/lib/stacBrowser/components/StacPanelResults.js +0 -48
- /package/lib/panelview/{components/filter-panel → filter-panel}/Filter.d.ts +0 -0
- /package/lib/panelview/{components/filter-panel → filter-panel}/FilterRow.d.ts +0 -0
- /package/lib/panelview/{components/filter-panel → filter-panel}/FilterRow.js +0 -0
- /package/lib/panelview/{components/identify-panel → identify-panel}/IdentifyPanel.d.ts +0 -0
- /package/lib/panelview/{components/identify-panel → identify-panel}/IdentifyPanel.js +0 -0
package/lib/mainview/mainView.js
CHANGED
|
@@ -16,11 +16,12 @@ import { UUID } from '@lumino/coreutils';
|
|
|
16
16
|
import { ContextMenu } from '@lumino/widgets';
|
|
17
17
|
import { Collection, Map as OlMap, View, getUid, } from 'ol';
|
|
18
18
|
import Feature from 'ol/Feature';
|
|
19
|
-
import { FullScreen, ScaleLine } from 'ol/control';
|
|
19
|
+
import { FullScreen, ScaleLine, Zoom } from 'ol/control';
|
|
20
20
|
import { singleClick } from 'ol/events/condition';
|
|
21
|
+
import { getCenter } from 'ol/extent';
|
|
21
22
|
import { GeoJSON, MVT } from 'ol/format';
|
|
22
23
|
import { Point } from 'ol/geom';
|
|
23
|
-
import { DragAndDrop, Select } from 'ol/interaction';
|
|
24
|
+
import { DragAndDrop, DragPan, DragRotate, DragZoom, KeyboardPan, KeyboardZoom, MouseWheelZoom, PinchRotate, PinchZoom, DoubleClickZoom, Select, } from 'ol/interaction';
|
|
24
25
|
import { Heatmap as HeatmapLayer, Image as ImageLayer, Layer, Vector as VectorLayer, VectorTile as VectorTileLayer, WebGLTile as WebGlTileLayer, } from 'ol/layer';
|
|
25
26
|
import TileLayer from 'ol/layer/Tile';
|
|
26
27
|
import { fromLonLat, get as getProjection, toLonLat, transformExtent, } from 'ol/proj';
|
|
@@ -43,8 +44,10 @@ import { debounce, isLightTheme, loadFile, throttle } from "../tools";
|
|
|
43
44
|
import CollaboratorPointers from './CollaboratorPointers';
|
|
44
45
|
import { FollowIndicator } from './FollowIndicator';
|
|
45
46
|
import TemporalSlider from './TemporalSlider';
|
|
47
|
+
import { hexToRgb } from '../dialogs/symbology/colorRampUtils';
|
|
46
48
|
import { markerIcon } from '../icons';
|
|
47
49
|
import { LeftPanel, RightPanel } from '../panelview';
|
|
50
|
+
import StoryViewerPanel from '../panelview/story-maps/StoryViewerPanel';
|
|
48
51
|
export class MainView extends React.Component {
|
|
49
52
|
constructor(props) {
|
|
50
53
|
super(props);
|
|
@@ -405,6 +408,138 @@ export class MainView extends React.Component {
|
|
|
405
408
|
}
|
|
406
409
|
}
|
|
407
410
|
};
|
|
411
|
+
/**
|
|
412
|
+
* Handler for when story maps change in the model.
|
|
413
|
+
* Updates specta state and presentation colors when story data becomes available.
|
|
414
|
+
*/
|
|
415
|
+
this._setupSpectaMode = () => {
|
|
416
|
+
this._removeAllInteractions();
|
|
417
|
+
this._setupStoryScrollListener();
|
|
418
|
+
// Update colors CSS variables with colors from story
|
|
419
|
+
this._updateSpectaPresentationColors();
|
|
420
|
+
};
|
|
421
|
+
this._removeAllInteractions = () => {
|
|
422
|
+
// Remove all default interactions
|
|
423
|
+
const interactions = this._Map.getInteractions();
|
|
424
|
+
const interactionArray = interactions.getArray();
|
|
425
|
+
// Remove each interaction type
|
|
426
|
+
const interactionsToRemove = [
|
|
427
|
+
DragPan,
|
|
428
|
+
DragRotate,
|
|
429
|
+
DragZoom,
|
|
430
|
+
KeyboardPan,
|
|
431
|
+
KeyboardZoom,
|
|
432
|
+
MouseWheelZoom,
|
|
433
|
+
PinchRotate,
|
|
434
|
+
PinchZoom,
|
|
435
|
+
DoubleClickZoom,
|
|
436
|
+
DragAndDrop,
|
|
437
|
+
Select,
|
|
438
|
+
];
|
|
439
|
+
this._zoomControl && this._Map.removeControl(this._zoomControl);
|
|
440
|
+
interactionsToRemove.forEach(InteractionClass => {
|
|
441
|
+
const interaction = interactionArray.find(interaction => interaction instanceof InteractionClass);
|
|
442
|
+
if (interaction) {
|
|
443
|
+
this._Map.removeInteraction(interaction);
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
};
|
|
447
|
+
this._setupStoryScrollListener = () => {
|
|
448
|
+
const segmentNavigationThrottle = 750; // Minimum time between segment changes (ms)
|
|
449
|
+
const SCROLL_EDGE_THRESHOLD = 0; // Pixels from top/bottom to trigger segment change
|
|
450
|
+
// Create throttled functions that call the current panel handle dynamically
|
|
451
|
+
const throttledHandleNext = throttle(() => {
|
|
452
|
+
const panelHandle = this.storyViewerPanelRef.current;
|
|
453
|
+
panelHandle === null || panelHandle === void 0 ? void 0 : panelHandle.handleNext();
|
|
454
|
+
}, segmentNavigationThrottle);
|
|
455
|
+
const throttledHandlePrev = throttle(() => {
|
|
456
|
+
const panelHandle = this.storyViewerPanelRef.current;
|
|
457
|
+
panelHandle === null || panelHandle === void 0 ? void 0 : panelHandle.handlePrev();
|
|
458
|
+
}, segmentNavigationThrottle);
|
|
459
|
+
const handleScroll = (e) => {
|
|
460
|
+
const currentPanelHandle = this.storyViewerPanelRef.current;
|
|
461
|
+
if (!currentPanelHandle || !currentPanelHandle.canNavigate) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
const wheelEvent = e;
|
|
465
|
+
const target = wheelEvent.target;
|
|
466
|
+
// Find the story viewer panel
|
|
467
|
+
const storyViewerPanel = document.querySelector('.jgis-story-viewer-panel');
|
|
468
|
+
// If no panel found, change segments normally
|
|
469
|
+
if (!storyViewerPanel) {
|
|
470
|
+
wheelEvent.preventDefault();
|
|
471
|
+
wheelEvent.deltaY > 0 ? throttledHandleNext() : throttledHandlePrev();
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const hasOverflow = storyViewerPanel.scrollHeight > storyViewerPanel.clientHeight;
|
|
475
|
+
// If panel has no overflow, change segments normally
|
|
476
|
+
if (!hasOverflow) {
|
|
477
|
+
wheelEvent.preventDefault();
|
|
478
|
+
wheelEvent.deltaY > 0 ? throttledHandleNext() : throttledHandlePrev();
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
// Panel has overflow - handle scroll forwarding and edge detection
|
|
482
|
+
const scrollTop = storyViewerPanel.scrollTop;
|
|
483
|
+
const scrollHeight = storyViewerPanel.scrollHeight;
|
|
484
|
+
const clientHeight = storyViewerPanel.clientHeight;
|
|
485
|
+
const isAtBottom = scrollTop + clientHeight >= scrollHeight - SCROLL_EDGE_THRESHOLD;
|
|
486
|
+
const isAtTop = scrollTop <= SCROLL_EDGE_THRESHOLD;
|
|
487
|
+
const isScrollingDown = wheelEvent.deltaY > 0;
|
|
488
|
+
const isScrollingUp = wheelEvent.deltaY < 0;
|
|
489
|
+
// At edges: change segments
|
|
490
|
+
if ((isScrollingDown && isAtBottom) || (isScrollingUp && isAtTop)) {
|
|
491
|
+
wheelEvent.preventDefault();
|
|
492
|
+
isScrollingDown ? throttledHandleNext() : throttledHandlePrev();
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
// If scrolling inside the panel, let it scroll naturally
|
|
496
|
+
if (target.closest('.jgis-story-viewer-panel')) {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
// Scrolling outside the panel: forward scroll to panel (no throttling for smooth scrolling)
|
|
500
|
+
wheelEvent.preventDefault();
|
|
501
|
+
const newScrollTop = Math.max(0, Math.min(scrollHeight - clientHeight, scrollTop + wheelEvent.deltaY));
|
|
502
|
+
storyViewerPanel.scrollTop = newScrollTop;
|
|
503
|
+
};
|
|
504
|
+
this._storyScrollHandler = handleScroll;
|
|
505
|
+
// Attach wheel event listener to the main container
|
|
506
|
+
const containerElement = document.querySelector('.jGIS-Mainview-Container');
|
|
507
|
+
if (containerElement) {
|
|
508
|
+
containerElement.addEventListener('wheel', handleScroll, {
|
|
509
|
+
passive: false,
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
this._cleanupStoryScrollListener = () => {
|
|
514
|
+
if (this._storyScrollHandler) {
|
|
515
|
+
const containerElement = document.querySelector('.jGIS-Mainview-Container');
|
|
516
|
+
if (containerElement) {
|
|
517
|
+
containerElement.removeEventListener('wheel', this._storyScrollHandler);
|
|
518
|
+
}
|
|
519
|
+
this._storyScrollHandler = null;
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
this._updateSpectaPresentationColors = () => {
|
|
523
|
+
var _a;
|
|
524
|
+
// Try ref first, fallback to querySelector if ref not available yet
|
|
525
|
+
const container = this.spectaContainerRef.current ||
|
|
526
|
+
((_a = this.divRef.current) === null || _a === void 0 ? void 0 : _a.querySelector('.jgis-specta-story-panel-container'));
|
|
527
|
+
if (!container) {
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
const story = this._model.getSelectedStory().story;
|
|
531
|
+
const bgColor = story === null || story === void 0 ? void 0 : story.presentaionBgColor;
|
|
532
|
+
const textColor = story === null || story === void 0 ? void 0 : story.presentaionTextColor;
|
|
533
|
+
// Set background color
|
|
534
|
+
if (bgColor) {
|
|
535
|
+
const rgb = hexToRgb(bgColor);
|
|
536
|
+
container.style.setProperty('--jgis-specta-bg-color', `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`);
|
|
537
|
+
}
|
|
538
|
+
// Set text color
|
|
539
|
+
if (textColor) {
|
|
540
|
+
container.style.setProperty('--jgis-specta-text-color', textColor);
|
|
541
|
+
}
|
|
542
|
+
};
|
|
408
543
|
this._onSharedMetadataChanged = (_, changes) => {
|
|
409
544
|
const newState = Object.assign({}, this.state.annotations);
|
|
410
545
|
changes.forEach((val, key) => {
|
|
@@ -450,10 +585,15 @@ export class MainView extends React.Component {
|
|
|
450
585
|
};
|
|
451
586
|
this._isPositionInitialized = false;
|
|
452
587
|
this.divRef = React.createRef(); // Reference of render div
|
|
588
|
+
this.controlsToolbarRef = React.createRef();
|
|
589
|
+
this.spectaContainerRef = React.createRef();
|
|
590
|
+
this.storyViewerPanelRef = React.createRef();
|
|
453
591
|
this._ready = false;
|
|
454
592
|
this._sourceToLayerMap = new Map();
|
|
455
593
|
this._originalFeatures = {};
|
|
456
594
|
this._featurePropertyCache = new Map();
|
|
595
|
+
this._isSpectaPresentationInitialized = false;
|
|
596
|
+
this._storyScrollHandler = null;
|
|
457
597
|
this._state = props.state;
|
|
458
598
|
this._formSchemaRegistry = props.formSchemaRegistry;
|
|
459
599
|
this._annotationModel = props.annotationModel;
|
|
@@ -488,6 +628,7 @@ export class MainView extends React.Component {
|
|
|
488
628
|
this._model.sharedModel.changed.connect(this._onSharedModelStateChange);
|
|
489
629
|
this._model.sharedMetadataChanged.connect(this._onSharedMetadataChanged, this);
|
|
490
630
|
this._model.zoomToPositionSignal.connect(this._onZoomToPosition, this);
|
|
631
|
+
this._model.settingsChanged.connect(this._onSettingsChanged, this);
|
|
491
632
|
this._model.updateLayerSignal.connect(this._triggerLayerUpdate, this);
|
|
492
633
|
this._model.addFeatureAsMsSignal.connect(this._convertFeatureToMs, this);
|
|
493
634
|
this._model.geolocationChanged.connect(this._handleGeolocationChanged, this);
|
|
@@ -512,6 +653,7 @@ export class MainView extends React.Component {
|
|
|
512
653
|
loadingErrors: [],
|
|
513
654
|
displayTemporalController: false,
|
|
514
655
|
filterStates: {},
|
|
656
|
+
isSpectaPresentation: false,
|
|
515
657
|
};
|
|
516
658
|
this._sources = [];
|
|
517
659
|
this._loadingLayers = new Set();
|
|
@@ -529,12 +671,19 @@ export class MainView extends React.Component {
|
|
|
529
671
|
: [0, 0];
|
|
530
672
|
const zoom = options.zoom !== undefined ? options.zoom : 1;
|
|
531
673
|
await this.generateMap(center, zoom);
|
|
532
|
-
this.addContextMenu();
|
|
533
674
|
this._mainViewModel.initSignal();
|
|
534
675
|
if (window.jupytergisMaps !== undefined && this._documentPath) {
|
|
535
676
|
window.jupytergisMaps[this._documentPath] = this._Map;
|
|
536
677
|
}
|
|
537
678
|
}
|
|
679
|
+
componentDidUpdate(prevProps, prevState) {
|
|
680
|
+
// Run setup when isSpectaPresentation changes from false/undefined to true
|
|
681
|
+
if (this.state.isSpectaPresentation &&
|
|
682
|
+
!this._isSpectaPresentationInitialized) {
|
|
683
|
+
this._setupSpectaMode();
|
|
684
|
+
this._isSpectaPresentationInitialized = true;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
538
687
|
componentWillUnmount() {
|
|
539
688
|
if (window.jupytergisMaps !== undefined && this._documentPath) {
|
|
540
689
|
delete window.jupytergisMaps[this._documentPath];
|
|
@@ -542,20 +691,37 @@ export class MainView extends React.Component {
|
|
|
542
691
|
window.removeEventListener('resize', this._handleWindowResize);
|
|
543
692
|
this._mainViewModel.viewSettingChanged.disconnect(this._onViewChanged, this);
|
|
544
693
|
this._model.themeChanged.disconnect(this._handleThemeChange, this);
|
|
694
|
+
this._model.settingsChanged.disconnect(this._onSettingsChanged, this);
|
|
545
695
|
this._model.sharedOptionsChanged.disconnect(this._onSharedOptionsChanged, this);
|
|
546
696
|
this._model.clientStateChanged.disconnect(this._onClientSharedStateChanged, this);
|
|
697
|
+
// Clean up story scroll listener
|
|
698
|
+
this._cleanupStoryScrollListener();
|
|
547
699
|
this._mainViewModel.dispose();
|
|
548
700
|
}
|
|
549
701
|
async generateMap(center, zoom) {
|
|
702
|
+
const scaleLine = new ScaleLine({
|
|
703
|
+
target: this.controlsToolbarRef.current || undefined,
|
|
704
|
+
});
|
|
705
|
+
const fullScreen = new FullScreen({
|
|
706
|
+
target: this.controlsToolbarRef.current || undefined,
|
|
707
|
+
});
|
|
708
|
+
this._zoomControl = new Zoom({
|
|
709
|
+
target: this.controlsToolbarRef.current || undefined,
|
|
710
|
+
});
|
|
711
|
+
const controls = [scaleLine, fullScreen];
|
|
712
|
+
if (this._model.jgisSettings.zoomButtonsEnabled) {
|
|
713
|
+
controls.push(this._zoomControl);
|
|
714
|
+
}
|
|
550
715
|
if (this.divRef.current) {
|
|
551
716
|
this._Map = new OlMap({
|
|
552
717
|
target: this.divRef.current,
|
|
718
|
+
keyboardEventTarget: document,
|
|
553
719
|
layers: [],
|
|
554
720
|
view: new View({
|
|
555
721
|
center,
|
|
556
722
|
zoom,
|
|
557
723
|
}),
|
|
558
|
-
controls
|
|
724
|
+
controls,
|
|
559
725
|
});
|
|
560
726
|
// Add map interactions
|
|
561
727
|
const dragAndDropInteraction = new DragAndDrop({
|
|
@@ -997,7 +1163,8 @@ export class MainView extends React.Component {
|
|
|
997
1163
|
let layerParameters;
|
|
998
1164
|
let sourceId;
|
|
999
1165
|
let source;
|
|
1000
|
-
|
|
1166
|
+
// Sourceless layers
|
|
1167
|
+
if (!['StacLayer', 'StorySegmentLayer'].includes(layer.type)) {
|
|
1001
1168
|
sourceId = (_a = layer.parameters) === null || _a === void 0 ? void 0 : _a.source;
|
|
1002
1169
|
if (!sourceId) {
|
|
1003
1170
|
return;
|
|
@@ -1099,6 +1266,10 @@ export class MainView extends React.Component {
|
|
|
1099
1266
|
this.setState(old => (Object.assign(Object.assign({}, old), { metadata: layerParameters.data.properties })));
|
|
1100
1267
|
break;
|
|
1101
1268
|
}
|
|
1269
|
+
case 'StorySegmentLayer': {
|
|
1270
|
+
// Special layer not for this
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1102
1273
|
}
|
|
1103
1274
|
// OpenLayers doesn't have name/id field so add it
|
|
1104
1275
|
newMapLayer.set('id', id);
|
|
@@ -1164,7 +1335,6 @@ export class MainView extends React.Component {
|
|
|
1164
1335
|
}
|
|
1165
1336
|
catch (error) {
|
|
1166
1337
|
if (this.state.loadingErrors.find(item => item.id === id && item.error === error.message)) {
|
|
1167
|
-
this._loadingLayers.delete(id);
|
|
1168
1338
|
return;
|
|
1169
1339
|
}
|
|
1170
1340
|
await showErrorMessage(`Error Adding ${layer.name}`, `Failed to add ${layer.name}: ${error.message || 'invalid file path'}`);
|
|
@@ -1174,7 +1344,10 @@ export class MainView extends React.Component {
|
|
|
1174
1344
|
error: error.message || 'invalid file path',
|
|
1175
1345
|
index,
|
|
1176
1346
|
});
|
|
1347
|
+
}
|
|
1348
|
+
finally {
|
|
1177
1349
|
this._loadingLayers.delete(id);
|
|
1350
|
+
this.setState(old => (Object.assign(Object.assign({}, old), { loadingLayer: false })));
|
|
1178
1351
|
}
|
|
1179
1352
|
}
|
|
1180
1353
|
/**
|
|
@@ -1185,7 +1358,7 @@ export class MainView extends React.Component {
|
|
|
1185
1358
|
*/
|
|
1186
1359
|
async updateLayer(id, layer, mapLayer, oldLayer) {
|
|
1187
1360
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1188
|
-
mapLayer.setVisible(layer.visible);
|
|
1361
|
+
layer.type !== 'StorySegmentLayer' && mapLayer.setVisible(layer.visible);
|
|
1189
1362
|
switch (layer.type) {
|
|
1190
1363
|
case 'RasterLayer': {
|
|
1191
1364
|
mapLayer.setOpacity(((_a = layer.parameters) === null || _a === void 0 ? void 0 : _a.opacity) || 1);
|
|
@@ -1371,12 +1544,42 @@ export class MainView extends React.Component {
|
|
|
1371
1544
|
}
|
|
1372
1545
|
}
|
|
1373
1546
|
_onSharedOptionsChanged() {
|
|
1547
|
+
// ! would prefer a model ready signal or something, this feels hacky
|
|
1548
|
+
const enableSpectaPresentation = this._model.isSpectaMode();
|
|
1549
|
+
// Handle initialization based on specta presentation state
|
|
1550
|
+
if (!this._isSpectaPresentationInitialized) {
|
|
1551
|
+
if (enableSpectaPresentation) {
|
|
1552
|
+
// _setupSpectaMode will be called in componentDidUpdate when state changes
|
|
1553
|
+
this.setState(old => (Object.assign(Object.assign({}, old), { isSpectaPresentation: true })));
|
|
1554
|
+
}
|
|
1555
|
+
else {
|
|
1556
|
+
// Add context menu when not in specta mode
|
|
1557
|
+
this.addContextMenu();
|
|
1558
|
+
this._isSpectaPresentationInitialized = true;
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1374
1561
|
if (!this._isPositionInitialized) {
|
|
1375
1562
|
const options = this._model.getOptions();
|
|
1376
1563
|
this.updateOptions(options);
|
|
1377
1564
|
this._isPositionInitialized = true;
|
|
1378
1565
|
}
|
|
1379
1566
|
}
|
|
1567
|
+
_onSettingsChanged(sender, key) {
|
|
1568
|
+
if (key !== 'zoomButtonsEnabled' || !this._Map) {
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
const enabled = this._model.jgisSettings.zoomButtonsEnabled;
|
|
1572
|
+
if (!enabled && this._zoomControl) {
|
|
1573
|
+
this._Map.removeControl(this._zoomControl);
|
|
1574
|
+
this._zoomControl = undefined;
|
|
1575
|
+
}
|
|
1576
|
+
if (enabled && !this._zoomControl) {
|
|
1577
|
+
this._zoomControl = new Zoom({
|
|
1578
|
+
target: this.controlsToolbarRef.current || undefined,
|
|
1579
|
+
});
|
|
1580
|
+
this._Map.addControl(this._zoomControl);
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1380
1583
|
async updateOptions(options) {
|
|
1381
1584
|
const { projection, extent, useExtent, latitude, longitude, zoom, bearing, } = options;
|
|
1382
1585
|
let view = this._Map.getView();
|
|
@@ -1545,8 +1748,9 @@ export class MainView extends React.Component {
|
|
|
1545
1748
|
}
|
|
1546
1749
|
});
|
|
1547
1750
|
}
|
|
1751
|
+
// TODO this and flyToPosition need a rework
|
|
1548
1752
|
_onZoomToPosition(_, id) {
|
|
1549
|
-
var _a;
|
|
1753
|
+
var _a, _b, _c;
|
|
1550
1754
|
// Check if the id is an annotation
|
|
1551
1755
|
const annotation = (_a = this._model.annotationModel) === null || _a === void 0 ? void 0 : _a.getAnnotation(id);
|
|
1552
1756
|
if (annotation) {
|
|
@@ -1557,6 +1761,45 @@ export class MainView extends React.Component {
|
|
|
1557
1761
|
let extent;
|
|
1558
1762
|
const layer = this.getLayer(id);
|
|
1559
1763
|
const source = layer === null || layer === void 0 ? void 0 : layer.getSource();
|
|
1764
|
+
const jgisLayer = this._model.getLayer(id);
|
|
1765
|
+
/**
|
|
1766
|
+
* Layer may be undefined in two cases:
|
|
1767
|
+
* 1. StorySegmentLayer: These layers don't have an associated OpenLayers layer
|
|
1768
|
+
* 2. StacLayer: When centerOnPosition is called immediately after adding the layer,
|
|
1769
|
+
* the OpenLayers layer hasn't been created yet, so we use the bbox from the
|
|
1770
|
+
* layer model's STAC data directly.
|
|
1771
|
+
*/
|
|
1772
|
+
if (!layer) {
|
|
1773
|
+
// Handle StacLayer that hasn't been added to the map yet
|
|
1774
|
+
if ((jgisLayer === null || jgisLayer === void 0 ? void 0 : jgisLayer.type) === 'StacLayer') {
|
|
1775
|
+
const layerParams = jgisLayer.parameters;
|
|
1776
|
+
const stacBbox = (_b = layerParams.data) === null || _b === void 0 ? void 0 : _b.bbox;
|
|
1777
|
+
if (stacBbox && stacBbox.length === 4) {
|
|
1778
|
+
// STAC bbox format: [west, south, east, north] in EPSG:4326
|
|
1779
|
+
const [west, south, east, north] = stacBbox;
|
|
1780
|
+
const bboxExtent = [west, south, east, north];
|
|
1781
|
+
// Convert from EPSG:4326 to view projection
|
|
1782
|
+
const viewProjection = this._Map.getView().getProjection();
|
|
1783
|
+
const transformedExtent = viewProjection.getCode() !== 'EPSG:4326'
|
|
1784
|
+
? transformExtent(bboxExtent, 'EPSG:4326', viewProjection)
|
|
1785
|
+
: bboxExtent;
|
|
1786
|
+
this._Map.getView().fit(transformedExtent, {
|
|
1787
|
+
size: this._Map.getSize(),
|
|
1788
|
+
duration: 500,
|
|
1789
|
+
padding: [250, 250, 250, 250],
|
|
1790
|
+
});
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
// Handle StorySegmentLayer
|
|
1795
|
+
if ((jgisLayer === null || jgisLayer === void 0 ? void 0 : jgisLayer.type) === 'StorySegmentLayer') {
|
|
1796
|
+
const layerParams = jgisLayer.parameters;
|
|
1797
|
+
const coords = getCenter(layerParams.extent);
|
|
1798
|
+
this._flyToPosition({ x: coords[0], y: coords[1] }, layerParams.zoom, ((_c = layerParams.transition.time) !== null && _c !== void 0 ? _c : 1) * 1000, // seconds -> ms
|
|
1799
|
+
layerParams.transition.type);
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1560
1803
|
if (source instanceof VectorSource) {
|
|
1561
1804
|
extent = source.getExtent();
|
|
1562
1805
|
}
|
|
@@ -1596,10 +1839,43 @@ export class MainView extends React.Component {
|
|
|
1596
1839
|
});
|
|
1597
1840
|
}
|
|
1598
1841
|
}
|
|
1599
|
-
_flyToPosition(center, zoom, duration = 1000) {
|
|
1842
|
+
_flyToPosition(center, zoom, duration = 1000, transitionType) {
|
|
1600
1843
|
const view = this._Map.getView();
|
|
1601
|
-
|
|
1602
|
-
view.
|
|
1844
|
+
// Cancel any in-progress animations before starting new ones
|
|
1845
|
+
view.cancelAnimations();
|
|
1846
|
+
const targetCenter = [center.x, center.y];
|
|
1847
|
+
if (transitionType === 'linear') {
|
|
1848
|
+
// Linear: direct zoom
|
|
1849
|
+
view.animate({
|
|
1850
|
+
center: targetCenter,
|
|
1851
|
+
zoom: zoom,
|
|
1852
|
+
duration,
|
|
1853
|
+
});
|
|
1854
|
+
return;
|
|
1855
|
+
}
|
|
1856
|
+
if (transitionType === 'smooth') {
|
|
1857
|
+
// Smooth: zoom out, center, and zoom in
|
|
1858
|
+
// Centering takes full duration, zoom out completes halfway, zoom in starts halfway
|
|
1859
|
+
// 3 shows most of the map
|
|
1860
|
+
const zoomOutLevel = 3;
|
|
1861
|
+
// Start centering (full duration) and zoom out (50% duration) simultaneously
|
|
1862
|
+
view.animate({
|
|
1863
|
+
center: targetCenter,
|
|
1864
|
+
duration: duration,
|
|
1865
|
+
});
|
|
1866
|
+
// Chain zoom out -> zoom in (zoom in starts when zoom out completes)
|
|
1867
|
+
view.animate({
|
|
1868
|
+
zoom: zoomOutLevel,
|
|
1869
|
+
duration: duration * 0.5,
|
|
1870
|
+
}, {
|
|
1871
|
+
zoom: zoom,
|
|
1872
|
+
duration: duration * 0.5,
|
|
1873
|
+
});
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
// Immediate move
|
|
1877
|
+
view.setCenter(targetCenter);
|
|
1878
|
+
view.setZoom(zoom);
|
|
1603
1879
|
}
|
|
1604
1880
|
_onPointerMove(e) {
|
|
1605
1881
|
const pixel = this._Map.getEventPixel(e);
|
|
@@ -1775,7 +2051,7 @@ export class MainView extends React.Component {
|
|
|
1775
2051
|
}),
|
|
1776
2052
|
React.createElement("div", { className: "jGIS-Mainview-Container" },
|
|
1777
2053
|
this.state.displayTemporalController && (React.createElement(TemporalSlider, { model: this._model, filterStates: this.state.filterStates })),
|
|
1778
|
-
React.createElement("div", { className: "jGIS-Mainview data-jgis-keybinding", tabIndex:
|
|
2054
|
+
React.createElement("div", { className: "jGIS-Mainview data-jgis-keybinding", tabIndex: 0, style: {
|
|
1779
2055
|
border: this.state.remoteUser
|
|
1780
2056
|
? `solid 3px ${this.state.remoteUser.color}`
|
|
1781
2057
|
: 'unset',
|
|
@@ -1787,9 +2063,12 @@ export class MainView extends React.Component {
|
|
|
1787
2063
|
width: '100%',
|
|
1788
2064
|
height: '100%',
|
|
1789
2065
|
} },
|
|
1790
|
-
React.createElement("div", { className: "jgis-panels-wrapper" },
|
|
2066
|
+
React.createElement("div", { className: "jgis-panels-wrapper" }, !this.state.isSpectaPresentation ? (React.createElement(React.Fragment, null,
|
|
1791
2067
|
this._state && (React.createElement(LeftPanel, { model: this._model, commands: this._mainViewModel.commands, state: this._state })),
|
|
1792
|
-
this._formSchemaRegistry && this._annotationModel && (React.createElement(RightPanel, { model: this._model, formSchemaRegistry: this._formSchemaRegistry, annotationModel: this._annotationModel }))))
|
|
2068
|
+
this._formSchemaRegistry && this._annotationModel && (React.createElement(RightPanel, { model: this._model, commands: this._mainViewModel.commands, formSchemaRegistry: this._formSchemaRegistry, annotationModel: this._annotationModel })))) : (React.createElement("div", { className: "jgis-specta-right-panel-container-mod jgis-right-panel-container" },
|
|
2069
|
+
React.createElement("div", { ref: this.spectaContainerRef, className: "jgis-specta-story-panel-container" },
|
|
2070
|
+
React.createElement(StoryViewerPanel, { ref: this.storyViewerPanelRef, model: this._model, isSpecta: this.state.isSpectaPresentation }))))),
|
|
2071
|
+
React.createElement("div", { ref: this.controlsToolbarRef, className: "jgis-controls-toolbar" }))),
|
|
1793
2072
|
React.createElement(StatusBar, { jgisModel: this._model, loading: this.state.loadingLayer, projection: this.state.viewProjection, scale: this.state.scale }))));
|
|
1794
2073
|
}
|
|
1795
2074
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IJupyterGISModel } from '@jupytergis/schema';
|
|
1
|
+
import { IJGISLayerTree, IJupyterGISModel } from '@jupytergis/schema';
|
|
2
2
|
import { IStateDB } from '@jupyterlab/statedb';
|
|
3
3
|
import { CommandRegistry } from '@lumino/commands';
|
|
4
4
|
import React from 'react';
|
|
@@ -6,6 +6,7 @@ interface IBodyProps {
|
|
|
6
6
|
model: IJupyterGISModel;
|
|
7
7
|
commands: CommandRegistry;
|
|
8
8
|
state: IStateDB;
|
|
9
|
+
layerTree: IJGISLayerTree;
|
|
9
10
|
}
|
|
10
11
|
export declare const LayersBodyComponent: React.FC<IBodyProps>;
|
|
11
12
|
export {};
|
|
@@ -3,7 +3,7 @@ import { Button, LabIcon, caretDownIcon, caretRightIcon, } from '@jupyterlab/ui-
|
|
|
3
3
|
import React, { useEffect, useState, } from 'react';
|
|
4
4
|
import { CommandIDs, icons } from "../../constants";
|
|
5
5
|
import { useGetSymbology } from "../../dialogs/symbology/hooks/useGetSymbology";
|
|
6
|
-
import { nonVisibilityIcon, visibilityIcon } from "../../icons";
|
|
6
|
+
import { nonVisibilityIcon, targetWithCenterIcon, visibilityIcon, } from "../../icons";
|
|
7
7
|
import { LegendItem } from './legendItem';
|
|
8
8
|
const LAYER_GROUP_CLASS = 'jp-gis-layerGroup';
|
|
9
9
|
const LAYER_GROUP_HEADER_CLASS = 'jp-gis-layerGroupHeader';
|
|
@@ -13,9 +13,10 @@ const LAYER_CLASS = 'jp-gis-layer';
|
|
|
13
13
|
const LAYER_TITLE_CLASS = 'jp-gis-layerTitle';
|
|
14
14
|
const LAYER_ICON_CLASS = 'jp-gis-layerIcon';
|
|
15
15
|
const LAYER_TEXT_CLASS = 'jp-gis-layerText data-jgis-keybinding';
|
|
16
|
+
const LAYER_SLIDE_NUMBER_CLASS = 'jp-gis-layerSlideNumber';
|
|
16
17
|
export const LayersBodyComponent = props => {
|
|
17
18
|
const model = props.model;
|
|
18
|
-
const [layerTree, setLayerTree] = useState(
|
|
19
|
+
const [layerTree, setLayerTree] = useState(props.layerTree || []);
|
|
19
20
|
const notifyCommands = () => {
|
|
20
21
|
// Notify commands that need updating
|
|
21
22
|
props.commands.notifyCommandChanged(CommandIDs.identify);
|
|
@@ -117,25 +118,13 @@ export const LayersBodyComponent = props => {
|
|
|
117
118
|
const onItemClick = ({ type, item, event }) => {
|
|
118
119
|
onSelect({ type, item, event });
|
|
119
120
|
};
|
|
120
|
-
|
|
121
|
-
* Listen to the layers and layer tree changes.
|
|
122
|
-
*/
|
|
121
|
+
// Update layerTree when prop changes
|
|
123
122
|
useEffect(() => {
|
|
124
|
-
|
|
125
|
-
setLayerTree(
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
updateLayers();
|
|
130
|
-
return () => {
|
|
131
|
-
model === null || model === void 0 ? void 0 : model.sharedModel.layersChanged.disconnect(updateLayers);
|
|
132
|
-
model === null || model === void 0 ? void 0 : model.sharedModel.layerTreeChanged.disconnect(updateLayers);
|
|
133
|
-
};
|
|
134
|
-
}, [model]);
|
|
135
|
-
return (React.createElement("div", { id: "jp-gis-layer-tree", onDrop: _onDrop, onDragOver: _onDragOver }, layerTree
|
|
136
|
-
.slice()
|
|
137
|
-
.reverse()
|
|
138
|
-
.map(layer => typeof layer === 'string' ? (React.createElement(LayerComponent, { key: layer, gisModel: model, layerId: layer, onClick: onItemClick })) : (React.createElement(LayerGroupComponent, { key: layer.name, gisModel: model, group: layer, onClick: onItemClick, state: props.state })))));
|
|
123
|
+
if (props.layerTree) {
|
|
124
|
+
setLayerTree(props.layerTree);
|
|
125
|
+
}
|
|
126
|
+
}, [props.layerTree]);
|
|
127
|
+
return (React.createElement("div", { id: "jp-gis-layer-tree", onDrop: _onDrop, onDragOver: _onDragOver }, layerTree.map(layer => typeof layer === 'string' ? (React.createElement(LayerComponent, { key: layer, gisModel: model, layerId: layer, onClick: onItemClick })) : (React.createElement(LayerGroupComponent, { key: layer.name, gisModel: model, group: layer, onClick: onItemClick, state: props.state })))));
|
|
139
128
|
};
|
|
140
129
|
/**
|
|
141
130
|
* The component to handle group of layers.
|
|
@@ -350,6 +339,23 @@ const LayerComponent = props => {
|
|
|
350
339
|
handleRenameCancel();
|
|
351
340
|
}
|
|
352
341
|
};
|
|
342
|
+
/**
|
|
343
|
+
* Set layer to current map view.
|
|
344
|
+
*/
|
|
345
|
+
const moveToExtent = () => {
|
|
346
|
+
gisModel === null || gisModel === void 0 ? void 0 : gisModel.centerOnPosition(layerId);
|
|
347
|
+
};
|
|
348
|
+
const getSlideNumber = () => {
|
|
349
|
+
if (!gisModel) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const { story } = gisModel.getSelectedStory();
|
|
353
|
+
if (!(story === null || story === void 0 ? void 0 : story.storySegments)) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
const slideNum = story.storySegments.indexOf(layerId) + 1;
|
|
357
|
+
return slideNum;
|
|
358
|
+
};
|
|
353
359
|
return (React.createElement("div", { className: `${LAYER_ITEM_CLASS} ${LAYER_CLASS}${selected ? ' jp-mod-selected' : ''}`, draggable: true, onDragStart: Private.onDragStart, onDragOver: Private.onDragOver, onDragEnd: Private.onDragEnd, "data-id": layerId, style: { display: 'flex', flexDirection: 'column' } },
|
|
354
360
|
React.createElement("div", { className: LAYER_TITLE_CLASS, onClick: setSelection, onContextMenu: setSelection, style: { display: 'flex' } },
|
|
355
361
|
hasSupportedSymbology && (React.createElement(Button, { minimal: true, onClick: e => {
|
|
@@ -357,8 +363,8 @@ const LayerComponent = props => {
|
|
|
357
363
|
setExpanded(v => !v);
|
|
358
364
|
}, title: expanded ? 'Hide legend' : 'Show legend' },
|
|
359
365
|
React.createElement(LabIcon.resolveReact, { icon: expanded ? caretDownIcon : caretRightIcon, tag: "span" }))),
|
|
360
|
-
React.createElement(Button, { title: layer.visible ? 'Hide layer' : 'Show layer', onClick: toggleVisibility, minimal: true },
|
|
361
|
-
React.createElement(LabIcon.resolveReact, { icon: layer.visible ? visibilityIcon : nonVisibilityIcon, className: `${LAYER_ICON_CLASS}${layer.visible ? '' : ' jp-gis-mod-hidden'}`, tag: "span" })),
|
|
366
|
+
layer.type === 'StorySegmentLayer' ? (React.createElement("span", { className: LAYER_SLIDE_NUMBER_CLASS, title: "Slide number" }, getSlideNumber())) : (React.createElement(Button, { title: layer.visible ? 'Hide layer' : 'Show layer', onClick: toggleVisibility, minimal: true },
|
|
367
|
+
React.createElement(LabIcon.resolveReact, { icon: layer.visible ? visibilityIcon : nonVisibilityIcon, className: `${LAYER_ICON_CLASS}${layer.visible ? '' : ' jp-gis-mod-hidden'}`, tag: "span" }))),
|
|
362
368
|
icons.has(layer.type) && (React.createElement(LabIcon.resolveReact, Object.assign({}, icons.get(layer.type), { className: LAYER_ICON_CLASS }))),
|
|
363
369
|
isEditing ? (React.createElement("input", { type: "text", value: editValue, onChange: e => setEditValue(e.target.value), onKeyDown: handleRenameKeyDown, onBlur: handleRenameSave, className: LAYER_TEXT_CLASS, style: {
|
|
364
370
|
flex: 1,
|
|
@@ -367,7 +373,9 @@ const LayerComponent = props => {
|
|
|
367
373
|
padding: '2px 4px',
|
|
368
374
|
fontSize: 'inherit',
|
|
369
375
|
fontFamily: 'inherit',
|
|
370
|
-
}, autoFocus: true })) : (React.createElement("span", { id: id, className: LAYER_TEXT_CLASS, tabIndex: -2 }, name))
|
|
376
|
+
}, autoFocus: true })) : (React.createElement("span", { id: id, className: LAYER_TEXT_CLASS, tabIndex: -2 }, name)),
|
|
377
|
+
React.createElement(Button, { title: 'Move map to the extent of the layer', onClick: moveToExtent, minimal: true },
|
|
378
|
+
React.createElement(LabIcon.resolveReact, { icon: targetWithCenterIcon, className: LAYER_ICON_CLASS, tag: "span" }))),
|
|
371
379
|
expanded && gisModel && hasSupportedSymbology && (React.createElement("div", { style: { marginTop: 6, width: '100%' } },
|
|
372
380
|
React.createElement(LegendItem, { layerId: layerId, model: gisModel })))));
|
|
373
381
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Button } from '@jupyterlab/ui-components';
|
|
2
2
|
import { cloneDeep } from 'lodash';
|
|
3
3
|
import React, { useEffect, useRef, useState } from 'react';
|
|
4
|
-
import { debounce, loadFile } from "
|
|
4
|
+
import { debounce, loadFile } from "../../tools";
|
|
5
5
|
import FilterRow from './FilterRow';
|
|
6
6
|
const FilterComponent = ({ model }) => {
|
|
7
7
|
const featuresInLayerRef = useRef({});
|