@jupytergis/base 0.2.1 → 0.3.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 (49) hide show
  1. package/lib/annotations/components/Annotation.js +1 -1
  2. package/lib/commands.js +30 -1
  3. package/lib/constants.d.ts +1 -0
  4. package/lib/constants.js +1 -0
  5. package/lib/dialogs/formdialog.d.ts +5 -0
  6. package/lib/dialogs/formdialog.js +2 -2
  7. package/lib/dialogs/symbology/components/color_ramp/ModeSelectRow.js +2 -1
  8. package/lib/dialogs/symbology/hooks/useGetBandInfo.d.ts +27 -0
  9. package/lib/dialogs/symbology/hooks/useGetBandInfo.js +59 -0
  10. package/lib/dialogs/symbology/hooks/useGetProperties.js +6 -1
  11. package/lib/dialogs/symbology/tiff_layer/TiffRendering.d.ts +1 -1
  12. package/lib/dialogs/symbology/tiff_layer/TiffRendering.js +14 -1
  13. package/lib/dialogs/symbology/tiff_layer/components/BandRow.d.ts +16 -3
  14. package/lib/dialogs/symbology/tiff_layer/components/BandRow.js +21 -7
  15. package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.d.ts +4 -0
  16. package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.js +84 -0
  17. package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.d.ts +0 -19
  18. package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +18 -59
  19. package/lib/formbuilder/creationform.d.ts +5 -0
  20. package/lib/formbuilder/creationform.js +2 -2
  21. package/lib/formbuilder/editform.d.ts +1 -0
  22. package/lib/formbuilder/editform.js +8 -3
  23. package/lib/formbuilder/formselectors.js +7 -0
  24. package/lib/formbuilder/objectform/baseform.d.ts +10 -0
  25. package/lib/formbuilder/objectform/baseform.js +39 -0
  26. package/lib/formbuilder/objectform/fileselectorwidget.d.ts +2 -0
  27. package/lib/formbuilder/objectform/fileselectorwidget.js +81 -0
  28. package/lib/formbuilder/objectform/geojsonsource.d.ts +5 -7
  29. package/lib/formbuilder/objectform/geojsonsource.js +8 -24
  30. package/lib/formbuilder/objectform/layerform.d.ts +2 -0
  31. package/lib/formbuilder/objectform/layerform.js +6 -0
  32. package/lib/formbuilder/objectform/pathbasedsource.d.ts +19 -0
  33. package/lib/formbuilder/objectform/pathbasedsource.js +98 -0
  34. package/lib/icons.d.ts +1 -0
  35. package/lib/icons.js +5 -0
  36. package/lib/keybindings.json +62 -0
  37. package/lib/mainview/mainView.d.ts +22 -5
  38. package/lib/mainview/mainView.js +224 -75
  39. package/lib/panelview/components/filter-panel/Filter.js +6 -1
  40. package/lib/statusbar/StatusBar.d.ts +13 -0
  41. package/lib/statusbar/StatusBar.js +52 -0
  42. package/lib/toolbar/widget.js +0 -5
  43. package/lib/tools.d.ts +40 -1
  44. package/lib/tools.js +308 -0
  45. package/package.json +16 -5
  46. package/style/base.css +1 -0
  47. package/style/icons/logo_mini_qgz.svg +31 -0
  48. package/style/leftPanel.css +8 -0
  49. package/style/statusBar.css +16 -0
@@ -1,10 +1,15 @@
1
1
  import { deepCopy } from '../tools';
2
2
  import * as React from 'react';
3
3
  import { getLayerTypeForm, getSourceTypeForm } from './formselectors';
4
+ import { Signal } from '@lumino/signaling';
4
5
  /**
5
6
  * Form for editing a source, a layer or both at the same time
6
7
  */
