2014-10-30 58 views
7

任何人都可以推薦一個Java組件,它可以讓你創建一個漂亮的世界地圖圖像,突出顯示某些國家(基於某些統計數據)。類似的東西這個形象:java的地理圖表

World map with highlighted countries

一些類似於谷歌地球圖(但爲Java):https://developers.google.com/chart/interactive/docs/gallery/geochart 但運行在服務器端,沒有連接到互聯網。理想情況下,我想重視幾個可以按比例突出顯示的國家。

無論是開源或商業(只要它不是可笑的價格)。

+0

你可以使用d3js和Javas ScriptEngine。 – user 2014-11-10 18:01:49

+0

你正在尋找什麼樣的輸出格式?你是否期望它是靜態的(即一個光柵圖像),還是需要動態/需要的能力,像鼠標懸停等腳本的東西? – 2014-11-11 02:10:28

+0

@SteveSiebert只是一個靜態的,包含在.pdf報告中,將被打印出來。所以大多數圖像格式應該足夠了 – AtliB 2014-11-11 11:46:42

回答

4

我無法找到一個Java庫,用來做什麼你要找的人,所以我看着服用SVG和修改其風格,並將其轉換爲圖像。我首先想到的是這個任務:Apache Batik。

我選擇使用wikimedia上的開源svg地圖(類似於Gregor Opheys的答案),因爲它不會讓你感到不關心授權,它也已經爲簡單的CSS修改做好了準備。國家。 (請參閱SVG註釋以獲取指示信息)。有一個問題,維基媒體上的一些圖像是由a python script生成的,它將CSS樣式也放入元素中。如果你想使用這些SVG文件之一,你必須爲此處理。

令我驚喜的是,有almost a perfect example on the batik wiki,只需要一些調整。我能夠通過一個小的語法修改(他們的outfile文件被命名爲.jpg,但使用png代碼轉換器)和一個小的更改來從項目資源而不是磁盤加載svg,從而生成修改後的映像(png)。下面是示例代碼與我小的改動:

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(); 
     } 
    } 
} 

正如我提到的,這種工作方式是將其轉換成你需要的光柵圖像之前添加CSS到SVG。祕密醬是SVGStyleElement,在這裏你可以看到美國變綠了。對於任何其他國家,您只需使用雙字母有向圖,然後選擇您想要填充的顏色。當然,因爲它是CSS,所以你也可以做更改邊框顏色或使用背景圖片而不是顏色的東西,所以你在這裏有很大的靈活性。

我也有依賴玩了一下,所以讓你過去這一關,我會包括我的Maven依賴:

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

如果你使用一個不同的構建經理,讓我知道,我可以轉儲傳遞依賴樹。

1

那麼,你可以做一個GWT應用程序。 GWT(Google Web ToolKit)是一個基於Java的平臺應用程序。它提供了與您在Google Charts JavaScript API中看到的相同類型的可視化API,但是在java代碼中。查看此鏈接以獲得基於Java的Chart API和Java代碼http://gwt-charts.appspot.com/#geochart中的地理圖的實例。希望這有助於:)

+0

非常接近,但似乎對Google服務器有依賴性,因此無法離線運行: https://developers.google.com/chart/interactive/faq?csw= 1#離線 – AtliB 2014-10-30 21:48:10

2

看看GeoTools,它可能有你所需要的。

+0

該項目嚴重缺少網頁上的一些截圖/示例 – AtliB 2014-11-11 11:45:17

+0

有,http://docs.geotools.org/latest/userguide/tutorial/。 – jmn 2014-11-12 05:52:32

0

我想對你更好:

gvSIG是對於具有友好的用戶界面,能夠 訪問最常用的格式,矢量和柵格的人知道。它 提供各種各樣的工具與地理類 信息(查詢工具,佈局創作,地理處理,網絡, 等)

源工作:gvSIG Wikipedia

+2

它似乎沒有API ...似乎是一個桌面應用程序。我需要一個可以創建圖片格式的地圖並將其包含在報告中的組件。 – AtliB 2014-11-06 13:14:14

1

作爲開始,你可以輕鬆將一個組件或者說像自己的函數使用SVG世界地圖 (例如this)。 將國名的java.util.Map保留爲SVG源中給出的路徑元素。 您的函數將採用一組國家名稱,並基本上以字符串形式返回地圖的svg。如果路徑的ID與組中的一個相匹配,您可以使用所需的顏色添加填充屬性...

這是代碼。您需要下載this並將其作爲world.svg放在同一個文件夾中。然後傳入您想要突出顯示的國家作爲參數。該程序將編寫一個文件out.svg,您可以在瀏覽器中打開(我使用Firefox)。我希望它能做到這一點。我只嘗試從eclipse運行它...

package com.examples.firstbundle; 


import java.io.FileNotFoundException; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.OutputStreamWriter; 
import java.io.PrintStream; 
import java.nio.charset.Charset; 
import java.nio.charset.StandardCharsets; 
import java.nio.file.FileSystems; 
import java.nio.file.Files; 
import java.text.MessageFormat; 
import java.util.List; 
import java.util.Map; 
import java.util.Set; 
import java.util.TreeMap; 
import java.util.TreeSet; 

public class SVGMap { 
    private static String prefix = ""; 
    private static String postfix = ""; 
    private static Map<String, String> worldMap = new TreeMap<String, String>(); 

    private final static String pathFormat = "<path id=\"{0}\" d=\"{1}\" fill=\"{2}\" />"; 
    static { 

     try { 
      List<String> lines = Files.readAllLines(FileSystems.getDefault().getPath("world.svg"), StandardCharsets.UTF_8); 

      boolean pathstarted = false; 
      for(String line : lines) { 
       if(isPath(line)) { 
        pathstarted = true; 
        worldMap.put(getCountry(line), getPath(line)); 
       } else { 
        if(!pathstarted) { 
         prefix += line; 
        } 
        else { 
         postfix += line; 
        } 

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

    } 

    /** 
    * @param args 
    * @throws IOException 
    */ 
    public static void main(String[] args) throws IOException { 
     Set<String> countries = new TreeSet<String>(); 
     for(String country : args) { 
      countries.add(country); 
     } 
     FileOutputStream fileOutputStream = new FileOutputStream("out.svg"); 
     OutputStreamWriter out = new OutputStreamWriter(fileOutputStream, StandardCharsets.UTF_8); 

     out.append(prefix); 
     for(String country : worldMap.keySet()) { 
      out.append(MessageFormat.format(pathFormat, country, worldMap.get(country), countries.contains(country) ? "tomato" : "grey")); 
     } 
     out.append(postfix); 
     out.flush(); 
     out.close(); 
    } 

    private static String getPath(String line) { 
     String part = line.split("=")[2]; 
     String path = part.split("\"")[1]; 
     return path; 
    } 

    private static String getCountry(String line) { 
     String part = line.split("=")[1]; 
     String country = part.split("\"")[1]; 
     return country; 
    } 

    private static boolean isPath(String line) { 
     return line.startsWith("<path"); 
    } 

} 
+0

最大的問題是定義路徑/座標的手動操作......並使它看起來很漂亮。但這正是我所傾向的。我本來預計這項工作已經在某個地方完成了! – AtliB 2014-11-11 11:42:00

+1

@AtliB:只需抓取我鏈接到的SVG的路徑/座標。時間允許,我會添加一些代碼,這樣做... – 2014-11-11 14:17:44

+0

我沒有意識到.svg只是一個XML文檔,在這種情況下與所有的國家定義...這很酷 – AtliB 2014-11-12 08:00:01