爱气象,爱气象家园! 

气象家园

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 434|回复: 13

Java二次开发出图有偏差

[复制链接]
发表于 2025-8-21 10:34:08 | 显示全部楼层 |阅读模式

登录后查看更多精彩内容~

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
本帖最后由 zhangbote 于 2025-8-21 10:40 编辑

Java二次开发遇到了问题,求教一下。。。
绘制降水图的时候,某个位置的降水量本身应该是大于100mm,绘制粉色的,但是却绘制了小于100mm的蓝色。我查询了区域内多个点位的数据,确实是大于100mm。因为数据是逐小时的,下个小时就又绘制上粉色了,上个小时也是粉色。数据差距并不大


绘制的图

绘制的图

实际图

实际图
密码修改失败请联系微信:mofangbao
发表于 2025-8-21 16:41:29 | 显示全部楼层
可以把出现问题的数据和代码共享出来,才有可能有针对性的找到问题并改进。
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

发表于 2025-8-23 17:23:32 | 显示全部楼层
可以检查下离散点的降水插值后的结果,是不是极值点的雨量值小于100了,插值结果有可能与极值点的值出现偏差。
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

 楼主| 发表于 7 天前 | 显示全部楼层
本帖最后由 zhangbote 于 2025-8-25 10:15 编辑
MeteoInfo 发表于 2025-8-21 16:41
可以把出现问题的数据和代码共享出来,才有可能有针对性的找到问题并改进。

