Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

Tuesday, August 19, 2014

STAGE - Desktop Tools (also) for Geopaparazzi

With the new upcoming geopaparazzi 4 release a few issues had to be solved:
  1. What should I do with all my old geopaparazzi 3 surveys? How do I integrate them with new data?
  2. What do I use to export data from a geopaparazzi 4 project to my GIS environment?
Well, we solved both the above problems inside STAGE, a Spatial Toolbox And Geoscripting Ennvironment, which is the tool we develop and use for the JGrasstools library for environmental modelling.

For those asking if it is the same Spatial Toolbox that comes with uDig, the answer is yes and no. Yes, because it is the same source code. And no, because since uDig is in the process of (and a bit stuck in) migrating through the Locationtech incubation, we needed to step forward with the geotools versions. So see STAGE as the temporary solution to work with the Spatial Toolbox, until we can enhance the uDig version again.


So where do I get STAGE and how do I use it for Geopaparazzi? A video tutorial showing it all can be found here:




Enjoy!



ADDENDUM: since it has been asked, this is the link to the current download area of STAGE: http://git.io/stage_releases


Saturday, July 9, 2011

eRCP, windows mobile 6, phoneMe, J9 fruit salad

I was asked to investigate a geopaparazzi port on Trimble Juno SC. Well, obviously it is not Android. But not only that, it is also not open source at all. In fact it is one of the closest source situations I have been in many years. Anyways, while investigating all the .Net and C# and gps apis, I also gave a look at a project I fell in love with long time ago, but never really had the possibility to try: eRCP.

To be honest the project looked quite dead and it took me a really long time to figure out the current way to do things best.

The best thing right now is to download the Pulsar eclipse IDE project
and install the ERCP SDK through the update site, which now is: http://www.eclipse.org/ercp/pulsar. At that point the sdk is installed.

At that point the docs tell to go under preferences->target platform and define a new target platform based on a new eRCP template. That will take the internal sdk and load it as target platform to develop against. Well, later I found out that it didn't work for me.
What worked instead was to download:
- the ercp sdk
- the win32 runtime
- the windows mobile runtime
and add those to the target platform configuration. At that point I was ready to develop.

First thing I wanted to run the demo codes, that can be found in the ercp cvs at dev.eclipse.org.

So simply create a cvs repo with:
host = dev.eclipse.org
repo path = /cvsroot/rt
using anonymous user and browse the HEAD into the org.eclipse.ercp folder to checkout as a project the org.eclipse.ercp.app application. That way you can run the example applications on your windows pc.

Obviously you want to run them on the mobile devide. Ok, that was trickier.

First thing you need to upload top the device (let's say in the root folder) the eRCP folder that can be found inside the windows mobile runtime downloaded before. That one contains also the example runner files.

The few examples found all make use of the J9 IBM environment to run the eRCP demos. Well, that one is not open source and also not free right now. But it seemed the only possible way to go. I tried to use the J9 bundle that gvSig mobile is using but was not successful. Being in need to really know if eRCP is a way to go, I finally decided to buy the J9 environment (I hate to buy things when I don't even know if I will ever need them!). Once bought, I installed it on the Juno and was looking forward to see the examples work.

Well, that also didn't happen. The launcher crashed and I was getting more and more frustrated. But I spent money and now it HAD to work. So I started to search for solutions for the J9 crashes.

That was the moment when the following two blog posts came in my way:
Phoneme advanced and eSWT Running Under x86 Windows
Running eRCP with Phoneme Advanced on your mobile phone

This man, apart of writing amazing posts, made me feel stupid for having bought something I didn't want (I deserve it), but also really solved my issues.

From the phoneme I donwloaded the right cab, uploaded it to the juno and installed it.

I then had to change the launcher links of the examples a bit from:

37#"\Program Files\J9\FOUN10\bin\j9.exe" -jcl:foun10 -cp \eRCP\startup.jar org.eclipse.core.launcher.Main -application org.eclipse.ercp.eworkbench.eWorkbench -console

to:

37#"\Programmi\pMEA FP\bin\cvm.exe" -Djava.specification.name="J2ME Foundation Specification" -cp \eRCP\startup.jar org.eclipse.core.launcher.Main -application org.eclipse.ercp.eworkbench.eWorkbench -consoleLog

And it finally worked!! Being an RCP developer for the uDig project I really love the eRCP approach. Now I just have to understand how to take pictures in windows mobile from eRCP and then I am really happy.

I hope this can help someone else to don't lose maoney and time in trying just to make some demo code run.

Thursday, July 2, 2009

jai & tail: cannot open `+215' for reading: No such file or directory

tail: cannot open `+215' for reading: No such file or director

I already had this once and I was able to forget. When installing jai_imageio in your jdk you might get this.
For example it happend me with jai_imageio-1_1-lib-linux-amd64-jdk.bin but I remember last time it was on a 32 bit.

The fix is really easy with sed (do not use a text editor to change, else the binary content of the installer will get corrupted). You just have to change the tail command syntax to work:

sed s/+215/-n+215/ jai_imageio-1_1-lib-linux-amd64-jdk.bin > jai_imageio-1_1-lib-linux-amd64-jdk-fixed.bin

Then run sh jai_imageio-1_1-lib-linux-amd64-jdk-fixed.bin


--------------
Update:
as a gentle anonymous hacker stated:

export _POSIX2_VERSION=199209

also works and is nicer than the idea to change the installation file. Thanks!

Wednesday, September 17, 2008

Notes on guidelines for writing readable code documentation

Last weekend the team of HydroloGIS went to visit the Geomatys team in Montpellier (mostly Martin and Adrian, which have the same attitude to work on weekends that we have :) ) . Apart of having a great time with those nice guys in discussing most of the scientific issues that rule the universe, we had a very long and good discussion about geoapi/geotools coverages (but that will be the content of another post at some point).

After having been amazed by the gorgeous level and readability of their code documentation (if you can find a non well documented class in this project, Martin would probably buy you a beer for having read it all :) ), I asked them to have a chat about code documentation. Well, what follows here are some notes about that chat.

