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$
}
}






Saturday, January 26, 2008

Docs: the JGrass Raster Legend

Finally it is here and it is much nicer to deal with than in the old JGrass... as more or less everything or better... as everything :)

What I'm talking about? The JGrass Raster Legend!!!

So if you look into the Mapgraphics, now there is a new entry, called JGrass Raster Legend.

Drag it on the map and it will ask you to choose a map for which to draw the legend. Chose one of your layers and press ok. The legend is smart and will check if your raster map has categories.

So this is how it looks like:

1) for a raster map without categories, like the well known elevation model in the spearfish dataset



2) for a raster map with categories, like the geology map in the same dataset



As you can see some properties can already be set to make the legend look better.

There is also a tool to drag the legend for a walk and define that way its position.

Enjoy!

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.

How to keep a decent government in Italy

There is no solution for this!
In Italy politics is just a game driven and destroyed by brainless people...

Thursday, January 24, 2008

How to solve the rcp-mozilla-browser exception problem in linux

As most of you probably know, the Eclipse-rcp environment uses native GUI resources, which steals to java the whole portability fun. The nice thing about it is that SWT looks 1000000 times better than swing (in the author's opinion).

One problem that could occur, is that some of the underlying resource is not found from time to time, as for example in the case of Linux OS and the swt browser widget.

The error thrown in that case is something like:
Exception in thread "Thread-3" org.eclipse.swt.SWTError: No more
handles [Unknown Mozilla path (MOZILLA_FIVE_HOME not set)]
at org.eclipse.swt.SWT.error(SWT.java:3589)
at org.eclipse.swt.browser.Mozilla.create(Mozilla.java:292)
at org.eclipse.swt.browser.Browser.(Browser.java:109)
at...

It seems that the environmental variable MOZILLA_FIVE_HOME has not been set, so let's try to set it.
In my case I add to my .bashrc the following line:

export MOZILLA_FIVE_HOME=/usr/lib/mozilla-firefox/

and magic is done! (please take care that the folder is the right one. For example on my newest linux it is: /usr/lib/firefox/)

If the magic at that point should not be done, then probably you need to run:

sudo apt-get install xul-runner





Wednesday, January 23, 2008

How to join the JGrass development

There is an effort ongoing to create a n uptodate documentation on the JGrass wiki at the following page:

http://jgrass.wiki.dev.fsc.bz.it/jgrass/Setup_Development_Environment


Please give feedback on the manual, we are working on it and need to know where the problems are.


Note that the howto was kept on one single site in order to be able to export the tutorial as a pdf manual.




WARNING
THE PART BELOW THIS LINE IS KEPT ONLY FOR THE RECORD, IT IS NO MORE UPTODATE:

To develop on JGrass you need 2 things to start:

1) the udig development environment + some additional plugins needed by JGrass

There is a very nice quickguide from the udig community here to get started.
We supply the target platform needed to develop. This is the udig sdk plus some more plugins added that are needed by JGrass. These are from the eclipse framework, mainly due to the fact that we have a console and this one needs some editor addons.

You can get this all here just choose the latest. The content of this has to be set as target platform in your eclipse plugin development environment.

2) the whole infinity of JGrass plugins :)

At the moment there is no official release, therefore they have to be checked out from the official SVN repository, i.e. take all you can get from here. or simply do from commandline:

svn checkout https://svn.dev.cocos.bz//svnroot/jgrass/jgrass3.0/trunk



SOME NOTES:
  • trunc - here you find the base plugins, these should always work. Blame me if they don't!
  • community/moovida - this is my playground, so don't be too scared. Not all of the plugins are ready to rumble, but some of them will soon go trunc. What does NOT work or is NOT needed (i.e. DON'T blame me if they do not work):
    • eu.hydrologis.jgrass.advancedstyletest - is just a test for SLD stuff
    • eu.hydrologis.jgrass.mapgraphicdragger - drag legends and so for a walk
    • eu.hydrologis.jgrass.chart - has been a try for a view with BIRT charting
    • eu.hydrologis.jgrass.dwgdxf.datastores - this will come whenever there will be founds for it
    • eu.hydrologis.jgrass.feature - this is useless, for now
    • eu.hydrologis.jgrass.java2c2fortran.and.back - an example for bridging java-C-fortran in udig. Wrapped in an operation.
    • eu.hydrologis.jgrass.renderer.sites - renderer for old GRASS sites, not maintained for now
    • eu.hydrologis.jgrass.tools
    • eu.hydrologis.jgrass.ui
    • eu.hydrologis.jgrass.visad.libs - needs java3d
    • eu.hydrologis.jgrass.visad.view - needs java3d

