阅读前请先看我的另一篇博客,配置好实用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);
}
}
与从“地理空间数据云”下载的影像文件夹内的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");
}
}
输出内容如下:
输出的投影点的值和上图相同,并且经纬度和从“地理空间数据云”下载的影像文件夹内的txt中CORNER_UL_LAT_PRODUCT 和CORNER_UL_LON_PRODUCT 数值相似。到此解决。
参考博客:https://blog.csdn.net/m0_37821031/article/details/79111051