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

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

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

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

使用Global Mapper生成WW缓存切片的步骤已上传至使用GlobalMapper生成WW缓存切片,这里不再赘述。生成后的切片可以放在任意文件夹下,目前参考了WWJ自带的例子InstallImageryAndElevationsDemo,暂时将数据放在C:\ProgramData\WorldWindInstalled目录下,如下图所示。
World Wind Java开发之8——加载本地缓存文件构建大范围三维场景
生成的XML文件修改如下:
World Wind Java开发之8——加载本地缓存文件构建大范围三维场景

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

未多做修改,写了一个加载缓存数据的类LoadCacheData,代码如下所示。
/**
 * @Copyright 2014-2020 @奔跑的鸡丝
 **/

package edu.whu.vge.util;

import edu.whu.vge.util.JavaCheckBoxTree.CheckBoxTreeNode;
import gov.nasa.worldwind.Factory;
import gov.nasa.worldwind.WorldWind;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.avlist.AVList;
import gov.nasa.worldwind.avlist.AVListImpl;
import gov.nasa.worldwind.awt.WorldWindowGLCanvas;
import gov.nasa.worldwind.cache.FileStore;
import gov.nasa.worldwind.exception.WWRuntimeException;
import gov.nasa.worldwind.geom.Sector;
import gov.nasa.worldwind.globes.Earth;
import gov.nasa.worldwind.globes.ElevationModel;
import gov.nasa.worldwind.layers.Layer;
import gov.nasa.worldwind.terrain.CompoundElevationModel;
import gov.nasa.worldwind.util.DataConfigurationFilter;
import gov.nasa.worldwind.util.DataConfigurationUtils;
import gov.nasa.worldwind.util.Logging;
import gov.nasa.worldwind.util.WWIO;
import gov.nasa.worldwind.util.WWXML;

import java.awt.Component;
import java.io.File;

import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.xml.xpath.XPath;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * @项目名称:GF_ZHJCYPG
 * @类名称:LoadCacheData
 * @类描述: 加载缓存数据
 * @创建人:奔跑的鸡丝
 * @创建时间:2014-12-19 下午4:30:49
 * @修改备注:
 * @版本:
 */

public class LoadCacheData
{
	private static WorldWindowGLCanvas worldWindowGLCanvas;
	private static JTree layerJTree;

	/**
	 * 
	 * 创建一个新的实例 LoadCacheData.
	 * 
	 * @param worWindowGLCanvas
	 */
	public LoadCacheData(WorldWindowGLCanvas worWindowGLCanvas, JTree jTree)
	{
		LoadCacheData.setWorldWindowGLCanvas(worWindowGLCanvas);
		LoadCacheData.setLayerJTree(jTree);
	}

	/**
	 * 
	 * @方法名称: loadPreviouslyInstalledData ;
	 * @方法描述: 加载已有的缓存文件 ;
	 * @参数 :
	 * @返回类型: void ;
	 * @创建人:奔跑的鸡丝 ;
	 * @创建时间:2014-12-19 下午7:06:09;
	 * @throws
	 */
	public void loadPreviouslyInstalledData()
	{

		Thread thread = new Thread(new Runnable()
		{

			@Override
			public void run()
			{
				// TODO Auto-generated method stub
				loadInstalledDataFromFileStore(WorldWind.getDataFileStore());
			}
		});
		thread.start();
	}

	/**
	 * 
	 * @方法名称: loadInstalledDataFromFileStore ;
	 * @方法描述: TODO ;
	 * @参数 :@param fileStore
	 * @返回类型: void ;
	 * @创建人:奔跑的鸡丝 ;
	 * @创建时间:2014-12-19 下午7:06:42;
	 * @throws
	 */
	protected void loadInstalledDataFromFileStore(FileStore fileStore)
	{
		// 便利已有的缓存文件
		for (File file : fileStore.getLocations())
		{
			// 文件存在并且是缓存文件目录
			if (file.exists() && fileStore.isInstallLocation(file.getPath()))
			{
				System.out.println(file.getPath());
				loadInstalledDataFromDirectory(file);
			}

		}
	}

