@jupytergis/schema 0.10.1 → 0.11.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.
@@ -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,7 @@ export type IJGISLayerItem = string | IJGISLayerGroup;
43
44
  * via the `definition` "jGISLayerTree".
44
45
  */
45
46
  export type IJGISLayerTree = IJGISLayerItem[];
47
+ export type WhetherPresentationModeIsActive = boolean;
46
48
 
47
49
  export interface IJGISContent {
48
50
  schemaVersion?: string;
@@ -140,4 +142,23 @@ export interface IJGISOptions {
140
142
  extent?: number[];
141
143
  projection?: string;
142
144
  useExtent?: boolean;
145
+ storyMapPresentationMode?: WhetherPresentationModeIsActive;
146
+ }
147
+ /**
148
+ * This interface was referenced by `IJGISContent`'s JSON-Schema
149
+ * via the `definition` "jGISStoryMap".
150
+ */
151
+ export interface IJGISStoryMap {
152
+ /**
153
+ * The title of the story map
154
+ */
155
+ title?: string;
156
+ /**
157
+ * The type of story map
158
+ */
159
+ storyType?: "guided" | "unguided";
160
+ /**
161
+ * Array of story segments for the story map
162
+ */
163
+ storySegments?: string[];
143
164
  }
@@ -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
- this._optionsChanged.emit(event.keys);
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
- this._metadataChanged.emit(event.keys);
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 metadata = (_e = value['metadata']) !== null && _e !== void 0 ? _e : {};
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
  }
@@ -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,14 @@ 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
+ storySegmentId: string;
233
+ story: IJGISStoryMap | undefined;
234
+ };
235
+ addStorySegment(): {
236
+ storySegmentId: string;
237
+ storyMapId: string;
238
+ } | null;
215
239
  }
