World Wind Java开发之八——加载本地缓存文件构建大范围三维场景(

http://blog.csdn.net/giser_whu/article/details/42044599

上一篇博客主要是针对小文件直接导入WW中显示,然而当文件特别大时,这种方式就不太可行。因此要将大文件切片,生成本地缓存,WW可以加载本地缓存文件,保障浏览场景时的流畅性。

1、使用Global Mapper生成WW缓存切片

使用Global Mapper生成WW缓存切片的步骤已上传至使用GlobalMapper生成WW缓存切片,这里不再赘述。生成后的切片可以放在任意文件夹下,目前参考了WWJ自带的例子InstallImageryAndElevationsDemo,暂时将数据放在C:ProgramDataWorldWindInstalled目录下,如下图所示。
生成的XML文件修改如下:

2、参照InstallImageryAndElevationsDemo示例实现缓存文件的初始化加载

未多做修改,写了一个加载缓存数据的类LoadCacheData,代码如下所示。
[java] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * @Copyright 2014-2020 @奔跑的鸡丝 
  3.  **/  
  4.   
  5. package edu.whu.vge.util;  
  6.   
  7. import edu.whu.vge.util.JavaCheckBoxTree.CheckBoxTreeNode;  
  8. import gov.nasa.worldwind.Factory;  
  9. import gov.nasa.worldwind.WorldWind;  
  10. import gov.nasa.worldwind.avlist.AVKey;  
  11. import gov.nasa.worldwind.avlist.AVList;  
  12. import gov.nasa.worldwind.avlist.AVListImpl;  
  13. import gov.nasa.worldwind.awt.WorldWindowGLCanvas;  
  14. import gov.nasa.worldwind.cache.FileStore;  
  15. import gov.nasa.worldwind.exception.WWRuntimeException;  
  16. import gov.nasa.worldwind.geom.Sector;  
  17. import gov.nasa.worldwind.globes.Earth;  
  18. import gov.nasa.worldwind.globes.ElevationModel;  
  19. import gov.nasa.worldwind.layers.Layer;  
  20. import gov.nasa.worldwind.terrain.CompoundElevationModel;  
  21. import gov.nasa.worldwind.util.DataConfigurationFilter;  
  22. import gov.nasa.worldwind.util.DataConfigurationUtils;  
  23. import gov.nasa.worldwind.util.Logging;  
  24. import gov.nasa.worldwind.util.WWIO;  
  25. import gov.nasa.worldwind.util.WWXML;  
  26.   
  27. import java.awt.Component;  
  28. import java.io.File;  
  29.   
  30. import javax.swing.JTree;  
  31. import javax.swing.SwingUtilities;  
  32. import javax.swing.tree.DefaultMutableTreeNode;  
  33. import javax.xml.xpath.XPath;  
  34.   
  35. import org.w3c.dom.Document;  
  36. import org.w3c.dom.Element;  
  37.   
  38. /** 
  39.  * @项目名称:GF_ZHJCYPG 
  40.  * @类名称:LoadCacheData 
  41.  * @类描述: 加载缓存数据 
  42.  * @创建人:奔跑的鸡丝 
  43.  * @创建时间:2014-12-19 下午4:30:49 
  44.  * @修改备注: 
  45.  * @版本: 
  46.  */  
  47.   
  48. public class LoadCacheData  
  49. {  
  50.     private static WorldWindowGLCanvas worldWindowGLCanvas;  
  51.     private static JTree layerJTree;  
  52.   
  53.     /** 
  54.      *  
  55.      * 创建一个新的实例 LoadCacheData. 
  56.      *  
  57.      * @param worWindowGLCanvas 
  58.      */  
  59.     public LoadCacheData(WorldWindowGLCanvas worWindowGLCanvas, JTree jTree)  
  60.     {  
  61.         LoadCacheData.setWorldWindowGLCanvas(worWindowGLCanvas);  
  62.         LoadCacheData.setLayerJTree(jTree);  
  63.     }  
  64.   
  65.     /** 
  66.      *  
  67.      * @方法名称: loadPreviouslyInstalledData ; 
  68.      * @方法描述: 加载已有的缓存文件 ; 
  69.      * @参数 : 
  70.      * @返回类型: void ; 
  71.      * @创建人:奔跑的鸡丝 ; 
  72.      * @创建时间:2014-12-19 下午7:06:09; 
  73.      * @throws 
  74.      */  
  75.     public void loadPreviouslyInstalledData()  
  76.     {  
  77.   
  78.         Thread thread = new Thread(new Runnable()  
  79.         {  
  80.   
  81.             @Override  
  82.             public void run()  
  83.             {  
  84.                 // TODO Auto-generated method stub  
  85.                 loadInstalledDataFromFileStore(WorldWind.getDataFileStore());  
  86.             }  
  87.         });  
  88.         thread.start();  
  89.     }  
  90.   
  91.     /** 
  92.      *  
  93.      * @方法名称: loadInstalledDataFromFileStore ; 
  94.      * @方法描述: TODO ; 
  95.      * @参数 :@param fileStore 
  96.      * @返回类型: void ; 
  97.      * @创建人:奔跑的鸡丝 ; 
  98.      * @创建时间:2014-12-19 下午7:06:42; 
  99.      * @throws 
  100.      */  
  101.     protected void loadInstalledDataFromFileStore(FileStore fileStore)  
  102.     {  
  103.         // 便利已有的缓存文件  
  104.         for (File file : fileStore.getLocations())  
  105.         {  
  106.             // 文件存在并且是缓存文件目录  
  107.             if (file.exists() && fileStore.isInstallLocation(file.getPath()))  
  108.             {  
  109.                 System.out.println(file.getPath());  
  110.                 loadInstalledDataFromDirectory(file);  
  111.             }  
  112.   
  113.         }  
  114.     }  
  115.   
  116.     /** 
  117.      *  
  118.      * @方法名称: loadInstalledDataFromDirectory ; 
  119.      * @方法描述: 从文件目录加载缓存数据 ; 
  120.      * @参数 :@param dir 
  121.      * @返回类型: void ; 
  122.      * @创建人:奔跑的鸡丝 ; 
  123.      * @创建时间:2014-12-19 下午7:43:36; 
  124.      * @throws 
  125.      */  
  126.     private void loadInstalledDataFromDirectory(File dir)  
  127.     {  
  128.         /** 
  129.          * 获取缓存文件xml配置文件的在缓存文件目录的相对目录,如LandsatLandsat.xml 
  130.          */  
  131.         String[] names = WWIO.listDescendantFilenames(dir,  
  132.                 new DataConfigurationFilter(), false);  
  133.         if (names == null || names.length == 0)  
  134.             return;  
  135.   
  136.         for (String filename : names)  
  137.         {  
  138.             Document doc = null;  
  139.   
  140.             try  
  141.             {  
  142.                 // 根据缓存文件XML描述文件创建Document对象  
  143.                 File dataConfigFile = new File(dir, filename);  
  144.                 doc = WWXML.openDocument(dataConfigFile);  
  145.                 doc = DataConfigurationUtils.convertToStandardDataConfigDocument(doc);  
  146.             }  
  147.             catch (WWRuntimeException e)  
  148.             {  
  149.                 e.printStackTrace();  
  150.             }  
  151.   
  152.             if (doc == null)  
  153.                 continue;  
  154.   
  155.             // 由于数据配置文件来自于已有的文件,因此不能保证它是由当前版本WW's Installer  
  156.             // 产生的。可能是由之前版本或其他应用程序产生的,因此要为可能缺失的参数设置备用值(这些参数需要用来构建图层或高程模拟)  
  157.             AVList params = new AVListImpl();  
  158.             setFallbackParams(doc, filename, params);  
  159.             // 添加数据  
  160.             addInstalledData(doc, params);  
  161.         }  
  162.     }  
  163.   
  164.     /** 
  165.      *  
  166.      * @方法名称: setFallbackParams ; 
  167.      * @方法描述: 设置备用参数值 ; 
  168.      * @参数 :@param dataConfig :数据配置XML文件 
  169.      * @参数 :@param filename :文件名 
  170.      * @参数 :@param params :参数列表 
  171.      * @返回类型: void ; 
  172.      * @创建人:奔跑的鸡丝 ; 
  173.      * @创建时间:2014-12-20 下午12:21:03; 
  174.      * @throws 
  175.      */  
  176.     private void setFallbackParams(Document dataConfig, String filename,  
  177.             AVList params)  
  178.     {  
  179.         XPath xpath = WWXML.makeXPath();  
  180.         Element domElement = dataConfig.getDocumentElement();  
  181.   
  182.         // If the data configuration document doesn't define a cache name, then  
  183.         // compute one using the file's path  
  184.         // relative to its file cache directory.  
  185.         String s = WWXML.getText(domElement, "DataCacheName", xpath);  
  186.         if (s == null || s.length() == 0)  
  187.             DataConfigurationUtils.getDataConfigCacheName(filename, params);  
  188.   
  189.         // If the data configuration document doesn't define the data's extreme  
  190.         // elevations, provide default values using  
  191.         // the minimum and maximum elevations of Earth.  
  192.         String type = DataConfigurationUtils.getDataConfigType(domElement);  
  193.         if (type.equalsIgnoreCase("ElevationModel"))  
  194.         {  
  195.             if (WWXML.getDouble(domElement, "ExtremeElevations/@min", xpath) == null)  
  196.                 params.setValue(AVKey.ELEVATION_MIN, Earth.ELEVATION_MIN);  
  197.             if (WWXML.getDouble(domElement, "ExtremeElevations/@max", xpath) == null)  
  198.                 params.setValue(AVKey.ELEVATION_MAX, Earth.ELEVATION_MAX);  
  199.         }  
  200.     }  
  201.   
  202.     /** 
  203.      *  
  204.      * @方法名称: addInstalledData ; 
  205.      * @方法描述: 添加缓存数据 ; 
  206.      * @参数 :@param dataConfig 
  207.      * @参数 :@param params 
  208.      * @返回类型: void ; 
  209.      * @创建人:奔跑的鸡丝 ; 
  210.      * @创建时间:2014-12-20 下午12:22:29; 
  211.      * @throws 
  212.      */  
  213.     private void addInstalledData(final Document dataConfig, final AVList params)  
  214.     {  
  215.         if (!SwingUtilities.isEventDispatchThread())  
  216.         {  
  217.             SwingUtilities.invokeLater(new Runnable()  
  218.             {  
  219.                 public void run()  
  220.                 {  
  221.                     addInstalledData(dataConfig, params);  
  222.                 }  
  223.             });  
  224.         }  
  225.         else  
  226.         {  
  227.             addInstalledCacheData(dataConfig.getDocumentElement(), params);  
  228.         }  
  229.   
  230.     }  
  231.   
  232.     /** 
  233.      *  
  234.      * @方法名称: addInstalledCacheData ; 
  235.      * @方法描述: 添加已有缓存数据 ; 
  236.      * @参数 :@param domElement :数据XML描述文件 
  237.      * @参数 :@param params :参数列表 
  238.      * @返回类型: void ; 
  239.      * @创建人:奔跑的鸡丝 ; 
  240.      * @创建时间:2014-12-19 下午8:02:21; 
  241.      * @throws 
  242.      */  
  243.     public void addInstalledCacheData(final Element domElement,  
  244.             final AVList params)  
  245.     {  
  246.         if (domElement == null)  
  247.         {  
  248.             String message = Logging.getMessage("nullValue.DocumentIsNull");  
  249.             Logging.logger().severe(message);  
  250.             throw new IllegalArgumentException(message);  
  251.         }  
  252.   
  253.         String description = getDescription(domElement); // 图层名称  
  254.         Sector sector = getSector(domElement); // 图层范围  
  255.         System.out.println(description);  
  256.         System.out.println(sector);  
  257.         addToWorldWindow(domElement, params);  
  258.   
  259.     }  
  260.   
  261.     /** 
  262.      *  
  263.      * @方法名称: addToWorldWindow ; 
  264.      * @方法描述: 将缓存文件加入WW ; 
  265.      * @参数 :@param domElement 
  266.      * @参数 :@param params 
  267.      * @返回类型: void ; 
  268.      * @创建人:奔跑的鸡丝 ; 
  269.      * @创建时间:2014-12-19 下午4:44:08; 
  270.      * @throws 
  271.      */  
  272.     private void addToWorldWindow(Element domElement, AVList params)  
  273.     {  
  274.         String type = DataConfigurationUtils.getDataConfigType(domElement);  
  275.         if (type == null)  
  276.             return;  
  277.   
  278.         if (type.equalsIgnoreCase("Layer"))  
  279.         {  
  280.             addLayerToWorldWindow(domElement, params);  
  281.         }  
  282.         else if (type.equalsIgnoreCase("ElevationModel"))  
  283.         {  
  284.             addElevationModelToWorldWindow(domElement, params);  
  285.         }  
  286.     }  
  287.   
  288.     /** 
  289.      *  
  290.      * @方法名称: addLayerToWorldWindow ; 
  291.      * @方法描述: 向WW中添加图层 ; 
  292.      * @参数 :@param domElement 
  293.      * @参数 :@param params 
  294.      * @返回类型: void ; 
  295.      * @创建人:奔跑的鸡丝 ; 
  296.      * @创建时间:2014-12-19 下午4:45:06; 
  297.      * @throws 
  298.      */  
  299.     private void addLayerToWorldWindow(Element domElement, AVList params)  
  300.     {  
  301.         Layer layer = null;  
  302.         try  
  303.         {  
  304.             // Factory创建的图层默认是不可见的  
  305.             Factory factory = (Factory) WorldWind.createConfigurationComponent(AVKey.LAYER_FACTORY);  
  306.             layer = (Layer) factory.createFromConfigSource(domElement, params);  
  307.         }  
  308.         catch (Exception e)  
  309.         {  
  310.             String message = Logging.getMessage(  
  311.                     "generic.CreationFromConfigurationFailed",  
  312.                     DataConfigurationUtils.getDataConfigDisplayName(domElement));  
  313.             Logging.logger().log(java.util.logging.Level.SEVERE, message, e);  
  314.         }  
  315.   
  316.         if (layer == null)  
  317.             return;  
  318.         layer.setEnabled(true); // 设置图层可见  
  319.   
  320.         // 添加至WW  
  321.         if (!getWorldWindowGLCanvas().getModel().getLayers().contains(layer))  
  322.         {  
  323.             getWorldWindowGLCanvas().getModel().getLayers().add(layer);  
  324.             // System.out.println(pLayerTree.getModel().getRoot().toString());  
  325.             Object rootObject = layerJTree.getModel().getRoot();  
  326.             if (!layerJTree.getModel().isLeaf(rootObject))  
  327.             {  
  328.                 int count = layerJTree.getModel().getChildCount(rootObject);  
  329.                 for (int i = 0; i < count; i++)  
  330.                 {  
  331.                     String childNodeNameString = layerJTree.getModel().getChild(  
  332.                             rootObject, i).toString();  
  333.                     if (childNodeNameString.equals("影像图层"))  
  334.                     {  
  335.                         ((DefaultMutableTreeNode) layerJTree.getModel().getChild(  
  336.                                 rootObject, i)).add(new CheckBoxTreeNode(  
  337.                                 layer.getName()));  
  338.                         layerJTree.updateUI();  
  339.                     }  
  340.                 }  
  341.             }  
  342.   
  343.         }  
  344.     }  
  345.   
  346.     /** 
  347.      *  
  348.      * @方法名称: addElevationModelToWorldWindow ; 
  349.      * @方法描述: 添加高程图层 ; 
  350.      * @参数 :@param domElement 
  351.      * @参数 :@param params 
  352.      * @返回类型: void ; 
  353.      * @创建人:奔跑的鸡丝 ; 
  354.      * @创建时间:2014-12-19 下午4:51:37; 
  355.      * @throws 
  356.      */  
  357.     private void addElevationModelToWorldWindow(Element domElement,  
  358.             AVList params)  
  359.     {  
  360.         ElevationModel em = null;  
  361.         try  
  362.         {  
  363.             Factory factory = (Factory) WorldWind.createConfigurationComponent(AVKey.ELEVATION_MODEL_FACTORY);  
  364.             em = (ElevationModel) factory.createFromConfigSource(domElement,  
  365.                     params);  
  366.         }  
  367.         catch (Exception e)  
  368.         {  
  369.             String message = Logging.getMessage(  
  370.                     "generic.CreationFromConfigurationFailed",  
  371.                     DataConfigurationUtils.getDataConfigDisplayName(domElement));  
  372.             Logging.logger().log(java.util.logging.Level.SEVERE, message, e);  
  373.         }  
  374.   
  375.         if (em == null)  
  376.             return;  
  377.   
  378.         ElevationModel defaultElevationModel = getWorldWindowGLCanvas().getModel().getGlobe().getElevationModel();  
  379.         if (defaultElevationModel instanceof CompoundElevationModel)  
  380.         {  
  381.             if (!((CompoundElevationModel) defaultElevationModel).containsElevationModel(em))  
  382.                 ((CompoundElevationModel) defaultElevationModel).addElevationModel(em);  
  383.         }  
  384.         else  
  385.         {  
  386.             CompoundElevationModel cm = new CompoundElevationModel();  
  387.             cm.addElevationModel(defaultElevationModel);  
  388.             cm.addElevationModel(em);  
  389.             getWorldWindowGLCanvas().getModel().getGlobe().setElevationModel(cm);  
  390.         }  
  391.     }  
  392.   
  393.     /** 
  394.      * 获取缓存文件类型 获取缓存配置文件描述:是Layer或者是Elevation 
  395.      *  
  396.      * @方法名称: getDescription ; 
  397.      * @方法描述: TODO ; 
  398.      * @参数 :@param domElement 
  399.      * @参数 :@return 
  400.      * @返回类型: String ; 
  401.      * @创建人:奔跑的鸡丝 ; 
  402.      * @创建时间:2014-12-19 下午4:53:26; 
  403.      * @throws 
  404.      */  
  405.     private String getDescription(Element domElement)  
  406.     {  
  407.         String displayName = DataConfigurationUtils.getDataConfigDisplayName(domElement);  
  408.         String type = DataConfigurationUtils.getDataConfigType(domElement);  
  409.   
  410.         StringBuilder sb = new StringBuilder(displayName);  
  411.   
  412.         if (type.equalsIgnoreCase("Layer"))  
  413.         {  
  414.             sb.append(" (Layer)");  
  415.         }  
  416.         else if (type.equalsIgnoreCase("ElevationModel"))  
  417.         {  
  418.             sb.append(" (Elevations)");  
  419.         }  
  420.   
  421.         return sb.toString();  
  422.     }  
  423.   
  424.     /** 
  425.      * 获取图层范围 
  426.      *  
  427.      * @方法名称: getSector ; 
  428.      * @方法描述: TODO ; 
  429.      * @参数 :@param domElement 
  430.      * @参数 :@return 
  431.      * @返回类型: Sector ; 
  432.      * @创建人:奔跑的鸡丝 ; 
  433.      * @创建时间:2014-12-19 下午4:54:17; 
  434.      * @throws 
  435.      */  
  436.     protected static Sector getSector(Element domElement)  
  437.     {  
  438.         return WWXML.getSector(domElement, "Sector", null);  
  439.     }  
  440.   
  441.     public static WorldWindowGLCanvas getWorldWindowGLCanvas()  
  442.     {  
  443.         return worldWindowGLCanvas;  
  444.     }  
  445.   
  446.     public static void setWorldWindowGLCanvas(  
  447.             WorldWindowGLCanvas worldWindowGLCanvas)  
  448.     {  
  449.         LoadCacheData.worldWindowGLCanvas = worldWindowGLCanvas;  
  450.     }  
  451.   
  452.     public JTree getLayerJTree()  
  453.     {  
  454.         return layerJTree;  
  455.     }  
  456.   
  457.     public static void setLayerJTree(JTree layerJTree)  
  458.     {  
  459.         LoadCacheData.layerJTree = layerJTree;  
  460.     }  
  461.   
  462. }  

3、高程数据的加载

高程数据采用NASA的30m公开DEM数据,使用World Wind Server发布即可,详见前面的搭建本地World wind Severe服务器。最终实现效果图如下图所示。
PS:年末各种忙啊,项目总算结题,明天小组年会,预祝一切顺利!欢迎大家留言交流,共享自己的学习笔记。
World Wind Java的资料实在太少啦,断断续续总算搭建起了三维框架,后面陆续添加功能,计划做一个基于新安江模型的洪涝模拟仿真模块,将之前做的洪涝模拟和参数率定、径流模拟全部整合到自己的这个平台上来。
---------------------------------分割线(2015年1月13日)-----------------------------------
补充:关于LoadCacheData的使用,只需在程序初始化时加入以下两句代码即可:
[java] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. // 加载缓存数据  
  2.             LoadCacheData loadCacheData = new LoadCacheData(wGlCanvas,  
  3.                     layerJTree);  
  4.             loadCacheData.loadPreviouslyInstalledData();  
其中layerJTree是图层树,大家可以不要这个参数,修改相应代码即可,另外如何测试自己的切片数据是否加载成功,可以参考示例中的InstallImageryAndElevationsDemo这个。可能有些朋友不知如何运行自带的示例,下面我贴图说明下哈(假设已添加Worldwind.jar文件,并且已使用GlobalMapper切片放置C:ProgramDataWorldWindInstalled文件夹下)
如果结果如上图所示,说明数据已加载成功。如果还未成功,检查缓存数据xml配置文件是否正确,可以跟一下源代码看下是如何加载缓存数据的。
PS:另外,最近期末考试,再加上其他项目的事情,更新有些慢哈。后面会陆续更新,欢迎大家留言交流!
原文地址:https://www.cnblogs.com/telwanggs/p/6774645.html