java使用GDAL把投影点转换为经纬度

阅读前请先看我的另一篇博客,配置好实用GDAL的环境:https://blog.csdn.net/qq_40820382/article/details/104870682

从“地理空间数据云”下载的影像文件夹内的txt中有
CORNER_UL_LAT_PRODUCT 和CORNER_UL_LON_PRODUCT 字段,分别代表影像的纬度和经度,别搞反了。
还有CORNER_UL_PROJECTION_X_PRODUCT 和CORNER_UL_PROJECTION_Y_PRODUCT ,代表的是影像的投影坐标的x和y值。原理上是可以互相转化的。java可以配合使用GDAL来对.TIF影像进行操作。

使用常规的方法读取.TIF图像一般只能根据 “六参数信息”获取到图像四个拐角的投影坐标,如果想直接获取四个拐角的经纬度坐标很麻烦,我花了很久才做好。如果只想获取投影坐标,新建一个类,复制使用如下代码即可:

package com.louis.mango.admin.gis;


import org.gdal.gdal.Band;
import org.gdal.gdal.Dataset;
import org.gdal.gdal.Driver;
import org.gdal.gdal.gdal;

import org.gdal.gdalconst.gdalconstConstants;
import org.gdal.osr.CoordinateTransformation;
import org.gdal.osr.SpatialReference;

import java.util.HashMap;
import java.util.Map;

/**
 * GetProjectionRef() 获取源影像的坐标参考
 * GetGeoTransform() 读取六参数,具体含义参考以上引用博文
 * CloneGeogCS() 获取一个投影系中的地理系,投影坐标系是地理坐标投影的结果,
 * CoordinateTransformation ct = new CoordinateTransformation(src_Crs, oLatLong);两种坐标系统的变换关系
 * ReprojectImage(Dataset src_ds, Dataset dst_ds, String src_wkt, String dst_wkt, int eResampleAlg)实现重投影的函数
 *
 */
