Skip to content

Commit fc9a19a

Browse files
Add story maps (#994)
* Add command * Add landmarks as a layer * Move to landmark wip * Add a zoom to layer button * Add a story map thing to the file * Add temp button to create story * Add story creation method to model * Add story editing(?) panel * Rename panel * Add storiesMap to file * Add some panels * Add temp viewer panel * Start implementing story viewer * Use textarea for markdown field * Push zoom button to right side * Only support one story * Handle story signal stuff * Update landmark order when drag and dropping * Update viewer order based on list order * Tidy * Hack story selection * Effect cleaning * Return ID as well * Refactor viewer panel * Refactor editor panel * Dont reverse landmark list * Update viewer content when selecting landmarks * Remove rank from schema * Update viewer panel style * Move to position when clicking landmark in unguided tour * Remove zoomies * Fix status bar loading when adding landmark * Use one panel for both story panels * Refactor editor panel * Remove stray console logs * Add story presentation as a setting * Add presentation flags to side panels * Some styling * see ess ess * Add button to recenter landmark * Remove hardcoded id * Clean up * Format-on-save, why hast thou forsaken me? * Landmark icon * Allow customization for transition animations * Change preview button to a switch * Move nav buttons * Cancel animations before starting another * Remove landmarks item from story editor form * Restore zoomies * Replace visibility toggle with slide number indicator * Move transition settings to landmark * Select layer when moving through story; Move CSS to classes * Better nav button styles * Get story when mounting component * Change default button border color * Add form button to update extent and zoom of landmark * Add padding * GOOD rebase * Preload image in case link is bad * Rename storiesMap * Change landmark extent button text * Dont display slide number if layerName is defined * Change add landmark label * Add add landmark option to layer context menu * Change add landmark command label wording * Fix disabling object properties in presentation mode * Invert presentation mode setting * Fix overwriting values on opening file * Add Story Map example * Stop console spam * Zoom out more on smooth transitions * Change layer name * Another GOOD rebase * Why were these snapshots so old * Update lockfile * Lint * Bot missed some snapshots, expected ones still had undo/redo * Update Playwright Snapshots * Update Playwright Snapshots * Test * Change UI text to use Segment instead of landmark * add button * Give model addStorySegment method * Lint * Update Playwright Snapshots * Landmark -> Story Segment * Lint * Update example * Bot on bot violence * Update settings/options * Use story mode setting and option to update UI * Use old moveToPosition * Update Playwright Snapshots * Update Playwright Snapshots * Fix presentation mode bool * Update Playwright Snapshots * Update Playwright Snapshots --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent d5ac999 commit fc9a19a

File tree

55 files changed

+2484
-77
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2484
-77
lines changed

examples/story_map.jGIS

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
{
2+
"layerTree": [
3+
"5d10a9ba-eeef-486d-9761-33ff1ba4106e",
4+
"5f790e80-230b-4c00-8426-b3a9385937a5",
5+
"8591b32f-11c0-4d70-a997-e94d258bb100",
6+
"bcd22241-e404-44b4-873b-81e6a4ccad84",
7+
"5862186d-0cae-4cee-be39-18951e31030f",
8+
"aabaa10b-4a6f-4a24-9ead-6f17bb7f2f7d",
9+
"93080edc-815f-4364-a874-90fd53a60c35"
10+
],
11+
"layers": {
12+
"5862186d-0cae-4cee-be39-18951e31030f": {
13+
"name": "The Redeemer",
14+
"parameters": {
15+
"opacity": 1.0,
16+
"source": "a7e1fc1e-1ab9-461f-89cb-2010c790062f",
17+
"symbologyState": {
18+
"renderType": "Single Symbol"
19+
}
20+
},
21+
"type": "VectorLayer",
22+
"visible": true
23+
},
24+
"5d10a9ba-eeef-486d-9761-33ff1ba4106e": {
25+
"name": "OpenStreetMap.Mapnik Layer",
26+
"parameters": {
27+
"extent": [
28+
-7248097.58721681,
29+
-743268.1554684378,
30+
-6334954.257801768,
31+
-303993.91946789756
32+
],
33+
"opacity": 1.0,
34+
"source": "32431800-9aad-4702-926b-52fcdce5530e",
35+
"zoom": 7.604194565762505
36+
},
37+
"type": "RasterLayer",
38+
"visible": true
39+
},
40+
"5f790e80-230b-4c00-8426-b3a9385937a5": {
41+
"name": "Cristo Redentor",
42+
"parameters": {
43+
"content": {
44+
"image": "https://images.pexels.com/photos/2771080/pexels-photo-2771080.jpeg",
45+
"markdown": "**Christ the Redeemer** (Portuguese: *Cristo Redentor*, standard Brazilian Portuguese: [\u02c8k\u027eistu \u0281ed\u1ebd\u02c8to\u0281]) is an Art Deco statue of Jesus in Rio de Janeiro, Brazil, created by French-Polish sculptor Paul Landowski and built by Brazilian engineer Heitor da Silva Costa, in collaboration with French engineer Albert Caquot.",
46+
"title": "The Redeemer"
47+
},
48+
"extent": [
49+
-4814802.663104911,
50+
-2630126.461218188,
51+
-4804544.428248574,
52+
-2622570.615984798
53+
],
54+
"transition": {
55+
"time": 2.0,
56+
"type": "smooth"
57+
},
58+
"zoom": 14.080181481426658
59+
},
60+
"type": "StorySegmentLayer",
61+
"visible": true
62+
},
63+
"8591b32f-11c0-4d70-a997-e94d258bb100": {
64+
"name": "La Libert\u00e9 \u00e9clairant le monde",
65+
"parameters": {
66+
"content": {
67+
"image": "https://images.pexels.com/photos/356844/pexels-photo-356844.jpeg",
68+
"markdown": "**The Statue of Liberty** (Liberty Enlightening the World; French: *La Libert\u00e9 \u00e9clairant le monde*) is a colossal neoclassical sculpture of a robed and crowned female on Liberty Island in New York Harbor, within New York City.",
69+
"title": "The Statue of Liberty"
70+
},
71+
"extent": [
72+
-8244347.787989774,
73+
4964878.911422763,
74+
-8240146.493537257,
75+
4967973.433151666
76+
],
77+
"transition": {
78+
"time": 2.0,
79+
"type": "smooth"
80+
},
81+
"zoom": 15.368058180489786
82+
},
83+
"type": "StorySegmentLayer",
84+
"visible": true
85+
},
86+
"93080edc-815f-4364-a874-90fd53a60c35": {
87+
"name": "The Thinker",
88+
"parameters": {
89+
"opacity": 1.0,
90+
"source": "19ef5002-5670-4c17-b53f-44a48b739158",
91+
"symbologyState": {
92+
"renderType": "Single Symbol"
93+
}
94+
},
95+
"type": "VectorLayer",
96+
"visible": true
97+
},
98+
"aabaa10b-4a6f-4a24-9ead-6f17bb7f2f7d": {
99+
"name": "Statue of Liberty",
100+
"parameters": {
101+
"opacity": 1.0,
102+
"source": "660bb0bb-e0ce-4692-a7c7-119829d6e0d1",
103+
"symbologyState": {
104+
"renderType": "Single Symbol"
105+
}
106+
},
107+
"type": "VectorLayer",
108+
"visible": true
109+
},
110+
"bcd22241-e404-44b4-873b-81e6a4ccad84": {
111+
"name": "Le Penseur",
112+
"parameters": {
113+
"content": {
114+
"image": "https://images.pexels.com/photos/6486115/pexels-photo-6486115.jpeg",
115+
"markdown": "**The Thinker** (French: *Le Penseur*), by Auguste Rodin, is a bronze sculpture depicting a nude male figure of heroic size, seated on a large rock, leaning forward, his right elbow placed upon his left thigh, with the back of his right hand supporting his chin in a posture evocative of deep thought and contemplation.",
116+
"title": "The Thinker"
117+
},
118+
"extent": [
119+
255360.959592592,
120+
6248688.604308756,
121+
261009.190947415,
122+
6252093.017728101
123+
],
124+
"transition": {
125+
"time": 2.0,
126+
"type": "smooth"
127+
},
128+
"zoom": 15.230368389488868
129+
},
130+
"type": "StorySegmentLayer",
131+
"visible": true
132+
}
133+
},
134+
"metadata": {},
135+
"options": {
136+
"bearing": 0.0,
137+
"extent": [
138+
256226.31587442243,
139+
6249293.335639825,
140+
260143.83466558298,
141+
6251488.286397034
142+
],
143+
"latitude": 48.855574302496535,
144+
"longitude": 2.319315992465925,
145+
"pitch": 0.0,
146+
"projection": "EPSG:3857",
147+
"zoom": 15.230368389488868
148+
},
149+
"schemaVersion": "0.5.0",
150+
"sources": {
151+
"19ef5002-5670-4c17-b53f-44a48b739158": {
152+
"name": "Marker",
153+
"parameters": {
154+
"feature": {
155+
"coords": [
156+
257771.740387152,
157+
6250219.77589449
158+
]
159+
}
160+
},
161+
"type": "MarkerSource"
162+
},
163+
"32431800-9aad-4702-926b-52fcdce5530e": {
164+
"name": "OpenStreetMap.Mapnik",
165+
"parameters": {
166+
"attribution": "(C) OpenStreetMap contributors",
167+
"interpolate": false,
168+
"maxZoom": 19.0,
169+
"minZoom": 0.0,
170+
"provider": "OpenStreetMap",
171+
"url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
172+
"urlParameters": {}
173+
},
174+
"type": "RasterSource"
175+
},
176+
"660bb0bb-e0ce-4692-a7c7-119829d6e0d1": {
177+
"name": "Marker",
178+
"parameters": {
179+
"feature": {
180+
"coords": [
181+
-8242685.709008398,
182+
4966710.369862526
183+
]
184+
}
185+
},
186+
"type": "MarkerSource"
187+
},
188+
"a7e1fc1e-1ab9-461f-89cb-2010c790062f": {
189+
"name": "Marker",
190+
"parameters": {
191+
"feature": {
192+
"coords": [
193+
-4810184.197896464,
194+
-2626176.8148461888
195+
]
196+
}
197+
},
198+
"type": "MarkerSource"
199+
}
200+
},
201+
"stories": {
202+
"1b181980-a95a-4f23-8e5d-980c66ff93ea": {
203+
"storySegments": [
204+
"5f790e80-230b-4c00-8426-b3a9385937a5",
205+
"8591b32f-11c0-4d70-a997-e94d258bb100",
206+
"bcd22241-e404-44b4-873b-81e6a4ccad84"
207+
],
208+
"storyType": "guided",
209+
"title": "Famous Statues"
210+
}
211+
}
212+
}

packages/base/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
"@radix-ui/react-dropdown-menu": "^2.1.15",
6969
"@radix-ui/react-popover": "^1.1.14",
7070
"@radix-ui/react-slot": "^1.2.3",
71+
"@radix-ui/react-switch": "^1.2.6",
7172
"@radix-ui/react-tabs": "^1.1.12",
7273
"@radix-ui/react-toggle-group": "^1.1.10",
7374
"@rjsf/core": "^4.2.0",
@@ -93,6 +94,7 @@
9394
"proj4-list": "1.0.4",
9495
"react": "^18.0.1",
9596
"react-day-picker": "^9.7.0",
97+
"react-markdown": "^10.1.0",
9698
"shpjs": "^6.1.0",
9799
"styled-components": "^5.3.6",
98100
"three": "^0.135.0",

packages/base/src/commands/BaseCommandIDs.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,6 @@ export const showFiltersTab = 'jupytergis:showFiltersTab';
6565
export const showObjectPropertiesTab = 'jupytergis:showObjectPropertiesTab';
6666
export const showAnnotationsTab = 'jupytergis:showAnnotationsTab';
6767
export const showIdentifyPanelTab = 'jupytergis:showIdentifyPanelTab';
68+
69+
// Story maps
70+
export const addStorySegment = 'jupytergis:addStorySegment';

packages/base/src/commands/index.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,28 @@ export function addCommands(
10541054
...icons.get(CommandIDs.addMarker),
10551055
});
10561056

