mapshaper 0.5.75 → 0.5.76
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/CHANGELOG.md +4 -0
- package/mapshaper.js +252 -101
- package/package.json +1 -1
- package/www/mapshaper-gui.js +2 -1
- package/www/mapshaper.js +252 -101
package/CHANGELOG.md
CHANGED
package/mapshaper.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
|
|
3
|
-
var VERSION = "0.5.
|
|
3
|
+
var VERSION = "0.5.75";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
var utils = /*#__PURE__*/Object.freeze({
|
|
@@ -2967,12 +2967,33 @@
|
|
|
2967
2967
|
orient2D(cx, cy, dx, dy, bx, by) <= 0;
|
|
2968
2968
|
}
|
|
2969
2969
|
|
|
2970
|
+
// Useful for determining if a segment that intersects another segment is
|
|
2971
|
+
// entering or leaving an enclosed buffer area
|
|
2972
|
+
// returns -1 if angle of p1p2 -> p3p4 is counter-clockwise (left turn)
|
|
2973
|
+
// returns 1 if angle is clockwise
|
|
2974
|
+
// return 0 if segments are collinear
|
|
2975
|
+
function segmentTurn(p1, p2, p3, p4) {
|
|
2976
|
+
var ax = p1[0],
|
|
2977
|
+
ay = p1[1],
|
|
2978
|
+
bx = p2[0],
|
|
2979
|
+
by = p2[1],
|
|
2980
|
+
// shift p3p4 segment to start at p2
|
|
2981
|
+
dx = bx - p3[0],
|
|
2982
|
+
dy = by - p3[1],
|
|
2983
|
+
cx = p4[0] + dx,
|
|
2984
|
+
cy = p4[1] + dy,
|
|
2985
|
+
orientation = orient2D(ax, ay, bx, by, cx, cy);
|
|
2986
|
+
if (!orientation) return 0;
|
|
2987
|
+
return orientation < 0 ? 1 : -1;
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2970
2990
|
var SegmentGeom = /*#__PURE__*/Object.freeze({
|
|
2971
2991
|
__proto__: null,
|
|
2972
2992
|
segmentIntersection: segmentIntersection,
|
|
2973
2993
|
findClosestPointOnSeg: findClosestPointOnSeg,
|
|
2974
2994
|
orient2D: orient2D,
|
|
2975
|
-
segmentHit: segmentHit
|
|
2995
|
+
segmentHit: segmentHit,
|
|
2996
|
+
segmentTurn: segmentTurn
|
|
2976
2997
|
});
|
|
2977
2998
|
|
|
2978
2999
|
var geom = Object.assign({}, Geom, PolygonGeom, PathGeom, SegmentGeom, PolygonCentroid);
|
|
@@ -8136,10 +8157,14 @@
|
|
|
8136
8157
|
}
|
|
8137
8158
|
|
|
8138
8159
|
function roundToDigits(n, d) {
|
|
8139
|
-
return +n.toFixed(d);
|
|
8160
|
+
return +n.toFixed(d); // string conversion makes this slow
|
|
8161
|
+
}
|
|
8162
|
+
|
|
8163
|
+
function roundToTenths(n) {
|
|
8164
|
+
return (Math.round(n * 10)) / 10;
|
|
8140
8165
|
}
|
|
8141
8166
|
|
|
8142
|
-
// inc: Rounding
|
|
8167
|
+
// inc: Rounding increment (e.g. 0.001 rounds to thousandths)
|
|
8143
8168
|
function getRoundingFunction(inc) {
|
|
8144
8169
|
if (!utils.isNumber(inc) || inc === 0) {
|
|
8145
8170
|
error("Rounding increment must be a non-zero number.");
|
|
@@ -8209,6 +8234,7 @@
|
|
|
8209
8234
|
__proto__: null,
|
|
8210
8235
|
roundToSignificantDigits: roundToSignificantDigits,
|
|
8211
8236
|
roundToDigits: roundToDigits,
|
|
8237
|
+
roundToTenths: roundToTenths,
|
|
8212
8238
|
getRoundingFunction: getRoundingFunction,
|
|
8213
8239
|
getBoundsPrecisionForDisplay: getBoundsPrecisionForDisplay,
|
|
8214
8240
|
getRoundedCoordString: getRoundedCoordString,
|
|
@@ -13811,7 +13837,7 @@
|
|
|
13811
13837
|
var symbolRenderers = {};
|
|
13812
13838
|
|
|
13813
13839
|
function getTransform(xy, scale) {
|
|
13814
|
-
var str = 'translate(' + xy[0] + ' ' + xy[1] + ')';
|
|
13840
|
+
var str = 'translate(' + roundToTenths(xy[0]) + ' ' + roundToTenths(xy[1]) + ')';
|
|
13815
13841
|
if (scale && scale != 1) {
|
|
13816
13842
|
str += ' scale(' + scale + ')';
|
|
13817
13843
|
}
|
|
@@ -19718,6 +19744,23 @@ ${svg}
|
|
|
19718
19744
|
.option('target', targetOpt)
|
|
19719
19745
|
.option('no-replace', noReplaceOpt);
|
|
19720
19746
|
|
|
19747
|
+
parser.command('split-lines')
|
|
19748
|
+
// .describe('divide lines into sections')
|
|
19749
|
+
.option('dash-length', {
|
|
19750
|
+
type: 'distance',
|
|
19751
|
+
describe: 'length of split-apart lines'
|
|
19752
|
+
})
|
|
19753
|
+
.option('gap-length', {
|
|
19754
|
+
type: 'distance',
|
|
19755
|
+
describe: 'length of gap between segments'
|
|
19756
|
+
})
|
|
19757
|
+
.option('planar', {
|
|
19758
|
+
type: 'flag',
|
|
19759
|
+
describe: 'use planar geometry'
|
|
19760
|
+
})
|
|
19761
|
+
.option('where', whereOpt)
|
|
19762
|
+
.option('target', targetOpt);
|
|
19763
|
+
|
|
19721
19764
|
parser.command('split-on-grid')
|
|
19722
19765
|
.describe('split features into separate layers using a grid')
|
|
19723
19766
|
.validate(validateGridOpts)
|
|
@@ -26263,97 +26306,6 @@ ${svg}
|
|
|
26263
26306
|
geom.segmentIntersection(a[0], a[1], b[0], b[1], bb.xmax, bb.ymin, bb.xmin, bb.ymin));
|
|
26264
26307
|
}
|
|
26265
26308
|
|
|
26266
|
-
function getGeodesic(P) {
|
|
26267
|
-
if (!isLatLngCRS(P)) error('Expected an unprojected CRS');
|
|
26268
|
-
var f = P.es / (1 + Math.sqrt(P.one_es));
|
|
26269
|
-
var GeographicLib = require('mproj').internal.GeographicLib;
|
|
26270
|
-
return new GeographicLib.Geodesic.Geodesic(P.a, f);
|
|
26271
|
-
}
|
|
26272
|
-
|
|
26273
|
-
function getPlanarSegmentEndpoint(x, y, bearing, meterDist) {
|
|
26274
|
-
var rad = bearing / 180 * Math.PI;
|
|
26275
|
-
var dx = Math.sin(rad) * meterDist;
|
|
26276
|
-
var dy = Math.cos(rad) * meterDist;
|
|
26277
|
-
return [x + dx, y + dy];
|
|
26278
|
-
}
|
|
26279
|
-
|
|
26280
|
-
// source: https://github.com/mapbox/cheap-ruler/blob/master/index.js
|
|
26281
|
-
function fastGeodeticSegmentFunction(lng, lat, bearing, meterDist) {
|
|
26282
|
-
var D2R = Math.PI / 180;
|
|
26283
|
-
var cos = Math.cos(lat * D2R);
|
|
26284
|
-
var cos2 = 2 * cos * cos - 1;
|
|
26285
|
-
var cos3 = 2 * cos * cos2 - cos;
|
|
26286
|
-
var cos4 = 2 * cos * cos3 - cos2;
|
|
26287
|
-
var cos5 = 2 * cos * cos4 - cos3;
|
|
26288
|
-
var kx = (111.41513 * cos - 0.09455 * cos3 + 0.00012 * cos5) * 1000;
|
|
26289
|
-
var ky = (111.13209 - 0.56605 * cos2 + 0.0012 * cos4) * 1000;
|
|
26290
|
-
var bearingRad = bearing * D2R;
|
|
26291
|
-
var lat2 = lat + Math.cos(bearingRad) * meterDist / ky;
|
|
26292
|
-
var lng2 = lng + Math.sin(bearingRad) * meterDist / kx;
|
|
26293
|
-
return [lng2, lat2];
|
|
26294
|
-
}
|
|
26295
|
-
|
|
26296
|
-
function getGeodeticSegmentFunction(P) {
|
|
26297
|
-
if (!isLatLngCRS(P)) {
|
|
26298
|
-
return getPlanarSegmentEndpoint;
|
|
26299
|
-
}
|
|
26300
|
-
var g = getGeodesic(P);
|
|
26301
|
-
return function(lng, lat, bearing, meterDist) {
|
|
26302
|
-
var o = g.Direct(lat, lng, bearing, meterDist);
|
|
26303
|
-
var p = [o.lon2, o.lat2];
|
|
26304
|
-
return p;
|
|
26305
|
-
};
|
|
26306
|
-
}
|
|
26307
|
-
|
|
26308
|
-
function getFastGeodeticSegmentFunction(P) {
|
|
26309
|
-
// CAREFUL: this function has higher error at very large distances and at the poles
|
|
26310
|
-
// also, it wouldn't work for other planets than Earth
|
|
26311
|
-
return isLatLngCRS(P) ? fastGeodeticSegmentFunction : getPlanarSegmentEndpoint;
|
|
26312
|
-
}
|
|
26313
|
-
|
|
26314
|
-
// Useful for determining if a segment that intersects another segment is
|
|
26315
|
-
// entering or leaving an enclosed buffer area
|
|
26316
|
-
// returns -1 if angle of p1p2 -> p3p4 is counter-clockwise (left turn)
|
|
26317
|
-
// returns 1 if angle is clockwise
|
|
26318
|
-
// return 0 if segments are collinear
|
|
26319
|
-
function segmentTurn(p1, p2, p3, p4) {
|
|
26320
|
-
var ax = p1[0],
|
|
26321
|
-
ay = p1[1],
|
|
26322
|
-
bx = p2[0],
|
|
26323
|
-
by = p2[1],
|
|
26324
|
-
// shift p3p4 segment to start at p2
|
|
26325
|
-
dx = bx - p3[0],
|
|
26326
|
-
dy = by - p3[1],
|
|
26327
|
-
cx = p4[0] + dx,
|
|
26328
|
-
cy = p4[1] + dy,
|
|
26329
|
-
orientation = geom.orient2D(ax, ay, bx, by, cx, cy);
|
|
26330
|
-
if (!orientation) return 0;
|
|
26331
|
-
return orientation < 0 ? 1 : -1;
|
|
26332
|
-
}
|
|
26333
|
-
|
|
26334
|
-
function bearingDegrees(a, b, c, d) {
|
|
26335
|
-
return geom.bearing(a, b, c, d) * 180 / Math.PI;
|
|
26336
|
-
}
|
|
26337
|
-
|
|
26338
|
-
function bearingDegrees2D(a, b, c, d) {
|
|
26339
|
-
return geom.bearing2D(a, b, c, d) * 180 / Math.PI;
|
|
26340
|
-
}
|
|
26341
|
-
|
|
26342
|
-
// return function to calculate bearing of a segment in degrees
|
|
26343
|
-
function getBearingFunction(dataset) {
|
|
26344
|
-
var P = getDatasetCRS(dataset);
|
|
26345
|
-
return isLatLngCRS(P) ? bearingDegrees : bearingDegrees2D;
|
|
26346
|
-
}
|
|
26347
|
-
|
|
26348
|
-
var Geodesic = /*#__PURE__*/Object.freeze({
|
|
26349
|
-
__proto__: null,
|
|
26350
|
-
getPlanarSegmentEndpoint: getPlanarSegmentEndpoint,
|
|
26351
|
-
getGeodeticSegmentFunction: getGeodeticSegmentFunction,
|
|
26352
|
-
getFastGeodeticSegmentFunction: getFastGeodeticSegmentFunction,
|
|
26353
|
-
segmentTurn: segmentTurn,
|
|
26354
|
-
getBearingFunction: getBearingFunction
|
|
26355
|
-
});
|
|
26356
|
-
|
|
26357
26309
|
function getPolylineBufferMaker2(arcs, geod, getBearing, opts) {
|
|
26358
26310
|
var makeLeftBuffer = getPathBufferMaker2(arcs, geod, getBearing, opts);
|
|
26359
26311
|
var geomType = opts.geometry_type;
|
|
@@ -26647,6 +26599,101 @@ ${svg}
|
|
|
26647
26599
|
};
|
|
26648
26600
|
}
|
|
26649
26601
|
|
|
26602
|
+
// GeographicLib docs: https://geographiclib.sourceforge.io/html/js/
|
|
26603
|
+
// https://geographiclib.sourceforge.io/html/js/module-GeographicLib_Geodesic.Geodesic.html
|
|
26604
|
+
// https://geographiclib.sourceforge.io/html/js/tutorial-2-interface.html
|
|
26605
|
+
function getGeodesic(P) {
|
|
26606
|
+
if (!isLatLngCRS(P)) error('Expected an unprojected CRS');
|
|
26607
|
+
var f = P.es / (1 + Math.sqrt(P.one_es));
|
|
26608
|
+
var GeographicLib = require('mproj').internal.GeographicLib;
|
|
26609
|
+
return new GeographicLib.Geodesic.Geodesic(P.a, f);
|
|
26610
|
+
}
|
|
26611
|
+
|
|
26612
|
+
function interpolatePoint2D(ax, ay, bx, by, k) {
|
|
26613
|
+
var j = 1 - k;
|
|
26614
|
+
return [ax * j + bx * k, ay * j + by * k];
|
|
26615
|
+
}
|
|
26616
|
+
|
|
26617
|
+
function getInterpolationFunction(P) {
|
|
26618
|
+
var spherical = P && isLatLngCRS(P);
|
|
26619
|
+
if (!spherical) return interpolatePoint2D;
|
|
26620
|
+
var geod = getGeodesic(P);
|
|
26621
|
+
return function(lng, lat, lng2, lat2, k) {
|
|
26622
|
+
var r = geod.Inverse(lat, lng, lat2, lng2);
|
|
26623
|
+
var dist = r.s12 * k;
|
|
26624
|
+
var r2 = geod.Direct(lat, lng, r.azi1, dist);
|
|
26625
|
+
return [r2.lon2, r2.lat2];
|
|
26626
|
+
};
|
|
26627
|
+
}
|
|
26628
|
+
|
|
26629
|
+
function getPlanarSegmentEndpoint(x, y, bearing, meterDist) {
|
|
26630
|
+
var rad = bearing / 180 * Math.PI;
|
|
26631
|
+
var dx = Math.sin(rad) * meterDist;
|
|
26632
|
+
var dy = Math.cos(rad) * meterDist;
|
|
26633
|
+
return [x + dx, y + dy];
|
|
26634
|
+
}
|
|
26635
|
+
|
|
26636
|
+
// source: https://github.com/mapbox/cheap-ruler/blob/master/index.js
|
|
26637
|
+
function fastGeodeticSegmentFunction(lng, lat, bearing, meterDist) {
|
|
26638
|
+
var D2R = Math.PI / 180;
|
|
26639
|
+
var cos = Math.cos(lat * D2R);
|
|
26640
|
+
var cos2 = 2 * cos * cos - 1;
|
|
26641
|
+
var cos3 = 2 * cos * cos2 - cos;
|
|
26642
|
+
var cos4 = 2 * cos * cos3 - cos2;
|
|
26643
|
+
var cos5 = 2 * cos * cos4 - cos3;
|
|
26644
|
+
var kx = (111.41513 * cos - 0.09455 * cos3 + 0.00012 * cos5) * 1000;
|
|
26645
|
+
var ky = (111.13209 - 0.56605 * cos2 + 0.0012 * cos4) * 1000;
|
|
26646
|
+
var bearingRad = bearing * D2R;
|
|
26647
|
+
var lat2 = lat + Math.cos(bearingRad) * meterDist / ky;
|
|
26648
|
+
var lng2 = lng + Math.sin(bearingRad) * meterDist / kx;
|
|
26649
|
+
return [lng2, lat2];
|
|
26650
|
+
}
|
|
26651
|
+
|
|
26652
|
+
function getGeodeticSegmentFunction(P) {
|
|
26653
|
+
if (!isLatLngCRS(P)) {
|
|
26654
|
+
return getPlanarSegmentEndpoint;
|
|
26655
|
+
}
|
|
26656
|
+
var g = getGeodesic(P);
|
|
26657
|
+
return function(lng, lat, bearing, meterDist) {
|
|
26658
|
+
var o = g.Direct(lat, lng, bearing, meterDist);
|
|
26659
|
+
var p = [o.lon2, o.lat2];
|
|
26660
|
+
return p;
|
|
26661
|
+
};
|
|
26662
|
+
}
|
|
26663
|
+
|
|
26664
|
+
function getFastGeodeticSegmentFunction(P) {
|
|
26665
|
+
// CAREFUL: this function has higher error at very large distances and at the poles
|
|
26666
|
+
// also, it wouldn't work for other planets than Earth
|
|
26667
|
+
return isLatLngCRS(P) ? fastGeodeticSegmentFunction : getPlanarSegmentEndpoint;
|
|
26668
|
+
}
|
|
26669
|
+
|
|
26670
|
+
|
|
26671
|
+
function bearingDegrees(a, b, c, d) {
|
|
26672
|
+
return geom.bearing(a, b, c, d) * 180 / Math.PI;
|
|
26673
|
+
}
|
|
26674
|
+
|
|
26675
|
+
function bearingDegrees2D(a, b, c, d) {
|
|
26676
|
+
return geom.bearing2D(a, b, c, d) * 180 / Math.PI;
|
|
26677
|
+
}
|
|
26678
|
+
|
|
26679
|
+
// return function to calculate bearing of a segment in degrees
|
|
26680
|
+
function getBearingFunction(dataset) {
|
|
26681
|
+
var P = getDatasetCRS(dataset);
|
|
26682
|
+
return isLatLngCRS(P) ? bearingDegrees : bearingDegrees2D;
|
|
26683
|
+
}
|
|
26684
|
+
|
|
26685
|
+
var Geodesic = /*#__PURE__*/Object.freeze({
|
|
26686
|
+
__proto__: null,
|
|
26687
|
+
interpolatePoint2D: interpolatePoint2D,
|
|
26688
|
+
getInterpolationFunction: getInterpolationFunction,
|
|
26689
|
+
getPlanarSegmentEndpoint: getPlanarSegmentEndpoint,
|
|
26690
|
+
getGeodeticSegmentFunction: getGeodeticSegmentFunction,
|
|
26691
|
+
getFastGeodeticSegmentFunction: getFastGeodeticSegmentFunction,
|
|
26692
|
+
bearingDegrees: bearingDegrees,
|
|
26693
|
+
bearingDegrees2D: bearingDegrees2D,
|
|
26694
|
+
getBearingFunction: getBearingFunction
|
|
26695
|
+
});
|
|
26696
|
+
|
|
26650
26697
|
function makePolylineBuffer(lyr, dataset, opts) {
|
|
26651
26698
|
var geojson = makeShapeBufferGeoJSON(lyr, dataset, opts);
|
|
26652
26699
|
var dataset2 = importGeoJSON(geojson, {});
|
|
@@ -31237,6 +31284,7 @@ ${svg}
|
|
|
31237
31284
|
type: 'FeatureCollection',
|
|
31238
31285
|
features: features
|
|
31239
31286
|
};
|
|
31287
|
+
|
|
31240
31288
|
// console.log(JSON.stringify(geojson, null, 2))
|
|
31241
31289
|
return importGeoJSON(geojson);
|
|
31242
31290
|
};
|
|
@@ -34403,11 +34451,6 @@ ${svg}
|
|
|
34403
34451
|
});
|
|
34404
34452
|
}
|
|
34405
34453
|
|
|
34406
|
-
function interpolatePoint2D(ax, ay, bx, by, k) {
|
|
34407
|
-
var j = 1 - k;
|
|
34408
|
-
return [ax * j + bx * k, ay * j + by * k];
|
|
34409
|
-
}
|
|
34410
|
-
|
|
34411
34454
|
function interpolatePointsAlongArc(ids, arcs, interval) {
|
|
34412
34455
|
var iter = arcs.getShapeIter(ids);
|
|
34413
34456
|
var distance = arcs.isPlanar() ? geom.distance2D : geom.greatCircleDistance;
|
|
@@ -37267,6 +37310,111 @@ ${svg}
|
|
|
37267
37310
|
getSplitNameFunction: getSplitNameFunction
|
|
37268
37311
|
});
|
|
37269
37312
|
|
|
37313
|
+
cmd.splitLines = function(lyr, dataset, opts) {
|
|
37314
|
+
var crs = getDatasetCRS(dataset);
|
|
37315
|
+
requirePolylineLayer(lyr);
|
|
37316
|
+
var splitFeature = getSplitFeatureFunction(crs, opts);
|
|
37317
|
+
|
|
37318
|
+
// TODO: remove duplication with mapshaper-each.js
|
|
37319
|
+
var editor = getFeatureEditor(lyr, dataset);
|
|
37320
|
+
var exprOpts = {
|
|
37321
|
+
geojson_editor: editor,
|
|
37322
|
+
context: {splitFeature}
|
|
37323
|
+
};
|
|
37324
|
+
var exp = `this.geojson = splitFeature(this.geojson)`;
|
|
37325
|
+
|
|
37326
|
+
var compiled = compileFeatureExpression(exp, lyr, dataset.arcs, exprOpts);
|
|
37327
|
+
var n = getFeatureCount(lyr);
|
|
37328
|
+
var filter;
|
|
37329
|
+
if (opts && opts.where) {
|
|
37330
|
+
filter = compileValueExpression(opts.where, lyr, dataset.arcs);
|
|
37331
|
+
}
|
|
37332
|
+
for (var i=0; i<n; i++) {
|
|
37333
|
+
if (!filter || filter(i)) {
|
|
37334
|
+
compiled(i);
|
|
37335
|
+
}
|
|
37336
|
+
}
|
|
37337
|
+
replaceLayerContents(lyr, dataset, editor.done());
|
|
37338
|
+
};
|
|
37339
|
+
|
|
37340
|
+
function getSplitFeatureFunction(crs, opts) {
|
|
37341
|
+
var dashLen = opts.dash_length ? convertDistanceParam(opts.dash_length, crs) : 0;
|
|
37342
|
+
var gapLen = opts.gap_length ? convertDistanceParam(opts.gap_length, crs) : 0;
|
|
37343
|
+
if (dashLen > 0 === false) {
|
|
37344
|
+
stop('Missing required segment-length parameter');
|
|
37345
|
+
}
|
|
37346
|
+
if (gapLen >= 0 == false) {
|
|
37347
|
+
stop('Invalid gap-length option');
|
|
37348
|
+
}
|
|
37349
|
+
var splitLine = getSplitLineFunction(crs, dashLen, gapLen, !!opts.planar);
|
|
37350
|
+
return function(feat) {
|
|
37351
|
+
var geom = feat.geometry;
|
|
37352
|
+
if (!geom) return feat;
|
|
37353
|
+
if (geom.type == 'LineString') {
|
|
37354
|
+
geom.type = 'MultiLineString';
|
|
37355
|
+
geom.coordinates = [geom.coordinates];
|
|
37356
|
+
}
|
|
37357
|
+
if (geom.type != 'MultiLineString') {
|
|
37358
|
+
error('Unexpected geometry:', geom.type);
|
|
37359
|
+
}
|
|
37360
|
+
geom.coordinates = geom.coordinates.reduce(function(memo, coords) {
|
|
37361
|
+
try {
|
|
37362
|
+
var parts = splitLine(coords);
|
|
37363
|
+
memo = memo.concat(parts);
|
|
37364
|
+
} catch(e) {
|
|
37365
|
+
console.error(e);
|
|
37366
|
+
throw e;
|
|
37367
|
+
}
|
|
37368
|
+
return memo;
|
|
37369
|
+
}, []);
|
|
37370
|
+
|
|
37371
|
+
return feat;
|
|
37372
|
+
};
|
|
37373
|
+
}
|
|
37374
|
+
|
|
37375
|
+
function getSplitLineFunction(crs, dashLen, gapLen, planar) {
|
|
37376
|
+
var interpolate = getInterpolationFunction(planar ? null : crs);
|
|
37377
|
+
var distance = isLatLngCRS(crs) ? greatCircleDistance : distance2D;
|
|
37378
|
+
var inDash, parts2, interval;
|
|
37379
|
+
function addPart(coords) {
|
|
37380
|
+
if (inDash) parts2.push(coords);
|
|
37381
|
+
if (gapLen > 0) {
|
|
37382
|
+
inDash = !inDash;
|
|
37383
|
+
interval = inDash ? dashLen : gapLen;
|
|
37384
|
+
}
|
|
37385
|
+
}
|
|
37386
|
+
return function splitLineString(coords) {
|
|
37387
|
+
var elapsedDist = 0;
|
|
37388
|
+
var p = coords[0];
|
|
37389
|
+
var coords2 = [p];
|
|
37390
|
+
var segLen, k, prev;
|
|
37391
|
+
// init this LineString
|
|
37392
|
+
inDash = true;
|
|
37393
|
+
parts2 = [];
|
|
37394
|
+
interval = gapLen;
|
|
37395
|
+
for (var i=1, n=coords.length; i<n; i++) {
|
|
37396
|
+
prev = p;
|
|
37397
|
+
p = coords[i];
|
|
37398
|
+
segLen = distance(prev[0], prev[1], p[0], p[1]);
|
|
37399
|
+
while (elapsedDist + segLen >= interval) {
|
|
37400
|
+
k = (interval - elapsedDist) / segLen;
|
|
37401
|
+
prev = interpolate(prev[0], prev[1], p[0], p[1], k);
|
|
37402
|
+
elapsedDist = 0;
|
|
37403
|
+
coords2.push(prev);
|
|
37404
|
+
addPart(coords2);
|
|
37405
|
+
coords2 = [prev];
|
|
37406
|
+
segLen = distance(prev[0], prev[1], p[0], p[1]);
|
|
37407
|
+
}
|
|
37408
|
+
coords2.push(p);
|
|
37409
|
+
elapsedDist += segLen;
|
|
37410
|
+
}
|
|
37411
|
+
if (elapsedDist > 0 && coords2.length > 1) {
|
|
37412
|
+
addPart(coords2);
|
|
37413
|
+
}
|
|
37414
|
+
return parts2;
|
|
37415
|
+
};
|
|
37416
|
+
}
|
|
37417
|
+
|
|
37270
37418
|
cmd.svgStyle = function(lyr, dataset, opts) {
|
|
37271
37419
|
var filter;
|
|
37272
37420
|
if (!lyr.data) {
|
|
@@ -38228,6 +38376,9 @@ ${svg}
|
|
|
38228
38376
|
} else if (name == 'split') {
|
|
38229
38377
|
outputLayers = applyCommandToEachLayer(cmd.splitLayer, targetLayers, opts.expression, opts);
|
|
38230
38378
|
|
|
38379
|
+
} else if (name == 'split-lines') {
|
|
38380
|
+
applyCommandToEachLayer(cmd.splitLines, targetLayers, targetDataset, opts);
|
|
38381
|
+
|
|
38231
38382
|
} else if (name == 'split-on-grid') {
|
|
38232
38383
|
outputLayers = applyCommandToEachLayer(cmd.splitLayerOnGrid, targetLayers, arcs, opts);
|
|
38233
38384
|
|
package/package.json
CHANGED
package/www/mapshaper-gui.js
CHANGED
|
@@ -485,6 +485,7 @@
|
|
|
485
485
|
}
|
|
486
486
|
|
|
487
487
|
function addCSS(el, css) {
|
|
488
|
+
// console.error(css);
|
|
488
489
|
el.style.cssText = mergeCSS(el.style.cssText, css);
|
|
489
490
|
}
|
|
490
491
|
|
|
@@ -1254,7 +1255,7 @@
|
|
|
1254
1255
|
|
|
1255
1256
|
function receiveFiles(files) {
|
|
1256
1257
|
var prevSize = queuedFiles.length;
|
|
1257
|
-
useQuickView = overQuickView;
|
|
1258
|
+
useQuickView = useQuickView || overQuickView;
|
|
1258
1259
|
files = handleZipFiles(utils.toArray(files));
|
|
1259
1260
|
addFilesToQueue(files);
|
|
1260
1261
|
if (queuedFiles.length === 0) return;
|
package/www/mapshaper.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
|
|
3
|
-
var VERSION = "0.5.
|
|
3
|
+
var VERSION = "0.5.75";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
var utils = /*#__PURE__*/Object.freeze({
|
|
@@ -2967,12 +2967,33 @@
|
|
|
2967
2967
|
orient2D(cx, cy, dx, dy, bx, by) <= 0;
|
|
2968
2968
|
}
|
|
2969
2969
|
|
|
2970
|
+
// Useful for determining if a segment that intersects another segment is
|
|
2971
|
+
// entering or leaving an enclosed buffer area
|
|
2972
|
+
// returns -1 if angle of p1p2 -> p3p4 is counter-clockwise (left turn)
|
|
2973
|
+
// returns 1 if angle is clockwise
|
|
2974
|
+
// return 0 if segments are collinear
|
|
2975
|
+
function segmentTurn(p1, p2, p3, p4) {
|
|
2976
|
+
var ax = p1[0],
|
|
2977
|
+
ay = p1[1],
|
|
2978
|
+
bx = p2[0],
|
|
2979
|
+
by = p2[1],
|
|
2980
|
+
// shift p3p4 segment to start at p2
|
|
2981
|
+
dx = bx - p3[0],
|
|
2982
|
+
dy = by - p3[1],
|
|
2983
|
+
cx = p4[0] + dx,
|
|
2984
|
+
cy = p4[1] + dy,
|
|
2985
|
+
orientation = orient2D(ax, ay, bx, by, cx, cy);
|
|
2986
|
+
if (!orientation) return 0;
|
|
2987
|
+
return orientation < 0 ? 1 : -1;
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2970
2990
|
var SegmentGeom = /*#__PURE__*/Object.freeze({
|
|
2971
2991
|
__proto__: null,
|
|
2972
2992
|
segmentIntersection: segmentIntersection,
|
|
2973
2993
|
findClosestPointOnSeg: findClosestPointOnSeg,
|
|
2974
2994
|
orient2D: orient2D,
|
|
2975
|
-
segmentHit: segmentHit
|
|
2995
|
+
segmentHit: segmentHit,
|
|
2996
|
+
segmentTurn: segmentTurn
|
|
2976
2997
|
});
|
|
2977
2998
|
|
|
2978
2999
|
var geom = Object.assign({}, Geom, PolygonGeom, PathGeom, SegmentGeom, PolygonCentroid);
|
|
@@ -8136,10 +8157,14 @@
|
|
|
8136
8157
|
}
|
|
8137
8158
|
|
|
8138
8159
|
function roundToDigits(n, d) {
|
|
8139
|
-
return +n.toFixed(d);
|
|
8160
|
+
return +n.toFixed(d); // string conversion makes this slow
|
|
8161
|
+
}
|
|
8162
|
+
|
|
8163
|
+
function roundToTenths(n) {
|
|
8164
|
+
return (Math.round(n * 10)) / 10;
|
|
8140
8165
|
}
|
|
8141
8166
|
|
|
8142
|
-
// inc: Rounding
|
|
8167
|
+
// inc: Rounding increment (e.g. 0.001 rounds to thousandths)
|
|
8143
8168
|
function getRoundingFunction(inc) {
|
|
8144
8169
|
if (!utils.isNumber(inc) || inc === 0) {
|
|
8145
8170
|
error("Rounding increment must be a non-zero number.");
|
|
@@ -8209,6 +8234,7 @@
|
|
|
8209
8234
|
__proto__: null,
|
|
8210
8235
|
roundToSignificantDigits: roundToSignificantDigits,
|
|
8211
8236
|
roundToDigits: roundToDigits,
|
|
8237
|
+
roundToTenths: roundToTenths,
|
|
8212
8238
|
getRoundingFunction: getRoundingFunction,
|
|
8213
8239
|
getBoundsPrecisionForDisplay: getBoundsPrecisionForDisplay,
|
|
8214
8240
|
getRoundedCoordString: getRoundedCoordString,
|
|
@@ -13811,7 +13837,7 @@
|
|
|
13811
13837
|
var symbolRenderers = {};
|
|
13812
13838
|
|
|
13813
13839
|
function getTransform(xy, scale) {
|
|
13814
|
-
var str = 'translate(' + xy[0] + ' ' + xy[1] + ')';
|
|
13840
|
+
var str = 'translate(' + roundToTenths(xy[0]) + ' ' + roundToTenths(xy[1]) + ')';
|
|
13815
13841
|
if (scale && scale != 1) {
|
|
13816
13842
|
str += ' scale(' + scale + ')';
|
|
13817
13843
|
}
|
|
@@ -19718,6 +19744,23 @@ ${svg}
|
|
|
19718
19744
|
.option('target', targetOpt)
|
|
19719
19745
|
.option('no-replace', noReplaceOpt);
|
|
19720
19746
|
|
|
19747
|
+
parser.command('split-lines')
|
|
19748
|
+
// .describe('divide lines into sections')
|
|
19749
|
+
.option('dash-length', {
|
|
19750
|
+
type: 'distance',
|
|
19751
|
+
describe: 'length of split-apart lines'
|
|
19752
|
+
})
|
|
19753
|
+
.option('gap-length', {
|
|
19754
|
+
type: 'distance',
|
|
19755
|
+
describe: 'length of gap between segments'
|
|
19756
|
+
})
|
|
19757
|
+
.option('planar', {
|
|
19758
|
+
type: 'flag',
|
|
19759
|
+
describe: 'use planar geometry'
|
|
19760
|
+
})
|
|
19761
|
+
.option('where', whereOpt)
|
|
19762
|
+
.option('target', targetOpt);
|
|
19763
|
+
|
|
19721
19764
|
parser.command('split-on-grid')
|
|
19722
19765
|
.describe('split features into separate layers using a grid')
|
|
19723
19766
|
.validate(validateGridOpts)
|
|
@@ -26263,97 +26306,6 @@ ${svg}
|
|
|
26263
26306
|
geom.segmentIntersection(a[0], a[1], b[0], b[1], bb.xmax, bb.ymin, bb.xmin, bb.ymin));
|
|
26264
26307
|
}
|
|
26265
26308
|
|
|
26266
|
-
function getGeodesic(P) {
|
|
26267
|
-
if (!isLatLngCRS(P)) error('Expected an unprojected CRS');
|
|
26268
|
-
var f = P.es / (1 + Math.sqrt(P.one_es));
|
|
26269
|
-
var GeographicLib = require('mproj').internal.GeographicLib;
|
|
26270
|
-
return new GeographicLib.Geodesic.Geodesic(P.a, f);
|
|
26271
|
-
}
|
|
26272
|
-
|
|
26273
|
-
function getPlanarSegmentEndpoint(x, y, bearing, meterDist) {
|
|
26274
|
-
var rad = bearing / 180 * Math.PI;
|
|
26275
|
-
var dx = Math.sin(rad) * meterDist;
|
|
26276
|
-
var dy = Math.cos(rad) * meterDist;
|
|
26277
|
-
return [x + dx, y + dy];
|
|
26278
|
-
}
|
|
26279
|
-
|
|
26280
|
-
// source: https://github.com/mapbox/cheap-ruler/blob/master/index.js
|
|
26281
|
-
function fastGeodeticSegmentFunction(lng, lat, bearing, meterDist) {
|
|
26282
|
-
var D2R = Math.PI / 180;
|
|
26283
|
-
var cos = Math.cos(lat * D2R);
|
|
26284
|
-
var cos2 = 2 * cos * cos - 1;
|
|
26285
|
-
var cos3 = 2 * cos * cos2 - cos;
|
|
26286
|
-
var cos4 = 2 * cos * cos3 - cos2;
|
|
26287
|
-
var cos5 = 2 * cos * cos4 - cos3;
|
|
26288
|
-
var kx = (111.41513 * cos - 0.09455 * cos3 + 0.00012 * cos5) * 1000;
|
|
26289
|
-
var ky = (111.13209 - 0.56605 * cos2 + 0.0012 * cos4) * 1000;
|
|
26290
|
-
var bearingRad = bearing * D2R;
|
|
26291
|
-
var lat2 = lat + Math.cos(bearingRad) * meterDist / ky;
|
|
26292
|
-
var lng2 = lng + Math.sin(bearingRad) * meterDist / kx;
|
|
26293
|
-
return [lng2, lat2];
|
|
26294
|
-
}
|
|
26295
|
-
|
|
26296
|
-
function getGeodeticSegmentFunction(P) {
|
|
26297
|
-
if (!isLatLngCRS(P)) {
|
|
26298
|
-
return getPlanarSegmentEndpoint;
|
|
26299
|
-
}
|
|
26300
|
-
var g = getGeodesic(P);
|
|
26301
|
-
return function(lng, lat, bearing, meterDist) {
|
|
26302
|
-
var o = g.Direct(lat, lng, bearing, meterDist);
|
|
26303
|
-
var p = [o.lon2, o.lat2];
|
|
26304
|
-
return p;
|
|
26305
|
-
};
|
|
26306
|
-
}
|
|
26307
|
-
|
|
26308
|
-
function getFastGeodeticSegmentFunction(P) {
|
|
26309
|
-
// CAREFUL: this function has higher error at very large distances and at the poles
|
|
26310
|
-
// also, it wouldn't work for other planets than Earth
|
|
26311
|
-
return isLatLngCRS(P) ? fastGeodeticSegmentFunction : getPlanarSegmentEndpoint;
|
|
26312
|
-
}
|
|
26313
|
-
|
|
26314
|
-
// Useful for determining if a segment that intersects another segment is
|
|
26315
|
-
// entering or leaving an enclosed buffer area
|
|
26316
|
-
// returns -1 if angle of p1p2 -> p3p4 is counter-clockwise (left turn)
|
|
26317
|
-
// returns 1 if angle is clockwise
|
|
26318
|
-
// return 0 if segments are collinear
|
|
26319
|
-
function segmentTurn(p1, p2, p3, p4) {
|
|
26320
|
-
var ax = p1[0],
|
|
26321
|
-
ay = p1[1],
|
|
26322
|
-
bx = p2[0],
|
|
26323
|
-
by = p2[1],
|
|
26324
|
-
// shift p3p4 segment to start at p2
|
|
26325
|
-
dx = bx - p3[0],
|
|
26326
|
-
dy = by - p3[1],
|
|
26327
|
-
cx = p4[0] + dx,
|
|
26328
|
-
cy = p4[1] + dy,
|
|
26329
|
-
orientation = geom.orient2D(ax, ay, bx, by, cx, cy);
|
|
26330
|
-
if (!orientation) return 0;
|
|
26331
|
-
return orientation < 0 ? 1 : -1;
|
|
26332
|
-
}
|
|
26333
|
-
|
|
26334
|
-
function bearingDegrees(a, b, c, d) {
|
|
26335
|
-
return geom.bearing(a, b, c, d) * 180 / Math.PI;
|
|
26336
|
-
}
|
|
26337
|
-
|
|
26338
|
-
function bearingDegrees2D(a, b, c, d) {
|
|
26339
|
-
return geom.bearing2D(a, b, c, d) * 180 / Math.PI;
|
|
26340
|
-
}
|
|
26341
|
-
|
|
26342
|
-
// return function to calculate bearing of a segment in degrees
|
|
26343
|
-
function getBearingFunction(dataset) {
|
|
26344
|
-
var P = getDatasetCRS(dataset);
|
|
26345
|
-
return isLatLngCRS(P) ? bearingDegrees : bearingDegrees2D;
|
|
26346
|
-
}
|
|
26347
|
-
|
|
26348
|
-
var Geodesic = /*#__PURE__*/Object.freeze({
|
|
26349
|
-
__proto__: null,
|
|
26350
|
-
getPlanarSegmentEndpoint: getPlanarSegmentEndpoint,
|
|
26351
|
-
getGeodeticSegmentFunction: getGeodeticSegmentFunction,
|
|
26352
|
-
getFastGeodeticSegmentFunction: getFastGeodeticSegmentFunction,
|
|
26353
|
-
segmentTurn: segmentTurn,
|
|
26354
|
-
getBearingFunction: getBearingFunction
|
|
26355
|
-
});
|
|
26356
|
-
|
|
26357
26309
|
function getPolylineBufferMaker2(arcs, geod, getBearing, opts) {
|
|
26358
26310
|
var makeLeftBuffer = getPathBufferMaker2(arcs, geod, getBearing, opts);
|
|
26359
26311
|
var geomType = opts.geometry_type;
|
|
@@ -26647,6 +26599,101 @@ ${svg}
|
|
|
26647
26599
|
};
|
|
26648
26600
|
}
|
|
26649
26601
|
|
|
26602
|
+
// GeographicLib docs: https://geographiclib.sourceforge.io/html/js/
|
|
26603
|
+
// https://geographiclib.sourceforge.io/html/js/module-GeographicLib_Geodesic.Geodesic.html
|
|
26604
|
+
// https://geographiclib.sourceforge.io/html/js/tutorial-2-interface.html
|
|
26605
|
+
function getGeodesic(P) {
|
|
26606
|
+
if (!isLatLngCRS(P)) error('Expected an unprojected CRS');
|
|
26607
|
+
var f = P.es / (1 + Math.sqrt(P.one_es));
|
|
26608
|
+
var GeographicLib = require('mproj').internal.GeographicLib;
|
|
26609
|
+
return new GeographicLib.Geodesic.Geodesic(P.a, f);
|
|
26610
|
+
}
|
|
26611
|
+
|
|
26612
|
+
function interpolatePoint2D(ax, ay, bx, by, k) {
|
|
26613
|
+
var j = 1 - k;
|
|
26614
|
+
return [ax * j + bx * k, ay * j + by * k];
|
|
26615
|
+
}
|
|
26616
|
+
|
|
26617
|
+
function getInterpolationFunction(P) {
|
|
26618
|
+
var spherical = P && isLatLngCRS(P);
|
|
26619
|
+
if (!spherical) return interpolatePoint2D;
|
|
26620
|
+
var geod = getGeodesic(P);
|
|
26621
|
+
return function(lng, lat, lng2, lat2, k) {
|
|
26622
|
+
var r = geod.Inverse(lat, lng, lat2, lng2);
|
|
26623
|
+
var dist = r.s12 * k;
|
|
26624
|
+
var r2 = geod.Direct(lat, lng, r.azi1, dist);
|
|
26625
|
+
return [r2.lon2, r2.lat2];
|
|
26626
|
+
};
|
|
26627
|
+
}
|
|
26628
|
+
|
|
26629
|
+
function getPlanarSegmentEndpoint(x, y, bearing, meterDist) {
|
|
26630
|
+
var rad = bearing / 180 * Math.PI;
|
|
26631
|
+
var dx = Math.sin(rad) * meterDist;
|
|
26632
|
+
var dy = Math.cos(rad) * meterDist;
|
|
26633
|
+
return [x + dx, y + dy];
|
|
26634
|
+
}
|
|
26635
|
+
|
|
26636
|
+
// source: https://github.com/mapbox/cheap-ruler/blob/master/index.js
|
|
26637
|
+
function fastGeodeticSegmentFunction(lng, lat, bearing, meterDist) {
|
|
26638
|
+
var D2R = Math.PI / 180;
|
|
26639
|
+
var cos = Math.cos(lat * D2R);
|
|
26640
|
+
var cos2 = 2 * cos * cos - 1;
|
|
26641
|
+
var cos3 = 2 * cos * cos2 - cos;
|
|
26642
|
+
var cos4 = 2 * cos * cos3 - cos2;
|
|
26643
|
+
var cos5 = 2 * cos * cos4 - cos3;
|
|
26644
|
+
var kx = (111.41513 * cos - 0.09455 * cos3 + 0.00012 * cos5) * 1000;
|
|
26645
|
+
var ky = (111.13209 - 0.56605 * cos2 + 0.0012 * cos4) * 1000;
|
|
26646
|
+
var bearingRad = bearing * D2R;
|
|
26647
|
+
var lat2 = lat + Math.cos(bearingRad) * meterDist / ky;
|
|
26648
|
+
var lng2 = lng + Math.sin(bearingRad) * meterDist / kx;
|
|
26649
|
+
return [lng2, lat2];
|
|
26650
|
+
}
|
|
26651
|
+
|
|
26652
|
+
function getGeodeticSegmentFunction(P) {
|
|
26653
|
+
if (!isLatLngCRS(P)) {
|
|
26654
|
+
return getPlanarSegmentEndpoint;
|
|
26655
|
+
}
|
|
26656
|
+
var g = getGeodesic(P);
|
|
26657
|
+
return function(lng, lat, bearing, meterDist) {
|
|
26658
|
+
var o = g.Direct(lat, lng, bearing, meterDist);
|
|
26659
|
+
var p = [o.lon2, o.lat2];
|
|
26660
|
+
return p;
|
|
26661
|
+
};
|
|
26662
|
+
}
|
|
26663
|
+
|
|
26664
|
+
function getFastGeodeticSegmentFunction(P) {
|
|
26665
|
+
// CAREFUL: this function has higher error at very large distances and at the poles
|
|
26666
|
+
// also, it wouldn't work for other planets than Earth
|
|
26667
|
+
return isLatLngCRS(P) ? fastGeodeticSegmentFunction : getPlanarSegmentEndpoint;
|
|
26668
|
+
}
|
|
26669
|
+
|
|
26670
|
+
|
|
26671
|
+
function bearingDegrees(a, b, c, d) {
|
|
26672
|
+
return geom.bearing(a, b, c, d) * 180 / Math.PI;
|
|
26673
|
+
}
|
|
26674
|
+
|
|
26675
|
+
function bearingDegrees2D(a, b, c, d) {
|
|
26676
|
+
return geom.bearing2D(a, b, c, d) * 180 / Math.PI;
|
|
26677
|
+
}
|
|
26678
|
+
|
|
26679
|
+
// return function to calculate bearing of a segment in degrees
|
|
26680
|
+
function getBearingFunction(dataset) {
|
|
26681
|
+
var P = getDatasetCRS(dataset);
|
|
26682
|
+
return isLatLngCRS(P) ? bearingDegrees : bearingDegrees2D;
|
|
26683
|
+
}
|
|
26684
|
+
|
|
26685
|
+
var Geodesic = /*#__PURE__*/Object.freeze({
|
|
26686
|
+
__proto__: null,
|
|
26687
|
+
interpolatePoint2D: interpolatePoint2D,
|
|
26688
|
+
getInterpolationFunction: getInterpolationFunction,
|
|
26689
|
+
getPlanarSegmentEndpoint: getPlanarSegmentEndpoint,
|
|
26690
|
+
getGeodeticSegmentFunction: getGeodeticSegmentFunction,
|
|
26691
|
+
getFastGeodeticSegmentFunction: getFastGeodeticSegmentFunction,
|
|
26692
|
+
bearingDegrees: bearingDegrees,
|
|
26693
|
+
bearingDegrees2D: bearingDegrees2D,
|
|
26694
|
+
getBearingFunction: getBearingFunction
|
|
26695
|
+
});
|
|
26696
|
+
|
|
26650
26697
|
function makePolylineBuffer(lyr, dataset, opts) {
|
|
26651
26698
|
var geojson = makeShapeBufferGeoJSON(lyr, dataset, opts);
|
|
26652
26699
|
var dataset2 = importGeoJSON(geojson, {});
|
|
@@ -31237,6 +31284,7 @@ ${svg}
|
|
|
31237
31284
|
type: 'FeatureCollection',
|
|
31238
31285
|
features: features
|
|
31239
31286
|
};
|
|
31287
|
+
|
|
31240
31288
|
// console.log(JSON.stringify(geojson, null, 2))
|
|
31241
31289
|
return importGeoJSON(geojson);
|
|
31242
31290
|
};
|
|
@@ -34403,11 +34451,6 @@ ${svg}
|
|
|
34403
34451
|
});
|
|
34404
34452
|
}
|
|
34405
34453
|
|
|
34406
|
-
function interpolatePoint2D(ax, ay, bx, by, k) {
|
|
34407
|
-
var j = 1 - k;
|
|
34408
|
-
return [ax * j + bx * k, ay * j + by * k];
|
|
34409
|
-
}
|
|
34410
|
-
|
|
34411
34454
|
function interpolatePointsAlongArc(ids, arcs, interval) {
|
|
34412
34455
|
var iter = arcs.getShapeIter(ids);
|
|
34413
34456
|
var distance = arcs.isPlanar() ? geom.distance2D : geom.greatCircleDistance;
|
|
@@ -37267,6 +37310,111 @@ ${svg}
|
|
|
37267
37310
|
getSplitNameFunction: getSplitNameFunction
|
|
37268
37311
|
});
|
|
37269
37312
|
|
|
37313
|
+
cmd.splitLines = function(lyr, dataset, opts) {
|
|
37314
|
+
var crs = getDatasetCRS(dataset);
|
|
37315
|
+
requirePolylineLayer(lyr);
|
|
37316
|
+
var splitFeature = getSplitFeatureFunction(crs, opts);
|
|
37317
|
+
|
|
37318
|
+
// TODO: remove duplication with mapshaper-each.js
|
|
37319
|
+
var editor = getFeatureEditor(lyr, dataset);
|
|
37320
|
+
var exprOpts = {
|
|
37321
|
+
geojson_editor: editor,
|
|
37322
|
+
context: {splitFeature}
|
|
37323
|
+
};
|
|
37324
|
+
var exp = `this.geojson = splitFeature(this.geojson)`;
|
|
37325
|
+
|
|
37326
|
+
var compiled = compileFeatureExpression(exp, lyr, dataset.arcs, exprOpts);
|
|
37327
|
+
var n = getFeatureCount(lyr);
|
|
37328
|
+
var filter;
|
|
37329
|
+
if (opts && opts.where) {
|
|
37330
|
+
filter = compileValueExpression(opts.where, lyr, dataset.arcs);
|
|
37331
|
+
}
|
|
37332
|
+
for (var i=0; i<n; i++) {
|
|
37333
|
+
if (!filter || filter(i)) {
|
|
37334
|
+
compiled(i);
|
|
37335
|
+
}
|
|
37336
|
+
}
|
|
37337
|
+
replaceLayerContents(lyr, dataset, editor.done());
|
|
37338
|
+
};
|
|
37339
|
+
|
|
37340
|
+
function getSplitFeatureFunction(crs, opts) {
|
|
37341
|
+
var dashLen = opts.dash_length ? convertDistanceParam(opts.dash_length, crs) : 0;
|
|
37342
|
+
var gapLen = opts.gap_length ? convertDistanceParam(opts.gap_length, crs) : 0;
|
|
37343
|
+
if (dashLen > 0 === false) {
|
|
37344
|
+
stop('Missing required segment-length parameter');
|
|
37345
|
+
}
|
|
37346
|
+
if (gapLen >= 0 == false) {
|
|
37347
|
+
stop('Invalid gap-length option');
|
|
37348
|
+
}
|
|
37349
|
+
var splitLine = getSplitLineFunction(crs, dashLen, gapLen, !!opts.planar);
|
|
37350
|
+
return function(feat) {
|
|
37351
|
+
var geom = feat.geometry;
|
|
37352
|
+
if (!geom) return feat;
|
|
37353
|
+
if (geom.type == 'LineString') {
|
|
37354
|
+
geom.type = 'MultiLineString';
|
|
37355
|
+
geom.coordinates = [geom.coordinates];
|
|
37356
|
+
}
|
|
37357
|
+
if (geom.type != 'MultiLineString') {
|
|
37358
|
+
error('Unexpected geometry:', geom.type);
|
|
37359
|
+
}
|
|
37360
|
+
geom.coordinates = geom.coordinates.reduce(function(memo, coords) {
|
|
37361
|
+
try {
|
|
37362
|
+
var parts = splitLine(coords);
|
|
37363
|
+
memo = memo.concat(parts);
|
|
37364
|
+
} catch(e) {
|
|
37365
|
+
console.error(e);
|
|
37366
|
+
throw e;
|
|
37367
|
+
}
|
|
37368
|
+
return memo;
|
|
37369
|
+
}, []);
|
|
37370
|
+
|
|
37371
|
+
return feat;
|
|
37372
|
+
};
|
|
37373
|
+
}
|
|
37374
|
+
|
|
37375
|
+
function getSplitLineFunction(crs, dashLen, gapLen, planar) {
|
|
37376
|
+
var interpolate = getInterpolationFunction(planar ? null : crs);
|
|
37377
|
+
var distance = isLatLngCRS(crs) ? greatCircleDistance : distance2D;
|
|
37378
|
+
var inDash, parts2, interval;
|
|
37379
|
+
function addPart(coords) {
|
|
37380
|
+
if (inDash) parts2.push(coords);
|
|
37381
|
+
if (gapLen > 0) {
|
|
37382
|
+
inDash = !inDash;
|
|
37383
|
+
interval = inDash ? dashLen : gapLen;
|
|
37384
|
+
}
|
|
37385
|
+
}
|
|
37386
|
+
return function splitLineString(coords) {
|
|
37387
|
+
var elapsedDist = 0;
|
|
37388
|
+
var p = coords[0];
|
|
37389
|
+
var coords2 = [p];
|
|
37390
|
+
var segLen, k, prev;
|
|
37391
|
+
// init this LineString
|
|
37392
|
+
inDash = true;
|
|
37393
|
+
parts2 = [];
|
|
37394
|
+
interval = gapLen;
|
|
37395
|
+
for (var i=1, n=coords.length; i<n; i++) {
|
|
37396
|
+
prev = p;
|
|
37397
|
+
p = coords[i];
|
|
37398
|
+
segLen = distance(prev[0], prev[1], p[0], p[1]);
|
|
37399
|
+
while (elapsedDist + segLen >= interval) {
|
|
37400
|
+
k = (interval - elapsedDist) / segLen;
|
|
37401
|
+
prev = interpolate(prev[0], prev[1], p[0], p[1], k);
|
|
37402
|
+
elapsedDist = 0;
|
|
37403
|
+
coords2.push(prev);
|
|
37404
|
+
addPart(coords2);
|
|
37405
|
+
coords2 = [prev];
|
|
37406
|
+
segLen = distance(prev[0], prev[1], p[0], p[1]);
|
|
37407
|
+
}
|
|
37408
|
+
coords2.push(p);
|
|
37409
|
+
elapsedDist += segLen;
|
|
37410
|
+
}
|
|
37411
|
+
if (elapsedDist > 0 && coords2.length > 1) {
|
|
37412
|
+
addPart(coords2);
|
|
37413
|
+
}
|
|
37414
|
+
return parts2;
|
|
37415
|
+
};
|
|
37416
|
+
}
|
|
37417
|
+
|
|
37270
37418
|
cmd.svgStyle = function(lyr, dataset, opts) {
|
|
37271
37419
|
var filter;
|
|
37272
37420
|
if (!lyr.data) {
|
|
@@ -38228,6 +38376,9 @@ ${svg}
|
|
|
38228
38376
|
} else if (name == 'split') {
|
|
38229
38377
|
outputLayers = applyCommandToEachLayer(cmd.splitLayer, targetLayers, opts.expression, opts);
|
|
38230
38378
|
|
|
38379
|
+
} else if (name == 'split-lines') {
|
|
38380
|
+
applyCommandToEachLayer(cmd.splitLines, targetLayers, targetDataset, opts);
|
|
38381
|
+
|
|
38231
38382
|
} else if (name == 'split-on-grid') {
|
|
38232
38383
|
outputLayers = applyCommandToEachLayer(cmd.splitLayerOnGrid, targetLayers, arcs, opts);
|
|
38233
38384
|
|