-
Notifications
You must be signed in to change notification settings - Fork 0
/
visualization.json
1 lines (1 loc) · 16.9 KB
/
visualization.json
1
{"templateId":"598a10503004fda14f4367f7","templateType":"BLANK","name":"mapbox_boundaries","accountId":"598a104f3004fda14f436772","enabled":true,"type":"CUSTOM","components":[{"id":"598a17513004f4c2a3d84f9e","name":"style.css","type":"text/css","visualizationId":"598a17513004f4c2a3d84f9c","body":"/**\n * Copyright (C) Zoomdata, Inc. 2012-2017. All rights reserved.\n */\n.small text {\n font-size:10px;\n}\n\n.medium text {\n font-size: 12px;\n}\n\n.large text {\n font-size:14px;\n}\n\n.mapboxgl-popup {\n max-width: 400px;\n font: 12px/20px 'Helvetica Neue', Arial, Helvetica, sans-serif;\n}\n","order":0,"uploadDate":"2017-09-15 18:23:24.007"},{"id":"598a17513004f4c2a3d84f9d","name":"Visualization.js","type":"text/javascript","visualizationId":"598a17513004f4c2a3d84f9c","body":"/*\n * Copyright (C) Zoomdata, Inc. 2012-2017. All rights reserved.\n */\n/* global controller */\n\nvar dataLookup = {};\n\nconsole.log('starting visualization, controller variables:', controller.variables);\n//load mapbox and define a target div\nvar uuid = new Date().getTime();\nvar mapVarId = 'map-' + uuid;\nvar mapDiv = document.createElement('div');\nvar dataLookup = {};\nmapDiv.id = mapVarId;\nmapDiv.style='width:100%;height:100%;'\nvar map = null;\nvar currentlyVisibleLayer = undefined;\ndataLookup = {};\nvar mb_css = document.createElement('link');\nmb_css.href='https://api.mapbox.com/mapbox-gl-js/v0.36.0/mapbox-gl.css';\nmb_css.rel='stylesheet';\nvar mb_js = document.createElement('script');\nmb_js.src = 'https://api.mapbox.com/mapbox-gl-js/v0.36.0/mapbox-gl.js';\nmb_js.type='text/javascript';\nmb_js.onload = function() {\n //mapbox has to be loaded before we do anything else\n initializeMap();\n};\n\nvar heightFactor = controller.variables['Extrusion Factor'];\nconsole.log('extrusion factor is ', heightFactor);\nvar layerOpacity = controller.variables['Opacity'];\n\ncontroller.element.appendChild(mb_js);\ncontroller.element.appendChild(mb_css);\ncontroller.element.appendChild(mapDiv);\n\n//The variables set in the chart properties drive what layers are displayed. This\n//chart uses administrative boundaries. Users can choose which ones, level 0 through\n//level 3 are supported. Each layer has a check box and a field to identify The\n// group by field for that level.\n\n//Pre-defined the zoom levels for different cases. We could do something clever here, but I'm\n//brute force defining all possible sets of levels\nvar allZoomLevels = [\n {minZoom: 0, maxZoom: 22},\n]\nvar testMapBoundariesLevels = {};\nfor(var i=0; i < 4; i++) {\n var currEnabledVar = 'Include Admin ' + i;\n var currGroupByVar = 'Admin ' + i + ' Group By'\n var currLayerKey = 'adm' + i;\n if(controller.variables[currEnabledVar]) {\n console.log('User set ' + currEnabledVar, ' group by field is ', controller.variables[currGroupByVar]);\n //TODO: set up some validation to make sure the group by field is filled in, and check if it is valid, if possible\n testMapBoundariesLevels[currLayerKey] = {\n level: i,\n source: \"admin-\" + i,\n source_layer: \"boundaries_admin_\" + i,\n minZoom: 0,\n maxZoom: 22,\n vtPropField: 'id', //always this for Mapbox enterprise boundaries\n dataPropField: controller.variables[currGroupByVar],\n colorStops: [['', \"rgba(0,255,0,.5)\"]],\n heightStops: [['', 0]]\n }\n }\n}\n\nconsole.log('Test Map boundaries are set to:', testMapBoundariesLevels);\n\nvar mapBoundariesLevels = {\n adm0: {\n level: 0,\n source: \"admin-0\",\n source_layer: \"boundaries_admin_0\",\n minZoom: 0,\n maxZoom: 3,\n vtPropField: 'id',//'country_code',\n dataPropField: 'adm0_id',\n colorStops: [['', \"rgba(0,255,0,.5)\"]],\n heightStops: [[0,0]]\n }, adm1: {\n level: 1,\n source: \"admin-1\",\n source_layer: \"boundaries_admin_1\",\n minZoom: 3,\n maxZoom: 7,\n vtPropField: 'id',\n dataPropField: 'adm1_id',\n colorStops: [['', \"rgba(128,0,0,0)\"]],\n heightStops: [[0,0]]\n },adm2: {\n level: 2,\n source: \"admin-2\",\n source_layer: \"boundaries_admin_2\",\n minZoom: 7,\n maxZoom: 12,\n vtPropField: 'id',\n dataPropField: 'adm2_id',\n colorStops: [['', \"rgba(0,0,128,0)\"]],\n heightStops: [[0,0]]\n },adm3: {\n level: 3,\n source: \"admin-3\",\n source_layer: \"boundaries_admin_3\",\n minZoom: 12,\n maxZoom: 22,\n vtPropField: 'id',\n dataPropField: 'adm3_id',\n colorStops: [['', \"rgba(0,255,0,0)\"]],\n heightStops: [[0,0]]\n }\n}\nconsole.log('Hard coded map boundaries ', mapBoundariesLevels);\n\nfunction getCurrentlyVisibleLayer() {\n var result = undefined;\n var currZoom = map.getZoom();\n //iterate through the layer configurations, find the zoom level that matches current zoom\n Object.keys(mapBoundariesLevels).forEach(function(levelKey) {\n var level = mapBoundariesLevels[levelKey];\n if((level.minZoom <= currZoom) && (currZoom <= level.maxZoom)) {\n var testLayer = map.getLayer(level.source+'_base_fill');\n if(testLayer) {\n result = level;\n }\n }\n })\n\n return result;\n}\n\nfunction initializeMap() {\n mapboxgl.accessToken = controller.variables['Mapbox Access Token'];\n map = new mapboxgl.Map({\n container: mapDiv.id,\n style: 'mapbox://styles/mapbox/streets-v9',\n //TODO: issue with float variable type in CLI/chart studio needs to be resolved\n //center: [controller.variables['Initial Map Center Lon'], controller.variables['Initial Map Center Lat']],\n center: [0, 15.0],\n zoom: 1.5, //TODO: add to controller variables\n minZoom: 1.5 //TODO: add to controller variables\n //TODO: maxBounds from controller variables\n });\n map.on('load', configureMap);\n\n}\n\nfunction configureMap() {\n var nav = new mapboxgl.NavigationControl();\n map.addControl(nav, 'top-left');\n map.addSource(\"admin-0\", {\n type: \"vector\",\n url: \"mapbox://mapbox.enterprise-boundaries-a0-v1\"\n });\n\n map.addSource(\"admin-1\", {\n type: \"vector\",\n url: \"mapbox://mapbox.enterprise-boundaries-a1-v1\"\n });\n\n map.addSource(\"admin-2\", {\n type: \"vector\",\n url: \"mapbox://mapbox.enterprise-boundaries-a2-v1\"\n });\n\n map.addSource(\"admin-3\", {\n type: \"vector\",\n url: \"mapbox://mapbox.enterprise-boundaries-a3-v1\"\n });\n\n Object.keys(mapBoundariesLevels).forEach(function(currKey) {\n // for each level we want a fill (extruded) and a border\n var boundary = mapBoundariesLevels[currKey];\n console.log('Adding layer to map:', boundary.source+\"_base_fill\");\n map.addLayer({\n \"id\": boundary.source + \"_base_fill\",\n \"type\": \"fill-extrusion\",\n \"source\": boundary.source,\n \"source-layer\": boundary.source_layer,\n minzoom: boundary.minZoom,\n maxzoom: boundary.maxZoom,\n \"paint\": {\n \"fill-extrusion-color\": \"green\",\n \"fill-extrusion-opacity\": layerOpacity,\n \"fill-extrusion-height\": 0\n }\n }, 'waterway-label');\n\n\n map.addLayer({\n id: boundary.source + \"_layer_borders\",\n type: \"line\",\n source: boundary.source,\n minzoom: boundary.minZoom,\n maxzoom: boundary.maxZoom,\n \"source-layer\": boundary.source_layer,\n layout: {},\n paint: {\n \"line-color\": \"darkgray\",\n \"line-width\": 1\n }\n });\n });\n currentlyVisibleLayer = getCurrentlyVisibleLayer();\n console.log('At start the visible layer is', currentlyVisibleLayer);\n setStops(dataLookup, currentlyVisibleLayer, map.queryRenderedFeatures());\n //Setting the map events here, they require the layers have been\n //added already\n map.on('zoom', function() {\n// console.log('Map zoom level is :', map.getZoom());\n //when the user zooms to a level that changes the visible layer\n // then we need to rebuild the query accordingly\n var visibleLayerAfterZoom = getCurrentlyVisibleLayer();\n if(visibleLayerAfterZoom.source !== currentlyVisibleLayer.source) {\n console.log(\"Changing visible layer on zoom\");\n currentlyVisibleLayer = visibleLayerAfterZoom;\n var currGroup = controller.dataAccessors['Group By'].getGroup();\n currGroup.name = currentlyVisibleLayer.dataPropField;\n currGroup.limit = 200000; //TOOD: hard-coded limit to make all features appear, need to make this dynamic if we can get a count of features from Mapbox layer\n //TODO: adjust limit dynamically to the number of featuers in the layer (or number of visible features, if we can do that)\n controller.dataAccessors['Group By'].setGroup((currentlyVisibleLayer.dataPropField, currGroup));\n //TODO: if we are filtering then we need to update filters here\n // Changing the group by will cause controller.update, which does this: setStops(dataLookup, currentlyVisibleLayer, map.queryRenderedFeatures());\n }\n });\n map.on('moveend', function() {\n console.log('Map moveend');\n //TODO: if we are filtering we need to update filters here\n })\n\n // When a click event occurs on a feature in the places layer, open a popup at the\n // location of the feature, with description HTML from its properties.\n\n //TODO: we could have a click event listener per layer, or remove the layer name param\n //and implement logic to determine what layer is active\n map.on('click', /*level.source*/ 'admin-0'+'_base_fill', function (e) {\n console.log('clicked on layer ', e);\n\n var tooltipString = '';//TODO: get name '<B>Area Name</b><br/>'\n var metrics = getMetrics();\n Object.keys(metrics).forEach(function(metricKey) {\n var val = getAreaMetric(e.features[0], metricKey);\n tooltipString += '<b>' + metrics[metricKey].getLabel() + ':</b> ';\n tooltipString += metrics[metricKey].format(val) + '<br/>';\n });\n\n //get the feature ID from the event, then we need to look up the actual values\n //from the Zoomdata data collection\n var featureID = e.features[0].properties.id;\n new mapboxgl.Popup()\n .setLngLat(e.lngLat)\n .setHTML(tooltipString)\n .addTo(map);\n });\n\n //map.on('mousemove', 'states_layer_base_fill', function(e) {console.log(e);});\n //setStops(dataLookup, map.getLayer('counties_base_fill'));\n console.log(\"Map configuration complete\");\n}\n\ncontroller.update = function(data, progress) {\n // Called when new data arrives\n// console.log('Controller update ', progress, ' with data ', data);\n dataLookup = {};\n for (var i = 0; i < data.length; i++) {\n var item = data[i];\n dataLookup[item.group] = item;\n }\n\n //set the style for each polygon based on the value of the data\n if(map !== null) {\n var currLayer = getCurrentlyVisibleLayer();\n if(currLayer) {\n setStops(dataLookup, currLayer, map.queryRenderedFeatures());\n }\n }\n};\n\n function getMetrics() {\n var dataAccessors = controller.dataAccessors;\n var metrics = {};\n\n _.forOwn(dataAccessors, function(value, key) {\n if (value.TYPE === value.TYPES.METRIC ||\n value.TYPE === value.TYPES.MULTI_METRIC) {\n metrics[key] = value;\n }\n });\n\n return metrics;\n }\n\n //Function finds the metric value associated with a specific feature\n getAreaMetric = function(feature, metricKey) {\n var metrics = getMetrics();\n var metricFieldName = metrics[metricKey].getMetric().name;\n var metricFuncName = metrics[metricKey].getMetric().func;\n var result = -1;\n try {\n var polygonId = feature.properties.id;\n //TODO: hack way to get value, need to use proper accessors\n if (typeof(dataLookup[polygonId]) !== 'undefined') {\n if(metricFieldName === 'count') {\n result = dataLookup[polygonId].current.count;\n } else {\n result = dataLookup[polygonId].current.metrics[metricFieldName][metricFuncName];\n }\n }\n } catch(e) {\n console.error('Error getting well metric ', e);\n }\n // console.log(' well metric for ', wellId, \" is \", result);\n return (result);\n };\n\nfunction setStops(data, layer, features) {\n console.log('setting stops for ', layer, ' against data ', data);\n var stopsArray = [];\n var heightStopsArray = [];\n var defaultColor = 'gray';\n var defaultHeight = 0;\n //TODO: only for features in currently visible layer\n\n Object.keys(dataLookup).forEach(function(currAttributeKey) {\n var val = dataLookup[currAttributeKey];\n if(val.group[0] !== null) { //Mapbox GL doesn't like stops with null\n var metrics = getMetrics();\n var currentMetricVal = metrics.Color.raw(val);\n var fillColor = metrics.Color.color(dataLookup[val.group]);\n var red = parseInt(fillColor.substring(1,3), 16);\n var green = parseInt(fillColor.substring(3,5), 16);\n var blue = parseInt(fillColor.substring(5), 16);\n var rgba = \"rgba(\" + red + \",\" + green + \",\" + blue + \",0.8)\";\n stopsArray.push([val.group[0], rgba]);\n //TODO: right now height is hard-coded to volume. Link to metric specified by user\n var heightField = controller.variables[\"Height\"];\n var heightMetricFieldName = metrics.Height.getMetric().name;\n var heightMetricFieldFunc = metrics.Height.getMetric().func;\n var heightMetricVal;\n if(heightMetricFieldName === 'count') {\n heightMetricVal = dataLookup[currAttributeKey].current.count;\n } else {\n heightMetricVal = dataLookup[currAttributeKey].current.metrics[heightMetricFieldName][heightMetricFieldFunc];\n }\n var height = heightMetricVal * heightFactor < 65000 ? heightMetricVal * heightFactor : 65000;\n heightStopsArray.push([val.group[0], height ]);\n }\n });\n\n\nconsole.log('setting stops for layer ', layer.source+'_base_fill', \" on property\", layer.vtPropField);\n map.setPaintProperty(layer.source+'_base_fill', 'fill-extrusion-color', {\n property: layer.vtPropField,\n type: 'categorical',\n stops: stopsArray,\n default: 'lightgray'\n } );\n\n map.setPaintProperty(layer.source+'_base_fill', 'fill-extrusion-height', {\n property: layer.vtPropField,\n type: 'categorical',\n stops: heightStopsArray,\n default: 0\n })\n console.log('Stops set to ', stopsArray);\n console.log('height stops:', heightStopsArray);\n}\ncontroller.resize = function(width, height, size) {\n // Called when the widget is resized\n if(map) { map.resize(); }\n};\n\ncontroller.createAxisLabel({\n picks: 'Color', // Variable Name\n orientation: 'horizontal',\n position: 'bottom',\n popoverTitle: 'Color'\n});\n\ncontroller.createAxisLabel({\n picks: 'Height', // Variable Name\n orientation: 'horizontal',\n position: 'bottom',\n popoverTitle: 'Height'\n});\n","order":1,"uploadDate":"2017-09-18 18:55:00.128"}],"libs":["lodash.min.js"],"objectFieldTypes":["TIME","NUMBER","MONEY","ATTRIBUTE","INTEGER"],"controls":["Sort","Filters","Info","TimePlayer"],"variables":[{"name":"Layer Configuration","type":"text","descr":"JSON string containing properties for each zoom level of data","defaultValue":"","colorMetric":false,"colorNumb":0,"required":false},{"name":"Mapbox Access Token","type":"string","descr":"Toke from https://www.mapbox.com/studio/account/tokens","defaultValue":"pk.eyJ1IjoibXdlcmxpbmciLCJhIjoiY2oxZzRlbnZjMDAwYTJ3cDZma3Vwa3NwbCJ9.-UCNWOrDq739KrtAcPJxaw","colorMetric":false,"colorNumb":0,"required":false},{"name":"Height","type":"metric","descr":"Values control the extruded height of the polygon","attributeType":["MONEY","INTEGER","NUMBER"],"defaultValue":[{"name":"count"}],"colorMetric":false,"colorNumb":0,"required":false},{"name":"Group By","type":"group","descr":"","attributeType":["ATTRIBUTE"],"defaultValue":"","colorMetric":false,"colorNumb":0,"groupType":"attribute","required":false},{"name":"Chart Description","type":"string","descr":"","defaultValue":"","colorMetric":false,"colorNumb":0,"required":false},{"name":"Chart Name","type":"string","descr":"","defaultValue":"","colorMetric":false,"colorNumb":0,"required":false},{"name":"Color","type":"metric","descr":"Color areas based on this metric","attributeType":["MONEY","INTEGER","NUMBER"],"defaultValue":[{"colorConfig":{"autoShowColorLegend":true},"name":"count"}],"colorMetric":false,"colorNumb":3,"metricType":"color","legendType":"palette","colors":[],"colorSet":"ZoomSequential","required":false},{"name":"Opacity","type":"float","descr":"Set how opaque the layer is. 0 is fully transparent, 1 is completely opaque, 0.5 is 50%","defaultValue":0.6,"colorMetric":false,"colorNumb":0,"required":false},{"name":"Extrusion Factor","type":"float","descr":"Will be multiplied by the metric to cause the extrusion to be larger/smaller in 3D view","defaultValue":1,"colorMetric":false,"colorNumb":0,"required":false}]}