mapshaper 0.6.78 → 0.6.80

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
@@ -1612,6 +1612,7 @@
1612
1612
  this.ymin -= halfHeight * ky;
1613
1613
  this.xmax += halfWidth * kx;
1614
1614
  this.ymax += halfHeight * ky;
1615
+ return this;
1615
1616
  };
1616
1617
 
1617
1618
  // Return a bounding box with the same extent as this one.
@@ -5003,7 +5004,7 @@
5003
5004
  if (isLatLngCRS(P)) {
5004
5005
  return xy.concat();
5005
5006
  }
5006
- proj = getProjTransform(P, parseCrsString('wgs84'));
5007
+ proj = getProjTransform2(P, parseCrsString('wgs84'));
5007
5008
  return proj(xy[0], xy[1]);
5008
5009
  }
5009
5010
 
@@ -26664,7 +26665,7 @@ ${svg}
26664
26665
  codepage = lookupCodePage(ldid),
26665
26666
  samples = getNonAsciiSamples(),
26666
26667
  only7bit = samples.length === 0,
26667
- encoding, msg;
26668
+ encoding;
26668
26669
 
26669
26670
  // First, check the ldid (language driver id) (an obsolete way to specify which
26670
26671
  // codepage to use for text encoding.)
@@ -26684,10 +26685,12 @@ ${svg}
26684
26685
  // As a last resort, try to guess the encoding:
26685
26686
  if (!encoding) {
26686
26687
  var info = detectEncoding(samples);
26688
+ var msg;
26687
26689
  encoding = info.encoding;
26688
26690
  if (info.confidence < 2) {
26689
- msg = 'Warning: Unable to auto-detect the DBF file text encoding with high confidence.';
26690
- msg += '\n\nDefaulting to: ' + encoding + (encoding in encodingNames ? ' (' + encodingNames[encoding] + ')' : '');
26691
+ msg = 'Warning: Unable to auto-detect the DBF file text encoding' +
26692
+ (info.confidence == 1 ? ' with high confidence' : '') + '.';
26693
+ msg += ' Defaulting to ' + encoding + (encoding in encodingNames ? ' (' + encodingNames[encoding] + ').' : '.');
26691
26694
  msg += '\n\nSample of how non-ascii text was imported:\n';
26692
26695
  if (runningInBrowser()) {
26693
26696
  msg += '<pre>' + formatStringsAsGrid(decodeSamples(encoding, samples), 50) + '</pre>';
@@ -45532,7 +45535,7 @@ ${svg}
45532
45535
  });
45533
45536
  }
45534
45537
 
45535
- var version = "0.6.78";
45538
+ var version = "0.6.80";
45536
45539
 
45537
45540
  // Parse command line args into commands and run them
45538
45541
  // 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.6.78",
3
+ "version": "0.6.80",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -2544,6 +2544,20 @@
2544
2544
  };
2545
2545
  }
2546
2546
 
2547
+ // p1: [x, y] coords
2548
+ // p2: [x, y] coords offset by 1x1 pixel
2549
+ function formatCoordsForDisplay(p1, p2) {
2550
+ var dx = Math.abs(p1[0] - p2[0]);
2551
+ var dy = Math.abs(p1[1] - p2[1]);
2552
+ var offs = (dx + dy) / 2;
2553
+ var decimals = 0;
2554
+ while (offs < 1 && decimals < 6) {
2555
+ offs *= 10;
2556
+ decimals++;
2557
+ }
2558
+ return [p1[0].toFixed(decimals), p1[1].toFixed(decimals)];
2559
+ }
2560
+
2547
2561
  // Convert a point from display CRS coordinates to data coordinates.
2548
2562
  // These are only different when using dynamic reprojection (basemap view).
2549
2563
  function translateDisplayPoint(lyr, p) {
@@ -2563,8 +2577,7 @@
2563
2577
  }
2564
2578
 
2565
2579
  function isProjectedLayer(lyr) {
2566
- // TODO: could do some validation on the layer's contents
2567
- return !!lyr.gui.invertPoint;
2580
+ return !!lyr?.gui.invertPoint;
2568
2581
  }
2569
2582
 
2570
2583
  var darkStroke = "#334",
@@ -2765,30 +2778,7 @@
2765
2778
 
2766
2779
 
2767
2780
  function getDefaultStyle(lyr, baseStyle) {
2768
- var style = Object.assign({}, baseStyle);
2769
- // reduce the dot size of large point layers
2770
- if (lyr.geometry_type == 'point' && style.dotSize > 0) {
2771
- style.dotSize *= getDotScale$1(lyr);
2772
- }
2773
- return style;
2774
- }
2775
-
2776
- function getDotScale$1(lyr) {
2777
- var topTier = 10000;
2778
- var n = countPoints(lyr.shapes, topTier); // short-circuit point counting above top threshold
2779
- var k = n < 200 && 4 || n < 2500 && 3 || n < topTier && 2 || 1;
2780
- return k;
2781
- }
2782
-
2783
- function countPoints(shapes, max) {
2784
- var count = 0;
2785
- var i, n, shp;
2786
- max = max || Infinity;
2787
- for (i=0, n=shapes.length; i<n && count<max; i++) {
2788
- shp = shapes[i];
2789
- count += shp ? shp.length : 0;
2790
- }
2791
- return count;
2781
+ return Object.assign({}, baseStyle);
2792
2782
  }
