Geolocation in Papervision3D – Plus some Quaternion Stuff


Positioning things in 3D can be difficult if you don't have a good understanding of, well, 3D.  I often focus on cool new experiments, or some new feature I've added to the repository, but after getting some questions about positioning things on a globe, I decided to take a step back and do something more functional and basic than normal.  Don't worry, I've put my usual spin on things, so hopefully there will some fun stuff you can pick up from this demo as well if you already know how to use spherical coordinates.

Check out the demo.  It will show you where I live (not too specifically), then where you live in relation.  If it can't find you, it will pick a random spot on the globe... but that hasn't happened yet.

I have divided this post into what i consider to be the main points of interest of this demo.  It isn't as sequential as normal, but hopefully will help you jump to something you are interested in and read specifically about that.  Believe it or not, this little demo has a lot packed into it.

Geolocation

Geolocation simply means we find where you currently are in the world.  This demo uses your IP address to figure it out.  I'm using the free, and incredibly accurate GeoLite City database and php module to figure out what city (or area) you are currently in.  This is all done on the server side, but don't worry, I have included the php I used to get the information that is passed into flash.  As a fallback, I have left in my original method, www.hostip.info - an online API that has flakey results and often has no information.  The XML returned is unparsable by flash, so you will notice in my code there is some interesting workarounds to format the XML correctly.  I've left it in just in case, and as a reference, but I woudln't recommend it for people to use on anything commercial.  GeoLite City on the other hand is fantastic, and the full version is even more complete.  There is also IP2Location, which doesn't have a free version, but seems to have the lions share of commercial geolocation.

GeoLite City includes the most pertinant information we need for geolocation in this application, which is longitude and latitude.  Once we have those two peices of information, we can figure out where that point is in both 2D and 3D space.  Keep in mind, these equations only work with equirectangular projections, so if your map has a different projection, you will need to use the correct formulas.

Adding a Marker in 2D

You will notice in the demo that under the 3D marker there is a bulls eye added to the texture.  I put this in for all those who don't want 3D markers, but would rather like to know how to add a marker in 2D.

Actionscript:
  1. var w2:Number = movieMaterial.rect.width/2;
  2. var h2:Number = movieMaterial.rect.height/2;
  3.  
  4. var m:Bitmap = new bullseye() as Bitmap;
  5. textureContainer.addChild(m);
  6.  
  7. m.y = (90-lat)/90*h2-m.height/2;
  8. m.x = (lon+180)/180*w2-m.width/2;
  9.  
  10. m.alpha = 0.5;
  11.  
  12. movieMaterial.drawBitmap();

You will notice that the first thing done is to find half the size of our world texture.  This will let us put the lon/lat in the same scale the texture is in.  The longitude and latitude we recieve range from (-180, 180) and (-90, 90).  This code: (90-lat)/90 gives us a percentage our latitude is from the top of the sphere.  We then multiply that by half the texture height to bring it into our 2D coordinates.  We then do the same thing to find the longitudes 2D coordinate.  I subtract half the bulls eye's width and height to center it on the postion.  The last thing we do is update the earth material, movieMaterial, so the texture changes become visible.

Adding a Marker in 3D

More people are probably interested in how to add a marker in 3D space, as it is a little more involved.  But keep in mind that the general concept is the same as adding a marker to 2D.  To begin, we simply get the spherical coordinates:

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

phi represents the angle from the top of the sphere to the current position.  theta is how far "around" the sphere our point is.   Both are in radians so we can use them for the next part: converting spherical to cartesian coordinates:

marker.x = (earthRadius+10) * Math.sin(phi)*Math.cos(theta);
marker.z = (earthRadius+10) * Math.sin(phi)*Math.sin(theta);
marker.y = (earthRadius+10) * Math.cos(phi);

The common misconception that people have is that if you know how far from the top, and how far around the radius a point is, you can simply use something like sin and cos on the XY and then XZ planes.  Don't fall for it - it doesn't work.  The three dimensions are tied together - and when dealing with spherical coordinates (rather than 2 planes of circular), you have to consider all 3.

Figuring Out The Rotation

I enjoy math - if you haven't figured it out over the past year of my blog - so this part of the demo is something I really enjoyed working on.  I used quaternions for this application, partly because I enjoy using them, and partly because it was a good opportunity to show some additional techniques for using quaternions.

