一、准备工作
1. GridData类 —— 气象数据
- 通过 GridData 算法,可以将不规则分布的数据点插值到规则网格上,生成平滑连续的数据表面。
- GridData 插值方法的选择会影响插值结果的平滑度和精确度。例如,线性插值 (linear) 会生成折线连接的曲面,而三次插值 (cubic) 会生成更平滑的曲面。最邻近插值 (nearest) 则简单地将查询点的值设为最近数据点的值。
import org.meteoinfo.data.GridData;
/**
* double[][] datas:气象数据点
* double[] lons: 经度数组
* double[] lats: 纬度数组
*/
GridDatas grid = new GridData(datas, lons, lats);
2. VectorLayer类 —— 矢量地理数据图层
- VectorLayer 是 OpenLayers 中用于表示矢量地理数据的图层,它允许用户在地图上加载和展示点、线、多边形等几何对象,这些对象可以描述地理空间中的实体,例如道路、建筑物、行政区划等。
- VectorLayer 类提供了详细的配置选项,包括但不限于设置图层的 CSS 类名、不透明度、可见性、渲染范围、Z 索引、最小和最大分辨率、最小和最大缩放级别、渲染顺序、渲染缓冲区、数据源、是否作为地图的覆盖层、是否启用 declutter 以及样式设置等。
import org.meteoinfo.geo.layer.VectorLayer;
/**
* String shpPath:存放shp文件的路径
*/
VectorLayer scmap = MapDataManage.readMapFile_ShapeFile(shpPath);
// 获取第一个图例分级颜色的断点
PolygonBreak pd = (PolygonBreak) scmap.getLegendScheme().getLegendBreak(0);
// 是否设置填充(当气象数据小于图例分级起点时,图层不填充颜色)
pd.setDrawFill(false);
pd.setOutlineColor(Color.BLACK); // 设置轮廓颜色
pd.setDrawOutline(true); // 设置是否绘制多边形的轮廓线
pd.setOutlineSize(1);
3. ImageBuilderConf类(自建)——图片配置信息
- 新建了一个类ImageBuilderConf,用以存储各类的图片配置信息。
public class ImageBuilderConf {
private Color[] colors; // 图例色阶颜色范围
private double[] levels; // 图例色阶数值范围
private int mapLevel; // 图例色阶数值个数
private String logoFilePath;
private String companyName;
private String title;
private String subTitle;
private float minLon;
private float minLat;
private float maxLon;
private float maxLat;
private String intersectFilePath;
private String jsonFilePath;
private String mark;
private boolean isColorMatch = false;
}
准备工作就位,下面开始用meteoinfo包来绘制色斑图了。
二、绘制色斑图
1. 方法 createGraduatedLegendScheme() —— 构建图例
- 包名:org.meteoinfo.geo.legend
- 作用:根据数据范围、颜色数组、形状类型和其他参数来构建分级图例方案。
- 返回值:LegendScheme 图例方案
- 参数:
参数类型 | 参数名 | 描述 |
---|---|---|
double[] | CValues | 图例数值断点范围 |
Color[] | colors | 图例色阶范围 |
ShapeTypes | aST | 形状类型:点(Point)、线(Polyline)、多边形(Polygon) 等 |
double | min | 数值区间的最小值 |
double | max | 数值区间的最大值 |
Boolean | hasNodata | 用于指示数据集中是否存在无效数据(nodata) |
double | unDef | 用来表示那些没有有效数值的数据点 |
2. 方法 createShadedLayer() —— 绘制阴影图层
- 包名:org.meteoinfo.geo.meteodata
- 作用:创建一个带有阴影效果的矢量图层。
- 返回值:VectorLayer 矢量图层
- 参数:
参数类型 | 参数名 | 描述 |
---|---|---|
GridData | gridData | 气象数据 |
LegendScheme | aLS | 图例方案 |
String | lName | 创建的矢量图层名称 |
String | fieldName | 指定了 gridData 中的哪个字段数据将被用于图层渲染 |
boolean | isSmooth | 是否应用平滑算法 |
-
疑问:lName、fieldName在该方法的运用逻辑?在参考案例(链接)中直接传递了空字符串。
-
解答:
lName 用于给生成的阴影图层命名,用来标识图层。在实际应用中,这个参数可以直接传递空字符串,因为它主要用于标识图层,有时候在代码中不需要特别指定
fieldName 图例方案里的字段名,在气象数据中,可能有多个变量(如温度、湿度等),这个参数就是用来选择其中一个变量来显示。如果仅有一个变量,不需要进行显示区分(图例区分),那么也可以直接传递空字符串。 -
代码:
VectorLayer aLayer = new VectorLayer(ShapeTypes.POLYGON);
aLayer.setLayerName(lName);//图层名
ls.setFieldName(fieldName + “_Low”);
aLayer.setLegendScheme(ls);//图例字段名
3. 方法 clip() —— 剪裁图层
- 包名:org.meteoinfo.geo.layer
- 作用:对矢量图层进行裁剪操作。
- 返回值:VectorLayer 剪裁后的矢量图层
- 参数:
参数类型 | 参数名 | 描述 |
---|---|---|
VectorLayer | clipLayer | 剪裁的原型图层 |
4. 自建方法
4.1 getMaxMinValue()
/**
* 获取包括图例数值在内的气象数据的最大值、最小值
*/
public static double[] getMaxMinValue(GridData grid, ImageBuilderConf conf)
{
//读取色阶数值范围
double[] values = conf.getLevels();
double[] maxMin = new double[2];
//用于获取栅格数据的最大值和最小值
if (!grid.getMaxMinValue(maxMin)) {
// 色阶数值范围起点 如果小于 栅格数据的最小值
if (conf.getLevels()[0] < maxMin[1]) {
maxMin[1] = values[0];
}
// 色阶数值范围终点 如果大于 栅格数据的最大值
if (values[values.length - 1] > maxMin[0]) {
maxMin[0] = values[values.length - 1];
}
}
return maxMin;
}
4.1 getMapLayout()
/**
* 创建布局
*/
public static MapLayout getMapLayout(MapView view) {
//视图设置
MapLayout layout = new MapLayout();
//是否在地图框架周围绘制边框线
layout.getActiveMapFrame().setDrawNeatLine(true);
//抗锯齿
layout.getActiveMapFrame().setMapView(view);
layout.setAntiAlias(true);
return layout;
}
4.1 getMapView()
/**
* 创建视图
*/
public static MapView getMapView(VectorLayer scmap, VectorLayer layer) {
MapView view = new MapView();
//叠加图层
view.addLayer(layer);
view.addLayer(scmap);
//抗锯齿
view.setAntiAlias(true);
return view;
}
5. 图片透明化(放最后了)
6. 参考代码
/**
* @Param grid 气象数据
* @Param vectorLayer 矢量图层
* @Param outPath 图片输出路径
* @Param conf 图片配置信息
*/
public void makeSpeckleImage(GridData grid, VectorLayer scmap, String outPath, ImageBuilderConf conf)
{
//读取色阶数值断点范围
double[] values = conf.getLevels();
double minData = 0;
double maxData = 0;
double[] maxMin = getMaxMinValue(grid, conf);
maxData = maxMin[0];
minData = maxMin[1];
//1.用于创建一个渐变色图例方案
LegendScheme als = LegendManage.createGraduatedLegendScheme(values, conf.getColors(), ShapeTypes.POLYGON, minData, maxData, false, grid.getDoubleMissingValue());
//2.绘制图层
VectorLayer layer = DrawMeteoData.createShadedLayer(grid, als, lName, "name", true);
//3.叠加图层,剪裁到合适地区
layer = layer.clip(scmap);
//创建视图和布局
MapView view = getMapView(scmap, layer); //创建视图,添加底层地图shp文件和气象色斑图图层
MapLayout layout = getMapLayout(view); //创建布局,设置页面大小、图层排列等
//返回视图的地理边界范围
Extent extent = view.getExtent();
int size = 1000;
//设置布局的宽高比例,保持视图的纵横比
Rectangle rectangle = new Rectangle(size, (int) (size * 1D / extent.getWidth() * extent.getHeight()));
//设置布局区域大小和外边距
int width = rectangle.width;
int height = rectangle.height;
int left = 10; int right = 250; int top = 15; int bottom = 200;
//设置页面边界
layout.setPageBounds(new Rectangle(0, 0, width + left + right, height + top + bottom));
//获取地图框,设置布局边界
MapFrame frame = layout.getActiveMapFrame();
frame.setLayoutBounds(new Rectangle(left, top, width, height));
//设置图例
Rectangle bounds = layout.getActiveMapFrame().getLayoutBounds();
LayoutLegend legend = layout.addLegend(bounds.x + bounds.width, 0);
legend.setLegendStyle(LegendStyles.NORMAL);
legend.setLegendLayer(layer);
//导出
layout.exportToPicture(outPath);
}
三、扩充内容
知识点1:图例类型
- 在 MeteoInfo 的文档中,图例类型有三种:单一符号(Single Symbol)、唯一值(Unique Value)和颜色分级(Graduated Color)。其中,单一符号图例会使图层中的所有空间要素使用统一的颜色或符号显示。而唯一值和颜色分级图例则依赖于图层属性数据,需要选择属性字段来区分不同的符号或颜色
知识点2:透明化图片
- ARGB颜色值是通过一个32位整数来表示的,其中每个8位(即一个字节)分别代表红色、绿色和蓝色分量,以及可选的 alpha(透明度)分量。
- int R = (rgb & 0xff0000) >> 16;
通过&( 按位与操作符 )选取 rgb 值中的红色分量,再通过>>( 右移操作符 )直接获取到原始颜色值中的红色分量。 - rgb = (alpha << 24) | (rgb & 0x00ffffff);
将修改后的透明度分量放回 rgb 值的最高位,同时保留原有的 RGB 分量。
public static void markTransparentImages(String readPath, String writePath) {
File file = new File(readPath);
BufferedImage bi = ImageIO.read(new File(readPath));
//类型转换
BufferedImage img = new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D) img.getGraphics();
g.drawImage(bi, null, 0, 0);
int alpha = 0;
for (int i = img.getMinY(); i < img.getHeight(); i++) {
for (int j = img.getMinX(); j < img.getWidth(); j++) {
int rgb = img.getRGB(j, i);
//透明部分不需要处理
if (rgb < 0) {
int R = (rgb & 0xff0000) >> 16;
int G = (rgb & 0xff00) >> 8;
int B = (rgb & 0xff);
//将白色剔除
Color color = Color.WHITE;
if (color.getRed() == R && color.getGreen() == G && color.getBlue() == B) {
alpha = 0;
} else {
alpha = (int) (alpha * 0.5);
}
rgb = (alpha << 24) | (rgb & 0x00ffffff);
img.setRGB(j, i, rgb);
}
}
}
//释放资源
g.dispose();
ImageIO.write(img, "png", new File(writePath));
file.delete();
}
四、参考链接
1. MeteoInfo-Java解析与绘图教程(四)
链接:https://www.cnblogs.com/zdsgjh/p/15237517.html
2. 可视化工具 MeteoInfoMap 中文文档
链接:http://meteothink.org/docs/meteoinfomap/desktop_cn/index.html