mapshaper 0.6.82 → 0.6.83

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
@@ -38860,14 +38860,15 @@ ${svg}
38860
38860
  return mergeDatasetsIntoDataset(target.dataset, datasets);
38861
38861
  };
38862
38862
 
38863
- cmd.rectangle = function(source, opts) {
38863
+ cmd.rectangle = function(target, opts) {
38864
38864
  var bounds, crsInfo;
38865
- if (source) {
38866
- bounds = getLayerBounds(source.layer, source.dataset.arcs);
38867
- crsInfo = getDatasetCrsInfo(source.dataset);
38868
- } else if (opts.bbox) {
38865
+ if (opts.bbox) {
38869
38866
  bounds = new Bounds(opts.bbox);
38870
- crsInfo = probablyDecimalDegreeBounds(bounds) ? getCrsInfo('wgs84') : {};
38867
+ crsInfo = target && getDatasetCrsInfo(target.dataset) ||
38868
+ probablyDecimalDegreeBounds(bounds) && getCrsInfo('wgs84') || {};
38869
+ } else if (target) {
38870
+ bounds = getLayerBounds(target.layer, target.dataset.arcs);
38871
+ crsInfo = getDatasetCrsInfo(target.dataset);
38871
38872
  }
38872
38873
  bounds = bounds && applyRectangleOptions(bounds, crsInfo.crs, opts);
38873
38874
  if (!bounds || !bounds.hasBounds()) {
@@ -45371,7 +45372,7 @@ ${svg}
45371
45372
  stop('Command requires a width= argument');
45372
45373
  }
45373
45374
  if (source || opts.bbox || targets.length === 0) {
45374
- job.catalog.addDataset(cmd.rectangle(source, opts));
45375
+ job.catalog.addDataset(cmd.rectangle(source || targets?.[0], opts));
45375
45376
  } else {
45376
45377
  outputLayers = cmd.rectangle2(targets[0], opts);
45377
45378
  }
@@ -45535,7 +45536,7 @@ ${svg}
45535
45536
  });
45536
45537
  }
45537
45538
 
45538
- var version = "0.6.82";
45539
+ var version = "0.6.83";
45539
45540
 
45540
45541
  // Parse command line args into commands and run them
45541
45542
  // 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.82",
3
+ "version": "0.6.83",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
package/www/index.html CHANGED
@@ -183,9 +183,9 @@ precision=0.001. Click to see all options.</div></div></div></a>
183
183
  <div class="info-box">
184
184
  <div class="close2-btn"></div>
185
185
  <h3>Basemap options</h3>
186
- <p class="basemap-note">Your data will be displayed using the Mercator projection.</p>
186
+ <p class="basemap-note"></p>
187
187
  <p class="basemap-warning"></p>
188
- <div class="basemap-styles"></div>
188
+ <div class="basemap-styles basemap-buttons"></div>
189
189
  <div>
190
190
  <!-- <div class="close-btn btn dialog-btn">Close</div> -->
191
191
  <div class="clear-btn btn dialog-btn disabled">Clear</div>
@@ -298,8 +298,8 @@ encoding=big5</span>. Click to see all options.</div></div></div></div>
298
298
  <span class="intersection-check text-btn colored-text">Check line intersections</span><span class="intersection-count">0 line intersections</span>
299
299
  <img class="close-btn" draggable="false" src="images/close.png">
300
300
  <div class="repair-btn text-btn colored-text">Repair</div>
301
-
302
301
  </div>
302
+ <div class="basemap-overlay-buttons basemap-buttons"></div>
303
303
  <div class="map-layers"></div>
304
304
  <div class="basemap-container"><div class="basemap"></div></div>
305
305
  </div>
@@ -2564,18 +2564,6 @@
2564
2564
  return isProjectedLayer(lyr) ? lyr.gui.invertPoint(p[0], p[1]) : p;
2565
2565
  }
2566
2566
 
2567
- // bbox: display coords
2568
- // intended to work with rectangular projections like Mercator
2569
- function getBBoxCoords(lyr, bbox) {
2570
- if (!isProjectedLayer(lyr)) return bbox.concat();
2571
- var a = translateDisplayPoint(lyr, [bbox[0], bbox[1]]);
2572
- var b = translateDisplayPoint(lyr, [bbox[2], bbox[3]]);
2573
- var bounds = new internal.Bounds();
2574
- bounds.mergePoint(a[0], a[1]);
2575
- bounds.mergePoint(b[0], b[1]);
2576
- return bounds.toArray();
2577
- }
2578
-
2579
2567
  function isProjectedLayer(lyr) {
2580
2568
  return !!lyr?.gui.invertPoint;
2581
2569
  }
@@ -3425,7 +3413,7 @@
3425
3413
  //
