UNPKG

naksha-react-ui

Version:

Visualize grid heat map or layer map from elastic search or https://github.com/strandls/naksha

811 lines (702 loc) 28.5 kB
import IndiaBoundaries from '../common/india-boundaries.js' import Config from '../common/config.js' var mapboxgl = require('mapbox-gl') var GoogleMapsLoader = require('google-maps') var baseUrl = "http://" + get_host() + "/naksha/geoserver/" var workspace_name = 'biodiv' var thumbnailsUrl = baseUrl + "thumbnails/"; var current_selected_layer = null; var current_selected_style = null; var map_style = null; var active_layers = []; var layerNameToTitleMap = {}; var layerToStyleOptionsMap = {}; var initial_zoom = null; var map = null; // var gmap = null; function initMap() { var india_center = {lat: 25, lng: 77}; var zoom = 3; // var gZoom = zoom + 1; // Google zoom levels are one higher than mapboxgl initMapboxglMap(india_center, zoom); // initGoogleMap(india_center, gZoom); // SyncGoogleAndMapboxglMaps(map, gmap); populateLayerPanel(); } function initGoogleMap(center, zoom) { GoogleMapsLoader.load(function(google) { new google.maps.Map(document.getElementById('gmap'), { zoom: zoom, center: center }); }) } function initMapboxglMap(center, zoom) { mapboxgl.accessToken = Config.mapboxgl_access_token; map = new mapboxgl.Map({ container: 'map', center: [center.lng, center.lat], zoom: zoom, style: 'mapbox://styles/mapbox/basic-v9' //india_boundary }); map.on('load', function() { IndiaBoundaries(map) }) map.addControl(new mapboxgl.NavigationControl()); addBaseLayerSelector(map); console.log(map.style); map.on('click', function (e) { showClickedFeature(e); }); } function addBaseLayerSelector() { var base_layers = [ { name: 'Mapbox Basic', type: 'mapbox', id: 'basic' }, { name: 'Mapbox Streets', type: 'mapbox', id: 'streets' }, { name: 'Mapbox Satellite', type: 'mapbox', id: 'satellite' } ]; var html = "<select class='base-layer-selector' onchange='changeBaseLayer(this)'>"; for (var i = 0; i < base_layers.length; i++) { var layer = base_layers[i]; html += "<option value='" + layer.id + "' type='" + layer.type + "'>" + layer.name + "</option>"; } html += "</select>"; //document.getElementById('map').innerHTML = html + document.getElementById('map').innerHTML; document.getElementById('map').insertAdjacentHTML('afterend', html) } function changeBaseLayer(baseLayerSelector) { var option = baseLayerSelector.options[baseLayerSelector.selectedIndex]; var type = option.getAttribute('type'); var layerId = option.getAttribute('value'); var addedSources = map.getStyle().sources; // remove the mapbox source delete addedSources.mapbox; // remove the layers added by mapbox. Now we only have layers that were added by user var addedLayers = map.getStyle().layers.filter(layer => layer.source !== 'mapbox' && layer.source !== 'world' && layer.id !== 'background'); if (type === 'mapbox'){ // user has switched to other mapbox layer // change the mapbox style. This will remove all the layers that were added. map.setStyle('mapbox://styles/mapbox/' + layerId + '-v9') } map.on('style.load', function() { // add all sources back Object.keys(addedSources).forEach(function(source) { map.addSource(source, addedSources[source]); }); // add all layers back addedLayers.forEach(function(layer) { map.addLayer(layer); }); }); } function SyncGoogleAndMapboxglMaps(map, gmap){ map.on('zoomstart', function(){ set_initial_zoom(map); }) map.on('zoom', function(){ syncMaps(map, gmap); }) map.on('move', function(){ sync_map_move(map, gmap); }) } function get_host(){ return 'localhost:8080'; } function getWorkspace() { return workspace_name; } function get_map_style(){ return map_style; } function set_map_style(style){ map_style = style; } // style_name should be combination of layer name and the attribute // on which the styling is needed function getStyle(style_name){ var style_file_url = baseUrl + "styles/" + style_name + ".json" var style = httpGetAsync(style_file_url); return JSON.parse(style); } // gets all available styles for a layer function getAvailableStyles(layer){ var styleFileUrl = baseUrl + "layers/" + layer + "/styles" var styles = httpGetAsync(styleFileUrl); var stylesJson = JSON.parse(styles); return stylesJson; } function httpGetAsync(theUrl, isXML=false) { // var xmlHttp = createCORSRequest("GET", theUrl) var xmlHttp = new XMLHttpRequest(); // below code is used in case of async requests, // which is the correct way going forward /*xmlHttp.onreadystatechange = function() { console.log(xmlHttp.readyState); if (xmlHttp.readyState == 4){ callback(xmlHttp.responseText) // return xmlHttp.responseText; } }*/ xmlHttp.open("GET", theUrl, false); // true for asynchronous xmlHttp.send(); if (isXML) return xmlHttp.responseXML; else return xmlHttp.responseText; } function getAvailableLayers(){ // var url = baseUrl + workspace_name + '/ows?SERVICE=WFS&REQUEST=GetCapabilities'; var url = baseUrl + 'layers/' + workspace_name + '/wfs'; console.log(url); var layers = []; var isXML = true; var response = httpGetAsync(url, isXML); var featureTypeList = response.getElementsByTagName('FeatureTypeList')[0]; for (var i = 0; i < featureTypeList.childNodes.length; i++){ var feature = featureTypeList.childNodes[i]; // console.log(feature) var layer_info = getLayerInfo_WMS_3(feature); if (layer_info !== undefined){ layers.push(layer_info); } } return layers // extract list of all available layers // var all_layers = layers_json.featureTypes.featureType.map(feature => feature.name); // return all_layers } function getLayerInfo_WMS_3(layerElement) { var name; var title; var bbox; var abstract; var keywords = []; var styles = []; var childNodes = layerElement.childNodes; var occurrence = getWorkspace() + ":occurrence"; for (var i=0; i<childNodes.length; i++) { if (childNodes[i].nodeName === "Name"){ if (childNodes[i].childNodes[0] === undefined) return; name = childNodes[i].childNodes[0].nodeValue.split(':')[1]; if (name === occurrence) return; }else if (childNodes[i].nodeName === "Title"){ if (childNodes[i].childNodes[0] === undefined || childNodes[i].childNodes[0] === null) return; title = childNodes[i].childNodes[0].nodeValue; }else if (childNodes[i].nodeName === "Abstract") { var abstract_node = childNodes[i].childNodes[0]; abstract = (abstract_node !== undefined)?abstract_node.nodeValue:''; } else if (childNodes[i].nodeName.endsWith("BoundingBox")){ bbox = getLatLonBBoxString(childNodes[i]); }/*else if (childNodes[i].nodeName.endsWith("Keywords")) keywords = getLayerKeywords(childNodes[i]);*/ // else if (childNodes[i].nodeName.endsWith("Style")) { // styles.push(getStyleInfo(childNodes[i])); // } } return {name: name, title: title, bbox: bbox, abstract: abstract, keywords: keywords, styles: styles}; } function getLatLonBBoxString(boundingBoxElement) { var lowerCorner = boundingBoxElement.childNodes[0].childNodes[0].nodeValue.split(' '); var upperCorner = boundingBoxElement.childNodes[1].childNodes[0].nodeValue.split(' '); var minx = lowerCorner[0]; var miny = lowerCorner[1]; var maxx = upperCorner[0]; var maxy = upperCorner[1]; var bboxArr = []; bboxArr.push(minx); bboxArr.push(miny); bboxArr.push(maxx); bboxArr.push(maxy); return bboxArr.join(','); } function layer_changed(){ var new_layer = document.getElementById('layer_input').value; if (new_layer === current_selected_layer) // nothing has changed return; // if new layer has been selected var styles = getAvailableStyles(new_layer); var style_selector = document.getElementById('styles'); var i = 0; while (i < style_selector.options.length){ style_selector.removeChild(style_selector.options[i]); } styles.forEach(function(style){ var option = document.createElement('option'); option.value = style; style_selector.appendChild(option); }); document.getElementById('style_input').value = styles[0]; style_changed() } function style_changed(){ var new_style = document.getElementById('style_input').value; if (new_style === current_selected_style) // nothing has changed return; // if new style has been selected var style = getStyle(new_style) update_map(style) } // async function update_map(style){ function update_map(style){ // add_background_to_style(style) map.setStyle(style); // await sleep(2000); // map.addLayer(new google.maps.TransitLayer()); } // Adds a background layer to the map style json function add_background_to_style(style){ style.sources["background"] = { "type": "raster", "tiles": ["http://a.tile.openstreetmap.org/{z}/{x}/{y}.png"], // "tiles": ["https://www.googleapis.com/tile/v1/tiles/{z}/{x}/{y}?key=AIzaSyCFan9y3E6XCb_3HE6kbbghfmRTmIgVJ9M"], "tileSize": 256 }; var first_layer_id = style.layers[0].id; style.layers.splice(first_layer_id, 0, { "id": "background", "type": "raster", "source": "background" }); } function getThemeNames(theme_type) { var by_themes = 'Biogeography///Abiotic///Demography///Species///Administrative Units///Land Use Land Cover///Conservation'; var by_geography = 'India///Nilgiri Biosphere Reserve///Western Ghats///BR Hills, Karnataka///Vembanad, Kerala///Bandipur, Karnataka'; if (theme_type === 1) return by_themes.split('///'); else if (theme_type === 2) return by_geography.split('///'); } function expand_layer_details(layer_id) { var expanded_div_id = layer_id + "_expanded"; var div = document.getElementById(expanded_div_id); div.classList.toggle('hide'); // On first time opening, fetch the thumbnail and show var thumb_div = div.getElementsByClassName('layer-thumb')[0]; console.log('id', expanded_div_id); console.log(baseUrl + layer_id + '_thumb.gif') console.log('children', thumb_div.children) if (thumb_div.children.length === 0){ console.log('thumb', layer_id); //uncomment following to get thumbnails through naksha thumb_div.insertAdjacentHTML('afterbegin', "<img src=" + thumbnailsUrl + layer_id + "_thumb.gif onerror='this.src=" + thumbnailsUrl + "no_preview.png' style='width: 100%;height: 100%;'></img>") //uncomment following to get thumbnails directly from geoserver //thumb_div.insertAdjacentHTML('afterbegin', "<img src=http://" + get_host() + "/geoserver/www/map_thumbnails/" + layer_id +"_thumb.gif></img>") } } function populateLayerPanel() { var layers = getAvailableLayers(); var nav_pane = document.getElementById('nav-all-layers'); var layer_pane_html = nav_pane.innerHTML; layers.forEach(function(layer){ layerNameToTitleMap[layer.name] = layer.title layer.thumbnail = "" layer_pane_html += "<div class='layer-div no-select'>" +"<div id="+layer.name+" class='layer-name-div no-select' onclick='expand_layer_details(this.id)'>" + layer.title + "</div>" +"<div id="+layer.name+"_expanded class='layer-expanded hide'>" +"<div class='layer-thumb'></div>" +"<div class='layer-desc'>"+layer.abstract+"</div>" +"<i id=add_"+layer.name+"_button class='fa fa-plus-circle float-right pointer' onclick='add_layer_to_map(\""+layer.name+"\",\""+layer.title+"\",\""+layer.bbox+"\")' style='font-size:36px;color:green;'></i>" +"<i id=rem_"+layer.name+"_button class='fa fa-minus-circle float-right pointer hide' onclick='remove_layer_from_map(\""+layer.name+"\")' style='font-size:36px;color:red;'></i>" +"</div>" +"</div>" }); nav_pane.innerHTML = layer_pane_html; } function toggleSideBar(){ document.getElementById("nav").classList.toggle("nav--active"); document.getElementById("nav").getElementsByClassName('hamburger')[0].classList.toggle("is-active"); } function toggleFeaturesSideBar() { document.getElementById("features-nav").classList.toggle("features-nav--active"); document.getElementById("features-nav").getElementsByClassName('hamburger')[0].classList.toggle("is-active"); } function add_layer_to_map(layerName, layerTitle, layerBbox){ if(active_layers.indexOf(layerName) !== -1){ // layer is already active alert("Layer " + layerName + " is already added to map"); return; } var allStyles = getAvailableStyles(layerName) var defaultStyle = allStyles.splice(0, 1)[0]; var style = getStyle(defaultStyle.styleName); append_new_style(style); addLayerToSelectedTab(layerName, layerTitle, layerBbox, allStyles, style) document.getElementById("add_" + layerName + "_button").classList.toggle('hide'); document.getElementById("rem_" + layerName + "_button").classList.toggle('hide'); active_layers.push(layerName); // fit bounds to new added layer zoomToExtent(layerBbox); } function append_new_style(style){ /*if(get_map_style == null || get_map_style == {}){ // if no style is currently present set_map_style(style); } else{*/ Object.keys(style.sources).forEach(function(key){ // if (!map.isSourceLoaded(key)) //style.sources[key].tiles = [style.sources[key].tiles[0].replace('6792', '8080')]; style.sources[key].tiles = [baseUrl + "gwc/service/tms/1.0.0/" + getWorkspace() + ":" + style.layers[0].id + "/{z}/{x}/{y}"]; //style.sources[key].tiles = ["http://" + get_host() + "/" + style.sources[key].tiles[0]]; map.addSource(key, style.sources[key]); }) style.layers.forEach(function(layer){ map.addLayer(layer) }) // } } function zoomToExtent(Bbox) { var coord = Bbox.split(','); var minx = Number(coord[0]); var miny = Number(coord[1]); var maxx = Number(coord[2]); var maxy = Number(coord[3]); //console.log(minx, miny, maxx, maxy); //console.log(minx+miny); map.fitBounds([[minx, miny],[maxx, maxy]]); } function addLayerToSelectedTab(layerName, layerTitle, layerBbox, all_styles, style){ var selectedLayersPanel = document.getElementById('nav-selected-layers'); var html = selectedLayersPanel.innerHTML; var layerType = style.layers[0].type; //circle or fill var styleSelectorHTML = "" var styleNameToTitleMap = {}; all_styles.forEach(function(style){ styleSelectorHTML += "<option value=" + style.styleName + ">" + style.styleTitle + "</option>"; styleNameToTitleMap[style.styleName] = style.styleTitle; }) layerToStyleOptionsMap[layerName] = styleNameToTitleMap; html += "<div id="+layerName+"_styler class='layer-div no-select'>" + "<div class='row'>" + "<div class='layer-name-div no-select col-sm-7'>" + layerTitle + "</div>" + "<i class='fa fa-minus-circle float-right pointer col-sm-2' onclick='remove_layer_from_map(\""+layerName+"\")' style='font-size:36px;color:red;'></i>" + "</div>" + "<div class='layer-styler-controls row'>" + "<div class='col-sm-5 zoom-to-extent-div inline' style='background-image:url("+thumbnailsUrl+"zoom-to-extent.png)' onclick='zoomToExtent(\""+layerBbox+"\")'>" + "zoom to extent" + "</div>" + "<div style='width: 18%;float: left;font-size: 14px;opacity: 0.5;margin: 0 0 0 3%; padding-left: 0%;' class='col-sm-3'>opacity</div>" + "<div class='slidecontainer inline col-sm-3'>" + "<input id="+layerName+"_slider class='slider' type='range' min='1' max='100' step='5' value="+getOpacity(style)+" onchange='setOpacity(\""+layerName+"\",\""+layerType+"\", this.value)' oninput='setOpacity(\""+layerName+"\",\""+layerType+"\", this.value)'></input>" + "</div>" + "</div>" + "<div style='font-size:14px;'>Style by: " + "<select class='style-selector' onchange='changeLayerStyle(\""+layerName+"\",this)'>" + styleSelectorHTML + "</select>" + "</div>" + "<div class='legend-div' onclick='toggleLegend(this)'>Legend" + "<i class='fas fa-chevron-right'></i>" + "</div>" + "<img id='"+layerName+"_legend' class='hide legend' src=" + baseUrl + "legend/" + layerName + "/" + all_styles[0].styleName+"></img>" +"</div>" selectedLayersPanel.innerHTML = html; } function toggleLegend(div) { div.parentNode.getElementsByClassName('legend')[0].classList.toggle('hide'); var legendArrow = div.getElementsByClassName('fas')[0] legendArrow.classList.toggle('fa-chevron-down'); legendArrow.classList.toggle('fa-chevron-right'); } function changeLayerStyle(layerName, layerStyleSelector) { var option = layerStyleSelector.options[layerStyleSelector.selectedIndex]; var selectedStyle = option.getAttribute('value'); var highlightedLayer = layerName + '-highlighted' map.removeLayer(layerName); if (map.getLayer(highlightedLayer)) map.removeLayer(highlightedLayer); map.removeSource(layerName); append_new_style(getStyle(selectedStyle)); // update the legend according to new style var legend = document.getElementById(layerName + '_legend'); legend.src = baseUrl + "legend/" + layerName + "/" + selectedStyle } function setOpacity(layerName, layerType, opacity) { console.log(layerName, layerType, opacity); if (map.getLayer(layerName) === undefined) return; if (layerType === 'fill') map.setPaintProperty(layerName, 'fill-opacity', opacity/100); else if (layerType === 'circle') map.setPaintProperty(layerName, 'circle-opacity', opacity/100); } function getOpacity(style){ if (style.layers[0].type === "circle") return style.layers[0].paint['circle-opacity'] * 100 else if (style.layers[0].type === "fill") return style.layers[0].paint['fill-opacity'] * 100 else return 70 } function remove_layer_from_map(layer_name){ if (active_layers.indexOf(layer_name) === -1){ alert("Layer " + layer_name + " is not present on the map"); } console.log("Removing source " + layer_name) // map.isSourceLoaded(layer_name); if (map.getLayer(layer_name+'-highlighted') !== undefined) map.removeLayer(layer_name+'-highlighted'); map.removeLayer(layer_name); map.removeSource(layer_name); document.getElementById("add_" + layer_name + "_button").classList.toggle('hide'); document.getElementById("rem_" + layer_name + "_button").classList.toggle('hide'); console.log(active_layers); active_layers.splice(layer_name, 1); //active_layers.splice(layer_name+'-highlighted', 1); console.log(active_layers); console.log(map) // remove the layer styler from selected layers tab var element = document.getElementById(layer_name + '_styler'); if (element !== undefined) element.parentNode.removeChild(element); } function openTab(evt, div_name) { // Declare all variables var i, tabcontent, tablinks; console.log('div name', div_name); // Get all elements with class="tabcontent" and hide them tabcontent = document.getElementsByClassName("tabcontent"); for (i = 0; i < tabcontent.length; i++) { tabcontent[i].classList.add('hide'); } // Get all elements with class="tablinks" and remove the class "active" tablinks = document.getElementsByClassName("tablinks"); for (i = 0; i < tablinks.length; i++) { tablinks[i].className = tablinks[i].className.replace(" active", ""); } // Show the current tab, and add an "active" class to the button that opened the tab document.getElementById(div_name).classList.remove('hide'); evt.currentTarget.className += " active"; } function showClickedFeature(event) { var features = map.queryRenderedFeatures(event.point); if (!features.length){ clear_selected_features(); console.log("clearing_selected_features: ", document.getElementById("features-nav").classList.contains("features-nav--active")); if (document.getElementById("features-nav").classList.contains("features-nav--active") == false) toggleFeaturesSideBar(); return; } highlightSelectedFeature(features); updateSelectedFeatureTree(features); if (document.getElementById("features-nav").classList.contains("features-nav--active")) toggleFeaturesSideBar(); } function removeHighlightedLayers(map){ // clear currently highlighted layers map.getStyle().layers.forEach(function(layer) { if (layer.id.indexOf('-highlighted') !== -1){ if (map.getLayer(layer.id)) map.removeLayer(layer.id) } }) } function highlightSelectedFeature(features) { console.log(map.getStyle().layers); removeHighlightedLayers(map) var layerToFeatures = {}; for (var i = 0; i < features.length; i++){ var feature = features[i]; // skip if the feature is coming from the mapbox base layers if (feature.layer.source === 'mapbox' || feature.layer.source === 'claimedboundaries') continue; var layer = feature.layer.id; if (!layerToFeatures[layer]) layerToFeatures[layer] = new Set(); layerToFeatures[layer].add(feature); //TODO: avoid adding the complete feature object } Object.keys(layerToFeatures).forEach(function(layer) { var features = layerToFeatures[layer]; var layerType; var layerSource; var filter = ['in', '__mlocate__id']; features.forEach(function(feature){ if (!layerType){ layerType = feature.layer.type; layerSource = feature.layer.source; } filter.push(feature.properties.__mlocate__id) }); var _type, _paint; if (layerType === 'fill'){ _type = 'line'; _paint = {'line-width': 1, 'line-color': 'red'}; } else if (layerType === 'circle'){ _type = 'circle'; _paint = {'circle-opacity': 0, 'circle-stroke-width': 1, 'circle-stroke-color': 'red'} } // create a layer for highlighted features map.addLayer({ 'id': layer+'-highlighted', 'type': _type, 'source': layerSource, 'source-layer': layerSource, 'paint': _paint, 'filter': filter }) }) } function clear_selected_features() { removeHighlightedLayers(map) clear_selected_feature_tree(); } function get_style_by(feature) { if (feature.layer.type === "fill") return feature.layer.paint['fill-color'].property; else if (feature.layer.type === "circle") return feature.layer.paint['circle-color'].property; else return null; } function clear_selected_feature_tree(){ document.getElementById('features').innerHTML = ""; } function updateSelectedFeatureTree(features) { var selectedFeaturesJson = {}; for (var i = 0; i < features.length; i++){ if (features[i].layer.source === 'mapbox' || features[i].layer.source === 'claimedboundaries') continue; var attributes = filterAttributes(features[i]); selectedFeaturesJson[layerNameToTitleMap[features[i].layer.id]] = attributes; } document.getElementById('features').innerHTML = renderJSON(selectedFeaturesJson); } function filterAttributes(feature) { var filteredAttributes = {}; var layerName = feature.layer.id; for (var key in feature.properties) { if (key.startsWith("__")) continue; else if (layerToStyleOptionsMap[layerName][layerName + '_' + key] !== undefined) filteredAttributes[layerToStyleOptionsMap[layerName][layerName + '_' + key]] = feature.properties[key]; else filteredAttributes[key] = feature.properties[key] } return filteredAttributes; } function renderJSON(obj) { var keys = [], retValue = ""; for (var key in obj) { if (key.startsWith("__")) continue; if (typeof obj[key] === 'object') { retValue += "<div class='tree-expandable'>"; retValue += "<div class='pointer no-select' onclick='toggleAllChildren(this.parentElement)'>" + key + "</div>"; retValue += renderJSON(obj[key]); retValue += "</div>"; } else { retValue += "<div class='tree-node hide'>" + key + " : " + obj[key] + "</div>"; } keys.push(key); } return retValue; } function toggleAllChildren(div) { if (!div.hasChildNodes()) return; var children = div.childNodes; // var isCollapsed = children[1].style.display == ''; for (var i = 1; i < children.length; i++){ children[i].classList.toggle('hide'); } } function set_initial_zoom(map){ initial_zoom = map.getZoom(); } function syncMaps(master, google_map){ var center = master.getCenter(); var zoom = master.getZoom(); google_map.setCenter(center); google_map.setZoom(zoom+1); // now if zoom level was non-integer, the google map // would have become blank. var new_zoom; if (zoom > initial_zoom) new_zoom = Math.ceil(zoom); else new_zoom = Math.floor(zoom); if (new_zoom === zoom) return; else{ master.setZoom(new_zoom); google_map.setZoom(new_zoom + 1); } } function sync_map_move(map, gmap){ var center = map.getCenter(); gmap.setCenter(center); } export default { toggleSideBar: toggleSideBar, toggleFeaturesSideBar: toggleFeaturesSideBar, openTab: openTab, initMap: initMap } window.initMap =initMap window.initGoogleMap =initGoogleMap window.initMapboxglMap =initMapboxglMap window.get_host =get_host window.getWorkspace =getWorkspace window.get_map_style =get_map_style window.set_map_style =set_map_style window.getStyle =getStyle window.getAvailableStyles =getAvailableStyles window.httpGetAsync =httpGetAsync window.getAvailableLayers =getAvailableLayers window.getLayerInfo_WMS_3 =getLayerInfo_WMS_3 window.getLatLonBBoxString =getLatLonBBoxString window.layer_changed =layer_changed window.style_changed =style_changed window.update_map =update_map window.add_background_to_style =add_background_to_style window.getThemeNames =getThemeNames window.expand_layer_details =expand_layer_details window.populateLayerPanel =populateLayerPanel window.toggleSideBar =toggleSideBar window.add_layer_to_map =add_layer_to_map window.append_new_style =append_new_style window.addLayerToSelectedTab =addLayerToSelectedTab window.getOpacity =getOpacity window.remove_layer_from_map =remove_layer_from_map window.openTab =openTab window.showClickedFeature =showClickedFeature window.highlightSelectedFeature =highlightSelectedFeature window.clear_selected_features =clear_selected_features window.get_style_by =get_style_by window.clear_selected_feature_tree =clear_selected_feature_tree window.updateSelectedFeatureTree =updateSelectedFeatureTree window.renderJSON =renderJSON window.toggleAllChildren =toggleAllChildren window.set_initial_zoom =set_initial_zoom window.syncMaps =syncMaps window.sync_map_move =sync_map_move window.SyncGoogleAndMapboxglMaps =SyncGoogleAndMapboxglMaps window.changeBaseLayer =changeBaseLayer window.zoomToExtent =zoomToExtent window.setOpacity =setOpacity window.changeLayerStyle =changeLayerStyle window.toggleLegend =toggleLegend window.toggleFeaturesSideBar =toggleFeaturesSideBar