We are introducing an upcoming set of features that enable Grasshopper users to work with geospatial data and export the project in the GeoJSON format that can be used to generate web maps and other interactive web content. 

In the first part of this introduction / tutorial I will give a brief overview of the new components. In the second part I will present the Mapbox platform, which offers web maps creation trough user-friendly editing interface without the requirement of prior web developement experience.

Research Team: Ondřej Veselý, Martin Bielik, Dr. Reinhard Koenig

You can download an early test release of components used in this article here.

Spatial Reference components|   

Give access to WKT (Well-known-text) definition for any spatial reference system (SRS) your data could use.

EPSG:4326 component provides you with WGS84. This is basicaly the default SRS that you should use for exporting your data as GeoJSON.

Get UTM component gives you UTM system used at the given location. This is the SRS your data were probably reprojected to, if you used plugins like Gismo or Elk2.

Get WKT component queries spatialreference.org for any other arbitrary SRS and gives you the WKT definition.

Reprojection components |   

Use ProjNET and GeoAPI libraries to reproject a set of points / coordinates from one SRS to another.

Transformation component generates a transformation guide that you can use to reproject points from one SRS to another.

Reprojection component transforms coordinates to target SRS using guide from Transformation component.

Source Offset component helps with cases, when your source dataset was translated from it’s original position in it’s respective SRS to Rhino origin point (or any other).

(Both most popuplar plugins for getting geospatial data into Grasshopper – Gismo and Elk2 – are guilty of doing this).

Decompose Extrusion component |  

Helps you preserve 3D geometry, since GeoJSON doesn’t have native support for extrusions or meshes.

Decompose Extrusion takes any breps or meshes and gives you their footprint and z-axis minimums and maximums. You can then export them as polygons with height informaton included as property.

GeoJSON components |  

Convert Rhino Geometry to GeoJSON Features. Each branch of Grasshopper data will becomes single Feature.

Points component converts Points to GeoJSON Point, or MultiPoint if it recieves mutliple objects.

Lines component converts Lines or Curves to GeoJSON LineString, or MultiLineString if it recieves multiple objects.

Polygons component converts Curves to GeoJSON Polygonor MultiPolygon if it recieves multiple objects. Will create only simple polygons without holes!

Complex Polygons component converts Curves to GeoJSON Polygons. It supports inner holes. First curve in the list will be the exterior curve, the rest will be interior.

GeoJSON Export components |  

Creates a GeoJSON file from your geometry features.

Merge GeoJSON component takes GeoJSON Features or complete FeatureCollections and merges them into single FeatureCollection (always do this before exporting).

Write File component saves a list of strings (texts) into text file on your computer (Therefore it’s use is limited not only to saving GeoJSON).

Uploading to Mapbox

