@radiantearth/stac-migrate 2.0.1 → 2.1.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.
- package/LICENSE +201 -201
- package/README.md +81 -41
- package/bin/cli.js +20 -20
- package/migrate.js +244 -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,11 @@ var Band = {
|
|
|
802
806
|
}
|
|
803
807
|
}
|
|
804
808
|
|
|
805
|
-
if (V.before(
|
|
806
|
-
_.ensure(obj,
|
|
809
|
+
if (V.before('1.1.0-beta.1') && (Array.isArray(obj['raster:bands']) || Array.isArray(obj['eo:bands']))) {
|
|
810
|
+
_.ensure(obj, 'bands', []);
|
|
807
811
|
|
|
808
|
-
const raster = obj[
|
|
809
|
-
const eo = obj[
|
|
812
|
+
const raster = obj['raster:bands'] || [];
|
|
813
|
+
const eo = obj['eo:bands'] || [];
|
|
810
814
|
const length = Math.max(raster.length, eo.length);
|
|
811
815
|
for (let i = 0; i < length; i++) {
|
|
812
816
|
_.ensure(obj.bands, i, {});
|
|
@@ -814,10 +818,9 @@ var Band = {
|
|
|
814
818
|
obj.bands[i] = Band.migrate(obj.bands[i], context);
|
|
815
819
|
}
|
|
816
820
|
|
|
817
|
-
delete obj[
|
|
818
|
-
delete obj[
|
|
821
|
+
delete obj['raster:bands'];
|
|
822
|
+
delete obj['eo:bands'];
|
|
819
823
|
}
|
|
820
|
-
|
|
821
824
|
},
|
|
822
825
|
|
|
823
826
|
migrate(band, context) {
|
|
@@ -846,12 +849,10 @@ var Band = {
|
|
|
846
849
|
_.rename(band, 'offset', 'raster:offset') && DONE;
|
|
847
850
|
_.rename(band, 'histogram', 'raster:histogram') && DONE;
|
|
848
851
|
}
|
|
849
|
-
}
|
|
850
|
-
|
|
852
|
+
},
|
|
851
853
|
};
|
|
852
854
|
|
|
853
855
|
var Fields = {
|
|
854
|
-
|
|
855
856
|
migrate(obj, context, summaries = false) {
|
|
856
857
|
_.runAll(Fields, obj, context, summaries);
|
|
857
858
|
|
|
@@ -885,31 +886,48 @@ var Fields = {
|
|
|
885
886
|
},
|
|
886
887
|
|
|
887
888
|
checksum(obj, context) {
|
|
888
|
-
if (V.before('0.9.0')
|
|
889
|
-
_.rename(obj, 'checksum:md5', 'checksum:multihash') &&
|
|
890
|
-
|
|
889
|
+
if (V.before('0.9.0')) {
|
|
890
|
+
_.rename(obj, 'checksum:md5', 'checksum:multihash') &&
|
|
891
|
+
Checksum.toMultihash(obj, 'checksum:multihash', 'md5') &&
|
|
892
|
+
DONE;
|
|
893
|
+
_.rename(obj, 'checksum:sha1', 'checksum:multihash') &&
|
|
894
|
+
Checksum.toMultihash(obj, 'checksum:multihash', 'sha1') &&
|
|
895
|
+
DONE;
|
|
891
896
|
// We assume sha2/3-256 although that may fail in some cases and other lengths are chosen
|
|
892
897
|
// 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
|
-
|
|
898
|
+
_.rename(obj, 'checksum:sha2', 'checksum:multihash') &&
|
|
899
|
+
Checksum.toMultihash(obj, 'checksum:multihash', 'sha2-256') &&
|
|
900
|
+
DONE;
|
|
901
|
+
_.rename(obj, 'checksum:sha3', 'checksum:multihash') &&
|
|
902
|
+
Checksum.toMultihash(obj, 'checksum:multihash', 'sha3-256') &&
|
|
903
|
+
DONE;
|
|
895
904
|
}
|
|
896
905
|
|
|
897
|
-
V.before('1.0.0-rc.1') &&
|
|
906
|
+
V.before('1.0.0-rc.1') &&
|
|
907
|
+
_.rename(obj, 'checksum:multihash', 'file:checksum') &&
|
|
908
|
+
_.addExtension(context, SCHEMAS.file) &&
|
|
909
|
+
DONE;
|
|
898
910
|
|
|
899
911
|
_.removeExtension(context, 'checksum');
|
|
900
912
|
},
|
|
901
913
|
|
|
902
914
|
classification(obj, context) {
|
|
903
915
|
if (V.before('1.1.0', 'classification')) {
|
|
904
|
-
_.forAll(obj, 'classification:classes', o => _.rename(o, 'color-hint', 'color_hint')) && DONE;
|
|
916
|
+
_.forAll(obj, 'classification:classes', (o) => _.rename(o, 'color-hint', 'color_hint')) && DONE;
|
|
905
917
|
}
|
|
906
918
|
if (V.before('2.0.0', 'classification')) {
|
|
907
|
-
_.forAll(obj, 'classification:classes', o => _.ensure(o, 'name', o.description)) && DONE;
|
|
919
|
+
_.forAll(obj, 'classification:classes', (o) => _.ensure(o, 'name', o.description)) && DONE;
|
|
908
920
|
}
|
|
909
921
|
|
|
910
922
|
_.upgradeExtension(context, SCHEMAS.classification);
|
|
911
923
|
},
|
|
912
924
|
|
|
925
|
+
cf(obj, context) {
|
|
926
|
+
_.rename(obj, 'cf:parameter', 'cf:standard_name') && DONE;
|
|
927
|
+
|
|
928
|
+
_.upgradeExtension(context, SCHEMAS.cf);
|
|
929
|
+
},
|
|
930
|
+
|
|
913
931
|
cube(obj, context) {
|
|
914
932
|
// We'd need to convert proj strings to something else for v1.0 -> v2.0, but that's unfeasible here.
|
|
915
933
|
// Nothing else to do here.
|
|
@@ -970,9 +988,21 @@ var Fields = {
|
|
|
970
988
|
_.upgradeExtension(context, SCHEMAS.label);
|
|
971
989
|
},
|
|
972
990
|
|
|
991
|
+
order(obj, context) {
|
|
992
|
+
if (_.rename(obj, 'order:expiration_date', 'expires')) {
|
|
993
|
+
_.toUTC(obj, 'expires');
|
|
994
|
+
_.addExtension(context, SCHEMAS.timestamps) && DONE;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
_.upgradeExtension(context, SCHEMAS.order);
|
|
998
|
+
},
|
|
999
|
+
|
|
973
1000
|
pc(obj, context) {
|
|
974
1001
|
V.before('0.8.0') && _.rename(obj, 'pc:schema', 'pc:schemas') && DONE;
|
|
975
1002
|
|
|
1003
|
+
// pc:encoding was removed in Point Cloud 2.0.0 (superseded by the asset's media type),
|
|
1004
|
+
// but it cannot be reliably reconstructed, so it is left untouched.
|
|
1005
|
+
|
|
976
1006
|
_.upgradeExtension(context, SCHEMAS.pointcloud);
|
|
977
1007
|
},
|
|
978
1008
|
|
|
@@ -982,6 +1012,12 @@ var Fields = {
|
|
|
982
1012
|
_.upgradeExtension(context, SCHEMAS.processing);
|
|
983
1013
|
},
|
|
984
1014
|
|
|
1015
|
+
product(obj, context) {
|
|
1016
|
+
// Nothing to do
|
|
1017
|
+
|
|
1018
|
+
_.upgradeExtension(context, SCHEMAS.product);
|
|
1019
|
+
},
|
|
1020
|
+
|
|
985
1021
|
proj(obj, context) {
|
|
986
1022
|
_.rename(obj, 'proj:epsg', 'proj:code') && _.formatString(obj, 'proj:code', 'EPSG:{}') && DONE;
|
|
987
1023
|
|
|
@@ -995,12 +1031,21 @@ var Fields = {
|
|
|
995
1031
|
sar(obj, context, summary) {
|
|
996
1032
|
// Which version have they been (re)moved?
|
|
997
1033
|
_.rename(obj, 'sar:incidence_angle', 'view:incidence_angle') && _.addExtension(context, SCHEMAS.view) && DONE;
|
|
998
|
-
_.rename(obj, 'sar:pass_direction', 'sat:orbit_state') &&
|
|
1034
|
+
_.rename(obj, 'sar:pass_direction', 'sat:orbit_state') &&
|
|
1035
|
+
_.mapValues(obj, 'sat:orbit_state', [null], ['geostationary']) &&
|
|
1036
|
+
_.addExtension(context, SCHEMAS.sat) &&
|
|
1037
|
+
DONE;
|
|
999
1038
|
|
|
1000
1039
|
if (V.before('0.7.0')) {
|
|
1001
1040
|
_.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
|
-
|
|
1041
|
+
_.flattenArray(obj, 'sar:pixel_spacing', ['sar:pixel_spacing_range', 'sar:pixel_spacing_azimuth'], summary) &&
|
|
1042
|
+
DONE;
|
|
1043
|
+
_.flattenArray(
|
|
1044
|
+
obj,
|
|
1045
|
+
'sar:looks',
|
|
1046
|
+
['sar:looks_range', 'sar:looks_azimuth', 'sar:looks_equivalent_number'],
|
|
1047
|
+
summary,
|
|
1048
|
+
) && DONE;
|
|
1004
1049
|
_.rename(obj, 'sar:off_nadir', 'view:off_nadir') && _.addExtension(context, SCHEMAS.view) && DONE;
|
|
1005
1050
|
}
|
|
1006
1051
|
|
|
@@ -1010,8 +1055,21 @@ var Fields = {
|
|
|
1010
1055
|
_.rename(obj, 'sar:constellation', 'constellation') && DONE;
|
|
1011
1056
|
_.rename(obj, 'sar:type', 'sar:product_type') && DONE;
|
|
1012
1057
|
_.rename(obj, 'sar:polarization', 'sar:polarizations') && DONE;
|
|
1013
|
-
_.flattenOneElementArray(obj, 'sar:absolute_orbit', summary) &&
|
|
1014
|
-
|
|
1058
|
+
_.flattenOneElementArray(obj, 'sar:absolute_orbit', summary) &&
|
|
1059
|
+
_.rename(obj, 'sar:absolute_orbit', 'sat:absolute_orbit') &&
|
|
1060
|
+
_.addExtension(context, SCHEMAS.sat) &&
|
|
1061
|
+
DONE;
|
|
1062
|
+
_.flattenOneElementArray(obj, 'sar:relative_orbit', summary) &&
|
|
1063
|
+
_.rename(obj, 'sar:relative_orbit', 'sat:relative_orbit') &&
|
|
1064
|
+
_.addExtension(context, SCHEMAS.sat) &&
|
|
1065
|
+
DONE;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
_.rename(obj, 'sar:product_type', 'product:type') && _.addExtension(context, SCHEMAS.product) && DONE;
|
|
1069
|
+
|
|
1070
|
+
if (_.rename(obj, 'sar:instrument_mode', 'instrument_modes')) {
|
|
1071
|
+
!summary && _.toArray(obj, 'instrument_modes');
|
|
1072
|
+
_.addExtension(context, SCHEMAS.instruments) && DONE;
|
|
1015
1073
|
}
|
|
1016
1074
|
|
|
1017
1075
|
_.upgradeExtension(context, SCHEMAS.sar);
|
|
@@ -1035,7 +1093,8 @@ var Fields = {
|
|
|
1035
1093
|
_.upgradeExtension(context, SCHEMAS.scientific);
|
|
1036
1094
|
},
|
|
1037
1095
|
|
|
1038
|
-
item(obj) {
|
|
1096
|
+
item(obj) {
|
|
1097
|
+
// Single Item
|
|
1039
1098
|
if (V.before('0.8.0')) {
|
|
1040
1099
|
_.rename(obj, 'item:license', 'license') && DONE;
|
|
1041
1100
|
_.rename(obj, 'item:providers', 'providers') && DONE;
|
|
@@ -1053,12 +1112,10 @@ var Fields = {
|
|
|
1053
1112
|
// Nothing to do
|
|
1054
1113
|
|
|
1055
1114
|
_.upgradeExtension(context, SCHEMAS.view);
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1115
|
+
},
|
|
1058
1116
|
};
|
|
1059
1117
|
|
|
1060
1118
|
var Migrate = {
|
|
1061
|
-
|
|
1062
1119
|
item(item, collection = null, updateVersionNumber = true) {
|
|
1063
1120
|
return Item.migrate(item, collection, updateVersionNumber);
|
|
1064
1121
|
},
|
|
@@ -1082,25 +1139,22 @@ var Migrate = {
|
|
|
1082
1139
|
stac(object, updateVersionNumber = true) {
|
|
1083
1140
|
if (object.type === 'Feature') {
|
|
1084
1141
|
return Migrate.item(object, null, updateVersionNumber);
|
|
1085
|
-
}
|
|
1086
|
-
else if (object.type === 'FeatureCollection') {
|
|
1142
|
+
} else if (object.type === 'FeatureCollection') {
|
|
1087
1143
|
return Migrate.itemCollection(object, updateVersionNumber);
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1144
|
+
} else if (
|
|
1145
|
+
object.type === 'Collection' ||
|
|
1146
|
+
(!object.type && _.isDefined(object.extent) && _.isDefined(object.license))
|
|
1147
|
+
) {
|
|
1090
1148
|
return Migrate.collection(object, updateVersionNumber);
|
|
1091
|
-
}
|
|
1092
|
-
else if (!object.type && Array.isArray(object.collections)) {
|
|
1149
|
+
} else if (!object.type && Array.isArray(object.collections)) {
|
|
1093
1150
|
return Migrate.collectionCollection(object, updateVersionNumber);
|
|
1094
|
-
}
|
|
1095
|
-
else {
|
|
1151
|
+
} else {
|
|
1096
1152
|
return Migrate.catalog(object, updateVersionNumber);
|
|
1097
1153
|
}
|
|
1098
1154
|
},
|
|
1099
1155
|
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1156
|
+
// Deprecated no-op; multihash conversion is always on.
|
|
1157
|
+
enableMultihash() {},
|
|
1104
1158
|
};
|
|
1105
1159
|
|
|
1106
1160
|
module.exports = Migrate;
|