mapshaper 0.7.11 → 0.7.13

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
@@ -17490,7 +17490,7 @@
17490
17490
  // higher-level mapshaper-svg.mjs (which would otherwise form a cycle).
17491
17491
 
17492
17492
  function featureHasSvgSymbol(d) {
17493
- return !!(d && (d['svg-symbol'] || d.r));
17493
+ return !!(d && (d['svg-symbol'] || d.r || d.icon || d['icon-size']));
17494
17494
  }
17495
17495
 
17496
17496
  function featureHasLabel(d) {
@@ -17763,6 +17763,9 @@
17763
17763
  'font-style': null,
17764
17764
  'font-stretch': null,
17765
17765
  'font-weight': null,
17766
+ icon: null,
17767
+ 'icon-color': 'color',
17768
+ 'icon-size': 'number',
17766
17769
  'label-text': null, // leaving this null
17767
17770
  'letter-spacing': 'measure',
17768
17771
  'line-height': 'measure',
@@ -18110,12 +18113,44 @@
18110
18113
  if (d['svg-symbol']) {
18111
18114
  return renderComplexSymbol(d['svg-symbol']);
18112
18115
  }
18116
+ if (featureHasIcon(d)) {
18117
+ return renderIcon(d);
18118
+ }
18113
18119
  if (d.r > 0) {
18114
18120
  return circle(d);
18115
18121
  }
18116
18122
  return empty();
18117
18123
  }
18118
18124
 
18125
+ function featureHasIcon(d) {
18126
+ return !!(d && (d.icon || d['icon-size'] || (d['icon-color'] && d.r > 0)));
18127
+ }
18128
+
18129
+ function renderIcon(d) {
18130
+ var type = d.icon || 'circle';
18131
+ var r = getIconRadius(d);
18132
+ if (r > 0 === false) return empty();
18133
+ if (type == 'circle') return circle(getIconStyleData(d, r));
18134
+ if (type == 'square') return square(getIconStyleData(d, r), 0, 0);
18135
+ if (type == 'ring') return ring(getIconStyleData(d, r), 0, 0);
18136
+ if (type == 'star') return star(getIconStyleData(d, r));
18137
+ message('Unknown icon type: ' + type);
18138
+ return empty();
18139
+ }
18140
+
18141
+ function getIconRadius(d) {
18142
+ if (d['icon-size'] > 0) return d['icon-size'] / 2;
18143
+ if (d.r > 0) return d.r;
18144
+ return 5;
18145
+ }
18146
+
18147
+ function getIconStyleData(d, r) {
18148
+ var o = utils.extend({}, d);
18149
+ o.r = r;
18150
+ o.fill = d['icon-color'] || d.fill || 'black';
18151
+ return o;
18152
+ }
18153
+
18119
18154
  function renderComplexSymbol(sym, x, y) {
18120
18155
  if (utils.isString(sym)) {
18121
18156
  sym = JSON.parse(sym);
@@ -18190,10 +18225,44 @@
18190
18225
  height: r * 2
18191
18226
  }
18192
18227
  };
18193
- applyStyleAttributes(o, 'point', d);
18228
+ applyStyleAttributes(o, 'point', d, nonCirclePointFilter);
18229
+ return o;
18230
+ }
18231
+
18232
+ function ring(d, x, y) {
18233
+ var o = circle(d, x, y);
18234
+ o.properties.fill = 'none';
18235
+ o.properties.stroke = d.fill;
18236
+ if (!o.properties['stroke-width']) {
18237
+ o.properties['stroke-width'] = 1;
18238
+ }
18239
+ return o;
18240
+ }
18241
+
18242
+ function star(d) {
18243
+ var coords = getStarCoords$1(d.r);
18244
+ var o = importPolygon([coords]);
18245
+ applyStyleAttributes(o, 'point', d, nonCirclePointFilter);
18194
18246
  return o;
18195
18247
  }
18196
18248
 
