@radiantearth/stac-migrate 1.6.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +16 -14
  2. package/migrate.js +999 -881
  3. package/package.json +41 -37
package/migrate.js CHANGED
@@ -4,968 +4,1086 @@
4
4
 
5
5
  var compareVersions = require('compare-versions');
6
6
 
7
- const LATEST_VERSION = '1.0.0';
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/v1.1.0/schema.json',
11
- 'datacube': 'https://stac-extensions.github.io/datacube/v2.1.0/schema.json',
12
- 'eo': 'https://stac-extensions.github.io/eo/v1.0.0/schema.json',
13
- 'file': 'https://stac-extensions.github.io/file/v1.0.0/schema.json',
14
- '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.1.0/schema.json',
18
- 'projection': 'https://stac-extensions.github.io/projection/v1.0.0/schema.json',
19
- 'raster': 'https://stac-extensions.github.io/raster/v1.1.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.0.0/schema.json',
25
- 'version': 'https://stac-extensions.github.io/version/v1.0.0/schema.json',
26
- 'view': 'https://stac-extensions.github.io/view/v1.0.0/schema.json'
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',
14
+ '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'
27
27
  };
28
28
  const EXTENSIONS = {
29
- // Add a : at the end to indicate it has a prefix, otherwise list all fields separately (see version extension for example).
30
- itemAndCollection: {
31
- // with prefix
32
- 'cube:': SCHEMAS.datacube,
33
- 'eo:': SCHEMAS.eo,
34
- 'file:': SCHEMAS.file,
35
- 'label:': SCHEMAS.label,
36
- 'pc:': SCHEMAS.pointcloud,
37
- 'processing:': SCHEMAS.processing,
38
- 'proj:': SCHEMAS.projection,
39
- 'raster:': SCHEMAS.raster,
40
- 'sar:': SCHEMAS.sar,
41
- 'sat:': SCHEMAS.sat,
42
- 'sci:': SCHEMAS.scientific,
43
- 'view:': SCHEMAS.view,
44
- // without prefix
45
- 'version': SCHEMAS.version,
46
- 'deprecated': SCHEMAS.version,
47
- 'published': SCHEMAS.timestamps,
48
- 'expires': SCHEMAS.timestamps,
49
- 'unpublished': SCHEMAS.timestamps
50
- },
51
- catalog: {
52
- // None yet
53
- },
54
- collection: {
55
- 'item_assets': SCHEMAS['item-assets']
56
- },
57
- item: {
58
- // None yet
59
- },
29
+ // Add a : at the end to indicate it has a prefix, otherwise list all fields separately (see version extension for example).
30
+ itemAndCollection: {
31
+ // with prefix
32
+ 'classification:': SCHEMAS.classification,
33
+ 'cube:': SCHEMAS.datacube,
34
+ 'eo:': SCHEMAS.eo,
35
+ 'file:': SCHEMAS.file,
36
+ 'label:': SCHEMAS.label,
37
+ 'pc:': SCHEMAS.pointcloud,
38
+ 'processing:': SCHEMAS.processing,
39
+ 'proj:': SCHEMAS.projection,
40
+ 'raster:': SCHEMAS.raster,
41
+ 'sar:': SCHEMAS.sar,
42
+ 'sat:': SCHEMAS.sat,
43
+ 'sci:': SCHEMAS.scientific,
44
+ 'view:': SCHEMAS.view,
45
+ // without prefix
46
+ 'version': SCHEMAS.version,
47
+ 'deprecated': SCHEMAS.version,
48
+ 'published': SCHEMAS.timestamps,
49
+ 'expires': SCHEMAS.timestamps,
50
+ 'unpublished': SCHEMAS.timestamps
51
+ },
52
+ catalog: {
53
+ // None yet
54
+ },
55
+ collection: {
56
+ // None yet
57
+ },
58
+ item: {
59
+ // None yet
60
+ },
60
61
  };
61
62
  EXTENSIONS.collection = Object.assign(EXTENSIONS.collection, EXTENSIONS.itemAndCollection);
62
63
  EXTENSIONS.item = Object.assign(EXTENSIONS.item, EXTENSIONS.itemAndCollection);
63
64
 
64
65
  var Ext = {
65
- parseUrl(url) {
66
- let match = url.match(/^https?:\/\/stac-extensions.github.io\/([^\/]+)\/v([^\/]+)\/[^.]+.json$/i);
67
- if (match) {
68
- return {
69
- id: match[1],
70
- version: match[2]
71
- };
72
- }
73
- }
66
+ parseUrl(url) {
67
+ let match = url.match(/^https?:\/\/stac-extensions.github.io\/([^\/]+)\/v([^\/]+)\/[^.]+.json$/i);
68
+ if (match) {
69
+ return {
70
+ id: match[1],
71
+ version: match[2]
72
+ };
73
+ }
74
+ }
74
75
  };
75
76
 
76
77
  var V = {
77
- version: LATEST_VERSION,
78
- extensions: {},
79
-
80
- set(stac) {
81
- if (typeof stac.stac_version !== 'string') {
82
- V.version = '0.6.0'; // Assume the worst case, it doesn't seem there's a clear indicator for 0.7.0
83
- }
84
- else {
85
- V.version = stac.stac_version;
86
- }
87
-
88
- if (Array.isArray(stac.stac_extensions)) {
89
- for(let ext of stac.stac_extensions) {
90
- let e = Ext.parseUrl(ext);
91
- if (e) {
92
- V.extensions[e.id] = e.version;
93
- }
94
- }
95
- }
96
- },
97
-
98
- before(version, ext = null) {
99
- let compareTo = ext ? V.extensions[ext] : V.version;
100
- if (typeof compareTo === 'undefined') {
101
- return false;
102
- }
103
- else {
104
- return compareVersions.compare(compareTo, version, '<');
105
- }
106
- }
78
+ version: LATEST_VERSION,
79
+ extensions: {},
80
+
81
+ set(stac) {
82
+ if (typeof stac.stac_version !== 'string') {
83
+ V.version = '0.6.0'; // Assume the worst case, it doesn't seem there's a clear indicator for 0.7.0
84
+ }
85
+ else {
86
+ V.version = stac.stac_version;
87
+ }
88
+
89
+ if (Array.isArray(stac.stac_extensions)) {
90
+ for (let ext of stac.stac_extensions) {
91
+ let e = Ext.parseUrl(ext);
92
+ if (e) {
93
+ V.extensions[e.id] = e.version;
94
+ }
95
+ }
96
+ }
97
+ },
98
+
99
+ before(version, ext = null) {
100
+ return V.compare('<', version, ext);
101
+ },
102
+
103
+ compare(comparator, version, ext = null) {
104
+ let compareTo = ext ? V.extensions[ext] : V.version;
105
+ if (typeof compareTo === 'undefined') {
106
+ return false;
107
+ }
108
+ else {
109
+ return compareVersions.compare(compareTo, version, comparator);
110
+ }
111
+ }
112
+
107
113
  };
108
114
 
