@jupytergis/base 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commands/BaseCommandIDs.d.ts +1 -1
- package/lib/commands/BaseCommandIDs.js +1 -1
- package/lib/commands/index.js +28 -34
- package/lib/constants.js +1 -0
- package/lib/dialogs/symbology/classificationModes.js +12 -16
- package/lib/dialogs/symbology/colorRampUtils.d.ts +47 -3
- package/lib/dialogs/symbology/colorRampUtils.js +112 -13
- package/lib/dialogs/symbology/components/color_ramp/ColorRampSelector.js +6 -14
- package/lib/dialogs/symbology/components/color_ramp/ColorRampSelectorEntry.d.ts +2 -2
- package/lib/dialogs/symbology/components/color_ramp/ColorRampSelectorEntry.js +3 -11
- package/lib/dialogs/symbology/components/color_ramp/RgbaColorPicker.d.ts +13 -0
- package/lib/dialogs/symbology/components/color_ramp/RgbaColorPicker.js +98 -0
- package/lib/dialogs/symbology/components/color_stops/StopContainer.js +3 -1
- package/lib/dialogs/symbology/components/color_stops/StopRow.d.ts +1 -1
- package/lib/dialogs/symbology/components/color_stops/StopRow.js +12 -7
- package/lib/dialogs/symbology/symbologyDialog.d.ts +2 -1
- package/lib/dialogs/symbology/symbologyUtils.d.ts +2 -2
- package/lib/dialogs/symbology/symbologyUtils.js +58 -40
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +14 -2
- package/lib/dialogs/symbology/vector_layer/VectorRendering.js +6 -5
- package/lib/dialogs/symbology/vector_layer/components/ValueSelect.js +3 -1
- package/lib/dialogs/symbology/vector_layer/types/Canonical.js +70 -5
- package/lib/dialogs/symbology/vector_layer/types/Categorized.js +81 -34
- package/lib/dialogs/symbology/vector_layer/types/Graduated.js +155 -43
- package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.js +31 -16
- package/lib/formbuilder/formselectors.js +4 -1
- package/lib/formbuilder/objectform/components/WmsTileSourceUrlInput.d.ts +3 -0
- package/lib/formbuilder/objectform/components/WmsTileSourceUrlInput.js +84 -0
- package/lib/formbuilder/objectform/source/index.d.ts +1 -0
- package/lib/formbuilder/objectform/source/index.js +1 -0
- package/lib/formbuilder/objectform/source/wmsTileSource.d.ts +4 -0
- package/lib/formbuilder/objectform/source/wmsTileSource.js +78 -0
- package/lib/formbuilder/objectform/useSchemaFormState.d.ts +1 -1
- package/lib/mainview/mainView.d.ts +3 -1
- package/lib/mainview/mainView.js +170 -23
- package/lib/menus.js +4 -0
- package/lib/panelview/components/layers.js +19 -2
- package/lib/panelview/components/legendItem.js +14 -4
- package/lib/panelview/filter-panel/Filter.d.ts +3 -0
- package/lib/panelview/filter-panel/Filter.js +9 -9
- package/lib/panelview/leftpanel.js +0 -7
- package/lib/panelview/story-maps/SpectaPanel.js +2 -2
- package/lib/panelview/story-maps/StoryViewerPanel.d.ts +1 -2
- package/lib/panelview/story-maps/StoryViewerPanel.js +1 -1
- package/lib/panelview/story-maps/components/SpectaDesktopView.d.ts +2 -1
- package/lib/panelview/story-maps/components/SpectaDesktopView.js +4 -4
- package/lib/panelview/story-maps/hooks/useStoryMap.d.ts +1 -0
- package/lib/panelview/story-maps/hooks/useStoryMap.js +3 -0
- package/lib/stacBrowser/components/filter-extension/QueryableComboBox.js +61 -20
- package/lib/stacBrowser/hooks/useStacFilterExtension.d.ts +1 -1
- package/lib/stacBrowser/hooks/useStacFilterExtension.js +195 -111
- package/lib/stacBrowser/hooks/useStacSearch.d.ts +1 -0
- package/lib/stacBrowser/hooks/useStacSearch.js +18 -10
- package/lib/tools.d.ts +1 -1
- package/lib/tools.js +3 -3
- package/lib/types.d.ts +7 -1
- package/package.json +5 -2
- package/style/shared/button.css +2 -5
- package/style/shared/input.css +2 -2
- package/style/shared/tabs.css +2 -2
- package/style/storyPanel.css +7 -0
- package/style/symbologyDialog.css +45 -1
|
@@ -8,6 +8,7 @@ export declare const addMarker = "jupytergis:addMarker";
|
|
|
8
8
|
export declare const getGeolocation = "jupytergis:getGeolocation";
|
|
9
9
|
export declare const openLayerBrowser = "jupytergis:openLayerBrowser";
|
|
10
10
|
export declare const openNewRasterDialog = "jupytergis:openNewRasterDialog";
|
|
11
|
+
export declare const openNewWmsDialog = "jupytergis:openNewWmsDialog";
|
|
11
12
|
export declare const openNewVectorTileDialog = "jupytergis:openNewVectorTileDialog";
|
|
12
13
|
export declare const openNewShapefileDialog = "jupytergis:openNewShapefileDialog";
|
|
13
14
|
export declare const openNewGeoJSONDialog = "jupytergis:openNewGeoJSONDialog";
|
|
@@ -33,7 +34,6 @@ export declare const toggleLeftPanel = "jupytergis:toggleLeftPanel";
|
|
|
33
34
|
export declare const toggleRightPanel = "jupytergis:toggleRightPanel";
|
|
34
35
|
export declare const showLayersTab = "jupytergis:showLayersTab";
|
|
35
36
|
export declare const showStacBrowserTab = "jupytergis:showStacBrowserTab";
|
|
36
|
-
export declare const showFiltersTab = "jupytergis:showFiltersTab";
|
|
37
37
|
export declare const showObjectPropertiesTab = "jupytergis:showObjectPropertiesTab";
|
|
38
38
|
export declare const showAnnotationsTab = "jupytergis:showAnnotationsTab";
|
|
39
39
|
export declare const showIdentifyPanelTab = "jupytergis:showIdentifyPanelTab";
|
|
@@ -16,6 +16,7 @@ export const getGeolocation = 'jupytergis:getGeolocation';
|
|
|
16
16
|
export const openLayerBrowser = 'jupytergis:openLayerBrowser';
|
|
17
17
|
// Layer and source
|
|
18
18
|
export const openNewRasterDialog = 'jupytergis:openNewRasterDialog';
|
|
19
|
+
export const openNewWmsDialog = 'jupytergis:openNewWmsDialog';
|
|
19
20
|
export const openNewVectorTileDialog = 'jupytergis:openNewVectorTileDialog';
|
|
20
21
|
export const openNewShapefileDialog = 'jupytergis:openNewShapefileDialog';
|
|
21
22
|
export const openNewGeoJSONDialog = 'jupytergis:openNewGeoJSONDialog';
|
|
@@ -46,7 +47,6 @@ export const toggleRightPanel = 'jupytergis:toggleRightPanel';
|
|
|
46
47
|
// Left panel tabs
|
|
47
48
|
export const showLayersTab = 'jupytergis:showLayersTab';
|
|
48
49
|
export const showStacBrowserTab = 'jupytergis:showStacBrowserTab';
|
|
49
|
-
export const showFiltersTab = 'jupytergis:showFiltersTab';
|
|
50
50
|
// Right panel tabs
|
|
51
51
|
export const showObjectPropertiesTab = 'jupytergis:showObjectPropertiesTab';
|
|
52
52
|
export const showAnnotationsTab = 'jupytergis:showAnnotationsTab';
|
package/lib/commands/index.js
CHANGED
|
@@ -267,7 +267,7 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
267
267
|
/**
|
|
268
268
|
* Source and layers
|
|
269
269
|
*/
|
|
270
|
-
commands.addCommand(CommandIDs.openNewRasterDialog, Object.assign({ label: trans.__('
|
|
270
|
+
commands.addCommand(CommandIDs.openNewRasterDialog, Object.assign({ label: trans.__('Raster Tile'), caption: 'Open a dialog to create a new raster tile layer and source in the current JupyterGIS document.', describedBy: {
|
|
271
271
|
args: {
|
|
272
272
|
type: 'object',
|
|
273
273
|
properties: {},
|
|
@@ -286,7 +286,7 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
286
286
|
sourceType: 'RasterSource',
|
|
287
287
|
layerType: 'RasterLayer',
|
|
288
288
|
}) }, icons.get(CommandIDs.openNewRasterDialog)));
|
|
289
|
-
commands.addCommand(CommandIDs.openNewVectorTileDialog, Object.assign({ label: trans.__('
|
|
289
|
+
commands.addCommand(CommandIDs.openNewVectorTileDialog, Object.assign({ label: trans.__('Vector Tile'), caption: 'Open a dialog to create a new vector tile layer and source in the current JupyterGIS document.', describedBy: {
|
|
290
290
|
args: {
|
|
291
291
|
type: 'object',
|
|
292
292
|
properties: {},
|
|
@@ -305,7 +305,7 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
305
305
|
sourceType: 'VectorTileSource',
|
|
306
306
|
layerType: 'VectorTileLayer',
|
|
307
307
|
}) }, icons.get(CommandIDs.openNewVectorTileDialog)));
|
|
308
|
-
commands.addCommand(CommandIDs.openNewGeoParquetDialog, Object.assign({ label: trans.__('
|
|
308
|
+
commands.addCommand(CommandIDs.openNewGeoParquetDialog, Object.assign({ label: trans.__('GeoParquet'), caption: 'Open a dialog to create a new GeoParquet layer and source in the current JupyterGIS document.', describedBy: {
|
|
309
309
|
args: {
|
|
310
310
|
type: 'object',
|
|
311
311
|
properties: {},
|
|
@@ -325,7 +325,7 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
325
325
|
sourceType: 'GeoParquetSource',
|
|
326
326
|
layerType: 'VectorLayer',
|
|
327
327
|
}) }, icons.get(CommandIDs.openNewGeoParquetDialog)));
|
|
328
|
-
commands.addCommand(CommandIDs.openNewGeoJSONDialog, Object.assign({ label: trans.__('
|
|
328
|
+
commands.addCommand(CommandIDs.openNewGeoJSONDialog, Object.assign({ label: trans.__('GeoJSON'), caption: 'Open a dialog to create a new GeoJSON layer and source in the current JupyterGIS document.', describedBy: {
|
|
329
329
|
args: {
|
|
330
330
|
type: 'object',
|
|
331
331
|
properties: {},
|
|
@@ -344,9 +344,28 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
344
344
|
sourceType: 'GeoJSONSource',
|
|
345
345
|
layerType: 'VectorLayer',
|
|
346
346
|
}) }, icons.get(CommandIDs.openNewGeoJSONDialog)));
|
|
347
|
+
commands.addCommand(CommandIDs.openNewWmsDialog, Object.assign({ label: trans.__('WMS Layer'), caption: 'Open a dialog to create a new WMS layer and source in the current JupyterGIS document.', describedBy: {
|
|
348
|
+
args: {
|
|
349
|
+
type: 'object',
|
|
350
|
+
properties: {},
|
|
351
|
+
},
|
|
352
|
+
}, isEnabled: () => {
|
|
353
|
+
return tracker.currentWidget
|
|
354
|
+
? tracker.currentWidget.model.sharedModel.editable
|
|
355
|
+
: false;
|
|
356
|
+
}, execute: Private.createEntry({
|
|
357
|
+
tracker,
|
|
358
|
+
formSchemaRegistry,
|
|
359
|
+
title: 'Create WMS Layer',
|
|
360
|
+
createLayer: true,
|
|
361
|
+
createSource: true,
|
|
362
|
+
layerData: { name: 'Custom WMS Layer' },
|
|
363
|
+
sourceType: 'WmsTileSource',
|
|
364
|
+
layerType: 'WebGlLayer',
|
|
365
|
+
}) }, icons.get(CommandIDs.openNewWmsDialog)));
|
|
347
366
|
//Add processing commands
|
|
348
367
|
addProcessingCommands(app, commands, tracker, trans, formSchemaRegistry, Object.fromEntries(formSchemaRegistry.getSchemas()));
|
|
349
|
-
commands.addCommand(CommandIDs.openNewHillshadeDialog, Object.assign({ label: trans.__('
|
|
368
|
+
commands.addCommand(CommandIDs.openNewHillshadeDialog, Object.assign({ label: trans.__('Hillshade'), caption: 'Open a dialog to create a new hillshade layer and source in the current JupyterGIS document.', describedBy: {
|
|
350
369
|
args: {
|
|
351
370
|
type: 'object',
|
|
352
371
|
properties: {},
|
|
@@ -365,7 +384,7 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
365
384
|
sourceType: 'RasterDemSource',
|
|
366
385
|
layerType: 'HillshadeLayer',
|
|
367
386
|
}) }, icons.get(CommandIDs.openNewHillshadeDialog)));
|
|
368
|
-
commands.addCommand(CommandIDs.openNewImageDialog, Object.assign({ label: trans.__('
|
|
387
|
+
commands.addCommand(CommandIDs.openNewImageDialog, Object.assign({ label: trans.__('Image'), caption: 'Open a dialog to create a new image layer and source in the current JupyterGIS document.', describedBy: {
|
|
369
388
|
args: {
|
|
370
389
|
type: 'object',
|
|
371
390
|
properties: {},
|
|
@@ -394,7 +413,7 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
394
413
|
sourceType: 'ImageSource',
|
|
395
414
|
layerType: 'ImageLayer',
|
|
396
415
|
}) }, icons.get(CommandIDs.openNewImageDialog)));
|
|
397
|
-
commands.addCommand(CommandIDs.openNewVideoDialog, Object.assign({ label: trans.__('
|
|
416
|
+
commands.addCommand(CommandIDs.openNewVideoDialog, Object.assign({ label: trans.__('Video'), caption: 'Open a dialog to create a new video layer and source in the current JupyterGIS document.', describedBy: {
|
|
398
417
|
args: {
|
|
399
418
|
type: 'object',
|
|
400
419
|
properties: {},
|
|
@@ -426,7 +445,7 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
426
445
|
sourceType: 'VideoSource',
|
|
427
446
|
layerType: 'RasterLayer',
|
|
428
447
|
}) }, icons.get(CommandIDs.openNewVideoDialog)));
|
|
429
|
-
commands.addCommand(CommandIDs.openNewGeoTiffDialog, Object.assign({ label: trans.__('
|
|
448
|
+
commands.addCommand(CommandIDs.openNewGeoTiffDialog, Object.assign({ label: trans.__('GeoTiff'), caption: 'Open a dialog to create a new GeoTiff layer and source in the current JupyterGIS document.', describedBy: {
|
|
430
449
|
args: {
|
|
431
450
|
type: 'object',
|
|
432
451
|
properties: {},
|
|
@@ -449,7 +468,7 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
449
468
|
sourceType: 'GeoTiffSource',
|
|
450
469
|
layerType: 'WebGlLayer',
|
|
451
470
|
}) }, icons.get(CommandIDs.openNewGeoTiffDialog)));
|
|
452
|
-
commands.addCommand(CommandIDs.openNewShapefileDialog, Object.assign({ label: trans.__('
|
|
471
|
+
commands.addCommand(CommandIDs.openNewShapefileDialog, Object.assign({ label: trans.__('Shapefile'), caption: 'Open a dialog to create a new shapefile layer and source in the current JupyterGIS document.', describedBy: {
|
|
453
472
|
args: {
|
|
454
473
|
type: 'object',
|
|
455
474
|
properties: {},
|
|
@@ -1047,31 +1066,6 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
1047
1066
|
commands.notifyCommandChanged(CommandIDs.showStacBrowserTab);
|
|
1048
1067
|
},
|
|
1049
1068
|
});
|
|
1050
|
-
commands.addCommand(CommandIDs.showFiltersTab, {
|
|
1051
|
-
label: trans.__('Show Filters Tab'),
|
|
1052
|
-
caption: 'Show the filters tab in the current JupyterGIS document.',
|
|
1053
|
-
describedBy: {
|
|
1054
|
-
args: {
|
|
1055
|
-
type: 'object',
|
|
1056
|
-
properties: {},
|
|
1057
|
-
},
|
|
1058
|
-
},
|
|
1059
|
-
isEnabled: () => Boolean(tracker.currentWidget),
|
|
1060
|
-
isToggled: () => tracker.currentWidget
|
|
1061
|
-
? !tracker.currentWidget.model.jgisSettings.filtersDisabled
|
|
1062
|
-
: false,
|
|
1063
|
-
execute: async () => {
|
|
1064
|
-
var _a, _b, _c;
|
|
1065
|
-
const current = tracker.currentWidget;
|
|
1066
|
-
if (!current) {
|
|
1067
|
-
return;
|
|
1068
|
-
}
|
|
1069
|
-
const settings = await current.model.getSettings();
|
|
1070
|
-
const currentValue = (_c = (_b = (_a = settings === null || settings === void 0 ? void 0 : settings.composite) === null || _a === void 0 ? void 0 : _a.filtersDisabled) !== null && _b !== void 0 ? _b : current.model.jgisSettings.filtersDisabled) !== null && _c !== void 0 ? _c : false;
|
|
1071
|
-
await (settings === null || settings === void 0 ? void 0 : settings.set('filtersDisabled', !currentValue));
|
|
1072
|
-
commands.notifyCommandChanged(CommandIDs.showFiltersTab);
|
|
1073
|
-
},
|
|
1074
|
-
});
|
|
1075
1069
|
// Right panel tabs
|
|
1076
1070
|
commands.addCommand(CommandIDs.showObjectPropertiesTab, {
|
|
1077
1071
|
label: trans.__('Show Object Properties Tab'),
|
package/lib/constants.js
CHANGED
|
@@ -23,6 +23,7 @@ const iconObject = {
|
|
|
23
23
|
[CommandIDs.undo]: { icon: undoIcon },
|
|
24
24
|
[CommandIDs.openLayerBrowser]: { icon: bookOpenIcon },
|
|
25
25
|
[CommandIDs.openNewRasterDialog]: { icon: rasterIcon },
|
|
26
|
+
[CommandIDs.openNewWmsDialog]: { iconClass: 'fa fa-server' },
|
|
26
27
|
[CommandIDs.openNewVectorTileDialog]: { icon: vectorSquareIcon },
|
|
27
28
|
[CommandIDs.openNewGeoJSONDialog]: { icon: geoJSONIcon },
|
|
28
29
|
[CommandIDs.openNewHillshadeDialog]: { icon: moundIcon },
|
|
@@ -202,22 +202,18 @@ export var VectorClassifications;
|
|
|
202
202
|
return breaks;
|
|
203
203
|
};
|
|
204
204
|
VectorClassifications.calculateLogarithmicBreaks = (values, nClasses) => {
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
const logMax = Math.
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
breaks = prettyBreaks;
|
|
219
|
-
for (let i = 0; i < breaks.length; i++) {
|
|
220
|
-
breaks[i] = Math.pow(10, breaks[i]);
|
|
205
|
+
const positiveValues = values.filter(v => v > 0);
|
|
206
|
+
if (positiveValues.length === 0 || nClasses < 1) {
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
const minimum = Math.min(...positiveValues);
|
|
210
|
+
const maximum = Math.max(...positiveValues);
|
|
211
|
+
const logMin = Math.log10(minimum);
|
|
212
|
+
const logMax = Math.log10(maximum);
|
|
213
|
+
const breaks = [];
|
|
214
|
+
for (let i = 0; i < nClasses; i++) {
|
|
215
|
+
const t = nClasses === 1 ? 0 : i / (nClasses - 1);
|
|
216
|
+
breaks.push(Math.pow(10, logMin + t * (logMax - logMin)));
|
|
221
217
|
}
|
|
222
218
|
return breaks;
|
|
223
219
|
};
|
|
@@ -1,10 +1,49 @@
|
|
|
1
|
+
export type RgbaColor = [number, number, number, number];
|
|
2
|
+
export declare const RGBA_INDEX: {
|
|
3
|
+
readonly r: 0;
|
|
4
|
+
readonly g: 1;
|
|
5
|
+
readonly b: 2;
|
|
6
|
+
readonly a: 3;
|
|
7
|
+
};
|
|
8
|
+
export type RgbaChannel = keyof typeof RGBA_INDEX;
|
|
9
|
+
/** OpenLayers default blue, used as the fallback color throughout symbology dialogs. */
|
|
10
|
+
export declare const DEFAULT_COLOR: RgbaColor;
|
|
11
|
+
/** Default stroke width in pixels, used as the initial value in all symbology dialogs. */
|
|
12
|
+
export declare const DEFAULT_STROKE_WIDTH = 1.25;
|
|
13
|
+
/**
|
|
14
|
+
* Returns true if `val` is a usable solid color: either a hex string or a
|
|
15
|
+
* plain [r,g,b,a] number array. Returns false for OL expression arrays like
|
|
16
|
+
* ['interpolate', ...] whose first element is a string.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isColor(val: unknown): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Recursively searches an OL expression tree for the first node whose first
|
|
21
|
+
* element matches `operator` (e.g. `'interpolate'`, `'case'`).
|
|
22
|
+
* Returns the matching sub-expression, or `null` if not found.
|
|
23
|
+
*/
|
|
24
|
+
export declare function findExprNode(expr: unknown, operator: string): unknown[] | null;
|
|
1
25
|
export interface IColorMap {
|
|
2
26
|
name: ColorRampName;
|
|
3
27
|
colors: string[];
|
|
28
|
+
type: 'continuous' | 'categorical';
|
|
4
29
|
}
|
|
5
30
|
export declare const COLOR_RAMP_NAMES: readonly ["jet", "hsv", "hot", "cool", "spring", "summer", "autumn", "winter", "bone", "copper", "greys", "YiGnBu", "greens", "YiOrRd", "bluered", "RdBu", "picnic", "rainbow", "portland", "blackbody", "earth", "electric", "viridis", "inferno", "magma", "plasma", "warm", "rainbow-soft", "bathymetry", "cdom", "chlorophyll", "density", "freesurface-blue", "freesurface-red", "oxygen", "par", "phase", "salinity", "temperature", "turbidity", "velocity-blue", "velocity-green", "cubehelix", "ice", "oxy", "matter", "amp", "tempo", "rain", "topo", "balance", "delta", "curl", "diff", "tarn"];
|
|
6
31
|
export declare const COLOR_RAMP_DEFAULTS: Partial<Record<ColorRampName, number>>;
|
|
7
|
-
export
|
|
32
|
+
export declare const D3_CATEGORICAL_SCHEMES: {
|
|
33
|
+
readonly schemeCategory10: readonly string[];
|
|
34
|
+
readonly schemeAccent: readonly string[];
|
|
35
|
+
readonly schemeDark2: readonly string[];
|
|
36
|
+
readonly schemeObservable10: readonly string[];
|
|
37
|
+
readonly schemePaired: readonly string[];
|
|
38
|
+
readonly schemePastel1: readonly string[];
|
|
39
|
+
readonly schemePastel2: readonly string[];
|
|
40
|
+
readonly schemeSet1: readonly string[];
|
|
41
|
+
readonly schemeSet2: readonly string[];
|
|
42
|
+
readonly schemeSet3: readonly string[];
|
|
43
|
+
readonly schemeTableau10: readonly string[];
|
|
44
|
+
};
|
|
45
|
+
export type D3SchemeName = keyof typeof D3_CATEGORICAL_SCHEMES;
|
|
46
|
+
export type ColorRampName = (typeof COLOR_RAMP_NAMES)[number] | D3SchemeName;
|
|
8
47
|
export declare const getColorMapList: () => IColorMap[];
|
|
9
48
|
/**
|
|
10
49
|
* Hook that loads and sets color maps.
|
|
@@ -15,6 +54,11 @@ export declare const useColorMapList: (setColorMaps: (maps: IColorMap[]) => void
|
|
|
15
54
|
*/
|
|
16
55
|
export declare const ensureHexColorCode: (color: number[] | string) => string;
|
|
17
56
|
/**
|
|
18
|
-
* Convert hex
|
|
57
|
+
* Convert any color value (hex string or [r,g,b,a] array) to RgbaColor.
|
|
58
|
+
* Alpha must be in 0-1 range; a warning is logged if it is not.
|
|
59
|
+
*/
|
|
60
|
+
export declare function colorToRgba(color: unknown): RgbaColor;
|
|
61
|
+
/**
|
|
62
|
+
* Draw a color ramp to a canvas.
|
|
19
63
|
*/
|
|
20
|
-
export declare
|
|
64
|
+
export declare const drawColorRamp: (canvas: HTMLCanvasElement, colorRamp: IColorMap) => void;
|
|
@@ -11,8 +11,46 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
};
|
|
12
12
|
import colormap from 'colormap';
|
|
13
13
|
import colorScale from 'colormap/colorScale.js';
|
|
14
|
+
import * as d3Chromatic from 'd3-scale-chromatic';
|
|
14
15
|
import { useEffect } from 'react';
|
|
15
16
|
import rawCmocean from "./components/color_ramp/cmocean.json";
|
|
17
|
+
import { objectEntries } from "../../tools";
|
|
18
|
+
export const RGBA_INDEX = { r: 0, g: 1, b: 2, a: 3 };
|
|
19
|
+
/** OpenLayers default blue, used as the fallback color throughout symbology dialogs. */
|
|
20
|
+
export const DEFAULT_COLOR = [51, 153, 204, 1];
|
|
21
|
+
/** Default stroke width in pixels, used as the initial value in all symbology dialogs. */
|
|
22
|
+
export const DEFAULT_STROKE_WIDTH = 1.25;
|
|
23
|
+
/**
|
|
24
|
+
* Returns true if `val` is a usable solid color: either a hex string or a
|
|
25
|
+
* plain [r,g,b,a] number array. Returns false for OL expression arrays like
|
|
26
|
+
* ['interpolate', ...] whose first element is a string.
|
|
27
|
+
*/
|
|
28
|
+
export function isColor(val) {
|
|
29
|
+
if (typeof val === 'string') {
|
|
30
|
+
return /^#?[0-9A-Fa-f]{3,8}$/.test(val);
|
|
31
|
+
}
|
|
32
|
+
return Array.isArray(val) && val.length >= 3 && typeof val[0] === 'number';
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Recursively searches an OL expression tree for the first node whose first
|
|
36
|
+
* element matches `operator` (e.g. `'interpolate'`, `'case'`).
|
|
37
|
+
* Returns the matching sub-expression, or `null` if not found.
|
|
38
|
+
*/
|
|
39
|
+
export function findExprNode(expr, operator) {
|
|
40
|
+
if (!Array.isArray(expr)) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
if (expr[0] === operator) {
|
|
44
|
+
return expr;
|
|
45
|
+
}
|
|
46
|
+
for (const child of expr) {
|
|
47
|
+
const found = findExprNode(child, operator);
|
|
48
|
+
if (found) {
|
|
49
|
+
return found;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
16
54
|
const { __license__: _ } = rawCmocean, cmocean = __rest(rawCmocean, ["__license__"]);
|
|
17
55
|
Object.assign(colorScale, cmocean);
|
|
18
56
|
export const COLOR_RAMP_NAMES = [
|
|
@@ -78,6 +116,19 @@ export const COLOR_RAMP_DEFAULTS = {
|
|
|
78
116
|
'rainbow-soft': 11,
|
|
79
117
|
cubehelix: 16,
|
|
80
118
|
};
|
|
119
|
+
export const D3_CATEGORICAL_SCHEMES = {
|
|
120
|
+
schemeCategory10: d3Chromatic.schemeCategory10,
|
|
121
|
+
schemeAccent: d3Chromatic.schemeAccent,
|
|
122
|
+
schemeDark2: d3Chromatic.schemeDark2,
|
|
123
|
+
schemeObservable10: d3Chromatic.schemeObservable10,
|
|
124
|
+
schemePaired: d3Chromatic.schemePaired,
|
|
125
|
+
schemePastel1: d3Chromatic.schemePastel1,
|
|
126
|
+
schemePastel2: d3Chromatic.schemePastel2,
|
|
127
|
+
schemeSet1: d3Chromatic.schemeSet1,
|
|
128
|
+
schemeSet2: d3Chromatic.schemeSet2,
|
|
129
|
+
schemeSet3: d3Chromatic.schemeSet3,
|
|
130
|
+
schemeTableau10: d3Chromatic.schemeTableau10,
|
|
131
|
+
};
|
|
81
132
|
export const getColorMapList = () => {
|
|
82
133
|
const colorMapList = [];
|
|
83
134
|
COLOR_RAMP_NAMES.forEach(name => {
|
|
@@ -86,7 +137,14 @@ export const getColorMapList = () => {
|
|
|
86
137
|
nshades: 255,
|
|
87
138
|
format: 'rgbaString',
|
|
88
139
|
});
|
|
89
|
-
colorMapList.push({ name, colors: colorRamp });
|
|
140
|
+
colorMapList.push({ name, colors: colorRamp, type: 'continuous' });
|
|
141
|
+
});
|
|
142
|
+
objectEntries(D3_CATEGORICAL_SCHEMES).forEach(([name, colors]) => {
|
|
143
|
+
colorMapList.push({
|
|
144
|
+
name,
|
|
145
|
+
colors: colors.map(c => c.toString()),
|
|
146
|
+
type: 'categorical',
|
|
147
|
+
});
|
|
90
148
|
});
|
|
91
149
|
return colorMapList;
|
|
92
150
|
};
|
|
@@ -115,18 +173,59 @@ export const ensureHexColorCode = (color) => {
|
|
|
115
173
|
return '#' + hex;
|
|
116
174
|
};
|
|
117
175
|
/**
|
|
118
|
-
* Convert hex
|
|
176
|
+
* Convert any color value (hex string or [r,g,b,a] array) to RgbaColor.
|
|
177
|
+
* Alpha must be in 0-1 range; a warning is logged if it is not.
|
|
119
178
|
*/
|
|
120
|
-
export function
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
179
|
+
export function colorToRgba(color) {
|
|
180
|
+
if (typeof color === 'string') {
|
|
181
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color);
|
|
182
|
+
if (!result) {
|
|
183
|
+
console.warn('Unable to parse hex color, using default');
|
|
184
|
+
return DEFAULT_COLOR;
|
|
185
|
+
}
|
|
186
|
+
return [
|
|
187
|
+
parseInt(result[1], 16),
|
|
188
|
+
parseInt(result[2], 16),
|
|
189
|
+
parseInt(result[3], 16),
|
|
190
|
+
1,
|
|
191
|
+
];
|
|
125
192
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
193
|
+
if (isColor(color)) {
|
|
194
|
+
const [r, g, b, a] = color;
|
|
195
|
+
const alpha = a !== null && a !== void 0 ? a : 1;
|
|
196
|
+
if (alpha > 1) {
|
|
197
|
+
console.warn(`Color alpha ${alpha} is out of 0-1 range`);
|
|
198
|
+
}
|
|
199
|
+
return [r, g, b, alpha];
|
|
200
|
+
}
|
|
201
|
+
return DEFAULT_COLOR;
|
|
132
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* Draw a color ramp to a canvas.
|
|
205
|
+
*/
|
|
206
|
+
export const drawColorRamp = (canvas, colorRamp) => {
|
|
207
|
+
const ctx = canvas.getContext('2d');
|
|
208
|
+
const colors = colorRamp.colors;
|
|
209
|
+
if (!ctx || !colors || colors.length === 0) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const width = canvas.width;
|
|
213
|
+
const height = canvas.height;
|
|
214
|
+
if (colorRamp.type === 'categorical') {
|
|
215
|
+
const blockWidth = width / colors.length;
|
|
216
|
+
colors.forEach((color, i) => {
|
|
217
|
+
ctx.beginPath();
|
|
218
|
+
ctx.fillStyle = color;
|
|
219
|
+
ctx.fillRect(i * blockWidth, 0, blockWidth, height);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
const gradient = ctx.createLinearGradient(0, 0, width, 0);
|
|
224
|
+
const step = 1 / (colors.length - 1);
|
|
225
|
+
colors.forEach((color, i) => {
|
|
226
|
+
gradient.addColorStop(i * step, color);
|
|
227
|
+
});
|
|
228
|
+
ctx.fillStyle = gradient;
|
|
229
|
+
ctx.fillRect(0, 0, width, height);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { Button } from '@jupyterlab/ui-components';
|
|
14
14
|
import React, { useEffect, useRef, useState } from 'react';
|
|
15
|
-
import { useColorMapList, } from "../../colorRampUtils";
|
|
15
|
+
import { useColorMapList, drawColorRamp, } from "../../colorRampUtils";
|
|
16
16
|
import ColorRampSelectorEntry from './ColorRampSelectorEntry';
|
|
17
17
|
const ColorRampSelector = ({ selectedRamp, setSelected, reverse, setReverse, }) => {
|
|
18
18
|
const containerRef = useRef(null);
|
|
@@ -49,23 +49,15 @@ const ColorRampSelector = ({ selectedRamp, setSelected, reverse, setReverse, })
|
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
51
|
canvas.style.visibility = 'hidden';
|
|
52
|
-
const
|
|
53
|
-
if (!
|
|
52
|
+
const ramp = colorMaps.find(c => c.name === rampName);
|
|
53
|
+
if (!ramp) {
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
if (reverse) {
|
|
59
|
-
colors = [...colors].reverse();
|
|
60
|
-
}
|
|
56
|
+
const displayRamp = reverse
|
|
57
|
+
? Object.assign(Object.assign({}, ramp), { colors: [...ramp.colors].reverse() }) : ramp;
|
|
61
58
|
canvas.width = canvasWidth;
|
|
62
59
|
canvas.height = canvasHeight;
|
|
63
|
-
|
|
64
|
-
ctx.beginPath();
|
|
65
|
-
const color = colors[i];
|
|
66
|
-
ctx.fillStyle = color;
|
|
67
|
-
ctx.fillRect(i * 2, 0, 2, canvasHeight);
|
|
68
|
-
}
|
|
60
|
+
drawColorRamp(canvas, displayRamp);
|
|
69
61
|
canvas.style.visibility = 'initial';
|
|
70
62
|
};
|
|
71
63
|
useEffect(() => {
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
* - `onClick`: Callback fired with the ramp name when clicked.
|
|
11
11
|
*/
|
|
12
12
|
import React from 'react';
|
|
13
|
-
import { IColorMap } from "../../colorRampUtils";
|
|
13
|
+
import { ColorRampName, IColorMap } from "../../colorRampUtils";
|
|
14
14
|
interface IColorRampSelectorEntryProps {
|
|
15
15
|
index: number;
|
|
16
16
|
colorMap: IColorMap;
|
|
17
|
-
onClick: (item:
|
|
17
|
+
onClick: (item: ColorRampName) => void;
|
|
18
18
|
}
|
|
19
19
|
declare const ColorRampSelectorEntry: React.FC<IColorRampSelectorEntryProps>;
|
|
20
20
|
export default ColorRampSelectorEntry;
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* - `onClick`: Callback fired with the ramp name when clicked.
|
|
11
11
|
*/
|
|
12
12
|
import React, { useEffect } from 'react';
|
|
13
|
+
import { drawColorRamp, } from "../../colorRampUtils";
|
|
13
14
|
const ColorRampSelectorEntry = ({ index, colorMap, onClick, }) => {
|
|
14
15
|
const canvasWidth = 512;
|
|
15
16
|
const canvasHeight = 30;
|
|
@@ -20,17 +21,8 @@ const ColorRampSelectorEntry = ({ index, colorMap, onClick, }) => {
|
|
|
20
21
|
}
|
|
21
22
|
canvas.width = canvasWidth;
|
|
22
23
|
canvas.height = canvasHeight;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
for (let i = 0; i <= 255; i++) {
|
|
28
|
-
ctx.beginPath();
|
|
29
|
-
const color = colorMap.colors[i];
|
|
30
|
-
ctx.fillStyle = color;
|
|
31
|
-
ctx.fillRect(i * 2, 0, 2, canvasHeight);
|
|
32
|
-
}
|
|
33
|
-
}, []);
|
|
24
|
+
drawColorRamp(canvas, colorMap);
|
|
25
|
+
}, [colorMap, index]);
|
|
34
26
|
return (React.createElement("div", { key: colorMap.name, onClick: () => onClick(colorMap.name), className: "jp-gis-color-ramp-entry" },
|
|
35
27
|
React.createElement("span", { className: "jp-gis-color-label" }, colorMap.name),
|
|
36
28
|
React.createElement("canvas", { id: `cv-${index}`, width: canvasWidth, height: canvasHeight, className: "jp-gis-color-canvas" })));
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { RgbaColor } from "../../colorRampUtils";
|
|
3
|
+
export type { RgbaColor };
|
|
4
|
+
interface IRgbaColorPickerProps {
|
|
5
|
+
color: RgbaColor;
|
|
6
|
+
onChange: (color: RgbaColor) => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A swatch button that opens a floating RGBA color picker on click.
|
|
10
|
+
* Color is stored as [r, g, b, a] where r/g/b are 0-255 and a is 0-1.
|
|
11
|
+
*/
|
|
12
|
+
declare const RgbaColorPicker: React.FC<IRgbaColorPickerProps>;
|
|
13
|
+
export default RgbaColorPicker;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { RgbaColorPicker as ReactColorfulRgba } from 'react-colorful';
|
|
3
|
+
import { RGBA_INDEX, } from "../../colorRampUtils";
|
|
4
|
+
/**
|
|
5
|
+
* A swatch button that opens a floating RGBA color picker on click.
|
|
6
|
+
* Color is stored as [r, g, b, a] where r/g/b are 0-255 and a is 0-1.
|
|
7
|
+
*/
|
|
8
|
+
const RgbaColorPicker = ({ color, onChange, }) => {
|
|
9
|
+
const [open, setOpen] = useState(false);
|
|
10
|
+
const containerRef = useRef(null);
|
|
11
|
+
const [r, g, b, a] = color;
|
|
12
|
+
const [inputs, setInputs] = useState({
|
|
13
|
+
r: String(Math.round(r)),
|
|
14
|
+
g: String(Math.round(g)),
|
|
15
|
+
b: String(Math.round(b)),
|
|
16
|
+
a: String(Math.round(a * 100)),
|
|
17
|
+
});
|
|
18
|
+
// Sync text inputs when color changes externally (e.g. picker drag)
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
setInputs({
|
|
21
|
+
r: String(Math.round(r)),
|
|
22
|
+
g: String(Math.round(g)),
|
|
23
|
+
b: String(Math.round(b)),
|
|
24
|
+
a: String(Math.round(a * 100)),
|
|
25
|
+
});
|
|
26
|
+
}, [r, g, b, a]);
|
|
27
|
+
const swatchStyle = {
|
|
28
|
+
background: `rgba(${r},${g},${b},${a})`,
|
|
29
|
+
width: 28,
|
|
30
|
+
height: 28,
|
|
31
|
+
borderRadius: 4,
|
|
32
|
+
border: '1px solid var(--jp-border-color1, #ccc)',
|
|
33
|
+
cursor: 'pointer',
|
|
34
|
+
flexShrink: 0,
|
|
35
|
+
};
|
|
36
|
+
const handlePickerChange = useCallback((c) => {
|
|
37
|
+
onChange([c.r, c.g, c.b, c.a]);
|
|
38
|
+
}, [onChange]);
|
|
39
|
+
const handleChannelInput = (channel, value) => {
|
|
40
|
+
setInputs(prev => (Object.assign(Object.assign({}, prev), { [channel]: value })));
|
|
41
|
+
const num = Number(value);
|
|
42
|
+
if (channel === 'a') {
|
|
43
|
+
if (num >= 0 && num <= 100) {
|
|
44
|
+
onChange([r, g, b, num / 100]);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
if (num >= 0 && num <= 255) {
|
|
49
|
+
const newColor = [...color];
|
|
50
|
+
newColor[RGBA_INDEX[channel]] = Math.round(num);
|
|
51
|
+
onChange(newColor);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const handleTransparentChange = (checked) => {
|
|
56
|
+
// Always restore to fully opaque rather than the previous alpha,
|
|
57
|
+
// which could have been near-zero and appear broken to the user.
|
|
58
|
+
onChange([r, g, b, checked ? 0 : 1]);
|
|
59
|
+
};
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (!open) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const handleClickOutside = (e) => {
|
|
65
|
+
if (containerRef.current &&
|
|
66
|
+
!containerRef.current.contains(e.target)) {
|
|
67
|
+
setOpen(false);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
71
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
72
|
+
}, [open]);
|
|
73
|
+
return (React.createElement("div", { ref: containerRef, className: "jp-gis-rgba-picker", style: { position: 'relative' } },
|
|
74
|
+
React.createElement("div", { style: swatchStyle, onClick: () => setOpen(v => !v) }),
|
|
75
|
+
open && (React.createElement("div", { style: {
|
|
76
|
+
position: 'absolute',
|
|
77
|
+
zIndex: 1000,
|
|
78
|
+
top: '110%',
|
|
79
|
+
left: 0,
|
|
80
|
+
background: 'var(--jp-layout-color1, #fff)',
|
|
81
|
+
border: '1px solid var(--jp-border-color1, #ccc)',
|
|
82
|
+
borderRadius: 6,
|
|
83
|
+
padding: 8,
|
|
84
|
+
boxShadow: '0 4px 12px rgba(0,0,0,0.2)',
|
|
85
|
+
} },
|
|
86
|
+
React.createElement(ReactColorfulRgba, { color: { r, g, b, a }, onChange: handlePickerChange }),
|
|
87
|
+
React.createElement("div", { className: "jp-gis-rgba-inputs" },
|
|
88
|
+
['r', 'g', 'b'].map(ch => (React.createElement("div", { key: ch, className: "jp-gis-rgba-field" },
|
|
89
|
+
React.createElement("label", null, ch.toUpperCase()),
|
|
90
|
+
React.createElement("input", { className: "jp-mod-styled", type: "number", min: 0, max: 255, value: inputs[ch], onChange: e => handleChannelInput(ch, e.target.value) })))),
|
|
91
|
+
React.createElement("div", { className: "jp-gis-rgba-field" },
|
|
92
|
+
React.createElement("label", null, "A%"),
|
|
93
|
+
React.createElement("input", { className: "jp-mod-styled", type: "number", min: 0, max: 100, value: inputs.a, onChange: e => handleChannelInput('a', e.target.value) }))),
|
|
94
|
+
React.createElement("label", { className: "jp-gis-transparent-label" },
|
|
95
|
+
React.createElement("input", { type: "checkbox", checked: a === 0, onChange: e => handleTransparentChange(e.target.checked) }),
|
|
96
|
+
"No color")))));
|
|
97
|
+
};
|
|
98
|
+
export default RgbaColorPicker;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { Button } from '@jupyterlab/ui-components';
|
|
2
|
+
import { UUID } from '@lumino/coreutils';
|
|
2
3
|
import React from 'react';
|
|
3
4
|
import StopRow from './StopRow';
|
|
4
5
|
const StopContainer = ({ selectedMethod, stopRows, setStopRows, }) => {
|
|
5
6
|
const addStopRow = () => {
|
|
6
7
|
setStopRows([
|
|
7
8
|
{
|
|
9
|
+
id: UUID.uuid4(),
|
|
8
10
|
stop: 0,
|
|
9
11
|
output: [0, 0, 0, 1],
|
|
10
12
|
},
|
|
@@ -21,7 +23,7 @@ const StopContainer = ({ selectedMethod, stopRows, setStopRows, }) => {
|
|
|
21
23
|
React.createElement("div", { className: "jp-gis-stop-labels", style: { display: 'flex', gap: 6 } },
|
|
22
24
|
React.createElement("span", { style: { flex: '0 0 18%' } }, "Value"),
|
|
23
25
|
React.createElement("span", null, "Output Value")),
|
|
24
|
-
stopRows.map((stop, index) => (React.createElement(StopRow, { key:
|
|
26
|
+
stopRows.map((stop, index) => (React.createElement(StopRow, { key: stop.id, index: index, dataValue: stop.stop, symbologyValue: stop.output, stopRows: stopRows, setStopRows: setStopRows, deleteRow: () => deleteStopRow(index), useNumber: selectedMethod === 'radius' ? true : false })))),
|
|
25
27
|
React.createElement("div", { className: "jp-gis-symbology-button-container" },
|
|
26
28
|
React.createElement(Button, { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", onClick: addStopRow }, "Add Stop"))));
|
|
27
29
|
};
|