mapshaper 0.6.0 → 0.6.4

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.0";
3
+ var VERSION = "0.6.4";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -81,6 +81,7 @@
81
81
  get findValueByPct () { return findValueByPct; },
82
82
  get findValueByRank () { return findValueByRank; },
83
83
  get findMedian () { return findMedian; },
84
+ get findQuantile () { return findQuantile; },
84
85
  get mean () { return mean; },
85
86
  get format () { return format; },
86
87
  get formatter () { return formatter; },
@@ -258,7 +259,7 @@
258
259
  '/': '/'
259
260
  };
260
261
  function htmlEscape(s) {
261
- return String(s).replace(/[&<>"'\/]/g, function(s) {
262
+ return String(s).replace(/[&<>"'/]/g, function(s) {
262
263
  return entityMap[s];
263
264
  });
264
265
  }
@@ -832,18 +833,24 @@
832
833
  return arr[k];
833
834
  }
834
835
 
835
- //
836
- //
837
836
  function findMedian(arr) {
838
- var n = arr.length,
839
- rank = Math.floor(n / 2) + 1,
840
- median = findValueByRank(arr, rank);
841
- if ((n & 1) == 0) {
842
- median = (median + findValueByRank(arr, rank - 1)) / 2;
843
- }
844
- return median;
837
+ return findQuantile(arr, 0.5);
845
838
  }
846
839
 
840
+ function findQuantile(arr, k) {
841
+ var n = arr.length,
842
+ i1 = Math.floor((n - 1) * k),
843
+ i2 = Math.ceil((n - 1) * k);
844
+ if (i1 < 0 || i2 >= n) return NaN;
845
+ var v1 = findValueByRank(arr, i1 + 1);
846
+ if (i1 == i2) return v1;
847
+ var v2 = findValueByRank(arr, i2 + 1);
848
+ // use linear interpolation
849
+ var w1 = i2 / (n - 1) - k;
850
+ var w2 = k - i1 / (n - 1);
851
+ var v = (v1 * w1 + v2 * w2) * (n - 1);
852
+ return v;
853
+ }
847
854
 
848
855
  function mean(arr) {
849
856
  var count = 0,
@@ -901,8 +908,8 @@
901
908
  var type = matches[4];
902
909
  var isString = type == 's',
903
910
  isHex = type == 'x' || type == 'X',
904
- isInt = type == 'd' || type == 'i',
905
- isFloat = type == 'f',
911
+ // isInt = type == 'd' || type == 'i',
912
+ // isFloat = type == 'f',
906
913
  isNumber = !isString;
907
914
 
908
915
  var sign = "",
@@ -961,7 +968,7 @@
961
968
 
962
969
  // Get a function for interpolating formatted values into a string.
963
970
  function formatter(fmt) {
964
- var codeRxp = /%([\',+0]*)([1-9]?)((?:\.[1-9])?)([sdifxX%])/g;
971
+ var codeRxp = /%([',+0]*)([1-9]?)((?:\.[1-9])?)([sdifxX%])/g;
965
972
  var literals = [],
966
973
  formatCodes = [],
967
974
  startIdx = 0,
@@ -3522,7 +3529,7 @@
3522
3529
  if (typeof require == 'function') {
3523
3530
  f = require;
3524
3531
  } else {
3525
- f = function(name) {
3532
+ f = function() {
3526
3533
  // console.error('Unable to load module', name);
3527
3534
  };
3528
3535
  }
@@ -6479,12 +6486,12 @@
6479
6486
  // Accessor function for arcs
6480
6487
  Object.defineProperty(this, 'arcs', {value: arcs});
6481
6488
 
6482
- var toArray = this.toArray = function() {
6489
+ this.toArray = function() {
6483
6490
  var chains = getNodeChains(),
6484
6491
  flags = new Uint8Array(chains.length),
6485
6492
  arr = [];
6486
6493
  utils.forEach(chains, function(nextIdx, thisIdx) {
6487
- var node, x, y, p;
6494
+ var node, p;
6488
6495
  if (flags[thisIdx] == 1) return;
6489
6496
  p = getEndpoint(thisIdx);
6490
6497
  if (!p) return; // endpoints of an excluded arc
@@ -10342,7 +10349,13 @@
10342
10349
  sum: captureNum,
10343
10350
  sums: capture,
10344
10351
  average: captureNum,
10352
+ mean: captureNum,
10345
10353
  median: captureNum,
10354
+ quantile: captureNum,
10355
+ iqr: captureNum,
10356
+ quartile1: captureNum,
10357
+ quartile2: captureNum,
10358
+ quartile3: captureNum,
10346
10359
  min: captureNum,
10347
10360
  max: captureNum,
10348
10361
  mode: capture,
@@ -10357,9 +10370,17 @@
10357
10370
  sum: wrap(utils.sum, 0),
10358
10371
  sums: wrap(sums),
10359
10372
  median: wrap(utils.findMedian),
10373
+ quantile: wrap2(utils.findQuantile),
10374
+ iqr: wrap(function(arr) {
10375
+ return utils.findQuantile(arr, 0.75) - utils.findQuantile(arr, 0.25);
10376
+ }),
10377
+ quartile1: wrap(function(arr) { return utils.findQuantile(arr, 0.25); }),
10378
+ quartile2: wrap(utils.findMedian),
10379
+ quartile3: wrap(function(arr) { return utils.findQuantile(arr, 0.75); }),
10360
10380
  min: wrap(min),
10361
10381
  max: wrap(max),
10362
10382
  average: wrap(utils.mean),
10383
+ mean: wrap(utils.mean),
10363
10384
  mode: wrap(getMode),
10364
10385
  collect: wrap(pass),
10365
10386
  first: wrap(pass),
@@ -10429,6 +10450,13 @@
10429
10450
  };
10430
10451
  }
10431
10452
 
10453
+ function wrap2(proc) {
10454
+ return function(arg1, arg2) {
10455
+ var c = colNo++;
10456
+ return rowNo > 0 ? proc(cols[c], arg2) : null;
10457
+ };
10458
+ }
10459
+
10432
10460
  function procAll() {
10433
10461
  for (var i=0; i<len; i++) {
10434
10462
  procRecord(i);
@@ -12059,10 +12087,6 @@
12059
12087
  // if point x,y falls on an endpoint
12060
12088
  // Assumes: i <= j
12061
12089
  function getCutPoint(x, y, i, j, xx, yy) {
12062
- var ix = xx[i],
12063
- iy = yy[i],
12064
- jx = xx[j],
12065
- jy = yy[j];
12066
12090
  if (j < i || j > i + 1) {
12067
12091
  error("Out-of-sequence arc ids:", i, j);
12068
12092
  }
@@ -14048,18 +14072,18 @@
14048
14072
  // accept variations on type names (dot, dots, square, squares, hatch, hatches, hatched)
14049
14073
  if (first.startsWith('dot')) {
14050
14074
  parts[0] = 'dots';
14051
- obj = parseDots(parts, str);
14075
+ obj = parseDots(parts);
14052
14076
  } else if (first.startsWith('square')) {
14053
14077
  parts[0] = 'squares';
14054
- obj = parseDots(parts, str);
14078
+ obj = parseDots(parts);
14055
14079
  } else if (first.startsWith('hatch')) {
14056
14080
  parts[0] = 'hatches';
14057
- obj = parseHatches(parts, str);
14081
+ obj = parseHatches(parts);
14058
14082
  } else if (first.startsWith('dash')) {
14059
- obj = parseDashes(parts, str);
14083
+ obj = parseDashes(parts);
14060
14084
  } else if (!isNaN(parseFloat(first))) {
14061
14085
  parts.unshift('hatches');
14062
- obj = parseHatches(parts, str); // hatches is the default, name can be omitted
14086
+ obj = parseHatches(parts); // hatches is the default, name can be omitted
14063
14087
  }
14064
14088
  if (!obj) {
14065
14089
  // consider
@@ -14068,7 +14092,7 @@
14068
14092
  return obj;
14069
14093
  }
14070
14094
 
14071
- function parseDashes(parts, str) {
14095
+ function parseDashes(parts) {
14072
14096
  // format:
14073
14097
  // "dashes" dash-len dash-space width color1 [color2...] space bg-color
14074
14098
  // examples:
@@ -14109,7 +14133,7 @@
14109
14133
  };
14110
14134
  }
14111
14135
 
14112
- function parseHatches(parts, str) {
14136
+ function parseHatches(parts) {
14113
14137
  // format:
14114
14138
  // [hatches] [rotation] width1 color1 [width2 color2 ...]
14115
14139
  // examples:
@@ -14136,7 +14160,7 @@
14136
14160
  return parseInt(str) > 0;
14137
14161
  }
14138
14162
 
14139
- function parseDots(parts, str) {
14163
+ function parseDots(parts) {
14140
14164
  // format:
14141
14165
  // "dots"|"squares" [rotation] size color1 [color2 ...] spacing bg-color
14142
14166
  // examples:
@@ -14300,6 +14324,7 @@
14300
14324
  'stroke-opacity': 'number',
14301
14325
  'stroke-miterlimit': 'number',
14302
14326
  'fill-opacity': 'number',
14327
+ 'vector-effect': null,
14303
14328
  'text-anchor': null
14304
14329
  };
14305
14330
 
@@ -14329,7 +14354,7 @@
14329
14354
  effect: null // e.g. "fade"
14330
14355
  }, stylePropertyTypes);
14331
14356
 
14332
- var commonProperties = 'css,class,opacity,stroke,stroke-width,stroke-dasharray,stroke-opacity,fill-opacity'.split(',');
14357
+ var commonProperties = 'css,class,opacity,stroke,stroke-width,stroke-dasharray,stroke-opacity,fill-opacity,vector-effect'.split(',');
14333
14358
 
14334
14359
  var propertiesBySymbolType = {
14335
14360
  polygon: utils.arrayToIndex(commonProperties.concat('fill', 'fill-pattern')),
@@ -14409,7 +14434,7 @@
14409
14434
  function mightBeExpression(str, fields) {
14410
14435
  fields = fields || [];
14411
14436
  if (fields.indexOf(str.trim()) > -1) return true;
14412
- return /[(){}./*?:&|=\[+-]/.test(str);
14437
+ return /[(){}./*?:&|=[+-]/.test(str);
14413
14438
  }
14414
14439
 
14415
14440
  function getSymbolPropertyAccessor(val, svgName, lyr) {
@@ -14536,7 +14561,7 @@
14536
14561
 
14537
14562
  function importGeoJSONFeatures(features, opts) {
14538
14563
  opts = opts || {};
14539
- return features.map(function(obj, i) {
14564
+ return features.map(function(obj) {
14540
14565
  var geom = obj.type == 'Feature' ? obj.geometry : obj; // could be null
14541
14566
  var geomType = geom && geom.type;
14542
14567
  var msType = GeoJSON.translateGeoJSONType(geomType);
@@ -14852,7 +14877,7 @@
14852
14877
  }
14853
14878
 
14854
14879
  // polyline coords are like GeoJSON MultiLineString coords: an array of 0 or more paths
14855
- function polyline(d, x, y) {
14880
+ function polyline(d) {
14856
14881
  var coords = d.coordinates || [];
14857
14882
  var o = importMultiLineString(coords);
14858
14883
  applyStyleAttributes(o, 'polyline', d);
@@ -14860,7 +14885,7 @@
14860
14885
  }
14861
14886
 
14862
14887
  // polygon coords are an array of rings (and holes), like flattened MultiPolygon coords
14863
- function polygon(d, x, y) {
14888
+ function polygon(d) {
14864
14889
  var coords = d.coordinates || [];
14865
14890
  var o = importPolygon(coords);
14866
14891
  applyStyleAttributes(o, 'polygon', d);
@@ -15407,7 +15432,6 @@ ${svg}
15407
15432
  var geojson = exportDatasetAsGeoJSON(d, opts);
15408
15433
  var features = geojson.features || geojson.geometries || (geojson.type ? [geojson] : []);
15409
15434
  var children = importGeoJSONFeatures(features, opts);
15410
- var data;
15411
15435
  if (opts.svg_data && lyr.data) {
15412
15436
  addDataAttributesToSVG(children, lyr.data, opts.svg_data);
15413
15437
  }
@@ -16336,7 +16360,7 @@ ${svg}
16336
16360
  return readFixedWidthRecordsFromString(str, opts);
16337
16361
  }
16338
16362
 
16339
- function readFixedWidthRecordsFromString(str, ops) {
16363
+ function readFixedWidthRecordsFromString(str) {
16340
16364
  var fields = parseFixedWidthInfo(str.substring(0, 2000));
16341
16365
  if (!fields) return [];
16342
16366
  var lines = utils.splitLines(str);
@@ -16483,11 +16507,6 @@ ${svg}
16483
16507
  return formatRow(rec);
16484
16508
  }
16485
16509
 
16486
- function formatDelimHeader(fields, delim) {
16487
- var formatValue = getDelimValueFormatter(delim);
16488
- return fields.map(formatValue).join(delim);
16489
- }
16490
-
16491
16510
  function getDelimRowFormatter(fields, delim, opts) {
16492
16511
  var formatValue = getDelimValueFormatter(delim, opts);
16493
16512
  return function(rec) {
@@ -18170,8 +18189,7 @@ ${svg}
18170
18189
  // @chars String of chars to look for in @str
18171
18190
  function getCharScore(str, chars) {
18172
18191
  var index = {},
18173
- count = 0,
18174
- score;
18192
+ count = 0;
18175
18193
  str = str.toLowerCase();
18176
18194
  for (var i=0, n=chars.length; i<n; i++) {
18177
18195
  index[chars[i]] = 1;
@@ -18192,7 +18210,7 @@ ${svg}
18192
18210
  // TODO: remove duplication with importJSON()
18193
18211
  var readFromFile = !data.content && data.content !== '',
18194
18212
  content = data.content,
18195
- filter, reader, records, delimiter, table, encoding;
18213
+ reader, records, delimiter, table, encoding;
18196
18214
  opts = opts || {};
18197
18215
 
18198
18216
  // // read content of all but very large files into a buffer
@@ -18434,43 +18452,19 @@ ${svg}
18434
18452
  }
18435
18453
  }
18436
18454
 
18437
- // var intervalStr = o.interval;
18438
- // if (intervalStr) {
18439
- // o.interval = Number(intervalStr);
18440
- // if (o.interval >= 0 === false) {
18441
- // error(utils.format("Out-of-range interval value: %s", intervalStr));
18442
- // }
18443
- // }
18444
-
18445
18455
  if (!o.interval && !o.percentage && !o.resolution) {
18446
18456
  error("Command requires an interval, percentage or resolution parameter");
18447
18457
  }
18448
18458
  }
18449
18459
 
18450
18460
  function validateProjOpts(cmd) {
18451
- var _ = cmd._,
18452
- proj4 = [];
18461
+ var _ = cmd._;
18453
18462
 
18454
18463
  if (_.length > 0 && !cmd.options.crs) {
18455
18464
  cmd.options.crs = _.join(' ');
18456
18465
  _ = [];
18457
18466
  }
18458
18467
 
18459
- // separate proj4 options
18460
- // _ = _.filter(function(arg) {
18461
- // if (/^\+[a-z]/i.test(arg)) {
18462
- // proj4.push(arg);
18463
- // return false;
18464
- // }
18465
- // return true;
18466
- // });
18467
-
18468
- // if (proj4.length > 0) {
18469
- // cmd.options.crs = proj4.join(' ');
18470
- // } else if (_.length > 0) {
18471
- // cmd.options.crs = _.shift();
18472
- // }
18473
-
18474
18468
  if (_.length > 0) {
18475
18469
  error("Received one or more unexpected parameters: " + _.join(', '));
18476
18470
  }
@@ -18565,7 +18559,7 @@ ${svg}
18565
18559
  }
18566
18560
  }
18567
18561
 
18568
- var assignmentRxp = /^([a-z0-9_+-]+)=(?!\=)(.*)$/i; // exclude ==
18562
+ var assignmentRxp = /^([a-z0-9_+-]+)=(?!=)(.*)$/i; // exclude ==
18569
18563
 
18570
18564
  function splitShellTokens(str) {
18571
18565
  var BAREWORD = '([^\'"\\s])+';
@@ -20953,6 +20947,10 @@ ${svg}
20953
20947
  // describe: (0-1) inset grid shapes by a percentage
20954
20948
  type: 'number'
20955
20949
  })
20950
+ .option('aligned', {
20951
+ // describe: all grids of a given cell size will be aligned
20952
+ type: 'flag'
20953
+ })
20956
20954
  .option('calc', calcOpt)
20957
20955
  .option('name', nameOpt)
20958
20956
  .option('target', targetOpt)
@@ -21099,6 +21097,9 @@ ${svg}
21099
21097
  })
21100
21098
  .option('target', targetOpt);
21101
21099
 
21100
+ parser.command('stop')
21101
+ .describe('stop processing (skip remaining commands)');
21102
+
21102
21103
  parser.section('Informational commands');
21103
21104
 
21104
21105
  parser.command('calc')
@@ -22760,7 +22761,6 @@ ${svg}
22760
22761
  shapes = [], // topological ids
22761
22762
  types = [],
22762
22763
  dataNulls = 0,
22763
- shapeNulls = 0,
22764
22764
  collectionType = null,
22765
22765
  shapeId;
22766
22766
 
@@ -22777,15 +22777,12 @@ ${svg}
22777
22777
  if (geom.type) {
22778
22778
  this.addShape(geom);
22779
22779
  }
22780
- if (shapes[shapeId] === null) {
22781
- shapeNulls++;
22782
- }
22783
22780
  };
22784
22781
 
22785
22782
  this.addShape = function(geom) {
22786
22783
  var curr = shapes[shapeId];
22787
22784
  var type = GeoJSON.translateGeoJSONType(geom.type);
22788
- var shape, importer;
22785
+ var shape;
22789
22786
  if (geom.type == "GeometryCollection") {
22790
22787
  geom.geometries.forEach(this.addShape, this);
22791
22788
  } else if (type) {
@@ -24065,12 +24062,12 @@ ${svg}
24065
24062
  function Job(catalog) {
24066
24063
  var currentCmd;
24067
24064
 
24068
- var job = {
24065
+ var job = {
24069
24066
  catalog: catalog || new Catalog(),
24070
24067
  defs: {},
24071
24068
  settings: {},
24072
24069
  input_files: []
24073
- };
24070
+ };
24074
24071
 
24075
24072
  job.initSettings = function(o) {
24076
24073
  job.settings = o;
@@ -27125,10 +27122,6 @@ ${svg}
27125
27122
  // polyline output could be used for debugging
27126
27123
  var outputGeom = opts.output_geometry == 'polyline' ? 'polyline' : 'polygon';
27127
27124
 
27128
- function polygonCoords(ring) {
27129
- return [ring];
27130
- }
27131
-
27132
27125
  function pathBufferCoords(pathArcs, dist) {
27133
27126
  var pathCoords = maker(pathArcs, dist);
27134
27127
  var revPathArcs;
@@ -27158,13 +27151,9 @@ ${svg}
27158
27151
  var backtrackSteps = opts.backtrack >= 0 ? opts.backtrack : 50;
27159
27152
  var pathIter = new ShapeIter(arcs);
27160
27153
  var capStyle = opts.cap_style || 'round'; // expect 'round' or 'flat'
27161
- var tolerance;
27154
+ // var tolerance;
27162
27155
  // TODO: implement other join styles than round
27163
27156
 
27164
- function updateTolerance(dist) {
27165
-
27166
- }
27167
-
27168
27157
  function addRoundJoin(arr, x, y, startDir, angle, dist) {
27169
27158
  var increment = 10;
27170
27159
  var endDir = startDir + angle;
@@ -27175,15 +27164,15 @@ ${svg}
27175
27164
  }
27176
27165
  }
27177
27166
 
27178
- function addRoundJoin2(arr, x, y, startDir, angle, dist) {
27179
- var increment = 10;
27180
- var endDir = startDir + angle;
27181
- var dir = startDir + increment;
27182
- while (dir < endDir) {
27183
- addBufferVertex(arr, geod(x, y, dir, dist), backtrackSteps);
27184
- dir += increment;
27185
- }
27186
- }
27167
+ // function addRoundJoin2(arr, x, y, startDir, angle, dist) {
27168
+ // var increment = 10;
27169
+ // var endDir = startDir + angle;
27170
+ // var dir = startDir + increment;
27171
+ // while (dir < endDir) {
27172
+ // addBufferVertex(arr, geod(x, y, dir, dist), backtrackSteps);
27173
+ // dir += increment;
27174
+ // }
27175
+ // }
27187
27176
 
27188
27177
  // Test if two points are within a snapping tolerance
27189
27178
  // TODO: calculate the tolerance more sensibly
@@ -27430,15 +27419,14 @@ ${svg}
27430
27419
  function getPathBufferMaker2(arcs, geod, getBearing, opts) {
27431
27420
  var backtrackSteps = opts.backtrack >= 0 ? opts.backtrack : 50;
27432
27421
  var pathIter = new ShapeIter(arcs);
27433
- var capStyle = opts.cap_style || 'round'; // expect 'round' or 'flat'
27434
- var tolerance;
27422
+ // var capStyle = opts.cap_style || 'round'; // expect 'round' or 'flat'
27435
27423
  var partials, left, center;
27436
27424
  var bounds;
27437
27425
  // TODO: implement other join styles than round
27438
27426
 
27439
- function updateTolerance(dist) {
27427
+ // function updateTolerance(dist) {
27440
27428
 
27441
- }
27429
+ // }
27442
27430
 
27443
27431
  function addRoundJoin(x, y, startDir, angle, dist) {
27444
27432
  var increment = 10;
@@ -27482,24 +27470,24 @@ ${svg}
27482
27470
  }
27483
27471
  }
27484
27472
 
27485
- function makeCap(x, y, direction, dist) {
27486
- if (capStyle == 'flat') {
27487
- return [[x, y]];
27488
- }
27489
- return makeRoundCap(x, y, direction, dist);
27490
- }
27473
+ // function makeCap(x, y, direction, dist) {
27474
+ // if (capStyle == 'flat') {
27475
+ // return [[x, y]];
27476
+ // }
27477
+ // return makeRoundCap(x, y, direction, dist);
27478
+ // }
27491
27479
 
27492
- function makeRoundCap(x, y, segmentDir, dist) {
27493
- var points = [];
27494
- var increment = 10;
27495
- var startDir = segmentDir - 90;
27496
- var angle = increment;
27497
- while (angle < 180) {
27498
- points.push(geod(x, y, startDir + angle, dist));
27499
- angle += increment;
27500
- }
27501
- return points;
27502
- }
27480
+ // function makeRoundCap(x, y, segmentDir, dist) {
27481
+ // var points = [];
27482
+ // var increment = 10;
27483
+ // var startDir = segmentDir - 90;
27484
+ // var angle = increment;
27485
+ // while (angle < 180) {
27486
+ // points.push(geod(x, y, startDir + angle, dist));
27487
+ // angle += increment;
27488
+ // }
27489
+ // return points;
27490
+ // }
27503
27491
 
27504
27492
  // get angle between two extruded segments in degrees
27505
27493
  // positive angle means join in convex (range: 0-180 degrees)
@@ -27596,18 +27584,20 @@ ${svg}
27596
27584
  }
27597
27585
 
27598
27586
  return function(path, dist) {
27599
- var x0, y0, x1, y1, x2, y2;
27587
+ // var x0, y0;
27588
+ var x1, y1, x2, y2;
27600
27589
  var p1, p2;
27601
- var bearing, prevBearing, firstBearing, joinAngle;
27590
+ // var firstBearing;
27591
+ var bearing, prevBearing, joinAngle;
27602
27592
  partials = [];
27603
27593
  left = [];
27604
27594
  center = [];
27605
27595
  pathIter.init(path);
27606
27596
 
27607
- if (pathIter.hasNext()) {
27608
- x0 = x2 = pathIter.x;
27609
- y0 = y2 = pathIter.y;
27610
- }
27597
+ // if (pathIter.hasNext()) {
27598
+ // x0 = x2 = pathIter.x;
27599
+ // y0 = y2 = pathIter.y;
27600
+ // }
27611
27601
  while (pathIter.hasNext()) {
27612
27602
  // TODO: use a tolerance
27613
27603
  if (pathIter.x === x2 && pathIter.y === y2) continue; // skip duplicate points
@@ -27624,9 +27614,9 @@ ${svg}
27624
27614
 
27625
27615
  if (center.length === 0) {
27626
27616
  // first loop, second point in this partial
27627
- if (partials.length === 0) {
27628
- firstBearing = bearing;
27629
- }
27617
+ // if (partials.length === 0) {
27618
+ // firstBearing = bearing;
27619
+ // }
27630
27620
  left.push(p1, p2);
27631
27621
  center.push([x1, y1], [x2, y2]);
27632
27622
  } else {
@@ -28207,7 +28197,7 @@ ${svg}
28207
28197
  rings.push([
28208
28198
  [[180, 90], [180, -90], [0, -90], [-180, -90], [-180, 90], [0, 90], [180, 90]],
28209
28199
  coords
28210
- ]);
28200
+ ]);
28211
28201
  } else {
28212
28202
  rings.push([coords]);
28213
28203
  }
@@ -34899,6 +34889,7 @@ ${svg}
34899
34889
  // }).join(",") + "}");
34900
34890
  // };
34901
34891
 
34892
+ // Removes points that are far from other points
34902
34893
  cmd.filterPoints = function(lyr, dataset, opts) {
34903
34894
  requireSinglePointLayer(lyr);
34904
34895
  if (opts.group_interval > 0 === false) {
@@ -34913,7 +34904,7 @@ ${svg}
34913
34904
  var index = new Uint8Array(points.length);
34914
34905
  var a, b, c, ai, bi, ci;
34915
34906
  for (var i=0, n=triangles.length; i<n; i+=3) {
34916
- // a, b, c: triangle verticies in CCW order
34907
+ // a, b, c: triangle vertices in CCW order
34917
34908
  ai = triangles[i];
34918
34909
  bi = triangles[i+1];
34919
34910
  ci = triangles[i+2];
@@ -36085,7 +36076,7 @@ ${svg}
36085
36076
  var arcId = o.arcId,
36086
36077
  key = classify(arcId),
36087
36078
  isContinuation, line;
36088
- if (!!key) {
36079
+ if (key) {
36089
36080
  line = key in index ? index[key] : null;
36090
36081
  isContinuation = key == prevKey && o.shapeId == prev.shapeId && o.partId == prev.partId;
36091
36082
  if (!line) {
@@ -36765,6 +36756,14 @@ ${svg}
36765
36756
  job.control = null;
36766
36757
  }
36767
36758
 
36759
+ function stopJob(job) {
36760
+ getState(job).stopped = true;
36761
+ }
36762
+
36763
+ function jobIsStopped(job) {
36764
+ return getState(job).stopped === true;
36765
+ }
36766
+
36768
36767
  function inControlBlock(job) {
36769
36768
  return !!getState(job).inControlBlock;
36770
36769
  }
@@ -36833,6 +36832,7 @@ ${svg}
36833
36832
 
36834
36833
  function skipCommand(cmdName, job) {
36835
36834
  // allow all control commands to run
36835
+ if (jobIsStopped(job)) return true;
36836
36836
  if (isControlFlowCommand(cmdName)) return false;
36837
36837
  return inControlBlock(job) && !inActiveBranch(job);
36838
36838
  }
@@ -38761,7 +38761,7 @@ ${svg}
38761
38761
  stop('Expected a non-negative interval parameter');
38762
38762
  }
38763
38763
  if (opts.radius > 0 === false) {
38764
- stop('Expected a non-negative radius parameter');
38764
+ // stop('Expected a non-negative radius parameter');
38765
38765
  }
38766
38766
  // var bbox = getLayerBounds(pointLyr).toArray();
38767
38767
  // Use target dataset, so grids are aligned between layers
@@ -38789,27 +38789,24 @@ ${svg}
38789
38789
  };
38790
38790
 
38791
38791
  function getPolygonDataset(pointLyr, gridBBox, opts) {
38792
- var interval = opts.interval;
38793
38792
  var points = getPointsInLayer(pointLyr);
38794
- var grid = getGridData(gridBBox, interval);
38795
- var lookup = getPointIndex(points, grid, opts.radius);
38796
- var n = grid.cells();
38793
+ var cellSize = opts.interval;
38794
+ var grid = getGridData(gridBBox, cellSize, opts);
38795
+ var pointCircleRadius = getPointCircleRadius(opts);
38796
+ var findPointIdsByCellId = getPointIndex(points, grid, pointCircleRadius);
38797
38797
  var geojson = {
38798
38798
  type: 'FeatureCollection',
38799
38799
  features: []
38800
38800
  };
38801
- var calc = null;
38802
- var cands, center, weight, d;
38803
- if (opts.calc) {
38804
- calc = getJoinCalc(pointLyr.data, opts.calc);
38805
- }
38801
+ var calc = opts.calc ? getJoinCalc(pointLyr.data, opts.calc) : null;
38802
+ var candidateIds, weights, center, weight, d;
38806
38803
 
38807
- for (var i=0; i<n; i++) {
38808
- cands = lookup(i);
38809
- if (!cands.length) continue;
38804
+ for (var i=0, n=grid.cells(); i<n; i++) {
38805
+ candidateIds = findPointIdsByCellId(i);
38806
+ if (!candidateIds.length) continue;
38810
38807
  center = grid.idxToPoint(i);
38811
- d = calcCellProperties(center, cands, points, calc, opts);
38812
- // weight = calcCellWeight(center, cands, points, opts);
38808
+ weights = calcWeights(center, cellSize, points, candidateIds, pointCircleRadius);
38809
+ d = calcCellProperties(candidateIds, weights, calc);
38813
38810
  if (d.weight > 0.05 === false) continue;
38814
38811
  d.id = i;
38815
38812
  geojson.features.push({
@@ -38818,44 +38815,43 @@ ${svg}
38818
38815
  geometry: makeCellPolygon(i, grid, opts)
38819
38816
  });
38820
38817
  }
38821
- var dataset = importGeoJSON(geojson, {});
38822
- return dataset;
38818
+ return importGeoJSON(geojson, {});
38823
38819
  }
38824
38820
 
38825
- function calcCellProperties(center, cands, points, calc, opts) {
38826
- // radius of circle with same area as the cell
38827
- var interval = opts.interval;
38828
- var radius = interval * Math.sqrt(1 / Math.PI);
38829
- var circleArea = Math.PI * opts.radius * opts.radius;
38830
- var cellArea = interval * interval;
38831
- var ids = [];
38832
- var totArea = 0;
38833
- var intersection;
38834
- for (var i=0; i<cands.length; i++) {
38835
- intersection = twoCircleIntersection(center, radius, points[cands[i]], opts.radius);
38836
- if (intersection > 0 === false) continue;
38837
- totArea += intersection;
38838
- ids.push(cands[i]);
38839
- }
38840
- var d = {weight: totArea / cellArea};
38821
+ function getPointCircleRadius(opts) {
38822
+ var cellRadius = opts.interval * Math.sqrt(1 / Math.PI);
38823
+ return opts.radius > 0 ? opts.radius : cellRadius;
38824
+ }
38825
+
38826
+ function calcCellProperties(pointIds, weights, calc) {
38827
+ var hitIds = [];
38828
+ var weight = 0;
38829
+ var partial;
38830
+ var d;
38831
+ for (var i=0; i<pointIds.length; i++) {
38832
+ partial = weights[i];
38833
+ if (partial > 0 === false) continue;
38834
+ weight += partial;
38835
+ hitIds.push(pointIds[i]);
38836
+ }
38837
+ d = {weight: weight};
38841
38838
  if (calc) {
38842
- calc(ids, d);
38839
+ calc(hitIds, d);
38843
38840
  }
38844
38841
  return d;
38845
38842
  }
38846
38843
 
38847
- // function calcCellWeight(center, ids, points, opts) {
38848
- // // radius of circle with same area as the cell
38849
- // var interval = opts.interval;
38850
- // var radius = interval * Math.sqrt(1 / Math.PI);
38851
- // var circleArea = Math.PI * opts.radius * opts.radius;
38852
- // var cellArea = interval * interval;
38853
- // var totArea = 0;
38854
- // for (var i=0; i<ids.length; i++) {
38855
- // totArea += twoCircleIntersection(center, radius, points[ids[i]], opts.radius);
38856
- // }
38857
- // return totArea / cellArea;
38858
- // }
38844
+ function calcWeights(cellCenter, cellSize, points, pointIds, pointRadius) {
38845
+ var weights = [];
38846
+ var cellRadius = cellSize * Math.sqrt(1 / Math.PI); // radius of circle with same area as cell
38847
+ var cellArea = cellSize * cellSize;
38848
+ var w;
38849
+ for (var i=0; i<pointIds.length; i++) {
38850
+ w = twoCircleIntersection(cellCenter, cellRadius, points[pointIds[i]], pointRadius) / cellArea;
38851
+ weights.push(w);
38852
+ }
38853
+ return weights;
38854
+ }
38859
38855
 
38860
38856
  // Source: https://diego.assencio.com/?index=8d6ca3d82151bad815f78addf9b5c1c6
38861
38857
  function twoCircleIntersection(c1, r1, c2, r2) {
@@ -38910,10 +38906,19 @@ ${svg}
38910
38906
  return function(i) {
38911
38907
  if (!gridIndex.hasId(i)) return empty;
38912
38908
  var bbox = grid.idxToBBox(i);
38913
- return bboxIndex.search.apply(bboxIndex, bbox);
38909
+ var indices = bboxIndex.search.apply(bboxIndex, bbox);
38910
+ return indices;
38914
38911
  };
38915
38912
  }
38916
38913
 
38914
+ function getPointsByIndex(points, indices) {
38915
+ var arr = [];
38916
+ for (var i=0; i<indices.length; i++) {
38917
+ arr.push(points[indices[i]]);
38918
+ }
38919
+ return arr;
38920
+ }
38921
+
38917
38922
  function addPointToGridIndex(p, index, grid) {
38918
38923
  var i = grid.pointToIdx(p);
38919
38924
  var c = grid.idxToCol(i);
@@ -38939,23 +38944,52 @@ ${svg}
38939
38944
  return [p[0] - radius, p[1] - radius, p[0] + radius, p[1] + radius];
38940
38945
  }
38941
38946
 
38942
- function getGridInterpolator(bbox, interval) {
38943
- var sparseArr = [];
38944
- var grid = getGridData(bbox, interval);
38945
-
38946
- }
38947
-
38948
- // TODO: put this in a separate file, use it for other grid-based commands
38949
- // like -dots
38950
- function getGridData(bbox, interval) {
38951
- var xmin = bbox[0] - interval;
38952
- var ymin = bbox[1] - interval;
38953
- var xmax = bbox[2] + interval;
38954
- var ymax = bbox[3] + interval;
38955
- var w = xmax - xmin;
38956
- var h = ymax - ymin;
38957
- var cols = Math.ceil(w / interval);
38958
- var rows = Math.ceil(h / interval);
38947
+ // grid boundaries includes the origin
38948
+ // (this way, grids calculated from different sets of points will all align)
38949
+ function getAlignedRange(minCoord, maxCoord, interval) {
38950
+ var idx = Math.floor(minCoord / interval) - 1;
38951
+ var idx2 = Math.ceil(maxCoord / interval) + 1;
38952
+ return [idx * interval, idx2 * interval];
38953
+ }
38954
+
38955
+ function getCenteredRange(minCoord, maxCoord, interval) {
38956
+ var w = maxCoord - minCoord;
38957
+ var w2 = Math.ceil(w / interval) * interval;
38958
+ var pad = (w2 - w) / 2 + interval;
38959
+ return [minCoord - pad, maxCoord + pad];
38960
+ }
38961
+
38962
+ function getAlignedGridBounds(bbox, interval) {
38963
+ var xx = getAlignedRange(bbox[0], bbox[2], interval);
38964
+ var yy = getAlignedRange(bbox[1], bbox[3], interval);
38965
+ return [xx[0], yy[0], xx[1], yy[1]];
38966
+ }
38967
+
38968
+ function getCenteredGridBounds(bbox, interval) {
38969
+ var xx = getCenteredRange(bbox[0], bbox[2], interval);
38970
+ var yy = getCenteredRange(bbox[1], bbox[3], interval);
38971
+ return [xx[0], yy[0], xx[1], yy[1]];
38972
+ }
38973
+
38974
+ // TODO: Use this function for other grid-based commands
38975
+ function getGridData(bbox, interval, opts) {
38976
+ var extent = opts && opts.aligned ?
38977
+ getAlignedGridBounds(bbox, interval) :
38978
+ getCenteredGridBounds(bbox, interval);
38979
+ var xmin = extent[0];
38980
+ var ymin = extent[1];
38981
+ var w = extent[2] - xmin;
38982
+ var h = extent[3] - ymin;
38983
+ var cols = Math.round(w / interval);
38984
+ var rows = Math.round(h / interval);
38985
+ // var xmin = bbox[0] - interval;
38986
+ // var ymin = bbox[1] - interval;
38987
+ // var xmax = bbox[2] + interval;
38988
+ // var ymax = bbox[3] + interval;
38989
+ // var w = xmax - xmin;
38990
+ // var h = ymax - ymin;
38991
+ // var cols = Math.ceil(w / interval);
38992
+ // var rows = Math.ceil(h / interval);
38959
38993
  function size() {
38960
38994
  return [cols, rows];
38961
38995
  }
@@ -40493,6 +40527,10 @@ ${svg}
40493
40527
  getSplitNameFunction: getSplitNameFunction
40494
40528
  });
40495
40529
 
40530
+ cmd.stop = function(job) {
40531
+ stopJob(job);
40532
+ };
40533
+
40496
40534
  cmd.svgStyle = function(lyr, dataset, opts) {
40497
40535
  var filter;
40498
40536
  if (!lyr.data) {
@@ -40638,10 +40676,10 @@ ${svg}
40638
40676
  dy = stemLen * Math.cos(theta / 2);
40639
40677
 
40640
40678
  if (stickArrow) {
40641
- stem = getCurvedStemCoords(-ax, -ay, dx, dy, theta);
40679
+ stem = getCurvedStemCoords(-ax, -ay, dx, dy);
40642
40680
  } else {
40643
- var leftStem = getCurvedStemCoords(-ax, -ay, -stemDx + dx, dy, theta);
40644
- var rightStem = getCurvedStemCoords(ax, ay, stemDx + dx, dy, theta);
40681
+ var leftStem = getCurvedStemCoords(-ax, -ay, -stemDx + dx, dy);
40682
+ var rightStem = getCurvedStemCoords(ax, ay, stemDx + dx, dy);
40645
40683
  stem = leftStem.concat(rightStem.reverse());
40646
40684
  }
40647
40685
 
@@ -40685,10 +40723,10 @@ ${svg}
40685
40723
  return coords;
40686
40724
  }
40687
40725
 
40688
- function calcStraightArrowCoords(stemLen, headLen, stemDx, headDx, baseDx) {
40689
- return [[baseDx, 0], [stemDx, stemLen], [headDx, stemLen], [0, stemLen + headLen],
40690
- [-headDx, stemLen], [-stemDx, stemLen], [-baseDx, 0], [baseDx, 0]];
40691
- }
40726
+ // function calcStraightArrowCoords(stemLen, headLen, stemDx, headDx, baseDx) {
40727
+ // return [[baseDx, 0], [stemDx, stemLen], [headDx, stemLen], [0, stemLen + headLen],
40728
+ // [-headDx, stemLen], [-stemDx, stemLen], [-baseDx, 0], [baseDx, 0]];
40729
+ // }
40692
40730
 
40693
40731
  function calcArrowSize(d, stickArrow) {
40694
40732
  // don't display arrows with negative length
@@ -40755,7 +40793,7 @@ ${svg}
40755
40793
 
40756
40794
  // ax, ay: point on the base
40757
40795
  // bx, by: point on the stem
40758
- function getCurvedStemCoords(ax, ay, bx, by, theta0) {
40796
+ function getCurvedStemCoords(ax, ay, bx, by) {
40759
40797
  // case: curved side intrudes into head (because stem is too short)
40760
40798
  if (ay > by) {
40761
40799
  return [[ax * by / ay, by]];
@@ -41478,7 +41516,7 @@ ${svg}
41478
41516
  return name == 'graticule' || name == 'i' || name == 'help' ||
41479
41517
  name == 'point-grid' || name == 'shape' || name == 'rectangle' ||
41480
41518
  name == 'include' || name == 'print' || name == 'comment' || name == 'if' || name == 'elif' ||
41481
- name == 'else' || name == 'endif';
41519
+ name == 'else' || name == 'endif' || name == 'stop';
41482
41520
  }
41483
41521
 
41484
41522
  function runCommand(command, job, cb) {
@@ -41808,6 +41846,9 @@ ${svg}
41808
41846
  } else if (name == 'split') {
41809
41847
  outputLayers = applyCommandToEachLayer(cmd.splitLayer, targetLayers, opts.expression, opts);
41810
41848
 
41849
+ } else if (name == 'stop') {
41850
+ cmd.stop(job);
41851
+
41811
41852
  } else if (name == 'split-on-grid') {
41812
41853
  outputLayers = applyCommandToEachLayer(cmd.splitLayerOnGrid, targetLayers, arcs, opts);
41813
41854
 
@@ -42573,7 +42614,6 @@ ${svg}
42573
42614
  // so they can be run by tests and by the GUI.
42574
42615
  // TODO: rewrite tests to import functions directly from modules,
42575
42616
  // export only functions called by the GUI.
42576
-
42577
42617
  var internal = {};
42578
42618
 
42579
42619
  internal.svg = Object.assign({}, SvgStringify, SvgPathUtils, GeojsonToSvg, SvgLabels, SvgSymbols);