mapshaper 0.6.13 → 0.6.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.6.13",
3
+ "version": "0.6.15",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -28,7 +28,7 @@
28
28
  "lint": "eslint --ext mjs src/",
29
29
  "prepublishOnly": "npm lint; npm test; ./pre-publish",
30
30
  "postpublish": "./release_web_ui; ./release_github_version",
31
- "browserify": "browserify -r sync-request -r mproj -r buffer -r iconv-lite -r fs -r flatbush -r rw -r path -r kdbush -r @tmcw/togeojson -o www/modules.js",
31
+ "browserify": "browserify -r sync-request -r mproj -r buffer -r iconv-lite -r fs -r flatbush -r rw -r path -r kdbush -r @tmcw/togeojson -r @placemarkio/tokml -o www/modules.js",
32
32
  "dev": "rollup --config --watch"
33
33
  },
34
34
  "main": "./mapshaper.js",
@@ -40,12 +40,15 @@
40
40
  "!.DS_Store"
41
41
  ],
42
42
  "dependencies": {
43
- "@tmcw/togeojson": "^4.7.0",
44
- "@xmldom/xmldom": "^0.8.2",
43
+ "@placemarkio/tokml": "^0.3.3",
44
+ "@tmcw/togeojson": "^5.5.0",
45
+ "@xmldom/xmldom": "^0.8.6",
46
+ "adm-zip": "^0.5.9",
45
47
  "commander": "7.0.0",
46
48
  "cookies": "^0.8.0",
47
- "d3-color": "2.0.0",
48
- "d3-scale-chromatic": "2.0.0",
49
+ "d3-color": "3.1.0",
50
+ "d3-interpolate": "^3.0.1",
51
+ "d3-scale-chromatic": "3.0.0",
49
52
  "delaunator": "^5.0.0",
50
53
  "flatbush": "^3.2.1",
51
54
  "geokdbush": "^1.1.0",
@@ -64,7 +67,7 @@
64
67
  "eslint": "^8.16.0",
65
68
  "mocha": "^10.0.0",
66
69
  "rollup": "^2.73.0",
67
- "shell-quote": "^1.6.1",
70
+ "shell-quote": "^1.7.4",
68
71
  "underscore": "^1.13.1"
69
72
  },