public class GISTest2 {
    /**
     * GDAL教程   https://www.jianshu.com/p/c25f9360459f
     * 宽、高、波段数
     * int x = rds.getRasterXSize();
     * int y = rds.getRasterYSize();
     * int b = rds.getRasterCount();
     *
     * @param args
     */
    public static void main(String[] args) {
        // String fileName_tif = "D:\\360downloads\\LC812340_B4.TIF";
        String fileName_tif = "D:\\毕业学习\\影像\\LC81210372016239LGN00\\LC81210372016239LGN00_BQA.TIF";
        // 首先是所有gdal程序都需要的注册语句:   之前的版本还需要加这句话来支持中文路径:    gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8","YES");
        gdal.AllRegister();
        // 读取影像
        Dataset hDataset = gdal.Open(fileName_tif, gdalconstConstants.GA_ReadOnly);
        // 影像投影信息
        String projection = hDataset.GetProjection();
        System.out.println("影像投影信息" + projection);

        if (hDataset == null) {
            System.err.println("GDALOpen failed - " + gdal.GetLastErrorNo());
            System.err.println(gdal.GetLastErrorMsg());
            System.exit(1);
        }
        Driver hDriver = hDataset.GetDriver();
        System.out.println("Driver: " + hDriver.getShortName() + "/" + hDriver.getLongName());
        // 列数  行数
        int iXSize = hDataset.getRasterXSize();
        int iYSize = hDataset.getRasterYSize();
        System.out.println("Size is " + iXSize + ", " + iYSize);

        // 六参数信息
        double[] geoTransform = hDataset.GetGeoTransform();

        //影像 左上角 投影坐标(左上角地理坐标????)
        double[] ulCoord = new double[2];
        ulCoord[0] = geoTransform[0];
        ulCoord[1] = geoTransform[3];

        //影像 右下角 投影坐标
        double[] brCoord = new double[2];
        //  geoTransform[2]:x方向旋转角, geoTransform[4]:y方向旋转角,一般是0,不用管他。geoTransform[5]是南北方向空间分辨率
        brCoord[0] = geoTransform[0] + iXSize * geoTransform[1] + iYSize * geoTransform[2];
        brCoord[1] = geoTransform[3] + iXSize * geoTransform[4] + iYSize * geoTransform[5];

        // 影像 左下角 投影坐标
        double[] blCoord = new double[2];
        blCoord[0] = geoTransform[0];
        blCoord[1] = geoTransform[3] + iXSize * geoTransform[4] + iYSize * geoTransform[5];

        //影像 右上角 投影坐标
        double[] urCoord = new double[2];
        //  geoTransform[2]:x方向旋转角, geoTransform[4]:y方向旋转角,一般是0,不用管他。
        urCoord[0] = geoTransform[0] + iXSize * geoTransform[1] + iYSize * geoTransform[2];
        urCoord[1] = geoTransform[3];

        double[] doubles = GaussXYDeal.GaussToBL(ulCoord[0], ulCoord[1]);
        System.out.println("经度为:" + doubles[0] + ",纬度为" + doubles[1]);
        // 影像 左上角 投影坐标
        System.out.println("影像左上角投影坐标--------------------(" + ulCoord[0] + ", " + ulCoord[1] + ")");
        //影像 右上角 投影坐标
        System.out.println("影像右上角投影坐标--------------------(" + urCoord[0] + ", " + urCoord[1] + ")");
        // 影像 左下角 投影坐标
        System.out.println("影像左下角投影坐标--------------------(" + blCoord[0] + ", " + blCoord[1] + ")");
        // 影像 右下角 投影坐标
        System.out.println("影像右下角投影坐标--------------------(" + brCoord[0] + ", " + brCoord[1] + ")");

        double[] gaussToBL = GaussXYDeal.GaussToBL(ulCoord[0], ulCoord[1]);
        System.out.println("影像左上角经度:" + gaussToBL[0] + ",纬度:" + gaussToBL[1]);
        double[] gaussToBL1 = GaussXYDeal.GaussToBL(urCoord[0], urCoord[1]);
        System.out.println("影像右上角经度:" + gaussToBL1[0] + ",纬度:" + gaussToBL1[1]);
        double[] gaussToBL2 = GaussXYDeal.GaussToBL(blCoord[0], blCoord[1]);
        System.out.println("影像左下角经度:" + gaussToBL2[0] + ",纬度:" + gaussToBL2[1]);
        double[] gaussToBL3 = GaussXYDeal.GaussToBL(brCoord[0], brCoord[1]);
        System.out.println("影像右下角经度:" + gaussToBL3[0] + ",纬度:" + gaussToBL3[1]);
        Map map = new HashMap<>();
        map.put("key1", gaussToBL);
        System.out.println("key1X:" + map.get("key1")[0] + "\nkey1Y:" + map.get("key1")[1]);

        double[] doubles1 = CoordinateConversion.mercatorToLonLats(ulCoord[0], ulCoord[1]);
        System.out.println("doubles1X: " + doubles1[0] + ",doubles1Y: " + doubles1[1]);


        // System.out.println("x方向旋转角-----------geoTransform[2]=" + geoTransform[2]);
        // System.out.println("y方向旋转角-----------geoTransform[4]=" + geoTransform[4]);
        // System.out.println("东西方向空间分辨率-----geoTransform[1]=" + geoTransform[1]);
        // System.out.println("南北方向空间分辨率-----geoTransform[5]=" + geoTransform[5]);
        // System.out.println("左上角x坐标-----------geoTransform[0]=" + geoTransform[0]);
        // System.out.println("左上角y坐标-----------geoTransform[3]=" + geoTransform[3]);

        Band band = hDataset.GetRasterBand(1);
        int buf[] = new int[iXSize];
        for (int i = 0; i < 10; i++) {
            band.ReadRaster(0, i, iXSize, 1, buf);
            for (int j = 0; j < 10; j++) {
                // System.out.print(buf[j] + ", ");
            }
            // System.out.println("\n");
        }
        hDataset.delete();

    }
}
//
//   //读取影像
//   Dataset rds = gdal.Open("影像路径", gdalconst.GA_ReadOnly);
//   //宽、高、波段数
//   int x = rds.getRasterXSize();
//   int y = rds.getRasterYSize();
//   int b = rds.getRasterCount();
//
//           //六参数信息
//           double[] geoTransform = rds.GetGeoTransform();
//           //影像左上角投影坐标
//           double[] ulCoord = new double[2];
//           ulCoord[0] = geoTransform[0];
//           ulCoord[1] = geoTransform[3];
//           //影像右下角投影坐标
//           double[] brCoord = new double[2];
//           brCoord[0] = geoTransform[0] + x * geoTransform[1] + y * geoTransform[2];
//           brCoord[1] = geoTransform[3] + x * geoTransform[4] + y * geoTransform[5];
//
//           //影像投影信息
//           String proj = rds.GetProjection();
//
//           这里有个数组geoTransform,容量为6,代表的是仿射变换六参数,其含义如下:
//
//           geoTransform[0]:左上角x坐标
//           geoTransform[1]:东西方向空间分辨率
//           geoTransform[2]:x方向旋转角
//           geoTransform[3]:左上角y坐标
//           geoTransform[4]:y方向旋转角
//           geoTransform[5]:南北方向空间分辨率
//             影像中任意一个像素的坐标可以用下式计算:
//
//
//           任意像素的坐标计算公式
//
//             其中六参数分别为{dx, a11, a12, dy, a21, a22},正常的影像,正北方向朝上,两个旋转角的值都是0。
//
package com.louis.mango.admin.gis;

