Google Maps 3D Overlay
The Flash Google Maps API offers what they call a 3D map. It's nothing more than a component that enables a perspective view of a classic 2D map with the associated controls. Here is how it works:
- The 3D view is engine independant and uses Flash 10 3D maths and "drawTriangles" method if available
- The API provides everything one needs to integrate 3D graphics as an overlay using the library of its choice
- It works just the same as the good old 2D Google Maps API
The first point is very important. It ensures the library is lightweight and does not include any third party software you wouldn't want to use.
The second is what makes the magic possible: the API exposes the viewport and camera data and provides methods to convert latitude/longitude into 3D world coordinates. With a little math and a few hours of debugging it is then easy to wrap it with the 3D engine of your choice.
The Experiment
You can use CTRL + mouse drag or SHIFT + mouse drag to look around.
The Code
You might want to read the official Google Maps Flash API documentation first, including the dedicated 3D maps section.
The Viewport
The Google Maps API provides the viewport width and height and the focal length. I use the field-of-view instead of the focal length but there is a simple formula to convert from one to the other:
var fieldOfView : Number = 2. * Math.atan(viewportWidth / (2. * focalLength));
Another issue is to find proper values for the near and far clipping planes. I chose 0.000001 for the near plane and 0.2 for the far plane. Those values are extremely low and might cause floating point inconsistencies. The best thing to do would be to scale the whole scene to be able to use reasonable clipping values. Maybe for a later version! This problem does not seem to affect the experiment though...
The following function presents how to get the relevant viewport values from the Google Maps API:
public function buildViewport(myMap : Map3D) : void { var g : TransformationGeometry = myMap.camera.getTransformationGeometry(); var width : Number = g.viewportSize.x; var height : Number = g.viewportSize.y; var fieldOfView : Number = 2. * Math.atan(g.viewportSize.y / (2. * g.focalLength)); // build viewport... }
The Camera
The following function retrieves the camera position and look-at vectors from the Google Maps API:
public function buildCamera(myMap : Map3D) : void { var cam : ICamera = myMap.camera; var mapCenter : Point = cam.latLngToWorld(cam.center); var g : TransformationGeometry = cam.getTransformationGeometry(); var pos : Point3D = g.cameraPosition; var zAxis : Point3D = g.cameraZAxis; var yaw : Number = cam.attitude.yaw * (Math.PI / 180.); var lookAt : Vector3D = new Vector3D(); var eye : Vector3D = new Vector3D(); eye.x = pos.x - 128; eye.y = pos.z; eye.z = 128 - pos.y; if (!zAxis.x && !zAxis.y) { lookAt.x = eye.x + EPSILON * Math.sin(yaw); lookAt.z = eye.z + EPSILON * Math.cos(yaw); } else { lookAt.x = eye.x - zAxis.x; lookAt.z = eye.z + zAxis.y; } lookAt.y = eye.y - zAxis.z; // build camera using eye position, look-at and Vector3D.Y_AXIS as the up vector }
I prefer computing the eye position and look-at vectors and using them to create the transform matrix rather than just creating the matrix directly. This way, you can expose the actual camera parameters (even if in read-only) and still use the transform matrix to do the math.
The Scene
The last step is to make it possible to position 3D objects on the map. What you actually want to do is to be able to add objects specifying their latitude/longitude instead of their actual (x, y, z) coordinates. Then again, the Google Maps API does it just fine:
public function computePosition(myLatitude : Number, myLongitude : Number) : Vector3D { var pos : Point = _map.camera.latLngToWorld(new LatLng(myLatitude, myLongitude)); return new Vector3D(pos.x - 128., 0., 128. - pos.y); }
Know Issues
As said previously, the extremely low far and near clipping plane values might introduce float point inconsistencies. Therefore, some clipping glitches might appear and frustum culling is performed only on the far clipping plane.
Credits
Credits goes to Marc Fahmi for the low-polygon 3D model of the Eiffel Tower.
