@radiantearth/stac-migrate 2.0.2 → 2.1.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/LICENSE +201 -201
- package/README.md +81 -41
- package/bin/cli.js +20 -20
- package/migrate.js +247 -190
- package/package.json +10 -7
package/migrate.js
CHANGED
|
@@ -7,23 +7,27 @@ var compareVersions = require('compare-versions');
|
|
|
7
7
|
const LATEST_VERSION = '1.1.0';
|
|
8
8
|
const DONE = true; // This is used to verify in code coverage whether something has been used or not
|
|
9
9
|
const SCHEMAS = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
cf: 'https://stac-extensions.github.io/cf/v1.0.0/schema.json',
|
|
11
|
+
classification: 'https://stac-extensions.github.io/classification/v2.0.0/schema.json',
|
|
12
|
+
datacube: 'https://stac-extensions.github.io/datacube/v2.3.0/schema.json',
|
|
13
|
+
eo: 'https://stac-extensions.github.io/eo/v2.0.0/schema.json',
|
|
14
|
+
file: 'https://stac-extensions.github.io/file/v2.1.0/schema.json',
|
|
15
|
+
instruments: 'https://stac-extensions.github.io/instruments/v0.1.0/schema.json',
|
|
14
16
|
'item-assets': 'https://stac-extensions.github.io/item-assets/v1.0.0/schema.json',
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
label: 'https://stac-extensions.github.io/label/v1.0.1/schema.json',
|
|
18
|
+
order: 'https://stac-extensions.github.io/order/v1.1.0/schema.json',
|
|
19
|
+
pointcloud: 'https://stac-extensions.github.io/pointcloud/v2.0.0/schema.json',
|
|
20
|
+
processing: 'https://stac-extensions.github.io/processing/v1.2.0/schema.json',
|
|
21
|
+
product: 'https://stac-extensions.github.io/product/v1.0.0/schema.json',
|
|
22
|
+
projection: 'https://stac-extensions.github.io/projection/v2.0.0/schema.json',
|
|
23
|
+
raster: 'https://stac-extensions.github.io/raster/v2.0.0/schema.json',
|
|
24
|
+
sar: 'https://stac-extensions.github.io/sar/v1.3.2/schema.json',
|
|
25
|
+
sat: 'https://stac-extensions.github.io/sat/v1.2.0/schema.json',
|
|
26
|
+
scientific: 'https://stac-extensions.github.io/scientific/v1.0.0/schema.json',
|
|
27
|
+
table: 'https://stac-extensions.github.io/table/v1.2.0/schema.json',
|
|
28
|
+
timestamps: 'https://stac-extensions.github.io/timestamps/v1.1.0/schema.json',
|
|
29
|
+
version: 'https://stac-extensions.github.io/version/v1.2.0/schema.json',
|
|
30
|
+
view: 'https://stac-extensions.github.io/view/v1.1.0/schema.json',
|
|
27
31
|
};
|
|
28
32
|
const EXTENSIONS = {
|
|
29
33
|
// Add a : at the end to indicate it has a prefix, otherwise list all fields separately (see version extension for example).
|
|
@@ -43,11 +47,11 @@ const EXTENSIONS = {
|
|
|
43
47
|
'sci:': SCHEMAS.scientific,
|
|
44
48
|
'view:': SCHEMAS.view,
|
|
45
49
|
// without prefix
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
version: SCHEMAS.version,
|
|
51
|
+
deprecated: SCHEMAS.version,
|
|
52
|
+
published: SCHEMAS.timestamps,
|
|
53
|
+
expires: SCHEMAS.timestamps,
|
|
54
|
+
unpublished: SCHEMAS.timestamps,
|
|
51
55
|
},
|
|
52
56
|
catalog: {
|
|
53
57
|
// None yet
|
|
@@ -65,11 +69,11 @@ EXTENSIONS.item = Object.assign(EXTENSIONS.item, EXTENSIONS.itemAndCollection);
|
|
|
65
69
|
var Ext = {
|
|
66
70
|
parseExtension(url) {
|
|
67
71
|
// Try to match name and version from official extensions
|
|
68
|
-
let match = url.match(/^https?:\/\/stac-extensions.github.io\/([
|
|
72
|
+
let match = url.match(/^https?:\/\/stac-extensions.github.io\/([^/]+)\/v([^/]+)\/[^.]+.json$/i);
|
|
69
73
|
if (match) {
|
|
70
74
|
return {
|
|
71
75
|
id: match[1],
|
|
72
|
-
version: match[2]
|
|
76
|
+
version: match[2],
|
|
73
77
|
};
|
|
74
78
|
}
|
|
75
79
|
// Try to match version from URIs
|
|
@@ -77,17 +81,17 @@ var Ext = {
|
|
|
77
81
|
if (match2) {
|
|
78
82
|
return {
|
|
79
83
|
id: url,
|
|
80
|
-
version: match2[1]
|
|
84
|
+
version: match2[1],
|
|
81
85
|
};
|
|
82
86
|
}
|
|
83
87
|
// Handle schortnames
|
|
84
88
|
if (url in SCHEMAS) {
|
|
85
89
|
return {
|
|
86
90
|
id: url,
|
|
87
|
-
version: '0.0.0'
|
|
91
|
+
version: '0.0.0',
|
|
88
92
|
};
|
|
89
93
|
}
|
|
90
|
-
}
|
|
94
|
+
},
|
|
91
95
|
};
|
|
92
96
|
|
|
93
97
|
var V = {
|
|
@@ -97,8 +101,7 @@ var V = {
|
|
|
97
101
|
set(stac) {
|
|
98
102
|
if (typeof stac.stac_version !== 'string') {
|
|
99
103
|
V.version = '0.6.0'; // Assume the worst case, it doesn't seem there's a clear indicator for 0.7.0
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
104
|
+
} else {
|
|
102
105
|
V.version = stac.stac_version;
|
|
103
106
|
}
|
|
104
107
|
|
|
@@ -120,23 +123,19 @@ var V = {
|
|
|
120
123
|
let compareTo = ext ? V.extensions[ext] : V.version;
|
|
121
124
|
if (typeof compareTo === 'undefined') {
|
|
122
125
|
return false;
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
126
|
+
} else {
|
|
125
127
|
return compareVersions.compare(compareTo, version, comparator);
|
|
126
128
|
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
+
},
|
|
129
130
|
};
|
|
130
131
|
|
|
131
132
|
var _ = {
|
|
132
|
-
|
|
133
133
|
type(val) {
|
|
134
134
|
let type = typeof val;
|
|
135
135
|
if (type === 'object') {
|
|
136
136
|
if (val === null) {
|
|
137
137
|
return 'null';
|
|
138
|
-
}
|
|
139
|
-
else if (Array.isArray(val)) {
|
|
138
|
+
} else if (Array.isArray(val)) {
|
|
140
139
|
return 'array';
|
|
141
140
|
}
|
|
142
141
|
}
|
|
@@ -146,18 +145,17 @@ var _ = {
|
|
|
146
145
|
is(val, type) {
|
|
147
146
|
if (Array.isArray(type)) {
|
|
148
147
|
return type.includes(_.type(val));
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
148
|
+
} else {
|
|
151
149
|
return _.type(val) === type;
|
|
152
150
|
}
|
|
153
151
|
},
|
|
154
152
|
|
|
155
153
|
isDefined(val) {
|
|
156
|
-
return
|
|
154
|
+
return typeof val !== 'undefined';
|
|
157
155
|
},
|
|
158
156
|
|
|
159
157
|
isObject(obj) {
|
|
160
|
-
return
|
|
158
|
+
return typeof obj === 'object' && obj === Object(obj) && !Array.isArray(obj);
|
|
161
159
|
},
|
|
162
160
|
|
|
163
161
|
rename(obj, oldKey, newKey) {
|
|
@@ -212,8 +210,7 @@ var _ = {
|
|
|
212
210
|
if (obj[key].length === 1) {
|
|
213
211
|
obj[key] = obj[key][0];
|
|
214
212
|
return true;
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
213
|
+
} else {
|
|
217
214
|
return false; // It's still an array and we don't know which element to choose
|
|
218
215
|
}
|
|
219
216
|
}
|
|
@@ -235,8 +232,7 @@ var _ = {
|
|
|
235
232
|
if (Array.isArray(obj[key]) && obj[key].length > 0) {
|
|
236
233
|
obj[key] = obj[key][0];
|
|
237
234
|
return true;
|
|
238
|
-
}
|
|
239
|
-
else {
|
|
235
|
+
} else {
|
|
240
236
|
delete obj[key];
|
|
241
237
|
return false;
|
|
242
238
|
}
|
|
@@ -251,22 +247,21 @@ var _ = {
|
|
|
251
247
|
|
|
252
248
|
upgradeExtension(context, extension) {
|
|
253
249
|
let { id, version } = Ext.parseExtension(extension);
|
|
254
|
-
let index = context.stac_extensions.findIndex(url => {
|
|
250
|
+
let index = context.stac_extensions.findIndex((url) => {
|
|
255
251
|
let old = Ext.parseExtension(url);
|
|
256
|
-
return
|
|
252
|
+
return old && old.id === id && compareVersions.compare(old.version, version, '<');
|
|
257
253
|
});
|
|
258
254
|
if (index !== -1) {
|
|
259
255
|
context.stac_extensions[index] = extension;
|
|
260
256
|
return true;
|
|
261
|
-
}
|
|
262
|
-
else {
|
|
257
|
+
} else {
|
|
263
258
|
return false;
|
|
264
259
|
}
|
|
265
260
|
},
|
|
266
261
|
|
|
267
262
|
addExtension(context, newExtension) {
|
|
268
263
|
let { id, version } = Ext.parseExtension(newExtension);
|
|
269
|
-
let index = context.stac_extensions.findIndex(url => {
|
|
264
|
+
let index = context.stac_extensions.findIndex((url) => {
|
|
270
265
|
if (url === newExtension) {
|
|
271
266
|
return true;
|
|
272
267
|
}
|
|
@@ -278,8 +273,7 @@ var _ = {
|
|
|
278
273
|
});
|
|
279
274
|
if (index === -1) {
|
|
280
275
|
context.stac_extensions.push(newExtension);
|
|
281
|
-
}
|
|
282
|
-
else {
|
|
276
|
+
} else {
|
|
283
277
|
context.stac_extensions[index] = newExtension;
|
|
284
278
|
}
|
|
285
279
|
|
|
@@ -317,8 +311,8 @@ var _ = {
|
|
|
317
311
|
objectsToCheck.push(context.links);
|
|
318
312
|
|
|
319
313
|
let obj;
|
|
320
|
-
while(obj = objectsToCheck.pop()) {
|
|
321
|
-
Object.keys(obj).forEach(key => {
|
|
314
|
+
while ((obj = objectsToCheck.pop())) {
|
|
315
|
+
Object.keys(obj).forEach((key) => {
|
|
322
316
|
// Add additional objects to check
|
|
323
317
|
if (Array.isArray(obj.bands)) {
|
|
324
318
|
objectsToCheck = objectsToCheck.concat(obj.bands);
|
|
@@ -335,7 +329,7 @@ var _ = {
|
|
|
335
329
|
},
|
|
336
330
|
|
|
337
331
|
mapValues(obj, key, oldValues, newValues) {
|
|
338
|
-
let fn = value => {
|
|
332
|
+
let fn = (value) => {
|
|
339
333
|
let index = oldValues.indexOf(value);
|
|
340
334
|
if (index >= 0) {
|
|
341
335
|
return newValues[index];
|
|
@@ -344,8 +338,7 @@ var _ = {
|
|
|
344
338
|
};
|
|
345
339
|
if (Array.isArray(obj[key])) {
|
|
346
340
|
obj[key] = obj[key].map(fn);
|
|
347
|
-
}
|
|
348
|
-
else if (typeof obj[key] !== 'undefined') {
|
|
341
|
+
} else if (typeof obj[key] !== 'undefined') {
|
|
349
342
|
obj[key] = fn(obj[key]);
|
|
350
343
|
}
|
|
351
344
|
return true;
|
|
@@ -361,14 +354,12 @@ var _ = {
|
|
|
361
354
|
let condition;
|
|
362
355
|
if (fromSummary) {
|
|
363
356
|
if (mergedSummary) {
|
|
364
|
-
condition = val => Array.isArray(val);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
condition = val => Array.isArray(val) && val.length === 1;
|
|
357
|
+
condition = (val) => Array.isArray(val);
|
|
358
|
+
} else {
|
|
359
|
+
condition = (val) => Array.isArray(val) && val.length === 1;
|
|
368
360
|
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
condition = _.isDefined
|
|
361
|
+
} else {
|
|
362
|
+
condition = _.isDefined;
|
|
372
363
|
}
|
|
373
364
|
if (condition(obj[key])) {
|
|
374
365
|
context[key] = fromSummary && !mergedSummary ? obj[key][0] : obj[key];
|
|
@@ -391,7 +382,7 @@ var _ = {
|
|
|
391
382
|
try {
|
|
392
383
|
obj[key] = this.toISOString(obj[key]);
|
|
393
384
|
return true;
|
|
394
|
-
} catch (error) {
|
|
385
|
+
} catch (error) {}
|
|
395
386
|
}
|
|
396
387
|
delete obj[key];
|
|
397
388
|
return false;
|
|
@@ -401,11 +392,11 @@ var _ = {
|
|
|
401
392
|
if (!(date instanceof Date)) {
|
|
402
393
|
date = new Date(date);
|
|
403
394
|
}
|
|
404
|
-
return date.toISOString().replace(/\.0+(?=[
|
|
395
|
+
return date.toISOString().replace(/\.0+(?=[-+Z])/, ''); // Don't export milliseconds if not needed
|
|
405
396
|
},
|
|
406
397
|
|
|
407
398
|
formatString(obj, key, format) {
|
|
408
|
-
const formatter = value => {
|
|
399
|
+
const formatter = (value) => {
|
|
409
400
|
if (_.is(value, ['string', 'number'])) {
|
|
410
401
|
return format.replaceAll('{}', value);
|
|
411
402
|
}
|
|
@@ -413,47 +404,61 @@ var _ = {
|
|
|
413
404
|
};
|
|
414
405
|
if (Array.isArray(obj[key])) {
|
|
415
406
|
obj[key] = obj[key].map(formatter);
|
|
416
|
-
}
|
|
417
|
-
else {
|
|
407
|
+
} else {
|
|
418
408
|
obj[key] = formatter(obj[key]);
|
|
419
409
|
}
|
|
420
|
-
}
|
|
421
|
-
|
|
410
|
+
},
|
|
422
411
|
};
|
|
423
412
|
|
|
424
413
|
var Checksum = {
|
|
414
|
+
// Multicodec codes, see https://github.com/multiformats/multicodec/
|
|
415
|
+
codes: {
|
|
416
|
+
md5: 0xd5,
|
|
417
|
+
sha1: 0x11,
|
|
418
|
+
'sha2-256': 0x12,
|
|
419
|
+
'sha3-256': 0x16,
|
|
420
|
+
},
|
|
425
421
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
422
|
+
// Unsigned varint as a hex string.
|
|
423
|
+
encodeVarint(value) {
|
|
424
|
+
let hex = '';
|
|
425
|
+
while (value >= 0x80) {
|
|
426
|
+
hex += ((value & 0x7f) | 0x80).toString(16).padStart(2, '0');
|
|
427
|
+
value >>>= 7;
|
|
431
428
|
}
|
|
432
|
-
|
|
429
|
+
hex += value.toString(16).padStart(2, '0');
|
|
430
|
+
return hex;
|
|
433
431
|
},
|
|
434
432
|
|
|
435
|
-
|
|
436
|
-
|
|
433
|
+
// Prefix a hex digest with its varint-encoded multicodec code and length.
|
|
434
|
+
encodeMultihash(digest, algo) {
|
|
435
|
+
if (!(algo in Checksum.codes)) {
|
|
436
|
+
throw new Error(`Unsupported hash function: ${algo}`);
|
|
437
|
+
}
|
|
438
|
+
if (digest.length === 0 || !/^[0-9a-f]+$/i.test(digest) || digest.length % 2 !== 0) {
|
|
439
|
+
throw new Error(`The string "${digest}" is not valid hex.`);
|
|
440
|
+
}
|
|
441
|
+
const code = Checksum.encodeVarint(Checksum.codes[algo]);
|
|
442
|
+
// Hex string, so byte length is half the string length.
|
|
443
|
+
const length = Checksum.encodeVarint(digest.length / 2);
|
|
444
|
+
return code + length + digest;
|
|
437
445
|
},
|
|
438
446
|
|
|
439
447
|
toMultihash(obj, key, algo) {
|
|
440
|
-
if (!
|
|
448
|
+
if (!_.is(obj[key], 'string')) {
|
|
441
449
|
return false;
|
|
442
450
|
}
|
|
443
451
|
try {
|
|
444
|
-
|
|
445
|
-
obj[key] = Checksum.uint8ToHex(encoded);
|
|
452
|
+
obj[key] = Checksum.encodeMultihash(obj[key], algo);
|
|
446
453
|
return true;
|
|
447
454
|
} catch (error) {
|
|
448
455
|
console.warn(error);
|
|
449
456
|
return false;
|
|
450
457
|
}
|
|
451
|
-
}
|
|
452
|
-
|
|
458
|
+
},
|
|
453
459
|
};
|
|
454
460
|
|
|
455
461
|
var Catalog = {
|
|
456
|
-
|
|
457
462
|
migrate(catalog, updateVersionNumber = true) {
|
|
458
463
|
V.set(catalog);
|
|
459
464
|
if (updateVersionNumber) {
|
|
@@ -470,15 +475,18 @@ var Catalog = {
|
|
|
470
475
|
|
|
471
476
|
_.runAll(Catalog, catalog, catalog);
|
|
472
477
|
|
|
478
|
+
// Migrate extension fields that live directly on the Catalog/Collection root
|
|
479
|
+
// (e.g. order, timestamps, version). Assets, Item properties, bands and Collection
|
|
480
|
+
// summaries are migrated through their own Fields.migrate calls.
|
|
481
|
+
Fields.migrate(catalog, catalog);
|
|
482
|
+
|
|
473
483
|
V.before('0.8.0') && _.populateExtensions(catalog, 'catalog') && DONE;
|
|
474
484
|
|
|
475
485
|
return catalog;
|
|
476
486
|
},
|
|
477
|
-
|
|
478
487
|
};
|
|
479
488
|
|
|
480
489
|
var Collection = {
|
|
481
|
-
|
|
482
490
|
migrate(collection, updateVersionNumber = true) {
|
|
483
491
|
Catalog.migrate(collection, updateVersionNumber); // Migrates stac_version, stac_extensions, id, title, description, links
|
|
484
492
|
collection.type = 'Collection';
|
|
@@ -488,11 +496,11 @@ var Collection = {
|
|
|
488
496
|
_.ensure(collection, 'license', 'other') && DONE;
|
|
489
497
|
_.ensure(collection, 'extent', {
|
|
490
498
|
spatial: {
|
|
491
|
-
bbox: []
|
|
499
|
+
bbox: [],
|
|
492
500
|
},
|
|
493
501
|
temporal: {
|
|
494
|
-
interval: []
|
|
495
|
-
}
|
|
502
|
+
interval: [],
|
|
503
|
+
},
|
|
496
504
|
}) && DONE;
|
|
497
505
|
|
|
498
506
|
_.runAll(Collection, collection, collection);
|
|
@@ -510,31 +518,27 @@ var Collection = {
|
|
|
510
518
|
},
|
|
511
519
|
|
|
512
520
|
extent(collection) {
|
|
513
|
-
_.ensure(collection,
|
|
521
|
+
_.ensure(collection, 'extent', {});
|
|
514
522
|
|
|
515
523
|
if (V.before('0.8.0')) {
|
|
516
524
|
// Restructure spatial extent
|
|
517
525
|
if (Array.isArray(collection.extent.spatial)) {
|
|
518
526
|
collection.extent.spatial = {
|
|
519
|
-
bbox: [
|
|
520
|
-
collection.extent.spatial
|
|
521
|
-
]
|
|
527
|
+
bbox: [collection.extent.spatial],
|
|
522
528
|
};
|
|
523
529
|
}
|
|
524
530
|
// Restructure temporal extent
|
|
525
531
|
if (Array.isArray(collection.extent.temporal)) {
|
|
526
532
|
collection.extent.temporal = {
|
|
527
|
-
interval: [
|
|
528
|
-
collection.extent.temporal
|
|
529
|
-
]
|
|
533
|
+
interval: [collection.extent.temporal],
|
|
530
534
|
};
|
|
531
535
|
}
|
|
532
536
|
}
|
|
533
537
|
|
|
534
|
-
_.ensure(collection.extent,
|
|
535
|
-
_.ensure(collection.extent.spatial,
|
|
536
|
-
_.ensure(collection.extent,
|
|
537
|
-
_.ensure(collection.extent.temporal,
|
|
538
|
+
_.ensure(collection.extent, 'spatial', {});
|
|
539
|
+
_.ensure(collection.extent.spatial, 'bbox', []);
|
|
540
|
+
_.ensure(collection.extent, 'temporal', {});
|
|
541
|
+
_.ensure(collection.extent.temporal, 'interval', []);
|
|
538
542
|
|
|
539
543
|
if (V.before('1.0.0-rc.3')) {
|
|
540
544
|
// The first extent in a Collection is always the overall extent, followed by more specific extents.
|
|
@@ -543,37 +547,35 @@ var Collection = {
|
|
|
543
547
|
for (let interval of collection.extent.temporal.interval) {
|
|
544
548
|
if (interval[0] === null) {
|
|
545
549
|
min = null;
|
|
546
|
-
}
|
|
547
|
-
else if (typeof interval[0] === 'string' && min !== null) {
|
|
550
|
+
} else if (typeof interval[0] === 'string' && min !== null) {
|
|
548
551
|
try {
|
|
549
552
|
let start = new Date(interval[0]);
|
|
550
553
|
if (typeof min === 'undefined' || start < min) {
|
|
551
554
|
min = start;
|
|
552
555
|
}
|
|
553
|
-
} catch (error) {
|
|
556
|
+
} catch (error) {}
|
|
554
557
|
}
|
|
555
558
|
|
|
556
559
|
if (interval[1] === null) {
|
|
557
560
|
max = null;
|
|
558
|
-
}
|
|
559
|
-
else if (typeof interval[1] === 'string' && max !== null) {
|
|
561
|
+
} else if (typeof interval[1] === 'string' && max !== null) {
|
|
560
562
|
try {
|
|
561
563
|
let end = new Date(interval[1]);
|
|
562
564
|
if (typeof max === 'undefined' || end > max) {
|
|
563
565
|
max = end;
|
|
564
566
|
}
|
|
565
|
-
} catch (error) {
|
|
567
|
+
} catch (error) {}
|
|
566
568
|
}
|
|
567
569
|
}
|
|
568
|
-
collection.extent.temporal.interval.unshift([
|
|
569
|
-
min ? _.toISOString(min) : null,
|
|
570
|
-
max ? _.toISOString(max) : null
|
|
571
|
-
]);
|
|
570
|
+
collection.extent.temporal.interval.unshift([min ? _.toISOString(min) : null, max ? _.toISOString(max) : null]);
|
|
572
571
|
}
|
|
573
572
|
if (collection.extent.spatial.bbox.length > 1) {
|
|
574
|
-
let count = collection.extent.spatial.bbox.reduce(
|
|
573
|
+
let count = collection.extent.spatial.bbox.reduce(
|
|
574
|
+
(val, bbox) => (Array.isArray(bbox) ? Math.max(bbox.length, val) : val),
|
|
575
|
+
4,
|
|
576
|
+
);
|
|
575
577
|
if (count >= 4) {
|
|
576
|
-
let union =
|
|
578
|
+
let union = Array.from({ length: count }).fill(null);
|
|
577
579
|
let middle = count / 2;
|
|
578
580
|
for (let bbox of collection.extent.spatial.bbox) {
|
|
579
581
|
if (!Array.isArray(bbox) || bbox.length < 4) {
|
|
@@ -583,17 +585,14 @@ var Collection = {
|
|
|
583
585
|
let c = bbox[i];
|
|
584
586
|
if (union[i] === null) {
|
|
585
587
|
union[i] = c;
|
|
586
|
-
}
|
|
587
|
-
else if (i < middle) {
|
|
588
|
+
} else if (i < middle) {
|
|
588
589
|
union[i] = Math.min(c, union[i]);
|
|
589
|
-
}
|
|
590
|
-
else {
|
|
590
|
+
} else {
|
|
591
591
|
union[i] = Math.max(c, union[i]);
|
|
592
592
|
}
|
|
593
|
-
|
|
594
593
|
}
|
|
595
594
|
}
|
|
596
|
-
if (union.findIndex(c => c === null) === -1) {
|
|
595
|
+
if (union.findIndex((c) => c === null) === -1) {
|
|
597
596
|
collection.extent.spatial.bbox.unshift(union);
|
|
598
597
|
}
|
|
599
598
|
}
|
|
@@ -628,13 +627,11 @@ var Collection = {
|
|
|
628
627
|
minimum: prop.extent[0],
|
|
629
628
|
maximum: prop.extent[1],
|
|
630
629
|
};
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
if (prop.values.filter(v => Array.isArray(v)).length === prop.values.length) {
|
|
630
|
+
} else if (Array.isArray(prop.values)) {
|
|
631
|
+
if (prop.values.filter((v) => Array.isArray(v)).length === prop.values.length) {
|
|
634
632
|
// If it is an array of arrays, merge the arrays
|
|
635
633
|
collection.summaries[key] = prop.values.reduce((a, b) => a.concat(b), []);
|
|
636
|
-
}
|
|
637
|
-
else {
|
|
634
|
+
} else {
|
|
638
635
|
collection.summaries[key] = prop.values;
|
|
639
636
|
}
|
|
640
637
|
}
|
|
@@ -645,7 +642,11 @@ var Collection = {
|
|
|
645
642
|
// Migrate Commons extension - part 2
|
|
646
643
|
// Move properties to (single element) summaries if the Collection is standalone
|
|
647
644
|
// see also https://github.com/stac-utils/stac-migrate/issues/3
|
|
648
|
-
if (
|
|
645
|
+
if (
|
|
646
|
+
V.before('1.0.0-beta.1') &&
|
|
647
|
+
_.isObject(collection.properties) &&
|
|
648
|
+
!collection.links.find((link) => ['child', 'item'].includes(link.rel))
|
|
649
|
+
) {
|
|
649
650
|
for (let key in collection.properties) {
|
|
650
651
|
let value = collection.properties[key];
|
|
651
652
|
if (!Array.isArray(value)) {
|
|
@@ -656,7 +657,7 @@ var Collection = {
|
|
|
656
657
|
}
|
|
657
658
|
|
|
658
659
|
if (V.before('1.0.0-rc.1')) {
|
|
659
|
-
_.mapObject(collection.summaries, val => {
|
|
660
|
+
_.mapObject(collection.summaries, (val) => {
|
|
660
661
|
_.rename(val, 'min', 'minimum') && DONE;
|
|
661
662
|
_.rename(val, 'max', 'maximum') && DONE;
|
|
662
663
|
return val;
|
|
@@ -667,21 +668,27 @@ var Collection = {
|
|
|
667
668
|
Fields.migrate(collection.summaries, collection, true);
|
|
668
669
|
|
|
669
670
|
// Some fields should usually be on root-level if there's only one element
|
|
670
|
-
_.moveTo(collection.summaries, 'sci:doi', collection, true) &&
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
_.moveTo(collection.summaries, '
|
|
671
|
+
_.moveTo(collection.summaries, 'sci:doi', collection, true) &&
|
|
672
|
+
_.addExtension(collection, SCHEMAS.scientific) &&
|
|
673
|
+
DONE;
|
|
674
|
+
_.moveTo(collection.summaries, 'sci:publications', collection, true, true) &&
|
|
675
|
+
_.addExtension(collection, SCHEMAS.scientific) &&
|
|
676
|
+
DONE;
|
|
677
|
+
_.moveTo(collection.summaries, 'sci:citation', collection, true) &&
|
|
678
|
+
_.addExtension(collection, SCHEMAS.scientific) &&
|
|
679
|
+
DONE;
|
|
680
|
+
_.moveTo(collection.summaries, 'cube:dimensions', collection, true) &&
|
|
681
|
+
_.addExtension(collection, SCHEMAS.datacube) &&
|
|
682
|
+
DONE;
|
|
674
683
|
|
|
675
684
|
// Remove summary field if empty
|
|
676
685
|
if (Object.keys(collection.summaries).length === 0) {
|
|
677
686
|
delete collection.summaries;
|
|
678
687
|
}
|
|
679
|
-
}
|
|
680
|
-
|
|
688
|
+
},
|
|
681
689
|
};
|
|
682
690
|
|
|
683
691
|
var Item = {
|
|
684
|
-
|
|
685
692
|
migrate(item, collection = null, updateVersionNumber = true) {
|
|
686
693
|
V.set(item);
|
|
687
694
|
if (updateVersionNumber) {
|
|
@@ -721,27 +728,23 @@ var Item = {
|
|
|
721
728
|
(V.before('0.8.0') || commons) && _.populateExtensions(item, 'item') && DONE;
|
|
722
729
|
|
|
723
730
|
return item;
|
|
724
|
-
}
|
|
725
|
-
|
|
731
|
+
},
|
|
726
732
|
};
|
|
727
733
|
|
|
728
734
|
var CollectionCollection = {
|
|
729
|
-
|
|
730
735
|
migrate(object, updateVersionNumber = true) {
|
|
731
736
|
_.ensure(object, 'collections', []) && DONE;
|
|
732
737
|
_.ensure(object, 'links', []) && DONE;
|
|
733
738
|
|
|
734
739
|
_.runAll(CollectionCollection, object, object);
|
|
735
740
|
|
|
736
|
-
object.collections = object.collections.map(collection => Collection.migrate(collection, updateVersionNumber));
|
|
741
|
+
object.collections = object.collections.map((collection) => Collection.migrate(collection, updateVersionNumber));
|
|
737
742
|
|
|
738
743
|
return object;
|
|
739
744
|
},
|
|
740
|
-
|
|
741
745
|
};
|
|
742
746
|
|
|
743
747
|
var ItemCollection = {
|
|
744
|
-
|
|
745
748
|
migrate(itemCollection, updateVersionNumber = true) {
|
|
746
749
|
_.ensure(itemCollection, 'type', 'FeatureCollection') && DONE;
|
|
747
750
|
_.ensure(itemCollection, 'features', []) && DONE;
|
|
@@ -749,15 +752,15 @@ var ItemCollection = {
|
|
|
749
752
|
|
|
750
753
|
_.runAll(ItemCollection, itemCollection, itemCollection);
|
|
751
754
|
|
|
752
|
-
itemCollection.features = itemCollection.features.map(feature =>
|
|
755
|
+
itemCollection.features = itemCollection.features.map((feature) =>
|
|
756
|
+
Item.migrate(feature, null, updateVersionNumber),
|
|
757
|
+
);
|
|
753
758
|
|
|
754
759
|
return itemCollection;
|
|
755
760
|
},
|
|
756
|
-
|
|
757
761
|
};
|
|
758
762
|
|
|
759
763
|
var Asset = {
|
|
760
|
-
|
|
761
764
|
migrateAll(context, field = 'assets') {
|
|
762
765
|
for (let key in context[field]) {
|
|
763
766
|
Asset.migrate(context[field][key], context);
|
|
@@ -773,21 +776,22 @@ var Asset = {
|
|
|
773
776
|
},
|
|
774
777
|
|
|
775
778
|
mediaTypes(asset) {
|
|
776
|
-
_.is(asset.type, 'string') &&
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
779
|
+
_.is(asset.type, 'string') &&
|
|
780
|
+
_.mapValues(
|
|
781
|
+
asset,
|
|
782
|
+
'type',
|
|
783
|
+
['image/vnd.stac.geotiff', 'image/vnd.stac.geotiff; cloud-optimized=true'],
|
|
784
|
+
['image/tiff; application=geotiff', 'image/tiff; application=geotiff; profile=cloud-optimized'],
|
|
785
|
+
);
|
|
786
|
+
},
|
|
783
787
|
};
|
|
784
788
|
|
|
785
789
|
var Band = {
|
|
786
|
-
|
|
787
790
|
migrateAll(obj, context) {
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
const bands =
|
|
791
|
+
if (V.before('1.0.0')) {
|
|
792
|
+
// Not sure when the index-based bands were removed
|
|
793
|
+
const bands =
|
|
794
|
+
_.isObject(context.properties) && Array.isArray(context.properties.bands) ? context.properties.bands : [];
|
|
791
795
|
if (Array.isArray(obj['eo:bands'])) {
|
|
792
796
|
for (let i in obj['eo:bands']) {
|
|
793
797
|
let band = obj['eo:bands'][i];
|
|
@@ -802,11 +806,14 @@ var Band = {
|
|
|
802
806
|
}
|
|
803
807
|
}
|
|
804
808
|
|
|
805
|
-
if (
|
|
806
|
-
|
|
809
|
+
if (
|
|
810
|
+
(Array.isArray(obj['raster:bands']) || Array.isArray(obj['eo:bands'])) &&
|
|
811
|
+
(V.before('1.1.0-beta.1') || V.before('2.0.0-beta.1', 'raster') || V.before('2.0.0-beta.1', 'eo'))
|
|
812
|
+
) {
|
|
813
|
+
_.ensure(obj, 'bands', []);
|
|
807
814
|
|
|
808
|
-
const raster = obj[
|
|
809
|
-
const eo = obj[
|
|
815
|
+
const raster = obj['raster:bands'] || [];
|
|
816
|
+
const eo = obj['eo:bands'] || [];
|
|
810
817
|
const length = Math.max(raster.length, eo.length);
|
|
811
818
|
for (let i = 0; i < length; i++) {
|
|
812
819
|
_.ensure(obj.bands, i, {});
|
|
@@ -814,10 +821,9 @@ var Band = {
|
|
|
814
821
|
obj.bands[i] = Band.migrate(obj.bands[i], context);
|
|
815
822
|
}
|
|
816
823
|
|
|
817
|
-
delete obj[
|
|
818
|
-
delete obj[
|
|
824
|
+
delete obj['raster:bands'];
|
|
825
|
+
delete obj['eo:bands'];
|
|
819
826
|
}
|
|
820
|
-
|
|
821
827
|
},
|
|
822
828
|
|
|
823
829
|
migrate(band, context) {
|
|
@@ -846,12 +852,10 @@ var Band = {
|
|
|
846
852
|
_.rename(band, 'offset', 'raster:offset') && DONE;
|
|
847
853
|
_.rename(band, 'histogram', 'raster:histogram') && DONE;
|
|
848
854
|
}
|
|
849
|
-
}
|
|
850
|
-
|
|
855
|
+
},
|
|
851
856
|
};
|
|
852
857
|
|
|
853
858
|
var Fields = {
|
|
854
|
-
|
|
855
859
|
migrate(obj, context, summaries = false) {
|
|
856
860
|
_.runAll(Fields, obj, context, summaries);
|
|
857
861
|
|
|
@@ -885,31 +889,48 @@ var Fields = {
|
|
|
885
889
|
},
|
|
886
890
|
|
|
887
891
|
checksum(obj, context) {
|
|
888
|
-
if (V.before('0.9.0')
|
|
889
|
-
_.rename(obj, 'checksum:md5', 'checksum:multihash') &&
|
|
890
|
-
|
|
892
|
+
if (V.before('0.9.0')) {
|
|
893
|
+
_.rename(obj, 'checksum:md5', 'checksum:multihash') &&
|
|
894
|
+
Checksum.toMultihash(obj, 'checksum:multihash', 'md5') &&
|
|
895
|
+
DONE;
|
|
896
|
+
_.rename(obj, 'checksum:sha1', 'checksum:multihash') &&
|
|
897
|
+
Checksum.toMultihash(obj, 'checksum:multihash', 'sha1') &&
|
|
898
|
+
DONE;
|
|
891
899
|
// We assume sha2/3-256 although that may fail in some cases and other lengths are chosen
|
|
892
900
|
// Never seen this implemtned in the wild, so let's try this until another use case comes up
|
|
893
|
-
_.rename(obj, 'checksum:sha2', 'checksum:multihash') &&
|
|
894
|
-
|
|
901
|
+
_.rename(obj, 'checksum:sha2', 'checksum:multihash') &&
|
|
902
|
+
Checksum.toMultihash(obj, 'checksum:multihash', 'sha2-256') &&
|
|
903
|
+
DONE;
|
|
904
|
+
_.rename(obj, 'checksum:sha3', 'checksum:multihash') &&
|
|
905
|
+
Checksum.toMultihash(obj, 'checksum:multihash', 'sha3-256') &&
|
|
906
|
+
DONE;
|
|
895
907
|
}
|
|
896
908
|
|
|
897
|
-
V.before('1.0.0-rc.1') &&
|
|
909
|
+
V.before('1.0.0-rc.1') &&
|
|
910
|
+
_.rename(obj, 'checksum:multihash', 'file:checksum') &&
|
|
911
|
+
_.addExtension(context, SCHEMAS.file) &&
|
|
912
|
+
DONE;
|
|
898
913
|
|
|
899
914
|
_.removeExtension(context, 'checksum');
|
|
900
915
|
},
|
|
901
916
|
|
|
902
917
|
classification(obj, context) {
|
|
903
918
|
if (V.before('1.1.0', 'classification')) {
|
|
904
|
-
_.forAll(obj, 'classification:classes', o => _.rename(o, 'color-hint', 'color_hint')) && DONE;
|
|
919
|
+
_.forAll(obj, 'classification:classes', (o) => _.rename(o, 'color-hint', 'color_hint')) && DONE;
|
|
905
920
|
}
|
|
906
921
|
if (V.before('2.0.0', 'classification')) {
|
|
907
|
-
_.forAll(obj, 'classification:classes', o => _.ensure(o, 'name', o.description)) && DONE;
|
|
922
|
+
_.forAll(obj, 'classification:classes', (o) => _.ensure(o, 'name', o.description)) && DONE;
|
|
908
923
|
}
|
|
909
924
|
|
|
910
925
|
_.upgradeExtension(context, SCHEMAS.classification);
|
|
911
926
|
},
|
|
912
927
|
|
|
928
|
+
cf(obj, context) {
|
|
929
|
+
_.rename(obj, 'cf:parameter', 'cf:standard_name') && DONE;
|
|
930
|
+
|
|
931
|
+
_.upgradeExtension(context, SCHEMAS.cf);
|
|
932
|
+
},
|
|
933
|
+
|
|
913
934
|
cube(obj, context) {
|
|
914
935
|
// We'd need to convert proj strings to something else for v1.0 -> v2.0, but that's unfeasible here.
|
|
915
936
|
// Nothing else to do here.
|
|
@@ -970,9 +991,21 @@ var Fields = {
|
|
|
970
991
|
_.upgradeExtension(context, SCHEMAS.label);
|
|
971
992
|
},
|
|
972
993
|
|
|
994
|
+
order(obj, context) {
|
|
995
|
+
if (_.rename(obj, 'order:expiration_date', 'expires')) {
|
|
996
|
+
_.toUTC(obj, 'expires');
|
|
997
|
+
_.addExtension(context, SCHEMAS.timestamps) && DONE;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
_.upgradeExtension(context, SCHEMAS.order);
|
|
1001
|
+
},
|
|
1002
|
+
|
|
973
1003
|
pc(obj, context) {
|
|
974
1004
|
V.before('0.8.0') && _.rename(obj, 'pc:schema', 'pc:schemas') && DONE;
|
|
975
1005
|
|
|
1006
|
+
// pc:encoding was removed in Point Cloud 2.0.0 (superseded by the asset's media type),
|
|
1007
|
+
// but it cannot be reliably reconstructed, so it is left untouched.
|
|
1008
|
+
|
|
976
1009
|
_.upgradeExtension(context, SCHEMAS.pointcloud);
|
|
977
1010
|
},
|
|
978
1011
|
|
|
@@ -982,6 +1015,12 @@ var Fields = {
|
|
|
982
1015
|
_.upgradeExtension(context, SCHEMAS.processing);
|
|
983
1016
|
},
|
|
984
1017
|
|
|
1018
|
+
product(obj, context) {
|
|
1019
|
+
// Nothing to do
|
|
1020
|
+
|
|
1021
|
+
_.upgradeExtension(context, SCHEMAS.product);
|
|
1022
|
+
},
|
|
1023
|
+
|
|
985
1024
|
proj(obj, context) {
|
|
986
1025
|
_.rename(obj, 'proj:epsg', 'proj:code') && _.formatString(obj, 'proj:code', 'EPSG:{}') && DONE;
|
|
987
1026
|
|
|
@@ -995,12 +1034,21 @@ var Fields = {
|
|
|
995
1034
|
sar(obj, context, summary) {
|
|
996
1035
|
// Which version have they been (re)moved?
|
|
997
1036
|
_.rename(obj, 'sar:incidence_angle', 'view:incidence_angle') && _.addExtension(context, SCHEMAS.view) && DONE;
|
|
998
|
-
_.rename(obj, 'sar:pass_direction', 'sat:orbit_state') &&
|
|
1037
|
+
_.rename(obj, 'sar:pass_direction', 'sat:orbit_state') &&
|
|
1038
|
+
_.mapValues(obj, 'sat:orbit_state', [null], ['geostationary']) &&
|
|
1039
|
+
_.addExtension(context, SCHEMAS.sat) &&
|
|
1040
|
+
DONE;
|
|
999
1041
|
|
|
1000
1042
|
if (V.before('0.7.0')) {
|
|
1001
1043
|
_.flattenArray(obj, 'sar:resolution', ['sar:resolution_range', 'sar:resolution_azimuth'], summary) && DONE;
|
|
1002
|
-
_.flattenArray(obj, 'sar:pixel_spacing', ['sar:pixel_spacing_range', 'sar:pixel_spacing_azimuth'], summary) &&
|
|
1003
|
-
|
|
1044
|
+
_.flattenArray(obj, 'sar:pixel_spacing', ['sar:pixel_spacing_range', 'sar:pixel_spacing_azimuth'], summary) &&
|
|
1045
|
+
DONE;
|
|
1046
|
+
_.flattenArray(
|
|
1047
|
+
obj,
|
|
1048
|
+
'sar:looks',
|
|
1049
|
+
['sar:looks_range', 'sar:looks_azimuth', 'sar:looks_equivalent_number'],
|
|
1050
|
+
summary,
|
|
1051
|
+
) && DONE;
|
|
1004
1052
|
_.rename(obj, 'sar:off_nadir', 'view:off_nadir') && _.addExtension(context, SCHEMAS.view) && DONE;
|
|
1005
1053
|
}
|
|
1006
1054
|
|
|
@@ -1010,8 +1058,21 @@ var Fields = {
|
|
|
1010
1058
|
_.rename(obj, 'sar:constellation', 'constellation') && DONE;
|
|
1011
1059
|
_.rename(obj, 'sar:type', 'sar:product_type') && DONE;
|
|
1012
1060
|
_.rename(obj, 'sar:polarization', 'sar:polarizations') && DONE;
|
|
1013
|
-
_.flattenOneElementArray(obj, 'sar:absolute_orbit', summary) &&
|
|
1014
|
-
|
|
1061
|
+
_.flattenOneElementArray(obj, 'sar:absolute_orbit', summary) &&
|
|
1062
|
+
_.rename(obj, 'sar:absolute_orbit', 'sat:absolute_orbit') &&
|
|
1063
|
+
_.addExtension(context, SCHEMAS.sat) &&
|
|
1064
|
+
DONE;
|
|
1065
|
+
_.flattenOneElementArray(obj, 'sar:relative_orbit', summary) &&
|
|
1066
|
+
_.rename(obj, 'sar:relative_orbit', 'sat:relative_orbit') &&
|
|
1067
|
+
_.addExtension(context, SCHEMAS.sat) &&
|
|
1068
|
+
DONE;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
_.rename(obj, 'sar:product_type', 'product:type') && _.addExtension(context, SCHEMAS.product) && DONE;
|
|
1072
|
+
|
|
1073
|
+
if (_.rename(obj, 'sar:instrument_mode', 'instrument_modes')) {
|
|
1074
|
+
!summary && _.toArray(obj, 'instrument_modes');
|
|
1075
|
+
_.addExtension(context, SCHEMAS.instruments) && DONE;
|
|
1015
1076
|
}
|
|
1016
1077
|
|
|
1017
1078
|
_.upgradeExtension(context, SCHEMAS.sar);
|
|
@@ -1035,7 +1096,8 @@ var Fields = {
|
|
|
1035
1096
|
_.upgradeExtension(context, SCHEMAS.scientific);
|
|
1036
1097
|
},
|
|
1037
1098
|
|
|
1038
|
-
item(obj) {
|
|
1099
|
+
item(obj) {
|
|
1100
|
+
// Single Item
|
|
1039
1101
|
if (V.before('0.8.0')) {
|
|
1040
1102
|
_.rename(obj, 'item:license', 'license') && DONE;
|
|
1041
1103
|
_.rename(obj, 'item:providers', 'providers') && DONE;
|
|
@@ -1053,12 +1115,10 @@ var Fields = {
|
|
|
1053
1115
|
// Nothing to do
|
|
1054
1116
|
|
|
1055
1117
|
_.upgradeExtension(context, SCHEMAS.view);
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1118
|
+
},
|
|
1058
1119
|
};
|
|
1059
1120
|
|
|
1060
1121
|
var Migrate = {
|
|
1061
|
-
|
|
1062
1122
|
item(item, collection = null, updateVersionNumber = true) {
|
|
1063
1123
|
return Item.migrate(item, collection, updateVersionNumber);
|
|
1064
1124
|
},
|
|
@@ -1082,25 +1142,22 @@ var Migrate = {
|
|
|
1082
1142
|
stac(object, updateVersionNumber = true) {
|
|
1083
1143
|
if (object.type === 'Feature') {
|
|
1084
1144
|
return Migrate.item(object, null, updateVersionNumber);
|
|
1085
|
-
}
|
|
1086
|
-
else if (object.type === 'FeatureCollection') {
|
|
1145
|
+
} else if (object.type === 'FeatureCollection') {
|
|
1087
1146
|
return Migrate.itemCollection(object, updateVersionNumber);
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1147
|
+
} else if (
|
|
1148
|
+
object.type === 'Collection' ||
|
|
1149
|
+
(!object.type && _.isDefined(object.extent) && _.isDefined(object.license))
|
|
1150
|
+
) {
|
|
1090
1151
|
return Migrate.collection(object, updateVersionNumber);
|
|
1091
|
-
}
|
|
1092
|
-
else if (!object.type && Array.isArray(object.collections)) {
|
|
1152
|
+
} else if (!object.type && Array.isArray(object.collections)) {
|
|
1093
1153
|
return Migrate.collectionCollection(object, updateVersionNumber);
|
|
1094
|
-
}
|
|
1095
|
-
else {
|
|
1154
|
+
} else {
|
|
1096
1155
|
return Migrate.catalog(object, updateVersionNumber);
|
|
1097
1156
|
}
|
|
1098
1157
|
},
|
|
1099
1158
|
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1159
|
+
// Deprecated no-op; multihash conversion is always on.
|
|
1160
|
+
enableMultihash() {},
|
|
1104
1161
|
};
|
|
1105
1162
|
|
|
1106
1163
|
module.exports = Migrate;
|