draw geographic object on windows form.

L

Laoballer

I have some lines that are described by geographic coordinates (x1,y1)
to (x2,y2) in UTM coordinate. What I'm trying to do is draw these
lines onto a windows form to visualize what they look like. I can
draw lines onto the form using the code below. my question is how do
I convert my UTM coordinates to a form that can be drawn on the
screen?

Bitmap DrawArea;
DrawArea = new Bitmap(pictureBox1.Size.Width,
pictureBox1.Size.Height);
pictureBox1.Image = DrawArea;
Graphics g;

g = Graphics.FromImage(DrawArea);

Pen mypen = new Pen(Color.Black);

g.DrawLine(mypen, 0, 0, 20, 6*i);

pictureBox1.Image = DrawArea;

g.Dispose();
 
P

Peter Duniho

Laoballer said:
I have some lines that are described by geographic coordinates (x1,y1)
to (x2,y2) in UTM coordinate. What I'm trying to do is draw these
lines onto a windows form to visualize what they look like. I can
draw lines onto the form using the code below. my question is how do
I convert my UTM coordinates to a form that can be drawn on the
screen?

You should read up on UTM coordinates and conversion to plain Cartesian
coordinates. You'll need to really understand that well in order to
have success.

That said, the basic idea is to scale and translate the coordinates as
necessary to fit the area you want to draw in. Assuming you want to
draw "north up", you'll also have to reverse the Y axis, since in screen
coordinate increasing values go down.

You can do this transformation more easily by setting the Transform
property of the Graphics instance to which you're drawing. For example:

Matrix GetTransform(Rectangle rectWorld, Rectangle rectView)
{
float scaleX = (float)rectView.Width / rectWorld.Width,
scaleY = (float)rectView.Height / rectWorld.Height;
float dxOffset = rectView.Left - rectWorld.Left * scaleX,
dyOffset = rectView.Top - rectWorld.Top * scaleY;

Matrix matrix = new Matrix();

matrix.Translate(dxOffset, dyOffset);
matrix.Scale(scaleX, scaleY);

return matrix;
}

Pass to the above method the rectangle defining the boundaries, in world
coordinates, that lines up visually with the rectangle, in
control/client/view coordinates (i.e. the screen coordinates for the
area where the drawing should occur).

To use this method with UTM, you'll have to have a way of getting for a
given UTM zone the exact range of grid positions for that zone.
Assuming you've gotten that, then you can create a Rectangle instance
where the position (upper-left corner…i.e. the Location) is set to the
UTM grid position coordinates for the northwest corner (again,
upper-left) of the UTM zone.

The width and height of the Rectangle should be calculated based on the
southeast corner. That is, width is "easternmost easting minus
westernmost easting" and height is "southernmost northing minus
northernmost northing". Note that this will necessarily result in a
negative height for the Rectangle. This is exactly what you want, in
order to "flip" the coordinates as I described.

Assuming you have a method that returns such a Rectangle instance:

Rectangle GetRectForUTMZone(int zone)
{
int eastingWest, eastingEast, northingSouth, northingNorth;

// initialize above according to your database of zones

return new Rectangle(eastingWest, northingNorth, eastingEast -
eastingWest, northingSouth - northingNorth);
}

Then drawing into a bitmap looks like this:

void SetPictureBox()
{
Bitmap bitmap;

using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.Transform = GetTransform(GetRectForUTMZone(zone),
new Rectangle(new Point(), bitmap.Size));

// draw your stuff using UTM coordinates
}

pictureBox1.Image = bitmap;
}

Hope that helps.

Pete
 
R

Random

I have some lines that are described by geographic coordinates (x1,y1)
to (x2,y2) in UTM coordinate.  What I'm trying to do is draw these
lines onto a windows form to visualize what they look like.  I can
draw lines onto the form using the code below.  my question is how do
I convert my UTM coordinates to a form that can be drawn on the
screen?

You need to convert the UTM coordinates to a latitude/longitude
coordinate system and scale it to fit whatever your drawing. Here's a
small example using an example I created a while ago for a related
project:

class MainForm : Form
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}

