mapshaper 0.5.73 → 0.5.77
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 +15 -0
- package/bin/mapshaper-gui +1 -1
- package/mapshaper.js +409 -141
- package/package.json +1 -1
- package/www/index.html +2 -2
- package/www/mapshaper-gui.js +2520 -5759
- package/www/mapshaper.js +409 -141
- package/www/page.css +1 -1
package/mapshaper.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
|
|
3
|
-
var VERSION = "0.5.
|
|
3
|
+
var VERSION = "0.5.76";
|
|
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
|
|
8140
8161
|
}
|
|
8141
8162
|
|
|
8142
|
-
|
|
8163
|
+
function roundToTenths(n) {
|
|
8164
|
+
return (Math.round(n * 10)) / 10;
|
|
8165
|
+
}
|
|
8166
|
+
|
|
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
|
}
|
|
@@ -18947,6 +18973,28 @@ ${svg}
|
|
|
18947
18973
|
'$ mapshaper data.json -colorizer name=getColor nodata=#eee breaks=20,40 \\\n' +
|
|
18948
18974
|
' colors=#e0f3db,#a8ddb5,#43a2ca -each \'fill = getColor(RATING)\' -o output.json');
|
|
18949
18975
|
|
|
18976
|
+
parser.command('dashlines')
|
|
18977
|
+
.describe('split lines into sections, with or without a gap')
|
|
18978
|
+
.oldAlias('split-lines')
|
|
18979
|
+
.option('dash-length', {
|
|
18980
|
+
type: 'distance',
|
|
18981
|
+
describe: 'length of split-apart lines (e.g. 200km)'
|
|
18982
|
+
})
|
|
18983
|
+
.option('gap-length', {
|
|
18984
|
+
type: 'distance',
|
|
18985
|
+
describe: 'length of gaps between dashes (default is 0)'
|
|
18986
|
+
})
|
|
18987
|
+
.option('scaled', {
|
|
18988
|
+
type: 'flag',
|
|
18989
|
+
describe: 'scale dashes and gaps to prevent partial dashes'
|
|
18990
|
+
})
|
|
18991
|
+
.option('planar', {
|
|
18992
|
+
type: 'flag',
|
|
18993
|
+
describe: 'use planar geometry'
|
|
18994
|
+
})
|
|
18995
|
+
.option('where', whereOpt)
|
|
18996
|
+
.option('target', targetOpt);
|
|
18997
|
+
|
|
18950
18998
|
parser.command('define')
|
|
18951
18999
|
// .describe('define expression variables')
|
|
18952
19000
|
.option('expression', {
|
|
@@ -26263,97 +26311,6 @@ ${svg}
|
|
|
26263
26311
|
geom.segmentIntersection(a[0], a[1], b[0], b[1], bb.xmax, bb.ymin, bb.xmin, bb.ymin));
|
|
26264
26312
|
}
|
|
26265
26313
|
|
|
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
26314
|
function getPolylineBufferMaker2(arcs, geod, getBearing, opts) {
|
|
26358
26315
|
var makeLeftBuffer = getPathBufferMaker2(arcs, geod, getBearing, opts);
|
|
26359
26316
|
var geomType = opts.geometry_type;
|
|
@@ -26647,6 +26604,101 @@ ${svg}
|
|
|
26647
26604
|
};
|
|
26648
26605
|
}
|
|
26649
26606
|
|
|
26607
|
+
// GeographicLib docs: https://geographiclib.sourceforge.io/html/js/
|
|
26608
|
+
// https://geographiclib.sourceforge.io/html/js/module-GeographicLib_Geodesic.Geodesic.html
|
|
26609
|
+
// https://geographiclib.sourceforge.io/html/js/tutorial-2-interface.html
|
|
26610
|
+
function getGeodesic(P) {
|
|
26611
|
+
if (!isLatLngCRS(P)) error('Expected an unprojected CRS');
|
|
26612
|
+
var f = P.es / (1 + Math.sqrt(P.one_es));
|
|
26613
|
+
var GeographicLib = require('mproj').internal.GeographicLib;
|
|
26614
|
+
return new GeographicLib.Geodesic.Geodesic(P.a, f);
|
|
26615
|
+
}
|
|
26616
|
+
|
|
26617
|
+
function interpolatePoint2D(ax, ay, bx, by, k) {
|
|
26618
|
+
var j = 1 - k;
|
|
26619
|
+
return [ax * j + bx * k, ay * j + by * k];
|
|
26620
|
+
}
|
|
26621
|
+
|
|
26622
|
+
function getInterpolationFunction(P) {
|
|
26623
|
+
var spherical = P && isLatLngCRS(P);
|
|
26624
|
+
if (!spherical) return interpolatePoint2D;
|
|
26625
|
+
var geod = getGeodesic(P);
|
|
26626
|
+
return function(lng, lat, lng2, lat2, k) {
|
|
26627
|
+
var r = geod.Inverse(lat, lng, lat2, lng2);
|
|
26628
|
+
var dist = r.s12 * k;
|
|
26629
|
+
var r2 = geod.Direct(lat, lng, r.azi1, dist);
|
|
26630
|
+
return [r2.lon2, r2.lat2];
|
|
26631
|
+
};
|
|
26632
|
+
}
|
|
26633
|
+
|
|
26634
|
+
function getPlanarSegmentEndpoint(x, y, bearing, meterDist) {
|
|
26635
|
+
var rad = bearing / 180 * Math.PI;
|
|
26636
|
+
var dx = Math.sin(rad) * meterDist;
|
|
26637
|
+
var dy = Math.cos(rad) * meterDist;
|
|
26638
|
+
return [x + dx, y + dy];
|
|
26639
|
+
}
|
|
26640
|
+
|
|
26641
|
+
// source: https://github.com/mapbox/cheap-ruler/blob/master/index.js
|
|
26642
|
+
function fastGeodeticSegmentFunction(lng, lat, bearing, meterDist) {
|
|
26643
|
+
var D2R = Math.PI / 180;
|
|
26644
|
+
var cos = Math.cos(lat * D2R);
|
|
26645
|
+
var cos2 = 2 * cos * cos - 1;
|
|
26646
|
+
var cos3 = 2 * cos * cos2 - cos;
|
|
26647
|
+
var cos4 = 2 * cos * cos3 - cos2;
|
|
26648
|
+
var cos5 = 2 * cos * cos4 - cos3;
|
|
26649
|
+
var kx = (111.41513 * cos - 0.09455 * cos3 + 0.00012 * cos5) * 1000;
|
|
26650
|
+
var ky = (111.13209 - 0.56605 * cos2 + 0.0012 * cos4) * 1000;
|
|
26651
|
+
var bearingRad = bearing * D2R;
|
|
26652
|
+
var lat2 = lat + Math.cos(bearingRad) * meterDist / ky;
|
|
26653
|
+
var lng2 = lng + Math.sin(bearingRad) * meterDist / kx;
|
|
26654
|
+
return [lng2, lat2];
|
|
26655
|
+
}
|
|
26656
|
+
|
|
26657
|
+
function getGeodeticSegmentFunction(P) {
|
|
26658
|
+
if (!isLatLngCRS(P)) {
|
|
26659
|
+
return getPlanarSegmentEndpoint;
|
|
26660
|
+
}
|
|
26661
|
+
var g = getGeodesic(P);
|
|
26662
|
+
return function(lng, lat, bearing, meterDist) {
|
|
26663
|
+
var o = g.Direct(lat, lng, bearing, meterDist);
|
|
26664
|
+
var p = [o.lon2, o.lat2];
|
|
26665
|
+
return p;
|
|
26666
|
+
};
|
|
26667
|
+
}
|
|
26668
|
+
|
|
26669
|
+
function getFastGeodeticSegmentFunction(P) {
|
|
26670
|
+
// CAREFUL: this function has higher error at very large distances and at the poles
|
|
26671
|
+
// also, it wouldn't work for other planets than Earth
|
|
26672
|
+
return isLatLngCRS(P) ? fastGeodeticSegmentFunction : getPlanarSegmentEndpoint;
|
|
26673
|
+
}
|
|
26674
|
+
|
|
26675
|
+
|
|
26676
|
+
function bearingDegrees(a, b, c, d) {
|
|
26677
|
+
return geom.bearing(a, b, c, d) * 180 / Math.PI;
|
|
26678
|
+
}
|
|
26679
|
+
|
|
26680
|
+
function bearingDegrees2D(a, b, c, d) {
|
|
26681
|
+
return geom.bearing2D(a, b, c, d) * 180 / Math.PI;
|
|
26682
|
+
}
|
|
26683
|
+
|
|
26684
|
+
// return function to calculate bearing of a segment in degrees
|
|
26685
|
+
function getBearingFunction(dataset) {
|
|
26686
|
+
var P = getDatasetCRS(dataset);
|
|
26687
|
+
return isLatLngCRS(P) ? bearingDegrees : bearingDegrees2D;
|
|
26688
|
+
}
|
|
26689
|
+
|
|
26690
|
+
var Geodesic = /*#__PURE__*/Object.freeze({
|
|
26691
|
+
__proto__: null,
|
|
26692
|
+
interpolatePoint2D: interpolatePoint2D,
|
|
26693
|
+
getInterpolationFunction: getInterpolationFunction,
|
|
26694
|
+
getPlanarSegmentEndpoint: getPlanarSegmentEndpoint,
|
|
26695
|
+
getGeodeticSegmentFunction: getGeodeticSegmentFunction,
|
|
26696
|
+
getFastGeodeticSegmentFunction: getFastGeodeticSegmentFunction,
|
|
26697
|
+
bearingDegrees: bearingDegrees,
|
|
26698
|
+
bearingDegrees2D: bearingDegrees2D,
|
|
26699
|
+
getBearingFunction: getBearingFunction
|
|
26700
|
+
});
|
|
26701
|
+
|
|
26650
26702
|
function makePolylineBuffer(lyr, dataset, opts) {
|
|
26651
26703
|
var geojson = makeShapeBufferGeoJSON(lyr, dataset, opts);
|
|
26652
26704
|
var dataset2 = importGeoJSON(geojson, {});
|
|
@@ -29682,6 +29734,178 @@ ${svg}
|
|
|
29682
29734
|
getColorizerFunction: getColorizerFunction
|
|
29683
29735
|
});
|
|
29684
29736
|
|
|
29737
|
+
function expressionUsesGeoJSON(exp) {
|
|
29738
|
+
return exp.includes('this.geojson');
|
|
29739
|
+
}
|
|
29740
|
+
|
|
29741
|
+
function getFeatureEditor(lyr, dataset) {
|
|
29742
|
+
var changed = false;
|
|
29743
|
+
var api = {};
|
|
29744
|
+
// need to copy attribute to avoid circular references if geojson is assigned
|
|
29745
|
+
// to a data property.
|
|
29746
|
+
var copy = copyLayer(lyr);
|
|
29747
|
+
var features = exportLayerAsGeoJSON(copy, dataset, {}, true);
|
|
29748
|
+
|
|
29749
|
+
api.get = function(i) {
|
|
29750
|
+
return features[i];
|
|
29751
|
+
};
|
|
29752
|
+
|
|
29753
|
+
api.set = function(feat, i) {
|
|
29754
|
+
changed = true;
|
|
29755
|
+
if (utils.isString(feat)) {
|
|
29756
|
+
feat = JSON.parse(feat);
|
|
29757
|
+
}
|
|
29758
|
+
features[i] = GeoJSON.toFeature(feat); // TODO: validate
|
|
29759
|
+
};
|
|
29760
|
+
|
|
29761
|
+
api.done = function() {
|
|
29762
|
+
if (!changed) return; // read-only expression
|
|
29763
|
+
// TODO: validate number of features, etc.
|
|
29764
|
+
var geojson = {
|
|
29765
|
+
type: 'FeatureCollection',
|
|
29766
|
+
features: features
|
|
29767
|
+
};
|
|
29768
|
+
|
|
29769
|
+
// console.log(JSON.stringify(geojson, null, 2))
|
|
29770
|
+
return importGeoJSON(geojson);
|
|
29771
|
+
};
|
|
29772
|
+
return api;
|
|
29773
|
+
}
|
|
29774
|
+
|
|
29775
|
+
cmd.dashlines = function(lyr, dataset, opts) {
|
|
29776
|
+
var crs = getDatasetCRS(dataset);
|
|
29777
|
+
var defs = getStateVar('defs');
|
|
29778
|
+
var exp = `this.geojson = splitFeature(this.geojson)`;
|
|
29779
|
+
requirePolylineLayer(lyr);
|
|
29780
|
+
defs.splitFeature = getSplitFeatureFunction(crs, opts);
|
|
29781
|
+
cmd.evaluateEachFeature(lyr, dataset, exp, opts);
|
|
29782
|
+
delete defs.splitFeature;
|
|
29783
|
+
};
|
|
29784
|
+
|
|
29785
|
+
function getSplitFeatureFunction(crs, opts) {
|
|
29786
|
+
var dashLen = opts.dash_length ? convertDistanceParam(opts.dash_length, crs) : 0;
|
|
29787
|
+
var gapLen = opts.gap_length ? convertDistanceParam(opts.gap_length, crs) : 0;
|
|
29788
|
+
if (dashLen > 0 === false) {
|
|
29789
|
+
stop('Missing required dash-length parameter');
|
|
29790
|
+
}
|
|
29791
|
+
if (gapLen >= 0 == false) {
|
|
29792
|
+
stop('Invalid gap-length option');
|
|
29793
|
+
}
|
|
29794
|
+
var splitLine = getSplitLineFunction(crs, dashLen, gapLen, opts);
|
|
29795
|
+
return function(feat) {
|
|
29796
|
+
var geom = feat.geometry;
|
|
29797
|
+
if (!geom) return feat;
|
|
29798
|
+
if (geom.type == 'LineString') {
|
|
29799
|
+
geom.type = 'MultiLineString';
|
|
29800
|
+
geom.coordinates = [geom.coordinates];
|
|
29801
|
+
}
|
|
29802
|
+
if (geom.type != 'MultiLineString') {
|
|
29803
|
+
error('Unexpected geometry:', geom.type);
|
|
29804
|
+
}
|
|
29805
|
+
geom.coordinates = geom.coordinates.reduce(function(memo, coords) {
|
|
29806
|
+
try {
|
|
29807
|
+
var parts = splitLine(coords);
|
|
29808
|
+
memo = memo.concat(parts);
|
|
29809
|
+
} catch(e) {
|
|
29810
|
+
console.error(e);
|
|
29811
|
+
throw e;
|
|
29812
|
+
}
|
|
29813
|
+
return memo;
|
|
29814
|
+
}, []);
|
|
29815
|
+
|
|
29816
|
+
return feat;
|
|
29817
|
+
};
|
|
29818
|
+
}
|
|
29819
|
+
|
|
29820
|
+
function getSplitLineFunction(crs, dashLen, gapLen, opts) {
|
|
29821
|
+
var planar = !!opts.planar;
|
|
29822
|
+
var interpolate = getInterpolationFunction(planar ? null : crs);
|
|
29823
|
+
var distance = isLatLngCRS(crs) ? greatCircleDistance : distance2D;
|
|
29824
|
+
var inDash, parts2, interval, scale;
|
|
29825
|
+
function addPart(coords) {
|
|
29826
|
+
if (inDash) parts2.push(coords);
|
|
29827
|
+
if (gapLen > 0) {
|
|
29828
|
+
inDash = !inDash;
|
|
29829
|
+
interval = scale * (inDash ? dashLen : gapLen);
|
|
29830
|
+
}
|
|
29831
|
+
}
|
|
29832
|
+
|
|
29833
|
+
return function splitLineString(coords) {
|
|
29834
|
+
var elapsedDist = 0;
|
|
29835
|
+
var p = coords[0];
|
|
29836
|
+
var coords2 = [p];
|
|
29837
|
+
var segLen, pct, prev;
|
|
29838
|
+
if (opts.scaled) {
|
|
29839
|
+
scale = scaleDashes(dashLen, gapLen, getLineLength(coords, distance));
|
|
29840
|
+
} else {
|
|
29841
|
+
scale = 1;
|
|
29842
|
+
}
|
|
29843
|
+
// init this LineString
|
|
29844
|
+
inDash = gapLen > 0 ? false : true;
|
|
29845
|
+
interval = scale * (inDash ? dashLen : gapLen);
|
|
29846
|
+
if (!inDash) {
|
|
29847
|
+
// start gapped lines with a half-gap
|
|
29848
|
+
// (a half-gap or a half-dash is probably better for rings and intersecting lines)
|
|
29849
|
+
interval *= 0.5;
|
|
29850
|
+
}
|
|
29851
|
+
parts2 = [];
|
|
29852
|
+
for (var i=1, n=coords.length; i<n; i++) {
|
|
29853
|
+
prev = p;
|
|
29854
|
+
p = coords[i];
|
|
29855
|
+
segLen = distance(prev[0], prev[1], p[0], p[1]);
|
|
29856
|
+
if (segLen <= 0) continue;
|
|
29857
|
+
while (elapsedDist + segLen >= interval) {
|
|
29858
|
+
// this segment contains a break either within it or at the far endpoint
|
|
29859
|
+
pct = (interval - elapsedDist) / segLen;
|
|
29860
|
+
if (pct > 0.999 && i == n - 1) {
|
|
29861
|
+
// snap to endpoint (so fp rounding errors don't result in a tiny
|
|
29862
|
+
// last segment)
|
|
29863
|
+
pct = 1;
|
|
29864
|
+
}
|
|
29865
|
+
if (pct < 1) {
|
|
29866
|
+
prev = interpolate(prev[0], prev[1], p[0], p[1], pct);
|
|
29867
|
+
} else {
|
|
29868
|
+
prev = p;
|
|
29869
|
+
}
|
|
29870
|
+
coords2.push(prev);
|
|
29871
|
+
addPart(coords2);
|
|
29872
|
+
// start a new part
|
|
29873
|
+
coords2 = pct < 1 ? [prev] : [];
|
|
29874
|
+
elapsedDist = 0;
|
|
29875
|
+
segLen = (1 - pct) * segLen;
|
|
29876
|
+
}
|
|
29877
|
+
coords2.push(p);
|
|
29878
|
+
elapsedDist += segLen;
|
|
29879
|
+
}
|
|
29880
|
+
if (elapsedDist > 0 && coords2.length > 1) {
|
|
29881
|
+
addPart(coords2);
|
|
29882
|
+
}
|
|
29883
|
+
return parts2;
|
|
29884
|
+
};
|
|
29885
|
+
}
|
|
29886
|
+
|
|
29887
|
+
function getLineLength(coords, distance) {
|
|
29888
|
+
var len = 0;
|
|
29889
|
+
for (var i=1, n=coords.length; i<n; i++) {
|
|
29890
|
+
len += distance(coords[i-1][0], coords[i-1][1], coords[i][0], coords[i][1]);
|
|
29891
|
+
}
|
|
29892
|
+
return len;
|
|
29893
|
+
}
|
|
29894
|
+
|
|
29895
|
+
function scaleDashes(dash, gap, len) {
|
|
29896
|
+
var dash2, gap2;
|
|
29897
|
+
var n = len / (dash + gap); // number of dashes
|
|
29898
|
+
var n1 = Math.floor(n);
|
|
29899
|
+
var n2 = Math.ceil(n);
|
|
29900
|
+
var k1 = len / (n1 * (dash + gap)); // scaled-up dashes, >1
|
|
29901
|
+
var k2 = len / (n2 * (dash + gap)); // scaled-down dashes <1
|
|
29902
|
+
var k = k2;
|
|
29903
|
+
if (k1 < 1/k2 && n1 > 0) {
|
|
29904
|
+
k = k1; // pick the smaller of the two scales
|
|
29905
|
+
}
|
|
29906
|
+
return k;
|
|
29907
|
+
}
|
|
29908
|
+
|
|
29685
29909
|
// This function creates a continuous mosaic of data values in a
|
|
29686
29910
|
// given field by assigning data from adjacent polygon features to polygons
|
|
29687
29911
|
// that contain null values.
|
|
@@ -31206,43 +31430,6 @@ ${svg}
|
|
|
31206
31430
|
}
|
|
31207
31431
|
};
|
|
31208
31432
|
|
|
31209
|
-
function expressionUsesGeoJSON(exp) {
|
|
31210
|
-
return exp.includes('this.geojson');
|
|
31211
|
-
}
|
|
31212
|
-
|
|
31213
|
-
function getFeatureEditor(lyr, dataset) {
|
|
31214
|
-
var changed = false;
|
|
31215
|
-
var api = {};
|
|
31216
|
-
// need to copy attribute to avoid circular references if geojson is assigned
|
|
31217
|
-
// to a data property.
|
|
31218
|
-
var copy = copyLayer(lyr);
|
|
31219
|
-
var features = exportLayerAsGeoJSON(copy, dataset, {}, true);
|
|
31220
|
-
|
|
31221
|
-
api.get = function(i) {
|
|
31222
|
-
return features[i];
|
|
31223
|
-
};
|
|
31224
|
-
|
|
31225
|
-
api.set = function(feat, i) {
|
|
31226
|
-
changed = true;
|
|
31227
|
-
if (utils.isString(feat)) {
|
|
31228
|
-
feat = JSON.parse(feat);
|
|
31229
|
-
}
|
|
31230
|
-
features[i] = GeoJSON.toFeature(feat); // TODO: validate
|
|
31231
|
-
};
|
|
31232
|
-
|
|
31233
|
-
api.done = function() {
|
|
31234
|
-
if (!changed) return; // read-only expression
|
|
31235
|
-
// TODO: validate number of features, etc.
|
|
31236
|
-
var geojson = {
|
|
31237
|
-
type: 'FeatureCollection',
|
|
31238
|
-
features: features
|
|
31239
|
-
};
|
|
31240
|
-
// console.log(JSON.stringify(geojson, null, 2))
|
|
31241
|
-
return importGeoJSON(geojson);
|
|
31242
|
-
};
|
|
31243
|
-
return api;
|
|
31244
|
-
}
|
|
31245
|
-
|
|
31246
31433
|
cmd.evaluateEachFeature = function(lyr, dataset, exp, opts) {
|
|
31247
31434
|
var n = getFeatureCount(lyr),
|
|
31248
31435
|
arcs = dataset.arcs,
|
|
@@ -31848,7 +32035,6 @@ ${svg}
|
|
|
31848
32035
|
return joinTableToLayer(targetLyr, polygonLyr.data, joinFunction, opts);
|
|
31849
32036
|
}
|
|
31850
32037
|
|
|
31851
|
-
|
|
31852
32038
|
function prepJoinLayers(targetLyr, srcLyr) {
|
|
31853
32039
|
if (!targetLyr.data) {
|
|
31854
32040
|
// create an empty data table if target layer is missing attributes
|
|
@@ -34176,6 +34362,32 @@ ${svg}
|
|
|
34176
34362
|
return d;
|
|
34177
34363
|
}
|
|
34178
34364
|
|
|
34365
|
+
function findNearestVertices(p, shp, arcs) {
|
|
34366
|
+
var p2 = findNearestVertex(p[0], p[1], shp, arcs);
|
|
34367
|
+
return findVertexIds(p2.x, p2.y, arcs);
|
|
34368
|
+
}
|
|
34369
|
+
|
|
34370
|
+
// p: point to snap
|
|
34371
|
+
// ids: ids of nearby vertices, possibly including an arc endpoint
|
|
34372
|
+
function snapPointToArcEndpoint(p, ids, arcs) {
|
|
34373
|
+
var p2, p3, dx, dy;
|
|
34374
|
+
ids.forEach(function(idx) {
|
|
34375
|
+
if (vertexIsArcStart(idx, arcs)) {
|
|
34376
|
+
p2 = getVertexCoords(idx + 1, arcs);
|
|
34377
|
+
} else if (vertexIsArcEnd(idx, arcs)) {
|
|
34378
|
+
p2 = getVertexCoords(idx - 1, arcs);
|
|
34379
|
+
}
|
|
34380
|
+
});
|
|
34381
|
+
if (!p2) return;
|
|
34382
|
+
dx = p2[0] - p[0];
|
|
34383
|
+
dy = p2[1] - p[1];
|
|
34384
|
+
if (Math.abs(dx) > Math.abs(dy)) {
|
|
34385
|
+
p[1] = p2[1]; // snap y coord
|
|
34386
|
+
} else {
|
|
34387
|
+
p[0] = p2[0];
|
|
34388
|
+
}
|
|
34389
|
+
}
|
|
34390
|
+
|
|
34179
34391
|
// Find ids of vertices with identical coordinates to x,y in an ArcCollection
|
|
34180
34392
|
// Caveat: does not exclude vertices that are not visible at the
|
|
34181
34393
|
// current level of simplification.
|
|
@@ -34238,6 +34450,18 @@ ${svg}
|
|
|
34238
34450
|
return minLen < Infinity ? {x: minX, y: minY} : null;
|
|
34239
34451
|
}
|
|
34240
34452
|
|
|
34453
|
+
var VertexUtils = /*#__PURE__*/Object.freeze({
|
|
34454
|
+
__proto__: null,
|
|
34455
|
+
findNearestVertices: findNearestVertices,
|
|
34456
|
+
snapPointToArcEndpoint: snapPointToArcEndpoint,
|
|
34457
|
+
findVertexIds: findVertexIds,
|
|
34458
|
+
getVertexCoords: getVertexCoords,
|
|
34459
|
+
vertexIsArcEnd: vertexIsArcEnd,
|
|
34460
|
+
vertexIsArcStart: vertexIsArcStart,
|
|
34461
|
+
setVertexCoords: setVertexCoords,
|
|
34462
|
+
findNearestVertex: findNearestVertex
|
|
34463
|
+
});
|
|
34464
|
+
|
|
34241
34465
|
// Returns x,y coordinates of the point that is at the midpoint of each polyline feature
|
|
34242
34466
|
// Uses 2d cartesian geometry
|
|
34243
34467
|
// TODO: optionally use spherical geometry
|
|
@@ -34365,11 +34589,6 @@ ${svg}
|
|
|
34365
34589
|
});
|
|
34366
34590
|
}
|
|
34367
34591
|
|
|
34368
|
-
function interpolatePoint2D(ax, ay, bx, by, k) {
|
|
34369
|
-
var j = 1 - k;
|
|
34370
|
-
return [ax * j + bx * k, ay * j + by * k];
|
|
34371
|
-
}
|
|
34372
|
-
|
|
34373
34592
|
function interpolatePointsAlongArc(ids, arcs, interval) {
|
|
34374
34593
|
var iter = arcs.getShapeIter(ids);
|
|
34375
34594
|
var distance = arcs.isPlanar() ? geom.distance2D : geom.greatCircleDistance;
|
|
@@ -34621,6 +34840,38 @@ ${svg}
|
|
|
34621
34840
|
}
|
|
34622
34841
|
}
|
|
34623
34842
|
|
|
34843
|
+
function pointsFromPolylinesForJoin(lyr, dataset) {
|
|
34844
|
+
var shapes = lyr.shapes.map(function(shp) {
|
|
34845
|
+
return polylineToMidpoints(shp, dataset.arcs);
|
|
34846
|
+
});
|
|
34847
|
+
return {
|
|
34848
|
+
geometry_type: 'point',
|
|
34849
|
+
shapes: shapes,
|
|
34850
|
+
data: lyr.data // TODO copy if needed
|
|
34851
|
+
};
|
|
34852
|
+
}
|
|
34853
|
+
|
|
34854
|
+
function validateOpts(opts) {
|
|
34855
|
+
if (!opts.point_method) {
|
|
34856
|
+
stop('The "point-method" flag is required for polyline-polygon joins');
|
|
34857
|
+
}
|
|
34858
|
+
}
|
|
34859
|
+
|
|
34860
|
+
function joinPolylinesToPolygons(targetLyr, targetDataset, source, opts) {
|
|
34861
|
+
validateOpts(opts);
|
|
34862
|
+
var pointLyr = pointsFromPolylinesForJoin(source.layer, source.dataset);
|
|
34863
|
+
var retn = joinPointsToPolygons(targetLyr, targetDataset.arcs, pointLyr, opts);
|
|
34864
|
+
return retn;
|
|
34865
|
+
}
|
|
34866
|
+
|
|
34867
|
+
function joinPolygonsToPolylines(targetLyr, targetDataset, source, opts) {
|
|
34868
|
+
validateOpts(opts);
|
|
34869
|
+
var pointLyr = pointsFromPolylinesForJoin(targetLyr, targetDataset);
|
|
34870
|
+
var retn = joinPolygonsToPoints(pointLyr, source.layer, source.dataset.arcs, opts);
|
|
34871
|
+
targetLyr.data = pointLyr.data;
|
|
34872
|
+
return retn;
|
|
34873
|
+
}
|
|
34874
|
+
|
|
34624
34875
|
class TinyQueue {
|
|
34625
34876
|
constructor(data = [], compare = defaultCompare) {
|
|
34626
34877
|
this.data = data;
|
|
@@ -34944,7 +35195,7 @@ ${svg}
|
|
|
34944
35195
|
|
|
34945
35196
|
cmd.join = function(targetLyr, targetDataset, src, opts) {
|
|
34946
35197
|
var srcType, targetType, retn;
|
|
34947
|
-
if (!src || !src.
|
|
35198
|
+
if (!src || !src.dataset) {
|
|
34948
35199
|
stop("Missing a joinable data source");
|
|
34949
35200
|
}
|
|
34950
35201
|
if (opts.keys) {
|
|
@@ -34952,9 +35203,18 @@ ${svg}
|
|
|
34952
35203
|
if (opts.keys.length != 2) {
|
|
34953
35204
|
stop("Expected two key fields: a target field and a source field");
|
|
34954
35205
|
}
|
|
35206
|
+
if (!src.layer.data) {
|
|
35207
|
+
stop("Source layer is missing attribute data");
|
|
35208
|
+
}
|
|
34955
35209
|
retn = joinAttributesToFeatures(targetLyr, src.layer.data, opts);
|
|
34956
35210
|
} else {
|
|
34957
35211
|
// spatial join
|
|
35212
|
+
if (!src.layer.data) {
|
|
35213
|
+
// KLUDGE -- users might want to join a layer without attributes
|
|
35214
|
+
// to test for intersection... the simplest way to support this is
|
|
35215
|
+
// to add an empty data table to the source layer
|
|
35216
|
+
initDataTable(src.layer);
|
|
35217
|
+
}
|
|
34958
35218
|
requireDatasetsHaveCompatibleCRS([targetDataset, src.dataset]);
|
|
34959
35219
|
srcType = src.layer.geometry_type;
|
|
34960
35220
|
targetType = targetLyr.geometry_type;
|
|
@@ -34966,6 +35226,10 @@ ${svg}
|
|
|
34966
35226
|
retn = joinPointsToPoints(targetLyr, src.layer, getDatasetCRS(targetDataset), opts);
|
|
34967
35227
|
} else if (srcType == 'polygon' && targetType == 'polygon') {
|
|
34968
35228
|
retn = joinPolygonsToPolygons(targetLyr, targetDataset, src, opts);
|
|
35229
|
+
} else if (srcType == 'polyline' && targetType == 'polygon') {
|
|
35230
|
+
retn = joinPolylinesToPolygons(targetLyr, targetDataset, src, opts);
|
|
35231
|
+
} else if (srcType == 'polygon' && targetType == 'polyline') {
|
|
35232
|
+
retn = joinPolygonsToPolylines(targetLyr, targetDataset, src, opts);
|
|
34969
35233
|
} else {
|
|
34970
35234
|
stop(utils.format("Unable to join %s geometry to %s geometry",
|
|
34971
35235
|
srcType || 'null', targetType || 'null'));
|
|
@@ -37990,6 +38254,9 @@ ${svg}
|
|
|
37990
38254
|
} else if (name == 'colorizer') {
|
|
37991
38255
|
outputLayers = cmd.colorizer(opts);
|
|
37992
38256
|
|
|
38257
|
+
} else if (name == 'dashlines') {
|
|
38258
|
+
applyCommandToEachLayer(cmd.dashlines, targetLayers, targetDataset, opts);
|
|
38259
|
+
|
|
37993
38260
|
} else if (name == 'define') {
|
|
37994
38261
|
cmd.define(opts);
|
|
37995
38262
|
|
|
@@ -39097,7 +39364,8 @@ ${svg}
|
|
|
39097
39364
|
TopojsonImport,
|
|
39098
39365
|
Topology,
|
|
39099
39366
|
Units,
|
|
39100
|
-
SvgHatch
|
|
39367
|
+
SvgHatch,
|
|
39368
|
+
VertexUtils
|
|
39101
39369
|
);
|
|
39102
39370
|
|
|
39103
39371
|
// The entry point for the core mapshaper module
|