mapshaper 0.6.43 → 0.6.45

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.43";
3
+ var VERSION = "0.6.45";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -8,6 +8,7 @@
8
8
  get default () { return utils; },
9
9
  get getUniqueName () { return getUniqueName; },
10
10
  get isFunction () { return isFunction; },
11
+ get isPromise () { return isPromise; },
11
12
  get isObject () { return isObject; },
12
13
  get clamp () { return clamp; },
13
14
  get isArray () { return isArray; },
@@ -147,6 +148,10 @@
147
148
  return typeof obj == 'function';
148
149
  }
149
150
 
151
+ function isPromise(arg) {
152
+ return arg ? isFunction(arg.then) : false;
153
+ }
154
+
150
155
  function isObject(obj) {
151
156
  return obj === Object(obj); // via underscore
152
157
  }
@@ -1877,12 +1882,67 @@
1877
1882
  arcs.updateVertexData(nn, xx2, yy2, zz2);
1878
1883
  }
1879
1884
 
1885
+ function countFilteredVertices(zz, zlimit) {
1886
+ var count = 0;
1887
+ for (var i=0, n = zz.length; i<n; i++) {
1888
+ if (zz[i] >= zlimit) count++;
1889
+ }
1890
+ return count;
1891
+ }
1892
+
1893
+ function filterVertexData(o, zlimit) {
1894
+ if (!o.zz) error('Expected simplification data');
1895
+ var xx = o.xx,
1896
+ yy = o.yy,
1897
+ zz = o.zz,
1898
+ len2 = countFilteredVertices(zz, zlimit),
1899
+ arcCount = o.nn.length,
1900
+ xx2 = new Float64Array(len2),
1901
+ yy2 = new Float64Array(len2),
1902
+ zz2 = new Float64Array(len2),
1903
+ nn2 = new Int32Array(arcCount),
1904
+ i = 0, i2 = 0,
1905
+ n, n2;
1906
+
1907
+ for (var arcId=0; arcId < arcCount; arcId++) {
1908
+ n2 = 0;
1909
+ n = o.nn[arcId];
1910
+ for (var end = i+n; i < end; i++) {
1911
+ if (zz[i] >= zlimit) {
1912
+ xx2[i2] = xx[i];
1913
+ yy2[i2] = yy[i];
1914
+ zz2[i2] = zz[i];
1915
+ i2++;
1916
+ n2++;
1917
+ }
1918
+ }
1919
+ if (n2 == 1) {
1920
+ error("Collapsed arc");
1921
+ // This should not happen (endpoints should be z == Infinity)
1922
+ // Could handle like this, instead of throwing an error:
1923
+ // n2 = 0;
1924
+ // xx2.pop();
1925
+ // yy2.pop();
1926
+ // zz2.pop();
1927
+ }
1928
+ nn2[arcId] = n2;
1929
+ }
1930
+ return {
1931
+ xx: xx2,
1932
+ yy: yy2,
1933
+ zz: zz2,
1934
+ nn: nn2
1935
+ };
1936
+ }
1937
+
1880
1938
  var ArcUtils = /*#__PURE__*/Object.freeze({
1881
1939
  __proto__: null,
1882
1940
  absArcId: absArcId,
1883
1941
  calcArcBounds: calcArcBounds,
1884
1942
  deleteVertex: deleteVertex,
1885
- insertVertex: insertVertex
1943
+ insertVertex: insertVertex,
1944
+ countFilteredVertices: countFilteredVertices,
1945
+ filterVertexData: filterVertexData
1886
1946
  });
1887
1947
 
1888
1948
  var WGS84 = {
@@ -5354,17 +5414,6 @@
5354
5414
  initZData(zz || null);
5355
5415
  };
5356
5416
 
5357
- // Give access to raw data arrays...
5358
- this.getVertexData = function() {
5359
- return {
5360
- xx: _xx,
5361
- yy: _yy,
5362
- zz: _zz,
5363
- bb: _bb,
5364
- nn: _nn,
5365
- ii: _ii
5366
- };
5367
- };
5368
5417
 