MainForm()
{
ClientSize = new Size(450, 300);
}

protected override void OnResize(EventArgs e)
{
Invalidate();

base.OnResize(e);
}

PointF UTMtoPointF(double x, double y, double f1)
{
double[] output = new double[2];

UTMConversions.UTMXYToLatLon(x, y, f1, false, ref output);

return new PointF((float)output[1], -(float)output[0]);
}

protected override void OnPaint(PaintEventArgs e)
{
List<PointF> points = new List<PointF>();

foreach (UTMCoords utm in UTMCoords.GetExample())
{
points.Add(UTMtoPointF(utm.Northing, utm.Easting,
utm.Zone));
}

PointF max = new PointF(float.MinValue, float.MinValue);
PointF min = new PointF(float.MaxValue, float.MaxValue);

foreach (PointF cur in points)
{
max.X = Math.Max(max.X, cur.X);
max.Y = Math.Max(max.Y, cur.Y);
min.X = Math.Min(min.X, cur.X);
min.Y = Math.Min(min.Y, cur.Y);
}

float temp = (max.X - min.X) * 0.05f;
min.X -= temp;
max.X += temp;
temp = (max.Y - min.Y) * 0.05f;
min.Y -= temp;
max.Y += temp;

List<PointF> output = new List<PointF>();

foreach (PointF cur in points)
{
output.Add(new PointF(
(cur.X - min.X) / (max.X - min.X) * ClientSize.Width,
(cur.Y - min.Y) / (max.Y - min.Y) *
ClientSize.Height));
}

e.Graphics.DrawPolygon(Pens.Black, output.ToArray());

base.OnPaint(e);
}
}

