# Drawing 3D Objects and Buildings on Google Maps

As a web developer for a school, I’ve always enjoyed the challenge of creating a good campus map. The school I work for has a growing online seminary program to train pastors around the world, but helping people get around the main campus in Dallas is a still an integral part of any school.

TL;DR: Final result: DTS “3D” campus map

## Back Story: The Old Map

Several years ago, I wanted to make an interactive 3D building map, so I learned Papervision 3D a powerful Flash-based 3D engine (papervision map). I really liked it at the time but now that Flash is largely out of the picture, it was time to replace the map. I’ve wanted to port the old map to Three.js a JavaScript based 3D engine, but I found that conversion wasn’t as easy as I thought (three.js experiment).

## To Google Maps

Since I also want to give first-class support to mobile devices, I decided to switch gears to Google Maps API since it runs really well on phones and tablets now. The problem was finding a way to display the map in an interesting and clear way.

### Attempt #1: Flat polygons

My team and I drew out the floor line of several buildings using Google’s Polygon tool with editable:true turned on, then we created a little loop to draw them all at once. It looks great, but the problem is that it’s not really clear that we’re showing buildings and other than color, it’s hard to tell what’s a parking lot.

var mapCenter = [32.794488, -96.780372], mapOptions = { zoom: dts.maps.current.campus.zoom, center: new google.maps.LatLng(mapCenter[0], mapCenter[1]), mapTypeId: google.maps.MapTypeId.SATELLITE }, map = new google.maps.Map(document.getElementById("map"), mapOptions), buildingCoordinates = [ [32.7948702128665,-96.78071821155265], [32.79478789915277,-96.780713852963], [32.794745606448785,-96.7807638091059], [32.79465594788115,-96.78065450908855], [32.794983444026556,-96.78026383449742], [32.79507153522898,-96.78037564908573], [32.79502490803293,-96.78043113728472], [32.79502635107838,-96.78054211368271] ], polygonCoords = []; for (var j=0; j<buildingCoordinates.length; j++) { polygonCoords.push(new google.maps.LatLng( buildingCoordinates[j][0], buildingCoordinates[j][1] )); } var polygon = new google.maps.Polygon({ paths: polygonCoords, strokeColor: '#ee1111', strokeOpacity: 0.6, strokeWeight: 1, fillColor: '#eeeeee', fillOpacity: 0.7 }); polygon.setMap(map);

### Attempt #2: Roof

The next step was trying draw a floating roof simply by copying the the coordinates and adding a little bit to the latitude to make it seem like it was “up in the air.” The result doesn’t really make sense, but it starts to give some building like feeling:

### Attempt #3: Drawing a single “Wall”

My next thought was to remove the floor and then draw to draw a single wall on the South side to make it look like a wrap around wall. To do this, I looked through the coordinates and found the western and eastern edge and then tried to draw along it. It worked in many places, but in complex buildings it looked a little strange since there is only one “south” wall.

### Attempt #4: Drawing individual Walls

To fix the problem of complex buildings, I decided to draw a polygon for each wall. This involves taking each floor coordinate and making a pair with the next one and then stretching it upward. Here’s what my new function looks like