5369
5418
  this.getCopy = function() {
5370
5419
  var copy = new ArcCollection(new Int32Array(_nn), new Float64Array(_xx),
@@ -5376,55 +5425,28 @@
5376
5425
  return copy;
5377
5426
  };
5378
5427
 
5428
+
5429
+ // Give access to raw data arrays...
5430
+ this.getVertexData = getVertexData;
5431
+
5432
+ function getVertexData() {
5433
+ return {
5434
+ xx: _xx,
5435
+ yy: _yy,
5436
+ zz: _zz,
5437
+ bb: _bb,
5438
+ nn: _nn,
5439
+ ii: _ii
5440
+ };
5441
+ }
5442
+
5379
5443
  function getFilteredPointCount() {
5380
- var zz = _zz, z = _zlimit;
5381
- if (!zz || !z) return this.getPointCount();
5382
- var count = 0;
5383
- for (var i=0, n = zz.length; i<n; i++) {
5384
- if (zz[i] >= z) count++;
5385
- }
5386
- return count;
5444
+ if (!_zz || !_zlimit) return this.getPointCount();
5445
+ return countFilteredVertices(_zz, _zlimit);
5387
5446
  }
5388
5447
 
5389
5448
  function getFilteredVertexData() {
5390
- var len2 = getFilteredPointCount();
5391
- var arcCount = _nn.length;
5392
- var xx2 = new Float64Array(len2),
5393
- yy2 = new Float64Array(len2),
5394
- zz2 = new Float64Array(len2),
5395
- nn2 = new Int32Array(arcCount),
5396
- i=0, i2 = 0,
5397
- n, n2;
5398
-
5399
- for (var arcId=0; arcId < arcCount; arcId++) {
5400
- n2 = 0;
5401
- n = _nn[arcId];
5402
- for (var end = i+n; i < end; i++) {
5403
- if (_zz[i] >= _zlimit) {
5404
- xx2[i2] = _xx[i];
5405
- yy2[i2] = _yy[i];
5406
- zz2[i2] = _zz[i];
5407
- i2++;
5408
- n2++;
5409
- }
5410
- }
5411
- if (n2 == 1) {
5412
- error("Collapsed arc");
5413
- // This should not happen (endpoints should be z == Infinity)
5414
- // Could handle like this, instead of throwing an error:
5415
- // n2 = 0;
5416
- // xx2.pop();
5417
- // yy2.pop();
5418
- // zz2.pop();
5419
- }
5420
- nn2[arcId] = n2;
5421
- }
5422
- return {
5423
- xx: xx2,
5424
- yy: yy2,
5425
- zz: zz2,
5426
- nn: nn2
5427
- };
5449
+ return filterVertexData(getVertexData(), _zlimit);
5428
5450
  }
5429
5451
 
5430
5452
  this.getFilteredCopy = function() {
@@ -10823,28 +10845,59 @@
10823
10845
  }
10824
10846
 
10825
10847
  // gui: (optional) gui instance
10826
- //
10848
+ // opts examples:
10849
+ // exporting from command line: { compact: true, file: 'tmp.msx', final: true }
10850
+ // exporting from gui export menu: {compact: true, format: 'msx'}
10851
+ // saving gui temp snapshot: {compact: false}
10827
10852
  async function exportDatasetsToPack(datasets, opts) {
10828
10853
  var obj = {
10829
10854
  version: 1,
10830
10855
  created: (new Date).toISOString(),
10831
- datasets: await Promise.all(datasets.map(exportDataset))
10856
+ datasets: await Promise.all(datasets.map(dataset => exportDataset(dataset, opts)))
10832
10857
  };
10833
- if (opts.compact) {
10834
- await applyCompression(obj);
10835
- }
10836
10858
  return obj;
10837
10859
  }
10838
10860
 
10839
- async function applyCompression(obj, opts) {
10840
- var promises = [];
10841
- obj.datasets.forEach(d => {
10842
- if (d.arcs) promises.push(compressArcs(d.arcs, opts));
10861
+ async function exportDataset(dataset, opts) {
10862
+ var arcs = dataset.arcs;
10863
+ var arcData = null;
10864
+ if (arcs) {
10865
+ arcData = arcs.getVertexData();
10866
+ arcData.zlimit = arcs.getRetainedInterval(); // TODO: add this to getVertexData()
10867
+ arcData = await exportArcData(arcData, opts);
10868
+ }
10869
+ return {
10870
+ arcs: arcData,
10871
+ info: dataset.info ? exportInfo(dataset.info) : null,
10872
+ layers: await Promise.all((dataset.layers || []).map(exportLayer))
10873
+ };
10874
+ }
10875
+
10876
+ // compress unpacked + uncompressed snapshot data in-place
10877
+ async function compressSnapshotForExport(obj) {
10878
+ var promises = obj.datasets.map(d => {
10879
+ compressDatasetForExport(d);
10843
10880
  });
10844
10881
  await Promise.all(promises);
10882
+ return;
10845
10883
  }
10846
10884
 
10847
- async function compressArcs(obj, opts) {
10885
+ async function compressDatasetForExport(obj) {
10886
+ if (!obj.arcs) return;
10887
+ var arcData = importArcData(obj.arcs); // convert buffers to typed arrays
10888
+ obj.arcs = await exportArcData(arcData, {compact: true}); // re-export to compressed buffers
10889
+ }
10890
+
10891
+ function flattenArcs(arcData) {
10892
+ if (arcData.zz && arcData.zlimit) {
10893
+ // replace unfiltered arc data with flattened arc data
10894
+ arcData = filterVertexData(arcData, arcData.zlimit);
10895
+ delete arcData.zz;
10896
+ }
10897
+ return arcData;
10898
+ }
10899
+
10900
+ async function gzipArcData(obj, opts) {
10848
10901
  var gzipOpts = Object.assign({level: 1, consume: false}, opts);
10849
10902
  var promises = [gzipAsync(obj.nn, gzipOpts), gzipAsync(obj.xx, gzipOpts), gzipAsync(obj.yy, gzipOpts)];
10850
10903
  if (obj.zz) promises.push(gzipAsync(obj.zz, gzipOpts));
@@ -10855,27 +10908,36 @@
10855
10908
  if (obj.zz) obj.zz = results.shift();
10856
10909
  }
10857
10910
 
10858
- async function exportDataset(dataset, opts) {
10911
+ function importArcData(obj) {
10859
10912
  return {
10860
- arcs: dataset.arcs ? exportArcs(dataset.arcs) : null,
10861
- info: dataset.info ? exportInfo(dataset.info) : null,
10862
- layers: await Promise.all((dataset.layers || []).map(exportLayer))
10913
+ nn: new Uint32Array(obj.nn.buffer, 0, obj.nn.length / 4),
10914
+ xx: new Float64Array(obj.xx.buffer, 0, obj.xx.length / 8),
10915
+ yy: new Float64Array(obj.yy.buffer, 0, obj.yy.length / 8),
10916
+ zz: obj.zz ? new Float64Array(obj.zz.buffer, 0, obj.zz.length / 8) : null,
10917
+ zlimit: obj.zlimit || 0
10863
10918
  };
10864
10919
  }
10865
10920
 
10866
- function typedArrayToBuffer(arr) {
10867
- return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
10868
- }
10869
-
10870
- function exportArcs(arcs) {
10871
- var data = arcs.getVertexData();
10872
- return {
10921
+ async function exportArcData(data, opts) {
10922
+ // TODO: consider removing arcs that are not referenced by any layer
10923
+ if (opts.compact && data.zz) {
10924
+ data = flattenArcs(data); // bake in any simplification
10925
+ }
10926
+ var output = {
10873
10927
  nn: typedArrayToBuffer(data.nn),
10874
10928
  xx: typedArrayToBuffer(data.xx),
10875
10929
  yy: typedArrayToBuffer(data.yy),
10876
10930
  zz: data.zz ? typedArrayToBuffer(data.zz) : null,
10877
- zlimit: arcs.getRetainedInterval()
10931
+ zlimit: data.zlimit || 0
10878
10932
  };
10933
+ if (opts.compact && data.zz) {
10934
+ await gzipArcData(output);
10935
+ }
10936
+ return output;
10937
+ }
10938
+
10939
+ function typedArrayToBuffer(arr) {
10940
+ return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
10879
10941
  }
10880
10942
 
10881
10943
  async function exportLayer(lyr) {
@@ -10909,8 +10971,8 @@
10909
10971
  exportPackedDatasets: exportPackedDatasets,
10910
10972
  pack: pack,
10911
10973
  exportDatasetsToPack: exportDatasetsToPack,
10912
- applyCompression: applyCompression,
10913
10974
  exportDataset: exportDataset,
10975
+ compressSnapshotForExport: compressSnapshotForExport,
10914
10976
  exportInfo: exportInfo
10915
10977
  });
10916
10978
 
@@ -12488,7 +12550,6 @@
12488
12550
  return +n.toPrecision(d);
12489
12551
  }
12490
12552
 
12491
-
12492
12553
  function roundToDigits(n, d) {
12493
12554
  return +n.toFixed(d); // string conversion makes this slow
12494
12555
  }
@@ -12552,6 +12613,37 @@
12552
12613
  });
12553
12614
  }
12554
12615
 
12616
+ const fround2 = (function() {
12617
+ var arr = new Float32Array(1);
12618
+ return function(x) {
12619
+ arr[0] = x;
12620
+ return arr[0];
12621
+ };
12622
+ })();
12623
+
12624
+ // This function rounds towards 0 (i.e. floor). TODO: round properly
12625
+ // @bits: number of bits to round
12626
+ // performance: about 3x slower than Math.fround()
12627
+ function getBinaryRoundingFunction(bits) {
12628
+ // double: sign (1) exponent (11) fraction (52)
12629
+ // single: sign (1) exponent (8) fraction (23)
12630
+ if ((bits >= 1 && bits <= 32) === false) {
12631
+ error('Invalid bits argument:', bits);
12632
+ }
12633
+ var isLE = require('os').endianness() == 'LE';
12634
+ var fp = new Float64Array(1);
12635
+ var leastBits = new Uint32Array(fp.buffer, isLE ? 0 : 4, 1);
12636
+ var mask = 2 ** 32 - 2 ** bits; // e.g. bits = 4 -> 0b11110000
12637
+ return function(x) {
12638
+ fp[0] = x;
12639
+ leastBits[0] = leastBits[0] & mask;
12640
+ return fp[0];
12641
+ };
12642
+ }
12643
+
12644
+ // "round to even" on the 23rd bit of the mantissa
12645
+ const fround = Math.fround || fround2;
12646
+
12555
12647
  function setCoordinatePrecision(dataset, precision) {
12556
12648
  var round = getRoundingFunction(precision);
12557
12649
  // var dissolvePolygon, nodes;
@@ -12586,6 +12678,9 @@
12586
12678
  getRoundedCoordString: getRoundedCoordString,
12587
12679
  getRoundedCoords: getRoundedCoords,
12588
12680
  roundPoints: roundPoints,
12681
+ fround2: fround2,
12682
+ getBinaryRoundingFunction: getBinaryRoundingFunction,
12683
+ fround: fround,
12589
12684
  setCoordinatePrecision: setCoordinatePrecision
12590
12685
  });
12591
12686
 
@@ -15914,6 +16009,67 @@
15914
16009
  formatIntersectingSegment: formatIntersectingSegment
15915
16010
  });
