World Wind Java开发之十——AnalyticSurface栅格渲染

30 篇文章 1 订阅
29 篇文章 0 订阅

1、AnalyticSurfaceDemo

ArcGIS下对栅格的各种分级渲染效果是非常好的,可以做出很漂亮的图,现在在WW下也可以做出同样的效果了,看到这里是不是有点小兴奋呢。先看下WW自带的AnalyticSurfaceDemo的运行效果图:


通过看源代码可以知道给出了三种渲染示例,其中两种是动态的,这里我需要的是对dem数据或者是单波段影像的渲染,也就是左上方的渲染效果。

2、AnalyticSurface类

下面来看下主要用到的类:


主要用到的方法:

// 创建AnalyticSurface并设置其属性
		final AnalyticSurface surface = new AnalyticSurface();
		surface.setSector(raster.getSector());
		surface.setDimensions(raster.getWidth(), raster.getHeight());
		surface.setValues(AnalyticSurface.createColorGradientValues(
				raster.getBuffer(), raster.getTransparentValue(), extremes[0],
				extremes[1], minHue, maxHue));
		// surface.setVerticalScale(5e3);
		// 设置表面渲染方式为 CLAMP_TO_GROUND
		surface.setAltitudeMode(WorldWind.CLAMP_TO_GROUND);
根据自己的需要可以查阅开发文档设置其他属性。

3、DEM渲染实例

将demo中的代码稍加修改封装为AnalyticSurfaceUtil类以供后面所有栅格数据的渲染使用,目前比较简单,后面陆续扩充该类。
WW下渲染效果:

ArcMap下渲染效果:

可以看到WW下渲染的效果丝毫不逊色,图是不是很漂亮呢。

4、洪涝模拟渲染

这是对之前洪涝模拟的改进,对洪涝模拟输出的范围图和深度图进行渲染。
(1)范围图

(2)深度图

这幅渲染的深度图是不是有种火山喷发的感觉,很有艺术美感,非常喜欢这个渲染的效果。改一下配色再看下另一种渲染效果:

5、源码。

下面是自己封装的AnalyticSurfaceUtil类,供大家参考:
/**
 * @Copyright 2014-2020 @刘硕
 **/

package edu.whu.vge.util;

import gov.nasa.worldwind.WorldWind;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.avlist.AVList;
import gov.nasa.worldwind.data.BufferWrapperRaster;
import gov.nasa.worldwind.data.DataRaster;
import gov.nasa.worldwind.data.DataRasterReader;
import gov.nasa.worldwind.data.DataRasterReaderFactory;
import gov.nasa.worldwind.exception.WWRuntimeException;
import gov.nasa.worldwind.geom.Extent;
import gov.nasa.worldwind.geom.Sector;
import gov.nasa.worldwind.layers.RenderableLayer;
import gov.nasa.worldwind.render.DrawContext;
import gov.nasa.worldwind.render.Renderable;
import gov.nasa.worldwind.util.Logging;
import gov.nasa.worldwind.util.WWBufferUtil;
import gov.nasa.worldwind.util.WWIO;
import gov.nasa.worldwind.util.WWMath;
import gov.nasa.worldwindx.examples.analytics.AnalyticSurface;
import gov.nasa.worldwindx.examples.analytics.AnalyticSurfaceAttributes;
import gov.nasa.worldwindx.examples.analytics.AnalyticSurfaceLegend;
import gov.nasa.worldwindx.examples.util.ExampleUtil;

import java.awt.Point;
import java.io.File;
import java.text.DecimalFormat;
import java.text.FieldPosition;
import java.text.Format;

import javax.swing.SwingUtilities;

/**
 * @项目名称:SmartScope
 * @类名称:AnalyticSurfaceUtil
 * @类描述:
 * @创建人:刘硕
 * @创建时间:2015-1-21 下午3:40:54
 * @修改备注:
 * @版本:
 */

public class AnalyticSurfaceUtil
{

	/**
	 * 创建一个新的实例 AnalyticSurfaceUtil.
	 * 
	 */
	public AnalyticSurfaceUtil()
	{
		// TODO Auto-generated constructor stub
	}

