电子商务网站开发的基本要求,买房在线咨询,wordpress+论坛+注册,锦州建设局网站目录 Java项目实战记录#xff1a;雷达数据渲染业务背景代码逻辑数据结构颜色渲染MapContent加载数据并输出截图 完整代码GenerateMapImage地图渲染工具测试代码 渲染效果 Java项目实战记录#xff1a;雷达数据渲染
业务背景
我之前已经成功使用Java语言解析了C处理的雷达数… 目录 Java项目实战记录雷达数据渲染业务背景代码逻辑数据结构颜色渲染MapContent加载数据并输出截图 完整代码GenerateMapImage地图渲染工具测试代码 渲染效果 Java项目实战记录雷达数据渲染
业务背景
我之前已经成功使用Java语言解析了C处理的雷达数据文件并提取了其中的经纬度点数据。这些文件中的点数据量相当庞大每个雷达产品文件通常包含超过20万个数据点有时甚至更多。
目前面临着如何在前端展示这20万个点数据的挑战。有两种可行的解决方案一种是直接将所有点数据传输到前端进行渲染另一种则是在后端将点数据渲染成PNG图片然后再将图片传输到前端展示。经过考虑决定采用第二种方案即在后端进行数据的渲染处理。这样不仅可以有效减轻前端的负担提高数据处理和展示的效率而且还可以通过优化渲染过程来提高整体的展示质量。
代码逻辑
这里选用了GeoTools这个强大的开源Java库来执行雷达数据的渲染任务。GeoTools是专为地理空间数据处理设计的工具集它提供了丰富的API来支持地理数据的读取、查询、分析和展示。利用GeoTools能够高效地将雷达扫描得到的点数据转换为图像并以PNG格式保存。
数据结构
雷达数据结构如下所示
import lombok.Data;/*** 雷达点数据信息*/
Data
public class LonLatData {private float lon; // 经度private float lat; // 纬度private double val; // 回拨强度 等于value/100
}颜色渲染
根据回拨强度需要渲染不同颜色定义一个Style样式具体分类如下所示 /* * 根据数值创建基于值的点样式。** return 样式对象根据不同的值范围应用不同的颜色。*/public static Style createValueBasedPointStyle() {// 创建一个黑色描边宽度为1。Stroke stroke styleFactory.createStroke(filterFactory.literal(Color.BLACK), filterFactory.literal(1));FeatureTypeStyle fts styleFactory.createFeatureTypeStyle();// 定义值的范围及其对应的颜色十六进制表示。Object[][] valueRanges {{-5, Double.NEGATIVE_INFINITY, #00acb8},{0, -5, #c1c0ac},{10, 0, #7a71fe},{15, 10, #1e26e3},{20, 15, #a6fcbb},{25, 20, #00ea15},{30, 25, #10932d},{35, 30, #fcf477},{40, 35, #c9c815},{45, 40, #8c8c12},{50, 45, #feadc0},{55, 50, #ff6370},{60, 55, #ee0244},{65, 60, #d48eff},{Double.POSITIVE_INFINITY, 65, #ab23ff}};// 遍历每个值范围为每个范围创建一个规则并添加到FeatureTypeStyle。for (Object[] range : valueRanges) {fts.rules().add(createRuleForRange(Double.parseDouble(range[1].toString()), Double.parseDouble(range[0].toString()), range[2].toString()));}// 创建样式并添加FeatureTypeStyle。Style style styleFactory.createStyle();style.featureTypeStyles().add(fts);return style;}MapContent加载数据并输出截图
首先创建了一个MapContent对象用它作为整个地图的内容载体。接着创建了一个包含所有雷达数据点的图层并为这些点指定了样式这样做旨在通过视觉上的区分提高数据的可读性。然后将这个图层添加到MapContent中最后使用GeoTools的功能将这些内容渲染成一张清晰展示雷达数据的PNG图片。 /*** 根据坐标和值创建地图图像。** param coordinates 坐标对象列表每个对象包含坐标点和与之相关联的值。* param outputPath 图像输出路径指定生成的地图图像保存的位置。* throws Exception 抛出异常处理文件操作或渲染过程中可能出现的错误。*/public static void createMapImageVal(ArrayListCoordinatePojo coordinates, String outputPath) throws Exception {// 步骤1: 定义SimpleFeatureTypeSimpleFeatureTypeBuilder typeBuilder new SimpleFeatureTypeBuilder();typeBuilder.setName(Point);typeBuilder.setCRS(DefaultGeographicCRS.WGS84); // 坐标参考系统typeBuilder.add(location, Point.class); // 添加位置字段typeBuilder.add(value, Double.class); // 添加值字段SimpleFeatureType TYPE typeBuilder.buildFeatureType(); // 构建特征类型// 步骤2: 创建SimpleFeatureCollectionDefaultFeatureCollection featureCollection new DefaultFeatureCollection(internal, TYPE);GeometryFactory geometryFactory JTSFactoryFinder.getGeometryFactory();SimpleFeatureBuilder featureBuilder new SimpleFeatureBuilder(TYPE);// 为每个坐标点创建特征并添加到集合中for (CoordinatePojo data : coordinates) {Point point geometryFactory.createPoint(data.getCoordinate()); // 根据坐标创建点// 根据点的值创建SimpleFeatureSimpleFeature feature SimpleFeatureBuilder.build(TYPE, new Object[]{point, data.getValue()}, null);featureCollection.add(feature); // 将特征添加到集合}// 创建基于值的点样式Style style createValueBasedPointStyle(); // 假设这是一个自定义方法创建基于值的样式// 步骤3: 定义样式// 这里我们使用了自定义的创建样式方法而不是SLD.createPointStyle// 步骤4: 渲染图像MapContent mapContent new MapContent();mapContent.setTitle(Sample Map); // 设置地图标题Layer layer new FeatureLayer(featureCollection, style); // 创建图层mapContent.addLayer(layer); // 将图层添加到地图内容GTRenderer renderer new StreamingRenderer();renderer.setMapContent(mapContent); // 设置地图内容Rectangle imageBounds null;ReferencedEnvelope mapBounds null;try {mapBounds mapContent.getMaxBounds(); // 获取最大边界double heightToWidth mapBounds.getSpan(1) / mapBounds.getSpan(0); // 计算高宽比imageBounds new Rectangle(0, 0, 800, (int) Math.round(800 * heightToWidth)); // 设置图像边界} catch (Exception e) {// 处理可能的异常}BufferedImage image new BufferedImage(imageBounds.width, imageBounds.height, BufferedImage.TYPE_INT_ARGB);Graphics2D gr image.createGraphics();gr.setPaint(Color.WHITE);gr.fill(imageBounds); // 填充背景色renderer.paint(gr, imageBounds, mapBounds); // 渲染图像File file new File(outputPath); // 创建文件ImageIO.write(image, png, file); // 写入图像文件gr.dispose(); // 释放资源mapContent.dispose(); // 释放地图内容}完整代码
GenerateMapImage地图渲染工具
import com.zykj.radar_server.datahandle.RadarDataParser;
import com.zykj.radar_server.datahandle.pojo.LonLatData;
import com.zykj.radar_server.entity.pojo.CoordinatePojo;
import com.zykj.radar_server.entity.pojo.RadarDataPojo;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.renderer.GTRenderer;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.styling.SLD;
import org.geotools.styling.Stroke;
import org.geotools.styling.Style;import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;import org.geotools.styling.*;
import org.opengis.filter.FilterFactory2;public class GenerateMapImage {/*** 根据坐标和值创建地图图像。** param coordinates 坐标对象列表每个对象包含坐标点和与之相关联的值。* param outputPath 图像输出路径指定生成的地图图像保存的位置。* throws Exception 抛出异常处理文件操作或渲染过程中可能出现的错误。*/public static void createMapImageVal(ArrayListCoordinatePojo coordinates, String outputPath) throws Exception {// 步骤1: 定义SimpleFeatureTypeSimpleFeatureTypeBuilder typeBuilder new SimpleFeatureTypeBuilder();typeBuilder.setName(Point);typeBuilder.setCRS(DefaultGeographicCRS.WGS84); // 坐标参考系统typeBuilder.add(location, Point.class); // 添加位置字段typeBuilder.add(value, Double.class); // 添加值字段SimpleFeatureType TYPE typeBuilder.buildFeatureType(); // 构建特征类型// 步骤2: 创建SimpleFeatureCollectionDefaultFeatureCollection featureCollection new DefaultFeatureCollection(internal, TYPE);GeometryFactory geometryFactory JTSFactoryFinder.getGeometryFactory();SimpleFeatureBuilder featureBuilder new SimpleFeatureBuilder(TYPE);// 为每个坐标点创建特征并添加到集合中for (CoordinatePojo data : coordinates) {Point point geometryFactory.createPoint(data.getCoordinate()); // 根据坐标创建点// 根据点的值创建SimpleFeatureSimpleFeature feature SimpleFeatureBuilder.build(TYPE, new Object[]{point, data.getValue()}, null);featureCollection.add(feature); // 将特征添加到集合}// 创建基于值的点样式Style style createValueBasedPointStyle(); // 假设这是一个自定义方法创建基于值的样式// 步骤3: 定义样式// 这里我们使用了自定义的创建样式方法而不是SLD.createPointStyle// 步骤4: 渲染图像MapContent mapContent new MapContent();mapContent.setTitle(Sample Map); // 设置地图标题Layer layer new FeatureLayer(featureCollection, style); // 创建图层mapContent.addLayer(layer); // 将图层添加到地图内容GTRenderer renderer new StreamingRenderer();renderer.setMapContent(mapContent); // 设置地图内容Rectangle imageBounds null;ReferencedEnvelope mapBounds null;try {mapBounds mapContent.getMaxBounds(); // 获取最大边界double heightToWidth mapBounds.getSpan(1) / mapBounds.getSpan(0); // 计算高宽比imageBounds new Rectangle(0, 0, 800, (int) Math.round(800 * heightToWidth)); // 设置图像边界} catch (Exception e) {// 处理可能的异常}BufferedImage image new BufferedImage(imageBounds.width, imageBounds.height, BufferedImage.TYPE_INT_ARGB);Graphics2D gr image.createGraphics();gr.setPaint(Color.WHITE);gr.fill(imageBounds); // 填充背景色renderer.paint(gr, imageBounds, mapBounds); // 渲染图像File file new File(outputPath); // 创建文件ImageIO.write(image, png, file); // 写入图像文件gr.dispose(); // 释放资源mapContent.dispose(); // 释放地图内容}// StyleFactory 用于创建地图样式元素例如图层的边框、填充等。private static final StyleFactory styleFactory CommonFactoryFinder.getStyleFactory();// FilterFactory2 用于创建过滤条件例如在样式规则中选择符合特定条件的要素。private static final FilterFactory2 filterFactory CommonFactoryFinder.getFilterFactory2();/*** 根据数值创建基于值的点样式。** return 样式对象根据不同的值范围应用不同的颜色。*/public static Style createValueBasedPointStyle() {// 创建一个黑色描边宽度为1。Stroke stroke styleFactory.createStroke(filterFactory.literal(Color.BLACK), filterFactory.literal(1));FeatureTypeStyle fts styleFactory.createFeatureTypeStyle();// 定义值的范围及其对应的颜色十六进制表示。Object[][] valueRanges {{-5, Double.NEGATIVE_INFINITY, #00acb8},{0, -5, #c1c0ac},{10, 0, #7a71fe},{15, 10, #1e26e3},{20, 15, #a6fcbb},{25, 20, #00ea15},{30, 25, #10932d},{35, 30, #fcf477},{40, 35, #c9c815},{45, 40, #8c8c12},{50, 45, #feadc0},{55, 50, #ff6370},{60, 55, #ee0244},{65, 60, #d48eff},{Double.POSITIVE_INFINITY, 65, #ab23ff}};// 遍历每个值范围为每个范围创建一个规则并添加到FeatureTypeStyle。for (Object[] range : valueRanges) {fts.rules().add(createRuleForRange(Double.parseDouble(range[1].toString()), Double.parseDouble(range[0].toString()), range[2].toString()));}// 创建样式并添加FeatureTypeStyle。Style style styleFactory.createStyle();style.featureTypeStyles().add(fts);return style;}/*** 根据给定的值范围和颜色创建一个规则。** param min 范围的最小值。* param max 范围的最大值。* param hexColor 颜色的十六进制表示。* return 样式规则包含一个基于值范围的符号化表示。*/public static Rule createRuleForRange(double min, Object max, String hexColor) {// 根据十六进制颜色创建填充并设置不透明度为1.0。Fill fill styleFactory.createFill(filterFactory.literal(hexToColor(hexColor)), filterFactory.literal(1.0));// 获取默认的圆形标记并设置填充和描边。Mark mark styleFactory.getCircleMark();mark.setFill(fill);mark.setStroke(null); // 不使用描边。// 创建图形对象清除默认的图形符号添加自定义的标记。Graphic graphic styleFactory.createDefaultGraphic();graphic.graphicalSymbols().clear();graphic.graphicalSymbols().add(mark);graphic.setSize(filterFactory.literal(4)); // 设置图形大小。// 创建点符号器应用上面的图形。PointSymbolizer symbolizer styleFactory.createPointSymbolizer(graphic, null);// 创建规则设置过滤条件为值在指定范围内。Rule rule styleFactory.createRule();rule.symbolizers().add(symbolizer);rule.setFilter(filterFactory.between(filterFactory.property(value),filterFactory.literal(min),filterFactory.literal(max)));return rule;}/*** 将十六进制颜色字符串转换为Color对象。** param hex 十六进制颜色字符串例如#ff0000表示红色。* return 转换后的Color对象。*/public static Color hexToColor(String hex) {// 将十六进制字符串转换为整数并创建颜色对象。// 这里没有添加前缀FF因为Color类的构造函数默认处理不透明颜色。return new Color(Integer.parseInt(hex.substring(1), 16));}}
测试代码
测试数据自行准备如何解析雷达数据请看我上一篇博客。
package com.zykj.radar_server.data;import com.zykj.radar_server.datahandle.GenerateMapImage;
import com.zykj.radar_server.datahandle.RadarDataParser;
import com.zykj.radar_server.datahandle.pojo.LonLatData;
import com.zykj.radar_server.entity.pojo.CoordinatePojo;
import com.zykj.radar_server.entity.pojo.RadarDataPojo;import org.junit.jupiter.api.Test;
import org.locationtech.jts.geom.Coordinate;
import org.springframework.boot.test.context.SpringBootTest;import java.util.ArrayList;
import java.util.List;SpringBootTest
public class GenerateMapImageTest {Testpublic void test() throws Exception {String filePath D:\\雷达测试数据\\数据\\20190305_110801_ZHBJ_Z_VOL_2.50.dat;RadarDataPojo radarDataPojo RadarDataParser.handlePolarProductForPojo(filePath);ArrayListLonLatData lonLatDataList radarDataPojo.getLonLatDataList();System.out.println(lonLatDataList.size());ArrayListCoordinatePojo coordinates new ArrayList();for (int i 0; i lonLatDataList.size(); i) {CoordinatePojo coordinatePojo new CoordinatePojo();coordinatePojo.setCoordinate(new Coordinate(lonLatDataList.get(i).getLon(), lonLatDataList.get(i).getLat()));double val lonLatDataList.get(i).getVal();coordinatePojo.setValue(val);coordinates.add(coordinatePojo);}// 指定输出图片路径String outputPath D:\\temp\\layers\\jiangsu_map_val4.png;GenerateMapImage.createMapImageVal(coordinates, outputPath);System.out.println(地图图片已生成: outputPath);}}渲染效果
雷达数据渲染效果如下所示颜色越深回波强度越大 渲染点的大小可自行调节。 文章转载自: http://www.morning.cfrz.cn.gov.cn.cfrz.cn http://www.morning.hbqfh.cn.gov.cn.hbqfh.cn http://www.morning.mxptg.cn.gov.cn.mxptg.cn http://www.morning.qcslh.cn.gov.cn.qcslh.cn http://www.morning.mspqw.cn.gov.cn.mspqw.cn http://www.morning.qgmwt.cn.gov.cn.qgmwt.cn http://www.morning.zkdbx.cn.gov.cn.zkdbx.cn http://www.morning.qstjr.cn.gov.cn.qstjr.cn http://www.morning.tzzkm.cn.gov.cn.tzzkm.cn http://www.morning.xhklb.cn.gov.cn.xhklb.cn http://www.morning.jyjqh.cn.gov.cn.jyjqh.cn http://www.morning.jtybl.cn.gov.cn.jtybl.cn http://www.morning.dgxrz.cn.gov.cn.dgxrz.cn http://www.morning.mmzhuti.com.gov.cn.mmzhuti.com http://www.morning.wdrxh.cn.gov.cn.wdrxh.cn http://www.morning.dmtwz.cn.gov.cn.dmtwz.cn http://www.morning.sgmis.com.gov.cn.sgmis.com http://www.morning.lpzqd.cn.gov.cn.lpzqd.cn http://www.morning.ktpzb.cn.gov.cn.ktpzb.cn http://www.morning.gmrxh.cn.gov.cn.gmrxh.cn http://www.morning.kysport1102.cn.gov.cn.kysport1102.cn http://www.morning.ynrzf.cn.gov.cn.ynrzf.cn http://www.morning.ftgwj.cn.gov.cn.ftgwj.cn http://www.morning.ssfq.cn.gov.cn.ssfq.cn http://www.morning.rpfpx.cn.gov.cn.rpfpx.cn http://www.morning.tknqr.cn.gov.cn.tknqr.cn http://www.morning.ppzgr.cn.gov.cn.ppzgr.cn http://www.morning.jsdntd.com.gov.cn.jsdntd.com http://www.morning.ruifund.com.gov.cn.ruifund.com http://www.morning.jfbgn.cn.gov.cn.jfbgn.cn http://www.morning.frnjm.cn.gov.cn.frnjm.cn http://www.morning.hdpcn.cn.gov.cn.hdpcn.cn http://www.morning.qgghr.cn.gov.cn.qgghr.cn http://www.morning.lmhwm.cn.gov.cn.lmhwm.cn http://www.morning.nuejun.com.gov.cn.nuejun.com http://www.morning.mcndn.cn.gov.cn.mcndn.cn http://www.morning.rpljf.cn.gov.cn.rpljf.cn http://www.morning.pmlgr.cn.gov.cn.pmlgr.cn http://www.morning.qynnw.cn.gov.cn.qynnw.cn http://www.morning.pflpb.cn.gov.cn.pflpb.cn http://www.morning.fthqc.cn.gov.cn.fthqc.cn http://www.morning.pbzgj.cn.gov.cn.pbzgj.cn http://www.morning.lhgkr.cn.gov.cn.lhgkr.cn http://www.morning.rfxyk.cn.gov.cn.rfxyk.cn http://www.morning.wrbx.cn.gov.cn.wrbx.cn http://www.morning.kghhl.cn.gov.cn.kghhl.cn http://www.morning.dhtdl.cn.gov.cn.dhtdl.cn http://www.morning.pjwfs.cn.gov.cn.pjwfs.cn http://www.morning.wpjst.cn.gov.cn.wpjst.cn http://www.morning.mtmnk.cn.gov.cn.mtmnk.cn http://www.morning.bhbxd.cn.gov.cn.bhbxd.cn http://www.morning.hxxyp.cn.gov.cn.hxxyp.cn http://www.morning.ktskc.cn.gov.cn.ktskc.cn http://www.morning.qxbsq.cn.gov.cn.qxbsq.cn http://www.morning.pzjrm.cn.gov.cn.pzjrm.cn http://www.morning.bnpn.cn.gov.cn.bnpn.cn http://www.morning.xnymt.cn.gov.cn.xnymt.cn http://www.morning.mbzlg.cn.gov.cn.mbzlg.cn http://www.morning.wjrtg.cn.gov.cn.wjrtg.cn http://www.morning.lcwhn.cn.gov.cn.lcwhn.cn http://www.morning.xbzfz.cn.gov.cn.xbzfz.cn http://www.morning.nppml.cn.gov.cn.nppml.cn http://www.morning.xmwdt.cn.gov.cn.xmwdt.cn http://www.morning.zqfz.cn.gov.cn.zqfz.cn http://www.morning.drywd.cn.gov.cn.drywd.cn http://www.morning.nqyzg.cn.gov.cn.nqyzg.cn http://www.morning.xnltz.cn.gov.cn.xnltz.cn http://www.morning.gbljq.cn.gov.cn.gbljq.cn http://www.morning.sjsks.cn.gov.cn.sjsks.cn http://www.morning.lmfxq.cn.gov.cn.lmfxq.cn http://www.morning.dytqf.cn.gov.cn.dytqf.cn http://www.morning.nmbbt.cn.gov.cn.nmbbt.cn http://www.morning.zyrcf.cn.gov.cn.zyrcf.cn http://www.morning.hlkxb.cn.gov.cn.hlkxb.cn http://www.morning.fxqjz.cn.gov.cn.fxqjz.cn http://www.morning.ytrbq.cn.gov.cn.ytrbq.cn http://www.morning.ghrhb.cn.gov.cn.ghrhb.cn http://www.morning.slfmp.cn.gov.cn.slfmp.cn http://www.morning.rnmmh.cn.gov.cn.rnmmh.cn http://www.morning.bqhlp.cn.gov.cn.bqhlp.cn