mapshaper 0.6.0 → 0.6.4

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.0",
3
+ "version": "0.6.4",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -9,7 +9,7 @@
9
9
  "cartography",
10
10
  "simplification",
11
11
  "topology",
12
- "csv",
12
+ "csv",
13
13
  "gis"
14
14
  ],
15
15
  "author": "Matthew Bloch <masiyou@gmail.com>",
@@ -25,7 +25,8 @@
25
25
  "scripts": {
26
26
  "test": "mocha test",
27
27
  "build": "rollup --config",
28
- "prepublishOnly": "npm test; ./pre-publish",
28
+ "lint": "eslint --ext mjs src/",
29
+ "prepublishOnly": "npm lint; npm test; ./pre-publish",
29
30
  "postpublish": "./release_web_ui; ./release_github_version",
30
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
32
  "dev": "rollup --config --watch"
@@ -35,7 +36,7 @@
35
36
  "/bin/**",
36
37
  "/www/**",
37
38
  "!/www/basemap.js",
38
- "!/www/nacis/",
39
+ "!/www/nacis/**",
39
40
  "/mapshaper.js",
40
41
  "!.DS_Store"
41
42
  ],
@@ -61,6 +62,7 @@
61
62
  "@rollup/plugin-node-resolve": "^13.3.0",
62
63
  "browserify": "^17.0.0",
63
64
  "csv-spectrum": "^1.0.0",
65
+ "eslint": "^8.16.0",
64
66
  "mocha": "^10.0.0",
65
67
  "rollup": "^2.73.0",
66
68
  "shell-quote": "^1.6.1",
@@ -71,6 +73,7 @@
71
73
  "node-option": [
72
74
  "experimental-specifier-resolution=node"
73
75
  ],
76
+ "check-leaks": true,
74
77
  "parallel": true,
75
78
  "jobs": 4
76
79
  },
@@ -78,6 +78,7 @@
78
78
  get findValueByPct () { return findValueByPct; },
79
79
  get findValueByRank () { return findValueByRank; },
80
80
  get findMedian () { return findMedian; },
81
+ get findQuantile () { return findQuantile; },
81
82
  get mean () { return mean; },
82
83
  get format () { return format; },
83
84
  get formatter () { return formatter; },
@@ -1682,7 +1683,6 @@
1682
1683
  }
1683
1684
 
