13

I have the Lat/Long value of an small area in Melbourne; -37.803134,145.132377 and also a flat image of that,that I exported from openstreet map( Osmarender Image ). Image width : 1018 and Height:916

I would like to be able to convert, using C++, the Lat/Long to an X,Y coordinate where the point would reflect the location.

I used various formula's that I found in web like one given below but nothing helps.

var y = ((-1 * lat) + 90) * (MAP_HEIGHT / 180);
var x = (lon + 180) * (MAP_WIDTH / 360);

It would be of great help if any one give me clear explanation of how to do this . Any code would be much appreciated.

Verve Innovation
  • 1,886
  • 6
  • 26
  • 48

4 Answers4

23

You need more information than just a single lat/lon pair to be able to do this.

At this stage, the information you have provided is missing two things:

  • how large an area does your image cover (in terms of lat/lon)? Based on what you've provided, I don't know if the image shows an area a metre wide or a kilometre wide.
  • what spot on your image does your reference coordinate (-37.803134, 145.132377) refer to? Is it one of the corners? In the middle somewhere?

I'm also going to assume that your image is aligned north/south - for example, it doesn't have north pointing towards the top left corner. That would tend to complicate things.

The easiest approach is to figure out exactly what lat/lon coordinates correspond to the (0, 0) pixel and the (1017, 915) pixel. Then you can figure out the pixel corresponding to a given lat/lon coordinate through interpolation.

To briefly outline that process, imagine that your (-37.803134, 145.132377) lat/lon corresponds to your (0, 0) pixel, and that you've discovered that your (1017, 915) pixel corresponds to the lat/lon (-37.798917, 145.138535). Assuming the usual convention with pixel (0, 0) in the bottom-left corner, this means north is up in the image.

Then, if you are interested in the target coordinate (-37.801465, 145.134984), you can figure out the corresponding number of pixels up the image it is as follows:

pixelY = ((targetLat - minLat) / (maxLat - minLat)) * (maxYPixel - minYPixel)
       = ((-37.801465 - -37.803134) / (-37.798917 - -37.803134)) * (915 - 0)
       = 362.138

That is, the corresponding pixel is 362 pixels from the bottom of the image. You can then do exactly the same for the horizontal pixel placement, but using longitudes and X pixels instead.

The part ((targetLat - minLat) / (maxLat - minLat)) works out how far you are between the two reference coordinates, and gives 0 to indicate that you are at the first one, 1 to indicate the second one, and numbers in between to indicate locations in between. So for example, it would produce 0.25 to indicate that you are 25% of the way north between the two reference coordinates. The last bit converts that into the equivalent pixels.

HTH!

EDIT Ok, based on your comment I can be a little more specific. Given that you seem to be using the top left corner as your primary reference point, I'll use the following definitions:

minLat = -37.803134
maxLat = -37.806232
MAP_HEIGHT = 916

Then, if we use an example coordinate of (-37.804465, 145.134984), the Y coordinate of the corresponding pixel, relative to the top-left corner, is:

pixelY = ((targetLat - minLat) / (maxLat - minLat)) * (MAP_HEIGHT - 1)
       = ((-37.804465 - -37.803134) / (-37.806232 - -37.803134)) * 915
       = 393.11

Therefore, the corresponding pixel is 393 pixels down from the top. I'll let you work out the horizontal equivalent for yourself - it's basically the same. NOTE The -1 with the MAP_HEIGHT is because if you start at zero, the maximum pixel number is 915, not 916.

EDIT: One thing I'd like to take the opportunity to point out is that this is an approximation. In reality, there isn't a simple linear relationship between latitude and longitude coordinates and other forms of Cartesian coordinates for a number of reasons including the projections that are used while making maps, and the fact that the Earth is not a perfect sphere. In small areas this approximation is close enough as to make no significant difference, but on larger scales discrepancies may become evident. Depending on your needs, YMMV. (My thanks to uray, whose answer below reminded me that this is the case).

