@globalfishingwatch/skills 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -0
- package/dist/decode-url/SKILL.md +51 -0
- package/dist/decode-url/index.js +20339 -0
- package/dist/decode-url/scripts/decode-url.mjs +18 -0
- package/dist/decode-url/scripts/register-gfw-resolver.mjs +24 -0
- package/dist/encode-url/MAINTENANCE.md +53 -0
- package/dist/encode-url/SKILL.md +135 -0
- package/dist/encode-url/index.js +20211 -0
- package/dist/encode-url/references/areas.json +1401 -0
- package/dist/encode-url/references/examples-conversations.md +113 -0
- package/dist/encode-url/references/examples.md +234 -0
- package/dist/encode-url/references/filters.md +67 -0
- package/dist/encode-url/references/highlighted-workspaces.md +65 -0
- package/dist/encode-url/references/layers.md +96 -0
- package/dist/encode-url/references/ports.json +27507 -0
- package/dist/encode-url/references/query-params.md +114 -0
- package/dist/encode-url/references/routes.md +24 -0
- package/dist/encode-url/scripts/encode-url.mjs +26 -0
- package/dist/encode-url/scripts/register-gfw-resolver.mjs +24 -0
- package/dist/index.js +20514 -0
- package/dist/package.json +35 -0
- package/package.json +35 -0
- package/src/decode-url/SKILL.md +51 -0
- package/src/decode-url/decode.ts +87 -0
- package/src/decode-url/index.ts +4 -0
- package/src/decode-url/scripts/decode-url.mjs +18 -0
- package/src/decode-url/scripts/register-gfw-resolver.mjs +24 -0
- package/src/decode-url/workspaces.ts +34 -0
- package/src/encode-url/MAINTENANCE.md +53 -0
- package/src/encode-url/SKILL.md +135 -0
- package/src/encode-url/config.ts +8 -0
- package/src/encode-url/dictionary.ts +305 -0
- package/src/encode-url/encode.ts +79 -0
- package/src/encode-url/index.ts +4 -0
- package/src/encode-url/references/areas.json +1401 -0
- package/src/encode-url/references/examples-conversations.md +113 -0
- package/src/encode-url/references/examples.md +234 -0
- package/src/encode-url/references/filters.md +67 -0
- package/src/encode-url/references/highlighted-workspaces.md +65 -0
- package/src/encode-url/references/layers.md +96 -0
- package/src/encode-url/references/ports.json +27507 -0
- package/src/encode-url/references/query-params.md +114 -0
- package/src/encode-url/references/routes.md +24 -0
- package/src/encode-url/routes.ts +166 -0
- package/src/encode-url/scripts/encode-url.mjs +26 -0
- package/src/encode-url/scripts/register-gfw-resolver.mjs +24 -0
- package/src/index.ts +6 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import * as WORKSPACE_CONFIG from '@fishing-map/config'
|
|
2
|
+
|
|
3
|
+
import { VESSEL_DATAVIEW_INSTANCE_PREFIX } from '@globalfishingwatch/dataviews-client'
|
|
4
|
+
|
|
5
|
+
export type LayerCategory = 'activity' | 'detections' | 'events' | 'environment' | 'context'
|
|
6
|
+
|
|
7
|
+
export type LayerInfo = {
|
|
8
|
+
name: string
|
|
9
|
+
category: LayerCategory
|
|
10
|
+
/** Backend dataview slug, filled automatically by the encoder for layer-library instances */
|
|
11
|
+
dataviewId?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Dataview instance ids used by the fishing-map default workspace and layer library.
|
|
16
|
+
* Ids and slugs come from @fishing-map/config (single source of truth with the app)
|
|
17
|
+
*/
|
|
18
|
+
export const LAYERS_DICTIONARY: Record<string, LayerInfo> = {
|
|
19
|
+
// Activity
|
|
20
|
+
[WORKSPACE_CONFIG.AIS_DATAVIEW_INSTANCE_ID]: {
|
|
21
|
+
name: 'Apparent fishing effort (AIS)',
|
|
22
|
+
category: 'activity',
|
|
23
|
+
dataviewId: WORKSPACE_CONFIG.FISHING_DATAVIEW_SLUG_AIS,
|
|
24
|
+
},
|
|
25
|
+
'fishing-effort-ais': {
|
|
26
|
+
name: 'Apparent fishing effort (AIS)',
|
|
27
|
+
category: 'activity',
|
|
28
|
+
dataviewId: WORKSPACE_CONFIG.FISHING_DATAVIEW_SLUG_AIS,
|
|
29
|
+
},
|
|
30
|
+
[WORKSPACE_CONFIG.VMS_DATAVIEW_INSTANCE_ID]: {
|
|
31
|
+
name: 'Apparent fishing effort (VMS)',
|
|
32
|
+
category: 'activity',
|
|
33
|
+
dataviewId: WORKSPACE_CONFIG.FISHING_DATAVIEW_SLUG_VMS,
|
|
34
|
+
},
|
|
35
|
+
'fishing-effort-vms': {
|
|
36
|
+
name: 'Apparent fishing effort (VMS)',
|
|
37
|
+
category: 'activity',
|
|
38
|
+
dataviewId: WORKSPACE_CONFIG.FISHING_DATAVIEW_SLUG_VMS,
|
|
39
|
+
},
|
|
40
|
+
[WORKSPACE_CONFIG.PRESENCE_DATAVIEW_INSTANCE_ID]: {
|
|
41
|
+
name: 'Vessel presence (AIS)',
|
|
42
|
+
category: 'activity',
|
|
43
|
+
dataviewId: WORKSPACE_CONFIG.PRESENCE_DATAVIEW_SLUG,
|
|
44
|
+
},
|
|
45
|
+
// Detections
|
|
46
|
+
[WORKSPACE_CONFIG.SAR_DATAVIEW_INSTANCE_ID]: {
|
|
47
|
+
name: 'Vessel detections (SAR)',
|
|
48
|
+
category: 'detections',
|
|
49
|
+
dataviewId: WORKSPACE_CONFIG.SAR_DATAVIEW_SLUG,
|
|
50
|
+
},
|
|
51
|
+
[WORKSPACE_CONFIG.SENTINEL2_DATAVIEW_INSTANCE_ID]: {
|
|
52
|
+
name: 'Vessel detections (Sentinel-2 optical)',
|
|
53
|
+
category: 'detections',
|
|
54
|
+
dataviewId: WORKSPACE_CONFIG.SENTINEL2_DATAVIEW_SLUG,
|
|
55
|
+
},
|
|
56
|
+
[WORKSPACE_CONFIG.VIIRS_DATAVIEW_INSTANCE_ID]: {
|
|
57
|
+
name: 'Night light detections (VIIRS EOG)',
|
|
58
|
+
category: 'detections',
|
|
59
|
+
dataviewId: WORKSPACE_CONFIG.VIIRS_MATCH_DATAVIEW_SLUG,
|
|
60
|
+
},
|
|
61
|
+
[WORKSPACE_CONFIG.VIIRS_SKYLIGHT_DATAVIEW_INSTANCE_ID]: {
|
|
62
|
+
name: 'Night light detections (VIIRS Skylight)',
|
|
63
|
+
category: 'detections',
|
|
64
|
+
dataviewId: WORKSPACE_CONFIG.VIIRS_MATCH_SKYLIGHT_DATAVIEW_SLUG,
|
|
65
|
+
},
|
|
66
|
+
// Events
|
|
67
|
+
[WORKSPACE_CONFIG.ENCOUNTER_EVENTS_SOURCE_ID]: {
|
|
68
|
+
name: 'Encounter events',
|
|
69
|
+
category: 'events',
|
|
70
|
+
dataviewId: WORKSPACE_CONFIG.CLUSTER_ENCOUNTER_EVENTS_DATAVIEW_SLUG,
|
|
71
|
+
},
|
|
72
|
+
[WORKSPACE_CONFIG.LOITERING_EVENTS_SOURCE_ID]: {
|
|
73
|
+
name: 'Loitering events',
|
|
74
|
+
category: 'events',
|
|
75
|
+
dataviewId: WORKSPACE_CONFIG.CLUSTER_LOITERING_EVENTS_DATAVIEW_SLUG,
|
|
76
|
+
},
|
|
77
|
+
[WORKSPACE_CONFIG.PORT_VISITS_EVENTS_SOURCE_ID]: {
|
|
78
|
+
name: 'Port visit events',
|
|
79
|
+
category: 'events',
|
|
80
|
+
dataviewId: WORKSPACE_CONFIG.CLUSTER_PORT_VISIT_EVENTS_DATAVIEW_SLUG,
|
|
81
|
+
},
|
|
82
|
+
[WORKSPACE_CONFIG.GAPS_EVENTS_SOURCE_ID]: {
|
|
83
|
+
name: 'AIS off events (gaps)',
|
|
84
|
+
category: 'events',
|
|
85
|
+
dataviewId: WORKSPACE_CONFIG.CLUSTER_GAPS_EVENTS_DATAVIEW_SLUG,
|
|
86
|
+
},
|
|
87
|
+
// Context
|
|
88
|
+
basemap: { name: 'Basemap', category: 'context' },
|
|
89
|
+
'basemap-labels': { name: 'Basemap labels', category: 'context' },
|
|
90
|
+
eez: {
|
|
91
|
+
name: 'EEZs (Exclusive Economic Zones)',
|
|
92
|
+
category: 'context',
|
|
93
|
+
dataviewId: WORKSPACE_CONFIG.EEZ_DATAVIEW_SLUG,
|
|
94
|
+
},
|
|
95
|
+
mpa: {
|
|
96
|
+
name: 'MPAs (Marine Protected Areas)',
|
|
97
|
+
category: 'context',
|
|
98
|
+
dataviewId: WORKSPACE_CONFIG.MPA_DATAVIEW_SLUG,
|
|
99
|
+
},
|
|
100
|
+
mpatlas: {
|
|
101
|
+
name: 'MPAtlas protection level',
|
|
102
|
+
category: 'context',
|
|
103
|
+
dataviewId: WORKSPACE_CONFIG.MPATLAS_DATAVIEW_SLUG,
|
|
104
|
+
},
|
|
105
|
+
protectedseas: {
|
|
106
|
+
name: 'ProtectedSeas regulations',
|
|
107
|
+
category: 'context',
|
|
108
|
+
dataviewId: WORKSPACE_CONFIG.PROTECTED_SEAS_DATAVIEW_SLUG,
|
|
109
|
+
},
|
|
110
|
+
rfmo: {
|
|
111
|
+
name: 'RFMOs (Tuna Regional Fisheries Management Organizations)',
|
|
112
|
+
category: 'context',
|
|
113
|
+
dataviewId: WORKSPACE_CONFIG.RFMO_DATAVIEW_SLUG,
|
|
114
|
+
},
|
|
115
|
+
'fao-areas': {
|
|
116
|
+
name: 'FAO major fishing areas',
|
|
117
|
+
category: 'context',
|
|
118
|
+
dataviewId: WORKSPACE_CONFIG.FAO_AREAS_DATAVIEW_SLUG,
|
|
119
|
+
},
|
|
120
|
+
'fao-major': {
|
|
121
|
+
name: 'FAO major fishing areas',
|
|
122
|
+
category: 'context',
|
|
123
|
+
dataviewId: WORKSPACE_CONFIG.FAO_AREAS_DATAVIEW_SLUG,
|
|
124
|
+
},
|
|
125
|
+
graticules: {
|
|
126
|
+
name: 'Latitude/longitude grids',
|
|
127
|
+
category: 'context',
|
|
128
|
+
dataviewId: WORKSPACE_CONFIG.GRATICULES_DATAVIEW_SLUG,
|
|
129
|
+
},
|
|
130
|
+
'high-seas': {
|
|
131
|
+
name: 'High seas',
|
|
132
|
+
category: 'context',
|
|
133
|
+
dataviewId: WORKSPACE_CONFIG.HIGH_SEAS_DATAVIEW_SLUG,
|
|
134
|
+
},
|
|
135
|
+
'high-seas-pockets': {
|
|
136
|
+
name: 'High seas pockets',
|
|
137
|
+
category: 'context',
|
|
138
|
+
dataviewId: WORKSPACE_CONFIG.HIGH_SEAS_POCKETS_DATAVIEW_SLUG,
|
|
139
|
+
},
|
|
140
|
+
'eez-areas-12nm': {
|
|
141
|
+
name: '12 nautical miles zones',
|
|
142
|
+
category: 'context',
|
|
143
|
+
dataviewId: WORKSPACE_CONFIG.EEZ_AREAS_12NM_DATAVIEW_SLUG,
|
|
144
|
+
},
|
|
145
|
+
'offshore-fixed-infrastructure': {
|
|
146
|
+
name: 'Offshore fixed infrastructure',
|
|
147
|
+
category: 'context',
|
|
148
|
+
dataviewId: WORKSPACE_CONFIG.FIXED_SAR_INFRASTRUCTURE,
|
|
149
|
+
},
|
|
150
|
+
'port-locations': {
|
|
151
|
+
name: 'Port locations (AIS)',
|
|
152
|
+
category: 'context',
|
|
153
|
+
dataviewId: WORKSPACE_CONFIG.PORTS_AIS_DATAVIEW_SLUG,
|
|
154
|
+
},
|
|
155
|
+
'port-locations-vms': {
|
|
156
|
+
name: 'Port locations (VMS)',
|
|
157
|
+
category: 'context',
|
|
158
|
+
dataviewId: WORKSPACE_CONFIG.PORTS_VMS_DATAVIEW_SLUG,
|
|
159
|
+
},
|
|
160
|
+
'paa-duke': {
|
|
161
|
+
name: 'Preferential access areas (Duke)',
|
|
162
|
+
category: 'context',
|
|
163
|
+
dataviewId: WORKSPACE_CONFIG.PAA_DUKE_DATAVIEW_SLUG,
|
|
164
|
+
},
|
|
165
|
+
'gfcm-fao': {
|
|
166
|
+
name: 'GFCM FAO areas',
|
|
167
|
+
category: 'context',
|
|
168
|
+
dataviewId: WORKSPACE_CONFIG.GFCM_FAO_DATAVIEW_SLUG,
|
|
169
|
+
},
|
|
170
|
+
'dsm-isa-leasing-areas': {
|
|
171
|
+
name: 'Deep sea mining ISA leasing areas',
|
|
172
|
+
category: 'context',
|
|
173
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_CONTEXT_DATAVIEW_SLUG,
|
|
174
|
+
},
|
|
175
|
+
immas: {
|
|
176
|
+
name: 'Important Marine Mammal Areas',
|
|
177
|
+
category: 'context',
|
|
178
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_CONTEXT_DATAVIEW_SLUG,
|
|
179
|
+
},
|
|
180
|
+
ebsas: {
|
|
181
|
+
name: 'Ecologically or Biologically Significant Areas',
|
|
182
|
+
category: 'context',
|
|
183
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_CONTEXT_DATAVIEW_SLUG,
|
|
184
|
+
},
|
|
185
|
+
// Environment
|
|
186
|
+
[WORKSPACE_CONFIG.BATHYMETRY_DATAVIEW_PREFIX]: {
|
|
187
|
+
name: 'Bathymetry',
|
|
188
|
+
category: 'environment',
|
|
189
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_HEATMAP_STATIC_DATAVIEW_SLUG,
|
|
190
|
+
},
|
|
191
|
+
currents: {
|
|
192
|
+
name: 'Currents',
|
|
193
|
+
category: 'environment',
|
|
194
|
+
dataviewId: WORKSPACE_CONFIG.CURRENTS_DATAVIEW_SLUG,
|
|
195
|
+
},
|
|
196
|
+
winds: {
|
|
197
|
+
name: 'Winds',
|
|
198
|
+
category: 'environment',
|
|
199
|
+
dataviewId: WORKSPACE_CONFIG.WINDS_DATAVIEW_SLUG,
|
|
200
|
+
},
|
|
201
|
+
chlorophyl: {
|
|
202
|
+
name: 'Chlorophyll-a concentration',
|
|
203
|
+
category: 'environment',
|
|
204
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_HEATMAP_ENVIRONMENT_DATAVIEW_SLUG,
|
|
205
|
+
},
|
|
206
|
+
nitrate: {
|
|
207
|
+
name: 'Nitrate concentration',
|
|
208
|
+
category: 'environment',
|
|
209
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_HEATMAP_ENVIRONMENT_DATAVIEW_SLUG,
|
|
210
|
+
},
|
|
211
|
+
oxygen: {
|
|
212
|
+
name: 'Dissolved oxygen',
|
|
213
|
+
category: 'environment',
|
|
214
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_HEATMAP_ENVIRONMENT_DATAVIEW_SLUG,
|
|
215
|
+
},
|
|
216
|
+
ph: {
|
|
217
|
+
name: 'pH',
|
|
218
|
+
category: 'environment',
|
|
219
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_HEATMAP_ENVIRONMENT_DATAVIEW_SLUG,
|
|
220
|
+
},
|
|
221
|
+
phosphate: {
|
|
222
|
+
name: 'Phosphate concentration',
|
|
223
|
+
category: 'environment',
|
|
224
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_HEATMAP_ENVIRONMENT_DATAVIEW_SLUG,
|
|
225
|
+
},
|
|
226
|
+
salinity: {
|
|
227
|
+
name: 'Salinity',
|
|
228
|
+
category: 'environment',
|
|
229
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_HEATMAP_ENVIRONMENT_DATAVIEW_SLUG,
|
|
230
|
+
},
|
|
231
|
+
sst: {
|
|
232
|
+
name: 'Sea surface temperature',
|
|
233
|
+
category: 'environment',
|
|
234
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_HEATMAP_ENVIRONMENT_DATAVIEW_SLUG,
|
|
235
|
+
},
|
|
236
|
+
'sst-anomalies': {
|
|
237
|
+
name: 'Sea surface temperature anomalies',
|
|
238
|
+
category: 'environment',
|
|
239
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_HEATMAP_ENVIRONMENT_DATAVIEW_SLUG,
|
|
240
|
+
},
|
|
241
|
+
'sst-anomalies-min': {
|
|
242
|
+
name: 'Sea surface temperature anomalies (min)',
|
|
243
|
+
category: 'environment',
|
|
244
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_HEATMAP_ENVIRONMENT_DATAVIEW_SLUG,
|
|
245
|
+
},
|
|
246
|
+
'sst-anomalies-max': {
|
|
247
|
+
name: 'Sea surface temperature anomalies (max)',
|
|
248
|
+
category: 'environment',
|
|
249
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_HEATMAP_ENVIRONMENT_DATAVIEW_SLUG,
|
|
250
|
+
},
|
|
251
|
+
thgt: {
|
|
252
|
+
name: 'Wave height',
|
|
253
|
+
category: 'environment',
|
|
254
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_HEATMAP_ENVIRONMENT_DATAVIEW_SLUG,
|
|
255
|
+
},
|
|
256
|
+
'marine-ecoregions': {
|
|
257
|
+
name: 'Marine ecoregions',
|
|
258
|
+
category: 'environment',
|
|
259
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_GFW_ENVIRONMENT_DATAVIEW_SLUG,
|
|
260
|
+
},
|
|
261
|
+
mangroves: {
|
|
262
|
+
name: 'Mangroves',
|
|
263
|
+
category: 'environment',
|
|
264
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_GFW_ENVIRONMENT_DATAVIEW_SLUG,
|
|
265
|
+
},
|
|
266
|
+
seamounts: {
|
|
267
|
+
name: 'Seamounts',
|
|
268
|
+
category: 'environment',
|
|
269
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_GFW_ENVIRONMENT_DATAVIEW_SLUG,
|
|
270
|
+
},
|
|
271
|
+
'coral-reefs': {
|
|
272
|
+
name: 'Coral reefs',
|
|
273
|
+
category: 'environment',
|
|
274
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_GFW_ENVIRONMENT_DATAVIEW_SLUG,
|
|
275
|
+
},
|
|
276
|
+
seagrasses: {
|
|
277
|
+
name: 'Seagrasses',
|
|
278
|
+
category: 'environment',
|
|
279
|
+
dataviewId: WORKSPACE_CONFIG.TEMPLATE_GFW_ENVIRONMENT_DATAVIEW_SLUG,
|
|
280
|
+
},
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const LIBRARY_ID_SUFFIX_REGEX = new RegExp(`${WORKSPACE_CONFIG.LAYER_LIBRARY_ID_SEPARATOR}\\d+$`)
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Resolves a dataview instance id to layer info. Handles the app id conventions:
|
|
287
|
+
* `context-layer-` prefix, `__<timestamp>` suffix (layer-library additions) and
|
|
288
|
+
* `vessel-` prefix (vessel track layers)
|
|
289
|
+
*/
|
|
290
|
+
export const getLayerInfo = (instanceId: string): LayerInfo => {
|
|
291
|
+
const baseId = instanceId.replace(LIBRARY_ID_SUFFIX_REGEX, '')
|
|
292
|
+
const contextId = baseId.startsWith(WORKSPACE_CONFIG.CONTEXT_LAYER_INSTANCE_PREFIX)
|
|
293
|
+
? baseId.replace(WORKSPACE_CONFIG.CONTEXT_LAYER_INSTANCE_PREFIX, '')
|
|
294
|
+
: baseId
|
|
295
|
+
if (LAYERS_DICTIONARY[contextId]) {
|
|
296
|
+
return LAYERS_DICTIONARY[contextId]
|
|
297
|
+
}
|
|
298
|
+
if (baseId.startsWith(VESSEL_DATAVIEW_INSTANCE_PREFIX)) {
|
|
299
|
+
return {
|
|
300
|
+
name: `Vessel track (${baseId.replace(VESSEL_DATAVIEW_INSTANCE_PREFIX, '')})`,
|
|
301
|
+
category: 'activity',
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return { name: baseId, category: 'context' }
|
|
305
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { LAYER_LIBRARY_ID_SEPARATOR } from '@fishing-map/config'
|
|
2
|
+
|
|
3
|
+
import type { BaseUrlWorkspace } from '@globalfishingwatch/dataviews-client'
|
|
4
|
+
import { stringifyWorkspace } from '@globalfishingwatch/dataviews-client'
|
|
5
|
+
import { getDateInIntervalResolution } from '@globalfishingwatch/deck-layers'
|
|
6
|
+
import { getFourwingsInterval } from '@globalfishingwatch/deck-loaders'
|
|
7
|
+
|
|
8
|
+
import { resolveDataviewSlug } from './config'
|
|
9
|
+
import { getLayerInfo } from './dictionary'
|
|
10
|
+
import type { MapRoute, RouteNavigation } from './routes'
|
|
11
|
+
import { buildRoutePath, DEFAULT_BASENAME, getRouteNavigation } from './routes'
|
|
12
|
+
|
|
13
|
+
// Layer-library instances (id convention `<libraryId>__<unique>`) require a dataviewId;
|
|
14
|
+
// fill it from the dictionary when missing so agents don't have to know slugs
|
|
15
|
+
const withDataviewId = (instance: any) => {
|
|
16
|
+
if (instance?.dataviewId) {
|
|
17
|
+
return { ...instance, dataviewId: resolveDataviewSlug(String(instance.dataviewId)) }
|
|
18
|
+
}
|
|
19
|
+
if (instance?.id?.includes(LAYER_LIBRARY_ID_SEPARATOR)) {
|
|
20
|
+
const { dataviewId } = getLayerInfo(instance.id)
|
|
21
|
+
if (dataviewId) {
|
|
22
|
+
return { ...instance, dataviewId }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return instance
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Snaps start/end to the fourwings interval resolution the app will render with
|
|
29
|
+
// (month boundaries for ~year ranges, day for short ranges) — same functions the map uses
|
|
30
|
+
const withSnappedTimeRange = (state: MapState): MapState => {
|
|
31
|
+
const start = typeof state.start === 'string' ? Date.parse(state.start) : NaN
|
|
32
|
+
const end = typeof state.end === 'string' ? Date.parse(state.end) : NaN
|
|
33
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) {
|
|
34
|
+
return state
|
|
35
|
+
}
|
|
36
|
+
const interval = getFourwingsInterval(start, end)
|
|
37
|
+
return {
|
|
38
|
+
...state,
|
|
39
|
+
start: new Date(getDateInIntervalResolution(start, interval)).toISOString(),
|
|
40
|
+
end: new Date(getDateInIntervalResolution(end, interval)).toISOString(),
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type MapState = BaseUrlWorkspace & Record<string, unknown>
|
|
45
|
+
|
|
46
|
+
export type EncodeMapUrlInput = {
|
|
47
|
+
route: MapRoute
|
|
48
|
+
state?: MapState
|
|
49
|
+
/** App basename prepended to `path`, defaults to '/map' */
|
|
50
|
+
basename?: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type EncodeMapUrlResult = {
|
|
54
|
+
/** TanStack Router navigation config to navigate from inside the map app */
|
|
55
|
+
navigation: RouteNavigation & { search: MapState }
|
|
56
|
+
/** Path + encoded query. External navigation = prepend origin (https://globalfishingwatch.org) */
|
|
57
|
+
path: string
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const encodeMapUrl = ({
|
|
61
|
+
route,
|
|
62
|
+
state = {},
|
|
63
|
+
basename = DEFAULT_BASENAME,
|
|
64
|
+
}: EncodeMapUrlInput): EncodeMapUrlResult => {
|
|
65
|
+
const navigation = getRouteNavigation(route)
|
|
66
|
+
state = withSnappedTimeRange(state)
|
|
67
|
+
if (state.dataviewInstances) {
|
|
68
|
+
state = {
|
|
69
|
+
...state,
|
|
70
|
+
dataviewInstances: state.dataviewInstances.map(withDataviewId),
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const query = stringifyWorkspace(state)
|
|
74
|
+
const path = `${basename}${buildRoutePath(navigation)}${query ? `?${query}` : ''}`
|
|
75
|
+
return {
|
|
76
|
+
navigation: { ...navigation, search: state },
|
|
77
|
+
path,
|
|
78
|
+
}
|
|
79
|
+
}
|