提问者:小点点

将笛卡尔像素数据数组转换为lat/lon像素数据数组


我有一个图像(基本上,我得到的原始图像数据是1024x1024像素)和图像中心像素的纬度/经度位置。

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

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

当我通过定义图像的lat/lon中的角来绘制它时,根据“像素*像素尺度的图像大小”计算并将从中心点到每个角的lat/lon坐标的距离转换,我想,图像没有正确地绘制到地图上。通过术语“未正确绘制”,我的意思是,图像似乎被移动了,图像的内容也不在地图位置,我期望它们在哪里。

我想这是因为我“混合”了一个像素缩放的图像和一个“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类型。有人能证实这一点吗?

从现在开始,这是使用地理工具解决这类问题的正确开始吗,还是我完全走错了路?

此外,我想补充一点,这将用于一个安静的动态系统。因此,我的图像更新大约是10Hz,转换必须相应地经常进行。再问一次,我最初的想法会导致解决方案吗,或者你有其他解决我的问题的方案吗?

非常感谢,Kiamur


共1个答案

匿名用户

这并不像听起来那么简单。你实际上是在试图用一个平的正方形来定义一个球体(技术上是椭球体)上的一个区域。因此,没有“正确”的方法来做这件事,所以你最终总是会有一些失真。如果不知道你的图片到底是从哪里来的,就没有办法准确地回答这个问题,但是下面的代码为你提供了3个不同的可能答案:

前两种方法利用GeoTools的< code>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投影来生成以我们的点为中心的正交投影,这将给出与蓝色非常相似的红色“正方形”。

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下这样做,那么你需要测试它们的速度,但我怀疑转换图像将是瓶颈。

一旦你有你的边界框设置到你满意,你可以转换你(未引用的)图像到地理参考覆盖使用:

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();
}