216
240
  export interface IUserData {
217
241
  userId: number;
@@ -316,4 +340,5 @@ export interface IJupyterGISSettings {
316
340
  objectPropertiesDisabled?: boolean;
317
341
  annotationsDisabled?: boolean;
318
342
  identifyDisabled?: boolean;
343
+ storyMapsDisabled: boolean;
319
344
  }
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,22 @@ export declare class JupyterGISModel implements IJupyterGISModel {
157
157
  itemId: string;
158
158
  } | null>;
159
159
  getClientId(): number;
160
+ /**
161
+ * Placeholder in case we eventually want to support multiple stories
162
+ * @returns First/only story
163
+ */
164
+ getSelectedStory(): {
165
+ storySegmentId: string;
166
+ story: IJGISStoryMap | undefined;
167
+ };
168
+ /**
169
+ * Adds a story segment from the current map view
170
+ * @returns Object with storySegmentId and storyMapId, or null if no extent/zoom found
171
+ */
172
+ addStorySegment(): {
173
+ storySegmentId: string;
174
+ storyMapId: string;
175
+ } | null;
160
176
  /**
161
177
  * Add an item in the layer tree.
162
178
  *
@@ -179,7 +195,7 @@ export declare class JupyterGISModel implements IJupyterGISModel {
179
195
  removeLayerGroup(groupName: string): void;
180
196
  /**
181
197
  * Toggle a map interaction mode on or off.
182
- * Toggleing off sets the mode to 'panning'.
198
+ * Toggling off sets the mode to 'panning'.
183
199
  * @param mode The mode to be toggled
184
200
  */
185
201
  toggleMode(mode: Modes): void;
@@ -228,6 +244,7 @@ export declare class JupyterGISModel implements IJupyterGISModel {
228
244
  private _geolocation;
229
245
  private _geolocationChanged;
230
246
  private _tileFeatureCache;
247
+ stories: Map<string, IJGISStoryMap>;
231
248
  }
232
249
  export declare namespace JupyterGISModel {
233
250
  /**
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,7 @@ const DEFAULT_SETTINGS = {
13
14
  objectPropertiesDisabled: false,
14
15
  annotationsDisabled: false,
15
16
  identifyDisabled: false,
17
+ storyMapsDisabled: false,
16
18
  };
17
19
  export class JupyterGISModel {
18
20
  constructor(options) {
@@ -60,6 +62,7 @@ export class JupyterGISModel {
60
62
  this._editingChanged = new Signal(this);
61
63
  this._geolocationChanged = new Signal(this);
62
64
  this._tileFeatureCache = new Map();
65
+ this.stories = new Map();
63
66
  const { annotationModel, sharedModel, settingRegistry } = options;
64
67
  if (sharedModel) {
65
68
  this._sharedModel = sharedModel;
@@ -409,7 +412,19 @@ export class JupyterGISModel {
409
412
  const source_id = (_a = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _a === void 0 ? void 0 : _a.source;
410
413
  this._removeLayerTreeLayer(this.getLayerTree(), layer_id);
411
414
  this.sharedModel.removeLayer(layer_id);
412
- this.sharedModel.removeSource(source_id);
415
+ if ((layer === null || layer === void 0 ? void 0 : layer.type) === 'StorySegmentLayer') {
416
+ // remove this layer id from story maps
417
+ Object.entries(this.sharedModel.stories).forEach(([storyMapId, storyMap]) => {
418
+ var _a;
419
+ if ((_a = storyMap.storySegments) === null || _a === void 0 ? void 0 : _a.includes(layer_id)) {
420
+ const updatedStorySegments = storyMap.storySegments.filter(id => id !== layer_id);
421
+ this.sharedModel.updateStoryMap(storyMapId, Object.assign(Object.assign({}, storyMap), { storySegments: updatedStorySegments }));
422
+ }
423
+ });
424
+ }
425
+ else {
426
+ this.sharedModel.removeSource(source_id);
427
+ }
413
428
  }
414
429
  setOptions(value) {
415
430
  this._sharedModel.options = value;
@@ -470,6 +485,65 @@ export class JupyterGISModel {
470
485
  getClientId() {
471
486
  return this.sharedModel.awareness.clientID;
472
487
  }
488
+ /**
489
+ * Placeholder in case we eventually want to support multiple stories
490
+ * @returns First/only story
491
+ */
492
+ getSelectedStory() {
493
+ const stories = this.sharedModel.stories;
494
+ const storyId = Object.keys(stories)[0];
495
+ return {
496
+ storySegmentId: storyId,
497
+ story: this.sharedModel.getStoryMap(storyId),
498
+ };
499
+ }
500
+ /**
501
+ * Adds a story segment from the current map view
502
+ * @returns Object with storySegmentId and storyMapId, or null if no extent/zoom found
503
+ */
504
+ addStorySegment() {
505
+ var _a;
506
+ const { zoom, extent } = this.getOptions();
507
+ if (!zoom || !extent) {
508
+ console.warn('No extent or zoom found');
509
+ return null;
510
+ }
511
+ const storyMapId = UUID.uuid4();
512
+ const newStorySegmentId = UUID.uuid4();
513
+ const layerParams = {
514
+ extent,
515
+ zoom,
516
+ transition: { type: 'linear', time: 1 },
517
+ };
518
+ const layerModel = {
519
+ type: 'StorySegmentLayer',
520
+ visible: true,
521
+ name: 'Story Segment',
522
+ parameters: layerParams,
523
+ };
524
+ this.addLayer(newStorySegmentId, layerModel);
525
+ // check for stories
526
+ const isStoriesExist = Object.keys(this.sharedModel.stories).length !== 0;
527
+ // if not stories, then just add simple
528
+ if (!isStoriesExist) {
529
+ const title = 'New Story';
530
+ const storyType = 'guided';
531
+ const storySegments = [newStorySegmentId];
532
+ const storyMap = { title, storyType, storySegments };
533
+ this.sharedModel.addStoryMap(storyMapId, storyMap);
534
+ }
535
+ else {
536
+ // else need to update stories
537
+ const { story } = this.getSelectedStory();
538
+ if (!story) {
539
+ console.warn('No story found, something went wrong');
540
+ return null;
541
+ }
542
+ const newStory = Object.assign(Object.assign({}, story), { storySegments: [...((_a = story.storySegments) !== null && _a !== void 0 ? _a : []), newStorySegmentId] });
543
+ this.sharedModel.updateStoryMap(storyMapId, newStory);
544
+ }
545
+ return { storySegmentId: newStorySegmentId, storyMapId };
546
+ }
473
547
  /**
474
548
  * Add an item in the layer tree.
475
549
  *
@@ -612,7 +686,7 @@ export class JupyterGISModel {
612
686
  }
613
687
  /**
614
688
  * Toggle a map interaction mode on or off.
615
- * Toggleing off sets the mode to 'panning'.
689
+ * Toggling off sets the mode to 'panning'.
616
690
  * @param mode The mode to be toggled
617
691
  */
618
692
  toggleMode(mode) {
@@ -41,7 +41,8 @@
41
41
  "WebGlLayer",
42
42
  "ImageLayer",
43
43
  "HeatmapLayer",
44
- "StacLayer"
44
+ "StacLayer",
45
+ "StorySegmentLayer"
45
46
  ]
46
47
  },
47
48
  "sourceType": {
@@ -136,6 +137,30 @@
136
137
  }
137
138
  ]
138
139
  },
140
+ "jGISStoryMap": {
141
+ "title": "IJGISStoryMap",
142
+ "type": "object",
143
+ "additionalProperties": false,
144
+ "properties": {
145
+ "title": {
146
+ "type": "string",
147
+ "description": "The title of the story map"
148
+ },
149
+ "storyType": {
150
+ "type": "string",
151
+ "enum": ["guided", "unguided"],
152
+ "description": "The type of story map"
153
+ },
154
+ "storySegments": {
155
+ "type": "array",
156
+ "default": [],
157
+ "description": "Array of story segments for the story map",
158
+ "items": {
159
+ "type": "string"
160
+ }
161
+ }
162
+ }
163
+ },
139
164
  "jGISLayers": {
140
165
  "title": "IJGISLayers",
141
166
  "type": "object",
@@ -201,6 +226,11 @@
201
226
  "useExtent": {
202
227
  "type": "boolean",
203
228
  "default": false
229
+ },
230
+ "storyMapPresentationMode": {
231
+ "type": "boolean",
232
+ "title": "Whether presentation mode is active",
233
+ "default": false
204
234
  }
205
235
  }
206
236
  },
@@ -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';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jupytergis/schema",
3
- "version": "0.10.1",
3
+ "version": "0.11.1",
4
4
  "description": "A JupyterGIS schema package.",
5
5
  "keywords": [
6
6
  "jupytergis"