Looks pretty cool with the isobars, wind arrows and a wind heat map.
The images below are showing a storm front (Xynthia) hitting Western Europe.











Comments are welcome!
openmap.components=... geonamessearcher
geonamessearcher.class=GeoNamesSearcher
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.plaf.basic.BasicComboBoxEditor;
import net.miginfocom.swing.MigLayout;
import org.apache.log4j.Logger;
import org.geonames.Toponym;
import org.geonames.ToponymSearchCriteria;
import org.geonames.ToponymSearchResult;
import org.geonames.WebService;
import com.bbn.openmap.event.CenterListener;
import com.bbn.openmap.event.CenterSupport;
import com.bbn.openmap.gui.OMToolComponent;
public class GeoNamesSearcher extends OMToolComponent {
private static final Logger log = Logger.getLogger(GeoNamesSearcher.class
.getName());
private JComboBox searchBox;
private CenterSupport centerDelegate;
private DefaultComboBoxModel searchBoxModel = new DefaultComboBoxModel();
public GeoNamesSearcher() {
centerDelegate = new CenterSupport(this);
setLayout(new MigLayout());
searchBox = new JComboBox(searchBoxModel);
searchBox.setEditable(true);
searchBox.setMaximumRowCount(25);
searchBox
.setToolTipText("Enter part of location to search for (and center to).");
searchBox.setRenderer(new DefaultListCellRenderer() {
@Override
public JComponent getListCellRendererComponent(JList list,
Object value, int index, boolean isSelected,
boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index,
isSelected, cellHasFocus);
if (value instanceof Toponym) {
Toponym toponym = (Toponym) value;
setText(toponym.getName() + " " + toponym.getCountryCode());
}
return this;
}
});
searchBox.setEditor(new BasicComboBoxEditor() {
@Override
public void setItem(Object anObject) {
if (anObject != null) {
if (anObject instanceof Toponym) {
Toponym toponym = (Toponym) anObject;
editor.setText(toponym.getName() + " "
+ toponym.getCountryCode());
// oldValue = anObject;
} else {
super.setItem(anObject);
}
} else {
editor.setText("");
}
}
});
searchBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
JComboBox cb = (JComboBox) evt.getSource();
Object selectedItem = cb.getSelectedItem();
if ("comboBoxEdited".equals(evt.getActionCommand())) {
// User has typed in a string; only possible with an
// editable combobox
try {
searchBoxModel.removeAllElements();
ToponymSearchCriteria searchCriteria = new ToponymSearchCriteria();
// searchCriteria.setCountryCode("DK");
searchCriteria.setQ((String) selectedItem);
ToponymSearchResult searchResult = WebService
.search(searchCriteria);
for (Toponym toponym : searchResult.getToponyms()) {
searchBoxModel.addElement(toponym);
}
if (searchResult.getTotalResultsCount() == 1) {
Toponym t = searchResult.getToponyms().get(0);
centerDelegate.fireCenter(t.getLatitude(), t
.getLongitude());
} else if (searchResult.getTotalResultsCount() > 1) {
searchBox.showPopup();
}
} catch (Exception e) {
log.error("", e);
}
} else if ("comboBoxChanged".equals(evt.getActionCommand())) {
// User has selected an item; it may be the same item
if (cb.getSelectedItem() instanceof Toponym) {
Toponym newItem = (Toponym) cb.getSelectedItem();
centerDelegate.fireCenter(newItem.getLatitude(),
newItem.getLongitude());
}
}
}
});
add(searchBox);
}
public synchronized void addCenterListener(CenterListener listener) {
centerDelegate.add(listener);
}
/**
* Remove a CenterListener
*
* @param listener
* CenterListener
*/
public synchronized void removeCenterListener(CenterListener listener) {
centerDelegate.remove(listener);
}
@Override
public void findAndInit(Object obj) {
if (obj instanceof CenterListener) {
addCenterListener((CenterListener) obj);
}
}
@Override
public void findAndUndo(Object obj) {
if (obj instanceof CenterListener) {
removeCenterListener((CenterListener) obj);
}
}
}

