mapshaper 0.6.44 → 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.44";
3
+ var VERSION = "0.6.45";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -1882,12 +1882,67 @@
1882
1882
  arcs.updateVertexData(nn, xx2, yy2, zz2);
1883
1883
  }
1884
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
+
1885
1938
  var ArcUtils = /*#__PURE__*/Object.freeze({
1886
1939
  __proto__: null,
1887
1940
  absArcId: absArcId,
1888
1941
  calcArcBounds: calcArcBounds,
1889
1942
  deleteVertex: deleteVertex,
1890
- insertVertex: insertVertex
1943
+ insertVertex: insertVertex,
1944
+ countFilteredVertices: countFilteredVertices,
1945
+ filterVertexData: filterVertexData
1891
1946
  });
1892
1947
 
1893
1948
  var WGS84 = {
@@ -5359,17 +5414,6 @@
5359
5414
  initZData(zz || null);
5360
5415
  };
5361
5416
 
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
5417
 
5374
5418
  this.getCopy = function() {
5375
5419
  var copy = new ArcCollection(new Int32Array(_nn), new Float64Array(_xx),
@@ -5381,55 +5425,28 @@
5381
5425
  return copy;
5382
5426
  };
5383
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
+
5384
5443
  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;
5444
+ if (!_zz || !_zlimit) return this.getPointCount();
5445
+ return countFilteredVertices(_zz, _zlimit);
5392
5446
  }
5393
5447
 
5394
5448
  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
- };
5449
+ return filterVertexData(getVertexData(), _zlimit);
5433
5450
  }
5434
5451
 
5435
5452
  this.getFilteredCopy = function() {
@@ -10828,28 +10845,59 @@
10828
10845
  }
10829
10846
 
10830
10847
  // gui: (optional) gui instance
10831
- //
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}
10832
10852
  async function exportDatasetsToPack(datasets, opts) {
10833
10853
  var obj = {
10834
10854
  version: 1,
10835
10855
  created: (new Date).toISOString(),
10836
- datasets: await Promise.all(datasets.map(exportDataset))
10856
+ datasets: await Promise.all(datasets.map(dataset => exportDataset(dataset, opts)))
10837
10857
  };
10838
- if (opts.compact) {
10839
- await applyCompression(obj);
10840
- }
10841
10858
  return obj;
10842
10859
  }
10843
10860
 
10844
- async function applyCompression(obj, opts) {
10845
- var promises = [];
10846
- obj.datasets.forEach(d => {
10847
- 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);
10848
10880
  });
10849
10881
  await Promise.all(promises);
10882
+ return;
10850
10883
  }
10851
10884
 
10852
- 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) {
10853
10901
  var gzipOpts = Object.assign({level: 1, consume: false}, opts);
10854
10902
  var promises = [gzipAsync(obj.nn, gzipOpts), gzipAsync(obj.xx, gzipOpts), gzipAsync(obj.yy, gzipOpts)];
10855
10903
  if (obj.zz) promises.push(gzipAsync(obj.zz, gzipOpts));
@@ -10860,27 +10908,36 @@
10860
10908
  if (obj.zz) obj.zz = results.shift();
10861
10909
  }
10862
10910
 
10863
- async function exportDataset(dataset, opts) {
10911
+ function importArcData(obj) {
10864
10912
  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))
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
10868
10918
  };
10869
10919
  }
10870
10920
 
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 {
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 = {
10878
10927
  nn: typedArrayToBuffer(data.nn),
10879
10928
  xx: typedArrayToBuffer(data.xx),
10880
10929
  yy: typedArrayToBuffer(data.yy),
10881
10930
  zz: data.zz ? typedArrayToBuffer(data.zz) : null,
10882
- zlimit: arcs.getRetainedInterval()
10931
+ zlimit: data.zlimit || 0
10883
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);
10884
10941
  }
10885
10942
 
10886
10943
  async function exportLayer(lyr) {
@@ -10914,8 +10971,8 @@
10914
10971
  exportPackedDatasets: exportPackedDatasets,
10915
10972
  pack: pack,
10916
10973
  exportDatasetsToPack: exportDatasetsToPack,
10917
- applyCompression: applyCompression,
10918
10974
  exportDataset: exportDataset,
10975
+ compressSnapshotForExport: compressSnapshotForExport,
10919
10976
  exportInfo: exportInfo
10920
10977
  });
