mapshaper 0.6.106 → 0.6.108

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/README.md CHANGED
@@ -80,6 +80,14 @@ npm run build # bundle source code files
80
80
  npm link # (optional) add global symlinks so scripts are available systemwide
81
81
  ```
82
82
 
83
+ ## Using mapshaper with Bun
84
+
85
+ Mapshaper's command line tools can be run with [Bun](https://bun.sh/) as an alternative to Node.js. The simplest option is to use `bunx`, like this:
86
+
87
+ ```
88
+ bunx mapshaper [commands]
89
+ ```
90
+
83
91
  ## Building and testing
84
92
 
85
93
  From the project directory, run `npm run build` to build both the cli and web UI modules.
package/mapshaper.js CHANGED
@@ -3927,7 +3927,10 @@
3927
3927
 
3928
3928
  function getValueType(val) {
3929
3929
  var type = null;
3930
- if (utils.isString(val)) {
3930
+ if (val === null || val === undefined) {
3931
+ // optimization, when scanning columns containing mostly null values
3932
+ type = null;
3933
+ } else if (utils.isString(val)) {
3931
3934
  type = 'string';
3932
3935
  } else if (utils.isNumber(val)) {
3933
3936
  type = 'number';
@@ -13542,14 +13545,8 @@
13542
13545
  function getBoundsPrecisionForDisplay(bbox) {
13543
13546
  var w = Math.abs(bbox[2] - bbox[0]),
13544
13547
  h = Math.abs(bbox[3] - bbox[1]),
13545
- // switched to max bound, based on experience with shift-drag box info
13546
- // range = Math.min(w, h) + 1e-8,
13547
- range = Math.max(w, h) + 1e-8,
13548
- digits = 0;
13549
- while (range < 2000 && digits < 1) {
13550
- range *= 10;
13551
- digits++;
13552
- }
13548
+ range = (w + h) / 2 + 1e-8,
13549
+ digits = Math.max(0, Math.round(3 - Math.log10(range)));
13553
13550
  return digits;
13554
13551
  }
13555
13552
 
@@ -46041,7 +46038,7 @@ ${svg}
46041
46038
  });
46042
46039
  }
46043
46040
 
46044
- var version = "0.6.106";
46041
+ var version = "0.6.108";
46045
46042
 
46046
46043
  // Parse command line args into commands and run them
46047
46044
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
@@ -46095,7 +46092,7 @@ ${svg}
46095
46092
  }
46096
46093
  if (!loggingEnabled()) argv += ' -quiet'; // kludge to pass logging setting to subprocess
46097
46094
  var mb = Math.round(gb * 1000);
46098
- var command = [process.execPath, '--max-old-space-size=' + mb, mapshaperScript, argv].join(' ');
46095
+ var command = [`"${process.execPath}"`, '--max-old-space-size=' + mb, `"${mapshaperScript}"`, argv].join(' ');
46099
46096
  var child = require$1('child_process').exec(command, {}, function(err, stdout, stderr) {
46100
46097
  opts.callback(err);
46101
46098
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.6.106",
3
+ "version": "0.6.108",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
package/www/elements.css CHANGED
@@ -130,7 +130,8 @@ div.tip {
130
130
 
131
131
  .btn {
132
132
  text-align: center;
133
- padding: 4px 7px 5px 7px;
133
+ /* padding: 4px 7px 5px 7px; */
134
+ padding: 3px 6px 4px 6px;
134
135
  border-radius: 4px;
135
136
  line-height: 1;
136
137
  display: inline-block;
package/www/index.html CHANGED
@@ -76,7 +76,7 @@
76
76
  <input type="text" value="label" class="clicktext" />
77
77
  </div></div>
78
78
  <div id="mode-buttons" class="page-header-buttons">
79
- <span class="console-btn header-btn btn">Console</span><span class="basemap-btn header-btn btn">Basemap</span><span class="separator"></span><span class="separator"></span><span class="simplify-btn header-btn btn">Simplify</span><span class="separator"></span><span class="export-btn header-btn btn">Export</span>
79
+ <span class="console-btn header-btn btn">Console</span><span class="display-btn header-btn btn">Display</span><span class="separator"></span><span class="separator"></span><span class="simplify-btn header-btn btn">Simplify</span><span class="separator"></span><span class="export-btn header-btn btn">Export</span>
80
80
  </div>
81
81
  <div id="splash-buttons" class="page-header-buttons">
82
82
  <a href="https://github.com/mbloch/mapshaper/wiki"><span id="wiki-btn" class="header-btn btn">Wiki</span></a><span class="separator"></span><a href="https://github.com/mbloch/mapshaper"><span id="github-btn" class="header-btn btn">GitHub</span></a>
@@ -174,23 +174,30 @@ precision=0.001. Click to see all options.</div></div></div></a>
174
174
  <div><span id="save-preference" style="display: none;"><input type="checkbox"/>choose output directory</span></div>
175
175
  <div><span id="save-to-clipboard" style="display: inline-block;"><input type="checkbox"/>save to clipboard</span></div>
176
176
  </div>
177
- <div id="export-btn" class="btn dialog-btn" style="margin-top: 4px;">Export</div>
177
+ <div id="export-btn" class="btn dialog-btn" style="margin-top: 5px;">Export</div>
178
178
 
179
179
  </div>
180
180
  </div>
181
181
 
182
- <div class="basemap-options main-area popup-dialog">
182
+ <div class="display-options main-area popup-dialog">
183
183
  <div class="info-box">
184
184
  <div class="close2-btn"></div>
185
- <h3>Basemap options</h3>
186
- <p class="basemap-note"></p>
187
- <p class="basemap-warning"></p>
188
- <div class="basemap-styles basemap-buttons"></div>
189
- <div>
190
- <!-- <div class="close-btn btn dialog-btn">Close</div> -->
191
- <div class="clear-btn btn dialog-btn disabled">Clear</div>
192
- <div class="fade-btn btn dialog-btn disabled">Fade</div>
193
- <!-- <div class="hide-btn btn dialog-btn disabled">Hide</div> -->
185
+ <h3>Display options</h3>
186
+
187
+ <div class=""><label for="intersections-opt"><input type="checkbox" id="intersections-opt" class="checkbox intersections-opt"/>detect line intersections</label></div>
188
+ <div class=""><label for="ghost-opt"><input type="checkbox" id="ghost-opt" class="checkbox ghost-opt"/>show reference lines</label></div>
189
+
190
+ <div class="basemap-opts">
191
+ <h4>Basemaps</h4>
192
+ <p class="basemap-warning"></p>
193
+ <div class="basemap-styles basemap-buttons"></div>
194
+ <p class="basemap-note"></p>
195
+ <div>
196
+ <!-- <div class="close-btn btn dialog-btn">Close</div> -->
197
+ <div class="clear-btn btn dialog-btn disabled">Clear</div>
198
+ <div class="fade-btn btn dialog-btn disabled">Fade</div>
199
+ <!-- <div class="hide-btn btn dialog-btn disabled">Hide</div> -->
200
+ </div>
194
201
  </div>
195
202
  </div>
196
203
  </div>
@@ -237,7 +244,6 @@ of more acute angles are
237
244
  preferentially removed, for
238
245
  a smoother appearance.</div></div></div></div>
239
246
 
240
-
241
247
  </div> <!-- option menu -->
242
248
 
243
249
  <div>
@@ -295,8 +301,7 @@ encoding=big5</span>. Click to see all options.</div></div></div></div>
295
301
  </div>
296
302
  <div class="mshp-main-map main-area map-area">
297
303
  <div class="intersection-display">
298
- <span class="intersection-check text-btn colored-text">Check line intersections</span><span class="intersection-count">0 line intersections</span>
299
- <img class="close-btn" draggable="false" src="images/close.png">
304
+ <span class="intersection-count">0 line intersections</span>
300
305
  <div class="repair-btn text-btn colored-text">Repair</div>
301
306
  </div>
302
307
  <div class="basemap-overlay-buttons basemap-buttons"></div>
@@ -1162,7 +1162,7 @@
1162
1162
  var topTier = 10000; // short-circuit counting here
1163
1163
  var count = 0;
1164
1164
  layers = layers.filter(function(lyr) {
1165
- return lyr.geometry_type == 'point' && lyr.gui.style.dotSize > 0;
1165
+ return lyr.geometry_type == 'point' && lyr.gui.style?.dotSize > 0;
1166
1166
  });
1167
1167
  layers.forEach(function(lyr) {
1168
1168
  count += countPoints(lyr.gui.displayLayer.shapes, topTier, bbox);
@@ -3042,7 +3042,7 @@
3042
3042
  displayLayer: null,
3043
3043
  source: {dataset},
3044
3044
  bounds: null,
3045
- style: null,
3045
+ // style: null, // protect old style in projectLayerForDisplay()
3046
3046
  dynamic_crs: null,
3047
3047
  invertPoint: null,
3048
3048
  projectPoint: null
@@ -4209,18 +4209,20 @@
4209
4209
  model = gui.model,
4210
4210
  el = gui.container.findChild(".intersection-display"),
4211
4211
  readout = el.findChild(".intersection-count"),
4212
- checkBtn = el.findChild(".intersection-check"),
4213
4212
  repairBtn = el.findChild(".repair-btn"),
4213
+ optBox = gui.container.findChild('.intersections-opt'),
4214
4214
  _simplifiedXX, // saved simplified intersections, for repair
4215
- _unsimplifiedXX, // saved unsimplified intersection data, for performance
4216
- _disabled = false,
4217
- _on = false;
4218
-
4219
- el.findChild('.close-btn').on('click', dismissForever);
4215
+ _unsimplifiedXX; // saved unsimplified intersection data, for performance
4220
4216
 
4221
4217
  gui.on('simplify_drag_start', function() {
4222
- if (intersectionsAreOn()) {
4223
- hide();
4218
+ hide();
4219
+ });
4220
+
4221
+ gui.on('mode', updateRepairBtn);
4222
+
4223
+ gui.on('display_option_change', function(e) {
4224
+ if (e.option == 'intersectionsOn') {
4225
+ updateAsync();
4224
4226
  }
4225
4227
  });
4226
4228
 
@@ -4228,12 +4230,6 @@
4228
4230
  updateSync('simplify_drag_end');
4229
4231
  });
4230
4232
 
4231
- checkBtn.on('click', function() {
4232
- checkBtn.hide();
4233
- _on = true;
4234
- updateSync();
4235
- });
4236
-
4237
4233
  repairBtn.on('click', function() {
4238
4234
  var e = model.getActiveLayer();
4239
4235
  if (!_simplifiedXX || !e.dataset.arcs) return;
@@ -4245,59 +4241,46 @@
4245
4241
  });
4246
4242
 
4247
4243
  model.on('update', function(e) {
4248
- if (!intersectionsAreOn()) {
4249
- reset(); // need this?
4250
- return;
4251
- }
4252
4244
  var needRefresh = e.flags.simplify_method || e.flags.simplify ||
4253
- e.flags.repair || e.flags.clean;
4254
- if (needRefresh) {
4245
+ e.flags.repair || e.flags.clean || e.flags.select || intersectionsMayHaveChanged(e.flags);
4246
+ if (!intersectionsAreOn()) {
4247
+ reset();
4248
+ } else if (needRefresh) {
4255
4249
  updateAsync();
4256
4250
  } else if (e.flags.simplify_amount) {
4257
4251
  // slider is being dragged - hide readout and dots, retain data
4258
4252
  hide();
4259
- } else if (intersectionsMayHaveChanged(e.flags)) {
4260
- // intersections may have changed -- reset the display
4261
- reset();
4262
4253
  } else {
4263
4254
  // keep displaying the current intersections
4264
4255
  }
4265
4256
  });
4266
4257
 
4258
+ function updateRepairBtn() {
4259
+ if (intersectionsAreOn() && gui.getMode() == 'simplify' &&
4260
+ _simplifiedXX?.length > 0) {
4261
+ repairBtn.show();
4262
+ } else {
4263
+ repairBtn.hide();
4264
+ }
4265
+ }
4266
+
4267
4267
  function intersectionsAreOn() {
4268
- return _on && !_disabled;
4268
+ var e = model.getActiveLayer();
4269
+ return !!(gui.display.getOptions().intersectionsOn &&
4270
+ e && e.dataset.arcs && internal.layerHasPaths(e.layer));
4269
4271
  }
4270
4272
 
4271
- function turnOff() {
4273
+ function reset() {
4272
4274
  hide();
4273
- _on = false;
4274
4275
  _simplifiedXX = null;
4275
4276
  _unsimplifiedXX = null;
4276
4277
  }
4277
4278
 
4278
- function reset() {
4279
- turnOff();
4280
- if (_disabled) {
4281
- return;
4282
- }
4283
- var e = model.getActiveLayer();
4284
- if (!e) return;
4285
- if (internal.layerHasPaths(e.layer)) {
4286
- el.show();
4287
- checkBtn.show();
4288
- readout.hide();
4289
- repairBtn.hide();
4290
- }
4291
- }
4292
-
4293
- function dismissForever() {
4294
- _disabled = true;
4295
- turnOff();
4296
- }
4297
-
4298
4279
  function hide() {
4299
4280
  map.setIntersectionLayer(null);
4300
4281
  el.hide();
4282
+ repairBtn.hide();
4283
+ readout.hide();
4301
4284
  }
4302
4285
 
4303
4286
  // Update intersection display, after a short delay so map can redraw after previous
@@ -4307,16 +4290,16 @@
4307
4290
  }
4308
4291
 
4309
4292
  function updateSync(action) {
4310
- if (!intersectionsAreOn()) return;
4311
4293
  var e = model.getActiveLayer();
4312
4294
  var arcs = e.dataset && e.dataset.arcs;
4295
+ if (!intersectionsAreOn() || !arcs || !internal.layerHasPaths(e.layer)) {
4296
+ reset();
4297
+ return;
4298
+ }
4313
4299
  var intersectionOpts = {
4314
4300
  unique: true,
4315
4301
  tolerance: 0
4316
4302
  };
4317
- if (!arcs || !internal.layerHasPaths(e.layer)) {
4318
- return;
4319
- }
4320
4303
  if (arcs.getRetainedInterval() > 0) {
4321
4304
  // simplification
4322
4305
  _simplifiedXX = internal.findSegmentIntersections(arcs, intersectionOpts);
@@ -4336,7 +4319,6 @@
4336
4319
  var pointLyr, count = 0, html;
4337
4320
  el.show();
4338
4321
  readout.show();
4339
- checkBtn.hide();
4340
4322
  if (xx.length > 0) {
4341
4323
  pointLyr = internal.getIntersectionLayer(xx, lyr, arcs);
4342
4324
  count = internal.countPointsInLayer(pointLyr);
@@ -4349,12 +4331,7 @@
4349
4331
  html = utils$1.format('<span class="icon"></span>%s line intersection%s', count, utils$1.pluralSuffix(count));
4350
4332
  }
4351
4333
  readout.html(html);
4352
-
4353
- if (_simplifiedXX && count > 0) {
4354
- repairBtn.show();
4355
- } else {
4356
- repairBtn.hide();
4357
- }
4334
+ updateRepairBtn();
4358
4335
  }
4359
4336
  }
4360
4337
 
@@ -10093,7 +10070,7 @@
10093
10070
  };
10094
10071
 
10095
10072
  function getOverlayLayers(activeLyr, hitData, styleOpts) {
10096
- if (activeLyr.hidden || !activeLyr?.gui?.style) return [];
10073
+ if (activeLyr?.hidden || !activeLyr?.gui?.style) return [];
10097
10074
  var displayLyr = activeLyr.gui.displayLayer;
10098
10075
  var layers, lyr, outlineStyle, ids;
10099
10076
  if (styleOpts.interactionMode == 'vertices') {
@@ -11282,8 +11259,8 @@
11282
11259
  dotSize: 1
11283
11260
  },
11284
11261
  intersectionStyle = {
11285
- dotColor: "#F24400",
11286
- dotSize: 1
11262
+ dotColor: "#FF421D",
11263
+ dotSize: 1.3
11287
11264
  };
11288
11265
 
11289
11266
  function getIntersectionStyle(lyr, opts) {
@@ -11316,6 +11293,11 @@
11316
11293
  } else {
11317
11294
  style = copyBaseStyle(activeStyle);
11318
11295
  }
11296
+ // kludge: no ghosted lines if not enabled
11297
+ if (style.strokeColors && !opts.ghostingOn) {
11298
+ style.strokeColors = [null, style.strokeColors[1]];
11299
+ }
11300
+
11319
11301
  return style;
11320
11302
  }
11321
11303
 
@@ -11780,22 +11762,23 @@
11780
11762
  var arcs;
11781
11763
  var style = lyr.gui.style;
11782
11764
  var arcCounts = lyr.gui.arcCounts;
11783
- var darkStyle = {strokeWidth: style.strokeWidth, strokeColor: style.strokeColors[1]},
11784
- lightStyle = {strokeWidth: style.strokeWidth, strokeColor: style.strokeColors[0]};
11765
+ var fgStyle = {strokeWidth: style.strokeWidth, strokeColor: style.strokeColors[1]},
11766
+ bgStyle = {strokeWidth: style.strokeWidth, strokeColor: style.strokeColors[0]};
11785
11767
  var filter;
11768
+
11786
11769
  if (internal.layerHasPaths(lyr.gui.displayLayer)) {
11787
11770
  if (!arcCounts) {
11788
11771
  arcCounts = lyr.gui.arcCounts = new Uint8Array(lyr.gui.displayArcs.size());
11789
11772
  internal.countArcsInShapes(lyr.gui.displayLayer.shapes, arcCounts);
11790
11773
  }
11791
11774
  arcs = getArcsForRendering(lyr, ext);
11792
- if (lightStyle.strokeColor) {
11775
+ if (bgStyle.strokeColor) {
11793
11776
  filter = getArcFilter(arcs, ext, false, arcCounts);
11794
- canv.drawArcs(arcs, lightStyle, filter);
11777
+ canv.drawArcs(arcs, bgStyle, filter);
11795
11778
  }
11796
- if (darkStyle.strokeColor && lyr.gui.displayLayer.geometry_type != 'point') {
11779
+ if (fgStyle.strokeColor && lyr.gui.displayLayer.geometry_type != 'point') {
11797
11780
  filter = getArcFilter(arcs, ext, true, arcCounts);
11798
- canv.drawArcs(arcs, darkStyle, filter);
11781
+ canv.drawArcs(arcs, fgStyle, filter);
11799
11782
  }
11800
11783
  }
11801
11784
  if (lyr.gui.displayLayer.geometry_type == 'point') {
@@ -12577,7 +12560,7 @@
12577
12560
  var self = new EventDispatcher();
12578
12561
  var box = new HighlightBox(gui, {name: 'box-tool', persistent: true, handles: true, draggable: true});
12579
12562
  var popup = gui.container.findChild('.box-tool-options');
12580
- var coords = popup.findChild('.box-coords');
12563
+ var coords = popup.findChild('.box-coords').hide();
12581
12564
  var _on = false;
12582
12565
  var instructionsShown = false;
12583
12566
  var alert;
@@ -12958,7 +12941,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12958
12941
  clearAllDisplayArcs();
12959
12942
 
12960
12943
  // Reproject all visible map layers
12961
- getContentLayers().forEach(function(lyr) {
12944
+ getContentLayers().concat(_intersectionLyr || []).forEach(function(lyr) {
12962
12945
  projectLayerForDisplay(lyr, newCRS);
12963
12946
  });
12964
12947
 
@@ -13001,7 +12984,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13001
12984
  darkMode: !!gui.state.dark_basemap,
13002
12985
  outlineMode: mode == 'vertices',
13003
12986
  interactionMode: mode
13004
- }, opts);
12987
+ }, opts, gui.display.getOptions()); // intersectionsOn, ghostingOn
13005
12988
  }
13006
12989
 
13007
12990
  // Refresh map display in response to data changes, layer selection, etc.
@@ -13220,14 +13203,13 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13220
13203
  // regenerating active style everytime, to support style change when
13221
13204
  // switching between outline and preview modes.
13222
13205
  style = getActiveLayerStyle(mapLayer.gui.displayLayer, getGlobalStyleOptions());
13223
- if (style.type != 'styled' && layers.length > 1 && style.strokeColors) {
13224
- // kludge to hide ghosted layers when reference layers are present
13225
- // TODO: consider never showing ghosted layers (which appear after
13226
- // commands like dissolve and filter).
13227
- style = utils$1.defaults({
13228
- strokeColors: [null, style.strokeColors[1]]
13229
- }, style);
13230
- }
13206
+ // changed: ghosting is set in gui-layer-styler.mjs
13207
+ // if (style.type != 'styled' && layers.length > 1 && style.strokeColors) {
13208
+ // // kludge to hide ghosted layers when reference layers are present
13209
+ // style = utils.defaults({
13210
+ // strokeColors: [null, style.strokeColors[1]]
13211
+ // }, style);
13212
+ // }
13231
13213
  } else {
13232
13214
  if (mapLayer == _activeLyr) {
13233
13215
  console.error("Error: shared map layer");
@@ -13363,14 +13345,12 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13363
13345
  }
13364
13346
 
13365
13347
  function Basemap(gui) {
13366
- var menu = gui.container.findChild('.basemap-options');
13348
+ var menu = gui.container.findChild('.display-options');
13367
13349
  var fadeBtn = new SimpleButton(menu.findChild('.fade-btn'));
13368
- var closeBtn = new SimpleButton(menu.findChild('.close2-btn'));
13369
13350
  var clearBtn = new SimpleButton(menu.findChild('.clear-btn'));
13370
13351
  var menuButtons = menu.findChild('.basemap-styles');
13371
13352
  var overlayButtons = gui.container.findChild('.basemap-overlay-buttons');
13372
13353
  var container = gui.container.findChild('.basemap-container');
13373
- var basemapBtn = gui.container.findChild('.basemap-btn');
13374
13354
  var basemapNote = gui.container.findChild('.basemap-note');
13375
13355
  var basemapWarning = gui.container.findChild('.basemap-warning');
13376
13356
  var mapEl = gui.container.findChild('.basemap');
@@ -13385,22 +13365,25 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13385
13365
  // TODO: check page URL for compatibility with mapbox key
13386
13366
  init();
13387
13367
  } else {
13388
- basemapBtn.hide();
13368
+ menu.findChild('.basemap-opts').hide();
13389
13369
  }
13390
13370
 
13391
13371
  function init() {
13392
- gui.addMode('basemap', turnOn, turnOff, basemapBtn);
13393
-
13394
- closeBtn.on('click', function() {
13395
- gui.clearMode();
13396
- turnOff();
13372
+ gui.on('mode', function(e) {
13373
+ if (e.prev == 'display_options') {
13374
+ basemapWarning.hide();
13375
+ basemapNote.hide();
13376
+ }
13377
+ if (e.name == 'display_options') {
13378
+ onUpdate();
13379
+ }
13397
13380
  });
13398
13381
 
13399
13382
  clearBtn.on('click', function() {
13400
13383
  if (activeStyle) {
13401
13384
  turnOffBasemap();
13402
13385
  updateButtons();
13403
- closeMenu();
13386
+ // closeMenu();
13404
13387
  }
13405
13388
  });
13406
13389
 
@@ -13441,7 +13424,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13441
13424
  showBasemap(style);
13442
13425
  }
13443
13426
  updateButtons();
13444
- closeMenu();
13427
+ // closeMenu();
13445
13428
  }
13446
13429
  });
13447
13430
  }
@@ -13481,11 +13464,6 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13481
13464
  });
13482
13465
  }
13483
13466
 
13484
- function turnOn() {
13485
- onUpdate();
13486
- menu.show();
13487
- }
13488
-
13489
13467
  function onUpdate() {
13490
13468
  var activeLyr = gui.model.getActiveLayer(); // may be null
13491
13469
  var info = getDatasetCrsInfo(activeLyr?.dataset); // defaults to wgs84
@@ -13507,19 +13485,13 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13507
13485
  activeStyle = null;
13508
13486
  updateButtons();
13509
13487
  } else {
13510
- note = `Your data ${activeStyle ? 'is' : 'will be'} displayed using the Mercator projection.`;
13488
+ note = `Note: basemaps use the Mercator projection.`;
13511
13489
  basemapNote.text(note).show();
13512
13490
  overlayButtons.show();
13513
13491
  overlayButtons.removeClass('disabled');
13514
13492
  }
13515
13493
  }
13516
13494
 
13517
- function turnOff() {
13518
- basemapWarning.hide();
13519
- basemapNote.hide();
13520
- menu.hide();
13521
- }
13522
-
13523
13495
  function enabled() {
13524
13496
  return !!(mapEl && params);
13525
13497
  }
@@ -13635,6 +13607,64 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13635
13607
  return {refresh, show: onUpdate};
13636
13608
  }
13637
13609
 
13610
+ function DisplayOptions(gui) {
13611
+ var menuBtn = gui.container.findChild('.display-btn');
13612
+ var menu = gui.container.findChild('.display-options');
13613
+ var closeBtn = new SimpleButton(menu.findChild('.close2-btn'));
13614
+ var xxBox = menu.findChild('.intersections-opt');
13615
+ var ghostBox = menu.findChild('.ghost-opt');
13616
+
13617
+ var savedOpts = GUI.getSavedValue('display_options') || {};
13618
+ xxBox.node().checked = savedOpts.intersectionsOn;
13619
+ ghostBox.node().checked = savedOpts.ghostingOn;
13620
+
13621
+
13622
+ gui.addMode('display_options', turnOn, turnOff, menuBtn);
13623
+
13624
+ closeBtn.on('click', function() {
13625
+ gui.clearMode();
13626
+ turnOff();
13627
+ });
13628
+
13629
+ gui.on('display_option_change', function() {
13630
+ GUI.setSavedValue('display_options', getOptions());
13631
+ });
13632
+
13633
+ xxBox.on('change', function() {
13634
+ gui.dispatchEvent('display_option_change', {
13635
+ option: 'intersectionsOn',
13636
+ value: getOptions().intersectionsOn
13637
+ });
13638
+ });
13639
+
13640
+ ghostBox.on('change', function() {
13641
+ gui.dispatchEvent('display_option_change', {
13642
+ option: 'ghostingOn',
13643
+ value: getOptions().ghostingOn
13644
+ });
13645
+ gui.dispatchEvent('map-needs-refresh');
13646
+ });
13647
+
13648
+ function getOptions() {
13649
+ return {
13650
+ intersectionsOn: xxBox.node().checked,
13651
+ ghostingOn: ghostBox.node().checked
13652
+ };
13653
+ }
13654
+
13655
+ function turnOn() {
13656
+ menu.show();
13657
+ }
13658
+
13659
+ function turnOff() {
13660
+ menu.hide();
13661
+ }
13662
+
13663
+ return {
13664
+ getOptions
13665
+ };
13666
+ }
13667
+
13638
13668
  function GuiInstance(container, opts) {
13639
13669
  var gui = new ModeSwitcher();
13640
13670
  opts = utils$1.extend({
@@ -13652,12 +13682,14 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13652
13682
  gui.model = new Model(gui);
13653
13683
  gui.keyboard = new KeyboardEvents(gui);
13654
13684
  gui.buttons = new SidebarButtons(gui);
13685
+ gui.display = new DisplayOptions(gui);
13655
13686
  gui.basemap = new Basemap(gui);
13656
13687
  gui.session = new SessionHistory(gui);
13657
13688
  gui.contextMenu = new ContextMenu();
13658
13689
  gui.undo = new Undo(gui);
13659
13690
  gui.map = new MshpMap(gui);
13660
13691
 
13692
+
13661
13693
  gui.state = {};
13662
13694
 
13663
13695
  var msgCount = 0;
package/www/mapshaper.js CHANGED
@@ -3927,7 +3927,10 @@
3927
3927
 
3928
3928
  function getValueType(val) {
3929
3929
  var type = null;
3930
- if (utils.isString(val)) {
3930
+ if (val === null || val === undefined) {
3931
+ // optimization, when scanning columns containing mostly null values
3932
+ type = null;
3933
+ } else if (utils.isString(val)) {
3931
3934
  type = 'string';
3932
3935
  } else if (utils.isNumber(val)) {
3933
3936
  type = 'number';
@@ -13542,14 +13545,8 @@
13542
13545
  function getBoundsPrecisionForDisplay(bbox) {
13543
13546
  var w = Math.abs(bbox[2] - bbox[0]),
13544
13547
  h = Math.abs(bbox[3] - bbox[1]),
13545
- // switched to max bound, based on experience with shift-drag box info
13546
- // range = Math.min(w, h) + 1e-8,
13547
- range = Math.max(w, h) + 1e-8,
13548
- digits = 0;
13549
- while (range < 2000 && digits < 1) {
13550
- range *= 10;
13551
- digits++;
13552
- }
13548
+ range = (w + h) / 2 + 1e-8,
13549
+ digits = Math.max(0, Math.round(3 - Math.log10(range)));
13553
13550
  return digits;
13554
13551
  }
13555
13552
 
@@ -46041,7 +46038,7 @@ ${svg}
46041
46038
  });
46042
46039
  }
46043
46040
 
46044
- var version = "0.6.106";
46041
+ var version = "0.6.108";
46045
46042
 
46046
46043
  // Parse command line args into commands and run them
46047
46044
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
@@ -46095,7 +46092,7 @@ ${svg}
46095
46092
  }
46096
46093
  if (!loggingEnabled()) argv += ' -quiet'; // kludge to pass logging setting to subprocess
46097
46094
  var mb = Math.round(gb * 1000);
46098
- var command = [process.execPath, '--max-old-space-size=' + mb, mapshaperScript, argv].join(' ');
46095
+ var command = [`"${process.execPath}"`, '--max-old-space-size=' + mb, `"${mapshaperScript}"`, argv].join(' ');
46099
46096
  var child = require$1('child_process').exec(command, {}, function(err, stdout, stderr) {
46100
46097
  opts.callback(err);
46101
46098
  });
package/www/page.css CHANGED
@@ -325,18 +325,18 @@ div.alert-box {
325
325
  /* --- Splash screen -------------- */
326
326
 
327
327
  .import-instructions {
328
- margin: 7px 0 0 0;
328
+ margin: 8px 0 0 0;
329
+ font-size: 15px;
329
330
  }
330
331
 
331
332
  .mini-drop-area {
332
333
  border: 1.5px dashed #999;
333
- max-width: 245px;
334
+ max-width: 260px;
334
335
  border-radius: 9px;
335
- font-size: 14px;
336
- line-height: 1.5;
336
+ line-height: 1.45;
337
337
  padding: 9px 11px 6px 11px;
338
338
  margin: 9px -1px 3px -1px;
339
- min-height: 120px;
339
+ min-height: 130px;
340
340
  background: #f8fdff;
341
341
  }
342
342
 
@@ -503,7 +503,7 @@ div.alert-box {
503
503
  text-align: left;
504
504
  margin-top: 12px;
505
505
  margin-right: 20px;
506
- padding: 11px 16px 12px 18px;
506
+ padding: 12px 16px 12px 18px;
507
507
  vertical-align: top;
508
508
  display: inline-block;
509
509
  /* border: 1px solid #aaa; */
@@ -533,7 +533,7 @@ div.alert-box {
533
533
  }
534
534
 
535
535
  .info-box h3 {
536
- font-size: 19px;
536
+ font-size: 20px;
537
537
  line-height: 1.1;
538
538
  padding: 0;
539
539
  margin: 0 0 0.4em 0;
@@ -821,7 +821,7 @@ img.close-btn {
821
821
  float: right;
822
822
  height: 18px;
823
823
  width: 18px;
824
- /* margin-top: 1px; */
824
+ margin-top: 1px;
825
825
  margin-right: -3px;
826
826
  cursor: pointer;
827
827
  border-radius: 4px;
@@ -1101,11 +1101,11 @@ img.close-btn:hover,
1101
1101
  height: 100%;
1102
1102
  }
1103
1103
 
1104
- .basemap-options {
1104
+ .display-options {
1105
1105
  pointer-events: none;
1106
1106
  }
1107
1107
 
1108
- .basemap-options .info-box {
1108
+ .display-options .info-box {
1109
1109
  width: min-content;
1110
1110
  pointer-events: initial;
1111
1111
  }
@@ -1134,6 +1134,10 @@ img.close-btn:hover,
1134
1134
  color: #cc0000;
1135
1135
  }
1136
1136
 
1137
+ .basemap-note {
1138
+ font-size: 90%;
1139
+ }
1140
+
1137
1141
  .basemap-prompt {
1138
1142
  width: 100%;
1139
1143
  text-align: center;
@@ -1146,7 +1150,7 @@ img.close-btn:hover,
1146
1150
 
1147
1151
  .basemap-styles > div {
1148
1152
  display: inline-block;
1149
- margin-bottom: 6px;
1153
+ margin-bottom: 4px;
1150
1154
  }
1151
1155
 
1152
1156
  .basemap-styles > div:nth-child(even) {