7
8
  export class EditForm extends React.Component {
9
+ constructor() {
10
+ super(...arguments);
11
+ this.sourceFormChangedSignal = new Signal(this);
12
+ }
8
13
  async syncObjectProperties(id, properties) {
9
14
  if (!id) {
10
15
  return;
@@ -47,14 +52,14 @@ export class EditForm extends React.Component {
47
52
  }
48
53
  return (React.createElement("div", null,
49
54
  this.props.layer && LayerForm && (React.createElement("div", null,
50
- React.createElement("h3", null, "Layer Properties"),
55
+ React.createElement("h3", { style: { paddingLeft: '5px' } }, "Layer Properties"),
51
56
  React.createElement(LayerForm, { formContext: "create", sourceType: (source === null || source === void 0 ? void 0 : source.type) || 'RasterSource', model: this.props.context.model, filePath: `${this.props.context.path}::panel`, schema: layerSchema, sourceData: layerData, syncData: (properties) => {
52
57
  this.syncObjectProperties(this.props.layer, properties);
53
58
  } }))),
54
59
  this.props.source && SourceForm && (React.createElement("div", null,
55
- React.createElement("h3", null, "Source Properties"),
60
+ React.createElement("h3", { style: { paddingLeft: '5px' } }, "Source Properties"),
56
61
  React.createElement(SourceForm, { formContext: "create", model: this.props.context.model, filePath: `${this.props.context.path}::panel`, schema: sourceSchema, sourceData: sourceData, syncData: (properties) => {
57
62
  this.syncObjectProperties(this.props.source, properties);
58
- } })))));
63
+ }, formChangedSignal: this.sourceFormChangedSignal, sourceType: (source === null || source === void 0 ? void 0 : source.type) || 'RasterSource' })))));
59
64
  }
60
65
  }
@@ -6,6 +6,7 @@ import { TileSourcePropertiesForm } from './objectform/tilesourceform';
6
6
  import { VectorLayerPropertiesForm } from './objectform/vectorlayerform';
7
7
  import { WebGlLayerPropertiesForm } from './objectform/webGlLayerForm';
8
8
  import { GeoTiffSourcePropertiesForm } from './objectform/geotiffsource';