With your geospatial data exported from Grasshopper, next thing you could want is to bring it to the web. Currently there are multiple open projects focused on the issue of Web Mapping (see Leaflet, OpenLayers, Mapnikthat allow you to build your web map application from the ground up, but that could be an overhelming task to anyone without previous knowledge of web development.

For those who are not comfortable around developing apps with JavaScript, I recommend looking into Mapbox. It’s an open source mapping platform, that integrates all the neccesary web technologies into one package. You can control most of the basic aspects of your web map trough Mapbox Studio, a visual interface for managing your geospatial data and controling how exactly will it be displayed to the end-users.

To prepare the data for use in your web map, it has to be first processed into vector tile sets – basically splitting the  data into smaller chunks. Luckily, Mapbox will handle this automaticaly and host your tile sets on their server.

Just upload the GeoJSON file as a new tileset and see for yourself.

Tip: There’s of limit on maximum number of user tile sets used in one map (15).

Try combining your data into single GeoJSON/tileset and then use filters to get specific data sets back. It will also save you time when reuploading updated data later.

To create your own map, you first need to create a new Style – you can also copy one of the exisiting ones. Then add your vector tile data as additional Layers to the Style. You can modify the Layer style properties, ie. colour, stroke witdth etc., directly in the interface and immediatly see the visual changes.

Once you are happy with the result, hit Publish to update the Style and Share to generate a link that anyone can use to view the map.

Done! It was that easy. 🙂

However, if you are interested in adding some interactivity to your model, you will have to get yourself familiar with JavaScript library called Mapbox GL JS.

Mapbox GL JS is a JavaScript library that renders vector tile data from Mapbox to your browser window, but you can also use it to add custom functionality to your web application.

It’s pretty accessible even for first time JavaScript users, since it’s very well documented including many examples for any basic functionality you would like to include.

Congratulations! If you followed trough the steps, your  project should be now ready to be uploaded to web and shared with the rest of the  world!

Below you can explore an example of a project we have published using the methods described.

.

An example of a project exported from Grasshopper using the methods described.

<style><!-- [et_pb_line_break_holder] --> /*-----CSS style for the layer and view selection widget-----*/<!-- [et_pb_line_break_holder] --> #layer-menu {<!-- [et_pb_line_break_holder] --> position: absolute;<!-- [et_pb_line_break_holder] --> z-index: 1;<!-- [et_pb_line_break_holder] --> top: 10px;<!-- [et_pb_line_break_holder] --> left: 10px;<!-- [et_pb_line_break_holder] --> width: 160px;<!-- [et_pb_line_break_holder] --> font-family: 'Open Sans', sans-serif;<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> #camera-menu {<!-- [et_pb_line_break_holder] --> position: absolute;<!-- [et_pb_line_break_holder] --> z-index: 1;<!-- [et_pb_line_break_holder] --> top: 10px;<!-- [et_pb_line_break_holder] --> right: 50px;<!-- [et_pb_line_break_holder] --> width: 80px;<!-- [et_pb_line_break_holder] --> font-family: 'Open Sans', sans-serif;<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> #function-menu {<!-- [et_pb_line_break_holder] --> position: absolute;<!-- [et_pb_line_break_holder] --> z-index: 1;<!-- [et_pb_line_break_holder] --> top: 10px;<!-- [et_pb_line_break_holder] --> right: 140px;<!-- [et_pb_line_break_holder] --> width: 140px;<!-- [et_pb_line_break_holder] --> font-family: 'Open Sans', sans-serif;<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> .dropbtn {<!-- [et_pb_line_break_holder] --> width: 100%;<!-- [et_pb_line_break_holder] --> position: relative;<!-- [et_pb_line_break_holder] --> z-index: 2;<!-- [et_pb_line_break_holder] --> background-color: #31b7bc; /*AIT colors*/;<!-- [et_pb_line_break_holder] --> color: white;<!-- [et_pb_line_break_holder] --> padding: 16px;<!-- [et_pb_line_break_holder] --> font-size: 16px;<!-- [et_pb_line_break_holder] --> border: none;<!-- [et_pb_line_break_holder] --> cursor: pointer;<!-- [et_pb_line_break_holder] --> outline: none;<!-- [et_pb_line_break_holder] --> border-radius: 4px;<!-- [et_pb_line_break_holder] --> box-shadow: 0px 0px 2px 0px rgba(0,0,0,0.5);<!-- [et_pb_line_break_holder] --> font-weight: 500;<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> .dropbtn:hover, .dropbtn:focus {<!-- [et_pb_line_break_holder] --> background-color: #22a1a1;<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> .dropdown-content {<!-- [et_pb_line_break_holder] --> border-radius: 5px;<!-- [et_pb_line_break_holder] --> text-align: left;<!-- [et_pb_line_break_holder] --> display: none;<!-- [et_pb_line_break_holder] --> position: absolute;<!-- [et_pb_line_break_holder] --> top: 55px;<!-- [et_pb_line_break_holder] --> background-color: #ffffff;<!-- [et_pb_line_break_holder] --> width: 100%;<!-- [et_pb_line_break_holder] --> overflow: auto;<!-- [et_pb_line_break_holder] --> box-shadow: 0px 0px 6px 0px rgba(0,0,0,0.4);<!-- [et_pb_line_break_holder] --> z-index: 1;<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> .dropdown-content a {<!-- [et_pb_line_break_holder] --> color: black;<!-- [et_pb_line_break_holder] --> padding: 12px 16px;<!-- [et_pb_line_break_holder] --> text-decoration: none;<!-- [et_pb_line_break_holder] --> display: block;<!-- [et_pb_line_break_holder] --> border-top: 1px solid #ddd;<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> .dropdown-content a.Off {<!-- [et_pb_line_break_holder] --> color: #6b6b6b;<!-- [et_pb_line_break_holder] --> background: #f9f9f9;<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> .dropdown-content a:hover {background-color: rgb(240, 240, 240);}<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> .show {display: block;} <!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> /*-----CSS style for the element info popup widget-----*/<!-- [et_pb_line_break_holder] --> dd {<!-- [et_pb_line_break_holder] --> margin-left: 0;<!-- [et_pb_line_break_holder] --> margin-bottom: 8px;<!-- [et_pb_line_break_holder] --> font-weight: bold;<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> dt {<!-- [et_pb_line_break_holder] --> color: #31b7bc; /*AIT colors*/<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> dl {<!-- [et_pb_line_break_holder] --> margin-bottom: 0;<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> .mapboxgl-popup-anchor-bottom .mapboxgl-popup-tip{<!-- [et_pb_line_break_holder] --> border-top-color: #31b7bc; /*AIT colors*/<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> .mapboxgl-popup-content{<!-- [et_pb_line_break_holder] --> font-size: 14px;<!-- [et_pb_line_break_holder] --> color: white;<!-- [et_pb_line_break_holder] --> background-color: rgba(0, 0, 0, 0.8);<!-- [et_pb_line_break_holder] --> box-shadow: 0 0 0px 2px #31b7bc; /*AIT colors*/<!-- [et_pb_line_break_holder] --> min-width: 120px;<!-- [et_pb_line_break_holder] --> max-width: 300px;<!-- [et_pb_line_break_holder] --> max-height: 170px;<!-- [et_pb_line_break_holder] --> overflow-y: auto;<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> .mapboxgl-popup-close-button{<!-- [et_pb_line_break_holder] --> color: white;<!-- [et_pb_line_break_holder] --> font-size: 20px;<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> /*-----CSS style for the map scale widget-----*/<!-- [et_pb_line_break_holder] --> .mapboxgl-ctrl-scale {<!-- [et_pb_line_break_holder] --> background-color: rgba(0, 0, 0, 0.75);<!-- [et_pb_line_break_holder] --> font-size: 12px;<!-- [et_pb_line_break_holder] --> border-width: medium 2px 3px;<!-- [et_pb_line_break_holder] --> border-style: none none solid;<!-- [et_pb_line_break_holder] --> border-color: #31b7bc; /*AIT colors*/<!-- [et_pb_line_break_holder] --> padding: 0 5px;<!-- [et_pb_line_break_holder] --> color: white;<!-- [et_pb_line_break_holder] --> box-sizing: border-box;<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> /*-----CSS style for copyright-----*/<!-- [et_pb_line_break_holder] --> #copyright {<!-- [et_pb_line_break_holder] --> font-size: 9px;<!-- [et_pb_line_break_holder] --> color:white;<!-- [et_pb_line_break_holder] --> text-align: center;<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> #copyright a {<!-- [et_pb_line_break_holder] --> color:#31b7bc; /*AIT colors*/<!-- [et_pb_line_break_holder] --> text-decoration: none;<!-- [et_pb_line_break_holder] --> font-weight: 1000;<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> </style><!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> <div id='map' style='min-height: 500px; min-width: 460px;'> <!-- MapBox container --><!-- [et_pb_line_break_holder] --> <nav id="layer-menu"><!-- [et_pb_line_break_holder] --> <button onclick="dropdownToggle('layerDropdown')" class="dropbtn"><!-- [et_pb_line_break_holder] --> Select scenario</button><!-- [et_pb_line_break_holder] --> <div id="layerDropdown" class="dropdown-content"></div><!-- [et_pb_line_break_holder] --> <div id = "copyright">created at <a target="_blank" <!-- [et_pb_line_break_holder] --> href="https://cities.ait.ac.at/site/">AIT</a><!-- [et_pb_line_break_holder] --> by Ondrej Vesely,  2018</div><!-- [et_pb_line_break_holder] --> </nav><!-- [et_pb_line_break_holder] --> <nav id="camera-menu"><!-- [et_pb_line_break_holder] --> <button onclick="dropdownToggle('cameraDropdown')" class="dropbtn"><!-- [et_pb_line_break_holder] --> Views</button><!-- [et_pb_line_break_holder] --> <div id="cameraDropdown" class="dropdown-content"></div><!-- [et_pb_line_break_holder] --> </nav><!-- [et_pb_line_break_holder] --> <nav id="function-menu"><!-- [et_pb_line_break_holder] --> <button onclick="dropdownToggle('functionDropdown')" class="dropbtn"><!-- [et_pb_line_break_holder] --> Functions</button><!-- [et_pb_line_break_holder] --> <div id="functionDropdown" class="dropdown-content"></div><!-- [et_pb_line_break_holder] --> </nav><!-- [et_pb_line_break_holder] --> </div><!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> <script><!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> // Load Map via Mapbox API<!-- [et_pb_line_break_holder] --> mapboxgl.accessToken = 'pk.eyJ1Ijoib25kcmVqLXZlc2VseSIsImEiOiJjamlhOWo3cnkwMnY3M3ZwZjU5bm54c3BsIn0.69pwP4aK4CmFBlpU8I4giw';<!-- [et_pb_line_break_holder] --> // dont forget to change this accessToken to you own if you use this code<!-- [et_pb_line_break_holder] --> var map = new mapboxgl.Map({<!-- [et_pb_line_break_holder] --> container: 'map',<!-- [et_pb_line_break_holder] --> style: 'mapbox://styles/ondrej-vesely/cjjsritez0dx42ro0vz8l2ks4'<!-- [et_pb_line_break_holder] --> });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> // Camera limits<!-- [et_pb_line_break_holder] --> map.setMaxZoom(17);<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> // Add navigation widget<!-- [et_pb_line_break_holder] --> var nav = new mapboxgl.NavigationControl();<!-- [et_pb_line_break_holder] --> map.addControl(nav,'top-right');<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> // Add fullscreen toggle button widget<!-- [et_pb_line_break_holder] --> var full = new mapboxgl.FullscreenControl();<!-- [et_pb_line_break_holder] --> map.addControl(full, 'top-right')<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> // Add map scale widget<!-- [et_pb_line_break_holder] --> var scale = new mapboxgl.ScaleControl({<!-- [et_pb_line_break_holder] --> maxWidth: 250,<!-- [et_pb_line_break_holder] --> unit: 'metric'<!-- [et_pb_line_break_holder] --> });<!-- [et_pb_line_break_holder] --> map.addControl(scale, 'bottom-right');<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> // On start wait till map loads,<!-- [et_pb_line_break_holder] --> map.on('load', function() {<!-- [et_pb_line_break_holder] --> // move camera a bit,<!-- [et_pb_line_break_holder] --> map.flyTo({center: [103.84, 1.2680], zoom: 13.8, speed: 0.1, bearing: 4})<!-- [et_pb_line_break_holder] --> // and open welcome popup<!-- [et_pb_line_break_holder] --> helpPopup()<!-- [et_pb_line_break_holder] --> });<!-- [et_pb_line_break_holder] --> <!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> // --- The interesting stuff starts here ----<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> // Dropdowns<!-- [et_pb_line_break_holder] --> // When the user clicks on the button, <!-- [et_pb_line_break_holder] --> // toggle between hiding and showing the dropdown content<!-- [et_pb_line_break_holder] --> function dropdownToggle(id) {<!-- [et_pb_line_break_holder] --> document.getElementById(id).classList.toggle("show");<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> // Layers which include multiple variants and should filtered based on displayed scenario<!-- [et_pb_line_break_holder] --> var scenLayers = [<!-- [et_pb_line_break_holder] --> 'land', 'land2', 'buildings', 'streets', 'trees', 'bridges', 'floors', 'syntax',<!-- [et_pb_line_break_holder] --> 'solar'<!-- [et_pb_line_break_holder] --> ]<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> // List of variants included in the data<!-- [et_pb_line_break_holder] --> var scenNames = ['scen1', 'scen3', 'scen5']<!-- [et_pb_line_break_holder] --> var scenNiceNames = ['Green Loop City', 'Garden City', 'Oceanfront']<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> // Add scenario names to dropdown to chenge scenario filter on click<!-- [et_pb_line_break_holder] --> for (var i = 0; i < scenNames.length; i++) {<!-- [et_pb_line_break_holder] --> var id = scenNames[i];<!-- [et_pb_line_break_holder] --> var name = scenNiceNames[i];<!-- [et_pb_line_break_holder] --> var link = document.createElement('a');<!-- [et_pb_line_break_holder] --> link.href = 'javascript:changeScen("' + id + '")';<!-- [et_pb_line_break_holder] --> link.textContent = name;<!-- [et_pb_line_break_holder] --> var $layerDropdown = document.getElementById('layerDropdown');<!-- [et_pb_line_break_holder] --> $layerDropdown.appendChild(link);<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> // Scenario change - Loops trough list of scenario layers,<!-- [et_pb_line_break_holder] --> // sets variant filter to given scenario name<!-- [et_pb_line_break_holder] --> function changeScen(id) {<!-- [et_pb_line_break_holder] --> for (var i = 0; i < scenLayers.length; i++) {<!-- [et_pb_line_break_holder] --> var layer = scenLayers[i];<!-- [et_pb_line_break_holder] --> filter = map.getFilter(layer); // gets current filter settings<!-- [et_pb_line_break_holder] --> for (var j = 0; j < filter.length; j++) {<!-- [et_pb_line_break_holder] --> if (filter[j][1] == "var") { // Key under which you specify variants<!-- [et_pb_line_break_holder] --> filter[j][2] = id; // change the Value to "id" (scenario name)<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> map.setFilter(layer, filter);<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> // For the cameras you want to save, put its position as CameraOptions here<!-- [et_pb_line_break_holder] --> var cameras = [<!-- [et_pb_line_break_holder] --> {center: [103.836607, 1.272156], zoom: 17, bearing: 136, pitch: 60}, <!-- [et_pb_line_break_holder] --> {center: [103.846564, 1.272998], zoom: 16, bearing: -180, pitch: 60}, <!-- [et_pb_line_break_holder] --> {center: [103.832839, 1.259721], zoom: 15, bearing: 0, pitch: 0}];<!-- [et_pb_line_break_holder] --> var cameraNames = ['Station', 'CBD', 'Island']<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> // Add views to camera dropdown to switch cameras on click<!-- [et_pb_line_break_holder] --> for (var i = 0; i < cameras.length; i++) {<!-- [et_pb_line_break_holder] --> var name = cameraNames[i]<!-- [et_pb_line_break_holder] --> var link = document.createElement('a');<!-- [et_pb_line_break_holder] --> link.href = 'javascript:changeCamera(cameras[' + i + '])';<!-- [et_pb_line_break_holder] --> link.textContent = name;<!-- [et_pb_line_break_holder] --> var $cameraDropdown = document.getElementById('cameraDropdown');<!-- [et_pb_line_break_holder] --> $cameraDropdown.appendChild(link);<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> // View switching<!-- [et_pb_line_break_holder] --> function changeCamera(cameraOptions){<!-- [et_pb_line_break_holder] --> // You can set camera fly speed here<!-- [et_pb_line_break_holder] --> cameraOptions['speed'] = '0.5';<!-- [et_pb_line_break_holder] --> map.flyTo(cameraOptions);<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> <!-- [et_pb_line_break_holder] --> // List of additional functions you want to include as tools<!-- [et_pb_line_break_holder] --> var functions = [<!-- [et_pb_line_break_holder] --> 'toggle3d()', 'toggleFloors()', 'toggleSun()',<!-- [et_pb_line_break_holder] --> 'toggleSyntax()', 'issues()', 'helpPopup()'<!-- [et_pb_line_break_holder] --> ]<!-- [et_pb_line_break_holder] --> var functionNames = [<!-- [et_pb_line_break_holder] --> '☑  3D buildings', '☐  Higher LOD', '☐  Shadow study',<!-- [et_pb_line_break_holder] --> '☐  SpaceSyntax', '☐  Comments', 'Need Help?'<!-- [et_pb_line_break_holder] --> ]<!-- [et_pb_line_break_holder] --> var startToggle = ['On', 'Off', 'Off', 'Off', 'Off', 'On']<!-- [et_pb_line_break_holder] --> // Add functions to dropdown to switch activate on click<!-- [et_pb_line_break_holder] --> for (var i = 0; i < functions.length; i++) {<!-- [et_pb_line_break_holder] --> var id = functions[i];<!-- [et_pb_line_break_holder] --> var name = functionNames[i];<!-- [et_pb_line_break_holder] --> var toggle = startToggle[i]<!-- [et_pb_line_break_holder] --> var link = document.createElement('a');<!-- [et_pb_line_break_holder] --> link.href = 'javascript:' + id<!-- [et_pb_line_break_holder] --> link.textContent = name;<!-- [et_pb_line_break_holder] --> link.id = id<!-- [et_pb_line_break_holder] --> link.classList = toggle<!-- [et_pb_line_break_holder] --> var $layerDropdown = document.getElementById('functionDropdown');<!-- [et_pb_line_break_holder] --> $layerDropdown.appendChild(link);<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> <!-- [et_pb_line_break_holder] --> // 3D buildings toggle<!-- [et_pb_line_break_holder] --> var extrusionHeight = []<!-- [et_pb_line_break_holder] --> map.on("load", function(){<!-- [et_pb_line_break_holder] --> extrusionHeight = map.getPaintProperty("buildings", "fill-extrusion-height")<!-- [et_pb_line_break_holder] --> })<!-- [et_pb_line_break_holder] --> function toggle3d(){<!-- [et_pb_line_break_holder] --> if(document.getElementById("toggle3d()").classList == "On") {<!-- [et_pb_line_break_holder] --> map.setPaintProperty("buildings", "fill-extrusion-height", 8)<!-- [et_pb_line_break_holder] --> map.setPaintProperty('floors', "fill-extrusion-opacity", 0);<!-- [et_pb_line_break_holder] --> document.getElementById("toggle3d()").textContent = "☐  3D buildings"<!-- [et_pb_line_break_holder] --> document.getElementById("toggle3d()").classList = "Off"<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> else if(document.getElementById("toggle3d()").classList == "Off") {<!-- [et_pb_line_break_holder] --> map.setPaintProperty("buildings", "fill-extrusion-height", extrusionHeight)<!-- [et_pb_line_break_holder] --> map.setPaintProperty('floors', "fill-extrusion-opacity", 1);<!-- [et_pb_line_break_holder] --> document.getElementById("toggle3d()").textContent = "☑  3D buildings"<!-- [et_pb_line_break_holder] --> document.getElementById("toggle3d()").classList = "On"<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> // Higher LOD -- Floors toggle<!-- [et_pb_line_break_holder] --> function toggleFloors() {<!-- [et_pb_line_break_holder] --> if(document.getElementById("toggleFloors()").classList == "Off") {<!-- [et_pb_line_break_holder] --> // First check if map isnt zoomed out too far<!-- [et_pb_line_break_holder] --> // If map is far, zoom slowly to proper zoom level<!-- [et_pb_line_break_holder] --> if(map.getZoom() < 15) {<!-- [et_pb_line_break_holder] --> map.flyTo({zoom: 15, speed: 0.5})<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> else {<!-- [et_pb_line_break_holder] --> map.flyTo({zoom: map.getZoom()+0.0001})<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> map.once('zoomend', function() {<!-- [et_pb_line_break_holder] --> map.setMinZoom(15)<!-- [et_pb_line_break_holder] --> map.setPaintProperty("buildings", "fill-extrusion-opacity", 0)<!-- [et_pb_line_break_holder] --> map.setLayoutProperty('floors', 'visibility', 'visible');<!-- [et_pb_line_break_holder] --> document.getElementById("toggleFloors()").textContent = "☑  Higher LOD"<!-- [et_pb_line_break_holder] --> document.getElementById("toggleFloors()").classList = "On"<!-- [et_pb_line_break_holder] --> })<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> else if(document.getElementById("toggleFloors()").classList == "On") {<!-- [et_pb_line_break_holder] --> map.setLayoutProperty('floors', 'visibility', 'none');<!-- [et_pb_line_break_holder] --> map.setPaintProperty("buildings", "fill-extrusion-opacity", 1)<!-- [et_pb_line_break_holder] --> document.getElementById("toggleFloors()").textContent = "☐  Higher LOD"<!-- [et_pb_line_break_holder] --> document.getElementById("toggleFloors()").classList = "Off"<!-- [et_pb_line_break_holder] --> map.setMinZoom(0)<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> // Toggle Sun hours / Shadowstudy<!-- [et_pb_line_break_holder] --> function toggleSun(){<!-- [et_pb_line_break_holder] --> if(document.getElementById("toggleSun()").classList == "Off") {<!-- [et_pb_line_break_holder] --> map.setLayoutProperty('solar', "visibility", "visible");<!-- [et_pb_line_break_holder] --> document.getElementById("toggleSun()").textContent = "☑  Shadow study"<!-- [et_pb_line_break_holder] --> document.getElementById("toggleSun()").classList = "On"<!-- [et_pb_line_break_holder] --> <!-- [et_pb_line_break_holder] --> var popup = new mapboxgl.Popup({closeButton: false, closeOnClick: false})<!-- [et_pb_line_break_holder] --> map.on('mousemove', 'solar', function(e) {<!-- [et_pb_line_break_holder] --> map.getCanvas().style.cursor = 'help';<!-- [et_pb_line_break_holder] --> popup.remove(map)<!-- [et_pb_line_break_holder] --> var features = map.queryRenderedFeatures(e.point, {layers: ["solar"]});<!-- [et_pb_line_break_holder] --> var face = features[0];<!-- [et_pb_line_break_holder] --> var hours = face.properties.hours<!-- [et_pb_line_break_holder] --> popup.setLngLat(e.lngLat)<!-- [et_pb_line_break_holder] --> .setHTML(<!-- [et_pb_line_break_holder] --> '<dd>' + hours + ' hours</dd>' +<!-- [et_pb_line_break_holder] --> '<dt> of sunlight <br>on 1st of August.</dt>'<!-- [et_pb_line_break_holder] --> )<!-- [et_pb_line_break_holder] --> .addTo(map);<!-- [et_pb_line_break_holder] --> })<!-- [et_pb_line_break_holder] --> map.on('mouseleave', 'solar', function(){<!-- [et_pb_line_break_holder] --> map.getCanvas().style.cursor = '';<!-- [et_pb_line_break_holder] --> popup.remove(map);<!-- [et_pb_line_break_holder] --> })<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> else if(document.getElementById("toggleSun()").classList == "On") {<!-- [et_pb_line_break_holder] --> map.setLayoutProperty('solar', "visibility", "none");<!-- [et_pb_line_break_holder] --> document.getElementById("toggleSun()").textContent = "☐  Shadow study"<!-- [et_pb_line_break_holder] --> document.getElementById("toggleSun()").classList = "Off"<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> // Toggle SpaceSyntax<!-- [et_pb_line_break_holder] --> function toggleSyntax(){<!-- [et_pb_line_break_holder] --> if(document.getElementById("toggleSyntax()").classList == "Off") {<!-- [et_pb_line_break_holder] --> map.setPaintProperty('syntax', "line-opacity", 1);<!-- [et_pb_line_break_holder] --> document.getElementById("toggleSyntax()").textContent = "☑  SpaceSyntax"<!-- [et_pb_line_break_holder] --> document.getElementById("toggleSyntax()").classList = "On"<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> else if(document.getElementById("toggleSyntax()").classList == "On") {<!-- [et_pb_line_break_holder] --> map.setPaintProperty('syntax', "line-opacity", 0);<!-- [et_pb_line_break_holder] --> document.getElementById("toggleSyntax()").textContent = "☐  SpaceSyntax"<!-- [et_pb_line_break_holder] --> document.getElementById("toggleSyntax()").classList = "Off"<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> // Feedback toggle<!-- [et_pb_line_break_holder] --> // Switch between icons visible and hidden<!-- [et_pb_line_break_holder] --> function issues() {<!-- [et_pb_line_break_holder] --> if(document.getElementById("issues()").classList == "Off") {<!-- [et_pb_line_break_holder] --> map.setLayoutProperty("wtp-comments", 'visibility', 'visible');<!-- [et_pb_line_break_holder] --> document.getElementById("issues()").textContent = "☑  Comments"<!-- [et_pb_line_break_holder] --> document.getElementById("issues()").classList = "On"<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> else if(document.getElementById("issues()").classList == "On") {<!-- [et_pb_line_break_holder] --> map.setLayoutProperty("wtp-comments", 'visibility', 'none');<!-- [et_pb_line_break_holder] --> document.getElementById("issues()").textContent = "☐  Comments"<!-- [et_pb_line_break_holder] --> document.getElementById("issues()").classList = "Off"<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --> // on hover open popup with text info<!-- [et_pb_line_break_holder] --> issuePopup = new mapboxgl.Popup({closeButton: false, closeOnClick: false})<!-- [et_pb_line_break_holder] --> map.on('mouseenter', 'wtp-comments', function(e) {<!-- [et_pb_line_break_holder] --> map.getCanvas().style.cursor = 'pointer';<!-- [et_pb_line_break_holder] --> var coordinates = e.features[0].geometry.coordinates.slice();<!-- [et_pb_line_break_holder] --> var text = e.features[0].properties.text;<!-- [et_pb_line_break_holder] --> var author = e.features[0].properties.author;<!-- [et_pb_line_break_holder] --> issuePopup.setLngLat(coordinates)<!-- [et_pb_line_break_holder] --> .setHTML('<dt>' + text + '</dt>' +<!-- [et_pb_line_break_holder] --> '<br><dd>' + author + '</dd>'<!-- [et_pb_line_break_holder] --> )<!-- [et_pb_line_break_holder] --> .addTo(map);<!-- [et_pb_line_break_holder] --> })<!-- [et_pb_line_break_holder] --> // close it on hover end<!-- [et_pb_line_break_holder] --> map.on('mouseleave', 'wtp-comments', function() {<!-- [et_pb_line_break_holder] --> map.getCanvas().style.cursor = '';<!-- [et_pb_line_break_holder] --> issuePopup.remove();<!-- [et_pb_line_break_holder] --> })<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> // +++ Other useful functions<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> // Help popup with controls info<!-- [et_pb_line_break_holder] --> function helpPopup() {<!-- [et_pb_line_break_holder] --> window.popup = new mapboxgl.Popup()<!-- [et_pb_line_break_holder] --> .setLngLat(map.getCenter())<!-- [et_pb_line_break_holder] --> .setHTML( //OLD IE COMPATABLE MUTLILINE STRING<!-- [et_pb_line_break_holder] --> '<dd>How can you explore the model?</dd>' +<!-- [et_pb_line_break_holder] --> '<dt>Click on "Select scenario" to choose between different variants' +<!-- [et_pb_line_break_holder] --> ' or click on buildings to display more information about them.</dt>' +<!-- [et_pb_line_break_holder] --> '<dt><br>To control the camera press Control + Left Mouse Button or use' +<!-- [et_pb_line_break_holder] --> ' one of the preset positions with "Views" menu.</dt>' +<!-- [et_pb_line_break_holder] --> '<dt><br>In the "Functions" menu you can hide the 3D geometry' +<!-- [et_pb_line_break_holder] --> ' and change the Level of Detail or show a demo of feedback system.</dt>'<!-- [et_pb_line_break_holder] --> )<!-- [et_pb_line_break_holder] --> .addTo(map);<!-- [et_pb_line_break_holder] --> }<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> // Layer with additional data popup on click<!-- [et_pb_line_break_holder] --> activeLayer = 'buildings'<!-- [et_pb_line_break_holder] --> // change the cursor when hovering over this layer<!-- [et_pb_line_break_holder] --> map.on('mouseenter', activeLayer, function(event) {<!-- [et_pb_line_break_holder] --> map.getCanvas().style.cursor = 'help';<!-- [et_pb_line_break_holder] --> }); <!-- [et_pb_line_break_holder] --> map.on('mouseleave', activeLayer, function() {<!-- [et_pb_line_break_holder] --> map.getCanvas().style.cursor = '';<!-- [et_pb_line_break_holder] --> });<!-- [et_pb_line_break_holder] --> // Open popup with GeoJson properties on click<!-- [et_pb_line_break_holder] --> map.on('click', function(event) {<!-- [et_pb_line_break_holder] --> var features = map.queryRenderedFeatures(event.point, {layers: [activeLayer]});<!-- [et_pb_line_break_holder] --> var building = features[0];<!-- [et_pb_line_break_holder] --> <!-- [et_pb_line_break_holder] --> // Keys you want to include<!-- [et_pb_line_break_holder] --> if (features.length) {<!-- [et_pb_line_break_holder] --> var landUse = building.properties.function <!-- [et_pb_line_break_holder] --> '–';<!-- [et_pb_line_break_holder] --> var height = Math.round(building.properties.height) <!-- [et_pb_line_break_holder] --> '–';<!-- [et_pb_line_break_holder] --> var gfa = Math.round(building.properties.GFA) <!-- [et_pb_line_break_holder] --> '–';<!-- [et_pb_line_break_holder] --> var id = building.properties.build_ID <!-- [et_pb_line_break_holder] --> '–';<!-- [et_pb_line_break_holder] --> <!-- [et_pb_line_break_holder] --> // HTML content of the popup<!-- [et_pb_line_break_holder] --> window.popup = new mapboxgl.Popup()<!-- [et_pb_line_break_holder] --> .setLngLat(event.lngLat)<!-- [et_pb_line_break_holder] --> .setHTML( <!-- [et_pb_line_break_holder] --> '<dl>' +<!-- [et_pb_line_break_holder] --> // '<dt>Land Use</dt>' +<!-- [et_pb_line_break_holder] --> // '<dd>' + landUse + '</dd>' +<!-- [et_pb_line_break_holder] --> '<dt>Building Height</dt>' +<!-- [et_pb_line_break_holder] --> '<dd>' + height + ' m' + '</dd>' +<!-- [et_pb_line_break_holder] --> '<dt>GFA</dt>' +<!-- [et_pb_line_break_holder] --> '<dd>' + gfa + ' sqm' + '</dd>' +<!-- [et_pb_line_break_holder] --> '<dt>ID#</dt>' +<!-- [et_pb_line_break_holder] --> '<dd>' + id + '</dd>' +<!-- [et_pb_line_break_holder] --> '</dl>') //OLD IE COMPATABLE MUTLILINE STRING FORMAT<!-- [et_pb_line_break_holder] --> .addTo(map);<!-- [et_pb_line_break_holder] --> };<!-- [et_pb_line_break_holder] --> });<!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --><!-- [et_pb_line_break_holder] --> </script>

Acknowledgement The project shown in the example above is a result of interdisciplinary international research project done by the students at the Bauhaus-Universität Weimar (BUW).  We are also grateful to the Singapore-ETH Centre teams Multi-Scale Energy Systems for Low Carbon Cities, Engaging Mobility, and Big Data-Informed Urban Design and Governance for contributing their knowledge, experience and time to the design studio.

Pin It on Pinterest