Monday, March 31, 2008

How to create OpenMI based JGrass models

As some of you probably know, the JGrass console engine has been designed to automagically link together OpenMI based models.

This post should really be a big big document and at some point it will be usefull also for first-time-openmi-developers, but as I start now, it is some growing documentation for the JGrass team members, so that we all follow same way of doing things.

So dear guest, if you feel this all sounds strange, don't worry, it has to.


Alright, let's start with some sparse thoughts:

1) for now I will assume you are extending the eu.hydrologis.jgrass.models.ModelsBackbone class
I don't have to tell you that you should not touch that class, right? :)

2) the out and err printstreams have been moved to the ModelsBackbone class. So do not re-declare them in your model's class.
You will be able to access out and err streams from your class, since they are declared protected in the ModelsBackbone.

Do not have such as the following lines in your model, else you will get troubles:

private PrintStream out;
private PrintStream err;

3) implement safeGetValues and safeInitialize instead of getValues and initialize

Two methods have been added to ModelsBackbone to trap exceptions and give the user a feedback on what is going on:
- safeGetValues
- safeInitialize

Those two methods are called from within the interface methods getValues and initialize and are wrapped inside a try-catch block that traps three types of Exceptions:

  • OutOfMemory: this one is trapped and sends a standard message to the user on the console, suggesting to add memory to the JGrass process. This is an error that we know very well, how often the active region is too big or the resolution to high and some analysis eats all the memory? With this trap the user will finally be warned.
  • ModelSematicException: this one is the most important for models developers. It takes a message for the user (passed directly as error to the console) and when thrown, it exits the safeGetValues or safeInitialize method and prints the message to console. That way semantic problems of a model can be told to the user. An example could be a wrong input flag in the models arguments or a non existing map warning. When processing the exception, also the variable isOkToGo is set to false. This is a protected boolean in the ModelsBackbone class, so it can again be used everywhere in the model to stop execution. If for example in the initialize method something goes wrong, the developer can throw a ModelSematicException and from that moment on the isOkToGo boolean is false and can be checked as first thing in the safeGetValues method.
  • Exception: every other exception thrown is sent to console, prefixed by a standard message introducing the real problem. Users will not love the message, but they will at least have something to report :)

4) throw ModelSematicException if there are problems

Inside your model, if something goes wrong and you want to write something usefull to the user, throw a ModelSemanticException passing it your message. It will take care of it.

5) do not call safeGetValues and safeInitialize directly

Always call the OpenMI interface methods getValues and initialize! Else you will lose beeing OpenMI based!!

6) to finish for now, a small code example:

The following shows a model (like the one you are writing if you read until now) in which a check on the passed parameters is done. If in troubles, the exception with a nice message is thrown:

public void safeInitialize( IArgument[] properties ) throws Exception {

String grassDb = null;
String location = null;
String mapset = null;
if (properties != null) {
for( IArgument argument : properties ) {
String key = argument.getKey();
if (key.compareTo(ModelsConstants.GRASSDB) == 0) {
grassDb = argument.getValue();
} else if (key.compareTo(ModelsConstants.LOCATION) == 0) {
location = argument.getValue();
} else if (key.compareTo(ModelsConstants.MAPSET) == 0) {
mapset = argument.getValue();
} else if (key.compareTo("activefield") == 0) { //$NON-NLS-1$
activeField = argument.getValue();
if (activeField == null) {
isOkToGo = false;
String pattern = "Parameter {0} supposed to be used but not supplied.";
Object[] args = new Object[]{key};
pattern = MessageFormat.format(pattern, args);
throw new ModelsSematicException(pattern);
}
} else {
isOkToGo = false;
String pattern = "Parameter {0} not recognized for model h.netshape2flow.";
Object[] args = new Object[]{key};
pattern = MessageFormat.format(pattern, args);
throw new ModelsSematicException(pattern);
}
}
}



Behind the coulisse, in the ModelsBackbone class, the exception is trapped and the isOkToGo variable is set to false:


try {
safeInitialize(properties);
} catch (OutOfMemoryError e) {
err.println(Messages.getString("ModelsBackbone.outofmemory")); //$NON-NLS-1$
e.printStackTrace();
isOkToGo = false;
return null;
} catch (ModelsSematicException e) {
err.println(e.getLocalizedMessage());
isOkToGo = false;
return null;
}
//... etc. etc.


Again in the ModelsBackbone class, when it comes to do the job, which is in the getValues method, before launching the safeGetValues method, a check is done if before that moment everything was ok, in order to decide wheather to stop or go on:


public IValueSet getValues( ITime time, String linkID ) {
// if there were problems, stop and return null
if (!isOkToGo) {
return null;
}
// if everything ok, do your stuff
try {
return safeGetValues(time, linkID);
}
//... etc. etc.



The same can be done eveywhere inside your own code (i.e. not in ModelsBackbone).

Thursday, March 27, 2008

How to translate messages with strings inside

Often you need to translate strings that contain one or more variables that in the different languages stay in different positions of the string, so a solution like the following gives huge problems for those who have to translate:


out.println("h.netshape2flow TOOK " + timetaken + " sec");


That is why java gives us an alternative way to deal with this:


String pattern = "h.netshape2flow TOOK {0} sec";
Object[] args = new Object[]{timetaken};
pattern = MessageFormat.format(pattern, args);
out.println(pattern);


After struggling around for a while with messages in which the {0} wasn't substituted at all, I found a problem that Italian translator will have:

Problem: the apostrophe makes the messageformat go mad. So something like
"Non ho trovato l'attributo {0} nel piano {1}."
is tranformed to:
"Non ho trovato lattributo {0} nel piano {1}."

Solution: put every time 2 apostrophes.
"Non ho trovato l''attributo {0} nel piano {1}."
is tranformed to:
"Non ho trovato l'attributo bacino nel piano layer_bacino."

Perhaps all do not know that... the interface JGrassConstants holds important path pieces

Since lots of things change as time passes by, the GRASS location and mapset path pieces are hold in a facility interface called JGrassConstants.
So instead of writing WIND to define the active region file, you can (or better should, or better must) use JGrassConstans.WIND

Here some of the most important parts:

/** folder of the JGrass database structure */
public final String PERMANENT_MAPSET = "PERMANENT";

/** folder of the JGrass database structure */
public final String DEFAULT_WIND = "DEFAULT_WIND";

/** folder of the JGrass database structure */
public final String PROJ_INFO = "PROJ_INFO";

/** folder of the JGrass database structure */
public final String PROJ_WKT = "PROJ_INFO.WKT";

/** folder of the JGrass database structure */
public final String PROJ_UNITS = "PROJ_UNITS";

/** folder of the JGrass database structure */
public final String WIND = "WIND";

/** folder of the JGrass database structure */
public final String MYNAME = "MYNAME";

/** folder of the JGrass database structure */
public final String FCELL = "fcell";

/** folder of the JGrass database structure */
public final String CELL = "cell";

/** folder of the JGrass database structure */
public final String CATS = "cats";

/** folder of the JGrass database structure */
public final String HIST = "hist";

/** folder of the JGrass database structure */
public final String CELLHD = "cellhd";

/** folder of the JGrass database structure */
public final String COLR = "colr";

/** folder of the JGrass database structure */
public final String CELL_MISC = "cell_misc";

/** folder of the JGrass database structure */
public final String CELLMISC_FORMAT = "f_format";

/** folder of the JGrass database structure */
public final String CELLMISC_QUANT = "f_quant";

/** folder of the JGrass database structure */
public final String CELLMISC_RANGE = "f_range";

/** folder of the JGrass database structure */
public final String CELLMISC_NULL = "null";

Tuesday, March 25, 2008

How to keep track of memory status

In our field of application, heavily using raster maps and analysing them, we get into out-of-memory problems rather often. Not always these exceptions are trapped and we end up waiting for something that will never come.

So one way to keep track of all this, is the nice FreeMem plugin by Markus Oliver. It is really easy to install throught the update manager (explained in the last post).
Just enter http://www.junginger.biz/eclipse/ as your new remote update site.

Install it and restart JGrass (Udig/Eclipse, it will work for all of them). In the list of loadable views there will be a new one, the FreeMem, which will look like this:



In the list of installable plugins there is also a RSS-feed reader. Just for fun I installed it also in JGrass and inserted as test the feed to this blog :)



I love this rcp environment!

Tuesday, March 18, 2008

How to load plugins from an update site

Imagine you want to install the very experimental and pre-pre-alpha beegis geonotes inside my current JGrass installation. What would you do?
Nothing easier than that:

1) in the help menu go on software updates -> find and install:



2) select on search for new features and go next:



3) push the New Remote Site button and enter the update site you which to install the plugin from. In this example I will use beesgis-extentions update site which is here:
http://www.beegis.org/upd-beegis/eu.hydrologis.jgrass.beegis-updatesite/




4) after clicking OK and finish, a list of available packages supplied by the update-site will appear. Select what you want to install:




5) next you will be asked to agree the licenses and so on.



6) if the packages are not signed, you will be warned and prompt about installation. If you are sure, just press install all.



7) you will be asked to restart JGrass. Do so and after restart you will find the geonotes layer in you mapgraphics and the geonotes tool in the info category tools.



The update is done. You can enjoy your new plugin.

To be honest there is one more step to go, since without the right database and database tables there is nothing to be done.

So get the database zipped from here and unzip it into some folder of your choice.

Now let's tell jgrass where the database is. Open the preferences, (Window->Preferences) and under HSQLDB server point to the unzipped geebisdatabase server:



Restart JGrass and the game is done.


PS: if you instead are interested in creating an update site for others, have a look at this nice article from the eclipse wiki.

Monday, March 17, 2008

How to find and use Bursa Wolf parameters for accurate coordinate transformation

Many times I had and heard about the problem of missing Bursa Wolf parameters for more accurate re-projections of data. While GRASS deals with them properly, most of the other GIS and also postgis do not. In Italy we have one of these odd problems related to the fact that historically we used to use as the main projection the EPSG:3003, Monte Mario / Italy Zone 1 projection.
Lately, everyone is migrating for standardization to the EPSG:32632, WGS 84 / UTM zone 32N.

What is the problem in all this?
The fact that the epsg 3004 comes like the following:


PROJCS["Monte Mario / Italy zone 1",
GEOGCS["Monte Mario",
DATUM["Monte_Mario",
SPHEROID["International 1924", 6378388.0, 297.0, AUTHORITY["EPSG","7022"]],
AUTHORITY["EPSG","6265"]],
PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
UNIT["degree", 0.017453292519943295],
AXIS["Lon", EAST],
AXIS["Lat", NORTH],
AUTHORITY["EPSG","4265"]],
PROJECTION["Transverse_Mercator"],
PARAMETER["central_meridian", 9.0],
PARAMETER["latitude_of_origin", 0.0],
PARAMETER["scale_factor", 0.9996],
PARAMETER["false_easting", 1500000.0],
PARAMETER["false_northing", 0.0],
UNIT["m", 1.0],
AXIS["x", EAST],
AXIS["y", NORTH],
AUTHORITY["EPSG","3003"]]

which doesn't contain the Bursa Wolf parameters (to know more about it read here) without which there can be reprojection problems of many meters.

So we need a way to get those Bursa Wolf parameters.
Thank God there is a nice site called: http://www.epsg-registry.org

Jump in there and click on the upper tab called: retrieve by code.
You could also search in the query by filter tab, this will be explained later in the post.

I will insert epsg 1660 and click on retrieve, which supplies me the following:



Select your area and click on Report selected results.



Please note that the domain of validity is described. Italy mainland, which is what I need.