10921
10978
 
@@ -15952,6 +16009,67 @@
15952
16009
  formatIntersectingSegment: formatIntersectingSegment
15953
16010
  });
15954
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
+
15955
16073
  // Functions for dividing polygons and polygons at points where arc-segments intersect
15956
16074
 
15957
16075
  // TODO:
@@ -16041,68 +16159,6 @@
16041
16159
  }
16042
16160
 
16043
16161
 
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
16162
  // Return a function for updating a path (array of arc ids)
16107
16163
  // @map array generated by insertCutPoints()
16108
16164
  // @arcCount number of arcs in divided collection (kludge)
@@ -17458,8 +17514,9 @@
17458
17514
  exporter = GeoJSON.exporters[lyr.geometry_type],
17459
17515
  geom = shape ? exporter(shape, dataset.arcs, opts) : null,
17460
17516
  obj = null;
17517
+
17461
17518
  if (asFeatures) {
17462
- obj = GeoJSON.toFeature(geom, properties ? properties[i] : null);
17519
+ obj = composeFeature(geom, properties ? properties[i] : null, opts);
17463
17520
  if (ids) {
17464
17521
  obj.id = ids[i];
17465
17522
  }
@@ -17483,6 +17540,20 @@
17483
17540
  }, []);
17484
17541
  }
17485
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
+ }
17486
17557
 
17487
17558
  function getRFC7946Warnings(dataset) {
17488
17559
  var P = getDatasetCRS(dataset);
@@ -21576,6 +21647,14 @@ ${svg}
21576
21647
  }
21577
21648
  return memo.concat(exportFileContent(dataset, opts));
21578
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
+
21579
21658
  // need unique names for multiple output files
21580
21659
  assignUniqueFileNames(files);
21581
21660
 
@@ -21633,15 +21712,7 @@ ${svg}
21633
21712
  }
21634
21713
 
21635
21714
  validateLayerData(dataset.layers);
21636
-
21637
21715
  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
21716
  validateFileNames(files);
21646
21717
  return files;
21647
21718
  }
@@ -21662,13 +21733,16 @@ ${svg}
21662
21733
  // Generate json file with bounding boxes and names of each export layer
21663
21734
  // TODO: consider making this a command, or at least make format settable
21664
21735
  //
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
- };
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
+ });
21672
21746
  });
21673
21747
 
21674
21748
  return {
@@ -23391,7 +23465,7 @@ ${svg}
23391
23465
  .option('string-fields', stringFieldsOpt)
23392
23466
  .option('field-types', fieldTypesOpt)
23393
23467
  .option('name', {
23394
- describe: 'Rename the imported layer(s)'
23468
+ describe: 'rename the imported layer(s)'
23395
23469
  })
23396
23470
  .option('geometry-type', {
23397
23471
  // undocumented; GeoJSON import rejects all but one kind of geometry
@@ -23560,6 +23634,10 @@ ${svg}
23560
23634
  describe: '[GeoJSON/JSON] output newline-delimited features or records',
23561
23635
  type: 'flag'
23562
23636
  })
23637
+ .option('hoist', {
23638
+ describe: '[GeoJSON] move properties to the root level of each Feature',
23639
+ type: 'strings'
23640
+ })
23563
23641
  .option('width', {
23564
23642
  describe: '[SVG/TopoJSON] pixel width of output (SVG default is 800)',
23565
23643
  type: 'number'
@@ -33510,7 +33588,6 @@ ${svg}
33510
33588
  stop('Missing a data field to classify');
33511
33589
  }
33512
33590
 
33513
-
33514
33591
  // get the number of classes and the number of values
33515
33592
  //
33516
33593
  // expand categories if value is '*'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.6.44",
3
+ "version": "0.6.45",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -68,7 +68,7 @@
68
68
  "browserify": "^17.0.0",
69
69
  "csv-spectrum": "^1.0.0",
70
70
  "eslint": "^8.16.0",
71
- "mocha": "^10.0.0",
71
+ "mocha": "^10.2.0",
72
72
  "rollup": "^2.73.0",
73
73
  "shell-quote": "^1.7.4",
74
74
  "underscore": "^1.13.1"
package/www/index.html CHANGED
@@ -170,8 +170,8 @@ precision=0.001. Click to see all options.</div></div></div></a>
170
170
  <input id="ofile-name" class="text-input" type="text" placeholder="output file name" />
171
171
  </div> -->
172
172
 
173
- <!-- <div class="cancel-btn btn dialog-btn">Cancel</div> -->
174
173
  <div class="save-btn btn dialog-btn">Export</div>
174
+ <!-- <span id="save-to-clipboard" class="inline-checkbox" style="display: inline-block;"><input type="checkbox"/>save to clipboard</span> -->
175
175
  <span id="save-preference" class="inline-checkbox" style="display: none;"><input type="checkbox"/>choose directory</span>
176
176
 
177
177
  </div>
@@ -1442,7 +1442,7 @@
1442
1442
  }).text('restore');
