mapshaper 0.6.39 → 0.6.40

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.39";
3
+ var VERSION = "0.6.40";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -13142,17 +13142,23 @@
13142
13142
  var rxp = fmt;
13143
13143
  rxp = rxp.replace('[-]', '(?<prefix>-)?'); // optional -
13144
13144
  rxp = rxp.replace(/\[[NSEW, +-]{2,}\]/, '(?<prefix>$&)');
13145
+ // TODO: validate that if there are degree decimals, there are no M or S codes
13146
+ rxp = rxp.replace(/D+(\.D+)?/, (m, g1) => {
13147
+ var s = '[0-9]+';
13148
+ if (g1) s += `\\.[0-9]+`;
13149
+ return `(?<d>${s})`;
13150
+ });
13151
+ // TODO: validate that if there are minutes decimals, there are no S codes
13145
13152
  rxp = rxp.replace(/(MM?)(\.M+)?/, (m, g1, g2) => {
13146
13153
  var s = g1.length == 1 ? '[0-9]+' : '[0-9][0-9]';
13147
- if (g2) s += `\\.[0-9]+`;
13154
+ if (g2) s += '\\.[0-9]+';
13148
13155
  return `(?<m>${s})`;
13149
13156
  });
13150
13157
  rxp = rxp.replace(/(SS?)(\.S+)?/, (m, g1, g2) => {
13151
13158
  var s = g1.length == 1 ? '[0-9]+' : '[0-9][0-9]';
13152
- if (g2) s += `\\.[0-9]+`;
13159
+ if (g2) s += '\\.[0-9]+';
13153
13160
  return `(?<s>${s})`;
13154
13161
  });
13155
- rxp = rxp.replace(/D+/, '(?<d>[0-9]+)');
13156
13162
  rxp = '^' + rxp + '$';
13157
13163
  try {
13158
13164
  // TODO: make sure all DMS codes have been matched
@@ -13173,43 +13179,42 @@
13173
13179
  return str;
13174
13180
  }
13175
13181
 
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
13182
  function formatDMS(coord, fmt) {
13183
13183
  if (!fmt) fmt = '[-]D°M\'S.SSS';
13184
13184
  var str = fmt;
13185
13185
  var dstr, mstr, sstr;
13186
- var match = /(D+)[^M]*(M+)[^S[\]]*(S+)?/.exec(fmt);
13187
- if (!match) {
13186
+ var match = /(D+)[^M]*(M+)?[^S[\]]*(S+)?/.exec(fmt);
13187
+ var gotSeconds = match && !!match[3];
13188
+ var gotMinutes = match && !!match[2];
13189
+ if (!match || gotSeconds && !gotMinutes) {
13188
13190
  stop('Invalid DMS format string:', fmt);
13189
13191
  }
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');
13192
+ var integers = gotSeconds && match[3].length || gotMinutes && match[2].length || match[1].length;
13193
+ var decimalRxp = gotSeconds && /S\.(S+)/ || gotMinutes && /M\.(M+)/ || /D\.(D+)/;
13194
+ var decimals = decimalRxp.test(fmt) ? decimalRxp.exec(fmt)[1].length : 0;
13195
+ if (gotMinutes) {
13196
+ var RES = Math.pow(10, decimals);
13197
+ var CONV = gotSeconds ? 3600 * RES : 60 * RES;
13198
+ var r = Math.floor(Math.abs(coord) * CONV + 0.5);
13199
+ var lastPart = formatNumber((r / RES) % 60, integers, decimals);
13200
+ if (gotSeconds) {
13201
+ r = Math.floor(r / (RES * 60));
13202
+ sstr = lastPart;
13203
+ mstr = String(r % 60).padStart(match[2].length, '0');
13204
+ } else {
13205
+ r = Math.floor(r / RES);
13206
+ mstr = lastPart;
13207
+ }
13208
+ dstr = String(Math.floor(r / 60)).padStart(match[1].length, '0');
13201
13209
  } else {
13202
- r = Math.floor(r / RES);
13203
- mstr = lastPart;
13204
- sstr = '';
13210
+ dstr = Math.abs(coord).toFixed(decimals);
13205
13211
  }
13206
- dstr = String(Math.floor(r / 60)).padStart(match[1].length, '0');
13207
13212
  str = str.replace(/\[-\]/, s => coord < 0 ? '-' : '');
13208
13213
  str = str.replace(/\[[+-]+\]/, s => coord < 0 ? '-' : '+');
13209
13214
  str = str.replace(/\[[NS, ]+\]/, s => coord < 0 ? 'S' : 'N');
13210
13215
  str = str.replace(/\[[EW, ]+\]/, s => coord < 0 ? 'W' : 'E');
13211
- str = str.replace(/D+/, dstr);
13212
- str = str.replace(/M+(\.M+)?/, mstr);
13216
+ str = str.replace(/D+(\.D+)?/, dstr);
13217
+ if (gotMinutes) str = str.replace(/M+(\.M+)?/, mstr);
13213
13218
  if (gotSeconds) str = str.replace(/S+(\.S+)?/, sstr);
13214
13219
  return str;
13215
13220
  }
@@ -17699,12 +17704,18 @@
17699
17704
  }