Fill in what is asked and just press play :)


On the lower part you will find the needed Bursa Wolf parameters, often also referred to as the "towgs parameters" :)

What is needed to be done, is to add to the 3003 definition these parameters:

TOWGS84[-104.1, -49.1, -9.9, 0.971, -2.917, 0.714, -11.68],

which will result in having the following definition:


PROJCS["Monte Mario / Italy zone 1",
GEOGCS["Monte Mario",
DATUM["Monte Mario",
SPHEROID["International 1924", 6378388.0, 297.0, AUTHORITY["EPSG","7022"]],
TOWGS84[-104.1, -49.1, -9.9, 0.971, -2.917, 0.714, -11.68],
AUTHORITY["EPSG","6265"]],
PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]],
UNIT["degree", 0.017453292519943295],
AXIS["Geodetic longitude", EAST],
AXIS["Geodetic latitude", NORTH],
AUTHORITY["EPSG","4265"]],
PROJECTION["Transverse_Mercator"],
PARAMETER["central_meridian", 9.0],
PARAMETER["latitude_of_origin", 0.0],
PARAMETER["scale_factor", 0.9996],
PARAMETER["false_easting", 1500000.0],
PARAMETER["false_northing", 0.0],
UNIT["m", 1.0],
AXIS["Easting", EAST],
AXIS["Northing", NORTH],
AUTHORITY["EPSG","10003003"]]

Note that I also changed the 3003 to 10003003 to be sure tyo not have conflicts with other crs. I created that in postgis as addionional CRS in order to be able to do proper transformations.

Usually you will not have the proper epsg code to use, right?
Luckily Antonio (thanks man, got a link to share?) supplied us with a better method to find what you are looking for.
Assuming you are still on the epsg-registry site:
1. start by clicking on "query by filter" button;
2. click on "Type" textbox and choose Coordinate Transformation and then Single only;
3. write the name of the area of use (e.g. Italy) in the "Area" textbox and finally click on Search button.
You will get a whole list of possible transformations as a result, which will probably be too many, so if you want to limit them to your area of interest to get the most precisely defined transformation for your region, you have to follow step 1. and 2. the same as before before.
Then:
2. write the name of the transformation of use (e.g. "Monte Mario to WGS 84") in the "Name" textbox
3. click on "Type" textbox and choose Coordinate Transformation, Single only and finally click on Search button.


In JGrass / uDig this is way easier. Click on the button reporting the projection definition. A window will ask to choose the crs from a list. A second tab says: Custom CRS. Select that one and paste your crs definition (in my case the 10003003) into the box and press apply and ok.

Thanks to the GFOSS.it mailinglist for discussing this and to Antonio Falciano for reporting the epsg-registry site.

Wednesday, March 12, 2008

Java is obsolete!!!


Folks, I have to share this! Today I got an incredible mail from which I got (from C developers) that java is an obsolete language. So together with the Hydrologiclady we tried to define this strange scale of values.
  • Java - obsolete?
  • C - archaic?
  • fortran - jurassic?
  • what next?
I find this flames always funny, cause in the end everyone is free to choose, but possibly without throwing shit on others!
Well, well, excuse me now, I have to go back to obsolution.

Sunday, March 9, 2008

Put your geonote on the map like a postit-note

One of the results of my PhD with the University of Urbino is the creation of geonotes, i.e. the possibility to put notes containing drawings, text or any known and unknown file, on a map.
This is part of a set of tools that we are developing for geological field surveys, named Beegis. There is a new site, which is started about Beegis at www.beegis.org.

Alright, let's come back to our notes.
If you load JGrass with the needed plugins, you will notice a new mapgraphic object in the catalog: the geonotes layer


Once the layer is dragged onto the map, nothing happens, since we do not have notes in our database yet.

If you click on the information button and hold until the dropdown list appears, you will see also a geonotes tool. This is the tool that is able to create new geonotes or open existing ones.

