mapshaper 0.6.23 → 0.6.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/mapshaper.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
 
3
- var VERSION = "0.6.23";
3
+ var VERSION = "0.6.24";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -10620,7 +10620,8 @@
10620
10620
  buf = new Uint8Array(buf);
10621
10621
  }
10622
10622
  opts = opts || {};
10623
- var out = await utils.promisify(gunzip)(buf, opts);
10623
+ var gunzip$1 = runningInBrowser() ? utils.promisify(gunzip) : utils.promisify(require('zlib').gunzip);
10624
+ var out = await gunzip$1(buf, opts);
10624
10625
  if (opts.filename && !isImportableAsBinary(opts.filename)) {
10625
10626
  out = strFromU8(out);
10626
10627
  }
@@ -10628,8 +10629,6 @@
10628
10629
  }
10629
10630
 
10630
10631
  function gunzipSync(buf, filename) {
10631
- // TODO: use native module in Node
10632
- // require('zlib').
10633
10632
  if (buf instanceof ArrayBuffer) {
10634
10633
  buf = new Uint8Array(buf);
10635
10634
  }
@@ -10768,16 +10767,39 @@
10768
10767
  // gui: (optional) gui instance
10769
10768
  //
10770
10769
  async function exportDatasetsToPack(datasets, opts) {
10771
- return {
10770
+ var obj = {
10772
10771
  version: 1,
10773
10772
  created: (new Date).toISOString(),
10774
- datasets: await Promise.all(datasets.map(d => exportDataset(d, opts || {})))
10773
+ datasets: await Promise.all(datasets.map(exportDataset))
10775
10774
  };
10775
+ if (opts.compact) {
10776
+ await applyCompression(obj);
10777
+ }
10778
+ return obj;
10779
+ }
10780
+
10781
+ async function applyCompression(obj, opts) {
10782
+ var promises = [];
10783
+ obj.datasets.forEach(d => {
10784
+ if (d.arcs) promises.push(compressArcs(d.arcs, opts));
10785
+ });
10786
+ await Promise.all(promises);
10787
+ }
10788
+
10789
+ async function compressArcs(obj, opts) {
10790
+ var gzipOpts = Object.assign({level: 1, consume: false}, opts);
10791
+ var promises = [gzipAsync(obj.nn, gzipOpts), gzipAsync(obj.xx, gzipOpts), gzipAsync(obj.yy, gzipOpts)];
10792
+ if (obj.zz) promises.push(gzipAsync(obj.zz, gzipOpts));
10793
+ var results = await Promise.all(promises);
10794
+ obj.nn = results.shift();
10795
+ obj.xx = results.shift();
10796
+ obj.yy = results.shift();
10797
+ if (obj.zz) obj.zz = results.shift();
10776
10798
  }
10777
10799
 
10778
10800
  async function exportDataset(dataset, opts) {
10779
10801
  return {
10780
- arcs: dataset.arcs ? await exportArcs(dataset.arcs, opts) : null,
10802
+ arcs: dataset.arcs ? exportArcs(dataset.arcs) : null,
10781
10803
  info: dataset.info ? exportInfo(dataset.info) : null,
10782
10804
  layers: await Promise.all((dataset.layers || []).map(exportLayer))
10783
10805
  };
@@ -10787,38 +10809,22 @@
10787
10809
  return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
10788
10810
  }
10789
10811
 
10790
- async function exportArcs(arcs, opts) {
10812
+ function exportArcs(arcs) {
10791
10813
  var data = arcs.getVertexData();
10792
- var obj = {
10814
+ return {
10793
10815
  nn: typedArrayToBuffer(data.nn),
10794
10816
  xx: typedArrayToBuffer(data.xx),
10795
10817
  yy: typedArrayToBuffer(data.yy),
10796
10818
  zz: data.zz ? typedArrayToBuffer(data.zz) : null,
10797
10819
  zlimit: arcs.getRetainedInterval()
10798
10820
  };
10799
-
10800
- // gzipping typically sees about 70% compression on unrounded coordinates
10801
- // -- possibly not worth the time
10802
- if (opts.compact) {
10803
- var gzipOpts = {level: 1, consume: false};
10804
- var promises = [gzipAsync(obj.nn, gzipOpts), gzipAsync(obj.xx, gzipOpts), gzipAsync(obj.yy, gzipOpts)];
10805
- if (obj.zz) promises.push(gzipAsync(obj.zz, gzipOpts));
10806
- var results = await Promise.all(promises);
10807
- obj.nn = results.shift();
10808
- obj.xx = results.shift();
10809
- obj.yy = results.shift();
10810
- if (obj.zz) obj.zz = results.shift();
10811
- }
10812
- return obj;
10813
10821
  }
10814
10822
 
10815
10823
  async function exportLayer(lyr) {
10816
- // console.time('table')
10817
10824
  var data = null;
10818
10825
  if (lyr.data) {
10819
10826
  data = await exportTable2(lyr.data);
10820
10827
  }
10821
- // console.timeEnd('table')
10822
10828
  return {
10823
10829
  name: lyr.name || null,
10824
10830
  geometry_type: lyr.geometry_type || null,
@@ -10845,6 +10851,7 @@
10845
10851
  exportPackedDatasets: exportPackedDatasets,
10846
10852
  pack: pack,
10847
10853
  exportDatasetsToPack: exportDatasetsToPack,
10854
+ applyCompression: applyCompression,
10848
10855
  exportDataset: exportDataset
10849
10856
  });
10850
10857
 
@@ -24922,6 +24929,9 @@ ${svg}
24922
24929
 
24923
24930
  parser.command('info')
24924
24931
  .describe('print information about data layers')
24932
+ .option('save-to', {
24933
+ describe: 'name of file to save info in JSON format'
24934
+ })
24925
24935
  .option('target', targetOpt);
24926
24936
 
24927
24937
  parser.command('inspect')
@@ -27457,12 +27467,14 @@ ${svg}
27457
27467
  // Import datasets contained in a BSON blob
27458
27468
  // Return command target as a dataset
27459
27469
  //
27460
- async function unpackSession(buf) {
27461
- var obj = unpack(buf, {});
27470
+ async function unpackSessionData(buf) {
27471
+ return restoreSessionData(unpack(buf, {}));
27472
+ }
27473
+
27474
+ async function restoreSessionData(obj) {
27462
27475
  if (!isValidSession(obj)) {
27463
27476
  stop('Invalid mapshaper session data object');
27464
27477
  }
27465
-
27466
27478
  var datasets = await Promise.all(obj.datasets.map(importDataset));
27467
27479
  return Object.assign(obj, {datasets: datasets});
27468
27480
  }
@@ -27539,7 +27551,8 @@ ${svg}
27539
27551
 
27540
27552
  var Unpack = /*#__PURE__*/Object.freeze({
27541
27553
  __proto__: null,
27542
- unpackSession: unpackSession
27554
+ unpackSessionData: unpackSessionData,
27555
+ restoreSessionData: restoreSessionData
27543
27556
  });
27544
27557
 
27545
27558
  cmd.importFiles = async function(catalog, opts) {
@@ -27593,7 +27606,7 @@ ${svg}
27593
27606
 
27594
27607
  async function importMshpFile(file, catalog, opts) {
27595
27608
  var buf = cli.readFile(file, null, opts.input);
27596
- var obj = await unpackSession(buf);
27609
+ var obj = await unpackSessionData(buf);
27597
27610
  obj.datasets.forEach(catalog.addDataset, catalog);
27598
27611
  return obj.target;
27599
27612
  }
@@ -38240,55 +38253,27 @@ ${svg}
38240
38253
 
38241
38254
  var MAX_RULE_LEN = 50;
38242
38255
 
38243
- cmd.printInfo = function(layers) {
38244
- var str = '';
38245
- layers.forEach(function(o, i) {
38246
- var title = 'Layer: ' + (o.layer.name || '[unnamed layer]');
38247
- var tableStr = getAttributeTableInfo(o.layer);
38248
- var tableWidth = measureLongestLine(tableStr);
38249
- var ruleLen = Math.min(Math.max(title.length, tableWidth), MAX_RULE_LEN);
38250
- str += '\n';
38251
- str += utils.lpad('', ruleLen, '=') + '\n';
38252
- str += title + '\n';
38253
- str += utils.lpad('', ruleLen, '-') + '\n';
38254
- str += getLayerInfo(o.layer, o.dataset);
38255
- str += tableStr;
38256
+ cmd.info = function(targets, opts) {
38257
+ var layers = expandCommandTargets(targets);
38258
+ var arr = layers.map(function(o) {
38259
+ return getLayerInfo(o.layer, o.dataset);
38256
38260
  });
38257
- message(str);
38258
- };
38259
-
38260
- function measureLongestLine(str) {
38261
- return Math.max.apply(null, str.split('\n').map(function(line) {return stringDisplayWidth(line);}));
38262
- }
38263
-
38264
- function stringDisplayWidth(str) {
38265
- var w = 0;
38266
- for (var i = 0, n=str.length; i < n; i++) {
38267
- w += charDisplayWidth(str.charCodeAt(i));
38261
+ message(formatInfo(arr));
38262
+ if (opts.save_to) {
38263
+ var output = [{
38264
+ filename: opts.save_to + (opts.save_to.endsWith('.json') ? '' : '.json'),
38265
+ content: JSON.stringify(arr, null, 2)
38266
+ }];
38267
+ writeFiles(output, opts);
38268
38268
  }
38269
- return w;
38270
- }
38269
+ };
38271
38270
 
38272
- // see https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
38273
- // this is a simplified version, focusing on double-width CJK chars and ignoring nonprinting etc chars
38274
- function charDisplayWidth(c) {
38275
- if (c >= 0x1100 &&
38276
- (c <= 0x115f || c == 0x2329 || c == 0x232a ||
38277
- (c >= 0x2e80 && c <= 0xa4cf && c != 0x303f) || /* CJK ... Yi */
38278
- (c >= 0xac00 && c <= 0xd7a3) || /* Hangul Syllables */
38279
- (c >= 0xf900 && c <= 0xfaff) || /* CJK Compatibility Ideographs */
38280
- (c >= 0xfe10 && c <= 0xfe19) || /* Vertical forms */
38281
- (c >= 0xfe30 && c <= 0xfe6f) || /* CJK Compatibility Forms */
38282
- (c >= 0xff00 && c <= 0xff60) || /* Fullwidth Forms */
38283
- (c >= 0xffe0 && c <= 0xffe6) ||
38284
- (c >= 0x20000 && c <= 0x2fffd) ||
38285
- (c >= 0x30000 && c <= 0x3fffd))) return 2;
38286
- return 1;
38287
- }
38271
+ cmd.printInfo = cmd.info; // old name
38288
38272
 
38289
- function getLayerData(lyr, dataset) {
38273
+ function getLayerInfo(lyr, dataset) {
38290
38274
  var n = getFeatureCount(lyr);
38291
38275
  var o = {
38276
+ layer_name: lyr.name,
38292
38277
  geometry_type: lyr.geometry_type,
38293
38278
  feature_count: n,
38294
38279
  null_shape_count: 0,
@@ -38296,31 +38281,47 @@ ${svg}
38296
38281
  };
38297
38282
  if (lyr.shapes && lyr.shapes.length > 0) {
38298
38283
  o.null_shape_count = countNullShapes(lyr.shapes);
38299
- o.bbox =getLayerBounds(lyr, dataset.arcs).toArray();
38284
+ o.bbox = getLayerBounds(lyr, dataset.arcs).toArray();
38300
38285
  o.proj4 = getProjInfo(dataset);
38301
38286
  }
38287
+ o.source_file = getLayerSourceFile(lyr, dataset) || null;
38288
+ o.attribute_data = getAttributeTableInfo(lyr);
38302
38289
  return o;
38303
38290
  }
38304
38291
 
38305
- // TODO: consider polygons with zero area or other invalid geometries
38306
- function countNullShapes(shapes) {
38307
- var count = 0;
38308
- for (var i=0; i<shapes.length; i++) {
38309
- if (!shapes[i] || shapes[i].length === 0) count++;
38292
+ // i: (optional) record index
38293
+ function getAttributeTableInfo(lyr, i) {
38294
+ if (!lyr.data || lyr.data.size() === 0 || lyr.data.getFields().length === 0) {
38295
+ return null;
38310
38296
  }
38311
- return count;
38297
+ var fields = applyFieldOrder(lyr.data.getFields(), 'ascending');
38298
+ var valueName = i === undefined ? 'first_value' : 'value';
38299
+ return fields.map(function(fname) {
38300
+ return {
38301
+ field: fname,
38302
+ [valueName]: lyr.data.getReadOnlyRecordAt(i || 0)[fname]
38303
+ };
38304
+ });
38312
38305
  }
38313
38306
 
38314
- function countNullRecords(records) {
38315
- var count = 0;
38316
- for (var i=0; i<records.length; i++) {
38317
- if (!records[i]) count++;
38318
- }
38319
- return count;
38307
+ function formatInfo(arr) {
38308
+ var str = '';
38309
+ arr.forEach(function(info, i) {
38310
+ var title = 'Layer: ' + (info.layer_name || '[unnamed layer]');
38311
+ var tableStr = formatAttributeTableInfo(info.attribute_data);
38312
+ var tableWidth = measureLongestLine(tableStr);
38313
+ var ruleLen = Math.min(Math.max(title.length, tableWidth), MAX_RULE_LEN);
38314
+ str += '\n';
38315
+ str += utils.lpad('', ruleLen, '=') + '\n';
38316
+ str += title + '\n';
38317
+ str += utils.lpad('', ruleLen, '-') + '\n';
38318
+ str += formatLayerInfo(info);
38319
+ str += tableStr;
38320
+ });
38321
+ return str;
38320
38322
  }
38321
38323
 
38322
- function getLayerInfo(lyr, dataset) {
38323
- var data = getLayerData(lyr, dataset);
38324
+ function formatLayerInfo(data) {
38324
38325
  var str = '';
38325
38326
  str += "Type: " + (data.geometry_type || "tabular data") + "\n";
38326
38327
  str += utils.format("Records: %,d\n",data.feature_count);
@@ -38331,21 +38332,19 @@ ${svg}
38331
38332
  str += "Bounds: " + data.bbox.join(',') + "\n";
38332
38333
  str += "CRS: " + data.proj4 + "\n";
38333
38334
  }
38334
- str += "Source: " + (getLayerSourceFile(lyr, dataset) || 'n/a') + "\n";
38335
+ str += "Source: " + (data.source_file || 'n/a') + "\n";
38335
38336
  return str;
38336
38337
  }
38337
38338
 
38338
- function getAttributeTableInfo(lyr, i) {
38339
- if (!lyr.data || lyr.data.size() === 0 || lyr.data.getFields().length === 0) {
38340
- return "Attribute data: [none]\n";
38341
- }
38342
- return "\nAttribute data\n" + formatAttributeTable(lyr.data, i);
38343
- }
38344
-
38345
- function formatAttributeTable(data, i) {
38346
- var fields = applyFieldOrder(data.getFields(), 'ascending');
38347
- var vals = fields.map(function(fname) {
38348
- return data.getReadOnlyRecordAt(i || 0)[fname];
38339
+ function formatAttributeTableInfo(arr) {
38340
+ if (!arr) return "Attribute data: [none]\n";
38341
+ var header = "\nAttribute data\n";
38342
+ var valKey = 'first_value' in arr[0] ? 'first_value' : 'value';
38343
+ var vals = [];
38344
+ var fields = [];
38345
+ arr.forEach(function(o) {
38346
+ fields.push(o.field);
38347
+ vals.push(o[valKey]);
38349
38348
  });
38350
38349
  var maxIntegralChars = vals.reduce(function(max, val) {
38351
38350
  if (utils.isNumber(val)) {
@@ -38357,20 +38356,66 @@ ${svg}
38357
38356
  var col2Arr = vals.reduce(function(memo, val) {
38358
38357
  memo.push(formatTableValue(val, maxIntegralChars));
38359
38358
  return memo;
38360
- }, [i >= 0 ? 'Value' : 'First value']);
38359
+ }, [valKey == 'first_value' ? 'First value' : 'Value']);
38361
38360
  var col1Chars = maxChars(col1Arr);
38362
38361
  var col2Chars = maxChars(col2Arr);
38363
38362
  var sepStr = (utils.rpad('', col1Chars + 2, '-') + '+' +
38364
38363
  utils.rpad('', col2Chars + 2, '-')).substr(0, MAX_RULE_LEN);
38365
38364
  var sepLine = sepStr + '\n';
38366
- var table = sepLine;
38365
+ var table = '';
38367
38366
  col1Arr.forEach(function(col1, i) {
38368
38367
  var w = stringDisplayWidth(col1);
38369
38368
  table += ' ' + col1 + utils.rpad('', col1Chars - w, ' ') + ' | ' +
38370
38369
  col2Arr[i] + '\n';
38371
38370
  if (i === 0) table += sepLine; // separator after first line
38372
38371
  });
38373
- return table + sepLine;
38372
+ return header + sepLine + table + sepLine;
38373
+ }
38374
+
38375
+ function measureLongestLine(str) {
38376
+ return Math.max.apply(null, str.split('\n').map(function(line) {return stringDisplayWidth(line);}));
38377
+ }
38378
+
38379
+ function stringDisplayWidth(str) {
38380
+ var w = 0;
38381
+ for (var i = 0, n=str.length; i < n; i++) {
38382
+ w += charDisplayWidth(str.charCodeAt(i));
38383
+ }
38384
+ return w;
38385
+ }
38386
+
38387
+ // see https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
38388
+ // this is a simplified version, focusing on double-width CJK chars and ignoring nonprinting etc chars
38389
+ function charDisplayWidth(c) {
38390
+ if (c >= 0x1100 &&
38391
+ (c <= 0x115f || c == 0x2329 || c == 0x232a ||
38392
+ (c >= 0x2e80 && c <= 0xa4cf && c != 0x303f) || /* CJK ... Yi */
38393
+ (c >= 0xac00 && c <= 0xd7a3) || /* Hangul Syllables */
38394
+ (c >= 0xf900 && c <= 0xfaff) || /* CJK Compatibility Ideographs */
38395
+ (c >= 0xfe10 && c <= 0xfe19) || /* Vertical forms */
38396
+ (c >= 0xfe30 && c <= 0xfe6f) || /* CJK Compatibility Forms */
38397
+ (c >= 0xff00 && c <= 0xff60) || /* Fullwidth Forms */
38398
+ (c >= 0xffe0 && c <= 0xffe6) ||
38399
+ (c >= 0x20000 && c <= 0x2fffd) ||
38400
+ (c >= 0x30000 && c <= 0x3fffd))) return 2;
38401
+ return 1;
38402
+ }
38403
+
38404
+ // TODO: consider polygons with zero area or other invalid geometries
38405
+ function countNullShapes(shapes) {
38406
+ var count = 0;
38407
+ for (var i=0; i<shapes.length; i++) {
38408
+ if (!shapes[i] || shapes[i].length === 0) count++;
38409
+ }
38410
+ return count;
38411
+ }
38412
+
38413
+ function countNullRecords(records) {
38414
+ var count = 0;
38415
+ for (var i=0; i<records.length; i++) {
38416
+ if (!records[i]) count++;
38417
+ }
38418
+ return count;
38374
38419
  }
38375
38420
 
38376
38421
  function maxChars(arr) {
@@ -38424,8 +38469,9 @@ ${svg}
38424
38469
 
38425
38470
  var Info = /*#__PURE__*/Object.freeze({
38426
38471
  __proto__: null,
38427
- getLayerData: getLayerData,
38472
+ getLayerInfo: getLayerInfo,
38428
38473
  getAttributeTableInfo: getAttributeTableInfo,
38474
+ formatAttributeTableInfo: formatAttributeTableInfo,
38429
38475
  formatTableValue: formatTableValue
38430
38476
  });
38431
38477
 
@@ -38477,7 +38523,7 @@ ${svg}
38477
38523
  function getFeatureInfo(id, lyr, arcs) {
38478
38524
  var msg = "Feature " + id + '\n';
38479
38525
  msg += getShapeInfo(id, lyr, arcs);
38480
- msg += getAttributeTableInfo(lyr, id);
38526
+ msg += formatAttributeTableInfo(getAttributeTableInfo(lyr, id));
38481
38527
  return msg;
38482
38528
  }
38483
38529
 
@@ -40589,7 +40635,7 @@ ${svg}
40589
40635
 
40590
40636
  function getRunCommandData(target) {
40591
40637
  var lyr = target.layers[0];
40592
- var data = getLayerData(lyr, target.dataset);
40638
+ var data = getLayerInfo(lyr, target.dataset);
40593
40639
  data.layer = lyr;
40594
40640
  data.dataset = target.dataset;
40595
40641
  return data;
@@ -42939,7 +42985,7 @@ ${svg}
42939
42985
  cmd.include(opts);
42940
42986
 
42941
42987
  } else if (name == 'info') {
42942
- cmd.printInfo(expandCommandTargets(targets));
42988
+ cmd.info(targets, opts);
42943
42989
 
42944
42990
  } else if (name == 'inlay') {
42945
42991
  outputLayers = cmd.inlay(targetLayers, source, targetDataset, opts);
@@ -43305,13 +43351,13 @@ ${svg}
43305
43351
  commands = runAndRemoveInfoCommands(commands);
43306
43352
  if (commands.length === 0) return done(null);
43307
43353
 
43308
- // add options to -i -o -join -clip -erase commands to bypass file i/o
43354
+ // add options to -i -o -join -clip -erase etc. commands to bypass file i/o
43309
43355
  // TODO: find a less kludgy solution
43310
43356
  commands.forEach(function(cmd) {
43311
43357
  if (commandTakesFileInput(cmd.name) && inputObj) {
43312
43358
  cmd.options.input = inputObj;
43313
43359
  }
43314
- if (cmd.name == 'o' && outputArr) {
43360
+ if (outputArr && (cmd.name == 'o' || cmd.name == 'info' && cmd.options.save_to)) {
43315
43361
  cmd.options.output = outputArr;
43316
43362
  }
43317
43363
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.6.23",
3
+ "version": "0.6.24",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -1437,7 +1437,9 @@
1437
1437
  closeMenu(100);
1438
1438
  }).text('restore');
1439
1439
  El('span').addClass('save-menu-btn').appendTo(line).on('click', async function(e) {
1440
- var buf = await idb.get(item.id);
1440
+ var obj = await idb.get(item.id);
1441
+ await internal.applyCompression(obj, {consume: true});
1442
+ var buf = internal.pack(obj);
1441
1443
  saveBlobToLocalFile('mapshaper_snapshot.msx', new Blob([buf]));
1442
1444
  }).text('export');
1443
1445
  El('span').addClass('save-menu-btn').appendTo(line).on('click', async function(e) {
@@ -1485,20 +1487,24 @@
1485
1487
  }
1486
1488
 
1487
1489
  async function saveSnapshot(gui) {
1488
- var buf = await captureSnapshot(gui);
1490
+ var obj = await captureSnapshot(gui);
1491
+ // storing an unpacked object is usually a bit faster (~20%)
1492
+ // note: we don't know the size of unpacked snapshot objects
1493
+ // obj = internal.pack(obj);
1489
1494
  var entryId = String(++snapshotCount).padStart(3, '0');
1490
1495
  var snapshotId = sessionId + '_' + entryId; // e.g. session_d89fw_001
1496
+ var size = obj.length;
1491
1497
  var entry = {
1492
1498
  created: Date.now(),
1493
1499
  session: sessionId,
1494
1500
  id: snapshotId,
1495
1501
  name: snapshotCount + '.',
1496
1502
  number: snapshotCount,
1497
- size: buf.length,
1498
- display_size: formatSize(buf.length)
1503
+ size: size,
1504
+ display_size: formatSize(size)
1499
1505
  };
1500
1506
 
1501
- await idb.set(entry.id, buf);
1507
+ await idb.set(entry.id, obj);
1502
1508
  await addToIndex(entry);
1503
1509
  renderMenu();
1504
1510
  }
@@ -1507,9 +1513,8 @@
1507
1513
  function formatSize(bytes) {
1508
1514
  var kb = Math.round(bytes / 1000);
1509
1515
  var mb = (bytes / 1e6).toFixed(1);
1510
- if (kb < 990) {
1511
- return kb + 'kB';
1512
- }
1516
+ if (!kb) return '';
1517
+ if (kb < 990) return kb + 'kB';
1513
1518
  return mb + 'MB';
1514
1519
  }
1515
1520
 
@@ -1531,13 +1536,13 @@
1531
1536
  }
1532
1537
 
1533
1538
  async function restoreSnapshotById(id, gui) {
1534
- var buf = await idb.get(id);
1535
- if (!buf) {
1536
- console.log('Snapshot not available:', id);
1537
- return;
1539
+ var data;
1540
+ try {
1541
+ data = await internal.restoreSessionData(await idb.get(id));
1542
+ } catch(e) {
1543
+ console.error(e);
1544
+ stop$1('Snapshot is not available');
1538
1545
  }
1539
- var data = await internal.unpackSession(buf);
1540
- buf = null;
1541
1546
  gui.model.clear();
1542
1547
  importDatasets(data.datasets, gui);
1543
1548
  gui.clearMode();
@@ -1550,7 +1555,7 @@
1550
1555
  if (buf instanceof ArrayBuffer) {
1551
1556
  buf = new Uint8Array(buf);
1552
1557
  }
1553
- var data = await internal.unpackSession(buf);
1558
+ var data = await internal.unpackSessionData(buf);
1554
1559
  importDatasets(data.datasets, gui);
1555
1560
  }
1556
1561
 
@@ -1573,7 +1578,7 @@
1573
1578
  // console.timeEnd('msx')
1574
1579
  delete lyr.active;
1575
1580
  obj.gui = getGuiState(gui);
1576
- return internal.pack(obj);
1581
+ return obj;
1577
1582
  }
1578
1583
 
1579
1584
  // TODO: capture gui state information to allow restoring more of the UI
@@ -3040,22 +3045,12 @@
3040
3045
  }
3041
3046
 
3042
3047
  function getHistory() {
3043
- var hist;
3044
- try {
3045
- hist = JSON.parse(window.localStorage.getItem('console_history'));
3046
- } catch(e) {
3047
- }
3048
- return hist && hist.length > 0 ? hist : [];
3048
+ return GUI.getSavedValue('console_history') || [];
3049
3049
  }
3050
3050
 
3051
3051
  function saveHistory() {
3052
- try {
3053
- history = history.filter(Boolean); // TODO: fix condition that leaves a blank line on the history
3054
- if (history.length) {
3055
- window.localStorage.setItem('console_history', JSON.stringify(history.slice(-50)));
3056
- }
3057
- } catch(e) {
3058
- }
3052
+ history = history.filter(Boolean); // TODO: fix condition that leaves a blank line on the history
3053
+ GUI.setSavedValue('console_history', history.slice(-100));
3059
3054
  }
3060
3055
 
3061
3056
  function toLog(str, cname) {
@@ -3678,7 +3673,13 @@
3678
3673
  gui.addMode('export', turnOn, turnOff, exportBtn);
3679
3674
  gui.keyboard.onMenuSubmit(menu, onExportClick);
3680
3675
  if (window.showSaveFilePicker) {
3681
- menu.findChild('#save-preference').css('display', 'inline-block');
3676
+ menu.findChild('#save-preference')
3677
+ .css('display', 'inline-block')
3678
+ .findChild('input')
3679
+ .on('change', function() {
3680
+ GUI.setSavedValue('choose-save-dir', this.checked);
3681
+ })
3682
+ .attr('checked', GUI.getSavedValue('choose-save-dir') || false);
3682
3683
  }
3683
3684
 
3684
3685
  function turnOn() {
@@ -3702,8 +3703,6 @@
3702
3703
 
3703
3704
  function onExportClick() {
3704
3705
  var layers = getSelectedLayers();
3705
- var chooseDir = menu.findChild('#save-preference input').node().checked;
3706
- GUI.setSavedValue('choose-save-dir', chooseDir);
3707
3706
  if (layers.length === 0) {
3708
3707
  return gui.alert('No layers were selected');
3709
3708
  }
@@ -9797,19 +9796,26 @@
9797
9796
  bx = t.bx,
9798
9797
  by = t.by;
9799
9798
  var x, y, xp, yp;
9799
+ var count = 0;
9800
9800
  if (!vec.hasNext()) return;
9801
- x = xp = round(vec.x * mx + bx);
9802
- y = yp = round(vec.y * my + by);
9801
+ x = round(vec.x * mx + bx);
9802
+ y = round(vec.y * my + by);
9803
9803
  ctx.moveTo(x, y);
9804
9804
  while (vec.hasNext()) {
9805
+ xp = x;
9806
+ yp = y;
9805
9807
  x = round(vec.x * mx + bx);
9806
9808
  y = round(vec.y * my + by);
9807
9809
  if (x != xp || y != yp) {
9808
9810
  ctx.lineTo(x, y);
9809
- xp = x;
9810
- yp = y;
9811
+ count++;
9811
9812
  }
9812
9813
  }
9814
+ if (count === 0) {
9815
+ // draw a tiny line if all coords round to the same location,
9816
+ // so tiny shapes with strokes will consistently be drawn as dots,
9817
+ ctx.lineTo(x + 0.1, y);
9818
+ }
9813
9819
  }
9814
9820
 
9815
9821
  function roundToPix(x) {
package/www/mapshaper.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
 
3
- var VERSION = "0.6.23";
3
+ var VERSION = "0.6.24";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -10620,7 +10620,8 @@
10620
10620
  buf = new Uint8Array(buf);
10621
10621
  }
10622
10622
  opts = opts || {};
10623
- var out = await utils.promisify(gunzip)(buf, opts);
10623
+ var gunzip$1 = runningInBrowser() ? utils.promisify(gunzip) : utils.promisify(require('zlib').gunzip);
10624
+ var out = await gunzip$1(buf, opts);
10624
10625
  if (opts.filename && !isImportableAsBinary(opts.filename)) {
10625
10626
  out = strFromU8(out);
10626
10627
  }
@@ -10628,8 +10629,6 @@
10628
10629
  }
10629
10630
 
10630
10631
  function gunzipSync(buf, filename) {
10631
- // TODO: use native module in Node
10632
- // require('zlib').
10633
10632
  if (buf instanceof ArrayBuffer) {
10634
10633
  buf = new Uint8Array(buf);
10635
10634
  }
@@ -10768,16 +10767,39 @@
10768
10767
  // gui: (optional) gui instance
10769
10768
  //
10770
10769
  async function exportDatasetsToPack(datasets, opts) {
10771
- return {
10770
+ var obj = {
10772
10771
  version: 1,
10773
10772
  created: (new Date).toISOString(),
10774
- datasets: await Promise.all(datasets.map(d => exportDataset(d, opts || {})))
10773
+ datasets: await Promise.all(datasets.map(exportDataset))
10775
10774
  };
10775
+ if (opts.compact) {
10776
+ await applyCompression(obj);
10777
+ }
10778
+ return obj;
10779
+ }
10780
+
10781
+ async function applyCompression(obj, opts) {
10782
+ var promises = [];
10783
+ obj.datasets.forEach(d => {
10784
+ if (d.arcs) promises.push(compressArcs(d.arcs, opts));
10785
+ });
10786
+ await Promise.all(promises);
10787
+ }
10788
+
10789
+ async function compressArcs(obj, opts) {
10790
+ var gzipOpts = Object.assign({level: 1, consume: false}, opts);
10791
+ var promises = [gzipAsync(obj.nn, gzipOpts), gzipAsync(obj.xx, gzipOpts), gzipAsync(obj.yy, gzipOpts)];
10792
+ if (obj.zz) promises.push(gzipAsync(obj.zz, gzipOpts));
10793
+ var results = await Promise.all(promises);
10794
+ obj.nn = results.shift();
10795
+ obj.xx = results.shift();
10796
+ obj.yy = results.shift();
10797
+ if (obj.zz) obj.zz = results.shift();
10776
10798
  }
10777
10799
 
10778
10800
  async function exportDataset(dataset, opts) {
10779
10801
  return {
10780
- arcs: dataset.arcs ? await exportArcs(dataset.arcs, opts) : null,
10802
+ arcs: dataset.arcs ? exportArcs(dataset.arcs) : null,
10781
10803
  info: dataset.info ? exportInfo(dataset.info) : null,
10782
10804
  layers: await Promise.all((dataset.layers || []).map(exportLayer))
10783
10805
  };
@@ -10787,38 +10809,22 @@
10787
10809
  return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
10788
10810
  }
10789
10811
 
10790
- async function exportArcs(arcs, opts) {
10812
+ function exportArcs(arcs) {
10791
10813
  var data = arcs.getVertexData();
10792
- var obj = {
10814
+ return {
10793
10815
  nn: typedArrayToBuffer(data.nn),
10794
10816
  xx: typedArrayToBuffer(data.xx),
10795
10817
  yy: typedArrayToBuffer(data.yy),
10796
10818
  zz: data.zz ? typedArrayToBuffer(data.zz) : null,
10797
10819
  zlimit: arcs.getRetainedInterval()
10798
10820
  };
10799
-
10800
- // gzipping typically sees about 70% compression on unrounded coordinates
10801
- // -- possibly not worth the time
10802
- if (opts.compact) {
10803
- var gzipOpts = {level: 1, consume: false};
10804
- var promises = [gzipAsync(obj.nn, gzipOpts), gzipAsync(obj.xx, gzipOpts), gzipAsync(obj.yy, gzipOpts)];
10805
- if (obj.zz) promises.push(gzipAsync(obj.zz, gzipOpts));
10806
- var results = await Promise.all(promises);
10807
- obj.nn = results.shift();
10808
- obj.xx = results.shift();
10809
- obj.yy = results.shift();
10810
- if (obj.zz) obj.zz = results.shift();
10811
- }
10812
- return obj;
10813
10821
  }
10814
10822
 
10815
10823
  async function exportLayer(lyr) {
10816
- // console.time('table')
10817
10824
  var data = null;
10818
10825
  if (lyr.data) {
10819
10826
  data = await exportTable2(lyr.data);
10820
10827
  }
10821
- // console.timeEnd('table')
10822
10828
  return {
10823
10829
  name: lyr.name || null,
10824
10830
  geometry_type: lyr.geometry_type || null,
@@ -10845,6 +10851,7 @@
10845
10851
  exportPackedDatasets: exportPackedDatasets,
10846
10852
  pack: pack,
10847
10853
  exportDatasetsToPack: exportDatasetsToPack,
10854
+ applyCompression: applyCompression,
10848
10855
  exportDataset: exportDataset
10849
10856
  });
10850
10857
 
@@ -24922,6 +24929,9 @@ ${svg}
24922
24929
 
24923
24930
  parser.command('info')
24924
24931
  .describe('print information about data layers')
24932
+ .option('save-to', {
24933
+ describe: 'name of file to save info in JSON format'
24934
+ })
24925
24935
  .option('target', targetOpt);
24926
24936
 
24927
24937
  parser.command('inspect')
@@ -27457,12 +27467,14 @@ ${svg}
27457
27467
  // Import datasets contained in a BSON blob
27458
27468
  // Return command target as a dataset
27459
27469
  //
27460
- async function unpackSession(buf) {
27461
- var obj = unpack(buf, {});
27470
+ async function unpackSessionData(buf) {
27471
+ return restoreSessionData(unpack(buf, {}));
27472
+ }
27473
+
27474
+ async function restoreSessionData(obj) {
27462
27475
  if (!isValidSession(obj)) {
27463
27476
  stop('Invalid mapshaper session data object');
27464
27477
  }
27465
-
27466
27478
  var datasets = await Promise.all(obj.datasets.map(importDataset));
27467
27479
  return Object.assign(obj, {datasets: datasets});
27468
27480
  }
@@ -27539,7 +27551,8 @@ ${svg}
27539
27551
 
27540
27552
  var Unpack = /*#__PURE__*/Object.freeze({
27541
27553
  __proto__: null,
27542
- unpackSession: unpackSession
27554
+ unpackSessionData: unpackSessionData,
27555
+ restoreSessionData: restoreSessionData
27543
27556
  });
27544
27557
 
27545
27558
  cmd.importFiles = async function(catalog, opts) {
@@ -27593,7 +27606,7 @@ ${svg}
27593
27606
 
27594
27607
  async function importMshpFile(file, catalog, opts) {
27595
27608
  var buf = cli.readFile(file, null, opts.input);
27596
- var obj = await unpackSession(buf);
27609
+ var obj = await unpackSessionData(buf);
27597
27610
  obj.datasets.forEach(catalog.addDataset, catalog);
27598
27611
  return obj.target;
27599
27612
  }
@@ -38240,55 +38253,27 @@ ${svg}
38240
38253
 
38241
38254
  var MAX_RULE_LEN = 50;
38242
38255
 
38243
- cmd.printInfo = function(layers) {
38244
- var str = '';
38245
- layers.forEach(function(o, i) {
38246
- var title = 'Layer: ' + (o.layer.name || '[unnamed layer]');
38247
- var tableStr = getAttributeTableInfo(o.layer);
38248
- var tableWidth = measureLongestLine(tableStr);
38249
- var ruleLen = Math.min(Math.max(title.length, tableWidth), MAX_RULE_LEN);
38250
- str += '\n';
38251
- str += utils.lpad('', ruleLen, '=') + '\n';
38252
- str += title + '\n';
38253
- str += utils.lpad('', ruleLen, '-') + '\n';
38254
- str += getLayerInfo(o.layer, o.dataset);
38255
- str += tableStr;
38256
+ cmd.info = function(targets, opts) {
38257
+ var layers = expandCommandTargets(targets);
38258
+ var arr = layers.map(function(o) {
38259
+ return getLayerInfo(o.layer, o.dataset);
38256
38260
  });
38257
- message(str);
38258
- };
38259
-
38260
- function measureLongestLine(str) {
38261
- return Math.max.apply(null, str.split('\n').map(function(line) {return stringDisplayWidth(line);}));
38262
- }
38263
-
38264
- function stringDisplayWidth(str) {
38265
- var w = 0;
38266
- for (var i = 0, n=str.length; i < n; i++) {
38267
- w += charDisplayWidth(str.charCodeAt(i));
38261
+ message(formatInfo(arr));
38262
+ if (opts.save_to) {
38263
+ var output = [{
38264
+ filename: opts.save_to + (opts.save_to.endsWith('.json') ? '' : '.json'),
38265
+ content: JSON.stringify(arr, null, 2)
38266
+ }];
38267
+ writeFiles(output, opts);
38268
38268
  }
38269
- return w;
38270
- }
38269
+ };
38271
38270
 
38272
- // see https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
38273
- // this is a simplified version, focusing on double-width CJK chars and ignoring nonprinting etc chars
38274
- function charDisplayWidth(c) {
38275
- if (c >= 0x1100 &&
38276
- (c <= 0x115f || c == 0x2329 || c == 0x232a ||
38277
- (c >= 0x2e80 && c <= 0xa4cf && c != 0x303f) || /* CJK ... Yi */
38278
- (c >= 0xac00 && c <= 0xd7a3) || /* Hangul Syllables */
38279
- (c >= 0xf900 && c <= 0xfaff) || /* CJK Compatibility Ideographs */
38280
- (c >= 0xfe10 && c <= 0xfe19) || /* Vertical forms */
38281
- (c >= 0xfe30 && c <= 0xfe6f) || /* CJK Compatibility Forms */
38282
- (c >= 0xff00 && c <= 0xff60) || /* Fullwidth Forms */
38283
- (c >= 0xffe0 && c <= 0xffe6) ||
38284
- (c >= 0x20000 && c <= 0x2fffd) ||
38285
- (c >= 0x30000 && c <= 0x3fffd))) return 2;
38286
- return 1;
38287
- }
38271
+ cmd.printInfo = cmd.info; // old name
38288
38272
 
38289
- function getLayerData(lyr, dataset) {
38273
+ function getLayerInfo(lyr, dataset) {
38290
38274
  var n = getFeatureCount(lyr);
38291
38275
  var o = {
38276
+ layer_name: lyr.name,
38292
38277
  geometry_type: lyr.geometry_type,
38293
38278
  feature_count: n,
38294
38279
  null_shape_count: 0,
@@ -38296,31 +38281,47 @@ ${svg}
38296
38281
  };
38297
38282
  if (lyr.shapes && lyr.shapes.length > 0) {
38298
38283
  o.null_shape_count = countNullShapes(lyr.shapes);
38299
- o.bbox =getLayerBounds(lyr, dataset.arcs).toArray();
38284
+ o.bbox = getLayerBounds(lyr, dataset.arcs).toArray();
38300
38285
  o.proj4 = getProjInfo(dataset);
38301
38286
  }
38287
+ o.source_file = getLayerSourceFile(lyr, dataset) || null;
38288
+ o.attribute_data = getAttributeTableInfo(lyr);
38302
38289
  return o;
38303
38290
  }
38304
38291
 
38305
- // TODO: consider polygons with zero area or other invalid geometries
38306
- function countNullShapes(shapes) {
38307
- var count = 0;
38308
- for (var i=0; i<shapes.length; i++) {
38309
- if (!shapes[i] || shapes[i].length === 0) count++;
38292
+ // i: (optional) record index
38293
+ function getAttributeTableInfo(lyr, i) {
38294
+ if (!lyr.data || lyr.data.size() === 0 || lyr.data.getFields().length === 0) {
38295
+ return null;
38310
38296
  }
38311
- return count;
38297
+ var fields = applyFieldOrder(lyr.data.getFields(), 'ascending');
38298
+ var valueName = i === undefined ? 'first_value' : 'value';
38299
+ return fields.map(function(fname) {
38300
+ return {
38301
+ field: fname,
38302
+ [valueName]: lyr.data.getReadOnlyRecordAt(i || 0)[fname]
38303
+ };
38304
+ });
38312
38305
  }
38313
38306
 
38314
- function countNullRecords(records) {
38315
- var count = 0;
38316
- for (var i=0; i<records.length; i++) {
38317
- if (!records[i]) count++;
38318
- }
38319
- return count;
38307
+ function formatInfo(arr) {
38308
+ var str = '';
38309
+ arr.forEach(function(info, i) {
38310
+ var title = 'Layer: ' + (info.layer_name || '[unnamed layer]');
38311
+ var tableStr = formatAttributeTableInfo(info.attribute_data);
38312
+ var tableWidth = measureLongestLine(tableStr);
38313
+ var ruleLen = Math.min(Math.max(title.length, tableWidth), MAX_RULE_LEN);
38314
+ str += '\n';
38315
+ str += utils.lpad('', ruleLen, '=') + '\n';
38316
+ str += title + '\n';
38317
+ str += utils.lpad('', ruleLen, '-') + '\n';
38318
+ str += formatLayerInfo(info);
38319
+ str += tableStr;
38320
+ });
38321
+ return str;
38320
38322
  }
38321
38323
 
38322
- function getLayerInfo(lyr, dataset) {
38323
- var data = getLayerData(lyr, dataset);
38324
+ function formatLayerInfo(data) {
38324
38325
  var str = '';
38325
38326
  str += "Type: " + (data.geometry_type || "tabular data") + "\n";
38326
38327
  str += utils.format("Records: %,d\n",data.feature_count);
@@ -38331,21 +38332,19 @@ ${svg}
38331
38332
  str += "Bounds: " + data.bbox.join(',') + "\n";
38332
38333
  str += "CRS: " + data.proj4 + "\n";
38333
38334
  }
38334
- str += "Source: " + (getLayerSourceFile(lyr, dataset) || 'n/a') + "\n";
38335
+ str += "Source: " + (data.source_file || 'n/a') + "\n";
38335
38336
  return str;
38336
38337
  }
38337
38338
 
38338
- function getAttributeTableInfo(lyr, i) {
38339
- if (!lyr.data || lyr.data.size() === 0 || lyr.data.getFields().length === 0) {
38340
- return "Attribute data: [none]\n";
38341
- }
38342
- return "\nAttribute data\n" + formatAttributeTable(lyr.data, i);
38343
- }
38344
-
38345
- function formatAttributeTable(data, i) {
38346
- var fields = applyFieldOrder(data.getFields(), 'ascending');
38347
- var vals = fields.map(function(fname) {
38348
- return data.getReadOnlyRecordAt(i || 0)[fname];
38339
+ function formatAttributeTableInfo(arr) {
38340
+ if (!arr) return "Attribute data: [none]\n";
38341
+ var header = "\nAttribute data\n";
38342
+ var valKey = 'first_value' in arr[0] ? 'first_value' : 'value';
38343
+ var vals = [];
38344
+ var fields = [];
38345
+ arr.forEach(function(o) {
38346
+ fields.push(o.field);
38347
+ vals.push(o[valKey]);
38349
38348
  });
38350
38349
  var maxIntegralChars = vals.reduce(function(max, val) {
38351
38350
  if (utils.isNumber(val)) {
@@ -38357,20 +38356,66 @@ ${svg}
38357
38356
  var col2Arr = vals.reduce(function(memo, val) {
38358
38357
  memo.push(formatTableValue(val, maxIntegralChars));
38359
38358
  return memo;
38360
- }, [i >= 0 ? 'Value' : 'First value']);
38359
+ }, [valKey == 'first_value' ? 'First value' : 'Value']);
38361
38360
  var col1Chars = maxChars(col1Arr);
38362
38361
  var col2Chars = maxChars(col2Arr);
38363
38362
  var sepStr = (utils.rpad('', col1Chars + 2, '-') + '+' +
38364
38363
  utils.rpad('', col2Chars + 2, '-')).substr(0, MAX_RULE_LEN);
38365
38364
  var sepLine = sepStr + '\n';
38366
- var table = sepLine;
38365
+ var table = '';
38367
38366
  col1Arr.forEach(function(col1, i) {
38368
38367
  var w = stringDisplayWidth(col1);
38369
38368
  table += ' ' + col1 + utils.rpad('', col1Chars - w, ' ') + ' | ' +
38370
38369
  col2Arr[i] + '\n';
38371
38370
  if (i === 0) table += sepLine; // separator after first line
38372
38371
  });
38373
- return table + sepLine;
38372
+ return header + sepLine + table + sepLine;
38373
+ }
38374
+
38375
+ function measureLongestLine(str) {
38376
+ return Math.max.apply(null, str.split('\n').map(function(line) {return stringDisplayWidth(line);}));
38377
+ }
38378
+
38379
+ function stringDisplayWidth(str) {
38380
+ var w = 0;
38381
+ for (var i = 0, n=str.length; i < n; i++) {
38382
+ w += charDisplayWidth(str.charCodeAt(i));
38383
+ }
38384
+ return w;
38385
+ }
38386
+
38387
+ // see https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
38388
+ // this is a simplified version, focusing on double-width CJK chars and ignoring nonprinting etc chars
38389
+ function charDisplayWidth(c) {
38390
+ if (c >= 0x1100 &&
38391
+ (c <= 0x115f || c == 0x2329 || c == 0x232a ||
38392
+ (c >= 0x2e80 && c <= 0xa4cf && c != 0x303f) || /* CJK ... Yi */
38393
+ (c >= 0xac00 && c <= 0xd7a3) || /* Hangul Syllables */
38394
+ (c >= 0xf900 && c <= 0xfaff) || /* CJK Compatibility Ideographs */
38395
+ (c >= 0xfe10 && c <= 0xfe19) || /* Vertical forms */
38396
+ (c >= 0xfe30 && c <= 0xfe6f) || /* CJK Compatibility Forms */
38397
+ (c >= 0xff00 && c <= 0xff60) || /* Fullwidth Forms */
38398
+ (c >= 0xffe0 && c <= 0xffe6) ||
38399
+ (c >= 0x20000 && c <= 0x2fffd) ||
38400
+ (c >= 0x30000 && c <= 0x3fffd))) return 2;
38401
+ return 1;
38402
+ }
38403
+
38404
+ // TODO: consider polygons with zero area or other invalid geometries
38405
+ function countNullShapes(shapes) {
38406
+ var count = 0;
38407
+ for (var i=0; i<shapes.length; i++) {
38408
+ if (!shapes[i] || shapes[i].length === 0) count++;
38409
+ }
38410
+ return count;
38411
+ }
38412
+
38413
+ function countNullRecords(records) {
38414
+ var count = 0;
38415
+ for (var i=0; i<records.length; i++) {
38416
+ if (!records[i]) count++;
38417
+ }
38418
+ return count;
38374
38419
  }
38375
38420
 
38376
38421
  function maxChars(arr) {
@@ -38424,8 +38469,9 @@ ${svg}
38424
38469
 
38425
38470
  var Info = /*#__PURE__*/Object.freeze({
38426
38471
  __proto__: null,
38427
- getLayerData: getLayerData,
38472
+ getLayerInfo: getLayerInfo,
38428
38473
  getAttributeTableInfo: getAttributeTableInfo,
38474
+ formatAttributeTableInfo: formatAttributeTableInfo,
38429
38475
  formatTableValue: formatTableValue
38430
38476
  });
38431
38477
 
@@ -38477,7 +38523,7 @@ ${svg}
38477
38523
  function getFeatureInfo(id, lyr, arcs) {
38478
38524
  var msg = "Feature " + id + '\n';
38479
38525
  msg += getShapeInfo(id, lyr, arcs);
38480
- msg += getAttributeTableInfo(lyr, id);
38526
+ msg += formatAttributeTableInfo(getAttributeTableInfo(lyr, id));
38481
38527
  return msg;
38482
38528
  }
38483
38529
 
@@ -40589,7 +40635,7 @@ ${svg}
40589
40635
 
40590
40636
  function getRunCommandData(target) {
40591
40637
  var lyr = target.layers[0];
40592
- var data = getLayerData(lyr, target.dataset);
40638
+ var data = getLayerInfo(lyr, target.dataset);
40593
40639
  data.layer = lyr;
40594
40640
  data.dataset = target.dataset;
40595
40641
  return data;
@@ -42939,7 +42985,7 @@ ${svg}
42939
42985
  cmd.include(opts);
42940
42986
 
42941
42987
  } else if (name == 'info') {
42942
- cmd.printInfo(expandCommandTargets(targets));
42988
+ cmd.info(targets, opts);
42943
42989
 
42944
42990
  } else if (name == 'inlay') {
42945
42991
  outputLayers = cmd.inlay(targetLayers, source, targetDataset, opts);
@@ -43305,13 +43351,13 @@ ${svg}
43305
43351
  commands = runAndRemoveInfoCommands(commands);
43306
43352
  if (commands.length === 0) return done(null);
43307
43353
 
43308
- // add options to -i -o -join -clip -erase commands to bypass file i/o
43354
+ // add options to -i -o -join -clip -erase etc. commands to bypass file i/o
43309
43355
  // TODO: find a less kludgy solution
43310
43356
  commands.forEach(function(cmd) {
43311
43357
  if (commandTakesFileInput(cmd.name) && inputObj) {
43312
43358
  cmd.options.input = inputObj;
43313
43359
  }
43314
- if (cmd.name == 'o' && outputArr) {
43360
+ if (outputArr && (cmd.name == 'o' || cmd.name == 'info' && cmd.options.save_to)) {
43315
43361
  cmd.options.output = outputArr;
43316
43362
  }
43317
43363
  });