mapshaper 0.5.86 → 0.5.91

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/www/mapshaper.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
 
3
- var VERSION = "0.5.85";
3
+ var VERSION = "0.5.88";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -1240,7 +1240,8 @@
1240
1240
  // print a message to stdout
1241
1241
  function print() {
1242
1242
  STDOUT = true; // tell logArgs() to print to stdout, not stderr
1243
- message.apply(null, arguments);
1243
+ // calling message() adds the "[command name]" prefix
1244
+ _message(utils.toArray(arguments));
1244
1245
  STDOUT = false;
1245
1246
  }
1246
1247
 
@@ -4895,6 +4896,11 @@
4895
4896
  };
4896
4897
  }
4897
4898
 
4899
+ function MultiShapeIter(arcs) {
4900
+ var iter = new ShapeIter(arcs);
4901
+
4902
+ }
4903
+
4898
4904
  // Iterate along a path made up of one or more arcs.
4899
4905
  //
4900
4906
  function ShapeIter(arcs) {
@@ -4945,6 +4951,7 @@
4945
4951
  PointIter: PointIter,
4946
4952
  ArcIter: ArcIter,
4947
4953
  FilteredArcIter: FilteredArcIter,
4954
+ MultiShapeIter: MultiShapeIter,
4948
4955
  ShapeIter: ShapeIter
4949
4956
  });
4950
4957
 
@@ -8222,10 +8229,21 @@
8222
8229
  return +n.toPrecision(d);
8223
8230
  }
8224
8231
 
8232
+
8225
8233
  function roundToDigits(n, d) {
8226
8234
  return +n.toFixed(d); // string conversion makes this slow
8227
8235
  }
8228
8236
 
8237
+ // Used in mapshaper-expression-utils.js
8238
+ // TODO: choose between this and the above function
8239
+ function roundToDigits2(n, d) {
8240
+ var k = 1;
8241
+ if (!n && n !== 0) return n; // don't coerce null to 0
8242
+ d = d | 0;
8243
+ while (d-- > 0) k *= 10;
8244
+ return Math.round(n * k) / k;
8245
+ }
8246
+
8229
8247
  function roundToTenths(n) {
8230
8248
  return (Math.round(n * 10)) / 10;
8231
8249
  }
@@ -8300,6 +8318,7 @@
8300
8318
  __proto__: null,
8301
8319
  roundToSignificantDigits: roundToSignificantDigits,
8302
8320
  roundToDigits: roundToDigits,
8321
+ roundToDigits2: roundToDigits2,
8303
8322
  roundToTenths: roundToTenths,
8304
8323
  getRoundingFunction: getRoundingFunction,
8305
8324
  getBoundsPrecisionForDisplay: getBoundsPrecisionForDisplay,
@@ -8938,15 +8957,15 @@
8938
8957
  });
8939
8958
  }
8940
8959
 