15916
16011
 
16012
+ // Remap any references to duplicate arcs in paths to use the same arcs
16013
+ // Remove any unused arcs from the dataset's ArcCollection.
16014
+ // Return a NodeCollection
16015
+ function cleanArcReferences(dataset) {
16016
+ var nodes = new NodeCollection(dataset.arcs);
16017
+ var map = findDuplicateArcs(nodes);
16018
+ var dropCount;
16019
+ if (map) {
16020
+ replaceIndexedArcIds(dataset, map);
16021
+ }
16022
+ dropCount = deleteUnusedArcs(dataset);
16023
+ if (dropCount > 0) {
16024
+ // rebuild nodes if arcs have changed
16025
+ nodes = new NodeCollection(dataset.arcs);
16026
+ }
16027
+ return nodes;
16028
+ }
16029
+
16030
+ function deleteUnusedArcs(dataset) {
16031
+ var test = getArcPresenceTest2(dataset.layers, dataset.arcs);
16032
+ var count1 = dataset.arcs.size();
16033
+ var map = dataset.arcs.deleteArcs(test); // condenses arcs
16034
+ var count2 = dataset.arcs.size();
16035
+ var deleteCount = count1 - count2;
16036
+ if (deleteCount > 0) {
16037
+ replaceIndexedArcIds(dataset, map);
16038
+ }
16039
+ return deleteCount;
16040
+ }
16041
+
16042
+ // @map an Object mapping old to new ids
16043
+ function replaceIndexedArcIds(dataset, map) {
16044
+ var remapPath = function(ids) {
16045
+ var arcId, absId, id2;
16046
+ for (var i=0; i<ids.length; i++) {
16047
+ arcId = ids[i];
16048
+ absId = absArcId(arcId);
16049
+ id2 = map[absId];
16050
+ ids[i] = arcId == absId ? id2 : ~id2;
16051
+ }
16052
+ return ids;
16053
+ };
16054
+ dataset.layers.forEach(function(lyr) {
16055
+ if (layerHasPaths(lyr)) {
16056
+ editShapes(lyr.shapes, remapPath);
16057
+ }
16058
+ });
16059
+ }
16060
+
16061
+ function findDuplicateArcs(nodes) {
16062
+ var map = new Int32Array(nodes.arcs.size()),
16063
+ count = 0,
16064
+ i2;
16065
+ for (var i=0, n=nodes.arcs.size(); i<n; i++) {
16066
+ i2 = nodes.findDuplicateArc(i);
16067
+ map[i] = i2;
16068
+ if (i != i2) count++;
16069
+ }
16070
+ return count > 0 ? map : null;
16071
+ }
16072
+
15917
16073
  // Functions for dividing polygons and polygons at points where arc-segments intersect
