mapshaper 0.6.81 → 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.
@@ -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)) {
@@ -5812,6 +5809,7 @@
5812
5809
  var ctrlDown = false;
5813
5810
  var metaDown = false;
5814
5811
  var altDown = false;
5812
+ var spaceDown = false;
5815
5813
 
5816
5814
  function updateControlKeys(e) {
5817
5815
  shiftDown = e.shiftKey;
@@ -5819,15 +5817,22 @@
5819
5817
  metaDown = e.metaKey;
5820
5818
  altDown = e.altKey;
5821
5819
  }
5820
+
5821
+ function mouseIsPressed() {
5822
+ return gui.map.getMouse().isDown();
5823
+ }
5824
+
5822
5825
  document.addEventListener('keyup', function(e) {
5823
- if (!GUI.isActiveInstance(gui)) return;
5826
+ if (!GUI.isActiveInstance(gui) || e.repeat) return;
5824
5827
  // this can fail to fire if keyup occurs over a context menu
5828
+ if (e.keyCode == 32) spaceDown = false;
5825
5829
  updateControlKeys(e);
5826
5830
  self.dispatchEvent('keyup', getEventData(e));
5827
5831
  });
5828
5832
 
5829
5833
  document.addEventListener('keydown', function(e) {
5830
- if (!GUI.isActiveInstance(gui)) return;
5834
+ if (!GUI.isActiveInstance(gui) || e.repeat) return;
5835
+ if (e.keyCode == 32) spaceDown = true;
5831
5836
  updateControlKeys(e);
5832
5837
  self.dispatchEvent('keydown', getEventData(e));
5833
5838
  });
@@ -5837,10 +5842,11 @@
5837
5842
  updateControlKeys(e);
5838
5843
  });
5839
5844
 
5840
- this.shiftIsPressed = function() { return shiftDown; };
5841
- this.ctrlIsPressed = function() { return ctrlDown; };
5842
- this.altIsPressed = function() { return altDown; };
5843
- this.metaIsPressed = function() { return metaDown; };
5845
+ this.shiftIsPressed = () => shiftDown;
5846
+ this.ctrlIsPressed = () => ctrlDown;
5847
+ this.altIsPressed = () => altDown;
5848
+ this.metaIsPressed = () => metaDown;
5849
+ this.spaceIsPressed = () => spaceDown;
5844
5850
 
