mapshaper 0.5.87 → 0.5.88

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.5.86";
3
+ var VERSION = "0.5.87";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -4895,6 +4895,11 @@
4895
4895
  };
4896
4896
  }
4897
4897
 
4898
+ function MultiShapeIter(arcs) {
4899
+ var iter = new ShapeIter(arcs);
4900
+
4901
+ }
4902
+
4898
4903
  // Iterate along a path made up of one or more arcs.
4899
4904
  //
4900
4905
  function ShapeIter(arcs) {
@@ -4945,6 +4950,7 @@
4945
4950
  PointIter: PointIter,
4946
4951
  ArcIter: ArcIter,
4947
4952
  FilteredArcIter: FilteredArcIter,
4953
+ MultiShapeIter: MultiShapeIter,
4948
4954
  ShapeIter: ShapeIter
4949
4955
  });
4950
4956
 
@@ -5362,10 +5368,6 @@
5362
5368
  return i - i2;
5363
5369
  };
5364
5370
 
5365
- this.getVertex2 = function(i) {
5366
- return [_xx[i], _yy[i]];
5367
- };
5368
-
5369
5371
  this.getVertex = function(arcId, nth) {
5370
5372
  var i = this.indexOfVertex(arcId, nth);
5371
5373
  return {
@@ -8226,10 +8228,21 @@
8226
8228
  return +n.toPrecision(d);
8227
8229
  }
8228
8230
 
8231
+
8229
8232
  function roundToDigits(n, d) {
8230
8233
  return +n.toFixed(d); // string conversion makes this slow
8231
8234
  }
8232
8235
 
8236
+ // Used in mapshaper-expression-utils.js
8237
+ // TODO: choose between this and the above function
8238
+ function roundToDigits2(n, d) {
8239
+ var k = 1;
8240
+ if (!n && n !== 0) return n; // don't coerce null to 0
8241
+ d = d | 0;
8242
+ while (d-- > 0) k *= 10;
8243
+ return Math.round(n * k) / k;
8244
+ }
8245
+
8233
8246
  function roundToTenths(n) {
8234
8247
  return (Math.round(n * 10)) / 10;
8235
8248
  }
@@ -8304,6 +8317,7 @@
8304
8317
  __proto__: null,
8305
8318
  roundToSignificantDigits: roundToSignificantDigits,
8306
8319
  roundToDigits: roundToDigits,
8320
+ roundToDigits2: roundToDigits2,
8307
8321
  roundToTenths: roundToTenths,
8308
8322
  getRoundingFunction: getRoundingFunction,
8309
8323
  getBoundsPrecisionForDisplay: getBoundsPrecisionForDisplay,
@@ -8942,15 +8956,15 @@
8942
8956
  });
8943
8957
  }
8944
8958
 
