@jupytergis/base 0.1.3 → 0.1.5
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.js +0 -37
- package/lib/constants.d.ts +0 -2
- package/lib/constants.js +0 -4
- package/lib/dialogs/components/symbology/SingleBandPseudoColor.js +4 -2
- package/lib/mainview/mainView.d.ts +8 -7
- package/lib/mainview/mainView.js +57 -90
- package/package.json +2 -2
- package/style/base.css +0 -1
- package/style/symbologyDialog.css +28 -0
- package/lib/dialogs/terrainDialog.d.ts +0 -21
- package/lib/dialogs/terrainDialog.js +0 -60
- package/style/terrainDialog.css +0 -14
package/lib/commands.js
CHANGED
|
@@ -3,7 +3,6 @@ import { CommandIDs, icons } from './constants';
|
|
|
3
3
|
import { CreationFormDialog } from './dialogs/formdialog';
|
|
4
4
|
import { LayerBrowserWidget } from './dialogs/layerBrowserDialog';
|
|
5
5
|
import { SymbologyWidget } from './dialogs/symbologyDialog';
|
|
6
|
-
import { TerrainDialogWidget } from './dialogs/terrainDialog';
|
|
7
6
|
/**
|
|
8
7
|
* Add the commands to the application's command registry.
|
|
9
8
|
*/
|
|
@@ -538,29 +537,6 @@ export function addCommands(app, tracker, translator, formSchemaRegistry, layerB
|
|
|
538
537
|
});
|
|
539
538
|
}
|
|
540
539
|
});
|
|
541
|
-
/**
|
|
542
|
-
* Terrain commands
|
|
543
|
-
*/
|
|
544
|
-
commands.addCommand(CommandIDs.newTerrain, Object.assign({ label: trans.__('New Terrain'), isEnabled: () => {
|
|
545
|
-
return tracker.currentWidget
|
|
546
|
-
? tracker.currentWidget.context.model.sharedModel.editable
|
|
547
|
-
: false;
|
|
548
|
-
}, execute: Private.createTerrainDialog(tracker) }, icons.get(CommandIDs.newTerrain)));
|
|
549
|
-
commands.addCommand(CommandIDs.removeTerrain, {
|
|
550
|
-
label: trans.__('Remove Terrain'),
|
|
551
|
-
isEnabled: () => {
|
|
552
|
-
return tracker.currentWidget
|
|
553
|
-
? tracker.currentWidget.context.model.sharedModel.editable
|
|
554
|
-
: false;
|
|
555
|
-
},
|
|
556
|
-
execute: () => {
|
|
557
|
-
var _a;
|
|
558
|
-
(_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.context.model.setTerrain({
|
|
559
|
-
source: '',
|
|
560
|
-
exaggeration: 0
|
|
561
|
-
});
|
|
562
|
-
}
|
|
563
|
-
});
|
|
564
540
|
// Console commands
|
|
565
541
|
commands.addCommand(CommandIDs.toggleConsole, {
|
|
566
542
|
label: trans.__('Toggle console'),
|
|
@@ -635,19 +611,6 @@ var Private;
|
|
|
635
611
|
};
|
|
636
612
|
}
|
|
637
613
|
Private.createLayerBrowser = createLayerBrowser;
|
|
638
|
-
function createTerrainDialog(tracker) {
|
|
639
|
-
return async () => {
|
|
640
|
-
const current = tracker.currentWidget;
|
|
641
|
-
if (!current) {
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
644
|
-
const dialog = new TerrainDialogWidget({
|
|
645
|
-
context: current.context
|
|
646
|
-
});
|
|
647
|
-
await dialog.launch();
|
|
648
|
-
};
|
|
649
|
-
}
|
|
650
|
-
Private.createTerrainDialog = createTerrainDialog;
|
|
651
614
|
function createSymbologyDialog(tracker, state) {
|
|
652
615
|
return async () => {
|
|
653
616
|
const current = tracker.currentWidget;
|
package/lib/constants.d.ts
CHANGED
|
@@ -38,8 +38,6 @@ export declare namespace CommandIDs {
|
|
|
38
38
|
const moveLayerToNewGroup = "jupytergis:moveLayerToNewGroup";
|
|
39
39
|
const renameSource = "jupytergis:renameSource";
|
|
40
40
|
const removeSource = "jupytergis:removeSource";
|
|
41
|
-
const newTerrain = "jupytergis:newTerrain";
|
|
42
|
-
const removeTerrain = "jupytergis:removeTerrain";
|
|
43
41
|
const toggleConsole = "jupytergis:toggleConsole";
|
|
44
42
|
const invokeCompleter = "jupytergis:invokeConsoleCompleter";
|
|
45
43
|
const removeConsole = "jupytergis:removeConsole";
|
package/lib/constants.js
CHANGED
|
@@ -46,9 +46,6 @@ export var CommandIDs;
|
|
|
46
46
|
// Source actions
|
|
47
47
|
CommandIDs.renameSource = 'jupytergis:renameSource';
|
|
48
48
|
CommandIDs.removeSource = 'jupytergis:removeSource';
|
|
49
|
-
// Terrain stuff
|
|
50
|
-
CommandIDs.newTerrain = 'jupytergis:newTerrain';
|
|
51
|
-
CommandIDs.removeTerrain = 'jupytergis:removeTerrain';
|
|
52
49
|
// Console commands
|
|
53
50
|
CommandIDs.toggleConsole = 'jupytergis:toggleConsole';
|
|
54
51
|
CommandIDs.invokeCompleter = 'jupytergis:invokeConsoleCompleter';
|
|
@@ -80,7 +77,6 @@ const iconObject = {
|
|
|
80
77
|
[CommandIDs.newVideoEntry]: { iconClass: 'fa fa-video' },
|
|
81
78
|
[CommandIDs.newShapefileLayer]: { iconClass: 'fa fa-file' },
|
|
82
79
|
[CommandIDs.newGeoTiffEntry]: { iconClass: 'fa fa-image' },
|
|
83
|
-
[CommandIDs.newTerrain]: { iconClass: 'fa fa-mountain' },
|
|
84
80
|
[CommandIDs.symbology]: { iconClass: 'fa fa-brush' }
|
|
85
81
|
};
|
|
86
82
|
/**
|
|
@@ -2,9 +2,9 @@ import { faSpinner } from '@fortawesome/free-solid-svg-icons';
|
|
|
2
2
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
3
3
|
import { Button } from '@jupyterlab/ui-components';
|
|
4
4
|
import React, { useEffect, useRef, useState } from 'react';
|
|
5
|
+
import { getGdal } from '../../../gdal';
|
|
5
6
|
import BandRow from './BandRow';
|
|
6
7
|
import StopRow from './StopRow';
|
|
7
|
-
import { getGdal } from '../../../gdal';
|
|
8
8
|
const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerId }) => {
|
|
9
9
|
const functions = ['discrete', 'linear', 'exact'];
|
|
10
10
|
const stopRowsRef = useRef();
|
|
@@ -248,7 +248,9 @@ const SingleBandPseudoColor = ({ context, state, okSignalPromise, cancel, layerI
|
|
|
248
248
|
(currentBand.stats.maximum - currentBand.stats.minimum));
|
|
249
249
|
};
|
|
250
250
|
return (React.createElement("div", { className: "jp-gis-layer-symbology-container" },
|
|
251
|
-
React.createElement("div", { className: "jp-gis-band-container" }, bandRows.length === 0 ? (React.createElement(
|
|
251
|
+
React.createElement("div", { className: "jp-gis-band-container" }, bandRows.length === 0 ? (React.createElement("div", { className: "jp-gis-band-info-loading-container" },
|
|
252
|
+
React.createElement("span", null, "Fetching band info..."),
|
|
253
|
+
React.createElement(FontAwesomeIcon, { icon: faSpinner, className: "jp-gis-loading-spinner" }))) : (React.createElement(BandRow
|
|
252
254
|
// Band numbers are 1 indexed
|
|
253
255
|
, {
|
|
254
256
|
// Band numbers are 1 indexed
|
|
@@ -47,6 +47,14 @@ export declare class MainView extends React.Component<IProps, IStates> {
|
|
|
47
47
|
*/
|
|
48
48
|
updateLayers(layerIds: string[]): void;
|
|
49
49
|
private _updateLayersImpl;
|
|
50
|
+
/**
|
|
51
|
+
* Build the map layer.
|
|
52
|
+
*
|
|
53
|
+
* @param id - id of the layer.
|
|
54
|
+
* @param layer - the layer object.
|
|
55
|
+
* @returns - the map layer.
|
|
56
|
+
*/
|
|
57
|
+
private _buildMapLayer;
|
|
50
58
|
/**
|
|
51
59
|
* Add a layer to the map.
|
|
52
60
|
*
|
|
@@ -65,13 +73,6 @@ export declare class MainView extends React.Component<IProps, IStates> {
|
|
|
65
73
|
* @returns
|
|
66
74
|
*/
|
|
67
75
|
private hillshadeMath;
|
|
68
|
-
/**
|
|
69
|
-
* Move a layer in the stack.
|
|
70
|
-
*
|
|
71
|
-
* @param id - id of the layer.
|
|
72
|
-
* @param index - expected index of the layer.
|
|
73
|
-
*/
|
|
74
|
-
moveLayer(id: string, index: number | undefined): void;
|
|
75
76
|
/**
|
|
76
77
|
* Update a layer of the map.
|
|
77
78
|
*
|
package/lib/mainview/mainView.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { JupyterGISModel } from '@jupytergis/schema';
|
|
2
2
|
import { UUID } from '@lumino/coreutils';
|
|
3
|
-
import { Map as OlMap, View } from 'ol';
|
|
3
|
+
import { Collection, Map as OlMap, View } from 'ol';
|
|
4
4
|
import { ScaleLine } from 'ol/control';
|
|
5
5
|
import { GeoJSON, MVT } from 'ol/format';
|
|
6
6
|
import DragAndDrop from 'ol/interaction/DragAndDrop';
|
|
@@ -37,7 +37,9 @@ export class MainView extends React.Component {
|
|
|
37
37
|
style: defaultStyle
|
|
38
38
|
};
|
|
39
39
|
const layerStyle = Object.assign({}, defaultRules);
|
|
40
|
-
if (layer.filters &&
|
|
40
|
+
if (layer.filters &&
|
|
41
|
+
layer.filters.logicalOp &&
|
|
42
|
+
layer.filters.appliedFilters.length !== 0) {
|
|
41
43
|
const filterExpr = [];
|
|
42
44
|
// 'Any' and 'All' operators require more than one argument
|
|
43
45
|
// So if there's only one filter, skip that part to avoid error
|
|
@@ -215,10 +217,7 @@ export class MainView extends React.Component {
|
|
|
215
217
|
projection: projection.getCode(),
|
|
216
218
|
zoom
|
|
217
219
|
};
|
|
218
|
-
|
|
219
|
-
if (currentOptions.extent) {
|
|
220
|
-
updatedOptions.extent = view.calculateExtent();
|
|
221
|
-
}
|
|
220
|
+
updatedOptions.extent = view.calculateExtent();
|
|
222
221
|
this._model.setOptions(Object.assign(Object.assign({}, currentOptions), updatedOptions));
|
|
223
222
|
});
|
|
224
223
|
if (JupyterGISModel.getOrderedLayerIds(this._model).length !== 0) {
|
|
@@ -306,13 +305,17 @@ export class MainView extends React.Component {
|
|
|
306
305
|
case 'GeoJSONSource': {
|
|
307
306
|
const data = ((_a = source.parameters) === null || _a === void 0 ? void 0 : _a.data) ||
|
|
308
307
|
(await this._model.readGeoJSON((_b = source.parameters) === null || _b === void 0 ? void 0 : _b.path));
|
|
309
|
-
const format = new GeoJSON(
|
|
308
|
+
const format = new GeoJSON({
|
|
309
|
+
featureProjection: this._Map.getView().getProjection()
|
|
310
|
+
});
|
|
310
311
|
// TODO: Don't hardcode projection
|
|
312
|
+
const featureArray = format.readFeatures(data, {
|
|
313
|
+
dataProjection: 'EPSG:4326',
|
|
314
|
+
featureProjection: this._Map.getView().getProjection()
|
|
315
|
+
});
|
|
316
|
+
const featureCollection = new Collection(featureArray);
|
|
311
317
|
newSource = new VectorSource({
|
|
312
|
-
features:
|
|
313
|
-
dataProjection: 'EPSG:4326',
|
|
314
|
-
featureProjection: this._Map.getView().getProjection()
|
|
315
|
-
})
|
|
318
|
+
features: featureCollection
|
|
316
319
|
});
|
|
317
320
|
break;
|
|
318
321
|
}
|
|
@@ -426,59 +429,30 @@ export class MainView extends React.Component {
|
|
|
426
429
|
this._updateLayersImpl(layerIds);
|
|
427
430
|
}
|
|
428
431
|
async _updateLayersImpl(layerIds) {
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
// bottom.
|
|
432
|
-
// This is to ensure that the beforeId (layer on top of the one we add/move)
|
|
433
|
-
// is already added/moved in the map.
|
|
434
|
-
const reversedLayerIds = layerIds.slice().reverse();
|
|
435
|
-
for (const layerId of reversedLayerIds) {
|
|
432
|
+
const mapLayers = [];
|
|
433
|
+
for (const layerId of layerIds) {
|
|
436
434
|
const layer = this._model.sharedModel.getLayer(layerId);
|
|
437
435
|
if (!layer) {
|
|
438
436
|
console.log(`Layer id ${layerId} does not exist`);
|
|
439
|
-
|
|
440
|
-
}
|
|
441
|
-
// Get the expected index in the map.
|
|
442
|
-
const currentLayerIds = [...previousLayerIds];
|
|
443
|
-
let indexInMap = currentLayerIds.length;
|
|
444
|
-
const nextLayer = layerIds[layerIds.indexOf(layerId) + 1];
|
|
445
|
-
if (nextLayer !== undefined) {
|
|
446
|
-
indexInMap = currentLayerIds.indexOf(nextLayer);
|
|
447
|
-
if (indexInMap === -1) {
|
|
448
|
-
indexInMap = currentLayerIds.length;
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
if (this.getLayer(layerId)) {
|
|
452
|
-
this.moveLayer(layerId, indexInMap);
|
|
437
|
+
continue;
|
|
453
438
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
// Remove the element of the previous list as treated.
|
|
458
|
-
const index = previousLayerIds.indexOf(layerId);
|
|
459
|
-
if (index > -1) {
|
|
460
|
-
previousLayerIds.splice(index, 1);
|
|
439
|
+
const newMapLayer = await this._buildMapLayer(layerId, layer);
|
|
440
|
+
if (newMapLayer !== undefined) {
|
|
441
|
+
mapLayers.push(newMapLayer);
|
|
461
442
|
}
|
|
462
443
|
}
|
|
463
|
-
|
|
464
|
-
previousLayerIds.forEach(layerId => {
|
|
465
|
-
this._Map.removeLayer(layerId);
|
|
466
|
-
});
|
|
444
|
+
this._Map.setLayers(mapLayers);
|
|
467
445
|
this._ready = true;
|
|
468
446
|
}
|
|
469
447
|
/**
|
|
470
|
-
*
|
|
448
|
+
* Build the map layer.
|
|
471
449
|
*
|
|
472
450
|
* @param id - id of the layer.
|
|
473
451
|
* @param layer - the layer object.
|
|
474
|
-
* @
|
|
452
|
+
* @returns - the map layer.
|
|
475
453
|
*/
|
|
476
|
-
async
|
|
454
|
+
async _buildMapLayer(id, layer) {
|
|
477
455
|
var _a;
|
|
478
|
-
if (this.getLayer(id)) {
|
|
479
|
-
// Layer already exists
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
456
|
const sourceId = (_a = layer.parameters) === null || _a === void 0 ? void 0 : _a.source;
|
|
483
457
|
const source = this._model.sharedModel.getSource(sourceId);
|
|
484
458
|
if (!source) {
|
|
@@ -487,14 +461,14 @@ export class MainView extends React.Component {
|
|
|
487
461
|
if (!this._sources[sourceId]) {
|
|
488
462
|
await this.addSource(sourceId, source);
|
|
489
463
|
}
|
|
490
|
-
let
|
|
464
|
+
let newMapLayer;
|
|
491
465
|
let layerParameters;
|
|
492
466
|
// TODO: OpenLayers provides a bunch of sources for specific tile
|
|
493
467
|
// providers, so maybe set up some way to use those
|
|
494
468
|
switch (layer.type) {
|
|
495
469
|
case 'RasterLayer': {
|
|
496
470
|
layerParameters = layer.parameters;
|
|
497
|
-
|
|
471
|
+
newMapLayer = new TileLayer({
|
|
498
472
|
opacity: layerParameters.opacity,
|
|
499
473
|
visible: layer.visible,
|
|
500
474
|
source: this._sources[layerParameters.source]
|
|
@@ -503,7 +477,7 @@ export class MainView extends React.Component {
|
|
|
503
477
|
}
|
|
504
478
|
case 'VectorLayer': {
|
|
505
479
|
layerParameters = layer.parameters;
|
|
506
|
-
|
|
480
|
+
newMapLayer = new VectorLayer({
|
|
507
481
|
opacity: layerParameters.opacity,
|
|
508
482
|
visible: layer.visible,
|
|
509
483
|
source: this._sources[layerParameters.source],
|
|
@@ -513,19 +487,16 @@ export class MainView extends React.Component {
|
|
|
513
487
|
}
|
|
514
488
|
case 'VectorTileLayer': {
|
|
515
489
|
layerParameters = layer.parameters;
|
|
516
|
-
|
|
517
|
-
return;
|
|
518
|
-
}
|
|
519
|
-
newLayer = new VectorTileLayer({
|
|
490
|
+
newMapLayer = new VectorTileLayer({
|
|
520
491
|
opacity: layerParameters.opacity,
|
|
521
492
|
source: this._sources[layerParameters.source]
|
|
522
493
|
});
|
|
523
|
-
this.updateLayer(id, layer,
|
|
494
|
+
this.updateLayer(id, layer, newMapLayer);
|
|
524
495
|
break;
|
|
525
496
|
}
|
|
526
497
|
case 'HillshadeLayer': {
|
|
527
498
|
layerParameters = layer.parameters;
|
|
528
|
-
|
|
499
|
+
newMapLayer = new WebGlTileLayer({
|
|
529
500
|
opacity: 0.3,
|
|
530
501
|
source: this._sources[layerParameters.source],
|
|
531
502
|
style: {
|
|
@@ -536,7 +507,7 @@ export class MainView extends React.Component {
|
|
|
536
507
|
}
|
|
537
508
|
case 'ImageLayer': {
|
|
538
509
|
layerParameters = layer.parameters;
|
|
539
|
-
|
|
510
|
+
newMapLayer = new ImageLayer({
|
|
540
511
|
opacity: layerParameters.opacity,
|
|
541
512
|
source: this._sources[layerParameters.source]
|
|
542
513
|
});
|
|
@@ -552,43 +523,32 @@ export class MainView extends React.Component {
|
|
|
552
523
|
if (layerParameters.color) {
|
|
553
524
|
layerOptions['style'] = { color: layerParameters.color };
|
|
554
525
|
}
|
|
555
|
-
|
|
526
|
+
newMapLayer = new WebGlTileLayer(layerOptions);
|
|
556
527
|
break;
|
|
557
528
|
}
|
|
558
529
|
}
|
|
559
530
|
// OpenLayers doesn't have name/id field so add it
|
|
560
|
-
|
|
531
|
+
newMapLayer.set('id', id);
|
|
561
532
|
// we need to keep track of which source has which layers
|
|
562
533
|
this._sourceToLayerMap.set(layerParameters.source, id);
|
|
563
|
-
|
|
534
|
+
return newMapLayer;
|
|
564
535
|
}
|
|
565
536
|
/**
|
|
566
|
-
*
|
|
537
|
+
* Add a layer to the map.
|
|
567
538
|
*
|
|
568
539
|
* @param id - id of the layer.
|
|
540
|
+
* @param layer - the layer object.
|
|
569
541
|
* @param index - expected index of the layer.
|
|
570
542
|
*/
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
let beforeId = undefined;
|
|
575
|
-
if (!(index === undefined) && index < currentLayerIds.length) {
|
|
576
|
-
beforeId = currentLayerIds[index];
|
|
577
|
-
}
|
|
578
|
-
const layerArray = this._Map.getLayers().getArray();
|
|
579
|
-
const movingLayer = this.getLayer(id);
|
|
580
|
-
if (!movingLayer || !index || !beforeId) {
|
|
543
|
+
async addLayer(id, layer, index) {
|
|
544
|
+
if (this.getLayer(id)) {
|
|
545
|
+
// Layer already exists
|
|
581
546
|
return;
|
|
582
547
|
}
|
|
583
|
-
const
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
if (!beforeLayer) {
|
|
587
|
-
return;
|
|
548
|
+
const newMapLayer = await this._buildMapLayer(id, layer);
|
|
549
|
+
if (newMapLayer !== undefined) {
|
|
550
|
+
this._Map.getLayers().insertAt(index, newMapLayer);
|
|
588
551
|
}
|
|
589
|
-
const indexOfBeforeLayer = layerArray.indexOf(beforeLayer);
|
|
590
|
-
layerArray.splice(indexOfBeforeLayer, 0, movingLayer);
|
|
591
|
-
this._Map.setLayers(layerArray);
|
|
592
552
|
}
|
|
593
553
|
/**
|
|
594
554
|
* Update a layer of the map.
|
|
@@ -662,14 +622,19 @@ export class MainView extends React.Component {
|
|
|
662
622
|
}
|
|
663
623
|
updateOptions(options) {
|
|
664
624
|
const view = this._Map.getView();
|
|
665
|
-
//
|
|
666
|
-
if (options.extent) {
|
|
625
|
+
// Use the extent only if explicitly requested (QGIS files).
|
|
626
|
+
if (options.extent && options.useExtent) {
|
|
667
627
|
view.fit(options.extent);
|
|
668
628
|
}
|
|
669
629
|
else {
|
|
670
630
|
const centerCoord = fromLonLat([options.longitude || 0, options.latitude || 0], this._Map.getView().getProjection());
|
|
671
631
|
this._Map.getView().setZoom(options.zoom || 0);
|
|
672
632
|
this._Map.getView().setCenter(centerCoord);
|
|
633
|
+
// Save the extent if it does not exists, to allow proper export to qgis.
|
|
634
|
+
if (options.extent === undefined) {
|
|
635
|
+
options.extent = view.calculateExtent();
|
|
636
|
+
this._model.setOptions(options);
|
|
637
|
+
}
|
|
673
638
|
}
|
|
674
639
|
view.setRotation(options.bearing || 0);
|
|
675
640
|
}
|
|
@@ -709,12 +674,14 @@ export class MainView extends React.Component {
|
|
|
709
674
|
}
|
|
710
675
|
else {
|
|
711
676
|
const mapLayer = this.getLayer(change.id);
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
677
|
+
const layerTree = JupyterGISModel.getOrderedLayerIds(this._model);
|
|
678
|
+
if (mapLayer) {
|
|
679
|
+
if (layerTree.includes(change.id)) {
|
|
680
|
+
this.updateLayer(change.id, layer, mapLayer);
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
683
|
+
this.updateLayers(layerTree);
|
|
684
|
+
}
|
|
718
685
|
}
|
|
719
686
|
}
|
|
720
687
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jupytergis/base",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "A JupyterLab extension for 3D modelling.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jupyter",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@deathbeds/jupyterlab-rjsf": "^1.1.0",
|
|
42
42
|
"@jupyter/docprovider": "^2.0.0",
|
|
43
43
|
"@jupyter/ydoc": "^1.0.0",
|
|
44
|
-
"@jupytergis/schema": "^0.1.
|
|
44
|
+
"@jupytergis/schema": "^0.1.5",
|
|
45
45
|
"@jupyterlab/application": "^4.0.0",
|
|
46
46
|
"@jupyterlab/apputils": "^4.0.0",
|
|
47
47
|
"@jupyterlab/completer": "^4.2.4",
|
package/style/base.css
CHANGED
|
@@ -92,3 +92,31 @@
|
|
|
92
92
|
.jp-gis-symbology-button-container {
|
|
93
93
|
padding-top: 0.5rem;
|
|
94
94
|
}
|
|
95
|
+
|
|
96
|
+
.jp-gis-band-info-loading-container {
|
|
97
|
+
display: flex;
|
|
98
|
+
justify-content: center;
|
|
99
|
+
align-items: center;
|
|
100
|
+
gap: 2rem;
|
|
101
|
+
margin: auto;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.jp-gis-loading-spinner {
|
|
105
|
+
animation: spin 4s linear infinite;
|
|
106
|
+
font-size: 1.5rem;
|
|
107
|
+
padding: 1rem 0;
|
|
108
|
+
margin: auto;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.jp-gis-band-info-loading-container > span {
|
|
112
|
+
font-size: 1rem;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@keyframes spin {
|
|
116
|
+
from {
|
|
117
|
+
transform: rotate(0deg);
|
|
118
|
+
}
|
|
119
|
+
to {
|
|
120
|
+
transform: rotate(360deg);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { IJupyterGISModel } from '@jupytergis/schema';
|
|
2
|
-
import { Dialog } from '@jupyterlab/apputils';
|
|
3
|
-
import { DocumentRegistry } from '@jupyterlab/docregistry';
|
|
4
|
-
import { PromiseDelegate } from '@lumino/coreutils';
|
|
5
|
-
import { Signal } from '@lumino/signaling';
|
|
6
|
-
import React from 'react';
|
|
7
|
-
interface ITerrainDialogProps {
|
|
8
|
-
context: DocumentRegistry.IContext<IJupyterGISModel>;
|
|
9
|
-
okSignalPromise: PromiseDelegate<Signal<TerrainDialogWidget, null>>;
|
|
10
|
-
cancel: () => void;
|
|
11
|
-
}
|
|
12
|
-
declare const TerrainDialog: ({ context, okSignalPromise, cancel }: ITerrainDialogProps) => React.JSX.Element;
|
|
13
|
-
export interface ITerrainDialogOptions {
|
|
14
|
-
context: DocumentRegistry.IContext<IJupyterGISModel>;
|
|
15
|
-
}
|
|
16
|
-
export declare class TerrainDialogWidget extends Dialog<boolean> {
|
|
17
|
-
private okSignal;
|
|
18
|
-
constructor(options: ITerrainDialogOptions);
|
|
19
|
-
resolve(index?: number): void;
|
|
20
|
-
}
|
|
21
|
-
export default TerrainDialog;
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { Dialog } from '@jupyterlab/apputils';
|
|
2
|
-
import { PromiseDelegate } from '@lumino/coreutils';
|
|
3
|
-
import { Signal } from '@lumino/signaling';
|
|
4
|
-
import React, { useEffect, useRef, useState } from 'react';
|
|
5
|
-
const TerrainDialog = ({ context, okSignalPromise, cancel }) => {
|
|
6
|
-
const rasterDemSources = context.model.getSourcesByType('RasterDemSource');
|
|
7
|
-
const [selectedSource, setSelectedSource] = useState(Object.keys(rasterDemSources)[0]);
|
|
8
|
-
const [exaggerationInput, setExaggerationInput] = useState(1);
|
|
9
|
-
const selectedSourceRef = useRef(selectedSource);
|
|
10
|
-
const exaggerationInputRef = useRef(exaggerationInput);
|
|
11
|
-
useEffect(() => {
|
|
12
|
-
selectedSourceRef.current = selectedSource;
|
|
13
|
-
exaggerationInputRef.current = exaggerationInput;
|
|
14
|
-
}, [selectedSource, exaggerationInput]);
|
|
15
|
-
// Handler for changing the selected option
|
|
16
|
-
const handleSourceChange = (event) => {
|
|
17
|
-
setSelectedSource(event.target.value);
|
|
18
|
-
};
|
|
19
|
-
// Handler for changing the number input
|
|
20
|
-
const handleExaggerationChange = (event) => {
|
|
21
|
-
setExaggerationInput(Number(event.target.value));
|
|
22
|
-
};
|
|
23
|
-
const handleOk = () => {
|
|
24
|
-
context.model.setTerrain({
|
|
25
|
-
source: selectedSourceRef.current,
|
|
26
|
-
exaggeration: exaggerationInputRef.current
|
|
27
|
-
});
|
|
28
|
-
cancel();
|
|
29
|
-
};
|
|
30
|
-
okSignalPromise.promise.then(okSignal => {
|
|
31
|
-
okSignal.connect(handleOk);
|
|
32
|
-
});
|
|
33
|
-
return (React.createElement("div", { className: "jp-gis-terrain-main" },
|
|
34
|
-
React.createElement("label", { className: "jp-gis-terrain-label", htmlFor: "source" }, "Source:"),
|
|
35
|
-
React.createElement("select", { id: "source", className: "jp-mod-styled", value: selectedSource, onChange: handleSourceChange, "aria-label": "Select source" }, Object.entries(rasterDemSources).map(([key, value]) => (React.createElement("option", { key: key, value: key }, value)))),
|
|
36
|
-
React.createElement("label", { className: "jp-gis-terrain-label", htmlFor: "exaggeration" }, "Exaggeration:"),
|
|
37
|
-
React.createElement("input", { id: "exaggeration", className: "jp-mod-styled", type: "number", min: 0, step: 0.1, value: exaggerationInput, onChange: handleExaggerationChange, placeholder: "Enter an exaggeration value", "aria-label": "Enter exaggeration value" })));
|
|
38
|
-
};
|
|
39
|
-
export class TerrainDialogWidget extends Dialog {
|
|
40
|
-
constructor(options) {
|
|
41
|
-
const cancelCallback = () => {
|
|
42
|
-
this.resolve(0);
|
|
43
|
-
};
|
|
44
|
-
const okSignalPromise = new PromiseDelegate();
|
|
45
|
-
const body = (React.createElement(TerrainDialog, { context: options.context, okSignalPromise: okSignalPromise, cancel: cancelCallback }));
|
|
46
|
-
super({ title: 'Add New Terrain', body });
|
|
47
|
-
this.id = 'jupytergis::terrain';
|
|
48
|
-
this.okSignal = new Signal(this);
|
|
49
|
-
okSignalPromise.resolve(this.okSignal);
|
|
50
|
-
}
|
|
51
|
-
resolve(index) {
|
|
52
|
-
if (index === 0) {
|
|
53
|
-
super.resolve(index);
|
|
54
|
-
}
|
|
55
|
-
if (index === 1) {
|
|
56
|
-
this.okSignal.emit(null);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
export default TerrainDialog;
|
package/style/terrainDialog.css
DELETED