2793
2783
 
2794
2784
 
@@ -3232,6 +3222,58 @@
3232
3222
  return layers.reverse();
3233
3223
  }
3234
3224
 
3225
+
3226
+ function adjustPointSymbolSizes(layers, overlayLyr, ext) {
3227
+ var bbox = ext.getBounds().scale(1.5).toArray();
3228
+ var testInBounds = function(p) {
3229
+ return p[0] > bbox[0] && p[0] < bbox[2] && p[1] > bbox[1] && p[1] < bbox[3];
3230
+ };
3231
+ var topTier = 50000;
3232
+ var count = 0;
3233
+ layers = layers.filter(function(lyr) {
3234
+ return lyr.geometry_type == 'point' && lyr.gui.style.dotSize > 0;
3235
+ });
3236
+ layers.forEach(function(lyr) {
3237
+ // short-circuit point counting above top threshold
3238
+ count += countPoints(lyr.gui.displayLayer.shapes, topTier, testInBounds);
3239
+ });
3240
+ count = Math.min(topTier, count) || 1;
3241
+ var k = Math.pow(6 - utils$1.clamp(Math.log10(count), 1, 5), 1.3);
3242
+
3243
+ // zoom adjustments
3244
+ var mapScale = ext.scale();
3245
+ if (mapScale < 0.5) {
3246
+ k *= Math.pow(mapScale + 0.5, 0.35);
3247
+ } else if (mapScale > 1) {
3248
+ // scale faster at first
3249
+ k *= Math.pow(Math.min(mapScale, 4), 0.15);
3250
+ k *= Math.pow(mapScale, 0.05);
3251
+ }
3252
+
3253
+ // scale down when map is small
3254
+ var smallSide = Math.min(ext.width(), ext.height());
3255
+ k *= utils$1.clamp(smallSide / 500, 0.5, 1);
3256
+
3257
+ layers.forEach(function(lyr) {
3258
+ lyr.gui.style.dotScale = k;
3259
+ });
3260
+ if (overlayLyr && overlayLyr.geometry_type == 'point' && overlayLyr.gui.style.dotSize > 0) {
3261
+ overlayLyr.gui.style.dotScale = k;
3262
+ }
3263
+ }
3264
+
3265
+ function countPoints(shapes, max, filter) {
3266
+ var count = 0;
3267
+ var i, j, n, m, shp;
3268
+ for (i=0, n=shapes.length; i<n && count<max; i++) {
3269
+ shp = shapes[i];
3270
+ for (j=0, m=(shp ? shp.length : 0); j<m; j++) {
3271
+ count += filter(shp[j]) ? 1 : 0;
3272
+ }
3273
+ }
3274
+ return count;
3275
+ }
3276
+
3235
3277
  // lyr: a map layer with gui property
3236
3278
  // displayCRS: CRS to use for display, or null (which clears any current display CRS)
3237
3279
  function projectLayerForDisplay(lyr, displayCRS) {
@@ -3286,6 +3328,13 @@
3286
3328
  }
3287
3329
  }
3288
3330
 
3331
+ // make sure that every path layer has an associated arc collection
3332
+ // (if the layer is empty, its dataset may not have an arc collection).
3333
+ // this enables adding shapes using the drawing tools.
3334
+ if (!dataset.arcs && (layer.geometry_type == 'polygon' || layer.geometry_type == 'polyline')) {
3335
+ dataset.arcs = new internal.ArcCollection();
3336
+ }
3337
+
3289
3338
  // Assume that dataset.displayArcs is in the display CRS
3290
3339
  // (it must be deleted upstream if reprojection is needed)
3291
3340
  // if (!obj.empty && dataset.arcs && !displayArcs) {
@@ -3370,6 +3419,17 @@
3370
3419
  }
3371
3420
  }
3372
3421
 
3422
+ function pencilPointIsSkippable(p2, points) {
3423
+ var p1 = points[0];
3424
+ var dist;
3425
+ if (points.length < 2) return true;
3426
+ for (var i=1; i<points.length; i++) {
3427
+ dist = Math.sqrt(geom.pointSegDistSq(points[i][0], points[i][1], p1[0], p1[1], p2[0], p2[1]));
3428
+ if (dist > 1.5) return false;
3429
+ }
3430
+ return true;
3431
+ }
3432
+
3373
3433
  function setZ(lyr, z) {
3374
3434
  lyr.gui.source.dataset.arcs.setRetainedInterval(z);
3375
3435
  if (isProjectedLayer(lyr)) {
@@ -3451,6 +3511,25 @@
3451
3511
  }
3452
3512
  }
3453
3513
 