Note that this will be related to javadoc documentation.

General rules:
  • Explain in you documentation why the class/method is used and how.
  • the first sentence of each javadoc is a short summary of what the class/methos/variable does. That sentence is put in every field reporting summary. It should therefore never start with a paragraph tag.
  • put always a first sentence, else you will end up with empty overviews in the documentation
  • For methods: always start with a verb. For fields: always start with a noun. For classes/interfaces: verb or noun, on a case-by-case basis. Of course exceptions can occurr...
  • end sentences with the dot.
  • document everything, especially public items
  • embed multiline code snippets in a pre tag. It will use a monospace font and write the contained parts in verbatim. Much better would be to create a taglet which could deal with formatting and coloring of the syntax.
  • if there are different constructors of the class, explain how and why you would use them.
  • lines should be at most 80 characters long.

Class annotations (minimum set to be used):
  • @author obviously defines the author. It will be converted to a comma separated list of authors in the case of more than one, so avoid to put commas in the author annotation. If you want to add the affiliation o fthe author, put it between brackets.
  • @since, @version a class may be there since version 1.0 and I may do some significant changes in version 2.0, in which case I upgrade the @version value to "2.0" but it doesn't allow me to remove this class. On the other hand, if I just introduced this class in version 2.0 (which would be indicated by @since 2.0) and I have not yet released this version of the software, then I still allowed to change my mind and remove this class/method.
  • @see here other classes, interfaces or methods can be referenced, that are somehow related to the current one
  • @deprecated defines that the class could disappear in future releases (usually the following one). Explain in this tag what the replacement is and if there is none, explain why.

Innline annotations:
  • @link defines a hyperlink inside the documentation to the related class. As a rule of tumb always link to a certain class only the first time it appears in the documentation block, in order to avoid to make the docu unreadable.
    • the basic syntax is {@link package.and.class.name}
    • it is possible to add a text for the link: {@link package.and.class.name text}, in order to make it more readable, instead of having just the name of the class.
    • link annotations are written in some monospace font and also permit the insertion of symbols like > and <.
    • in the case one would not want to have a monospace font, but instead the current font of the documentation, the @linkplain annotation can be exploited
    • links to methods are build the html anchor way with an #: {@link package.and.class.name#method}
  • @value this link can be used for example in the case of constant variables: {@value package.and.class.name#const} will supply that value in the javadoc
  • @code can be used for inline snipptes. They will be written in monospace font.

Package documentation:
  • to document a package, in recent versions of java, add a PackageInfo.java file containing the javadoc to the package.





I wrote this mostly because I need to get more consistent in my code documentation, but I will also try to create a small guide to put on the JGrass wiki out of this and future reviews, in order to pass it to beginning developers that join our team. Learning things properly from the start is much better than forcing ourself to get them later, when there seems to never be the time.

Tuesday, May 27, 2008

How to pass data from java to C with incredible ease

From the moment I started to develop in java, because of the interaction with academic environmental modelers, I often had the problem of retrieving data with java, or having a GUI in java, but needing to use the data in a supercomplex C or fortran algorithm.
So I spent days and nights in learing and applying JNI techniques, hating them every day.
Then one day SWIG came around and helped a bit, but I yet find pure java development much more fun and maintainable :)

And then one day this guy on the imagio mailinglist pointed me to JNA... and I gave it a first simple try... and I have to share this...
no nightmare...
no worries...
just the time to write the code... INCREDIBLE :)

Since I am currently working on porting the GRASS binary raster format to imageio, i will do an example with that. I will read a GRASS raster and print its content in java, in C as pointer of doubles and as array of doubles.