1684
1685
  function Slider(ref, opts) {
1685
- var _el = El(ref);
1686
1686
  var _self = this;
1687
1687
  var defaults = {
1688
1688
  space: 7
@@ -1713,7 +1713,7 @@
1713
1713
  _handle = El(ref);
1714
1714
  draggable(_handle)
1715
1715
  .on('drag', function(e) {
1716
- setHandlePos(startX + e.dx, true);
1716
+ setHandlePos(startX + e.dx);
1717
1717
  })
1718
1718
  .on('dragstart', function(e) {
1719
1719
  startX = position();
@@ -1739,7 +1739,7 @@
1739
1739
  return _pct;
1740
1740
  };
1741
1741
 
1742
- function setHandlePos(x, fire) {
1742
+ function setHandlePos(x) {
1743
1743
  x = utils$1.clamp(x, 0, size());
1744
1744
  var pct = x / size();
1745
1745
  if (pct != _pct) {
@@ -3122,7 +3122,7 @@
3122
3122
 
3123
3123
  function cleanLayerName(raw) {
3124
3124
  return raw.replace(/[\n\t/\\]/g, '')
3125
- .replace(/^[\.\s]+/, '').replace(/[\.\s]+$/, '');
3125
+ .replace(/^[.\s]+/, '').replace(/[.\s]+$/, '');
3126
3126
  }
3127
3127
 
3128
3128
  function updateLayerStackOrder(layers) {
@@ -3856,8 +3856,6 @@
3856
3856
  }
3857
3857
  }
3858
3858
 
3859
- var snapVerticesToPoint = internal.snapVerticesToPoint;
3860
- var cloneShape = internal.cloneShape;
3861
3859
  var copyRecord = internal.copyRecord;
3862
3860
 
3863
3861
  function Undo(gui) {
@@ -4741,7 +4739,7 @@
4741
4739
  '/': '&#x2F;'
4742
4740
  };
4743
4741
  function htmlEscape(s) {
4744
- return String(s).replace(/[&<>"'\/]/g, function(s) {
4742
+ return String(s).replace(/[&<>"'/]/g, function(s) {
4745
4743
  return entityMap[s];
4746
4744
  });
4747
4745
  }
@@ -5315,18 +5313,24 @@
5315
5313
  return arr[k];
5316
5314
  }
5317
5315
 
5318
- //
5319
- //
5320
5316
  function findMedian(arr) {
5321
- var n = arr.length,
5322
- rank = Math.floor(n / 2) + 1,
5323
- median = findValueByRank(arr, rank);
5324
- if ((n & 1) == 0) {
5325
- median = (median + findValueByRank(arr, rank - 1)) / 2;
5326
- }
5327
- return median;
5317
+ return findQuantile(arr, 0.5);
5328
5318
  }
5329
5319
 
5320
+ function findQuantile(arr, k) {
5321
+ var n = arr.length,
5322
+ i1 = Math.floor((n - 1) * k),
5323
+ i2 = Math.ceil((n - 1) * k);
5324
+ if (i1 < 0 || i2 >= n) return NaN;
5325
+ var v1 = findValueByRank(arr, i1 + 1);
5326
+ if (i1 == i2) return v1;
5327
+ var v2 = findValueByRank(arr, i2 + 1);
5328
+ // use linear interpolation
5329
+ var w1 = i2 / (n - 1) - k;
5330
+ var w2 = k - i1 / (n - 1);
5331
+ var v = (v1 * w1 + v2 * w2) * (n - 1);
5332
+ return v;
5333
+ }
5330
5334
 
5331
5335
  function mean(arr) {
5332
5336
  var count = 0,
@@ -5384,8 +5388,8 @@
5384
5388
  var type = matches[4];
5385
5389
  var isString = type == 's',
5386
5390
  isHex = type == 'x' || type == 'X',
5387
- isInt = type == 'd' || type == 'i',
5388
- isFloat = type == 'f',
5391
+ // isInt = type == 'd' || type == 'i',
5392
+ // isFloat = type == 'f',
5389
5393
  isNumber = !isString;
5390
5394
 
5391
5395
  var sign = "",
@@ -5444,7 +5448,7 @@
5444
5448
 
5445
5449
  // Get a function for interpolating formatted values into a string.
5446
5450
  function formatter(fmt) {
5447
- var codeRxp = /%([\',+0]*)([1-9]?)((?:\.[1-9])?)([sdifxX%])/g;
5451
+ var codeRxp = /%([',+0]*)([1-9]?)((?:\.[1-9])?)([sdifxX%])/g;
5448
5452
  var literals = [],
5449
5453
  formatCodes = [],
5450
5454
  startIdx = 0,
@@ -5844,27 +5848,21 @@
5844
5848
 
5845
5849
  function pointTest(x, y) {
5846
5850
  var bullseyeDist = 2, // hit all points w/in 2 px
5847
- tinyDist = 0.5,
5848
- toPx = ext.getTransform().mx,
5849
- hits = [],
5850
5851
  hitThreshold = 25,
5851
- newThreshold = Infinity;
5852
+ toPx = ext.getTransform().mx,
5853
+ hits = [];
5852
5854
 
5855
+ // inlining forEachPoint() does not not appreciably speed this up
5853
5856
  internal.forEachPoint(displayLayer.layer.shapes, function(p, id) {
5854
5857
  var dist = geom.distance2D(x, y, p[0], p[1]) * toPx;
5855
5858
  if (dist > hitThreshold) return;
5856
- // got a hit
5857
- if (dist < newThreshold) {
5858
- // start a collection of hits
5859
- hits = [id];
5860
- hitThreshold = Math.max(bullseyeDist, dist + tinyDist);
5861
- newThreshold = dist < bullseyeDist ? -1 : dist - tinyDist;
5862
- } else {
5863
- // add to hits if inside bullseye or is same dist as previous hit
5864
- hits.push(id);
5859
+ if (dist < hitThreshold && hitThreshold > bullseyeDist) {
5860
+ hits = [];
5861
+ hitThreshold = Math.max(bullseyeDist, dist);
5865
5862
  }
5863
+ hits.push(id);
5866
5864
  });
5867
- // TODO: add info on what part of a shape gets hit
5865
+ // TODO: add info on what part of a shape gets hit?
5868
5866
  return {
5869
5867
  ids: utils$1.uniq(hits) // multipoint features can register multiple hits
5870
5868
  };
@@ -8273,20 +8271,25 @@
8273
8271
  _frame;
8274
8272
 
8275
8273
  _position.on('resize', function(e) {
8276
- if (_contentBounds) {
8274
+ if (ready()) {
8277
8275
  onChange({resize: true});
8278
8276
  }
8279
8277
  });
8280
8278
 
8279
+ function ready() { return !!_contentBounds; }
8280
+
8281
8281
  this.reset = function() {
8282
+ if (!ready()) return;
8282
8283
  recenter(_contentBounds.centerX(), _contentBounds.centerY(), 1, {reset: true});
8283
8284
  };
8284
8285
 
8285
8286
  this.home = function() {
8287
+ if (!ready()) return;
8286
8288
  recenter(_contentBounds.centerX(), _contentBounds.centerY(), 1);
8287
8289
  };
8288
8290
 
8289
8291
  this.pan = function(xpix, ypix) {
8292
+ if (!ready()) return;
8290
8293
  var t = this.getTransform();
8291
8294
  recenter(_cx - xpix / t.mx, _cy - ypix / t.my);
8292
8295
  };
@@ -8294,6 +8297,7 @@
8294
8297
  // Zoom to @w (width of the map viewport in coordinates)
8295
8298
  // @xpct, @ypct: optional focus, [0-1]...
8296
8299
  this.zoomToExtent = function(w, xpct, ypct) {
8300
+ if (!ready()) return;
8297
8301
  if (arguments.length < 3) {
8298
8302
  xpct = 0.5;
8299
8303
  ypct = 0.5;
@@ -8313,6 +8317,7 @@
8313
8317
  };
8314
8318
 
8315
8319
  this.zoomByPct = function(pct, xpct, ypct) {
8320
+ if (!ready()) return;
8316
8321
  this.zoomToExtent(this.getBounds().width() / pct, xpct, ypct);
8317
8322
  };
8318
8323
 
@@ -8754,7 +8759,7 @@
8754
8759
  bx = t.bx,
8755
8760
  by = t.by;
8756
8761
  if (size === 0) return;
8757
- if (size <= 4 && !styler) {
8762
+ if (size <= 6 && !styler) {
8758
8763
  // optimized drawing of many small same-colored dots
8759
8764
  _self.drawSquareDotsFaster(shapes, color, size, t);
8760
8765
  return;
@@ -8811,14 +8816,12 @@
8811
8816
  // pixels: Uint32Array of pixel colors
8812
8817
  // w, h: Size of canvas
8813
8818
  function drawSquareFaster(x, y, rgba, size, pixels, w, h) {
8814
- var xmin = (x - size * 0.5) | 0;
8815
- var ymin = (y - size * 0.5) | 0;
8816
- var xmax = xmin + size - 1;
8817
- var ymax = ymin + size - 1;
8818
- var c, r;
8819
- for (c = xmin; c <= xmax; c++) {
8820
- if (c < 0 || c >= w) continue;
8821
- for (r = ymin; r <= ymax && r >= 0 && r < h; r++) {
8819
+ var xmin = x < 0 ? 0 : (x - size * 0.5) | 0;
8820
+ var ymin = y < 0 ? 0 : (y - size * 0.5) | 0;
8821
+ var xmax = x >= w-1 ? w-1 : xmin + size - 1;
8822
+ var ymax = y >= h-1 ? h-1 : ymin + size - 1;
8823
+ for (var r = ymin; r <= ymax; r++) {
8824
+ for (var c = xmin; c <= xmax; c++) {
8822
8825
  pixels[r * w + c] = rgba;
8823
8826
  }
8824
8827
  }
@@ -8905,24 +8908,25 @@
8905
8908
  return s;
8906
8909
  }
8907
8910
 
8908
-
8909
8911
  function getDotScale(ext) {
8910
8912
  var smallSide = Math.min(ext.width(), ext.height());
8913
+ var mapScale = ext.scale();
8911
8914
  // reduce size on smaller screens
8912
8915
  var j = smallSide < 200 && 0.5 || smallSide < 400 && 0.75 || 1;
8916
+ // grow dots as map zooms in
8913
8917
  var k = 1;
8914
- var mapScale = ext.scale();
8915
8918
  if (mapScale < 0.5) {
8916
8919
  k = Math.pow(mapScale + 0.5, 0.35);
8917
- }
8918
- if (mapScale > 1) {
8920
+ } else if (mapScale > 1) {
8919
8921
  // scale faster at first, so small dots in large datasets
8920
8922
  // become easily visible and clickable after zooming in a bit
8921
8923
  k *= Math.pow(Math.min(mapScale, 10), 0.3);
8922
8924
  k *= Math.pow(mapScale, 0.1);
8923
8925
  }
8924
-
8925
- return k * j * GUI.getPixelRatio();
8926
+ // grow pixels more slowly on retina displays (to reduce number of pixels to
8927
+ // draw for large point datasets when slightly zoomed in)
8928
+ var l = Math.pow(GUI.getPixelRatio(), 0.8);
8929
+ return j * k * l;
8926
8930
  }
8927
8931
 
8928
8932
  function getScaledTransform(ext) {
@@ -9495,7 +9499,7 @@
9495
9499
 
9496
9500
  // Only generate low-detail arcs for larger datasets
9497
9501
  if (size > 5e5) {
9498
- if (!!unfilteredArcs.getVertexData().zz) {
9502
+ if (unfilteredArcs.getVertexData().zz) {
9499
9503
  // Use precalculated simplification data for vertex filtering, if available
9500
9504
  filteredArcs = initFilteredArcs(unfilteredArcs);
9501
9505
  filteredSegLen = internal.getAvgSegment(filteredArcs);
@@ -9861,7 +9865,13 @@
9861
9865
  var warning;
9862
9866
 
9863
9867
  if (!dataCRS || !displayCRS || !crsIsUsable(displayCRS) || !crsIsUsable(dataCRS)) {
9864
- warning = 'The current layer is not compatible with the projection used by the basemaps.';
9868
+ warning = 'This data is incompatible with the basemaps.';
9869
+ if (!internal.layerHasGeometry(activeLyr.layer)) {
9870
+ warning += ' Reason: layer is missing geographic data';
9871
+ } else if (!dataCRS) {
9872
+ warning += ' Reason: unknown projection.';
9873
+ }
9874
+
9865
9875
  basemapWarning.html(warning).show();
9866
9876
  basemapNote.hide();
9867
9877
  } else {