mapshaper 0.7.44 → 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 +2062 -60
- package/package.json +1 -1
- package/www/mapshaper.js +2062 -60
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
|
}
|
|
@@ -41375,7 +43184,7 @@ ${svg}
|
|
|
41375
43184
|
|
|
41376
43185
|
function getPlanarFilter(interval) {
|
|
41377
43186
|
return function(a, b) {
|
|
41378
|
-
return distance2D(a[0], a[1], b[0], b[1]) <= interval;
|
|
43187
|
+
return distance2D$1(a[0], a[1], b[0], b[1]) <= interval;
|
|
41379
43188
|
};
|
|
41380
43189
|
}
|
|
41381
43190
|
|
|
@@ -41808,7 +43617,7 @@ ${svg}
|
|
|
41808
43617
|
}
|
|
41809
43618
|
|
|
41810
43619
|
function probeIsExposed(index, skipA, skipB, vx, vy, p) {
|
|
41811
|
-
var r = distance2D(vx, vy, p[0], p[1]);
|
|
43620
|
+
var r = distance2D$1(vx, vy, p[0], p[1]);
|
|
41812
43621
|
if (!(r > 0)) return false;
|
|
41813
43622
|
var r2 = r * r * 0.98 * 0.98;
|
|
41814
43623
|
var px = p[0], py = p[1];
|
|
@@ -43390,7 +45199,7 @@ ${svg}
|
|
|
43390
45199
|
|
|
43391
45200
|
function stopIfBufferReachesPole(shape, dist) {
|
|
43392
45201
|
var maxAbsLat = 0;
|
|
43393
|
-
var angularDist = dist / R$3 * R2D$
|
|
45202
|
+
var angularDist = dist / R$3 * R2D$5;
|
|
43394
45203
|
(shape || []).forEach(function(path) {
|
|
43395
45204
|
latLngPathIter.init(path);
|
|
43396
45205
|
while (latLngPathIter.hasNext()) {
|
|
@@ -43881,11 +45690,11 @@ ${svg}
|
|
|
43881
45690
|
if (a[0] === b[0] && a[1] === b[1]) continue;
|
|
43882
45691
|
bearing = bearingDegrees2D(a[0], a[1], b[0], b[1]);
|
|
43883
45692
|
probe = getOffsetPoint(a[0], a[1], bearing - 90, dist);
|
|
43884
|
-
rUnits = distance2D(a[0], a[1], probe[0], probe[1]);
|
|
45693
|
+
rUnits = distance2D$1(a[0], a[1], probe[0], probe[1]);
|
|
43885
45694
|
// perpendicular stab lines every ~0.35r along the band, so cuts
|
|
43886
45695
|
// affecting only part of a long segment cannot slip between them
|
|
43887
45696
|
m = Math.max(1, Math.min(60,
|
|
43888
|
-
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))));
|
|
43889
45698
|
for (ti = 0; ti < m; ti++) {
|
|
43890
45699
|
t = (ti + 0.5) / m;
|
|
43891
45700
|
base = [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
|
|
@@ -43919,14 +45728,14 @@ ${svg}
|
|
|
43919
45728
|
function findAtRiskSegments(verts, dist) {
|
|
43920
45729
|
var n = verts.length;
|
|
43921
45730
|
var probe0 = getOffsetPoint(verts[0][0], verts[0][1], 0, dist);
|
|
43922
|
-
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]);
|
|
43923
45732
|
var cumLen = new Float64Array(n);
|
|
43924
45733
|
var riskVert = new Uint8Array(n);
|
|
43925
45734
|
var any = false;
|
|
43926
45735
|
var i, j, k;
|
|
43927
45736
|
for (i = 1; i < n; i++) {
|
|
43928
45737
|
cumLen[i] = cumLen[i - 1] +
|
|
43929
|
-
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]);
|
|
43930
45739
|
}
|
|
43931
45740
|
// the ring's start radial and cap-closing radial can cut nearby bands
|
|
43932
45741
|
riskVert[0] = riskVert[n - 1] = 1;
|
|
@@ -43998,7 +45807,7 @@ ${svg}
|
|
|
43998
45807
|
j = ids[k];
|
|
43999
45808
|
if (j <= i) continue;
|
|
44000
45809
|
var pathDist = cumLen[j] - cumLen[i];
|
|
44001
|
-
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]);
|
|
44002
45811
|
// 0.8 flags vertex pairs whose intervening path bends by
|
|
44003
45812
|
// ~130 degrees or more (a circle's chord/arc ratio is 2/pi,
|
|
44004
45813
|
// so even gentle loops qualify)
|
|
@@ -44050,7 +45859,7 @@ ${svg}
|
|
|
44050
45859
|
// at the near end is seeded by an upward ray over those same edges.
|
|
44051
45860
|
function getRingStabber(rings, dist) {
|
|
44052
45861
|
var probe0 = getOffsetPoint(rings[0][0][0], rings[0][0][1], 0, dist);
|
|
44053
|
-
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]);
|
|
44054
45863
|
var ex = [], ey = [], fx = [], fy = []; // edge data
|
|
44055
45864
|
var slabs = new Map();
|
|
44056
45865
|
rings.forEach(function(ring) {
|
|
@@ -44150,8 +45959,8 @@ ${svg}
|
|
|
44150
45959
|
var eps = dist * 1e-4;
|
|
44151
45960
|
var aIn = getOffsetPoint(a[0], a[1], bearing, eps);
|
|
44152
45961
|
var bIn = getOffsetPoint(b[0], b[1], bearing - 180, eps);
|
|
44153
|
-
if (distance2D(a[0], a[1], b[0], b[1]) <
|
|
44154
|
-
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) {
|
|
44155
45964
|
return; // segment too short to inset
|
|
44156
45965
|
}
|
|
44157
45966
|
rings.push([
|
|
@@ -44548,7 +46357,7 @@ ${svg}
|
|
|
44548
46357
|
}
|
|
44549
46358
|
|
|
44550
46359
|
function shallowAngleJoin(a, b, cx, cy, dist) {
|
|
44551
|
-
var gap = distance2D(a[0], a[1], b[0], b[1]);
|
|
46360
|
+
var gap = distance2D$1(a[0], a[1], b[0], b[1]);
|
|
44552
46361
|
var radius = getJoinExtensionDistance(cx, cy, a, b, dist);
|
|
44553
46362
|
if (gap > radius * 1e-3) return null;
|
|
44554
46363
|
return [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
|
|
@@ -45507,12 +47316,12 @@ ${svg}
|
|
|
45507
47316
|
}
|
|
45508
47317
|
|
|
45509
47318
|
function projectBufferPoint(x, y, spherical) {
|
|
45510
|
-
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] :
|
|
45511
47320
|
[x, y];
|
|
45512
47321
|
}
|
|
45513
47322
|
|
|
45514
47323
|
function unprojectBufferPoint(x, y, spherical) {
|
|
45515
|
-
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)] :
|
|
45516
47325
|
[x, y];
|
|
45517
47326
|
}
|
|
45518
47327
|
|
|
@@ -50183,7 +51992,7 @@ ${svg}
|
|
|
50183
51992
|
var maxAbsLat = Math.max(Math.abs(bounds.ymin), Math.abs(bounds.ymax));
|
|
50184
51993
|
var maxPositiveDistance = getMaxPositiveBufferDistance(lyr, dataset, opts);
|
|
50185
51994
|
if (!(maxPositiveDistance > 0)) return false;
|
|
50186
|
-
return maxAbsLat + maxPositiveDistance / R$3 * R2D$
|
|
51995
|
+
return maxAbsLat + maxPositiveDistance / R$3 * R2D$5 >= 90 - POLAR_BUFFER_MARGIN_DEGREES;
|
|
50187
51996
|
}
|
|
50188
51997
|
|
|
50189
51998
|
function getMaxPositiveBufferDistance(lyr, dataset, opts) {
|
|
@@ -51435,7 +53244,7 @@ ${svg}
|
|
|
51435
53244
|
}
|
|
51436
53245
|
|
|
51437
53246
|
function getCoordinateDistance(distance, arcs) {
|
|
51438
|
-
return arcs.isPlanar() ? distance : distance / R$3 * R2D$
|
|
53247
|
+
return arcs.isPlanar() ? distance : distance / R$3 * R2D$5;
|
|
51439
53248
|
}
|
|
51440
53249
|
|
|
51441
53250
|
// @shapeIndex: chunk-bounds index of the source shape (see buildShapeSegmentIndex)
|
|
@@ -52103,7 +53912,8 @@ ${svg}
|
|
|
52103
53912
|
// TODO: consider projections that may or may not be aligned,
|
|
52104
53913
|
// depending on parameters
|
|
52105
53914
|
if (inList(P, 'cassini,gnom,bertin1953,chamb,ob_tran,tpeqd,healpix,rhealpix,' +
|
|
52106
|
-
'ocea,omerc,tmerc,etmerc,nicol'
|
|
53915
|
+
'ocea,omerc,tmerc,etmerc,nicol,dymaxion,dymaxion2,butterfly,butterfly2,' +
|
|
53916
|
+
'cahill_keyes')) {
|
|
52107
53917
|
return false;
|
|
52108
53918
|
}
|
|
52109
53919
|
if (isAzimuthal(P)) {
|
|
@@ -52155,16 +53965,16 @@ ${svg}
|
|
|
52155
53965
|
}
|
|
52156
53966
|
|
|
52157
53967
|
function getRotationFunction2(rotation, inv) {
|
|
52158
|
-
var a = (rotation[0] || 0) * D2R$
|
|
52159
|
-
b = (rotation[1] || 0) * D2R$
|
|
52160
|
-
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;
|
|
52161
53971
|
return function(p) {
|
|
52162
|
-
p[0] *= D2R$
|
|
52163
|
-
p[1] *= D2R$
|
|
53972
|
+
p[0] *= D2R$6;
|
|
53973
|
+
p[1] *= D2R$6;
|
|
52164
53974
|
var rotate = inv ? rotatePointInv : rotatePoint;
|
|
52165
53975
|
rotate(p, a, b, c);
|
|
52166
|
-
p[0] *= R2D$
|
|
52167
|
-
p[1] *= R2D$
|
|
53976
|
+
p[0] *= R2D$5;
|
|
53977
|
+
p[1] *= R2D$5;
|
|
52168
53978
|
return p;
|
|
52169
53979
|
};
|
|
52170
53980
|
}
|
|
@@ -52623,6 +54433,9 @@ ${svg}
|
|
|
52623
54433
|
};
|
|
52624
54434
|
|
|
52625
54435
|
function getProjectionTopology(P) {
|
|
54436
|
+
if (P && P.__projection_topology) {
|
|
54437
|
+
return P.__projection_topology;
|
|
54438
|
+
}
|
|
52626
54439
|
var defn = P && projectionTopologies[getCrsSlug(P)];
|
|
52627
54440
|
if (!defn) return null;
|
|
52628
54441
|
var lon0 = P.lam0 * 180 / Math.PI;
|
|
@@ -52646,6 +54459,11 @@ ${svg}
|
|
|
52646
54459
|
}
|
|
52647
54460
|
|
|
52648
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
|
+
}
|
|
52649
54467
|
var defn = P && projectionTopologies[getCrsSlug(P)];
|
|
52650
54468
|
return !!defn && defn.seams.some(function(o) {
|
|
52651
54469
|
return o.type == 'cut';
|
|
@@ -52719,6 +54537,9 @@ ${svg}
|
|
|
52719
54537
|
|
|
52720
54538
|
// Return projected polygon extent of both clipped and unclipped projections
|
|
52721
54539
|
function getPolygonDataset$1(src, dest, opts) {
|
|
54540
|
+
if (dest.__projected_outline) {
|
|
54541
|
+
return getCustomProjectedOutline(dest, 'polygon');
|
|
54542
|
+
}
|
|
52722
54543
|
// use clipping area if projection is clipped
|
|
52723
54544
|
var dataset = getUnprojectedBoundingPolygon(src, dest, opts);
|
|
52724
54545
|
if (!dataset) {
|
|
@@ -52734,6 +54555,9 @@ ${svg}
|
|
|
52734
54555
|
|
|
52735
54556
|
// Return projected outline of clipped projections
|
|
52736
54557
|
function getOutlineDataset(src, dest, opts) {
|
|
54558
|
+
if (dest.__projected_outline) {
|
|
54559
|
+
return getCustomProjectedOutline(dest, 'polyline');
|
|
54560
|
+
}
|
|
52737
54561
|
var dataset = getUnprojectedBoundingPolygon(src, dest, opts);
|
|
52738
54562
|
if (!dataset && isInterruptedProjection(dest)) {
|
|
52739
54563
|
dataset = getBoundingRectangle(dest, {clip_bbox: [-180, -90, 180, 90]});
|
|
@@ -52745,10 +54569,33 @@ ${svg}
|
|
|
52745
54569
|
removeTinyFootprintRings(dataset);
|
|
52746
54570
|
}
|
|
52747
54571
|
dataset.layers[0].geometry_type = 'polyline';
|
|
54572
|
+
if (dest.__remove_outline_extreme_connectors) {
|
|
54573
|
+
dataset = removeOutlineExtremeConnectors(dataset);
|
|
54574
|
+
}
|
|
52748
54575
|
}
|
|
52749
54576
|
return dataset || null;
|
|
52750
54577
|
}
|
|
52751
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
|
+
|
|
52752
54599
|
// Narrow pre-projection gutters can leave zero-area rings after a rotated
|
|
52753
54600
|
// world boundary is cleaned. Remove only rings that are negligible relative
|
|
52754
54601
|
// to the main footprint; legitimate disconnected lobes are retained.
|
|
@@ -52770,6 +54617,54 @@ ${svg}
|
|
|
52770
54617
|
dissolveArcs(dataset);
|
|
52771
54618
|
}
|
|
52772
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
|
+
|
|
52773
54668
|
function getBoundingRectangle(dest, opts) {
|
|
52774
54669
|
var bbox = opts.clip_bbox || getDefaultClipBBox(dest);
|
|
52775
54670
|
var rotation = getRotationParams(dest);
|
|
@@ -52949,19 +54844,79 @@ ${svg}
|
|
|
52949
54844
|
var pathLayers = dataset.layers.filter(layerHasPaths);
|
|
52950
54845
|
var cutPaths, geojson, clip;
|
|
52951
54846
|
if (!topology || pathLayers.length === 0) return false;
|
|
54847
|
+
insertProjectionTopologyVertices(dataset, topology);
|
|
52952
54848
|
cutPaths = topology.seams
|
|
52953
54849
|
.filter(function(o) { return o.type == 'cut'; })
|
|
52954
54850
|
.reduce(function(memo, o) {
|
|
54851
|
+
if (o.paths) return memo.concat(o.paths);
|
|
52955
54852
|
return memo.concat(getCutSeamPaths(o.coordinates));
|
|
52956
54853
|
}, []);
|
|
52957
54854
|
if (cutPaths.length === 0) return false;
|
|
52958
54855
|
geojson = getCutMaskGeoJSON(cutPaths);
|
|
52959
54856
|
if (geojson.features.length === 0) return false;
|
|
52960
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
|
+
}
|
|
52961
54864
|
clipLayersInPlace(pathLayers, clip, dataset, 'erase', getInternalClipOpts());
|
|
52962
54865
|
return true;
|
|
52963
54866
|
}
|
|
52964
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
|
+
|
|
52965
54920
|
function clampDataset(dataset, bbox) {
|
|
52966
54921
|
transformPoints(dataset, function(x, y) {
|
|
52967
54922
|
return [utils.clamp(x, bbox[0], bbox[2]), utils.clamp(y, bbox[1], bbox[3])];
|
|
@@ -53016,8 +54971,8 @@ ${svg}
|
|
|
53016
54971
|
// their projected edges follow the projection instead of becoming straight
|
|
53017
54972
|
// chords between seam endpoints.
|
|
53018
54973
|
function getCutMaskGeoJSON(paths) {
|
|
53019
|
-
var e = 1e-8;
|
|
53020
54974
|
var features = paths.map(function(coords) {
|
|
54975
|
+
var e = coords.mask_width || 1e-8;
|
|
53021
54976
|
var ring = getCutMaskRing(densifyPathByInterval(coords, 0.5), e);
|
|
53022
54977
|
return {
|
|
53023
54978
|
type: 'Feature',
|
|
@@ -53030,6 +54985,33 @@ ${svg}
|
|
|
53030
54985
|
}).filter(function(feature) {
|
|
53031
54986
|
return feature.geometry.coordinates[0].length > 3;
|
|
53032
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
|
+
});
|
|
53033
55015
|
return {
|
|
53034
55016
|
type: 'FeatureCollection',
|
|
53035
55017
|
features: features
|
|
@@ -53048,8 +55030,8 @@ ${svg}
|
|
|
53048
55030
|
if (len === 0) continue;
|
|
53049
55031
|
var nx = -dy / len * width;
|
|
53050
55032
|
var ny = dx / len * width;
|
|
53051
|
-
left.push([coords[i][0] + nx, coords[i][1] + ny]);
|
|
53052
|
-
right.push([coords[i][0] - nx, coords[i][1] - ny]);
|
|
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]);
|
|
53053
55035
|
}
|
|
53054
55036
|
var ring = left.concat(right.reverse());
|
|
53055
55037
|
if (ring.length > 0) ring.push(ring[0].concat());
|
|
@@ -57459,7 +59441,7 @@ ${svg}
|
|
|
57459
59441
|
function getSplitLineFunction(crs, dashLen, gapLen, opts) {
|
|
57460
59442
|
var planar = !!opts.planar;
|
|
57461
59443
|
var interpolate = getInterpolationFunction(planar ? null : crs);
|
|
57462
|
-
var distance = isLatLngCRS(crs) ? greatCircleDistance : distance2D;
|
|
59444
|
+
var distance = isLatLngCRS(crs) ? greatCircleDistance : distance2D$1;
|
|
57463
59445
|
var inDash, parts2, interval, scale;
|
|
57464
59446
|
function addPart(coords) {
|
|
57465
59447
|
if (inDash) parts2.push(coords);
|
|
@@ -58330,7 +60312,7 @@ ${svg}
|
|
|
58330
60312
|
// Isometric ("Mercator-stretched") latitude in degrees-in; returns +/-Infinity at
|
|
58331
60313
|
// the poles.
|
|
58332
60314
|
function isometricLatitude(latDeg) {
|
|
58333
|
-
return Math.log(Math.tan(Math.PI / 4 + latDeg * D2R$
|
|
60315
|
+
return Math.log(Math.tan(Math.PI / 4 + latDeg * D2R$6 / 2));
|
|
58334
60316
|
}
|
|
58335
60317
|
|
|
58336
60318
|
// Rhumb-line (loxodrome) interpolation, fraction k of the way from A to B.
|
|
@@ -58355,9 +60337,9 @@ ${svg}
|
|
|
58355
60337
|
// Rhumb-line distance in meters, using the raw longitude delta (so a full-parallel
|
|
58356
60338
|
// sweep returns the parallel's length rather than 0).
|
|
58357
60339
|
function rhumbDistance(a, b) {
|
|
58358
|
-
var phi1 = a[1] * D2R$
|
|
60340
|
+
var phi1 = a[1] * D2R$6, phi2 = b[1] * D2R$6;
|
|
58359
60341
|
var dPhi = phi2 - phi1;
|
|
58360
|
-
var dLambda = (b[0] - a[0]) * D2R$
|
|
60342
|
+
var dLambda = (b[0] - a[0]) * D2R$6; // raw
|
|
58361
60343
|
var dPsi = Math.log(Math.tan(Math.PI / 4 + phi2 / 2) / Math.tan(Math.PI / 4 + phi1 / 2));
|
|
58362
60344
|
var q = Math.abs(dPsi) > 1e-12 ? dPhi / dPsi : Math.cos(phi1);
|
|
58363
60345
|
return Math.sqrt(dPhi * dPhi + q * q * dLambda * dLambda) * R$3;
|
|
@@ -62022,9 +64004,11 @@ ${svg}
|
|
|
62022
64004
|
// project graticule to match dataset
|
|
62023
64005
|
destInfo = getDatasetCrsInfo(dataset);
|
|
62024
64006
|
if (!destInfo.crs) stop$1("Coordinate system is unknown, unable to create a graticule");
|
|
62025
|
-
graticule =
|
|
62026
|
-
|
|
62027
|
-
|
|
64007
|
+
graticule = opts.outline ?
|
|
64008
|
+
createProjectedOutline(destInfo.crs, opts) :
|
|
64009
|
+
opts.polygon ?
|
|
64010
|
+
createProjectedPolygon(destInfo.crs, opts) :
|
|
64011
|
+
createProjectedGraticule(destInfo.crs, opts);
|
|
62028
64012
|
setDatasetCrsInfo(graticule, destInfo);
|
|
62029
64013
|
} else {
|
|
62030
64014
|
graticule = boundary ?
|
|
@@ -62049,6 +64033,12 @@ ${svg}
|
|
|
62049
64033
|
return getPolygonDataset$1(src, dest, opts);
|
|
62050
64034
|
}
|
|
62051
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
|
+
|
|
62052
64042
|
function createUnprojectedGraticule(opts) {
|
|
62053
64043
|
var src = parseCrsString$1('wgs84');
|
|
62054
64044
|
var graticule = importGeoJSON(createGraticule(src, false, opts));
|
|
@@ -62130,12 +64120,15 @@ ${svg}
|
|
|
62130
64120
|
// extended: meridian extends to pole
|
|
62131
64121
|
function createMeridian(x, extended) {
|
|
62132
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;
|
|
62133
64126
|
createMeridianPart(x, -90 + y0, 90 - y0);
|
|
62134
64127
|
if (extended && y0 > 0) {
|
|
62135
64128
|
// adding extensions as separate parts, so if the polar coordinates
|
|
62136
64129
|
// fail to project, at least the rest of the meridian line will remain
|
|
62137
|
-
createMeridianPart(x, -90, -90 + y0);
|
|
62138
|
-
createMeridianPart(x, 90 - y0, 90);
|
|
64130
|
+
createMeridianPart(x, -90 + poleInset, -90 + y0);
|
|
64131
|
+
createMeridianPart(x, 90 - y0, 90 - poleInset);
|
|
62139
64132
|
}
|
|
62140
64133
|
}
|
|
62141
64134
|
|
|
@@ -62145,7 +64138,14 @@ ${svg}
|
|
|
62145
64138
|
}
|
|
62146
64139
|
|
|
62147
64140
|
function createParallel(y) {
|
|
62148
|
-
|
|
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);
|
|
62149
64149
|
parallels.push(graticuleFeature(coords, {type: 'parallel', value: y}));
|
|
62150
64150
|
}
|
|
62151
64151
|
}
|
|
@@ -62744,7 +64744,7 @@ ${svg}
|
|
|
62744
64744
|
if (halfLen > 0 === false) {
|
|
62745
64745
|
return [a, b];
|
|
62746
64746
|
}
|
|
62747
|
-
var segLen = distance2D(a, b, c, d);
|
|
64747
|
+
var segLen = distance2D$1(a, b, c, d);
|
|
62748
64748
|
var k;
|
|
62749
64749
|
if (partialLen + segLen >= halfLen) {
|
|
62750
64750
|
k = (halfLen - partialLen) / segLen;
|
|
@@ -64665,7 +66665,7 @@ ${svg}
|
|
|
64665
66665
|
|
|
64666
66666
|
// Source: https://diego.assencio.com/?index=8d6ca3d82151bad815f78addf9b5c1c6
|
|
64667
66667
|
function twoCircleIntersection(c1, r1, c2, r2) {
|
|
64668
|
-
var d = distance2D(c1[0], c1[1], c2[0], c2[1]);
|
|
66668
|
+
var d = distance2D$1(c1[0], c1[1], c2[0], c2[1]);
|
|
64669
66669
|
if (d >= r1 + r2) return 0;
|
|
64670
66670
|
var r1sq = r1 * r1,
|
|
64671
66671
|
r2sq = r2 * r2,
|
|
@@ -66660,7 +68660,7 @@ ${svg}
|
|
|
66660
68660
|
if (totalSegs < 1) return 0;
|
|
66661
68661
|
var MAX_SAMPLES = 100000;
|
|
66662
68662
|
var stride = Math.ceil(totalSegs / MAX_SAMPLES);
|
|
66663
|
-
var distFn = spherical ? greatCircleDistance : distance2D;
|
|
68663
|
+
var distFn = spherical ? greatCircleDistance : distance2D$1;
|
|
66664
68664
|
var lens = [];
|
|
66665
68665
|
var counter = 0;
|
|
66666
68666
|
arcs.forEach3(function(xx, yy) {
|
|
@@ -68660,7 +70660,7 @@ ${svg}
|
|
|
68660
70660
|
return name == 'rectangle' || name == 'rectangles' || name == 'filter' && opts.cleanup;
|
|
68661
70661
|
}
|
|
68662
70662
|
|
|
68663
|
-
var version = "0.7.
|
|
70663
|
+
var version = "0.7.45";
|
|
68664
70664
|
|
|
68665
70665
|
// Parse command line args into commands and run them
|
|
68666
70666
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|
|
@@ -70524,6 +72524,8 @@ ${svg}
|
|
|
70524
72524
|
ClipPoints,
|
|
70525
72525
|
Colorizer,
|
|
70526
72526
|
CustomProjections,
|
|
72527
|
+
Dymaxion,
|
|
72528
|
+
ButterflyProjections,
|
|
70527
72529
|
DataAggregation,
|
|
70528
72530
|
DatasetUtils,
|
|
70529
72531
|
DataUtils,
|