mapshaper 0.7.44 → 0.7.46
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 +2685 -273
- package/package.json +1 -1
- package/www/mapshaper-gui.js +147 -104
- package/www/mapshaper.js +2685 -273
- package/www/page.css +33 -3
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$4(val, min, max) {
|
|
82
82
|
return val < min ? min : (val > max ? max : val);
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -261,36 +261,6 @@
|
|
|
261
261
|
};
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
// Call @iter on each member of an array (similar to Array#reduce(iter))
|
|
265
|
-
// iter: function(memo, item, callback)
|
|
266
|
-
// Call @done when all members have been processed or if an error occurs
|
|
267
|
-
// done: function(err, memo)
|
|
268
|
-
// @memo: Initial value
|
|
269
|
-
//
|
|
270
|
-
function reduceAsync(arr, memo, iter, done) {
|
|
271
|
-
var call = typeof setImmediate == 'undefined' ? setTimeout : setImmediate;
|
|
272
|
-
var i=0;
|
|
273
|
-
next(null, memo);
|
|
274
|
-
|
|
275
|
-
function next(err, memo) {
|
|
276
|
-
// Detach next operation from call stack to prevent overflow
|
|
277
|
-
// Don't use setTimeout(, 0) if setImmediate is available
|
|
278
|
-
// (setTimeout() can introduce a long delay if previous operation was slow,
|
|
279
|
-
// as of Node 0.10.32 -- a bug?)
|
|
280
|
-
if (err) {
|
|
281
|
-
return done(err, null);
|
|
282
|
-
}
|
|
283
|
-
call(function() {
|
|
284
|
-
if (i < arr.length === false) {
|
|
285
|
-
done(null, memo);
|
|
286
|
-
} else {
|
|
287
|
-
iter(memo, arr[i++], next);
|
|
288
|
-
}
|
|
289
|
-
}, 0);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
|
|
294
264
|
// Append elements of @src array to @dest array
|
|
295
265
|
function merge$1(dest, src) {
|
|
296
266
|
if (!isArray(dest) || !isArray(src)) {
|
|
@@ -751,7 +721,7 @@
|
|
|
751
721
|
function findValueByRank(arr, rank) {
|
|
752
722
|
if (!arr.length || rank < 1 || rank > arr.length) error$1("[findValueByRank()] invalid input");
|
|
753
723
|
|
|
754
|
-
rank = clamp$
|
|
724
|
+
rank = clamp$4(rank | 0, 1, arr.length);
|
|
755
725
|
var k = rank - 1, // conv. rank to array index
|
|
756
726
|
n = arr.length,
|
|
757
727
|
l = 0,
|
|
@@ -1125,7 +1095,7 @@
|
|
|
1125
1095
|
// self-import and the resulting Rollup circular-dependency warning.
|
|
1126
1096
|
var utils = {
|
|
1127
1097
|
addThousandsSep, addslashes, arrayToIndex,
|
|
1128
|
-
clamp: clamp$
|
|
1098
|
+
clamp: clamp$4, cleanNumericString, contains, copyElements, countValues, createBuffer,
|
|
1129
1099
|
defaults, difference,
|
|
1130
1100
|
endsWith, every, expandoBuffer, extend: extend$1, extendBuffer,
|
|
1131
1101
|
find: find$1, findMedian, findQuantile, findRankByValue, findStringPrefix,
|
|
@@ -1145,7 +1115,7 @@
|
|
|
1145
1115
|
parseIntlNumber, parseNumber: parseNumber$1, parseString, pickOne, pluck,
|
|
1146
1116
|
pluralSuffix, promisify,
|
|
1147
1117
|
quicksort: quicksort$1, quicksortPartition,
|
|
1148
|
-
range,
|
|
1118
|
+
range, regexEscape, reorderArray: reorderArray$1, repeat, repeatString,
|
|
1149
1119
|
replaceArray, rpad, rtrim,
|
|
1150
1120
|
shuffle, some, sortArrayIndex, sortOn, splitLines, sum: sum$1,
|
|
1151
1121
|
toArray: toArray$1, toBuffer, trim, trimQuotes,
|
|
@@ -2083,12 +2053,12 @@
|
|
|
2083
2053
|
// TODO: remove this constant, use actual data from dataset CRS,
|
|
2084
2054
|
// also consider using ellipsoidal formulas where greater accuracy might be important.
|
|
2085
2055
|
var R$3 = WGS84.SEMIMAJOR_AXIS;
|
|
2086
|
-
var D2R$
|
|
2087
|
-
var R2D$
|
|
2056
|
+
var D2R$7 = Math.PI / 180;
|
|
2057
|
+
var R2D$6 = 180 / Math.PI;
|
|
2088
2058
|
|
|
2089
2059
|
// Equirectangular projection
|
|
2090
2060
|
function degreesToMeters(deg) {
|
|
2091
|
-
return deg * D2R$
|
|
2061
|
+
return deg * D2R$7 * R$3;
|
|
2092
2062
|
}
|
|
2093
2063
|
|
|
2094
2064
|
function distance3D(ax, ay, az, bx, by, bz) {
|
|
@@ -2104,7 +2074,7 @@
|
|
|
2104
2074
|
return dx * dx + dy * dy;
|
|
2105
2075
|
}
|
|
2106
2076
|
|
|
2107
|
-
function distance2D(ax, ay, bx, by) {
|
|
2077
|
+
function distance2D$1(ax, ay, bx, by) {
|
|
2108
2078
|
var dx = ax - bx,
|
|
2109
2079
|
dy = ay - by;
|
|
2110
2080
|
return Math.sqrt(dx * dx + dy * dy);
|
|
@@ -2235,16 +2205,16 @@
|
|
|
2235
2205
|
|
|
2236
2206
|
function xyzToLngLat(x, y, z, p) {
|
|
2237
2207
|
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$
|
|
2208
|
+
var lat = Math.asin(z / d) / D2R$7;
|
|
2209
|
+
var lng = Math.atan2(y / d, x / d) / D2R$7;
|
|
2240
2210
|
p[0] = lng;
|
|
2241
2211
|
p[1] = lat;
|
|
2242
2212
|
}
|
|
2243
2213
|
|
|
2244
2214
|
function lngLatToXYZ(lng, lat, p) {
|
|
2245
2215
|
var cosLat;
|
|
2246
|
-
lng *= D2R$
|
|
2247
|
-
lat *= D2R$
|
|
2216
|
+
lng *= D2R$7;
|
|
2217
|
+
lat *= D2R$7;
|
|
2248
2218
|
cosLat = Math.cos(lat);
|
|
2249
2219
|
p[0] = Math.cos(lng) * cosLat * R$3;
|
|
2250
2220
|
p[1] = Math.sin(lng) * cosLat * R$3;
|
|
@@ -2282,7 +2252,7 @@
|
|
|
2282
2252
|
}
|
|
2283
2253
|
|
|
2284
2254
|
function cosine(ax, ay, bx, by, cx, cy) {
|
|
2285
|
-
var den = distance2D(ax, ay, bx, by) * distance2D(bx, by, cx, cy),
|
|
2255
|
+
var den = distance2D$1(ax, ay, bx, by) * distance2D$1(bx, by, cx, cy),
|
|
2286
2256
|
cos = 0;
|
|
2287
2257
|
if (den > 0) {
|
|
2288
2258
|
cos = ((ax - bx) * (cx - bx) + (ay - by) * (cy - by)) / den;
|
|
@@ -2364,9 +2334,9 @@
|
|
|
2364
2334
|
|
|
2365
2335
|
var Geom = /*#__PURE__*/Object.freeze({
|
|
2366
2336
|
__proto__: null,
|
|
2367
|
-
D2R: D2R$
|
|
2337
|
+
D2R: D2R$7,
|
|
2368
2338
|
R: R$3,
|
|
2369
|
-
R2D: R2D$
|
|
2339
|
+
R2D: R2D$6,
|
|
2370
2340
|
bearing: bearing,
|
|
2371
2341
|
bearing2D: bearing2D,
|
|
2372
2342
|
containsBounds: containsBounds,
|
|
@@ -2374,7 +2344,7 @@
|
|
|
2374
2344
|
cosine: cosine,
|
|
2375
2345
|
cosine3D: cosine3D,
|
|
2376
2346
|
degreesToMeters: degreesToMeters,
|
|
2377
|
-
distance2D: distance2D,
|
|
2347
|
+
distance2D: distance2D$1,
|
|
2378
2348
|
distance3D: distance3D,
|
|
2379
2349
|
distanceSq: distanceSq,
|
|
2380
2350
|
distanceSq3D: distanceSq3D,
|
|
@@ -2525,7 +2495,7 @@
|
|
|
2525
2495
|
if (spherical && arcs.isPlanar()) {
|
|
2526
2496
|
error("Expected lat-long coordinates");
|
|
2527
2497
|
}
|
|
2528
|
-
calcLen = spherical ? greatCircleDistance : distance2D;
|
|
2498
|
+
calcLen = spherical ? greatCircleDistance : distance2D$1;
|
|
2529
2499
|
len = 0;
|
|
2530
2500
|
for (var i=0, n=path.length; i<n; i++) {
|
|
2531
2501
|
arcs.forEachArcSegment(path[i], addSegLen);
|
|
@@ -3311,8 +3281,8 @@
|
|
|
3311
3281
|
// angle and seg length ratio thresholds were found by comparing
|
|
3312
3282
|
// fast and robust outputs on sample data
|
|
3313
3283
|
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);
|
|
3284
|
+
var len1 = distance2D$1(ax, ay, bx, by);
|
|
3285
|
+
var len2 = distance2D$1(cx, cy, dx, dy);
|
|
3316
3286
|
var ratio = (len1 < len2 ? len1 / len2 : len2 / len1) || 0;
|
|
3317
3287
|
if (ratio < 0.001) return true;
|
|
3318
3288
|
return false;
|
|
@@ -3405,7 +3375,7 @@
|
|
|
3405
3375
|
}
|
|
3406
3376
|
|
|
3407
3377
|
function snapIfCloser(p, minDist, x, y, x2, y2) {
|
|
3408
|
-
var dist = distance2D(x, y, x2, y2);
|
|
3378
|
+
var dist = distance2D$1(x, y, x2, y2);
|
|
3409
3379
|
if (dist < minDist) {
|
|
3410
3380
|
minDist = dist;
|
|
3411
3381
|
p[0] = x2;
|
|
@@ -6287,7 +6257,2291 @@
|
|
|
6287
6257
|
probablyDecimalDegreeBounds: probablyDecimalDegreeBounds
|
|
6288
6258
|
});
|
|
6289
6259
|
|
|
6260
|
+
/*
|
|
6261
|
+
* Polyhedral unfolding code adapted from d3-geo-polygon.
|
|
6262
|
+
*
|
|
6263
|
+
* Copyright 2017-2024 Mike Bostock
|
|
6264
|
+
* ISC License: https://github.com/d3/d3-geo-polygon/blob/main/LICENSE
|
|
6265
|
+
*/
|
|
6266
|
+
|
|
6267
|
+
var D2R$6 = Math.PI / 180;
|
|
6268
|
+
var R2D$5 = 180 / Math.PI;
|
|
6269
|
+
var EPS$2 = 1e-12;
|
|
6270
|
+
|
|
6271
|
+
// Build a forward-only spherical polyhedral projection.
|
|
6272
|
+
//
|
|
6273
|
+
// config.faces contains spherical polygons in degrees. config.parents defines
|
|
6274
|
+
// a spanning tree of attached faces. faceProjector(face) returns a local
|
|
6275
|
+
// projection accepting radians and returning planar coordinates.
|
|
6276
|
+
function createPolyhedralProjection(config) {
|
|
6277
|
+
var faces = config.faces.map(function(coords, i) {
|
|
6278
|
+
return createFace(coords, i, config.faceSites && config.faceSites[i]);
|
|
6279
|
+
});
|
|
6280
|
+
var parents = config.parents;
|
|
6281
|
+
var attachedPairs = new Set();
|
|
6282
|
+
var faceProjector = config.faceProjector;
|
|
6283
|
+
|
|
6284
|
+
faces.forEach(function(face) {
|
|
6285
|
+
face.project = faceProjector(face);
|
|
6286
|
+
});
|
|
6287
|
+
|
|
6288
|
+
faces.forEach(function(face) {
|
|
6289
|
+
initFaceTransform(face.id);
|
|
6290
|
+
});
|
|
6291
|
+
|
|
6292
|
+
function initFaceTransform(faceId) {
|
|
6293
|
+
var parentId = parents[faceId];
|
|
6294
|
+
var face = faces[faceId];
|
|
6295
|
+
if (face.transform) return;
|
|
6296
|
+
if (parentId < 0) {
|
|
6297
|
+
face.transform = identityMatrix();
|
|
6298
|
+
return;
|
|
6299
|
+
}
|
|
6300
|
+
initFaceTransform(parentId);
|
|
6301
|
+
var parent = faces[parentId];
|
|
6302
|
+
var shared = findSharedEdge(face.coords, parent.coords);
|
|
6303
|
+
if (!shared) {
|
|
6304
|
+
throw new Error('Invalid polyhedral face tree');
|
|
6305
|
+
}
|
|
6306
|
+
var childEdge = shared.map(function(p) {
|
|
6307
|
+
return face.project(p[0] * D2R$6, p[1] * D2R$6);
|
|
6308
|
+
});
|
|
6309
|
+
var parentEdge = shared.map(function(p) {
|
|
6310
|
+
return parent.project(p[0] * D2R$6, p[1] * D2R$6);
|
|
6311
|
+
});
|
|
6312
|
+
face.transform = multiplyMatrices(
|
|
6313
|
+
parent.transform,
|
|
6314
|
+
getEdgeTransform(parentEdge, childEdge)
|
|
6315
|
+
);
|
|
6316
|
+
attachedPairs.add(pairKey(faceId, parentId));
|
|
6317
|
+
}
|
|
6318
|
+
|
|
6319
|
+
var rootEdge = findLongestEdge(faces[0].coords);
|
|
6320
|
+
var rootPlanarEdge = rootEdge.map(function(p) {
|
|
6321
|
+
return faces[0].project(p[0] * D2R$6, p[1] * D2R$6);
|
|
6322
|
+
});
|
|
6323
|
+
var sphericalEdgeLength = angularDistance(rootEdge[0], rootEdge[1]);
|
|
6324
|
+
var planarEdgeLength = distance2D(rootPlanarEdge[0], rootPlanarEdge[1]);
|
|
6325
|
+
var scale = sphericalEdgeLength / planarEdgeLength;
|
|
6326
|
+
var planarAngle = (config.angle || 0) * D2R$6;
|
|
6327
|
+
var edgeIndex = indexEdges(faces, attachedPairs);
|
|
6328
|
+
var outline = buildOutline(faces, attachedPairs).map(function(ring) {
|
|
6329
|
+
return ring.map(transformOutputPoint);
|
|
6330
|
+
});
|
|
6331
|
+
var bounds = getBounds(outline);
|
|
6332
|
+
var centerX = (bounds[0] + bounds[2]) / 2;
|
|
6333
|
+
var centerY = (bounds[1] + bounds[3]) / 2;
|
|
6334
|
+
|
|
6335
|
+
// Centering is calculated after scale and planar rotation.
|
|
6336
|
+
outline.forEach(function(ring) {
|
|
6337
|
+
ring.forEach(function(p) {
|
|
6338
|
+
p[0] -= centerX;
|
|
6339
|
+
p[1] -= centerY;
|
|
6340
|
+
});
|
|
6341
|
+
});
|
|
6342
|
+
|
|
6343
|
+
return {
|
|
6344
|
+
faces: faces,
|
|
6345
|
+
outline: outline,
|
|
6346
|
+
forward: forward,
|
|
6347
|
+
findFace: findFace,
|
|
6348
|
+
findTransitionRegion: findTransitionRegion,
|
|
6349
|
+
getTopology: getTopology
|
|
6350
|
+
};
|
|
6351
|
+
|
|
6352
|
+
function forward(lam, phi) {
|
|
6353
|
+
var rotated = rotateRadians(
|
|
6354
|
+
lam,
|
|
6355
|
+
phi,
|
|
6356
|
+
config.rotation[0] * D2R$6,
|
|
6357
|
+
config.rotation[1] * D2R$6,
|
|
6358
|
+
config.rotation[2] * D2R$6
|
|
6359
|
+
);
|
|
6360
|
+
var face = findFaceRotated(rotated[0], rotated[1]);
|
|
6361
|
+
if (!face) return null;
|
|
6362
|
+
var p = face.project(rotated[0], rotated[1]);
|
|
6363
|
+
p = applyMatrix(face.transform, p);
|
|
6364
|
+
p = transformOutputPoint(p);
|
|
6365
|
+
p[0] -= centerX;
|
|
6366
|
+
p[1] -= centerY;
|
|
6367
|
+
return p;
|
|
6368
|
+
}
|
|
6369
|
+
|
|
6370
|
+
function findFace(lam, phi) {
|
|
6371
|
+
var rotated = rotateRadians(
|
|
6372
|
+
lam,
|
|
6373
|
+
phi,
|
|
6374
|
+
config.rotation[0] * D2R$6,
|
|
6375
|
+
config.rotation[1] * D2R$6,
|
|
6376
|
+
config.rotation[2] * D2R$6
|
|
6377
|
+
);
|
|
6378
|
+
var face = findFaceRotated(rotated[0], rotated[1]);
|
|
6379
|
+
return face ? face.id : -1;
|
|
6380
|
+
}
|
|
6381
|
+
|
|
6382
|
+
function findTransitionRegion(lam, phi) {
|
|
6383
|
+
var rotated = rotateRadians(
|
|
6384
|
+
lam,
|
|
6385
|
+
phi,
|
|
6386
|
+
config.rotation[0] * D2R$6,
|
|
6387
|
+
config.rotation[1] * D2R$6,
|
|
6388
|
+
config.rotation[2] * D2R$6
|
|
6389
|
+
);
|
|
6390
|
+
var face = findFaceRotated(rotated[0], rotated[1]);
|
|
6391
|
+
var region;
|
|
6392
|
+
if (!face) return '-1';
|
|
6393
|
+
region = face.project.findRegion ?
|
|
6394
|
+
face.project.findRegion(rotated[0], rotated[1]) :
|
|
6395
|
+
0;
|
|
6396
|
+
return face.id + ':' + region;
|
|
6397
|
+
}
|
|
6398
|
+
|
|
6399
|
+
function findFaceRotated(lam, phi) {
|
|
6400
|
+
var faceId;
|
|
6401
|
+
if (config.findFace) {
|
|
6402
|
+
faceId = config.findFace(lam, phi);
|
|
6403
|
+
return faceId >= 0 ? faces[faceId] : null;
|
|
6404
|
+
}
|
|
6405
|
+
var p = radiansToVector$1(lam, phi);
|
|
6406
|
+
for (var i = 0; i < faces.length; i++) {
|
|
6407
|
+
if (faceContainsVector(faces[i], p)) return faces[i];
|
|
6408
|
+
}
|
|
6409
|
+
return null;
|
|
6410
|
+
}
|
|
6411
|
+
|
|
6412
|
+
function transformOutputPoint(p) {
|
|
6413
|
+
var x = p[0] * scale;
|
|
6414
|
+
// Local unfolding uses screen-oriented y coordinates to match the D3
|
|
6415
|
+
// Airocean face layout; return conventional projected coordinates (y up).
|
|
6416
|
+
var y = -p[1] * scale;
|
|
6417
|
+
if (planarAngle) {
|
|
6418
|
+
return [
|
|
6419
|
+
x * Math.cos(planarAngle) - y * Math.sin(planarAngle),
|
|
6420
|
+
x * Math.sin(planarAngle) + y * Math.cos(planarAngle)
|
|
6421
|
+
];
|
|
6422
|
+
}
|
|
6423
|
+
return [x, y];
|
|
6424
|
+
}
|
|
6425
|
+
|
|
6426
|
+
function getTopology(lon0) {
|
|
6427
|
+
var seams = edgeIndex.map(function(edge) {
|
|
6428
|
+
var endpoints = edge.points.map(function(p) {
|
|
6429
|
+
var q = rotateRadians(
|
|
6430
|
+
p[0] * D2R$6,
|
|
6431
|
+
p[1] * D2R$6,
|
|
6432
|
+
config.rotation[0] * D2R$6,
|
|
6433
|
+
config.rotation[1] * D2R$6,
|
|
6434
|
+
config.rotation[2] * D2R$6,
|
|
6435
|
+
true
|
|
6436
|
+
);
|
|
6437
|
+
return [normalizeLongitude$2(q[0] * R2D$5 + lon0), q[1] * R2D$5];
|
|
6438
|
+
});
|
|
6439
|
+
var paths = splitPathAtAntimeridian$2(
|
|
6440
|
+
interpolateGreatCircle(endpoints[0], endpoints[1], 0.05)
|
|
6441
|
+
);
|
|
6442
|
+
paths.forEach(function(path) {
|
|
6443
|
+
// The mask must be wider than the tiny chord error between samples.
|
|
6444
|
+
path.mask_width = 4e-5;
|
|
6445
|
+
});
|
|
6446
|
+
return {
|
|
6447
|
+
type: edge.attached ? 'attached' : 'cut',
|
|
6448
|
+
faces: edge.faces,
|
|
6449
|
+
paths: paths
|
|
6450
|
+
};
|
|
6451
|
+
});
|
|
6452
|
+
var regionPairs = new Map();
|
|
6453
|
+
edgeIndex.forEach(function(edge) {
|
|
6454
|
+
if (edge.faces.length == 2) {
|
|
6455
|
+
regionPairs.set(pairKey(edge.faces[0], edge.faces[1]), edge.attached);
|
|
6456
|
+
}
|
|
6457
|
+
});
|
|
6458
|
+
return {
|
|
6459
|
+
regions: faces.map(function(face) {
|
|
6460
|
+
return {id: face.id};
|
|
6461
|
+
}),
|
|
6462
|
+
seams: seams,
|
|
6463
|
+
findRegion: function(lon, lat) {
|
|
6464
|
+
var lam = normalizeLongitude$2(lon - lon0) * D2R$6;
|
|
6465
|
+
return findFace(lam, lat * D2R$6);
|
|
6466
|
+
},
|
|
6467
|
+
findTransitionRegion: function(lon, lat) {
|
|
6468
|
+
var lam = normalizeLongitude$2(lon - lon0) * D2R$6;
|
|
6469
|
+
return findTransitionRegion(lam, lat * D2R$6);
|
|
6470
|
+
},
|
|
6471
|
+
regionsAreAttached: function(a, b) {
|
|
6472
|
+
return regionPairs.get(pairKey(a, b)) === true;
|
|
6473
|
+
},
|
|
6474
|
+
outline: outline.map(function(ring) {
|
|
6475
|
+
return ring.map(function(p) {
|
|
6476
|
+
return p.concat();
|
|
6477
|
+
});
|
|
6478
|
+
})
|
|
6479
|
+
};
|
|
6480
|
+
}
|
|
6481
|
+
}
|
|
6482
|
+
|
|
6483
|
+
function rotateSphericalRadians(lam, phi, rotation, invert) {
|
|
6484
|
+
return rotateRadians(
|
|
6485
|
+
lam,
|
|
6486
|
+
phi,
|
|
6487
|
+
(rotation[0] || 0) * D2R$6,
|
|
6488
|
+
(rotation[1] || 0) * D2R$6,
|
|
6489
|
+
(rotation[2] || 0) * D2R$6,
|
|
6490
|
+
invert
|
|
6491
|
+
);
|
|
6492
|
+
}
|
|
6493
|
+
|
|
6494
|
+
function createFace(coords, id, site) {
|
|
6495
|
+
var vectors = coords.map(function(p) {
|
|
6496
|
+
return degreesToVector$2(p[0], p[1]);
|
|
6497
|
+
});
|
|
6498
|
+
var inside = normalizeVector$2(vectors.reduce(function(sum, p) {
|
|
6499
|
+
sum[0] += p[0];
|
|
6500
|
+
sum[1] += p[1];
|
|
6501
|
+
sum[2] += p[2];
|
|
6502
|
+
return sum;
|
|
6503
|
+
}, [0, 0, 0]));
|
|
6504
|
+
var edges = [];
|
|
6505
|
+
for (var i = 0; i < vectors.length; i++) {
|
|
6506
|
+
var a = vectors[i];
|
|
6507
|
+
var b = vectors[(i + 1) % vectors.length];
|
|
6508
|
+
var normal = cross$2(a, b);
|
|
6509
|
+
edges.push({
|
|
6510
|
+
normal: normal,
|
|
6511
|
+
sign: dot$2(normal, inside) < 0 ? -1 : 1
|
|
6512
|
+
});
|
|
6513
|
+
}
|
|
6514
|
+
return {
|
|
6515
|
+
id: id,
|
|
6516
|
+
coords: coords,
|
|
6517
|
+
vectors: vectors,
|
|
6518
|
+
edges: edges,
|
|
6519
|
+
centroid: site || vectorToDegrees$2(inside)
|
|
6520
|
+
};
|
|
6521
|
+
}
|
|
6522
|
+
|
|
6523
|
+
function faceContainsVector(face, p) {
|
|
6524
|
+
for (var i = 0; i < face.edges.length; i++) {
|
|
6525
|
+
var edge = face.edges[i];
|
|
6526
|
+
if (dot$2(edge.normal, p) * edge.sign < -EPS$2) return false;
|
|
6527
|
+
}
|
|
6528
|
+
return true;
|
|
6529
|
+
}
|
|
6530
|
+
|
|
6531
|
+
function indexEdges(faces, attachedPairs) {
|
|
6532
|
+
var index = new Map();
|
|
6533
|
+
faces.forEach(function(face) {
|
|
6534
|
+
for (var i = 0; i < face.coords.length; i++) {
|
|
6535
|
+
var a = face.coords[i];
|
|
6536
|
+
var b = face.coords[(i + 1) % face.coords.length];
|
|
6537
|
+
var key = edgeKey(a, b);
|
|
6538
|
+
var edge = index.get(key);
|
|
6539
|
+
if (!edge) {
|
|
6540
|
+
edge = {points: [a, b], faces: []};
|
|
6541
|
+
index.set(key, edge);
|
|
6542
|
+
}
|
|
6543
|
+
edge.faces.push(face.id);
|
|
6544
|
+
}
|
|
6545
|
+
});
|
|
6546
|
+
return Array.from(index.values()).map(function(edge) {
|
|
6547
|
+
edge.attached = edge.faces.length == 2 &&
|
|
6548
|
+
attachedPairs.has(pairKey(edge.faces[0], edge.faces[1]));
|
|
6549
|
+
return edge;
|
|
6550
|
+
});
|
|
6551
|
+
}
|
|
6552
|
+
|
|
6553
|
+
function buildOutline(faces, attachedPairs) {
|
|
6554
|
+
var edges = [];
|
|
6555
|
+
faces.forEach(function(face) {
|
|
6556
|
+
for (var i = 0; i < face.coords.length; i++) {
|
|
6557
|
+
var a = face.coords[i];
|
|
6558
|
+
var b = face.coords[(i + 1) % face.coords.length];
|
|
6559
|
+
var adjacent = findAdjacentFace(faces, face.id, a, b);
|
|
6560
|
+
if (adjacent >= 0 && attachedPairs.has(pairKey(face.id, adjacent))) continue;
|
|
6561
|
+
edges.push([
|
|
6562
|
+
applyMatrix(face.transform, face.project(a[0] * D2R$6, a[1] * D2R$6)),
|
|
6563
|
+
applyMatrix(face.transform, face.project(b[0] * D2R$6, b[1] * D2R$6))
|
|
6564
|
+
]);
|
|
6565
|
+
}
|
|
6566
|
+
});
|
|
6567
|
+
return connectEdges(edges);
|
|
6568
|
+
}
|
|
6569
|
+
|
|
6570
|
+
function connectEdges(edges) {
|
|
6571
|
+
var unused = edges.concat();
|
|
6572
|
+
var rings = [];
|
|
6573
|
+
while (unused.length) {
|
|
6574
|
+
var edge = unused.pop();
|
|
6575
|
+
var ring = [edge[0], edge[1]];
|
|
6576
|
+
while (!pointsAlmostEqual(ring[0], ring[ring.length - 1])) {
|
|
6577
|
+
var last = ring[ring.length - 1];
|
|
6578
|
+
var found = -1;
|
|
6579
|
+
var next;
|
|
6580
|
+
for (var i = 0; i < unused.length; i++) {
|
|
6581
|
+
if (pointsAlmostEqual(last, unused[i][0])) {
|
|
6582
|
+
found = i;
|
|
6583
|
+
next = unused[i][1];
|
|
6584
|
+
break;
|
|
6585
|
+
}
|
|
6586
|
+
if (pointsAlmostEqual(last, unused[i][1])) {
|
|
6587
|
+
found = i;
|
|
6588
|
+
next = unused[i][0];
|
|
6589
|
+
break;
|
|
6590
|
+
}
|
|
6591
|
+
}
|
|
6592
|
+
if (found < 0) break;
|
|
6593
|
+
unused.splice(found, 1);
|
|
6594
|
+
ring.push(next);
|
|
6595
|
+
}
|
|
6596
|
+
if (ring.length > 3 && pointsAlmostEqual(ring[0], ring[ring.length - 1])) {
|
|
6597
|
+
ring[ring.length - 1] = ring[0].concat();
|
|
6598
|
+
rings.push(ring);
|
|
6599
|
+
}
|
|
6600
|
+
}
|
|
6601
|
+
return rings;
|
|
6602
|
+
}
|
|
6603
|
+
|
|
6604
|
+
function findAdjacentFace(faces, faceId, a, b) {
|
|
6605
|
+
var key = edgeKey(a, b);
|
|
6606
|
+
for (var i = 0; i < faces.length; i++) {
|
|
6607
|
+
if (i == faceId) continue;
|
|
6608
|
+
var coords = faces[i].coords;
|
|
6609
|
+
for (var j = 0; j < coords.length; j++) {
|
|
6610
|
+
if (edgeKey(coords[j], coords[(j + 1) % coords.length]) == key) return i;
|
|
6611
|
+
}
|
|
6612
|
+
}
|
|
6613
|
+
return -1;
|
|
6614
|
+
}
|
|
6615
|
+
|
|
6616
|
+
function findSharedEdge(a, b) {
|
|
6617
|
+
for (var i = 0; i < a.length; i++) {
|
|
6618
|
+
var p = a[i];
|
|
6619
|
+
for (var j = 0; j < b.length; j++) {
|
|
6620
|
+
var q = a[(i + 1) % a.length];
|
|
6621
|
+
var c = b[j];
|
|
6622
|
+
var d = b[(j + 1) % b.length];
|
|
6623
|
+
if (sameSphericalPoint(p, c) && sameSphericalPoint(q, d) ||
|
|
6624
|
+
sameSphericalPoint(p, d) && sameSphericalPoint(q, c)) {
|
|
6625
|
+
return [p, q];
|
|
6626
|
+
}
|
|
6627
|
+
}
|
|
6628
|
+
}
|
|
6629
|
+
return null;
|
|
6630
|
+
}
|
|
6631
|
+
|
|
6632
|
+
function findLongestEdge(coords) {
|
|
6633
|
+
var longest = null;
|
|
6634
|
+
var max = -Infinity;
|
|
6635
|
+
for (var i = 0; i < coords.length; i++) {
|
|
6636
|
+
var edge = [coords[i], coords[(i + 1) % coords.length]];
|
|
6637
|
+
var length = angularDistance(edge[0], edge[1]);
|
|
6638
|
+
if (length > max) {
|
|
6639
|
+
longest = edge;
|
|
6640
|
+
max = length;
|
|
6641
|
+
}
|
|
6642
|
+
}
|
|
6643
|
+
return longest;
|
|
6644
|
+
}
|
|
6645
|
+
|
|
6646
|
+
function getEdgeTransform(dest, src) {
|
|
6647
|
+
var ux = dest[1][0] - dest[0][0];
|
|
6648
|
+
var uy = dest[1][1] - dest[0][1];
|
|
6649
|
+
var vx = src[1][0] - src[0][0];
|
|
6650
|
+
var vy = src[1][1] - src[0][1];
|
|
6651
|
+
var scale = Math.sqrt((ux * ux + uy * uy) / (vx * vx + vy * vy));
|
|
6652
|
+
var angle = Math.atan2(ux * vy - uy * vx, ux * vx + uy * vy);
|
|
6653
|
+
var cos = Math.cos(angle) * scale;
|
|
6654
|
+
var sin = Math.sin(angle) * scale;
|
|
6655
|
+
return [
|
|
6656
|
+
cos,
|
|
6657
|
+
sin,
|
|
6658
|
+
dest[0][0] - cos * src[0][0] - sin * src[0][1],
|
|
6659
|
+
-sin,
|
|
6660
|
+
cos,
|
|
6661
|
+
dest[0][1] + sin * src[0][0] - cos * src[0][1]
|
|
6662
|
+
];
|
|
6663
|
+
}
|
|
6664
|
+
|
|
6665
|
+
function identityMatrix() {
|
|
6666
|
+
return [1, 0, 0, 0, 1, 0];
|
|
6667
|
+
}
|
|
6668
|
+
|
|
6669
|
+
function multiplyMatrices(a, b) {
|
|
6670
|
+
return [
|
|
6671
|
+
a[0] * b[0] + a[1] * b[3],
|
|
6672
|
+
a[0] * b[1] + a[1] * b[4],
|
|
6673
|
+
a[0] * b[2] + a[1] * b[5] + a[2],
|
|
6674
|
+
a[3] * b[0] + a[4] * b[3],
|
|
6675
|
+
a[3] * b[1] + a[4] * b[4],
|
|
6676
|
+
a[3] * b[2] + a[4] * b[5] + a[5]
|
|
6677
|
+
];
|
|
6678
|
+
}
|
|
6679
|
+
|
|
6680
|
+
function applyMatrix(m, p) {
|
|
6681
|
+
return [
|
|
6682
|
+
m[0] * p[0] + m[1] * p[1] + m[2],
|
|
6683
|
+
m[3] * p[0] + m[4] * p[1] + m[5]
|
|
6684
|
+
];
|
|
6685
|
+
}
|
|
6686
|
+
|
|
6687
|
+
function rotateRadians(lam, phi, deltaLam, deltaPhi, deltaGamma, invert) {
|
|
6688
|
+
if (invert) {
|
|
6689
|
+
var inv = rotatePhiGamma$1(lam, phi, deltaPhi, deltaGamma, true);
|
|
6690
|
+
return [normalizeRadians$2(inv[0] - deltaLam), inv[1]];
|
|
6691
|
+
}
|
|
6692
|
+
lam = normalizeRadians$2(lam + deltaLam);
|
|
6693
|
+
return rotatePhiGamma$1(lam, phi, deltaPhi, deltaGamma, false);
|
|
6694
|
+
}
|
|
6695
|
+
|
|
6696
|
+
// Based on d3-geo spherical rotation (ISC license).
|
|
6697
|
+
function rotatePhiGamma$1(lam, phi, deltaPhi, deltaGamma, invert) {
|
|
6698
|
+
var cosDeltaPhi = Math.cos(deltaPhi);
|
|
6699
|
+
var sinDeltaPhi = Math.sin(deltaPhi);
|
|
6700
|
+
var cosDeltaGamma = Math.cos(deltaGamma);
|
|
6701
|
+
var sinDeltaGamma = Math.sin(deltaGamma);
|
|
6702
|
+
var cosPhi = Math.cos(phi);
|
|
6703
|
+
var x = Math.cos(lam) * cosPhi;
|
|
6704
|
+
var y = Math.sin(lam) * cosPhi;
|
|
6705
|
+
var z = Math.sin(phi);
|
|
6706
|
+
var k;
|
|
6707
|
+
if (invert) {
|
|
6708
|
+
k = z * cosDeltaGamma - y * sinDeltaGamma;
|
|
6709
|
+
return [
|
|
6710
|
+
Math.atan2(y * cosDeltaGamma + z * sinDeltaGamma,
|
|
6711
|
+
x * cosDeltaPhi + k * sinDeltaPhi),
|
|
6712
|
+
Math.asin(clamp$3(k * cosDeltaPhi - x * sinDeltaPhi, -1, 1))
|
|
6713
|
+
];
|
|
6714
|
+
}
|
|
6715
|
+
k = z * cosDeltaPhi + x * sinDeltaPhi;
|
|
6716
|
+
return [
|
|
6717
|
+
Math.atan2(y * cosDeltaGamma - k * sinDeltaGamma,
|
|
6718
|
+
x * cosDeltaPhi - z * sinDeltaPhi),
|
|
6719
|
+
Math.asin(clamp$3(k * cosDeltaGamma + y * sinDeltaGamma, -1, 1))
|
|
6720
|
+
];
|
|
6721
|
+
}
|
|
6722
|
+
|
|
6723
|
+
function interpolateGreatCircle(a, b, interval) {
|
|
6724
|
+
var av = degreesToVector$2(a[0], a[1]);
|
|
6725
|
+
var bv = degreesToVector$2(b[0], b[1]);
|
|
6726
|
+
var angle = Math.acos(clamp$3(dot$2(av, bv), -1, 1));
|
|
6727
|
+
var n = Math.max(1, Math.ceil(angle * R2D$5 / interval));
|
|
6728
|
+
var sinAngle = Math.sin(angle);
|
|
6729
|
+
var points = [];
|
|
6730
|
+
for (var i = 0; i <= n; i++) {
|
|
6731
|
+
var t = i / n;
|
|
6732
|
+
var p;
|
|
6733
|
+
if (sinAngle < EPS$2) {
|
|
6734
|
+
p = av;
|
|
6735
|
+
} else {
|
|
6736
|
+
var ka = Math.sin((1 - t) * angle) / sinAngle;
|
|
6737
|
+
var kb = Math.sin(t * angle) / sinAngle;
|
|
6738
|
+
p = [
|
|
6739
|
+
av[0] * ka + bv[0] * kb,
|
|
6740
|
+
av[1] * ka + bv[1] * kb,
|
|
6741
|
+
av[2] * ka + bv[2] * kb
|
|
6742
|
+
];
|
|
6743
|
+
}
|
|
6744
|
+
points.push(vectorToDegrees$2(normalizeVector$2(p)));
|
|
6745
|
+
}
|
|
6746
|
+
return points;
|
|
6747
|
+
}
|
|
6748
|
+
|
|
6749
|
+
function splitPathAtAntimeridian$2(path) {
|
|
6750
|
+
var paths = [];
|
|
6751
|
+
var part = [path[0]];
|
|
6752
|
+
for (var i = 1; i < path.length; i++) {
|
|
6753
|
+
var a = path[i - 1];
|
|
6754
|
+
var b = path[i];
|
|
6755
|
+
if (Math.abs(a[0] - b[0]) > 180) {
|
|
6756
|
+
var bLon = b[0] + (b[0] < a[0] ? 360 : -360);
|
|
6757
|
+
var edgeLon = a[0] < 0 ? -180 : 180;
|
|
6758
|
+
var t = (edgeLon - a[0]) / (bLon - a[0]);
|
|
6759
|
+
var lat = a[1] + (b[1] - a[1]) * t;
|
|
6760
|
+
part.push([edgeLon, lat]);
|
|
6761
|
+
paths.push(part);
|
|
6762
|
+
part = [[-edgeLon, lat], b];
|
|
6763
|
+
} else {
|
|
6764
|
+
part.push(b);
|
|
6765
|
+
}
|
|
6766
|
+
}
|
|
6767
|
+
if (part.length > 1) paths.push(part);
|
|
6768
|
+
return paths;
|
|
6769
|
+
}
|
|
6770
|
+
|
|
6771
|
+
function getBounds(rings) {
|
|
6772
|
+
var bounds = [Infinity, Infinity, -Infinity, -Infinity];
|
|
6773
|
+
rings.forEach(function(ring) {
|
|
6774
|
+
ring.forEach(function(p) {
|
|
6775
|
+
bounds[0] = Math.min(bounds[0], p[0]);
|
|
6776
|
+
bounds[1] = Math.min(bounds[1], p[1]);
|
|
6777
|
+
bounds[2] = Math.max(bounds[2], p[0]);
|
|
6778
|
+
bounds[3] = Math.max(bounds[3], p[1]);
|
|
6779
|
+
});
|
|
6780
|
+
});
|
|
6781
|
+
return bounds;
|
|
6782
|
+
}
|
|
6783
|
+
|
|
6784
|
+
function angularDistance(a, b) {
|
|
6785
|
+
return Math.acos(clamp$3(dot$2(degreesToVector$2(a[0], a[1]),
|
|
6786
|
+
degreesToVector$2(b[0], b[1])), -1, 1));
|
|
6787
|
+
}
|
|
6788
|
+
|
|
6789
|
+
function degreesToVector$2(lon, lat) {
|
|
6790
|
+
return radiansToVector$1(lon * D2R$6, lat * D2R$6);
|
|
6791
|
+
}
|
|
6792
|
+
|
|
6793
|
+
function radiansToVector$1(lam, phi) {
|
|
6794
|
+
var cosPhi = Math.cos(phi);
|
|
6795
|
+
return [Math.cos(lam) * cosPhi, Math.sin(lam) * cosPhi, Math.sin(phi)];
|
|
6796
|
+
}
|
|
6797
|
+
|
|
6798
|
+
function vectorToDegrees$2(p) {
|
|
6799
|
+
return [
|
|
6800
|
+
Math.atan2(p[1], p[0]) * R2D$5,
|
|
6801
|
+
Math.asin(clamp$3(p[2], -1, 1)) * R2D$5
|
|
6802
|
+
];
|
|
6803
|
+
}
|
|
6804
|
+
|
|
6805
|
+
function normalizeVector$2(p) {
|
|
6806
|
+
var k = 1 / Math.sqrt(dot$2(p, p));
|
|
6807
|
+
return [p[0] * k, p[1] * k, p[2] * k];
|
|
6808
|
+
}
|
|
6809
|
+
|
|
6810
|
+
function cross$2(a, b) {
|
|
6811
|
+
return [
|
|
6812
|
+
a[1] * b[2] - a[2] * b[1],
|
|
6813
|
+
a[2] * b[0] - a[0] * b[2],
|
|
6814
|
+
a[0] * b[1] - a[1] * b[0]
|
|
6815
|
+
];
|
|
6816
|
+
}
|
|
6817
|
+
|
|
6818
|
+
function dot$2(a, b) {
|
|
6819
|
+
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
6820
|
+
}
|
|
6821
|
+
|
|
6822
|
+
function distance2D(a, b) {
|
|
6823
|
+
var dx = b[0] - a[0];
|
|
6824
|
+
var dy = b[1] - a[1];
|
|
6825
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
6826
|
+
}
|
|
6827
|
+
|
|
6828
|
+
function sameSphericalPoint(a, b) {
|
|
6829
|
+
return Math.abs(a[0] - b[0]) < EPS$2 && Math.abs(a[1] - b[1]) < EPS$2;
|
|
6830
|
+
}
|
|
6831
|
+
|
|
6832
|
+
function pointsAlmostEqual(a, b) {
|
|
6833
|
+
return Math.abs(a[0] - b[0]) < 1e-9 && Math.abs(a[1] - b[1]) < 1e-9;
|
|
6834
|
+
}
|
|
6835
|
+
|
|
6836
|
+
function pointKey$1(p) {
|
|
6837
|
+
return p[0].toFixed(12) + ',' + p[1].toFixed(12);
|
|
6838
|
+
}
|
|
6839
|
+
|
|
6840
|
+
function edgeKey(a, b) {
|
|
6841
|
+
var ka = pointKey$1(a);
|
|
6842
|
+
var kb = pointKey$1(b);
|
|
6843
|
+
return ka < kb ? ka + '|' + kb : kb + '|' + ka;
|
|
6844
|
+
}
|
|
6845
|
+
|
|
6846
|
+
function pairKey(a, b) {
|
|
6847
|
+
return a < b ? a + '~' + b : b + '~' + a;
|
|
6848
|
+
}
|
|
6849
|
+
|
|
6850
|
+
function normalizeRadians$2(lam) {
|
|
6851
|
+
while (lam > Math.PI) lam -= Math.PI * 2;
|
|
6852
|
+
while (lam < -Math.PI) lam += Math.PI * 2;
|
|
6853
|
+
return lam;
|
|
6854
|
+
}
|
|
6855
|
+
|
|
6856
|
+
function normalizeLongitude$2(lon) {
|
|
6857
|
+
while (lon > 180) lon -= 360;
|
|
6858
|
+
while (lon < -180) lon += 360;
|
|
6859
|
+
return lon;
|
|
6860
|
+
}
|
|
6861
|
+
|
|
6862
|
+
function clamp$3(val, min, max) {
|
|
6863
|
+
return Math.max(min, Math.min(max, val));
|
|
6864
|
+
}
|
|
6865
|
+
|
|
6866
|
+
/*
|
|
6867
|
+
* Buckminster Fuller's Airocean arrangement of the icosahedron.
|
|
6868
|
+
*
|
|
6869
|
+
* Face layout adapted from d3-geo-polygon (ISC license):
|
|
6870
|
+
* https://github.com/d3/d3-geo-polygon/blob/main/src/airocean.js
|
|
6871
|
+
*
|
|
6872
|
+
* The Gray-Fuller raw transform below is based on Robert W. Gray's published
|
|
6873
|
+
* equations and Philippe Rivière's public-domain clean-room implementation:
|
|
6874
|
+
* https://github.com/d3/d3-geo-polygon/blob/main/src/grayfuller.js
|
|
6875
|
+
*/
|
|
6876
|
+
|
|
6877
|
+
|
|
6878
|
+
var D2R$5 = Math.PI / 180;
|
|
6879
|
+
var R2D$4 = 180 / Math.PI;
|
|
6880
|
+
var SQRT3$1 = Math.sqrt(3);
|
|
6881
|
+
var GRAY_Z = Math.sqrt(5 + 2 * Math.sqrt(5)) / Math.sqrt(15);
|
|
6882
|
+
var GRAY_EL = Math.sqrt(8) / Math.sqrt(5 + Math.sqrt(5));
|
|
6883
|
+
var GRAY_DVE = Math.sqrt(3 + Math.sqrt(5)) / Math.sqrt(5 + Math.sqrt(5));
|
|
6884
|
+
var ROTATION = [-83.65929, 25.44458, -87.45184];
|
|
6885
|
+
var PARENTS = [
|
|
6886
|
+
-1, 0, 1, 11, 13,
|
|
6887
|
+
6, 7, 1, 7, 8,
|
|
6888
|
+
9, 10, 11, 12, 13,
|
|
6889
|
+
6, 8, 10, 17, 21,
|
|
6890
|
+
16, 15, 19, 19
|
|
6891
|
+
];
|
|
6892
|
+
var engines$2 = {};
|
|
6893
|
+
|
|
6894
|
+
function registerDymaxionProjections(mproj) {
|
|
6895
|
+
// Some unit tests import source modules directly in an ESM context where
|
|
6896
|
+
// optional CommonJS dependencies are intentionally unavailable.
|
|
6897
|
+
if (!mproj) return;
|
|
6898
|
+
register('dymaxion', 'Dymaxion (Gray-Fuller facets)', 'fuller');
|
|
6899
|
+
register('dymaxion2', 'Dymaxion (Gnomonic facets)', 'gnomonic');
|
|
6900
|
+
|
|
6901
|
+
function register(id, name, method) {
|
|
6902
|
+
if (mproj.internal.pj_list[id]) return;
|
|
6903
|
+
mproj.pj_add(function(P) {
|
|
6904
|
+
initDymaxion(P, method);
|
|
6905
|
+
}, id, name);
|
|
6906
|
+
}
|
|
6907
|
+
}
|
|
6908
|
+
|
|
6909
|
+
function getDymaxionEngine(method) {
|
|
6910
|
+
method = method || 'fuller';
|
|
6911
|
+
if (!engines$2[method]) {
|
|
6912
|
+
var data = createAiroceanFaces();
|
|
6913
|
+
engines$2[method] = createPolyhedralProjection({
|
|
6914
|
+
faces: data.faces,
|
|
6915
|
+
faceSites: data.sites,
|
|
6916
|
+
parents: PARENTS,
|
|
6917
|
+
rotation: ROTATION,
|
|
6918
|
+
angle: -60,
|
|
6919
|
+
faceProjector: method == 'gnomonic' ?
|
|
6920
|
+
createGnomonicFaceProjector :
|
|
6921
|
+
createGrayFullerFaceProjector
|
|
6922
|
+
});
|
|
6923
|
+
}
|
|
6924
|
+
return engines$2[method];
|
|
6925
|
+
}
|
|
6926
|
+
|
|
6927
|
+
function initDymaxion(P, method) {
|
|
6928
|
+
var engine = getDymaxionEngine(method);
|
|
6929
|
+
P.es = 0;
|
|
6930
|
+
P.inv = null;
|
|
6931
|
+
P.fwd = function(lp, xy) {
|
|
6932
|
+
var p = engine.forward(lp.lam, lp.phi);
|
|
6933
|
+
if (!p) {
|
|
6934
|
+
xy.x = xy.y = Infinity;
|
|
6935
|
+
} else {
|
|
6936
|
+
xy.x = p[0];
|
|
6937
|
+
xy.y = p[1];
|
|
6938
|
+
}
|
|
6939
|
+
};
|
|
6940
|
+
P.__projection_topology = engine.getTopology(P.lam0 * R2D$4);
|
|
6941
|
+
P.__projected_outline = engine.outline;
|
|
6942
|
+
}
|
|
6943
|
+
|
|
6944
|
+
function createAiroceanFaces() {
|
|
6945
|
+
var theta = Math.atan(0.5) * R2D$4;
|
|
6946
|
+
var vertices = [[0, 90], [0, -90]];
|
|
6947
|
+
var faces;
|
|
6948
|
+
var sites;
|
|
6949
|
+
for (var i = 0; i < 10; i++) {
|
|
6950
|
+
vertices.push([(i * 36 + 180) % 360 - 180, i & 1 ? theta : -theta]);
|
|
6951
|
+
}
|
|
6952
|
+
faces = [
|
|
6953
|
+
[0, 3, 11],
|
|
6954
|
+
[0, 5, 3],
|
|
6955
|
+
[0, 7, 5],
|
|
6956
|
+
[0, 9, 7],
|
|
6957
|
+
[0, 11, 9],
|
|
6958
|
+
[2, 11, 3],
|
|
6959
|
+
[3, 4, 2],
|
|
6960
|
+
[4, 3, 5],
|
|
6961
|
+
[5, 6, 4],
|
|
6962
|
+
[6, 5, 7],
|
|
6963
|
+
[7, 8, 6],
|
|
6964
|
+
[8, 7, 9],
|
|
6965
|
+
[9, 10, 8],
|
|
6966
|
+
[10, 9, 11],
|
|
6967
|
+
[11, 2, 10],
|
|
6968
|
+
[1, 2, 4],
|
|
6969
|
+
[1, 4, 6],
|
|
6970
|
+
[1, 6, 8],
|
|
6971
|
+
[1, 8, 10],
|
|
6972
|
+
[1, 10, 2]
|
|
6973
|
+
].map(function(ids) {
|
|
6974
|
+
return ids.map(function(id) {
|
|
6975
|
+
return vertices[id];
|
|
6976
|
+
});
|
|
6977
|
+
});
|
|
6978
|
+
sites = faces.map(getSphericalCentroid);
|
|
6979
|
+
splitAiroceanFaces(faces, sites);
|
|
6980
|
+
return {faces: faces, sites: sites};
|
|
6981
|
+
}
|
|
6982
|
+
|
|
6983
|
+
function splitAiroceanFaces(faces, sites) {
|
|
6984
|
+
var face = faces[15];
|
|
6985
|
+
var site = sites[15];
|
|
6986
|
+
var original = face.concat();
|
|
6987
|
+
face[0] = site;
|
|
6988
|
+
faces.push([original[0], site, original[2]]);
|
|
6989
|
+
sites.push(site);
|
|
6990
|
+
faces.push([original[0], original[1], site]);
|
|
6991
|
+
sites.push(site);
|
|
6992
|
+
|
|
6993
|
+
face = faces[14];
|
|
6994
|
+
site = sites[14];
|
|
6995
|
+
original = face.concat();
|
|
6996
|
+
var mid = getGreatCircleMidpoint(face[1], face[2]);
|
|
6997
|
+
face[1] = mid;
|
|
6998
|
+
faces.push([original[0], original[1], mid]);
|
|
6999
|
+
sites.push(site);
|
|
7000
|
+
|
|
7001
|
+
face = faces[19];
|
|
7002
|
+
site = sites[19];
|
|
7003
|
+
original = face.concat();
|
|
7004
|
+
face[1] = mid;
|
|
7005
|
+
faces.push([mid, original[0], original[1]]);
|
|
7006
|
+
sites.push(site);
|
|
7007
|
+
}
|
|
7008
|
+
|
|
7009
|
+
function createGrayFullerFaceProjector(face) {
|
|
7010
|
+
var c = face.centroid;
|
|
7011
|
+
var direction = Math.abs(c[1] - 52.62) < 1 ||
|
|
7012
|
+
Math.abs(c[1] + 10.81) < 1 ? 0 : 60;
|
|
7013
|
+
var rotation = [-c[0], -c[1], direction];
|
|
7014
|
+
return function(lam, phi) {
|
|
7015
|
+
var p = rotateSphericalRadians(lam, phi, rotation);
|
|
7016
|
+
var q = grayFullerRaw(p[0], p[1]);
|
|
7017
|
+
// D3's face projections use screen-oriented local y coordinates; retaining
|
|
7018
|
+
// that convention reproduces the published Airocean unfolding matrices.
|
|
7019
|
+
return [q[0], -q[1]];
|
|
7020
|
+
};
|
|
7021
|
+
}
|
|
7022
|
+
|
|
7023
|
+
function createGnomonicFaceProjector(face) {
|
|
7024
|
+
var c = face.centroid;
|
|
7025
|
+
var rotation = [-c[0], -c[1], 0];
|
|
7026
|
+
return function(lam, phi) {
|
|
7027
|
+
var p = rotateSphericalRadians(lam, phi, rotation);
|
|
7028
|
+
var cosPhi = Math.cos(p[1]);
|
|
7029
|
+
var k = cosPhi * Math.cos(p[0]);
|
|
7030
|
+
return [
|
|
7031
|
+
cosPhi * Math.sin(p[0]) / k,
|
|
7032
|
+
-Math.sin(p[1]) / k
|
|
7033
|
+
];
|
|
7034
|
+
};
|
|
7035
|
+
}
|
|
7036
|
+
|
|
7037
|
+
function grayFullerRaw(lam, phi) {
|
|
7038
|
+
var cosPhi = Math.cos(phi);
|
|
7039
|
+
var s = GRAY_Z / (cosPhi * Math.cos(lam));
|
|
7040
|
+
var x = cosPhi * Math.sin(lam) * s;
|
|
7041
|
+
var y = Math.sin(phi) * s;
|
|
7042
|
+
var a1p = Math.atan2(2 * y / SQRT3$1 + GRAY_EL / 3 - GRAY_EL / 2, GRAY_DVE);
|
|
7043
|
+
var a2p = Math.atan2(x - y / SQRT3$1 + GRAY_EL / 3 - GRAY_EL / 2, GRAY_DVE);
|
|
7044
|
+
var a3p = Math.atan2(GRAY_EL / 3 - x - y / SQRT3$1 - GRAY_EL / 2, GRAY_DVE);
|
|
7045
|
+
return [SQRT3$1 * (a2p - a3p), 2 * a1p - a2p - a3p];
|
|
7046
|
+
}
|
|
7047
|
+
|
|
7048
|
+
function getSphericalCentroid(coords) {
|
|
7049
|
+
var sum = [0, 0, 0];
|
|
7050
|
+
coords.forEach(function(p) {
|
|
7051
|
+
var v = degreesToVector$1(p);
|
|
7052
|
+
sum[0] += v[0];
|
|
7053
|
+
sum[1] += v[1];
|
|
7054
|
+
sum[2] += v[2];
|
|
7055
|
+
});
|
|
7056
|
+
return vectorToDegrees$1(normalize(sum));
|
|
7057
|
+
}
|
|
7058
|
+
|
|
7059
|
+
function getGreatCircleMidpoint(a, b) {
|
|
7060
|
+
var av = degreesToVector$1(a);
|
|
7061
|
+
var bv = degreesToVector$1(b);
|
|
7062
|
+
return vectorToDegrees$1(normalize([
|
|
7063
|
+
av[0] + bv[0],
|
|
7064
|
+
av[1] + bv[1],
|
|
7065
|
+
av[2] + bv[2]
|
|
7066
|
+
]));
|
|
7067
|
+
}
|
|
7068
|
+
|
|
7069
|
+
function degreesToVector$1(p) {
|
|
7070
|
+
var lam = p[0] * D2R$5;
|
|
7071
|
+
var phi = p[1] * D2R$5;
|
|
7072
|
+
var cosPhi = Math.cos(phi);
|
|
7073
|
+
return [Math.cos(lam) * cosPhi, Math.sin(lam) * cosPhi, Math.sin(phi)];
|
|
7074
|
+
}
|
|
7075
|
+
|
|
7076
|
+
function vectorToDegrees$1(p) {
|
|
7077
|
+
return [
|
|
7078
|
+
Math.atan2(p[1], p[0]) * R2D$4,
|
|
7079
|
+
Math.asin(Math.max(-1, Math.min(1, p[2]))) * R2D$4
|
|
7080
|
+
];
|
|
7081
|
+
}
|
|
7082
|
+
|
|
7083
|
+
function normalize(p) {
|
|
7084
|
+
var k = 1 / Math.sqrt(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]);
|
|
7085
|
+
return [p[0] * k, p[1] * k, p[2] * k];
|
|
7086
|
+
}
|
|
7087
|
+
|
|
7088
|
+
var Dymaxion = /*#__PURE__*/Object.freeze({
|
|
7089
|
+
__proto__: null,
|
|
7090
|
+
getDymaxionEngine: getDymaxionEngine,
|
|
7091
|
+
registerDymaxionProjections: registerDymaxionProjections
|
|
7092
|
+
});
|
|
7093
|
+
|
|
7094
|
+
/*
|
|
7095
|
+
* Vertex-oriented great-circle "slice-and-dice" projection.
|
|
7096
|
+
*
|
|
7097
|
+
* Based on van Leeuwen and Strebe (2006), with the vector formulation
|
|
7098
|
+
* described by Hall et al. (2020) and implemented independently by DGGAL.
|
|
7099
|
+
* https://doi.org/10.1559/152304006779500687
|
|
7100
|
+
* https://doi.org/10.3390/ijgi9050315
|
|
7101
|
+
*/
|
|
7102
|
+
|
|
7103
|
+
|
|
7104
|
+
function createGnomonicProjector(center) {
|
|
7105
|
+
var rotation = [-center[0], -center[1], 0];
|
|
7106
|
+
return function(lam, phi) {
|
|
7107
|
+
var p = rotateSphericalRadians(lam, phi, rotation);
|
|
7108
|
+
var cosPhi = Math.cos(p[1]);
|
|
7109
|
+
var k = cosPhi * Math.cos(p[0]);
|
|
7110
|
+
return [
|
|
7111
|
+
cosPhi * Math.sin(p[0]) / k,
|
|
7112
|
+
-Math.sin(p[1]) / k
|
|
7113
|
+
];
|
|
7114
|
+
};
|
|
7115
|
+
}
|
|
7116
|
+
|
|
7117
|
+
/*
|
|
7118
|
+
* Cahill-Keyes 12-zone forward transform.
|
|
7119
|
+
*
|
|
7120
|
+
* Implemented in Perl by Mary Jo Graça (2011), ported to D3.js by
|
|
7121
|
+
* Enrico Spinielli (2013), and adapted here from d3-geo-polygon.
|
|
7122
|
+
*
|
|
7123
|
+
* Copyright 2017-2024 Mike Bostock
|
|
7124
|
+
* ISC License: https://github.com/d3/d3-geo-polygon/blob/main/LICENSE
|
|
7125
|
+
*/
|
|
7126
|
+
|
|
7127
|
+
var D2R$4 = Math.PI / 180;
|
|
7128
|
+
var R2D$3 = 180 / Math.PI;
|
|
7129
|
+
|
|
7130
|
+
function createCahillKeyesRaw(mg) {
|
|
7131
|
+
return createCahillKeyesTransform(mg, false);
|
|
7132
|
+
}
|
|
7133
|
+
|
|
7134
|
+
function createCahillKeyesTransform(mg, faceOnly) {
|
|
7135
|
+
var CK = {lengthMG: mg};
|
|
7136
|
+
init();
|
|
7137
|
+
|
|
7138
|
+
return function(lambda, phi) {
|
|
7139
|
+
if (faceOnly) {
|
|
7140
|
+
var lon = lambda * R2D$3;
|
|
7141
|
+
var side = lon < 0 ? -1 : lon > 0 ? 1 : 0;
|
|
7142
|
+
var local = mp2xy(Math.abs(lon), Math.abs(phi * R2D$3));
|
|
7143
|
+
return [local[0], side * local[1]];
|
|
7144
|
+
}
|
|
7145
|
+
var res = ll2mp(lambda * R2D$3, phi * R2D$3);
|
|
7146
|
+
var xy = mp2xy(res[0], res[1]);
|
|
7147
|
+
var p = [xy[0], res[2] * xy[1]];
|
|
7148
|
+
return mj2g(p, res[3]);
|
|
7149
|
+
};
|
|
7150
|
+
|
|
7151
|
+
function init() {
|
|
7152
|
+
var k = Math.sqrt(3);
|
|
7153
|
+
var pointN, lengthMB, lengthMN, lengthNG, pointU;
|
|
7154
|
+
var p73a, lF, lT, lM, l, pointV;
|
|
7155
|
+
CK.lengthMA = 0.094 * CK.lengthMG;
|
|
7156
|
+
CK.lengthParallel0to73At0 = CK.lengthMG / 100;
|
|
7157
|
+
CK.lengthParallel73to90At0 =
|
|
7158
|
+
(CK.lengthMG - CK.lengthMA - CK.lengthParallel0to73At0 * 73) / 17;
|
|
7159
|
+
CK.sin60 = k / 2;
|
|
7160
|
+
CK.cos60 = 0.5;
|
|
7161
|
+
CK.pointM = [0, 0];
|
|
7162
|
+
CK.pointG = [CK.lengthMG, 0];
|
|
7163
|
+
pointN = [CK.lengthMG, CK.lengthMG * Math.tan(30 * D2R$4)];
|
|
7164
|
+
CK.pointA = [CK.lengthMA, 0];
|
|
7165
|
+
CK.pointB = lineIntersection(CK.pointM, 30, CK.pointA, 45);
|
|
7166
|
+
CK.lengthAG = distance(CK.pointA, CK.pointG);
|
|
7167
|
+
CK.lengthAB = distance(CK.pointA, CK.pointB);
|
|
7168
|
+
lengthMB = distance(CK.pointM, CK.pointB);
|
|
7169
|
+
lengthMN = distance(CK.pointM, pointN);
|
|
7170
|
+
lengthNG = distance(pointN, CK.pointG);
|
|
7171
|
+
CK.pointD = interpolate(lengthMB, lengthMN, pointN, CK.pointM);
|
|
7172
|
+
CK.pointF = [CK.lengthMG, lengthNG - lengthMB];
|
|
7173
|
+
CK.pointE = [
|
|
7174
|
+
pointN[0] - CK.lengthMA * Math.sin(30 * D2R$4),
|
|
7175
|
+
pointN[1] - CK.lengthMA * Math.cos(30 * D2R$4)
|
|
7176
|
+
];
|
|
7177
|
+
CK.lengthGF = distance(CK.pointG, CK.pointF);
|
|
7178
|
+
CK.lengthBD = distance(CK.pointB, CK.pointD);
|
|
7179
|
+
CK.lengthGFE = CK.lengthGF + CK.lengthAB;
|
|
7180
|
+
CK.deltaMEq = CK.lengthGFE / 45;
|
|
7181
|
+
CK.lengthAP75 = 15 * CK.lengthParallel73to90At0;
|
|
7182
|
+
CK.lengthAP73 = CK.lengthMG - CK.lengthMA -
|
|
7183
|
+
CK.lengthParallel0to73At0 * 73;
|
|
7184
|
+
pointU = [
|
|
7185
|
+
CK.pointA[0] + CK.lengthAP73 * Math.cos(30 * D2R$4),
|
|
7186
|
+
CK.pointA[1] + CK.lengthAP73 * Math.sin(30 * D2R$4)
|
|
7187
|
+
];
|
|
7188
|
+
CK.pointT = lineIntersection(pointU, -60, CK.pointB, 30);
|
|
7189
|
+
|
|
7190
|
+
p73a = parallel73(29);
|
|
7191
|
+
lF = p73a.lengthParallel73;
|
|
7192
|
+
lT = lengthTorridSegment(29);
|
|
7193
|
+
lM = lengthMiddleSegment(29);
|
|
7194
|
+
l = 15 * (lT + lM + lF) / 73 - lT;
|
|
7195
|
+
pointV = interpolate(l, lM, jointT(29), jointF(29));
|
|
7196
|
+
CK.pointC = [0, 0];
|
|
7197
|
+
CK.pointC[1] = (
|
|
7198
|
+
pointV[0] * pointV[0] + pointV[1] * pointV[1] -
|
|
7199
|
+
CK.pointD[0] * CK.pointD[0] - CK.pointD[1] * CK.pointD[1]
|
|
7200
|
+
) / (
|
|
7201
|
+
2 * (k * pointV[0] + pointV[1] -
|
|
7202
|
+
k * CK.pointD[0] - CK.pointD[1])
|
|
7203
|
+
);
|
|
7204
|
+
CK.pointC[0] = k * CK.pointC[1];
|
|
7205
|
+
CK.radius = distance(CK.pointC, CK.pointD);
|
|
7206
|
+
}
|
|
7207
|
+
|
|
7208
|
+
function ll2mp(lon, lat) {
|
|
7209
|
+
var south = [0, 6, 7, 8, 5];
|
|
7210
|
+
var octant = truncate((lon + 180) / 90 + 1);
|
|
7211
|
+
var meridian = (lon + 720) % 90 - 45;
|
|
7212
|
+
var side = meridian < 0 ? -1 : meridian > 0 ? 1 : 0;
|
|
7213
|
+
meridian = Math.abs(meridian);
|
|
7214
|
+
if (octant === 5) octant = 1;
|
|
7215
|
+
if (lat < 0) octant = south[octant];
|
|
7216
|
+
return [meridian, Math.abs(lat), side, octant];
|
|
7217
|
+
}
|
|
7218
|
+
|
|
7219
|
+
function mp2xy(m, p) {
|
|
7220
|
+
if (m === 0) return p >= 75 ? zoneA(m, p) : zoneB(m, p);
|
|
7221
|
+
if (p >= 75) return zoneC(m, p);
|
|
7222
|
+
if (p === 0) return zoneD(m);
|
|
7223
|
+
if (p >= 73 && m <= 30) return zoneE(m, p);
|
|
7224
|
+
if (m === 45) {
|
|
7225
|
+
return p <= 15 ? zoneF(m, p) :
|
|
7226
|
+
p <= 73 ? zoneG(m, p) : zoneH(m, p);
|
|
7227
|
+
}
|
|
7228
|
+
if (m <= 29) return zoneI(m, p);
|
|
7229
|
+
if (p >= 73) return zoneJ(m, p);
|
|
7230
|
+
var lT = lengthTorridSegment(m);
|
|
7231
|
+
var hit = circleLineIntersection(
|
|
7232
|
+
CK.pointC, CK.radius, jointT(m), jointF(m)
|
|
7233
|
+
);
|
|
7234
|
+
var l15;
|
|
7235
|
+
if (hit[0]) {
|
|
7236
|
+
l15 = lT + distance(jointT(m), hit[1]);
|
|
7237
|
+
} else {
|
|
7238
|
+
hit = circleLineIntersection(
|
|
7239
|
+
CK.pointC, CK.radius, jointE(m), jointT(m)
|
|
7240
|
+
);
|
|
7241
|
+
l15 = lT - distance(jointT(m), hit[1]);
|
|
7242
|
+
}
|
|
7243
|
+
return p <= 15 ? zoneK(m, p, l15) : zoneL(m, p, l15);
|
|
7244
|
+
}
|
|
7245
|
+
|
|
7246
|
+
function zoneA(m, p) {
|
|
7247
|
+
return [CK.pointA[0] + (90 - p) * 104, 0];
|
|
7248
|
+
}
|
|
7249
|
+
|
|
7250
|
+
function zoneB(m, p) {
|
|
7251
|
+
return [CK.pointG[0] - p * 100, 0];
|
|
7252
|
+
}
|
|
7253
|
+
|
|
7254
|
+
function zoneC(m, p) {
|
|
7255
|
+
return radialPoint(CK.pointA, 104 * (90 - p), m);
|
|
7256
|
+
}
|
|
7257
|
+
|
|
7258
|
+
function zoneD(m) {
|
|
7259
|
+
return equator(m);
|
|
7260
|
+
}
|
|
7261
|
+
|
|
7262
|
+
function zoneE(m, p) {
|
|
7263
|
+
return radialPoint(CK.pointA, 1560 + (75 - p) * 100, m);
|
|
7264
|
+
}
|
|
7265
|
+
|
|
7266
|
+
function zoneF(m, p) {
|
|
7267
|
+
return interpolate(p, 15, CK.pointE, CK.pointD);
|
|
7268
|
+
}
|
|
7269
|
+
|
|
7270
|
+
function zoneG(m, p) {
|
|
7271
|
+
return interpolate(p - 15, 58, CK.pointD, CK.pointT);
|
|
7272
|
+
}
|
|
7273
|
+
|
|
7274
|
+
function zoneH(m, p) {
|
|
7275
|
+
var p75 = parallel75(45);
|
|
7276
|
+
var p73 = parallel73(m).parallel73;
|
|
7277
|
+
var lF = distance(CK.pointT, CK.pointB);
|
|
7278
|
+
var lF75 = distance(CK.pointB, p75);
|
|
7279
|
+
var l = (75 - p) * (lF75 + lF) / 2;
|
|
7280
|
+
return l <= lF75 ?
|
|
7281
|
+
interpolate(l, lF75, p75, CK.pointB) :
|
|
7282
|
+
interpolate(l - lF75, lF, CK.pointB, p73);
|
|
7283
|
+
}
|
|
7284
|
+
|
|
7285
|
+
function zoneI(m, p) {
|
|
7286
|
+
var p73a = parallel73(m);
|
|
7287
|
+
var lT = lengthTorridSegment(m);
|
|
7288
|
+
var lM = lengthMiddleSegment(m);
|
|
7289
|
+
var l = p * (lT + lM + p73a.lengthParallel73) / 73;
|
|
7290
|
+
if (l <= lT) return interpolate(l, lT, jointE(m), jointT(m));
|
|
7291
|
+
if (l <= lT + lM) {
|
|
7292
|
+
return interpolate(l - lT, lM, jointT(m), jointF(m));
|
|
7293
|
+
}
|
|
7294
|
+
return interpolate(
|
|
7295
|
+
l - lT - lM,
|
|
7296
|
+
p73a.lengthParallel73,
|
|
7297
|
+
jointF(m),
|
|
7298
|
+
p73a.parallel73
|
|
7299
|
+
);
|
|
7300
|
+
}
|
|
7301
|
+
|
|
7302
|
+
function zoneJ(m, p) {
|
|
7303
|
+
var p75 = parallel75(m);
|
|
7304
|
+
var p73a = parallel73(m);
|
|
7305
|
+
var lF75 = distance(jointF(m), p75);
|
|
7306
|
+
var l = (75 - p) * (lF75 - p73a.lengthParallel73) / 2;
|
|
7307
|
+
return l <= lF75 ?
|
|
7308
|
+
interpolate(l, lF75, p75, jointF(m)) :
|
|
7309
|
+
interpolate(
|
|
7310
|
+
l - lF75,
|
|
7311
|
+
-p73a.lengthParallel73,
|
|
7312
|
+
jointF(m),
|
|
7313
|
+
p73a.parallel73
|
|
7314
|
+
);
|
|
7315
|
+
}
|
|
7316
|
+
|
|
7317
|
+
function zoneK(m, p, l15) {
|
|
7318
|
+
var l = p * l15 / 15;
|
|
7319
|
+
var lT = lengthTorridSegment(m);
|
|
7320
|
+
var lM = lengthMiddleSegment(m);
|
|
7321
|
+
return l <= lT ?
|
|
7322
|
+
interpolate(l, lT, jointE(m), jointT(m)) :
|
|
7323
|
+
interpolate(l - lT, lM, jointT(m), jointF(m));
|
|
7324
|
+
}
|
|
7325
|
+
|
|
7326
|
+
function zoneL(m, p, l15) {
|
|
7327
|
+
var p73a = parallel73(m);
|
|
7328
|
+
var lT = lengthTorridSegment(m);
|
|
7329
|
+
var lM = lengthMiddleSegment(m);
|
|
7330
|
+
var lF = p73a.lengthParallel73;
|
|
7331
|
+
var l = l15 + (p - 15) * (lT + lM + lF - l15) / 58;
|
|
7332
|
+
if (l <= lT) return interpolate(l, lT, jointE(m), jointF(m));
|
|
7333
|
+
if (l <= lT + lM) {
|
|
7334
|
+
return interpolate(l - lT, lM, jointT(m), jointF(m));
|
|
7335
|
+
}
|
|
7336
|
+
return interpolate(
|
|
7337
|
+
l - lT - lM, lF, jointF(m), p73a.parallel73
|
|
7338
|
+
);
|
|
7339
|
+
}
|
|
7340
|
+
|
|
7341
|
+
function equator(m) {
|
|
7342
|
+
var l = CK.deltaMEq * m;
|
|
7343
|
+
return l <= CK.lengthGF ?
|
|
7344
|
+
[CK.pointG[0], l] :
|
|
7345
|
+
interpolate(l - CK.lengthGF, CK.lengthAB, CK.pointF, CK.pointE);
|
|
7346
|
+
}
|
|
7347
|
+
|
|
7348
|
+
function jointE(m) {
|
|
7349
|
+
return equator(m);
|
|
7350
|
+
}
|
|
7351
|
+
|
|
7352
|
+
function jointT(m) {
|
|
7353
|
+
return lineIntersection(CK.pointM, 2 * m / 3, jointE(m), m / 3);
|
|
7354
|
+
}
|
|
7355
|
+
|
|
7356
|
+
function jointF(m) {
|
|
7357
|
+
if (m === 0) return [CK.pointA[0] + CK.lengthAB, 0];
|
|
7358
|
+
return lineIntersection(CK.pointA, m, CK.pointM, 2 * m / 3);
|
|
7359
|
+
}
|
|
7360
|
+
|
|
7361
|
+
function lengthTorridSegment(m) {
|
|
7362
|
+
return distance(jointE(m), jointT(m));
|
|
7363
|
+
}
|
|
7364
|
+
|
|
7365
|
+
function lengthMiddleSegment(m) {
|
|
7366
|
+
return distance(jointT(m), jointF(m));
|
|
7367
|
+
}
|
|
7368
|
+
|
|
7369
|
+
function parallel73(m) {
|
|
7370
|
+
var p73, lF, xy;
|
|
7371
|
+
var jF = jointF(m);
|
|
7372
|
+
if (m <= 30) {
|
|
7373
|
+
p73 = radialPoint(CK.pointA, CK.lengthAP73, m);
|
|
7374
|
+
lF = distance(jF, p73);
|
|
7375
|
+
} else {
|
|
7376
|
+
p73 = lineIntersection(CK.pointT, -60, jF, m);
|
|
7377
|
+
lF = distance(jF, p73);
|
|
7378
|
+
if (m > 44) {
|
|
7379
|
+
xy = lineIntersection(CK.pointT, -60, jF, 2 * m / 3);
|
|
7380
|
+
if (xy[0] > p73[0]) {
|
|
7381
|
+
p73 = xy;
|
|
7382
|
+
lF = -distance(jF, p73);
|
|
7383
|
+
}
|
|
7384
|
+
}
|
|
7385
|
+
}
|
|
7386
|
+
return {parallel73: p73, lengthParallel73: lF};
|
|
7387
|
+
}
|
|
7388
|
+
|
|
7389
|
+
function parallel75(m) {
|
|
7390
|
+
return radialPoint(CK.pointA, CK.lengthAP75, m);
|
|
7391
|
+
}
|
|
7392
|
+
|
|
7393
|
+
function mj2g(xy, octant) {
|
|
7394
|
+
var out;
|
|
7395
|
+
if (octant === 0) {
|
|
7396
|
+
out = rotate(xy, -60);
|
|
7397
|
+
} else if (octant === 1) {
|
|
7398
|
+
out = rotate(xy, -120);
|
|
7399
|
+
out[0] -= CK.lengthMG;
|
|
7400
|
+
} else if (octant === 2) {
|
|
7401
|
+
out = rotate(xy, -60);
|
|
7402
|
+
out[0] -= CK.lengthMG;
|
|
7403
|
+
} else if (octant === 3) {
|
|
7404
|
+
out = rotate(xy, -120);
|
|
7405
|
+
out[0] += CK.lengthMG;
|
|
7406
|
+
} else if (octant === 4) {
|
|
7407
|
+
out = rotate(xy, -60);
|
|
7408
|
+
out[0] += CK.lengthMG;
|
|
7409
|
+
} else if (octant === 5) {
|
|
7410
|
+
out = rotate([2 * CK.lengthMG - xy[0], xy[1]], -60);
|
|
7411
|
+
out[0] += CK.lengthMG;
|
|
7412
|
+
} else if (octant === 6) {
|
|
7413
|
+
out = rotate([2 * CK.lengthMG - xy[0], xy[1]], -120);
|
|
7414
|
+
out[0] -= CK.lengthMG;
|
|
7415
|
+
} else if (octant === 7) {
|
|
7416
|
+
out = rotate([2 * CK.lengthMG - xy[0], xy[1]], -60);
|
|
7417
|
+
out[0] -= CK.lengthMG;
|
|
7418
|
+
} else if (octant === 8) {
|
|
7419
|
+
out = rotate([2 * CK.lengthMG - xy[0], xy[1]], -120);
|
|
7420
|
+
out[0] += CK.lengthMG;
|
|
7421
|
+
}
|
|
7422
|
+
return out;
|
|
7423
|
+
}
|
|
7424
|
+
|
|
7425
|
+
function radialPoint(origin, length, angle) {
|
|
7426
|
+
return [
|
|
7427
|
+
origin[0] + length * Math.cos(angle * D2R$4),
|
|
7428
|
+
origin[1] + length * Math.sin(angle * D2R$4)
|
|
7429
|
+
];
|
|
7430
|
+
}
|
|
7431
|
+
|
|
7432
|
+
function rotate(xy, angle) {
|
|
7433
|
+
if (angle === -60) {
|
|
7434
|
+
return [
|
|
7435
|
+
xy[0] * CK.cos60 + xy[1] * CK.sin60,
|
|
7436
|
+
-xy[0] * CK.sin60 + xy[1] * CK.cos60
|
|
7437
|
+
];
|
|
7438
|
+
}
|
|
7439
|
+
if (angle === -120) {
|
|
7440
|
+
return [
|
|
7441
|
+
-xy[0] * CK.cos60 + xy[1] * CK.sin60,
|
|
7442
|
+
-xy[0] * CK.sin60 - xy[1] * CK.cos60
|
|
7443
|
+
];
|
|
7444
|
+
}
|
|
7445
|
+
var cos = Math.cos(angle * D2R$4);
|
|
7446
|
+
var sin = Math.sin(angle * D2R$4);
|
|
7447
|
+
return [xy[0] * cos - xy[1] * sin, xy[0] * sin + xy[1] * cos];
|
|
7448
|
+
}
|
|
7449
|
+
}
|
|
7450
|
+
|
|
7451
|
+
function createCahillKeyesFaceRaw(mg) {
|
|
7452
|
+
return createCahillKeyesTransform(mg, true);
|
|
7453
|
+
}
|
|
7454
|
+
|
|
7455
|
+
function distance(a, b) {
|
|
7456
|
+
return Math.hypot(a[0] - b[0], a[1] - b[1]);
|
|
7457
|
+
}
|
|
7458
|
+
|
|
7459
|
+
function interpolate(length, total, start, end) {
|
|
7460
|
+
return [
|
|
7461
|
+
start[0] + (end[0] - start[0]) * length / total,
|
|
7462
|
+
start[1] + (end[1] - start[1]) * length / total
|
|
7463
|
+
];
|
|
7464
|
+
}
|
|
7465
|
+
|
|
7466
|
+
function lineIntersection(p1, slope1, p2, slope2) {
|
|
7467
|
+
var m1 = Math.tan(slope1 * D2R$4);
|
|
7468
|
+
var m2 = Math.tan(slope2 * D2R$4);
|
|
7469
|
+
var x = (m1 * p1[0] - m2 * p2[0] - p1[1] + p2[1]) / (m1 - m2);
|
|
7470
|
+
return [x, m1 * (x - p1[0]) + p1[1]];
|
|
7471
|
+
}
|
|
7472
|
+
|
|
7473
|
+
function circleLineIntersection(center, radius, p1, p2) {
|
|
7474
|
+
var dx = p2[0] - p1[0];
|
|
7475
|
+
var dy = p2[1] - p1[1];
|
|
7476
|
+
var fx = p1[0] - center[0];
|
|
7477
|
+
var fy = p1[1] - center[1];
|
|
7478
|
+
var a = dx * dx + dy * dy;
|
|
7479
|
+
var b = 2 * (dx * fx + dy * fy);
|
|
7480
|
+
var c = fx * fx + fy * fy - radius * radius;
|
|
7481
|
+
var d = b * b - 4 * a * c;
|
|
7482
|
+
if (a === 0 || d < 0) return [0, [0, 0]];
|
|
7483
|
+
var root = Math.sqrt(d);
|
|
7484
|
+
var u1 = (-b + root) / (2 * a);
|
|
7485
|
+
var u2 = (-b - root) / (2 * a);
|
|
7486
|
+
var u = u1 >= 0 && u1 <= 1 ? u1 :
|
|
7487
|
+
u2 >= 0 && u2 <= 1 ? u2 : null;
|
|
7488
|
+
return u === null ? [0, [0, 0]] :
|
|
7489
|
+
[1, [p1[0] + u * dx, p1[1] + u * dy]];
|
|
7490
|
+
}
|
|
7491
|
+
|
|
7492
|
+
function truncate(n) {
|
|
7493
|
+
return n > 0 ? Math.floor(n) : Math.ceil(n);
|
|
7494
|
+
}
|
|
7495
|
+
|
|
7496
|
+
var D2R$3 = Math.PI / 180;
|
|
7497
|
+
var EPS$1 = 1e-12;
|
|
7498
|
+
|
|
7499
|
+
// Create a smooth facet projection from any radial azimuthal projection.
|
|
7500
|
+
// The azimuthal coordinates are warped back to the gnomonic polygon along
|
|
7501
|
+
// the boundary, so adjacent facets retain identical straight edges.
|
|
7502
|
+
function createRadialFacetProjector(coords, options) {
|
|
7503
|
+
options = options || {};
|
|
7504
|
+
var center = options.planarCenter || getSphericalCenter(coords);
|
|
7505
|
+
var gnomonic = createGnomonicProjector(center);
|
|
7506
|
+
var planar = coords.map(function(p) {
|
|
7507
|
+
return gnomonic(p[0] * D2R$3, p[1] * D2R$3);
|
|
7508
|
+
});
|
|
7509
|
+
var edges = createNormalizedEdges(planar);
|
|
7510
|
+
var radial = createRadialFunction(
|
|
7511
|
+
options.radial || 'equal-area',
|
|
7512
|
+
options.radial2,
|
|
7513
|
+
options.radialBlend
|
|
7514
|
+
);
|
|
7515
|
+
var boundaryStrength = options.boundaryStrength || 1;
|
|
7516
|
+
var radialScale = getRadialScale(planar, radial) *
|
|
7517
|
+
(options.radialScale || 1);
|
|
7518
|
+
|
|
7519
|
+
project.radial = options.radial || 'equal-area';
|
|
7520
|
+
project.boundaryStrength = boundaryStrength;
|
|
7521
|
+
return project;
|
|
7522
|
+
|
|
7523
|
+
function project(lam, phi) {
|
|
7524
|
+
var p = gnomonic(lam, phi);
|
|
7525
|
+
var r = Math.hypot(p[0], p[1]);
|
|
7526
|
+
if (r < EPS$1) return [0, 0];
|
|
7527
|
+
var c = Math.atan(r);
|
|
7528
|
+
var azimuthalRadius = radial(c) * radialScale;
|
|
7529
|
+
var boundaryWeight = getBoundaryWeight(p, edges, boundaryStrength);
|
|
7530
|
+
var scale = 1 + boundaryWeight * (azimuthalRadius / r - 1);
|
|
7531
|
+
return [p[0] * scale, p[1] * scale];
|
|
7532
|
+
}
|
|
7533
|
+
}
|
|
7534
|
+
|
|
7535
|
+
function createRadialFunction(radial1, radial2, blend) {
|
|
7536
|
+
var f1 = getRadialFunction(radial1);
|
|
7537
|
+
if (!radial2) return f1;
|
|
7538
|
+
var f2 = getRadialFunction(radial2);
|
|
7539
|
+
var k = Math.max(0, Math.min(1, blend == null ? 0.5 : blend));
|
|
7540
|
+
return function(c) {
|
|
7541
|
+
return f1(c) + (f2(c) - f1(c)) * k;
|
|
7542
|
+
};
|
|
7543
|
+
}
|
|
7544
|
+
|
|
7545
|
+
function getRadialFunction(id) {
|
|
7546
|
+
if (typeof id == 'function') return id;
|
|
7547
|
+
id = normalizeRadialProjectionName(id);
|
|
7548
|
+
if (id == 'aeqd') return function(c) {
|
|
7549
|
+
return c;
|
|
7550
|
+
};
|
|
7551
|
+
if (id == 'stere') return function(c) {
|
|
7552
|
+
return 2 * Math.tan(c / 2);
|
|
7553
|
+
};
|
|
7554
|
+
if (id == 'ortho') return function(c) {
|
|
7555
|
+
return Math.sin(c);
|
|
7556
|
+
};
|
|
7557
|
+
if (id == 'gnom') return function(c) {
|
|
7558
|
+
return Math.tan(c);
|
|
7559
|
+
};
|
|
7560
|
+
// Lambert azimuthal equal-area
|
|
7561
|
+
return function(c) {
|
|
7562
|
+
return 2 * Math.sin(c / 2);
|
|
7563
|
+
};
|
|
7564
|
+
}
|
|
7565
|
+
|
|
7566
|
+
function normalizeRadialProjectionName(id) {
|
|
7567
|
+
id = String(id || '').toLowerCase().replace(/_/g, '-');
|
|
7568
|
+
if (id == 'laea' || id == 'lambert' || id == 'equal-area') return 'laea';
|
|
7569
|
+
if (id == 'aeqd' || id == 'equidistant') return 'aeqd';
|
|
7570
|
+
if (id == 'stere' || id == 'stereographic') return 'stere';
|
|
7571
|
+
if (id == 'ortho' || id == 'orthographic') return 'ortho';
|
|
7572
|
+
if (id == 'gnom' || id == 'gnomonic') return 'gnom';
|
|
7573
|
+
throw new Error('Unsupported facet projection: ' + id);
|
|
7574
|
+
}
|
|
7575
|
+
|
|
7576
|
+
function getRadialScale(planar, radial) {
|
|
7577
|
+
var sum = 0;
|
|
7578
|
+
planar.forEach(function(p) {
|
|
7579
|
+
var r = Math.hypot(p[0], p[1]);
|
|
7580
|
+
sum += r / radial(Math.atan(r));
|
|
7581
|
+
});
|
|
7582
|
+
return sum / planar.length;
|
|
7583
|
+
}
|
|
7584
|
+
|
|
7585
|
+
function createNormalizedEdges(planar) {
|
|
7586
|
+
var center = [0, 0];
|
|
7587
|
+
return planar.map(function(a, i) {
|
|
7588
|
+
var b = planar[(i + 1) % planar.length];
|
|
7589
|
+
var dx = b[0] - a[0];
|
|
7590
|
+
var dy = b[1] - a[1];
|
|
7591
|
+
var centerCross = dx * (center[1] - a[1]) -
|
|
7592
|
+
dy * (center[0] - a[0]);
|
|
7593
|
+
return {
|
|
7594
|
+
a: a,
|
|
7595
|
+
dx: dx,
|
|
7596
|
+
dy: dy,
|
|
7597
|
+
denominator: centerCross
|
|
7598
|
+
};
|
|
7599
|
+
});
|
|
7600
|
+
}
|
|
7601
|
+
|
|
7602
|
+
function getBoundaryWeight(p, edges, strength) {
|
|
7603
|
+
var product = 1;
|
|
7604
|
+
for (var i = 0; i < edges.length; i++) {
|
|
7605
|
+
var edge = edges[i];
|
|
7606
|
+
var cross = edge.dx * (p[1] - edge.a[1]) -
|
|
7607
|
+
edge.dy * (p[0] - edge.a[0]);
|
|
7608
|
+
product *= Math.max(0, cross / edge.denominator);
|
|
7609
|
+
}
|
|
7610
|
+
product = Math.max(0, Math.min(1, product));
|
|
7611
|
+
return 1 - Math.pow(1 - product, strength);
|
|
7612
|
+
}
|
|
7613
|
+
|
|
7614
|
+
function getSphericalCenter(coords) {
|
|
7615
|
+
var sum = coords.reduce(function(memo, p) {
|
|
7616
|
+
var lam = p[0] * D2R$3;
|
|
7617
|
+
var phi = p[1] * D2R$3;
|
|
7618
|
+
var cosPhi = Math.cos(phi);
|
|
7619
|
+
memo[0] += Math.cos(lam) * cosPhi;
|
|
7620
|
+
memo[1] += Math.sin(lam) * cosPhi;
|
|
7621
|
+
memo[2] += Math.sin(phi);
|
|
7622
|
+
return memo;
|
|
7623
|
+
}, [0, 0, 0]);
|
|
7624
|
+
return [
|
|
7625
|
+
Math.atan2(sum[1], sum[0]) / D2R$3,
|
|
7626
|
+
Math.atan2(sum[2], Math.hypot(sum[0], sum[1])) / D2R$3
|
|
7627
|
+
];
|
|
7628
|
+
}
|
|
7629
|
+
|
|
7630
|
+
/*
|
|
7631
|
+
* Octahedral butterfly projections.
|
|
7632
|
+
*
|
|
7633
|
+
* Face layouts are adapted from d3-geo-polygon (ISC license):
|
|
7634
|
+
* https://github.com/d3/d3-geo-polygon
|
|
7635
|
+
*
|
|
7636
|
+
* Copyright 2017-2024 Mike Bostock
|
|
7637
|
+
* ISC License: https://github.com/d3/d3-geo-polygon/blob/main/LICENSE
|
|
7638
|
+
*/
|
|
7639
|
+
|
|
7640
|
+
|
|
7641
|
+
var D2R$2 = Math.PI / 180;
|
|
7642
|
+
var R2D$2 = 180 / Math.PI;
|
|
7643
|
+
var RADIAL_BOUNDARY_STRENGTH = 1;
|
|
7644
|
+
var OCTAHEDRON = createOctahedron();
|
|
7645
|
+
var BUTTERFLY_PARENTS = [-1, 0, 0, 1, 0, 1, 4, 5];
|
|
7646
|
+
var DEFAULT_LON0 = {
|
|
7647
|
+
butterfly: 157.5,
|
|
7648
|
+
butterfly2: -20,
|
|
7649
|
+
cahill_keyes: -20
|
|
7650
|
+
};
|
|
7651
|
+
var engines$1 = {};
|
|
7652
|
+
|
|
7653
|
+
function registerButterflyProjections(mproj) {
|
|
7654
|
+
if (!mproj) return;
|
|
7655
|
+
register('butterfly', 'Butterfly projection (Pacific aspect)', 'butterfly');
|
|
7656
|
+
register('butterfly2', 'Butterfly projection (Atlantic aspect)', 'butterfly2');
|
|
7657
|
+
register('cahill_keyes', 'Cahill-Keyes butterfly', 'cahill_keyes');
|
|
7658
|
+
|
|
7659
|
+
function register(id, name, method) {
|
|
7660
|
+
if (mproj.internal.pj_list[id]) return;
|
|
7661
|
+
mproj.pj_add(function(P) {
|
|
7662
|
+
initButterfly(P, method);
|
|
7663
|
+
}, id, name);
|
|
7664
|
+
}
|
|
7665
|
+
}
|
|
7666
|
+
|
|
7667
|
+
function getButterflyEngine(method) {
|
|
7668
|
+
var key = method == 'butterfly2' ? 'butterfly' : method;
|
|
7669
|
+
if (!engines$1[key]) {
|
|
7670
|
+
if (key == 'cahill_keyes') {
|
|
7671
|
+
engines$1[key] = createCahillKeyesEngine();
|
|
7672
|
+
} else if (key == 'butterfly') {
|
|
7673
|
+
engines$1[key] = createButterflyEngine();
|
|
7674
|
+
} else {
|
|
7675
|
+
throw new Error('Unknown butterfly projection: ' + method);
|
|
7676
|
+
}
|
|
7677
|
+
}
|
|
7678
|
+
return engines$1[key];
|
|
7679
|
+
}
|
|
7680
|
+
|
|
7681
|
+
function initButterfly(P, method) {
|
|
7682
|
+
var engine = getButterflyEngine(method);
|
|
7683
|
+
if (!P.params.lon_0) {
|
|
7684
|
+
P.lam0 = DEFAULT_LON0[method] * D2R$2;
|
|
7685
|
+
}
|
|
7686
|
+
P.es = 0;
|
|
7687
|
+
P.inv = null;
|
|
7688
|
+
P.fwd = function(lp, xy) {
|
|
7689
|
+
var p = engine.forward(lp.lam, lp.phi);
|
|
7690
|
+
if (!p) {
|
|
7691
|
+
xy.x = xy.y = Infinity;
|
|
7692
|
+
} else {
|
|
7693
|
+
xy.x = p[0];
|
|
7694
|
+
xy.y = p[1];
|
|
7695
|
+
}
|
|
7696
|
+
};
|
|
7697
|
+
P.__projection_topology = engine.getTopology(P.lam0 * R2D$2);
|
|
7698
|
+
if (engine.removeOutlineExtremeConnectors) {
|
|
7699
|
+
P.__remove_outline_extreme_connectors = true;
|
|
7700
|
+
}
|
|
7701
|
+
if (engine.useProjectedOutline !== false) {
|
|
7702
|
+
P.__projected_outline = engine.outline;
|
|
7703
|
+
}
|
|
7704
|
+
}
|
|
7705
|
+
|
|
7706
|
+
function createButterflyEngine() {
|
|
7707
|
+
var faceCenters = [-45, 45, -45, 45, -135, 135, -135, 135];
|
|
7708
|
+
// Reuse the Cahill-Keyes polar incisions and 12-zone octant transform,
|
|
7709
|
+
// then unfold the faces with the butterfly attachment tree.
|
|
7710
|
+
var data = createTruncatedOctahedronFaces(
|
|
7711
|
+
Math.cos(17 * D2R$2),
|
|
7712
|
+
Math.sin(17 * D2R$2),
|
|
7713
|
+
BUTTERFLY_PARENTS
|
|
7714
|
+
);
|
|
7715
|
+
var faceRaw = createCahillKeyesFaceRaw(10000);
|
|
7716
|
+
var engine = createPolyhedralProjection({
|
|
7717
|
+
faces: data.faces,
|
|
7718
|
+
parents: data.parents,
|
|
7719
|
+
rotation: [0, 0, 0],
|
|
7720
|
+
// Face-local CK coordinates point opposite the root face.
|
|
7721
|
+
angle: 150,
|
|
7722
|
+
findFace: createTruncatedFaceFinder(data.cornerNormals),
|
|
7723
|
+
faceProjector: function(face) {
|
|
7724
|
+
var baseId = face.id < 8 ? face.id : data.parents[face.id];
|
|
7725
|
+
var center = faceCenters[baseId] * D2R$2;
|
|
7726
|
+
var raw = function(lam, phi) {
|
|
7727
|
+
return faceRaw(normalizeRadians$1(lam - center), phi);
|
|
7728
|
+
};
|
|
7729
|
+
return createNormalizedFacetProjector(face.coords, raw);
|
|
7730
|
+
}
|
|
7731
|
+
});
|
|
7732
|
+
engine.facetProjection = 'cahill_keyes';
|
|
7733
|
+
return engine;
|
|
7734
|
+
}
|
|
7735
|
+
|
|
7736
|
+
function createNormalizedFacetProjector(coords, raw) {
|
|
7737
|
+
// Remove the global placement and scale of a raw facet while preserving its
|
|
7738
|
+
// internal shape. The polyhedral engine then controls how facets unfold.
|
|
7739
|
+
var points = coords.map(function(p) {
|
|
7740
|
+
return raw(p[0] * D2R$2, p[1] * D2R$2);
|
|
7741
|
+
});
|
|
7742
|
+
var edge = 0;
|
|
7743
|
+
var maxLengthSq = -1;
|
|
7744
|
+
for (var i = 0; i < points.length; i++) {
|
|
7745
|
+
var a = points[i];
|
|
7746
|
+
var b = points[(i + 1) % points.length];
|
|
7747
|
+
var dx = b[0] - a[0];
|
|
7748
|
+
var dy = b[1] - a[1];
|
|
7749
|
+
var lengthSq = dx * dx + dy * dy;
|
|
7750
|
+
if (lengthSq > maxLengthSq) {
|
|
7751
|
+
maxLengthSq = lengthSq;
|
|
7752
|
+
edge = i;
|
|
7753
|
+
}
|
|
7754
|
+
}
|
|
7755
|
+
var origin = points[edge];
|
|
7756
|
+
var end = points[(edge + 1) % points.length];
|
|
7757
|
+
var ux = end[0] - origin[0];
|
|
7758
|
+
var uy = end[1] - origin[1];
|
|
7759
|
+
var center = points.reduce(function(sum, p) {
|
|
7760
|
+
sum[0] += p[0] / points.length;
|
|
7761
|
+
sum[1] += p[1] / points.length;
|
|
7762
|
+
return sum;
|
|
7763
|
+
}, [0, 0]);
|
|
7764
|
+
var side = ux * (center[1] - origin[1]) -
|
|
7765
|
+
uy * (center[0] - origin[0]) < 0 ? -1 : 1;
|
|
7766
|
+
return function(lam, phi) {
|
|
7767
|
+
var p = raw(lam, phi);
|
|
7768
|
+
var x = p[0] - origin[0];
|
|
7769
|
+
var y = p[1] - origin[1];
|
|
7770
|
+
return [
|
|
7771
|
+
(x * ux + y * uy) / maxLengthSq,
|
|
7772
|
+
side * (ux * y - uy * x) / maxLengthSq
|
|
7773
|
+
];
|
|
7774
|
+
};
|
|
7775
|
+
}
|
|
7776
|
+
|
|
7777
|
+
function createRadialButterflyEngine(options) {
|
|
7778
|
+
options = normalizeRadialFacetOptions(options);
|
|
7779
|
+
var data = createRadialButterflyFaces();
|
|
7780
|
+
var radialOptions = {
|
|
7781
|
+
radial: options.projection,
|
|
7782
|
+
radial2: options.projection2,
|
|
7783
|
+
radialBlend: options.blend,
|
|
7784
|
+
boundaryStrength: RADIAL_BOUNDARY_STRENGTH
|
|
7785
|
+
};
|
|
7786
|
+
var squareProjectors = createSquareProjectors(data.faces, radialOptions);
|
|
7787
|
+
var engine = createPolyhedralProjection({
|
|
7788
|
+
faces: data.faces,
|
|
7789
|
+
parents: data.parents,
|
|
7790
|
+
rotation: [0, 0, 0],
|
|
7791
|
+
angle: -30,
|
|
7792
|
+
findFace: createTruncatedFaceFinder(data.cornerNormals),
|
|
7793
|
+
faceProjector: function(face) {
|
|
7794
|
+
if (face.id < 8) {
|
|
7795
|
+
return createRadialFacetProjector(face.coords, radialOptions);
|
|
7796
|
+
}
|
|
7797
|
+
return squareProjectors.get(pointKey(face.coords[0]));
|
|
7798
|
+
}
|
|
7799
|
+
});
|
|
7800
|
+
engine.radialFacet = {
|
|
7801
|
+
projection: options.projection,
|
|
7802
|
+
projection2: options.projection2,
|
|
7803
|
+
blend: options.blend,
|
|
7804
|
+
boundaryStrength: RADIAL_BOUNDARY_STRENGTH
|
|
7805
|
+
};
|
|
7806
|
+
return engine;
|
|
7807
|
+
}
|
|
7808
|
+
|
|
7809
|
+
function normalizeRadialFacetOptions(options) {
|
|
7810
|
+
options = options || {};
|
|
7811
|
+
var projection2 = options.projection2 ?
|
|
7812
|
+
normalizeRadialProjectionName(options.projection2) :
|
|
7813
|
+
null;
|
|
7814
|
+
var blend = projection2 ?
|
|
7815
|
+
options.blend == null ? 0.5 : Number(options.blend) :
|
|
7816
|
+
0;
|
|
7817
|
+
if (!Number.isFinite(blend) || blend < 0 || blend > 1) {
|
|
7818
|
+
throw new Error('Facet projection blend must be between 0 and 1');
|
|
7819
|
+
}
|
|
7820
|
+
return {
|
|
7821
|
+
projection: normalizeRadialProjectionName(options.projection || 'laea'),
|
|
7822
|
+
projection2: projection2,
|
|
7823
|
+
blend: blend
|
|
7824
|
+
};
|
|
7825
|
+
}
|
|
7826
|
+
|
|
7827
|
+
function createCahillKeyesEngine() {
|
|
7828
|
+
var data = createTruncatedOctahedronFaces(
|
|
7829
|
+
Math.cos(17 * D2R$2),
|
|
7830
|
+
Math.sin(17 * D2R$2),
|
|
7831
|
+
[-1, 3, 0, 2, 0, 1, 4, 5]
|
|
7832
|
+
);
|
|
7833
|
+
var raw = createCahillKeyesRaw(10000);
|
|
7834
|
+
var engine = createPolyhedralProjection({
|
|
7835
|
+
faces: data.faces,
|
|
7836
|
+
parents: data.parents,
|
|
7837
|
+
rotation: [0, 0, 0],
|
|
7838
|
+
angle: 0,
|
|
7839
|
+
findFace: createTruncatedFaceFinder(data.cornerNormals),
|
|
7840
|
+
faceProjector: function() {
|
|
7841
|
+
return function(lam, phi) {
|
|
7842
|
+
var p = raw(lam, phi);
|
|
7843
|
+
return [p[0], -p[1]];
|
|
7844
|
+
};
|
|
7845
|
+
}
|
|
7846
|
+
});
|
|
7847
|
+
// The 12-zone transform positions all octants directly in its M-profile.
|
|
7848
|
+
// Its face-tree boundary retraces several edges, so derive graticule
|
|
7849
|
+
// footprints through the normal spherical clipping path instead.
|
|
7850
|
+
engine.useProjectedOutline = false;
|
|
7851
|
+
engine.removeOutlineExtremeConnectors = true;
|
|
7852
|
+
return engine;
|
|
7853
|
+
}
|
|
7854
|
+
|
|
7855
|
+
function createRadialButterflyFaces() {
|
|
7856
|
+
return createTruncatedOctahedronFaces(
|
|
7857
|
+
Math.sqrt(0.9),
|
|
7858
|
+
Math.sqrt(0.1),
|
|
7859
|
+
BUTTERFLY_PARENTS
|
|
7860
|
+
);
|
|
7861
|
+
}
|
|
7862
|
+
|
|
7863
|
+
function createTruncatedOctahedronFaces(edgeWeight, vertexWeight, baseParents) {
|
|
7864
|
+
var cornerNormals = [];
|
|
7865
|
+
var faces = OCTAHEDRON.map(function(face) {
|
|
7866
|
+
var vectors = face.map(degreesToVector);
|
|
7867
|
+
var hexagon = [];
|
|
7868
|
+
var a = vectors[vectors.length - 1];
|
|
7869
|
+
vectors.forEach(function(b) {
|
|
7870
|
+
hexagon.push(
|
|
7871
|
+
vectorToDegrees(normalizeVector$1(addScaledVectors(
|
|
7872
|
+
a, edgeWeight, b, vertexWeight
|
|
7873
|
+
))),
|
|
7874
|
+
vectorToDegrees(normalizeVector$1(addScaledVectors(
|
|
7875
|
+
b, edgeWeight, a, vertexWeight
|
|
7876
|
+
)))
|
|
7877
|
+
);
|
|
7878
|
+
a = b;
|
|
7879
|
+
});
|
|
7880
|
+
return hexagon;
|
|
7881
|
+
});
|
|
7882
|
+
canonicalizeVertices(faces);
|
|
7883
|
+
faces.forEach(function(hexagon) {
|
|
7884
|
+
var normals = [];
|
|
7885
|
+
for (var i = 0; i < 3; i++) {
|
|
7886
|
+
normals.push(crossVectors(
|
|
7887
|
+
degreesToVector(hexagon[(i * 2 + 2) % 6]),
|
|
7888
|
+
degreesToVector(hexagon[(i * 2 + 1) % 6])
|
|
7889
|
+
));
|
|
7890
|
+
}
|
|
7891
|
+
cornerNormals.push(normals);
|
|
7892
|
+
});
|
|
7893
|
+
var parents = baseParents.concat();
|
|
7894
|
+
faces.slice().forEach(function(hexagon, j) {
|
|
7895
|
+
var face = OCTAHEDRON[j];
|
|
7896
|
+
for (var i = 0; i < 3; i++) {
|
|
7897
|
+
faces.push([
|
|
7898
|
+
face[i],
|
|
7899
|
+
hexagon[(i * 2 + 2) % 6],
|
|
7900
|
+
hexagon[(i * 2 + 1) % 6]
|
|
7901
|
+
]);
|
|
7902
|
+
parents.push(j);
|
|
7903
|
+
}
|
|
7904
|
+
});
|
|
7905
|
+
return {faces: faces, parents: parents, cornerNormals: cornerNormals};
|
|
7906
|
+
}
|
|
7907
|
+
|
|
7908
|
+
function createTruncatedFaceFinder(cornerNormals) {
|
|
7909
|
+
return function(lam, phi) {
|
|
7910
|
+
var cosPhi = Math.cos(phi);
|
|
7911
|
+
var p = [cosPhi * Math.cos(lam), cosPhi * Math.sin(lam), Math.sin(phi)];
|
|
7912
|
+
var face = lam < -Math.PI / 2 ?
|
|
7913
|
+
phi < 0 ? 6 : 4 :
|
|
7914
|
+
lam < 0 ?
|
|
7915
|
+
phi < 0 ? 2 : 0 :
|
|
7916
|
+
lam < Math.PI / 2 ?
|
|
7917
|
+
phi < 0 ? 3 : 1 :
|
|
7918
|
+
phi < 0 ? 7 : 5;
|
|
7919
|
+
var normals = cornerNormals[face];
|
|
7920
|
+
if (dotVectors(normals[0], p) < 0) return 8 + 3 * face;
|
|
7921
|
+
if (dotVectors(normals[1], p) < 0) return 8 + 3 * face + 1;
|
|
7922
|
+
if (dotVectors(normals[2], p) < 0) return 8 + 3 * face + 2;
|
|
7923
|
+
return face;
|
|
7924
|
+
};
|
|
7925
|
+
}
|
|
7926
|
+
|
|
7927
|
+
function canonicalizeVertices(faces) {
|
|
7928
|
+
var index = new Map();
|
|
7929
|
+
faces.forEach(function(face) {
|
|
7930
|
+
face.forEach(function(p, i) {
|
|
7931
|
+
var v = degreesToVector(p);
|
|
7932
|
+
var key = v.map(function(x) {
|
|
7933
|
+
return x.toFixed(12);
|
|
7934
|
+
}).join(',');
|
|
7935
|
+
if (index.has(key)) {
|
|
7936
|
+
face[i] = index.get(key);
|
|
7937
|
+
} else {
|
|
7938
|
+
index.set(key, p);
|
|
7939
|
+
}
|
|
7940
|
+
});
|
|
7941
|
+
});
|
|
7942
|
+
}
|
|
7943
|
+
|
|
7944
|
+
function createSquareProjectors(faces, radialOptions) {
|
|
7945
|
+
var verticesByCenter = new Map();
|
|
7946
|
+
var projectors = new Map();
|
|
7947
|
+
faces.slice(8).forEach(function(face) {
|
|
7948
|
+
var key = pointKey(face[0]);
|
|
7949
|
+
var vertices = verticesByCenter.get(key);
|
|
7950
|
+
if (!vertices) {
|
|
7951
|
+
vertices = new Map();
|
|
7952
|
+
verticesByCenter.set(key, vertices);
|
|
7953
|
+
}
|
|
7954
|
+
vertices.set(pointKey(face[1]), face[1]);
|
|
7955
|
+
vertices.set(pointKey(face[2]), face[2]);
|
|
7956
|
+
});
|
|
7957
|
+
verticesByCenter.forEach(function(vertices, key) {
|
|
7958
|
+
var center = key.split(',').map(Number);
|
|
7959
|
+
var gnomonic = createGnomonicProjector(center);
|
|
7960
|
+
var coords = Array.from(vertices.values()).sort(function(a, b) {
|
|
7961
|
+
return planarAngle(a, gnomonic) - planarAngle(b, gnomonic);
|
|
7962
|
+
});
|
|
7963
|
+
projectors.set(key, createRadialFacetProjector(coords, {
|
|
7964
|
+
planarCenter: center,
|
|
7965
|
+
radial: radialOptions.radial,
|
|
7966
|
+
radial2: radialOptions.radial2,
|
|
7967
|
+
radialBlend: radialOptions.radialBlend,
|
|
7968
|
+
boundaryStrength: radialOptions.boundaryStrength
|
|
7969
|
+
}));
|
|
7970
|
+
});
|
|
7971
|
+
return projectors;
|
|
7972
|
+
}
|
|
7973
|
+
|
|
7974
|
+
function planarAngle(p, project) {
|
|
7975
|
+
var q = project(p[0] * D2R$2, p[1] * D2R$2);
|
|
7976
|
+
return Math.atan2(q[1], q[0]);
|
|
7977
|
+
}
|
|
7978
|
+
|
|
7979
|
+
function createOctahedron() {
|
|
7980
|
+
var vertices = [
|
|
7981
|
+
[0, 90],
|
|
7982
|
+
[-90, 0],
|
|
7983
|
+
[0, 0],
|
|
7984
|
+
[90, 0],
|
|
7985
|
+
[180, 0],
|
|
7986
|
+
[0, -90]
|
|
7987
|
+
];
|
|
7988
|
+
return [
|
|
7989
|
+
[0, 2, 1],
|
|
7990
|
+
[0, 3, 2],
|
|
7991
|
+
[5, 1, 2],
|
|
7992
|
+
[5, 2, 3],
|
|
7993
|
+
[0, 1, 4],
|
|
7994
|
+
[0, 4, 3],
|
|
7995
|
+
[5, 4, 1],
|
|
7996
|
+
[5, 3, 4]
|
|
7997
|
+
].map(function(ids) {
|
|
7998
|
+
return ids.map(function(id) {
|
|
7999
|
+
return vertices[id];
|
|
8000
|
+
});
|
|
8001
|
+
});
|
|
8002
|
+
}
|
|
8003
|
+
|
|
8004
|
+
function pointKey(p) {
|
|
8005
|
+
return p[0] + ',' + p[1];
|
|
8006
|
+
}
|
|
8007
|
+
|
|
8008
|
+
function degreesToVector(p) {
|
|
8009
|
+
var lam = p[0] * D2R$2;
|
|
8010
|
+
var phi = p[1] * D2R$2;
|
|
8011
|
+
var cosPhi = Math.cos(phi);
|
|
8012
|
+
return [Math.cos(lam) * cosPhi, Math.sin(lam) * cosPhi, Math.sin(phi)];
|
|
8013
|
+
}
|
|
8014
|
+
|
|
8015
|
+
function vectorToDegrees(p) {
|
|
8016
|
+
return [
|
|
8017
|
+
Math.atan2(p[1], p[0]) * R2D$2,
|
|
8018
|
+
Math.asin(Math.max(-1, Math.min(1, p[2]))) * R2D$2
|
|
8019
|
+
];
|
|
8020
|
+
}
|
|
8021
|
+
|
|
8022
|
+
function addScaledVectors(a, ka, b, kb) {
|
|
8023
|
+
return [
|
|
8024
|
+
a[0] * ka + b[0] * kb,
|
|
8025
|
+
a[1] * ka + b[1] * kb,
|
|
8026
|
+
a[2] * ka + b[2] * kb
|
|
8027
|
+
];
|
|
8028
|
+
}
|
|
8029
|
+
|
|
8030
|
+
function crossVectors(a, b) {
|
|
8031
|
+
return [
|
|
8032
|
+
a[1] * b[2] - a[2] * b[1],
|
|
8033
|
+
a[2] * b[0] - a[0] * b[2],
|
|
8034
|
+
a[0] * b[1] - a[1] * b[0]
|
|
8035
|
+
];
|
|
8036
|
+
}
|
|
8037
|
+
|
|
8038
|
+
function dotVectors(a, b) {
|
|
8039
|
+
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
8040
|
+
}
|
|
8041
|
+
|
|
8042
|
+
function normalizeVector$1(p) {
|
|
8043
|
+
var k = 1 / Math.sqrt(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]);
|
|
8044
|
+
return [p[0] * k, p[1] * k, p[2] * k];
|
|
8045
|
+
}
|
|
8046
|
+
|
|
8047
|
+
function normalizeRadians$1(lam) {
|
|
8048
|
+
return (lam + Math.PI * 3) % (Math.PI * 2) - Math.PI;
|
|
8049
|
+
}
|
|
8050
|
+
|
|
8051
|
+
var ButterflyProjections = /*#__PURE__*/Object.freeze({
|
|
8052
|
+
__proto__: null,
|
|
8053
|
+
createRadialButterflyEngine: createRadialButterflyEngine,
|
|
8054
|
+
getButterflyEngine: getButterflyEngine,
|
|
8055
|
+
registerButterflyProjections: registerButterflyProjections
|
|
8056
|
+
});
|
|
8057
|
+
|
|
8058
|
+
/*
|
|
8059
|
+
* Forward implementation of Hajime Narukawa's 2022 mathematical
|
|
8060
|
+
* approximation of the AuthaGraph construction.
|
|
8061
|
+
*
|
|
8062
|
+
* Facet formula:
|
|
8063
|
+
* H. Narukawa, "Formulation of AuthaGraph Map Projection" (2022)
|
|
8064
|
+
* https://www.jstage.jst.go.jp/article/jjca/60/1/60_1/_article/-char/en
|
|
8065
|
+
*
|
|
8066
|
+
* The rectangular facet routing follows the public Imago arrangement by
|
|
8067
|
+
* Justin Kunimune. The face transform itself uses Narukawa's published
|
|
8068
|
+
* equations, not Imago's power-law approximation.
|
|
8069
|
+
*/
|
|
8070
|
+
|
|
8071
|
+
var D2R$1 = Math.PI / 180;
|
|
8072
|
+
var R2D$1 = 180 / Math.PI;
|
|
8073
|
+
var HALF_PI = Math.PI / 2;
|
|
8074
|
+
var SQRT2 = Math.sqrt(2);
|
|
8075
|
+
var SQRT3 = Math.sqrt(3);
|
|
8076
|
+
var ASIN_ONE_THIRD = Math.asin(1 / 3);
|
|
8077
|
+
var EDGE_SCALE = Math.acos(-1 / 3) / 2;
|
|
8078
|
+
var XMIN = -2 * SQRT3;
|
|
8079
|
+
var XMAX = 2 * SQRT3;
|
|
8080
|
+
var YMIN = -1.5;
|
|
8081
|
+
var YMAX = 1.5;
|
|
8082
|
+
var BLOCK_HEIGHT = 2 * SQRT3;
|
|
8083
|
+
var LAYOUT_SHIFT = 1.16;
|
|
8084
|
+
var EPS = 1e-12;
|
|
8085
|
+
var engines = {};
|
|
8086
|
+
|
|
8087
|
+
// The four tetrahedron vertices published by Narukawa, in latitude-longitude
|
|
8088
|
+
// order. The extra precision preserves the regular tetrahedron to about 1e-9
|
|
8089
|
+
// in vector dot products.
|
|
8090
|
+
var GEOGRAPHIC_VERTICES = [
|
|
8091
|
+
[76.8810628, 149.4509913],
|
|
8092
|
+
[-27.9527772, 97.3570035],
|
|
8093
|
+
[-6.6370473, -18.8522325],
|
|
8094
|
+
[-22.9282364, -133.2827588]
|
|
8095
|
+
];
|
|
8096
|
+
|
|
8097
|
+
// D3 Imago's vertex-oriented tetrahedral block. Each entry contains the
|
|
8098
|
+
// spherical center, local meridian, planar rotation and planar center.
|
|
8099
|
+
var FACET_DEFS = [
|
|
8100
|
+
[0, SQRT3, HALF_PI, 0, 0, -HALF_PI],
|
|
8101
|
+
[0, -SQRT3, -ASIN_ONE_THIRD, 0, Math.PI, HALF_PI],
|
|
8102
|
+
[3, 0, -ASIN_ONE_THIRD, 2 * Math.PI / 3, Math.PI, 5 * Math.PI / 6],
|
|
8103
|
+
[-3, 0, -ASIN_ONE_THIRD, -2 * Math.PI / 3, Math.PI, Math.PI / 6]
|
|
8104
|
+
];
|
|
8105
|
+
|
|
8106
|
+
function registerNarukawa2022Projection(mproj) {
|
|
8107
|
+
if (!mproj || mproj.internal.pj_list.narukawa2022) return;
|
|
8108
|
+
mproj.pj_add(function(P) {
|
|
8109
|
+
initNarukawa2022(P);
|
|
8110
|
+
}, 'narukawa2022', 'Narukawa 2022 tetrahedral world map');
|
|
8111
|
+
}
|
|
8112
|
+
|
|
8113
|
+
function getNarukawa2022Engine() {
|
|
8114
|
+
if (!engines.default) {
|
|
8115
|
+
engines.default = createNarukawa2022Engine();
|
|
8116
|
+
}
|
|
8117
|
+
return engines.default;
|
|
8118
|
+
}
|
|
8119
|
+
|
|
8120
|
+
function initNarukawa2022(P) {
|
|
8121
|
+
var engine = getNarukawa2022Engine();
|
|
8122
|
+
P.es = 0;
|
|
8123
|
+
P.inv = null;
|
|
8124
|
+
P.fwd = function(lp, xy) {
|
|
8125
|
+
var p = engine.forward(lp.lam, lp.phi);
|
|
8126
|
+
xy.x = p[0];
|
|
8127
|
+
xy.y = p[1];
|
|
8128
|
+
};
|
|
8129
|
+
P.__projection_topology = engine.getTopology(P.lam0 * R2D$1);
|
|
8130
|
+
P.__projected_outline = engine.outline;
|
|
8131
|
+
}
|
|
8132
|
+
|
|
8133
|
+
function createNarukawa2022Engine() {
|
|
8134
|
+
var orientation = createPublishedOrientation();
|
|
8135
|
+
var facets = FACET_DEFS.map(function(def, i) {
|
|
8136
|
+
return {
|
|
8137
|
+
id: i,
|
|
8138
|
+
x: def[0],
|
|
8139
|
+
y: def[1],
|
|
8140
|
+
lat: def[2],
|
|
8141
|
+
lon: def[3],
|
|
8142
|
+
meridian: def[4],
|
|
8143
|
+
rotation: def[5]
|
|
8144
|
+
};
|
|
8145
|
+
});
|
|
8146
|
+
var outline = [[
|
|
8147
|
+
[XMIN * EDGE_SCALE, YMIN * EDGE_SCALE],
|
|
8148
|
+
[XMAX * EDGE_SCALE, YMIN * EDGE_SCALE],
|
|
8149
|
+
[XMAX * EDGE_SCALE, YMAX * EDGE_SCALE],
|
|
8150
|
+
[XMIN * EDGE_SCALE, YMAX * EDGE_SCALE],
|
|
8151
|
+
[XMIN * EDGE_SCALE, YMIN * EDGE_SCALE]
|
|
8152
|
+
]];
|
|
8153
|
+
|
|
8154
|
+
return {
|
|
8155
|
+
forward: forward,
|
|
8156
|
+
inverse: inverse,
|
|
8157
|
+
outline: outline,
|
|
8158
|
+
getTopology: getTopology
|
|
8159
|
+
};
|
|
8160
|
+
|
|
8161
|
+
function forward(lam, phi) {
|
|
8162
|
+
var state = projectState(lam, phi);
|
|
8163
|
+
return [state.x * EDGE_SCALE, state.y * EDGE_SCALE];
|
|
8164
|
+
}
|
|
8165
|
+
|
|
8166
|
+
// Internal inverse used only for tracing the geographic rectangular cut.
|
|
8167
|
+
// P.inv remains null because the full Mapshaper inverse path is unsupported.
|
|
8168
|
+
function inverse(x, y) {
|
|
8169
|
+
var p = invertLayout(x / EDGE_SCALE, y / EDGE_SCALE, facets);
|
|
8170
|
+
if (!p) return null;
|
|
8171
|
+
return fromCanonical(p[0], p[1], orientation);
|
|
8172
|
+
}
|
|
8173
|
+
|
|
8174
|
+
function projectState(lam, phi) {
|
|
8175
|
+
var p = toCanonical(lam, phi, orientation);
|
|
8176
|
+
var state = projectCanonical(p[0], p[1], facets);
|
|
8177
|
+
state.region = encodeRegion(state);
|
|
8178
|
+
return state;
|
|
8179
|
+
}
|
|
8180
|
+
|
|
8181
|
+
function getTopology(lon0) {
|
|
8182
|
+
var paths = createCutPaths(inverse, lon0);
|
|
8183
|
+
return {
|
|
8184
|
+
regions: createRegionIndex(),
|
|
8185
|
+
seams: [{
|
|
8186
|
+
type: 'cut',
|
|
8187
|
+
paths: paths
|
|
8188
|
+
}],
|
|
8189
|
+
findRegion: findRegion,
|
|
8190
|
+
findTransitionRegion: findRegion,
|
|
8191
|
+
outline: outline.map(function(ring) {
|
|
8192
|
+
return ring.map(function(p) {
|
|
8193
|
+
return p.concat();
|
|
8194
|
+
});
|
|
8195
|
+
})
|
|
8196
|
+
};
|
|
8197
|
+
|
|
8198
|
+
function findRegion(lon, lat) {
|
|
8199
|
+
var lam = normalizeRadians((lon - lon0) * D2R$1);
|
|
8200
|
+
return projectState(lam, lat * D2R$1).region;
|
|
8201
|
+
}
|
|
8202
|
+
}
|
|
8203
|
+
}
|
|
8204
|
+
|
|
8205
|
+
function projectCanonical(lam, phi, facets) {
|
|
8206
|
+
var facet = findForwardFacet(lam, phi, facets);
|
|
8207
|
+
var relative = obliquifySpherical(phi, lam, facet);
|
|
8208
|
+
var sector = Math.floor((relative[1] + Math.PI / 3) / (2 * Math.PI / 3));
|
|
8209
|
+
var base = sector * 2 * Math.PI / 3;
|
|
8210
|
+
var polar = narukawaFaceForward(relative[1] - base, relative[0]);
|
|
8211
|
+
var angle = polar[1] + facet.rotation + base / 2;
|
|
8212
|
+
var x = polar[0] * Math.cos(angle) + facet.x;
|
|
8213
|
+
var y = polar[0] * Math.sin(angle) + facet.y;
|
|
8214
|
+
var oob = 0;
|
|
8215
|
+
var folded = 0;
|
|
8216
|
+
var wrap = 0;
|
|
8217
|
+
|
|
8218
|
+
if (Math.abs(x) > 3 + EPS) {
|
|
8219
|
+
x = 2 * facet.x - x;
|
|
8220
|
+
y = -y;
|
|
8221
|
+
oob = 1;
|
|
8222
|
+
} else if (Math.abs(y) > SQRT3 + EPS) {
|
|
8223
|
+
x = -x;
|
|
8224
|
+
y = BLOCK_HEIGHT * Math.sign(y) - y;
|
|
8225
|
+
oob = 2;
|
|
8226
|
+
}
|
|
8227
|
+
|
|
8228
|
+
var qx = y;
|
|
8229
|
+
var qy = -x;
|
|
8230
|
+
if (qy > EPS) {
|
|
8231
|
+
qx = BLOCK_HEIGHT - qx;
|
|
8232
|
+
qy = -qy;
|
|
8233
|
+
folded = 1;
|
|
8234
|
+
}
|
|
8235
|
+
qx += LAYOUT_SHIFT;
|
|
8236
|
+
if (qx < 0) {
|
|
8237
|
+
qx += 2 * BLOCK_HEIGHT;
|
|
8238
|
+
wrap = 1;
|
|
8239
|
+
}
|
|
8240
|
+
x = qx - BLOCK_HEIGHT;
|
|
8241
|
+
y = qy + 1.5;
|
|
8242
|
+
x = clamp$2(x, XMIN, XMAX);
|
|
8243
|
+
y = clamp$2(y, YMIN, YMAX);
|
|
8244
|
+
return {
|
|
8245
|
+
x: x,
|
|
8246
|
+
y: y,
|
|
8247
|
+
facet: facet.id,
|
|
8248
|
+
sector: mod(sector, 3),
|
|
8249
|
+
oob: oob,
|
|
8250
|
+
folded: folded,
|
|
8251
|
+
wrap: wrap
|
|
8252
|
+
};
|
|
8253
|
+
}
|
|
8254
|
+
|
|
8255
|
+
function findForwardFacet(lam, phi, facets) {
|
|
8256
|
+
var best = null;
|
|
8257
|
+
var bestLat = -Infinity;
|
|
8258
|
+
for (var i = 0; i < 4; i++) {
|
|
8259
|
+
var facet = facets[i];
|
|
8260
|
+
var relative = obliquifySpherical(phi, lam, facet);
|
|
8261
|
+
if (relative[0] > bestLat) {
|
|
8262
|
+
bestLat = relative[0];
|
|
8263
|
+
best = facet;
|
|
8264
|
+
}
|
|
8265
|
+
}
|
|
8266
|
+
return best;
|
|
8267
|
+
}
|
|
8268
|
+
|
|
8269
|
+
function narukawaFaceForward(lam, phi) {
|
|
8270
|
+
var a = lam - Math.asin(Math.sin(lam) / SQRT3);
|
|
8271
|
+
var theta = Math.atan(2 * SQRT3 / Math.PI * a);
|
|
8272
|
+
var denominator = 2 + SQRT2 * Math.tan(phi);
|
|
8273
|
+
var q = denominator > 0 ? (2 + Math.cos(lam)) / denominator : 0;
|
|
8274
|
+
var r = q * SQRT3 / Math.cos(theta);
|
|
8275
|
+
return [r, theta];
|
|
8276
|
+
}
|
|
8277
|
+
|
|
8278
|
+
function narukawaFaceInverse(r, theta) {
|
|
8279
|
+
var target = Math.tan(theta) * Math.PI / (2 * SQRT3);
|
|
8280
|
+
var lo = -Math.PI / 3;
|
|
8281
|
+
var hi = Math.PI / 3;
|
|
8282
|
+
var lam;
|
|
8283
|
+
for (var i = 0; i < 55; i++) {
|
|
8284
|
+
lam = (lo + hi) / 2;
|
|
8285
|
+
var a = lam - Math.asin(Math.sin(lam) / SQRT3);
|
|
8286
|
+
if (a < target) lo = lam;
|
|
8287
|
+
else hi = lam;
|
|
8288
|
+
}
|
|
8289
|
+
lam = (lo + hi) / 2;
|
|
8290
|
+
var q = r * Math.cos(theta) / SQRT3;
|
|
8291
|
+
var phi = q < EPS ? HALF_PI :
|
|
8292
|
+
Math.atan(((2 + Math.cos(lam)) / q - 2) / SQRT2);
|
|
8293
|
+
return [phi, lam];
|
|
8294
|
+
}
|
|
8295
|
+
|
|
8296
|
+
function invertLayout(x, y, facets) {
|
|
8297
|
+
var qx = x + BLOCK_HEIGHT;
|
|
8298
|
+
var qy = y - 1.5;
|
|
8299
|
+
var normalizedX = (qx - LAYOUT_SHIFT) / BLOCK_HEIGHT;
|
|
8300
|
+
if (normalizedX > 1.5) normalizedX -= 2;
|
|
8301
|
+
if (normalizedX > 0.5) {
|
|
8302
|
+
normalizedX = 1 - normalizedX;
|
|
8303
|
+
qy *= -1;
|
|
8304
|
+
}
|
|
8305
|
+
x = -qy;
|
|
8306
|
+
y = normalizedX * BLOCK_HEIGHT;
|
|
8307
|
+
|
|
8308
|
+
var facet = findInverseFacet(x, y, facets);
|
|
8309
|
+
var dx = x - facet.x;
|
|
8310
|
+
var dy = y - facet.y;
|
|
8311
|
+
var r = Math.hypot(dx, dy);
|
|
8312
|
+
var theta = normalizeRadians(Math.atan2(dy, dx) - facet.rotation);
|
|
8313
|
+
var base = Math.floor((theta + Math.PI / 6) / (Math.PI / 3)) *
|
|
8314
|
+
Math.PI / 3;
|
|
8315
|
+
var relative = narukawaFaceInverse(r, theta - base);
|
|
8316
|
+
relative[1] += base * 2;
|
|
8317
|
+
var p = deobliquifySpherical(relative[0], relative[1], facet);
|
|
8318
|
+
return [p[1], p[0]];
|
|
8319
|
+
}
|
|
8320
|
+
|
|
8321
|
+
function findInverseFacet(x, y, facets) {
|
|
8322
|
+
var best = null;
|
|
8323
|
+
var minDistance = Infinity;
|
|
8324
|
+
facets.forEach(function(facet) {
|
|
8325
|
+
var d = Math.hypot(x - facet.x, y - facet.y);
|
|
8326
|
+
if (d < minDistance) {
|
|
8327
|
+
minDistance = d;
|
|
8328
|
+
best = facet;
|
|
8329
|
+
}
|
|
8330
|
+
});
|
|
8331
|
+
return best;
|
|
8332
|
+
}
|
|
8333
|
+
|
|
8334
|
+
function createPublishedOrientation() {
|
|
8335
|
+
// Match the D3 Imago arrangement: the third published vertex is the
|
|
8336
|
+
// canonical southern vertex at longitude zero.
|
|
8337
|
+
var north = latLonToVector(GEOGRAPHIC_VERTICES[0]);
|
|
8338
|
+
var south = latLonToVector(GEOGRAPHIC_VERTICES[2]);
|
|
8339
|
+
var tangent = normalizeVector(subtract(
|
|
8340
|
+
south,
|
|
8341
|
+
multiply(north, dot$1(south, north))
|
|
8342
|
+
));
|
|
8343
|
+
var x = tangent;
|
|
8344
|
+
var y = cross$1(north, tangent);
|
|
8345
|
+
return {x: x, y: y, z: north};
|
|
8346
|
+
}
|
|
8347
|
+
|
|
8348
|
+
function toCanonical(lam, phi, orientation) {
|
|
8349
|
+
var v = radiansToVector(lam, phi);
|
|
8350
|
+
return [
|
|
8351
|
+
Math.atan2(dot$1(v, orientation.y), dot$1(v, orientation.x)),
|
|
8352
|
+
Math.asin(clamp$2(dot$1(v, orientation.z), -1, 1))
|
|
8353
|
+
];
|
|
8354
|
+
}
|
|
8355
|
+
|
|
8356
|
+
function fromCanonical(lam, phi, orientation) {
|
|
8357
|
+
var v = radiansToVector(lam, phi);
|
|
8358
|
+
var p = [
|
|
8359
|
+
orientation.x[0] * v[0] + orientation.y[0] * v[1] + orientation.z[0] * v[2],
|
|
8360
|
+
orientation.x[1] * v[0] + orientation.y[1] * v[1] + orientation.z[1] * v[2],
|
|
8361
|
+
orientation.x[2] * v[0] + orientation.y[2] * v[1] + orientation.z[2] * v[2]
|
|
8362
|
+
];
|
|
8363
|
+
return [Math.atan2(p[1], p[0]), Math.asin(clamp$2(p[2], -1, 1))];
|
|
8364
|
+
}
|
|
8365
|
+
|
|
8366
|
+
// Adapted from Justin Kunimune's Imago implementation.
|
|
8367
|
+
function obliquifySpherical(lat, lon, pole) {
|
|
8368
|
+
var lat0 = pole.lat;
|
|
8369
|
+
var lon0 = pole.lon;
|
|
8370
|
+
var theta0 = pole.meridian;
|
|
8371
|
+
var lat1;
|
|
8372
|
+
var lon1;
|
|
8373
|
+
if (Math.abs(lat0 - HALF_PI) < EPS) {
|
|
8374
|
+
lat1 = lat;
|
|
8375
|
+
lon1 = lon - lon0;
|
|
8376
|
+
} else {
|
|
8377
|
+
lat1 = Math.asin(clamp$2(
|
|
8378
|
+
Math.sin(lat0) * Math.sin(lat) +
|
|
8379
|
+
Math.cos(lat0) * Math.cos(lat) * Math.cos(lon0 - lon),
|
|
8380
|
+
-1,
|
|
8381
|
+
1
|
|
8382
|
+
));
|
|
8383
|
+
var denominator = Math.cos(lat1);
|
|
8384
|
+
var value = denominator < EPS ? 1 :
|
|
8385
|
+
(Math.cos(lat0) * Math.sin(lat) -
|
|
8386
|
+
Math.sin(lat0) * Math.cos(lat) * Math.cos(lon0 - lon)) /
|
|
8387
|
+
denominator;
|
|
8388
|
+
lon1 = Math.acos(clamp$2(value, -1, 1)) - Math.PI;
|
|
8389
|
+
if (Math.sin(lon - lon0) > 0) lon1 = -lon1;
|
|
8390
|
+
}
|
|
8391
|
+
return [lat1, normalizeRadians(lon1 - theta0)];
|
|
8392
|
+
}
|
|
8393
|
+
|
|
8394
|
+
function deobliquifySpherical(lat, lon, pole) {
|
|
8395
|
+
var lat0 = pole.lat;
|
|
8396
|
+
var lon0 = pole.lon;
|
|
8397
|
+
lon += pole.meridian;
|
|
8398
|
+
var latOut = Math.asin(clamp$2(
|
|
8399
|
+
Math.sin(lat0) * Math.sin(lat) -
|
|
8400
|
+
Math.cos(lat0) * Math.cos(lon) * Math.cos(lat),
|
|
8401
|
+
-1,
|
|
8402
|
+
1
|
|
8403
|
+
));
|
|
8404
|
+
var lonOut;
|
|
8405
|
+
if (Math.abs(lat0 - HALF_PI) < EPS) {
|
|
8406
|
+
lonOut = lon + lon0;
|
|
8407
|
+
} else {
|
|
8408
|
+
var value = Math.sin(lat) / Math.cos(lat0) / Math.cos(latOut) -
|
|
8409
|
+
Math.tan(lat0) * Math.tan(latOut);
|
|
8410
|
+
if (Math.sin(lon) > 0) lonOut = lon0 + Math.acos(clamp$2(value, -1, 1));
|
|
8411
|
+
else lonOut = lon0 - Math.acos(clamp$2(value, -1, 1));
|
|
8412
|
+
}
|
|
8413
|
+
return [latOut, normalizeRadians(lonOut)];
|
|
8414
|
+
}
|
|
8415
|
+
|
|
8416
|
+
function createCutPaths(inverse, lon0) {
|
|
8417
|
+
// The rectangular edges become moderately curved geographic seams. Sampling
|
|
8418
|
+
// at about 0.03 degrees prevents straight mask chords from missing crossings.
|
|
8419
|
+
var n = 6000;
|
|
8420
|
+
var epsilon = 1e-8;
|
|
8421
|
+
var edges = [
|
|
8422
|
+
[[XMIN + epsilon, YMIN], [XMIN + epsilon, YMAX]],
|
|
8423
|
+
[[XMIN, YMAX - epsilon], [XMAX, YMAX - epsilon]],
|
|
8424
|
+
[[XMIN, YMIN + epsilon], [XMAX, YMIN + epsilon]]
|
|
8425
|
+
];
|
|
8426
|
+
var paths = edges.reduce(function(memo, edge) {
|
|
8427
|
+
var path = [];
|
|
8428
|
+
for (var i = 0; i <= n; i++) {
|
|
8429
|
+
var t = i / n;
|
|
8430
|
+
var x = edge[0][0] + (edge[1][0] - edge[0][0]) * t;
|
|
8431
|
+
var y = edge[0][1] + (edge[1][1] - edge[0][1]) * t;
|
|
8432
|
+
var p = inverse(x * EDGE_SCALE, y * EDGE_SCALE);
|
|
8433
|
+
path.push([
|
|
8434
|
+
normalizeLongitude$1(p[0] * R2D$1 + lon0),
|
|
8435
|
+
p[1] * R2D$1
|
|
8436
|
+
]);
|
|
8437
|
+
}
|
|
8438
|
+
return memo.concat(splitPathAtAntimeridian$1(path));
|
|
8439
|
+
}, []);
|
|
8440
|
+
return paths.map(function(part) {
|
|
8441
|
+
part.mask_width = 4e-5;
|
|
8442
|
+
return part;
|
|
8443
|
+
});
|
|
8444
|
+
}
|
|
8445
|
+
|
|
8446
|
+
function splitPathAtAntimeridian$1(path) {
|
|
8447
|
+
var paths = [];
|
|
8448
|
+
var part = [path[0]];
|
|
8449
|
+
for (var i = 1; i < path.length; i++) {
|
|
8450
|
+
var a = path[i - 1];
|
|
8451
|
+
var b = path[i];
|
|
8452
|
+
if (Math.abs(a[0] - b[0]) > 180) {
|
|
8453
|
+
var adjusted = b[0] + (b[0] < a[0] ? 360 : -360);
|
|
8454
|
+
var edge = a[0] < 0 ? -180 : 180;
|
|
8455
|
+
var t = (edge - a[0]) / (adjusted - a[0]);
|
|
8456
|
+
var lat = a[1] + (b[1] - a[1]) * t;
|
|
8457
|
+
part.push([edge, lat]);
|
|
8458
|
+
paths.push(part);
|
|
8459
|
+
part = [[-edge, lat], b];
|
|
8460
|
+
} else {
|
|
8461
|
+
part.push(b);
|
|
8462
|
+
}
|
|
8463
|
+
}
|
|
8464
|
+
if (part.length > 1) paths.push(part);
|
|
8465
|
+
return paths;
|
|
8466
|
+
}
|
|
8467
|
+
|
|
8468
|
+
function encodeRegion(state) {
|
|
8469
|
+
return ((((state.facet * 3 + state.sector) * 3 + state.oob) * 2 +
|
|
8470
|
+
state.folded) * 2 + state.wrap);
|
|
8471
|
+
}
|
|
8472
|
+
|
|
8473
|
+
function createRegionIndex() {
|
|
8474
|
+
var regions = [];
|
|
8475
|
+
for (var i = 0; i < 144; i++) {
|
|
8476
|
+
regions.push({id: i});
|
|
8477
|
+
}
|
|
8478
|
+
return regions;
|
|
8479
|
+
}
|
|
8480
|
+
|
|
8481
|
+
function latLonToVector(p) {
|
|
8482
|
+
return radiansToVector(p[1] * D2R$1, p[0] * D2R$1);
|
|
8483
|
+
}
|
|
8484
|
+
|
|
8485
|
+
function radiansToVector(lam, phi) {
|
|
8486
|
+
var cosPhi = Math.cos(phi);
|
|
8487
|
+
return [Math.cos(lam) * cosPhi, Math.sin(lam) * cosPhi, Math.sin(phi)];
|
|
8488
|
+
}
|
|
8489
|
+
|
|
8490
|
+
function subtract(a, b) {
|
|
8491
|
+
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
8492
|
+
}
|
|
8493
|
+
|
|
8494
|
+
function multiply(a, k) {
|
|
8495
|
+
return [a[0] * k, a[1] * k, a[2] * k];
|
|
8496
|
+
}
|
|
8497
|
+
|
|
8498
|
+
function normalizeVector(a) {
|
|
8499
|
+
var k = 1 / Math.hypot(a[0], a[1], a[2]);
|
|
8500
|
+
return multiply(a, k);
|
|
8501
|
+
}
|
|
8502
|
+
|
|
8503
|
+
function cross$1(a, b) {
|
|
8504
|
+
return [
|
|
8505
|
+
a[1] * b[2] - a[2] * b[1],
|
|
8506
|
+
a[2] * b[0] - a[0] * b[2],
|
|
8507
|
+
a[0] * b[1] - a[1] * b[0]
|
|
8508
|
+
];
|
|
8509
|
+
}
|
|
8510
|
+
|
|
8511
|
+
function dot$1(a, b) {
|
|
8512
|
+
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
8513
|
+
}
|
|
8514
|
+
|
|
8515
|
+
function mod(a, n) {
|
|
8516
|
+
return (a % n + n) % n;
|
|
8517
|
+
}
|
|
8518
|
+
|
|
8519
|
+
function normalizeRadians(lam) {
|
|
8520
|
+
while (lam > Math.PI) lam -= 2 * Math.PI;
|
|
8521
|
+
while (lam < -Math.PI) lam += 2 * Math.PI;
|
|
8522
|
+
return lam;
|
|
8523
|
+
}
|
|
8524
|
+
|
|
8525
|
+
function normalizeLongitude$1(lon) {
|
|
8526
|
+
while (lon > 180) lon -= 360;
|
|
8527
|
+
while (lon < -180) lon += 360;
|
|
8528
|
+
return lon;
|
|
8529
|
+
}
|
|
8530
|
+
|
|
8531
|
+
function clamp$2(value, min, max) {
|
|
8532
|
+
return Math.max(min, Math.min(max, value));
|
|
8533
|
+
}
|
|
8534
|
+
|
|
8535
|
+
var Narukawa2022 = /*#__PURE__*/Object.freeze({
|
|
8536
|
+
__proto__: null,
|
|
8537
|
+
getNarukawa2022Engine: getNarukawa2022Engine,
|
|
8538
|
+
registerNarukawa2022Projection: registerNarukawa2022Projection
|
|
8539
|
+
});
|
|
8540
|
+
|
|
6290
8541
|
var mproj$1 = require$1('mproj');
|
|
8542
|
+
registerDymaxionProjections(mproj$1);
|
|
8543
|
+
registerButterflyProjections(mproj$1);
|
|
8544
|
+
registerNarukawa2022Projection(mproj$1);
|
|
6291
8545
|
|
|
6292
8546
|
var asyncLoader = null;
|
|
6293
8547
|
|
|
@@ -6456,9 +8710,18 @@
|
|
|
6456
8710
|
if (!defn) {
|
|
6457
8711
|
stop$1("Unknown projection definition:", str);
|
|
6458
8712
|
}
|
|
8713
|
+
if (utils.isString(defn)) {
|
|
8714
|
+
defn = addDefaultProjectionParams(defn);
|
|
8715
|
+
}
|
|
6459
8716
|
return defn;
|
|
6460
8717
|
}
|
|
6461
8718
|
|
|
8719
|
+
function addDefaultProjectionParams(defn) {
|
|
8720
|
+
var oceanic = /(^| )\+proj=(igh_o|imoll_o)(?= |$)/.test(defn);
|
|
8721
|
+
var hasLon0 = /(^| )\+lon_0=/.test(defn);
|
|
8722
|
+
return oceanic && !hasLon0 ? defn + ' +lon_0=-160' : defn;
|
|
8723
|
+
}
|
|
8724
|
+
|
|
6462
8725
|
function looksLikeInitString(str) {
|
|
6463
8726
|
return /^(esri|epsg|nad83|nad27):[0-9]+$/i.test(String(str));
|
|
6464
8727
|
}
|
|
@@ -9266,7 +11529,7 @@
|
|
|
9266
11529
|
function layerNameIsUnsafeFilename(name) {
|
|
9267
11530
|
name = String(name);
|
|
9268
11531
|
// path separators (directory traversal on any OS) or NUL
|
|
9269
|
-
if (/[
|
|
11532
|
+
if (/[/\\\0]/.test(name)) return true;
|
|
9270
11533
|
// Windows drive-relative prefix, e.g. "C:foo" can write to another drive
|
|
9271
11534
|
if (/^[A-Za-z]:/.test(name)) return true;
|
|
9272
11535
|
return false;
|
|
@@ -39161,7 +41424,9 @@ ${svg}
|
|
|
39161
41424
|
return finalizeImportedDataset(dataset, dataFmt, data, opts);
|
|
39162
41425
|
}
|
|
39163
41426
|
|
|
39164
|
-
|
|
41427
|
+
// Canonical content import interface. Always resolves to an array because some
|
|
41428
|
+
// formats (notably GeoPackage) may contain datasets that cannot be merged.
|
|
41429
|
+
async function importDatasetsFromContent(obj, opts) {
|
|
39165
41430
|
var dataset, dataFmt, data;
|
|
39166
41431
|
opts = opts || {};
|
|
39167
41432
|
if (obj.fgb) {
|
|
@@ -39185,14 +41450,16 @@ ${svg}
|
|
|
39185
41450
|
data = obj[dataFmt];
|
|
39186
41451
|
dataset = await importImageRaster(obj, opts);
|
|
39187
41452
|
} else {
|
|
39188
|
-
return importContent(obj, opts);
|
|
39189
|
-
}
|
|
39190
|
-
if (Array.isArray(dataset)) {
|
|
39191
|
-
return dataset.map(function(ds) {
|
|
39192
|
-
return finalizeImportedDataset(ds, dataFmt, data, opts);
|
|
39193
|
-
});
|
|
41453
|
+
return normalizeImportedDatasets$1(importContent(obj, opts));
|
|
39194
41454
|
}
|
|
39195
|
-
return
|
|
41455
|
+
return normalizeImportedDatasets$1(dataset).map(function(ds) {
|
|
41456
|
+
return finalizeImportedDataset(ds, dataFmt, data, opts);
|
|
41457
|
+
});
|
|
41458
|
+
}
|
|
41459
|
+
|
|
41460
|
+
// Compatibility facade for callers that expect one dataset when possible.
|
|
41461
|
+
async function importContentAsync(obj, opts) {
|
|
41462
|
+
return denormalizeImportedDatasets$1(await importDatasetsFromContent(obj, opts));
|
|
39196
41463
|
}
|
|
39197
41464
|
|
|
39198
41465
|
// Deprecated (included for compatibility with older tests)
|
|
@@ -39203,6 +41470,14 @@ ${svg}
|
|
|
39203
41470
|
return importContent(input, opts);
|
|
39204
41471
|
}
|
|
39205
41472
|
|
|
41473
|
+
function normalizeImportedDatasets$1(datasetOrArray) {
|
|
41474
|
+
return Array.isArray(datasetOrArray) ? datasetOrArray : [datasetOrArray];
|
|
41475
|
+
}
|
|
41476
|
+
|
|
41477
|
+
function denormalizeImportedDatasets$1(datasets) {
|
|
41478
|
+
return datasets.length == 1 ? datasets[0] : datasets;
|
|
41479
|
+
}
|
|
41480
|
+
|
|
39206
41481
|
|
|
39207
41482
|
function importShapefile(obj, opts) {
|
|
39208
41483
|
var shpSrc = obj.shp.content || obj.shp.filename, // read from a file if (binary) content is missing
|
|
@@ -39285,6 +41560,7 @@ ${svg}
|
|
|
39285
41560
|
__proto__: null,
|
|
39286
41561
|
importContent: importContent,
|
|
39287
41562
|
importContentAsync: importContentAsync,
|
|
41563
|
+
importDatasetsFromContent: importDatasetsFromContent,
|
|
39288
41564
|
importFileContent: importFileContent
|
|
39289
41565
|
});
|
|
39290
41566
|
|
|
@@ -39453,8 +41729,7 @@ ${svg}
|
|
|
39453
41729
|
var dataset, datasets, target;
|
|
39454
41730
|
|
|
39455
41731
|
if (opts.stdin) {
|
|
39456
|
-
|
|
39457
|
-
datasets = normalizeImportedDatasets(dataset);
|
|
41732
|
+
datasets = await importDatasetsFromFile('/dev/stdin', opts);
|
|
39458
41733
|
catalog.addDatasets(datasets);
|
|
39459
41734
|
if (datasets.length > 1) {
|
|
39460
41735
|
catalog.setDefaultTargets(datasets.map(function(ds) {
|
|
@@ -39492,11 +41767,11 @@ ${svg}
|
|
|
39492
41767
|
}
|
|
39493
41768
|
|
|
39494
41769
|
if (files.length == 1) {
|
|
39495
|
-
|
|
41770
|
+
datasets = await importDatasetsFromFile(files[0], opts);
|
|
39496
41771
|
} else {
|
|
39497
41772
|
dataset = await importFilesTogetherAsync(files, opts);
|
|
41773
|
+
datasets = [dataset];
|
|
39498
41774
|
}
|
|
39499
|
-
datasets = normalizeImportedDatasets(dataset);
|
|
39500
41775
|
datasets = validateAndCleanGpkgSelection(datasets, opts);
|
|
39501
41776
|
|
|
39502
41777
|
if (opts.merge_files && files.length > 1) {
|
|
@@ -39613,9 +41888,12 @@ ${svg}
|
|
|
39613
41888
|
});
|
|
39614
41889
|
}
|
|
39615
41890
|
|
|
39616
|
-
// Let the web UI replace
|
|
41891
|
+
// Let the web UI replace path-based imports with a browser-friendly resolver.
|
|
39617
41892
|
function replaceImportFile(func) {
|
|
39618
41893
|
_importFile = func;
|
|
41894
|
+
_importDatasetsFromFile = async function(path, opts) {
|
|
41895
|
+
return normalizeImportedDatasets(await func(path, opts));
|
|
41896
|
+
};
|
|
39619
41897
|
}
|
|
39620
41898
|
|
|
39621
41899
|
function importFile(path, opts) {
|
|
@@ -39623,69 +41901,29 @@ ${svg}
|
|
|
39623
41901
|
}
|
|
39624
41902
|
|
|
39625
41903
|
async function importFileAsync(path, opts) {
|
|
39626
|
-
return
|
|
41904
|
+
return denormalizeImportedDatasets(await importDatasetsFromFile(path, opts));
|
|
39627
41905
|
}
|
|
39628
41906
|
|
|
39629
|
-
|
|
39630
|
-
|
|
39631
|
-
|
|
39632
|
-
|
|
39633
|
-
cache = opts && opts.input || null,
|
|
39634
|
-
cached = cache && (path in cache),
|
|
39635
|
-
content;
|
|
39636
|
-
|
|
39637
|
-
cli.checkFileExists(path, cache);
|
|
39638
|
-
|
|
39639
|
-
if ((fileType == 'shp' || fileType == 'json' || fileType == 'text' || fileType == 'dbf' ||
|
|
39640
|
-
fileType == 'gpkg') && !cached) {
|
|
39641
|
-
// these file types are read incrementally
|
|
39642
|
-
content = null;
|
|
39643
|
-
|
|
39644
|
-
} else if (fileType && isSupportedBinaryInputType(path)) {
|
|
39645
|
-
content = cli.readFile(path, null, cache);
|
|
39646
|
-
if (utils.isString(content)) {
|
|
39647
|
-
// Fix for issue #264 (applyCommands() input is file path instead of binary content)
|
|
39648
|
-
stop$1('Expected binary content, received a string');
|
|
39649
|
-
}
|
|
39650
|
-
|
|
39651
|
-
} else if (fileType) { // string type, e.g. kml, geojson
|
|
39652
|
-
content = cli.readFile(path, encoding || 'utf-8', cache);
|
|
39653
|
-
|
|
39654
|
-
} else if (getFileExtension(path) == 'gz') {
|
|
39655
|
-
var pathgz = path;
|
|
39656
|
-
path = pathgz.replace(/\.gz$/, '');
|
|
39657
|
-
fileType = guessInputFileType(path);
|
|
39658
|
-
if (!fileType) {
|
|
39659
|
-
stop$1('Unrecognized file type:', path);
|
|
39660
|
-
}
|
|
39661
|
-
content = gunzipSync(cli.readFile(pathgz, null, cache), path);
|
|
39662
|
-
|
|
39663
|
-
} else { // type can't be inferred from filename -- try reading as text
|
|
39664
|
-
content = cli.readFile(path, encoding || 'utf-8', cache);
|
|
39665
|
-
fileType = guessInputContentType(content);
|
|
39666
|
-
if (fileType == 'text' && content.indexOf('\ufffd') > -1) {
|
|
39667
|
-
// invalidate string data that contains the 'replacement character'
|
|
39668
|
-
fileType = null;
|
|
39669
|
-
}
|
|
39670
|
-
}
|
|
41907
|
+
// Canonical path import interface.
|
|
41908
|
+
async function importDatasetsFromFile(path, opts) {
|
|
41909
|
+
return _importDatasetsFromFile(path, opts);
|
|
41910
|
+
}
|
|
39671
41911
|
|
|
39672
|
-
|
|
39673
|
-
|
|
39674
|
-
}
|
|
39675
|
-
input[fileType] = {filename: path, content: content};
|
|
39676
|
-
content = null; // for g.c.
|
|
39677
|
-
if (fileType == 'shp' || fileType == 'dbf') {
|
|
39678
|
-
readShapefileAuxFiles(path, input, cache);
|
|
39679
|
-
} else if (isRasterImageInputType(fileType)) {
|
|
39680
|
-
readRasterImageAuxFiles(path, input, cache);
|
|
39681
|
-
}
|
|
39682
|
-
if (fileType == 'shp' && !input.dbf) {
|
|
39683
|
-
message(utils.format("[%s] .dbf file is missing - shapes imported without attribute data.", path));
|
|
39684
|
-
}
|
|
41912
|
+
var _importFile = function(path, opts) {
|
|
41913
|
+
var input = prepareImportFile(path, opts);
|
|
39685
41914
|
return importContent(input, opts);
|
|
39686
41915
|
};
|
|
39687
41916
|
|
|
39688
|
-
async function
|
|
41917
|
+
var _importDatasetsFromFile = async function(path, opts) {
|
|
41918
|
+
var input = prepareImportFile(path, opts);
|
|
41919
|
+
return importDatasetsFromContent(input, opts);
|
|
41920
|
+
};
|
|
41921
|
+
|
|
41922
|
+
// Read a file (or cache entry) and collect any sidecars into the normalized
|
|
41923
|
+
// content-group shape consumed by the content importers.
|
|
41924
|
+
// File acquisition is currently synchronous in both import paths; only some
|
|
41925
|
+
// format parsers require asynchronous work.
|
|
41926
|
+
function prepareImportFile(path, opts) {
|
|
39689
41927
|
var fileType = guessInputFileType(path),
|
|
39690
41928
|
input = {},
|
|
39691
41929
|
encoding = opts && opts.encoding || null,
|
|
@@ -39695,7 +41933,8 @@ ${svg}
|
|
|
39695
41933
|
|
|
39696
41934
|
cli.checkFileExists(path, cache);
|
|
39697
41935
|
|
|
39698
|
-
if ((fileType == 'shp' || fileType == 'json' || fileType == 'text' || fileType == 'dbf'
|
|
41936
|
+
if ((fileType == 'shp' || fileType == 'json' || fileType == 'text' || fileType == 'dbf' ||
|
|
41937
|
+
fileType == 'gpkg') && !cached) {
|
|
39699
41938
|
// these file types are read incrementally
|
|
39700
41939
|
content = null;
|
|
39701
41940
|
|
|
@@ -39738,37 +41977,10 @@ ${svg}
|
|
|
39738
41977
|
if (fileType == 'shp' && !input.dbf) {
|
|
39739
41978
|
message(utils.format("[%s] .dbf file is missing - shapes imported without attribute data.", path));
|
|
39740
41979
|
}
|
|
39741
|
-
return
|
|
39742
|
-
}
|
|
39743
|
-
|
|
39744
|
-
// Import multiple files to a single dataset
|
|
39745
|
-
function importFilesTogether(files, opts) {
|
|
39746
|
-
var unbuiltTopology = false;
|
|
39747
|
-
var datasets = files.reduce(function(memo, fname) {
|
|
39748
|
-
// import without topology or snapping
|
|
39749
|
-
var importOpts = utils.defaults({no_topology: true, snap: false, snap_interval: null, files: [fname]}, opts);
|
|
39750
|
-
var imported = normalizeImportedDatasets(importFile(fname, importOpts));
|
|
39751
|
-
// check if dataset contains non-topological paths
|
|
39752
|
-
// TODO: may also need to rebuild topology if multiple topojson files are merged
|
|
39753
|
-
imported.forEach(function(dataset) {
|
|
39754
|
-
if (dataset.arcs && dataset.arcs.size() > 0 && dataset.info.input_formats[0] != 'topojson') {
|
|
39755
|
-
unbuiltTopology = true;
|
|
39756
|
-
}
|
|
39757
|
-
memo.push(dataset);
|
|
39758
|
-
});
|
|
39759
|
-
return memo;
|
|
39760
|
-
}, []);
|
|
39761
|
-
var combined = mergeDatasets(datasets);
|
|
39762
|
-
// Build topology, if needed
|
|
39763
|
-
// TODO: consider updating topology of TopoJSON files instead of concatenating arcs
|
|
39764
|
-
// (but problem of mismatched coordinates due to quantization in input files.)
|
|
39765
|
-
if (unbuiltTopology && !opts.no_topology) {
|
|
39766
|
-
cleanPathsAfterImport(combined, opts);
|
|
39767
|
-
buildTopology(combined);
|
|
39768
|
-
}
|
|
39769
|
-
return combined;
|
|
41980
|
+
return input;
|
|
39770
41981
|
}
|
|
39771
41982
|
|
|
41983
|
+
// Import multiple files to a single dataset.
|
|
39772
41984
|
async function importFilesTogetherAsync(files, opts) {
|
|
39773
41985
|
var unbuiltTopology = false;
|
|
39774
41986
|
var datasets = [];
|
|
@@ -39776,7 +41988,7 @@ ${svg}
|
|
|
39776
41988
|
for (var fname of files) {
|
|
39777
41989
|
// import without topology or snapping
|
|
39778
41990
|
var importOpts = utils.defaults({no_topology: true, snap: false, snap_interval: null, files: [fname]}, opts);
|
|
39779
|
-
var imported =
|
|
41991
|
+
var imported = await importDatasetsFromFile(fname, importOpts);
|
|
39780
41992
|
imported.forEach(function(dataset) {
|
|
39781
41993
|
if (dataset.arcs && dataset.arcs.size() > 0 && dataset.info.input_formats[0] != 'topojson') {
|
|
39782
41994
|
unbuiltTopology = true;
|
|
@@ -39838,6 +42050,10 @@ ${svg}
|
|
|
39838
42050
|
return Array.isArray(datasetOrArray) ? datasetOrArray : [datasetOrArray];
|
|
39839
42051
|
}
|
|
39840
42052
|
|
|
42053
|
+
function denormalizeImportedDatasets(datasets) {
|
|
42054
|
+
return datasets.length == 1 ? datasets[0] : datasets;
|
|
42055
|
+
}
|
|
42056
|
+
|
|
39841
42057
|
function normalizeImportedTarget(datasets) {
|
|
39842
42058
|
if (datasets.length == 1) return datasets[0];
|
|
39843
42059
|
return {
|
|
@@ -39937,9 +42153,9 @@ ${svg}
|
|
|
39937
42153
|
|
|
39938
42154
|
var FileImport = /*#__PURE__*/Object.freeze({
|
|
39939
42155
|
__proto__: null,
|
|
42156
|
+
importDatasetsFromFile: importDatasetsFromFile,
|
|
39940
42157
|
importFile: importFile,
|
|
39941
42158
|
importFileAsync: importFileAsync,
|
|
39942
|
-
importFilesTogether: importFilesTogether,
|
|
39943
42159
|
importFilesTogetherAsync: importFilesTogetherAsync,
|
|
39944
42160
|
replaceImportFile: replaceImportFile
|
|
39945
42161
|
});
|
|
@@ -39969,31 +42185,37 @@ ${svg}
|
|
|
39969
42185
|
return /[$][{]/.test(name);
|
|
39970
42186
|
}
|
|
39971
42187
|
|
|
39972
|
-
function
|
|
42188
|
+
async function findCommandSourceAsync(sourceName, catalog, opts) {
|
|
39973
42189
|
var source = catalog.findSingleLayer(sourceName);
|
|
39974
|
-
var
|
|
42190
|
+
var sourceDatasets;
|
|
39975
42191
|
if (!source) {
|
|
39976
|
-
//
|
|
39977
|
-
|
|
39978
|
-
|
|
39979
|
-
// clip/erase -- topology is built later, when datasets are combined
|
|
39980
|
-
sourceDataset = importFile(sourceName, utils.defaults({no_topology: true}, opts));
|
|
39981
|
-
if (!sourceDataset) {
|
|
39982
|
-
stop$1(utils.format('Unable to find source [%s]', sourceName));
|
|
39983
|
-
} else if (sourceDataset.layers.length > 1) {
|
|
39984
|
-
stop$1('Multiple-layer sources are not supported');
|
|
39985
|
-
}
|
|
39986
|
-
// mark as disposable to indicate that data can be mutated
|
|
39987
|
-
source = {dataset: sourceDataset, layer: sourceDataset.layers[0], disposable: true};
|
|
42192
|
+
// Source-file topology is built later if it is needed by the command.
|
|
42193
|
+
sourceDatasets = await importDatasetsFromFile(sourceName, utils.defaults({no_topology: true}, opts));
|
|
42194
|
+
source = getImportedSource(sourceDatasets, sourceName);
|
|
39988
42195
|
}
|
|
39989
42196
|
return source;
|
|
39990
42197
|
}
|
|
39991
42198
|
|
|
42199
|
+
function getImportedSource(sourceDatasets, sourceName) {
|
|
42200
|
+
if (sourceDatasets.length != 1) {
|
|
42201
|
+
if (sourceDatasets.length > 1) {
|
|
42202
|
+
stop$1('Multiple-dataset sources are not supported');
|
|
42203
|
+
}
|
|
42204
|
+
stop$1(utils.format('Unable to find source [%s]', sourceName));
|
|
42205
|
+
}
|
|
42206
|
+
var sourceDataset = sourceDatasets[0];
|
|
42207
|
+
if (sourceDataset.layers.length > 1) {
|
|
42208
|
+
stop$1('Multiple-layer sources are not supported');
|
|
42209
|
+
}
|
|
42210
|
+
// mark as disposable to indicate that data can be mutated
|
|
42211
|
+
return {dataset: sourceDataset, layer: sourceDataset.layers[0], disposable: true};
|
|
42212
|
+
}
|
|
42213
|
+
|
|
39992
42214
|
var SourceUtils = /*#__PURE__*/Object.freeze({
|
|
39993
42215
|
__proto__: null,
|
|
39994
42216
|
convertInterpolatedName: convertInterpolatedName,
|
|
39995
42217
|
convertSourceName: convertSourceName,
|
|
39996
|
-
|
|
42218
|
+
findCommandSourceAsync: findCommandSourceAsync
|
|
39997
42219
|
});
|
|
39998
42220
|
|
|
39999
42221
|
// Catalog contains zero or more multi-layer datasets
|
|
@@ -41375,7 +43597,7 @@ ${svg}
|
|
|
41375
43597
|
|
|
41376
43598
|
function getPlanarFilter(interval) {
|
|
41377
43599
|
return function(a, b) {
|
|
41378
|
-
return distance2D(a[0], a[1], b[0], b[1]) <= interval;
|
|
43600
|
+
return distance2D$1(a[0], a[1], b[0], b[1]) <= interval;
|
|
41379
43601
|
};
|
|
41380
43602
|
}
|
|
41381
43603
|
|
|
@@ -41808,7 +44030,7 @@ ${svg}
|
|
|
41808
44030
|
}
|
|
41809
44031
|
|
|
41810
44032
|
function probeIsExposed(index, skipA, skipB, vx, vy, p) {
|
|
41811
|
-
var r = distance2D(vx, vy, p[0], p[1]);
|
|
44033
|
+
var r = distance2D$1(vx, vy, p[0], p[1]);
|
|
41812
44034
|
if (!(r > 0)) return false;
|
|
41813
44035
|
var r2 = r * r * 0.98 * 0.98;
|
|
41814
44036
|
var px = p[0], py = p[1];
|
|
@@ -41848,6 +44070,13 @@ ${svg}
|
|
|
41848
44070
|
return new GeographicLib.Geodesic.Geodesic(P.a, f);
|
|
41849
44071
|
}
|
|
41850
44072
|
|
|
44073
|
+
function getGeodesicDistanceFunction(P) {
|
|
44074
|
+
var geod = getGeodesic(P);
|
|
44075
|
+
return function(lng, lat, lng2, lat2) {
|
|
44076
|
+
return geod.Inverse(lat, lng, lat2, lng2).s12;
|
|
44077
|
+
};
|
|
44078
|
+
}
|
|
44079
|
+
|
|
41851
44080
|
function interpolatePoint2D(ax, ay, bx, by, k) {
|
|
41852
44081
|
var j = 1 - k;
|
|
41853
44082
|
return [ax * j + bx * k, ay * j + by * k];
|
|
@@ -41950,6 +44179,7 @@ ${svg}
|
|
|
41950
44179
|
bearingDegrees2D: bearingDegrees2D,
|
|
41951
44180
|
getBearingFunction: getBearingFunction,
|
|
41952
44181
|
getFastGeodeticSegmentFunction: getFastGeodeticSegmentFunction,
|
|
44182
|
+
getGeodesicDistanceFunction: getGeodesicDistanceFunction,
|
|
41953
44183
|
getGeodeticSegmentFunction: getGeodeticSegmentFunction,
|
|
41954
44184
|
getInterpolationFunction: getInterpolationFunction,
|
|
41955
44185
|
getPlanarSegmentEndpoint: getPlanarSegmentEndpoint,
|
|
@@ -43390,7 +45620,7 @@ ${svg}
|
|
|
43390
45620
|
|
|
43391
45621
|
function stopIfBufferReachesPole(shape, dist) {
|
|
43392
45622
|
var maxAbsLat = 0;
|
|
43393
|
-
var angularDist = dist / R$3 * R2D$
|
|
45623
|
+
var angularDist = dist / R$3 * R2D$6;
|
|
43394
45624
|
(shape || []).forEach(function(path) {
|
|
43395
45625
|
latLngPathIter.init(path);
|
|
43396
45626
|
while (latLngPathIter.hasNext()) {
|
|
@@ -43881,11 +46111,11 @@ ${svg}
|
|
|
43881
46111
|
if (a[0] === b[0] && a[1] === b[1]) continue;
|
|
43882
46112
|
bearing = bearingDegrees2D(a[0], a[1], b[0], b[1]);
|
|
43883
46113
|
probe = getOffsetPoint(a[0], a[1], bearing - 90, dist);
|
|
43884
|
-
rUnits = distance2D(a[0], a[1], probe[0], probe[1]);
|
|
46114
|
+
rUnits = distance2D$1(a[0], a[1], probe[0], probe[1]);
|
|
43885
46115
|
// perpendicular stab lines every ~0.35r along the band, so cuts
|
|
43886
46116
|
// affecting only part of a long segment cannot slip between them
|
|
43887
46117
|
m = Math.max(1, Math.min(60,
|
|
43888
|
-
Math.ceil(distance2D(a[0], a[1], b[0], b[1]) / (rUnits * 0.35))));
|
|
46118
|
+
Math.ceil(distance2D$1(a[0], a[1], b[0], b[1]) / (rUnits * 0.35))));
|
|
43889
46119
|
for (ti = 0; ti < m; ti++) {
|
|
43890
46120
|
t = (ti + 0.5) / m;
|
|
43891
46121
|
base = [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
|
|
@@ -43919,14 +46149,14 @@ ${svg}
|
|
|
43919
46149
|
function findAtRiskSegments(verts, dist) {
|
|
43920
46150
|
var n = verts.length;
|
|
43921
46151
|
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]);
|
|
46152
|
+
var rUnits = distance2D$1(verts[0][0], verts[0][1], probe0[0], probe0[1]);
|
|
43923
46153
|
var cumLen = new Float64Array(n);
|
|
43924
46154
|
var riskVert = new Uint8Array(n);
|
|
43925
46155
|
var any = false;
|
|
43926
46156
|
var i, j, k;
|
|
43927
46157
|
for (i = 1; i < n; i++) {
|
|
43928
46158
|
cumLen[i] = cumLen[i - 1] +
|
|
43929
|
-
distance2D(verts[i - 1][0], verts[i - 1][1], verts[i][0], verts[i][1]);
|
|
46159
|
+
distance2D$1(verts[i - 1][0], verts[i - 1][1], verts[i][0], verts[i][1]);
|
|
43930
46160
|
}
|
|
43931
46161
|
// the ring's start radial and cap-closing radial can cut nearby bands
|
|
43932
46162
|
riskVert[0] = riskVert[n - 1] = 1;
|
|
@@ -43998,7 +46228,7 @@ ${svg}
|
|
|
43998
46228
|
j = ids[k];
|
|
43999
46229
|
if (j <= i) continue;
|
|
44000
46230
|
var pathDist = cumLen[j] - cumLen[i];
|
|
44001
|
-
var d = distance2D(verts[i][0], verts[i][1], verts[j][0], verts[j][1]);
|
|
46231
|
+
var d = distance2D$1(verts[i][0], verts[i][1], verts[j][0], verts[j][1]);
|
|
44002
46232
|
// 0.8 flags vertex pairs whose intervening path bends by
|
|
44003
46233
|
// ~130 degrees or more (a circle's chord/arc ratio is 2/pi,
|
|
44004
46234
|
// so even gentle loops qualify)
|
|
@@ -44050,7 +46280,7 @@ ${svg}
|
|
|
44050
46280
|
// at the near end is seeded by an upward ray over those same edges.
|
|
44051
46281
|
function getRingStabber(rings, dist) {
|
|
44052
46282
|
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]);
|
|
46283
|
+
var slabW = distance2D$1(rings[0][0][0], rings[0][0][1], probe0[0], probe0[1]);
|
|
44054
46284
|
var ex = [], ey = [], fx = [], fy = []; // edge data
|
|
44055
46285
|
var slabs = new Map();
|
|
44056
46286
|
rings.forEach(function(ring) {
|
|
@@ -44150,8 +46380,8 @@ ${svg}
|
|
|
44150
46380
|
var eps = dist * 1e-4;
|
|
44151
46381
|
var aIn = getOffsetPoint(a[0], a[1], bearing, eps);
|
|
44152
46382
|
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) {
|
|
46383
|
+
if (distance2D$1(a[0], a[1], b[0], b[1]) <
|
|
46384
|
+
distance2D$1(a[0], a[1], aIn[0], aIn[1]) * 3) {
|
|
44155
46385
|
return; // segment too short to inset
|
|
44156
46386
|
}
|
|
44157
46387
|
rings.push([
|
|
@@ -44548,7 +46778,7 @@ ${svg}
|
|
|
44548
46778
|
}
|
|
44549
46779
|
|
|
44550
46780
|
function shallowAngleJoin(a, b, cx, cy, dist) {
|
|
44551
|
-
var gap = distance2D(a[0], a[1], b[0], b[1]);
|
|
46781
|
+
var gap = distance2D$1(a[0], a[1], b[0], b[1]);
|
|
44552
46782
|
var radius = getJoinExtensionDistance(cx, cy, a, b, dist);
|
|
44553
46783
|
if (gap > radius * 1e-3) return null;
|
|
44554
46784
|
return [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
|
|
@@ -45507,12 +47737,12 @@ ${svg}
|
|
|
45507
47737
|
}
|
|
45508
47738
|
|
|
45509
47739
|
function projectBufferPoint(x, y, spherical) {
|
|
45510
|
-
return spherical ? [x * D2R$
|
|
47740
|
+
return spherical ? [x * D2R$7 * R$3, Math.log(Math.tan(Math.PI / 4 + y * D2R$7 / 2)) * R$3] :
|
|
45511
47741
|
[x, y];
|
|
45512
47742
|
}
|
|
45513
47743
|
|
|
45514
47744
|
function unprojectBufferPoint(x, y, spherical) {
|
|
45515
|
-
return spherical ? [x / (D2R$
|
|
47745
|
+
return spherical ? [x / (D2R$7 * R$3), (2 * Math.atan(Math.exp(y / R$3)) - Math.PI / 2) / (D2R$7)] :
|
|
45516
47746
|
[x, y];
|
|
45517
47747
|
}
|
|
45518
47748
|
|
|
@@ -50183,7 +52413,7 @@ ${svg}
|
|
|
50183
52413
|
var maxAbsLat = Math.max(Math.abs(bounds.ymin), Math.abs(bounds.ymax));
|
|
50184
52414
|
var maxPositiveDistance = getMaxPositiveBufferDistance(lyr, dataset, opts);
|
|
50185
52415
|
if (!(maxPositiveDistance > 0)) return false;
|
|
50186
|
-
return maxAbsLat + maxPositiveDistance / R$3 * R2D$
|
|
52416
|
+
return maxAbsLat + maxPositiveDistance / R$3 * R2D$6 >= 90 - POLAR_BUFFER_MARGIN_DEGREES;
|
|
50187
52417
|
}
|
|
50188
52418
|
|
|
50189
52419
|
function getMaxPositiveBufferDistance(lyr, dataset, opts) {
|
|
@@ -51435,7 +53665,7 @@ ${svg}
|
|
|
51435
53665
|
}
|
|
51436
53666
|
|
|
51437
53667
|
function getCoordinateDistance(distance, arcs) {
|
|
51438
|
-
return arcs.isPlanar() ? distance : distance / R$3 * R2D$
|
|
53668
|
+
return arcs.isPlanar() ? distance : distance / R$3 * R2D$6;
|
|
51439
53669
|
}
|
|
51440
53670
|
|
|
51441
53671
|
// @shapeIndex: chunk-bounds index of the source shape (see buildShapeSegmentIndex)
|
|
@@ -52103,7 +54333,8 @@ ${svg}
|
|
|
52103
54333
|
// TODO: consider projections that may or may not be aligned,
|
|
52104
54334
|
// depending on parameters
|
|
52105
54335
|
if (inList(P, 'cassini,gnom,bertin1953,chamb,ob_tran,tpeqd,healpix,rhealpix,' +
|
|
52106
|
-
'ocea,omerc,tmerc,etmerc,nicol'
|
|
54336
|
+
'ocea,omerc,tmerc,etmerc,nicol,dymaxion,dymaxion2,butterfly,butterfly2,' +
|
|
54337
|
+
'cahill_keyes,narukawa2022')) {
|
|
52107
54338
|
return false;
|
|
52108
54339
|
}
|
|
52109
54340
|
if (isAzimuthal(P)) {
|
|
@@ -52155,16 +54386,16 @@ ${svg}
|
|
|
52155
54386
|
}
|
|
52156
54387
|
|
|
52157
54388
|
function getRotationFunction2(rotation, inv) {
|
|
52158
|
-
var a = (rotation[0] || 0) * D2R$
|
|
52159
|
-
b = (rotation[1] || 0) * D2R$
|
|
52160
|
-
c = (rotation[2] || 0) * D2R$
|
|
54389
|
+
var a = (rotation[0] || 0) * D2R$7,
|
|
54390
|
+
b = (rotation[1] || 0) * D2R$7,
|
|
54391
|
+
c = (rotation[2] || 0) * D2R$7;
|
|
52161
54392
|
return function(p) {
|
|
52162
|
-
p[0] *= D2R$
|
|
52163
|
-
p[1] *= D2R$
|
|
54393
|
+
p[0] *= D2R$7;
|
|
54394
|
+
p[1] *= D2R$7;
|
|
52164
54395
|
var rotate = inv ? rotatePointInv : rotatePoint;
|
|
52165
54396
|
rotate(p, a, b, c);
|
|
52166
|
-
p[0] *= R2D$
|
|
52167
|
-
p[1] *= R2D$
|
|
54397
|
+
p[0] *= R2D$6;
|
|
54398
|
+
p[1] *= R2D$6;
|
|
52168
54399
|
return p;
|
|
52169
54400
|
};
|
|
52170
54401
|
}
|
|
@@ -52623,6 +54854,9 @@ ${svg}
|
|
|
52623
54854
|
};
|
|
52624
54855
|
|
|
52625
54856
|
function getProjectionTopology(P) {
|
|
54857
|
+
if (P && P.__projection_topology) {
|
|
54858
|
+
return P.__projection_topology;
|
|
54859
|
+
}
|
|
52626
54860
|
var defn = P && projectionTopologies[getCrsSlug(P)];
|
|
52627
54861
|
if (!defn) return null;
|
|
52628
54862
|
var lon0 = P.lam0 * 180 / Math.PI;
|
|
@@ -52646,6 +54880,11 @@ ${svg}
|
|
|
52646
54880
|
}
|
|
52647
54881
|
|
|
52648
54882
|
function isInterruptedProjection(P) {
|
|
54883
|
+
if (P && P.__projection_topology) {
|
|
54884
|
+
return P.__projection_topology.seams.some(function(o) {
|
|
54885
|
+
return o.type == 'cut';
|
|
54886
|
+
});
|
|
54887
|
+
}
|
|
52649
54888
|
var defn = P && projectionTopologies[getCrsSlug(P)];
|
|
52650
54889
|
return !!defn && defn.seams.some(function(o) {
|
|
52651
54890
|
return o.type == 'cut';
|
|
@@ -52719,6 +54958,9 @@ ${svg}
|
|
|
52719
54958
|
|
|
52720
54959
|
// Return projected polygon extent of both clipped and unclipped projections
|
|
52721
54960
|
function getPolygonDataset$1(src, dest, opts) {
|
|
54961
|
+
if (dest.__projected_outline) {
|
|
54962
|
+
return getCustomProjectedOutline(dest, 'polygon');
|
|
54963
|
+
}
|
|
52722
54964
|
// use clipping area if projection is clipped
|
|
52723
54965
|
var dataset = getUnprojectedBoundingPolygon(src, dest, opts);
|
|
52724
54966
|
if (!dataset) {
|
|
@@ -52734,6 +54976,9 @@ ${svg}
|
|
|
52734
54976
|
|
|
52735
54977
|
// Return projected outline of clipped projections
|
|
52736
54978
|
function getOutlineDataset(src, dest, opts) {
|
|
54979
|
+
if (dest.__projected_outline) {
|
|
54980
|
+
return getCustomProjectedOutline(dest, 'polyline');
|
|
54981
|
+
}
|
|
52737
54982
|
var dataset = getUnprojectedBoundingPolygon(src, dest, opts);
|
|
52738
54983
|
if (!dataset && isInterruptedProjection(dest)) {
|
|
52739
54984
|
dataset = getBoundingRectangle(dest, {clip_bbox: [-180, -90, 180, 90]});
|
|
@@ -52745,10 +54990,33 @@ ${svg}
|
|
|
52745
54990
|
removeTinyFootprintRings(dataset);
|
|
52746
54991
|
}
|
|
52747
54992
|
dataset.layers[0].geometry_type = 'polyline';
|
|
54993
|
+
if (dest.__remove_outline_extreme_connectors) {
|
|
54994
|
+
dataset = removeOutlineExtremeConnectors(dataset);
|
|
54995
|
+
}
|
|
52748
54996
|
}
|
|
52749
54997
|
return dataset || null;
|
|
52750
54998
|
}
|
|
52751
54999
|
|
|
55000
|
+
function getCustomProjectedOutline(P, geometryType) {
|
|
55001
|
+
var rings = P.__projected_outline.map(function(ring) {
|
|
55002
|
+
return ring.map(function(p) {
|
|
55003
|
+
return [
|
|
55004
|
+
P.fr_meter * (P.a * p[0] + P.x0),
|
|
55005
|
+
P.fr_meter * (P.a * p[1] + P.y0)
|
|
55006
|
+
];
|
|
55007
|
+
});
|
|
55008
|
+
});
|
|
55009
|
+
var dataset = importGeoJSON({
|
|
55010
|
+
type: rings.length == 1 ? 'Polygon' : 'MultiPolygon',
|
|
55011
|
+
coordinates: rings.length == 1 ? rings : rings.map(function(ring) {
|
|
55012
|
+
return [ring];
|
|
55013
|
+
})
|
|
55014
|
+
});
|
|
55015
|
+
dataset.layers[0].geometry_type = geometryType;
|
|
55016
|
+
dataset.info.crs = P;
|
|
55017
|
+
return dataset;
|
|
55018
|
+
}
|
|
55019
|
+
|
|
52752
55020
|
// Narrow pre-projection gutters can leave zero-area rings after a rotated
|
|
52753
55021
|
// world boundary is cleaned. Remove only rings that are negligible relative
|
|
52754
55022
|
// to the main footprint; legitimate disconnected lobes are retained.
|
|
@@ -52770,6 +55038,54 @@ ${svg}
|
|
|
52770
55038
|
dissolveArcs(dataset);
|
|
52771
55039
|
}
|
|
52772
55040
|
|
|
55041
|
+
// Polygon clipping closes open interrupted-projection boundaries to form valid
|
|
55042
|
+
// rings. In Cahill-Keyes, some closures connect unrelated polar facet images.
|
|
55043
|
+
// Remove only long, nearly horizontal segments at the map's vertical extent.
|
|
55044
|
+
function removeOutlineExtremeConnectors(dataset) {
|
|
55045
|
+
var bounds = getDatasetBounds(dataset);
|
|
55046
|
+
var width = bounds.width();
|
|
55047
|
+
var height = bounds.height();
|
|
55048
|
+
var edgeTolerance = height * 1e-3;
|
|
55049
|
+
var flatTolerance = height * 1e-4;
|
|
55050
|
+
var arcs = dataset.arcs;
|
|
55051
|
+
var lyr = dataset.layers[0];
|
|
55052
|
+
var paths = [];
|
|
55053
|
+
var removed = 0;
|
|
55054
|
+
lyr.shapes.forEach(function(shp) {
|
|
55055
|
+
(shp || []).forEach(function(path) {
|
|
55056
|
+
var points = [];
|
|
55057
|
+
var iter = arcs.getShapeIter(path);
|
|
55058
|
+
while (iter.hasNext()) {
|
|
55059
|
+
points.push([iter.x, iter.y]);
|
|
55060
|
+
}
|
|
55061
|
+
var part = [points[0]];
|
|
55062
|
+
for (var i = 1; i < points.length; i++) {
|
|
55063
|
+
var a = points[i - 1];
|
|
55064
|
+
var b = points[i];
|
|
55065
|
+
var atTop = Math.min(a[1], b[1]) >= bounds.ymax - edgeTolerance;
|
|
55066
|
+
var atBottom = Math.max(a[1], b[1]) <= bounds.ymin + edgeTolerance;
|
|
55067
|
+
var isFlat = Math.abs(a[1] - b[1]) <= flatTolerance;
|
|
55068
|
+
var isWide = Math.abs(a[0] - b[0]) >= width * 0.02;
|
|
55069
|
+
if ((atTop || atBottom) && isFlat && isWide) {
|
|
55070
|
+
if (part.length > 1) paths.push(part);
|
|
55071
|
+
part = [b];
|
|
55072
|
+
removed++;
|
|
55073
|
+
} else {
|
|
55074
|
+
part.push(b);
|
|
55075
|
+
}
|
|
55076
|
+
}
|
|
55077
|
+
if (part.length > 1) paths.push(part);
|
|
55078
|
+
});
|
|
55079
|
+
});
|
|
55080
|
+
if (removed === 0) return dataset;
|
|
55081
|
+
var cleaned = importGeoJSON({
|
|
55082
|
+
type: paths.length == 1 ? 'LineString' : 'MultiLineString',
|
|
55083
|
+
coordinates: paths.length == 1 ? paths[0] : paths
|
|
55084
|
+
});
|
|
55085
|
+
cleaned.info = dataset.info;
|
|
55086
|
+
return cleaned;
|
|
55087
|
+
}
|
|
55088
|
+
|
|
52773
55089
|
function getBoundingRectangle(dest, opts) {
|
|
52774
55090
|
var bbox = opts.clip_bbox || getDefaultClipBBox(dest);
|
|
52775
55091
|
var rotation = getRotationParams(dest);
|
|
@@ -52949,19 +55265,79 @@ ${svg}
|
|
|
52949
55265
|
var pathLayers = dataset.layers.filter(layerHasPaths);
|
|
52950
55266
|
var cutPaths, geojson, clip;
|
|
52951
55267
|
if (!topology || pathLayers.length === 0) return false;
|
|
55268
|
+
insertProjectionTopologyVertices(dataset, topology);
|
|
52952
55269
|
cutPaths = topology.seams
|
|
52953
55270
|
.filter(function(o) { return o.type == 'cut'; })
|
|
52954
55271
|
.reduce(function(memo, o) {
|
|
55272
|
+
if (o.paths) return memo.concat(o.paths);
|
|
52955
55273
|
return memo.concat(getCutSeamPaths(o.coordinates));
|
|
52956
55274
|
}, []);
|
|
52957
55275
|
if (cutPaths.length === 0) return false;
|
|
52958
55276
|
geojson = getCutMaskGeoJSON(cutPaths);
|
|
52959
55277
|
if (geojson.features.length === 0) return false;
|
|
52960
55278
|
clip = importGeoJSON(geojson);
|
|
55279
|
+
if (topology.findRegion) {
|
|
55280
|
+
// Polyhedral cut masks meet and overlap at facet vertices. Union them before
|
|
55281
|
+
// clipping so overlapping source polygons are not interpreted as holes.
|
|
55282
|
+
addIntersectionCuts(clip, {});
|
|
55283
|
+
clip.layers[0] = dissolvePolygonLayer2(clip.layers[0], clip, {quiet: true});
|
|
55284
|
+
}
|
|
52961
55285
|
clipLayersInPlace(pathLayers, clip, dataset, 'erase', getInternalClipOpts());
|
|
52962
55286
|
return true;
|
|
52963
55287
|
}
|
|
52964
55288
|
|
|
55289
|
+
// Add a vertex wherever a path changes projection regions. This preserves a
|
|
55290
|
+
// bend at attached polyhedral edges without opening a gap; cut edges are
|
|
55291
|
+
// subsequently disconnected by the narrow seam masks below.
|
|
55292
|
+
function insertProjectionTopologyVertices(dataset, topology) {
|
|
55293
|
+
if (!dataset.arcs || !topology.findRegion) return;
|
|
55294
|
+
var findRegion = topology.findTransitionRegion || topology.findRegion;
|
|
55295
|
+
editArcs(dataset.arcs, function(append, x, y, prevX, prevY, i) {
|
|
55296
|
+
if (i > 0) {
|
|
55297
|
+
findRegionTransitions(prevX, prevY, x, y, findRegion).forEach(append);
|
|
55298
|
+
}
|
|
55299
|
+
append([x, y]);
|
|
55300
|
+
});
|
|
55301
|
+
}
|
|
55302
|
+
|
|
55303
|
+
function findRegionTransitions(ax, ay, bx, by, findRegion) {
|
|
55304
|
+
var interval = 0.5;
|
|
55305
|
+
var n = Math.max(1, Math.ceil(Math.max(Math.abs(bx - ax), Math.abs(by - ay)) / interval));
|
|
55306
|
+
var points = [];
|
|
55307
|
+
var t0 = 0;
|
|
55308
|
+
var region0 = findRegion(ax, ay);
|
|
55309
|
+
for (var i = 1; i <= n; i++) {
|
|
55310
|
+
var t1 = i / n;
|
|
55311
|
+
var x1 = ax + (bx - ax) * t1;
|
|
55312
|
+
var y1 = ay + (by - ay) * t1;
|
|
55313
|
+
var region1 = findRegion(x1, y1);
|
|
55314
|
+
if (region0 != region1 && region0 >= 0 && region1 >= 0) {
|
|
55315
|
+
var t = findRegionBoundary(ax, ay, bx, by, t0, region0, t1, findRegion);
|
|
55316
|
+
var p = [ax + (bx - ax) * t, ay + (by - ay) * t];
|
|
55317
|
+
if (!points.length || Math.abs(t - points[points.length - 1].t) > 1e-10) {
|
|
55318
|
+
p.t = t;
|
|
55319
|
+
points.push(p);
|
|
55320
|
+
}
|
|
55321
|
+
}
|
|
55322
|
+
t0 = t1;
|
|
55323
|
+
region0 = region1;
|
|
55324
|
+
}
|
|
55325
|
+
return points;
|
|
55326
|
+
}
|
|
55327
|
+
|
|
55328
|
+
function findRegionBoundary(ax, ay, bx, by, ta, regionA, tb, findRegion) {
|
|
55329
|
+
for (var i = 0; i < 45; i++) {
|
|
55330
|
+
var tm = (ta + tb) / 2;
|
|
55331
|
+
var regionM = findRegion(ax + (bx - ax) * tm, ay + (by - ay) * tm);
|
|
55332
|
+
if (regionM == regionA) {
|
|
55333
|
+
ta = tm;
|
|
55334
|
+
} else {
|
|
55335
|
+
tb = tm;
|
|
55336
|
+
}
|
|
55337
|
+
}
|
|
55338
|
+
return (ta + tb) / 2;
|
|
55339
|
+
}
|
|
55340
|
+
|
|
52965
55341
|
function clampDataset(dataset, bbox) {
|
|
52966
55342
|
transformPoints(dataset, function(x, y) {
|
|
52967
55343
|
return [utils.clamp(x, bbox[0], bbox[2]), utils.clamp(y, bbox[1], bbox[3])];
|
|
@@ -53016,8 +55392,8 @@ ${svg}
|
|
|
53016
55392
|
// their projected edges follow the projection instead of becoming straight
|
|
53017
55393
|
// chords between seam endpoints.
|
|
53018
55394
|
function getCutMaskGeoJSON(paths) {
|
|
53019
|
-
var e = 1e-8;
|
|
53020
55395
|
var features = paths.map(function(coords) {
|
|
55396
|
+
var e = coords.mask_width || 1e-8;
|
|
53021
55397
|
var ring = getCutMaskRing(densifyPathByInterval(coords, 0.5), e);
|
|
53022
55398
|
return {
|
|
53023
55399
|
type: 'Feature',
|
|
@@ -53030,6 +55406,33 @@ ${svg}
|
|
|
53030
55406
|
}).filter(function(feature) {
|
|
53031
55407
|
return feature.geometry.coordinates[0].length > 3;
|
|
53032
55408
|
});
|
|
55409
|
+
// Give finite seams a small end cap. Without this, a path passing exactly
|
|
55410
|
+
// through a polyhedral vertex can touch only the boundary of several masks
|
|
55411
|
+
// and remain connected across an interruption.
|
|
55412
|
+
paths.forEach(function(coords) {
|
|
55413
|
+
var endpoints = [coords[0], coords[coords.length - 1]];
|
|
55414
|
+
endpoints.forEach(function(p) {
|
|
55415
|
+
var e2 = Math.max(1e-7, (coords.mask_width || 0) * 2);
|
|
55416
|
+
var xmin = Math.max(-180, p[0] - e2);
|
|
55417
|
+
var xmax = Math.min(180, p[0] + e2);
|
|
55418
|
+
var ymin = Math.max(-90, p[1] - e2);
|
|
55419
|
+
var ymax = Math.min(90, p[1] + e2);
|
|
55420
|
+
features.push({
|
|
55421
|
+
type: 'Feature',
|
|
55422
|
+
properties: {},
|
|
55423
|
+
geometry: {
|
|
55424
|
+
type: 'Polygon',
|
|
55425
|
+
coordinates: [[
|
|
55426
|
+
[xmin, ymin],
|
|
55427
|
+
[xmax, ymin],
|
|
55428
|
+
[xmax, ymax],
|
|
55429
|
+
[xmin, ymax],
|
|
55430
|
+
[xmin, ymin]
|
|
55431
|
+
]]
|
|
55432
|
+
}
|
|
55433
|
+
});
|
|
55434
|
+
});
|
|
55435
|
+
});
|
|
53033
55436
|
return {
|
|
53034
55437
|
type: 'FeatureCollection',
|
|
53035
55438
|
features: features
|
|
@@ -53048,8 +55451,8 @@ ${svg}
|
|
|
53048
55451
|
if (len === 0) continue;
|
|
53049
55452
|
var nx = -dy / len * width;
|
|
53050
55453
|
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]);
|
|
55454
|
+
left.push([utils.clamp(coords[i][0] + nx, -180, 180), coords[i][1] + ny]);
|
|
55455
|
+
right.push([utils.clamp(coords[i][0] - nx, -180, 180), coords[i][1] - ny]);
|
|
53053
55456
|
}
|
|
53054
55457
|
var ring = left.concat(right.reverse());
|
|
53055
55458
|
if (ring.length > 0) ring.push(ring[0].concat());
|
|
@@ -57459,7 +59862,7 @@ ${svg}
|
|
|
57459
59862
|
function getSplitLineFunction(crs, dashLen, gapLen, opts) {
|
|
57460
59863
|
var planar = !!opts.planar;
|
|
57461
59864
|
var interpolate = getInterpolationFunction(planar ? null : crs);
|
|
57462
|
-
var distance = isLatLngCRS(crs) ? greatCircleDistance : distance2D;
|
|
59865
|
+
var distance = isLatLngCRS(crs) ? greatCircleDistance : distance2D$1;
|
|
57463
59866
|
var inDash, parts2, interval, scale;
|
|
57464
59867
|
function addPart(coords) {
|
|
57465
59868
|
if (inDash) parts2.push(coords);
|
|
@@ -58330,7 +60733,7 @@ ${svg}
|
|
|
58330
60733
|
// Isometric ("Mercator-stretched") latitude in degrees-in; returns +/-Infinity at
|
|
58331
60734
|
// the poles.
|
|
58332
60735
|
function isometricLatitude(latDeg) {
|
|
58333
|
-
return Math.log(Math.tan(Math.PI / 4 + latDeg * D2R$
|
|
60736
|
+
return Math.log(Math.tan(Math.PI / 4 + latDeg * D2R$7 / 2));
|
|
58334
60737
|
}
|
|
58335
60738
|
|
|
58336
60739
|
// Rhumb-line (loxodrome) interpolation, fraction k of the way from A to B.
|
|
@@ -58355,9 +60758,9 @@ ${svg}
|
|
|
58355
60758
|
// Rhumb-line distance in meters, using the raw longitude delta (so a full-parallel
|
|
58356
60759
|
// sweep returns the parallel's length rather than 0).
|
|
58357
60760
|
function rhumbDistance(a, b) {
|
|
58358
|
-
var phi1 = a[1] * D2R$
|
|
60761
|
+
var phi1 = a[1] * D2R$7, phi2 = b[1] * D2R$7;
|
|
58359
60762
|
var dPhi = phi2 - phi1;
|
|
58360
|
-
var dLambda = (b[0] - a[0]) * D2R$
|
|
60763
|
+
var dLambda = (b[0] - a[0]) * D2R$7; // raw
|
|
58361
60764
|
var dPsi = Math.log(Math.tan(Math.PI / 4 + phi2 / 2) / Math.tan(Math.PI / 4 + phi1 / 2));
|
|
58362
60765
|
var q = Math.abs(dPsi) > 1e-12 ? dPhi / dPsi : Math.cos(phi1);
|
|
58363
60766
|
return Math.sqrt(dPhi * dPhi + q * q * dLambda * dLambda) * R$3;
|
|
@@ -62022,9 +64425,11 @@ ${svg}
|
|
|
62022
64425
|
// project graticule to match dataset
|
|
62023
64426
|
destInfo = getDatasetCrsInfo(dataset);
|
|
62024
64427
|
if (!destInfo.crs) stop$1("Coordinate system is unknown, unable to create a graticule");
|
|
62025
|
-
graticule =
|
|
62026
|
-
|
|
62027
|
-
|
|
64428
|
+
graticule = opts.outline ?
|
|
64429
|
+
createProjectedOutline(destInfo.crs, opts) :
|
|
64430
|
+
opts.polygon ?
|
|
64431
|
+
createProjectedPolygon(destInfo.crs, opts) :
|
|
64432
|
+
createProjectedGraticule(destInfo.crs, opts);
|
|
62028
64433
|
setDatasetCrsInfo(graticule, destInfo);
|
|
62029
64434
|
} else {
|
|
62030
64435
|
graticule = boundary ?
|
|
@@ -62049,6 +64454,12 @@ ${svg}
|
|
|
62049
64454
|
return getPolygonDataset$1(src, dest, opts);
|
|
62050
64455
|
}
|
|
62051
64456
|
|
|
64457
|
+
function createProjectedOutline(dest, opts) {
|
|
64458
|
+
var src = parseCrsString$1('wgs84');
|
|
64459
|
+
return getOutlineDataset(src, dest, opts) ||
|
|
64460
|
+
getPolygonDataset$1(src, dest, opts);
|
|
64461
|
+
}
|
|
64462
|
+
|
|
62052
64463
|
function createUnprojectedGraticule(opts) {
|
|
62053
64464
|
var src = parseCrsString$1('wgs84');
|
|
62054
64465
|
var graticule = importGeoJSON(createGraticule(src, false, opts));
|
|
@@ -62130,12 +64541,15 @@ ${svg}
|
|
|
62130
64541
|
// extended: meridian extends to pole
|
|
62131
64542
|
function createMeridian(x, extended) {
|
|
62132
64543
|
var y0 = ystep <= 15 ? ystep : 0;
|
|
64544
|
+
// An exact pole can have several images in a polyhedral projection.
|
|
64545
|
+
// The separate outline supplies the missing, negligible end interval.
|
|
64546
|
+
var poleInset = outlined ? 1e-4 : 0;
|
|
62133
64547
|
createMeridianPart(x, -90 + y0, 90 - y0);
|
|
62134
64548
|
if (extended && y0 > 0) {
|
|
62135
64549
|
// adding extensions as separate parts, so if the polar coordinates
|
|
62136
64550
|
// 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);
|
|
64551
|
+
createMeridianPart(x, -90 + poleInset, -90 + y0);
|
|
64552
|
+
createMeridianPart(x, 90 - y0, 90 - poleInset);
|
|
62139
64553
|
}
|
|
62140
64554
|
}
|
|
62141
64555
|
|
|
@@ -62145,7 +64559,14 @@ ${svg}
|
|
|
62145
64559
|
}
|
|
62146
64560
|
|
|
62147
64561
|
function createParallel(y) {
|
|
62148
|
-
|
|
64562
|
+
// Avoid joining distinct images of the antimeridian (and the degenerate
|
|
64563
|
+
// polar parallels) when a projected outline is generated separately.
|
|
64564
|
+
var edgeInset = outlined ? 1e-4 : 0;
|
|
64565
|
+
if (outlined && Math.abs(y) == 90) return;
|
|
64566
|
+
var coords = densifyPathByInterval([
|
|
64567
|
+
[-180 + edgeInset, y],
|
|
64568
|
+
[180 - edgeInset, y]
|
|
64569
|
+
], precision);
|
|
62149
64570
|
parallels.push(graticuleFeature(coords, {type: 'parallel', value: y}));
|
|
62150
64571
|
}
|
|
62151
64572
|
}
|
|
@@ -62744,7 +65165,7 @@ ${svg}
|
|
|
62744
65165
|
if (halfLen > 0 === false) {
|
|
62745
65166
|
return [a, b];
|
|
62746
65167
|
}
|
|
62747
|
-
var segLen = distance2D(a, b, c, d);
|
|
65168
|
+
var segLen = distance2D$1(a, b, c, d);
|
|
62748
65169
|
var k;
|
|
62749
65170
|
if (partialLen + segLen >= halfLen) {
|
|
62750
65171
|
k = (halfLen - partialLen) / segLen;
|
|
@@ -64665,7 +67086,7 @@ ${svg}
|
|
|
64665
67086
|
|
|
64666
67087
|
// Source: https://diego.assencio.com/?index=8d6ca3d82151bad815f78addf9b5c1c6
|
|
64667
67088
|
function twoCircleIntersection(c1, r1, c2, r2) {
|
|
64668
|
-
var d = distance2D(c1[0], c1[1], c2[0], c2[1]);
|
|
67089
|
+
var d = distance2D$1(c1[0], c1[1], c2[0], c2[1]);
|
|
64669
67090
|
if (d >= r1 + r2) return 0;
|
|
64670
67091
|
var r1sq = r1 * r1,
|
|
64671
67092
|
r2sq = r2 * r2,
|
|
@@ -65498,6 +67919,9 @@ ${svg}
|
|
|
65498
67919
|
var commands;
|
|
65499
67920
|
try {
|
|
65500
67921
|
commands = parseCommands(commandStr);
|
|
67922
|
+
if (opts && opts.validate_commands) {
|
|
67923
|
+
opts.validate_commands(commands);
|
|
67924
|
+
}
|
|
65501
67925
|
} catch(e) {
|
|
65502
67926
|
e.message = 'Error in command file ' + file + ': ' + e.message;
|
|
65503
67927
|
throw e;
|
|
@@ -65519,6 +67943,9 @@ ${svg}
|
|
|
65519
67943
|
}
|
|
65520
67944
|
if (c.name == 'run') {
|
|
65521
67945
|
c.options._run_depth = depth;
|
|
67946
|
+
if (opts && opts.validate_commands) {
|
|
67947
|
+
c.options.validate_commands = opts.validate_commands;
|
|
67948
|
+
}
|
|
65522
67949
|
}
|
|
65523
67950
|
});
|
|
65524
67951
|
|
|
@@ -65556,6 +67983,9 @@ ${svg}
|
|
|
65556
67983
|
ctx = getBaseContext();
|
|
65557
67984
|
// io proxy adds ability to add datasets dynamically in a required function
|
|
65558
67985
|
ctx.io = getIOProxy();
|
|
67986
|
+
if (opts.input) {
|
|
67987
|
+
Object.assign(ctx.io._cache, opts.input);
|
|
67988
|
+
}
|
|
65559
67989
|
tmp = await evalTemplateExpression(opts.expression, targets, ctx);
|
|
65560
67990
|
if (tmp && !utils.isString(tmp)) {
|
|
65561
67991
|
stop$1('Expected a string containing mapshaper commands; received:', tmp);
|
|
@@ -65564,6 +67994,9 @@ ${svg}
|
|
|
65564
67994
|
// truncate message (command might include a large GeoJSON string in an -i command)
|
|
65565
67995
|
message(`command: [${truncateString(tmp, 150)}]`);
|
|
65566
67996
|
commands = parseCommands(tmp);
|
|
67997
|
+
if (opts.validate_commands) {
|
|
67998
|
+
opts.validate_commands(commands);
|
|
67999
|
+
}
|
|
65567
68000
|
|
|
65568
68001
|
// TODO: remove duplication with mapshaper-run-commands.mjs
|
|
65569
68002
|
var outputArr = opts && opts.output || null;
|
|
@@ -65578,6 +68011,9 @@ ${svg}
|
|
|
65578
68011
|
cmd.name == 'info' && cmd.options.save_to)) {
|
|
65579
68012
|
cmd.options.output = outputArr;
|
|
65580
68013
|
}
|
|
68014
|
+
if (cmd.name == 'run' && opts.validate_commands) {
|
|
68015
|
+
cmd.options.validate_commands = opts.validate_commands;
|
|
68016
|
+
}
|
|
65581
68017
|
});
|
|
65582
68018
|
|
|
65583
68019
|
await utils.promisify(runParsedCommands)(commands, job);
|
|
@@ -66660,7 +69096,7 @@ ${svg}
|
|
|
66660
69096
|
if (totalSegs < 1) return 0;
|
|
66661
69097
|
var MAX_SAMPLES = 100000;
|
|
66662
69098
|
var stride = Math.ceil(totalSegs / MAX_SAMPLES);
|
|
66663
|
-
var distFn = spherical ? greatCircleDistance : distance2D;
|
|
69099
|
+
var distFn = spherical ? greatCircleDistance : distance2D$1;
|
|
66664
69100
|
var lens = [];
|
|
66665
69101
|
var counter = 0;
|
|
66666
69102
|
arcs.forEach3(function(xx, yy) {
|
|
@@ -68114,7 +70550,7 @@ ${svg}
|
|
|
68114
70550
|
if (Object.prototype.hasOwnProperty.call(command, '_sourceOverride')) {
|
|
68115
70551
|
source = command._sourceOverride;
|
|
68116
70552
|
} else if (opts.source) {
|
|
68117
|
-
source =
|
|
70553
|
+
source = await findCommandSourceAsync(convertSourceName(opts.source, targets), job.catalog, opts);
|
|
68118
70554
|
}
|
|
68119
70555
|
|
|
68120
70556
|
// identify command target/input (for postprocessing)
|
|
@@ -68520,7 +70956,7 @@ ${svg}
|
|
|
68520
70956
|
var disposableSourceTargetIndex = -1;
|
|
68521
70957
|
|
|
68522
70958
|
if (command.options.source && !sourceNameIsInterpolated(command.options.source)) {
|
|
68523
|
-
source = resolveSourceForTargets(command, job, targets);
|
|
70959
|
+
source = await resolveSourceForTargets(command, job, targets);
|
|
68524
70960
|
items = moveSourceTargetLast(items, source);
|
|
68525
70961
|
disposableSourceTargetIndex = findLastDisposableSourceTarget(
|
|
68526
70962
|
items,
|
|
@@ -68555,10 +70991,10 @@ ${svg}
|
|
|
68555
70991
|
return job;
|
|
68556
70992
|
}
|
|
68557
70993
|
|
|
68558
|
-
function resolveSourceForTargets(command, job, targets) {
|
|
70994
|
+
async function resolveSourceForTargets(command, job, targets) {
|
|
68559
70995
|
job.startCommand(command);
|
|
68560
70996
|
try {
|
|
68561
|
-
return
|
|
70997
|
+
return await findCommandSourceAsync(
|
|
68562
70998
|
convertSourceName(command.options.source, targets),
|
|
68563
70999
|
job.catalog,
|
|
68564
71000
|
command.options
|
|
@@ -68660,7 +71096,7 @@ ${svg}
|
|
|
68660
71096
|
return name == 'rectangle' || name == 'rectangles' || name == 'filter' && opts.cleanup;
|
|
68661
71097
|
}
|
|
68662
71098
|
|
|
68663
|
-
var version = "0.7.
|
|
71099
|
+
var version = "0.7.46";
|
|
68664
71100
|
|
|
68665
71101
|
// Parse command line args into commands and run them
|
|
68666
71102
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|
|
@@ -68690,7 +71126,6 @@ ${svg}
|
|
|
68690
71126
|
if (err) {
|
|
68691
71127
|
return callback(err);
|
|
68692
71128
|
}
|
|
68693
|
-
if (opts.legacy) return callback(null, toLegacyOutputFormat(outputArr));
|
|
68694
71129
|
return callback(null, toOutputFormat(outputArr));
|
|
68695
71130
|
});
|
|
68696
71131
|
if (opts.promise) return opts.promise;
|
|
@@ -68732,9 +71167,6 @@ ${svg}
|
|
|
68732
71167
|
opts.callback = arg1;
|
|
68733
71168
|
} else if (utils.isFunction(arg2)) {
|
|
68734
71169
|
opts.callback = arg2;
|
|
68735
|
-
// identify legacy input format (used by some tests)
|
|
68736
|
-
opts.legacy = arg1 && guessInputContentType(arg1) != null;
|
|
68737
|
-
opts.input = arg1;
|
|
68738
71170
|
} else {
|
|
68739
71171
|
// if no callback, create a promise and a callback for resolving the promise
|
|
68740
71172
|
opts.promise = new Promise(function(resolve, reject) {
|
|
@@ -68744,7 +71176,7 @@ ${svg}
|
|
|
68744
71176
|
};
|
|
68745
71177
|
});
|
|
68746
71178
|
}
|
|
68747
|
-
if (
|
|
71179
|
+
if (utils.isObject(arg1)) {
|
|
68748
71180
|
if (arg1.xl) {
|
|
68749
71181
|
// options for runCommandsXL()
|
|
68750
71182
|
opts.options = arg1;
|
|
@@ -68777,12 +71209,6 @@ ${svg}
|
|
|
68777
71209
|
return callback(e);
|
|
68778
71210
|
}
|
|
68779
71211
|
|
|
68780
|
-
if (opts.legacy) {
|
|
68781
|
-
message("Warning: deprecated input format");
|
|
68782
|
-
commands = convertLegacyCommands(commands, inputObj);
|
|
68783
|
-
inputObj = null;
|
|
68784
|
-
}
|
|
68785
|
-
|
|
68786
71212
|
if (commands.length === 0) {
|
|
68787
71213
|
return callback(new UserError("No commands to run"));
|
|
68788
71214
|
}
|
|
@@ -68819,13 +71245,22 @@ ${svg}
|
|
|
68819
71245
|
// (e.g. `mapshaper *.shp batch-mode -o out.json` writing all inputs to the
|
|
68820
71246
|
// same target on disk).
|
|
68821
71247
|
var sharedOutputs = [];
|
|
68822
|
-
|
|
71248
|
+
runBatches().then(function(job) {
|
|
71249
|
+
done(null, job);
|
|
71250
|
+
}, done);
|
|
68823
71251
|
|
|
68824
|
-
function
|
|
68825
|
-
|
|
68826
|
-
|
|
68827
|
-
|
|
68828
|
-
|
|
71252
|
+
async function runBatches() {
|
|
71253
|
+
var job;
|
|
71254
|
+
for (var batch of batches) {
|
|
71255
|
+
try {
|
|
71256
|
+
job = await runParsedCommands(batch, new Job(null, {output_files: sharedOutputs}));
|
|
71257
|
+
} catch (e) {
|
|
71258
|
+
var fatalError = handleNonFatalError(e);
|
|
71259
|
+
if (fatalError) throw fatalError;
|
|
71260
|
+
job = undefined;
|
|
71261
|
+
}
|
|
71262
|
+
}
|
|
71263
|
+
return job;
|
|
68829
71264
|
}
|
|
68830
71265
|
|
|
68831
71266
|
function done(err, job) {
|
|
@@ -68839,33 +71274,6 @@ ${svg}
|
|
|
68839
71274
|
}
|
|
68840
71275
|
|
|
68841
71276
|
|
|
68842
|
-
function toLegacyOutputFormat(arr) {
|
|
68843
|
-
if (arr.length > 1) {
|
|
68844
|
-
// Return an array if multiple files are output
|
|
68845
|
-
return utils.pluck(arr, 'content');
|
|
68846
|
-
}
|
|
68847
|
-
if (arr.length == 1) {
|
|
68848
|
-
// Return content if a single file is output
|
|
68849
|
-
return arr[0].content;
|
|
68850
|
-
}
|
|
68851
|
-
return null;
|
|
68852
|
-
}
|
|
68853
|
-
|
|
68854
|
-
function convertLegacyCommands(arr, inputObj) {
|
|
68855
|
-
var i = utils.find(arr, function(cmd) {return cmd.name == 'i';});
|
|
68856
|
-
var o = utils.find(arr, function(cmd) {return cmd.name == 'o';});
|
|
68857
|
-
if (!i) {
|
|
68858
|
-
i = {name: 'i', options: {}};
|
|
68859
|
-
arr.unshift(i);
|
|
68860
|
-
}
|
|
68861
|
-
i.options.files = ['__input__'];
|
|
68862
|
-
i.options.input = {__input__: inputObj};
|
|
68863
|
-
if (!o) {
|
|
68864
|
-
arr.push({name: 'o', options: {}});
|
|
68865
|
-
}
|
|
68866
|
-
return arr;
|
|
68867
|
-
}
|
|
68868
|
-
|
|
68869
71277
|
// TODO: rewrite tests and remove this function
|
|
68870
71278
|
function testCommands(argv, done) {
|
|
68871
71279
|
_runCommands(argv, {}, function(err, job) {
|
|
@@ -68883,9 +71291,20 @@ ${svg}
|
|
|
68883
71291
|
// Execute a sequence of parsed commands
|
|
68884
71292
|
// @commands Array of parsed commands
|
|
68885
71293
|
// @job: Job object containing previously imported data
|
|
68886
|
-
// @done: function([error], [job])
|
|
71294
|
+
// @done: optional function([error], [job]); returns a Promise if omitted
|
|
68887
71295
|
//
|
|
68888
71296
|
function runParsedCommands(commands, job, done) {
|
|
71297
|
+
var promise = runParsedCommandsAsync(commands, job);
|
|
71298
|
+
if (done) {
|
|
71299
|
+
promise.then(function(job) {
|
|
71300
|
+
done(null, job);
|
|
71301
|
+
}, done);
|
|
71302
|
+
return;
|
|
71303
|
+
}
|
|
71304
|
+
return promise;
|
|
71305
|
+
}
|
|
71306
|
+
|
|
71307
|
+
async function runParsedCommandsAsync(commands, job) {
|
|
68889
71308
|
if (!job) job = new Job();
|
|
68890
71309
|
commands = readAndRemoveSettings(job, commands);
|
|
68891
71310
|
if (!runningInBrowser()) {
|
|
@@ -68893,26 +71312,16 @@ ${svg}
|
|
|
68893
71312
|
}
|
|
68894
71313
|
commands = runAndRemoveInfoCommands(commands);
|
|
68895
71314
|
if (commands.length === 0) {
|
|
68896
|
-
return
|
|
71315
|
+
return;
|
|
68897
71316
|
}
|
|
68898
71317
|
// we're no longer using the same Job for all batches -- no reset needed
|
|
68899
71318
|
// // resetting closes any unterminated -if blocks from a previous command sequence
|
|
68900
71319
|
// resetControlFlow(job);
|
|
68901
|
-
|
|
68902
|
-
|
|
68903
|
-
|
|
68904
|
-
var resolved;
|
|
68905
|
-
try {
|
|
68906
|
-
resolved = maybeInterpolateCommand(cmd, job);
|
|
68907
|
-
} catch(e) {
|
|
68908
|
-
return next(e);
|
|
68909
|
-
}
|
|
68910
|
-
runCommand(resolved, job).then(function(result) {
|
|
68911
|
-
next(null, result);
|
|
68912
|
-
}).catch(function(e) {
|
|
68913
|
-
next(e);
|
|
68914
|
-
});
|
|
71320
|
+
for (var command of commands) {
|
|
71321
|
+
var resolved = maybeInterpolateCommand(command, job);
|
|
71322
|
+
job = await runCommand(resolved, job);
|
|
68915
71323
|
}
|
|
71324
|
+
return job;
|
|
68916
71325
|
}
|
|
68917
71326
|
|
|
68918
71327
|
// Late-binding interpolation: just before each command runs, replace any
|
|
@@ -70524,6 +72933,9 @@ ${svg}
|
|
|
70524
72933
|
ClipPoints,
|
|
70525
72934
|
Colorizer,
|
|
70526
72935
|
CustomProjections,
|
|
72936
|
+
Dymaxion,
|
|
72937
|
+
ButterflyProjections,
|
|
72938
|
+
Narukawa2022,
|
|
70527
72939
|
DataAggregation,
|
|
70528
72940
|
DatasetUtils,
|
|
70529
72941
|
DataUtils,
|