last updated 2008_03_26

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 deal with the svn Checksum mismatch

I have this bad habbit to forget that I work in an svn environment and sometimes remove-copy-edit-trash-and-whatever files and folders by hand.
In an eclipse plugin environment (and not only) this can lead to great problems because of the lost sync between the local and remote repositories.
The result is something like:

commit -m "" /home/moovida/rcpdevelopment/WORKSPACES/jgrassudig33workspace/eu.hydrologis.jgrass.charting.jfreechart.libs/META-INF/MANIFEST.MF
Sending /home/moovida/rcpdevelopment/WORKSPACES/jgrassudig33workspace/eu.hydrologis.jgrass.charting.jfreechart.libs/META-INF/MANIFEST.MF
Transmitting file data ...
svn: Commit failed (details follow):
svn: Commit failed (details follow):
svn: Checksum mismatch for '/home/moovida/rcpdevelopment/WORKSPACES/jgrassudig33workspace/eu.hydrologis.jgrass.charting.jfreechart.libs/META-INF/MANIFEST.MF'; expected: 'e59ba98152ab43ed640cdf076b0cdc51', actual: '0fad1171d44dba8be24547671ef6b510'


Well, after trying to replace the local file with the remote one a couple of times:

revert -N /home/moovida/rcpdevelopment/WORKSPACES/jgrassudig33workspace/eu.hydrologis.jgrass.charting.jfreechart.libs/META-INF/MANIFEST.MF
Reverted /home/moovida/rcpdevelopment/WORKSPACES/jgrassudig33workspace/eu.hydrologis.jgrass.charting.jfreechart.libs/META-INF/MANIFEST.MF


The problem wasn't fixed yet.
The real problem is in those nice hidden svn-something files in every folder. They keep track of everything that happens.

So the deal is to remove the folder containing the corrupted file and update again.
At that point do your changes and commit. It will do so.

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?

Thursday, January 10, 2008

How to supply network to a linux box through a macosx through ethernet

This is for sure no GIS and no java, but it can be usefull to others, so here I go with it.

The problem: supply internet connection to a colleagues computer running linux from my macosx box. I have a broadband connection given me trough an USB-modem.

This was pretty easy, at some point I'll have to get that also throught airport connection, not sure about the security issues tho.

ON MY MAC:
1) the first thing to do is to activate leopard's (my current macosx operating system) internet connection sharing. This is easy. Go under preferences->sharing and just activate it. Select in the combobox the active internet connection and from the list below the way you want to share the connection with the linux box.

In my case I have an ethernet crossed cable for connections between two pcs, so I choose Ethernet.

2) check out the connection properties. In the network settings I see that my modem is connected with the IP address: 10.98.0.76


BTW I need to know the gateway of that all, i.e. the connection address to the outer world. This can be achieved through the supplied connection software of my modem, though I'm sure there are a lot of other ways. There are also google widgets to trace the connection.


As you can see the connection goes to 10.64.64.64. Now I have to set up properly the ethernet connection, since the connection will not supply dhcp and we need to be all on the same network.


So i simply set up the ethernet connection with a static address of one more than the modem ip address and the proper netmask. The router is the link to the outer world and that's all.

No wait, we need the DNS, which we can retrieve in the advanced settings of the modem connection, in my case:
62.13.171.4
62.13.171.5

Alright, now I'm ready to jump to my linux box:

ON MY LINUX BOX (KUBUNTU FEISTY):

1) Edit the network settings file: /etc/network/interfaces
and put the following into it:

auto lo
iface lo inet loopback

mapping hotplug
script grep

iface eth0 inet static
address 10.98.0.78
netmask 255.255.0.0
gateway 10.98.0.77


2) Edit the DNS file: /etc/resolv.conf

nameserver 62.13.171.4
nameserver 62.13.171.5


And start your network:
sudo ifup eth0


This should work at that point! Enjoy!




UPDATE: I right notice that the static IP thing could be misleading as I put it.

Instead of 10.98.0.78 on your linux box and 10.98.0.77 on your macosx system you could use more usual numbers for the internal networks, as for example 192.168.1.123 and 192.168.1.124.