Im Artikel “Erste Schritte mit JavaX JXMapKit” habe ich schon kurz beschrieben, wie man mit NetBeans und SwingX-WS schnell und einfach eine Kartendarestllung á la GoogleMaps in Java bauen kann.
Wenn man nicht nur eine Karte anzeigen sondern auch Punkte einzeichnen will, hat man die Möglichkeiten, per jXMapKit.setAddressLocation(new GeoPosition(lat, lon)); die Koordinaten setzen, zeichnen und die Karte dorthin zentrieren. Allerdings wird die Karte damit auch immer gleich zentriert und vor allem kann man nur einen einzelnen Punkt setzen.
Set set = new HashSet();
WaypointPainter waypointPainter = new WaypointPainter();
waypointPainter.setWaypoints(set);
set.add(new Waypoint(47.76098, 11.55932));
jXMapKit.getMainMap().setOverlayPainter(waypointPainter);
repaint();
Das repaint() am Ende sollte immer erfolgen, wenn neue Punkte in das Set gesetzt werden, damit die Änderung auch sofort sichtbar ist. Andernfalls muss man die Karte etwas verschieben um ein repaint zu erzwingen. Will man jetzt noch die Darstellung der Wegpunkte verändern, muss man sich noch mit dem WaypointRenderer beschäftigen.
Das heißt, ich kann aus meinen Bildern (die ich mit locr mit Geotags versehe) die Position nicht extrahieren – sehr schade. Immerhin ist der Bug bekannt und nun um ein Vote reicher. Vielleicht wird der Bug ja bald behoben. Je nachdem wie dringend ich die Funktionalität bald brauche, sehe ich mich doch schon fast bald dieselben Tests mit Imagero durchführen.
Update 26.11.2009: Nachdem ich mir heute vorgenommen hatte, mir den Sanselan Code einmal selbst anzusehen um den Bug vielleicht zu finden, war ich doch sehr überrascht, als ich eine Mail vom Apache-Bugtracker bekomme in der Bill Evans schreibt, er habe den Fehler und eine Lösung gefunden.
Und dank der Testdateien von Seb (siehe Kommentar) auch noch gleich neue GPS-Tags entdeckt:
GPS Img Direction Ref: ‘M’
GPS Img Direction: 2133/10 (213,3)
Also die Richtung in die fotografiert wurde.
Update 9.12.2010: Der Bug ist immer noch nicht gefixed obwohl ein Patch existiert. Sanselan ist auch immernoch ein Incubator-Projekt ohne voll in Apache integriert zu sein.
Update 28.11.2011: Der Bug ist als fixed markiert!
etching GPS Latitude Ref gets Interoperability Index instead of Reference
Wer mit JXMapviewer (aus SwingX-WS) gearbeitet hat (siehe früherer Post), wird früher oder später darauf gekommen sein, dass SwingX-WS nicht/kaum mehr maintained ist und man irgendwann anfangen darf, selbst zu patchen – mit der Gewissheit, dass die Änderung wohl nie in die offizielle Distribution zurückfließen wird.
Fabrizio Giudici hat dasselbe Problem – und wie es scheint, würde er sich daran machen, die MapViewer-Komponente auszugliedern und separat weiterzuführen. Entsprechende Hinweise finden sich in seinem Blog und im Java.Net-Forum. Bleibt zu hoffen, dass er es durchzieht!
Update 2014: Mittlerweile kann es durchaus schlauer sein JavaFX zu verwenden, so wie es hier beschrieben ist.
Nachdem ich festgestellt habe, dass Nasa WorldWind zum Anzeigen von Kartenpositionen vielleicht doch ein bisschen Overkill ist, habe ich mir JXMapKit des SwingLabs-Projekts angesehen.
Um das Beispiel überhaupt zum Laufen zu bekommen, benötigen wir natürlich die richtigen Libraries. Das wären dann SwingX und SwingX-ws. Derzeit wird man mit der Kombination nicht ganz glücklich, da in SwingX 1.0 (mindestens) eine Methode entfernt wurde, die in SwingX-ws benötigt wird. Der zugehörige Bug ist zwar reported, aber natürlich noch nicht in der aktuellsten Version eingebaut (Stand 30.7.2009). Eine gepatchte Version habe ich hier online gestellt: Jar / Quellen.
Wenn die Libraries erst einmal im Classpath liegen ist es im Prinzip ganz einfach (ich erkläre wie immer anhand von NetBeans aufgrund des besseren GUI-Editors):
JFrame-Form erstellen
JXMapKit in den Frame ziehen
die JXMapKit Komponente anklicken und in den Properties den defaultProvider auf OpenStreetMap stellen, andernfalls bekommt man nur Exceptions.
fertig!
Das ganze sollte dann so aussehen:
Der Code dazu sieht folgendermaßen aus:
public class MapViewer extends javax.swing.JFrame {
public MapViewer() {
initComponents();
}
private void initComponents() {
jXMapKit1 = new org.jdesktop.swingx.JXMapKit();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jXMapKit1.setDefaultProvider(org.jdesktop.swingx.JXMapKit.DefaultProviders.OpenStreetMaps);
getContentPane().add(jXMapKit1, java.awt.BorderLayout.CENTER);
pack();
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new MapViewer().setVisible(true);
}
});
}
private org.jdesktop.swingx.JXMapKit jXMapKit1;
}
OpenStreetMap ist ja ganz schön. Aber eine Karte wäre ja auch fein. Das erreicht man, indem eine neue TileFactory erstellt wird. Dazu wird schnell der Konstruktor geändert:
public MapViewer() {
initComponents();
WMSService wms = new WMSService();
wms.setLayer("BMNG");
wms.setBaseUrl("http://wms.jpl.nasa.gov/wms.cgi?");
TileFactory fact = new WMSTileFactory(wms);
jXMapKit1.setTileFactory(fact);
}
Und schon sieht’s so aus:
Beim Starten kann es sein, dass man erst mal nur ein blaues Fenster sieht. Ändert sich leicht, indem man ein paar mal auf den Minus-Button drückt und etwas abwartet, da das Laden der Bilder vom Nasa-Server etwas dauern kann. Bei genauem Hinsehen, wird man im obigen Screenshot auch bemerken, dass der markierte Ausschnitt im rechten unteren Teil nicht mit der tatsächlichen Darstellung übereinstimmt sondern (in dem Fall) einen Ausschnitt anzeigt, der deutlich südlicher liegt (im Fenster sieht man nämlich eigentlich die Nordspitze Schottlands.
Photos auf einer Karte anzuzeigen kann so schwer nicht sein möchte man meinen. Anbindung an Google Maps oder Google Earth und gut is.
Will man diese Kartenanzeige jetzt noch in ein Java-Programm integrieren, sieht’s schon anders aus. Google Maps wäre kein Problem, wenn denn JWebPane schon fertig wäre. Ist es aber nicht. Also bleiben derzeit nur noch 2 Methoden: Nasa WorldWind einbinden oder JXMapViewer benutzen.
Der erste Test mit Nasa WorldWind ging erheblich schneller als erwartet: Das NetBeansWiki beschreibt die wenigen nötigen Schritte.
und folgenden Code unter den initComponents() Aufruf des Konstruktors:
Model m = (Model) WorldWind.createConfigurationComponent(AVKey.MODEL_CLASS_NAME);
worldWindowGLCanvas1.setModel(m);
In den Projekteigenschaften noch folgende JVM-Property setzen: -Djava.library.path=c:pfadzumnasaworldwindsdk
fertig!
Die ganze Klasse sieht dann so aus:
import gov.nasa.worldwind.*;
import gov.nasa.worldwind.avlist.AVKey;
public class NWW extends javax.swing.JFrame {
public NWW() {
initComponents();
Model m = (Model) WorldWind.createConfigurationComponent(AVKey.MODEL_CLASS_NAME);
worldWindowGLCanvas1.setModel(m);
}
private void initComponents() {
worldWindowGLCanvas1 = new gov.nasa.worldwind.awt.WorldWindowGLCanvas();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("Nasa World WInd");
setMinimumSize(new java.awt.Dimension(640, 480));
getContentPane().add(worldWindowGLCanvas1, java.awt.BorderLayout.CENTER);
pack();
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new NWW().setVisible(true);
}
});
}
private gov.nasa.worldwind.awt.WorldWindowGLCanvas worldWindowGLCanvas1;
}