Clinker, a software development ecosystem

Recently I discovered Clinker, a so called Software Development Ecosystem.

Mysteriously there is no definition for Development Ecosystem on Wikipedia yet but a close definition could be:

»
Author's profile picture acanimal

Crop image on the client side with JCrop and HTML5 canvas element

Suppose you are working on a nice web application where the user can upload images to, for example, a shop catalogue (mmm... that makes me think on something :p ) but wait... you don't the catalogue uses the whole image you upload instead a piece of it. So, we need to crop the image.

»
Author's profile picture acanimal

A word about LESS

Woow!!! That is the word that best defines Less.

From time to time someone creates something really useful with a touch of magic. John Resig creates jQuery, not the only one neither the first one, but it is the most famous JavaScript library.

»
Author's profile picture acanimal

Customizing jQuery UI Dialog: hiding close button and changing opacity

Sometimes when you are programming small things are the hard things, little details becomes difficult to solve and you need to spend lot of time to solve them. This is logically :) because you spent the major part of your time thinking and designing the big or complex things, leaving in a second plane the small things and because this they became the new "big" things. Ok, stop talking with buggy sentences and talk about this post. Recently I was working in a web page using jQuery UI dialogs that have a couple of special requirements that takes me some time and because this I want to share here with you:

»
Author's profile picture acanimal

Local storage: Storing sticky notes on your machine with HTML5

Every history has a beginning and for this post it starts when some time ago I saw this good post from tutorialzine.com. Really I love the tutorials at tutorialzine.com, they are great quality examples to learn.

Looking for similar things, it seems I was not the first trying to do something similar :) http://rqru.com/sticky. but what I wanted too was to learn a bit more about HTML5 features, concretely about local storage.

»
Author's profile picture acanimal

A geek joke

It makes me smile:

A man walks into a Doctor’s and says “Doctor, I think I’m addicted to Twitter.”

Doctor looks at him and says “Sorry, I don’t follow you.”

@opencontent - David Wiley

»
Author's profile picture acanimal

Generating map tiles without a map server. GeoTools the GIS swissknife.

Recently I was playing with latest version of GeoServer. It includes the GeoWebCache, something which can improve your server performance greatly. GeoServer solves and helps lots of problems to work and visualize geospatial data but as you know map servers lakes from scalability.

Because this GeoWebCache is a great tool. Basically, each map request is processed and the result image is stored to be directly returned on subsequent requests. The processed images are stored as a pyramid of tiles depending on the bounding box or zoom level of the request.

Another way to solve scalability problems is directly pre-generate the pyramid of tiles, something that makes Google, Bing, Yahoo, OpenStreetMaps, etc.

What I need?

I have a lightnings database with thousands of lightnings for a period of some months. I need to show lightnings in my maps but only those corresponding to a period or interval of time. For example, render the lightnings from 00:00h to 00:30h and allow the user to go forward or backward in time.

One important thing is I only need an "image" with the information in that period. I don't need to render each lightning as a feature in the map -this will degrade the performance rendering in a storm with thousands of lightnings per period.

The problem

So why don't use GeoServer+GeoWebCache for this? I can configure a layer pointing to my lightning database, make requests and rest assured subsequent call will get the previously created map.

The problem is at this moment -while I write this post- GeoServer lakes from TIME support in requests. That means if I define a layer from my lightnings table on DB, every GeoServer request will work against all data while I only need a subset of my data -determined by an interval- to be rendered in the requested maps.

Adopted solution

Ok, be quiet. GeoServer is build on top of GeoTools, an open source Java library which provides standards compliant methods for the manipulation of geospatial data and, more important, GeoTools library implements Open Geospatial Consortium (OGC) specifications as they are developed.

With all this the solution seems easy: code a program to query the desired period of lightning data and generate a pyramid of tiles (for the desired levels).

 A brief description of the implementation

Next is a brief summary of things to do, or take into account, to generate your own pyramid of tiles programmatically with Geotools.

All the lightnings information is stored on a PostgreSQL/PostGIS table called 'lightnings'. Data related with a lightning are: date (the UTC instant in which the lightning occurs, represented as a long number in Unix time), position (latitude/longitude/altitude), value and sign (the electric charge).

Set the DataSource connection

GeoTools tries to simplify thing and because this it tries to abstracts as much as possible. Features can be provided from many source: files (shapefiles, GML, ...) or a database (PostgreSQL/PostGIS, Oracle, ...).

The first step then is to set a DataSource instance pointing to our database:

Map<String, Object> params = new HashMap<String, Object>();
params.put(PostgisNGDataStoreFactory.DBTYPE.key, dbconn.getType());
params.put(PostgisNGDataStoreFactory.HOST.key, dbconn.getHost());
params.put(PostgisNGDataStoreFactory.PORT.key, dbconn.getPort());
params.put(PostgisNGDataStoreFactory.SCHEMA.key, "public");
params.put(PostgisNGDataStoreFactory.DATABASE.key, dbconn.getDatabase());
params.put(PostgisNGDataStoreFactory.USER.key, dbconn.getUser());
params.put(PostgisNGDataStoreFactory.PASSWD.key, dbconn.getPassword());
params.put(PostgisNGDataStoreFactory.EXPOSE_PK.key, true);

// Get lightning store
DataStore dataStore = DataStoreFinder.getDataStore(params);
SimpleFeatureSource sfs = dataStore.getFeatureSource("lightnings");

Note: 'dbconn' is an object which stores my DB connection parameters.

Filtering data

We don't want to get all the lightnings in the database but only those withing a period of tim, so what we need is to filter the data using Filter classes. Given a period of time represented by values 'long start_date' and 'long end_date' we can define the desired filter as:

FilterFactory2 filterFactory = CommonFactoryFinder.getFilterFactory2(null);
// Create filter for specified initial and end dates
Filter filterStart = filterFactory.greaterOrEqual(filterFactory.property("date"), filterFactory.literal(start_date));
Filter filterEnd = filterFactory.less(filterFactory.property("date"), filterFactory.literal(end_date));
Filter filterTime = filterFactory.and(filterStart, filterEnd);

Create styles before rendering for features

There are some ways to create styles for our features. One is to use a SLD document and the other is doing programmatically.

In my case, I chose to use the second form so here is a bit of cumbersome code (I ommited the try/catch section) which create the desired style to identify positive and negative lightnings.

// Create style
StyleBuilder styleBuilder = new StyleBuilder();
StyleFactory styleFactory = styleBuilder.getStyleFactory();
FilterFactory2 filterFactory = styleBuilder.getFilterFactory();

// Style for Positivos
Graphic grP = styleFactory.createDefaultGraphic();
Mark markP = styleFactory.getCircleMark();
markP.setStroke(styleFactory.createStroke(filterFactory.literal(Color.BLUE), filterFactory.literal(1)));
markP.setFill(styleFactory.createFill(filterFactory.literal(Color.CYAN)));
grP.graphicalSymbols().clear();
grP.graphicalSymbols().add(markP);
grP.setSize(filterFactory.literal(5));

// Style for Negativos
Graphic grN = styleFactory.createDefaultGraphic();
Mark markN = styleFactory.getCircleMark();
markN.setStroke(styleFactory.createStroke(filterFactory.literal(Color.RED), filterFactory.literal(1)));
markN.setFill(styleFactory.createFill(filterFactory.literal(Color.ORANGE)));
grN.graphicalSymbols().clear();
grN.graphicalSymbols().add(markN);
grN.setSize(filterFactory.literal(5));

Filter filterPositiveValor = ff.and(filter, CQL.toFilter("value >= 0"));
Filter filterNegativeValor = ff.and(filter, CQL.toFilter("value < 0"));

// Create symbols and rules to render every feature
PointSymbolizer symPositivos = styleFactory.createPointSymbolizer(grP, null);
PointSymbolizer symNegativos = styleFactory.createPointSymbolizer(grN, null);

Rule ruleP = styleFactory.createRule();
ruleP.symbolizers().add(symPositivos);
ruleP.setFilter(filterPositiveValor);
FeatureTypeStyle ftsP = styleFactory.createFeatureTypeStyle(new Rule[]{ruleP});

Rule ruleN = styleFactory.createRule();
ruleN.symbolizers().add(symNegativos);
ruleN.setFilter(filterNegativeValor);
FeatureTypeStyle ftsN = styleFactory.createFeatureTypeStyle(new Rule[]{ruleN});

// Finally create out style
Style style = styleFactory.createStyle();
style.featureTypeStyles().add(ftsP);
style.featureTypeStyles().add(ftsN);

Create the map and render to a file

The map creating is straightforward:

MapContext map = new DefaultMapContext();
CoordinateReferenceSystem crs = CRS.decode("EPSG:3785");
map.setCoordinateReferenceSystem(crs);

and then render it to a file. I will paste here the code on the GTRenderer tutorial. You can play a bit with the code and change some values: area of interest, size of the output image, etc.

public void saveImage(final MapContent map, final String file, final int imageWidth) {

    GTRenderer renderer = new StreamingRenderer();
    renderer.setMapContent(map);

    Rectangle imageBounds = null;
    ReferencedEnvelope mapBounds = null;
    try {
        mapBounds = map.getMaxBounds();
        double heightToWidth = mapBounds.getSpan(1) / mapBounds.getSpan(0);
        imageBounds = new Rectangle(
                0, 0, imageWidth, (int) Math.round(imageWidth * heightToWidth));

    } catch (Exception e) {
        // failed to access map layers
        throw new RuntimeException(e);
    }

    BufferedImage image = new BufferedImage(imageBounds.width, imageBounds.height, BufferedImage.TYPE_INT_RGB);

    Graphics2D gr = image.createGraphics();
    gr.setPaint(Color.WHITE);
    gr.fill(imageBounds);

    try {
        renderer.paint(gr, imageBounds, mapBounds);
        File fileToSave = new File(file);
        ImageIO.write(image, "jpeg", fileToSave);

    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

Conclusions

Every tool is designed and built with a goal in mind, because this normally the use of a map server is always the proper selection. But sometimes you have specific needs that general tools doesn't solve and here is when open source project like GeoTools can help you.

I would to note that programing this way the image generation is much more faster than use of GeoServer because we are avoiding lots of intermediate steps a map server does: get request, parse, check and validate parameters and once query is executed, image composed it must be returned to the client via HTTP protocol.

References

http://docs.geotools.org

http://docs.geotools.org/latest/userguide/library/render/gtrenderer.html

http://docs.geotools.org/stable/tutorials/filter/query.html

 

»
Author's profile picture acanimal

A heatmaps layer for OpenLayers

As I commented on a previous post I know about heatmaps.js project some time ago and interest me a lot by its possibilities with OpenLayers. Of course it is not the only solution, see here and here but it has a great performance.

»
Author's profile picture acanimal

Heatmaps in OpenLayers

Some month ago I found nice post on how to create heatmaps using HTML5 canvas element. Its author, Patrick Wied, created a nice JavaScript code responsible to create a heatmap over an image depending on the mouse click or mouse move events.

»
Author's profile picture acanimal

XCode4 and the mystery with Route-Me code sense

If you follow this blog you will know I prefer to write "long" posts with nice content :) form time to time than write lots of short posts without much substance. For that I use Twitter :p

But always there an exception... and here is mine.

»
Author's profile picture acanimal