Mac
  • 14,085
  • 9
  • 61
  • 77
  • To Answer your question that you have asked above..how large an area does your image cover?It covers roughtly 2 - 4 miles.what spot on your image does your reference coordinate (-37.803134, 145.132377) refer to? Is it one of the corners? In the middle somewhere? Its the Top left corner.Complete coordinate is (-37.803134,-37.806232,145.132377,145.136733 ) . – Verve Innovation Feb 10 '11 at 05:08
  • Hey thanks for your answer . Have an small doubt.According to his formula pixelY = ((targetLat - minLat) / (maxLat - minLat)) * (MAP_HEIGHT - 1) ,if I am trying to locate (-37.803134,145.132377) which is nothing but same as my top left it should be 0 isnt it ??? But its not ?? ((-37.803134 - -37.806232) / (-37.803134 - -37.806232)) * 915 = 915 – Verve Innovation Feb 11 '11 at 03:45
  • Right you are, good catch - very sorry, I seem to have mixed myself up posting that update. Swap the values for minLat and maxLat and you should be fine. – Mac Feb 11 '11 at 12:12
  • @ITion: I did, in my previous comment. I've also changed my answer to suit now. – Mac Feb 14 '11 at 00:23
  • @Mac Sorry for bothering you .I just want to make it clear . if I am trying to locate (-37.803134,145.132377) which is same as my top left corner coordinates (i.e) ((-37.803134 - -37.806232) / (-37.803134 - -37.806232)) * 915 gives 915 as output which is nothing but 915 pixels down from the top .whereas It should be Zero because My Target Coordinate is same as top left boundary coordinate. Note : my pixel (0, 0) is Top Left corner . – Verve Innovation Feb 14 '11 at 00:44
  • @ITion: yep, as I said, the numbers I mentioned in my original edit are wrong. The correct formula for that is ((-37.803134 - -37.803134) / (-37.806232 - -37.803134)) * 915. This is exactly the same as before, but with minLat and maxLat exchanged (as per my above comment), and is also the same formula as in my recently edited answer. – Mac Feb 14 '11 at 01:33
  • @Mac so thats nothing but (targetLat - maxLat) / (minLat- maxLat) ????according to ur edit ((-37.803134(targetLat) - -37.803134(MaxLat)) / (-37.806232(MinLat) - -37.803134(MaxLat))) * 915 – Verve Innovation Feb 14 '11 at 01:51
  • @ITion: yep. I'd rather change the values of minLat and maxLat than change the formula (as you've described), but you could look at it that way if you want. – Mac Feb 14 '11 at 01:55
  • So to sum up,let put this way ... float pixelY = ((targetLat - maxLat) / (minLat - maxLat))* (MAP_HEIGHT - 1); float pixelX = ((targetLong - minLong) / (maxLong - minLong)) * (MAP_WIDTH - 1); Thank you very much for your help – Verve Innovation Feb 14 '11 at 04:01
9

if you're looking for accurate conversion of geodetic (lot,lan) into your defined cartesian coordinate (x,y meters from reference point), you can do with my code snippet here, this function will accept geodetic coordinate in radian and output the result in x,y

input:

  • refLat,refLon : geodetic coordinate which you defined as 0,0 in cartesian coordinate (the unit is in radian)
  • lat,lon : geodetic coordinate which you want to calculate its cartesian coordinate (the unit is in radian)
  • xOffset,yOffset : the result in cartesian coordinate x,y (the unit is in meters)

code:

#define GD_semiMajorAxis 6378137.000000
#define GD_TranMercB     6356752.314245
#define GD_geocentF      0.003352810664

