Technologien und Services zur Darstellung und Integration von Karten in Applikationen, haben sich in den letzten Jahren stark verbreitet. Neben dem Platzhirsch Google Maps gibt es u.a. noch Yahoo! Maps oder Bing Maps (früher Windows Live Maps).
Die drei genannten Services lassen sich relativ einfach über eine API in eigenen (Web) Anwendungen benutzen, haben aber einen entscheidenen Nachteil: Die Lizenzen lassen eine Benutzung in einem kommerziellen Umfeld nur mehr oder wenig eingeschränkt zu.
Eine Alternative ist das 2004 gegründete OpenStreetMap Projekt. OpenStreetMap sammelt frei verfügbare Geodaten und stellt sie unter Creative Commons Attribution-ShareAlike 2.0 Lizenz zur Verfügung. Die Daten von OpenStreetMap können auf vielfältige Weise in eigene Anwendungen integriert werden, beschrieben werden soll hier der Zugriff aus einer klassischen Java / Swing Desktopanwendung heraus mit JXMapViewer.
JXMapViewer ist ein Projekt von Swinglabs.org. Die Benutzung in einer Java Swing Applikation ist denkbar einfach. Von Swinglabs lädt man sich das Paket SwingX-WS herunter und fügt die darin enthaltenen Bibliotheken dem Classpath des Projekts hinzu. Anstatt nun wie üblich einer Swing Anwendung ein JPanel hinzuzufügen, wird eine Instanz der Klasse org.jdesktop.swingx.JXMapKit() benutzt. JXMapKit ist letztlich ein spezialisiertes JPanel, welches die Möglichkeit besitzt, Kacheln von Mapserver zu laden.
mapKit = new JXMapKit();
mapKit.setName("mapKit");
mapKit.setPreferredSize(new java.awt.Dimension(413, 218));
mainPanel.add(mapKit, java.awt.BorderLayout.CENTER);
mapKit.setDefaultProvider(DefaultProviders.OpenStreetMaps);
Als Mapserver wird hier OpenStreetMap benutzt und das Resultat stellt sich in etwa so dar:

Standard Mapserver ist SwingLabsBlueMarble, leider ist die im Code hinterlegte URL ungültig und dieses Manko wurde noch nicht gefixt.
Will man Kacheln (Tiles) eines anderen Mapserver benutzen, ist die kurze und knappe Vorgehensweise wie folgt: Der DefaultProvider wird auf Custom gesetzt mapKit.setDefaultProvider(DefaultProviders.Custom);, Im nächsten Schritt benötigt man die Instanz einer TileFactoryInfo und macht diese der Instanz von JXMapKit bekannt:
TileFactoryInfo info = new TileFactoryInfo(
0, // minimumZoomLevel
8, // maximumZoomLevel
10, // totalMapZoom
256, //tile size
true, true, // x/y orientation is normal
"file:/myLocalMapServer/tiles", // baseURL
"x","y","z" // URL params for x, y, z
) {
public String getTileUrl(int x, int y, int z) {
return this.baseURL + x+"_"+y+"_"+"z"+".jpg";
}
};
mapKit.setTileFactory(new DefaultTileFactory(info));
Die Tiles werden jetzt von einem LOKALEN Mapserver zur Verfügung gestellt. Wichtig ist, das die Bilder der Tiles dem definierten Url-Schema entsprechen! Die Aufteilung der Tiles in eine Größe von 256 Pixeln ist nicht wahllos gewählt sondern hängt von der gewählten Karten-Projektion ab. Nimmt man als Grundlage die Merkator-Projektion und schneidet die Karte bei +/- 85,051 Grad ab, bekommt man die Welt so wunderbar aufgeteilt. Eine genauere Erläuterung dieser Thematik findet man unter MapWrecker 2.0: Deconstructing Mercator, Part 2. Benutzt wird die Mercator-Projektion unter anderem von Openstreetmap und Google Maps.
Nicht besonders hervorgehoben ist in der Dokumentation vom JXMapViewer die Möglichkeit, über die Klasse org.jdesktop.swingx.mapviewer.wms.WMSService einen OGC-konformen Web Map Service anzusprechen:
WMSService wms = new WMSService();
wms.setLayer("BMNG");
wms.setBaseUrl("http://wms.jpl.nasa.gov/wms.cgi?");
TileFactory fact = new WMSTileFactory(wms);
mapKit.setTileFactory(fact);
Beispiel mit Nasa World Wind Daten:

Beispiel Übersichtskarte Niedersachen:
wms.setLayer("navigation_f");
wms.setBaseUrl("http://www.geobasisdaten.niedersachsen.de/bestand?");

Beispiel Luftbilder Deutschland:
wms.setLayer("0");
wms.setBaseUrl("http://gdz-dop.bkg.bund.de/ArcGIS/services/DOP/ImageServer/WMSServer?");

