GeoTools入门(七)-- 操作栅格数据

1. 概述

在前面的章节,我们主要讲述了GeoTools对于矢量(主要是Shape)数据的操作。在地理信息系统的世界里,还有一类很重要的数据类型,那便是栅格数据。虽然GeoTools对于栅格数据的支持并没有gdal强大,但既然他作为GeoTools的一部分,我们还是又必要了解一下它。

2. GridCoverage

GeoTools对于栅格数据的支持主要又由GridCoverage实现的。作为一名程序员,我们习惯于处理诸如JPEG、GIF或者PNG等格式的栅格数据。在地理空间方面,有一个Coverage个概念,他是空间定位要素的集合。非正式地,我们将地图和Coverage视为等同,当然,这是相对于地理意思而非编程思维上。

GridCoverage是Coverage的一个特例,他要求栅格要以矩形的形式填充到Coverage的区域中。在我们的java代码中,我们可以使用位图图形作为GridCoverage的后台数据结构,并使用其他元素记录特定坐标系中的空间边界。

这里有许多种GridCoverage的文件格式,最常用的如一下三类。

  • World+ 图像

    一种普通的图像格式,如jpeg或png,它有一个sidecar 文件来描述它的位置,还有一个prjsidecar文件来定义地图投影,就像shapefile使用的那样。请注意,尽管jpeg格式由于下载量小而很常见;运行时的性能非常糟糕,因为整个图像都需要读入内存。TIFF等格式没有此限制。

  • GeoTiff

    具有存储在图像元数据字段中的地理空间信息的普通tiff图像。这通常在安全的前提下表现出良好的性能;特别是如果它已经准备了一个内部覆盖(可用于缩小)或内部平铺(允许快速平移时放大)。当计算机的磁盘速度比CPU快时,性能最佳。

  • JPEG2000

    他jpeg的扩展,使用小波压缩来处理大量图像。文件格式还支持可用于存储地理空间信息的元数据字段。当您CPU速度比磁盘访问更快的情况下,这种格式的性能最好。

针对上面三种数据个数的描述,我们知道,第一种数据格式基本是被摒弃的那种。GeoTiff和JPEG2000的区别是要看IO性能VS CPU性能。

3. GridCoverage对栅格数据的操作

在本篇文章中,我们主要关注GridCoverage对于栅格数据的加载,波段提取,显示样式的创建。

3.1 栅格数据的加载

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);

3.2 波段提取

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());
}

3.3 样式创建

3.3.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);
}
3.3.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);

通过上面的例子,我们可以简单画个时序图及流程图方便大家记忆:

CommonFactoryFinder StyleFactory FilterFactory2 ContrastEnhancement SelectedChannelType RasterSymbolizer ChannelSelection SLD Style 1.getStyleFactory 2.getFilterFactory2 3.contrastEnhancement(CommonFactoryFinder) 4.createSelectedChannelType 5.getDefaultRasterSymbolizer 6.channelSelection(SelectedChannelType) 7.setChannelSelection(ChannelSelection) 7.wrapSymbolizers(RasterSymbolizer) CommonFactoryFinder StyleFactory FilterFactory2 ContrastEnhancement SelectedChannelType RasterSymbolizer ChannelSelection SLD Style

4. 示例

前面的示例中,我们查看了如何读取和显示Shape文件。对于ImageLab.java,我们将通过显示一个三波段的全球卫星图像,并将其与shapefile中的国家边界进行叠加,将光栅数据添加到混合中。

4.1 pom引入

<dependencies>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-shapefile</artifactId>
        <version>${geotools.version}</version>
    </dependency>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-swing</artifactId>
        <version>${geotools.version}</version>
    </dependency>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-epsg-hsql</artifactId>
        <version>${geotools.version}</version>
    </dependency>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-geotiff</artifactId>
        <version>${geotools.version}</version>
    </dependency>
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-image</artifactId>
        <version>${geotools.version}</version>
    </dependency>
</dependencies>
<repositories>
    <repository>
        <id>osgeo</id>
        <name>OSGeo Release Repository</name>
        <url>https://repo.osgeo.org/repository/release/</url>
        <snapshots><enabled>false</enabled></snapshots>
        <releases><enabled>true</enabled></releases>
    </repository>
    <repository>
        <id>osgeo-snapshot</id>
        <name>OSGeo Snapshot Repository</name>
        <url>https://repo.osgeo.org/repository/snapshot/</url>
        <snapshots><enabled>true</enabled></snapshots>
        <releases><enabled>false</enabled></releases>
    </repository>
</repositories>

4.2 创建ImageLab类

public class ImageLab {
    private StyleFactory sf = CommonFactoryFinder.getStyleFactory();
    private FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
    private JMapFrame frame;
    private GridCoverage2DReader reader;

    public static void main(String[] args) throws Exception {
        ImageLab me = new ImageLab();
        me.getLayersAndDisplay();
    }
}

