mapshaper 0.7.15 → 0.7.16

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/www/mapshaper.js CHANGED
@@ -78,7 +78,7 @@
78
78
  return obj === Object(obj); // via underscore
79
79
  }
80
80
 
81
- function clamp(val, min, max) {
81
+ function clamp$1(val, min, max) {
82
82
  return val < min ? min : (val > max ? max : val);
83
83
  }
84
84
 
@@ -751,7 +751,7 @@
751
751
  function findValueByRank(arr, rank) {
752
752
  if (!arr.length || rank < 1 || rank > arr.length) error$1("[findValueByRank()] invalid input");
753
753
 
754
- rank = clamp(rank | 0, 1, arr.length);
754
+ rank = clamp$1(rank | 0, 1, arr.length);
755
755
  var k = rank - 1, // conv. rank to array index
756
756
  n = arr.length,
757
757
  l = 0,
@@ -1125,7 +1125,7 @@
1125
1125
  // self-import and the resulting Rollup circular-dependency warning.
1126
1126
  var utils = {
1127
1127
  addThousandsSep, addslashes, arrayToIndex,
1128
- clamp, cleanNumericString, contains, copyElements, countValues, createBuffer,
1128
+ clamp: clamp$1, cleanNumericString, contains, copyElements, countValues, createBuffer,
1129
1129
  defaults, difference,
1130
1130
  endsWith, every, expandoBuffer, extend: extend$1, extendBuffer,
1131
1131
  find: find$1, findMedian, findQuantile, findRankByValue, findStringPrefix,
@@ -1298,7 +1298,7 @@
1298
1298
  }
1299
1299
  }
1300
1300
 
1301
- function timeEnd(slug) {
1301
+ function timeEnd$1(slug) {
1302
1302
  if (useDebug()) {
1303
1303
  console.timeEnd(slug);
1304
1304
  }
@@ -1424,7 +1424,7 @@
1424
1424
  setLoggingFunctions: setLoggingFunctions,
1425
1425
  stop: stop$1,
1426
1426
  time: time,
1427
- timeEnd: timeEnd,
1427
+ timeEnd: timeEnd$1,
1428
1428
  truncateString: truncateString,
1429
1429
  useDebug: useDebug,
1430
1430
  useVerbose: useVerbose,
@@ -4776,7 +4776,18 @@
4776
4776
  pathIsRectangle: pathIsRectangle
4777
4777
  });
4778
4778
 
4779
+ function runningInBrowser() {
4780
+ return typeof window !== 'undefined' && typeof window.document !== 'undefined';
4781
+ }
4782
+
4783
+ var Env = /*#__PURE__*/Object.freeze({
4784
+ __proto__: null,
4785
+ runningInBrowser: runningInBrowser
4786
+ });
4787
+
4779
4788
  var DEFAULT_MAX_PREVIEW_PIXELS = 4e6;
4789
+ var MAX_EXACT_AVERAGE_PIXELS = 256;
4790
+ var MAX_APPROX_AVERAGE_STEPS = 16;
4780
4791
 
4781
4792
  function getRasterGrid(raster) {
4782
4793
  return raster && (raster.grid || raster);
@@ -4922,6 +4933,13 @@
4922
4933
  });
4923
4934
  }
4924
4935
 
4936
+ function renderRasterExportPreview(raster, bbox, width, height, opts) {
4937
+ var grid = getRasterGrid(raster);
4938
+ var recipe = getRasterViewRecipe(grid, raster.view && raster.view.recipe, opts);
4939
+ var stats = getRasterViewScalingStats(raster, recipe);
4940
+ return renderRasterGridPreview(grid, recipe, width, height, stats, bbox, getRasterExportResamplingMethod(grid, bbox, width, height));
4941
+ }
4942
+
4925
4943
  function getRasterScalingStats(grid, recipe) {
4926
4944
  recipe = getRasterViewRecipe(grid, recipe);
4927
4945
  return getScalingStats(grid.samples, grid.bands, grid.nodata, recipe);
@@ -4946,7 +4964,7 @@
4946
4964
  return stats;
4947
4965
  }
4948
4966
 
4949
- function renderRasterGridPreview(grid, recipe, width, height, statsArg, sourceBbox) {
4967
+ function renderRasterGridPreview(grid, recipe, width, height, statsArg, sourceBbox, resamplingMethod) {
4950
4968
  var samples = grid.samples;
4951
4969
  var bands = grid.bands;
4952
4970
  var noData = grid.nodata;
@@ -4954,23 +4972,29 @@
4954
4972
  var stats = statsArg || getScalingStats(samples, bands, noData, recipe);
4955
4973
  var sourceRange = recipe.scaling == 'none' ? getPixelTypeRange(grid.pixelType) : null;
4956
4974
  var displayRange = getDisplayRange(recipe.scaleRange);
4957
- var src, dest, val, isNoData, j;
4975
+ var src, dest, val, isNoData, j, sample;
4958
4976
  for (var y = 0; y < height; y++) {
4959
4977
  for (var x = 0; x < width; x++) {
4960
- src = getPreviewSourceOffset(grid, sourceBbox, x, y, width, height, bands);
4961
4978
  dest = (y * width + x) * 4;
4962
- isNoData = noData !== null && noData !== undefined && allSamplesAreNoData(samples, src, bands, noData);
4979
+ if (resamplingMethod) {
4980
+ sample = getResampledRasterSample(grid, sourceBbox, x, y, width, height, resamplingMethod);
4981
+ isNoData = !sample.valid;
4982
+ } else {
4983
+ src = getPreviewSourceOffset(grid, sourceBbox, x, y, width, height, bands);
4984
+ sample = null;
4985
+ isNoData = noData !== null && noData !== undefined && allSamplesAreNoData(samples, src, bands, noData);
4986
+ }
4963
4987
  if (bands == 1) {
4964
- val = scaleSample(samples[src], stats && stats[0], sourceRange, displayRange);
4988
+ val = scaleSample(sample ? sample.values[0] : samples[src], stats && stats[0], sourceRange, displayRange);
4965
4989
  pixels[dest] = val;
4966
4990
  pixels[dest + 1] = val;
4967
4991
  pixels[dest + 2] = val;
4968
4992
  pixels[dest + 3] = isNoData ? 0 : 255;
4969
4993
  } else {
4970
4994
  for (j = 0; j < 3; j++) {
4971
- pixels[dest + j] = scaleSample(samples[src + j], stats && stats[j], sourceRange, displayRange);
4995
+ pixels[dest + j] = scaleSample(sample ? sample.values[j] : samples[src + j], stats && stats[j], sourceRange, displayRange);
4972
4996
  }
4973
- pixels[dest + 3] = isNoData ? 0 : bands >= 4 ? scaleSample(samples[src + 3], stats && stats[3], sourceRange, [0, 255]) : 255;
4997
+ pixels[dest + 3] = isNoData ? 0 : bands >= 4 ? scaleSample(sample ? sample.values[3] : samples[src + 3], stats && stats[3], sourceRange, [0, 255]) : 255;
4974
4998
  }
4975
4999
  }
4976
5000
  }
@@ -4984,6 +5008,156 @@
4984
5008
  };
4985
5009
  }
4986
5010
 
