mapshaper 0.7.49 → 0.7.50
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 +1784 -276
- package/package.json +2 -2
- package/www/mapshaper-gui.js +122 -19
- package/www/mapshaper.js +1784 -276
- package/www/modules.js +122 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mapshaper",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.50",
|
|
4
4
|
"description": "A tool for editing geospatial data for mapping and GIS.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"shapefile",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"idb-keyval": "^6.2.0",
|
|
72
72
|
"jpeg-js": "^0.4.4",
|
|
73
73
|
"kdbush": "^3.0.0",
|
|
74
|
-
"mproj": "0.1.
|
|
74
|
+
"mproj": "0.1.6",
|
|
75
75
|
"msgpackr": "^1.11.12",
|
|
76
76
|
"open": "^11.0.0",
|
|
77
77
|
"pngjs": "^7.0.0",
|
package/www/mapshaper-gui.js
CHANGED
|
@@ -7398,14 +7398,20 @@
|
|
|
7398
7398
|
}
|
|
7399
7399
|
}
|
|
7400
7400
|
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
|
|
7401
|
+
// Draw and select what was imported. Called when import mode ends, and by
|
|
7402
|
+
// importQueuedFiles() if something else has already ended it.
|
|
7403
|
+
function finishImport() {
|
|
7404
7404
|
if (importCount > 0) {
|
|
7405
7405
|
onImportComplete();
|
|
7406
7406
|
importTotal += importCount;
|
|
7407
7407
|
importCount = 0;
|
|
7408
7408
|
}
|
|
7409
|
+
}
|
|
7410
|
+
|
|
7411
|
+
function turnOff() {
|
|
7412
|
+
var target;
|
|
7413
|
+
if (catalog) catalog.reset(); // re-enable clickable catalog
|
|
7414
|
+
finishImport();
|
|
7409
7415
|
gui.clearProgressMessage();
|
|
7410
7416
|
initialImport = false; // unset 'quick view' mode, if on
|
|
7411
7417
|
clearQueuedFiles();
|
|
@@ -7432,8 +7438,13 @@
|
|
|
7432
7438
|
gui.alert(e.message, 'Import error');
|
|
7433
7439
|
}
|
|
7434
7440
|
if (gui.getMode() == 'import') {
|
|
7435
|
-
//
|
|
7436
|
-
|
|
7441
|
+
gui.clearMode(); // leaving import mode finishes the import
|
|
7442
|
+
} else {
|
|
7443
|
+
// A popup shown while files were loading (an alert about one file's
|
|
7444
|
+
// metadata, say) takes the app out of import mode, so leaving it is no
|
|
7445
|
+
// longer what finishes the import. Without this, the files that did load
|
|
7446
|
+
// would sit in the catalog undrawn.
|
|
7447
|
+
finishImport();
|
|
7437
7448
|
}
|
|
7438
7449
|
if (undoTransaction && imported) {
|
|
7439
7450
|
addUndoTransactionToHistory(gui, undoTransaction, {
|
|
@@ -10905,12 +10916,23 @@
|
|
|
10905
10916
|
var dataset = active.dataset;
|
|
10906
10917
|
var inputFmt = dataset.info && dataset.info.input_formats &&
|
|
10907
10918
|
dataset.info.input_formats[0];
|
|
10908
|
-
|
|
10919
|
+
// SVG stays the default for rasters: it is the only format that also
|
|
10920
|
+
// accepts the vector layers a session usually has alongside them.
|
|
10921
|
+
if (activeLayerHasRaster()) return 'svg';
|
|
10909
10922
|
return getExportFormats().includes(inputFmt) ? inputFmt : 'geojson';
|
|
10910
10923
|
}
|
|
10911
10924
|
|
|
10912
10925
|
function getExportFormats() {
|
|
10913
|
-
|
|
10926
|
+
var formats = ['shapefile', 'json', 'geojson', 'dsv', 'topojson', 'flatgeobuf', 'geopackage', 'geoparquet', 'kml', 'svg', internal.PACKAGE_EXT];
|
|
10927
|
+
// GeoTIFF is the one format here that only accepts raster layers, so it is
|
|
10928
|
+
// offered only when there is a raster to export.
|
|
10929
|
+
if (activeLayerHasRaster()) formats.push('geotiff');
|
|
10930
|
+
return formats;
|
|
10931
|
+
}
|
|
10932
|
+
|
|
10933
|
+
function activeLayerHasRaster() {
|
|
10934
|
+
var active = model.getActiveLayer();
|
|
10935
|
+
return !!active.layer && internal.layerHasRaster(active.layer);
|
|
10914
10936
|
}
|
|
10915
10937
|
|
|
10916
10938
|
function initFormatMenu() {
|
|
@@ -20962,6 +20984,31 @@
|
|
|
20962
20984
|
|
|
20963
20985
|
utils$1.inherit(MapExtent, EventDispatcher);
|
|
20964
20986
|
|
|
20987
|
+
// Rules for drawing a raster that is being displayed larger than its data.
|
|
20988
|
+
// Kept free of browser and mapshaper dependencies so they can be tested
|
|
20989
|
+
// directly.
|
|
20990
|
+
|
|
20991
|
+
// Screen pixels per raster pixel at which a magnified raster is drawn as
|
|
20992
|
+
// squares instead of being smoothed. Below this there is little detail to lose
|
|
20993
|
+
// and smoothing looks better; above it, the smoothing is what a user sees
|
|
20994
|
+
// instead of the data.
|
|
20995
|
+
var MIN_CRISP_MAGNIFICATION = 2;
|
|
20996
|
+
|
|
20997
|
+
// True if a preview image holds every raster pixel in view, and so is showing
|
|
20998
|
+
// the data itself rather than a scaled-down rendering of it. Only such an
|
|
20999
|
+
// image is worth drawing as squares: magnifying a coarser preview would show
|
|
21000
|
+
// crisp squares that are not pixels of the raster.
|
|
21001
|
+
function previewHasSourcePixels(width, height, sourceWidth, sourceHeight) {
|
|
21002
|
+
if (!(sourceWidth > 0) || !(sourceHeight > 0)) return false;
|
|
21003
|
+
return width >= sourceWidth && height >= sourceHeight;
|
|
21004
|
+
}
|
|
21005
|
+
|
|
21006
|
+
// sourcePixels: from previewHasSourcePixels()
|
|
21007
|
+
// magnification: screen pixels per pixel of the preview image
|
|
21008
|
+
function rasterPreviewIsSmoothed(sourcePixels, magnification) {
|
|
21009
|
+
return !sourcePixels || !(magnification >= MIN_CRISP_MAGNIFICATION);
|
|
21010
|
+
}
|
|
21011
|
+
|
|
20965
21012
|
var MAX_VIEWPORT_PREVIEW_PIXELS = 6e6;
|
|
20966
21013
|
var cache$1 = new WeakMap();
|
|
20967
21014
|
var requestId$1 = 0;
|
|
@@ -20988,6 +21035,7 @@
|
|
|
20988
21035
|
stats = getCachedRasterScalingStats(params, timing);
|
|
20989
21036
|
timing.renderStart = getTimer$1();
|
|
20990
21037
|
preview = internal.renderRasterViewportPreview(params.grid, params.recipe, params.bbox, params.width, params.height, stats);
|
|
21038
|
+
if (preview) preview.sourcePixels = params.sourcePixels;
|
|
20991
21039
|
timing.renderMs = getTimer$1() - timing.renderStart;
|
|
20992
21040
|
logRasterPreviewTiming(params, timing);
|
|
20993
21041
|
current = cache$1.get(layer);
|
|
@@ -21039,6 +21087,7 @@
|
|
|
21039
21087
|
return {
|
|
21040
21088
|
key: key,
|
|
21041
21089
|
needed: needed,
|
|
21090
|
+
sourcePixels: previewHasSourcePixels(width, height, crop.width, crop.height),
|
|
21042
21091
|
raster: raster,
|
|
21043
21092
|
grid: grid,
|
|
21044
21093
|
recipe: recipe,
|
|
@@ -21197,6 +21246,7 @@
|
|
|
21197
21246
|
}
|
|
21198
21247
|
timing.renderStart = getTimer();
|
|
21199
21248
|
preview = internal.renderRasterPreview(grid, params.recipe, grid.width, grid.height, params.stats);
|
|
21249
|
+
preview.sourcePixels = params.sourcePixels;
|
|
21200
21250
|
applyCoverageMask(preview, grid.coverage);
|
|
21201
21251
|
timing.renderMs = getTimer() - timing.renderStart;
|
|
21202
21252
|
logRasterReprojectionTiming(params, timing);
|
|
@@ -21220,12 +21270,13 @@
|
|
|
21220
21270
|
var viewBbox = ext && ext.getBounds().toArray();
|
|
21221
21271
|
var bbox = rasterBbox && viewBbox && internal.intersectBboxes(rasterBbox, viewBbox);
|
|
21222
21272
|
var size = bbox && getPreviewSize(ext, sourceGrid);
|
|
21223
|
-
var recipe, stats;
|
|
21273
|
+
var recipe, stats, sourcePixels;
|
|
21224
21274
|
if (!sourceCRS || !displayCRS || !sourceGrid || !sourceGrid.samples || !bbox || !size) return null;
|
|
21225
21275
|
recipe = internal.getRasterViewRecipe(sourceGrid, raster.view && raster.view.recipe);
|
|
21226
21276
|
stats = internal.getRasterViewScalingStats(raster, recipe);
|
|
21277
|
+
sourcePixels = outputHasSourcePixels(sourceGrid, bbox, sourceCRS, displayCRS, size);
|
|
21227
21278
|
return {
|
|
21228
|
-
key: getRasterReprojectedPreviewKey(layer, bbox, size, sourceCRS, displayCRS, recipe),
|
|
21279
|
+
key: getRasterReprojectedPreviewKey(layer, bbox, size, sourceCRS, displayCRS, recipe, sourcePixels),
|
|
21229
21280
|
grid: sourceGrid,
|
|
21230
21281
|
sourceCRS: sourceCRS,
|
|
21231
21282
|
displayCRS: displayCRS,
|
|
@@ -21234,11 +21285,49 @@
|
|
|
21234
21285
|
height: size.height,
|
|
21235
21286
|
recipe: recipe,
|
|
21236
21287
|
stats: stats,
|
|
21237
|
-
|
|
21288
|
+
sourcePixels: sourcePixels,
|
|
21289
|
+
sampleMethod: getRasterReprojectionSampleMethod(sourcePixels),
|
|
21238
21290
|
meshInterval: 32
|
|
21239
21291
|
};
|
|
21240
21292
|
}
|
|
21241
21293
|
|
|
21294
|
+
// True if the reprojected grid has room for every source pixel in view, so that
|
|
21295
|
+
// sampling it without interpolation gives back the raster's own pixels. This is
|
|
21296
|
+
// the zoomed-in case: reprojecting a raster to fewer pixels than it has needs
|
|
21297
|
+
// interpolation to avoid dropping data.
|
|
21298
|
+
function outputHasSourcePixels(grid, bbox, sourceCRS, displayCRS, size) {
|
|
21299
|
+
var sourceSize = getSourcePixelsInView(grid, bbox, sourceCRS, displayCRS);
|
|
21300
|
+
return !!sourceSize && previewHasSourcePixels(
|
|
21301
|
+
size.width, size.height, sourceSize.width, sourceSize.height);
|
|
21302
|
+
}
|
|
21303
|
+
|
|
21304
|
+
// Estimates how many of the raster's pixels a view covers, by taking the part
|
|
21305
|
+
// of the source grid that the view maps back onto. Returns null if the view
|
|
21306
|
+
// does not map back (e.g. it includes coordinates outside the projection).
|
|
21307
|
+
function getSourcePixelsInView(grid, bbox, sourceCRS, displayCRS) {
|
|
21308
|
+
var transform = internal.getProjTransform2(displayCRS, sourceCRS);
|
|
21309
|
+
var xx = [bbox[0], (bbox[0] + bbox[2]) / 2, bbox[2]];
|
|
21310
|
+
var yy = [bbox[1], (bbox[1] + bbox[3]) / 2, bbox[3]];
|
|
21311
|
+
var xmin = Infinity, ymin = Infinity, xmax = -Infinity, ymax = -Infinity;
|
|
21312
|
+
var p;
|
|
21313
|
+
if (!transform) return null;
|
|
21314
|
+
for (var i = 0; i < xx.length; i++) {
|
|
21315
|
+
for (var j = 0; j < yy.length; j++) {
|
|
21316
|
+
p = transform(xx[i], yy[j]);
|
|
21317
|
+
if (!p || !isFinite(p[0]) || !isFinite(p[1])) continue;
|
|
21318
|
+
if (p[0] < xmin) xmin = p[0];
|
|
21319
|
+
if (p[0] > xmax) xmax = p[0];
|
|
21320
|
+
if (p[1] < ymin) ymin = p[1];
|
|
21321
|
+
if (p[1] > ymax) ymax = p[1];
|
|
21322
|
+
}
|
|
21323
|
+
}
|
|
21324
|
+
if (!(xmax > xmin) || !(ymax > ymin)) return null;
|
|
21325
|
+
return {
|
|
21326
|
+
width: (xmax - xmin) / (grid.bbox[2] - grid.bbox[0]) * grid.width,
|
|
21327
|
+
height: (ymax - ymin) / (grid.bbox[3] - grid.bbox[1]) * grid.height
|
|
21328
|
+
};
|
|
21329
|
+
}
|
|
21330
|
+
|
|
21242
21331
|
function getPreviewSize(ext, grid) {
|
|
21243
21332
|
var pixelRatio = GUI.getPixelRatio();
|
|
21244
21333
|
var t = ext.getTransform(pixelRatio);
|
|
@@ -21253,10 +21342,11 @@
|
|
|
21253
21342
|
return {width: width, height: height};
|
|
21254
21343
|
}
|
|
21255
21344
|
|
|
21256
|
-
function getRasterReprojectedPreviewKey(layer, bbox, size, sourceCRS, displayCRS, recipe) {
|
|
21345
|
+
function getRasterReprojectedPreviewKey(layer, bbox, size, sourceCRS, displayCRS, recipe, sourcePixels) {
|
|
21257
21346
|
return [
|
|
21258
21347
|
size.width,
|
|
21259
21348
|
size.height,
|
|
21349
|
+
sourcePixels,
|
|
21260
21350
|
bbox.join(','),
|
|
21261
21351
|
internal.crsToProj4(sourceCRS),
|
|
21262
21352
|
internal.crsToProj4(displayCRS),
|
|
@@ -21264,7 +21354,7 @@
|
|
|
21264
21354
|
recipe.scaling,
|
|
21265
21355
|
recipe.scaleRange && recipe.scaleRange.join(','),
|
|
21266
21356
|
recipe.percentileRange && recipe.percentileRange.join(','),
|
|
21267
|
-
getRasterReprojectionSampleMethod(),
|
|
21357
|
+
getRasterReprojectionSampleMethod(sourcePixels),
|
|
21268
21358
|
layer.raster && layer.raster.grid && layer.raster.grid.samples && layer.raster.grid.samples.length
|
|
21269
21359
|
].join('|');
|
|
21270
21360
|
}
|
|
@@ -21292,10 +21382,13 @@
|
|
|
21292
21382
|
}
|
|
21293
21383
|
}
|
|
21294
21384
|
|
|
21295
|
-
function getRasterReprojectionSampleMethod() {
|
|
21385
|
+
function getRasterReprojectionSampleMethod(sourcePixels) {
|
|
21296
21386
|
var vars = GUI.getUrlVars();
|
|
21297
21387
|
var val = vars['raster-bilinear'] ?? vars.raster_bilinear;
|
|
21298
|
-
|
|
21388
|
+
if (val === false || val == '0') return 'nearest';
|
|
21389
|
+
// Interpolating between pixels when there is room for all of them would blur
|
|
21390
|
+
// the data away for nothing.
|
|
21391
|
+
return sourcePixels ? 'nearest' : 'bilinear';
|
|
21299
21392
|
}
|
|
21300
21393
|
|
|
21301
21394
|
function logRasterReprojectionTiming(params, timing) {
|
|
@@ -21584,7 +21677,7 @@
|
|
|
21584
21677
|
var raster = layer.raster;
|
|
21585
21678
|
var options = opts || {};
|
|
21586
21679
|
var preview = null;
|
|
21587
|
-
var bbox;
|
|
21680
|
+
var bbox, grid, sourcePixels;
|
|
21588
21681
|
if (layer.gui && layer.gui.dynamic_crs) {
|
|
21589
21682
|
preview = getCachedRasterReprojectedPreview(layer, _ext);
|
|
21590
21683
|
if (!preview) {
|
|
@@ -21595,7 +21688,7 @@
|
|
|
21595
21688
|
}
|
|
21596
21689
|
bbox = preview.bbox;
|
|
21597
21690
|
if (!preview.pixels || !bbox) return;
|
|
21598
|
-
drawRasterPreview(preview, bbox);
|
|
21691
|
+
drawRasterPreview(preview, bbox, preview.sourcePixels);
|
|
21599
21692
|
if (options.action != 'nav' && options.onViewportPreviewReady) {
|
|
21600
21693
|
scheduleRasterReprojectedPreview(layer, _ext, options.onViewportPreviewReady);
|
|
21601
21694
|
}
|
|
@@ -21603,15 +21696,21 @@
|
|
|
21603
21696
|
}
|
|
21604
21697
|
preview = options.action == 'nav' ? null : getCachedRasterViewportPreview(layer, _ext);
|
|
21605
21698
|
bbox = preview && preview.bbox;
|
|
21699
|
+
sourcePixels = preview && preview.sourcePixels;
|
|
21606
21700
|
if (!preview) {
|
|
21701
|
+
// The whole-raster preview made at import time, which is scaled down if
|
|
21702
|
+
// the raster is large.
|
|
21607
21703
|
preview = raster && internal.getRasterPreview(raster);
|
|
21608
21704
|
bbox = raster && internal.getRasterBBox(raster);
|
|
21705
|
+
grid = raster && internal.getRasterGrid(raster);
|
|
21706
|
+
sourcePixels = !!(preview && grid) && previewHasSourcePixels(
|
|
21707
|
+
preview.width, preview.height, grid.width, grid.height);
|
|
21609
21708
|
if (options.action != 'nav' && options.onViewportPreviewReady) {
|
|
21610
21709
|
scheduleRasterViewportPreview(layer, _ext, options.onViewportPreviewReady);
|
|
21611
21710
|
}
|
|
21612
21711
|
}
|
|
21613
21712
|
if (!preview || !preview.pixels || !bbox) return;
|
|
21614
|
-
drawRasterPreview(preview, bbox);
|
|
21713
|
+
drawRasterPreview(preview, bbox, sourcePixels);
|
|
21615
21714
|
};
|
|
21616
21715
|
|
|
21617
21716
|
/*
|
|
@@ -21813,12 +21912,16 @@
|
|
|
21813
21912
|
}
|
|
21814
21913
|
}
|
|
21815
21914
|
|
|
21816
|
-
|
|
21915
|
+
// sourcePixels: true if one pixel of the preview is one pixel of the raster,
|
|
21916
|
+
// so that drawing it as squares shows the data rather than an artifact of
|
|
21917
|
+
// whatever it was scaled down to.
|
|
21918
|
+
function drawRasterPreview(preview, bbox, sourcePixels) {
|
|
21817
21919
|
var img = getRasterCanvas(preview);
|
|
21818
21920
|
var t = _ext.getTransform(GUI.getPixelRatio());
|
|
21819
21921
|
var p1 = t.transform(bbox[0], bbox[3]);
|
|
21820
21922
|
var p2 = t.transform(bbox[2], bbox[1]);
|
|
21821
|
-
|
|
21923
|
+
var magnification = Math.abs(p2[0] - p1[0]) / img.width / GUI.getPixelRatio();
|
|
21924
|
+
_ctx.imageSmoothingEnabled = rasterPreviewIsSmoothed(sourcePixels, magnification);
|
|
21822
21925
|
_ctx.drawImage(img, p1[0], p1[1], p2[0] - p1[0], p2[1] - p1[1]);
|
|
21823
21926
|
}
|
|
21824
21927
|
|