@jupytergis/base 0.3.0 → 0.4.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.
Files changed (76) hide show
  1. package/lib/annotations/components/Annotation.js +1 -1
  2. package/lib/annotations/model.d.ts +6 -7
  3. package/lib/annotations/model.js +15 -15
  4. package/lib/commands.d.ts +2 -3
  5. package/lib/commands.js +117 -62
  6. package/lib/constants.d.ts +2 -0
  7. package/lib/constants.js +4 -1
  8. package/lib/dialogs/formdialog.js +2 -2
  9. package/lib/dialogs/layerBrowserDialog.d.ts +4 -5
  10. package/lib/dialogs/layerBrowserDialog.js +9 -9
  11. package/lib/dialogs/symbology/hooks/useGetBandInfo.d.ts +1 -2
  12. package/lib/dialogs/symbology/hooks/useGetBandInfo.js +11 -6
  13. package/lib/dialogs/symbology/hooks/useGetProperties.d.ts +1 -1
  14. package/lib/dialogs/symbology/hooks/useGetProperties.js +6 -8
  15. package/lib/dialogs/symbology/symbologyDialog.d.ts +2 -3
  16. package/lib/dialogs/symbology/symbologyDialog.js +10 -9
  17. package/lib/dialogs/symbology/tiff_layer/TiffRendering.d.ts +1 -1
  18. package/lib/dialogs/symbology/tiff_layer/TiffRendering.js +6 -6
  19. package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.d.ts +1 -1
  20. package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.js +5 -4
  21. package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.d.ts +1 -1
  22. package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +8 -7
  23. package/lib/dialogs/symbology/vector_layer/VectorRendering.d.ts +1 -1
  24. package/lib/dialogs/symbology/vector_layer/VectorRendering.js +18 -13
  25. package/lib/dialogs/symbology/vector_layer/types/Categorized.d.ts +1 -1
  26. package/lib/dialogs/symbology/vector_layer/types/Categorized.js +30 -19
  27. package/lib/dialogs/symbology/vector_layer/types/Graduated.d.ts +1 -1
  28. package/lib/dialogs/symbology/vector_layer/types/Graduated.js +16 -13
  29. package/lib/dialogs/symbology/vector_layer/types/Heatmap.d.ts +4 -0
  30. package/lib/dialogs/symbology/vector_layer/types/Heatmap.js +77 -0
  31. package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.d.ts +1 -1
  32. package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.js +4 -3
  33. package/lib/formbuilder/creationform.d.ts +1 -2
  34. package/lib/formbuilder/creationform.js +4 -4
  35. package/lib/formbuilder/editform.d.ts +1 -2
  36. package/lib/formbuilder/editform.js +7 -7
  37. package/lib/formbuilder/formselectors.js +5 -2
  38. package/lib/formbuilder/objectform/baseform.d.ts +3 -4
  39. package/lib/formbuilder/objectform/baseform.js +2 -2
  40. package/lib/formbuilder/objectform/fileselectorwidget.js +13 -6
  41. package/lib/formbuilder/objectform/geotiffsource.d.ts +5 -1
  42. package/lib/formbuilder/objectform/geotiffsource.js +64 -18
  43. package/lib/formbuilder/objectform/heatmapLayerForm.d.ts +11 -0
  44. package/lib/formbuilder/objectform/heatmapLayerForm.js +60 -0
  45. package/lib/formbuilder/objectform/vectorlayerform.d.ts +0 -2
  46. package/lib/formbuilder/objectform/vectorlayerform.js +0 -59
  47. package/lib/mainview/TemporalSlider.d.ts +8 -0
  48. package/lib/mainview/TemporalSlider.js +303 -0
  49. package/lib/mainview/mainView.d.ts +25 -4
  50. package/lib/mainview/mainView.js +213 -75
  51. package/lib/mainview/mainviewmodel.d.ts +4 -0
  52. package/lib/mainview/mainviewmodel.js +4 -0
  53. package/lib/mainview/mainviewwidget.d.ts +0 -2
  54. package/lib/mainview/mainviewwidget.js +0 -2
  55. package/lib/panelview/annotationPanel.js +5 -5
  56. package/lib/panelview/components/filter-panel/Filter.js +4 -25
  57. package/lib/panelview/components/identify-panel/IdentifyPanel.js +1 -1
  58. package/lib/panelview/components/layers.js +2 -2
  59. package/lib/panelview/components/sources.js +1 -1
  60. package/lib/panelview/leftpanel.d.ts +3 -0
  61. package/lib/panelview/leftpanel.js +5 -1
  62. package/lib/panelview/model.js +8 -8
  63. package/lib/panelview/objectproperties.js +10 -10
  64. package/lib/panelview/rightpanel.d.ts +1 -1
  65. package/lib/panelview/rightpanel.js +10 -10
  66. package/lib/toolbar/widget.d.ts +1 -1
  67. package/lib/toolbar/widget.js +44 -32
  68. package/lib/tools.d.ts +10 -6
  69. package/lib/tools.js +89 -15
  70. package/lib/types.d.ts +2 -0
  71. package/lib/widget.d.ts +29 -5
  72. package/lib/widget.js +41 -7
  73. package/package.json +4 -3
  74. package/style/base.css +10 -0
  75. package/style/symbologyDialog.css +7 -1
  76. package/style/temporalSlider.css +47 -0