Einige weitere Möglichkeiten des JXMapViewer
Zoom und Zentrierung der Karte:
mapKit.setCenterPosition(new GeoPosition(50, 8.6));
mapKit.setZoom(7)
Darstellung von Wegepunkten (Waypoints):
Set<Waypoint> waypoints = new HashSet<Waypoint>();
waypoints.add(new Waypoint(50, 8.4));
waypoints.add(new Waypoint(50, 8.5));
waypoints.add(new Waypoint(50, 8.6));
waypoints.add(new Waypoint(50.05, 8.6));
waypoints.add(new Waypoint(50.1, 8.6));
WaypointPainter painter = new WaypointPainter();
painter.setWaypoints(waypoints);
mapKit.getMainMap().setOverlayPainter(painter);

Voreingestellt ist die Darstellung sogenannter Teardrops für die Wegepunkte. Dem Anpassungswillen des Benutzers sind hier aber keine Grenzen gesetzt:
painter.setRenderer(new WaypointRenderer() {
public boolean paintWaypoint(Graphics2D g, JXMapViewer map, Waypoint wp) {
g.setColor(Color.RED);
g.fillRoundRect(-10, -10, 10, 10, 10, 10);
return true;
}
});
Dem painter wird ein neuer Renderer untergeschoben und man darf sich mit Graphics2D ungehindert am Customizing versuchen.

Darstellung von Routen
Für die Darstellung einer Route benötigt man einen definierten Painter, plus eine Liste mit den jeweiligen Punkten der Route als GeoPosition (hier in model.getGpxTrack()).
GeoPosition hat Attribute für Latitude und Longitude in Dezimalform. Diese Attribute müssen für die Darstellung in der Karte abhängig vom Zoom in Pixel-Koordinaten umgerechnet werden. Die TileFactory der Map bietet hier mit der Methode geoToPixel die entsprechende Hilfe.