5845
5851
  this.onMenuSubmit = function(menuEl, cb) {
5846
5852
  gui.on('enter_key', function(e) {
@@ -5878,7 +5884,7 @@
5878
5884
 
5879
5885
  var menus = {
5880
5886
  standard: ['info', 'selection', 'box'],
5881
- empty: ['edit_points', 'edit_lines', 'edit_polygons'],
5887
+ empty: ['edit_points', 'edit_lines', 'edit_polygons', 'box'],
5882
5888
  polygons: ['info', 'selection', 'box', 'edit_polygons'],
5883
5889
  rectangles: ['info', 'selection', 'box', 'rectangles'],
5884
5890
  lines: ['info', 'selection', 'box' , 'edit_lines'],
@@ -8056,7 +8062,6 @@
8056
8062
  var transientIds = []; // e.g. hit ids while dragging a box
8057
8063
  var drawingId = -1; // kludge to allow hit detection and drawing (different feature ids)
8058
8064
  var active = false;
8059
- var interactionMode;
8060
8065
  var targetLayer;
8061
8066
  var hitTest;
8062
8067
  var pinnedOn; // used in multi-edit mode (selection) for toggling pinning behavior
@@ -8066,16 +8071,11 @@
8066
8071
  var priority = 2;
8067
8072
 
8068
8073
  mouse.on('contextmenu', function(e) {
8069
- // shift key enables default menu (for development)
8070
- if (gui.keyboard.shiftIsPressed()) {
8071
- return;
8072
- }
8073
8074
  e.originalEvent.preventDefault();
8074
- if (!El('body').hasClass('map-view')) {
8075
- return;
8075
+ if (El('body').hasClass('map-view')) {
8076
+ triggerHitEvent('contextmenu', e);
8076
8077
  }
8077
- triggerHitEvent('contextmenu', e);
8078
- }, false);
8078
+ });
8079
8079
 
8080
8080
  // init keyboard controls for pinned features
8081
8081
  gui.keyboard.on('keydown', function(evt) {
@@ -8117,11 +8117,14 @@
8117
8117
  };
8118
8118
 
8119
8119
  function updateHitTest(featureFilter) {
8120
- hitTest = getPointerHitTest(targetLayer, ext, interactionMode, featureFilter);
8120
+ hitTest = getPointerHitTest(targetLayer, ext, interactionMode(), featureFilter);
8121
+ }
8122
+
8123
+ function interactionMode() {
8124
+ return gui.interaction.getMode();
8121
8125
  }
8122
8126
 
8123
8127
  function turnOn(mode) {
8124
- interactionMode = mode;
8125
8128
  active = true;
8126
8129
  updateHitTest();
8127
8130
  }
@@ -8136,12 +8139,8 @@
8136
8139
  }
8137
8140
  }
8138
8141
 
8139
- function hoverable() {
8140
- return !!interactionMode;
8141
- }
8142
-
8143
8142
  function selectable() {
8144
- return interactionMode == 'selection';
8143
+ return interactionMode() == 'selection';
8145
8144
  }
8146
8145
 
8147
8146
  function pinnable() {
@@ -8150,16 +8149,16 @@
8150
8149
  }
8151
8150
 
8152
8151
  function draggable() {
8153
- return interactionMode == 'vertices' || interactionMode == 'edit_points' ||
8154
- interactionMode == 'labels' || interactionMode == 'edit_lines' ||
8155
- interactionMode == 'edit_polygons';
8152
+ var mode = interactionMode();
8153
+ return mode == 'vertices' || mode == 'edit_points' ||
8154
+ mode == 'labels' || mode == 'edit_lines' || mode == 'edit_polygons';
8156
8155
  }
8157
8156
 
8158
8157
  function clickable() {
8158
+ var mode = interactionMode();
8159
8159
  // click used to pin popup and select features
8160
- return interactionMode == 'data' || interactionMode == 'info' ||
8161
- interactionMode == 'selection' || interactionMode == 'rectangles' ||
8162
- interactionMode == 'edit_points';
8160
+ return mode == 'data' || mode == 'info' || mode == 'selection' ||
8161
+ mode == 'rectangles' || mode == 'edit_points';
8163
8162
  }
8164
8163
 
8165
8164
  self.getHitId = function() {
@@ -8284,7 +8283,6 @@
8284
8283
  // (some modes do not support pinning)
8285
8284
  gui.on('interaction_mode_change', function(e) {
8286
8285
  self.clearSelection();
8287
- // if (e.mode == 'off' || e.mode == 'box') {
8288
8286
  if (gui.interaction.modeUsesHitDetection(e.mode)) {
8289
8287
  turnOn(e.mode);
8290
8288
  } else {
@@ -8317,7 +8315,7 @@
8317
8315
  updateSelectionState(convertClickDataToSelectionData(hitTest(e)));
8318
8316
  }
8319
8317
 
8320
- if (pinned && interactionMode == 'edit_points') {
8318
+ if (pinned && interactionMode() == 'edit_points') {
8321
8319
  // kludge: intercept the click event if popup is turning off, so
8322
8320
  // a new point doesn't get made
8323
8321
  return;
@@ -8442,6 +8440,7 @@
8442
8440
 
8443
8441
  // check if an event is used in the current interaction mode
8444
8442
  function eventIsEnabled(type) {
8443
+ var mode = interactionMode();
8445
8444
  if (!active) return false;
8446
8445
  if (type == 'click' && gui.keyboard.ctrlIsPressed()) {
8447
8446
  return false; // don't fire if context menu might open
@@ -8450,13 +8449,13 @@
8450
8449
  return false;
8451
8450
  }
8452
8451
  if (type == 'click' &&
8453
- (interactionMode == 'edit_lines' || interactionMode == 'edit_polygons')) {
8452
+ (mode == 'edit_lines' || mode == 'edit_polygons')) {
8454
8453
  return true; // click events are triggered even if no shape is hit
8455
8454
  }
8456
- if (type == 'click' && interactionMode == 'edit_points') {
8455
+ if (type == 'click' && mode == 'edit_points') {
8457
8456
  return true;
8458
8457
  }
8459
- if ((interactionMode == 'edit_lines' || interactionMode == 'edit_polygons') &&
8458
+ if ((mode == 'edit_lines' || mode == 'edit_polygons') &&
8460
8459
  (type == 'hover' || type == 'dblclick')) {
8461
8460
  return true; // special case -- using hover for line drawing animation
8462
8461
  }
@@ -8477,7 +8476,7 @@
8477
8476
  }
8478
8477
 
8479
8478
  function possiblyStopPropagation(e) {
8480
- if (interactionMode == 'edit_lines' || interactionMode == 'edit_polygons') {
8479
+ if (interactionMode() == 'edit_lines' || interactionMode() == 'edit_polygons') {
8481
8480
  // handled conditionally in the control
8482
8481
  return;
8483
8482
  }
@@ -8494,7 +8493,7 @@
8494
8493
  // evt: event data (may be a pointer event object, an ordinary object or null)
8495
8494
  function triggerHitEvent(type, evt) {
8496
8495
  var eventData = {
8497
- mode: interactionMode
8496
+ mode: interactionMode()
8498
8497
  };
8499
8498
  if (evt) {
8500
8499
  // data coordinates
@@ -8813,6 +8812,11 @@
8813
8812
  element.addEventListener('mousedown', onAreaDown);
8814
8813
  element.addEventListener('dblclick', onAreaDblClick);
8815
8814
  document.addEventListener('contextmenu', function(e) {
8815
+ if (!e.ctrlKey) {
8816
+ e.preventDefault();
8817
+ }
8818
+ });
8819
+ element.addEventListener('contextmenu', function(e) {
8816
8820
  _self.dispatchEvent('contextmenu', procMouseEvent(e));
8817
8821
  });
8818
8822
 
@@ -9090,9 +9094,9 @@
9090
9094
 
9091
9095
  box.getDataCoords = function() {
9092
9096
  if (!boxCoords) return null;
9093
- var dataBox = getBBoxCoords(gui.map.getActiveLayer(), boxCoords);
9097
+ var lyr = gui.map.getActiveLayer();
9098
+ var dataBox = lyr ? translateCoordsToLayerCRS(boxCoords, lyr) : translateCoordsToLatLon(boxCoords);
9094
9099
  fixBounds(dataBox);
9095
- // return internal.getRoundedCoords(dataBox, internal.getBoundsPrecisionForDisplay(dataBox));
9096
9100
  return dataBox;
9097
9101
  };
9098
9102
 
@@ -9127,6 +9131,26 @@
9127
9131
  }
9128
9132
  };
9129
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
9130
9154
  function getBoxCoords(e) {
9131
9155
  var bbox = pixToCoords(e.a.concat(e.b), gui.map.getExtent());
9132
9156
  fixBounds(bbox);
@@ -9227,6 +9251,7 @@
9227
9251
  zoomBox = new HighlightBox(gui, {draggable: true, name: 'zoom-box'}), // .addClass('zooming'),
9228
9252
  shiftDrag = false,
9229
9253
  zoomScaleMultiplier = 1,
9254
+ panCount = 0,
9230
9255
  inBtn, outBtn,
9231
9256
  dragStartEvt,
9232
9257
  _fx, _fy; // zoom foci, [0,1]
@@ -9278,6 +9303,7 @@
9278
9303
  // var lyr = gui.model.getActiveLayer()?.layer;
9279
9304
  // if (lyr && !internal.layerHasGeometry(lyr)) return;
9280
9305
  shiftDrag = !!e.shiftKey;
9306
+ panCount = 0;
9281
9307
  if (shiftDrag) {
9282
9308
  if (useBoxZoom()) zoomBox.turnOn();
9283
9309
  dragStartEvt = e;
@@ -9289,9 +9315,18 @@
9289
9315
  if (disabled()) return;
9290
9316
  if (shiftDrag) {
9291
9317
  gui.dispatchEvent('shift_drag', getBoxData(e));
9292
- } else {
9293
- ext.pan(e.dx, e.dy);
9318
+ return;
9294
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);
9328
+ }
9329
+ ext.pan(e.dx, e.dy);
9295
9330
  });
9296
9331
 
9297
9332
  mouse.on('dragend', function(e) {
@@ -9301,6 +9336,8 @@
9301
9336
  shiftDrag = false;
9302
9337
  gui.dispatchEvent('shift_drag_end', getBoxData(e));
9303
9338
  zoomBox.turnOff();
9339
+ } else {
9340
+ El('body').removeClass('panning').removeClass('pan');
9304
9341
  }
9305
9342
  });
9306
9343
 
@@ -10304,7 +10341,7 @@
10304
10341
  }
10305
10342
 
10306
10343
  function pencilIsActive() {
10307
- return cmdKeyDown() && active() && !vertexDragging() && !!pencilPoints;
10344
+ return active() && (cmdKeyDown() || pathDrawing()) && !vertexDragging() && !!pencilPoints;
10308
10345
  }
10309
10346
 
10310
10347
  function polygonMode() {
@@ -10389,8 +10426,7 @@
10389
10426
  function showInstructions() {
10390
10427
  var isMac = navigator.userAgent.includes('Mac');
10391
10428
  var undoKey = isMac ? '⌘' : '^';
10392
- var drawKey = isMac ? '⌘' : 'Alt';
10393
- 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.`;
10429
+ var msg = `Instructions: click to start a path, click or drag to keep drawing. Drag vertices to reshape a path. Type ${undoKey}Z/${undoKey}Y to undo/redo.`;
10394
10430
  alert = showPopupAlert(msg, null, {
10395
10431
  non_blocking: true, max_width: '388px'});
10396
10432
  }
@@ -10449,8 +10485,15 @@
10449
10485
  drawingId = -1;
10450
10486
  hoverVertexInfo = null;
10451
10487
  prevClickEvent = prevHoverEvent = null;
10488
+ updateCursor();
10452
10489
  }
10453
10490
 
10491
+ gui.keyboard.on('keydown', function(e) {
10492
+ if (active() && e.keyName == 'space') {
10493
+ e.stopPropagation(); // prevent console from opening if shift-panning
10494
+ }
10495
+ }, null, 1);
10496
+
10454
10497
  hit.on('contextmenu', function(e) {
10455
10498
  if (!active() || pathDrawing() || vertexDragging()) return;
10456
10499
  var target = hit.getHitTarget();
@@ -10482,11 +10525,21 @@
10482
10525
  });
10483
10526
 
10484
10527
  gui.map.getMouse().on('drag', function(e) {
10485
- if (!active() || !cmdKeyDown() || vertexDragging()) return;
10528
+ if (!pencilIsActive()) {
10529
+ if (!pencilPoints) {
10530
+ // null points signals that a path was just completed -- block panning
10531
+ e.stopPropagation();
10532
+ }
10533
+ return;
10534
+ }
10535
+ if (gui.keyboard.spaceIsPressed()) {
10536
+ // pan if dragging with spacebar down
10537
+ pencilPoints = []; // don't continue previous line after panning
10538
+ return;
10539
+ }
10486
10540
  e.stopPropagation(); // prevent panning
10487
- if (!pencilIsActive()) return;
10488
10541
  hoverVertexInfo = findPathStartInfo(e);
10489
- var xy = [e.x, e.y];
10542
+ var xy = [e.x, e.y], xy2;
10490
10543
  var p = pixToDataCoords(e.x, e.y);
10491
10544
  if (!pathDrawing()) {
10492
10545
  pencilPoints = [xy];
@@ -10495,17 +10548,20 @@
10495
10548
  // start pencil-drawing when a path is started
10496
10549
  pencilPoints = [xy];
10497
10550
  extendCurrentPath(p);
10498
- } else if (hoverVertexInfo && pencilPoints.length > 2) {
10551
+ } else if (polygonMode() && hoverVertexInfo && pencilPoints.length > 2) {
10499
10552
  // close path
10500
10553
  p = hoverVertexInfo.point;
10501
10554
  appendVertex$1(hit.getHitTarget(), p);
10502
10555
  extendCurrentPath(p);
10503
10556
  pencilPoints = null; // stop drawing
10504
- } else if (pointExceedsTolerance(xy, pencilPoints, 1.5)) {
10505
- xy = pencilPoints.pop();
10506
- p = pixToDataCoords(xy[0], xy[1]);
10507
- pencilPoints = [xy];
10557
+ } else if (pencilPoints.length >= 2 && pointExceedsTolerance(xy, pencilPoints, 1.4)) {
10558
+ xy2 = pencilPoints.pop();
10559
+ p = pixToDataCoords(xy2[0], xy2[1]);
10508
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];
10509
10565
  } else {
10510
10566
  // skip this point, update the hover line
10511
10567
  pencilPoints.push(xy);
@@ -10624,9 +10680,11 @@
10624
10680
  }
10625
10681
 
10626
10682
  function updateCursor() {
10627
- gui.container.findChild('.map-layers').classed('drawing', active());
10683
+ var el = gui.container.findChild('.map-layers');
10684
+ el.classed('draw-tool', active());
10628
10685
  var useArrow = hoverVertexInfo && !hoverVertexInfo.extendable && !pathDrawing();
10629
- gui.container.findChild('.map-layers').classed('dragging', useArrow);
10686
+ el.classed('dragging', useArrow);
10687
+ el.classed('drawing', pathDrawing());
10630
10688
  }
10631
10689
 
10632
10690
  function vertexIsEndpoint(info, target) {
@@ -12063,7 +12121,7 @@
12063
12121
 
12064
12122
  new SimpleButton(popup.findChild('.select-btn')).on('click', function() {
12065
12123
  var coords = box.getDataCoords();
12066
- if (!coords) return;
12124
+ if (!coords || noData()) return;
12067
12125
  gui.enterMode('selection_tool');
12068
12126
  gui.interaction.setMode('selection');
12069
12127
  // kludge to pass bbox to the selection tool
@@ -12072,6 +12130,10 @@
12072
12130
  });
12073
12131
  });
12074
12132
 
12133
+ function noData() {
12134
+ return !gui.model.getActiveLayer();
12135
+ }
12136
+
12075
12137
  new SimpleButton(popup.findChild('.clip-btn')).on('click', function() {
12076
12138
  runCommand('-clip bbox=' + box.getDataCoords().join(','));
12077
12139
  });
@@ -12129,7 +12191,9 @@
12129
12191
 
12130
12192
  function showCoords() {
12131
12193
  El(infoBtn.node()).addClass('selected-btn');
12132
- coords.text(box.getDataCoords().join(','));
12194
+ var bbox = box.getDataCoords();
12195
+ var rounded = internal.getRoundedCoords(bbox, internal.getBoundsPrecisionForDisplay(bbox));
12196
+ coords.text(rounded.join(','));
12133
12197
  coords.show();
12134
12198
  GUI.selectElement(coords.node());
12135
12199
  }
@@ -12262,373 +12326,107 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12262
12326
  }
12263
12327
  }
12264
12328
 
12265
- function loadScript(url, cb) {
12266
- var script = document.createElement('script');
12267
- script.onload = cb;
12268
- script.src = url;
12269
- document.head.appendChild(script);
12270
- }
12329
+ utils$1.inherit(MshpMap, EventDispatcher);
12271
12330
 
12272
- function loadStylesheet(url) {
12273
- var el = document.createElement('link');
12274
- el.rel = 'stylesheet';
12275
- el.type = 'text/css';
12276
- el.media = 'screen';
12277
- el.href = url;
12278
- document.head.appendChild(el);
12279
- }
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;
12280
12344
 
12281
- function Basemap(gui, ext) {
12282
- var menu = gui.container.findChild('.basemap-options');
12283
- // var hideBtn = new SimpleButton(menu.findChild('.hide-btn'));
12284
- var fadeBtn = new SimpleButton(menu.findChild('.fade-btn'));
12285
- var closeBtn = new SimpleButton(menu.findChild('.close2-btn'));
12286
- var clearBtn = new SimpleButton(menu.findChild('.clear-btn'));
12287
- var list = menu.findChild('.basemap-styles');
12288
- var container = gui.container.findChild('.basemap-container');
12289
- var basemapBtn = gui.container.findChild('.basemap-btn');
12290
- var basemapNote = gui.container.findChild('.basemap-note');
12291
- var basemapWarning = gui.container.findChild('.basemap-warning');
12292
- var mapEl = gui.container.findChild('.basemap');
12293
- var extentNote = El('div').addClass('basemap-prompt').appendTo(container).hide();
12294
- var params = window.mapboxParams;
12295
- var map;
12296
- var activeStyle;
12297
- var loading = false;
12298
- var faded = false;
12345
+ _mouse.disable(); // wait for gui.focus() to activate mouse events
12299
12346
 
12300
- if (params) {
12301
- // TODO: check page URL for compatibility with mapbox key
12302
- init();
12303
- } else {
12304
- basemapBtn.hide();
12305
- }
12347
+ model.on('select', function(e) {
12348
+ _intersectionLyr = null;
12349
+ _overlayLyr = null;
12350
+ });
12306
12351
 
12307
- function init() {
12308
- gui.addMode('basemap', turnOn, turnOff, basemapBtn);
12352
+ gui.on('active', function() {
12353
+ _mouse.enable();
12354
+ });
12309
12355
 
12310
- closeBtn.on('click', function() {
12311
- gui.clearMode();
12312
- turnOff();
12313
- });
12356
+ gui.on('inactive', function() {
12357
+ _mouse.disable();
12358
+ });
12314
12359
 
12315
- clearBtn.on('click', function() {
12316
- if (activeStyle) {
12317
- turnOffBasemap();
12318
- updateButtons();
12319
- closeMenu();
12320
- }
12321
- });
12360
+ gui.on('map-needs-refresh', function() {
12361
+ drawLayers();
12362
+ });
12322
12363
 
12323
- fadeBtn.on('click', function() {
12324
- if (faded) {
12325
- mapEl.css('opacity', 1);
12326
- faded = false;
12327
- fadeBtn.text('Fade');
12328
- } else if (activeStyle) {
12329
- mapEl.css('opacity', 0.35);
12330
- faded = true;
12331
- fadeBtn.text('Unfade');
12332
- }
12333
- });
12364
+ model.on('update', onUpdate);
12334
12365
 
12335
- gui.on('map_click', function() {
12336
- // close menu if user click on the map
12337
- if (gui.getMode() == 'basemap') gui.clearMode();
12338
- });
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
+ });
12339
12371
 
12340
- params.styles.forEach(function(style) {
12341
- var btn = El('div').html(`<div class="basemap-style-btn"><img src="${style.icon}"></img></div><div class="basemap-style-label">${style.name}</div>`);
12342
- btn.findChild('.basemap-style-btn').on('click', function() {
12343
- if (style == activeStyle) {
12344
- turnOffBasemap();
12345
- } else {
12346
- showBasemap(style);
12347
- }
12348
- updateButtons();
12349
- closeMenu();
12350
- });
12351
- btn.appendTo(list);
12352
- });
12353
- }
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;
12381
+ }
12382
+ // TODO: try to avoid redrawing layers twice (in some situations)
12383
+ drawLayers();
12384
+ };
12354
12385
 
12355
- // close and turn off mode
12356
- function closeMenu() {
12357
- setTimeout(function() {
12358
- gui.clearMode();
12359
- }, 200);
12360
- }
12386
+ this.setLayerPinning = function(target, pinned) {
12387
+ target.layer.pinned = !!pinned;
12388
+ };
12361
12389
 
12362
- function turnOffBasemap() {
12363
- activeStyle = null;
12364
- gui.map.setDisplayCRS(null);
12365
- refresh();
12366
- }
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
+ };
12367
12395
 
12368
- function showBasemap(style) {
12369
- activeStyle = style;
12370
- // TODO: consider enabling dark basemap mode
12371
- // Make sure that the selected layer style gets updated in gui-map.js
12372
- // gui.state.dark_basemap = style && style.dark || false;
12373
- if (map) {
12374
- map.setStyle(style.url);
12375
- refresh();
12376
- } else if (prepareMapView()) {
12377
- initMap();
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;
12378
12403
  }
12379
- }
12380
-
12381
- function updateButtons() {
12382
- list.findChildren('.basemap-style-btn').forEach(function(el, i) {
12383
- el.classed('active', params.styles[i] == activeStyle);
12384
- });
12385
- }
12386
-
12387
- function turnOn() {
12388
- // TODO: show basemap even if there is no data
12389
- var activeLyr = gui.model.getActiveLayer(); // may be null
12390
- var info = getDatasetCrsInfo(activeLyr?.dataset); // defaults to wgs84
12391
- var dataCRS = info.crs || null;
12392
- var displayCRS = gui.map.getDisplayCRS();
12393
- var warning;
12404
+ return formatCoordsForDisplay(p1, p2);
12405
+ };
12394
12406
 
12395
- if (!dataCRS || !displayCRS || !crsIsUsable(displayCRS) || !crsIsUsable(dataCRS)) {
12396
- warning = 'This data is incompatible with the basemaps.';
12397
- if (!internal.layerHasGeometry(activeLyr.layer)) {
12398
- warning += ' Reason: layer is missing geographic data';
12399
- } else if (!dataCRS) {
12400
- warning += ' Reason: unknown projection.';
12401
- }
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
+ // };
12402
12416
 
12403
- basemapWarning.html(warning).show();
12404
- basemapNote.hide();
12405
- } else {
12406
- basemapNote.show();
12417
+ this.getDisplayCRS = function() {
12418
+ if (!_activeLyr) {
12419
+ return _dynamicCRS || internal.parseCrsString('wgs84');
12407
12420
  }
12408
- menu.show();
12409
- }
12410
-
12411
- function turnOff() {
12412
- basemapWarning.hide();
12413
- basemapNote.hide();
12414
- menu.hide();
12415
- }
12416
-
12417
- function enabled() {
12418
- return !!(mapEl && params);
12419
- }
12420
-
12421
- function show() {
12422
- gui.container.addClass('basemap-on');
12423
- mapEl.node().style.display = 'block';
12424
- }
12425
-
12426
- function hide() {
12427
- gui.container.removeClass('basemap-on');
12428
- mapEl.node().style.display = 'none';
12429
- }
12430
-
12431
- function getLonLatBounds() {
12432
- var bbox = ext.getBounds().toArray();
12433
- var bbox2 = fromWebMercator(bbox[0], bbox[1])
12434
- .concat(fromWebMercator(bbox[2], bbox[3]));
12435
- return bbox2;
12436
- }
12437
-
12438
- function initMap() {
12439
- if (!enabled() || map || loading) return;
12440
- loading = true;
12441
- loadStylesheet(params.css);
12442
- loadScript(params.js, function() {
12443
- map = new window.mapboxgl.Map({
12444
- accessToken: params.key,
12445
- logoPosition: 'bottom-left',
12446
- container: mapEl.node(),
12447
- style: activeStyle.url,
12448
- bounds: getLonLatBounds(),
12449
- doubleClickZoom: false,
12450
- dragPan: false,
12451
- dragRotate: false,
12452
- scrollZoom: false,
12453
- interactive: false,
12454
- keyboard: false,
12455
- maxPitch: 0,
12456
- renderWorldCopies: true // false // false prevents panning off the map
12457
- });
12458
- map.on('load', function() {
12459
- loading = false;
12460
- refresh();
12461
- });
12462
- });
12463
- }
12464
-
12465
- // @bbox: latlon bounding box of current map extent
12466
- function checkBounds(bbox) {
12467
- var mpp = ext.getBounds().width() / ext.width();
12468
- var z = scaleToZoom(mpp);
12469
- var msg;
12470
- if (bbox[1] >= -85 && bbox[3] <= 85 && z <= 20) {
12471
- extentNote.hide();
12472
- return true;
12473
- }
12474
- if (z > 20) {
12475
- msg = 'zoom out';
12476
- } else if (bbox[1] > 0) {
12477
- msg = 'pan south';
12478
- } else if (bbox[3] < 0) {
12479
- msg = 'pan north';
12480
- } else {
12481
- msg = msg = 'zoom in';
12482
- }
12483
- extentNote.html(msg + ' to see the basemap').show();
12484
- return false;
12485
- }
12486
-
12487
- function crsIsUsable(crs) {
12488
- if (!crs) return false;
12489
- if (!internal.isInvertibleCRS(crs)) return false;
12490
- return true;
12491
- }
12492
-
12493
- function prepareMapView() {
12494
- var crs = gui.map.getDisplayCRS();
12495
- if (!crs) return false;
12496
- if (!internal.isWebMercator(crs)) {
12497
- gui.map.setDisplayCRS(internal.parseCrsString('webmercator'));
12498
- }
12499
- return true;
12500
- }
12501
-
12502
- function refresh() {
12503
- var crs = gui.map.getDisplayCRS();
12504
- var off = !crs || !enabled() || !map || loading || !activeStyle;
12505
- fadeBtn.active(!off);
12506
- clearBtn.active(!off);
12507
- if (off) {
12508
- hide();
12509
- extentNote.hide();
12510
- return;
12511
- }
12512
-
12513
- prepareMapView();
12514
- var bbox = getLonLatBounds();
12515
- if (!checkBounds(bbox)) {
12516
- // map does not display outside these bounds
12517
- hide();
12518
- } else {
12519
- show();
12520
- map.resize();
12521
- map.fitBounds(bbox, {animate: false});
12522
- }
12523
- }
12524
-
12525
- return {refresh: refresh}; // called by map when extent changes
12526
- }
12527
-
12528
- utils$1.inherit(MshpMap, EventDispatcher);
12529
-
12530
- function MshpMap(gui) {
12531
- var opts = gui.options,
12532
- el = gui.container.findChild('.map-layers').node(),
12533
- position = new ElementPosition(el),
12534
- model = gui.model,
12535
- map = this,
12536
- _mouse = new MouseArea(el, position),
12537
- _ext = new MapExtent(position),
12538
- _nav = new MapNav(gui, _ext, _mouse),
12539
- _visibleLayers = [], // cached visible map layers
12540
- _hit,
12541
- _basemap,
12542
- _intersectionLyr, _activeLyr, _overlayLyr,
12543
- _renderer, _dynamicCRS;
12544
-
12545
- _basemap = new Basemap(gui, _ext);
12546
-
12547
- _mouse.disable(); // wait for gui.focus() to activate mouse events
12548
-
12549
- model.on('select', function(e) {
12550
- _intersectionLyr = null;
12551
- _overlayLyr = null;
12552
- });
12553
-
12554
- gui.on('active', function() {
12555
- _mouse.enable();
12556
- });
12557
-
12558
- gui.on('inactive', function() {
12559
- _mouse.disable();
12560
- });
12561
-
12562
- gui.on('map-needs-refresh', function() {
12563
- drawLayers();
12564
- });
12565
-
12566
- model.on('update', onUpdate);
12567
-
12568
- document.addEventListener('visibilitychange', function(e) {
12569
- // refresh map when browser tab is re-activated (Chrome on mac has been
12570
- // blanking the canvas after several other tabs are visited)
12571
- if (document.visibilityState == 'visible') drawLayers();
12572
- });
12573
-
12574
- // Update display of segment intersections
12575
- this.setIntersectionLayer = function(lyr, dataset) {
12576
- if (lyr == _intersectionLyr) return; // no change
12577
- if (lyr) {
12578
- enhanceLayerForDisplay(lyr, dataset, getDisplayOptions());
12579
- _intersectionLyr = lyr;
12580
- _intersectionLyr.gui.style = getIntersectionStyle(_intersectionLyr.gui.displayLayer, getGlobalStyleOptions());
12581
- } else {
12582
- _intersectionLyr = null;
12583
- }
12584
- // TODO: try to avoid redrawing layers twice (in some situations)
12585
- drawLayers();
12586
- };
12587
-
12588
- this.setLayerPinning = function(target, pinned) {
12589
- target.layer.pinned = !!pinned;
12590
- };
12591
-
12592
- this.translatePixelCoords = function(x, y) {
12593
- var p = _ext.translatePixelCoords(x, y);
12594
- if (!_dynamicCRS) return p;
12595
- return internal.toLngLat(p, _dynamicCRS);
12596
- };
12597
-
12598
- this.pixelCoordsToDisplayCoords = function(x, y) {
12599
- var p1 = _ext.translatePixelCoords(x, y);
12600
- var p2 = _ext.translatePixelCoords(x+1, y+1);
12601
- var crs = this.getDisplayCRS();
12602
- if (crs) {
12603
- p1 = internal.toLngLat(p1, crs) || p1;
12604
- p2 = internal.toLngLat(p2, crs) || p2;
12605
- }
12606
- return formatCoordsForDisplay(p1, p2);
12607
- };
12608
-
12609
- // this.getCenterLngLat = function() {
12610
- // var bounds = _ext.getBounds();
12611
- // var crs = this.getDisplayCRS();
12612
- // // TODO: handle case where active layer is a frame layer
12613
- // if (!bounds.hasBounds() || !crs) {
12614
- // return null;
12615
- // }
12616
- // return internal.toLngLat([bounds.centerX(), bounds.centerY()], crs);
12617
- // };
12618
-
12619
- this.getDisplayCRS = function() {
12620
- if (!_activeLyr) {
12621
- return _dynamicCRS || internal.parseCrsString('wgs84');
12622
- }
12623
- if (!_activeLyr.gui.geographic) {
12624
- return null;
12625
- }
12626
- if (_activeLyr.gui.dynamic_crs) {
12627
- return _activeLyr.gui.dynamic_crs;
12628
- }
12629
- var info = getDatasetCrsInfo(_activeLyr.gui.source.dataset);
12630
- return info.crs || null;
12631
- };
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
+ };
12632
12430
 
12633
12431
  this.getExtent = function() {return _ext;};
12634
12432
  this.getMouse = function() {return _mouse;};
@@ -12694,7 +12492,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12694
12492
  }
12695
12493
 
12696
12494
  _ext.on('change', function(e) {
12697
- if (_basemap) _basemap.refresh(); // keep basemap synced up (if enabled)
12495
+ gui?.basemap.refresh(); // keep basemap synced up (if enabled)
12698
12496
  drawLayers(e.redraw ? '' : 'nav');
12699
12497
  });
12700
12498
 
@@ -12773,7 +12571,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12773
12571
 
12774
12572
  if (needReset) {
12775
12573
  _ext.reset();
12776
- if (_basemap) _basemap.refresh();
12574
+ gui?.basemap.refresh();
12777
12575
  }
12778
12576
  drawLayers();
12779
12577
  map.dispatchEvent('updated');
@@ -13163,6 +12961,293 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13163
12961
  return retn;
13164
12962
  }
13165
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
+
13166
13251
  function GuiInstance(container, opts) {
13167
13252
  var gui = new ModeSwitcher();
13168
13253
  opts = utils$1.extend({
@@ -13180,6 +13265,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13180
13265
  gui.model = new Model(gui);
13181
13266
  gui.keyboard = new KeyboardEvents(gui);
13182
13267
  gui.buttons = new SidebarButtons(gui);
13268
+ gui.basemap = new Basemap(gui);
13183
13269
  gui.session = new SessionHistory(gui);
13184
13270
  gui.contextMenu = new ContextMenu();
13185
13271
  gui.undo = new Undo(gui);
@@ -13345,6 +13431,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13345
13431
  if (dataLoaded) return;
13346
13432
  dataLoaded = true;
13347
13433
  gui.buttons.show();
13434
+ gui.basemap.show();
13348
13435
  El('#mode-buttons').show();
13349
13436
  El('#splash-buttons').hide();
13350
13437
  El('body').addClass('map-view');