@@ -104,8 +104,8 @@ function LayersBodyComponent(props) {
104
104
  */
105
105
  (_b = props.model) === null || _b === void 0 ? void 0 : _b.documentChanged.connect((_, widget) => {
106
106
  var _a;
107
- setModel(widget === null || widget === void 0 ? void 0 : widget.context.model);
108
- setLayerTree(((_a = widget === null || widget === void 0 ? void 0 : widget.context.model) === null || _a === void 0 ? void 0 : _a.getLayerTree()) || []);
107
+ setModel(widget === null || widget === void 0 ? void 0 : widget.model);
108
+ setLayerTree(((_a = widget === null || widget === void 0 ? void 0 : widget.model) === null || _a === void 0 ? void 0 : _a.getLayerTree()) || []);
109
109
  });
110
110
  return (React.createElement("div", { id: "jp-gis-layer-tree" }, layerTree
111
111
  .slice()
@@ -55,7 +55,7 @@ function SourcesBodyComponent(props) {
55
55
  * Update the model when it changes.
56
56
  */
57
57
  (_b = props.model) === null || _b === void 0 ? void 0 : _b.documentChanged.connect((_, widget) => {
58
- setModel(widget === null || widget === void 0 ? void 0 : widget.context.model);
58
+ setModel(widget === null || widget === void 0 ? void 0 : widget.model);
59
59
  });
60
60
  return (React.createElement("div", { id: "jp-gis-sources" }, sourceIds.map(sourceId => {
61
61
  return (React.createElement(SourceComponent, { key: `source-${sourceId}`, gisModel: model, sourceId: sourceId, onClick: onItemClick }));
@@ -4,6 +4,7 @@ import { SidePanel } from '@jupyterlab/ui-components';
4
4
  import { Message } from '@lumino/messaging';
5
5
  import { MouseEvent as ReactMouseEvent } from 'react';
6
6
  import { IControlPanelModel } from '../types';
7
+ import { CommandRegistry } from '@lumino/commands';
7
8
  /**
8
9
  * Options of the left panel widget.
9
10
  */
@@ -37,12 +38,14 @@ export declare class LeftPanelWidget extends SidePanel {
37
38
  private _lastSelectedNodeId;
38
39
  private _model;
39
40
  private _state;
41
+ private _commands;
40
42
  }
41
43
  export declare namespace LeftPanelWidget {
42
44
  interface IOptions {
43
45
  model: IControlPanelModel;
44
46
  tracker: IJupyterGISTracker;
45
47
  state: IStateDB;
48
+ commands: CommandRegistry;
46
49
  }
47
50
  interface IProps {
48
51
  filePath?: string;
@@ -3,6 +3,7 @@ import { LayersPanel } from './components/layers';
3
3
  import { SourcesPanel } from './components/sources';
4
4
  import { ControlPanelHeader } from './header';
5
5
  import { FilterPanel } from './components/filter-panel/Filter';
6
+ import { CommandIDs } from '../constants';
6
7
  export class LeftPanelWidget extends SidePanel {
7
8
  constructor(options) {
8
9
  super();
@@ -50,11 +51,13 @@ export class LeftPanelWidget extends SidePanel {
50
51
  const updatedSelectedValue = Object.assign(Object.assign({}, selectedValue), { [item]: { type, selectedNodeId: nodeId } });
51
52
  this._lastSelectedNodeId = nodeId;
52
53
  jGISModel.syncSelected(updatedSelectedValue, this.id);
54
+ this._commands.notifyCommandChanged(CommandIDs.temporalController);
53
55
  }
54
56
  };
55
57
  this.addClass('jGIS-sidepanel-widget');
56
58
  this._model = options.model;
57
59
  this._state = options.state;
60
+ this._commands = options.commands;
58
61
  const header = new ControlPanelHeader();
59
62
  this.header.addWidget(header);
60
63
  const sourcesPanel = new SourcesPanel({
@@ -81,7 +84,7 @@ export class LeftPanelWidget extends SidePanel {
81
84
  this.addWidget(filterPanel);
82
85
  options.tracker.currentChanged.connect((_, changed) => {
83
86
  if (changed) {
84
- header.title.label = changed.context.localPath;
87
+ header.title.label = changed.model.filePath;
85
88
  }
86
89
  else {
87
90
  header.title.label = '-';
@@ -129,5 +132,6 @@ export class LeftPanelWidget extends SidePanel {
129
132
  this._lastSelectedNodeId = nodeId;
130
133
  }
131
134
  (_b = (_a = this._model) === null || _a === void 0 ? void 0 : _a.jGISModel) === null || _b === void 0 ? void 0 : _b.syncSelected(selection, this.id);
135
+ this._commands.notifyCommandChanged(CommandIDs.temporalController);
132
136
  }
133
137
  }
@@ -8,23 +8,23 @@ export class ControlPanelModel {
8
8
  }
9
9
  get filePath() {
10
10
  var _a;
11
- return (_a = this._tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.context.localPath;
11
+ return (_a = this._tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model.filePath;
12
12
  }
13
13
  get jGISModel() {
14
14
  var _a;
15
- return (_a = this._tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.context.model;
15
+ return (_a = this._tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model;
16
16
  }
17
17
  get sharedModel() {
18
18
  var _a;
19
- return (_a = this._tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.context.model.sharedModel;
19
+ return (_a = this._tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model.sharedModel;
20
20
  }
21
21
  disconnect(f) {
22
22
  this._tracker.forEach(w => {
23
- w.context.model.sharedLayersChanged.disconnect(f);
24
- w.context.model.sharedSourcesChanged.disconnect(f);
25
- w.context.model.sharedOptionsChanged.disconnect(f);
23
+ w.model.sharedLayersChanged.disconnect(f);
24
+ w.model.sharedSourcesChanged.disconnect(f);
25
+ w.model.sharedOptionsChanged.disconnect(f);
26
26
  });
27
- this._tracker.forEach(w => w.context.model.themeChanged.disconnect(f));
28
- this._tracker.forEach(w => w.context.model.clientStateChanged.disconnect(f));
27
+ this._tracker.forEach(w => w.model.themeChanged.disconnect(f));
28
+ this._tracker.forEach(w => w.model.clientStateChanged.disconnect(f));
29
29
  }
30
30
  }
@@ -45,7 +45,7 @@ class ObjectPropertiesReact extends React.Component {
45
45
  }
46
46
  };
47
47
  this.state = {
48
- context: (_a = props.tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.context,
48
+ model: (_a = props.tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model,
49
49
  clientId: null,
50
50
  id: uuid()
51
51
  };
@@ -55,14 +55,14 @@ class ObjectPropertiesReact extends React.Component {
55
55
  if (changed) {
56
56
  this.props.cpModel.disconnect(this._sharedJGISModelChanged);
57
57
  this.props.cpModel.disconnect(this._onClientSharedStateChanged);
58
- changed.context.model.sharedLayersChanged.connect(this._sharedJGISModelChanged);
59
- changed.context.model.sharedSourcesChanged.connect(this._sharedJGISModelChanged);
60
- changed.context.model.clientStateChanged.connect(this._onClientSharedStateChanged);
61
- this.setState(old => (Object.assign(Object.assign({}, old), { context: changed.context, filePath: changed.context.localPath, clientId: changed.context.model.getClientId() })));
58
+ changed.model.sharedLayersChanged.connect(this._sharedJGISModelChanged);
59
+ changed.model.sharedSourcesChanged.connect(this._sharedJGISModelChanged);
60
+ changed.model.clientStateChanged.connect(this._onClientSharedStateChanged);
61
+ this.setState(old => (Object.assign(Object.assign({}, old), { model: changed.model, filePath: changed.model.filePath, clientId: changed.model.getClientId() })));
62
62
  }
63
63
  else {
64
64
  this.setState({
65
- context: undefined,
65
+ model: undefined,
66
66
  selectedObject: undefined
67
67
  });
68
68
  }
@@ -71,22 +71,22 @@ class ObjectPropertiesReact extends React.Component {
71
71
  render() {
72
72
  var _a;
73
73
  const selectedObject = this.state.selectedObject;
74
- if (!selectedObject || !this.state.context) {
74
+ if (!selectedObject || !this.state.model) {
75
75
  return React.createElement("div", null);
76
76
  }
77
77
  let layerId = undefined;
78
78
  let sourceId = undefined;
79
- const layer = this.state.context.model.getLayer(selectedObject);
79
+ const layer = this.state.model.getLayer(selectedObject);
80
80
  if (layer) {
81
81
  layerId = selectedObject;
82
82
  sourceId = (_a = layer.parameters) === null || _a === void 0 ? void 0 : _a.source;
83
83
  }
84
84
  else {
85
- const source = this.state.context.model.getSource(selectedObject);
85
+ const source = this.state.model.getSource(selectedObject);
86
86
  if (source) {
87
87
  sourceId = selectedObject;
88
88
  }
89
89
  }
90
- return (React.createElement(EditForm, { layer: layerId, source: sourceId, formSchemaRegistry: this.props.formSchemaRegistry, context: this.state.context }));
90
+ return (React.createElement(EditForm, { layer: layerId, source: sourceId, formSchemaRegistry: this.props.formSchemaRegistry, model: this.state.model }));
91
91
  }
92
92
  }
@@ -4,7 +4,7 @@ import { IControlPanelModel } from '../types';
4
4
  export declare class RightPanelWidget extends SidePanel {
5
5
  constructor(options: RightPanelWidget.IOptions);
6
6
  dispose(): void;
7
- private _currentContext;
7
+ private _currentModel;
8
8
  private _model;
9
9
  private _annotationModel;
10
10
  }
@@ -31,12 +31,12 @@ export class RightPanelWidget extends SidePanel {
31
31
  this.addWidget(identifyPanel);
32
32
  this._model.documentChanged.connect((_, changed) => {
33
33
  if (changed) {
34
- if (changed.context.model.sharedModel.editable) {
35
- header.title.label = changed.context.localPath;
34
+ if (changed.model.sharedModel.editable) {
35
+ header.title.label = changed.model.filePath;
36
36
  properties.show();
37
37
  }
38
38
  else {
39
- header.title.label = `${changed.context.localPath} - Read Only`;
39
+ header.title.label = `${changed.model.filePath} - Read Only`;
40
40
  properties.hide();
41
41
  }
42
42
  }
@@ -47,16 +47,16 @@ export class RightPanelWidget extends SidePanel {
47
47
  options.tracker.currentChanged.connect(async (_, changed) => {
48
48
  var _a;
49
49
  if (changed) {
50
- this._currentContext = changed.context;
51
- header.title.label = this._currentContext.localPath;
52
- this._annotationModel.context =
53
- ((_a = options.tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.context) || undefined;
54
- await changed.context.ready;
50
+ this._currentModel = changed.model;
51
+ header.title.label = this._currentModel.filePath;
52
+ this._annotationModel.model =
53
+ ((_a = options.tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model) || undefined;
54
+ // await changed.context.ready;
55
55
  }
56
56
  else {
57
57
  header.title.label = '-';
58
- this._currentContext = null;
59
- this._annotationModel.context = undefined;
58
+ this._currentModel = null;
59
+ this._annotationModel.model = undefined;
60
60
  }
61
61
  });
62
62
  }
@@ -1,5 +1,5 @@
1
1
  import { IJGISExternalCommand, JupyterGISModel } from '@jupytergis/schema';
2
- import { Toolbar, ReactiveToolbar } from '@jupyterlab/ui-components';
2
+ import { ReactiveToolbar, Toolbar } from '@jupyterlab/ui-components';
3
3
  import { CommandRegistry } from '@lumino/commands';
4
4
  import { Widget } from '@lumino/widgets';
5
5
  export declare const TOOLBAR_SEPARATOR_CLASS = "jGIS-Toolbar-Separator";
@@ -1,8 +1,9 @@
1
1
  import { CommandToolbarButton } from '@jupyterlab/apputils';
2
- import { ReactWidget, ReactiveToolbar, ToolbarButton, addIcon, redoIcon, undoIcon, terminalIcon } from '@jupyterlab/ui-components';
2
+ import { MenuSvg, ReactWidget, ReactiveToolbar, ToolbarButton, addIcon, redoIcon, terminalIcon, undoIcon } from '@jupyterlab/ui-components';
3
3
  import { Menu, Widget } from '@lumino/widgets';
4
4
  import * as React from 'react';
5
5
  import { CommandIDs } from '../constants';
6
+ import { rasterIcon } from '../icons';
6
7
  import { UsersItem } from './usertoolbaritem';
7
8
  export const TOOLBAR_SEPARATOR_CLASS = 'jGIS-Toolbar-Separator';
8
9
  export const TOOLBAR_GROUPNAME_CLASS = 'jGIS-Toolbar-GroupName';
@@ -55,52 +56,63 @@ export class ToolbarWidget extends ReactiveToolbar {
55
56
  label: '',
56
57
  commands: options.commands
57
58
  }));
58
- const NewButton = new ToolbarButton({
59
+ // vector sub menu
60
+ const vectorSubMenu = new Menu({ commands: options.commands });
61
+ vectorSubMenu.title.label = 'Add Vector Layer';
62
+ vectorSubMenu.title.iconClass = 'fa fa-vector-square';
63
+ vectorSubMenu.id = 'jp-gis-toolbar-vector-menu';
64
+ vectorSubMenu.addItem({
65
+ type: 'command',
66
+ command: CommandIDs.newGeoJSONEntry
67
+ });
68
+ vectorSubMenu.addItem({
69
+ type: 'command',
70
+ command: CommandIDs.newShapefileLayer
71
+ });
72
+ //raster submenu
73
+ const rasterSubMenu = new Menu({ commands: options.commands });
74
+ rasterSubMenu.title.label = 'Add Raster Layer';
75
+ rasterSubMenu.title.icon = rasterIcon;
76
+ rasterSubMenu.id = 'jp-gis-toolbar-raster-menu';
77
+ rasterSubMenu.addItem({
78
+ type: 'command',
79
+ command: CommandIDs.newHillshadeEntry
80
+ });
81
+ rasterSubMenu.addItem({
82
+ type: 'command',
83
+ command: CommandIDs.newImageEntry
84
+ });
85
+ rasterSubMenu.addItem({
86
+ type: 'command',
87
+ command: CommandIDs.newGeoTiffEntry
88
+ });
89
+ const NewSubMenu = new MenuSvg({ commands: options.commands });
90
+ NewSubMenu.title.label = 'Add Layer';
91
+ NewSubMenu.addItem({ type: 'submenu', submenu: rasterSubMenu });
92
+ NewSubMenu.addItem({ type: 'submenu', submenu: vectorSubMenu });
93
+ const NewEntryButton = new ToolbarButton({
59
94
  icon: addIcon,
60
95
  noFocusOnClick: false,
61
96
  onClick: () => {
62
97
  if (!options.commands) {
63
98
  return;
64
99
  }
65
- const bbox = NewButton.node.getBoundingClientRect();
66
- const NewSubMenu = new Menu({ commands: options.commands });
67
- NewSubMenu.title.label = 'New Layer';
68
- NewSubMenu.addItem({
69
- type: 'command',
70
- command: CommandIDs.newHillshadeEntry
71
- });
72
- NewSubMenu.addItem({
73
- type: 'separator'
74
- });
75
- NewSubMenu.addItem({
76
- type: 'command',
77
- command: CommandIDs.newImageEntry
78
- });
79
- NewSubMenu.addItem({
80
- type: 'separator'
81
- });
82
- NewSubMenu.addItem({
83
- type: 'command',
84
- command: CommandIDs.newShapefileLayer
85
- });
86
- NewSubMenu.addItem({
87
- type: 'command',
88
- command: CommandIDs.newGeoTiffEntry
89
- });
90
- NewSubMenu.addItem({
91
- type: 'command',
92
- command: CommandIDs.newGeoJSONEntry
93
- });
100
+ const bbox = NewEntryButton.node.getBoundingClientRect();
94
101
  NewSubMenu.open(bbox.x, bbox.bottom);
95
102
  }
96
103
  });
97
- this.addItem('New', NewButton);
104
+ this.addItem('New', NewEntryButton);
98
105
  this.addItem('separator2', new Separator());
99
106
  this.addItem('identify', new CommandToolbarButton({
100
107
  id: CommandIDs.identify,
101
108
  label: '',
102
109
  commands: options.commands
103
110
  }));
111
+ this.addItem('temporalController', new CommandToolbarButton({
112
+ id: CommandIDs.temporalController,
113
+ label: '',
114
+ commands: options.commands
115
+ }));
104
116
  this.addItem('spacer', ReactiveToolbar.createSpacerItem());
105
117
  // Users
106
118
  this.addItem('users', ReactWidget.create(React.createElement(UsersItem, { model: options.model })));
package/lib/tools.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { VectorTile } from '@mapbox/vector-tile';
2
+ import { Contents } from '@jupyterlab/services';
2
3
  import { IDict, IJGISLayerBrowserRegistry, IJGISOptions, IJGISSource, IJupyterGISModel } from '@jupytergis/schema';
3
4
  export declare const debounce: (func: CallableFunction, timeout?: number) => CallableFunction;
4
5
  export declare function throttle<T extends (...args: any[]) => void>(callback: T, delay?: number): T;
@@ -22,7 +23,6 @@ export declare function deepCopy<T = IDict<any>>(value: T): T;
22
23
  */
23
24
  export declare function createDefaultLayerRegistry(layerBrowserRegistry: IJGISLayerBrowserRegistry): void;
24
25
  export declare function getLayerTileInfo(tileUrl: string, mapOptions: Pick<IJGISOptions, 'latitude' | 'longitude' | 'extent' | 'zoom'>, urlParameters?: IDict<string>): Promise<VectorTile>;
25
- export declare function getSourceLayerNames(tileUrl: string, urlParameters?: IDict<string>): Promise<string[]>;
26
26
  export interface IParsedStyle {
27
27
  fillColor: string;
28
28
  strokeColor: string;
@@ -46,24 +46,27 @@ export declare const openDatabase: () => Promise<IDBDatabase>;
46
46
  * @param metadata metadata of file.
47
47
  * @returns A promise that resolves once the data is successfully saved.
48
48
  */
49
- export declare const saveToIndexedDB: (key: string, file: Blob, metadata: any) => Promise<void>;
49
+ export declare const saveToIndexedDB: (key: string, file: any, metadata?: any | undefined) => Promise<void>;
50
50
  /**
51
51
  * Retrieve a file and its metadata from the IndexedDB database.
52
52
  *
53
53
  * @param key fileID (sourceUrl).
54
54
  * @returns A promise that resolves to the stored data object or undefined.
55
55
  */
56
- export declare const getFromIndexedDB: (key: string) => Promise<any>;
56
+ export declare const getFromIndexedDB: (key: string) => Promise<{
57
+ file: any;
58
+ metadata?: any | undefined;
59
+ } | undefined>;
57
60
  /**
58
61
  * Load a GeoTIFF file from IndexedDB database cache or fetch it .
59
62
  *
60
63
  * @param sourceInfo object containing the URL of the GeoTIFF file.
61
64
  * @returns A promise that resolves to the file as a Blob, or undefined .
62
65
  */
63
- export declare const loadGeoTIFFWithCache: (sourceInfo: {
66
+ export declare const loadGeoTiff: (sourceInfo: {
64
67
  url?: string | undefined;
65
- }) => Promise<{
66
- file: Blob;
68
+ }, file?: Contents.IModel | null) => Promise<{
69
+ file: any;
67
70
  metadata: any;
68
71
  sourceUrl: string;
69
72
  } | null>;
@@ -106,3 +109,4 @@ export declare const getMimeType: (filename: string) => string;
106
109
  * @returns An ArrayBuffer.
107
110
  */
108
111
  export declare const stringToArrayBuffer: (content: string) => Promise<ArrayBuffer>;
112
+ export declare const getNumericFeatureAttributes: (featureProperties: Record<string, Set<any>>) => Record<string, Set<number>>;
package/lib/tools.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import Protobuf from 'pbf';
2
2
  import { VectorTile } from '@mapbox/vector-tile';
3
- import { URLExt } from '@jupyterlab/coreutils';
3
+ import { PathExt, URLExt } from '@jupyterlab/coreutils';
4
4
  import { ServerConnection } from '@jupyterlab/services';
5
+ import { showErrorMessage } from '@jupyterlab/apputils';
5
6
  import * as d3Color from 'd3-color';
6
- import { PathExt } from '@jupyterlab/coreutils';
7
7
  import shp from 'shpjs';
8
8
  import RASTER_LAYER_GALLERY from '../rasterlayer_gallery/raster_layer_gallery.json';
9
9
  import { getGdal } from './gdal';
@@ -215,11 +215,6 @@ export async function getLayerTileInfo(tileUrl, mapOptions, urlParameters) {
215
215
  const tile = new VectorTile(new Protobuf(arrayBuffer));
216
216
  return tile;
217
217
  }
218
- export async function getSourceLayerNames(tileUrl, urlParameters) {
219
- const tile = await getLayerTileInfo(tileUrl, { latitude: 0, longitude: 0, zoom: 0, extent: [] }, urlParameters);
220
- const layerNames = Object.keys(tile.layers);
221
- return layerNames;
222
- }
223
218
  export function parseColor(type, style) {
224
219
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
225
220
  if (!type || !style) {
@@ -312,23 +307,36 @@ export const getFromIndexedDB = async (key) => {
312
307
  * @param sourceInfo object containing the URL of the GeoTIFF file.
313
308
  * @returns A promise that resolves to the file as a Blob, or undefined .
314
309
  */
315
- export const loadGeoTIFFWithCache = async (sourceInfo) => {
310
+ export const loadGeoTiff = async (sourceInfo, file) => {
316
311
  if (!(sourceInfo === null || sourceInfo === void 0 ? void 0 : sourceInfo.url)) {
317
312
  return null;
318
313
  }
314
+ const mimeType = getMimeType(sourceInfo.url);
315
+ if (!mimeType || !mimeType.startsWith('image/tiff')) {
316
+ throw new Error('Invalid file type. Expected GeoTIFF (image/tiff).');
317
+ }
319
318
  const cachedData = await getFromIndexedDB(sourceInfo.url);
320
319
  if (cachedData) {
321
320
  return {
322
- file: new Blob([cachedData.file]),
321
+ file: cachedData.file,
323
322
  metadata: cachedData.metadata,
324
323
  sourceUrl: sourceInfo.url
325
324
  };
326
325
  }
327
- const response = await fetch(sourceInfo.url);
328
- const fileBlob = await response.blob();
329
- const file = new File([fileBlob], 'loaded.tif');
326
+ let fileBlob;
327
+ if (!file) {
328
+ const response = await fetch(`/jupytergis_core/proxy?url=${sourceInfo.url}`);
329
+ if (!response.ok) {
330
+ throw new Error(`Failed to fetch file. Status: ${response.status}`);
331
+ }
332
+ fileBlob = await response.blob();
333
+ }
334
+ else {
335
+ fileBlob = await base64ToBlob(file.content, mimeType);
336
+ }
337
+ const geotiff = new File([fileBlob], 'loaded.tif');
330
338
  const Gdal = await getGdal();
331
- const result = await Gdal.open(file);
339
+ const result = await Gdal.open(geotiff);
332
340
  const tifDataset = result.datasets[0];
333
341
  const metadata = await Gdal.gdalinfo(tifDataset, ['-stats']);
334
342
  Gdal.close(tifDataset);
@@ -369,24 +377,58 @@ export const loadFile = async (fileInfo) => {
369
377
  }
370
378
  }
371
379
  case 'ShapefileSource': {
380
+ const cached = await getFromIndexedDB(filepath);
381
+ if (cached) {
382
+ return cached.file;
383
+ }
384
+ // First trying a direct fetch
385
+ try {
386
+ const response = await fetch(filepath);
387
+ const arrayBuffer = await response.arrayBuffer();
388
+ const geojson = await shp(arrayBuffer);
389
+ await saveToIndexedDB(filepath, geojson);
390
+ return geojson;
391
+ }
392
+ catch (error) {
393
+ console.warn('Cannot load shapefile from ${filepath}: ${error}');
394
+ }
395
+ // Trying through our proxy server
372
396
  try {
373
397
  const response = await fetch(`/jupytergis_core/proxy?url=${filepath}`);
374
398
  const arrayBuffer = await response.arrayBuffer();
375
399
  const geojson = await shp(arrayBuffer);
400
+ await saveToIndexedDB(filepath, geojson);
376
401
  return geojson;
377
402
  }
378
403
  catch (error) {
379
- console.error('Error loading remote shapefile:', error);
380
- throw error;
404
+ console.warn('Cannot communicate with the JupyterGIS proxy server:', error);
381
405
  }
406
+ // Trying through an external proxy server
407
+ try {
408
+ const response = await fetch(`https://corsproxy.io/?url=${filepath}`);
409
+ const arrayBuffer = await response.arrayBuffer();
410
+ const geojson = await shp(arrayBuffer);
411
+ await saveToIndexedDB(filepath, geojson);
412
+ return geojson;
413
+ }
414
+ catch (error) {
415
+ console.warn('Cannot communicate with external proxy server', error);
416
+ }
417
+ showErrorMessage('Network error', 'Failed to fetch ${filepath}');
418
+ throw new Error('Failed to fetch ${filepath}');
382
419
  }
383
420
  case 'GeoJSONSource': {
421
+ const cached = await getFromIndexedDB(filepath);
422
+ if (cached) {
423
+ return cached.file;
424
+ }
384
425
  try {
385
426
  const response = await fetch(`/jupytergis_core/proxy?url=${filepath}`);
386
427
  if (!response.ok) {
387
428
  throw new Error(`Failed to fetch GeoJSON from URL: ${filepath}`);
388
429
  }
389
430
  const geojson = await response.json();
431
+ await saveToIndexedDB(filepath, geojson);
390
432
  return geojson;
391
433
  }
392
434
  catch (error) {
@@ -394,6 +436,16 @@ export const loadFile = async (fileInfo) => {
394
436
  throw error;
395
437
  }
396
438
  }
439
+ case 'GeoTiffSource': {
440
+ try {
441
+ const tiff = loadGeoTiff({ url: filepath });
442
+ return tiff;
443
+ }
444
+ catch (error) {
445
+ console.error('Error loading remote GeoTIFF:', error);
446
+ throw error;
447
+ }
448
+ }
397
449
  default: {
398
450
  throw new Error(`Unsupported URL handling for source type: ${type}`);
399
451
  }
@@ -441,6 +493,15 @@ export const loadFile = async (fileInfo) => {
441
493
  throw new Error('Invalid file format for image content.');
442
494
  }
443
495
  }
496
+ case 'GeoTiffSource': {
497
+ if (typeof file.content === 'string') {
498
+ const tiff = loadGeoTiff({ url: filepath }, file);
499
+ return tiff;
500
+ }
501
+ else {
502
+ throw new Error('Invalid file format for tiff content.');
503
+ }
504
+ }
444
505
  default: {
445
506
  throw new Error(`Unsupported source type: ${type}`);
446
507
  }
@@ -645,3 +706,16 @@ export const stringToArrayBuffer = async (content) => {
645
706
  const base64Response = await fetch(`data:application/octet-stream;base64,${content}`);
646
707
  return await base64Response.arrayBuffer();
647
708
  };
709
+ export const getNumericFeatureAttributes = (featureProperties) => {
710
+ // We only want number values here
711
+ const filteredRecord = {};
712
+ for (const [key, set] of Object.entries(featureProperties)) {
713
+ const firstValue = set.values().next().value;
714
+ // Check if the first value is a string that cannot be parsed as a number
715
+ const isInvalidString = typeof firstValue === 'string' && isNaN(Number(firstValue));
716
+ if (!isInvalidString) {
717
+ filteredRecord[key] = set;
718
+ }
719
+ }
720
+ return filteredRecord;
721
+ };
package/lib/types.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import { IDict, IJupyterGISDoc, IJupyterGISModel, IJupyterGISTracker, IJupyterGISWidget } from '@jupytergis/schema';
2
+ import { WidgetTracker } from '@jupyterlab/apputils';
2
3
  import { ISignal } from '@lumino/signaling';
3
4
  import { Map } from 'ol';
4
5
  export { IDict };
5
6
  export type ValueOf<T> = T[keyof T];
7
+ export type JupyterGISTracker = WidgetTracker<IJupyterGISWidget>;
6
8
  export interface IControlPanelModel {
7
9
  disconnect(f: any): void;
8
10
  documentChanged: ISignal<IJupyterGISTracker, IJupyterGISWidget | null>;
package/lib/widget.d.ts CHANGED
@@ -1,25 +1,48 @@
1
- import { JSONValue } from '@lumino/coreutils';
2
- import { ISignal } from '@lumino/signaling';
3
- import { SplitPanel } from '@lumino/widgets';
1
+ import { MainAreaWidget } from '@jupyterlab/apputils';
4
2
  import { ConsolePanel, IConsoleTracker } from '@jupyterlab/console';
5
3
  import { DocumentWidget } from '@jupyterlab/docregistry';
6
4
  import { IObservableMap, ObservableMap } from '@jupyterlab/observables';
7
- import { IJupyterGISModel, IJupyterGISWidget } from '@jupytergis/schema';
5
+ import { JSONValue } from '@lumino/coreutils';
6
+ import { ISignal } from '@lumino/signaling';
7
+ import { SplitPanel } from '@lumino/widgets';
8
+ import { IJupyterGISModel, IJupyterGISOutputWidget, IJupyterGISDocumentWidget } from '@jupytergis/schema';
8
9
  import { JupyterGISMainViewPanel } from './mainview';
9
10
  import { MainViewModel } from './mainview/mainviewmodel';
10
11
  import { ConsoleView } from './console';
11
- export declare class JupyterGISWidget extends DocumentWidget<JupyterGISPanel, IJupyterGISModel> implements IJupyterGISWidget {
12
+ import { CommandRegistry } from '@lumino/commands';
13
+ export type JupyterGISWidget = JupyterGISDocumentWidget | JupyterGISOutputWidget;
14
+ export declare class JupyterGISDocumentWidget extends DocumentWidget<JupyterGISPanel, IJupyterGISModel> implements IJupyterGISDocumentWidget {
12
15
  constructor(options: DocumentWidget.IOptions<JupyterGISPanel, IJupyterGISModel>);
16
+ get model(): IJupyterGISModel;
13
17
  /**
14
18
  * Dispose of the resources held by the widget.
15
19
  */
16
20
  dispose(): void;
17
21
  onResize: (msg: any) => void;
18
22
  }
23
+ /**
24
+ * A main area widget designed to be used as Notebook cell output widget, to ease the
25
+ * integration of toolbar and tracking.
26
+ */
27
+ export declare class JupyterGISOutputWidget extends MainAreaWidget<JupyterGISPanel> implements IJupyterGISOutputWidget {
28
+ constructor(options: JupyterGISOutputWidget.IOptions);
29
+ /**
30
+ * Dispose of the resources held by the widget.
31
+ */
32
+ dispose(): void;
33
+ readonly model: IJupyterGISModel;
34
+ readonly resizeObserver: ResizeObserver;
35
+ }
36
+ export declare namespace JupyterGISOutputWidget {
37
+ interface IOptions extends MainAreaWidget.IOptions<JupyterGISPanel> {
38
+ model: IJupyterGISModel;
39
+ }
40
+ }
19
41
  export declare class JupyterGISPanel extends SplitPanel {
20
42
  constructor(options: JupyterGISPanel.IOptions);
21
43
  _initModel(options: {
22
44
  model: IJupyterGISModel;
45
+ commandRegistry: CommandRegistry;
23
46
  }): void;
24
47
  _initView(): void;
25
48
  get jupyterGISMainViewPanel(): JupyterGISMainViewPanel;
@@ -44,6 +67,7 @@ export declare class JupyterGISPanel extends SplitPanel {
44
67
  export declare namespace JupyterGISPanel {
45
68
  interface IOptions extends Partial<ConsoleView.IOptions> {
46
69
  model: IJupyterGISModel;
70
+ commandRegistry: CommandRegistry;
47
71
  consoleTracker?: IConsoleTracker;
48
72
  }
49
73
  }