class UTMCoords
{
public double Northing;
public double Easting;
public double Zone;

public static IEnumerable<UTMCoords> GetExample()
{
yield return new UTMCoords(398835.00, 5355174.23, 10);
yield return new UTMCoords(490002.21, 5347785.39, 10);
yield return new UTMCoords(503008.13, 5412417.89, 10);
yield return new UTMCoords(320173.51, 5408878.96, 15);
yield return new UTMCoords(500865.42, 5373721.38, 15);
yield return new UTMCoords(625180.27, 5329608.40, 15);
yield return new UTMCoords(374391.94, 5310006.98, 16);
yield return new UTMCoords(485978.82, 5302106.62, 16);
yield return new UTMCoords(660245.76, 5178393.16, 16);
yield return new UTMCoords(333068.16, 5063639.65, 17);
yield return new UTMCoords(412472.80, 4861109.26, 17);
yield return new UTMCoords(359727.69, 4698202.27, 17);
yield return new UTMCoords(285227.76, 4627613.26, 17);
yield return new UTMCoords(431193.91, 4610145.49, 17);
yield return new UTMCoords(511580.62, 4689595.67, 17);
yield return new UTMCoords(656341.12, 4691331.56, 17);
yield return new UTMCoords(683192.02, 4770921.90, 17);
yield return new UTMCoords(652790.78, 4848200.80, 17);
yield return new UTMCoords(325147.14, 4834620.75, 18);
yield return new UTMCoords(474475.13, 4951399.02, 18);
yield return new UTMCoords(641090.13, 4973681.42, 18);
yield return new UTMCoords(313315.03, 4981765.95, 19);
yield return new UTMCoords(405614.00, 5123203.36, 19);
yield return new UTMCoords(473913.72, 5249490.78, 19);
yield return new UTMCoords(587147.13, 5216921.57, 19);
yield return new UTMCoords(603304.17, 5048602.61, 19);
yield return new UTMCoords(653944.01, 4939287.50, 19);
yield return new UTMCoords(514541.47, 4881616.83, 19);
yield return new UTMCoords(372073.36, 4791028.22, 19);
yield return new UTMCoords(355871.30, 4698280.78, 19);
yield return new UTMCoords(383133.47, 4596162.82, 19);
yield return new UTMCoords(701299.33, 4568712.98, 18);
yield return new UTMCoords(651100.68, 4501141.06, 18);
yield return new UTMCoords(592832.85, 4403321.12, 18);
yield return new UTMCoords(517836.41, 4281898.07, 18);
yield return new UTMCoords(448215.38, 4151581.71, 18);
yield return new UTMCoords(407868.30, 4011530.01, 18);
yield return new UTMCoords(438587.95, 3884554.80, 18);
yield return new UTMCoords(251319.48, 3775310.89, 18);
yield return new UTMCoords(595804.98, 3593289.89, 17);
yield return new UTMCoords(479857.86, 3427139.65, 17);
yield return new UTMCoords(445537.75, 3275888.61, 17);
yield return new UTMCoords(548361.30, 3105273.76, 17);
yield return new UTMCoords(663236.14, 2907027.53, 17);
yield return new UTMCoords(585359.05, 2730547.74, 17);
yield return new UTMCoords(408244.51, 2836302.72, 17);
yield return new UTMCoords(313857.94, 3020366.11, 17);
yield return new UTMCoords(342472.60, 3208900.79, 17);
yield return new UTMCoords(788042.60, 3287927.71, 16);
yield return new UTMCoords(626611.27, 3267998.76, 16);
yield return new UTMCoords(549582.41, 3351810.65, 16);
yield return new UTMCoords(338034.52, 3336060.52, 16);
yield return new UTMCoords(731624.71, 3244200.20, 15);
yield return new UTMCoords(586427.62, 3250658.13, 15);
yield return new UTMCoords(433259.15, 3309772.89, 15);
yield return new UTMCoords(398561.06, 3225293.72, 15);
yield return new UTMCoords(269459.09, 3184531.67, 15);
yield return new UTMCoords(718908.21, 3132943.63, 14);
yield return new UTMCoords(634039.65, 3011034.47, 14);
yield return new UTMCoords(652599.89, 2915625.75, 14);
yield return new UTMCoords(697646.53, 2837455.81, 14);
yield return new UTMCoords(556314.23, 2871158.49, 14);
yield return new UTMCoords(459936.26, 3001714.52, 14);
yield return new UTMCoords(408787.60, 3148406.80, 14);
yield return new UTMCoords(290479.01, 3269236.36, 14);
yield return new UTMCoords(734578.08, 3320499.22, 13);
yield return new UTMCoords(642580.33, 3217222.41, 13);
yield return new UTMCoords(497180.66, 3343286.02, 13);
yield return new UTMCoords(397061.36, 3485927.85, 13);
yield return new UTMCoords(773818.52, 3530518.63, 12);
yield return new UTMCoords(767206.57, 3463872.68, 12);
yield return new UTMCoords(516135.91, 3485431.48, 12);
yield return new UTMCoords(291604.12, 3528980.60, 12);
yield return new UTMCoords(666566.56, 3610649.16, 11);
yield return new UTMCoords(501649.05, 3609257.86, 11);
yield return new UTMCoords(395507.03, 3707813.39, 11);
yield return new UTMCoords(264866.66, 3710115.28, 11);
yield return new UTMCoords(737302.99, 3839429.05, 10);
yield return new UTMCoords(614450.43, 3996051.78, 10);
yield return new UTMCoords(534469.16, 4205461.88, 10);
yield return new UTMCoords(434956.29, 4304951.80, 10);
yield return new UTMCoords(399933.77, 4537206.66, 10);
yield return new UTMCoords(395675.96, 4747832.39, 10);
yield return new UTMCoords(412469.89, 4931092.01, 10);
yield return new UTMCoords(429039.93, 5156560.10, 10);
yield return new UTMCoords(398835.00, 5355174.23, 10);
}

public UTMCoords(double Northing, double Easting, double Zone)
{
this.Northing = Northing;
this.Easting = Easting;
this.Zone = Zone;
}
}

