@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.
Files changed (5) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +81 -41
  3. package/bin/cli.js +20 -20
  4. package/migrate.js +247 -190
  5. 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
- 'classification': 'https://stac-extensions.github.io/classification/v2.0.0/schema.json',
11
- 'datacube': 'https://stac-extensions.github.io/datacube/v2.2.0/schema.json',
12
- 'eo': 'https://stac-extensions.github.io/eo/v2.0.0/schema.json',
13
- 'file': 'https://stac-extensions.github.io/file/v2.1.0/schema.json',
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
- 'label': 'https://stac-extensions.github.io/label/v1.0.1/schema.json',
16
- 'pointcloud': 'https://stac-extensions.github.io/pointcloud/v1.0.0/schema.json',
17
- 'processing': 'https://stac-extensions.github.io/processing/v1.2.0/schema.json',
18
- 'projection': 'https://stac-extensions.github.io/projection/v2.0.0/schema.json',
19
- 'raster': 'https://stac-extensions.github.io/raster/v2.0.0/schema.json',
20
- 'sar': 'https://stac-extensions.github.io/sar/v1.0.0/schema.json',
21
- 'sat': 'https://stac-extensions.github.io/sat/v1.0.0/schema.json',
22
- 'scientific': 'https://stac-extensions.github.io/scientific/v1.0.0/schema.json',
23
- 'table': 'https://stac-extensions.github.io/table/v1.2.0/schema.json',
24
- 'timestamps': 'https://stac-extensions.github.io/timestamps/v1.1.0/schema.json',
25
- 'version': 'https://stac-extensions.github.io/version/v1.2.0/schema.json',
26
- 'view': 'https://stac-extensions.github.io/view/v1.0.0/schema.json'
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
- 'version': SCHEMAS.version,
47
- 'deprecated': SCHEMAS.version,
48
- 'published': SCHEMAS.timestamps,
49
- 'expires': SCHEMAS.timestamps,
50
- 'unpublished': SCHEMAS.timestamps
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\/([^\/]+)\/v([^\/]+)\/[^.]+.json$/i);
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 (typeof val !== 'undefined');
154
+ return typeof val !== 'undefined';
157
155
  },
158
156
 
159
157
  isObject(obj) {
160
- return (typeof obj === 'object' && obj === Object(obj) && !Array.isArray(obj));
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 (old && old.id === id && compareVersions.compare(old.version, version, '<'));
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
- else {
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
- else {
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+(?=[-\+Z])/, ''); // Don't export milliseconds if not needed
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
- multihash: null,
427
-
428
- hexToUint8(hexString) {
429
- if (hexString.length === 0 || hexString.length % 2 !== 0) {
430
- throw new Error(`The string "${hexString}" is not valid hex.`)
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
- return new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
429
+ hex += value.toString(16).padStart(2, '0');
430
+ return hex;
433
431
  },
434
432
 
435
- uint8ToHex(bytes) {
436
- return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
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 (!Checksum.multihash || !_.is(obj[key], 'string')) {
448
+ if (!_.is(obj[key], 'string')) {
441
449
  return false;
442
450
  }
443
451
  try {
444
- const encoded = Checksum.multihash.encode(Checksum.hexToUint8(obj[key]), algo);
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, "extent", {});
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, "spatial", {});
535
- _.ensure(collection.extent.spatial, "bbox", []);
536
- _.ensure(collection.extent, "temporal", {});
537
- _.ensure(collection.extent.temporal, "interval", []);
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((val, bbox) => Array.isArray(bbox) ? Math.max(bbox.length, val) : val, 4);
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 = new Array(count).fill(null);
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
- else if (Array.isArray(prop.values)) {
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 (V.before('1.0.0-beta.1') && _.isObject(collection.properties) && !collection.links.find(link => ['child', 'item'].includes(link.rel))) {
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) && _.addExtension(collection, SCHEMAS.scientific) && DONE;
671
- _.moveTo(collection.summaries, 'sci:publications', collection, true, true) && _.addExtension(collection, SCHEMAS.scientific) && DONE;
672
- _.moveTo(collection.summaries, 'sci:citation', collection, true) && _.addExtension(collection, SCHEMAS.scientific) && DONE;
673
- _.moveTo(collection.summaries, 'cube:dimensions', collection, true) && _.addExtension(collection, SCHEMAS.datacube) && DONE;
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 => Item.migrate(feature, null, updateVersionNumber));
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') && _.mapValues(
777
- asset, 'type',
778
- ['image/vnd.stac.geotiff', 'image/vnd.stac.geotiff; cloud-optimized=true'],
779
- ['image/tiff; application=geotiff', 'image/tiff; application=geotiff; profile=cloud-optimized']
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
- if (V.before('1.0.0')) { // Not sure when the index-based bands were removed
790
- const bands = _.isObject(context.properties) && Array.isArray(context.properties.bands) ? context.properties.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 (V.before("1.1.0-beta.1") && (Array.isArray(obj["raster:bands"]) || Array.isArray(obj["eo:bands"]))) {
806
- _.ensure(obj, "bands", []);
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["raster:bands"] || [];
809
- const eo = obj["eo:bands"] || [];
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["raster:bands"];
818
- delete obj["eo:bands"];
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') && Checksum.multihash) {
889
- _.rename(obj, 'checksum:md5', 'checksum:multihash') && Checksum.toMultihash(obj, 'checksum:multihash', 'md5') && DONE;
890
- _.rename(obj, 'checksum:sha1', 'checksum:multihash') && Checksum.toMultihash(obj, 'checksum:multihash', 'sha1') && DONE;
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') && Checksum.toMultihash(obj, 'checksum:multihash', 'sha2-256') && DONE;
894
- _.rename(obj, 'checksum:sha3', 'checksum:multihash') && Checksum.toMultihash(obj, 'checksum:multihash', 'sha3-256') && DONE;
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') && _.rename(obj, 'checksum:multihash', 'file:checksum') && _.addExtension(context, SCHEMAS.file) && DONE;
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') && _.mapValues(obj, 'sat:orbit_state', [null], ['geostationary']) && _.addExtension(context, SCHEMAS.sat) && DONE;
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) && DONE;
1003
- _.flattenArray(obj, 'sar:looks', ['sar:looks_range', 'sar:looks_azimuth', 'sar:looks_equivalent_number'], summary) && DONE;
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) && _.rename(obj, 'sar:absolute_orbit', 'sat:absolute_orbit') && _.addExtension(context, SCHEMAS.sat) && DONE;
1014
- _.flattenOneElementArray(obj, 'sar:relative_orbit', summary) && _.rename(obj, 'sar:relative_orbit', 'sat:relative_orbit') && _.addExtension(context, SCHEMAS.sat) && DONE;
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) { // Single Item
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
- else if (object.type === 'Collection' || (!object.type && _.isDefined(object.extent) && _.isDefined(object.license))) {
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
- enableMultihash(multihash) {
1101
- Checksum.multihash = multihash;
1102
- }
1103
-
1159
+ // Deprecated no-op; multihash conversion is always on.
1160
+ enableMultihash() {},
1104
1161
  };
1105
1162
 
1106
1163
  module.exports = Migrate;