mapshaper 0.6.15 → 0.6.17
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 +2929 -106
- package/package.json +94 -85
- package/www/elements.css +0 -8
- package/www/index.html +4 -7
- package/www/mapshaper-gui.js +402 -317
- package/www/mapshaper.js +2929 -106
- package/www/modules.js +7542 -1
- package/www/page.css +27 -5
- package/www/codecs.js +0 -64
- package/www/deflate.js +0 -2060
- package/www/pako.deflate.js +0 -2
- package/www/pako.inflate.js +0 -2
- package/www/z-worker.js +0 -153
- package/www/zip.js +0 -969
package/www/mapshaper-gui.js
CHANGED
|
@@ -215,197 +215,6 @@
|
|
|
215
215
|
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
var GUI = {};
|
|
219
|
-
|
|
220
|
-
GUI.isActiveInstance = function(gui) {
|
|
221
|
-
return gui == GUI.__active;
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
GUI.getPixelRatio = function() {
|
|
225
|
-
var deviceRatio = window.devicePixelRatio || window.webkitDevicePixelRatio || 1;
|
|
226
|
-
return deviceRatio > 1 ? 2 : 1;
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
GUI.browserIsSupported = function() {
|
|
230
|
-
return typeof ArrayBuffer != 'undefined' &&
|
|
231
|
-
typeof Blob != 'undefined' && typeof File != 'undefined';
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
GUI.exportIsSupported = function() {
|
|
235
|
-
return typeof URL != 'undefined' && URL.createObjectURL &&
|
|
236
|
-
typeof document.createElement("a").download != "undefined" ||
|
|
237
|
-
!!window.navigator.msSaveBlob;
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
// TODO: make this relative to a single GUI instance
|
|
241
|
-
GUI.canSaveToServer = function() {
|
|
242
|
-
return !!(mapshaper.manifest && mapshaper.manifest.allow_saving) && typeof fetch == 'function';
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
GUI.getUrlVars = function() {
|
|
246
|
-
var q = window.location.search.substring(1);
|
|
247
|
-
return q.split('&').reduce(function(memo, chunk) {
|
|
248
|
-
var pair = chunk.split('=');
|
|
249
|
-
var key = decodeURIComponent(pair[0]);
|
|
250
|
-
memo[key] = parseVal(pair[1]);
|
|
251
|
-
return memo;
|
|
252
|
-
}, {});
|
|
253
|
-
|
|
254
|
-
function parseVal(val) {
|
|
255
|
-
var str = val ? decodeURIComponent(val) : 'true';
|
|
256
|
-
if (str == 'true' || str == 'false') return JSON.parse(str);
|
|
257
|
-
return str;
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
// Assumes that URL path ends with a filename
|
|
262
|
-
GUI.getUrlFilename = function(url) {
|
|
263
|
-
var path = /\/\/([^#?]+)/.exec(url);
|
|
264
|
-
var file = path ? path[1].split('/').pop() : '';
|
|
265
|
-
return file;
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
GUI.formatMessageArgs = function(args) {
|
|
269
|
-
// .replace(/^\[[^\]]+\] ?/, ''); // remove cli annotation (if present)
|
|
270
|
-
return internal.formatLogArgs(args);
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
GUI.handleDirectEvent = function(cb) {
|
|
274
|
-
return function(e) {
|
|
275
|
-
if (e.target == this) cb();
|
|
276
|
-
};
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
GUI.getInputElement = function() {
|
|
280
|
-
var el = document.activeElement;
|
|
281
|
-
return (el && (el.tagName == 'INPUT' || el.contentEditable == 'true')) ? el : null;
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
GUI.textIsSelected = function() {
|
|
285
|
-
return !!GUI.getInputElement();
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
GUI.selectElement = function(el) {
|
|
289
|
-
var range = document.createRange(),
|
|
290
|
-
sel = window.getSelection();
|
|
291
|
-
range.selectNodeContents(el);
|
|
292
|
-
sel.removeAllRanges();
|
|
293
|
-
sel.addRange(range);
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
GUI.blurActiveElement = function() {
|
|
297
|
-
var el = GUI.getInputElement();
|
|
298
|
-
if (el) el.blur();
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
// Filter out delayed click events, e.g. so users can highlight and copy text
|
|
302
|
-
GUI.onClick = function(el, cb) {
|
|
303
|
-
var time;
|
|
304
|
-
el.on('mousedown', function() {
|
|
305
|
-
time = +new Date();
|
|
306
|
-
});
|
|
307
|
-
el.on('mouseup', function(e) {
|
|
308
|
-
if (+new Date() - time < 300) cb(e);
|
|
309
|
-
});
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
// tests if filename is a type that can be used
|
|
313
|
-
GUI.isReadableFileType = function(filename) {
|
|
314
|
-
return !!internal.guessInputFileType(filename) || internal.couldBeDsvFile(filename) ||
|
|
315
|
-
internal.isZipFile(filename);
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
GUI.parseFreeformOptions = function(raw, cmd) {
|
|
319
|
-
var str = raw.trim(),
|
|
320
|
-
parsed;
|
|
321
|
-
if (!str) {
|
|
322
|
-
return {};
|
|
323
|
-
}
|
|
324
|
-
if (!/^-/.test(str)) {
|
|
325
|
-
str = '-' + cmd + ' ' + str;
|
|
326
|
-
}
|
|
327
|
-
parsed = internal.parseCommands(str);
|
|
328
|
-
if (!parsed.length || parsed[0].name != cmd) {
|
|
329
|
-
stop$1("Unable to parse command line options");
|
|
330
|
-
}
|
|
331
|
-
return parsed[0].options;
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
// Convert an options object to a command line options string
|
|
335
|
-
// (used by gui-import-control.js)
|
|
336
|
-
// TODO: handle options with irregular string <-> object conversion
|
|
337
|
-
GUI.formatCommandOptions = function(o) {
|
|
338
|
-
var arr = [];
|
|
339
|
-
Object.keys(o).forEach(function(key) {
|
|
340
|
-
var name = key.replace(/_/g, '-');
|
|
341
|
-
var val = o[key];
|
|
342
|
-
var str;
|
|
343
|
-
// TODO: quote values that contain spaces
|
|
344
|
-
if (Array.isArray(val)) {
|
|
345
|
-
str = name + '=' + val.join(',');
|
|
346
|
-
} else if (val === true) {
|
|
347
|
-
str = name;
|
|
348
|
-
} else if (val === false) {
|
|
349
|
-
return;
|
|
350
|
-
} else {
|
|
351
|
-
str = name + '=' + val;
|
|
352
|
-
}
|
|
353
|
-
arr.push(str);
|
|
354
|
-
});
|
|
355
|
-
return arr.join(' ');
|
|
356
|
-
};
|
|
357
|
-
|
|
358
|
-
// @file: Zip file
|
|
359
|
-
// @cb: function(err, <files>)
|
|
360
|
-
//
|
|
361
|
-
GUI.readZipFile = function(file, cb) {
|
|
362
|
-
var zip = window.zip; // Assume zip.js is loaded and zip is defined globally
|
|
363
|
-
var _files = [];
|
|
364
|
-
zip.createReader(new zip.BlobReader(file), importZipContent, onError);
|
|
365
|
-
|
|
366
|
-
function onError(err) {
|
|
367
|
-
cb(err);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
function onDone() {
|
|
371
|
-
cb(null, _files);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
function importZipContent(reader) {
|
|
375
|
-
var _entries;
|
|
376
|
-
reader.getEntries(readEntries);
|
|
377
|
-
|
|
378
|
-
function readEntries(entries) {
|
|
379
|
-
_entries = entries || [];
|
|
380
|
-
readNext();
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
function readNext() {
|
|
384
|
-
if (_entries.length > 0) {
|
|
385
|
-
readEntry(_entries.pop());
|
|
386
|
-
} else {
|
|
387
|
-
reader.close();
|
|
388
|
-
onDone();
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
function readEntry(entry) {
|
|
393
|
-
var filename = entry.filename,
|
|
394
|
-
isValid = !entry.directory && GUI.isReadableFileType(filename) &&
|
|
395
|
-
!/^__MACOSX/.test(filename); // ignore "resource-fork" files
|
|
396
|
-
if (isValid) {
|
|
397
|
-
entry.getData(new zip.BlobWriter(), function(file) {
|
|
398
|
-
file.name = filename; // Give the Blob a name, like a File object
|
|
399
|
-
_files.push(file);
|
|
400
|
-
readNext();
|
|
401
|
-
});
|
|
402
|
-
} else {
|
|
403
|
-
readNext();
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
};
|
|
408
|
-
|
|
409
218
|
function Handler(type, target, callback, listener, priority) {
|
|
410
219
|
this.type = type;
|
|
411
220
|
this.callback = callback;
|
|
@@ -969,6 +778,146 @@
|
|
|
969
778
|
return this;
|
|
970
779
|
};
|
|
971
780
|
|
|
781
|
+
var GUI = {};
|
|
782
|
+
|
|
783
|
+
GUI.isActiveInstance = function(gui) {
|
|
784
|
+
return gui == GUI.__active;
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
GUI.getPixelRatio = function() {
|
|
788
|
+
var deviceRatio = window.devicePixelRatio || window.webkitDevicePixelRatio || 1;
|
|
789
|
+
return deviceRatio > 1 ? 2 : 1;
|
|
790
|
+
};
|
|
791
|
+
|
|
792
|
+
GUI.browserIsSupported = function() {
|
|
793
|
+
return typeof ArrayBuffer != 'undefined' &&
|
|
794
|
+
typeof Blob != 'undefined' && typeof File != 'undefined';
|
|
795
|
+
};
|
|
796
|
+
|
|
797
|
+
GUI.exportIsSupported = function() {
|
|
798
|
+
return typeof URL != 'undefined' && URL.createObjectURL &&
|
|
799
|
+
typeof document.createElement("a").download != "undefined" ||
|
|
800
|
+
!!window.navigator.msSaveBlob;
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
// TODO: make this relative to a single GUI instance
|
|
804
|
+
GUI.canSaveToServer = function() {
|
|
805
|
+
return !!(mapshaper.manifest && mapshaper.manifest.allow_saving) && typeof fetch == 'function';
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
GUI.getUrlVars = function() {
|
|
809
|
+
var q = window.location.search.substring(1);
|
|
810
|
+
return q.split('&').reduce(function(memo, chunk) {
|
|
811
|
+
var pair = chunk.split('=');
|
|
812
|
+
var key = decodeURIComponent(pair[0]);
|
|
813
|
+
memo[key] = parseVal(pair[1]);
|
|
814
|
+
return memo;
|
|
815
|
+
}, {});
|
|
816
|
+
|
|
817
|
+
function parseVal(val) {
|
|
818
|
+
var str = val ? decodeURIComponent(val) : 'true';
|
|
819
|
+
if (str == 'true' || str == 'false') return JSON.parse(str);
|
|
820
|
+
return str;
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
|
|
824
|
+
// Assumes that URL path ends with a filename
|
|
825
|
+
GUI.getUrlFilename = function(url) {
|
|
826
|
+
var path = /\/\/([^#?]+)/.exec(url);
|
|
827
|
+
var file = path ? path[1].split('/').pop() : '';
|
|
828
|
+
return file;
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
GUI.formatMessageArgs = function(args) {
|
|
832
|
+
// .replace(/^\[[^\]]+\] ?/, ''); // remove cli annotation (if present)
|
|
833
|
+
return internal.formatLogArgs(args);
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
GUI.handleDirectEvent = function(cb) {
|
|
837
|
+
return function(e) {
|
|
838
|
+
if (e.target == this) cb();
|
|
839
|
+
};
|
|
840
|
+
};
|
|
841
|
+
|
|
842
|
+
GUI.getInputElement = function() {
|
|
843
|
+
var el = document.activeElement;
|
|
844
|
+
return (el && (el.tagName == 'INPUT' || el.contentEditable == 'true')) ? el : null;
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
GUI.textIsSelected = function() {
|
|
848
|
+
return !!GUI.getInputElement();
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
GUI.selectElement = function(el) {
|
|
852
|
+
var range = document.createRange(),
|
|
853
|
+
sel = window.getSelection();
|
|
854
|
+
range.selectNodeContents(el);
|
|
855
|
+
sel.removeAllRanges();
|
|
856
|
+
sel.addRange(range);
|
|
857
|
+
};
|
|
858
|
+
|
|
859
|
+
GUI.blurActiveElement = function() {
|
|
860
|
+
var el = GUI.getInputElement();
|
|
861
|
+
if (el) el.blur();
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
// Filter out delayed click events, e.g. so users can highlight and copy text
|
|
865
|
+
GUI.onClick = function(el, cb) {
|
|
866
|
+
var time;
|
|
867
|
+
el.on('mousedown', function() {
|
|
868
|
+
time = +new Date();
|
|
869
|
+
});
|
|
870
|
+
el.on('mouseup', function(e) {
|
|
871
|
+
if (+new Date() - time < 300) cb(e);
|
|
872
|
+
});
|
|
873
|
+
};
|
|
874
|
+
|
|
875
|
+
// tests if filename is a type that can be used
|
|
876
|
+
// GUI.isReadableFileType = function(filename) {
|
|
877
|
+
// return !!internal.guessInputFileType(filename) || internal.couldBeDsvFile(filename) ||
|
|
878
|
+
// internal.isZipFile(filename);
|
|
879
|
+
// };
|
|
880
|
+
|
|
881
|
+
GUI.parseFreeformOptions = function(raw, cmd) {
|
|
882
|
+
var str = raw.trim(),
|
|
883
|
+
parsed;
|
|
884
|
+
if (!str) {
|
|
885
|
+
return {};
|
|
886
|
+
}
|
|
887
|
+
if (!/^-/.test(str)) {
|
|
888
|
+
str = '-' + cmd + ' ' + str;
|
|
889
|
+
}
|
|
890
|
+
parsed = internal.parseCommands(str);
|
|
891
|
+
if (!parsed.length || parsed[0].name != cmd) {
|
|
892
|
+
stop$1("Unable to parse command line options");
|
|
893
|
+
}
|
|
894
|
+
return parsed[0].options;
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
// Convert an options object to a command line options string
|
|
898
|
+
// (used by gui-import-control.js)
|
|
899
|
+
// TODO: handle options with irregular string <-> object conversion
|
|
900
|
+
GUI.formatCommandOptions = function(o) {
|
|
901
|
+
var arr = [];
|
|
902
|
+
Object.keys(o).forEach(function(key) {
|
|
903
|
+
var name = key.replace(/_/g, '-');
|
|
904
|
+
var val = o[key];
|
|
905
|
+
var str;
|
|
906
|
+
// TODO: quote values that contain spaces
|
|
907
|
+
if (Array.isArray(val)) {
|
|
908
|
+
str = name + '=' + val.join(',');
|
|
909
|
+
} else if (val === true) {
|
|
910
|
+
str = name;
|
|
911
|
+
} else if (val === false) {
|
|
912
|
+
return;
|
|
913
|
+
} else {
|
|
914
|
+
str = name + '=' + val;
|
|
915
|
+
}
|
|
916
|
+
arr.push(str);
|
|
917
|
+
});
|
|
918
|
+
return arr.join(' ');
|
|
919
|
+
};
|
|
920
|
+
|
|
972
921
|
// TODO: switch all ClickText to ClickText2
|
|
973
922
|
|
|
974
923
|
// @ref Reference to an element containing a text node
|
|
@@ -1320,6 +1269,7 @@
|
|
|
1320
1269
|
await importFiles(files);
|
|
1321
1270
|
}
|
|
1322
1271
|
} catch(e) {
|
|
1272
|
+
console.log(e);
|
|
1323
1273
|
gui.alert(e.message, 'Import error');
|
|
1324
1274
|
}
|
|
1325
1275
|
if (gui.getMode() == 'import') {
|
|
@@ -1373,7 +1323,7 @@
|
|
|
1373
1323
|
var index = {};
|
|
1374
1324
|
queuedFiles = queuedFiles.concat(files).reduce(function(memo, f) {
|
|
1375
1325
|
// filter out unreadable types and dupes
|
|
1376
|
-
if (
|
|
1326
|
+
if (internal.looksLikeContentFile(f.name) && f.name in index === false) {
|
|
1377
1327
|
index[f.name] = true;
|
|
1378
1328
|
memo.push(f);
|
|
1379
1329
|
}
|
|
@@ -1389,16 +1339,17 @@
|
|
|
1389
1339
|
}
|
|
1390
1340
|
|
|
1391
1341
|
async function receiveFiles(files) {
|
|
1342
|
+
var names = getFileNames(files);
|
|
1392
1343
|
var expanded = [];
|
|
1393
1344
|
try {
|
|
1394
1345
|
expanded = await expandFiles(files);
|
|
1395
1346
|
} catch(e) {
|
|
1347
|
+
console.log(e);
|
|
1396
1348
|
gui.alert(e.message, 'Import error');
|
|
1397
1349
|
return;
|
|
1398
1350
|
}
|
|
1399
1351
|
addFilesToQueue(expanded);
|
|
1400
1352
|
if (queuedFiles.length === 0) {
|
|
1401
|
-
var names = getFileNames(files);
|
|
1402
1353
|
var msg = `Unable to import data from: ${names.join(', ')}`;
|
|
1403
1354
|
gui.alert(msg, 'Import error');
|
|
1404
1355
|
return;
|
|
@@ -1418,24 +1369,29 @@
|
|
|
1418
1369
|
}
|
|
1419
1370
|
|
|
1420
1371
|
async function expandFiles(files) {
|
|
1421
|
-
var
|
|
1372
|
+
var expanded = [], tmp;
|
|
1373
|
+
await wait(35); // pause a beat so status message can display
|
|
1422
1374
|
for (var f of files) {
|
|
1423
|
-
|
|
1424
|
-
|
|
1375
|
+
var data = await readFileData(f);
|
|
1376
|
+
if (internal.isGzipFile(f.name)) {
|
|
1377
|
+
tmp = await readGzipFile(data);
|
|
1378
|
+
} else if (internal.isZipFile(f.name)) {
|
|
1379
|
+
tmp = await readZipFile(data);
|
|
1425
1380
|
} else if (internal.isKmzFile(f.name)) {
|
|
1426
|
-
|
|
1381
|
+
tmp = await readKmzFile(data);
|
|
1427
1382
|
} else {
|
|
1428
|
-
|
|
1383
|
+
tmp = [data];
|
|
1429
1384
|
}
|
|
1430
|
-
|
|
1385
|
+
expanded = expanded.concat(tmp);
|
|
1431
1386
|
}
|
|
1432
|
-
|
|
1387
|
+
files.length = 0; // clear source array for gc (works?)
|
|
1388
|
+
return expanded;
|
|
1433
1389
|
}
|
|
1434
1390
|
|
|
1435
|
-
async function importFiles(
|
|
1436
|
-
var fileData = await readFiles(files);
|
|
1391
|
+
async function importFiles(fileData) {
|
|
1437
1392
|
var importOpts = readImportOpts();
|
|
1438
1393
|
var groups = groupFilesForImport(fileData, importOpts);
|
|
1394
|
+
fileData = null;
|
|
1439
1395
|
for (var group of groups) {
|
|
1440
1396
|
if (group.size > 4e7) {
|
|
1441
1397
|
gui.showProgressMessage('Importing');
|
|
@@ -1485,7 +1441,6 @@
|
|
|
1485
1441
|
});
|
|
1486
1442
|
}
|
|
1487
1443
|
|
|
1488
|
-
|
|
1489
1444
|
function isShapefilePart(name) {
|
|
1490
1445
|
return /\.(shp|shx|dbf|prj|cpg)$/i.test(name);
|
|
1491
1446
|
}
|
|
@@ -1505,13 +1460,7 @@
|
|
|
1505
1460
|
|
|
1506
1461
|
// @file a File object
|
|
1507
1462
|
async function readContentFileAsync(file, cb) {
|
|
1508
|
-
var
|
|
1509
|
-
reader = new FileReader(),
|
|
1510
|
-
useBinary = internal.isSupportedBinaryInputType(name) ||
|
|
1511
|
-
internal.isZipFile(name) ||
|
|
1512
|
-
internal.guessInputFileType(name) == 'json' ||
|
|
1513
|
-
internal.guessInputFileType(name) == 'text';
|
|
1514
|
-
|
|
1463
|
+
var reader = new FileReader();
|
|
1515
1464
|
reader.addEventListener('loadend', function(e) {
|
|
1516
1465
|
if (!reader.result) {
|
|
1517
1466
|
cb(new Error());
|
|
@@ -1519,10 +1468,9 @@
|
|
|
1519
1468
|
cb(null, reader.result);
|
|
1520
1469
|
}
|
|
1521
1470
|
});
|
|
1522
|
-
if (
|
|
1471
|
+
if (internal.isImportableAsBinary(file.name)) {
|
|
1523
1472
|
reader.readAsArrayBuffer(file);
|
|
1524
1473
|
} else {
|
|
1525
|
-
// TODO: consider using "encoding" option, to support CSV files in other encodings than utf8
|
|
1526
1474
|
reader.readAsText(file, 'UTF-8');
|
|
1527
1475
|
}
|
|
1528
1476
|
}
|
|
@@ -1541,7 +1489,8 @@
|
|
|
1541
1489
|
item.url = '/data/' + name;
|
|
1542
1490
|
item.url = item.url.replace('/../', '/~/'); // kludge to allow accessing one parent
|
|
1543
1491
|
}
|
|
1544
|
-
return GUI.isReadableFileType(item.basename) ? item : null;
|
|
1492
|
+
// return GUI.isReadableFileType(item.basename) ? item : null;
|
|
1493
|
+
return internal.looksLikeImportableFile(item.basename) ? item : null;
|
|
1545
1494
|
});
|
|
1546
1495
|
return items.filter(Boolean);
|
|
1547
1496
|
}
|
|
@@ -1593,21 +1542,48 @@
|
|
|
1593
1542
|
return files;
|
|
1594
1543
|
}
|
|
1595
1544
|
|
|
1596
|
-
async function
|
|
1597
|
-
var
|
|
1545
|
+
async function readGzipFile(file) {
|
|
1546
|
+
var name = file.name.replace(/\.gz$/, '');
|
|
1598
1547
|
await wait(35); // pause a beat so status message can display
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1548
|
+
return [{
|
|
1549
|
+
name: name,
|
|
1550
|
+
content: internal.gunzipSync(file.content, name)
|
|
1551
|
+
}];
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
async function readZipFile(file) {
|
|
1555
|
+
// Async is up to twice as fast unzipping large files
|
|
1556
|
+
// var index = internal.unzipSync(file.content);
|
|
1557
|
+
var index = await runAsync(internal.unzipAsync, file.content);
|
|
1558
|
+
return Object.keys(index).reduce(function(memo, filename) {
|
|
1559
|
+
if (!/\.txt$/i.test(filename)) {
|
|
1560
|
+
memo.push({
|
|
1561
|
+
name: filename,
|
|
1562
|
+
content: index[filename]
|
|
1563
|
+
});
|
|
1564
|
+
return memo;
|
|
1565
|
+
}
|
|
1566
|
+
}, []);
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
function fileSize(data) {
|
|
1570
|
+
return data.content.byteLength || data.content.length; // ArrayBuffer or string
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
function fileType(data) {
|
|
1574
|
+
return internal.guessInputType(data.name, data.content);
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
function key(basename, type) {
|
|
1578
|
+
return basename + '.' + type;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
function fileBase(data) {
|
|
1582
|
+
return internal.getFileBase(data.name).toLowerCase();
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
function fileKey(data) {
|
|
1586
|
+
return key(fileBase(data), fileType(data));
|
|
1611
1587
|
}
|
|
1612
1588
|
|
|
1613
1589
|
async function readFileData(file) {
|
|
@@ -1615,10 +1591,7 @@
|
|
|
1615
1591
|
var content = await runAsync(readContentFileAsync, file);
|
|
1616
1592
|
return {
|
|
1617
1593
|
content: content,
|
|
1618
|
-
|
|
1619
|
-
name: file.name,
|
|
1620
|
-
basename: internal.getFileBase(file.name).toLowerCase(),
|
|
1621
|
-
type: internal.guessInputType(file.name, content)
|
|
1594
|
+
name: file.name
|
|
1622
1595
|
};
|
|
1623
1596
|
} catch (e) {
|
|
1624
1597
|
console.error(e);
|
|
@@ -1626,37 +1599,28 @@
|
|
|
1626
1599
|
}
|
|
1627
1600
|
}
|
|
1628
1601
|
|
|
1629
|
-
async function readFiles(files) {
|
|
1630
|
-
var data = [], d;
|
|
1631
|
-
for (var file of files) {
|
|
1632
|
-
d = await readFileData(file);
|
|
1633
|
-
if (d) data.push(d);
|
|
1634
|
-
}
|
|
1635
|
-
return data;
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
1602
|
function groupFilesForImport(data, importOpts) {
|
|
1639
1603
|
var names = importOpts.name ? [importOpts.name] : null;
|
|
1640
1604
|
if (initialImport && opts.name) { // name from mapshaper-gui --name option
|
|
1641
1605
|
names = opts.name.split(',');
|
|
1642
1606
|
}
|
|
1643
1607
|
|
|
1644
|
-
function key(basename, type) {
|
|
1645
|
-
return basename + '.' + type;
|
|
1646
|
-
}
|
|
1647
1608
|
function hasShp(basename) {
|
|
1648
1609
|
var shpKey = key(basename, 'shp');
|
|
1649
|
-
return data.some(d =>
|
|
1610
|
+
return data.some(d => fileKey(d) == shpKey);
|
|
1650
1611
|
}
|
|
1612
|
+
|
|
1651
1613
|
data.forEach(d => {
|
|
1652
|
-
|
|
1653
|
-
|
|
1614
|
+
var basename = fileBase(d);
|
|
1615
|
+
var type = fileType(d);
|
|
1616
|
+
if (type == 'shp' || !isShapefilePart(d.name)) {
|
|
1617
|
+
d.group = key(basename, type);
|
|
1654
1618
|
d.filename = d.name;
|
|
1655
|
-
} else if (hasShp(
|
|
1656
|
-
d.group = key(
|
|
1657
|
-
} else if (
|
|
1619
|
+
} else if (hasShp(basename)) {
|
|
1620
|
+
d.group = key(basename, 'shp');
|
|
1621
|
+
} else if (type == 'dbf') {
|
|
1658
1622
|
d.filename = d.name;
|
|
1659
|
-
d.group = key(
|
|
1623
|
+
d.group = key(basename, 'dbf');
|
|
1660
1624
|
} else {
|
|
1661
1625
|
// shapefile part without a .shp file
|
|
1662
1626
|
d.group = null;
|
|
@@ -1673,8 +1637,8 @@
|
|
|
1673
1637
|
groups.push(g);
|
|
1674
1638
|
index[d.group] = g;
|
|
1675
1639
|
}
|
|
1676
|
-
g.size = (g.size || 0) + d
|
|
1677
|
-
g[d
|
|
1640
|
+
g.size = (g.size || 0) + fileSize(d); // accumulate size
|
|
1641
|
+
g[fileType(d)] = {
|
|
1678
1642
|
filename: d.name,
|
|
1679
1643
|
content: d.content
|
|
1680
1644
|
};
|
|
@@ -1792,42 +1756,97 @@
|
|
|
1792
1756
|
|
|
1793
1757
|
utils$1.inherit(Slider, EventDispatcher);
|
|
1794
1758
|
|
|
1795
|
-
function
|
|
1796
|
-
var
|
|
1797
|
-
var
|
|
1798
|
-
var
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
|
-
|
|
1759
|
+
function showPopupAlert(msg, title) {
|
|
1760
|
+
var self = {}, html = '';
|
|
1761
|
+
var _cancel, _close;
|
|
1762
|
+
var warningRxp = /^Warning: /;
|
|
1763
|
+
var el = El('div').appendTo('body').addClass('error-wrapper');
|
|
1764
|
+
var infoBox = El('div').appendTo(el).addClass('error-box info-box selectable');
|
|
1765
|
+
if (!title && warningRxp.test(msg)) {
|
|
1766
|
+
title = 'Warning';
|
|
1767
|
+
msg = msg.replace(warningRxp, '');
|
|
1768
|
+
}
|
|
1769
|
+
if (title) {
|
|
1770
|
+
html += `<div class="error-title">${title}</div>`;
|
|
1771
|
+
}
|
|
1772
|
+
html += `<p class="error-message">${msg}</p>`;
|
|
1773
|
+
El('div').appendTo(infoBox).addClass('close2-btn').on('click', function() {
|
|
1774
|
+
if (_cancel) _cancel();
|
|
1775
|
+
self.close();
|
|
1776
|
+
});
|
|
1777
|
+
El('div').appendTo(infoBox).addClass('error-content').html(html);
|
|
1807
1778
|
|
|
1808
|
-
function
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
if (err && err.message) {
|
|
1813
|
-
msg = err.message;
|
|
1814
|
-
}
|
|
1815
|
-
if (msg) {
|
|
1816
|
-
str += ": " + msg;
|
|
1817
|
-
}
|
|
1818
|
-
done(str);
|
|
1819
|
-
}
|
|
1779
|
+
self.onCancel = function(cb) {
|
|
1780
|
+
_cancel = cb;
|
|
1781
|
+
return self;
|
|
1782
|
+
};
|
|
1820
1783
|
|
|
1821
|
-
function
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1784
|
+
self.onClose = function(cb) {
|
|
1785
|
+
_close = cb;
|
|
1786
|
+
return self;
|
|
1787
|
+
};
|
|
1788
|
+
|
|
1789
|
+
self.button = function(label, cb) {
|
|
1790
|
+
El('div')
|
|
1791
|
+
.addClass("btn dialog-btn alert-btn")
|
|
1792
|
+
.appendTo(infoBox)
|
|
1793
|
+
.html(label)
|
|
1794
|
+
.on('click', function() {
|
|
1795
|
+
self.close();
|
|
1796
|
+
cb();
|
|
1825
1797
|
});
|
|
1798
|
+
return self;
|
|
1799
|
+
};
|
|
1800
|
+
|
|
1801
|
+
self.close = function() {
|
|
1802
|
+
if (el) el.remove();
|
|
1803
|
+
if (_close) _close();
|
|
1804
|
+
el = _cancel = _close = null;
|
|
1805
|
+
};
|
|
1806
|
+
return self;
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
function AlertControl(gui) {
|
|
1810
|
+
var openAlert; // error popup
|
|
1811
|
+
var openPopup; // any popup
|
|
1812
|
+
|
|
1813
|
+
gui.addMode('alert', function() {}, closePopup);
|
|
1814
|
+
|
|
1815
|
+
gui.alert = function(str, title) {
|
|
1816
|
+
closePopup();
|
|
1817
|
+
openAlert = openPopup = showPopupAlert(str, title);
|
|
1818
|
+
// alert.button('close', gui.clearMode);
|
|
1819
|
+
openAlert.onClose(gui.clearMode);
|
|
1820
|
+
gui.enterMode('alert');
|
|
1821
|
+
};
|
|
1822
|
+
|
|
1823
|
+
gui.message = function(str, title) {
|
|
1824
|
+
if (openPopup) return; // don't stomp on another popup
|
|
1825
|
+
openPopup = showPopupAlert(str, title);
|
|
1826
|
+
openPopup.onClose(function() {openPopup = null;});
|
|
1827
|
+
};
|
|
1828
|
+
|
|
1829
|
+
function closePopup() {
|
|
1830
|
+
if (openPopup) openPopup.close();
|
|
1831
|
+
openPopup = openAlert = null;
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
function saveZipFile(zipfileName, files, done) {
|
|
1836
|
+
internal.zipAsync(files, function(err, buf) {
|
|
1837
|
+
if (err) {
|
|
1838
|
+
done(errorMessage(err));
|
|
1826
1839
|
} else {
|
|
1827
|
-
|
|
1828
|
-
blob = new Blob([obj.content]);
|
|
1829
|
-
zipWriter.add(obj.filename, new zip.BlobReader(blob), nextFile);
|
|
1840
|
+
saveBlobToLocalFile(zipfileName, new Blob([buf]), done);
|
|
1830
1841
|
}
|
|
1842
|
+
});
|
|
1843
|
+
|
|
1844
|
+
function errorMessage(err) {
|
|
1845
|
+
var str = "Error creating Zip file";
|
|
1846
|
+
if (err.message) {
|
|
1847
|
+
str += ": " + err.message;
|
|
1848
|
+
}
|
|
1849
|
+
return str;
|
|
1831
1850
|
}
|
|
1832
1851
|
}
|
|
1833
1852
|
|
|
@@ -1860,12 +1879,91 @@
|
|
|
1860
1879
|
});
|
|
1861
1880
|
}
|
|
1862
1881
|
|
|
1863
|
-
function
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1882
|
+
async function saveBlobToLocalFile(filename, blob, done) {
|
|
1883
|
+
if (window.showSaveFilePicker) {
|
|
1884
|
+
saveBlobToSelectedFile(filename, blob, done);
|
|
1885
|
+
} else {
|
|
1886
|
+
saveBlobToDownloadsFolder(filename, blob, done);
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
function showSaveDialog(filename, blob, done) {
|
|
1891
|
+
showPopupAlert(`Save ${filename} to:`)
|
|
1892
|
+
.button('selected folder', function() {
|
|
1893
|
+
saveBlobToSelectedFile(filename, blob, done);
|
|
1894
|
+
})
|
|
1895
|
+
.button('downloads', function() {
|
|
1896
|
+
saveBlobToDownloadsFolder(filename, blob, done);
|
|
1897
|
+
})
|
|
1898
|
+
.onCancel(done);
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
async function saveBlobToSelectedFile(filename, blob, done) {
|
|
1902
|
+
// see: https://developer.chrome.com/articles/file-system-access/
|
|
1903
|
+
// note: saving multiple files to a directory using showDirectoryPicker()
|
|
1904
|
+
// does not work well (in Chrome). User is prompted for permission each time,
|
|
1905
|
+
// and some directories (like Downloads and Documents) are blocked.
|
|
1906
|
+
//
|
|
1907
|
+
var options = getSaveFileOptions(filename);
|
|
1908
|
+
var handle;
|
|
1909
|
+
try {
|
|
1910
|
+
handle = await window.showSaveFilePicker(options);
|
|
1911
|
+
var writable = await handle.createWritable();
|
|
1912
|
+
await writable.write(blob);
|
|
1913
|
+
await writable.close();
|
|
1914
|
+
} catch(e) {
|
|
1915
|
+
if (e.name == 'SecurityError') {
|
|
1916
|
+
// assuming this is a timeout error, with message like:
|
|
1917
|
+
// "Must be handling a user gesture to show a file picker."
|
|
1918
|
+
showSaveDialog(filename, blob, done);
|
|
1919
|
+
} else if (e.name == 'AbortError') {
|
|
1920
|
+
// fired if user clicks a cancel button (normal, no error message)
|
|
1921
|
+
// BUT: this kind of erro rmay also get fired when saving to a protected folder
|
|
1922
|
+
// (should show error message)
|
|
1923
|
+
done();
|
|
1924
|
+
} else {
|
|
1925
|
+
console.error(e.name, e.message, e);
|
|
1926
|
+
done('Save failed for an unknown reason');
|
|
1927
|
+
}
|
|
1928
|
+
return;
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
done();
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
function getSaveFileOptions(filename) {
|
|
1935
|
+
// see: https://wicg.github.io/file-system-access/#api-filepickeroptions
|
|
1936
|
+
var type = internal.guessInputFileType(filename);
|
|
1937
|
+
var ext = internal.getFileExtension(filename).toLowerCase();
|
|
1938
|
+
var accept = {};
|
|
1939
|
+
if (ext == 'kml') {
|
|
1940
|
+
accept['application/vnd.google-earth.kml+xml'] = ['.kml'];
|
|
1941
|
+
} else if (ext == 'svg') {
|
|
1942
|
+
accept['image/svg+xml'] = ['.svg'];
|
|
1943
|
+
} else if (ext == 'zip') {
|
|
1944
|
+
accept['application/zip'] == ['.zip'];
|
|
1945
|
+
} else if (type == 'text') {
|
|
1946
|
+
accept['text/csv'] = ['.csv', '.tsv', '.tab', '.txt'];
|
|
1947
|
+
} else if (type == 'json') {
|
|
1948
|
+
accept['application/json'] = ['.json', '.geojson', '.topojson'];
|
|
1949
|
+
} else {
|
|
1950
|
+
accept['application/octet-stream'] = ['.' + ext];
|
|
1868
1951
|
}
|
|
1952
|
+
return {
|
|
1953
|
+
suggestedName: filename,
|
|
1954
|
+
// If startIn is given, Chrome will always start there
|
|
1955
|
+
// Default is to start in the previously selected dir (better)
|
|
1956
|
+
// // startIn: 'downloads', // or: desktop, documents, [file handle], [directory handle]
|
|
1957
|
+
types: [{
|
|
1958
|
+
description: 'Files',
|
|
1959
|
+
accept: accept
|
|
1960
|
+
}]
|
|
1961
|
+
};
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
|
|
1965
|
+
function saveBlobToDownloadsFolder(filename, blob, done) {
|
|
1966
|
+
var anchor, blobUrl;
|
|
1869
1967
|
try {
|
|
1870
1968
|
blobUrl = URL.createObjectURL(blob);
|
|
1871
1969
|
} catch(e) {
|
|
@@ -1901,6 +1999,8 @@
|
|
|
1901
1999
|
}
|
|
1902
2000
|
|
|
1903
2001
|
function message() {
|
|
2002
|
+
var msg = GUI.formatMessageArgs(arguments);
|
|
2003
|
+
gui.message(msg);
|
|
1904
2004
|
internal.logArgs(arguments);
|
|
1905
2005
|
}
|
|
1906
2006
|
|
|
@@ -1932,7 +2032,7 @@
|
|
|
1932
2032
|
}
|
|
1933
2033
|
});
|
|
1934
2034
|
} else if (files.length == 1) {
|
|
1935
|
-
|
|
2035
|
+
saveBlobToLocalFile(files[0].filename, new Blob([files[0].content]), done);
|
|
1936
2036
|
} else {
|
|
1937
2037
|
filename = internal.getCommonFileBase(utils$1.pluck(files, 'filename')) || "output";
|
|
1938
2038
|
saveZipFile(filename + ".zip", files, done);
|
|
@@ -3010,34 +3110,6 @@
|
|
|
3010
3110
|
|
|
3011
3111
|
}
|
|
3012
3112
|
|
|
3013
|
-
function AlertControl(gui) {
|
|
3014
|
-
var el;
|
|
3015
|
-
gui.addMode('alert', function() {}, turnOff);
|
|
3016
|
-
|
|
3017
|
-
gui.alert = function(str, title) {
|
|
3018
|
-
var infoBox, html = '';
|
|
3019
|
-
if (!el) {
|
|
3020
|
-
el = El('div').appendTo('body').addClass('error-wrapper');
|
|
3021
|
-
infoBox = El('div').appendTo(el).addClass('error-box info-box selectable');
|
|
3022
|
-
El('div').appendTo(infoBox).addClass('error-content');
|
|
3023
|
-
El('div').addClass("btn dialog-btn").appendTo(infoBox).html('close').on('click', gui.clearMode);
|
|
3024
|
-
}
|
|
3025
|
-
if (title) {
|
|
3026
|
-
html += `<div class="error-title">${title}</div>`;
|
|
3027
|
-
}
|
|
3028
|
-
html += `<p class="error-message">${str}</p>`;
|
|
3029
|
-
el.findChild('.error-content').html(html);
|
|
3030
|
-
gui.enterMode('alert');
|
|
3031
|
-
};
|
|
3032
|
-
|
|
3033
|
-
function turnOff() {
|
|
3034
|
-
if (el) {
|
|
3035
|
-
el.remove();
|
|
3036
|
-
el = null;
|
|
3037
|
-
}
|
|
3038
|
-
}
|
|
3039
|
-
}
|
|
3040
|
-
|
|
3041
3113
|
function RepairControl(gui) {
|
|
3042
3114
|
var map = gui.map,
|
|
3043
3115
|
model = gui.model,
|
|
@@ -3221,6 +3293,7 @@
|
|
|
3221
3293
|
|
|
3222
3294
|
function turnOn() {
|
|
3223
3295
|
layersArr = initLayerMenu();
|
|
3296
|
+
// initZipOption();
|
|
3224
3297
|
initFormatMenu();
|
|
3225
3298
|
menu.show();
|
|
3226
3299
|
}
|
|
@@ -3269,6 +3342,9 @@
|
|
|
3269
3342
|
if (/format=/.test(freeform) === false) {
|
|
3270
3343
|
freeform += ' format=' + getSelectedFormat();
|
|
3271
3344
|
}
|
|
3345
|
+
if (getZipOption()) {
|
|
3346
|
+
freeform += ' zip';
|
|
3347
|
+
}
|
|
3272
3348
|
return freeform.trim();
|
|
3273
3349
|
}
|
|
3274
3350
|
|
|
@@ -3413,10 +3489,19 @@
|
|
|
3413
3489
|
menu.findChild('.export-formats input[value="' + getDefaultExportFormat() + '"]').node().checked = true;
|
|
3414
3490
|
}
|
|
3415
3491
|
|
|
3492
|
+
function initZipOption() {
|
|
3493
|
+
var html = `<label><input type="checkbox">Save to .zip file</label>`;
|
|
3494
|
+
menu.findChild('.export-zip-option').html(html);
|
|
3495
|
+
}
|
|
3496
|
+
|
|
3416
3497
|
function getSelectedFormat() {
|
|
3417
3498
|
return menu.findChild('.export-formats input:checked').node().value;
|
|
3418
3499
|
}
|
|
3419
3500
|
|
|
3501
|
+
function getZipOption() {
|
|
3502
|
+
return !!menu.findChild('.export-zip-option input:checked');
|
|
3503
|
+
}
|
|
3504
|
+
|
|
3420
3505
|
function getTargetLayerIds() {
|
|
3421
3506
|
return layersArr.reduce(function(memo, o, i) {
|
|
3422
3507
|
if (o.checkbox.checked) memo.push(o.checkbox.value);
|