8945
- function addUtils(env) {
8959
+ function cleanExpression(exp) {
8960
+ // workaround for problem in GNU Make v4: end-of-line backslashes inside
8961
+ // quoted strings are left in the string (other shell environments remove them)
8962
+ return exp.replace(/\\\n/g, ' ');
8963
+ }
8964
+
8965
+ function addFeatureExpressionUtils(env) {
8946
8966
  Object.assign(env, {
8947
- round: function(val, dig) {
8948
- var k = 1;
8949
- if (!val && val !== 0) return val; // don't coerce null to 0
8950
- dig = dig | 0;
8951
- while(dig-- > 0) k *= 10;
8952
- return Math.round(val * k) / k;
8953
- },
8967
+ round: roundToDigits2,
8954
8968
  int_median: interpolated_median,
8955
8969
  sprintf: utils.format,
8956
8970
  blend: blend
@@ -9329,6 +9343,39 @@
9329
9343
  };
9330
9344
  }
9331
9345
 
9346
+ // Returns an object representing a layer in a JS expression
9347
+ function getLayerProxy(lyr, arcs) {
9348
+ var obj = {};
9349
+ var records = lyr.data ? lyr.data.getRecords() : null;
9350
+ var getters = {
9351
+ name: lyr.name,
9352
+ data: records,
9353
+ type: lyr.geometry_type
9354
+ };
9355
+ addGetters(obj, getters);
9356
+ addBBoxGetter(obj, lyr, arcs);
9357
+ obj.empty = function() {
9358
+ return getFeatureCount(lyr) === 0;
9359
+ };
9360
+ obj.size = function() {
9361
+ return getFeatureCount(lyr);
9362
+ };
9363
+ return obj;
9364
+ }
9365
+
9366
+ function addLayerGetters(ctx, lyr, arcs) {
9367
+ var layerProxy;
9368
+ addGetters(ctx, {
9369
+ layer_name: lyr.name || '', // consider removing this
9370
+ layer: function() {
9371
+ // init on first access (to avoid overhead if not used)
9372
+ if (!layerProxy) layerProxy = getLayerProxy(lyr, arcs);
9373
+ return layerProxy;
9374
+ }
9375
+ });
9376
+ return ctx;
9377
+ }
9378
+
9332
9379
  function addBBoxGetter(obj, lyr, arcs) {
9333
9380
  var bbox;
9334
9381
  addGetters(obj, {
@@ -9358,32 +9405,6 @@
9358
9405
  return bbox;
9359
9406
  }
9360
9407
 
9361
- // Returns an object representing a layer in a JS expression
9362
- function getLayerProxy(lyr, arcs) {
9363
- var obj = {};
9364
- var records = lyr.data ? lyr.data.getRecords() : null;
9365
- var getters = {
9366
- name: lyr.name,
9367
- data: records
9368
- };
9369
- addGetters(obj, getters);
9370
- addBBoxGetter(obj, lyr, arcs);
9371
- return obj;
9372
- }
9373
-
9374
- function addLayerGetters(ctx, lyr, arcs) {
9375
- var layerProxy;
9376
- addGetters(ctx, {
9377
- layer_name: lyr.name || '', // consider removing this
9378
- layer: function() {
9379
- // init on first access (to avoid overhead if not used)
9380
- if (!layerProxy) layerProxy = getLayerProxy(lyr, arcs);
9381
- return layerProxy;
9382
- }
9383
- });
9384
- return ctx;
9385
- }
9386
-
9387
9408
  // Returns a function to return a feature proxy by id
9388
9409
  // (the proxy appears as "this" or "$" in a feature expression)
9389
9410
  function initFeatureProxy(lyr, arcs, optsArg) {
@@ -9580,11 +9601,6 @@
9580
9601
  return compileFeatureExpression(exp, lyr, arcs, opts);
9581
9602
  }
9582
9603
 
9583
- function cleanExpression(exp) {
9584
- // workaround for problem in GNU Make v4: end-of-line backslashes inside
9585
- // quoted strings are left in the string (other shell environments remove them)
9586
- return exp.replace(/\\\n/g, ' ');
9587
- }
9588
9604
 
9589
9605
  function compileFeaturePairFilterExpression(exp, lyr, arcs) {
9590
9606
  var func = compileFeaturePairExpression(exp, lyr, arcs);
@@ -9720,14 +9736,19 @@
9720
9736
 
9721
9737
  function compileExpressionToFunction(exp, opts) {
9722
9738
  // $$ added to avoid duplication with data field variables (an error condition)
9723
- var functionBody = "with($$env){with($$record){ " + (opts.returns ? 'return ' : '') +
9724
- exp + "}}";
9725
- var func;
9739
+ var functionBody, func;
9740
+ if (opts.returns) {
9741
+ // functionBody = 'return ' + functionBody;
9742
+ functionBody = 'var $$retn = ' + exp + '; return $$retn;';
9743
+ } else {
9744
+ functionBody = exp;
9745
+ }
9746
+ functionBody = 'with($$env){with($$record){ ' + functionBody + '}}';
9726
9747
  try {
9727
- func = new Function("$$record,$$env", functionBody);
9748
+ func = new Function('$$record,$$env', functionBody);
9728
9749
  } catch(e) {
9729
9750
  // if (opts.quiet) throw e;
9730
- stop(e.name, "in expression [" + exp + "]");
9751
+ stop(e.name, 'in expression [' + exp + ']');
9731
9752
  }
9732
9753
  return func;
9733
9754
  }
@@ -9770,7 +9791,7 @@
9770
9791
  var ctx = {};
9771
9792
  var fields = lyr.data ? lyr.data.getFields() : [];
9772
9793
  opts = opts || {};
9773
- addUtils(env); // mix in round(), sprintf(), etc.
9794
+ addFeatureExpressionUtils(env); // mix in round(), sprintf(), etc.
9774
9795
  if (fields.length > 0) {
9775
9796
  // default to null values, so assignments to missing data properties
9776
9797
  // are applied to the data record, not the global object
@@ -9825,7 +9846,6 @@
9825
9846
  var Expressions = /*#__PURE__*/Object.freeze({
9826
9847
  __proto__: null,
9827
9848
  compileValueExpression: compileValueExpression,
9828
- cleanExpression: cleanExpression,
9829
9849
  compileFeaturePairFilterExpression: compileFeaturePairFilterExpression,
9830
9850
  compileFeaturePairExpression: compileFeaturePairExpression,
9831
9851
  compileFeatureExpression: compileFeatureExpression,
@@ -18568,6 +18588,13 @@ ${svg}
18568
18588
  return this;
18569
18589
  };
18570
18590
 
18591
+ this.options = function(o) {
18592
+ Object.keys(o).forEach(function(key) {
18593
+ this.option(key, o[key]);
18594
+ }, this);
18595
+ return this;
18596
+ };
18597
+
18571
18598
  this.done = function() {
18572
18599
  return _command;
18573
18600
  };
@@ -19531,14 +19558,6 @@ ${svg}
19531
19558
  .option('target', targetOpt)
19532
19559
  .option('no-replace', noReplaceOpt);
19533
19560
 
19534
- parser.command('ignore')
19535
- // .describe('stop processing if a condition is met')
19536
- .option('empty', {
19537
- describe: 'ignore empty files',
19538
- type: 'flag'
19539
- })
19540
- .option('target', targetOpt);
19541
-
19542
19561
  parser.command('include')
19543
19562
  .describe('import JS data and functions for use in JS expressions')
19544
19563
  .option('file', {
@@ -20501,6 +20520,49 @@ ${svg}
20501
20520
  })
20502
20521
  .option('target', targetOpt);
20503
20522
 
20523
+ parser.section('Control flow commands');
20524
+
20525
+ var ifOpts = {
20526
+ expression: {
20527
+ DEFAULT: true,
20528
+ describe: 'JS expression targeting a single layer'
20529
+ },
20530
+ empty: {
20531
+ describe: 'run if layer is empty',
20532
+ type: 'flag'
20533
+ },
20534
+ 'not-empty': {
20535
+ describe: 'run if layer is not empty',
20536
+ type: 'flag'
20537
+ },
20538
+ layer: {
20539
+ describe: 'name or id of layer to test (default is current target)'
20540
+ },
20541
+ target: targetOpt
20542
+ };
20543
+
20544
+ parser.command('if')
20545
+ .describe('run the following commands if a condition is met')
20546
+ .options(ifOpts);
20547
+
20548
+ parser.command('elif')
20549
+ .describe('test an alternate condition; used after -if')
20550
+ .options(ifOpts);
20551
+
20552
+ parser.command('else')
20553
+ .describe('run commands if all preceding -if/-elif conditions are false');
20554
+
20555
+ parser.command('endif')
20556
+ .describe('mark the end of an -if sequence');
20557
+
20558
+ parser.command('ignore')
20559
+ // .describe('stop processing if a condition is met')
20560
+ .option('empty', {
20561
+ describe: 'ignore empty files',
20562
+ type: 'flag'
20563
+ })
20564
+ .option('target', targetOpt);
20565
+
20504
20566
  parser.section('Informational commands');
20505
20567
 
20506
20568
  parser.command('calc')
@@ -34173,6 +34235,141 @@ ${svg}
34173
34235
  };
34174
34236
  }
34175
34237
 
34238
+ function resetControlFlow() {
34239
+ setStateVar('control', null);
34240
+ }
34241
+
34242
+ function inControlBlock() {
34243
+ var state = getState();
34244
+ return !!state.inControlBlock;
34245
+ }
34246
+
34247
+ function enterActiveBranch() {
34248
+ var state = getState();
34249
+ state.inControlBlock = true;
34250
+ state.active = true;
34251
+ state.complete = true;
34252
+ }
34253
+
34254
+ function enterInactiveBranch() {
34255
+ var state = getState();
34256
+ state.inControlBlock = true;
34257
+ state.active = false;
34258
+ }
34259
+
34260
+ function blockWasActive() {
34261
+ return !!getState().complete;
34262
+ }
34263
+
34264
+ function inActiveBranch() {
34265
+ return !!getState().active;
34266
+ }
34267
+
34268
+ function getState() {
34269
+ var o = getStateVar('control') || setStateVar('control', {}) || getStateVar('control');
34270
+ return o;
34271
+ }
34272
+
34273
+ function compileLayerExpression(expr, lyr, dataset, opts) {
34274
+ var proxy = getLayerProxy(lyr, dataset.arcs, opts);
34275
+ var exprOpts = Object.assign({returns: true}, opts);
34276
+ var func = compileExpressionToFunction(expr, exprOpts);
34277
+ var ctx = proxy;
34278
+ return function() {
34279
+ try {
34280
+ return func.call(proxy, {}, ctx);
34281
+ } catch(e) {
34282
+ // if (opts.quiet) throw e;
34283
+ stop(e.name, "in expression [" + expr + "]:", e.message);
34284
+ }
34285
+ };
34286
+ }
34287
+
34288
+ function skipCommand(cmdName) {
34289
+ // allow all control commands to run
34290
+ if (isControlFlowCommand(cmdName)) return false;
34291
+ return inControlBlock() && !inActiveBranch();
34292
+ }
34293
+
34294
+ cmd.if = function(catalog, opts) {
34295
+ if (inControlBlock()) {
34296
+ stop('Nested -if commands are not supported.');
34297
+ }
34298
+ evaluateIf(catalog, opts);
34299
+ };
34300
+
34301
+ cmd.elif = function(catalog, opts) {
34302
+ if (!inControlBlock()) {
34303
+ stop('-elif command must be preceded by an -if command.');
34304
+ }
34305
+ evaluateIf(catalog, opts);
34306
+ };
34307
+
34308
+ cmd.else = function() {
34309
+ if (!inControlBlock()) {
34310
+ stop('-else command must be preceded by an -if command.');
34311
+ }
34312
+ if (blockWasActive()) {
34313
+ enterInactiveBranch();
34314
+ } else {
34315
+ enterActiveBranch();
34316
+ }
34317
+ };
34318
+
34319
+ cmd.endif = function() {
34320
+ if (!inControlBlock()) {
34321
+ stop('-endif command must be preceded by an -if command.');
34322
+ }
34323
+ resetControlFlow();
34324
+ };
34325
+
34326
+ function isControlFlowCommand(cmd) {
34327
+ return ['if','elif','else','endif'].includes(cmd);
34328
+ }
34329
+
34330
+ function testLayer(catalog, opts) {
34331
+ var targ = getTargetLayer(catalog, opts);
34332
+ if (opts.expression) {
34333
+ return compileLayerExpression(opts.expression, targ.layer, targ.dataset, opts)();
34334
+ }
34335
+ if (opts.empty) {
34336
+ return layerIsEmpty(targ.layer);
34337
+ }
34338
+ if (opts.not_empty) {
34339
+ return !layerIsEmpty(targ.layer);
34340
+ }
34341
+ return true;
34342
+ }
34343
+
34344
+ function evaluateIf(catalog, opts) {
34345
+ if (!blockWasActive() && testLayer(catalog, opts)) {
34346
+ enterActiveBranch();
34347
+ } else {
34348
+ enterInactiveBranch();
34349
+ }
34350
+ }
34351
+
34352
+ // layerId: optional layer identifier
34353
+ //
34354
+ function getTargetLayer(catalog, opts) {
34355
+ var layerId = opts.layer || opts.target;
34356
+ var targets = catalog.findCommandTargets(layerId);
34357
+ if (targets.length === 0) {
34358
+ if (layerId) {
34359
+ stop('Layer not found:', layerId);
34360
+ } else {
34361
+ stop('Missing a target layer.');
34362
+ }
34363
+ }
34364
+ if (targets.length > 1 || targets[0].layers.length > 1) {
34365
+ stop('Command requires a single target layer.');
34366
+ }
34367
+ return {
34368
+ layer: targets[0].layers[0],
34369
+ dataset: targets[0].dataset
34370
+ };
34371
+ }
34372
+
34176
34373
  cmd.ignore = function(targetLayer, dataset, opts) {
34177
34374
  if (opts.empty && layerIsEmpty(targetLayer)) {
34178
34375
  interrupt('Layer is empty, stopping processing');
@@ -34719,6 +34916,28 @@ ${svg}
34719
34916
  return findVertexIds(p2.x, p2.y, arcs);
34720
34917
  }
34721
34918
 
34919
+ // Given a location @p (e.g. corresponding to the mouse pointer location),
34920
+ // find the midpoint of two vertices on @shp suitable for inserting a new vertex,
34921
+ // but only if:
34922
+ // 1. point @p is closer to the midpoint than either adjacent vertex
34923
+ // 2. the segment containing @p is longer than a minimum distance in pixels.
34924
+ //
34925
+ function findInsertionPoint(p, shp, arcs, pixelSize) {
34926
+ var p2 = findNearestVertex(p[0], p[1], shp, arcs);
34927
+
34928
+ }
34929
+
34930
+ function snapVerticesToPoint(ids, p, arcs, final) {
34931
+ ids.forEach(function(idx) {
34932
+ setVertexCoords(p[0], p[1], idx, arcs);
34933
+ });
34934
+ if (final) {
34935
+ // kludge to get dataset to recalculate internal bounding boxes
34936
+ arcs.transformPoints(function() {});
34937
+ }
34938
+ }
34939
+
34940
+
34722
34941
  // p: point to snap
34723
34942
  // ids: ids of nearby vertices, possibly including an arc endpoint
34724
34943
  function snapPointToArcEndpoint(p, ids, arcs) {
@@ -34805,6 +35024,8 @@ ${svg}
34805
35024
  var VertexUtils = /*#__PURE__*/Object.freeze({
34806
35025
  __proto__: null,
34807
35026
  findNearestVertices: findNearestVertices,
35027
+ findInsertionPoint: findInsertionPoint,
35028
+ snapVerticesToPoint: snapVerticesToPoint,
34808
35029
  snapPointToArcEndpoint: snapPointToArcEndpoint,
34809
35030
  findVertexIds: findVertexIds,
34810
35031
  getVertexCoords: getVertexCoords,
@@ -38192,7 +38413,9 @@ ${svg}
38192
38413
  if (d.anchor == 'end') {
38193
38414
  scaleAndShiftCoords(coords, 1, [-dx, -dy - headLen]);
38194
38415
  } else if (d.anchor == 'middle') {
38195
- scaleAndShiftCoords(coords, 1, [-dx/2, (-dy - headLen)/2]);
38416
+ // shift midpoint away from the head a bit for a more balanced placement
38417
+ // scaleAndShiftCoords(coords, 1, [-dx/2, (-dy - headLen)/2]);
38418
+ scaleAndShiftCoords(coords, 1, [-dx * 0.5, -dy * 0.5 - headLen * 0.25]);
38196
38419
  }
38197
38420
 
38198
38421
  rotateCoords(coords, direction);
@@ -38869,8 +39092,13 @@ ${svg}
38869
39092
  targets,
38870
39093
  targetDataset,
38871
39094
  targetLayers,
39095
+ target,
38872
39096
  arcs;
38873
39097
 
39098
+ if (skipCommand(name)) {
39099
+ return done(null);
39100
+ }
39101
+
38874
39102
  try { // catch errors from synchronous functions
38875
39103
 
38876
39104
  T$1.start();
@@ -39037,6 +39265,14 @@ ${svg}
39037
39265
  outputLayers = targetDataset.layers; // kludge to allow layer naming below
39038
39266
  }
39039
39267
 
39268
+ } else if (name == 'if' || name == 'elif') {
39269
+ // target = findSingleTargetLayer(opts.layer, targets[0], catalog);
39270
+ // cmd[name](target.layer, target.dataset, opts);
39271
+ cmd[name](catalog, opts);
39272
+
39273
+ } else if (name == 'else' || name == 'endif') {
39274
+ cmd[name]();
39275
+
39040
39276
  } else if (name == 'ignore') {
39041
39277
  applyCommandToEachLayer(cmd.ignore, targetLayers, targetDataset, opts);
39042
39278
 
@@ -39269,6 +39505,7 @@ ${svg}
39269
39505
  });
39270
39506
  }
39271
39507
 
39508
+
39272
39509
  // Apply a command to an array of target layers
39273
39510
  function applyCommandToEachLayer(func, targetLayers) {
39274
39511
  var args = utils.toArray(arguments).slice(2);
@@ -39527,6 +39764,8 @@ ${svg}
39527
39764
  }
39528
39765
 
39529
39766
  function runParsedCommands2(commands, catalog, cb) {
39767
+ // resetting closes any unterminated -if blocks from a previous command sequence
39768
+ resetControlFlow();
39530
39769
  utils.reduceAsync(commands, catalog, nextCommand, cb);
39531
39770
 
39532
39771
  function nextCommand(catalog, cmd, next) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.5.87",
3
+ "version": "0.5.88",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",