1443
1443
  El('span').addClass('save-menu-btn').appendTo(line).on('click', async function(e) {
1444
1444
  var obj = await idb.get(item.id);
1445
- await internal.applyCompression(obj, {consume: true});
1445
+ await internal.compressSnapshotForExport(obj);
1446
1446
  var buf = internal.pack(obj);
1447
1447
  // choose output filename and directory every time
1448
1448
  // saveBlobToLocalFile('mapshaper_snapshot.msx', new Blob([buf]));
@@ -6372,6 +6372,61 @@
6372
6372
  arcs.updateVertexData(nn, xx2, yy2, zz2);
6373
6373
  }
6374
6374
 
6375
+ function countFilteredVertices(zz, zlimit) {
6376
+ var count = 0;
6377
+ for (var i=0, n = zz.length; i<n; i++) {
6378
+ if (zz[i] >= zlimit) count++;
6379
+ }
6380
+ return count;
6381
+ }
6382
+
6383
+ function filterVertexData(o, zlimit) {
6384
+ if (!o.zz) error('Expected simplification data');
6385
+ var xx = o.xx,
6386
+ yy = o.yy,
6387
+ zz = o.zz,
6388
+ len2 = countFilteredVertices(zz, zlimit),
6389
+ arcCount = o.nn.length,
6390
+ xx2 = new Float64Array(len2),
6391
+ yy2 = new Float64Array(len2),
6392
+ zz2 = new Float64Array(len2),
6393
+ nn2 = new Int32Array(arcCount),
6394
+ i = 0, i2 = 0,
6395
+ n, n2;
6396
+
6397
+ for (var arcId=0; arcId < arcCount; arcId++) {
6398
+ n2 = 0;
6399
+ n = o.nn[arcId];
6400
+ for (var end = i+n; i < end; i++) {
6401
+ if (zz[i] >= zlimit) {
6402
+ xx2[i2] = xx[i];
6403
+ yy2[i2] = yy[i];
6404
+ zz2[i2] = zz[i];
6405
+ i2++;
6406
+ n2++;
6407
+ }
6408
+ }
6409
+ if (n2 == 1) {
6410
+ error("Collapsed arc");
6411
+ // This should not happen (endpoints should be z == Infinity)
6412
+ // Could handle like this, instead of throwing an error:
6413
+ // n2 = 0;
6414
+ // xx2.pop();
6415
+ // yy2.pop();
6416
+ // zz2.pop();
6417
+ } else if (n2 === 0) {
6418
+ // collapsed arc... ignoring
6419
+ }
6420
+ nn2[arcId] = n2;
6421
+ }
6422
+ return {
6423
+ xx: xx2,
6424
+ yy: yy2,
6425
+ zz: zz2,
6426
+ nn: nn2
6427
+ };
6428
+ }
6429
+
6375
6430
  function getShapeHitTest(displayLayer, ext, interactionMode) {
6376
6431
  var geoType = displayLayer.layer.geometry_type;
6377
6432
  var test;
@@ -8023,14 +8078,13 @@
8023
8078
  });
8024
8079
 