8941
- function addUtils(env) {
8960
+ function cleanExpression(exp) {
8961
+ // workaround for problem in GNU Make v4: end-of-line backslashes inside
8962
+ // quoted strings are left in the string (other shell environments remove them)
8963
+ return exp.replace(/\\\n/g, ' ');
8964
+ }
8965
+
8966
+ function addFeatureExpressionUtils(env) {
8942
8967
  Object.assign(env, {
8943
- round: function(val, dig) {
8944
- var k = 1;
8945
- if (!val && val !== 0) return val; // don't coerce null to 0
8946
- dig = dig | 0;
8947
- while(dig-- > 0) k *= 10;
8948
- return Math.round(val * k) / k;
8949
- },
8968
+ round: roundToDigits2,
8950
8969
  int_median: interpolated_median,
8951
8970
  sprintf: utils.format,
8952
8971
  blend: blend
@@ -9325,6 +9344,47 @@
9325
9344
  };
9326
9345
  }
9327
9346
 
9347
+ // Returns an object representing a layer in a JS expression
9348
+ function getLayerProxy(lyr, arcs) {
9349
+ var obj = {};
9350
+ var records = lyr.data ? lyr.data.getRecords() : null;
9351
+ var getters = {
9352
+ name: lyr.name,
9353
+ data: records,
9354
+ type: lyr.geometry_type,
9355
+ size: getFeatureCount(lyr),
9356
+ empty: getFeatureCount(lyr) === 0
9357
+ };
9358
+ addGetters(obj, getters);
9359
+ addBBoxGetter(obj, lyr, arcs);
9360
+ obj.field_exists = function(name) {
9361
+ return lyr.data && lyr.data.fieldExists(name) ? true : false;
9362
+ };
9363
+ obj.field_type = function(name) {
9364
+ return lyr.data && getColumnType(name, lyr.data.getRecords()) || null;
9365
+ };
9366
+ obj.field_includes = function(name, val) {
9367
+ if (!lyr.data) return false;
9368
+ return lyr.data.getRecords().some(function(rec) {
9369
+ return rec && (rec[name] === val);
9370
+ });
9371
+ };
9372
+ return obj;
9373
+ }
9374
+
9375
+ function addLayerGetters(ctx, lyr, arcs) {
9376
+ var layerProxy;
9377
+ addGetters(ctx, {
9378
+ layer_name: lyr.name || '', // consider removing this
9379
+ layer: function() {
9380
+ // init on first access (to avoid overhead if not used)
9381
+ if (!layerProxy) layerProxy = getLayerProxy(lyr, arcs);
9382
+ return layerProxy;
9383
+ }
9384
+ });
9385
+ return ctx;
9386
+ }
9387
+
9328
9388
  function addBBoxGetter(obj, lyr, arcs) {
9329
9389
  var bbox;
9330
9390
  addGetters(obj, {
@@ -9354,32 +9414,6 @@
9354
9414
  return bbox;
9355
9415
  }
9356
9416
 
9357
- // Returns an object representing a layer in a JS expression
9358
- function getLayerProxy(lyr, arcs) {
9359
- var obj = {};
9360
- var records = lyr.data ? lyr.data.getRecords() : null;
9361
- var getters = {
9362
- name: lyr.name,
9363
- data: records
9364
- };
9365
- addGetters(obj, getters);
9366
- addBBoxGetter(obj, lyr, arcs);
9367
- return obj;
9368
- }
9369
-
9370
- function addLayerGetters(ctx, lyr, arcs) {
9371
- var layerProxy;
9372
- addGetters(ctx, {
9373
- layer_name: lyr.name || '', // consider removing this
9374
- layer: function() {
9375
- // init on first access (to avoid overhead if not used)
9376
- if (!layerProxy) layerProxy = getLayerProxy(lyr, arcs);
9377
- return layerProxy;
9378
- }
9379
- });
9380
- return ctx;
9381
- }
9382
-
9383
9417
  // Returns a function to return a feature proxy by id
9384
9418
  // (the proxy appears as "this" or "$" in a feature expression)
9385
9419
  function initFeatureProxy(lyr, arcs, optsArg) {
@@ -9576,11 +9610,6 @@
9576
9610
  return compileFeatureExpression(exp, lyr, arcs, opts);
9577
9611
  }
9578
9612
 
9579
- function cleanExpression(exp) {
9580
- // workaround for problem in GNU Make v4: end-of-line backslashes inside
9581
- // quoted strings are left in the string (other shell environments remove them)
9582
- return exp.replace(/\\\n/g, ' ');
9583
- }
9584
9613
 
9585
9614
  function compileFeaturePairFilterExpression(exp, lyr, arcs) {
9586
9615
  var func = compileFeaturePairExpression(exp, lyr, arcs);
@@ -9716,14 +9745,19 @@
9716
9745
 
9717
9746
  function compileExpressionToFunction(exp, opts) {
9718
9747
  // $$ added to avoid duplication with data field variables (an error condition)
9719
- var functionBody = "with($$env){with($$record){ " + (opts.returns ? 'return ' : '') +
9720
- exp + "}}";
9721
- var func;
9748
+ var functionBody, func;
9749
+ if (opts.returns) {
9750
+ // functionBody = 'return ' + functionBody;
9751
+ functionBody = 'var $$retn = ' + exp + '; return $$retn;';
9752
+ } else {
9753
+ functionBody = exp;
9754
+ }
9755
+ functionBody = 'with($$env){with($$record){ ' + functionBody + '}}';
9722
9756
  try {
9723
- func = new Function("$$record,$$env", functionBody);
9757
+ func = new Function('$$record,$$env', functionBody);
9724
9758
  } catch(e) {
9725
9759
  // if (opts.quiet) throw e;
9726
- stop(e.name, "in expression [" + exp + "]");
9760
+ stop(e.name, 'in expression [' + exp + ']');
9727
9761
  }
9728
9762
  return func;
9729
9763
  }
@@ -9766,7 +9800,7 @@
9766
9800
  var ctx = {};
9767
9801
  var fields = lyr.data ? lyr.data.getFields() : [];
9768
9802
  opts = opts || {};
9769
- addUtils(env); // mix in round(), sprintf(), etc.
9803
+ addFeatureExpressionUtils(env); // mix in round(), sprintf(), etc.
9770
9804
  if (fields.length > 0) {
9771
9805
  // default to null values, so assignments to missing data properties
9772
9806
  // are applied to the data record, not the global object
@@ -9821,7 +9855,6 @@
9821
9855
  var Expressions = /*#__PURE__*/Object.freeze({
9822
9856
  __proto__: null,
9823
9857
  compileValueExpression: compileValueExpression,
9824
- cleanExpression: cleanExpression,
9825
9858
  compileFeaturePairFilterExpression: compileFeaturePairFilterExpression,
9826
9859
  compileFeaturePairExpression: compileFeaturePairExpression,
9827
9860
  compileFeatureExpression: compileFeatureExpression,
@@ -18564,6 +18597,13 @@ ${svg}
18564
18597
  return this;
18565
18598
  };
18566
18599
 
18600
+ this.options = function(o) {
18601
+ Object.keys(o).forEach(function(key) {
18602
+ this.option(key, o[key]);
18603
+ }, this);
18604
+ return this;
18605
+ };
18606
+
18567
18607
  this.done = function() {
18568
18608
  return _command;
18569
18609
  };
@@ -19527,14 +19567,6 @@ ${svg}
19527
19567
  .option('target', targetOpt)
19528
19568
  .option('no-replace', noReplaceOpt);
19529
19569
 
19530
- parser.command('ignore')
19531
- // .describe('stop processing if a condition is met')
19532
- .option('empty', {
19533
- describe: 'ignore empty files',
19534
- type: 'flag'
19535
- })
19536
- .option('target', targetOpt);
19537
-
19538
19570
  parser.command('include')
19539
19571
  .describe('import JS data and functions for use in JS expressions')
19540
19572
  .option('file', {
@@ -20093,33 +20125,22 @@ ${svg}
20093
20125
  .option('target', targetOpt);
20094
20126
 
20095
20127
  parser.command('symbols')
20096
- // .describe('symbolize points as polygons, circles, stars or arrows')
20128
+ .describe('symbolize points as arrows, circles, stars, polygons, etc.')
20097
20129
  .option('type', {
20098
- describe: 'symbol type (e.g. star, polygon, circle, arrow)'
20130
+ describe: 'symbol type (e.g. arrow, circle, square, star, polygon, ring)'
20099
20131
  })
20100
- .option('scale', {
20101
- describe: 'scale symbols by a factor',
20102
- type: 'number'
20103
- })
20104
- .option('pixel-scale', {
20105
- describe: 'symbol scale in meters-per-pixel (see polygons option)',
20106
- type: 'number',
20132
+ .option('stroke', {})
20133
+ .option('stroke-width', {})
20134
+ .option('fill', {
20135
+ describe: 'symbol fill color'
20107
20136
  })
20108
20137
  .option('polygons', {
20109
20138
  describe: 'generate symbols as polygons instead of SVG objects',
20110
20139
  type: 'flag'
20111
20140
  })
20112
- .option('radius', {
20113
- describe: 'distance from center to farthest point on the symbol',
20114
- type: 'distance'
20115
- })
20116
- .option('sides', {
20117
- describe: 'number of sides of a polygon symbol',
20118
- type: 'number'
20119
- })
20120
- .option('orientation', {
20121
- // TODO: removed (replaced by flipped and rotated)
20122
- // describe: 'use orientation=b for a rotated or flipped orientation'
20141
+ .option('pixel-scale', {
20142
+ describe: 'set symbol scale in meters-per-pixel (for polygons option)',
20143
+ type: 'number',
20123
20144
  })
20124
20145
  // .option('flipped', {
20125
20146
  // type: 'flag',
@@ -20127,11 +20148,23 @@ ${svg}
20127
20148
  // })
20128
20149
  .option('rotated', {
20129
20150
  type: 'flag',
20130
- describe: 'symbol is rotated to a different orientation'
20151
+ describe: 'symbol is rotated to an alternate orientation'
20131
20152
  })
20132
20153
  .option('rotation', {
20133
20154
  describe: 'rotation of symbol in degrees'
20134
20155
  })
20156
+ .option('scale', {
20157
+ describe: 'scale symbols by a multiplier',
20158
+ type: 'number'
20159
+ })
20160
+ .option('radius', {
20161
+ describe: 'distance from center to farthest point on the symbol',
20162
+ type: 'distance'
20163
+ })
20164
+ .option('sides', {
20165
+ describe: '(polygon) number of sides of a (regular) polygon symbol',
20166
+ type: 'number'
20167
+ })
20135
20168
  .option('points', {
20136
20169
  describe: '(star) number of points'
20137
20170
  })
@@ -20149,7 +20182,7 @@ ${svg}
20149
20182
  })
20150
20183
  .option('direction', {
20151
20184
  old_alias: 'arrow-direction',
20152
- describe: '(arrow) angle off vertical (-90 = left-pointing)'
20185
+ describe: '(arrow) angle off of vertical (-90 = left-pointing)'
20153
20186
  })
20154
20187
  .option('head-angle', {
20155
20188
  old_alias: 'arrow-head-angle',
@@ -20184,17 +20217,12 @@ ${svg}
20184
20217
  })
20185
20218
  .option('min-stem-ratio', {
20186
20219
  old_alias: 'arrow-min-stem',
20187
- describe: '(arrow) min ratio of stem to total length',
20220
+ describe: '(arrow) minimum ratio of stem to total length',
20188
20221
  type: 'number'
20189
20222
  })
20190
20223
  .option('anchor', {
20191
20224
  describe: '(arrow) takes one of: start, middle, end (default is start)'
20192
20225
  })
20193
- .option('stroke', {})
20194
- .option('stroke-width', {})
20195
- .option('fill', {
20196
- describe: 'symbol fill color'
20197
- })
20198
20226
  .option('effect', {})
20199
20227
  // .option('where', whereOpt)
20200
20228
  .option('name', nameOpt)
@@ -20497,6 +20525,49 @@ ${svg}
20497
20525
  })
20498
20526
  .option('target', targetOpt);
20499
20527
 
20528
+ parser.section('Control flow commands');
20529
+
20530
+ var ifOpts = {
20531
+ expression: {
20532
+ DEFAULT: true,
20533
+ describe: 'JS expression targeting a single layer'
20534
+ },
20535
+ empty: {
20536
+ describe: 'run if layer is empty',
20537
+ type: 'flag'
20538
+ },
20539
+ 'not-empty': {
20540
+ describe: 'run if layer is not empty',
20541
+ type: 'flag'
20542
+ },
20543
+ layer: {
20544
+ describe: 'name or id of layer to test (default is current target)'
20545
+ },
20546
+ target: targetOpt
20547
+ };
20548
+
20549
+ parser.command('if')
20550
+ .describe('run the following commands if a condition is met')
20551
+ .options(ifOpts);
20552
+
20553
+ parser.command('elif')
20554
+ .describe('test an alternate condition; used after -if')
20555
+ .options(ifOpts);
20556
+
20557
+ parser.command('else')
20558
+ .describe('run commands if all preceding -if/-elif conditions are false');
20559
+
20560
+ parser.command('endif')
20561
+ .describe('mark the end of an -if sequence');
20562
+
20563
+ parser.command('ignore')
20564
+ // .describe('stop processing if a condition is met')
20565
+ .option('empty', {
20566
+ describe: 'ignore empty files',
20567
+ type: 'flag'
20568
+ })
20569
+ .option('target', targetOpt);
20570
+
20500
20571
  parser.section('Informational commands');
20501
20572
 
20502
20573
  parser.command('calc')
@@ -20540,6 +20611,10 @@ ${svg}
20540
20611
  .option('target', targetOpt)
20541
20612
  .validate(validateExpressionOpt);
20542
20613
 
20614
+ parser.command('print')
20615
+ .describe('print a message to stdout')
20616
+ .flag('multi_arg');
20617
+
20543
20618
  parser.command('projections')
20544
20619
  .describe('print list of supported projections');
20545
20620
 
@@ -34169,6 +34244,141 @@ ${svg}
34169
34244
  };
34170
34245
  }
34171
34246
 
34247
+ function resetControlFlow() {
34248
+ setStateVar('control', null);
34249
+ }
34250
+
34251
+ function inControlBlock() {
34252
+ var state = getState();
34253
+ return !!state.inControlBlock;
34254
+ }
34255
+
34256
+ function enterActiveBranch() {
34257
+ var state = getState();
34258
+ state.inControlBlock = true;
34259
+ state.active = true;
34260
+ state.complete = true;
34261
+ }
34262
+
34263
+ function enterInactiveBranch() {
34264
+ var state = getState();
34265
+ state.inControlBlock = true;
34266
+ state.active = false;
34267
+ }
34268
+
34269
+ function blockWasActive() {
34270
+ return !!getState().complete;
34271
+ }
34272
+
34273
+ function inActiveBranch() {
34274
+ return !!getState().active;
34275
+ }
34276
+
34277
+ function getState() {
34278
+ var o = getStateVar('control') || setStateVar('control', {}) || getStateVar('control');
34279
+ return o;
34280
+ }
34281
+
34282
+ function compileLayerExpression(expr, lyr, dataset, opts) {
34283
+ var proxy = getLayerProxy(lyr, dataset.arcs, opts);
34284
+ var exprOpts = Object.assign({returns: true}, opts);
34285
+ var func = compileExpressionToFunction(expr, exprOpts);
34286
+ var ctx = proxy;
34287
+ return function() {
34288
+ try {
34289
+ return func.call(proxy, {}, ctx);
34290
+ } catch(e) {
34291
+ // if (opts.quiet) throw e;
34292
+ stop(e.name, "in expression [" + expr + "]:", e.message);
34293
+ }
34294
+ };
34295
+ }
34296
+
34297
+ function skipCommand(cmdName) {
34298
+ // allow all control commands to run
34299
+ if (isControlFlowCommand(cmdName)) return false;
34300
+ return inControlBlock() && !inActiveBranch();
34301
+ }
34302
+
34303
+ cmd.if = function(catalog, opts) {
34304
+ if (inControlBlock()) {
34305
+ stop('Nested -if commands are not supported.');
34306
+ }
34307
+ evaluateIf(catalog, opts);
34308
+ };
34309
+
34310
+ cmd.elif = function(catalog, opts) {
34311
+ if (!inControlBlock()) {
34312
+ stop('-elif command must be preceded by an -if command.');
34313
+ }
34314
+ evaluateIf(catalog, opts);
34315
+ };
34316
+
34317
+ cmd.else = function() {
34318
+ if (!inControlBlock()) {
34319
+ stop('-else command must be preceded by an -if command.');
34320
+ }
34321
+ if (blockWasActive()) {
34322
+ enterInactiveBranch();
34323
+ } else {
34324
+ enterActiveBranch();
34325
+ }
34326
+ };
34327
+
34328
+ cmd.endif = function() {
34329
+ if (!inControlBlock()) {
34330
+ stop('-endif command must be preceded by an -if command.');
34331
+ }
34332
+ resetControlFlow();
34333
+ };
34334
+
34335
+ function isControlFlowCommand(cmd) {
34336
+ return ['if','elif','else','endif'].includes(cmd);
34337
+ }
34338
+
34339
+ function testLayer(catalog, opts) {
34340
+ var targ = getTargetLayer(catalog, opts);
34341
+ if (opts.expression) {
34342
+ return compileLayerExpression(opts.expression, targ.layer, targ.dataset, opts)();
34343
+ }
34344
+ if (opts.empty) {
34345
+ return layerIsEmpty(targ.layer);
34346
+ }
34347
+ if (opts.not_empty) {
34348
+ return !layerIsEmpty(targ.layer);
34349
+ }
34350
+ return true;
34351
+ }
34352
+
34353
+ function evaluateIf(catalog, opts) {
34354
+ if (!blockWasActive() && testLayer(catalog, opts)) {
34355
+ enterActiveBranch();
34356
+ } else {
34357
+ enterInactiveBranch();
34358
+ }
34359
+ }
34360
+
34361
+ // layerId: optional layer identifier
34362
+ //
34363
+ function getTargetLayer(catalog, opts) {
34364
+ var layerId = opts.layer || opts.target;
34365
+ var targets = catalog.findCommandTargets(layerId);
34366
+ if (targets.length === 0) {
34367
+ if (layerId) {
34368
+ stop('Layer not found:', layerId);
34369
+ } else {
34370
+ stop('Missing a target layer.');
34371
+ }
34372
+ }
34373
+ if (targets.length > 1 || targets[0].layers.length > 1) {
34374
+ stop('Command requires a single target layer.');
34375
+ }
34376
+ return {
34377
+ layer: targets[0].layers[0],
34378
+ dataset: targets[0].dataset
34379
+ };
34380
+ }
34381
+
34172
34382
  cmd.ignore = function(targetLayer, dataset, opts) {
34173
34383
  if (opts.empty && layerIsEmpty(targetLayer)) {
34174
34384
  interrupt('Layer is empty, stopping processing');
@@ -34715,6 +34925,28 @@ ${svg}
34715
34925
  return findVertexIds(p2.x, p2.y, arcs);
34716
34926
  }
34717
34927
 
34928
+ // Given a location @p (e.g. corresponding to the mouse pointer location),
34929
+ // find the midpoint of two vertices on @shp suitable for inserting a new vertex,
34930
+ // but only if:
34931
+ // 1. point @p is closer to the midpoint than either adjacent vertex
34932
+ // 2. the segment containing @p is longer than a minimum distance in pixels.
34933
+ //
34934
+ function findInsertionPoint(p, shp, arcs, pixelSize) {
34935
+ var p2 = findNearestVertex(p[0], p[1], shp, arcs);
34936
+
34937
+ }
34938
+
34939
+ function snapVerticesToPoint(ids, p, arcs, final) {
34940
+ ids.forEach(function(idx) {
34941
+ setVertexCoords(p[0], p[1], idx, arcs);
34942
+ });
34943
+ if (final) {
34944
+ // kludge to get dataset to recalculate internal bounding boxes
34945
+ arcs.transformPoints(function() {});
34946
+ }
34947
+ }
34948
+
34949
+
34718
34950
  // p: point to snap
34719
34951
  // ids: ids of nearby vertices, possibly including an arc endpoint
34720
34952
  function snapPointToArcEndpoint(p, ids, arcs) {
@@ -34801,6 +35033,8 @@ ${svg}
34801
35033
  var VertexUtils = /*#__PURE__*/Object.freeze({
34802
35034
  __proto__: null,
34803
35035
  findNearestVertices: findNearestVertices,
35036
+ findInsertionPoint: findInsertionPoint,
35037
+ snapVerticesToPoint: snapVerticesToPoint,
34804
35038
  snapPointToArcEndpoint: snapPointToArcEndpoint,
34805
35039
  findVertexIds: findVertexIds,
34806
35040
  getVertexCoords: getVertexCoords,
@@ -36416,6 +36650,10 @@ ${svg}
36416
36650
  };
36417
36651
  }
36418
36652
 
36653
+ cmd.print = function(msgArg) {
36654
+ print(msgArg || '');
36655
+ };
36656
+
36419
36657
  cmd.renameLayers = function(layers, names, catalog) {
36420
36658
  if (names && names.join('').indexOf('=') > -1) {
36421
36659
  renameByAssignment(names, catalog);
@@ -38088,7 +38326,8 @@ ${svg}
38088
38326
  }
38089
38327
 
38090
38328
  function calcArrowSize(d) {
38091
- var totalLen = d.radius || d.length || d.r || 0,
38329
+ // don't display arrows with negative length
38330
+ var totalLen = Math.max(d.radius || d.length || d.r || 0, 0),
38092
38331
  scale = 1,
38093
38332
  o = initArrowSize(d); // calc several parameters
38094
38333
 
@@ -38187,7 +38426,9 @@ ${svg}
38187
38426
  if (d.anchor == 'end') {
38188
38427
  scaleAndShiftCoords(coords, 1, [-dx, -dy - headLen]);
38189
38428
  } else if (d.anchor == 'middle') {
38190
- scaleAndShiftCoords(coords, 1, [-dx/2, (-dy - headLen)/2]);
38429
+ // shift midpoint away from the head a bit for a more balanced placement
38430
+ // scaleAndShiftCoords(coords, 1, [-dx/2, (-dy - headLen)/2]);
38431
+ scaleAndShiftCoords(coords, 1, [-dx * 0.5, -dy * 0.5 - headLen * 0.25]);
38191
38432
  }
38192
38433
 
38193
38434
  rotateCoords(coords, direction);
@@ -38864,8 +39105,13 @@ ${svg}
38864
39105
  targets,
38865
39106
  targetDataset,
38866
39107
  targetLayers,
39108
+ target,
38867
39109
  arcs;
38868
39110
 
39111
+ if (skipCommand(name)) {
39112
+ return done(null);
39113
+ }
39114
+
38869
39115
  try { // catch errors from synchronous functions
38870
39116
 
38871
39117
  T$1.start();
@@ -38915,7 +39161,7 @@ ${svg}
38915
39161
  }
38916
39162
  if (!(name == 'graticule' || name == 'i' || name == 'help' ||
38917
39163
  name == 'point-grid' || name == 'shape' || name == 'rectangle' ||
38918
- name == 'include')) {
39164
+ name == 'include' || name == 'print')) {
38919
39165
  throw new UserError("No data is available");
38920
39166
  }
38921
39167
  }
@@ -39032,6 +39278,14 @@ ${svg}
39032
39278
  outputLayers = targetDataset.layers; // kludge to allow layer naming below
39033
39279
  }
39034
39280
 
39281
+ } else if (name == 'if' || name == 'elif') {
39282
+ // target = findSingleTargetLayer(opts.layer, targets[0], catalog);
39283
+ // cmd[name](target.layer, target.dataset, opts);
39284
+ cmd[name](catalog, opts);
39285
+
39286
+ } else if (name == 'else' || name == 'endif') {
39287
+ cmd[name]();
39288
+
39035
39289
  } else if (name == 'ignore') {
39036
39290
  applyCommandToEachLayer(cmd.ignore, targetLayers, targetDataset, opts);
39037
39291
 
@@ -39092,6 +39346,9 @@ ${svg}
39092
39346
  } else if (name == 'polygons') {
39093
39347
  outputLayers = cmd.polygons(targetLayers, targetDataset, opts);
39094
39348
 
39349
+ } else if (name == 'print') {
39350
+ cmd.print(command._.join(' '));
39351
+
39095
39352
  } else if (name == 'proj') {
39096
39353
  initProjLibrary(opts, function() {
39097
39354
  var err = null;
@@ -39264,6 +39521,7 @@ ${svg}
39264
39521
  });
39265
39522
  }
39266
39523
 
39524
+
39267
39525
  // Apply a command to an array of target layers
39268
39526
  function applyCommandToEachLayer(func, targetLayers) {
39269
39527
  var args = utils.toArray(arguments).slice(2);
@@ -39522,6 +39780,8 @@ ${svg}
39522
39780
  }
39523
39781
 
39524
39782
  function runParsedCommands2(commands, catalog, cb) {
39783
+ // resetting closes any unterminated -if blocks from a previous command sequence
39784
+ resetControlFlow();
39525
39785
  utils.reduceAsync(commands, catalog, nextCommand, cb);
39526
39786
 
39527
39787
  function nextCommand(catalog, cmd, next) {
@@ -0,0 +1,22 @@
1
+
2
+ map:
3
+ mapshaper countyp010.shp name=counties \
4
+ -filter 'STATE_FIPS < 72' \
5
+ -filter 'COUNTY != ""' \
6
+ -dissolve FIPS copy-fields=STATE \
7
+ -simplify 2% \
8
+ -proj albersusa \
9
+ -points inner + name=points \
10
+ -i 2012-president-general-counties.csv string-fields=fips name=data \
11
+ -filter-fields fips,votes,obama,romney \
12
+ -each 'margin = obama - romney' \
13
+ -each 'absmargin = Math.abs(margin)' \
14
+ -join target=points data keys=FIPS,fips \
15
+ -sort absmargin descending \
16
+ -svg-style r='Math.sqrt(absmargin) * 0.02' \
17
+ -svg-style opacity=0.5 fill='margin > 0 ? "#0061aa" : "#cc0000"' \
18
+ -lines STATE target=counties \
19
+ -svg-style stroke="#ddd" where='TYPE == "inner"' \
20
+ -svg-style stroke="#777" where='TYPE == "outer"' \
21
+ -svg-style stroke="#999" where='TYPE == "STATE"' \
22
+ -o map.svg target=counties,points