18249
+ function nonCirclePointFilter(k) {
18250
+ return k != 'r';
18251
+ }
18252
+
18253
+ function getStarCoords$1(r) {
18254
+ var coords = [];
18255
+ var innerR = r * 0.42;
18256
+ var angle, len;
18257
+ for (var i=0; i<10; i++) {
18258
+ len = i % 2 === 0 ? r : innerR;
18259
+ angle = -Math.PI / 2 + i * Math.PI / 5;
18260
+ coords.push([roundToTenths(Math.cos(angle) * len), roundToTenths(Math.sin(angle) * len)]);
18261
+ }
18262
+ coords.push(coords[0].concat());
18263
+ return coords;
18264
+ }
18265
+
18197
18266
  function line(d, x, y) {
18198
18267
  var coords, o;
18199
18268
  coords = [[x, y], [x + (d.dx || 0), y + (d.dy || 0)]];
@@ -18899,7 +18968,7 @@
18899
18968
 
18900
18969
  // just a dot, no label or icon
18901
18970
  function isSimpleCircle(rec) {
18902
- return rec && (rec.r > 0 && !rec['svg-symbol'] && !rec['label-text']);
18971
+ return rec && (rec.r > 0 && !rec['svg-symbol'] && !rec['label-text'] && !rec.icon && !rec['icon-size'] && !rec['icon-color']);
18903
18972
  }
18904
18973
 
18905
18974
  function importMultiPoint(coords, rec) {
@@ -18927,6 +18996,8 @@
18927
18996
  importPolygon: importPolygon
18928
18997
  });
18929
18998
 
18999
+ var ILLUSTRATOR_PATH_VERTEX_LIMIT = 32000;
19000
+
18930
19001
  //
18931
19002
  function exportSVG(dataset, opts) {
18932
19003
  var namespace = 'xmlns="http://www.w3.org/2000/svg"';
@@ -19091,6 +19162,7 @@ ${svg}
19091
19162
  var d = utils.defaults({layers: [lyr]}, dataset);
19092
19163
  var geojson = exportDatasetAsGeoJSON(d, opts);
19093
19164
  var features = geojson.features || geojson.geometries || (geojson.type ? [geojson] : []);
19165
+ warnIfIllustratorPathLimitExceeded(lyr, features);
19094
19166
  var children = importGeoJSONFeatures(features, opts);
19095
19167
  // Drop empty placeholder <g/> elements (features whose geometry was null in the
19096
19168
  // source data, collapsed during simplification, or otherwise produced no
@@ -19109,6 +19181,53 @@ ${svg}
19109
19181
  return children;
19110
19182
  }
19111
19183
 
19184
+ function warnIfIllustratorPathLimitExceeded(lyr, features) {
19185
+ var count = 0;
19186
+ var max = 0;
19187
+ if (lyr.geometry_type != 'polygon' && lyr.geometry_type != 'polyline') return;
19188
+ features.forEach(function(feature) {
19189
+ var geom = feature.geometry || feature;
19190
+ var n = countSvgPathVertices(geom);
19191
+ if (n > ILLUSTRATOR_PATH_VERTEX_LIMIT) {
19192
+ count++;
19193
+ max = Math.max(max, n);
19194
+ }
19195
+ });
19196
+ if (count > 0) {
19197
+ warn(utils.format(
19198
+ '%,d SVG path%s in layer "%s" contain%s more than %,d vertices; Adobe Illustrator may not import %s. The largest path has %,d vertices.',
19199
+ count,
19200
+ utils.pluralSuffix(count),
19201
+ lyr.name || '[unnamed]',
19202
+ count == 1 ? 's' : '',
19203
+ ILLUSTRATOR_PATH_VERTEX_LIMIT,
19204
+ count == 1 ? 'it' : 'them',
19205
+ max
19206
+ ));
19207
+ }
19208
+ }
19209
+
19210
+ function countSvgPathVertices(geom) {
19211
+ var coords = geom && geom.coordinates;
19212
+ if (!coords) return 0;
19213
+ if (geom.type == 'LineString') return coords.length;
19214
+ if (geom.type == 'MultiLineString' || geom.type == 'Polygon') {
19215
+ return sumPathLengths(coords);
19216
+ }
19217
+ if (geom.type == 'MultiPolygon') {
19218
+ return coords.reduce(function(sum, polygon) {
19219
+ return sum + sumPathLengths(polygon);
19220
+ }, 0);
19221
+ }
19222
+ return 0;
19223
+ }
19224
+
19225
+ function sumPathLengths(paths) {
19226
+ return paths.reduce(function(sum, path) {
19227
+ return sum + path.length;
19228
+ }, 0);
19229
+ }
19230
+
19112
19231
  function isEmptyPlaceholder(o) {
19113
19232
  return o.tag == 'g' && (!o.children || o.children.length === 0);
19114
19233
  }
