【问题标题】:Need help rendering a JMapFrame froma cropped geotiff需要帮助从裁剪的 geotiff 渲染地图框
【发布时间】:2019-10-22 17:10:23
【问题描述】:

我是 geotools 的新手,对 Java 还有些陌生。我创建了一个程序,它可以读取 geotiff,对其进行裁剪,并使用 JavaFX 将裁剪后的图像渲染到 ImageView 中。现在,我想在渲染图像上添加地理点作为图层。我已经完成了创建带有标题的 MapContent。我遇到问题的地方是渲染一个 JMapFrame 来测试正在传递的数据。我正在尝试创建并添加裁剪图像的 GridCoverageLayer。我无法让 JMapFrame 渲染图像,它似乎卡在一个循环中。我怀疑问题是将图层的样式设置为 NULL。如果这是问题,我该如何创建基于栅格的样式?我已经尝试阅读 Geotools API 和教程,但我有一半的时间无法做出正面或反面...... 我的最终目标是使用 JavaFX 而不是 AWT 用符号渲染地图。

import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.processing.CoverageProcessor;
import org.geotools.gce.geotiff.GeoTiffReader;
import org.geotools.geometry.GeneralDirectPosition;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.GridCoverageLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.referencing.GeodeticCalculator;
import org.geotools.swing.JMapFrame;
import org.geotools.util.factory.Hints;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.TransformException;

import java.io.File;
import java.io.IOException;

public class Processor {

    private static void getImage(File file, double NE_lon, double NE_lat, double SW_lon, double SW_lat) throws IOException, TransformException{

            //Create the coverage processor and create the crop operation
            final CoverageProcessor processor = new CoverageProcessor();
            final ParameterValueGroup param = processor.getOperation("CoverageCrop").getParameters();

            //Read the TIFF, create the coverage/grid, get the CRS, and get the image envelope
            GeoTiffReader reader = new GeoTiffReader(file, new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER,Boolean.TRUE));
            GridCoverage2D coverage = reader.read(null);
            CoordinateReferenceSystem inCRS = coverage.getCoordinateReferenceSystem();
            GeneralEnvelope inEnvelope = (GeneralEnvelope) coverage.getEnvelope();

            //Get the image envelope min/max coordinates
            GeneralDirectPosition inMaxDP = (GeneralDirectPosition) inEnvelope.getUpperCorner();
            GeneralDirectPosition inMinDP = (GeneralDirectPosition) inEnvelope.getLowerCorner();

            //Calculate the crop cartesian min/max coordinates
            GeodeticCalculator calc = new GeodeticCalculator(inCRS);
            calc.setStartingGeographicPoint(NE_lon,NE_lat);
            GeneralDirectPosition cropMaxDP = (GeneralDirectPosition) calc.getStartingPosition();
            calc.setStartingGeographicPoint(SW_lon,SW_lat);
            GeneralDirectPosition cropMinDP = (GeneralDirectPosition) calc.getStartingPosition();

            //Output to console the original and cropped cartesian min/max coordinates
            System.out.println("Coordinate system: ");
            System.out.println("NE (max) corner (meters from meridian (x), origin (y): "+inMaxDP);
            System.out.println("SW (min) corner (meters from meridian (x), origin (y): "+inMinDP);
            System.out.println();
            System.out.println("NE (max) trim corner (lon,lat): "+NE_lon+","+NE_lat);
            System.out.println("SW (min) trim corner (lon,lat): "+SW_lon+","+SW_lat);
            System.out.println("NE (max) trim corner (meters from meridian (x), origin (y): "+cropMaxDP);
            System.out.println("SW (min) trim corner (meters from meridian (x), origin (y): "+cropMinDP);
            System.out.println();

            //Create the crop envelope size and crop the image envelope
            final ReferencedEnvelope crop = new ReferencedEnvelope(
                    cropMinDP.getOrdinate(0),
                    cropMaxDP.getOrdinate(0),
                    cropMinDP.getOrdinate(1),
                    cropMaxDP.getOrdinate(1),
                    inCRS);

            //Set the Processor to look at the Coverage2D image and crop to the ReferenceEnvelope set
            param.parameter("Source").setValue( coverage );
            param.parameter("Envelope").setValue( crop );
            GridCoverage2D cropCoverage = (GridCoverage2D) processor.doOperation(param);

            //Create a Map with layers
            MapContent map = new MapContent();
            map.setTitle("Detroit");
            Layer coverageLayer = new GridCoverageLayer(cropCoverage,null,"Background");
            map.addLayer(coverageLayer);

            JMapFrame.showMap(map);

            //Generate a BufferedImage of the GridCoverage2D
    //        PlanarImage croppedRenderedImageImage = (PlanarImage) cropCoverage.getRenderedImage();
    //        BufferedImage image = croppedRenderedImageImage.getAsBufferedImage();
    //        System.out.println("Image type: "+image.getType());
    //        System.out.println("Image height: "+image.getHeight());
    //        System.out.println("Image width: "+image.getWidth());

            //Write crop to file system
            /*File outFile = new File("/home/greg/Software_Projects/JavaProjects/charts/Detroit_98/Detroit_SEC_98.tif");
            GeoTiffWriter writer = new GeoTiffWriter(outFile,new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER,Boolean.TRUE));
            writer.write(cropped,null);*/
        }

        public static void main(String[] args) throws IOException, TransformException {
            File inFile = new File("/home/greg/Software_Projects/JavaProjects/charts/Detroit_98/Detroit SEC 98.tif");
            getImage(inFile,-81,42,-82.5,41);
        }
}