15918
16074
 
15919
16075
  // TODO:
@@ -16003,68 +16159,6 @@
16003
16159
  }
16004
16160
 
16005
16161
 
16006
- // Remap any references to duplicate arcs in paths to use the same arcs
16007
- // Remove any unused arcs from the dataset's ArcCollection.
16008
- // Return a NodeCollection
16009
- function cleanArcReferences(dataset) {
16010
- var nodes = new NodeCollection(dataset.arcs);
16011
- var map = findDuplicateArcs(nodes);
16012
- var dropCount;
16013
- if (map) {
16014
- replaceIndexedArcIds(dataset, map);
16015
- }
16016
- dropCount = deleteUnusedArcs(dataset);
16017
- if (dropCount > 0) {
16018
- // rebuild nodes if arcs have changed
16019
- nodes = new NodeCollection(dataset.arcs);
16020
- }
16021
- return nodes;
16022
- }
16023
-
16024
-
16025
- // @map an Object mapping old to new ids
16026
- function replaceIndexedArcIds(dataset, map) {
16027
- var remapPath = function(ids) {
16028
- var arcId, absId, id2;
16029
- for (var i=0; i<ids.length; i++) {
16030
- arcId = ids[i];
16031
- absId = absArcId(arcId);
16032
- id2 = map[absId];
16033
- ids[i] = arcId == absId ? id2 : ~id2;
16034
- }
16035
- return ids;
16036
- };
16037
- dataset.layers.forEach(function(lyr) {
16038
- if (layerHasPaths(lyr)) {
16039
- editShapes(lyr.shapes, remapPath);
16040
- }
16041
- });
16042
- }
16043
-
16044
- function findDuplicateArcs(nodes) {
16045
- var map = new Int32Array(nodes.arcs.size()),
16046
- count = 0,
16047
- i2;
16048
- for (var i=0, n=nodes.arcs.size(); i<n; i++) {
16049
- i2 = nodes.findDuplicateArc(i);
16050
- map[i] = i2;
16051
- if (i != i2) count++;
16052
- }
16053
- return count > 0 ? map : null;
16054
- }
16055
-
16056
- function deleteUnusedArcs(dataset) {
16057
- var test = getArcPresenceTest2(dataset.layers, dataset.arcs);
16058
- var count1 = dataset.arcs.size();
16059
- var map = dataset.arcs.deleteArcs(test); // condenses arcs
16060
- var count2 = dataset.arcs.size();
16061
- var deleteCount = count1 - count2;
16062
- if (deleteCount > 0) {
16063
- replaceIndexedArcIds(dataset, map);
16064
- }
16065
- return deleteCount;
16066
- }
16067
-
16068
16162
  // Return a function for updating a path (array of arc ids)
