mapshaper 0.6.110 → 0.6.112
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 +985 -1646
- package/package.json +1 -1
- package/www/mapshaper-gui.js +80 -27
- package/www/mapshaper.js +985 -1646
- package/www/page.css +1 -1
package/mapshaper.js
CHANGED
|
@@ -1211,8 +1211,13 @@
|
|
|
1211
1211
|
if (len >= 2) {
|
|
1212
1212
|
first = str.charAt(0);
|
|
1213
1213
|
last = str.charAt(len-1);
|
|
1214
|
-
if (first == '"' && last == '"' && !str.includes('","') ||
|
|
1215
|
-
|
|
1214
|
+
// if (first == '"' && last == '"' && !str.includes('","') ||
|
|
1215
|
+
// first == "'" && last == "'" && !str.includes("','")) {
|
|
1216
|
+
// don't strip if there are unescaped quotes
|
|
1217
|
+
// e.g. expressions that start and end with quotes
|
|
1218
|
+
// e.g. comma-separated list of quoted values
|
|
1219
|
+
if (first == '"' && last == '"' && !/[^\\]"./.test(str) ||
|
|
1220
|
+
first == "'" && last == "'" && !/[^\\]'./.test(str)) {
|
|
1216
1221
|
str = str.substr(1, len-2);
|
|
1217
1222
|
// remove string escapes
|
|
1218
1223
|
str = str.replace(first == '"' ? /\\(?=")/g : /\\(?=')/g, '');
|
|
@@ -1347,6 +1352,18 @@
|
|
|
1347
1352
|
}
|
|
1348
1353
|
}
|
|
1349
1354
|
|
|
1355
|
+
function time(slug) {
|
|
1356
|
+
if (useDebug()) {
|
|
1357
|
+
console.time(slug);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
function timeEnd(slug) {
|
|
1362
|
+
if (useDebug()) {
|
|
1363
|
+
console.timeEnd(slug);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1350
1367
|
function printError(err) {
|
|
1351
1368
|
var msg;
|
|
1352
1369
|
if (!LOGGING) return;
|
|
@@ -1465,6 +1482,8 @@
|
|
|
1465
1482
|
setLoggingForCLI: setLoggingForCLI,
|
|
1466
1483
|
setLoggingFunctions: setLoggingFunctions,
|
|
1467
1484
|
stop: stop,
|
|
1485
|
+
time: time,
|
|
1486
|
+
timeEnd: timeEnd,
|
|
1468
1487
|
truncateString: truncateString,
|
|
1469
1488
|
useDebug: useDebug,
|
|
1470
1489
|
useVerbose: useVerbose,
|
|
@@ -3113,1037 +3132,70 @@
|
|
|
3113
3132
|
snapEndpointsByInterval: snapEndpointsByInterval
|
|
3114
3133
|
});
|
|
3115
3134
|
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
INVALID_RM = INVALID + 'rounding mode',
|
|
3181
|
-
DIV_BY_ZERO = NAME + 'Division by zero',
|
|
3182
|
-
|
|
3183
|
-
// The shared prototype object.
|
|
3184
|
-
P = {},
|
|
3185
|
-
UNDEFINED = void 0,
|
|
3186
|
-
NUMERIC = /^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i;
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
/*
|
|
3190
|
-
* Create and return a Big constructor.
|
|
3191
|
-
*/
|
|
3192
|
-
function _Big_() {
|
|
3193
|
-
|
|
3194
|
-
/*
|
|
3195
|
-
* The Big constructor and exported function.
|
|
3196
|
-
* Create and return a new instance of a Big number object.
|
|
3197
|
-
*
|
|
3198
|
-
* n {number|string|Big} A numeric value.
|
|
3199
|
-
*/
|
|
3200
|
-
function Big(n) {
|
|
3201
|
-
var x = this;
|
|
3202
|
-
|
|
3203
|
-
// Enable constructor usage without new.
|
|
3204
|
-
if (!(x instanceof Big)) {
|
|
3205
|
-
return n === UNDEFINED && arguments.length === 0 ? _Big_() : new Big(n);
|
|
3206
|
-
}
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
// Duplicate.
|
|
3210
|
-
if (n instanceof Big) {
|
|
3211
|
-
x.s = n.s;
|
|
3212
|
-
x.e = n.e;
|
|
3213
|
-
x.c = n.c.slice();
|
|
3214
|
-
} else {
|
|
3215
|
-
if (typeof n !== 'string') {
|
|
3216
|
-
if (Big.strict === true && typeof n !== 'bigint') {
|
|
3217
|
-
throw TypeError(INVALID + 'value');
|
|
3218
|
-
}
|
|
3219
|
-
|
|
3220
|
-
// Minus zero?
|
|
3221
|
-
n = n === 0 && 1 / n < 0 ? '-0' : String(n);
|
|
3222
|
-
}
|
|
3223
|
-
|
|
3224
|
-
parse(x, n);
|
|
3225
|
-
}
|
|
3226
|
-
|
|
3227
|
-
// Retain a reference to this Big constructor.
|
|
3228
|
-
// Shadow Big.prototype.constructor which points to Object.
|
|
3229
|
-
x.constructor = Big;
|
|
3230
|
-
}
|
|
3231
|
-
|
|
3232
|
-
Big.prototype = P;
|
|
3233
|
-
Big.DP = DP;
|
|
3234
|
-
Big.RM = RM;
|
|
3235
|
-
Big.NE = NE;
|
|
3236
|
-
Big.PE = PE;
|
|
3237
|
-
Big.strict = STRICT;
|
|
3238
|
-
Big.roundDown = 0;
|
|
3239
|
-
Big.roundHalfUp = 1;
|
|
3240
|
-
Big.roundHalfEven = 2;
|
|
3241
|
-
Big.roundUp = 3;
|
|
3242
|
-
|
|
3243
|
-
return Big;
|
|
3244
|
-
}
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
/*
|
|
3248
|
-
* Parse the number or string value passed to a Big constructor.
|
|
3249
|
-
*
|
|
3250
|
-
* x {Big} A Big number instance.
|
|
3251
|
-
* n {number|string} A numeric value.
|
|
3252
|
-
*/
|
|
3253
|
-
function parse(x, n) {
|
|
3254
|
-
var e, i, nl;
|
|
3255
|
-
|
|
3256
|
-
if (!NUMERIC.test(n)) {
|
|
3257
|
-
throw Error(INVALID + 'number');
|
|
3258
|
-
}
|
|
3259
|
-
|
|
3260
|
-
// Determine sign.
|
|
3261
|
-
x.s = n.charAt(0) == '-' ? (n = n.slice(1), -1) : 1;
|
|
3262
|
-
|
|
3263
|
-
// Decimal point?
|
|
3264
|
-
if ((e = n.indexOf('.')) > -1) n = n.replace('.', '');
|
|
3265
|
-
|
|
3266
|
-
// Exponential form?
|
|
3267
|
-
if ((i = n.search(/e/i)) > 0) {
|
|
3268
|
-
|
|
3269
|
-
// Determine exponent.
|
|
3270
|
-
if (e < 0) e = i;
|
|
3271
|
-
e += +n.slice(i + 1);
|
|
3272
|
-
n = n.substring(0, i);
|
|
3273
|
-
} else if (e < 0) {
|
|
3274
|
-
|
|
3275
|
-
// Integer.
|
|
3276
|
-
e = n.length;
|
|
3277
|
-
}
|
|
3278
|
-
|
|
3279
|
-
nl = n.length;
|
|
3280
|
-
|
|
3281
|
-
// Determine leading zeros.
|
|
3282
|
-
for (i = 0; i < nl && n.charAt(i) == '0';) ++i;
|
|
3283
|
-
|
|
3284
|
-
if (i == nl) {
|
|
3285
|
-
|
|
3286
|
-
// Zero.
|
|
3287
|
-
x.c = [x.e = 0];
|
|
3288
|
-
} else {
|
|
3289
|
-
|
|
3290
|
-
// Determine trailing zeros.
|
|
3291
|
-
for (; nl > 0 && n.charAt(--nl) == '0';);
|
|
3292
|
-
x.e = e - i - 1;
|
|
3293
|
-
x.c = [];
|
|
3294
|
-
|
|
3295
|
-
// Convert string to array of digits without leading/trailing zeros.
|
|
3296
|
-
for (e = 0; i <= nl;) x.c[e++] = +n.charAt(i++);
|
|
3297
|
-
}
|
|
3298
|
-
|
|
3299
|
-
return x;
|
|
3300
|
-
}
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
/*
|
|
3304
|
-
* Round Big x to a maximum of sd significant digits using rounding mode rm.
|
|
3305
|
-
*
|
|
3306
|
-
* x {Big} The Big to round.
|
|
3307
|
-
* sd {number} Significant digits: integer, 0 to MAX_DP inclusive.
|
|
3308
|
-
* rm {number} Rounding mode: 0 (down), 1 (half-up), 2 (half-even) or 3 (up).
|
|
3309
|
-
* [more] {boolean} Whether the result of division was truncated.
|
|
3310
|
-
*/
|
|
3311
|
-
function round(x, sd, rm, more) {
|
|
3312
|
-
var xc = x.c;
|
|
3313
|
-
|
|
3314
|
-
if (rm === UNDEFINED) rm = x.constructor.RM;
|
|
3315
|
-
if (rm !== 0 && rm !== 1 && rm !== 2 && rm !== 3) {
|
|
3316
|
-
throw Error(INVALID_RM);
|
|
3317
|
-
}
|
|
3318
|
-
|
|
3319
|
-
if (sd < 1) {
|
|
3320
|
-
more =
|
|
3321
|
-
rm === 3 && (more || !!xc[0]) || sd === 0 && (
|
|
3322
|
-
rm === 1 && xc[0] >= 5 ||
|
|
3323
|
-
rm === 2 && (xc[0] > 5 || xc[0] === 5 && (more || xc[1] !== UNDEFINED))
|
|
3324
|
-
);
|
|
3325
|
-
|
|
3326
|
-
xc.length = 1;
|
|
3327
|
-
|
|
3328
|
-
if (more) {
|
|
3329
|
-
|
|
3330
|
-
// 1, 0.1, 0.01, 0.001, 0.0001 etc.
|
|
3331
|
-
x.e = x.e - sd + 1;
|
|
3332
|
-
xc[0] = 1;
|
|
3333
|
-
} else {
|
|
3334
|
-
|
|
3335
|
-
// Zero.
|
|
3336
|
-
xc[0] = x.e = 0;
|
|
3337
|
-
}
|
|
3338
|
-
} else if (sd < xc.length) {
|
|
3339
|
-
|
|
3340
|
-
// xc[sd] is the digit after the digit that may be rounded up.
|
|
3341
|
-
more =
|
|
3342
|
-
rm === 1 && xc[sd] >= 5 ||
|
|
3343
|
-
rm === 2 && (xc[sd] > 5 || xc[sd] === 5 &&
|
|
3344
|
-
(more || xc[sd + 1] !== UNDEFINED || xc[sd - 1] & 1)) ||
|
|
3345
|
-
rm === 3 && (more || !!xc[0]);
|
|
3346
|
-
|
|
3347
|
-
// Remove any digits after the required precision.
|
|
3348
|
-
xc.length = sd;
|
|
3349
|
-
|
|
3350
|
-
// Round up?
|
|
3351
|
-
if (more) {
|
|
3352
|
-
|
|
3353
|
-
// Rounding up may mean the previous digit has to be rounded up.
|
|
3354
|
-
for (; ++xc[--sd] > 9;) {
|
|
3355
|
-
xc[sd] = 0;
|
|
3356
|
-
if (sd === 0) {
|
|
3357
|
-
++x.e;
|
|
3358
|
-
xc.unshift(1);
|
|
3359
|
-
break;
|
|
3360
|
-
}
|
|
3361
|
-
}
|
|
3362
|
-
}
|
|
3363
|
-
|
|
3364
|
-
// Remove trailing zeros.
|
|
3365
|
-
for (sd = xc.length; !xc[--sd];) xc.pop();
|
|
3366
|
-
}
|
|
3367
|
-
|
|
3368
|
-
return x;
|
|
3369
|
-
}
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
/*
|
|
3373
|
-
* Return a string representing the value of Big x in normal or exponential notation.
|
|
3374
|
-
* Handles P.toExponential, P.toFixed, P.toJSON, P.toPrecision, P.toString and P.valueOf.
|
|
3375
|
-
*/
|
|
3376
|
-
function stringify$1(x, doExponential, isNonzero) {
|
|
3377
|
-
var e = x.e,
|
|
3378
|
-
s = x.c.join(''),
|
|
3379
|
-
n = s.length;
|
|
3380
|
-
|
|
3381
|
-
// Exponential notation?
|
|
3382
|
-
if (doExponential) {
|
|
3383
|
-
s = s.charAt(0) + (n > 1 ? '.' + s.slice(1) : '') + (e < 0 ? 'e' : 'e+') + e;
|
|
3384
|
-
|
|
3385
|
-
// Normal notation.
|
|
3386
|
-
} else if (e < 0) {
|
|
3387
|
-
for (; ++e;) s = '0' + s;
|
|
3388
|
-
s = '0.' + s;
|
|
3389
|
-
} else if (e > 0) {
|
|
3390
|
-
if (++e > n) {
|
|
3391
|
-
for (e -= n; e--;) s += '0';
|
|
3392
|
-
} else if (e < n) {
|
|
3393
|
-
s = s.slice(0, e) + '.' + s.slice(e);
|
|
3394
|
-
}
|
|
3395
|
-
} else if (n > 1) {
|
|
3396
|
-
s = s.charAt(0) + '.' + s.slice(1);
|
|
3397
|
-
}
|
|
3398
|
-
|
|
3399
|
-
return x.s < 0 && isNonzero ? '-' + s : s;
|
|
3400
|
-
}
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
// Prototype/instance methods
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
/*
|
|
3407
|
-
* Return a new Big whose value is the absolute value of this Big.
|
|
3408
|
-
*/
|
|
3409
|
-
P.abs = function () {
|
|
3410
|
-
var x = new this.constructor(this);
|
|
3411
|
-
x.s = 1;
|
|
3412
|
-
return x;
|
|
3413
|
-
};
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
/*
|
|
3417
|
-
* Return 1 if the value of this Big is greater than the value of Big y,
|
|
3418
|
-
* -1 if the value of this Big is less than the value of Big y, or
|
|
3419
|
-
* 0 if they have the same value.
|
|
3420
|
-
*/
|
|
3421
|
-
P.cmp = function (y) {
|
|
3422
|
-
var isneg,
|
|
3423
|
-
x = this,
|
|
3424
|
-
xc = x.c,
|
|
3425
|
-
yc = (y = new x.constructor(y)).c,
|
|
3426
|
-
i = x.s,
|
|
3427
|
-
j = y.s,
|
|
3428
|
-
k = x.e,
|
|
3429
|
-
l = y.e;
|
|
3430
|
-
|
|
3431
|
-
// Either zero?
|
|
3432
|
-
if (!xc[0] || !yc[0]) return !xc[0] ? !yc[0] ? 0 : -j : i;
|
|
3433
|
-
|
|
3434
|
-
// Signs differ?
|
|
3435
|
-
if (i != j) return i;
|
|
3436
|
-
|
|
3437
|
-
isneg = i < 0;
|
|
3438
|
-
|
|
3439
|
-
// Compare exponents.
|
|
3440
|
-
if (k != l) return k > l ^ isneg ? 1 : -1;
|
|
3441
|
-
|
|
3442
|
-
j = (k = xc.length) < (l = yc.length) ? k : l;
|
|
3443
|
-
|
|
3444
|
-
// Compare digit by digit.
|
|
3445
|
-
for (i = -1; ++i < j;) {
|
|
3446
|
-
if (xc[i] != yc[i]) return xc[i] > yc[i] ^ isneg ? 1 : -1;
|
|
3447
|
-
}
|
|
3448
|
-
|
|
3449
|
-
// Compare lengths.
|
|
3450
|
-
return k == l ? 0 : k > l ^ isneg ? 1 : -1;
|
|
3451
|
-
};
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
/*
|
|
3455
|
-
* Return a new Big whose value is the value of this Big divided by the value of Big y, rounded,
|
|
3456
|
-
* if necessary, to a maximum of Big.DP decimal places using rounding mode Big.RM.
|
|
3457
|
-
*/
|
|
3458
|
-
P.div = function (y) {
|
|
3459
|
-
var x = this,
|
|
3460
|
-
Big = x.constructor,
|
|
3461
|
-
a = x.c, // dividend
|
|
3462
|
-
b = (y = new Big(y)).c, // divisor
|
|
3463
|
-
k = x.s == y.s ? 1 : -1,
|
|
3464
|
-
dp = Big.DP;
|
|
3465
|
-
|
|
3466
|
-
if (dp !== ~~dp || dp < 0 || dp > MAX_DP) {
|
|
3467
|
-
throw Error(INVALID_DP);
|
|
3468
|
-
}
|
|
3469
|
-
|
|
3470
|
-
// Divisor is zero?
|
|
3471
|
-
if (!b[0]) {
|
|
3472
|
-
throw Error(DIV_BY_ZERO);
|
|
3473
|
-
}
|
|
3474
|
-
|
|
3475
|
-
// Dividend is 0? Return +-0.
|
|
3476
|
-
if (!a[0]) {
|
|
3477
|
-
y.s = k;
|
|
3478
|
-
y.c = [y.e = 0];
|
|
3479
|
-
return y;
|
|
3480
|
-
}
|
|
3481
|
-
|
|
3482
|
-
var bl, bt, n, cmp, ri,
|
|
3483
|
-
bz = b.slice(),
|
|
3484
|
-
ai = bl = b.length,
|
|
3485
|
-
al = a.length,
|
|
3486
|
-
r = a.slice(0, bl), // remainder
|
|
3487
|
-
rl = r.length,
|
|
3488
|
-
q = y, // quotient
|
|
3489
|
-
qc = q.c = [],
|
|
3490
|
-
qi = 0,
|
|
3491
|
-
p = dp + (q.e = x.e - y.e) + 1; // precision of the result
|
|
3492
|
-
|
|
3493
|
-
q.s = k;
|
|
3494
|
-
k = p < 0 ? 0 : p;
|
|
3495
|
-
|
|
3496
|
-
// Create version of divisor with leading zero.
|
|
3497
|
-
bz.unshift(0);
|
|
3498
|
-
|
|
3499
|
-
// Add zeros to make remainder as long as divisor.
|
|
3500
|
-
for (; rl++ < bl;) r.push(0);
|
|
3501
|
-
|
|
3502
|
-
do {
|
|
3503
|
-
|
|
3504
|
-
// n is how many times the divisor goes into current remainder.
|
|
3505
|
-
for (n = 0; n < 10; n++) {
|
|
3506
|
-
|
|
3507
|
-
// Compare divisor and remainder.
|
|
3508
|
-
if (bl != (rl = r.length)) {
|
|
3509
|
-
cmp = bl > rl ? 1 : -1;
|
|
3510
|
-
} else {
|
|
3511
|
-
for (ri = -1, cmp = 0; ++ri < bl;) {
|
|
3512
|
-
if (b[ri] != r[ri]) {
|
|
3513
|
-
cmp = b[ri] > r[ri] ? 1 : -1;
|
|
3514
|
-
break;
|
|
3515
|
-
}
|
|
3516
|
-
}
|
|
3517
|
-
}
|
|
3518
|
-
|
|
3519
|
-
// If divisor < remainder, subtract divisor from remainder.
|
|
3520
|
-
if (cmp < 0) {
|
|
3521
|
-
|
|
3522
|
-
// Remainder can't be more than 1 digit longer than divisor.
|
|
3523
|
-
// Equalise lengths using divisor with extra leading zero?
|
|
3524
|
-
for (bt = rl == bl ? b : bz; rl;) {
|
|
3525
|
-
if (r[--rl] < bt[rl]) {
|
|
3526
|
-
ri = rl;
|
|
3527
|
-
for (; ri && !r[--ri];) r[ri] = 9;
|
|
3528
|
-
--r[ri];
|
|
3529
|
-
r[rl] += 10;
|
|
3530
|
-
}
|
|
3531
|
-
r[rl] -= bt[rl];
|
|
3532
|
-
}
|
|
3533
|
-
|
|
3534
|
-
for (; !r[0];) r.shift();
|
|
3535
|
-
} else {
|
|
3536
|
-
break;
|
|
3537
|
-
}
|
|
3538
|
-
}
|
|
3539
|
-
|
|
3540
|
-
// Add the digit n to the result array.
|
|
3541
|
-
qc[qi++] = cmp ? n : ++n;
|
|
3542
|
-
|
|
3543
|
-
// Update the remainder.
|
|
3544
|
-
if (r[0] && cmp) r[rl] = a[ai] || 0;
|
|
3545
|
-
else r = [a[ai]];
|
|
3546
|
-
|
|
3547
|
-
} while ((ai++ < al || r[0] !== UNDEFINED) && k--);
|
|
3548
|
-
|
|
3549
|
-
// Leading zero? Do not remove if result is simply zero (qi == 1).
|
|
3550
|
-
if (!qc[0] && qi != 1) {
|
|
3551
|
-
|
|
3552
|
-
// There can't be more than one zero.
|
|
3553
|
-
qc.shift();
|
|
3554
|
-
q.e--;
|
|
3555
|
-
p--;
|
|
3556
|
-
}
|
|
3557
|
-
|
|
3558
|
-
// Round?
|
|
3559
|
-
if (qi > p) round(q, p, Big.RM, r[0] !== UNDEFINED);
|
|
3560
|
-
|
|
3561
|
-
return q;
|
|
3562
|
-
};
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
/*
|
|
3566
|
-
* Return true if the value of this Big is equal to the value of Big y, otherwise return false.
|
|
3567
|
-
*/
|
|
3568
|
-
P.eq = function (y) {
|
|
3569
|
-
return this.cmp(y) === 0;
|
|
3570
|
-
};
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
/*
|
|
3574
|
-
* Return true if the value of this Big is greater than the value of Big y, otherwise return
|
|
3575
|
-
* false.
|
|
3576
|
-
*/
|
|
3577
|
-
P.gt = function (y) {
|
|
3578
|
-
return this.cmp(y) > 0;
|
|
3579
|
-
};
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
/*
|
|
3583
|
-
* Return true if the value of this Big is greater than or equal to the value of Big y, otherwise
|
|
3584
|
-
* return false.
|
|
3585
|
-
*/
|
|
3586
|
-
P.gte = function (y) {
|
|
3587
|
-
return this.cmp(y) > -1;
|
|
3588
|
-
};
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
/*
|
|
3592
|
-
* Return true if the value of this Big is less than the value of Big y, otherwise return false.
|
|
3593
|
-
*/
|
|
3594
|
-
P.lt = function (y) {
|
|
3595
|
-
return this.cmp(y) < 0;
|
|
3596
|
-
};
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
/*
|
|
3600
|
-
* Return true if the value of this Big is less than or equal to the value of Big y, otherwise
|
|
3601
|
-
* return false.
|
|
3602
|
-
*/
|
|
3603
|
-
P.lte = function (y) {
|
|
3604
|
-
return this.cmp(y) < 1;
|
|
3605
|
-
};
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
/*
|
|
3609
|
-
* Return a new Big whose value is the value of this Big minus the value of Big y.
|
|
3610
|
-
*/
|
|
3611
|
-
P.minus = P.sub = function (y) {
|
|
3612
|
-
var i, j, t, xlty,
|
|
3613
|
-
x = this,
|
|
3614
|
-
Big = x.constructor,
|
|
3615
|
-
a = x.s,
|
|
3616
|
-
b = (y = new Big(y)).s;
|
|
3617
|
-
|
|
3618
|
-
// Signs differ?
|
|
3619
|
-
if (a != b) {
|
|
3620
|
-
y.s = -b;
|
|
3621
|
-
return x.plus(y);
|
|
3622
|
-
}
|
|
3623
|
-
|
|
3624
|
-
var xc = x.c.slice(),
|
|
3625
|
-
xe = x.e,
|
|
3626
|
-
yc = y.c,
|
|
3627
|
-
ye = y.e;
|
|
3628
|
-
|
|
3629
|
-
// Either zero?
|
|
3630
|
-
if (!xc[0] || !yc[0]) {
|
|
3631
|
-
if (yc[0]) {
|
|
3632
|
-
y.s = -b;
|
|
3633
|
-
} else if (xc[0]) {
|
|
3634
|
-
y = new Big(x);
|
|
3635
|
-
} else {
|
|
3636
|
-
y.s = 1;
|
|
3637
|
-
}
|
|
3638
|
-
return y;
|
|
3639
|
-
}
|
|
3640
|
-
|
|
3641
|
-
// Determine which is the bigger number. Prepend zeros to equalise exponents.
|
|
3642
|
-
if (a = xe - ye) {
|
|
3643
|
-
|
|
3644
|
-
if (xlty = a < 0) {
|
|
3645
|
-
a = -a;
|
|
3646
|
-
t = xc;
|
|
3647
|
-
} else {
|
|
3648
|
-
ye = xe;
|
|
3649
|
-
t = yc;
|
|
3650
|
-
}
|
|
3651
|
-
|
|
3652
|
-
t.reverse();
|
|
3653
|
-
for (b = a; b--;) t.push(0);
|
|
3654
|
-
t.reverse();
|
|
3655
|
-
} else {
|
|
3656
|
-
|
|
3657
|
-
// Exponents equal. Check digit by digit.
|
|
3658
|
-
j = ((xlty = xc.length < yc.length) ? xc : yc).length;
|
|
3659
|
-
|
|
3660
|
-
for (a = b = 0; b < j; b++) {
|
|
3661
|
-
if (xc[b] != yc[b]) {
|
|
3662
|
-
xlty = xc[b] < yc[b];
|
|
3663
|
-
break;
|
|
3664
|
-
}
|
|
3665
|
-
}
|
|
3666
|
-
}
|
|
3667
|
-
|
|
3668
|
-
// x < y? Point xc to the array of the bigger number.
|
|
3669
|
-
if (xlty) {
|
|
3670
|
-
t = xc;
|
|
3671
|
-
xc = yc;
|
|
3672
|
-
yc = t;
|
|
3673
|
-
y.s = -y.s;
|
|
3674
|
-
}
|
|
3675
|
-
|
|
3676
|
-
/*
|
|
3677
|
-
* Append zeros to xc if shorter. No need to add zeros to yc if shorter as subtraction only
|
|
3678
|
-
* needs to start at yc.length.
|
|
3679
|
-
*/
|
|
3680
|
-
if ((b = (j = yc.length) - (i = xc.length)) > 0) for (; b--;) xc[i++] = 0;
|
|
3681
|
-
|
|
3682
|
-
// Subtract yc from xc.
|
|
3683
|
-
for (b = i; j > a;) {
|
|
3684
|
-
if (xc[--j] < yc[j]) {
|
|
3685
|
-
for (i = j; i && !xc[--i];) xc[i] = 9;
|
|
3686
|
-
--xc[i];
|
|
3687
|
-
xc[j] += 10;
|
|
3688
|
-
}
|
|
3689
|
-
|
|
3690
|
-
xc[j] -= yc[j];
|
|
3691
|
-
}
|
|
3692
|
-
|
|
3693
|
-
// Remove trailing zeros.
|
|
3694
|
-
for (; xc[--b] === 0;) xc.pop();
|
|
3695
|
-
|
|
3696
|
-
// Remove leading zeros and adjust exponent accordingly.
|
|
3697
|
-
for (; xc[0] === 0;) {
|
|
3698
|
-
xc.shift();
|
|
3699
|
-
--ye;
|
|
3700
|
-
}
|
|
3701
|
-
|
|
3702
|
-
if (!xc[0]) {
|
|
3703
|
-
|
|
3704
|
-
// n - n = +0
|
|
3705
|
-
y.s = 1;
|
|
3706
|
-
|
|
3707
|
-
// Result must be zero.
|
|
3708
|
-
xc = [ye = 0];
|
|
3709
|
-
}
|
|
3710
|
-
|
|
3711
|
-
y.c = xc;
|
|
3712
|
-
y.e = ye;
|
|
3713
|
-
|
|
3714
|
-
return y;
|
|
3715
|
-
};
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
/*
|
|
3719
|
-
* Return a new Big whose value is the value of this Big modulo the value of Big y.
|
|
3720
|
-
*/
|
|
3721
|
-
P.mod = function (y) {
|
|
3722
|
-
var ygtx,
|
|
3723
|
-
x = this,
|
|
3724
|
-
Big = x.constructor,
|
|
3725
|
-
a = x.s,
|
|
3726
|
-
b = (y = new Big(y)).s;
|
|
3727
|
-
|
|
3728
|
-
if (!y.c[0]) {
|
|
3729
|
-
throw Error(DIV_BY_ZERO);
|
|
3730
|
-
}
|
|
3731
|
-
|
|
3732
|
-
x.s = y.s = 1;
|
|
3733
|
-
ygtx = y.cmp(x) == 1;
|
|
3734
|
-
x.s = a;
|
|
3735
|
-
y.s = b;
|
|
3736
|
-
|
|
3737
|
-
if (ygtx) return new Big(x);
|
|
3738
|
-
|
|
3739
|
-
a = Big.DP;
|
|
3740
|
-
b = Big.RM;
|
|
3741
|
-
Big.DP = Big.RM = 0;
|
|
3742
|
-
x = x.div(y);
|
|
3743
|
-
Big.DP = a;
|
|
3744
|
-
Big.RM = b;
|
|
3745
|
-
|
|
3746
|
-
return this.minus(x.times(y));
|
|
3747
|
-
};
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
/*
|
|
3751
|
-
* Return a new Big whose value is the value of this Big negated.
|
|
3752
|
-
*/
|
|
3753
|
-
P.neg = function () {
|
|
3754
|
-
var x = new this.constructor(this);
|
|
3755
|
-
x.s = -x.s;
|
|
3756
|
-
return x;
|
|
3757
|
-
};
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
/*
|
|
3761
|
-
* Return a new Big whose value is the value of this Big plus the value of Big y.
|
|
3762
|
-
*/
|
|
3763
|
-
P.plus = P.add = function (y) {
|
|
3764
|
-
var e, k, t,
|
|
3765
|
-
x = this,
|
|
3766
|
-
Big = x.constructor;
|
|
3767
|
-
|
|
3768
|
-
y = new Big(y);
|
|
3769
|
-
|
|
3770
|
-
// Signs differ?
|
|
3771
|
-
if (x.s != y.s) {
|
|
3772
|
-
y.s = -y.s;
|
|
3773
|
-
return x.minus(y);
|
|
3774
|
-
}
|
|
3775
|
-
|
|
3776
|
-
var xe = x.e,
|
|
3777
|
-
xc = x.c,
|
|
3778
|
-
ye = y.e,
|
|
3779
|
-
yc = y.c;
|
|
3780
|
-
|
|
3781
|
-
// Either zero?
|
|
3782
|
-
if (!xc[0] || !yc[0]) {
|
|
3783
|
-
if (!yc[0]) {
|
|
3784
|
-
if (xc[0]) {
|
|
3785
|
-
y = new Big(x);
|
|
3786
|
-
} else {
|
|
3787
|
-
y.s = x.s;
|
|
3788
|
-
}
|
|
3789
|
-
}
|
|
3790
|
-
return y;
|
|
3791
|
-
}
|
|
3792
|
-
|
|
3793
|
-
xc = xc.slice();
|
|
3794
|
-
|
|
3795
|
-
// Prepend zeros to equalise exponents.
|
|
3796
|
-
// Note: reverse faster than unshifts.
|
|
3797
|
-
if (e = xe - ye) {
|
|
3798
|
-
if (e > 0) {
|
|
3799
|
-
ye = xe;
|
|
3800
|
-
t = yc;
|
|
3801
|
-
} else {
|
|
3802
|
-
e = -e;
|
|
3803
|
-
t = xc;
|
|
3804
|
-
}
|
|
3805
|
-
|
|
3806
|
-
t.reverse();
|
|
3807
|
-
for (; e--;) t.push(0);
|
|
3808
|
-
t.reverse();
|
|
3809
|
-
}
|
|
3810
|
-
|
|
3811
|
-
// Point xc to the longer array.
|
|
3812
|
-
if (xc.length - yc.length < 0) {
|
|
3813
|
-
t = yc;
|
|
3814
|
-
yc = xc;
|
|
3815
|
-
xc = t;
|
|
3816
|
-
}
|
|
3817
|
-
|
|
3818
|
-
e = yc.length;
|
|
3819
|
-
|
|
3820
|
-
// Only start adding at yc.length - 1 as the further digits of xc can be left as they are.
|
|
3821
|
-
for (k = 0; e; xc[e] %= 10) k = (xc[--e] = xc[e] + yc[e] + k) / 10 | 0;
|
|
3822
|
-
|
|
3823
|
-
// No need to check for zero, as +x + +y != 0 && -x + -y != 0
|
|
3824
|
-
|
|
3825
|
-
if (k) {
|
|
3826
|
-
xc.unshift(k);
|
|
3827
|
-
++ye;
|
|
3828
|
-
}
|
|
3829
|
-
|
|
3830
|
-
// Remove trailing zeros.
|
|
3831
|
-
for (e = xc.length; xc[--e] === 0;) xc.pop();
|
|
3832
|
-
|
|
3833
|
-
y.c = xc;
|
|
3834
|
-
y.e = ye;
|
|
3835
|
-
|
|
3836
|
-
return y;
|
|
3837
|
-
};
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
/*
|
|
3841
|
-
* Return a Big whose value is the value of this Big raised to the power n.
|
|
3842
|
-
* If n is negative, round to a maximum of Big.DP decimal places using rounding
|
|
3843
|
-
* mode Big.RM.
|
|
3844
|
-
*
|
|
3845
|
-
* n {number} Integer, -MAX_POWER to MAX_POWER inclusive.
|
|
3846
|
-
*/
|
|
3847
|
-
P.pow = function (n) {
|
|
3848
|
-
var x = this,
|
|
3849
|
-
one = new x.constructor('1'),
|
|
3850
|
-
y = one,
|
|
3851
|
-
isneg = n < 0;
|
|
3852
|
-
|
|
3853
|
-
if (n !== ~~n || n < -MAX_POWER || n > MAX_POWER) {
|
|
3854
|
-
throw Error(INVALID + 'exponent');
|
|
3855
|
-
}
|
|
3856
|
-
|
|
3857
|
-
if (isneg) n = -n;
|
|
3858
|
-
|
|
3859
|
-
for (;;) {
|
|
3860
|
-
if (n & 1) y = y.times(x);
|
|
3861
|
-
n >>= 1;
|
|
3862
|
-
if (!n) break;
|
|
3863
|
-
x = x.times(x);
|
|
3864
|
-
}
|
|
3865
|
-
|
|
3866
|
-
return isneg ? one.div(y) : y;
|
|
3867
|
-
};
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
/*
|
|
3871
|
-
* Return a new Big whose value is the value of this Big rounded to a maximum precision of sd
|
|
3872
|
-
* significant digits using rounding mode rm, or Big.RM if rm is not specified.
|
|
3873
|
-
*
|
|
3874
|
-
* sd {number} Significant digits: integer, 1 to MAX_DP inclusive.
|
|
3875
|
-
* rm? {number} Rounding mode: 0 (down), 1 (half-up), 2 (half-even) or 3 (up).
|
|
3876
|
-
*/
|
|
3877
|
-
P.prec = function (sd, rm) {
|
|
3878
|
-
if (sd !== ~~sd || sd < 1 || sd > MAX_DP) {
|
|
3879
|
-
throw Error(INVALID + 'precision');
|
|
3880
|
-
}
|
|
3881
|
-
return round(new this.constructor(this), sd, rm);
|
|
3882
|
-
};
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
/*
|
|
3886
|
-
* Return a new Big whose value is the value of this Big rounded to a maximum of dp decimal places
|
|
3887
|
-
* using rounding mode rm, or Big.RM if rm is not specified.
|
|
3888
|
-
* If dp is negative, round to an integer which is a multiple of 10**-dp.
|
|
3889
|
-
* If dp is not specified, round to 0 decimal places.
|
|
3890
|
-
*
|
|
3891
|
-
* dp? {number} Integer, -MAX_DP to MAX_DP inclusive.
|
|
3892
|
-
* rm? {number} Rounding mode: 0 (down), 1 (half-up), 2 (half-even) or 3 (up).
|
|
3893
|
-
*/
|
|
3894
|
-
P.round = function (dp, rm) {
|
|
3895
|
-
if (dp === UNDEFINED) dp = 0;
|
|
3896
|
-
else if (dp !== ~~dp || dp < -MAX_DP || dp > MAX_DP) {
|
|
3897
|
-
throw Error(INVALID_DP);
|
|
3898
|
-
}
|
|
3899
|
-
return round(new this.constructor(this), dp + this.e + 1, rm);
|
|
3900
|
-
};
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
/*
|
|
3904
|
-
* Return a new Big whose value is the square root of the value of this Big, rounded, if
|
|
3905
|
-
* necessary, to a maximum of Big.DP decimal places using rounding mode Big.RM.
|
|
3906
|
-
*/
|
|
3907
|
-
P.sqrt = function () {
|
|
3908
|
-
var r, c, t,
|
|
3909
|
-
x = this,
|
|
3910
|
-
Big = x.constructor,
|
|
3911
|
-
s = x.s,
|
|
3912
|
-
e = x.e,
|
|
3913
|
-
half = new Big('0.5');
|
|
3914
|
-
|
|
3915
|
-
// Zero?
|
|
3916
|
-
if (!x.c[0]) return new Big(x);
|
|
3917
|
-
|
|
3918
|
-
// Negative?
|
|
3919
|
-
if (s < 0) {
|
|
3920
|
-
throw Error(NAME + 'No square root');
|
|
3921
|
-
}
|
|
3922
|
-
|
|
3923
|
-
// Estimate.
|
|
3924
|
-
s = Math.sqrt(+stringify$1(x, true, true));
|
|
3925
|
-
|
|
3926
|
-
// Math.sqrt underflow/overflow?
|
|
3927
|
-
// Re-estimate: pass x coefficient to Math.sqrt as integer, then adjust the result exponent.
|
|
3928
|
-
if (s === 0 || s === 1 / 0) {
|
|
3929
|
-
c = x.c.join('');
|
|
3930
|
-
if (!(c.length + e & 1)) c += '0';
|
|
3931
|
-
s = Math.sqrt(c);
|
|
3932
|
-
e = ((e + 1) / 2 | 0) - (e < 0 || e & 1);
|
|
3933
|
-
r = new Big((s == 1 / 0 ? '5e' : (s = s.toExponential()).slice(0, s.indexOf('e') + 1)) + e);
|
|
3934
|
-
} else {
|
|
3935
|
-
r = new Big(s + '');
|
|
3936
|
-
}
|
|
3937
|
-
|
|
3938
|
-
e = r.e + (Big.DP += 4);
|
|
3939
|
-
|
|
3940
|
-
// Newton-Raphson iteration.
|
|
3941
|
-
do {
|
|
3942
|
-
t = r;
|
|
3943
|
-
r = half.times(t.plus(x.div(t)));
|
|
3944
|
-
} while (t.c.slice(0, e).join('') !== r.c.slice(0, e).join(''));
|
|
3945
|
-
|
|
3946
|
-
return round(r, (Big.DP -= 4) + r.e + 1, Big.RM);
|
|
3947
|
-
};
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
/*
|
|
3951
|
-
* Return a new Big whose value is the value of this Big times the value of Big y.
|
|
3952
|
-
*/
|
|
3953
|
-
P.times = P.mul = function (y) {
|
|
3954
|
-
var c,
|
|
3955
|
-
x = this,
|
|
3956
|
-
Big = x.constructor,
|
|
3957
|
-
xc = x.c,
|
|
3958
|
-
yc = (y = new Big(y)).c,
|
|
3959
|
-
a = xc.length,
|
|
3960
|
-
b = yc.length,
|
|
3961
|
-
i = x.e,
|
|
3962
|
-
j = y.e;
|
|
3963
|
-
|
|
3964
|
-
// Determine sign of result.
|
|
3965
|
-
y.s = x.s == y.s ? 1 : -1;
|
|
3966
|
-
|
|
3967
|
-
// Return signed 0 if either 0.
|
|
3968
|
-
if (!xc[0] || !yc[0]) {
|
|
3969
|
-
y.c = [y.e = 0];
|
|
3970
|
-
return y;
|
|
3971
|
-
}
|
|
3972
|
-
|
|
3973
|
-
// Initialise exponent of result as x.e + y.e.
|
|
3974
|
-
y.e = i + j;
|
|
3975
|
-
|
|
3976
|
-
// If array xc has fewer digits than yc, swap xc and yc, and lengths.
|
|
3977
|
-
if (a < b) {
|
|
3978
|
-
c = xc;
|
|
3979
|
-
xc = yc;
|
|
3980
|
-
yc = c;
|
|
3981
|
-
j = a;
|
|
3982
|
-
a = b;
|
|
3983
|
-
b = j;
|
|
3984
|
-
}
|
|
3985
|
-
|
|
3986
|
-
// Initialise coefficient array of result with zeros.
|
|
3987
|
-
for (c = new Array(j = a + b); j--;) c[j] = 0;
|
|
3988
|
-
|
|
3989
|
-
// Multiply.
|
|
3990
|
-
|
|
3991
|
-
// i is initially xc.length.
|
|
3992
|
-
for (i = b; i--;) {
|
|
3993
|
-
b = 0;
|
|
3994
|
-
|
|
3995
|
-
// a is yc.length.
|
|
3996
|
-
for (j = a + i; j > i;) {
|
|
3997
|
-
|
|
3998
|
-
// Current sum of products at this digit position, plus carry.
|
|
3999
|
-
b = c[j] + yc[i] * xc[j - i - 1] + b;
|
|
4000
|
-
c[j--] = b % 10;
|
|
4001
|
-
|
|
4002
|
-
// carry
|
|
4003
|
-
b = b / 10 | 0;
|
|
4004
|
-
}
|
|
4005
|
-
|
|
4006
|
-
c[j] = b;
|
|
4007
|
-
}
|
|
4008
|
-
|
|
4009
|
-
// Increment result exponent if there is a final carry, otherwise remove leading zero.
|
|
4010
|
-
if (b) ++y.e;
|
|
4011
|
-
else c.shift();
|
|
4012
|
-
|
|
4013
|
-
// Remove trailing zeros.
|
|
4014
|
-
for (i = c.length; !c[--i];) c.pop();
|
|
4015
|
-
y.c = c;
|
|
4016
|
-
|
|
4017
|
-
return y;
|
|
4018
|
-
};
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
/*
|
|
4022
|
-
* Return a string representing the value of this Big in exponential notation rounded to dp fixed
|
|
4023
|
-
* decimal places using rounding mode rm, or Big.RM if rm is not specified.
|
|
4024
|
-
*
|
|
4025
|
-
* dp? {number} Decimal places: integer, 0 to MAX_DP inclusive.
|
|
4026
|
-
* rm? {number} Rounding mode: 0 (down), 1 (half-up), 2 (half-even) or 3 (up).
|
|
4027
|
-
*/
|
|
4028
|
-
P.toExponential = function (dp, rm) {
|
|
4029
|
-
var x = this,
|
|
4030
|
-
n = x.c[0];
|
|
4031
|
-
|
|
4032
|
-
if (dp !== UNDEFINED) {
|
|
4033
|
-
if (dp !== ~~dp || dp < 0 || dp > MAX_DP) {
|
|
4034
|
-
throw Error(INVALID_DP);
|
|
4035
|
-
}
|
|
4036
|
-
x = round(new x.constructor(x), ++dp, rm);
|
|
4037
|
-
for (; x.c.length < dp;) x.c.push(0);
|
|
4038
|
-
}
|
|
4039
|
-
|
|
4040
|
-
return stringify$1(x, true, !!n);
|
|
4041
|
-
};
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
/*
|
|
4045
|
-
* Return a string representing the value of this Big in normal notation rounded to dp fixed
|
|
4046
|
-
* decimal places using rounding mode rm, or Big.RM if rm is not specified.
|
|
4047
|
-
*
|
|
4048
|
-
* dp? {number} Decimal places: integer, 0 to MAX_DP inclusive.
|
|
4049
|
-
* rm? {number} Rounding mode: 0 (down), 1 (half-up), 2 (half-even) or 3 (up).
|
|
4050
|
-
*
|
|
4051
|
-
* (-0).toFixed(0) is '0', but (-0.1).toFixed(0) is '-0'.
|
|
4052
|
-
* (-0).toFixed(1) is '0.0', but (-0.01).toFixed(1) is '-0.0'.
|
|
4053
|
-
*/
|
|
4054
|
-
P.toFixed = function (dp, rm) {
|
|
4055
|
-
var x = this,
|
|
4056
|
-
n = x.c[0];
|
|
4057
|
-
|
|
4058
|
-
if (dp !== UNDEFINED) {
|
|
4059
|
-
if (dp !== ~~dp || dp < 0 || dp > MAX_DP) {
|
|
4060
|
-
throw Error(INVALID_DP);
|
|
4061
|
-
}
|
|
4062
|
-
x = round(new x.constructor(x), dp + x.e + 1, rm);
|
|
4063
|
-
|
|
4064
|
-
// x.e may have changed if the value is rounded up.
|
|
4065
|
-
for (dp = dp + x.e + 1; x.c.length < dp;) x.c.push(0);
|
|
4066
|
-
}
|
|
4067
|
-
|
|
4068
|
-
return stringify$1(x, false, !!n);
|
|
4069
|
-
};
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
/*
|
|
4073
|
-
* Return a string representing the value of this Big.
|
|
4074
|
-
* Return exponential notation if this Big has a positive exponent equal to or greater than
|
|
4075
|
-
* Big.PE, or a negative exponent equal to or less than Big.NE.
|
|
4076
|
-
* Omit the sign for negative zero.
|
|
4077
|
-
*/
|
|
4078
|
-
P.toJSON = P.toString = function () {
|
|
4079
|
-
var x = this,
|
|
4080
|
-
Big = x.constructor;
|
|
4081
|
-
return stringify$1(x, x.e <= Big.NE || x.e >= Big.PE, !!x.c[0]);
|
|
4082
|
-
};
|
|
4083
|
-
|
|
4084
|
-
if (typeof Symbol !== "undefined") {
|
|
4085
|
-
P[Symbol.for('nodejs.util.inspect.custom')] = P.toJSON;
|
|
4086
|
-
}
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
/*
|
|
4090
|
-
* Return the value of this Big as a primitive number.
|
|
4091
|
-
*/
|
|
4092
|
-
P.toNumber = function () {
|
|
4093
|
-
var n = +stringify$1(this, true, true);
|
|
4094
|
-
if (this.constructor.strict === true && !this.eq(n.toString())) {
|
|
4095
|
-
throw Error(NAME + 'Imprecise conversion');
|
|
4096
|
-
}
|
|
4097
|
-
return n;
|
|
4098
|
-
};
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
/*
|
|
4102
|
-
* Return a string representing the value of this Big rounded to sd significant digits using
|
|
4103
|
-
* rounding mode rm, or Big.RM if rm is not specified.
|
|
4104
|
-
* Use exponential notation if sd is less than the number of digits necessary to represent
|
|
4105
|
-
* the integer part of the value in normal notation.
|
|
4106
|
-
*
|
|
4107
|
-
* sd {number} Significant digits: integer, 1 to MAX_DP inclusive.
|
|
4108
|
-
* rm? {number} Rounding mode: 0 (down), 1 (half-up), 2 (half-even) or 3 (up).
|
|
4109
|
-
*/
|
|
4110
|
-
P.toPrecision = function (sd, rm) {
|
|
4111
|
-
var x = this,
|
|
4112
|
-
Big = x.constructor,
|
|
4113
|
-
n = x.c[0];
|
|
4114
|
-
|
|
4115
|
-
if (sd !== UNDEFINED) {
|
|
4116
|
-
if (sd !== ~~sd || sd < 1 || sd > MAX_DP) {
|
|
4117
|
-
throw Error(INVALID + 'precision');
|
|
4118
|
-
}
|
|
4119
|
-
x = round(new Big(x), sd, rm);
|
|
4120
|
-
for (; x.c.length < sd;) x.c.push(0);
|
|
4121
|
-
}
|
|
4122
|
-
|
|
4123
|
-
return stringify$1(x, sd <= x.e || x.e <= Big.NE || x.e >= Big.PE, !!n);
|
|
4124
|
-
};
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
/*
|
|
4128
|
-
* Return a string representing the value of this Big.
|
|
4129
|
-
* Return exponential notation if this Big has a positive exponent equal to or greater than
|
|
4130
|
-
* Big.PE, or a negative exponent equal to or less than Big.NE.
|
|
4131
|
-
* Include the sign for negative zero.
|
|
4132
|
-
*/
|
|
4133
|
-
P.valueOf = function () {
|
|
4134
|
-
var x = this,
|
|
4135
|
-
Big = x.constructor;
|
|
4136
|
-
if (Big.strict === true) {
|
|
4137
|
-
throw Error(NAME + 'valueOf disallowed');
|
|
4138
|
-
}
|
|
4139
|
-
return stringify$1(x, x.e <= Big.NE || x.e >= Big.PE, true);
|
|
4140
|
-
};
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
// Export
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
var Big = _Big_();
|
|
3135
|
+
function toScaledStr(num, k) {
|
|
3136
|
+
var abs = Math.abs(num);
|
|
3137
|
+
var signed = num != abs;
|
|
3138
|
+
// String() matches behavior of big.js
|
|
3139
|
+
// (as opposed to toFixed() or toPrecision())
|
|
3140
|
+
var s = abs < 1e-6 ? abs.toFixed(k) : String(abs);
|
|
3141
|
+
var parts = s.split('.');
|
|
3142
|
+
var integer = parts[0] == '0' ? '' : parts[0];
|
|
3143
|
+
var decimal = parts.length == 2 ? parts[1] : '';
|
|
3144
|
+
if (decimal.length < k) {
|
|
3145
|
+
decimal = decimal.padEnd(k, '0');
|
|
3146
|
+
} else if (decimal.length > k) {
|
|
3147
|
+
decimal = decimal.slice(0, k);
|
|
3148
|
+
}
|
|
3149
|
+
var s2 = integer + decimal;
|
|
3150
|
+
// remove any leading 0s
|
|
3151
|
+
while (s2[0] == 0 && s2.length > 1) {
|
|
3152
|
+
s2 = s2.slice(1);
|
|
3153
|
+
}
|
|
3154
|
+
if (signed) {
|
|
3155
|
+
s2 = '-' + s2;
|
|
3156
|
+
}
|
|
3157
|
+
return s2;
|
|
3158
|
+
}
|
|
3159
|
+
|
|
3160
|
+
// returns Number
|
|
3161
|
+
function fromScaledStr(s, decimals) {
|
|
3162
|
+
var signed = s[0] == '-';
|
|
3163
|
+
var uns = signed ? s.slice(1) : s;
|
|
3164
|
+
var s2, len;
|
|
3165
|
+
len = uns.length;
|
|
3166
|
+
if (len > decimals) {
|
|
3167
|
+
s2 = uns.slice(0, len - decimals) + '.' + uns.slice(-decimals);
|
|
3168
|
+
} else if (len == decimals) {
|
|
3169
|
+
s2 = '0.' + uns;
|
|
3170
|
+
} else {
|
|
3171
|
+
s2 = '0.' + uns.padStart(decimals, '0');
|
|
3172
|
+
}
|
|
3173
|
+
if (signed) {
|
|
3174
|
+
s2 = '-' + s2;
|
|
3175
|
+
}
|
|
3176
|
+
return Number(s2);
|
|
3177
|
+
}
|
|
3178
|
+
|
|
3179
|
+
function findBigIntScaleFactor() {
|
|
3180
|
+
var minVal = Infinity;
|
|
3181
|
+
var intLen = 0, s;
|
|
3182
|
+
for (var i=0, n=arguments.length; i<n; i++) {
|
|
3183
|
+
minVal = Math.min(minVal, Math.abs(arguments[i]));
|
|
3184
|
+
}
|
|
3185
|
+
if (minVal >= 1) {
|
|
3186
|
+
s = minVal.toFixed(1);
|
|
3187
|
+
intLen = s.indexOf('.');
|
|
3188
|
+
} else if (minVal !== 0) {
|
|
3189
|
+
s = minVal.toFixed(10).slice(2); // decimal part, up to _ decimals
|
|
3190
|
+
while (s[0] === '0') {
|
|
3191
|
+
intLen--;
|
|
3192
|
+
s = s.slice(1);
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
return 17 - intLen;
|
|
3196
|
+
}
|
|
3197
|
+
|
|
3198
|
+
//import { findCrossIntersection_big } from '../geom/mapshaper-segment-geom-big';
|
|
4147
3199
|
|
|
4148
3200
|
// Find the intersection between two 2D segments
|
|
4149
3201
|
// Returns 0, 1 or 2 [x, y] locations as null, [x, y], or [x1, y1, x2, y2]
|
|
@@ -4179,18 +3231,31 @@
|
|
|
4179
3231
|
return touches || cross || null;
|
|
4180
3232
|
}
|
|
4181
3233
|
|
|
4182
|
-
|
|
4183
3234
|
function findCrossIntersection(ax, ay, bx, by, cx, cy, dx, dy, eps) {
|
|
4184
3235
|
var p;
|
|
4185
|
-
|
|
4186
|
-
|
|
3236
|
+
// The normal-precision hit function works for all inputs when eps > 0 because
|
|
3237
|
+
// the geometries that cause the ordinary function fails are detected as
|
|
3238
|
+
// 'touches' or endpoint hits (at least this was true in all the real-world
|
|
3239
|
+
// data samples that were tested).
|
|
3240
|
+
//
|
|
3241
|
+
if (eps > 0 && !segmentHit_fast(ax, ay, bx, by, cx, cy, dx, dy)) {
|
|
3242
|
+
return null;
|
|
3243
|
+
} else if (eps === 0 && !segmentHit_robust(ax, ay, bx, by, cx, cy, dx, dy)) {
|
|
3244
|
+
return null;
|
|
3245
|
+
}
|
|
4187
3246
|
|
|
4188
|
-
// in a typical layer with many intersections
|
|
4189
|
-
// most (>90%) segment intersections in order to keep
|
|
4190
|
-
// error within a small interval (e.g. 50% of eps)
|
|
3247
|
+
// in a typical layer with many intersections along shared polygon boundaries,
|
|
3248
|
+
// robust is preferred in most (>90%) segment intersections in order to keep
|
|
3249
|
+
// the positional error within a small interval (e.g. 50% of eps)
|
|
4191
3250
|
//
|
|
4192
3251
|
if (useRobustCross(ax, ay, bx, by, cx, cy, dx, dy)) {
|
|
4193
3252
|
p = findCrossIntersection_robust(ax, ay, bx, by, cx, cy, dx, dy);
|
|
3253
|
+
// var p2 = findCrossIntersection_big(ax, ay, bx, by, cx, cy, dx, dy);
|
|
3254
|
+
// var dx = p[0] - p2[0];
|
|
3255
|
+
// var dy = p[1] - p2[1];
|
|
3256
|
+
// if (dx != 0 || dy != 0) {
|
|
3257
|
+
// console.log(dx, dy)
|
|
3258
|
+
// }
|
|
4194
3259
|
} else {
|
|
4195
3260
|
p = findCrossIntersection_fast(ax, ay, bx, by, cx, cy, dx, dy);
|
|
4196
3261
|
}
|
|
@@ -4210,41 +3275,6 @@
|
|
|
4210
3275
|
return p;
|
|
4211
3276
|
}
|
|
4212
3277
|
|
|
4213
|
-
// Find the intersection point of two segments that cross each other,
|
|
4214
|
-
// or return null if the segments do not cross.
|
|
4215
|
-
// Assumes endpoint intersections have already been detected
|
|
4216
|
-
function findCrossIntersection_robust(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
4217
|
-
var ax_big = Big(ax);
|
|
4218
|
-
var ay_big = Big(ay);
|
|
4219
|
-
var bx_big = Big(bx);
|
|
4220
|
-
var by_big = Big(by);
|
|
4221
|
-
var cx_big = Big(cx);
|
|
4222
|
-
var cy_big = Big(cy);
|
|
4223
|
-
var dx_big = Big(dx);
|
|
4224
|
-
var dy_big = Big(dy);
|
|
4225
|
-
var v1x = bx_big.minus(ax_big);
|
|
4226
|
-
var v1y = by_big.minus(ay_big);
|
|
4227
|
-
var den_big = determinant2D_big(v1x, v1y, dx_big.minus(cx), dy_big.minus(cy));
|
|
4228
|
-
if (den_big.eq(0)) {
|
|
4229
|
-
debug("DIV0 error (should have been caught upstream)");
|
|
4230
|
-
// console.log("hit?", segmentHit_big(ax, ay, bx, by, cx, cy, dx, dy))
|
|
4231
|
-
// console.log('Seg 1', getSegFeature(ax, ay, bx, by, true))
|
|
4232
|
-
// console.log('Seg 2', getSegFeature(cx, cy, dx, dy, false))
|
|
4233
|
-
return null;
|
|
4234
|
-
}
|
|
4235
|
-
// perform division using regular math, which does not reduce overall
|
|
4236
|
-
// precision in test data (big.js division is very slow)
|
|
4237
|
-
// tests show identical result to:
|
|
4238
|
-
// orient2D_big(cx_big, cy_big, dx_big, dy_big, ax_big, ay_big).div(den_big)
|
|
4239
|
-
var m = orient2D_big(cx_big, cy_big, dx_big, dy_big, ax_big,
|
|
4240
|
-
ay_big).toNumber() / den_big.toNumber();
|
|
4241
|
-
var m_big = Big(m);
|
|
4242
|
-
var x_big = ax_big.plus(m_big.times(v1x).round(16));
|
|
4243
|
-
var y_big = ay_big.plus(m_big.times(v1y).round(16));
|
|
4244
|
-
var p = [x_big.toNumber(), y_big.toNumber()];
|
|
4245
|
-
return p;
|
|
4246
|
-
}
|
|
4247
|
-
|
|
4248
3278
|
function findCrossIntersection_fast(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
4249
3279
|
var den = determinant2D(bx - ax, by - ay, dx - cx, dy - cy);
|
|
4250
3280
|
var m = orient2D(cx, cy, dx, dy, ax, ay) / den;
|
|
@@ -4259,6 +3289,33 @@
|
|
|
4259
3289
|
return p;
|
|
4260
3290
|
}
|
|
4261
3291
|
|
|
3292
|
+
// this function, using BigInt, is 3-4x faster than the version using big.js
|
|
3293
|
+
function findCrossIntersection_robust(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
3294
|
+
var d = findBigIntScaleFactor(ax, ay, bx, by, cx, cy, dx, dy);
|
|
3295
|
+
var d2 = 16; // scale numerator of integer division by this many decimal digits
|
|
3296
|
+
var k_bi = 10000000000000000n; // matches d2
|
|
3297
|
+
var ax_bi = BigInt(toScaledStr(ax, d));
|
|
3298
|
+
var ay_bi = BigInt(toScaledStr(ay, d));
|
|
3299
|
+
var bx_bi = BigInt(toScaledStr(bx, d));
|
|
3300
|
+
var by_bi = BigInt(toScaledStr(by, d));
|
|
3301
|
+
var cx_bi = BigInt(toScaledStr(cx, d));
|
|
3302
|
+
var cy_bi = BigInt(toScaledStr(cy, d));
|
|
3303
|
+
var dx_bi = BigInt(toScaledStr(dx, d));
|
|
3304
|
+
var dy_bi = BigInt(toScaledStr(dy, d));
|
|
3305
|
+
var den = determinant2D(bx_bi - ax_bi, by_bi - ay_bi, dx_bi - cx_bi, dy_bi - cy_bi);
|
|
3306
|
+
if (den === 0n) {
|
|
3307
|
+
debug('DIV0 error - should have been identified as collinear "touch" intersection.');
|
|
3308
|
+
return null;
|
|
3309
|
+
}
|
|
3310
|
+
var num = orient2D(cx_bi, cy_bi, dx_bi, dy_bi, ax_bi, ay_bi) * k_bi;
|
|
3311
|
+
var m_bi = num / den;
|
|
3312
|
+
var x_bi = ax_bi * k_bi + m_bi * (bx_bi - ax_bi);
|
|
3313
|
+
var y_bi = ay_bi * k_bi + m_bi * (by_bi - ay_bi);
|
|
3314
|
+
var x = fromScaledStr(x_bi.toString(), d + d2);
|
|
3315
|
+
var y = fromScaledStr(y_bi.toString(), d + d2);
|
|
3316
|
+
return [x, y];
|
|
3317
|
+
}
|
|
3318
|
+
|
|
4262
3319
|
function useRobustCross(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
4263
3320
|
// angle and seg length ratio thresholds were found by comparing
|
|
4264
3321
|
// fast and robust outputs on sample data
|
|
@@ -4270,7 +3327,7 @@
|
|
|
4270
3327
|
return false;
|
|
4271
3328
|
}
|
|
4272
3329
|
|
|
4273
|
-
// Returns smaller
|
|
3330
|
+
// Returns smaller of two angles between two segments (unsigned)
|
|
4274
3331
|
function innerAngle(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
4275
3332
|
var v1x = bx - ax;
|
|
4276
3333
|
var v1y = by - ay;
|
|
@@ -4419,18 +3476,6 @@
|
|
|
4419
3476
|
p[1] = y;
|
|
4420
3477
|
}
|
|
4421
3478
|
|
|
4422
|
-
// function getSegFeature(x1, y1, x2, y2, hot) {
|
|
4423
|
-
// return JSON.stringify({
|
|
4424
|
-
// type: "Feature",
|
|
4425
|
-
// properties: {
|
|
4426
|
-
// stroke: hot ? "orange" : "blue"
|
|
4427
|
-
// },
|
|
4428
|
-
// geometry: {
|
|
4429
|
-
// type: "LineString",
|
|
4430
|
-
// coordinates: [[x1, y1], [x2, y2]]
|
|
4431
|
-
// }
|
|
4432
|
-
// });
|
|
4433
|
-
// }
|
|
4434
3479
|
|
|
4435
3480
|
// a: coordinate of point
|
|
4436
3481
|
// b: endpoint coordinate of segment
|
|
@@ -4463,10 +3508,6 @@
|
|
|
4463
3508
|
return a * d - b * c;
|
|
4464
3509
|
}
|
|
4465
3510
|
|
|
4466
|
-
function determinant2D_big(a, b, c, d) {
|
|
4467
|
-
return a.times(d).minus(b.times(c));
|
|
4468
|
-
}
|
|
4469
|
-
|
|
4470
3511
|
// returns a positive value if the points a, b, and c are arranged in
|
|
4471
3512
|
// counterclockwise order, a negative value if the points are in clockwise
|
|
4472
3513
|
// order, and zero if the points are collinear.
|
|
@@ -4475,22 +3516,31 @@
|
|
|
4475
3516
|
return determinant2D(ax - cx, ay - cy, bx - cx, by - cy);
|
|
4476
3517
|
}
|
|
4477
3518
|
|
|
4478
|
-
function
|
|
4479
|
-
var
|
|
4480
|
-
var
|
|
4481
|
-
|
|
3519
|
+
function orient2D_robust(ax, ay, bx, by, cx, cy) {
|
|
3520
|
+
var d = findBigIntScaleFactor(ax, ay, bx, by, cx, cy);
|
|
3521
|
+
var ax_bi = BigInt(toScaledStr(ax, d));
|
|
3522
|
+
var ay_bi = BigInt(toScaledStr(ay, d));
|
|
3523
|
+
var bx_bi = BigInt(toScaledStr(bx, d));
|
|
3524
|
+
var by_bi = BigInt(toScaledStr(by, d));
|
|
3525
|
+
var cx_bi = BigInt(toScaledStr(cx, d));
|
|
3526
|
+
var cy_bi = BigInt(toScaledStr(cy, d));
|
|
3527
|
+
var o2d_bi = orient2D(ax_bi, ay_bi, bx_bi, by_bi, cx_bi, cy_bi);
|
|
3528
|
+
return fromScaledStr(o2d_bi.toString(), d);
|
|
4482
3529
|
}
|
|
4483
3530
|
|
|
4484
|
-
function
|
|
4485
|
-
|
|
3531
|
+
function segmentHit_robust(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
3532
|
+
var d = findBigIntScaleFactor(ax, ay, bx, by, cx, cy, dx, dy);
|
|
3533
|
+
var ax_bi = BigInt(toScaledStr(ax, d));
|
|
3534
|
+
var ay_bi = BigInt(toScaledStr(ay, d));
|
|
3535
|
+
var bx_bi = BigInt(toScaledStr(bx, d));
|
|
3536
|
+
var by_bi = BigInt(toScaledStr(by, d));
|
|
3537
|
+
var cx_bi = BigInt(toScaledStr(cx, d));
|
|
3538
|
+
var cy_bi = BigInt(toScaledStr(cy, d));
|
|
3539
|
+
var dx_bi = BigInt(toScaledStr(dx, d));
|
|
3540
|
+
var dy_bi = BigInt(toScaledStr(dy, d));
|
|
3541
|
+
return segmentHit_fast(ax_bi, ay_bi, bx_bi, by_bi, cx_bi, cy_bi, dx_bi, dy_bi);
|
|
4486
3542
|
}
|
|
4487
3543
|
|
|
4488
|
-
|
|
4489
|
-
// export function orient2D_v2(ax, ay, bx, by, cx, cy) {
|
|
4490
|
-
// return -orient2D_robust(ax, ay, bx, by, cx, cy);
|
|
4491
|
-
// }
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
3544
|
// Source: Sedgewick, _Algorithms in C_
|
|
4495
3545
|
// (Other functions were tried that were more sensitive to floating point errors
|
|
4496
3546
|
// than this function)
|
|
@@ -4501,25 +3551,6 @@
|
|
|
4501
3551
|
orient2D(cx, cy, dx, dy, bx, by) <= 0;
|
|
4502
3552
|
}
|
|
4503
3553
|
|
|
4504
|
-
function segmentHit_big(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
4505
|
-
return orient2D_big(ax, ay, bx, by, cx, cy).times(
|
|
4506
|
-
orient2D_big(ax, ay, bx, by, dx, dy)).lte(0) &&
|
|
4507
|
-
orient2D_big(cx, cy, dx, dy, ax, ay).times(
|
|
4508
|
-
orient2D_big(cx, cy, dx, dy, bx, by)).lte(0);
|
|
4509
|
-
}
|
|
4510
|
-
|
|
4511
|
-
function segmentHit_big2(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
4512
|
-
return segmentHit_big(Big(ax), Big(ay), Big(bx), Big(by),
|
|
4513
|
-
Big(cx), Big(cy), Big(dx), Big(dy));
|
|
4514
|
-
}
|
|
4515
|
-
|
|
4516
|
-
// export function segmentHit_robust(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
4517
|
-
// return -orient2D_robust(ax, ay, bx, by, cx, cy) *
|
|
4518
|
-
// -orient2D_robust(ax, ay, bx, by, dx, dy) <= 0 &&
|
|
4519
|
-
// -orient2D_robust(cx, cy, dx, dy, ax, ay) *
|
|
4520
|
-
// -orient2D_robust(cx, cy, dx, dy, bx, by) <= 0;
|
|
4521
|
-
// }
|
|
4522
|
-
|
|
4523
3554
|
// Useful for determining if a segment that intersects another segment is
|
|
4524
3555
|
// entering or leaving an enclosed buffer area
|
|
4525
3556
|
// returns -1 if angle of p1p2 -> p3p4 is counter-clockwise (left turn)
|
|
@@ -4543,11 +3574,9 @@
|
|
|
4543
3574
|
var SegmentGeom = /*#__PURE__*/Object.freeze({
|
|
4544
3575
|
__proto__: null,
|
|
4545
3576
|
findClosestPointOnSeg: findClosestPointOnSeg,
|
|
3577
|
+
findCrossIntersection_robust: findCrossIntersection_robust,
|
|
4546
3578
|
orient2D: orient2D,
|
|
4547
|
-
|
|
4548
|
-
orient2D_big2: orient2D_big2,
|
|
4549
|
-
segmentHit_big: segmentHit_big,
|
|
4550
|
-
segmentHit_big2: segmentHit_big2,
|
|
3579
|
+
orient2D_robust: orient2D_robust,
|
|
4551
3580
|
segmentHit_fast: segmentHit_fast,
|
|
4552
3581
|
segmentIntersection: segmentIntersection,
|
|
4553
3582
|
segmentTurn: segmentTurn
|
|
@@ -4878,10 +3907,12 @@
|
|
|
4878
3907
|
for (var i=0; i<n; i++) {
|
|
4879
3908
|
retn = cb(parts[i], i, parts, shpId);
|
|
4880
3909
|
if (retn === null) {
|
|
4881
|
-
nulls++;
|
|
4882
3910
|
parts[i] = null;
|
|
4883
3911
|
} else if (utils.isArray(retn)) {
|
|
4884
|
-
parts[i] = retn;
|
|
3912
|
+
parts[i] = retn.length > 0 ? retn : null;
|
|
3913
|
+
}
|
|
3914
|
+
if (parts[i] === null) {
|
|
3915
|
+
nulls++;
|
|
4885
3916
|
}
|
|
4886
3917
|
}
|
|
4887
3918
|
if (nulls == n) {
|
|
@@ -7058,10 +6089,11 @@
|
|
|
7058
6089
|
};
|
|
7059
6090
|
|
|
7060
6091
|
this.arcIsDegenerate = function(arcId) {
|
|
7061
|
-
return this.arcHasZeroLength(arcId) || this.arcIsSpike(arcId);
|
|
6092
|
+
return this.arcHasZeroLength(arcId) || this.arcIsSpike(arcId) || this.arcIsSpike_v1(arcId);
|
|
7062
6093
|
};
|
|
7063
6094
|
|
|
7064
|
-
|
|
6095
|
+
// only finds two-segment spikes
|
|
6096
|
+
this.arcIsSpike_v1 = function(arcId) {
|
|
7065
6097
|
var iter = this.getArcIter(arcId);
|
|
7066
6098
|
var x0, y0;
|
|
7067
6099
|
if (iter.hasNext()) {
|
|
@@ -7078,6 +6110,22 @@
|
|
|
7078
6110
|
return false;
|
|
7079
6111
|
};
|
|
7080
6112
|
|
|
6113
|
+
// identifies n-segment spikes
|
|
6114
|
+
this.arcIsSpike = function(arcId) {
|
|
6115
|
+
arcId = absArcId(arcId);
|
|
6116
|
+
var i = _ii[arcId];
|
|
6117
|
+
var j = i + _nn[arcId] - 1;
|
|
6118
|
+
while (j > i) {
|
|
6119
|
+
if (_xx[i] != _xx[j] || _yy[i] != _yy[j]) {
|
|
6120
|
+
return false;
|
|
6121
|
+
}
|
|
6122
|
+
j--;
|
|
6123
|
+
i++;
|
|
6124
|
+
}
|
|
6125
|
+
return true;
|
|
6126
|
+
};
|
|
6127
|
+
|
|
6128
|
+
|
|
7081
6129
|
this.arcHasZeroLength = function(arcId) {
|
|
7082
6130
|
var iter = this.getArcIter(arcId);
|
|
7083
6131
|
var i = 0,
|
|
@@ -8512,7 +7560,15 @@
|
|
|
8512
7560
|
}
|
|
8513
7561
|
|
|
8514
7562
|
function mergeOutputLayerIntoDataset(lyr, dataset, dataset2, opts) {
|
|
8515
|
-
|
|
7563
|
+
var output = mergeOutputLayersIntoDataset(lyr, dataset, dataset2, opts);
|
|
7564
|
+
if (output.length != 1) {
|
|
7565
|
+
error('Expected 1 output layer, received:', output.length);
|
|
7566
|
+
}
|
|
7567
|
+
return output[0];
|
|
7568
|
+
}
|
|
7569
|
+
|
|
7570
|
+
function mergeOutputLayersIntoDataset(lyr, dataset, dataset2, opts) {
|
|
7571
|
+
if (!dataset2 || dataset2.layers.length === 0) {
|
|
8516
7572
|
error('Invalid source dataset');
|
|
8517
7573
|
}
|
|
8518
7574
|
if (dataset.layers.includes(lyr) === false) {
|
|
@@ -8521,24 +7577,21 @@
|
|
|
8521
7577
|
// this command returns merged layers instead of adding them to target dataset
|
|
8522
7578
|
var outputLayers = mergeDatasetsIntoDataset(dataset, [dataset2]);
|
|
8523
7579
|
var lyr2 = outputLayers[0];
|
|
8524
|
-
|
|
8525
7580
|
// TODO: find a more reliable way of knowing when to copy data
|
|
8526
7581
|
var copyData = !lyr2.data && lyr.data && getFeatureCount(lyr2) == lyr.data.size();
|
|
8527
|
-
|
|
8528
7582
|
if (copyData) {
|
|
8529
7583
|
lyr2.data = opts.no_replace ? lyr.data.clone() : lyr.data;
|
|
8530
7584
|
}
|
|
8531
|
-
|
|
8532
|
-
|
|
7585
|
+
lyr2.name = opts.name || lyr.name;
|
|
7586
|
+
if (!opts.no_replace) {
|
|
7587
|
+
outputLayers[0] = Object.assign(lyr, {data: null, shapes: null}, lyr2);
|
|
8533
7588
|
if (layerHasPaths(lyr)) {
|
|
8534
7589
|
// Remove unused arcs from replaced layer
|
|
8535
7590
|
// TODO: consider using clean insead of this
|
|
8536
7591
|
dissolveArcs(dataset);
|
|
8537
7592
|
}
|
|
8538
7593
|
}
|
|
8539
|
-
|
|
8540
|
-
lyr2.name = opts.name || lyr2.name;
|
|
8541
|
-
return lyr2;
|
|
7594
|
+
return outputLayers;
|
|
8542
7595
|
}
|
|
8543
7596
|
|
|
8544
7597
|
// Transform the points in a dataset in-place; don't clean up corrupted shapes
|
|
@@ -8566,6 +7619,7 @@
|
|
|
8566
7619
|
getDatasetBounds: getDatasetBounds,
|
|
8567
7620
|
mergeDatasetInfo: mergeDatasetInfo,
|
|
8568
7621
|
mergeOutputLayerIntoDataset: mergeOutputLayerIntoDataset,
|
|
7622
|
+
mergeOutputLayersIntoDataset: mergeOutputLayersIntoDataset,
|
|
8569
7623
|
pruneArcs: pruneArcs,
|
|
8570
7624
|
replaceLayerContents: replaceLayerContents,
|
|
8571
7625
|
replaceLayers: replaceLayers,
|
|
@@ -12521,7 +11575,7 @@
|
|
|
12521
11575
|
shapes: lyr.shapes || null,
|
|
12522
11576
|
data: data,
|
|
12523
11577
|
menu_order: lyr.menu_order || null,
|
|
12524
|
-
pinned: lyr.pinned || false,
|
|
11578
|
+
pinned: lyr.pinned || opts.show_all || false,
|
|
12525
11579
|
active: !!(lyr.active || lyr == opts.active_layer) // lyr.active: deprecated
|
|
12526
11580
|
};
|
|
12527
11581
|
}
|
|
@@ -13912,6 +12966,23 @@
|
|
|
13912
12966
|
vertexIsArcStart: vertexIsArcStart
|
|
13913
12967
|
});
|
|
13914
12968
|
|
|
12969
|
+
function debugConnectedArcs(ids, arcs) {
|
|
12970
|
+
var colors = ['orange', 'blue', 'green', 'red', 'magenta', 'grey'];
|
|
12971
|
+
var features = ids.map(function(arcId, i) {
|
|
12972
|
+
return getArcFeature(arcId, arcs, {arcId: arcId, stroke: colors[i] || 'black'});
|
|
12973
|
+
});
|
|
12974
|
+
var geojson = '{"type": "FeatureCollection", "features": [' + features.join(',') + ']}';
|
|
12975
|
+
debug(geojson);
|
|
12976
|
+
}
|
|
12977
|
+
|
|
12978
|
+
function getArcFeature(arcId, arcs, properties) {
|
|
12979
|
+
return JSON.stringify({
|
|
12980
|
+
type: "Feature",
|
|
12981
|
+
properties: properties,
|
|
12982
|
+
geometry: GeoJSON.exportLineGeom([[arcId]], arcs)
|
|
12983
|
+
});
|
|
12984
|
+
}
|
|
12985
|
+
|
|
13915
12986
|
function isValidArc(arcId, arcs) {
|
|
13916
12987
|
// check for arcs with no vertices
|
|
13917
12988
|
// TODO: also check for other kinds of degenerate arcs
|
|
@@ -13958,7 +13029,6 @@
|
|
|
13958
13029
|
error("Duplicate point error");
|
|
13959
13030
|
}*/
|
|
13960
13031
|
|
|
13961
|
-
|
|
13962
13032
|
for (j=0; j<ids.length; j++) {
|
|
13963
13033
|
candId = ids[j];
|
|
13964
13034
|
if (!isValidArc(candId, arcs)) {
|
|
@@ -13977,6 +13047,10 @@
|
|
|
13977
13047
|
|
|
13978
13048
|
if (xx[ito] == xx[icand] && yy[ito] == yy[icand]) {
|
|
13979
13049
|
debug("Pathfinder warning: duplicate segments: i:", ito, "j:", icand, "ids:", [fromArcId].concat(ids));
|
|
13050
|
+
if (useDebug()) {
|
|
13051
|
+
// debugDuplicatePathfinderSegments(nodeX, nodeY, ito, icand, nodes.arcs);
|
|
13052
|
+
debugConnectedArcs(ids, nodes.arcs);
|
|
13053
|
+
}
|
|
13980
13054
|
}
|
|
13981
13055
|
if (code == 2) {
|
|
13982
13056
|
ito = icand;
|
|
@@ -14027,8 +13101,7 @@
|
|
|
14027
13101
|
}
|
|
14028
13102
|
|
|
14029
13103
|
function chooseBetweenClosePaths(nodeX, nodeY, ax, ay, bx, by) {
|
|
14030
|
-
|
|
14031
|
-
var orient = orient2D_big2(ax, ay, nodeX, nodeY, bx, by);
|
|
13104
|
+
var orient = orient2D_robust(ax, ay, nodeX, nodeY, bx, by);
|
|
14032
13105
|
var code;
|
|
14033
13106
|
if (orient > 0) {
|
|
14034
13107
|
code = 2;
|
|
@@ -18016,17 +17089,14 @@
|
|
|
18016
17089
|
if (changed || opts.rebuild_topology) {
|
|
18017
17090
|
buildTopology(dataset);
|
|
18018
17091
|
}
|
|
18019
|
-
|
|
18020
|
-
//
|
|
18021
|
-
// TODO: consider alternative -- avoid creating degenerate arcs
|
|
18022
|
-
// in insertCutPoints()
|
|
17092
|
+
// Remove degenerate shapes
|
|
17093
|
+
// Without this step, pathfinder function would encounter dead ends.
|
|
18023
17094
|
dataset.layers.forEach(function(lyr) {
|
|
18024
17095
|
if (layerHasPaths(lyr)) {
|
|
18025
17096
|
cleanShapes(lyr.shapes, arcs, lyr.geometry_type);
|
|
18026
17097
|
}
|
|
18027
17098
|
});
|
|
18028
|
-
|
|
18029
|
-
// Further clean-up -- remove duplicate and missing arcs
|
|
17099
|
+
// Further clean-up -- remove duplicate and unused arcs, etc.
|
|
18030
17100
|
nodes = cleanArcReferences(dataset);
|
|
18031
17101
|
return nodes;
|
|
18032
17102
|
}
|
|
@@ -18036,38 +17106,26 @@
|
|
|
18036
17106
|
var cutOpts = snapDist > 0 ? {tolerance: snapDist} : {tolerance: 0};
|
|
18037
17107
|
var coordsHaveChanged = false;
|
|
18038
17108
|
var snapCount, dupeCount, cutCount;
|
|
18039
|
-
var
|
|
18040
|
-
snapCount = snapCoordsByInterval(arcs, snapDist);
|
|
18041
|
-
dupeCount = arcs.dedupCoords();
|
|
18042
|
-
|
|
18043
|
-
// why was topology built here previously????
|
|
18044
|
-
// if (snapCount > 0 || dupeCount > 0) {
|
|
18045
|
-
// // Detect topology again if coordinates have changed
|
|
18046
|
-
// internal.buildTopology(dataset);
|
|
18047
|
-
// }
|
|
18048
|
-
|
|
18049
|
-
// cut arcs at points where segments intersect
|
|
18050
|
-
cutCount = cutPathsAtIntersections(dataset, cutOpts);
|
|
18051
|
-
passCount++;
|
|
18052
|
-
if (cutCount > 0 || snapCount > 0 || dupeCount > 0) {
|
|
18053
|
-
coordsHaveChanged = true;
|
|
18054
|
-
}
|
|
17109
|
+
var maxLoops = 4, loopCount = 0;
|
|
18055
17110
|
|
|
18056
|
-
//
|
|
18057
|
-
|
|
18058
|
-
|
|
17111
|
+
// snap + cut until no more cuts are needed
|
|
17112
|
+
do {
|
|
17113
|
+
loopCount++;
|
|
17114
|
+
cutCount = 0;
|
|
18059
17115
|
snapCount = snapCoordsByInterval(arcs, snapDist);
|
|
18060
17116
|
dupeCount = arcs.dedupCoords();
|
|
18061
|
-
|
|
18062
|
-
|
|
17117
|
+
if (snapCount > 0 || cutCount > 0 || loopCount == 1) {
|
|
17118
|
+
// cut arcs at points where segments intersect
|
|
18063
17119
|
cutCount = cutPathsAtIntersections(dataset, cutOpts);
|
|
18064
|
-
debug("[snapAndCut] pass:", passCount, "snaps:", snapCount, "dupes:", dupeCount, "cuts:", cutCount);
|
|
18065
17120
|
}
|
|
18066
|
-
|
|
17121
|
+
coordsHaveChanged |= (snapCount + dupeCount + cutCount) > 0;
|
|
17122
|
+
debug("[snapAndCut] pass:", loopCount, "snaps:", snapCount, "dupes:", dupeCount, "cuts:", cutCount);
|
|
17123
|
+
} while (loopCount < maxLoops && cutCount > 0);
|
|
18067
17124
|
|
|
18068
|
-
//
|
|
17125
|
+
// should this be added?
|
|
17126
|
+
// if (cutCount > 0 && loopCount == maxLoops) {
|
|
17127
|
+
// snapCount = snapCoordsByInterval(arcs, snapDist);
|
|
18069
17128
|
// arcs.dedupCoords(); // need to do this here?
|
|
18070
|
-
// debug('Second-pass vertices added:', cutCount, 'consider third pass?');
|
|
18071
17129
|
// }
|
|
18072
17130
|
|
|
18073
17131
|
return coordsHaveChanged;
|
|
@@ -19596,6 +18654,7 @@
|
|
|
19596
18654
|
return geom;
|
|
19597
18655
|
};
|
|
19598
18656
|
|
|
18657
|
+
// ids: shape data (array of array of arc ids)
|
|
19599
18658
|
GeoJSON.exportLineGeom = function(ids, arcs) {
|
|
19600
18659
|
var obj = exportPathData(ids, arcs, "polyline");
|
|
19601
18660
|
if (obj.pointCount === 0) return null;
|
|
@@ -19611,6 +18670,7 @@
|
|
|
19611
18670
|
};
|
|
19612
18671
|
};
|
|
19613
18672
|
|
|
18673
|
+
// ids: shape data (array of array of arc ids)
|
|
19614
18674
|
GeoJSON.exportPolygonGeom = function(ids, arcs, opts) {
|
|
19615
18675
|
var obj = exportPathData(ids, arcs, "polygon");
|
|
19616
18676
|
if (obj.pointCount === 0) return null;
|
|
@@ -25689,6 +24749,10 @@ ${svg}
|
|
|
25689
24749
|
type: 'flag',
|
|
25690
24750
|
describe: '[CSV] export numbers with decimal commas not points'
|
|
25691
24751
|
})
|
|
24752
|
+
.option('show-all', {
|
|
24753
|
+
type: 'flag',
|
|
24754
|
+
describe: '[Snapshot] show all layers in Web UI'
|
|
24755
|
+
})
|
|
25692
24756
|
.option('final', {
|
|
25693
24757
|
type: 'flag' // for testing
|
|
25694
24758
|
})
|
|
@@ -25737,24 +24801,54 @@ ${svg}
|
|
|
25737
24801
|
// describe: 'number of vertices to use when buffering points',
|
|
25738
24802
|
type: 'integer'
|
|
25739
24803
|
})
|
|
24804
|
+
.option('arc-quality', {
|
|
24805
|
+
// segments per quarter-circle in joins and caps
|
|
24806
|
+
type: 'integer'
|
|
24807
|
+
})
|
|
24808
|
+
.option('slice-length', {
|
|
24809
|
+
// max path segments per buffer section
|
|
24810
|
+
type: 'integer'
|
|
24811
|
+
})
|
|
25740
24812
|
.option('backtrack', {
|
|
24813
|
+
// ...
|
|
25741
24814
|
type: 'integer'
|
|
25742
24815
|
})
|
|
24816
|
+
.option('cap-style', {
|
|
24817
|
+
describe: 'flat or round (default is round)'
|
|
24818
|
+
})
|
|
25743
24819
|
.option('type', {
|
|
25744
24820
|
// left, right, outer, inner (default is full buffer)
|
|
25745
24821
|
})
|
|
25746
24822
|
.option('planar', {
|
|
25747
24823
|
type: 'flag'
|
|
25748
24824
|
})
|
|
25749
|
-
.option('v2', {
|
|
24825
|
+
.option('v2', {
|
|
24826
|
+
type: 'flag'
|
|
24827
|
+
})
|
|
24828
|
+
.option('v3', {
|
|
24829
|
+
type: 'flag'
|
|
24830
|
+
})
|
|
24831
|
+
.option('debug-offset', {
|
|
25750
24832
|
type: 'flag'
|
|
25751
24833
|
})
|
|
25752
|
-
.option('debug-
|
|
24834
|
+
.option('debug-winding', {
|
|
25753
24835
|
type: 'flag'
|
|
25754
24836
|
})
|
|
24837
|
+
.option('debug-points', {
|
|
24838
|
+
type: 'flag'
|
|
24839
|
+
})
|
|
24840
|
+
// .option('debug-division', {
|
|
24841
|
+
// type: 'flag'
|
|
24842
|
+
// })
|
|
25755
24843
|
.option('debug-mosaic', {
|
|
25756
24844
|
type: 'flag'
|
|
25757
24845
|
})
|
|
24846
|
+
.option('left', {
|
|
24847
|
+
type: 'flag'
|
|
24848
|
+
})
|
|
24849
|
+
.option('right', {
|
|
24850
|
+
type: 'flag'
|
|
24851
|
+
})
|
|
25758
24852
|
.option('no-cleanup', {
|
|
25759
24853
|
type: 'flag'
|
|
25760
24854
|
})
|
|
@@ -31891,14 +30985,112 @@ ${svg}
|
|
|
31891
30985
|
return dataset;
|
|
31892
30986
|
}
|
|
31893
30987
|
|
|
30988
|
+
function getPolygonRewinder(nodes) {
|
|
30989
|
+
var splitter = getSelfIntersectionSplitter(nodes);
|
|
30990
|
+
return rewindPolygon;
|
|
30991
|
+
|
|
30992
|
+
function rewindPolygon(shp) {
|
|
30993
|
+
var shp2 = [];
|
|
30994
|
+
forEachShapePart(shp, function(ids) {
|
|
30995
|
+
var rings = splitter(ids);
|
|
30996
|
+
var path;
|
|
30997
|
+
for (var i=0; i<rings.length; i++) {
|
|
30998
|
+
path = rings[i];
|
|
30999
|
+
if (geom.getPlanarPathArea(path, nodes.arcs) < 0) {
|
|
31000
|
+
path = reversePath(path);
|
|
31001
|
+
}
|
|
31002
|
+
shp2.push(path);
|
|
31003
|
+
}
|
|
31004
|
+
});
|
|
31005
|
+
return shp2.length > 0 ? shp2 : null;
|
|
31006
|
+
}
|
|
31007
|
+
}
|
|
31008
|
+
|
|
31009
|
+
function rewindPolygonParts(lyr, nodes) {
|
|
31010
|
+
var rewind = getPolygonRewinder(nodes);
|
|
31011
|
+
lyr.shapes = lyr.shapes.map(function(shp) {
|
|
31012
|
+
return rewind(shp);
|
|
31013
|
+
});
|
|
31014
|
+
}
|
|
31015
|
+
|
|
31016
|
+
// TODO: Need to rethink polygon repair: these function can cause problems
|
|
31017
|
+
// when part of a self-intersecting polygon is removed
|
|
31018
|
+
//
|
|
31019
|
+
function repairPolygonGeometry(layers, dataset, opts) {
|
|
31020
|
+
var nodes = addIntersectionCuts(dataset);
|
|
31021
|
+
layers.forEach(function(lyr) {
|
|
31022
|
+
repairSelfIntersections(lyr, nodes);
|
|
31023
|
+
});
|
|
31024
|
+
return layers;
|
|
31025
|
+
}
|
|
31026
|
+
|
|
31027
|
+
// Remove any small shapes formed by twists in each ring
|
|
31028
|
+
// // OOPS, NO // Retain only the part with largest area
|
|
31029
|
+
// // this causes problems when a cut-off hole has a matching ring in another polygon
|
|
31030
|
+
// TODO: consider cases where cut-off parts should be retained
|
|
31031
|
+
//
|
|
31032
|
+
function repairSelfIntersections(lyr, nodes) {
|
|
31033
|
+
var splitter = getSelfIntersectionSplitter(nodes);
|
|
31034
|
+
|
|
31035
|
+
lyr.shapes = lyr.shapes.map(function(shp, i) {
|
|
31036
|
+
return cleanPolygon(shp);
|
|
31037
|
+
});
|
|
31038
|
+
|
|
31039
|
+
function cleanPolygon(shp) {
|
|
31040
|
+
var cleanedPolygon = [];
|
|
31041
|
+
forEachShapePart(shp, function(ids) {
|
|
31042
|
+
// TODO: consider returning null if path can't be split
|
|
31043
|
+
var splitIds = splitter(ids);
|
|
31044
|
+
if (splitIds.length === 0) {
|
|
31045
|
+
error("[cleanPolygon()] Defective path:", ids);
|
|
31046
|
+
} else if (splitIds.length == 1) {
|
|
31047
|
+
cleanedPolygon.push(splitIds[0]);
|
|
31048
|
+
} else {
|
|
31049
|
+
var shapeArea = geom.getPlanarPathArea(ids, nodes.arcs),
|
|
31050
|
+
sign = shapeArea > 0 ? 1 : -1,
|
|
31051
|
+
mainRing;
|
|
31052
|
+
|
|
31053
|
+
splitIds.reduce(function(max, ringIds, i) {
|
|
31054
|
+
var pathArea = geom.getPlanarPathArea(ringIds, nodes.arcs) * sign;
|
|
31055
|
+
if (pathArea > max) {
|
|
31056
|
+
mainRing = ringIds;
|
|
31057
|
+
max = pathArea;
|
|
31058
|
+
}
|
|
31059
|
+
return max;
|
|
31060
|
+
}, 0);
|
|
31061
|
+
|
|
31062
|
+
if (mainRing) {
|
|
31063
|
+
cleanedPolygon.push(mainRing);
|
|
31064
|
+
}
|
|
31065
|
+
}
|
|
31066
|
+
});
|
|
31067
|
+
return cleanedPolygon.length > 0 ? cleanedPolygon : null;
|
|
31068
|
+
}
|
|
31069
|
+
}
|
|
31070
|
+
|
|
31071
|
+
var PolygonRepair = /*#__PURE__*/Object.freeze({
|
|
31072
|
+
__proto__: null,
|
|
31073
|
+
getPolygonRewinder: getPolygonRewinder,
|
|
31074
|
+
repairPolygonGeometry: repairPolygonGeometry,
|
|
31075
|
+
repairSelfIntersections: repairSelfIntersections,
|
|
31076
|
+
rewindPolygonParts: rewindPolygonParts
|
|
31077
|
+
});
|
|
31078
|
+
|
|
31894
31079
|
function dissolveBufferDataset(dataset, optsArg) {
|
|
31895
31080
|
var opts = optsArg || {};
|
|
31896
31081
|
var lyr = dataset.layers[0];
|
|
31897
31082
|
var tmp;
|
|
31898
|
-
|
|
31899
|
-
|
|
31900
|
-
return debugBufferDivision(lyr, nodes);
|
|
31083
|
+
if (opts.debug_offset) {
|
|
31084
|
+
return; // raw offset path
|
|
31901
31085
|
}
|
|
31086
|
+
var nodes = addIntersectionCuts(dataset, {rebuild_topology: true});
|
|
31087
|
+
if (opts.debug_winding) {
|
|
31088
|
+
rewindPolygonParts(lyr, nodes);
|
|
31089
|
+
// debugRingsAndHoles(lyr, nodes);
|
|
31090
|
+
return;
|
|
31091
|
+
}
|
|
31092
|
+
rewindPolygonParts(lyr, nodes);
|
|
31093
|
+
|
|
31902
31094
|
var mosaicIndex = new MosaicIndex(lyr, nodes, {flat: false, no_holes: false});
|
|
31903
31095
|
if (opts.debug_mosaic) {
|
|
31904
31096
|
tmp = composeMosaicLayer(lyr, mosaicIndex.mosaic);
|
|
@@ -31921,49 +31113,6 @@ ${svg}
|
|
|
31921
31113
|
}
|
|
31922
31114
|
}
|
|
31923
31115
|
|
|
31924
|
-
function debugBufferDivision(lyr, nodes) {
|
|
31925
|
-
var divide = getHoleDivider(nodes);
|
|
31926
|
-
var shapes2 = [];
|
|
31927
|
-
var records = [];
|
|
31928
|
-
lyr.shapes.forEach(divideShape);
|
|
31929
|
-
lyr.shapes = shapes2;
|
|
31930
|
-
lyr.data = new DataTable(records);
|
|
31931
|
-
return lyr;
|
|
31932
|
-
|
|
31933
|
-
function divideShape(shp) {
|
|
31934
|
-
var cw = [], ccw = [];
|
|
31935
|
-
divide(shp, cw, ccw);
|
|
31936
|
-
cw.forEach(function(ring) {
|
|
31937
|
-
shapes2.push([ring]);
|
|
31938
|
-
records.push({type: 'ring'});
|
|
31939
|
-
});
|
|
31940
|
-
ccw.forEach(function(hole) {
|
|
31941
|
-
shapes2.push([reversePath(hole)]);
|
|
31942
|
-
records.push({type: 'hole'});
|
|
31943
|
-
});
|
|
31944
|
-
}
|
|
31945
|
-
}
|
|
31946
|
-
|
|
31947
|
-
// n = number of segments used to approximate a circle
|
|
31948
|
-
// Returns tolerance as a percent of circle radius
|
|
31949
|
-
function getBufferToleranceFromCircleSegments(n) {
|
|
31950
|
-
return 1 - Math.cos(Math.PI / n);
|
|
31951
|
-
}
|
|
31952
|
-
|
|
31953
|
-
function getArcDegreesFromTolerancePct(pct) {
|
|
31954
|
-
return 360 * Math.acos(1 - pct) / Math.PI;
|
|
31955
|
-
}
|
|
31956
|
-
|
|
31957
|
-
// n = number of segments used to approximate a circle
|
|
31958
|
-
// Returns tolerance as a percent of circle radius
|
|
31959
|
-
function getBufferToleranceFromCircleSegments2(n) {
|
|
31960
|
-
return 1 / Math.cos(Math.PI / n) - 1;
|
|
31961
|
-
}
|
|
31962
|
-
|
|
31963
|
-
function getArcDegreesFromTolerancePct2(pct) {
|
|
31964
|
-
return 360 * Math.acos(1 / (pct + 1)) / Math.PI;
|
|
31965
|
-
}
|
|
31966
|
-
|
|
31967
31116
|
// return constant distance in meters, or return null if unparsable
|
|
31968
31117
|
function parseConstantBufferDistance(str, crs) {
|
|
31969
31118
|
var parsed = parseMeasure2(str);
|
|
@@ -31971,16 +31120,6 @@ ${svg}
|
|
|
31971
31120
|
return convertDistanceParam(str, crs) || null;
|
|
31972
31121
|
}
|
|
31973
31122
|
|
|
31974
|
-
function getBufferToleranceFunction(dataset, opts) {
|
|
31975
|
-
var crs = getDatasetCRS(dataset);
|
|
31976
|
-
var constTol = opts.tolerance ? parseConstantBufferDistance(opts.tolerance, crs) : 0;
|
|
31977
|
-
var pctOfRadius = 1/100;
|
|
31978
|
-
return function(meterDist) {
|
|
31979
|
-
if (constTol) return constTol;
|
|
31980
|
-
return constTol ? constTol : meterDist * pctOfRadius;
|
|
31981
|
-
};
|
|
31982
|
-
}
|
|
31983
|
-
|
|
31984
31123
|
function getBufferDistanceFunction(lyr, dataset, opts) {
|
|
31985
31124
|
if (!opts.radius) {
|
|
31986
31125
|
stop('Missing expected radius parameter');
|
|
@@ -31999,17 +31138,579 @@ ${svg}
|
|
|
31999
31138
|
};
|
|
32000
31139
|
}
|
|
32001
31140
|
|
|
32002
|
-
|
|
32003
|
-
|
|
32004
|
-
|
|
32005
|
-
|
|
32006
|
-
|
|
32007
|
-
|
|
32008
|
-
|
|
32009
|
-
|
|
32010
|
-
|
|
32011
|
-
|
|
32012
|
-
|
|
31141
|
+
function BufferBuilder(opts) {
|
|
31142
|
+
var self = {};
|
|
31143
|
+
var buffer, path, insideFlags;
|
|
31144
|
+
var points = [];
|
|
31145
|
+
var backtrackSteps = opts.backtrack >= 0 ? opts.backtrack : 100;
|
|
31146
|
+
var backtrackStopIdx = 0; // lowest vertex id in backtrack pass
|
|
31147
|
+
|
|
31148
|
+
init();
|
|
31149
|
+
|
|
31150
|
+
function init() {
|
|
31151
|
+
buffer = [];
|
|
31152
|
+
path = [];
|
|
31153
|
+
insideFlags = [];
|
|
31154
|
+
backtrackStopIdx = 0;
|
|
31155
|
+
}
|
|
31156
|
+
|
|
31157
|
+
self.size = function() {
|
|
31158
|
+
return path.length + buffer.length;
|
|
31159
|
+
};
|
|
31160
|
+
|
|
31161
|
+
self.getDebugPoints = function() {
|
|
31162
|
+
var tmp = points;
|
|
31163
|
+
points = [];
|
|
31164
|
+
return tmp;
|
|
31165
|
+
};
|
|
31166
|
+
|
|
31167
|
+
function makeDebugPoints() {
|
|
31168
|
+
points = points.concat(buffer.map((p, i) => {
|
|
31169
|
+
return {
|
|
31170
|
+
type: 'Feature',
|
|
31171
|
+
geometry: {
|
|
31172
|
+
type: 'Point',
|
|
31173
|
+
coordinates: p
|
|
31174
|
+
},
|
|
31175
|
+
properties: {
|
|
31176
|
+
id: i,
|
|
31177
|
+
inside: insideFlags[i],
|
|
31178
|
+
fill: insideFlags[i] ? 'magenta' : 'dodgerblue'
|
|
31179
|
+
}
|
|
31180
|
+
};
|
|
31181
|
+
}));
|
|
31182
|
+
}
|
|
31183
|
+
|
|
31184
|
+
self.addPathVertex = function(p) {
|
|
31185
|
+
path.push(p);
|
|
31186
|
+
};
|
|
31187
|
+
|
|
31188
|
+
self.addBufferVertices = function(arr) {
|
|
31189
|
+
for (var i=0; i<arr.length; i++) {
|
|
31190
|
+
self.addBufferVertex(arr[i]);
|
|
31191
|
+
}
|
|
31192
|
+
};
|
|
31193
|
+
|
|
31194
|
+
self.addBufferVertex = function(p, isLeftTurn) {
|
|
31195
|
+
var bufferLen = insideFlags.length;
|
|
31196
|
+
var prevIdx = bufferLen - 1;
|
|
31197
|
+
if (isLeftTurn) {
|
|
31198
|
+
// first extruded point after left turn
|
|
31199
|
+
// assume that the point is inside the buffer
|
|
31200
|
+
insideFlags.push(true);
|
|
31201
|
+
buffer.push(p);
|
|
31202
|
+
return;
|
|
31203
|
+
}
|
|
31204
|
+
if (prevIdx == -1) {
|
|
31205
|
+
// first point in buffer
|
|
31206
|
+
buffer.push(p);
|
|
31207
|
+
insideFlags.push(false);
|
|
31208
|
+
return;
|
|
31209
|
+
}
|
|
31210
|
+
var prevP = buffer[prevIdx];
|
|
31211
|
+
var prevFlag = insideFlags[prevIdx];
|
|
31212
|
+
if (pointsAreSame(prevP, p)) {
|
|
31213
|
+
// console.log('SAME POINT')
|
|
31214
|
+
return;
|
|
31215
|
+
}
|
|
31216
|
+
// if no segment cross is detected below, inside/outside stays the same
|
|
31217
|
+
var newFlag = prevFlag;
|
|
31218
|
+
var hit, a, b, flagA, flagB;
|
|
31219
|
+
for (var i=0, idx = bufferLen - 3; i < backtrackSteps && idx >= backtrackStopIdx; i++, idx--) {
|
|
31220
|
+
a = buffer[idx];
|
|
31221
|
+
b = buffer[idx + 1];
|
|
31222
|
+
// TODO: consider using a geodetic intersection function for lat-long datasets
|
|
31223
|
+
// TODO: consider case of an endpoint hit
|
|
31224
|
+
// TODO: consider case of collinear hit
|
|
31225
|
+
hit = bufferIntersection$2(a[0], a[1], b[0], b[1], prevP[0], prevP[1], p[0], p[1]);
|
|
31226
|
+
if (!hit) {
|
|
31227
|
+
// continue scanning backward for an intersection
|
|
31228
|
+
continue;
|
|
31229
|
+
}
|
|
31230
|
+
// console.log("hit")
|
|
31231
|
+
flagA = insideFlags[idx];
|
|
31232
|
+
flagB = insideFlags[idx + 1];
|
|
31233
|
+
if (flagA && flagB) {
|
|
31234
|
+
// newest segment collides with an interior segment - do not rewind
|
|
31235
|
+
continue;
|
|
31236
|
+
}
|
|
31237
|
+
if (prevFlag === false) {
|
|
31238
|
+
// if segment intersects an outside segment from outside the buffer,
|
|
31239
|
+
// we are likely closing a loop,
|
|
31240
|
+
// so we stop backtracking and reset the backtrack limit to avoid
|
|
31241
|
+
// removing the loop
|
|
31242
|
+
backtrackStopIdx = i + 1;
|
|
31243
|
+
break;
|
|
31244
|
+
}
|
|
31245
|
+
|
|
31246
|
+
// newest segment collides with an exterior segment
|
|
31247
|
+
// * assume we're going from inside to outside
|
|
31248
|
+
// * and can therefore remove an interior loop
|
|
31249
|
+
// TODO: improve (this assumption does not hold when closing an oxbow type loop)
|
|
31250
|
+
while (buffer.length > idx + 1) {
|
|
31251
|
+
buffer.pop();
|
|
31252
|
+
insideFlags.pop();
|
|
31253
|
+
}
|
|
31254
|
+
buffer.push(hit);
|
|
31255
|
+
insideFlags.push(false);
|
|
31256
|
+
newFlag = false;
|
|
31257
|
+
// console.log("going out")
|
|
31258
|
+
break;
|
|
31259
|
+
}
|
|
31260
|
+
|
|
31261
|
+
buffer.push(p);
|
|
31262
|
+
insideFlags.push(newFlag);
|
|
31263
|
+
};
|
|
31264
|
+
|
|
31265
|
+
self.done = function() {
|
|
31266
|
+
if (opts.debug_points) {
|
|
31267
|
+
makeDebugPoints();
|
|
31268
|
+
}
|
|
31269
|
+
var ring = path.reverse().concat(buffer);
|
|
31270
|
+
if (ring.length < 3) {
|
|
31271
|
+
error('Defective buffer ring:', ring);
|
|
31272
|
+
}
|
|
31273
|
+
ring.push(ring[0]);
|
|
31274
|
+
init();
|
|
31275
|
+
return ring;
|
|
31276
|
+
};
|
|
31277
|
+
|
|
31278
|
+
return self;
|
|
31279
|
+
}
|
|
31280
|
+
|
|
31281
|
+
function pointsAreSame(a, b) {
|
|
31282
|
+
return a[0] === b[0] && a[1] === b[1];
|
|
31283
|
+
}
|
|
31284
|
+
|
|
31285
|
+
// Exclude segments with non-intersecting bounding boxes before
|
|
31286
|
+
// calling intersection function
|
|
31287
|
+
// Possibly slightly faster than direct call... not worth it?
|
|
31288
|
+
function bufferIntersection$2(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
31289
|
+
if (ax < cx && ax < dx && bx < cx && bx < dx ||
|
|
31290
|
+
ax > cx && ax > dx && bx > cx && bx > dx ||
|
|
31291
|
+
ay < cy && ay < dy && by < cy && by < dy ||
|
|
31292
|
+
ay > cy && ay > dy && by > cy && by > dy) return null;
|
|
31293
|
+
return segmentIntersection(ax, ay, bx, by, cx, cy, dx, dy);
|
|
31294
|
+
}
|
|
31295
|
+
|
|
31296
|
+
// Returns a function for generating GeoJSON MultiPolygon geometries
|
|
31297
|
+
function getPolylineBufferMaker$2(arcs, geod, getBearing, opts) {
|
|
31298
|
+
// var sliceLen = opts.slice_length || Infinity;
|
|
31299
|
+
var segsPerQuadrant = opts.arc_quality >= 2 ? opts.arc_quality : 12;
|
|
31300
|
+
var capStyle = opts.cap_style || 'round'; // expect 'round' or 'flat'
|
|
31301
|
+
var pathIter = new ShapeIter(arcs);
|
|
31302
|
+
var builder = new BufferBuilder(opts);
|
|
31303
|
+
|
|
31304
|
+
return function makeBufferGeoJSON(shape, distance) {
|
|
31305
|
+
var rings = [];
|
|
31306
|
+
(shape || []).forEach(function(path, i) {
|
|
31307
|
+
var pathRings = makeSinglePathRings(path, distance);
|
|
31308
|
+
rings = rings.concat(pathRings);
|
|
31309
|
+
});
|
|
31310
|
+
if (rings.length === 0) return null;
|
|
31311
|
+
var feat = {
|
|
31312
|
+
type: 'Feature',
|
|
31313
|
+
properties: null,
|
|
31314
|
+
geometry: {
|
|
31315
|
+
type: 'MultiPolygon',
|
|
31316
|
+
coordinates: rings.map(ring => [ring]) // to Polygon format
|
|
31317
|
+
}
|
|
31318
|
+
};
|
|
31319
|
+
if (opts.debug_points) {
|
|
31320
|
+
return [feat].concat(builder.getDebugPoints());
|
|
31321
|
+
}
|
|
31322
|
+
return feat;
|
|
31323
|
+
};
|
|
31324
|
+
|
|
31325
|
+
// each path may be converted into multiple buffer rings, which later
|
|
31326
|
+
// need to be dissolved
|
|
31327
|
+
function makeSinglePathRings(pathArcs, dist) {
|
|
31328
|
+
var revPathArcs;
|
|
31329
|
+
var rings = [];
|
|
31330
|
+
if (!opts.right || opts.left) {
|
|
31331
|
+
rings = rings.concat(makeLeftBufferRings(pathArcs, dist));
|
|
31332
|
+
}
|
|
31333
|
+
if (!opts.left || opts.right) {
|
|
31334
|
+
revPathArcs = reversePath(pathArcs.concat());
|
|
31335
|
+
rings = rings.concat(makeLeftBufferRings(revPathArcs, dist));
|
|
31336
|
+
}
|
|
31337
|
+
return rings;
|
|
31338
|
+
}
|
|
31339
|
+
|
|
31340
|
+
function makeLeftBufferRings(path, dist) {
|
|
31341
|
+
var rings = [];
|
|
31342
|
+
var x0, y0, x1, y1, x2, y2; // path traversal coords
|
|
31343
|
+
var p1, p2; // extruded points
|
|
31344
|
+
var bearing1, bearing2, prevBearing, joinAngle;
|
|
31345
|
+
var firstBearing;
|
|
31346
|
+
var i = 0;
|
|
31347
|
+
pathIter.init(path);
|
|
31348
|
+
|
|
31349
|
+
if (pathIter.hasNext()) {
|
|
31350
|
+
x0 = x2 = pathIter.x;
|
|
31351
|
+
y0 = y2 = pathIter.y;
|
|
31352
|
+
i++;
|
|
31353
|
+
}
|
|
31354
|
+
|
|
31355
|
+
while (pathIter.hasNext()) {
|
|
31356
|
+
// TODO: use a tolerance
|
|
31357
|
+
if (pathIter.x === x2 && pathIter.y === y2) {
|
|
31358
|
+
debug("skipping a duplicate point");
|
|
31359
|
+
continue;
|
|
31360
|
+
}
|
|
31361
|
+
x1 = x2;
|
|
31362
|
+
y1 = y2;
|
|
31363
|
+
x2 = pathIter.x;
|
|
31364
|
+
y2 = pathIter.y;
|
|
31365
|
+
|
|
31366
|
+
// calculate bearing at both segment points
|
|
31367
|
+
// TODO: no need to calculate twice with planar coordinates
|
|
31368
|
+
prevBearing = bearing2;
|
|
31369
|
+
bearing1 = getBearing(x1, y1, x2, y2);
|
|
31370
|
+
bearing2 = getBearing(x2, y2, x1, y1) - 180;
|
|
31371
|
+
p1 = geod(x1, y1, bearing1 - 90, dist);
|
|
31372
|
+
p2 = geod(x2, y2, bearing2 - 90, dist);
|
|
31373
|
+
|
|
31374
|
+
if (i == 1) {
|
|
31375
|
+
firstBearing = bearing1;
|
|
31376
|
+
builder.addPathVertex([x1, y1]);
|
|
31377
|
+
} else {
|
|
31378
|
+
joinAngle = getJoinAngle(prevBearing, bearing1);
|
|
31379
|
+
}
|
|
31380
|
+
|
|
31381
|
+
builder.addPathVertex([x2, y2]);
|
|
31382
|
+
|
|
31383
|
+
if (i > 1 && joinAngle > 0) {
|
|
31384
|
+
builder.addBufferVertices(makeRoundJoin(x1, y1, prevBearing - 90, joinAngle, dist));
|
|
31385
|
+
}
|
|
31386
|
+
|
|
31387
|
+
builder.addBufferVertex(p1, joinAngle < 0);
|
|
31388
|
+
builder.addBufferVertex(p2);
|
|
31389
|
+
|
|
31390
|
+
// TODO: restore slicing?
|
|
31391
|
+
// if (center.length - 1 >= sliceLen) {
|
|
31392
|
+
// builder.done(rings);
|
|
31393
|
+
// }
|
|
31394
|
+
|
|
31395
|
+
i++;
|
|
31396
|
+
}
|
|
31397
|
+
|
|
31398
|
+
if (x2 == x0 && y2 == y0) { // closed path
|
|
31399
|
+
// add join to finish closed path
|
|
31400
|
+
// TODO - figure out which bearing to use
|
|
31401
|
+
joinAngle = getJoinAngle(bearing2, firstBearing);
|
|
31402
|
+
if (joinAngle > 0) {
|
|
31403
|
+
builder.addBufferVertices(makeFinalJoin(x2, y2, bearing1 - 90, joinAngle, dist));
|
|
31404
|
+
}
|
|
31405
|
+
} else if (capStyle == 'round') { // open path
|
|
31406
|
+
// add a cap to finish open path
|
|
31407
|
+
builder.addBufferVertices(makeRoundCap(x2, y2, bearing2 - 90, dist));
|
|
31408
|
+
}
|
|
31409
|
+
|
|
31410
|
+
rings.push(builder.done());
|
|
31411
|
+
return rings;
|
|
31412
|
+
}
|
|
31413
|
+
|
|
31414
|
+
// function extendArray(arr, arr2) {
|
|
31415
|
+
// arr2.reverse();
|
|
31416
|
+
// while(arr2.length > 0) arr.push(arr2.pop());
|
|
31417
|
+
// }
|
|
31418
|
+
|
|
31419
|
+
function makeFinalJoin(x, y, direction, angle, dist) {
|
|
31420
|
+
var points = makeRoundJoin(x, y, direction, angle, dist);
|
|
31421
|
+
points.push(geod(x, y, direction + angle, dist));
|
|
31422
|
+
return points;
|
|
31423
|
+
}
|
|
31424
|
+
|
|
31425
|
+
function makeRoundCap(x, y, startDir, dist) {
|
|
31426
|
+
var points = makeRoundJoin(x, y, startDir, 180, dist);
|
|
31427
|
+
points.push(geod(x, y, startDir + 180, dist)); // add final vertex
|
|
31428
|
+
return points;
|
|
31429
|
+
}
|
|
31430
|
+
|
|
31431
|
+
// get interior vertices of an interpolated CW arc
|
|
31432
|
+
function makeRoundJoin(cx, cy, startBearing, arcAngle, dist) {
|
|
31433
|
+
var points = [];
|
|
31434
|
+
var increment = 90 / segsPerQuadrant;
|
|
31435
|
+
var angle = increment;
|
|
31436
|
+
while (angle < arcAngle) {
|
|
31437
|
+
points.push(geod(cx, cy, startBearing + angle, dist));
|
|
31438
|
+
angle += increment;
|
|
31439
|
+
}
|
|
31440
|
+
return points;
|
|
31441
|
+
}
|
|
31442
|
+
|
|
31443
|
+
// get angle between two extruded segments in degrees
|
|
31444
|
+
// positive angle means join in convex (range: 0-180 degrees)
|
|
31445
|
+
// negative angle means join is concave (range: -180-0 degrees)
|
|
31446
|
+
function getJoinAngle(direction1, direction2) {
|
|
31447
|
+
var delta = direction2 - direction1;
|
|
31448
|
+
if (delta > 180) {
|
|
31449
|
+
delta -= 360;
|
|
31450
|
+
}
|
|
31451
|
+
if (delta < -180) {
|
|
31452
|
+
delta += 360;
|
|
31453
|
+
}
|
|
31454
|
+
return delta;
|
|
31455
|
+
}
|
|
31456
|
+
|
|
31457
|
+
}
|
|
31458
|
+
|
|
31459
|
+
// Returns a function for generating GeoJSON MultiPolygon geometries
|
|
31460
|
+
function getPolylineBufferMaker$1(arcs, geod, getBearing, opts) {
|
|
31461
|
+
var sliceLen = opts.slice_length || Infinity;
|
|
31462
|
+
var backtrackSteps = opts.backtrack >= 0 ? opts.backtrack : 100;
|
|
31463
|
+
var segsPerQuadrant = opts.arc_quality >= 2 ? opts.arc_quality : 12;
|
|
31464
|
+
var capStyle = opts.cap_style || 'round'; // expect 'round' or 'flat'
|
|
31465
|
+
var pathIter = new ShapeIter(arcs);
|
|
31466
|
+
var left, center, rings;
|
|
31467
|
+
|
|
31468
|
+
return function makeBufferGeoJSON(shape, distance) {
|
|
31469
|
+
var rings = [];
|
|
31470
|
+
(shape || []).forEach(function(path, i) {
|
|
31471
|
+
var pathRings = makeSinglePathRings(path, distance);
|
|
31472
|
+
rings = rings.concat(pathRings);
|
|
31473
|
+
});
|
|
31474
|
+
if (rings.length === 0) return null;
|
|
31475
|
+
return {
|
|
31476
|
+
type: 'MultiPolygon',
|
|
31477
|
+
coordinates: rings.map(ring => [ring]) // to Polygon format
|
|
31478
|
+
};
|
|
31479
|
+
};
|
|
31480
|
+
|
|
31481
|
+
// each path may be converted into multiple buffer rings, which later
|
|
31482
|
+
// need to be dissolved
|
|
31483
|
+
function makeSinglePathRings(pathArcs, dist) {
|
|
31484
|
+
var revPathArcs;
|
|
31485
|
+
var rings = [];
|
|
31486
|
+
if (!opts.right || opts.left) {
|
|
31487
|
+
rings = rings.concat(makeLeftBufferRings(pathArcs, dist));
|
|
31488
|
+
}
|
|
31489
|
+
if (!opts.left || opts.right) {
|
|
31490
|
+
revPathArcs = reversePath(pathArcs.concat());
|
|
31491
|
+
rings = rings.concat(makeLeftBufferRings(revPathArcs, dist));
|
|
31492
|
+
}
|
|
31493
|
+
return rings;
|
|
31494
|
+
}
|
|
31495
|
+
|
|
31496
|
+
function makeLeftBufferRings(path, dist) {
|
|
31497
|
+
left = [];
|
|
31498
|
+
center = [];
|
|
31499
|
+
rings = [];
|
|
31500
|
+
var x0, y0, x1, y1, x2, y2; // path traversal coords
|
|
31501
|
+
var p1, p2; // extruded points
|
|
31502
|
+
var prevP;
|
|
31503
|
+
var bearing1, bearing2, prevBearing, joinAngle;
|
|
31504
|
+
var firstBearing;
|
|
31505
|
+
var i = 0;
|
|
31506
|
+
pathIter.init(path);
|
|
31507
|
+
|
|
31508
|
+
if (pathIter.hasNext()) {
|
|
31509
|
+
x0 = x2 = pathIter.x;
|
|
31510
|
+
y0 = y2 = pathIter.y;
|
|
31511
|
+
i++;
|
|
31512
|
+
}
|
|
31513
|
+
|
|
31514
|
+
while (pathIter.hasNext()) {
|
|
31515
|
+
// TODO: use a tolerance
|
|
31516
|
+
if (pathIter.x === x2 && pathIter.y === y2) {
|
|
31517
|
+
debug("skipping a duplicate point");
|
|
31518
|
+
continue;
|
|
31519
|
+
}
|
|
31520
|
+
x1 = x2;
|
|
31521
|
+
y1 = y2;
|
|
31522
|
+
x2 = pathIter.x;
|
|
31523
|
+
y2 = pathIter.y;
|
|
31524
|
+
|
|
31525
|
+
// calculate bearing at both segment points
|
|
31526
|
+
// TODO: no need to calculate twice with planar coordinates
|
|
31527
|
+
prevBearing = bearing2;
|
|
31528
|
+
bearing1 = getBearing(x1, y1, x2, y2);
|
|
31529
|
+
bearing2 = getBearing(x2, y2, x1, y1) - 180;
|
|
31530
|
+
// extrude current segment to the left
|
|
31531
|
+
prevP = p2;
|
|
31532
|
+
p1 = geod(x1, y1, bearing1 - 90, dist);
|
|
31533
|
+
p2 = geod(x2, y2, bearing2 - 90, dist);
|
|
31534
|
+
|
|
31535
|
+
if (i == 1) {
|
|
31536
|
+
firstBearing = bearing1;
|
|
31537
|
+
} else {
|
|
31538
|
+
joinAngle = getJoinAngle(prevBearing, bearing1);
|
|
31539
|
+
}
|
|
31540
|
+
|
|
31541
|
+
// extend center coords (i.e. original path)
|
|
31542
|
+
if (center.length == 0) {
|
|
31543
|
+
center.push([x1, y1]);
|
|
31544
|
+
}
|
|
31545
|
+
center.push([x2, y2]);
|
|
31546
|
+
|
|
31547
|
+
// if (veryCloseToPrevPoint(left, p1[0], p1[1])) {
|
|
31548
|
+
// console.log("VERY CLOSE")
|
|
31549
|
+
// // skip first point
|
|
31550
|
+
// addBufferVertex(p2);
|
|
31551
|
+
// }
|
|
31552
|
+
|
|
31553
|
+
if (i > 1 && joinAngle > 0) {
|
|
31554
|
+
if (prevP) {
|
|
31555
|
+
// start join from last extruded vertex of previous buffer slice
|
|
31556
|
+
addBufferVertex(prevP);
|
|
31557
|
+
}
|
|
31558
|
+
makeRoundJoin(x1, y1, prevBearing - 90, joinAngle, dist).forEach(addBufferVertex);
|
|
31559
|
+
addBufferVertex(p1);
|
|
31560
|
+
addBufferVertex(p2);
|
|
31561
|
+
// left.push(p1, p2)
|
|
31562
|
+
} else {
|
|
31563
|
+
// left.push(p1, p2)
|
|
31564
|
+
addBufferVertex(p1);
|
|
31565
|
+
addBufferVertex(p2);
|
|
31566
|
+
}
|
|
31567
|
+
|
|
31568
|
+
// TODO: finish
|
|
31569
|
+
if (center.length - 1 >= sliceLen) {
|
|
31570
|
+
finishRing();
|
|
31571
|
+
}
|
|
31572
|
+
i++;
|
|
31573
|
+
}
|
|
31574
|
+
|
|
31575
|
+
if (center.length > 1) {
|
|
31576
|
+
finishRing();
|
|
31577
|
+
}
|
|
31578
|
+
|
|
31579
|
+
if (x2 == x0 && y2 == y0) { // closed path
|
|
31580
|
+
// add join to finish closed path
|
|
31581
|
+
// TODO - figure out which bearing to use
|
|
31582
|
+
joinAngle = getJoinAngle(bearing2, firstBearing);
|
|
31583
|
+
if (joinAngle > 0) {
|
|
31584
|
+
appendJoinToRing(rings[rings.length-1], x2, y2, bearing1 - 90, joinAngle, dist);
|
|
31585
|
+
}
|
|
31586
|
+
} else { // open path
|
|
31587
|
+
// add a cap to finish open path
|
|
31588
|
+
// left.push.apply(left, makeCap(x2, y2, bearing, dist));
|
|
31589
|
+
appendCapToRing(rings[rings.length-1], x2, y2, bearing2, dist);
|
|
31590
|
+
}
|
|
31591
|
+
|
|
31592
|
+
return rings;
|
|
31593
|
+
}
|
|
31594
|
+
|
|
31595
|
+
function finishRing() {
|
|
31596
|
+
if (center.length < 2) {
|
|
31597
|
+
debug('defective path, skipping');
|
|
31598
|
+
return;
|
|
31599
|
+
}
|
|
31600
|
+
var ring = center.reverse().concat(left);
|
|
31601
|
+
ring.push(ring[0]);
|
|
31602
|
+
|
|
31603
|
+
// Start next partial (if there is one)
|
|
31604
|
+
left = [];
|
|
31605
|
+
center = [];
|
|
31606
|
+
rings.push(ring);
|
|
31607
|
+
}
|
|
31608
|
+
|
|
31609
|
+
// function extendArray(arr, arr2) {
|
|
31610
|
+
// arr2.reverse();
|
|
31611
|
+
// while(arr2.length > 0) arr.push(arr2.pop());
|
|
31612
|
+
// }
|
|
31613
|
+
|
|
31614
|
+
function appendJoinToRing(ring, x, y, direction, angle, dist) {
|
|
31615
|
+
var p1 = ring.pop();
|
|
31616
|
+
var coords = makeRoundJoin(x, y, direction, angle, dist);
|
|
31617
|
+
ring.push.apply(ring, coords);
|
|
31618
|
+
ring.push(geod(x, y, direction + angle, dist));
|
|
31619
|
+
ring.push(p1);
|
|
31620
|
+
}
|
|
31621
|
+
|
|
31622
|
+
function appendCapToRing(ring, x, y, direction, dist) {
|
|
31623
|
+
if (capStyle == 'flat' || ring.length < 4) {
|
|
31624
|
+
return;
|
|
31625
|
+
}
|
|
31626
|
+
var p1 = ring.pop();
|
|
31627
|
+
var coords = makeRoundCap(x, y, direction - 90, dist);
|
|
31628
|
+
ring.push.apply(ring, coords);
|
|
31629
|
+
ring.push(p1);
|
|
31630
|
+
}
|
|
31631
|
+
|
|
31632
|
+
function makeRoundCap(x, y, startDir, dist) {
|
|
31633
|
+
var points = makeRoundJoin(x, y, startDir, 180, dist);
|
|
31634
|
+
points.push(geod(x, y, startDir + 180, dist)); // add final vertex
|
|
31635
|
+
return points;
|
|
31636
|
+
}
|
|
31637
|
+
|
|
31638
|
+
// get interior vertices of an interpolated CW arc
|
|
31639
|
+
function makeRoundJoin(cx, cy, startBearing, arcAngle, dist) {
|
|
31640
|
+
var points = [];
|
|
31641
|
+
var increment = 90 / segsPerQuadrant;
|
|
31642
|
+
var angle = increment;
|
|
31643
|
+
while (angle < arcAngle) {
|
|
31644
|
+
points.push(geod(cx, cy, startBearing + angle, dist));
|
|
31645
|
+
angle += increment;
|
|
31646
|
+
}
|
|
31647
|
+
return points;
|
|
31648
|
+
}
|
|
31649
|
+
|
|
31650
|
+
// get angle between two extruded segments in degrees
|
|
31651
|
+
// positive angle means join in convex (range: 0-180 degrees)
|
|
31652
|
+
// negative angle means join is concave (range: -180-0 degrees)
|
|
31653
|
+
function getJoinAngle(direction1, direction2) {
|
|
31654
|
+
var delta = direction2 - direction1;
|
|
31655
|
+
if (delta > 180) {
|
|
31656
|
+
delta -= 360;
|
|
31657
|
+
}
|
|
31658
|
+
if (delta < -180) {
|
|
31659
|
+
delta += 360;
|
|
31660
|
+
}
|
|
31661
|
+
return delta;
|
|
31662
|
+
}
|
|
31663
|
+
|
|
31664
|
+
|
|
31665
|
+
function addBufferVertex(d) {
|
|
31666
|
+
var arr = left;
|
|
31667
|
+
var a, b, c, hit;
|
|
31668
|
+
// c is the start point of the segment formed by appending point d to the polyline.
|
|
31669
|
+
c = arr[arr.length - 1];
|
|
31670
|
+
for (var i=0, idx = arr.length - 3; i < backtrackSteps && idx >= 0; i++, idx--) {
|
|
31671
|
+
a = arr[idx];
|
|
31672
|
+
b = arr[idx + 1];
|
|
31673
|
+
// TODO: consider using a geodetic intersection function for lat-long datasets
|
|
31674
|
+
hit = bufferIntersection$1(a[0], a[1], b[0], b[1], c[0], c[1], d[0], d[1]);
|
|
31675
|
+
// TODO: disregard hits caused by oxbows
|
|
31676
|
+
if (hit) {
|
|
31677
|
+
// TODO: handle collinear segments
|
|
31678
|
+
// if (hit.length != 2) console.log('COLLINEAR', hit)
|
|
31679
|
+
// segments intersect -- replace two internal segment endpoints with xx point
|
|
31680
|
+
while (arr.length > idx + 1) arr.pop();
|
|
31681
|
+
appendPoint(arr, hit);
|
|
31682
|
+
c = hit; // update starting point of the newly added segment
|
|
31683
|
+
}
|
|
31684
|
+
}
|
|
31685
|
+
appendPoint(arr, d);
|
|
31686
|
+
}
|
|
31687
|
+
|
|
31688
|
+
}
|
|
31689
|
+
|
|
31690
|
+
// Test if two points are within a snapping tolerance
|
|
31691
|
+
// TODO: calculate the tolerance more sensibly
|
|
31692
|
+
function veryClose(x1, y1, x2, y2, tol) {
|
|
31693
|
+
var dist = geom.distance2D(x1, y1, x2, y2);
|
|
31694
|
+
return dist < tol;
|
|
31695
|
+
}
|
|
31696
|
+
|
|
31697
|
+
function appendPoint(arr, p) {
|
|
31698
|
+
var prev = arr[arr.length - 1];
|
|
31699
|
+
if (!prev || !veryClose(prev[0], prev[1], p[0], p[1], 1e-10)) {
|
|
31700
|
+
arr.push(p);
|
|
31701
|
+
}
|
|
31702
|
+
}
|
|
31703
|
+
|
|
31704
|
+
// Exclude segments with non-intersecting bounding boxes before
|
|
31705
|
+
// calling intersection function
|
|
31706
|
+
// Possibly slightly faster than direct call... not worth it?
|
|
31707
|
+
function bufferIntersection$1(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
31708
|
+
if (ax < cx && ax < dx && bx < cx && bx < dx ||
|
|
31709
|
+
ax > cx && ax > dx && bx > cx && bx > dx ||
|
|
31710
|
+
ay < cy && ay < dy && by < cy && by < dy ||
|
|
31711
|
+
ay > cy && ay > dy && by > cy && by > dy) return null;
|
|
31712
|
+
return geom.segmentIntersection(ax, ay, bx, by, cx, cy, dx, dy);
|
|
31713
|
+
}
|
|
32013
31714
|
|
|
32014
31715
|
// Returns a function for generating GeoJSON geometries (MultiLineString or MultiPolygon)
|
|
32015
31716
|
function getPolylineBufferMaker(arcs, geod, getBearing, opts) {
|
|
@@ -32203,28 +31904,6 @@ ${svg}
|
|
|
32203
31904
|
};
|
|
32204
31905
|
}
|
|
32205
31906
|
|
|
32206
|
-
function addBufferVertex(arr, d, maxBacktrack) {
|
|
32207
|
-
var a, b, c, hit;
|
|
32208
|
-
for (var i=0, idx = arr.length - 3; i<maxBacktrack && idx >= 0; i++, idx--) {
|
|
32209
|
-
c = arr[arr.length - 1];
|
|
32210
|
-
a = arr[idx];
|
|
32211
|
-
b = arr[idx + 1];
|
|
32212
|
-
// TODO: consider using a geodetic intersection function for lat-long datasets
|
|
32213
|
-
hit = bufferIntersection(a[0], a[1], b[0], b[1], c[0], c[1], d[0], d[1]);
|
|
32214
|
-
if (hit) {
|
|
32215
|
-
// TODO: handle collinear segments
|
|
32216
|
-
if (hit.length != 2) ;
|
|
32217
|
-
// segments intersect -- replace two internal segment endpoints with xx point
|
|
32218
|
-
while (arr.length > idx + 1) arr.pop();
|
|
32219
|
-
// TODO: check proximity of hit to several points
|
|
32220
|
-
arr.push(hit);
|
|
32221
|
-
}
|
|
32222
|
-
}
|
|
32223
|
-
|
|
32224
|
-
// TODO: check proximity to previous point
|
|
32225
|
-
arr.push(d); // add new point
|
|
32226
|
-
}
|
|
32227
|
-
|
|
32228
31907
|
// Exclude segments with non-intersecting bounding boxes before
|
|
32229
31908
|
// calling intersection function
|
|
32230
31909
|
// Possibly slightly faster than direct call... not worth it?
|
|
@@ -32236,302 +31915,6 @@ ${svg}
|
|
|
32236
31915
|
return geom.segmentIntersection(ax, ay, bx, by, cx, cy, dx, dy);
|
|
32237
31916
|
}
|
|
32238
31917
|
|
|
32239
|
-
var PathBuffer = /*#__PURE__*/Object.freeze({
|
|
32240
|
-
__proto__: null,
|
|
32241
|
-
addBufferVertex: addBufferVertex,
|
|
32242
|
-
bufferIntersection: bufferIntersection,
|
|
32243
|
-
getPolylineBufferMaker: getPolylineBufferMaker
|
|
32244
|
-
});
|
|
32245
|
-
|
|
32246
|
-
function getPolylineBufferMaker2(arcs, geod, getBearing, opts) {
|
|
32247
|
-
var makeLeftBuffer = getPathBufferMaker2(arcs, geod, getBearing, opts);
|
|
32248
|
-
var geomType = opts.geometry_type;
|
|
32249
|
-
|
|
32250
|
-
function polygonCoords(ring) {
|
|
32251
|
-
return [ring];
|
|
32252
|
-
}
|
|
32253
|
-
|
|
32254
|
-
function needLeftBuffer(path, arcs) {
|
|
32255
|
-
if (geomType == 'polyline') {
|
|
32256
|
-
return opts.type != 'right';
|
|
32257
|
-
}
|
|
32258
|
-
// assume polygon type
|
|
32259
|
-
if (opts.type == 'outer') {
|
|
32260
|
-
return geom.getPathWinding(path, arcs) == 1;
|
|
32261
|
-
}
|
|
32262
|
-
if (opts.type == 'inner') {
|
|
32263
|
-
return geom.getPathWinding(path, arcs) == -1;
|
|
32264
|
-
}
|
|
32265
|
-
return true;
|
|
32266
|
-
}
|
|
32267
|
-
|
|
32268
|
-
function needRightBuffer() {
|
|
32269
|
-
return geomType == 'polyline' && opts.type != 'left';
|
|
32270
|
-
}
|
|
32271
|
-
|
|
32272
|
-
function makeBufferParts(pathArcs, dist) {
|
|
32273
|
-
var leftPartials, rightPartials, parts, revPathArcs;
|
|
32274
|
-
|
|
32275
|
-
if (needLeftBuffer(pathArcs, arcs)) {
|
|
32276
|
-
leftPartials = makeLeftBuffer(pathArcs, dist);
|
|
32277
|
-
}
|
|
32278
|
-
if (needRightBuffer()) {
|
|
32279
|
-
revPathArcs = reversePath(pathArcs.concat());
|
|
32280
|
-
rightPartials = makeLeftBuffer(revPathArcs, dist);
|
|
32281
|
-
}
|
|
32282
|
-
parts = (leftPartials || []).concat(rightPartials || []);
|
|
32283
|
-
return parts.map(polygonCoords);
|
|
32284
|
-
}
|
|
32285
|
-
|
|
32286
|
-
// Returns a GeoJSON Geometry (MultiLineString or MultiPolygon) or null
|
|
32287
|
-
return function(shape, dist) {
|
|
32288
|
-
var geom = {
|
|
32289
|
-
type: 'MultiPolygon',
|
|
32290
|
-
coordinates: []
|
|
32291
|
-
};
|
|
32292
|
-
for (var i=0; i<shape.length; i++) {
|
|
32293
|
-
geom.coordinates = geom.coordinates.concat(makeBufferParts(shape[i], dist));
|
|
32294
|
-
}
|
|
32295
|
-
return geom.coordinates.length == 0 ? null : geom;
|
|
32296
|
-
};
|
|
32297
|
-
}
|
|
32298
|
-
|
|
32299
|
-
function getPathBufferMaker2(arcs, geod, getBearing, opts) {
|
|
32300
|
-
var backtrackSteps = opts.backtrack >= 0 ? opts.backtrack : 50;
|
|
32301
|
-
var pathIter = new ShapeIter(arcs);
|
|
32302
|
-
// var capStyle = opts.cap_style || 'round'; // expect 'round' or 'flat'
|
|
32303
|
-
var partials, left, center;
|
|
32304
|
-
var bounds;
|
|
32305
|
-
// TODO: implement other join styles than round
|
|
32306
|
-
|
|
32307
|
-
// function updateTolerance(dist) {
|
|
32308
|
-
|
|
32309
|
-
// }
|
|
32310
|
-
|
|
32311
|
-
function addRoundJoin(x, y, startDir, angle, dist) {
|
|
32312
|
-
var increment = 10;
|
|
32313
|
-
var endDir = startDir + angle;
|
|
32314
|
-
var dir = startDir + increment;
|
|
32315
|
-
while (dir < endDir) {
|
|
32316
|
-
addBufferVertex(geod(x, y, dir, dist));
|
|
32317
|
-
dir += increment;
|
|
32318
|
-
}
|
|
32319
|
-
}
|
|
32320
|
-
|
|
32321
|
-
// function addRoundJoin2(arr, x, y, startDir, angle, dist) {
|
|
32322
|
-
// var increment = 10;
|
|
32323
|
-
// var endDir = startDir + angle;
|
|
32324
|
-
// var dir = startDir + increment;
|
|
32325
|
-
// while (dir < endDir) {
|
|
32326
|
-
// addBufferVertex(arr, geod(x, y, dir, dist));
|
|
32327
|
-
// dir += increment;
|
|
32328
|
-
// }
|
|
32329
|
-
// }
|
|
32330
|
-
|
|
32331
|
-
// Test if two points are within a snapping tolerance
|
|
32332
|
-
// TODO: calculate the tolerance more sensibly
|
|
32333
|
-
function veryClose(x1, y1, x2, y2, tol) {
|
|
32334
|
-
var dist = geom.distance2D(x1, y1, x2, y2);
|
|
32335
|
-
return dist < tol;
|
|
32336
|
-
}
|
|
32337
|
-
|
|
32338
|
-
function veryCloseToPrevPoint(arr, x, y) {
|
|
32339
|
-
var prev = arr[arr.length - 1];
|
|
32340
|
-
return veryClose(prev[0], prev[1], x, y, 0.000001);
|
|
32341
|
-
}
|
|
32342
|
-
|
|
32343
|
-
function appendPoint(arr, p) {
|
|
32344
|
-
var prev = arr[arr.length - 1];
|
|
32345
|
-
if (!veryClose(prev[0], prev[1], p[0], p[1], 1e-10)) {
|
|
32346
|
-
arr.push(p);
|
|
32347
|
-
}
|
|
32348
|
-
}
|
|
32349
|
-
|
|
32350
|
-
// function makeCap(x, y, direction, dist) {
|
|
32351
|
-
// if (capStyle == 'flat') {
|
|
32352
|
-
// return [[x, y]];
|
|
32353
|
-
// }
|
|
32354
|
-
// return makeRoundCap(x, y, direction, dist);
|
|
32355
|
-
// }
|
|
32356
|
-
|
|
32357
|
-
// function makeRoundCap(x, y, segmentDir, dist) {
|
|
32358
|
-
// var points = [];
|
|
32359
|
-
// var increment = 10;
|
|
32360
|
-
// var startDir = segmentDir - 90;
|
|
32361
|
-
// var angle = increment;
|
|
32362
|
-
// while (angle < 180) {
|
|
32363
|
-
// points.push(geod(x, y, startDir + angle, dist));
|
|
32364
|
-
// angle += increment;
|
|
32365
|
-
// }
|
|
32366
|
-
// return points;
|
|
32367
|
-
// }
|
|
32368
|
-
|
|
32369
|
-
// get angle between two extruded segments in degrees
|
|
32370
|
-
// positive angle means join in convex (range: 0-180 degrees)
|
|
32371
|
-
// negative angle means join is concave (range: -180-0 degrees)
|
|
32372
|
-
function getJoinAngle(direction1, direction2) {
|
|
32373
|
-
var delta = direction2 - direction1;
|
|
32374
|
-
if (delta > 180) {
|
|
32375
|
-
delta -= 360;
|
|
32376
|
-
}
|
|
32377
|
-
if (delta < -180) {
|
|
32378
|
-
delta += 360;
|
|
32379
|
-
}
|
|
32380
|
-
return delta;
|
|
32381
|
-
}
|
|
32382
|
-
|
|
32383
|
-
|
|
32384
|
-
// TODO: handle polygon holes
|
|
32385
|
-
function addBufferVertex(d) {
|
|
32386
|
-
var arr = left;
|
|
32387
|
-
var a, b, c, c0, hit;
|
|
32388
|
-
// c is the start point of the segment formed by appending point d to the polyline.
|
|
32389
|
-
c0 = c = arr[arr.length - 1];
|
|
32390
|
-
for (var i=0, idx = arr.length - 3; idx >= 0; i++, idx--) {
|
|
32391
|
-
a = arr[idx];
|
|
32392
|
-
b = arr[idx + 1];
|
|
32393
|
-
// TODO: consider using a geodetic intersection function for lat-long datasets
|
|
32394
|
-
hit = bufferIntersection(a[0], a[1], b[0], b[1], c[0], c[1], d[0], d[1]);
|
|
32395
|
-
if (hit) {
|
|
32396
|
-
if (segmentTurn(a, b, c, d) == 1) {
|
|
32397
|
-
// interpretation: segment cd crosses segment ab from outside to inside
|
|
32398
|
-
// the buffer -- we need to start a new partial; otherwise,
|
|
32399
|
-
// the following code would likely remove a loop representing
|
|
32400
|
-
// an oxbow-type hole in the buffer.
|
|
32401
|
-
//
|
|
32402
|
-
finishPartial();
|
|
32403
|
-
break;
|
|
32404
|
-
}
|
|
32405
|
-
// TODO: handle collinear segments (consider creating new partial)
|
|
32406
|
-
// if (hit.length != 2) console.log('COLLINEAR', hit)
|
|
32407
|
-
|
|
32408
|
-
// segments intersect, indicating a spurious loop: remove the loop and
|
|
32409
|
-
// replace the endpoints of the intersecting segments with the intersection point.
|
|
32410
|
-
while (arr.length > idx + 1) arr.pop();
|
|
32411
|
-
appendPoint(arr, hit);
|
|
32412
|
-
c = hit; // update starting point of the newly added segment
|
|
32413
|
-
}
|
|
32414
|
-
|
|
32415
|
-
// Maintain a bounding box around vertices before the backtrack limit.
|
|
32416
|
-
// If the latest segment intersects this bounding box, there could be a self-
|
|
32417
|
-
// intersection -- start a new partial to prevent self-intersection.
|
|
32418
|
-
//
|
|
32419
|
-
if (i >= backtrackSteps) {
|
|
32420
|
-
if (!bounds) {
|
|
32421
|
-
bounds = new Bounds();
|
|
32422
|
-
bounds.mergePoint(a[0], a[1]);
|
|
32423
|
-
}
|
|
32424
|
-
bounds.mergePoint(b[0], b[1]);
|
|
32425
|
-
if (testSegmentBoundsIntersection(c0, d, bounds)) {
|
|
32426
|
-
finishPartial();
|
|
32427
|
-
}
|
|
32428
|
-
break;
|
|
32429
|
-
}
|
|
32430
|
-
}
|
|
32431
|
-
|
|
32432
|
-
appendPoint(arr, d);
|
|
32433
|
-
}
|
|
32434
|
-
|
|
32435
|
-
function finishPartial() {
|
|
32436
|
-
// Get endpoints of the two polylines, for starting the next partial
|
|
32437
|
-
var leftEP = left[left.length - 1];
|
|
32438
|
-
var centerEP = center[center.length - 1];
|
|
32439
|
-
|
|
32440
|
-
// Make a polygon ring
|
|
32441
|
-
var ring = [];
|
|
32442
|
-
extendArray(ring, left);
|
|
32443
|
-
center.reverse();
|
|
32444
|
-
extendArray(ring, center);
|
|
32445
|
-
ring.push(ring[0]); // close ring
|
|
32446
|
-
partials.push(ring);
|
|
32447
|
-
|
|
32448
|
-
// Start next partial
|
|
32449
|
-
left.push(leftEP);
|
|
32450
|
-
center.push(centerEP);
|
|
32451
|
-
|
|
32452
|
-
// clear bbox
|
|
32453
|
-
// bbox = null;
|
|
32454
|
-
}
|
|
32455
|
-
|
|
32456
|
-
function extendArray(arr, arr2) {
|
|
32457
|
-
arr2.reverse();
|
|
32458
|
-
while(arr2.length > 0) arr.push(arr2.pop());
|
|
32459
|
-
}
|
|
32460
|
-
|
|
32461
|
-
return function(path, dist) {
|
|
32462
|
-
// var x0, y0;
|
|
32463
|
-
var x1, y1, x2, y2;
|
|
32464
|
-
var p1, p2;
|
|
32465
|
-
// var firstBearing;
|
|
32466
|
-
var bearing, prevBearing, joinAngle;
|
|
32467
|
-
partials = [];
|
|
32468
|
-
left = [];
|
|
32469
|
-
center = [];
|
|
32470
|
-
pathIter.init(path);
|
|
32471
|
-
|
|
32472
|
-
// if (pathIter.hasNext()) {
|
|
32473
|
-
// x0 = x2 = pathIter.x;
|
|
32474
|
-
// y0 = y2 = pathIter.y;
|
|
32475
|
-
// }
|
|
32476
|
-
while (pathIter.hasNext()) {
|
|
32477
|
-
// TODO: use a tolerance
|
|
32478
|
-
if (pathIter.x === x2 && pathIter.y === y2) continue; // skip duplicate points
|
|
32479
|
-
x1 = x2;
|
|
32480
|
-
y1 = y2;
|
|
32481
|
-
x2 = pathIter.x;
|
|
32482
|
-
y2 = pathIter.y;
|
|
32483
|
-
|
|
32484
|
-
prevBearing = bearing;
|
|
32485
|
-
bearing = getBearing(x1, y1, x2, y2);
|
|
32486
|
-
// shift original polyline segment to the left by buffer distance
|
|
32487
|
-
p1 = geod(x1, y1, bearing - 90, dist);
|
|
32488
|
-
p2 = geod(x2, y2, bearing - 90, dist);
|
|
32489
|
-
|
|
32490
|
-
if (center.length === 0) {
|
|
32491
|
-
// first loop, second point in this partial
|
|
32492
|
-
// if (partials.length === 0) {
|
|
32493
|
-
// firstBearing = bearing;
|
|
32494
|
-
// }
|
|
32495
|
-
left.push(p1, p2);
|
|
32496
|
-
center.push([x1, y1], [x2, y2]);
|
|
32497
|
-
} else {
|
|
32498
|
-
//
|
|
32499
|
-
joinAngle = getJoinAngle(prevBearing, bearing);
|
|
32500
|
-
if (veryCloseToPrevPoint(left, p1[0], p1[1])) {
|
|
32501
|
-
// skip first point
|
|
32502
|
-
addBufferVertex(p2);
|
|
32503
|
-
} else if (joinAngle > 0) {
|
|
32504
|
-
addRoundJoin(x1, y1, prevBearing - 90, joinAngle, dist);
|
|
32505
|
-
addBufferVertex(p1);
|
|
32506
|
-
addBufferVertex(p2);
|
|
32507
|
-
} else {
|
|
32508
|
-
addBufferVertex(p1);
|
|
32509
|
-
addBufferVertex(p2);
|
|
32510
|
-
}
|
|
32511
|
-
center.push([x2, y2]);
|
|
32512
|
-
}
|
|
32513
|
-
}
|
|
32514
|
-
|
|
32515
|
-
if (center.length > 1) {
|
|
32516
|
-
finishPartial();
|
|
32517
|
-
}
|
|
32518
|
-
// TODO: handle defective polylines
|
|
32519
|
-
|
|
32520
|
-
// if (x2 == x0 && y2 == y0) {
|
|
32521
|
-
// // add join to finish closed path
|
|
32522
|
-
// joinAngle = getJoinAngle(bearing, firstBearing);
|
|
32523
|
-
// if (joinAngle > 0) {
|
|
32524
|
-
// addRoundJoin(leftpart, x2, y2, bearing - 90, joinAngle, dist);
|
|
32525
|
-
// }
|
|
32526
|
-
// } else {
|
|
32527
|
-
// // add a cap to finish open path
|
|
32528
|
-
// leftpart.push.apply(leftpart, makeCap(x2, y2, bearing, dist));
|
|
32529
|
-
// }
|
|
32530
|
-
|
|
32531
|
-
return partials;
|
|
32532
|
-
};
|
|
32533
|
-
}
|
|
32534
|
-
|
|
32535
31918
|
// GeographicLib docs: https://geographiclib.sourceforge.io/html/js/
|
|
32536
31919
|
// https://geographiclib.sourceforge.io/html/js/module-GeographicLib_Geodesic.Geodesic.html
|
|
32537
31920
|
// https://geographiclib.sourceforge.io/html/js/tutorial-2-interface.html
|
|
@@ -32628,30 +32011,43 @@ ${svg}
|
|
|
32628
32011
|
});
|
|
32629
32012
|
|
|
32630
32013
|
function makePolylineBuffer(lyr, dataset, opts) {
|
|
32014
|
+
time('buffer');
|
|
32631
32015
|
var geojson = makeShapeBufferGeoJSON(lyr, dataset, opts);
|
|
32632
32016
|
var dataset2 = importGeoJSON(geojson, {});
|
|
32633
|
-
|
|
32017
|
+
if (!opts.debug_points) {
|
|
32018
|
+
dissolveBufferDataset(dataset2, opts);
|
|
32019
|
+
}
|
|
32020
|
+
timeEnd('buffer');
|
|
32634
32021
|
return dataset2;
|
|
32635
32022
|
}
|
|
32636
32023
|
|
|
32637
32024
|
function makeShapeBufferGeoJSON(lyr, dataset, opts) {
|
|
32638
32025
|
var distanceFn = getBufferDistanceFunction(lyr, dataset, opts);
|
|
32639
|
-
getBufferToleranceFunction(dataset, opts);
|
|
32026
|
+
// var toleranceFn = getBufferToleranceFunction(dataset, opts);
|
|
32640
32027
|
var geod = getFastGeodeticSegmentFunction(getDatasetCRS(dataset));
|
|
32641
32028
|
var getBearing = getBearingFunction(dataset);
|
|
32642
32029
|
var makerOpts = Object.assign({geometry_type: lyr.geometry_type}, opts);
|
|
32643
|
-
var
|
|
32644
|
-
|
|
32645
|
-
|
|
32646
|
-
|
|
32647
|
-
|
|
32648
|
-
|
|
32649
|
-
|
|
32650
|
-
|
|
32030
|
+
var makeShapeBuffer =
|
|
32031
|
+
opts.v2 && getPolylineBufferMaker$1(dataset.arcs, geod, getBearing, makerOpts) ||
|
|
32032
|
+
opts.v3 && getPolylineBufferMaker$2(dataset.arcs, geod, getBearing, makerOpts) ||
|
|
32033
|
+
getPolylineBufferMaker(dataset.arcs, geod, getBearing, makerOpts);
|
|
32034
|
+
// var records = lyr.data ? lyr.data.getRecords() : null;
|
|
32035
|
+
var arr = lyr.shapes.reduce(function(memo, shape, i) {
|
|
32036
|
+
var distance = distanceFn(i);
|
|
32037
|
+
if (!distance || !shape) return memo;
|
|
32038
|
+
// retn might be an array of features or a single feature
|
|
32039
|
+
var retn = makeShapeBuffer(shape, distance);
|
|
32040
|
+
return memo.concat(retn);
|
|
32041
|
+
}, []);
|
|
32042
|
+
var geojsonType = arr?.[0].type;
|
|
32651
32043
|
// TODO: make sure that importer supports null geometries (not standard GeoJSON);
|
|
32652
|
-
|
|
32044
|
+
// console.log(arr)
|
|
32045
|
+
return geojsonType == 'Feature' ? {
|
|
32046
|
+
type: 'FeatureCollection',
|
|
32047
|
+
features: arr
|
|
32048
|
+
} : {
|
|
32653
32049
|
type: 'GeometryCollection',
|
|
32654
|
-
geometries:
|
|
32050
|
+
geometries: arr
|
|
32655
32051
|
};
|
|
32656
32052
|
}
|
|
32657
32053
|
|
|
@@ -33103,8 +32499,7 @@ ${svg}
|
|
|
33103
32499
|
stop("Unsupported geometry type");
|
|
33104
32500
|
}
|
|
33105
32501
|
|
|
33106
|
-
|
|
33107
|
-
return [lyr2];
|
|
32502
|
+
return mergeOutputLayersIntoDataset(lyr, dataset, dataset2, opts);
|
|
33108
32503
|
}
|
|
33109
32504
|
|
|
33110
32505
|
// currently undocumented, used in tests
|
|
@@ -37755,9 +37150,6 @@ ${svg}
|
|
|
37755
37150
|
getLayerInfo: getLayerInfo
|
|
37756
37151
|
});
|
|
37757
37152
|
|
|
37758
|
-
// import { importGeoJSON } from '../geojson/geojson-import';
|
|
37759
|
-
|
|
37760
|
-
|
|
37761
37153
|
function addTargetProxies(targets, ctx) {
|
|
37762
37154
|
if (targets && targets.length > 0) {
|
|
37763
37155
|
var proxies = expandCommandTargets(targets).reduce(function(memo, target) {
|
|
@@ -37780,11 +37172,19 @@ ${svg}
|
|
|
37780
37172
|
var proxy = getLayerInfo(target.layer, target.dataset); // layer_name, feature_count etc
|
|
37781
37173
|
proxy.layer = target.layer;
|
|
37782
37174
|
proxy.dataset = target.dataset;
|
|
37783
|
-
|
|
37784
|
-
|
|
37785
|
-
|
|
37175
|
+
|
|
37176
|
+
Object.defineProperty(proxy, 'geojson', {
|
|
37177
|
+
set: setGeoJSON,
|
|
37178
|
+
get: getGeoJSON
|
|
37786
37179
|
});
|
|
37787
37180
|
|
|
37181
|
+
function setGeoJSON(o) {
|
|
37182
|
+
var dataset2 = importGeoJSON(o);
|
|
37183
|
+
var lyr2 = dataset2.layers[0];
|
|
37184
|
+
lyr2.name = target.layer.name;
|
|
37185
|
+
replaceLayerContents(target.layer, target.dataset, dataset2);
|
|
37186
|
+
}
|
|
37187
|
+
|
|
37788
37188
|
function getGeoJSON() {
|
|
37789
37189
|
var features = exportLayerAsGeoJSON(target.layer, target.dataset, {rfc7946: true}, true);
|
|
37790
37190
|
return {
|
|
@@ -42303,7 +41703,7 @@ ${svg}
|
|
|
42303
41703
|
function findPathMidpoint(path, arcs, useNearestVertex) {
|
|
42304
41704
|
var halfLen = calcPathLen(path, arcs, false) / 2;
|
|
42305
41705
|
var partialLen = 0;
|
|
42306
|
-
var p;
|
|
41706
|
+
var p = null;
|
|
42307
41707
|
forEachSegmentInPath(path, arcs, function(i, j, xx, yy) {
|
|
42308
41708
|
var a = xx[i],
|
|
42309
41709
|
b = yy[i],
|
|
@@ -42323,7 +41723,7 @@ ${svg}
|
|
|
42323
41723
|
partialLen += segLen;
|
|
42324
41724
|
});
|
|
42325
41725
|
if (!p) {
|
|
42326
|
-
|
|
41726
|
+
debug('[findPathMidpoint()] defective path:', path);
|
|
42327
41727
|
}
|
|
42328
41728
|
return p;
|
|
42329
41729
|
}
|
|
@@ -47259,7 +46659,7 @@ ${svg}
|
|
|
47259
46659
|
});
|
|
47260
46660
|
}
|
|
47261
46661
|
|
|
47262
|
-
var version = "0.6.
|
|
46662
|
+
var version = "0.6.112";
|
|
47263
46663
|
|
|
47264
46664
|
// Parse command line args into commands and run them
|
|
47265
46665
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|
|
@@ -47862,67 +47262,6 @@ ${svg}
|
|
|
47862
47262
|
return false;
|
|
47863
47263
|
}
|
|
47864
47264
|
|
|
47865
|
-
// TODO: Need to rethink polygon repair: these function can cause problems
|
|
47866
|
-
// when part of a self-intersecting polygon is removed
|
|
47867
|
-
//
|
|
47868
|
-
function repairPolygonGeometry(layers, dataset, opts) {
|
|
47869
|
-
var nodes = addIntersectionCuts(dataset);
|
|
47870
|
-
layers.forEach(function(lyr) {
|
|
47871
|
-
repairSelfIntersections(lyr, nodes);
|
|
47872
|
-
});
|
|
47873
|
-
return layers;
|
|
47874
|
-
}
|
|
47875
|
-
|
|
47876
|
-
// Remove any small shapes formed by twists in each ring
|
|
47877
|
-
// // OOPS, NO // Retain only the part with largest area
|
|
47878
|
-
// // this causes problems when a cut-off hole has a matching ring in another polygon
|
|
47879
|
-
// TODO: consider cases where cut-off parts should be retained
|
|
47880
|
-
//
|
|
47881
|
-
function repairSelfIntersections(lyr, nodes) {
|
|
47882
|
-
var splitter = getSelfIntersectionSplitter(nodes);
|
|
47883
|
-
|
|
47884
|
-
lyr.shapes = lyr.shapes.map(function(shp, i) {
|
|
47885
|
-
return cleanPolygon(shp);
|
|
47886
|
-
});
|
|
47887
|
-
|
|
47888
|
-
function cleanPolygon(shp) {
|
|
47889
|
-
var cleanedPolygon = [];
|
|
47890
|
-
forEachShapePart(shp, function(ids) {
|
|
47891
|
-
// TODO: consider returning null if path can't be split
|
|
47892
|
-
var splitIds = splitter(ids);
|
|
47893
|
-
if (splitIds.length === 0) {
|
|
47894
|
-
error("[cleanPolygon()] Defective path:", ids);
|
|
47895
|
-
} else if (splitIds.length == 1) {
|
|
47896
|
-
cleanedPolygon.push(splitIds[0]);
|
|
47897
|
-
} else {
|
|
47898
|
-
var shapeArea = geom.getPlanarPathArea(ids, nodes.arcs),
|
|
47899
|
-
sign = shapeArea > 0 ? 1 : -1,
|
|
47900
|
-
mainRing;
|
|
47901
|
-
|
|
47902
|
-
splitIds.reduce(function(max, ringIds, i) {
|
|
47903
|
-
var pathArea = geom.getPlanarPathArea(ringIds, nodes.arcs) * sign;
|
|
47904
|
-
if (pathArea > max) {
|
|
47905
|
-
mainRing = ringIds;
|
|
47906
|
-
max = pathArea;
|
|
47907
|
-
}
|
|
47908
|
-
return max;
|
|
47909
|
-
}, 0);
|
|
47910
|
-
|
|
47911
|
-
if (mainRing) {
|
|
47912
|
-
cleanedPolygon.push(mainRing);
|
|
47913
|
-
}
|
|
47914
|
-
}
|
|
47915
|
-
});
|
|
47916
|
-
return cleanedPolygon.length > 0 ? cleanedPolygon : null;
|
|
47917
|
-
}
|
|
47918
|
-
}
|
|
47919
|
-
|
|
47920
|
-
var PolygonRepair = /*#__PURE__*/Object.freeze({
|
|
47921
|
-
__proto__: null,
|
|
47922
|
-
repairPolygonGeometry: repairPolygonGeometry,
|
|
47923
|
-
repairSelfIntersections: repairSelfIntersections
|
|
47924
|
-
});
|
|
47925
|
-
|
|
47926
47265
|
// Attach functions exported by modules to the "internal" object,
|
|
47927
47266
|
// so they can be run by tests and by the GUI.
|
|
47928
47267
|
// TODO: rewrite tests to import functions directly from modules,
|
|
@@ -47969,7 +47308,7 @@ ${svg}
|
|
|
47969
47308
|
ArcUtils,
|
|
47970
47309
|
Bbox2Clipping,
|
|
47971
47310
|
BinArray$1,
|
|
47972
|
-
BufferCommon,
|
|
47311
|
+
// BufferCommon,
|
|
47973
47312
|
Calc,
|
|
47974
47313
|
CalcUtils,
|
|
47975
47314
|
Catalog$1,
|
|
@@ -48024,7 +47363,7 @@ ${svg}
|
|
|
48024
47363
|
OverlayUtils,
|
|
48025
47364
|
Pack, Unpack,
|
|
48026
47365
|
ParseCommands,
|
|
48027
|
-
PathBuffer,
|
|
47366
|
+
// PathBuffer,
|
|
48028
47367
|
PathEndpoints,
|
|
48029
47368
|
PathExport,
|
|
48030
47369
|
Pathfinder,
|