mapshaper 0.6.37 → 0.6.39

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
 
3
- var VERSION = "0.6.37";
3
+ var VERSION = "0.6.39";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -65,7 +65,7 @@
65
65
  get rtrim () { return rtrim; },
66
66
  get addThousandsSep () { return addThousandsSep; },
67
67
  get numToStr () { return numToStr; },
68
- get formatNumber () { return formatNumber; },
68
+ get formatNumber () { return formatNumber$1; },
69
69
  get formatIntlNumber () { return formatIntlNumber; },
70
70
  get formatNumberForDisplay () { return formatNumberForDisplay; },
71
71
  get shuffle () { return shuffle; },
@@ -649,12 +649,12 @@
649
649
  return decimals >= 0 ? num.toFixed(decimals) : String(num);
650
650
  }
651
651
 
652
- function formatNumber(val) {
652
+ function formatNumber$1(val) {
653
653
  return val + '';
654
654
  }
655
655
 
656
656
  function formatIntlNumber(val) {
657
- var str = formatNumber(val);
657
+ var str = formatNumber$1(val);
658
658
  return '"' + str.replace('.', ',') + '"'; // need to quote if comma-delimited
659
659
  }
660
660
 
@@ -1247,7 +1247,8 @@
1247
1247
 
1248
1248
  // Handle an error caused by invalid input or misuse of API
1249
1249
  function stop() {
1250
- _stop.apply(null, utils.toArray(arguments));
1250
+ // _stop.apply(null, utils.toArray(arguments));
1251
+ _stop.apply(null, messageArgs(arguments));
1251
1252
  }
1252
1253
 
1253
1254
  function interrupt() {
@@ -4670,12 +4671,11 @@
4670
4671
  albersusa: AlbersUSA
4671
4672
  };
4672
4673
 
4673
- // This stub is replaced when loaded in GUI, which may need to load some files
4674
- function initProjLibrary(opts, done) {
4675
- if (!asyncLoader) return done();
4676
- asyncLoader(opts, done);
4674
+ async function initProjLibrary(opts) {
4675
+ if (asyncLoader) await asyncLoader(opts);
4677
4676
  }
4678
4677
 
4678
+ // used by web UI to support loading projection assets asyncronously
4679
4679
  function setProjectionLoader(loader) {
4680
4680
  asyncLoader = loader;
4681
4681
  }
@@ -13115,6 +13115,105 @@
13115
13115
  });
13116
13116
  }
13117
13117
 
13118
+ // Parse a formatted value in DMS DM or D to a numeric value. Returns NaN if unparsable.
13119
+ // Delimiters: degrees: D|d|°; minutes: '; seconds: "
13120
+ function parseDMS(str, fmt) {
13121
+ var defaultRxp = /^(?<prefix>[nsew+-]?)(?<d>[0-9.]+)[d°]? ?(?<m>[0-9.]*)['′]? ?(?<s>[0-9.]*)["″]? ?(?<suffix>[nsew]?)$/i;
13122
+ var rxp = fmt ? getParseRxp(fmt) : defaultRxp;
13123
+ var match = rxp.exec(str.trim());
13124
+ var d = NaN;
13125
+ var deg, min, sec;
13126
+ if (match) {
13127
+ deg = match.groups.d || '0';
13128
+ min = match.groups.m || '0';
13129
+ sec = match.groups.s || '0';
13130
+ d = (+deg) + (+min) / 60 + (+sec) / 3600;
13131
+ if (/[sw-]/i.test(match.groups.prefix || '') || /[sw]/i.test(match.groups.suffix || '')) {
13132
+ d = -d;
13133
+ }
13134
+ }
13135
+ return d;
13136
+ }
13137
+
13138
+ var cache$1 = {};
13139
+
13140
+ function getParseRxp(fmt) {
13141
+ if (fmt in cache$1) return cache$1[fmt];
13142
+ var rxp = fmt;
13143
+ rxp = rxp.replace('[-]', '(?<prefix>-)?'); // optional -
13144
+ rxp = rxp.replace(/\[[NSEW, +-]{2,}\]/, '(?<prefix>$&)');
13145
+ rxp = rxp.replace(/(MM?)(\.M+)?/, (m, g1, g2) => {
13146
+ var s = g1.length == 1 ? '[0-9]+' : '[0-9][0-9]';
13147
+ if (g2) s += `\\.[0-9]+`;
13148
+ return `(?<m>${s})`;
13149
+ });
13150
+ rxp = rxp.replace(/(SS?)(\.S+)?/, (m, g1, g2) => {
13151
+ var s = g1.length == 1 ? '[0-9]+' : '[0-9][0-9]';
13152
+ if (g2) s += `\\.[0-9]+`;
13153
+ return `(?<s>${s})`;
13154
+ });
13155
+ rxp = rxp.replace(/D+/, '(?<d>[0-9]+)');
13156
+ rxp = '^' + rxp + '$';
13157
+ try {
13158
+ // TODO: make sure all DMS codes have been matched
13159
+ cache$1[fmt] = new RegExp(rxp);
13160
+ } catch(e) {
13161
+ stop('Invalid DMS format string:', fmt);
13162
+ }
13163
+ return cache$1[fmt];
13164
+ }
13165
+
13166
+ function formatNumber(val, integers, decimals) {
13167
+ var str = val.toFixed(decimals);
13168
+ var parts = str.split('.');
13169
+ if (parts.length > 0) {
13170
+ parts[0] = parts[0].padStart(integers, '0');
13171
+ str = parts.join('.');
13172
+ }
13173
+ return str;
13174
+ }
13175
+
13176
+ function getDecimals(fmt) {
13177
+ var match = /S\.(S+)/.exec(fmt) || /M\.(M+)/.exec(fmt) || null;
13178
+ return match ? match[1].length : 0;
13179
+ }
13180
+
13181
+ // TODO: support DD.DDDDD
13182
+ function formatDMS(coord, fmt) {
13183
+ if (!fmt) fmt = '[-]D°M\'S.SSS';
13184
+ var str = fmt;
13185
+ var dstr, mstr, sstr;
13186
+ var match = /(D+)[^M]*(M+)[^S[\]]*(S+)?/.exec(fmt);
13187
+ if (!match) {
13188
+ stop('Invalid DMS format string:', fmt);
13189
+ }
13190
+ var gotSeconds = !!match[3];
13191
+ var decimals = getDecimals(fmt);
13192
+ var integers = gotSeconds ? match[3].length : match[2].length;
13193
+ var RES = Math.pow(10, decimals);
13194
+ var CONV = gotSeconds ? 3600 * RES : 60 * RES;
13195
+ var r = Math.floor(Math.abs(coord) * CONV + 0.5);
13196
+ var lastPart = formatNumber((r / RES) % 60, integers, decimals);
13197
+ if (gotSeconds) {
13198
+ r = Math.floor(r / (RES * 60));
13199
+ sstr = lastPart;
13200
+ mstr = String(r % 60).padStart(match[2].length, '0');
13201
+ } else {
13202
+ r = Math.floor(r / RES);
13203
+ mstr = lastPart;
13204
+ sstr = '';
13205
+ }
13206
+ dstr = String(Math.floor(r / 60)).padStart(match[1].length, '0');
13207
+ str = str.replace(/\[-\]/, s => coord < 0 ? '-' : '');
13208
+ str = str.replace(/\[[+-]+\]/, s => coord < 0 ? '-' : '+');
13209
+ str = str.replace(/\[[NS, ]+\]/, s => coord < 0 ? 'S' : 'N');
13210
+ str = str.replace(/\[[EW, ]+\]/, s => coord < 0 ? 'W' : 'E');
13211
+ str = str.replace(/D+/, dstr);
13212
+ str = str.replace(/M+(\.M+)?/, mstr);
13213
+ if (gotSeconds) str = str.replace(/S+(\.S+)?/, sstr);
13214
+ return str;
13215
+ }
13216
+
13118
13217
  function cleanExpression(exp) {
13119
13218
  // workaround for problem in GNU Make v4: end-of-line backslashes inside
13120
13219
  // quoted strings are left in the string (other shell environments remove them)
@@ -13126,7 +13225,9 @@
13126
13225
  round: roundToDigits2,
13127
13226
  int_median: interpolated_median,
13128
13227
  sprintf: utils.format,
13129
- blend: blend
13228
+ blend: blend,
13229
+ format_dms: formatDMS,
13230
+ parse_dms: parseDMS
13130
13231
  });