@@ -19206,7 +19325,7 @@ ${svg}
19206
19325
  // TODO: set fill="none" in SVG symbols, not on the container
19207
19326
  // (setting fill=none on the container overrides the default black fill
19208
19327
  // on paths, which may alter the appearance of SVG icons loaded from external URLs).
19209
- if (lyr.geometry_type == 'polyline' || layerHasSvgSymbols(lyr)) {
19328
+ if (lyr.geometry_type == 'polyline' || layerHasSvgSymbolField(lyr)) {
19210
19329
  layerObj.properties.fill = 'none';
19211
19330
  }
19212
19331
 
@@ -19229,6 +19348,15 @@ ${svg}
19229
19348
  }
19230
19349
 
19231
19350
  function layerHasSvgSymbols(lyr) {
19351
+ return lyr.geometry_type == 'point' && lyr.data && (
19352
+ lyr.data.fieldExists('svg-symbol') ||
19353
+ lyr.data.fieldExists('icon') ||
19354
+ lyr.data.fieldExists('icon-size') ||
19355
+ (lyr.data.fieldExists('icon-color') && lyr.data.fieldExists('r'))
19356
+ );
19357
+ }
19358
+
19359
+ function layerHasSvgSymbolField(lyr) {
19232
19360
  return lyr.geometry_type == 'point' && lyr.data && lyr.data.fieldExists('svg-symbol');
19233
19361
  }
19234
19362
 
@@ -28250,7 +28378,7 @@ ${svg}
28250
28378
 
28251
28379
  };
28252
28380
 
