@jupytergis/base 0.1.6 → 0.2.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/annotations/components/Annotation.d.ts +11 -0
- package/lib/annotations/components/Annotation.js +61 -0
- package/lib/annotations/components/AnnotationFloater.d.ts +7 -0
- package/lib/annotations/components/AnnotationFloater.js +30 -0
- package/lib/annotations/components/Message.d.ts +8 -0
- package/lib/annotations/components/Message.js +17 -0
- package/lib/annotations/index.d.ts +3 -0
- package/lib/annotations/index.js +3 -0
- package/lib/annotations/model.d.ts +28 -0
- package/lib/annotations/model.js +67 -0
- package/lib/classificationModes.d.ts +13 -0
- package/lib/classificationModes.js +326 -0
- package/lib/commands.js +52 -7
- package/lib/constants.d.ts +2 -0
- package/lib/constants.js +5 -1
- package/lib/dialogs/symbology/classificationModes.d.ts +13 -0
- package/lib/dialogs/symbology/classificationModes.js +326 -0
- package/lib/dialogs/symbology/components/color_ramp/CanvasSelectComponent.d.ts +11 -0
- package/lib/dialogs/symbology/components/color_ramp/CanvasSelectComponent.js +119 -0
- package/lib/dialogs/symbology/components/color_ramp/ColorRamp.d.ts +15 -0
- package/lib/dialogs/symbology/components/color_ramp/ColorRamp.js +33 -0
- package/lib/dialogs/symbology/components/color_ramp/ColorRampEntry.d.ts +9 -0
- package/lib/dialogs/symbology/components/color_ramp/ColorRampEntry.js +24 -0
- package/lib/dialogs/symbology/components/color_ramp/ModeSelectRow.d.ts +10 -0
- package/lib/dialogs/symbology/components/color_ramp/ModeSelectRow.js +11 -0
- package/lib/dialogs/symbology/components/color_stops/StopContainer.d.ts +9 -0
- package/lib/dialogs/symbology/components/color_stops/StopContainer.js +28 -0
- package/lib/dialogs/{components/symbology → symbology/components/color_stops}/StopRow.js +9 -2
- package/lib/dialogs/symbology/hooks/useGetProperties.d.ts +12 -0
- package/lib/dialogs/symbology/hooks/useGetProperties.js +47 -0
- package/lib/dialogs/{symbologyDialog.js → symbology/symbologyDialog.js} +3 -3
- package/lib/dialogs/symbology/symbologyUtils.d.ts +9 -0
- package/lib/dialogs/symbology/symbologyUtils.js +94 -0
- package/lib/dialogs/symbology/tiff_layer/TiffRendering.d.ts +4 -0
- package/lib/dialogs/{components/symbology/BandRendering.js → symbology/tiff_layer/TiffRendering.js} +3 -3
- package/lib/dialogs/{components/symbology → symbology/tiff_layer/components}/BandRow.d.ts +1 -1
- package/lib/dialogs/{components/symbology → symbology/tiff_layer/types}/SingleBandPseudoColor.d.ts +9 -1
- package/lib/dialogs/{components/symbology → symbology/tiff_layer/types}/SingleBandPseudoColor.js +131 -83
- package/lib/dialogs/{components/symbology → symbology/vector_layer}/VectorRendering.d.ts +1 -1
- package/lib/dialogs/{components/symbology → symbology/vector_layer}/VectorRendering.js +10 -13
- package/lib/dialogs/symbology/vector_layer/components/ValueSelect.d.ts +8 -0
- package/lib/dialogs/symbology/vector_layer/components/ValueSelect.js +7 -0
- package/lib/dialogs/symbology/vector_layer/types/Categorized.d.ts +4 -0
- package/lib/dialogs/symbology/vector_layer/types/Categorized.js +94 -0
- package/lib/dialogs/symbology/vector_layer/types/Graduated.js +169 -0
- package/lib/dialogs/{components/symbology → symbology/vector_layer/types}/SimpleSymbol.js +8 -13
- package/lib/formbuilder/formselectors.js +4 -0
- package/lib/formbuilder/objectform/baseform.d.ts +1 -1
- package/lib/formbuilder/objectform/baseform.js +31 -42
- package/lib/formbuilder/objectform/geojsonsource.js +33 -30
- package/lib/formbuilder/objectform/geotiffsource.d.ts +16 -0
- package/lib/formbuilder/objectform/geotiffsource.js +71 -0
- package/lib/formbuilder/objectform/vectorlayerform.js +1 -0
- package/lib/formbuilder/objectform/webGlLayerForm.js +1 -0
- package/lib/index.d.ts +7 -4
- package/lib/index.js +7 -4
- package/lib/mainview/CollaboratorPointers.d.ts +17 -0
- package/lib/mainview/CollaboratorPointers.js +37 -0
- package/lib/mainview/FollowIndicator.d.ts +7 -0
- package/lib/mainview/FollowIndicator.js +9 -0
- package/lib/mainview/mainView.d.ts +39 -3
- package/lib/mainview/mainView.js +451 -41
- package/lib/mainview/mainviewmodel.d.ts +2 -1
- package/lib/mainview/mainviewmodel.js +5 -0
- package/lib/panelview/annotationPanel.d.ts +27 -0
- package/lib/panelview/annotationPanel.js +45 -0
- package/lib/panelview/components/filter-panel/Filter.d.ts +7 -2
- package/lib/panelview/components/filter-panel/Filter.js +1 -1
- package/lib/panelview/components/filter-panel/FilterRow.js +3 -3
- package/lib/panelview/components/identify-panel/IdentifyPanel.d.ts +15 -0
- package/lib/panelview/components/identify-panel/IdentifyPanel.js +108 -0
- package/lib/panelview/components/layers.js +4 -4
- package/lib/panelview/leftpanel.js +8 -0
- package/lib/panelview/rightpanel.d.ts +4 -1
- package/lib/panelview/rightpanel.js +28 -7
- package/lib/store.d.ts +9 -0
- package/lib/store.js +25 -0
- package/lib/toolbar/widget.js +12 -2
- package/lib/tools.d.ts +35 -0
- package/lib/tools.js +86 -0
- package/lib/types.d.ts +14 -0
- package/package.json +18 -20
- package/style/base.css +4 -8
- package/style/dialog.css +1 -1
- package/style/icons/logo_mini.svg +70 -148
- package/style/icons/nonvisibility.svg +2 -7
- package/style/icons/visibility.svg +2 -6
- package/style/leftPanel.css +5 -0
- package/style/symbologyDialog.css +104 -3
- package/lib/dialogs/components/symbology/BandRendering.d.ts +0 -4
- package/lib/dialogs/components/symbology/Graduated.js +0 -188
- /package/lib/dialogs/{components/symbology → symbology/components/color_stops}/StopRow.d.ts +0 -0
- /package/lib/dialogs/{symbologyDialog.d.ts → symbology/symbologyDialog.d.ts} +0 -0
- /package/lib/dialogs/{components/symbology → symbology/tiff_layer/components}/BandRow.js +0 -0
- /package/lib/dialogs/{components/symbology → symbology/vector_layer/types}/Graduated.d.ts +0 -0
- /package/lib/dialogs/{components/symbology → symbology/vector_layer/types}/SimpleSymbol.d.ts +0 -0
package/lib/commands.js
CHANGED
|
@@ -2,7 +2,7 @@ import { showErrorMessage } from '@jupyterlab/apputils';
|
|
|
2
2
|
import { CommandIDs, icons } from './constants';
|
|
3
3
|
import { CreationFormDialog } from './dialogs/formdialog';
|
|
4
4
|
import { LayerBrowserWidget } from './dialogs/layerBrowserDialog';
|
|
5
|
-
import { SymbologyWidget } from './dialogs/symbologyDialog';
|
|
5
|
+
import { SymbologyWidget } from './dialogs/symbology/symbologyDialog';
|
|
6
6
|
/**
|
|
7
7
|
* Add the commands to the application's command registry.
|
|
8
8
|
*/
|
|
@@ -11,15 +11,34 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
11
11
|
const trans = translator.load('jupyterlab');
|
|
12
12
|
const { commands } = app;
|
|
13
13
|
commands.addCommand(CommandIDs.symbology, Object.assign({ label: trans.__('Edit Symbology'), isEnabled: () => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
var _a, _b;
|
|
15
|
+
const model = (_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.context.model;
|
|
16
|
+
const localState = model === null || model === void 0 ? void 0 : model.sharedModel.awareness.getLocalState();
|
|
17
|
+
if (!model || !localState || !((_b = localState['selected']) === null || _b === void 0 ? void 0 : _b.value)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const selectedLayers = localState['selected'].value;
|
|
21
|
+
// Can't open more than one symbology dialog at once
|
|
22
|
+
if (Object.keys(selectedLayers).length > 1) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const layerId = Object.keys(selectedLayers)[0];
|
|
26
|
+
const layer = model.getLayer(layerId);
|
|
27
|
+
if (!layer) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const isValidLayer = [
|
|
31
|
+
'VectorLayer',
|
|
32
|
+
'VectorTileLayer',
|
|
33
|
+
'WebGlLayer'
|
|
34
|
+
].includes(layer.type);
|
|
35
|
+
return isValidLayer;
|
|
17
36
|
}, execute: Private.createSymbologyDialog(tracker, state) }, icons.get(CommandIDs.symbology)));
|
|
18
37
|
commands.addCommand(CommandIDs.redo, Object.assign({ label: trans.__('Redo'), isEnabled: () => {
|
|
19
38
|
return tracker.currentWidget
|
|
20
39
|
? tracker.currentWidget.context.model.sharedModel.editable
|
|
21
40
|
: false;
|
|
22
|
-
}, execute:
|
|
41
|
+
}, execute: () => {
|
|
23
42
|
const current = tracker.currentWidget;
|
|
24
43
|
if (current) {
|
|
25
44
|
return current.context.model.sharedModel.redo();
|
|
@@ -29,12 +48,38 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
29
48
|
return tracker.currentWidget
|
|
30
49
|
? tracker.currentWidget.context.model.sharedModel.editable
|
|
31
50
|
: false;
|
|
32
|
-
}, execute:
|
|
51
|
+
}, execute: () => {
|
|
33
52
|
const current = tracker.currentWidget;
|
|
34
53
|
if (current) {
|
|
35
54
|
return current.context.model.sharedModel.undo();
|
|
36
55
|
}
|
|
37
56
|
} }, icons.get(CommandIDs.undo)));
|
|
57
|
+
commands.addCommand(CommandIDs.identify, Object.assign({ label: trans.__('Identify'), isToggled: () => {
|
|
58
|
+
var _a;
|
|
59
|
+
return ((_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.context.model.isIdentifying) || false;
|
|
60
|
+
}, isEnabled: () => {
|
|
61
|
+
return tracker.currentWidget
|
|
62
|
+
? tracker.currentWidget.context.model.sharedModel.editable
|
|
63
|
+
: false;
|
|
64
|
+
}, execute: args => {
|
|
65
|
+
const current = tracker.currentWidget;
|
|
66
|
+
if (!current) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const luminoEvent = args['_luminoEvent'];
|
|
70
|
+
if (luminoEvent) {
|
|
71
|
+
const keysPressed = luminoEvent.keys;
|
|
72
|
+
if (keysPressed === null || keysPressed === void 0 ? void 0 : keysPressed.includes('Escape')) {
|
|
73
|
+
current.context.model.isIdentifying = false;
|
|
74
|
+
current.node.classList.remove('jGIS-identify-tool');
|
|
75
|
+
commands.notifyCommandChanged(CommandIDs.identify);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
current.node.classList.toggle('jGIS-identify-tool');
|
|
80
|
+
current.context.model.toggleIdentify();
|
|
81
|
+
commands.notifyCommandChanged(CommandIDs.identify);
|
|
82
|
+
} }, icons.get(CommandIDs.identify)));
|
|
38
83
|
/**
|
|
39
84
|
* SOURCES and LAYERS creation commands.
|
|
40
85
|
*/
|
|
@@ -185,7 +230,7 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
185
230
|
createSource: true,
|
|
186
231
|
sourceData: {
|
|
187
232
|
name: 'Custom GeoTiff Source',
|
|
188
|
-
urls: [
|
|
233
|
+
urls: [{}]
|
|
189
234
|
},
|
|
190
235
|
layerData: { name: 'Custom GeoTiff Layer' },
|
|
191
236
|
sourceType: 'GeoTiffSource',
|
package/lib/constants.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export declare namespace CommandIDs {
|
|
|
7
7
|
const redo = "jupytergis:redo";
|
|
8
8
|
const undo = "jupytergis:undo";
|
|
9
9
|
const symbology = "jupytergis:symbology";
|
|
10
|
+
const identify = "jupytergis:identify";
|
|
10
11
|
const openLayerBrowser = "jupytergis:openLayerBrowser";
|
|
11
12
|
const newRasterEntry = "jupytergis:newRasterEntry";
|
|
12
13
|
const newVectorTileEntry = "jupytergis:newVectorTileEntry";
|
|
@@ -43,6 +44,7 @@ export declare namespace CommandIDs {
|
|
|
43
44
|
const removeConsole = "jupytergis:removeConsole";
|
|
44
45
|
const executeConsole = "jupytergis:executeConsole";
|
|
45
46
|
const selectCompleter = "jupytergis:selectConsoleCompleter";
|
|
47
|
+
const addAnnotation = "jupytergis:addAnnotation";
|
|
46
48
|
}
|
|
47
49
|
interface IRegisteredIcon {
|
|
48
50
|
icon?: LabIcon;
|
package/lib/constants.js
CHANGED
|
@@ -9,6 +9,7 @@ export var CommandIDs;
|
|
|
9
9
|
CommandIDs.redo = 'jupytergis:redo';
|
|
10
10
|
CommandIDs.undo = 'jupytergis:undo';
|
|
11
11
|
CommandIDs.symbology = 'jupytergis:symbology';
|
|
12
|
+
CommandIDs.identify = 'jupytergis:identify';
|
|
12
13
|
// Layers and sources creation commands
|
|
13
14
|
CommandIDs.openLayerBrowser = 'jupytergis:openLayerBrowser';
|
|
14
15
|
// Layer and source
|
|
@@ -52,6 +53,8 @@ export var CommandIDs;
|
|
|
52
53
|
CommandIDs.removeConsole = 'jupytergis:removeConsole';
|
|
53
54
|
CommandIDs.executeConsole = 'jupytergis:executeConsole';
|
|
54
55
|
CommandIDs.selectCompleter = 'jupytergis:selectConsoleCompleter';
|
|
56
|
+
// Map Commands
|
|
57
|
+
CommandIDs.addAnnotation = 'jupytergis:addAnnotation';
|
|
55
58
|
})(CommandIDs || (CommandIDs = {}));
|
|
56
59
|
const iconObject = {
|
|
57
60
|
RasterSource: { icon: rasterIcon },
|
|
@@ -77,7 +80,8 @@ const iconObject = {
|
|
|
77
80
|
[CommandIDs.newVideoEntry]: { iconClass: 'fa fa-video' },
|
|
78
81
|
[CommandIDs.newShapefileLayer]: { iconClass: 'fa fa-file' },
|
|
79
82
|
[CommandIDs.newGeoTiffEntry]: { iconClass: 'fa fa-image' },
|
|
80
|
-
[CommandIDs.symbology]: { iconClass: 'fa fa-brush' }
|
|
83
|
+
[CommandIDs.symbology]: { iconClass: 'fa fa-brush' },
|
|
84
|
+
[CommandIDs.identify]: { iconClass: 'fa fa-info' }
|
|
81
85
|
};
|
|
82
86
|
/**
|
|
83
87
|
* The registered icons
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { InterpolationType } from './tiff_layer/types/SingleBandPseudoColor';
|
|
2
|
+
export declare namespace VectorClassifications {
|
|
3
|
+
const calculateQuantileBreaks: (values: number[], nClasses: number) => number[];
|
|
4
|
+
const calculateEqualIntervalBreaks: (values: number[], nClasses: number) => number[];
|
|
5
|
+
const calculateJenksBreaks: (values: number[], nClasses: number) => any[];
|
|
6
|
+
const calculatePrettyBreaks: (values: number[], nClasses: number) => number[];
|
|
7
|
+
const calculateLogarithmicBreaks: (values: number[], nClasses: number) => number[];
|
|
8
|
+
}
|
|
9
|
+
export declare namespace GeoTiffClassifications {
|
|
10
|
+
const classifyQuantileBreaks: (nClasses: number, bandNumber: number, url: string, colorRampType: string) => Promise<number[]>;
|
|
11
|
+
const classifyContinuousBreaks: (nClasses: number, minimumValue: number, maximumValue: number, colorRampType: InterpolationType) => number[];
|
|
12
|
+
const classifyEqualIntervalBreaks: (nClasses: number, minimumValue: number, maximumValue: number, colorRampType: InterpolationType) => number[];
|
|
13
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
// Adapted from https://github.com/qgis/QGIS/blob/master/src/core/classification/
|
|
2
|
+
import { Pool, fromUrl } from 'geotiff';
|
|
3
|
+
export var VectorClassifications;
|
|
4
|
+
(function (VectorClassifications) {
|
|
5
|
+
VectorClassifications.calculateQuantileBreaks = (values, nClasses) => {
|
|
6
|
+
// q-th quantile of a data set:
|
|
7
|
+
// value where q fraction of data is below and (1-q) fraction is above this value
|
|
8
|
+
// Xq = (1 - r) * X_NI1 + r * X_NI2
|
|
9
|
+
// NI1 = (int) (q * (n+1))
|
|
10
|
+
// NI2 = NI1 + 1
|
|
11
|
+
// r = q * (n+1) - (int) (q * (n+1))
|
|
12
|
+
// (indices of X: 1...n)
|
|
13
|
+
const sortedValues = [...values].sort((a, b) => a - b);
|
|
14
|
+
const breaks = [];
|
|
15
|
+
if (!sortedValues) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
const n = sortedValues.length;
|
|
19
|
+
let xq = n > 0 ? sortedValues[0] : 0;
|
|
20
|
+
for (let i = 1; i < nClasses; i++) {
|
|
21
|
+
if (n > 1) {
|
|
22
|
+
const q = i / nClasses;
|
|
23
|
+
const a = q * (n - 1);
|
|
24
|
+
const aa = Math.floor(a);
|
|
25
|
+
const r = a - aa;
|
|
26
|
+
xq = (1 - r) * sortedValues[aa] + r * sortedValues[aa + 1];
|
|
27
|
+
}
|
|
28
|
+
breaks.push(xq);
|
|
29
|
+
}
|
|
30
|
+
breaks.push(sortedValues[n - 1]);
|
|
31
|
+
return breaks;
|
|
32
|
+
};
|
|
33
|
+
VectorClassifications.calculateEqualIntervalBreaks = (values, nClasses) => {
|
|
34
|
+
const minimum = Math.min(...values);
|
|
35
|
+
const maximum = Math.max(...values);
|
|
36
|
+
const breaks = [];
|
|
37
|
+
const step = (maximum - minimum) / nClasses;
|
|
38
|
+
let value = minimum;
|
|
39
|
+
for (let i = 0; i < nClasses; i++) {
|
|
40
|
+
value += step;
|
|
41
|
+
breaks.push(value);
|
|
42
|
+
}
|
|
43
|
+
breaks[nClasses - 1] = maximum;
|
|
44
|
+
return breaks;
|
|
45
|
+
};
|
|
46
|
+
VectorClassifications.calculateJenksBreaks = (values, nClasses) => {
|
|
47
|
+
const maximum = Math.max(...values);
|
|
48
|
+
if (values.length === 0) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
if (nClasses <= 1) {
|
|
52
|
+
return [maximum];
|
|
53
|
+
}
|
|
54
|
+
if (nClasses >= values.length) {
|
|
55
|
+
return values;
|
|
56
|
+
}
|
|
57
|
+
const sample = [...values].sort((a, b) => a - b);
|
|
58
|
+
const n = sample.length;
|
|
59
|
+
const matrixOne = Array.from({ length: n + 1 }, () => Array(nClasses + 1).fill(0));
|
|
60
|
+
const matrixTwo = Array.from({ length: n + 1 }, () => Array(nClasses + 1).fill(Number.MAX_VALUE));
|
|
61
|
+
for (let i = 1; i <= nClasses; i++) {
|
|
62
|
+
matrixOne[0][i] = 1;
|
|
63
|
+
matrixOne[1][i] = 1;
|
|
64
|
+
matrixTwo[0][i] = 0.0;
|
|
65
|
+
for (let j = 2; j <= n; j++) {
|
|
66
|
+
matrixTwo[j][i] = Number.MAX_VALUE;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
for (let l = 2; l <= n; l++) {
|
|
70
|
+
let s1 = 0.0;
|
|
71
|
+
let s2 = 0.0;
|
|
72
|
+
let w = 0;
|
|
73
|
+
let v = 0.0;
|
|
74
|
+
for (let m = 1; m <= l; m++) {
|
|
75
|
+
const i3 = l - m + 1;
|
|
76
|
+
const val = sample[i3 - 1];
|
|
77
|
+
s2 += val * val;
|
|
78
|
+
s1 += val;
|
|
79
|
+
w++;
|
|
80
|
+
v = s2 - (s1 * s1) / w;
|
|
81
|
+
const i4 = i3 - 1;
|
|
82
|
+
if (i4 !== 0) {
|
|
83
|
+
for (let j = 2; j <= nClasses; j++) {
|
|
84
|
+
if (matrixTwo[l][j] >= v + matrixTwo[i4][j - 1]) {
|
|
85
|
+
matrixOne[l][j] = i4;
|
|
86
|
+
matrixTwo[l][j] = v + matrixTwo[i4][j - 1];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
matrixOne[l][1] = 1;
|
|
92
|
+
matrixTwo[l][1] = v;
|
|
93
|
+
}
|
|
94
|
+
const breaks = Array(nClasses);
|
|
95
|
+
breaks[nClasses - 1] = sample[n - 1];
|
|
96
|
+
for (let j = nClasses, k = n; j >= 2; j--) {
|
|
97
|
+
const id = matrixOne[k][j] - 1;
|
|
98
|
+
breaks[j - 2] = sample[id];
|
|
99
|
+
k = matrixOne[k][j] - 1;
|
|
100
|
+
}
|
|
101
|
+
return breaks;
|
|
102
|
+
};
|
|
103
|
+
VectorClassifications.calculatePrettyBreaks = (values, nClasses) => {
|
|
104
|
+
const minimum = Math.min(...values);
|
|
105
|
+
const maximum = Math.max(...values);
|
|
106
|
+
const breaks = [];
|
|
107
|
+
if (nClasses < 1) {
|
|
108
|
+
breaks.push(maximum);
|
|
109
|
+
return breaks;
|
|
110
|
+
}
|
|
111
|
+
const minimumCount = Math.floor(nClasses / 3);
|
|
112
|
+
const shrink = 0.75;
|
|
113
|
+
const highBias = 1.5;
|
|
114
|
+
const adjustBias = 0.5 + 1.5 * highBias;
|
|
115
|
+
const divisions = nClasses;
|
|
116
|
+
const h = highBias;
|
|
117
|
+
let cell;
|
|
118
|
+
let small = false;
|
|
119
|
+
const dx = maximum - minimum;
|
|
120
|
+
let U;
|
|
121
|
+
cell = Math.max(Math.abs(minimum), Math.abs(maximum));
|
|
122
|
+
if (adjustBias >= 1.5 * h + 0.5) {
|
|
123
|
+
U = 1 + 1.0 / (1 + h);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
U = 1 + 1.5 / (1 + adjustBias);
|
|
127
|
+
}
|
|
128
|
+
small = dx < cell * U * Math.max(1, divisions) * 1e-7 * 3.0;
|
|
129
|
+
if (small) {
|
|
130
|
+
if (cell > 10) {
|
|
131
|
+
cell = 9 + cell / 10;
|
|
132
|
+
cell = cell * shrink;
|
|
133
|
+
}
|
|
134
|
+
if (minimumCount > 1) {
|
|
135
|
+
cell = cell / minimumCount;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
cell = dx;
|
|
140
|
+
if (divisions > 1) {
|
|
141
|
+
cell = cell / divisions;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (cell < 20 * 1e-7) {
|
|
145
|
+
cell = 20 * 1e-7;
|
|
146
|
+
}
|
|
147
|
+
const base = Math.pow(10.0, Math.floor(Math.log10(cell)));
|
|
148
|
+
let unit = base;
|
|
149
|
+
if (2 * base - cell < h * (cell - unit)) {
|
|
150
|
+
unit = 2.0 * base;
|
|
151
|
+
if (5 * base - cell < adjustBias * (cell - unit)) {
|
|
152
|
+
unit = 5.0 * base;
|
|
153
|
+
if (10.0 * base - cell < h * (cell - unit)) {
|
|
154
|
+
unit = 10.0 * base;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
let start = Math.floor(minimum / unit + 1e-7);
|
|
159
|
+
let end = Math.ceil(maximum / unit - 1e-7);
|
|
160
|
+
while (start * unit > minimum + 1e-7 * unit) {
|
|
161
|
+
start = start - 1;
|
|
162
|
+
}
|
|
163
|
+
while (end * unit < maximum - 1e-7 * unit) {
|
|
164
|
+
end = end + 1;
|
|
165
|
+
}
|
|
166
|
+
let k = Math.floor(0.5 + end - start);
|
|
167
|
+
if (k < minimumCount) {
|
|
168
|
+
k = minimumCount - k;
|
|
169
|
+
if (start >= 0) {
|
|
170
|
+
end = end + k / 2;
|
|
171
|
+
start = start - k / 2 + (k % 2);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
start = start - k / 2;
|
|
175
|
+
end = end + k / 2 + (k % 2);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const minimumBreak = start * unit;
|
|
179
|
+
const count = end - start;
|
|
180
|
+
for (let i = 1; i < count + 1; i++) {
|
|
181
|
+
breaks.push(minimumBreak + i * unit);
|
|
182
|
+
}
|
|
183
|
+
if (breaks.length === 0) {
|
|
184
|
+
return breaks;
|
|
185
|
+
}
|
|
186
|
+
if (breaks[0] < minimum) {
|
|
187
|
+
breaks[0] = minimum;
|
|
188
|
+
}
|
|
189
|
+
if (breaks[breaks.length - 1] > maximum) {
|
|
190
|
+
breaks[breaks.length - 1] = maximum;
|
|
191
|
+
}
|
|
192
|
+
if (minimum < 0.0 && maximum > 0.0) {
|
|
193
|
+
const breaksMinusZero = breaks.map(b => b - 0.0);
|
|
194
|
+
let posOfMin = 0;
|
|
195
|
+
for (let i = 1; i < breaks.length; i++) {
|
|
196
|
+
if (Math.abs(breaksMinusZero[i]) < Math.abs(breaksMinusZero[posOfMin])) {
|
|
197
|
+
posOfMin = i;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
breaks[posOfMin] = 0.0; // Set the closest break to zero
|
|
201
|
+
}
|
|
202
|
+
return breaks;
|
|
203
|
+
};
|
|
204
|
+
VectorClassifications.calculateLogarithmicBreaks = (values, nClasses) => {
|
|
205
|
+
const minimum = Math.min(...values);
|
|
206
|
+
const maximum = Math.max(...values);
|
|
207
|
+
let positiveMinimum = Number.MAX_VALUE;
|
|
208
|
+
let breaks = [];
|
|
209
|
+
positiveMinimum = minimum;
|
|
210
|
+
const actualLogMin = Math.log10(positiveMinimum);
|
|
211
|
+
let logMin = Math.floor(actualLogMin);
|
|
212
|
+
const logMax = Math.ceil(Math.log10(maximum));
|
|
213
|
+
let prettyBreaks = VectorClassifications.calculatePrettyBreaks([logMin, logMax], nClasses);
|
|
214
|
+
while (prettyBreaks.length > 0 && prettyBreaks[0] < actualLogMin) {
|
|
215
|
+
logMin += 1.0;
|
|
216
|
+
prettyBreaks = VectorClassifications.calculatePrettyBreaks([logMin, logMax], nClasses);
|
|
217
|
+
}
|
|
218
|
+
breaks = prettyBreaks;
|
|
219
|
+
for (let i = 0; i < breaks.length; i++) {
|
|
220
|
+
breaks[i] = Math.pow(10, breaks[i]);
|
|
221
|
+
}
|
|
222
|
+
return breaks;
|
|
223
|
+
};
|
|
224
|
+
})(VectorClassifications || (VectorClassifications = {}));
|
|
225
|
+
export var GeoTiffClassifications;
|
|
226
|
+
(function (GeoTiffClassifications) {
|
|
227
|
+
GeoTiffClassifications.classifyQuantileBreaks = async (nClasses, bandNumber, url, colorRampType) => {
|
|
228
|
+
const breaks = [];
|
|
229
|
+
const isDiscrete = colorRampType === 'discrete';
|
|
230
|
+
const pool = new Pool();
|
|
231
|
+
const tiff = await fromUrl(url);
|
|
232
|
+
const image = await tiff.getImage();
|
|
233
|
+
const values = await image.readRasters({ pool });
|
|
234
|
+
// Band numbers are 1 indexed
|
|
235
|
+
const bandValues = values[bandNumber - 1];
|
|
236
|
+
const bandSortedValues = bandValues
|
|
237
|
+
.filter(value => value !== 0)
|
|
238
|
+
.sort((a, b) => a - b);
|
|
239
|
+
pool.destroy();
|
|
240
|
+
if (!bandSortedValues) {
|
|
241
|
+
return [];
|
|
242
|
+
}
|
|
243
|
+
// Adapted from https://github.com/GeoTIFF/geoblaze/blob/master/src/histogram/histogram.core.js#L64
|
|
244
|
+
// iterate through values and use a counter to
|
|
245
|
+
// decide when to set up the next bin.
|
|
246
|
+
let numValuesInCurrentBin;
|
|
247
|
+
let valuesPerBin;
|
|
248
|
+
let startIndex;
|
|
249
|
+
if (isDiscrete) {
|
|
250
|
+
valuesPerBin = bandSortedValues.length / nClasses;
|
|
251
|
+
numValuesInCurrentBin = 0;
|
|
252
|
+
startIndex = 0;
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
valuesPerBin = bandSortedValues.length / (nClasses - 1);
|
|
256
|
+
breaks.push(1);
|
|
257
|
+
numValuesInCurrentBin = 1;
|
|
258
|
+
startIndex = 1;
|
|
259
|
+
}
|
|
260
|
+
for (let i = startIndex; i < bandSortedValues.length; i++) {
|
|
261
|
+
if (numValuesInCurrentBin + 1 < valuesPerBin) {
|
|
262
|
+
numValuesInCurrentBin++;
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
breaks.push(bandSortedValues[i]);
|
|
266
|
+
numValuesInCurrentBin = 0;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (breaks.length !== nClasses) {
|
|
270
|
+
//TODO: This should be set based on the type of bandSortedValues I think
|
|
271
|
+
breaks.push(65535);
|
|
272
|
+
}
|
|
273
|
+
return breaks;
|
|
274
|
+
};
|
|
275
|
+
GeoTiffClassifications.classifyContinuousBreaks = (nClasses, minimumValue, maximumValue, colorRampType) => {
|
|
276
|
+
const min = minimumValue;
|
|
277
|
+
const max = maximumValue;
|
|
278
|
+
if (min > max) {
|
|
279
|
+
return [];
|
|
280
|
+
}
|
|
281
|
+
const isDiscrete = colorRampType === 'discrete';
|
|
282
|
+
const breaks = [];
|
|
283
|
+
const numberOfEntries = nClasses;
|
|
284
|
+
if (isDiscrete) {
|
|
285
|
+
const intervalDiff = ((max - min) * (numberOfEntries - 1)) / numberOfEntries;
|
|
286
|
+
for (let i = 1; i < numberOfEntries; i++) {
|
|
287
|
+
const val = i / numberOfEntries;
|
|
288
|
+
breaks.push(min + val * intervalDiff);
|
|
289
|
+
}
|
|
290
|
+
breaks.push(max);
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
for (let i = 0; i <= numberOfEntries; i++) {
|
|
294
|
+
if (i === 26) {
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
const val = i / numberOfEntries;
|
|
298
|
+
breaks.push(min + val * (max - min));
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return breaks;
|
|
302
|
+
};
|
|
303
|
+
GeoTiffClassifications.classifyEqualIntervalBreaks = (nClasses, minimumValue, maximumValue, colorRampType) => {
|
|
304
|
+
const min = minimumValue;
|
|
305
|
+
const max = maximumValue;
|
|
306
|
+
if (min > max) {
|
|
307
|
+
return [];
|
|
308
|
+
}
|
|
309
|
+
const isDiscrete = colorRampType === 'discrete';
|
|
310
|
+
const breaks = [];
|
|
311
|
+
if (isDiscrete) {
|
|
312
|
+
const intervalDiff = (max - min) / nClasses;
|
|
313
|
+
for (let i = 1; i < nClasses; i++) {
|
|
314
|
+
breaks.push(min + i * intervalDiff);
|
|
315
|
+
}
|
|
316
|
+
breaks.push(max);
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
const intervalDiff = (max - min) / (nClasses - 1);
|
|
320
|
+
for (let i = 0; i < nClasses; i++) {
|
|
321
|
+
breaks.push(min + i * intervalDiff);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return breaks;
|
|
325
|
+
};
|
|
326
|
+
})(GeoTiffClassifications || (GeoTiffClassifications = {}));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface IColorMap {
|
|
3
|
+
name: string;
|
|
4
|
+
colors: string[];
|
|
5
|
+
}
|
|
6
|
+
interface ICanvasSelectComponentProps {
|
|
7
|
+
selectedRamp: string;
|
|
8
|
+
setSelected: (item: any) => void;
|
|
9
|
+
}
|
|
10
|
+
declare const CanvasSelectComponent: ({ selectedRamp, setSelected }: ICanvasSelectComponentProps) => React.JSX.Element;
|
|
11
|
+
export default CanvasSelectComponent;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { Button } from '@jupyterlab/ui-components';
|
|
2
|
+
import colormap from 'colormap';
|
|
3
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
4
|
+
import ColorRampEntry from './ColorRampEntry';
|
|
5
|
+
const CanvasSelectComponent = ({ selectedRamp, setSelected }) => {
|
|
6
|
+
const colorRampNames = [
|
|
7
|
+
'jet',
|
|
8
|
+
// 'hsv', 11 steps min
|
|
9
|
+
'hot',
|
|
10
|
+
'cool',
|
|
11
|
+
'spring',
|
|
12
|
+
'summer',
|
|
13
|
+
'autumn',
|
|
14
|
+
'winter',
|
|
15
|
+
'bone',
|
|
16
|
+
'copper',
|
|
17
|
+
'greys',
|
|
18
|
+
'YiGnBu',
|
|
19
|
+
'greens',
|
|
20
|
+
'YiOrRd',
|
|
21
|
+
'bluered',
|
|
22
|
+
'RdBu',
|
|
23
|
+
// 'picnic', 11 steps min
|
|
24
|
+
'rainbow',
|
|
25
|
+
'portland',
|
|
26
|
+
'blackbody',
|
|
27
|
+
'earth',
|
|
28
|
+
'electric',
|
|
29
|
+
'viridis',
|
|
30
|
+
'inferno',
|
|
31
|
+
'magma',
|
|
32
|
+
'plasma',
|
|
33
|
+
'warm',
|
|
34
|
+
// 'rainbow-soft', 11 steps min
|
|
35
|
+
'bathymetry',
|
|
36
|
+
'cdom',
|
|
37
|
+
'chlorophyll',
|
|
38
|
+
'density',
|
|
39
|
+
'freesurface-blue',
|
|
40
|
+
'freesurface-red',
|
|
41
|
+
'oxygen',
|
|
42
|
+
'par',
|
|
43
|
+
'phase',
|
|
44
|
+
'salinity',
|
|
45
|
+
'temperature',
|
|
46
|
+
'turbidity',
|
|
47
|
+
'velocity-blue',
|
|
48
|
+
'velocity-green'
|
|
49
|
+
// 'cubehelix' 16 steps min
|
|
50
|
+
];
|
|
51
|
+
const containerRef = useRef(null);
|
|
52
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
53
|
+
const [colorMaps, setColorMaps] = useState([]);
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
const colorMapList = [];
|
|
56
|
+
colorRampNames.forEach(name => {
|
|
57
|
+
const colorRamp = colormap({
|
|
58
|
+
colormap: name,
|
|
59
|
+
nshades: 255,
|
|
60
|
+
format: 'rgbaString'
|
|
61
|
+
});
|
|
62
|
+
const colorMap = { name: name, colors: colorRamp };
|
|
63
|
+
colorMapList.push(colorMap);
|
|
64
|
+
setColorMaps(colorMapList);
|
|
65
|
+
});
|
|
66
|
+
}, []);
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (colorMaps.length > 0) {
|
|
69
|
+
updateCanvas(selectedRamp);
|
|
70
|
+
}
|
|
71
|
+
}, [selectedRamp]);
|
|
72
|
+
const toggleDropdown = () => {
|
|
73
|
+
setIsOpen(!isOpen);
|
|
74
|
+
};
|
|
75
|
+
const selectItem = (item) => {
|
|
76
|
+
setSelected(item);
|
|
77
|
+
setIsOpen(false);
|
|
78
|
+
updateCanvas(item);
|
|
79
|
+
};
|
|
80
|
+
const handleOutsideClick = (event) => {
|
|
81
|
+
if (!containerRef.current) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (!containerRef.current.contains(event.target)) {
|
|
85
|
+
setIsOpen(false);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const updateCanvas = (rampName) => {
|
|
89
|
+
// update canvas for displayed color ramp
|
|
90
|
+
const canvas = document.getElementById('cv');
|
|
91
|
+
if (!canvas) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
canvas.style.visibility = 'hidden';
|
|
95
|
+
const ctx = canvas.getContext('2d');
|
|
96
|
+
if (!ctx) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const ramp = colorMaps.filter(c => c.name === rampName);
|
|
100
|
+
for (let i = 0; i <= 255; i++) {
|
|
101
|
+
ctx.beginPath();
|
|
102
|
+
const color = ramp[0].colors[i];
|
|
103
|
+
ctx.fillStyle = color;
|
|
104
|
+
ctx.fillRect(i * 2, 0, 2, 50);
|
|
105
|
+
}
|
|
106
|
+
canvas.style.visibility = 'initial';
|
|
107
|
+
};
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
document.addEventListener('mousedown', handleOutsideClick);
|
|
110
|
+
return () => {
|
|
111
|
+
document.removeEventListener('mousedown', handleOutsideClick);
|
|
112
|
+
};
|
|
113
|
+
}, []);
|
|
114
|
+
return (React.createElement("div", { ref: containerRef, className: "jp-gis-canvas-button-wrapper" },
|
|
115
|
+
React.createElement(Button, { id: "jp-gis-canvas-button", onClick: toggleDropdown, className: "jp-Dialog-button jp-gis-canvas-button" },
|
|
116
|
+
React.createElement("canvas", { id: "cv", className: "jp-gis-color-canvas-display", height: "30" })),
|
|
117
|
+
React.createElement("div", { className: `jp-gis-color-ramp-dropdown ${isOpen ? 'jp-gis-open' : ''}` }, colorMaps.map((item, index) => (React.createElement(ColorRampEntry, { index: index, colorMap: item, onClick: selectItem }))))));
|
|
118
|
+
};
|
|
119
|
+
export default CanvasSelectComponent;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { IDict } from '@jupytergis/schema';
|
|
3
|
+
interface IColorRampProps {
|
|
4
|
+
modeOptions: string[];
|
|
5
|
+
layerParams: IDict;
|
|
6
|
+
classifyFunc: (selectedMode: string, numberOfShades: string, selectedRamp: string, setIsLoading: (isLoading: boolean) => void) => void;
|
|
7
|
+
showModeRow: boolean;
|
|
8
|
+
}
|
|
9
|
+
export type ColorRampOptions = {
|
|
10
|
+
selectedRamp: string;
|
|
11
|
+
numberOfShades: string;
|
|
12
|
+
selectedMode: string;
|
|
13
|
+
};
|
|
14
|
+
declare const ColorRamp: ({ layerParams, modeOptions, classifyFunc, showModeRow }: IColorRampProps) => React.JSX.Element;
|
|
15
|
+
export default ColorRamp;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Button } from '@jupyterlab/ui-components';
|
|
2
|
+
import React, { useEffect, useState } from 'react';
|
|
3
|
+
import CanvasSelectComponent from './CanvasSelectComponent';
|
|
4
|
+
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
|
|
5
|
+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
6
|
+
import ModeSelectRow from './ModeSelectRow';
|
|
7
|
+
const ColorRamp = ({ layerParams, modeOptions, classifyFunc, showModeRow }) => {
|
|
8
|
+
const [selectedRamp, setSelectedRamp] = useState('');
|
|
9
|
+
const [selectedMode, setSelectedMode] = useState('');
|
|
10
|
+
const [numberOfShades, setNumberOfShades] = useState('');
|
|
11
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
populateOptions();
|
|
14
|
+
}, []);
|
|
15
|
+
const populateOptions = async () => {
|
|
16
|
+
let nClasses, singleBandMode, colorRamp;
|
|
17
|
+
if (layerParams.symbologyState) {
|
|
18
|
+
nClasses = layerParams.symbologyState.nClasses;
|
|
19
|
+
singleBandMode = layerParams.symbologyState.mode;
|
|
20
|
+
colorRamp = layerParams.symbologyState.colorRamp;
|
|
21
|
+
}
|
|
22
|
+
setNumberOfShades(nClasses ? nClasses : '9');
|
|
23
|
+
setSelectedMode(singleBandMode ? singleBandMode : 'equal interval');
|
|
24
|
+
setSelectedRamp(colorRamp ? colorRamp : 'cool');
|
|
25
|
+
};
|
|
26
|
+
return (React.createElement("div", { className: "jp-gis-color-ramp-container" },
|
|
27
|
+
React.createElement("div", { className: "jp-gis-symbology-row" },
|
|
28
|
+
React.createElement("label", { htmlFor: "color-ramp-select" }, "Color Ramp:"),
|
|
29
|
+
React.createElement(CanvasSelectComponent, { selectedRamp: selectedRamp, setSelected: setSelectedRamp })),
|
|
30
|
+
showModeRow && (React.createElement(ModeSelectRow, { modeOptions: modeOptions, numberOfShades: numberOfShades, setNumberOfShades: setNumberOfShades, selectedMode: selectedMode, setSelectedMode: setSelectedMode })),
|
|
31
|
+
isLoading ? (React.createElement(FontAwesomeIcon, { icon: faSpinner, className: "jp-gis-loading-spinner" })) : (React.createElement(Button, { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", onClick: () => classifyFunc(selectedMode, numberOfShades, selectedRamp, setIsLoading) }, "Classify"))));
|
|
32
|
+
};
|
|
33
|
+
export default ColorRamp;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { IColorMap } from './CanvasSelectComponent';
|
|
3
|
+
interface IColorRampEntryProps {
|
|
4
|
+
index: number;
|
|
5
|
+
colorMap: IColorMap;
|
|
6
|
+
onClick: (item: any) => void;
|
|
7
|
+
}
|
|
8
|
+
declare const ColorRampEntry: ({ index, colorMap, onClick }: IColorRampEntryProps) => React.JSX.Element;
|
|
9
|
+
export default ColorRampEntry;
|