mapshaper 0.5.85 → 0.5.89

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.83";
3
+ var VERSION = "0.5.88";
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
 
@@ -8222,10 +8228,21 @@
8222
8228
  return +n.toPrecision(d);
8223
8229
  }
8224
8230
 
8231
+
8225
8232
  function roundToDigits(n, d) {
8226
8233
  return +n.toFixed(d); // string conversion makes this slow
8227
8234
  }
8228
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
+
8229
8246
  function roundToTenths(n) {
8230
8247
  return (Math.round(n * 10)) / 10;
8231
8248
  }
@@ -8300,6 +8317,7 @@
8300
8317
  __proto__: null,
8301
8318
  roundToSignificantDigits: roundToSignificantDigits,
8302
8319
  roundToDigits: roundToDigits,
8320
+ roundToDigits2: roundToDigits2,
8303
8321
  roundToTenths: roundToTenths,
8304
8322
  getRoundingFunction: getRoundingFunction,
8305
8323
  getBoundsPrecisionForDisplay: getBoundsPrecisionForDisplay,
@@ -8938,15 +8956,15 @@
8938
8956
  });
8939
8957
  }
8940
8958
 
8941
- 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) {
8942
8966
  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
- },
8967
+ round: roundToDigits2,
8950
8968
  int_median: interpolated_median,
8951
8969
  sprintf: utils.format,
8952
8970
  blend: blend
@@ -9325,6 +9343,35 @@
9325
9343
  };
9326
9344
  }
