@jupytergis/base 0.3.0 → 0.4.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.
Files changed (76) hide show
  1. package/lib/annotations/components/Annotation.js +1 -1
  2. package/lib/annotations/model.d.ts +6 -7
  3. package/lib/annotations/model.js +15 -15
  4. package/lib/commands.d.ts +2 -3
  5. package/lib/commands.js +117 -62
  6. package/lib/constants.d.ts +2 -0
  7. package/lib/constants.js +4 -1
  8. package/lib/dialogs/formdialog.js +2 -2
  9. package/lib/dialogs/layerBrowserDialog.d.ts +4 -5
  10. package/lib/dialogs/layerBrowserDialog.js +9 -9
  11. package/lib/dialogs/symbology/hooks/useGetBandInfo.d.ts +1 -2
  12. package/lib/dialogs/symbology/hooks/useGetBandInfo.js +11 -6
  13. package/lib/dialogs/symbology/hooks/useGetProperties.d.ts +1 -1
  14. package/lib/dialogs/symbology/hooks/useGetProperties.js +6 -8
  15. package/lib/dialogs/symbology/symbologyDialog.d.ts +2 -3
  16. package/lib/dialogs/symbology/symbologyDialog.js +10 -9
  17. package/lib/dialogs/symbology/tiff_layer/TiffRendering.d.ts +1 -1
  18. package/lib/dialogs/symbology/tiff_layer/TiffRendering.js +6 -6
  19. package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.d.ts +1 -1
  20. package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.js +5 -4
  21. package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.d.ts +1 -1
  22. package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +8 -7
  23. package/lib/dialogs/symbology/vector_layer/VectorRendering.d.ts +1 -1
  24. package/lib/dialogs/symbology/vector_layer/VectorRendering.js +18 -13
  25. package/lib/dialogs/symbology/vector_layer/types/Categorized.d.ts +1 -1
  26. package/lib/dialogs/symbology/vector_layer/types/Categorized.js +30 -19
  27. package/lib/dialogs/symbology/vector_layer/types/Graduated.d.ts +1 -1
  28. package/lib/dialogs/symbology/vector_layer/types/Graduated.js +16 -13
  29. package/lib/dialogs/symbology/vector_layer/types/Heatmap.d.ts +4 -0
  30. package/lib/dialogs/symbology/vector_layer/types/Heatmap.js +77 -0
  31. package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.d.ts +1 -1
  32. package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.js +4 -3
  33. package/lib/formbuilder/creationform.d.ts +1 -2
  34. package/lib/formbuilder/creationform.js +4 -4
  35. package/lib/formbuilder/editform.d.ts +1 -2
  36. package/lib/formbuilder/editform.js +7 -7
  37. package/lib/formbuilder/formselectors.js +5 -2
  38. package/lib/formbuilder/objectform/baseform.d.ts +3 -4
  39. package/lib/formbuilder/objectform/baseform.js +2 -2
  40. package/lib/formbuilder/objectform/fileselectorwidget.js +13 -6
  41. package/lib/formbuilder/objectform/geotiffsource.d.ts +5 -1
  42. package/lib/formbuilder/objectform/geotiffsource.js +64 -18
  43. package/lib/formbuilder/objectform/heatmapLayerForm.d.ts +11 -0
  44. package/lib/formbuilder/objectform/heatmapLayerForm.js +60 -0
  45. package/lib/formbuilder/objectform/vectorlayerform.d.ts +0 -2
  46. package/lib/formbuilder/objectform/vectorlayerform.js +0 -59
  47. package/lib/mainview/TemporalSlider.d.ts +8 -0
  48. package/lib/mainview/TemporalSlider.js +303 -0
  49. package/lib/mainview/mainView.d.ts +25 -4
  50. package/lib/mainview/mainView.js +213 -75
  51. package/lib/mainview/mainviewmodel.d.ts +4 -0
  52. package/lib/mainview/mainviewmodel.js +4 -0
  53. package/lib/mainview/mainviewwidget.d.ts +0 -2
  54. package/lib/mainview/mainviewwidget.js +0 -2
  55. package/lib/panelview/annotationPanel.js +5 -5
  56. package/lib/panelview/components/filter-panel/Filter.js +4 -25
  57. package/lib/panelview/components/identify-panel/IdentifyPanel.js +1 -1
  58. package/lib/panelview/components/layers.js +2 -2
  59. package/lib/panelview/components/sources.js +1 -1
  60. package/lib/panelview/leftpanel.d.ts +3 -0
  61. package/lib/panelview/leftpanel.js +5 -1
  62. package/lib/panelview/model.js +8 -8
  63. package/lib/panelview/objectproperties.js +10 -10
  64. package/lib/panelview/rightpanel.d.ts +1 -1
  65. package/lib/panelview/rightpanel.js +10 -10
  66. package/lib/toolbar/widget.d.ts +1 -1
  67. package/lib/toolbar/widget.js +44 -32
  68. package/lib/tools.d.ts +10 -6
  69. package/lib/tools.js +89 -15
  70. package/lib/types.d.ts +2 -0
  71. package/lib/widget.d.ts +29 -5
  72. package/lib/widget.js +41 -7
  73. package/package.json +4 -3
  74. package/style/base.css +10 -0
  75. package/style/symbologyDialog.css +7 -1
  76. package/style/temporalSlider.css +47 -0