9
+ import { PathBasedSourcePropertiesForm } from './objectform/pathbasedsource';
9
10
  export function getLayerTypeForm(layerType) {
10
11
  let LayerForm = LayerPropertiesForm;
11
12
  switch (layerType) {
@@ -29,6 +30,12 @@ export function getSourceTypeForm(sourceType) {
29
30
  case 'GeoJSONSource':
30
31
  SourceForm = GeoJSONSourcePropertiesForm;
31
32
  break;
33
+ case 'ImageSource':
34
+ SourceForm = PathBasedSourcePropertiesForm;
35
+ break;
36
+ case 'ShapefileSource':
37
+ SourceForm = PathBasedSourcePropertiesForm;
38
+ break;
32
39
  case 'GeoTiffSource':
33
40
  SourceForm = GeoTiffSourcePropertiesForm;
34
41
  break;
@@ -4,6 +4,7 @@ import { IJupyterGISModel } from '@jupytergis/schema';
4
4
  import { Dialog } from '@jupyterlab/apputils';
5
5
  import { Signal } from '@lumino/signaling';
6
6
  import { IDict } from '../../types';
7
+ import { SourceType } from '@jupytergis/schema';
7
8
  export interface IBaseFormStates {
8
9
  schema?: IDict;
9
10
  extraErrors?: any;
@@ -51,6 +52,15 @@ export interface IBaseFormProps {
51
52
  * extra errors or not.
52
53
  */
53
54
  formErrorSignal?: Signal<Dialog<any>, boolean>;
55
+ /**
56
+ * Configuration options for the dialog, including settings for layer data, source data,
57
+ * and other form-related parameters.
58
+ */
59
+ dialogOptions?: any;
60
+ /**
61
+ * Source type property
62
+ */
63
+ sourceType: SourceType;
54
64
  }
55
65
  /**
56
66
  * Generate a form to edit a layer/source type. This class is meant to be sub-classed to create more refined forms for specific layers/sources.
@@ -13,6 +13,7 @@ import { FormComponent } from '@jupyterlab/ui-components';
13
13
  import validatorAjv8 from '@rjsf/validator-ajv8';
14
14
  import * as React from 'react';
15
15
  import { deepCopy } from '../../tools';
16
+ import { Slider } from '@jupyter/react-components';
16
17
  const WrappedFormComponent = (props) => {
17
18
  const { fields } = props, rest = __rest(props, ["fields"]);
18
19
  return (React.createElement(FormComponent, Object.assign({}, rest, { validator: validatorAjv8, fields: Object.assign({}, fields) })));
@@ -69,6 +70,44 @@ export class BaseForm extends React.Component {
69
70
  if (v['type'] === 'object') {
70
71
  this.processSchema(data, v, uiSchema[k]);
71
72
  }
73
+ if (k === 'opacity') {
74
+ uiSchema[k] = {
75
+ 'ui:field': (props) => {
76
+ const [inputValue, setInputValue] = React.useState(props.formData.toFixed(1));
77
+ React.useEffect(() => {
78
+ setInputValue(props.formData.toFixed(1));
79
+ }, [props.formData]);
80
+ const handleSliderChange = (event) => {
81
+ const target = event.target;
82
+ if (target && '_value' in target) {
83
+ const sliderValue = parseFloat(target._value); // Slider value is in 0–10 range
84
+ const normalizedValue = sliderValue / 10; // Normalize to 0.1–1 range
85
+ props.onChange(normalizedValue);
86
+ }
87
+ };
88
+ const handleInputChange = (event) => {
89
+ const value = event.target.value;
90
+ setInputValue(value);
91
+ const parsedValue = parseFloat(value);
92
+ if (!isNaN(parsedValue) &&
93
+ parsedValue >= 0.1 &&
94
+ parsedValue <= 1) {
95
+ props.onChange(parsedValue);
96
+ }
97
+ };
98
+ return (React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '8px' } },
99
+ React.createElement(Slider, { min: 1, max: 10, step: 1, value: props.formData * 10, onChange: handleSliderChange }),
100
+ React.createElement("input", { type: "number", value: inputValue, step: 0.1, min: 0.1, max: 1, onChange: handleInputChange, style: {
101
+ width: '50px',
102
+ textAlign: 'center',
103
+ border: '1px solid #ccc',
104
+ borderRadius: '4px',
105
+ padding: '4px',
106
+ marginBottom: '5px'
107
+ } })));
108
+ }
109
+ };
110
+ }
72
111
  // Don't show readOnly properties when it's a form for updating an object
73
112
  if (v['readOnly']) {
74
113
  if (this.props.formContext === 'create') {
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const FileSelectorWidget: (props: any) => React.JSX.Element;
@@ -0,0 +1,81 @@
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+ import { FileDialog } from '@jupyterlab/filebrowser';
3
+ import { Dialog } from '@jupyterlab/apputils';
4
+ import { CreationFormDialog } from '../../dialogs/formdialog';
5
+ import { PathExt } from '@jupyterlab/coreutils';
6
+ export const FileSelectorWidget = (props) => {
7
+ const { options } = props;
8
+ const { docManager, formOptions } = options;
9
+ const [serverFilePath, setServerFilePath] = useState('');
10
+ const [urlPath, setUrlPath] = useState('');
11
+ const isTypingURL = useRef(false); // Tracks whether the user is manually typing a URL
12
+ useEffect(() => {
13
+ if (!isTypingURL.current && props.value) {
14
+ if (props.value.startsWith('http://') ||
15
+ props.value.startsWith('https://')) {
16
+ setUrlPath(props.value);
17
+ setServerFilePath('');
18
+ }
19
+ else {
20
+ setServerFilePath(props.value);
21
+ setUrlPath('');
22
+ }
23
+ }
24
+ }, [props.value]);
25
+ const handleBrowseServerFiles = async () => {
26
+ try {
27
+ const dialogElement = document.querySelector('dialog[aria-modal="true"]');
28
+ if (dialogElement) {
29
+ const dialogInstance = Dialog.tracker.find(dialog => dialog.node === dialogElement);
30
+ if (dialogInstance) {
31
+ dialogInstance.resolve(0);
32
+ }
33
+ }
34
+ else {
35
+ console.warn('No open dialog found.');
36
+ }
37
+ const output = await FileDialog.getOpenFiles({
38
+ title: 'Select a File',
39
+ manager: docManager
40
+ });
41
+ if (output.value && output.value.length > 0) {
42
+ const selectedFilePath = output.value[0].path;
43
+ const relativePath = PathExt.relative(formOptions.filePath, selectedFilePath);
44
+ setServerFilePath(relativePath);
45
+ setUrlPath('');
46
+ props.onChange(relativePath);
47
+ if (dialogElement) {
48
+ formOptions.dialogOptions.sourceData = Object.assign(Object.assign({}, formOptions.sourceData), { path: relativePath });
49
+ const formDialog = new CreationFormDialog(Object.assign({}, formOptions.dialogOptions));
50
+ await formDialog.launch();
51
+ }
52
+ }
53
+ else {
54
+ if (dialogElement) {
55
+ const formDialog = new CreationFormDialog(Object.assign({}, formOptions.dialogOptions));
56
+ await formDialog.launch();
57
+ }
58
+ }
59
+ }
60
+ catch (e) {
61
+ console.error('Error handling file dialog:', e);
62
+ }
63
+ };
64
+ const handleURLChange = (event) => {
65
+ const url = event.target.value.trim();
66
+ isTypingURL.current = true;
67
+ setUrlPath(url);
68
+ setServerFilePath('');
69
+ props.onChange(url);
70
+ };
71
+ const handleURLBlur = () => {
72
+ isTypingURL.current = false;
73
+ };
74
+ return (React.createElement("div", null,
75
+ React.createElement("div", null,
76
+ React.createElement("input", { type: "text", className: "jp-mod-styled", value: serverFilePath || '', readOnly: true, style: { width: '70%', marginRight: '10px' } }),
77
+ React.createElement("button", { className: "jp-mod-styled", onClick: handleBrowseServerFiles }, "Browse Server Files")),
78
+ React.createElement("div", null,
79
+ React.createElement("h3", { className: "jp-FormGroup-fieldLabel jp-FormGroup-contentItem" }, "Or enter external URL"),
80
+ React.createElement("input", { type: "text", id: "root_path", className: "jp-mod-styled", onChange: handleURLChange, onBlur: handleURLBlur, value: urlPath || '', style: { width: '100%' } }))));
81
+ };
@@ -1,19 +1,17 @@
1
1
  import { IDict } from '@jupytergis/schema';
2
- import { ISubmitEvent } from '@rjsf/core';
3
- import { BaseForm, IBaseFormProps } from './baseform';
2
+ import { IBaseFormProps } from './baseform';
3
+ import { PathBasedSourcePropertiesForm } from './pathbasedsource';
4
4
  /**
5
5
  * The form to modify a GeoJSON source.
6
6
  */
7
- export declare class GeoJSONSourcePropertiesForm extends BaseForm {
7
+ export declare class GeoJSONSourcePropertiesForm extends PathBasedSourcePropertiesForm {
8
+ private _validate;
8
9
  constructor(props: IBaseFormProps);
9
10
  protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
10
- protected onFormBlur(id: string, value: any): void;
11
- protected onFormSubmit(e: ISubmitEvent<any>): void;
12
11
  /**
13
12
  * Validate the path, to avoid invalid path or invalid GeoJSON.
14
13
  *
15
14
  * @param path - the path to validate.
16
15
  */
17
- private _validatePath;
18
- private _validate;
16
+ protected _validatePath(path: string): Promise<void>;
19
17
  }
@@ -1,11 +1,11 @@
1
- import { showErrorMessage } from '@jupyterlab/apputils';
2
1
  import { Ajv } from 'ajv';
3
2
  import * as geojson from '@jupytergis/schema/src/schema/geojson.json';
4
- import { BaseForm } from './baseform';
3
+ import { PathBasedSourcePropertiesForm } from './pathbasedsource';
4
+ import { loadFile } from '../../tools';
5
5
  /**
6
6
  * The form to modify a GeoJSON source.
7
7
  */
8
- export class GeoJSONSourcePropertiesForm extends BaseForm {
8
+ export class GeoJSONSourcePropertiesForm extends PathBasedSourcePropertiesForm {
9
9
  constructor(props) {
10
10
  var _a, _b;
11
11
  super(props);
@@ -18,26 +18,6 @@ export class GeoJSONSourcePropertiesForm extends BaseForm {
18
18
  this.removeFormEntry('data', data, schema, uiSchema);
19
19
  }
20
20
  super.processSchema(data, schema, uiSchema);
21
- if (!schema.properties || !data) {
22
- return;
23
- }
24
- // This is not user-editable
25
- delete schema.properties.valid;
26
- }
27
- onFormBlur(id, value) {
28
- // Is there a better way to spot the path text entry?
29
- if (!id.endsWith('_path')) {
30
- return;
31
- }
32
- this._validatePath(value);
33
- }
34
- onFormSubmit(e) {
35
- var _a, _b, _c;
36
- if (((_c = (_b = (_a = this.state.extraErrors) === null || _a === void 0 ? void 0 : _a.path) === null || _b === void 0 ? void 0 : _b.__errors) === null || _c === void 0 ? void 0 : _c.length) >= 1) {
37
- showErrorMessage('Invalid JSON file', this.state.extraErrors.path.__errors[0]);
38
- return;
39
- }
40
- super.onFormSubmit(e);
41
21
  }
42
22
  /**
43
23
  * Validate the path, to avoid invalid path or invalid GeoJSON.
@@ -51,7 +31,11 @@ export class GeoJSONSourcePropertiesForm extends BaseForm {
51
31
  let valid = false;
52
32
  if (path) {
53
33
  try {
54
- const geoJSONData = await this.props.model.readGeoJSON(path);
34
+ const geoJSONData = await loadFile({
35
+ filepath: path,
36
+ type: this.props.sourceType,
37
+ model: this.props.model
38
+ });
55
39
  valid = this._validate(geoJSONData);
56
40
  if (!valid) {
57
41
  error = `"${path}" is not a valid GeoJSON file`;
@@ -1,6 +1,7 @@
1
1
  import { IDict, SourceType } from '@jupytergis/schema';
2
2
  import { BaseForm, IBaseFormProps } from './baseform';
3
3
  import { Signal } from '@lumino/signaling';
4
+ import { IChangeEvent } from '@rjsf/core';
4
5
  export interface ILayerProps extends IBaseFormProps {
5
6
  /**
6
7
  * The source type for the layer
@@ -16,4 +17,5 @@ export declare class LayerPropertiesForm extends BaseForm {
16
17
  protected sourceFormChangedSignal: Signal<any, IDict<any>> | undefined;
17
18
  constructor(props: ILayerProps);
18
19
  protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
20
+ protected onFormChange(e: IChangeEvent): void;
19
21
  }
@@ -14,4 +14,10 @@ export class LayerPropertiesForm extends BaseForm {
14
14
  schema.properties.source.enumNames = Object.values(availableSources);
15
15
  schema.properties.source.enum = Object.keys(availableSources);
16
16
  }
17
+ onFormChange(e) {
18
+ super.onFormChange(e);
19
+ if (this.props.dialogOptions) {
20
+ this.props.dialogOptions.layerData = Object.assign({}, e.formData);
21
+ }
22
+ }
17
23
  }
@@ -0,0 +1,19 @@
1
+ import { IDict } from '@jupytergis/schema';
2
+ import { IChangeEvent, ISubmitEvent } from '@rjsf/core';
3
+ import { BaseForm, IBaseFormProps } from './baseform';
4
+ /**
5
+ * The form to modify a PathBasedSource source.
6
+ */
7
+ export declare class PathBasedSourcePropertiesForm extends BaseForm {
8
+ constructor(props: IBaseFormProps);
9
+ protected processSchema(data: IDict<any> | undefined, schema: IDict, uiSchema: IDict): void;
10
+ protected onFormBlur(id: string, value: any): void;
11
+ protected onFormChange(e: IChangeEvent): void;
12
+ protected onFormSubmit(e: ISubmitEvent<any>): void;
13
+ /**
14
+ * Validate the path, to avoid invalid path.
15
+ *
16
+ * @param path - the path to validate.
17
+ */
18
+ protected _validatePath(path: string): Promise<void>;
19
+ }
@@ -0,0 +1,98 @@
1
+ import { showErrorMessage } from '@jupyterlab/apputils';
2
+ import { BaseForm } from './baseform';
3
+ import { loadFile } from '../../tools';
4
+ import { FileSelectorWidget } from './fileselectorwidget';
5
+ /**
6
+ * The form to modify a PathBasedSource source.
7
+ */
8
+ export class PathBasedSourcePropertiesForm extends BaseForm {
9
+ constructor(props) {
10
+ var _a, _b;
11
+ super(props);
12
+ if (this.props.sourceType !== 'GeoJSONSource') {
13
+ this._validatePath((_b = (_a = props.sourceData) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : '');
14
+ }
15
+ }
16
+ processSchema(data, schema, uiSchema) {
17
+ var _a;
18
+ super.processSchema(data, schema, uiSchema);
19
+ if (!schema.properties || !data) {
20
+ return;
21
+ }
22
+ // Customize the widget for path field
23
+ if (schema.properties && schema.properties.path) {
24
+ const docManager = (_a = this.props.formChangedSignal) === null || _a === void 0 ? void 0 : _a.sender.props.formSchemaRegistry.getDocManager();
25
+ uiSchema.path = {
26
+ 'ui:widget': FileSelectorWidget,
27
+ 'ui:options': {
28
+ docManager,
29
+ formOptions: this.props
30
+ }
31
+ };
32
+ }
33
+ // This is not user-editable
34
+ delete schema.properties.valid;
35
+ }
36
+ onFormBlur(id, value) {
37
+ // Is there a better way to spot the path text entry?
38
+ if (!id.endsWith('_path')) {
39
+ return;
40
+ }
41
+ this._validatePath(value);
42
+ }
43
+ // we need to use `onFormChange` instead of `onFormBlur` because it's no longer a text field
44
+ onFormChange(e) {
45
+ var _a;
46
+ super.onFormChange(e);
47
+ if (((_a = e.formData) === null || _a === void 0 ? void 0 : _a.path) !== undefined) {
48
+ this._validatePath(e.formData.path);
49
+ }
50
+ }
51
+ onFormSubmit(e) {
52
+ var _a, _b, _c;
53
+ if (((_c = (_b = (_a = this.state.extraErrors) === null || _a === void 0 ? void 0 : _a.path) === null || _b === void 0 ? void 0 : _b.__errors) === null || _c === void 0 ? void 0 : _c.length) >= 1) {
54
+ showErrorMessage('Invalid file', this.state.extraErrors.path.__errors[0]);
55
+ return;
56
+ }
57
+ super.onFormSubmit(e);
58
+ }
59
+ /**
60
+ * Validate the path, to avoid invalid path.
61
+ *
62
+ * @param path - the path to validate.
63
+ */
64
+ async _validatePath(path) {
65
+ const extraErrors = this.state.extraErrors;
66
+ let error = '';
67
+ let valid = true;
68
+ if (!path) {
69
+ valid = false;
70
+ error = 'Path is required';
71
+ }
72
+ else {
73
+ try {
74
+ await loadFile({
75
+ filepath: path,
76
+ type: this.props.sourceType,
77
+ model: this.props.model
78
+ });
79
+ }
80
+ catch (e) {
81
+ valid = false;
82
+ error = `"${path}" is not a valid ${this.props.sourceType} file.`;
83
+ }
84
+ }
85
+ if (!valid) {
86
+ extraErrors.path = {
87
+ __errors: [error]
88
+ };
89
+ this.setState(old => (Object.assign(Object.assign({}, old), { extraErrors })));
90
+ }
91
+ else {
92
+ this.setState(old => (Object.assign(Object.assign({}, old), { extraErrors: Object.assign(Object.assign({}, extraErrors), { path: { __errors: [] } }) })));
93
+ }
94
+ if (this.props.formErrorSignal) {
95
+ this.props.formErrorSignal.emit(!valid);
96
+ }
97
+ }
98
+ }
package/lib/icons.d.ts CHANGED
@@ -7,3 +7,4 @@ export declare const visibilityIcon: LabIcon;
7
7
  export declare const nonVisibilityIcon: LabIcon;
8
8
  export declare const geoJSONIcon: LabIcon;
9
9
  export declare const moundIcon: LabIcon;
10
+ export declare const logoMiniIconQGZ: LabIcon;
package/lib/icons.js CHANGED
@@ -12,6 +12,7 @@ import visibilitySvgStr from '../style/icons/visibility.svg';
12
12
  import nonVisibilitySvgStr from '../style/icons/nonvisibility.svg';
13
13
  import geoJsonSvgStr from '../style/icons/geojson.svg';
14
14
  import moundSvgStr from '../style/icons/mound.svg';
15
+ import logoMiniQGZ from '../style/icons/logo_mini_qgz.svg';
15
16
  export const logoIcon = new LabIcon({
16
17
  name: 'jupytergis::logo',
17
18
  svgstr: logoSvgStr
@@ -44,3 +45,7 @@ export const moundIcon = new LabIcon({
44
45
  name: 'jupytergis::mound',
45
46
  svgstr: moundSvgStr
46
47
  });
48
+ export const logoMiniIconQGZ = new LabIcon({
49
+ name: 'jupytergis::logoQGZ',
50
+ svgstr: logoMiniQGZ
51
+ });
@@ -0,0 +1,62 @@
1
+ [
2
+ {
3
+ "command": "jupytergis:undo",
4
+ "keys": ["Accel Z"],
5
+ "selector": ".data-jgis-keybinding"
6
+ },
7
+ {
8
+ "command": "jupytergis:redo",
9
+ "keys": ["Accel Shift Z"],
10
+ "selector": ".data-jgis-keybinding"
11
+ },
12
+ {
13
+ "command": "jupytergis:identify",
14
+ "keys": ["Escape"],
15
+ "selector": ".data-jgis-keybinding"
16
+ },
17
+ {
18
+ "command": "jupytergis:removeSource",
19
+ "keys": ["Delete"],
20
+ "selector": ".data-jgis-keybinding .jp-gis-source.jp-gis-sourceUnused"
21
+ },
22
+ {
23
+ "command": "jupytergis:renameSource",
24
+ "keys": ["F2"],
25
+ "selector": ".data-jgis-keybinding .jp-gis-source"
26
+ },
27
+ {
28
+ "command": "jupytergis:removeLayer",
29
+ "keys": ["Delete"],
30
+ "selector": ".data-jgis-keybinding .jp-gis-layerItem"
31
+ },
32
+ {
33
+ "command": "jupytergis:renameLayer",
34
+ "keys": ["F2"],
35
+ "selector": ".data-jgis-keybinding .jp-gis-layerItem"
36
+ },
37
+ {
38
+ "command": "jupytergis:removeGroup",
39
+ "keys": ["Delete"],
40
+ "selector": ".data-jgis-keybinding .jp-gis-layerGroupHeader"
41
+ },
42
+ {
43
+ "command": "jupytergis:renameGroup",
44
+ "keys": ["F2"],
45
+ "selector": ".data-jgis-keybinding .jp-gis-layerGroupHeader"
46
+ },
47
+ {
48
+ "command": "jupytergis:executeConsole",
49
+ "keys": ["Shift Enter"],
50
+ "selector": ".jpgis-console .jp-CodeConsole[data-jp-interaction-mode='notebook'] .jp-CodeConsole-promptCell"
51
+ },
52
+ {
53
+ "command": "jupytergis:invokeConsoleCompleter",
54
+ "keys": ["Tab"],
55
+ "selector": ".jpgis-console .jp-CodeConsole-promptCell .jp-mod-completer-enabled"
56
+ },
57
+ {
58
+ "command": "jupytergis:selectConsoleCompleter",
59
+ "keys": ["Enter"],
60
+ "selector": ".jpgis-console .jp-ConsolePanel .jp-mod-completer-active"
61
+ }
62
+ ]
@@ -1,9 +1,9 @@
1
1
  import { IAnnotation, IDict, IJGISLayer, IJGISSource } from '@jupytergis/schema';
2
2
  import { User } from '@jupyterlab/services';
3
- import BaseLayer from 'ol/layer/Base';
3
+ import { Layer } from 'ol/layer';
4
4
  import * as React from 'react';
5
- import { MainViewModel } from './mainviewmodel';
6
5
  import { ClientPointer } from './CollaboratorPointers';
6
+ import { MainViewModel } from './mainviewmodel';
7
7
  interface IProps {
8
8
  viewModel: MainViewModel;
9
9
  }
@@ -15,14 +15,20 @@ interface IStates {
15
15
  firstLoad: boolean;
16
16
  annotations: IDict<IAnnotation>;
17
17
  clientPointers: IDict<ClientPointer>;
18
+ viewProjection: {
19
+ code: string;
20
+ units: string;
21
+ };
22
+ loadingLayer: boolean;
23
+ scale: number;
18
24
  }
19
25
  export declare class MainView extends React.Component<IProps, IStates> {
20
26
  constructor(props: IProps);
21
27
  componentDidMount(): Promise<void>;
22
28
  componentWillUnmount(): void;
23
29
  generateScene(): Promise<void>;
30
+ createSelectInteraction: () => void;
24
31
  addContextMenu: () => void;
25
- private _loadShapefileAsGeoJSON;
26
32
  private _loadGeoTIFFWithCache;
27
33
  /**
28
34
  * Add a source in the map.
@@ -66,6 +72,7 @@ export declare class MainView extends React.Component<IProps, IStates> {
66
72
  * @returns - the map layer.
67
73
  */
68
74
  private _buildMapLayer;
75
+ addProjection(newMapLayer: Layer): void;
69
76
  /**
70
77
  * Add a layer to the map.
71
78
  *
@@ -90,7 +97,16 @@ export declare class MainView extends React.Component<IProps, IStates> {
90
97
  * @param id - id of the layer.
91
98
  * @param layer - the layer object.
92
99
  */
93
- updateLayer(id: string, layer: IJGISLayer, mapLayer: BaseLayer): Promise<void>;
100
+ updateLayer(id: string, layer: IJGISLayer, mapLayer: Layer): Promise<void>;
101
+ /**
102
+ * Wait for all layers to be loaded.
103
+ */
104
+ private _waitForReady;
105
+ /**
106
+ * Wait for a layers source state to be 'ready'
107
+ * @param layer The Layer to check
108
+ */
109
+ private _waitForSourceReady;
94
110
  /**
95
111
  * Remove a layer from the map.
96
112
  *
@@ -129,7 +145,7 @@ export declare class MainView extends React.Component<IProps, IStates> {
129
145
  private _onSharedMetadataChanged;
130
146
  private _computeAnnotationPosition;
131
147
  private _updateAnnotation;
132
- private _onZoomToAnnotation;
148
+ private _onZoomToPosition;
133
149
  private _moveToPosition;
134
150
  private _onPointerMove;
135
151
  private _syncPointer;
@@ -149,5 +165,6 @@ export declare class MainView extends React.Component<IProps, IStates> {
149
165
  private _sourceToLayerMap;
150
166
  private _documentPath?;
151
167
  private _contextMenu;
168
+ private _loadingLayers;
152
169
  }
153
170
  export {};