3426
3414
  function pointExceedsTolerance(p, points, tolerance) {
3427
3415
  if (points.length < 2) return false;
3428
- var p1 = points[0], p2, dist;
3416
+ var p1 = points[0], p2, dist, angle;
3429
3417
  for (var i=1; i<points.length; i++) {
3430
3418
  p2 = points[i];
3431
3419
  dist = Math.sqrt(geom.pointSegDistSq(p2[0], p2[1], p1[0], p1[1], p[0], p[1]));
@@ -3434,6 +3422,15 @@
3434
3422
  return false;
3435
3423
  }
3436
3424
 
3425
+ function getAvgPoint(points) {
3426
+ var x=0, y=0;
3427
+ for (var i=0; i<points.length; i++) {
3428
+ x += points[i][0];
3429
+ y += points[i][1];
3430
+ }
3431
+ return [x/points.length, y/points.length];
3432
+ }
3433
+
3437
3434
  function setZ(lyr, z) {
3438
3435
  lyr.gui.source.dataset.arcs.setRetainedInterval(z);
3439
3436
  if (isProjectedLayer(lyr)) {
@@ -8065,7 +8062,6 @@
8065
8062
  var transientIds = []; // e.g. hit ids while dragging a box
8066
8063
  var drawingId = -1; // kludge to allow hit detection and drawing (different feature ids)
8067
8064
  var active = false;
8068
- var interactionMode;
8069
8065
  var targetLayer;
8070
8066
  var hitTest;
8071
8067
  var pinnedOn; // used in multi-edit mode (selection) for toggling pinning behavior
@@ -8075,16 +8071,11 @@
8075
8071
  var priority = 2;
8076
8072
 
8077
8073
  mouse.on('contextmenu', function(e) {
8078
- // shift key enables default menu (for development)
8079
- if (gui.keyboard.shiftIsPressed()) {
8080
- return;
8081
- }
8082
8074
  e.originalEvent.preventDefault();
8083
- if (!El('body').hasClass('map-view')) {
8084
- return;
8075
+ if (El('body').hasClass('map-view')) {
8076
+ triggerHitEvent('contextmenu', e);
8085
8077
  }
8086
- triggerHitEvent('contextmenu', e);
8087
- }, false);
8078
+ });
8088
8079
 
8089
8080
  // init keyboard controls for pinned features
8090
8081
  gui.keyboard.on('keydown', function(evt) {
@@ -8126,11 +8117,14 @@
8126
8117
  };
8127
8118
 
8128
8119
  function updateHitTest(featureFilter) {
8129
- hitTest = getPointerHitTest(targetLayer, ext, interactionMode, featureFilter);
8120
+ hitTest = getPointerHitTest(targetLayer, ext, interactionMode(), featureFilter);
8121
+ }
8122
+
8123
+ function interactionMode() {
8124
+ return gui.interaction.getMode();
8130
8125
  }
8131
8126
 
8132
8127
  function turnOn(mode) {
8133
- interactionMode = mode;
8134
8128
  active = true;
8135
8129
  updateHitTest();
8136
8130
  }
@@ -8145,12 +8139,8 @@
8145
8139
  }
8146
8140
  }
8147
8141
 
8148
- function hoverable() {
8149
- return !!interactionMode;
8150
- }
8151
-
8152
8142
  function selectable() {
8153
- return interactionMode == 'selection';
8143
+ return interactionMode() == 'selection';
8154
8144
  }
8155
8145
 
8156
8146
  function pinnable() {
@@ -8159,16 +8149,16 @@
8159
8149
  }
8160
8150
 
8161
8151
  function draggable() {
8162
- return interactionMode == 'vertices' || interactionMode == 'edit_points' ||
8163
- interactionMode == 'labels' || interactionMode == 'edit_lines' ||
8164
- interactionMode == 'edit_polygons';
8152
+ var mode = interactionMode();
8153
+ return mode == 'vertices' || mode == 'edit_points' ||
8154
+ mode == 'labels' || mode == 'edit_lines' || mode == 'edit_polygons';
8165
8155
  }
8166
8156
 
8167
8157
  function clickable() {
8158
+ var mode = interactionMode();
8168
8159
  // click used to pin popup and select features
8169
- return interactionMode == 'data' || interactionMode == 'info' ||
8170
- interactionMode == 'selection' || interactionMode == 'rectangles' ||
8171
- interactionMode == 'edit_points';
8160
+ return mode == 'data' || mode == 'info' || mode == 'selection' ||
8161
+ mode == 'rectangles' || mode == 'edit_points';
8172
8162
  }
8173
8163
 
8174
8164
  self.getHitId = function() {
@@ -8293,7 +8283,6 @@
8293
8283
  // (some modes do not support pinning)
8294
8284
  gui.on('interaction_mode_change', function(e) {
8295
8285
  self.clearSelection();
8296
- // if (e.mode == 'off' || e.mode == 'box') {
8297
8286
  if (gui.interaction.modeUsesHitDetection(e.mode)) {
8298
8287
  turnOn(e.mode);
8299
8288
  } else {
@@ -8326,7 +8315,7 @@
8326
8315
  updateSelectionState(convertClickDataToSelectionData(hitTest(e)));
8327
8316
  }
8328
8317
 
8329
- if (pinned && interactionMode == 'edit_points') {
8318
+ if (pinned && interactionMode() == 'edit_points') {
8330
8319
  // kludge: intercept the click event if popup is turning off, so
8331
8320
  // a new point doesn't get made
8332
8321
  return;
@@ -8451,6 +8440,7 @@
8451
8440
 
8452
8441
  // check if an event is used in the current interaction mode
8453
8442
  function eventIsEnabled(type) {
8443
+ var mode = interactionMode();
8454
8444
  if (!active) return false;
8455
8445
  if (type == 'click' && gui.keyboard.ctrlIsPressed()) {
8456
8446
  return false; // don't fire if context menu might open
@@ -8459,13 +8449,13 @@
8459
8449
  return false;
8460
8450
  }
8461
8451
  if (type == 'click' &&
8462
- (interactionMode == 'edit_lines' || interactionMode == 'edit_polygons')) {
8452
+ (mode == 'edit_lines' || mode == 'edit_polygons')) {
8463
8453
  return true; // click events are triggered even if no shape is hit
8464
8454
  }
8465
- if (type == 'click' && interactionMode == 'edit_points') {
8455
+ if (type == 'click' && mode == 'edit_points') {
8466
8456
  return true;
8467
8457
  }
8468
- if ((interactionMode == 'edit_lines' || interactionMode == 'edit_polygons') &&
8458
+ if ((mode == 'edit_lines' || mode == 'edit_polygons') &&
8469
8459
  (type == 'hover' || type == 'dblclick')) {
8470
8460
  return true; // special case -- using hover for line drawing animation
8471
8461
  }
@@ -8486,7 +8476,7 @@
8486
8476
  }
8487
8477
 
8488
8478
  function possiblyStopPropagation(e) {
8489
- if (interactionMode == 'edit_lines' || interactionMode == 'edit_polygons') {
8479
+ if (interactionMode() == 'edit_lines' || interactionMode() == 'edit_polygons') {
8490
8480
  // handled conditionally in the control
8491
8481
  return;
8492
8482
  }
@@ -8503,7 +8493,7 @@
8503
8493
  // evt: event data (may be a pointer event object, an ordinary object or null)
8504
8494
  function triggerHitEvent(type, evt) {
8505
8495
  var eventData = {
8506
- mode: interactionMode
8496
+ mode: interactionMode()
8507
8497
  };
8508
8498
  if (evt) {
8509
8499
  // data coordinates
@@ -8822,6 +8812,11 @@
8822
8812
  element.addEventListener('mousedown', onAreaDown);
8823
8813
  element.addEventListener('dblclick', onAreaDblClick);
8824
8814
  document.addEventListener('contextmenu', function(e) {
8815
+ if (!e.ctrlKey) {
8816
+ e.preventDefault();
8817
+ }
8818
+ });
8819
+ element.addEventListener('contextmenu', function(e) {
8825
8820
  _self.dispatchEvent('contextmenu', procMouseEvent(e));
8826
8821
  });
8827
8822
 
@@ -9099,9 +9094,9 @@
9099
9094
 
9100
9095
  box.getDataCoords = function() {
9101
9096
  if (!boxCoords) return null;
9102
- var dataBox = getBBoxCoords(gui.map.getActiveLayer(), boxCoords);
9097
+ var lyr = gui.map.getActiveLayer();
9098
+ var dataBox = lyr ? translateCoordsToLayerCRS(boxCoords, lyr) : translateCoordsToLatLon(boxCoords);
9103
9099
  fixBounds(dataBox);
9104
- // return internal.getRoundedCoords(dataBox, internal.getBoundsPrecisionForDisplay(dataBox));
9105
9100
  return dataBox;
9106
9101
  };
9107
9102
 
@@ -9136,6 +9131,26 @@
9136
9131
  }
9137
9132
  };
9138
9133
 
9134
+ function translateCoordsToLatLon(bbox) {
9135
+ var crs = gui.map.getDisplayCRS();
9136
+ var a = internal.toLngLat([bbox[0], bbox[1]], crs);
9137
+ var b = internal.toLngLat([bbox[2], bbox[3]], crs);
9138
+ return a.concat(b);
9139
+ }
9140
+
9141
+ // bbox: display coords
9142
+ // intended to work with rectangular projections like Mercator
9143
+ function translateCoordsToLayerCRS(bbox, lyr) {
9144
+ if (!isProjectedLayer(lyr)) return bbox.concat();
9145
+ var a = translateDisplayPoint(lyr, [bbox[0], bbox[1]]);
9146
+ var b = translateDisplayPoint(lyr, [bbox[2], bbox[3]]);
9147
+ var bounds = new internal.Bounds();
9148
+ bounds.mergePoint(a[0], a[1]);
9149
+ bounds.mergePoint(b[0], b[1]);
9150
+ return bounds.toArray();
9151
+ }
9152
+
9153
+ // get bbox coords in the display CRS
9139
9154
  function getBoxCoords(e) {
9140
9155
  var bbox = pixToCoords(e.a.concat(e.b), gui.map.getExtent());
9141
9156
  fixBounds(bbox);
@@ -9236,6 +9251,7 @@
9236
9251
  zoomBox = new HighlightBox(gui, {draggable: true, name: 'zoom-box'}), // .addClass('zooming'),
9237
9252
  shiftDrag = false,
9238
9253
  zoomScaleMultiplier = 1,
9254
+ panCount = 0,
9239
9255
  inBtn, outBtn,
9240
9256
  dragStartEvt,
9241
9257
  _fx, _fy; // zoom foci, [0,1]
@@ -9287,18 +9303,11 @@
9287
9303
  // var lyr = gui.model.getActiveLayer()?.layer;
9288
9304
  // if (lyr && !internal.layerHasGeometry(lyr)) return;
9289
9305
  shiftDrag = !!e.shiftKey;
9306
+ panCount = 0;
9290
9307
  if (shiftDrag) {
9291
9308
  if (useBoxZoom()) zoomBox.turnOn();
9292
9309
  dragStartEvt = e;
9293
9310
  gui.dispatchEvent('shift_drag_start');
9294
- } else {
9295
- El('body').addClass('pan');
9296
- setTimeout(function() {
9297
- var body = El('body');
9298
- if (body.hasClass('pan')) {
9299
- body.addClass('panning');
9300
- }
9301
- }, 100);
9302
9311
  }
9303
9312
  });
9304
9313
 
@@ -9306,9 +9315,18 @@
9306
9315
  if (disabled()) return;
9307
9316
  if (shiftDrag) {
9308
9317
  gui.dispatchEvent('shift_drag', getBoxData(e));
9309
- } else {
9310
- ext.pan(e.dx, e.dy);
9318
+ return;
9319
+ }
9320
+ if (++panCount == 1) {
9321
+ El('body').addClass('pan');
9322
+ setTimeout(function() {
9323
+ var body = El('body');
9324
+ if (body.hasClass('pan')) {
9325
+ body.addClass('panning');
9326
+ }
9327
+ }, 100);
9311
9328
  }
9329
+ ext.pan(e.dx, e.dy);
9312
9330
  });
9313
9331
 
9314
9332
  mouse.on('dragend', function(e) {
@@ -10323,7 +10341,7 @@
10323
10341
  }
10324
10342
 
10325
10343
  function pencilIsActive() {
10326
- return active() && (cmdKeyDown() || pathDrawing()) && !vertexDragging();
10344
+ return active() && (cmdKeyDown() || pathDrawing()) && !vertexDragging() && !!pencilPoints;
10327
10345
  }
10328
10346
 
10329
10347
  function polygonMode() {
@@ -10521,7 +10539,7 @@
10521
10539
  }
10522
10540
  e.stopPropagation(); // prevent panning
10523
10541
  hoverVertexInfo = findPathStartInfo(e);
10524
- var xy = [e.x, e.y];
10542
+ var xy = [e.x, e.y], xy2;
10525
10543
  var p = pixToDataCoords(e.x, e.y);
10526
10544
  if (!pathDrawing()) {
10527
10545
  pencilPoints = [xy];
@@ -10530,17 +10548,20 @@
10530
10548
  // start pencil-drawing when a path is started
10531
10549
  pencilPoints = [xy];
10532
10550
  extendCurrentPath(p);
10533
- } else if (hoverVertexInfo && pencilPoints.length > 2) {
10551
+ } else if (polygonMode() && hoverVertexInfo && pencilPoints.length > 2) {
10534
10552
  // close path
10535
10553
  p = hoverVertexInfo.point;
10536
10554
  appendVertex$1(hit.getHitTarget(), p);
10537
10555
  extendCurrentPath(p);
10538
10556
  pencilPoints = null; // stop drawing
10539
- } else if (pointExceedsTolerance(xy, pencilPoints, 1.5)) {
10540
- xy = pencilPoints.pop();
10541
- p = pixToDataCoords(xy[0], xy[1]);
10542
- pencilPoints = [xy];
10557
+ } else if (pencilPoints.length >= 2 && pointExceedsTolerance(xy, pencilPoints, 1.4)) {
10558
+ xy2 = pencilPoints.pop();
10559
+ p = pixToDataCoords(xy2[0], xy2[1]);
10543
10560
  extendCurrentPath(p);
10561
+ // kludgy way to get a smoother line (could be better)
10562
+ // not this pencilPoints = [getAvgPoint(pencilPoints), xy2, xy];
10563
+ // not this pencilPoints = pencilPoints.slice(-2).concat([xy2, xy]);
10564
+ pencilPoints = [getAvgPoint(pencilPoints.slice(-3).concat([xy2])), xy2, xy];
10544
10565
  } else {
10545
10566
  // skip this point, update the hover line
10546
10567
  pencilPoints.push(xy);
@@ -12305,373 +12326,107 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12305
12326
  }
12306
12327
  }
12307
12328
 
12308
- function loadScript(url, cb) {
12309
- var script = document.createElement('script');
12310
- script.onload = cb;
12311
- script.src = url;
12312
- document.head.appendChild(script);
12313
- }
12314
-
12315
- function loadStylesheet(url) {
12316
- var el = document.createElement('link');
12317
- el.rel = 'stylesheet';
12318
- el.type = 'text/css';
12319
- el.media = 'screen';
12320
- el.href = url;
12321
- document.head.appendChild(el);
12322
- }
12323
-
12324
- function Basemap(gui, ext) {
12325
- var menu = gui.container.findChild('.basemap-options');
12326
- // var hideBtn = new SimpleButton(menu.findChild('.hide-btn'));
12327
- var fadeBtn = new SimpleButton(menu.findChild('.fade-btn'));
12328
- var closeBtn = new SimpleButton(menu.findChild('.close2-btn'));
12329
- var clearBtn = new SimpleButton(menu.findChild('.clear-btn'));
12330
- var list = menu.findChild('.basemap-styles');
12331
- var container = gui.container.findChild('.basemap-container');
12332
- var basemapBtn = gui.container.findChild('.basemap-btn');
12333
- var basemapNote = gui.container.findChild('.basemap-note');
12334
- var basemapWarning = gui.container.findChild('.basemap-warning');
12335
- var mapEl = gui.container.findChild('.basemap');
12336
- var extentNote = El('div').addClass('basemap-prompt').appendTo(container).hide();
12337
- var params = window.mapboxParams;
12338
- var map;
12339
- var activeStyle;
12340
- var loading = false;
12341
- var faded = false;
12342
-
12343
- if (params) {
12344
- // TODO: check page URL for compatibility with mapbox key
12345
- init();
12346
- } else {
12347
- basemapBtn.hide();
12348
- }
12329
+ utils$1.inherit(MshpMap, EventDispatcher);
12349
12330
 
12350
- function init() {
12351
- gui.addMode('basemap', turnOn, turnOff, basemapBtn);
12331
+ function MshpMap(gui) {
12332
+ var opts = gui.options,
12333
+ el = gui.container.findChild('.map-layers').node(),
12334
+ position = new ElementPosition(el),
12335
+ model = gui.model,
12336
+ map = this,
12337
+ _mouse = new MouseArea(el, position),
12338
+ _ext = new MapExtent(position),
12339
+ _nav = new MapNav(gui, _ext, _mouse),
12340
+ _visibleLayers = [], // cached visible map layers
12341
+ _hit,
12342
+ _intersectionLyr, _activeLyr, _overlayLyr,
12343
+ _renderer, _dynamicCRS;
12352
12344
 
12353
- closeBtn.on('click', function() {
12354
- gui.clearMode();
12355
- turnOff();
12356
- });
12345
+ _mouse.disable(); // wait for gui.focus() to activate mouse events
12357
12346
 
12358
- clearBtn.on('click', function() {
12359
- if (activeStyle) {
12360
- turnOffBasemap();
12361
- updateButtons();
12362
- closeMenu();
12363
- }
12364
- });
12347
+ model.on('select', function(e) {
12348
+ _intersectionLyr = null;
12349
+ _overlayLyr = null;
12350
+ });
12365
12351
 
12366
- fadeBtn.on('click', function() {
12367
- if (faded) {
12368
- mapEl.css('opacity', 1);
12369
- faded = false;
12370
- fadeBtn.text('Fade');
12371
- } else if (activeStyle) {
12372
- mapEl.css('opacity', 0.35);
12373
- faded = true;
12374
- fadeBtn.text('Unfade');
12375
- }
12376
- });
12352
+ gui.on('active', function() {
12353
+ _mouse.enable();
12354
+ });
12377
12355
 
12378
- gui.on('map_click', function() {
12379
- // close menu if user click on the map
12380
- if (gui.getMode() == 'basemap') gui.clearMode();
12381
- });
12356
+ gui.on('inactive', function() {
12357
+ _mouse.disable();
12358
+ });
12382
12359
 
12383
- params.styles.forEach(function(style) {
12384
- var btn = El('div').html(`<div class="basemap-style-btn"><img src="${style.icon}"></img></div><div class="basemap-style-label">${style.name}</div>`);
12385
- btn.findChild('.basemap-style-btn').on('click', function() {
12386
- if (style == activeStyle) {
12387
- turnOffBasemap();
12388
- } else {
12389
- showBasemap(style);
12390
- }
12391
- updateButtons();
12392
- closeMenu();
12393
- });
12394
- btn.appendTo(list);
12395
- });
12396
- }
12360
+ gui.on('map-needs-refresh', function() {
12361
+ drawLayers();
12362
+ });
12397
12363
 
12398
- // close and turn off mode
12399
- function closeMenu() {
12400
- setTimeout(function() {
12401
- gui.clearMode();
12402
- }, 200);
12403
- }
12364
+ model.on('update', onUpdate);
12404
12365
 
12405
- function turnOffBasemap() {
12406
- activeStyle = null;
12407
- gui.map.setDisplayCRS(null);
12408
- refresh();
12409
- }
12366
+ document.addEventListener('visibilitychange', function(e) {
12367
+ // refresh map when browser tab is re-activated (Chrome on mac has been
12368
+ // blanking the canvas after several other tabs are visited)
12369
+ if (document.visibilityState == 'visible') drawLayers();
12370
+ });
12410
12371
 
12411
- function showBasemap(style) {
12412
- activeStyle = style;
12413
- // TODO: consider enabling dark basemap mode
12414
- // Make sure that the selected layer style gets updated in gui-map.js
12415
- // gui.state.dark_basemap = style && style.dark || false;
12416
- if (map) {
12417
- map.setStyle(style.url);
12418
- refresh();
12419
- } else if (prepareMapView()) {
12420
- initMap();
12372
+ // Update display of segment intersections
12373
+ this.setIntersectionLayer = function(lyr, dataset) {
12374
+ if (lyr == _intersectionLyr) return; // no change
12375
+ if (lyr) {
12376
+ enhanceLayerForDisplay(lyr, dataset, getDisplayOptions());
12377
+ _intersectionLyr = lyr;
12378
+ _intersectionLyr.gui.style = getIntersectionStyle(_intersectionLyr.gui.displayLayer, getGlobalStyleOptions());
12379
+ } else {
12380
+ _intersectionLyr = null;
12421
12381
  }
12422
- }
12423
-
12424
- function updateButtons() {
12425
- list.findChildren('.basemap-style-btn').forEach(function(el, i) {
12426
- el.classed('active', params.styles[i] == activeStyle);
12427
- });
12428
- }
12382
+ // TODO: try to avoid redrawing layers twice (in some situations)
12383
+ drawLayers();
12384
+ };
12429
12385
 
12430
- function turnOn() {
12431
- // TODO: show basemap even if there is no data
12432
- var activeLyr = gui.model.getActiveLayer(); // may be null
12433
- var info = getDatasetCrsInfo(activeLyr?.dataset); // defaults to wgs84
12434
- var dataCRS = info.crs || null;
12435
- var displayCRS = gui.map.getDisplayCRS();
12436
- var warning;
12386
+ this.setLayerPinning = function(target, pinned) {
12387
+ target.layer.pinned = !!pinned;
12388
+ };
12437
12389
 
12438
- if (!dataCRS || !displayCRS || !crsIsUsable(displayCRS) || !crsIsUsable(dataCRS)) {
12439
- warning = 'This data is incompatible with the basemaps.';
12440
- if (!internal.layerHasGeometry(activeLyr.layer)) {
12441
- warning += ' Reason: layer is missing geographic data';
12442
- } else if (!dataCRS) {
12443
- warning += ' Reason: unknown projection.';
12444
- }
12390
+ this.translatePixelCoords = function(x, y) {
12391
+ var p = _ext.translatePixelCoords(x, y);
12392
+ if (!_dynamicCRS) return p;
12393
+ return internal.toLngLat(p, _dynamicCRS);
12394
+ };
12445
12395
 
12446
- basemapWarning.html(warning).show();
12447
- basemapNote.hide();
12448
- } else {
12449
- basemapNote.show();
12396
+ this.pixelCoordsToDisplayCoords = function(x, y) {
12397
+ var p1 = _ext.translatePixelCoords(x, y);
12398
+ var p2 = _ext.translatePixelCoords(x+1, y+1);
12399
+ var crs = this.getDisplayCRS();
12400
+ if (crs) {
12401
+ p1 = internal.toLngLat(p1, crs) || p1;
12402
+ p2 = internal.toLngLat(p2, crs) || p2;
12450
12403
  }
12451
- menu.show();
12452
- }
12404
+ return formatCoordsForDisplay(p1, p2);
12405
+ };
12453
12406
 
12454
- function turnOff() {
12455
- basemapWarning.hide();
12456
- basemapNote.hide();
12457
- menu.hide();
12458
- }
12407
+ // this.getCenterLngLat = function() {
12408
+ // var bounds = _ext.getBounds();
12409
+ // var crs = this.getDisplayCRS();
12410
+ // // TODO: handle case where active layer is a frame layer
12411
+ // if (!bounds.hasBounds() || !crs) {
12412
+ // return null;
12413
+ // }
12414
+ // return internal.toLngLat([bounds.centerX(), bounds.centerY()], crs);
12415
+ // };
12459
12416
 
12460
- function enabled() {
12461
- return !!(mapEl && params);
12462
- }
12463
-
12464
- function show() {
12465
- gui.container.addClass('basemap-on');
12466
- mapEl.node().style.display = 'block';
12467
- }
12468
-
12469
- function hide() {
12470
- gui.container.removeClass('basemap-on');
12471
- mapEl.node().style.display = 'none';
12472
- }
12473
-
12474
- function getLonLatBounds() {
12475
- var bbox = ext.getBounds().toArray();
12476
- var bbox2 = fromWebMercator(bbox[0], bbox[1])
12477
- .concat(fromWebMercator(bbox[2], bbox[3]));
12478
- return bbox2;
12479
- }
12480
-
12481
- function initMap() {
12482
- if (!enabled() || map || loading) return;
12483
- loading = true;
12484
- loadStylesheet(params.css);
12485
- loadScript(params.js, function() {
12486
- map = new window.mapboxgl.Map({
12487
- accessToken: params.key,
12488
- logoPosition: 'bottom-left',
12489
- container: mapEl.node(),
12490
- style: activeStyle.url,
12491
- bounds: getLonLatBounds(),
12492
- doubleClickZoom: false,
12493
- dragPan: false,
12494
- dragRotate: false,
12495
- scrollZoom: false,
12496
- interactive: false,
12497
- keyboard: false,
12498
- maxPitch: 0,
12499
- renderWorldCopies: true // false // false prevents panning off the map
12500
- });
12501
- map.on('load', function() {
12502
- loading = false;
12503
- refresh();
12504
- });
12505
- });
12506
- }
12507
-
12508
- // @bbox: latlon bounding box of current map extent
12509
- function checkBounds(bbox) {
12510
- var mpp = ext.getBounds().width() / ext.width();
12511
- var z = scaleToZoom(mpp);
12512
- var msg;
12513
- if (bbox[1] >= -85 && bbox[3] <= 85 && z <= 20) {
12514
- extentNote.hide();
12515
- return true;
12516
- }
12517
- if (z > 20) {
12518
- msg = 'zoom out';
12519
- } else if (bbox[1] > 0) {
12520
- msg = 'pan south';
12521
- } else if (bbox[3] < 0) {
12522
- msg = 'pan north';
12523
- } else {
12524
- msg = msg = 'zoom in';
12525
- }
12526
- extentNote.html(msg + ' to see the basemap').show();
12527
- return false;
12528
- }
12529
-
12530
- function crsIsUsable(crs) {
12531
- if (!crs) return false;
12532
- if (!internal.isInvertibleCRS(crs)) return false;
12533
- return true;
12534
- }
12535
-
12536
- function prepareMapView() {
12537
- var crs = gui.map.getDisplayCRS();
12538
- if (!crs) return false;
12539
- if (!internal.isWebMercator(crs)) {
12540
- gui.map.setDisplayCRS(internal.parseCrsString('webmercator'));
12541
- }
12542
- return true;
12543
- }
12544
-
12545
- function refresh() {
12546
- var crs = gui.map.getDisplayCRS();
12547
- var off = !crs || !enabled() || !map || loading || !activeStyle;
12548
- fadeBtn.active(!off);
12549
- clearBtn.active(!off);
12550
- if (off) {
12551
- hide();
12552
- extentNote.hide();
12553
- return;
12554
- }
12555
-
12556
- prepareMapView();
12557
- var bbox = getLonLatBounds();
12558
- if (!checkBounds(bbox)) {
12559
- // map does not display outside these bounds
12560
- hide();
12561
- } else {
12562
- show();
12563
- map.resize();
12564
- map.fitBounds(bbox, {animate: false});
12565
- }
12566
- }
12567
-
12568
- return {refresh: refresh}; // called by map when extent changes
12569
- }
12570
-
12571
- utils$1.inherit(MshpMap, EventDispatcher);
12572
-
12573
- function MshpMap(gui) {
12574
- var opts = gui.options,
12575
- el = gui.container.findChild('.map-layers').node(),
12576
- position = new ElementPosition(el),
12577
- model = gui.model,
12578
- map = this,
12579
- _mouse = new MouseArea(el, position),
12580
- _ext = new MapExtent(position),
12581
- _nav = new MapNav(gui, _ext, _mouse),
12582
- _visibleLayers = [], // cached visible map layers
12583
- _hit,
12584
- _basemap,
12585
- _intersectionLyr, _activeLyr, _overlayLyr,
12586
- _renderer, _dynamicCRS;
12587
-
12588
- _basemap = new Basemap(gui, _ext);
12589
-
12590
- _mouse.disable(); // wait for gui.focus() to activate mouse events
12591
-
12592
- model.on('select', function(e) {
12593
- _intersectionLyr = null;
12594
- _overlayLyr = null;
12595
- });
12596
-
12597
- gui.on('active', function() {
12598
- _mouse.enable();
12599
- });
12600
-
12601
- gui.on('inactive', function() {
12602
- _mouse.disable();
12603
- });
12604
-
12605
- gui.on('map-needs-refresh', function() {
12606
- drawLayers();
12607
- });
12608
-
12609
- model.on('update', onUpdate);
12610
-
12611
- document.addEventListener('visibilitychange', function(e) {
12612
- // refresh map when browser tab is re-activated (Chrome on mac has been
12613
- // blanking the canvas after several other tabs are visited)
12614
- if (document.visibilityState == 'visible') drawLayers();
12615
- });
12616
-
12617
- // Update display of segment intersections
12618
- this.setIntersectionLayer = function(lyr, dataset) {
12619
- if (lyr == _intersectionLyr) return; // no change
12620
- if (lyr) {
12621
- enhanceLayerForDisplay(lyr, dataset, getDisplayOptions());
12622
- _intersectionLyr = lyr;
12623
- _intersectionLyr.gui.style = getIntersectionStyle(_intersectionLyr.gui.displayLayer, getGlobalStyleOptions());
12624
- } else {
12625
- _intersectionLyr = null;
12626
- }
12627
- // TODO: try to avoid redrawing layers twice (in some situations)
12628
- drawLayers();
12629
- };
12630
-
12631
- this.setLayerPinning = function(target, pinned) {
12632
- target.layer.pinned = !!pinned;
12633
- };
12634
-
12635
- this.translatePixelCoords = function(x, y) {
12636
- var p = _ext.translatePixelCoords(x, y);
12637
- if (!_dynamicCRS) return p;
12638
- return internal.toLngLat(p, _dynamicCRS);
12639
- };
12640
-
12641
- this.pixelCoordsToDisplayCoords = function(x, y) {
12642
- var p1 = _ext.translatePixelCoords(x, y);
12643
- var p2 = _ext.translatePixelCoords(x+1, y+1);
12644
- var crs = this.getDisplayCRS();
12645
- if (crs) {
12646
- p1 = internal.toLngLat(p1, crs) || p1;
12647
- p2 = internal.toLngLat(p2, crs) || p2;
12648
- }
12649
- return formatCoordsForDisplay(p1, p2);
12650
- };
12651
-
12652
- // this.getCenterLngLat = function() {
12653
- // var bounds = _ext.getBounds();
12654
- // var crs = this.getDisplayCRS();
12655
- // // TODO: handle case where active layer is a frame layer
12656
- // if (!bounds.hasBounds() || !crs) {
12657
- // return null;
12658
- // }
12659
- // return internal.toLngLat([bounds.centerX(), bounds.centerY()], crs);
12660
- // };
12661
-
12662
- this.getDisplayCRS = function() {
12663
- if (!_activeLyr) {
12664
- return _dynamicCRS || internal.parseCrsString('wgs84');
12665
- }
12666
- if (!_activeLyr.gui.geographic) {
12667
- return null;
12668
- }
12669
- if (_activeLyr.gui.dynamic_crs) {
12670
- return _activeLyr.gui.dynamic_crs;
12671
- }
12672
- var info = getDatasetCrsInfo(_activeLyr.gui.source.dataset);
12673
- return info.crs || null;
12674
- };
12417
+ this.getDisplayCRS = function() {
12418
+ if (!_activeLyr) {
12419
+ return _dynamicCRS || internal.parseCrsString('wgs84');
12420
+ }
12421
+ if (!_activeLyr.gui.geographic) {
12422
+ return null;
12423
+ }
12424
+ if (_activeLyr.gui.dynamic_crs) {
12425
+ return _activeLyr.gui.dynamic_crs;
12426
+ }
12427
+ var info = getDatasetCrsInfo(_activeLyr.gui.source.dataset);
12428
+ return info.crs || null;
12429
+ };
12675
12430
 
12676
12431
  this.getExtent = function() {return _ext;};
12677
12432
  this.getMouse = function() {return _mouse;};
@@ -12737,7 +12492,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12737
12492
  }
12738
12493
 
12739
12494
  _ext.on('change', function(e) {
12740
- if (_basemap) _basemap.refresh(); // keep basemap synced up (if enabled)
12495
+ gui?.basemap.refresh(); // keep basemap synced up (if enabled)
12741
12496
  drawLayers(e.redraw ? '' : 'nav');
12742
12497
  });
12743
12498
 
@@ -12816,7 +12571,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12816
12571
 
12817
12572
  if (needReset) {
12818
12573
  _ext.reset();
12819
- if (_basemap) _basemap.refresh();
12574
+ gui?.basemap.refresh();
12820
12575
  }
12821
12576
  drawLayers();
12822
12577
  map.dispatchEvent('updated');
@@ -13206,6 +12961,293 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13206
12961
  return retn;
13207
12962
  }
13208
12963
 
12964
+ function loadScript(url, cb) {
12965
+ var script = document.createElement('script');
12966
+ script.onload = cb;
12967
+ script.src = url;
12968
+ document.head.appendChild(script);
12969
+ }
12970
+
12971
+ function loadStylesheet(url) {
12972
+ var el = document.createElement('link');
12973
+ el.rel = 'stylesheet';
12974
+ el.type = 'text/css';
12975
+ el.media = 'screen';
12976
+ el.href = url;
12977
+ document.head.appendChild(el);
12978
+ }
12979
+
12980
+ function Basemap(gui) {
12981
+ var menu = gui.container.findChild('.basemap-options');
12982
+ var fadeBtn = new SimpleButton(menu.findChild('.fade-btn'));
12983
+ var closeBtn = new SimpleButton(menu.findChild('.close2-btn'));
12984
+ var clearBtn = new SimpleButton(menu.findChild('.clear-btn'));
12985
+ var menuButtons = menu.findChild('.basemap-styles');
12986
+ var overlayButtons = gui.container.findChild('.basemap-overlay-buttons');
12987
+ var container = gui.container.findChild('.basemap-container');
12988
+ var basemapBtn = gui.container.findChild('.basemap-btn');
12989
+ var basemapNote = gui.container.findChild('.basemap-note');
12990
+ var basemapWarning = gui.container.findChild('.basemap-warning');
12991
+ var mapEl = gui.container.findChild('.basemap');
12992
+ var extentNote = El('div').addClass('basemap-prompt').appendTo(container).hide();
12993
+ var params = window.mapboxParams;
12994
+ var map;
12995
+ var activeStyle;
12996
+ var loading = false;
12997
+ var faded = false;
12998
+
12999
+ if (params) {
13000
+ // TODO: check page URL for compatibility with mapbox key
13001
+ init();
13002
+ } else {
13003
+ basemapBtn.hide();
13004
+ }
13005
+
13006
+ function init() {
13007
+ gui.addMode('basemap', turnOn, turnOff, basemapBtn);
13008
+
13009
+ closeBtn.on('click', function() {
13010
+ gui.clearMode();
13011
+ turnOff();
13012
+ });
13013
+
13014
+ clearBtn.on('click', function() {
13015
+ if (activeStyle) {
13016
+ turnOffBasemap();
13017
+ updateButtons();
13018
+ closeMenu();
13019
+ }
13020
+ });
13021
+
13022
+ fadeBtn.on('click', function() {
13023
+ if (faded) {
13024
+ mapEl.css('opacity', 1);
13025
+ faded = false;
13026
+ fadeBtn.text('Fade');
13027
+ } else if (activeStyle) {
13028
+ mapEl.css('opacity', 0.35);
13029
+ faded = true;
13030
+ fadeBtn.text('Unfade');
13031
+ }
13032
+ });
13033
+
13034
+ gui.model.on('update', onUpdate);
13035
+
13036
+ gui.on('map_click', function() {
13037
+ // close menu if user click on the map
13038
+ if (gui.getMode() == 'basemap') gui.clearMode();
13039
+ });
13040
+
13041
+ params.styles.forEach(function(style) {
13042
+ El('div')
13043
+ .html(`<div class="basemap-style-btn"><img src="${style.icon}"></img></div><div class="basemap-style-label">${style.name}</div>`)
13044
+ .appendTo(menuButtons)
13045
+ .findChild('.basemap-style-btn').on('click', onClick);
13046
+
13047
+ El('div').addClass('basemap-overlay-btn basemap-style-btn')
13048
+ .html(`<img src="${style.icon}"></img>`).on('click', onClick)
13049
+ .appendTo(overlayButtons);
13050
+
13051
+ function onClick() {
13052
+ if (overlayButtons.hasClass('disabled')) return;
13053
+ if (style == activeStyle) {
13054
+ turnOffBasemap();
13055
+ } else {
13056
+ showBasemap(style);
13057
+ }
13058
+ updateButtons();
13059
+ closeMenu();
13060
+ }
13061
+ });
13062
+ }
13063
+
13064
+ // close and turn off mode
13065
+ function closeMenu() {
13066
+ setTimeout(function() {
13067
+ gui.clearMode();
13068
+ }, 200);
13069
+ }
13070
+
13071
+ function turnOffBasemap() {
13072
+ activeStyle = null;
13073
+ gui.map.setDisplayCRS(null);
13074
+ refresh();
13075
+ }
13076
+
13077
+ function showBasemap(style) {
13078
+ activeStyle = style;
13079
+ // TODO: consider enabling dark basemap mode
13080
+ // Make sure that the selected layer style gets updated in gui-map.js
13081
+ // gui.state.dark_basemap = style && style.dark || false;
13082
+ if (map) {
13083
+ map.setStyle(style.url);
13084
+ refresh();
13085
+ } else if (prepareMapView()) {
13086
+ initMap();
13087
+ }
13088
+ }
13089
+
13090
+ function updateButtons() {
13091
+ menuButtons.findChildren('.basemap-style-btn').forEach(function(el, i) {
13092
+ el.classed('active', params.styles[i] == activeStyle);
13093
+ });
13094
+ overlayButtons.findChildren('.basemap-style-btn').forEach(function(el, i) {
13095
+ el.classed('active', params.styles[i] == activeStyle);
13096
+ });
13097
+ }
13098
+
13099
+ function turnOn() {
13100
+ onUpdate();
13101
+ menu.show();
13102
+ }
13103
+
13104
+ function onUpdate() {
13105
+ var activeLyr = gui.model.getActiveLayer(); // may be null
13106
+ var info = getDatasetCrsInfo(activeLyr?.dataset); // defaults to wgs84
13107
+ var dataCRS = info.crs || null;
13108
+ var displayCRS = gui.map.getDisplayCRS();
13109
+ var warning, note;
13110
+
13111
+
13112
+ if (!dataCRS || !displayCRS || !crsIsUsable(displayCRS) || !crsIsUsable(dataCRS)) {
13113
+ warning = 'This data is incompatible with the basemaps.';
13114
+ if (!internal.layerHasGeometry(activeLyr.layer)) {
13115
+ warning += ' Reason: layer is missing geographic data';
13116
+ } else if (!dataCRS) {
13117
+ warning += ' Reason: unknown projection.';
13118
+ }
13119
+ basemapWarning.html(warning).show();
13120
+ basemapNote.hide();
13121
+ overlayButtons.addClass('disabled');
13122
+ activeStyle = null;
13123
+ updateButtons();
13124
+ } else {
13125
+ note = `Your data ${activeStyle ? 'is' : 'will be'} displayed using the Mercator projection.`;
13126
+ basemapNote.text(note).show();
13127
+ overlayButtons.show();
13128
+ overlayButtons.removeClass('disabled');
13129
+ }
13130
+ }
13131
+
13132
+ function turnOff() {
13133
+ basemapWarning.hide();
13134
+ basemapNote.hide();
13135
+ menu.hide();
13136
+ }
13137
+
13138
+ function enabled() {
13139
+ return !!(mapEl && params);
13140
+ }
13141
+
13142
+ function show() {
13143
+ gui.container.addClass('basemap-on');
13144
+ mapEl.node().style.display = 'block';
13145
+ }
13146
+
13147
+ function hide() {
13148
+ gui.container.removeClass('basemap-on');
13149
+ mapEl.node().style.display = 'none';
13150
+ }
13151
+
13152
+ function getLonLatBounds() {
13153
+ var ext = gui.map.getExtent();
13154
+ var bbox = ext.getBounds().toArray();
13155
+ var bbox2 = fromWebMercator(bbox[0], bbox[1])
13156
+ .concat(fromWebMercator(bbox[2], bbox[3]));
13157
+ return bbox2;
13158
+ }
13159
+
13160
+ function initMap() {
13161
+ if (!enabled() || map || loading) return;
13162
+ loading = true;
13163
+ loadStylesheet(params.css);
13164
+ loadScript(params.js, function() {
13165
+ map = new window.mapboxgl.Map({
13166
+ accessToken: params.key,
13167
+ logoPosition: 'bottom-left',
13168
+ container: mapEl.node(),
13169
+ style: activeStyle.url,
13170
+ bounds: getLonLatBounds(),
13171
+ doubleClickZoom: false,
13172
+ dragPan: false,
13173
+ dragRotate: false,
13174
+ scrollZoom: false,
13175
+ interactive: false,
13176
+ keyboard: false,
13177
+ maxPitch: 0,
13178
+ renderWorldCopies: true // false // false prevents panning off the map
13179
+ });
13180
+ map.on('load', function() {
13181
+ loading = false;
13182
+ refresh();
13183
+ });
13184
+ });
13185
+ }
13186
+
13187
+ // @bbox: latlon bounding box of current map extent
13188
+ function checkBounds(bbox) {
13189
+ var ext = gui.map.getExtent();
13190
+ var mpp = ext.getBounds().width() / ext.width();
13191
+ var z = scaleToZoom(mpp);
13192
+ var msg;
13193
+ if (bbox[1] >= -85 && bbox[3] <= 85 && z <= 20) {
13194
+ extentNote.hide();
13195
+ return true;
13196
+ }
13197
+ if (z > 20) {
13198
+ msg = 'zoom out';
13199
+ } else if (bbox[1] > 0) {
13200
+ msg = 'pan south';
13201
+ } else if (bbox[3] < 0) {
13202
+ msg = 'pan north';
13203
+ } else {
13204
+ msg = msg = 'zoom in';
13205
+ }
13206
+ extentNote.html(msg + ' to see the basemap').show();
13207
+ return false;
13208
+ }
13209
+
13210
+ function crsIsUsable(crs) {
13211
+ if (!crs) return false;
13212
+ if (!internal.isInvertibleCRS(crs)) return false;
13213
+ return true;
13214
+ }
13215
+
13216
+ function prepareMapView() {
13217
+ var crs = gui.map.getDisplayCRS();
13218
+ if (!crs) return false;
13219
+ if (!internal.isWebMercator(crs)) {
13220
+ gui.map.setDisplayCRS(internal.parseCrsString('webmercator'));
13221
+ }
13222
+ return true;
13223
+ }
13224
+
13225
+ function refresh() {
13226
+ var crs = gui.map.getDisplayCRS();
13227
+ var off = !crs || !enabled() || !map || loading || !activeStyle;
13228
+ fadeBtn.active(!off);
13229
+ clearBtn.active(!off);
13230
+ if (off) {
13231
+ hide();
13232
+ extentNote.hide();
13233
+ return;
13234
+ }
13235
+
13236
+ prepareMapView();
13237
+ var bbox = getLonLatBounds();
13238
+ if (!checkBounds(bbox)) {
13239
+ // map does not display outside these bounds
13240
+ hide();
13241
+ } else {
13242
+ show();
13243
+ map.resize();
13244
+ map.fitBounds(bbox, {animate: false});
13245
+ }
13246
+ }
13247
+
13248
+ return {refresh, show: onUpdate};
13249
+ }
13250
+
13209
13251
  function GuiInstance(container, opts) {
13210
13252
  var gui = new ModeSwitcher();
13211
13253
  opts = utils$1.extend({
@@ -13223,6 +13265,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13223
13265
  gui.model = new Model(gui);
13224
13266
  gui.keyboard = new KeyboardEvents(gui);
13225
13267
  gui.buttons = new SidebarButtons(gui);
13268
+ gui.basemap = new Basemap(gui);
13226
13269
  gui.session = new SessionHistory(gui);
13227
13270
  gui.contextMenu = new ContextMenu();
13228
13271
  gui.undo = new Undo(gui);
@@ -13388,6 +13431,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13388
13431
  if (dataLoaded) return;
13389
13432
  dataLoaded = true;
13390
13433
  gui.buttons.show();
13434
+ gui.basemap.show();
13391
13435
  El('#mode-buttons').show();
13392
13436
  El('#splash-buttons').hide();
13393
13437
  El('body').addClass('map-view');
package/www/mapshaper.js CHANGED
@@ -38860,14 +38860,15 @@ ${svg}
38860
38860
  return mergeDatasetsIntoDataset(target.dataset, datasets);
38861
38861
  };
38862
38862
 
38863
- cmd.rectangle = function(source, opts) {
38863
+ cmd.rectangle = function(target, opts) {
38864
38864
  var bounds, crsInfo;
38865
- if (source) {
38866
- bounds = getLayerBounds(source.layer, source.dataset.arcs);
38867
- crsInfo = getDatasetCrsInfo(source.dataset);
38868
- } else if (opts.bbox) {
38865
+ if (opts.bbox) {
38869
38866
  bounds = new Bounds(opts.bbox);
38870
- crsInfo = probablyDecimalDegreeBounds(bounds) ? getCrsInfo('wgs84') : {};
38867
+ crsInfo = target && getDatasetCrsInfo(target.dataset) ||
38868
+ probablyDecimalDegreeBounds(bounds) && getCrsInfo('wgs84') || {};
38869
+ } else if (target) {
38870
+ bounds = getLayerBounds(target.layer, target.dataset.arcs);
38871
+ crsInfo = getDatasetCrsInfo(target.dataset);
38871
38872
  }
38872
38873
  bounds = bounds && applyRectangleOptions(bounds, crsInfo.crs, opts);
38873
38874
  if (!bounds || !bounds.hasBounds()) {
@@ -45371,7 +45372,7 @@ ${svg}
45371
45372
  stop('Command requires a width= argument');
45372
45373
  }
45373
45374
  if (source || opts.bbox || targets.length === 0) {
45374
- job.catalog.addDataset(cmd.rectangle(source, opts));
45375
+ job.catalog.addDataset(cmd.rectangle(source || targets?.[0], opts));
45375
45376
  } else {
45376
45377
  outputLayers = cmd.rectangle2(targets[0], opts);
45377
45378
  }
@@ -45535,7 +45536,7 @@ ${svg}
45535
45536
  });
45536
45537
  }
45537
45538
 
45538
- var version = "0.6.82";
45539
+ var version = "0.6.83";
45539
45540
 
45540
45541
  // Parse command line args into commands and run them
45541
45542
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
package/www/page.css CHANGED
@@ -1124,8 +1124,6 @@ img.close-btn:hover,
1124
1124
  height: 100%;
1125
1125
  }
1126
1126
 
1127
-
1128
-
1129
1127
  .basemap-styles {
1130
1128
  margin: 0 -2px;
1131
1129
  white-space: nowrap;
@@ -1155,26 +1153,63 @@ img.close-btn:hover,
1155
1153
  margin-left: 10px;
1156
1154
  }
1157
1155
 
1156
+ .basemap-style-btn {
1157
+ position: relative;
1158
+ border-width: 2px;
1159
+ border-color: #98cde2;
1160
+ border-color: #bbddea;
1161
+ border-style: solid;
1162
+ border-radius: 6px;
1163
+ }
1164
+
1158
1165
  .basemap-style-btn img {
1159
1166
  display: block;
1160
- position: relative;
1161
- border: 2px solid transparent;
1162
1167
  width: 108px;
1163
1168
  }
1164
1169
 
1165
- div.basemap-style-btn.active img {
1166
- border: 2px solid #e8ba52;
1170
+ div.basemap-style-btn.active {
1171
+ border-color: #e8ba52;
1167
1172
  }
1168
1173
 
1169
- .basemap-style-btn:hover img {
1174
+ .basemap-buttons:not(.disabled) .basemap-style-btn:hover {
1170
1175
  cursor: pointer;
1171
- border: 2px solid #ead683;
1176
+ border-color: #f7e286;
1172
1177
  }
1173
1178
 
1174
1179
  .basemap-style-label {
1175
1180
  margin-left: 2px;
1176
1181
  }
1177
1182
 
1183
+ .basemap-overlay-buttons {
1184
+ z-index: 20;
1185
+ position: absolute;
1186
+ top: 6px;
1187
+ right: 36px;
1188
+ padding: 4px;
1189
+ background: white;
1190
+ border-radius: 7px;
1191
+ line-height: 0px;
1192
+ display: none;
1193
+ }
1194
+
1195
+ .basemap-style-btn.basemap-overlay-btn {
1196
+ width: 35px;
1197
+ height: 35px;
1198
+ margin: 3px;
1199
+ overflow: hidden;
1200
+ display: inline-block;
1201
+ /*border-width: 1px;*/
1202
+ }
1203
+
1204
+ .basemap-overlay-buttons.disabled > div {
1205
+ opacity: 0.5;
1206
+ }
1207
+
1208
+ .basemap-overlay-btn img {
1209
+ margin-left: -21px;
1210
+ width: 70px;
1211
+ }
1212
+
1178
1213
  .mapbox-improve-map {
1179
1214
  display: none;
1180
1215
  }
@@ -1254,10 +1289,11 @@ body.pan.panning .map-layers:not(.drawing) {
1254
1289
  z-index: 20;
1255
1290
  position: absolute;
1256
1291
  text-align: center;
1257
- top: 12px;
1292
+ top: 6px;
1258
1293
  right: 0;
1259
1294
  padding: 3px;
1260
1295
  background-color: rgba(255, 255, 255, 1);
1296
+ border-radius: 7px;
1261
1297
  }
1262
1298
 
1263
1299
  .nav-btn-wrapper {
@@ -1327,6 +1363,7 @@ body.pan.panning .map-layers:not(.drawing) {
1327
1363
 
1328
1364
  .nav-btn.selected {
1329
1365
  background-color: black;
1366
+ border-radius: 5px;
1330
1367
  }
1331
1368
 
1332
1369
  .nav-btn:hover:not(.disabled) svg *,