Create a new geonote
Let's create a new note. Select the geonotes tool and click somewhere on the map.
A brand new geonote appears in its default state.


The geonote is made of an upper part containing a title label, a few small buttons throught which the background of the note can be changed, and two buttons used to close the note saving or not saving it.

The lower part is completely dedicated to the taking of notes. It is separtated in three different tabs, each of which with a different usage.

1) the drawing tab
This tab reminds us that beegis is mainly dedicated to tablet pc usage on field and therefore is needed to take fast notes throught the tablet's pen. Here a try with my laptop's mousepad.



2) the text tab
On this tab text can be written with the keyboard.



3) the media tab
This tab is a very useful one. In this tab you can draw every possible type of file and it will be stored inside your note. That means that you can load into it pictures of your field survey or whatever else comes to your mind.


Once you are done taking your notes, you can decide to save the geonote by clicking on the disk button. Be carefull, since the files you dragged into the media area are saved into the hsqldb database that comes with JGrass.
You can also decide to discard your recent changes by clicking on the button with the red X. This will close the note without saving it.

Settings
It is possible to define some settings. To access the settings tab, just doubleclick on the title label and the panel will appear. Set title, drawing width, colors, and click on ok, and the note will update its style.


Save the note
Once you save the note, on the map a pin remains. The note is saved with its coordinates and the used reference system. That is important because teh position is reprojected on the fly for every needed projection system and can be used in evey case.


Open a geonote
To open a geonote you simply need to take the geonotes tool and click as near as possible on the pin in the point it gives the sensation to enter the map. If your note doesn't open, but instead a fresh new note appears, simply close it without saving it and try again closer.

Transfer geonotes
For now geonotes are kept totally in the hsqldb database, so by passing to someone else the folder containing the notes, nothing else is needed for him to see your notes.

Tuesday, March 4, 2008

How to install a new plugin without waiting for a new release

Assuming that the plugin was supplied by someone who knows how the release was made (java versions, etc etc), this is really easy.

1) close any JGrass session you could have open

2) get the new plugin ( ex. download the plugin from the jgrass site) and put it into the same folder in which the jgrass installation folder is.




3) extract the plugin if it was compressed



4) enter in the jgrass installation: grass->plugins and remove the plugin you are going to substitute.



5) copy the downloaded plugin into the plugins folder


6) restart JGrass and as usual... enjoy!



PS: this obviously works for any plugin, therefore also eclipse plugins (if you want to add a tetris plugin to JGrass, you could judt do so) and udig plugins

Monday, March 3, 2008

Glad to see there is interest

After all this time without a new JGrass, I was really afraid about how people could react to the new JGrass release... I'm glad to see there is interest, also because there haven't been much announcements from my side.

Can you guess the official release day? :)


Docs: load a GRASS location

So you want to see a GRASS location in JGrass?
To do it the funky way, just create an empty file named wks.jgrass into the location.


After that, just drag the *.jgrass file into the catalog view of JGrass.

If it is the first time you watch that location in JGrass, then you will be asked to choose the proper projection. This due to an incompatibility of the projection information between JGrass and GRASS.

So you will get a list of projections between which you will have to choose one:



JGrass will create a WKT file with the projection info in it.

And finally the location appears and you can browse the mapsets and drag the maps into the map window to view them:


There is also an import button on the catalog view that launches a wizard to the import of data.

Sunday, March 2, 2008

JGrass released in prealpha after an amazing year!!

2008/02/29 the day I finally packaged a prealpha JGrass release!

We hope that releasing at the 29th of a leap year will be of good luck for the project and not mean that we will release every 4 years :).

This is the first touchable result of a long time of heavy development to migrate the old JGrass functionalities into the new udig framework. Enjoy!

I would like to personally thank the great uDig community that accepted us and gave us every needed help. Hat off to Refractions and its developers. And Jesse and Jody, you really were patient with us. Thank you so much! There is always a free beer for you in Italy ;)

Well, what more to say? Run to the new JGrass page and try it out!