16069
16163
  // @map array generated by insertCutPoints()
16070
16164
  // @arcCount number of arcs in divided collection (kludge)
@@ -16458,7 +16552,8 @@
16458
16552
  if (id >= 0 && id < n) {
16459
16553
  return index[id] - 1;
16460
16554
  } else {
16461
- error('Invalid index');
16555
+ return -1;
16556
+ // error('Invalid index');
16462
16557
  }
16463
16558
  };
16464
16559
  }
@@ -17419,8 +17514,9 @@
17419
17514
  exporter = GeoJSON.exporters[lyr.geometry_type],
17420
17515
  geom = shape ? exporter(shape, dataset.arcs, opts) : null,
17421
17516
  obj = null;
17517
+
17422
17518
  if (asFeatures) {
17423
- obj = GeoJSON.toFeature(geom, properties ? properties[i] : null);
17519
+ obj = composeFeature(geom, properties ? properties[i] : null, opts);
17424
17520
  if (ids) {
17425
17521
  obj.id = ids[i];
17426
17522
  }
@@ -17444,6 +17540,20 @@
17444
17540
  }, []);
17445
17541
  }
17446
17542
 
17543
+ function composeFeature(geom, properties, opts) {
17544
+ var feat = GeoJSON.toFeature(geom, properties);
17545
+ if (Array.isArray(opts.hoist) && properties) {
17546
+ // don't modify properties of source feature
17547
+ feat.properties = Object.assign({}, properties);
17548
+ opts.hoist.forEach(field => {
17549
+ if (properties.hasOwnProperty(field)) {
17550
+ feat[field] = properties[field];
17551
+ delete feat.properties[field];
17552
+ }
17553
+ });
17554
+ }
17555
+ return feat;
17556
+ }
17447
17557
 
