Mombu the Programming Forum sponsored links

Go Back   Mombu the Programming Forum > Programming > LAT/LON to X/Y and back
User Name
Password
REGISTER NOW! Mark Forums Read

sponsored links


Reply
 
1 16th October 22:54
rich
External User
 
Posts: 1
Default LAT/LON to X/Y and back



I don't know if this is the right place to post this problem or not....but
hre goes...

I have a 1024 x 1024 form with nothing on it. I have the code below in it
that takes some LAT and LON coordinates and converts them to screen
coordinates and draws them on the screen with the LAT/LON and the screen
coordinates in text. This all works very nicely. The problem I am having
is trying to figure out how to go in the opposite direction. In other
words, if I click on the form I want to be able to show the LAT/LON in the
form caption. Can anyone figure out how to do this and provides me with a
working example?

While we are at it I welcome any comments that might help me improve on the
code I already have here. I know I shouldn't but for the sake of getting
this to work I have all of the coordinates hard coded in this example.

*** I got the math to do this from Wolfram Research Resource Library based
on reasearch by J.P Snyder USGS Pro Paper 1395
Map Projections -- A Working Manual. I only wish I was smart enough to have
come up with them myself =)

Thanks for any and all assistance.

Rich

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Math, StdCtrls;

type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure FormClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
function ValidateLon(VAR Lon:Extended): Boolean;
procedure ValidateLat(VAR Lat:Extended);
procedure PolyconicLLtoCartesian(Lat, Lon: Extended;
VAR CartX, CartY:
integer);
end;

var
Form1: TForm1;
origin_X, origin_Y :integer;
MCECanvas:TCanvas;
MCELatitude :Extended = 30.407; //Default Latitude for Center
MCELongitude :Extended = -88.921; //Default Longitude for Center

//the shifts required to make the key location in the middle of the
screen
//also used to track user offsets
LatShift : Extended = 0.0;
LonShift : Extended = 0.0;
ScaleExpansion : Integer = 2; //shown in submenu area EXP=
ScaleMultiplier : Double = 100.0; //used to expand map to correct size

implementation

{$R *.dfm}

(************************************************* *****************************)
(* This function makes sure the Longitude is within the range of
*)
(* 180.0 to -18.0 and returns true if the Longitude sent was OK to start
with *)
(* If it has to adjust the Longitude, it will return false.
*)
(************************************************* *****************************)
function TForm1.ValidateLon(VAR Lon:Extended): Boolean;
begin
Result := True; //default is a valid Lon

while (Lon > 180.0) do //make sure Lon is a valid number
begin
Lon := Lon - 360.0;
Result := False; //return of false means it was invalid
end;

while (Lon < -180.0) do
begin
Lon := Lon + 360.0;
Result := False;
end;
end;
(************************************************* *****************************)
procedure TForm1.ValidateLat(VAR Lat:Extended);
begin
while (Lat > 90.0) do
Lat := Abs(Lat - 180.0);

while (Lat < -90.0) do
Lat := -(Lat + 180.0);
end;

(************************************************* *****************************)
(* Receives latitude and logitude and uses them as polyconic coordinate
*)
(* values and returns X,Y scaled to Cartesian coodinate system with the
*)
(* lesson location longitude centered.
*)
(************************************************* *****************************)
procedure TForm1.PolyconicLLtoCartesian (Lat, Lon: Extended;VAR CartX,
CartY: integer);
const
PrevLonIsValid : Boolean = True;
var
r : Extended;
CotanLat, LonSinLat : Extended;
NextLonIsValid : Boolean;
begin
r := 58.0; //arbitrary scaler to make it look right

//spin the world to make current location's longitude
// in the center of the screen. DO NOT shift latitude like this since it
// is to scale only at the center longitude in this type projection.
Lon := Lon - LonShift;

//if Lon is a valid number ValidateLon returns true
NextLonIsValid := ValidateLon(Lon);
//check to see if the previous and next numbers cross 180 degrees from the
//central meridian
//save info for next time