public class GaussXYDeal {
    //  由高斯投影坐标反算成经纬度
    public static double[] GaussToBL(double X, double Y)//, double *longitude, double *latitude)
    {
        int ProjNo;
        int ZoneWide; 带宽
        double[] output = new double[2];
        double longitude1, latitude1, longitude0, X0, Y0, xval, yval;//latitude0,
        double e1, e2, f, a, ee, NN, T, C, M, D, R, u, fai, iPI;
        iPI = 0.0174532925199433; 3.1415926535898/180.0;
        //a = 6378245.0; f = 1.0/298.3; //54年北京坐标系参数
        a = 6378140.0;
        f = 1 / 298.257; //80年西安坐标系参数
        ZoneWide = 6; 6度带宽
        ProjNo = (int) (X / 1000000L); //查找带号
        longitude0 = (ProjNo - 1) * ZoneWide + ZoneWide / 2;
        longitude0 = longitude0 * iPI; //中央经线


        X0 = ProjNo * 1000000L + 500000L;
        Y0 = 0;
        xval = X - X0;
        yval = Y - Y0; //带内大地坐标
        e2 = 2 * f - f * f;
        e1 = (1.0 - Math.sqrt(1 - e2)) / (1.0 + Math.sqrt(1 - e2));
        ee = e2 / (1 - e2);
        M = yval;
        u = M / (a * (1 - e2 / 4 - 3 * e2 * e2 / 64 - 5 * e2 * e2 * e2 / 256));
        fai = u + (3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * Math.sin(2 * u) + (21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * Math.sin(
                4 * u)
                + (151 * e1 * e1 * e1 / 96) * Math.sin(6 * u) + (1097 * e1 * e1 * e1 * e1 / 512) * Math.sin(8 * u);
        C = ee * Math.cos(fai) * Math.cos(fai);
        T = Math.tan(fai) * Math.tan(fai);
        NN = a / Math.sqrt(1.0 - e2 * Math.sin(fai) * Math.sin(fai));
        R = a * (1 - e2) / Math.sqrt((1 - e2 * Math.sin(fai) * Math.sin(fai)) * (1 - e2 * Math.sin(fai) * Math.sin(fai)) * (1 - e2 * Math.sin
                (fai) * Math.sin(fai)));
        D = xval / NN;
        //计算经度(Longitude) 纬度(Latitude)
        longitude1 = longitude0 + (D - (1 + 2 * T + C) * D * D * D / 6 + (5 - 2 * C + 28 * T - 3 * C * C + 8 * ee + 24 * T * T) * D
                * D * D * D * D / 120) / Math.cos(fai);
        latitude1 = fai - (NN * Math.tan(fai) / R) * (D * D / 2 - (5 + 3 * T + 10 * C - 4 * C * C - 9 * ee) * D * D * D * D / 24
                + (61 + 90 * T + 298 * C + 45 * T * T - 256 * ee - 3 * C * C) * D * D * D * D * D * D / 720);
        //转换为度 DD
        output[0] = longitude1 / iPI;
        output[1] = latitude1 / iPI;
        return output;
        //*longitude = longitude1 / iPI;
        //*latitude = latitude1 / iPI;
    }

      由经纬度反算成高斯投影坐标
    public void GaussToBLToGauss(double longitude, double latitude) {
        int ProjNo = 0;
        int ZoneWide; 带宽
        double longitude1, latitude1, longitude0, latitude0, X0, Y0, xval, yval;
        double a, f, e2, ee, NN, T, C, A, M, iPI;
        iPI = 0.0174532925199433; 3.1415926535898/180.0;
        ZoneWide = 6; 6度带宽
        a = 6378245.0;
        f = 1.0 / 298.3; //54年北京坐标系参数
        a=6378140.0; f=1/298.257; //80年西安坐标系参数
        ProjNo = (int) (longitude / ZoneWide);
        longitude0 = ProjNo * ZoneWide + ZoneWide / 2;
        longitude0 = longitude0 * iPI;
        latitude0 = 0;
        System.out.println(latitude0);
        longitude1 = longitude * iPI; //经度转换为弧度
        latitude1 = latitude * iPI; //纬度转换为弧度
        e2 = 2 * f - f * f;
        ee = e2 * (1.0 - e2);
        NN = a / Math.sqrt(1.0 - e2 * Math.sin(latitude1) * Math.sin(latitude1));
        T = Math.tan(latitude1) * Math.tan(latitude1);
        C = ee * Math.cos(latitude1) * Math.cos(latitude1);
        A = (longitude1 - longitude0) * Math.cos(latitude1);
        M = a * ((1 - e2 / 4 - 3 * e2 * e2 / 64 - 5 * e2 * e2 * e2 / 256) * latitude1 - (3 * e2 / 8 + 3 * e2 * e2 / 32 + 45 * e2 * e2
                * e2 / 1024) * Math.sin(2 * latitude1)
                + (15 * e2 * e2 / 256 + 45 * e2 * e2 * e2 / 1024) * Math.sin(4 * latitude1) - (35 * e2 * e2 * e2 / 3072) * Math.sin(6 * latitude1));
        xval = NN * (A + (1 - T + C) * A * A * A / 6 + (5 - 18 * T + T * T + 72 * C - 58 * ee) * A * A * A * A * A / 120);
        yval = M + NN * Math.tan(latitude1) * (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24
                + (61 - 58 * T + T * T + 600 * C - 330 * ee) * A * A * A * A * A * A / 720);
        X0 = 1000000L * (ProjNo + 1) + 500000L;
        Y0 = 0;
        xval = xval + X0;
        yval = yval + Y0;
        //*X = xval;
        //*Y = yval;
        System.out.println("x:" + xval);
        System.out.println("y:" + yval);

    }
}

注意更换.TIF图像的路径。
可以获取如下输出:
java使用GDAL把投影点转换为经纬度_第1张图片

与从“地理空间数据云”下载的影像文件夹内的txt中CORNER_UL_PROJECTION_X_PRODUCT 和
CORNER_UL_PROJECTION_Y_PRODUCT
数值近似相同,但是由于调用的GaussToBL()方法精度严重不足,导致转换出的经纬度偏差很大。如何把这些投影点换为经纬度坐标呢?如下即可:

package com.louis.mango.admin.gis;

import org.gdal.gdal.Dataset;
import org.gdal.gdal.gdal;
import org.gdal.gdalconst.gdalconstConstants;
import org.gdal.osr.CoordinateTransformation;
import org.gdal.osr.SpatialReference;

public class Test {


    /**
     * java+gdal实现影像重投影
     * https://blog.csdn.net/m0_37821031/article/details/79111051
     * @param args
     */
    public static void main(String[] args) {
        // String fileName_tif = "D:\\360downloads\\LC812340_B4.TIF";
        String fileName_tif = "D:\\毕业学习\\影像\\LC81210372016239LGN00\\LC81210372016239LGN00_BQA.TIF";
        // 首先是所有gdal程序都需要的注册语句:   之前的版本还需要加这句话来支持中文路径:    gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8","YES");
        gdal.AllRegister();
        // 读取影像
        Dataset hDataset = gdal.Open(fileName_tif, gdalconstConstants.GA_ReadOnly);
        // 影像投影信息
        String projection = hDataset.GetProjection();
        // System.out.println("影像投影信息" + projection);

        // 打开的影像像素、波段等信息
        int numBands=hDataset.GetRasterCount(); // 读取影像波段数
        int xSize = hDataset.GetRasterXSize(); // 栅格尺寸
        int ySize = hDataset.GetRasterYSize(); //

        //获取源图像crs     获取源影像的坐标参考
        String src_wkt = hDataset.GetProjectionRef();
        //构造投影坐标系统的空间参考(wkt)
        SpatialReference src_Crs = new SpatialReference(src_wkt);

        //设置输出图像的坐标
        SpatialReference oLatLong;
        //获取该投影坐标系统中的地理坐标系
        oLatLong = src_Crs.CloneGeogCS();
        //构造一个从投影坐标系到地理坐标系的转换关系
        CoordinateTransformation ct = new CoordinateTransformation(src_Crs, oLatLong);

        double[] geoTransform = hDataset.GetGeoTransform();
        // 图像范围
        // 东西方向空间分辨率  --->像素宽度
        double w_src = geoTransform[1];
        // 南北方向空间分辨率 ---> 像素高度
        double h_src = geoTransform[5];
        // x方向旋转角
        double xRotate = geoTransform[2];
        // y方向旋转角
        double yRotate = geoTransform[4];

        // 左上角x坐标,y坐标 ---> 影像 左上角 投影坐标
        double xmin = geoTransform[0];
        double ymax = geoTransform[3];

        // 右下角 x坐标,y坐标
        double xmax = geoTransform[0] + xSize * w_src+ySize * xRotate;
        double ymin = geoTransform[3]  + xSize* yRotate + ySize * h_src;

        // 左下角 x坐标,y坐标
        double xmax2 = geoTransform[0];
        double ymin2 = geoTransform[3]  + xSize* yRotate + ySize * h_src;

        // 右上角 x坐标,y坐标
        double xmax3 = geoTransform[0] + xSize * w_src+ySize * xRotate;
        double ymin3 = geoTransform[3]  ;

        //计算目标影像的左上和右下坐标,即目标影像的仿射变换参数,投影转换为经纬度
        double a[]= ct.TransformPoint(xmin, ymax);
        double b[]= ct.TransformPoint(xmax, ymin);
        double c[]= ct.TransformPoint(xmax2, ymin2);
        double d[]= ct.TransformPoint(xmax3, ymin3);
        // double dbX[]={a[0],b[0],c[0],d[0]};
        // double dbY[]={a[1],b[1],c[1],d[1]};
        System.out.println("左上角投影点:");
        System.out.println(xmin);
        System.out.println(ymax);
        System.out.println("右下角投影点:");
        System.out.println(xmax);
        System.out.println(ymin);
        System.out.println("左下角投影点:");
        System.out.println(xmax2);
        System.out.println(ymin2);
        System.out.println("右上角投影点:");
        System.out.println(xmax3);
        System.out.println(ymin3);

        System.out.println("左上角经纬度:");
        System.out.println(a[0]);
        System.out.println(a[1]);
        System.out.println("右下角经纬度:");
        System.out.println(b[0]);
        System.out.println(b[1]);
        System.out.println("左下角经纬度:");
        System.out.println(c[0]);
        System.out.println(c[1]);
        System.out.println("右上角经纬度:");
        System.out.println(d[0]);
        System.out.println(d[1]);




        // System.out.println(dbX[0]+"\n");
        // System.out.println(dbX[1]+"\n");
        // System.out.println(dbX[2]+"\n");
        // System.out.println(dbX[3]+"\n");
        //
        // System.out.println(dbY[0]+"\n");
        // System.out.println(dbY[1]+"\n");
        // System.out.println(dbY[2]+"\n");
        // System.out.println(dbY[3]+"\n");

    }
    }

输出内容如下:
java使用GDAL把投影点转换为经纬度_第2张图片
输出的投影点的值和上图相同,并且经纬度和从“地理空间数据云”下载的影像文件夹内的txt中CORNER_UL_LAT_PRODUCT 和CORNER_UL_LON_PRODUCT 数值相似。到此解决。

参考博客:https://blog.csdn.net/m0_37821031/article/details/79111051

你可能感兴趣的:(编程,gdal,java,tiff)