109
115
  var _ = {
110
116
 
111
- type(val) {
112
- let type = typeof val;
113
- if (type === 'object') {
114
- if (val === null) {
115
- return 'null';
116
- }
117
- else if (Array.isArray(val)) {
118
- return 'array';
119
- }
120
- }
121
- return type;
122
- },
123
-
124
- is(val, type) {
125
- return (_.type(val) === type);
126
- },
127
-
128
- isDefined(val) {
129
- return (typeof val !== 'undefined');
130
- },
131
-
132
- isObject(obj) {
133
- return (typeof obj === 'object' && obj === Object(obj) && !Array.isArray(obj));
134
- },
135
-
136
- rename(obj, oldKey, newKey) {
137
- if (typeof obj[oldKey] !== 'undefined' && typeof obj[newKey] === 'undefined') {
138
- obj[newKey] = obj[oldKey];
139
- delete obj[oldKey];
140
- return true;
141
- }
142
- return false;
143
- },
144
-
145
- forAll(obj, key, fn) {
146
- if (obj[key] && typeof obj[key] === 'object') {
147
- for(let i in obj[key]) {
148
- fn(obj[key][i]);
149
- }
150
- }
151
- },
152
-
153
- toArray(obj, key) {
154
- if (typeof obj[key] !== 'undefined' && !Array.isArray(obj[key])) {
155
- obj[key] = [obj[key]];
156
- return true;
157
- }
158
- return false;
159
- },
160
-
161
- flattenArray(obj, key, newKeys, summary = false) {
162
- if (Array.isArray(obj[key])) {
163
- for(let i in obj[key]) {
164
- if (typeof newKeys[i] === 'string') {
165
- let value = obj[key][i];
166
- obj[newKeys[i]] = summary ? [value] : value;
167
- }
168
- }
169
- delete obj[key];
170
- return true;
171
- }
172
- return false;
173
- },
174
-
175
- flattenOneElementArray(obj, key, summary = false) {
176
- if (!summary && Array.isArray(obj[key])) {
177
- if (obj[key].length === 1) {
178
- obj[key] = obj[key][0];
179
- return true;
180
- }
181
- else {
182
- return false; // It's still an array and we don't know which element to choose
183
- }
184
- }
185
- return true; // It's already a single element
186
- },
187
-
188
- removeFromArray(obj, key, valueToRemove) {
189
- if (Array.isArray(obj[key])) {
190
- let index = obj[key].indexOf(valueToRemove);
191
- if (index > -1) {
192
- obj[key].splice(index, 1);
193
- }
194
- return true;
195
- }
196
- return false;
197
- },
198
-
199
- ensure(obj, key, defaultValue) {
200
- if (_.type(defaultValue) !== _.type(obj[key])) {
201
- obj[key] = defaultValue;
202
- }
203
- return true;
204
- },
205
-
206
- upgradeExtension(context, extension) {
207
- let {id, version} = Ext.parseUrl(extension);
208
- let index = context.stac_extensions.findIndex(url => {
209
- let old = Ext.parseUrl(url);
210
- return (old && old.id === id && compareVersions.compare(old.version, version, '<'));
211
- });
212
- if (index !== -1) {
213
- context.stac_extensions[index] = extension;
214
- return true;
215
- }
216
- else {
217
- return false;
218
- }
219
- },
220
-
221
- addExtension(context, newExtension) {
222
- let {id, version} = Ext.parseUrl(newExtension);
223
- let index = context.stac_extensions.findIndex(url => {
224
- if (url === newExtension) {
225
- return true;
226
- }
227
- let old = Ext.parseUrl(url);
228
- if (old && old.id === id && compareVersions.compare(old.version, version, '<')) {
229
- return true;
230
- }
231
- return false;
232
- });
233
- if (index === -1) {
234
- context.stac_extensions.push(newExtension);
235
- }
236
- else {
237
- context.stac_extensions[index] = newExtension;
238
- }
239
-
240
- context.stac_extensions.sort();
241
- return true;
242
- },
243
-
244
- removeExtension(context, oldExtension) {
245
- return _.removeFromArray(context, 'stac_extensions', oldExtension);
246
- },
247
-
248
- migrateExtensionShortnames(context) {
249
- let oldShortnames = Object.keys(SCHEMAS);
250
- let newSchemas = Object.values(SCHEMAS);
251
- return _.mapValues(context, 'stac_extensions', oldShortnames, newSchemas);
252
- },
253
-
254
- populateExtensions(context, type) {
255
- let objectsToCheck = [];
256
- if (type == 'catalog' || type == 'collection') {
257
- objectsToCheck.push(context);
258
- }
259
- if ((type == 'item' || type == 'collection') && _.isObject(context.assets)) {
260
- objectsToCheck = objectsToCheck.concat(Object.values(context.assets));
261
- }
262
- if (type == 'collection' && _.isObject(context.item_assets)) {
263
- objectsToCheck = objectsToCheck.concat(Object.values(context.item_assets));
264
- }
265
- if (type == 'collection' && _.isObject(context.summaries)) {
266
- objectsToCheck.push(context.summaries);
267
- }
268
- if (type == 'item' && _.isObject(context.properties)) {
269
- objectsToCheck.push(context.properties);
270
- }
271
-
272
- for (let obj of objectsToCheck) {
273
- Object.keys(obj).forEach(key => {
274
- let prefix = key.match(/^(\w+:|[^:]+$)/i);
275
- if (Array.isArray(prefix)) {
276
- let ext = EXTENSIONS[type][prefix[0]];
277
- _.is(ext, 'string') && _.addExtension(context, ext) && DONE;
278
- }
279
- });
280
- }
281
- },
282
-
283
- mapValues(obj, key, oldValues, newValues) {
284
- let fn = value => {
285
- let index = oldValues.indexOf(value);
286
- if (index >= 0) {
287
- return newValues[index];
288
- }
289
- return value;
290
- };
291
- if (Array.isArray(obj[key])) {
292
- obj[key] = obj[key].map(fn);
293
- }
294
- else if (typeof obj[key] !== 'undefined') {
295
- obj[key] = fn(obj[key]);
296
- }
297
- return true;
298
- },
299
-
300
- mapObject(obj, fn) {
301
- for(let key in obj) {
302
- obj[key] = fn(obj[key], key);
303
- }
304
- },
305
-
306
- moveTo(obj, key, context, fromSummary = false, mergedSummary = false) {
307
- let condition;
308
- if (fromSummary) {
309
- if (mergedSummary) {
310
- condition = val => Array.isArray(val);
311
- }
312
- else {
313
- condition = val => Array.isArray(val) && val.length === 1;
314
- }
315
- }
316
- else {
317
- condition = _.isDefined
318
- }
319
- if (condition(obj[key])) {
320
- context[key] = fromSummary && !mergedSummary ? obj[key][0] : obj[key];
321
- delete obj[key];
322
- return true;
323
- }
324
- return false;
325
- },
326
-
327
- runAll(migrations, obj, context, summaries) {
328
- for(let fn in migrations) {
329
- if (!fn.startsWith('migrate')) {
330
- migrations[fn](obj, context, summaries);
331
- }
332
- }
333
- },
334
-
335
- toUTC(obj, key) {
336
- if (typeof obj[key] === 'string') {
337
- try {
338
- obj[key] = this.toISOString(obj[key]);
339
- return true;
340
- } catch(error) {}
341
- }
342
- delete obj[key];
343
- return false;
344
- },
345
-
346
- toISOString(date) {
347
- if (!(date instanceof Date)) {
348
- date = new Date(date);
349
- }
350
- return date.toISOString().replace('.000', ''); // Don't export milliseconds if not needed
351
- }
117
+ type(val) {
118
+ let type = typeof val;
119
+ if (type === 'object') {
120
+ if (val === null) {
121
+ return 'null';
122
+ }
123
+ else if (Array.isArray(val)) {
124
+ return 'array';
125
+ }
126
+ }
127
+ return type;
128
+ },
129
+
130
+ is(val, type) {
131
+ if (Array.isArray(type)) {
132
+ return type.includes(_.type(val));
133
+ }
134
+ else {
135
+ return _.type(val) === type;
136
+ }
137
+ },
138
+
139
+ isDefined(val) {
140
+ return (typeof val !== 'undefined');
141
+ },
142
+
143
+ isObject(obj) {
144
+ return (typeof obj === 'object' && obj === Object(obj) && !Array.isArray(obj));
145
+ },
146
+
147
+ rename(obj, oldKey, newKey) {
148
+ if (typeof obj[oldKey] !== 'undefined' && typeof obj[newKey] === 'undefined') {
149
+ obj[newKey] = obj[oldKey];
150
+ delete obj[oldKey];
151
+ return true;
152
+ }
153
+ return false;
154
+ },
155
+
156
+ copy(obj, oldKey, newKey) {
157
+ if (typeof obj[oldKey] !== 'undefined' && typeof obj[newKey] === 'undefined') {
158
+ obj[newKey] = obj[oldKey];
159
+ return true;
160
+ }
161
+ return false;
162
+ },
163
+
164
+ forAll(obj, key, fn) {
165
+ if (obj[key] && typeof obj[key] === 'object') {
166
+ for (let i in obj[key]) {
167
+ fn(obj[key][i]);
168
+ }
169
+ }
170
+ },
171
+
172
+ toArray(obj, key) {
173
+ if (typeof obj[key] !== 'undefined' && !Array.isArray(obj[key])) {
174
+ obj[key] = [obj[key]];
175
+ return true;
176
+ }
177
+ return false;
178
+ },
179
+
180
+ flattenArray(obj, key, newKeys, summary = false) {
181
+ if (Array.isArray(obj[key])) {
182
+ for (let i in obj[key]) {
183
+ if (typeof newKeys[i] === 'string') {
184
+ let value = obj[key][i];
185
+ obj[newKeys[i]] = summary ? [value] : value;
186
+ }
187
+ }
188
+ delete obj[key];
189
+ return true;
190
+ }
191
+ return false;
192
+ },
193
+
194
+ flattenOneElementArray(obj, key, summary = false) {
195
+ if (!summary && Array.isArray(obj[key])) {
196
+ if (obj[key].length === 1) {
197
+ obj[key] = obj[key][0];
198
+ return true;
199
+ }
200
+ else {
201
+ return false; // It's still an array and we don't know which element to choose
202
+ }
203
+ }
204
+ return true; // It's already a single element
205
+ },
206
+
207
+ removeFromArray(obj, key, valueToRemove) {
208
+ if (Array.isArray(obj[key])) {
209
+ let index = obj[key].indexOf(valueToRemove);
210
+ if (index > -1) {
211
+ obj[key].splice(index, 1);
212
+ }
213
+ return true;
214
+ }
215
+ return false;
216
+ },
217
+
218
+ pickFirst(obj, key) {
219
+ if (Array.isArray(obj[key]) && obj[key].length > 0) {
220
+ obj[key] = obj[key][0];
221
+ return true;
222
+ }
223
+ else {
224
+ delete obj[key];
225
+ return false;
226
+ }
227
+ },
228
+
229
+ ensure(obj, key, defaultValue) {
230
+ if (_.type(defaultValue) !== _.type(obj[key])) {
231
+ obj[key] = defaultValue;
232
+ }
233
+ return true;
234
+ },
235
+
236
+ upgradeExtension(context, extension) {
237
+ let { id, version } = Ext.parseUrl(extension);
238
+ let index = context.stac_extensions.findIndex(url => {
239
+ let old = Ext.parseUrl(url);
240
+ return (old && old.id === id && compareVersions.compare(old.version, version, '<'));
241
+ });
242
+ if (index !== -1) {
243
+ context.stac_extensions[index] = extension;
244
+ return true;
245
+ }
246
+ else {
247
+ return false;
248
+ }
249
+ },
250
+
251
+ addExtension(context, newExtension) {
252
+ let { id, version } = Ext.parseUrl(newExtension);
253
+ let index = context.stac_extensions.findIndex(url => {
254
+ if (url === newExtension) {
255
+ return true;
256
+ }
257
+ let old = Ext.parseUrl(url);
258
+ if (old && old.id === id && compareVersions.compare(old.version, version, '<')) {
259
+ return true;
260
+ }
261
+ return false;
262
+ });
263
+ if (index === -1) {
264
+ context.stac_extensions.push(newExtension);
265
+ }
266
+ else {
267
+ context.stac_extensions[index] = newExtension;
268
+ }
269
+
270
+ context.stac_extensions.sort();
271
+ return true;
272
+ },
273
+
274
+ removeExtension(context, oldExtension) {
275
+ return _.removeFromArray(context, 'stac_extensions', oldExtension);
276
+ },
277
+
278
+ migrateExtensionShortnames(context) {
279
+ let oldShortnames = Object.keys(SCHEMAS);
280
+ let newSchemas = Object.values(SCHEMAS);
281
+ return _.mapValues(context, 'stac_extensions', oldShortnames, newSchemas);
282
+ },
283
+
284
+ populateExtensions(context, type) {
285
+ let objectsToCheck = [];
286
+ if (type == 'catalog' || type == 'collection') {
287
+ objectsToCheck.push(context);
288
+ }
289
+ if ((type == 'item' || type == 'collection') && _.isObject(context.assets)) {
290
+ objectsToCheck = objectsToCheck.concat(Object.values(context.assets));
291
+ }
292
+ if (type == 'collection' && _.isObject(context.item_assets)) {
293
+ objectsToCheck = objectsToCheck.concat(Object.values(context.item_assets));
294
+ }
295
+ if (type == 'collection' && _.isObject(context.summaries)) {
296
+ objectsToCheck.push(context.summaries);
297
+ }
298
+ if (type == 'item' && _.isObject(context.properties)) {
299
+ objectsToCheck.push(context.properties);
300
+ }
301
+ objectsToCheck.push(context.links);
302
+
303
+ let obj;
304
+ while(obj = objectsToCheck.pop()) {
305
+ Object.keys(obj).forEach(key => {
306
+ // Add additional objects to check
307
+ if (Array.isArray(obj.bands)) {
308
+ objectsToCheck = objectsToCheck.concat(obj.bands);
309
+ }
310
+
311
+ // Check for fields with extension prefixes
312
+ let prefix = key.match(/^(\w+:|[^:]+$)/i);
313
+ if (Array.isArray(prefix)) {
314
+ let ext = EXTENSIONS[type][prefix[0]];
315
+ _.is(ext, 'string') && _.addExtension(context, ext) && DONE;
316
+ }
317
+ });
318
+ }
319
+ },
320
+
321
+ mapValues(obj, key, oldValues, newValues) {
322
+ let fn = value => {
323
+ let index = oldValues.indexOf(value);
324
+ if (index >= 0) {
325
+ return newValues[index];
326
+ }
327
+ return value;
328
+ };
329
+ if (Array.isArray(obj[key])) {
330
+ obj[key] = obj[key].map(fn);
331
+ }
332
+ else if (typeof obj[key] !== 'undefined') {
333
+ obj[key] = fn(obj[key]);
334
+ }
335
+ return true;
336
+ },
337
+
338
+ mapObject(obj, fn) {
339
+ for (let key in obj) {
340
+ obj[key] = fn(obj[key], key);
341
+ }
342
+ },
343
+
344
+ moveTo(obj, key, context, fromSummary = false, mergedSummary = false) {
345
+ let condition;
346
+ if (fromSummary) {
347
+ if (mergedSummary) {
348
+ condition = val => Array.isArray(val);
349
+ }
350
+ else {
351
+ condition = val => Array.isArray(val) && val.length === 1;
352
+ }
353
+ }
354
+ else {
355
+ condition = _.isDefined
356
+ }
357
+ if (condition(obj[key])) {
358
+ context[key] = fromSummary && !mergedSummary ? obj[key][0] : obj[key];
359
+ delete obj[key];
360
+ return true;
361
+ }
362
+ return false;
363
+ },
364
+
365
+ runAll(migrations, obj, context, summaries) {
366
+ for (let fn in migrations) {
367
+ if (!fn.startsWith('migrate')) {
368
+ migrations[fn](obj, context, summaries);
369
+ }
370
+ }
371
+ },
372
+
373
+ toUTC(obj, key) {
374
+ if (_.is(obj[key], 'string')) {
375
+ try {
376
+ obj[key] = this.toISOString(obj[key]);
377
+ return true;
378
+ } catch (error) { }
379
+ }
380
+ delete obj[key];
381
+ return false;
382
+ },
383
+
384
+ toISOString(date) {
385
+ if (!(date instanceof Date)) {
386
+ date = new Date(date);
387
+ }
388
+ return date.toISOString().replace(/\.0+/, ''); // Don't export milliseconds if not needed
389
+ },
390
+
391
+ formatString(obj, key, format) {
392
+ const formatter = value => {
393
+ if (_.is(value, ['string', 'number'])) {
394
+ return format.replaceAll('{}', value);
395
+ }
396
+ return value;
397
+ };
398
+ if (Array.isArray(obj[key])) {
399
+ obj[key] = obj[key].map(formatter);
400
+ }
401
+ else {
402
+ obj[key] = formatter(obj[key]);
403
+ }
404
+ }
352
405
 
353
406
  };