I don't really feel like doing a complete math tutorial, so here is all I will say about finding the angles:

  • cross = perpendicular vector
  • dot = scalar magnitude
  • acos(dot)  = angle

Now that angles is taken care of, take a look at the getRotatedQuat function:

Actionscript:
  1. private function getRotatedQuat(axis:Number3D, startQuat:Quaternion, angle:Number, resultOffset:Number):Quaternion{
  2.  
  3. var rotQuat:Quaternion = Quaternion.createFromAxisAngle(axis.x, axis.y, axis.z, angle);
  4. rotQuat.normalize();
  5.  
  6. var result:Quaternion = Quaternion.multiply(Quaternion.multiply(rotQuat, startQuat), Quaternion.conjugate(rotQuat));
  7.  
  8. ...

You notice that we have an axis we want to rotate around, the starting Quaternion (representing our start vector), and how far around the axis we want to go from that start point.  Quaternions make rotating vectors pretty easy, so don't be discouraged if you aren't sure what the code above is talking about.  I'm going to try and make it a little more clear.

You might be wondering about the startQuat, which is defined as:

var startQuat:Quaternion = new Quaternion(marker1.x, marker1.y, marker1.z, 0);

You will notice that this quaternion is representing a vector, rather than a rotation.  I don't want to get into some crazy proofs, but to put it simply, you can represent a vector, or a point, with a quaternion - and this is known as a pure quaternion.  When you multiply a pure quaternion with a rotation quaternion and its inverse (also equal to the conjugate for rotation quaternions), you get another pure quaternion out of it.  This resulting pure quaternion is the now rotated vector.

Lets put it into a form that is easier to read:

The first thing we do is convert our axis of rotation and amount of rotation (angle) into a rotation quaternion.  We will call it R from now on.  Once we have R, we can use it with our pure quaternion (startQuat), which i will call V, to get a rotated vector from V's initial position.  The formula to do this is:

result = R*V*R`

You might be wondering, why do I have to use R` in this equation.  Simply put, by multiplying the inverse of the rotation, we can get our resulting quaternion back into a "pure" state.  Now, we have a quaternion that contains our rotated vector!  You will notice I store these in an array, which then define the bezier curve that my dotted line follows.  Keep in mind there are other ways you an derive these intermediate points, but this way is fun and hopefully you have learned something useful!

In Conclusion

I think that is enough for now - you can look over the code for any other things that might be useful.

Check out the demo

and

Get the Source

As A Side Note

There is a competition for you to compete in!  Jim Foley over at http://www.madvertices.com is hosting a Pv3D competition with some nice prizes for the guy(s) with the coolest soccer ball related application.  You can check out the competition details at http://madvertices.yuku.com/topic/11 - it should be fun!  Thanks for putting that together Jim!


30 Comments, Comment or Ping

  1. Very nice. Keep going this way :) .

    More Quaternions!

    Thanks!

    January 8th, 2009

  2. Incredibly useful demo. Thanks again Andy.

    January 8th, 2009

  3. slopester

    real nice – thanks Andy

    January 8th, 2009

  4. In order to make the source run with the most recent version of Tweener, add this import:

    import caurina.transitions.properties.CurveModifiers;

    and then add this line to the GeoLocate() constructor:

    CurveModifiers.init();

    Besides that, you’ll need earthmap2k, which google told me I could get here: http://www.rikkertdekoe.nl/andre/Work/JavaScript/3DEarth/Big/earthmap2k.jpg and some png image to use as bullseye.png; I grabbed a random one from a google image search.

    I’m working on two things: the beckground is grey in the source and the 3d marker is placed to the right of each bullseye.

    (I haven’t tried to use his php yet, so I can’t comment on its status)

    January 8th, 2009

  5. @Bill – Thanks for pointing out the markers are too the right – i had fixed the sphere class (its UV were actually wrong) so that it is positioned correctly. Update your PV3D svn and you should be fine!

    Thanks for the tip for tweener as well.

    January 8th, 2009

  6. Oh yeah I forgot about papervision! Does Mouse3d compile properly for you, Andy? I had to revert r804 [1] in order to make papervision compile.

    Your fix for sphere.as fixes the bullseyes for me; thanks! I would have spent a lot of time in your code before I suspected papervision of being broken, I think.

    Thanks for this!

    [1]: http://code.google.com/p/papervision3d/source/diff?spec=svn804&r=804&format=side&path=/trunk/as3/trunk/src/org/papervision3d/core/utils/Mouse3D.as&old_path=/trunk/as3/trunk/src/org/papervision3d/core/utils/Mouse3D.as&old=728

    January 8th, 2009

  7. Thats exactly what I did. I coudlnt’ figure out what was wrong with my math – finally figured out sphere was the problem itself. Probably wasted a good 4 hours on that issue.

    January 8th, 2009

  8. Hey guys, the entries are in for the Papervision 3D Contest #1 : Soccer

    Check it out here: http://madvertices.yuku.com/topic/17

    Please vote for your favorite!

    February 2nd, 2009

  9. Will Jutsum

    Damn I could have done with this article when we were building:

    http://www.seaofthought.com/

    February 15th, 2009

  10. richard

    Hello,

    Sorry but o’m a beginner in papervision 3D….

    I’m using flash cs3.

    I have a problem to do the marker in 2d…
    is it possible to have the .as Basicview and fla for this work.

    Thx
    Richard

    February 17th, 2009

  11. richard

    Ok… i have ve found the problem… [Embed] doesn’t work in FLASH API…. only for FLEX. I have used a simple movieclip linked in my libray.

    how do you do in order to place the good map at the right place?? Do you have the good map of the earth?
    Thx

    February 17th, 2009

  12. aditya

    hi andy
    can you show how to make an fps(first person shooter) camera using quaternions in papervision. i will be really grateful to you.
    thank you

    April 7th, 2009

  13. Hi Andy,

    Tried using your code but it did not work, I get the following errors:

    Vertices3d.as, Line 144 1061: Call to a possibly undefined method getPosition through a reference with static type org.papervision3d.core.geom:Vertex3D.

    Sphere.as, Line 1 5006: An ActionScript file can not have more than one externally visible definition: Circle, Sphere

    I’m using the following version of pv3d: 2.0.883

    Any idea where I am going wrong?

    Bob

    May 14th, 2009

  14. It work correctly and it very useful.

    Thanx.

    July 19th, 2009

  15. Nick

    So how do we use this? Is it for Flash only or can it be used in Flex?

    I get compile errors and stuff. Lots of them. The source code seems kind of incomplete.

    July 31st, 2009

  16. vermut

    Your site is super! Very interesting and useful information, which is rarely found where. All available written Thank you! Continue in the same spirit! Unfortunately I do not got, configure the demo. Weácan: in your archive “geolocate.zip”, in the file “index.php” is present import scripts ..

    Could you obsnit their origin.

    P.S. sorry for the fractured English.

    October 12th, 2009

  17. Thanks for the math! I’m working on a personal project that includes plotting points on a globe. I’ll let you know when I have something so you can check it out.

    October 15th, 2009

  18. Adydas

    Very nice article. Quick question though, what would the calculation look like to get the lat,lon from an x,y mouse position. Tried doing some of my own calculations but then realized I hate math =)

    November 4th, 2009

  19. Hey. Would you be prepared to spend a few minutes helping me with some math? I am working on extending Joshua Davis’s Hype framework so it works with Papervision in 3D and I am struggling with the math in this ‘Swarm’ experiment (http://rezzynet.com/swarm/Main.html). Basically I need to adjust the code so the objects head towards a point in 3D space but they need to follow an orbit to get there. I’d really appreciate some help with this. You can skype me at lee.probert or email me directly.

    November 5th, 2009

  20. Adydas

    For those of you interested Andy helped me out in reversing the calculation so that you can get a lat,long from an x,y position on the map

    Here it is

    h2 = (height of texture)/2
    w2 = (width of texture)/2
    pX = X position on texture
    pY = Y position on texture

    var lat : Number = (h2-pY)/h2 * 90;
    var lon : Number = (pX)/w2 * 180 – 180;

    November 6th, 2009

  21. thank you !

    very interessant and useful, i’ll use it in few times.

    December 28th, 2009

  22. michabre

    Regarding the error: 1061: Call to a possibly undefined method getPosition through a reference with static type org.papervision3d.core.geom:Vertex3D

    I did the reinstall of papervision several times, but I found that by removing my Classpaths to my own custom class directories under Preferences > Actionscript 3 Settings did the trick.

    January 6th, 2010

  23. Andy you rock. You are so awesome, and I like the point of view and source you share. Will you be my God.

    January 23rd, 2010

Reply to “Geolocation in Papervision3D – Plus some Quaternion Stuff”