mapshaper 0.7.45 → 0.7.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.7.45",
3
+ "version": "0.7.46",
4
4
  "description": "A tool for editing geospatial data for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -2638,36 +2638,6 @@
2638
2638
  });
2639
2639
  }
2640
2640
 
2641
- // Call @iter on each member of an array (similar to Array#reduce(iter))
2642
- // iter: function(memo, item, callback)
2643
- // Call @done when all members have been processed or if an error occurs
2644
- // done: function(err, memo)
2645
- // @memo: Initial value
2646
- //
2647
- function reduceAsync(arr, memo, iter, done) {
2648
- var call = typeof setImmediate == 'undefined' ? setTimeout : setImmediate;
2649
- var i=0;
2650
- next(null, memo);
2651
-
2652
- function next(err, memo) {
2653
- // Detach next operation from call stack to prevent overflow
2654
- // Don't use setTimeout(, 0) if setImmediate is available
2655
- // (setTimeout() can introduce a long delay if previous operation was slow,
2656
- // as of Node 0.10.32 -- a bug?)
2657
- if (err) {
2658
- return done(err, null);
2659
- }
2660
- call(function() {
2661
- if (i < arr.length === false) {
2662
- done(null, memo);
2663
- } else {
2664
- iter(memo, arr[i++], next);
2665
- }
2666
- }, 0);
2667
- }
2668
- }
2669
-
2670
-
2671
2641
  // Append elements of @src array to @dest array
2672
2642
  function merge(dest, src) {
2673
2643
  if (!isArray(dest) || !isArray(src)) {
@@ -3522,7 +3492,7 @@
3522
3492
  parseIntlNumber, parseNumber, parseString, pickOne, pluck,
3523
3493
  pluralSuffix, promisify,
3524
3494
  quicksort, quicksortPartition,
3525
- range, reduceAsync, regexEscape, reorderArray: reorderArray$1, repeat, repeatString,
3495
+ range, regexEscape, reorderArray: reorderArray$1, repeat, repeatString,
3526
3496
  replaceArray, rpad, rtrim,
3527
3497
  shuffle, some, sortArrayIndex, sortOn, splitLines, sum,
3528
3498
  toArray, toBuffer, trim, trimQuotes,
@@ -7465,6 +7435,11 @@
7465
7435
  }
7466
7436
 
7467
7437
  async function importFiles(fileData, importOpts) {
7438
+ var commandFiles = extractCommandFiles(fileData);
7439
+ commandFiles.forEach(registerCommandFile);
7440
+ fileData = fileData.filter(function(file) {
7441
+ return commandFiles.indexOf(file) == -1;
7442
+ });
7468
7443
  var groups = groupFilesForImport(fileData, importOpts);
7469
7444
  var optStr = GUI.formatCommandOptions(importOpts);
7470
7445
  fileData = null;
@@ -7493,6 +7468,31 @@
7493
7468
  }
7494
7469
  }
7495
7470
 
7471
+ function extractCommandFiles(files) {
7472
+ return files.filter(function(file) {
7473
+ if (!internal.isPotentialCommandFile(file.name)) return false;
7474
+ file.commandFileContent = decodeCommandFileContent(file.content);
7475
+ return internal.stringLooksLikeCommandFile(file.commandFileContent);
7476
+ });
7477
+ }
7478
+
7479
+ function decodeCommandFileContent(content) {
7480
+ if (utils$1.isString(content)) return content;
7481
+ return internal.decodeString(new Uint8Array(content), 'utf8');
7482
+ }
7483
+
7484
+ function registerCommandFile(file) {
7485
+ var replaced = gui.commandFiles.add(file.name, file.commandFileContent);
7486
+ var command = '-run ' + internal.formatOptionValue(file.name);
7487
+ if (!gui.notify) return;
7488
+ gui.notify({
7489
+ severity: 'info',
7490
+ title: replaced ? 'Command file replaced' : 'Command file loaded',
7491
+ body: 'Run ' + command + ' in the console.',
7492
+ dedupKey: 'command-file:' + file.name
7493
+ });
7494
+ }
7495
+
7496
7496
  // Surface a passive warning if a .shp file came in without its .dbf or .prj
7497
7497
  // sibling. Both files are technically optional, but their absence has very
7498
7498
  // different consequences (no attribute data, no projection metadata) and
@@ -7517,7 +7517,6 @@
7517
7517
  }
7518
7518
 
