mapshaper 0.6.112 → 0.6.113

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.6.112",
3
+ "version": "0.6.113",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -52,6 +52,7 @@
52
52
  "delaunator": "^5.0.0",
53
53
  "fflate": "0.8.2",
54
54
  "flatbush": "^3.2.1",
55
+ "geographiclib-geodesic": "^2.2.0",
55
56
  "geokdbush": "^1.1.0",
56
57
  "iconv-lite": "^0.6.3",
57
58
  "idb-keyval": "^6.2.0",
@@ -60,7 +61,7 @@
60
61
  "msgpackr": "^1.10.1",
61
62
  "opn": "^5.3.0",
62
63
  "rw": "~1.3.3",
63
- "sync-request": "5.0.0",
64
+ "sync-request": "6.1.0",
64
65
  "tinyqueue": "^2.0.3"
65
66
  },
66
67
  "devDependencies": {
package/www/basemap.js CHANGED
@@ -1,14 +1,16 @@
1
1
  window.mapboxParams = {
2
- js: 'https://api.mapbox.com/mapbox-gl-js/v2.11.1/mapbox-gl.js',
3
- css: 'https://api.mapbox.com/mapbox-gl-js/v2.11.1/mapbox-gl.css',
2
+ // js: 'https://api.mapbox.com/mapbox-gl-js/v2.11.1/mapbox-gl.js',
3
+ // css: 'https://api.mapbox.com/mapbox-gl-js/v2.11.1/mapbox-gl.css',
4
+ js: 'https://api.mapbox.com/mapbox-gl-js/v3.15.0/mapbox-gl.js',
5
+ css: 'https://api.mapbox.com/mapbox-gl-js/v3.15.0/mapbox-gl.css',
4
6
  production_key: 'pk.eyJ1IjoiZ3JhbW1hdGEiLCJhIjoiY21jZ2RlbGk1MGhvNzJrcHV2dTYzZWE2byJ9.btkRnRrPW75c0AlQ5lH2cg',
5
7
  localhost_key: 'pk.eyJ1IjoiZ3JhbW1hdGEiLCJhIjoiY21jZ2Rna2hrMGo2ZjJqcHFxMHAycDlyMSJ9.q8FchhB-NAN_FGBHN0AJeg',
6
8
  styles: [{
7
- name: 'Map',
9
+ name: 'Reference map',
8
10
  icon: 'images/thumb-map.jpg',
9
11
  url: 'mapbox://styles/grammata/cl0ymri0f000214lc1mgtweqk'
10
12
  }, {
11
- name: 'Satellite',
13
+ name: 'Satellite image',
12
14
  icon: 'images/thumb-satellite.jpg',
13
15
  dark: true,
14
16
  url: 'mapbox://styles/grammata/cl0ymvkkl004115mlgf5o442n'
package/www/elements.css CHANGED
@@ -147,7 +147,7 @@ div.tip {
147
147
  .inline-btn {
148
148
  margin: 0;
149
149
  border: 1px solid #aaa;
150
- padding: 1px 4px 3px 3px;
150
+ padding: 2px 4px 3px 3px;
151
151
  }
152
152
 
153
153
  .text-btn {
Binary file
package/www/index.html CHANGED
@@ -164,7 +164,29 @@ precision=0.001. Click to see all options.</div></div></div></a>
164
164
  </div>
165
165
 
166
166
  <div class="display-options main-area popup-dialog">
167
- <div class="info-box">
167
+
168
+ <div class="info-box add-basemap-menu">
169
+ <!-- <div class="close2-btn"></div> -->
170
+ <h3>Add basemap layer</h3>
171
+ Name (optional)
172
+ <input type="text" class="add-layer-name" placeholder="">
173
+ <div class="menu-section">
174
+ Option 1: Mapbox style URL and key
175
+ <input type="text" class="add-mapbox-url" placeholder="mapbox://...">
176
+ <input type="text" class="add-mapbox-key" placeholder="key">
177
+ </div>
178
+ <div class="menu-section">
179
+ Option 2: raster tile URL template
180
+ <input type="text" class="add-template-url" placeholder="https://...">
181
+ <div><input type="checkbox" class="tms">TMS (reversed y-index)</div>
182
+ </div>
183
+ <div>
184
+ <div class="add-layer-btn btn dialog-btn">Add</div>
185
+ <div class="add-cancel-btn btn dialog-btn">Cancel</div>
186
+ </div>
187
+ </div>
188
+
189
+ <div class="info-box display-main-options">
168
190
  <div class="close2-btn"></div>
169
191
  <h3>Display options</h3>
170
192
 
@@ -174,14 +196,11 @@ precision=0.001. Click to see all options.</div></div></div></a>
174
196
  <div class="basemap-opts">
175
197
  <h4>Basemaps</h4>
176
198
  <p class="basemap-warning"></p>
177
- <div class="basemap-styles basemap-buttons"></div>
178
- <p class="basemap-note"></p>
179
- <div>
180
- <!-- <div class="close-btn btn dialog-btn">Close</div> -->
181
- <div class="clear-btn btn dialog-btn disabled">Clear</div>
182
- <div class="fade-btn btn dialog-btn disabled">Fade</div>
183
- <!-- <div class="hide-btn btn dialog-btn disabled">Hide</div> -->
199
+ <!-- <div class="basemap-styles basemap-buttons"></div> -->
200
+ <div class="added-basemaps">
184
201
  </div>
202
+ <p class="basemap-note"></p>
203
+ <div class="add-basemap"><span class="add-btn inline-btn btn">Add</span> a basemap</div>
185
204
  </div>
186
205
  </div>
187
206
  </div>
@@ -655,9 +655,14 @@
655
655
  },
656
656
 
657
657
  show: function(css) {
658
- var tag = this.el && this.el.tagName;
658
+ // var tag = this.el && this.el.tagName;
659
659
  if (!this.visible()) {
660
- this.css('display', tag == 'SPAN' ? 'inline-block' : 'block');
660
+ // don't assume 'display:block'
661
+ this.el?.style.removeProperty('display');
662
+ if (this.computedStyle().display == 'none') {
663
+ this.css('display', 'block');
664
+ }
665
+ // this.css('display', tag == 'SPAN' ? 'inline-block' : 'block');
661
666
  this._hidden = false;
662
667
  }
663
668
  return this;
@@ -666,13 +671,15 @@
666
671
  html: function(html) {
667
672
  if (arguments.length == 0) {
668
673
  return this.el.innerHTML;
669
- } else {
670
- this.el.innerHTML = html;
671
- return this;
672
674
  }
675
+ this.el.innerHTML = html;
676
+ return this;
673
677
  },
674
678
 
675
679
  text: function(str) {
680
+ if (arguments.length == 0) {
681
+ return this.el.innerText;
682
+ }
676
683
  this.html(utils$1.htmlEscape(str));
677
684
  return this;
678
685
  },
@@ -1936,6 +1943,7 @@
1936
1943
  .on('drop', ondrop)
1937
1944
  .on('paste', onpaste);
1938
1945
  area.node().addEventListener('paste', onpaste);
1946
+ // TODO: use same function for drop and paste
1939
1947
  function ondrop(e) {
1940
1948
  var files = e.dataTransfer.files;
1941
1949
  var types = e.dataTransfer.types;
@@ -1946,7 +1954,7 @@
1946
1954
  cb(e.dataTransfer.getData('text/uri-list').split(','));
1947
1955
  } else if (types.includes('text/html')) {
1948
1956
  // drag-dropping a highlighted link may pull in a chunk of html
1949
- var urls = e.dataTransfer.getData('text/html').match(/https?:[^"']+/);
1957
+ var urls = pastedHtmlToUrls(e.dataTransfer.getData('text/html'));
1950
1958
  if (urls.length) {
1951
1959
  cb(urls);
1952
1960
  }
@@ -1955,7 +1963,7 @@
1955
1963
  function onpaste(e) {
1956
1964
  var types = Array.from(e.clipboardData.types || []).join(',');
1957
1965
  var items = Array.from(e.clipboardData.items || []);
1958
- var files;
1966
+ var files, str, urls;
1959
1967
  if (GUI.textIsSelected()) {
1960
1968
  // user is probably pasting text into an editable text field
1961
1969
  return;
@@ -1971,12 +1979,22 @@
1971
1979
  // formatted text can be available as both text/plain and text/html (e.g.
1972
1980
  // a JSON data object copied from a GitHub issue).
1973
1981
  //
1982
+
1983
+ // if html is present, it could be data (e.g. from Google Sheets) or a pasted link.
1984
+ // first we check for a link
1985
+ if (types.includes('text/html')) {
1986
+ urls = pastedHtmlToUrls(e.clipboardData.getData('text/html'));
1987
+ if (urls.length) {
1988
+ return cb(urls);
1989
+ }
1990
+ }
1974
1991
  if (types.includes('text/plain')) {
1975
1992
  // text from clipboard (supported by Chrome, FF, Safari)
1976
1993
  // TODO: handle FF case of string containing multiple file names.
1977
- var str = e.clipboardData.getData('text/plain');
1978
- if (isUrl(str)) {
1979
- return cb(str.split(','));
1994
+ str = e.clipboardData.getData('text/plain');
1995
+ urls = pastedTextToUrls(str);
1996
+ if (urls.length) {
1997
+ return cb(urls);
1980
1998
  }
1981
1999
  files = [pastedTextToFile(str)];
1982
2000
  } else {
@@ -1998,6 +2016,21 @@
1998
2016
  }
1999
2017
  }
2000
2018
 
2019
+ function pastedHtmlToUrls(html) {
2020
+ var hrefRegex = /href\s*=\s*["']([^"']+)["']/gi;
2021
+ var matches = html.matchAll(hrefRegex);
2022
+ var urls = Array.from(matches, match => match[1]);
2023
+ return urls;
2024
+ }
2025
+
2026
+ function pastedTextToUrls(str) {
2027
+ if (!looksLikeUrl(str)) return [];
2028
+ var regex = /https?:\/\/[^\s]+?(?=[\s,]|$)/g;
2029
+ var matches = str.matchAll(regex);
2030
+ var urls = Array.from(matches, match => match[0]);
2031
+ return urls;
2032
+ }
2033
+
2001
2034
  function pastedTextToFile(str) {
2002
2035
  var type = internal.guessInputContentType(str);
2003
2036
  var name;
@@ -2012,7 +2045,7 @@
2012
2045
  return new File([blob], name);
2013
2046
  }
2014
2047
 
2015
- function isUrl(str) {
2048
+ function looksLikeUrl(str) {
2016
2049
  return /^https?:\/\//.test(str);
2017
2050
  }
2018
2051
 
@@ -2340,7 +2373,7 @@
2340
2373
  function prepFilesForDownload(names) {
2341
2374
  var items = names.map(function(name) {
2342
2375
  var item = {name: name};
2343
- if (isUrl(name)) {
2376
+ if (looksLikeUrl(name)) {
2344
2377
  item.url = name;
2345
2378
  item.basename = GUI.getUrlFilename(name);
2346
2379
 
@@ -13548,6 +13581,12 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13548
13581
  });
13549
13582
  }
13550
13583
 
13584
+ var EMPTY_STYLE = {
13585
+ version: 8,
13586
+ sources: {},
13587
+ layers: []
13588
+ };
13589
+
13551
13590
  function loadScript(url, cb) {
13552
13591
  var script = document.createElement('script');
13553
13592
  script.onload = cb;
@@ -13565,60 +13604,107 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13565
13604
  }
13566
13605
 
13567
13606
  function Basemap(gui) {
13568
- var menu = gui.container.findChild('.display-options');
13569
- var fadeBtn = new SimpleButton(menu.findChild('.fade-btn'));
13570
- var clearBtn = new SimpleButton(menu.findChild('.clear-btn'));
13571
- var menuButtons = menu.findChild('.basemap-styles');
13607
+ var menuWrapper = gui.container.findChild('.display-options');
13608
+ var mainMenu = gui.container.findChild('.display-main-options');
13609
+ var addLayerMenu = gui.container.findChild('.add-basemap-menu').hide();
13610
+ var basemapList = gui.container.findChild('.added-basemaps');
13572
13611
  var overlayButtons = gui.container.findChild('.basemap-overlay-buttons');
13573
13612
  var container = gui.container.findChild('.basemap-container');
13574
13613
  var basemapNote = gui.container.findChild('.basemap-note');
13575
- var basemapWarning = gui.container.findChild('.basemap-warning');
13614
+ var addBasemap = gui.container.findChild('.add-basemap');
13615
+ var basemapWarning = gui.container.findChild('.basemap-warning').hide();
13576
13616
  var mapEl = gui.container.findChild('.basemap');
13577
13617
  var extentNote = El('div').addClass('basemap-prompt').appendTo(container).hide();
13578
13618
  var params = window.mapboxParams;
13579
13619
  var map;
13580
13620
  var activeStyle;
13581
13621
  var loading = false;
13582
- var faded = false;
13622
+ // var faded = false;
13623
+ // var fadeBtn, clearBtn; // not in use
13624
+ var addBtn, addLayerBtn, cancelBtn;
13625
+ var customStyles = [];
13583
13626
 
13584
13627
  if (params) {
13585
13628
  // TODO: check page URL for compatibility with mapbox key
13586
13629
  init();
13587
13630
  } else {
13588
- menu.findChild('.basemap-opts').hide();
13631
+ menuWrapper.findChild('.basemap-opts').hide();
13589
13632
  }
13590
13633
 
13591
13634
  function init() {
13592
13635
  gui.on('mode', function(e) {
13593
13636
  if (e.prev == 'display_options') {
13594
- basemapWarning.hide();
13595
- basemapNote.hide();
13637
+ // reset UI when leaving display options mode
13638
+ basemapWarning.hide();
13639
+ basemapNote.hide();
13640
+ // make sure secondary options menu gets closed
13641
+ mainMenu.show();
13642
+ addLayerMenu.hide();
13596
13643
  }
13597
13644
  if (e.name == 'display_options') {
13598
13645
  onUpdate();
13599
13646
  }
13600
13647
  });
13601
13648
 
13602
- clearBtn.on('click', function() {
13603
- if (activeStyle) {
13604
- turnOffBasemap();
13605
- updateButtons();
13606
- // closeMenu();
13607
- }
13649
+ customStyles = GUI.getSavedValue('custom_basemaps') || [];
13650
+ updateBasemapList();
13651
+
13652
+ addBtn = new SimpleButton(menuWrapper.findChild('.add-btn'));
13653
+ addBtn.on('click', function(e) {
13654
+ addLayerMenu.show();
13655
+ mainMenu.hide();
13608
13656
  });
13609
13657
 
13610
- fadeBtn.on('click', function() {
13611
- if (faded) {
13612
- mapEl.css('opacity', 1);
13613
- faded = false;
13614
- fadeBtn.text('Fade');
13615
- } else if (activeStyle) {
13616
- mapEl.css('opacity', 0.35);
13617
- faded = true;
13618
- fadeBtn.text('Unfade');
13619
- }
13658
+ addLayerBtn = new SimpleButton(menuWrapper.findChild('.add-layer-btn'));
13659
+ addLayerBtn.on('click', function(e) {
13660
+ var name = menuWrapper.findChild('.add-layer-name').el.value || 'Custom layer';
13661
+ var mapboxUrl = menuWrapper.findChild('.add-mapbox-url').el.value;
13662
+ var mapboxKey = menuWrapper.findChild('.add-mapbox-key')?.el?.value;
13663
+ var templateUrl = menuWrapper.findChild('.add-template-url').el.value;
13664
+ var style = {name};
13665
+
13666
+ if (mapboxUrl) {
13667
+ style.url = mapboxUrl;
13668
+ style.key = mapboxKey; // may be undefined
13669
+ style.type = 'mapbox';
13670
+ } else if (templateUrl) {
13671
+ style.url = templateUrl;
13672
+ style.type = menuWrapper.findChild('.tms').el.checked ? 'tms' : 'xyz';
13673
+ }
13674
+
13675
+ customStyles.push(style);
13676
+ updateBasemapList();
13677
+ showBasemap(style);
13678
+ addLayerMenu.hide();
13679
+ mainMenu.show();
13680
+ });
13681
+
13682
+ cancelBtn = new SimpleButton(menuWrapper.findChild('.add-cancel-btn'));
13683
+ cancelBtn.on('click', function() {
13684
+ addLayerMenu.hide();
13685
+ mainMenu.show();
13620
13686
  });
13621
13687
 
13688
+ // fadeBtn = new SimpleButton(menuWrapper.findChild('.fade-btn'));
13689
+ // clearBtn = new SimpleButton(menuWrapper.findChild('.clear-btn'));
13690
+ // clearBtn.on('click', function() {
13691
+ // if (activeStyle) {
13692
+ // turnOffBasemap();
13693
+ // }
13694
+ // });
13695
+
13696
+ // fadeBtn.on('click', function() {
13697
+ // if (faded) {
13698
+ // mapEl.css('opacity', 1);
13699
+ // faded = false;
13700
+ // fadeBtn.text('Fade');
13701
+ // } else if (activeStyle) {
13702
+ // mapEl.css('opacity', 0.35);
13703
+ // faded = true;
13704
+ // fadeBtn.text('Unfade');
13705
+ // }
13706
+ // });
13707
+
13622
13708
  gui.model.on('update', onUpdate);
13623
13709
 
13624
13710
  gui.on('map_click', function() {
@@ -13627,10 +13713,10 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13627
13713
  });
13628
13714
 
13629
13715
  params.styles.forEach(function(style) {
13630
- El('div')
13631
- .html(`<div class="basemap-style-btn"><img src="${style.icon}"></img></div><div class="basemap-style-label">${style.name}</div>`)
13632
- .appendTo(menuButtons)
13633
- .findChild('.basemap-style-btn').on('click', onClick);
13716
+ // El('div')
13717
+ // .html(`<div class="basemap-style-btn"><img src="${style.icon}"></img></div><div class="basemap-style-label">${style.name}</div>`)
13718
+ // .appendTo(menuButtons)
13719
+ // .findChild('.basemap-style-btn').on('click', onClick);
13634
13720
 
13635
13721
  El('div').addClass('basemap-overlay-btn basemap-style-btn')
13636
13722
  .html(`<img src="${style.icon}"></img>`).on('click', onClick)
@@ -13644,7 +13730,6 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13644
13730
  showBasemap(style);
13645
13731
  }
13646
13732
  updateButtons();
13647
- // closeMenu();
13648
13733
  }
13649
13734
  });
13650
13735
  }
@@ -13659,7 +13744,6 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13659
13744
  function turnOffBasemap() {
13660
13745
  activeStyle = null;
13661
13746
  gui.map.setDisplayCRS(null);
13662
- refresh();
13663
13747
  }
13664
13748
 
13665
13749
  function showBasemap(style) {
@@ -13668,20 +13752,98 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13668
13752
  // Make sure that the selected layer style gets updated in gui-map.js
13669
13753
  // gui.state.dark_basemap = style && style.dark || false;
13670
13754
  if (map) {
13671
- map.setStyle(style.url);
13755
+ setStyle(activeStyle);
13672
13756
  refresh();
13673
13757
  } else if (prepareMapView()) {
13674
13758
  initMap();
13675
13759
  }
13676
13760
  }
13677
13761
 
13678
- function updateButtons() {
13679
- menuButtons.findChildren('.basemap-style-btn').forEach(function(el, i) {
13680
- el.classed('active', params.styles[i] == activeStyle);
13762
+ function updateBasemapList() {
13763
+ var styles = params.styles.concat(customStyles);
13764
+ basemapList.empty();
13765
+ styles.forEach(function(style) {
13766
+ renderBasemapListItem(basemapList, style);
13767
+ });
13768
+ updateButtons();
13769
+ GUI.setSavedValue('custom_basemaps', customStyles);
13770
+ }
13771
+
13772
+ function isActiveStyle(style) {
13773
+ return style.url == activeStyle?.url;
13774
+ }
13775
+
13776
+ function renderBasemapListItem(parent, style) {
13777
+ var isCustomStyle = !!style.type;
13778
+ var el = El('div').html(`<div data-slug="${getStyleId(style)}" class="basemap-list-item"><img class="on-icon" src="images/eye3.png"><img class="off-icon" src="images/eye.png"> ${style.name} ${isCustomStyle ? '<img class="close-btn" draggable="false" src="images/close.png">' : ''}</div>`);
13779
+ el.appendTo(parent);
13780
+ el.findChild('.basemap-list-item').on('click', function() {
13781
+ if (isActiveStyle(style)) {
13782
+ turnOffBasemap();
13783
+ } else {
13784
+ showBasemap(style);
13785
+ }
13681
13786
  });
13787
+
13788
+ var closeBtn = el.findChild('.close-btn');
13789
+ closeBtn?.on('click', function(e) {
13790
+ e.stopPropagation();
13791
+ customStyles = customStyles.filter(function(o) {
13792
+ return o.url != style.url;
13793
+ });
13794
+ updateBasemapList();
13795
+ if (isActiveStyle(style)) {
13796
+ turnOffBasemap();
13797
+ }
13798
+ });
13799
+ el.appendTo(parent);
13800
+ }
13801
+
13802
+ function getStyleId(style) {
13803
+ return ((style.name || '') + style.url).replace(/[^a-z0-9_]+/ig, '_');
13804
+ }
13805
+
13806
+ function setStyle(style) {
13807
+ // update mapbox access token (user-defined styles may have a different key)
13808
+ window.mapboxgl.accessToken = style.key || (window.location.hostname == 'localhost' ?
13809
+ params.localhost_key : params.production_key) || params.key;
13810
+
13811
+ if (style.type == 'mapbox' || !style.type) {
13812
+ map.setStyle(style.url);
13813
+ } else if (style.type == 'xyz' || style.type == 'tms') {
13814
+ map.setStyle({
13815
+ 'version': 8,
13816
+ 'sources': {
13817
+ 'raster-tiles': {
13818
+ 'type': 'raster',
13819
+ 'tiles': [style.url],
13820
+ 'scheme': style.type, // xyz or tms
13821
+ 'tileSize': 256, // style.url.includes('@2x') ? 512 : 256
13822
+ }
13823
+ },
13824
+ 'layers': [
13825
+ {
13826
+ 'id': getStyleId(style),
13827
+ 'type': 'raster',
13828
+ 'source': 'raster-tiles',
13829
+ 'minzoom': 0
13830
+ // 'maxzoom': 22
13831
+ }
13832
+ ]
13833
+ });
13834
+ } else {
13835
+ error$1('unsupported map style:', style);
13836
+ }
13837
+ }
13838
+
13839
+ function updateButtons() {
13682
13840
  overlayButtons.findChildren('.basemap-style-btn').forEach(function(el, i) {
13683
13841
  el.classed('active', params.styles[i] == activeStyle);
13684
13842
  });
13843
+
13844
+ menuWrapper.findChildren('.basemap-list-item').forEach(function(el, i) {
13845
+ el.classed('active', el.node().getAttribute('data-slug') == (activeStyle ? getStyleId(activeStyle) : ''));
13846
+ });
13685
13847
  }
13686
13848
 
13687
13849
  function onUpdate() {
@@ -13689,16 +13851,18 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13689
13851
  var info = getDatasetCrsInfo(activeLyr?.dataset); // defaults to wgs84
13690
13852
  var dataCRS = info.crs || null;
13691
13853
  var displayCRS = gui.map.getDisplayCRS();
13854
+ var basemapsNotAvailable = !dataCRS || !displayCRS || !crsIsUsable(displayCRS) || !crsIsUsable(dataCRS);
13692
13855
  var warning, note;
13693
13856
 
13694
-
13695
- if (!dataCRS || !displayCRS || !crsIsUsable(displayCRS) || !crsIsUsable(dataCRS)) {
13857
+ if (basemapsNotAvailable) {
13696
13858
  warning = 'This data is incompatible with the basemaps.';
13697
- if (!internal.layerHasGeometry(activeLyr.layer)) {
13859
+ if (activeLyr && !internal.layerHasGeometry(activeLyr.layer)) {
13698
13860
  warning += ' Reason: layer is missing geographic data';
13699
13861
  } else if (!dataCRS) {
13700
13862
  warning += ' Reason: unknown projection.';
13701
13863
  }
13864
+ basemapList.hide();
13865
+ addBasemap.hide();
13702
13866
  basemapWarning.html(warning).show();
13703
13867
  basemapNote.hide();
13704
13868
  overlayButtons.addClass('disabled');
@@ -13708,6 +13872,8 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13708
13872
  note = `Note: basemaps use the Mercator projection.`;
13709
13873
  basemapNote.text(note).show();
13710
13874
  overlayButtons.show();
13875
+ basemapList.show();
13876
+ addBasemap.show();
13711
13877
  overlayButtons.removeClass('disabled');
13712
13878
  }
13713
13879
  }
@@ -13735,17 +13901,17 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13735
13901
  }
13736
13902
 
13737
13903
  function initMap() {
13738
- var accessToken = (window.location.hostname == 'localhost' ?
13739
- params.localhost_key : params.production_key) || params.key;
13904
+ // var accessToken = (window.location.hostname == 'localhost' ?
13905
+ // params.localhost_key : params.production_key) || params.key;
13740
13906
  if (!enabled() || map || loading) return;
13741
13907
  loading = true;
13742
13908
  loadStylesheet(params.css);
13743
13909
  loadScript(params.js, function() {
13744
13910
  map = new window.mapboxgl.Map({
13745
- accessToken: accessToken,
13746
13911
  logoPosition: 'bottom-left',
13747
13912
  container: mapEl.node(),
13748
- style: activeStyle.url,
13913
+ // style: activeStyle.url,
13914
+ style: EMPTY_STYLE, // initializing with empty style to support custom styles
13749
13915
  bounds: getLonLatBounds(),
13750
13916
  doubleClickZoom: false,
13751
13917
  dragPan: false,
@@ -13754,8 +13920,10 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13754
13920
  interactive: false,
13755
13921
  keyboard: false,
13756
13922
  maxPitch: 0,
13923
+ projection: 'mercator', // prevent globe view when zoomed out
13757
13924
  renderWorldCopies: true // false // false prevents panning off the map
13758
13925
  });
13926
+ setStyle(activeStyle);
13759
13927
  map.on('load', function() {
13760
13928
  loading = false;
13761
13929
  refresh();
@@ -13804,8 +13972,11 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
13804
13972
  function refresh() {
13805
13973
  var off = !enabled() || !map || loading || !activeStyle ||
13806
13974
  !gui.map.getDisplayCRS(); // may be slow if getting bounds of many shapes
13807
- fadeBtn.active(!off);
13808
- clearBtn.active(!off);
13975
+ // fadeBtn.active(!off);
13976
+ // clearBtn.active(!off);
13977
+
13978
+ updateButtons();
13979
+
13809
13980
  if (off) {
13810
13981
  hide();
13811
13982
  extentNote.hide();