mapshaper 0.6.45 → 0.6.47

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.45";
3
+ var VERSION = "0.6.47";
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;
@@ -1939,6 +1956,7 @@
1939
1956
  __proto__: null,
1940
1957
  absArcId: absArcId,
1941
1958
  calcArcBounds: calcArcBounds,
1959
+ findArcIdFromVertexId: findArcIdFromVertexId,
1942
1960
  deleteVertex: deleteVertex,
1943
1961
  insertVertex: insertVertex,
1944
1962
  countFilteredVertices: countFilteredVertices,
@@ -3405,7 +3423,7 @@
3405
3423
  var item;
3406
3424
  for (var i=0; i<arr.length; i++) {
3407
3425
  item = arr[i];
3408
- if (item instanceof Array) {
3426
+ if (Array.isArray(item)) {
3409
3427
  forEachArcId(item, cb);
3410
3428
  } else if (utils.isInteger(item)) {
3411
3429
  var val = cb(item);
@@ -6953,6 +6971,15 @@
6953
6971
  utils.defaults(destInfo, srcInfo);
6954
6972
  }
6955
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
+ }
6956
6983
 
6957
6984
  function splitApartLayers(dataset, layers) {
6958
6985
  var datasets = [];
@@ -7119,6 +7146,7 @@
7119
7146
  __proto__: null,
7120
7147
  splitDataset: splitDataset,
7121
7148
  mergeDatasetInfo: mergeDatasetInfo,
7149
+ copyDatasetInfo: copyDatasetInfo,
7122
7150
  splitApartLayers: splitApartLayers,
7123
7151
  copyDataset: copyDataset,
7124
7152
  copyDatasetForExport: copyDatasetForExport,
@@ -14424,16 +14452,17 @@
14424
14452
  }, ctx);
14425
14453
  }
14426
14454
 
14427
- function getBaseContext() {
14455
+ function getBaseContext(ctx) {
14456
+ ctx = ctx || {};
14428
14457
  // Mask global properties (is this effective/worth doing?)
14429
- var obj = {globalThis: void 0}; // some globals are not iterable
14458
+ ctx.globalThis = void 0; // some globals are not iterable
14430
14459
  (function() {
14431
14460
  for (var key in this) {
14432
- obj[key] = void 0;
14461
+ ctx[key] = void 0;
14433
14462
  }
14434
14463
  }());
14435
- obj.console = console;
14436
- return obj;
14464
+ ctx.console = console;
14465
+ return ctx;
14437
14466
  }
14438
14467
 
14439
14468
  var Expressions = /*#__PURE__*/Object.freeze({
@@ -15481,6 +15510,17 @@
15481
15510
 
15482
15511
  // Keep track of whether positive or negative integer ids are 'used' or not.
15483
15512
 
15513
+
15514
+ function SimpleIdTestIndex(n) {
15515
+ var index = new Uint8Array(n);
15516
+ this.setId = function(id) {
15517
+ index[id] = 1;
15518
+ };
15519
+ this.hasId = function(id) {
15520
+ return index[id] === 1;
15521
+ };
15522
+ }
15523
+
15484
15524
  function IdTestIndex(n) {
15485
15525
  var index = new Uint8Array(n);
15486
15526
  var setList = [];
@@ -15743,15 +15783,30 @@
15743
15783
  getHoleDivider: getHoleDivider
15744
15784
  });
15745
15785
 
15746
- // Convert an array of intersections into an ArcCollection (for display)
15747
- //
15748
-
15749
15786
  function getIntersectionPoints(intersections) {
15750
15787
  return intersections.map(function(obj) {
15751
15788
  return [obj.x, obj.y];
15752
15789
  });
15753
15790
  }
15754
15791
 
15792
+ function getIntersectionLayer(intersections, lyr, arcs) {
15793
+ // return {geometry_type: 'point', shapes: [getIntersectionPoints(XX)]};
15794
+ var ii = arcs.getVertexData().ii;
15795
+ var index = new SimpleIdTestIndex(arcs.size());
15796
+ forEachArcId(lyr.shapes, arcId => {
15797
+ index.setId(absArcId(arcId));
15798
+ });
15799
+ var points = [];
15800
+ intersections.forEach(obj => {
15801
+ var arc1 = findArcIdFromVertexId(obj.a[0], ii);
15802
+ var arc2 = findArcIdFromVertexId(obj.b[0], ii);
15803
+ if (index.hasId(arc1) && index.hasId(arc2)) {
15804
+ points.push([obj.x, obj.y]);
15805
+ }
15806
+ });
15807
+ return {geometry_type: 'point', shapes: [points]};
15808
+ }
15809
+
15755
15810
  // Identify intersecting segments in an ArcCollection
15756
15811
  //
15757
15812
  // To find all intersections:
@@ -15999,6 +16054,7 @@
15999
16054
  var SegmentIntersection = /*#__PURE__*/Object.freeze({
16000
16055
  __proto__: null,
16001
16056
  getIntersectionPoints: getIntersectionPoints,
16057
+ getIntersectionLayer: getIntersectionLayer,
16002
16058
  findSegmentIntersections: findSegmentIntersections,
16003
16059
  sortIntersections: sortIntersections,
16004
16060
  dedupIntersections: dedupIntersections,
@@ -21304,7 +21360,7 @@ ${svg}
21304
21360
 
21305
21361
  // export layers as TopoJSON named objects
21306
21362
  topology.objects = dataset.layers.reduce(function(objects, lyr, i) {
21307
- var name = lyr.name || "layer" + (i + 1);
21363
+ var name = lyr.name || 'layer' + (i + 1);
21308
21364
  objects[name] = TopoJSON.exportLayer(lyr, dataset.arcs, opts);
21309
21365
  return objects;
21310
21366
  }, {});
@@ -21667,7 +21723,7 @@ ${svg}
21667
21723
  return files;
21668
21724
  }
21669
21725
 
21670
- // Return an array of objects with "filename" and "content" members.
21726
+ // Return an array of objects with 'filename' and 'content' members.
21671
21727
  //
21672
21728
  function exportFileContent(dataset, opts) {
21673
21729
  var outFmt = opts.format = getOutputFormat(dataset, opts),
@@ -21675,9 +21731,9 @@ ${svg}
21675
21731
  files = [];
21676
21732
 
21677
21733
  if (!outFmt) {
21678
- error("Missing output format");
21734
+ error('Missing output format');
21679
21735
  } else if (!exporter) {
21680
- error("Unknown output format:", outFmt);
21736
+ error('Unknown output format:', outFmt);
21681
21737
  }
21682
21738
 
21683
21739
  // shallow-copy dataset and layers, so layers can be renamed for export
@@ -21747,7 +21803,7 @@ ${svg}
21747
21803
 
21748
21804
  return {
21749
21805
  content: JSON.stringify(index),
21750
- filename: "bbox-index.json"
21806
+ filename: 'bbox-index.json'
21751
21807
  };
21752
21808
  }
21753
21809
 
@@ -21759,14 +21815,14 @@ ${svg}
21759
21815
  if (lyr.shapes && utils.some(lyr.shapes, function(o) {
21760
21816
  return !!o;
21761
21817
  })) {
21762
- error("A layer contains shape records and a null geometry type");
21818
+ error('A layer contains shape records and a null geometry type');
21763
21819
  }
21764
21820
  } else {
21765
21821
  if (!utils.contains(['polygon', 'polyline', 'point'], lyr.geometry_type)) {
21766
- error ("A layer has an invalid geometry type:", lyr.geometry_type);
21822
+ error ('A layer has an invalid geometry type:', lyr.geometry_type);
21767
21823
  }
21768
21824
  if (!lyr.shapes) {
21769
- error ("A layer is missing shape data");
21825
+ error ('A layer is missing shape data');
21770
21826
  }
21771
21827
  }
21772
21828
  });