static class UTMConversions
{
//From: http://home.hiwaay.net/~taylorc/toolbox/geography/geoutm.html

const double cPI = 3.14159265358979;
const double cSM_A = 6378137.0;
const double cSM_B = 6356752.314;
const double cUTMScaleFactor = 0.9996;

static double DegToRad(double deg)
{
return (deg / 180.0 * cPI);
}

static double RadToDeg(double rad)
{
return (rad / cPI * 180.0);
}

static double ArcLengthOfMeridian(double phi)
{
double alpha, beta, gamma, delta, epsilon, n;
double result;

n = (cSM_A - cSM_B) / (cSM_A + cSM_B);
alpha = ((cSM_A + cSM_B) / 2.0)
* (1.0 + (Math.Pow(n, 2.0) / 4.0) + (Math.Pow(n, 4.0) /
64.0));
beta = (-3.0 * n / 2.0) + (9.0 * Math.Pow(n, 3.0) / 16.0)
+ (-3.0 * Math.Pow(n, 5.0) / 32.0);
gamma = (15.0 * Math.Pow(n, 2.0) / 16.0)
+ (-15.0 * Math.Pow(n, 4.0) / 32.0);
delta = (-35.0 * Math.Pow(n, 3.0) / 48.0)
+ (105.0 * Math.Pow(n, 5.0) / 256.0);
epsilon = (315.0 * Math.Pow(n, 4.0) / 512.0);
result = alpha
* (phi + (beta * Math.Sin(2.0 * phi))
+ (gamma * Math.Sin(4.0 * phi))
+ (delta * Math.Sin(6.0 * phi))
+ (epsilon * Math.Sin(8.0 * phi)));

return result;
}

static double UTMCentralMeridian(double zone)
{
return DegToRad(-183.0 + (zone * 6.0));
}

static double FootpointLatitude(double y)
{
double y_, alpha_, beta_, gamma_, delta_, epsilon_, n;
double result;

n = (cSM_A - cSM_B) / (cSM_A + cSM_B);
alpha_ = ((cSM_A + cSM_B) / 2.0)
* (1 + (Math.Pow(n, 2.0) / 4) + (Math.Pow(n, 4.0) / 64));
y_ = y / alpha_;
beta_ = (3.0 * n / 2.0) + (-27.0 * Math.Pow(n, 3.0) / 32.0)
+ (269.0 * Math.Pow(n, 5.0) / 512.0);
gamma_ = (21.0 * Math.Pow(n, 2.0) / 16.0)
+ (-55.0 * Math.Pow(n, 4.0) / 32.0);
delta_ = (151.0 * Math.Pow(n, 3.0) / 96.0)
+ (-417.0 * Math.Pow(n, 5.0) / 128.0);
epsilon_ = (1097.0 * Math.Pow(n, 4.0) / 512.0);
result = y_ + (beta_ * Math.Sin(2.0 * y_))
+ (gamma_ * Math.Sin(4.0 * y_))
+ (delta_ * Math.Sin(6.0 * y_))
+ (epsilon_ * Math.Sin(8.0 * y_));

return result;
}

static void MapLatLonToXY(double phi, double lambda, double
lambda0, ref double[] xy)
{
double N, nu2, ep2, t, t2, l;
double l3coef, l4coef, l5coef, l6coef, l7coef, l8coef;
double tmp;

ep2 = (Math.Pow(cSM_A, 2.0) - Math.Pow(cSM_B, 2.0)) /
Math.Pow(cSM_B, 2.0);
nu2 = ep2 * Math.Pow(Math.Cos(phi), 2.0);
N = Math.Pow(cSM_A, 2.0) / (cSM_B * Math.Sqrt(1 + nu2));
t = Math.Tan(phi);
t2 = t * t;
tmp = (t2 * t2 * t2) - Math.Pow(t, 6.0);
l = lambda - lambda0;
l3coef = 1.0 - t2 + nu2;
l4coef = 5.0 - t2 + 9 * nu2 + 4.0 * (nu2 * nu2);
l5coef = 5.0 - 18.0 * t2 + (t2 * t2) + 14.0 * nu2
- 58.0 * t2 * nu2;
l6coef = 61.0 - 58.0 * t2 + (t2 * t2) + 270.0 * nu2
- 330.0 * t2 * nu2;
l7coef = 61.0 - 479.0 * t2 + 179.0 * (t2 * t2) - (t2 * t2 *
t2);
l8coef = 1385.0 - 3111.0 * t2 + 543.0 * (t2 * t2) - (t2 * t2 *
t2);
xy[0] = N * Math.Cos(phi) * l
+ (N / 6.0 * Math.Pow(Math.Cos(phi), 3.0) * l3coef *
Math.Pow(l, 3.0))
+ (N / 120.0 * Math.Pow(Math.Cos(phi), 5.0) * l5coef *
Math.Pow(l, 5.0))
+ (N / 5040.0 * Math.Pow(Math.Cos(phi), 7.0) * l7coef *
Math.Pow(l, 7.0));
xy[1] = ArcLengthOfMeridian(phi)
+ (t / 2.0 * N * Math.Pow(Math.Cos(phi), 2.0) *
Math.Pow(l, 2.0))
+ (t / 24.0 * N * Math.Pow(Math.Cos(phi), 4.0) * l4coef *
Math.Pow(l, 4.0))
+ (t / 720.0 * N * Math.Pow(Math.Cos(phi), 6.0) * l6coef *
Math.Pow(l, 6.0))
+ (t / 40320.0 * N * Math.Pow(Math.Cos(phi), 8.0) * l8coef
* Math.Pow(l, 8.0));
}

static void MapXYToLatLon(double x, double y, double lambda0, ref
double[] philambda)
{
double phif, Nf, Nfpow, nuf2, ep2, tf, tf2, tf4, cf;
double x1frac, x2frac, x3frac, x4frac, x5frac, x6frac, x7frac,
x8frac;
double x2poly, x3poly, x4poly, x5poly, x6poly, x7poly, x8poly;

phif = FootpointLatitude(y);
ep2 = (Math.Pow(cSM_A, 2.0) - Math.Pow(cSM_B, 2.0))
/ Math.Pow(cSM_B, 2.0);
cf = Math.Cos(phif);
nuf2 = ep2 * Math.Pow(cf, 2.0);
Nf = Math.Pow(cSM_A, 2.0) / (cSM_B * Math.Sqrt(1 + nuf2));
Nfpow = Nf;
tf = Math.Tan(phif);
tf2 = tf * tf;
tf4 = tf2 * tf2;
x1frac = 1.0 / (Nfpow * cf);
Nfpow *= Nf;
x2frac = tf / (2.0 * Nfpow);
Nfpow *= Nf;
x3frac = 1.0 / (6.0 * Nfpow * cf);
Nfpow *= Nf;
x4frac = tf / (24.0 * Nfpow);
Nfpow *= Nf;
x5frac = 1.0 / (120.0 * Nfpow * cf);
Nfpow *= Nf;
x6frac = tf / (720.0 * Nfpow);
Nfpow *= Nf;
x7frac = 1.0 / (5040.0 * Nfpow * cf);
Nfpow *= Nf;
x8frac = tf / (40320.0 * Nfpow);
x2poly = -1.0 - nuf2;
x3poly = -1.0 - 2 * tf2 - nuf2;
x4poly = 5.0 + 3.0 * tf2 + 6.0 * nuf2 - 6.0 * tf2 * nuf2
- 3.0 * (nuf2 * nuf2) - 9.0 * tf2 * (nuf2 * nuf2);
x5poly = 5.0 + 28.0 * tf2 + 24.0 * tf4 + 6.0 * nuf2 + 8.0 *
tf2 * nuf2;
x6poly = -61.0 - 90.0 * tf2 - 45.0 * tf4 - 107.0 * nuf2
+ 162.0 * tf2 * nuf2;
x7poly = -61.0 - 662.0 * tf2 - 1320.0 * tf4 - 720.0 * (tf4 *
tf2);
x8poly = 1385.0 + 3633.0 * tf2 + 4095.0 * tf4 + 1575 * (tf4 *
tf2);
philambda[0] = phif + x2frac * x2poly * (x * x)
+ x4frac * x4poly * Math.Pow(x, 4.0)
+ x6frac * x6poly * Math.Pow(x, 6.0)
+ x8frac * x8poly * Math.Pow(x, 8.0);
philambda[1] = lambda0 + x1frac * x
+ x3frac * x3poly * Math.Pow(x, 3.0)
+ x5frac * x5poly * Math.Pow(x, 5.0)
+ x7frac * x7poly * Math.Pow(x, 7.0);
}

static public double LatLonToUTMXY(double lat, double lon, ref
double[] xy)
{
double zone = Math.Floor((lon + 180.0) / 6) + 1;
lat = DegToRad(lat);
lon = DegToRad(lon);

MapLatLonToXY(lat, lon, UTMCentralMeridian(zone), ref xy);

xy[0] = xy[0] * cUTMScaleFactor + 500000.0;
xy[1] = xy[1] * cUTMScaleFactor;
if (xy[1] < 0.0)
{
xy[1] = xy[1] + 10000000.0;
}

return zone;
}

static public void UTMXYToLatLon(double x, double y, double zone,
bool southhemi, ref double[] latlon)
{
double cmeridian;

x -= 500000.0;
x /= cUTMScaleFactor;

if (southhemi)
{
y -= 10000000.0;
}

y /= cUTMScaleFactor;

cmeridian = UTMCentralMeridian(zone);
MapXYToLatLon(x, y, cmeridian, ref latlon);
}
}
 
