mapshaper 0.6.19 → 0.6.20
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 +157 -74
- package/package.json +1 -1
- package/www/index.html +2 -0
- package/www/mapshaper-gui.js +131 -74
- package/www/mapshaper.js +157 -74
- package/www/page.css +17 -7
package/www/mapshaper-gui.js
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
get defaults () { return defaults; },
|
|
28
28
|
get extend () { return extend; },
|
|
29
29
|
get inherit () { return inherit; },
|
|
30
|
+
get promisify () { return promisify; },
|
|
30
31
|
get reduceAsync () { return reduceAsync; },
|
|
31
32
|
get merge () { return merge; },
|
|
32
33
|
get difference () { return difference; },
|
|
@@ -1342,13 +1343,6 @@
|
|
|
1342
1343
|
var sessionId = getUniqId('session');
|
|
1343
1344
|
var snapshotCount = 0;
|
|
1344
1345
|
|
|
1345
|
-
window.addEventListener('beforeunload', async function() {
|
|
1346
|
-
// delete snapshot data
|
|
1347
|
-
// This is not ideal, because the data gets deleted even if the user
|
|
1348
|
-
// cancels the page close... but there's no apparent good alternative
|
|
1349
|
-
await finalCleanup();
|
|
1350
|
-
});
|
|
1351
|
-
|
|
1352
1346
|
function getUniqId(prefix) {
|
|
1353
1347
|
return prefix + '_' + (Math.random() + 1).toString(36).substring(2,8);
|
|
1354
1348
|
}
|
|
@@ -1358,29 +1352,43 @@
|
|
|
1358
1352
|
}
|
|
1359
1353
|
|
|
1360
1354
|
function SessionSnapshots(gui) {
|
|
1361
|
-
if (!gui.options.saveControl) return;
|
|
1362
1355
|
var _menuOpen = false;
|
|
1363
1356
|
var _menuTimeout;
|
|
1364
|
-
var btn
|
|
1365
|
-
var menu = El('div').addClass('nav-sub-menu save-menu').appendTo(btn.node());
|
|
1357
|
+
var btn, menu;
|
|
1366
1358
|
|
|
1367
|
-
|
|
1359
|
+
init();
|
|
1368
1360
|
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
if (!_menuOpen) {
|
|
1373
|
-
openMenu();
|
|
1374
|
-
}
|
|
1375
|
-
});
|
|
1361
|
+
async function init() {
|
|
1362
|
+
var enabled = await isStorageEnabled();
|
|
1363
|
+
if (!enabled) return;
|
|
1376
1364
|
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1365
|
+
btn = gui.buttons.addButton('#ribbon-icon').addClass('menu-btn save-btn');
|
|
1366
|
+
menu = El('div').addClass('nav-sub-menu save-menu').appendTo(btn.node());
|
|
1367
|
+
await initialCleanup();
|
|
1368
|
+
|
|
1369
|
+
window.addEventListener('beforeunload', async function() {
|
|
1370
|
+
// delete snapshot data
|
|
1371
|
+
// This is not ideal, because the data gets deleted even if the user
|
|
1372
|
+
// cancels the page close... but there's no apparent good alternative
|
|
1373
|
+
await finalCleanup();
|
|
1374
|
+
});
|
|
1375
|
+
|
|
1376
|
+
btn.on('mouseenter', function() {
|
|
1377
|
+
btn.addClass('hover');
|
|
1378
|
+
clearTimeout(_menuTimeout); // prevent timed closing
|
|
1379
|
+
if (!_menuOpen) {
|
|
1380
|
+
openMenu();
|
|
1381
|
+
}
|
|
1382
|
+
});
|
|
1383
|
+
|
|
1384
|
+
btn.on('mouseleave', function() {
|
|
1385
|
+
if (!_menuOpen) {
|
|
1386
|
+
btn.removeClass('hover');
|
|
1387
|
+
} else {
|
|
1388
|
+
closeMenu(200);
|
|
1389
|
+
}
|
|
1390
|
+
});
|
|
1391
|
+
}
|
|
1384
1392
|
|
|
1385
1393
|
async function renderMenu() {
|
|
1386
1394
|
var snapshots = await fetchSnapshotList();
|
|
@@ -1407,7 +1415,7 @@
|
|
|
1407
1415
|
var line = El('div').appendTo(menu).addClass('save-menu-item');
|
|
1408
1416
|
El('span').appendTo(line).html(`<span class="save-item-label">#${item.number}</span> `);
|
|
1409
1417
|
// show snapshot size
|
|
1410
|
-
|
|
1418
|
+
El('span').appendTo(line).html(` <span class="save-item-size">${item.display_size}</span>`);
|
|
1411
1419
|
El('span').addClass('save-menu-btn').appendTo(line).on('click', async function(e) {
|
|
1412
1420
|
await restoreSnapshotById(item.id, gui);
|
|
1413
1421
|
closeMenu(100);
|
|
@@ -1461,7 +1469,7 @@
|
|
|
1461
1469
|
}
|
|
1462
1470
|
|
|
1463
1471
|
async function saveSnapshot(gui) {
|
|
1464
|
-
var buf = captureSnapshot(gui);
|
|
1472
|
+
var buf = await captureSnapshot(gui);
|
|
1465
1473
|
var entryId = String(++snapshotCount).padStart(3, '0');
|
|
1466
1474
|
var snapshotId = sessionId + '_' + entryId; // e.g. session_d89fw_001
|
|
1467
1475
|
var entry = {
|
|
@@ -1512,7 +1520,7 @@
|
|
|
1512
1520
|
console.log('Snapshot not available:', id);
|
|
1513
1521
|
return;
|
|
1514
1522
|
}
|
|
1515
|
-
var data = internal.unpackSession(buf);
|
|
1523
|
+
var data = await internal.unpackSession(buf);
|
|
1516
1524
|
buf = null;
|
|
1517
1525
|
gui.model.clear();
|
|
1518
1526
|
importDatasets(data.datasets, gui);
|
|
@@ -1522,11 +1530,11 @@
|
|
|
1522
1530
|
// Add datasets to the current project
|
|
1523
1531
|
// TODO: figure out if interface data should be imported (e.g. should
|
|
1524
1532
|
// visibility flag of imported layers be imported)
|
|
1525
|
-
function importSessionData(buf, gui) {
|
|
1533
|
+
async function importSessionData(buf, gui) {
|
|
1526
1534
|
if (buf instanceof ArrayBuffer) {
|
|
1527
1535
|
buf = new Uint8Array(buf);
|
|
1528
1536
|
}
|
|
1529
|
-
var data = internal.unpackSession(buf);
|
|
1537
|
+
var data = await internal.unpackSession(buf);
|
|
1530
1538
|
importDatasets(data.datasets, gui);
|
|
1531
1539
|
}
|
|
1532
1540
|
|
|
@@ -1537,11 +1545,16 @@
|
|
|
1537
1545
|
gui.model.updated({select: true, arc_count: true}); // arc_count to refresh display shapes
|
|
1538
1546
|
}
|
|
1539
1547
|
|
|
1540
|
-
function captureSnapshot(gui) {
|
|
1548
|
+
async function captureSnapshot(gui) {
|
|
1541
1549
|
var datasets = gui.model.getDatasets();
|
|
1542
1550
|
var lyr = gui.model.getActiveLayer().layer;
|
|
1551
|
+
// compact: true applies compression to vector coordinates, for ~30% reduction
|
|
1552
|
+
// in file size in a typical polygon or polyline file, but longer processing time
|
|
1553
|
+
var opts = {compact: false};
|
|
1543
1554
|
lyr.active = true;
|
|
1544
|
-
|
|
1555
|
+
// console.time('msx');
|
|
1556
|
+
var obj = await internal.exportDatasetsToPack(datasets, opts);
|
|
1557
|
+
// console.timeEnd('msx')
|
|
1545
1558
|
delete lyr.active;
|
|
1546
1559
|
obj.gui = getGuiState(gui);
|
|
1547
1560
|
return internal.pack(obj);
|
|
@@ -1597,7 +1610,7 @@
|
|
|
1597
1610
|
}
|
|
1598
1611
|
});
|
|
1599
1612
|
// remove old indexed snapshots
|
|
1600
|
-
|
|
1613
|
+
await updateIndex(function(index) {
|
|
1601
1614
|
index.snapshots = index.snapshots.filter(function(snap) {
|
|
1602
1615
|
var msPerDay = 1000 * 60 * 60 * 24;
|
|
1603
1616
|
var daysOld = (Date.now() - snap.created) / msPerDay;
|
|
@@ -1679,17 +1692,24 @@
|
|
|
1679
1692
|
}
|
|
1680
1693
|
|
|
1681
1694
|
function getSessionData() {
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
return data || [];
|
|
1685
|
-
} catch(e) {}
|
|
1686
|
-
return [];
|
|
1695
|
+
var data = JSON.parse(window.localStorage.getItem('session_data'));
|
|
1696
|
+
return data || [];
|
|
1687
1697
|
}
|
|
1688
1698
|
|
|
1689
1699
|
function setSessionData(arr) {
|
|
1690
1700
|
window.localStorage.setItem('session_data', JSON.stringify(arr));
|
|
1691
1701
|
}
|
|
1692
1702
|
|
|
1703
|
+
async function isStorageEnabled() {
|
|
1704
|
+
try {
|
|
1705
|
+
setSessionData(getSessionData());
|
|
1706
|
+
await updateIndex(function() {});
|
|
1707
|
+
return true;
|
|
1708
|
+
} catch(e) {
|
|
1709
|
+
return false;
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1693
1713
|
// @cb function(<FileList>)
|
|
1694
1714
|
function DropControl(gui, el, cb) {
|
|
1695
1715
|
var area = El(el);
|
|
@@ -1983,7 +2003,7 @@
|
|
|
1983
2003
|
await wait(35);
|
|
1984
2004
|
}
|
|
1985
2005
|
if (group[internal.PACKAGE_EXT]) {
|
|
1986
|
-
importSessionData(group[internal.PACKAGE_EXT].content, gui);
|
|
2006
|
+
await importSessionData(group[internal.PACKAGE_EXT].content, gui);
|
|
1987
2007
|
} else {
|
|
1988
2008
|
importDataset(group, importOpts);
|
|
1989
2009
|
}
|
|
@@ -3489,7 +3509,7 @@
|
|
|
3489
3509
|
flags.arc_count || flags.snap || flags.affine || flags.points || flags['merge-layers'];
|
|
3490
3510
|
if (intersectionsMayHaveChanged) {
|
|
3491
3511
|
// delete any cached intersection data, to trigger re-calculation
|
|
3492
|
-
e.dataset.
|
|
3512
|
+
e.dataset._intersections = null;
|
|
3493
3513
|
updateAsync();
|
|
3494
3514
|
} else if (flags.select) {
|
|
3495
3515
|
// new active layer, but no editing commands were run -- use cached intersections (if available)
|
|
@@ -3538,11 +3558,11 @@
|
|
|
3538
3558
|
XX = internal.findSegmentIntersections(arcs, opts);
|
|
3539
3559
|
showBtn = XX.length > 0;
|
|
3540
3560
|
} else { // no simplification
|
|
3541
|
-
XX = dataset.
|
|
3561
|
+
XX = dataset._intersections;
|
|
3542
3562
|
if (!XX) {
|
|
3543
3563
|
// cache intersections at 0 simplification, to avoid recalculating
|
|
3544
3564
|
// every time the simplification slider is set to 100% or the layer is selected at 100%
|
|
3545
|
-
XX = dataset.
|
|
3565
|
+
XX = dataset._intersections = internal.findSegmentIntersections(arcs, opts);
|
|
3546
3566
|
}
|
|
3547
3567
|
showBtn = false;
|
|
3548
3568
|
}
|
|
@@ -3559,7 +3579,7 @@
|
|
|
3559
3579
|
|
|
3560
3580
|
function dismiss() {
|
|
3561
3581
|
var dataset = model.getActiveLayer().dataset;
|
|
3562
|
-
dataset.
|
|
3582
|
+
dataset._intersections = null;
|
|
3563
3583
|
dataset.info.no_intersections = true;
|
|
3564
3584
|
reset();
|
|
3565
3585
|
}
|
|
@@ -3624,8 +3644,6 @@
|
|
|
3624
3644
|
return layers.reverse();
|
|
3625
3645
|
}
|
|
3626
3646
|
|
|
3627
|
-
// import { groupLayersByDataset } from '../dataset/mapshaper-target-utils';
|
|
3628
|
-
|
|
3629
3647
|
// Export buttons and their behavior
|
|
3630
3648
|
var ExportControl = function(gui) {
|
|
3631
3649
|
var model = gui.model;
|
|
@@ -3677,16 +3695,15 @@
|
|
|
3677
3695
|
gui.clearMode();
|
|
3678
3696
|
gui.showProgressMessage('Exporting');
|
|
3679
3697
|
setTimeout(function() {
|
|
3680
|
-
exportMenuSelection(layers
|
|
3681
|
-
if (err) {
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
gui.alert('Export failed for an unknown reason');
|
|
3688
|
-
}
|
|
3698
|
+
exportMenuSelection(layers).catch(function(err) {
|
|
3699
|
+
if (utils$1.isString(err)) {
|
|
3700
|
+
gui.alert(err);
|
|
3701
|
+
} else {
|
|
3702
|
+
// stack seems to change if Error is logged directly
|
|
3703
|
+
console.error(err.stack);
|
|
3704
|
+
gui.alert('Export failed for an unknown reason');
|
|
3689
3705
|
}
|
|
3706
|
+
}).finally(function() {
|
|
3690
3707
|
gui.clearProgressMessage();
|
|
3691
3708
|
});
|
|
3692
3709
|
}, 20);
|
|
@@ -3708,17 +3725,12 @@
|
|
|
3708
3725
|
}
|
|
3709
3726
|
|
|
3710
3727
|
// done: function(string|Error|null)
|
|
3711
|
-
function exportMenuSelection(layers
|
|
3712
|
-
var opts
|
|
3713
|
-
try {
|
|
3714
|
-
opts = getExportOpts();
|
|
3728
|
+
async function exportMenuSelection(layers) {
|
|
3729
|
+
var opts = getExportOpts();
|
|
3715
3730
|
// note: command line "target" option gets ignored
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
return done(e);
|
|
3720
|
-
}
|
|
3721
|
-
internal.writeFiles(files, opts, done);
|
|
3731
|
+
var files = await internal.exportTargetLayers(layers, opts);
|
|
3732
|
+
gui.session.layersExported(getTargetLayerIds(), getExportOptsAsString());
|
|
3733
|
+
await utils$1.promisify(internal.writeFiles)(files, opts);
|
|
3722
3734
|
}
|
|
3723
3735
|
|
|
3724
3736
|
function initLayerItem(o, i) {
|
|
@@ -3838,7 +3850,7 @@
|
|
|
3838
3850
|
}
|
|
3839
3851
|
|
|
3840
3852
|
function initFormatMenu() {
|
|
3841
|
-
var defaults = ['shapefile', 'geojson', 'topojson', 'json', 'dsv', 'kml', 'svg'];
|
|
3853
|
+
var defaults = ['shapefile', 'geojson', 'topojson', 'json', 'dsv', 'kml', 'svg', internal.PACKAGE_EXT];
|
|
3842
3854
|
var formats = utils$1.uniq(defaults.concat(getInputFormats()));
|
|
3843
3855
|
var items = formats.map(function(fmt) {
|
|
3844
3856
|
return utils$1.format('<div><label><input type="radio" name="format" value="%s"' +
|
|
@@ -3966,7 +3978,7 @@
|
|
|
3966
3978
|
clearClass('insert-below');
|
|
3967
3979
|
dragTargetId = layerOrderSlug = null;
|
|
3968
3980
|
if (dragging) {
|
|
3969
|
-
|
|
3981
|
+
renderLayerList(); // in case menu changed...
|
|
3970
3982
|
dragging = false;
|
|
3971
3983
|
}
|
|
3972
3984
|
}
|
|
@@ -4003,6 +4015,32 @@
|
|
|
4003
4015
|
}
|
|
4004
4016
|
|
|
4005
4017
|
function render() {
|
|
4018
|
+
renderLayerList();
|
|
4019
|
+
renderFileList();
|
|
4020
|
+
}
|
|
4021
|
+
|
|
4022
|
+
function renderFileList() {
|
|
4023
|
+
var list = el.findChild('.file-list');
|
|
4024
|
+
var files = [];
|
|
4025
|
+
list.empty();
|
|
4026
|
+
model.forEachLayer(function(lyr, dataset) {
|
|
4027
|
+
var file = internal.getLayerSourceFile(lyr, dataset);
|
|
4028
|
+
if (!file || files.includes(file)) return;
|
|
4029
|
+
files.push(file);
|
|
4030
|
+
var warnings = getWarnings(lyr, dataset);
|
|
4031
|
+
var html = '<div class="layer-item">';
|
|
4032
|
+
html += rowHTML('name', file);
|
|
4033
|
+
if (warnings) {
|
|
4034
|
+
// html += rowHTML('problems', warnings, 'layer-problems');
|
|
4035
|
+
html += rowHTML('', warnings, 'layer-problems');
|
|
4036
|
+
}
|
|
4037
|
+
html += '</div>';
|
|
4038
|
+
list.appendChild(El('div').html(html).firstChild());
|
|
4039
|
+
});
|
|
4040
|
+
|
|
4041
|
+
}
|
|
4042
|
+
|
|
4043
|
+
function renderLayerList() {
|
|
4006
4044
|
var list = el.findChild('.layer-list');
|
|
4007
4045
|
var uniqIds = {};
|
|
4008
4046
|
var pinnableCount = 0;
|
|
@@ -4049,7 +4087,6 @@
|
|
|
4049
4087
|
cache.cleanup();
|
|
4050
4088
|
|
|
4051
4089
|
function renderLayer(lyr, dataset, opts) {
|
|
4052
|
-
var warnings = getWarnings(lyr, dataset);
|
|
4053
4090
|
var classes = 'layer-item';
|
|
4054
4091
|
var entry, html;
|
|
4055
4092
|
|
|
@@ -4059,13 +4096,7 @@
|
|
|
4059
4096
|
|
|
4060
4097
|
html = '<!-- ' + lyr.menu_id + '--><div class="' + classes + '">';
|
|
4061
4098
|
html += rowHTML('name', '<span class="layer-name colored-text dot-underline">' + formatLayerNameForDisplay(lyr.name) + '</span>', 'row1');
|
|
4062
|
-
if (opts.show_source) {
|
|
4063
|
-
html += rowHTML('source file', describeSrc(lyr, dataset) || 'n/a');
|
|
4064
|
-
}
|
|
4065
4099
|
html += rowHTML('contents', describeLyr(lyr));
|
|
4066
|
-
if (warnings) {
|
|
4067
|
-
html += rowHTML('problems', warnings, 'layer-problems');
|
|
4068
|
-
}
|
|
4069
4100
|
html += '<img class="close-btn" draggable="false" src="images/close.png">';
|
|
4070
4101
|
if (opts.pinnable) {
|
|
4071
4102
|
html += '<img class="pin-btn unpinned" draggable="false" src="images/eye.png">';
|
|
@@ -4197,6 +4228,8 @@
|
|
|
4197
4228
|
var file = internal.getLayerSourceFile(lyr, dataset);
|
|
4198
4229
|
var missing = [];
|
|
4199
4230
|
var msg;
|
|
4231
|
+
// show missing file warning for first layer in dataset
|
|
4232
|
+
// (assuming it represents the content of the original file)
|
|
4200
4233
|
if (utils$1.endsWith(file, '.shp') && lyr == dataset.layers[0]) {
|
|
4201
4234
|
if (!lyr.data) {
|
|
4202
4235
|
missing.push('.dbf');
|
|
@@ -5316,6 +5349,27 @@
|
|
|
5316
5349
|
targ.prototype.__super__ = f;
|
|
5317
5350
|
}
|
|
5318
5351
|
|
|
5352
|
+
function promisify(asyncFn) {
|
|
5353
|
+
return function() {
|
|
5354
|
+
var args = toArray(arguments);
|
|
5355
|
+
return new Promise((resolve, reject) => {
|
|
5356
|
+
var cb = function(err, data) {
|
|
5357
|
+
if (err) reject(err);
|
|
5358
|
+
else resolve(data);
|
|
5359
|
+
};
|
|
5360
|
+
args.push(cb);
|
|
5361
|
+
asyncFn.apply(this, args);
|
|
5362
|
+
});
|
|
5363
|
+
};
|
|
5364
|
+
}
|
|
5365
|
+
|
|
5366
|
+
function runAsync(fn, arg) {
|
|
5367
|
+
return new Promise((resolve, reject) => {
|
|
5368
|
+
fn(arg, function(err, data) {
|
|
5369
|
+
return err ? reject(err) : resolve(data);
|
|
5370
|
+
});
|
|
5371
|
+
});
|
|
5372
|
+
}
|
|
5319
5373
|
|
|
5320
5374
|
// Call @iter on each member of an array (similar to Array#reduce(iter))
|
|
5321
5375
|
// iter: function(memo, item, callback)
|
|
@@ -11191,11 +11245,14 @@
|
|
|
11191
11245
|
gui.buttons = new SidebarButtons(gui);
|
|
11192
11246
|
gui.map = new MshpMap(gui);
|
|
11193
11247
|
gui.interaction = new InteractionMode(gui);
|
|
11194
|
-
gui.sessionSave = new SessionSnapshots(gui);
|
|
11195
11248
|
gui.session = new SessionHistory(gui);
|
|
11196
11249
|
gui.undo = new Undo(gui);
|
|
11197
11250
|
gui.state = {};
|
|
11198
11251
|
|
|
11252
|
+
if (opts.saveControl) {
|
|
11253
|
+
new SessionSnapshots(gui);
|
|
11254
|
+
}
|
|
11255
|
+
|
|
11199
11256
|
initModeRules(gui);
|
|
11200
11257
|
|
|
11201
11258
|
gui.showProgressMessage = function(msg) {
|