我有一个图像(基本上,我得到的原始图像数据为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种可能的答案:
前两个利用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();
}