5011
+ function getRasterExportResamplingMethod(grid, bbox, width, height) {
5012
+ var srcSize = getRasterSourcePixelSize(grid, bbox);
5013
+ return width < srcSize.width || height < srcSize.height ? 'average' : 'bilinear';
5014
+ }
5015
+
5016
+ function getRasterSourcePixelSize(grid, bbox) {
5017
+ var rb = grid.bbox;
5018
+ return {
5019
+ width: Math.abs((bbox[2] - bbox[0]) / (rb[2] - rb[0]) * grid.width),
5020
+ height: Math.abs((bbox[3] - bbox[1]) / (rb[3] - rb[1]) * grid.height)
5021
+ };
5022
+ }
5023
+
5024
+ function getResampledRasterSample(grid, bbox, x, y, width, height, method) {
5025
+ return method == 'average' ?
5026
+ getAverageRasterSample(grid, bbox, x, y, width, height) :
5027
+ getBilinearRasterSample(grid, bbox, x, y, width, height);
5028
+ }
5029
+
5030
+ function getAverageRasterSample(grid, bbox, x, y, width, height) {
5031
+ var bounds = getRasterSourcePixelBounds(grid, bbox, x, y, width, height);
5032
+ if (getSourcePixelBoundsArea(bounds) > MAX_EXACT_AVERAGE_PIXELS) {
5033
+ return getApproxAverageRasterSample(grid, bounds);
5034
+ }
5035
+ var samples = grid.samples;
5036
+ var bands = grid.bands;
5037
+ var values = new Array(bands).fill(0);
5038
+ var total = 0;
5039
+ var src, weight;
5040
+ var x0 = Math.max(0, Math.floor(bounds.x0));
5041
+ var x1 = Math.min(grid.width, Math.ceil(bounds.x1));
5042
+ var y0 = Math.max(0, Math.floor(bounds.y0));
5043
+ var y1 = Math.min(grid.height, Math.ceil(bounds.y1));
5044
+ for (var sy = y0; sy < y1; sy++) {
5045
+ for (var sx = x0; sx < x1; sx++) {
5046
+ weight = getIntervalOverlap(bounds.x0, bounds.x1, sx, sx + 1) *
5047
+ getIntervalOverlap(bounds.y0, bounds.y1, sy, sy + 1);
5048
+ if (weight <= 0) continue;
5049
+ src = (sy * grid.width + sx) * bands;
5050
+ if (sampleIsNoData(samples, src, bands, grid.nodata)) continue;
5051
+ for (var band = 0; band < bands; band++) {
5052
+ values[band] += samples[src + band] * weight;
5053
+ }
5054
+ total += weight;
5055
+ }
5056
+ }
5057
+ if (total <= 0) return {valid: false, values: values};
5058
+ for (var i = 0; i < bands; i++) values[i] /= total;
5059
+ return {valid: true, values: values};
5060
+ }
5061
+
5062
+ function getApproxAverageRasterSample(grid, bounds) {
5063
+ var samples = grid.samples;
5064
+ var bands = grid.bands;
5065
+ var values = new Array(bands).fill(0);
5066
+ var xSteps = Math.min(MAX_APPROX_AVERAGE_STEPS, Math.max(1, Math.ceil(Math.abs(bounds.x1 - bounds.x0))));
5067
+ var ySteps = Math.min(MAX_APPROX_AVERAGE_STEPS, Math.max(1, Math.ceil(Math.abs(bounds.y1 - bounds.y0))));
5068
+ var count = 0;
5069
+ var sx, sy, src;
5070
+ for (var y = 0; y < ySteps; y++) {
5071
+ for (var x = 0; x < xSteps; x++) {
5072
+ sx = Math.max(0, Math.min(grid.width - 1, Math.floor(bounds.x0 + (x + 0.5) / xSteps * (bounds.x1 - bounds.x0))));
5073
+ sy = Math.max(0, Math.min(grid.height - 1, Math.floor(bounds.y0 + (y + 0.5) / ySteps * (bounds.y1 - bounds.y0))));
5074
+ src = (sy * grid.width + sx) * bands;
5075
+ if (sampleIsNoData(samples, src, bands, grid.nodata)) continue;
5076
+ for (var band = 0; band < bands; band++) {
5077
+ values[band] += samples[src + band];
5078
+ }
5079
+ count++;
5080
+ }
5081
+ }
5082
+ if (count === 0) return {valid: false, values: values};
5083
+ for (var i = 0; i < bands; i++) values[i] /= count;
5084
+ return {valid: true, values: values};
5085
+ }
5086
+
5087
+ function getBilinearRasterSample(grid, bbox, x, y, width, height) {
5088
+ var p = getRasterSourcePixelCenter(grid, bbox, x, y, width, height);
5089
+ var x0 = Math.max(0, Math.min(grid.width - 1, Math.floor(p.x)));
5090
+ var y0 = Math.max(0, Math.min(grid.height - 1, Math.floor(p.y)));
5091
+ var x1 = Math.max(0, Math.min(grid.width - 1, x0 + 1));
5092
+ var y1 = Math.max(0, Math.min(grid.height - 1, y0 + 1));
5093
+ var tx = Math.max(0, Math.min(1, p.x - x0));
5094
+ var ty = Math.max(0, Math.min(1, p.y - y0));
5095
+ var samples = grid.samples;
5096
+ var bands = grid.bands;
5097
+ var values = new Array(bands).fill(0);
5098
+ var offsets = [
5099
+ (y0 * grid.width + x0) * bands,
5100
+ (y0 * grid.width + x1) * bands,
5101
+ (y1 * grid.width + x0) * bands,
5102
+ (y1 * grid.width + x1) * bands
5103
+ ];
5104
+ var weights = [
5105
+ (1 - tx) * (1 - ty),
5106
+ tx * (1 - ty),
5107
+ (1 - tx) * ty,
5108
+ tx * ty
5109
+ ];
5110
+ var total = 0;
5111
+ offsets.forEach(function(src, i) {
5112
+ if (weights[i] <= 0 || sampleIsNoData(samples, src, bands, grid.nodata)) return;
5113
+ for (var band = 0; band < bands; band++) {
5114
+ values[band] += samples[src + band] * weights[i];
5115
+ }
5116
+ total += weights[i];
5117
+ });
5118
+ if (total <= 0) return {valid: false, values: values};
5119
+ for (var j = 0; j < bands; j++) values[j] /= total;
5120
+ return {valid: true, values: values};
5121
+ }
5122
+
5123
+ function getRasterSourcePixelBounds(grid, bbox, x, y, width, height) {
5124
+ return {
5125
+ x0: mapXToRasterPixel(grid, bbox[0] + x / width * (bbox[2] - bbox[0])),
5126
+ x1: mapXToRasterPixel(grid, bbox[0] + (x + 1) / width * (bbox[2] - bbox[0])),
5127
+ y0: mapYToRasterPixel(grid, bbox[3] - y / height * (bbox[3] - bbox[1])),
5128
+ y1: mapYToRasterPixel(grid, bbox[3] - (y + 1) / height * (bbox[3] - bbox[1]))
5129
+ };
5130
+ }
5131
+
5132
+ function getRasterSourcePixelCenter(grid, bbox, x, y, width, height) {
5133
+ var mapX = bbox[0] + (x + 0.5) / width * (bbox[2] - bbox[0]);
5134
+ var mapY = bbox[3] - (y + 0.5) / height * (bbox[3] - bbox[1]);
5135
+ return {
5136
+ x: mapXToRasterPixel(grid, mapX) - 0.5,
5137
+ y: mapYToRasterPixel(grid, mapY) - 0.5
5138
+ };
5139
+ }
5140
+
5141
+ function mapXToRasterPixel(grid, x) {
5142
+ return (x - grid.bbox[0]) / (grid.bbox[2] - grid.bbox[0]) * grid.width;
5143
+ }
5144
+
5145
+ function mapYToRasterPixel(grid, y) {
5146
+ return (grid.bbox[3] - y) / (grid.bbox[3] - grid.bbox[1]) * grid.height;
5147
+ }
5148
+
5149
+ function getIntervalOverlap(a0, a1, b0, b1) {
5150
+ return Math.max(0, Math.min(Math.max(a0, a1), b1) - Math.max(Math.min(a0, a1), b0));
5151
+ }
5152
+
5153
+ function getSourcePixelBoundsArea(bounds) {
5154
+ return Math.abs((bounds.x1 - bounds.x0) * (bounds.y1 - bounds.y0));
5155
+ }
5156
+
5157
+ function sampleIsNoData(samples, offset, bands, noData) {
5158
+ return noData !== null && noData !== undefined && allSamplesAreNoData(samples, offset, bands, noData);
5159
+ }
5160
+
4987
5161
  function renderRawEightBitPreview(grid, width, height, sourceBbox) {
4988
5162
  var samples = grid.samples;
4989
5163
  var bands = grid.bands;
@@ -5048,7 +5222,12 @@
5048
5222
  noteLayerWillChange(lyr, {operation: 'clipRasterToBBox', unit: 'raster'});
5049
5223
  raster.grid = cropRasterGrid(grid, crop, clipBbox);
5050
5224
  raster.view = raster.view || {};
5051
- raster.view.preview = createRasterPreview(raster, opts || {});
5225
+ delete raster.view.scalingStats;
5226
+ if (runningInBrowser()) {
5227
+ raster.view.preview = createRasterPreview(raster, opts || {});
5228
+ } else {
5229
+ delete raster.view.preview;
5230
+ }
5052
5231
  clearLegacyRasterFields(raster);
5053
5232
  markLayerChanged(lyr, {operation: 'clipRasterToBBox', unit: 'raster'});
5054
5233
  return true;
@@ -5393,6 +5572,7 @@
5393
5572
  getRasterViewScalingStats: getRasterViewScalingStats,
5394
5573
  getRasterWidth: getRasterWidth,
5395
5574
  intersectBboxes: intersectBboxes,
5575
+ renderRasterExportPreview: renderRasterExportPreview,
5396
5576
  renderRasterPreview: renderRasterPreview,
5397
5577
  renderRasterViewportPreview: renderRasterViewportPreview,
5398
5578
  requireRasterLayer: requireRasterLayer
@@ -10775,15 +10955,6 @@
10775
10955
  return files;
10776
10956
  }