	/**
	 * 
	 * @方法名称: loadInstalledDataFromDirectory ;
	 * @方法描述: 从文件目录加载缓存数据 ;
	 * @参数 :@param dir
	 * @返回类型: void ;
	 * @创建人:奔跑的鸡丝 ;
	 * @创建时间:2014-12-19 下午7:43:36;
	 * @throws
	 */
	private void loadInstalledDataFromDirectory(File dir)
	{
		/**
		 * 获取缓存文件xml配置文件的在缓存文件目录的相对目录,如Landsat\Landsat.xml
		 */
		String[] names = WWIO.listDescendantFilenames(dir,
				new DataConfigurationFilter(), false);
		if (names == null || names.length == 0)
			return;

		for (String filename : names)
		{
			Document doc = null;

			try
			{
				// 根据缓存文件XML描述文件创建Document对象
				File dataConfigFile = new File(dir, filename);
				doc = WWXML.openDocument(dataConfigFile);
				doc = DataConfigurationUtils.convertToStandardDataConfigDocument(doc);
			}
			catch (WWRuntimeException e)
			{
				e.printStackTrace();
			}

			if (doc == null)
				continue;

			// 由于数据配置文件来自于已有的文件,因此不能保证它是由当前版本WW's Installer
			// 产生的。可能是由之前版本或其他应用程序产生的,因此要为可能缺失的参数设置备用值(这些参数需要用来构建图层或高程模拟)
			AVList params = new AVListImpl();
			setFallbackParams(doc, filename, params);
			// 添加数据
			addInstalledData(doc, params);
		}
	}

	/**
	 * 
	 * @方法名称: setFallbackParams ;
	 * @方法描述: 设置备用参数值 ;
	 * @参数 :@param dataConfig :数据配置XML文件
	 * @参数 :@param filename :文件名
	 * @参数 :@param params :参数列表
	 * @返回类型: void ;
	 * @创建人:奔跑的鸡丝 ;
	 * @创建时间:2014-12-20 下午12:21:03;
	 * @throws
	 */
	private void setFallbackParams(Document dataConfig, String filename,
			AVList params)
	{
		XPath xpath = WWXML.makeXPath();
		Element domElement = dataConfig.getDocumentElement();

		// If the data configuration document doesn't define a cache name, then
		// compute one using the file's path
		// relative to its file cache directory.
		String s = WWXML.getText(domElement, "DataCacheName", xpath);
		if (s == null || s.length() == 0)
			DataConfigurationUtils.getDataConfigCacheName(filename, params);

		// If the data configuration document doesn't define the data's extreme
		// elevations, provide default values using
		// the minimum and maximum elevations of Earth.
		String type = DataConfigurationUtils.getDataConfigType(domElement);
		if (type.equalsIgnoreCase("ElevationModel"))
		{
			if (WWXML.getDouble(domElement, "ExtremeElevations/@min", xpath) == null)
				params.setValue(AVKey.ELEVATION_MIN, Earth.ELEVATION_MIN);
			if (WWXML.getDouble(domElement, "ExtremeElevations/@max", xpath) == null)
				params.setValue(AVKey.ELEVATION_MAX, Earth.ELEVATION_MAX);
		}
	}

	/**
	 * 
	 * @方法名称: addInstalledData ;
	 * @方法描述: 添加缓存数据 ;
	 * @参数 :@param dataConfig
	 * @参数 :@param params
	 * @返回类型: void ;
	 * @创建人:奔跑的鸡丝 ;
	 * @创建时间:2014-12-20 下午12:22:29;
	 * @throws
	 */
	private void addInstalledData(final Document dataConfig, final AVList params)
	{
		if (!SwingUtilities.isEventDispatchThread())
		{
			SwingUtilities.invokeLater(new Runnable()
			{
				public void run()
				{
					addInstalledData(dataConfig, params);
				}
			});
		}
		else
		{
			addInstalledCacheData(dataConfig.getDocumentElement(), params);
		}

	}

