mapshaper 0.6.117 → 0.6.118

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.6.117",
3
+ "version": "0.6.118",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
package/www/index.html CHANGED
@@ -287,6 +287,10 @@ a smoother appearance.</div></div></div></div>
287
287
  <div class="dropped-file-list"></div>
288
288
  <div class="option-menu">
289
289
  <div id="path-import-options">
290
+ <div id="gpkg-import-options" class="hidden">
291
+ <h4>GeoPackage layers</h4>
292
+ <div class="gpkg-layer-list option-menu"></div>
293
+ </div>
290
294
  </div>
291
295
  <div style="margin-top: 10px"><input type="text" class="text-input advanced-options" placeholder="import options">
292
296
  <a href="https://github.com/mbloch/mapshaper/wiki/Command-Reference#-i-input" target="mapshaper_import_docs">
@@ -1956,6 +1956,28 @@
1956
1956
  }
1957
1957
  }
1958
1958
 
1959
+ async function getGeoPackageFeatureTables(content) {
1960
+ var geopackage, gpkg, source;
1961
+ await loadGeopackageLib();
1962
+ geopackage = window.modules && window.modules['@ngageoint/geopackage'];
1963
+ if (!geopackage || !geopackage.GeoPackageAPI) {
1964
+ throw Error('GeoPackage library is not loaded');
1965
+ }
1966
+ if (content instanceof Uint8Array) {
1967
+ source = content;
1968
+ } else if (content instanceof ArrayBuffer) {
1969
+ source = new Uint8Array(content);
1970
+ } else {
1971
+ source = content;
1972
+ }
1973
+ gpkg = await geopackage.GeoPackageAPI.open(source);
1974
+ try {
1975
+ return gpkg.getFeatureTables() || [];
1976
+ } finally {
1977
+ gpkg.close();
1978
+ }
1979
+ }
1980
+
1959
1981
  // @cb function(<FileList>)
1960
1982
  function DropControl(gui, el, cb) {
1961
1983
  var area = El(el);
@@ -2105,6 +2127,10 @@
2105
2127
  var importTotal = 0;
2106
2128
  var useQuickView = false;
2107
2129
  var queuedFiles = [];
2130
+ var gpkgLayerEntries = [];
2131
+ var gpkgSelectAllBtn = null;
2132
+ var gpkgListRequestId = 0;
2133
+ var gpkgListPending = false;
2108
2134
  var manifestFiles = opts.files || [];
2109
2135
  var catalog;
2110
2136
 
@@ -2192,6 +2218,7 @@
2192
2218
 
2193
2219
  function clearQueuedFiles() {
2194
2220
  queuedFiles = [];
2221
+ clearGeoPackageLayerSelectionMenu();
2195
2222
  gui.container.removeClass('queued-files');
2196
2223
  gui.container.findChild('.dropped-file-list').empty();
2197
2224
  }
@@ -2223,12 +2250,13 @@
2223
2250
  });
2224
2251
  if (queuedFiles.length > 0) {
2225
2252
  showQueuedFiles();
2253
+ updateGeoPackageLayerSelectionMenu();
2226
2254
  } else {
2227
2255
  gui.clearMode();
2228
2256
  }
2229
2257
  });
2230
2258
  });
2231
- submitBtn.classed('disabled', queuedFiles.length === 0);
2259
+ updateImportSubmitState();
2232
2260
  }
2233
2261
 
2234
2262
  function receiveDroppedItems(arr) {
@@ -2277,6 +2305,7 @@
2277
2305
  gui.container.classed('queued-files', queuedFiles.length > 0);
2278
2306
  El('#path-import-options').classed('hidden', !filesMayContainPaths(queuedFiles));
2279
2307
  showQueuedFiles();
2308
+ updateGeoPackageLayerSelectionMenu();
2280
2309
  }
2281
2310
 