F

Family Tree Mike

You should read up on UTM coordinates and conversion to plain Cartesian
coordinates. You'll need to really understand that well in order to have
success.

That said, the basic idea is to scale and translate the coordinates as
necessary to fit the area you want to draw in. Assuming you want to draw
"north up", you'll also have to reverse the Y axis, since in screen
coordinate increasing values go down.

You can do this transformation more easily by setting the Transform
property of the Graphics instance to which you're drawing. For example:

Matrix GetTransform(Rectangle rectWorld, Rectangle rectView)
{
float scaleX = (float)rectView.Width / rectWorld.Width,
scaleY = (float)rectView.Height / rectWorld.Height;
float dxOffset = rectView.Left - rectWorld.Left * scaleX,
dyOffset = rectView.Top - rectWorld.Top * scaleY;

Matrix matrix = new Matrix();

matrix.Translate(dxOffset, dyOffset);
matrix.Scale(scaleX, scaleY);

return matrix;
}

Pass to the above method the rectangle defining the boundaries, in world
coordinates, that lines up visually with the rectangle, in
control/client/view coordinates (i.e. the screen coordinates for the
area where the drawing should occur).

To use this method with UTM, you'll have to have a way of getting for a
given UTM zone the exact range of grid positions for that zone. Assuming
you've gotten that, then you can create a Rectangle instance where the
position (upper-left corner…i.e. the Location) is set to the UTM grid
position coordinates for the northwest corner (again, upper-left) of the
UTM zone.