13131
13232
  }
13132
13233
 
@@ -22791,7 +22892,6 @@ ${svg}
22791
22892
 
22792
22893
  this.getHelpMessage = function(cmdName) {
22793
22894
  var helpCommands, singleCommand, lines;
22794
-
22795
22895
  if (cmdName) {
22796
22896
  singleCommand = findCommandDefn(cmdName, getCommands());
22797
22897
  if (!singleCommand) {
@@ -22910,10 +23010,6 @@ ${svg}
22910
23010
  }
22911
23011
  };
22912
23012
 
22913
- this.printHelp = function(command) {
22914
- print(this.getHelpMessage(command));
22915
- };
22916
-
22917
23013
  function getCommands() {
22918
23014
  return _commands.map(function(cmd) {
22919
23015
  return cmd.done();
@@ -23633,7 +23729,7 @@ ${svg}
23633
23729
  type: 'flag'
23634
23730
  })
23635
23731
  .option('other', {
23636
- describe: 'default color for categorical scheme (defaults to no-data color)'
23732
+ describe: 'default color for categorical scheme (default is nodata color)'
23637
23733
  })
23638
23734
  .option('nodata', {
23639
23735
  describe: 'color to use for invalid or missing data (default is white)'
@@ -24561,7 +24657,7 @@ ${svg}
24561
24657
  parser.command('symbols')
24562
24658
  .describe('symbolize points as arrows, circles, stars, polygons, etc.')
24563
24659
  .option('type', {
24564
- describe: 'symbol type (e.g. arrow, circle, square, star, polygon, ring)'
24660
+ describe: 'types: arrow, circle, square, star, polygon, ring'
24565
24661
  })
24566
24662
  .option('stroke', {})
24567
24663
  .option('stroke-width', {})
@@ -24574,6 +24670,9 @@ ${svg}
24574
24670
  .option('stroke-width', {
24575
24671
  describe: 'symbol line width (linear symbols only)'
24576
24672
  })
24673
+ .option('opacity', {
24674
+ describe: 'symbol opacity'
24675
+ })
24577
24676
  .option('geographic', {
24578
24677
  old_alias: 'polygons',
24579
24678
  describe: 'make geographic shapes instead of SVG objects',
@@ -24674,7 +24773,6 @@ ${svg}
24674
24773
  .option('no-replace', noReplaceOpt);
24675
24774
  // .option('name', nameOpt);
24676
24775
 
24677
-
24678
24776
  parser.command('target')
24679
24777
  .describe('set active layer (or layers)')
24680
24778
  .option('target', {
@@ -24684,6 +24782,14 @@ ${svg}
24684
24782
  .option('type', {
24685
24783
  describe: 'type of layer to target (polygon|polyline|point)'
24686
24784
  })
24785
+ // .option('combine', {
24786
+ // type: 'flag',
24787
+ // describe: 'place all targeted layers in one dataset together with any associated layers'
24788
+ // })
24789
+ // .option('isolate', {
24790
+ // type: 'flag',
24791
+ // describe: 'place all targeted layers in one dataset exclusive of associated layers'
24792
+ // })
24687
24793
  .option('name', {
24688
24794
  describe: 'rename the target layer'
24689
24795
  });
@@ -38516,6 +38622,11 @@ ${svg}
38516
38622
  };
38517
38623
  }
38518
38624
 
38625
+ cmd.printHelp = function(opts) {
38626
+ var str = getOptionParser().getHelpMessage(opts.command);
38627
+ print(str);
38628
+ };
38629
+
38519
38630
  function resetControlFlow(job) {
38520
38631
  job.control = null;
38521
38632
  }
@@ -39155,25 +39266,6 @@ ${svg}
39155
39266
  };
39156
39267
  }
39157
39268
 
39158
- // Parse a formatted value in DMS DM or D to a numeric value. Returns NaN if unparsable.
39159
- // Delimiters: degrees: D|d|°; minutes: '; seconds: "
39160
- function parseDMS(str) {
39161
- var rxp = /^([nsew+-]?)([0-9.]+)[d°]? ?([0-9.]*)['′]? ?([0-9.]*)["″]? ?([nsew]?)$/i;
39162
- var match = rxp.exec(str.trim());
39163
- var d = NaN;
39164
- var deg, min, sec;
39165
- if (match) {
39166
- deg = match[2] || '0';
39167
- min = match[3] || '0';
39168
- sec = match[4] || '0';
39169
- d = (+deg) + (+min) / 60 + (+sec) / 3600;
39170
- if (/[sw-]/i.test(match[1]) || /[sw]/i.test(match[5])) {
39171
- d = -d;
39172
- }
39173
- }
39174
- return d;
39175
- }
39176
-
39177
39269
  function findNearestVertices(p, shp, arcs) {
39178
39270
  var p2 = findNearestVertex(p[0], p[1], shp, arcs);
39179
39271
  return findVertexIds(p2.x, p2.y, arcs);
@@ -42281,6 +42373,18 @@ ${svg}
42281
42373
  return d.stroke || d.fill || 'magenta';
42282
42374
  }
42283
42375
 
42376
+ function applySymbolStyles(sym, d) {
42377
+ if (sym.type == 'polyline') {
42378
+ sym.stroke = getSymbolStrokeColor(d);
42379
+ } else {
42380
+ sym.fill = getSymbolFillColor(d);
42381
+ }
42382
+ if (d.opacity) {
42383
+ sym.opacity = d.opacity;
42384
+ }
42385
+ return sym;
42386
+ }
42387
+
42284
42388
  function getSymbolRadius(d) {
42285
42389
  if (d.radius === 0 || d.length === 0 || d.r === 0) return 0;
42286
42390
  return d.radius || d.length || d.r || 5; // use a default value
@@ -42518,11 +42622,8 @@ ${svg}
42518
42622
  var radius = getSymbolRadius(d);
42519
42623
  // TODO: remove duplication with svg-symbols.js
42520
42624
  if (+opts.scale) radius *= +opts.scale;
42521
- return {
42522
- type: 'circle',
42523
- fill: getSymbolFillColor(d),
42524
- r: radius
42525
- };
42625
+ var sym = { type: 'circle', r: radius };
42626
+ return applySymbolStyles(sym, d);
42526
42627
  }
42527
42628
 
42528
42629
  function getPolygonCoords(d) {
@@ -42614,11 +42715,13 @@ ${svg}
42614
42715
  var radii = parseRings(d.radii || '2').map(function(r) { return r * scale; });
42615
42716
  var solidCenter = utils.isOdd(radii.length);
42616
42717
  var color = getSymbolFillColor(d);
42718
+ var opacity = opts.opacity || undefined;
42617
42719
  var parts = [];
42618
42720
  if (solidCenter) {
42619
42721
  parts.push({
42620
42722
  type: 'circle',
42621
42723
  fill: color,
42724
+ opacity: opacity,
42622
42725
  r: radii.shift()
42623
42726
  });
42624
42727
  }
@@ -42627,6 +42730,7 @@ ${svg}
42627
42730
  type: 'circle',
42628
42731
  fill: 'none', // TODO remove default black fill so this is not needed
42629
42732
  stroke: color,
42733
+ opacity: opacity,
42630
42734
  'stroke-width': roundToTenths(radii[i+1] - radii[i]),
42631
42735
  r: roundToTenths(radii[i+1] * 0.5 + radii[i] * 0.5)
42632
42736
  });
@@ -42674,19 +42778,18 @@ ${svg}
42674
42778
  if (geojsonType == 'MultiPolygon' || geojsonType == 'Polygon') {
42675
42779
  sym = {
42676
42780
  type: 'polygon',
42677
- fill: getSymbolFillColor(properties),
42678
42781
  coordinates: geojsonType == 'Polygon' ? coords : flattenMultiPolygonCoords(coords)
42679
42782
  };
42680
42783
  } else if (geojsonType == 'LineString' || geojsonType == 'MultiLineString') {
42681
42784
  sym = {
42682
42785
  type: 'polyline',
42683
- stroke: getSymbolStrokeColor(properties),
42684
42786
  'stroke-width': properties['stroke-width'] || 2,
42685
42787
  coordinates: geojsonType == 'LineString' ? [coords] : coords
42686
42788
  };
42687
42789
  } else {
42688
42790
  error('Unsupported type:', geojsonType);
42689
42791
  }
42792
+ applySymbolStyles(sym, properties);
42690
42793
  roundCoordsForSVG(sym.coordinates);
42691
42794
  return sym;
42692
42795
  }
@@ -42746,6 +42849,7 @@ ${svg}
42746
42849
  scaleAndShiftCoords(coords, metersPerPx, shp[0]);
42747
42850
  if (d.fill) rec.fill = d.fill;
42748
42851
  if (d.stroke) rec.stroke = d.stroke;
42852
+ if (d.opacity) rec.opacity = d.opacity;
42749
42853
  return createGeometry(coords, geojsonType);
42750
42854
  } else {
42751
42855
  rec['svg-symbol'] = makePathSymbol(coords, d, geojsonType);
@@ -42811,9 +42915,41 @@ ${svg}
42811
42915
  // TODO: improve this
42812
42916
  targets[0].layers[0].name = opts.name;
42813
42917
  }
42918
+ if (opts.combine) {
42919
+ targets = combineTargets(targets, catalog);
42920
+ } else if (opts.isolate) {
42921
+ targets = isolateTargets(targets, catalog);
42922
+ }
42814
42923
  catalog.setDefaultTargets(targets);
42815
42924
  };
42816
42925
 
42926
+ function combineTargets(targets, catalog) {
42927
+ var datasets = [];
42928
+ var layers = [];
42929
+ targets.forEach(function(o) {
42930
+ datasets.push(o.dataset);
42931
+ catalog.removeDataset(o.dataset);
42932
+ layers = layers.concat(o.layers);
42933
+ });
42934
+ var combined = mergeDatasets(datasets);
42935
+ catalog.addDataset(combined);
42936
+ return [{
42937
+ dataset: combined,
42938
+ layers: layers
42939
+ }];
42940
+ }
42941
+
42942
+ function isolateTargets(targets, catalog) {
42943
+ var datasets = [];
42944
+ targets.forEach(function(o) {
42945
+ datasets.push(o.dataset);
42946
+ o.layers.forEach(function(lyr) {
42947
+ catalog.removeLayer(lyr, o.dataset);
42948
+
42949
+ });
42950
+ });
42951
+ }
42952
+
42817
42953
  cmd.union = function(targetLayers, targetDataset, opts) {
42818
42954
  if (targetLayers.length < 2) {
42819
42955
  stop('Command requires at least two target layers');
@@ -43198,7 +43334,7 @@ ${svg}
43198
43334
  function commandAcceptsMultipleTargetDatasets(name) {
43199
43335
  return name == 'rotate' || name == 'info' || name == 'proj' ||
43200
43336
  name == 'drop' || name == 'target' || name == 'if' || name == 'elif' ||
43201
- name == 'else' || name == 'endif' || name == 'run';
43337
+ name == 'else' || name == 'endif' || name == 'run' || name == 'i';
43202
43338
  }
43203
43339
 
43204
43340
  function commandAcceptsEmptyTarget(name) {
@@ -43387,8 +43523,8 @@ ${svg}
43387
43523
  job.catalog.addDataset(cmd.graticule(targetDataset, opts));
43388
43524
 
43389
43525
  } else if (name == 'help') {
43390
- // placing this here to handle errors from invalid command names
43391
- getOptionParser().printHelp(command.options.command);
43526
+ // placing help command here to handle errors from invalid command names
43527
+ cmd.printHelp(command.options);
43392
43528
 
43393
43529
  } else if (name == 'i') {
43394
43530
  if (opts.replace) job.catalog = new Catalog(); // is this what we want?
@@ -43470,7 +43606,7 @@ ${svg}
43470
43606
  cmd.print(command._.join(' '));
43471
43607
 
43472
43608
  } else if (name == 'proj') {
43473
- await utils.promisify(initProjLibrary)(opts);
43609
+ await initProjLibrary(opts);
43474
43610
  job.resumeCommand();
43475
43611
  targets.forEach(function(targ) {
43476
43612
  cmd.proj(targ.dataset, job.catalog, opts);
@@ -44305,6 +44441,7 @@ ${svg}
44305
44441
  Heap,
44306
44442
  NodeCollection,
44307
44443
  parseDMS,
44444
+ formatDMS,
44308
44445
  PathIndex,
44309
44446
  PolygonIndex,
44310
44447
  ShpReader,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.6.37",
3
+ "version": "0.6.39",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -2479,42 +2479,18 @@
2479
2479
  });
2480
2480
  }
2481
2481
 
2482
+ internal.setProjectionLoader(loadProjLibs);
2483
+
2482
2484
  // load Proj.4 CRS definition files dynamically
2483
2485
  //
2484
- internal.setProjectionLoader(function(opts, done) {
2486
+ async function loadProjLibs(opts) {
2485
2487
  var mproj = require('mproj');
2486
2488
  var libs = internal.findProjLibs([opts.init || '', opts.match || '', opts.crs || ''].join(' '));
2487
- // skip loaded libs
2488
- libs = libs.filter(function(name) {return !mproj.internal.mproj_search_libcache(name);});
2489
- loadProjLibs(libs, done);
2490
- });
2491
-
2492
- function loadProjLibs(libs, done) {
2493
- var mproj = require('mproj');
2494
- var i = 0;
2495
- next();
2496
-
2497
- function next() {
2498
- var libName = libs[i];
2499
- var content, req;
2500
- if (!libName) return done();
2501
- req = new XMLHttpRequest();
2502
- req.addEventListener('load', function(e) {
2503
- if (req.status == 200) {
2504
- content = req.response;
2505
- }
2506
- });
2507
- req.addEventListener('loadend', function() {
2508
- if (content) {
2509
- mproj.internal.mproj_insert_libcache(libName, content);
2510
- }
2511
- // TODO: consider stopping with an error message if no content was loaded
2512
- // (currently, a less specific error will occur when mapshaper tries to use the library)
2513
- next();
2514
- });
2515
- req.open('GET', 'assets/' + libName);
2516
- req.send();
2517
- i++;
2489
+ libs = libs.filter(function(name) {return !mproj.internal.mproj_search_libcache(name);}); // skip loaded libs
2490
+ for (var libName of libs) {
2491
+ var content = await fetch('assets/' + libName).then(resp => resp.ok ? resp.text() : null);
2492
+ if (!content) stop$1(`Unable to load projection resource [${libName}]`);
2493
+ mproj.internal.mproj_insert_libcache(libName, content);
2518
2494
  }
2519
2495
  }
2520
2496
 
@@ -3371,7 +3347,10 @@
3371
3347
  setDisplayProjection(gui, cmd);
3372
3348
  } else {
3373
3349
  line.hide(); // hide cursor while command is being run
3374
- runMapshaperCommands(cmd, function(err) {
3350
+ runMapshaperCommands(cmd, function(err, flags) {
3351
+ if (flags) {
3352
+ gui.clearMode();
3353
+ }
3375
3354
  if (err) {
3376
3355
  onError(err);
3377
3356
  }
@@ -3404,14 +3383,9 @@
3404
3383
  }
3405
3384
  }
3406
3385
  if (flags) {
3407
- // if the command may have changed data, and a tool with an edit mode is being used,
3408
- // close the tool. (we may need a better way to allow the console and other tools
3409
- // to be used at the same time).
3410
- gui.clearMode();
3411
-
3412
3386
  model.updated(flags); // info commands do not return flags
3413
3387
  }
3414
- done(err);
3388
+ done(err, flags);
3415
3389
  });
3416
3390
  }
3417
3391
 
@@ -5068,7 +5042,8 @@
5068
5042
 
5069
5043
  // Handle an error caused by invalid input or misuse of API
5070
5044
  function stop() {
5071
- _stop.apply(null, utils.toArray(arguments));
5045
+ // _stop.apply(null, utils.toArray(arguments));
5046
+ _stop.apply(null, messageArgs(arguments));
5072
5047
  }
5073
5048
 
5074
5049
  function interrupt() {
@@ -7976,10 +7951,11 @@
7976
7951
  hit.clearSelection();
7977
7952
  });
7978
7953
 
7979
- function runCommand(cmd) {
7954
+ function runCommand(cmd, turnOff) {
7980
7955
  popup.hide();
7981
7956
  if (gui.console) gui.console.runMapshaperCommands(cmd, function(err) {
7982
7957
  reset();
7958
+ if (turnOff) gui.clearMode();
7983
7959
  });
7984
7960
  }
7985
7961
  }
@@ -10311,6 +10287,7 @@
10311
10287
  if (gui.console) {
10312
10288
  gui.console.runMapshaperCommands(cmd, function(err) {
10313
10289
  reset();
10290
+ gui.clearMode();
10314
10291
  });
10315
10292
  }
10316
10293
  // reset(); // TODO: exit interactive mode
package/www/mapshaper.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
 
3
- var VERSION = "0.6.37";
3
+ var VERSION = "0.6.39";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -65,7 +65,7 @@
65
65
  get rtrim () { return rtrim; },
66
66
  get addThousandsSep () { return addThousandsSep; },
67
67
  get numToStr () { return numToStr; },
68
- get formatNumber () { return formatNumber; },
68
+ get formatNumber () { return formatNumber$1; },
69
69
  get formatIntlNumber () { return formatIntlNumber; },
70
70
  get formatNumberForDisplay () { return formatNumberForDisplay; },
71
71
  get shuffle () { return shuffle; },
@@ -649,12 +649,12 @@
649
649
  return decimals >= 0 ? num.toFixed(decimals) : String(num);
650
650
  }
651
651
 
652
- function formatNumber(val) {
652
+ function formatNumber$1(val) {
653
653
  return val + '';
654
654
  }
655
655
 
656
656
  function formatIntlNumber(val) {
657
- var str = formatNumber(val);
657
+ var str = formatNumber$1(val);
658
658
  return '"' + str.replace('.', ',') + '"'; // need to quote if comma-delimited
659
659
  }
660
660
 
@@ -1247,7 +1247,8 @@
1247
1247
 
1248
1248
  // Handle an error caused by invalid input or misuse of API
1249
1249
  function stop() {
1250
- _stop.apply(null, utils.toArray(arguments));
1250
+ // _stop.apply(null, utils.toArray(arguments));
1251
+ _stop.apply(null, messageArgs(arguments));
1251
1252
  }
1252
1253
 
1253
1254
  function interrupt() {
@@ -4670,12 +4671,11 @@
4670
4671
  albersusa: AlbersUSA
4671
4672
  };
4672
4673
 
4673
- // This stub is replaced when loaded in GUI, which may need to load some files
4674
- function initProjLibrary(opts, done) {
4675
- if (!asyncLoader) return done();
4676
- asyncLoader(opts, done);
4674
+ async function initProjLibrary(opts) {
4675
+ if (asyncLoader) await asyncLoader(opts);
4677
4676
  }
4678
4677
 
4678
+ // used by web UI to support loading projection assets asyncronously
4679
4679
  function setProjectionLoader(loader) {
4680
4680
  asyncLoader = loader;
4681
4681
  }
@@ -13115,6 +13115,105 @@
13115
13115
  });
13116
13116
  }
13117
13117
 
13118
+ // Parse a formatted value in DMS DM or D to a numeric value. Returns NaN if unparsable.
13119
+ // Delimiters: degrees: D|d|°; minutes: '; seconds: "
13120
+ function parseDMS(str, fmt) {
13121
+ var defaultRxp = /^(?<prefix>[nsew+-]?)(?<d>[0-9.]+)[d°]? ?(?<m>[0-9.]*)['′]? ?(?<s>[0-9.]*)["″]? ?(?<suffix>[nsew]?)$/i;
13122
+ var rxp = fmt ? getParseRxp(fmt) : defaultRxp;
13123
+ var match = rxp.exec(str.trim());
13124
+ var d = NaN;
13125
+ var deg, min, sec;
13126
+ if (match) {
13127
+ deg = match.groups.d || '0';
13128
+ min = match.groups.m || '0';
13129
+ sec = match.groups.s || '0';
13130
+ d = (+deg) + (+min) / 60 + (+sec) / 3600;
13131
+ if (/[sw-]/i.test(match.groups.prefix || '') || /[sw]/i.test(match.groups.suffix || '')) {
13132
+ d = -d;
13133
+ }
13134
+ }
13135
+ return d;
13136
+ }
13137
+
13138
+ var cache$1 = {};
13139
+
13140
+ function getParseRxp(fmt) {
13141
+ if (fmt in cache$1) return cache$1[fmt];
13142
+ var rxp = fmt;
13143
+ rxp = rxp.replace('[-]', '(?<prefix>-)?'); // optional -
13144
+ rxp = rxp.replace(/\[[NSEW, +-]{2,}\]/, '(?<prefix>$&)');
13145
+ rxp = rxp.replace(/(MM?)(\.M+)?/, (m, g1, g2) => {
13146
+ var s = g1.length == 1 ? '[0-9]+' : '[0-9][0-9]';
13147
+ if (g2) s += `\\.[0-9]+`;
13148
+ return `(?<m>${s})`;
13149
+ });
13150
+ rxp = rxp.replace(/(SS?)(\.S+)?/, (m, g1, g2) => {
13151
+ var s = g1.length == 1 ? '[0-9]+' : '[0-9][0-9]';
13152
+ if (g2) s += `\\.[0-9]+`;
13153
+ return `(?<s>${s})`;
13154
+ });
13155
+ rxp = rxp.replace(/D+/, '(?<d>[0-9]+)');
13156
+ rxp = '^' + rxp + '$';
13157
+ try {
13158
+ // TODO: make sure all DMS codes have been matched
13159
+ cache$1[fmt] = new RegExp(rxp);
13160
+ } catch(e) {
13161
+ stop('Invalid DMS format string:', fmt);
13162
+ }
13163
+ return cache$1[fmt];
13164
+ }
13165
+
13166
+ function formatNumber(val, integers, decimals) {
13167
+ var str = val.toFixed(decimals);
13168
+ var parts = str.split('.');
13169
+ if (parts.length > 0) {
13170
+ parts[0] = parts[0].padStart(integers, '0');
13171
+ str = parts.join('.');
13172
+ }
13173
+ return str;
13174
+ }
13175
+
13176
+ function getDecimals(fmt) {
13177
+ var match = /S\.(S+)/.exec(fmt) || /M\.(M+)/.exec(fmt) || null;
13178
+ return match ? match[1].length : 0;
13179
+ }
13180
+
13181
+ // TODO: support DD.DDDDD
13182
+ function formatDMS(coord, fmt) {
13183
+ if (!fmt) fmt = '[-]D°M\'S.SSS';
13184
+ var str = fmt;
13185
+ var dstr, mstr, sstr;
13186
+ var match = /(D+)[^M]*(M+)[^S[\]]*(S+)?/.exec(fmt);
13187
+ if (!match) {
13188
+ stop('Invalid DMS format string:', fmt);
13189
+ }
13190
+ var gotSeconds = !!match[3];
13191
+ var decimals = getDecimals(fmt);
13192
+ var integers = gotSeconds ? match[3].length : match[2].length;
13193
+ var RES = Math.pow(10, decimals);
13194
+ var CONV = gotSeconds ? 3600 * RES : 60 * RES;
13195
+ var r = Math.floor(Math.abs(coord) * CONV + 0.5);
13196
+ var lastPart = formatNumber((r / RES) % 60, integers, decimals);
13197
+ if (gotSeconds) {
13198
+ r = Math.floor(r / (RES * 60));
13199
+ sstr = lastPart;
13200
+ mstr = String(r % 60).padStart(match[2].length, '0');
13201
+ } else {
13202
+ r = Math.floor(r / RES);
13203
+ mstr = lastPart;
13204
+ sstr = '';
13205
+ }
13206
+ dstr = String(Math.floor(r / 60)).padStart(match[1].length, '0');
13207
+ str = str.replace(/\[-\]/, s => coord < 0 ? '-' : '');
13208
+ str = str.replace(/\[[+-]+\]/, s => coord < 0 ? '-' : '+');
13209
+ str = str.replace(/\[[NS, ]+\]/, s => coord < 0 ? 'S' : 'N');
13210
+ str = str.replace(/\[[EW, ]+\]/, s => coord < 0 ? 'W' : 'E');
13211
+ str = str.replace(/D+/, dstr);
13212
+ str = str.replace(/M+(\.M+)?/, mstr);
13213
+ if (gotSeconds) str = str.replace(/S+(\.S+)?/, sstr);
13214
+ return str;
13215
+ }
13216
+
13118
13217
  function cleanExpression(exp) {
13119
13218
  // workaround for problem in GNU Make v4: end-of-line backslashes inside
13120
13219
  // quoted strings are left in the string (other shell environments remove them)
@@ -13126,7 +13225,9 @@
13126
13225
  round: roundToDigits2,
13127
13226
  int_median: interpolated_median,
13128
13227
  sprintf: utils.format,
13129
- blend: blend
13228
+ blend: blend,
13229
+ format_dms: formatDMS,
13230
+ parse_dms: parseDMS
13130
13231
  });
13131
13232
  }
