Since the early days of navigation, maps have played a vital part
of commerce. A sailor without a map was completely lost. Without a
map, a land owner wouldn't know what they owned. Thomas Jefferson
even sent Lewis and Clark on a two-year trip across the entire
North American continent to bring back accurate maps. Despite this
long history of mapping, things are changing quickly. Thanks to
recent innovations in devices, networks, and satellites, mapping
technology is set to completely explode with new ideas and
applications. Google really kicked off this mapping explosion with
the release of Google Maps
and their acquisition of 3D mapping company Keyhole (now called
Google Earth). A few
enterprising developers quickly began hacking on Google Maps,
creating a new application type, the mashup.
However, Google Maps mashups were just the beginning.
Many websites now provide all sorts of interesting data that can
be searched, indexed, and visualized geographically. Flickr,
Craigslist, and Wikipedia are just a few of the many providers of
geodata. Yet despite all of the many ways people generate and use
geographic data, there is one common component: the map viewer.
Though much of the attention has been focused on Ajax maps from
Yahoo and Google, Swing has its own mapping component, the open
source JXMapViewer. Originally created for a JavaOne
demo, the JXMapViewer can let you embed mapping
abilities into your own Swing-based Java application (or applet).
By the end of this article, you will know how to build and run a
simple Swing application using the JXMapViewer
combined with new features in NetBeans 6
that can greatly increase your productivity.
Building a Basic Application
The JXMapViewer is an open source (LGPL) Swing
component created by the developers at SwingLabs. At its core, the
JXMapViewer is a special JPanel that
knows how to load map tiles from an image server. All of the
details of how to convert coordinates to pixels, cache tiles, and
stitch them together on screen are nicely hidden inside
JXMapViewer's API. All you need to do is add it to
your Swing application the way you would with any other
JPanel.
In this article, we will build a simple program that shows a map and lets
you zoom and pan around. We will also add a few controls to show
locations on the map with custom code. You can see what the
application looks like in Figure 1.
Figure 1. Basic map application
The easiest way to work with the JXMapViewer is by
using a recent build of NetBeans 6, which you can get from the
NetBeans
download page. You must use Beta 2 to get the JNLP support used
at the end of this article. Like other SwingLabs projects, the
JXMapViewer will work with any IDE or Java development
tool, but this article will use NetBeans because recently added
features in NetBeans 6 will greatly increase your productivity and
let you build complex programs very quickly.
After you have NetBeans 6 installed and running, create a new
project and choose the Java Desktop Application project type
(Figure 2). Accept the default Application Shell type of Basic
Application (Figure 3).
Figure 2. Creating a Desktop Java Application project (click for
full-size image)
Figure 3. Using the Basic Application shell (click for full-size
image)
You will now have a basic desktop application with a default
form containing a menu bar, status label, and prefab resource
bundles (Figure 4).
Figure 4. Empty Java desktop application
Adding the JXMapKit
Now download the latest weekly build of swingx-ws
from the SwingLabs
download page and unzip it in a lib directory inside of
your project. You can add the .jars to your project by right-clicking on the Libraries node in the Projects
inspector and choosing Add Jar/Folder. (Figure 5).
You will need to add the swingx-ws-2007_10_14.jar (the
exact name may be different, depending on which weekly build you
downloaded). You must also add the support .jars (swingx-bean.jar, swingx.jar, and
swing-worker.jar) to the
lib/swingx-ws-[YY_MM_DD]-bin/lib/cobundle directory
of your project.
Figure 5. Empty Java desktop application
Now that the .jars are added to your project, you can add the
JXMapKit to your form (I'll explain in a second why we
are using the JXMapKit instead of the
JXMapViewer). One of the new features of NetBeans 6 is
the ability to add components directly from your library .jars
without having to add them to the palette first. Navigate through
the class hierarchy of the SwingX-WS .jar in the Projects inspector
to find the org.jdesktop.swingx.JXMapKit class
(Figure 6).
Figure 6. Find the JXMapKit class
Once you have found the class, you can drag it directly into your
form. NetBeans will create a new instance of the
JXMapKit and add it (Figure 7). Note that the
drag operation will only work if all of the .jars are inside of your
project directory. That's why you must move them all to the
[projectdir]/lib directory first.
Figure 7. Dragging the JXMapKit into the form (click for full-size image)
In this example, I'm using the JXMapKit class
instead of the JXMapViewer because the kit version
includes zoom buttons, a zoom slider, and a mini-view. The
JXMapKit is just a wrapper for the
JXMapViewer that includes the most commonly used
features. Once you have added the component, you can resize it to
fill the form.
Now press the triangle-shaped run button or select
Run->Run Main Project from the main menu to see your
program running (Figure 8). You can drag the map around and
also zoom in and out. That's it! This is your first mapping
application.
Figure 8. Basic map application
Customizing the Map
Now that you have a map application, you might want to customize
it. Fortunately, the JXMapKit has many customization
options. The first things you might want to change are the visible
control components. There are properties to turn on and off the
mini-map, zoom slider, and zoom buttons. For example, if you wanted
to show the map in a smaller space, you could turn off the zoom
slider and mini-map but leave the zoom buttons visible. You can set
these properties in the Property Sheet to the left of the form
editor.
Re-Centering and Adding Waypoints
Now that you have a map, you probably want to do something with
it. The two basic operations supported by the
JXMapViewer are moving the map and adding markers. To
move the map, you can use the setAddressLocation()
method. New features in NetBeans 6 make it very easy to tie a
button to the map.
Let's make a button that will center the map on Chicago. Just
add a button to the form from the palette, and then right-click the button and choose Set Action... to open the Action
editor for that button. In the Action to edit combo box,
choose Create New Action. Next, type in the method name
goChicago and the text Go To Chicago.
(Figure 9). Finally, press the OK button to close the Action
editor. NetBeans will create the new empty goChicago
method and jump to it.
Figure 9. Creating a goChicago action (click for
full-size image)
The GeoPosition class represents a coordinate on
the globe expressed as a latitude and longitude. All
JXMapViewer APIs express coordinates using the
GeoPosition class. You can find the coordinates of
many cities and landmarks at Wikipedia. Each entry
has coordinates in the sidebar summary on the right side of the
screen under the map.
Now run the application and press the Go To Chicago
button. The map will re-center above Chicago using the coordinates I
grabbed from
Wikipedia (Figure 10).
Figure 10. Centering on Chicago
The JXMapKit tracks two different positions.
Whenever you call the setAddressPosition() method, the
map will move and set a marker on the selected point. You can also
read the current center position of the map using the
getCenterPosition() method. The center position will
change whenever the user moves around the map. The address
position only changes when you call
setAddressPosition().
Adding Waypoints
To make your map really useful, you need to draw points on it
that represent locations. In the mapping world, coordinates that
represent physical locations are called waypoints. Waypoints
are represented with the WayPoint class. The
JXMapViewer has a special painter that can properly
draw points on the map. Painters are classes that implement the
Painter interface and can be set on a SwingX component
to customize that component's drawing. JXMapViewer
can accept painters using the setOverlayPainter()
method. Below is an action method that will add the points for New
York City and Chicago using the standard
WaypointPainter.
@org.jdesktop.application.Action
public void addWaypoint() {
//create a Set of waypoints
Set<Waypoint> waypoints = new HashSet<Waypoint>();
waypoints.add(new Waypoint(41.881944,-87.627778));
waypoints.add(new Waypoint(40.716667,-74));
//crate a WaypointPainter to draw the points
WaypointPainter painter = new WaypointPainter();
painter.setWaypoints(waypoints);
jXMapKit1.getMainMap().setOverlayPainter(painter);
}
Note the call to jXMapKit1.getMainMap(). JXMapKit is actually a wrapper for two instances of
the JXMapViewer; one for the main map and one for the
mini map. You can get access to those instances with the
getMainMap() and getMiniMap()
methods.
Using a Custom Waypoint Renderer
By default, waypoints are drawn using a blue teardrop shape, but
you may want to customize them with your own drawing code. You can
easily do this by implementing the WaypointRenderer
interface. Below is an addition to the code above where I have
added a custom WaypointRenderer to the painter that
draws each waypoint as a red "X."
The WaypointPainter does all necessary coordinate
calculations and pre-translates the Graphics2D object
before calling your paintWaypoint() method. This means
the center of the waypoint will be at 0,0, so creating a ten-pixel "X"
is easily accomplished by drawing from -5 to +5. References to the
map and current waypoint are provided in case your code needs to
conditionally draw certain waypoints differently. The return
Boolean is not currently used so you can just return true.
Using Alternate Map Servers
By default the JXMapKit uses a copy of NASA's Blue
Marble satellite images hosted on a SwingLabs server. The Blue
Marble data only goes to 8-km resolution, however. Another option
is the OpenStreetMap.org tile server,
which serves up a vector map of the world created by volunteers
with GPS receivers. JXMapKit is preconfigured with both maps so
you can switch between them by setting the
defaultProvider property in the Property Sheet.
You can also select custom as the defaultProvider to use your
own map server, but this is a more complicated task that I will
cover in a future article. If you select the OpenStreetMaps, your
application will look like Figure 11.
Figure 11. Using the OpenStreetMaps server
Deploying Your Application
Now that you have built a cool mapping application, you probably
want to share it on the Web. One of the great new features in
NetBeans 6 is support for Java Web Start deployment. To use it, you
must turn on Web Start by going to the Project Properties,
selecting the Web Start tab, and checking the "Enable Web Start"
checkbox (Figure 12). Then you should change the codebase to "User
defined (e.g. HTTP deployment)," type in the base URL where you
will upload your application, and then check the "Self-signed"
checkbox. Now you can build the project and upload the contents of
your project's dist directory to your web server.
Figure 12. Enabling Java Web Start (click for full-size
image)
Once your application is uploaded to your web server, you can
point your browser at the uploaded JNLP file. For example, I
uploaded my application to
projects.joshy.org/articles/MapApp, so the JNLP URL is
http://projects.joshy.org/articles/MapApp/launch.jnlp.
You can test my copy of the program here.
Future Work
Thanks to recent innovations in wireless networks, GPS
receivers, and general web technology, mapping is becoming cheaper
and more flexible. Many people believe that mapping will eventually
become a part of the fabric of the internet. Nokia believes this so
much it
recently paid over eight billion dollars to buy one of the premier map
vendors, Navteq. And the OpenStreetMap project, mentioned
earlier, has attracted a group of volunteers to the huge
undertaking of building a completely free and open map of the
world. I highly encourage you to check them out and consider
contributing.
Mapping is becoming so important that the ability to work with
maps and geocoded data will one day be a part of every software
platform. Fortunately, with the JXMapViewer you can
easily build mapping into your own Java applications today. Come
back for my next article, where I will show you how to plug in a
custom tile server, perform coordinate transformations, add
rollovers and animation, and build mashups with several popular
web services.
Great API and awesome article. I have few question about that API.
1. Is there a support for multilayers.
2. Support for ESRAI files , shp ssx .etc.
Thanks
Can't drag'n drop JXMap
2008-05-21 17:36:06 phana59
[Reply | View]
The drag and drop problem is bothering me also. I created a project MapApp3 in NetBeans IDE and exited. I downloaded swingx-ws-2008_01_27.jar. I manually created a lib subdir under the project directory (~/MapApp3) and copied the jar file from my download area to ~/MapApp3/lib. I restarted NetBeans, clicked over the Libraries folder and added the jar file located in ~/MapApp3/lib. I have not set a CLASSPATH.
I guess I don't understand what you want, or how to copy, the three support jar files to lib/swingx*/lib/cobundle (or even where these support jar files come from. Are they inside swingx-ws-2008_01_27.jar?)
This is on Fedora 9 Linux box. I'm an old C programmer!
What am I missing?
Local stored maps
2008-05-05 01:43:05 sreetharan
[Reply | View]
Great article. instructions are clear and it works well for me. instead of openstreetmaps or bllue marble imgaes can i use local stored maps to use with JXMapViewer?
Paint and labels on the map.
2008-01-15 08:43:06 lillbiff
[Reply | View]
This is good stuff indeed!
I looking a bit in the javadoc for swingx-ws. I look for some features, involving paint stuff on the map.
- I wonder. Is it possible to add a label at a geographical position on the map? (that you can use as a button?).
- I found out how to paint a waypoint at a geographical point on the map. Is it posible to paint a line with geographical coordinates (if you want to have a line between 2 waypoints for example)?
Paint and labels on the map.
2008-01-15 18:14:46 joshy
[Reply | View]
You can easily do these things. Take a look at part 2 of this article. It explains everything you need.
Great Job! Looking forward to your next article about using custom tiles providers)
Will it be possible later to drag & drop waypoints?
Local Data
2008-01-03 09:22:46 belmarbob
[Reply | View]
Hi:
How can I get JXMapViewer to read local data read off a filesystem on the same machine ?
Local Data
2008-01-03 20:17:19 joshy
[Reply | View]
You can easily do this with a custom tile factory. Something like this:
TileFactory tf = new DefaultTileFactory(
new TileFactoryInfo(0,3,5,256, true, true,
"http://mt2.google.com/", "x","y","zoom") {
public String getTileUrl(int x, int y, int zoom) {
return "file:///Users/joshy/imgs/foo."+x+"."+y+".tiff";
}
});
mapkit.setTileFactory(tf);
In the code above I have overridden the getTileUrl method to ignore the base url and string parameters. Instead it returns a custom file: url.
Great demo !
2008-01-01 08:14:40 fmatz
[Reply | View]
How can I set other JXMapKit1.setAddressPosition(..) ?
Thanks
Great demo !
2008-01-01 20:16:27 joshy
[Reply | View]
I'm not exactly sure what your question is. You can set any lat/long you want for the address position of the map.
Great demo !
2008-01-04 08:40:32 fmatz
[Reply | View]
Sorry, I'm a newbie. Can You give me a example to set the address ?
Thanks
Great demo !
2008-01-04 08:57:07 joshy
[Reply | View]
Sure. Just call the setAddressLocation() method on your JXMapViewer object. This method takes a GeoPosition, which represents a latitude / longitude pair. So you would call it like this:
GeoPosition gp = new GeoPosition(-146.57, 37.5); mapviewer.setAddressLocation(gp).
You can find the coordinates of many cities from Wikipedia. Yahoo also has a geocoding webservice to turn street addresses into lat/longs.
Great demo !
2008-01-04 14:38:03 fmatz
[Reply | View]
Many thanks for your fast reply.
I have a little demo to get coordinates of click points:
http://www.free-dev.com/gmaps/g33.html
and now your demo with my point works:
http://www.free-dev.com/map/launch.jnlp
- Best.
This is Fantastic
2007-11-09 15:35:34 jyeary
[Reply | View]
This is a fantastic example of what you can do with Swing and the latest components from the Swing Labs. Thanks Josh for the excellent example.
The piece around adding the jars was a bit unclear. "Simply drag the jars into the MapApp directory before adding them to the project libraries"; probably would have been a bit clearer.</p.
<p>The example is really cool!!!
Thank you, this is great. Any idea how large in total the image files for blue marble or openstreetmaps are? Would it even be reasonable to store them locally instead of a web service?
It's quite easy to store locally. The 2km data is about 20MB. The larger 500m data is much bigger, though. NASA actually encourages you to download it via bittorrent. :)
Great article, followed your instructions and it works a treat. Important to add library jars for swingx.jar, swingx-bean.jar and swing-worker.jar before the drag / drop works.
I connected to a Web Service that retrieves information including latitude / longitude and a status value. I would like the custom renderer to use e.g. a different colour based on the status value. What is the best way to add this functionality? I tried extending WayPoint - adding the status information was OK, but then had trouble overiding paintWaypoint.
Also, whilst the WS lookup is happening I would like to display the busy cusrsor - what's the recommended way to do that in this framework?
Solved the 'busy cursor' by implementing the WS lookup within a Task, as outlined here:
http://java.sun.com/developer/technicalArticles/javase/swingappfr/
solved the custom rendering with code like this:
//crate a WaypointPainter to draw the points
WaypointPainter painter = new WaypointPainter();
painter.setWaypoints(waypoints);
// our own custom renderer..
painter.setRenderer(new WaypointRenderer() {
public boolean paintWaypoint(Graphics2D g, JXMapViewer map, Waypoint waypoint) {
// get the weather
String name = ((WeatherWaypoint)waypoint).getName();
boolean statusOK = ((WeatherWaypoint)waypoint).isStatus();
// draw the waypoint
if (statusOK)
g.setColor(Color.CYAN);
else
g.setColor(Color.RED);
g.fillOval(-8,-8,16,16);
g.setColor(Color.BLACK);
g.drawString("" + name, -15,-10);
return false;
}
});
Nice Article. I look forward to a followup on custom Tile providers. I'd like to integrate an ESRI image server into a swing app in an application that does not have internet connectivity to retrieve imagery/base data.
Not able to drag and drop JXMapKit class.
2007-10-31 00:23:13 aadrat
[Reply | View]
I have added the jar in project build path but not able to drag n drop to form. Its giving the error "The class must be compiled and must be on the classpath of the project to which this form belongs".
Any idea about create a new instance of the JXMapKit without doing drag n drop.
Please help.
Not able to drag and drop JXMapKit class.
2007-10-31 00:24:37 joshy
[Reply | View]
Where did you put the jar? It must be in your project directory, not just in the path. You can also add the class directly to the palette and then drag from there.
Not able to drag and drop JXMapKit class.
2008-05-21 17:15:33 phana59
[Reply | View]
Yes, this problem is bothering me also. I created a project MapApp3 in NetBeans IDE and exited. I downloaded swingx-ws-2008_01_27.jar. I manually created a lib subdir under the project directory (~/MapApp3) and copied the jar file from my download area to ~/MapApp3/lib. I restarted NetBeans, clicked over the Libraries folder and added the jar file located in ~/MapApp3/lib. I have not set a CLASSPATH.
I guess I don't understand what you want, or how to copy, the three support jar files to lib/swingx*/lib/cobundle.
This is on Fedora 9 Linux box. I'm an old C programmer!
What am I missing?
Not able to drag and drop JXMapKit class.
2007-10-31 02:29:00 aadrat
[Reply | View]
Thanx for the quick reply.
I have added the support jars in classpath directly. Drag and drop problem has been solved.
But when I am running the Main project its trying to load images and failed there.
Failed to load a tile at url: http://maps.joshy.net/tiles/bmng_tiles_3/3/0/2.jpg
I have to use a proxy in my network as a automatic configuration script.
And I dont know how to configure it in NetBeans. Its not a HTTP Proxy so I can not it directly. Currently my netbeans proxy setting is "Use System Proxy Settings".
Not able to drag and drop JXMapKit class.
2007-10-31 09:42:15 joshy
[Reply | View]
I'm not sure. This sounds like more of a general Java question. Can you successfully open a url to that image directly without using the JXMapViewer? If you call new URL("http://maps.joshy.net/tiles/bmng_tiles_3/3/0/2.jpg").openStream()
and then try to read from it, what happens?