mapshaper 0.6.44 → 0.6.46

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.44";
3
+ var VERSION = "0.6.46";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -1813,6 +1813,23 @@
1813
1813
  return [xmin, ymin, xmax, ymax];
1814
1814
  }
1815
1815
 
1816
+
1817
+ function findArcIdFromVertexId(i, ii) {
1818
+ // binary search
1819
+ // possible optimization: use interpolation to find a better partition value.
1820
+ var lower = 0, upper = ii.length - 1;
1821
+ var middle;
1822
+ while (lower < upper) {
1823
+ middle = Math.ceil((lower + upper) / 2);
1824
+ if (i < ii[middle]) {
1825
+ upper = middle - 1;
1826
+ } else {
1827
+ lower = middle;
1828
+ }
1829
+ }
1830
+ return lower; // assumes dataset is not empty
1831
+ }
1832
+
1816
1833
  function deleteVertex(arcs, i) {
1817
1834
  var data = arcs.getVertexData();
1818
1835
  var nn = data.nn;
@@ -1882,12 +1899,68 @@
1882
1899
  arcs.updateVertexData(nn, xx2, yy2, zz2);
1883
1900
  }
1884
1901
 
1902
+ function countFilteredVertices(zz, zlimit) {
1903
+ var count = 0;
1904
+ for (var i=0, n = zz.length; i<n; i++) {
1905
+ if (zz[i] >= zlimit) count++;
1906
+ }
1907
+ return count;
1908
+ }
1909
+
1910
+ function filterVertexData(o, zlimit) {
1911
+ if (!o.zz) error('Expected simplification data');
1912
+ var xx = o.xx,
1913
+ yy = o.yy,
1914
+ zz = o.zz,
1915
+ len2 = countFilteredVertices(zz, zlimit),
1916
+ arcCount = o.nn.length,
1917
+ xx2 = new Float64Array(len2),
1918
+ yy2 = new Float64Array(len2),
1919
+ zz2 = new Float64Array(len2),
1920
+ nn2 = new Int32Array(arcCount),
1921
+ i = 0, i2 = 0,
1922
+ n, n2;
1923
+
1924
+ for (var arcId=0; arcId < arcCount; arcId++) {
1925
+ n2 = 0;
1926
+ n = o.nn[arcId];
1927
+ for (var end = i+n; i < end; i++) {
1928
+ if (zz[i] >= zlimit) {
1929
+ xx2[i2] = xx[i];
1930
+ yy2[i2] = yy[i];
1931
+ zz2[i2] = zz[i];
1932
+ i2++;
1933
+ n2++;
1934
+ }
1935
+ }
1936
+ if (n2 == 1) {
1937
+ error("Collapsed arc");
1938
+ // This should not happen (endpoints should be z == Infinity)
1939
+ // Could handle like this, instead of throwing an error:
1940
+ // n2 = 0;
1941
+ // xx2.pop();
1942
+ // yy2.pop();
1943
+ // zz2.pop();
1944
+ }
1945
+ nn2[arcId] = n2;
1946
+ }
1947
+ return {
1948
+ xx: xx2,
1949
+ yy: yy2,
1950
+ zz: zz2,
1951
+ nn: nn2
1952
+ };
1953
+ }
1954
+
1885
1955
  var ArcUtils = /*#__PURE__*/Object.freeze({
1886
1956
  __proto__: null,
1887
1957
  absArcId: absArcId,
1888
1958
  calcArcBounds: calcArcBounds,
1959
+ findArcIdFromVertexId: findArcIdFromVertexId,
1889
1960
  deleteVertex: deleteVertex,
1890
- insertVertex: insertVertex
1961
+ insertVertex: insertVertex,
1962
+ countFilteredVertices: countFilteredVertices,
1963
+ filterVertexData: filterVertexData
1891
1964
  });
1892
1965
 