编辑

图片的GdalInfo

Image Structure Metadata: INTERLEAVE=BAND 
Corner Coordinates: Upper Left ( -84165.569, -73866.808) ( 82d 0'29.23"W, 41d29'48.29"N) 
Lower Left ( -84165.569, -129071.257) ( 82d 0' 0.36"W, 40d59'59.09"N) 
Upper Right ( -41702.491, -73866.808) ( 81d29'58.28"W, 41d30' 0.88"N) 
Lower Right ( -41702.491, -129071.257) ( 81d29'43.98"W, 41d 0'11.57"N) 
Center ( -62934.030, -101469.032) ( 81d45' 2.94"W, 41d15' 0.94"N) 
Band 1 Block=1003x1 Type=Byte, ColorInterp=Palette NoData Value=0 Color Table (RGB with 256 entries) 

【问题讨论】:

    标签: geotools


    【解决方案1】:

    更新

    您的 tiff 不是一个简单的光栅,它包含一个调色图像 (ColorInterp=Palette),因此每个像素都包含一个 0-255 之间的单个字节,它映射到一种颜色。因此,您的目标是符号化图像将不起作用,因为像素值和颜色之间没有线性关系。要在 GeoTools 中显示此图像,您需要一个空的 RasterSymbolizer,这就是 createGreyscaleStyle() 方法所做的。我已经用调色板图像对其进行了测试,它对我来说效果很好(注意带从 1 开始计数,而你只有一个带)。

    private Style createGreyscaleStyle(int band) {
        ContrastEnhancement ce = new ContrastEnhancementImpl();
        SelectedChannelType sct = sf.createSelectedChannelType(String.valueOf(band), ce);
    
        RasterSymbolizer sym = sf.getDefaultRasterSymbolizer();
        ChannelSelection sel = sf.channelSelection(sct);
        sym.setChannelSelection(sel);
    
        return SLD.wrapSymbolizers(sym);
    }
    

    Image Tutorial 的第 4 节展示了如何创建彩色光栅 SLD - 您不能只使用 NULL 样式,因为 GeoTools 现在不知道如何将波段转换为图像。 SLD reference in the GeoServer manual 中对可能的 RasterSymbolizer 选项有更完整的描述。或者,您可以导入包含该样式的 SLD 文件。

     /**
         * This method examines the names of the sample dimensions in the provided coverage looking for
         * "red...", "green..." and "blue..." (case insensitive match). If these names are not found it
         * uses bands 1, 2, and 3 for the red, green and blue channels. It then sets up a raster
         * symbolizer and returns this wrapped in a Style.
         *
         * @return a new Style object containing a raster symbolizer set up for RGB image
         */
        private Style createRGBStyle() {
            GridCoverage2D cov = null;
            try {
                cov = reader.read(null);
            } catch (IOException giveUp) {
                throw new RuntimeException(giveUp);
            }
            // We need at least three bands to create an RGB style
            int numBands = cov.getNumSampleDimensions();
            if (numBands < 3) {
                return null;
            }
            // Get the names of the bands
            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};
            // We examine the band names looking for "red...", "green...", "blue...".
            // Note that the channel numbers we record are indexed from 1, not 0.
            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 we didn't find named bands "red...", "green...", "blue..."
            // we fall back to using the first three bands in order
            if (channelNum[RED] < 0 || channelNum[GREEN] < 0 || channelNum[BLUE] < 0) {
                channelNum[RED] = 1;
                channelNum[GREEN] = 2;
                channelNum[BLUE] = 3;
            }
            // Now we create a RasterSymbolizer using the selected channels
            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);
        }
    }
    

    【讨论】:

    • 这就是我所怀疑的。问题是,我只读取了一个样本维度。我阅读本教程的方式将返回 NULL,因为它旨在为 RGB 读取三个。 // 我们需要至少三个波段来创建一个 RGB 样式 int numBands = cov.getNumSampleDimensions(); if (numBands 因此,我的图层为 NULL。如果我将图像读入 BufferedImage 和 getType,它会返回类型 13。
    • 在这种情况下,请查看同一教程中的灰度 sld
    • 谢谢,我会试试的。由于我几乎是新手,即使我的覆盖范围是彩色光栅化 TIFF 并报告一维,是否有可能拉出 RGB 波段?
    • 我尝试了这两种方法。灰度错误说“波段#无效。#表示波段#我试图阅读......设置什么数字并不重要。我什至尝试了GridCoverage.getDimension。所以,我然后尝试了RGB方法。它没有没有错,但 JMap 再次挂在循环渲染地图中。有什么想法吗?
    • 太棒了!有效!!感谢您的帮助和耐心!现在我有了光栅背景,我可以使用 GeoTools 并尝试添加图层。学习这一点的唯一方法是练习、失败并提出问题以使其正确。再次感谢!
    猜你喜欢
    • 2011-04-07
    • 1970-01-01
    • 2013-04-20
    • 2021-05-10
    • 1970-01-01
    • 2010-10-28
    • 2020-08-17
    • 1970-01-01
    • 2011-06-11
    相关资源
    最近更新 更多