目录
前言
在地理信息系统(GIS)领域,GeoTIFF 作为一种常见的地理空间数据格式,承载着丰富的地理信息数据,比如常见的人口数据、地形数据、夜间灯光数据等等,覆盖了从遥感影像到地形地貌数据,GeoTIFF 文件广泛应用于地图绘制、资源勘探、环境监测等众多领域。对这些 GeoTIFF 数据进行有效的分析和处理,是挖掘其潜在价值的关键环节。而在众多的数据分析需求中,确定 GeoTIFF 数据集的最大值和最小值是一项基础且重要的任务。
当我们面对一个 GeoTIFF 文件时,其中存储的地理要素的属性值范围对我们理解数据的分布特征、进行数据可视化以及开展后续的复杂分析都有着至关重要的意义。例如,在遥感影像分析中,不同地物类型的反射率或发射率值存在差异,通过获取影像数据的最大最小值,可以初步判断地物类型的分布情况和影像的质量状况;在地形分析中,高程数据的最大最小值能够直接反映出研究区域的海拔高差范围,为坡度、坡向等分析提供基础依据。
本文旨在深入探讨基于 GeoTools 求解 GeoTIFF 最大最小值的方法。首先介绍 GeoTIFF 格式的相关特性和 GeoTools 中处理 GeoTIFF 数据的关键类与方法。主要介绍利用QGIS软件来读取Tif文件的最大最小值,然后详细阐述如何利用 GeoTools 的读取、解析和计算功能来获取 GeoTIFF 数据中的最大值和最小值,并对整个过程进行分析和优化,以确保得到准确可靠的结果,为后续的地理信息分析和决策提供坚实的数据基础。
一、在QGIS中查看极值的三种方式
本节将重点介绍在QGIS中直接查看GeoTiff文件的最大最小值的三种方法,分别是在属性信息中tab页中查看,在符号化tab页中查看以及使用直方图的形式进行查看。当然,除了使用QGIS软件,还可以利用ArcMap或者ArcGIS的相关软件也是可以直接查看的。
1、属性信息查看
利用QGIS加载相应的tif数据之后,可以单击鼠标右键,在打开的信息窗口中可以直接查看,以属性信息栏窗口为例,如下图所示:
在这里,其实已经将一些极值的信息列了出来,供大家查阅。
STATISTICS_MAXIMUM=2095
STATISTICS_MEAN=355.64031618431
STATISTICS_MINIMUM=-35
STATISTICS_STDDEV=297.91063586873
STATISTICS_VALID_PERCENT=64.56
可以看到,使用属性信息窗口可以在波段信息中看到极值信息。
2、在符号化中查看
除了在基本信息中可以直接的查看极值信息之外,在标绘时也是可以比较直观的进行查看的。在QGIS中切换到符号化的tab页,同样的波段信息下面可以看到当前tif的最大最小值,如下图所示:
通过符号化窗口看到的最大值是2095,最小值是-35;这个值与上一小节的属性信息中看到的极值是一致的的。
3、直方图统计
前面介绍了两种查看tif文件极值的方法,接着介绍最后一种基于直方图的统计方法。在属性页面,与矢量数据的展示不一样的是还有一个直方图的统计页面,点击直方图tab页,会打开如下界面:
同样的,在这里,我们依然可以看得到栅格的直方图统计信息,可以很直观的在底部可以看到当前的tif文件对应的最大值和最小值,可以看到这个最大值最小值与前面得到的值都是一致的。
二、GeoTools读取极值
GeoTools 作为一款功能强大的开源 GIS 工具库,为开发者提供了丰富的地理数据处理和分析功能。它具备良好的扩展性和灵活性,能够方便地与各种 GIS 数据格式进行交互,并且提供了高效的算法来处理地理空间数据。因此,借助 GeoTools 来求解 GeoTIFF 的最大最小值,不仅可以提高数据处理的效率和准确性,还能充分利用其现有的功能模块,减少重复开发的工作量,使开发者能够将更多的精力集中在数据分析和应用的深层次领域。本节分享两种使用Geotools来统计tif极值的方法,分别是采用直接循环遍历法和基于CoverageClassStats的统计方法。
1、直接循环遍历法
第一种方法比较简单,就是采用循环读取的方式,使用Geotools中的读取栅格坐标点的信息来进行循环读取,大致的代码如下:
File file = new File("F:/vector_data/worldPop人口数据/worldpop/chn_ppp_2020_1km_Aggregated.tif");
GeoTiffReader reader = new GeoTiffReader(file, new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.TRUE));
GridCoverage2D coverage = reader.read(null);
// 设置目标坐标系并重采样
CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:4326");
coverage = (GridCoverage2D) Operations.DEFAULT.resample(coverage, targetCRS);
RenderedImage image = coverage.getRenderedImage();
GridGeometry2D geometry = coverage.getGridGeometry();
// 获取栅格数据并批量读取像素值
Raster raster = image.getData();
width = raster.getWidth();
int height = raster.getHeight();
double[] pixels = raster.getSamples(raster.getMinX(), raster.getMinY(), width, height, 0, (double[]) null);
// 收集非零像素的坐标
List<GridCoordinates2D> nonZeroCoords = new ArrayList<>();
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
if (pixels[j * width + i] > 0) {
nonZeroCoords.add(new GridCoordinates2D(i, j));
}
}
}
// 获取仿射变换以手动计算坐标
MathTransform2D gridToCRS = geometry.getGridToCRS2D();
if (!(gridToCRS instanceof AffineTransform2D)) {
throw new RuntimeException("仅支持仿射变换");
}
AffineTransform2D affine = (AffineTransform2D) gridToCRS;
BigDecimal total = new BigDecimal("0.0");
double t = 0;
// 处理非零像素
for (GridCoordinates2D coord : nonZeroCoords) {
int i = coord.x;
int j = coord.y;
double[] src = new double[]{i + 0.5, j + 0.5}; // 像素中心坐标
double[] dest = new double[2];
affine.transform(src, 0, dest, 0, 1);
double longitude = dest[0];
double latitude = dest[1];
/*System.out.printf("像素点 (%d, %d) 的值:%f 坐标为: %f, %f%n",
i, j, pixels[j * width + i], longitude, latitude);*/
total = total.add(new BigDecimal(pixels[j * width + i]));
t += pixels[j * width + i];
}
我们可以从栅格像素点中去读取对应的值,比如人口和海拔高程等。然后将得到的数据保存到集合中,最后使用集合排序,然后取出两个极值即为我们需要求解的目标。这种方法在范围较小的情况下,可以如此使用,大面积不建议这么使用,可能你的内存都会受不了。
2、基于CoverageClassStats读取
与直接循环遍历方法不一样的是,在前面的QGIS软件展示极值信息时。我们曾简单的提起波段的属性信息。因此我们是否可以采取类似的方法来加速呢?这里分享一种方法,代码如下:
package com.yelang.project.geotoolstiff;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.GridFormatFinder;
import org.geotools.gce.geotiff.GeoTiffFormat;
import org.geotools.gce.geotiff.GeoTiffReader;
import org.geotools.process.ProcessException;
import org.geotools.process.classify.ClassificationMethod;
import org.geotools.process.raster.CoverageClassStats;
import org.geotools.util.factory.Hints;
import org.jaitools.numeric.Statistic;
public class DemMinMaxStat {
public static void main(String[] args) throws ProcessException, IOException {
/**
* 湖南地形 min :-35 max:2095
*/
File file = new File("C:/BaiduDownload/湖南省_DEM_30m分辨率_NASA数据.tif");
/**
* 全国人口 min :-99999.0 max:286414.71875
*/
//File file = new File("F:/vector_data/worldPop人口数据/worldpop/chn_ppp_2020_1km_Aggregated.tif");
AbstractGridFormat format = GridFormatFinder.findFormat(file);
if (!(format instanceof GeoTiffFormat)) {
throw new RuntimeException(String.format("传入文件不是geoTif格式文件"));
}
Hints hints = new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.TRUE);
AbstractGridCoverage2DReader reader = format.getReader(file, hints);
GridCoverage2D coverage = reader.read(null);
CoverageClassStats rasterProcess = new CoverageClassStats();
Set<Statistic> set = new HashSet<Statistic>(2);
//最大值
set.add(Statistic.MAX);
//最小值
set.add(Statistic.MIN);
CoverageClassStats.Results results = rasterProcess.execute(coverage, set, 0, 1,
ClassificationMethod.QUANTILE, null, null);
System.out.println("geometry = " + results);
double maxVal = results.value(0, Statistic.MAX);
double minVal = results.value(0, Statistic.MIN);
System.out.println("Minimum value: " + minVal);
System.out.println("Maximum value: " + maxVal);
}
}
在IDE中运行以上方法后可以在控制台中直接看到我们的极值效果,如下所示:
通过程序运行可以看到,使用Geotools程序同样可以基于CoverageClassStats进行数据的极值采集,采集的值与使用Qgis软件提供的值完全一致,说明结果是正确的。
三、总结
以上就是本文的主要内容, 本文旨在深入探讨基于 GeoTools 求解 GeoTIFF 最大最小值的方法。首先介绍 GeoTIFF 格式的相关特性和 GeoTools 中处理 GeoTIFF 数据的关键类与方法。主要介绍利用QGIS软件来读取Tif文件的最大最小值,然后详细阐述如何利用 GeoTools 的读取、解析和计算功能来获取 GeoTIFF 数据中的最大值和最小值,并对整个过程进行分析和优化,以确保得到准确可靠的结果,为后续的地理信息分析和决策提供坚实的数据基础。行文仓促,定有不足之处,欢迎各位朋友在评论区批评指正,不胜感激。