Step one: get the jni.jar from the link above

Step two: write some java code that reads an image or, as in my case a raster map


public class PrintImageInC {

public PrintImageInC( String filePath ) {

try {
// create the grass reader
GrassBinaryImageReaderSpi readerSpi = new GrassBinaryImageReaderSpi();
ImageReader reader = readerSpi.createReaderInstance();
File file = new File(filePath);
reader.setInput(file);
int imageIndex = 0;

// set the novalue to use
GrassImageReadParam imageReadParam = new GrassImageReadParam();
imageReadParam.setNovalue(-9999.0);

// read the raster
BufferedImage bi = reader.read(imageIndex, imageReadParam);

// get the databuffer out of the raster and wrap the data array
DataBufferDouble dataBuffer = (DataBufferDouble) bi.getData().getDataBuffer();
double[] ds = dataBuffer.getBankData()[0];

int rows = bi.getHeight();
int cols = bi.getWidth();


WritableRaster raster = bi.getRaster();
System.out.println("Printing array in Java");
System.out.println("-----------------------------------");
for( int i = 0; i rows; i++ ) {
for( int j = 0; j cols; j++ ) {
System.out.print(raster.getSampleDouble(j, i, 0) + " ");
}
System.out.println();
}
System.out.println();

PrintLibrary.INSTANCE.printimageArray(ds, rows, cols);

DoubleBuffer wrapDs = DoubleBuffer.wrap(ds);
PrintLibrary.INSTANCE.printimagePtr(wrapDs, rows, cols);

} catch (IOException e) {
e.printStackTrace();
}
}

// here the magic is done
public interface PrintLibrary extends Library {
// load the C library
PrintLibrary INSTANCE = (PrintLibrary) Native.loadLibrary("imageioprint",
PrintLibrary.class);

// define prototypes
void printimageArray( double[] imgdata, int rows, int cols );
void printimagePtr( DoubleBuffer imgdata, int rows, int cols );
}

public static void main( String[] args ) {

String infile = "/home/moovida/grass/grassdb/flangitest/prova/cell/flowy";
new PrintImageInC(infile);
}

}


Step three: write some simple C code to print out the contents (here my testfile imageioprint.c)


#include stdio.h


void printimagePtr(double *imgdata, int rows, int cols)
{
int i = 0;
int j = 0;
printf ("Printing pointer in C\n");
printf ("--------------------------------------------\n");
for (i = 0; i rows; i++)
{
for (j = 0; j cols; j++)
{
printf ("%f ", *imgdata);
imgdata++;
}
printf("\n");
}
printf("\n");
}

void printimageArray(double imgdata[], int rows, int cols)
{
int i = 0;
int j = 0;
printf ("Printing array in C\n");
printf ("--------------------------------------------\n");
for (i = 0; i rows; i++)
{
for (j = 0; j cols; j++)
{
printf ("%f ", imgdata[ i*cols + j ]);
}
printf("\n");
}
printf("\n");
}


Step four: compile you C code to a shared library

gcc -shared -o libimageioprint.so imageioprint.c


Step five: supply the needed LD_LIBRARY_PATH to the java environment (i.e. the folder inside which the lib is located) and execute!




PS: JNA dynamically sets types, so it is less performant than JNI. So if you have to call a method millions of times, JNI will be the cross you have to carry, but in my case, with one single heavy raster to pass, the performance is no matter at all.

Sunday, May 11, 2008

How to learn a new groovy friend Italian accents (and not only)

While updating my webpage-pieces-merger to be groovy, I noticed some odd problem on Italian accents (same would be for German umlaute and so on).


So what is going on with UTF-8?? Why is the encoding wrong, when my java encoding is always right?

When I have a look at the System.properties, I notice that the encoding is:
file.encoding=MacRoman

Alright, so I just have to change that one?
try 1) groovy -c UTF-8 myScript -> NOT WORKING

try 2) first set the encoding in the java options on the console:
export JAVA_OPTS=-Dfile.encoding=UTF-8
after that I get a nice file.encoding=UTF-8

and the text is ok:

so what about setting the property inside the script?

try 3) inside the myScript set System.setProperty("file.encoding","UTF-8")
Nice, I get file.encoding=UTF-8 but for some reason the accents are messed up again.


I really wonder why try 3 is not working, probably setting this inside the process is too late? Wrong JVM? Hmmm... really no idea about it. Try 2 is working ok, but I don't like the fact that I have to set a system variable before. I hope to find a solution soon.

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."

Thursday, February 7, 2008

How to properly log errors, so that a user can send them to you

One of the biggest problems we had in the past in JGrass, was the fact that the users usually send an email to the list telling that JGrass "doesn't work". No other explenation, no way for us to understand. Since JGrass at that time was really buggy, it all got a mantainance nightmare.