354
407
 
355
408
  var Checksum = {
356
409
 
357
- multihash: null,
358
-
359
- hexToUint8(hexString) {
360
- if(hexString.length === 0 || hexString.length % 2 !== 0){
361
- throw new Error(`The string "${hexString}" is not valid hex.`)
362
- }
363
- return new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
364
- },
365
-
366
- uint8ToHex(bytes) {
367
- return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
368
- },
369
-
370
- toMultihash(obj, key, algo) {
371
- if (!Checksum.multihash || !_.is(obj[key], 'string')) {
372
- return false;
373
- }
374
- try {
375
- const encoded = Checksum.multihash.encode(Checksum.hexToUint8(obj[key]), algo);
376
- obj[key] = Checksum.uint8ToHex(encoded);
377
- return true;
378
- } catch (error) {
379
- console.warn(error);
380
- return false;
381
- }
382
- }
410
+ multihash: null,
411
+
412
+ hexToUint8(hexString) {
413
+ if (hexString.length === 0 || hexString.length % 2 !== 0) {
414
+ throw new Error(`The string "${hexString}" is not valid hex.`)
415
+ }
416
+ return new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
417
+ },
418
+
419
+ uint8ToHex(bytes) {
420
+ return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
421
+ },
422
+
423
+ toMultihash(obj, key, algo) {
424
+ if (!Checksum.multihash || !_.is(obj[key], 'string')) {
425
+ return false;
426
+ }
427
+ try {
428
+ const encoded = Checksum.multihash.encode(Checksum.hexToUint8(obj[key]), algo);
429
+ obj[key] = Checksum.uint8ToHex(encoded);
430
+ return true;
431
+ } catch (error) {
432
+ console.warn(error);
433
+ return false;
434
+ }
435
+ }
383
436
 
384
437
  };
385
438
 
386
439
  var Catalog = {
387
440
 
388
- migrate(catalog, updateVersionNumber = true) {
389
- V.set(catalog);
390
- if (updateVersionNumber) {
391
- catalog.stac_version = LATEST_VERSION;
392
- }
393
- catalog.type = 'Catalog';
441
+ migrate(catalog, updateVersionNumber = true) {
442
+ V.set(catalog);
443
+ if (updateVersionNumber) {
444
+ catalog.stac_version = LATEST_VERSION;
445
+ }
446
+ catalog.type = 'Catalog';
394
447
 
395
- _.ensure(catalog, 'stac_extensions', []) && DONE;
396
- V.before('1.0.0-rc.1') && _.migrateExtensionShortnames(catalog) && DONE;
448
+ _.ensure(catalog, 'stac_extensions', []) && DONE;
449
+ V.before('1.0.0-rc.1') && _.migrateExtensionShortnames(catalog) && DONE;
397
450
 
398
- _.ensure(catalog, 'id', '') && DONE;
399
- _.ensure(catalog, 'description', '') && DONE;
400
- _.ensure(catalog, 'links', []) && DONE;
451
+ _.ensure(catalog, 'id', '') && DONE;
452
+ _.ensure(catalog, 'description', '') && DONE;
453
+ _.ensure(catalog, 'links', []) && DONE;
401
454
 
402
- _.runAll(Catalog, catalog, catalog);
455
+ _.runAll(Catalog, catalog, catalog);
403
456
 
404
- V.before('0.8.0') && _.populateExtensions(catalog, 'catalog') && DONE;
457
+ V.before('0.8.0') && _.populateExtensions(catalog, 'catalog') && DONE;
405
458
 
406
- return catalog;
407
- },
459
+ return catalog;
460
+ },
408
461
 
409
462
  };
410
463
 