// These evil calculations are brought to you by Wolfram Research Resource
// Library based on reasearch by J.P Snyder USGS Pro Paper 1395
// Map Projections -- A Working Manual

//pre-calculate to make CartX, CartY formula easier
LonSinLat := Lon * Sin(DegToRad(Lat));

//don't send a 0 to Cotan function
if ((Lat > 0.0) OR (Lat < 0.0)) then
begin
CotanLat := Cotan(DegToRad(Lat));
CartX := Round(ScaleMultiplier * (r * CotanLat *
Sin(DegToRad(LonSinLat))));
end
else
begin
CotanLat:= 0.000001;
CartX := Round(ScaleMultiplier * (r * DegToRad(Lon)));
end;

CartY := Round(ScaleMultiplier * (r * (DegToRad(Lat - LatShift) +
(CotanLat * (1.0 - Cos(DegToRad(LonSinLat)))))));

end;
(************************************************* *****************************)

procedure TForm1.FormCreate(Sender: TObject);
begin
MCECanvas := TCanvas.Create;
//get the handle for the Tod frame
MCECanvas.Handle := GetDC(Handle);

origin_X := (Width div 2);
origin_Y := (Height div 2);

//Logical units are mapped to arbitrary units with equally scaled axes
SetMapMode(MCECanvas.Handle, MM_ISOTROPIC);

SetWindowExtEx(MCECanvas.Handle, Width, Height, nil);

//the negative Frame.Height causes positive y to be up
SetViewportExtEx(MCECanvas.Handle, Width, -Height, nil);

//set the origin in middle of frame
SetViewportOrgEx(MCECanvas.Handle, origin_X, origin_Y, nil);

MCECanvas.Brush.Style := bsClear;//bsSolid; //bsClear to see behind text
MCECanvas.Brush.Color := clBlack; //color behind TextOut
MCECanvas.Font.Name := 'System';
MCECanvas.Font.Color := clBlack;

MCECanvas.Pen.Color := clBlack;
MCECanvas.Pen.Style := psSolid;
MCECanvas.Pen.Width := 2;

LatShift := MCELatitude;
LonShift := MCELongitude;
end;

procedure TForm1.FormPaint(Sender: TObject);
var
Here : TPoint;
MyLat, MyLon : extended;
IntLatLon : TPoint;
mid : TPoint;
begin
//Clear off the canvas
MCECanvas.Brush := Brush;
MCECanvas.FillRect(ClientRect);

//get the target's lat/lon and convert to Cartesian coords
PolyconicLLtoCartesian(30.407, -88.921, Here.x, Here.y);
MCECanvas.Ellipse(Here.X, Here.Y, Here.X + 10, Here.Y + 10);
mid.x := (Here.x + 5);
mid.y := (Here.y + 5);
MCECanvas.Font.Color := clRed;
MCECanvas.TextOut(Here.x + 10, Here.y + 10, IntToStr(mid.X) + ':' +
IntToStr(mid.Y));
MCECanvas.Font.Color := clBlue;
MCECanvas.TextOut(Here.x - 40, Here.y - 10, '30.407 : -88.921');

PolyconicLLtoCartesian(34.682, -88.921, Here.x, Here.y);
MCECanvas.Ellipse(Here.X, Here.Y, Here.X + 10, Here.Y + 10);
mid.x := (Here.x + 5);
mid.y := (Here.y + 5);
MCECanvas.Font.Color := clRed;
MCECanvas.TextOut(Here.x + 10, Here.y + 10, IntToStr(mid.X) + ':' +
IntToStr(mid.Y));
MCECanvas.Font.Color := clBlue;
MCECanvas.TextOut(Here.x - 40, Here.y - 10, '34.682 : -88.921');

