mapshaper 0.6.111 → 0.6.113

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
@@ -1211,8 +1211,13 @@
1211
1211
  if (len >= 2) {
1212
1212
  first = str.charAt(0);
1213
1213
  last = str.charAt(len-1);
1214
- if (first == '"' && last == '"' && !str.includes('","') ||
1215
- first == "'" && last == "'" && !str.includes("','")) {
1214
+ // if (first == '"' && last == '"' && !str.includes('","') ||
1215
+ // first == "'" && last == "'" && !str.includes("','")) {
1216
+ // don't strip if there are unescaped quotes
1217
+ // e.g. expressions that start and end with quotes
1218
+ // e.g. comma-separated list of quoted values
1219
+ if (first == '"' && last == '"' && !/[^\\]"./.test(str) ||
1220
+ first == "'" && last == "'" && !/[^\\]'./.test(str)) {
1216
1221
  str = str.substr(1, len-2);
1217
1222
  // remove string escapes
1218
1223
  str = str.replace(first == '"' ? /\\(?=")/g : /\\(?=')/g, '');
@@ -1347,6 +1352,18 @@
1347
1352
  }
1348
1353
  }
1349
1354
 
1355
+ function time(slug) {
1356
+ if (useDebug()) {
1357
+ console.time(slug);
1358
+ }
1359
+ }
1360
+
1361
+ function timeEnd(slug) {
1362
+ if (useDebug()) {
1363
+ console.timeEnd(slug);
1364
+ }
1365
+ }
1366
+
1350
1367
  function printError(err) {
1351
1368
  var msg;
1352
1369
  if (!LOGGING) return;
@@ -1465,6 +1482,8 @@
1465
1482
  setLoggingForCLI: setLoggingForCLI,
1466
1483
  setLoggingFunctions: setLoggingFunctions,
1467
1484
  stop: stop,
1485
+ time: time,
1486
+ timeEnd: timeEnd,
1468
1487
  truncateString: truncateString,
1469
1488
  useDebug: useDebug,
1470
1489
  useVerbose: useVerbose,
@@ -2088,13 +2107,13 @@
2088
2107
 
2089
2108
  // TODO: remove this constant, use actual data from dataset CRS,
2090
2109
  // also consider using ellipsoidal formulas where greater accuracy might be important.
2091
- var R$1 = WGS84.SEMIMAJOR_AXIS;
2110
+ var R$2 = WGS84.SEMIMAJOR_AXIS;
2092
2111
  var D2R = Math.PI / 180;
2093
2112
  var R2D = 180 / Math.PI;
2094
2113
 
2095
2114
  // Equirectangular projection
2096
2115
  function degreesToMeters(deg) {
2097
- return deg * D2R * R$1;
2116
+ return deg * D2R * R$2;
2098
2117
  }
2099
2118
 
2100
2119
  function distance3D(ax, ay, az, bx, by, bz) {
@@ -2191,7 +2210,8 @@
2191
2210
  lat1 *= D2R;
2192
2211
  lat2 *= D2R;
2193
2212
  var y = Math.sin(lng2-lng1) * Math.cos(lat2),
2194
- x = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(lng2-lng1);
2213
+ x = Math.cos(lat1) * Math.sin(lat2) -
2214
+ Math.sin(lat1) * Math.cos(lat2) * Math.cos(lng2-lng1);
2195
2215
  return Math.atan2(y, x);
2196
2216
  }
2197
2217
 
@@ -2251,9 +2271,9 @@
2251
2271
  lng *= D2R;
2252
2272
  lat *= D2R;
2253
2273
  cosLat = Math.cos(lat);
2254
- p[0] = Math.cos(lng) * cosLat * R$1;
2255
- p[1] = Math.sin(lng) * cosLat * R$1;
2256
- p[2] = Math.sin(lat) * R$1;
2274
+ p[0] = Math.cos(lng) * cosLat * R$2;
2275
+ p[1] = Math.sin(lng) * cosLat * R$2;
2276
+ p[2] = Math.sin(lat) * R$2;
2257
2277
  }
2258
2278
 
2259
2279
  // Haversine formula (well conditioned at small distances)
@@ -2272,7 +2292,7 @@
2272
2292
  function greatCircleDistance(lng1, lat1, lng2, lat2) {
2273
2293
  var D2R = Math.PI / 180,
2274
2294
  dist = sphericalDistance(lng1 * D2R, lat1 * D2R, lng2 * D2R, lat2 * D2R);
2275
- return dist * R$1;
2295
+ return dist * R$2;
2276
2296
  }
2277
2297
 
2278
2298
 
@@ -2370,7 +2390,7 @@
2370
2390
  var Geom = /*#__PURE__*/Object.freeze({
2371
2391
  __proto__: null,
2372
2392
  D2R: D2R,
2373
- R: R$1,
2393
+ R: R$2,
2374
2394
  R2D: R2D,
2375
2395
  bearing: bearing,
2376
2396
  bearing2D: bearing2D,
@@ -3888,10 +3908,12 @@
3888
3908
  for (var i=0; i<n; i++) {
3889
3909
  retn = cb(parts[i], i, parts, shpId);
3890
3910
  if (retn === null) {
3891
- nulls++;
3892
3911
  parts[i] = null;
3893
3912
  } else if (utils.isArray(retn)) {
3894
- parts[i] = retn;
3913
+ parts[i] = retn.length > 0 ? retn : null;
3914
+ }
3915
+ if (parts[i] === null) {
3916
+ nulls++;
3895
3917
  }
3896
3918
  }
3897
3919
  if (nulls == n) {
@@ -7539,7 +7561,15 @@
7539
7561
  }
7540
7562
 
7541
7563
  function mergeOutputLayerIntoDataset(lyr, dataset, dataset2, opts) {
7542
- if (!dataset2 || dataset2.layers.length != 1) {
7564
+ var output = mergeOutputLayersIntoDataset(lyr, dataset, dataset2, opts);
7565
+ if (output.length != 1) {
7566
+ error('Expected 1 output layer, received:', output.length);
7567
+ }
7568
+ return output[0];
7569
+ }
7570
+
7571
+ function mergeOutputLayersIntoDataset(lyr, dataset, dataset2, opts) {
7572
+ if (!dataset2 || dataset2.layers.length === 0) {
7543
7573
  error('Invalid source dataset');
7544
7574
  }
7545
7575
  if (dataset.layers.includes(lyr) === false) {
@@ -7548,24 +7578,21 @@
7548
7578
  // this command returns merged layers instead of adding them to target dataset
7549
7579
  var outputLayers = mergeDatasetsIntoDataset(dataset, [dataset2]);
7550
7580
  var lyr2 = outputLayers[0];
7551
-
7552
7581
  // TODO: find a more reliable way of knowing when to copy data
7553
7582
  var copyData = !lyr2.data && lyr.data && getFeatureCount(lyr2) == lyr.data.size();
7554
-
7555
7583
  if (copyData) {
7556
7584
  lyr2.data = opts.no_replace ? lyr.data.clone() : lyr.data;
7557
7585
  }
7558
- if (opts.no_replace) ; else {
7559
- lyr2 = Object.assign(lyr, {data: null, shapes: null}, lyr2);
7586
+ lyr2.name = opts.name || lyr.name;
7587
+ if (!opts.no_replace) {
7588
+ outputLayers[0] = Object.assign(lyr, {data: null, shapes: null}, lyr2);
7560
7589
  if (layerHasPaths(lyr)) {
7561
7590
  // Remove unused arcs from replaced layer
7562
7591
  // TODO: consider using clean insead of this
7563
7592
  dissolveArcs(dataset);
7564
7593
  }
7565
7594
  }
7566
-
7567
- lyr2.name = opts.name || lyr2.name;
7568
- return lyr2;
7595
+ return outputLayers;
7569
7596
  }
7570
7597
 
7571
7598
  // Transform the points in a dataset in-place; don't clean up corrupted shapes
@@ -7593,6 +7620,7 @@
7593
7620
  getDatasetBounds: getDatasetBounds,
7594
7621
  mergeDatasetInfo: mergeDatasetInfo,
7595
7622
  mergeOutputLayerIntoDataset: mergeOutputLayerIntoDataset,
7623
+ mergeOutputLayersIntoDataset: mergeOutputLayersIntoDataset,
7596
7624
  pruneArcs: pruneArcs,
7597
7625
  replaceLayerContents: replaceLayerContents,
7598
7626
  replaceLayers: replaceLayers,
@@ -11548,7 +11576,7 @@
11548
11576
  shapes: lyr.shapes || null,
11549
11577
  data: data,
11550
11578
  menu_order: lyr.menu_order || null,
11551
- pinned: lyr.pinned || false,
11579
+ pinned: lyr.pinned || opts.show_all || false,
11552
11580
  active: !!(lyr.active || lyr == opts.active_layer) // lyr.active: deprecated
11553
11581
  };
11554
11582
  }
@@ -16774,7 +16802,6 @@
16774
16802
 
16775
16803
  function cleanPath(path, arcs) {
16776
16804
  var nulls = 0;
16777
- // console.log("[cleanPath()]", path, path?.[0])
16778
16805
  for (var i=0, n=path.length; i<n; i++) {
16779
16806
  if (arcs.arcIsDegenerate(path[i])) {
16780
16807
  nulls++;
@@ -16922,47 +16949,6 @@
16922
16949
  splitPathByIds: splitPathByIds
16923
16950
  });
16924
16951
 
16925
- // TODO: also delete positive-space rings nested inside holes
16926
- function deleteHoles(lyr, arcs) {
16927
- editShapes(lyr.shapes, function(path) {
16928
- if (geom.getPathArea(path, arcs) <= 0) {
16929
- return null; // null deletes the path
16930
- }
16931
- });
16932
- }
16933
-
16934
- // Returns a function that separates rings in a polygon into space-enclosing rings
16935
- // and holes. Also fixes self-intersections.
16936
- //
16937
- function getHoleDivider(nodes, spherical) {
16938
- var split = getSelfIntersectionSplitter(nodes);
16939
- // var split = internal.getSelfIntersectionSplitter_v1(nodes); console.log('split')
16940
-
16941
- return function(rings, cw, ccw) {
16942
- var pathArea = spherical ? geom.getSphericalPathArea : geom.getPlanarPathArea;
16943
- forEachShapePart(rings, function(ringIds) {
16944
- var splitRings = split(ringIds);
16945
- if (splitRings.length === 0) {
16946
- debug("[getRingDivider()] Defective path:", ringIds);
16947
- }
16948
- splitRings.forEach(function(ringIds, i) {
16949
- var ringArea = pathArea(ringIds, nodes.arcs);
16950
- if (ringArea > 0) {
16951
- cw.push(ringIds);
16952
- } else if (ringArea < 0) {
16953
- ccw.push(ringIds);
16954
- }
16955
- });
16956
- });
16957
- };
16958
- }
16959
-
16960
- var PolygonHoles = /*#__PURE__*/Object.freeze({
16961
- __proto__: null,
16962
- deleteHoles: deleteHoles,
16963
- getHoleDivider: getHoleDivider
16964
- });
16965
-
16966
16952
  // Remap any references to duplicate arcs in paths to use the same arcs
16967
16953
  // Remove any unused arcs from the dataset's ArcCollection.
16968
16954
  // Return a NodeCollection
@@ -17567,6 +17553,47 @@
17567
17553
  return idx;
17568
17554
  }
17569
17555
 
17556
+ // TODO: also delete positive-space rings nested inside holes
17557
+ function deleteHoles(lyr, arcs) {
17558
+ editShapes(lyr.shapes, function(path) {
17559
+ if (geom.getPathArea(path, arcs) <= 0) {
17560
+ return null; // null deletes the path
17561
+ }
17562
+ });
17563
+ }
17564
+
17565
+ // Returns a function that separates rings in a polygon into space-enclosing rings
17566
+ // and holes. Also fixes self-intersections.
17567
+ //
17568
+ function getHoleDivider(nodes, spherical) {
17569
+ var split = getSelfIntersectionSplitter(nodes);
17570
+ // var split = internal.getSelfIntersectionSplitter_v1(nodes); console.log('split')
17571
+
17572
+ return function(rings, cw, ccw) {
17573
+ var pathArea = spherical ? geom.getSphericalPathArea : geom.getPlanarPathArea;
17574
+ forEachShapePart(rings, function(ringIds) {
17575
+ var splitRings = split(ringIds);
17576
+ if (splitRings.length === 0) {
17577
+ debug("[getRingDivider()] Defective path:", ringIds);
17578
+ }
17579
+ splitRings.forEach(function(ringIds, i) {
17580
+ var ringArea = pathArea(ringIds, nodes.arcs);
17581
+ if (ringArea > 0) {
17582
+ cw.push(ringIds);
17583
+ } else if (ringArea < 0) {
17584
+ ccw.push(ringIds);
17585
+ }
17586
+ });
17587
+ });
17588
+ };
17589
+ }
17590
+
17591
+ var PolygonHoles = /*#__PURE__*/Object.freeze({
17592
+ __proto__: null,
17593
+ deleteHoles: deleteHoles,
17594
+ getHoleDivider: getHoleDivider
17595
+ });
17596
+
17570
17597
  // Associate mosaic tiles with shapes (i.e. identify the groups of tiles that
17571
17598
  // belong to each shape)
17572
17599
  //
@@ -17675,7 +17702,6 @@
17675
17702
  function MosaicIndex(lyr, nodes, optsArg) {
17676
17703
  var opts = optsArg || {};
17677
17704
  var shapes = lyr.shapes;
17678
- getHoleDivider(nodes);
17679
17705
  var mosaic = buildPolygonMosaic(nodes).mosaic;
17680
17706
  // map arc ids to tile ids
17681
17707
  var arcTileIndex = new ShapeArcIndex(mosaic, nodes.arcs);
@@ -24723,6 +24749,10 @@ ${svg}
24723
24749
  type: 'flag',
24724
24750
  describe: '[CSV] export numbers with decimal commas not points'
24725
24751
  })
24752
+ .option('show-all', {
24753
+ type: 'flag',
24754
+ describe: '[Snapshot] show all layers in Web UI'
24755
+ })
24726
24756
  .option('final', {
24727
24757
  type: 'flag' // for testing
24728
24758
  })
@@ -24771,24 +24801,54 @@ ${svg}
24771
24801
  // describe: 'number of vertices to use when buffering points',
24772
24802
  type: 'integer'
24773
24803
  })
24804
+ .option('arc-quality', {
24805
+ // segments per quarter-circle in joins and caps
24806
+ type: 'integer'
24807
+ })
24808
+ .option('slice-length', {
24809
+ // max path segments per buffer section
24810
+ type: 'integer'
24811
+ })
24774
24812
  .option('backtrack', {
24813
+ // ...
24775
24814
  type: 'integer'
24776
24815
  })
24816
+ .option('cap-style', {
24817
+ describe: 'flat or round (default is round)'
24818
+ })
24777
24819
  .option('type', {
24778
24820
  // left, right, outer, inner (default is full buffer)
24779
24821
  })
24780
24822
  .option('planar', {
24781
24823
  type: 'flag'
24782
24824
  })
24783
- .option('v2', { // use v2 method
24825
+ .option('v2', {
24826
+ type: 'flag'
24827
+ })
24828
+ .option('v3', {
24784
24829
  type: 'flag'
24785
24830
  })
24786
- .option('debug-division', {
24831
+ .option('debug-offset', {
24787
24832
  type: 'flag'
24788
24833
  })
24834
+ .option('debug-winding', {
24835
+ type: 'flag'
24836
+ })
24837
+ .option('debug-points', {
24838
+ type: 'flag'
24839
+ })
24840
+ // .option('debug-division', {
24841
+ // type: 'flag'
24842
+ // })
24789
24843
  .option('debug-mosaic', {
24790
24844
  type: 'flag'
24791
24845
  })
24846
+ .option('left', {
24847
+ type: 'flag'
24848
+ })
24849
+ .option('right', {
24850
+ type: 'flag'
24851
+ })
24792
24852
  .option('no-cleanup', {
24793
24853
  type: 'flag'
24794
24854
  })
@@ -30925,15 +30985,120 @@ ${svg}
30925
30985
  return dataset;
30926
30986
  }
30927
30987
 
30928
- function dissolveBufferDataset(dataset, optsArg) {
30988
+ function getPolygonRewinder(nodes) {
30989
+ var splitter = getSelfIntersectionSplitter(nodes);
30990
+ return rewindPolygon;
30991
+
30992
+ function rewindPolygon(shp) {
30993
+ var shp2 = [];
30994
+ forEachShapePart(shp, function(ids) {
30995
+ var rings = splitter(ids);
30996
+ var path;
30997
+ for (var i=0; i<rings.length; i++) {
30998
+ path = rings[i];
30999
+ if (geom.getPlanarPathArea(path, nodes.arcs) < 0) {
31000
+ path = reversePath(path);
31001
+ }
31002
+ shp2.push(path);
31003
+ }
31004
+ });
31005
+ return shp2.length > 0 ? shp2 : null;
31006
+ }
31007
+ }
31008
+
31009
+ function rewindPolygonParts(lyr, nodes) {
31010
+ var rewind = getPolygonRewinder(nodes);
31011
+ lyr.shapes = lyr.shapes.map(function(shp) {
31012
+ return rewind(shp);
31013
+ });
31014
+ }
31015
+
31016
+ // TODO: Need to rethink polygon repair: these function can cause problems
31017
+ // when part of a self-intersecting polygon is removed
31018
+ //
31019
+ function repairPolygonGeometry(layers, dataset, opts) {
31020
+ var nodes = addIntersectionCuts(dataset);
31021
+ layers.forEach(function(lyr) {
31022
+ repairSelfIntersections(lyr, nodes);
31023
+ });
31024
+ return layers;
31025
+ }
31026
+
31027
+ // Remove any small shapes formed by twists in each ring
31028
+ // // OOPS, NO // Retain only the part with largest area
31029
+ // // this causes problems when a cut-off hole has a matching ring in another polygon
31030
+ // TODO: consider cases where cut-off parts should be retained
31031
+ //
31032
+ function repairSelfIntersections(lyr, nodes) {
31033
+ var splitter = getSelfIntersectionSplitter(nodes);
31034
+
31035
+ lyr.shapes = lyr.shapes.map(function(shp, i) {
31036
+ return cleanPolygon(shp);
31037
+ });
31038
+
31039
+ function cleanPolygon(shp) {
31040
+ var cleanedPolygon = [];
31041
+ forEachShapePart(shp, function(ids) {
31042
+ // TODO: consider returning null if path can't be split
31043
+ var splitIds = splitter(ids);
31044
+ if (splitIds.length === 0) {
31045
+ error("[cleanPolygon()] Defective path:", ids);
31046
+ } else if (splitIds.length == 1) {
31047
+ cleanedPolygon.push(splitIds[0]);
31048
+ } else {
31049
+ var shapeArea = geom.getPlanarPathArea(ids, nodes.arcs),
31050
+ sign = shapeArea > 0 ? 1 : -1,
31051
+ mainRing;
31052
+
31053
+ splitIds.reduce(function(max, ringIds, i) {
31054
+ var pathArea = geom.getPlanarPathArea(ringIds, nodes.arcs) * sign;
31055
+ if (pathArea > max) {
31056
+ mainRing = ringIds;
31057
+ max = pathArea;
31058
+ }
31059
+ return max;
31060
+ }, 0);
31061
+
31062
+ if (mainRing) {
31063
+ cleanedPolygon.push(mainRing);
31064
+ }
31065
+ }
31066
+ });
31067
+ return cleanedPolygon.length > 0 ? cleanedPolygon : null;
31068
+ }
31069
+ }
31070
+
31071
+ var PolygonRepair = /*#__PURE__*/Object.freeze({
31072
+ __proto__: null,
31073
+ getPolygonRewinder: getPolygonRewinder,
31074
+ repairPolygonGeometry: repairPolygonGeometry,
31075
+ repairSelfIntersections: repairSelfIntersections,
31076
+ rewindPolygonParts: rewindPolygonParts
31077
+ });
31078
+
31079
+ function dissolveBufferDataset2(dataset, optsArg) {
30929
31080
  var opts = optsArg || {};
30930
31081
  var lyr = dataset.layers[0];
30931
31082
  var tmp;
30932
- var nodes = addIntersectionCuts(dataset, {});
30933
- if (opts.debug_division) {
30934
- return debugBufferDivision(lyr, nodes);
31083
+ if (opts.debug_offset) {
31084
+ return; // raw offset path
30935
31085
  }
31086
+ var nodes = addIntersectionCuts(dataset, {rebuild_topology: true});
30936
31087
  var mosaicIndex = new MosaicIndex(lyr, nodes, {flat: false, no_holes: false});
31088
+
31089
+ // rewindPolygonParts(lyr, nodes);
31090
+ lyr.shapes = lyr.shapes.map(function(shp, i) {
31091
+ var tiles = mosaicIndex.getTilesByShapeIds([i]);
31092
+ if (!tiles.length) return null;
31093
+ return tiles.reduce(function(memo, tile) {
31094
+ return memo.concat(tile);
31095
+ }, []);
31096
+ });
31097
+
31098
+ if (opts.debug_winding) {
31099
+ return;
31100
+ }
31101
+
30937
31102
  if (opts.debug_mosaic) {
30938
31103
  tmp = composeMosaicLayer(lyr, mosaicIndex.mosaic);
30939
31104
  lyr.shapes = tmp.shapes;
@@ -30955,47 +31120,46 @@ ${svg}
30955
31120
  }
30956
31121
  }
30957
31122
 
30958
- function debugBufferDivision(lyr, nodes) {
30959
- var divide = getHoleDivider(nodes);
30960
- var shapes2 = [];
30961
- var records = [];
30962
- lyr.shapes.forEach(divideShape);
30963
- lyr.shapes = shapes2;
30964
- lyr.data = new DataTable(records);
30965
- return lyr;
30966
-
30967
- function divideShape(shp) {
30968
- var cw = [], ccw = [];
30969
- divide(shp, cw, ccw);
30970
- cw.forEach(function(ring) {
30971
- shapes2.push([ring]);
30972
- records.push({type: 'ring'});
30973
- });
30974
- ccw.forEach(function(hole) {
30975
- shapes2.push([reversePath(hole)]);
30976
- records.push({type: 'hole'});
30977
- });
30978
- }
30979
- }
30980
-
30981
- // n = number of segments used to approximate a circle
30982
- // Returns tolerance as a percent of circle radius
30983
- function getBufferToleranceFromCircleSegments(n) {
30984
- return 1 - Math.cos(Math.PI / n);
31123
+ // TODO: try geodesic option
31124
+ function getIntersectionFunction(crs) {
31125
+ return bufferIntersection$2;
30985
31126
  }
30986
31127
 
30987
- function getArcDegreesFromTolerancePct(pct) {
30988
- return 360 * Math.acos(1 - pct) / Math.PI;
30989
- }
30990
-
30991
- // n = number of segments used to approximate a circle
30992
- // Returns tolerance as a percent of circle radius
30993
- function getBufferToleranceFromCircleSegments2(n) {
30994
- return 1 / Math.cos(Math.PI / n) - 1;
30995
- }
31128
+ function dissolveBufferDataset(dataset, optsArg) {
31129
+ var opts = {};
31130
+ var lyr = dataset.layers[0];
31131
+ var tmp;
31132
+ if (opts.debug_offset) {
31133
+ return; // raw offset path
31134
+ }
31135
+ var nodes = addIntersectionCuts(dataset, {rebuild_topology: true});
31136
+ if (opts.debug_winding) {
31137
+ rewindPolygonParts(lyr, nodes);
31138
+ // debugRingsAndHoles(lyr, nodes);
31139
+ return;
31140
+ }
31141
+ rewindPolygonParts(lyr, nodes);
30996
31142
 
30997
- function getArcDegreesFromTolerancePct2(pct) {
30998
- return 360 * Math.acos(1 / (pct + 1)) / Math.PI;
31143
+ var mosaicIndex = new MosaicIndex(lyr, nodes, {flat: false, no_holes: false});
31144
+ if (opts.debug_mosaic) {
31145
+ tmp = composeMosaicLayer(lyr, mosaicIndex.mosaic);
31146
+ lyr.shapes = tmp.shapes;
31147
+ lyr.data = tmp.data;
31148
+ return;
31149
+ }
31150
+ var pathfind = getRingIntersector(mosaicIndex.nodes);
31151
+ var shapes2 = lyr.shapes.map(function(shp, shapeId) {
31152
+ var tiles = mosaicIndex.getTilesByShapeIds([shapeId]);
31153
+ var rings = [];
31154
+ for (var i=0; i<tiles.length; i++) {
31155
+ rings.push(tiles[i][0]);
31156
+ }
31157
+ return pathfind(rings, 'dissolve');
31158
+ });
31159
+ lyr.shapes = shapes2;
31160
+ if (!opts.no_dissolve) {
31161
+ dissolveArcs(dataset);
31162
+ }
30999
31163
  }
31000
31164
 
31001
31165
  // return constant distance in meters, or return null if unparsable
@@ -31005,16 +31169,6 @@ ${svg}
31005
31169
  return convertDistanceParam(str, crs) || null;
31006
31170
  }
31007
31171
 
31008
- function getBufferToleranceFunction(dataset, opts) {
31009
- var crs = getDatasetCRS(dataset);
31010
- var constTol = opts.tolerance ? parseConstantBufferDistance(opts.tolerance, crs) : 0;
31011
- var pctOfRadius = 1/100;
31012
- return function(meterDist) {
31013
- if (constTol) return constTol;
31014
- return constTol ? constTol : meterDist * pctOfRadius;
31015
- };
31016
- }
31017
-
31018
31172
  function getBufferDistanceFunction(lyr, dataset, opts) {
31019
31173
  if (!opts.radius) {
31020
31174
  stop('Missing expected radius parameter');
@@ -31033,110 +31187,536 @@ ${svg}
31033
31187
  };
31034
31188
  }
31035
31189
 
31036
- var BufferCommon = /*#__PURE__*/Object.freeze({
31190
+
31191
+ function bufferIntersection$2(a, b, c, d) {
31192
+ return bufferIntersection2(a[0], a[1], b[0], b[1], c[0], c[1], d[0], d[1]);
31193
+ }
31194
+
31195
+ // Exclude segments with non-intersecting bounding boxes before
31196
+ // calling intersection function
31197
+ // Possibly slightly faster than direct call... not worth it?
31198
+ function bufferIntersection2(ax, ay, bx, by, cx, cy, dx, dy) {
31199
+ if (ax < cx && ax < dx && bx < cx && bx < dx ||
31200
+ ax > cx && ax > dx && bx > cx && bx > dx ||
31201
+ ay < cy && ay < dy && by < cy && by < dy ||
31202
+ ay > cy && ay > dy && by > cy && by > dy) return null;
31203
+ return segmentIntersection(ax, ay, bx, by, cx, cy, dx, dy);
31204
+ }
31205
+
31206
+ function BufferBuilder(bufferIntersection, opts) {
31207
+ var self = {};
31208
+ var buffer, path, insideFlags;
31209
+ var points = [];
31210
+ var backtrackSteps = opts.backtrack >= 0 ? opts.backtrack : 100;
31211
+ var backtrackStopIdx = 0; // lowest vertex id in backtrack pass
31212
+
31213
+ init();
31214
+
31215
+ function init() {
31216
+ buffer = [];
31217
+ path = [];
31218
+ insideFlags = [];
31219
+ backtrackStopIdx = 0;
31220
+ }
31221
+
31222
+ self.size = function() {
31223
+ return path.length + buffer.length;
31224
+ };
31225
+
31226
+ self.getDebugPoints = function() {
31227
+ var tmp = points;
31228
+ points = [];
31229
+ return tmp;
31230
+ };
31231
+
31232
+ function makeDebugPoints() {
31233
+ points = points.concat(buffer.map((p, i) => {
31234
+ return {
31235
+ type: 'Feature',
31236
+ geometry: {
31237
+ type: 'Point',
31238
+ coordinates: p
31239
+ },
31240
+ properties: {
31241
+ id: i,
31242
+ inside: insideFlags[i],
31243
+ fill: insideFlags[i] ? 'magenta' : 'dodgerblue'
31244
+ }
31245
+ };
31246
+ }));
31247
+ }
31248
+
31249
+ self.addPathVertex = function(p) {
31250
+ path.push(p);
31251
+ };
31252
+
31253
+ self.addBufferVertices = function(arr) {
31254
+ for (var i=0; i<arr.length; i++) {
31255
+ self.addBufferVertex(arr[i]);
31256
+ }
31257
+ };
31258
+
31259
+ self.addBufferVertex = function(p, isLeftTurn) {
31260
+ var bufferLen = insideFlags.length;
31261
+ var prevIdx = bufferLen - 1;
31262
+ if (isLeftTurn) {
31263
+ // first extruded point after left turn
31264
+ // assume that the point is inside the buffer
31265
+ insideFlags.push(true);
31266
+ buffer.push(p);
31267
+ return;
31268
+ }
31269
+ if (prevIdx == -1) {
31270
+ // first point in buffer
31271
+ buffer.push(p);
31272
+ insideFlags.push(false);
31273
+ return;
31274
+ }
31275
+ var prevP = buffer[prevIdx];
31276
+ var prevFlag = insideFlags[prevIdx];
31277
+ if (pointsAreSame(prevP, p)) {
31278
+ // console.log('SAME POINT')
31279
+ return;
31280
+ }
31281
+ // if no segment cross is detected below, inside/outside stays the same
31282
+ var newFlag = prevFlag;
31283
+ var hit, a, b, flagA, flagB;
31284
+ for (var i=0, idx = bufferLen - 3; i < backtrackSteps && idx >= backtrackStopIdx; i++, idx--) {
31285
+ a = buffer[idx];
31286
+ b = buffer[idx + 1];
31287
+ // TODO: consider using a geodetic intersection function for lat-long datasets
31288
+ // TODO: consider case of an endpoint hit
31289
+ // TODO: consider case of collinear hit
31290
+ hit = bufferIntersection(a, b, prevP, p);
31291
+ if (!hit) {
31292
+ // continue scanning backward for an intersection
31293
+ continue;
31294
+ }
31295
+
31296
+ flagA = insideFlags[idx];
31297
+ flagB = insideFlags[idx + 1];
31298
+ if (flagA && flagB) {
31299
+ // newest segment collides with an interior segment - do not rewind
31300
+ continue;
31301
+ }
31302
+
31303
+ // newest segment collides with an exterior segment
31304
+ // * assume we're going from inside to outside
31305
+ // * and can therefore remove an interior loop
31306
+ // TODO: improve (this assumption does not hold when closing an oxbow type loop)
31307
+ while (buffer.length > idx + 1) {
31308
+ buffer.pop();
31309
+ insideFlags.pop();
31310
+ }
31311
+ buffer.push(hit);
31312
+ insideFlags.push(false);
31313
+ newFlag = false;
31314
+ // console.log("going out")
31315
+ break;
31316
+ }
31317
+
31318
+ buffer.push(p);
31319
+ insideFlags.push(newFlag);
31320
+ };
31321
+
31322
+ self.done = function() {
31323
+ if (opts.debug_points) {
31324
+ makeDebugPoints();
31325
+ }
31326
+ var ring = path.reverse().concat(buffer);
31327
+ if (ring.length < 3) {
31328
+ error('Defective buffer ring:', ring);
31329
+ }
31330
+ ring.push(ring[0]);
31331
+ init();
31332
+ return ring;
31333
+ };
31334
+
31335
+ return self;
31336
+ }
31337
+
31338
+ function pointsAreSame(a, b) {
31339
+ return a[0] === b[0] && a[1] === b[1];
31340
+ }
31341
+
31342
+ // function countExtendedHits(p0, p1, buffer) {
31343
+ // var bbox = getBbox();
31344
+ // var width = Math.max(bbox[2] - bbox[0], bbox[3] - bbox[1]);
31345
+ // var p2 = [extend(p1[0], p1[0] - p0[0], size), extend(p1[1], p1[1] - p0[1], size)];
31346
+ // var hits = 0;
31347
+ // for (var i=backtrackStopIdx, n = buffer.length-1; i<n; i++) {
31348
+ // if (bufferIntersection(buffer[i], buffer[i+1], p1, p2)) {
31349
+ // hits++;
31350
+ // }
31351
+ // }
31352
+ // return hits;
31353
+ // };
31354
+
31355
+ var R$1 = WGS84.SEMIMAJOR_AXIS;
31356
+
31357
+ // GeographicLib docs: https://geographiclib.sourceforge.io/html/js/
31358
+ // https://geographiclib.sourceforge.io/html/js/module-GeographicLib_Geodesic.Geodesic.html
31359
+ // https://geographiclib.sourceforge.io/html/js/tutorial-2-interface.html
31360
+ function getGeodesic(P) {
31361
+ if (!isLatLngCRS(P)) error('Expected an unprojected CRS');
31362
+ var f = P.es / (1 + Math.sqrt(P.one_es));
31363
+ // var GeographicLib = require('mproj').internal.GeographicLib;
31364
+ var GeographicLib = require$1('geographiclib-geodesic');
31365
+ // return new GeographicLib.Geodesic.Geodesic(P.a, 0)
31366
+ return new GeographicLib.Geodesic.Geodesic(P.a, f);
31367
+ }
31368
+
31369
+ function interpolatePoint2D(ax, ay, bx, by, k) {
31370
+ var j = 1 - k;
31371
+ return [ax * j + bx * k, ay * j + by * k];
31372
+ }
31373
+
31374
+ function getInterpolationFunction(P) {
31375
+ var spherical = P && isLatLngCRS(P);
31376
+ if (!spherical) return interpolatePoint2D;
31377
+ var geod = getGeodesic(P);
31378
+ return function(lng, lat, lng2, lat2, k) {
31379
+ var r = geod.Inverse(lat, lng, lat2, lng2);
31380
+ var dist = r.s12 * k;
31381
+ var r2 = geod.Direct(lat, lng, r.azi1, dist);
31382
+ return [r2.lon2, r2.lat2];
31383
+ };
31384
+ }
31385
+
31386
+ function getPlanarSegmentEndpoint(x, y, bearing, meterDist) {
31387
+ var rad = bearing / 180 * Math.PI;
31388
+ var dx = Math.sin(rad) * meterDist;
31389
+ var dy = Math.cos(rad) * meterDist;
31390
+ return [x + dx, y + dy];
31391
+ }
31392
+
31393
+ // source: https://github.com/mapbox/cheap-ruler/blob/master/index.js
31394
+ function fastGeodeticSegmentFunction(lng, lat, bearing, meterDist) {
31395
+ var D2R = Math.PI / 180;
31396
+ var cos = Math.cos(lat * D2R);
31397
+ var cos2 = 2 * cos * cos - 1;
31398
+ var cos3 = 2 * cos * cos2 - cos;
31399
+ var cos4 = 2 * cos * cos3 - cos2;
31400
+ var cos5 = 2 * cos * cos4 - cos3;
31401
+ var kx = (111.41513 * cos - 0.09455 * cos3 + 0.00012 * cos5) * 1000;
31402
+ var ky = (111.13209 - 0.56605 * cos2 + 0.0012 * cos4) * 1000;
31403
+ var bearingRad = bearing * D2R;
31404
+ var lat2 = lat + Math.cos(bearingRad) * meterDist / ky;
31405
+ var lng2 = lng + Math.sin(bearingRad) * meterDist / kx;
31406
+ return [lng2, lat2];
31407
+ }
31408
+
31409
+ function wrap(deg) {
31410
+ while (deg < -180) deg += 360;
31411
+ while (deg > 180) deg -= 360;
31412
+ return deg;
31413
+ }
31414
+
31415
+ function fastGeodeticBearingFunction(lng1, lat1, lng2, lat2) {
31416
+ var D2R = Math.PI / 180;
31417
+ var f = 1 / 298.257223563;
31418
+ var e2 = f * (2 - f);
31419
+ var m = R$1 * D2R;
31420
+ var coslat = Math.cos(lat1 * D2R);
31421
+ var w2 = 1 / (1 - e2 * (1 - coslat * coslat));
31422
+ var w = Math.sqrt(w2);
31423
+ var kx = m * w * coslat;
31424
+ var ky = m * w * w2 * (1 - e2);
31425
+ var dx = wrap(lng2 - lng1) * kx;
31426
+ var dy = (lat2 - lat1) * ky;
31427
+ return Math.atan2(dx, dy) / D2R;
31428
+ }
31429
+
31430
+ function getGeodeticSegmentFunction(P) {
31431
+ if (!isLatLngCRS(P)) {
31432
+ return getPlanarSegmentEndpoint;
31433
+ }
31434
+ var g = getGeodesic(P);
31435
+ return function(lng, lat, bearing, meterDist) {
31436
+ var o = g.Direct(lat, lng, bearing, meterDist);
31437
+ var p = [o.lon2, o.lat2];
31438
+ return p;
31439
+ };
31440
+ }
31441
+
31442
+ function getFastGeodeticSegmentFunction(P) {
31443
+ // CAREFUL: this function has higher error at very large distances and at the poles
31444
+ // also, it wouldn't work for other planets than Earth
31445
+ return isLatLngCRS(P) ? fastGeodeticSegmentFunction : getPlanarSegmentEndpoint;
31446
+ }
31447
+
31448
+ // return function to calculate bearing of a segment in degrees
31449
+ function getBearingFunction(dataset) {
31450
+ var P = getDatasetCRS(dataset);
31451
+ // var g = getGeodesic(P);
31452
+ // if (isLatLngCRS) {
31453
+ // return function(lng1, lat1, lng2, lat2) {
31454
+ // var tmp = g.Inverse(lat1, lng1, lat2, lng2);
31455
+ // return tmp.azi1;
31456
+ // // return bearingDegrees(lng1, lat1, lng2, lat2);
31457
+ // };
31458
+ // }
31459
+ // return isLatLngCRS(P) ? bearingDegrees : bearingDegrees2D;
31460
+ return isLatLngCRS(P) ? fastGeodeticBearingFunction : bearingDegrees2D;
31461
+ }
31462
+
31463
+ // get bearing in degrees from point ab to point cd
31464
+ function bearingDegrees(a, b, c, d) {
31465
+ return geom.bearing(a, b, c, d) * 180 / Math.PI;
31466
+ }
31467
+
31468
+ function bearingDegrees2D(a, b, c, d) {
31469
+ return geom.bearing2D(a, b, c, d) * 180 / Math.PI;
31470
+ }
31471
+
31472
+ var Geodesic = /*#__PURE__*/Object.freeze({
31037
31473
  __proto__: null,
31038
- dissolveBufferDataset: dissolveBufferDataset,
31039
- getArcDegreesFromTolerancePct: getArcDegreesFromTolerancePct,
31040
- getArcDegreesFromTolerancePct2: getArcDegreesFromTolerancePct2,
31041
- getBufferDistanceFunction: getBufferDistanceFunction,
31042
- getBufferToleranceFromCircleSegments: getBufferToleranceFromCircleSegments,
31043
- getBufferToleranceFromCircleSegments2: getBufferToleranceFromCircleSegments2,
31044
- getBufferToleranceFunction: getBufferToleranceFunction,
31045
- parseConstantBufferDistance: parseConstantBufferDistance
31474
+ bearingDegrees: bearingDegrees,
31475
+ bearingDegrees2D: bearingDegrees2D,
31476
+ getBearingFunction: getBearingFunction,
31477
+ getFastGeodeticSegmentFunction: getFastGeodeticSegmentFunction,
31478
+ getGeodeticSegmentFunction: getGeodeticSegmentFunction,
31479
+ getInterpolationFunction: getInterpolationFunction,
31480
+ getPlanarSegmentEndpoint: getPlanarSegmentEndpoint,
31481
+ interpolatePoint2D: interpolatePoint2D
31046
31482
  });
31047
31483
 
31048
- // Returns a function for generating GeoJSON geometries (MultiLineString or MultiPolygon)
31049
- function getPolylineBufferMaker(arcs, geod, getBearing, opts) {
31050
- var maker = getPathBufferMaker(arcs, geod, getBearing, opts);
31051
- var geomType = opts.geometry_type;
31052
- // polyline output could be used for debugging
31053
- var outputGeom = opts.output_geometry == 'polyline' ? 'polyline' : 'polygon';
31484
+ // Returns a function for generating GeoJSON MultiPolygon geometries
31485
+ function getPolylineBufferMaker$2(dataset, opts) {
31486
+ // var sliceLen = opts.slice_length || Infinity;
31487
+ var crs = getDatasetCRS(dataset);
31488
+ var geod = getFastGeodeticSegmentFunction(crs);
31489
+ var bufferIntersection = getIntersectionFunction();
31490
+ var getBearing = getBearingFunction(dataset);
31491
+ var segsPerQuadrant = opts.arc_quality >= 2 ? opts.arc_quality : 12;
31492
+ var segAngle = 360 / segsPerQuadrant / 4;
31493
+ var capStyle = opts.cap_style || 'round'; // expect 'round' or 'flat'
31494
+ var pathIter = new ShapeIter(dataset.arcs);
31495
+ var builder = new BufferBuilder(bufferIntersection, opts);
31054
31496
 
31055
- function pathBufferCoords(pathArcs, dist) {
31056
- var pathCoords = maker(pathArcs, dist);
31497
+ return function makeBufferGeoJSON(shape, distance) {
31498
+ var rings = [];
31499
+ (shape || []).forEach(function(path, i) {
31500
+ var pathRings = makeSinglePathRings(path, distance);
31501
+ rings = rings.concat(pathRings);
31502
+ });
31503
+ if (rings.length === 0) return null;
31504
+ var feat = {
31505
+ type: 'Feature',
31506
+ properties: null,
31507
+ geometry: {
31508
+ type: 'MultiPolygon',
31509
+ coordinates: rings.map(ring => [ring]) // to Polygon format
31510
+ }
31511
+ };
31512
+ if (opts.debug_points) {
31513
+ return [feat].concat(builder.getDebugPoints());
31514
+ }
31515
+ return feat;
31516
+ };
31517
+
31518
+ // each path may be converted into multiple buffer rings, which later
31519
+ // need to be dissolved
31520
+ function makeSinglePathRings(pathArcs, dist) {
31057
31521
  var revPathArcs;
31058
- if (geomType == 'polyline') {
31522
+ var rings = [];
31523
+ if (!opts.right || opts.left) {
31524
+ rings = rings.concat(makeLeftBufferRings(pathArcs, dist));
31525
+ }
31526
+ if (!opts.left || opts.right) {
31059
31527
  revPathArcs = reversePath(pathArcs.concat());
31060
- pathCoords = pathCoords.concat(maker(revPathArcs, dist));
31528
+ rings = rings.concat(makeLeftBufferRings(revPathArcs, dist));
31061
31529
  }
31062
- pathCoords.push(pathCoords[0]); // close path
31063
- return outputGeom == 'polyline' ? pathCoords : [pathCoords];
31530
+ return rings;
31064
31531
  }
31065
31532
 
31066
- return function(shape, dist) {
31067
- var geom = {
31068
- type: outputGeom == 'polyline' ? 'MultiLineString' : 'MultiPolygon',
31069
- coordinates: []
31070
- };
31071
- for (var i=0; i<shape.length; i++) {
31072
- geom.coordinates.push(pathBufferCoords(shape[i], dist));
31533
+ function makeLeftBufferRings(path, dist) {
31534
+ var rings = [];
31535
+ var x0, y0, x1, y1, x2, y2; // path traversal coords
31536
+ var p1, p2; // extruded points
31537
+ var p1Prev, p2Prev;
31538
+ var bearing1, bearing2, bearing2Prev, joinAngle, hit;
31539
+ var firstBearing;
31540
+ var joinPoints;
31541
+ var i = 0;
31542
+ pathIter.init(path);
31543
+
31544
+ if (pathIter.hasNext()) {
31545
+ x0 = x2 = pathIter.x;
31546
+ y0 = y2 = pathIter.y;
31547
+ builder.addPathVertex([x0, y0]); // start building the path side
31548
+ i++;
31073
31549
  }
31074
- return geom.coordinates.length == 0 ? null : geom;
31075
- };
31076
- }
31077
31550
 
31551
+ while (pathIter.hasNext()) {
31552
+ // TODO: use a tolerance
31553
+ if (pathIter.x === x2 && pathIter.y === y2) {
31554
+ debug("skipping a duplicate point");
31555
+ continue;
31556
+ }
31078
31557
 
31079
- function getPathBufferMaker(arcs, geod, getBearing, opts) {
31558
+ x1 = x2;
31559
+ y1 = y2;
31560
+ x2 = pathIter.x;
31561
+ y2 = pathIter.y;
31562
+ builder.addPathVertex([x2, y2]); // extend path
31080
31563
 
31081
- var backtrackSteps = opts.backtrack >= 0 ? opts.backtrack : 50;
31082
- var pathIter = new ShapeIter(arcs);
31083
- var capStyle = opts.cap_style || 'round'; // expect 'round' or 'flat'
31084
- // var tolerance;
31085
- // TODO: implement other join styles than round
31564
+ // bearing (direction) of the segment is slightly different at the first
31565
+ // and second endpoint, using geodesic math
31566
+ // TODO: no need to calculate twice with planar coordinates
31567
+ bearing1 = getBearing(x1, y1, x2, y2);
31568
+ bearing2 = getBearing(x2, y2, x1, y1) - 180;
31569
+ // extrude current segment to the left
31570
+ p1 = geod(x1, y1, bearing1 - 90, dist);
31571
+ p2 = geod(x2, y2, bearing2 - 90, dist);
31086
31572
 
31087
- function addRoundJoin(arr, x, y, startDir, angle, dist) {
31088
- var increment = 10;
31089
- var endDir = startDir + angle;
31090
- var dir = startDir + increment;
31091
- while (dir < endDir) {
31092
- addBufferVertex(arr, geod(x, y, dir, dist), backtrackSteps);
31093
- dir += increment;
31573
+ if (i == 1) {
31574
+ firstBearing = bearing1;
31575
+ } else {
31576
+ joinAngle = getJoinAngle(bearing2Prev, bearing1);
31577
+ }
31578
+
31579
+ // various connections between current extruded segment and prev segment
31580
+ if (i == 1) {
31581
+ // first segment - no join
31582
+ builder.addBufferVertex(p1, false);
31583
+ } else if (joinAngle > segAngle * 1.5) {
31584
+ // round join
31585
+ // builder.addBufferVertex(p2Prev, false) // testing
31586
+ joinPoints = makeOutsideRoundJoin(x1, y1, bearing2Prev - 90, joinAngle, dist);
31587
+ builder.addBufferVertices(joinPoints);
31588
+ // builder.addBufferVertex(p1, false) // testing
31589
+ p1 = joinPoints.pop();
31590
+ } else if (joinAngle > 0 && (hit = elbowJoin(p1Prev, p2Prev, p1, p2)) ||
31591
+ joinAngle < 0 && (hit = bufferIntersection(p1Prev, p2Prev, p1, p2))) {
31592
+ // elbow join (concave or convex)
31593
+ builder.addBufferVertex(hit, false);
31594
+ p1 = hit;
31595
+ } else if (joinAngle == 0) {
31596
+ // collinear segments (can we really skip p2Prev on a sphere?)
31597
+ builder.addBufferVertex(p1, false);
31598
+ } else {
31599
+ // if (joinAngle > 0) {
31600
+ // probably a leftward bend and extruded segments do not intersect
31601
+ // // console.log("UNEXPECTED SEGMENT JOIN")
31602
+ // }
31603
+ builder.addBufferVertex(p2Prev);
31604
+ builder.addBufferVertex(p1, joinAngle < 0);
31605
+ }
31606
+
31607
+ bearing2Prev = bearing2;
31608
+ p1Prev = p1;
31609
+ p2Prev = p2;
31610
+ i++;
31611
+ }
31612
+
31613
+ // TODO: add this to cap and join code below
31614
+ if (p2Prev) {
31615
+ builder.addBufferVertex(p2Prev);
31616
+ }
31617
+
31618
+ if (x2 == x0 && y2 == y0) { // closed path
31619
+ // add join to finish closed path
31620
+ // TODO - figure out which bearing to use
31621
+ joinAngle = getJoinAngle(bearing2, firstBearing);
31622
+ if (joinAngle > 0) {
31623
+ builder.addBufferVertices(makeFinalJoin(x2, y2, bearing1 - 90, joinAngle, dist));
31624
+ }
31625
+ } else if (capStyle == 'round') { // open path
31626
+ // add a cap to finish open path
31627
+ builder.addBufferVertices(makeRoundCap(x2, y2, bearing2 - 90, dist));
31094
31628
  }
31629
+
31630
+ rings.push(builder.done());
31631
+ return rings;
31095
31632
  }
31096
31633
 
31097
- // function addRoundJoin2(arr, x, y, startDir, angle, dist) {
31098
- // var increment = 10;
31099
- // var endDir = startDir + angle;
31100
- // var dir = startDir + increment;
31101
- // while (dir < endDir) {
31102
- // addBufferVertex(arr, geod(x, y, dir, dist), backtrackSteps);
31103
- // dir += increment;
31104
- // }
31634
+ // function extendArray(arr, arr2) {
31635
+ // arr2.reverse();
31636
+ // while(arr2.length > 0) arr.push(arr2.pop());
31105
31637
  // }
31106
31638
 
31107
- // Test if two points are within a snapping tolerance
31108
- // TODO: calculate the tolerance more sensibly
31109
- function veryClose(x1, y1, x2, y2, tol) {
31110
- var dist = geom.distance2D(x1, y1, x2, y2);
31111
- return dist < tol;
31639
+ function makeFinalJoin(x, y, direction, angle, dist) {
31640
+ var points = makeRoundJoin(x, y, direction, angle, dist);
31641
+ points.push(geod(x, y, direction + angle, dist));
31642
+ return points;
31112
31643
  }
31113
31644
 
31114
- function veryCloseToPrevPoint(arr, x, y) {
31115
- var prev = arr[arr.length - 1];
31116
- return veryClose(prev[0], prev[1], x, y, 0.000001);
31645
+ function makeRoundCap(x, y, startDir, dist) {
31646
+ var points = makeRoundJoin(x, y, startDir, 180, dist);
31647
+ points.push(geod(x, y, startDir + 180, dist)); // add final vertex
31648
+ return points;
31117
31649
  }
31118
31650
 
31119
- function appendPoint(arr, p) {
31120
- var prev = arr[arr.length - 1];
31121
- if (!veryClose(prev[0], prev[1], p[0], p[1], 1e-10)) {
31122
- arr.push(p);
31651
+ // The vertices of this join are outside of the arc that it approximates and
31652
+ // the segments of the join touch the arc at their midpoints.
31653
+ // The first and last of the returned vertices extend the segments on either
31654
+ // side of the join.
31655
+ function makeOutsideRoundJoin(cx, cy, startBearing, arcAngle, dist) {
31656
+ // point count of 1 would be an elbow joint
31657
+ // (elbow joins should be created elsewhere)
31658
+ var pointCount = Math.max(1, Math.round(arcAngle / segAngle));
31659
+ var stepAngle = arcAngle / pointCount;
31660
+ var points = [];
31661
+ var i = 0;
31662
+ var a, b, c, d, joinP, tanP, bearing;
31663
+ while (i <= pointCount) {
31664
+ bearing = startBearing + stepAngle * i;
31665
+ tanP = geod(cx, cy, bearing, dist);
31666
+ c = geod(tanP[0], tanP[1], bearing - 90, dist * 2);
31667
+ d = geod(tanP[0], tanP[1], bearing + 90, dist * 2);
31668
+ if (i > 0) {
31669
+ joinP = bufferIntersection(a, b, c, d);
31670
+ if (!joinP) {
31671
+ throw Error(`no intersection on ${i} of ${pointCount}`);
31672
+ } else {
31673
+ points.push(joinP);
31674
+ }
31675
+ }
31676
+ a = c;
31677
+ b = d;
31678
+ i++;
31123
31679
  }
31680
+ return points;
31124
31681
  }
31125
31682
 
31126
- function makeCap(x, y, direction, dist) {
31127
- if (capStyle == 'flat') {
31128
- return [[x, y]];
31129
- }
31130
- return makeRoundCap(x, y, direction, dist);
31683
+ function elbowJoin(a, b, c, d) {
31684
+ var k = 2 ** 13;
31685
+ var b2 = [extend(b[0], b[0] - a[0], k), extend(b[1], b[1] - a[1], k)];
31686
+ var c2 = [extend(c[0], c[0] - d[0], k), extend(c[1], c[1] - d[1], k)];
31687
+ return bufferIntersection(a, b2, c2, d);
31131
31688
  }
31132
31689
 
31133
- function makeRoundCap(x, y, segmentDir, dist) {
31690
+
31691
+ // function getBbox() {
31692
+ // var lastIdx = buffer.length - 1;
31693
+ // var x, y;
31694
+ // if (!_bbox) {
31695
+ // _bbox = [-Infinity, -Infinity, Infinity, Infinity];
31696
+ // }
31697
+ // while (_idx < lastIdx) {
31698
+ // _idx++;
31699
+ // x = buffer[_idx][0];
31700
+ // y = buffer[_idx][1];
31701
+ // _bbox[0] = Math.min(x, _bbox[0]);
31702
+ // _bbox[1] = Math.min(y, _bbox[1]);
31703
+ // _bbox[2] = Math.max(x, _bbox[2]);
31704
+ // _bbox[3] = Math.max(y, _bbox[3]);
31705
+ // }
31706
+ // return _bbox;
31707
+ // }
31708
+
31709
+ function extend(n, d, k) {
31710
+ return n + d * k;
31711
+ }
31712
+
31713
+ // get interior vertices of an interpolated CW arc
31714
+ function makeRoundJoin(cx, cy, startBearing, arcAngle, dist) {
31134
31715
  var points = [];
31135
- var increment = 10;
31136
- var startDir = segmentDir - 90;
31716
+ var increment = 90 / segsPerQuadrant;
31137
31717
  var angle = increment;
31138
- while (angle < 180) {
31139
- points.push(geod(x, y, startDir + angle, dist));
31718
+ while (angle < arcAngle) {
31719
+ points.push(geod(cx, cy, startBearing + angle, dist));
31140
31720
  angle += increment;
31141
31721
  }
31142
31722
  return points;
@@ -31156,113 +31736,259 @@ ${svg}
31156
31736
  return delta;
31157
31737
  }
31158
31738
 
31159
- function addBufferVertex(arr, d, maxBacktrack) {
31160
- var a, b, c, hit;
31161
- for (var i=0, idx = arr.length - 3; i<maxBacktrack && idx >= 0; i++, idx--) {
31162
- c = arr[arr.length - 1];
31163
- a = arr[idx];
31164
- b = arr[idx + 1];
31165
- // TODO: consider using a geodetic intersection function for lat-long datasets
31166
- hit = bufferIntersection(a[0], a[1], b[0], b[1], c[0], c[1], d[0], d[1]);
31167
- if (hit) {
31168
- // TODO: handle collinear segments
31169
- // if (hit.length != 2) console.log('COLLINEAR', hit)
31170
- // segments intersect -- replace two internal segment endpoints with xx point
31171
- while (arr.length > idx + 1) arr.pop();
31172
- appendPoint(arr, hit);
31173
- }
31174
- }
31739
+ }
31175
31740
 
31176
- appendPoint(arr, d);
31741
+ // Returns a function for generating GeoJSON MultiPolygon geometries
31742
+ function getPolylineBufferMaker$1(dataset, opts) {
31743
+ var geod = getFastGeodeticSegmentFunction(getDatasetCRS(dataset));
31744
+ var getBearing = getBearingFunction(dataset);
31745
+ var sliceLen = opts.slice_length || Infinity;
31746
+ var backtrackSteps = opts.backtrack >= 0 ? opts.backtrack : 100;
31747
+ var segsPerQuadrant = opts.arc_quality >= 2 ? opts.arc_quality : 12;
31748
+ var capStyle = opts.cap_style || 'round'; // expect 'round' or 'flat'
31749
+ var pathIter = new ShapeIter(dataset.arcs);
31750
+ var left, center, rings;
31751
+
31752
+ return function makeBufferGeoJSON(shape, distance) {
31753
+ var rings = [];
31754
+ (shape || []).forEach(function(path, i) {
31755
+ var pathRings = makeSinglePathRings(path, distance);
31756
+ rings = rings.concat(pathRings);
31757
+ });
31758
+ if (rings.length === 0) return null;
31759
+ return {
31760
+ type: 'MultiPolygon',
31761
+ coordinates: rings.map(ring => [ring]) // to Polygon format
31762
+ };
31763
+ };
31764
+
31765
+ // each path may be converted into multiple buffer rings, which later
31766
+ // need to be dissolved
31767
+ function makeSinglePathRings(pathArcs, dist) {
31768
+ var revPathArcs;
31769
+ var rings = [];
31770
+ if (!opts.right || opts.left) {
31771
+ rings = rings.concat(makeLeftBufferRings(pathArcs, dist));
31772
+ }
31773
+ if (!opts.left || opts.right) {
31774
+ revPathArcs = reversePath(pathArcs.concat());
31775
+ rings = rings.concat(makeLeftBufferRings(revPathArcs, dist));
31776
+ }
31777
+ return rings;
31177
31778
  }
31178
31779
 
31179
- return function(path, dist) {
31180
- var left = [];
31181
- var x0, y0, x1, y1, x2, y2;
31182
- var p1, p2;
31183
- var bearing, prevBearing, firstBearing, joinAngle;
31780
+ function makeLeftBufferRings(path, dist) {
31781
+ left = [];
31782
+ center = [];
31783
+ rings = [];
31784
+ var x0, y0, x1, y1, x2, y2; // path traversal coords
31785
+ var p1, p2; // extruded points
31786
+ var prevP;
31787
+ var bearing1, bearing2, prevBearing, joinAngle;
31788
+ var firstBearing;
31184
31789
  var i = 0;
31185
31790
  pathIter.init(path);
31186
31791
 
31792
+ if (pathIter.hasNext()) {
31793
+ x0 = x2 = pathIter.x;
31794
+ y0 = y2 = pathIter.y;
31795
+ i++;
31796
+ }
31797
+
31187
31798
  while (pathIter.hasNext()) {
31188
31799
  // TODO: use a tolerance
31189
- if (pathIter.x === x2 && pathIter.y === y2) continue; // skip duplicate points
31800
+ if (pathIter.x === x2 && pathIter.y === y2) {
31801
+ debug("skipping a duplicate point");
31802
+ continue;
31803
+ }
31190
31804
  x1 = x2;
31191
31805
  y1 = y2;
31192
31806
  x2 = pathIter.x;
31193
31807
  y2 = pathIter.y;
31194
- if (i >= 1) {
31195
- prevBearing = bearing;
31196
- bearing = getBearing(x1, y1, x2, y2);
31197
- p1 = geod(x1, y1, bearing - 90, dist);
31198
- p2 = geod(x2, y2, bearing - 90, dist);
31199
- // left.push([x1, y1], p1) // debug extrusion lines
31200
- // left.push([x2, y2], p2) // debug extrusion lines
31201
- }
31808
+
31809
+ // calculate bearing at both segment points
31810
+ // TODO: no need to calculate twice with planar coordinates
31811
+ prevBearing = bearing2;
31812
+ bearing1 = getBearing(x1, y1, x2, y2);
31813
+ bearing2 = getBearing(x2, y2, x1, y1) - 180;
31814
+ // extrude current segment to the left
31815
+ prevP = p2;
31816
+ p1 = geod(x1, y1, bearing1 - 90, dist);
31817
+ p2 = geod(x2, y2, bearing2 - 90, dist);
31818
+
31202
31819
  if (i == 1) {
31203
- firstBearing = bearing;
31204
- x0 = x1;
31205
- y0 = y1;
31206
- left.push(p1, p2);
31820
+ firstBearing = bearing1;
31821
+ } else {
31822
+ joinAngle = getJoinAngle(prevBearing, bearing1);
31207
31823
  }
31208
- if (i > 1) {
31209
- joinAngle = getJoinAngle(prevBearing, bearing);
31210
- if (veryCloseToPrevPoint(left, p1[0], p1[1])) {
31211
- // skip first point
31212
- addBufferVertex(left, p2, backtrackSteps);
31213
- } else if (joinAngle > 0) {
31214
- addRoundJoin(left, x1, y1, prevBearing - 90, joinAngle, dist);
31215
- addBufferVertex(left, p1, backtrackSteps);
31216
- addBufferVertex(left, p2, backtrackSteps);
31217
- } else {
31218
- addBufferVertex(left, p1, backtrackSteps);
31219
- addBufferVertex(left, p2, backtrackSteps);
31824
+
31825
+ // extend center coords (i.e. original path)
31826
+ if (center.length == 0) {
31827
+ center.push([x1, y1]);
31828
+ }
31829
+ center.push([x2, y2]);
31830
+
31831
+ // if (veryCloseToPrevPoint(left, p1[0], p1[1])) {
31832
+ // console.log("VERY CLOSE")
31833
+ // // skip first point
31834
+ // addBufferVertex(p2);
31835
+ // }
31836
+
31837
+ if (i > 1 && joinAngle > 0) {
31838
+ if (prevP) {
31839
+ // start join from last extruded vertex of previous buffer slice
31840
+ addBufferVertex(prevP);
31220
31841
  }
31842
+ makeRoundJoin(x1, y1, prevBearing - 90, joinAngle, dist).forEach(addBufferVertex);
31843
+ addBufferVertex(p1);
31844
+ addBufferVertex(p2);
31845
+ // left.push(p1, p2)
31846
+ } else {
31847
+ // left.push(p1, p2)
31848
+ addBufferVertex(p1);
31849
+ addBufferVertex(p2);
31850
+ }
31851
+
31852
+ // TODO: finish
31853
+ if (center.length - 1 >= sliceLen) {
31854
+ finishRing();
31221
31855
  }
31222
31856
  i++;
31223
31857
  }
31224
- // TODO: handle defective polylines
31225
31858
 
31226
- if (x2 == x0 && y2 == y0) {
31859
+ if (center.length > 1) {
31860
+ finishRing();
31861
+ }
31862
+
31863
+ if (x2 == x0 && y2 == y0) { // closed path
31227
31864
  // add join to finish closed path
31228
- joinAngle = getJoinAngle(bearing, firstBearing);
31865
+ // TODO - figure out which bearing to use
31866
+ joinAngle = getJoinAngle(bearing2, firstBearing);
31229
31867
  if (joinAngle > 0) {
31230
- addRoundJoin(left, x2, y2, bearing - 90, joinAngle, dist);
31868
+ appendJoinToRing(rings[rings.length-1], x2, y2, bearing1 - 90, joinAngle, dist);
31231
31869
  }
31232
- } else {
31870
+ } else { // open path
31233
31871
  // add a cap to finish open path
31234
- left.push.apply(left, makeCap(x2, y2, bearing, dist));
31872
+ // left.push.apply(left, makeCap(x2, y2, bearing, dist));
31873
+ appendCapToRing(rings[rings.length-1], x2, y2, bearing2, dist);
31235
31874
  }
31236
- return left;
31237
- };
31238
- }
31239
31875
 
31240
- function addBufferVertex(arr, d, maxBacktrack) {
31241
- var a, b, c, hit;
31242
- for (var i=0, idx = arr.length - 3; i<maxBacktrack && idx >= 0; i++, idx--) {
31876
+ return rings;
31877
+ }
31878
+
31879
+ function finishRing() {
31880
+ if (center.length < 2) {
31881
+ debug('defective path, skipping');
31882
+ return;
31883
+ }
31884
+ var ring = center.reverse().concat(left);
31885
+ ring.push(ring[0]);
31886
+
31887
+ // Start next partial (if there is one)
31888
+ left = [];
31889
+ center = [];
31890
+ rings.push(ring);
31891
+ }
31892
+
31893
+ // function extendArray(arr, arr2) {
31894
+ // arr2.reverse();
31895
+ // while(arr2.length > 0) arr.push(arr2.pop());
31896
+ // }
31897
+
31898
+ function appendJoinToRing(ring, x, y, direction, angle, dist) {
31899
+ var p1 = ring.pop();
31900
+ var coords = makeRoundJoin(x, y, direction, angle, dist);
31901
+ ring.push.apply(ring, coords);
31902
+ ring.push(geod(x, y, direction + angle, dist));
31903
+ ring.push(p1);
31904
+ }
31905
+
31906
+ function appendCapToRing(ring, x, y, direction, dist) {
31907
+ if (capStyle == 'flat' || ring.length < 4) {
31908
+ return;
31909
+ }
31910
+ var p1 = ring.pop();
31911
+ var coords = makeRoundCap(x, y, direction - 90, dist);
31912
+ ring.push.apply(ring, coords);
31913
+ ring.push(p1);
31914
+ }
31915
+
31916
+ function makeRoundCap(x, y, startDir, dist) {
31917
+ var points = makeRoundJoin(x, y, startDir, 180, dist);
31918
+ points.push(geod(x, y, startDir + 180, dist)); // add final vertex
31919
+ return points;
31920
+ }
31921
+
31922
+ // get interior vertices of an interpolated CW arc
31923
+ function makeRoundJoin(cx, cy, startBearing, arcAngle, dist) {
31924
+ var points = [];
31925
+ var increment = 90 / segsPerQuadrant;
31926
+ var angle = increment;
31927
+ while (angle < arcAngle) {
31928
+ points.push(geod(cx, cy, startBearing + angle, dist));
31929
+ angle += increment;
31930
+ }
31931
+ return points;
31932
+ }
31933
+
31934
+ // get angle between two extruded segments in degrees
31935
+ // positive angle means join in convex (range: 0-180 degrees)
31936
+ // negative angle means join is concave (range: -180-0 degrees)
31937
+ function getJoinAngle(direction1, direction2) {
31938
+ var delta = direction2 - direction1;
31939
+ if (delta > 180) {
31940
+ delta -= 360;
31941
+ }
31942
+ if (delta < -180) {
31943
+ delta += 360;
31944
+ }
31945
+ return delta;
31946
+ }
31947
+
31948
+
31949
+ function addBufferVertex(d) {
31950
+ var arr = left;
31951
+ var a, b, c, hit;
31952
+ // c is the start point of the segment formed by appending point d to the polyline.
31243
31953
  c = arr[arr.length - 1];
31244
- a = arr[idx];
31245
- b = arr[idx + 1];
31246
- // TODO: consider using a geodetic intersection function for lat-long datasets
31247
- hit = bufferIntersection(a[0], a[1], b[0], b[1], c[0], c[1], d[0], d[1]);
31248
- if (hit) {
31249
- // TODO: handle collinear segments
31250
- if (hit.length != 2) ;
31251
- // segments intersect -- replace two internal segment endpoints with xx point
31252
- while (arr.length > idx + 1) arr.pop();
31253
- // TODO: check proximity of hit to several points
31254
- arr.push(hit);
31954
+ for (var i=0, idx = arr.length - 3; i < backtrackSteps && idx >= 0; i++, idx--) {
31955
+ a = arr[idx];
31956
+ b = arr[idx + 1];
31957
+ // TODO: consider using a geodetic intersection function for lat-long datasets
31958
+ hit = bufferIntersection$1(a[0], a[1], b[0], b[1], c[0], c[1], d[0], d[1]);
31959
+ // TODO: disregard hits caused by oxbows
31960
+ if (hit) {
31961
+ // TODO: handle collinear segments
31962
+ // if (hit.length != 2) console.log('COLLINEAR', hit)
31963
+ // segments intersect -- replace two internal segment endpoints with xx point
31964
+ while (arr.length > idx + 1) arr.pop();
31965
+ appendPoint(arr, hit);
31966
+ c = hit; // update starting point of the newly added segment
31967
+ }
31255
31968
  }
31969
+ appendPoint(arr, d);
31256
31970
  }
31257
31971
 
31258
- // TODO: check proximity to previous point
31259
- arr.push(d); // add new point
31972
+ }
31973
+
31974
+ // Test if two points are within a snapping tolerance
31975
+ // TODO: calculate the tolerance more sensibly
31976
+ function veryClose(x1, y1, x2, y2, tol) {
31977
+ var dist = geom.distance2D(x1, y1, x2, y2);
31978
+ return dist < tol;
31979
+ }
31980
+
31981
+ function appendPoint(arr, p) {
31982
+ var prev = arr[arr.length - 1];
31983
+ if (!prev || !veryClose(prev[0], prev[1], p[0], p[1], 1e-10)) {
31984
+ arr.push(p);
31985
+ }
31260
31986
  }
31261
31987
 
31262
31988
  // Exclude segments with non-intersecting bounding boxes before
31263
31989
  // calling intersection function
31264
31990
  // Possibly slightly faster than direct call... not worth it?
31265
- function bufferIntersection(ax, ay, bx, by, cx, cy, dx, dy) {
31991
+ function bufferIntersection$1(ax, ay, bx, by, cx, cy, dx, dy) {
31266
31992
  if (ax < cx && ax < dx && bx < cx && bx < dx ||
31267
31993
  ax > cx && ax > dx && bx > cx && bx > dx ||
31268
31994
  ay < cy && ay < dy && by < cy && by < dy ||
@@ -31270,84 +31996,53 @@ ${svg}
31270
31996
  return geom.segmentIntersection(ax, ay, bx, by, cx, cy, dx, dy);
31271
31997
  }
31272
31998
 
31273
- var PathBuffer = /*#__PURE__*/Object.freeze({
31274
- __proto__: null,
31275
- addBufferVertex: addBufferVertex,
31276
- bufferIntersection: bufferIntersection,
31277
- getPolylineBufferMaker: getPolylineBufferMaker
31278
- });
31279
-
31280
- function getPolylineBufferMaker2(arcs, geod, getBearing, opts) {
31281
- var makeLeftBuffer = getPathBufferMaker2(arcs, geod, getBearing, opts);
31999
+ // Returns a function for generating GeoJSON geometries (MultiLineString or MultiPolygon)
32000
+ function getPolylineBufferMaker(dataset, opts) {
32001
+ var geod = getFastGeodeticSegmentFunction(getDatasetCRS(dataset));
32002
+ var getBearing = getBearingFunction(dataset);
32003
+ var maker = getPathBufferMaker(dataset.arcs, geod, getBearing, opts);
31282
32004
  var geomType = opts.geometry_type;
32005
+ // polyline output could be used for debugging
32006
+ var outputGeom = opts.output_geometry == 'polyline' ? 'polyline' : 'polygon';
31283
32007
 
31284
- function polygonCoords(ring) {
31285
- return [ring];
31286
- }
31287
-
31288
- function needLeftBuffer(path, arcs) {
32008
+ function pathBufferCoords(pathArcs, dist) {
32009
+ var pathCoords = maker(pathArcs, dist);
32010
+ var revPathArcs;
31289
32011
  if (geomType == 'polyline') {
31290
- return opts.type != 'right';
31291
- }
31292
- // assume polygon type
31293
- if (opts.type == 'outer') {
31294
- return geom.getPathWinding(path, arcs) == 1;
31295
- }
31296
- if (opts.type == 'inner') {
31297
- return geom.getPathWinding(path, arcs) == -1;
31298
- }
31299
- return true;
31300
- }
31301
-
31302
- function needRightBuffer() {
31303
- return geomType == 'polyline' && opts.type != 'left';
31304
- }
31305
-
31306
- function makeBufferParts(pathArcs, dist) {
31307
- var leftPartials, rightPartials, parts, revPathArcs;
31308
-
31309
- if (needLeftBuffer(pathArcs, arcs)) {
31310
- leftPartials = makeLeftBuffer(pathArcs, dist);
31311
- }
31312
- if (needRightBuffer()) {
31313
32012
  revPathArcs = reversePath(pathArcs.concat());
31314
- rightPartials = makeLeftBuffer(revPathArcs, dist);
32013
+ pathCoords = pathCoords.concat(maker(revPathArcs, dist));
31315
32014
  }
31316
- parts = (leftPartials || []).concat(rightPartials || []);
31317
- return parts.map(polygonCoords);
32015
+ pathCoords.push(pathCoords[0]); // close path
32016
+ return outputGeom == 'polyline' ? pathCoords : [pathCoords];
31318
32017
  }
31319
32018
 
31320
- // Returns a GeoJSON Geometry (MultiLineString or MultiPolygon) or null
31321
32019
  return function(shape, dist) {
31322
32020
  var geom = {
31323
- type: 'MultiPolygon',
32021
+ type: outputGeom == 'polyline' ? 'MultiLineString' : 'MultiPolygon',
31324
32022
  coordinates: []
31325
32023
  };
31326
32024
  for (var i=0; i<shape.length; i++) {
31327
- geom.coordinates = geom.coordinates.concat(makeBufferParts(shape[i], dist));
32025
+ geom.coordinates.push(pathBufferCoords(shape[i], dist));
31328
32026
  }
31329
32027
  return geom.coordinates.length == 0 ? null : geom;
31330
32028
  };
31331
32029
  }
31332
32030
 
31333
- function getPathBufferMaker2(arcs, geod, getBearing, opts) {
32031
+
32032
+ function getPathBufferMaker(arcs, geod, getBearing, opts) {
32033
+
31334
32034
  var backtrackSteps = opts.backtrack >= 0 ? opts.backtrack : 50;
31335
32035
  var pathIter = new ShapeIter(arcs);
31336
- // var capStyle = opts.cap_style || 'round'; // expect 'round' or 'flat'
31337
- var partials, left, center;
31338
- var bounds;
32036
+ var capStyle = opts.cap_style || 'round'; // expect 'round' or 'flat'
32037
+ // var tolerance;
31339
32038
  // TODO: implement other join styles than round
31340
32039
 
31341
- // function updateTolerance(dist) {
31342
-
31343
- // }
31344
-
31345
- function addRoundJoin(x, y, startDir, angle, dist) {
32040
+ function addRoundJoin(arr, x, y, startDir, angle, dist) {
31346
32041
  var increment = 10;
31347
32042
  var endDir = startDir + angle;
31348
32043
  var dir = startDir + increment;
31349
32044
  while (dir < endDir) {
31350
- addBufferVertex(geod(x, y, dir, dist));
32045
+ addBufferVertex(arr, geod(x, y, dir, dist), backtrackSteps);
31351
32046
  dir += increment;
31352
32047
  }
31353
32048
  }
@@ -31357,7 +32052,7 @@ ${svg}
31357
32052
  // var endDir = startDir + angle;
31358
32053
  // var dir = startDir + increment;
31359
32054
  // while (dir < endDir) {
31360
- // addBufferVertex(arr, geod(x, y, dir, dist));
32055
+ // addBufferVertex(arr, geod(x, y, dir, dist), backtrackSteps);
31361
32056
  // dir += increment;
31362
32057
  // }
31363
32058
  // }
@@ -31381,24 +32076,24 @@ ${svg}
31381
32076
  }
31382
32077
  }
31383
32078
 
31384
- // function makeCap(x, y, direction, dist) {
31385
- // if (capStyle == 'flat') {
31386
- // return [[x, y]];
31387
- // }
31388
- // return makeRoundCap(x, y, direction, dist);
31389
- // }
32079
+ function makeCap(x, y, direction, dist) {
32080
+ if (capStyle == 'flat') {
32081
+ return [[x, y]];
32082
+ }
32083
+ return makeRoundCap(x, y, direction, dist);
32084
+ }
31390
32085
 
31391
- // function makeRoundCap(x, y, segmentDir, dist) {
31392
- // var points = [];
31393
- // var increment = 10;
31394
- // var startDir = segmentDir - 90;
31395
- // var angle = increment;
31396
- // while (angle < 180) {
31397
- // points.push(geod(x, y, startDir + angle, dist));
31398
- // angle += increment;
31399
- // }
31400
- // return points;
31401
- // }
32086
+ function makeRoundCap(x, y, segmentDir, dist) {
32087
+ var points = [];
32088
+ var increment = 10;
32089
+ var startDir = segmentDir - 90;
32090
+ var angle = increment;
32091
+ while (angle < 180) {
32092
+ points.push(geod(x, y, startDir + angle, dist));
32093
+ angle += increment;
32094
+ }
32095
+ return points;
32096
+ }
31402
32097
 
31403
32098
  // get angle between two extruded segments in degrees
31404
32099
  // positive angle means join in convex (range: 0-180 degrees)
@@ -31414,99 +32109,34 @@ ${svg}
31414
32109
  return delta;
31415
32110
  }
31416
32111
 
31417
-
31418
- // TODO: handle polygon holes
31419
- function addBufferVertex(d) {
31420
- var arr = left;
31421
- var a, b, c, c0, hit;
31422
- // c is the start point of the segment formed by appending point d to the polyline.
31423
- c0 = c = arr[arr.length - 1];
31424
- for (var i=0, idx = arr.length - 3; idx >= 0; i++, idx--) {
32112
+ function addBufferVertex(arr, d, maxBacktrack) {
32113
+ var a, b, c, hit;
32114
+ for (var i=0, idx = arr.length - 3; i<maxBacktrack && idx >= 0; i++, idx--) {
32115
+ c = arr[arr.length - 1];
31425
32116
  a = arr[idx];
31426
32117
  b = arr[idx + 1];
31427
32118
  // TODO: consider using a geodetic intersection function for lat-long datasets
31428
32119
  hit = bufferIntersection(a[0], a[1], b[0], b[1], c[0], c[1], d[0], d[1]);
31429
32120
  if (hit) {
31430
- if (segmentTurn(a, b, c, d) == 1) {
31431
- // interpretation: segment cd crosses segment ab from outside to inside
31432
- // the buffer -- we need to start a new partial; otherwise,
31433
- // the following code would likely remove a loop representing
31434
- // an oxbow-type hole in the buffer.
31435
- //
31436
- finishPartial();
31437
- break;
31438
- }
31439
- // TODO: handle collinear segments (consider creating new partial)
32121
+ // TODO: handle collinear segments
31440
32122
  // if (hit.length != 2) console.log('COLLINEAR', hit)
31441
-
31442
- // segments intersect, indicating a spurious loop: remove the loop and
31443
- // replace the endpoints of the intersecting segments with the intersection point.
32123
+ // segments intersect -- replace two internal segment endpoints with xx point
31444
32124
  while (arr.length > idx + 1) arr.pop();
31445
32125
  appendPoint(arr, hit);
31446
- c = hit; // update starting point of the newly added segment
31447
- }
31448
-
31449
- // Maintain a bounding box around vertices before the backtrack limit.
31450
- // If the latest segment intersects this bounding box, there could be a self-
31451
- // intersection -- start a new partial to prevent self-intersection.
31452
- //
31453
- if (i >= backtrackSteps) {
31454
- if (!bounds) {
31455
- bounds = new Bounds();
31456
- bounds.mergePoint(a[0], a[1]);
31457
- }
31458
- bounds.mergePoint(b[0], b[1]);
31459
- if (testSegmentBoundsIntersection(c0, d, bounds)) {
31460
- finishPartial();
31461
- }
31462
- break;
31463
32126
  }
31464
32127
  }
31465
32128
 
31466
32129
  appendPoint(arr, d);
31467
32130
  }
31468
32131
 
31469
- function finishPartial() {
31470
- // Get endpoints of the two polylines, for starting the next partial
31471
- var leftEP = left[left.length - 1];
31472
- var centerEP = center[center.length - 1];
31473
-
31474
- // Make a polygon ring
31475
- var ring = [];
31476
- extendArray(ring, left);
31477
- center.reverse();
31478
- extendArray(ring, center);
31479
- ring.push(ring[0]); // close ring
31480
- partials.push(ring);
31481
-
31482
- // Start next partial
31483
- left.push(leftEP);
31484
- center.push(centerEP);
31485
-
31486
- // clear bbox
31487
- // bbox = null;
31488
- }
31489
-
31490
- function extendArray(arr, arr2) {
31491
- arr2.reverse();
31492
- while(arr2.length > 0) arr.push(arr2.pop());
31493
- }
31494
-
31495
32132
  return function(path, dist) {
31496
- // var x0, y0;
31497
- var x1, y1, x2, y2;
32133
+ var left = [];
32134
+ var x0, y0, x1, y1, x2, y2;
31498
32135
  var p1, p2;
31499
- // var firstBearing;
31500
- var bearing, prevBearing, joinAngle;
31501
- partials = [];
31502
- left = [];
31503
- center = [];
32136
+ var bearing, prevBearing, firstBearing, joinAngle;
32137
+ var i = 0;
31504
32138
  pathIter.init(path);
31505
32139
 
31506
- // if (pathIter.hasNext()) {
31507
- // x0 = x2 = pathIter.x;
31508
- // y0 = y2 = pathIter.y;
31509
- // }
31510
32140
  while (pathIter.hasNext()) {
31511
32141
  // TODO: use a tolerance
31512
32142
  if (pathIter.x === x2 && pathIter.y === y2) continue; // skip duplicate points
@@ -31514,178 +32144,99 @@ ${svg}
31514
32144
  y1 = y2;
31515
32145
  x2 = pathIter.x;
31516
32146
  y2 = pathIter.y;
31517
-
31518
- prevBearing = bearing;
31519
- bearing = getBearing(x1, y1, x2, y2);
31520
- // shift original polyline segment to the left by buffer distance
31521
- p1 = geod(x1, y1, bearing - 90, dist);
31522
- p2 = geod(x2, y2, bearing - 90, dist);
31523
-
31524
- if (center.length === 0) {
31525
- // first loop, second point in this partial
31526
- // if (partials.length === 0) {
31527
- // firstBearing = bearing;
31528
- // }
32147
+ if (i >= 1) {
32148
+ prevBearing = bearing;
32149
+ bearing = getBearing(x1, y1, x2, y2);
32150
+ p1 = geod(x1, y1, bearing - 90, dist);
32151
+ p2 = geod(x2, y2, bearing - 90, dist);
32152
+ // left.push([x1, y1], p1) // debug extrusion lines
32153
+ // left.push([x2, y2], p2) // debug extrusion lines
32154
+ }
32155
+ if (i == 1) {
32156
+ firstBearing = bearing;
32157
+ x0 = x1;
32158
+ y0 = y1;
31529
32159
  left.push(p1, p2);
31530
- center.push([x1, y1], [x2, y2]);
31531
- } else {
31532
- //
32160
+ }
32161
+ if (i > 1) {
31533
32162
  joinAngle = getJoinAngle(prevBearing, bearing);
31534
32163
  if (veryCloseToPrevPoint(left, p1[0], p1[1])) {
31535
32164
  // skip first point
31536
- addBufferVertex(p2);
32165
+ addBufferVertex(left, p2, backtrackSteps);
31537
32166
  } else if (joinAngle > 0) {
31538
- addRoundJoin(x1, y1, prevBearing - 90, joinAngle, dist);
31539
- addBufferVertex(p1);
31540
- addBufferVertex(p2);
32167
+ addRoundJoin(left, x1, y1, prevBearing - 90, joinAngle, dist);
32168
+ addBufferVertex(left, p1, backtrackSteps);
32169
+ addBufferVertex(left, p2, backtrackSteps);
31541
32170
  } else {
31542
- addBufferVertex(p1);
31543
- addBufferVertex(p2);
32171
+ addBufferVertex(left, p1, backtrackSteps);
32172
+ addBufferVertex(left, p2, backtrackSteps);
31544
32173
  }
31545
- center.push([x2, y2]);
31546
32174
  }
31547
- }
31548
-
31549
- if (center.length > 1) {
31550
- finishPartial();
32175
+ i++;
31551
32176
  }
31552
32177
  // TODO: handle defective polylines
31553
32178
 
31554
- // if (x2 == x0 && y2 == y0) {
31555
- // // add join to finish closed path
31556
- // joinAngle = getJoinAngle(bearing, firstBearing);
31557
- // if (joinAngle > 0) {
31558
- // addRoundJoin(leftpart, x2, y2, bearing - 90, joinAngle, dist);
31559
- // }
31560
- // } else {
31561
- // // add a cap to finish open path
31562
- // leftpart.push.apply(leftpart, makeCap(x2, y2, bearing, dist));
31563
- // }
31564
-
31565
- return partials;
31566
- };
31567
- }
31568
-
31569
- // GeographicLib docs: https://geographiclib.sourceforge.io/html/js/
31570
- // https://geographiclib.sourceforge.io/html/js/module-GeographicLib_Geodesic.Geodesic.html
31571
- // https://geographiclib.sourceforge.io/html/js/tutorial-2-interface.html
31572
- function getGeodesic(P) {
31573
- if (!isLatLngCRS(P)) error('Expected an unprojected CRS');
31574
- var f = P.es / (1 + Math.sqrt(P.one_es));
31575
- var GeographicLib = require$1('mproj').internal.GeographicLib;
31576
- return new GeographicLib.Geodesic.Geodesic(P.a, f);
31577
- }
31578
-
31579
- function interpolatePoint2D(ax, ay, bx, by, k) {
31580
- var j = 1 - k;
31581
- return [ax * j + bx * k, ay * j + by * k];
31582
- }
31583
-
31584
- function getInterpolationFunction(P) {
31585
- var spherical = P && isLatLngCRS(P);
31586
- if (!spherical) return interpolatePoint2D;
31587
- var geod = getGeodesic(P);
31588
- return function(lng, lat, lng2, lat2, k) {
31589
- var r = geod.Inverse(lat, lng, lat2, lng2);
31590
- var dist = r.s12 * k;
31591
- var r2 = geod.Direct(lat, lng, r.azi1, dist);
31592
- return [r2.lon2, r2.lat2];
31593
- };
31594
- }
31595
-
31596
- function getPlanarSegmentEndpoint(x, y, bearing, meterDist) {
31597
- var rad = bearing / 180 * Math.PI;
31598
- var dx = Math.sin(rad) * meterDist;
31599
- var dy = Math.cos(rad) * meterDist;
31600
- return [x + dx, y + dy];
31601
- }
31602
-
31603
- // source: https://github.com/mapbox/cheap-ruler/blob/master/index.js
31604
- function fastGeodeticSegmentFunction(lng, lat, bearing, meterDist) {
31605
- var D2R = Math.PI / 180;
31606
- var cos = Math.cos(lat * D2R);
31607
- var cos2 = 2 * cos * cos - 1;
31608
- var cos3 = 2 * cos * cos2 - cos;
31609
- var cos4 = 2 * cos * cos3 - cos2;
31610
- var cos5 = 2 * cos * cos4 - cos3;
31611
- var kx = (111.41513 * cos - 0.09455 * cos3 + 0.00012 * cos5) * 1000;
31612
- var ky = (111.13209 - 0.56605 * cos2 + 0.0012 * cos4) * 1000;
31613
- var bearingRad = bearing * D2R;
31614
- var lat2 = lat + Math.cos(bearingRad) * meterDist / ky;
31615
- var lng2 = lng + Math.sin(bearingRad) * meterDist / kx;
31616
- return [lng2, lat2];
31617
- }
31618
-
31619
- function getGeodeticSegmentFunction(P) {
31620
- if (!isLatLngCRS(P)) {
31621
- return getPlanarSegmentEndpoint;
31622
- }
31623
- var g = getGeodesic(P);
31624
- return function(lng, lat, bearing, meterDist) {
31625
- var o = g.Direct(lat, lng, bearing, meterDist);
31626
- var p = [o.lon2, o.lat2];
31627
- return p;
32179
+ if (x2 == x0 && y2 == y0) {
32180
+ // add join to finish closed path
32181
+ joinAngle = getJoinAngle(bearing, firstBearing);
32182
+ if (joinAngle > 0) {
32183
+ addRoundJoin(left, x2, y2, bearing - 90, joinAngle, dist);
32184
+ }
32185
+ } else {
32186
+ // add a cap to finish open path
32187
+ left.push.apply(left, makeCap(x2, y2, bearing, dist));
32188
+ }
32189
+ return left;
31628
32190
  };
31629
32191
  }
31630
32192
 
31631
- function getFastGeodeticSegmentFunction(P) {
31632
- // CAREFUL: this function has higher error at very large distances and at the poles
31633
- // also, it wouldn't work for other planets than Earth
31634
- return isLatLngCRS(P) ? fastGeodeticSegmentFunction : getPlanarSegmentEndpoint;
31635
- }
31636
-
31637
-
31638
- function bearingDegrees(a, b, c, d) {
31639
- return geom.bearing(a, b, c, d) * 180 / Math.PI;
31640
- }
31641
-
31642
- function bearingDegrees2D(a, b, c, d) {
31643
- return geom.bearing2D(a, b, c, d) * 180 / Math.PI;
31644
- }
31645
-
31646
- // return function to calculate bearing of a segment in degrees
31647
- function getBearingFunction(dataset) {
31648
- var P = getDatasetCRS(dataset);
31649
- return isLatLngCRS(P) ? bearingDegrees : bearingDegrees2D;
32193
+ // Exclude segments with non-intersecting bounding boxes before
32194
+ // calling intersection function
32195
+ // Possibly slightly faster than direct call... not worth it?
32196
+ function bufferIntersection(ax, ay, bx, by, cx, cy, dx, dy) {
32197
+ if (ax < cx && ax < dx && bx < cx && bx < dx ||
32198
+ ax > cx && ax > dx && bx > cx && bx > dx ||
32199
+ ay < cy && ay < dy && by < cy && by < dy ||
32200
+ ay > cy && ay > dy && by > cy && by > dy) return null;
32201
+ return geom.segmentIntersection(ax, ay, bx, by, cx, cy, dx, dy);
31650
32202
  }
31651
32203
 
31652
- var Geodesic = /*#__PURE__*/Object.freeze({
31653
- __proto__: null,
31654
- bearingDegrees: bearingDegrees,
31655
- bearingDegrees2D: bearingDegrees2D,
31656
- getBearingFunction: getBearingFunction,
31657
- getFastGeodeticSegmentFunction: getFastGeodeticSegmentFunction,
31658
- getGeodeticSegmentFunction: getGeodeticSegmentFunction,
31659
- getInterpolationFunction: getInterpolationFunction,
31660
- getPlanarSegmentEndpoint: getPlanarSegmentEndpoint,
31661
- interpolatePoint2D: interpolatePoint2D
31662
- });
31663
-
31664
32204
  function makePolylineBuffer(lyr, dataset, opts) {
32205
+ time('buffer');
31665
32206
  var geojson = makeShapeBufferGeoJSON(lyr, dataset, opts);
31666
32207
  var dataset2 = importGeoJSON(geojson, {});
31667
- dissolveBufferDataset(dataset2, opts);
32208
+ if (!opts.debug_points) {
32209
+ dissolveBufferDataset2(dataset2, opts);
32210
+ }
32211
+ timeEnd('buffer');
31668
32212
  return dataset2;
31669
32213
  }
31670
32214
 
31671
32215
  function makeShapeBufferGeoJSON(lyr, dataset, opts) {
31672
32216
  var distanceFn = getBufferDistanceFunction(lyr, dataset, opts);
31673
- getBufferToleranceFunction(dataset, opts);
31674
- var geod = getFastGeodeticSegmentFunction(getDatasetCRS(dataset));
31675
- var getBearing = getBearingFunction(dataset);
32217
+ // var toleranceFn = getBufferToleranceFunction(dataset, opts);
31676
32218
  var makerOpts = Object.assign({geometry_type: lyr.geometry_type}, opts);
31677
- var factory = opts.v2 ? getPolylineBufferMaker2 : getPolylineBufferMaker;
31678
- var makeShapeBuffer = factory(dataset.arcs, geod, getBearing, makerOpts);
31679
- lyr.data ? lyr.data.getRecords() : null;
31680
- var geometries = lyr.shapes.map(function(shape, i) {
31681
- var dist = distanceFn(i);
31682
- if (!dist || !shape) return null;
31683
- return makeShapeBuffer(shape, dist, lyr.geometry_type);
31684
- });
32219
+ var makeShapeBuffer =
32220
+ opts.v2 && getPolylineBufferMaker$1(dataset, makerOpts) ||
32221
+ opts.v3 && getPolylineBufferMaker$2(dataset, makerOpts) ||
32222
+ getPolylineBufferMaker(dataset, makerOpts);
32223
+ // var records = lyr.data ? lyr.data.getRecords() : null;
32224
+ var arr = lyr.shapes.reduce(function(memo, shape, i) {
32225
+ var distance = distanceFn(i);
32226
+ if (!distance || !shape) return memo;
32227
+ // retn might be an array of features or a single feature
32228
+ var retn = makeShapeBuffer(shape, distance);
32229
+ return memo.concat(retn);
32230
+ }, []);
32231
+ var geojsonType = arr?.[0].type;
31685
32232
  // TODO: make sure that importer supports null geometries (not standard GeoJSON);
31686
- return {
32233
+ // console.log(arr)
32234
+ return geojsonType == 'Feature' ? {
32235
+ type: 'FeatureCollection',
32236
+ features: arr
32237
+ } : {
31687
32238
  type: 'GeometryCollection',
31688
- geometries: geometries
32239
+ geometries: arr
31689
32240
  };
31690
32241
  }
31691
32242
 
@@ -32137,8 +32688,7 @@ ${svg}
32137
32688
  stop("Unsupported geometry type");
32138
32689
  }
32139
32690
 
32140
- var lyr2 = mergeOutputLayerIntoDataset(lyr, dataset, dataset2, opts);
32141
- return [lyr2];
32691
+ return mergeOutputLayersIntoDataset(lyr, dataset, dataset2, opts);
32142
32692
  }
32143
32693
 
32144
32694
  // currently undocumented, used in tests
@@ -36789,9 +37339,6 @@ ${svg}
36789
37339
  getLayerInfo: getLayerInfo
36790
37340
  });
36791
37341
 
36792
- // import { importGeoJSON } from '../geojson/geojson-import';
36793
-
36794
-
36795
37342
  function addTargetProxies(targets, ctx) {
36796
37343
  if (targets && targets.length > 0) {
36797
37344
  var proxies = expandCommandTargets(targets).reduce(function(memo, target) {
@@ -36814,11 +37361,19 @@ ${svg}
36814
37361
  var proxy = getLayerInfo(target.layer, target.dataset); // layer_name, feature_count etc
36815
37362
  proxy.layer = target.layer;
36816
37363
  proxy.dataset = target.dataset;
36817
- addGetters(proxy, {
36818
- // export as an object, not a string or buffer
36819
- geojson: getGeoJSON
37364
+
37365
+ Object.defineProperty(proxy, 'geojson', {
37366
+ set: setGeoJSON,
37367
+ get: getGeoJSON
36820
37368
  });
36821
37369
 
37370
+ function setGeoJSON(o) {
37371
+ var dataset2 = importGeoJSON(o);
37372
+ var lyr2 = dataset2.layers[0];
37373
+ lyr2.name = target.layer.name;
37374
+ replaceLayerContents(target.layer, target.dataset, dataset2);
37375
+ }
37376
+
36822
37377
  function getGeoJSON() {
36823
37378
  var features = exportLayerAsGeoJSON(target.layer, target.dataset, {rfc7946: true}, true);
36824
37379
  return {
@@ -41337,7 +41892,7 @@ ${svg}
41337
41892
  function findPathMidpoint(path, arcs, useNearestVertex) {
41338
41893
  var halfLen = calcPathLen(path, arcs, false) / 2;
41339
41894
  var partialLen = 0;
41340
- var p;
41895
+ var p = null;
41341
41896
  forEachSegmentInPath(path, arcs, function(i, j, xx, yy) {
41342
41897
  var a = xx[i],
41343
41898
  b = yy[i],
@@ -41357,7 +41912,7 @@ ${svg}
41357
41912
  partialLen += segLen;
41358
41913
  });
41359
41914
  if (!p) {
41360
- error('Geometry error');
41915
+ debug('[findPathMidpoint()] defective path:', path);
41361
41916
  }
41362
41917
  return p;
41363
41918
  }
@@ -46293,7 +46848,7 @@ ${svg}
46293
46848
  });
46294
46849
  }
46295
46850
 
46296
- var version = "0.6.111";
46851
+ var version = "0.6.113";
46297
46852
 
46298
46853
  // Parse command line args into commands and run them
46299
46854
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
@@ -46896,67 +47451,6 @@ ${svg}
46896
47451
  return false;
46897
47452
  }
46898
47453
 
46899
- // TODO: Need to rethink polygon repair: these function can cause problems
46900
- // when part of a self-intersecting polygon is removed
46901
- //
46902
- function repairPolygonGeometry(layers, dataset, opts) {
46903
- var nodes = addIntersectionCuts(dataset);
46904
- layers.forEach(function(lyr) {
46905
- repairSelfIntersections(lyr, nodes);
46906
- });
46907
- return layers;
46908
- }
46909
-
46910
- // Remove any small shapes formed by twists in each ring
46911
- // // OOPS, NO // Retain only the part with largest area
46912
- // // this causes problems when a cut-off hole has a matching ring in another polygon
46913
- // TODO: consider cases where cut-off parts should be retained
46914
- //
46915
- function repairSelfIntersections(lyr, nodes) {
46916
- var splitter = getSelfIntersectionSplitter(nodes);
46917
-
46918
- lyr.shapes = lyr.shapes.map(function(shp, i) {
46919
- return cleanPolygon(shp);
46920
- });
46921
-
46922
- function cleanPolygon(shp) {
46923
- var cleanedPolygon = [];
46924
- forEachShapePart(shp, function(ids) {
46925
- // TODO: consider returning null if path can't be split
46926
- var splitIds = splitter(ids);
46927
- if (splitIds.length === 0) {
46928
- error("[cleanPolygon()] Defective path:", ids);
46929
- } else if (splitIds.length == 1) {
46930
- cleanedPolygon.push(splitIds[0]);
46931
- } else {
46932
- var shapeArea = geom.getPlanarPathArea(ids, nodes.arcs),
46933
- sign = shapeArea > 0 ? 1 : -1,
46934
- mainRing;
46935
-
46936
- splitIds.reduce(function(max, ringIds, i) {
46937
- var pathArea = geom.getPlanarPathArea(ringIds, nodes.arcs) * sign;
46938
- if (pathArea > max) {
46939
- mainRing = ringIds;
46940
- max = pathArea;
46941
- }
46942
- return max;
46943
- }, 0);
46944
-
46945
- if (mainRing) {
46946
- cleanedPolygon.push(mainRing);
46947
- }
46948
- }
46949
- });
46950
- return cleanedPolygon.length > 0 ? cleanedPolygon : null;
46951
- }
46952
- }
46953
-
46954
- var PolygonRepair = /*#__PURE__*/Object.freeze({
46955
- __proto__: null,
46956
- repairPolygonGeometry: repairPolygonGeometry,
46957
- repairSelfIntersections: repairSelfIntersections
46958
- });
46959
-
46960
47454
  // Attach functions exported by modules to the "internal" object,
46961
47455
  // so they can be run by tests and by the GUI.
46962
47456
  // TODO: rewrite tests to import functions directly from modules,
@@ -47003,7 +47497,7 @@ ${svg}
47003
47497
  ArcUtils,
47004
47498
  Bbox2Clipping,
47005
47499
  BinArray$1,
47006
- BufferCommon,
47500
+ // BufferCommon,
47007
47501
  Calc,
47008
47502
  CalcUtils,
47009
47503
  Catalog$1,
@@ -47058,7 +47552,7 @@ ${svg}
47058
47552
  OverlayUtils,
47059
47553
  Pack, Unpack,
47060
47554
  ParseCommands,
47061
- PathBuffer,
47555
+ // PathBuffer,
47062
47556
  PathEndpoints,
47063
47557
  PathExport,
47064
47558
  Pathfinder,