1057+
commands.addCommand(CommandIDs.addStorySegment, {
1058+
label: trans.__('Add Story Segment'),
1059+
isEnabled: () => {
1060+
const current = tracker.currentWidget;
1061+
if (!current) {
1062+
return false;
1063+
}
1064+
return (
1065+
current.model.sharedModel.editable &&
1066+
!current.model.jgisSettings.storyMapsDisabled
1067+
);
1068+
},
1069+
execute: args => {
1070+
const current = tracker.currentWidget;
1071+
if (!current) {
1072+
return;
1073+
}
1074+
current.model.addStorySegment();
1075+
},
1076+
...icons.get(CommandIDs.addStorySegment),
1077+
});
1078+
10571079
loadKeybindings(commands, keybindings);
10581080
}
10591081

packages/base/src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const iconObject = {
5858
[CommandIDs.identify]: { icon: infoIcon },
5959
[CommandIDs.temporalController]: { icon: clockIcon },
6060
[CommandIDs.addMarker]: { icon: markerIcon },
61+
[CommandIDs.addStorySegment]: { iconClass: 'fa fa-link' },
6162
};
6263

6364
/**

packages/base/src/formbuilder/formselectors.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { LayerType, SourceType } from '@jupytergis/schema';
33
import {
44
HeatmapLayerPropertiesForm,
55
HillshadeLayerPropertiesForm,
6+
StorySegmentLayerPropertiesForm,
67
LayerPropertiesForm,
78
VectorLayerPropertiesForm,
89
WebGlLayerPropertiesForm,
@@ -33,6 +34,11 @@ export function getLayerTypeForm(
3334
break;
3435
case 'HeatmapLayer':
3536
LayerForm = HeatmapLayerPropertiesForm;
37+
break;
38+
case 'StorySegmentLayer':
39+
LayerForm = StorySegmentLayerPropertiesForm;
40+
break;
41+
3642
// ADD MORE FORM TYPES HERE
3743
}
3844

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { IDict } from '@jupytergis/schema';
2+
3+
import { BaseForm } from './baseform';
4+
5+
/**
6+
* The form to modify a hillshade layer.
7+
*/
8+
export class StoryEditorPropertiesForm extends BaseForm {
9+
protected processSchema(
10+
data: IDict<any> | undefined,
11+
schema: IDict,
12+
uiSchema: IDict,
13+
) {
14+
super.processSchema(data, schema, uiSchema);
15+
this.removeFormEntry('storySegments', data, schema, uiSchema);
16+
}
17+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { IJupyterGISModel } from '@jupytergis/schema';
2+
import { LabIcon } from '@jupyterlab/ui-components';
3+
import React from 'react';
4+
5+
import { targetWithCenterIcon } from '@/src/icons';
6+
import { Button } from '@/src/shared/components/Button';
7+
8+
interface IStorySegmentResetProps {
9+
model?: IJupyterGISModel;
10+
layerId?: string;
11+
}
12+
13+
function StorySegmentReset({ model, layerId }: IStorySegmentResetProps) {
14+
const handleSetStorySegmentToCurrentView = () => {
15+
if (!model || !layerId) {
16+
return;
17+
}
18+
const layer = model.getLayer(layerId);
19+
if (!layer) {
20+
return;
21+
}
22+
const { zoom, extent } = model.getOptions();
23+
const updatedLayer = {
24+
...layer,
25+
parameters: {
26+
...layer.parameters,
27+
zoom,
28+
extent,
29+
},
30+
};
31+
32+
model.sharedModel.updateLayer(layerId, updatedLayer);
33+
};
34+
35+
return (
36+
<div>
37+
<Button
38+
title="Set story segment to current viewport"
39+
onClick={handleSetStorySegmentToCurrentView}
40+
>
41+
<LabIcon.resolveReact
42+
icon={targetWithCenterIcon}
43+
className="jp-gis-layerIcon"
44+
tag="span"
45+
/>
46+
Set Story Segment Extent
47+
</Button>
48+
</div>
49+
);
50+
}
51+
52+
export default StorySegmentReset;

packages/base/src/formbuilder/objectform/layer/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from './hillshadeLayerForm';
33
export * from './layerform';
44
export * from './vectorlayerform';
55
export * from './webGlLayerForm';
6+
export * from './storySegmentLayerForm';

0 commit comments

Comments
 (0)