	/**
	 * 
	 * @方法名称: addInstalledCacheData ;
	 * @方法描述: 添加已有缓存数据 ;
	 * @参数 :@param domElement :数据XML描述文件
	 * @参数 :@param params :参数列表
	 * @返回类型: void ;
	 * @创建人:奔跑的鸡丝 ;
	 * @创建时间:2014-12-19 下午8:02:21;
	 * @throws
	 */
	public void addInstalledCacheData(final Element domElement,
			final AVList params)
	{
		if (domElement == null)
		{
			String message = Logging.getMessage("nullValue.DocumentIsNull");
			Logging.logger().severe(message);
			throw new IllegalArgumentException(message);
		}

		String description = getDescription(domElement); // 图层名称
		Sector sector = getSector(domElement); // 图层范围
		System.out.println(description);
		System.out.println(sector);
		addToWorldWindow(domElement, params);

	}

	/**
	 * 
	 * @方法名称: addToWorldWindow ;
	 * @方法描述: 将缓存文件加入WW ;
	 * @参数 :@param domElement
	 * @参数 :@param params
	 * @返回类型: void ;
	 * @创建人:奔跑的鸡丝 ;
	 * @创建时间:2014-12-19 下午4:44:08;
	 * @throws
	 */
	private void addToWorldWindow(Element domElement, AVList params)
	{
		String type = DataConfigurationUtils.getDataConfigType(domElement);
		if (type == null)
			return;

		if (type.equalsIgnoreCase("Layer"))
		{
			addLayerToWorldWindow(domElement, params);
		}
		else if (type.equalsIgnoreCase("ElevationModel"))
		{
			addElevationModelToWorldWindow(domElement, params);
		}
	}

	/**
	 * 
	 * @方法名称: addLayerToWorldWindow ;
	 * @方法描述: 向WW中添加图层 ;
	 * @参数 :@param domElement
	 * @参数 :@param params
	 * @返回类型: void ;
	 * @创建人:奔跑的鸡丝 ;
	 * @创建时间:2014-12-19 下午4:45:06;
	 * @throws
	 */
	private void addLayerToWorldWindow(Element domElement, AVList params)
	{
		Layer layer = null;
		try
		{
			// Factory创建的图层默认是不可见的
			Factory factory = (Factory) WorldWind.createConfigurationComponent(AVKey.LAYER_FACTORY);
			layer = (Layer) factory.createFromConfigSource(domElement, params);
		}
		catch (Exception e)
		{
			String message = Logging.getMessage(
					"generic.CreationFromConfigurationFailed",
					DataConfigurationUtils.getDataConfigDisplayName(domElement));
			Logging.logger().log(java.util.logging.Level.SEVERE, message, e);
		}

		if (layer == null)
			return;
		layer.setEnabled(true); // 设置图层可见

		// 添加至WW
		if (!getWorldWindowGLCanvas().getModel().getLayers().contains(layer))
		{
			getWorldWindowGLCanvas().getModel().getLayers().add(layer);
			// System.out.println(pLayerTree.getModel().getRoot().toString());
			Object rootObject = layerJTree.getModel().getRoot();
			if (!layerJTree.getModel().isLeaf(rootObject))
			{
				int count = layerJTree.getModel().getChildCount(rootObject);
				for (int i = 0; i < count; i++)
				{
					String childNodeNameString = layerJTree.getModel().getChild(
							rootObject, i).toString();
					if (childNodeNameString.equals("影像图层"))
					{
						((DefaultMutableTreeNode) layerJTree.getModel().getChild(
								rootObject, i)).add(new CheckBoxTreeNode(
								layer.getName()));
						layerJTree.updateUI();
					}
				}
			}

		}
	}

