以正确的方式将经度和纬度坐标转换为地图像素(X 和 Y)
Converting longitude and latitude coordinates to map pixels (X and Y) the right way
请看看我的另一个问题,这个问题做错了here。
我需要做什么
我有一张我国地图的图像,我从 Google 地图中截取了地图的一部分,这意味着我知道所有角落的经纬度坐标。我的程序需要显示地图,并在地图上绘制目标,每个目标都有其唯一的经度和纬度,类似于雷达显示目标的方式。
问题
我的解决方案的问题在于它并没有真正使用真正的数学公式来获取 X、Y 位置。正如您在我的其他问题中看到的那样,它使用简单的除法和乘以最小值和最大值的比率。
这是方法:
protected Location getCoordinatesByGlobe(float latitude, float longitude) {
/**
* Work out minimum and maximums, clamp inside map bounds
*/
latitude = Math.max(mapLatitudeMin, Math.min(mapLatitudeMax, latitude));
longitude = Math.max(mapLongitudeMin, Math.min(mapLongitudeMax, longitude));
/**
* We need the distance from 0 or minimum long/lat
*/
float adjLon = longitude - mapLongitudeMin;
float adjLat = latitude - mapLatitudeMin;
float mapLongWidth = mapLongitudeMax - mapLongitudeMin;
float mapLatHeight = mapLatitudeMax - mapLatitudeMin;
float mapWidth = mapImage.getWidth();
float mapHeight = mapImage.getHeight();
float longPixelRatio = mapWidth / mapLongWidth;
float latPixelRatio = mapHeight / mapLatHeight;
int x = Math.round(adjLon * longPixelRatio) - 3;// these are offsets for the target icon that shows.. eedit laterrr @oz
int y = Math.round(adjLat * latPixelRatio) + 3; //
// turn it up
y = (int) (mapHeight - y);
return new Location(x, y);
}
我试过的
所以我有点想自己想一些合乎逻辑的事情,我该怎么做。我想出了一些并不真正有效的东西:
如果我们有左上角的坐标(经度和纬度),并且我们有我们想要显示的目标的坐标,那意味着我们可以distanceToPoint
知道有多少距离起点还有公里。
在那之后,我们需要知道到那个点的航向,所以我们做 calculateHeading
这给了我们到目标点的角度。
所以我们称 A 为起点(左上角)
float aLat = 33.49f;
float aLong = 33.69f;
我们的目标点我们称之为 b:
float bLat = 32f;
float bLong = 35f;
然后我们可以计算出从A到B的距离,单位公里:
double km = distanceTopPoint(aLat, aLong, bLat, bLong);
然后我们计算到点的角度:
double angle = calculateHeading(aLat, aLong, bLat, bLong);
如果我们有公里距离和角度,我们就可以知道经度和纬度的距离(以公里为单位):
int latDistance = (int) Math.round(km * Math.cos(angle));
int lonDistance = (int) Math.round(km * Math.sin(angle));
所以现在我大概知道经度到目标经度的距离,纬度也一样。但是我能用这些信息做什么呢?
我再次尝试思考,我发现我可以知道从左上角到右上角的距离(以公里为单位)以及左上角到左上角的距离。然后我可以 width / km
得到每像素的公里数。
但我真的不确定,我确定我做错了什么。
有什么想法吗?
墨卡托投影是圆柱投影,即广义坐标可以计算为:
a = longitude
b = tan(latitude)
这些是未缩放的坐标,即它们不对应于像素位置。
假设您有一张 w
x h
像素的图像,表示 (min_long, min_lat) - (max_long, max_lat)
之间的区域。可以使用上述公式将这些坐标转换为广义投影坐标,得到 (min_a, min_b) - (max_a, max_b)
。
现在您需要广义坐标到像素位置的线性映射。这个线性映射可以用四个参数(两个缩放参数和两个偏移参数)来表示:
x = s_a * a + o_a
y = s_b * b = o_a
因此,您需要找到这四个参数。你知道左上角有像素坐标 (0, 0)
和广义坐标 (min_a, max_b)
。同样的右下角。这为您提供了四个约束和一个线性方程组:
0 = s_a * min_a + o_a
0 = s_b * max_b + o_b
w = s_a * max_a + o_a
h = s_b * min_b + o_b
本系统的解决方案是:
s_a = w / (max_a - min_a)
o_a = -w * min_a / (max_a - min_a)
s_b = -h / (max_b - min_b)
o_b = h * max_b / (max_b - min_b)
就是这样。如果你想要某个任意点的像素坐标 `(long, lat),那么请执行以下操作:
- 计算广义坐标
a
和b
(计算切线时注意使用弧度)。
- 使用线性映射将
a
和b
转换为像素坐标x
和y
并使用预先计算的参数。
反转
要从像素坐标获取纬度和经度,请执行以下操作:
计算广义坐标:
a = (x - o_a) / s_a
b = (x - o_b) / s_b
计算地理坐标:
longitude = a
latitude = arc tan (b)
再次提醒,注意radians/degrees。
请看看我的另一个问题,这个问题做错了here。
我需要做什么
我有一张我国地图的图像,我从 Google 地图中截取了地图的一部分,这意味着我知道所有角落的经纬度坐标。我的程序需要显示地图,并在地图上绘制目标,每个目标都有其唯一的经度和纬度,类似于雷达显示目标的方式。
问题
我的解决方案的问题在于它并没有真正使用真正的数学公式来获取 X、Y 位置。正如您在我的其他问题中看到的那样,它使用简单的除法和乘以最小值和最大值的比率。
这是方法:
protected Location getCoordinatesByGlobe(float latitude, float longitude) {
/**
* Work out minimum and maximums, clamp inside map bounds
*/
latitude = Math.max(mapLatitudeMin, Math.min(mapLatitudeMax, latitude));
longitude = Math.max(mapLongitudeMin, Math.min(mapLongitudeMax, longitude));
/**
* We need the distance from 0 or minimum long/lat
*/
float adjLon = longitude - mapLongitudeMin;
float adjLat = latitude - mapLatitudeMin;
float mapLongWidth = mapLongitudeMax - mapLongitudeMin;
float mapLatHeight = mapLatitudeMax - mapLatitudeMin;
float mapWidth = mapImage.getWidth();
float mapHeight = mapImage.getHeight();
float longPixelRatio = mapWidth / mapLongWidth;
float latPixelRatio = mapHeight / mapLatHeight;
int x = Math.round(adjLon * longPixelRatio) - 3;// these are offsets for the target icon that shows.. eedit laterrr @oz
int y = Math.round(adjLat * latPixelRatio) + 3; //
// turn it up
y = (int) (mapHeight - y);
return new Location(x, y);
}
我试过的
所以我有点想自己想一些合乎逻辑的事情,我该怎么做。我想出了一些并不真正有效的东西:
如果我们有左上角的坐标(经度和纬度),并且我们有我们想要显示的目标的坐标,那意味着我们可以distanceToPoint
知道有多少距离起点还有公里。
在那之后,我们需要知道到那个点的航向,所以我们做 calculateHeading
这给了我们到目标点的角度。
所以我们称 A 为起点(左上角)
float aLat = 33.49f;
float aLong = 33.69f;
我们的目标点我们称之为 b:
float bLat = 32f;
float bLong = 35f;
然后我们可以计算出从A到B的距离,单位公里:
double km = distanceTopPoint(aLat, aLong, bLat, bLong);
然后我们计算到点的角度:
double angle = calculateHeading(aLat, aLong, bLat, bLong);
如果我们有公里距离和角度,我们就可以知道经度和纬度的距离(以公里为单位):
int latDistance = (int) Math.round(km * Math.cos(angle));
int lonDistance = (int) Math.round(km * Math.sin(angle));
所以现在我大概知道经度到目标经度的距离,纬度也一样。但是我能用这些信息做什么呢?
我再次尝试思考,我发现我可以知道从左上角到右上角的距离(以公里为单位)以及左上角到左上角的距离。然后我可以 width / km
得到每像素的公里数。
但我真的不确定,我确定我做错了什么。 有什么想法吗?
墨卡托投影是圆柱投影,即广义坐标可以计算为:
a = longitude
b = tan(latitude)
这些是未缩放的坐标,即它们不对应于像素位置。
假设您有一张 w
x h
像素的图像,表示 (min_long, min_lat) - (max_long, max_lat)
之间的区域。可以使用上述公式将这些坐标转换为广义投影坐标,得到 (min_a, min_b) - (max_a, max_b)
。
现在您需要广义坐标到像素位置的线性映射。这个线性映射可以用四个参数(两个缩放参数和两个偏移参数)来表示:
x = s_a * a + o_a
y = s_b * b = o_a
因此,您需要找到这四个参数。你知道左上角有像素坐标 (0, 0)
和广义坐标 (min_a, max_b)
。同样的右下角。这为您提供了四个约束和一个线性方程组:
0 = s_a * min_a + o_a
0 = s_b * max_b + o_b
w = s_a * max_a + o_a
h = s_b * min_b + o_b
本系统的解决方案是:
s_a = w / (max_a - min_a)
o_a = -w * min_a / (max_a - min_a)
s_b = -h / (max_b - min_b)
o_b = h * max_b / (max_b - min_b)
就是这样。如果你想要某个任意点的像素坐标 `(long, lat),那么请执行以下操作:
- 计算广义坐标
a
和b
(计算切线时注意使用弧度)。 - 使用线性映射将
a
和b
转换为像素坐标x
和y
并使用预先计算的参数。
反转
要从像素坐标获取纬度和经度,请执行以下操作:
计算广义坐标:
a = (x - o_a) / s_a
b = (x - o_b) / s_b
计算地理坐标:
longitude = a
latitude = arc tan (b)
再次提醒,注意radians/degrees。