The width and height of the Rectangle should be calculated based on the
southeast corner. That is, width is "easternmost easting minus
westernmost easting" and height is "southernmost northing minus
northernmost northing". Note that this will necessarily result in a
negative height for the Rectangle. This is exactly what you want, in
order to "flip" the coordinates as I described.

Assuming you have a method that returns such a Rectangle instance:

Rectangle GetRectForUTMZone(int zone)
{
int eastingWest, eastingEast, northingSouth, northingNorth;

// initialize above according to your database of zones

return new Rectangle(eastingWest, northingNorth, eastingEast -
eastingWest, northingSouth - northingNorth);
}

Then drawing into a bitmap looks like this:

void SetPictureBox()
{
Bitmap bitmap;

using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.Transform = GetTransform(GetRectForUTMZone(zone),
new Rectangle(new Point(), bitmap.Size));

// draw your stuff using UTM coordinates
}

pictureBox1.Image = bitmap;
}

Hope that helps.

Pete

Just suggesting a small change. UTM Zones, as specified by a single
integer value, go from pole to pole (90 North to 90 South). A map
showing this for Europe is here:
http://en.wikipedia.org/wiki/File:LA2-Europe-UTM-zones.png.

A zone with a letter, covers a much smaller area, only eight degrees in
the north-south direction. Your code probably meant to create a
function of the smaller six by eight degree rectangle, and so it should
take a string, such as "30U".

But there is no reason why the code could not simply compute the min and
max x and y values in the provided data, and use those for the transform
on the form, basically ignoring the UTM fact.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top