The new JGrass has a solution to all this given by the Eclipse/Udig framework, which is the possibility to log the errors and let the user send the log to some poor guy that then has to understand what is happening. This is much better for us than the "doesn't work".

Obviously a developer will have to include some small snippet into his code to get the logging enabled. Here it is:

Step 1: insert the following code snippet into your Plugin Activator class



public static void log( String message2, Throwable t ) {
if (getDefault() == null) {
t.printStackTrace();
return;
}
String message = message2;
if (message == null)
message = ""; //$NON-NLS-1$
int status = t instanceof Exception || message != null ? IStatus.ERROR : IStatus.WARNING;
getDefault().getLog().log(new Status(status, PLUGIN_ID, IStatus.OK, message, t));
}


Step 2: call the logging method from every needed part of your code, i.e. everywhere an exception is thrown and you would like to know it.
For example in the JGrass catalog plugin that would look like:



try {
// ... your code
} catch (Exception e) {
JGrassPlugin.log("JGrassPlugin problem:eu.hydrologis.udig.catalog.internal.jgrass
#JGrassMapGeoResource#getIdentifier", e);
e.printStackTrace();
}


this would be an exception thrown inside the
class: JGrassMapGeoResource
package: eu.hydrologis.udig.catalog.internal.jgrass
method: getIdentifier

You do not think I wrote that by myself, right?
The Eclipse template engine helps you in this. If you want it the same as above, just add the following template to the java->editor->templates list (all in one line):


${pluginActivator}.log("${pluginActivator} problem:
${enclosing_package}#${enclosing_type}#
${enclosing_method}", ${throwable}); //$$NON-NLS-1$$



This will automagically insert the method-class-package, you just have to supply the plugin activator class name and the name of the exception.


If everything is done well then the user should be able to go under submit the log


And send us the content of everything that has been logged.



Please use that thing :)

Wednesday, February 6, 2008

How to easily add menus & actions to jface/swt viewers - adding a table row

Assume you created a nice new empty table to be filled by a user.
And let's say you created a first empty row with two columns.


List elements = new ArrayList();

TableViewer tViewer = new TableViewer(parent, SWT.MULTI);
table = tViewer.getTable();
table.setLayoutData(new GridData(GridData.FILL_BOTH));
table.setHeaderVisible(true);
table.setLinesVisible(true);
TableLayout layout = new TableLayout();
layout.addColumnData(new ColumnWeightData(50, true));
layout.addColumnData(new ColumnWeightData(50, true));
table.setLayout(layout);
TableColumn dateColumn = new TableColumn(table, SWT.LEFT);
dateColumn.setText("DATE");
TableColumn dischargeColumn = new TableColumn(table, SWT.LEFT);
dischargeColumn.setText("DISCHARGE");
TableLabelProvider sLP = new TableLabelProvider(); // implement this yourself
tViewer.setLabelProvider(sLP);
tViewer.setContentProvider(new ArrayContentProvider());
// the empty row
elements.add(new String[]{"", ""});
// set the input
tViewer.setInput(elements);


what to do very quickly to add a new row?
After fiddling a bit around with listeners and such stuff, I decided to exploit the menus:


// add a popup for new rows
MenuManager popManager = new MenuManager();
IAction menuAction = new NewRowAction();
popManager.add(menuAction);
Menu menu = popManager.createContextMenu(table);
table.setMenu(menu);


And how easy is the creation of the action?
Like that:


private class NewRowAction extends Action {
public NewRowAction() {
super("Add a new row");
}
public void run() {
elements.add(new String[]{"", ""});
tViewer .refresh();
}
}


et voila', right-click on the table and tell it to add the new row.

Thursday, January 31, 2008

How to create a temporary layer with proper CRS with new FeatureType

In the code examples of the udig sdk you can find a nice snippet explaining how to create a temporary resource layer. One of those nice things which a user can decide whether to keep and save to shapefile or just trash it.

Let's see the code:


List featuresList = this is my list subset of a leyer or whatever
String geomType = "MultiPolygon";
Object[][] ob = new Object[featuresList.size()][2];
for( int i = 0; i < featuresList.size(); i++ ) {

ob[i][0] = featuresList.get(i).getGeometry();
ob[i][1] = i;
}
FeatureType featureType = null;
String typeName = "Macrobacini";
try {
featureType = DataUtilities.createType(typeName, "geom:" + geomType
+ ",indice:java.lang.Integer");
featureType = DataUtilities.createSubType(featureType, null, ApplicationGIS.getActiveMap().getViewportModel().getCRS());
} catch (SchemaException e1) {
e1.printStackTrace();
}

JGrassCatalogUtilities.removeMemoryServiceByTypeName(typeName);

IGeoResource resource = CatalogPlugin.getDefault().getLocalCatalog()
.createTemporaryResource(featureType);
FeatureCollection coll = FeatureUtilities.createFeatures(featureType, ob);
try {
resource.resolve(FeatureStore.class, pm).addFeatures(coll);
} catch (IOException e) {
e.printStackTrace();
}
ApplicationGIS.addLayersToMap(ApplicationGIS.getActiveMap(), Collections
.singletonList(resource), -1);



I added the createsubtype part because beeing the featuretype created from scratch, the CRS was missing and it interpreted my coordinates as lat/long, and therefore out of the admitted degrees.

The removeMemoryService part is needed because no two memorydatastores of the same name are admitted in the same catalog service, and a nice:
java.lang.UnsupportedOperationException: Schema modification not supported
is thrown whenever you force it to try nevertheless.

Therefore the service has to be removed before going on, which is done like that:


public static synchronized void removeMemoryServiceByTypeName( String typeName ) {
MemoryServiceImpl service = null;
try {
List< ? extends IResolve> members = CatalogPlugin.getDefault().getLocalCatalog()
.members(new NullProgressMonitor());
for( IResolve resolve : members ) {
if (resolve instanceof MemoryServiceImpl) {
if (URLUtils.urlEquals(resolve.getIdentifier(), MemoryServiceExtensionImpl.URL,
true)) {
service = (MemoryServiceImpl) resolve;
break;
}
}
}
MemoryDataStore ds = service.resolve(MemoryDataStore.class, new NullProgressMonitor());
if (Arrays.asList(ds.getTypeNames()).contains(typeName)) {
CatalogPlugin.getDefault().getLocalCatalog().remove(service);
}
} catch (IOException e) {
CatalogPlugin.log("Error finding services", e); //$NON-NLS-1$
}
}






Friday, January 25, 2008

How to threat threads right when in the wrong thread

Not sure if it is only me, but sometimes you are in a display thread and you need to stop to do others gui things like open a dialog and ask the user something, but there is confusion about wrong thread access exceptions and if not that, then perhaps, the thing doesn't stop were it should and... and... if you feel like that, like panic, just sit down again, take a deep break and look what good guys can show you (not me, but the eclipse guys :))


goGo = false;

// create a thread and inside do a syncExec
Thread thread = new Thread(){
public void run() {
Display.getDefault().syncExec(new Runnable(){
public void run() {
SomeDialog someDialog = new SomeDialog();
someDialog.open(Display.getDefault().getActiveShell());
goGo = true;
}
});
}
};
thread.start();

// wait for the dialog to finish
while( !goGo ) {
try {
Thread.sleep(300);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}


and then finally do whatever you have to do with the asked result.

Wednesday, January 23, 2008

How to really create a ShapeFile

I have been struggling around for a very long while and went through many different methods of creating a shapefile. Thanks to Jody Garnett and the geotools team this has now come to a (temporary) end. Adapted from some mailinglist thread and this page:


// Create the DataStoreFactory
FileDataStoreFactorySpi factory = new IndexedShapefileDataStoreFactory();

// Create the file you want to write to
File file = null;
if (pathToFile.toLowerCase().endsWith(".shp")) {
file = new File(pathToFile);
} else {
file = new File(pathToFile + ".shp");
}
// Create a Map object used by our DataStore Factory
// NOTE: file.toURI().toURL() is used because file.toURL() is deprecated
Map map = Collections.singletonMap("shapefile url", file.toURI().toURL());

// Create the ShapefileDataStore from our factory based on our Map object
ShapefileDataStore myData = (ShapefileDataStore) factory.createNewDataStore(map);

// Tell this shapefile what type of data it will store
// Shapefile handle only : MultiPoint, MultiLineString, MultiPolygon
FeatureType featureType = DataUtilities.createType("some name", fieldsSpec);
// where fieldsSpec is a string description of the contents of the shapefile,
// somthing like "geom:MultiPoint,name:String,id:Integer,description:String


// Create the Shapefile (empty at this point)
myData.createSchema(featureType);

// Tell the DataStore what type of Coordinate Reference System (CRS) to use
myData.forceSchemaCRS(crs);

FeatureCollection collection = FeatureCollections.newCollection();

Object[][] attr = attributes;
// where attributes is an array of arrays (sometimes called matrix,
// but it is easier to understand in this context :)) of the attributes
// of the future shapefile, also containing in the first array the geometries
try {
for( int i = 0; i < record =" new" j =" 0;" featurename =" myData.getTypeNames()[0];" transaction =" null;" store =" null;" transaction =" new" store =" (FeatureStore)">

How to deal with projections (i.e. Coordinate Reference Systems in geotools)

Note that this is a brute copy and paste from the geotools wiki.


- get the CRS from an epsg code:

import org.geotools.referencing.CRS;
CoordinateReferenceSystem sourceCRS = CRS.decode("EPSG:4326");


- create a CRS from well known text (WKT):


String wkt = "GEOGCS[" + "\"WGS 84\"," + " DATUM[" + " \"WGS_1984\","
+ " SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],"
+ " TOWGS84[0,0,0,0,0,0,0]," + " AUTHORITY[\"EPSG\",\"6326\"]],"
+ " PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
+ " UNIT[\"DMSH\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],"
+ " AXIS[\"Lat\",NORTH]," + " AXIS[\"Long\",EAST],"
+ " AUTHORITY[\"EPSG\",\"4326\"]]";
CoordinateReferenceSystem crs = CRS.parseWKT(wkt);


- search for the official epsg code from a WKT:


String name = "ED50";
String wkt =
"GEOGCS[\"" + name + "\",\n" +
" DATUM[\"European Datum 1950\",\n" +
" SPHEROID[\"International 1924\", 6378388.0, 297.0]],\n" +
"PRIMEM[\"Greenwich\", 0.0],\n" +
"UNIT[\"degree\", 0.017453292519943295]]";
CoordianteReferenceSystem example = CRS.parseWKT(wkt);

String code = CRS.lookupIdentifier( example, true ); // should be "EPSG:4230"
CoordinateReferenceSystem crs = CRS.decode( code );


- reproject geometries:


CoordinateReferenceSystem sourceCRS = CRS.decode("EPSG:4326");
CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:23032");
MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS);
// jts geometry
Geometry targetGeometry = JTS.transform( sourceGeometry, transform);
// iso geometry
Geometry target = geometry.transform( targetCRS );

Sunday, January 20, 2008

How to check the operating system type and architecture

even if java is superportable (:]), more than often we need to execute operating dependent stuff. The rcp environment helps a lot in this:


if (Platform.getOS().equals(Platform.OS_WIN32)) {
// do windows specific stuff
}else if (Platform.getOS().equals(Platform.OS_LINUX)) {
// do linux specific stuff
}else if (Platform.getOS().equals(Platform.OS_MACOSX)) {
// do macosx specific stuff
}else{
// throw exceptions
}

Tuesday, January 15, 2008

How to use nice windows to select maps, layers, etc in JGrass

In JGrass with the need to often select raster and vector maps from within windows, dialogs and so on, and because of the need to have particular informations about Locations and Mapset given to the executed algoritms, we decided to create a set of widgets to do so.

They are really easy to call and give back the list of the selected resource:


JGRasterChooserDialog tree = new JGRasterChooserDialog();
tree.open(elevationGroup.getShell(), SWT.SINGLE);
JGrassMapGeoResource selected = tree.getSelectedResources().get(0);
String layerName =selected.getTypeNames()[0];



Which looks like the following two images, depending on whether you want to pick up the resource from the loaded layers in the project or also from the resources loaded in the catalog:

Here I loaded I GRASS raster map,


and here all the maps in the locations that is loaded in the catalog are seen.



The same applies for the featuresources:


FeatureChooserDialog tree = new FeatureChooserDialog();
tree.open(reachShapeGroup.getShell(), SWT.SINGLE);
DataStore selected = tree.getSelectedResources().get(0);
String layerName =selected.getTypeNames()[0];




and catalog.




Fairly easy, isn't it? :)

Sunday, January 13, 2008

How to rasterize a polygon in JGrass - the quick and dirty scan line algorithm

After trying for a while to rasterize polygons by quering every point of the raster matrix to be inside of the given polygon, Martin Davis pointed me out to the scan line algorythm.

To make a quick and dirty implementation of it with the support of the JTS suite was fairly easy in JGrass:


GeometryFactory gFactory = new GeometryFactory();
int rows = active.getRows();
int cols = active.getCols();

for( int i = 0; i < rows; i++ ) {
for( int j = 0; j < cols; j++ ) {
if (rasterToMap != null) {
raster.setValueAt(i, j, JGrassConstans.defaultNovalue);
} else {
raster.setValueAt(i, j, 0.0);
}
}
// do scan line to fill the polygon
LineString line = gFactory.createLineString(new Coordinate[]{
rowColToCenterCoordinates(active, i, 0),
rowColToCenterCoordinates(active, i, cols - 1),});
if (polygon.intersects(line)) {
Geometry internalLines = polygon.intersection(line);
Coordinate[] coords = internalLines.getCoordinates();
for( int j = 0; j < coords.length; j = j + 2 ) {

int[] startcol = coordinateToNearestRowCol(active, coords[j]);
int[] endcol = coordinateToNearestRowCol(active, coords[j + 1]);

if (startcol == null || endcol == null) {
// vertex is outside of the region, ignore it
continue;
}
/*
* the part in between has to be filled
*/
for( int k = startcol[0]; k <= endcol[0]; k++ ) {
if (rasterToMap != null) {
raster.setValueAt(i, k, rasterToMap.getValueAt(i, k));
} else {
raster.setValueAt(i, k, 1.0);
}
}
}
}
}


Where raster and rasterToMap are two RasterData objects that simply wrap a double[][] matrix. Raster is an empty matrix to be filled, whereas rasterToMap is a map that can be used to "cut" out the data inside the polygon area to be put in the matrix to be filled.

As I said, quick and dirty :)

Friday, January 11, 2008

How to create quick and nice charts in JGrass

Now JFreechart is a beautiful charting library.
Now that they have SWT support built in, it is really the best open sourced out there.
Visit them and buy a nice manual, they deserve it :) www.jfree.org