411
464
  var Collection = {
412
465
 
413
- migrate(collection, updateVersionNumber = true) {
414
- Catalog.migrate(collection, updateVersionNumber); // Migrates stac_version, stac_extensions, id, title, description, links
415
- collection.type = 'Collection';
416
-
417
- V.before('1.0.0-rc.1') && _.migrateExtensionShortnames(collection) && DONE;
418
-
419
- _.ensure(collection, 'license', 'proprietary') && DONE;
420
- _.ensure(collection, 'extent', {
421
- spatial: {
422
- bbox: []
423
- },
424
- temporal: {
425
- interval: []
426
- }
427
- }) && DONE;
428
-
429
- _.runAll(Collection, collection, collection);
430
-
431
- // Migrate Commons extension - part 3
432
- if (_.isObject(collection.properties)) {
433
- _.removeFromArray(collection, 'stac_extensions', 'commons') && DONE;
434
- delete collection.properties;
435
- }
436
-
437
- V.before('0.8.0') && _.populateExtensions(collection, 'collection') && DONE;
438
- V.before('1.0.0-beta.1') && _.mapValues(collection, 'stac_extensions', ['assets'], ['item-assets']) && DONE;
439
-
440
- return collection;
441
- },
442
-
443
- extent(collection) {
444
- _.ensure(collection, "extent", {});
445
-
446
- if (V.before('0.8.0')) {
447
- // Restructure spatial extent
448
- if (Array.isArray(collection.extent.spatial)) {
449
- collection.extent.spatial = {
450
- bbox: [
451
- collection.extent.spatial
452
- ]
453
- };
454
- }
455
- // Restructure temporal extent
456
- if (Array.isArray(collection.extent.temporal)) {
457
- collection.extent.temporal = {
458
- interval: [
459
- collection.extent.temporal
460
- ]
461
- };
462
- }
463
- }
464
-
465
- _.ensure(collection.extent, "spatial", {});
466
- _.ensure(collection.extent.spatial, "bbox", []);
467
- _.ensure(collection.extent, "temporal", {});
468
- _.ensure(collection.extent.temporal, "interval", []);
469
-
470
- if (V.before('1.0.0-rc.3')) {
471
- // The first extent in a Collection is always the overall extent, followed by more specific extents.
472
- if (collection.extent.temporal.interval.length > 1) {
473
- let min, max;
474
- for(let interval of collection.extent.temporal.interval) {
475
- if (interval[0] === null) {
476
- min = null;
477
- }
478
- else if (typeof interval[0] === 'string' && min !== null) {
479
- try {
480
- let start = new Date(interval[0]);
481
- if (typeof min === 'undefined' || start < min) {
482
- min = start;
483
- }
484
- } catch (error) {}
485
- }
486
-
487
- if (interval[1] === null) {
488
- max = null;
489
- }
490
- else if (typeof interval[1] === 'string' && max !== null) {
491
- try {
492
- let end = new Date(interval[1]);
493
- if (typeof max === 'undefined' || end > max) {
494
- max = end;
495
- }
496
- } catch (error) {}
497
- }
498
- }
499
- collection.extent.temporal.interval.unshift([
500
- min ? _.toISOString(min) : null,
501
- max ? _.toISOString(max) : null
502
- ]);
503
- }
504
- if (collection.extent.spatial.bbox.length > 1) {
505
- let count = collection.extent.spatial.bbox.reduce((val, bbox) => Array.isArray(bbox) ? Math.max(bbox.length, val) : val, 4);
506
- if (count >= 4) {
507
- let union = new Array(count).fill(null);
508
- let middle = count / 2;
509
- for(let bbox of collection.extent.spatial.bbox) {
510
- if (!Array.isArray(bbox) || bbox.length < 4) {
511
- break;
512
- }
513
- for(let i in bbox) {
514
- let c = bbox[i];
515
- if (union[i] === null) {
516
- union[i] = c;
517
- }
518
- else if (i < middle) {
519
- union[i] = Math.min(c, union[i]);
520
- }
521
- else {
522
- union[i] = Math.max(c, union[i]);
523
- }
524
-
525
- }
526
- }
527
- if (union.findIndex(c => c === null) === -1) {
528
- collection.extent.spatial.bbox.unshift(union);
529
- }
530
- }
531
- }
532
- }
533
- },
534
-
535
- collectionAssets(collection) {
536
- V.before('1.0.0-rc.1') && _.removeExtension(collection, 'collection-assets') && DONE;
537
-
538
- Asset.migrateAll(collection);
539
- },
540
-
541
- itemAsset(collection) {
542
- V.before('1.0.0-beta.2') && _.rename(collection, 'item_assets', 'assets');
543
-
544
- Asset.migrateAll(collection, 'item_assets');
545
- },
546
-
547
- summaries(collection) {
548
- _.ensure(collection, 'summaries', {});
549
-
550
- // other_properties: An early version of the Collection summaries with a similar structure
551
- // Was mostly used in standalone collections from openEO and GEE. Move other_properties to summaries.
552
- if (V.before('0.8.0') && _.isObject(collection.other_properties)) {
553
- for(let key in collection.other_properties) {
554
- let prop = collection.other_properties[key];
555
- if (Array.isArray(prop.extent) && prop.extent.length === 2) {
556
- collection.summaries[key] = {
557
- minimum: prop.extent[0],
558
- maximum: prop.extent[1],
559
- };
560
- }
561
- else if (Array.isArray(prop.values)) {
562
- if (prop.values.filter(v => Array.isArray(v)).length === prop.values.length) {
563
- // If it is an array of arrays, merge the arrays
564
- collection.summaries[key] = prop.values.reduce((a, b) => a.concat(b), []);
565
- }
566
- else {
567
- collection.summaries[key] = prop.values;
568
- }
569
- }
570
- }
571
- delete collection.other_properties;
572
- }
573
-
574
- // Migrate Commons extension - part 2
575
- // Move properties to (single element) summaries if the Collection is standalone
576
- // see also https://github.com/stac-utils/stac-migrate/issues/3
577
- if (V.before('1.0.0-beta.1') && _.isObject(collection.properties) && !collection.links.find(link => ['child', 'item'].includes(link.rel))) {
578
- for(let key in collection.properties) {
579
- let value = collection.properties[key];
580
- if (!Array.isArray(value)) {
581
- value = [value];
582
- }
583
- collection.summaries[key] = value;
584
- }
585
- }
586
-
587
- if (V.before('1.0.0-rc.1')) {
588
- _.mapObject(collection.summaries, val => {
589
- _.rename(val, 'min', 'minimum') && DONE;
590
- _.rename(val, 'max', 'maximum') && DONE;
591
- return val;
592
- });
593
- }
594
-
595
- // now we can work on all summaries and migrate them
596
- Fields.migrate(collection.summaries, collection, true);
597
-
598
- // Some fields should usually be on root-level if there's only one element
599
- _.moveTo(collection.summaries, 'sci:doi', collection, true) && _.addExtension(collection, SCHEMAS.scientific) && DONE;
600
- _.moveTo(collection.summaries, 'sci:publications', collection, true, true) && _.addExtension(collection, SCHEMAS.scientific) && DONE;
601
- _.moveTo(collection.summaries, 'sci:citation', collection, true) && _.addExtension(collection, SCHEMAS.scientific) && DONE;
602
- _.moveTo(collection.summaries, 'cube:dimensions', collection, true) && _.addExtension(collection, SCHEMAS.datacube) && DONE;
603
-
604
- // Remove summary field if empty
605
- if (Object.keys(collection.summaries).length === 0) {
606
- delete collection.summaries;
607
- }
608
- }
466
+ migrate(collection, updateVersionNumber = true) {
467
+ Catalog.migrate(collection, updateVersionNumber); // Migrates stac_version, stac_extensions, id, title, description, links
468
+ collection.type = 'Collection';
469
+
470
+ V.before('1.0.0-rc.1') && _.migrateExtensionShortnames(collection) && DONE;
471
+
472
+ _.ensure(collection, 'license', 'other') && DONE;
473
+ _.ensure(collection, 'extent', {
474
+ spatial: {
475
+ bbox: []
476
+ },
477
+ temporal: {
478
+ interval: []
479
+ }
480
+ }) && DONE;
481
+
482
+ _.runAll(Collection, collection, collection);
483
+
484
+ // Migrate Commons extension - part 3
485
+ if (_.isObject(collection.properties)) {
486
+ _.removeFromArray(collection, 'stac_extensions', 'commons') && DONE;
487
+ delete collection.properties;
488
+ }
489
+
490
+ V.before('0.8.0') && _.populateExtensions(collection, 'collection') && DONE;
491
+ V.before('1.0.0-beta.1') && _.mapValues(collection, 'stac_extensions', ['assets'], ['item-assets']) && DONE;
492
+
493
+ return collection;
494
+ },
495
+
496
+ extent(collection) {
497
+ _.ensure(collection, "extent", {});
498
+
499
+ if (V.before('0.8.0')) {
500
+ // Restructure spatial extent
501
+ if (Array.isArray(collection.extent.spatial)) {
502
+ collection.extent.spatial = {
503
+ bbox: [
504
+ collection.extent.spatial
505
+ ]
506
+ };
507
+ }
508
+ // Restructure temporal extent
509
+ if (Array.isArray(collection.extent.temporal)) {
510
+ collection.extent.temporal = {
511
+ interval: [
512
+ collection.extent.temporal
513
+ ]
514
+ };
515
+ }
516
+ }
517
+
518
+ _.ensure(collection.extent, "spatial", {});
519
+ _.ensure(collection.extent.spatial, "bbox", []);
520
+ _.ensure(collection.extent, "temporal", {});
521
+ _.ensure(collection.extent.temporal, "interval", []);
522
+
523
+ if (V.before('1.0.0-rc.3')) {
524
+ // The first extent in a Collection is always the overall extent, followed by more specific extents.
525
+ if (collection.extent.temporal.interval.length > 1) {
526
+ let min, max;
527
+ for (let interval of collection.extent.temporal.interval) {
528
+ if (interval[0] === null) {
529
+ min = null;
530
+ }
531
+ else if (typeof interval[0] === 'string' && min !== null) {
532
+ try {
533
+ let start = new Date(interval[0]);
534
+ if (typeof min === 'undefined' || start < min) {
535
+ min = start;
536
+ }
537
+ } catch (error) { }
538
+ }
539
+
540
+ if (interval[1] === null) {
541
+ max = null;
542
+ }
543
+ else if (typeof interval[1] === 'string' && max !== null) {
544
+ try {
545
+ let end = new Date(interval[1]);
546
+ if (typeof max === 'undefined' || end > max) {
547
+ max = end;
548
+ }
549
+ } catch (error) { }
550
+ }
551
+ }
552
+ collection.extent.temporal.interval.unshift([
553
+ min ? _.toISOString(min) : null,
554
+ max ? _.toISOString(max) : null
555
+ ]);
556
+ }
557
+ if (collection.extent.spatial.bbox.length > 1) {
558
+ let count = collection.extent.spatial.bbox.reduce((val, bbox) => Array.isArray(bbox) ? Math.max(bbox.length, val) : val, 4);
559
+ if (count >= 4) {
560
+ let union = new Array(count).fill(null);
561
+ let middle = count / 2;
562
+ for (let bbox of collection.extent.spatial.bbox) {
563
+ if (!Array.isArray(bbox) || bbox.length < 4) {
564
+ break;
565
+ }
566
+ for (let i in bbox) {
567
+ let c = bbox[i];
568
+ if (union[i] === null) {
569
+ union[i] = c;
570
+ }
571
+ else if (i < middle) {
572
+ union[i] = Math.min(c, union[i]);
573
+ }
574
+ else {
575
+ union[i] = Math.max(c, union[i]);
576
+ }
577
+
578
+ }
579
+ }
580
+ if (union.findIndex(c => c === null) === -1) {
581
+ collection.extent.spatial.bbox.unshift(union);
582
+ }
583
+ }
584
+ }
585
+ }
586
+ },
587
+
588
+ collectionAssets(collection) {
589
+ V.before('1.0.0-rc.1') && _.removeExtension(collection, 'collection-assets') && DONE;
590
+
591
+ Asset.migrateAll(collection);
592
+ },
593
+
594
+ itemAsset(collection) {
595
+ V.before('1.0.0-beta.2') && _.rename(collection, 'item_assets', 'assets');
596
+
597
+ _.removeExtension(collection, SCHEMAS['item-assets']) && DONE;
598
+
599
+ Asset.migrateAll(collection, 'item_assets');
600
+ },
601
+
602
+ summaries(collection) {
603
+ _.ensure(collection, 'summaries', {});
604
+
605
+ // other_properties: An early version of the Collection summaries with a similar structure
606
+ // Was mostly used in standalone collections from openEO and GEE. Move other_properties to summaries.
607
+ if (V.before('0.8.0') && _.isObject(collection.other_properties)) {
608
+ for (let key in collection.other_properties) {
609
+ let prop = collection.other_properties[key];
610
+ if (Array.isArray(prop.extent) && prop.extent.length === 2) {
611
+ collection.summaries[key] = {
612
+ minimum: prop.extent[0],
613
+ maximum: prop.extent[1],
614
+ };
615
+ }
616
+ else if (Array.isArray(prop.values)) {
617
+ if (prop.values.filter(v => Array.isArray(v)).length === prop.values.length) {
618
+ // If it is an array of arrays, merge the arrays
619
+ collection.summaries[key] = prop.values.reduce((a, b) => a.concat(b), []);
620
+ }
621
+ else {
622
+ collection.summaries[key] = prop.values;
623
+ }
624
+ }
625
+ }
626
+ delete collection.other_properties;
627
+ }
628
+
629
+ // Migrate Commons extension - part 2
630
+ // Move properties to (single element) summaries if the Collection is standalone
631
+ // see also https://github.com/stac-utils/stac-migrate/issues/3
632
+ if (V.before('1.0.0-beta.1') && _.isObject(collection.properties) && !collection.links.find(link => ['child', 'item'].includes(link.rel))) {
633
+ for (let key in collection.properties) {
634
+ let value = collection.properties[key];
635
+ if (!Array.isArray(value)) {
636
+ value = [value];
637
+ }
638
+ collection.summaries[key] = value;
639
+ }
640
+ }
641
+
642
+ if (V.before('1.0.0-rc.1')) {
643
+ _.mapObject(collection.summaries, val => {
644
+ _.rename(val, 'min', 'minimum') && DONE;
645
+ _.rename(val, 'max', 'maximum') && DONE;
646
+ return val;
647
+ });
648
+ }
649
+
650
+ // now we can work on all summaries and migrate them
651
+ Fields.migrate(collection.summaries, collection, true);
652
+
653
+ // Some fields should usually be on root-level if there's only one element
654
+ _.moveTo(collection.summaries, 'sci:doi', collection, true) && _.addExtension(collection, SCHEMAS.scientific) && DONE;
655
+ _.moveTo(collection.summaries, 'sci:publications', collection, true, true) && _.addExtension(collection, SCHEMAS.scientific) && DONE;
656
+ _.moveTo(collection.summaries, 'sci:citation', collection, true) && _.addExtension(collection, SCHEMAS.scientific) && DONE;
657
+ _.moveTo(collection.summaries, 'cube:dimensions', collection, true) && _.addExtension(collection, SCHEMAS.datacube) && DONE;
658
+
659
+ // Remove summary field if empty
660
+ if (Object.keys(collection.summaries).length === 0) {
661
+ delete collection.summaries;
662
+ }
663
+ }
609
664
 
610
665
  };
