编程语言
首页 > 编程语言> > java-将笛卡尔像素数据数组转换为纬度/经度像素数据数组

java-将笛卡尔像素数据数组转换为纬度/经度像素数据数组

作者:互联网

我有一个图像(基本上,我得到的原始图像数据为1024×1024像素),并且图像的中心像素的经度/纬度位置.

每个像素代表以米为单位的相同固定像素比例(例如,每个像素30m).

现在,我想将图像绘制到使用坐标参考系统“ EPSG:4326”(WGS84)的地图上.

当我通过仅在图像的纬度/经度中定义拐角来绘制图像时,我想,根据“以像素为单位的图像大小*像素比例”计算,并将从中心点的距离转换为每个角的纬度/经度坐标,图像未正确绘制到地图上.
术语“未正确绘制”是指图像似乎发生了偏移,并且图像的内容也不在我希望它们位于的地图位置.

我想是这种情况,因为我将像素缩放的图像和“ EPSG:4326”坐标参考系统“混合”了.

现在,根据我提供的信息,是否可以使用Geotools将整个像素矩阵从固定像素比例基础转换为“ EPSG:4326”坐标参考系统中的新像素矩阵?
当然,变换必须取决于我给出的经度/纬度的中心位置以及像素比例.

我想知道是否使用这样的方法会指引我正确的方向:

MathTransform transform = CRS.findMathTransform(DefaultGeocentricCRS.CARTESIAN, DefaultGeographicCRS.WGS84, true);

  DirectPosition2D srcDirectPosition2D = new DirectPosition2D(DefaultGeocentricCRS.CARTESIAN, degreeLat.getDegree(), degreeLon.getDegree());
  DirectPosition2D destDirectPosition2D = new DirectPosition2D();
  transform.transform(srcDirectPosition2D, destDirectPosition2D);

  double transX = destDirectPosition2D.x;
  double transY = destDirectPosition2D.y;

  int kmPerPixel = mapImage.getWidth / 1024; // It is known to me that my map is 1024x1024km ...

  double x = zeroPointX + ((transX * 0.001) * kmPerPixel);
  double y = zeroPointY + (((transX * -1) * 0.001) * kmPerPixel);

(从另一个SO线程获得了此代码,并对其进行了一些修改,但是仍然想知道这是否是解决我的问题的正确起点.)

我仅假定我的原始图像坐标参考系统是DefaultGeocentricCRS.CARTESIAN类型.有人可以确认吗?

从这里开始,这是使用Geotools解决此类问题的正确开始,还是我走在一条完全错误的道路上?

另外,我想补充一点,它将在安静的动态系统中使用.因此,我的图像更新约为10Hz,因此必须经常执行转换.
再说一遍,这是我最初的想法导致解决方案,还是您有其他解决方案来解决我的问题?

非常感谢你,
凯慕尔

解决方法:

这并不像听起来那么简单.您实际上是在尝试使用扁平正方形在球体上定义一个区域(从技术上说是椭圆体).因此,没有“正确”的方法可以完成操作,因此您总是会失真.不知道确切的图像来源,就无法完全回答,但是以下代码为您提供了3种可能的答案:

enter image description here

前两个利用GeoTools的GeodeticCalculator通过方位角和距离来计算拐角点.这些是上方的蓝色“正方形”和绿色“正方形”.蓝色直接计算角点,而绿色则计算边缘并从相交处推断出角点(这就是为什么它更方形).

final int width = 1024, height = 1024;
GeometryFactory gf = new GeometryFactory();
Point centre = gf.createPoint(new Coordinate(0,51));
WKTWriter writer = new WKTWriter();
//direct method
GeodeticCalculator calc = new GeodeticCalculator(DefaultGeographicCRS.WGS84);
calc.setStartingGeographicPoint(centre.getX(), centre.getY());
double height2 = height/2.0;
double width2 = width/2.0;
double dist = Math.sqrt(height2*height2+width2 *width2);
double bearing = 45.0;
Coordinate[] corners = new Coordinate[5];
for (int i=0;i<4;i++) {
  calc.setDirection(bearing, dist*1000.0 );
  Point2D corner = calc.getDestinationGeographicPoint();
  corners[i] = new Coordinate(corner.getX(),corner.getY());
  bearing+=90.0;
}
corners[4] = corners[0];
Polygon bbox = gf.createPolygon(corners);
System.out.println(writer.write(bbox));

double[] edges = new double[4];
bearing = 0;
for(int i=0;i<4;i++) {
  calc.setDirection(bearing, height2*1000.0 );
  Point2D corner = calc.getDestinationGeographicPoint();

  if(i%2 ==0) {
    edges[i] = corner.getY();
  }else {
    edges[i] = corner.getX();
  }
  bearing+=90.0;
}

corners[0] = new Coordinate( edges[1],edges[0]);
corners[1] = new Coordinate( edges[1],edges[2]);
corners[2] = new Coordinate( edges[3],edges[2]);
corners[3] = new Coordinate( edges[3],edges[0]);

corners[4] = corners[0];
bbox = gf.createPolygon(corners);
System.out.println(writer.write(bbox));

完成此操作的另一种方法是将中心点转换为“更平滑”的投影,并使用简单的加法来计算角,然后反转转换.为此,我们可以使用OGC WMS规范定义的AUTO projection来生成以我们的点为中心的正交投影,这将产生红色的“正方形”,与蓝色的正方形非常相似.

String code = "AUTO:42003," + centre.getX() + "," + centre.getY();
// System.out.println(code);
CoordinateReferenceSystem auto = CRS.decode(code);
// System.out.println(auto);
MathTransform transform = CRS.findMathTransform(DefaultGeographicCRS.WGS84,
    auto);
MathTransform rtransform = CRS.findMathTransform(auto,DefaultGeographicCRS.WGS84);
Point g = (Point)JTS.transform(centre, transform);

width2 *=1000.0;
height2 *= 1000.0;
corners[0] = new Coordinate(g.getX()-width2,g.getY()-height2);
corners[1] = new Coordinate(g.getX()+width2,g.getY()-height2);
corners[2] = new Coordinate(g.getX()+width2,g.getY()+height2);
corners[3] = new Coordinate(g.getX()-width2,g.getY()+height2);
corners[4] = corners[0];
bbox = gf.createPolygon(corners);
bbox = (Polygon)JTS.transform(bbox, rtransform);
System.out.println(writer.write(bbox));

使用哪种解决方案取决于您的口味,并且取决于图像的来源,但是我怀疑红色或蓝色是最好的.如果您需要以10Hz的频率执行此操作,则需要测试它们的速度,但是我怀疑变换图像会成为瓶颈.

设置好边界框后,您可以使用以下方法将(未参考的)图像转换为地理参考的coverage:

GridCoverageFactory factory = CoverageFactoryFinder.getGridCoverageFactory(null);
GridCoverage2D gc = factory.create("name", image, new ReferencedEnvelope(bbox.getEnvelopeInternal(),DefaultGeographicCRS.WGS84));
String fileName = "myImage.tif";
AbstractGridFormat format = GridFormatFinder.findFormat(fileName);
File out = new File(fileName);
GridCoverageWriter writer = format.getWriter(out);
try {
  writer.write(gc, null);
  writer.dispose();
} catch (IllegalArgumentException | IOException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
}

标签:geotools,java
来源: https://codeday.me/bug/20191111/2021165.html