4.3 选择Image和shape文件

 private void getLayersAndDisplay() throws Exception {
     List<Parameter<?>> list = new ArrayList<>();
     list.add(
         new Parameter<>(
             "image",
             File.class,
             "Image",
             "GeoTiff or World+Image to display as basemap",
             new KVP(Parameter.EXT, "tif", Parameter.EXT, "jpg")));
     list.add(
         new Parameter<>(
             "shape",
             File.class,
             "Shapefile",
             "Shapefile contents to display",
             new KVP(Parameter.EXT, "shp")));

     JParameterListWizard wizard =
         new JParameterListWizard("Image Lab", "Fill in the following layers", list);
     int finish = wizard.showModalDialog();

     if (finish != JWizard.FINISH) {
         System.exit(0);
     }
     File imageFile = (File) wizard.getConnectionParameters().get("image");
     File shapeFile = (File) wizard.getConnectionParameters().get("shape");
     displayLayers(imageFile, shapeFile);
 }

4.4 显示地图

private void displayLayers(File rasterFile, File shpFile) throws Exception {
    AbstractGridFormat format = GridFormatFinder.findFormat(rasterFile);
    Hints hints = new Hints();
    if (format instanceof GeoTiffFormat) {
        hints = new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.TRUE);
    }
    reader = format.getReader(rasterFile, hints);

    // 创建一个灰度样式
    Style rasterStyle = createGreyscaleStyle(1);

    // 连接shape图层
    FileDataStore dataStore = FileDataStoreFinder.getDataStore(shpFile);
    SimpleFeatureSource shapefileSource = dataStore.getFeatureSource();

    //创建一个黄色边界且没有填充的简单样式
    Style shpStyle = SLD.createPolygonStyle(Color.YELLOW, null, 0.0f);

    // 创建一个两个图层的地图
    final MapContent map = new MapContent();
    map.setTitle("ImageLab");

    Layer rasterLayer = new GridReaderLayer(reader, rasterStyle);
    map.addLayer(rasterLayer);

    Layer shpLayer = new FeatureLayer(shapefileSource, shpStyle);
    map.addLayer(shpLayer);

    //设置地图界面的样式
    frame = new JMapFrame(map);
    frame.setSize(800, 600);
    frame.enableStatusBar(true);
    frame.enableToolBar(true);

    JMenuBar menuBar = new JMenuBar();
    frame.setJMenuBar(menuBar);
    JMenu menu = new JMenu("Raster");
    menuBar.add(menu);

    menu.add(
        new SafeAction("Grayscale display") {
            public void action(ActionEvent e) throws Throwable {
                Style style = createGreyscaleStyle();
                if (style != null) {
                    ((StyleLayer) map.layers().get(0)).setStyle(style);
                    frame.repaint();
                }
            }
        });

    menu.add(
        new SafeAction("RGB display") {
            public void action(ActionEvent e) throws Throwable {
                Style style = createRGBStyle();
                if (style != null) {
                    ((StyleLayer) map.layers().get(0)).setStyle(style);
                    frame.repaint();
                }
            }
        });
    frame.setVisible(true);
}
4.4.1 创建选择灰度的对话框
private Style createGreyscaleStyle() {
    GridCoverage2D cov = null;
    try {
        cov = reader.read(null);
    } catch (IOException giveUp) {
        throw new RuntimeException(giveUp);
    }
    int numBands = cov.getNumSampleDimensions();
    Integer[] bandNumbers = new Integer[numBands];
    for (int i = 0; i < numBands; i++) {
        bandNumbers[i] = i + 1;
    }
    Object selection =
        JOptionPane.showInputDialog(
        frame,
        "Band to use for greyscale display",
        "Select an image band",
        JOptionPane.QUESTION_MESSAGE,
        null,
        bandNumbers,
        1);
    if (selection != null) {
        int band = ((Number) selection).intValue();
        return createGreyscaleStyle(band);
    }
    return null;
}
4.4.2 创建灰度样式
private Style createGreyscaleStyle(int band) {
    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);
}

4.5 创建RGB样式

private Style createRGBStyle() {
    GridCoverage2D cov = null;
    try {
        cov = reader.read(null);
    } catch (IOException giveUp) {
        throw new RuntimeException(giveUp);
    }
    //我们需要至少三个波段的样式
    int numBands = cov.getNumSampleDimensions();
    if (numBands < 3) {
        return null;
    }
    //获取各个波段的名称
    String[] sampleDimensionNames = new String[numBands];
    for (int i = 0; i < numBands; i++) {
        GridSampleDimension dim = cov.getSampleDimension(i);
        sampleDimensionNames[i] = dim.getDescription().toString();
    }
    final int RED = 0, GREEN = 1, BLUE = 2;
    int[] channelNum = {-1, -1, -1};
    for (int i = 0; i < numBands; i++) {
        String name = sampleDimensionNames[i].toLowerCase();
        if (name != null) {
            if (name.matches("red.*")) {
                channelNum[RED] = i + 1;
            } else if (name.matches("green.*")) {
                channelNum[GREEN] = i + 1;
            } else if (name.matches("blue.*")) {
                channelNum[BLUE] = i + 1;
            }
        }
    }
    if (channelNum[RED] < 0 || channelNum[GREEN] < 0 || channelNum[BLUE] < 0) {
        channelNum[RED] = 1;
        channelNum[GREEN] = 2;
        channelNum[BLUE] = 3;
    }
    //我们使用selected通道创建RasterSymbolizer样式
    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);
}
  • 11
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

surpassLiang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值