8025
8080
  self.show = function(id, ids, lyr, pinned) {
8026
- var table = lyr.data; // table can be null (e.g. if layer has no attribute data)
8027
8081
  var editable = pinned && gui.interaction.getMode() == 'data';
8028
8082
  var maxHeight = parent.node().clientHeight - 36;
8029
8083
  currId = id;
8030
8084
  // stash a function for refreshing the current popup when data changes
8031
8085
  // while the popup is being displayed (e.g. while dragging a label)
8032
8086
  refresh = function() {
8033
- render(content, id, table, editable);
8087
+ render(content, id, lyr, editable);
8034
8088
  };
8035
8089
  refresh();
8036
8090
  if (ids && ids.length > 1) {
@@ -8069,7 +8123,8 @@
8069
8123
  tab.show();
8070
8124
  }
8071
8125
 
8072
- function render(el, recId, table, editable) {
8126
+ function render(el, recId, lyr, editable) {
8127
+ var table = lyr.data; // table can be null (e.g. if layer has no attribute data)
8073
8128
  var rec = table && (editable ? table.getRecordAt(recId) : table.getReadOnlyRecordAt(recId)) || {};
8074
8129
  var tableEl = El('table').addClass('selectable'),
8075
8130
  rows = 0;
@@ -8111,14 +8166,14 @@
8111
8166
  if (editable) {
8112
8167
  // render "add field" button
8113
8168
  var line = El('div').appendTo(el);
8114
- El('span').addClass('save-menu-btn').appendTo(line).on('click', async function(e) {
8169
+ El('span').addClass('add-field-btn').appendTo(line).on('click', async function(e) {
8115
8170
  // show "add field" dialog
8116
- renderAddFieldPopup(recId, table);
8171
+ renderAddFieldPopup(recId, lyr);
8117
8172
  }).text('+ add field');
8118
8173
  }
8119
8174
  }
8120
8175
 
8121
- function renderAddFieldPopup(recId, table) {
8176
+ function renderAddFieldPopup(recId, lyr) {
8122
8177
  var popup = showPopupAlert('', 'Add field');
8123
8178
  var el = popup.container();
8124
8179
  el.addClass('option-menu');
@@ -8132,6 +8187,7 @@
8132
8187
  var val = el.findChild('.field-value');
8133
8188
  var box = el.findChild('.all');
8134
8189
  var btn = el.findChild('.btn').on('click', function() {
8190
+ var table = internal.getLayerDataTable(lyr); // creates new table if missing
8135
8191
  var all = box.node().checked;
8136
8192
  var nameStr = name.node().value.trim();
8137
8193
  if (!nameStr) return;
package/www/mapshaper.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
 
3
- var VERSION = "0.6.44";
3
+ var VERSION = "0.6.45";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -1882,12 +1882,67 @@
1882
1882
  arcs.updateVertexData(nn, xx2, yy2, zz2);
1883
1883
  }
1884
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
+
1885
1938
  var ArcUtils = /*#__PURE__*/Object.freeze({
1886
1939
  __proto__: null,
1887
1940
  absArcId: absArcId,
1888
1941
  calcArcBounds: calcArcBounds,
1889
1942
  deleteVertex: deleteVertex,
1890
- insertVertex: insertVertex
1943
+ insertVertex: insertVertex,
1944
+ countFilteredVertices: countFilteredVertices,
1945
+ filterVertexData: filterVertexData
1891
1946
  });
1892
1947
 
1893
1948
  var WGS84 = {
@@ -5359,17 +5414,6 @@
5359
5414
  initZData(zz || null);
5360
5415
  };
5361
5416
 
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
5417
 
5374
5418
  this.getCopy = function() {
5375
5419
  var copy = new ArcCollection(new Int32Array(_nn), new Float64Array(_xx),
@@ -5381,55 +5425,28 @@
5381
5425
  return copy;
5382
5426
  };
5383
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
+
5384
5443
  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;
5444
+ if (!_zz || !_zlimit) return this.getPointCount();
5445
+ return countFilteredVertices(_zz, _zlimit);
5392
5446
  }
5393
5447
 
5394
5448
  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
- };
5449
+ return filterVertexData(getVertexData(), _zlimit);
5433
5450
  }
5434
5451
 
5435
5452
  this.getFilteredCopy = function() {
@@ -10828,28 +10845,59 @@
10828
10845
  }
10829
10846
 
10830
10847
  // gui: (optional) gui instance
10831
- //
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}
10832
10852
  async function exportDatasetsToPack(datasets, opts) {
10833
10853
  var obj = {
10834
10854
  version: 1,
10835
10855
  created: (new Date).toISOString(),
10836
- datasets: await Promise.all(datasets.map(exportDataset))
10856
+ datasets: await Promise.all(datasets.map(dataset => exportDataset(dataset, opts)))
10837
10857
  };
10838
- if (opts.compact) {
10839
- await applyCompression(obj);
10840
- }
10841
10858
  return obj;
10842
10859
  }
10843
10860
 
10844
- async function applyCompression(obj, opts) {
10845
- var promises = [];
10846
- obj.datasets.forEach(d => {
10847
- 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);
10848
10880
  });
10849
10881
  await Promise.all(promises);
10882
+ return;
10850
10883
  }
10851
10884
 
10852
- 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) {
10853
10901
  var gzipOpts = Object.assign({level: 1, consume: false}, opts);
10854
10902
  var promises = [gzipAsync(obj.nn, gzipOpts), gzipAsync(obj.xx, gzipOpts), gzipAsync(obj.yy, gzipOpts)];
10855
10903
  if (obj.zz) promises.push(gzipAsync(obj.zz, gzipOpts));
@@ -10860,27 +10908,36 @@
10860
10908
  if (obj.zz) obj.zz = results.shift();
10861
10909
  }
10862
10910
 
10863
- async function exportDataset(dataset, opts) {
10911
+ function importArcData(obj) {
10864
10912
  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))
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
10868
10918
  };
10869
10919
  }
