一、栅格数据概述
对于地理空间数据而言,GIS有两大基本存储模型,一种是矢量数据模型,一种是栅格数据模型。栅格数据模型与矢量数据模型是地理信息系统中空间数据组织的两种最基本的方式。
同样信息的表达,在矢量数据模型中,我们看到的是清晰的点、线、面的实体,来表达河流、湖泊、地块这样的信息。而在栅格数据模型中,我们看到的则是一个个的格子,相同的像元值在地图上展示出相同的颜色,从而也呈现出河流、湖泊、地块的形态。
虽然都能表达出一样的信息,但是这两种存储模型是完全不同,矢量是以对象为单位,我们可以把一个湖泊的面积等属性都存储在该对象中;而用栅格表达的话,湖泊是由一组像元组成的,我们不可能将整个湖泊的面积分别赋予每个像元。另外,栅格数据是以二维矩阵(行和列或格网)的形式来表示空间地物或现象分布的数据组织方式,每个矩阵单位称为一个栅格单元(cell), 每个像元都包含一个信息值(例如温度)。栅格的每个数据表示地物或现象的属性数据.因此栅格数据有属性明显,定位隐含的特点。而矢量数据结构是利用点,线,面的形式来表达现实世界,具有定位明显,属性隐含的特点。
现实世界的哪些现象是可以用栅格数据模型来管理呢?概况起来,主要是以下三类:
1、专题数据(也称为离散数据)表示土地利用或土壤数据等要素。
2、连续数据表示温度、高程或光谱数据(例如,卫星影像或航空像片)等现象。
3、图片则包括扫描的地图或绘图,以及建筑物照片。
1、栅格数据用途
尽管栅格数据的结构很简单,但它在各种应用中都极为重要。在 GIS 中,栅格数据一般在以下四种情况下使用:
1.1 将栅格用作底图
在 GIS的应用中,将影像作为基础底图是常见的使用方式,在影像基础底图上叠加专题图层,可以让我们更加准确的知道专题数据的位置是不是正确的(准确来说应该是:数据在空间上跟影像所表述的地物是不是对齐了)。我们可以把正射航空摄影、正射卫星影像和正射扫描地图用栅格数据模型来存储,简称为栅格底图。
1.2 将栅格用作表面地图
栅格非常适合用来表达那些沿地表连续变化的数据。譬如说高程数据是表面地图常见的使用方式,当然我们也可以将降雨量、温度、密度和人口密度等连续变化的数据,用栅格来表达。上面的栅格图是一份DEM数据,其中使用绿色的区域表示地势比较低的地区,红色、粉红色和白色像元则地势比较高的地区。
备注:数字高程模型(Digital Elevation Model),简称DEM,属于连续表面的栅格制图表达,要将真实的地球表面进行数字化,最常见的是采用DEM模型。
1.3 将栅格用作主题地图
随着你接入越来越多的GIS项目,特别是涉及到国土、测绘、规划等部门的业务系统的时候,土地利用现状图是绕不过去的专业名词。像上面的这张土地利用现状分类图包含了农田、草地、水域等地物,也就是说这块地是干什么用的,是种树、种田还是搞了水利设施,又或者是盖了房子,调查这些地块的用途所做出来的专题地图就叫土地利用现状图。它要表达的是土地资源的利用现状、地域差异和分类。
像这类的专题图,可以用栅格来表达。一般来说,要获取这种表达主题数据的栅格,我们可以通过分析其他数据来获得,譬如可以把多光谱数据进行数据解译后,划分成各个类别的数据(植被、水系、道路等等),这些数据就可以用栅格来组织管理
1.4 将栅格用作要素的属性
看到【将栅格用作要素的属性】这个标题,你可以会一头雾水。确实也很少这么总结过,但其实或多或少我们都这么干过。简单来说就是,我们可以数字照片、扫描的文档或扫描的绘图作为地理对象的属性。譬如上面这张古树图片,我们就可以把它作为古树图层的一个要素属性,也就是说,这个位置有一颗古树,这个古树长的什么样,那就看这张照片。这张图片我们就可以用栅格来存储管理。
2、栅格数据模型的优势
其实除了影像数据必须要以栅格进行管理之外,其他的要素(例如点要素)和测量值(例如降雨量)既可以存储为栅格数据类型也可以存储为矢量数据类型。那为什么还需要把数据存储为栅格呢?这就要说到栅格数据的优势了:
-
栅格数据结构更加简单,即由像元组成矩阵结构,其中的像元值表示坐标,有时与属性表相关联
-
可进行高级的空间和统计分析,ArcGIS提供了一个空间分析扩展模块专门针对栅格数进行地理处理和分析
-
可以表示连续表面以及执行表面分析
-
点、线、面和表面都可同样存储
-
对复杂数据集也可执行快速叠置
3、栅格数据模型的局限性
- 由于栅格数据集的像元尺寸具有局限性,所以可能会带来空间误差。
- 栅格数据集可能会非常大。虽然分辨率会随着栅格像元大小的减小而提高,但这会占用更多的磁盘空间,而且会拖慢处理速度。对于给定区域,将栅格像元大小更改为现有大小的一半时,所需的存储空间会增大为原来的四倍,具体情况取决于所使用的数据类型和存储技术。
- 将数据重建到固定间距的栅格像元边界时也会损失一定的精度。
- 如果要做地物的空间定位,矢量数据模型更加适合。
二、栅格数据操作
GeoTools对于栅格数据的支持主要又由GridCoverage实现的。作为一名程序员,我们习惯于处理诸如JPEG、GIF或者PNG等格式的栅格数据。在地理空间方面,有一个Coverage个概念,他是空间定位要素的集合。非正式地,我们将地图和Coverage视为等同,当然,这是相对于地理意思而非编程思维上。
1、栅格数据读取
public static Coverage readTiff(String tiffPath) throws IOException {
File f = new File(tiffPath);
ParameterValue<OverviewPolicy> policy = AbstractGridFormat.OVERVIEW_POLICY.createValue();
policy.setValue(OverviewPolicy.IGNORE);
ParameterValue<String> gridsize = AbstractGridFormat.SUGGESTED_TILE_SIZE.createValue();
ParameterValue<Boolean> useJaiRead = AbstractGridFormat.USE_JAI_IMAGEREAD.createValue();
useJaiRead.setValue(true);
GridCoverage2D image = new GeoTiffReader(f).read(new GeneralParameterValue[]{policy, gridsize, useJaiRead});
return image;
}
2、栅格数据的生成
File file = new File(outTiffPath);
GeoTiffWriter geoTiffWriter = new GeoTiffWriter(file);
final GeoTiffFormat format = new GeoTiffFormat();
final GeoTiffWriteParams wp = new GeoTiffWriteParams();
// 设置写出参数
wp.setCompressionMode(GeoTiffWriteParams.MODE_DEFAULT);
wp.setTilingMode(GeoToolsWriteParams.MODE_DEFAULT);
ParameterValueGroup paramWrite = format.getWriteParameters();
paramWrite.parameter(AbstractGridFormat.GEOTOOLS_WRITE_PARAMS.getName().toString()).setValue(wp);
geoTiffWriter.write((GridCoverage) coverage, paramWrite.values().toArray(new GeneralParameterValue[4]) );
geoTiffWriter.dispose();
3、栅格数据的加载
AbstractGridFormat format = GridFormatFinder.findFormat(rasterFile);
// this is a bit hacky but does make more geotiffs work
Hints hints = new Hints();
if (format instanceof GeoTiffFormat) {
hints = new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.TRUE);
}
reader = format.getReader(rasterFile, hints);
4、波段提取
GridCoverage2D cov = reader.read(null);
int numBands = cov.getNumSampleDimensions();
for (int i = 0; i < numBands; i++) {
GridSampleDimension dim = cov.getSampleDimension(i);
//这里依次输出 RED_BAND GREEN_BANK BLUE_BANK
System.out.println(dim.getDescription().toString());
}
5、样式创建
5.1 创建灰度的样式
private Style createGreyscaleStyle(int band) {
StyleFactory sf = CommonFactoryFinder.getStyleFactory();
FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
ContrastEnhancement ce = sf.contrastEnhancement(ff.literal(1.0), ContrastMethod.NORMALIZE);
SelectedChannelType sct = sf.createSelectedChannelType(String.valueOf(band), ce);
RasterSymbolizer sym = sf.getDefaultRasterSymbolizer();
ChannelSelection sel = sf.channelSelection(sct);
sym.setChannelSelection(sel);
return SLD.wrapSymbolizers(sym);
}
5.2 创建RGB样式
StyleFactory sf = CommonFactoryFinder.getStyleFactory();
FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
SelectedChannelType[] sct = new SelectedChannelType[cov.getNumSampleDimensions()];
ContrastEnhancement ce = sf.contrastEnhancement(ff.literal(1.0), ContrastMethod.NORMALIZE);
for (int i = 0; i < 3; i++) {
sct[i] = sf.createSelectedChannelType(String.valueOf(channelNum[i]), ce);
}
RasterSymbolizer sym = sf.getDefaultRasterSymbolizer();
ChannelSelection sel = sf.channelSelection(sct[RED], sct[GREEN], sct[BLUE]);
sym.setChannelSelection(sel);
return SLD.wrapSymbolizers(sym);