@@ -21776,15 +21832,15 @@ ${svg}
21776
21832
  var index = {};
21777
21833
  files.forEach(function(file, i) {
21778
21834
  var filename = file.filename;
21779
- if (!filename) error("Missing a filename for file" + i);
21780
- if (filename in index) error("Duplicate filename", filename);
21835
+ if (!filename) error('Missing a filename for file' + i);
21836
+ if (filename in index) error('Duplicate filename', filename);
21781
21837
  index[filename] = true;
21782
21838
  });
21783
21839
  }
21784
21840
 
21785
21841
  function assignUniqueLayerNames(layers) {
21786
21842
  var names = layers.map(function(lyr) {
21787
- return lyr.name || "layer";
21843
+ return lyr.name || 'layer';
21788
21844
  });
21789
21845
  var uniqueNames = utils.uniqifyNames(names);
21790
21846
  layers.forEach(function(lyr, i) {
@@ -22619,12 +22675,9 @@ ${svg}
22619
22675
  });
22620
22676
 
22621
22677
  function validateInputOpts(cmd) {
22622
- var o = cmd.options,
22623
- _ = cmd._;
22678
+ var o = cmd.options;
22679
+ cmd._;
22624
22680
 
22625
- if (_.length > 0 && !o.files) {
22626
- o.files = _;
22627
- }
22628
22681
  if (o.files) {
22629
22682
  o.files = cli.expandInputFiles(o.files);
22630
22683
  if (o.files[0] == '-' || o.files[0] == '/dev/stdin') {
@@ -22633,8 +22686,8 @@ ${svg}
22633
22686
  }
22634
22687
  }
22635
22688
 
22636
- if ("precision" in o && o.precision > 0 === false) {
22637
- error("precision= option should be a positive number");
22689
+ if ('precision' in o && o.precision > 0 === false) {
22690
+ error('precision= option should be a positive number');
22638
22691
  }
22639
22692
 
22640
22693
  if (o.encoding) {
@@ -22643,36 +22696,15 @@ ${svg}
22643
22696
  }
22644
22697
 
22645
22698
  function validateSimplifyOpts(cmd) {
22646
- var o = cmd.options,
22647
- arg = cmd._[0];
22648
-
22649
- if (arg) {
22650
- if (/^[0-9.]+%?$/.test(arg)) {
22651
- o.percentage = utils.parsePercent(arg);
22652
- } else {
22653
- error("Unparsable option:", arg);
22654
- }
22655
- }
22656
-
22699
+ var o = cmd.options;
22657
22700
  if (!o.interval && !o.percentage && !o.resolution) {
22658
- error("Command requires an interval, percentage or resolution parameter");
22701
+ error('Command requires an interval, percentage or resolution parameter');
22659
22702
  }
22660
22703
  }
22661
22704
 
22662
22705
  function validateProjOpts(cmd) {
22663
- var _ = cmd._;
22664
-
22665
- if (_.length > 0 && !cmd.options.crs) {
22666
- cmd.options.crs = _.join(' ');
22667
- _ = [];
22668
- }
22669
-
22670
- if (_.length > 0) {
22671
- error("Received one or more unexpected parameters: " + _.join(', '));
22672
- }
22673
-
22674
22706
  if (!(cmd.options.crs || cmd.options.match || cmd.options.init)) {
22675
- stop("Missing projection data");
22707
+ stop('Missing projection data');
22676
22708
  }
22677
22709
  }
22678
22710
 
@@ -22687,25 +22719,24 @@ ${svg}
22687
22719
 
22688
22720
  function validateExpressionOpt(cmd) {
22689
22721
  if (!cmd.options.expression) {
22690
- error("Command requires a JavaScript expression");
22722
+ error('Command requires a JavaScript expression');
22691
22723
  }
22692
22724
  }
22693
22725
 
22694
22726
  function validateOutputOpts(cmd) {
22695
- var _ = cmd._,
22696
- o = cmd.options,
22697
- arg = _[0] || "",
22727
+ var o = cmd.options,
22728
+ arg = o._ || '',
22698
22729
  pathInfo = parseLocalPath(arg);
22699
22730
 
22700
- if (_.length > 1) {
22701
- error("Command takes one file or directory argument");
22702
- }
22731
+ // if (!arg) {
22732
+ // error('Command requires an output file or directory.');
22733
+ // }
22703
22734
 
22704
22735
  if (arg == '-' || arg == '/dev/stdout') {
22705
22736
  o.stdout = true;
22706
22737
  } else if (arg && !pathInfo.extension) {
22707
22738
  if (!cli.isDirectory(arg)) {
22708
- error("Unknown output option:", arg);
22739
+ error('Unknown output option:', arg);
22709
22740
  }
22710
22741
  o.directory = arg;
22711
22742
  } else if (arg) {
@@ -22733,7 +22764,7 @@ ${svg}
22733
22764
  }
22734
22765
 
22735
22766
  if (filenameIsUnsupportedOutputType(o.file)) {
22736
- error("Output file looks like an unsupported file type:", o.file);
22767
+ error('Output file looks like an unsupported file type:', o.file);
22737
22768
  }
22738
22769
  }
22739
22770
 
@@ -22747,15 +22778,15 @@ ${svg}
22747
22778
  o.delimiter = o.delimiter || '\t';
22748
22779
  }
22749
22780
  if (!isSupportedOutputFormat(o.format)) {
22750
- error("Unsupported output format:", o.format);
22781
+ error('Unsupported output format:', o.format);
22751
22782
  }
22752
22783
  }
22753
22784
 
22754
22785
  if (o.delimiter) {
22755
- // convert "\t" '\t' \t to tab
22786
+ // convert '\t' '\t' \t to tab
22756
22787
  o.delimiter = o.delimiter.replace(/^["']?\\t["']?$/, '\t');
22757
22788
  if (!isSupportedDelimiter(o.delimiter)) {
22758
- error("Unsupported delimiter:", o.delimiter);
22789
+ error('Unsupported delimiter:', o.delimiter);
22759
22790
  }
22760
22791
  }
22761
22792
 
@@ -22768,12 +22799,12 @@ ${svg}
22768
22799
  }
22769
22800
 
22770
22801
  // topojson-specific
22771
- if ("quantization" in o && o.quantization > 0 === false) {
22772
- error("quantization= option should be a nonnegative integer");
22802
+ if ('quantization' in o && o.quantization > 0 === false) {
22803
+ error('quantization= option should be a nonnegative integer');
22773
22804
  }
22774
22805
 
22775
- if ("topojson_precision" in o && o.topojson_precision > 0 === false) {
22776
- error("topojson-precision= option should be a positive number");
22806
+ if ('topojson_precision' in o && o.topojson_precision > 0 === false) {
22807
+ error('topojson-precision= option should be a positive number');
22777
22808
  }
22778
22809
  }
22779
22810
 
@@ -22959,14 +22990,18 @@ ${svg}
22959
22990
  if (!cmdName) {
22960
22991
  stop("Invalid command:", argv[0]);
22961
22992
  }
22962
- cmdDef = findCommandDefn(cmdName, commandDefs);
22993
+ cmdDef = findCommandDefn(cmdName, commandDefs) || null;
22963
22994
  if (!cmdDef) {
22964
- // In order to support adding commands at runtime, unknown commands
22965
- // are parsed without options (tokens get stored for later parsing)
22966
- // stop("Unknown command:", cmdName);
22967
- cmdDef = {name: cmdName, options: [], multi_arg: true};
22995
+ cmd = parseUnknownCommandOptions(argv, cmdName);
22996
+ } else {
22997
+ cmd = parseCommandOptions(argv, cmdDef);
22968
22998
  }
22969
- cmd = {
22999
+ commands.push(cmd);
23000
+ }
23001
+ return commands;
23002
+
23003
+ function parseCommandOptions(argv, cmdDef) {
23004
+ var cmd = {
22970
23005
  name: cmdDef.name,
22971
23006
  options: {},
22972
23007
  _: []
@@ -22977,25 +23012,32 @@ ${svg}
22977
23012
  }
22978
23013
 
22979
23014
  try {
22980
- if (cmd._.length > 0 && cmdDef.no_arg) {
22981
- error("Received one or more unexpected parameters:", cmd._.join(' '));
22982
- }
22983
- if (cmd._.length > 1 && !cmdDef.multi_arg) {
22984
- error("Command expects a single value. Received:", cmd._.join(' '));
22985
- }
22986
- if (cmdDef.default && cmd._.length == 1) {
22987
- // TODO: support multiple-token values, like -i filenames
23015
+ if (cmd._.length > 0) {
22988
23016
  readDefaultOptionValue(cmd, cmdDef);
22989
23017
  }
22990
23018
  if (cmdDef.validate) {
22991
23019
  cmdDef.validate(cmd);
22992
23020
  }
23021
+ delete cmd.options._; // kludge to remove -o placeholder option
22993
23022
  } catch(e) {
22994
23023
  stop("[" + cmdName + "] " + e.message);
22995
23024
  }
22996
- commands.push(cmd);
23025
+ return cmd;
23026
+ }
23027
+
23028
+ function parseUnknownCommandOptions(argv, cmdName) {
23029
+ // In order to support adding commands at runtime, unknown commands
23030
+ // are parsed without options (tokens get stored for later parsing)
23031
+ var cmd = {
23032
+ name: cmdName,
23033
+ options: {},
23034
+ _: []
23035
+ };
23036
+ while (argv.length > 0 && !tokenLooksLikeCommand(argv[0])) {
23037
+ cmd._.push(argv.shift());
23038
+ }
23039
+ return cmd;
22997
23040
  }
22998
- return commands;
22999
23041
 
23000
23042
  function tokenLooksLikeCommand(s) {
23001
23043
  if (invalidCommandRxp.test(s)) {
@@ -23025,11 +23067,7 @@ ${svg}
23025
23067
  }
23026
23068
 
23027
23069
  if (!optDef) {
23028
- // REMOVING quote trimming -- it prevents the use of quoted commands in -run (for example)
23029
- // token is not a defined option; add it to _ array for later processing
23030
- // Stripping surrounding quotes here, although this may not be necessary since
23031
- // (some, most, all?) shells seem to remove quotes.
23032
- // cmd._.push(utils.trimQuotes(token));
23070
+ // token is not a known option -- add to array of unnamed options
23033
23071
  cmd._.push(token);
23034
23072
  return;
23035
23073
  }
@@ -23058,9 +23096,36 @@ ${svg}
23058
23096
  return parseOptionValue(argv.shift(), optDef); // remove token from argv
23059
23097
  }
23060
23098
 
23099
+ // convert strings in cmd._ array to command parameers in cmd.options object
23100
+ //
23061
23101
  function readDefaultOptionValue(cmd, cmdDef) {
23062
- var optDef = findOptionDefn(cmdDef.default, cmdDef);
23063
- cmd.options[cmdDef.default] = readOptionValue(cmd._, optDef);
23102
+ var optDef = findDefaultOptionDefn(cmdDef);
23103
+ var argv = cmd._;
23104
+ var value;
23105
+ if (cmdDef)
23106
+ if (!optDef) {
23107
+ // no option has been specified as the default option
23108
+ error('Received one or more unexpected parameters:', argv.join(' '));
23109
+ }
23110
+ // DEFAULT may be true (simple case of one argument) or an object
23111
+ var argDef = optDef.DEFAULT === true ? {} : optDef.DEFAULT;
23112
+ argDef.type = argDef.type || optDef.type || 'string';
23113
+ argDef.name = optDef.name; // used in parse error message
23114
+
23115
+ if (argv.length > 1 && !argDef.multi_arg) {
23116
+ error((argDef.multi_error_msg || 'Command expects a single value.'),
23117
+ 'Received:', argv.join(' '));
23118
+ }
23119
+
23120
+ argv = argv.map(arg => parseOptionValue(arg, argDef));
23121
+ if (!argDef.multi_arg) {
23122
+ value = argv[0];
23123
+ } else if (utils.isString(argDef.join)) {
23124
+ value = argv.join(argDef.join);
23125
+ } else {
23126
+ value = argv;
23127
+ }
23128
+ cmd.options[optDef.name] = value;
23064
23129
  }
23065
23130
 
23066
23131
  function parseOptionValue(token, optDef) {
@@ -23138,15 +23203,15 @@ ${svg}
23138
23203
 
23139
23204
  function getSingleCommandLines(cmd) {
23140
23205
  var lines = [];
23141
- // command name
23142
- lines.push('COMMAND', getCommandLine(cmd));
23206
+ var options = [];
23207
+ cmd.options.forEach(function(opt) {
23208
+ options = options.concat(getOptionLines(opt));
23209
+ });
23143
23210
 
23144
- // options
23145
- if (cmd.options.length > 0) {
23211
+ lines.push('COMMAND', getCommandLine(cmd));
23212
+ if (options.length > 0) {
23146
23213
  lines.push('', 'OPTIONS');
23147
- cmd.options.forEach(function(opt) {
23148
- lines = lines.concat(getOptionLines(opt, cmd));
23149
- });
23214
+ lines = lines.concat(options);
23150
23215
  }
23151
23216
 
23152
23217
  // examples
@@ -23168,7 +23233,7 @@ ${svg}
23168
23233
  var label;
23169
23234
  if (!description) ; else if (opt.label) {
23170
23235
  lines.push([opt.label, description]);
23171
- } else if (opt.name == cmd.default) {
23236
+ } else if (opt.DEFAULT) {
23172
23237
  label = opt.name + '=';
23173
23238
  lines.push(['<' + opt.name + '>', 'shortcut for ' + label]);
23174
23239
  lines.push([label, description]);
@@ -23250,6 +23315,12 @@ ${svg}
23250
23315
  return o.name === name || o.alias === name || o.old_alias === name;
23251
23316
  });
23252
23317
  }
23318
+
23319
+ function findDefaultOptionDefn(cmdDef) {
23320
+ return utils.find(cmdDef.options, function(o) {
23321
+ return !!o.DEFAULT;
23322
+ });
23323
+ }
23253
23324
  }
23254
23325
 
23255
23326
  function CommandOptions(name) {
@@ -23293,17 +23364,10 @@ ${svg}
23293
23364
  return this;
23294
23365
  };
23295
23366
 
23296
- this.flag = function(name) {
23297
- _command[name] = true;
23298
- return this;
23299
- };
23300
-
23301
23367
  this.option = function(name, opts) {
23302
23368
  opts = utils.extend({}, opts); // accept just a name -- some options don't need properties
23303
23369
  if (!utils.isString(name) || !name) error("Missing option name");
23304
23370
  if (!utils.isObject(opts)) error("Invalid option definition:", opts);
23305
- // default option -- assign unnamed argument to this option
23306
- if (opts.DEFAULT) _command.default = name;
23307
23371
  opts.name = name;
23308
23372
  _command.options.push(opts);
23309
23373
  return this;
@@ -23425,9 +23489,11 @@ ${svg}
23425
23489
  parser.command('i')
23426
23490
  .describe('input one or more files')
23427
23491
  .validate(validateInputOpts)
23428
- .flag('multi_arg')
23429
23492
  .option('files', {
23430
- DEFAULT: true,
23493
+ DEFAULT: {
23494
+ multi_arg: true,
23495
+ type: 'string'
23496
+ },
23431
23497
  type: 'strings',
23432
23498
  describe: 'one or more files to import, or - to use stdin'
23433
23499
  })
@@ -23518,7 +23584,10 @@ ${svg}
23518
23584
  .validate(validateOutputOpts)
23519
23585
  .option('_', {
23520
23586
  label: '<file|directory>',
23521
- describe: '(optional) name of output file or directory, - for stdout'
23587
+ describe: '(optional) name of output file or directory, - for stdout',
23588
+ DEFAULT: {
23589
+ multi_error_msg: 'Command takes one file or directory argument.'
23590
+ }
23522
23591
  })
23523
23592
  .option('format', {
23524
23593
  describe: 'options: shapefile,geojson,topojson,json,dbf,csv,tsv,svg'
@@ -23702,7 +23771,6 @@ ${svg}
23702
23771
 
23703
23772
  parser.command('affine')
23704
23773
  .describe('transform coordinates by shifting, scaling and rotating')
23705
- .flag('no_args')
23706
23774
  .option('shift', {
23707
23775
  type: 'strings',
23708
23776
  describe: 'x,y offsets in source units (e.g. 5000,-5000)'
@@ -23941,7 +24009,6 @@ ${svg}
23941
24009
 
23942
24010
  parser.command('colorizer')
23943
24011
  .describe('define a function to convert data values to color classes')
23944
- .flag('no_arg')
23945
24012
  .option('colors', {
23946
24013
  describe: 'comma-separated list of CSS colors',
23947
24014
  type: 'colors'
@@ -24130,7 +24197,6 @@ ${svg}
24130
24197
 
24131
24198
  parser.command('drop')
24132
24199
  .describe('delete layer(s) or elements within the target layer(s)')
24133
- .flag('no_arg') // prevent trying to pass a list of layer names as default option
24134
24200
  .option('geometry', {
24135
24201
  describe: 'delete all geometry from the target layer(s)',
24136
24202
  type: 'flag'
@@ -24337,7 +24403,6 @@ ${svg}
24337
24403
 
24338
24404
  parser.command('innerlines')
24339
24405
  .describe('convert polygons to polylines along shared edges')
24340
- .flag('no_arg')
24341
24406
  .option('where', whereOpt2)
24342
24407
  // .option('each', eachOpt2)
24343
24408
  .option('name', nameOpt)
@@ -24453,7 +24518,6 @@ ${svg}
24453
24518
 
24454
24519
  parser.command('merge-layers')
24455
24520
  .describe('merge multiple layers into as few layers as possible')
24456
- .flag('no_arg')
24457
24521
  .option('force', {
24458
24522
  type: 'flag',
24459
24523
  describe: 'merge layers with inconsistent data fields'
@@ -24475,9 +24539,10 @@ ${svg}
24475
24539
  parser.command('point-grid')
24476
24540
  .describe('create a rectangular grid of points')
24477
24541
  .validate(validateGridOpts)
24478
- .option('-', {
24542
+ .option('_', {
24479
24543
  label: '<cols,rows>',
24480
- describe: 'size of the grid, e.g. -point-grid 100,100'
24544
+ describe: 'size of the grid, e.g. -point-grid 100,100',
24545
+ DEFAULT: true
24481
24546
  })
24482
24547
  .option('interval', {
24483
24548
  describe: 'distance between adjacent points, in source units',
@@ -24499,7 +24564,6 @@ ${svg}
24499
24564
 
24500
24565
  parser.command('points')
24501
24566
  .describe('create a point layer from a different layer type')
24502
- .flag('no_arg')
24503
24567
  .option('x', {
24504
24568
  describe: 'field containing x coordinate'
24505
24569
  })
@@ -24561,9 +24625,11 @@ ${svg}
24561
24625
 
24562
24626
  parser.command('proj')
24563
24627
  .describe('project your data (using Proj.4)')
24564
- .flag('multi_arg')
24565
24628
  .option('crs', {
24566
- DEFAULT: true,
24629
+ DEFAULT: {
24630
+ multi_arg: true,
24631
+ join: ' '
24632
+ },
24567
24633
  describe: 'set destination CRS using a Proj.4 definition or alias'
24568
24634
  })
24569
24635
  .option('projection', {
@@ -24639,7 +24705,6 @@ ${svg}
24639
24705
  })
24640
24706
  .option('target', targetOpt);
24641
24707
 
24642
-
24643
24708
  parser.command('simplify')
24644
24709
  .validate(validateSimplifyOpts)
24645
24710
  .example('Retain 10% of removable vertices\n$ mapshaper input.shp -simplify 10%')
@@ -24793,7 +24858,8 @@ ${svg}
24793
24858
  parser.command('split-on-grid')
24794
24859
  .describe('split features into separate layers using a grid')
24795
24860
  .validate(validateGridOpts)
24796
- .option('-', {
24861
+ .option('_', {
24862
+ DEFAULT: true,
24797
24863
  label: '<cols,rows>',
24798
24864
  describe: 'size of the grid, e.g. -split-on-grid 12,10'
24799
24865
  })
@@ -25390,7 +25456,12 @@ ${svg}
25390
25456
 
25391
25457
  parser.command('comment')
25392
25458
  .describe('add a comment to the sequence of commands')
25393
- .flag('multi_arg');
25459
+ .option('message', {
25460
+ DEFAULT: {
25461
+ multi_arg: true,
25462
+ join: ' '
25463
+ }
25464
+ });
25394
25465
 
25395
25466
  parser.command('encodings')
25396
25467
  .describe('print list of supported text encodings (for .dbf import)');
@@ -25421,7 +25492,12 @@ ${svg}
25421
25492
 
25422
25493
  parser.command('print')
25423
25494
  .describe('print a message to stdout')
25424
- .flag('multi_arg');
25495
+ .option('message', {
25496
+ DEFAULT: {
25497
+ multi_arg: true,
25498
+ join: ' '
25499
+ }
25500
+ });
25425
25501
 
25426
25502
  parser.command('projections')
25427
25503
  .describe('print list of supported projections');
@@ -28053,6 +28129,8 @@ ${svg}
28053
28129
  opts = Object.assign({}, opts);
28054
28130
  opts.input = Object.assign({}, opts.input); // make sure we have a cache
28055
28131
 
28132
+ convertDataObjects(files, opts.input);
28133
+
28056
28134
  files = expandFiles(files, opts.input);
28057
28135
 
28058
28136
  if (files.length === 0) {
@@ -28083,6 +28161,22 @@ ${svg}
28083
28161
  return dataset;
28084
28162
  };
28085
28163
 
28164
+ // replace any JSON data objects with filenames and cache the data
28165
+ function convertDataObjects(files, cache) {
28166
+ var names = files.map(str => stringLooksLikeJSON(str) ? 'layer.json' : null).filter(Boolean);
28167
+ if (names.length === 0) return;
28168
+ if (names.length > 1) {
28169
+ // make unique names if importing multiple objects
28170
+ names = utils.uniqifyNames(names, formatVersionedFileName);
28171
+ }
28172
+ files.forEach((str, i) => {
28173
+ if (!stringLooksLikeJSON(str)) return;
28174
+ var name = names.shift();
28175
+ cache[name] = str;
28176
+ files[i] = name;
28177
+ });
28178
+ }
28179
+
28086
28180
  async function importMshpFile(file, catalog, opts) {
28087
28181
  var buf = cli.readFile(file, null, opts.input);
28088
28182
  var obj = await unpackSessionData(buf);
@@ -33396,7 +33490,7 @@ ${svg}
33396
33490
  if (n != values.length) {
33397
33491
  stop('Mismatch in number of categories and number of values');
33398
33492
  }
33399
- return values;
33493
+ return parseValues(values); // convert numerical strings to numbers
33400
33494
  }
33401
33495
 
33402
33496
  function getIndexes(n) {
@@ -33564,7 +33658,7 @@ ${svg}
33564
33658
  if (opts.index_field) {
33565
33659
  dataField = opts.index_field;
33566
33660
  fieldType = getColumnType(opts.field, records);
33567
- } else if (opts.field) {
33661
+ } else if (opts.field) {
33568
33662
  dataField = opts.field;
33569
33663
  fieldType = getColumnType(opts.field, records);
33570
33664
  }
@@ -33596,6 +33690,9 @@ ${svg}
33596
33690
  if ((!opts.categories || opts.categories.includes('*')) && dataField) {
33597
33691
  opts.categories = getUniqFieldValues(records, dataField);
33598
33692
  }
33693
+ if (opts.categories && fieldType == 'number') {
33694
+ opts.categories = opts.categories.map(str => +str);
33695
+ }
33599
33696
  }
33600
33697
 
33601
33698
  if (opts.classes) {
@@ -34740,34 +34837,48 @@ ${svg}
34740
34837
  }
34741
34838
 
34742
34839
  function getFeatureEditor(lyr, dataset) {
34743
- var changed = false;
34744
34840
  var api = {};
34745
34841
  // need to copy attribute to avoid circular references if geojson is assigned
34746
34842
  // to a data property.
34747
34843
  var copy = copyLayer(lyr);
34748
34844
  var features = exportLayerAsGeoJSON(copy, dataset, {}, true);
34845
+ var features2 = [];
34749
34846
 
34750
34847
  api.get = function(i) {
34848
+ if (i > 0) features[i-1] = null; // garbage-collect old features
34751
34849
  return features[i];
34752
34850
  };
34753
34851
 
34754
34852
  api.set = function(feat, i) {
34755
- changed = true;
34853
+ var arr;
34854
+
34756
34855
  if (utils.isString(feat)) {
34757
34856
  feat = JSON.parse(feat);
34758
34857
  }
34759
- features[i] = GeoJSON.toFeature(feat); // TODO: validate
34858
+
34859
+ if (!feat) return;
34860
+
34861
+ if (feat.type == 'GeometryCollection') {
34862
+ arr = feat.geometries.map(geom => GeoJSON.toFeature(geom));
34863
+ } else if (feat.type == 'FeatureCollection') {
34864
+ arr = feat.features;
34865
+ } else {
34866
+ feat = GeoJSON.toFeature(feat);
34867
+ }
34868
+
34869
+ if (arr) {
34870
+ features2 = features2.concat(arr);
34871
+ } else {
34872
+ features2.push(feat);
34873
+ }
34760
34874
  };
34761
34875
 
34762
34876
  api.done = function() {
34763
- if (!changed) return; // read-only expression
34764
- // TODO: validate number of features, etc.
34877
+ if (features2.length === 0) return; // read-only expression
34765
34878
  var geojson = {
34766
34879
  type: 'FeatureCollection',
34767
- features: features
34880
+ features: features2
34768
34881
  };
34769
-
34770
- // console.log(JSON.stringify(geojson, null, 2))
34771
34882
  return importGeoJSON(geojson);
34772
34883
  };
34773
34884
  return api;
@@ -40530,9 +40641,8 @@ ${svg}
40530
40641
  cmd.polygonGrid = function(targetLayers, targetDataset, opts) {
40531
40642
  requireProjectedDataset(targetDataset);
40532
40643
  var params = getGridParams(targetLayers, targetDataset, opts);
40533
- var gridDataset = makeGridDataset(params);
40534
-
40535
- gridDataset.info = targetDataset.info; // copy CRS to grid dataset // TODO: improve
40644
+ var gridDataset = makeGridDataset(params); // grid is a new dataset
40645
+ gridDataset.info = copyDatasetInfo(targetDataset.info);
40536
40646
  setOutputLayerName(gridDataset.layers[0], null, 'grid', opts);
40537
40647
  if (opts.debug) gridDataset.layers.push(cmd.pointGrid2(targetLayers, targetDataset, opts));
40538
40648
  return gridDataset;
@@ -41393,31 +41503,91 @@ ${svg}
41393
41503
  parseConsoleCommands: parseConsoleCommands
41394
41504
  });
41395
41505
 
41506
+ function getTargetProxy(target) {
41507
+ var lyr = target.layers[0];
41508
+ var data = getLayerInfo(lyr, target.dataset);
41509
+ data.layer = lyr;
41510
+ data.dataset = target.dataset;
41511
+ return data;
41512
+ }
41513
+
41514
+ function getIOProxy(job) {
41515
+ var obj = {
41516
+ _cache: {}
41517
+ };
41518
+ obj.addInputFile = function(filename, content) {
41519
+ obj._cache[filename] = content;
41520
+ };
41521
+ return obj;
41522
+ }
41523
+
41524
+ function commandTakesFileInput(name) {
41525
+ return (name == 'i' || name == 'join' || name == 'erase' || name == 'clip' || name == 'include');
41526
+ }
41527
+
41528
+ // TODO: implement these and other functions
41529
+ // TODO: move this info into individual command definitions (to make
41530
+ // commands more modular and support a future plugin system)
41531
+
41532
+ // export function commandMayRemoveArcs(cmd) {
41533
+
41534
+ // }
41535
+
41536
+ // export function commandMayChangeArcs(cmd) {
41537
+ // // return arcsMayHaveChanged({[cmd]: true});
41538
+ // }
41539
+
41540
+ // export function arcsMayNeedCleanup(flags) {
41541
+ // return flags.clip || flags.erase || flags.slice || flags.rectangle || flags.buffer ||
41542
+ // flags.union || flags.clean || flags.drop || false;
41543
+ // }
41544
+
41545
+ // export function arcsMayBeChanged(flags) {
41546
+ // return arcsMayNeedCleanup(flags) || flags.proj || flags.simplify ||
41547
+ // flags.simplify_method || flags.arc_count || flags.repair || flags.affine ||
41548
+ // flags.mosaic || flags.snap;
41549
+ // }
41550
+
41396
41551
  cmd.run = async function(job, targets, opts) {
41397
- var commandStr, commands;
41552
+ var tmp, commands;
41398
41553
  if (!opts.expression) {
41399
41554
  stop("Missing expression parameter");
41400
41555
  }
41401
- commandStr = runGlobalExpression(opts.expression, targets);
41556
+
41557
+ // io proxy adds ability to add datasets dynamically in a required function
41558
+ var ctx = getBaseContext();
41559
+ ctx.io = getIOProxy();
41560
+ tmp = runGlobalExpression(opts.expression, targets, ctx);
41561
+
41402
41562
  // Support async functions as expressions
41403
- if (utils.isPromise(commandStr)) {
41404
- commandStr = await commandStr;
41563
+ if (utils.isPromise(tmp)) {
41564
+ tmp = await tmp;
41565
+ }
41566
+ if (tmp && !utils.isString(tmp)) {
41567
+ stop('Expected a string containing mapshaper commands; received:', tmp);
41405
41568
  }
41406
- if (commandStr) {
41407
- message(`command: [${commandStr}]`);
41408
- commands = parseCommands(commandStr);
41569
+ if (tmp) {
41570
+ message(`command: [${tmp}]`);
41571
+ commands = parseCommands(tmp);
41572
+
41573
+ // TODO: remove duplication with mapshaper-run-commands.mjs
41574
+ commands.forEach(function(cmd) {
41575
+ if (commandTakesFileInput(cmd.name)) {
41576
+ cmd.options.input = ctx.io._cache;
41577
+ }
41578
+ });
41579
+
41409
41580
  await utils.promisify(runParsedCommands)(commands, job);
41410
41581
  }
41411
41582
  };
41412
41583
 
41413
41584
  // This could return a Promise or a value or nothing
41414
- function runGlobalExpression(expression, targets) {
41415
- var ctx = getBaseContext();
41416
- var output, targetData;
41585
+ function runGlobalExpression(expression, targets, ctx) {
41586
+ ctx = ctx || getBaseContext();
41587
+ var output;
41417
41588
  // TODO: throw an informative error if target is used when there are multiple targets
41418
41589
  if (targets && targets.length == 1) {
41419
- targetData = getRunCommandData(targets[0]);
41420
- Object.defineProperty(ctx, 'target', {value: targetData});
41590
+ Object.defineProperty(ctx, 'target', {value: getTargetProxy(targets[0])});
41421
41591
  }
41422
41592
  // Add defined functions and data to the expression context
41423
41593
  // (Such as functions imported via the -require command)
@@ -41430,15 +41600,6 @@ ${svg}
41430
41600
  return output;
41431
41601
  }
41432
41602
 
41433
-
41434
- function getRunCommandData(target) {
41435
- var lyr = target.layers[0];
41436
- var data = getLayerInfo(lyr, target.dataset);
41437
- data.layer = lyr;
41438
- data.dataset = target.dataset;
41439
- return data;
41440
- }
41441
-
41442
41603
  cmd.require = function(targets, opts) {
41443
41604
  var defs = getStashedVar('defs');
41444
41605
  var moduleFile, moduleName, mod;
@@ -44210,6 +44371,7 @@ ${svg}
44210
44371
  commands;
44211
44372
  try {
44212
44373
  commands = parseCommands(argv);
44374
+
44213
44375
  } catch(e) {
44214
44376
  printError(e);
44215
44377
  return callback(e);
@@ -44263,9 +44425,6 @@ ${svg}
44263
44425
  }
44264
44426
  }
44265
44427
 
44266
- function commandTakesFileInput(name) {
44267
- return (name == 'i' || name == 'join' || name == 'erase' || name == 'clip' || name == 'include');
44268
- }
44269
44428
 
44270
44429
  function toLegacyOutputFormat(arr) {
44271
44430
  if (arr.length > 1) {