2282
2311
  function hideImportMenu() {
@@ -2313,13 +2342,14 @@
2313
2342
  var optStr = GUI.formatCommandOptions(importOpts);
2314
2343
  fileData = null;
2315
2344
  for (var group of groups) {
2345
+ var groupImportOpts = getGroupImportOpts(group, importOpts);
2316
2346
  if (group.size > 4e7) {
2317
2347
  gui.showProgressMessage('Importing');
2318
2348
  await wait(35);
2319
2349
  }
2320
2350
  if (group[internal.PACKAGE_EXT]) {
2321
2351
  await importSessionData(group[internal.PACKAGE_EXT].content, gui);
2322
- } else if (await importDataset(group, importOpts)) {
2352
+ } else if (await importDataset(group, groupImportOpts)) {
2323
2353
  importCount++;
2324
2354
  gui.session.fileImported(group.filename, optStr);
2325
2355
  }
@@ -2328,6 +2358,8 @@
2328
2358
 
2329
2359
  async function importDataset(group, importOpts) {
2330
2360
  var dataset;
2361
+ var datasets;
2362
+ var imported = false;
2331
2363
  if (group.gpkg) {
2332
2364
  await loadGeopackageLib();
2333
2365
  }
@@ -2336,21 +2368,25 @@
2336
2368
  } else {
2337
2369
  dataset = internal.importContent(group, importOpts);
2338
2370
  }
2339
- if (datasetIsEmpty(dataset)) return false;
2340
- if (group.layername) {
2341
- dataset.layers.forEach(lyr => lyr.name = group.layername);
2342
- }
2343
- // TODO: add popup here
2344
- // save import options for use by repair control, etc.
2345
- dataset.info.import_options = importOpts;
2346
- try {
2347
- await considerReprojecting(gui, dataset, importOpts);
2348
- } catch(e) {
2349
- gui.alert(e.message, 'Projection error');
2350
- return false;
2371
+ datasets = Array.isArray(dataset) ? dataset : [dataset];
2372
+ for (var d of datasets) {
2373
+ if (datasetIsEmpty(d)) continue;
2374
+ if (group.layername) {
2375
+ d.layers.forEach(lyr => lyr.name = group.layername);
2376
+ }
2377
+ // TODO: add popup here
2378
+ // save import options for use by repair control, etc.
2379
+ d.info.import_options = importOpts;
2380
+ try {
2381
+ await considerReprojecting(gui, d, importOpts);
2382
+ } catch(e) {
2383
+ gui.alert(e.message, 'Projection error');
2384
+ return false;
2385
+ }
2386
+ model.addDataset(d);
2387
+ imported = true;
2351
2388
  }
2352
- model.addDataset(dataset);
2353
- return true;
2389
+ return imported;
2354
2390
  }
2355
2391
 
2356
2392
 
@@ -2358,7 +2394,7 @@
2358
2394
  function filesMayContainPaths(files) {
2359
2395
  return utils$1.some(files, function(f) {
2360
2396
  var type = internal.guessInputFileType(f.name);
2361
- return type == 'shp' || type == 'json' || internal.isZipFile(f.name);
2397
+ return type == 'shp' || type == 'json' || type == 'gpkg' || internal.isZipFile(f.name);
2362
2398
  });
2363
2399
  }
2364
2400
 
@@ -2383,6 +2419,167 @@
2383
2419
  return importOpts;
2384
2420
  }
2385
2421
 
2422
+ function getGroupImportOpts(group, importOpts) {
2423
+ var layersByFile, filename, selected;
2424
+ if (!group.gpkg) return importOpts;
2425
+ layersByFile = getSelectedGeoPackageLayersByFile();
2426
+ filename = group.gpkg.filename;
2427
+ selected = layersByFile[filename];
2428
+ if (!selected || selected.length === 0) return importOpts;
2429
+ return Object.assign({}, importOpts, {
2430
+ gpkg_layers: selected
2431
+ });
2432
+ }
2433
+
2434
+ function clearGeoPackageLayerSelectionMenu() {
2435
+ gpkgLayerEntries = [];
2436
+ gpkgSelectAllBtn = null;
2437
+ gpkgListPending = false;
2438
+ var container = El('#gpkg-import-options');
2439
+ if (!container.node()) return;
2440
+ container.addClass('hidden');
2441
+ container.findChild('.gpkg-layer-list').empty();
2442
+ }
2443
+
2444
+ function getQueuedGeoPackageFiles() {
2445
+ return queuedFiles.filter(function(file) {
2446
+ return internal.guessInputFileType(file.name) == 'gpkg';
2447
+ });
2448
+ }
2449
+
2450
+ async function updateGeoPackageLayerSelectionMenu() {
2451
+ var gpkgFiles = getQueuedGeoPackageFiles();
2452
+ var requestId = ++gpkgListRequestId;
2453
+ clearGeoPackageLayerSelectionMenu();
2454
+ if (useQuickView || gpkgFiles.length === 0) {
2455
+ updateImportSubmitState();
2456
+ return;
2457
+ }
2458
+ gpkgListPending = true;
2459
+ updateImportSubmitState();
2460
+ try {
2461
+ var tablesByFile = await Promise.all(gpkgFiles.map(async function(file) {
2462
+ return {
2463
+ file: file,
2464
+ tables: await getGeoPackageFeatureTables(file.content)
2465
+ };
2466
+ }));
2467
+ if (requestId != gpkgListRequestId) return;
2468
+ renderGeoPackageLayerSelectionMenu(tablesByFile);
2469
+ } catch (e) {
2470
+ console.error(e);
2471
+ if (requestId == gpkgListRequestId) {
2472
+ clearGeoPackageLayerSelectionMenu();
2473
+ gui.alert(e.message || 'Unable to read GeoPackage layers', 'Import error');
2474
+ }
2475
+ } finally {
2476
+ if (requestId == gpkgListRequestId) {
2477
+ gpkgListPending = false;
2478
+ updateImportSubmitState();
2479
+ }
2480
+ }
2481
+ }
2482
+
2483
+ function renderGeoPackageLayerSelectionMenu(tablesByFile) {
2484
+ var container = El('#gpkg-import-options');
2485
+ var list = container.findChild('.gpkg-layer-list').empty();
2486
+ gpkgLayerEntries = [];
2487
+ gpkgSelectAllBtn = null;
2488
+ tablesByFile.forEach(function(item) {
2489
+ var filename = item.file.name;
2490
+ var tables = item.tables || [];
2491
+ if (tables.length === 0) return;
2492
+ tables.forEach(function(tableName) {
2493
+ var label = El('label').addClass('gpkg-layer-item');
2494
+ var box = El('input')
2495
+ .attr('type', 'checkbox')
2496
+ .attr('data-gpkg-file', filename)
2497
+ .attr('data-gpkg-layer', tableName)
2498
+ .node();
2499
+ box.checked = true;
2500
+ box.addEventListener('click', updateGeoPackageSelectAllToggle);
2501
+ label.appendChild(box);
2502
+ label.appendChild(El('span').text(' ' + tableName));
2503
+ list.appendChild(label);
2504
+ var entry = {
2505
+ filename: filename,
2506
+ layer: tableName,
2507
+ checkbox: box
2508
+ };
2509
+ gpkgLayerEntries.push(entry);
2510
+ });
2511
+ });
2512
+ if (gpkgLayerEntries.length === 0) {
2513
+ container.addClass('hidden');
2514
+ return;
2515
+ }
2516
+ if (gpkgLayerEntries.length > 1) {
2517
+ list.node().insertBefore(initGeoPackageSelectAllToggle().node(), list.node().firstChild);
2518
+ }
2519
+ container.removeClass('hidden');
2520
+ updateGeoPackageSelectAllToggle();
2521
+ }
2522
+
2523
+ function initGeoPackageSelectAllToggle() {
2524
+ var toggle = El('label').addClass('gpkg-layer-item');
2525
+ var btn = El('input')
2526
+ .attr('type', 'checkbox')
2527
+ .attr('value', 'toggle')
2528
+ .node();
2529
+ btn.checked = true;
2530
+ btn.addEventListener('click', function() {
2531
+ var state = getGeoPackageSelectionState();
2532
+ setGeoPackageLayerSelection(state != 'all');
2533
+ updateGeoPackageSelectAllToggle();
2534
+ });
2535
+ toggle.appendChild(btn);
2536
+ toggle.appendChild(El('span').text(' Select all'));
2537
+ gpkgSelectAllBtn = btn;
2538
+ return toggle;
2539
+ }
2540
+
2541
+ function setGeoPackageLayerSelection(checked) {
2542
+ gpkgLayerEntries.forEach(function(entry) {
2543
+ entry.checkbox.checked = !!checked;
2544
+ });
2545
+ }
2546
+
2547
+ function getGeoPackageSelectionState() {
2548
+ var count = getSelectedGeoPackageLayerCount();
2549
+ if (gpkgLayerEntries.length > 0 && count == gpkgLayerEntries.length) return 'all';
2550
+ if (count === 0) return 'none';
2551
+ return 'some';
2552
+ }
2553
+
2554
+ function getSelectedGeoPackageLayerCount() {
2555
+ return gpkgLayerEntries.reduce(function(memo, entry) {
2556
+ return memo + (entry.checkbox.checked ? 1 : 0);
2557
+ }, 0);
2558
+ }
2559
+
2560
+ function getSelectedGeoPackageLayersByFile() {
2561
+ return gpkgLayerEntries.reduce(function(memo, entry) {
2562
+ if (!entry.checkbox.checked) return memo;
2563
+ if (!memo[entry.filename]) memo[entry.filename] = [];
2564
+ memo[entry.filename].push(entry.layer);
2565
+ return memo;
2566
+ }, {});
2567
+ }
2568
+
2569
+ function updateGeoPackageSelectAllToggle() {
2570
+ if (gpkgSelectAllBtn) {
2571
+ gpkgSelectAllBtn.checked = getGeoPackageSelectionState() == 'all';
2572
+ }
2573
+ updateImportSubmitState();
2574
+ }
2575
+
2576
+ function updateImportSubmitState() {
2577
+ var disabled = queuedFiles.length === 0 ||
2578
+ gpkgListPending ||
2579
+ (gpkgLayerEntries.length > 0 && getSelectedGeoPackageLayerCount() === 0);
2580
+ submitBtn.classed('disabled', disabled);
2581
+ }
2582
+
2386
2583
  // @file a File object
2387
2584
  async function readContentFileAsync(file, cb) {
2388
2585
  var reader = new FileReader();