17700
17705
 
17701
17706
  function calcFrameData(dataset, opts) {
17702
- var bounds = getDatasetBounds(dataset);
17703
- var bounds2 = calcOutputSizeInPixels(bounds, opts);
17707
+ var bounds;
17708
+ if (opts.svg_bbox) {
17709
+ bounds = new Bounds(opts.svg_bbox);
17710
+ opts = Object.assign({margin: 0}, opts); // prevent default pixel margin around content
17711
+ } else {
17712
+ bounds = getDatasetBounds(dataset);
17713
+ }
17714
+ var pixBounds = calcOutputSizeInPixels(bounds, opts);
17704
17715
  return {
17705
17716
  bbox: bounds.toArray(),
17706
- width: Math.round(bounds2.width()),
17707
- height: Math.round(bounds2.height()) || 1,
17717
+ width: Math.round(pixBounds.width()),
17718
+ height: Math.round(pixBounds.height()) || 1,
17708
17719
  type: 'frame'
17709
17720
  };
17710
17721
  }
@@ -23439,6 +23450,10 @@ ${svg}
23439
23450
  describe: '[SVG] source units per pixel (alternative to width= option)',
23440
23451
  type: 'number'
23441
23452
  })
23453
+ .option('svg-bbox', {
23454
+ describe: '[SVG] bounding box of SVG map in projected map units',
23455
+ type: 'bbox'
23456
+ })
23442
23457
  .option('point-symbol', {
23443
23458
  describe: '[SVG] circle or square (default is circle)'
23444
23459
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.6.39",
3
+ "version": "0.6.40",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
package/www/index.html CHANGED
@@ -149,19 +149,27 @@
149
149
  </div>
150
150
  <div class="export-zip-option option-menu">
151
151
 
152
+ <div style="height:10px"></div>
153
+
152
154
  </div>
153
155
  <h4>File format</h4>
154
156
  <div class="export-formats option-menu">
155
157
  </div>
156
158
 
157
- <div class="option-menu"><input type="text" class="advanced-options" placeholder="command line options" />
159
+ <div style="height:10px"></div>
160
+
161
+ <div class="option-menu"><input type="text" class="text-input advanced-options" placeholder="command line options" />
158
162
  <a href="https://github.com/mbloch/mapshaper/wiki/Command-Reference#-o-output" target="_mapshaper_output_docs">
159
163
  <div class="tip-button">?<div class="tip-anchor">
160
164
  <div class="tip">Enter options from the command line interface for
161
165
  the -o command. Examples: bbox no-quantization
162
166
  precision=0.001. Click to see all options.</div></div></div></a>
163
-
164
167
  </div>
168
+
169
+ <!-- <div class="option-menu">
170
+ <input id="ofile-name" class="text-input" type="text" placeholder="output file name" />
171
+ </div> -->
172
+
165
173
  <!-- <div class="cancel-btn btn dialog-btn">Cancel</div> -->
166
174
  <div class="save-btn btn dialog-btn">Export</div>
167
175
  <span id="save-preference"><input type="checkbox"/>choose directory</span>
@@ -288,7 +296,7 @@ apply to TopoJSON files.</div></div></div></div> -->
288
296
 
289
297
  </div>
290
298
 
291
- <div><input type="text" class="advanced-options" placeholder="import options" />
299
+ <div><input type="text" class="text-input advanced-options" placeholder="import options" />
292
300
  <a href="https://github.com/mbloch/mapshaper/wiki/Command-Reference#-i-input" target="_mapshaper_import_docs">
293
301
  <div class="tip-button">?<div class="tip-anchor">
294
302
  <div class="tip">Enter options from the command line
@@ -1265,7 +1265,6 @@
1265
1265
  .onCancel(done);
1266
1266
  }
1267
1267
 
1268
-
1269
1268
  async function saveBlobToSelectedFile(filename, blob, done) {
1270
1269
  // see: https://developer.chrome.com/articles/file-system-access/
1271
1270
  // note: saving multiple files to a directory using showDirectoryPicker()
@@ -1427,7 +1426,6 @@
1427
1426
  // }
1428
1427
 
1429
1428
  snapshots.forEach(function(item, i) {
1430
- var id = i + 1;
1431
1429
  var line = El('div').appendTo(menu).addClass('save-menu-item');
1432
1430
  El('span').appendTo(line).html(`<span class="save-item-label">#${item.number}</span> `);
1433
1431
  // show snapshot size
@@ -1440,7 +1438,10 @@
1440
1438
  var obj = await idb.get(item.id);
1441
1439
  await internal.applyCompression(obj, {consume: true});
1442
1440
  var buf = internal.pack(obj);
1443
- saveBlobToLocalFile('mapshaper_snapshot.msx', new Blob([buf]));
1441
+ // choose output filename and directory every time
1442
+ // saveBlobToLocalFile('mapshaper_snapshot.msx', new Blob([buf]));
1443
+ var fileName = `snapshot-${String(item.number).padStart(2, '0')}.msx`;
1444
+ saveBlobToSelectedFile(fileName, new Blob([buf]), function() {});
1444
1445
  }).text('export');
1445
1446
  El('span').addClass('save-menu-btn').appendTo(line).on('click', async function(e) {
1446
1447
  await removeSnapshotById(item.id);
@@ -3644,6 +3645,7 @@
3644
3645
  var layersArr = [];
3645
3646
  var toggleBtn = null; // checkbox <input> for toggling layer selection
3646
3647
  var exportBtn = gui.container.findChild('.export-btn');
3648
+ var ofileName = gui.container.findChild('#ofile-name');
3647
3649
  new SimpleButton(menu.findChild('.close2-btn')).on('click', gui.clearMode);
3648
3650
 
3649
3651
  if (!GUI.exportIsSupported()) {
@@ -3729,7 +3731,7 @@
3729
3731
  // done: function(string|Error|null)
3730
3732
  async function exportMenuSelection(layers) {
3731
3733
  var opts = getExportOpts();
3732
- // note: command line "target" option gets ignored
3734
+ // note: command line "target" option gets ignored
3733
3735
  var files = await internal.exportTargetLayers(layers, opts);
3734
3736
  gui.session.layersExported(getTargetLayerIds(), getExportOptsAsString());
3735
3737
  await utils$1.promisify(internal.writeFiles)(files, opts);
package/www/mapshaper.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function () {
2
2
 
3
- var VERSION = "0.6.39";
3
+ var VERSION = "0.6.40";
4
4
 
5
5
 
6
6
  var utils = /*#__PURE__*/Object.freeze({
@@ -13142,17 +13142,23 @@
13142
13142
  var rxp = fmt;
13143
13143
  rxp = rxp.replace('[-]', '(?<prefix>-)?'); // optional -
13144
13144
  rxp = rxp.replace(/\[[NSEW, +-]{2,}\]/, '(?<prefix>$&)');
13145
+ // TODO: validate that if there are degree decimals, there are no M or S codes
13146
+ rxp = rxp.replace(/D+(\.D+)?/, (m, g1) => {
13147
+ var s = '[0-9]+';
13148
+ if (g1) s += `\\.[0-9]+`;
13149
+ return `(?<d>${s})`;
13150
+ });
13151
+ // TODO: validate that if there are minutes decimals, there are no S codes
13145
13152
  rxp = rxp.replace(/(MM?)(\.M+)?/, (m, g1, g2) => {
13146
13153
  var s = g1.length == 1 ? '[0-9]+' : '[0-9][0-9]';
13147
- if (g2) s += `\\.[0-9]+`;
13154
+ if (g2) s += '\\.[0-9]+';
13148
13155
  return `(?<m>${s})`;
13149
13156
  });
13150
13157
  rxp = rxp.replace(/(SS?)(\.S+)?/, (m, g1, g2) => {
13151
13158
  var s = g1.length == 1 ? '[0-9]+' : '[0-9][0-9]';
13152
- if (g2) s += `\\.[0-9]+`;
13159
+ if (g2) s += '\\.[0-9]+';
13153
13160
  return `(?<s>${s})`;
13154
13161
  });
13155
- rxp = rxp.replace(/D+/, '(?<d>[0-9]+)');
13156
13162
  rxp = '^' + rxp + '$';
13157
13163
  try {
13158
13164
  // TODO: make sure all DMS codes have been matched
@@ -13173,43 +13179,42 @@
13173
13179
  return str;
13174
13180
  }
13175
13181
 
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
13182
  function formatDMS(coord, fmt) {
13183
13183
  if (!fmt) fmt = '[-]D°M\'S.SSS';
13184
13184
  var str = fmt;
13185
13185
  var dstr, mstr, sstr;
13186
- var match = /(D+)[^M]*(M+)[^S[\]]*(S+)?/.exec(fmt);
13187
- if (!match) {
13186
+ var match = /(D+)[^M]*(M+)?[^S[\]]*(S+)?/.exec(fmt);
13187
+ var gotSeconds = match && !!match[3];
13188
+ var gotMinutes = match && !!match[2];
13189
+ if (!match || gotSeconds && !gotMinutes) {
13188
13190
  stop('Invalid DMS format string:', fmt);
13189
13191
  }
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');
13192
+ var integers = gotSeconds && match[3].length || gotMinutes && match[2].length || match[1].length;
13193
+ var decimalRxp = gotSeconds && /S\.(S+)/ || gotMinutes && /M\.(M+)/ || /D\.(D+)/;
13194
+ var decimals = decimalRxp.test(fmt) ? decimalRxp.exec(fmt)[1].length : 0;
13195
+ if (gotMinutes) {
13196
+ var RES = Math.pow(10, decimals);
13197
+ var CONV = gotSeconds ? 3600 * RES : 60 * RES;
13198
+ var r = Math.floor(Math.abs(coord) * CONV + 0.5);
13199
+ var lastPart = formatNumber((r / RES) % 60, integers, decimals);
13200
+ if (gotSeconds) {
13201
+ r = Math.floor(r / (RES * 60));
13202
+ sstr = lastPart;
13203
+ mstr = String(r % 60).padStart(match[2].length, '0');
13204
+ } else {
13205
+ r = Math.floor(r / RES);
13206
+ mstr = lastPart;
13207
+ }
13208
+ dstr = String(Math.floor(r / 60)).padStart(match[1].length, '0');
13201
13209
  } else {
13202
- r = Math.floor(r / RES);
13203
- mstr = lastPart;
13204
- sstr = '';
13210
+ dstr = Math.abs(coord).toFixed(decimals);
13205
13211
  }
13206
- dstr = String(Math.floor(r / 60)).padStart(match[1].length, '0');
13207
13212
  str = str.replace(/\[-\]/, s => coord < 0 ? '-' : '');
13208
13213
  str = str.replace(/\[[+-]+\]/, s => coord < 0 ? '-' : '+');
13209
13214
  str = str.replace(/\[[NS, ]+\]/, s => coord < 0 ? 'S' : 'N');
13210
13215
  str = str.replace(/\[[EW, ]+\]/, s => coord < 0 ? 'W' : 'E');
13211
- str = str.replace(/D+/, dstr);
13212
- str = str.replace(/M+(\.M+)?/, mstr);
13216
+ str = str.replace(/D+(\.D+)?/, dstr);
13217
+ if (gotMinutes) str = str.replace(/M+(\.M+)?/, mstr);
13213
13218
  if (gotSeconds) str = str.replace(/S+(\.S+)?/, sstr);
13214
13219
  return str;
13215
13220
  }
@@ -17699,12 +17704,18 @@
17699
17704
  }
17700
17705
 
17701
17706
  function calcFrameData(dataset, opts) {
17702
- var bounds = getDatasetBounds(dataset);
17703
- var bounds2 = calcOutputSizeInPixels(bounds, opts);
17707
+ var bounds;
17708
+ if (opts.svg_bbox) {
17709
+ bounds = new Bounds(opts.svg_bbox);
17710
+ opts = Object.assign({margin: 0}, opts); // prevent default pixel margin around content
17711
+ } else {
17712
+ bounds = getDatasetBounds(dataset);
17713
+ }
17714
+ var pixBounds = calcOutputSizeInPixels(bounds, opts);
17704
17715
  return {
17705
17716
  bbox: bounds.toArray(),
17706
- width: Math.round(bounds2.width()),
17707
- height: Math.round(bounds2.height()) || 1,
17717
+ width: Math.round(pixBounds.width()),
17718
+ height: Math.round(pixBounds.height()) || 1,
17708
17719
  type: 'frame'
17709
17720
  };
17710
17721
  }
@@ -23439,6 +23450,10 @@ ${svg}
23439
23450
  describe: '[SVG] source units per pixel (alternative to width= option)',
23440
23451
  type: 'number'
23441
23452
  })
23453
+ .option('svg-bbox', {
23454
+ describe: '[SVG] bounding box of SVG map in projected map units',
23455
+ type: 'bbox'
23456
+ })
23442
23457
  .option('point-symbol', {
23443
23458
  describe: '[SVG] circle or square (default is circle)'
23444
23459
  })
package/www/page.css CHANGED
@@ -242,10 +242,6 @@ body {
242
242
  white-space: nowrap;
243
243
  }
244
244
 
245
- .export-options .option-menu .advanced-options {
246
- width: 195px;
247
- }
248
-
249
245
  /* --- Main area ------------- */
250
246
 
251
247
  .main-area {
@@ -450,14 +446,14 @@ body.dragover #import-options-drop-area .drop-area {
450
446
 
451
447
  /* --- Import screen -------------- */
452
448
 
453
-
454
- .option-menu .advanced-options {
455
- width: 250px;
449
+ .option-menu .text-input {
450
+ width: 230px;
456
451
  background: rgba(255, 255, 255, 0.4);
457
452
  border: 1px solid #999;
458
453
  padding: 0 3px;
459
454
  height: 18px;
460
455
  font-size: 13px;
456
+ margin: 0 0 5px 0;
461
457
  }
462
458
 
463
459
  #mshp-not-supported {
@@ -548,10 +544,6 @@ body.dragover #import-options-drop-area .drop-area {
548
544
  font-size: 90%;
549
545
  }
550
546
 
551
- .option-menu {
552
- padding: 0 0 9px 0;
553
- }
554
-
555
547
  /*.option-menu input[type="radio"],
556
548
  .option-menu input[type="checkbox"] */
557
549
  .info-box input[type="radio"],