Lately we have built in some facilities in JGrass to do some nice and straightforward charting. Here we go with a standalone example that links to the JGrass eu.hydrologis.jgrass.charting plugin:


public static void main( String[] args ) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new GridLayout());

NumericChartData numericChartData = new NumericChartData(2);

/*
* first tab
*/
NumericChartDataItem tab1 = numericChartData.getChartDataItem(0);
/*
* title to be taken in the case of composed charts. In that case it is ambiguos which title
* of which chart should be taken
*/
tab1.bigTitle = "Title of tab 1";
/*
* extra string that will be taken to give the tab a name, the title could be non suitable
*/
tab1.chartStringExtra = "Text of tab 1";
/*
* in tab 1: first chart with 2 series
*/
// the title for this chart
tab1.chartTitles.add("Chart title 1 in tab 1");
// x label
tab1.chartXLabels.add("X data");
// y label
tab1.chartYLabels.add("Y data");
// define some data
double[][][] data11 = new double[][][]{{{-1, -2, -3}, {2, 4, 6}}, {{1, 2, 3}, {2, 4, 6}}};
tab1.chartSeriesData.add(data11);
// give the series for this chart a name
tab1.seriesName.add(new String[]{"series 1", "series 2"});
/*
* in tab 1: second chart with 1 serie
*/
tab1.seriesName.add(new String[]{"series 1"});
double[][][] data12 = new double[][][]{{{1, 2, 3}, {2, 4, 6}}};
tab1.chartTitles.add("Chart title 2 in tab 1");
tab1.chartXLabels.add("X data");
tab1.chartYLabels.add("Y data");
tab1.chartSeriesData.add(data12);