PolyconicLLtoCartesian(34.58, -94.086, Here.x, Here.y);
MCECanvas.Ellipse(Here.X, Here.Y, Here.X + 10, Here.Y + 10);
mid.x := (Here.x + 5);
mid.y := (Here.y + 5);
MCECanvas.Font.Color := clRed;
MCECanvas.TextOut(Here.x + 10, Here.y + 10, IntToStr(mid.X) + ':' +
IntToStr(mid.Y));
MCECanvas.Font.Color := clBlue;
MCECanvas.TextOut(Here.x - 40, Here.y - 10, '34.58 : -94.086');

PolyconicLLtoCartesian(34.58, -83.757, Here.x, Here.y);
MCECanvas.Ellipse(Here.X, Here.Y, Here.X + 10, Here.Y + 10);
mid.x := (Here.x + 5);
mid.y := (Here.y + 5);
MCECanvas.Font.Color := clRed;
MCECanvas.TextOut(Here.x + 10, Here.y + 10, IntToStr(mid.X) + ':' +
IntToStr(mid.Y));
MCECanvas.Font.Color := clBlue;
MCECanvas.TextOut(Here.x - 40, Here.y - 10, '34.58 : -83.757');

PolyconicLLtoCartesian(26.128, -88.922, Here.x, Here.y);
MCECanvas.Ellipse(Here.X, Here.Y, Here.X + 10, Here.Y + 10);
mid.x := (Here.x + 5);
mid.y := (Here.y + 5);
MCECanvas.Font.Color := clRed;
MCECanvas.TextOut(Here.x + 10, Here.y + 10, IntToStr(mid.X) + ':' +
IntToStr(mid.Y));
MCECanvas.Font.Color := clBlue;
MCECanvas.TextOut(Here.x - 40, Here.y - 10, '26.128 : -88.922');

PolyconicLLtoCartesian(26.043, -93.655, Here.x, Here.y);
MCECanvas.Ellipse(Here.X, Here.Y, Here.X + 10, Here.Y + 10);
mid.x := (Here.x + 5);
mid.y := (Here.y + 5);
MCECanvas.Font.Color := clRed;
MCECanvas.TextOut(Here.x + 10, Here.y + 10, IntToStr(mid.X) + ':' +
IntToStr(mid.Y));
MCECanvas.Font.Color := clBlue;
MCECanvas.TextOut(Here.x - 40, Here.y - 10, '26.043 : -93.655');

PolyconicLLtoCartesian(26.043, -84.188, Here.x, Here.y);
MCECanvas.Ellipse(Here.X, Here.Y, Here.X + 10, Here.Y + 10);
mid.x := (Here.x + 5);
mid.y := (Here.y + 5);
MCECanvas.Font.Color := clRed;
MCECanvas.TextOut(Here.x + 10, Here.y + 10, IntToStr(mid.X) + ':' +
IntToStr(mid.Y));
MCECanvas.Font.Color := clBlue;
MCECanvas.TextOut(Here.x - 40, Here.y - 10, '26.043 : -84.188');

MCECanvas.Brush.Color := clRed;
MCECanvas.Ellipse(0, 0, 10, 10);
end;

procedure TForm1.FormClick(Sender: TObject);
begin
//need to show the Lat and Lon of the loacation where the mouse was
clicked
end;

end.
  Reply With Quote


  sponsored links


2 16th October 22:59
frederico pissarra
External User
 
Posts: 1
Default LAT/LON to X/Y and back



Is this correct? r * Longitude (in degrees)?!

[]s
Fred
  Reply With Quote
3 16th October 23:00
jd
External User
 
Posts: 1
Default LAT/LON to X/Y and back


Using the OnClick event would require you to get the current
mouse coords and convert them to client-relative. OTOH, if
you use OnMouseDown instead, that event has X,Y parameters
that you can use to calculate what you want to know. You might
also consider using OnMouseMove (if the Shift parameter
contains ssRight) to allow the user to drag the mouse around
while displaying the Lon/Lat.


That would require digesting your calculations which I'm not
inclined to do. Presumably, it would be the reverse of how you
calculate the X,Y given a Lon/Lat.

~ JD
  Reply With Quote
4 16th October 23:00
nils haeck
External User
 