void geodeticOffsetInv( double refLat, double refLon,
                        double lat,    double lon, 
                        double& xOffset, double& yOffset )
{
    double a = GD_semiMajorAxis;
    double b = GD_TranMercB;
    double f = GD_geocentF;

    double L     = lon-refLon
    double U1    = atan((1-f) * tan(refLat));
    double U2    = atan((1-f) * tan(lat));
    double sinU1 = sin(U1); 
    double cosU1 = cos(U1);
    double sinU2 = sin(U2);
    double cosU2 = cos(U2);

    double lambda = L;
    double lambdaP;
    double sinSigma;
    double sigma;
    double cosSigma;
    double cosSqAlpha;
    double cos2SigmaM;
    double sinLambda;
    double cosLambda;
    double sinAlpha;
    int iterLimit = 100;
    do {
        sinLambda = sin(lambda);
        cosLambda = cos(lambda);
        sinSigma = sqrt((cosU2*sinLambda) * (cosU2*sinLambda) + 
                        (cosU1*sinU2-sinU1*cosU2*cosLambda) * 
                        (cosU1*sinU2-sinU1*cosU2*cosLambda) );
        if (sinSigma==0)
        {
            xOffset = 0.0;
            yOffset = 0.0;
            return ;  // co-incident points
        }
        cosSigma    = sinU1*sinU2 + cosU1*cosU2*cosLambda;
        sigma       = atan2(sinSigma, cosSigma);
        sinAlpha    = cosU1 * cosU2 * sinLambda / sinSigma;
        cosSqAlpha  = 1 - sinAlpha*sinAlpha;
        cos2SigmaM  = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
        if (cos2SigmaM != cos2SigmaM) //isNaN
        {
            cos2SigmaM = 0;  // equatorial line: cosSqAlpha=0 (§6)
        }
        double C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
        lambdaP = lambda;
        lambda = L + (1-C) * f * sinAlpha *
            (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
    } while (fabs(lambda-lambdaP) > 1e-12 && --iterLimit>0);

    if (iterLimit==0)
    {
        xOffset = 0.0;
        yOffset = 0.0;
        return;  // formula failed to converge
    }

    double uSq  = cosSqAlpha * (a*a - b*b) / (b*b);
    double A    = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
    double B    = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
    double deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
        B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
    double s = b*A*(sigma-deltaSigma);

    double bearing = atan2(cosU2*sinLambda,  cosU1*sinU2-sinU1*cosU2*cosLambda);
    xOffset = sin(bearing)*s;
    yOffset = cos(bearing)*s;
}
uray
  • 10,130
  • 9
  • 46
  • 73
  • Hey thanks for your input . But What formula is this ?? . Its full of Maths and It's hard to understand . If u tell me how its done or some reference link it will be easy for me to understand . – Verve Innovation Feb 14 '11 at 22:57
  • @ITion: rather than converting a difference between lat/lon coordinates to a position in pixels, it instead changes it to a difference in metres. If I understand it correctly, it's more complicated because it takes into account the fact that the planet is not a perfect sphere - something that I think is probably not a big deal given the small area you are working with. – Mac Feb 15 '11 at 05:19
  • @Mac: True, for the area that ITion is working with, you can simplify things by forgetting that earth is really some elipsoid, but once you start to deal with larger areas, you can no longer ingore that fact... then you have to figure out which elipsoid your going to believe earth is... and that is a whole other disscussion... but one of the more popular ones is WGS-84 – diverscuba23 Feb 18 '11 at 22:15
3

I wouldn't worry too much about earth curvature. I haven't used openstreetmap before, but I've just had a quick look and it appears that they use a Mercator projection.

Which simply means that they've flattened the planet for you onto a rectangle, making X proportional to Longitude, and Y almost exactly proportional to Latitude.

So you can go ahead and use Mac's simple formulas and you will be very accurate. Your Latitude will be out by much less then a pixel's worth for the small map you are dealing with. Even on a map the size of Victoria you would only get an error of 2-3%.

diverscuba23 pointed out that you have to choose an ellipsoid... openstreetmap uses WGS84, and so does most modern mapping. However, beware that many maps in Australia use the older AGD66 which can differ by 100-200 metres or so.

Jake R
  • 116
  • 5
2
double testClass::getX(double lon, int width)
{
    // width is map width
    double x = fmod((width*(180+lon)/360), (width +(width/2)));

    return x;
}

double testClass::getY(double lat, int height, int width)
{
    // height and width are map height and width
    double PI = 3.14159265359;
    double latRad = lat*PI/180;

    // get y value
    double mercN = log(tan((PI/4)+(latRad/2)));
    double y     = (height/2)-(width*mercN/(2*PI));
    return y;
}
AngryDuck
  • 3,561
  • 9
  • 50
  • 82