/*
* second tab
*/
NumericChartDataItem tab2 = numericChartData.getChartDataItem(1);
tab2.bigTitle = "Title of tab 2";
tab2.chartStringExtra = "Text of tab 2";
/*
* in tab 2: one single chart with 3 series
*/
tab2.chartTitles.add("Chart title 1 in tab 2");
tab2.chartXLabels.add("X data");
tab2.chartYLabels.add("Y data");
double[][][] data2 = new double[][][]{{{-1, -2, -3}, {2, 4, 6}}, {{1, 2, 3}, {2, 4, 6}},
{{1, 2, 3}, {-2, -4, -6}}};
tab2.chartSeriesData.add(data2);
tab2.seriesName.add(new String[]{"series 1", "series 2", "series 3"});

/*
* create the chart using a
*/
ChartCreator creator = new MultiXYTimeChartCreator();
// tweak some stuff
/* create all the charts in the list for every tab? Yes. */
creator.M_HINT_CREATE_CHART = new boolean[][]{{true, true}, {true, false}};
/* create the checkboxes to hide and unhide the series? First no, second yes */
creator.M_HINT_CREATE_TOGGLEHIDESERIES = new boolean[][]{{false, false}, {true, false}};
/* define the types of chart to create */
creator.M_HINT_CHART_TYPE = new int[][]{
{ChartCreator.XYBARCHART, ChartCreator.XYLINECHART}, {ChartCreator.XYLINECHART, -1}};
/* define the vertical orientation of the chart */
creator.M_HINT_CHARTORIENTATION_UP = new boolean[][]{{false, true}, {true, true}};
/*
* define the colors of the series, if = null, colors are taken automatically
*/
creator.M_HINT_CHARTSERIESCOLOR = new Color[2][2][3];
// tab 1, chart 1, all series
creator.M_HINT_CHARTSERIESCOLOR[0][0] = new Color[]{Color.blue, Color.red, null};
// tab 1, chart 2, all series
creator.M_HINT_CHARTSERIESCOLOR[0][1] = new Color[]{Color.green, null, null};
// tab 2, chart 1, all series
creator.M_HINT_CHARTSERIESCOLOR[1][0] = new Color[]{Color.blue, Color.red, Color.yellow};

