pcl_openmap_OpenMap教程–第1部分
pcl_openmap
介紹
本系列教程將向您展示如何使用OpenMap GIS Java Swing庫構建Java應用程序。
OpenMap的開發人員指南是非常有用的文檔,描述了OpenMap的體系結構,但沒有說明如何逐步啟動和構建應用程序。 源代碼附帶的示例很有用,但還不夠。
OpenMap用Swing編寫。 在撰寫本文時,最新版本是5.1.12。 您可以從GitHub下載源代碼和可執行jar。 將其復制/解壓縮/克隆到目錄后,可以通過運行適用于您平臺的相關腳本( openmap.bat或openmap )或雙擊lib/openmap.jar來執行它。 您應該看到一個完整的GIS應用程序,如圖1所示。在本系列文章的最后,我們將嘗試構建一個類似的應用程序。 OpenMap源代碼還包含一些有關如何使用OpenMap的示例。 在本教程中,我們將基于com.bbn.openmap.app.example.SimpleMap 。 在第二個教程中,我們將使用com.bbn.openmap.app.example.SimpleMap2代碼。 以后的教程將基于其他示例。
在本系列教程中,我們將使用最新的NetBeans IDE 8.1創建我們的應用程序。
教程1 –構建基本的地圖應用程序
創建一個JFrame應用程序
在第一個教程中,我們將構建一個包含映射的基本JFrame應用程序(請參見圖2)。 通過執行以下步驟,打開NetBeans并創建一個新的Java應用程序:
圖1:OpenMap GIS應用程序窗口
基本的OpenMap Swing應用程序
我們可以將OpenMap JavaBeans添加到面板中。 要做到這一點:
3:創建一個新的Java應用程序
添加地圖
F圖4:提供項目名稱和位置
圖5:將openmap.jar添加到您的Libraries文件夾
com.bbn.openmap.MapBean組件是OpenMap工具包中的主要地圖窗口組件。 MapBean派生自java.awt.Container類。 因為它是Swing組件,所以可以像其他任何用戶界面組件一樣將其添加到Java窗口層次結構中。
為了在MapBean創建地圖,需要將Layers (com.bbn.openmap.Layer)添加到MapBean 。 圖層派生自java.awt.Component ,它們是可以添加到MapBean的唯一組件。 因為Layers是MapBean容器中包含的Components ,所以Layers在地圖上的呈現由Java組件呈現機制控制。 該機制控制分層組件如何在彼此之上繪制。 為了確保按正確的順序將每個組件繪制到窗口中, Component類包括一個方法,該方法允許其告知渲染機制它想被繪制。 此功能允許Layers彼此獨立工作,并使MapBean避免知道圖層上正在發生的事情。
清單1:基本的Swing應用程序
public class MapFrame extends javax.swing.JFrame {/** Creates new form MapFrame */public MapFrame() {super("Simple Map");initComponents();}@SuppressWarnings("unchecked")// <editor-fold defaultstate="collapsed" desc="Generated Code"> private void initComponents() {// Content suppressed} /*** @param args the command line arguments*/public static void main(String args[]) {/* Create and display the form */java.awt.EventQueue.invokeLater(new Runnable() {@Overridepublic void run() {new MapFrame().setVisible(true);}});} }清單2:添加一個MapBean
@SuppressWarnings("unchecked")// <editor-fold defaultstate="collapsed" desc="Generated Code"> private void initComponents() {mapBean = new com.bbn.openmap.MapBean();setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);getContentPane().add(mapBean, java.awt.BorderLayout.CENTER);pack();}清單3:將ShapeLayer添加到MapBean
/*** Create a ShapeLayer to show world political boundaries. Set the properties of the layer. This assumes that* the datafiles {@code dcwpo-browse.shp} and {@code dcwpo-browse.ssx} are in a path specified in the CLASSPATH variable.* These files are distributed with OpenMap and reside in the toplevel "share" subdirectory.*/ private void initMap() {Properties shapeLayerProps = new Properties();shapeLayerProps.put("prettyName", "Political Solid");shapeLayerProps.put("lineColor", "000000");shapeLayerProps.put("fillColor", "BDDE83");shapeLayerProps.put("shapeFile", "resources/map/shape/dcwpo-browse.shp");shapeLayerProps.put("spatialIndex", "resources/map/shape/dcwpo-browse.ssx");ShapeLayer shapeLayer = new ShapeLayer();shapeLayer.setProperties(shapeLayerProps);// Add the political layer to the mapmapBean.add(shapeLayer); }OpenMap應用程序中的圖層可以使用來自許多來源的數據:
- 通過計算
- 來自本地硬盤驅動器的數據文件。
- 來自URL的數據文件。
- 來自jar文件中包含的數據文件。
- 使用從數據庫(JDBC)檢索的信息。
- 使用從地圖服務器接收的信息(圖像或地圖對象)。
OpenMap應用程序配置有openmap.properties file 。 該文件的內容指定創建哪些組件并將其添加到應用程序框架(包括層)中。 只需使用文本編輯器修改o penmap.properties文件,即可配置應用程序而無需重新編譯。 只需將上述屬性文件添加進去,就可以將已經了解框架的組件添加到應用程序中。 為使用屬性編寫的組件將獲得其設置,以便正確初始化自己。 例如,依賴于數據文件或服務器位置的層通常具有允許在運行時設置這些位置的屬性。 此屬性文件通常位于應用程序文件夾中,或者位于用戶的主文件夾中。 在后一種情況下,每個用戶都可以根據自己的需求自定義應用程序。
讓我們將形狀圖層的屬性移動到屬性文件,然后從那里讀取它們。
OpenMap提供了一個特殊的類來處理屬性。 com.bbn.openmap.PropertyHandler是使用openmap.properties文件配置應用程序的組件。 可以知道在Java類路徑和應用程序用戶的主目錄中,從哪個文件讀取屬性或將其留給自己來查找openmap.properties文件。
清單5:initMap()的內容
InputStream is = null;try {is = new FileInputStream("openmap.properties");shapeLayerProps.load(is);} catch (FileNotFoundException ex) {Logger.getLogger(OpenMap.class.getName()).log(Level.SEVERE, null, ex);} catch (IOException ex) {Logger.getLogger(OpenMap.class.getName()).log(Level.SEVERE, null, ex);} finally {if (is != null) {try {is.close();} catch (IOException ex) {Logger.getLogger(OpenMap.class.getName()).log(Level.SEVERE, null, ex);}}}并發呢?
剩下的唯一障礙是我們將地圖文件加載到EDT線程中。 如果我們需要加載一個大地圖,這將延遲等待加載大地圖的應用程序的啟動。 我們需要將此任務委托給另一個線程。
有(至少)四種方法可以執行此操作:
- javax.swing.SwingWorker
- com.bbn.openmap.util.SwingWorker
- java.awt.SecondaryLoop
- java.util.concurrent.CompletableFuture
讓我們開始看看它們中的每一個。
javax.swing.SwingWorker
傳統方法是使用SwingWorker來完成臟工作(清單7)。 通用類SwingWorker提供了兩種參數化類型。 第一個參數化類型( ShapeLayer )是doInBackground()和get()方法的返回類型。 通過返回的對象doInBackground()是可訪問的get()時,后臺任務完成。 第二個參數化類型適用于定期發布的值。 當長時間運行的任務發布部分結果時,這很有用。 在這里,我們使用Void ,因為我們不發布部分結果。 doInBackground()的代碼在后臺線程中執行。 在這里,我們使用PropertyHandler讀取屬性,并創建并返回ShapeLayer 。
要啟動后臺線程,我們調用SwingWorker's execute()方法。 這樣可以安排線程執行并立即返回。 后臺任務完成后,將在EDT中調用overridden done()方法。 使用此方法可以在其中放置代碼以更新或刷新GUI. Method get() GUI. Method get()阻塞,直到后臺任務完成為止。 但是,如果您在方法done()調用get() ,則由于后臺任務已完成,因此不會發生阻塞。 在此方法中,我們將圖層添加到mapBean 。 但是,由于已經渲染了MapFrame ,因此也需要刷新它才能渲染地圖圖層。 這是通過重新驗證MapFrame來實現的。
清單7:使用javax.swing.SwingWorker的initMap()內容
private void initMap() {SwingWorker<ShapeLayer, Void> worker = new SwingWorker<ShapeLayer, Void>() {@Overridepublic ShapeLayer doInBackground() {PropertyHandler propertyHandler = null;try {propertyHandler = new PropertyHandler.Builder().setPropertiesFile("./openmap.properties").build();} catch (IOException ex) {Logger.getLogger(MapFrame.class.getName()).log(Level.SEVERE, null, ex);}ShapeLayer shapeLayer = new ShapeLayer();if (propertyHandler != null) {shapeLayer.setProperties(propertyHandler.getProperties());}return shapeLayer;}@Overrideprotected void done() {try {if (!isCancelled()) {// Add the political layer to the mapmapBean.add(get());MapFrame.this.revalidate();}} catch (InterruptedException | ExecutionException ex) {Logger.getLogger(MapFrame.class.getName()).log(Level.SEVERE, null, ex);}}};// invoke background threadworker.execute(); }com.bbn.openmap.util.SwingWorker
第二種解決方案使用OpenMap提供的SwingWorker (清單8)。 這是Java Swing的SwingWorker的簡化版本。 參數化類型( ShapeLayer )是Construct()方法的返回類型。 在這里,我們將ShapeLayer的創建重構為其自己的方法getShapeLayer() (清單9)。
清單8:使用com.bbn.openmap.util.SwingWorker的initMap()內容
private void initMap() {com.bbn.openmap.util.SwingWorker<ShapeLayer> worker = new com.bbn.openmap.util.SwingWorker<ShapeLayer>() {@Overridepublic ShapeLayer construct() {return getShapeLayer();}@Overridepublic void finished() {// Add the political layer to the mapmapBean.add(get());MapFrame.this.revalidate();}};// invoke background threadworker.execute(); }要啟動后臺線程,我們調用SwingWorker的execute()方法。 這樣可以安排線程執行并立即返回。 后臺任務完成后,將在EDT中調用重寫的finished()方法。 使用此方法可以在其中放置代碼以更新或刷新GUI。 Method get()阻塞,直到后臺任務完成為止。 但是,如果在方法finished()調用get() ,則由于后臺任務已完成,因此不會發生阻塞。 在此方法中,我們將圖層添加到mapBean 。 但是,由于已經渲染了MapFrame ,因此也需要刷新它才能渲染地圖圖層。 這是通過重新驗證MapFrame來實現的。
如果添加Thread.sleep(10_000);則可以在快速的計算機中做到這一點Thread.sleep(10_000); construct()方法中return語句之前的語句。 您應該看到應用程序的窗口沒有等待SwingWorker完成其工作才能顯示。
清單9:getShapeLayer()重構方法
private ShapeLayer getShapeLayer() {PropertyHandler propertyHandler = null;try {propertyHandler = new PropertyHandler.Builder().setPropertiesFile("./openmap.properties").build();} catch (IOException ex) {Logger.getLogger(MapFrame.class.getName()).log(Level.SEVERE, null, ex);}ShapeLayer shapeLayer = new ShapeLayer();if (propertyHandler != null) {shapeLayer.setProperties(propertyHandler.getProperties());} // try { // Thread.sleep(10_000); // } catch (InterruptedException ex) { // Logger.getLogger(MapFrame.class.getName()).log(Level.SEVERE, null, ex); // }return shapeLayer; }java.awt.SecondaryLoop
第三種解決方案使用SecondaryLoop(清單11)。 該接口提供了兩種方法enter()和exit() ,可用于啟動和停止事件循環。 即使屬性的加載和形狀層的創建是在其他線程中完成的,UI也沒有響應,并且正在等待工作線程完成之后才在屏幕上呈現。
在JavaDoc中:“當調用enter()方法時, 當前線程將被阻塞,直到循環被exit()方法終止為止。 同樣,新的事件循環在事件分發線程上啟動,該線程可能是當前線程,也可能不是當前線程。 通過調用其exit()方法,可以在任何線程上終止該循環。 […]應用此接口的典型用例是AWT和Swing模態對話框。 當模式對話框顯示在事件分配線程上時,它將進入一個新的輔助循環。 稍后,當對話框被隱藏或放置時,它退出循環,線程繼續執行?!?換句話說,它確實阻塞了當前線程,因此在所有情況下都不是SwingWorker的“替代”。 沒有像SwingWorker中那樣的done()回調方法,您可以在不阻塞當前線程的情況下調用get() 。
清單10:使用SecondaryLoop的initMap()內容
private void initMap() {final ShapeLayer shapeLayer = new ShapeLayer();final SecondaryLoop loop = Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop();Thread work = new Thread() {@Overridepublic void run() {PropertyHandler propertyHandler = null;try {propertyHandler = new PropertyHandler.Builder().setPropertiesFile("./openmap.properties").build();} catch (IOException ex) {Logger.getLogger(MapFrame.class.getName()).log(Level.SEVERE, null, ex);}if (propertyHandler != null) {shapeLayer.setProperties(propertyHandler.getProperties());}loop.exit();}};// We start the thread to do the real work work.start();// Blocks until loop.exit() is called loop.enter();// Add the political layer to the mapmapBean.add(shapeLayer); }java.util.concurrent.CompletableFuture
Java 8提供了一個新類CompletableFuture 。 CompletableFuture<T>通過提供功能性的單子運算并促進異步,事件驅動的編程模型(而不是在較早的Java中進行阻止)來擴展Future<T> 。
您需要具有JDK 8或更高版本才能使用它。 否則,請右鍵單擊OpenMap項目,然后選擇Properties 。 選擇類別庫,然后選擇Java 8 Java平臺(您可能需要通過單擊管理平臺按鈕并導航到下載和安裝JDK 8的文件夾來添加新的Java 8平臺)。 然后,選擇Sources類別,并將Source / Binary Format更改為JDK 8 。
通常,Future表示由其他線程運行的一段代碼,但它們不是異步的,即,您不能告訴它們異步執行任務并在將來的某個時間返回結果。 在這種情況下,您可以簡單地創建一個CompletableFuture,將其返回給客戶端,并且只要您認為結果可用,就可以complete() future,解鎖所有等待該將來的客戶端。 當然,像SwingWorker一樣,有一個阻塞的get()方法。
CompletableFuture提供了異步方法和非異步方法,這些方法在與前一個任務不同的另一個線程中執行其任務, 而非異步方法在與前一個任務相同的線程中執行其任務。 在異步方法中,任務被提交到fork-join線程池,完成后,結果將傳遞到下一個任務。 下一個任務完成時,其結果將進一步發送,依此類推。 它非常簡潔明了。
清單11:使用CompletableFuture的initMap()內容
private void initMap() {CompletableFuture.supplyAsync(() -> getShapeLayer()).thenAcceptAsync(shapeLayer -> {// Add the political layer to the mapmapBean.add(shapeLayer);MapFrame.this.revalidate();}); }修改后的initMap()如清單11所示。您可以通過調用supplyAsync()并傳遞Supplier ( () -> getShapeLayer() )向JDK 8中引入的全局通用ForkJoinPool.commonPool()提供新任務。 。 如果您不想使用公共線程池,還有一個重寫的supplyAsync()方法可以接受執行程序。 Supplier<R>是Java 8中引入的新接口,它不接受任何參數并返回類型R的值(在本例中為ShapeLayer )。
您可以使用thenApply()或thenApplyAsync()方法(接受Function<T, R> )來應用進一步的處理,但是在我們的示例中thenApplyAsync() 。
您可以通過使用非阻塞的 thenAccept()或thenAcceptAsync()方法異步地返回結果,該方法接受Consumer<T> 。 它們使您可以在準備就緒時消費未來的價值。 Consumer<T>與Supplier<R>;相反Supplier<R>; 它接受類型T的參數并返回void 。
看看最后一個解決方案有多優雅。
結論
我們在OpenMap的第一個教程中走了很長一段路。 我們學習了如何在NetBeans IDE(它是一個Swing JFrame創建MapFrame ,并了解了如何使用IDE將OpenMap JavaBeans添加到Palette,然后將MapBean拖到MapFrame 。 我們學習了如何向MapBean添加圖層以顯示.shp地圖文件。 通過屬性文件配置層。 我們看到了如何使用PropertyHandler讀取屬性。 我們還看到了四種從不同線程加載地圖文件的方法,即使在加載地圖文件的時間過長時也可以使MapFrame保持響應。
在下一個教程中,我們將更深入地了解有關MapHandler的OpenMap內部。
翻譯自: https://www.javacodegeeks.com/2015/10/openmap-tutorial-part-1.html
pcl_openmap
總結
以上是生活随笔為你收集整理的pcl_openmap_OpenMap教程–第1部分的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么设置来电归属地(怎样设置来电显示归属
- 下一篇: 金蝶账套怎样输入美金(金蝶账套怎样输入美