Weitere Links
Hallo Herr Wilkening,
mit dem JXMapViewer beschäftige ich mich auch schon eine Weile. Das mit den Routen sowie mit den WMSService war mir neu. Vielen Dank.
Jedoch habe ich ein Problem und hoffe Sie können mir dabei behilflich sein.
Beim Start versucht JXMapKit bereits die Daten zu laden, ist jedoch kein Proxy konfiguriert oder die Internetverbindung gestört erscheint " SCHWERWIEGEND: Failed to load a tile at url: http://tile.openstreetmap.orgXXXX, retrying java.lang.NullPointerException [...]"
Nach einem retry versuchts er mit dem nächsten Bild. Nun habe ich habe in meinen Programm die Möglichkeit, einen Proxy zu konfigurieren.
Bilder die er vorher vergeblich zu laden versuchte lädt er aber nicht mehr neu nach.
Ein: mapkit.getMainMap().invalidate(); makit.repaint();
brachte auch keine Lösung. Wie kann ich JXMapKit reseten bzw. ihn dazu überreden die fehlenden Images nachzuladen sobald eine Internetverbindung besteht?
Mfg Steffen Jürges
Hallo Steffen,
interessantes Problem. Ich habe mich noch einmal kurz in der API vergewissert, ob es dort vielleicht einen Lösungsansatz geben könnte, wurde aber nicht fündig.
Hast Du die Möglichkeit, die "Qualität" deiner Internetverbindung vor der Einbindung / Darstellung des JXMapKit Panels in deiner Applikation zu prüfen?
Andere Möglichkeit wäre sich an den Sourcen zu versuchen. Beispielsweise wird in der AbstractTileFactory dreimal versucht die entsprechenden Kachel zu laden.
Hallo, ich habe den WMS Service Luftbilder Deutschland aus obigen Artikel von Oliver Wilkening versucht. Dabei erschien die folgende Fehlermeldung: SCHWERWIEGEND: Failed to load a tile at url: http://gdz-dop.bkg.bund.de/ArcGIS/services/DOP/ImageServer/WMSServer?version=1.1.1&request=GetMap&Layers=0&format=image/jpeg&BBOX=9.7064208984375,52.086257332338384,9.7119140625,52.08963261363971&width=500&height=500&SRS=EPSG:4326&Styles=, retrying
Dabei fällt auf, dass die Parameter von BBOX offensichtlich geographische Längen und Breiten sind, die dann zu LatLonBoundingBox gehörten. In anderen Beispielen habe ich gesehen, dass unter BBOX kartesische Koordinaten standen. Können die Koordinaten von BBOX die Ursache für den Fehler sein? MfG BO
Hallo Bernd,
der Service scheint generell leider nicht mehr zu funktionieren. Ich konnte bisher leider auch nicht herausfinden, ob sich lediglich die URL geändert hat
Hallo Oliver, die url der Luftbilder Deutschland stimmt noch. Das kann man erkennen, wenn man den DOP-Viewer http://www.geodatenzentrum.de/dienste/dop_viewer.htm aufruft und dabei die Java Konsole aktiviert hat. Da werden die Netz-Zugriffe protokolliert, z.B. http://gdz-dop.bkg.bund.de/arcgis/services/DOP/ImageServer/WMSServer?VERSION=1.1.0&REQUEST=GetMap&LAYERS=0&SRS=EPSG:25832&STYLES=&BBOX=-35040.89979550103,5235000.0,1235040.899795501,6100000.0&WIDTH=718&HEIGHT=489&FORMAT=image/jpeg&BGCOLOR=0xFFFEFD
Ich vermute, dass der Online-Dienst jetzt gebührenpflichtig ist. Siehe https://upd.geodatenzentrum.de/geodaten/gdz_rahmen.gdz_div unter Online Shop, Dienste Shop, Gebührenrichtlinien. Schade, die Luftbilder sind wirklich gut mit einer hervorragenden Auflösung. (s. DOP Viewer)
MfG BO
Hallo Oliver,
hatte in der API nämlich auch sonst nicht passendes gefunden.
Das mit der "Internetverbindung prüfen" wird wohl die schnellste Lösung sein. Für den Verwendungszweck sollte das genügen.
Mfg Steffen Jürges
Hi Oliver, I am brand new to JXMapViewer Thank you a lot for the tutorial! In the "Darstellung von Routen" of your tutorial are there some type like "Graphics2D", "Rectangle", "Color" and so on, which are all with the prefix: "{FNAMEL}+java.sun.com&btnI=I%27m%20Feeling%20Lucky">" ! Why with this? Weijian Ji
Hi Weijan,
just ignore this "prefix". There is a problem with a code highlighting plugin in our blog system
OK,i got it:)
Hi Oliver, Another question about "Darstellung von Routen": which type of class has the model in "model.getGpxTrack"; I'm going to demostrate a route between two points on the map. Thanks
model.getGpxTrack returns a List of org.jdesktop.swingx.mapviewer.GeoPosition.
Oliver,thank you a dozen!
I guess you have more experience about routering on a JFMapViewer. Maybe you could tell me, whether you've used a existing route-planner in this Example(like GraphServer, PHPRoute, pgRouting, JGraphT, Traveling-Salesman), if so, which one?
I am writing a destktop application. I set a jXMapKit1 to OpenStreetMap as Defaultprovider, how can i get the object of the Map from a this JXMapKit-instance in order to use e.g. a DijkstraRouter-Methode on it to get the Route for two Waypoint?
jXMapKit1.getMainMap() is still type of JXMapViewer instead of a map object
Weijian Ii
Hi Weijian,
I didn't used a route planner in this example. I just parsed and visualized a GPX track / file.
If I have understood you correctly, you want to mark two points on the map and calculate and display the route after this?
Yes Oliver,that is actually i want to do - calculate a route between two points on the map and display on the map. Under your helpful tutorial is drawing or marking points no problem. The problem is how can i use any algorithm on the map in oder to find the route?
The secret is to add a MouseListener on the Map: mapKit.getMainMap().addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
GeoPosition pos = mapKit.getMainMap().convertPointToGeoPosition(evt.getPoint());
// Do sth. with this point... } });
Hi Oliver,
maybe it sounds simple for you but if i've already had a GPX track file, how can i parsed it and then visualized it? Which classes will be needed?
my regards,
Dante
Hi Dante,
you can write your own parser or use the GPX-Module provided by codehaus: http://docs.codehaus.org/display/GEOTOOLS/GPX+module
If you need an existing and working example, check out the sources from my jGeotagger project on sourceforge (it is in a pre-alpha status): http://sourceforge.net/projects/jgeotagger/
Hallo,
erstmal danke für die super Einführung in das Thema.
Ich habe nun festgestellt, dass das geladene Openstreetmap-Kartenmaterial nicht ganz so detailliert ist wie unter "openstreetmaps.org". Bei letzterem lässt sich meiner Erfahrung nach zwei Mal mehr rein zoomen.
Kennt wohl jemand eine andere Datenquelle (URL), oder liegt das wohl am Zoom-Balken?
Vielleicht hat ja jemand eine Idee.
Danke!!!
Ich kenne das Problemnicht,ich arbeitet ausschließlich mit dem online-Karten. Du kannst bei Marcus Wolschon " http://forum.openstreetmap.org/viewtopic.php?id=2872" fragen, er weiß die Anwort bestimmt.
Hallo, danke für die Einführung in das Thema. Das ganze macht mir jetzt doch wieder mehr Mut mich damit zu befassen (bin eher Neuling in Java).
Ich würde mich auch für die Berechnung einer Route interessieren. Das ganze muss bei mir nicht zwingend über openstreetmap laufen (da nicht kommerziell).
Gibt es da denn echt gar keine einfache Möglichkeit die Route irgendwie berechnen zu lassen?
Um mal etwas konkreter zu werden für was ich das brauch: Ich bastle zur Zeit für die Feuerwehr einen Ausrückebildschirm, auf dem dann auch die Route zum Einsatzort angezeigt werden soll.