7519
7519
  async function importDataset(group, importOpts) {
7520
- var dataset;
7521
7520
  var datasets;
7522
7521
  var imported = false;
7523
7522
  if (group.gpkg) {
@@ -7529,12 +7528,7 @@
7529
7528
  if (group.geotiff) {
7530
7529
  await loadGeoTIFFLib();
7531
7530
  }
7532
- if (group.gpkg || group.fgb || group.parquet || group.geotiff || group.png || group.jpeg) {
7533
- dataset = await internal.importContentAsync(group, importOpts);
7534
- } else {
7535
- dataset = internal.importContent(group, importOpts);
7536
- }
7537
- datasets = Array.isArray(dataset) ? dataset : [dataset];
7531
+ datasets = await internal.importDatasetsFromContent(group, importOpts);
7538
7532
  for (var d of datasets) {
7539
7533
  if (datasetIsEmpty(d)) continue;
7540
7534
  if (group.layername) {
@@ -7788,37 +7782,31 @@
7788
7782
  return items.filter(Boolean);
7789
7783
  }
7790
7784
 
7791
- function downloadFiles(paths) {
7785
+ async function downloadFiles(paths) {
7792
7786
  var items = prepFilesForDownload(paths);
7793
- utils$1.reduceAsync(items, [], downloadNextFile, function(err, files) {
7794
- if (err) {
7795
- gui.alert(err);
7796
- } else if (!files.length) {
7797
- gui.clearMode();
7798
- } else {
7799
- receiveFiles(files);
7800
- }
7801
- });
7802
- }
7803
-
7804
- function downloadNextFile(memo, item, next) {
7805
- var err;
7806
- fetch(item.url).then(resp => {
7807
- if (resp.status != 200) {
7808
- // e.g. 404 because a URL listed in the GUI query string does not exist
7809
- throw Error();
7810
- }
7811
- return resp.blob();
7812
- }).then(blob => {
7813
- if (blob) {
7814
- blob.name = item.basename;
7815
- memo.push(blob);
7787
+ var files = [];
7788
+ for (var item of items) {
7789
+ try {
7790
+ var resp = await fetch(item.url);
7791
+ if (resp.status != 200) {
7792
+ // e.g. 404 because a URL listed in the GUI query string does not exist
7793
+ throw Error();
7794
+ }
7795
+ var blob = await resp.blob();
7796
+ if (blob) {
7797
+ blob.name = item.basename;
7798
+ files.push(blob);
7799
+ }
7800
+ } catch (e) {
7801
+ gui.alert("Error&nbsp;loading&nbsp;" + item.name + ". Possible causes include: wrong URL, no network connection, server not configured for cross-domain sharing (CORS).");
7802
+ return;
7816
7803
  }
7817
- }).catch(e => {
7818
- err = "Error&nbsp;loading&nbsp;" + item.name + ". Possible causes include: wrong URL, no network connection, server not configured for cross-domain sharing (CORS).";
7819
- }).finally(() => {
7820
- next(err, memo);
7821
- });
7804
+ }
7805
+ if (!files.length) {
7806
+ gui.clearMode();
7807
+ } else {
7808
+ receiveFiles(files);
7809
+ }
7822
7810
  }
7823
7811
 
7824
7812
  function wait(ms) {
@@ -8182,12 +8170,12 @@
8182
8170
 
8183
8171
  internal.replaceImportFile(function(src, opts) {
8184
8172
  var dataset = find(src);
8185
- // Return a copy with layers duplicated, so changes won't affect original layers
8186
- // This makes an (unsafe) assumption that the dataset arcs won't be changed...
8187
- // need to rethink this.
8188
- return utils$1.defaults({
8189
- layers: dataset.layers.map(internal.copyLayer)
8190
- }, dataset);
8173
+ // Browser-side file references resolve to loaded data. Return a full copy
8174
+ // so a command that imports and edits the match cannot mutate the original
8175
+ // dataset through shared layers, tables, arcs or metadata.
8176
+ var copy = internal.copyDataset(dataset);
8177
+ copy.info = internal.copyDatasetInfo(dataset.info || {});
8178
+ return copy;
8191
8179
  });
8192
8180
  }
8193
8181
 
@@ -9599,10 +9587,11 @@
9599
9587
 
9600
9588
  // get active layer field names and other layer names
9601
9589
  function getCompletionWords() {
9602
- var lyr = model.getActiveLayer().layer;
9603
- var fieldNames = lyr.data ? lyr.data.getFields() : [];
9604
- var lyrNames = findOtherLayerNames(lyr);
9605
- return fieldNames.concat(lyrNames).concat(fieldNames);
9590
+ var active = model.getActiveLayer();
9591
+ var lyr = active && active.layer;
9592
+ var fieldNames = lyr && lyr.data ? lyr.data.getFields() : [];
9593
+ var lyrNames = lyr ? findOtherLayerNames(lyr) : [];
9594
+ return fieldNames.concat(lyrNames, gui.commandFiles.getNames());
9606
9595
  }
9607
9596
 
9608
9597
  function findOtherLayerNames(lyr) {
@@ -9746,6 +9735,7 @@
9746
9735
  // don't add info commands to console history
9747
9736
  // (for one thing, they interfere with target resetting)
9748
9737
  commands = internal.runAndRemoveInfoCommands(commands);
9738
+ prepareRunCommands(commands);
9749
9739
  } catch (e) {
9750
9740
  return done(e, {});
9751
9741
  }
@@ -9758,6 +9748,24 @@
9758
9748
  });
9759
9749
  }
9760
9750
 
9751
+ function prepareRunCommands(commands) {
9752
+ var cache = gui.commandFiles.getInputCache();
9753
+ commands.forEach(function(cmd) {
9754
+ if (cmd.name != 'run') return;
9755
+ cmd.options.input = cache;
9756
+ cmd.options.validate_commands = validateBrowserRunCommands;
9757
+ });
9758
+ }
9759
+
9760
+ function validateBrowserRunCommands(commands) {
9761
+ commands.forEach(function(cmd) {
9762
+ if (['include', 'require', 'external'].includes(cmd.name)) {
9763
+ internal.stop('The ' + cmd.name +
9764
+ ' command cannot be run from a command file in the web console.');
9765
+ }
9766
+ });
9767
+ }
9768
+
9761
9769
  function applyParsedCommands(commands, commandString, done) {
9762
9770
  var active = model.getActiveLayer(),
9763
9771
  prevArcs = active?.dataset.arcs,
@@ -9807,6 +9815,14 @@
9807
9815
  if (sameTable) {
9808
9816
  flags.same_table = true;
9809
9817
  }
9818
+ // A -run command can contain any number of nested commands. The outer
9819
+ // command name does not describe which display caches or controls the
9820
+ // nested commands changed, so refresh conservatively.
9821
+ if (flags.run) {
9822
+ flags.arc_count = true;
9823
+ flags.select = true;
9824
+ flags.same_table = false;
9825
+ }
9810
9826
  if (active && active?.layer == active2?.layer) {
9811
9827
  // this can get set after some commands that don't set a new target
9812
9828
  // (e.g. -dissolve)
@@ -22674,6 +22690,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
22674
22690
  var content = El('div').addClass('ruler-popup-content').appendTo(popup);
22675
22691
  var closeBtn = El('button').addClass('label-style-close ruler-close-btn').appendTo(popup).text('×');
22676
22692
  var wgs84 = internal.parseCrsString('wgs84');
22693
+ var geographicDistance = internal.getGeodesicDistanceFunction(wgs84);
22677
22694
  var _on = false;
22678
22695
  var startPoint = null;
22679
22696
  var pointerPoint = null;
@@ -22870,9 +22887,9 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
22870
22887
 
22871
22888
  measurement = getMeasurement(path);
22872
22889
  drawRulerLines(path, measurement);
22873
- drawRulerPointMarkers(measurement.isLatLng);
22890
+ drawRulerPointMarkers(measurement.geographicMeters !== null);
22874
22891
  if (placed) {
22875
- drawMarker(endMarker, endPoint, measurement.isLatLng);
22892
+ drawMarker(endMarker, endPoint, measurement.geographicMeters !== null);
22876
22893
  } else {
22877
22894
  hideMarker(endMarker);
22878
22895
  }
@@ -22888,20 +22905,33 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
22888
22905
  }
22889
22906
 
22890
22907
  function drawRulerLines(path, measurement) {
22891
- if (measurement.isLatLng) {
22908
+ if (measurement.geographicMeters !== null) {
22892
22909
  drawPath(geodesicPath, getGeodesicPathPixels(path));
22893
22910
  clearSvgPath(directPath);
22894
- } else {
22911
+ } else if (measurement.projectedMeters !== null) {
22895
22912
  drawPath(directPath, path.map(function(point) { return point.display; }));
22896
- drawPath(geodesicPath, measurement.greatCircleMeters ? getGeodesicPathPixels(path) : null);
22913
+ clearSvgPath(geodesicPath);
22914
+ } else {
22915
+ clearSvgPath(directPath);
22916
+ clearSvgPath(geodesicPath);
22897
22917
  }
22898
22918
  }
22899
22919
 
22900
22920
  function showPopup(measurement) {
22901
22921
  content.empty();
22902
- measurement.labels.forEach(function(label) {
22903
- El('div').appendTo(content).text(label);
22904
- });
22922
+ if (measurement.geographicMeters !== null) {
22923
+ El('div').addClass('ruler-geographic-distance').appendTo(content)
22924
+ .text('Geographic distance: ' + measurement.distanceLabel);
22925
+ El('div').addClass('ruler-distance-method').appendTo(content)
22926
+ .text('Shortest surface path (WGS84 ellipsoid)');
22927
+ } else if (measurement.projectedMeters !== null) {
22928
+ El('div').addClass('ruler-projected-distance').appendTo(content)
22929
+ .text('Projected distance: ' + measurement.distanceLabel);
22930
+ El('div').addClass('ruler-distance-method').appendTo(content)
22931
+ .text('Geographic distance not available');
22932
+ } else {
22933
+ El('div').appendTo(content).text('Distance unavailable');
22934
+ }
22905
22935
  if (placed) {
22906
22936
  closeBtn.show();
22907
22937
  } else {
@@ -23077,23 +23107,13 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
23077
23107
  function getMeasurement(path) {
23078
23108
  var isLatLng = internal.isLatLngCRS(path[0].sourceCRS);
23079
23109
  var projectedMeters = isLatLng ? null : getProjectedPathDistanceMeters(path);
23080
- var greatCircleMeters = getGreatCirclePathDistanceMeters(path);
23081
- var unit = getDistanceUnit(projectedMeters || greatCircleMeters || 0);
23082
- var labels = [];
23083
-
23084
- if (projectedMeters !== null) {
23085
- labels.push('Projected: ' + getDistanceDisplay(projectedMeters, unit).label);
23086
- if (greatCircleMeters !== null) {
23087
- labels.push('Great circle: ' + getDistanceDisplay(greatCircleMeters, unit).label);
23088
- }
23089
- } else if (greatCircleMeters !== null) {
23090
- labels.push('Great circle: ' + getDistanceDisplay(greatCircleMeters, unit).label);
23091
- }
23110
+ var geographicMeters = getGeographicPathDistanceMeters(path);
23111
+ var displayedMeters = geographicMeters !== null ? geographicMeters : projectedMeters;
23112
+ var unit = getDistanceUnit(displayedMeters || 0);
23092
23113
  return {
23093
- isLatLng,
23094
23114
  projectedMeters,
23095
- greatCircleMeters,
23096
- labels: labels.length ? labels : ['Distance unavailable']
23115
+ geographicMeters,
23116
+ distanceLabel: displayedMeters === null ? null : getDistanceDisplay(displayedMeters, unit).label
23097
23117
  };
23098
23118
  }
23099
23119
 
@@ -23107,13 +23127,13 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
23107
23127
  return geom.distance2D(a.data[0], a.data[1], b.data[0], b.data[1]) * toMeter;
23108
23128
  }
23109
23129
 
23110
- function getGreatCirclePathDistanceMeters(path) {
23111
- return sumPathDistance(path, getGreatCircleDistanceMeters);
23130
+ function getGeographicPathDistanceMeters(path) {
23131
+ return sumPathDistance(path, getGeographicDistanceMeters);
23112
23132
  }
23113
23133
 
23114
- function getGreatCircleDistanceMeters(a, b) {
23134
+ function getGeographicDistanceMeters(a, b) {
23115
23135
  if (!a.lngLat || !b.lngLat) return null;
23116
- return geom.greatCircleDistance(a.lngLat[0], a.lngLat[1], b.lngLat[0], b.lngLat[1]);
23136
+ return geographicDistance(a.lngLat[0], a.lngLat[1], b.lngLat[0], b.lngLat[1]);
23117
23137
  }
23118
23138
 
23119
23139
  function sumPathDistance(path, calcSegmentDistance) {
@@ -24684,6 +24704,28 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
24684
24704
  };
24685
24705
  }
24686
24706
 
24707
+ function CommandFileStore() {
24708
+ var files = Object.create(null);
24709
+
24710
+ this.add = function(name, content) {
24711
+ var replaced = Object.prototype.hasOwnProperty.call(files, name);
24712
+ files[name] = content;
24713
+ return replaced;
24714
+ };
24715
+
24716
+ this.getNames = function() {
24717
+ return Object.keys(files);
24718
+ };
24719
+
24720
+ this.getInputCache = function() {
24721
+ var cache = Object.create(null);
24722
+ Object.keys(files).forEach(function(name) {
24723
+ cache[name] = files[name];
24724
+ });
24725
+ return cache;
24726
+ };
24727
+ }
24728
+
24687
24729
  // import { ProjectOptions } from './gui-project-control';
24688
24730
 
24689
24731
 
@@ -24702,6 +24744,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
24702
24744
  gui.options = opts;
24703
24745
  gui.container = El(container);
24704
24746
  gui.model = new Model(gui);
24747
+ gui.commandFiles = new CommandFileStore();
24705
24748
  gui.keyboard = new KeyboardEvents(gui);
24706
24749
  gui.buttons = new SidebarButtons(gui);
24707
24750
  gui.display = new DisplayOptions(gui);