Ben Beri Ben Beri - 3 months ago 22
Java Question

Converting longitude and latitude coordinates to map pixels (X and Y) the right way

Please take a look at my other question which this was done incorrectly here.

What I need to do



I have an image of a map of my country, I took the piece of the map from Google Maps, so that means I know all of the corner's coordinates in longitude and latitude. My program needs to show up the map, and paint targets on it where each target has its only longitude and latitude, similar to how radar displays targets.

The problem



The problem with my solution is that it's not really using real mathematical formulas to get the X, Y position. It uses simple division and multiple by ratio with minimum and maximum as you can see in my other question.

this is the method:

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


What I have tried



So I was a bit with myself tried to think of something logical, on how can I do this. And I came up with something that doesn't really work exactly:

If we have the corner top-left for example coordinates (Longitude and latitude), and we have the coordinates of the target that we want to display, that means we can do
distanceToPoint
to know how many kilometers far from the start it is.

After that, we need to know the heading to that point, so we do
calculateHeading
which gives us the angle to the target point.

So lets call A the starting point (top-left corner)

float aLat = 33.49f;
float aLong = 33.69f;


And our target point we call it b:

float bLat = 32f;
float bLong = 35f;


And then we can calculate the distance from A to B in kilometers:

double km = distanceTopPoint(aLat, aLong, bLat, bLong);


And then we calculate the angle to the point:

double angle = calculateHeading(aLat, aLong, bLat, bLong);


And if we have the km distance and angle, we can know the distance in km for longitude and latitude:

int latDistance = (int) Math.round(km * Math.cos(angle));
int lonDistance = (int) Math.round(km * Math.sin(angle));


So now I probably have the distance from the longitude to the target's longitude and same for latitude. But what can I do with this information?

I tried thinking again, and I figured out that I can know the distance from the left top corner to the right top corner distance in km and same for top left corner to top left bottom corner. And then I can do
width / km
to get the km per pixel.

But I am really unsure, im sure that I am doing something wrong.
Any ideas?

Answer

The Mercator projection is a cylindrical projection, i.e. the generalized coordinates can be calculated as:

a = longitude
b = tan(latitude)

These are unscaled coordinates, i.e. they do not correspond to pixel positions.

Let's say you have an image of w x h pixels that represents the area between (min_long, min_lat) - (max_long, max_lat). These coordinates can be converted to generalized projected coordinates with the above formulas, which yields (min_a, min_b) - (max_a, max_b).

Now you need a linear mapping of the generalized coordinates to pixel positions. This linear mapping can be expressed with four parameters (two scaling parameters and two offset parameters):

x = s_a * a + o_a
y = s_b * b = o_a

Therefore, you need to find the four parameters. You know that the top left corner has pixel coordinates (0, 0) and generalized coordinates (min_a, max_b). Similarly for the bottom right corner. This gives you four constraints and a linear system of equations:

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

The solution of this system is:

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)

And this is it. If you want the pixel coordinates for some arbitrary point `(long, lat), then do the following:

  1. Calculate the generalized coordinates a and b (be careful to use radians when calculating the tangens).
  2. Use the linear map to convert a and b to pixel coordinates x and y with the pre-calculated parameters.

Inversion

To get latitude and longitude from pixel coordinates, do the following:

Calculate the generalized coordinates:

a = (x - o_a) / s_a
b = (x - o_b) / s_b

Calculate the geo-coordinates:

longitude = a
latitude = arc tan (b)

Again, be careful about radians/degrees.