@@ -1,4 +1,3 @@
1
- import { getSourceLayerNames } from '../../tools';
2
1
  import { LayerPropertiesForm } from './layerform';
3
2
  /**
4
3
  * The form to modify a vector layer.
@@ -6,29 +5,6 @@ import { LayerPropertiesForm } from './layerform';
6
5
  export class VectorLayerPropertiesForm extends LayerPropertiesForm {
7
6
  constructor(props) {
8
7
  super(props);
9
- this.sourceLayers = [];
10
- this.fetchSourceLayers(this.props.sourceData);
11
- // If there is a source form attached, we listen to its changes
12
- if (this.sourceFormChangedSignal) {
13
- this.sourceFormChangedSignal.connect((sender, sourceData) => {
14
- if (this.props.sourceType === 'VectorTileSource') {
15
- this.fetchSourceLayers(this.currentFormData, sourceData);
16
- }
17
- });
18
- }
19
- props.model.clientStateChanged.connect(() => {
20
- var _a;
21
- if (!((_a = props.model.localState) === null || _a === void 0 ? void 0 : _a.selected.value)) {
22
- return;
23
- }
24
- const l = this.props.model.getLayer(Object.keys(props.model.localState.selected.value)[0]);
25
- const source = this.props.model.getSource(l === null || l === void 0 ? void 0 : l.parameters.source);
26
- if (!source || source.type !== 'VectorTileSource') {
27
- return;
28
- }
29
- const sourceData = source.parameters;
30
- this.fetchSourceLayers(this.currentFormData, sourceData);
31
- });
32
8
  }
33
9
  onFormChange(e) {
34
10
  super.onFormChange(e);
@@ -40,7 +16,6 @@ export class VectorLayerPropertiesForm extends LayerPropertiesForm {
40
16
  if (!source || source.type !== 'VectorTileSource') {
41
17
  return;
42
18
  }
43
- this.fetchSourceLayers(this.currentFormData, source.parameters);
44
19
  }
45
20
  processSchema(data, schema, uiSchema) {
46
21
  this.removeFormEntry('color', data, schema, uiSchema);
@@ -49,39 +24,5 @@ export class VectorLayerPropertiesForm extends LayerPropertiesForm {
49
24
  if (!data) {
50
25
  return;
51
26
  }
52
- // Show a dropdown for available sourceLayers if available
53
- // And automatically select one
54
- if (this.sourceLayers.length !== 0) {
55
- if (!data.sourceLayer || !this.sourceLayers.includes(data.sourceLayer)) {
56
- data.sourceLayer = this.sourceLayers[0];
57
- }
58
- schema.properties.sourceLayer.enum = this.sourceLayers;
59
- }
60
- }
61
- async fetchSourceLayers(data, sourceData) {
62
- if (data && data.source) {
63
- this.currentSourceId = data.source;
64
- if (!sourceData) {
65
- const currentSource = this.props.model.getSource(data.source);
66
- if (!currentSource || currentSource.type !== 'VectorTileSource') {
67
- this.sourceLayers = [];
68
- this.forceUpdate();
69
- return;
70
- }
71
- sourceData = currentSource.parameters;
72
- }
73
- try {
74
- this.sourceLayers = await getSourceLayerNames(sourceData.url, sourceData.urlParameters);
75
- this.forceUpdate();
76
- }
77
- catch (e) {
78
- console.error(e);
79
- }
80
- }
81
- else {
82
- this.currentSourceId = '';
83
- this.sourceLayers = [];
84
- this.forceUpdate();
85
- }
86
27
  }
87
28
  }
@@ -0,0 +1,8 @@
1
+ import { IDict, IJGISFilterItem, IJupyterGISModel } from '@jupytergis/schema';
2
+ import React from 'react';
3
+ interface ITemporalSliderProps {
4
+ model: IJupyterGISModel;
5
+ filterStates: IDict<IJGISFilterItem | undefined>;
6
+ }
7
+ declare const TemporalSlider: ({ model, filterStates }: ITemporalSliderProps) => React.JSX.Element;
8
+ export default TemporalSlider;
@@ -0,0 +1,303 @@
1
+ import { faPause, faPlay } from '@fortawesome/free-solid-svg-icons';
2
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
3
+ import { Button, Slider } from '@jupyter/react-components';
4
+ import { format, isValid, parse } from 'date-fns';
5
+ import { daysInYear, millisecondsInDay, millisecondsInHour, millisecondsInMinute, millisecondsInSecond, millisecondsInWeek, minutesInMonth } from 'date-fns/constants';
6
+ import React, { useEffect, useRef, useState } from 'react';
7
+ import { useGetProperties } from '../dialogs/symbology/hooks/useGetProperties';
8
+ // List of common date formats to try
9
+ // TODO: Not even close to every valid format
10
+ const commonDateFormats = [
11
+ 'yyyy-MM-dd', // ISO format (e.g., 2023-10-05)
12
+ 'dd/MM/yyyy', // European format (e.g., 05/10/2023)
13
+ 'MM/dd/yyyy', // US format (e.g., 10/05/2023)
14
+ 'yyyyMMdd', // Compact format (e.g., 20231005)
15
+ 'dd-MM-yyyy', // European format with hyphens (e.g., 05-10-2023)
16
+ 'MM-dd-yyyy', // US format with hyphens (e.g., 10-05-2023)
17
+ 'yyyy/MM/dd', // ISO format with slashes (e.g., 2023/10/05)
18
+ 'dd.MM.yyyy', // European format with dots (e.g., 05.10.2023)
19
+ 'MM.dd.yyyy' // US format with dots (e.g., 10.05.2023)
20
+ ];
21
+ // Time steps in milliseconds
22
+ const stepMap = {
23
+ millisecond: 1,
24
+ second: millisecondsInSecond,
25
+ minute: millisecondsInMinute,
26
+ hour: millisecondsInHour,
27
+ day: millisecondsInDay,
28
+ week: millisecondsInWeek,
29
+ month: minutesInMonth * millisecondsInMinute,
30
+ year: millisecondsInDay * daysInYear
31
+ };
32
+ const TemporalSlider = ({ model, filterStates }) => {
33
+ const [layerId, setLayerId] = useState('');
34
+ const [selectedFeature, setSelectedFeature] = useState('');
35
+ const [range, setRange] = useState({ start: 0, end: 1 }); // min/max of current range being displayed
36
+ const [minMax, setMinMax] = useState({ min: 0, max: 1 }); // min/max of data values
37
+ const [validFeatures, setValidFeatures] = useState([]);
38
+ const [dateFormat, setDateFormat] = useState('yyyy-MM-dd');
39
+ const [step, setStep] = useState(stepMap.year);
40
+ const [currentValue, setCurrentValue] = useState(0);
41
+ const [fps, setFps] = useState(1);
42
+ const [validSteps, setValidSteps] = useState({});
43
+ const layerIdRef = useRef('');
44
+ const intervalRef = useRef(null);
45
+ const { featureProperties } = useGetProperties({ layerId, model });
46
+ useEffect(() => {
47
+ // This is for when the selected layer changes
48
+ const handleClientStateChanged = () => {
49
+ var _a, _b;
50
+ if (!((_b = (_a = model.localState) === null || _a === void 0 ? void 0 : _a.selected) === null || _b === void 0 ? void 0 : _b.value)) {
51
+ return;
52
+ }
53
+ const selectedLayerId = Object.keys(model.localState.selected.value)[0];
54
+ // reset
55
+ if (selectedLayerId !== layerIdRef.current) {
56
+ setLayerId(selectedLayerId);
57
+ setDateFormat('yyyy-MM-dd');
58
+ setFps(1);
59
+ }
60
+ };
61
+ // this is for when the layer itself changes
62
+ const handleLayerChange = (_, change) => {
63
+ var _a;
64
+ // Get the changes for the selected layer
65
+ const selectedLayer = (_a = change.layerChange) === null || _a === void 0 ? void 0 : _a.find(layer => layer.id === layerIdRef.current);
66
+ // Bail if there's no relevant change
67
+ if (!(selectedLayer === null || selectedLayer === void 0 ? void 0 : selectedLayer.newValue)) {
68
+ return;
69
+ }
70
+ const { newValue, oldValue } = selectedLayer;
71
+ // If layer was deleted (empty object) or the layer type changed, close the temporal controller
72
+ if (Object.keys(newValue).length === 0 ||
73
+ newValue.type !== oldValue.type) {
74
+ model.toggleTemporalController();
75
+ }
76
+ };
77
+ // Initial state
78
+ handleClientStateChanged();
79
+ model.clientStateChanged.connect(handleClientStateChanged);
80
+ model.sharedLayersChanged.connect(handleLayerChange);
81
+ return () => {
82
+ model.clientStateChanged.disconnect(handleClientStateChanged);
83
+ model.sharedLayersChanged.disconnect(handleLayerChange);
84
+ removeFilter();
85
+ if (intervalRef.current) {
86
+ clearInterval(intervalRef.current);
87
+ }
88
+ };
89
+ }, []);
90
+ useEffect(() => {
91
+ layerIdRef.current = layerId;
92
+ }, [layerId]);
93
+ useEffect(() => {
94
+ const results = [];
95
+ for (const [key, set] of Object.entries(featureProperties)) {
96
+ if (set.size === 0) {
97
+ continue;
98
+ }
99
+ const sampleValue = set.values().next().value;
100
+ // Validate value type
101
+ const isString = typeof sampleValue === 'string';
102
+ const isInteger = Number.isInteger(sampleValue);
103
+ if (!isString && !isInteger) {
104
+ continue;
105
+ }
106
+ // Date validation
107
+ if (isString) {
108
+ const dateFormatFromString = determineDateFormat(sampleValue);
109
+ if (!dateFormatFromString) {
110
+ continue;
111
+ }
112
+ setDateFormat(dateFormatFromString);
113
+ }
114
+ results.push(key);
115
+ }
116
+ // if we have state then remove the ms from the converted feature name
117
+ const currentState = filterStates[layerId];
118
+ const currentFeature = currentState === null || currentState === void 0 ? void 0 : currentState.feature.slice(0, -2);
119
+ setValidFeatures(results);
120
+ setSelectedFeature(currentFeature !== null && currentFeature !== void 0 ? currentFeature : results[0]);
121
+ }, [featureProperties]);
122
+ useEffect(() => {
123
+ var _a, _b, _c;
124
+ if (!selectedFeature || !featureProperties[selectedFeature]) {
125
+ return;
126
+ }
127
+ // Get and validate values
128
+ const valueSet = featureProperties[selectedFeature];
129
+ if (valueSet.size === 0) {
130
+ return;
131
+ }
132
+ const values = Array.from(valueSet);
133
+ const [firstValue] = values;
134
+ // Check the type of the first element
135
+ const isString = typeof firstValue === 'string';
136
+ let convertedValues;
137
+ if (isString) {
138
+ convertedValues = values.map(value => Date.parse(value)); // Convert all strings to milliseconds
139
+ }
140
+ else {
141
+ convertedValues = values; // Keep numbers as they are
142
+ }
143
+ // Calculate min and max
144
+ const min = Math.min(...convertedValues);
145
+ const max = Math.max(...convertedValues);
146
+ // Get valid step options
147
+ const filteredSteps = Object.fromEntries(Object.entries(stepMap).filter(([_, val]) => val < max - min));
148
+ //using filter item as a state object to restore prev values
149
+ const currentState = filterStates[layerId];
150
+ const step = (_a = Object.values(filteredSteps).slice(-1)[0]) !== null && _a !== void 0 ? _a : stepMap.millisecond;
151
+ setValidSteps(filteredSteps);
152
+ setStep(step);
153
+ setMinMax({ min, max });
154
+ setRange({
155
+ start: (_b = currentState === null || currentState === void 0 ? void 0 : currentState.betweenMin) !== null && _b !== void 0 ? _b : min,
156
+ end: (_c = currentState === null || currentState === void 0 ? void 0 : currentState.betweenMax) !== null && _c !== void 0 ? _c : min + step
157
+ });
158
+ model.addFeatureAsMs(layerId, selectedFeature);
159
+ }, [selectedFeature]);
160
+ // minMax needs to be set before current value so the slider displays correctly
161
+ useEffect(() => {
162
+ const currentState = filterStates[layerId];
163
+ setCurrentValue(typeof (currentState === null || currentState === void 0 ? void 0 : currentState.value) === 'number' ? currentState.value : minMax.min);
164
+ }, [minMax]);
165
+ // Guess the date format from a date string
166
+ const determineDateFormat = (dateString) => {
167
+ for (const format of commonDateFormats) {
168
+ const parsedDate = parse(dateString, format, new Date());
169
+ if (isValid(parsedDate)) {
170
+ return format; // Return the format if the date is valid
171
+ }
172
+ }
173
+ return null; // Return null if no format matches
174
+ };
175
+ // Convert milliseconds back to the original date string format
176
+ const millisecondsToDateString = (milliseconds, dateFormat) => {
177
+ const date = new Date(milliseconds); // Create a Date object from milliseconds
178
+ return format(date, dateFormat); // Format back to the original string format
179
+ };
180
+ const handleChange = (e) => {
181
+ setCurrentValue(+e.target.value);
182
+ setRange({ start: +e.target.value, end: +e.target.value + step });
183
+ applyFilter(+e.target.value);
184
+ };
185
+ const applyFilter = (value) => {
186
+ var _a, _b;
187
+ const newFilter = {
188
+ feature: `${selectedFeature}ms`,
189
+ operator: 'between',
190
+ value: value,
191
+ betweenMin: value,
192
+ betweenMax: value + step
193
+ };
194
+ const layer = model.getLayer(layerId);
195
+ if (!layer) {
196
+ return;
197
+ }
198
+ const appliedFilters = ((_a = layer.filters) === null || _a === void 0 ? void 0 : _a.appliedFilters) || [];
199
+ const logicalOp = ((_b = layer.filters) === null || _b === void 0 ? void 0 : _b.logicalOp) || 'all';
200
+ // This is the only way to add a 'between' filter so
201
+ // find the index of the existing 'between' filter
202
+ const betweenFilterIndex = appliedFilters.findIndex(filter => filter.operator === 'between');
203
+ if (betweenFilterIndex !== -1) {
204
+ // If found, replace the existing filter
205
+ appliedFilters[betweenFilterIndex] = Object.assign({}, newFilter);
206
+ }
207
+ else {
208
+ // If not found, add the new filter
209
+ appliedFilters.push(newFilter);
210
+ }
211
+ // Apply the updated filters to the layer
212
+ layer.filters = { logicalOp, appliedFilters };
213
+ model.triggerLayerUpdate(layerId, layer);
214
+ };
215
+ const removeFilter = () => {
216
+ var _a, _b;
217
+ const layer = model.getLayer(layerIdRef.current);
218
+ if (!layer) {
219
+ return;
220
+ }
221
+ const appliedFilters = ((_a = layer.filters) === null || _a === void 0 ? void 0 : _a.appliedFilters) || [];
222
+ const logicalOp = ((_b = layer.filters) === null || _b === void 0 ? void 0 : _b.logicalOp) || 'all';
223
+ // This is the only way to add a 'between' filter so
224
+ // find the index of the existing 'between' filter
225
+ const betweenFilterIndex = appliedFilters.findIndex(filter => filter.operator === 'between');
226
+ if (betweenFilterIndex !== -1) {
227
+ // If found, replace the existing filter
228
+ appliedFilters.splice(betweenFilterIndex, 1);
229
+ }
230
+ // Apply the updated filters to the layer
231
+ layer.filters = { logicalOp, appliedFilters };
232
+ model.triggerLayerUpdate(layerIdRef.current, layer);
233
+ };
234
+ const playAnimation = () => {
235
+ // Clear any existing interval first
236
+ if (intervalRef.current) {
237
+ clearInterval(intervalRef.current);
238
+ }
239
+ const incrementValue = () => {
240
+ setCurrentValue(prev => {
241
+ // Calculate next value with safety bounds
242
+ const nextValue = prev + step;
243
+ // Clear interval if we've reached the max
244
+ // step is subtracted to keep range values correct
245
+ if (nextValue >= minMax.max - step && intervalRef.current) {
246
+ clearInterval(intervalRef.current);
247
+ return minMax.max - step;
248
+ }
249
+ return nextValue;
250
+ });
251
+ };
252
+ // Start animation
253
+ intervalRef.current = setInterval(incrementValue, 1000 / fps);
254
+ };
255
+ const pauseAnimation = () => {
256
+ if (intervalRef.current) {
257
+ clearInterval(intervalRef.current);
258
+ }
259
+ };
260
+ return (React.createElement("div", { className: "jp-gis-temporal-slider-container" },
261
+ React.createElement("div", { className: "jp-gis-temporal-slider-row" },
262
+ React.createElement("div", null,
263
+ React.createElement("label", { htmlFor: "time-feature-select" }, "Feature: "),
264
+ React.createElement("select", { id: "time-feature-select", onChange: e => {
265
+ setSelectedFeature(e.target.value);
266
+ } }, validFeatures.map(feature => {
267
+ return (React.createElement("option", { value: feature, selected: selectedFeature === feature }, feature));
268
+ }))),
269
+ React.createElement("div", null,
270
+ React.createElement("span", null, "Current Frame:"),
271
+ ' ',
272
+ millisecondsToDateString(range.start, dateFormat),
273
+ " \u2264 ",
274
+ React.createElement("span", null, "t"),
275
+ " \u2264",
276
+ ' ',
277
+ millisecondsToDateString(range.end, dateFormat))),
278
+ React.createElement("div", { className: "jp-gis-temporal-slider-row" },
279
+ React.createElement("div", { className: "jp-gis-temporal-slider-controls" },
280
+ React.createElement("div", { className: "jp-gis-temporal-slider-sub-controls" },
281
+ React.createElement(Button, { appearance: "neutral", scale: "medium", onClick: pauseAnimation },
282
+ React.createElement(FontAwesomeIcon, { icon: faPause })),
283
+ React.createElement(Button, { appearance: "neutral", scale: "medium", onClick: playAnimation },
284
+ React.createElement(FontAwesomeIcon, { icon: faPlay }))),
285
+ React.createElement("div", { className: "jp-gis-temporal-slider-sub-controls", style: { minWidth: 0 } },
286
+ React.createElement("label", { htmlFor: "fps-number-input" }, "FPS:"),
287
+ React.createElement("input", { name: "fps-number-input", type: "number", value: fps, onChange: e => setFps(+e.target.value) }))),
288
+ React.createElement("div", null,
289
+ React.createElement(Slider, { min: minMax.min, max: minMax.max - step, value: currentValue, valueAsNumber: currentValue, step: step, onChange: handleChange, className: "jp-gis-temporal-slider" }))),
290
+ React.createElement("div", { className: "jp-gis-temporal-slider-row" },
291
+ React.createElement("div", null,
292
+ React.createElement("span", null, "Range: "),
293
+ millisecondsToDateString(minMax.min, dateFormat),
294
+ " ",
295
+ React.createElement("span", null, "to "),
296
+ millisecondsToDateString(minMax.max, dateFormat)),
297
+ React.createElement("div", null,
298
+ React.createElement("label", { htmlFor: "time-step-select" }, "Step: "),
299
+ React.createElement("select", { id: "time-step-select", onChange: e => {
300
+ setStep(+e.target.value);
301
+ } }, Object.entries(validSteps).map(([key, val]) => (React.createElement("option", { key: key, selected: val === step, value: val }, key))))))));
302
+ };
303
+ export default TemporalSlider;
@@ -1,4 +1,4 @@
1
- import { IAnnotation, IDict, IJGISLayer, IJGISSource } from '@jupytergis/schema';
1
+ import { IAnnotation, IDict, IJGISFilterItem, IJGISLayer, IJGISSource } from '@jupytergis/schema';
2
2
  import { User } from '@jupyterlab/services';
3
3
  import { Layer } from 'ol/layer';
4
4
  import * as React from 'react';
@@ -21,6 +21,13 @@ interface IStates {
21
21
  };
22
22
  loadingLayer: boolean;
23
23
  scale: number;
24
+ loadingErrors: Array<{
25
+ id: string;
26
+ error: any;
27
+ index: number;
28
+ }>;
29
+ displayTemporalController: boolean;
30
+ filterStates: IDict<IJGISFilterItem | undefined>;
24
31
  }
25
32
  export declare class MainView extends React.Component<IProps, IStates> {
26
33
  constructor(props: IProps);
@@ -29,7 +36,6 @@ export declare class MainView extends React.Component<IProps, IStates> {
29
36
  generateScene(): Promise<void>;
30
37
  createSelectInteraction: () => void;
31
38
  addContextMenu: () => void;
32
- private _loadGeoTIFFWithCache;
33
39
  /**
34
40
  * Add a source in the map.
35
41
  *
@@ -97,7 +103,13 @@ export declare class MainView extends React.Component<IProps, IStates> {
97
103
  * @param id - id of the layer.
98
104
  * @param layer - the layer object.
99
105
  */