611
666
 
612
667
  var Item = {
613
668
 
614
- migrate(item, collection = null, updateVersionNumber = true) {
615
- V.set(item);
616
- if (updateVersionNumber) {
617
- item.stac_version = LATEST_VERSION;
618
- }
669
+ migrate(item, collection = null, updateVersionNumber = true) {
670
+ V.set(item);
671
+ if (updateVersionNumber) {
672
+ item.stac_version = LATEST_VERSION;
673
+ }
619
674
 
620
- _.ensure(item, 'stac_extensions', []) && DONE;
621
- V.before('1.0.0-rc.1') && _.migrateExtensionShortnames(item) && DONE;
675
+ _.ensure(item, 'stac_extensions', []) && DONE;
676
+ V.before('1.0.0-rc.1') && _.migrateExtensionShortnames(item) && DONE;
622
677
 
623
- _.ensure(item, 'id', '') && DONE;
624
- _.ensure(item, 'type', 'Feature') && DONE;
625
- if (!_.isObject(item.geometry)) {
626
- item.geometry = null;
627
- }
628
- if (item.geometry !== null) {
629
- _.ensure(item, 'bbox', []) && DONE;
630
- }
631
- _.ensure(item, 'properties', {}) && DONE;
632
- _.ensure(item, 'links', []) && DONE;
633
- _.ensure(item, 'assets', {}) && DONE;
678
+ _.ensure(item, 'id', '') && DONE;
679
+ _.ensure(item, 'type', 'Feature') && DONE;
680
+ if (!_.isObject(item.geometry)) {
681
+ item.geometry = null;
682
+ }
683
+ if (item.geometry !== null) {
684
+ _.ensure(item, 'bbox', []) && DONE;
685
+ }
686
+ _.ensure(item, 'properties', {}) && DONE;
687
+ _.ensure(item, 'links', []) && DONE;
688
+ _.ensure(item, 'assets', {}) && DONE;
634
689
 
635
- // Migrate Commons extension - part 1
636
- let commons = false;
637
- if (_.isObject(collection) && _.isObject(collection.properties)) {
638
- _.removeFromArray(item, 'stac_extensions', 'commons');
639
- item.properties = Object.assign({}, collection.properties, item.properties);
640
- commons = true;
641
- }
690
+ // Migrate Commons extension - part 1
691
+ let commons = false;
692
+ if (_.isObject(collection) && _.isObject(collection.properties)) {
693
+ _.removeFromArray(item, 'stac_extensions', 'commons');
694
+ item.properties = Object.assign({}, collection.properties, item.properties);
695
+ commons = true;
696
+ }
642
697
 
643
- _.runAll(Item, item, item);
698
+ _.runAll(Item, item, item);
644
699
 
645
- Fields.migrate(item.properties, item);
700
+ Fields.migrate(item.properties, item);
646
701
 
647
- Asset.migrateAll(item);
702
+ Asset.migrateAll(item);
648
703
 
649
- // Also populate extensions if commons has been implemented
650
- (V.before('0.8.0') || commons) && _.populateExtensions(item, 'item') && DONE;
704
+ // Also populate extensions if commons has been implemented
705
+ (V.before('0.8.0') || commons) && _.populateExtensions(item, 'item') && DONE;
651
706
 
652
- return item;
653
- }
707
+ return item;
708
+ }
654
709
 
655
710
  };
656
711
 
657
712
  var CollectionCollection = {
658
713
 
659
- migrate(object, updateVersionNumber = true) {
660
- _.ensure(object, 'collections', []) && DONE;
661
- _.ensure(object, 'links', []) && DONE;
714
+ migrate(object, updateVersionNumber = true) {
715
+ _.ensure(object, 'collections', []) && DONE;
716
+ _.ensure(object, 'links', []) && DONE;
662
717
 
663
- _.runAll(CollectionCollection, object, object);
718
+ _.runAll(CollectionCollection, object, object);
664
719
 
665
- object.collections = object.collections.map(collection => Collection.migrate(collection, updateVersionNumber));
720
+ object.collections = object.collections.map(collection => Collection.migrate(collection, updateVersionNumber));
666
721
 
667
- return object;
668
- },
722
+ return object;
723
+ },
669
724
 
670
725
  };
671
726
 
672
727
  var ItemCollection = {
673
728
 
674
- migrate(itemCollection, updateVersionNumber = true) {
675
- _.ensure(itemCollection, 'type', 'FeatureCollection') && DONE;
676
- _.ensure(itemCollection, 'features', []) && DONE;
677
- _.ensure(itemCollection, 'links', []) && DONE;
729
+ migrate(itemCollection, updateVersionNumber = true) {
730
+ _.ensure(itemCollection, 'type', 'FeatureCollection') && DONE;
731
+ _.ensure(itemCollection, 'features', []) && DONE;
732
+ _.ensure(itemCollection, 'links', []) && DONE;
678
733
 
679
- _.runAll(ItemCollection, itemCollection, itemCollection);
734
+ _.runAll(ItemCollection, itemCollection, itemCollection);
680
735
 
681
- itemCollection.features = itemCollection.features.map(feature => Item.migrate(feature, null, updateVersionNumber));
736
+ itemCollection.features = itemCollection.features.map(feature => Item.migrate(feature, null, updateVersionNumber));
682
737
 
683
- return itemCollection;
684
- },
738
+ return itemCollection;
739
+ },
685
740
 
686
741
  };
687
742
 