1893
1966
  var WGS84 = {
@@ -3350,7 +3423,7 @@
3350
3423
  var item;
3351
3424
  for (var i=0; i<arr.length; i++) {
3352
3425
  item = arr[i];
3353
- if (item instanceof Array) {
3426
+ if (Array.isArray(item)) {
3354
3427
  forEachArcId(item, cb);
3355
3428
  } else if (utils.isInteger(item)) {
3356
3429
  var val = cb(item);
@@ -5359,17 +5432,6 @@
5359
5432
  initZData(zz || null);
5360
5433
  };
5361
5434
 
5362
- // Give access to raw data arrays...
5363
- this.getVertexData = function() {
5364
- return {
5365
- xx: _xx,
5366
- yy: _yy,
5367
- zz: _zz,
5368
- bb: _bb,
5369
- nn: _nn,
5370
- ii: _ii
5371
- };
5372
- };
5373
5435
 
5374
5436
  this.getCopy = function() {
5375
5437
  var copy = new ArcCollection(new Int32Array(_nn), new Float64Array(_xx),
@@ -5381,55 +5443,28 @@
5381
5443
  return copy;
5382
5444
  };
5383
5445
 
5446
+
5447
+ // Give access to raw data arrays...
5448
+ this.getVertexData = getVertexData;
5449
+
5450
+ function getVertexData() {
5451
+ return {
5452
+ xx: _xx,
5453
+ yy: _yy,
5454
+ zz: _zz,
5455
+ bb: _bb,
5456
+ nn: _nn,
5457
+ ii: _ii
5458
+ };
5459
+ }
5460
+
5384
5461
  function getFilteredPointCount() {
5385
- var zz = _zz, z = _zlimit;
5386
- if (!zz || !z) return this.getPointCount();
5387
- var count = 0;
5388
- for (var i=0, n = zz.length; i<n; i++) {
5389
- if (zz[i] >= z) count++;
5390
- }
5391
- return count;
5462
+ if (!_zz || !_zlimit) return this.getPointCount();
5463
+ return countFilteredVertices(_zz, _zlimit);
5392
5464
  }
5393
5465
 
5394
5466
  function getFilteredVertexData() {
5395
- var len2 = getFilteredPointCount();
5396
- var arcCount = _nn.length;
5397
- var xx2 = new Float64Array(len2),
5398
- yy2 = new Float64Array(len2),
5399
- zz2 = new Float64Array(len2),
5400
- nn2 = new Int32Array(arcCount),
5401
- i=0, i2 = 0,
5402
- n, n2;
5403
-
5404
- for (var arcId=0; arcId < arcCount; arcId++) {
5405
- n2 = 0;
5406
- n = _nn[arcId];
5407
- for (var end = i+n; i < end; i++) {
5408
- if (_zz[i] >= _zlimit) {
5409
- xx2[i2] = _xx[i];
5410
- yy2[i2] = _yy[i];
5411
- zz2[i2] = _zz[i];
5412
- i2++;
5413
- n2++;
5414
- }
5415
- }
5416
- if (n2 == 1) {
5417
- error("Collapsed arc");
5418
- // This should not happen (endpoints should be z == Infinity)
5419
- // Could handle like this, instead of throwing an error:
5420
- // n2 = 0;
5421
- // xx2.pop();
5422
- // yy2.pop();
5423
- // zz2.pop();
5424
- }
5425
- nn2[arcId] = n2;
5426
- }
5427
- return {
5428
- xx: xx2,
5429
- yy: yy2,
5430
- zz: zz2,
5431
- nn: nn2
5432
- };
5467
+ return filterVertexData(getVertexData(), _zlimit);
5433
5468
  }
5434
5469
 
5435
5470
  this.getFilteredCopy = function() {
@@ -6936,6 +6971,15 @@
6936
6971
  utils.defaults(destInfo, srcInfo);
6937
6972
  }
6938
6973
 
6974
+ function copyDatasetInfo(info) {
6975
+ // not a deep copy... objects like info.crs are read-only, so copy-by-reference
6976
+ // should be ok
6977
+ var info2 = Object.assign({}, info);
6978
+ if (Array.isArray(info.input_files)) {
6979
+ info2.input_files = info.input_files.concat();
6980
+ }
6981
+ return info2;
6982
+ }
6939
6983
 
6940
6984
  function splitApartLayers(dataset, layers) {
6941
6985
  var datasets = [];
@@ -7102,6 +7146,7 @@
7102
7146
  __proto__: null,
7103
7147
  splitDataset: splitDataset,
7104
7148
  mergeDatasetInfo: mergeDatasetInfo,
7149
+ copyDatasetInfo: copyDatasetInfo,
7105
7150
  splitApartLayers: splitApartLayers,
7106
7151
  copyDataset: copyDataset,
7107
7152
  copyDatasetForExport: copyDatasetForExport,
@@ -10828,28 +10873,59 @@
10828
10873
  }
10829
10874
 
10830
10875
  // gui: (optional) gui instance
10831
- //
10876
+ // opts examples:
10877
+ // exporting from command line: { compact: true, file: 'tmp.msx', final: true }
10878
+ // exporting from gui export menu: {compact: true, format: 'msx'}
10879
+ // saving gui temp snapshot: {compact: false}
10832
10880
  async function exportDatasetsToPack(datasets, opts) {
10833
10881
  var obj = {
10834
10882
  version: 1,
10835
10883
  created: (new Date).toISOString(),
10836
- datasets: await Promise.all(datasets.map(exportDataset))
10884
+ datasets: await Promise.all(datasets.map(dataset => exportDataset(dataset, opts)))
10837
10885
  };
10838
- if (opts.compact) {
10839
- await applyCompression(obj);
10840
- }
10841
10886
  return obj;
10842
10887
  }
10843
10888
 
10844
- async function applyCompression(obj, opts) {
10845
- var promises = [];
10846
- obj.datasets.forEach(d => {
10847
- if (d.arcs) promises.push(compressArcs(d.arcs, opts));
10889
+ async function exportDataset(dataset, opts) {
10890
+ var arcs = dataset.arcs;
10891
+ var arcData = null;
10892
+ if (arcs) {
10893
+ arcData = arcs.getVertexData();
10894
+ arcData.zlimit = arcs.getRetainedInterval(); // TODO: add this to getVertexData()
10895
+ arcData = await exportArcData(arcData, opts);
10896
+ }
10897
+ return {
10898
+ arcs: arcData,
10899
+ info: dataset.info ? exportInfo(dataset.info) : null,
10900
+ layers: await Promise.all((dataset.layers || []).map(exportLayer))
10901
+ };
10902
+ }
10903
+
10904
+ // compress unpacked + uncompressed snapshot data in-place
10905
+ async function compressSnapshotForExport(obj) {
10906
+ var promises = obj.datasets.map(d => {
10907
+ compressDatasetForExport(d);
10848
10908
  });
10849
10909
  await Promise.all(promises);
10910
+ return;
10911
+ }
10912
+
10913
+ async function compressDatasetForExport(obj) {
10914
+ if (!obj.arcs) return;
10915
+ var arcData = importArcData(obj.arcs); // convert buffers to typed arrays
10916
+ obj.arcs = await exportArcData(arcData, {compact: true}); // re-export to compressed buffers
10850
10917
  }
10851
10918
 
10852
- async function compressArcs(obj, opts) {
10919
+ function flattenArcs(arcData) {
10920
+ if (arcData.zz && arcData.zlimit) {
10921
+ // replace unfiltered arc data with flattened arc data
10922
+ arcData = filterVertexData(arcData, arcData.zlimit);
10923
+ delete arcData.zz;
10924
+ }
10925
+ return arcData;
10926
+ }
10927
+
10928
+ async function gzipArcData(obj, opts) {
10853
10929
  var gzipOpts = Object.assign({level: 1, consume: false}, opts);
10854
10930
  var promises = [gzipAsync(obj.nn, gzipOpts), gzipAsync(obj.xx, gzipOpts), gzipAsync(obj.yy, gzipOpts)];
10855
10931
  if (obj.zz) promises.push(gzipAsync(obj.zz, gzipOpts));
@@ -10860,27 +10936,36 @@
10860
10936
  if (obj.zz) obj.zz = results.shift();
10861
10937
  }
10862
10938
 
10863
- async function exportDataset(dataset, opts) {
10939
+ function importArcData(obj) {
10864
10940
  return {
10865
- arcs: dataset.arcs ? exportArcs(dataset.arcs) : null,
10866
- info: dataset.info ? exportInfo(dataset.info) : null,
10867
- layers: await Promise.all((dataset.layers || []).map(exportLayer))
10941
+ nn: new Uint32Array(obj.nn.buffer, 0, obj.nn.length / 4),
10942
+ xx: new Float64Array(obj.xx.buffer, 0, obj.xx.length / 8),
10943
+ yy: new Float64Array(obj.yy.buffer, 0, obj.yy.length / 8),
10944
+ zz: obj.zz ? new Float64Array(obj.zz.buffer, 0, obj.zz.length / 8) : null,
10945
+ zlimit: obj.zlimit || 0
10868
10946
  };
10869
10947
  }
10870
10948
 
10871
- function typedArrayToBuffer(arr) {
10872
- return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
10873
- }
10874
-
10875
- function exportArcs(arcs) {
10876
- var data = arcs.getVertexData();
10877
- return {
10949
+ async function exportArcData(data, opts) {
10950
+ // TODO: consider removing arcs that are not referenced by any layer
10951
+ if (opts.compact && data.zz) {
10952
+ data = flattenArcs(data); // bake in any simplification
10953
+ }
10954
+ var output = {
10878
10955
  nn: typedArrayToBuffer(data.nn),
10879
10956
  xx: typedArrayToBuffer(data.xx),
10880
10957
  yy: typedArrayToBuffer(data.yy),
10881
10958
  zz: data.zz ? typedArrayToBuffer(data.zz) : null,
10882
- zlimit: arcs.getRetainedInterval()
10959
+ zlimit: data.zlimit || 0
10883
10960
  };
10961
+ if (opts.compact && data.zz) {
10962
+ await gzipArcData(output);
10963
+ }
10964
+ return output;
10965
+ }
10966
+
10967
+ function typedArrayToBuffer(arr) {
10968
+ return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
10884
10969
  }
10885
10970
 
10886
10971
  async function exportLayer(lyr) {
@@ -10914,8 +10999,8 @@
10914
10999
  exportPackedDatasets: exportPackedDatasets,
10915
11000
  pack: pack,
10916
11001
  exportDatasetsToPack: exportDatasetsToPack,
10917
- applyCompression: applyCompression,
10918
11002
  exportDataset: exportDataset,
11003
+ compressSnapshotForExport: compressSnapshotForExport,
10919
11004
  exportInfo: exportInfo
10920
11005
  });
10921
11006
 
@@ -15424,6 +15509,17 @@
15424
15509
 
15425
15510
  // Keep track of whether positive or negative integer ids are 'used' or not.
15426
15511
 
15512
+
15513
+ function SimpleIdTestIndex(n) {
15514
+ var index = new Uint8Array(n);
15515
+ this.setId = function(id) {
15516
+ index[id] = 1;
15517
+ };
15518
+ this.hasId = function(id) {
15519
+ return index[id] === 1;
15520
+ };
15521
+ }
15522
+
15427
15523
  function IdTestIndex(n) {
15428
15524
  var index = new Uint8Array(n);
15429
15525
  var setList = [];
@@ -15686,15 +15782,30 @@
15686
15782
  getHoleDivider: getHoleDivider
15687
15783
  });
15688
15784
 
15689
- // Convert an array of intersections into an ArcCollection (for display)
15690
- //
15691
-
15692
15785
  function getIntersectionPoints(intersections) {
15693
15786
  return intersections.map(function(obj) {
15694
15787
  return [obj.x, obj.y];
15695
15788
  });
15696
15789
  }
15697
15790
 
15791
+ function getIntersectionLayer(intersections, lyr, arcs) {
15792
+ // return {geometry_type: 'point', shapes: [getIntersectionPoints(XX)]};
15793
+ var ii = arcs.getVertexData().ii;
15794
+ var index = new SimpleIdTestIndex(arcs.size());
15795
+ forEachArcId(lyr.shapes, arcId => {
15796
+ index.setId(absArcId(arcId));
15797
+ });
15798
+ var points = [];
15799
+ intersections.forEach(obj => {
15800
+ var arc1 = findArcIdFromVertexId(obj.a[0], ii);
15801
+ var arc2 = findArcIdFromVertexId(obj.b[0], ii);
15802
+ if (index.hasId(arc1) && index.hasId(arc2)) {
15803
+ points.push([obj.x, obj.y]);
15804
+ }
15805
+ });
15806
+ return {geometry_type: 'point', shapes: [points]};
15807
+ }
15808
+
15698
15809
  // Identify intersecting segments in an ArcCollection
15699
15810
  //
15700
15811
  // To find all intersections:
@@ -15942,6 +16053,7 @@
15942
16053
  var SegmentIntersection = /*#__PURE__*/Object.freeze({
15943
16054
  __proto__: null,
15944
16055
  getIntersectionPoints: getIntersectionPoints,
16056
+ getIntersectionLayer: getIntersectionLayer,
15945
16057
  findSegmentIntersections: findSegmentIntersections,
15946
16058
  sortIntersections: sortIntersections,
15947
16059
  dedupIntersections: dedupIntersections,
@@ -15952,6 +16064,67 @@
15952
16064
  formatIntersectingSegment: formatIntersectingSegment
15953
16065
  });
15954
16066
 
16067
+ // Remap any references to duplicate arcs in paths to use the same arcs
16068
+ // Remove any unused arcs from the dataset's ArcCollection.
16069
+ // Return a NodeCollection
16070
+ function cleanArcReferences(dataset) {
16071
+ var nodes = new NodeCollection(dataset.arcs);
16072
+ var map = findDuplicateArcs(nodes);
16073
+ var dropCount;
16074
+ if (map) {
16075
+ replaceIndexedArcIds(dataset, map);
16076
+ }
16077
+ dropCount = deleteUnusedArcs(dataset);
16078
+ if (dropCount > 0) {
16079
+ // rebuild nodes if arcs have changed
16080
+ nodes = new NodeCollection(dataset.arcs);
16081
+ }
16082
+ return nodes;
16083
+ }
16084
+
16085
+ function deleteUnusedArcs(dataset) {
16086
+ var test = getArcPresenceTest2(dataset.layers, dataset.arcs);
16087
+ var count1 = dataset.arcs.size();
16088
+ var map = dataset.arcs.deleteArcs(test); // condenses arcs
16089
+ var count2 = dataset.arcs.size();
16090
+ var deleteCount = count1 - count2;
16091
+ if (deleteCount > 0) {
16092
+ replaceIndexedArcIds(dataset, map);
16093
+ }
16094
+ return deleteCount;
16095
+ }
16096
+
16097
+ // @map an Object mapping old to new ids
16098
+ function replaceIndexedArcIds(dataset, map) {
16099
+ var remapPath = function(ids) {
16100
+ var arcId, absId, id2;
16101
+ for (var i=0; i<ids.length; i++) {
16102
+ arcId = ids[i];
16103
+ absId = absArcId(arcId);
16104
+ id2 = map[absId];
16105
+ ids[i] = arcId == absId ? id2 : ~id2;
16106
+ }
16107
+ return ids;
16108
+ };
16109
+ dataset.layers.forEach(function(lyr) {
16110
+ if (layerHasPaths(lyr)) {
16111
+ editShapes(lyr.shapes, remapPath);
16112
+ }
16113
+ });
16114
+ }
16115
+
16116
+ function findDuplicateArcs(nodes) {
16117
+ var map = new Int32Array(nodes.arcs.size()),
16118
+ count = 0,
16119
+ i2;
16120
+ for (var i=0, n=nodes.arcs.size(); i<n; i++) {
16121
+ i2 = nodes.findDuplicateArc(i);
16122
+ map[i] = i2;
16123
+ if (i != i2) count++;
16124
+ }
16125
+ return count > 0 ? map : null;
16126
+ }
16127
+
15955
16128
  // Functions for dividing polygons and polygons at points where arc-segments intersect
15956
16129
 
15957
16130
  // TODO:
@@ -16041,68 +16214,6 @@
16041
16214
  }
16042
16215
 
16043
16216
 
16044
- // Remap any references to duplicate arcs in paths to use the same arcs
16045
- // Remove any unused arcs from the dataset's ArcCollection.
16046
- // Return a NodeCollection
16047
- function cleanArcReferences(dataset) {
16048
- var nodes = new NodeCollection(dataset.arcs);
16049
- var map = findDuplicateArcs(nodes);
16050
- var dropCount;
16051
- if (map) {
16052
- replaceIndexedArcIds(dataset, map);
16053
- }
16054
- dropCount = deleteUnusedArcs(dataset);
16055
- if (dropCount > 0) {
16056
- // rebuild nodes if arcs have changed
16057
- nodes = new NodeCollection(dataset.arcs);
16058
- }
16059
- return nodes;
16060
- }
16061
-
16062
-
16063
- // @map an Object mapping old to new ids
16064
- function replaceIndexedArcIds(dataset, map) {
16065
- var remapPath = function(ids) {
16066
- var arcId, absId, id2;
16067
- for (var i=0; i<ids.length; i++) {
16068
- arcId = ids[i];
16069
- absId = absArcId(arcId);
16070
- id2 = map[absId];
16071
- ids[i] = arcId == absId ? id2 : ~id2;
16072
- }
16073
- return ids;
16074
- };
16075
- dataset.layers.forEach(function(lyr) {
16076
- if (layerHasPaths(lyr)) {
16077
- editShapes(lyr.shapes, remapPath);
16078
- }
16079
- });
16080
- }
16081
-
16082
- function findDuplicateArcs(nodes) {
16083
- var map = new Int32Array(nodes.arcs.size()),
16084
- count = 0,
16085
- i2;
16086
- for (var i=0, n=nodes.arcs.size(); i<n; i++) {
16087
- i2 = nodes.findDuplicateArc(i);
16088
- map[i] = i2;
16089
- if (i != i2) count++;
16090
- }
16091
- return count > 0 ? map : null;
16092
- }
16093
-
16094
- function deleteUnusedArcs(dataset) {
16095
- var test = getArcPresenceTest2(dataset.layers, dataset.arcs);
16096
- var count1 = dataset.arcs.size();
16097
- var map = dataset.arcs.deleteArcs(test); // condenses arcs
16098
- var count2 = dataset.arcs.size();
16099
- var deleteCount = count1 - count2;
16100
- if (deleteCount > 0) {
16101
- replaceIndexedArcIds(dataset, map);
16102
- }
16103
- return deleteCount;
16104
- }
16105
-
16106
16217
  // Return a function for updating a path (array of arc ids)
16107
16218
  // @map array generated by insertCutPoints()
16108
16219
  // @arcCount number of arcs in divided collection (kludge)
@@ -17458,8 +17569,9 @@
17458
17569
  exporter = GeoJSON.exporters[lyr.geometry_type],
17459
17570
  geom = shape ? exporter(shape, dataset.arcs, opts) : null,
17460
17571
  obj = null;
17572
+
17461
17573
  if (asFeatures) {
17462
- obj = GeoJSON.toFeature(geom, properties ? properties[i] : null);
17574
+ obj = composeFeature(geom, properties ? properties[i] : null, opts);
17463
17575
  if (ids) {
17464
17576
  obj.id = ids[i];
17465
17577
  }
@@ -17483,6 +17595,20 @@
17483
17595
  }, []);
17484
17596
  }
17485
17597
 
17598
+ function composeFeature(geom, properties, opts) {
17599
+ var feat = GeoJSON.toFeature(geom, properties);
17600
+ if (Array.isArray(opts.hoist) && properties) {
17601
+ // don't modify properties of source feature
17602
+ feat.properties = Object.assign({}, properties);
17603
+ opts.hoist.forEach(field => {
17604
+ if (properties.hasOwnProperty(field)) {
17605
+ feat[field] = properties[field];
17606
+ delete feat.properties[field];
17607
+ }
17608
+ });
17609
+ }
17610
+ return feat;
17611
+ }
17486
17612
 
17487
17613
  function getRFC7946Warnings(dataset) {
17488
17614
  var P = getDatasetCRS(dataset);
@@ -21576,6 +21702,14 @@ ${svg}
21576
21702
  }
21577
21703
  return memo.concat(exportFileContent(dataset, opts));
21578
21704
  }, []);
21705
+
21706
+ if (opts.bbox_index) {
21707
+ // If rounding or quantization are applied during export, bounds may
21708
+ // change somewhat... consider adding a bounds property to each layer during
21709
+ // export when appropriate.
21710
+ files.push(createIndexFile(datasets));
21711
+ }
21712
+
21579
21713
  // need unique names for multiple output files
21580
21714
  assignUniqueFileNames(files);
21581
21715
 
@@ -21633,15 +21767,7 @@ ${svg}
21633
21767
  }
21634
21768
 
21635
21769
  validateLayerData(dataset.layers);
21636
-
21637
21770
  files = exporter(dataset, opts).concat(files);
21638
- // If rounding or quantization are applied during export, bounds may
21639
- // change somewhat... consider adding a bounds property to each layer during
21640
- // export when appropriate.
21641
- if (opts.bbox_index) {
21642
- files.push(createIndexFile(dataset));
21643
- }
21644
-
21645
21771
  validateFileNames(files);
21646
21772
  return files;
21647
21773
  }
@@ -21662,13 +21788,16 @@ ${svg}
21662
21788
  // Generate json file with bounding boxes and names of each export layer
21663
21789
  // TODO: consider making this a command, or at least make format settable
21664
21790
  //
21665
- function createIndexFile(dataset) {
21666
- var index = dataset.layers.map(function(lyr) {
21667
- var bounds = getLayerBounds(lyr, dataset.arcs);
21668
- return {
21669
- bbox: bounds.toArray(),
21670
- name: lyr.name
21671
- };
21791
+ function createIndexFile(datasets) {
21792
+ var index = [];
21793
+ datasets.forEach(function(dataset) {
21794
+ dataset.layers.forEach(function(lyr) {
21795
+ var bounds = getLayerBounds(lyr, dataset.arcs);
21796
+ index.push({
21797
+ bbox: bounds.toArray(),
21798
+ name: lyr.name
21799
+ });
21800
+ });
21672
21801
  });
21673
21802
 
21674
21803
  return {
@@ -23391,7 +23520,7 @@ ${svg}
23391
23520
  .option('string-fields', stringFieldsOpt)
23392
23521
  .option('field-types', fieldTypesOpt)
23393
23522
  .option('name', {
23394
- describe: 'Rename the imported layer(s)'
23523
+ describe: 'rename the imported layer(s)'
23395
23524
  })
23396
23525
  .option('geometry-type', {
23397
23526
  // undocumented; GeoJSON import rejects all but one kind of geometry
@@ -23560,6 +23689,10 @@ ${svg}
23560
23689
  describe: '[GeoJSON/JSON] output newline-delimited features or records',
23561
23690
  type: 'flag'
23562
23691
  })
23692
+ .option('hoist', {
23693
+ describe: '[GeoJSON] move properties to the root level of each Feature',
23694
+ type: 'strings'
23695
+ })
23563
23696
  .option('width', {
23564
23697
  describe: '[SVG/TopoJSON] pixel width of output (SVG default is 800)',
23565
23698
  type: 'number'
@@ -33318,7 +33451,7 @@ ${svg}
33318
33451
  if (n != values.length) {
33319
33452
  stop('Mismatch in number of categories and number of values');
33320
33453
  }
33321
- return values;
33454
+ return parseValues(values); // convert numerical strings to numbers
33322
33455
  }
33323
33456
 
33324
33457
  function getIndexes(n) {
@@ -33486,7 +33619,7 @@ ${svg}
33486
33619
  if (opts.index_field) {
33487
33620
  dataField = opts.index_field;
33488
33621
  fieldType = getColumnType(opts.field, records);
33489
- } else if (opts.field) {
33622
+ } else if (opts.field) {
33490
33623
  dataField = opts.field;
33491
33624
  fieldType = getColumnType(opts.field, records);
33492
33625
  }
@@ -33510,7 +33643,6 @@ ${svg}
33510
33643
  stop('Missing a data field to classify');
33511
33644
  }
33512
33645
 
33513
-
33514
33646
  // get the number of classes and the number of values
33515
33647
  //
33516
33648
  // expand categories if value is '*'
@@ -33519,6 +33651,9 @@ ${svg}
33519
33651
  if ((!opts.categories || opts.categories.includes('*')) && dataField) {
33520
33652
  opts.categories = getUniqFieldValues(records, dataField);
33521
33653
  }
33654
+ if (opts.categories && fieldType == 'number') {
33655
+ opts.categories = opts.categories.map(str => +str);
33656
+ }
33522
33657
  }
33523
33658
 
33524
33659
  if (opts.classes) {
@@ -34663,34 +34798,48 @@ ${svg}
34663
34798
  }
34664
34799
 
34665
34800
  function getFeatureEditor(lyr, dataset) {
34666
- var changed = false;
34667
34801
  var api = {};
34668
34802
  // need to copy attribute to avoid circular references if geojson is assigned
34669
34803
  // to a data property.
34670
34804
  var copy = copyLayer(lyr);
34671
34805
  var features = exportLayerAsGeoJSON(copy, dataset, {}, true);
34806
+ var features2 = [];
34672
34807
 
34673
34808
  api.get = function(i) {
34809
+ if (i > 0) features[i-1] = null; // garbage-collect old features
34674
34810
  return features[i];
34675
34811
  };
34676
34812
 
34677
34813
  api.set = function(feat, i) {
34678
- changed = true;
34814
+ var arr;
34815
+
34679
34816
  if (utils.isString(feat)) {
34680
34817
  feat = JSON.parse(feat);
34681
34818
  }
34682
- features[i] = GeoJSON.toFeature(feat); // TODO: validate
34819
+
34820
+ if (!feat) return;
34821
+
34822
+ if (feat.type == 'GeometryCollection') {
34823
+ arr = feat.geometries.map(geom => GeoJSON.toFeature(geom));
34824
+ } else if (feat.type == 'FeatureCollection') {
34825
+ arr = feat.features;
34826
+ } else {
34827
+ feat = GeoJSON.toFeature(feat);
34828
+ }
34829
+
34830
+ if (arr) {
34831
+ features2 = features2.concat(arr);
34832
+ } else {
34833
+ features2.push(feat);
34834
+ }
34683
34835
  };
34684
34836
 
34685
34837
  api.done = function() {
34686
- if (!changed) return; // read-only expression
34687
- // TODO: validate number of features, etc.
34838
+ if (features2.length === 0) return; // read-only expression
34688
34839
  var geojson = {
34689
34840
  type: 'FeatureCollection',
34690
- features: features
34841
+ features: features2
34691
34842
  };
34692
-
34693
- // console.log(JSON.stringify(geojson, null, 2))
34694
34843
  return importGeoJSON(geojson);
34695
34844
  };
34696
34845
  return api;
@@ -40453,9 +40602,8 @@ ${svg}
40453
40602
  cmd.polygonGrid = function(targetLayers, targetDataset, opts) {
40454
40603
  requireProjectedDataset(targetDataset);
40455
40604
  var params = getGridParams(targetLayers, targetDataset, opts);
40456
- var gridDataset = makeGridDataset(params);
40457
-
40458
- gridDataset.info = targetDataset.info; // copy CRS to grid dataset // TODO: improve
40605
+ var gridDataset = makeGridDataset(params); // grid is a new dataset
40606
+ gridDataset.info = copyDatasetInfo(targetDataset.info);
40459
40607
  setOutputLayerName(gridDataset.layers[0], null, 'grid', opts);
40460
40608
  if (opts.debug) gridDataset.layers.push(cmd.pointGrid2(targetLayers, targetDataset, opts));
40461
40609
  return gridDataset;