mapshaper 0.7.48 → 0.7.49
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 +1484 -613
- package/package.json +1 -1
- package/www/mapshaper-gui.js +281 -64
- package/www/mapshaper.js +1484 -613
- package/www/page.css +11 -0
package/www/mapshaper.js
CHANGED
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
return obj === Object(obj); // via underscore
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
function clamp$
|
|
81
|
+
function clamp$3(val, min, max) {
|
|
82
82
|
return val < min ? min : (val > max ? max : val);
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -721,7 +721,7 @@
|
|
|
721
721
|
function findValueByRank(arr, rank) {
|
|
722
722
|
if (!arr.length || rank < 1 || rank > arr.length) error$1("[findValueByRank()] invalid input");
|
|
723
723
|
|
|
724
|
-
rank = clamp$
|
|
724
|
+
rank = clamp$3(rank | 0, 1, arr.length);
|
|
725
725
|
var k = rank - 1, // conv. rank to array index
|
|
726
726
|
n = arr.length,
|
|
727
727
|
l = 0,
|
|
@@ -1095,7 +1095,7 @@
|
|
|
1095
1095
|
// self-import and the resulting Rollup circular-dependency warning.
|
|
1096
1096
|
var utils = {
|
|
1097
1097
|
addThousandsSep, addslashes, arrayToIndex,
|
|
1098
|
-
clamp: clamp$
|
|
1098
|
+
clamp: clamp$3, cleanNumericString, contains, copyElements, countValues, createBuffer,
|
|
1099
1099
|
defaults, difference,
|
|
1100
1100
|
endsWith, every, expandoBuffer, extend: extend$1, extendBuffer,
|
|
1101
1101
|
find: find$1, findMedian, findQuantile, findRankByValue, findStringPrefix,
|
|
@@ -4740,6 +4740,89 @@
|
|
|
4740
4740
|
pathIsRectangle: pathIsRectangle
|
|
4741
4741
|
});
|
|
4742
4742
|
|
|
4743
|
+
// Low-level access to raster grid samples, shared by the code that reads or
|
|
4744
|
+
// rewrites pixels (-proj, -blur, clipping and preview rendering). These used
|
|
4745
|
+
// to be reimplemented per module, which let the nodata and coverage rules
|
|
4746
|
+
// drift apart.
|
|
4747
|
+
//
|
|
4748
|
+
// Samples are band-interleaved: pixel i occupies
|
|
4749
|
+
// samples[i * bands] through samples[i * bands + bands - 1].
|
|
4750
|
+
|
|
4751
|
+
// A pixel is nodata only when every one of its color-carrying bands matches
|
|
4752
|
+
// the nodata value. An alpha band is ignored, so a transparent pixel whose
|
|
4753
|
+
// color channels hold real values is still treated as data.
|
|
4754
|
+
function samplesAreNodataAtOffset(samples, offset, bands, nodata) {
|
|
4755
|
+
var n = Math.min(bands, 3);
|
|
4756
|
+
if (nodata === null || nodata === undefined) return false;
|
|
4757
|
+
for (var i = 0; i < n; i++) {
|
|
4758
|
+
if (samples[offset + i] != nodata) return false;
|
|
4759
|
+
}
|
|
4760
|
+
return true;
|
|
4761
|
+
}
|
|
4762
|
+
|
|
4763
|
+
function rasterPixelIsNodata(grid, pixelId) {
|
|
4764
|
+
return samplesAreNodataAtOffset(grid.samples, pixelId * grid.bands, grid.bands, grid.nodata);
|
|
4765
|
+
}
|
|
4766
|
+
|
|
4767
|
+
// The coverage mask is set by reprojection to record which output pixels
|
|
4768
|
+
// received source content, independently of the nodata fill value.
|
|
4769
|
+
function rasterPixelIsCovered(grid, pixelId) {
|
|
4770
|
+
return !grid.coverage || grid.coverage[pixelId] > 0;
|
|
4771
|
+
}
|
|
4772
|
+
|
|
4773
|
+
function rasterPixelIsValid(grid, pixelId) {
|
|
4774
|
+
return rasterPixelIsCovered(grid, pixelId) && !rasterPixelIsNodata(grid, pixelId);
|
|
4775
|
+
}
|
|
4776
|
+
|
|
4777
|
+
// False when every pixel is known to be valid, so callers can skip
|
|
4778
|
+
// per-pixel validity testing entirely.
|
|
4779
|
+
function rasterGridHasInvalidPixels(grid) {
|
|
4780
|
+
return !!grid.coverage || grid.nodata !== null && grid.nodata !== undefined;
|
|
4781
|
+
}
|
|
4782
|
+
|
|
4783
|
+
// One byte per pixel, so that code sampling a grid repeatedly can test
|
|
4784
|
+
// validity with a single lookup instead of re-reading every band. Returns
|
|
4785
|
+
// the coverage mask itself when there is no nodata value to fold in, and
|
|
4786
|
+
// null when every pixel is valid.
|
|
4787
|
+
function getRasterValidityMask(grid) {
|
|
4788
|
+
var n = grid.width * grid.height;
|
|
4789
|
+
var mask, i;
|
|
4790
|
+
if (grid.nodata === null || grid.nodata === undefined) return grid.coverage || null;
|
|
4791
|
+
mask = new Uint8Array(n);
|
|
4792
|
+
for (i = 0; i < n; i++) {
|
|
4793
|
+
mask[i] = rasterPixelIsValid(grid, i) ? 1 : 0;
|
|
4794
|
+
}
|
|
4795
|
+
return mask;
|
|
4796
|
+
}
|
|
4797
|
+
|
|
4798
|
+
function rasterSamplesAreFloat(samples) {
|
|
4799
|
+
return samples instanceof Float32Array || samples instanceof Float64Array;
|
|
4800
|
+
}
|
|
4801
|
+
|
|
4802
|
+
// Value range of a sample array, used to clamp computed values before
|
|
4803
|
+
// writing them back into an integer array.
|
|
4804
|
+
function getSampleArrayRange(samples) {
|
|
4805
|
+
if (samples instanceof Uint8Array || samples instanceof Uint8ClampedArray) return {min: 0, max: 255};
|
|
4806
|
+
if (samples instanceof Int8Array) return {min: -128, max: 127};
|
|
4807
|
+
if (samples instanceof Uint16Array) return {min: 0, max: 65535};
|
|
4808
|
+
if (samples instanceof Int16Array) return {min: -32768, max: 32767};
|
|
4809
|
+
if (samples instanceof Uint32Array) return {min: 0, max: 4294967295};
|
|
4810
|
+
if (samples instanceof Int32Array) return {min: -2147483648, max: 2147483647};
|
|
4811
|
+
return {min: -Infinity, max: Infinity};
|
|
4812
|
+
}
|
|
4813
|
+
|
|
4814
|
+
var RasterGrid = /*#__PURE__*/Object.freeze({
|
|
4815
|
+
__proto__: null,
|
|
4816
|
+
getRasterValidityMask: getRasterValidityMask,
|
|
4817
|
+
getSampleArrayRange: getSampleArrayRange,
|
|
4818
|
+
rasterGridHasInvalidPixels: rasterGridHasInvalidPixels,
|
|
4819
|
+
rasterPixelIsCovered: rasterPixelIsCovered,
|
|
4820
|
+
rasterPixelIsNodata: rasterPixelIsNodata,
|
|
4821
|
+
rasterPixelIsValid: rasterPixelIsValid,
|
|
4822
|
+
rasterSamplesAreFloat: rasterSamplesAreFloat,
|
|
4823
|
+
samplesAreNodataAtOffset: samplesAreNodataAtOffset
|
|
4824
|
+
});
|
|
4825
|
+
|
|
4743
4826
|
function runningInBrowser() {
|
|
4744
4827
|
return typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
|
4745
4828
|
}
|
|
@@ -4821,6 +4904,9 @@
|
|
|
4821
4904
|
function copyRasterGrid(grid) {
|
|
4822
4905
|
var copy = utils.extend({}, grid);
|
|
4823
4906
|
if (grid.samples) copy.samples = copyTypedArray(grid.samples);
|
|
4907
|
+
// The coverage mask is mutated in place by the reprojection code, so an
|
|
4908
|
+
// undo snapshot that shared it would track later edits.
|
|
4909
|
+
if (grid.coverage) copy.coverage = copyTypedArray(grid.coverage);
|
|
4824
4910
|
if (grid.sampleBands) copy.sampleBands = grid.sampleBands.concat();
|
|
4825
4911
|
if (grid.bbox) copy.bbox = grid.bbox.concat();
|
|
4826
4912
|
if (grid.transform) copy.transform = copyObjectOrArray(grid.transform);
|
|
@@ -4928,6 +5014,69 @@
|
|
|
4928
5014
|
return stats;
|
|
4929
5015
|
}
|
|
4930
5016
|
|
|
5017
|
+
// Returns the band values and display color of the pixel containing a map
|
|
5018
|
+
// coordinate, for readouts like the GUI's right-click menu, or null if the point
|
|
5019
|
+
// falls outside the raster. Rotated grids are not supported, because pixels can
|
|
5020
|
+
// only be located through the axis-aligned bbox.
|
|
5021
|
+
// x, y: a point in the coordinate system of the raster's bbox
|
|
5022
|
+
function getRasterPixelAtMapXY(raster, x, y) {
|
|
5023
|
+
var grid = getRasterGrid(raster);
|
|
5024
|
+
var col, row, pixelId, offset, values, valid, isColor, band;
|
|
5025
|
+
if (!grid || !grid.samples || !grid.bbox || rasterGridIsRotated(grid)) return null;
|
|
5026
|
+
col = Math.floor(mapXToRasterPixel(grid, x));
|
|
5027
|
+
row = Math.floor(mapYToRasterPixel(grid, y));
|
|
5028
|
+
if (col < 0 || col >= grid.width || row < 0 || row >= grid.height) return null;
|
|
5029
|
+
pixelId = row * grid.width + col;
|
|
5030
|
+
offset = pixelId * grid.bands;
|
|
5031
|
+
values = [];
|
|
5032
|
+
for (band = 0; band < grid.bands; band++) {
|
|
5033
|
+
values.push(grid.samples[offset + band]);
|
|
5034
|
+
}
|
|
5035
|
+
// The renderer reads interleaved bands in display order, so three or more
|
|
5036
|
+
// bands are shown as R, G, B and (if present) alpha.
|
|
5037
|
+
isColor = grid.bands >= 3;
|
|
5038
|
+
valid = rasterPixelIsValid(grid, pixelId);
|
|
5039
|
+
return {
|
|
5040
|
+
col: col,
|
|
5041
|
+
row: row,
|
|
5042
|
+
values: values,
|
|
5043
|
+
isColor: isColor,
|
|
5044
|
+
isFloat: rasterSamplesAreFloat(grid.samples),
|
|
5045
|
+
valid: valid,
|
|
5046
|
+
// Only color images get a color, so that inspecting a measurement raster
|
|
5047
|
+
// does not trigger a scaling-stats scan of the whole grid.
|
|
5048
|
+
// [r, g, b, a] in the 0-255 range, for formatColor() or a canvas.
|
|
5049
|
+
color: isColor && valid ? getRasterPixelDisplayColor(raster, grid, pixelId) : null
|
|
5050
|
+
};
|
|
5051
|
+
}
|
|
5052
|
+
|
|
5053
|
+
// Returns [r, g, b, a] in the 0-255 range, using the same recipe and scaling as
|
|
5054
|
+
// the preview renderer, so that a readout agrees with how the pixel is drawn.
|
|
5055
|
+
function getRasterPixelDisplayColor(raster, grid, pixelId) {
|
|
5056
|
+
var view = getRasterView(raster);
|
|
5057
|
+
var recipe = getRasterViewRecipe(grid, view && view.recipe);
|
|
5058
|
+
var stats = getRasterViewScalingStats(raster, recipe);
|
|
5059
|
+
var sourceRange = recipe.scaling == 'none' ? getPixelTypeRange(grid.pixelType) : null;
|
|
5060
|
+
var displayRange = getDisplayRange(recipe.scaleRange);
|
|
5061
|
+
var offset = pixelId * grid.bands;
|
|
5062
|
+
var color = [0, 1, 2].map(function(band) {
|
|
5063
|
+
return scaleSample(grid.samples[offset + band], stats && stats[band], sourceRange, displayRange);
|
|
5064
|
+
});
|
|
5065
|
+
color.push(grid.bands >= 4 ?
|
|
5066
|
+
scaleSample(grid.samples[offset + 3], stats && stats[3], sourceRange, [0, 255]) : 255);
|
|
5067
|
+
return color;
|
|
5068
|
+
}
|
|
5069
|
+
|
|
5070
|
+
// Float samples print with binary noise at full precision (a Float32 holding
|
|
5071
|
+
// 1234.5678 prints as 1234.5677490234375), so they are rounded to the number of
|
|
5072
|
+
// digits that the type can actually represent.
|
|
5073
|
+
function formatRasterSampleValue(val, isFloat) {
|
|
5074
|
+
if (val === null || val === undefined) return '';
|
|
5075
|
+
if (!isFinite(val)) return String(val); // NaN, Infinity
|
|
5076
|
+
if (!isFloat) return String(val);
|
|
5077
|
+
return String(+val.toPrecision(7));
|
|
5078
|
+
}
|
|
5079
|
+
|
|
4931
5080
|
function renderRasterGridPreview(grid, recipe, width, height, statsArg, sourceBbox, resamplingMethod) {
|
|
4932
5081
|
var samples = grid.samples;
|
|
4933
5082
|
var bands = grid.bands;
|
|
@@ -5170,10 +5319,22 @@
|
|
|
5170
5319
|
return (sy * grid.width + sx) * bands;
|
|
5171
5320
|
}
|
|
5172
5321
|
|
|
5322
|
+
// A grid is north-up when its affine transform has no rotation/skew terms.
|
|
5323
|
+
// Cropping and reprojection both index pixels through the axis-aligned bbox,
|
|
5324
|
+
// so they can only handle north-up grids.
|
|
5325
|
+
function rasterGridIsRotated(grid) {
|
|
5326
|
+
var t = grid && grid.transform;
|
|
5327
|
+
return !!t && (t[1] !== 0 || t[3] !== 0);
|
|
5328
|
+
}
|
|
5329
|
+
|
|
5173
5330
|
function clipRasterToBBox(lyr, bbox, opts) {
|
|
5174
5331
|
var raster = lyr.raster;
|
|
5175
5332
|
var grid = getRasterGrid(raster);
|
|
5176
|
-
var clipBbox
|
|
5333
|
+
var clipBbox;
|
|
5334
|
+
if (rasterGridIsRotated(grid)) {
|
|
5335
|
+
stop$1('Clipping a rotated or skewed raster is not supported');
|
|
5336
|
+
}
|
|
5337
|
+
clipBbox = intersectBboxes(grid.bbox, bbox);
|
|
5177
5338
|
if (!clipBbox) {
|
|
5178
5339
|
warn('Raster clipping rectangle does not intersect the raster layer');
|
|
5179
5340
|
return false;
|
|
@@ -5234,11 +5395,30 @@
|
|
|
5234
5395
|
width: crop.width,
|
|
5235
5396
|
height: crop.height,
|
|
5236
5397
|
samples: samples,
|
|
5398
|
+
coverage: cropRasterCoverage(grid, crop),
|
|
5237
5399
|
bbox: bbox,
|
|
5238
5400
|
transform: updateTransformForBBox(grid.transform, bbox, crop.width, crop.height)
|
|
5239
5401
|
});
|
|
5240
5402
|
}
|
|
5241
5403
|
|
|
5404
|
+
// The coverage mask is one byte per pixel (not per sample), and must be
|
|
5405
|
+
// cropped alongside the samples -- consumers index it with the cropped
|
|
5406
|
+
// grid's dimensions.
|
|
5407
|
+
function cropRasterCoverage(grid, crop) {
|
|
5408
|
+
var coverage = grid.coverage;
|
|
5409
|
+
var cropped, src, dest, x, y;
|
|
5410
|
+
if (!coverage) return coverage;
|
|
5411
|
+
cropped = new Uint8Array(crop.width * crop.height);
|
|
5412
|
+
for (y = 0; y < crop.height; y++) {
|
|
5413
|
+
src = (crop.y + y) * grid.width + crop.x;
|
|
5414
|
+
dest = y * crop.width;
|
|
5415
|
+
for (x = 0; x < crop.width; x++) {
|
|
5416
|
+
cropped[dest + x] = coverage[src + x];
|
|
5417
|
+
}
|
|
5418
|
+
}
|
|
5419
|
+
return cropped;
|
|
5420
|
+
}
|
|
5421
|
+
|
|
5242
5422
|
function getRasterLayerBounds(lyr) {
|
|
5243
5423
|
var bbox = lyr.raster && getRasterBBox(lyr.raster);
|
|
5244
5424
|
return bbox && bbox.length == 4 ? new Bounds(bbox) : null;
|
|
@@ -5499,11 +5679,7 @@
|
|
|
5499
5679
|
}
|
|
5500
5680
|
|
|
5501
5681
|
function allSamplesAreNoData(data, offset, bands, noData) {
|
|
5502
|
-
|
|
5503
|
-
for (var i = 0; i < n; i++) {
|
|
5504
|
-
if (data[offset + i] != noData) return false;
|
|
5505
|
-
}
|
|
5506
|
-
return true;
|
|
5682
|
+
return samplesAreNodataAtOffset(data, offset, bands, noData);
|
|
5507
5683
|
}
|
|
5508
5684
|
|
|
5509
5685
|
function copyObjectOrArray(obj) {
|
|
@@ -5520,12 +5696,14 @@
|
|
|
5520
5696
|
copyTypedArray: copyTypedArray,
|
|
5521
5697
|
createRasterPreview: createRasterPreview,
|
|
5522
5698
|
cropRasterGrid: cropRasterGrid,
|
|
5699
|
+
formatRasterSampleValue: formatRasterSampleValue,
|
|
5523
5700
|
getRasterBBox: getRasterBBox,
|
|
5524
5701
|
getRasterBandCount: getRasterBandCount,
|
|
5525
5702
|
getRasterCrop: getRasterCrop,
|
|
5526
5703
|
getRasterGrid: getRasterGrid,
|
|
5527
5704
|
getRasterHeight: getRasterHeight,
|
|
5528
5705
|
getRasterLayerBounds: getRasterLayerBounds,
|
|
5706
|
+
getRasterPixelAtMapXY: getRasterPixelAtMapXY,
|
|
5529
5707
|
getRasterPixelType: getRasterPixelType,
|
|
5530
5708
|
getRasterPreview: getRasterPreview,
|
|
5531
5709
|
getRasterScalingStats: getRasterScalingStats,
|
|
@@ -5536,6 +5714,7 @@
|
|
|
5536
5714
|
getRasterViewScalingStats: getRasterViewScalingStats,
|
|
5537
5715
|
getRasterWidth: getRasterWidth,
|
|
5538
5716
|
intersectBboxes: intersectBboxes,
|
|
5717
|
+
rasterGridIsRotated: rasterGridIsRotated,
|
|
5539
5718
|
renderRasterExportPreview: renderRasterExportPreview,
|
|
5540
5719
|
renderRasterPreview: renderRasterPreview,
|
|
5541
5720
|
renderRasterViewportPreview: renderRasterViewportPreview,
|
|
@@ -5712,6 +5891,14 @@
|
|
|
5712
5891
|
stop$1(layerTypeMessage(lyr, "Expected a polygon or polyline layer", msg));
|
|
5713
5892
|
}
|
|
5714
5893
|
|
|
5894
|
+
// Guard for vector commands that would otherwise replace a raster layer with
|
|
5895
|
+
// a geometry-less result, silently discarding its pixels.
|
|
5896
|
+
function requireNotRasterLayer(lyr, msg) {
|
|
5897
|
+
if (layerHasRaster(lyr)) {
|
|
5898
|
+
stop$1(msg || 'This command does not support raster layers');
|
|
5899
|
+
}
|
|
5900
|
+
}
|
|
5901
|
+
|
|
5715
5902
|
// Used by info command and gui layer menu
|
|
5716
5903
|
function getLayerSourceFile(lyr, dataset) {
|
|
5717
5904
|
var inputs = dataset.info && dataset.info.input_files;
|
|
@@ -5913,6 +6100,7 @@
|
|
|
5913
6100
|
layerTypeMessage: layerTypeMessage,
|
|
5914
6101
|
requireDataField: requireDataField,
|
|
5915
6102
|
requireDataFields: requireDataFields,
|
|
6103
|
+
requireNotRasterLayer: requireNotRasterLayer,
|
|
5916
6104
|
requirePathLayer: requirePathLayer,
|
|
5917
6105
|
requirePointLayer: requirePointLayer,
|
|
5918
6106
|
requirePolygonLayer: requirePolygonLayer,
|
|
@@ -6257,6 +6445,124 @@
|
|
|
6257
6445
|
probablyDecimalDegreeBounds: probablyDecimalDegreeBounds
|
|
6258
6446
|
});
|
|
6259
6447
|
|
|
6448
|
+
/*
|
|
6449
|
+
* Small geometric helpers shared by the polyhedral projection modules
|
|
6450
|
+
* (mapshaper-polyhedral-projection, mapshaper-lee-tetrahedral,
|
|
6451
|
+
* mapshaper-narukawa2022). These projections all unfold a polyhedral net into
|
|
6452
|
+
* affine layout copies and clip the copies to a rectangular frame, so they need
|
|
6453
|
+
* the same 2x3 affine arithmetic and the same half-plane polygon clipping.
|
|
6454
|
+
*/
|
|
6455
|
+
|
|
6456
|
+
var EPS$4 = 1e-12;
|
|
6457
|
+
|
|
6458
|
+
// A 2x3 affine matrix, stored as [a, b, tx, c, d, ty].
|
|
6459
|
+
function applyMatrix(m, p) {
|
|
6460
|
+
return [
|
|
6461
|
+
m[0] * p[0] + m[1] * p[1] + m[2],
|
|
6462
|
+
m[3] * p[0] + m[4] * p[1] + m[5]
|
|
6463
|
+
];
|
|
6464
|
+
}
|
|
6465
|
+
|
|
6466
|
+
function invertMatrix(m) {
|
|
6467
|
+
var det = m[0] * m[4] - m[1] * m[3];
|
|
6468
|
+
return [
|
|
6469
|
+
m[4] / det,
|
|
6470
|
+
-m[1] / det,
|
|
6471
|
+
(m[1] * m[5] - m[4] * m[2]) / det,
|
|
6472
|
+
-m[3] / det,
|
|
6473
|
+
m[0] / det,
|
|
6474
|
+
(m[3] * m[2] - m[0] * m[5]) / det
|
|
6475
|
+
];
|
|
6476
|
+
}
|
|
6477
|
+
|
|
6478
|
+
// Sutherland-Hodgman clip of a [x, y] ring against one axis-aligned half-plane.
|
|
6479
|
+
function clipPolygonAxis(polygon, axis, value, keepGreater) {
|
|
6480
|
+
var output = [];
|
|
6481
|
+
if (!polygon.length) return output;
|
|
6482
|
+
var a = polygon[polygon.length - 1];
|
|
6483
|
+
var aInside = isInside(a[axis], value, keepGreater);
|
|
6484
|
+
polygon.forEach(function(b) {
|
|
6485
|
+
var bInside = isInside(b[axis], value, keepGreater);
|
|
6486
|
+
if (aInside != bInside) {
|
|
6487
|
+
var t = (value - a[axis]) / (b[axis] - a[axis]);
|
|
6488
|
+
var p = [
|
|
6489
|
+
a[0] + (b[0] - a[0]) * t,
|
|
6490
|
+
a[1] + (b[1] - a[1]) * t
|
|
6491
|
+
];
|
|
6492
|
+
p[axis] = value;
|
|
6493
|
+
output.push(p);
|
|
6494
|
+
}
|
|
6495
|
+
if (bInside) output.push(b.concat());
|
|
6496
|
+
a = b;
|
|
6497
|
+
aInside = bInside;
|
|
6498
|
+
});
|
|
6499
|
+
return output;
|
|
6500
|
+
}
|
|
6501
|
+
|
|
6502
|
+
// As clipPolygonAxis, but for raster mesh vertices, which carry the source
|
|
6503
|
+
// grid position (sx, sy) and geographic position (lon, lat) alongside the
|
|
6504
|
+
// projected position. The axis argument is a property name ('x' or 'y').
|
|
6505
|
+
function clipRasterPolygonAxis(polygon, axis, value, keepGreater) {
|
|
6506
|
+
var output = [];
|
|
6507
|
+
if (!polygon.length) return output;
|
|
6508
|
+
var a = polygon[polygon.length - 1];
|
|
6509
|
+
var aInside = isInside(a[axis], value, keepGreater);
|
|
6510
|
+
polygon.forEach(function(b) {
|
|
6511
|
+
var bInside = isInside(b[axis], value, keepGreater);
|
|
6512
|
+
if (aInside != bInside) {
|
|
6513
|
+
var t = (value - a[axis]) / (b[axis] - a[axis]);
|
|
6514
|
+
var vertex = {
|
|
6515
|
+
x: a.x + (b.x - a.x) * t,
|
|
6516
|
+
y: a.y + (b.y - a.y) * t,
|
|
6517
|
+
sx: a.sx + (b.sx - a.sx) * t,
|
|
6518
|
+
sy: a.sy + (b.sy - a.sy) * t,
|
|
6519
|
+
lon: interpolateLongitude(a.lon, b.lon, t),
|
|
6520
|
+
lat: a.lat + (b.lat - a.lat) * t
|
|
6521
|
+
};
|
|
6522
|
+
vertex[axis] = value;
|
|
6523
|
+
output.push(vertex);
|
|
6524
|
+
}
|
|
6525
|
+
if (bInside) output.push(copyRasterVertex(b, b.x, b.y));
|
|
6526
|
+
a = b;
|
|
6527
|
+
aInside = bInside;
|
|
6528
|
+
});
|
|
6529
|
+
return output;
|
|
6530
|
+
}
|
|
6531
|
+
|
|
6532
|
+
function copyRasterVertex(vertex, x, y) {
|
|
6533
|
+
return {
|
|
6534
|
+
x: x,
|
|
6535
|
+
y: y,
|
|
6536
|
+
sx: vertex.sx,
|
|
6537
|
+
sy: vertex.sy,
|
|
6538
|
+
lon: vertex.lon,
|
|
6539
|
+
lat: vertex.lat
|
|
6540
|
+
};
|
|
6541
|
+
}
|
|
6542
|
+
|
|
6543
|
+
// Interpolate along the shorter of the two arcs, so a cell straddling the
|
|
6544
|
+
// antimeridian does not interpolate the long way around the globe.
|
|
6545
|
+
function interpolateLongitude(a, b, t) {
|
|
6546
|
+
var delta = b - a;
|
|
6547
|
+
if (delta > 180) delta -= 360;
|
|
6548
|
+
if (delta < -180) delta += 360;
|
|
6549
|
+
return normalizeLongitude$1(a + delta * t);
|
|
6550
|
+
}
|
|
6551
|
+
|
|
6552
|
+
function normalizeLongitude$1(lon) {
|
|
6553
|
+
while (lon > 180) lon -= 360;
|
|
6554
|
+
while (lon < -180) lon += 360;
|
|
6555
|
+
return lon;
|
|
6556
|
+
}
|
|
6557
|
+
|
|
6558
|
+
function clamp$2(value, min, max) {
|
|
6559
|
+
return Math.max(min, Math.min(max, value));
|
|
6560
|
+
}
|
|
6561
|
+
|
|
6562
|
+
function isInside(coord, value, keepGreater) {
|
|
6563
|
+
return keepGreater ? coord >= value - EPS$4 : coord <= value + EPS$4;
|
|
6564
|
+
}
|
|
6565
|
+
|
|
6260
6566
|
/*
|
|
6261
6567
|
* Polyhedral unfolding code adapted from d3-geo-polygon.
|
|
6262
6568
|
*
|
|
@@ -6264,6 +6570,7 @@
|
|
|
6264
6570
|
* ISC License: https://github.com/d3/d3-geo-polygon/blob/main/LICENSE
|
|
6265
6571
|
*/
|
|
6266
6572
|
|
|
6573
|
+
|
|
6267
6574
|
var D2R$7 = Math.PI / 180;
|
|
6268
6575
|
var R2D$6 = 180 / Math.PI;
|
|
6269
6576
|
var EPS$3 = 1e-12;
|
|
@@ -6378,7 +6685,7 @@
|
|
|
6378
6685
|
|
|
6379
6686
|
function projectFacePoint(face, lam, phi) {
|
|
6380
6687
|
var p = face.project(lam, phi);
|
|
6381
|
-
p = applyMatrix
|
|
6688
|
+
p = applyMatrix(face.transform, p);
|
|
6382
6689
|
p = transformOutputPoint(p);
|
|
6383
6690
|
p[0] -= centerX;
|
|
6384
6691
|
p[1] -= centerY;
|
|
@@ -6390,7 +6697,7 @@
|
|
|
6390
6697
|
var p, rotated;
|
|
6391
6698
|
if (!face || !face.project.invert) return null;
|
|
6392
6699
|
p = inverseTransformOutputPoint([x + centerX, y + centerY]);
|
|
6393
|
-
p = applyMatrix
|
|
6700
|
+
p = applyMatrix(invertMatrix(face.transform), p);
|
|
6394
6701
|
rotated = face.project.invert(p[0], p[1]);
|
|
6395
6702
|
if (!rotated) return null;
|
|
6396
6703
|
return rotateRadians(
|
|
@@ -6483,7 +6790,7 @@
|
|
|
6483
6790
|
config.rotation[2] * D2R$7,
|
|
6484
6791
|
true
|
|
6485
6792
|
);
|
|
6486
|
-
return [normalizeLongitude$
|
|
6793
|
+
return [normalizeLongitude$1(q[0] * R2D$6 + lon0), q[1] * R2D$6];
|
|
6487
6794
|
});
|
|
6488
6795
|
var paths = splitPathAtAntimeridian$3(
|
|
6489
6796
|
interpolateGreatCircle(endpoints[0], endpoints[1], 0.05)
|
|
@@ -6513,15 +6820,15 @@
|
|
|
6513
6820
|
}),
|
|
6514
6821
|
seams: seams,
|
|
6515
6822
|
findRegion: function(lon, lat) {
|
|
6516
|
-
var lam = normalizeLongitude$
|
|
6823
|
+
var lam = normalizeLongitude$1(lon - lon0) * D2R$7;
|
|
6517
6824
|
return findFace(lam, lat * D2R$7);
|
|
6518
6825
|
},
|
|
6519
6826
|
findTransitionRegion: function(lon, lat) {
|
|
6520
|
-
var lam = normalizeLongitude$
|
|
6827
|
+
var lam = normalizeLongitude$1(lon - lon0) * D2R$7;
|
|
6521
6828
|
return findTransitionRegion(lam, lat * D2R$7);
|
|
6522
6829
|
},
|
|
6523
6830
|
projectRegion: function(lon, lat, regionId) {
|
|
6524
|
-
var lam = normalizeLongitude$
|
|
6831
|
+
var lam = normalizeLongitude$1(lon - lon0) * D2R$7;
|
|
6525
6832
|
return forwardFace(lam, lat * D2R$7, regionId);
|
|
6526
6833
|
},
|
|
6527
6834
|
regionsAreAttached: function(a, b) {
|
|
@@ -6629,8 +6936,8 @@
|
|
|
6629
6936
|
var adjacent = findAdjacentFace(faces, face.id, a, b);
|
|
6630
6937
|
if (adjacent >= 0 && attachedPairs.has(pairKey(face.id, adjacent))) continue;
|
|
6631
6938
|
edges.push([
|
|
6632
|
-
applyMatrix
|
|
6633
|
-
applyMatrix
|
|
6939
|
+
applyMatrix(face.transform, face.project(a[0] * D2R$7, a[1] * D2R$7)),
|
|
6940
|
+
applyMatrix(face.transform, face.project(b[0] * D2R$7, b[1] * D2R$7))
|
|
6634
6941
|
]);
|
|
6635
6942
|
}
|
|
6636
6943
|
});
|
|
@@ -6747,25 +7054,6 @@
|
|
|
6747
7054
|
];
|
|
6748
7055
|
}
|
|
6749
7056
|
|
|
6750
|
-
function applyMatrix$1(m, p) {
|
|
6751
|
-
return [
|
|
6752
|
-
m[0] * p[0] + m[1] * p[1] + m[2],
|
|
6753
|
-
m[3] * p[0] + m[4] * p[1] + m[5]
|
|
6754
|
-
];
|
|
6755
|
-
}
|
|
6756
|
-
|
|
6757
|
-
function invertMatrix$1(m) {
|
|
6758
|
-
var det = m[0] * m[4] - m[1] * m[3];
|
|
6759
|
-
return [
|
|
6760
|
-
m[4] / det,
|
|
6761
|
-
-m[1] / det,
|
|
6762
|
-
(m[1] * m[5] - m[4] * m[2]) / det,
|
|
6763
|
-
-m[3] / det,
|
|
6764
|
-
m[0] / det,
|
|
6765
|
-
(m[3] * m[2] - m[0] * m[5]) / det
|
|
6766
|
-
];
|
|
6767
|
-
}
|
|
6768
|
-
|
|
6769
7057
|
function rotateRadians(lam, phi, deltaLam, deltaPhi, deltaGamma, invert) {
|
|
6770
7058
|
if (invert) {
|
|
6771
7059
|
var inv = rotatePhiGamma$1(lam, phi, deltaPhi, deltaGamma, true);
|
|
@@ -6791,21 +7079,21 @@
|
|
|
6791
7079
|
return [
|
|
6792
7080
|
Math.atan2(y * cosDeltaGamma + z * sinDeltaGamma,
|
|
6793
7081
|
x * cosDeltaPhi + k * sinDeltaPhi),
|
|
6794
|
-
Math.asin(clamp$
|
|
7082
|
+
Math.asin(clamp$2(k * cosDeltaPhi - x * sinDeltaPhi, -1, 1))
|
|
6795
7083
|
];
|
|
6796
7084
|
}
|
|
6797
7085
|
k = z * cosDeltaPhi + x * sinDeltaPhi;
|
|
6798
7086
|
return [
|
|
6799
7087
|
Math.atan2(y * cosDeltaGamma - k * sinDeltaGamma,
|
|
6800
7088
|
x * cosDeltaPhi - z * sinDeltaPhi),
|
|
6801
|
-
Math.asin(clamp$
|
|
7089
|
+
Math.asin(clamp$2(k * cosDeltaGamma + y * sinDeltaGamma, -1, 1))
|
|
6802
7090
|
];
|
|
6803
7091
|
}
|
|
6804
7092
|
|
|
6805
7093
|
function interpolateGreatCircle(a, b, interval) {
|
|
6806
7094
|
var av = degreesToVector$2(a[0], a[1]);
|
|
6807
7095
|
var bv = degreesToVector$2(b[0], b[1]);
|
|
6808
|
-
var angle = Math.acos(clamp$
|
|
7096
|
+
var angle = Math.acos(clamp$2(dot$2(av, bv), -1, 1));
|
|
6809
7097
|
var n = Math.max(1, Math.ceil(angle * R2D$6 / interval));
|
|
6810
7098
|
var sinAngle = Math.sin(angle);
|
|
6811
7099
|
var points = [];
|
|
@@ -6864,7 +7152,7 @@
|
|
|
6864
7152
|
}
|
|
6865
7153
|
|
|
6866
7154
|
function angularDistance(a, b) {
|
|
6867
|
-
return Math.acos(clamp$
|
|
7155
|
+
return Math.acos(clamp$2(dot$2(degreesToVector$2(a[0], a[1]),
|
|
6868
7156
|
degreesToVector$2(b[0], b[1])), -1, 1));
|
|
6869
7157
|
}
|
|
6870
7158
|
|
|
@@ -6880,7 +7168,7 @@
|
|
|
6880
7168
|
function vectorToDegrees$2(p) {
|
|
6881
7169
|
return [
|
|
6882
7170
|
Math.atan2(p[1], p[0]) * R2D$6,
|
|
6883
|
-
Math.asin(clamp$
|
|
7171
|
+
Math.asin(clamp$2(p[2], -1, 1)) * R2D$6
|
|
6884
7172
|
];
|
|
6885
7173
|
}
|
|
6886
7174
|
|
|
@@ -6935,16 +7223,6 @@
|
|
|
6935
7223
|
return lam;
|
|
6936
7224
|
}
|
|
6937
7225
|
|
|
6938
|
-
function normalizeLongitude$3(lon) {
|
|
6939
|
-
while (lon > 180) lon -= 360;
|
|
6940
|
-
while (lon < -180) lon += 360;
|
|
6941
|
-
return lon;
|
|
6942
|
-
}
|
|
6943
|
-
|
|
6944
|
-
function clamp$4(val, min, max) {
|
|
6945
|
-
return Math.max(min, Math.min(max, val));
|
|
6946
|
-
}
|
|
6947
|
-
|
|
6948
7226
|
/*
|
|
6949
7227
|
* Buckminster Fuller's Airocean arrangement of the icosahedron.
|
|
6950
7228
|
*
|
|
@@ -8150,6 +8428,7 @@
|
|
|
8150
8428
|
* equations, not Imago's power-law approximation.
|
|
8151
8429
|
*/
|
|
8152
8430
|
|
|
8431
|
+
|
|
8153
8432
|
var D2R$2 = Math.PI / 180;
|
|
8154
8433
|
var R2D$2 = 180 / Math.PI;
|
|
8155
8434
|
var HALF_PI = Math.PI / 2;
|
|
@@ -8298,7 +8577,7 @@
|
|
|
8298
8577
|
boundary: sector.sphericalBoundary.map(function(p) {
|
|
8299
8578
|
var q = fromCanonical(p[0], p[1], orientation);
|
|
8300
8579
|
return [
|
|
8301
|
-
normalizeLongitude$
|
|
8580
|
+
normalizeLongitude$1(q[0] * R2D$2 + lon0),
|
|
8302
8581
|
q[1] * R2D$2
|
|
8303
8582
|
];
|
|
8304
8583
|
})
|
|
@@ -8378,7 +8657,7 @@
|
|
|
8378
8657
|
return applyOobTransform(p, facet, oobKey);
|
|
8379
8658
|
});
|
|
8380
8659
|
[0, 1].forEach(function(folded) {
|
|
8381
|
-
var foldedPolygon = clipPolygonAxis
|
|
8660
|
+
var foldedPolygon = clipPolygonAxis(oobPolygon, 0, 0, folded == 0);
|
|
8382
8661
|
if (foldedPolygon.length < 3) return;
|
|
8383
8662
|
foldedPolygon = foldedPolygon.map(function(p) {
|
|
8384
8663
|
return applyFoldTransform(p, folded);
|
|
@@ -8507,26 +8786,26 @@
|
|
|
8507
8786
|
var polygon = clipRasterOobPolygon(vertices, piece.oobKey);
|
|
8508
8787
|
polygon = polygon.map(function(vertex) {
|
|
8509
8788
|
var p = applyOobTransform([vertex.x, vertex.y], facet, piece.oobKey);
|
|
8510
|
-
return copyRasterVertex
|
|
8789
|
+
return copyRasterVertex(vertex, p[0], p[1]);
|
|
8511
8790
|
});
|
|
8512
|
-
polygon = clipRasterPolygonAxis
|
|
8791
|
+
polygon = clipRasterPolygonAxis(
|
|
8513
8792
|
polygon, 'x', 0, piece.folded == 0);
|
|
8514
8793
|
polygon = polygon.map(function(vertex) {
|
|
8515
8794
|
var p = applyFoldTransform([vertex.x, vertex.y], piece.folded);
|
|
8516
|
-
return copyRasterVertex
|
|
8795
|
+
return copyRasterVertex(vertex, p[0], p[1]);
|
|
8517
8796
|
});
|
|
8518
8797
|
polygon = polygon.map(function(vertex) {
|
|
8519
|
-
return copyRasterVertex
|
|
8798
|
+
return copyRasterVertex(
|
|
8520
8799
|
vertex,
|
|
8521
8800
|
vertex.x + LAYOUT_SHIFT +
|
|
8522
8801
|
piece.wrap * 2 * BLOCK_HEIGHT - BLOCK_HEIGHT,
|
|
8523
8802
|
vertex.y + 1.5
|
|
8524
8803
|
);
|
|
8525
8804
|
});
|
|
8526
|
-
polygon = clipRasterPolygonAxis
|
|
8527
|
-
polygon = clipRasterPolygonAxis
|
|
8528
|
-
polygon = clipRasterPolygonAxis
|
|
8529
|
-
polygon = clipRasterPolygonAxis
|
|
8805
|
+
polygon = clipRasterPolygonAxis(polygon, 'x', XMIN, true);
|
|
8806
|
+
polygon = clipRasterPolygonAxis(polygon, 'x', XMAX, false);
|
|
8807
|
+
polygon = clipRasterPolygonAxis(polygon, 'y', YMIN, true);
|
|
8808
|
+
polygon = clipRasterPolygonAxis(polygon, 'y', YMAX, false);
|
|
8530
8809
|
polygon.forEach(function(vertex) {
|
|
8531
8810
|
vertex.x *= EDGE_SCALE;
|
|
8532
8811
|
vertex.y *= EDGE_SCALE;
|
|
@@ -8536,62 +8815,22 @@
|
|
|
8536
8815
|
|
|
8537
8816
|
function clipRasterOobPolygon(polygon, oobKey) {
|
|
8538
8817
|
if (oobKey == 'xpos') {
|
|
8539
|
-
return clipRasterPolygonAxis
|
|
8818
|
+
return clipRasterPolygonAxis(polygon, 'x', 3, true);
|
|
8540
8819
|
}
|
|
8541
8820
|
if (oobKey == 'xneg') {
|
|
8542
|
-
return clipRasterPolygonAxis
|
|
8821
|
+
return clipRasterPolygonAxis(polygon, 'x', -3, false);
|
|
8543
8822
|
}
|
|
8544
8823
|
if (oobKey == 'ypos' || oobKey == 'yneg') {
|
|
8545
|
-
polygon = clipRasterPolygonAxis
|
|
8546
|
-
polygon = clipRasterPolygonAxis
|
|
8547
|
-
return clipRasterPolygonAxis
|
|
8824
|
+
polygon = clipRasterPolygonAxis(polygon, 'x', -3, true);
|
|
8825
|
+
polygon = clipRasterPolygonAxis(polygon, 'x', 3, false);
|
|
8826
|
+
return clipRasterPolygonAxis(
|
|
8548
8827
|
polygon, 'y', oobKey == 'ypos' ? SQRT3$1 : -SQRT3$1,
|
|
8549
8828
|
oobKey == 'ypos');
|
|
8550
8829
|
}
|
|
8551
|
-
polygon = clipRasterPolygonAxis
|
|
8552
|
-
polygon = clipRasterPolygonAxis
|
|
8553
|
-
polygon = clipRasterPolygonAxis
|
|
8554
|
-
return clipRasterPolygonAxis
|
|
8555
|
-
}
|
|
8556
|
-
|
|
8557
|
-
function clipRasterPolygonAxis$1(polygon, axis, value, keepGreater) {
|
|
8558
|
-
var output = [];
|
|
8559
|
-
if (polygon.length === 0) return output;
|
|
8560
|
-
var a = polygon[polygon.length - 1];
|
|
8561
|
-
var aInside = keepGreater ?
|
|
8562
|
-
a[axis] >= value - EPS$1 : a[axis] <= value + EPS$1;
|
|
8563
|
-
polygon.forEach(function(b) {
|
|
8564
|
-
var bInside = keepGreater ?
|
|
8565
|
-
b[axis] >= value - EPS$1 : b[axis] <= value + EPS$1;
|
|
8566
|
-
if (aInside != bInside) {
|
|
8567
|
-
var t = (value - a[axis]) / (b[axis] - a[axis]);
|
|
8568
|
-
var vertex = {
|
|
8569
|
-
x: a.x + (b.x - a.x) * t,
|
|
8570
|
-
y: a.y + (b.y - a.y) * t,
|
|
8571
|
-
sx: a.sx + (b.sx - a.sx) * t,
|
|
8572
|
-
sy: a.sy + (b.sy - a.sy) * t,
|
|
8573
|
-
lon: a.lon + (b.lon - a.lon) * t,
|
|
8574
|
-
lat: a.lat + (b.lat - a.lat) * t
|
|
8575
|
-
};
|
|
8576
|
-
vertex[axis] = value;
|
|
8577
|
-
output.push(vertex);
|
|
8578
|
-
}
|
|
8579
|
-
if (bInside) output.push(copyRasterVertex$1(b, b.x, b.y));
|
|
8580
|
-
a = b;
|
|
8581
|
-
aInside = bInside;
|
|
8582
|
-
});
|
|
8583
|
-
return output;
|
|
8584
|
-
}
|
|
8585
|
-
|
|
8586
|
-
function copyRasterVertex$1(vertex, x, y) {
|
|
8587
|
-
return {
|
|
8588
|
-
x: x,
|
|
8589
|
-
y: y,
|
|
8590
|
-
sx: vertex.sx,
|
|
8591
|
-
sy: vertex.sy,
|
|
8592
|
-
lon: vertex.lon,
|
|
8593
|
-
lat: vertex.lat
|
|
8594
|
-
};
|
|
8830
|
+
polygon = clipRasterPolygonAxis(polygon, 'x', -3, true);
|
|
8831
|
+
polygon = clipRasterPolygonAxis(polygon, 'x', 3, false);
|
|
8832
|
+
polygon = clipRasterPolygonAxis(polygon, 'y', -SQRT3$1, true);
|
|
8833
|
+
return clipRasterPolygonAxis(polygon, 'y', SQRT3$1, false);
|
|
8595
8834
|
}
|
|
8596
8835
|
|
|
8597
8836
|
function applyOobTransform(p, facet, oobKey) {
|
|
@@ -8612,57 +8851,34 @@
|
|
|
8612
8851
|
}
|
|
8613
8852
|
|
|
8614
8853
|
function clipOobPolygon(polygon, oobKey) {
|
|
8615
|
-
if (oobKey == 'xpos') return clipPolygonAxis
|
|
8616
|
-
if (oobKey == 'xneg') return clipPolygonAxis
|
|
8854
|
+
if (oobKey == 'xpos') return clipPolygonAxis(polygon, 0, 3, true);
|
|
8855
|
+
if (oobKey == 'xneg') return clipPolygonAxis(polygon, 0, -3, false);
|
|
8617
8856
|
if (oobKey == 'ypos') {
|
|
8618
|
-
return clipPolygonAxis
|
|
8619
|
-
clipPolygonAxis
|
|
8620
|
-
clipPolygonAxis
|
|
8857
|
+
return clipPolygonAxis(
|
|
8858
|
+
clipPolygonAxis(
|
|
8859
|
+
clipPolygonAxis(polygon, 0, -3, true), 0, 3, false),
|
|
8621
8860
|
1, SQRT3$1, true);
|
|
8622
8861
|
}
|
|
8623
8862
|
if (oobKey == 'yneg') {
|
|
8624
|
-
return clipPolygonAxis
|
|
8625
|
-
clipPolygonAxis
|
|
8626
|
-
clipPolygonAxis
|
|
8863
|
+
return clipPolygonAxis(
|
|
8864
|
+
clipPolygonAxis(
|
|
8865
|
+
clipPolygonAxis(polygon, 0, -3, true), 0, 3, false),
|
|
8627
8866
|
1, -SQRT3$1, false);
|
|
8628
8867
|
}
|
|
8629
8868
|
return clipRectangle(polygon, -3, -SQRT3$1, 3, SQRT3$1);
|
|
8630
8869
|
}
|
|
8631
8870
|
|
|
8632
8871
|
function clipRectangle(polygon, xmin, ymin, xmax, ymax) {
|
|
8633
|
-
polygon = clipPolygonAxis
|
|
8634
|
-
polygon = clipPolygonAxis
|
|
8635
|
-
polygon = clipPolygonAxis
|
|
8636
|
-
return clipPolygonAxis
|
|
8637
|
-
}
|
|
8638
|
-
|
|
8639
|
-
function clipPolygonAxis$1(polygon, axis, value, keepGreater) {
|
|
8640
|
-
var output = [];
|
|
8641
|
-
if (polygon.length === 0) return output;
|
|
8642
|
-
var a = polygon[polygon.length - 1];
|
|
8643
|
-
var aInside = keepGreater ? a[axis] >= value - EPS$1 : a[axis] <= value + EPS$1;
|
|
8644
|
-
polygon.forEach(function(b) {
|
|
8645
|
-
var bInside = keepGreater ? b[axis] >= value - EPS$1 : b[axis] <= value + EPS$1;
|
|
8646
|
-
if (aInside != bInside) {
|
|
8647
|
-
var t = (value - a[axis]) / (b[axis] - a[axis]);
|
|
8648
|
-
var p = [
|
|
8649
|
-
a[0] + (b[0] - a[0]) * t,
|
|
8650
|
-
a[1] + (b[1] - a[1]) * t
|
|
8651
|
-
];
|
|
8652
|
-
p[axis] = value;
|
|
8653
|
-
output.push(p);
|
|
8654
|
-
}
|
|
8655
|
-
if (bInside) output.push(b.concat());
|
|
8656
|
-
a = b;
|
|
8657
|
-
aInside = bInside;
|
|
8658
|
-
});
|
|
8659
|
-
return output;
|
|
8872
|
+
polygon = clipPolygonAxis(polygon, 0, xmin, true);
|
|
8873
|
+
polygon = clipPolygonAxis(polygon, 0, xmax, false);
|
|
8874
|
+
polygon = clipPolygonAxis(polygon, 1, ymin, true);
|
|
8875
|
+
return clipPolygonAxis(polygon, 1, ymax, false);
|
|
8660
8876
|
}
|
|
8661
8877
|
|
|
8662
8878
|
function interpolateSphericalRadians(a, b, interval) {
|
|
8663
8879
|
var av = radiansToVector(a[0], a[1]);
|
|
8664
8880
|
var bv = radiansToVector(b[0], b[1]);
|
|
8665
|
-
var angle = Math.acos(clamp$
|
|
8881
|
+
var angle = Math.acos(clamp$2(dot$1(av, bv), -1, 1));
|
|
8666
8882
|
var count = Math.max(1, Math.ceil(angle / interval));
|
|
8667
8883
|
var sinAngle = Math.sin(angle);
|
|
8668
8884
|
var points = [];
|
|
@@ -8682,7 +8898,7 @@
|
|
|
8682
8898
|
function vectorToRadians(p) {
|
|
8683
8899
|
return [
|
|
8684
8900
|
Math.atan2(p[1], p[0]),
|
|
8685
|
-
Math.asin(clamp$
|
|
8901
|
+
Math.asin(clamp$2(p[2], -1, 1))
|
|
8686
8902
|
];
|
|
8687
8903
|
}
|
|
8688
8904
|
|
|
@@ -8756,8 +8972,8 @@
|
|
|
8756
8972
|
}
|
|
8757
8973
|
x = qx - BLOCK_HEIGHT;
|
|
8758
8974
|
y = qy + 1.5;
|
|
8759
|
-
x = clamp$
|
|
8760
|
-
y = clamp$
|
|
8975
|
+
x = clamp$2(x, XMIN, XMAX);
|
|
8976
|
+
y = clamp$2(y, YMIN, YMAX);
|
|
8761
8977
|
return {
|
|
8762
8978
|
x: x,
|
|
8763
8979
|
y: y,
|
|
@@ -8867,7 +9083,7 @@
|
|
|
8867
9083
|
var v = radiansToVector(lam, phi);
|
|
8868
9084
|
return [
|
|
8869
9085
|
Math.atan2(dot$1(v, orientation.y), dot$1(v, orientation.x)),
|
|
8870
|
-
Math.asin(clamp$
|
|
9086
|
+
Math.asin(clamp$2(dot$1(v, orientation.z), -1, 1))
|
|
8871
9087
|
];
|
|
8872
9088
|
}
|
|
8873
9089
|
|
|
@@ -8878,7 +9094,7 @@
|
|
|
8878
9094
|
orientation.x[1] * v[0] + orientation.y[1] * v[1] + orientation.z[1] * v[2],
|
|
8879
9095
|
orientation.x[2] * v[0] + orientation.y[2] * v[1] + orientation.z[2] * v[2]
|
|
8880
9096
|
];
|
|
8881
|
-
return [Math.atan2(p[1], p[0]), Math.asin(clamp$
|
|
9097
|
+
return [Math.atan2(p[1], p[0]), Math.asin(clamp$2(p[2], -1, 1))];
|
|
8882
9098
|
}
|
|
8883
9099
|
|
|
8884
9100
|
// Adapted from Justin Kunimune's Imago implementation.
|
|
@@ -8892,7 +9108,7 @@
|
|
|
8892
9108
|
lat1 = lat;
|
|
8893
9109
|
lon1 = lon - lon0;
|
|
8894
9110
|
} else {
|
|
8895
|
-
lat1 = Math.asin(clamp$
|
|
9111
|
+
lat1 = Math.asin(clamp$2(
|
|
8896
9112
|
Math.sin(lat0) * Math.sin(lat) +
|
|
8897
9113
|
Math.cos(lat0) * Math.cos(lat) * Math.cos(lon0 - lon),
|
|
8898
9114
|
-1,
|
|
@@ -8903,7 +9119,7 @@
|
|
|
8903
9119
|
(Math.cos(lat0) * Math.sin(lat) -
|
|
8904
9120
|
Math.sin(lat0) * Math.cos(lat) * Math.cos(lon0 - lon)) /
|
|
8905
9121
|
denominator;
|
|
8906
|
-
lon1 = Math.acos(clamp$
|
|
9122
|
+
lon1 = Math.acos(clamp$2(value, -1, 1)) - Math.PI;
|
|
8907
9123
|
if (Math.sin(lon - lon0) > 0) lon1 = -lon1;
|
|
8908
9124
|
}
|
|
8909
9125
|
return [lat1, normalizeRadians(lon1 - theta0)];
|
|
@@ -8913,7 +9129,7 @@
|
|
|
8913
9129
|
var lat0 = pole.lat;
|
|
8914
9130
|
var lon0 = pole.lon;
|
|
8915
9131
|
lon += pole.meridian;
|
|
8916
|
-
var latOut = Math.asin(clamp$
|
|
9132
|
+
var latOut = Math.asin(clamp$2(
|
|
8917
9133
|
Math.sin(lat0) * Math.sin(lat) -
|
|
8918
9134
|
Math.cos(lat0) * Math.cos(lon) * Math.cos(lat),
|
|
8919
9135
|
-1,
|
|
@@ -8925,8 +9141,8 @@
|
|
|
8925
9141
|
} else {
|
|
8926
9142
|
var value = Math.sin(lat) / Math.cos(lat0) / Math.cos(latOut) -
|
|
8927
9143
|
Math.tan(lat0) * Math.tan(latOut);
|
|
8928
|
-
if (Math.sin(lon) > 0) lonOut = lon0 + Math.acos(clamp$
|
|
8929
|
-
else lonOut = lon0 - Math.acos(clamp$
|
|
9144
|
+
if (Math.sin(lon) > 0) lonOut = lon0 + Math.acos(clamp$2(value, -1, 1));
|
|
9145
|
+
else lonOut = lon0 - Math.acos(clamp$2(value, -1, 1));
|
|
8930
9146
|
}
|
|
8931
9147
|
return [latOut, normalizeRadians(lonOut)];
|
|
8932
9148
|
}
|
|
@@ -8949,7 +9165,7 @@
|
|
|
8949
9165
|
var y = edge[0][1] + (edge[1][1] - edge[0][1]) * t;
|
|
8950
9166
|
var p = inverse(x * EDGE_SCALE, y * EDGE_SCALE);
|
|
8951
9167
|
path.push([
|
|
8952
|
-
normalizeLongitude$
|
|
9168
|
+
normalizeLongitude$1(p[0] * R2D$2 + lon0),
|
|
8953
9169
|
p[1] * R2D$2
|
|
8954
9170
|
]);
|
|
8955
9171
|
}
|
|
@@ -9040,20 +9256,194 @@
|
|
|
9040
9256
|
return lam;
|
|
9041
9257
|
}
|
|
9042
9258
|
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
|
|
9046
|
-
|
|
9259
|
+
var Narukawa2022 = /*#__PURE__*/Object.freeze({
|
|
9260
|
+
__proto__: null,
|
|
9261
|
+
getNarukawa2022Engine: getNarukawa2022Engine,
|
|
9262
|
+
registerNarukawa2022Projection: registerNarukawa2022Projection
|
|
9263
|
+
});
|
|
9264
|
+
|
|
9265
|
+
// Lightweight hierarchical profiler.
|
|
9266
|
+
// Intended for ad-hoc performance work on hot pipelines (e.g. addIntersectionCuts).
|
|
9267
|
+
//
|
|
9268
|
+
// Usage:
|
|
9269
|
+
// import { profileStart, profileEnd, profileWrap, ... } from './utils/mapshaper-profile';
|
|
9270
|
+
// profileStart('phase'); doWork(); profileEnd('phase');
|
|
9271
|
+
// var result = profileWrap('phase', () => doWork());
|
|
9272
|
+
//
|
|
9273
|
+
// When disabled (the default) every call short-circuits in ~one comparison; safe
|
|
9274
|
+
// to leave in hot code paths. Enable from the CLI with the `-profile` command,
|
|
9275
|
+
// from JS with enableProfiling(), or by setting the MAPSHAPER_PROFILE env var.
|
|
9276
|
+
//
|
|
9277
|
+
// The profiler tracks a stack of currently-open phases so calls can nest. Each
|
|
9278
|
+
// unique stack path accumulates ms-elapsed and a call count. profileReport()
|
|
9279
|
+
// returns a flat array; formatProfileReport() pretty-prints a tree.
|
|
9280
|
+
|
|
9281
|
+
var ENABLED = false;
|
|
9282
|
+
var ROOT = makeNode('<root>');
|
|
9283
|
+
var STACK = [ROOT];
|
|
9284
|
+
var WALL_START = 0;
|
|
9285
|
+
|
|
9286
|
+
function makeNode(label) {
|
|
9287
|
+
return {
|
|
9288
|
+
label: label,
|
|
9289
|
+
totalMs: 0,
|
|
9290
|
+
selfMs: 0,
|
|
9291
|
+
calls: 0,
|
|
9292
|
+
childMsAccum: 0,
|
|
9293
|
+
children: Object.create(null)
|
|
9294
|
+
};
|
|
9047
9295
|
}
|
|
9048
9296
|
|
|
9049
|
-
function
|
|
9050
|
-
|
|
9297
|
+
function nowMs() {
|
|
9298
|
+
if (typeof process !== 'undefined' && process.hrtime && process.hrtime.bigint) {
|
|
9299
|
+
// bigint -> number division: precision down to 1 microsecond is fine for our needs
|
|
9300
|
+
return Number(process.hrtime.bigint()) / 1e6;
|
|
9301
|
+
}
|
|
9302
|
+
if (typeof performance !== 'undefined' && performance.now) {
|
|
9303
|
+
return performance.now();
|
|
9304
|
+
}
|
|
9305
|
+
return Date.now();
|
|
9051
9306
|
}
|
|
9052
9307
|
|
|
9053
|
-
|
|
9308
|
+
function enableProfiling() {
|
|
9309
|
+
ENABLED = true;
|
|
9310
|
+
WALL_START = nowMs();
|
|
9311
|
+
}
|
|
9312
|
+
|
|
9313
|
+
function disableProfiling() {
|
|
9314
|
+
ENABLED = false;
|
|
9315
|
+
}
|
|
9316
|
+
|
|
9317
|
+
function profileEnabled() {
|
|
9318
|
+
return ENABLED;
|
|
9319
|
+
}
|
|
9320
|
+
|
|
9321
|
+
function profileReset() {
|
|
9322
|
+
ROOT = makeNode('<root>');
|
|
9323
|
+
STACK = [ROOT];
|
|
9324
|
+
WALL_START = ENABLED ? nowMs() : 0;
|
|
9325
|
+
}
|
|
9326
|
+
|
|
9327
|
+
// Open a new phase. Cheap (~one branch) when disabled.
|
|
9328
|
+
function profileStart(label) {
|
|
9329
|
+
if (!ENABLED) return;
|
|
9330
|
+
var parent = STACK[STACK.length - 1];
|
|
9331
|
+
var node = parent.children[label];
|
|
9332
|
+
if (!node) {
|
|
9333
|
+
node = makeNode(label);
|
|
9334
|
+
parent.children[label] = node;
|
|
9335
|
+
}
|
|
9336
|
+
node._t0 = nowMs();
|
|
9337
|
+
STACK.push(node);
|
|
9338
|
+
}
|
|
9339
|
+
|
|
9340
|
+
function profileEnd(label) {
|
|
9341
|
+
if (!ENABLED) return;
|
|
9342
|
+
var node = STACK[STACK.length - 1];
|
|
9343
|
+
if (label && node.label !== label) {
|
|
9344
|
+
// Mismatched labels usually mean an early return forgot to call profileEnd().
|
|
9345
|
+
// Walk up the stack until we find a match, closing the intervening frames.
|
|
9346
|
+
while (STACK.length > 1 && STACK[STACK.length - 1].label !== label) {
|
|
9347
|
+
profileEnd(STACK[STACK.length - 1].label);
|
|
9348
|
+
}
|
|
9349
|
+
node = STACK[STACK.length - 1];
|
|
9350
|
+
if (node.label !== label) return; // give up rather than throw
|
|
9351
|
+
}
|
|
9352
|
+
var elapsed = nowMs() - node._t0;
|
|
9353
|
+
node._t0 = 0;
|
|
9354
|
+
node.totalMs += elapsed;
|
|
9355
|
+
node.calls += 1;
|
|
9356
|
+
STACK.pop();
|
|
9357
|
+
var parent = STACK[STACK.length - 1];
|
|
9358
|
+
parent.childMsAccum += elapsed;
|
|
9359
|
+
}
|
|
9360
|
+
|
|
9361
|
+
// Wrap a function call. Re-throws if the callback throws but still closes the
|
|
9362
|
+
// phase so the stack stays consistent.
|
|
9363
|
+
function profileWrap(label, fn) {
|
|
9364
|
+
if (!ENABLED) return fn();
|
|
9365
|
+
profileStart(label);
|
|
9366
|
+
try {
|
|
9367
|
+
return fn();
|
|
9368
|
+
} finally {
|
|
9369
|
+
profileEnd(label);
|
|
9370
|
+
}
|
|
9371
|
+
}
|
|
9372
|
+
|
|
9373
|
+
// Build a flat tree report: array of {depth, label, totalMs, selfMs, calls}.
|
|
9374
|
+
function profileReport() {
|
|
9375
|
+
var rows = [];
|
|
9376
|
+
function visit(node, depth) {
|
|
9377
|
+
if (depth > 0) {
|
|
9378
|
+
rows.push({
|
|
9379
|
+
depth: depth - 1,
|
|
9380
|
+
label: node.label,
|
|
9381
|
+
totalMs: node.totalMs,
|
|
9382
|
+
selfMs: Math.max(0, node.totalMs - node.childMsAccum),
|
|
9383
|
+
calls: node.calls
|
|
9384
|
+
});
|
|
9385
|
+
}
|
|
9386
|
+
var keys = Object.keys(node.children);
|
|
9387
|
+
keys.sort(function(a, b) {
|
|
9388
|
+
return node.children[b].totalMs - node.children[a].totalMs;
|
|
9389
|
+
});
|
|
9390
|
+
for (var i = 0; i < keys.length; i++) {
|
|
9391
|
+
visit(node.children[keys[i]], depth + 1);
|
|
9392
|
+
}
|
|
9393
|
+
}
|
|
9394
|
+
visit(ROOT, 0);
|
|
9395
|
+
return rows;
|
|
9396
|
+
}
|
|
9397
|
+
|
|
9398
|
+
function profileWallElapsedMs() {
|
|
9399
|
+
return ENABLED && WALL_START ? nowMs() - WALL_START : 0;
|
|
9400
|
+
}
|
|
9401
|
+
|
|
9402
|
+
// Pretty-print the current report as a column-aligned tree.
|
|
9403
|
+
function formatProfileReport(opts) {
|
|
9404
|
+
var rows = profileReport();
|
|
9405
|
+
if (rows.length === 0) return '(profile is empty)';
|
|
9406
|
+
opts = opts || {};
|
|
9407
|
+
var indent = ' ';
|
|
9408
|
+
var lines = [];
|
|
9409
|
+
// header
|
|
9410
|
+
lines.push(['phase', 'total ms', 'self ms', 'calls'].join('\t'));
|
|
9411
|
+
for (var i = 0; i < rows.length; i++) {
|
|
9412
|
+
var r = rows[i];
|
|
9413
|
+
var label = '';
|
|
9414
|
+
for (var j = 0; j < r.depth; j++) label += indent;
|
|
9415
|
+
label += r.label;
|
|
9416
|
+
lines.push([
|
|
9417
|
+
label,
|
|
9418
|
+
r.totalMs.toFixed(2),
|
|
9419
|
+
r.selfMs.toFixed(2),
|
|
9420
|
+
String(r.calls)
|
|
9421
|
+
].join('\t'));
|
|
9422
|
+
}
|
|
9423
|
+
if (opts.includeWall) {
|
|
9424
|
+
lines.push('');
|
|
9425
|
+
lines.push('wall elapsed ms: ' + profileWallElapsedMs().toFixed(2));
|
|
9426
|
+
}
|
|
9427
|
+
return lines.join('\n');
|
|
9428
|
+
}
|
|
9429
|
+
|
|
9430
|
+
// Honour an env var so users (and CI harnesses) can opt in without code changes.
|
|
9431
|
+
if (typeof process !== 'undefined' && process.env && process.env.MAPSHAPER_PROFILE) {
|
|
9432
|
+
enableProfiling();
|
|
9433
|
+
}
|
|
9434
|
+
|
|
9435
|
+
var Profile = /*#__PURE__*/Object.freeze({
|
|
9054
9436
|
__proto__: null,
|
|
9055
|
-
|
|
9056
|
-
|
|
9437
|
+
disableProfiling: disableProfiling,
|
|
9438
|
+
enableProfiling: enableProfiling,
|
|
9439
|
+
formatProfileReport: formatProfileReport,
|
|
9440
|
+
profileEnabled: profileEnabled,
|
|
9441
|
+
profileEnd: profileEnd,
|
|
9442
|
+
profileReport: profileReport,
|
|
9443
|
+
profileReset: profileReset,
|
|
9444
|
+
profileStart: profileStart,
|
|
9445
|
+
profileWallElapsedMs: profileWallElapsedMs,
|
|
9446
|
+
profileWrap: profileWrap
|
|
9057
9447
|
});
|
|
9058
9448
|
|
|
9059
9449
|
/*
|
|
@@ -9086,6 +9476,32 @@
|
|
|
9086
9476
|
var RECT_YMAX = 0;
|
|
9087
9477
|
var EPS = 1e-12;
|
|
9088
9478
|
var MIN_PIECE_AREA = 1e-6;
|
|
9479
|
+
|
|
9480
|
+
// Frame-cut tracing parameters (see createFrameCutPaths).
|
|
9481
|
+
//
|
|
9482
|
+
// FRAME_INSET is how far inside the frame the traced sampling lines run, and it
|
|
9483
|
+
// is bounded on both sides. Measured over 18003 samples with a 6000-sample
|
|
9484
|
+
// edge, the safe window is about [4e-8, 1.3e-7] and this value is its centre:
|
|
9485
|
+
//
|
|
9486
|
+
// * Too small and invertLeeRaw becomes ill-conditioned, because the frame edge
|
|
9487
|
+
// coincides with a Lee face boundary. The share of samples that fail to
|
|
9488
|
+
// invert climbs from 0.2% inside the window to 4% at 3e-8 and 14% at 1e-8,
|
|
9489
|
+
// and *which* samples fail depends on last-bit floating-point behaviour, so
|
|
9490
|
+
// different JS engines traced different seams from the same input.
|
|
9491
|
+
// * Too large and the strip of sphere between the traced seam and the true
|
|
9492
|
+
// frame edge survives the cut, leaving a hairline connection that shows up as
|
|
9493
|
+
// a long chord running along the frame edge. This appears by 1.6e-7.
|
|
9494
|
+
//
|
|
9495
|
+
// The 0.2% of samples that fail inside the window are the four facet vertices,
|
|
9496
|
+
// which genuinely have no inverse.
|
|
9497
|
+
var FRAME_INSET = 8e-8;
|
|
9498
|
+
// Guards the lower bound above. Inside the window the failure rate is 0.2%, so
|
|
9499
|
+
// this only trips on a regression, and the resulting map would be mis-cut.
|
|
9500
|
+
var MAX_FAILED_SAMPLE_RATIO = 0.02;
|
|
9501
|
+
// Samples per frame edge. This sets the resolution of the cut along the frame
|
|
9502
|
+
// (about 8 km on an Earth-sized map).
|
|
9503
|
+
var FRAME_SAMPLES = 6000;
|
|
9504
|
+
|
|
9089
9505
|
var engines = {};
|
|
9090
9506
|
|
|
9091
9507
|
var TETRAHEDRON_VERTICES = [
|
|
@@ -9208,14 +9624,20 @@
|
|
|
9208
9624
|
|
|
9209
9625
|
// Internal inverse used to trace the geographic cut corresponding to the
|
|
9210
9626
|
// rectangular frame. The public projection remains forward-only.
|
|
9627
|
+
//
|
|
9628
|
+
// Layout pieces can overlap along their shared edges, so a candidate is only
|
|
9629
|
+
// accepted once it round-trips through forward().
|
|
9211
9630
|
function inverse(x, y) {
|
|
9212
9631
|
var q = uncenterOutputPoint([x, y]);
|
|
9213
9632
|
for (var i = 0; i < rasterPieces.length; i++) {
|
|
9214
9633
|
var piece = rasterPieces[i];
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
9634
|
+
var bounds = piece.bounds;
|
|
9635
|
+
if (x < bounds[0] || x > bounds[2] || y < bounds[1] || y > bounds[3]) {
|
|
9636
|
+
continue;
|
|
9637
|
+
}
|
|
9638
|
+
if (!pointInRing$2([x, y], piece.boundary)) continue;
|
|
9639
|
+
var p = applyMatrix(piece.inverse_matrix, q);
|
|
9640
|
+
if (getLayoutCopy(p).id != piece.copy) continue;
|
|
9219
9641
|
p = denormalizeBasePoint(p, normalization);
|
|
9220
9642
|
var spherical = base.invertFace(p[0], p[1], piece.face);
|
|
9221
9643
|
if (!spherical) continue;
|
|
@@ -9278,9 +9700,12 @@
|
|
|
9278
9700
|
paths: seam.paths
|
|
9279
9701
|
};
|
|
9280
9702
|
});
|
|
9703
|
+
var frameCut = createFrameCutPaths(inverse, lon0);
|
|
9281
9704
|
seams.push({
|
|
9282
9705
|
type: 'cut',
|
|
9283
|
-
paths:
|
|
9706
|
+
paths: frameCut.paths,
|
|
9707
|
+
// How much of the frame the trace resolved; see FRAME_INSET.
|
|
9708
|
+
trace_stats: frameCut.stats
|
|
9284
9709
|
});
|
|
9285
9710
|
seamCache.set(lon0, seams);
|
|
9286
9711
|
}
|
|
@@ -9390,13 +9815,16 @@
|
|
|
9390
9815
|
projected = clipPolygonAxis(projected, 1, RECT_YMAX, false);
|
|
9391
9816
|
if (projected.length < 3 ||
|
|
9392
9817
|
Math.abs(getRingArea$2(projected)) < MIN_PIECE_AREA) return;
|
|
9818
|
+
var boundary = projected.map(centerOutputPoint);
|
|
9393
9819
|
pieces.push({
|
|
9394
9820
|
id: pieces.length,
|
|
9395
9821
|
face: region.id,
|
|
9396
9822
|
source_region: region.id,
|
|
9397
9823
|
copy: copy.id,
|
|
9398
9824
|
matrix: matrix,
|
|
9399
|
-
|
|
9825
|
+
inverse_matrix: invertMatrix(matrix),
|
|
9826
|
+
bounds: getRingBounds(boundary),
|
|
9827
|
+
boundary: boundary
|
|
9400
9828
|
});
|
|
9401
9829
|
});
|
|
9402
9830
|
});
|
|
@@ -9438,40 +9866,59 @@
|
|
|
9438
9866
|
};
|
|
9439
9867
|
}
|
|
9440
9868
|
|
|
9869
|
+
// Trace the geographic curves that the rectangular frame corresponds to. The
|
|
9870
|
+
// repeated tetrahedral net is continuous inside the frame, so its only
|
|
9871
|
+
// geographic cuts are the three independent sides of the periodic rectangle.
|
|
9872
|
+
//
|
|
9873
|
+
// Sampling is uniform in frame coordinates rather than adaptive. The traced
|
|
9874
|
+
// curve becomes the centreline of the seam mask, so the polygon edges left
|
|
9875
|
+
// behind by the cut inherit this spacing where they run along the frame; the
|
|
9876
|
+
// even resolution is what lets splitPolygonFrameChords recognize a run along a
|
|
9877
|
+
// frame side. Sampling by geographic curvature instead concentrates vertices
|
|
9878
|
+
// where the map curves and leaves the frame edges coarse, which reintroduces
|
|
9879
|
+
// the long chords along the frame that the cut is meant to remove.
|
|
9441
9880
|
function createFrameCutPaths(inverse, lon0) {
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
var n = 6000;
|
|
9445
|
-
// Stay far enough inside the frame to avoid browser-specific inverse
|
|
9446
|
-
// branches at the exact Lee face boundary.
|
|
9447
|
-
var epsilon = 5e-8;
|
|
9881
|
+
profileStart('lee.traceFrameCuts');
|
|
9882
|
+
var e = FRAME_INSET;
|
|
9448
9883
|
var xmin = centerOutputPoint([RECT_XMIN, 0])[0];
|
|
9449
9884
|
var xmax = centerOutputPoint([RECT_XMAX, 0])[0];
|
|
9450
9885
|
var ymin = centerOutputPoint([0, RECT_YMIN])[1];
|
|
9451
9886
|
var ymax = centerOutputPoint([0, RECT_YMAX])[1];
|
|
9452
9887
|
var edges = [
|
|
9453
|
-
[[xmin +
|
|
9454
|
-
[[xmin, ymax -
|
|
9455
|
-
[[xmin, ymin +
|
|
9888
|
+
[[xmin + e, ymin], [xmin + e, ymax]],
|
|
9889
|
+
[[xmin, ymax - e], [xmax, ymax - e]],
|
|
9890
|
+
[[xmin, ymin + e], [xmax, ymin + e]]
|
|
9456
9891
|
];
|
|
9457
|
-
|
|
9892
|
+
var stats = {sample_count: 0, failed_count: 0};
|
|
9893
|
+
var paths = edges.reduce(function(memo, edge) {
|
|
9458
9894
|
var path = [];
|
|
9459
|
-
for (var i = 0; i <=
|
|
9460
|
-
var t = i /
|
|
9461
|
-
var
|
|
9462
|
-
|
|
9463
|
-
|
|
9464
|
-
|
|
9465
|
-
|
|
9466
|
-
|
|
9467
|
-
|
|
9468
|
-
|
|
9895
|
+
for (var i = 0; i <= FRAME_SAMPLES; i++) {
|
|
9896
|
+
var t = i / FRAME_SAMPLES;
|
|
9897
|
+
var p = inverse(
|
|
9898
|
+
edge[0][0] + (edge[1][0] - edge[0][0]) * t,
|
|
9899
|
+
edge[0][1] + (edge[1][1] - edge[0][1]) * t
|
|
9900
|
+
);
|
|
9901
|
+
stats.sample_count++;
|
|
9902
|
+
// Samples without an inverse occur only at the facet vertices, which
|
|
9903
|
+
// both neighbours converge on, so the chord across the gap is short.
|
|
9904
|
+
if (!p) {
|
|
9905
|
+
stats.failed_count++;
|
|
9906
|
+
continue;
|
|
9907
|
+
}
|
|
9908
|
+
path.push([normalizeLongitude$1(p[0] * R2D$1 + lon0), p[1] * R2D$1]);
|
|
9469
9909
|
}
|
|
9470
9910
|
return memo.concat(splitPathAtAntimeridian$1(path));
|
|
9471
9911
|
}, []).map(function(path) {
|
|
9472
9912
|
path.mask_width = 4e-5;
|
|
9473
9913
|
return path;
|
|
9474
9914
|
});
|
|
9915
|
+
profileEnd('lee.traceFrameCuts');
|
|
9916
|
+
if (stats.failed_count > stats.sample_count * MAX_FAILED_SAMPLE_RATIO) {
|
|
9917
|
+
error('Unable to trace the projection frame: ' + stats.failed_count +
|
|
9918
|
+
' of ' + stats.sample_count +
|
|
9919
|
+
' frame samples had no inverse (see FRAME_INSET).');
|
|
9920
|
+
}
|
|
9921
|
+
return {paths: paths, stats: stats};
|
|
9475
9922
|
}
|
|
9476
9923
|
|
|
9477
9924
|
function splitPathAtAntimeridian$1(path) {
|
|
@@ -9577,60 +10024,77 @@
|
|
|
9577
10024
|
return project;
|
|
9578
10025
|
}
|
|
9579
10026
|
|
|
10027
|
+
// leeRaw runs tens of times per Newton step in invertLeeRaw, so its constant
|
|
10028
|
+
// tables and the three cube roots of unity are built once here rather than on
|
|
10029
|
+
// every call, and the Horner loops below accumulate into scalars instead of
|
|
10030
|
+
// allocating a complex pair per term. The arithmetic is unchanged.
|
|
10031
|
+
var LEE_W1 = 1.4021821053254548;
|
|
10032
|
+
var LEE_COEFFICIENTS = [
|
|
10033
|
+
1.15470053837925, 0.192450089729875, 0.0481125224324687,
|
|
10034
|
+
0.010309826235529, 3.34114739114366e-4, -0.00150351632601465,
|
|
10035
|
+
-0.0012304417796231, -675190201960282e-18,
|
|
10036
|
+
-284084537293856e-18, -821205120500051e-19,
|
|
10037
|
+
-159257630018706e-20, 1.91691805888369e-5,
|
|
10038
|
+
1.73095888028726e-5, 1.03865580818367e-5,
|
|
10039
|
+
4.70614523937179e-6, 1.4413500104181e-6,
|
|
10040
|
+
1.92757960170179e-8, -3.82869799649063e-7,
|
|
10041
|
+
-3.57526015225576e-7, -2.2175964844211e-7
|
|
10042
|
+
];
|
|
10043
|
+
var LEE_H0 = [1, 1 / 8, 3 / 56, 1 / 32, 35 / 1664, 63 / 4096, 231 / 19456];
|
|
10044
|
+
var LEE_ROTATIONS = [0, 1, 2].map(function(i) {
|
|
10045
|
+
return complexPower([-0.5, SQRT3 / 2], [i, 0]);
|
|
10046
|
+
});
|
|
10047
|
+
|
|
9580
10048
|
function leeRaw(lam, phi) {
|
|
9581
|
-
var
|
|
9582
|
-
var z =
|
|
9583
|
-
var
|
|
9584
|
-
|
|
9585
|
-
});
|
|
10049
|
+
var s = stereographicRaw(lam, phi);
|
|
10050
|
+
var z = [s[0] * SQRT2, s[1] * SQRT2];
|
|
10051
|
+
var i, j;
|
|
10052
|
+
// Pick the sector whose rotation maximizes the real part of rot * z.
|
|
9586
10053
|
var sector = 0;
|
|
9587
|
-
|
|
9588
|
-
|
|
9589
|
-
|
|
10054
|
+
var best = z[0] * LEE_ROTATIONS[0][0] - z[1] * LEE_ROTATIONS[0][1];
|
|
10055
|
+
for (i = 1; i < LEE_ROTATIONS.length; i++) {
|
|
10056
|
+
var re = z[0] * LEE_ROTATIONS[i][0] - z[1] * LEE_ROTATIONS[i][1];
|
|
10057
|
+
if (re > best) {
|
|
10058
|
+
best = re;
|
|
10059
|
+
sector = i;
|
|
10060
|
+
}
|
|
9590
10061
|
}
|
|
9591
|
-
var rot =
|
|
10062
|
+
var rot = LEE_ROTATIONS[sector];
|
|
9592
10063
|
var n = complexNorm(z);
|
|
9593
10064
|
var h = [0, 0];
|
|
9594
10065
|
var k = [0, 0];
|
|
9595
10066
|
|
|
9596
10067
|
if (n > 0.3) {
|
|
9597
10068
|
var y = complexSubtract([1, 0], complexMultiply(rot, z));
|
|
9598
|
-
var
|
|
9599
|
-
var
|
|
9600
|
-
|
|
9601
|
-
0
|
|
9602
|
-
|
|
9603
|
-
|
|
9604
|
-
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
-3.57526015225576e-7, -2.2175964844211e-7
|
|
9609
|
-
];
|
|
9610
|
-
var g = [0, 0];
|
|
9611
|
-
for (i = coefficients.length - 1; i >= 0; i--) {
|
|
9612
|
-
g = complexAdd([coefficients[i], 0], complexMultiply(g, y));
|
|
9613
|
-
}
|
|
9614
|
-
k = complexSubtract([w1, 0], complexMultiply(complexPower(y, 0.5), g));
|
|
10069
|
+
var g0 = 0;
|
|
10070
|
+
var g1 = 0;
|
|
10071
|
+
for (i = LEE_COEFFICIENTS.length - 1; i >= 0; i--) {
|
|
10072
|
+
j = g1 * y[0] + g0 * y[1];
|
|
10073
|
+
g0 = LEE_COEFFICIENTS[i] + (g0 * y[0] - g1 * y[1]);
|
|
10074
|
+
g1 = j;
|
|
10075
|
+
}
|
|
10076
|
+
k = complexSubtract(
|
|
10077
|
+
[LEE_W1, 0],
|
|
10078
|
+
complexMultiply(complexPower(y, 0.5), [g0, g1]));
|
|
9615
10079
|
k = complexMultiply(complexMultiply(k, rot), rot);
|
|
9616
10080
|
}
|
|
9617
10081
|
|
|
9618
10082
|
if (n < 0.5) {
|
|
9619
|
-
var h0 = [1, 1 / 8, 3 / 56, 1 / 32, 35 / 1664, 63 / 4096, 231 / 19456];
|
|
9620
10083
|
var z3 = complexPower(z, [3, 0]);
|
|
9621
|
-
|
|
9622
|
-
|
|
10084
|
+
var h0 = 0;
|
|
10085
|
+
var h1 = 0;
|
|
10086
|
+
for (i = LEE_H0.length - 1; i >= 0; i--) {
|
|
10087
|
+
j = h1 * z3[0] + h0 * z3[1];
|
|
10088
|
+
h0 = LEE_H0[i] + (h0 * z3[0] - h1 * z3[1]);
|
|
10089
|
+
h1 = j;
|
|
9623
10090
|
}
|
|
9624
|
-
h = complexMultiply(
|
|
10091
|
+
h = complexMultiply([h0, h1], z);
|
|
9625
10092
|
}
|
|
9626
10093
|
|
|
9627
10094
|
if (n < 0.3) return h;
|
|
9628
10095
|
if (n > 0.5) return k;
|
|
9629
10096
|
var t = (n - 0.3) / 0.2;
|
|
9630
|
-
return
|
|
9631
|
-
complexMultiply(k, [t, 0]),
|
|
9632
|
-
complexMultiply(h, [1 - t, 0])
|
|
9633
|
-
);
|
|
10097
|
+
return [k[0] * t + h[0] * (1 - t), k[1] * t + h[1] * (1 - t)];
|
|
9634
10098
|
}
|
|
9635
10099
|
|
|
9636
10100
|
function invertLeeRaw(x, y) {
|
|
@@ -9680,10 +10144,6 @@
|
|
|
9680
10144
|
return [k * cosPhi * Math.sin(lam), k * Math.sin(phi)];
|
|
9681
10145
|
}
|
|
9682
10146
|
|
|
9683
|
-
function complexAdd(a, b) {
|
|
9684
|
-
return [a[0] + b[0], a[1] + b[1]];
|
|
9685
|
-
}
|
|
9686
|
-
|
|
9687
10147
|
function complexSubtract(a, b) {
|
|
9688
10148
|
return [a[0] - b[0], a[1] - b[1]];
|
|
9689
10149
|
}
|
|
@@ -9711,13 +10171,6 @@
|
|
|
9711
10171
|
return [magnitude * Math.cos(angle), magnitude * Math.sin(angle)];
|
|
9712
10172
|
}
|
|
9713
10173
|
|
|
9714
|
-
function applyMatrix(m, p) {
|
|
9715
|
-
return [
|
|
9716
|
-
m[0] * p[0] + m[1] * p[1] + m[2],
|
|
9717
|
-
m[3] * p[0] + m[4] * p[1] + m[5]
|
|
9718
|
-
];
|
|
9719
|
-
}
|
|
9720
|
-
|
|
9721
10174
|
function shiftMatrixX(matrix, offset) {
|
|
9722
10175
|
var shifted = matrix.concat();
|
|
9723
10176
|
shifted[2] += offset;
|
|
@@ -9730,84 +10183,15 @@
|
|
|
9730
10183
|
return x;
|
|
9731
10184
|
}
|
|
9732
10185
|
|
|
9733
|
-
function
|
|
9734
|
-
var
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
(
|
|
9739
|
-
|
|
9740
|
-
m[0] / det,
|
|
9741
|
-
(m[3] * m[2] - m[0] * m[5]) / det
|
|
9742
|
-
];
|
|
9743
|
-
}
|
|
9744
|
-
|
|
9745
|
-
function clipPolygonAxis(polygon, axis, value, keepGreater) {
|
|
9746
|
-
var output = [];
|
|
9747
|
-
if (!polygon.length) return output;
|
|
9748
|
-
var a = polygon[polygon.length - 1];
|
|
9749
|
-
var aInside = keepGreater ? a[axis] >= value - EPS : a[axis] <= value + EPS;
|
|
9750
|
-
polygon.forEach(function(b) {
|
|
9751
|
-
var bInside = keepGreater ? b[axis] >= value - EPS : b[axis] <= value + EPS;
|
|
9752
|
-
if (aInside != bInside) {
|
|
9753
|
-
var t = (value - a[axis]) / (b[axis] - a[axis]);
|
|
9754
|
-
var p = [
|
|
9755
|
-
a[0] + (b[0] - a[0]) * t,
|
|
9756
|
-
a[1] + (b[1] - a[1]) * t
|
|
9757
|
-
];
|
|
9758
|
-
p[axis] = value;
|
|
9759
|
-
output.push(p);
|
|
9760
|
-
}
|
|
9761
|
-
if (bInside) output.push(b.concat());
|
|
9762
|
-
a = b;
|
|
9763
|
-
aInside = bInside;
|
|
9764
|
-
});
|
|
9765
|
-
return output;
|
|
9766
|
-
}
|
|
9767
|
-
|
|
9768
|
-
function clipRasterPolygonAxis(polygon, axis, value, keepGreater) {
|
|
9769
|
-
var output = [];
|
|
9770
|
-
if (!polygon.length) return output;
|
|
9771
|
-
var a = polygon[polygon.length - 1];
|
|
9772
|
-
var aInside = keepGreater ? a[axis] >= value - EPS : a[axis] <= value + EPS;
|
|
9773
|
-
polygon.forEach(function(b) {
|
|
9774
|
-
var bInside = keepGreater ? b[axis] >= value - EPS : b[axis] <= value + EPS;
|
|
9775
|
-
if (aInside != bInside) {
|
|
9776
|
-
var t = (value - a[axis]) / (b[axis] - a[axis]);
|
|
9777
|
-
var vertex = {
|
|
9778
|
-
x: a.x + (b.x - a.x) * t,
|
|
9779
|
-
y: a.y + (b.y - a.y) * t,
|
|
9780
|
-
sx: a.sx + (b.sx - a.sx) * t,
|
|
9781
|
-
sy: a.sy + (b.sy - a.sy) * t,
|
|
9782
|
-
lon: interpolateLongitude(a.lon, b.lon, t),
|
|
9783
|
-
lat: a.lat + (b.lat - a.lat) * t
|
|
9784
|
-
};
|
|
9785
|
-
vertex[axis] = value;
|
|
9786
|
-
output.push(vertex);
|
|
9787
|
-
}
|
|
9788
|
-
if (bInside) output.push(copyRasterVertex(b, b.x, b.y));
|
|
9789
|
-
a = b;
|
|
9790
|
-
aInside = bInside;
|
|
10186
|
+
function getRingBounds(ring) {
|
|
10187
|
+
var bounds = [Infinity, Infinity, -Infinity, -Infinity];
|
|
10188
|
+
ring.forEach(function(p) {
|
|
10189
|
+
if (p[0] < bounds[0]) bounds[0] = p[0];
|
|
10190
|
+
if (p[1] < bounds[1]) bounds[1] = p[1];
|
|
10191
|
+
if (p[0] > bounds[2]) bounds[2] = p[0];
|
|
10192
|
+
if (p[1] > bounds[3]) bounds[3] = p[1];
|
|
9791
10193
|
});
|
|
9792
|
-
return
|
|
9793
|
-
}
|
|
9794
|
-
|
|
9795
|
-
function copyRasterVertex(vertex, x, y) {
|
|
9796
|
-
return {
|
|
9797
|
-
x: x,
|
|
9798
|
-
y: y,
|
|
9799
|
-
sx: vertex.sx,
|
|
9800
|
-
sy: vertex.sy,
|
|
9801
|
-
lon: vertex.lon,
|
|
9802
|
-
lat: vertex.lat
|
|
9803
|
-
};
|
|
9804
|
-
}
|
|
9805
|
-
|
|
9806
|
-
function interpolateLongitude(a, b, t) {
|
|
9807
|
-
var delta = b - a;
|
|
9808
|
-
if (delta > 180) delta -= 360;
|
|
9809
|
-
if (delta < -180) delta += 360;
|
|
9810
|
-
return normalizeLongitude$1(a + delta * t);
|
|
10194
|
+
return bounds;
|
|
9811
10195
|
}
|
|
9812
10196
|
|
|
9813
10197
|
function getRingArea$2(ring) {
|
|
@@ -9863,16 +10247,6 @@
|
|
|
9863
10247
|
return Math.abs(a[0] - b[0]) < EPS && Math.abs(a[1] - b[1]) < EPS;
|
|
9864
10248
|
}
|
|
9865
10249
|
|
|
9866
|
-
function normalizeLongitude$1(lon) {
|
|
9867
|
-
lon = (lon + 180) % 360;
|
|
9868
|
-
if (lon < 0) lon += 360;
|
|
9869
|
-
return lon - 180;
|
|
9870
|
-
}
|
|
9871
|
-
|
|
9872
|
-
function clamp$2(value, min, max) {
|
|
9873
|
-
return Math.max(min, Math.min(max, value));
|
|
9874
|
-
}
|
|
9875
|
-
|
|
9876
10250
|
var mproj$1 = require$1('mproj');
|
|
9877
10251
|
registerDymaxionProjections(mproj$1);
|
|
9878
10252
|
registerButterflyProjections(mproj$1);
|
|
@@ -12190,190 +12564,6 @@
|
|
|
12190
12564
|
};
|
|
12191
12565
|
}
|
|
12192
12566
|
|
|
12193
|
-
// Lightweight hierarchical profiler.
|
|
12194
|
-
// Intended for ad-hoc performance work on hot pipelines (e.g. addIntersectionCuts).
|
|
12195
|
-
//
|
|
12196
|
-
// Usage:
|
|
12197
|
-
// import { profileStart, profileEnd, profileWrap, ... } from './utils/mapshaper-profile';
|
|
12198
|
-
// profileStart('phase'); doWork(); profileEnd('phase');
|
|
12199
|
-
// var result = profileWrap('phase', () => doWork());
|
|
12200
|
-
//
|
|
12201
|
-
// When disabled (the default) every call short-circuits in ~one comparison; safe
|
|
12202
|
-
// to leave in hot code paths. Enable from the CLI with the `-profile` command,
|
|
12203
|
-
// from JS with enableProfiling(), or by setting the MAPSHAPER_PROFILE env var.
|
|
12204
|
-
//
|
|
12205
|
-
// The profiler tracks a stack of currently-open phases so calls can nest. Each
|
|
12206
|
-
// unique stack path accumulates ms-elapsed and a call count. profileReport()
|
|
12207
|
-
// returns a flat array; formatProfileReport() pretty-prints a tree.
|
|
12208
|
-
|
|
12209
|
-
var ENABLED = false;
|
|
12210
|
-
var ROOT = makeNode('<root>');
|
|
12211
|
-
var STACK = [ROOT];
|
|
12212
|
-
var WALL_START = 0;
|
|
12213
|
-
|
|
12214
|
-
function makeNode(label) {
|
|
12215
|
-
return {
|
|
12216
|
-
label: label,
|
|
12217
|
-
totalMs: 0,
|
|
12218
|
-
selfMs: 0,
|
|
12219
|
-
calls: 0,
|
|
12220
|
-
childMsAccum: 0,
|
|
12221
|
-
children: Object.create(null)
|
|
12222
|
-
};
|
|
12223
|
-
}
|
|
12224
|
-
|
|
12225
|
-
function nowMs() {
|
|
12226
|
-
if (typeof process !== 'undefined' && process.hrtime && process.hrtime.bigint) {
|
|
12227
|
-
// bigint -> number division: precision down to 1 microsecond is fine for our needs
|
|
12228
|
-
return Number(process.hrtime.bigint()) / 1e6;
|
|
12229
|
-
}
|
|
12230
|
-
if (typeof performance !== 'undefined' && performance.now) {
|
|
12231
|
-
return performance.now();
|
|
12232
|
-
}
|
|
12233
|
-
return Date.now();
|
|
12234
|
-
}
|
|
12235
|
-
|
|
12236
|
-
function enableProfiling() {
|
|
12237
|
-
ENABLED = true;
|
|
12238
|
-
WALL_START = nowMs();
|
|
12239
|
-
}
|
|
12240
|
-
|
|
12241
|
-
function disableProfiling() {
|
|
12242
|
-
ENABLED = false;
|
|
12243
|
-
}
|
|
12244
|
-
|
|
12245
|
-
function profileEnabled() {
|
|
12246
|
-
return ENABLED;
|
|
12247
|
-
}
|
|
12248
|
-
|
|
12249
|
-
function profileReset() {
|
|
12250
|
-
ROOT = makeNode('<root>');
|
|
12251
|
-
STACK = [ROOT];
|
|
12252
|
-
WALL_START = ENABLED ? nowMs() : 0;
|
|
12253
|
-
}
|
|
12254
|
-
|
|
12255
|
-
// Open a new phase. Cheap (~one branch) when disabled.
|
|
12256
|
-
function profileStart(label) {
|
|
12257
|
-
if (!ENABLED) return;
|
|
12258
|
-
var parent = STACK[STACK.length - 1];
|
|
12259
|
-
var node = parent.children[label];
|
|
12260
|
-
if (!node) {
|
|
12261
|
-
node = makeNode(label);
|
|
12262
|
-
parent.children[label] = node;
|
|
12263
|
-
}
|
|
12264
|
-
node._t0 = nowMs();
|
|
12265
|
-
STACK.push(node);
|
|
12266
|
-
}
|
|
12267
|
-
|
|
12268
|
-
function profileEnd(label) {
|
|
12269
|
-
if (!ENABLED) return;
|
|
12270
|
-
var node = STACK[STACK.length - 1];
|
|
12271
|
-
if (label && node.label !== label) {
|
|
12272
|
-
// Mismatched labels usually mean an early return forgot to call profileEnd().
|
|
12273
|
-
// Walk up the stack until we find a match, closing the intervening frames.
|
|
12274
|
-
while (STACK.length > 1 && STACK[STACK.length - 1].label !== label) {
|
|
12275
|
-
profileEnd(STACK[STACK.length - 1].label);
|
|
12276
|
-
}
|
|
12277
|
-
node = STACK[STACK.length - 1];
|
|
12278
|
-
if (node.label !== label) return; // give up rather than throw
|
|
12279
|
-
}
|
|
12280
|
-
var elapsed = nowMs() - node._t0;
|
|
12281
|
-
node._t0 = 0;
|
|
12282
|
-
node.totalMs += elapsed;
|
|
12283
|
-
node.calls += 1;
|
|
12284
|
-
STACK.pop();
|
|
12285
|
-
var parent = STACK[STACK.length - 1];
|
|
12286
|
-
parent.childMsAccum += elapsed;
|
|
12287
|
-
}
|
|
12288
|
-
|
|
12289
|
-
// Wrap a function call. Re-throws if the callback throws but still closes the
|
|
12290
|
-
// phase so the stack stays consistent.
|
|
12291
|
-
function profileWrap(label, fn) {
|
|
12292
|
-
if (!ENABLED) return fn();
|
|
12293
|
-
profileStart(label);
|
|
12294
|
-
try {
|
|
12295
|
-
return fn();
|
|
12296
|
-
} finally {
|
|
12297
|
-
profileEnd(label);
|
|
12298
|
-
}
|
|
12299
|
-
}
|
|
12300
|
-
|
|
12301
|
-
// Build a flat tree report: array of {depth, label, totalMs, selfMs, calls}.
|
|
12302
|
-
function profileReport() {
|
|
12303
|
-
var rows = [];
|
|
12304
|
-
function visit(node, depth) {
|
|
12305
|
-
if (depth > 0) {
|
|
12306
|
-
rows.push({
|
|
12307
|
-
depth: depth - 1,
|
|
12308
|
-
label: node.label,
|
|
12309
|
-
totalMs: node.totalMs,
|
|
12310
|
-
selfMs: Math.max(0, node.totalMs - node.childMsAccum),
|
|
12311
|
-
calls: node.calls
|
|
12312
|
-
});
|
|
12313
|
-
}
|
|
12314
|
-
var keys = Object.keys(node.children);
|
|
12315
|
-
keys.sort(function(a, b) {
|
|
12316
|
-
return node.children[b].totalMs - node.children[a].totalMs;
|
|
12317
|
-
});
|
|
12318
|
-
for (var i = 0; i < keys.length; i++) {
|
|
12319
|
-
visit(node.children[keys[i]], depth + 1);
|
|
12320
|
-
}
|
|
12321
|
-
}
|
|
12322
|
-
visit(ROOT, 0);
|
|
12323
|
-
return rows;
|
|
12324
|
-
}
|
|
12325
|
-
|
|
12326
|
-
function profileWallElapsedMs() {
|
|
12327
|
-
return ENABLED && WALL_START ? nowMs() - WALL_START : 0;
|
|
12328
|
-
}
|
|
12329
|
-
|
|
12330
|
-
// Pretty-print the current report as a column-aligned tree.
|
|
12331
|
-
function formatProfileReport(opts) {
|
|
12332
|
-
var rows = profileReport();
|
|
12333
|
-
if (rows.length === 0) return '(profile is empty)';
|
|
12334
|
-
opts = opts || {};
|
|
12335
|
-
var indent = ' ';
|
|
12336
|
-
var lines = [];
|
|
12337
|
-
// header
|
|
12338
|
-
lines.push(['phase', 'total ms', 'self ms', 'calls'].join('\t'));
|
|
12339
|
-
for (var i = 0; i < rows.length; i++) {
|
|
12340
|
-
var r = rows[i];
|
|
12341
|
-
var label = '';
|
|
12342
|
-
for (var j = 0; j < r.depth; j++) label += indent;
|
|
12343
|
-
label += r.label;
|
|
12344
|
-
lines.push([
|
|
12345
|
-
label,
|
|
12346
|
-
r.totalMs.toFixed(2),
|
|
12347
|
-
r.selfMs.toFixed(2),
|
|
12348
|
-
String(r.calls)
|
|
12349
|
-
].join('\t'));
|
|
12350
|
-
}
|
|
12351
|
-
if (opts.includeWall) {
|
|
12352
|
-
lines.push('');
|
|
12353
|
-
lines.push('wall elapsed ms: ' + profileWallElapsedMs().toFixed(2));
|
|
12354
|
-
}
|
|
12355
|
-
return lines.join('\n');
|
|
12356
|
-
}
|
|
12357
|
-
|
|
12358
|
-
// Honour an env var so users (and CI harnesses) can opt in without code changes.
|
|
12359
|
-
if (typeof process !== 'undefined' && process.env && process.env.MAPSHAPER_PROFILE) {
|
|
12360
|
-
enableProfiling();
|
|
12361
|
-
}
|
|
12362
|
-
|
|
12363
|
-
var Profile = /*#__PURE__*/Object.freeze({
|
|
12364
|
-
__proto__: null,
|
|
12365
|
-
disableProfiling: disableProfiling,
|
|
12366
|
-
enableProfiling: enableProfiling,
|
|
12367
|
-
formatProfileReport: formatProfileReport,
|
|
12368
|
-
profileEnabled: profileEnabled,
|
|
12369
|
-
profileEnd: profileEnd,
|
|
12370
|
-
profileReport: profileReport,
|
|
12371
|
-
profileReset: profileReset,
|
|
12372
|
-
profileStart: profileStart,
|
|
12373
|
-
profileWallElapsedMs: profileWallElapsedMs,
|
|
12374
|
-
profileWrap: profileWrap
|
|
12375
|
-
});
|
|
12376
|
-
|
|
12377
12567
|
// Dissolve arcs that can be merged without affecting topology of layers
|
|
12378
12568
|
// remove arcs that are not referenced by any layer; remap arc ids
|
|
12379
12569
|
// in layers. (dataset.arcs is replaced).
|
|
@@ -18090,6 +18280,15 @@
|
|
|
18090
18280
|
};
|
|
18091
18281
|
}
|
|
18092
18282
|
|
|
18283
|
+
var ColorUtils = /*#__PURE__*/Object.freeze({
|
|
18284
|
+
__proto__: null,
|
|
18285
|
+
formatColor: formatColor,
|
|
18286
|
+
parseColor: parseColor,
|
|
18287
|
+
parseHexColor: parseHexColor,
|
|
18288
|
+
parseRGBA: parseRGBA,
|
|
18289
|
+
validateColor: validateColor
|
|
18290
|
+
});
|
|
18291
|
+
|
|
18093
18292
|
function blend(a, b) {
|
|
18094
18293
|
var colors, weights, args;
|
|
18095
18294
|
if (Array.isArray(a)) {
|
|
@@ -22200,9 +22399,12 @@
|
|
|
22200
22399
|
// Assumes that input layers are members of the same dataset (and therefore
|
|
22201
22400
|
// share the same ArcCollection, if layers have paths).
|
|
22202
22401
|
cmd.mergeLayers = function(layersArg, opts) {
|
|
22203
|
-
var layers =
|
|
22204
|
-
var merged = {};
|
|
22402
|
+
var layers, merged = {};
|
|
22205
22403
|
opts = opts || {};
|
|
22404
|
+
layersArg.forEach(function(lyr) {
|
|
22405
|
+
requireNotRasterLayer(lyr, 'Raster layers cannot be merged');
|
|
22406
|
+
});
|
|
22407
|
+
layers = layersArg.filter(getFeatureCount); // ignore empty layers
|
|
22206
22408
|
if (!layers.length && layersArg.length > 1) {
|
|
22207
22409
|
layers = layersArg; // merge all-empty layer sets
|
|
22208
22410
|
}
|
|
@@ -28608,6 +28810,9 @@ ${svg}
|
|
|
28608
28810
|
if (raster.grid.samples) {
|
|
28609
28811
|
copy.grid.samples = typedArrayToBuffer(raster.grid.samples);
|
|
28610
28812
|
}
|
|
28813
|
+
if (raster.grid.coverage) {
|
|
28814
|
+
copy.grid.coverage = typedArrayToBuffer(raster.grid.coverage);
|
|
28815
|
+
}
|
|
28611
28816
|
}
|
|
28612
28817
|
if (raster.view) {
|
|
28613
28818
|
copy.view = Object.assign({}, raster.view);
|
|
@@ -33853,8 +34058,9 @@ ${svg}
|
|
|
33853
34058
|
error('precision= option should be a positive number');
|
|
33854
34059
|
}
|
|
33855
34060
|
|
|
33856
|
-
if (o.
|
|
33857
|
-
|
|
34061
|
+
if (o.interpretation && o.interpretation != 'image' &&
|
|
34062
|
+
o.interpretation != 'categorical' && o.interpretation != 'continuous') {
|
|
34063
|
+
error('Unsupported raster-type:', o.interpretation);
|
|
33858
34064
|
}
|
|
33859
34065
|
|
|
33860
34066
|
if (o.encoding) {
|
|
@@ -35167,8 +35373,15 @@ ${svg}
|
|
|
35167
35373
|
.option('percentile-range', {
|
|
35168
35374
|
describe: '[raster] input percentile range for percentile scaling, e.g. 2,98'
|
|
35169
35375
|
})
|
|
35376
|
+
// The user-facing spelling is raster-type=; the parsed option is named
|
|
35377
|
+
// interpretation to keep it distinct from layer.raster_type, which is a
|
|
35378
|
+
// storage tag, not a semantic one.
|
|
35379
|
+
.option('interpretation', {
|
|
35380
|
+
label: 'raster-type=',
|
|
35381
|
+
describe: '[raster] image, categorical or continuous (default is image)'
|
|
35382
|
+
})
|
|
35170
35383
|
.option('raster-type', {
|
|
35171
|
-
|
|
35384
|
+
alias_to: 'interpretation'
|
|
35172
35385
|
})
|
|
35173
35386
|
.option('rendition', {
|
|
35174
35387
|
describe: '[GeoTIFF] import a GeoTIFF rendition: full,overview-1,etc.'
|
|
@@ -35615,9 +35828,11 @@ ${svg}
|
|
|
35615
35828
|
.option('no-replace', noReplaceOpt);
|
|
35616
35829
|
|
|
35617
35830
|
parser.command('blur')
|
|
35618
|
-
|
|
35831
|
+
.describe('apply a Gaussian-like blur to a projected raster layer')
|
|
35619
35832
|
.option('radius', {
|
|
35620
|
-
|
|
35833
|
+
DEFAULT: true,
|
|
35834
|
+
type: 'distance',
|
|
35835
|
+
describe: 'blur amount as pixels or a distance (e.g. 5px, 20m)'
|
|
35621
35836
|
})
|
|
35622
35837
|
.option('target', targetOpt);
|
|
35623
35838
|
|
|
@@ -35838,6 +36053,35 @@ ${svg}
|
|
|
35838
36053
|
type: 'number'
|
|
35839
36054
|
});
|
|
35840
36055
|
|
|
36056
|
+
parser.command('contours')
|
|
36057
|
+
.describe('convert a raster layer to contour lines')
|
|
36058
|
+
.option('interval', {
|
|
36059
|
+
describe: 'spacing between contour levels (default is a round interval)',
|
|
36060
|
+
type: 'number'
|
|
36061
|
+
})
|
|
36062
|
+
.option('levels', {
|
|
36063
|
+
describe: 'explicit contour levels, instead of interval= (e.g. 0,100,500)',
|
|
36064
|
+
type: 'numbers'
|
|
36065
|
+
})
|
|
36066
|
+
.option('base', {
|
|
36067
|
+
describe: 'value to align interval= to (default is 0)',
|
|
36068
|
+
type: 'number'
|
|
36069
|
+
})
|
|
36070
|
+
.option('band', {
|
|
36071
|
+
describe: '[raster] band to read values from (default is 0)',
|
|
36072
|
+
type: 'number'
|
|
36073
|
+
})
|
|
36074
|
+
.option('field', {
|
|
36075
|
+
describe: 'name of field to hold contour values (default is "value")'
|
|
36076
|
+
})
|
|
36077
|
+
.option('no-smoothing', {
|
|
36078
|
+
describe: 'skip smoothing away the one-pixel contour staircase',
|
|
36079
|
+
type: 'flag'
|
|
36080
|
+
})
|
|
36081
|
+
.option('name', nameOpt)
|
|
36082
|
+
.option('target', targetOpt)
|
|
36083
|
+
.option('no-replace', noReplaceOpt);
|
|
36084
|
+
|
|
35841
36085
|
parser.command('dashlines')
|
|
35842
36086
|
.describe('split lines into sections, with or without a gap')
|
|
35843
36087
|
.oldAlias('split-lines')
|
|
@@ -41239,7 +41483,7 @@ ${svg}
|
|
|
41239
41483
|
}
|
|
41240
41484
|
|
|
41241
41485
|
function getRasterInterpretation$1(opts) {
|
|
41242
|
-
return opts.
|
|
41486
|
+
return opts.interpretation || 'image';
|
|
41243
41487
|
}
|
|
41244
41488
|
|
|
41245
41489
|
async function selectGeoTIFFImportImage(tiff, sourceImage, opts) {
|
|
@@ -41667,7 +41911,7 @@ ${svg}
|
|
|
41667
41911
|
}
|
|
41668
41912
|
|
|
41669
41913
|
function getRasterInterpretation(opts) {
|
|
41670
|
-
return opts.
|
|
41914
|
+
return opts.interpretation || 'image';
|
|
41671
41915
|
}
|
|
41672
41916
|
|
|
41673
41917
|
async function decodeImage(input, imageType) {
|
|
@@ -43012,6 +43256,9 @@ ${svg}
|
|
|
43012
43256
|
if (raster.grid.samples) {
|
|
43013
43257
|
copy.grid.samples = restoreRasterSamples(raster.grid.samples, raster.grid.pixelType);
|
|
43014
43258
|
}
|
|
43259
|
+
if (raster.grid.coverage) {
|
|
43260
|
+
copy.grid.coverage = new Uint8Array(BinArray.copyToArrayBuffer(raster.grid.coverage));
|
|
43261
|
+
}
|
|
43015
43262
|
}
|
|
43016
43263
|
if (raster.view) {
|
|
43017
43264
|
copy.view = Object.assign({}, raster.view);
|
|
@@ -57090,6 +57337,16 @@ ${svg}
|
|
|
57090
57337
|
derivation.type == 'categorical';
|
|
57091
57338
|
}
|
|
57092
57339
|
|
|
57340
|
+
// Whether uncovered output pixels should get the source's numeric nodata
|
|
57341
|
+
// value instead of a fill color. This is a separate question from the
|
|
57342
|
+
// resampling method: elevation data (raster-type=continuous) wants numeric
|
|
57343
|
+
// nodata AND bilinear resampling, a combination that a single
|
|
57344
|
+
// image/categorical switch could not express.
|
|
57345
|
+
function rasterUsesNumericNodataFill(raster) {
|
|
57346
|
+
return !!raster && raster.interpretation == 'continuous' ||
|
|
57347
|
+
rasterAppearsCategorical(raster);
|
|
57348
|
+
}
|
|
57349
|
+
|
|
57093
57350
|
function getProjectedRasterGridBBox(raster, srcCRS, destCRS, optsArg) {
|
|
57094
57351
|
var opts = optsArg || {};
|
|
57095
57352
|
var grid = getRasterGrid(raster);
|
|
@@ -57768,9 +58025,8 @@ ${svg}
|
|
|
57768
58025
|
}
|
|
57769
58026
|
|
|
57770
58027
|
function validateRasterGridForProjection(grid) {
|
|
57771
|
-
var t = grid && grid.transform;
|
|
57772
58028
|
if (!grid || !grid.samples || !grid.bbox) stop$1('Raster layer is missing required projection data');
|
|
57773
|
-
if (
|
|
58029
|
+
if (rasterGridIsRotated(grid)) {
|
|
57774
58030
|
stop$1('Raster reprojection does not support rotated or skewed rasters');
|
|
57775
58031
|
}
|
|
57776
58032
|
}
|
|
@@ -57902,18 +58158,35 @@ ${svg}
|
|
|
57902
58158
|
var color = getNoDataColor(raster, opts);
|
|
57903
58159
|
var noData = grid.nodata;
|
|
57904
58160
|
if (color) {
|
|
58161
|
+
warnIfPaintingOverContinuousData(samples, grid, raster, opts);
|
|
57905
58162
|
fillProjectedRasterColor(samples, bands, color);
|
|
57906
58163
|
return;
|
|
57907
58164
|
}
|
|
57908
|
-
if (noData === null || noData === undefined || !isFinite(noData))
|
|
58165
|
+
if (noData === null || noData === undefined || !isFinite(noData)) {
|
|
58166
|
+
if (raster && raster.interpretation == 'continuous') {
|
|
58167
|
+
warnOnce('This raster has no nodata value, so uncovered pixels will be 0');
|
|
58168
|
+
}
|
|
58169
|
+
return;
|
|
58170
|
+
}
|
|
57909
58171
|
samples.fill(noData);
|
|
57910
58172
|
}
|
|
57911
58173
|
|
|
58174
|
+
// Painting a color into a float grid means writing a color value into what
|
|
58175
|
+
// are probably measurements. Flag it, since raster-type=continuous is
|
|
58176
|
+
// usually what the user wanted.
|
|
58177
|
+
function warnIfPaintingOverContinuousData(samples, grid, raster, opts) {
|
|
58178
|
+
if (opts.nodata_color || opts.nodataColor) return; // explicit user choice
|
|
58179
|
+
if (!rasterSamplesAreFloat(samples)) return;
|
|
58180
|
+
if (grid.nodata === null || grid.nodata === undefined) return;
|
|
58181
|
+
warnOnce('Filling uncovered pixels of a floating-point raster with a color. ' +
|
|
58182
|
+
'Use -i raster-type=continuous to preserve its nodata value instead.');
|
|
58183
|
+
}
|
|
58184
|
+
|
|
57912
58185
|
function getNoDataColor(raster, opts) {
|
|
57913
58186
|
var arg = opts.nodata_color || opts.nodataColor;
|
|
57914
58187
|
var color;
|
|
57915
58188
|
if (arg == null || arg === '') {
|
|
57916
|
-
return
|
|
58189
|
+
return rasterUsesNumericNodataFill(raster) ? null : {r: 255, g: 255, b: 255, a: 1};
|
|
57917
58190
|
}
|
|
57918
58191
|
if (String(arg).toLowerCase() == 'transparent') {
|
|
57919
58192
|
return {r: 0, g: 0, b: 0, a: 0};
|
|
@@ -57938,7 +58211,8 @@ ${svg}
|
|
|
57938
58211
|
}
|
|
57939
58212
|
}
|
|
57940
58213
|
|
|
57941
|
-
function rasterizeProjectedMesh(srcGrid, destGrid, mesh,
|
|
58214
|
+
function rasterizeProjectedMesh(srcGrid, destGrid, mesh, sampleMethodArg) {
|
|
58215
|
+
var sampleMethod = getRasterSamplerContext(srcGrid, destGrid, sampleMethodArg);
|
|
57942
58216
|
var regionData = mesh.projectedRegions ?
|
|
57943
58217
|
createProjectedRegionMask(
|
|
57944
58218
|
destGrid, mesh.projectedRegions, mesh.fillRegionMask) :
|
|
@@ -58135,9 +58409,24 @@ ${svg}
|
|
|
58135
58409
|
];
|
|
58136
58410
|
}
|
|
58137
58411
|
|
|
58138
|
-
|
|
58139
|
-
|
|
58140
|
-
|
|
58412
|
+
// Per-projection sampling context, built once so the inner rasterizing loop
|
|
58413
|
+
// does not repeat type or metadata tests for every output pixel.
|
|
58414
|
+
function getRasterSamplerContext(srcGrid, destGrid, method) {
|
|
58415
|
+
return {
|
|
58416
|
+
bilinear: method == 'bilinear',
|
|
58417
|
+
// Integer sample arrays truncate on assignment, so interpolated values
|
|
58418
|
+
// have to be rounded explicitly. Float arrays must NOT be rounded, or
|
|
58419
|
+
// elevation data loses its sub-unit precision.
|
|
58420
|
+
round: !rasterSamplesAreFloat(destGrid.samples),
|
|
58421
|
+
// Resolved once for the whole source grid; null means every pixel is
|
|
58422
|
+
// valid and the sampler can skip validity testing altogether.
|
|
58423
|
+
validity: getRasterValidityMask(srcGrid)
|
|
58424
|
+
};
|
|
58425
|
+
}
|
|
58426
|
+
|
|
58427
|
+
function copyRasterSample(srcGrid, destGrid, sx, sy, dx, dy, sampler, wrapX) {
|
|
58428
|
+
if (sampler.bilinear) {
|
|
58429
|
+
copyBilinearRasterSample(srcGrid, destGrid, sx, sy, dx, dy, sampler, wrapX);
|
|
58141
58430
|
} else {
|
|
58142
58431
|
copyNearestRasterSample(srcGrid, destGrid, sx, sy, dx, dy, wrapX);
|
|
58143
58432
|
}
|
|
@@ -58150,7 +58439,9 @@ ${svg}
|
|
|
58150
58439
|
var srcY = clamp$1(Math.floor(sy), 0, srcGrid.height - 1);
|
|
58151
58440
|
var src = (srcY * srcGrid.width + srcX) * srcGrid.bands;
|
|
58152
58441
|
var dest = (dy * destGrid.width + dx) * destGrid.bands;
|
|
58153
|
-
|
|
58442
|
+
// Nearest sampling copies a nodata sample through unchanged, rather than
|
|
58443
|
+
// skipping it, so nodata areas survive a round trip.
|
|
58444
|
+
if (!rasterPixelIsCovered(srcGrid, srcY * srcGrid.width + srcX)) return;
|
|
58154
58445
|
for (var band = 0; band < srcGrid.bands; band++) {
|
|
58155
58446
|
destGrid.samples[dest + band] = srcGrid.samples[src + band];
|
|
58156
58447
|
}
|
|
@@ -58158,7 +58449,7 @@ ${svg}
|
|
|
58158
58449
|
if (destGrid.coverage) destGrid.coverage[dy * destGrid.width + dx] = 1;
|
|
58159
58450
|
}
|
|
58160
58451
|
|
|
58161
|
-
function copyBilinearRasterSample(srcGrid, destGrid, sx, sy, dx, dy, wrapX) {
|
|
58452
|
+
function copyBilinearRasterSample(srcGrid, destGrid, sx, sy, dx, dy, sampler, wrapX) {
|
|
58162
58453
|
if (wrapX) sx = modulo(sx, srcGrid.width);
|
|
58163
58454
|
var srcX = wrapX ?
|
|
58164
58455
|
modulo(Math.floor(sx - 0.5), srcGrid.width) :
|
|
@@ -58174,79 +58465,81 @@ ${svg}
|
|
|
58174
58465
|
var src01 = (srcY2 * srcGrid.width + srcX) * srcGrid.bands;
|
|
58175
58466
|
var src11 = (srcY2 * srcGrid.width + srcX2) * srcGrid.bands;
|
|
58176
58467
|
var dest = (dy * destGrid.width + dx) * destGrid.bands;
|
|
58177
|
-
if (!
|
|
58178
|
-
copyBilinearRasterSampleFast(srcGrid, destGrid, src00, src10, src01, src11, dest, tx, ty);
|
|
58468
|
+
if (!sampler.validity) {
|
|
58469
|
+
copyBilinearRasterSampleFast(srcGrid, destGrid, src00, src10, src01, src11, dest, tx, ty, sampler.round);
|
|
58179
58470
|
if (destGrid.coverage) destGrid.coverage[dy * destGrid.width + dx] = 1;
|
|
58180
58471
|
return;
|
|
58181
58472
|
}
|
|
58182
|
-
if (
|
|
58473
|
+
if (copyBilinearRasterSampleChecked(srcGrid, destGrid, sampler.validity, srcX, srcY, srcX2, srcY2, src00, src10, src01, src11, dest, tx, ty, sampler.round)) {
|
|
58183
58474
|
if (destGrid.coverage) destGrid.coverage[dy * destGrid.width + dx] = 1;
|
|
58184
58475
|
}
|
|
58185
58476
|
}
|
|
58186
58477
|
|
|
58187
|
-
function copyBilinearRasterSampleFast(srcGrid, destGrid, src00, src10, src01, src11, dest, tx, ty) {
|
|
58478
|
+
function copyBilinearRasterSampleFast(srcGrid, destGrid, src00, src10, src01, src11, dest, tx, ty, round) {
|
|
58188
58479
|
if (srcGrid.bands == 3) {
|
|
58189
|
-
copyBilinearRgbSample(srcGrid.samples, destGrid.samples, src00, src10, src01, src11, dest, tx, ty);
|
|
58480
|
+
copyBilinearRgbSample(srcGrid.samples, destGrid.samples, src00, src10, src01, src11, dest, tx, ty, round);
|
|
58190
58481
|
} else if (srcGrid.bands == 4) {
|
|
58191
|
-
copyBilinearRgbaSample(srcGrid.samples, destGrid.samples, src00, src10, src01, src11, dest, tx, ty);
|
|
58482
|
+
copyBilinearRgbaSample(srcGrid.samples, destGrid.samples, src00, src10, src01, src11, dest, tx, ty, round);
|
|
58192
58483
|
} else {
|
|
58193
|
-
copyBilinearGenericSample(srcGrid.samples, destGrid.samples, srcGrid.bands, src00, src10, src01, src11, dest, tx, ty);
|
|
58484
|
+
copyBilinearGenericSample(srcGrid.samples, destGrid.samples, srcGrid.bands, src00, src10, src01, src11, dest, tx, ty, round);
|
|
58194
58485
|
}
|
|
58195
58486
|
if (destGrid.bands > srcGrid.bands) destGrid.samples[dest + 3] = 255;
|
|
58196
58487
|
}
|
|
58197
58488
|
|
|
58198
|
-
function copyBilinearRgbSample(src, destArr, src00, src10, src01, src11, dest, tx, ty) {
|
|
58199
|
-
copyBilinearBand(src, destArr, src00, src10, src01, src11, dest, 0, tx, ty);
|
|
58200
|
-
copyBilinearBand(src, destArr, src00, src10, src01, src11, dest, 1, tx, ty);
|
|
58201
|
-
copyBilinearBand(src, destArr, src00, src10, src01, src11, dest, 2, tx, ty);
|
|
58489
|
+
function copyBilinearRgbSample(src, destArr, src00, src10, src01, src11, dest, tx, ty, round) {
|
|
58490
|
+
copyBilinearBand(src, destArr, src00, src10, src01, src11, dest, 0, tx, ty, round);
|
|
58491
|
+
copyBilinearBand(src, destArr, src00, src10, src01, src11, dest, 1, tx, ty, round);
|
|
58492
|
+
copyBilinearBand(src, destArr, src00, src10, src01, src11, dest, 2, tx, ty, round);
|
|
58202
58493
|
}
|
|
58203
58494
|
|
|
58204
|
-
function copyBilinearRgbaSample(src, destArr, src00, src10, src01, src11, dest, tx, ty) {
|
|
58205
|
-
copyBilinearRgbSample(src, destArr, src00, src10, src01, src11, dest, tx, ty);
|
|
58206
|
-
copyBilinearBand(src, destArr, src00, src10, src01, src11, dest, 3, tx, ty);
|
|
58495
|
+
function copyBilinearRgbaSample(src, destArr, src00, src10, src01, src11, dest, tx, ty, round) {
|
|
58496
|
+
copyBilinearRgbSample(src, destArr, src00, src10, src01, src11, dest, tx, ty, round);
|
|
58497
|
+
copyBilinearBand(src, destArr, src00, src10, src01, src11, dest, 3, tx, ty, round);
|
|
58207
58498
|
}
|
|
58208
58499
|
|
|
58209
|
-
function copyBilinearGenericSample(src, destArr, bands, src00, src10, src01, src11, dest, tx, ty) {
|
|
58500
|
+
function copyBilinearGenericSample(src, destArr, bands, src00, src10, src01, src11, dest, tx, ty, round) {
|
|
58210
58501
|
for (var band = 0; band < bands; band++) {
|
|
58211
|
-
copyBilinearBand(src, destArr, src00, src10, src01, src11, dest, band, tx, ty);
|
|
58502
|
+
copyBilinearBand(src, destArr, src00, src10, src01, src11, dest, band, tx, ty, round);
|
|
58212
58503
|
}
|
|
58213
58504
|
}
|
|
58214
58505
|
|
|
58215
|
-
function copyBilinearBand(src, destArr, src00, src10, src01, src11, dest, band, tx, ty) {
|
|
58506
|
+
function copyBilinearBand(src, destArr, src00, src10, src01, src11, dest, band, tx, ty, round) {
|
|
58216
58507
|
var a = src[src00 + band] * (1 - tx) + src[src10 + band] * tx;
|
|
58217
58508
|
var b = src[src01 + band] * (1 - tx) + src[src11 + band] * tx;
|
|
58218
|
-
|
|
58219
|
-
|
|
58220
|
-
|
|
58221
|
-
|
|
58222
|
-
|
|
58223
|
-
|
|
58224
|
-
|
|
58225
|
-
|
|
58226
|
-
|
|
58227
|
-
|
|
58228
|
-
var
|
|
58229
|
-
var
|
|
58509
|
+
var val = a * (1 - ty) + b * ty;
|
|
58510
|
+
destArr[dest + band] = round ? Math.round(val) : val;
|
|
58511
|
+
}
|
|
58512
|
+
|
|
58513
|
+
// Bilinear sampling that skips invalid source pixels and renormalizes the
|
|
58514
|
+
// remaining weights, so nodata is never blended into a real value. Validity
|
|
58515
|
+
// is resolved once per output pixel rather than once per band.
|
|
58516
|
+
function copyBilinearRasterSampleChecked(srcGrid, destGrid, validity, srcX, srcY, srcX2, srcY2, src00, src10, src01, src11, dest, tx, ty, round) {
|
|
58517
|
+
var w00 = (1 - tx) * (1 - ty);
|
|
58518
|
+
var w10 = tx * (1 - ty);
|
|
58519
|
+
var w01 = (1 - tx) * ty;
|
|
58520
|
+
var w11 = tx * ty;
|
|
58521
|
+
var width = srcGrid.width;
|
|
58522
|
+
var samples = srcGrid.samples;
|
|
58523
|
+
var ok00 = w00 > 0 && validity[srcY * width + srcX] > 0;
|
|
58524
|
+
var ok10 = w10 > 0 && validity[srcY * width + srcX2] > 0;
|
|
58525
|
+
var ok01 = w01 > 0 && validity[srcY2 * width + srcX] > 0;
|
|
58526
|
+
var ok11 = w11 > 0 && validity[srcY2 * width + srcX2] > 0;
|
|
58527
|
+
var total = (ok00 ? w00 : 0) + (ok10 ? w10 : 0) + (ok01 ? w01 : 0) + (ok11 ? w11 : 0);
|
|
58528
|
+
var val;
|
|
58529
|
+
if (total <= 0) return false;
|
|
58230
58530
|
for (var band = 0; band < srcGrid.bands; band++) {
|
|
58231
58531
|
val = 0;
|
|
58232
|
-
|
|
58233
|
-
|
|
58234
|
-
|
|
58235
|
-
|
|
58236
|
-
|
|
58237
|
-
|
|
58238
|
-
}
|
|
58239
|
-
if (total <= 0) return false;
|
|
58240
|
-
destGrid.samples[dest + band] = Math.round(val / total);
|
|
58532
|
+
if (ok00) val += samples[src00 + band] * w00;
|
|
58533
|
+
if (ok10) val += samples[src10 + band] * w10;
|
|
58534
|
+
if (ok01) val += samples[src01 + band] * w01;
|
|
58535
|
+
if (ok11) val += samples[src11 + band] * w11;
|
|
58536
|
+
val /= total;
|
|
58537
|
+
destGrid.samples[dest + band] = round ? Math.round(val) : val;
|
|
58241
58538
|
}
|
|
58242
58539
|
if (destGrid.bands > srcGrid.bands) destGrid.samples[dest + 3] = 255;
|
|
58243
58540
|
return true;
|
|
58244
58541
|
}
|
|
58245
58542
|
|
|
58246
|
-
function rasterSourcePixelIsCovered(grid, x, y) {
|
|
58247
|
-
return !grid.coverage || grid.coverage[y * grid.width + x] > 0;
|
|
58248
|
-
}
|
|
58249
|
-
|
|
58250
58543
|
function timeStart(timing, name) {
|
|
58251
58544
|
if (!timing) return;
|
|
58252
58545
|
timing[name + 'Start'] = getTimer();
|
|
@@ -58290,6 +58583,33 @@ ${svg}
|
|
|
58290
58583
|
projectRasterGridForward: projectRasterGridForward
|
|
58291
58584
|
});
|
|
58292
58585
|
|
|
58586
|
+
/*
|
|
58587
|
+
* Post-projection repair for projections whose layout wraps at a rectangular
|
|
58588
|
+
* frame (currently the Lee tetrahedral pair, the only ones that publish
|
|
58589
|
+
* frame_bounds).
|
|
58590
|
+
*
|
|
58591
|
+
* Why this is needed: such a projection is cut along a curved seam traced in
|
|
58592
|
+
* geographic space, and the seam mask splits any path that crosses it. But
|
|
58593
|
+
* without -densify, a path segment is a straight chord in lon/lat, and a chord
|
|
58594
|
+
* between two vertices either side of the seam can pass on the wrong side of
|
|
58595
|
+
* the curve entirely. The projected path then leaves one edge of the frame and
|
|
58596
|
+
* re-enters at the opposite edge, so the ring closes with a long run along a
|
|
58597
|
+
* frame side, joining two halves that belong on opposite sides of the map.
|
|
58598
|
+
* Densified input curves with the seam and is cut normally, which is why the
|
|
58599
|
+
* artifact only appears without that option.
|
|
58600
|
+
*
|
|
58601
|
+
* The repair looks for a ring that meets the same frame side in two or more
|
|
58602
|
+
* separate runs and splits it there. A ring that legitimately touches a frame
|
|
58603
|
+
* side does so once.
|
|
58604
|
+
*
|
|
58605
|
+
* Note that the jump threshold in getFrameSide is coupled to the resolution of
|
|
58606
|
+
* the seam trace: it recognizes a spurious connection as a single long segment
|
|
58607
|
+
* lying on a frame side. Coarsening the trace breaks the connection into a
|
|
58608
|
+
* chain of shorter segments that fall below the threshold and are missed, so
|
|
58609
|
+
* the two need to be changed together.
|
|
58610
|
+
*/
|
|
58611
|
+
|
|
58612
|
+
|
|
58293
58613
|
function splitPolygonFrameChords(dataset, bounds) {
|
|
58294
58614
|
var editor = new DatasetEditor(dataset);
|
|
58295
58615
|
dataset.layers.forEach(function(lyr) {
|
|
@@ -62045,6 +62365,529 @@ ${svg}
|
|
|
62045
62365
|
// message('[comment]', opts.message);
|
|
62046
62366
|
}; // no-op, so -comment doesn't trigger a parsing error
|
|
62047
62367
|
|
|
62368
|
+
// Trace contour lines (isolines) from a raster layer's working samples using
|
|
62369
|
+
// marching squares.
|
|
62370
|
+
//
|
|
62371
|
+
// Samples are treated as point measurements at pixel centers, so a W x H grid
|
|
62372
|
+
// contours over a lattice of W x H values and (W-1) x (H-1) cells. Contour
|
|
62373
|
+
// vertices therefore span from the first pixel center to the last, half a
|
|
62374
|
+
// pixel inside the grid bbox. This matches gdal_contour.
|
|
62375
|
+
|
|
62376
|
+
|
|
62377
|
+
var MAX_CONTOUR_LEVELS = 2000;
|
|
62378
|
+
var TARGET_LEVEL_COUNT = 15;
|
|
62379
|
+
|
|
62380
|
+
// Marching squares traces a staircase with treads one pixel wide, so smoothing
|
|
62381
|
+
// at one pixel is what removes the stepping. Measured against an analytic cone
|
|
62382
|
+
// quantized to whole meters (as elevation models usually are), one pixel gives
|
|
62383
|
+
// the lowest deviation from the true contour: 0.16 px, against 0.29 px
|
|
62384
|
+
// unsmoothed and 0.18 px at 1.5 px. On unquantized float data, where there is
|
|
62385
|
+
// no staircase to remove, the cost is under 0.1 px.
|
|
62386
|
+
var SMOOTHING_PIXELS = 1;
|
|
62387
|
+
|
|
62388
|
+
// A degree of longitude vanishes at the poles; keep the implied pixel width
|
|
62389
|
+
// from collapsing to zero there.
|
|
62390
|
+
var MIN_LATITUDE_SCALE = 0.05;
|
|
62391
|
+
|
|
62392
|
+
// Cell corners, and the edges between them:
|
|
62393
|
+
//
|
|
62394
|
+
// edge 0
|
|
62395
|
+
// c0 --------- c1
|
|
62396
|
+
// | |
|
|
62397
|
+
// | edge 3 | edge 1
|
|
62398
|
+
// | |
|
|
62399
|
+
// c3 --------- c2
|
|
62400
|
+
// edge 2
|
|
62401
|
+
//
|
|
62402
|
+
// Corner bits: 1 = c0, 2 = c1, 4 = c2, 8 = c3. A corner is set when its value
|
|
62403
|
+
// is >= the contour level.
|
|
62404
|
+
//
|
|
62405
|
+
// Each entry is the pair of edges a contour segment crosses, directed so that
|
|
62406
|
+
// the above-level side is always on the same hand. Consistent orientation is
|
|
62407
|
+
// what lets segments be stitched together by edge id alone: a crossing is the
|
|
62408
|
+
// exit of one cell and the entry of its neighbor.
|
|
62409
|
+
var CONTOUR_CASES = [
|
|
62410
|
+
null, // 0: none above
|
|
62411
|
+
[3, 0], // 1: c0
|
|
62412
|
+
[0, 1], // 2: c1
|
|
62413
|
+
[3, 1], // 3: c0 c1
|
|
62414
|
+
[1, 2], // 4: c2
|
|
62415
|
+
null, // 5: c0 c2 -- saddle
|
|
62416
|
+
[0, 2], // 6: c1 c2
|
|
62417
|
+
[3, 2], // 7: c0 c1 c2
|
|
62418
|
+
[2, 3], // 8: c3
|
|
62419
|
+
[2, 0], // 9: c0 c3
|
|
62420
|
+
null, // 10: c1 c3 -- saddle
|
|
62421
|
+
[2, 1], // 11: c0 c1 c3
|
|
62422
|
+
[1, 3], // 12: c2 c3
|
|
62423
|
+
[1, 0], // 13: c0 c2 c3
|
|
62424
|
+
[0, 3], // 14: c1 c2 c3
|
|
62425
|
+
null // 15: all above
|
|
62426
|
+
];
|
|
62427
|
+
|
|
62428
|
+
// A saddle cell has crossings on all four edges, which can be paired two ways.
|
|
62429
|
+
// The cell-center average breaks the tie: when the center is above the level,
|
|
62430
|
+
// the two above-level corners are joined through the middle of the cell and
|
|
62431
|
+
// the below-level corners are cut off separately.
|
|
62432
|
+
var SADDLE_C0_C2_JOINED = [[1, 0], [3, 2]];
|
|
62433
|
+
var SADDLE_C0_C2_SPLIT = [[3, 0], [1, 2]];
|
|
62434
|
+
var SADDLE_C1_C3_JOINED = [[0, 3], [2, 1]];
|
|
62435
|
+
var SADDLE_C1_C3_SPLIT = [[0, 1], [2, 3]];
|
|
62436
|
+
|
|
62437
|
+
function getRasterContourLines(raster, opts) {
|
|
62438
|
+
var grid = getRasterGrid(raster);
|
|
62439
|
+
var band, levels;
|
|
62440
|
+
validateRasterGridForContours(grid);
|
|
62441
|
+
band = getContourBand(grid, opts);
|
|
62442
|
+
levels = getContourLevels(grid, band, opts);
|
|
62443
|
+
return {
|
|
62444
|
+
levels: levels,
|
|
62445
|
+
lines: traceRasterContours(grid, band, levels)
|
|
62446
|
+
};
|
|
62447
|
+
}
|
|
62448
|
+
|
|
62449
|
+
// Auto-selected smoothing interval for traced contours, returned the way
|
|
62450
|
+
// -smooth reads a plain distance= number: as meters when the CRS is known, and
|
|
62451
|
+
// as raw coordinate units when it is not.
|
|
62452
|
+
function getContourSmoothingDistance(grid, crs) {
|
|
62453
|
+
var bbox = grid.bbox;
|
|
62454
|
+
var dx = Math.abs(bbox[2] - bbox[0]) / grid.width;
|
|
62455
|
+
var dy = Math.abs(bbox[3] - bbox[1]) / grid.height;
|
|
62456
|
+
var lat;
|
|
62457
|
+
if (crs && crs.is_latlong) {
|
|
62458
|
+
lat = (bbox[1] + bbox[3]) / 2;
|
|
62459
|
+
dx = degreesToMeters(dx) * Math.max(Math.cos(lat * D2R$8), MIN_LATITUDE_SCALE);
|
|
62460
|
+
dy = degreesToMeters(dy);
|
|
62461
|
+
} else if (crs && crs.to_meter > 0) {
|
|
62462
|
+
dx *= crs.to_meter;
|
|
62463
|
+
dy *= crs.to_meter;
|
|
62464
|
+
}
|
|
62465
|
+
// Geometric mean, so a grid with non-square pixels gets an interval between
|
|
62466
|
+
// its two resolutions rather than one axis winning.
|
|
62467
|
+
return Math.sqrt(dx * dy) * SMOOTHING_PIXELS;
|
|
62468
|
+
}
|
|
62469
|
+
|
|
62470
|
+
function validateRasterGridForContours(grid) {
|
|
62471
|
+
if (!grid || !grid.samples || !grid.bbox) {
|
|
62472
|
+
stop$1('Raster layer is missing required grid data');
|
|
62473
|
+
}
|
|
62474
|
+
if (rasterGridIsRotated(grid)) {
|
|
62475
|
+
stop$1('Tracing contours from a rotated or skewed raster is not supported');
|
|
62476
|
+
}
|
|
62477
|
+
if (grid.width < 2 || grid.height < 2) {
|
|
62478
|
+
stop$1('Raster layer is too small to contour (needs to be at least 2x2 pixels)');
|
|
62479
|
+
}
|
|
62480
|
+
}
|
|
62481
|
+
|
|
62482
|
+
function getContourBand(grid, opts) {
|
|
62483
|
+
var band = opts && 'band' in opts ? opts.band : 0;
|
|
62484
|
+
if (!(band >= 0) || band !== Math.floor(band) || band >= grid.bands) {
|
|
62485
|
+
stop$1('Invalid band=' + band + '; this raster has ' + grid.bands +
|
|
62486
|
+
(grid.bands == 1 ? ' band' : ' bands'));
|
|
62487
|
+
}
|
|
62488
|
+
return band;
|
|
62489
|
+
}
|
|
62490
|
+
|
|
62491
|
+
function getContourLevels(grid, band, opts) {
|
|
62492
|
+
var levels = opts && opts.levels ?
|
|
62493
|
+
getExplicitLevels(opts.levels) :
|
|
62494
|
+
getIntervalLevels(getRasterSampleRange(grid, band), opts || {});
|
|
62495
|
+
if (levels.length > MAX_CONTOUR_LEVELS) {
|
|
62496
|
+
stop$1('Contouring would generate ' + levels.length + ' levels; the limit is ' +
|
|
62497
|
+
MAX_CONTOUR_LEVELS + '. Use a larger interval= or an explicit levels= list.');
|
|
62498
|
+
}
|
|
62499
|
+
return levels;
|
|
62500
|
+
}
|
|
62501
|
+
|
|
62502
|
+
function getExplicitLevels(arg) {
|
|
62503
|
+
var levels = [];
|
|
62504
|
+
arg.forEach(function(val) {
|
|
62505
|
+
if (typeof val != 'number' || !isFinite(val)) {
|
|
62506
|
+
stop$1('Invalid contour level:', val);
|
|
62507
|
+
}
|
|
62508
|
+
if (levels.indexOf(val) == -1) levels.push(val);
|
|
62509
|
+
});
|
|
62510
|
+
return levels.sort(ascending);
|
|
62511
|
+
}
|
|
62512
|
+
|
|
62513
|
+
function getIntervalLevels(range, opts) {
|
|
62514
|
+
var base = 'base' in opts ? opts.base : 0;
|
|
62515
|
+
var levels = [];
|
|
62516
|
+
var interval, k, level;
|
|
62517
|
+
// Only fall back to an automatic interval when none was given: a bad
|
|
62518
|
+
// interval= should be reported, not quietly replaced.
|
|
62519
|
+
if (opts.interval === null || opts.interval === undefined) {
|
|
62520
|
+
interval = getAutoContourInterval(range);
|
|
62521
|
+
} else {
|
|
62522
|
+
interval = opts.interval;
|
|
62523
|
+
if (!(interval > 0) || !isFinite(interval)) {
|
|
62524
|
+
stop$1('Contour interval= must be a positive number');
|
|
62525
|
+
}
|
|
62526
|
+
}
|
|
62527
|
+
if (!isFinite(base)) {
|
|
62528
|
+
stop$1('Contour base= must be a number');
|
|
62529
|
+
}
|
|
62530
|
+
// A level at exactly the data minimum has no cell to cross, because every
|
|
62531
|
+
// corner counts as above it.
|
|
62532
|
+
if (!(range.max > range.min)) return levels;
|
|
62533
|
+
k = Math.ceil((range.min - base) / interval);
|
|
62534
|
+
while (levels.length <= MAX_CONTOUR_LEVELS) {
|
|
62535
|
+
level = cleanLevelValue(base + k * interval);
|
|
62536
|
+
if (level > range.max) break;
|
|
62537
|
+
if (level > range.min) levels.push(level);
|
|
62538
|
+
k++;
|
|
62539
|
+
}
|
|
62540
|
+
return levels;
|
|
62541
|
+
}
|
|
62542
|
+
|
|
62543
|
+
// Choose a round interval that divides the data range into roughly
|
|
62544
|
+
// TARGET_LEVEL_COUNT steps.
|
|
62545
|
+
function getAutoContourInterval(range) {
|
|
62546
|
+
var span = range.max - range.min;
|
|
62547
|
+
var raw, magnitude, normalized;
|
|
62548
|
+
if (!(span > 0) || !isFinite(span)) return 1;
|
|
62549
|
+
raw = span / TARGET_LEVEL_COUNT;
|
|
62550
|
+
magnitude = Math.pow(10, Math.floor(Math.log(raw) / Math.LN10));
|
|
62551
|
+
normalized = raw / magnitude;
|
|
62552
|
+
return (normalized >= 5 ? 5 : normalized >= 2 ? 2 : 1) * magnitude;
|
|
62553
|
+
}
|
|
62554
|
+
|
|
62555
|
+
// Repeated addition of a fractional interval accumulates error that shows up
|
|
62556
|
+
// in output field values, e.g. 0.30000000000000004.
|
|
62557
|
+
function cleanLevelValue(value) {
|
|
62558
|
+
return value === 0 ? 0 : Number(value.toPrecision(12));
|
|
62559
|
+
}
|
|
62560
|
+
|
|
62561
|
+
function getRasterSampleRange(grid, band) {
|
|
62562
|
+
var samples = grid.samples;
|
|
62563
|
+
var bands = grid.bands;
|
|
62564
|
+
var valid = getRasterValidityMask(grid);
|
|
62565
|
+
var n = grid.width * grid.height;
|
|
62566
|
+
var min = Infinity;
|
|
62567
|
+
var max = -Infinity;
|
|
62568
|
+
var v, i;
|
|
62569
|
+
for (i = 0; i < n; i++) {
|
|
62570
|
+
if (valid && !valid[i]) continue;
|
|
62571
|
+
v = samples[i * bands + band];
|
|
62572
|
+
if (v !== v) continue; // NaN, used as a nodata value by some float rasters
|
|
62573
|
+
if (v < min) min = v;
|
|
62574
|
+
if (v > max) max = v;
|
|
62575
|
+
}
|
|
62576
|
+
return {min: min, max: max};
|
|
62577
|
+
}
|
|
62578
|
+
|
|
62579
|
+
function traceRasterContours(grid, band, levels) {
|
|
62580
|
+
var segments = collectContourSegments(grid, band, levels);
|
|
62581
|
+
var toMapXY = getLatticeToMapTransform(grid);
|
|
62582
|
+
var hCount = (grid.width - 1) * grid.height;
|
|
62583
|
+
var lines = [];
|
|
62584
|
+
segments.forEach(function(levelSegments, i) {
|
|
62585
|
+
stitchContourSegments(levelSegments).forEach(function(path) {
|
|
62586
|
+
var coords = getPathCoords(path, grid, band, levels[i], hCount, toMapXY);
|
|
62587
|
+
if (coords.length > 1) {
|
|
62588
|
+
lines.push({value: levels[i], coords: coords});
|
|
62589
|
+
}
|
|
62590
|
+
});
|
|
62591
|
+
});
|
|
62592
|
+
return lines;
|
|
62593
|
+
}
|
|
62594
|
+
|
|
62595
|
+
// Visit each cell once and march it for every level that falls inside its
|
|
62596
|
+
// value range, rather than scanning the whole grid once per level.
|
|
62597
|
+
function collectContourSegments(grid, band, levels) {
|
|
62598
|
+
var W = grid.width;
|
|
62599
|
+
var H = grid.height;
|
|
62600
|
+
var bands = grid.bands;
|
|
62601
|
+
var samples = grid.samples;
|
|
62602
|
+
var hCount = (W - 1) * H;
|
|
62603
|
+
var valid = getRasterValidityMask(grid);
|
|
62604
|
+
var rowStride = W * bands;
|
|
62605
|
+
var out = levels.map(function() { return {from: [], to: []}; });
|
|
62606
|
+
var cx, cy, pixelId, off, v0, v1, v2, v3, min, max, i;
|
|
62607
|
+
if (levels.length === 0) return out;
|
|
62608
|
+
for (cy = 0; cy < H - 1; cy++) {
|
|
62609
|
+
for (cx = 0; cx < W - 1; cx++) {
|
|
62610
|
+
pixelId = cy * W + cx;
|
|
62611
|
+
if (valid && (!valid[pixelId] || !valid[pixelId + 1] ||
|
|
62612
|
+
!valid[pixelId + W] || !valid[pixelId + W + 1])) continue;
|
|
62613
|
+
off = pixelId * bands + band;
|
|
62614
|
+
v0 = samples[off];
|
|
62615
|
+
v1 = samples[off + bands];
|
|
62616
|
+
v3 = samples[off + rowStride];
|
|
62617
|
+
v2 = samples[off + rowStride + bands];
|
|
62618
|
+
if (v0 !== v0 || v1 !== v1 || v2 !== v2 || v3 !== v3) continue; // NaN
|
|
62619
|
+
min = v0 < v1 ? v0 : v1;
|
|
62620
|
+
if (v2 < min) min = v2;
|
|
62621
|
+
if (v3 < min) min = v3;
|
|
62622
|
+
max = v0 > v1 ? v0 : v1;
|
|
62623
|
+
if (v2 > max) max = v2;
|
|
62624
|
+
if (v3 > max) max = v3;
|
|
62625
|
+
// A level crosses the cell when min < level <= max, which matches the
|
|
62626
|
+
// >= test that classifies each corner.
|
|
62627
|
+
for (i = firstLevelAbove(levels, min); i < levels.length && levels[i] <= max; i++) {
|
|
62628
|
+
addCellSegments(out[i], levels[i], v0, v1, v2, v3, cx, cy, W, hCount);
|
|
62629
|
+
}
|
|
62630
|
+
}
|
|
62631
|
+
}
|
|
62632
|
+
return out;
|
|
62633
|
+
}
|
|
62634
|
+
|
|
62635
|
+
function firstLevelAbove(levels, value) {
|
|
62636
|
+
var lo = 0;
|
|
62637
|
+
var hi = levels.length;
|
|
62638
|
+
var mid;
|
|
62639
|
+
while (lo < hi) {
|
|
62640
|
+
mid = (lo + hi) >> 1;
|
|
62641
|
+
if (levels[mid] > value) hi = mid;
|
|
62642
|
+
else lo = mid + 1;
|
|
62643
|
+
}
|
|
62644
|
+
return lo;
|
|
62645
|
+
}
|
|
62646
|
+
|
|
62647
|
+
function addCellSegments(dest, level, v0, v1, v2, v3, cx, cy, W, hCount) {
|
|
62648
|
+
var caseId = (v0 >= level ? 1 : 0) | (v1 >= level ? 2 : 0) |
|
|
62649
|
+
(v2 >= level ? 4 : 0) | (v3 >= level ? 8 : 0);
|
|
62650
|
+
var pairs, pair;
|
|
62651
|
+
if (caseId === 5 || caseId === 10) {
|
|
62652
|
+
pairs = getSaddlePairs(caseId, level, v0, v1, v2, v3);
|
|
62653
|
+
addSegment(dest, pairs[0], cx, cy, W, hCount);
|
|
62654
|
+
addSegment(dest, pairs[1], cx, cy, W, hCount);
|
|
62655
|
+
return;
|
|
62656
|
+
}
|
|
62657
|
+
pair = CONTOUR_CASES[caseId];
|
|
62658
|
+
if (pair) addSegment(dest, pair, cx, cy, W, hCount);
|
|
62659
|
+
}
|
|
62660
|
+
|
|
62661
|
+
function getSaddlePairs(caseId, level, v0, v1, v2, v3) {
|
|
62662
|
+
var centerAbove = (v0 + v1 + v2 + v3) / 4 >= level;
|
|
62663
|
+
if (caseId === 5) {
|
|
62664
|
+
return centerAbove ? SADDLE_C0_C2_JOINED : SADDLE_C0_C2_SPLIT;
|
|
62665
|
+
}
|
|
62666
|
+
return centerAbove ? SADDLE_C1_C3_JOINED : SADDLE_C1_C3_SPLIT;
|
|
62667
|
+
}
|
|
62668
|
+
|
|
62669
|
+
function addSegment(dest, pair, cx, cy, W, hCount) {
|
|
62670
|
+
dest.from.push(getCellEdgeId(pair[0], cx, cy, W, hCount));
|
|
62671
|
+
dest.to.push(getCellEdgeId(pair[1], cx, cy, W, hCount));
|
|
62672
|
+
}
|
|
62673
|
+
|
|
62674
|
+
// Edges are numbered so that neighboring cells agree on the id of the edge
|
|
62675
|
+
// they share: horizontal edges run left to right along a lattice row,
|
|
62676
|
+
// vertical edges run top to bottom down a lattice column.
|
|
62677
|
+
function getCellEdgeId(edge, cx, cy, W, hCount) {
|
|
62678
|
+
if (edge === 0) return cy * (W - 1) + cx; // top
|
|
62679
|
+
if (edge === 1) return hCount + cy * W + cx + 1; // right
|
|
62680
|
+
if (edge === 2) return (cy + 1) * (W - 1) + cx; // bottom
|
|
62681
|
+
return hCount + cy * W + cx; // left
|
|
62682
|
+
}
|
|
62683
|
+
|
|
62684
|
+
// Join directed segments end to end. Because every segment is oriented the
|
|
62685
|
+
// same way relative to the contour, each edge is the start of at most one
|
|
62686
|
+
// segment and the end of at most one, so following the chain is unambiguous.
|
|
62687
|
+
function stitchContourSegments(segments) {
|
|
62688
|
+
var from = segments.from;
|
|
62689
|
+
var to = segments.to;
|
|
62690
|
+
var next = new Map();
|
|
62691
|
+
var hasIncoming = new Set();
|
|
62692
|
+
var visited = new Set();
|
|
62693
|
+
var paths = [];
|
|
62694
|
+
var i, ring;
|
|
62695
|
+
for (i = 0; i < from.length; i++) {
|
|
62696
|
+
next.set(from[i], to[i]);
|
|
62697
|
+
hasIncoming.add(to[i]);
|
|
62698
|
+
}
|
|
62699
|
+
// Trace open contours first, starting from the edges nothing leads into, so
|
|
62700
|
+
// that a contour running between two grid boundaries is not entered part
|
|
62701
|
+
// way along and split in two.
|
|
62702
|
+
for (i = 0; i < from.length; i++) {
|
|
62703
|
+
if (hasIncoming.has(from[i]) || visited.has(from[i])) continue;
|
|
62704
|
+
paths.push(followContour(from[i], next, visited));
|
|
62705
|
+
}
|
|
62706
|
+
// Anything left over forms a closed ring.
|
|
62707
|
+
for (i = 0; i < from.length; i++) {
|
|
62708
|
+
if (visited.has(from[i])) continue;
|
|
62709
|
+
ring = followContour(from[i], next, visited);
|
|
62710
|
+
ring.push(ring[0]);
|
|
62711
|
+
paths.push(ring);
|
|
62712
|
+
}
|
|
62713
|
+
return paths;
|
|
62714
|
+
}
|
|
62715
|
+
|
|
62716
|
+
function followContour(startEdge, next, visited) {
|
|
62717
|
+
var path = [];
|
|
62718
|
+
var edge = startEdge;
|
|
62719
|
+
while (edge !== undefined && !visited.has(edge)) {
|
|
62720
|
+
visited.add(edge);
|
|
62721
|
+
path.push(edge);
|
|
62722
|
+
edge = next.get(edge);
|
|
62723
|
+
}
|
|
62724
|
+
return path;
|
|
62725
|
+
}
|
|
62726
|
+
|
|
62727
|
+
function getPathCoords(path, grid, band, level, hCount, toMapXY) {
|
|
62728
|
+
var coords = [];
|
|
62729
|
+
var prev = null;
|
|
62730
|
+
var point, xy;
|
|
62731
|
+
for (var i = 0; i < path.length; i++) {
|
|
62732
|
+
point = getEdgeCrossing(grid, band, level, path[i], hCount);
|
|
62733
|
+
xy = toMapXY(point[0], point[1]);
|
|
62734
|
+
// A corner value exactly equal to the level puts two crossings in the
|
|
62735
|
+
// same place; keeping both would emit zero-length segments.
|
|
62736
|
+
if (prev && xy[0] === prev[0] && xy[1] === prev[1]) continue;
|
|
62737
|
+
coords.push(xy);
|
|
62738
|
+
prev = xy;
|
|
62739
|
+
}
|
|
62740
|
+
return coords;
|
|
62741
|
+
}
|
|
62742
|
+
|
|
62743
|
+
// Returns the crossing point in lattice coordinates. The result depends only
|
|
62744
|
+
// on the edge and the level, so both cells sharing an edge compute the same
|
|
62745
|
+
// point, down to the last bit.
|
|
62746
|
+
function getEdgeCrossing(grid, band, level, edgeId, hCount) {
|
|
62747
|
+
var W = grid.width;
|
|
62748
|
+
var bands = grid.bands;
|
|
62749
|
+
var samples = grid.samples;
|
|
62750
|
+
var x, y, v0, v1;
|
|
62751
|
+
if (edgeId < hCount) {
|
|
62752
|
+
y = Math.floor(edgeId / (W - 1));
|
|
62753
|
+
x = edgeId - y * (W - 1);
|
|
62754
|
+
v0 = samples[(y * W + x) * bands + band];
|
|
62755
|
+
v1 = samples[(y * W + x + 1) * bands + band];
|
|
62756
|
+
return [x + interpolateCrossing(level, v0, v1), y];
|
|
62757
|
+
}
|
|
62758
|
+
edgeId -= hCount;
|
|
62759
|
+
y = Math.floor(edgeId / W);
|
|
62760
|
+
x = edgeId - y * W;
|
|
62761
|
+
v0 = samples[(y * W + x) * bands + band];
|
|
62762
|
+
v1 = samples[((y + 1) * W + x) * bands + band];
|
|
62763
|
+
return [x, y + interpolateCrossing(level, v0, v1)];
|
|
62764
|
+
}
|
|
62765
|
+
|
|
62766
|
+
function interpolateCrossing(level, v0, v1) {
|
|
62767
|
+
var t;
|
|
62768
|
+
if (v1 === v0) return 0.5;
|
|
62769
|
+
t = (level - v0) / (v1 - v0);
|
|
62770
|
+
return t < 0 ? 0 : t > 1 ? 1 : t;
|
|
62771
|
+
}
|
|
62772
|
+
|
|
62773
|
+
// Lattice coordinates are indexed from the center of the first pixel, not the
|
|
62774
|
+
// corner of the grid.
|
|
62775
|
+
function getLatticeToMapTransform(grid) {
|
|
62776
|
+
var bbox = grid.bbox;
|
|
62777
|
+
var dx = (bbox[2] - bbox[0]) / grid.width;
|
|
62778
|
+
var dy = (bbox[3] - bbox[1]) / grid.height;
|
|
62779
|
+
var x0 = bbox[0] + dx / 2;
|
|
62780
|
+
var y0 = bbox[3] - dy / 2;
|
|
62781
|
+
return function(col, row) {
|
|
62782
|
+
return [x0 + col * dx, y0 - row * dy];
|
|
62783
|
+
};
|
|
62784
|
+
}
|
|
62785
|
+
|
|
62786
|
+
function ascending(a, b) {
|
|
62787
|
+
return a - b;
|
|
62788
|
+
}
|
|
62789
|
+
|
|
62790
|
+
var RasterContours = /*#__PURE__*/Object.freeze({
|
|
62791
|
+
__proto__: null,
|
|
62792
|
+
getContourBand: getContourBand,
|
|
62793
|
+
getContourLevels: getContourLevels,
|
|
62794
|
+
getContourSmoothingDistance: getContourSmoothingDistance,
|
|
62795
|
+
getRasterContourLines: getRasterContourLines,
|
|
62796
|
+
getRasterSampleRange: getRasterSampleRange,
|
|
62797
|
+
traceRasterContours: traceRasterContours,
|
|
62798
|
+
validateRasterGridForContours: validateRasterGridForContours
|
|
62799
|
+
});
|
|
62800
|
+
|
|
62801
|
+
var DEFAULT_CONTOUR_FIELD = 'value';
|
|
62802
|
+
|
|
62803
|
+
cmd.contours = function(targetLyr, targetDataset, opts) {
|
|
62804
|
+
var field = opts.field || DEFAULT_CONTOUR_FIELD;
|
|
62805
|
+
var contours, dataset, outputLayers;
|
|
62806
|
+
if (!layerHasRaster(targetLyr)) {
|
|
62807
|
+
stop$1('Command requires a raster layer');
|
|
62808
|
+
}
|
|
62809
|
+
// Contours come from the layer's working samples, so they reflect any
|
|
62810
|
+
// earlier edits (e.g. -blur, -clip) rather than the original source pixels.
|
|
62811
|
+
contours = getRasterContourLines(targetLyr.raster, opts);
|
|
62812
|
+
message(getContoursMessage(contours));
|
|
62813
|
+
if (contours.lines.length === 0) {
|
|
62814
|
+
return [createEmptyContourLayer(targetLyr, opts)];
|
|
62815
|
+
}
|
|
62816
|
+
dataset = importGeoJSON(getContoursGeoJSON(contours.lines, field), {});
|
|
62817
|
+
setDatasetCrsInfo(dataset, getDatasetCrsInfo(targetDataset));
|
|
62818
|
+
// Smooth before merging, so that only the contour arcs are affected. Merging
|
|
62819
|
+
// first would put them in the same ArcCollection as any vector layer already
|
|
62820
|
+
// in the target dataset, and -smooth rewrites every arc it is given.
|
|
62821
|
+
if (!opts.no_smoothing) {
|
|
62822
|
+
smoothContourDataset(dataset, getRasterGrid(targetLyr.raster));
|
|
62823
|
+
}
|
|
62824
|
+
outputLayers = mergeDatasetsIntoDataset(targetDataset, [dataset]);
|
|
62825
|
+
setOutputLayerName(outputLayers[0], targetLyr, 'contours', opts);
|
|
62826
|
+
return outputLayers;
|
|
62827
|
+
};
|
|
62828
|
+
|
|
62829
|
+
function smoothContourDataset(dataset, grid) {
|
|
62830
|
+
var crs = getDatasetCRS(dataset);
|
|
62831
|
+
var distance = getContourSmoothingDistance(grid, crs);
|
|
62832
|
+
if (!(distance > 0)) {
|
|
62833
|
+
message('Skipped contour smoothing: unable to determine the pixel size');
|
|
62834
|
+
return;
|
|
62835
|
+
}
|
|
62836
|
+
message('Smoothing contours with an auto-selected interval of ' +
|
|
62837
|
+
formatSmoothingDistance(distance, crs) + ' (one pixel)');
|
|
62838
|
+
cmd.smooth(dataset, {
|
|
62839
|
+
distance: distance,
|
|
62840
|
+
// The contour staircase is an artifact, so there are no real corners to
|
|
62841
|
+
// preserve and no sub-pixel detail worth prefiltering out.
|
|
62842
|
+
no_corners: true,
|
|
62843
|
+
no_prefilter: true
|
|
62844
|
+
}, dataset.layers);
|
|
62845
|
+
}
|
|
62846
|
+
|
|
62847
|
+
function formatSmoothingDistance(distance, crs) {
|
|
62848
|
+
var rounded = Number(distance.toPrecision(3));
|
|
62849
|
+
return crs ? rounded + 'm' : String(rounded);
|
|
62850
|
+
}
|
|
62851
|
+
|
|
62852
|
+
function getContoursGeoJSON(lines, field) {
|
|
62853
|
+
return {
|
|
62854
|
+
type: 'FeatureCollection',
|
|
62855
|
+
features: lines.map(function(line) {
|
|
62856
|
+
var properties = {};
|
|
62857
|
+
properties[field] = line.value;
|
|
62858
|
+
return {
|
|
62859
|
+
type: 'Feature',
|
|
62860
|
+
properties: properties,
|
|
62861
|
+
geometry: {
|
|
62862
|
+
type: 'LineString',
|
|
62863
|
+
coordinates: line.coords
|
|
62864
|
+
}
|
|
62865
|
+
};
|
|
62866
|
+
})
|
|
62867
|
+
};
|
|
62868
|
+
}
|
|
62869
|
+
|
|
62870
|
+
function createEmptyContourLayer(targetLyr, opts) {
|
|
62871
|
+
var lyr = {
|
|
62872
|
+
geometry_type: 'polyline',
|
|
62873
|
+
shapes: [],
|
|
62874
|
+
data: new DataTable([])
|
|
62875
|
+
};
|
|
62876
|
+
setOutputLayerName(lyr, targetLyr, 'contours', opts);
|
|
62877
|
+
return lyr;
|
|
62878
|
+
}
|
|
62879
|
+
|
|
62880
|
+
function getContoursMessage(contours) {
|
|
62881
|
+
var levels = contours.levels;
|
|
62882
|
+
if (levels.length === 0) {
|
|
62883
|
+
return 'No contour levels fall inside the range of this raster';
|
|
62884
|
+
}
|
|
62885
|
+
return 'Traced ' + contours.lines.length + ' contour ' +
|
|
62886
|
+
(contours.lines.length == 1 ? 'line' : 'lines') + ' at ' + levels.length +
|
|
62887
|
+
(levels.length == 1 ? ' level' : ' levels') +
|
|
62888
|
+
' (' + levels[0] + ' to ' + levels[levels.length - 1] + ')';
|
|
62889
|
+
}
|
|
62890
|
+
|
|
62048
62891
|
cmd.dashlines = function(lyr, dataset, opts) {
|
|
62049
62892
|
var crs = getDatasetCRS(dataset);
|
|
62050
62893
|
var defs = getStashedVar('defs');
|
|
@@ -63593,11 +64436,19 @@ ${svg}
|
|
|
63593
64436
|
//
|
|
63594
64437
|
cmd.dissolve = function(arg1, arg2, opts) {
|
|
63595
64438
|
if (Array.isArray(arg1)) {
|
|
64439
|
+
arg1.forEach(requireDissolvableLayer);
|
|
63596
64440
|
return dissolveLayers(arg1, arg2, opts);
|
|
63597
64441
|
}
|
|
64442
|
+
requireDissolvableLayer(arg1);
|
|
63598
64443
|
return dissolveSingleLayer(arg1, arg2, opts);
|
|
63599
64444
|
};
|
|
63600
64445
|
|
|
64446
|
+
// Without this guard a raster layer takes the tabular branch of
|
|
64447
|
+
// dissolveOneLayer() and is replaced by a geometry-less aggregate.
|
|
64448
|
+
function requireDissolvableLayer(lyr) {
|
|
64449
|
+
requireNotRasterLayer(lyr, 'Raster layers cannot be dissolved');
|
|
64450
|
+
}
|
|
64451
|
+
|
|
63601
64452
|
function dissolveLayers(layers, dataset, optsArg) {
|
|
63602
64453
|
var opts = utils.extend({}, optsArg);
|
|
63603
64454
|
if (opts.field) opts.fields = [opts.field]; // support old "field" parameter
|
|
@@ -72671,7 +73522,7 @@ ${svg}
|
|
|
72671
73522
|
}
|
|
72672
73523
|
return [
|
|
72673
73524
|
'affine', 'alpha-shapes', 'blur', 'buffer', 'calc', 'check-geometry',
|
|
72674
|
-
'classify', 'clean', 'clip', 'cluster', 'dashlines', 'data-fill',
|
|
73525
|
+
'classify', 'clean', 'clip', 'cluster', 'contours', 'dashlines', 'data-fill',
|
|
72675
73526
|
'densify', 'dissolve', 'dissolve2', 'divide', 'dots', 'each', 'erase',
|
|
72676
73527
|
'explode', 'filter',
|
|
72677
73528
|
'filter-detail', 'filter-fields', 'filter-geom', 'filter-islands',
|
|
@@ -72848,6 +73699,9 @@ ${svg}
|
|
|
72848
73699
|
// } else if (name == 'comment') {
|
|
72849
73700
|
// // no-op
|
|
72850
73701
|
|
|
73702
|
+
} else if (name == 'contours') {
|
|
73703
|
+
outputLayers = applyCommandToEachLayer(cmd.contours, targetLayers, targetDataset, opts);
|
|
73704
|
+
|
|
72851
73705
|
} else if (name == 'dashlines') {
|
|
72852
73706
|
applyCommandToEachLayer(cmd.dashlines, targetLayers, targetDataset, opts);
|
|
72853
73707
|
|
|
@@ -73332,7 +74186,7 @@ ${svg}
|
|
|
73332
74186
|
return name == 'rectangle' || name == 'rectangles' || name == 'filter' && opts.cleanup;
|
|
73333
74187
|
}
|
|
73334
74188
|
|
|
73335
|
-
var version = "0.7.
|
|
74189
|
+
var version = "0.7.49";
|
|
73336
74190
|
|
|
73337
74191
|
// Parse command line args into commands and run them
|
|
73338
74192
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|
|
@@ -73996,10 +74850,10 @@ ${svg}
|
|
|
73996
74850
|
|
|
73997
74851
|
var BOX_BLUR_PASSES = 3;
|
|
73998
74852
|
|
|
73999
|
-
function blurRasterGrid(raster, optsArg) {
|
|
74853
|
+
function blurRasterGrid(raster, optsArg, crs) {
|
|
74000
74854
|
var opts = optsArg || {};
|
|
74001
74855
|
var grid = getRasterGrid(raster);
|
|
74002
|
-
var radius = getBlurRadius(opts);
|
|
74856
|
+
var radius = getBlurRadius(grid, opts, crs);
|
|
74003
74857
|
var sigma = radius / 2;
|
|
74004
74858
|
var boxes = getGaussianBoxWidths(sigma, BOX_BLUR_PASSES);
|
|
74005
74859
|
var samples = new grid.samples.constructor(grid.samples.length);
|
|
@@ -74023,20 +74877,48 @@ ${svg}
|
|
|
74023
74877
|
});
|
|
74024
74878
|
}
|
|
74025
74879
|
|
|
74026
|
-
|
|
74880
|
+
// The blur kernel works in pixels, but the radius may be given either in pixels
|
|
74881
|
+
// (a bare number or a px suffix) or as a real-world distance (e.g. 20m), which
|
|
74882
|
+
// is converted using the size of a pixel on the ground.
|
|
74883
|
+
function getBlurRadius(grid, opts, crs) {
|
|
74027
74884
|
var arg = opts.radius;
|
|
74028
|
-
var radius;
|
|
74029
|
-
if (arg
|
|
74030
|
-
|
|
74031
|
-
|
|
74885
|
+
var measure, radius;
|
|
74886
|
+
if (arg === null || arg === undefined || arg === '') {
|
|
74887
|
+
stop$1('Missing blur radius');
|
|
74888
|
+
}
|
|
74889
|
+
if (typeof arg == 'string' && /px$/i.test(arg.trim())) {
|
|
74890
|
+
radius = Number(arg.trim().replace(/px$/i, ''));
|
|
74891
|
+
} else {
|
|
74892
|
+
measure = parseMeasure(arg);
|
|
74893
|
+
radius = measure.units ?
|
|
74894
|
+
convertDistanceToPixels(arg, grid, crs) :
|
|
74895
|
+
measure.value;
|
|
74032
74896
|
}
|
|
74033
|
-
radius = Number(arg);
|
|
74034
74897
|
if (!(radius > 0 && isFinite(radius))) {
|
|
74035
|
-
stop$1('Expected
|
|
74898
|
+
stop$1('Expected a positive blur radius, received:', arg);
|
|
74036
74899
|
}
|
|
74037
74900
|
return radius;
|
|
74038
74901
|
}
|
|
74039
74902
|
|
|
74903
|
+
function convertDistanceToPixels(arg, grid, crs) {
|
|
74904
|
+
// convertIntervalParam() gives the distance in the coordinate units of the
|
|
74905
|
+
// dataset, which is what the grid's bbox is measured in.
|
|
74906
|
+
return convertIntervalParam(arg, crs) / getPixelSize(grid);
|
|
74907
|
+
}
|
|
74908
|
+
|
|
74909
|
+
// Blurring is isotropic in pixel space, so a distance has to map to a single
|
|
74910
|
+
// pixel count; use the geometric mean when the pixels are not square.
|
|
74911
|
+
function getPixelSize(grid) {
|
|
74912
|
+
var bbox = grid.bbox;
|
|
74913
|
+
var dx, dy;
|
|
74914
|
+
if (!bbox) {
|
|
74915
|
+
stop$1('Raster layer is missing the georeferencing needed to blur by a distance');
|
|
74916
|
+
}
|
|
74917
|
+
dx = Math.abs(bbox[2] - bbox[0]) / grid.width;
|
|
74918
|
+
dy = Math.abs(bbox[3] - bbox[1]) / grid.height;
|
|
74919
|
+
return Math.sqrt(dx * dy);
|
|
74920
|
+
}
|
|
74921
|
+
|
|
74040
74922
|
function validateBlurGrid(grid) {
|
|
74041
74923
|
if (!grid || !grid.samples || !grid.width || !grid.height || !grid.bands) {
|
|
74042
74924
|
stop$1('Expected a raster grid');
|
|
@@ -74185,8 +75067,8 @@ ${svg}
|
|
|
74185
75067
|
|
|
74186
75068
|
function copyUnpremultipliedChannelToBand(channel, alpha, samples, grid, band) {
|
|
74187
75069
|
var bands = grid.bands;
|
|
74188
|
-
var isFloat = samples
|
|
74189
|
-
var range = isFloat ? null :
|
|
75070
|
+
var isFloat = rasterSamplesAreFloat(samples);
|
|
75071
|
+
var range = isFloat ? null : getSampleArrayRange(samples);
|
|
74190
75072
|
var maxAlpha = getAlphaMaxValue(samples);
|
|
74191
75073
|
var val, a;
|
|
74192
75074
|
for (var i = 0, j = band; i < channel.length; i++, j += bands) {
|
|
@@ -74198,8 +75080,8 @@ ${svg}
|
|
|
74198
75080
|
|
|
74199
75081
|
function copyChannelToBand(channel, samples, grid, band) {
|
|
74200
75082
|
var bands = grid.bands;
|
|
74201
|
-
var isFloat = samples
|
|
74202
|
-
var range = isFloat ? null :
|
|
75083
|
+
var isFloat = rasterSamplesAreFloat(samples);
|
|
75084
|
+
var range = isFloat ? null : getSampleArrayRange(samples);
|
|
74203
75085
|
var val;
|
|
74204
75086
|
for (var i = 0, j = band; i < channel.length; i++, j += bands) {
|
|
74205
75087
|
val = channel[i];
|
|
@@ -74208,62 +75090,47 @@ ${svg}
|
|
|
74208
75090
|
}
|
|
74209
75091
|
|
|
74210
75092
|
function getRasterBlurWeights(grid) {
|
|
74211
|
-
if (!grid
|
|
75093
|
+
if (!rasterGridHasInvalidPixels(grid)) return null;
|
|
74212
75094
|
var weights = new Float32Array(grid.width * grid.height);
|
|
74213
75095
|
for (var i = 0; i < weights.length; i++) {
|
|
74214
|
-
weights[i] =
|
|
75096
|
+
weights[i] = rasterPixelIsValid(grid, i) ? 1 : 0;
|
|
74215
75097
|
}
|
|
74216
75098
|
return weights;
|
|
74217
75099
|
}
|
|
74218
75100
|
|
|
74219
|
-
function rasterBlurPixelIsValid(grid, pixelId) {
|
|
74220
|
-
var off, n;
|
|
74221
|
-
if (grid.coverage && grid.coverage[pixelId] === 0) return false;
|
|
74222
|
-
if (grid.nodata == null) return true;
|
|
74223
|
-
off = pixelId * grid.bands;
|
|
74224
|
-
n = Math.min(grid.bands, 3);
|
|
74225
|
-
for (var i = 0; i < n; i++) {
|
|
74226
|
-
if (grid.samples[off + i] != grid.nodata) return true;
|
|
74227
|
-
}
|
|
74228
|
-
return false;
|
|
74229
|
-
}
|
|
74230
|
-
|
|
74231
75101
|
function getAlphaMaxValue(samples) {
|
|
74232
|
-
if (samples
|
|
74233
|
-
return
|
|
74234
|
-
}
|
|
74235
|
-
|
|
74236
|
-
function getTypedArrayRange(arr) {
|
|
74237
|
-
if (arr instanceof Uint8Array || arr instanceof Uint8ClampedArray) return {min: 0, max: 255};
|
|
74238
|
-
if (arr instanceof Int8Array) return {min: -128, max: 127};
|
|
74239
|
-
if (arr instanceof Uint16Array) return {min: 0, max: 65535};
|
|
74240
|
-
if (arr instanceof Int16Array) return {min: -32768, max: 32767};
|
|
74241
|
-
if (arr instanceof Uint32Array) return {min: 0, max: 4294967295};
|
|
74242
|
-
if (arr instanceof Int32Array) return {min: -2147483648, max: 2147483647};
|
|
74243
|
-
return {min: -Infinity, max: Infinity};
|
|
75102
|
+
if (rasterSamplesAreFloat(samples)) return 1;
|
|
75103
|
+
return getSampleArrayRange(samples).max;
|
|
74244
75104
|
}
|
|
74245
75105
|
|
|
74246
75106
|
function clamp(val, min, max) {
|
|
74247
75107
|
return val < min ? min : val > max ? max : val;
|
|
74248
75108
|
}
|
|
74249
75109
|
|
|
75110
|
+
var RasterBlur = /*#__PURE__*/Object.freeze({
|
|
75111
|
+
__proto__: null,
|
|
75112
|
+
blurRasterGrid: blurRasterGrid,
|
|
75113
|
+
getBlurRadius: getBlurRadius
|
|
75114
|
+
});
|
|
75115
|
+
|
|
74250
75116
|
cmd.blur = blurRasterLayers;
|
|
74251
75117
|
|
|
74252
75118
|
function blurRasterLayers(layers, dataset, optsArg) {
|
|
74253
75119
|
var opts = optsArg || {};
|
|
75120
|
+
var crs = getDatasetCRS(dataset);
|
|
74254
75121
|
requireProjectedDataset(dataset);
|
|
74255
75122
|
layers.forEach(function(lyr) {
|
|
74256
75123
|
if (!layerHasRaster(lyr)) {
|
|
74257
75124
|
stop$1('Command requires a raster layer');
|
|
74258
75125
|
}
|
|
74259
|
-
blurRasterLayer(lyr, opts);
|
|
75126
|
+
blurRasterLayer(lyr, opts, crs);
|
|
74260
75127
|
});
|
|
74261
75128
|
}
|
|
74262
75129
|
|
|
74263
|
-
function blurRasterLayer(lyr, opts) {
|
|
75130
|
+
function blurRasterLayer(lyr, opts, crs) {
|
|
74264
75131
|
var raster = lyr.raster;
|
|
74265
75132
|
noteLayerWillChange(lyr, {operation: 'blurRasterLayer', unit: 'raster'});
|
|
74266
|
-
raster.grid = blurRasterGrid(raster, opts);
|
|
75133
|
+
raster.grid = blurRasterGrid(raster, opts, crs);
|
|
74267
75134
|
raster.view = raster.view || {};
|
|
74268
75135
|
delete raster.view.scalingStats;
|
|
74269
75136
|
if (runningInBrowser()) {
|
|
@@ -75167,6 +76034,7 @@ ${svg}
|
|
|
75167
76034
|
Catalog$1,
|
|
75168
76035
|
ClipErase,
|
|
75169
76036
|
ClipPoints,
|
|
76037
|
+
ColorUtils,
|
|
75170
76038
|
Colorizer,
|
|
75171
76039
|
CustomProjections,
|
|
75172
76040
|
Dymaxion,
|
|
@@ -75214,6 +76082,9 @@ ${svg}
|
|
|
75214
76082
|
Lines,
|
|
75215
76083
|
Logging,
|
|
75216
76084
|
Profile,
|
|
76085
|
+
RasterBlur,
|
|
76086
|
+
RasterContours,
|
|
76087
|
+
RasterGrid,
|
|
75217
76088
|
RasterReprojection,
|
|
75218
76089
|
RasterUtils,
|
|
75219
76090
|
Merging,
|