Geocoding Earth

Tuesday, April 21, 2009

One of my most complex works yet. This 3D planet maps several Mercator Projections of the Earth (courtesy Nasa). You'll notice a reflection on the surface. (not really necessary but I like the effect).
That is achieved through a papervision EnvMap or Environment Map. A subtle glow effect is added around the earth to add atmosphere.

The coolest part of this app is the Geocoding. You type in any address in the world (include the city name) and it sends it off to google, who kindly returns me a latitude and longitude coordinates. I then project those coordinates using a sphere and placing it at the correct location, thus highlighting the exact address you typed in. (I had help with the formula from http://blog.zupko.info/?p=221 on this part.) Go ahead try it! (click on the image above)

Here's some code for ya!


//i've already loaded bitmap data from external jpg files into local variables cloud_texture, earth_texture, and bump_texture

var earth_Shader:GouraudShader = new GouraudShader(light, 0xFFFFFF, 0x111111, 30);

//EnvMap gives us our reflection, the bump_texture gives us the small peaks and valleys on the earth
var earth_env:EnvMapShader = new EnvMapShader(light, cloud_texture, earth_texture, 0x0034c1, bump_texture);
var earth_mat:ShadedMaterial = new ShadedMaterial(new BitmapMaterial(earth_texture), earth_env, 0);

//let there be stars!!
var stars:ParticleField = new ParticleField(new ParticleMaterial(0xFFFFFF, .4, 1), 500);
scene.addChild(stars);

var earth:Sphere = new Sphere(earth_mat, 50, 25, 25);
scene.addChild(earth);

//this accomplishes the blurred glow effect around our 3D Earth
var bfx:BitmapEffectLayer = new BitmapEffectLayer(viewport, 1000, 1000, true, 0, "CLEAR_POST", false, true);//very important to set the last param to true! Otherwise the glow will continue to concatenate to itself! AHHH!

var gf:GlowFilter = new GlowFilter(0x00aaf6, .2, 5, 30, 2, 1, false);
var ble:BitmapLayerEffect = new BitmapLayerEffect(gf);
bfx.addEffect(ble);
viewport.containerSprite.addLayer(bfx);
bfx.addDisplayObject3D(earth);


//GeoCoding Functions
//I have this built into its own separate class

//Google makes you use their API. I tried over and over to make a simple URLRequest to get the coords...it worked just great locally, but as soon as I posted it to the server..nothin.



import com.google.maps.Map;
import com.google.maps.MapEvent;
import com.google.maps.services.*

//You only get access to the geocoder once a new Map Object has been instantiated
public function GPS(stage:*){
map = new Map();
map.key = "my_google_maps_api_key_here";
map.addEventListener(MapEvent.MAP_READY, onMapReady);
map.visible = false;
stage.addChild(map);

function onMapReady(event:Event):void {
trace(map);
}
}

//when user clicks on the find button, receive address as string, and call back function for when processing is complete.
public function Get_Coords_From_Address(address:String, callBack:Function) {

//format the address for google maps api
var temp:Array = address.split(" ");
address = temp.join("+").toString();

//call their geocoder, it just makes a URLRequest with special params
var geocoder:ClientGeocoder = new ClientGeocoder();
geocoder.addEventListener(GeocodingEvent.GEOCODING_SUCCESS, handleGeocodingSuccess);
geocoder.geocode(address);

//geocode received successfully now parse out latitude and longitude and return for mapping!
function handleGeocodingSuccess(e:GeocodingEvent) {
var t1:int = e.response.placemarks.toString().indexOf("@(") + 2;
var t2:int = e.response.placemarks.toString().indexOf(")");
var data:String = e.response.placemarks.toString().substring(t1, t2);
callBack(data);
}


//here is the callback function
function callBack(data:String){
trace(coords);
var lat:Number = parseInt(coords[0]);
var lon:Number = parseInt(coords[1]);

//convert_coords_to_rotation(lat, lon);
var phi:Number = (90-lat)*(Math.PI/180);
var theta:Number = (lon+180)*(Math.PI/180);

//create the marker
var marker:Sphere = new Sphere(new ShadedMaterial(new BitmapMaterial(new BitmapData(20,20,false,0x00aaf6)), new PhongShader(light, 0x00aaf6)),1,5,5);

//figure the spatial coords in which the sphere should be placed (will be placed directly over the address that the user entered..code courtesy http://blog.zupko.info/?p=221)
var fx:Number = (earth_radius+3) * Math.sin(phi)*Math.cos(theta);
var fz:Number = (earth_radius+3) * Math.sin(phi)*Math.sin(theta);
var fy:Number = (earth_radius+3) * Math.cos(phi);

earth.addChild(marker);
bfx.addDisplayObject3D(marker);

marker.x = 0;
marker.y = 300;
marker.z = 0;
marker.alpha = 0;
//animate the sphere into place from 300 pixels north
TweenMax.to(marker, 2, { x:fx, y:fy, z:fz, ease:Regular.easeOut, alpha:1 } );
}

1 comments:

Technologicguy said...

Kyle this is really cool. It reminds me of the 3D solar system that I'm writing in OpenGL. Obviously our approaches are a lot different, I know you've done well on this.

How about making another version that allows you to choose between other planets? Great work man!

Post a Comment