You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Java/Scala、GeoTools/GeoTrellis将Sentinel-2的JP2转为多波段GeoTIFF

嘿,针对你处理Sentinel-2 JP2数据的需求,结合你熟悉的GeoTools和GeoTrellis,我整理了具体的实现方案,应该能解决你的两个问题:

一、使用GeoTools实现

问题a:给定范围坐标将JP2转为带地理参考的GeoTIFF

先提个小细节:Sentinel-2 L1C的JP2其实是附带地理信息的,可能你没同步拿到对应的元数据文件?不过没关系,我们可以手动为栅格赋予坐标参考和范围。步骤如下:

  1. 读取JP2文件并转为GridCoverage2D
  2. 指定对应的坐标参考系(你提供的tile是10S EG,对应UTM Zone 10S,EPSG代码是32710)
  3. 根据你给定的坐标范围创建Envelope
  4. 将地理信息绑定到栅格后写入GeoTIFF

代码示例(Java):

import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridCoverageFactory;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.gce.geotiff.GeoTiffWriter;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

import javax.imageio.ImageIO;
import java.io.File;
import java.awt.image.RenderedImage;

public class Jp2ToGeoTiff {
    public static void main(String[] args) throws Exception {
        // 1. 读取JP2文件
        File jp2File = new File("path/to/your/band.jp2");
        RenderedImage image = ImageIO.read(jp2File);
        GridCoverage2D rawCoverage = new GridCoverageFactory().create("raw_band", image, null);

        // 2. 设置坐标参考系(UTM 10S,EPSG:32710)
        CoordinateReferenceSystem crs = CRS.decode("EPSG:32710");

        // 3. 定义地理范围(替换成你给定的minX, minY, maxX, maxY)
        ReferencedEnvelope envelope = new ReferencedEnvelope(minX, maxX, minY, maxY, crs);

        // 4. 绑定地理信息并写入GeoTIFF
        GridCoverage2D geoCoverage = new GridCoverageFactory().create(
                "geo_band",
                rawCoverage.getRenderedImage(),
                envelope,
                rawCoverage.getSampleDimensions(),
                null,
                null
        );

        GeoTiffWriter writer = new GeoTiffWriter(new File("path/to/output/band.tif"));
        writer.write(geoCoverage, null);
        writer.dispose();
    }
}

问题b:合并多波段为单个GeoTIFF

要合并多个单波段栅格,核心是确保所有栅格的坐标参考系、地理范围和分辨率完全一致,然后用GridCoverageFactory将它们组合成多波段栅格:

import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridCoverageFactory;
import org.geotools.gce.geotiff.GeoTiffReader;
import org.geotools.gce.geotiff.GeoTiffWriter;

import java.io.File;
import java.util.Arrays;
import java.util.List;

public class MergeBands {
    public static void main(String[] args) throws Exception {
        // 1. 读取所有单波段GeoTIFF(或直接从JP2处理后的GridCoverage2D)
        List<File> bandFiles = Arrays.asList(
                new File("path/to/band1.tif"),
                new File("path/to/band2.tif"),
                new File("path/to/band3.tif")
                // 添加其他波段文件
        );

        List<GridCoverage2D> bandCoverages = bandFiles.stream()
                .map(file -> {
                    try {
                        return new GeoTiffReader(file).read(null);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                })
                .toList();

        // 2. 合并为多波段栅格
        GridCoverage2D multiBandCoverage = new GridCoverageFactory().create(
                "sentinel2_multi_band",
                new javax.media.jai.MultiRenderedImage(
                        bandCoverages.stream()
                                .map(GridCoverage2D::getRenderedImage)
                                .toArray(RenderedImage[]::new)
                ),
                bandCoverages.get(0).getEnvelope(),
                bandCoverages.stream()
                        .flatMap(cov -> Arrays.stream(cov.getSampleDimensions()))
                        .toArray(SampleDimension[]::new),
                null,
                null
        );

        // 3. 写入多波段GeoTIFF
        GeoTiffWriter writer = new GeoTiffWriter(new File("path/to/output/multi_band.tif"));
        writer.write(multiBandCoverage, null);
        writer.dispose();
    }
}

二、使用GeoTrellis(Scala)实现

如果你更倾向用Scala,GeoTrellis的栅格处理API更简洁,适合批量操作:

问题a:给定范围坐标转JP2为GeoTIFF

GeoTrellis支持直接读取JP2,然后绑定地理信息后写入GeoTIFF:

import geotrellis.raster._
import geotrellis.raster.io.geotiff._
import geotrellis.proj4._
import java.io.File

object Jp2ToGeoTiff {
  def main(args: Array[String]): Unit = {
    // 读取JP2文件
    val jp2Path = "path/to/your/band.jp2"
    val rawTile = GeoTiffReader.readSingleband(jp2Path).tile

    // 设置坐标参考系(UTM 10S,EPSG:32710)
    val crs = CRS.fromEpsgCode(32710)

    // 定义地理范围(替换成你的坐标)
    val extent = Extent(minX, minY, maxX, maxY)

    // 创建带地理信息的单波段GeoTIFF并写入
    val singlebandTiff = SinglebandGeoTiff(rawTile, extent, crs)
    singlebandTiff.write("path/to/output/band.tif")
  }
}

问题b:合并多波段为单个GeoTIFF

GeoTrellis的MultibandTile可以直接组合多个单波段Tile,前提是所有Tile的Extent和CRS一致:

import geotrellis.raster._
import geotrellis.raster.io.geotiff._
import java.io.File

object MergeBands {
  def main(args: Array[String]): Unit = {
    // 读取所有单波段GeoTIFF
    val bandPaths = List(
      "path/to/band1.tif",
      "path/to/band2.tif",
      "path/to/band3.tif"
      // 添加其他波段
    )

    val singlebandTiffs = bandPaths.map(GeoTiffReader.readSingleband)

    // 确保所有栅格的Extent和CRS一致(如果不一致需要先重采样对齐)
    val baseExtent = singlebandTiffs.head.extent
    val baseCrs = singlebandTiffs.head.crs

    // 合并为多波段Tile
    val multibandTile = MultibandTile(singlebandTiffs.map(_.tile))

    // 创建多波段GeoTIFF并写入
    val multibandTiff = MultibandGeoTiff(multibandTile, baseExtent, baseCrs)
    multibandTiff.write("path/to/output/multi_band.tif")
  }
}

额外提示

如果你的Sentinel-2 JP2其实带有元数据(比如MTD_MSIL1C.xml文件),可以读取元数据自动获取Extent和CRS,这样比手动设置更准确,避免坐标偏差。比如GeoTrellis可以解析Sentinel-2的元数据文件来直接获取栅格的地理参数。

内容的提问来源于stack exchange,提问作者user3059517

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:19:44