@jupytergis/schema 0.1.1
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/_interface/forms.json +1987 -0
- package/lib/_interface/geoTiffSource.d.ts +31 -0
- package/lib/_interface/geojsonsource.d.ts +404 -0
- package/lib/_interface/hillshadeLayer.d.ts +20 -0
- package/lib/_interface/imageLayer.d.ts +20 -0
- package/lib/_interface/imageSource.d.ts +20 -0
- package/lib/_interface/jgis.d.ts +140 -0
- package/lib/_interface/rasterDemSource.d.ts +28 -0
- package/lib/_interface/rasterlayer.d.ts +20 -0
- package/lib/_interface/rastersource.d.ts +43 -0
- package/lib/_interface/shapefileSource.d.ts +34 -0
- package/lib/_interface/vectorTileLayer.d.ts +32 -0
- package/lib/_interface/vectorlayer.d.ts +32 -0
- package/lib/_interface/vectortilesource.d.ts +35 -0
- package/lib/_interface/videoSource.d.ts +20 -0
- package/lib/_interface/webGlLayer.d.ts +24 -0
- package/lib/doc.d.ts +59 -0
- package/lib/doc.js +251 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +4 -0
- package/lib/interfaces.d.ts +189 -0
- package/lib/interfaces.js +1 -0
- package/lib/model.d.ts +142 -0
- package/lib/model.js +554 -0
- package/lib/schema/geoTiffSource.json +37 -0
- package/lib/schema/geojsonsource.json +23 -0
- package/lib/schema/hillshadeLayer.json +18 -0
- package/lib/schema/imageLayer.json +21 -0
- package/lib/schema/imageSource.json +30 -0
- package/lib/schema/jgis.json +248 -0
- package/lib/schema/rasterDemSource.json +33 -0
- package/lib/schema/rasterlayer.json +21 -0
- package/lib/schema/rastersource.json +66 -0
- package/lib/schema/shapefileSource.json +37 -0
- package/lib/schema/vectorTileLayer.json +36 -0
- package/lib/schema/vectorlayer.json +36 -0
- package/lib/schema/vectortilesource.json +40 -0
- package/lib/schema/videoSource.json +33 -0
- package/lib/schema/webGlLayer.json +41 -0
- package/lib/token.d.ts +6 -0
- package/lib/token.js +5 -0
- package/lib/types.d.ts +19 -0
- package/lib/types.js +22 -0
- package/package.json +65 -0
package/lib/model.js
ADDED
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
import { PathExt } from '@jupyterlab/coreutils';
|
|
2
|
+
import { Signal } from '@lumino/signaling';
|
|
3
|
+
import Ajv from 'ajv';
|
|
4
|
+
import { JupyterGISDoc } from './doc';
|
|
5
|
+
import jgisSchema from './schema/jgis.json';
|
|
6
|
+
export class JupyterGISModel {
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this._onSharedModelChanged = (sender, changes) => {
|
|
9
|
+
var _a;
|
|
10
|
+
if (changes && ((_a = changes === null || changes === void 0 ? void 0 : changes.objectChange) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
11
|
+
this._contentChanged.emit(void 0);
|
|
12
|
+
this.dirty = true;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
this.collaborative = true;
|
|
16
|
+
this._onClientStateChanged = (changed) => {
|
|
17
|
+
const clients = this.sharedModel.awareness.getStates();
|
|
18
|
+
this._clientStateChanged.emit(clients);
|
|
19
|
+
this._sharedModel.awareness.on('change', (update) => {
|
|
20
|
+
if (update.added.length || update.removed.length) {
|
|
21
|
+
this._userChanged.emit(this.users);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
this.defaultKernelName = '';
|
|
26
|
+
this.defaultKernelLanguage = '';
|
|
27
|
+
this._dirty = false;
|
|
28
|
+
this._readOnly = false;
|
|
29
|
+
this._isDisposed = false;
|
|
30
|
+
this._userChanged = new Signal(this);
|
|
31
|
+
this._disposed = new Signal(this);
|
|
32
|
+
this._contentChanged = new Signal(this);
|
|
33
|
+
this._stateChanged = new Signal(this);
|
|
34
|
+
this._themeChanged = new Signal(this);
|
|
35
|
+
this._clientStateChanged = new Signal(this);
|
|
36
|
+
const { sharedModel } = options;
|
|
37
|
+
if (sharedModel) {
|
|
38
|
+
this._sharedModel = sharedModel;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
this._sharedModel = JupyterGISDoc.create();
|
|
42
|
+
this._sharedModel.changed.connect(this._onSharedModelChanged);
|
|
43
|
+
}
|
|
44
|
+
this.sharedModel.awareness.on('change', this._onClientStateChanged);
|
|
45
|
+
}
|
|
46
|
+
get sharedModel() {
|
|
47
|
+
return this._sharedModel;
|
|
48
|
+
}
|
|
49
|
+
get isDisposed() {
|
|
50
|
+
return this._isDisposed;
|
|
51
|
+
}
|
|
52
|
+
get contentChanged() {
|
|
53
|
+
return this._contentChanged;
|
|
54
|
+
}
|
|
55
|
+
get stateChanged() {
|
|
56
|
+
return this._stateChanged;
|
|
57
|
+
}
|
|
58
|
+
get themeChanged() {
|
|
59
|
+
return this._themeChanged;
|
|
60
|
+
}
|
|
61
|
+
get currentUserId() {
|
|
62
|
+
var _a;
|
|
63
|
+
return (_a = this.sharedModel) === null || _a === void 0 ? void 0 : _a.awareness.clientID;
|
|
64
|
+
}
|
|
65
|
+
get users() {
|
|
66
|
+
var _a;
|
|
67
|
+
this._usersMap = (_a = this._sharedModel) === null || _a === void 0 ? void 0 : _a.awareness.getStates();
|
|
68
|
+
const users = [];
|
|
69
|
+
if (this._usersMap) {
|
|
70
|
+
this._usersMap.forEach((val, key) => {
|
|
71
|
+
users.push({ userId: key, userData: val.user });
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return users;
|
|
75
|
+
}
|
|
76
|
+
get userChanged() {
|
|
77
|
+
return this._userChanged;
|
|
78
|
+
}
|
|
79
|
+
get dirty() {
|
|
80
|
+
return this._dirty;
|
|
81
|
+
}
|
|
82
|
+
set dirty(value) {
|
|
83
|
+
this._dirty = value;
|
|
84
|
+
}
|
|
85
|
+
get readOnly() {
|
|
86
|
+
return this._readOnly;
|
|
87
|
+
}
|
|
88
|
+
set readOnly(value) {
|
|
89
|
+
this._readOnly = value;
|
|
90
|
+
}
|
|
91
|
+
get localState() {
|
|
92
|
+
return this.sharedModel.awareness.getLocalState();
|
|
93
|
+
}
|
|
94
|
+
get clientStateChanged() {
|
|
95
|
+
return this._clientStateChanged;
|
|
96
|
+
}
|
|
97
|
+
get sharedOptionsChanged() {
|
|
98
|
+
return this.sharedModel.optionsChanged;
|
|
99
|
+
}
|
|
100
|
+
get sharedLayersChanged() {
|
|
101
|
+
return this.sharedModel.layersChanged;
|
|
102
|
+
}
|
|
103
|
+
get sharedLayerTreeChanged() {
|
|
104
|
+
return this.sharedModel.layerTreeChanged;
|
|
105
|
+
}
|
|
106
|
+
get sharedSourcesChanged() {
|
|
107
|
+
return this.sharedModel.sourcesChanged;
|
|
108
|
+
}
|
|
109
|
+
get terrainChanged() {
|
|
110
|
+
return this.sharedModel.terrainChanged;
|
|
111
|
+
}
|
|
112
|
+
get disposed() {
|
|
113
|
+
return this._disposed;
|
|
114
|
+
}
|
|
115
|
+
dispose() {
|
|
116
|
+
if (this._isDisposed) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
this._isDisposed = true;
|
|
120
|
+
this._sharedModel.dispose();
|
|
121
|
+
this._disposed.emit();
|
|
122
|
+
Signal.clearData(this);
|
|
123
|
+
}
|
|
124
|
+
toString() {
|
|
125
|
+
return JSON.stringify(this.getContent(), null, 2);
|
|
126
|
+
}
|
|
127
|
+
fromString(data) {
|
|
128
|
+
const jsonData = JSON.parse(data);
|
|
129
|
+
const ajv = new Ajv();
|
|
130
|
+
const validate = ajv.compile(jgisSchema);
|
|
131
|
+
const valid = validate(jsonData);
|
|
132
|
+
if (!valid) {
|
|
133
|
+
let errorMsg = 'File format errors:\n';
|
|
134
|
+
for (const error of validate.errors || []) {
|
|
135
|
+
errorMsg = `${errorMsg}- ${error.instancePath} ${error.message}\n`;
|
|
136
|
+
}
|
|
137
|
+
throw Error(errorMsg);
|
|
138
|
+
}
|
|
139
|
+
this.sharedModel.transact(() => {
|
|
140
|
+
var _a, _b, _c, _d, _e;
|
|
141
|
+
this.sharedModel.sources = (_a = jsonData.sources) !== null && _a !== void 0 ? _a : {};
|
|
142
|
+
this.sharedModel.layers = (_b = jsonData.layers) !== null && _b !== void 0 ? _b : {};
|
|
143
|
+
this.sharedModel.layerTree = (_c = jsonData.layerTree) !== null && _c !== void 0 ? _c : [];
|
|
144
|
+
this.sharedModel.terrain = (_d = jsonData.terrain) !== null && _d !== void 0 ? _d : {
|
|
145
|
+
source: '',
|
|
146
|
+
exaggeration: 0
|
|
147
|
+
};
|
|
148
|
+
this.sharedModel.options = (_e = jsonData.options) !== null && _e !== void 0 ? _e : {
|
|
149
|
+
latitude: 0,
|
|
150
|
+
longitude: 0,
|
|
151
|
+
zoom: 0,
|
|
152
|
+
bearing: 0,
|
|
153
|
+
pitch: 0,
|
|
154
|
+
projection: 'EPSG:3857'
|
|
155
|
+
};
|
|
156
|
+
});
|
|
157
|
+
this.dirty = true;
|
|
158
|
+
}
|
|
159
|
+
toJSON() {
|
|
160
|
+
return JSON.parse(this.toString());
|
|
161
|
+
}
|
|
162
|
+
fromJSON(data) {
|
|
163
|
+
// nothing to do
|
|
164
|
+
}
|
|
165
|
+
initialize() {
|
|
166
|
+
//
|
|
167
|
+
}
|
|
168
|
+
getWorker() {
|
|
169
|
+
return JupyterGISModel.worker;
|
|
170
|
+
}
|
|
171
|
+
getContent() {
|
|
172
|
+
return {
|
|
173
|
+
sources: this.sharedModel.sources,
|
|
174
|
+
layers: this.sharedModel.layers,
|
|
175
|
+
layerTree: this.sharedModel.layerTree,
|
|
176
|
+
options: this.sharedModel.options,
|
|
177
|
+
terrain: this.sharedModel.terrain
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
setDrive(value, filePath) {
|
|
181
|
+
this._drive = value;
|
|
182
|
+
this._filePath = filePath;
|
|
183
|
+
}
|
|
184
|
+
getLayers() {
|
|
185
|
+
return this.sharedModel.layers;
|
|
186
|
+
}
|
|
187
|
+
getSources() {
|
|
188
|
+
return this.sharedModel.sources;
|
|
189
|
+
}
|
|
190
|
+
getLayerTree() {
|
|
191
|
+
return this.sharedModel.layerTree;
|
|
192
|
+
}
|
|
193
|
+
getLayer(id) {
|
|
194
|
+
return this.sharedModel.getLayer(id);
|
|
195
|
+
}
|
|
196
|
+
getSource(id) {
|
|
197
|
+
return this.sharedModel.getSource(id);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Get a {[key: id]: name} dictionary of sources for a given source type
|
|
201
|
+
* @param type The required source type
|
|
202
|
+
*/
|
|
203
|
+
getSourcesByType(type) {
|
|
204
|
+
const sources = {};
|
|
205
|
+
for (const sourceId of Object.keys(this.getSources() || {})) {
|
|
206
|
+
const source = this.getSource(sourceId);
|
|
207
|
+
if ((source === null || source === void 0 ? void 0 : source.type) === type) {
|
|
208
|
+
sources[sourceId] = source.name;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return sources;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Get the list of layers using a source.
|
|
215
|
+
*
|
|
216
|
+
* @param id - the source id.
|
|
217
|
+
* @returns a list of layer ids that use the source.
|
|
218
|
+
*/
|
|
219
|
+
getLayersBySource(id) {
|
|
220
|
+
const usingLayers = [];
|
|
221
|
+
Object.entries(this.getLayers() || {}).forEach(([layerId, layer]) => {
|
|
222
|
+
var _a;
|
|
223
|
+
if (((_a = layer.parameters) === null || _a === void 0 ? void 0 : _a.source) === id) {
|
|
224
|
+
usingLayers.push(layerId);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
return usingLayers;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Read a GeoJSON file.
|
|
231
|
+
*
|
|
232
|
+
* @param filepath - the path of the GeoJSON file.
|
|
233
|
+
* @returns a promise to the GeoJSON data.
|
|
234
|
+
*/
|
|
235
|
+
async readGeoJSON(filepath) {
|
|
236
|
+
if (!this._drive) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
let dir = PathExt.dirname(this._filePath);
|
|
240
|
+
if (dir.includes(':')) {
|
|
241
|
+
dir = dir.split(':')[1];
|
|
242
|
+
}
|
|
243
|
+
const absolutePath = PathExt.join(dir, filepath);
|
|
244
|
+
return this._drive
|
|
245
|
+
.get(absolutePath)
|
|
246
|
+
.then(contentModel => {
|
|
247
|
+
return JSON.parse(contentModel.content);
|
|
248
|
+
})
|
|
249
|
+
.catch(e => {
|
|
250
|
+
throw e;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Add a layer group in the layer tree.
|
|
255
|
+
*
|
|
256
|
+
* @param name - the name of the group.
|
|
257
|
+
* @param groupName - (optional) the name of the parent group in which to include the
|
|
258
|
+
* new group.
|
|
259
|
+
* @param position - (optional) the index of the new group in its parent group or
|
|
260
|
+
* from root of layer tree.
|
|
261
|
+
*/
|
|
262
|
+
addGroup(name, groupName, position) {
|
|
263
|
+
const indexesPath = Private.findItemPath(this.getLayerTree(), name);
|
|
264
|
+
if (indexesPath.length) {
|
|
265
|
+
console.warn(`The group "${groupName}" already exist in the layer tree`);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const item = {
|
|
269
|
+
name,
|
|
270
|
+
layers: []
|
|
271
|
+
};
|
|
272
|
+
this._addLayerTreeItem(item, groupName, position);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Add a layer in the layer tree and the layers list.
|
|
276
|
+
*
|
|
277
|
+
* @param id - the ID of the layer.
|
|
278
|
+
* @param layer - the layer object.
|
|
279
|
+
* @param groupName - (optional) the name of the group in which to include the new
|
|
280
|
+
* layer.
|
|
281
|
+
* @param position - (optional) the index of the new layer in its parent group or
|
|
282
|
+
* from root of layer tree.
|
|
283
|
+
*/
|
|
284
|
+
addLayer(id, layer, groupName, position) {
|
|
285
|
+
if (!this.getLayer(id)) {
|
|
286
|
+
this.sharedModel.addLayer(id, layer);
|
|
287
|
+
}
|
|
288
|
+
this._addLayerTreeItem(id, groupName, position);
|
|
289
|
+
}
|
|
290
|
+
removeLayer(layer_id) {
|
|
291
|
+
this._removeLayerTreeLayer(this.getLayerTree(), layer_id);
|
|
292
|
+
this.sharedModel.removeLayer(layer_id);
|
|
293
|
+
}
|
|
294
|
+
setTerrain(terrain) {
|
|
295
|
+
this._sharedModel.terrain = terrain;
|
|
296
|
+
}
|
|
297
|
+
setOptions(value) {
|
|
298
|
+
this._sharedModel.options = value;
|
|
299
|
+
}
|
|
300
|
+
getOptions() {
|
|
301
|
+
return this._sharedModel.options;
|
|
302
|
+
}
|
|
303
|
+
syncSelected(value, emitter) {
|
|
304
|
+
this.sharedModel.awareness.setLocalStateField('selected', {
|
|
305
|
+
value,
|
|
306
|
+
emitter: emitter
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
setUserToFollow(userId) {
|
|
310
|
+
if (this._sharedModel) {
|
|
311
|
+
this._sharedModel.awareness.setLocalStateField('remoteUser', userId);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
getClientId() {
|
|
315
|
+
return this.sharedModel.awareness.clientID;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Add an item in the layer tree.
|
|
319
|
+
*
|
|
320
|
+
* @param item - the item to add.
|
|
321
|
+
* @param groupName - (optional) the name of the parent group in which to include the
|
|
322
|
+
* new item.
|
|
323
|
+
* @param index - (optional) the index of the new item in its parent group or
|
|
324
|
+
* from root of layer tree.
|
|
325
|
+
*/
|
|
326
|
+
_addLayerTreeItem(item, groupName, index) {
|
|
327
|
+
if (groupName) {
|
|
328
|
+
const layerTreeInfo = this._getLayerTreeInfo(groupName);
|
|
329
|
+
if (layerTreeInfo) {
|
|
330
|
+
layerTreeInfo.workingGroup.layers.splice(index !== null && index !== void 0 ? index : layerTreeInfo.workingGroup.layers.length, 0, item);
|
|
331
|
+
this._sharedModel.updateLayerTreeItem(layerTreeInfo.mainGroupIndex, layerTreeInfo.mainGroup);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
this.sharedModel.addLayerTreeItem(index !== null && index !== void 0 ? index : this.getLayerTree().length, item);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
moveItemsToGroup(items, groupName, index) {
|
|
339
|
+
const layerTree = this.getLayerTree();
|
|
340
|
+
for (const item of items) {
|
|
341
|
+
if (this.getLayer(item)) {
|
|
342
|
+
// the item is a layer, remove and add it at the correct position.
|
|
343
|
+
this._removeLayerTreeLayer(layerTree, item);
|
|
344
|
+
this._addLayerTreeItem(item, groupName, index);
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
// the item is a group, let's copy it before removing it.
|
|
348
|
+
const treeInfo = this._getLayerTreeInfo(item);
|
|
349
|
+
if (treeInfo === undefined) {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
const group = Object.assign({}, treeInfo.workingGroup);
|
|
353
|
+
this._removeLayerTreeGroup(layerTree, item);
|
|
354
|
+
this._addLayerTreeItem(group, groupName, index);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
moveItemRelatedTo(item, relativeItem, after) {
|
|
359
|
+
var _a;
|
|
360
|
+
const layerTree = this.getLayerTree();
|
|
361
|
+
let insertedItem;
|
|
362
|
+
if (this.getLayer(item)) {
|
|
363
|
+
this._removeLayerTreeLayer(layerTree, item);
|
|
364
|
+
insertedItem = item;
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
const treeInfo = this._getLayerTreeInfo(item);
|
|
368
|
+
if (treeInfo === undefined) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
insertedItem = Object.assign({}, treeInfo.workingGroup);
|
|
372
|
+
this._removeLayerTreeGroup(layerTree, item);
|
|
373
|
+
}
|
|
374
|
+
const indexesPath = Private.findItemPath(layerTree, relativeItem);
|
|
375
|
+
const insertedIndex = ((_a = indexesPath.pop()) !== null && _a !== void 0 ? _a : 0) + (after ? 1 : 0);
|
|
376
|
+
let parentGroupName = '';
|
|
377
|
+
let workingGroupId = indexesPath.shift();
|
|
378
|
+
if (workingGroupId !== undefined) {
|
|
379
|
+
let workingGroup = layerTree[workingGroupId];
|
|
380
|
+
while (indexesPath.length) {
|
|
381
|
+
workingGroupId = indexesPath.shift();
|
|
382
|
+
if (workingGroupId === undefined) {
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
workingGroup = workingGroup.layers[workingGroupId];
|
|
386
|
+
}
|
|
387
|
+
parentGroupName = workingGroup.name;
|
|
388
|
+
}
|
|
389
|
+
this._addLayerTreeItem(insertedItem, parentGroupName, insertedIndex);
|
|
390
|
+
}
|
|
391
|
+
addNewLayerGroup(selected, group) {
|
|
392
|
+
const layerTree = this.getLayerTree();
|
|
393
|
+
for (const item in selected) {
|
|
394
|
+
this._removeLayerTreeLayer(layerTree, item);
|
|
395
|
+
}
|
|
396
|
+
this._addLayerTreeItem(group);
|
|
397
|
+
}
|
|
398
|
+
_removeLayerTreeLayer(layerTree, layerIdToRemove) {
|
|
399
|
+
// Iterate over each item in the layerTree
|
|
400
|
+
for (let i = 0; i < layerTree.length; i++) {
|
|
401
|
+
const currentItem = layerTree[i];
|
|
402
|
+
// Check if the current item is a string and matches the target
|
|
403
|
+
if (typeof currentItem === 'string' && currentItem === layerIdToRemove) {
|
|
404
|
+
// Remove the item from the array
|
|
405
|
+
layerTree.splice(i, 1);
|
|
406
|
+
// Decrement i to ensure the next iteration processes the remaining items correctly
|
|
407
|
+
i--;
|
|
408
|
+
}
|
|
409
|
+
else if (typeof currentItem !== 'string' && 'layers' in currentItem) {
|
|
410
|
+
// If the current item is a group, recursively call the function on its layers
|
|
411
|
+
this._removeLayerTreeLayer(currentItem.layers, layerIdToRemove);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
this.sharedModel.layerTree = layerTree;
|
|
415
|
+
}
|
|
416
|
+
_removeLayerTreeGroup(layerTree, groupName) {
|
|
417
|
+
// Iterate over each item in the layerTree
|
|
418
|
+
for (let i = 0; i < layerTree.length; i++) {
|
|
419
|
+
const currentItem = layerTree[i];
|
|
420
|
+
// Check if the current item is a string and matches the target
|
|
421
|
+
if (typeof currentItem !== 'string' && currentItem.name === groupName) {
|
|
422
|
+
// Remove the item from the array
|
|
423
|
+
layerTree.splice(i, 1);
|
|
424
|
+
// Decrement i to ensure the next iteration processes the remaining items correctly
|
|
425
|
+
i--;
|
|
426
|
+
}
|
|
427
|
+
else if (typeof currentItem !== 'string' && 'layers' in currentItem) {
|
|
428
|
+
// If the current item is a group, recursively call the function on its layers
|
|
429
|
+
this._removeLayerTreeGroup(currentItem.layers, groupName);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
this.sharedModel.layerTree = layerTree;
|
|
433
|
+
}
|
|
434
|
+
renameLayerGroup(groupName, newName) {
|
|
435
|
+
const layerTreeInfo = this._getLayerTreeInfo(groupName);
|
|
436
|
+
if (layerTreeInfo) {
|
|
437
|
+
layerTreeInfo.workingGroup.name = newName;
|
|
438
|
+
this._sharedModel.updateLayerTreeItem(layerTreeInfo.mainGroupIndex, layerTreeInfo.mainGroup);
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
console.log('Something went wrong when renaming layer');
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
removeLayerGroup(groupName) {
|
|
445
|
+
const layerTree = this.getLayerTree();
|
|
446
|
+
const layerTreeInfo = this._getLayerTreeInfo(groupName);
|
|
447
|
+
const updatedLayerTree = removeLayerGroupEntry(layerTree, groupName);
|
|
448
|
+
function removeLayerGroupEntry(layerTree, groupName) {
|
|
449
|
+
const result = [];
|
|
450
|
+
for (const item of layerTree) {
|
|
451
|
+
if (typeof item === 'string') {
|
|
452
|
+
result.push(item); // Push layer IDs directly
|
|
453
|
+
}
|
|
454
|
+
else if (item.name !== groupName) {
|
|
455
|
+
const filteredLayers = removeLayerGroupEntry(item.layers, groupName);
|
|
456
|
+
result.push(Object.assign(Object.assign({}, item), { layers: filteredLayers })); // Update layers with filtered list
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return result;
|
|
460
|
+
}
|
|
461
|
+
if (layerTreeInfo) {
|
|
462
|
+
this._sharedModel.updateLayerTreeItem(layerTreeInfo.mainGroupIndex, updatedLayerTree[layerTreeInfo.mainGroupIndex]);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
_getLayerTreeInfo(groupName) {
|
|
466
|
+
const layerTree = this.getLayerTree();
|
|
467
|
+
const indexesPath = Private.findItemPath(layerTree, groupName);
|
|
468
|
+
if (!indexesPath.length) {
|
|
469
|
+
console.warn(`The group "${groupName}" does not exist in the layer tree`);
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
const mainGroupIndex = indexesPath.shift();
|
|
473
|
+
if (mainGroupIndex === undefined) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const mainGroup = layerTree[mainGroupIndex];
|
|
477
|
+
let workingGroup = mainGroup;
|
|
478
|
+
while (indexesPath.length) {
|
|
479
|
+
const groupIndex = indexesPath.shift();
|
|
480
|
+
if (groupIndex === undefined) {
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
workingGroup = workingGroup.layers[groupIndex];
|
|
484
|
+
}
|
|
485
|
+
return {
|
|
486
|
+
mainGroup,
|
|
487
|
+
workingGroup,
|
|
488
|
+
mainGroupIndex
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
(function (JupyterGISModel) {
|
|
493
|
+
/**
|
|
494
|
+
* Function to get the ordered list of layers according to the tree.
|
|
495
|
+
*/
|
|
496
|
+
function getOrderedLayerIds(model) {
|
|
497
|
+
return Private.layerTreeRecursion(model.sharedModel.layerTree);
|
|
498
|
+
}
|
|
499
|
+
JupyterGISModel.getOrderedLayerIds = getOrderedLayerIds;
|
|
500
|
+
})(JupyterGISModel || (JupyterGISModel = {}));
|
|
501
|
+
var Private;
|
|
502
|
+
(function (Private) {
|
|
503
|
+
/**
|
|
504
|
+
* Recursive function through the layer tree to retrieve the flattened layers order.
|
|
505
|
+
*
|
|
506
|
+
* @param items - the items list being scanned.
|
|
507
|
+
* @param current - the current flattened layers.
|
|
508
|
+
*/
|
|
509
|
+
function layerTreeRecursion(items, current = []) {
|
|
510
|
+
for (const layer of items) {
|
|
511
|
+
if (typeof layer === 'string') {
|
|
512
|
+
current.push(layer);
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
current.push(...layerTreeRecursion(layer.layers));
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return current;
|
|
519
|
+
}
|
|
520
|
+
Private.layerTreeRecursion = layerTreeRecursion;
|
|
521
|
+
/**
|
|
522
|
+
* Recursive function through the layer tree to retrieve the indexes path to a group
|
|
523
|
+
* or a layer.
|
|
524
|
+
*
|
|
525
|
+
* @param items - the items list being scanned.
|
|
526
|
+
* @param itemId - the target group name or layer ID.
|
|
527
|
+
* @param indexes - the current indexes path to the group
|
|
528
|
+
*/
|
|
529
|
+
function findItemPath(items, itemId, indexes = []) {
|
|
530
|
+
for (let index = 0; index < items.length; index++) {
|
|
531
|
+
const item = items[index];
|
|
532
|
+
if (typeof item === 'string') {
|
|
533
|
+
if (item === itemId) {
|
|
534
|
+
const workingIndexes = [...indexes];
|
|
535
|
+
workingIndexes.push(index);
|
|
536
|
+
return workingIndexes;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
const workingIndexes = [...indexes];
|
|
541
|
+
workingIndexes.push(index);
|
|
542
|
+
if (item.name === itemId) {
|
|
543
|
+
return workingIndexes;
|
|
544
|
+
}
|
|
545
|
+
const foundIndexes = findItemPath(item.layers, itemId, workingIndexes);
|
|
546
|
+
if (foundIndexes.length > workingIndexes.length) {
|
|
547
|
+
return foundIndexes;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return indexes;
|
|
552
|
+
}
|
|
553
|
+
Private.findItemPath = findItemPath;
|
|
554
|
+
})(Private || (Private = {}));
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "object",
|
|
3
|
+
"description": "GeoTiffSource",
|
|
4
|
+
"title": "IGeoTiffSource",
|
|
5
|
+
"required": ["urls"],
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"properties": {
|
|
8
|
+
"urls": {
|
|
9
|
+
"type": "array",
|
|
10
|
+
"items": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"properties": {
|
|
13
|
+
"url": {
|
|
14
|
+
"type": "string"
|
|
15
|
+
},
|
|
16
|
+
"min": {
|
|
17
|
+
"type": "number"
|
|
18
|
+
},
|
|
19
|
+
"max": {
|
|
20
|
+
"type": "number"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"minItems": 1,
|
|
25
|
+
"default": [],
|
|
26
|
+
"description": "URLs"
|
|
27
|
+
},
|
|
28
|
+
"normalize": {
|
|
29
|
+
"type": "boolean",
|
|
30
|
+
"default": true
|
|
31
|
+
},
|
|
32
|
+
"wrapX": {
|
|
33
|
+
"type": "boolean",
|
|
34
|
+
"default": false
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "object",
|
|
3
|
+
"description": "GeoJSONSource",
|
|
4
|
+
"title": "IGeoJSONSource",
|
|
5
|
+
"required": [],
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"properties": {
|
|
8
|
+
"path": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "The local path to a GeoJSON file"
|
|
11
|
+
},
|
|
12
|
+
"data": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"description": "The GeoJSON data",
|
|
15
|
+
"$ref": "https://geojson.org/schema/GeoJSON.json"
|
|
16
|
+
},
|
|
17
|
+
"valid": {
|
|
18
|
+
"type": "boolean",
|
|
19
|
+
"description": "Whether the data are valid or not",
|
|
20
|
+
"readOnly": true
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "object",
|
|
3
|
+
"description": "HillshadeLayer",
|
|
4
|
+
"title": "IHillshadeLayer",
|
|
5
|
+
"required": ["source"],
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"properties": {
|
|
8
|
+
"source": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "The id of the source"
|
|
11
|
+
},
|
|
12
|
+
"shadowColor": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "The color of the the shadows",
|
|
15
|
+
"default": "#473B24"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "object",
|
|
3
|
+
"description": "ImageLayer",
|
|
4
|
+
"title": "IImageLayer",
|
|
5
|
+
"required": ["source"],
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"properties": {
|
|
8
|
+
"source": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "The id of the source"
|
|
11
|
+
},
|
|
12
|
+
"opacity": {
|
|
13
|
+
"type": "number",
|
|
14
|
+
"description": "The opacity of the source",
|
|
15
|
+
"default": 1,
|
|
16
|
+
"multipleOf": 0.1,
|
|
17
|
+
"minimum": 0,
|
|
18
|
+
"maximum": 1
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "object",
|
|
3
|
+
"description": "ImageSource",
|
|
4
|
+
"title": "IImageSource",
|
|
5
|
+
"required": ["url", "coordinates"],
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"properties": {
|
|
8
|
+
"url": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"readOnly": true,
|
|
11
|
+
"description": "URL that points to an image"
|
|
12
|
+
},
|
|
13
|
+
"coordinates": {
|
|
14
|
+
"type": "array",
|
|
15
|
+
"readOnly": true,
|
|
16
|
+
"items": {
|
|
17
|
+
"type": "array",
|
|
18
|
+
"items": {
|
|
19
|
+
"type": "number"
|
|
20
|
+
},
|
|
21
|
+
"minItems": 2,
|
|
22
|
+
"maxItems": 2
|
|
23
|
+
},
|
|
24
|
+
"minItems": 4,
|
|
25
|
+
"maxItems": 4,
|
|
26
|
+
"default": [],
|
|
27
|
+
"description": "Corners of image specified in longitude, latitude pairs"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|