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/package.json
CHANGED
package/www/mapshaper-gui.js
CHANGED
|
@@ -4544,6 +4544,77 @@
|
|
|
4544
4544
|
return this;
|
|
4545
4545
|
};
|
|
4546
4546
|
|
|
4547
|
+
// Low-level access to raster grid samples, shared by the code that reads or
|
|
4548
|
+
// rewrites pixels (-proj, -blur, clipping and preview rendering). These used
|
|
4549
|
+
// to be reimplemented per module, which let the nodata and coverage rules
|
|
4550
|
+
// drift apart.
|
|
4551
|
+
//
|
|
4552
|
+
// Samples are band-interleaved: pixel i occupies
|
|
4553
|
+
// samples[i * bands] through samples[i * bands + bands - 1].
|
|
4554
|
+
|
|
4555
|
+
// A pixel is nodata only when every one of its color-carrying bands matches
|
|
4556
|
+
// the nodata value. An alpha band is ignored, so a transparent pixel whose
|
|
4557
|
+
// color channels hold real values is still treated as data.
|
|
4558
|
+
function samplesAreNodataAtOffset(samples, offset, bands, nodata) {
|
|
4559
|
+
var n = Math.min(bands, 3);
|
|
4560
|
+
if (nodata === null || nodata === undefined) return false;
|
|
4561
|
+
for (var i = 0; i < n; i++) {
|
|
4562
|
+
if (samples[offset + i] != nodata) return false;
|
|
4563
|
+
}
|
|
4564
|
+
return true;
|
|
4565
|
+
}
|
|
4566
|
+
|
|
4567
|
+
function rasterPixelIsNodata(grid, pixelId) {
|
|
4568
|
+
return samplesAreNodataAtOffset(grid.samples, pixelId * grid.bands, grid.bands, grid.nodata);
|
|
4569
|
+
}
|
|
4570
|
+
|
|
4571
|
+
// The coverage mask is set by reprojection to record which output pixels
|
|
4572
|
+
// received source content, independently of the nodata fill value.
|
|
4573
|
+
function rasterPixelIsCovered(grid, pixelId) {
|
|
4574
|
+
return !grid.coverage || grid.coverage[pixelId] > 0;
|
|
4575
|
+
}
|
|
4576
|
+
|
|
4577
|
+
function rasterPixelIsValid(grid, pixelId) {
|
|
4578
|
+
return rasterPixelIsCovered(grid, pixelId) && !rasterPixelIsNodata(grid, pixelId);
|
|
4579
|
+
}
|
|
4580
|
+
|
|
4581
|
+
// False when every pixel is known to be valid, so callers can skip
|
|
4582
|
+
// per-pixel validity testing entirely.
|
|
4583
|
+
function rasterGridHasInvalidPixels(grid) {
|
|
4584
|
+
return !!grid.coverage || grid.nodata !== null && grid.nodata !== undefined;
|
|
4585
|
+
}
|
|
4586
|
+
|
|
4587
|
+
// One byte per pixel, so that code sampling a grid repeatedly can test
|
|
4588
|
+
// validity with a single lookup instead of re-reading every band. Returns
|
|
4589
|
+
// the coverage mask itself when there is no nodata value to fold in, and
|
|
4590
|
+
// null when every pixel is valid.
|
|
4591
|
+
function getRasterValidityMask(grid) {
|
|
4592
|
+
var n = grid.width * grid.height;
|
|
4593
|
+
var mask, i;
|
|
4594
|
+
if (grid.nodata === null || grid.nodata === undefined) return grid.coverage || null;
|
|
4595
|
+
mask = new Uint8Array(n);
|
|
4596
|
+
for (i = 0; i < n; i++) {
|
|
4597
|
+
mask[i] = rasterPixelIsValid(grid, i) ? 1 : 0;
|
|
4598
|
+
}
|
|
4599
|
+
return mask;
|
|
4600
|
+
}
|
|
4601
|
+
|
|
4602
|
+
function rasterSamplesAreFloat(samples) {
|
|
4603
|
+
return samples instanceof Float32Array || samples instanceof Float64Array;
|
|
4604
|
+
}
|
|
4605
|
+
|
|
4606
|
+
// Value range of a sample array, used to clamp computed values before
|
|
4607
|
+
// writing them back into an integer array.
|
|
4608
|
+
function getSampleArrayRange(samples) {
|
|
4609
|
+
if (samples instanceof Uint8Array || samples instanceof Uint8ClampedArray) return {min: 0, max: 255};
|
|
4610
|
+
if (samples instanceof Int8Array) return {min: -128, max: 127};
|
|
4611
|
+
if (samples instanceof Uint16Array) return {min: 0, max: 65535};
|
|
4612
|
+
if (samples instanceof Int16Array) return {min: -32768, max: 32767};
|
|
4613
|
+
if (samples instanceof Uint32Array) return {min: 0, max: 4294967295};
|
|
4614
|
+
if (samples instanceof Int32Array) return {min: -2147483648, max: 2147483647};
|
|
4615
|
+
return {min: -Infinity, max: Infinity};
|
|
4616
|
+
}
|
|
4617
|
+
|
|
4547
4618
|
function runningInBrowser() {
|
|
4548
4619
|
return typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
|
4549
4620
|
}
|
|
@@ -4823,6 +4894,9 @@
|
|
|
4823
4894
|
function copyRasterGrid(grid) {
|
|
4824
4895
|
var copy = utils.extend({}, grid);
|
|
4825
4896
|
if (grid.samples) copy.samples = copyTypedArray(grid.samples);
|
|
4897
|
+
// The coverage mask is mutated in place by the reprojection code, so an
|
|
4898
|
+
// undo snapshot that shared it would track later edits.
|
|
4899
|
+
if (grid.coverage) copy.coverage = copyTypedArray(grid.coverage);
|
|
4826
4900
|
if (grid.sampleBands) copy.sampleBands = grid.sampleBands.concat();
|
|
4827
4901
|
if (grid.bbox) copy.bbox = grid.bbox.concat();
|
|
4828
4902
|
if (grid.transform) copy.transform = copyObjectOrArray(grid.transform);
|
|
@@ -4930,6 +5004,69 @@
|
|
|
4930
5004
|
return stats;
|
|
4931
5005
|
}
|
|
4932
5006
|
|
|
5007
|
+
// Returns the band values and display color of the pixel containing a map
|
|
5008
|
+
// coordinate, for readouts like the GUI's right-click menu, or null if the point
|
|
5009
|
+
// falls outside the raster. Rotated grids are not supported, because pixels can
|
|
5010
|
+
// only be located through the axis-aligned bbox.
|
|
5011
|
+
// x, y: a point in the coordinate system of the raster's bbox
|
|
5012
|
+
function getRasterPixelAtMapXY(raster, x, y) {
|
|
5013
|
+
var grid = getRasterGrid(raster);
|
|
5014
|
+
var col, row, pixelId, offset, values, valid, isColor, band;
|
|
5015
|
+
if (!grid || !grid.samples || !grid.bbox || rasterGridIsRotated(grid)) return null;
|
|
5016
|
+
col = Math.floor(mapXToRasterPixel(grid, x));
|
|
5017
|
+
row = Math.floor(mapYToRasterPixel(grid, y));
|
|
5018
|
+
if (col < 0 || col >= grid.width || row < 0 || row >= grid.height) return null;
|
|
5019
|
+
pixelId = row * grid.width + col;
|
|
5020
|
+
offset = pixelId * grid.bands;
|
|
5021
|
+
values = [];
|
|
5022
|
+
for (band = 0; band < grid.bands; band++) {
|
|
5023
|
+
values.push(grid.samples[offset + band]);
|
|
5024
|
+
}
|
|
5025
|
+
// The renderer reads interleaved bands in display order, so three or more
|
|
5026
|
+
// bands are shown as R, G, B and (if present) alpha.
|
|
5027
|
+
isColor = grid.bands >= 3;
|
|
5028
|
+
valid = rasterPixelIsValid(grid, pixelId);
|
|
5029
|
+
return {
|
|
5030
|
+
col: col,
|
|
5031
|
+
row: row,
|
|
5032
|
+
values: values,
|
|
5033
|
+
isColor: isColor,
|
|
5034
|
+
isFloat: rasterSamplesAreFloat(grid.samples),
|
|
5035
|
+
valid: valid,
|
|
5036
|
+
// Only color images get a color, so that inspecting a measurement raster
|
|
5037
|
+
// does not trigger a scaling-stats scan of the whole grid.
|
|
5038
|
+
// [r, g, b, a] in the 0-255 range, for formatColor() or a canvas.
|
|
5039
|
+
color: isColor && valid ? getRasterPixelDisplayColor(raster, grid, pixelId) : null
|
|
5040
|
+
};
|
|
5041
|
+
}
|
|
5042
|
+
|
|
5043
|
+
// Returns [r, g, b, a] in the 0-255 range, using the same recipe and scaling as
|
|
5044
|
+
// the preview renderer, so that a readout agrees with how the pixel is drawn.
|
|
5045
|
+
function getRasterPixelDisplayColor(raster, grid, pixelId) {
|
|
5046
|
+
var view = getRasterView(raster);
|
|
5047
|
+
var recipe = getRasterViewRecipe(grid, view && view.recipe);
|
|
5048
|
+
var stats = getRasterViewScalingStats(raster, recipe);
|
|
5049
|
+
var sourceRange = recipe.scaling == 'none' ? getPixelTypeRange(grid.pixelType) : null;
|
|
5050
|
+
var displayRange = getDisplayRange(recipe.scaleRange);
|
|
5051
|
+
var offset = pixelId * grid.bands;
|
|
5052
|
+
var color = [0, 1, 2].map(function(band) {
|
|
5053
|
+
return scaleSample(grid.samples[offset + band], stats && stats[band], sourceRange, displayRange);
|
|
5054
|
+
});
|
|
5055
|
+
color.push(grid.bands >= 4 ?
|
|
5056
|
+
scaleSample(grid.samples[offset + 3], stats && stats[3], sourceRange, [0, 255]) : 255);
|
|
5057
|
+
return color;
|
|
5058
|
+
}
|
|
5059
|
+
|
|
5060
|
+
// Float samples print with binary noise at full precision (a Float32 holding
|
|
5061
|
+
// 1234.5678 prints as 1234.5677490234375), so they are rounded to the number of
|
|
5062
|
+
// digits that the type can actually represent.
|
|
5063
|
+
function formatRasterSampleValue(val, isFloat) {
|
|
5064
|
+
if (val === null || val === undefined) return '';
|
|
5065
|
+
if (!isFinite(val)) return String(val); // NaN, Infinity
|
|
5066
|
+
if (!isFloat) return String(val);
|
|
5067
|
+
return String(+val.toPrecision(7));
|
|
5068
|
+
}
|
|
5069
|
+
|
|
4933
5070
|
function renderRasterGridPreview(grid, recipe, width, height, statsArg, sourceBbox, resamplingMethod) {
|
|
4934
5071
|
var samples = grid.samples;
|
|
4935
5072
|
var bands = grid.bands;
|
|
@@ -5172,10 +5309,22 @@
|
|
|
5172
5309
|
return (sy * grid.width + sx) * bands;
|
|
5173
5310
|
}
|
|
5174
5311
|
|
|
5312
|
+
// A grid is north-up when its affine transform has no rotation/skew terms.
|
|
5313
|
+
// Cropping and reprojection both index pixels through the axis-aligned bbox,
|
|
5314
|
+
// so they can only handle north-up grids.
|
|
5315
|
+
function rasterGridIsRotated(grid) {
|
|
5316
|
+
var t = grid && grid.transform;
|
|
5317
|
+
return !!t && (t[1] !== 0 || t[3] !== 0);
|
|
5318
|
+
}
|
|
5319
|
+
|
|
5175
5320
|
function clipRasterToBBox(lyr, bbox, opts) {
|
|
5176
5321
|
var raster = lyr.raster;
|
|
5177
5322
|
var grid = getRasterGrid(raster);
|
|
5178
|
-
var clipBbox
|
|
5323
|
+
var clipBbox;
|
|
5324
|
+
if (rasterGridIsRotated(grid)) {
|
|
5325
|
+
stop('Clipping a rotated or skewed raster is not supported');
|
|
5326
|
+
}
|
|
5327
|
+
clipBbox = intersectBboxes(grid.bbox, bbox);
|
|
5179
5328
|
if (!clipBbox) {
|
|
5180
5329
|
warn('Raster clipping rectangle does not intersect the raster layer');
|
|
5181
5330
|
return false;
|
|
@@ -5236,11 +5385,30 @@
|
|
|
5236
5385
|
width: crop.width,
|
|
5237
5386
|
height: crop.height,
|
|
5238
5387
|
samples: samples,
|
|
5388
|
+
coverage: cropRasterCoverage(grid, crop),
|
|
5239
5389
|
bbox: bbox,
|
|
5240
5390
|
transform: updateTransformForBBox(grid.transform, bbox, crop.width, crop.height)
|
|
5241
5391
|
});
|
|
5242
5392
|
}
|
|
5243
5393
|
|
|
5394
|
+
// The coverage mask is one byte per pixel (not per sample), and must be
|
|
5395
|
+
// cropped alongside the samples -- consumers index it with the cropped
|
|
5396
|
+
// grid's dimensions.
|
|
5397
|
+
function cropRasterCoverage(grid, crop) {
|
|
5398
|
+
var coverage = grid.coverage;
|
|
5399
|
+
var cropped, src, dest, x, y;
|
|
5400
|
+
if (!coverage) return coverage;
|
|
5401
|
+
cropped = new Uint8Array(crop.width * crop.height);
|
|
5402
|
+
for (y = 0; y < crop.height; y++) {
|
|
5403
|
+
src = (crop.y + y) * grid.width + crop.x;
|
|
5404
|
+
dest = y * crop.width;
|
|
5405
|
+
for (x = 0; x < crop.width; x++) {
|
|
5406
|
+
cropped[dest + x] = coverage[src + x];
|
|
5407
|
+
}
|
|
5408
|
+
}
|
|
5409
|
+
return cropped;
|
|
5410
|
+
}
|
|
5411
|
+
|
|
5244
5412
|
function getRasterLayerBounds(lyr) {
|
|
5245
5413
|
var bbox = lyr.raster && getRasterBBox(lyr.raster);
|
|
5246
5414
|
return bbox && bbox.length == 4 ? new Bounds(bbox) : null;
|
|
@@ -5501,11 +5669,7 @@
|
|
|
5501
5669
|
}
|
|
5502
5670
|
|
|
5503
5671
|
function allSamplesAreNoData(data, offset, bands, noData) {
|
|
5504
|
-
|
|
5505
|
-
for (var i = 0; i < n; i++) {
|
|
5506
|
-
if (data[offset + i] != noData) return false;
|
|
5507
|
-
}
|
|
5508
|
-
return true;
|
|
5672
|
+
return samplesAreNodataAtOffset(data, offset, bands, noData);
|
|
5509
5673
|
}
|
|
5510
5674
|
|
|
5511
5675
|
function copyObjectOrArray(obj) {
|
|
@@ -6539,7 +6703,6 @@
|
|
|
6539
6703
|
notifyUndoWarning(gui, 'Undo restore data was not deleted', e);
|
|
6540
6704
|
});
|
|
6541
6705
|
}, {
|
|
6542
|
-
maxStates: opts.maxStates,
|
|
6543
6706
|
evictToken: evictToken,
|
|
6544
6707
|
preserveOnModeChange: true
|
|
6545
6708
|
});
|
|
@@ -6713,8 +6876,6 @@
|
|
|
6713
6876
|
// instead.
|
|
6714
6877
|
|
|
6715
6878
|
|
|
6716
|
-
var DEFAULT_HISTORY_LIMIT = 10;
|
|
6717
|
-
|
|
6718
6879
|
// Returns true if the manifest, URL query, gui-installed checker, or
|
|
6719
6880
|
// localStorage indicates that app-level undo should be active.
|
|
6720
6881
|
function appUndoIsEnabled(gui) {
|
|
@@ -6754,11 +6915,6 @@
|
|
|
6754
6915
|
return gui.storedUndoHistory;
|
|
6755
6916
|
}
|
|
6756
6917
|
|
|
6757
|
-
function getUndoHistoryLimit(gui) {
|
|
6758
|
-
var opt = gui && gui.options && gui.options.undoHistoryLimit;
|
|
6759
|
-
return opt > 0 ? opt : DEFAULT_HISTORY_LIMIT;
|
|
6760
|
-
}
|
|
6761
|
-
|
|
6762
6918
|
// Construct an UndoTransaction if and only if app undo is enabled and the
|
|
6763
6919
|
// transaction can plausibly be added to history. Returns null when undo is
|
|
6764
6920
|
// off, when the gui's undo manager is missing, or when the UndoTransaction
|
|
@@ -6772,12 +6928,13 @@
|
|
|
6772
6928
|
return Transaction ? new Transaction(label || '') : null;
|
|
6773
6929
|
}
|
|
6774
6930
|
|
|
6775
|
-
// Add a captured transaction to gui-level history.
|
|
6776
|
-
// stored-undo-history.addTransaction()
|
|
6931
|
+
// Add a captured transaction to gui-level history. Options are passed through
|
|
6932
|
+
// to stored-undo-history.addTransaction(). History is not capped by entry
|
|
6933
|
+
// count: entries are dropped only when their restore data no longer fits in
|
|
6934
|
+
// the payload store.
|
|
6777
6935
|
function addUndoTransactionToHistory(gui, tx, opts) {
|
|
6778
6936
|
if (!tx) return Promise.resolve(null);
|
|
6779
|
-
|
|
6780
|
-
return getStoredUndoHistory(gui).addTransaction(tx, fullOpts).catch(function(e) {
|
|
6937
|
+
return getStoredUndoHistory(gui).addTransaction(tx, opts || {}).catch(function(e) {
|
|
6781
6938
|
console.error(e);
|
|
6782
6939
|
});
|
|
6783
6940
|
}
|
|
@@ -9336,7 +9493,9 @@
|
|
|
9336
9493
|
this.runCommand = function(str) {
|
|
9337
9494
|
str = str.trim();
|
|
9338
9495
|
if (!str) return;
|
|
9339
|
-
gui.
|
|
9496
|
+
if (!gui.consoleIsOpen()) {
|
|
9497
|
+
gui.toggleSidebarPanel('console');
|
|
9498
|
+
}
|
|
9340
9499
|
submit(str);
|
|
9341
9500
|
};
|
|
9342
9501
|
|
|
@@ -9884,7 +10043,6 @@
|
|
|
9884
10043
|
error: err,
|
|
9885
10044
|
flags: flags,
|
|
9886
10045
|
entryPrefix: 'command',
|
|
9887
|
-
maxStates: getCommandUndoHistoryLimit(),
|
|
9888
10046
|
onUndo: function() {
|
|
9889
10047
|
gui.session.setCommandsActive(historyIds, false);
|
|
9890
10048
|
},
|
|
@@ -9899,11 +10057,6 @@
|
|
|
9899
10057
|
return appUndoIsEnabled(gui);
|
|
9900
10058
|
}
|
|
9901
10059
|
|
|
9902
|
-
function getCommandUndoHistoryLimit() {
|
|
9903
|
-
var opt = gui.options && gui.options.undoHistoryLimit;
|
|
9904
|
-
return opt > 0 ? opt : 10;
|
|
9905
|
-
}
|
|
9906
|
-
|
|
9907
10060
|
function logCommandTiming(commandString, commands, err, totalMillis, undoTiming) {
|
|
9908
10061
|
var enabled = getQueryFlag('command-timing') == 'on' ||
|
|
9909
10062
|
getQueryFlag('undo-timing') == 'on';
|
|
@@ -10835,10 +10988,17 @@
|
|
|
10835
10988
|
|
|
10836
10989
|
var openMenu;
|
|
10837
10990
|
var openMenuId;
|
|
10991
|
+
// Prefixes shown to the left of a menu item. The checkmark is what a copyable
|
|
10992
|
+
// item shows after it has been copied.
|
|
10993
|
+
var BULLET = '• ';
|
|
10994
|
+
var CHECKMARK = '✓ ';
|
|
10995
|
+
var COPIED_DELAY = 1200;
|
|
10838
10996
|
|
|
10839
10997
|
document.addEventListener('mousedown', function(e) {
|
|
10840
|
-
|
|
10841
|
-
|
|
10998
|
+
// Clicks inside the menu are handled by the menu itself: an item that runs a
|
|
10999
|
+
// command closes the menu, one that copies a value leaves it open.
|
|
11000
|
+
if (e.target.closest?.('.contextmenu')) {
|
|
11001
|
+
return;
|
|
10842
11002
|
}
|
|
10843
11003
|
closeOpenMenu();
|
|
10844
11004
|
});
|
|
@@ -10892,20 +11052,47 @@
|
|
|
10892
11052
|
}, 200);
|
|
10893
11053
|
}
|
|
10894
11054
|
|
|
10895
|
-
function
|
|
10896
|
-
var prefix = prefixArg === undefined ?
|
|
10897
|
-
|
|
11055
|
+
function createMenuItem(label, prefixArg) {
|
|
11056
|
+
var prefix = prefixArg === undefined ? BULLET : prefixArg;
|
|
11057
|
+
return El('div')
|
|
10898
11058
|
.appendTo(menu)
|
|
10899
11059
|
.addClass('contextmenu-item')
|
|
10900
11060
|
.html(prefix + label)
|
|
10901
11061
|
.show();
|
|
11062
|
+
}
|
|
10902
11063
|
|
|
11064
|
+
function addMenuItem(label, func, prefixArg) {
|
|
11065
|
+
var item = createMenuItem(label, prefixArg);
|
|
10903
11066
|
GUI.onClick(item, function(e) {
|
|
10904
11067
|
func();
|
|
10905
11068
|
closeOpenMenu();
|
|
10906
11069
|
});
|
|
10907
11070
|
}
|
|
10908
11071
|
|
|
11072
|
+
// An item that copies a value to the clipboard. Unlike a command item, it
|
|
11073
|
+
// leaves the menu open, so that several values from the same click point can
|
|
11074
|
+
// be copied in turn. The menu closing is therefore no longer the sign that the
|
|
11075
|
+
// copy happened, so the item's prefix briefly becomes a checkmark instead.
|
|
11076
|
+
// content: the string to copy, or a function returning it
|
|
11077
|
+
function addCopyItem(label, content, prefixArg) {
|
|
11078
|
+
var prefix = prefixArg === undefined ? BULLET : prefixArg;
|
|
11079
|
+
var item = createMenuItem(label, prefix);
|
|
11080
|
+
var timeout;
|
|
11081
|
+
|
|
11082
|
+
GUI.onClick(item, function() {
|
|
11083
|
+
var str = typeof content == 'function' ? content() : content;
|
|
11084
|
+
saveFileContentToClipboard(str).then(showCopied);
|
|
11085
|
+
});
|
|
11086
|
+
|
|
11087
|
+
function showCopied() {
|
|
11088
|
+
item.html(CHECKMARK + label);
|
|
11089
|
+
clearTimeout(timeout);
|
|
11090
|
+
timeout = setTimeout(function() {
|
|
11091
|
+
item.html(prefix + label);
|
|
11092
|
+
}, COPIED_DELAY);
|
|
11093
|
+
}
|
|
11094
|
+
}
|
|
11095
|
+
|
|
10909
11096
|
function addMenuLabel(label) {
|
|
10910
11097
|
El('div')
|
|
10911
11098
|
.appendTo(menu)
|
|
@@ -10948,7 +11135,7 @@
|
|
|
10948
11135
|
addMenuItem('delete point', e.deletePoint);
|
|
10949
11136
|
}
|
|
10950
11137
|
if (e.ids?.length) {
|
|
10951
|
-
|
|
11138
|
+
addCopyItem('copy as GeoJSON', getSelectionGeoJSON);
|
|
10952
11139
|
}
|
|
10953
11140
|
if (e.deleteFeature) {
|
|
10954
11141
|
addMenuItem(getDeleteLabel(), e.deleteFeature);
|
|
@@ -10963,6 +11150,9 @@
|
|
|
10963
11150
|
addMenuLabel('x, y');
|
|
10964
11151
|
addCoords(e.projected_coordinates);
|
|
10965
11152
|
}
|
|
11153
|
+
if (e.raster_pixel) {
|
|
11154
|
+
addRasterPixel(e.raster_pixel);
|
|
11155
|
+
}
|
|
10966
11156
|
}
|
|
10967
11157
|
|
|
10968
11158
|
if (menu.node().childNodes.length === 0) {
|
|
@@ -11009,16 +11199,32 @@
|
|
|
11009
11199
|
return 'delete ' + (lyr.geometry_type == 'point' ? 'point' : 'shape');
|
|
11010
11200
|
}
|
|
11011
11201
|
|
|
11202
|
+
// info: pixel data from internal.getRasterPixelAtMapXY()
|
|
11203
|
+
function addRasterPixel(info) {
|
|
11204
|
+
var values = info.values.map(function(val) {
|
|
11205
|
+
return internal.formatRasterSampleValue(val, info.isFloat);
|
|
11206
|
+
});
|
|
11207
|
+
var valueStr = values.join(', ');
|
|
11208
|
+
addMenuLabel(getBandLabel(info.values.length) +
|
|
11209
|
+
(info.valid ? '' : ' (no data)'));
|
|
11210
|
+
addCopyItem(valueStr, valueStr);
|
|
11211
|
+
if (info.color) {
|
|
11212
|
+
addColor(getColorString(info.color));
|
|
11213
|
+
}
|
|
11214
|
+
}
|
|
11215
|
+
|
|
11216
|
+
function addColor(color) {
|
|
11217
|
+
addMenuLabel('color');
|
|
11218
|
+
addCopyItem(color, color, getSwatchHtml(color));
|
|
11219
|
+
}
|
|
11220
|
+
|
|
11012
11221
|
function addCoords(p) {
|
|
11013
11222
|
var coordStr = p[0] + ',' + p[1];
|
|
11014
|
-
// var displayStr = '• ' + coordStr.replace(/-/g, '–').replace(',', ', ');
|
|
11015
11223
|
var displayStr = coordStr.replace(/-/g, '–').replace(',', ', ');
|
|
11016
|
-
|
|
11017
|
-
saveFileContentToClipboard(coordStr);
|
|
11018
|
-
});
|
|
11224
|
+
addCopyItem(displayStr, coordStr);
|
|
11019
11225
|
}
|
|
11020
11226
|
|
|
11021
|
-
function
|
|
11227
|
+
function getSelectionGeoJSON() {
|
|
11022
11228
|
var opts = {
|
|
11023
11229
|
no_replace: true,
|
|
11024
11230
|
ids: e.ids,
|
|
@@ -11032,13 +11238,32 @@
|
|
|
11032
11238
|
layer.geometry_type = 'polyline';
|
|
11033
11239
|
}
|
|
11034
11240
|
var features = internal.exportLayerAsGeoJSON(layer, dataset, {rfc7946: true, prettify: true}, true, 'string');
|
|
11035
|
-
|
|
11036
|
-
saveFileContentToClipboard(str);
|
|
11241
|
+
return internal.geojson.formatCollection({"type": "FeatureCollection"}, features);
|
|
11037
11242
|
}
|
|
11038
11243
|
};
|
|
11039
11244
|
}
|
|
11040
11245
|
|
|
11041
11246
|
|
|
11247
|
+
// rgba: [r, g, b, a] channels in the 0-255 range
|
|
11248
|
+
function getColorString(rgba) {
|
|
11249
|
+
return internal.formatColor({
|
|
11250
|
+
r: rgba[0], g: rgba[1], b: rgba[2], a: rgba[3] / 255
|
|
11251
|
+
});
|
|
11252
|
+
}
|
|
11253
|
+
|
|
11254
|
+
function getBandLabel(bands) {
|
|
11255
|
+
if (bands == 1) return 'band value';
|
|
11256
|
+
if (bands == 3) return 'red, green, blue';
|
|
11257
|
+
if (bands == 4) return 'red, green, blue, alpha';
|
|
11258
|
+
return 'band values';
|
|
11259
|
+
}
|
|
11260
|
+
|
|
11261
|
+
// A color tile, used in place of the bullet that other menu items get.
|
|
11262
|
+
function getSwatchHtml(color) {
|
|
11263
|
+
return '<span class="contextmenu-swatch" style="background-color: ' +
|
|
11264
|
+
color + '"></span>';
|
|
11265
|
+
}
|
|
11266
|
+
|
|
11042
11267
|
function layerHasOpenPaths(layer, arcs) {
|
|
11043
11268
|
var retn = false;
|
|
11044
11269
|
internal.editShapes(layer.shapes, function(part) {
|
|
@@ -12462,7 +12687,6 @@
|
|
|
12462
12687
|
if (!preserveOnModeChange) {
|
|
12463
12688
|
editSession.noteEdit();
|
|
12464
12689
|
}
|
|
12465
|
-
trimHistory(opts);
|
|
12466
12690
|
fireHistoryChange();
|
|
12467
12691
|
}
|
|
12468
12692
|
|
|
@@ -12505,14 +12729,6 @@
|
|
|
12505
12729
|
fireHistoryChange();
|
|
12506
12730
|
}
|
|
12507
12731
|
|
|
12508
|
-
function trimHistory(opts) {
|
|
12509
|
-
var max = opts && opts.maxStates;
|
|
12510
|
-
var overflow;
|
|
12511
|
-
if (!(max > 0) || history.length <= max) return;
|
|
12512
|
-
overflow = history.length - max;
|
|
12513
|
-
disposeHistoryItems(history.splice(0, overflow));
|
|
12514
|
-
}
|
|
12515
|
-
|
|
12516
12732
|
function fireHistoryChange() {
|
|
12517
12733
|
gui.dispatchEvent('history_change', {
|
|
12518
12734
|
canUndo: history.length - offset > 0,
|
|
@@ -12613,8 +12829,7 @@
|
|
|
12613
12829
|
if (!wasChanged) return;
|
|
12614
12830
|
getStoredUndoHistory(gui).addTransaction(finishedTx, {
|
|
12615
12831
|
flags: {select: true},
|
|
12616
|
-
entryPrefix: 'edit-session'
|
|
12617
|
-
maxStates: getEditSessionUndoHistoryLimit()
|
|
12832
|
+
entryPrefix: 'edit-session'
|
|
12618
12833
|
}).catch(function(e) {
|
|
12619
12834
|
console.error(e);
|
|
12620
12835
|
});
|
|
@@ -12661,11 +12876,6 @@
|
|
|
12661
12876
|
return internal.UndoTransaction && (internal.UndoTransaction.UndoTransaction || internal.UndoTransaction);
|
|
12662
12877
|
}
|
|
12663
12878
|
|
|
12664
|
-
function getEditSessionUndoHistoryLimit() {
|
|
12665
|
-
var opt = gui.options && gui.options.undoHistoryLimit;
|
|
12666
|
-
return opt > 0 ? opt : 10;
|
|
12667
|
-
}
|
|
12668
|
-
|
|
12669
12879
|
}
|
|
12670
12880
|
|
|
12671
12881
|
function SidebarButtons(gui) {
|
|
@@ -17332,6 +17542,11 @@
|
|
|
17332
17542
|
// data coordinates
|
|
17333
17543
|
eventData.projected_coordinates = gui.map.pixelCoordsToProjectedCoords(evt.x, evt.y);
|
|
17334
17544
|
eventData.lonlat_coordinates = gui.map.pixelCoordsToLngLatCoords(evt.x, evt.y);
|
|
17545
|
+
if (type == 'contextmenu') {
|
|
17546
|
+
// Only the context menu reads pixel values, and sampling one can cost a
|
|
17547
|
+
// scan of the grid, so it is not done on every hover event.
|
|
17548
|
+
eventData.raster_pixel = gui.map.pixelCoordsToRasterPixel(evt.x, evt.y);
|
|
17549
|
+
}
|
|
17335
17550
|
eventData.originalEvent = evt;
|
|
17336
17551
|
eventData.overMap = isOverMap(evt);
|
|
17337
17552
|
utils$1.defaults(eventData, evt.data);
|
|
@@ -20782,10 +20997,6 @@
|
|
|
20782
20997
|
}, 0);
|
|
20783
20998
|
}
|
|
20784
20999
|
|
|
20785
|
-
function invalidateRasterViewportPreview(layer) {
|
|
20786
|
-
cache$1.delete(layer);
|
|
20787
|
-
}
|
|
20788
|
-
|
|
20789
21000
|
function getRasterViewportPreviewParams(layer, ext) {
|
|
20790
21001
|
var raster = layer && layer.raster;
|
|
20791
21002
|
var grid = internal.getRasterGrid(raster);
|
|
@@ -20795,7 +21006,7 @@
|
|
|
20795
21006
|
var preview = internal.getRasterPreview(raster);
|
|
20796
21007
|
var recipe, pixelRatio, t, p1, p2, displayWidth, displayHeight, crop, width, height, scale, key, needed;
|
|
20797
21008
|
if (!grid || !grid.samples || !visibleBbox || !grid.bbox) return null;
|
|
20798
|
-
if (
|
|
21009
|
+
if (internal.rasterGridIsRotated(grid)) return null;
|
|
20799
21010
|
crop = getRasterSourceWindow(grid, visibleBbox);
|
|
20800
21011
|
if (!crop) return null;
|
|
20801
21012
|
visibleBbox = crop.bbox;
|
|
@@ -20901,11 +21112,6 @@
|
|
|
20901
21112
|
return Math.round(ms * 10) / 10 + 'ms';
|
|
20902
21113
|
}
|
|
20903
21114
|
|
|
20904
|
-
function supportsNorthUpRaster(grid) {
|
|
20905
|
-
var t = grid.transform;
|
|
20906
|
-
return !t || t[1] === 0 && t[3] === 0;
|
|
20907
|
-
}
|
|
20908
|
-
|
|
20909
21115
|
function getRasterSourcePixelSize(grid, bbox) {
|
|
20910
21116
|
var crop = getRasterSourceWindow(grid, bbox);
|
|
20911
21117
|
return crop ? {width: crop.width, height: crop.height} : {width: 0, height: 0};
|
|
@@ -23521,6 +23727,17 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
23521
23727
|
return p1 && p2 ? formatCoordsForDisplay(p1, p2) : null;
|
|
23522
23728
|
};
|
|
23523
23729
|
|
|
23730
|
+
// Returns band values and display color of the raster pixel under a screen
|
|
23731
|
+
// location, or null if the active layer is not a raster or the point misses it.
|
|
23732
|
+
this.pixelCoordsToRasterPixel = function(x, y) {
|
|
23733
|
+
var p;
|
|
23734
|
+
if (!_activeLyr || !internal.layerHasRaster(_activeLyr)) return null;
|
|
23735
|
+
// The grid is georeferenced in the layer's own CRS, so a display point has
|
|
23736
|
+
// to be translated back when the layer is being reprojected for display.
|
|
23737
|
+
p = translateDisplayPoint(_activeLyr, _ext.pixCoordsToMapCoords(x, y));
|
|
23738
|
+
return p ? internal.getRasterPixelAtMapXY(_activeLyr.raster, p[0], p[1]) : null;
|
|
23739
|
+
};
|
|
23740
|
+
|
|
23524
23741
|
this.getDisplayCRS = function() {
|
|
23525
23742
|
if (!_activeLyr) {
|
|
23526
23743
|
return _dynamicCRS || internal.parseCrsString('wgs84');
|