/*
* finally create that plot
*/
creator.makePlot(shell, numericChartData);

shell.pack();
shell.open();
while( !shell.isDisposed() ) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}


And this is how the snippet looks in the real world:



How to open a view from an action and maximize it programmatically

You want to open an rcp view to be maximized over the open parteditors in order to gain user's focus on that? That is how to do it:


IWorkbenchPage activePage = window.getActivePage();
if (activePage.findView(SimulationViewer.ID) == null) {
try {
// open the view
activePage.showView(SimulationViewer.ID);
// and maximize it
activePage.toggleZoom(activePage.findViewReference(SimulationViewer.ID));
} catch (PartInitException es) {
es.printStackTrace();
}
}


The view will be maximized if it wasn't and vice versa.

Have to find out how to deal with the fact that when I close the view the other views of the group stay maximized, whereas the user will want to get back to its part editors. Any idea?

Monday, December 17, 2007

How to get the numbers out of a GRASS raster map in JGrass


// Get the current active region from the mapset path
Window originalActiveRegion = Window.getActiveWindowFromMapset(mapsetPath);

// adapt the region to a poligonal geometry extracted from a feature,
// let's say basinBounds
Window readRegion = Window.adaptActiveRegionToEnvelope(basinsBounds,
originalActiveRegion);