数据不太好分享是多个1公里的格点数据累加的结果。我想了解一下,使用格点数据绘图的时候,是否也会内置先插值一下?完整的绘图Util的代码如下
  1. @Component
  2. @AllArgsConstructor
  3. public class RollRainMeteoUtils {

  4.     private LocalFileProp fileProp;

  5.     public String createPngByGrid(MeteoDataVO info, GridData grid, String outputPath, String outputName, String timeTitle, String publishTitle) throws Exception {
  6.         if (!outputPath.endsWith("/")) {
  7.             outputPath += "/";
  8.         }

  9.         VectorLayer hebeiMap = this.createMapLayer(fileProp.getShpPath() + "hebei(1).shp");
  10.         hebeiMap.setLayerName("jjjMap");
  11.         VectorLayer nameMap = MapDataManage.readMapFile_ShapeFile(fileProp.getShpPath() + "county.shp");
  12.         // 描述地图边界线
  13.         PolygonBreak pb = (PolygonBreak) nameMap.getLegendScheme().getLegendBreak(0);
  14.         // 是否设置填充
  15.         pb.setDrawFill(false);
  16.         pb.setDrawOutline(false);
  17.         checkCityName(hebeiMap);
  18.         checkCountyName(nameMap);
  19.         // 读取色阶
  20.         LegendScheme als = this.createLegendScheme(info.getLegendValues(), info.getColorStrArr(), grid.getDoubleMissingValue());
  21.         // 绘制图层
  22.         VectorLayer layer = DrawMeteoData.createShadedLayer(grid, als, "", "", false);
  23.         layer.setMaskout(true);
  24.         // 创建视图
  25.         MapView view = new MapView();
  26.         // 叠加图层
  27.         view.addLayer(layer);
  28.         view.addLayer(hebeiMap);
  29.         view.addLayer(nameMap);

  30.         // 叠加图层
  31.         if (info.getIfZoom()) {
  32.             if (info.getZoomXY() != null && info.getZoomXY().length != 0) {
  33.                 view.zoomToExtentLonLatEx(new Extent(info.getZoomXY()[0], info.getZoomXY()[1], info.getZoomXY()[2], info.getZoomXY()[3]));
  34.             }
  35.         }
  36.         // 改变投影
  37.         String projStr = "+datum=WGS84 +a=6378137.0 +rf=298.257223563 +proj=longlat +ellps=WGS84 ";
  38.         // String projStr = "+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +R=6378137 +units=m +no_defs";
  39.         ProjectionInfo aProjInfo = ProjectionInfo.factory(projStr);
  40.         view.projectLayers(aProjInfo);

  41.         // 视图设置
  42.         MapLayout layout = new MapLayout();
  43.         // 获取地图框
  44.         MapFrame frame = layout.getActiveMapFrame();

  45.         frame.setMapView(view);
  46.         view.setAntiAlias(false);
  47.         layout.setAntiAlias(false);
  48.         Extent extent = view.getExtent();
  49.         int rectangleWidth = info.getPngSize() == 0 ? 800 : info.getPngSize();
  50.         int rectangleHeight = (int) (rectangleWidth * 1D / extent.getWidth() * extent.getHeight());
  51.         Rectangle rectangle = new Rectangle(rectangleWidth, rectangleHeight);

  52.         // 设置地图区域大小和外边距
  53.         int width = rectangle.width;
  54.         int height = rectangle.height;

  55.         // 设置页面边界
  56.         int left = 20;
  57.         int right = 20;
  58.         int top = 100;
  59.         int bottom = 100;
  60.         layout.setPageBounds(new Rectangle(0, 0, width + left + right, height + top + bottom));
  61.         MapView mapView = frame.getMapView();
  62.         mapView.getMaskOut().setMask(true);
  63.         mapView.getMaskOut().setMaskLayer(hebeiMap.getLayerName());
  64.         // 图形边框
  65.         frame.setDrawNeatLine(false);
  66.         frame.setDrawGridLabel(false);
  67.         // 设置布局边界
  68.         frame.setLayoutBounds(new Rectangle(left, top, width, height));
  69.         // 绘制网格刻度线
  70.         frame.setDrawGridLine(false);
  71.         // 设置网格间隔值
  72.         frame.setBackColor(new Color(255, 255, 255));
  73.         layout.setPageBackColor(new Color(255, 255, 255));

  74.         // this.setLegend(layout, layer, height);

  75.         FileUtils.forceMkdir(new File(outputPath));

  76.         String totalPath = outputPath + outputName;
  77.         // 将字符串路径转换为 Path 对象
  78.         Path path = Paths.get(totalPath);
  79.         if (Files.exists(path)) {
  80.             System.out.println(totalPath + " 文件存在,正在删除...");
  81.             // 删除文件
  82.             Files.delete(path);
  83.         }
  84.         layout.exportToPicture(totalPath);
  85.         this.addBaseInfo(totalPath, timeTitle, publishTitle);
  86.         return totalPath;
  87.     }

  88.     private void addBaseInfo(String pngPath, String timeTitle, String publishTitle) throws Exception {
  89.         // 读取原始的PNG图片
  90.         File file = new File(pngPath);
  91.         BufferedImage image = ImageIO.read(file);
  92.         int width = image.getWidth();
  93.         int height = image.getHeight();

  94.         // 创建Graphics2D对象
  95.         Graphics2D g2d = image.createGraphics();
  96.         // 启用抗锯齿
  97.         g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

  98.         this.addCenterContent(g2d, "河北省降水量预报", Color.BLACK,
  99.                 getChineseFont(30),
  100.                 width, 40);

  101.         this.addCenterContent(g2d, timeTitle, Color.BLACK,
  102.                 getChineseFont(20),
  103.                 width, 70);

  104.         this.addContent(g2d, "图例(单位:毫米)", Color.BLACK,
  105.                 getChineseFont(15), 470, 750);

  106.         this.addContent(g2d, "雨", Color.BLACK,
  107.                 getChineseFont(15), 470, 795);
  108.         this.addContent(g2d, "雨夹雪或", Color.BLACK,
  109.                 getChineseFont(15), 590, 780);
  110.         this.addContent(g2d, "雨转雪", Color.BLACK,
  111.                 getChineseFont(15), 590, 795);
  112.         this.addContent(g2d, "雪", Color.BLACK,
  113.                 getChineseFont(15), 710, 795);

  114.         this.addContent(g2d, publishTitle, Color.BLACK,
  115.                 getChineseFont(17), 520, 960);

  116.         // 释放Graphics2D对象
  117.         g2d.dispose();

  118.         BufferedImage newImage = this.addLegend(image, LegendUtil.createRainSnowLegend());
  119.         // 保存新的图片到文件
  120.         File outputfile = new File(pngPath);
  121.         File parentFile = outputfile.getParentFile();
  122.         if (!parentFile.exists()) {
  123.             parentFile.mkdirs();
  124.         }
  125.         if (outputfile.exists()) {
  126.             outputfile.delete();
  127.         }
  128.         ImageIO.write(newImage, "png", outputfile);
  129.     }

  130.     /**
  131.      * 添加图例
  132.      * @param baseImage 底图
  133.      * @param legendImage 图例
  134.      * @return
  135.      */
  136.     private BufferedImage addLegend(BufferedImage baseImage, BufferedImage legendImage) {
  137.         int width = baseImage.getWidth();
  138.         int height = baseImage.getHeight();
  139.         BufferedImage newImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
  140.         Graphics2D newG2d = newImage.createGraphics();

  141.         // 绘制底层图像
  142.         newG2d.drawImage(baseImage, 0, 0, null);

  143.         // 计算图例的位置(右下角)
  144.         int overlayX = width - legendImage.getWidth() - 50;
  145.         int overlayY = height - legendImage.getHeight() - 115;

  146.         // 绘制图例图像
  147.         newG2d.drawImage(legendImage, overlayX, overlayY, null);

  148.         newG2d.dispose();
  149.         return newImage;
  150.     }

  151.     /**
  152.      * 图片图例
  153.      * @param values 图例范围数据
  154.      * @param colorStrArr 颜色RGB码值
  155.      * @return 图例
  156.      */
  157.     public LegendScheme createLegendScheme(double[] values, String[] colorStrArr, double missValue) {
  158.         // 创建图例
  159.         Color[] colors = Arrays.stream(colorStrArr)
  160.                 .map(str -> Color.decode(String.valueOf(Integer.parseInt(str, 16))))
  161.                 .toArray(Color[]::new);

  162.         List<Color> newList = new ArrayList<>();
  163.         for (Color color : colors) {
  164.             if (color.getRed() == 255 && color.getGreen() == 255 && color.getBlue() == 255) {
  165.                 newList.add(new Color(color.getRed(), color.getGreen(), color.getBlue(), 0));
  166.             } else {
  167.                 newList.add(new Color(color.getRed(), color.getGreen(), color.getBlue()));
  168.             }
  169.         }
  170.         Color[] newColor = newList.toArray(new Color[0]);

  171.         double minData = values[0];
  172.         double maxData = values[values.length - 1];

  173.         // 渐变值
  174.         return LegendManage.createGraduatedLegendScheme(values, newColor, ShapeTypes.POLYGON, minData, maxData, false, missValue);
  175.     }

  176.     /**
  177.      * 创建地图
  178.      * @param mapPath 地图路径
  179.      * @return 地图layer
  180.      * @throws Exception
  181.      */
  182.     public VectorLayer createMapLayer(String mapPath) throws Exception {
  183.         // 读取地图
  184.         VectorLayer map = MapDataManage.readMapFile_ShapeFile(mapPath);

  185.         // 描述地图边界线
  186.         PolygonBreak pb = (PolygonBreak) map.getLegendScheme().getLegendBreak(0);
  187.         // 是否设置填充
  188.         pb.setDrawFill(false);
  189.         // 设置轮廓大小
  190.         pb.setOutlineSize(2f);
  191.         // 设置轮廓颜色
  192.         pb.setOutlineColor(Color.black);

  193.         return map;
  194.     }

  195.     /**
  196.      * 设置图例
  197.      * @param layout 视图
  198.      * @param layer 图层
  199.      * @param height 图例高度
  200.      */
  201.     public void setLegend(MapLayout layout, VectorLayer layer, int height) {
  202.         // 设置图例
  203.         Rectangle bounds = layout.getActiveMapFrame().getLayoutBounds();
  204.         LayoutLegend legend = layout.addLegend(bounds.x + 150, bounds.y + bounds.height + 40);
  205.         legend.setLegendStyle(LegendStyles.BAR_HORIZONTAL);
  206.         legend.setHeight(30);
  207.         legend.setWidth(bounds.width - 150);
  208.         legend.setLegendLayer(layer);
  209.     }

  210.     private void checkCityName(VectorLayer nameMap) {
  211.         AttributeTable attributeTable = nameMap.getAttributeTable();
  212.         DataTable table = attributeTable.getTable();
  213.         int rowCount = table.getRowCount();
  214.         for (int i = 0; i < rowCount; i++) {
  215.             Object oldNameObj = table.getValue(i, "NAME");
  216.             String oldName = String.valueOf(oldNameObj);
  217.             if (oldName.contains("aaa")) {
  218.                 table.setValue(i, "NAME", "雄安新区");
  219.             }
  220.         }

  221.         attributeTable.setTable(table);
  222.         nameMap.setAttributeTable(attributeTable);

  223.         LabelSet ls = new LabelSet();
  224.         ls.setFieldName("NAME");
  225.         ls.setLabelFont(new Font("黑体", Font.PLAIN, 18));
  226.         nameMap.setLabelSet(ls);
  227.         nameMap.addLabels();
  228.     }

  229.     private void checkCountyName(VectorLayer nameMap) {
  230.         AttributeTable attributeTable = nameMap.getAttributeTable();
  231.         DataTable table = attributeTable.getTable();
  232.         int rowCount = table.getRowCount();
  233.         for (int i = 0; i < rowCount; i++) {
  234.             Object oldNameObj = table.getValue(i, "XZQMC");
  235.             String oldName = String.valueOf(oldNameObj);
  236.             String newName = oldName.replace("满族自治县", "")
  237.                     .replace("回族自治县", "")
  238.                     .replace("蒙古族自治县", "")
  239.                     .replace("鹰手营子矿区", "")
  240.                     .replace("井陉矿区", "")
  241.                     .replace("渤海新区", "")
  242.                     .replace("芦台县", "")
  243.                     .replace("滦 县", "")
  244.                     .replace("汉沽管理区", "");
  245.             table.setValue(i, "XZQMC", newName);
  246.         }
  247.         attributeTable.setTable(table);
  248.         nameMap.setAttributeTable(attributeTable);

  249.         LabelSet ls = new LabelSet();
  250.         ls.setFieldName("XZQMC");
  251.         ls.setLabelFont(new Font("宋体", Font.PLAIN, 10));
  252.         ls.setLabelColor(Color.GRAY);
  253.         nameMap.setLabelSet(ls);
  254.         nameMap.addLabels();
  255.     }

  256.     /**
  257.      * 添加居中文字
  258.      * @param g2d 图片
  259.      * @param content 文字内容
  260.      * @param color 颜色
  261.      * @param font 字体
  262.      * @param width 横向宽度
  263.      * @param y y的位置
  264.      */
  265.     private void addCenterContent(Graphics2D g2d, String content, Color color, Font font, int width, int y) {
  266.         g2d.setFont(font);
  267.         g2d.setColor(color);
  268.         FontMetrics fm = g2d.getFontMetrics();
  269.         // 设置文字位置和内容
  270.         int x = width / 2 - fm.stringWidth(content) / 2;
  271.         // 绘制文字
  272.         g2d.drawString(content, x, y);
  273.     }

  274.     /**
  275.      * 添加文字
  276.      * @param g2d 图片
  277.      * @param content 文字内容
  278.      * @param color 颜色
  279.      * @param font 字体
  280.      * @param x x的位置
  281.      * @param y y的位置
  282.      */
  283.     private void addContent(Graphics2D g2d, String content, Color color, Font font, int x, int y) {
  284.         g2d.setFont(font);
  285.         g2d.setColor(color);
  286.         // 绘制文字
  287.         g2d.drawString(content, x, y);
  288.     }

  289.     /**
  290.      * 获取中文字体
  291.      * @return
  292.      */
  293.     private Font getChineseFont(int size){
  294.         return new Font("宋体", Font.PLAIN, size);
  295.     }

  296. }