	/**
	 * 
	 * @方法名称: addElevationModelToWorldWindow ;
	 * @方法描述: 添加高程图层 ;
	 * @参数 :@param domElement
	 * @参数 :@param params
	 * @返回类型: void ;
	 * @创建人:奔跑的鸡丝 ;
	 * @创建时间:2014-12-19 下午4:51:37;
	 * @throws
	 */
	private void addElevationModelToWorldWindow(Element domElement,
			AVList params)
	{
		ElevationModel em = null;
		try
		{
			Factory factory = (Factory) WorldWind.createConfigurationComponent(AVKey.ELEVATION_MODEL_FACTORY);
			em = (ElevationModel) factory.createFromConfigSource(domElement,
					params);
		}
		catch (Exception e)
		{
			String message = Logging.getMessage(
					"generic.CreationFromConfigurationFailed",
					DataConfigurationUtils.getDataConfigDisplayName(domElement));
			Logging.logger().log(java.util.logging.Level.SEVERE, message, e);
		}

		if (em == null)
			return;

		ElevationModel defaultElevationModel = getWorldWindowGLCanvas().getModel().getGlobe().getElevationModel();
		if (defaultElevationModel instanceof CompoundElevationModel)
		{
			if (!((CompoundElevationModel) defaultElevationModel).containsElevationModel(em))
				((CompoundElevationModel) defaultElevationModel).addElevationModel(em);
		}
		else
		{
			CompoundElevationModel cm = new CompoundElevationModel();
			cm.addElevationModel(defaultElevationModel);
			cm.addElevationModel(em);
			getWorldWindowGLCanvas().getModel().getGlobe().setElevationModel(cm);
		}
	}

	/**
	 * 获取缓存文件类型 获取缓存配置文件描述:是Layer或者是Elevation
	 * 
	 * @方法名称: getDescription ;
	 * @方法描述: TODO ;
	 * @参数 :@param domElement
	 * @参数 :@return
	 * @返回类型: String ;
	 * @创建人:奔跑的鸡丝 ;
	 * @创建时间:2014-12-19 下午4:53:26;
	 * @throws
	 */
	private String getDescription(Element domElement)
	{
		String displayName = DataConfigurationUtils.getDataConfigDisplayName(domElement);
		String type = DataConfigurationUtils.getDataConfigType(domElement);

		StringBuilder sb = new StringBuilder(displayName);

		if (type.equalsIgnoreCase("Layer"))
		{
			sb.append(" (Layer)");
		}
		else if (type.equalsIgnoreCase("ElevationModel"))
		{
			sb.append(" (Elevations)");
		}

		return sb.toString();
	}

	/**
	 * 获取图层范围
	 * 
	 * @方法名称: getSector ;
	 * @方法描述: TODO ;
	 * @参数 :@param domElement
	 * @参数 :@return
	 * @返回类型: Sector ;
	 * @创建人:奔跑的鸡丝 ;
	 * @创建时间:2014-12-19 下午4:54:17;
	 * @throws
	 */
	protected static Sector getSector(Element domElement)
	{
		return WWXML.getSector(domElement, "Sector", null);
	}

	public static WorldWindowGLCanvas getWorldWindowGLCanvas()
	{
		return worldWindowGLCanvas;
	}

	public static void setWorldWindowGLCanvas(
			WorldWindowGLCanvas worldWindowGLCanvas)
	{
		LoadCacheData.worldWindowGLCanvas = worldWindowGLCanvas;
	}

	public JTree getLayerJTree()
	{
		return layerJTree;
	}

	public static void setLayerJTree(JTree layerJTree)
	{
		LoadCacheData.layerJTree = layerJTree;
	}

}

3、高程数据的加载

高程数据采用NASA的30m公开DEM数据,使用World Wind Server发布即可,详见前面的搭建本地World wind Severe服务器。最终实现效果图如下图所示。
World Wind Java开发之8——加载本地缓存文件构建大范围三维场景
PS:年末各种忙啊,项目总算结题,明天小组年会,预祝一切顺利!欢迎大家留言交流,共享自己的学习笔记。
World Wind Java的资料实在太少啦,断断续续总算搭建起了三维框架,后面陆续添加功能,计划做一个基于新安江模型的洪涝模拟仿真模块,将之前做的洪涝模拟和参数率定、径流模拟全部整合到自己的这个平台上来。