// shrink the active region to the needed part in order to make the scripts work
// without overhead
Window.writeActiveWindowToMapset(mapsetPath, readRegion);

// create the reader
JGrassRasterMapReader jgReader = new JGrassRasterMapReader(readRegion,
elevationMapName, mapsetName, locationPath, monitor);

// read the data
RasterData rasterData = null;
if (jgReader.open() && jgReader.hasMoreData()) {
rasterData = jgReader.getNextData();
}

// and rasterdata is just a wrapper around a matrix based dataset,
// so just loop over it with two nested for loop in rows and cols.
// this was done just to make migration to geotools coverage
// easier at some point

Friday, December 14, 2007

Hot to get mad with the paths in rcp environments

How do I get to a folder with a relative path to my installation? If this and many other questions about what is where came to your mind, read on:


// 1
// get the file path from resources in a bundle
try {
URL fileURL = FileLocator.find(Platform.getBundle("eu.hydrologis.jgrass.hydrocare"),
new Path("/icons/extract.png"), null);
fileURL = FileLocator.toFileURL(fileURL);
String fileUrlPath = fileURL.getPath();
System.out.println(fileUrlPath);
} catch (Exception e1) {
e1.printStackTrace();
}
// get paths in eclipse
// 2
URL pluginInternalURL = Platform.getInstanceLocation().getURL();
System.out.println(pluginInternalURL.getPath());
// 3
pluginInternalURL = Platform.getInstallLocation().getURL();
System.out.println(pluginInternalURL.getPath());
// 4
pluginInternalURL = Platform.getConfigurationLocation().getURL();
System.out.println(pluginInternalURL.getPath());
// 5
pluginInternalURL = Platform.getUserLocation().getURL();
System.out.println(pluginInternalURL.getPath());
// 6
try {
pluginInternalURL = Platform.getLocation().toFile().toURL();
} catch (MalformedURLException e1) {
e1.printStackTrace();
} catch (IllegalStateException e1) {
e1.printStackTrace();
}
System.out.println(pluginInternalURL.getPath());


/*
* in the case I launch it from inside eclipse the following appears
*/
// 1
// the path is correctly transformed in a local path
/Users/moovida/rcpdevelopment/WORKSPACES/jgrassudig33workspace/eu.hydrologis.jgrass.hydrocare/icons/extract.png
// 2
// instancelocation seems to be the workspace folder
/Users/moovida/rcpdevelopment/WORKSPACES/runtime-New_configuration/
// 3
// the install location is the path to the folder inside which I keep the target platform
/Users/moovida/rcpdevelopment/jgrass-udig-sdk-delta/
// 4
// configuration location is inside the metadata folder inside the eclipse
// ide workspace location I defined for my development
/Users/moovida/rcpdevelopment/WORKSPACES/jgrassudig33workspace/.metadata/.plugins/org.eclipse.pde.core/jgrassudig/
// 5
// the userlocation is a strange path that doesn't even exist
/Users/moovida/user/
// 6
// the platform location is the same as the workspace folder
/Users/moovida/rcpdevelopment/WORKSPACES/runtime-New_configuration/


/*
* in the case I launch it from an installed JGrass version
* (install folder is /Applications/jgrass3.0alpha/)
*/
// 1
// the path is correctly transformed in a local path
/Applications/jgrass3.0alpha/plugins/eu.hydrologis.jgrass.hydrocare_1.0.0/icons/extract.png
// 2
// instancelocation seems to be the workspace folder
/Applications/jgrass3.0alpha/jgrass.app/Contents/MacOS/workspace/
// 3
// the install location is infact the path inside which the application resides
/Applications/jgrass3.0alpha/
// 4
// configuration location is the configuration folder inside the installation
/Applications/jgrass3.0alpha/configuration/
// 5
// again the userlocation is a strange path that doesn't even exist
/Users/moovida/user/
// 6
// and again the platform location is the same as the workspace folder
/Applications/jgrass3.0alpha/jgrass.app/Contents/MacOS/workspace/