/**
* @author Matt Hicks (matt@matthicks.com)
*/
public class Clock {
private float radius = 77;
private float centerX = 144;
private float centerY = 144;
private Calendar calendar = Calendar.getInstance();
private int hours;
private int minutes;
private int seconds;
private FloatVariable hoursVariable;
private FloatVariable minutesVariable;
private FloatVariable secondsVariable;
public Clock() throws IOException {
nextTick();
// Build JavaFX clock
Group group = new Group();
{
ImageView imageView = new ImageView();
Image image = new Image();
// should be image.set$url() but does not work ?
image.loc$platformImage().set(
ImageIO.read(getClass().getClassLoader().getResource(
"clock_background.png")));
imageView.set$image(image);
group.loc$content().insert(imageView);
Group face = new Group();
{
Translate translate = new Translate();
translate.set$x(centerX);
translate.set$y(centerY);
face.loc$transforms.insert(translate);
// Every third hour
for (int i = 3; i <= 12; i += 3) {
Text text = new Text();
translate = new Translate();
translate.set$x(-5.0f);
translate.set$y(5.0f);
text.loc$transforms.insert(translate);
text.set$font(Font.font("Arial", 16));
text.set$x(radius * ((i + 0) % 2 * (2 - i / 3)));
text.set$y(radius * ((i + 1) % 2 * (3 - i / 3)));
text.set$content(String.valueOf(i));
face.loc$content.insert(text);
}
// Black circle for the rest of the hours
for (int i = 1; i < 12; i++) {
if (i % 3 == 0) {
continue; // Don't show a circle on every third hour
}
Circle circle = new Circle();
Rotate rotate = new Rotate();
rotate.set$angle(30.0f * i);
circle.loc$transforms.insert(rotate);
circle.set$centerX(radius);
circle.set$radius(3.0f);
circle.set$fill(Color.$BLACK);
face.loc$content.insert(circle);
}
// Center circles
Circle circle = new Circle();
circle.set$radius(5.0f);
circle.set$fill(Color.$DARKRED);
face.loc$content.insert(circle);
circle = new Circle();
circle.set$radius(3.0f);
circle.set$fill(Color.$RED);
face.loc$content.insert(circle);
// Second hand
Line line = new Line();
{
Rotate rotate = new Rotate();
BindingExpression exp = new AbstractBindingExpression() {
@Override
public void compute() {
pushValue(seconds * 6f);
}
};
secondsVariable = FloatVariable.make(exp);
rotate.loc$angle().bind(false, secondsVariable);
line.loc$transforms.insert(rotate);
line.set$endY(-radius - 3.0f);
line.set$strokeWidth(2.0f);
line.set$stroke(Color.$RED);
}
face.loc$content.insert(line);
// Hour hand
Path path = new Path();
{
Rotate rotate = new Rotate();
BindingExpression exp = new AbstractBindingExpression() {
@Override
public void compute() {
pushValue((float) (hours + minutes / 60) * 30 - 90);
}
};
hoursVariable = FloatVariable.make(exp);
rotate.loc$angle().bind(false, hoursVariable);
path.loc$transforms.insert(rotate);
path.set$fill(Color.$BLACK);
MoveTo e1 = new MoveTo();
e1.set$x(4.0f);
e1.set$y(4.0f);
path.loc$elements.insert(e1);
ArcTo e2 = new ArcTo();
e2.set$x(4.0f);
e2.set$y(-4.0f);
e2.set$radiusX(1.0f);
e2.set$radiusY(1.0f);
path.loc$elements.insert(e2);
LineTo e3 = new LineTo();
e3.set$x(radius - 15.0f);
e3.set$y(0.0f);
path.loc$elements.insert(e3);
}
face.loc$content.insert(path);
// Minute hand
path = new Path();
{
Rotate rotate = new Rotate();
BindingExpression exp = new AbstractBindingExpression() {
public void compute() {
pushValue((float) minutes * 6 - 90);
}
};
minutesVariable = FloatVariable.make(exp);
rotate.loc$angle().bind(false, minutesVariable);
path.loc$transforms.insert(rotate);
path.set$fill(Color.$BLACK);
MoveTo e1 = new MoveTo();
e1.set$x(4.0f);
e1.set$y(4.0f);
path.loc$elements.insert(e1);
ArcTo e2 = new ArcTo();
e2.set$x(4.0f);
e2.set$y(-4.0f);
e2.set$radiusX(1.0f);
e2.set$radiusY(1.0f);
path.loc$elements.insert(e2);
LineTo e3 = new LineTo();
e3.set$x(radius);
e3.set$y(0.0f);
path.loc$elements.insert(e3);
}
face.loc$content.insert(path);
group.loc$content.insert(face);
}
}
Timeline timeline = new Timeline();
timeline.set$repeatCount(Timeline.$INDEFINITE);
KeyFrame kf = new KeyFrame();
kf.set$time(Duration.valueOf(1000.0f));
kf.set$canSkip(true);
kf.set$action(new Function0<Void>() {
public Void invoke() {
nextTick();
return null;
}
});
timeline.loc$keyFrames.insert(kf);
// this is somewhat hairy JFxtras does it like this I think
Scene scene = new Scene();
scene.loc$content().insert(group);
JPanel panel = new JPanel(new BorderLayout());
TKScene fxNode = scene.get$javafx$scene$Scene$impl_peer();
panel.add(((SwingScene) fxNode).scenePanel, BorderLayout.CENTER);
JFrame frame = new JFrame("JavaFX Clock Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setSize(295, 325);
frame.setVisible(true);
timeline.play();
}
public void nextTick() {
calendar.setTimeInMillis(System.currentTimeMillis());
seconds = calendar.get(Calendar.SECOND);
minutes = calendar.get(Calendar.MINUTE);
hours = calendar.get(Calendar.HOUR_OF_DAY);
// trigger bindings to re calc and move hands
if (secondsVariable != null) {
secondsVariable.invalidate();
minutesVariable.invalidate();
hoursVariable.invalidate();
}
}
public static void main(String[] args) throws Exception {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
new Clock();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
public class Clock extends CustomNode {
public var radius: Number = 77;
public var centerX: Number = 144;
public var centerY: Number = 144;
public var hours:Number;
public var minutes:Number;
public var seconds:Number;
public function nextTick () {
var now = new Date();
seconds = now.getSeconds();
minutes = now.getMinutes();
hours = now.getHours();
}
public override function create() : Node {
return Group {
content: [
ImageView {
image: Image {
url: "{__DIR__}clock_background.png"
}
},
Group {
transforms: Translate {
x: centerX,
y: centerY
}
content: [
// code to display the numbers for every third hour
for (i in [3, 6, 9, 12])
Text {
transforms: Translate {
x: -5,
y: 5
}
font: Font {
size: 16
}
x: radius * (( i + 0 ) mod 2 * ( 2 - i / 3))
y: radius * (( i + 1 ) mod 2 * ( 3 - i / 3))
content: "{i}"
}, //Text
//code to display a black circle for the rest of the hours on the clock
for (i in [1..12])
if (i mod 3 != 0 ) then Circle {
transforms: Rotate {
angle: 30 * i
}
centerX: radius
radius: 3
fill: Color.BLACK
} //for
else [ ],
// code for the clock's first center circle
Circle {
radius: 5
fill: Color.DARKRED
}, //Circle
//code for the smaller center circle
Circle {
radius: 3
fill: Color.RED
}, //Circle
//code for the seconds hand
Line {
transforms: Rotate {
angle: bind seconds * 6
}
endY: -radius - 3
strokeWidth: 2
stroke: Color.RED
}, //Line
//code for the hour hand
Path {
transforms: Rotate {
angle: bind (hours + minutes / 60) * 30 - 90
}
fill: Color.BLACK
elements: [
MoveTo {
x: 4,
y: 4},
ArcTo {
x: 4
y: -4
radiusX: 1
radiusY: 1},
LineTo{
x: radius - 15
y: 0},
] //elements
}, // Path
// code for the minutes hand
Path {
transforms: Rotate {
angle: bind minutes * 6 - 90
}
fill: Color.BLACK
elements: [
MoveTo {
x: 4,
y: 4},
ArcTo {
x: 4
y: -4
radiusX: 1
radiusY: 1},
LineTo{
x: radius
y: 0},
] // elements
} // Path
] //content
}
]
};
}
init {
var timeline = Timeline {
repeatCount: Timeline.INDEFINITE
keyFrames: [
KeyFrame {
time: 1s
canSkip: true
action: function() {
nextTick();
}
}
]
}
timeline.play();
}
}

public class GPSLayer extends OMGraphicHandlerLayer {
private static String GPSDATA = "gpsData";
private static final double KT2MPS = 1852.0 / 3600.0;
private static final double speedVectorLengthInMinutes = 6;
private String gpsDataPath = "";
private float latitude, longitude, speed, course;
private OMGraphicList graphics = new OMGraphicList();;
private OMRect gpsPosition = new OMRect(0, 0, 0, 0, 10, 10);
private OMLine speedVector;
private OMText gpsText = new OMText(10, 20, "GPS Data",
OMText.JUSTIFY_LEFT);
private Timer timer;
public GPSLayer() {
gpsText.setFillPaint(Color.WHITE);
gpsText.setTextMatteColor(new Color(182, 235, 219));
gpsPosition.setFillPaint(Color.pink);
}
@Override
public void setProperties(String prefix, Properties props) {
super.setProperties(prefix, props);
gpsDataPath = props.getProperty(prefix + "." + GPSDATA);
// redraw every 5 secs
timer = new Timer(5000, this);
timer.start();
// emulate reading GPS data (threading issue here access to members not
// protected!)
new Thread(new Runnable() {
public void run() {
try {
BufferedReader in = new BufferedReader(new FileReader(
gpsDataPath));
String str;
while ((str = in.readLine()) != null) {
Thread.sleep(100);
if (str.startsWith("$GPRMC")) {
String[] fields = str.split(",");
// utc_date = fields[1];
double lat = Double.parseDouble(fields[3]);
double degrees = Math.floor((lat / 100.0));
double minute = (lat / 100.0) - degrees;
lat = (degrees) + ((minute * 100.0) / 60);
if (fields[4].equals("S"))
lat = -lat;
latitude = (float) lat;
double lon = Double.parseDouble(fields[5]);
degrees = Math.floor((lon / 100.0));
minute = (lon / 100.0) - degrees;
lon = (degrees) + ((minute * 100.0) / 60);
if (fields[6].equals("W"))
lon = -lon;
longitude = (float) lon;
speed = (float) (Double.parseDouble(fields[7]) * KT2MPS);
if (!fields[8].equals("")) {
course = (float) Double.parseDouble(fields[8]);
}
// could parse date but to lazy
// date = fields[9];
}
}
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
@Override
public synchronized OMGraphicList prepare() {
gpsPosition.setLocation(latitude, longitude, -5, -5, 5, 5);
// calc. speed vector length using OM GIS functions
LatLonPoint startPos = new LatLonPoint(latitude, longitude);
float length = (float) Length.KM.toRadians(speedVectorLengthInMinutes
* ((speed * 3.6) / 60.0));
LatLonPoint endPos = startPos.getPoint(length, (float) ProjMath
.degToRad(course));
speedVector = new OMLine(startPos.getLatitude(), startPos
.getLongitude(), endPos.getLatitude(), endPos.getLongitude(),
OMLine.LINETYPE_STRAIGHT);
// vec.addArrowHead(OMArrowHead.ARROWHEAD_DIRECTION_FORWARD, 100, 3, 1);
speedVector.setLinePaint(Color.DARK_GRAY);
graphics.clear();
// order of add determines what is rendered on top
graphics.add(speedVector);
graphics.add(gpsPosition);
gpsText.setData(String.format("GPS Data\n%4.2f Km/h",
(speed * 3.60)));
graphics.add(gpsText);
graphics.project(getProjection());
return graphics;
}
@Override
public void actionPerformed(ActionEvent ae) {
doPrepare();
}
}
uc = new URL(gpsDataPath).openConnection(); // gps.gpsData=http://localhost:2244
InputStreamReader icr = new InputStreamReader(uc.getInputStream());
in = new BufferedReader(icr);
openmap.layers=gps graticule shapePolitical
openmap.startUpLayers=gps graticule shapePolitical
gps.class=GPSLayer
gps.prettyName=GPS Position
gps.gpsData=/tmp/gpslog.txt
$GPRMC,143346,A,5616.9232,N,01008.2504,E,074.6,011.3,110105,000.9,E,A*13
$GPRMC,143347,A,5616.9435,N,01008.2571,E,074.5,010.4,110105,000.9,E,A*14
$GPRMC,143348,A,5616.9638,N,01008.2632,E,074.4,009.5,110105,000.9,E,A*18
$GPRMC,143349,A,5616.9842,N,01008.2686,E,074.3,008.4,110105,000.9,E,A*12
/**
*
* @See http://puces-blog.blogspot.com/2009/04/netbeans-platform-meets-swing.html
*/
public class ModuleApplicationContext extends ApplicationContext {
private String storageDirectoryPath = "";
static {
// download from https://jdnc-incubator.dev.java.net/source/browse/jdnc-incubator/trunk/src/kleopatra/java/org/jdesktop/appframework/swingx/XProperties.java?rev=3198&view=markup
new XProperties().registerPersistenceDelegates();
}
public ModuleApplicationContext(String path) {
// Needed due to issue
// https://appframework.dev.java.net/issues/show_bug.cgi?id=112
setLocalStorage(new ModuleLocalStorage(this));
// getLocalStorage().setDirectory(getModuleSessionStorageDir(moduleInfo));
storageDirectoryPath = path;
getLocalStorage().setDirectory(new File(storageDirectoryPath));
getSessionStorage().putProperty(JXTable.class,
new XProperties.XTableProperty());
}
}
/**
* A LocalStorage for modules. It respects the direcory property in JNLP mode.
*
* Needed due to issue * HREF="https://appframework.dev.java.net/issues/show_bug.cgi?id=112">
* https://appframework.dev.java.net/issues/show_bug.cgi?id=112
*
* @author puce
*/
public class ModuleLocalStorage extends LocalStorage {
public ModuleLocalStorage(ApplicationContext context) {
super(context);
}
@Override
public boolean deleteFile(String fileName) throws IOException {
File path = new File(getDirectory(), fileName);
return path.delete();
}
@Override
public InputStream openInputFile(String fileName) throws IOException {
File path = new File(getDirectory(), fileName);
return new BufferedInputStream(new FileInputStream(path));
}
@Override
public OutputStream openOutputFile(String fileName) throws IOException {
File path = new File(getDirectory(), fileName);
return new BufferedOutputStream(new FileOutputStream(path));
}
}
public class TestJXTable extends JFrame {
String data[][] = { { "John", "Sutherland", "Student" },
{ "George", "Davies", "Student" },
{ "Melissa", "Anderson", "Associate" },
{ "Stergios", "Maglaras", "Developer" }, };
String fields[] = { "Name", "Surname", "Status" };
ModuleApplicationContext mac;
JXTable jt;
JScrollPane pane;
public static void main(String[] argv) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
TestJXTable myExample = new TestJXTable(
"JXTable Example");
}
});
}
public TestJXTable(String title) {
super(title);
// save settings in users home dir
mac = new ModuleApplicationContext(System.getProperty("user.home"));
setSize(150, 150);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
dispose();
try {
jt.getParent().remove(jt);
mac.getSessionStorage().save(jt, "testTable.xml");
} catch (IOException e) {
e.printStackTrace();
}
System.exit(0);
}
});
init();
pack();
setVisible(true);
}
private void init() {
jt = new JXTable(data, fields);
jt.setColumnControlVisible(true);
jt.setName("testTable");
pane = new JScrollPane(jt);
pane.setName("testPane");
try {
mac.getSessionStorage().restore(jt, "testTable.xml");
} catch (IOException e) {
e.printStackTrace();
}
getContentPane().add(pane);
}
}
public class FontPanel extends OMComponentPanel {
public FontPanel() {
}
@Override
public void findAndInit(Object obj) {
if (obj instanceof InformationDelegator) {
InformationDelegator delgator = (InformationDelegator) obj;
GridBagConstraints c = new GridBagConstraints();
c.weightx = 1;
c.weighty = 1;
c.anchor = GridBagConstraints.EAST;
c.fill = GridBagConstraints.HORIZONTAL;
c.insets = new Insets(0, 0, 0, 4);
Component statusPanel = DialogUtils.getChildNamed(delgator,
StatusLightPanel.class);
delgator.remove(statusPanel);
delgator.add(FontSizePanel.getPanel(), c);
delgator.add(statusPanel);
}
}
}
-Dswing.defaultlaf=org.jvnet.substance.skin.SubstanceCremeLookAndFeelClick here to see how this looks.

DockingUISettings.getInstance().installUI();
//and start customizing... MyDockViewTitleBarUI
UIManager.put("DockViewTitleBarUI", "MyDockViewTitleBarUI");
public class MyDockViewTitleBarUI extends DockViewTitleBarUI {
public MyDockViewTitleBarUI(DockViewTitleBar tb) {
super(tb);
SubstanceLookAndFeel.setDecorationType(tb,
DecorationAreaType.PRIMARY_TITLE_PANE);
tb.setForeground(SubstanceColorUtilities
.getForegroundColor(SubstanceColorSchemeUtilities
.getColorScheme(tb, ComponentState.ACTIVE)));
}
static public MyDockViewTitleBarUI createUI(JComponent tb) {
return new MyDockViewTitleBarUI((DockViewTitleBar) tb);
}
@Override
public void paint(Graphics g, JComponent c) {
DockViewTitleBar tb = (DockViewTitleBar) c;
SubstanceSkin skin = SubstanceCoreUtilities.getSkin(tb);
if (skin != null) {
SubstanceDecorationUtilities
.paintDecorationBackground(g, tb, false);
} else {
super.paint(g, tb);
}
}
}
public class DefaultTableFactory implements TableFactory {
public JTable createTable() {
JXTable result = new JXTable();
return configureTable(result);
}
public JTable createTable(TableModel model) {
JXTable result = new JXTable(model);
return configureTable(result);
}
private JXTable configureTable(JXTable result) {
result.getSelectionMapper().setEnabled(false);
result.setColumnControlVisible(true);
result.setHighlighters(createHighlighter(result), new ColorHighlighter(
HighlightPredicate.ROLLOVER_ROW, null, Color.BLUE));
result.setRolloverEnabled(true);
result.setHorizontalScrollEnabled(true);
return result;
}
public CompoundHighlighter createHighlighter(JXTable t) {
ColorHighlighter first = new SubstanceHighLighter(HighlightPredicate.EVEN, t);
ColorHighlighter hl = new SubstanceHighLighter(HighlightPredicate.ODD,t);
return new CompoundHighlighter(first, hl);
}
// get striping on jxtable to work with substance
public class SubstanceHighLighter extends ColorHighlighter {
private JXTable comp;
SubstanceHighLighter(HighlightPredicate pred, JXTable t) {
setHighlightPredicate(pred);
comp = t;
}
@Override
public Color getBackground() {
return SubstanceColorUtilities.getStripedBackground(comp,
getHighlightPredicate() == HighlightPredicate.EVEN ? 1 : 0);
}
}