13132
13233
 
@@ -22791,7 +22892,6 @@ ${svg}
22791
22892
 
22792
22893
  this.getHelpMessage = function(cmdName) {
22793
22894
  var helpCommands, singleCommand, lines;
22794
-
22795
22895
  if (cmdName) {
22796
22896
  singleCommand = findCommandDefn(cmdName, getCommands());
22797
22897
  if (!singleCommand) {
@@ -22910,10 +23010,6 @@ ${svg}
22910
23010
  }
22911
23011
  };
22912
23012
 
22913
- this.printHelp = function(command) {
22914
- print(this.getHelpMessage(command));
22915
- };
22916
-
22917
23013
  function getCommands() {
22918
23014
  return _commands.map(function(cmd) {
22919
23015
  return cmd.done();
@@ -23633,7 +23729,7 @@ ${svg}
23633
23729
  type: 'flag'
23634
23730
  })
23635
23731
  .option('other', {
23636
- describe: 'default color for categorical scheme (defaults to no-data color)'
23732
+ describe: 'default color for categorical scheme (default is nodata color)'
23637
23733
  })
23638
23734
  .option('nodata', {
23639
23735
  describe: 'color to use for invalid or missing data (default is white)'
@@ -24561,7 +24657,7 @@ ${svg}
24561
24657
  parser.command('symbols')
24562
24658
  .describe('symbolize points as arrows, circles, stars, polygons, etc.')
24563
24659
  .option('type', {
24564
- describe: 'symbol type (e.g. arrow, circle, square, star, polygon, ring)'
24660
+ describe: 'types: arrow, circle, square, star, polygon, ring'
24565
24661
  })
24566
24662
  .option('stroke', {})
24567
24663
  .option('stroke-width', {})
@@ -24574,6 +24670,9 @@ ${svg}
24574
24670
  .option('stroke-width', {
24575
24671
  describe: 'symbol line width (linear symbols only)'
24576
24672
  })
24673
+ .option('opacity', {
24674
+ describe: 'symbol opacity'
24675
+ })
24577
24676
  .option('geographic', {
24578
24677
  old_alias: 'polygons',
24579
24678
  describe: 'make geographic shapes instead of SVG objects',
@@ -24674,7 +24773,6 @@ ${svg}
24674
24773
  .option('no-replace', noReplaceOpt);
24675
24774
  // .option('name', nameOpt);
24676
24775
 
24677
-
24678
24776
  parser.command('target')
24679
24777
  .describe('set active layer (or layers)')
24680
24778
  .option('target', {
@@ -24684,6 +24782,14 @@ ${svg}
24684
24782
  .option('type', {
24685
24783
  describe: 'type of layer to target (polygon|polyline|point)'
24686
24784
  })
24785
+ // .option('combine', {
24786
+ // type: 'flag',
24787
+ // describe: 'place all targeted layers in one dataset together with any associated layers'
24788
+ // })
24789
+ // .option('isolate', {
24790
+ // type: 'flag',
24791
+ // describe: 'place all targeted layers in one dataset exclusive of associated layers'
24792
+ // })
24687
24793
  .option('name', {
24688
24794
  describe: 'rename the target layer'
24689
24795
  });
@@ -38516,6 +38622,11 @@ ${svg}
38516
38622
  };
38517
38623
  }
38518
38624
 
38625
+ cmd.printHelp = function(opts) {
38626
+ var str = getOptionParser().getHelpMessage(opts.command);
38627
+ print(str);
38628
+ };
38629
+
38519
38630
  function resetControlFlow(job) {
38520
38631
  job.control = null;
38521
38632
  }
@@ -39155,25 +39266,6 @@ ${svg}
39155
39266
  };
39156
39267
  }
39157
39268
 
39158
- // Parse a formatted value in DMS DM or D to a numeric value. Returns NaN if unparsable.
39159
- // Delimiters: degrees: D|d|°; minutes: '; seconds: "
39160
- function parseDMS(str) {
39161
- var rxp = /^([nsew+-]?)([0-9.]+)[d°]? ?([0-9.]*)['′]? ?([0-9.]*)["″]? ?([nsew]?)$/i;
39162
- var match = rxp.exec(str.trim());
39163
- var d = NaN;
39164
- var deg, min, sec;
39165
- if (match) {
39166
- deg = match[2] || '0';
39167
- min = match[3] || '0';
39168
- sec = match[4] || '0';
39169
- d = (+deg) + (+min) / 60 + (+sec) / 3600;
39170
- if (/[sw-]/i.test(match[1]) || /[sw]/i.test(match[5])) {
39171
- d = -d;
39172
- }
39173
- }
39174
- return d;
39175
- }
39176
-
39177
39269
  function findNearestVertices(p, shp, arcs) {
39178
39270
  var p2 = findNearestVertex(p[0], p[1], shp, arcs);
39179
39271
  return findVertexIds(p2.x, p2.y, arcs);
@@ -42281,6 +42373,18 @@ ${svg}
42281
42373
  return d.stroke || d.fill || 'magenta';
42282
42374
  }
42283
42375
 
42376
+ function applySymbolStyles(sym, d) {
42377
+ if (sym.type == 'polyline') {
42378
+ sym.stroke = getSymbolStrokeColor(d);
42379
+ } else {
42380
+ sym.fill = getSymbolFillColor(d);
42381
+ }
42382
+ if (d.opacity) {
42383
+ sym.opacity = d.opacity;
42384
+ }
42385
+ return sym;
42386
+ }
42387
+
42284
42388
  function getSymbolRadius(d) {
42285
42389
  if (d.radius === 0 || d.length === 0 || d.r === 0) return 0;
42286
42390
  return d.radius || d.length || d.r || 5; // use a default value
@@ -42518,11 +42622,8 @@ ${svg}
42518
42622
  var radius = getSymbolRadius(d);
42519
42623
  // TODO: remove duplication with svg-symbols.js
42520
42624
  if (+opts.scale) radius *= +opts.scale;
42521
- return {
42522
- type: 'circle',
42523
- fill: getSymbolFillColor(d),
42524
- r: radius
42525
- };
42625
+ var sym = { type: 'circle', r: radius };
42626
+ return applySymbolStyles(sym, d);
42526
42627
  }
42527
42628
 
42528
42629
  function getPolygonCoords(d) {
@@ -42614,11 +42715,13 @@ ${svg}
42614
42715
  var radii = parseRings(d.radii || '2').map(function(r) { return r * scale; });
42615
42716
  var solidCenter = utils.isOdd(radii.length);
42616
42717
  var color = getSymbolFillColor(d);
42718
+ var opacity = opts.opacity || undefined;
42617
42719
  var parts = [];
42618
42720
  if (solidCenter) {
42619
42721
  parts.push({
42620
42722
  type: 'circle',
42621
42723
  fill: color,
42724
+ opacity: opacity,
42622
42725
  r: radii.shift()
42623
42726
  });
42624
42727
  }
@@ -42627,6 +42730,7 @@ ${svg}
42627
42730
  type: 'circle',
42628
42731
  fill: 'none', // TODO remove default black fill so this is not needed
42629
42732
  stroke: color,
42733
+ opacity: opacity,
42630
42734
  'stroke-width': roundToTenths(radii[i+1] - radii[i]),
42631
42735
  r: roundToTenths(radii[i+1] * 0.5 + radii[i] * 0.5)
42632
42736
  });
@@ -42674,19 +42778,18 @@ ${svg}
42674
42778
  if (geojsonType == 'MultiPolygon' || geojsonType == 'Polygon') {
42675
42779
  sym = {
42676
42780
  type: 'polygon',
42677
- fill: getSymbolFillColor(properties),
42678
42781
  coordinates: geojsonType == 'Polygon' ? coords : flattenMultiPolygonCoords(coords)
42679
42782
  };
42680
42783
  } else if (geojsonType == 'LineString' || geojsonType == 'MultiLineString') {
42681
42784
  sym = {
42682
42785
  type: 'polyline',
42683
- stroke: getSymbolStrokeColor(properties),
42684
42786
  'stroke-width': properties['stroke-width'] || 2,
42685
42787
  coordinates: geojsonType == 'LineString' ? [coords] : coords
42686
42788
  };
42687
42789
  } else {
42688
42790
  error('Unsupported type:', geojsonType);
42689
42791
  }
42792
+ applySymbolStyles(sym, properties);
42690
42793
  roundCoordsForSVG(sym.coordinates);
42691
42794
  return sym;
42692
42795
  }
@@ -42746,6 +42849,7 @@ ${svg}
42746
42849
  scaleAndShiftCoords(coords, metersPerPx, shp[0]);
42747
42850
  if (d.fill) rec.fill = d.fill;
42748
42851
  if (d.stroke) rec.stroke = d.stroke;
42852
+ if (d.opacity) rec.opacity = d.opacity;
42749
42853
  return createGeometry(coords, geojsonType);
42750
42854
  } else {
42751
42855
  rec['svg-symbol'] = makePathSymbol(coords, d, geojsonType);
@@ -42811,9 +42915,41 @@ ${svg}
42811
42915
  // TODO: improve this
42812
42916
  targets[0].layers[0].name = opts.name;
42813
42917
  }
42918
+ if (opts.combine) {
42919
+ targets = combineTargets(targets, catalog);
42920
+ } else if (opts.isolate) {
42921
+ targets = isolateTargets(targets, catalog);
42922
+ }
42814
42923
  catalog.setDefaultTargets(targets);
42815
42924
  };
42816
42925
 
42926
+ function combineTargets(targets, catalog) {
42927
+ var datasets = [];
42928
+ var layers = [];
42929
+ targets.forEach(function(o) {
42930
+ datasets.push(o.dataset);
42931
+ catalog.removeDataset(o.dataset);
42932
+ layers = layers.concat(o.layers);
42933
+ });
42934
+ var combined = mergeDatasets(datasets);
42935
+ catalog.addDataset(combined);
42936
+ return [{
42937
+ dataset: combined,
42938
+ layers: layers
42939
+ }];
42940
+ }
42941
+
42942
+ function isolateTargets(targets, catalog) {
42943
+ var datasets = [];
42944
+ targets.forEach(function(o) {
42945
+ datasets.push(o.dataset);
42946
+ o.layers.forEach(function(lyr) {
42947
+ catalog.removeLayer(lyr, o.dataset);
42948
+
42949
+ });
42950
+ });
42951
+ }
42952
+
42817
42953
  cmd.union = function(targetLayers, targetDataset, opts) {
42818
42954
  if (targetLayers.length < 2) {
42819
42955
  stop('Command requires at least two target layers');
@@ -43198,7 +43334,7 @@ ${svg}
43198
43334
  function commandAcceptsMultipleTargetDatasets(name) {
43199
43335
  return name == 'rotate' || name == 'info' || name == 'proj' ||
43200
43336
  name == 'drop' || name == 'target' || name == 'if' || name == 'elif' ||
43201
- name == 'else' || name == 'endif' || name == 'run';
43337
+ name == 'else' || name == 'endif' || name == 'run' || name == 'i';
43202
43338
  }
43203
43339
 
43204
43340
  function commandAcceptsEmptyTarget(name) {
@@ -43387,8 +43523,8 @@ ${svg}
43387
43523
  job.catalog.addDataset(cmd.graticule(targetDataset, opts));
43388
43524
 
43389
43525
  } else if (name == 'help') {
43390
- // placing this here to handle errors from invalid command names
43391
- getOptionParser().printHelp(command.options.command);
43526
+ // placing help command here to handle errors from invalid command names
43527
+ cmd.printHelp(command.options);
43392
43528
 
43393
43529
  } else if (name == 'i') {
43394
43530
  if (opts.replace) job.catalog = new Catalog(); // is this what we want?
@@ -43470,7 +43606,7 @@ ${svg}
43470
43606
  cmd.print(command._.join(' '));
43471
43607
 
43472
43608
  } else if (name == 'proj') {
43473
- await utils.promisify(initProjLibrary)(opts);
43609
+ await initProjLibrary(opts);
43474
43610
  job.resumeCommand();
43475
43611
  targets.forEach(function(targ) {
43476
43612
  cmd.proj(targ.dataset, job.catalog, opts);
@@ -44305,6 +44441,7 @@ ${svg}
44305
44441
  Heap,
44306
44442
  NodeCollection,
44307
44443
  parseDMS,
44444
+ formatDMS,
44308
44445
  PathIndex,
44309
44446
  PolygonIndex,
44310
44447
  ShpReader,