 |
NASA Maps in your Swing App
Posted by joshy on October 12, 2006 at 10:35 AM | Comments (19)
Short short version:

Not Awesome

Awesome!
Short version
You can now embed some NASA map servers into your own Swing apps using the JXMapViewer component in SwingX-WS.
Long version
For the Aerith demo we showed at JavaOne we build a component that can embed Google Maps into a Swing application. Using the Google service in the way we were doing violates their terms of service and though Google would like to work with us it would be difficult to change the licensing on the map data to fit our needs. This is why, in my previous blog, we shipped the JXMapViewer component with a dummy TileFactory and showed how to plug in your own local data. This is interesting but not very useful. There is no conversion between local tiles and lat/long coordinates so you can't draw waypoints over the map in any meaningful way. Shipping a component which doesn't do anything useful out of the box does not make for a compelling library. It's time for that to change.
I have been investigating how to hook into one of NASA's map servers for quite some time. They use a completely different mechanism for retrieving images and, quite frankly, I don't really understand how all of this mapping stuff works. Mapping involves a lot of complicated equations that project real world data, ie: maps and photos of the earth, onto a flat surface of some kind. We created this component to specifically hide the complexity of mapping, but someone has to write that component and those people were Rich, Romain, and I. I created the mapping equations for the original demo by looking at examples on the web. I copied and translated the code but I didn't really understand them. Finally, last weekend, I figured it out. Thanks to these two great weblog entries by Kyle Mulka and Charlie Savage I can now hook NASA's server into the JXMapViewer component.
How it works
Google's map server divides the entire world up into equal sized tiles which you reference with an x/y coordinate. This is Google's own design and it makes the client side of mapping very easy, pushing all of the hard work onto their servers (I've heard they have quite a few of them:). NASA uses a map server specification called Web Map Service or WMS.
Instead of using tiles, WMS lets you request a rectangular image of the world expressed as two lat/long coordinates. The JXMapViewer thinks in terms of tiles, so if you can convert between the two systems you can show the NASA data in the map viewer. And that is exactly what the new classes below do:
WMSService wms = new WMSService();
wms.setLayer("BMNG");
wms.setBaseUrl("http://wms.jpl.nasa.gov/wms.cgi?");
TileFactory fact = new WMSTileFactory(wms);
map.setFactory(fact);
The WMS web service is represented, creatively, by the WMSService class. You can set the BaseUrl for the web service and the layer with properties on the WMSService instance. The BaseURL is the URL where the service resides minus any parameters. The layer represents a particular portion of the data available from this map service. In general, these two pieces of information are all you need to connect to a WMS server.
Most WMS servers have several layers, usually a single base layer and several data layers. The base layer usually covers the entire world and the data layers are transparent data sets which can overlaid on top of the base layer. This demo just uses the NASA base layer, often called the Blue Marble. In a future version of the API we will add support for multiple layers at a time.
Once you have the WMSService instance you can wrap it in a new WMSTileFactory and set it on the map. That's it! The WMSTileFactory knows all about the Mercator projection that will convert WMS requests into the tile based coordinate system that JXMapViewer understands.
If you want to try it out take a look at this webstart app.
Just before I posted this app I started having problems connecting to NASA's server.
I will continue working on the problem, but in the mean time I have set it to connect to the map server at wms.lizardtech.com, which contains the same data set

In this application I have loaded up the NASA map and then overlaid three waypoints. Each waypoint represents a weather station. Using a library I wrote a while back (available in the weatherlib project on Java.net) you can easily get the weather from any weather station in NOAA's weather.gov network. There XML feeds also provide the lat/long of most weather stations, which makes it easy to draw them on the map. In this example application I have added a waypoint for the airport weather stations in Atlanta, Georgia; San Francisco, California; and Eugene, Oregon. The code looks like this:
WaypointMapOverlay overlay;
/** Creates new form MainFrame */
public MainFrame() {
// .... set up the gui here
overlay = new WaypointMapOverlay() {
protected void paintBackground(Graphics2D g, JXMapViewer component) {
}
protected void paintWaypointSummary(Graphics2D g,
JXMapViewer map, Waypoint waypoint) {
}
};
map.setMapOverlay(overlay);
try {
addWeatherWaypoint("KEUG");
addWeatherWaypoint("KSFO");
addWeatherWaypoint("KATL");
} catch (Exception ex) {
ex.printStackTrace();
}
}
class WeatherWaypoint implements Waypoint {
private Weather weather;
public WeatherWaypoint(Weather weather) {
this.weather = weather;
}
public GeoPosition getPosition() {
return new GeoPosition(weather.getLatitude(), weather.getLongitude());
}
public Weather getWeather() {
return weather;
}
}
private void addWeatherWaypoint(String station) throws Exception {
final Weather weather;
weather = WeatherFactory.newInstance().getWeather(station);
overlay.getWaypoints().add(new WeatherWaypoint(weather));
map.repaint();
}
In the code above there are two things to notice. First, I have created an instance of a WaypointMapOverlay which adds nothing to the drawing, but inherits the default drawing code for waypoints. (I admit this is cumbersome. The overlay API needs an overhaul). The other thing to notice is the addWeatherWaypoint(String station) method which creates a new waypoint for the requested station. To get the weather it just calls WeatherFactory.newInstance().getWeather(station), where station is the four character name of the weather station. (see this page for a full list of available stations. Note that some stations don't have lat/long data available).
With this code it will draw a circle for each weather station. Just drawing a circle isn't very interesting, however. It would be much nicer to actually show the current temperature with a transparent overlay. You can do this by adding a WaypointRenderer:
overlay.setRenderer(new WaypointRenderer() {
public boolean paintWaypoint(Graphics2D g, JXMapViewer map, Waypoint waypoint) {
// make a copy of the graphics object
g = (Graphics2D) g.create();
// convert to map space
Point2D center = map.getTileFactory().getBitmapCoordinate(
waypoint.getPosition(), map.getZoom());
Rectangle bounds = map.getViewportBounds();
// translate 0,0 to be the center of the waypoint
g.translate(center.getX()-bounds.getX(), center.getY()-bounds.getY());
// get the weather
Weather weather = ((WeatherWaypoint)waypoint).getWeather();
// draw the waypoint
g.setColor(new Color(255,255,255,175));
g.fillOval(-20,-20,40,40);
g.setColor(Color.BLACK);
g.drawString(""+weather.getTempF(),-15,5);
return false;
}
});
Caveats and known issues.
First some caveats about NASA's map server. This particular server only has data within a certain range. If you zoom in too far you will start to see pixelation. Also the component itself and the WMS conversion still need some work. The tiles are slow to load and sometimes fail completely. There is some distortion at the top-most zoom level and the tiles along the edges of the map fail due to some rounding errors. Clearly there is more work to do, but this is a good start. (hint hint, consider joining in and contributing patches). Most importantly, we can ship a component that does something useful right out of the box.
So what is it good for?
In addition to the photo demo we created in Aerith, there are lots of cool things you can do with easy to use geo-mapping. The overlay API lets you draw anything you want on top of the map, including waypoints and geo-polygons, so we can imagine applications like:
- show the timezones of the world graphically
- show the trails that you have hiked with your gps receiver
- manually draw a trail on the map and produce a list of geopositions out of it
- draw zipcode and area code boundaries of your local area
- show where photos were taken and make it searchable to show by date or keyword
(I think that Fabrizio is working on something like this)
- combine with census data to show population by zip across the US
- show active volcanoes and black bear popuplations. (yes, you really can download datasets for this stuff.)
Basically any set of lat/long coordinates can be drawn on top of the map. With the power of Java2D at your fingertips you can make rollovers, translucent region overlays, or any other crazy visualization imaginable. And because the WMSService class works with any WMS map server you can hook it up to many different map sources. Some creative Googling turns up lots and lots and lots of WMS servers out their, each with their own data. I can imagine some very interesting applications made by combining several WMS sources with other kinds of webservices.
We help and ideas
Don't just download the demo. Try out the component in your own apps and let us know what you think. If you make something interesting then please send us a link and let us know if we can mention it on our website or feature it on Java.net. Please also join the forum and mailing list. We need patches, feedback, and new ideas.
You can find the JXMapViewer and other great Swing tools for working with web services at the SwingX-WS project. The new WMS support is in CVS on the new_tile_provider branch.
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
Hi Joshua.
Yep, blueMarine EA6 does show the recorded trails and places the photos in the right points, even though the graphics is very essential (but I've just downloaded Romain's "extreme makeover GUI..." and I'm going to study it in the weekend :-). The problem with EA6 is that it's a bit too unstable even for being a EA (in facts I didn't publicise the release). But EA7 should be a matter of days.
BTW blueMarine wraps your TileFactory stuff in a NetBeans module, so adding the new NASA provider should be just a matter of minutes (I originally adapted your code with the static NASA images, but at this point I'll remove it).
Question 1: in your opinion is it possible / easy at this point to write code to drag waypoints around the map? My latest three years of photos pre-GPS were manually tagged with the name of places, and thanks to the Yahoo! GeoCoding interface of SwingLabs I was able to perform a batch operation that assigned "coarse" geotags to them (that is it assigned the coordinates of the reference town). Now I'd like to fine tune them - by just dragging them on the map.
Question 2: do you know some NASA service with the "historical" weather data? Matching the photo timestamp I could retrieve the weather at the moment of shoot. I don't know if it's useful, but it's cool. ;-)
PS The moral of the history is that SwingLabs rocks :-)
Posted by: fabriziogiudici on October 12, 2006 at 02:12 PM
-
Joshua, forgive the silly question, but where is WMService? I've done a CVS update on swingx-ws but I can't find it...
Posted by: fabriziogiudici on October 12, 2006 at 03:19 PM
-
Gah. I forgot it's currently on the new_tile_provider branch.
Posted by: joshy on October 12, 2006 at 05:51 PM
-
I also fixed the spelling of your name.
Posted by: joshy on October 12, 2006 at 05:52 PM
-
Okay, on to the questions: 1) it should be easy to drag waypoints around. The overlay interface gives you mouse events. Currently it's not as easy as it could be, however. I need to create some general coordinate conversion methods, but you can do it now manually by calling methods on the tile factory. After the drag you could just serialize the waypoints bakc into your own datastructor. 2) I'm not aware of a NASA service to do this, but that doesn't mean it's not there. I'm still exploring whats out there and I'm discovering that there's a lot. I know that have records of storms.
Posted by: joshy on October 12, 2006 at 06:03 PM
-
I just updated the screenshot to show the new waypoint renderer.
Posted by: joshy on October 12, 2006 at 06:12 PM
-
Wow, 100% phenomenal!
Posted by: commanderkeith on October 13, 2006 at 09:11 AM
-
Any of this inspired by the work with NASA on the World Wind port from .Net to Java? :)
Posted by: olsonje on October 13, 2006 at 11:16 AM
-
Originally this component was inspired by Google Maps. Well, more acurately, a dare that I couldn't build it for Google Maps. :) Once that was done, however, I immediately began looking into how to do the NASA version. The blue marble image was my desktop background for a long time (a smaller version, of course), so that was the logical first choice. Sadly other tasks took priority (like, you know, shipping Mustang and stuff). But last weekend when I was sick I started reading up on the Mercator projection and decided to tackle it again.
I believe that World Wind uses this same data set, actually, for the top few levels
Posted by: joshy on October 14, 2006 at 07:48 AM
-
The JXMapViewer seems very useful. Is it possible to use it with data stored in other coordinate systems instead of lat/lon? How easy would it be to overlay things on the map if all the data was in another coordinate system.
Posted by: khandes3 on November 07, 2006 at 07:38 AM
-
Thanks. You can use it with any other sort of dataprovider. You will need to convert lat/lon to your own coordinate system, however, by implementing your own TileFactory. If you have an example I might be able to help you with it.
Posted by: joshy on November 07, 2006 at 07:51 AM
-
I am not sure why I would need to convert my own coordinate system into lat/lon? My data is already corrected for displaying on a 2D surface. So it would be a waste to convert the data into lat/lon and then project back onto a 2D screen. I can go into a bit more detail but would prefer if you email me instead at khandelwalsaurabh (at) gmail
Posted by: khandes3 on November 08, 2006 at 01:08 AM
-
Hello Joshua:
I'm looking for an application in Java which is able to draw two-dimensional maps using input data.your application for importing maps from NASA's site gave me a hint on which to work on. I wonder if you have any knowledge of some application which does this:
Taking ASCII input data and drawing a colored two-dimensional map .
thanks in advance.
Posted by: einarabelc5 on February 13, 2007 at 11:53 AM
-
einarabelc5: I'm not sure what you mean by this. What kind of ASCII input?
Posted by: joshy on February 13, 2007 at 12:23 PM
-
Any hope of making this work with Java 1.6?
Posted by: hallkbrdz on March 18, 2007 at 02:47 PM
-
It should already work with Java 6. Anything that runs on Java 5.0 will run on Java 6.
Posted by: joshy on March 18, 2007 at 08:13 PM
-
Hello, I'm french so I hope I will succeed in making you understand my problem.
First, your blog is really interesting.
I have to work on a similar subject: I have to trace trajectories on google earth or world wind to witch I must add climatic conditions. I have understood I could enter those data that I have in Excel in the form of layers. I have already used Java and more precisely JBuilder but I don't understand what is the usefulness of JXMapViewer and I don't know if this programmation will be too technical for me. Could you help me in finding documents that explain this because I suppose my question is too light for you.
I suppose I will have more serious questions about your code later.
thanks in advance.
Posted by: lucy22 on June 07, 2007 at 07:55 AM
-
Hi, the component jxmapviewer is so interesting, but not esy to use.
Is there a way to find some documentation / examples.
I'm trying to make a project to show an italian map, then there should be the ability to attach some point on the map.
Can some one provide som code, or the example reported on this page ?
Thanks
bye
Posted by: ado2000 on October 14, 2007 at 06:26 AM
-
Hey Josh, you alluded to a new version that would support layers. It's been quite a while since this post, so I'm wondering if that is still in the works or not. If not, could you at least give us some ideas of what you were thinking on implementing layers and maybe I can help out. I need support for the base layer, country borders, loi names, etc. And the ability to turn them on and off. We are trying to stay away from a heavy weight map like worldwind because of the hardware requirements and so far JXMapViewer does the job except for the limited images/layer support. Let me know.
Posted by: serff on February 22, 2008 at 01:43 PM
|