688
743
  var Asset = {
689
744
 
690
- migrateAll(context, field = 'assets') {
691
- for(let key in context[field]) {
692
- Asset.migrate(context[field][key], context);
693
- }
694
- },
695
-
696
- migrate(asset, context) {
697
- _.runAll(Asset, asset, context);
698
-
699
- Fields.migrate(asset, context);
700
-
701
- return asset;
702
- },
703
-
704
- mediaTypes(asset) {
705
- _.is(asset.type, 'string') && _.mapValues(
706
- asset, 'type',
707
- ['image/vnd.stac.geotiff', 'image/vnd.stac.geotiff; cloud-optimized=true'],
708
- ['image/tiff; application=geotiff', 'image/tiff; application=geotiff; profile=cloud-optimized']
709
- );
710
- },
711
-
712
- eo(asset, context) {
713
- let bands = _.isObject(context.properties) && Array.isArray(context.properties['eo:bands']) ? context.properties['eo:bands'] : [];
714
- if (Array.isArray(asset['eo:bands'])) {
715
- for(let i in asset['eo:bands']) {
716
- let band = asset['eo:bands'][i];
717
- if (_.is(band, 'number') && _.isObject(bands[band])) {
718
- band = bands[band];
719
- }
720
- else if (!_.isObject(band)) {
721
- band = {}; // "Fix" invalid band index
722
- }
723
- asset['eo:bands'][i] = band;
724
- }
725
- }
726
- }
745
+ migrateAll(context, field = 'assets') {
746
+ for (let key in context[field]) {
747
+ Asset.migrate(context[field][key], context);
748
+ }
749
+ },
727
750
 
728
- };
751
+ migrate(asset, context) {
752
+ _.runAll(Asset, asset, context);
729
753
 
730
- var Fields = {
754
+ Fields.migrate(asset, context);
731
755
 
732
- migrate(obj, context, summaries = false) {
733
- _.runAll(Fields, obj, context, summaries);
734
-
735
- return obj;
736
- },
737
-
738
- _commonMetadata(obj) {
739
- // Timestamps must be always in UTC
740
- // datetime, start_datetime and end_datetime already required UTC before
741
- if (V.before('1.0.0-rc.3')) {
742
- _.toUTC(obj, 'created') && DONE;
743
- _.toUTC(obj, 'updated') && DONE;
744
- }
745
- },
746
-
747
- _timestamps(obj, context) {
748
- // Timestamps must be always in UTC
749
- _.toUTC(obj, 'published') && DONE;
750
- _.toUTC(obj, 'expires') && DONE;
751
- _.toUTC(obj, 'unpublished') && DONE;
752
-
753
- _.upgradeExtension(context, SCHEMAS.timestamps);
754
- },
755
-
756
- _versioningIndicator(obj, context) {
757
- // Nothing to do
758
-
759
- _.upgradeExtension(context, SCHEMAS.version);
760
- },
761
-
762
- checksum(obj, context) {
763
- if (V.before('0.9.0') && Checksum.multihash) {
764
- _.rename(obj, 'checksum:md5', 'checksum:multihash') && Checksum.toMultihash(obj, 'checksum:multihash', 'md5') && DONE;
765
- _.rename(obj, 'checksum:sha1', 'checksum:multihash') && Checksum.toMultihash(obj, 'checksum:multihash', 'sha1') && DONE;
766
- // We assume sha2/3-256 although that may fail in some cases and other lengths are chosen
767
- // Never seen this implemtned in the wild, so let's try this until another use case comes up
768
- _.rename(obj, 'checksum:sha2', 'checksum:multihash') && Checksum.toMultihash(obj, 'checksum:multihash', 'sha2-256') && DONE;
769
- _.rename(obj, 'checksum:sha3', 'checksum:multihash') && Checksum.toMultihash(obj, 'checksum:multihash', 'sha3-256') && DONE;
770
- }
771
-
772
- V.before('1.0.0-rc.1') && _.rename(obj, 'checksum:multihash', 'file:checksum') && _.addExtension(context, SCHEMAS.file) && DONE;
773
-
774
- _.removeExtension(context, 'checksum');
775
- },
776
-
777
- classification(obj, context) {
778
- if (V.before('1.1.0', 'classification')) {
779
- _.forAll(obj, 'classification:classes', o => _.rename(o, 'color-hint', 'color_hint')) && DONE;
780
- }
781
-
782
- _.upgradeExtension(context, SCHEMAS.classification);
783
- },
784
-
785
- cube(obj, context) {
786
- // We'd need to convert proj strings to something else for v1.0 -> v2.0, but that's unfeasible here.
787
- // Nothing else to do here.
788
-
789
- _.upgradeExtension(context, SCHEMAS.datacube);
790
- },
791
-
792
- dtr(obj, context) {
793
- if (V.before('0.9.0')) {
794
- _.rename(obj, 'dtr:start_datetime', 'start_datetime') && DONE;
795
- _.rename(obj, 'dtr:end_datetime', 'end_datetime') && DONE;
796
- _.removeExtension(context, 'datetime-range') && DONE;
797
- }
798
- },
799
-
800
- eo(obj, context) {
801
- if (V.before('0.9.0')) {
802
- _.rename(obj, 'eo:epsg', 'proj:epsg') && _.addExtension(context, SCHEMAS.projection) && DONE;
803
- _.rename(obj, 'eo:platform', 'platform') && DONE;
804
- _.rename(obj, 'eo:instrument', 'instruments') && _.toArray(obj, 'instruments') && DONE;
805
- _.rename(obj, 'eo:constellation', 'constellation') && DONE;
806
- _.rename(obj, 'eo:off_nadir', 'view:off_nadir') && _.addExtension(context, SCHEMAS.view) && DONE;
807
- _.rename(obj, 'eo:azimuth', 'view:azimuth') && _.addExtension(context, SCHEMAS.view) && DONE;
808
- _.rename(obj, 'eo:incidence_angle', 'view:incidence_angle') && _.addExtension(context, SCHEMAS.view) && DONE;
809
- _.rename(obj, 'eo:sun_azimuth', 'view:sun_azimuth') && _.addExtension(context, SCHEMAS.view) && DONE;
810
- _.rename(obj, 'eo:sun_elevation', 'view:sun_elevation') && _.addExtension(context, SCHEMAS.view) && DONE;
811
- }
812
-
813
- V.before('1.0.0-beta.1') && _.rename(obj, 'eo:gsd', 'gsd') && DONE;
814
-
815
- _.upgradeExtension(context, SCHEMAS.eo);
816
- },
817
-
818
- file(obj, context) {
819
- // TODO: Migrate to v2.1.0 - https://github.com/stac-utils/stac-migrate/issues/7
820
-
821
- _.upgradeExtension(context, SCHEMAS.file);
822
- },
823
-
824
- label(obj, context) {
825
- // Migrate 0.8.0-rc1 non-pluralized forms
826
- if (V.before('0.8.0')) {
827
- _.rename(obj, 'label:property', 'label:properties') && DONE;
828
- _.rename(obj, 'label:task', 'label:tasks') && DONE;
829
- _.rename(obj, 'label:overview', 'label:overviews') && _.toArray(obj, 'label:overviews') && DONE;
830
- _.rename(obj, 'label:method', 'label:methods') && DONE;
831
- _.toArray(obj, 'label:classes') && DONE;
832
- }
833
-
834
- _.upgradeExtension(context, SCHEMAS.label);
835
- },
836
-
837
- pc(obj, context) {
838
- V.before('0.8.0') && _.rename(obj, 'pc:schema', 'pc:schemas') && DONE;
839
-
840
- _.upgradeExtension(context, SCHEMAS.pointcloud);
841
- },
842
-
843
- processing(obj, context) {
844
- // Nothing to do
845
-
846
- _.upgradeExtension(context, SCHEMAS.processing);
847
- },
848
-
849
- proj(obj, context) {
850
- // Nothing to do
851
-
852
- _.upgradeExtension(context, SCHEMAS.projection);
853
- },
854
-
855
- raster(obj, context) {
856
- // Nothing to do
857
-
858
- _.upgradeExtension(context, SCHEMAS.raster);
859
- },
860
-
861
- sar(obj, context, summary) {
862
- // Which version have they been (re)moved?
863
- _.rename(obj, 'sar:incidence_angle', 'view:incidence_angle') && _.addExtension(context, SCHEMAS.view) && DONE;
864
- _.rename(obj, 'sar:pass_direction', 'sat:orbit_state') && _.mapValues(obj, 'sat:orbit_state', [null], ['geostationary']) && _.addExtension(context, SCHEMAS.sat) && DONE;
865
-
866
- if (V.before('0.7.0')) {
867
- _.flattenArray(obj, 'sar:resolution', ['sar:resolution_range', 'sar:resolution_azimuth'], summary) && DONE;
868
- _.flattenArray(obj, 'sar:pixel_spacing', ['sar:pixel_spacing_range', 'sar:pixel_spacing_azimuth'], summary) && DONE;
869
- _.flattenArray(obj, 'sar:looks', ['sar:looks_range', 'sar:looks_azimuth', 'sar:looks_equivalent_number'], summary) && DONE;
870
- _.rename(obj, 'sar:off_nadir', 'view:off_nadir') && _.addExtension(context, SCHEMAS.view) && DONE;
871
- }
872
-
873
- if (V.before('0.9.0')) {
874
- _.rename(obj, 'sar:platform', 'platform') && DONE;
875
- _.rename(obj, 'sar:instrument', 'instruments') && _.toArray(obj, 'instruments') && DONE;
876
- _.rename(obj, 'sar:constellation', 'constellation') && DONE;
877
- _.rename(obj, 'sar:type', 'sar:product_type') && DONE;
878
- _.rename(obj, 'sar:polarization', 'sar:polarizations') && DONE;
879
- _.flattenOneElementArray(obj, 'sar:absolute_orbit', summary) && _.rename(obj, 'sar:absolute_orbit', 'sat:absolute_orbit') && _.addExtension(context, SCHEMAS.sat) && DONE;
880
- _.flattenOneElementArray(obj, 'sar:relative_orbit', summary) && _.rename(obj, 'sar:relative_orbit', 'sat:relative_orbit') && _.addExtension(context, SCHEMAS.sat) && DONE;
881
- }
882
-
883
- _.upgradeExtension(context, SCHEMAS.sar);
884
- },
885
-
886
- sat(obj, context) {
887
- // Migrate 0.9.0-rc _angle suffixes
888
- if (V.before('0.9.0')) {
889
- _.rename(obj, 'sat:off_nadir_angle', 'sat:off_nadir') && DONE;
890
- _.rename(obj, 'sat:azimuth_angle', 'sat:azimuth') && DONE;
891
- _.rename(obj, 'sat:sun_azimuth_angle', 'sat:sun_azimuth') && DONE;
892
- _.rename(obj, 'sat:sun_elevation_angle', 'sat:sun_elevation') && DONE;
893
- }
894
-
895
- _.upgradeExtension(context, SCHEMAS.sat);
896
- },
897
-
898
- sci(obj, context) {
899
- // Nothing to do
900
-
901
- _.upgradeExtension(context, SCHEMAS.scientific);
902
- },
903
-
904
- item(obj) { // Single Item
905
- if (V.before('0.8.0')) {
906
- _.rename(obj, 'item:license', 'license') && DONE;
907
- _.rename(obj, 'item:providers', 'providers') && DONE;
908
- // No need to remove the extension from stac_extensions as it was not available before 0.8.0
909
- }
910
- },
756
+ return asset;
757
+ },
911
758
 
912
- table(obj, context) {
913
- // Nothing to do
759
+ mediaTypes(asset) {
760
+ _.is(asset.type, 'string') && _.mapValues(
761
+ asset, 'type',
762
+ ['image/vnd.stac.geotiff', 'image/vnd.stac.geotiff; cloud-optimized=true'],
763
+ ['image/tiff; application=geotiff', 'image/tiff; application=geotiff; profile=cloud-optimized']
764
+ );
765
+ }
766
+
767
+ };
914
768
 
915
- _.upgradeExtension(context, SCHEMAS.table);
916
- },
769
+ var Band = {
770
+
771
+ migrateAll(obj, context) {
772
+
773
+ if (V.before('1.0.0')) { // Not sure when the index-based bands were removed
774
+ const bands = _.isObject(context.properties) && Array.isArray(context.properties.bands) ? context.properties.bands : [];
775
+ if (Array.isArray(obj['eo:bands'])) {
776
+ for (let i in obj['eo:bands']) {
777
+ let band = obj['eo:bands'][i];
778
+ if (_.is(band, 'number') && _.isObject(bands[band])) {
779
+ band = bands[band];
780
+ }
781
+ if (!_.isObject(band)) {
782
+ band = {}; // "Fix" invalid band index
783
+ }
784
+ obj['eo:bands'][i] = band;
785
+ }
786
+ }
787
+ }
788
+
789
+ if (V.before("1.1.0-beta.1") && (Array.isArray(obj["raster:bands"]) || Array.isArray(obj["eo:bands"]))) {
790
+ _.ensure(obj, "bands", []);
791
+
792
+ const raster = obj["raster:bands"] || [];
793
+ const eo = obj["eo:bands"] || [];
794
+ const length = Math.max(raster.length, eo.length);
795
+ for (let i = 0; i < length; i++) {
796
+ _.ensure(obj.bands, i, {});
797
+ Object.assign(obj.bands[i], raster[i], eo[i]);
798
+ obj.bands[i] = Band.migrate(obj.bands[i], context);
799
+ }
800
+
801
+ delete obj["raster:bands"];
802
+ delete obj["eo:bands"];
803
+ }
804
+
805
+ },
806
+
807
+ migrate(band, context) {
808
+ _.runAll(Band, band, context);
809
+
810
+ Fields.migrate(band, context);
811
+
812
+ return band;
813
+ },
814
+
815
+ eo(band) {
816
+ if (V.before('2.0.0-beta.1', 'eo')) {
817
+ _.rename(band, 'common_name', 'eo:common_name') && DONE;
818
+ _.rename(band, 'center_wavelength', 'eo:center_wavelength') && DONE;
819
+ _.rename(band, 'full_width_half_max', 'eo:full_width_half_max') && DONE;
820
+ _.rename(band, 'solar_illumination', 'eo:solar_illumination') && DONE;
821
+ }
822
+ },
823
+
824
+ raster(band) {
825
+ if (V.before('2.0.0-beta.1', 'raster')) {
826
+ _.rename(band, 'sampling', 'raster:sampling') && DONE;
827
+ _.rename(band, 'bits_per_sample', 'raster:bits_per_sample') && DONE;
828
+ _.rename(band, 'spatial_resolution', 'raster:spatial_resolution') && DONE;
829
+ _.rename(band, 'scale', 'raster:scale') && DONE;
830
+ _.rename(band, 'offset', 'raster:offset') && DONE;
831
+ _.rename(band, 'histogram', 'raster:histogram') && DONE;
832
+ }
833
+ }
834
+
835
+ };
836
+
837
+ var Fields = {
917
838
 
918
- view(obj, context) {
919
- // Nothing to do
920
-
921
- _.upgradeExtension(context, SCHEMAS.view);
922
- }
839
+ migrate(obj, context, summaries = false) {
840
+ _.runAll(Fields, obj, context, summaries);
841
+
842
+ return obj;
843
+ },
844
+
845
+ _commonMetadata(obj, context) {
846
+ // Timestamps must be always in UTC
847
+ // datetime, start_datetime and end_datetime already required UTC before
848
+ if (V.before('1.0.0-rc.3')) {
849
+ _.toUTC(obj, 'created') && DONE;
850
+ _.toUTC(obj, 'updated') && DONE;
851
+ }
852
+
853
+ Band.migrateAll(obj, context);
854
+ },
855
+
856
+ _timestamps(obj, context) {
857
+ // Timestamps must be always in UTC
858
+ _.toUTC(obj, 'published') && DONE;
859
+ _.toUTC(obj, 'expires') && DONE;
860
+ _.toUTC(obj, 'unpublished') && DONE;
861
+
862
+ _.upgradeExtension(context, SCHEMAS.timestamps);
863
+ },
864
+
865
+ _versioningIndicator(obj, context) {
866
+ // Nothing to do
867
+
868
+ _.upgradeExtension(context, SCHEMAS.version);
869
+ },
870
+
871
+ checksum(obj, context) {
872
+ if (V.before('0.9.0') && Checksum.multihash) {
873
+ _.rename(obj, 'checksum:md5', 'checksum:multihash') && Checksum.toMultihash(obj, 'checksum:multihash', 'md5') && DONE;
874
+ _.rename(obj, 'checksum:sha1', 'checksum:multihash') && Checksum.toMultihash(obj, 'checksum:multihash', 'sha1') && DONE;
875
+ // We assume sha2/3-256 although that may fail in some cases and other lengths are chosen
876
+ // Never seen this implemtned in the wild, so let's try this until another use case comes up
877
+ _.rename(obj, 'checksum:sha2', 'checksum:multihash') && Checksum.toMultihash(obj, 'checksum:multihash', 'sha2-256') && DONE;
878
+ _.rename(obj, 'checksum:sha3', 'checksum:multihash') && Checksum.toMultihash(obj, 'checksum:multihash', 'sha3-256') && DONE;
879
+ }
880
+
881
+ V.before('1.0.0-rc.1') && _.rename(obj, 'checksum:multihash', 'file:checksum') && _.addExtension(context, SCHEMAS.file) && DONE;
882
+
883
+ _.removeExtension(context, 'checksum');
884
+ },
885
+
886
+ classification(obj, context) {
887
+ if (V.before('1.1.0', 'classification')) {
888
+ _.forAll(obj, 'classification:classes', o => _.rename(o, 'color-hint', 'color_hint')) && DONE;
889
+ }
890
+ if (V.before('2.0.0', 'classification')) {
891
+ _.forAll(obj, 'classification:classes', o => _.ensure(o, 'name', o.description)) && DONE;
892
+ }
893
+
894
+ _.upgradeExtension(context, SCHEMAS.classification);
895
+ },
896
+
897
+ cube(obj, context) {
898
+ // We'd need to convert proj strings to something else for v1.0 -> v2.0, but that's unfeasible here.
899
+ // Nothing else to do here.
900
+
901
+ _.upgradeExtension(context, SCHEMAS.datacube);
902
+ },
903
+
904
+ dtr(obj, context) {
905
+ if (V.before('0.9.0')) {
906
+ _.rename(obj, 'dtr:start_datetime', 'start_datetime') && DONE;
907
+ _.rename(obj, 'dtr:end_datetime', 'end_datetime') && DONE;
908
+ _.removeExtension(context, 'datetime-range') && DONE;
909
+ }
910
+ },
911
+
912
+ eo(obj, context) {
913
+ if (V.before('0.9.0')) {
914
+ _.rename(obj, 'eo:epsg', 'proj:epsg') && _.addExtension(context, SCHEMAS.projection) && DONE;
915
+ _.rename(obj, 'eo:platform', 'platform') && DONE;
916
+ _.rename(obj, 'eo:instrument', 'instruments') && _.toArray(obj, 'instruments') && DONE;
917
+ _.rename(obj, 'eo:constellation', 'constellation') && DONE;
918
+ _.rename(obj, 'eo:off_nadir', 'view:off_nadir') && _.addExtension(context, SCHEMAS.view) && DONE;
919
+ _.rename(obj, 'eo:azimuth', 'view:azimuth') && _.addExtension(context, SCHEMAS.view) && DONE;
920
+ _.rename(obj, 'eo:incidence_angle', 'view:incidence_angle') && _.addExtension(context, SCHEMAS.view) && DONE;
921
+ _.rename(obj, 'eo:sun_azimuth', 'view:sun_azimuth') && _.addExtension(context, SCHEMAS.view) && DONE;
922
+ _.rename(obj, 'eo:sun_elevation', 'view:sun_elevation') && _.addExtension(context, SCHEMAS.view) && DONE;
923
+ }
924
+
925
+ V.before('1.0.0-beta.1') && _.rename(obj, 'eo:gsd', 'gsd') && DONE;
926
+
927
+ _.upgradeExtension(context, SCHEMAS.eo);
928
+ },
929
+
930
+ file(obj, context, summaries) {
931
+ _.rename(obj, 'file:bits_per_sample', 'raster:bits_per_sample') && _.addExtension(context, SCHEMAS.raster) && DONE;
932
+ _.rename(obj, 'file:data_type', 'data_type') && _.addExtension(context, SCHEMAS.raster) && DONE;
933
+ _.rename(obj, 'file:unit', 'unit') && _.addExtension(context, SCHEMAS.raster) && DONE;
934
+
935
+ if (Array.isArray(obj['file:nodata']) && obj['file:nodata'].length > 1) {
936
+ // In case of more than one no-data value we need to create a custom property
937
+ // as there's no alternative for multiple no-data values yet
938
+ _.copy(obj, 'file:nodata', 'nodata:values') && DONE;
939
+ }
940
+ _.rename(obj, 'file:nodata', 'nodata') && !summaries && _.pickFirst(obj, 'nodata') && DONE;
941
+ _.upgradeExtension(context, SCHEMAS.file);
942
+ },
943
+
944
+ label(obj, context) {
945
+ // Migrate 0.8.0-rc1 non-pluralized forms
946
+ if (V.before('0.8.0')) {
947
+ _.rename(obj, 'label:property', 'label:properties') && DONE;
948
+ _.rename(obj, 'label:task', 'label:tasks') && DONE;
949
+ _.rename(obj, 'label:overview', 'label:overviews') && _.toArray(obj, 'label:overviews') && DONE;
950
+ _.rename(obj, 'label:method', 'label:methods') && DONE;
951
+ _.toArray(obj, 'label:classes') && DONE;
952
+ }
953
+
954
+ _.upgradeExtension(context, SCHEMAS.label);
955
+ },
956
+
957
+ pc(obj, context) {
958
+ V.before('0.8.0') && _.rename(obj, 'pc:schema', 'pc:schemas') && DONE;
959
+
960
+ _.upgradeExtension(context, SCHEMAS.pointcloud);
961
+ },
962
+
963
+ processing(obj, context) {
964
+ // Nothing to do
965
+
966
+ _.upgradeExtension(context, SCHEMAS.processing);
967
+ },
968
+
969
+ proj(obj, context) {
970
+ _.rename(obj, 'proj:epsg', 'proj:code') && _.formatString(obj, 'proj:code', 'EPSG:{}') && DONE;
971
+
972
+ _.upgradeExtension(context, SCHEMAS.projection);
973
+ },
974
+
975
+ raster(obj, context) {
976
+ _.upgradeExtension(context, SCHEMAS.raster);
977
+ },
978
+
979
+ sar(obj, context, summary) {
980
+ // Which version have they been (re)moved?
981
+ _.rename(obj, 'sar:incidence_angle', 'view:incidence_angle') && _.addExtension(context, SCHEMAS.view) && DONE;
982
+ _.rename(obj, 'sar:pass_direction', 'sat:orbit_state') && _.mapValues(obj, 'sat:orbit_state', [null], ['geostationary']) && _.addExtension(context, SCHEMAS.sat) && DONE;
983
+
984
+ if (V.before('0.7.0')) {
985
+ _.flattenArray(obj, 'sar:resolution', ['sar:resolution_range', 'sar:resolution_azimuth'], summary) && DONE;
986
+ _.flattenArray(obj, 'sar:pixel_spacing', ['sar:pixel_spacing_range', 'sar:pixel_spacing_azimuth'], summary) && DONE;
987
+ _.flattenArray(obj, 'sar:looks', ['sar:looks_range', 'sar:looks_azimuth', 'sar:looks_equivalent_number'], summary) && DONE;
988
+ _.rename(obj, 'sar:off_nadir', 'view:off_nadir') && _.addExtension(context, SCHEMAS.view) && DONE;
989
+ }
990
+
991
+ if (V.before('0.9.0')) {
992
+ _.rename(obj, 'sar:platform', 'platform') && DONE;
993
+ _.rename(obj, 'sar:instrument', 'instruments') && _.toArray(obj, 'instruments') && DONE;
994
+ _.rename(obj, 'sar:constellation', 'constellation') && DONE;
995
+ _.rename(obj, 'sar:type', 'sar:product_type') && DONE;
996
+ _.rename(obj, 'sar:polarization', 'sar:polarizations') && DONE;
997
+ _.flattenOneElementArray(obj, 'sar:absolute_orbit', summary) && _.rename(obj, 'sar:absolute_orbit', 'sat:absolute_orbit') && _.addExtension(context, SCHEMAS.sat) && DONE;
998
+ _.flattenOneElementArray(obj, 'sar:relative_orbit', summary) && _.rename(obj, 'sar:relative_orbit', 'sat:relative_orbit') && _.addExtension(context, SCHEMAS.sat) && DONE;
999
+ }
1000
+
1001
+ _.upgradeExtension(context, SCHEMAS.sar);
1002
+ },
1003
+
1004
+ sat(obj, context) {
1005
+ // Migrate 0.9.0-rc _angle suffixes
1006
+ if (V.before('0.9.0')) {
1007
+ _.rename(obj, 'sat:off_nadir_angle', 'sat:off_nadir') && DONE;
1008
+ _.rename(obj, 'sat:azimuth_angle', 'sat:azimuth') && DONE;
1009
+ _.rename(obj, 'sat:sun_azimuth_angle', 'sat:sun_azimuth') && DONE;
1010
+ _.rename(obj, 'sat:sun_elevation_angle', 'sat:sun_elevation') && DONE;
1011
+ }
1012
+
1013
+ _.upgradeExtension(context, SCHEMAS.sat);
1014
+ },
1015
+
1016
+ sci(obj, context) {
1017
+ // Nothing to do
1018
+
1019
+ _.upgradeExtension(context, SCHEMAS.scientific);
1020
+ },
1021
+
1022
+ item(obj) { // Single Item
1023
+ if (V.before('0.8.0')) {
1024
+ _.rename(obj, 'item:license', 'license') && DONE;
1025
+ _.rename(obj, 'item:providers', 'providers') && DONE;
1026
+ // No need to remove the extension from stac_extensions as it was not available before 0.8.0
1027
+ }
1028
+ },
1029
+
1030
+ table(obj, context) {
1031
+ // Nothing to do
1032
+
1033
+ _.upgradeExtension(context, SCHEMAS.table);
1034
+ },
1035
+
1036
+ view(obj, context) {
1037
+ // Nothing to do
1038
+
1039
+ _.upgradeExtension(context, SCHEMAS.view);
1040
+ }
923
1041
 
924
1042
  };