100
- updateLayer(id: string, layer: IJGISLayer, mapLayer: Layer): Promise<void>;
106
+ updateLayer(id: string, layer: IJGISLayer, mapLayer: Layer, oldLayer?: IDict): Promise<void>;
107
+ /**
108
+ * Heatmap layers don't work with style based filtering.
109
+ * This modifies the features in the underlying source
110
+ * to work with the temporal controller
111
+ */
112
+ handleTemporalController: (id: string, layer: IJGISLayer) => void;
101
113
  /**
102
114
  * Wait for all layers to be loaded.
103
115
  */
@@ -138,6 +150,12 @@ export declare class MainView extends React.Component<IProps, IStates> {
138
150
  * @param index - expected index of the layer.
139
151
  */
140
152
  moveLayer(id: string, index: number): void;
153
+ /**
154
+ * Remove and recreate layer
155
+ * @param id ID of layer being replaced
156
+ * @param layer New layer to replace with
157
+ */
158
+ replaceLayer(id: string, layer: IJGISLayer): void;
141
159
  private _onLayersChanged;
142
160
  private _onLayerTreeChange;
143
161
  private _onSourcesChange;
@@ -150,12 +168,14 @@ export declare class MainView extends React.Component<IProps, IStates> {
150
168
  private _onPointerMove;
151
169
  private _syncPointer;
152
170
  private _identifyFeature;
171
+ private _triggerLayerUpdate;
172
+ private _convertFeatureToMs;
153
173
  private _handleThemeChange;
154
174
  private _handleWindowResize;
155
175
  render(): JSX.Element;
156
176
  private _clickCoords;
157
177
  private _commands;
158
- private _initializedPosition;
178
+ private _isPositionInitialized;
159
179
  private divRef;
160
180
  private _Map;
161
181
  private _model;
@@ -166,5 +186,6 @@ export declare class MainView extends React.Component<IProps, IStates> {
166
186
  private _documentPath?;
167
187
  private _contextMenu;
168
188
  private _loadingLayers;
189
+ private _originalFeatures;
169
190
  }
170
191
  export {};