mapshaper 0.6.14 → 0.6.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/mapshaper.js +3140 -192
- package/package.json +11 -7
- package/www/elements.css +0 -8
- package/www/index.html +4 -7
- package/www/mapshaper-gui.js +410 -316
- package/www/mapshaper.js +3140 -192
- package/www/modules.js +8446 -3
- 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-force" 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,22 +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
|
-
|
|
1425
|
-
|
|
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);
|
|
1380
|
+
} else if (internal.isKmzFile(f.name)) {
|
|
1381
|
+
tmp = await readKmzFile(data);
|
|
1426
1382
|
} else {
|
|
1427
|
-
|
|
1383
|
+
tmp = [data];
|
|
1428
1384
|
}
|
|
1385
|
+
expanded = expanded.concat(tmp);
|
|
1429
1386
|
}
|
|
1430
|
-
|
|
1387
|
+
files.length = 0; // clear source array for gc (works?)
|
|
1388
|
+
return expanded;
|
|
1431
1389
|
}
|
|
1432
1390
|
|
|
1433
|
-
async function importFiles(
|
|
1434
|
-
var fileData = await readFiles(files);
|
|
1391
|
+
async function importFiles(fileData) {
|
|
1435
1392
|
var importOpts = readImportOpts();
|
|
1436
1393
|
var groups = groupFilesForImport(fileData, importOpts);
|
|
1394
|
+
fileData = null;
|
|
1437
1395
|
for (var group of groups) {
|
|
1438
1396
|
if (group.size > 4e7) {
|
|
1439
1397
|
gui.showProgressMessage('Importing');
|
|
@@ -1483,7 +1441,6 @@
|
|
|
1483
1441
|
});
|
|
1484
1442
|
}
|
|
1485
1443
|
|
|
1486
|
-
|
|
1487
1444
|
function isShapefilePart(name) {
|
|
1488
1445
|
return /\.(shp|shx|dbf|prj|cpg)$/i.test(name);
|
|
1489
1446
|
}
|
|
@@ -1503,13 +1460,7 @@
|
|
|
1503
1460
|
|
|
1504
1461
|
// @file a File object
|
|
1505
1462
|
async function readContentFileAsync(file, cb) {
|
|
1506
|
-
var
|
|
1507
|
-
reader = new FileReader(),
|
|
1508
|
-
useBinary = internal.isSupportedBinaryInputType(name) ||
|
|
1509
|
-
internal.isZipFile(name) ||
|
|
1510
|
-
internal.guessInputFileType(name) == 'json' ||
|
|
1511
|
-
internal.guessInputFileType(name) == 'text';
|
|
1512
|
-
|
|
1463
|
+
var reader = new FileReader();
|
|
1513
1464
|
reader.addEventListener('loadend', function(e) {
|
|
1514
1465
|
if (!reader.result) {
|
|
1515
1466
|
cb(new Error());
|
|
@@ -1517,10 +1468,9 @@
|
|
|
1517
1468
|
cb(null, reader.result);
|
|
1518
1469
|
}
|
|
1519
1470
|
});
|
|
1520
|
-
if (
|
|
1471
|
+
if (internal.isImportableAsBinary(file.name)) {
|
|
1521
1472
|
reader.readAsArrayBuffer(file);
|
|
1522
1473
|
} else {
|
|
1523
|
-
// TODO: consider using "encoding" option, to support CSV files in other encodings than utf8
|
|
1524
1474
|
reader.readAsText(file, 'UTF-8');
|
|
1525
1475
|
}
|
|
1526
1476
|
}
|
|
@@ -1539,7 +1489,8 @@
|
|
|
1539
1489
|
item.url = '/data/' + name;
|
|
1540
1490
|
item.url = item.url.replace('/../', '/~/'); // kludge to allow accessing one parent
|
|
1541
1491
|
}
|
|
1542
|
-
return GUI.isReadableFileType(item.basename) ? item : null;
|
|
1492
|
+
// return GUI.isReadableFileType(item.basename) ? item : null;
|
|
1493
|
+
return internal.looksLikeImportableFile(item.basename) ? item : null;
|
|
1543
1494
|
});
|
|
1544
1495
|
return items.filter(Boolean);
|
|
1545
1496
|
}
|
|
@@ -1582,32 +1533,63 @@
|
|
|
1582
1533
|
});
|
|
1583
1534
|
}
|
|
1584
1535
|
|
|
1585
|
-
async function
|
|
1586
|
-
var files =
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
files =
|
|
1590
|
-
// don't try to import .txt files from zip files
|
|
1591
|
-
// (these would be parsed as dsv and throw errows)
|
|
1592
|
-
files = files.filter(function(f) {
|
|
1593
|
-
return !/\.txt$/i.test(f.name);
|
|
1594
|
-
});
|
|
1595
|
-
} catch(e) {
|
|
1596
|
-
console.error(e);
|
|
1597
|
-
throw Error(`Unable to unzip ${file.name}`);
|
|
1536
|
+
async function readKmzFile(file) {
|
|
1537
|
+
var files = await readZipFile(file);
|
|
1538
|
+
var name = files[0] && files[0].name;
|
|
1539
|
+
if (name == 'doc.kml') {
|
|
1540
|
+
files[0].name = internal.replaceFileExtension(file.name, 'kml');
|
|
1598
1541
|
}
|
|
1599
1542
|
return files;
|
|
1600
1543
|
}
|
|
1601
1544
|
|
|
1545
|
+
async function readGzipFile(file) {
|
|
1546
|
+
var name = file.name.replace(/\.gz$/, '');
|
|
1547
|
+
await wait(35); // pause a beat so status message can display
|
|
1548
|
+
return [{
|
|
1549
|
+
name: name,
|
|
1550
|
+
content: internal.gunzipSync(file.content, name)
|
|
1551
|
+
}];
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
async function readZipFile(file) {
|
|
1555
|
+
var index = internal.unzipSync(file.content);
|
|
1556
|
+
return Object.keys(index).reduce(function(memo, filename) {
|
|
1557
|
+
if (!/\.txt$/i.test(filename)) {
|
|
1558
|
+
memo.push({
|
|
1559
|
+
name: filename,
|
|
1560
|
+
content: index[filename]
|
|
1561
|
+
});
|
|
1562
|
+
return memo;
|
|
1563
|
+
}
|
|
1564
|
+
}, []);
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
function fileSize(data) {
|
|
1568
|
+
return data.content.byteLength || data.content.length; // ArrayBuffer or string
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
function fileType(data) {
|
|
1572
|
+
return internal.guessInputType(data.name, data.content);
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
function key(basename, type) {
|
|
1576
|
+
return basename + '.' + type;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
function fileBase(data) {
|
|
1580
|
+
return internal.getFileBase(data.name).toLowerCase();
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
function fileKey(data) {
|
|
1584
|
+
return key(fileBase(data), fileType(data));
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1602
1587
|
async function readFileData(file) {
|
|
1603
1588
|
try {
|
|
1604
1589
|
var content = await runAsync(readContentFileAsync, file);
|
|
1605
1590
|
return {
|
|
1606
1591
|
content: content,
|
|
1607
|
-
|
|
1608
|
-
name: file.name,
|
|
1609
|
-
basename: internal.getFileBase(file.name).toLowerCase(),
|
|
1610
|
-
type: internal.guessInputType(file.name, content)
|
|
1592
|
+
name: file.name
|
|
1611
1593
|
};
|
|
1612
1594
|
} catch (e) {
|
|
1613
1595
|
console.error(e);
|
|
@@ -1615,37 +1597,28 @@
|
|
|
1615
1597
|
}
|
|
1616
1598
|
}
|
|
1617
1599
|
|
|
1618
|
-
async function readFiles(files) {
|
|
1619
|
-
var data = [], d;
|
|
1620
|
-
for (var file of files) {
|
|
1621
|
-
d = await readFileData(file);
|
|
1622
|
-
if (d) data.push(d);
|
|
1623
|
-
}
|
|
1624
|
-
return data;
|
|
1625
|
-
}
|
|
1626
|
-
|
|
1627
1600
|
function groupFilesForImport(data, importOpts) {
|
|
1628
1601
|
var names = importOpts.name ? [importOpts.name] : null;
|
|
1629
1602
|
if (initialImport && opts.name) { // name from mapshaper-gui --name option
|
|
1630
1603
|
names = opts.name.split(',');
|
|
1631
1604
|
}
|
|
1632
1605
|
|
|
1633
|
-
function key(basename, type) {
|
|
1634
|
-
return basename + '.' + type;
|
|
1635
|
-
}
|
|
1636
1606
|
function hasShp(basename) {
|
|
1637
1607
|
var shpKey = key(basename, 'shp');
|
|
1638
|
-
return data.some(d =>
|
|
1608
|
+
return data.some(d => fileKey(d) == shpKey);
|
|
1639
1609
|
}
|
|
1610
|
+
|
|
1640
1611
|
data.forEach(d => {
|
|
1641
|
-
|
|
1642
|
-
|
|
1612
|
+
var basename = fileBase(d);
|
|
1613
|
+
var type = fileType(d);
|
|
1614
|
+
if (type == 'shp' || !isShapefilePart(d.name)) {
|
|
1615
|
+
d.group = key(basename, type);
|
|
1643
1616
|
d.filename = d.name;
|
|
1644
|
-
} else if (hasShp(
|
|
1645
|
-
d.group = key(
|
|
1646
|
-
} else if (
|
|
1617
|
+
} else if (hasShp(basename)) {
|
|
1618
|
+
d.group = key(basename, 'shp');
|
|
1619
|
+
} else if (type == 'dbf') {
|
|
1647
1620
|
d.filename = d.name;
|
|
1648
|
-
d.group = key(
|
|
1621
|
+
d.group = key(basename, 'dbf');
|
|
1649
1622
|
} else {
|
|
1650
1623
|
// shapefile part without a .shp file
|
|
1651
1624
|
d.group = null;
|
|
@@ -1662,8 +1635,8 @@
|
|
|
1662
1635
|
groups.push(g);
|
|
1663
1636
|
index[d.group] = g;
|
|
1664
1637
|
}
|
|
1665
|
-
g.size = (g.size || 0) + d
|
|
1666
|
-
g[d
|
|
1638
|
+
g.size = (g.size || 0) + fileSize(d); // accumulate size
|
|
1639
|
+
g[fileType(d)] = {
|
|
1667
1640
|
filename: d.name,
|
|
1668
1641
|
content: d.content
|
|
1669
1642
|
};
|
|
@@ -1781,42 +1754,97 @@
|
|
|
1781
1754
|
|
|
1782
1755
|
utils$1.inherit(Slider, EventDispatcher);
|
|
1783
1756
|
|
|
1784
|
-
function
|
|
1785
|
-
var
|
|
1786
|
-
var
|
|
1787
|
-
var
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
}
|
|
1794
|
-
|
|
1795
|
-
|
|
1757
|
+
function showPopupAlert(msg, title) {
|
|
1758
|
+
var self = {}, html = '';
|
|
1759
|
+
var _cancel, _close;
|
|
1760
|
+
var warningRxp = /^Warning: /;
|
|
1761
|
+
var el = El('div').appendTo('body').addClass('error-wrapper');
|
|
1762
|
+
var infoBox = El('div').appendTo(el).addClass('error-box info-box selectable');
|
|
1763
|
+
if (!title && warningRxp.test(msg)) {
|
|
1764
|
+
title = 'Warning';
|
|
1765
|
+
msg = msg.replace(warningRxp, '');
|
|
1766
|
+
}
|
|
1767
|
+
if (title) {
|
|
1768
|
+
html += `<div class="error-title">${title}</div>`;
|
|
1769
|
+
}
|
|
1770
|
+
html += `<p class="error-message">${msg}</p>`;
|
|
1771
|
+
El('div').appendTo(infoBox).addClass('close2-btn').on('click', function() {
|
|
1772
|
+
if (_cancel) _cancel();
|
|
1773
|
+
self.close();
|
|
1774
|
+
});
|
|
1775
|
+
El('div').appendTo(infoBox).addClass('error-content').html(html);
|
|
1796
1776
|
|
|
1797
|
-
function
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
}
|
|
1807
|
-
done(str);
|
|
1808
|
-
}
|
|
1777
|
+
self.onCancel = function(cb) {
|
|
1778
|
+
_cancel = cb;
|
|
1779
|
+
return self;
|
|
1780
|
+
};
|
|
1781
|
+
|
|
1782
|
+
self.onClose = function(cb) {
|
|
1783
|
+
_close = cb;
|
|
1784
|
+
return self;
|
|
1785
|
+
};
|
|
1809
1786
|
|
|
1810
|
-
function
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1787
|
+
self.button = function(label, cb) {
|
|
1788
|
+
El('div')
|
|
1789
|
+
.addClass("btn dialog-btn alert-btn")
|
|
1790
|
+
.appendTo(infoBox)
|
|
1791
|
+
.html(label)
|
|
1792
|
+
.on('click', function() {
|
|
1793
|
+
self.close();
|
|
1794
|
+
cb();
|
|
1814
1795
|
});
|
|
1796
|
+
return self;
|
|
1797
|
+
};
|
|
1798
|
+
|
|
1799
|
+
self.close = function() {
|
|
1800
|
+
if (el) el.remove();
|
|
1801
|
+
if (_close) _close();
|
|
1802
|
+
el = _cancel = _close = null;
|
|
1803
|
+
};
|
|
1804
|
+
return self;
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
function AlertControl(gui) {
|
|
1808
|
+
var openAlert; // error popup
|
|
1809
|
+
var openPopup; // any popup
|
|
1810
|
+
|
|
1811
|
+
gui.addMode('alert', function() {}, closePopup);
|
|
1812
|
+
|
|
1813
|
+
gui.alert = function(str, title) {
|
|
1814
|
+
closePopup();
|
|
1815
|
+
openAlert = openPopup = showPopupAlert(str, title);
|
|
1816
|
+
// alert.button('close', gui.clearMode);
|
|
1817
|
+
openAlert.onClose(gui.clearMode);
|
|
1818
|
+
gui.enterMode('alert');
|
|
1819
|
+
};
|
|
1820
|
+
|
|
1821
|
+
gui.message = function(str, title) {
|
|
1822
|
+
if (openPopup) return; // don't stomp on another popup
|
|
1823
|
+
openPopup = showPopupAlert(str, title);
|
|
1824
|
+
openPopup.onClose(function() {openPopup = null;});
|
|
1825
|
+
};
|
|
1826
|
+
|
|
1827
|
+
function closePopup() {
|
|
1828
|
+
if (openPopup) openPopup.close();
|
|
1829
|
+
openPopup = openAlert = null;
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
function saveZipFile(zipfileName, files, done) {
|
|
1834
|
+
internal.zipAsync(files, function(err, buf) {
|
|
1835
|
+
if (err) {
|
|
1836
|
+
done(errorMessage(err));
|
|
1815
1837
|
} else {
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1838
|
+
saveBlobToLocalFile(zipfileName, new Blob([buf]), done);
|
|
1839
|
+
}
|
|
1840
|
+
});
|
|
1841
|
+
|
|
1842
|
+
function errorMessage(err) {
|
|
1843
|
+
var str = "Error creating Zip file";
|
|
1844
|
+
if (err.message) {
|
|
1845
|
+
str += ": " + err.message;
|
|
1819
1846
|
}
|
|
1847
|
+
return str;
|
|
1820
1848
|
}
|
|
1821
1849
|
}
|
|
1822
1850
|
|
|
@@ -1849,12 +1877,91 @@
|
|
|
1849
1877
|
});
|
|
1850
1878
|
}
|
|
1851
1879
|
|
|
1852
|
-
function
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1880
|
+
async function saveBlobToLocalFile(filename, blob, done) {
|
|
1881
|
+
if (window.showSaveFilePicker) {
|
|
1882
|
+
saveBlobToSelectedFile(filename, blob, done);
|
|
1883
|
+
} else {
|
|
1884
|
+
saveBlobToDownloadsFolder(filename, blob, done);
|
|
1857
1885
|
}
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
function showSaveDialog(filename, blob, done) {
|
|
1889
|
+
showPopupAlert(`Save ${filename} to:`)
|
|
1890
|
+
.button('selected folder', function() {
|
|
1891
|
+
saveBlobToSelectedFile(filename, blob, done);
|
|
1892
|
+
})
|
|
1893
|
+
.button('downloads', function() {
|
|
1894
|
+
saveBlobToDownloadsFolder(filename, blob, done);
|
|
1895
|
+
})
|
|
1896
|
+
.onCancel(done);
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
async function saveBlobToSelectedFile(filename, blob, done) {
|
|
1900
|
+
// see: https://developer.chrome.com/articles/file-system-access/
|
|
1901
|
+
// note: saving multiple files to a directory using showDirectoryPicker()
|
|
1902
|
+
// does not work well (in Chrome). User is prompted for permission each time,
|
|
1903
|
+
// and some directories (like Downloads and Documents) are blocked.
|
|
1904
|
+
//
|
|
1905
|
+
var options = getSaveFileOptions(filename);
|
|
1906
|
+
var handle;
|
|
1907
|
+
try {
|
|
1908
|
+
handle = await window.showSaveFilePicker(options);
|
|
1909
|
+
var writable = await handle.createWritable();
|
|
1910
|
+
await writable.write(blob);
|
|
1911
|
+
await writable.close();
|
|
1912
|
+
} catch(e) {
|
|
1913
|
+
if (e.name == 'SecurityError') {
|
|
1914
|
+
// assuming this is a timeout error, with message like:
|
|
1915
|
+
// "Must be handling a user gesture to show a file picker."
|
|
1916
|
+
showSaveDialog(filename, blob, done);
|
|
1917
|
+
} else if (e.name == 'AbortError') {
|
|
1918
|
+
// fired if user clicks a cancel button (normal, no error message)
|
|
1919
|
+
// BUT: this kind of erro rmay also get fired when saving to a protected folder
|
|
1920
|
+
// (should show error message)
|
|
1921
|
+
done();
|
|
1922
|
+
} else {
|
|
1923
|
+
console.error(e.name, e.message, e);
|
|
1924
|
+
done('Save failed for an unknown reason');
|
|
1925
|
+
}
|
|
1926
|
+
return;
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
done();
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
function getSaveFileOptions(filename) {
|
|
1933
|
+
// see: https://wicg.github.io/file-system-access/#api-filepickeroptions
|
|
1934
|
+
var type = internal.guessInputFileType(filename);
|
|
1935
|
+
var ext = internal.getFileExtension(filename).toLowerCase();
|
|
1936
|
+
var accept = {};
|
|
1937
|
+
if (ext == 'kml') {
|
|
1938
|
+
accept['application/vnd.google-earth.kml+xml'] = ['.kml'];
|
|
1939
|
+
} else if (ext == 'svg') {
|
|
1940
|
+
accept['image/svg+xml'] = ['.svg'];
|
|
1941
|
+
} else if (ext == 'zip') {
|
|
1942
|
+
accept['application/zip'] == ['.zip'];
|
|
1943
|
+
} else if (type == 'text') {
|
|
1944
|
+
accept['text/csv'] = ['.csv', '.tsv', '.tab', '.txt'];
|
|
1945
|
+
} else if (type == 'json') {
|
|
1946
|
+
accept['application/json'] = ['.json', '.geojson', '.topojson'];
|
|
1947
|
+
} else {
|
|
1948
|
+
accept['application/octet-stream'] = ['.' + ext];
|
|
1949
|
+
}
|
|
1950
|
+
return {
|
|
1951
|
+
suggestedName: filename,
|
|
1952
|
+
// If startIn is given, Chrome will always start there
|
|
1953
|
+
// Default is to start in the previously selected dir (better)
|
|
1954
|
+
// // startIn: 'downloads', // or: desktop, documents, [file handle], [directory handle]
|
|
1955
|
+
types: [{
|
|
1956
|
+
description: 'Files',
|
|
1957
|
+
accept: accept
|
|
1958
|
+
}]
|
|
1959
|
+
};
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
|
|
1963
|
+
function saveBlobToDownloadsFolder(filename, blob, done) {
|
|
1964
|
+
var anchor, blobUrl;
|
|
1858
1965
|
try {
|
|
1859
1966
|
blobUrl = URL.createObjectURL(blob);
|
|
1860
1967
|
} catch(e) {
|
|
@@ -1890,6 +1997,8 @@
|
|
|
1890
1997
|
}
|
|
1891
1998
|
|
|
1892
1999
|
function message() {
|
|
2000
|
+
var msg = GUI.formatMessageArgs(arguments);
|
|
2001
|
+
gui.message(msg);
|
|
1893
2002
|
internal.logArgs(arguments);
|
|
1894
2003
|
}
|
|
1895
2004
|
|
|
@@ -1921,7 +2030,7 @@
|
|
|
1921
2030
|
}
|
|
1922
2031
|
});
|
|
1923
2032
|
} else if (files.length == 1) {
|
|
1924
|
-
|
|
2033
|
+
saveBlobToLocalFile(files[0].filename, new Blob([files[0].content]), done);
|
|
1925
2034
|
} else {
|
|
1926
2035
|
filename = internal.getCommonFileBase(utils$1.pluck(files, 'filename')) || "output";
|
|
1927
2036
|
saveZipFile(filename + ".zip", files, done);
|
|
@@ -2999,34 +3108,6 @@
|
|
|
2999
3108
|
|
|
3000
3109
|
}
|
|
3001
3110
|
|
|
3002
|
-
function AlertControl(gui) {
|
|
3003
|
-
var el;
|
|
3004
|
-
gui.addMode('alert', function() {}, turnOff);
|
|
3005
|
-
|
|
3006
|
-
gui.alert = function(str, title) {
|
|
3007
|
-
var infoBox, html = '';
|
|
3008
|
-
if (!el) {
|
|
3009
|
-
el = El('div').appendTo('body').addClass('error-wrapper');
|
|
3010
|
-
infoBox = El('div').appendTo(el).addClass('error-box info-box selectable');
|
|
3011
|
-
El('div').appendTo(infoBox).addClass('error-content');
|
|
3012
|
-
El('div').addClass("btn dialog-btn").appendTo(infoBox).html('close').on('click', gui.clearMode);
|
|
3013
|
-
}
|
|
3014
|
-
if (title) {
|
|
3015
|
-
html += `<div class="error-title">${title}</div>`;
|
|
3016
|
-
}
|
|
3017
|
-
html += `<p class="error-message">${str}</p>`;
|
|
3018
|
-
el.findChild('.error-content').html(html);
|
|
3019
|
-
gui.enterMode('alert');
|
|
3020
|
-
};
|
|
3021
|
-
|
|
3022
|
-
function turnOff() {
|
|
3023
|
-
if (el) {
|
|
3024
|
-
el.remove();
|
|
3025
|
-
el = null;
|
|
3026
|
-
}
|
|
3027
|
-
}
|
|
3028
|
-
}
|
|
3029
|
-
|
|
3030
3111
|
function RepairControl(gui) {
|
|
3031
3112
|
var map = gui.map,
|
|
3032
3113
|
model = gui.model,
|
|
@@ -3210,6 +3291,7 @@
|
|
|
3210
3291
|
|
|
3211
3292
|
function turnOn() {
|
|
3212
3293
|
layersArr = initLayerMenu();
|
|
3294
|
+
// initZipOption();
|
|
3213
3295
|
initFormatMenu();
|
|
3214
3296
|
menu.show();
|
|
3215
3297
|
}
|
|
@@ -3258,6 +3340,9 @@
|
|
|
3258
3340
|
if (/format=/.test(freeform) === false) {
|
|
3259
3341
|
freeform += ' format=' + getSelectedFormat();
|
|
3260
3342
|
}
|
|
3343
|
+
if (getZipOption()) {
|
|
3344
|
+
freeform += ' zip';
|
|
3345
|
+
}
|
|
3261
3346
|
return freeform.trim();
|
|
3262
3347
|
}
|
|
3263
3348
|
|
|
@@ -3392,7 +3477,7 @@
|
|
|
3392
3477
|
}
|
|
3393
3478
|
|
|
3394
3479
|
function initFormatMenu() {
|
|
3395
|
-
var defaults = ['shapefile', 'geojson', 'topojson', 'json', 'dsv', 'svg'];
|
|
3480
|
+
var defaults = ['shapefile', 'geojson', 'topojson', 'json', 'dsv', 'kml', 'svg'];
|
|
3396
3481
|
var formats = utils$1.uniq(defaults.concat(getInputFormats()));
|
|
3397
3482
|
var items = formats.map(function(fmt) {
|
|
3398
3483
|
return utils$1.format('<div><label><input type="radio" name="format" value="%s"' +
|
|
@@ -3402,10 +3487,19 @@
|
|
|
3402
3487
|
menu.findChild('.export-formats input[value="' + getDefaultExportFormat() + '"]').node().checked = true;
|
|
3403
3488
|
}
|
|
3404
3489
|
|
|
3490
|
+
function initZipOption() {
|
|
3491
|
+
var html = `<label><input type="checkbox">Save to .zip file</label>`;
|
|
3492
|
+
menu.findChild('.export-zip-option').html(html);
|
|
3493
|
+
}
|
|
3494
|
+
|
|
3405
3495
|
function getSelectedFormat() {
|
|
3406
3496
|
return menu.findChild('.export-formats input:checked').node().value;
|
|
3407
3497
|
}
|
|
3408
3498
|
|
|
3499
|
+
function getZipOption() {
|
|
3500
|
+
return !!menu.findChild('.export-zip-option input:checked');
|
|
3501
|
+
}
|
|
3502
|
+
|
|
3409
3503
|
function getTargetLayerIds() {
|
|
3410
3504
|
return layersArr.reduce(function(memo, o, i) {
|
|
3411
3505
|
if (o.checkbox.checked) memo.push(o.checkbox.value);
|