28253
- this.getHelpMessage = function(cmdName) {
28381
+ this.getHelpLines = function(cmdName) {
28254
28382
  var helpCommands, singleCommand, lines;
28255
28383
  if (cmdName) {
28256
28384
  singleCommand = findCommandDefn(cmdName, getCommands());
@@ -28262,113 +28390,115 @@ ${svg}
28262
28390
  helpCommands = getCommands().filter(function(cmd) {return cmd.name && cmd.describe || cmd.title;});
28263
28391
  lines = getMultiCommandLines(helpCommands);
28264
28392
  }
28393
+ return lines;
28394
+ };
28265
28395
 
28266
- return formatLines(lines);
28267
-
28268
- function formatLines(lines) {
28269
- var colWidth = calcColWidth(lines);
28270
- var gutter = ' ';
28271
- var indent = runningInBrowser() ? '' : ' ';
28272
- var helpStr = lines.map(function(line) {
28273
- if (Array.isArray(line)) {
28274
- line = indent + utils.rpad(line[0], colWidth, ' ') + gutter + line[1];
28275
- }
28276
- return line;
28277
- }).join('\n');
28278
- return helpStr;
28279
- }
28280
-
28281
- function getSingleCommandLines(cmd) {
28282
- var lines = [];
28283
- var options = [];
28284
- cmd.options.forEach(function(opt) {
28285
- options = options.concat(getOptionLines(opt));
28286
- });
28396
+ this.getHelpMessage = function(cmdName) {
28397
+ return formatLines(this.getHelpLines(cmdName));
28398
+ };
28287
28399
 
28288
- lines.push('COMMAND', getCommandLine(cmd));
28289
- if (options.length > 0) {
28290
- lines.push('', 'OPTIONS');
28291
- lines = lines.concat(options);
28400
+ function formatLines(lines) {
28401
+ var colWidth = calcColWidth(lines);
28402
+ var gutter = ' ';
28403
+ var indent = runningInBrowser() ? '' : ' ';
28404
+ var helpStr = lines.map(function(line) {
28405
+ if (Array.isArray(line)) {
28406
+ line = indent + utils.rpad(line[0], colWidth, ' ') + gutter + line[1];
28292
28407
  }
28408
+ return line;
28409
+ }).join('\n');
28410
+ return helpStr;
28411
+ }
28293
28412
 
28294
- // examples
28295
- if (cmd.examples) {
28296
- lines.push('', 'EXAMPLE' + (cmd.examples.length > 1 ? 'S' : ''));
28297
- cmd.examples.forEach(function(ex, i) {
28298
- if (i > 0) lines.push('');
28299
- ex.split('\n').forEach(function(line, i) {
28300
- lines.push(' ' + line);
28301
- });
28302
- });
28303
- }
28304
- return lines;
28413
+ function getSingleCommandLines(cmd) {
28414
+ var lines = [];
28415
+ var options = [];
28416
+ cmd.options.forEach(function(opt) {
28417
+ options = options.concat(getOptionLines(opt));
28418
+ });
28419
+
28420
+ lines.push('COMMAND', getCommandLine(cmd));
28421
+ if (options.length > 0) {
28422
+ lines.push('', 'OPTIONS');
28423
+ lines = lines.concat(options);
28305
28424
  }
28306
28425
 
28307
- function getOptionLines(opt, cmd) {
28308
- var lines = [];
28309
- var description = opt.describe;
28310
- var label;
28311
- if (!description) ; else if (opt.label) {
28312
- lines.push([opt.label, description]);
28313
- } else if (opt.DEFAULT) {
28314
- label = opt.name + '=';
28315
- lines.push(['<' + opt.name + '>', 'shortcut for ' + label]);
28316
- lines.push([label, description]);
28317
- } else {
28318
- label = opt.name;
28319
- if (opt.alias) label += ', ' + opt.alias;
28320
- if (opt.type != 'flag' && !opt.assign_to) label += '=';
28321
- lines.push([label, description]);
28322
- }
28323
- return lines;
28426
+ // examples
28427
+ if (cmd.examples) {
28428
+ lines.push('', 'EXAMPLE' + (cmd.examples.length > 1 ? 'S' : ''));
28429
+ cmd.examples.forEach(function(ex, i) {
28430
+ if (i > 0) lines.push('');
28431
+ ex.split('\n').forEach(function(line, i) {
28432
+ lines.push(' ' + line);
28433
+ });
28434
+ });
28324
28435
  }
28436
+ return lines;
28437
+ }
28325
28438
 
28326
- function getCommandLine(cmd) {
28327
- var name = cmd.name ? "-" + cmd.name : '';
28328
- if (cmd.alias) name += ', -' + cmd.alias;
28329
- return [name, cmd.describe || '(undocumented command)'];
28439
+ function getOptionLines(opt, cmd) {
28440
+ var lines = [];
28441
+ var description = opt.describe;
28442
+ var label;
28443
+ if (!description) ; else if (opt.label) {
28444
+ lines.push([opt.label, description]);
28445
+ } else if (opt.DEFAULT) {
28446
+ label = opt.name + '=';
28447
+ lines.push(['<' + opt.name + '>', 'shortcut for ' + label]);
28448
+ lines.push([label, description]);
28449
+ } else {
28450
+ label = opt.name;
28451
+ if (opt.alias) label += ', ' + opt.alias;
28452
+ if (opt.type != 'flag' && !opt.assign_to) label += '=';
28453
+ lines.push([label, description]);
28330
28454
  }
28455
+ return lines;
28456
+ }
28331
28457
 
28332
- function getMultiCommandLines(commands) {
28333
- var lines = [];
28334
- // usage
28335
- if (_usage) lines.push(_usage);
28458
+ function getCommandLine(cmd) {
28459
+ var name = cmd.name ? "-" + cmd.name : '';
28460
+ if (cmd.alias) name += ', -' + cmd.alias;
28461
+ return [name, cmd.describe || '(undocumented command)'];
28462
+ }
28336
28463
 
28337
- // list of commands
28338
- commands.forEach(function(cmd) {
28339
- if (cmd.title) {
28340
- lines.push('', cmd.title);
28341
- } else {
28342
- lines.push(getCommandLine(cmd));
28343
- }
28344
- });
28464
+ function getMultiCommandLines(commands) {
28465
+ var lines = [];
28466
+ // usage
28467
+ if (_usage) lines.push(_usage);
28345
28468
 
28346
- // examples
28347
- if (_examples.length > 0) {
28348
- lines.push('', 'Examples');
28349
- _examples.forEach(function(str) {
28350
- lines.push('', str);
28351
- });
28469
+ // list of commands
28470
+ commands.forEach(function(cmd) {
28471
+ if (cmd.title) {
28472
+ lines.push('', cmd.title);
28473
+ } else {
28474
+ lines.push(getCommandLine(cmd));
28352
28475
  }
28476
+ });
28353
28477
 
28354
- // note
28355
- if (_note) {
28356
- lines.push('', _note);
28357
- }
28358
- return lines;
28478
+ // examples
28479
+ if (_examples.length > 0) {
28480
+ lines.push('', 'Examples');
28481
+ _examples.forEach(function(str) {
28482
+ lines.push('', str);
28483
+ });
28359
28484
  }
28360
28485
 
28361
-
28362
- function calcColWidth(lines) {
28363
- var w = 0;
28364
- lines.forEach(function(line) {
28365
- if (Array.isArray(line)) {
28366
- w = Math.max(w, line[0].length);
28367
- }
28368
- });
28369
- return w;
28486
+ // note
28487
+ if (_note) {
28488
+ lines.push('', _note);
28370
28489
  }
28371
- };
28490
+ return lines;
28491
+ }
28492
+
28493
+ function calcColWidth(lines) {
28494
+ var w = 0;
28495
+ lines.forEach(function(line) {
28496
+ if (Array.isArray(line)) {
28497
+ w = Math.max(w, line[0].length);
28498
+ }
28499
+ });
28500
+ return w;
28501
+ }
28372
28502
 
28373
28503
  function getCommands() {
28374
28504
  return _commands.map(function(cmd) {
@@ -30488,6 +30618,15 @@ ${svg}
30488
30618
  .option('r', {
30489
30619
  describe: 'symbol radius (set this to export points as circles)',
30490
30620
  })
30621
+ .option('icon', {
30622
+ describe: 'point icon shape; one of: circle, square, ring, star'
30623
+ })
30624
+ .option('icon-size', {
30625
+ describe: 'point icon size in pixels'
30626
+ })
30627
+ .option('icon-color', {
30628
+ describe: 'point icon color (defaults to fill color, then black)'
30629
+ })
30491
30630
  .option('label-text', {
30492
30631
  describe: 'label text (set this to export points as labels)'
30493
30632
  })
@@ -44055,7 +44194,14 @@ ${svg}
44055
44194
  }]
44056
44195
  };
44057
44196
  }
44058
- message(formatInfo(arr));
44197
+ if (runningInBrowser()) {
44198
+ message({
44199
+ type: 'mapshaper-console-info',
44200
+ layers: arr
44201
+ });
44202
+ } else {
44203
+ message(formatInfo(arr));
44204
+ }
44059
44205
  };
44060
44206
 
44061
44207
  cmd.printInfo = cmd.info; // old name
@@ -48764,7 +48910,7 @@ ${svg}
48764
48910
  return layerHasGeometry(lyr) && !layerIsFullyEnclosed(lyr, dataset, clipData);
48765
48911
  });
48766
48912
  if (layers.length > 0) {
48767
- clipLayersInPlace(layers, clipData, dataset, 'clip');
48913
+ clipLayersInPlace(layers, clipData, dataset, 'clip', getInternalClipOpts());
48768
48914
  return true;
48769
48915
  }
48770
48916
  return false;
@@ -48803,13 +48949,21 @@ ${svg}
48803
48949
  }
48804
48950
 
48805
48951
  function datasetCrossesLon(dataset, lon) {
48806
- var crosses = 0;
48807
- dataset.arcs.forEachSegment(function(i, j, xx, yy) {
48808
- var ax = xx[i],
48809
- bx = xx[j];
48810
- if (ax <= lon && bx >= lon || ax >= lon && bx <= lon) crosses++;
48952
+ var crosses = false;
48953
+ dataset.layers.filter(layerHasPaths).forEach(function(lyr) {
48954
+ if (crosses) return;
48955
+ lyr.shapes.forEach(function(shp) {
48956
+ if (crosses || !shp) return;
48957
+ forEachSegmentInShape(shp, dataset.arcs, function(i, j, xx, yy) {
48958
+ var ax = xx[i],
48959
+ bx = xx[j];
48960
+ if (ax <= lon && bx >= lon || ax >= lon && bx <= lon) {
48961
+ crosses = true;
48962
+ }
48963
+ });
48964
+ });
48811
48965
  });
48812
- return crosses > 0;
48966
+ return crosses;
48813
48967
  }
48814
48968
 
48815
48969
  function insertVerticalCut(dataset, lon) {
@@ -48820,29 +48974,38 @@ ${svg}
48820
48974
  // densify (so cut line can curve, e.g. Cupola projection)
48821
48975
  var geojson = bboxToPolygon(bbox, {interval: 0.5});
48822
48976
  var clip = importGeoJSON(geojson);
48823
- clipLayersInPlace(pathLayers, clip, dataset, 'erase');
48977
+ clipLayersInPlace(pathLayers, clip, dataset, 'erase', getInternalClipOpts());
48824
48978
  }
48825
48979
 
48826
- // Converts a Proj.4 projection name (e.g. lcc, tmerc) to a Proj.4 string
48980
+ function getInternalClipOpts() {
48981
+ return {no_cleanup: true, no_warn: true};
48982
+ }
48983
+
48984
+ // Converts a Proj.4 projection name (e.g. lcc, tmerc, utm) to a Proj.4 string
48827
48985
  // by picking parameters that are appropriate to the extent of the dataset
48828
48986
  // being projected (e.g. standard parallels, longitude of origin)
48829
- // Works for lcc, aea, tmerc, etc.
48987
+ // Works for lcc, aea, tmerc, utm, etc.
48830
48988
  // TODO: add more projections
48831
48989
  //
48832
48990
  function expandProjDefn(str, dataset, targetLayers) {
48833
48991
  var mproj = require$1('mproj');
48834
- var proj4, params, bbox, isConic2SP, isCentered, decimals;
48992
+ var proj4, params, bbox, isConic2SP, isCentered, isUtm, decimals;
48835
48993
  if (str in mproj.internal.pj_list === false) {
48836
48994
  // not a bare projection code -- assume valid projection string in other format
48837
48995
  return str;
48838
48996
  }
48839
48997
  isConic2SP = ['lcc', 'aea'].includes(str);
48840
48998
  isCentered = ['tmerc', 'etmerc'].includes(str);
48999
+ isUtm = str == 'utm';
48841
49000
  proj4 = '+proj=' + str;
48842
- if (isConic2SP || isCentered) {
49001
+ if (isConic2SP || isCentered || isUtm) {
48843
49002
  bbox = getBBox(dataset, targetLayers); // TODO: support projected datasets
48844
49003
  decimals = getBoundsPrecisionForDisplay(bbox);
48845
- params = isCentered ? getCenterParams(bbox, decimals) : getConicParams(bbox, decimals);
49004
+ if (isUtm) {
49005
+ params = getUtmParams(bbox);
49006
+ } else {
49007
+ params = isCentered ? getCenterParams(bbox, decimals) : getConicParams(bbox, decimals);
49008
+ }
48846
49009
  proj4 += ' ' + params;
48847
49010
  message(`Converted "${str}" to "${proj4}"`);
48848
49011
  }
@@ -48858,7 +49021,77 @@ ${svg}
48858
49021
  if (!isLatLngCRS(getDatasetCRS(dataset))) {
48859
49022
  stop$1('Expected unprojected data');
48860
49023
  }
48861
- return getDatasetBounds(source).toArray();
49024
+ return getAutoFitBBox(source);
49025
+ }
49026
+
49027
+ function getAutoFitBBox(dataset) {
49028
+ var bbox = getDatasetBounds(dataset).toArray();
49029
+ if (bbox[2] - bbox[0] > 180) {
49030
+ bbox = getWrappedBBox(dataset, bbox) || bbox;
49031
+ }
49032
+ return bbox;
49033
+ }
49034
+
49035
+ function getWrappedBBox(dataset, bbox) {
49036
+ var xmaxW = -Infinity,
49037
+ xminE = Infinity, xmaxE = -Infinity,
49038
+ ymin = bbox[1], ymax = bbox[3],
49039
+ gap;
49040
+
49041
+ // Detect dateline clustering with a streaming east/west split. The gap
49042
+ // between the two boxes can only shrink as more coordinates are scanned.
49043
+ forEachDatasetCoord(dataset, function(x) {
49044
+ if (x < 0) {
49045
+ if (x > xmaxW) xmaxW = x;
49046
+ } else {
49047
+ if (x < xminE) xminE = x;
49048
+ if (x > xmaxE) xmaxE = x;
49049
+ }
49050
+ if (xmaxW > -Infinity && xmaxE > -Infinity) {
49051
+ gap = xminE - xmaxW;
49052
+ return gap > 180;
49053
+ }
49054
+ });
49055
+
49056
+ if (xmaxW == -Infinity || xmaxE == -Infinity) return null;
49057
+ if (xminE - xmaxW <= 180) return null;
49058
+ return [xminE, ymin, xmaxW + 360, ymax];
49059
+ }
49060
+
49061
+ function forEachDatasetCoord(dataset, cb) {
49062
+ var arcs = dataset.arcs;
49063
+ var usedArcs = arcs ? new Uint8Array(arcs.size()) : null;
49064
+ var keepGoing = true;
49065
+ dataset.layers.forEach(function(lyr) {
49066
+ if (!keepGoing) return;
49067
+ if (lyr.geometry_type == 'point') {
49068
+ keepGoing = scanPointCoords(lyr.shapes, cb);
49069
+ } else if (arcs && (lyr.geometry_type == 'polygon' || lyr.geometry_type == 'polyline')) {
49070
+ forEachArcId(lyr.shapes, function(id) {
49071
+ var absId, iter;
49072
+ if (!keepGoing) return;
49073
+ absId = id < 0 ? ~id : id;
49074
+ if (usedArcs[absId]) return;
49075
+ usedArcs[absId] = 1;
49076
+ iter = arcs.getArcIter(absId);
49077
+ while (keepGoing && iter.hasNext()) {
49078
+ keepGoing = cb(iter.x, iter.y) !== false;
49079
+ }
49080
+ });
49081
+ }
49082
+ });
49083
+ }
49084
+
49085
+ function scanPointCoords(shapes, cb) {
49086
+ var shp, p;
49087
+ for (var i=0, n=shapes.length; i<n; i++) {
49088
+ shp = shapes[i];
49089
+ for (var j=0, m=shp ? shp.length : 0; j<m; j++) {
49090
+ p = shp[j];
49091
+ if (cb(p[0], p[1]) === false) return false;
49092
+ }
49093
+ }
49094
+ return true;
48862
49095
  }
48863
49096
 
48864
49097
  // See: Savric & Jenny, "Automating the selection of standard parallels for conic map projections"
@@ -48877,11 +49110,21 @@ ${svg}
48877
49110
  return `+lon_0=${ cx.toFixed(decimals) } +lat_0=${ cy.toFixed(decimals) }`;
48878
49111
  }
48879
49112
 
49113
+ function getUtmParams(bbox) {
49114
+ var cx = (bbox[0] + bbox[2]) / 2;
49115
+ var cy = (bbox[1] + bbox[3]) / 2;
49116
+ var zone = Math.floor((cx + 180) / 6) + 1;
49117
+ zone = Math.max(1, Math.min(60, zone));
49118
+ return `+zone=${ zone }` + (cy < 0 ? ' +south' : '');
49119
+ }
49120
+
48880
49121
  var ProjectionParams = /*#__PURE__*/Object.freeze({
48881
49122
  __proto__: null,
48882
49123
  expandProjDefn: expandProjDefn,
49124
+ getAutoFitBBox: getAutoFitBBox,
48883
49125
  getCenterParams: getCenterParams,
48884
- getConicParams: getConicParams
49126
+ getConicParams: getConicParams,
49127
+ getUtmParams: getUtmParams
48885
49128
  });
48886
49129
 
48887
49130
  cmd.proj = function(dataset, catalog, opts, targetLayers) {
@@ -49280,8 +49523,15 @@ ${svg}
49280
49523
  }
49281
49524
 
49282
49525
  cmd.printHelp = function(opts) {
49283
- var str = getOptionParser().getHelpMessage(opts.command);
49284
- print(str);
49526
+ var parser = getOptionParser();
49527
+ if (runningInBrowser()) {
49528
+ message({
49529
+ type: 'mapshaper-console-help',
49530
+ lines: parser.getHelpLines(opts.command)
49531
+ });
49532
+ } else {
49533
+ print(parser.getHelpMessage(opts.command));
49534
+ }
49285
49535
  };
49286
49536
 
49287
49537
  function stopJob(job) {
@@ -55431,7 +55681,7 @@ ${svg}
55431
55681
  });
55432
55682
  }
55433
55683
 
55434
- var version = "0.7.11";
55684
+ var version = "0.7.13";
55435
55685
 
55436
55686
  // Parse command line args into commands and run them
55437
55687
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.7.11",
3
+ "version": "0.7.13",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -28,10 +28,10 @@
28
28
  "docs": "node build-docs.mjs",
29
29
  "roadmap": "node build-roadmap.mjs",
30
30
  "lint": "eslint --ext mjs src/",
31
- "prepublishOnly": "npm test; ./pre-publish",
31
+ "prepublishOnly": "npm test && npm run test:browser && ./pre-publish",
32
32
  "postpublish": "./release_web_ui; ./release_github_version",
33
33
  "dev": "rollup --config --watch",
34
- "test:browser": "playwright test"
34
+ "test:browser": "playwright test --fully-parallel --workers=6"
35
35
  },
36
36
  "main": "./mapshaper.js",
37
37
  "files": [