17448
17558
  function getRFC7946Warnings(dataset) {
17449
17559
  var P = getDatasetCRS(dataset);
@@ -18412,7 +18522,8 @@
18412
18522
  // null values indicate the lack of a function for parsing/identifying this property
18413
18523
  // (in which case a heuristic is used for distinguishing a string literal from an expression)
18414
18524
  var stylePropertyTypes = {
18415
- css: null,
18525
+ // css: null,
18526
+ css: 'inlinecss',
18416
18527
  class: 'classname',
18417
18528
  dx: 'measure',
18418
18529
  dy: 'measure',
@@ -18599,6 +18710,8 @@
18599
18710
  val = isPattern(strVal) ? strVal : null;
18600
18711
  } else if (type == 'boolean') {
18601
18712
  val = parseBoolean(strVal);
18713
+ } else if (type == 'inlinecss') {
18714
+ val = strVal; // TODO: validate
18602
18715
  }
18603
18716
  // else {
18604
18717
  // // unknown type -- assume literal value
@@ -21534,6 +21647,14 @@ ${svg}
21534
21647
  }
21535
21648
  return memo.concat(exportFileContent(dataset, opts));
21536
21649
  }, []);
21650
+
21651
+ if (opts.bbox_index) {
21652
+ // If rounding or quantization are applied during export, bounds may
21653
+ // change somewhat... consider adding a bounds property to each layer during
21654
+ // export when appropriate.
21655
+ files.push(createIndexFile(datasets));
21656
+ }
21657
+
21537
21658
  // need unique names for multiple output files
21538
21659
  assignUniqueFileNames(files);
21539
21660
 
@@ -21591,15 +21712,7 @@ ${svg}
21591
21712
  }
21592
21713
 
21593
21714
  validateLayerData(dataset.layers);
21594
-
21595
21715
  files = exporter(dataset, opts).concat(files);
21596
- // If rounding or quantization are applied during export, bounds may
21597
- // change somewhat... consider adding a bounds property to each layer during
21598
- // export when appropriate.
21599
- if (opts.bbox_index) {
21600
- files.push(createIndexFile(dataset));
21601
- }
21602
-
21603
21716
  validateFileNames(files);
21604
21717
  return files;
21605
21718
  }
@@ -21620,13 +21733,16 @@ ${svg}
21620
21733
  // Generate json file with bounding boxes and names of each export layer
21621
21734
  // TODO: consider making this a command, or at least make format settable
21622
21735
  //