3514
+ function deletePoint(lyr, fid) {
3515
+ var records = lyr.data?.getRecords();
3516
+ lyr.shapes.splice(fid, 1);
3517
+ if (records) records.splice(fid, 1);
3518
+ if (isProjectedLayer(lyr)) {
3519
+ lyr.gui.displayLayer.shapes.splice(fid, 1);
3520
+ }
3521
+ }
3522
+
3523
+ function insertPoint(lyr, fid, shp, d) {
3524
+ var records = lyr.data?.getRecords();
3525
+ if (records) records.splice(fid, 0, d);
3526
+ lyr.shapes.splice(fid, 0, shp);
3527
+ if (isProjectedLayer(lyr)) {
3528
+ var shp2 = projectPointCoords(shp, lyr.gui.projectPoint);
3529
+ lyr.gui.displayLayer.shapes.splice(fid, 0, shp2);
3530
+ }
3531
+ }
3532
+
3454
3533
  function deleteLastPoint(lyr) {
3455
3534
  if (lyr.data) {
3456
3535
  lyr.data.getRecords().pop();
@@ -5522,6 +5601,16 @@
5522
5601
  addHistoryState(undo, redo);
5523
5602
  });
5524
5603
 
5604
+ gui.on('point_delete', function(e) {
5605
+ var redo = function() {
5606
+ deletePoint(e.data.target, e.fid);
5607
+ };
5608
+ var undo = function() {
5609
+ insertPoint(e.data.target, e.fid, e.coords, e.d);
5610
+ };
5611
+ addHistoryState(undo, redo);
5612
+ });
5613
+
5525
5614
  gui.on('path_add', function(e) {
5526
5615
  var redo = function() {
5527
5616
  gui.dispatchEvent('redo_path_add', {p1: e.p1, p2: e.p2});
@@ -5619,10 +5708,7 @@
5619
5708
  };
5620
5709
 
5621
5710
  function updateVisibility() {
5622
- var noData = !gui.model.getActiveLayer();
5623
- if (GUI.isActiveInstance(gui) && !_hidden && !noData) {
5624
- buttons.show();
5625
- } else if (noData && gui.getMode() != 'import') {
5711
+ if (GUI.isActiveInstance(gui) && !_hidden) {
5626
5712
  buttons.show();
5627
5713
  } else {
5628
5714
  buttons.hide();
@@ -5716,24 +5802,37 @@
5716
5802
  var self = this;
5717
5803
  var shiftDown = false;
5718
5804
  var ctrlDown = false;
5805
+ var metaDown = false;
5806
+ var altDown = false;
5807
+
5808
+ function updateControlKeys(e) {
5809
+ shiftDown = e.shiftKey;
5810
+ ctrlDown = e.ctrlKey;
5811
+ metaDown = e.metaKey;
5812
+ altDown = e.altKey;
5813
+ }
5719
5814
  document.addEventListener('keyup', function(e) {
5720
5815
  if (!GUI.isActiveInstance(gui)) return;
5721
5816
  // this can fail to fire if keyup occurs over a context menu
5722
- // if (e.keyCode == 16) shiftDown = false;
5723
- shiftDown = e.shiftKey;
5724
- if (e.keyCode == 17) ctrlDown = false;
5817
+ updateControlKeys(e);
5725
5818
  self.dispatchEvent('keyup', getEventData(e));
5726
5819
  });
5727
5820
 
5728
5821
  document.addEventListener('keydown', function(e) {
5729
5822
  if (!GUI.isActiveInstance(gui)) return;
5730
- shiftDown = e.shiftKey;
5731
- if (e.keyCode == 17) ctrlDown = true;
5823
+ updateControlKeys(e);
5732
5824
  self.dispatchEvent('keydown', getEventData(e));
5733
5825
  });
5734
5826
 
5827
+ document.addEventListener('mousemove', function(e) {
5828
+ // refreshing these here to prevent problems when context menu opens
5829
+ updateControlKeys(e);
5830
+ });
5831
+
5735
5832
  this.shiftIsPressed = function() { return shiftDown; };
5736
5833
  this.ctrlIsPressed = function() { return ctrlDown; };
5834
+ this.altIsPressed = function() { return altDown; };
5835
+ this.metaIsPressed = function() { return metaDown; };
5737
5836
 
5738
5837
  this.onMenuSubmit = function(menuEl, cb) {
5739
5838
  gui.on('enter_key', function(e) {
@@ -7964,7 +8063,9 @@
7964
8063
  return;
7965
8064
  }
7966
8065
  e.originalEvent.preventDefault();
7967
- if (!targetLayer) return; // TODO: enable menu on empty map
8066
+ if (!El('body').hasClass('map-view')) {
8067
+ return;
8068
+ }
7968
8069
  triggerHitEvent('contextmenu', e);
7969
8070
  }, false);
7970
8071
 
@@ -8060,7 +8161,7 @@
8060
8161
  self.setHitId = function(id) {
8061
8162
  if (storedData.id == id) return;
8062
8163
  storedData.id = id;
8063
- storedData.ids = [id];
8164
+ storedData.ids = id == -1 ? [] : [id];
8064
8165
  triggerHitEvent('change');
8065
8166
  };
8066
8167
 
@@ -8388,7 +8489,9 @@
8388
8489
  mode: interactionMode
8389
8490
  };
8390
8491
  if (evt) {
8492
+ // data coordinates
8391
8493
  eventData.coordinates = translateDisplayPoint(targetLayer, ext.translatePixelCoords(evt.x, evt.y));
8494
+ eventData.display_coordinates = gui.map.pixelCoordsToDisplayCoords(evt.x, evt.y);
8392
8495
  eventData.originalEvent = evt;
8393
8496
  eventData.overMap = isOverMap(evt);
8394
8497
  }
@@ -9269,8 +9372,7 @@
9269
9372
 
9270
9373
  function useBoxZoom() {
9271
9374
  var mode = gui.getMode();
9272
- var disabled = ['selection_tool', 'box_tool', 'rectangle_tool'].includes(mode);
9273
- return !disabled;
9375
+ return !'selection_tool,box_tool,rectangle_tool,drawing_tool'.includes(mode);
9274
9376
  }
9275
9377
 
9276
9378
  function getBoxData(e) {
@@ -9803,8 +9905,8 @@
9803
9905
 
9804
9906
  hit.on('contextmenu', function(e) {
9805
9907
  var target = hit.getHitTarget();
9806
- if (!e.overMap || !target || e.mode == 'edit_lines' ||
9807
- e.mode == 'edit_polygons') {
9908
+ if (!e.overMap || e.mode == 'edit_lines' ||
9909
+ e.mode == 'edit_polygons' || e.mode == 'edit_points') {
9808
9910
  return;
9809
9911
  }
9810
9912
  gui.contextMenu.open(e, hit.getHitTarget());
@@ -10099,7 +10201,8 @@
10099
10201
  }
10100
10202
 
10101
10203
  function initPointEditing(gui, ext, hit) {
10102
- var symbolInfo;
10204
+ var instructionsShown = false;
10205
+ var symbolInfo, alert;
10103
10206
  function active(e) {
10104
10207
  return gui.interaction.getMode() == 'edit_points';
10105
10208
  }
@@ -10108,17 +10211,57 @@
10108
10211
  return active(e) && e.id > -1;
10109
10212
  }
10110
10213
 
10214
+ function hideInstructions() {
10215
+ if (!alert) return;
10216
+ alert.close('fade');
10217
+ alert = null;
10218
+ }
10219
+
10220
+ function showInstructions() {
10221
+ var isMac = navigator.userAgent.includes('Mac');
10222
+ var symbol = isMac ? '⌘' : '^';
10223
+ var msg = `Instructions: Click on the map to add points. Move points by dragging. Type ${symbol}Z/${symbol}Y to undo/redo.`;
10224
+ alert = showPopupAlert(msg, null, { non_blocking: true, max_width: '360px'});
10225
+ }
10226
+
10111
10227
  gui.on('interaction_mode_change', function(e) {
10112
10228
  if (e.mode == 'edit_points' && !gui.model.getActiveLayer()) {
10113
10229
  addEmptyLayer(gui, undefined, 'point');
10114
10230
  } else if (e.prev_mode == 'edit_points') {
10231
+ hideInstructions();
10115
10232
  gui.container.findChild('.map-layers').classed('add-points', false);
10116
10233
  }
10234
+ if (e.mode == 'edit_points' && !instructionsShown) {
10235
+ instructionsShown = true;
10236
+ showInstructions();
10237
+ }
10238
+ });
10117
10239
 
10240
+ hit.on('contextmenu', function(e) {
10241
+ if (!active(e)) return;
10242
+ var target = hit.getHitTarget();
10243
+ var id = e.id;
10244
+ if (id > -1) {
10245
+ e.deletePoint = function() {
10246
+ removePoint(target, id);
10247
+ };
10248
+ }
10249
+ gui.contextMenu.open(e, target);
10118
10250
  });
10119
10251
 
10252
+ function removePoint(target, id) {
10253
+ var d = target.data ? target.data.getRecords()[id] : null;
10254
+ var coords = target.shapes[id];
10255
+ deletePoint(target, id);
10256
+ gui.dispatchEvent('point_delete', {coords, d, target, fid: id});
10257
+ gui.dispatchEvent('map-needs-refresh');
10258
+ hit.setHitId(-1);
10259
+ }
10260
+
10120
10261
  hit.on('click', function(e) {
10121
10262
  if (overPoint(e) || !active(e)) return;
10263
+ hideInstructions();
10264
+
10122
10265
  // add point
10123
10266
  var p = pixToDataCoords(e.x, e.y);
10124
10267
  var target = hit.getHitTarget();
@@ -10135,6 +10278,7 @@
10135
10278
 
10136
10279
  hit.on('dragstart', function(e) {
10137
10280
  if (!overPoint(e)) return;
10281
+ hideInstructions();
10138
10282
  var target = hit.getHitTarget();
10139
10283
  symbolInfo = {
10140
10284
  FID: e.id,
@@ -10188,6 +10332,7 @@
10188
10332
  var drawingId = -1; // feature id of path being drawn
10189
10333
  var sessionCount = 0;
10190
10334
  var alert;
10335
+ var pencilPoints;
10191
10336
  var _dragging = false;
10192
10337
 
10193
10338
  function active() {
@@ -10202,6 +10347,10 @@
10202
10347
  return drawingId > -1;
10203
10348
  }
10204
10349
 
10350
+ function usingPencil() {
10351
+ return (gui.keyboard.altIsPressed() || gui.keyboard.metaIsPressed()) && active() && !dragging();
10352
+ }
10353
+
10205
10354
  function polygonMode() {
10206
10355
  return active() && hit.getHitTarget().geometry_type == 'polygon';
10207
10356
  }
@@ -10283,11 +10432,12 @@
10283
10432
 
10284
10433
  function showInstructions() {
10285
10434
  var isMac = navigator.userAgent.includes('Mac');
10286
- var symbol = isMac ? '⌘' : '^';
10435
+ var undoKey = isMac ? '⌘' : '^';
10436
+ var drawKey = isMac ? '⌘' : 'Alt';
10287
10437
  var pathStr = polygonMode() ? 'closed paths' : 'paths';
10288
- var msg = `Instructions: Click on the map to draw ${pathStr}. Drag vertices to reshape a path. Type ${symbol}Z/${symbol}Y to undo/redo.`;
10438
+ var msg = `Instructions: Click to start a path or add a point. ${drawKey}-drag draws a path. Drag points to reshape. Type ${undoKey}Z/${undoKey}Y to undo/redo.`;
10289
10439
  alert = showPopupAlert(msg, null, {
10290
- non_blocking: true, max_width: '360px'});
10440
+ non_blocking: true, max_width: '388px'});
10291
10441
  }
10292
10442
 
10293
10443
  function hideInstructions() {
@@ -10372,9 +10522,28 @@
10372
10522
  hit.setHoverVertex(hoverVertexInfo.displayPoint, hoverVertexInfo.type);
10373
10523
  });
10374
10524
 
10525
+ gui.map.getMouse().on('drag', function(e) {
10526
+ if (!usingPencil()) return;
10527
+ e.stopPropagation();
10528
+ var xy = [e.x, e.y];
10529
+ var p = pixToDataCoords(e.x, e.y);
10530
+ if (!drawing()) {
10531
+ pencilPoints = [xy];
10532
+ startNewPath(p);
10533
+ } else if (!pencilPoints || !pencilPointIsSkippable(xy, pencilPoints)) {
10534
+ pencilPoints = [xy];
10535
+ extendCurrentPath(p);
10536
+ } else {
10537
+ // skip this point, update the hover line
10538
+ pencilPoints.push(xy);
10539
+ updatePathEndpoint(p);
10540
+ }
10541
+ }, null, 3); // higher priority than hit control
10542
+
10375
10543
  hit.on('drag', function(e) {
10376
10544
  if (!dragging() || drawing()) return;
10377
10545
  e.originalEvent.stopPropagation();
10546
+ // dragging a vertex
10378
10547
  var target = hit.getHitTarget();
10379
10548
  var p = ext.translatePixelCoords(e.x, e.y);
10380
10549
  if (gui.keyboard.shiftIsPressed()) {
@@ -10453,8 +10622,6 @@
10453
10622
  deleteActiveVertex(e);
10454
10623
  } else {
10455
10624
  startNewPath(p);
10456
- hideInstructions();
10457
- updateCursor();
10458
10625
  }
10459
10626
  prevClickEvent = e;
10460
10627
  });
@@ -10565,6 +10732,14 @@
10565
10732
  gui.dispatchEvent('path_add', {target, p1, p2});
10566
10733
  drawingId = target.shapes.length - 1;
10567
10734
  hit.setDrawingId(drawingId);
10735
+ hideInstructions();
10736
+ updateCursor();
10737
+ }
10738
+
10739
+ function pencilDraw(e) {
10740
+ var p = pixToDataCoords(e.x, e.y);
10741
+ extendCurrentPath(p);
10742
+
10568
10743
  }
10569
10744
 
10570
10745
  // p: [x, y] source data coordinates of new point on path
@@ -10585,7 +10760,6 @@
10585
10760
  gui.dispatchEvent('path_extend', {target, p, shapes1, shapes2});
10586
10761
  clearDrawingInfo();
10587
10762
  fullRedraw();
10588
-
10589
10763
  } else {
10590
10764
  appendVertex$1(target, p);
10591
10765
  gui.dispatchEvent('path_extend', {target, p});
@@ -10622,20 +10796,19 @@
10622
10796
  function findPathStartInfo(e) {
10623
10797
  var target = hit.getHitTarget();
10624
10798
  var arcId = target.gui.displayArcs.size() - 1;
10625
- var data = target.gui.displayArcs.getVertexData();
10799
+ var p1 = ext.translatePixelCoords(e.x, e.y); // mouse coords
10800
+ var p2 = internal.getArcStartCoords(arcId, target.gui.displayArcs); // vertex coords
10801
+ var p3 = internal.getArcStartCoords(arcId, target.gui.source.dataset.arcs);
10802
+ var dist = geom.distance2D(p1[0], p1[1], p2[0], p2[1]);
10803
+ var data = target.gui.source.dataset.arcs.getVertexData();
10626
10804
  var i = data.ii[arcId];
10627
- var x = data.xx[i];
10628
- var y = data.yy[i];
10629
- var p = ext.translatePixelCoords(e.x, e.y);
10630
- var dist = geom.distance2D(p[0], p[1], x, y);
10631
10805
  var pathLen = data.nn[arcId];
10632
10806
  var pixelDist = dist / ext.getPixelSize();
10633
10807
  if (pixelDist > HOVER_THRESHOLD || pathLen < 4) {
10634
10808
  return null;
10635
10809
  }
10636
- var point = translateDisplayPoint(target, [x, y]);
10637
10810
  return {
10638
- target, ids: [i], extendable: false, point, displayPoint: [x, y], type: 'vertex'
10811
+ target, ids: [i], extendable: false, displayPoint: p2, point: p3, type: 'vertex'
10639
10812
  };
10640
10813
  }
10641
10814
 
@@ -10871,6 +11044,7 @@
10871
11044
  }
10872
11045
  };
10873
11046
 
11047
+ // translate display CRS coords to pixel coords
10874
11048
  this.translateCoords = function(x, y) {
10875
11049
  return this.getTransform().transform(x, y);
10876
11050
  };
@@ -10890,6 +11064,7 @@
10890
11064
  return bounds2.width() / _frame.width;
10891
11065
  };
10892
11066
 
11067
+ // convert pixel coords (0,0 is top left corner of map) to display CRS coords
10893
11068
  this.translatePixelCoords = function(x, y) {
10894
11069
  return this.getTransform().invert().transform(x, y);
10895
11070
  };
@@ -11352,8 +11527,7 @@
11352
11527
 
11353
11528
  _self.drawSquareDots = function(shapes, style) {
11354
11529
  var t = getScaledTransform(_ext),
11355
- scaleRatio = getDotScale(_ext),
11356
- size = Math.round((style.dotSize || 1) * scaleRatio),
11530
+ size = getDotSize(style),
11357
11531
  styler = style.styler || null,
11358
11532
  xmax = _canvas.width + size,
11359
11533
  ymax = _canvas.height + size,
@@ -11373,7 +11547,7 @@
11373
11547
  for (i=0, n=shapes.length; i<n; i++) {
11374
11548
  if (styler !== null) { // e.g. selected points
11375
11549
  styler(style, i);
11376
- size = style.dotSize * scaleRatio;
11550
+ size = getDotSize(style);
11377
11551
  if (style.dotColor != color) {
11378
11552
  color = style.dotColor;
11379
11553
  _ctx.fillStyle = color;
@@ -11520,25 +11694,16 @@
11520
11694
  return s;
11521
11695
  }
11522
11696
 
11523
- function getDotScale(ext) {
11524
- var smallSide = Math.min(ext.width(), ext.height());
11525
- var mapScale = ext.scale();
11526
- // reduce size on smaller screens
11527
- var j = smallSide < 200 && 0.5 || smallSide < 400 && 0.75 || 1;
11528
- // grow dots as map zooms in
11529
- var k = 1;
11530
- if (mapScale < 0.5) {
11531
- k = Math.pow(mapScale + 0.5, 0.35);
11532
- } else if (mapScale > 1) {
11533
- // scale faster at first, so small dots in large datasets
11534
- // become easily visible and clickable after zooming in a bit
11535
- k *= Math.pow(Math.min(mapScale, 10), 0.3);
11536
- k *= Math.pow(mapScale, 0.1);
11537
- }
11538
- // grow pixels more slowly on retina displays (to reduce number of pixels to
11539
- // draw for large point datasets when slightly zoomed in)
11540
- var l = Math.pow(GUI.getPixelRatio(), 0.8);
11541
- return j * k * l;
11697
+ function getDotSize(style) {
11698
+ var size = style.dotSize || 1;
11699
+ // TODO: improve
11700
+ var scale = style.dotScale || 1;
11701
+ size += (scale - 1) / 2;
11702
+ size *= Math.pow(scale, 0.3);
11703
+
11704
+ // shrink dots slightly on retina displays, to adjust for greater clarity
11705
+ // and reduce number of pixels to draw on large datasets.
11706
+ return Math.round(Math.pow(GUI.getPixelRatio(), 0.8) * size);
11542
11707
  }
11543
11708
 
11544
11709
  function getScaledTransform(ext) {
@@ -12460,16 +12625,27 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12460
12625
  return internal.toLngLat(p, _dynamicCRS);
12461
12626
  };
12462
12627
 
12463
- this.getCenterLngLat = function() {
12464
- var bounds = _ext.getBounds();
12628
+ this.pixelCoordsToDisplayCoords = function(x, y) {
12629
+ var p1 = _ext.translatePixelCoords(x, y);
12630
+ var p2 = _ext.translatePixelCoords(x+1, y+1);
12465
12631
  var crs = this.getDisplayCRS();
12466
- // TODO: handle case where active layer is a frame layer
12467
- if (!bounds.hasBounds() || !crs) {
12468
- return null;
12632
+ if (crs) {
12633
+ p1 = internal.toLngLat(p1, crs) || p1;
12634
+ p2 = internal.toLngLat(p2, crs) || p2;
12469
12635
  }
12470
- return internal.toLngLat([bounds.centerX(), bounds.centerY()], crs);
12636
+ return formatCoordsForDisplay(p1, p2);
12471
12637
  };
12472
12638
 
12639
+ // this.getCenterLngLat = function() {
12640
+ // var bounds = _ext.getBounds();
12641
+ // var crs = this.getDisplayCRS();
12642
+ // // TODO: handle case where active layer is a frame layer
12643
+ // if (!bounds.hasBounds() || !crs) {
12644
+ // return null;
12645
+ // }
12646
+ // return internal.toLngLat([bounds.centerX(), bounds.centerY()], crs);
12647
+ // };
12648
+
12473
12649
  this.getDisplayCRS = function() {
12474
12650
  if (!_activeLyr) {
12475
12651
  return _dynamicCRS || internal.parseCrsString('wgs84');
@@ -12536,7 +12712,6 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12536
12712
  _ext.setFullBounds(calcFullBounds());
12537
12713
  _ext.resize();
12538
12714
  _renderer = new LayerRenderer(gui, el);
12539
- gui.buttons.show();
12540
12715
 
12541
12716
  if (opts.inspectorControl) {
12542
12717
  _hit = new HitControl(gui, _ext, _mouse),
@@ -12598,10 +12773,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12598
12773
  }
12599
12774
 
12600
12775
  if (e.layer) {
12601
- enhanceLayerForDisplay(e.layer, e.dataset, getDisplayOptions());
12602
- _activeLyr = e.layer;
12603
- _activeLyr.gui.style = getActiveLayerStyle(_activeLyr.gui.displayLayer, getGlobalStyleOptions());
12604
- _activeLyr.active = true;
12776
+ _activeLyr = initActiveLayer(e.layer, e.dataset);
12605
12777
  } else {
12606
12778
  _activeLyr = null;
12607
12779
  }
@@ -12639,10 +12811,12 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12639
12811
 
12640
12812
 
12641
12813
  function updateOverlayLayer(e) {
12642
- var style = getOverlayStyle(_activeLyr.gui.displayLayer, e, getGlobalStyleOptions());
12814
+ var style = !_activeLyr?.gui?.style ? null :
12815
+ getOverlayStyle(_activeLyr.gui.displayLayer, e, getGlobalStyleOptions());
12643
12816
  if (style) {
12644
12817
  var displayLayer = filterLayerByIds(_activeLyr.gui.displayLayer, style.ids);
12645
12818
  var gui = Object.assign({}, _activeLyr.gui, {style, displayLayer});
12819
+ style.dotScale = _activeLyr.gui.style.dotScale;
12646
12820
  _overlayLyr = utils$1.defaults({gui}, _activeLyr);
12647
12821
  } else {
12648
12822
  _overlayLyr = null;
@@ -12786,6 +12960,17 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12786
12960
  });
12787
12961
  }
12788
12962
 
12963
+ function initActiveLayer(lyr, dataset) {
12964
+ enhanceLayerForDisplay(lyr, dataset, getDisplayOptions());
12965
+ lyr.gui.style = getActiveLayerStyle(lyr.gui.displayLayer, getGlobalStyleOptions());
12966
+ lyr.active = true;
12967
+ getVisibleMapLayers().forEach(function(lyr) {
12968
+ delete lyr.active;
12969
+ });
12970
+ lyr.active = true;
12971
+ return lyr;
12972
+ }
12973
+
12789
12974
  function getDrawableFurnitureLayers(layers) {
12790
12975
  if (!isPreviewView()) return [];
12791
12976
  return getVisibleMapLayers().filter(function(o) {
@@ -12852,6 +13037,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12852
13037
  updateLayerStyles(contentLayers);
12853
13038
  updateLayerStackOrder(model.getLayers());// update menu_order property of all layers
12854
13039
  }
13040
+ adjustPointSymbolSizes(contentLayers, _overlayLyr, _ext);
12855
13041
  sortMapLayers(contentLayers);
12856
13042
  if (_intersectionLyr) {
12857
13043
  contentLayers = contentLayers.concat(_intersectionLyr);
@@ -12920,7 +13106,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12920
13106
  menu.hide();
12921
13107
  _open = false;
12922
13108
  }
12923
- }, 300);
13109
+ }, 200);
12924
13110
  }
12925
13111
 
12926
13112
  function addMenuItem(label, func) {
@@ -12933,7 +13119,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12933
13119
  }
12934
13120
 
12935
13121
  this.open = function(e, lyr) {
12936
- if (!lyr || !lyr.gui.geographic) return;
13122
+ if (lyr && !lyr.gui.geographic) return; // no popup for tabular data
12937
13123
  _open = true;
12938
13124
  _openCount++;
12939
13125
  var rspace = body.clientWidth - e.pageX;
@@ -12949,19 +13135,22 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12949
13135
  menu.css('top', (e.pageY - 15) + 'px');
12950
13136
 
12951
13137
  // menu contents
12952
- if (e.coordinates) {
12953
- addCopyCoords();
13138
+ //
13139
+ if (e.display_coordinates) {
13140
+ addCoords(e.display_coordinates);
12954
13141
  }
12955
13142
  if (e.deleteVertex) {
12956
13143
  addMenuItem('Delete vertex', e.deleteVertex);
12957
13144
  }
13145
+ if (e.deletePoint) {
13146
+ addMenuItem('Delete point', e.deletePoint);
13147
+ }
12958
13148
  if (e.ids?.length) {
12959
13149
  addMenuItem('Copy as GeoJSON', copyGeoJSON);
12960
13150
  }
12961
13151
 
12962
- function addCopyCoords() {
12963
- var bbox = internal.getLayerBounds(lyr, lyr.gui.source.dataset.arcs).toArray();
12964
- var coordStr = internal.getRoundedCoordString(e.coordinates, internal.getBoundsPrecisionForDisplay(bbox));
13152
+ function addCoords(p) {
13153
+ var coordStr = p[0] + ',' + p[1];
12965
13154
  var displayStr = '• &nbsp;' + coordStr.replace(/-/g, '–').replace(',', ', ');
12966
13155
  addMenuItem(displayStr, function() {
12967
13156
  saveFileContentToClipboard(coordStr);
package/www/mapshaper.js CHANGED
@@ -1612,6 +1612,7 @@
1612
1612
  this.ymin -= halfHeight * ky;
1613
1613
  this.xmax += halfWidth * kx;
1614
1614
  this.ymax += halfHeight * ky;
1615
+ return this;
1615
1616
  };
1616
1617
 
1617
1618
  // Return a bounding box with the same extent as this one.
@@ -5003,7 +5004,7 @@
5003
5004
  if (isLatLngCRS(P)) {
5004
5005
  return xy.concat();
5005
5006
  }
5006
- proj = getProjTransform(P, parseCrsString('wgs84'));
5007
+ proj = getProjTransform2(P, parseCrsString('wgs84'));
5007
5008
  return proj(xy[0], xy[1]);
5008
5009
  }
5009
5010
 
@@ -26664,7 +26665,7 @@ ${svg}
26664
26665
  codepage = lookupCodePage(ldid),
26665
26666
  samples = getNonAsciiSamples(),
26666
26667
  only7bit = samples.length === 0,
26667
- encoding, msg;
26668
+ encoding;
26668
26669
 
26669
26670
  // First, check the ldid (language driver id) (an obsolete way to specify which
26670
26671
  // codepage to use for text encoding.)
@@ -26684,10 +26685,12 @@ ${svg}
26684
26685
  // As a last resort, try to guess the encoding:
26685
26686
  if (!encoding) {
26686
26687
  var info = detectEncoding(samples);
26688
+ var msg;
26687
26689
  encoding = info.encoding;
26688
26690
  if (info.confidence < 2) {
26689
- msg = 'Warning: Unable to auto-detect the DBF file text encoding with high confidence.';
26690
- msg += '\n\nDefaulting to: ' + encoding + (encoding in encodingNames ? ' (' + encodingNames[encoding] + ')' : '');
26691
+ msg = 'Warning: Unable to auto-detect the DBF file text encoding' +
26692
+ (info.confidence == 1 ? ' with high confidence' : '') + '.';
26693
+ msg += ' Defaulting to ' + encoding + (encoding in encodingNames ? ' (' + encodingNames[encoding] + ').' : '.');
26691
26694
  msg += '\n\nSample of how non-ascii text was imported:\n';
26692
26695
  if (runningInBrowser()) {
26693
26696
  msg += '<pre>' + formatStringsAsGrid(decodeSamples(encoding, samples), 50) + '</pre>';
@@ -45532,7 +45535,7 @@ ${svg}
45532
45535
  });
45533
45536
  }
45534
45537
 
45535
- var version = "0.6.78";
45538
+ var version = "0.6.80";
45536
45539
 
45537
45540
  // Parse command line args into commands and run them
45538
45541
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
package/www/page.css CHANGED
@@ -59,7 +59,7 @@ body.map-view {
59
59
 
60
60
 
61
61
  .selectable,.selectable * {
62
- user-select: default;
62
+ user-select: text;
63
63
  cursor: text;
64
64
  }
65
65
 
@@ -343,17 +343,15 @@ div.alert-box {
343
343
  }
344
344
 
345
345
  .mini-drop-area {
346
- border: 1.5px dashed #bbb;
347
- max-width: 240px;
346
+ border: 1.5px dashed #999;
347
+ max-width: 245px;
348
348
  border-radius: 9px;
349
- font-size: 15px;
349
+ font-size: 14px;
350
350
  line-height: 1.5;
351
- color: #888;
352
- /* background-color: #FFFEF8;*/
353
- padding: 6px 4px 7px 7px;
354
- padding: 11px 14px;
351
+ padding: 9px 11px 6px 11px;
355
352
  margin: 9px -1px 3px -1px;
356
353
  min-height: 120px;
354
+ background: #f8fdff;
357
355
  }
358
356
 
359
357
  .catalog-mode .file-catalog {