BaldPenguin has asked for the
wisdom of the Perl Monks concerning the following question:
I need to find either some CPAN modules or at least a good math page to help me convert pixels to latlong on a map image. Given a map image with the dimensions of 200 x 200 and a known latlong center point, I am trying to compute the next center point if I pan the map 100 pixels in any direction.
Does anyone know of some Perl modules or even some reference pages to help me on my quest?
Update: For purposes of documenting the quest. The Map projection is a Mercator Projection and I have scale lookups.
Don
Everything I've learned in life can be summed up in a small perl script!
Re: Converting Pixels to LatLong by Albannach (Prior) on Feb 14, 2007 at 18:37 UTC 
This problem is not necessarily a simple one. Depending on the scale of the map, and the map projection used (the map is flat, the earth isn't), your task may be a relatively simple one or virtually impossible.
For starters, check out the Geo::Coordinates modules. You can convert the lat/lon to an appropriate UTM mapping given an ellipsoid. Then, as kyle points out, you will need to know how many metres to the pixel, or perhaps the lat/lon of another point on the map. With just one point and no scale, you cannot do what you want.

I'd like to be able to assign to an luser
 [reply] 
Re: Converting Pixels to LatLong by akho (Hermit) on Feb 14, 2007 at 19:00 UTC 
Things are even worse: it's not enough to know the pixels/meters ratio. You have to know a) which projection was used and b) its exact parameters. It seems obvious that a pixel near the North Pole on a rectangular map represents a larger ew distance than a pixel on the equator.
I'd read http://en.wikipedia.org/wiki/Map_projection if I were you.
BTW, you can compute the eastwest distance represented by 1 pixel on the equator easily, since you know the length of the equator and its length in pixels on your map. That will give you scale if you find which projection was used.
 [reply] 
Re: Converting Pixels to LatLong by akho (Hermit) on Feb 14, 2007 at 20:03 UTC 
If it's a Mercator projection, then
long = x
lat = atan(sinhy)
Where lat, long, x, y of the center point == 0.
Most probably.
This should be adjusted to scale, of course. This can be done for long, as it's just linear. Lat is a bit harder, however. You should find a point with known lat_real on your map, then find it's lat_formula using the above formula. If the map was linearly stretched (which it probably was), you can use
lat = (lat_real / lat_formula) * atan(sinhy)
I strongly advise you to check these formulas for sanity before using them, though (just check several more points, like cities, peninsulas or whatever is visible on your map).  [reply] 
Re: Converting Pixels to LatLong by blue_cowdawg (Monsignor) on Feb 14, 2007 at 18:34 UTC 
Does anyone know of some Perl modules or even some reference pages to help me on my quest?
Google is your friend. I did a Google search and found
this page
which shows how to take two latitudes and longitudes and
calculate distance.
Now the problem becomes one of calculating the distance
represented by one pixel and applying some algebra to the
formula pointed out above and you have your answer.
Hope this is a help...
Peter L. Berghold  Unix Professional
Peter at Berghold dot Net; AOL IM redcowdawg Yahoo IM: blue_cowdawg
 [reply] 
Re: Converting Pixels to LatLong by shotgunefx (Parson) on Feb 14, 2007 at 23:46 UTC 
 [reply] 
Re: Converting Pixels to LatLong by moklevat (Priest) on Feb 14, 2007 at 20:15 UTC 
 [reply] 
Re: Converting Pixels to LatLong by ikegami (Pope) on Feb 14, 2007 at 19:03 UTC 
That depends on the projection used to obtain the map image.
This post is wrong from this point on.
If you draw a three points on the map such that
A_{y} == B_{y} && A_{x} != B_{x}
A_{x} == C_{x} && A_{y} != C_{y}
+=====================+
# #
# AB #
#  #
#  #
# C #
# #
+=====================+
Is A_{lat} == B_{lat} && A_{long} == C_{long}?

If so, every pixel represents the same long and lat difference.
Pixel_{lat} = (B_{lat}  A_{lat}) / (B_{x}  A_{x})
Pixel_{long} = (C_{long}  A_{long}) / (C_{y}  A_{y})

If not, the math gets a bit more complicated, but it's still pretty straightforward. You'll probably need a fourth point D such that
D_{x} == B_{x} && D_{y} == C_{y}
+=====================+
# #
# AB #
#   #
#   #
# CD #
# #
+=====================+
There surely exists modules to help you here.
 [reply] [d/l] [select] 

No.
If the Mercator projection was used, then A_{lat} == B_{lat} && A_{long} == C_{long}, but pixels near the poles and pixels on the equator represent very different long values.
It's hard to find out which projection was used if you only look at a finite number of points.
Finding a map with a known projection for use in the program seems a better way to do it.
 [reply] 

Shoot! I mixed up distances with long/lat somewhere along the way.
 [reply] 
Re: Converting Pixels to LatLong by kyle (Abbot) on Feb 14, 2007 at 18:26 UTC 
In addition to the data you've described, you'd also have to know the scale of the map. What real distance is represented by those 100 pixels?
 [reply] 
Re: Converting Pixels to LatLong by Moron (Curate) on Feb 15, 2007 at 13:41 UTC 
IMO The most important module for this purpose is Math::Trig. Mercator projects from the centre of the earth onto an infinite cylinder of equatorial radius running NorthSouth, scaling being only applicable on the projection (degrees = angles can't have scale). Longitudinal recentering means simply moving the arc difference between two arctangents whereas lateral recentering is linear. Apart from having the required tan and atan for Mercator projection work, the module also has more specific functions for the lazier mind for converting between spherical and cylindrical coordinates.
It isn't possible for the recentering in any direction to traverse either pole but there are potential issues if suitable measures are not taken when crossing zero and the extreme lateral boundaries. This can be made transparent for latitude by converting to and from modulo 180, for example:
(updated to include earth radius in mercator projection algorithm)
# untested
use Math::Trig;
my $Radius = 6378569.135 # in metres, so need $scale also in metres
sub ReCentre {
my ( $lat, $long, $direction, $scale ) = @_;
# scale should be passed as the change in degrees
# of latitude afforded by a 100 pixel move
#  this routine then handles the effects of
# nonlinear longitude automatically
$lat %= 180; # transform from signed to unsigned ring element
# handle lateral move
if ( $direction eq 'W' ) {
$direction = 'E';
$scale = $scale;
}
if ( $direction eq 'E' ) {
return ( Ensign( $lat + 100 * $scale), $long );
# i.e. move while in unsigned transformation
# before converting back to signed
}
# handle logitudinal move
$long = tan( $long*pi()/180.0 ) * $Radius; # Project Mercatorially
$scale = $scale if ( $direction eq 'S' );
return ( $lat, 180.0 * atan( $long + 100*$scale )/pi() );
# i.e. move scaled distance while on Mercator projection,
# and then reverse projection to get degrees.
}
sub Ensign { # convert back from unsigned ring element to degrees of
+latitude
( $_[0] > 90 ) ? $_[0]  180 : $_[0];
}
 [reply] [d/l] 
Re: Converting Pixels to LatLong by punkish (Priest) on Feb 15, 2007 at 20:07 UTC 
The short answer is, you can't, not without inaccuracies. That is because lat/lon is not a projection and is not cartesian. If your map is already Mercator (and you say it is), then you can use the following code 
my $h_img_dims => {img_width => 200, img_height => 200};
my $h_map_bbox => {map_minx => 2090491, map_miny => 482208, map_maxx
+ => 2224055, map_maxy => 587708};
my $h_new_center => {x => 200, y => 100}; # for pan right/East
#my $h_new_center => {x => 0, y => 100}; # for pan left/West
#my $h_new_center => {x => 100, y => 200}; # for pan bottom/South
#my $h_new_center => {x => 100, y => 0}; # for pan up/North
# Convert the mouse click to earth coordinates
my ($x, $y) = _pixel2earth(
h_img_dims => $h_img_dims,
h_map_bbox => $h_map_bbox,
h_new_center => $h_new_center,
);
sub _pixel2earth {
my (%args) = @_;
my $h_img_dims = $args{h_img_dims};
my $h_map_bbox = $args{h_map_bbox};
my $h_new_center = $args{h_new_center};
# Mouse clicks equivalent in earth coordinates
return (
$h_map_bbox>{map_minx} + ($h_new_center>{x} * ( ($h_map_bbox>{m
+ap_maxx}  $h_map_bbox>{map_minx}) / $h_img_dims>{img_width} )),
$h_map_bbox>{map_miny} + ($h_new_center>{y} * ( ($h_map_bbox>{m
+ap_maxy}  $h_map_bbox>{map_miny}) / $h_img_dims>{img_height} ))
);
}
If you want to display a rectangular image, but measure nonrect coordinates such as lat/lon, you will have to project on the fly. Look at Proj.4, or better yet, look at MapServer which can do the image generation for you as well.

when small people start casting long shadows, it is time to go to bed
 [reply] [d/l] 

