Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Geo chart for java

Tags:

java

graph

maps

geo

Can anyone recommend a Java component that lets you create a pretty looking image of a world map, highlighting certain countries (based on some statistics). Something similar to this image:

World map with highlighted countries

Something similar to Google Geo Charts (but for Java): https://developers.google.com/chart/interactive/docs/gallery/geochart but runs on the server side, without an internet connection. Ideally I'd like to attach weight to a few countries which would highlight them proportionally.

Either open source or commercial (as long as it's not something ridiculously priced).

like image 414
AtliB Avatar asked Oct 30 '14 17:10

AtliB


3 Answers

I couldn't find a java library to do what you were looking for, so I looked into taking an SVG and modifying its style, and converting it to an image. First thought that came to my mind for this task: Apache Batik.

I chose to use the open source svg maps available on wikimedia (similar to Gregor Opheys answer) because not does it make it so you're not concerned about licensing, it also is already ready-made for simple CSS modifications by country. (See the SVG comments for instructions). There is one hitch, some of those image on wikimedia were generated by a python script, which puts the CSS styling also in the elements. If you want to use one of these SVG files, you'll have to handle for that.

To my happy surprise, there was almost a perfect example on the batik wiki, needing just a couple tweaks. I was able to produce a modified image (png) with one small syntax change (their outfile file is named .jpg but its using a png transcoder) and a small change to load the svg from the project resources instead of disk. Below is the example code with my minor changes:

public class MapMaker {

    private final static String MAP_FLAT = "BlankMap-World6-Equirectangular.svg";
    private final static String MAP_ROUND = "WorldMap.svg";

    public static void main(String... args) {

        try {
            // make a Document with the base map 
            String parser = XMLResourceDescriptor.getXMLParserClassName();
            SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser);
            Document doc = f.createDocument("http://example.com/stuff",
                    MapMaker.class.getClassLoader().getResourceAsStream(MAP_ROUND));

            // prepare to modify and transcode the document
            SVGDocument sdoc = (SVGDocument) doc;
            Element svgRoot = sdoc.getDocumentElement();
            PNGTranscoder t = new PNGTranscoder();
            TranscoderInput input = new TranscoderInput(doc);

            // find the existing stylesheet in the document
            NodeList stylesList = doc.getElementsByTagName("style");
            Node styleNode = stylesList.item(0);

            // append another stylesheet after the existing one
            SVGStyleElement style = (SVGStyleElement) doc.createElementNS(SVG_NAMESPACE_URI, "style");
            style.setAttributeNS(null, "type", "text/css");
            style.appendChild(doc.createCDATASection(".us {fill: blue;}"));
            styleNode.getParentNode().appendChild(style);

            // transcode the map
            OutputStream ostream = new FileOutputStream("outblue.jpg");
            TranscoderOutput output = new TranscoderOutput(ostream);
            t.transcode(input, output);
            ostream.close();

            // replace the appended stylesheet with another
            SVGStyleElement oldStyle = style;
            style = (SVGStyleElement) doc.createElementNS(SVG_NAMESPACE_URI, "style");
            style.setAttributeNS(null, "type", "text/css");
            style.appendChild(doc.createCDATASection(".us {fill: green;}"));
            styleNode.getParentNode().replaceChild(style, oldStyle);

            // transcode the revised map
            File outFile = new File(System.getProperty("java.io.tmpdir"), "outgreen.png");
            ostream = new FileOutputStream(outFile);
            output = new TranscoderOutput(ostream);
            t.transcode(input, output);
            ostream.close();
            System.out.println("Out File: " + outFile);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

As I mentioned, the way this works is by adding CSS to the SVG prior to converting it to the raster image you need. The secret sauce is the SVGStyleElement, here you can see that the United States is turned green. For any other country, you would simply use their 2-letter digraph and chose the color you would like to fill. Of course, since it's CSS, you can also do things like change the border colors or use a background image instead of a color, so you have a lot of flexibility there.

I did have to play with the dependencies a bit, so to get you past this hurdle, I'll include my maven dependencies:

<dependencies>
    <dependency>
        <groupId>org.apache.xmlgraphics</groupId>
        <artifactId>batik-parser</artifactId>
        <version>1.7</version>
    </dependency>
    <dependency>
        <groupId>org.apache.xmlgraphics</groupId>
        <artifactId>batik-css</artifactId>
        <version>1.7</version>
    </dependency>
    <dependency>
        <groupId>org.apache.xmlgraphics</groupId>
        <artifactId>batik-svg-dom</artifactId>
        <version>1.7</version>
    </dependency>
    <dependency>
        <groupId>org.apache.xmlgraphics</groupId>
        <artifactId>batik-transcoder</artifactId>
        <version>1.7</version>
    </dependency>
    <dependency>
        <groupId>org.apache.xmlgraphics</groupId>
        <artifactId>batik-rasterizer</artifactId>
        <version>1.7</version>
    </dependency>
    <dependency>
        <groupId>org.apache.xmlgraphics</groupId>
        <artifactId>batik-codec</artifactId>
        <version>1.7</version>
    </dependency>
</dependencies>

If you use a different build manager, let me know, and I can dump the transitive dependency tree.

like image 184
Steve Siebert Avatar answered Oct 22 '22 00:10

Steve Siebert


Have a look at GeoTools, it may have what you need.

like image 26
jmn Avatar answered Oct 22 '22 01:10

jmn


Haven't used it and not sure if it is appropriate for your needs but you can have a look at Nasa WorldWind. It features an SDK and it seems like that there is an off-line mode available.

Check :

  • http://worldwind.arc.nasa.gov/java/ and http://goworldwind.org/demos/

  • http://goworldwind.org/examples/#network (for offline mode)

like image 43
zlinks Avatar answered Oct 21 '22 23:10

zlinks