10777
10957
 
10778
- function runningInBrowser() {
10779
- return typeof window !== 'undefined' && typeof window.document !== 'undefined';
10780
- }
10781
-
10782
- var Env = /*#__PURE__*/Object.freeze({
10783
- __proto__: null,
10784
- runningInBrowser: runningInBrowser
10785
- });
10786
-
10787
10958
  // input: A file path or a buffer
10788
10959
  function unzipSync(input) {
10789
10960
  if (input instanceof ArrayBuffer) {
@@ -19819,15 +19990,15 @@ ${svg}
19819
19990
 
19820
19991
  function exportRasterLayerForSVG(lyr, frame, opts) {
19821
19992
  var raster = lyr.raster;
19822
- var clipped = clipRasterPreviewToFrame(raster, frame);
19823
- var bbox = transformRasterBboxForSVG(clipped.bbox, frame);
19993
+ var rendered = renderRasterForSVG(raster, frame, opts);
19994
+ var bbox = transformRasterBboxForSVG(rendered.bbox, frame);
19824
19995
  var href;
19825
19996
  var layerObj = getEmptyLayerForSVG(lyr, opts);
19826
- if (!clipped.preview.width || !clipped.preview.height) {
19997
+ if (!rendered.preview.width || !rendered.preview.height) {
19827
19998
  layerObj.children = [];
19828
19999
  return layerObj;
19829
20000
  }
19830
- href = encodeRasterPreview(clipped.preview, opts);
20001
+ href = encodeRasterPreview(rendered.preview, opts);
19831
20002
  layerObj.children = [{
19832
20003
  tag: 'image',
19833
20004
  properties: {
@@ -19842,50 +20013,47 @@ ${svg}
19842
20013
  return layerObj;
19843
20014
  }
19844
20015
 
19845
- function clipRasterPreviewToFrame(raster, frame) {
20016
+ function renderRasterForSVG(raster, frame, opts) {
19846
20017
  var rasterBbox = getRasterBBox(raster);
19847
- var preview = getRasterPreview(raster);
19848
20018
  var rasterBounds = new Bounds(rasterBbox);
19849
20019
  var clipBounds = new Bounds(frame.bbox);
19850
20020
  var bbox = intersectBboxes(rasterBounds.toArray(), clipBounds.toArray()) || [0, 0, 0, 0];
19851
- var crop = getRasterPreviewCrop(rasterBbox, preview, bbox);
20021
+ var svgBbox = transformRasterBboxForSVG(bbox, frame);
20022
+ var size = getSvgRasterOutputSize(raster, bbox, svgBbox, opts);
19852
20023
  return {
19853
20024
  bbox: bbox,
19854
- preview: cropRasterPreview(preview, crop)
20025
+ preview: size.width > 0 && size.height > 0 ?
20026
+ renderRasterExportPreview(raster, bbox, size.width, size.height, opts) :
20027
+ {width: 0, height: 0, pixels: new Uint8ClampedArray(0)}
19855
20028
  };
19856
20029
  }
19857
20030
 
19858
- function getRasterPreviewCrop(rasterBbox, preview, bbox) {
19859
- var rb = rasterBbox;
19860
- var p = preview;
19861
- var x0 = Math.max(0, Math.floor((bbox[0] - rb[0]) / (rb[2] - rb[0]) * p.width));
19862
- var x1 = Math.min(p.width, Math.ceil((bbox[2] - rb[0]) / (rb[2] - rb[0]) * p.width));
19863
- var y0 = Math.max(0, Math.floor((rb[3] - bbox[3]) / (rb[3] - rb[1]) * p.height));
19864
- var y1 = Math.min(p.height, Math.ceil((rb[3] - bbox[1]) / (rb[3] - rb[1]) * p.height));
20031
+ function getSvgRasterOutputSize(raster, bbox, svgBbox, opts) {
20032
+ var res = getSvgRasterResolution(opts);
20033
+ var sourceSize = getRasterSourceSize(raster, bbox);
20034
+ var svgWidth = Math.abs(svgBbox[2] - svgBbox[0]);
20035
+ var svgHeight = Math.abs(svgBbox[3] - svgBbox[1]);
20036
+ var width = svgWidth > 0 ? Math.max(1, Math.round(svgWidth * res)) : 0;
20037
+ var height = svgHeight > 0 ? Math.max(1, Math.round(svgHeight * res)) : 0;
19865
20038
  return {
19866
- x: x0,
19867
- y: y0,
19868
- width: Math.max(0, x1 - x0),
19869
- height: Math.max(0, y1 - y0)
20039
+ width: Math.max(0, Math.min(Math.max(1, Math.ceil(sourceSize.width)), width)),
20040
+ height: Math.max(0, Math.min(Math.max(1, Math.ceil(sourceSize.height)), height))
19870
20041
  };
19871
20042
  }
19872
20043
 
19873
- function cropRasterPreview(preview, crop) {
19874
- if (crop.x === 0 && crop.y === 0 && crop.width == preview.width && crop.height == preview.height) {
19875
- return preview;
19876
- }
19877
- var pixels = new Uint8ClampedArray(crop.width * crop.height * 4);
19878
- var src, dest, rowBytes = crop.width * 4;
19879
- for (var y = 0; y < crop.height; y++) {
19880
- src = ((crop.y + y) * preview.width + crop.x) * 4;
19881
- dest = y * rowBytes;
19882
- pixels.set(preview.pixels.subarray(src, src + rowBytes), dest);
19883
- }
19884
- return Object.assign({}, preview, {
19885
- width: crop.width,
19886
- height: crop.height,
19887
- pixels: pixels
19888
- });
20044
+ function getSvgRasterResolution(opts) {
20045
+ var res = opts.raster_res || opts.rasterRes || 1;
20046
+ if (res > 0 === false) stop$1('Expected raster-res= to be a positive number');
20047
+ return res;
20048
+ }
20049
+
20050
+ function getRasterSourceSize(raster, bbox) {
20051
+ var grid = raster.grid;
20052
+ var rb = getRasterBBox(raster);
20053
+ return {
20054
+ width: Math.abs((bbox[2] - bbox[0]) / (rb[2] - rb[0]) * grid.width),
20055
+ height: Math.abs((bbox[3] - bbox[1]) / (rb[3] - rb[1]) * grid.height)
20056
+ };
19889
20057
  }
19890
20058
 
19891
20059
  function transformRasterBboxForSVG(bbox, frame) {
@@ -23424,20 +23592,11 @@ ${svg}
23424
23592
  }
23425
23593
  if (raster.view) {
23426
23594
  copy.view = Object.assign({}, raster.view);
23427
- if (raster.view.preview) {
23428
- copy.view.preview = Object.assign({}, raster.view.preview);
23429
- delete copy.view.preview.canvas;
23430
- if (raster.view.preview.pixels) {
23431
- copy.view.preview.pixels = typedArrayToBuffer(raster.view.preview.pixels);
23432
- }
23433
- }
23595
+ delete copy.view.preview;
23596
+ delete copy.view.scalingStats;
23434
23597
  }
23435
23598
  if (raster.preview) {
23436
- copy.preview = Object.assign({}, raster.preview);
23437
- delete copy.preview.canvas;
23438
- if (raster.preview.pixels) {
23439
- copy.preview.pixels = typedArrayToBuffer(raster.preview.pixels);
23440
- }
23599
+ delete copy.preview;
23441
23600
  }
23442
23601
  if (raster.pixels) {
23443
23602
  copy.pixels = typedArrayToBuffer(raster.pixels);
@@ -28669,6 +28828,10 @@ ${svg}
28669
28828
  error('precision= option should be a positive number');
28670
28829
  }
28671
28830
 
28831
+ if (o.raster_type && o.raster_type != 'image' && o.raster_type != 'categorical') {
28832
+ error('Unsupported raster-type:', o.raster_type);
28833
+ }
28834
+
28672
28835
  if (o.encoding) {
28673
28836
  o.encoding = validateEncoding(o.encoding);
28674
28837
  }
@@ -28682,9 +28845,13 @@ ${svg}
28682
28845
  }
28683
28846
 
28684
28847
  function validateProjOpts(cmd) {
28848
+ var resampling = cmd.options.resampling;
28685
28849
  if (!(cmd.options.crs || cmd.options.match || cmd.options.init)) {
28686
28850
  stop$1('Missing projection data');
28687
28851
  }
28852
+ if (resampling && resampling != 'nearest' && resampling != 'bilinear') {
28853
+ stop$1('Unsupported resampling method:', resampling);
28854
+ }
28688
28855
  }
28689
28856
 
28690
28857
  function validateGridOpts(cmd) {
@@ -29964,6 +30131,9 @@ ${svg}
29964
30131
  .option('percentile-range', {
29965
30132
  describe: '[raster] input percentile range for percentile scaling, e.g. 2,98'
29966
30133
  })
30134
+ .option('raster-type', {
30135
+ describe: '[raster] image or categorical (default is image)'
30136
+ })
29967
30137
  .option('rendition', {
29968
30138
  describe: '[GeoTIFF] import a GeoTIFF rendition: full,overview-1,etc.'
29969
30139
  })
@@ -30185,6 +30355,10 @@ ${svg}
30185
30355
  describe: '[SVG] bounding box of SVG map in projected map units',
30186
30356
  type: 'bbox'
30187
30357
  })
30358
+ .option('raster-res', {
30359
+ describe: '[SVG] raster pixels per SVG pixel (default is 1)',
30360
+ type: 'number'
30361
+ })
30188
30362
  .option('fit-extent', {
30189
30363
  describe: '[SVG] layer to use for the map extent'
30190
30364
  })
@@ -31206,6 +31380,12 @@ ${svg}
31206
31380
  describe: 'clip to a lat-long bounding box before projecting',
31207
31381
  type: 'bbox'
31208
31382
  })
31383
+ .option('nodata-color', {
31384
+ describe: '[raster] color for uncovered pixels after reprojection'
31385
+ })
31386
+ .option('resampling', {
31387
+ describe: '[raster] nearest or bilinear (default is bilinear)'
31388
+ })
31209
31389
  .option('target', targetOpt)
31210
31390
  .validate(validateProjOpts);
31211
31391
 
@@ -35774,6 +35954,7 @@ ${svg}
35774
35954
  var sourceId = getSourceId(input);
35775
35955
  var raster = {
35776
35956
  sourceId: sourceId,
35957
+ interpretation: getRasterInterpretation$1(opts),
35777
35958
  grid: {
35778
35959
  width: width,
35779
35960
  height: height,
@@ -35798,13 +35979,19 @@ ${svg}
35798
35979
  }
35799
35980
  };
35800
35981
  raster.view.recipe = getRasterViewRecipe(raster.grid, raster.view.recipe, opts);
35801
- raster.view.preview = createRasterPreview(raster, opts);
35982
+ if (runningInBrowser()) {
35983
+ raster.view.preview = createRasterPreview(raster, opts);
35984
+ }
35802
35985
  return {
35803
35986
  raster: raster,
35804
35987
  source: getSourceInfo$1(input, sourceId, sourceImage)
35805
35988
  };
35806
35989
  }
35807
35990
 
35991
+ function getRasterInterpretation$1(opts) {
35992
+ return opts.raster_type || opts.rasterType || 'image';
35993
+ }
35994
+
35808
35995
  async function selectGeoTIFFImportImage(tiff, sourceImage, opts) {
35809
35996
  var maxPixels = getMaxImportPixels(opts);
35810
35997
  var renditions = await getGeoTIFFRenditions(tiff, sourceImage);
@@ -36187,6 +36374,7 @@ ${svg}
36187
36374
  bbox = getWorldFileBBox(transform, decoded.width, decoded.height);
36188
36375
  raster = {
36189
36376
  sourceId: sourceId,
36377
+ interpretation: getRasterInterpretation(opts),
36190
36378
  grid: {
36191
36379
  width: decoded.width,
36192
36380
  height: decoded.height,
@@ -36211,7 +36399,9 @@ ${svg}
36211
36399
  }
36212
36400
  };
36213
36401
  raster.view.recipe = getRasterViewRecipe(raster.grid, raster.view.recipe, opts);
36214
- raster.view.preview = createRasterPreview(raster, opts);
36402
+ if (runningInBrowser()) {
36403
+ raster.view.preview = createRasterPreview(raster, opts);
36404
+ }
36215
36405
  dataset = {
36216
36406
  info: {
36217
36407
  raster_sources: [getSourceInfo(imageInput, sourceId, imageType, input)]
@@ -36226,6 +36416,10 @@ ${svg}
36226
36416
  return dataset;
36227
36417
  }
36228
36418
 
36419
+ function getRasterInterpretation(opts) {
36420
+ return opts.raster_type || opts.rasterType || 'image';
36421
+ }
36422
+
36229
36423
  async function decodeImage(input, imageType) {
36230
36424
  if (runningInBrowser()) {
36231
36425
  return decodeImageInBrowser(input.content, imageType);
@@ -40821,7 +41015,7 @@ ${svg}
40821
41015
  if (!opts.debug_points) {
40822
41016
  dissolveBufferDataset2(dataset2, opts);
40823
41017
  }
40824
- timeEnd('buffer');
41018
+ timeEnd$1('buffer');
40825
41019
  return dataset2;
40826
41020
  }
40827
41021
 
@@ -50828,6 +51022,581 @@ ${svg}
50828
51022
  getUtmParams: getUtmParams
50829
51023
  });
50830
51024
 
51025
+ var DEFAULT_MESH_INTERVAL = 32;
51026
+ var DEFAULT_MAX_EDGE_FACTOR = 20;
51027
+
51028
+ function projectRasterGridForward(raster, srcCRS, destCRS, optsArg) {
51029
+ var opts = optsArg || {};
51030
+ var grid = getRasterGrid(raster);
51031
+ var interval = opts.raster_mesh_interval || opts.rasterMeshInterval || DEFAULT_MESH_INTERVAL;
51032
+ var transform = getProjTransform2(srcCRS, destCRS);
51033
+ var timing = opts.timing;
51034
+ var mesh, bbox, outSize, outGrid;
51035
+ validateRasterGridForProjection(grid);
51036
+ timeStart(timing, 'mesh');
51037
+ mesh = buildProjectedRasterMesh(grid, transform, interval);
51038
+ classifyProjectedMeshCells(mesh, opts);
51039
+ timeEnd(timing, 'mesh');
51040
+ bbox = opts.output_bbox || opts.outputBbox || getProjectedMeshBBox(mesh);
51041
+ if (!bbox) stop$1('Unable to project raster layer');
51042
+ outSize = getOutputGridSize(grid, bbox, opts);
51043
+ outGrid = createProjectedRasterGrid(grid, bbox, outSize.width, outSize.height, opts);
51044
+ timeStart(timing, 'rasterize');
51045
+ rasterizeProjectedMesh(grid, outGrid, mesh, getRasterProjectionSampleMethod(raster, opts));
51046
+ timeEnd(timing, 'rasterize');
51047
+ if (timing) {
51048
+ timing.outputWidth = outGrid.width;
51049
+ timing.outputHeight = outGrid.height;
51050
+ timing.meshVertices = mesh.vertices.length;
51051
+ timing.meshCells = mesh.cells.length;
51052
+ timing.meshSkippedCells = mesh.skippedCellCount;
51053
+ }
51054
+ return outGrid;
51055
+ }
51056
+
51057
+ function getRasterProjectionSampleMethod(raster, opts) {
51058
+ var method = opts.resampling || opts.sample_method || opts.sampleMethod || getDefaultRasterProjectionSampleMethod(raster);
51059
+ if (method != 'nearest' && method != 'bilinear') {
51060
+ stop$1('Unsupported resampling method:', method);
51061
+ }
51062
+ return method;
51063
+ }
51064
+
51065
+ function getDefaultRasterProjectionSampleMethod(raster) {
51066
+ return rasterAppearsCategorical(raster) ? 'nearest' : 'bilinear';
51067
+ }
51068
+
51069
+ function rasterAppearsCategorical(raster) {
51070
+ var grid = getRasterGrid(raster);
51071
+ var recipe = raster && raster.view && raster.view.recipe || {};
51072
+ var derivation = raster && raster.derivation || {};
51073
+ return raster && raster.interpretation == 'categorical' ||
51074
+ grid && grid.colorModel == 'palette' ||
51075
+ recipe.type == 'palette' ||
51076
+ recipe.type == 'categorical' ||
51077
+ derivation.type == 'palette' ||
51078
+ derivation.type == 'categorical';
51079
+ }
51080
+
51081
+ function getProjectedRasterGridBBox(raster, srcCRS, destCRS, optsArg) {
51082
+ var opts = optsArg || {};
51083
+ var grid = getRasterGrid(raster);
51084
+ var interval = opts.raster_mesh_interval || opts.rasterMeshInterval || DEFAULT_MESH_INTERVAL;
51085
+ var transform = getProjTransform2(srcCRS, destCRS);
51086
+ var mesh, bbox;
51087
+ validateRasterGridForProjection(grid);
51088
+ mesh = buildProjectedRasterMesh(grid, transform, interval);
51089
+ classifyProjectedMeshCells(mesh, opts);
51090
+ bbox = getProjectedMeshBBox(mesh);
51091
+ return bbox;
51092
+ }
51093
+
51094
+ function getProjectedRasterMeshBBox(mesh, optsArg) {
51095
+ classifyProjectedMeshCells(mesh, optsArg || {});
51096
+ return getProjectedMeshBBox(mesh);
51097
+ }
51098
+
51099
+ function buildProjectedRasterMesh(grid, transform, interval) {
51100
+ var xs = getMeshStops(grid.width, interval);
51101
+ var ys = getMeshStops(grid.height, interval);
51102
+ var vertices = [];
51103
+ for (var y = 0; y < ys.length; y++) {
51104
+ for (var x = 0; x < xs.length; x++) {
51105
+ vertices.push(projectRasterMeshVertex(grid, xs[x], ys[y], transform));
51106
+ }
51107
+ }
51108
+ return {
51109
+ xs: xs,
51110
+ ys: ys,
51111
+ vertices: vertices
51112
+ };
51113
+ }
51114
+
51115
+ function classifyProjectedMeshCells(mesh, opts) {
51116
+ var cols = mesh.xs.length;
51117
+ var cells = [];
51118
+ var lengths = [];
51119
+ var maxEdge, skipped;
51120
+ for (var y = 0; y < mesh.ys.length - 1; y++) {
51121
+ for (var x = 0; x < mesh.xs.length - 1; x++) {
51122
+ var cell = getMeshCell(mesh, cols, x, y);
51123
+ cell.maxEdge = getCellMaxEdge(cell);
51124
+ cells.push(cell);
51125
+ if (cell.valid) lengths.push(cell.maxEdge);
51126
+ }
51127
+ }
51128
+ maxEdge = getProjectedMeshMaxEdge(lengths, opts);
51129
+ skipped = 0;
51130
+ cells.forEach(function(cell) {
51131
+ if (!cell.valid || cell.maxEdge > maxEdge) {
51132
+ cell.valid = false;
51133
+ skipped++;
51134
+ }
51135
+ });
51136
+ if (opts.raster_component_filter || opts.rasterComponentFilter) {
51137
+ skipped += keepLargestValidCellComponent(cells, mesh.xs.length - 1, mesh.ys.length - 1);
51138
+ }
51139
+ mesh.cells = cells;
51140
+ mesh.maxEdge = maxEdge;
51141
+ mesh.skippedCellCount = skipped;
51142
+ }
51143
+
51144
+ function keepLargestValidCellComponent(cells, cols, rows) {
51145
+ var componentIds = new Int32Array(cells.length);
51146
+ var components = [];
51147
+ var componentId = 0;
51148
+ var largestId = -1;
51149
+ var largestSize = 0;
51150
+ var skipped = 0;
51151
+ componentIds.fill(-1);
51152
+ for (var i = 0; i < cells.length; i++) {
51153
+ if (!cells[i].valid || componentIds[i] != -1) continue;
51154
+ components[componentId] = floodFillCellComponent(cells, componentIds, cols, rows, i, componentId);
51155
+ if (components[componentId].size > largestSize) {
51156
+ largestSize = components[componentId].size;
51157
+ largestId = componentId;
51158
+ }
51159
+ componentId++;
51160
+ }
51161
+ if (largestId == -1) return 0;
51162
+ cells.forEach(function(cell, i) {
51163
+ if (cell.valid && !componentShouldBeKept(components[componentIds[i]], components[largestId])) {
51164
+ cell.valid = false;
51165
+ skipped++;
51166
+ }
51167
+ });
51168
+ return skipped;
51169
+ }
51170
+
51171
+ function floodFillCellComponent(cells, componentIds, cols, rows, start, componentId) {
51172
+ var stack = [start];
51173
+ var component = {
51174
+ size: 0,
51175
+ bbox: [Infinity, Infinity, -Infinity, -Infinity]
51176
+ };
51177
+ while (stack.length > 0) {
51178
+ var id = stack.pop();
51179
+ var x, y;
51180
+ if (!cells[id].valid || componentIds[id] != -1) continue;
51181
+ componentIds[id] = componentId;
51182
+ component.size++;
51183
+ expandComponentBBox(component, cells[id]);
51184
+ x = id % cols;
51185
+ y = Math.floor(id / cols);
51186
+ if (x > 0) stack.push(id - 1);
51187
+ if (x < cols - 1) stack.push(id + 1);
51188
+ if (y > 0) stack.push(id - cols);
51189
+ if (y < rows - 1) stack.push(id + cols);
51190
+ }
51191
+ return component;
51192
+ }
51193
+
51194
+ function componentShouldBeKept(component, mainComponent) {
51195
+ if (component == mainComponent) return true;
51196
+ // A valid antimeridian split can create multiple disconnected source-grid
51197
+ // components whose projected bboxes overlap. Remote components are more
51198
+ // likely to be projection discontinuities that would create spiky triangles.
51199
+ return bboxesOverlap(component.bbox, mainComponent.bbox);
51200
+ }
51201
+
51202
+ function expandComponentBBox(component, cell) {
51203
+ [cell.v00, cell.v10, cell.v01, cell.v11].forEach(function(p) {
51204
+ if (p.x < component.bbox[0]) component.bbox[0] = p.x;
51205
+ if (p.y < component.bbox[1]) component.bbox[1] = p.y;
51206
+ if (p.x > component.bbox[2]) component.bbox[2] = p.x;
51207
+ if (p.y > component.bbox[3]) component.bbox[3] = p.y;
51208
+ });
51209
+ }
51210
+
51211
+ function bboxesOverlap(a, b) {
51212
+ return a[0] <= b[2] && a[2] >= b[0] && a[1] <= b[3] && a[3] >= b[1];
51213
+ }
51214
+
51215
+ function getMeshCell(mesh, cols, x, y) {
51216
+ var v = y * cols + x;
51217
+ var v00 = mesh.vertices[v];
51218
+ var v10 = mesh.vertices[v + 1];
51219
+ var v01 = mesh.vertices[v + cols];
51220
+ var v11 = mesh.vertices[v + cols + 1];
51221
+ return {
51222
+ v00: v00,
51223
+ v10: v10,
51224
+ v01: v01,
51225
+ v11: v11,
51226
+ valid: vertexIsValid(v00) && vertexIsValid(v10) && vertexIsValid(v01) && vertexIsValid(v11)
51227
+ };
51228
+ }
51229
+
51230
+ function getCellMaxEdge(cell) {
51231
+ if (!cell.valid) return Infinity;
51232
+ return Math.max(
51233
+ getProjectedEdgeLength(cell.v00, cell.v10),
51234
+ getProjectedEdgeLength(cell.v10, cell.v11),
51235
+ getProjectedEdgeLength(cell.v11, cell.v01),
51236
+ getProjectedEdgeLength(cell.v01, cell.v00)
51237
+ );
51238
+ }
51239
+
51240
+ function getProjectedEdgeLength(a, b) {
51241
+ var dx = a.x - b.x;
51242
+ var dy = a.y - b.y;
51243
+ return Math.sqrt(dx * dx + dy * dy);
51244
+ }
51245
+
51246
+ function getProjectedMeshMaxEdge(lengths, opts) {
51247
+ var factor = opts.raster_max_edge_factor || opts.rasterMaxEdgeFactor || DEFAULT_MAX_EDGE_FACTOR;
51248
+ if (lengths.length === 0) return 0;
51249
+ lengths.sort(function(a, b) { return a - b; });
51250
+ return lengths[Math.floor(lengths.length / 2)] * factor;
51251
+ }
51252
+
51253
+ function validateRasterGridForProjection(grid) {
51254
+ var t = grid && grid.transform;
51255
+ if (!grid || !grid.samples || !grid.bbox) stop$1('Raster layer is missing required projection data');
51256
+ if (t && (t[1] !== 0 || t[3] !== 0)) {
51257
+ stop$1('Raster reprojection does not support rotated or skewed rasters');
51258
+ }
51259
+ }
51260
+
51261
+ function getMeshStops(size, interval) {
51262
+ var stops = [];
51263
+ var i;
51264
+ interval = Math.max(1, interval | 0);
51265
+ for (i = 0; i < size; i += interval) {
51266
+ stops.push(i);
51267
+ }
51268
+ if (stops[stops.length - 1] != size) stops.push(size);
51269
+ return stops;
51270
+ }
51271
+
51272
+ function projectRasterMeshVertex(grid, px, py, transform) {
51273
+ var xy = rasterPixelToMapXY(grid, px, py);
51274
+ var p = transform(xy[0], xy[1]);
51275
+ return {
51276
+ sx: px,
51277
+ sy: py,
51278
+ x: p && isFinite(p[0]) ? p[0] : NaN,
51279
+ y: p && isFinite(p[1]) ? p[1] : NaN
51280
+ };
51281
+ }
51282
+
51283
+ function rasterPixelToMapXY(grid, px, py) {
51284
+ var t = grid.transform;
51285
+ var bbox = grid.bbox;
51286
+ if (t) {
51287
+ return [
51288
+ t[0] * px + t[1] * py + t[2],
51289
+ t[3] * px + t[4] * py + t[5]
51290
+ ];
51291
+ }
51292
+ return [
51293
+ bbox[0] + px / grid.width * (bbox[2] - bbox[0]),
51294
+ bbox[3] - py / grid.height * (bbox[3] - bbox[1])
51295
+ ];
51296
+ }
51297
+
51298
+ function getProjectedMeshBBox(mesh) {
51299
+ var xmin = Infinity, ymin = Infinity, xmax = -Infinity, ymax = -Infinity;
51300
+ mesh.cells.forEach(function(cell) {
51301
+ if (!cell.valid) return;
51302
+ [cell.v00, cell.v10, cell.v01, cell.v11].forEach(function(p) {
51303
+ if (p.x < xmin) xmin = p.x;
51304
+ if (p.x > xmax) xmax = p.x;
51305
+ if (p.y < ymin) ymin = p.y;
51306
+ if (p.y > ymax) ymax = p.y;
51307
+ });
51308
+ });
51309
+ return xmin < Infinity && xmax > xmin && ymax > ymin ? [xmin, ymin, xmax, ymax] : null;
51310
+ }
51311
+
51312
+ function createProjectedRasterGrid(grid, bbox, widthArg, heightArg, opts) {
51313
+ var width = widthArg || grid.width;
51314
+ var height = heightArg || grid.height;
51315
+ var bands = getOutputBandCount(grid, opts);
51316
+ var samples = new grid.samples.constructor(width * height * bands);
51317
+ var coverage = new Uint8Array(width * height);
51318
+ fillProjectedRasterSamples(samples, bands, grid, opts || {});
51319
+ return Object.assign({}, grid, {
51320
+ width: width,
51321
+ height: height,
51322
+ bands: bands,
51323
+ samples: samples,
51324
+ coverage: coverage,
51325
+ bbox: bbox,
51326
+ transform: [
51327
+ (bbox[2] - bbox[0]) / width,
51328
+ 0,
51329
+ bbox[0],
51330
+ 0,
51331
+ -(bbox[3] - bbox[1]) / height,
51332
+ bbox[3]
51333
+ ]
51334
+ });
51335
+ }
51336
+
51337
+ function getOutputGridSize(grid, bbox, opts) {
51338
+ var width = opts.output_width || opts.outputWidth;
51339
+ var height = opts.output_height || opts.outputHeight;
51340
+ var pixels, aspect;
51341
+ if (width || height) {
51342
+ return {
51343
+ width: width || Math.max(1, Math.round(grid.width * height / grid.height)),
51344
+ height: height || Math.max(1, Math.round(grid.height * width / grid.width))
51345
+ };
51346
+ }
51347
+ pixels = grid.width * grid.height;
51348
+ aspect = (bbox[2] - bbox[0]) / (bbox[3] - bbox[1]);
51349
+ if (!isFinite(aspect) || aspect <= 0) return {width: grid.width, height: grid.height};
51350
+ width = Math.max(1, Math.round(Math.sqrt(pixels * aspect)));
51351
+ height = Math.max(1, Math.round(pixels / width));
51352
+ return {width: width, height: height};
51353
+ }
51354
+
51355
+ function getOutputBandCount(grid, opts) {
51356
+ var color = getNoDataColor(opts);
51357
+ return color && color.a === 0 && grid.bands > 1 && grid.bands < 4 ? 4 : grid.bands;
51358
+ }
51359
+
51360
+ function fillProjectedRasterSamples(samples, bands, grid, opts) {
51361
+ var color = getNoDataColor(opts);
51362
+ var noData = grid.nodata;
51363
+ if (color) {
51364
+ fillProjectedRasterColor(samples, bands, color);
51365
+ return;
51366
+ }
51367
+ if (noData === null || noData === undefined || !isFinite(noData)) return;
51368
+ samples.fill(noData);
51369
+ }
51370
+
51371
+ function getNoDataColor(opts) {
51372
+ var arg = opts.nodata_color || opts.nodataColor;
51373
+ var color;
51374
+ if (arg == null || arg === '') return null;
51375
+ if (String(arg).toLowerCase() == 'transparent') {
51376
+ return {r: 0, g: 0, b: 0, a: 0};
51377
+ }
51378
+ color = parseColor(arg);
51379
+ if (!color) stop$1('Unsupported nodata-color:', arg);
51380
+ return color;
51381
+ }
51382
+
51383
+ function fillProjectedRasterColor(samples, bands, color) {
51384
+ var gray = Math.round(0.299 * color.r + 0.587 * color.g + 0.114 * color.b);
51385
+ var alpha = Math.round((color.a == null ? 1 : color.a) * 255);
51386
+ for (var i = 0; i < samples.length; i += bands) {
51387
+ if (bands == 1) {
51388
+ samples[i] = gray;
51389
+ } else {
51390
+ samples[i] = color.r;
51391
+ samples[i + 1] = color.g;
51392
+ samples[i + 2] = color.b;
51393
+ if (bands > 3) samples[i + 3] = alpha;
51394
+ }
51395
+ }
51396
+ }
51397
+
51398
+ function rasterizeProjectedMesh(srcGrid, destGrid, mesh, sampleMethod) {
51399
+ mesh.cells.forEach(function(cell) {
51400
+ if (!cell.valid) return;
51401
+ rasterizeProjectedTriangle(srcGrid, destGrid, cell.v00, cell.v10, cell.v11, sampleMethod);
51402
+ rasterizeProjectedTriangle(srcGrid, destGrid, cell.v00, cell.v11, cell.v01, sampleMethod);
51403
+ });
51404
+ }
51405
+
51406
+ function rasterizeProjectedTriangle(srcGrid, destGrid, a, b, c, sampleMethod) {
51407
+ var bbox, x0, x1, y0, y1, det, w1, w2, w3, sx, sy, ap, bp, cp;
51408
+ if (!vertexIsValid(a) || !vertexIsValid(b) || !vertexIsValid(c)) return;
51409
+ ap = vertexToDestPixel(destGrid, a);
51410
+ bp = vertexToDestPixel(destGrid, b);
51411
+ cp = vertexToDestPixel(destGrid, c);
51412
+ det = triangleDet(ap, bp, cp);
51413
+ if (det === 0) return;
51414
+ bbox = getTrianglePixelBounds(destGrid, ap, bp, cp);
51415
+ x0 = bbox[0]; y0 = bbox[1]; x1 = bbox[2]; y1 = bbox[3];
51416
+ for (var y = y0; y <= y1; y++) {
51417
+ for (var x = x0; x <= x1; x++) {
51418
+ w1 = edgeFunction(bp, cp, x + 0.5, y + 0.5) / det;
51419
+ w2 = edgeFunction(cp, ap, x + 0.5, y + 0.5) / det;
51420
+ w3 = 1 - w1 - w2;
51421
+ if (w1 < -1e-9 || w2 < -1e-9 || w3 < -1e-9) continue;
51422
+ sx = w1 * a.sx + w2 * b.sx + w3 * c.sx;
51423
+ sy = w1 * a.sy + w2 * b.sy + w3 * c.sy;
51424
+ copyRasterSample(srcGrid, destGrid, sx, sy, x, y, sampleMethod);
51425
+ }
51426
+ }
51427
+ }
51428
+
51429
+ function vertexToDestPixel(grid, vertex) {
51430
+ var p = mapXYToRasterPixel(grid, vertex.x, vertex.y);
51431
+ return {
51432
+ x: p[0],
51433
+ y: p[1],
51434
+ sx: vertex.sx,
51435
+ sy: vertex.sy
51436
+ };
51437
+ }
51438
+
51439
+ function getTrianglePixelBounds(grid, p1, p2, p3) {
51440
+ return [
51441
+ clamp(Math.floor(Math.min(p1.x, p2.x, p3.x)), 0, grid.width - 1),
51442
+ clamp(Math.floor(Math.min(p1.y, p2.y, p3.y)), 0, grid.height - 1),
51443
+ clamp(Math.ceil(Math.max(p1.x, p2.x, p3.x)), 0, grid.width - 1),
51444
+ clamp(Math.ceil(Math.max(p1.y, p2.y, p3.y)), 0, grid.height - 1)
51445
+ ];
51446
+ }
51447
+
51448
+ function mapXYToRasterPixel(grid, x, y) {
51449
+ var bbox = grid.bbox;
51450
+ return [
51451
+ (x - bbox[0]) / (bbox[2] - bbox[0]) * grid.width,
51452
+ (bbox[3] - y) / (bbox[3] - bbox[1]) * grid.height
51453
+ ];
51454
+ }
51455
+
51456
+ function copyRasterSample(srcGrid, destGrid, sx, sy, dx, dy, sampleMethod) {
51457
+ if (sampleMethod == 'bilinear') {
51458
+ copyBilinearRasterSample(srcGrid, destGrid, sx, sy, dx, dy);
51459
+ } else {
51460
+ copyNearestRasterSample(srcGrid, destGrid, sx, sy, dx, dy);
51461
+ }
51462
+ }
51463
+
51464
+ function copyNearestRasterSample(srcGrid, destGrid, sx, sy, dx, dy) {
51465
+ var srcX = clamp(Math.floor(sx), 0, srcGrid.width - 1);
51466
+ var srcY = clamp(Math.floor(sy), 0, srcGrid.height - 1);
51467
+ var src = (srcY * srcGrid.width + srcX) * srcGrid.bands;
51468
+ var dest = (dy * destGrid.width + dx) * destGrid.bands;
51469
+ if (!rasterSourcePixelIsCovered(srcGrid, srcX, srcY)) return;
51470
+ for (var band = 0; band < srcGrid.bands; band++) {
51471
+ destGrid.samples[dest + band] = srcGrid.samples[src + band];
51472
+ }
51473
+ if (destGrid.bands > srcGrid.bands) destGrid.samples[dest + 3] = 255;
51474
+ if (destGrid.coverage) destGrid.coverage[dy * destGrid.width + dx] = 1;
51475
+ }
51476
+
51477
+ function copyBilinearRasterSample(srcGrid, destGrid, sx, sy, dx, dy) {
51478
+ var srcX = clamp(Math.floor(sx - 0.5), 0, srcGrid.width - 1);
51479
+ var srcY = clamp(Math.floor(sy - 0.5), 0, srcGrid.height - 1);
51480
+ var srcX2 = clamp(srcX + 1, 0, srcGrid.width - 1);
51481
+ var srcY2 = clamp(srcY + 1, 0, srcGrid.height - 1);
51482
+ var tx = clamp(sx - 0.5 - srcX, 0, 1);
51483
+ var ty = clamp(sy - 0.5 - srcY, 0, 1);
51484
+ var src00 = (srcY * srcGrid.width + srcX) * srcGrid.bands;
51485
+ var src10 = (srcY * srcGrid.width + srcX2) * srcGrid.bands;
51486
+ var src01 = (srcY2 * srcGrid.width + srcX) * srcGrid.bands;
51487
+ var src11 = (srcY2 * srcGrid.width + srcX2) * srcGrid.bands;
51488
+ var dest = (dy * destGrid.width + dx) * destGrid.bands;
51489
+ if (!srcGrid.coverage) {
51490
+ copyBilinearRasterSampleFast(srcGrid, destGrid, src00, src10, src01, src11, dest, tx, ty);
51491
+ if (destGrid.coverage) destGrid.coverage[dy * destGrid.width + dx] = 1;
51492
+ return;
51493
+ }
51494
+ if (copyBilinearRasterSampleWithCoverage(srcGrid, destGrid, srcX, srcY, srcX2, srcY2, src00, src10, src01, src11, dest, tx, ty)) {
51495
+ if (destGrid.coverage) destGrid.coverage[dy * destGrid.width + dx] = 1;
51496
+ }
51497
+ }
51498
+
51499
+ function copyBilinearRasterSampleFast(srcGrid, destGrid, src00, src10, src01, src11, dest, tx, ty) {
51500
+ if (srcGrid.bands == 3) {
51501
+ copyBilinearRgbSample(srcGrid.samples, destGrid.samples, src00, src10, src01, src11, dest, tx, ty);
51502
+ } else if (srcGrid.bands == 4) {
51503
+ copyBilinearRgbaSample(srcGrid.samples, destGrid.samples, src00, src10, src01, src11, dest, tx, ty);
51504
+ } else {
51505
+ copyBilinearGenericSample(srcGrid.samples, destGrid.samples, srcGrid.bands, src00, src10, src01, src11, dest, tx, ty);
51506
+ }
51507
+ if (destGrid.bands > srcGrid.bands) destGrid.samples[dest + 3] = 255;
51508
+ }
51509
+
51510
+ function copyBilinearRgbSample(src, destArr, src00, src10, src01, src11, dest, tx, ty) {
51511
+ copyBilinearBand(src, destArr, src00, src10, src01, src11, dest, 0, tx, ty);
51512
+ copyBilinearBand(src, destArr, src00, src10, src01, src11, dest, 1, tx, ty);
51513
+ copyBilinearBand(src, destArr, src00, src10, src01, src11, dest, 2, tx, ty);
51514
+ }
51515
+
51516
+ function copyBilinearRgbaSample(src, destArr, src00, src10, src01, src11, dest, tx, ty) {
51517
+ copyBilinearRgbSample(src, destArr, src00, src10, src01, src11, dest, tx, ty);
51518
+ copyBilinearBand(src, destArr, src00, src10, src01, src11, dest, 3, tx, ty);
51519
+ }
51520
+
51521
+ function copyBilinearGenericSample(src, destArr, bands, src00, src10, src01, src11, dest, tx, ty) {
51522
+ for (var band = 0; band < bands; band++) {
51523
+ copyBilinearBand(src, destArr, src00, src10, src01, src11, dest, band, tx, ty);
51524
+ }
51525
+ }
51526
+
51527
+ function copyBilinearBand(src, destArr, src00, src10, src01, src11, dest, band, tx, ty) {
51528
+ var a = src[src00 + band] * (1 - tx) + src[src10 + band] * tx;
51529
+ var b = src[src01 + band] * (1 - tx) + src[src11 + band] * tx;
51530
+ destArr[dest + band] = Math.round(a * (1 - ty) + b * ty);
51531
+ }
51532
+
51533
+ function copyBilinearRasterSampleWithCoverage(srcGrid, destGrid, srcX, srcY, srcX2, srcY2, src00, src10, src01, src11, dest, tx, ty) {
51534
+ var coords = [
51535
+ [srcX, srcY, src00, (1 - tx) * (1 - ty)],
51536
+ [srcX2, srcY, src10, tx * (1 - ty)],
51537
+ [srcX, srcY2, src01, (1 - tx) * ty],
51538
+ [srcX2, srcY2, src11, tx * ty]
51539
+ ];
51540
+ var total = 0;
51541
+ var val, item;
51542
+ for (var band = 0; band < srcGrid.bands; band++) {
51543
+ val = 0;
51544
+ total = 0;
51545
+ for (var i = 0; i < coords.length; i++) {
51546
+ item = coords[i];
51547
+ if (item[3] <= 0 || !rasterSourcePixelIsCovered(srcGrid, item[0], item[1])) continue;
51548
+ val += srcGrid.samples[item[2] + band] * item[3];
51549
+ total += item[3];
51550
+ }
51551
+ if (total <= 0) return false;
51552
+ destGrid.samples[dest + band] = Math.round(val / total);
51553
+ }
51554
+ if (destGrid.bands > srcGrid.bands) destGrid.samples[dest + 3] = 255;
51555
+ return true;
51556
+ }
51557
+
51558
+ function rasterSourcePixelIsCovered(grid, x, y) {
51559
+ return !grid.coverage || grid.coverage[y * grid.width + x] > 0;
51560
+ }
51561
+
51562
+ function timeStart(timing, name) {
51563
+ if (!timing) return;
51564
+ timing[name + 'Start'] = getTimer();
51565
+ }
51566
+
51567
+ function timeEnd(timing, name) {
51568
+ if (!timing) return;
51569
+ timing[name + 'Ms'] = getTimer() - timing[name + 'Start'];
51570
+ }
51571
+
51572
+ function getTimer() {
51573
+ return typeof performance != 'undefined' && performance.now ? performance.now() : Date.now();
51574
+ }
51575
+
51576
+ function vertexIsValid(p) {
51577
+ return p && isFinite(p.x) && isFinite(p.y);
51578
+ }
51579
+
51580
+ function triangleDet(a, b, c) {
51581
+ return edgeFunction(a, b, c.x, c.y);
51582
+ }
51583
+
51584
+ function edgeFunction(a, b, x, y) {
51585
+ return (x - a.x) * (b.y - a.y) - (y - a.y) * (b.x - a.x);
51586
+ }
51587
+
51588
+ function clamp(val, min, max) {
51589
+ return val < min ? min : val > max ? max : val;
51590
+ }
51591
+
51592
+ var RasterReprojection = /*#__PURE__*/Object.freeze({
51593
+ __proto__: null,
51594
+ buildProjectedRasterMesh: buildProjectedRasterMesh,
51595
+ getProjectedRasterGridBBox: getProjectedRasterGridBBox,
51596
+ getProjectedRasterMeshBBox: getProjectedRasterMeshBBox,
51597
+ projectRasterGridForward: projectRasterGridForward
51598
+ });
51599
+
50831
51600
  cmd.proj = function(dataset, catalog, opts, targetLayers) {
50832
51601
  var srcInfo, destInfo, destStr;
50833
51602
  var implicitlyProjectedNames = getImplicitlyTargetedLayerNames(dataset, targetLayers, layerHasGeometry);
@@ -50864,7 +51633,7 @@ ${svg}
50864
51633
  stop$1("Missing projection data");
50865
51634
  }
50866
51635
 
50867
- if (!datasetHasGeometry(dataset)) {
51636
+ if (!datasetHasGeometry(dataset) && !datasetHasRaster(dataset)) {
50868
51637
  // still set the crs of datasets that are missing geometry
50869
51638
  setDatasetCrsInfo(dataset, destInfo);
50870
51639
  return false;
@@ -50918,6 +51687,7 @@ ${svg}
50918
51687
  return a.name != b.name ||
50919
51688
  a.geometry_type != b.geometry_type ||
50920
51689
  a.data !== b.data ||
51690
+ a.raster !== b.raster ||
50921
51691
  !arraysAreEqual(a.shapes, b.shapes);
50922
51692
  }
50923
51693
 
@@ -50965,6 +51735,8 @@ ${svg}
50965
51735
  dataset.layers.forEach(function(lyr) {
50966
51736
  if (layerHasPoints(lyr)) {
50967
51737
  badPoints += projectPointLayer(lyr, proj); // v2 compatible (invalid points are removed)
51738
+ } else if (layerHasRaster(lyr)) {
51739
+ projectRasterLayer(lyr, src, dest, opts);
50968
51740
  }
50969
51741
  });
50970
51742
  if (dataset.arcs) {
@@ -50990,6 +51762,18 @@ ${svg}
50990
51762
  dataset.info.crs = dest;
50991
51763
  }
50992
51764
 
51765
+ function projectRasterLayer(lyr, src, dest, opts) {
51766
+ var raster = lyr.raster;
51767
+ raster.grid = projectRasterGridForward(raster, src, dest, opts || {});
51768
+ raster.view = raster.view || {};
51769
+ delete raster.view.scalingStats;
51770
+ if (runningInBrowser()) {
51771
+ raster.view.preview = createRasterPreview(raster, opts || {});
51772
+ } else {
51773
+ delete raster.view.preview;
51774
+ }
51775
+ }
51776
+
50993
51777
  // * Heals cuts in previously split-apart polygons
50994
51778
  // * Removes line intersections
50995
51779
  // * TODO: what if a layer contains polygons with desired overlaps? should
@@ -57382,7 +58166,7 @@ ${svg}
57382
58166
  });
57383
58167
  }
57384
58168
 
57385
- var version = "0.7.15";
58169
+ var version = "0.7.16";
57386
58170
 
57387
58171
  // Parse command line args into commands and run them
57388
58172
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
@@ -58911,6 +59695,7 @@ ${svg}
58911
59695
  Lines,
58912
59696
  Logging,
58913
59697
  Profile,
59698
+ RasterReprojection,
58914
59699
  RasterUtils,
58915
59700
  Merging,
58916
59701
  MosaicIndex$1,