10870
10920
 
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 {
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 = {
10878
10927
  nn: typedArrayToBuffer(data.nn),
10879
10928
  xx: typedArrayToBuffer(data.xx),
10880
10929
  yy: typedArrayToBuffer(data.yy),
10881
10930
  zz: data.zz ? typedArrayToBuffer(data.zz) : null,
10882
- zlimit: arcs.getRetainedInterval()
10931
+ zlimit: data.zlimit || 0
10883
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);
10884
10941
  }
10885
10942
 
10886
10943
  async function exportLayer(lyr) {
@@ -10914,8 +10971,8 @@
10914
10971
  exportPackedDatasets: exportPackedDatasets,
10915
10972
  pack: pack,
10916
10973
  exportDatasetsToPack: exportDatasetsToPack,
10917
- applyCompression: applyCompression,
10918
10974
  exportDataset: exportDataset,
10975
+ compressSnapshotForExport: compressSnapshotForExport,
10919
10976
  exportInfo: exportInfo
10920
10977
  });
10921
10978
 
@@ -15952,6 +16009,67 @@
15952
16009
  formatIntersectingSegment: formatIntersectingSegment
15953
16010
  });
15954
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
+
15955
16073
  // Functions for dividing polygons and polygons at points where arc-segments intersect
15956
16074
 
15957
16075
  // TODO:
@@ -16041,68 +16159,6 @@
16041
16159
  }
16042
16160
 
16043
16161
 
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
16162
  // Return a function for updating a path (array of arc ids)
16107
16163
  // @map array generated by insertCutPoints()
16108
16164
  // @arcCount number of arcs in divided collection (kludge)
@@ -17458,8 +17514,9 @@
17458
17514
  exporter = GeoJSON.exporters[lyr.geometry_type],
17459
17515
  geom = shape ? exporter(shape, dataset.arcs, opts) : null,
17460
17516
  obj = null;
17517
+
17461
17518
  if (asFeatures) {
17462
- obj = GeoJSON.toFeature(geom, properties ? properties[i] : null);
17519
+ obj = composeFeature(geom, properties ? properties[i] : null, opts);
17463
17520
  if (ids) {
17464
17521
  obj.id = ids[i];
17465
17522
  }
@@ -17483,6 +17540,20 @@
17483
17540
  }, []);
17484
17541
  }
17485
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
+ }
17486
17557
 
