@jupytergis/schema 0.10.1 → 0.12.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/_interface/forms.json +64 -0
- package/lib/_interface/project/jgis.d.ts +36 -1
- package/lib/_interface/project/layers/storySegmentLayer.d.ts +39 -0
- package/lib/doc.d.ts +12 -2
- package/lib/doc.js +79 -5
- package/lib/interfaces.d.ts +28 -1
- package/lib/model.d.ts +26 -2
- package/lib/model.js +94 -5
- package/lib/schema/project/jgis.json +46 -1
- package/lib/schema/project/layers/storySegmentLayer.json +55 -0
- package/lib/types.d.ts +1 -0
- package/lib/types.js +1 -0
- package/package.json +1 -1
|
@@ -302,6 +302,70 @@
|
|
|
302
302
|
}
|
|
303
303
|
}
|
|
304
304
|
},
|
|
305
|
+
"StorySegmentLayer": {
|
|
306
|
+
"type": "object",
|
|
307
|
+
"additionalProperties": false,
|
|
308
|
+
"required": [
|
|
309
|
+
"zoom",
|
|
310
|
+
"extent",
|
|
311
|
+
"transition"
|
|
312
|
+
],
|
|
313
|
+
"properties": {
|
|
314
|
+
"zoom": {
|
|
315
|
+
"type": "number",
|
|
316
|
+
"default": 0
|
|
317
|
+
},
|
|
318
|
+
"extent": {
|
|
319
|
+
"type": "array",
|
|
320
|
+
"default": null,
|
|
321
|
+
"items": {
|
|
322
|
+
"type": "number"
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
"content": {
|
|
326
|
+
"type": "object",
|
|
327
|
+
"additionalProperties": false,
|
|
328
|
+
"properties": {
|
|
329
|
+
"title": {
|
|
330
|
+
"type": "string"
|
|
331
|
+
},
|
|
332
|
+
"image": {
|
|
333
|
+
"type": "string",
|
|
334
|
+
"description": "Link to image for the story"
|
|
335
|
+
},
|
|
336
|
+
"markdown": {
|
|
337
|
+
"type": "string",
|
|
338
|
+
"description": "Markdown string representing the content of the story stop"
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
"transition": {
|
|
343
|
+
"type": "object",
|
|
344
|
+
"description": "Transition configuration between to this story segment",
|
|
345
|
+
"properties": {
|
|
346
|
+
"type": {
|
|
347
|
+
"type": "string",
|
|
348
|
+
"enum": [
|
|
349
|
+
"linear",
|
|
350
|
+
"immediate",
|
|
351
|
+
"smooth"
|
|
352
|
+
],
|
|
353
|
+
"description": "Transition animation style",
|
|
354
|
+
"default": "linear"
|
|
355
|
+
},
|
|
356
|
+
"time": {
|
|
357
|
+
"type": "number",
|
|
358
|
+
"description": "The time in seconds for the transition",
|
|
359
|
+
"default": 1
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
"required": [
|
|
363
|
+
"type",
|
|
364
|
+
"time"
|
|
365
|
+
]
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
},
|
|
305
369
|
"VectorLayer": {
|
|
306
370
|
"type": "object",
|
|
307
371
|
"required": [
|
|
@@ -17,7 +17,8 @@ export type LayerType =
|
|
|
17
17
|
| "WebGlLayer"
|
|
18
18
|
| "ImageLayer"
|
|
19
19
|
| "HeatmapLayer"
|
|
20
|
-
| "StacLayer"
|
|
20
|
+
| "StacLayer"
|
|
21
|
+
| "StorySegmentLayer";
|
|
21
22
|
/**
|
|
22
23
|
* This interface was referenced by `IJGISContent`'s JSON-Schema
|
|
23
24
|
* via the `definition` "sourceType".
|
|
@@ -43,6 +44,23 @@ export type IJGISLayerItem = string | IJGISLayerGroup;
|
|
|
43
44
|
* via the `definition` "jGISLayerTree".
|
|
44
45
|
*/
|
|
45
46
|
export type IJGISLayerTree = IJGISLayerItem[];
|
|
47
|
+
export type WhetherPresentationModeIsActive = boolean;
|
|
48
|
+
/**
|
|
49
|
+
* The title of the story map
|
|
50
|
+
*/
|
|
51
|
+
export type Title = string;
|
|
52
|
+
/**
|
|
53
|
+
* The type of story map
|
|
54
|
+
*/
|
|
55
|
+
export type StoryType = "guided" | "unguided";
|
|
56
|
+
/**
|
|
57
|
+
* The background color of the story map
|
|
58
|
+
*/
|
|
59
|
+
export type PresentationBackgroundColor = string;
|
|
60
|
+
/**
|
|
61
|
+
* The text color of the story map
|
|
62
|
+
*/
|
|
63
|
+
export type PresentationTextColor = string;
|
|
46
64
|
|
|
47
65
|
export interface IJGISContent {
|
|
48
66
|
schemaVersion?: string;
|
|
@@ -50,6 +68,7 @@ export interface IJGISContent {
|
|
|
50
68
|
sources: IJGISSources;
|
|
51
69
|
layerTree?: IJGISLayerTree;
|
|
52
70
|
options?: IJGISOptions;
|
|
71
|
+
stories?: IJGISStoryMap;
|
|
53
72
|
metadata?: {
|
|
54
73
|
/**
|
|
55
74
|
* This interface was referenced by `undefined`'s JSON-Schema definition
|
|
@@ -140,4 +159,20 @@ export interface IJGISOptions {
|
|
|
140
159
|
extent?: number[];
|
|
141
160
|
projection?: string;
|
|
142
161
|
useExtent?: boolean;
|
|
162
|
+
storyMapPresentationMode?: WhetherPresentationModeIsActive;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* This interface was referenced by `IJGISContent`'s JSON-Schema
|
|
166
|
+
* via the `definition` "jGISStoryMap".
|
|
167
|
+
*/
|
|
168
|
+
export interface IJGISStoryMap {
|
|
169
|
+
title?: Title;
|
|
170
|
+
storyType?: StoryType;
|
|
171
|
+
/**
|
|
172
|
+
* Array of story segments for the story map
|
|
173
|
+
*/
|
|
174
|
+
storySegments?: string[];
|
|
175
|
+
presentaionBgColor?: PresentationBackgroundColor;
|
|
176
|
+
presentaionTextColor?: PresentationTextColor;
|
|
177
|
+
[k: string]: any;
|
|
143
178
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/**
|
|
3
|
+
* This file was automatically generated by json-schema-to-typescript.
|
|
4
|
+
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
|
|
5
|
+
* and run json-schema-to-typescript to regenerate this file.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* StorySegmentLayer
|
|
10
|
+
*/
|
|
11
|
+
export interface IStorySegmentLayer {
|
|
12
|
+
zoom: number;
|
|
13
|
+
extent: number[];
|
|
14
|
+
content?: {
|
|
15
|
+
title?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Link to image for the story
|
|
18
|
+
*/
|
|
19
|
+
image?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Markdown string representing the content of the story stop
|
|
22
|
+
*/
|
|
23
|
+
markdown?: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Transition configuration between to this story segment
|
|
27
|
+
*/
|
|
28
|
+
transition: {
|
|
29
|
+
/**
|
|
30
|
+
* Transition animation style
|
|
31
|
+
*/
|
|
32
|
+
type: "linear" | "immediate" | "smooth";
|
|
33
|
+
/**
|
|
34
|
+
* The time in seconds for the transition
|
|
35
|
+
*/
|
|
36
|
+
time: number;
|
|
37
|
+
[k: string]: any;
|
|
38
|
+
};
|
|
39
|
+
}
|
package/lib/doc.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { MapChange, YDocument } from '@jupyter/ydoc';
|
|
2
2
|
import { JSONObject } from '@lumino/coreutils';
|
|
3
3
|
import { ISignal } from '@lumino/signaling';
|
|
4
|
-
import { IJGISLayer, IJGISLayerItem, IJGISLayerTree, IJGISLayers, IJGISOptions, IJGISSource, IJGISSources } from './_interface/project/jgis';
|
|
5
|
-
import { IDict, IJGISLayerDocChange, IJGISLayerTreeDocChange, IJGISSourceDocChange, IJupyterGISDoc, IJupyterGISDocChange } from './interfaces';
|
|
4
|
+
import { IJGISLayer, IJGISLayerItem, IJGISLayerTree, IJGISLayers, IJGISOptions, IJGISSource, IJGISSources, IJGISStoryMap } from './_interface/project/jgis';
|
|
5
|
+
import { IDict, IJGISLayerDocChange, IJGISLayerTreeDocChange, IJGISSourceDocChange, IJGISStoryMapDocChange, IJGISStoryMaps, IJupyterGISDoc, IJupyterGISDocChange } from './interfaces';
|
|
6
6
|
export declare class JupyterGISDoc extends YDocument<IJupyterGISDocChange> implements IJupyterGISDoc {
|
|
7
7
|
constructor();
|
|
8
8
|
getSource(): JSONObject;
|
|
@@ -13,6 +13,8 @@ export declare class JupyterGISDoc extends YDocument<IJupyterGISDocChange> imple
|
|
|
13
13
|
set layers(layers: IJGISLayers);
|
|
14
14
|
set sources(sources: IJGISSources);
|
|
15
15
|
get sources(): IJGISSources;
|
|
16
|
+
set stories(stories: IJGISStoryMaps);
|
|
17
|
+
get stories(): IJGISStoryMaps;
|
|
16
18
|
get layerTree(): IJGISLayerTree;
|
|
17
19
|
set layerTree(layerTree: IJGISLayerTree);
|
|
18
20
|
getLayer(id: string): IJGISLayer | undefined;
|
|
@@ -22,6 +24,7 @@ export declare class JupyterGISDoc extends YDocument<IJupyterGISDocChange> imple
|
|
|
22
24
|
get layersChanged(): ISignal<IJupyterGISDoc, IJGISLayerDocChange>;
|
|
23
25
|
get layerTreeChanged(): ISignal<IJupyterGISDoc, IJGISLayerTreeDocChange>;
|
|
24
26
|
get sourcesChanged(): ISignal<IJupyterGISDoc, IJGISSourceDocChange>;
|
|
27
|
+
get storyMapsChanged(): ISignal<IJupyterGISDoc, IJGISStoryMapDocChange>;
|
|
25
28
|
get optionsChanged(): ISignal<IJupyterGISDoc, MapChange>;
|
|
26
29
|
layerExists(id: string): boolean;
|
|
27
30
|
removeLayer(id: string): void;
|
|
@@ -35,6 +38,10 @@ export declare class JupyterGISDoc extends YDocument<IJupyterGISDocChange> imple
|
|
|
35
38
|
removeSource(id: string): void;
|
|
36
39
|
addSource(id: string, value: IJGISSource): void;
|
|
37
40
|
updateSource(id: string, value: any): void;
|
|
41
|
+
removeStoryMap(id: string): void;
|
|
42
|
+
addStoryMap(id: string, value: IJGISStoryMap): void;
|
|
43
|
+
updateStoryMap(id: string, value: any): void;
|
|
44
|
+
getStoryMap(id: string): IJGISStoryMap | undefined;
|
|
38
45
|
getOption(key: keyof IJGISOptions): IDict | undefined;
|
|
39
46
|
setOption(key: keyof IJGISOptions, value: IDict): void;
|
|
40
47
|
getMetadata(key: string): string | undefined;
|
|
@@ -52,16 +59,19 @@ export declare class JupyterGISDoc extends YDocument<IJupyterGISDocChange> imple
|
|
|
52
59
|
private _layersObserver;
|
|
53
60
|
private _layerTreeObserver;
|
|
54
61
|
private _sourcesObserver;
|
|
62
|
+
private _storyMapsObserver;
|
|
55
63
|
private _optionsObserver;
|
|
56
64
|
private _metaObserver;
|
|
57
65
|
private _layers;
|
|
58
66
|
private _layerTree;
|
|
59
67
|
private _sources;
|
|
68
|
+
private _stories;
|
|
60
69
|
private _options;
|
|
61
70
|
private _metadata;
|
|
62
71
|
private _optionsChanged;
|
|
63
72
|
private _layersChanged;
|
|
64
73
|
private _layerTreeChanged;
|
|
65
74
|
private _sourcesChanged;
|
|
75
|
+
private _storyMapsChanged;
|
|
66
76
|
private _metadataChanged;
|
|
67
77
|
}
|
package/lib/doc.js
CHANGED
|
@@ -7,27 +7,47 @@ export class JupyterGISDoc extends YDocument {
|
|
|
7
7
|
super();
|
|
8
8
|
this.editable = true;
|
|
9
9
|
this._optionsObserver = (event) => {
|
|
10
|
-
|
|
10
|
+
const changes = new Map();
|
|
11
|
+
event.changes.keys.forEach((event, key) => {
|
|
12
|
+
changes.set(key, {
|
|
13
|
+
action: event.action,
|
|
14
|
+
oldValue: event.oldValue,
|
|
15
|
+
newValue: this._options.get(key),
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
this._optionsChanged.emit(changes);
|
|
11
19
|
};
|
|
12
20
|
this._metaObserver = (event) => {
|
|
13
|
-
|
|
21
|
+
const changes = new Map();
|
|
22
|
+
event.changes.keys.forEach((event, key) => {
|
|
23
|
+
changes.set(key, {
|
|
24
|
+
action: event.action,
|
|
25
|
+
oldValue: event.oldValue,
|
|
26
|
+
newValue: this._metadata.get(key),
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
this._metadataChanged.emit(changes);
|
|
14
30
|
};
|
|
15
31
|
this._optionsChanged = new Signal(this);
|
|
16
32
|
this._layersChanged = new Signal(this);
|
|
17
33
|
this._layerTreeChanged = new Signal(this);
|
|
18
34
|
this._sourcesChanged = new Signal(this);
|
|
35
|
+
this._storyMapsChanged = new Signal(this);
|
|
19
36
|
this._metadataChanged = new Signal(this);
|
|
20
37
|
this._options = this.ydoc.getMap('options');
|
|
21
38
|
this._layers = this.ydoc.getMap('layers');
|
|
22
39
|
this._layerTree = this.ydoc.getArray('layerTree');
|
|
23
40
|
this._sources = this.ydoc.getMap('sources');
|
|
41
|
+
this._stories = this.ydoc.getMap('stories');
|
|
24
42
|
this._metadata = this.ydoc.getMap('metadata');
|
|
25
43
|
this.undoManager.addToScope(this._layers);
|
|
26
44
|
this.undoManager.addToScope(this._sources);
|
|
45
|
+
this.undoManager.addToScope(this._stories);
|
|
27
46
|
this.undoManager.addToScope(this._layerTree);
|
|
28
47
|
this._layers.observeDeep(this._layersObserver.bind(this));
|
|
29
48
|
this._layerTree.observe(this._layerTreeObserver.bind(this));
|
|
30
49
|
this._sources.observeDeep(this._sourcesObserver.bind(this));
|
|
50
|
+
this._stories.observeDeep(this._storyMapsObserver.bind(this));
|
|
31
51
|
this._options.observe(this._optionsObserver.bind(this));
|
|
32
52
|
this._metadata.observe(this._metaObserver.bind(this));
|
|
33
53
|
}
|
|
@@ -36,8 +56,9 @@ export class JupyterGISDoc extends YDocument {
|
|
|
36
56
|
const layerTree = this._layerTree.toJSON();
|
|
37
57
|
const options = this._options.toJSON();
|
|
38
58
|
const sources = this._sources.toJSON();
|
|
59
|
+
const stories = this._stories.toJSON();
|
|
39
60
|
const metadata = this._metadata.toJSON();
|
|
40
|
-
return { layers, layerTree, sources, options, metadata };
|
|
61
|
+
return { layers, layerTree, sources, stories, options, metadata };
|
|
41
62
|
}
|
|
42
63
|
setSource(value) {
|
|
43
64
|
if (!value) {
|
|
@@ -48,7 +69,7 @@ export class JupyterGISDoc extends YDocument {
|
|
|
48
69
|
}
|
|
49
70
|
value = value;
|
|
50
71
|
this.transact(() => {
|
|
51
|
-
var _a, _b, _c, _d, _e;
|
|
72
|
+
var _a, _b, _c, _d, _e, _f;
|
|
52
73
|
const layers = (_a = value['layers']) !== null && _a !== void 0 ? _a : {};
|
|
53
74
|
Object.entries(layers).forEach(([key, val]) => this._layers.set(key, val));
|
|
54
75
|
const layerTree = (_b = value['layerTree']) !== null && _b !== void 0 ? _b : [];
|
|
@@ -59,7 +80,9 @@ export class JupyterGISDoc extends YDocument {
|
|
|
59
80
|
Object.entries(options).forEach(([key, val]) => this._options.set(key, val));
|
|
60
81
|
const sources = (_d = value['sources']) !== null && _d !== void 0 ? _d : {};
|
|
61
82
|
Object.entries(sources).forEach(([key, val]) => this._sources.set(key, val));
|
|
62
|
-
const
|
|
83
|
+
const stories = (_e = value['stories']) !== null && _e !== void 0 ? _e : {};
|
|
84
|
+
Object.entries(stories).forEach(([key, val]) => this._stories.set(key, val));
|
|
85
|
+
const metadata = (_f = value['metadata']) !== null && _f !== void 0 ? _f : {};
|
|
63
86
|
Object.entries(metadata).forEach(([key, val]) => this._metadata.set(key, val));
|
|
64
87
|
});
|
|
65
88
|
}
|
|
@@ -89,6 +112,16 @@ export class JupyterGISDoc extends YDocument {
|
|
|
89
112
|
get sources() {
|
|
90
113
|
return JSONExt.deepCopy(this._sources.toJSON());
|
|
91
114
|
}
|
|
115
|
+
set stories(stories) {
|
|
116
|
+
this.transact(() => {
|
|
117
|
+
for (const [key, value] of Object.entries(stories)) {
|
|
118
|
+
this._stories.set(key, value);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
get stories() {
|
|
123
|
+
return JSONExt.deepCopy(this._stories.toJSON());
|
|
124
|
+
}
|
|
92
125
|
get layerTree() {
|
|
93
126
|
return JSONExt.deepCopy(this._layerTree.toJSON());
|
|
94
127
|
}
|
|
@@ -129,6 +162,9 @@ export class JupyterGISDoc extends YDocument {
|
|
|
129
162
|
get sourcesChanged() {
|
|
130
163
|
return this._sourcesChanged;
|
|
131
164
|
}
|
|
165
|
+
get storyMapsChanged() {
|
|
166
|
+
return this._storyMapsChanged;
|
|
167
|
+
}
|
|
132
168
|
get optionsChanged() {
|
|
133
169
|
return this._optionsChanged;
|
|
134
170
|
}
|
|
@@ -201,6 +237,25 @@ export class JupyterGISDoc extends YDocument {
|
|
|
201
237
|
updateSource(id, value) {
|
|
202
238
|
this.transact(() => this._sources.set(id, value));
|
|
203
239
|
}
|
|
240
|
+
removeStoryMap(id) {
|
|
241
|
+
this.transact(() => {
|
|
242
|
+
this._stories.delete(id);
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
addStoryMap(id, value) {
|
|
246
|
+
this.transact(() => {
|
|
247
|
+
this._stories.set(id, value);
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
updateStoryMap(id, value) {
|
|
251
|
+
this.transact(() => this._stories.set(id, value));
|
|
252
|
+
}
|
|
253
|
+
getStoryMap(id) {
|
|
254
|
+
if (!this._stories.has(id)) {
|
|
255
|
+
return undefined;
|
|
256
|
+
}
|
|
257
|
+
return JSONExt.deepCopy(this._stories.get(id));
|
|
258
|
+
}
|
|
204
259
|
getOption(key) {
|
|
205
260
|
const content = this._options.get(key);
|
|
206
261
|
if (!content) {
|
|
@@ -293,4 +348,23 @@ export class JupyterGISDoc extends YDocument {
|
|
|
293
348
|
this._sourcesChanged.emit({ sourceChange: changes });
|
|
294
349
|
}
|
|
295
350
|
}
|
|
351
|
+
_storyMapsObserver(events) {
|
|
352
|
+
const changes = [];
|
|
353
|
+
let needEmit = false;
|
|
354
|
+
events.forEach(event => {
|
|
355
|
+
event.keys.forEach((change, key) => {
|
|
356
|
+
if (!needEmit) {
|
|
357
|
+
needEmit = true;
|
|
358
|
+
}
|
|
359
|
+
changes.push({
|
|
360
|
+
id: key,
|
|
361
|
+
newValue: JSONExt.deepCopy(event.target.toJSON()[key]),
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
needEmit = changes.length === 0 ? true : needEmit;
|
|
366
|
+
if (needEmit) {
|
|
367
|
+
this._storyMapsChanged.emit({ storyMapChange: changes });
|
|
368
|
+
}
|
|
369
|
+
}
|
|
296
370
|
}
|
package/lib/interfaces.d.ts
CHANGED
|
@@ -9,10 +9,13 @@ import { JSONObject } from '@lumino/coreutils';
|
|
|
9
9
|
import { ISignal, Signal } from '@lumino/signaling';
|
|
10
10
|
import { SplitPanel } from '@lumino/widgets';
|
|
11
11
|
import { FeatureLike } from 'ol/Feature';
|
|
12
|
-
import { IJGISContent, IJGISLayer, IJGISLayerGroup, IJGISLayerItem, IJGISLayers, IJGISLayerTree, IJGISOptions, IJGISSource, IJGISSources, SourceType } from './_interface/project/jgis';
|
|
12
|
+
import { IJGISContent, IJGISLayer, IJGISLayerGroup, IJGISLayerItem, IJGISLayers, IJGISLayerTree, IJGISOptions, IJGISSource, IJGISSources, IJGISStoryMap, SourceType } from './_interface/project/jgis';
|
|
13
13
|
import { IRasterSource } from './_interface/project/sources/rasterSource';
|
|
14
14
|
import { Modes } from './types';
|
|
15
15
|
export { IGeoJSONSource } from './_interface/project/sources/geoJsonSource';
|
|
16
|
+
export interface IJGISStoryMaps {
|
|
17
|
+
[k: string]: IJGISStoryMap;
|
|
18
|
+
}
|
|
16
19
|
export type JgisCoordinates = {
|
|
17
20
|
x: number;
|
|
18
21
|
y: number;
|
|
@@ -40,6 +43,12 @@ export interface IJGISLayerDocChange {
|
|
|
40
43
|
export interface IJGISLayerTreeDocChange {
|
|
41
44
|
layerTreeChange?: Delta<IJGISLayerItem[]>;
|
|
42
45
|
}
|
|
46
|
+
export interface IJGISStoryMapDocChange {
|
|
47
|
+
storyMapChange?: Array<{
|
|
48
|
+
id: string;
|
|
49
|
+
newValue: IJGISStoryMap | undefined;
|
|
50
|
+
}>;
|
|
51
|
+
}
|
|
43
52
|
export interface IJGISSourceDocChange {
|
|
44
53
|
sourceChange?: Array<{
|
|
45
54
|
id: string;
|
|
@@ -84,6 +93,7 @@ export interface IJupyterGISDoc extends YDocument<IJupyterGISDocChange> {
|
|
|
84
93
|
options: IJGISOptions;
|
|
85
94
|
layers: IJGISLayers;
|
|
86
95
|
sources: IJGISSources;
|
|
96
|
+
stories: IJGISStoryMaps;
|
|
87
97
|
layerTree: IJGISLayerTree;
|
|
88
98
|
metadata: any;
|
|
89
99
|
readonly editable: boolean;
|
|
@@ -100,6 +110,10 @@ export interface IJupyterGISDoc extends YDocument<IJupyterGISDocChange> {
|
|
|
100
110
|
removeSource(id: string): void;
|
|
101
111
|
addSource(id: string, value: IJGISSource): void;
|
|
102
112
|
updateSource(id: string, value: IJGISSource): void;
|
|
113
|
+
getStoryMap(id: string): IJGISStoryMap | undefined;
|
|
114
|
+
removeStoryMap(id: string): void;
|
|
115
|
+
addStoryMap(id: string, value: IJGISStoryMap): void;
|
|
116
|
+
updateStoryMap(id: string, value: IJGISStoryMap): void;
|
|
103
117
|
addLayerTreeItem(index: number, item: IJGISLayerItem): void;
|
|
104
118
|
updateLayerTreeItem(index: number, item: IJGISLayerItem): void;
|
|
105
119
|
updateObjectParameters(id: string, value: IJGISLayer['parameters'] | IJGISSource['parameters']): void;
|
|
@@ -112,6 +126,7 @@ export interface IJupyterGISDoc extends YDocument<IJupyterGISDocChange> {
|
|
|
112
126
|
optionsChanged: ISignal<IJupyterGISDoc, MapChange>;
|
|
113
127
|
layersChanged: ISignal<IJupyterGISDoc, IJGISLayerDocChange>;
|
|
114
128
|
sourcesChanged: ISignal<IJupyterGISDoc, IJGISSourceDocChange>;
|
|
129
|
+
storyMapsChanged: ISignal<IJupyterGISDoc, IJGISStoryMapDocChange>;
|
|
115
130
|
layerTreeChanged: ISignal<IJupyterGISDoc, IJGISLayerTreeDocChange>;
|
|
116
131
|
metadataChanged: ISignal<IJupyterGISDoc, MapChange>;
|
|
117
132
|
}
|
|
@@ -151,6 +166,7 @@ export interface IJupyterGISModel extends DocumentRegistry.IModel {
|
|
|
151
166
|
contentsManager: Contents.IManager | undefined;
|
|
152
167
|
filePath: string;
|
|
153
168
|
pathChanged: ISignal<IJupyterGISModel, string>;
|
|
169
|
+
stories: Map<string, IJGISStoryMap>;
|
|
154
170
|
getFeaturesForCurrentTile: ({ sourceId, }: {
|
|
155
171
|
sourceId: string;
|
|
156
172
|
}) => FeatureLike[];
|
|
@@ -212,6 +228,15 @@ export interface IJupyterGISModel extends DocumentRegistry.IModel {
|
|
|
212
228
|
addFeatureAsMs(id: string, selectedFeature: string): void;
|
|
213
229
|
triggerLayerUpdate(layerId: string, layer: IJGISLayer): void;
|
|
214
230
|
disposed: ISignal<any, void>;
|
|
231
|
+
getSelectedStory(): {
|
|
232
|
+
storyId: string;
|
|
233
|
+
story: IJGISStoryMap | undefined;
|
|
234
|
+
};
|
|
235
|
+
addStorySegment(): {
|
|
236
|
+
storySegmentId: string;
|
|
237
|
+
storyId: string;
|
|
238
|
+
} | null;
|
|
239
|
+
isSpectaMode(): boolean;
|
|
215
240
|
}
|
|
216
241
|
export interface IUserData {
|
|
217
242
|
userId: number;
|
|
@@ -316,4 +341,6 @@ export interface IJupyterGISSettings {
|
|
|
316
341
|
objectPropertiesDisabled?: boolean;
|
|
317
342
|
annotationsDisabled?: boolean;
|
|
318
343
|
identifyDisabled?: boolean;
|
|
344
|
+
storyMapsDisabled: boolean;
|
|
345
|
+
zoomButtonsEnabled?: boolean;
|
|
319
346
|
}
|
package/lib/model.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { ISettingRegistry } from '@jupyterlab/settingregistry';
|
|
|
6
6
|
import { PartialJSONObject } from '@lumino/coreutils';
|
|
7
7
|
import { ISignal, Signal } from '@lumino/signaling';
|
|
8
8
|
import { FeatureLike } from 'ol/Feature';
|
|
9
|
-
import { IJGISContent, IJGISLayer, IJGISLayerGroup, IJGISLayerTree, IJGISLayers, IJGISOptions, IJGISSource, IJGISSources } from './_interface/project/jgis';
|
|
9
|
+
import { IJGISContent, IJGISLayer, IJGISLayerGroup, IJGISLayerTree, IJGISLayers, IJGISOptions, IJGISSource, IJGISSources, IJGISStoryMap } from './_interface/project/jgis';
|
|
10
10
|
import { IAnnotationModel, IDict, IJGISLayerDocChange, IJGISLayerTreeDocChange, IJGISSourceDocChange, IJupyterGISClientState, IJupyterGISDoc, IJupyterGISModel, ISelection, IUserData, IViewPortState, JgisCoordinates, Pointer, IJupyterGISSettings, SelectionType } from './interfaces';
|
|
11
11
|
import { Modes } from './types';
|
|
12
12
|
export declare class JupyterGISModel implements IJupyterGISModel {
|
|
@@ -157,6 +157,29 @@ export declare class JupyterGISModel implements IJupyterGISModel {
|
|
|
157
157
|
itemId: string;
|
|
158
158
|
} | null>;
|
|
159
159
|
getClientId(): number;
|
|
160
|
+
/**
|
|
161
|
+
* Check if the application is running in Specta mode.
|
|
162
|
+
* Specta mode is enabled when the URL contains 'specta' AND the model has stories.
|
|
163
|
+
*
|
|
164
|
+
* @returns True if running in Specta mode
|
|
165
|
+
*/
|
|
166
|
+
isSpectaMode(): boolean;
|
|
167
|
+
/**
|
|
168
|
+
* Placeholder in case we eventually want to support multiple stories
|
|
169
|
+
* @returns First/only story
|
|
170
|
+
*/
|
|
171
|
+
getSelectedStory(): {
|
|
172
|
+
storyId: string;
|
|
173
|
+
story: IJGISStoryMap | undefined;
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Adds a story segment from the current map view
|
|
177
|
+
* @returns Object with storySegmentId and storyMapId, or null if no extent/zoom found
|
|
178
|
+
*/
|
|
179
|
+
addStorySegment(): {
|
|
180
|
+
storySegmentId: string;
|
|
181
|
+
storyId: string;
|
|
182
|
+
} | null;
|
|
160
183
|
/**
|
|
161
184
|
* Add an item in the layer tree.
|
|
162
185
|
*
|
|
@@ -179,7 +202,7 @@ export declare class JupyterGISModel implements IJupyterGISModel {
|
|
|
179
202
|
removeLayerGroup(groupName: string): void;
|
|
180
203
|
/**
|
|
181
204
|
* Toggle a map interaction mode on or off.
|
|
182
|
-
*
|
|
205
|
+
* Toggling off sets the mode to 'panning'.
|
|
183
206
|
* @param mode The mode to be toggled
|
|
184
207
|
*/
|
|
185
208
|
toggleMode(mode: Modes): void;
|
|
@@ -228,6 +251,7 @@ export declare class JupyterGISModel implements IJupyterGISModel {
|
|
|
228
251
|
private _geolocation;
|
|
229
252
|
private _geolocationChanged;
|
|
230
253
|
private _tileFeatureCache;
|
|
254
|
+
stories: Map<string, IJGISStoryMap>;
|
|
231
255
|
}
|
|
232
256
|
export declare namespace JupyterGISModel {
|
|
233
257
|
/**
|
package/lib/model.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { UUID } from '@lumino/coreutils';
|
|
1
2
|
import { Signal } from '@lumino/signaling';
|
|
2
3
|
import Ajv from 'ajv';
|
|
3
4
|
import { JupyterGISDoc } from './doc';
|
|
@@ -13,6 +14,8 @@ const DEFAULT_SETTINGS = {
|
|
|
13
14
|
objectPropertiesDisabled: false,
|
|
14
15
|
annotationsDisabled: false,
|
|
15
16
|
identifyDisabled: false,
|
|
17
|
+
storyMapsDisabled: false,
|
|
18
|
+
zoomButtonsEnabled: false,
|
|
16
19
|
};
|
|
17
20
|
export class JupyterGISModel {
|
|
18
21
|
constructor(options) {
|
|
@@ -60,6 +63,7 @@ export class JupyterGISModel {
|
|
|
60
63
|
this._editingChanged = new Signal(this);
|
|
61
64
|
this._geolocationChanged = new Signal(this);
|
|
62
65
|
this._tileFeatureCache = new Map();
|
|
66
|
+
this.stories = new Map();
|
|
63
67
|
const { annotationModel, sharedModel, settingRegistry } = options;
|
|
64
68
|
if (sharedModel) {
|
|
65
69
|
this._sharedModel = sharedModel;
|
|
@@ -258,11 +262,12 @@ export class JupyterGISModel {
|
|
|
258
262
|
console.warn(errorMsg);
|
|
259
263
|
}
|
|
260
264
|
this.sharedModel.transact(() => {
|
|
261
|
-
var _a, _b, _c, _d, _e;
|
|
265
|
+
var _a, _b, _c, _d, _e, _f;
|
|
262
266
|
this.sharedModel.sources = (_a = jsonData.sources) !== null && _a !== void 0 ? _a : {};
|
|
263
267
|
this.sharedModel.layers = (_b = jsonData.layers) !== null && _b !== void 0 ? _b : {};
|
|
264
268
|
this.sharedModel.layerTree = (_c = jsonData.layerTree) !== null && _c !== void 0 ? _c : [];
|
|
265
|
-
this.sharedModel.
|
|
269
|
+
this.sharedModel.stories = (_d = jsonData.stories) !== null && _d !== void 0 ? _d : {};
|
|
270
|
+
this.sharedModel.options = (_e = jsonData.options) !== null && _e !== void 0 ? _e : {
|
|
266
271
|
latitude: 0,
|
|
267
272
|
longitude: 0,
|
|
268
273
|
zoom: 0,
|
|
@@ -270,7 +275,7 @@ export class JupyterGISModel {
|
|
|
270
275
|
pitch: 0,
|
|
271
276
|
projection: 'EPSG:3857',
|
|
272
277
|
};
|
|
273
|
-
this.sharedModel.metadata = (
|
|
278
|
+
this.sharedModel.metadata = (_f = jsonData.metadata) !== null && _f !== void 0 ? _f : {};
|
|
274
279
|
});
|
|
275
280
|
this.dirty = true;
|
|
276
281
|
}
|
|
@@ -409,7 +414,19 @@ export class JupyterGISModel {
|
|
|
409
414
|
const source_id = (_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.source;
|
|
410
415
|
this._removeLayerTreeLayer(this.getLayerTree(), layer_id);
|
|
411
416
|
this.sharedModel.removeLayer(layer_id);
|
|
412
|
-
|
|
417
|
+
if ((layer === null || layer === void 0 ? void 0 : layer.type) === 'StorySegmentLayer') {
|
|
418
|
+
// remove this layer id from story maps
|
|
419
|
+
Object.entries(this.sharedModel.stories).forEach(([storyMapId, storyMap]) => {
|
|
420
|
+
var _a;
|
|
421
|
+
if ((_a = storyMap.storySegments) === null || _a === void 0 ? void 0 : _a.includes(layer_id)) {
|
|
422
|
+
const updatedStorySegments = storyMap.storySegments.filter(id => id !== layer_id);
|
|
423
|
+
this.sharedModel.updateStoryMap(storyMapId, Object.assign(Object.assign({}, storyMap), { storySegments: updatedStorySegments }));
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
this.sharedModel.removeSource(source_id);
|
|
429
|
+
}
|
|
413
430
|
}
|
|
414
431
|
setOptions(value) {
|
|
415
432
|
this._sharedModel.options = value;
|
|
@@ -470,6 +487,78 @@ export class JupyterGISModel {
|
|
|
470
487
|
getClientId() {
|
|
471
488
|
return this.sharedModel.awareness.clientID;
|
|
472
489
|
}
|
|
490
|
+
/**
|
|
491
|
+
* Check if the application is running in Specta mode.
|
|
492
|
+
* Specta mode is enabled when the URL contains 'specta' AND the model has stories.
|
|
493
|
+
*
|
|
494
|
+
* @returns True if running in Specta mode
|
|
495
|
+
*/
|
|
496
|
+
isSpectaMode() {
|
|
497
|
+
var _a;
|
|
498
|
+
const hasStories = Object.keys(this.sharedModel.stories).length > 0;
|
|
499
|
+
const isSpecta = !!document.querySelector('meta[name="specta-config"]');
|
|
500
|
+
const guidedMode = ((_a = this.getSelectedStory().story) === null || _a === void 0 ? void 0 : _a.storyType) === 'guided';
|
|
501
|
+
return isSpecta && hasStories && guidedMode;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Placeholder in case we eventually want to support multiple stories
|
|
505
|
+
* @returns First/only story
|
|
506
|
+
*/
|
|
507
|
+
getSelectedStory() {
|
|
508
|
+
const stories = this.sharedModel.stories;
|
|
509
|
+
const storyId = Object.keys(stories)[0];
|
|
510
|
+
return {
|
|
511
|
+
storyId: storyId,
|
|
512
|
+
story: this.sharedModel.getStoryMap(storyId),
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Adds a story segment from the current map view
|
|
517
|
+
* @returns Object with storySegmentId and storyMapId, or null if no extent/zoom found
|
|
518
|
+
*/
|
|
519
|
+
addStorySegment() {
|
|
520
|
+
var _a;
|
|
521
|
+
const { zoom, extent } = this.getOptions();
|
|
522
|
+
const { storyId } = this.getSelectedStory();
|
|
523
|
+
if (!zoom || !extent) {
|
|
524
|
+
console.warn('No extent or zoom found');
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
const newStorySegmentId = UUID.uuid4();
|
|
528
|
+
const layerParams = {
|
|
529
|
+
extent,
|
|
530
|
+
zoom,
|
|
531
|
+
transition: { type: 'linear', time: 1 },
|
|
532
|
+
};
|
|
533
|
+
const layerModel = {
|
|
534
|
+
type: 'StorySegmentLayer',
|
|
535
|
+
visible: true,
|
|
536
|
+
name: 'Story Segment',
|
|
537
|
+
parameters: layerParams,
|
|
538
|
+
};
|
|
539
|
+
this.addLayer(newStorySegmentId, layerModel);
|
|
540
|
+
// if story doesn't exist then add one
|
|
541
|
+
if (!storyId) {
|
|
542
|
+
const storyId = UUID.uuid4();
|
|
543
|
+
const title = 'New Story';
|
|
544
|
+
const storyType = 'guided';
|
|
545
|
+
const storySegments = [newStorySegmentId];
|
|
546
|
+
const storyMap = { title, storyType, storySegments };
|
|
547
|
+
this.sharedModel.addStoryMap(storyId, storyMap);
|
|
548
|
+
return { storySegmentId: newStorySegmentId, storyId };
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
// else need to update story
|
|
552
|
+
const { story } = this.getSelectedStory();
|
|
553
|
+
if (!story) {
|
|
554
|
+
console.warn('No story found, something went wrong');
|
|
555
|
+
return null;
|
|
556
|
+
}
|
|
557
|
+
const newStory = Object.assign(Object.assign({}, story), { storySegments: [...((_a = story.storySegments) !== null && _a !== void 0 ? _a : []), newStorySegmentId] });
|
|
558
|
+
this.sharedModel.updateStoryMap(storyId, newStory);
|
|
559
|
+
return { storySegmentId: newStorySegmentId, storyId };
|
|
560
|
+
}
|
|
561
|
+
}
|
|
473
562
|
/**
|
|
474
563
|
* Add an item in the layer tree.
|
|
475
564
|
*
|
|
@@ -612,7 +701,7 @@ export class JupyterGISModel {
|
|
|
612
701
|
}
|
|
613
702
|
/**
|
|
614
703
|
* Toggle a map interaction mode on or off.
|
|
615
|
-
*
|
|
704
|
+
* Toggling off sets the mode to 'panning'.
|
|
616
705
|
* @param mode The mode to be toggled
|
|
617
706
|
*/
|
|
618
707
|
toggleMode(mode) {
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
"options": {
|
|
21
21
|
"$ref": "#/definitions/jGISOptions"
|
|
22
22
|
},
|
|
23
|
+
"stories": {
|
|
24
|
+
"$ref": "#/definitions/jGISStoryMap"
|
|
25
|
+
},
|
|
23
26
|
"metadata": {
|
|
24
27
|
"type": "object",
|
|
25
28
|
"patternProperties": {
|
|
@@ -41,7 +44,8 @@
|
|
|
41
44
|
"WebGlLayer",
|
|
42
45
|
"ImageLayer",
|
|
43
46
|
"HeatmapLayer",
|
|
44
|
-
"StacLayer"
|
|
47
|
+
"StacLayer",
|
|
48
|
+
"StorySegmentLayer"
|
|
45
49
|
]
|
|
46
50
|
},
|
|
47
51
|
"sourceType": {
|
|
@@ -136,6 +140,42 @@
|
|
|
136
140
|
}
|
|
137
141
|
]
|
|
138
142
|
},
|
|
143
|
+
"jGISStoryMap": {
|
|
144
|
+
"title": "IJGISStoryMap",
|
|
145
|
+
"type": "object",
|
|
146
|
+
"additionalProperties": true,
|
|
147
|
+
"properties": {
|
|
148
|
+
"title": {
|
|
149
|
+
"type": "string",
|
|
150
|
+
"title": "Title",
|
|
151
|
+
"description": "The title of the story map"
|
|
152
|
+
},
|
|
153
|
+
"storyType": {
|
|
154
|
+
"type": "string",
|
|
155
|
+
"title": "Story Type",
|
|
156
|
+
"enum": ["guided", "unguided"],
|
|
157
|
+
"description": "The type of story map"
|
|
158
|
+
},
|
|
159
|
+
"storySegments": {
|
|
160
|
+
"type": "array",
|
|
161
|
+
"default": [],
|
|
162
|
+
"description": "Array of story segments for the story map",
|
|
163
|
+
"items": {
|
|
164
|
+
"type": "string"
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
"presentaionBgColor": {
|
|
168
|
+
"type": "string",
|
|
169
|
+
"title": "Presentation Background Color",
|
|
170
|
+
"description": "The background color of the story map"
|
|
171
|
+
},
|
|
172
|
+
"presentaionTextColor": {
|
|
173
|
+
"type": "string",
|
|
174
|
+
"title": "Presentation Text Color",
|
|
175
|
+
"description": "The text color of the story map"
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
},
|
|
139
179
|
"jGISLayers": {
|
|
140
180
|
"title": "IJGISLayers",
|
|
141
181
|
"type": "object",
|
|
@@ -201,6 +241,11 @@
|
|
|
201
241
|
"useExtent": {
|
|
202
242
|
"type": "boolean",
|
|
203
243
|
"default": false
|
|
244
|
+
},
|
|
245
|
+
"storyMapPresentationMode": {
|
|
246
|
+
"type": "boolean",
|
|
247
|
+
"title": "Whether presentation mode is active",
|
|
248
|
+
"default": false
|
|
204
249
|
}
|
|
205
250
|
}
|
|
206
251
|
},
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "object",
|
|
3
|
+
"description": "StorySegmentLayer",
|
|
4
|
+
"title": "IStorySegmentLayer",
|
|
5
|
+
"additionalProperties": false,
|
|
6
|
+
"required": ["zoom", "extent", "transition"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"zoom": {
|
|
9
|
+
"type": "number",
|
|
10
|
+
"default": 0
|
|
11
|
+
},
|
|
12
|
+
"extent": {
|
|
13
|
+
"type": "array",
|
|
14
|
+
"default": null,
|
|
15
|
+
"items": {
|
|
16
|
+
"type": "number"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"content": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"additionalProperties": false,
|
|
22
|
+
"properties": {
|
|
23
|
+
"title": {
|
|
24
|
+
"type": "string"
|
|
25
|
+
},
|
|
26
|
+
"image": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"description": "Link to image for the story"
|
|
29
|
+
},
|
|
30
|
+
"markdown": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"description": "Markdown string representing the content of the story stop"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"transition": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"description": "Transition configuration between to this story segment",
|
|
39
|
+
"properties": {
|
|
40
|
+
"type": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"enum": ["linear", "immediate", "smooth"],
|
|
43
|
+
"description": "Transition animation style",
|
|
44
|
+
"default": "linear"
|
|
45
|
+
},
|
|
46
|
+
"time": {
|
|
47
|
+
"type": "number",
|
|
48
|
+
"description": "The time in seconds for the transition",
|
|
49
|
+
"default": 1
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"required": ["type", "time"]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
package/lib/types.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export * from './_interface/project/sources/geoParquetSource';
|
|
|
11
11
|
export * from './_interface/project/sources/markerSource';
|
|
12
12
|
export * from './_interface/project/layers/heatmapLayer';
|
|
13
13
|
export * from './_interface/project/layers/hillshadeLayer';
|
|
14
|
+
export * from './_interface/project/layers/storySegmentLayer';
|
|
14
15
|
export * from './_interface/project/layers/rasterLayer';
|
|
15
16
|
export * from './_interface/project/layers/vectorLayer';
|
|
16
17
|
export * from './_interface/project/layers/imageLayer';
|
package/lib/types.js
CHANGED
|
@@ -13,6 +13,7 @@ export * from './_interface/project/sources/markerSource';
|
|
|
13
13
|
// Layers
|
|
14
14
|
export * from './_interface/project/layers/heatmapLayer';
|
|
15
15
|
export * from './_interface/project/layers/hillshadeLayer';
|
|
16
|
+
export * from './_interface/project/layers/storySegmentLayer';
|
|
16
17
|
export * from './_interface/project/layers/rasterLayer';
|
|
17
18
|
export * from './_interface/project/layers/vectorLayer';
|
|
18
19
|
export * from './_interface/project/layers/imageLayer';
|