9327
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
+ size: getFeatureCount(lyr),
9355
+ empty: getFeatureCount(lyr) === 0
9356
+ };
9357
+ addGetters(obj, getters);
9358
+ addBBoxGetter(obj, lyr, arcs);
9359
+ return obj;
9360
+ }
9361
+
9362
+ function addLayerGetters(ctx, lyr, arcs) {
9363
+ var layerProxy;
9364
+ addGetters(ctx, {
9365
+ layer_name: lyr.name || '', // consider removing this
9366
+ layer: function() {
9367
+ // init on first access (to avoid overhead if not used)
9368
+ if (!layerProxy) layerProxy = getLayerProxy(lyr, arcs);
9369
+ return layerProxy;
9370
+ }
9371
+ });
9372
+ return ctx;
9373
+ }
9374
+
9328
9375
  function addBBoxGetter(obj, lyr, arcs) {
9329
9376
  var bbox;
9330
9377
  addGetters(obj, {
@@ -9354,32 +9401,6 @@
9354
9401
  return bbox;
9355
9402
  }
9356
9403
 
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
9404
  // Returns a function to return a feature proxy by id
9384
9405
  // (the proxy appears as "this" or "$" in a feature expression)
9385
9406
  function initFeatureProxy(lyr, arcs, optsArg) {
@@ -9576,11 +9597,6 @@
9576
9597
  return compileFeatureExpression(exp, lyr, arcs, opts);
9577
9598
  }
9578
9599
 
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
9600
 
9585
9601
  function compileFeaturePairFilterExpression(exp, lyr, arcs) {
9586
9602
  var func = compileFeaturePairExpression(exp, lyr, arcs);
@@ -9716,14 +9732,19 @@
9716
9732
 
9717
9733
  function compileExpressionToFunction(exp, opts) {
9718
9734
  // $$ 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;
9735
+ var functionBody, func;
9736
+ if (opts.returns) {
9737
+ // functionBody = 'return ' + functionBody;
9738
+ functionBody = 'var $$retn = ' + exp + '; return $$retn;';
9739
+ } else {
9740
+ functionBody = exp;
9741
+ }
9742
+ functionBody = 'with($$env){with($$record){ ' + functionBody + '}}';
9722
9743
  try {
9723
- func = new Function("$$record,$$env", functionBody);
9744
+ func = new Function('$$record,$$env', functionBody);
9724
9745
  } catch(e) {
9725
9746
  // if (opts.quiet) throw e;
9726
- stop(e.name, "in expression [" + exp + "]");
9747
+ stop(e.name, 'in expression [' + exp + ']');
9727
9748
  }
9728
9749
  return func;
9729
9750
  }
@@ -9766,7 +9787,7 @@
9766
9787
  var ctx = {};
9767
9788
  var fields = lyr.data ? lyr.data.getFields() : [];
9768
9789
  opts = opts || {};
9769
- addUtils(env); // mix in round(), sprintf(), etc.
9790
+ addFeatureExpressionUtils(env); // mix in round(), sprintf(), etc.
9770
9791
  if (fields.length > 0) {
9771
9792
  // default to null values, so assignments to missing data properties
9772
9793
  // are applied to the data record, not the global object
@@ -9821,7 +9842,6 @@
9821
9842
  var Expressions = /*#__PURE__*/Object.freeze({
9822
9843
  __proto__: null,
9823
9844
  compileValueExpression: compileValueExpression,
9824
- cleanExpression: cleanExpression,
9825
9845
  compileFeaturePairFilterExpression: compileFeaturePairFilterExpression,
9826
9846
  compileFeaturePairExpression: compileFeaturePairExpression,
9827
9847
  compileFeatureExpression: compileFeatureExpression,
@@ -18564,6 +18584,13 @@ ${svg}
18564
18584
  return this;
18565
18585
  };
18566
18586
 
18587
+ this.options = function(o) {
18588
+ Object.keys(o).forEach(function(key) {
18589
+ this.option(key, o[key]);
18590
+ }, this);
18591
+ return this;
18592
+ };
18593
+
18567
18594
  this.done = function() {
18568
18595
  return _command;
18569
18596
  };
@@ -19527,14 +19554,6 @@ ${svg}
19527
19554
  .option('target', targetOpt)
19528
19555
  .option('no-replace', noReplaceOpt);
19529
19556
 
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
19557
  parser.command('include')
19539
19558
  .describe('import JS data and functions for use in JS expressions')
19540
19559
  .option('file', {
@@ -20093,45 +20112,46 @@ ${svg}
20093
20112
  .option('target', targetOpt);
20094
20113
 
20095
20114
  parser.command('symbols')
20096
- // .describe('symbolize points as polygons, circles, stars or arrows')
20115
+ .describe('symbolize points as arrows, circles, stars, polygons, etc.')
20097
20116
  .option('type', {
20098
- describe: 'symbol type (e.g. star, polygon, circle, arrow)'
20117
+ describe: 'symbol type (e.g. arrow, circle, square, star, polygon, ring)'
20099
20118
  })
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',
20119
+ .option('stroke', {})
20120
+ .option('stroke-width', {})
20121
+ .option('fill', {
20122
+ describe: 'symbol fill color'
20107
20123
  })
20108
20124
  .option('polygons', {
20109
20125
  describe: 'generate symbols as polygons instead of SVG objects',
20110
20126
  type: 'flag'
20111
20127
  })
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'
20123
- })
20124
- .option('flipped', {
20125
- type: 'flag',
20126
- describe: 'symbol is vertically flipped'
20128
+ .option('pixel-scale', {
20129
+ describe: 'set symbol scale in meters-per-pixel (for polygons option)',
20130
+ type: 'number',
20127
20131
  })
20132
+ // .option('flipped', {
20133
+ // type: 'flag',
20134
+ // describe: 'symbol is vertically flipped'
20135
+ // })
20128
20136
  .option('rotated', {
20129
20137
  type: 'flag',
20130
- describe: 'symbol is rotated to a different orientation'
20138
+ describe: 'symbol is rotated to an alternate orientation'
20131
20139
  })
20132
20140
  .option('rotation', {
20133
20141
  describe: 'rotation of symbol in degrees'
20134
20142
  })
20143
+ .option('scale', {
20144
+ describe: 'scale symbols by a multiplier',
20145
+ type: 'number'
20146
+ })
20147
+ .option('radius', {
20148
+ describe: 'distance from center to farthest point on the symbol',
20149
+ type: 'distance'
20150
+ })
20151
+ .option('sides', {
20152
+ describe: '(polygon) number of sides of a (regular) polygon symbol',
20153
+ type: 'number'
20154
+ })
20135
20155
  .option('points', {
20136
20156
  describe: '(star) number of points'
20137
20157
  })
@@ -20149,7 +20169,7 @@ ${svg}
20149
20169
  })
20150
20170
  .option('direction', {
20151
20171
  old_alias: 'arrow-direction',
20152
- describe: '(arrow) angle off vertical (-90 = left-pointing)'
20172
+ describe: '(arrow) angle off of vertical (-90 = left-pointing)'
20153
20173
  })
20154
20174
  .option('head-angle', {
20155
20175
  old_alias: 'arrow-head-angle',
@@ -20184,17 +20204,12 @@ ${svg}
20184
20204
  })
20185
20205
  .option('min-stem-ratio', {
20186
20206
  old_alias: 'arrow-min-stem',
20187
- describe: '(arrow) min ratio of stem to total length',
20207
+ describe: '(arrow) minimum ratio of stem to total length',
20188
20208
  type: 'number'
20189
20209
  })
20190
20210
  .option('anchor', {
20191
20211
  describe: '(arrow) takes one of: start, middle, end (default is start)'
20192
20212
  })
20193
- .option('stroke', {})
20194
- .option('stroke-width', {})
20195
- .option('fill', {
20196
- describe: 'symbol fill color'
20197
- })
20198
20213
  .option('effect', {})
20199
20214
  // .option('where', whereOpt)
20200
20215
  .option('name', nameOpt)
@@ -20497,6 +20512,49 @@ ${svg}
20497
20512
  })
20498
20513
  .option('target', targetOpt);
20499
20514
 
20515
+ parser.section('Control flow commands');
20516
+
20517
+ var ifOpts = {
20518
+ expression: {
20519
+ DEFAULT: true,
20520
+ describe: 'JS expression targeting a single layer'
20521
+ },
20522
+ empty: {
20523
+ describe: 'run if layer is empty',
20524
+ type: 'flag'
20525
+ },
20526
+ 'not-empty': {
20527
+ describe: 'run if layer is not empty',
20528
+ type: 'flag'
20529
+ },
20530
+ layer: {
20531
+ describe: 'name or id of layer to test (default is current target)'
20532
+ },
20533
+ target: targetOpt
20534
+ };
20535
+
20536
+ parser.command('if')
20537
+ .describe('run the following commands if a condition is met')
20538
+ .options(ifOpts);
20539
+
20540
+ parser.command('elif')
20541
+ .describe('test an alternate condition; used after -if')
20542
+ .options(ifOpts);
20543
+
20544
+ parser.command('else')
20545
+ .describe('run commands if all preceding -if/-elif conditions are false');
20546
+
20547
+ parser.command('endif')
20548
+ .describe('mark the end of an -if sequence');
20549
+
20550
+ parser.command('ignore')
20551
+ // .describe('stop processing if a condition is met')
20552
+ .option('empty', {
20553
+ describe: 'ignore empty files',
20554
+ type: 'flag'
20555
+ })
20556
+ .option('target', targetOpt);
20557
+
20500
20558
  parser.section('Informational commands');
20501
20559
 
20502
20560
  parser.command('calc')
@@ -34169,6 +34227,141 @@ ${svg}
34169
34227
  };
34170
34228
  }
34171
34229
 
34230
+ function resetControlFlow() {
34231
+ setStateVar('control', null);
34232
+ }
34233
+
34234
+ function inControlBlock() {
34235
+ var state = getState();
34236
+ return !!state.inControlBlock;
34237
+ }
34238
+
34239
+ function enterActiveBranch() {
34240
+ var state = getState();
34241
+ state.inControlBlock = true;
34242
+ state.active = true;
34243
+ state.complete = true;
34244
+ }
34245
+
34246
+ function enterInactiveBranch() {
34247
+ var state = getState();
34248
+ state.inControlBlock = true;
34249
+ state.active = false;
34250
+ }
34251
+
34252
+ function blockWasActive() {
34253
+ return !!getState().complete;
34254
+ }
34255
+
34256
+ function inActiveBranch() {
34257
+ return !!getState().active;
34258
+ }
34259
+
34260
+ function getState() {
34261
+ var o = getStateVar('control') || setStateVar('control', {}) || getStateVar('control');
34262
+ return o;
34263
+ }
34264
+
34265
+ function compileLayerExpression(expr, lyr, dataset, opts) {
34266
+ var proxy = getLayerProxy(lyr, dataset.arcs, opts);
34267
+ var exprOpts = Object.assign({returns: true}, opts);
34268
+ var func = compileExpressionToFunction(expr, exprOpts);
34269
+ var ctx = proxy;
34270
+ return function() {
34271
+ try {
34272
+ return func.call(proxy, {}, ctx);
34273
+ } catch(e) {
34274
+ // if (opts.quiet) throw e;
34275
+ stop(e.name, "in expression [" + expr + "]:", e.message);
34276
+ }
34277
+ };
34278
+ }
34279
+
34280
+ function skipCommand(cmdName) {
34281
+ // allow all control commands to run
34282
+ if (isControlFlowCommand(cmdName)) return false;
34283
+ return inControlBlock() && !inActiveBranch();
34284
+ }
34285
+
34286
+ cmd.if = function(catalog, opts) {
34287
+ if (inControlBlock()) {
34288
+ stop('Nested -if commands are not supported.');
34289
+ }
34290
+ evaluateIf(catalog, opts);
34291
+ };
34292
+
34293
+ cmd.elif = function(catalog, opts) {
34294
+ if (!inControlBlock()) {
34295
+ stop('-elif command must be preceded by an -if command.');
34296
+ }
34297
+ evaluateIf(catalog, opts);
34298
+ };
34299
+
34300
+ cmd.else = function() {
34301
+ if (!inControlBlock()) {
34302
+ stop('-else command must be preceded by an -if command.');
34303
+ }
34304
+ if (blockWasActive()) {
34305
+ enterInactiveBranch();
34306
+ } else {
34307
+ enterActiveBranch();
34308
+ }
34309
+ };
34310
+
34311
+ cmd.endif = function() {
34312
+ if (!inControlBlock()) {
34313
+ stop('-endif command must be preceded by an -if command.');
34314
+ }
34315
+ resetControlFlow();
34316
+ };
34317
+
34318
+ function isControlFlowCommand(cmd) {
34319
+ return ['if','elif','else','endif'].includes(cmd);
34320
+ }
34321
+
34322
+ function testLayer(catalog, opts) {
34323
+ var targ = getTargetLayer(catalog, opts);
34324
+ if (opts.expression) {
34325
+ return compileLayerExpression(opts.expression, targ.layer, targ.dataset, opts)();
34326
+ }
34327
+ if (opts.empty) {
34328
+ return layerIsEmpty(targ.layer);
34329
+ }
34330
+ if (opts.not_empty) {
34331
+ return !layerIsEmpty(targ.layer);
34332
+ }
34333
+ return true;
34334
+ }
34335
+
34336
+ function evaluateIf(catalog, opts) {
34337
+ if (!blockWasActive() && testLayer(catalog, opts)) {
34338
+ enterActiveBranch();
34339
+ } else {
34340
+ enterInactiveBranch();
34341
+ }
34342
+ }
34343
+
34344
+ // layerId: optional layer identifier
34345
+ //
34346
+ function getTargetLayer(catalog, opts) {
34347
+ var layerId = opts.layer || opts.target;
34348
+ var targets = catalog.findCommandTargets(layerId);
34349
+ if (targets.length === 0) {
34350
+ if (layerId) {
34351
+ stop('Layer not found:', layerId);
34352
+ } else {
34353
+ stop('Missing a target layer.');
34354
+ }
34355
+ }
34356
+ if (targets.length > 1 || targets[0].layers.length > 1) {
34357
+ stop('Command requires a single target layer.');
34358
+ }
34359
+ return {
34360
+ layer: targets[0].layers[0],
34361
+ dataset: targets[0].dataset
34362
+ };
34363
+ }
34364
+
34172
34365
  cmd.ignore = function(targetLayer, dataset, opts) {
34173
34366
  if (opts.empty && layerIsEmpty(targetLayer)) {
34174
34367
  interrupt('Layer is empty, stopping processing');
@@ -34715,6 +34908,28 @@ ${svg}
34715
34908
  return findVertexIds(p2.x, p2.y, arcs);
34716
34909
  }
34717
34910
 
34911
+ // Given a location @p (e.g. corresponding to the mouse pointer location),
34912
+ // find the midpoint of two vertices on @shp suitable for inserting a new vertex,
34913
+ // but only if:
34914
+ // 1. point @p is closer to the midpoint than either adjacent vertex
34915
+ // 2. the segment containing @p is longer than a minimum distance in pixels.
34916
+ //
34917
+ function findInsertionPoint(p, shp, arcs, pixelSize) {
34918
+ var p2 = findNearestVertex(p[0], p[1], shp, arcs);
34919
+
34920
+ }
34921
+
34922
+ function snapVerticesToPoint(ids, p, arcs, final) {
34923
+ ids.forEach(function(idx) {
34924
+ setVertexCoords(p[0], p[1], idx, arcs);
34925
+ });
34926
+ if (final) {
34927
+ // kludge to get dataset to recalculate internal bounding boxes
34928
+ arcs.transformPoints(function() {});
34929
+ }
34930
+ }
34931
+
34932
+
34718
34933
  // p: point to snap
34719
34934
  // ids: ids of nearby vertices, possibly including an arc endpoint
34720
34935
  function snapPointToArcEndpoint(p, ids, arcs) {
@@ -34801,6 +35016,8 @@ ${svg}
34801
35016
  var VertexUtils = /*#__PURE__*/Object.freeze({
34802
35017
  __proto__: null,
34803
35018
  findNearestVertices: findNearestVertices,
35019
+ findInsertionPoint: findInsertionPoint,
35020
+ snapVerticesToPoint: snapVerticesToPoint,
34804
35021
  snapPointToArcEndpoint: snapPointToArcEndpoint,
34805
35022
  findVertexIds: findVertexIds,
34806
35023
  getVertexCoords: getVertexCoords,
@@ -38088,7 +38305,8 @@ ${svg}
38088
38305
  }
38089
38306
 
38090
38307
  function calcArrowSize(d) {
38091
- var totalLen = d.radius || d.length || d.r || 0,
38308
+ // don't display arrows with negative length
38309
+ var totalLen = Math.max(d.radius || d.length || d.r || 0, 0),
38092
38310
  scale = 1,
38093
38311
  o = initArrowSize(d); // calc several parameters
38094
38312
 
@@ -38187,7 +38405,9 @@ ${svg}
38187
38405
  if (d.anchor == 'end') {
38188
38406
  scaleAndShiftCoords(coords, 1, [-dx, -dy - headLen]);
38189
38407
  } else if (d.anchor == 'middle') {
38190
- scaleAndShiftCoords(coords, 1, [-dx/2, (-dy - headLen)/2]);
38408
+ // shift midpoint away from the head a bit for a more balanced placement
38409
+ // scaleAndShiftCoords(coords, 1, [-dx/2, (-dy - headLen)/2]);
38410
+ scaleAndShiftCoords(coords, 1, [-dx * 0.5, -dy * 0.5 - headLen * 0.25]);
38191
38411
  }
38192
38412
 
38193
38413
  rotateCoords(coords, direction);
@@ -38231,71 +38451,31 @@ ${svg}
38231
38451
  return coords;
38232
38452
  }
38233
38453
 
38234
- function getMinorRadius(points) {
38235
- var innerAngle = 360 / points;
38236
- var pointAngle = getDefaultPointAngle(points);
38237
- var thetaA = Math.PI / 180 * innerAngle / 2;
38238
- var thetaB = Math.PI / 180 * pointAngle / 2;
38239
- var a = Math.tan(thetaB) / (Math.tan(thetaB) + Math.tan(thetaA));
38240
- var c = a / Math.cos(thetaA);
38241
- return c;
38242
- }
38243
-
38244
-
38245
- function getPointAngle(points, skip) {
38246
- var unitAngle = 360 / points;
38247
- var centerAngle = unitAngle * (skip + 1);
38248
- return 180 - centerAngle;
38249
- }
38250
-
38251
- function getDefaultPointAngle(points) {
38252
- var minSkip = 1;
38253
- var maxSkip = Math.ceil(points / 2) - 2;
38254
- var skip = Math.floor((maxSkip + minSkip) / 2);
38255
- return getPointAngle(points, skip);
38256
- }
38257
-
38258
- // sides: e.g. 5-pointed star has 10 sides
38259
- // radius: distance from center to point
38260
- //
38261
38454
  function getPolygonCoords(d) {
38262
- var radius = d.radius || d.length || d.r;
38455
+ var radius = d.radius || d.length || d.r,
38456
+ sides = +d.sides || getSidesByType(d.type),
38457
+ rotated = sides % 2 == 1,
38458
+ coords = [],
38459
+ angle, b;
38460
+
38263
38461
  if (radius > 0 === false) return null;
38264
- var type = d.type;
38265
- var sides = +d.sides || getDefaultSides(type);
38266
- var isStar = type == 'star';
38267
- if (isStar && d.points > 0) {
38268
- sides = d.points * 2;
38269
- }
38270
- var starRatio = isStar ? d.star_ratio || getMinorRadius(sides / 2) : 0;
38271
- if (isStar && (sides < 10 || sides % 2 !== 0)) {
38272
- stop(`Invalid number of points for a star (${sides / 2})`);
38273
- } else if (sides >= 3 === false) {
38462
+ if (sides >= 3 === false) {
38274
38463
  stop(`Invalid number of sides (${sides})`);
38275
38464
  }
38276
- var coords = [],
38277
- angle = 360 / sides,
38278
- b = isStar ? 1 : 0.5,
38279
- theta, even, len;
38280
38465
  if (d.orientation == 'b' || d.flipped || d.rotated) {
38281
- b = 0;
38466
+ rotated = !rotated;
38282
38467
  }
38468
+ b = rotated ? 0 : 0.5;
38283
38469
  for (var i=0; i<sides; i++) {
38284
- even = i % 2 == 0;
38285
- len = radius;
38286
- if (isStar && even) {
38287
- len *= starRatio;
38288
- }
38289
- theta = (i + b) * angle % 360;
38290
- coords.push(getPlanarSegmentEndpoint(0, 0, theta, len));
38470
+ angle = (i + b) / sides * 360;
38471
+ coords.push(getPlanarSegmentEndpoint(0, 0, angle, radius));
38291
38472
  }
38292
38473
  coords.push(coords[0].concat());
38293
38474
  return [coords];
38294
38475
  }
38295
38476
 
38296
- function getDefaultSides(type) {
38477
+ function getSidesByType(type) {
38297
38478
  return {
38298
- star: 10,
38299
38479
  circle: 72,
38300
38480
  triangle: 3,
38301
38481
  square: 4,
@@ -38308,6 +38488,52 @@ ${svg}
38308
38488
  }[type] || 4;
38309
38489
  }
38310
38490
 
38491
+ function getStarCoords(d) {
38492
+ var radius = d.radius || d.length || d.r,
38493
+ points = d.points || d.sides && d.sides / 2 || 5,
38494
+ sides = points * 2,
38495
+ minorRadius = getMinorRadius(points) * radius,
38496
+ b = d.orientation == 'b' || d.flipped || d.rotated ? 0 : 1,
38497
+ coords = [],
38498
+ angle, len;
38499
+
38500
+ if (radius > 0 === false) return null;
38501
+ if (points < 5) {
38502
+ stop(`Invalid number of points for a star (${points})`);
38503
+ }
38504
+ for (var i=0; i<sides; i++) {
38505
+ len = i % 2 == 0 ? minorRadius : radius;
38506
+ angle = (i + b) / sides * 360;
38507
+ coords.push(getPlanarSegmentEndpoint(0, 0, angle, len));
38508
+ }
38509
+ coords.push(coords[0].concat());
38510
+ return [coords];
38511
+ }
38512
+
38513
+ function getMinorRadius(points) {
38514
+ var innerAngle = 360 / points;
38515
+ var pointAngle = getDefaultPointAngle(points);
38516
+ var thetaA = Math.PI / 180 * innerAngle / 2;
38517
+ var thetaB = Math.PI / 180 * pointAngle / 2;
38518
+ var a = Math.tan(thetaB) / (Math.tan(thetaB) + Math.tan(thetaA));
38519
+ var c = a / Math.cos(thetaA);
38520
+ return c;
38521
+ }
38522
+
38523
+ function getDefaultPointAngle(points) {
38524
+ var minSkip = 1;
38525
+ var maxSkip = Math.ceil(points / 2) - 2;
38526
+ var skip = Math.floor((maxSkip + minSkip) / 2);
38527
+ return getPointAngle(points, skip);
38528
+ }
38529
+
38530
+ // skip: number of adjacent points to skip when drawing a segment
38531
+ function getPointAngle(points, skip) {
38532
+ var unitAngle = 360 / points;
38533
+ var centerAngle = unitAngle * (skip + 1);
38534
+ return 180 - centerAngle;
38535
+ }
38536
+
38311
38537
  // Returns GeoJSON MultiPolygon coords
38312
38538
  function getRingCoords(d) {
38313
38539
  var radii = parseRings(d.radii || '2');
@@ -38362,6 +38588,8 @@ ${svg}
38362
38588
  } else if (d.type == 'ring') {
38363
38589
  coords = getRingCoords(d);
38364
38590
  geojsonType = 'MultiPolygon';
38591
+ } else if (d.type == 'star') {
38592
+ coords = getStarCoords(d);
38365
38593
  } else {
38366
38594
  coords = getPolygonCoords(d);
38367
38595
  }
@@ -38856,8 +39084,13 @@ ${svg}
38856
39084
  targets,
38857
39085
  targetDataset,
38858
39086
  targetLayers,
39087
+ target,
38859
39088
  arcs;
38860
39089
 
39090
+ if (skipCommand(name)) {
39091
+ return done(null);
39092
+ }
39093
+
38861
39094
  try { // catch errors from synchronous functions
38862
39095
 
38863
39096
  T$1.start();
@@ -39024,6 +39257,14 @@ ${svg}
39024
39257
  outputLayers = targetDataset.layers; // kludge to allow layer naming below
39025
39258
  }
39026
39259
 
39260
+ } else if (name == 'if' || name == 'elif') {
39261
+ // target = findSingleTargetLayer(opts.layer, targets[0], catalog);
39262
+ // cmd[name](target.layer, target.dataset, opts);
39263
+ cmd[name](catalog, opts);
39264
+
39265
+ } else if (name == 'else' || name == 'endif') {
39266
+ cmd[name]();
39267
+
39027
39268
  } else if (name == 'ignore') {
39028
39269
  applyCommandToEachLayer(cmd.ignore, targetLayers, targetDataset, opts);
39029
39270
 
@@ -39256,6 +39497,7 @@ ${svg}
39256
39497
  });
39257
39498
  }
39258
39499
 
39500
+
39259
39501
  // Apply a command to an array of target layers
39260
39502
  function applyCommandToEachLayer(func, targetLayers) {
39261
39503
  var args = utils.toArray(arguments).slice(2);
@@ -39514,6 +39756,8 @@ ${svg}
39514
39756
  }
39515
39757
 
39516
39758
  function runParsedCommands2(commands, catalog, cb) {
39759
+ // resetting closes any unterminated -if blocks from a previous command sequence
39760
+ resetControlFlow();
39517
39761
  utils.reduceAsync(commands, catalog, nextCommand, cb);
39518
39762
 
39519
39763
  function nextCommand(catalog, cmd, next) {