复制代码

密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

 楼主| 发表于 7 天前 | 显示全部楼层
山水美不美 发表于 2025-8-23 17:23
可以检查下离散点的降水插值后的结果,是不是极值点的雨量值小于100了,插值结果有可能与极值点的值出现偏 ...

使用GridData格点数据进行绘图的时候,是否也会再内置调用插值算法,然后再进行绘图
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

发表于 7 天前 | 显示全部楼层
zhangbote 发表于 2025-8-25 09:08
使用GridData格点数据进行绘图的时候,是否也会再内置调用插值算法,然后再进行绘图

插值是单独调用的吧,已经是网格数据,不会再内插值。没有研究过王老师的代码,我猜的,因为我自己写等值线(面)追踪功能,插值是分开的。不过,你可以问问王老师。
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

 楼主| 发表于 7 天前 | 显示全部楼层
山水美不美 发表于 2025-8-25 09:54
插值是单独调用的吧,已经是网格数据,不会再内插值。没有研究过王老师的代码,我猜的,因为我自己写等值 ...

好的,感谢。
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

发表于 7 天前 | 显示全部楼层
zhangbote 发表于 2025-8-25 09:08
使用GridData格点数据进行绘图的时候,是否也会再内置调用插值算法,然后再进行绘图

格点数据不用再插值了
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

 楼主| 发表于 7 天前 | 显示全部楼层
MeteoInfo 发表于 2025-8-25 10:23
格点数据不用再插值了

还有疑问,创建图例的时候,要传入的最大、最小值应该如何定义呢?我一般都是直接取图例中的最小值和最大值。

创建图例的方法

创建图例的方法
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

 楼主| 发表于 5 天前 | 显示全部楼层
MeteoInfo 发表于 2025-8-25 10:23
格点数据不用再插值了

老师,还有一个问题。我使用一个json格点数据进行绘图,但是绘制的图和实际对比,出入较大,少很多点位。这个有办法解决吗?

对比图

对比图
1.json (1.9 MB, 下载次数: 1)
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Copyright ©2011-2014 bbs.06climate.com All Rights Reserved.  Powered by Discuz! (京ICP-10201084)

本站信息均由会员发表,不代表气象家园立场,禁止在本站发表与国家法律相抵触言论

快速回复 返回顶部 返回列表