17487
17558
  function getRFC7946Warnings(dataset) {
17488
17559
  var P = getDatasetCRS(dataset);
@@ -21576,6 +21647,14 @@ ${svg}
21576
21647
  }
21577
21648
  return memo.concat(exportFileContent(dataset, opts));
21578
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
+
21579
21658
  // need unique names for multiple output files
21580
21659
  assignUniqueFileNames(files);
21581
21660
 
@@ -21633,15 +21712,7 @@ ${svg}
21633
21712
  }
21634
21713
 
21635
21714
  validateLayerData(dataset.layers);
21636
-
21637
21715
  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
21716
  validateFileNames(files);
21646
21717
  return files;
21647
21718
  }
@@ -21662,13 +21733,16 @@ ${svg}
21662
21733
  // Generate json file with bounding boxes and names of each export layer
21663
21734
  // TODO: consider making this a command, or at least make format settable
21664
21735
  //
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
- };
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
+ });
21672
21746
  });
21673
21747
 
21674
21748
  return {
@@ -23391,7 +23465,7 @@ ${svg}
23391
23465
  .option('string-fields', stringFieldsOpt)
23392
23466
  .option('field-types', fieldTypesOpt)
23393
23467
  .option('name', {
23394
- describe: 'Rename the imported layer(s)'
23468
+ describe: 'rename the imported layer(s)'
23395
23469
  })
23396
23470
  .option('geometry-type', {
23397
23471
  // undocumented; GeoJSON import rejects all but one kind of geometry
@@ -23560,6 +23634,10 @@ ${svg}
23560
23634
  describe: '[GeoJSON/JSON] output newline-delimited features or records',
23561
23635
  type: 'flag'
23562
23636
  })
23637
+ .option('hoist', {
23638
+ describe: '[GeoJSON] move properties to the root level of each Feature',
23639
+ type: 'strings'
23640
+ })
23563
23641
  .option('width', {
23564
23642
  describe: '[SVG/TopoJSON] pixel width of output (SVG default is 800)',
23565
23643
  type: 'number'
@@ -33510,7 +33588,6 @@ ${svg}
33510
33588
  stop('Missing a data field to classify');
33511
33589
  }
33512
33590
 
33513
-
33514
33591
  // get the number of classes and the number of values
33515
33592
  //
33516
33593
  // expand categories if value is '*'
package/www/page.css CHANGED
@@ -58,6 +58,7 @@ body {
58
58
  .colored-text,
59
59
  .save-menu-link,
60
60
  .save-menu-btn,
61
+ .add-field-btn,
61
62
  .nav-menu-item {
62
63
  color: #10699b;
63
64
  }
@@ -1314,6 +1315,7 @@ div.basemap-style-btn.active img {
1314
1315
 
1315
1316
  .save-menu {
1316
1317
  text-align: right;
1318
+ padding-bottom: 5px;
1317
1319
  }
1318
1320
 
1319
1321
  .save-menu-entry {
@@ -1324,27 +1326,29 @@ div.basemap-style-btn.active img {
1324
1326
  background: white;
1325
1327
  }
1326
1328
 
1327
- /*.save-menu-btn {
1329
+ .save-menu-btn {
1328
1330
  display: inline-block;
1329
1331
  border-radius: 4px;
1330
1332
  border: 1px solid #aaa;
1331
1333
  font-size: 12px;
1332
1334
  margin-left: 2px;
1333
1335
  padding: 1px 2px 3px 2px;
1334
- }*/
1336
+ }
1335
1337
 
1336
- .save-menu-btn {
1338
+ .add-field-btn {
1337
1339
  display: inline-block;
1338
1340
  font-size: 12px;
1339
1341
  margin-top: 2px;
1340
1342
  }
1341
1343
 
1342
- .save-menu-btn:hover {
1344
+ .save-menu-btn:hover,
1345
+ .add-field-btn:hover {
1343
1346
  color: black;
1344
1347
  }
1345
1348
 
1346
1349
  .save-menu-link,
1347
- .save-menu-btn {
1350
+ .save-menu-btn,
1351
+ .add-field-btn {
1348
1352
  cursor: pointer;
1349
1353
  }
1350
1354