70
73
  "mocha": {
package/www/basemap.js CHANGED
@@ -1,6 +1,6 @@
1
1
  window.mapboxParams = {
2
- js: 'https://api.mapbox.com/mapbox-gl-js/v2.7.0/mapbox-gl.js',
3
- css: 'https://api.mapbox.com/mapbox-gl-js/v2.7.0/mapbox-gl.css',
2
+ js: 'https://api.mapbox.com/mapbox-gl-js/v2.11.1/mapbox-gl.js',
3
+ css: 'https://api.mapbox.com/mapbox-gl-js/v2.11.1/mapbox-gl.css',
4
4
  key: 'pk.eyJ1IjoiZ3JhbW1hdGEiLCJhIjoiY2wxMzI4b3dqMDNhMjNpcDhzcmU0aGh5diJ9.m1lacx48pTYls-X3FVhG9g',
5
5
  styles: [{
6
6
  name: 'Map',
@@ -392,7 +392,7 @@
392
392
  function readEntry(entry) {
393
393
  var filename = entry.filename,
394
394
  isValid = !entry.directory && GUI.isReadableFileType(filename) &&
395
- !/^__MACOSX/.test(filename); // ignore "resource-force" files
395
+ !/^__MACOSX/.test(filename); // ignore "resource-fork" files
396
396
  if (isValid) {
397
397
  entry.getData(new zip.BlobWriter(), function(file) {
398
398
  file.name = filename; // Give the Blob a name, like a File object
@@ -1420,12 +1420,14 @@
1420
1420
  async function expandFiles(files) {
1421
1421
  var files2 = [], expanded;
1422
1422
  for (var f of files) {
1423
- if (internal.isZipFile(f.name) || /\.kmz$/.test(f.name)) {
1423
+ if (internal.isZipFile(f.name)) {
1424
1424
  expanded = await readZipFile(f);
1425
- files2 = files2.concat(expanded);
1425
+ } else if (internal.isKmzFile(f.name)) {
1426
+ expanded = await readKmzFile(f);
1426
1427
  } else {
1427
- files2.push(f);
1428
+ expanded = [f];
1428
1429
  }
1430
+ files2 = files2.concat(expanded);
1429
1431
  }
1430
1432
  return files2;
1431
1433
  }
@@ -1582,6 +1584,15 @@
1582
1584
  });
1583
1585
  }
1584
1586
 
1587
+ async function readKmzFile(file) {
1588
+ var files = await readZipFile(file);
1589
+ var name = files[0] && files[0].name;
1590
+ if (name == 'doc.kml') {
1591
+ files[0].name = internal.replaceFileExtension(file.name, 'kml');
1592
+ }
1593
+ return files;
1594
+ }
1595
+
1585
1596
  async function readZipFile(file) {
1586
1597
  var files = [];
1587
1598
  await wait(35); // pause a beat so status message can display
@@ -2466,13 +2477,13 @@
2466
2477
  // if map is at full extent, show full extent
2467
2478
  // TODO: handle case that scale is 1 and map is panned away from center
2468
2479
  if (ext.scale() == 1 || !dest) {
2469
- ext.setBounds(newBounds, strictBounds);
2480
+ ext.setFullBounds(newBounds, strictBounds);
2470
2481
  ext.home(); // sets full extent and triggers redraw
2471
2482
  } else {
2472
2483
  // if map is zoomed, stay centered on the same geographic location, at the same relative scale
2473
2484
  proj = internal.getProjTransform2(src, dest);
2474
2485
  newCP = proj(oldBounds.centerX(), oldBounds.centerY());
2475
- ext.setBounds(newBounds, strictBounds);
2486
+ ext.setFullBounds(newBounds, strictBounds);
2476
2487
  if (!newCP) {
2477
2488
  // projection of center point failed; use center of bounds
2478
2489
  // (also consider just resetting the view using ext.home())
@@ -3392,7 +3403,7 @@
3392
3403
  }
3393
3404
 
3394
3405
  function initFormatMenu() {
3395
- var defaults = ['shapefile', 'geojson', 'topojson', 'json', 'dsv', 'svg'];
3406
+ var defaults = ['shapefile', 'geojson', 'topojson', 'json', 'dsv', 'kml', 'svg'];
3396
3407
  var formats = utils$1.uniq(defaults.concat(getInputFormats()));
3397
3408
  var items = formats.map(function(fmt) {
3398
3409
  return utils$1.format('<div><label><input type="radio" name="format" value="%s"' +
@@ -3898,6 +3909,11 @@
3898
3909
  var history, offset, stashedUndo;
3899
3910
  reset();
3900
3911
 
3912
+ // Undo history is cleared when the editing mode changes.
3913
+ gui.on('interaction_mode_change', function(e) {
3914
+ gui.undo.clear();
3915
+ });
3916
+
3901
3917
  function reset() {
3902
3918
  history = [];
3903
3919
  stashedUndo = null;
@@ -4167,12 +4183,28 @@
4167
4183
 
4168
4184
  function InteractionMode(gui) {
4169
4185
 
4186
+ // TODO: finish this list
4187
+ // var modes = [{
4188
+ // name: 'info',
4189
+ // label: 'inspect features',
4190
+ // selection: true,
4191
+ // popup: true,
4192
+ // types: ['standard', 'polygons', 'lines', 'labels', 'points']
4193
+ // }, {
4194
+ // name: 'selection',
4195
+ // label: 'select features',
4196
+ // selection: true,
4197
+ // popup: true,
4198
+ // types: ['standard', 'polygons', 'lines', 'table', 'labels']
4199
+ // }]
4200
+
4170
4201
  var menus = {
4171
4202
  standard: ['info', 'selection', 'data', 'box'],
4172
4203
  polygons: ['info', 'selection', 'data', 'box', 'vertices'],
4173
4204
  lines: ['info', 'selection', 'data', 'box', 'vertices'],
4174
4205
  table: ['info', 'selection', 'data'],
4175
4206
  labels: ['info', 'selection', 'data', 'box', 'labels', 'location'],
4207
+ // points: ['info', 'selection', 'data', 'box', 'location', 'add-points']
4176
4208
  points: ['info', 'selection', 'data', 'box', 'location']
4177
4209
  };
4178
4210
 
@@ -4191,6 +4223,7 @@
4191
4223
  location: 'drag points',
4192
4224
  vertices: 'edit vertices',
4193
4225
  selection: 'select features',
4226
+ 'add-points': 'add points',
4194
4227
  off: 'turn off'
4195
4228
  };
4196
4229
  var btn, menu;
@@ -4243,6 +4276,10 @@
4243
4276
  setMode('off');
4244
4277
  };
4245
4278
 
4279
+ this.modeUsesSelection = function(mode) {
4280
+ return ['info', 'selection', 'data', 'labels', 'location', 'vertices'].includes(mode);
4281
+ };
4282
+
4246
4283
  this.modeUsesPopup = function(mode) {
4247
4284
  return ['info', 'selection', 'data', 'box', 'labels', 'location'].includes(mode);
4248
4285
  };
@@ -6289,10 +6326,11 @@
6289
6326
  // (some modes do not support pinning)
6290
6327
  gui.on('interaction_mode_change', function(e) {
6291
6328
  updateSelectionState(null);
6292
- if (e.mode == 'off' || e.mode == 'box') {
6293
- turnOff();
6294
- } else {
6329
+ // if (e.mode == 'off' || e.mode == 'box') {
6330
+ if (gui.interaction.modeUsesSelection(e.mode)) {
6295
6331
  turnOn(e.mode);
6332
+ } else {
6333
+ turnOff();
6296
6334
  }
6297
6335
  });
6298
6336
 
@@ -7637,7 +7675,14 @@
7637
7675
  });
7638
7676
 
7639
7677
  _popup.on('update', function(e) {
7640
- _self.dispatchEvent('data_change', e.data); // let map know which field has changed
7678
+ // data_change event no longer needed (update is handled below)
7679
+ // _self.dispatchEvent('data_change', e.data); // let map know which field has changed
7680
+ gui.session.dataValueUpdated(e.id, e.field, e.value);
7681
+ // Refresh the display if a style variable has been changed interactively
7682
+ if (internal.isSupportedSvgStyleProperty(e.field)) {
7683
+ // drawLayers();
7684
+ gui.dispatchEvent('map-needs-refresh');
7685
+ }
7641
7686
  });
7642
7687
 
7643
7688
  hit.on('change', function(e) {
@@ -8135,10 +8180,6 @@
8135
8180
  initPointDragging(gui, ext, hit);
8136
8181
  initVertexDragging(gui, ext, hit);
8137
8182
 
8138
- gui.on('interaction_mode_change', function(e) {
8139
- gui.undo.clear(); // TODO: put this elsewhere?
8140
- });
8141
-
8142
8183
  // function isClickEvent(up, down) {
8143
8184
  // var elapsed = Math.abs(down.timeStamp - up.timeStamp);
8144
8185
  // var dx = up.screenX - down.screenX;
@@ -8148,6 +8189,39 @@
8148
8189
  // }
8149
8190
  }
8150
8191
 
8192
+ function initPointDrawing(gui, ext, hit) {
8193
+ function active(e) {
8194
+ return e.id > -1 && gui.interaction.getMode() == 'add-points';
8195
+ }
8196
+
8197
+ }
8198
+
8199
+ function Pencil(gui, mouse, hit) {
8200
+ var self = this;
8201
+ var _on = false;
8202
+
8203
+ self.turnOn = function() {
8204
+ _on = true;
8205
+ };
8206
+
8207
+ self.turnOff = function() {
8208
+ _on = false;
8209
+ };
8210
+
8211
+
8212
+
8213
+
8214
+
8215
+
8216
+ }
8217
+
8218
+ function initDrawing(gui, ext, mouse, hit) {
8219
+
8220
+ initPointDrawing(gui, new Pencil(gui, mouse, hit));
8221
+
8222
+
8223
+ }
8224
+
8151
8225
  var darkStroke = "#334",
8152
8226
  lightStroke = "#b7d9ea",
8153
8227
  violet = "#cc6acc",
@@ -8473,27 +8547,27 @@
8473
8547
  function MapExtent(_position) {
8474
8548
  var _scale = 1,
8475
8549
  _cx, _cy, // center in geographic units
8476
- _contentBounds,
8550
+ _fullBounds, // full (zoomed-out) content bounds, including any padding
8477
8551
  _strictBounds, // full extent must fit inside, if set
8478
8552
  _self = this,
8479
8553
  _frame;
8480
8554
 
8481
8555
  _position.on('resize', function(e) {
8482
8556
  if (ready()) {
8483
- onChange({resize: true});
8557
+ triggerChangeEvent({resize: true});
8484
8558
  }
8485
8559
  });
8486
8560
 
8487
- function ready() { return !!_contentBounds; }
8561
+ function ready() { return !!_fullBounds; }
8488
8562
 
8489
8563
  this.reset = function() {
8490
8564
  if (!ready()) return;
8491
- recenter(_contentBounds.centerX(), _contentBounds.centerY(), 1, {reset: true});
8565
+ recenter(_fullBounds.centerX(), _fullBounds.centerY(), 1, {reset: true});
8492
8566
  };
8493
8567
 
8494
8568
  this.home = function() {
8495
8569
  if (!ready()) return;
8496
- recenter(_contentBounds.centerX(), _contentBounds.centerY(), 1);
8570
+ recenter(_fullBounds.centerX(), _fullBounds.centerY(), 1);
8497
8571
  };
8498
8572
 
8499
8573
  this.pan = function(xpix, ypix) {
@@ -8560,30 +8634,29 @@
8560
8634
 
8561
8635
  // k scales the size of the bbox (used by gui to control fp error when zoomed very far)
8562
8636
  this.getBounds = function(k) {
8563
- if (!_contentBounds) return new Bounds();
8637
+ if (!_fullBounds) return new Bounds();
8564
8638
  return calcBounds(_cx, _cy, _scale / (k || 1));
8565
8639
  };
8566
8640
 
8567
8641
  // Update the extent of 'full' zoom without navigating the current view
8568
8642
  //
8569
- this.setBounds = function(contentBounds, strictBounds) {
8570
- var b = contentBounds;
8571
- var prev = _contentBounds;
8643
+ this.setFullBounds = function(fullBounds, strictBounds) {
8644
+ var prev = _fullBounds;
8645
+ var b = _fullBounds = fullBounds;
8572
8646
  if (!b.hasBounds()) return; // kludge
8573
8647
  if (strictBounds) {
8574
8648
  _strictBounds = Array.isArray(strictBounds) ? new Bounds(strictBounds) : strictBounds;
8575
8649
  } else {
8576
8650
  _strictBounds = null;
8577
8651
  }
8578
- _contentBounds = _frame ? b : padBounds(b, 4); // padding if not in frame mode
8579
8652
  if (_strictBounds) {
8580
- _contentBounds = fitIn(_contentBounds, _strictBounds);
8653
+ _fullBounds = fitIn(_fullBounds, _strictBounds);
8581
8654
  }
8582
8655
  if (prev) {
8583
- _scale = _scale * fillOut(_contentBounds).width() / fillOut(prev).width();
8656
+ _scale = _scale * fillOut(_fullBounds).width() / fillOut(prev).width();
8584
8657
  } else {
8585
- _cx = _contentBounds.centerX();
8586
- _cy = _contentBounds.centerY();
8658
+ _cx = _fullBounds.centerX();
8659
+ _cy = _fullBounds.centerY();
8587
8660
  }
8588
8661
  };
8589
8662
 
@@ -8614,12 +8687,12 @@
8614
8687
  scale = scale ? limitScale(scale) : _scale;
8615
8688
  if (cx == _cx && cy == _cy && scale == _scale) return;
8616
8689
  navigate(cx, cy, scale);
8617
- onChange(data);
8690
+ triggerChangeEvent(data);
8618
8691
  }
8619
8692
 
8620
8693
  function navigate(cx, cy, scale) {
8621
8694
  if (_strictBounds) {
8622
- var full = fillOut(_contentBounds);
8695
+ var full = fillOut(_fullBounds);
8623
8696
  var minScale = full.height() / _strictBounds.height();
8624
8697
  if (scale < minScale) {
8625
8698
  var dx = cx - _cx;
@@ -8641,7 +8714,7 @@
8641
8714
  _scale = scale;
8642
8715
  }
8643
8716
 
8644
- function onChange(data) {
8717
+ function triggerChangeEvent(data) {
8645
8718
  data = data || {};
8646
8719
  _self.dispatchEvent('change', data);
8647
8720
  }
@@ -8649,10 +8722,10 @@
8649
8722
  // stop zooming before rounding errors become too obvious
8650
8723
  function maxScale() {
8651
8724
  var minPixelScale = 1e-16;
8652
- var xmax = maxAbs(_contentBounds.xmin, _contentBounds.xmax, _contentBounds.centerX());
8653
- var ymax = maxAbs(_contentBounds.ymin, _contentBounds.ymax, _contentBounds.centerY());
8654
- var xscale = _contentBounds.width() / _position.width() / xmax / minPixelScale;
8655
- var yscale = _contentBounds.height() / _position.height() / ymax / minPixelScale;
8725
+ var xmax = maxAbs(_fullBounds.xmin, _fullBounds.xmax, _fullBounds.centerX());
8726
+ var ymax = maxAbs(_fullBounds.ymin, _fullBounds.ymax, _fullBounds.centerY());
8727
+ var xscale = _fullBounds.width() / _position.width() / xmax / minPixelScale;
8728
+ var yscale = _fullBounds.height() / _position.height() / ymax / minPixelScale;
8656
8729
  return Math.min(xscale, yscale);
8657
8730
  }
8658
8731
 
@@ -8669,7 +8742,7 @@
8669
8742
  if (_frame) {
8670
8743
  full = fillOutFrameBounds(_frame);
8671
8744
  } else {
8672
- full = fillOut(_contentBounds);
8745
+ full = fillOut(_fullBounds);
8673
8746
  }
8674
8747
  if (_strictBounds) {
8675
8748
  full = fitIn(full, _strictBounds);
@@ -8689,9 +8762,9 @@
8689
8762
  return bounds;
8690
8763
  }
8691
8764
 
8692
- function padBounds(b, margin) {
8693
- var wpix = _position.width() - 2 * margin,
8694
- hpix = _position.height() - 2 * margin,
8765
+ function padBounds(b, marginpix) {
8766
+ var wpix = _position.width() - 2 * marginpix,
8767
+ hpix = _position.height() - 2 * marginpix,
8695
8768
  xpad, ypad, b2;
8696
8769
  if (wpix <= 0 || hpix <= 0) {
8697
8770
  return new Bounds(0, 0, 0, 0);
@@ -8699,8 +8772,8 @@
8699
8772
  b = b.clone();
8700
8773
  b2 = b.clone();
8701
8774
  b2.fillOut(wpix / hpix);
8702
- xpad = b2.width() / wpix * margin;
8703
- ypad = b2.height() / hpix * margin;
8775
+ xpad = b2.width() / wpix * marginpix;
8776
+ ypad = b2.height() / hpix * marginpix;
8704
8777
  b.padBounds(xpad, ypad, xpad, ypad);
8705
8778
  return b;
8706
8779
  }
@@ -8830,8 +8903,8 @@
8830
8903
  function getPixelColorFunction() {
8831
8904
  var canv = El('canvas').node();
8832
8905
  canv.width = canv.height = 1;
8906
+ var ctx = canv.getContext('2d', {willReadFrequently: true});
8833
8907
  return function(col) {
8834
- var ctx = canv.getContext('2d');
8835
8908
  var pixels;
8836
8909
  ctx.fillStyle = col;
8837
8910
  ctx.fillRect(0, 0, 1, 1);
@@ -8843,6 +8916,8 @@
8843
8916
  function DisplayCanvas() {
8844
8917
  var _self = El('canvas'),
8845
8918
  _canvas = _self.node(),
8919
+ // TODO: compare performance of willReadFrequently setting
8920
+ // _ctx = _canvas.getContext('2d', {willReadFrequently: true}),
8846
8921
  _ctx = _canvas.getContext('2d'),
8847
8922
  _pixelColor = getPixelColorFunction(),
8848
8923
  _ext;
@@ -8997,7 +9072,6 @@
8997
9072
  var w = _canvas.width,
8998
9073
  h = _canvas.height,
8999
9074
  rgba = _pixelColor(color),
9000
- // imageData = _ctx.createImageData(w, h),
9001
9075
  imageData = _ctx.getImageData(0, 0, w, h),
9002
9076
  pixels = new Uint32Array(imageData.data.buffer),
9003
9077
  shp, x, y, i, j, n, m,
@@ -10083,7 +10157,6 @@
10083
10157
  return bl.concat(tr);
10084
10158
  }
10085
10159
 
10086
-
10087
10160
  function initMap() {
10088
10161
  if (!enabled() || map || loading) return;
10089
10162
  loading = true;
@@ -10177,17 +10250,15 @@
10177
10250
  map = this,
10178
10251
  _mouse = new MouseArea(el, position),
10179
10252
  _ext = new MapExtent(position),
10180
- _hit = new InteractiveSelection(gui, _ext, _mouse),
10181
10253
  _nav = new MapNav(gui, _ext, _mouse),
10182
- _boxTool = new BoxTool(gui, _ext, _nav),
10183
- _selectionTool = new SelectionTool(gui, _ext, _hit),
10184
10254
  _visibleLayers = [], // cached visible map layers
10185
10255
  _fullBounds = null,
10256
+ _hit,
10257
+ _basemap,
10186
10258
  _intersectionLyr, _activeLyr, _overlayLyr,
10187
- _inspector, _stack,
10188
- _dynamicCRS;
10259
+ _stack, _dynamicCRS;
10189
10260
 
10190
- var _basemap = new Basemap(gui, _ext);
10261
+ _basemap = new Basemap(gui, _ext);
10191
10262
 
10192
10263
  if (gui.options.showMouseCoordinates) {
10193
10264
  new CoordinatesDisplay(gui, _ext, _mouse);
@@ -10326,16 +10397,16 @@
10326
10397
 
10327
10398
  _activeLyr = getDisplayLayer(e.layer, e.dataset, getDisplayOptions());
10328
10399
  _activeLyr.style = getActiveStyle(_activeLyr.layer, gui.state.dark_basemap);
10329
-
10330
10400
  _activeLyr.active = true;
10331
- // if (_inspector) _inspector.updateLayer(_activeLyr);
10332
- _hit.setLayer(_activeLyr);
10401
+
10333
10402
  if (e.flags.same_table) {
10334
10403
  // data may have changed; if popup is open, it needs to be refreshed
10335
10404
  gui.dispatchEvent('popup-needs-refresh');
10336
- } else {
10405
+ } else if (_hit) {
10406
+ _hit.setLayer(_activeLyr);
10337
10407
  _hit.clearSelection();
10338
10408
  }
10409
+
10339
10410
  updateVisibleMapLayers();
10340
10411
  fullBounds = getFullBounds();
10341
10412
 
@@ -10352,7 +10423,8 @@
10352
10423
  } else {
10353
10424
  _nav.setZoomFactor(1);
10354
10425
  }
10355
- _ext.setBounds(fullBounds, getStrictBounds()); // update 'home' button extent
10426
+ _ext.setFullBounds(fullBounds, getStrictBounds()); // update 'home' button extent
10427
+
10356
10428
  _fullBounds = fullBounds;
10357
10429
  if (needReset) {
10358
10430
  _ext.reset();
@@ -10368,19 +10440,13 @@
10368
10440
  gui.buttons.show();
10369
10441
 
10370
10442
  if (opts.inspectorControl) {
10371
- _inspector = new InspectionControl2(gui, _hit);
10372
- _inspector.on('data_change', function(e) {
10373
- // Add an entry to the session history
10374
- gui.session.dataValueUpdated(e.id, e.field, e.value);
10375
- // Refresh the display if a style variable has been changed interactively
10376
- if (internal.isSupportedSvgStyleProperty(e.field)) {
10377
- drawLayers();
10378
- }
10379
- });
10380
- }
10381
-
10382
- if (gui.interaction) {
10443
+ _hit = new InteractiveSelection(gui, _ext, _mouse),
10444
+ new InspectionControl2(gui, _hit);
10445
+ new SelectionTool(gui, _ext, _hit),
10446
+ new BoxTool(gui, _ext, _nav),
10383
10447
  initInteractiveEditing(gui, _ext, _hit);
10448
+ // initDrawing(gui, _ext, _mouse, _hit);
10449
+ _hit.on('change', updateOverlayLayer);
10384
10450
  }
10385
10451
 
10386
10452
  _ext.on('change', function(e) {
@@ -10392,8 +10458,6 @@
10392
10458
  drawLayers('nav');
10393
10459
  });
10394
10460
 
10395
- _hit.on('change', updateOverlayLayer);
10396
-
10397
10461
  gui.on('resize', function() {
10398
10462
  position.update(); // kludge to detect new map size after console toggle
10399
10463
  });
@@ -10438,7 +10502,7 @@
10438
10502
  var cy = viewBounds.centerY();
10439
10503
  rec.bbox = [cx - w/2, cy - h/2, cx + w/2, cy + h/2];
10440
10504
  _ext.setFrame(getFrameData());
10441
- _ext.setBounds(new Bounds(rec.bbox));
10505
+ _ext.setFullBounds(new Bounds(rec.bbox));
10442
10506
  _ext.reset();
10443
10507
  }
10444
10508
 
@@ -10450,39 +10514,33 @@
10450
10514
  }
10451
10515
 
10452
10516
  function getFullBounds() {
10453
- var b = new Bounds();
10454
- var marginPct = 0.025;
10455
- var pad = 1e-4;
10456
10517
  if (isPreviewView()) {
10457
10518
  return internal.getFrameLayerBounds(internal.findFrameLayer(model));
10458
10519
  }
10520
+ var b = new Bounds();
10459
10521
  getDrawableContentLayers().forEach(function(lyr) {
10460
10522
  b.mergeBounds(lyr.bounds);
10461
- if (isTableView()) {
10462
- marginPct = getTableMargin(lyr.layer);
10463
- }
10464
10523
  });
10524
+
10465
10525
  if (!b.hasBounds()) {
10466
10526
  // assign bounds to empty layers, to prevent rendering errors downstream
10467
10527
  b.setBounds(0,0,0,0);
10468
10528
  }
10469
- // Inflate display bounding box by a tiny amount (gives extent to single-point layers and collapsed shapes)
10470
- b.padBounds(pad,pad,pad,pad);
10471
- // add margin
10472
- b.scale(1 + marginPct * 2);
10473
- return b;
10474
- }
10475
10529
 
10476
- // Calculate margin when displaying content at full zoom, as pct of screen size
10477
- function getTableMargin(lyr) {
10478
- var n = internal.getFeatureCount(lyr);
10479
- var pct = 0.04;
10480
- if (n < 5) {
10481
- pct = 0.2;
10482
- } else if (n < 100) {
10483
- pct = 0.1;
10530
+ // add margin
10531
+ // use larger margin for small sizes
10532
+ var widthPx = _ext.width();
10533
+ var marginPct = widthPx < 700 && 3.5 || widthPx < 800 && 3 || 2.5;
10534
+ if (isTableView()) {
10535
+ var n = internal.getFeatureCount(_activeLyr.layer);
10536
+ marginPct = n < 5 && 20 || n < 100 && 10 || 4;
10484
10537
  }
10485
- return pct;
10538
+ b.scale(1 + marginPct / 100 * 2);
10539
+
10540
+ // Inflate display bounding box by a tiny amount (gives extent to single-point layers and collapsed shapes)
10541
+ b.padBounds(1e-4, 1e-4, 1e-4, 1e-4);
10542
+
10543
+ return b;
10486
10544
  }
10487
10545
 
10488
10546
  function isActiveLayer(lyr) {
@@ -10615,7 +10673,7 @@
10615
10673
  if (layersMayHaveChanged) {
10616
10674
  // kludge to handle layer visibility toggling
10617
10675
  _ext.setFrame(isPreviewView() ? getFrameData() : null);
10618
- _ext.setBounds(getFullBounds(), getStrictBounds());
10676
+ _ext.setFullBounds(getFullBounds(), getStrictBounds());
10619
10677
  updateLayerStyles(contentLayers);
10620
10678
  updateLayerStackOrder(model.getLayers());// update stack_id property of all layers
10621
10679
  }