21623
- function createIndexFile(dataset) {
21624
- var index = dataset.layers.map(function(lyr) {
21625
- var bounds = getLayerBounds(lyr, dataset.arcs);
21626
- return {
21627
- bbox: bounds.toArray(),
21628
- name: lyr.name
21629
- };
21736
+ function createIndexFile(datasets) {
21737
+ var index = [];
21738
+ datasets.forEach(function(dataset) {
21739
+ dataset.layers.forEach(function(lyr) {
21740
+ var bounds = getLayerBounds(lyr, dataset.arcs);
21741
+ index.push({
21742
+ bbox: bounds.toArray(),
21743
+ name: lyr.name
21744
+ });
21745
+ });
21630
21746
  });
21631
21747
 
21632
21748
  return {
@@ -23349,7 +23465,7 @@ ${svg}
23349
23465
  .option('string-fields', stringFieldsOpt)
23350
23466
  .option('field-types', fieldTypesOpt)
23351
23467
  .option('name', {
23352
- describe: 'Rename the imported layer(s)'
23468
+ describe: 'rename the imported layer(s)'
23353
23469
  })
23354
23470
  .option('geometry-type', {
23355
23471
  // undocumented; GeoJSON import rejects all but one kind of geometry
@@ -23518,6 +23634,10 @@ ${svg}
23518
23634
  describe: '[GeoJSON/JSON] output newline-delimited features or records',
23519
23635
  type: 'flag'
23520
23636
  })
23637
+ .option('hoist', {
23638
+ describe: '[GeoJSON] move properties to the root level of each Feature',
23639
+ type: 'strings'
23640
+ })
23521
23641
  .option('width', {
23522
23642
  describe: '[SVG/TopoJSON] pixel width of output (SVG default is 800)',
23523
23643
  type: 'number'
@@ -24696,9 +24816,9 @@ ${svg}
24696
24816
  .option('class', {
24697
24817
  describe: 'name of CSS class or classes (space-separated)'
24698
24818
  })
24699
- // .option('css', {
24700
- // describe: 'inline css style'
24701
- // })
24819
+ .option('css', {
24820
+ describe: 'inline css style'
24821
+ })
24702
24822
  .option('fill', {
24703
24823
  describe: 'fill color; examples: #eee pink rgba(0, 0, 0, 0.2)'
24704
24824
  })
@@ -33468,7 +33588,6 @@ ${svg}
33468
33588
  stop('Missing a data field to classify');
33469
33589
  }
33470
33590
 
33471
-
33472
33591
  // get the number of classes and the number of values
33473
33592
  //
33474
33593
  // expand categories if value is '*'
@@ -41274,21 +41393,24 @@ ${svg}
41274
41393
  parseConsoleCommands: parseConsoleCommands
41275
41394
  });
41276
41395
 
41277
- cmd.run = function(job, targets, opts, cb) {
41396
+ cmd.run = async function(job, targets, opts) {
41278
41397
  var commandStr, commands;
41279
41398
  if (!opts.expression) {
41280
41399
  stop("Missing expression parameter");
41281
41400
  }
41282
41401
  commandStr = runGlobalExpression(opts.expression, targets);
41402
+ // Support async functions as expressions
41403
+ if (utils.isPromise(commandStr)) {
41404
+ commandStr = await commandStr;
41405
+ }
41283
41406
  if (commandStr) {
41284
41407
  message(`command: [${commandStr}]`);
41285
41408
  commands = parseCommands(commandStr);
41286
- runParsedCommands(commands, job, cb);
41287
- } else {
41288
- cb(null);
41409
+ await utils.promisify(runParsedCommands)(commands, job);
41289
41410
  }
41290
41411
  };
41291
41412
 
41413
+ // This could return a Promise or a value or nothing
41292
41414
  function runGlobalExpression(expression, targets) {
41293
41415
  var ctx = getBaseContext();
41294
41416
  var output, targetData;
@@ -42415,6 +42537,7 @@ ${svg}
42415
42537
  shapes = lyr0.shapes,
42416
42538
  index = {},
42417
42539
  splitLayers = [],
42540
+ n = getFeatureCount(lyr0),
42418
42541
  namer;
42419
42542
 
42420
42543
  if (opts.ids) {
@@ -42423,11 +42546,18 @@ ${svg}
42423
42546
  namer = getSplitNameFunction(lyr0, expression);
42424
42547
  }
42425
42548
 
42549
+ // // halt if split field is missing
42426
42550
  // if (splitField) {
42427
42551
  // internal.requireDataField(lyr0, splitField);
42428
42552
  // }
42429
42553
 
42430
- utils.repeat(getFeatureCount(lyr0), function(i) {
42554
+ // if input layer is empty, return original layer
42555
+ // TODO: consider halting
42556
+ if (n === 0) {
42557
+ return [lyr0];
42558
+ }
42559
+
42560
+ utils.repeat(n, function(i) {
42431
42561
  var name = namer(i),
42432
42562
  lyr;
42433
42563
 
@@ -42450,6 +42580,7 @@ ${svg}
42450
42580
  lyr.data.getRecords().push(properties[i]);
42451
42581
  }
42452
42582
  });
42583
+
42453
42584
  return splitLayers;
42454
42585
  };
42455
42586
 
@@ -42460,26 +42591,34 @@ ${svg}
42460
42591
  };
42461
42592
  }
42462
42593
 
42463
- function getDefaultSplitFunction(lyr) {
42464
- // if not splitting on an expression and layer is unnamed, name split-apart layers
42465
- // like: split-1, split-2, ...
42466
- return function(i) {
42467
- return (lyr && lyr.name || 'split') + '-' + (i + 1);
42468
- };
42469
- }
42470
-
42471
- function getSplitNameFunction(lyr, exp) {
42594
+ function getSplitNameFunction(lyr, arg) {
42472
42595
  var compiled;
42473
- if (!exp) return getDefaultSplitFunction(lyr);
42596
+ if (!arg) {
42597
+ // if not splitting on an expression and layer is unnamed, name split-apart layers
42598
+ // like: split-1, split-2, ...
42599
+ return function(i) {
42600
+ return (lyr && lyr.name || 'split') + '-' + (i + 1);
42601
+ };
42602
+ }
42603
+ if (lyr.data && lyr.data.fieldExists(arg)) {
42604
+ // Argument is a field name
42605
+ return function(i) {
42606
+ var rec = lyr.data.getRecords()[i];
42607
+ return rec ? valueToLayerName(rec[arg]) : '';
42608
+ };
42609
+ }
42610
+ // Assume: argument is an expression
42474
42611
  lyr = {name: lyr.name, data: lyr.data}; // remove shape info
42475
- compiled = compileValueExpression(exp, lyr, null);
42612
+ compiled = compileValueExpression(arg, lyr, null);
42476
42613
  return function(i) {
42477
42614
  var val = compiled(i);
42478
- return String(val);
42479
- // return val || val === 0 ? String(val) : '';
42615
+ return valueToLayerName(val);
42480
42616
  };
42481
42617
  }
42482
42618
 
42619
+ function valueToLayerName(val) {
42620
+ return String(val);
42621
+ }
42483
42622
 
42484
42623
  // internal.getSplitKey = function(i, field, properties) {
42485
42624
  // var rec = field && properties ? properties[i] : null;
@@ -42515,6 +42654,9 @@ ${svg}
42515
42654
 
42516
42655
  cmd.svgStyle = function(lyr, dataset, opts) {
42517
42656
  var filter;
42657
+ if (getFeatureCount(lyr) === 0) {
42658
+ return;
42659
+ }
42518
42660
  if (!lyr.data) {
42519
42661
  initDataTable(lyr);
42520
42662
  }
@@ -43820,7 +43962,7 @@ ${svg}
43820
43962
  });
43821
43963
 
43822
43964
  } else if (name == 'run') {
43823
- await utils.promisify(cmd.run)(job, targets, opts);
43965
+ await cmd.run(job, targets, opts);
43824
43966
 
43825
43967
  } else if (name == 'scalebar') {
43826
43968
  cmd.scalebar(job.catalog, opts);
@@ -43843,7 +43985,7 @@ ${svg}
43843
43985
 
43844
43986
  } else if (name == 'snap') {
43845
43987
  // cmd.snap(targetDataset, opts);
43846
- applyCommandToEachTarget(targets, opts);
43988
+ applyCommandToEachTarget(cmd.snap, targets, opts);
43847
43989
 
43848
43990
  } else if (name == 'sort') {
43849
43991
  applyCommandToEachLayer(cmd.sortFeatures, targetLayers, arcs, opts);
@@ -44097,6 +44239,13 @@ ${svg}
44097
44239
  }
44098
44240
  });
44099
44241
 
44242
+ var lastCmd = commands[commands.length - 1];
44243
+ if (!runningInBrowser() && lastCmd.name == 'o') {
44244
+ // in CLI, set 'final' flag on final -o command, so the export function knows
44245
+ // that it can modify the output dataset in-place instead of making a copy.
44246
+ lastCmd.options.final = true;
44247
+ }
44248
+
44100
44249
  var batches = divideImportCommand(commands);
44101
44250
  utils.reduceAsync(batches, null, nextGroup, done);
44102
44251
 
@@ -44165,11 +44314,6 @@ ${svg}
44165
44314
  // @done: function([error], [job])
44166
44315
  //
44167
44316
  function runParsedCommands(commands, job, done) {
44168
- if (!runningInBrowser() && commands[commands.length-1].name == 'o') {
44169
- // in CLI, set 'final' flag on final -o command, so the export function knows
44170
- // that it can modify the output dataset in-place instead of making a copy.
44171
- commands[commands.length-1].options.final = true;
44172
- }
44173
44317
  if (!job) job = new Job();
44174
44318
  commands = readAndRemoveSettings(job, commands);
44175
44319
  if (!runningInBrowser()) {