Promethe’s Blog Web, RIAs and chocolate spaghettis…

3Feb/102

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:

  1. The 3D view is engine independant and uses Flash 10 3D maths and "drawTriangles" method if available
  2. The API provides everything one needs to integrate 3D graphics as an overlay using the library of its choice
  3. 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.

Comments (2) Trackbacks (0)
  1. Thanks for this — I knew about the Google 3D view, but I didn’t realize you could insert your own 3D content. That opens up a lot of interesting possibilities…

  2. Indeed. The 3D engine I’m working on will feature a plugin to use this feature.


Leave a comment


No trackbacks yet.