mapshaper 0.7.41 → 0.7.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/mapshaper.js +2498 -63
- package/package.json +2 -2
- package/www/mapshaper-gui.js +119 -24
- package/www/mapshaper.js +2498 -63
- package/www/modules.js +294 -24
package/mapshaper.js
CHANGED
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
return obj === Object(obj); // via underscore
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
function clamp$
|
|
81
|
+
function clamp$3(val, min, max) {
|
|
82
82
|
return val < min ? min : (val > max ? max : val);
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -751,7 +751,7 @@
|
|
|
751
751
|
function findValueByRank(arr, rank) {
|
|
752
752
|
if (!arr.length || rank < 1 || rank > arr.length) error$1("[findValueByRank()] invalid input");
|
|
753
753
|
|
|
754
|
-
rank = clamp$
|
|
754
|
+
rank = clamp$3(rank | 0, 1, arr.length);
|
|
755
755
|
var k = rank - 1, // conv. rank to array index
|
|
756
756
|
n = arr.length,
|
|
757
757
|
l = 0,
|
|
@@ -1125,7 +1125,7 @@
|
|
|
1125
1125
|
// self-import and the resulting Rollup circular-dependency warning.
|
|
1126
1126
|
var utils = {
|
|
1127
1127
|
addThousandsSep, addslashes, arrayToIndex,
|
|
1128
|
-
clamp: clamp$
|
|
1128
|
+
clamp: clamp$3, cleanNumericString, contains, copyElements, countValues, createBuffer,
|
|
1129
1129
|
defaults, difference,
|
|
1130
1130
|
endsWith, every, expandoBuffer, extend: extend$1, extendBuffer,
|
|
1131
1131
|
find: find$1, findMedian, findQuantile, findRankByValue, findStringPrefix,
|
|
@@ -2083,12 +2083,12 @@
|
|
|
2083
2083
|
// TODO: remove this constant, use actual data from dataset CRS,
|
|
2084
2084
|
// also consider using ellipsoidal formulas where greater accuracy might be important.
|
|
2085
2085
|
var R$3 = WGS84.SEMIMAJOR_AXIS;
|
|
2086
|
-
var D2R$
|
|
2087
|
-
var R2D$
|
|
2086
|
+
var D2R$6 = Math.PI / 180;
|
|
2087
|
+
var R2D$5 = 180 / Math.PI;
|
|
2088
2088
|
|
|
2089
2089
|
// Equirectangular projection
|
|
2090
2090
|
function degreesToMeters(deg) {
|
|
2091
|
-
return deg * D2R$
|
|
2091
|
+
return deg * D2R$6 * R$3;
|
|
2092
2092
|
}
|
|
2093
2093
|
|
|
2094
2094
|
function distance3D(ax, ay, az, bx, by, bz) {
|
|
@@ -2104,7 +2104,7 @@
|
|
|
2104
2104
|
return dx * dx + dy * dy;
|
|
2105
2105
|
}
|
|
2106
2106
|
|
|
2107
|
-
function distance2D(ax, ay, bx, by) {
|
|
2107
|
+
function distance2D$1(ax, ay, bx, by) {
|
|
2108
2108
|
var dx = ax - bx,
|
|
2109
2109
|
dy = ay - by;
|
|
2110
2110
|
return Math.sqrt(dx * dx + dy * dy);
|
|
@@ -2235,16 +2235,16 @@
|
|
|
2235
2235
|
|
|
2236
2236
|
function xyzToLngLat(x, y, z, p) {
|
|
2237
2237
|
var d = distance3D(0, 0, 0, x, y, z); // normalize
|
|
2238
|
-
var lat = Math.asin(z / d) / D2R$
|
|
2239
|
-
var lng = Math.atan2(y / d, x / d) / D2R$
|
|
2238
|
+
var lat = Math.asin(z / d) / D2R$6;
|
|
2239
|
+
var lng = Math.atan2(y / d, x / d) / D2R$6;
|
|
2240
2240
|
p[0] = lng;
|
|
2241
2241
|
p[1] = lat;
|
|
2242
2242
|
}
|
|
2243
2243
|
|
|
2244
2244
|
function lngLatToXYZ(lng, lat, p) {
|
|
2245
2245
|
var cosLat;
|
|
2246
|
-
lng *= D2R$
|
|
2247
|
-
lat *= D2R$
|
|
2246
|
+
lng *= D2R$6;
|
|
2247
|
+
lat *= D2R$6;
|
|
2248
2248
|
cosLat = Math.cos(lat);
|
|
2249
2249
|
p[0] = Math.cos(lng) * cosLat * R$3;
|
|
2250
2250
|
p[1] = Math.sin(lng) * cosLat * R$3;
|
|
@@ -2282,7 +2282,7 @@
|
|
|
2282
2282
|
}
|
|
2283
2283
|
|
|
2284
2284
|
function cosine(ax, ay, bx, by, cx, cy) {
|
|
2285
|
-
var den = distance2D(ax, ay, bx, by) * distance2D(bx, by, cx, cy),
|
|
2285
|
+
var den = distance2D$1(ax, ay, bx, by) * distance2D$1(bx, by, cx, cy),
|
|
2286
2286
|
cos = 0;
|
|
2287
2287
|
if (den > 0) {
|
|
2288
2288
|
cos = ((ax - bx) * (cx - bx) + (ay - by) * (cy - by)) / den;
|
|
@@ -2364,9 +2364,9 @@
|
|
|
2364
2364
|
|
|
2365
2365
|
var Geom = /*#__PURE__*/Object.freeze({
|
|
2366
2366
|
__proto__: null,
|
|
2367
|
-
D2R: D2R$
|
|
2367
|
+
D2R: D2R$6,
|
|
2368
2368
|
R: R$3,
|
|
2369
|
-
R2D: R2D$
|
|
2369
|
+
R2D: R2D$5,
|
|
2370
2370
|
bearing: bearing,
|
|
2371
2371
|
bearing2D: bearing2D,
|
|
2372
2372
|
containsBounds: containsBounds,
|
|
@@ -2374,7 +2374,7 @@
|
|
|
2374
2374
|
cosine: cosine,
|
|
2375
2375
|
cosine3D: cosine3D,
|
|
2376
2376
|
degreesToMeters: degreesToMeters,
|
|
2377
|
-
distance2D: distance2D,
|
|
2377
|
+
distance2D: distance2D$1,
|
|
2378
2378
|
distance3D: distance3D,
|
|
2379
2379
|
distanceSq: distanceSq,
|
|
2380
2380
|
distanceSq3D: distanceSq3D,
|
|
@@ -2525,7 +2525,7 @@
|
|
|
2525
2525
|
if (spherical && arcs.isPlanar()) {
|
|
2526
2526
|
error("Expected lat-long coordinates");
|
|
2527
2527
|
}
|
|
2528
|
-
calcLen = spherical ? greatCircleDistance : distance2D;
|
|
2528
|
+
calcLen = spherical ? greatCircleDistance : distance2D$1;
|
|
2529
2529
|
len = 0;
|
|
2530
2530
|
for (var i=0, n=path.length; i<n; i++) {
|
|
2531
2531
|
arcs.forEachArcSegment(path[i], addSegLen);
|
|
@@ -3311,8 +3311,8 @@
|
|
|
3311
3311
|
// angle and seg length ratio thresholds were found by comparing
|
|
3312
3312
|
// fast and robust outputs on sample data
|
|
3313
3313
|
if (innerAngle(ax, ay, bx, by, cx, cy, dx, dy) < 0.1) return true;
|
|
3314
|
-
var len1 = distance2D(ax, ay, bx, by);
|
|
3315
|
-
var len2 = distance2D(cx, cy, dx, dy);
|
|
3314
|
+
var len1 = distance2D$1(ax, ay, bx, by);
|
|
3315
|
+
var len2 = distance2D$1(cx, cy, dx, dy);
|
|
3316
3316
|
var ratio = (len1 < len2 ? len1 / len2 : len2 / len1) || 0;
|
|
3317
3317
|
if (ratio < 0.001) return true;
|
|
3318
3318
|
return false;
|
|
@@ -3405,7 +3405,7 @@
|
|
|
3405
3405
|
}
|
|
3406
3406
|
|
|
3407
3407
|
function snapIfCloser(p, minDist, x, y, x2, y2) {
|
|
3408
|
-
var dist = distance2D(x, y, x2, y2);
|
|
3408
|
+
var dist = distance2D$1(x, y, x2, y2);
|
|
3409
3409
|
if (dist < minDist) {
|
|
3410
3410
|
minDist = dist;
|
|
3411
3411
|
p[0] = x2;
|
|
@@ -6287,7 +6287,1807 @@
|
|
|
6287
6287
|
probablyDecimalDegreeBounds: probablyDecimalDegreeBounds
|
|
6288
6288
|
});
|
|
6289
6289
|
|
|
6290
|
+
/*
|
|
6291
|
+
* Polyhedral unfolding code adapted from d3-geo-polygon.
|
|
6292
|
+
*
|
|
6293
|
+
* Copyright 2017-2024 Mike Bostock
|
|
6294
|
+
* ISC License: https://github.com/d3/d3-geo-polygon/blob/main/LICENSE
|
|
6295
|
+
*/
|
|
6296
|
+
|
|
6297
|
+
var D2R$5 = Math.PI / 180;
|
|
6298
|
+
var R2D$4 = 180 / Math.PI;
|
|
6299
|
+
var EPS$1 = 1e-12;
|
|
6300
|
+
|
|
6301
|
+
// Build a forward-only spherical polyhedral projection.
|
|
6302
|
+
//
|
|
6303
|
+
// config.faces contains spherical polygons in degrees. config.parents defines
|
|
6304
|
+
// a spanning tree of attached faces. faceProjector(face) returns a local
|
|
6305
|
+
// projection accepting radians and returning planar coordinates.
|
|
6306
|
+
function createPolyhedralProjection(config) {
|
|
6307
|
+
var faces = config.faces.map(function(coords, i) {
|
|
6308
|
+
return createFace(coords, i, config.faceSites && config.faceSites[i]);
|
|
6309
|
+
});
|
|
6310
|
+
var parents = config.parents;
|
|
6311
|
+
var attachedPairs = new Set();
|
|
6312
|
+
var faceProjector = config.faceProjector;
|
|
6313
|
+
|
|
6314
|
+
faces.forEach(function(face) {
|
|
6315
|
+
face.project = faceProjector(face);
|
|
6316
|
+
});
|
|
6317
|
+
|
|
6318
|
+
faces.forEach(function(face) {
|
|
6319
|
+
initFaceTransform(face.id);
|
|
6320
|
+
});
|
|
6321
|
+
|
|
6322
|
+
function initFaceTransform(faceId) {
|
|
6323
|
+
var parentId = parents[faceId];
|
|
6324
|
+
var face = faces[faceId];
|
|
6325
|
+
if (face.transform) return;
|
|
6326
|
+
if (parentId < 0) {
|
|
6327
|
+
face.transform = identityMatrix();
|
|
6328
|
+
return;
|
|
6329
|
+
}
|
|
6330
|
+
initFaceTransform(parentId);
|
|
6331
|
+
var parent = faces[parentId];
|
|
6332
|
+
var shared = findSharedEdge(face.coords, parent.coords);
|
|
6333
|
+
if (!shared) {
|
|
6334
|
+
throw new Error('Invalid polyhedral face tree');
|
|
6335
|
+
}
|
|
6336
|
+
var childEdge = shared.map(function(p) {
|
|
6337
|
+
return face.project(p[0] * D2R$5, p[1] * D2R$5);
|
|
6338
|
+
});
|
|
6339
|
+
var parentEdge = shared.map(function(p) {
|
|
6340
|
+
return parent.project(p[0] * D2R$5, p[1] * D2R$5);
|
|
6341
|
+
});
|
|
6342
|
+
face.transform = multiplyMatrices(
|
|
6343
|
+
parent.transform,
|
|
6344
|
+
getEdgeTransform(parentEdge, childEdge)
|
|
6345
|
+
);
|
|
6346
|
+
attachedPairs.add(pairKey(faceId, parentId));
|
|
6347
|
+
}
|
|
6348
|
+
|
|
6349
|
+
var rootEdge = findLongestEdge(faces[0].coords);
|
|
6350
|
+
var rootPlanarEdge = rootEdge.map(function(p) {
|
|
6351
|
+
return faces[0].project(p[0] * D2R$5, p[1] * D2R$5);
|
|
6352
|
+
});
|
|
6353
|
+
var sphericalEdgeLength = angularDistance(rootEdge[0], rootEdge[1]);
|
|
6354
|
+
var planarEdgeLength = distance2D(rootPlanarEdge[0], rootPlanarEdge[1]);
|
|
6355
|
+
var scale = sphericalEdgeLength / planarEdgeLength;
|
|
6356
|
+
var planarAngle = (config.angle || 0) * D2R$5;
|
|
6357
|
+
var edgeIndex = indexEdges(faces, attachedPairs);
|
|
6358
|
+
var outline = buildOutline(faces, attachedPairs).map(function(ring) {
|
|
6359
|
+
return ring.map(transformOutputPoint);
|
|
6360
|
+
});
|
|
6361
|
+
var bounds = getBounds(outline);
|
|
6362
|
+
var centerX = (bounds[0] + bounds[2]) / 2;
|
|
6363
|
+
var centerY = (bounds[1] + bounds[3]) / 2;
|
|
6364
|
+
|
|
6365
|
+
// Centering is calculated after scale and planar rotation.
|
|
6366
|
+
outline.forEach(function(ring) {
|
|
6367
|
+
ring.forEach(function(p) {
|
|
6368
|
+
p[0] -= centerX;
|
|
6369
|
+
p[1] -= centerY;
|
|
6370
|
+
});
|
|
6371
|
+
});
|
|
6372
|
+
|
|
6373
|
+
return {
|
|
6374
|
+
faces: faces,
|
|
6375
|
+
outline: outline,
|
|
6376
|
+
forward: forward,
|
|
6377
|
+
findFace: findFace,
|
|
6378
|
+
findTransitionRegion: findTransitionRegion,
|
|
6379
|
+
getTopology: getTopology
|
|
6380
|
+
};
|
|
6381
|
+
|
|
6382
|
+
function forward(lam, phi) {
|
|
6383
|
+
var rotated = rotateRadians(
|
|
6384
|
+
lam,
|
|
6385
|
+
phi,
|
|
6386
|
+
config.rotation[0] * D2R$5,
|
|
6387
|
+
config.rotation[1] * D2R$5,
|
|
6388
|
+
config.rotation[2] * D2R$5
|
|
6389
|
+
);
|
|
6390
|
+
var face = findFaceRotated(rotated[0], rotated[1]);
|
|
6391
|
+
if (!face) return null;
|
|
6392
|
+
var p = face.project(rotated[0], rotated[1]);
|
|
6393
|
+
p = applyMatrix(face.transform, p);
|
|
6394
|
+
p = transformOutputPoint(p);
|
|
6395
|
+
p[0] -= centerX;
|
|
6396
|
+
p[1] -= centerY;
|
|
6397
|
+
return p;
|
|
6398
|
+
}
|
|
6399
|
+
|
|
6400
|
+
function findFace(lam, phi) {
|
|
6401
|
+
var rotated = rotateRadians(
|
|
6402
|
+
lam,
|
|
6403
|
+
phi,
|
|
6404
|
+
config.rotation[0] * D2R$5,
|
|
6405
|
+
config.rotation[1] * D2R$5,
|
|
6406
|
+
config.rotation[2] * D2R$5
|
|
6407
|
+
);
|
|
6408
|
+
var face = findFaceRotated(rotated[0], rotated[1]);
|
|
6409
|
+
return face ? face.id : -1;
|
|
6410
|
+
}
|
|
6411
|
+
|
|
6412
|
+
function findTransitionRegion(lam, phi) {
|
|
6413
|
+
var rotated = rotateRadians(
|
|
6414
|
+
lam,
|
|
6415
|
+
phi,
|
|
6416
|
+
config.rotation[0] * D2R$5,
|
|
6417
|
+
config.rotation[1] * D2R$5,
|
|
6418
|
+
config.rotation[2] * D2R$5
|
|
6419
|
+
);
|
|
6420
|
+
var face = findFaceRotated(rotated[0], rotated[1]);
|
|
6421
|
+
var region;
|
|
6422
|
+
if (!face) return '-1';
|
|
6423
|
+
region = face.project.findRegion ?
|
|
6424
|
+
face.project.findRegion(rotated[0], rotated[1]) :
|
|
6425
|
+
0;
|
|
6426
|
+
return face.id + ':' + region;
|
|
6427
|
+
}
|
|
6428
|
+
|
|
6429
|
+
function findFaceRotated(lam, phi) {
|
|
6430
|
+
var faceId;
|
|
6431
|
+
if (config.findFace) {
|
|
6432
|
+
faceId = config.findFace(lam, phi);
|
|
6433
|
+
return faceId >= 0 ? faces[faceId] : null;
|
|
6434
|
+
}
|
|
6435
|
+
var p = radiansToVector(lam, phi);
|
|
6436
|
+
for (var i = 0; i < faces.length; i++) {
|
|
6437
|
+
if (faceContainsVector(faces[i], p)) return faces[i];
|
|
6438
|
+
}
|
|
6439
|
+
return null;
|
|
6440
|
+
}
|
|
6441
|
+
|
|
6442
|
+
function transformOutputPoint(p) {
|
|
6443
|
+
var x = p[0] * scale;
|
|
6444
|
+
// Local unfolding uses screen-oriented y coordinates to match the D3
|
|
6445
|
+
// Airocean face layout; return conventional projected coordinates (y up).
|
|
6446
|
+
var y = -p[1] * scale;
|
|
6447
|
+
if (planarAngle) {
|
|
6448
|
+
return [
|
|
6449
|
+
x * Math.cos(planarAngle) - y * Math.sin(planarAngle),
|
|
6450
|
+
x * Math.sin(planarAngle) + y * Math.cos(planarAngle)
|
|
6451
|
+
];
|
|
6452
|
+
}
|
|
6453
|
+
return [x, y];
|
|
6454
|
+
}
|
|
6455
|
+
|
|
6456
|
+
function getTopology(lon0) {
|
|
6457
|
+
var seams = edgeIndex.map(function(edge) {
|
|
6458
|
+
var endpoints = edge.points.map(function(p) {
|
|
6459
|
+
var q = rotateRadians(
|
|
6460
|
+
p[0] * D2R$5,
|
|
6461
|
+
p[1] * D2R$5,
|
|
6462
|
+
config.rotation[0] * D2R$5,
|
|
6463
|
+
config.rotation[1] * D2R$5,
|
|
6464
|
+
config.rotation[2] * D2R$5,
|
|
6465
|
+
true
|
|
6466
|
+
);
|
|
6467
|
+
return [normalizeLongitude$1(q[0] * R2D$4 + lon0), q[1] * R2D$4];
|
|
6468
|
+
});
|
|
6469
|
+
var paths = splitPathAtAntimeridian$1(
|
|
6470
|
+
interpolateGreatCircle(endpoints[0], endpoints[1], 0.05)
|
|
6471
|
+
);
|
|
6472
|
+
paths.forEach(function(path) {
|
|
6473
|
+
// The mask must be wider than the tiny chord error between samples.
|
|
6474
|
+
path.mask_width = 4e-5;
|
|
6475
|
+
});
|
|
6476
|
+
return {
|
|
6477
|
+
type: edge.attached ? 'attached' : 'cut',
|
|
6478
|
+
faces: edge.faces,
|
|
6479
|
+
paths: paths
|
|
6480
|
+
};
|
|
6481
|
+
});
|
|
6482
|
+
var regionPairs = new Map();
|
|
6483
|
+
edgeIndex.forEach(function(edge) {
|
|
6484
|
+
if (edge.faces.length == 2) {
|
|
6485
|
+
regionPairs.set(pairKey(edge.faces[0], edge.faces[1]), edge.attached);
|
|
6486
|
+
}
|
|
6487
|
+
});
|
|
6488
|
+
return {
|
|
6489
|
+
regions: faces.map(function(face) {
|
|
6490
|
+
return {id: face.id};
|
|
6491
|
+
}),
|
|
6492
|
+
seams: seams,
|
|
6493
|
+
findRegion: function(lon, lat) {
|
|
6494
|
+
var lam = normalizeLongitude$1(lon - lon0) * D2R$5;
|
|
6495
|
+
return findFace(lam, lat * D2R$5);
|
|
6496
|
+
},
|
|
6497
|
+
findTransitionRegion: function(lon, lat) {
|
|
6498
|
+
var lam = normalizeLongitude$1(lon - lon0) * D2R$5;
|
|
6499
|
+
return findTransitionRegion(lam, lat * D2R$5);
|
|
6500
|
+
},
|
|
6501
|
+
regionsAreAttached: function(a, b) {
|
|
6502
|
+
return regionPairs.get(pairKey(a, b)) === true;
|
|
6503
|
+
},
|
|
6504
|
+
outline: outline.map(function(ring) {
|
|
6505
|
+
return ring.map(function(p) {
|
|
6506
|
+
return p.concat();
|
|
6507
|
+
});
|
|
6508
|
+
})
|
|
6509
|
+
};
|
|
6510
|
+
}
|
|
6511
|
+
}
|
|
6512
|
+
|
|
6513
|
+
function rotateSphericalRadians(lam, phi, rotation, invert) {
|
|
6514
|
+
return rotateRadians(
|
|
6515
|
+
lam,
|
|
6516
|
+
phi,
|
|
6517
|
+
(rotation[0] || 0) * D2R$5,
|
|
6518
|
+
(rotation[1] || 0) * D2R$5,
|
|
6519
|
+
(rotation[2] || 0) * D2R$5,
|
|
6520
|
+
invert
|
|
6521
|
+
);
|
|
6522
|
+
}
|
|
6523
|
+
|
|
6524
|
+
function createFace(coords, id, site) {
|
|
6525
|
+
var vectors = coords.map(function(p) {
|
|
6526
|
+
return degreesToVector$2(p[0], p[1]);
|
|
6527
|
+
});
|
|
6528
|
+
var inside = normalizeVector$1(vectors.reduce(function(sum, p) {
|
|
6529
|
+
sum[0] += p[0];
|
|
6530
|
+
sum[1] += p[1];
|
|
6531
|
+
sum[2] += p[2];
|
|
6532
|
+
return sum;
|
|
6533
|
+
}, [0, 0, 0]));
|
|
6534
|
+
var edges = [];
|
|
6535
|
+
for (var i = 0; i < vectors.length; i++) {
|
|
6536
|
+
var a = vectors[i];
|
|
6537
|
+
var b = vectors[(i + 1) % vectors.length];
|
|
6538
|
+
var normal = cross$1(a, b);
|
|
6539
|
+
edges.push({
|
|
6540
|
+
normal: normal,
|
|
6541
|
+
sign: dot$1(normal, inside) < 0 ? -1 : 1
|
|
6542
|
+
});
|
|
6543
|
+
}
|
|
6544
|
+
return {
|
|
6545
|
+
id: id,
|
|
6546
|
+
coords: coords,
|
|
6547
|
+
vectors: vectors,
|
|
6548
|
+
edges: edges,
|
|
6549
|
+
centroid: site || vectorToDegrees$2(inside)
|
|
6550
|
+
};
|
|
6551
|
+
}
|
|
6552
|
+
|
|
6553
|
+
function faceContainsVector(face, p) {
|
|
6554
|
+
for (var i = 0; i < face.edges.length; i++) {
|
|
6555
|
+
var edge = face.edges[i];
|
|
6556
|
+
if (dot$1(edge.normal, p) * edge.sign < -EPS$1) return false;
|
|
6557
|
+
}
|
|
6558
|
+
return true;
|
|
6559
|
+
}
|
|
6560
|
+
|
|
6561
|
+
function indexEdges(faces, attachedPairs) {
|
|
6562
|
+
var index = new Map();
|
|
6563
|
+
faces.forEach(function(face) {
|
|
6564
|
+
for (var i = 0; i < face.coords.length; i++) {
|
|
6565
|
+
var a = face.coords[i];
|
|
6566
|
+
var b = face.coords[(i + 1) % face.coords.length];
|
|
6567
|
+
var key = edgeKey(a, b);
|
|
6568
|
+
var edge = index.get(key);
|
|
6569
|
+
if (!edge) {
|
|
6570
|
+
edge = {points: [a, b], faces: []};
|
|
6571
|
+
index.set(key, edge);
|
|
6572
|
+
}
|
|
6573
|
+
edge.faces.push(face.id);
|
|
6574
|
+
}
|
|
6575
|
+
});
|
|
6576
|
+
return Array.from(index.values()).map(function(edge) {
|
|
6577
|
+
edge.attached = edge.faces.length == 2 &&
|
|
6578
|
+
attachedPairs.has(pairKey(edge.faces[0], edge.faces[1]));
|
|
6579
|
+
return edge;
|
|
6580
|
+
});
|
|
6581
|
+
}
|
|
6582
|
+
|
|
6583
|
+
function buildOutline(faces, attachedPairs) {
|
|
6584
|
+
var edges = [];
|
|
6585
|
+
faces.forEach(function(face) {
|
|
6586
|
+
for (var i = 0; i < face.coords.length; i++) {
|
|
6587
|
+
var a = face.coords[i];
|
|
6588
|
+
var b = face.coords[(i + 1) % face.coords.length];
|
|
6589
|
+
var adjacent = findAdjacentFace(faces, face.id, a, b);
|
|
6590
|
+
if (adjacent >= 0 && attachedPairs.has(pairKey(face.id, adjacent))) continue;
|
|
6591
|
+
edges.push([
|
|
6592
|
+
applyMatrix(face.transform, face.project(a[0] * D2R$5, a[1] * D2R$5)),
|
|
6593
|
+
applyMatrix(face.transform, face.project(b[0] * D2R$5, b[1] * D2R$5))
|
|
6594
|
+
]);
|
|
6595
|
+
}
|
|
6596
|
+
});
|
|
6597
|
+
return connectEdges(edges);
|
|
6598
|
+
}
|
|
6599
|
+
|
|
6600
|
+
function connectEdges(edges) {
|
|
6601
|
+
var unused = edges.concat();
|
|
6602
|
+
var rings = [];
|
|
6603
|
+
while (unused.length) {
|
|
6604
|
+
var edge = unused.pop();
|
|
6605
|
+
var ring = [edge[0], edge[1]];
|
|
6606
|
+
while (!pointsAlmostEqual(ring[0], ring[ring.length - 1])) {
|
|
6607
|
+
var last = ring[ring.length - 1];
|
|
6608
|
+
var found = -1;
|
|
6609
|
+
var next;
|
|
6610
|
+
for (var i = 0; i < unused.length; i++) {
|
|
6611
|
+
if (pointsAlmostEqual(last, unused[i][0])) {
|
|
6612
|
+
found = i;
|
|
6613
|
+
next = unused[i][1];
|
|
6614
|
+
break;
|
|
6615
|
+
}
|
|
6616
|
+
if (pointsAlmostEqual(last, unused[i][1])) {
|
|
6617
|
+
found = i;
|
|
6618
|
+
next = unused[i][0];
|
|
6619
|
+
break;
|
|
6620
|
+
}
|
|
6621
|
+
}
|
|
6622
|
+
if (found < 0) break;
|
|
6623
|
+
unused.splice(found, 1);
|
|
6624
|
+
ring.push(next);
|
|
6625
|
+
}
|
|
6626
|
+
if (ring.length > 3 && pointsAlmostEqual(ring[0], ring[ring.length - 1])) {
|
|
6627
|
+
ring[ring.length - 1] = ring[0].concat();
|
|
6628
|
+
rings.push(ring);
|
|
6629
|
+
}
|
|
6630
|
+
}
|
|
6631
|
+
return rings;
|
|
6632
|
+
}
|
|
6633
|
+
|
|
6634
|
+
function findAdjacentFace(faces, faceId, a, b) {
|
|
6635
|
+
var key = edgeKey(a, b);
|
|
6636
|
+
for (var i = 0; i < faces.length; i++) {
|
|
6637
|
+
if (i == faceId) continue;
|
|
6638
|
+
var coords = faces[i].coords;
|
|
6639
|
+
for (var j = 0; j < coords.length; j++) {
|
|
6640
|
+
if (edgeKey(coords[j], coords[(j + 1) % coords.length]) == key) return i;
|
|
6641
|
+
}
|
|
6642
|
+
}
|
|
6643
|
+
return -1;
|
|
6644
|
+
}
|
|
6645
|
+
|
|
6646
|
+
function findSharedEdge(a, b) {
|
|
6647
|
+
for (var i = 0; i < a.length; i++) {
|
|
6648
|
+
var p = a[i];
|
|
6649
|
+
for (var j = 0; j < b.length; j++) {
|
|
6650
|
+
var q = a[(i + 1) % a.length];
|
|
6651
|
+
var c = b[j];
|
|
6652
|
+
var d = b[(j + 1) % b.length];
|
|
6653
|
+
if (sameSphericalPoint(p, c) && sameSphericalPoint(q, d) ||
|
|
6654
|
+
sameSphericalPoint(p, d) && sameSphericalPoint(q, c)) {
|
|
6655
|
+
return [p, q];
|
|
6656
|
+
}
|
|
6657
|
+
}
|
|
6658
|
+
}
|
|
6659
|
+
return null;
|
|
6660
|
+
}
|
|
6661
|
+
|
|
6662
|
+
function findLongestEdge(coords) {
|
|
6663
|
+
var longest = null;
|
|
6664
|
+
var max = -Infinity;
|
|
6665
|
+
for (var i = 0; i < coords.length; i++) {
|
|
6666
|
+
var edge = [coords[i], coords[(i + 1) % coords.length]];
|
|
6667
|
+
var length = angularDistance(edge[0], edge[1]);
|
|
6668
|
+
if (length > max) {
|
|
6669
|
+
longest = edge;
|
|
6670
|
+
max = length;
|
|
6671
|
+
}
|
|
6672
|
+
}
|
|
6673
|
+
return longest;
|
|
6674
|
+
}
|
|
6675
|
+
|
|
6676
|
+
function getEdgeTransform(dest, src) {
|
|
6677
|
+
var ux = dest[1][0] - dest[0][0];
|
|
6678
|
+
var uy = dest[1][1] - dest[0][1];
|
|
6679
|
+
var vx = src[1][0] - src[0][0];
|
|
6680
|
+
var vy = src[1][1] - src[0][1];
|
|
6681
|
+
var scale = Math.sqrt((ux * ux + uy * uy) / (vx * vx + vy * vy));
|
|
6682
|
+
var angle = Math.atan2(ux * vy - uy * vx, ux * vx + uy * vy);
|
|
6683
|
+
var cos = Math.cos(angle) * scale;
|
|
6684
|
+
var sin = Math.sin(angle) * scale;
|
|
6685
|
+
return [
|
|
6686
|
+
cos,
|
|
6687
|
+
sin,
|
|
6688
|
+
dest[0][0] - cos * src[0][0] - sin * src[0][1],
|
|
6689
|
+
-sin,
|
|
6690
|
+
cos,
|
|
6691
|
+
dest[0][1] + sin * src[0][0] - cos * src[0][1]
|
|
6692
|
+
];
|
|
6693
|
+
}
|
|
6694
|
+
|
|
6695
|
+
function identityMatrix() {
|
|
6696
|
+
return [1, 0, 0, 0, 1, 0];
|
|
6697
|
+
}
|
|
6698
|
+
|
|
6699
|
+
function multiplyMatrices(a, b) {
|
|
6700
|
+
return [
|
|
6701
|
+
a[0] * b[0] + a[1] * b[3],
|
|
6702
|
+
a[0] * b[1] + a[1] * b[4],
|
|
6703
|
+
a[0] * b[2] + a[1] * b[5] + a[2],
|
|
6704
|
+
a[3] * b[0] + a[4] * b[3],
|
|
6705
|
+
a[3] * b[1] + a[4] * b[4],
|
|
6706
|
+
a[3] * b[2] + a[4] * b[5] + a[5]
|
|
6707
|
+
];
|
|
6708
|
+
}
|
|
6709
|
+
|
|
6710
|
+
function applyMatrix(m, p) {
|
|
6711
|
+
return [
|
|
6712
|
+
m[0] * p[0] + m[1] * p[1] + m[2],
|
|
6713
|
+
m[3] * p[0] + m[4] * p[1] + m[5]
|
|
6714
|
+
];
|
|
6715
|
+
}
|
|
6716
|
+
|
|
6717
|
+
function rotateRadians(lam, phi, deltaLam, deltaPhi, deltaGamma, invert) {
|
|
6718
|
+
if (invert) {
|
|
6719
|
+
var inv = rotatePhiGamma$1(lam, phi, deltaPhi, deltaGamma, true);
|
|
6720
|
+
return [normalizeRadians$1(inv[0] - deltaLam), inv[1]];
|
|
6721
|
+
}
|
|
6722
|
+
lam = normalizeRadians$1(lam + deltaLam);
|
|
6723
|
+
return rotatePhiGamma$1(lam, phi, deltaPhi, deltaGamma, false);
|
|
6724
|
+
}
|
|
6725
|
+
|
|
6726
|
+
// Based on d3-geo spherical rotation (ISC license).
|
|
6727
|
+
function rotatePhiGamma$1(lam, phi, deltaPhi, deltaGamma, invert) {
|
|
6728
|
+
var cosDeltaPhi = Math.cos(deltaPhi);
|
|
6729
|
+
var sinDeltaPhi = Math.sin(deltaPhi);
|
|
6730
|
+
var cosDeltaGamma = Math.cos(deltaGamma);
|
|
6731
|
+
var sinDeltaGamma = Math.sin(deltaGamma);
|
|
6732
|
+
var cosPhi = Math.cos(phi);
|
|
6733
|
+
var x = Math.cos(lam) * cosPhi;
|
|
6734
|
+
var y = Math.sin(lam) * cosPhi;
|
|
6735
|
+
var z = Math.sin(phi);
|
|
6736
|
+
var k;
|
|
6737
|
+
if (invert) {
|
|
6738
|
+
k = z * cosDeltaGamma - y * sinDeltaGamma;
|
|
6739
|
+
return [
|
|
6740
|
+
Math.atan2(y * cosDeltaGamma + z * sinDeltaGamma,
|
|
6741
|
+
x * cosDeltaPhi + k * sinDeltaPhi),
|
|
6742
|
+
Math.asin(clamp$2(k * cosDeltaPhi - x * sinDeltaPhi, -1, 1))
|
|
6743
|
+
];
|
|
6744
|
+
}
|
|
6745
|
+
k = z * cosDeltaPhi + x * sinDeltaPhi;
|
|
6746
|
+
return [
|
|
6747
|
+
Math.atan2(y * cosDeltaGamma - k * sinDeltaGamma,
|
|
6748
|
+
x * cosDeltaPhi - z * sinDeltaPhi),
|
|
6749
|
+
Math.asin(clamp$2(k * cosDeltaGamma + y * sinDeltaGamma, -1, 1))
|
|
6750
|
+
];
|
|
6751
|
+
}
|
|
6752
|
+
|
|
6753
|
+
function interpolateGreatCircle(a, b, interval) {
|
|
6754
|
+
var av = degreesToVector$2(a[0], a[1]);
|
|
6755
|
+
var bv = degreesToVector$2(b[0], b[1]);
|
|
6756
|
+
var angle = Math.acos(clamp$2(dot$1(av, bv), -1, 1));
|
|
6757
|
+
var n = Math.max(1, Math.ceil(angle * R2D$4 / interval));
|
|
6758
|
+
var sinAngle = Math.sin(angle);
|
|
6759
|
+
var points = [];
|
|
6760
|
+
for (var i = 0; i <= n; i++) {
|
|
6761
|
+
var t = i / n;
|
|
6762
|
+
var p;
|
|
6763
|
+
if (sinAngle < EPS$1) {
|
|
6764
|
+
p = av;
|
|
6765
|
+
} else {
|
|
6766
|
+
var ka = Math.sin((1 - t) * angle) / sinAngle;
|
|
6767
|
+
var kb = Math.sin(t * angle) / sinAngle;
|
|
6768
|
+
p = [
|
|
6769
|
+
av[0] * ka + bv[0] * kb,
|
|
6770
|
+
av[1] * ka + bv[1] * kb,
|
|
6771
|
+
av[2] * ka + bv[2] * kb
|
|
6772
|
+
];
|
|
6773
|
+
}
|
|
6774
|
+
points.push(vectorToDegrees$2(normalizeVector$1(p)));
|
|
6775
|
+
}
|
|
6776
|
+
return points;
|
|
6777
|
+
}
|
|
6778
|
+
|
|
6779
|
+
function splitPathAtAntimeridian$1(path) {
|
|
6780
|
+
var paths = [];
|
|
6781
|
+
var part = [path[0]];
|
|
6782
|
+
for (var i = 1; i < path.length; i++) {
|
|
6783
|
+
var a = path[i - 1];
|
|
6784
|
+
var b = path[i];
|
|
6785
|
+
if (Math.abs(a[0] - b[0]) > 180) {
|
|
6786
|
+
var bLon = b[0] + (b[0] < a[0] ? 360 : -360);
|
|
6787
|
+
var edgeLon = a[0] < 0 ? -180 : 180;
|
|
6788
|
+
var t = (edgeLon - a[0]) / (bLon - a[0]);
|
|
6789
|
+
var lat = a[1] + (b[1] - a[1]) * t;
|
|
6790
|
+
part.push([edgeLon, lat]);
|
|
6791
|
+
paths.push(part);
|
|
6792
|
+
part = [[-edgeLon, lat], b];
|
|
6793
|
+
} else {
|
|
6794
|
+
part.push(b);
|
|
6795
|
+
}
|
|
6796
|
+
}
|
|
6797
|
+
if (part.length > 1) paths.push(part);
|
|
6798
|
+
return paths;
|
|
6799
|
+
}
|
|
6800
|
+
|
|
6801
|
+
function getBounds(rings) {
|
|
6802
|
+
var bounds = [Infinity, Infinity, -Infinity, -Infinity];
|
|
6803
|
+
rings.forEach(function(ring) {
|
|
6804
|
+
ring.forEach(function(p) {
|
|
6805
|
+
bounds[0] = Math.min(bounds[0], p[0]);
|
|
6806
|
+
bounds[1] = Math.min(bounds[1], p[1]);
|
|
6807
|
+
bounds[2] = Math.max(bounds[2], p[0]);
|
|
6808
|
+
bounds[3] = Math.max(bounds[3], p[1]);
|
|
6809
|
+
});
|
|
6810
|
+
});
|
|
6811
|
+
return bounds;
|
|
6812
|
+
}
|
|
6813
|
+
|
|
6814
|
+
function angularDistance(a, b) {
|
|
6815
|
+
return Math.acos(clamp$2(dot$1(degreesToVector$2(a[0], a[1]),
|
|
6816
|
+
degreesToVector$2(b[0], b[1])), -1, 1));
|
|
6817
|
+
}
|
|
6818
|
+
|
|
6819
|
+
function degreesToVector$2(lon, lat) {
|
|
6820
|
+
return radiansToVector(lon * D2R$5, lat * D2R$5);
|
|
6821
|
+
}
|
|
6822
|
+
|
|
6823
|
+
function radiansToVector(lam, phi) {
|
|
6824
|
+
var cosPhi = Math.cos(phi);
|
|
6825
|
+
return [Math.cos(lam) * cosPhi, Math.sin(lam) * cosPhi, Math.sin(phi)];
|
|
6826
|
+
}
|
|
6827
|
+
|
|
6828
|
+
function vectorToDegrees$2(p) {
|
|
6829
|
+
return [
|
|
6830
|
+
Math.atan2(p[1], p[0]) * R2D$4,
|
|
6831
|
+
Math.asin(clamp$2(p[2], -1, 1)) * R2D$4
|
|
6832
|
+
];
|
|
6833
|
+
}
|
|
6834
|
+
|
|
6835
|
+
function normalizeVector$1(p) {
|
|
6836
|
+
var k = 1 / Math.sqrt(dot$1(p, p));
|
|
6837
|
+
return [p[0] * k, p[1] * k, p[2] * k];
|
|
6838
|
+
}
|
|
6839
|
+
|
|
6840
|
+
function cross$1(a, b) {
|
|
6841
|
+
return [
|
|
6842
|
+
a[1] * b[2] - a[2] * b[1],
|
|
6843
|
+
a[2] * b[0] - a[0] * b[2],
|
|
6844
|
+
a[0] * b[1] - a[1] * b[0]
|
|
6845
|
+
];
|
|
6846
|
+
}
|
|
6847
|
+
|
|
6848
|
+
function dot$1(a, b) {
|
|
6849
|
+
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
6850
|
+
}
|
|
6851
|
+
|
|
6852
|
+
function distance2D(a, b) {
|
|
6853
|
+
var dx = b[0] - a[0];
|
|
6854
|
+
var dy = b[1] - a[1];
|
|
6855
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
6856
|
+
}
|
|
6857
|
+
|
|
6858
|
+
function sameSphericalPoint(a, b) {
|
|
6859
|
+
return Math.abs(a[0] - b[0]) < EPS$1 && Math.abs(a[1] - b[1]) < EPS$1;
|
|
6860
|
+
}
|
|
6861
|
+
|
|
6862
|
+
function pointsAlmostEqual(a, b) {
|
|
6863
|
+
return Math.abs(a[0] - b[0]) < 1e-9 && Math.abs(a[1] - b[1]) < 1e-9;
|
|
6864
|
+
}
|
|
6865
|
+
|
|
6866
|
+
function pointKey$1(p) {
|
|
6867
|
+
return p[0].toFixed(12) + ',' + p[1].toFixed(12);
|
|
6868
|
+
}
|
|
6869
|
+
|
|
6870
|
+
function edgeKey(a, b) {
|
|
6871
|
+
var ka = pointKey$1(a);
|
|
6872
|
+
var kb = pointKey$1(b);
|
|
6873
|
+
return ka < kb ? ka + '|' + kb : kb + '|' + ka;
|
|
6874
|
+
}
|
|
6875
|
+
|
|
6876
|
+
function pairKey(a, b) {
|
|
6877
|
+
return a < b ? a + '~' + b : b + '~' + a;
|
|
6878
|
+
}
|
|
6879
|
+
|
|
6880
|
+
function normalizeRadians$1(lam) {
|
|
6881
|
+
while (lam > Math.PI) lam -= Math.PI * 2;
|
|
6882
|
+
while (lam < -Math.PI) lam += Math.PI * 2;
|
|
6883
|
+
return lam;
|
|
6884
|
+
}
|
|
6885
|
+
|
|
6886
|
+
function normalizeLongitude$1(lon) {
|
|
6887
|
+
while (lon > 180) lon -= 360;
|
|
6888
|
+
while (lon < -180) lon += 360;
|
|
6889
|
+
return lon;
|
|
6890
|
+
}
|
|
6891
|
+
|
|
6892
|
+
function clamp$2(val, min, max) {
|
|
6893
|
+
return Math.max(min, Math.min(max, val));
|
|
6894
|
+
}
|
|
6895
|
+
|
|
6896
|
+
/*
|
|
6897
|
+
* Buckminster Fuller's Airocean arrangement of the icosahedron.
|
|
6898
|
+
*
|
|
6899
|
+
* Face layout adapted from d3-geo-polygon (ISC license):
|
|
6900
|
+
* https://github.com/d3/d3-geo-polygon/blob/main/src/airocean.js
|
|
6901
|
+
*
|
|
6902
|
+
* The Gray-Fuller raw transform below is based on Robert W. Gray's published
|
|
6903
|
+
* equations and Philippe Rivière's public-domain clean-room implementation:
|
|
6904
|
+
* https://github.com/d3/d3-geo-polygon/blob/main/src/grayfuller.js
|
|
6905
|
+
*/
|
|
6906
|
+
|
|
6907
|
+
|
|
6908
|
+
var D2R$4 = Math.PI / 180;
|
|
6909
|
+
var R2D$3 = 180 / Math.PI;
|
|
6910
|
+
var SQRT3 = Math.sqrt(3);
|
|
6911
|
+
var GRAY_Z = Math.sqrt(5 + 2 * Math.sqrt(5)) / Math.sqrt(15);
|
|
6912
|
+
var GRAY_EL = Math.sqrt(8) / Math.sqrt(5 + Math.sqrt(5));
|
|
6913
|
+
var GRAY_DVE = Math.sqrt(3 + Math.sqrt(5)) / Math.sqrt(5 + Math.sqrt(5));
|
|
6914
|
+
var ROTATION = [-83.65929, 25.44458, -87.45184];
|
|
6915
|
+
var PARENTS = [
|
|
6916
|
+
-1, 0, 1, 11, 13,
|
|
6917
|
+
6, 7, 1, 7, 8,
|
|
6918
|
+
9, 10, 11, 12, 13,
|
|
6919
|
+
6, 8, 10, 17, 21,
|
|
6920
|
+
16, 15, 19, 19
|
|
6921
|
+
];
|
|
6922
|
+
var engines$1 = {};
|
|
6923
|
+
|
|
6924
|
+
function registerDymaxionProjections(mproj) {
|
|
6925
|
+
// Some unit tests import source modules directly in an ESM context where
|
|
6926
|
+
// optional CommonJS dependencies are intentionally unavailable.
|
|
6927
|
+
if (!mproj) return;
|
|
6928
|
+
register('dymaxion', 'Dymaxion (Gray-Fuller facets)', 'fuller');
|
|
6929
|
+
register('dymaxion2', 'Dymaxion (Gnomonic facets)', 'gnomonic');
|
|
6930
|
+
|
|
6931
|
+
function register(id, name, method) {
|
|
6932
|
+
if (mproj.internal.pj_list[id]) return;
|
|
6933
|
+
mproj.pj_add(function(P) {
|
|
6934
|
+
initDymaxion(P, method);
|
|
6935
|
+
}, id, name);
|
|
6936
|
+
}
|
|
6937
|
+
}
|
|
6938
|
+
|
|
6939
|
+
function getDymaxionEngine(method) {
|
|
6940
|
+
method = method || 'fuller';
|
|
6941
|
+
if (!engines$1[method]) {
|
|
6942
|
+
var data = createAiroceanFaces();
|
|
6943
|
+
engines$1[method] = createPolyhedralProjection({
|
|
6944
|
+
faces: data.faces,
|
|
6945
|
+
faceSites: data.sites,
|
|
6946
|
+
parents: PARENTS,
|
|
6947
|
+
rotation: ROTATION,
|
|
6948
|
+
angle: -60,
|
|
6949
|
+
faceProjector: method == 'gnomonic' ?
|
|
6950
|
+
createGnomonicFaceProjector :
|
|
6951
|
+
createGrayFullerFaceProjector
|
|
6952
|
+
});
|
|
6953
|
+
}
|
|
6954
|
+
return engines$1[method];
|
|
6955
|
+
}
|
|
6956
|
+
|
|
6957
|
+
function initDymaxion(P, method) {
|
|
6958
|
+
var engine = getDymaxionEngine(method);
|
|
6959
|
+
P.es = 0;
|
|
6960
|
+
P.inv = null;
|
|
6961
|
+
P.fwd = function(lp, xy) {
|
|
6962
|
+
var p = engine.forward(lp.lam, lp.phi);
|
|
6963
|
+
if (!p) {
|
|
6964
|
+
xy.x = xy.y = Infinity;
|
|
6965
|
+
} else {
|
|
6966
|
+
xy.x = p[0];
|
|
6967
|
+
xy.y = p[1];
|
|
6968
|
+
}
|
|
6969
|
+
};
|
|
6970
|
+
P.__projection_topology = engine.getTopology(P.lam0 * R2D$3);
|
|
6971
|
+
P.__projected_outline = engine.outline;
|
|
6972
|
+
}
|
|
6973
|
+
|
|
6974
|
+
function createAiroceanFaces() {
|
|
6975
|
+
var theta = Math.atan(0.5) * R2D$3;
|
|
6976
|
+
var vertices = [[0, 90], [0, -90]];
|
|
6977
|
+
var faces;
|
|
6978
|
+
var sites;
|
|
6979
|
+
for (var i = 0; i < 10; i++) {
|
|
6980
|
+
vertices.push([(i * 36 + 180) % 360 - 180, i & 1 ? theta : -theta]);
|
|
6981
|
+
}
|
|
6982
|
+
faces = [
|
|
6983
|
+
[0, 3, 11],
|
|
6984
|
+
[0, 5, 3],
|
|
6985
|
+
[0, 7, 5],
|
|
6986
|
+
[0, 9, 7],
|
|
6987
|
+
[0, 11, 9],
|
|
6988
|
+
[2, 11, 3],
|
|
6989
|
+
[3, 4, 2],
|
|
6990
|
+
[4, 3, 5],
|
|
6991
|
+
[5, 6, 4],
|
|
6992
|
+
[6, 5, 7],
|
|
6993
|
+
[7, 8, 6],
|
|
6994
|
+
[8, 7, 9],
|
|
6995
|
+
[9, 10, 8],
|
|
6996
|
+
[10, 9, 11],
|
|
6997
|
+
[11, 2, 10],
|
|
6998
|
+
[1, 2, 4],
|
|
6999
|
+
[1, 4, 6],
|
|
7000
|
+
[1, 6, 8],
|
|
7001
|
+
[1, 8, 10],
|
|
7002
|
+
[1, 10, 2]
|
|
7003
|
+
].map(function(ids) {
|
|
7004
|
+
return ids.map(function(id) {
|
|
7005
|
+
return vertices[id];
|
|
7006
|
+
});
|
|
7007
|
+
});
|
|
7008
|
+
sites = faces.map(getSphericalCentroid);
|
|
7009
|
+
splitAiroceanFaces(faces, sites);
|
|
7010
|
+
return {faces: faces, sites: sites};
|
|
7011
|
+
}
|
|
7012
|
+
|
|
7013
|
+
function splitAiroceanFaces(faces, sites) {
|
|
7014
|
+
var face = faces[15];
|
|
7015
|
+
var site = sites[15];
|
|
7016
|
+
var original = face.concat();
|
|
7017
|
+
face[0] = site;
|
|
7018
|
+
faces.push([original[0], site, original[2]]);
|
|
7019
|
+
sites.push(site);
|
|
7020
|
+
faces.push([original[0], original[1], site]);
|
|
7021
|
+
sites.push(site);
|
|
7022
|
+
|
|
7023
|
+
face = faces[14];
|
|
7024
|
+
site = sites[14];
|
|
7025
|
+
original = face.concat();
|
|
7026
|
+
var mid = getGreatCircleMidpoint(face[1], face[2]);
|
|
7027
|
+
face[1] = mid;
|
|
7028
|
+
faces.push([original[0], original[1], mid]);
|
|
7029
|
+
sites.push(site);
|
|
7030
|
+
|
|
7031
|
+
face = faces[19];
|
|
7032
|
+
site = sites[19];
|
|
7033
|
+
original = face.concat();
|
|
7034
|
+
face[1] = mid;
|
|
7035
|
+
faces.push([mid, original[0], original[1]]);
|
|
7036
|
+
sites.push(site);
|
|
7037
|
+
}
|
|
7038
|
+
|
|
7039
|
+
function createGrayFullerFaceProjector(face) {
|
|
7040
|
+
var c = face.centroid;
|
|
7041
|
+
var direction = Math.abs(c[1] - 52.62) < 1 ||
|
|
7042
|
+
Math.abs(c[1] + 10.81) < 1 ? 0 : 60;
|
|
7043
|
+
var rotation = [-c[0], -c[1], direction];
|
|
7044
|
+
return function(lam, phi) {
|
|
7045
|
+
var p = rotateSphericalRadians(lam, phi, rotation);
|
|
7046
|
+
var q = grayFullerRaw(p[0], p[1]);
|
|
7047
|
+
// D3's face projections use screen-oriented local y coordinates; retaining
|
|
7048
|
+
// that convention reproduces the published Airocean unfolding matrices.
|
|
7049
|
+
return [q[0], -q[1]];
|
|
7050
|
+
};
|
|
7051
|
+
}
|
|
7052
|
+
|
|
7053
|
+
function createGnomonicFaceProjector(face) {
|
|
7054
|
+
var c = face.centroid;
|
|
7055
|
+
var rotation = [-c[0], -c[1], 0];
|
|
7056
|
+
return function(lam, phi) {
|
|
7057
|
+
var p = rotateSphericalRadians(lam, phi, rotation);
|
|
7058
|
+
var cosPhi = Math.cos(p[1]);
|
|
7059
|
+
var k = cosPhi * Math.cos(p[0]);
|
|
7060
|
+
return [
|
|
7061
|
+
cosPhi * Math.sin(p[0]) / k,
|
|
7062
|
+
-Math.sin(p[1]) / k
|
|
7063
|
+
];
|
|
7064
|
+
};
|
|
7065
|
+
}
|
|
7066
|
+
|
|
7067
|
+
function grayFullerRaw(lam, phi) {
|
|
7068
|
+
var cosPhi = Math.cos(phi);
|
|
7069
|
+
var s = GRAY_Z / (cosPhi * Math.cos(lam));
|
|
7070
|
+
var x = cosPhi * Math.sin(lam) * s;
|
|
7071
|
+
var y = Math.sin(phi) * s;
|
|
7072
|
+
var a1p = Math.atan2(2 * y / SQRT3 + GRAY_EL / 3 - GRAY_EL / 2, GRAY_DVE);
|
|
7073
|
+
var a2p = Math.atan2(x - y / SQRT3 + GRAY_EL / 3 - GRAY_EL / 2, GRAY_DVE);
|
|
7074
|
+
var a3p = Math.atan2(GRAY_EL / 3 - x - y / SQRT3 - GRAY_EL / 2, GRAY_DVE);
|
|
7075
|
+
return [SQRT3 * (a2p - a3p), 2 * a1p - a2p - a3p];
|
|
7076
|
+
}
|
|
7077
|
+
|
|
7078
|
+
function getSphericalCentroid(coords) {
|
|
7079
|
+
var sum = [0, 0, 0];
|
|
7080
|
+
coords.forEach(function(p) {
|
|
7081
|
+
var v = degreesToVector$1(p);
|
|
7082
|
+
sum[0] += v[0];
|
|
7083
|
+
sum[1] += v[1];
|
|
7084
|
+
sum[2] += v[2];
|
|
7085
|
+
});
|
|
7086
|
+
return vectorToDegrees$1(normalize(sum));
|
|
7087
|
+
}
|
|
7088
|
+
|
|
7089
|
+
function getGreatCircleMidpoint(a, b) {
|
|
7090
|
+
var av = degreesToVector$1(a);
|
|
7091
|
+
var bv = degreesToVector$1(b);
|
|
7092
|
+
return vectorToDegrees$1(normalize([
|
|
7093
|
+
av[0] + bv[0],
|
|
7094
|
+
av[1] + bv[1],
|
|
7095
|
+
av[2] + bv[2]
|
|
7096
|
+
]));
|
|
7097
|
+
}
|
|
7098
|
+
|
|
7099
|
+
function degreesToVector$1(p) {
|
|
7100
|
+
var lam = p[0] * D2R$4;
|
|
7101
|
+
var phi = p[1] * D2R$4;
|
|
7102
|
+
var cosPhi = Math.cos(phi);
|
|
7103
|
+
return [Math.cos(lam) * cosPhi, Math.sin(lam) * cosPhi, Math.sin(phi)];
|
|
7104
|
+
}
|
|
7105
|
+
|
|
7106
|
+
function vectorToDegrees$1(p) {
|
|
7107
|
+
return [
|
|
7108
|
+
Math.atan2(p[1], p[0]) * R2D$3,
|
|
7109
|
+
Math.asin(Math.max(-1, Math.min(1, p[2]))) * R2D$3
|
|
7110
|
+
];
|
|
7111
|
+
}
|
|
7112
|
+
|
|
7113
|
+
function normalize(p) {
|
|
7114
|
+
var k = 1 / Math.sqrt(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]);
|
|
7115
|
+
return [p[0] * k, p[1] * k, p[2] * k];
|
|
7116
|
+
}
|
|
7117
|
+
|
|
7118
|
+
var Dymaxion = /*#__PURE__*/Object.freeze({
|
|
7119
|
+
__proto__: null,
|
|
7120
|
+
getDymaxionEngine: getDymaxionEngine,
|
|
7121
|
+
registerDymaxionProjections: registerDymaxionProjections
|
|
7122
|
+
});
|
|
7123
|
+
|
|
7124
|
+
/*
|
|
7125
|
+
* Vertex-oriented great-circle "slice-and-dice" projection.
|
|
7126
|
+
*
|
|
7127
|
+
* Based on van Leeuwen and Strebe (2006), with the vector formulation
|
|
7128
|
+
* described by Hall et al. (2020) and implemented independently by DGGAL.
|
|
7129
|
+
* https://doi.org/10.1559/152304006779500687
|
|
7130
|
+
* https://doi.org/10.3390/ijgi9050315
|
|
7131
|
+
*/
|
|
7132
|
+
|
|
7133
|
+
|
|
7134
|
+
function createGnomonicProjector(center) {
|
|
7135
|
+
var rotation = [-center[0], -center[1], 0];
|
|
7136
|
+
return function(lam, phi) {
|
|
7137
|
+
var p = rotateSphericalRadians(lam, phi, rotation);
|
|
7138
|
+
var cosPhi = Math.cos(p[1]);
|
|
7139
|
+
var k = cosPhi * Math.cos(p[0]);
|
|
7140
|
+
return [
|
|
7141
|
+
cosPhi * Math.sin(p[0]) / k,
|
|
7142
|
+
-Math.sin(p[1]) / k
|
|
7143
|
+
];
|
|
7144
|
+
};
|
|
7145
|
+
}
|
|
7146
|
+
|
|
7147
|
+
/*
|
|
7148
|
+
* Cahill-Keyes 12-zone forward transform.
|
|
7149
|
+
*
|
|
7150
|
+
* Implemented in Perl by Mary Jo Graça (2011), ported to D3.js by
|
|
7151
|
+
* Enrico Spinielli (2013), and adapted here from d3-geo-polygon.
|
|
7152
|
+
*
|
|
7153
|
+
* Copyright 2017-2024 Mike Bostock
|
|
7154
|
+
* ISC License: https://github.com/d3/d3-geo-polygon/blob/main/LICENSE
|
|
7155
|
+
*/
|
|
7156
|
+
|
|
7157
|
+
var D2R$3 = Math.PI / 180;
|
|
7158
|
+
var R2D$2 = 180 / Math.PI;
|
|
7159
|
+
|
|
7160
|
+
function createCahillKeyesRaw(mg) {
|
|
7161
|
+
return createCahillKeyesTransform(mg, false);
|
|
7162
|
+
}
|
|
7163
|
+
|
|
7164
|
+
function createCahillKeyesTransform(mg, faceOnly) {
|
|
7165
|
+
var CK = {lengthMG: mg};
|
|
7166
|
+
init();
|
|
7167
|
+
|
|
7168
|
+
return function(lambda, phi) {
|
|
7169
|
+
if (faceOnly) {
|
|
7170
|
+
var lon = lambda * R2D$2;
|
|
7171
|
+
var side = lon < 0 ? -1 : lon > 0 ? 1 : 0;
|
|
7172
|
+
var local = mp2xy(Math.abs(lon), Math.abs(phi * R2D$2));
|
|
7173
|
+
return [local[0], side * local[1]];
|
|
7174
|
+
}
|
|
7175
|
+
var res = ll2mp(lambda * R2D$2, phi * R2D$2);
|
|
7176
|
+
var xy = mp2xy(res[0], res[1]);
|
|
7177
|
+
var p = [xy[0], res[2] * xy[1]];
|
|
7178
|
+
return mj2g(p, res[3]);
|
|
7179
|
+
};
|
|
7180
|
+
|
|
7181
|
+
function init() {
|
|
7182
|
+
var k = Math.sqrt(3);
|
|
7183
|
+
var pointN, lengthMB, lengthMN, lengthNG, pointU;
|
|
7184
|
+
var p73a, lF, lT, lM, l, pointV;
|
|
7185
|
+
CK.lengthMA = 0.094 * CK.lengthMG;
|
|
7186
|
+
CK.lengthParallel0to73At0 = CK.lengthMG / 100;
|
|
7187
|
+
CK.lengthParallel73to90At0 =
|
|
7188
|
+
(CK.lengthMG - CK.lengthMA - CK.lengthParallel0to73At0 * 73) / 17;
|
|
7189
|
+
CK.sin60 = k / 2;
|
|
7190
|
+
CK.cos60 = 0.5;
|
|
7191
|
+
CK.pointM = [0, 0];
|
|
7192
|
+
CK.pointG = [CK.lengthMG, 0];
|
|
7193
|
+
pointN = [CK.lengthMG, CK.lengthMG * Math.tan(30 * D2R$3)];
|
|
7194
|
+
CK.pointA = [CK.lengthMA, 0];
|
|
7195
|
+
CK.pointB = lineIntersection(CK.pointM, 30, CK.pointA, 45);
|
|
7196
|
+
CK.lengthAG = distance(CK.pointA, CK.pointG);
|
|
7197
|
+
CK.lengthAB = distance(CK.pointA, CK.pointB);
|
|
7198
|
+
lengthMB = distance(CK.pointM, CK.pointB);
|
|
7199
|
+
lengthMN = distance(CK.pointM, pointN);
|
|
7200
|
+
lengthNG = distance(pointN, CK.pointG);
|
|
7201
|
+
CK.pointD = interpolate(lengthMB, lengthMN, pointN, CK.pointM);
|
|
7202
|
+
CK.pointF = [CK.lengthMG, lengthNG - lengthMB];
|
|
7203
|
+
CK.pointE = [
|
|
7204
|
+
pointN[0] - CK.lengthMA * Math.sin(30 * D2R$3),
|
|
7205
|
+
pointN[1] - CK.lengthMA * Math.cos(30 * D2R$3)
|
|
7206
|
+
];
|
|
7207
|
+
CK.lengthGF = distance(CK.pointG, CK.pointF);
|
|
7208
|
+
CK.lengthBD = distance(CK.pointB, CK.pointD);
|
|
7209
|
+
CK.lengthGFE = CK.lengthGF + CK.lengthAB;
|
|
7210
|
+
CK.deltaMEq = CK.lengthGFE / 45;
|
|
7211
|
+
CK.lengthAP75 = 15 * CK.lengthParallel73to90At0;
|
|
7212
|
+
CK.lengthAP73 = CK.lengthMG - CK.lengthMA -
|
|
7213
|
+
CK.lengthParallel0to73At0 * 73;
|
|
7214
|
+
pointU = [
|
|
7215
|
+
CK.pointA[0] + CK.lengthAP73 * Math.cos(30 * D2R$3),
|
|
7216
|
+
CK.pointA[1] + CK.lengthAP73 * Math.sin(30 * D2R$3)
|
|
7217
|
+
];
|
|
7218
|
+
CK.pointT = lineIntersection(pointU, -60, CK.pointB, 30);
|
|
7219
|
+
|
|
7220
|
+
p73a = parallel73(29);
|
|
7221
|
+
lF = p73a.lengthParallel73;
|
|
7222
|
+
lT = lengthTorridSegment(29);
|
|
7223
|
+
lM = lengthMiddleSegment(29);
|
|
7224
|
+
l = 15 * (lT + lM + lF) / 73 - lT;
|
|
7225
|
+
pointV = interpolate(l, lM, jointT(29), jointF(29));
|
|
7226
|
+
CK.pointC = [0, 0];
|
|
7227
|
+
CK.pointC[1] = (
|
|
7228
|
+
pointV[0] * pointV[0] + pointV[1] * pointV[1] -
|
|
7229
|
+
CK.pointD[0] * CK.pointD[0] - CK.pointD[1] * CK.pointD[1]
|
|
7230
|
+
) / (
|
|
7231
|
+
2 * (k * pointV[0] + pointV[1] -
|
|
7232
|
+
k * CK.pointD[0] - CK.pointD[1])
|
|
7233
|
+
);
|
|
7234
|
+
CK.pointC[0] = k * CK.pointC[1];
|
|
7235
|
+
CK.radius = distance(CK.pointC, CK.pointD);
|
|
7236
|
+
}
|
|
7237
|
+
|
|
7238
|
+
function ll2mp(lon, lat) {
|
|
7239
|
+
var south = [0, 6, 7, 8, 5];
|
|
7240
|
+
var octant = truncate((lon + 180) / 90 + 1);
|
|
7241
|
+
var meridian = (lon + 720) % 90 - 45;
|
|
7242
|
+
var side = meridian < 0 ? -1 : meridian > 0 ? 1 : 0;
|
|
7243
|
+
meridian = Math.abs(meridian);
|
|
7244
|
+
if (octant === 5) octant = 1;
|
|
7245
|
+
if (lat < 0) octant = south[octant];
|
|
7246
|
+
return [meridian, Math.abs(lat), side, octant];
|
|
7247
|
+
}
|
|
7248
|
+
|
|
7249
|
+
function mp2xy(m, p) {
|
|
7250
|
+
if (m === 0) return p >= 75 ? zoneA(m, p) : zoneB(m, p);
|
|
7251
|
+
if (p >= 75) return zoneC(m, p);
|
|
7252
|
+
if (p === 0) return zoneD(m);
|
|
7253
|
+
if (p >= 73 && m <= 30) return zoneE(m, p);
|
|
7254
|
+
if (m === 45) {
|
|
7255
|
+
return p <= 15 ? zoneF(m, p) :
|
|
7256
|
+
p <= 73 ? zoneG(m, p) : zoneH(m, p);
|
|
7257
|
+
}
|
|
7258
|
+
if (m <= 29) return zoneI(m, p);
|
|
7259
|
+
if (p >= 73) return zoneJ(m, p);
|
|
7260
|
+
var lT = lengthTorridSegment(m);
|
|
7261
|
+
var hit = circleLineIntersection(
|
|
7262
|
+
CK.pointC, CK.radius, jointT(m), jointF(m)
|
|
7263
|
+
);
|
|
7264
|
+
var l15;
|
|
7265
|
+
if (hit[0]) {
|
|
7266
|
+
l15 = lT + distance(jointT(m), hit[1]);
|
|
7267
|
+
} else {
|
|
7268
|
+
hit = circleLineIntersection(
|
|
7269
|
+
CK.pointC, CK.radius, jointE(m), jointT(m)
|
|
7270
|
+
);
|
|
7271
|
+
l15 = lT - distance(jointT(m), hit[1]);
|
|
7272
|
+
}
|
|
7273
|
+
return p <= 15 ? zoneK(m, p, l15) : zoneL(m, p, l15);
|
|
7274
|
+
}
|
|
7275
|
+
|
|
7276
|
+
function zoneA(m, p) {
|
|
7277
|
+
return [CK.pointA[0] + (90 - p) * 104, 0];
|
|
7278
|
+
}
|
|
7279
|
+
|
|
7280
|
+
function zoneB(m, p) {
|
|
7281
|
+
return [CK.pointG[0] - p * 100, 0];
|
|
7282
|
+
}
|
|
7283
|
+
|
|
7284
|
+
function zoneC(m, p) {
|
|
7285
|
+
return radialPoint(CK.pointA, 104 * (90 - p), m);
|
|
7286
|
+
}
|
|
7287
|
+
|
|
7288
|
+
function zoneD(m) {
|
|
7289
|
+
return equator(m);
|
|
7290
|
+
}
|
|
7291
|
+
|
|
7292
|
+
function zoneE(m, p) {
|
|
7293
|
+
return radialPoint(CK.pointA, 1560 + (75 - p) * 100, m);
|
|
7294
|
+
}
|
|
7295
|
+
|
|
7296
|
+
function zoneF(m, p) {
|
|
7297
|
+
return interpolate(p, 15, CK.pointE, CK.pointD);
|
|
7298
|
+
}
|
|
7299
|
+
|
|
7300
|
+
function zoneG(m, p) {
|
|
7301
|
+
return interpolate(p - 15, 58, CK.pointD, CK.pointT);
|
|
7302
|
+
}
|
|
7303
|
+
|
|
7304
|
+
function zoneH(m, p) {
|
|
7305
|
+
var p75 = parallel75(45);
|
|
7306
|
+
var p73 = parallel73(m).parallel73;
|
|
7307
|
+
var lF = distance(CK.pointT, CK.pointB);
|
|
7308
|
+
var lF75 = distance(CK.pointB, p75);
|
|
7309
|
+
var l = (75 - p) * (lF75 + lF) / 2;
|
|
7310
|
+
return l <= lF75 ?
|
|
7311
|
+
interpolate(l, lF75, p75, CK.pointB) :
|
|
7312
|
+
interpolate(l - lF75, lF, CK.pointB, p73);
|
|
7313
|
+
}
|
|
7314
|
+
|
|
7315
|
+
function zoneI(m, p) {
|
|
7316
|
+
var p73a = parallel73(m);
|
|
7317
|
+
var lT = lengthTorridSegment(m);
|
|
7318
|
+
var lM = lengthMiddleSegment(m);
|
|
7319
|
+
var l = p * (lT + lM + p73a.lengthParallel73) / 73;
|
|
7320
|
+
if (l <= lT) return interpolate(l, lT, jointE(m), jointT(m));
|
|
7321
|
+
if (l <= lT + lM) {
|
|
7322
|
+
return interpolate(l - lT, lM, jointT(m), jointF(m));
|
|
7323
|
+
}
|
|
7324
|
+
return interpolate(
|
|
7325
|
+
l - lT - lM,
|
|
7326
|
+
p73a.lengthParallel73,
|
|
7327
|
+
jointF(m),
|
|
7328
|
+
p73a.parallel73
|
|
7329
|
+
);
|
|
7330
|
+
}
|
|
7331
|
+
|
|
7332
|
+
function zoneJ(m, p) {
|
|
7333
|
+
var p75 = parallel75(m);
|
|
7334
|
+
var p73a = parallel73(m);
|
|
7335
|
+
var lF75 = distance(jointF(m), p75);
|
|
7336
|
+
var l = (75 - p) * (lF75 - p73a.lengthParallel73) / 2;
|
|
7337
|
+
return l <= lF75 ?
|
|
7338
|
+
interpolate(l, lF75, p75, jointF(m)) :
|
|
7339
|
+
interpolate(
|
|
7340
|
+
l - lF75,
|
|
7341
|
+
-p73a.lengthParallel73,
|
|
7342
|
+
jointF(m),
|
|
7343
|
+
p73a.parallel73
|
|
7344
|
+
);
|
|
7345
|
+
}
|
|
7346
|
+
|
|
7347
|
+
function zoneK(m, p, l15) {
|
|
7348
|
+
var l = p * l15 / 15;
|
|
7349
|
+
var lT = lengthTorridSegment(m);
|
|
7350
|
+
var lM = lengthMiddleSegment(m);
|
|
7351
|
+
return l <= lT ?
|
|
7352
|
+
interpolate(l, lT, jointE(m), jointT(m)) :
|
|
7353
|
+
interpolate(l - lT, lM, jointT(m), jointF(m));
|
|
7354
|
+
}
|
|
7355
|
+
|
|
7356
|
+
function zoneL(m, p, l15) {
|
|
7357
|
+
var p73a = parallel73(m);
|
|
7358
|
+
var lT = lengthTorridSegment(m);
|
|
7359
|
+
var lM = lengthMiddleSegment(m);
|
|
7360
|
+
var lF = p73a.lengthParallel73;
|
|
7361
|
+
var l = l15 + (p - 15) * (lT + lM + lF - l15) / 58;
|
|
7362
|
+
if (l <= lT) return interpolate(l, lT, jointE(m), jointF(m));
|
|
7363
|
+
if (l <= lT + lM) {
|
|
7364
|
+
return interpolate(l - lT, lM, jointT(m), jointF(m));
|
|
7365
|
+
}
|
|
7366
|
+
return interpolate(
|
|
7367
|
+
l - lT - lM, lF, jointF(m), p73a.parallel73
|
|
7368
|
+
);
|
|
7369
|
+
}
|
|
7370
|
+
|
|
7371
|
+
function equator(m) {
|
|
7372
|
+
var l = CK.deltaMEq * m;
|
|
7373
|
+
return l <= CK.lengthGF ?
|
|
7374
|
+
[CK.pointG[0], l] :
|
|
7375
|
+
interpolate(l - CK.lengthGF, CK.lengthAB, CK.pointF, CK.pointE);
|
|
7376
|
+
}
|
|
7377
|
+
|
|
7378
|
+
function jointE(m) {
|
|
7379
|
+
return equator(m);
|
|
7380
|
+
}
|
|
7381
|
+
|
|
7382
|
+
function jointT(m) {
|
|
7383
|
+
return lineIntersection(CK.pointM, 2 * m / 3, jointE(m), m / 3);
|
|
7384
|
+
}
|
|
7385
|
+
|
|
7386
|
+
function jointF(m) {
|
|
7387
|
+
if (m === 0) return [CK.pointA[0] + CK.lengthAB, 0];
|
|
7388
|
+
return lineIntersection(CK.pointA, m, CK.pointM, 2 * m / 3);
|
|
7389
|
+
}
|
|
7390
|
+
|
|
7391
|
+
function lengthTorridSegment(m) {
|
|
7392
|
+
return distance(jointE(m), jointT(m));
|
|
7393
|
+
}
|
|
7394
|
+
|
|
7395
|
+
function lengthMiddleSegment(m) {
|
|
7396
|
+
return distance(jointT(m), jointF(m));
|
|
7397
|
+
}
|
|
7398
|
+
|
|
7399
|
+
function parallel73(m) {
|
|
7400
|
+
var p73, lF, xy;
|
|
7401
|
+
var jF = jointF(m);
|
|
7402
|
+
if (m <= 30) {
|
|
7403
|
+
p73 = radialPoint(CK.pointA, CK.lengthAP73, m);
|
|
7404
|
+
lF = distance(jF, p73);
|
|
7405
|
+
} else {
|
|
7406
|
+
p73 = lineIntersection(CK.pointT, -60, jF, m);
|
|
7407
|
+
lF = distance(jF, p73);
|
|
7408
|
+
if (m > 44) {
|
|
7409
|
+
xy = lineIntersection(CK.pointT, -60, jF, 2 * m / 3);
|
|
7410
|
+
if (xy[0] > p73[0]) {
|
|
7411
|
+
p73 = xy;
|
|
7412
|
+
lF = -distance(jF, p73);
|
|
7413
|
+
}
|
|
7414
|
+
}
|
|
7415
|
+
}
|
|
7416
|
+
return {parallel73: p73, lengthParallel73: lF};
|
|
7417
|
+
}
|
|
7418
|
+
|
|
7419
|
+
function parallel75(m) {
|
|
7420
|
+
return radialPoint(CK.pointA, CK.lengthAP75, m);
|
|
7421
|
+
}
|
|
7422
|
+
|
|
7423
|
+
function mj2g(xy, octant) {
|
|
7424
|
+
var out;
|
|
7425
|
+
if (octant === 0) {
|
|
7426
|
+
out = rotate(xy, -60);
|
|
7427
|
+
} else if (octant === 1) {
|
|
7428
|
+
out = rotate(xy, -120);
|
|
7429
|
+
out[0] -= CK.lengthMG;
|
|
7430
|
+
} else if (octant === 2) {
|
|
7431
|
+
out = rotate(xy, -60);
|
|
7432
|
+
out[0] -= CK.lengthMG;
|
|
7433
|
+
} else if (octant === 3) {
|
|
7434
|
+
out = rotate(xy, -120);
|
|
7435
|
+
out[0] += CK.lengthMG;
|
|
7436
|
+
} else if (octant === 4) {
|
|
7437
|
+
out = rotate(xy, -60);
|
|
7438
|
+
out[0] += CK.lengthMG;
|
|
7439
|
+
} else if (octant === 5) {
|
|
7440
|
+
out = rotate([2 * CK.lengthMG - xy[0], xy[1]], -60);
|
|
7441
|
+
out[0] += CK.lengthMG;
|
|
7442
|
+
} else if (octant === 6) {
|
|
7443
|
+
out = rotate([2 * CK.lengthMG - xy[0], xy[1]], -120);
|
|
7444
|
+
out[0] -= CK.lengthMG;
|
|
7445
|
+
} else if (octant === 7) {
|
|
7446
|
+
out = rotate([2 * CK.lengthMG - xy[0], xy[1]], -60);
|
|
7447
|
+
out[0] -= CK.lengthMG;
|
|
7448
|
+
} else if (octant === 8) {
|
|
7449
|
+
out = rotate([2 * CK.lengthMG - xy[0], xy[1]], -120);
|
|
7450
|
+
out[0] += CK.lengthMG;
|
|
7451
|
+
}
|
|
7452
|
+
return out;
|
|
7453
|
+
}
|
|
7454
|
+
|
|
7455
|
+
function radialPoint(origin, length, angle) {
|
|
7456
|
+
return [
|
|
7457
|
+
origin[0] + length * Math.cos(angle * D2R$3),
|
|
7458
|
+
origin[1] + length * Math.sin(angle * D2R$3)
|
|
7459
|
+
];
|
|
7460
|
+
}
|
|
7461
|
+
|
|
7462
|
+
function rotate(xy, angle) {
|
|
7463
|
+
if (angle === -60) {
|
|
7464
|
+
return [
|
|
7465
|
+
xy[0] * CK.cos60 + xy[1] * CK.sin60,
|
|
7466
|
+
-xy[0] * CK.sin60 + xy[1] * CK.cos60
|
|
7467
|
+
];
|
|
7468
|
+
}
|
|
7469
|
+
if (angle === -120) {
|
|
7470
|
+
return [
|
|
7471
|
+
-xy[0] * CK.cos60 + xy[1] * CK.sin60,
|
|
7472
|
+
-xy[0] * CK.sin60 - xy[1] * CK.cos60
|
|
7473
|
+
];
|
|
7474
|
+
}
|
|
7475
|
+
var cos = Math.cos(angle * D2R$3);
|
|
7476
|
+
var sin = Math.sin(angle * D2R$3);
|
|
7477
|
+
return [xy[0] * cos - xy[1] * sin, xy[0] * sin + xy[1] * cos];
|
|
7478
|
+
}
|
|
7479
|
+
}
|
|
7480
|
+
|
|
7481
|
+
function createCahillKeyesFaceRaw(mg) {
|
|
7482
|
+
return createCahillKeyesTransform(mg, true);
|
|
7483
|
+
}
|
|
7484
|
+
|
|
7485
|
+
function distance(a, b) {
|
|
7486
|
+
return Math.hypot(a[0] - b[0], a[1] - b[1]);
|
|
7487
|
+
}
|
|
7488
|
+
|
|
7489
|
+
function interpolate(length, total, start, end) {
|
|
7490
|
+
return [
|
|
7491
|
+
start[0] + (end[0] - start[0]) * length / total,
|
|
7492
|
+
start[1] + (end[1] - start[1]) * length / total
|
|
7493
|
+
];
|
|
7494
|
+
}
|
|
7495
|
+
|
|
7496
|
+
function lineIntersection(p1, slope1, p2, slope2) {
|
|
7497
|
+
var m1 = Math.tan(slope1 * D2R$3);
|
|
7498
|
+
var m2 = Math.tan(slope2 * D2R$3);
|
|
7499
|
+
var x = (m1 * p1[0] - m2 * p2[0] - p1[1] + p2[1]) / (m1 - m2);
|
|
7500
|
+
return [x, m1 * (x - p1[0]) + p1[1]];
|
|
7501
|
+
}
|
|
7502
|
+
|
|
7503
|
+
function circleLineIntersection(center, radius, p1, p2) {
|
|
7504
|
+
var dx = p2[0] - p1[0];
|
|
7505
|
+
var dy = p2[1] - p1[1];
|
|
7506
|
+
var fx = p1[0] - center[0];
|
|
7507
|
+
var fy = p1[1] - center[1];
|
|
7508
|
+
var a = dx * dx + dy * dy;
|
|
7509
|
+
var b = 2 * (dx * fx + dy * fy);
|
|
7510
|
+
var c = fx * fx + fy * fy - radius * radius;
|
|
7511
|
+
var d = b * b - 4 * a * c;
|
|
7512
|
+
if (a === 0 || d < 0) return [0, [0, 0]];
|
|
7513
|
+
var root = Math.sqrt(d);
|
|
7514
|
+
var u1 = (-b + root) / (2 * a);
|
|
7515
|
+
var u2 = (-b - root) / (2 * a);
|
|
7516
|
+
var u = u1 >= 0 && u1 <= 1 ? u1 :
|
|
7517
|
+
u2 >= 0 && u2 <= 1 ? u2 : null;
|
|
7518
|
+
return u === null ? [0, [0, 0]] :
|
|
7519
|
+
[1, [p1[0] + u * dx, p1[1] + u * dy]];
|
|
7520
|
+
}
|
|
7521
|
+
|
|
7522
|
+
function truncate(n) {
|
|
7523
|
+
return n > 0 ? Math.floor(n) : Math.ceil(n);
|
|
7524
|
+
}
|
|
7525
|
+
|
|
7526
|
+
var D2R$2 = Math.PI / 180;
|
|
7527
|
+
var EPS = 1e-12;
|
|
7528
|
+
|
|
7529
|
+
// Create a smooth facet projection from any radial azimuthal projection.
|
|
7530
|
+
// The azimuthal coordinates are warped back to the gnomonic polygon along
|
|
7531
|
+
// the boundary, so adjacent facets retain identical straight edges.
|
|
7532
|
+
function createRadialFacetProjector(coords, options) {
|
|
7533
|
+
options = options || {};
|
|
7534
|
+
var center = options.planarCenter || getSphericalCenter(coords);
|
|
7535
|
+
var gnomonic = createGnomonicProjector(center);
|
|
7536
|
+
var planar = coords.map(function(p) {
|
|
7537
|
+
return gnomonic(p[0] * D2R$2, p[1] * D2R$2);
|
|
7538
|
+
});
|
|
7539
|
+
var edges = createNormalizedEdges(planar);
|
|
7540
|
+
var radial = createRadialFunction(
|
|
7541
|
+
options.radial || 'equal-area',
|
|
7542
|
+
options.radial2,
|
|
7543
|
+
options.radialBlend
|
|
7544
|
+
);
|
|
7545
|
+
var boundaryStrength = options.boundaryStrength || 1;
|
|
7546
|
+
var radialScale = getRadialScale(planar, radial) *
|
|
7547
|
+
(options.radialScale || 1);
|
|
7548
|
+
|
|
7549
|
+
project.radial = options.radial || 'equal-area';
|
|
7550
|
+
project.boundaryStrength = boundaryStrength;
|
|
7551
|
+
return project;
|
|
7552
|
+
|
|
7553
|
+
function project(lam, phi) {
|
|
7554
|
+
var p = gnomonic(lam, phi);
|
|
7555
|
+
var r = Math.hypot(p[0], p[1]);
|
|
7556
|
+
if (r < EPS) return [0, 0];
|
|
7557
|
+
var c = Math.atan(r);
|
|
7558
|
+
var azimuthalRadius = radial(c) * radialScale;
|
|
7559
|
+
var boundaryWeight = getBoundaryWeight(p, edges, boundaryStrength);
|
|
7560
|
+
var scale = 1 + boundaryWeight * (azimuthalRadius / r - 1);
|
|
7561
|
+
return [p[0] * scale, p[1] * scale];
|
|
7562
|
+
}
|
|
7563
|
+
}
|
|
7564
|
+
|
|
7565
|
+
function createRadialFunction(radial1, radial2, blend) {
|
|
7566
|
+
var f1 = getRadialFunction(radial1);
|
|
7567
|
+
if (!radial2) return f1;
|
|
7568
|
+
var f2 = getRadialFunction(radial2);
|
|
7569
|
+
var k = Math.max(0, Math.min(1, blend == null ? 0.5 : blend));
|
|
7570
|
+
return function(c) {
|
|
7571
|
+
return f1(c) + (f2(c) - f1(c)) * k;
|
|
7572
|
+
};
|
|
7573
|
+
}
|
|
7574
|
+
|
|
7575
|
+
function getRadialFunction(id) {
|
|
7576
|
+
if (typeof id == 'function') return id;
|
|
7577
|
+
id = normalizeRadialProjectionName(id);
|
|
7578
|
+
if (id == 'aeqd') return function(c) {
|
|
7579
|
+
return c;
|
|
7580
|
+
};
|
|
7581
|
+
if (id == 'stere') return function(c) {
|
|
7582
|
+
return 2 * Math.tan(c / 2);
|
|
7583
|
+
};
|
|
7584
|
+
if (id == 'ortho') return function(c) {
|
|
7585
|
+
return Math.sin(c);
|
|
7586
|
+
};
|
|
7587
|
+
if (id == 'gnom') return function(c) {
|
|
7588
|
+
return Math.tan(c);
|
|
7589
|
+
};
|
|
7590
|
+
// Lambert azimuthal equal-area
|
|
7591
|
+
return function(c) {
|
|
7592
|
+
return 2 * Math.sin(c / 2);
|
|
7593
|
+
};
|
|
7594
|
+
}
|
|
7595
|
+
|
|
7596
|
+
function normalizeRadialProjectionName(id) {
|
|
7597
|
+
id = String(id || '').toLowerCase().replace(/_/g, '-');
|
|
7598
|
+
if (id == 'laea' || id == 'lambert' || id == 'equal-area') return 'laea';
|
|
7599
|
+
if (id == 'aeqd' || id == 'equidistant') return 'aeqd';
|
|
7600
|
+
if (id == 'stere' || id == 'stereographic') return 'stere';
|
|
7601
|
+
if (id == 'ortho' || id == 'orthographic') return 'ortho';
|
|
7602
|
+
if (id == 'gnom' || id == 'gnomonic') return 'gnom';
|
|
7603
|
+
throw new Error('Unsupported facet projection: ' + id);
|
|
7604
|
+
}
|
|
7605
|
+
|
|
7606
|
+
function getRadialScale(planar, radial) {
|
|
7607
|
+
var sum = 0;
|
|
7608
|
+
planar.forEach(function(p) {
|
|
7609
|
+
var r = Math.hypot(p[0], p[1]);
|
|
7610
|
+
sum += r / radial(Math.atan(r));
|
|
7611
|
+
});
|
|
7612
|
+
return sum / planar.length;
|
|
7613
|
+
}
|
|
7614
|
+
|
|
7615
|
+
function createNormalizedEdges(planar) {
|
|
7616
|
+
var center = [0, 0];
|
|
7617
|
+
return planar.map(function(a, i) {
|
|
7618
|
+
var b = planar[(i + 1) % planar.length];
|
|
7619
|
+
var dx = b[0] - a[0];
|
|
7620
|
+
var dy = b[1] - a[1];
|
|
7621
|
+
var centerCross = dx * (center[1] - a[1]) -
|
|
7622
|
+
dy * (center[0] - a[0]);
|
|
7623
|
+
return {
|
|
7624
|
+
a: a,
|
|
7625
|
+
dx: dx,
|
|
7626
|
+
dy: dy,
|
|
7627
|
+
denominator: centerCross
|
|
7628
|
+
};
|
|
7629
|
+
});
|
|
7630
|
+
}
|
|
7631
|
+
|
|
7632
|
+
function getBoundaryWeight(p, edges, strength) {
|
|
7633
|
+
var product = 1;
|
|
7634
|
+
for (var i = 0; i < edges.length; i++) {
|
|
7635
|
+
var edge = edges[i];
|
|
7636
|
+
var cross = edge.dx * (p[1] - edge.a[1]) -
|
|
7637
|
+
edge.dy * (p[0] - edge.a[0]);
|
|
7638
|
+
product *= Math.max(0, cross / edge.denominator);
|
|
7639
|
+
}
|
|
7640
|
+
product = Math.max(0, Math.min(1, product));
|
|
7641
|
+
return 1 - Math.pow(1 - product, strength);
|
|
7642
|
+
}
|
|
7643
|
+
|
|
7644
|
+
function getSphericalCenter(coords) {
|
|
7645
|
+
var sum = coords.reduce(function(memo, p) {
|
|
7646
|
+
var lam = p[0] * D2R$2;
|
|
7647
|
+
var phi = p[1] * D2R$2;
|
|
7648
|
+
var cosPhi = Math.cos(phi);
|
|
7649
|
+
memo[0] += Math.cos(lam) * cosPhi;
|
|
7650
|
+
memo[1] += Math.sin(lam) * cosPhi;
|
|
7651
|
+
memo[2] += Math.sin(phi);
|
|
7652
|
+
return memo;
|
|
7653
|
+
}, [0, 0, 0]);
|
|
7654
|
+
return [
|
|
7655
|
+
Math.atan2(sum[1], sum[0]) / D2R$2,
|
|
7656
|
+
Math.atan2(sum[2], Math.hypot(sum[0], sum[1])) / D2R$2
|
|
7657
|
+
];
|
|
7658
|
+
}
|
|
7659
|
+
|
|
7660
|
+
/*
|
|
7661
|
+
* Octahedral butterfly projections.
|
|
7662
|
+
*
|
|
7663
|
+
* Face layouts are adapted from d3-geo-polygon (ISC license):
|
|
7664
|
+
* https://github.com/d3/d3-geo-polygon
|
|
7665
|
+
*
|
|
7666
|
+
* Copyright 2017-2024 Mike Bostock
|
|
7667
|
+
* ISC License: https://github.com/d3/d3-geo-polygon/blob/main/LICENSE
|
|
7668
|
+
*/
|
|
7669
|
+
|
|
7670
|
+
|
|
7671
|
+
var D2R$1 = Math.PI / 180;
|
|
7672
|
+
var R2D$1 = 180 / Math.PI;
|
|
7673
|
+
var RADIAL_BOUNDARY_STRENGTH = 1;
|
|
7674
|
+
var OCTAHEDRON = createOctahedron();
|
|
7675
|
+
var BUTTERFLY_PARENTS = [-1, 0, 0, 1, 0, 1, 4, 5];
|
|
7676
|
+
var DEFAULT_LON0 = {
|
|
7677
|
+
butterfly: 157.5,
|
|
7678
|
+
butterfly2: -20,
|
|
7679
|
+
cahill_keyes: -20
|
|
7680
|
+
};
|
|
7681
|
+
var engines = {};
|
|
7682
|
+
|
|
7683
|
+
function registerButterflyProjections(mproj) {
|
|
7684
|
+
if (!mproj) return;
|
|
7685
|
+
register('butterfly', 'Butterfly projection (Pacific aspect)', 'butterfly');
|
|
7686
|
+
register('butterfly2', 'Butterfly projection (Atlantic aspect)', 'butterfly2');
|
|
7687
|
+
register('cahill_keyes', 'Cahill-Keyes butterfly', 'cahill_keyes');
|
|
7688
|
+
|
|
7689
|
+
function register(id, name, method) {
|
|
7690
|
+
if (mproj.internal.pj_list[id]) return;
|
|
7691
|
+
mproj.pj_add(function(P) {
|
|
7692
|
+
initButterfly(P, method);
|
|
7693
|
+
}, id, name);
|
|
7694
|
+
}
|
|
7695
|
+
}
|
|
7696
|
+
|
|
7697
|
+
function getButterflyEngine(method) {
|
|
7698
|
+
var key = method == 'butterfly2' ? 'butterfly' : method;
|
|
7699
|
+
if (!engines[key]) {
|
|
7700
|
+
if (key == 'cahill_keyes') {
|
|
7701
|
+
engines[key] = createCahillKeyesEngine();
|
|
7702
|
+
} else if (key == 'butterfly') {
|
|
7703
|
+
engines[key] = createButterflyEngine();
|
|
7704
|
+
} else {
|
|
7705
|
+
throw new Error('Unknown butterfly projection: ' + method);
|
|
7706
|
+
}
|
|
7707
|
+
}
|
|
7708
|
+
return engines[key];
|
|
7709
|
+
}
|
|
7710
|
+
|
|
7711
|
+
function initButterfly(P, method) {
|
|
7712
|
+
var engine = getButterflyEngine(method);
|
|
7713
|
+
if (!P.params.lon_0) {
|
|
7714
|
+
P.lam0 = DEFAULT_LON0[method] * D2R$1;
|
|
7715
|
+
}
|
|
7716
|
+
P.es = 0;
|
|
7717
|
+
P.inv = null;
|
|
7718
|
+
P.fwd = function(lp, xy) {
|
|
7719
|
+
var p = engine.forward(lp.lam, lp.phi);
|
|
7720
|
+
if (!p) {
|
|
7721
|
+
xy.x = xy.y = Infinity;
|
|
7722
|
+
} else {
|
|
7723
|
+
xy.x = p[0];
|
|
7724
|
+
xy.y = p[1];
|
|
7725
|
+
}
|
|
7726
|
+
};
|
|
7727
|
+
P.__projection_topology = engine.getTopology(P.lam0 * R2D$1);
|
|
7728
|
+
if (engine.removeOutlineExtremeConnectors) {
|
|
7729
|
+
P.__remove_outline_extreme_connectors = true;
|
|
7730
|
+
}
|
|
7731
|
+
if (engine.useProjectedOutline !== false) {
|
|
7732
|
+
P.__projected_outline = engine.outline;
|
|
7733
|
+
}
|
|
7734
|
+
}
|
|
7735
|
+
|
|
7736
|
+
function createButterflyEngine() {
|
|
7737
|
+
var faceCenters = [-45, 45, -45, 45, -135, 135, -135, 135];
|
|
7738
|
+
// Reuse the Cahill-Keyes polar incisions and 12-zone octant transform,
|
|
7739
|
+
// then unfold the faces with the butterfly attachment tree.
|
|
7740
|
+
var data = createTruncatedOctahedronFaces(
|
|
7741
|
+
Math.cos(17 * D2R$1),
|
|
7742
|
+
Math.sin(17 * D2R$1),
|
|
7743
|
+
BUTTERFLY_PARENTS
|
|
7744
|
+
);
|
|
7745
|
+
var faceRaw = createCahillKeyesFaceRaw(10000);
|
|
7746
|
+
var engine = createPolyhedralProjection({
|
|
7747
|
+
faces: data.faces,
|
|
7748
|
+
parents: data.parents,
|
|
7749
|
+
rotation: [0, 0, 0],
|
|
7750
|
+
// Face-local CK coordinates point opposite the root face.
|
|
7751
|
+
angle: 150,
|
|
7752
|
+
findFace: createTruncatedFaceFinder(data.cornerNormals),
|
|
7753
|
+
faceProjector: function(face) {
|
|
7754
|
+
var baseId = face.id < 8 ? face.id : data.parents[face.id];
|
|
7755
|
+
var center = faceCenters[baseId] * D2R$1;
|
|
7756
|
+
var raw = function(lam, phi) {
|
|
7757
|
+
return faceRaw(normalizeRadians(lam - center), phi);
|
|
7758
|
+
};
|
|
7759
|
+
return createNormalizedFacetProjector(face.coords, raw);
|
|
7760
|
+
}
|
|
7761
|
+
});
|
|
7762
|
+
engine.facetProjection = 'cahill_keyes';
|
|
7763
|
+
return engine;
|
|
7764
|
+
}
|
|
7765
|
+
|
|
7766
|
+
function createNormalizedFacetProjector(coords, raw) {
|
|
7767
|
+
// Remove the global placement and scale of a raw facet while preserving its
|
|
7768
|
+
// internal shape. The polyhedral engine then controls how facets unfold.
|
|
7769
|
+
var points = coords.map(function(p) {
|
|
7770
|
+
return raw(p[0] * D2R$1, p[1] * D2R$1);
|
|
7771
|
+
});
|
|
7772
|
+
var edge = 0;
|
|
7773
|
+
var maxLengthSq = -1;
|
|
7774
|
+
for (var i = 0; i < points.length; i++) {
|
|
7775
|
+
var a = points[i];
|
|
7776
|
+
var b = points[(i + 1) % points.length];
|
|
7777
|
+
var dx = b[0] - a[0];
|
|
7778
|
+
var dy = b[1] - a[1];
|
|
7779
|
+
var lengthSq = dx * dx + dy * dy;
|
|
7780
|
+
if (lengthSq > maxLengthSq) {
|
|
7781
|
+
maxLengthSq = lengthSq;
|
|
7782
|
+
edge = i;
|
|
7783
|
+
}
|
|
7784
|
+
}
|
|
7785
|
+
var origin = points[edge];
|
|
7786
|
+
var end = points[(edge + 1) % points.length];
|
|
7787
|
+
var ux = end[0] - origin[0];
|
|
7788
|
+
var uy = end[1] - origin[1];
|
|
7789
|
+
var center = points.reduce(function(sum, p) {
|
|
7790
|
+
sum[0] += p[0] / points.length;
|
|
7791
|
+
sum[1] += p[1] / points.length;
|
|
7792
|
+
return sum;
|
|
7793
|
+
}, [0, 0]);
|
|
7794
|
+
var side = ux * (center[1] - origin[1]) -
|
|
7795
|
+
uy * (center[0] - origin[0]) < 0 ? -1 : 1;
|
|
7796
|
+
return function(lam, phi) {
|
|
7797
|
+
var p = raw(lam, phi);
|
|
7798
|
+
var x = p[0] - origin[0];
|
|
7799
|
+
var y = p[1] - origin[1];
|
|
7800
|
+
return [
|
|
7801
|
+
(x * ux + y * uy) / maxLengthSq,
|
|
7802
|
+
side * (ux * y - uy * x) / maxLengthSq
|
|
7803
|
+
];
|
|
7804
|
+
};
|
|
7805
|
+
}
|
|
7806
|
+
|
|
7807
|
+
function createRadialButterflyEngine(options) {
|
|
7808
|
+
options = normalizeRadialFacetOptions(options);
|
|
7809
|
+
var data = createRadialButterflyFaces();
|
|
7810
|
+
var radialOptions = {
|
|
7811
|
+
radial: options.projection,
|
|
7812
|
+
radial2: options.projection2,
|
|
7813
|
+
radialBlend: options.blend,
|
|
7814
|
+
boundaryStrength: RADIAL_BOUNDARY_STRENGTH
|
|
7815
|
+
};
|
|
7816
|
+
var squareProjectors = createSquareProjectors(data.faces, radialOptions);
|
|
7817
|
+
var engine = createPolyhedralProjection({
|
|
7818
|
+
faces: data.faces,
|
|
7819
|
+
parents: data.parents,
|
|
7820
|
+
rotation: [0, 0, 0],
|
|
7821
|
+
angle: -30,
|
|
7822
|
+
findFace: createTruncatedFaceFinder(data.cornerNormals),
|
|
7823
|
+
faceProjector: function(face) {
|
|
7824
|
+
if (face.id < 8) {
|
|
7825
|
+
return createRadialFacetProjector(face.coords, radialOptions);
|
|
7826
|
+
}
|
|
7827
|
+
return squareProjectors.get(pointKey(face.coords[0]));
|
|
7828
|
+
}
|
|
7829
|
+
});
|
|
7830
|
+
engine.radialFacet = {
|
|
7831
|
+
projection: options.projection,
|
|
7832
|
+
projection2: options.projection2,
|
|
7833
|
+
blend: options.blend,
|
|
7834
|
+
boundaryStrength: RADIAL_BOUNDARY_STRENGTH
|
|
7835
|
+
};
|
|
7836
|
+
return engine;
|
|
7837
|
+
}
|
|
7838
|
+
|
|
7839
|
+
function normalizeRadialFacetOptions(options) {
|
|
7840
|
+
options = options || {};
|
|
7841
|
+
var projection2 = options.projection2 ?
|
|
7842
|
+
normalizeRadialProjectionName(options.projection2) :
|
|
7843
|
+
null;
|
|
7844
|
+
var blend = projection2 ?
|
|
7845
|
+
options.blend == null ? 0.5 : Number(options.blend) :
|
|
7846
|
+
0;
|
|
7847
|
+
if (!Number.isFinite(blend) || blend < 0 || blend > 1) {
|
|
7848
|
+
throw new Error('Facet projection blend must be between 0 and 1');
|
|
7849
|
+
}
|
|
7850
|
+
return {
|
|
7851
|
+
projection: normalizeRadialProjectionName(options.projection || 'laea'),
|
|
7852
|
+
projection2: projection2,
|
|
7853
|
+
blend: blend
|
|
7854
|
+
};
|
|
7855
|
+
}
|
|
7856
|
+
|
|
7857
|
+
function createCahillKeyesEngine() {
|
|
7858
|
+
var data = createTruncatedOctahedronFaces(
|
|
7859
|
+
Math.cos(17 * D2R$1),
|
|
7860
|
+
Math.sin(17 * D2R$1),
|
|
7861
|
+
[-1, 3, 0, 2, 0, 1, 4, 5]
|
|
7862
|
+
);
|
|
7863
|
+
var raw = createCahillKeyesRaw(10000);
|
|
7864
|
+
var engine = createPolyhedralProjection({
|
|
7865
|
+
faces: data.faces,
|
|
7866
|
+
parents: data.parents,
|
|
7867
|
+
rotation: [0, 0, 0],
|
|
7868
|
+
angle: 0,
|
|
7869
|
+
findFace: createTruncatedFaceFinder(data.cornerNormals),
|
|
7870
|
+
faceProjector: function() {
|
|
7871
|
+
return function(lam, phi) {
|
|
7872
|
+
var p = raw(lam, phi);
|
|
7873
|
+
return [p[0], -p[1]];
|
|
7874
|
+
};
|
|
7875
|
+
}
|
|
7876
|
+
});
|
|
7877
|
+
// The 12-zone transform positions all octants directly in its M-profile.
|
|
7878
|
+
// Its face-tree boundary retraces several edges, so derive graticule
|
|
7879
|
+
// footprints through the normal spherical clipping path instead.
|
|
7880
|
+
engine.useProjectedOutline = false;
|
|
7881
|
+
engine.removeOutlineExtremeConnectors = true;
|
|
7882
|
+
return engine;
|
|
7883
|
+
}
|
|
7884
|
+
|
|
7885
|
+
function createRadialButterflyFaces() {
|
|
7886
|
+
return createTruncatedOctahedronFaces(
|
|
7887
|
+
Math.sqrt(0.9),
|
|
7888
|
+
Math.sqrt(0.1),
|
|
7889
|
+
BUTTERFLY_PARENTS
|
|
7890
|
+
);
|
|
7891
|
+
}
|
|
7892
|
+
|
|
7893
|
+
function createTruncatedOctahedronFaces(edgeWeight, vertexWeight, baseParents) {
|
|
7894
|
+
var cornerNormals = [];
|
|
7895
|
+
var faces = OCTAHEDRON.map(function(face) {
|
|
7896
|
+
var vectors = face.map(degreesToVector);
|
|
7897
|
+
var hexagon = [];
|
|
7898
|
+
var a = vectors[vectors.length - 1];
|
|
7899
|
+
vectors.forEach(function(b) {
|
|
7900
|
+
hexagon.push(
|
|
7901
|
+
vectorToDegrees(normalizeVector(addScaledVectors(
|
|
7902
|
+
a, edgeWeight, b, vertexWeight
|
|
7903
|
+
))),
|
|
7904
|
+
vectorToDegrees(normalizeVector(addScaledVectors(
|
|
7905
|
+
b, edgeWeight, a, vertexWeight
|
|
7906
|
+
)))
|
|
7907
|
+
);
|
|
7908
|
+
a = b;
|
|
7909
|
+
});
|
|
7910
|
+
return hexagon;
|
|
7911
|
+
});
|
|
7912
|
+
canonicalizeVertices(faces);
|
|
7913
|
+
faces.forEach(function(hexagon) {
|
|
7914
|
+
var normals = [];
|
|
7915
|
+
for (var i = 0; i < 3; i++) {
|
|
7916
|
+
normals.push(crossVectors(
|
|
7917
|
+
degreesToVector(hexagon[(i * 2 + 2) % 6]),
|
|
7918
|
+
degreesToVector(hexagon[(i * 2 + 1) % 6])
|
|
7919
|
+
));
|
|
7920
|
+
}
|
|
7921
|
+
cornerNormals.push(normals);
|
|
7922
|
+
});
|
|
7923
|
+
var parents = baseParents.concat();
|
|
7924
|
+
faces.slice().forEach(function(hexagon, j) {
|
|
7925
|
+
var face = OCTAHEDRON[j];
|
|
7926
|
+
for (var i = 0; i < 3; i++) {
|
|
7927
|
+
faces.push([
|
|
7928
|
+
face[i],
|
|
7929
|
+
hexagon[(i * 2 + 2) % 6],
|
|
7930
|
+
hexagon[(i * 2 + 1) % 6]
|
|
7931
|
+
]);
|
|
7932
|
+
parents.push(j);
|
|
7933
|
+
}
|
|
7934
|
+
});
|
|
7935
|
+
return {faces: faces, parents: parents, cornerNormals: cornerNormals};
|
|
7936
|
+
}
|
|
7937
|
+
|
|
7938
|
+
function createTruncatedFaceFinder(cornerNormals) {
|
|
7939
|
+
return function(lam, phi) {
|
|
7940
|
+
var cosPhi = Math.cos(phi);
|
|
7941
|
+
var p = [cosPhi * Math.cos(lam), cosPhi * Math.sin(lam), Math.sin(phi)];
|
|
7942
|
+
var face = lam < -Math.PI / 2 ?
|
|
7943
|
+
phi < 0 ? 6 : 4 :
|
|
7944
|
+
lam < 0 ?
|
|
7945
|
+
phi < 0 ? 2 : 0 :
|
|
7946
|
+
lam < Math.PI / 2 ?
|
|
7947
|
+
phi < 0 ? 3 : 1 :
|
|
7948
|
+
phi < 0 ? 7 : 5;
|
|
7949
|
+
var normals = cornerNormals[face];
|
|
7950
|
+
if (dotVectors(normals[0], p) < 0) return 8 + 3 * face;
|
|
7951
|
+
if (dotVectors(normals[1], p) < 0) return 8 + 3 * face + 1;
|
|
7952
|
+
if (dotVectors(normals[2], p) < 0) return 8 + 3 * face + 2;
|
|
7953
|
+
return face;
|
|
7954
|
+
};
|
|
7955
|
+
}
|
|
7956
|
+
|
|
7957
|
+
function canonicalizeVertices(faces) {
|
|
7958
|
+
var index = new Map();
|
|
7959
|
+
faces.forEach(function(face) {
|
|
7960
|
+
face.forEach(function(p, i) {
|
|
7961
|
+
var v = degreesToVector(p);
|
|
7962
|
+
var key = v.map(function(x) {
|
|
7963
|
+
return x.toFixed(12);
|
|
7964
|
+
}).join(',');
|
|
7965
|
+
if (index.has(key)) {
|
|
7966
|
+
face[i] = index.get(key);
|
|
7967
|
+
} else {
|
|
7968
|
+
index.set(key, p);
|
|
7969
|
+
}
|
|
7970
|
+
});
|
|
7971
|
+
});
|
|
7972
|
+
}
|
|
7973
|
+
|
|
7974
|
+
function createSquareProjectors(faces, radialOptions) {
|
|
7975
|
+
var verticesByCenter = new Map();
|
|
7976
|
+
var projectors = new Map();
|
|
7977
|
+
faces.slice(8).forEach(function(face) {
|
|
7978
|
+
var key = pointKey(face[0]);
|
|
7979
|
+
var vertices = verticesByCenter.get(key);
|
|
7980
|
+
if (!vertices) {
|
|
7981
|
+
vertices = new Map();
|
|
7982
|
+
verticesByCenter.set(key, vertices);
|
|
7983
|
+
}
|
|
7984
|
+
vertices.set(pointKey(face[1]), face[1]);
|
|
7985
|
+
vertices.set(pointKey(face[2]), face[2]);
|
|
7986
|
+
});
|
|
7987
|
+
verticesByCenter.forEach(function(vertices, key) {
|
|
7988
|
+
var center = key.split(',').map(Number);
|
|
7989
|
+
var gnomonic = createGnomonicProjector(center);
|
|
7990
|
+
var coords = Array.from(vertices.values()).sort(function(a, b) {
|
|
7991
|
+
return planarAngle(a, gnomonic) - planarAngle(b, gnomonic);
|
|
7992
|
+
});
|
|
7993
|
+
projectors.set(key, createRadialFacetProjector(coords, {
|
|
7994
|
+
planarCenter: center,
|
|
7995
|
+
radial: radialOptions.radial,
|
|
7996
|
+
radial2: radialOptions.radial2,
|
|
7997
|
+
radialBlend: radialOptions.radialBlend,
|
|
7998
|
+
boundaryStrength: radialOptions.boundaryStrength
|
|
7999
|
+
}));
|
|
8000
|
+
});
|
|
8001
|
+
return projectors;
|
|
8002
|
+
}
|
|
8003
|
+
|
|
8004
|
+
function planarAngle(p, project) {
|
|
8005
|
+
var q = project(p[0] * D2R$1, p[1] * D2R$1);
|
|
8006
|
+
return Math.atan2(q[1], q[0]);
|
|
8007
|
+
}
|
|
8008
|
+
|
|
8009
|
+
function createOctahedron() {
|
|
8010
|
+
var vertices = [
|
|
8011
|
+
[0, 90],
|
|
8012
|
+
[-90, 0],
|
|
8013
|
+
[0, 0],
|
|
8014
|
+
[90, 0],
|
|
8015
|
+
[180, 0],
|
|
8016
|
+
[0, -90]
|
|
8017
|
+
];
|
|
8018
|
+
return [
|
|
8019
|
+
[0, 2, 1],
|
|
8020
|
+
[0, 3, 2],
|
|
8021
|
+
[5, 1, 2],
|
|
8022
|
+
[5, 2, 3],
|
|
8023
|
+
[0, 1, 4],
|
|
8024
|
+
[0, 4, 3],
|
|
8025
|
+
[5, 4, 1],
|
|
8026
|
+
[5, 3, 4]
|
|
8027
|
+
].map(function(ids) {
|
|
8028
|
+
return ids.map(function(id) {
|
|
8029
|
+
return vertices[id];
|
|
8030
|
+
});
|
|
8031
|
+
});
|
|
8032
|
+
}
|
|
8033
|
+
|
|
8034
|
+
function pointKey(p) {
|
|
8035
|
+
return p[0] + ',' + p[1];
|
|
8036
|
+
}
|
|
8037
|
+
|
|
8038
|
+
function degreesToVector(p) {
|
|
8039
|
+
var lam = p[0] * D2R$1;
|
|
8040
|
+
var phi = p[1] * D2R$1;
|
|
8041
|
+
var cosPhi = Math.cos(phi);
|
|
8042
|
+
return [Math.cos(lam) * cosPhi, Math.sin(lam) * cosPhi, Math.sin(phi)];
|
|
8043
|
+
}
|
|
8044
|
+
|
|
8045
|
+
function vectorToDegrees(p) {
|
|
8046
|
+
return [
|
|
8047
|
+
Math.atan2(p[1], p[0]) * R2D$1,
|
|
8048
|
+
Math.asin(Math.max(-1, Math.min(1, p[2]))) * R2D$1
|
|
8049
|
+
];
|
|
8050
|
+
}
|
|
8051
|
+
|
|
8052
|
+
function addScaledVectors(a, ka, b, kb) {
|
|
8053
|
+
return [
|
|
8054
|
+
a[0] * ka + b[0] * kb,
|
|
8055
|
+
a[1] * ka + b[1] * kb,
|
|
8056
|
+
a[2] * ka + b[2] * kb
|
|
8057
|
+
];
|
|
8058
|
+
}
|
|
8059
|
+
|
|
8060
|
+
function crossVectors(a, b) {
|
|
8061
|
+
return [
|
|
8062
|
+
a[1] * b[2] - a[2] * b[1],
|
|
8063
|
+
a[2] * b[0] - a[0] * b[2],
|
|
8064
|
+
a[0] * b[1] - a[1] * b[0]
|
|
8065
|
+
];
|
|
8066
|
+
}
|
|
8067
|
+
|
|
8068
|
+
function dotVectors(a, b) {
|
|
8069
|
+
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
8070
|
+
}
|
|
8071
|
+
|
|
8072
|
+
function normalizeVector(p) {
|
|
8073
|
+
var k = 1 / Math.sqrt(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]);
|
|
8074
|
+
return [p[0] * k, p[1] * k, p[2] * k];
|
|
8075
|
+
}
|
|
8076
|
+
|
|
8077
|
+
function normalizeRadians(lam) {
|
|
8078
|
+
return (lam + Math.PI * 3) % (Math.PI * 2) - Math.PI;
|
|
8079
|
+
}
|
|
8080
|
+
|
|
8081
|
+
var ButterflyProjections = /*#__PURE__*/Object.freeze({
|
|
8082
|
+
__proto__: null,
|
|
8083
|
+
createRadialButterflyEngine: createRadialButterflyEngine,
|
|
8084
|
+
getButterflyEngine: getButterflyEngine,
|
|
8085
|
+
registerButterflyProjections: registerButterflyProjections
|
|
8086
|
+
});
|
|
8087
|
+
|
|
6290
8088
|
var mproj$1 = require$1('mproj');
|
|
8089
|
+
registerDymaxionProjections(mproj$1);
|
|
8090
|
+
registerButterflyProjections(mproj$1);
|
|
6291
8091
|
|
|
6292
8092
|
var asyncLoader = null;
|
|
6293
8093
|
|
|
@@ -6456,9 +8256,18 @@
|
|
|
6456
8256
|
if (!defn) {
|
|
6457
8257
|
stop$1("Unknown projection definition:", str);
|
|
6458
8258
|
}
|
|
8259
|
+
if (utils.isString(defn)) {
|
|
8260
|
+
defn = addDefaultProjectionParams(defn);
|
|
8261
|
+
}
|
|
6459
8262
|
return defn;
|
|
6460
8263
|
}
|
|
6461
8264
|
|
|
8265
|
+
function addDefaultProjectionParams(defn) {
|
|
8266
|
+
var oceanic = /(^| )\+proj=(igh_o|imoll_o)(?= |$)/.test(defn);
|
|
8267
|
+
var hasLon0 = /(^| )\+lon_0=/.test(defn);
|
|
8268
|
+
return oceanic && !hasLon0 ? defn + ' +lon_0=-160' : defn;
|
|
8269
|
+
}
|
|
8270
|
+
|
|
6462
8271
|
function looksLikeInitString(str) {
|
|
6463
8272
|
return /^(esri|epsg|nad83|nad27):[0-9]+$/i.test(String(str));
|
|
6464
8273
|
}
|
|
@@ -32622,6 +34431,10 @@ ${svg}
|
|
|
32622
34431
|
describe: 'create a polygon to match the outline of the graticule',
|
|
32623
34432
|
type: 'flag'
|
|
32624
34433
|
})
|
|
34434
|
+
.option('outline', {
|
|
34435
|
+
describe: 'create a polyline matching the outline of the graticule',
|
|
34436
|
+
type: 'flag'
|
|
34437
|
+
})
|
|
32625
34438
|
.option('name', nameOpt)
|
|
32626
34439
|
.option('target', targetOpt);
|
|
32627
34440
|
|
|
@@ -41371,7 +43184,7 @@ ${svg}
|
|
|
41371
43184
|
|
|
41372
43185
|
function getPlanarFilter(interval) {
|
|
41373
43186
|
return function(a, b) {
|
|
41374
|
-
return distance2D(a[0], a[1], b[0], b[1]) <= interval;
|
|
43187
|
+
return distance2D$1(a[0], a[1], b[0], b[1]) <= interval;
|
|
41375
43188
|
};
|
|
41376
43189
|
}
|
|
41377
43190
|
|
|
@@ -41804,7 +43617,7 @@ ${svg}
|
|
|
41804
43617
|
}
|
|
41805
43618
|
|
|
41806
43619
|
function probeIsExposed(index, skipA, skipB, vx, vy, p) {
|
|
41807
|
-
var r = distance2D(vx, vy, p[0], p[1]);
|
|
43620
|
+
var r = distance2D$1(vx, vy, p[0], p[1]);
|
|
41808
43621
|
if (!(r > 0)) return false;
|
|
41809
43622
|
var r2 = r * r * 0.98 * 0.98;
|
|
41810
43623
|
var px = p[0], py = p[1];
|
|
@@ -43386,7 +45199,7 @@ ${svg}
|
|
|
43386
45199
|
|
|
43387
45200
|
function stopIfBufferReachesPole(shape, dist) {
|
|
43388
45201
|
var maxAbsLat = 0;
|
|
43389
|
-
var angularDist = dist / R$3 * R2D$
|
|
45202
|
+
var angularDist = dist / R$3 * R2D$5;
|
|
43390
45203
|
(shape || []).forEach(function(path) {
|
|
43391
45204
|
latLngPathIter.init(path);
|
|
43392
45205
|
while (latLngPathIter.hasNext()) {
|
|
@@ -43877,11 +45690,11 @@ ${svg}
|
|
|
43877
45690
|
if (a[0] === b[0] && a[1] === b[1]) continue;
|
|
43878
45691
|
bearing = bearingDegrees2D(a[0], a[1], b[0], b[1]);
|
|
43879
45692
|
probe = getOffsetPoint(a[0], a[1], bearing - 90, dist);
|
|
43880
|
-
rUnits = distance2D(a[0], a[1], probe[0], probe[1]);
|
|
45693
|
+
rUnits = distance2D$1(a[0], a[1], probe[0], probe[1]);
|
|
43881
45694
|
// perpendicular stab lines every ~0.35r along the band, so cuts
|
|
43882
45695
|
// affecting only part of a long segment cannot slip between them
|
|
43883
45696
|
m = Math.max(1, Math.min(60,
|
|
43884
|
-
Math.ceil(distance2D(a[0], a[1], b[0], b[1]) / (rUnits * 0.35))));
|
|
45697
|
+
Math.ceil(distance2D$1(a[0], a[1], b[0], b[1]) / (rUnits * 0.35))));
|
|
43885
45698
|
for (ti = 0; ti < m; ti++) {
|
|
43886
45699
|
t = (ti + 0.5) / m;
|
|
43887
45700
|
base = [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
|
|
@@ -43915,14 +45728,14 @@ ${svg}
|
|
|
43915
45728
|
function findAtRiskSegments(verts, dist) {
|
|
43916
45729
|
var n = verts.length;
|
|
43917
45730
|
var probe0 = getOffsetPoint(verts[0][0], verts[0][1], 0, dist);
|
|
43918
|
-
var rUnits = distance2D(verts[0][0], verts[0][1], probe0[0], probe0[1]);
|
|
45731
|
+
var rUnits = distance2D$1(verts[0][0], verts[0][1], probe0[0], probe0[1]);
|
|
43919
45732
|
var cumLen = new Float64Array(n);
|
|
43920
45733
|
var riskVert = new Uint8Array(n);
|
|
43921
45734
|
var any = false;
|
|
43922
45735
|
var i, j, k;
|
|
43923
45736
|
for (i = 1; i < n; i++) {
|
|
43924
45737
|
cumLen[i] = cumLen[i - 1] +
|
|
43925
|
-
distance2D(verts[i - 1][0], verts[i - 1][1], verts[i][0], verts[i][1]);
|
|
45738
|
+
distance2D$1(verts[i - 1][0], verts[i - 1][1], verts[i][0], verts[i][1]);
|
|
43926
45739
|
}
|
|
43927
45740
|
// the ring's start radial and cap-closing radial can cut nearby bands
|
|
43928
45741
|
riskVert[0] = riskVert[n - 1] = 1;
|
|
@@ -43994,7 +45807,7 @@ ${svg}
|
|
|
43994
45807
|
j = ids[k];
|
|
43995
45808
|
if (j <= i) continue;
|
|
43996
45809
|
var pathDist = cumLen[j] - cumLen[i];
|
|
43997
|
-
var d = distance2D(verts[i][0], verts[i][1], verts[j][0], verts[j][1]);
|
|
45810
|
+
var d = distance2D$1(verts[i][0], verts[i][1], verts[j][0], verts[j][1]);
|
|
43998
45811
|
// 0.8 flags vertex pairs whose intervening path bends by
|
|
43999
45812
|
// ~130 degrees or more (a circle's chord/arc ratio is 2/pi,
|
|
44000
45813
|
// so even gentle loops qualify)
|
|
@@ -44046,7 +45859,7 @@ ${svg}
|
|
|
44046
45859
|
// at the near end is seeded by an upward ray over those same edges.
|
|
44047
45860
|
function getRingStabber(rings, dist) {
|
|
44048
45861
|
var probe0 = getOffsetPoint(rings[0][0][0], rings[0][0][1], 0, dist);
|
|
44049
|
-
var slabW = distance2D(rings[0][0][0], rings[0][0][1], probe0[0], probe0[1]);
|
|
45862
|
+
var slabW = distance2D$1(rings[0][0][0], rings[0][0][1], probe0[0], probe0[1]);
|
|
44050
45863
|
var ex = [], ey = [], fx = [], fy = []; // edge data
|
|
44051
45864
|
var slabs = new Map();
|
|
44052
45865
|
rings.forEach(function(ring) {
|
|
@@ -44146,8 +45959,8 @@ ${svg}
|
|
|
44146
45959
|
var eps = dist * 1e-4;
|
|
44147
45960
|
var aIn = getOffsetPoint(a[0], a[1], bearing, eps);
|
|
44148
45961
|
var bIn = getOffsetPoint(b[0], b[1], bearing - 180, eps);
|
|
44149
|
-
if (distance2D(a[0], a[1], b[0], b[1]) <
|
|
44150
|
-
distance2D(a[0], a[1], aIn[0], aIn[1]) * 3) {
|
|
45962
|
+
if (distance2D$1(a[0], a[1], b[0], b[1]) <
|
|
45963
|
+
distance2D$1(a[0], a[1], aIn[0], aIn[1]) * 3) {
|
|
44151
45964
|
return; // segment too short to inset
|
|
44152
45965
|
}
|
|
44153
45966
|
rings.push([
|
|
@@ -44544,7 +46357,7 @@ ${svg}
|
|
|
44544
46357
|
}
|
|
44545
46358
|
|
|
44546
46359
|
function shallowAngleJoin(a, b, cx, cy, dist) {
|
|
44547
|
-
var gap = distance2D(a[0], a[1], b[0], b[1]);
|
|
46360
|
+
var gap = distance2D$1(a[0], a[1], b[0], b[1]);
|
|
44548
46361
|
var radius = getJoinExtensionDistance(cx, cy, a, b, dist);
|
|
44549
46362
|
if (gap > radius * 1e-3) return null;
|
|
44550
46363
|
return [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
|
|
@@ -45503,12 +47316,12 @@ ${svg}
|
|
|
45503
47316
|
}
|
|
45504
47317
|
|
|
45505
47318
|
function projectBufferPoint(x, y, spherical) {
|
|
45506
|
-
return spherical ? [x * D2R$
|
|
47319
|
+
return spherical ? [x * D2R$6 * R$3, Math.log(Math.tan(Math.PI / 4 + y * D2R$6 / 2)) * R$3] :
|
|
45507
47320
|
[x, y];
|
|
45508
47321
|
}
|
|
45509
47322
|
|
|
45510
47323
|
function unprojectBufferPoint(x, y, spherical) {
|
|
45511
|
-
return spherical ? [x / (D2R$
|
|
47324
|
+
return spherical ? [x / (D2R$6 * R$3), (2 * Math.atan(Math.exp(y / R$3)) - Math.PI / 2) / (D2R$6)] :
|
|
45512
47325
|
[x, y];
|
|
45513
47326
|
}
|
|
45514
47327
|
|
|
@@ -50179,7 +51992,7 @@ ${svg}
|
|
|
50179
51992
|
var maxAbsLat = Math.max(Math.abs(bounds.ymin), Math.abs(bounds.ymax));
|
|
50180
51993
|
var maxPositiveDistance = getMaxPositiveBufferDistance(lyr, dataset, opts);
|
|
50181
51994
|
if (!(maxPositiveDistance > 0)) return false;
|
|
50182
|
-
return maxAbsLat + maxPositiveDistance / R$3 * R2D$
|
|
51995
|
+
return maxAbsLat + maxPositiveDistance / R$3 * R2D$5 >= 90 - POLAR_BUFFER_MARGIN_DEGREES;
|
|
50183
51996
|
}
|
|
50184
51997
|
|
|
50185
51998
|
function getMaxPositiveBufferDistance(lyr, dataset, opts) {
|
|
@@ -51431,7 +53244,7 @@ ${svg}
|
|
|
51431
53244
|
}
|
|
51432
53245
|
|
|
51433
53246
|
function getCoordinateDistance(distance, arcs) {
|
|
51434
|
-
return arcs.isPlanar() ? distance : distance / R$3 * R2D$
|
|
53247
|
+
return arcs.isPlanar() ? distance : distance / R$3 * R2D$5;
|
|
51435
53248
|
}
|
|
51436
53249
|
|
|
51437
53250
|
// @shapeIndex: chunk-bounds index of the source shape (see buildShapeSegmentIndex)
|
|
@@ -52099,7 +53912,8 @@ ${svg}
|
|
|
52099
53912
|
// TODO: consider projections that may or may not be aligned,
|
|
52100
53913
|
// depending on parameters
|
|
52101
53914
|
if (inList(P, 'cassini,gnom,bertin1953,chamb,ob_tran,tpeqd,healpix,rhealpix,' +
|
|
52102
|
-
'ocea,omerc,tmerc,etmerc,nicol'
|
|
53915
|
+
'ocea,omerc,tmerc,etmerc,nicol,dymaxion,dymaxion2,butterfly,butterfly2,' +
|
|
53916
|
+
'cahill_keyes')) {
|
|
52103
53917
|
return false;
|
|
52104
53918
|
}
|
|
52105
53919
|
if (isAzimuthal(P)) {
|
|
@@ -52151,16 +53965,16 @@ ${svg}
|
|
|
52151
53965
|
}
|
|
52152
53966
|
|
|
52153
53967
|
function getRotationFunction2(rotation, inv) {
|
|
52154
|
-
var a = (rotation[0] || 0) * D2R$
|
|
52155
|
-
b = (rotation[1] || 0) * D2R$
|
|
52156
|
-
c = (rotation[2] || 0) * D2R$
|
|
53968
|
+
var a = (rotation[0] || 0) * D2R$6,
|
|
53969
|
+
b = (rotation[1] || 0) * D2R$6,
|
|
53970
|
+
c = (rotation[2] || 0) * D2R$6;
|
|
52157
53971
|
return function(p) {
|
|
52158
|
-
p[0] *= D2R$
|
|
52159
|
-
p[1] *= D2R$
|
|
53972
|
+
p[0] *= D2R$6;
|
|
53973
|
+
p[1] *= D2R$6;
|
|
52160
53974
|
var rotate = inv ? rotatePointInv : rotatePoint;
|
|
52161
53975
|
rotate(p, a, b, c);
|
|
52162
|
-
p[0] *= R2D$
|
|
52163
|
-
p[1] *= R2D$
|
|
53976
|
+
p[0] *= R2D$5;
|
|
53977
|
+
p[1] *= R2D$5;
|
|
52164
53978
|
return p;
|
|
52165
53979
|
};
|
|
52166
53980
|
}
|
|
@@ -52573,6 +54387,128 @@ ${svg}
|
|
|
52573
54387
|
polygonsToLines: polygonsToLines
|
|
52574
54388
|
});
|
|
52575
54389
|
|
|
54390
|
+
// Geographic topology used to prepare paths for projections whose forward
|
|
54391
|
+
// transform is piecewise. A "cut" seam opens a visible gap; an "attached"
|
|
54392
|
+
// seam (reserved for polyhedral projections) marks a transform boundary that
|
|
54393
|
+
// must gain vertices without disconnecting the path.
|
|
54394
|
+
var landTopology = {
|
|
54395
|
+
regions: [
|
|
54396
|
+
region('northwest', [-180, 0, -40, 90], -100),
|
|
54397
|
+
region('northeast', [-40, 0, 180, 90], 30),
|
|
54398
|
+
region('southwest', [-180, -90, -100, 0], -160),
|
|
54399
|
+
region('south_central_west', [-100, -90, -20, 0], -60),
|
|
54400
|
+
region('south_central_east', [-20, -90, 80, 0], 20),
|
|
54401
|
+
region('southeast', [80, -90, 180, 0], 140)
|
|
54402
|
+
],
|
|
54403
|
+
seams: [
|
|
54404
|
+
cutSeam([[-40, 0], [-40, 91]]),
|
|
54405
|
+
cutSeam([[-100, -91], [-100, 0]]),
|
|
54406
|
+
cutSeam([[-20, -91], [-20, 0]]),
|
|
54407
|
+
cutSeam([[80, -91], [80, 0]])
|
|
54408
|
+
]
|
|
54409
|
+
};
|
|
54410
|
+
|
|
54411
|
+
var oceanTopology = {
|
|
54412
|
+
regions: [
|
|
54413
|
+
region('northwest', [-180, 0, -90, 90], -140),
|
|
54414
|
+
region('north_central', [-90, 0, 60, 90], -10),
|
|
54415
|
+
region('northeast', [60, 0, 180, 90], 130),
|
|
54416
|
+
region('southwest', [-180, -90, -60, 0], -110),
|
|
54417
|
+
region('south_central', [-60, -90, 90, 0], 20),
|
|
54418
|
+
region('southeast', [90, -90, 180, 0], 150)
|
|
54419
|
+
],
|
|
54420
|
+
seams: [
|
|
54421
|
+
cutSeam([[-90, 0], [-90, 91]]),
|
|
54422
|
+
cutSeam([[60, 0], [60, 91]]),
|
|
54423
|
+
cutSeam([[-60, -91], [-60, 0]]),
|
|
54424
|
+
cutSeam([[90, -91], [90, 0]])
|
|
54425
|
+
]
|
|
54426
|
+
};
|
|
54427
|
+
|
|
54428
|
+
var projectionTopologies = {
|
|
54429
|
+
igh: landTopology,
|
|
54430
|
+
imoll: landTopology,
|
|
54431
|
+
igh_o: oceanTopology,
|
|
54432
|
+
imoll_o: oceanTopology
|
|
54433
|
+
};
|
|
54434
|
+
|
|
54435
|
+
function getProjectionTopology(P) {
|
|
54436
|
+
if (P && P.__projection_topology) {
|
|
54437
|
+
return P.__projection_topology;
|
|
54438
|
+
}
|
|
54439
|
+
var defn = P && projectionTopologies[getCrsSlug(P)];
|
|
54440
|
+
if (!defn) return null;
|
|
54441
|
+
var lon0 = P.lam0 * 180 / Math.PI;
|
|
54442
|
+
return {
|
|
54443
|
+
regions: defn.regions.map(function(o) {
|
|
54444
|
+
return {
|
|
54445
|
+
id: o.id,
|
|
54446
|
+
boundary: rotatePath(o.boundary, lon0),
|
|
54447
|
+
transform: {
|
|
54448
|
+
lon_0: normalizeLongitude(o.transform.lon_0 + lon0)
|
|
54449
|
+
}
|
|
54450
|
+
};
|
|
54451
|
+
}),
|
|
54452
|
+
seams: defn.seams.map(function(o) {
|
|
54453
|
+
return {
|
|
54454
|
+
type: o.type,
|
|
54455
|
+
coordinates: rotatePath(o.coordinates, lon0)
|
|
54456
|
+
};
|
|
54457
|
+
})
|
|
54458
|
+
};
|
|
54459
|
+
}
|
|
54460
|
+
|
|
54461
|
+
function isInterruptedProjection(P) {
|
|
54462
|
+
if (P && P.__projection_topology) {
|
|
54463
|
+
return P.__projection_topology.seams.some(function(o) {
|
|
54464
|
+
return o.type == 'cut';
|
|
54465
|
+
});
|
|
54466
|
+
}
|
|
54467
|
+
var defn = P && projectionTopologies[getCrsSlug(P)];
|
|
54468
|
+
return !!defn && defn.seams.some(function(o) {
|
|
54469
|
+
return o.type == 'cut';
|
|
54470
|
+
});
|
|
54471
|
+
}
|
|
54472
|
+
|
|
54473
|
+
function region(id, bbox, lon0) {
|
|
54474
|
+
return {
|
|
54475
|
+
id: id,
|
|
54476
|
+
boundary: [
|
|
54477
|
+
[bbox[0], bbox[1]],
|
|
54478
|
+
[bbox[0], bbox[3]],
|
|
54479
|
+
[bbox[2], bbox[3]],
|
|
54480
|
+
[bbox[2], bbox[1]],
|
|
54481
|
+
[bbox[0], bbox[1]]
|
|
54482
|
+
],
|
|
54483
|
+
transform: {lon_0: lon0}
|
|
54484
|
+
};
|
|
54485
|
+
}
|
|
54486
|
+
|
|
54487
|
+
function cutSeam(coordinates) {
|
|
54488
|
+
return {
|
|
54489
|
+
type: 'cut',
|
|
54490
|
+
coordinates: coordinates
|
|
54491
|
+
};
|
|
54492
|
+
}
|
|
54493
|
+
|
|
54494
|
+
function rotatePath(path, degrees) {
|
|
54495
|
+
return path.map(function(p) {
|
|
54496
|
+
return [normalizeLongitude(p[0] + degrees), p[1]];
|
|
54497
|
+
});
|
|
54498
|
+
}
|
|
54499
|
+
|
|
54500
|
+
function normalizeLongitude(lon) {
|
|
54501
|
+
while (lon > 180) lon -= 360;
|
|
54502
|
+
while (lon < -180) lon += 360;
|
|
54503
|
+
return lon;
|
|
54504
|
+
}
|
|
54505
|
+
|
|
54506
|
+
var ProjectionTopology = /*#__PURE__*/Object.freeze({
|
|
54507
|
+
__proto__: null,
|
|
54508
|
+
getProjectionTopology: getProjectionTopology,
|
|
54509
|
+
isInterruptedProjection: isInterruptedProjection
|
|
54510
|
+
});
|
|
54511
|
+
|
|
52576
54512
|
function getClippingDataset(src, dest, opts) {
|
|
52577
54513
|
return getUnprojectedBoundingPolygon(src, dest, opts);
|
|
52578
54514
|
}
|
|
@@ -52601,6 +54537,9 @@ ${svg}
|
|
|
52601
54537
|
|
|
52602
54538
|
// Return projected polygon extent of both clipped and unclipped projections
|
|
52603
54539
|
function getPolygonDataset$1(src, dest, opts) {
|
|
54540
|
+
if (dest.__projected_outline) {
|
|
54541
|
+
return getCustomProjectedOutline(dest, 'polygon');
|
|
54542
|
+
}
|
|
52604
54543
|
// use clipping area if projection is clipped
|
|
52605
54544
|
var dataset = getUnprojectedBoundingPolygon(src, dest, opts);
|
|
52606
54545
|
if (!dataset) {
|
|
@@ -52608,20 +54547,124 @@ ${svg}
|
|
|
52608
54547
|
dataset = getBoundingRectangle(dest, {clip_bbox: [-180,-90,180,90]});
|
|
52609
54548
|
}
|
|
52610
54549
|
projectDataset(dataset, src, dest, {no_clip: false, quiet: true});
|
|
54550
|
+
if (isInterruptedProjection(dest)) {
|
|
54551
|
+
removeTinyFootprintRings(dataset);
|
|
54552
|
+
}
|
|
52611
54553
|
return dataset;
|
|
52612
54554
|
}
|
|
52613
54555
|
|
|
52614
54556
|
// Return projected outline of clipped projections
|
|
52615
54557
|
function getOutlineDataset(src, dest, opts) {
|
|
54558
|
+
if (dest.__projected_outline) {
|
|
54559
|
+
return getCustomProjectedOutline(dest, 'polyline');
|
|
54560
|
+
}
|
|
52616
54561
|
var dataset = getUnprojectedBoundingPolygon(src, dest, opts);
|
|
54562
|
+
if (!dataset && isInterruptedProjection(dest)) {
|
|
54563
|
+
dataset = getBoundingRectangle(dest, {clip_bbox: [-180, -90, 180, 90]});
|
|
54564
|
+
}
|
|
52617
54565
|
if (dataset) {
|
|
52618
54566
|
// project, with cutting & cleanup
|
|
52619
54567
|
projectDataset(dataset, src, dest, {no_clip: false, quiet: true});
|
|
54568
|
+
if (isInterruptedProjection(dest)) {
|
|
54569
|
+
removeTinyFootprintRings(dataset);
|
|
54570
|
+
}
|
|
52620
54571
|
dataset.layers[0].geometry_type = 'polyline';
|
|
54572
|
+
if (dest.__remove_outline_extreme_connectors) {
|
|
54573
|
+
dataset = removeOutlineExtremeConnectors(dataset);
|
|
54574
|
+
}
|
|
52621
54575
|
}
|
|
52622
54576
|
return dataset || null;
|
|
52623
54577
|
}
|
|
52624
54578
|
|
|
54579
|
+
function getCustomProjectedOutline(P, geometryType) {
|
|
54580
|
+
var rings = P.__projected_outline.map(function(ring) {
|
|
54581
|
+
return ring.map(function(p) {
|
|
54582
|
+
return [
|
|
54583
|
+
P.fr_meter * (P.a * p[0] + P.x0),
|
|
54584
|
+
P.fr_meter * (P.a * p[1] + P.y0)
|
|
54585
|
+
];
|
|
54586
|
+
});
|
|
54587
|
+
});
|
|
54588
|
+
var dataset = importGeoJSON({
|
|
54589
|
+
type: rings.length == 1 ? 'Polygon' : 'MultiPolygon',
|
|
54590
|
+
coordinates: rings.length == 1 ? rings : rings.map(function(ring) {
|
|
54591
|
+
return [ring];
|
|
54592
|
+
})
|
|
54593
|
+
});
|
|
54594
|
+
dataset.layers[0].geometry_type = geometryType;
|
|
54595
|
+
dataset.info.crs = P;
|
|
54596
|
+
return dataset;
|
|
54597
|
+
}
|
|
54598
|
+
|
|
54599
|
+
// Narrow pre-projection gutters can leave zero-area rings after a rotated
|
|
54600
|
+
// world boundary is cleaned. Remove only rings that are negligible relative
|
|
54601
|
+
// to the main footprint; legitimate disconnected lobes are retained.
|
|
54602
|
+
function removeTinyFootprintRings(dataset) {
|
|
54603
|
+
var lyr = dataset.layers[0];
|
|
54604
|
+
var arcs = dataset.arcs;
|
|
54605
|
+
var maxArea = 0;
|
|
54606
|
+
lyr.shapes.forEach(function(shp) {
|
|
54607
|
+
(shp || []).forEach(function(path) {
|
|
54608
|
+
maxArea = Math.max(maxArea, Math.abs(geom.getPathArea(path, arcs)));
|
|
54609
|
+
});
|
|
54610
|
+
});
|
|
54611
|
+
if (maxArea === 0) return;
|
|
54612
|
+
editShapes(lyr.shapes, function(path) {
|
|
54613
|
+
if (Math.abs(geom.getPathArea(path, arcs)) <= maxArea * 1e-12) {
|
|
54614
|
+
return null;
|
|
54615
|
+
}
|
|
54616
|
+
});
|
|
54617
|
+
dissolveArcs(dataset);
|
|
54618
|
+
}
|
|
54619
|
+
|
|
54620
|
+
// Polygon clipping closes open interrupted-projection boundaries to form valid
|
|
54621
|
+
// rings. In Cahill-Keyes, some closures connect unrelated polar facet images.
|
|
54622
|
+
// Remove only long, nearly horizontal segments at the map's vertical extent.
|
|
54623
|
+
function removeOutlineExtremeConnectors(dataset) {
|
|
54624
|
+
var bounds = getDatasetBounds(dataset);
|
|
54625
|
+
var width = bounds.width();
|
|
54626
|
+
var height = bounds.height();
|
|
54627
|
+
var edgeTolerance = height * 1e-3;
|
|
54628
|
+
var flatTolerance = height * 1e-4;
|
|
54629
|
+
var arcs = dataset.arcs;
|
|
54630
|
+
var lyr = dataset.layers[0];
|
|
54631
|
+
var paths = [];
|
|
54632
|
+
var removed = 0;
|
|
54633
|
+
lyr.shapes.forEach(function(shp) {
|
|
54634
|
+
(shp || []).forEach(function(path) {
|
|
54635
|
+
var points = [];
|
|
54636
|
+
var iter = arcs.getShapeIter(path);
|
|
54637
|
+
while (iter.hasNext()) {
|
|
54638
|
+
points.push([iter.x, iter.y]);
|
|
54639
|
+
}
|
|
54640
|
+
var part = [points[0]];
|
|
54641
|
+
for (var i = 1; i < points.length; i++) {
|
|
54642
|
+
var a = points[i - 1];
|
|
54643
|
+
var b = points[i];
|
|
54644
|
+
var atTop = Math.min(a[1], b[1]) >= bounds.ymax - edgeTolerance;
|
|
54645
|
+
var atBottom = Math.max(a[1], b[1]) <= bounds.ymin + edgeTolerance;
|
|
54646
|
+
var isFlat = Math.abs(a[1] - b[1]) <= flatTolerance;
|
|
54647
|
+
var isWide = Math.abs(a[0] - b[0]) >= width * 0.02;
|
|
54648
|
+
if ((atTop || atBottom) && isFlat && isWide) {
|
|
54649
|
+
if (part.length > 1) paths.push(part);
|
|
54650
|
+
part = [b];
|
|
54651
|
+
removed++;
|
|
54652
|
+
} else {
|
|
54653
|
+
part.push(b);
|
|
54654
|
+
}
|
|
54655
|
+
}
|
|
54656
|
+
if (part.length > 1) paths.push(part);
|
|
54657
|
+
});
|
|
54658
|
+
});
|
|
54659
|
+
if (removed === 0) return dataset;
|
|
54660
|
+
var cleaned = importGeoJSON({
|
|
54661
|
+
type: paths.length == 1 ? 'LineString' : 'MultiLineString',
|
|
54662
|
+
coordinates: paths.length == 1 ? paths[0] : paths
|
|
54663
|
+
});
|
|
54664
|
+
cleaned.info = dataset.info;
|
|
54665
|
+
return cleaned;
|
|
54666
|
+
}
|
|
54667
|
+
|
|
52625
54668
|
function getBoundingRectangle(dest, opts) {
|
|
52626
54669
|
var bbox = opts.clip_bbox || getDefaultClipBBox(dest);
|
|
52627
54670
|
var rotation = getRotationParams(dest);
|
|
@@ -52735,6 +54778,7 @@ ${svg}
|
|
|
52735
54778
|
// rotated normal-aspect projections can generally have a thin slice removed
|
|
52736
54779
|
// from the rotated antimeridian, instead of clipping them
|
|
52737
54780
|
var cut = insertPreProjectionCuts(dataset, src, dest);
|
|
54781
|
+
cut = insertProjectionTopologyCuts(dataset, dest) || cut;
|
|
52738
54782
|
var clipped = false;
|
|
52739
54783
|
var clipData;
|
|
52740
54784
|
// experimental -- we can probably get away with just clamping some CRSs that
|
|
@@ -52795,6 +54839,84 @@ ${svg}
|
|
|
52795
54839
|
return false;
|
|
52796
54840
|
}
|
|
52797
54841
|
|
|
54842
|
+
function insertProjectionTopologyCuts(dataset, dest) {
|
|
54843
|
+
var topology = getProjectionTopology(dest);
|
|
54844
|
+
var pathLayers = dataset.layers.filter(layerHasPaths);
|
|
54845
|
+
var cutPaths, geojson, clip;
|
|
54846
|
+
if (!topology || pathLayers.length === 0) return false;
|
|
54847
|
+
insertProjectionTopologyVertices(dataset, topology);
|
|
54848
|
+
cutPaths = topology.seams
|
|
54849
|
+
.filter(function(o) { return o.type == 'cut'; })
|
|
54850
|
+
.reduce(function(memo, o) {
|
|
54851
|
+
if (o.paths) return memo.concat(o.paths);
|
|
54852
|
+
return memo.concat(getCutSeamPaths(o.coordinates));
|
|
54853
|
+
}, []);
|
|
54854
|
+
if (cutPaths.length === 0) return false;
|
|
54855
|
+
geojson = getCutMaskGeoJSON(cutPaths);
|
|
54856
|
+
if (geojson.features.length === 0) return false;
|
|
54857
|
+
clip = importGeoJSON(geojson);
|
|
54858
|
+
if (topology.findRegion) {
|
|
54859
|
+
// Polyhedral cut masks meet and overlap at facet vertices. Union them before
|
|
54860
|
+
// clipping so overlapping source polygons are not interpreted as holes.
|
|
54861
|
+
addIntersectionCuts(clip, {});
|
|
54862
|
+
clip.layers[0] = dissolvePolygonLayer2(clip.layers[0], clip, {quiet: true});
|
|
54863
|
+
}
|
|
54864
|
+
clipLayersInPlace(pathLayers, clip, dataset, 'erase', getInternalClipOpts());
|
|
54865
|
+
return true;
|
|
54866
|
+
}
|
|
54867
|
+
|
|
54868
|
+
// Add a vertex wherever a path changes projection regions. This preserves a
|
|
54869
|
+
// bend at attached polyhedral edges without opening a gap; cut edges are
|
|
54870
|
+
// subsequently disconnected by the narrow seam masks below.
|
|
54871
|
+
function insertProjectionTopologyVertices(dataset, topology) {
|
|
54872
|
+
if (!dataset.arcs || !topology.findRegion) return;
|
|
54873
|
+
var findRegion = topology.findTransitionRegion || topology.findRegion;
|
|
54874
|
+
editArcs(dataset.arcs, function(append, x, y, prevX, prevY, i) {
|
|
54875
|
+
if (i > 0) {
|
|
54876
|
+
findRegionTransitions(prevX, prevY, x, y, findRegion).forEach(append);
|
|
54877
|
+
}
|
|
54878
|
+
append([x, y]);
|
|
54879
|
+
});
|
|
54880
|
+
}
|
|
54881
|
+
|
|
54882
|
+
function findRegionTransitions(ax, ay, bx, by, findRegion) {
|
|
54883
|
+
var interval = 0.5;
|
|
54884
|
+
var n = Math.max(1, Math.ceil(Math.max(Math.abs(bx - ax), Math.abs(by - ay)) / interval));
|
|
54885
|
+
var points = [];
|
|
54886
|
+
var t0 = 0;
|
|
54887
|
+
var region0 = findRegion(ax, ay);
|
|
54888
|
+
for (var i = 1; i <= n; i++) {
|
|
54889
|
+
var t1 = i / n;
|
|
54890
|
+
var x1 = ax + (bx - ax) * t1;
|
|
54891
|
+
var y1 = ay + (by - ay) * t1;
|
|
54892
|
+
var region1 = findRegion(x1, y1);
|
|
54893
|
+
if (region0 != region1 && region0 >= 0 && region1 >= 0) {
|
|
54894
|
+
var t = findRegionBoundary(ax, ay, bx, by, t0, region0, t1, findRegion);
|
|
54895
|
+
var p = [ax + (bx - ax) * t, ay + (by - ay) * t];
|
|
54896
|
+
if (!points.length || Math.abs(t - points[points.length - 1].t) > 1e-10) {
|
|
54897
|
+
p.t = t;
|
|
54898
|
+
points.push(p);
|
|
54899
|
+
}
|
|
54900
|
+
}
|
|
54901
|
+
t0 = t1;
|
|
54902
|
+
region0 = region1;
|
|
54903
|
+
}
|
|
54904
|
+
return points;
|
|
54905
|
+
}
|
|
54906
|
+
|
|
54907
|
+
function findRegionBoundary(ax, ay, bx, by, ta, regionA, tb, findRegion) {
|
|
54908
|
+
for (var i = 0; i < 45; i++) {
|
|
54909
|
+
var tm = (ta + tb) / 2;
|
|
54910
|
+
var regionM = findRegion(ax + (bx - ax) * tm, ay + (by - ay) * tm);
|
|
54911
|
+
if (regionM == regionA) {
|
|
54912
|
+
ta = tm;
|
|
54913
|
+
} else {
|
|
54914
|
+
tb = tm;
|
|
54915
|
+
}
|
|
54916
|
+
}
|
|
54917
|
+
return (ta + tb) / 2;
|
|
54918
|
+
}
|
|
54919
|
+
|
|
52798
54920
|
function clampDataset(dataset, bbox) {
|
|
52799
54921
|
transformPoints(dataset, function(x, y) {
|
|
52800
54922
|
return [utils.clamp(x, bbox[0], bbox[2]), utils.clamp(y, bbox[1], bbox[3])];
|
|
@@ -52830,6 +54952,92 @@ ${svg}
|
|
|
52830
54952
|
clipLayersInPlace(pathLayers, clip, dataset, 'erase', getInternalClipOpts());
|
|
52831
54953
|
}
|
|
52832
54954
|
|
|
54955
|
+
// Duplicate a seam on the opposite edge of the standard longitude range.
|
|
54956
|
+
// This is needed when a rotated seam coincides with the antimeridian.
|
|
54957
|
+
function getCutSeamPaths(coords) {
|
|
54958
|
+
var paths = [coords];
|
|
54959
|
+
var onEdge = coords.every(function(p) {
|
|
54960
|
+
return Math.abs(Math.abs(p[0]) - 180) < 1e-12;
|
|
54961
|
+
});
|
|
54962
|
+
if (onEdge) {
|
|
54963
|
+
paths.push(coords.map(function(p) {
|
|
54964
|
+
return [p[0] == 180 ? -180 : 180, p[1]];
|
|
54965
|
+
}));
|
|
54966
|
+
}
|
|
54967
|
+
return paths;
|
|
54968
|
+
}
|
|
54969
|
+
|
|
54970
|
+
// Convert seam paths to narrow polygon masks. Densification is required so
|
|
54971
|
+
// their projected edges follow the projection instead of becoming straight
|
|
54972
|
+
// chords between seam endpoints.
|
|
54973
|
+
function getCutMaskGeoJSON(paths) {
|
|
54974
|
+
var features = paths.map(function(coords) {
|
|
54975
|
+
var e = coords.mask_width || 1e-8;
|
|
54976
|
+
var ring = getCutMaskRing(densifyPathByInterval(coords, 0.5), e);
|
|
54977
|
+
return {
|
|
54978
|
+
type: 'Feature',
|
|
54979
|
+
properties: {},
|
|
54980
|
+
geometry: {
|
|
54981
|
+
type: 'Polygon',
|
|
54982
|
+
coordinates: [ring]
|
|
54983
|
+
}
|
|
54984
|
+
};
|
|
54985
|
+
}).filter(function(feature) {
|
|
54986
|
+
return feature.geometry.coordinates[0].length > 3;
|
|
54987
|
+
});
|
|
54988
|
+
// Give finite seams a small end cap. Without this, a path passing exactly
|
|
54989
|
+
// through a polyhedral vertex can touch only the boundary of several masks
|
|
54990
|
+
// and remain connected across an interruption.
|
|
54991
|
+
paths.forEach(function(coords) {
|
|
54992
|
+
var endpoints = [coords[0], coords[coords.length - 1]];
|
|
54993
|
+
endpoints.forEach(function(p) {
|
|
54994
|
+
var e2 = Math.max(1e-7, (coords.mask_width || 0) * 2);
|
|
54995
|
+
var xmin = Math.max(-180, p[0] - e2);
|
|
54996
|
+
var xmax = Math.min(180, p[0] + e2);
|
|
54997
|
+
var ymin = Math.max(-90, p[1] - e2);
|
|
54998
|
+
var ymax = Math.min(90, p[1] + e2);
|
|
54999
|
+
features.push({
|
|
55000
|
+
type: 'Feature',
|
|
55001
|
+
properties: {},
|
|
55002
|
+
geometry: {
|
|
55003
|
+
type: 'Polygon',
|
|
55004
|
+
coordinates: [[
|
|
55005
|
+
[xmin, ymin],
|
|
55006
|
+
[xmax, ymin],
|
|
55007
|
+
[xmax, ymax],
|
|
55008
|
+
[xmin, ymax],
|
|
55009
|
+
[xmin, ymin]
|
|
55010
|
+
]]
|
|
55011
|
+
}
|
|
55012
|
+
});
|
|
55013
|
+
});
|
|
55014
|
+
});
|
|
55015
|
+
return {
|
|
55016
|
+
type: 'FeatureCollection',
|
|
55017
|
+
features: features
|
|
55018
|
+
};
|
|
55019
|
+
}
|
|
55020
|
+
|
|
55021
|
+
function getCutMaskRing(coords, width) {
|
|
55022
|
+
var left = [];
|
|
55023
|
+
var right = [];
|
|
55024
|
+
for (var i = 0; i < coords.length; i++) {
|
|
55025
|
+
var a = coords[i > 0 ? i - 1 : i];
|
|
55026
|
+
var b = coords[i < coords.length - 1 ? i + 1 : i];
|
|
55027
|
+
var dx = b[0] - a[0];
|
|
55028
|
+
var dy = b[1] - a[1];
|
|
55029
|
+
var len = Math.sqrt(dx * dx + dy * dy);
|
|
55030
|
+
if (len === 0) continue;
|
|
55031
|
+
var nx = -dy / len * width;
|
|
55032
|
+
var ny = dx / len * width;
|
|
55033
|
+
left.push([utils.clamp(coords[i][0] + nx, -180, 180), coords[i][1] + ny]);
|
|
55034
|
+
right.push([utils.clamp(coords[i][0] - nx, -180, 180), coords[i][1] - ny]);
|
|
55035
|
+
}
|
|
55036
|
+
var ring = left.concat(right.reverse());
|
|
55037
|
+
if (ring.length > 0) ring.push(ring[0].concat());
|
|
55038
|
+
return ring;
|
|
55039
|
+
}
|
|
55040
|
+
|
|
52833
55041
|
function getInternalClipOpts() {
|
|
52834
55042
|
return {no_cleanup: true, no_warn: true};
|
|
52835
55043
|
}
|
|
@@ -53699,9 +55907,18 @@ ${svg}
|
|
|
53699
55907
|
}
|
|
53700
55908
|
|
|
53701
55909
|
function projectDataset(dataset, src, dest, opts) {
|
|
55910
|
+
if (isProjectedCRS(src) && !isInvertibleCRS(src)) {
|
|
55911
|
+
stop$1('Unable to project from a coordinate system that has no inverse transform');
|
|
55912
|
+
}
|
|
55913
|
+
if (isInterruptedProjection(dest) && dataset.layers.some(layerHasRaster)) {
|
|
55914
|
+
stop$1('Raster reprojection is not currently supported for interrupted projections');
|
|
55915
|
+
}
|
|
53702
55916
|
var proj = getProjTransform2(src, dest); // v2 returns null points instead of throwing an error
|
|
53703
55917
|
var badArcs = 0;
|
|
53704
55918
|
var badPoints = 0;
|
|
55919
|
+
var healAntimeridian = isLatLngCRS(src) &&
|
|
55920
|
+
isInterruptedProjection(dest) &&
|
|
55921
|
+
Math.abs(dest.lam0) > 1e-12;
|
|
53705
55922
|
var clipped = preProjectionClip(dataset, src, dest, opts);
|
|
53706
55923
|
dataset.layers.forEach(function(lyr) {
|
|
53707
55924
|
if (layerHasPoints(lyr)) {
|
|
@@ -53721,7 +55938,7 @@ ${svg}
|
|
|
53721
55938
|
if (clipped) {
|
|
53722
55939
|
// TODO: could more selective in cleaning clipped layers
|
|
53723
55940
|
// (probably only needed when clipped area crosses the antimeridian or includes a pole)
|
|
53724
|
-
cleanProjectedPathLayers(dataset);
|
|
55941
|
+
cleanProjectedPathLayers(dataset, {heal_antimeridian: healAntimeridian});
|
|
53725
55942
|
}
|
|
53726
55943
|
|
|
53727
55944
|
if (badArcs > 0 && !opts.quiet) {
|
|
@@ -53749,9 +55966,21 @@ ${svg}
|
|
|
53749
55966
|
// * Removes line intersections
|
|
53750
55967
|
// * TODO: what if a layer contains polygons with desired overlaps? should
|
|
53751
55968
|
// we ignore overlaps between different features?
|
|
53752
|
-
function cleanProjectedPathLayers(dataset) {
|
|
55969
|
+
function cleanProjectedPathLayers(dataset, opts) {
|
|
55970
|
+
opts = opts || {};
|
|
53753
55971
|
// TODO: only clean affected polygons (cleaning all polygons can be slow)
|
|
53754
55972
|
var polygonLayers = dataset.layers.filter(lyr => lyr.geometry_type == 'polygon');
|
|
55973
|
+
if (opts.heal_antimeridian && dataset.arcs) {
|
|
55974
|
+
// A rotated projection moves the source +/-180 degree boundary into the
|
|
55975
|
+
// map interior. Mathematically identical boundary vertices can diverge by
|
|
55976
|
+
// a few floating-point units during projection, preventing topology from
|
|
55977
|
+
// joining polygon parts that were split at the source antimeridian.
|
|
55978
|
+
var arcs = dataset.arcs;
|
|
55979
|
+
var snapDist = getHighPrecisionSnapInterval(arcs.getBounds().toArray());
|
|
55980
|
+
if (snapCoordsByInterval(arcs, snapDist) > 0) {
|
|
55981
|
+
arcs.dedupCoords();
|
|
55982
|
+
}
|
|
55983
|
+
}
|
|
53755
55984
|
// clean options: force a topology update (by default, this only happens when
|
|
53756
55985
|
// vertices change during cleaning, but reprojection can require a topology update
|
|
53757
55986
|
// even if clean does not change vertices)
|
|
@@ -57212,7 +59441,7 @@ ${svg}
|
|
|
57212
59441
|
function getSplitLineFunction(crs, dashLen, gapLen, opts) {
|
|
57213
59442
|
var planar = !!opts.planar;
|
|
57214
59443
|
var interpolate = getInterpolationFunction(planar ? null : crs);
|
|
57215
|
-
var distance = isLatLngCRS(crs) ? greatCircleDistance : distance2D;
|
|
59444
|
+
var distance = isLatLngCRS(crs) ? greatCircleDistance : distance2D$1;
|
|
57216
59445
|
var inDash, parts2, interval, scale;
|
|
57217
59446
|
function addPart(coords) {
|
|
57218
59447
|
if (inDash) parts2.push(coords);
|
|
@@ -58083,7 +60312,7 @@ ${svg}
|
|
|
58083
60312
|
// Isometric ("Mercator-stretched") latitude in degrees-in; returns +/-Infinity at
|
|
58084
60313
|
// the poles.
|
|
58085
60314
|
function isometricLatitude(latDeg) {
|
|
58086
|
-
return Math.log(Math.tan(Math.PI / 4 + latDeg * D2R$
|
|
60315
|
+
return Math.log(Math.tan(Math.PI / 4 + latDeg * D2R$6 / 2));
|
|
58087
60316
|
}
|
|
58088
60317
|
|
|
58089
60318
|
// Rhumb-line (loxodrome) interpolation, fraction k of the way from A to B.
|
|
@@ -58108,9 +60337,9 @@ ${svg}
|
|
|
58108
60337
|
// Rhumb-line distance in meters, using the raw longitude delta (so a full-parallel
|
|
58109
60338
|
// sweep returns the parallel's length rather than 0).
|
|
58110
60339
|
function rhumbDistance(a, b) {
|
|
58111
|
-
var phi1 = a[1] * D2R$
|
|
60340
|
+
var phi1 = a[1] * D2R$6, phi2 = b[1] * D2R$6;
|
|
58112
60341
|
var dPhi = phi2 - phi1;
|
|
58113
|
-
var dLambda = (b[0] - a[0]) * D2R$
|
|
60342
|
+
var dLambda = (b[0] - a[0]) * D2R$6; // raw
|
|
58114
60343
|
var dPsi = Math.log(Math.tan(Math.PI / 4 + phi2 / 2) / Math.tan(Math.PI / 4 + phi1 / 2));
|
|
58115
60344
|
var q = Math.abs(dPsi) > 1e-12 ? dPhi / dPsi : Math.cos(phi1);
|
|
58116
60345
|
return Math.sqrt(dPhi * dPhi + q * q * dLambda * dLambda) * R$3;
|
|
@@ -61765,22 +63994,31 @@ ${svg}
|
|
|
61765
63994
|
}
|
|
61766
63995
|
|
|
61767
63996
|
cmd.graticule = function(dataset, opts) {
|
|
61768
|
-
|
|
63997
|
+
if (opts.polygon && opts.outline) {
|
|
63998
|
+
stop$1('The polygon and outline options cannot be used together');
|
|
63999
|
+
}
|
|
64000
|
+
var boundary = opts.polygon || opts.outline;
|
|
64001
|
+
var name = opts.name || opts.outline && 'outline' || opts.polygon && 'polygon' || 'graticule';
|
|
61769
64002
|
var graticule, destInfo;
|
|
61770
64003
|
if (dataset && !isLatLngDataset(dataset)) {
|
|
61771
64004
|
// project graticule to match dataset
|
|
61772
64005
|
destInfo = getDatasetCrsInfo(dataset);
|
|
61773
64006
|
if (!destInfo.crs) stop$1("Coordinate system is unknown, unable to create a graticule");
|
|
61774
|
-
graticule = opts.
|
|
61775
|
-
|
|
61776
|
-
|
|
64007
|
+
graticule = opts.outline ?
|
|
64008
|
+
createProjectedOutline(destInfo.crs, opts) :
|
|
64009
|
+
opts.polygon ?
|
|
64010
|
+
createProjectedPolygon(destInfo.crs, opts) :
|
|
64011
|
+
createProjectedGraticule(destInfo.crs, opts);
|
|
61777
64012
|
setDatasetCrsInfo(graticule, destInfo);
|
|
61778
64013
|
} else {
|
|
61779
|
-
graticule =
|
|
64014
|
+
graticule = boundary ?
|
|
61780
64015
|
createUnprojectedPolygon(opts) :
|
|
61781
64016
|
createUnprojectedGraticule(opts);
|
|
61782
64017
|
setDatasetCrsInfo(graticule, getCrsInfo('wgs84'));
|
|
61783
64018
|
}
|
|
64019
|
+
if (opts.outline) {
|
|
64020
|
+
graticule.layers[0].geometry_type = 'polyline';
|
|
64021
|
+
}
|
|
61784
64022
|
graticule.layers[0].name = name;
|
|
61785
64023
|
return graticule;
|
|
61786
64024
|
};
|
|
@@ -61795,6 +64033,12 @@ ${svg}
|
|
|
61795
64033
|
return getPolygonDataset$1(src, dest, opts);
|
|
61796
64034
|
}
|
|
61797
64035
|
|
|
64036
|
+
function createProjectedOutline(dest, opts) {
|
|
64037
|
+
var src = parseCrsString$1('wgs84');
|
|
64038
|
+
return getOutlineDataset(src, dest, opts) ||
|
|
64039
|
+
getPolygonDataset$1(src, dest, opts);
|
|
64040
|
+
}
|
|
64041
|
+
|
|
61798
64042
|
function createUnprojectedGraticule(opts) {
|
|
61799
64043
|
var src = parseCrsString$1('wgs84');
|
|
61800
64044
|
var graticule = importGeoJSON(createGraticule(src, false, opts));
|
|
@@ -61876,12 +64120,15 @@ ${svg}
|
|
|
61876
64120
|
// extended: meridian extends to pole
|
|
61877
64121
|
function createMeridian(x, extended) {
|
|
61878
64122
|
var y0 = ystep <= 15 ? ystep : 0;
|
|
64123
|
+
// An exact pole can have several images in a polyhedral projection.
|
|
64124
|
+
// The separate outline supplies the missing, negligible end interval.
|
|
64125
|
+
var poleInset = outlined ? 1e-4 : 0;
|
|
61879
64126
|
createMeridianPart(x, -90 + y0, 90 - y0);
|
|
61880
64127
|
if (extended && y0 > 0) {
|
|
61881
64128
|
// adding extensions as separate parts, so if the polar coordinates
|
|
61882
64129
|
// fail to project, at least the rest of the meridian line will remain
|
|
61883
|
-
createMeridianPart(x, -90, -90 + y0);
|
|
61884
|
-
createMeridianPart(x, 90 - y0, 90);
|
|
64130
|
+
createMeridianPart(x, -90 + poleInset, -90 + y0);
|
|
64131
|
+
createMeridianPart(x, 90 - y0, 90 - poleInset);
|
|
61885
64132
|
}
|
|
61886
64133
|
}
|
|
61887
64134
|
|
|
@@ -61891,7 +64138,14 @@ ${svg}
|
|
|
61891
64138
|
}
|
|
61892
64139
|
|
|
61893
64140
|
function createParallel(y) {
|
|
61894
|
-
|
|
64141
|
+
// Avoid joining distinct images of the antimeridian (and the degenerate
|
|
64142
|
+
// polar parallels) when a projected outline is generated separately.
|
|
64143
|
+
var edgeInset = outlined ? 1e-4 : 0;
|
|
64144
|
+
if (outlined && Math.abs(y) == 90) return;
|
|
64145
|
+
var coords = densifyPathByInterval([
|
|
64146
|
+
[-180 + edgeInset, y],
|
|
64147
|
+
[180 - edgeInset, y]
|
|
64148
|
+
], precision);
|
|
61895
64149
|
parallels.push(graticuleFeature(coords, {type: 'parallel', value: y}));
|
|
61896
64150
|
}
|
|
61897
64151
|
}
|
|
@@ -62490,7 +64744,7 @@ ${svg}
|
|
|
62490
64744
|
if (halfLen > 0 === false) {
|
|
62491
64745
|
return [a, b];
|
|
62492
64746
|
}
|
|
62493
|
-
var segLen = distance2D(a, b, c, d);
|
|
64747
|
+
var segLen = distance2D$1(a, b, c, d);
|
|
62494
64748
|
var k;
|
|
62495
64749
|
if (partialLen + segLen >= halfLen) {
|
|
62496
64750
|
k = (halfLen - partialLen) / segLen;
|
|
@@ -64411,7 +66665,7 @@ ${svg}
|
|
|
64411
66665
|
|
|
64412
66666
|
// Source: https://diego.assencio.com/?index=8d6ca3d82151bad815f78addf9b5c1c6
|
|
64413
66667
|
function twoCircleIntersection(c1, r1, c2, r2) {
|
|
64414
|
-
var d = distance2D(c1[0], c1[1], c2[0], c2[1]);
|
|
66668
|
+
var d = distance2D$1(c1[0], c1[1], c2[0], c2[1]);
|
|
64415
66669
|
if (d >= r1 + r2) return 0;
|
|
64416
66670
|
var r1sq = r1 * r1,
|
|
64417
66671
|
r2sq = r2 * r2,
|
|
@@ -66406,7 +68660,7 @@ ${svg}
|
|
|
66406
68660
|
if (totalSegs < 1) return 0;
|
|
66407
68661
|
var MAX_SAMPLES = 100000;
|
|
66408
68662
|
var stride = Math.ceil(totalSegs / MAX_SAMPLES);
|
|
66409
|
-
var distFn = spherical ? greatCircleDistance : distance2D;
|
|
68663
|
+
var distFn = spherical ? greatCircleDistance : distance2D$1;
|
|
66410
68664
|
var lens = [];
|
|
66411
68665
|
var counter = 0;
|
|
66412
68666
|
arcs.forEach3(function(xx, yy) {
|
|
@@ -67735,6 +69989,28 @@ ${svg}
|
|
|
67735
69989
|
name == 'comment' || name == 'rename-layers';
|
|
67736
69990
|
}
|
|
67737
69991
|
|
|
69992
|
+
// Commands in this group keep datasets separate and run once for each grouped
|
|
69993
|
+
// target returned by findCommandTargets(). Commands that consume all targets
|
|
69994
|
+
// together (e.g. -frame) or merge targets (-union, -merge-layers) are
|
|
69995
|
+
// intentionally excluded.
|
|
69996
|
+
function commandRunsPerTargetDataset(name, opts) {
|
|
69997
|
+
if (name == 'rectangle') {
|
|
69998
|
+
return !opts.source && !opts.bbox;
|
|
69999
|
+
}
|
|
70000
|
+
return [
|
|
70001
|
+
'affine', 'alpha-shapes', 'blur', 'buffer', 'calc', 'check-geometry',
|
|
70002
|
+
'classify', 'clean', 'clip', 'cluster', 'dashlines', 'data-fill',
|
|
70003
|
+
'densify', 'dissolve', 'dissolve2', 'divide', 'dots', 'each', 'erase',
|
|
70004
|
+
'explode', 'filter',
|
|
70005
|
+
'filter-detail', 'filter-fields', 'filter-geom', 'filter-islands',
|
|
70006
|
+
'filter-islands2', 'filter-points', 'filter-slivers', 'grid', 'grid2',
|
|
70007
|
+
'fuzzy-join', 'ignore', 'inlay', 'innerlines', 'inspect', 'join',
|
|
70008
|
+
'lines', 'mosaic', 'points', 'polygons', 'rectangles', 'rename-fields',
|
|
70009
|
+
'shapes', 'simplify', 'slice', 'smooth', 'sort', 'split',
|
|
70010
|
+
'split-on-grid', 'stitch', 'style', 'subdivide', 'symbols', 'uniq'
|
|
70011
|
+
].indexOf(name) > -1;
|
|
70012
|
+
}
|
|
70013
|
+
|
|
67738
70014
|
function commandAcceptsEmptyTarget(name) {
|
|
67739
70015
|
return name == 'graticule' || name == 'i' || name == 'help' ||
|
|
67740
70016
|
name == 'point-grid' || name == 'shape' || name == 'rectangle' || name == 'frame' ||
|
|
@@ -67766,13 +70042,32 @@ ${svg}
|
|
|
67766
70042
|
}
|
|
67767
70043
|
|
|
67768
70044
|
if (!job) job = new Job();
|
|
70045
|
+
|
|
70046
|
+
// Preserve the existing single-dataset dispatch and postprocessing paths by
|
|
70047
|
+
// invoking them once per dataset. Collect the resulting targets so a
|
|
70048
|
+
// multi-dataset command does not leave only its final dataset selected.
|
|
70049
|
+
if (!command._targetOverride && commandRunsPerTargetDataset(name, opts)) {
|
|
70050
|
+
targets = job.catalog.findCommandTargets(opts.target);
|
|
70051
|
+
if (targets.length > 1) {
|
|
70052
|
+
validatePerDatasetTargets(name, targets, opts);
|
|
70053
|
+
return runCommandOnEachTarget(command, job, targets);
|
|
70054
|
+
}
|
|
70055
|
+
}
|
|
70056
|
+
|
|
67769
70057
|
job.startCommand(command);
|
|
67770
70058
|
|
|
67771
70059
|
|
|
67772
70060
|
try { // catch errors from synchronous functions
|
|
67773
70061
|
T$1.start();
|
|
67774
70062
|
|
|
67775
|
-
if (
|
|
70063
|
+
if (command._targetOverride) {
|
|
70064
|
+
targets = [command._targetOverride];
|
|
70065
|
+
targetDataset = targets[0].dataset;
|
|
70066
|
+
arcs = targetDataset.arcs;
|
|
70067
|
+
targetLayers = targets[0].layers;
|
|
70068
|
+
job.catalog.setDefaultTarget(targetLayers, targetDataset);
|
|
70069
|
+
|
|
70070
|
+
} else if (name == 'rename-layers') {
|
|
67776
70071
|
targets = job.catalog.findCommandTargets(opts.target);
|
|
67777
70072
|
targetLayers = targets.reduce(function(memo, obj) {
|
|
67778
70073
|
return memo.concat(obj.layers);
|
|
@@ -67816,7 +70111,9 @@ ${svg}
|
|
|
67816
70111
|
}
|
|
67817
70112
|
}
|
|
67818
70113
|
|
|
67819
|
-
if (
|
|
70114
|
+
if (Object.prototype.hasOwnProperty.call(command, '_sourceOverride')) {
|
|
70115
|
+
source = command._sourceOverride;
|
|
70116
|
+
} else if (opts.source) {
|
|
67820
70117
|
source = findCommandSource(convertSourceName(opts.source, targets), job.catalog, opts);
|
|
67821
70118
|
}
|
|
67822
70119
|
|
|
@@ -68214,6 +70511,141 @@ ${svg}
|
|
|
68214
70511
|
}
|
|
68215
70512
|
}
|
|
68216
70513
|
|
|
70514
|
+
async function runCommandOnEachTarget(command, job, targets) {
|
|
70515
|
+
var outputTargets = [];
|
|
70516
|
+
var source = null;
|
|
70517
|
+
var items = targets.map(function(target, i) {
|
|
70518
|
+
return {target: target, index: i};
|
|
70519
|
+
});
|
|
70520
|
+
var disposableSourceTargetIndex = -1;
|
|
70521
|
+
|
|
70522
|
+
if (command.options.source && !sourceNameIsInterpolated(command.options.source)) {
|
|
70523
|
+
source = resolveSourceForTargets(command, job, targets);
|
|
70524
|
+
items = moveSourceTargetLast(items, source);
|
|
70525
|
+
disposableSourceTargetIndex = findLastDisposableSourceTarget(
|
|
70526
|
+
items,
|
|
70527
|
+
source,
|
|
70528
|
+
command.name,
|
|
70529
|
+
command.options
|
|
70530
|
+
);
|
|
70531
|
+
}
|
|
70532
|
+
|
|
70533
|
+
for (var i = 0; i < items.length; i++) {
|
|
70534
|
+
var item = items[i];
|
|
70535
|
+
var target = {
|
|
70536
|
+
dataset: item.target.dataset,
|
|
70537
|
+
layers: item.target.layers.concat()
|
|
70538
|
+
};
|
|
70539
|
+
var command2 = Object.assign({}, command, {_targetOverride: target});
|
|
70540
|
+
if (source) {
|
|
70541
|
+
command2._sourceOverride = getSourceForTarget(
|
|
70542
|
+
source,
|
|
70543
|
+
target,
|
|
70544
|
+
command.name,
|
|
70545
|
+
item.index == disposableSourceTargetIndex,
|
|
70546
|
+
command.options
|
|
70547
|
+
);
|
|
70548
|
+
}
|
|
70549
|
+
await runCommand(command2, job);
|
|
70550
|
+
outputTargets[item.index] = copyCommandTargets(job.catalog.getDefaultTargets());
|
|
70551
|
+
}
|
|
70552
|
+
job.catalog.setDefaultTargets(outputTargets.reduce(function(memo, arr) {
|
|
70553
|
+
return memo.concat(arr);
|
|
70554
|
+
}, []));
|
|
70555
|
+
return job;
|
|
70556
|
+
}
|
|
70557
|
+
|
|
70558
|
+
function resolveSourceForTargets(command, job, targets) {
|
|
70559
|
+
job.startCommand(command);
|
|
70560
|
+
try {
|
|
70561
|
+
return findCommandSource(
|
|
70562
|
+
convertSourceName(command.options.source, targets),
|
|
70563
|
+
job.catalog,
|
|
70564
|
+
command.options
|
|
70565
|
+
);
|
|
70566
|
+
} finally {
|
|
70567
|
+
job.endCommand();
|
|
70568
|
+
}
|
|
70569
|
+
}
|
|
70570
|
+
|
|
70571
|
+
function sourceNameIsInterpolated(name) {
|
|
70572
|
+
return /[$][{]/.test(name);
|
|
70573
|
+
}
|
|
70574
|
+
|
|
70575
|
+
// If a retained catalog source is also targeted, process its group last so
|
|
70576
|
+
// earlier groups always read the original source. Output target ordering is
|
|
70577
|
+
// restored after execution.
|
|
70578
|
+
function moveSourceTargetLast(items, source) {
|
|
70579
|
+
if (!source || source.disposable) return items;
|
|
70580
|
+
return items.filter(function(item) {
|
|
70581
|
+
return item.target.layers.indexOf(source.layer) == -1;
|
|
70582
|
+
}).concat(items.filter(function(item) {
|
|
70583
|
+
return item.target.layers.indexOf(source.layer) > -1;
|
|
70584
|
+
}));
|
|
70585
|
+
}
|
|
70586
|
+
|
|
70587
|
+
function commandMutatesDisposableSource(name, target, source, opts) {
|
|
70588
|
+
var overlayCommand = (name == 'clip' || name == 'erase' || name == 'slice' ||
|
|
70589
|
+
name == 'inlay' || name == 'divide') &&
|
|
70590
|
+
utils.some(target.layers, layerHasPaths);
|
|
70591
|
+
var polygonMosaicJoin = name == 'join' && !opts.keys && !opts.point_method &&
|
|
70592
|
+
source.layer.geometry_type == 'polygon' &&
|
|
70593
|
+
target.layers.some(function(lyr) { return lyr.geometry_type == 'polygon'; });
|
|
70594
|
+
return overlayCommand || polygonMosaicJoin;
|
|
70595
|
+
}
|
|
70596
|
+
|
|
70597
|
+
// A file source is disposable, so the final topology-changing target can use it
|
|
70598
|
+
// directly. Earlier topology-changing targets receive only a shape copy; the
|
|
70599
|
+
// potentially large source ArcCollection is safely shared.
|
|
70600
|
+
function findLastDisposableSourceTarget(items, source, name, opts) {
|
|
70601
|
+
if (!source?.disposable) return -1;
|
|
70602
|
+
for (var i = items.length - 1; i >= 0; i--) {
|
|
70603
|
+
if (commandMutatesDisposableSource(name, items[i].target, source, opts)) {
|
|
70604
|
+
return items[i].index;
|
|
70605
|
+
}
|
|
70606
|
+
}
|
|
70607
|
+
return -1;
|
|
70608
|
+
}
|
|
70609
|
+
|
|
70610
|
+
function getSourceForTarget(source, target, name, useOriginal, opts) {
|
|
70611
|
+
if (!source.disposable ||
|
|
70612
|
+
!commandMutatesDisposableSource(name, target, source, opts) ||
|
|
70613
|
+
useOriginal) {
|
|
70614
|
+
return source;
|
|
70615
|
+
}
|
|
70616
|
+
var layer = copyLayerShapes(source.layer);
|
|
70617
|
+
var dataset = Object.assign({}, source.dataset, {layers: [layer]});
|
|
70618
|
+
return {dataset: dataset, layer: layer, disposable: true};
|
|
70619
|
+
}
|
|
70620
|
+
|
|
70621
|
+
function copyCommandTargets(targets) {
|
|
70622
|
+
return targets.map(function(target) {
|
|
70623
|
+
return {
|
|
70624
|
+
dataset: target.dataset,
|
|
70625
|
+
layers: target.layers.concat()
|
|
70626
|
+
};
|
|
70627
|
+
});
|
|
70628
|
+
}
|
|
70629
|
+
|
|
70630
|
+
// Fail cardinality checks before the first dataset is modified.
|
|
70631
|
+
function validatePerDatasetTargets(name, targets, opts) {
|
|
70632
|
+
if (opts.source && sourceNameIsInterpolated(opts.source) && targets.some(function(target) {
|
|
70633
|
+
return target.layers.length != 1;
|
|
70634
|
+
})) {
|
|
70635
|
+
stop$1('Interpolated names are not compatible with multiple targets.');
|
|
70636
|
+
}
|
|
70637
|
+
if (name == 'mosaic' && targets.some(function(target) {
|
|
70638
|
+
return target.layers.length != 1;
|
|
70639
|
+
})) {
|
|
70640
|
+
stop$1('Command takes a single target layer from each dataset');
|
|
70641
|
+
}
|
|
70642
|
+
if (name == 'simplify' && opts.variable && targets.some(function(target) {
|
|
70643
|
+
return target.layers.length != 1;
|
|
70644
|
+
})) {
|
|
70645
|
+
stop$1('Variable simplification requires a single target layer from each dataset');
|
|
70646
|
+
}
|
|
70647
|
+
}
|
|
70648
|
+
|
|
68217
70649
|
function outputLayersAreDifferent(output, input) {
|
|
68218
70650
|
return !utils.some(input, function(lyr) {
|
|
68219
70651
|
return output.indexOf(lyr) > -1;
|
|
@@ -68228,7 +70660,7 @@ ${svg}
|
|
|
68228
70660
|
return name == 'rectangle' || name == 'rectangles' || name == 'filter' && opts.cleanup;
|
|
68229
70661
|
}
|
|
68230
70662
|
|
|
68231
|
-
var version = "0.7.
|
|
70663
|
+
var version = "0.7.45";
|
|
68232
70664
|
|
|
68233
70665
|
// Parse command line args into commands and run them
|
|
68234
70666
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|
|
@@ -70092,6 +72524,8 @@ ${svg}
|
|
|
70092
72524
|
ClipPoints,
|
|
70093
72525
|
Colorizer,
|
|
70094
72526
|
CustomProjections,
|
|
72527
|
+
Dymaxion,
|
|
72528
|
+
ButterflyProjections,
|
|
70095
72529
|
DataAggregation,
|
|
70096
72530
|
DatasetUtils,
|
|
70097
72531
|
DataUtils,
|
|
@@ -70168,6 +72602,7 @@ ${svg}
|
|
|
70168
72602
|
Proj,
|
|
70169
72603
|
Projections,
|
|
70170
72604
|
ProjectionParams,
|
|
72605
|
+
ProjectionTopology,
|
|
70171
72606
|
Rectangle,
|
|
70172
72607
|
RectangleUtils,
|
|
70173
72608
|
Rounding,
|