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

时间:2017-03-09 14:12:48

标签: java geotools

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

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

现在,我想将图像绘制到使用坐标参考系统" EPSG:4326" (WGS84)。

当我通过定义图像的纬度/经度中的角来绘制它时,取决于像素*像素比例的"图像尺寸"计算和转换从中心点到每个角落的纬度/经度坐标的距离,我想,图像没有正确地绘制到地图上。 通过术语"未正确绘制"我的意思是,图像似乎被移动了,而且图像的内容也不在我预期的地图位置。

我认为情况就是这样,因为我"混合"像素缩放图像和" EPSG:4326"坐标参考系。

现在,根据我给出的信息,我可以将整个像素矩阵从固定像素比例基础转换为" EPSG:4326"坐标参考系统,使用Geotools? 当然,转换必须依赖于纬度/经度的中心位置,我已经给出的,以及像素尺度。

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

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,并且必须经常相应地执行变换。 再一次,这是我最初的想法导致一个解决方案,还是你有其他解决方案来解决我的问题?

非常感谢, Kiamur

1 个答案:

答案 0 :(得分:3)

这并不像听起来那么简单。您基本上是尝试使用扁平方形在球体上定义一个区域(技术上为椭圆体)。因此,没有"正确"这样做的方式,所以你总会有一些失真。如果不确切知道图像的来源,就无法准确回答这个问题,但以下代码为您提供了3种不同的答案:

enter image description here

前两个使用GeoTools' GeodeticCalculator使用方位和距离计算角点。这些是蓝色" square"和绿色" square"以上。蓝色直接计算角落,而绿色计算边缘并推断交叉点的角落(这就是为什么它是方形的)。

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

另一种方法是将中心点转换为更平坦的投影&#34;并使用简单的加法来计算角点,然后反转变换。为此,我们可以使用OGC WMS规范定义的AUTO projection来生成以我们的点为中心的正交投影,这样可以得到红色&#34; square&#34;这与蓝色非常相似。

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