function drawExcrudedShape(map, coordinates, height, strokeColor, strokeOpacity, strokeWeight, fillColor, fillOpacity) { var pairs = [], polygons = []; // build line pairs for each wall for (var i=0; i<coordinates.length; i++) { var point = coordinates[i], otherIndex = (i == coordinates.length-1) ? 0 : i+1, otherPoint = coordinates[otherIndex]; pairs.push([point, otherPoint]); } // draw excrusions for (var i=0; i<pairs.length; i++) { var first = pairs[i][0], second = pairs[i][1], wallCoordinates = [ new google.maps.LatLng(first[0],first[1]), new google.maps.LatLng(first[0]+height,first[1]), new google.maps.LatLng(second[0]+height,second[1]), new google.maps.LatLng(second[0],second[1]) ], polygon = new google.maps.Polygon({ paths: wallCoordinates, strokeColor: strokeColor, strokeOpacity: strokeOpacity, strokeWeight: strokeWeight, fillColor: fillColor, fillOpacity: fillOpacity zIndex: zIndexBase+i }); polygon.setMap(map); polygons.push(polygon); } return polygons; }

Here is the result:

This looks much better, but now we have two problems. First, some walls incorrectly overlap since I haven’t explicitly told Google the correct order to draw them in z-index problem. Second, if you were to rotate the map 180 degrees (see below), the buildings would be upside-down. This is because I’m not checking which wall is the southern most or the direction of the map.

### Attempt #5: Re-Ordering the Walls

So in my final attempt, I’ve taken the pairs above and ordered them based on the Google’s heading (map.getHeading()). This allows me to figure out which way is “up” and correctly layer the walls so that they look like real 3D objects. Here’s the final function and map result:

function drawExcrudedShape(map, coordinates, height, zIndexBase, heading, strokeColor, strokeOpacity, strokeWeight, fillColor, fillOpacity) { var pairs = [], polygons = []; // build line pairs for (var i=0; i<coordinates.length; i++) { var point = coordinates[i], otherIndex = (i == coordinates.length-1) ? 0 : i+1, otherPoint = coordinates[otherIndex]; pairs.push([point, otherPoint]); } // sort the pairs based on which one has the "lowest" point based on the heading pairs.sort(function(a, b) { var aLowest = 0, bLowest = 0; switch (heading) { case 0: aLowest = Math.min(a[0][0], a[1][0]); bLowest = Math.min(b[0][0], b[1][0]); if (aLowest < bLowest) { return 1; } else if (aLowest > bLowest) { return -1; } else { return 0; } case 90: aLowest = Math.min(a[0][1], a[1][1]); bLowest = Math.min(b[0][1], b[1][1]); if (aLowest < bLowest) { return 1; } else if (aLowest > bLowest) { return -1; } else { return 0; } case 180: aLowest = Math.max(a[0][0], a[1][0]); bLowest = Math.max(b[0][0], b[1][0]); if (aLowest > bLowest) { return 1; } else if (aLowest < bLowest) { return -1; } else { return 0; } case 270: aLowest = Math.max(a[0][1], a[1][1]); bLowest = Math.max(b[0][1], b[1][1]); if (aLowest > bLowest) { return 1; } else if (aLowest < bLowest) { return -1; } else { return 0; } } }); // draw excrusions for (var i=0; i<pairs.length; i++) { var first = pairs[i][0], second = pairs[i][1], wallCoordinates = null; switch (heading) { case 0: wallCoordinates = [ new google.maps.LatLng(first[0],first[1]), new google.maps.LatLng(first[0]+height,first[1]), new google.maps.LatLng(second[0]+height,second[1]), new google.maps.LatLng(second[0],second[1]) ]; break; case 90: wallCoordinates = [ new google.maps.LatLng(first[0],first[1]), new google.maps.LatLng(first[0],first[1]+height), new google.maps.LatLng(second[0],second[1]+height), new google.maps.LatLng(second[0],second[1]) ]; break; case 180: wallCoordinates = [ new google.maps.LatLng(first[0],first[1]), new google.maps.LatLng(first[0]-height,first[1]), new google.maps.LatLng(second[0]-height,second[1]), new google.maps.LatLng(second[0],second[1]) ]; break; case 270: wallCoordinates = [ new google.maps.LatLng(first[0],first[1]), new google.maps.LatLng(first[0],first[1]-height), new google.maps.LatLng(second[0],second[1]-height), new google.maps.LatLng(second[0],second[1]) ]; break; } var polygon = new google.maps.Polygon({ paths: wallCoordinates, strokeColor: strokeColor, strokeOpacity: strokeOpacity, strokeWeight: strokeWeight, fillColor: fillColor, fillOpacity: fillOpacity, zIndex: zIndexBase+i }); polygon.setMap(map); polygons.push(polygon); } return polygons; }

### Final Map

Here is the final result. We’ve changed the parking lots to just have a colored border to help people know where to park and the full map has some interactivity on the buildings, lots, and departments. Go give it a try!

Thanks to Google Maps Mania for the kind words here and here.

Wow. That is impressive my friend. Good work

This is really cool. What’s the license on your code?

These Maude Mustard Flats embody everything I love: bow-tie detailing, cute patterns,

vegan leather, and not to mention, it’s honey yellow.

While Lv harness or even bandage is definitely aswell the best permanently allocation in order to your ex.

Daphne’s Hollywood (Herve Leger) because of its close to the body line “bandage” Herve Leger

Bandage design and famous.

Feel free to surf to my blog: boutique dresses wholesale

We’re a group of volunteers and starting a new scheme in our community.

Your website provided us with valuable information to work

on. You’ve performed a formidable task and our whole community will probably be

grateful to you.

All natural, completely safe, along with appetite suppressing are just a couple of

perks you might find after you work with the

HCG for fat loss. Just because HCG drops cost more does not mean that they are better

than the more reasonably priced product that can be as much as half the cost.

Real HCG diet plans can add elasticity to your skin.

Feel free to visit my blog post; weight loss liquid hcg

5 to frontt layout fսll and stuck double pike іn crosstumbling.

” People the world over go onto a new diet and find out after a few days nothing is happening, they check and find they are the same weight as when they started. With nothing else around but the weights and machines, there is nothing there to distract you, and it becomes much easier to focus on the training session.

Fantastic beat ! I wish to apprentice at the same time as you amend your

web site, how could i subscribe for a blog website?

The account helped me a appropriate deal. I were a little bit familiar of this your broadcast provided shiny clear concept

Un discours de mariage rédigé en bon français,

foncez!

Why viewers still use to read news papers when in this technological world everything is

existing on net?

Legal o seu site, continue assim! Tem iPhone?

A tela quebrou? Se você for de Salvador, procure a iPhoneSSA, troca de tela de iPhone

em Salvador com o melhor preço.