925
1043
 
926
1044
  var Migrate = {
927
1045
 
928
- item(item, collection = null, updateVersionNumber = true) {
929
- return Item.migrate(item, collection, updateVersionNumber);
930
- },
931
-
932
- catalog(catalog, updateVersionNumber = true) {
933
- return Catalog.migrate(catalog, updateVersionNumber);
934
- },
935
-
936
- collection(collection, updateVersionNumber = true) {
937
- return Collection.migrate(collection, updateVersionNumber);
938
- },
939
-
940
- collectionCollection(collections, updateVersionNumber = true) {
941
- return CollectionCollection.migrate(collections, updateVersionNumber);
942
- },
943
-
944
- itemCollection(itemCollection, updateVersionNumber = true) {
945
- return ItemCollection.migrate(itemCollection, updateVersionNumber);
946
- },
947
-
948
- stac(object, updateVersionNumber = true) {
949
- if (object.type === 'Feature') {
950
- return Migrate.item(object, null, updateVersionNumber);
951
- }
952
- else if (object.type === 'FeatureCollection') {
953
- return Migrate.itemCollection(object, updateVersionNumber);
954
- }
955
- else if (object.type === 'Collection' || (!object.type && _.isDefined(object.extent) && _.isDefined(object.license))) {
956
- return Migrate.collection(object, updateVersionNumber);
957
- }
958
- else if (!object.type && Array.isArray(object.collections)) {
959
- return Migrate.collectionCollection(object, updateVersionNumber);
960
- }
961
- else {
962
- return Migrate.catalog(object, updateVersionNumber);
963
- }
964
- },
965
-
966
- enableMultihash(multihash) {
967
- Checksum.multihash = multihash;
968
- }
1046
+ item(item, collection = null, updateVersionNumber = true) {
1047
+ return Item.migrate(item, collection, updateVersionNumber);
1048
+ },
1049
+
1050
+ catalog(catalog, updateVersionNumber = true) {
1051
+ return Catalog.migrate(catalog, updateVersionNumber);
1052
+ },
1053
+
1054
+ collection(collection, updateVersionNumber = true) {
1055
+ return Collection.migrate(collection, updateVersionNumber);
1056
+ },
1057
+
1058
+ collectionCollection(collections, updateVersionNumber = true) {
1059
+ return CollectionCollection.migrate(collections, updateVersionNumber);
1060
+ },
1061
+
1062
+ itemCollection(itemCollection, updateVersionNumber = true) {
1063
+ return ItemCollection.migrate(itemCollection, updateVersionNumber);
1064
+ },
1065
+
1066
+ stac(object, updateVersionNumber = true) {
1067
+ if (object.type === 'Feature') {
1068
+ return Migrate.item(object, null, updateVersionNumber);
1069
+ }
1070
+ else if (object.type === 'FeatureCollection') {
1071
+ return Migrate.itemCollection(object, updateVersionNumber);
1072
+ }
1073
+ else if (object.type === 'Collection' || (!object.type && _.isDefined(object.extent) && _.isDefined(object.license))) {
1074
+ return Migrate.collection(object, updateVersionNumber);
1075
+ }
1076
+ else if (!object.type && Array.isArray(object.collections)) {
1077
+ return Migrate.collectionCollection(object, updateVersionNumber);
1078
+ }
1079
+ else {
1080
+ return Migrate.catalog(object, updateVersionNumber);
1081
+ }
1082
+ },
1083
+
1084
+ enableMultihash(multihash) {
1085
+ Checksum.multihash = multihash;
1086
+ }
969
1087
 
970
1088
  };
971
1089