Posts: 1
Default LAT/LON to X/Y and back


First of all, I'd clean up the conversion a bit. There's a mess of
"degtorads" everywhere now. Something like this:

procedure PolyconicLLtoXY(Lat, Lon: double; var X, Y: double);
var
LonR, LatR, LatShiftR, SinLat, CotLat: double;
begin
// Avoid wraparound for longitude
while Lon < LonShift - 180 do
Lon := Lon + 360;
while Lon > LonShift + 180 do
Lon := Lon - 360;

// Degree to radian conversion
LonR := DegToRad(Lon - LonShift);
LatR := DegToRad(Lat);
LatShiftR := DegToRad(LatShift);

// Polyconic projection
SinLat := sin(LatR);
if abs(LatR) > 1E-6 then begin
CotLat := Cotan(LatR);
X := CotLat * Sin(LonR * SinLat);
end else begin
CotLat := 1E-6;
X := LonR;
end;
Y := LatR - LatShiftR + CotLat * (1 - cos(LonR * SinLat));
end;

After obtaining X,Y (double precision), you can multiply them by some
constant to make them suitable for screen coordinates.

Going backwards (XY to LL) is much more difficult because the formula is not
linear. You'll have to estimate the LL from XY by making an educated guess
using the slopes of the function, then iterate until you're at the exact
position. This process is called "Newton-Rhapson approximation with
linearization". Consult e.g. Numerical Recipes or other literature to find
out exactly how it's done.

Here's an idea:

procedure PolyconicXYtoLLEst(X, Y, LatBase, LonBase: double; var Lat, Lon,
Err: double);
var
X0, Y0, X1, Y1, T, dFX, dFY, Xest, Yest, Delta: double;
begin
// Linearization
Delta := 1E-3;
PolyconicLLtoXY(LatBase, LonBase, X0, Y0);
PolyconicLLtoXY(LatBase + Delta, LonBase, X1, T);
PolyconicLLtoXY(LatBase, LonBase + Delta, T, Y1);
// Estimate based on f(L) ~ F(L0) + dF/dL * (L-L0)
dFX := (X1 - X0)/Delta;
dFY := (Y1 - Y0)/Delta;
Lat := LatBase + dFX * (X - X0);
Lon := LonBase + dFY * (Y - Y0);
// Error
PolyconicLLtoXY(Lat, Lon, Xest, Yest);
Err := sqrt(sqr(Xest - X) + sqr(Yest - Y));
end;

procedure PolyconicXYtoLL(X, Y: double; var Lat, Lon: double);
var
LatBase, LonBase, Err: double;
begin
LatBase := LatShift;
LonBase := LonShift;
repeat
PolyconicXYtoLLEst(X, Y, LatBase, LonBase, Lat, Lon, Err);
LatBase := Lat;
LonBase := Lon;
until Err < 1E-8;
end;

I didn't test this so there might be (huge) bugs, but this is the general
idea. Also note that X,Y is *not* the same yet as the Mou***, MouseY. You'll
have to do an additional conversion (linear) for these first.

Hope that helps,

Nils Haeck
http://www.simdesign.nl
  Reply With Quote
5 20th October 14:26
rbwinston
External User
 
Posts: 1
Default LAT/LON to X/Y and back


Get Snyder's paper. It has algorithms for conversions in both
directions.
  Reply With Quote
6 20th October 14:26
jim dodd
External User
 
Posts: 1
Default LAT/LON to X/Y and back


He may be referring to the US Geological Survey Bulletin 1395 by John P.
Snyder.

http://pubs.er.usgs.gov/usgspubs/pp/pp1395

This supersedes the old 1532 bulletin that I used for years when I
worked for the Survey. It has not only the algorithms but worked
examples and even the history of the various projections.

Regards,
Jim Dodd
Onset Computer Corp.
  Reply With Quote


  sponsored links


Reply


Thread Tools
Display Modes




Copyright © 2006 SmartyDevil.com - Dies Mies Jeschet Boenedoesef Douvema Enitemaus -
666