	public static void createPrecipitationSurface(double minHue, double maxHue,
			final RenderableLayer outLayer)
	{
		String DATA_PATH = "J:/data/wwj/FloodDepth.tif";
		BufferWrapperRaster raster = loadRasterElevations(DATA_PATH);
		if (raster == null)
			return;

		// 获取像元最大值与最小值
		double[] extremes = WWBufferUtil.computeExtremeValues(
				raster.getBuffer(), raster.getTransparentValue());
		if (extremes == null)
			return;

		// 创建AnalyticSurface并设置其属性
		final AnalyticSurface surface = new AnalyticSurface();
		surface.setSector(raster.getSector());
		surface.setDimensions(raster.getWidth(), raster.getHeight());
		surface.setValues(AnalyticSurface.createColorGradientValues(
				raster.getBuffer(), raster.getTransparentValue(), extremes[0],
				extremes[1], minHue, maxHue));
		// surface.setVerticalScale(5e3);
		// 设置表面渲染方式为 CLAMP_TO_GROUND
		surface.setAltitudeMode(WorldWind.CLAMP_TO_GROUND);
		AnalyticSurfaceAttributes attr = new AnalyticSurfaceAttributes();
		attr.setDrawOutline(false);
		attr.setDrawShadow(false);
		attr.setInteriorOpacity(0.6);
		surface.setSurfaceAttributes(attr);

		// 设置图例样式
		Format legendLabelFormat = new DecimalFormat("# m")
		{
			public StringBuffer format(double number, StringBuffer result,
					FieldPosition fieldPosition)
			{
				double valueInFeet = number;
				return super.format(valueInFeet, result, fieldPosition);
			}
		};

		// 创建图例
		final AnalyticSurfaceLegend legend = AnalyticSurfaceLegend.fromColorGradient(
				extremes[0], extremes[1], minHue, maxHue,
				AnalyticSurfaceLegend.createDefaultColorGradientLabels(
						extremes[0], extremes[1], legendLabelFormat),
				AnalyticSurfaceLegend.createDefaultTitle("Legend"));
		legend.setOpacity(0.8);
		legend.setScreenLocation(new Point(100, 300));

		SwingUtilities.invokeLater(new Runnable()
		{
			public void run()
			{
				surface.setClientLayer(outLayer);
				outLayer.addRenderable(surface);
				outLayer.addRenderable(createLegendRenderable(surface, 600,
						legend));
			}
		});
	}

	/**
	 * 
	 * @方法名称: loadRasterElevations ;
	 * @方法描述: 读取数据(单波段) ;
	 * @参数 :@param path
	 * @参数 :@return
	 * @返回类型: BufferWrapperRaster ;
	 * @创建人:刘硕;
	 * @创建时间:2015-1-22 上午11:25:40;
	 * @throws
	 */
	public static BufferWrapperRaster loadRasterElevations(String path)
	{
		// Download the data and save it in a temp file.
		File file = ExampleUtil.saveResourceToTempFile(path,
				"." + WWIO.getSuffix(path));

		// Create a raster reader for the file type.
		DataRasterReaderFactory readerFactory = (DataRasterReaderFactory) WorldWind.createConfigurationComponent(AVKey.DATA_RASTER_READER_FACTORY_CLASS_NAME);
		DataRasterReader reader = readerFactory.findReaderFor(file, null);

		try
		{
			// Before reading the raster, verify that the file contains
			// elevations.
			AVList metadata = reader.readMetadata(file, null);
			if (metadata == null
					|| !AVKey.ELEVATION.equals(metadata.getStringValue(AVKey.PIXEL_FORMAT)))
			{
				String msg = Logging.getMessage(
						"ElevationModel.SourceNotElevations",
						file.getAbsolutePath());
				Logging.logger().severe(msg);
				throw new IllegalArgumentException(msg);
			}

			// Read the file into the raster.
			DataRaster[] rasters = reader.read(file, null);
			if (rasters == null || rasters.length == 0)
			{
				String msg = Logging.getMessage(
						"ElevationModel.CannotReadElevations",
						file.getAbsolutePath());
				Logging.logger().severe(msg);
				throw new WWRuntimeException(msg);
			}

			// Determine the sector covered by the elevations. This
			// information
			// is in the GeoTIFF file or auxiliary
			// files associated with the elevations file.
			Sector sector = (Sector) rasters[0].getValue(AVKey.SECTOR);
			if (sector == null)
			{
				String msg = Logging.getMessage("DataRaster.MissingMetadata",
						AVKey.SECTOR);
				Logging.logger().severe(msg);
				throw new IllegalArgumentException(msg);
			}

			// Request a sub-raster that contains the whole file. This step
			// is
			// necessary because only sub-rasters
			// are reprojected (if necessary); primary rasters are not.
			int width = rasters[0].getWidth();
			int height = rasters[0].getHeight();

			DataRaster subRaster = rasters[0].getSubRaster(width, height,
					sector, rasters[0]);

			// Verify that the sub-raster can create a ByteBuffer, then
			// create
			// one.
			if (!(subRaster instanceof BufferWrapperRaster))
			{
				String msg = Logging.getMessage(
						"ElevationModel.CannotCreateElevationBuffer", path);
				Logging.logger().severe(msg);
				throw new WWRuntimeException(msg);
			}

			return (BufferWrapperRaster) subRaster;
		}
		catch (Exception e)
		{
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 
	 * @方法名称: createLegendRenderable ;
	 * @方法描述: 创建图例 ;
	 * @参数 :@param surface
	 * @参数 :@param surfaceMinScreenSize
	 * @参数 :@param legend
	 * @参数 :@return
	 * @返回类型: Renderable ;
	 * @创建人:刘硕;
	 * @创建时间:2015-1-22 上午11:26:07;
	 * @throws
	 */
	protected static Renderable createLegendRenderable(
			final AnalyticSurface surface, final double surfaceMinScreenSize,
			final AnalyticSurfaceLegend legend)
	{
		return new Renderable()
		{
			public void render(DrawContext dc)
			{
				Extent extent = surface.getExtent(dc);
				if (!extent.intersects(dc.getView().getFrustumInModelCoordinates()))
					return;

				if (WWMath.computeSizeInWindowCoordinates(dc, extent) < surfaceMinScreenSize)
					return;

				legend.render(dc);
			}
		};
	}

}

目前还很不完善,后面有需要的话打算做一个类似于ArcGIS的分级渲染工具,对于降雨量蒸散发量等数据都可以很方便的进行渲染。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值