Drawing on the map

Topics: Windows Forms
Mar 24, 2013 at 3:35 PM
I've got a map loaded into my program (using Visual C# 2010, and gMapControl). I can draw lines on the map (YAY). I'm using:
        private void gMapControl1_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            Pen fl = new Pen(Color.Red, 3.0f);
            e.Graphics.DrawLine(fl, x_start, y_start, xx, yy);
        }
Where x_start and y_start is the left mouseDown, and xx, yy is the current position, as below:
        private void gMapControl1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                mouseDown = 1;
                Point pmousePos = new Point(e.X, e.Y);
                x_start = e.X;
                y_start = e.Y;
            }
        }
My problem is that when I pan the map, or zoom the map, my drawn lines stay in the same place on the screen; they're not "tied to the ground (map").

Any direction would be appreciated.
AW
Mar 24, 2013 at 3:54 PM
use GMapRoute/Marker/Polygon
Mar 24, 2013 at 5:13 PM
I appreciate the direction, but that only killed the battery on my cell.

Should I not be using my second sample above to get coordinates? Is this method only returning screen coordinates?
Or perhaps I should be feeding the method with "map coordinates"?
Should I not be using the gMapControl1_Paint method to "paint" lines between mouseDown's?
Mar 25, 2013 at 4:56 PM
It looks like I need to draw (or Paint) my line on an overlay, and then add the overlay to the map. It this correct?
Mar 25, 2013 at 5:13 PM
It looks like I need to draw (or Paint) my line on an overlay, and then add the overlay to the map. It this correct?
Mar 25, 2013 at 8:52 PM
This is some of my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using GMap.NET.MapProviders;
using GMap.NET.WindowsForms;
using GMap.NET.WindowsForms.Markers;

namespace GMap_AW
{

    public partial class Form1 : Form
    {
        GMapControl MainMap = new GMapControl();
        GMapMarkerImage m = new GMapMarkerImage(new PointLatLng());
        GMapOverlay overlayOne;

        int dragging = 0;
        int mouseDown = 0;
        float x_start;
        float y_start;
        float x_end;
        float y_end;
        float xx;
Why am I getting this error ?
Error   1   The type or namespace name 'GMapMarkerImage' could not be found (are you missing a using directive or an assembly reference?)   
Mar 27, 2013 at 3:13 PM
I realize now that I need to reference the screen coordinates relative to the map, and then redraw my lines on the screen after I pan or zoom.
To reference the screen I use:
      Double   lat = gMapControl1.FromLocalToLatLng(e.X, e.Y).Lat;
      Double   lng = gMapControl1.FromLocalToLatLng(e.X, e.Y).Lng;
After I pan or zoom I need to find out where the above coordinates are relative to the screen. The command is:
      Double   lng = gMapControl1.FromLatLngToLocal(PointLatLng Point);
But what is the proper syntax? Because the compiler doesn't like "PointLatLng Point".

Thanks
Aw
Mar 27, 2013 at 4:42 PM
"Point" is a structure of namespace System.Drawing, that's why the compiler is complaining...
Why don't you just use a GMapRoute as radioman already suggested?
Mar 27, 2013 at 5:34 PM
I've tried using:

GMapRoute m = new GMapRoute(new Point.LatLng());

But I get an error of:

Error 1 The type name 'LatLng' does not exist in the type 'System.Drawing.Point'

AW
Mar 27, 2013 at 5:38 PM
I'm sorry, but you should learn to program C# first...
        GMapRoute route = new GMapRoute(new List<PointLatLng>(), "test");
        //Add all your points here
        route.Points.Add(...
        yourOverlay.Routes.Add(route);
Mar 30, 2013 at 6:27 PM
Xandolph (radioman);

Well, I can appreciate that comment.
I'm an old guy; when I got out of school there were no computers. Any function I can grasp and learn is a "HELLS YEAH" moment for me...I do enjoy the logic.

I'm building my list of "line ends" with:
line_end_coordinate.Add(x_start = e.X;);
line_end_coordinate.Add(y_start = e.y);
and so on...

Which is triggered by mouse (up/down) activity.
But how do I feed this into: route.Points.Add(???) ?

I do appreciate your responses, please excuse my short comings in understanding.
AW
Mar 30, 2013 at 6:53 PM
well just like that, nothing advanced, if you'll figure it out by yourself, later it will be a lot easier
don't think, just do, don't try to understand every detail, there are just too many of them... well at least for me ;}
Mar 31, 2013 at 8:30 AM
Edited Mar 31, 2013 at 8:31 AM
Ok, lets start over :-) First, just tell us exactly what you want to do, then we'll find a solution.
Mar 31, 2013 at 2:56 PM
Xandolph;

Wow; I appreciate that.
I'm not a programmer, never had a class of any kind; and you will see this as fact (as in my previous posts).
Allow me to gather my thoughts, and I will post back tomorrow.

Thank you
AW
Apr 1, 2013 at 4:05 PM
Xandolph;

I'm loading maps from ArcGIS_Topo_US_2D_MapProvider. I like these maps because my clients send their mapping flight lines to me on USGS 7.5 minute Quad maps; the digital Topo's have ground contour elevation lines that I require to set the final altitude for the flight line.

I draw flight lines on these digital maps using mouse up / down activity. While drawing these flight lines, I capture the latitude / longitude of the start and stop of each flight line. These coordinates are fed into my flight management system; which provides aircraft steering direction for the pilot. The steering resolution is 0.10 degrees horizontally and one foot vertically.

My problem is that some projects require the ArcGIS Topo map to be either zoomed or moved during the digitizing of the flight lines. When I zoom or move the map, my drawn lines stay in one place while the map moves. I finally realized that I needed to do something like "gMapControl1.FromLatLngToLocal" after zooming or moving the map, and then redraw my flight lines by looping thru my list of end_of_line_coordinates.

You suggested using:

GMapRoute route = new GMapRoute(new List<PointLatLng>(), "test");
    //Add all your points here
    route.Points.Add(...
    yourOverlay.Routes.Add(route);

But I don't know how to feed my list of end_of _line_coordinates to "route.Points.Add(...". I tried to feed the coordinates manually such as:

route.Points.Add(39.302751, 077.52230);
...

But this is not the way.

This is where I am. What is the syntax to feed my end of line coordinates to route.Points.Add(???);

Thank you
AW
Apr 1, 2013 at 4:34 PM
Edited Apr 1, 2013 at 4:49 PM
Hi!
You have to add a PointLatLng structure to the list of points that define your route. If you take a look at the constructor of PointLatLng, you can see, that you have to pass the latitude as first parameter and the longitude as the second parameter:
  public PointLatLng(double lat, double lng)
  {
     this.lat = lat;
     this.lng = lng;
  }
So, to add a point to your route you just need the following lines:
PointLatLng p = new PointLatLng(39.302751d, 77.52230d);
route.Points.Add(p);
Your mouse click event could look something like this (you could get the geographic position (Lat/Long) from the location of the mouse click):
    private void map1_MouseClick(object sender, MouseEventArgs e)
    {
        if (e.Button == System.Windows.Forms.MouseButtons.Left)
        {
            PointLatLng p = map1.FromLocalToLatLng(e.X, e.Y);
            route.Points.Add(p);
            map1.UpdateRouteLocalPosition(route);
        }
    }
By the way, if you want some good information on the basics of C#, check out http://www.informit.com/library/library.aspx?b=STY_Csharp_24hours it also helped me, when I started to learn. But mostly it's learning by doing...
Apr 1, 2013 at 7:42 PM
Xandolph;

Thank you for the reading link, I will absolutely spend much time with that!

Using your suggestion above, I modified it to fit my code as below:
               PointLatLng p = gMapControl1.FromLocalToLatLng(e.X, e.Y);
                route.Points.Add(p);
                label9.Text = Convert.ToString(p);
My "lable" lets me see the Points.

But when I move my map the lines stay on the screen where drawn. I have more mountains to climb. My "map" does not have a name. I initialize the map like this:
       // Initialize map:
        //gMapControl1.MapProvider = GMap.NET.MapProviders.BingMapProvider.Instance;
        gMapControl1.MapProvider = GMap.NET.MapProviders.ArcGIS_Topo_US_2D_MapProvider.Instance;
        GMap.NET.GMaps.Instance.Mode = GMap.NET.AccessMode.ServerOnly;
        gMapControl1.Position = new GMap.NET.PointLatLng(39.401389, -077.986111); //mrb
        gMapControl1.Zoom = 10;
Instead of using your "map1.UpdateRouteLocalPosition(route);", I use:
                 gMapControl1.UpdateRouteLocalPosition(route);
How do I re-draw these lines after the map is moved or zoomed?

AW
Apr 2, 2013 at 2:21 PM
Xandolph;

Well, my world just opened up! I've been under the impression that I had to convert the lat / longs to screen coordinates, and then feed them back into the Control1_Paint function to redraw the map when it was zoomed or moved...NOT SO !!! I found that I can do this:
                PointLatLng p = gMapControl1.FromLocalToLatLng(e.X, e.Y);
                route.Points.Add(p);
                label9.Text = Convert.ToString(p);
                gMapControl1.UpdateRouteLocalPosition(route);
                route.Stroke.Width = 5;
                route.Stroke.Color = Color.Black;
                GMapOverlay routesOverlay = new GMapOverlay(gMapControl1, "route");
                routesOverlay.Routes.Add(route);
                gMapControl1.ZoomAndCenterRoute(route);
                gMapControl1.Overlays.Clear();
                gMapControl1.Overlays.Add(routesOverlay);
My HELLS YEAH moment!

Thanks Xandolph!
AW
Apr 2, 2013 at 2:57 PM
That's the way to do it :-)
You don't need to draw a line by yourself.
Some more points to consider: Do you add your overlay to the map in the mouse click event handler? If so, don't! Move this code to the constructor. (You only need to add it once.)
Set the code for your route(s) in the constructor or in the form_load event as well (Stroke settings, add it to the overlay).
You just need to add the new point in the mouse click event handler.
Apr 2, 2013 at 7:07 PM
Xandolph;

I very much appreciate your direction.
To create my "route", I'm using:

GMapRoute route = new GMapRoute(new List<PointLatLng>(), "Route");

My problem is that my drawn flight lines can be very much random, like lines that are drawn on paper. I'm using gMapControl1.Overlays.Add(... it makes one continuous path. I would like my drawn lines to not be connected to each other. I'm wondering if I should (or if I can) make a new route for each flight line, so that the .Add(routesOverlay) only draws the straight flight lines.

Something like:
GMapRoute route_1 = new GMapRoute(new List<PointLatLng>(), "Route");
GMapRoute route_2 = new GMapRoute(new List<PointLatLng>(), "Route");
GMapRoute route_3 = new GMapRoute(new List<PointLatLng>(), "Route");
(I could have 100 flight lines, or more).

And then, if the above could be done, can the routes be named dynamically, as on the fly?


Thanks
AW
Apr 2, 2013 at 7:25 PM
Edited Apr 2, 2013 at 7:28 PM
Questions: Does every flight line consist only of 2 points (start/end)? Do you need to have a unique name for every route?

Think of the overlay as a kind of container for routes, markers, polygons, etc. Usually you only need one overlay, it can contain an arbitrary number of routes.
You could use an array or a list of routes (the name you pass as string does not rerally matter) to accomplish what you want.
Apr 2, 2013 at 7:56 PM
Yes Sir, each flight line consists of only 2 points, start and end.

To capture the start of the line on mouseDown I do:
 PointLatLng p = gMapControl1.FromLocalToLatLng(e.X, e.Y);
                route.Points.Add(p);
And I do the same thing at the end of the line:
 PointLatLng p = gMapControl1.FromLocalToLatLng(e.X, e.Y);
                route.Points.Add(p);
However, with more than one flight line (and there's always more than one), now when its drawn, the path is straight between the first two mouse captures, then a line is drawn from the end of the first flight line to the beginning of the second flight line.
Apr 3, 2013 at 6:54 PM
What you could do:

define 2 class variables:

private bool bStartnewRoute = true;

private GMapRoute route = null;

in your mouse down:

If bStartNewRoute is true, set it to false, create a new route and add startpoint. If bStartNewRoute is false, set it to true and add endpoint. Now you can add as many flight lines as you want.

If you need more help, I can send you some code, but try for yourself first :-)
Apr 3, 2013 at 7:06 PM
I appreciate that. I've been paddling in circles over this. My HELLS YEAH has become a pffffttt.
I understand the creation of a switch (as in on / off). Yep, I can do that for sure.
So if new flight line, then make a new route as below ?

GMapRoute route_1 = new GMapRoute(new List<PointLatLng>(), "Route");
Apr 3, 2013 at 7:25 PM
Edited Apr 3, 2013 at 8:59 PM
Re-use the route object, otherwise you cannot add the 2nd point!

In your mouse_down event handler:
if(bStartNewRoute)
{
   bStartNewRoute = false;
   route = new GMapRoute(new List<PointLatLng>(), "Route"); 
   route.Points.Add...
}
else
{
   bStartNewRoute = true;
   route.Points.Add...
   gMapControl1.UpdateRouteLocalPosition(route);
}
Apr 4, 2013 at 7:26 PM
Xandolph;

With your help, I've got it drawing multiple lines like I want. At some point, I will need to modify a drawn line. Using the method as above, a new instance of "route" is being generated every time there is a "first" mouse down. I'm thinking that because of this method of creating a new instance of "route", perhaps all history is lost to revisit a drawn line to modify it. Its just something I've got to think about.

For now, sometimes, I need to re-position the map after the first mouse down, to pan to the next point. I'm using "gMapControl1_Paint" to draw my lines in real-time (as the mouse moves). This works real well for me. However, when I have to move the map (after the first mouse down), the beginning of the flight line stays in one place. And it should, because I'm drawing basically on the Form. I need to be able to convert "first mouse down lat / long" to screen coordinates, so when I move the map after the first mouse down, my flight line start stays where it should on the map. I'm trying to use:
              ............
    public Point FromLatLngToPixel(double lat, double lng, int zoom)
    {
        Point converted_to_screen_xy = new Point();
               return converted_to screen_xy;
    }
But I don't know how to call the function. I know I need to feed it the lat / lng and the zoom ratio, but I don't know the syntax.
          Point xx_yy = gMapControl1.gMap.FromLatLngToPixel(x_y, 10);
is not right.
Apr 4, 2013 at 8:00 PM
Hi!
First of all, don't draw in gMapControl1_Paint, it is not necessary!

If you want to dynamically show the endpoint of the current flightline while moving the map, do the following:

Add TWO points (with the same coordinates!) in the Mouse_down event.

In the MouseMove event:

Set the 2nd point of your current route object to the point of the mouse event, if "bStartNewRoute" is false, then update the route to show it correctly:
        private void gMapControl1_MouseMove(object sender, MouseEventArgs e)
        {
            if(bStartNewRoute == false)
            {
                PointLatLng pos = map1.FromLocalToLatLng(e.X, e.Y);
                route.Points[1] = pos;
                gMapControl1.UpdateRouteLocalPosition(route);
            }
            
Make sure you don't add the point again in MouseDown event if(bStartNewRoute == false)!

If you want to change the routes you already added, you got to store them somewhere. A List<GMapRoute> should do the trick. But to move the points later is a bit tricky, you need to add some markers at start and end of route, if you need more help just tell me.

Greetings from Vienna, Austria :-)
Apr 4, 2013 at 8:54 PM
Xandolph;

I gotta tell you, I was afraid to dump the _Paint thing because I'm familiar with it; but your instruction helped me to achieve EXACTLY what I want.

You should be a teacher !!!
AW
Apr 19, 2013 at 5:33 PM
Xandolph;

On the 2nd of this month you gave some advise:

"Some more points to consider: Do you add your overlay to the map in the mouse click event handler? If so, don't! Move this code to the constructor. (You only need to add it once.)
Set the code for your route(s) in the constructor or in the form_load event as well (Stroke settings, add it to the overlay).
You just need to add the new point in the mouse click event handler."

I moved this to the Form1_load event (out of the gMapControl1_MouseMove event where it did work);
        GMapRoute route = new GMapRoute(new List<PointLatLng>(), "Route");
        route.Stroke.Width = 3;
        route.Stroke.Color = Color.Red;
        GMapOverlay routesOverlay1 = new GMapOverlay(gMapControl1, "route");
        routesOverlay1.Routes.Add(route);
        gMapControl1.Overlays.Add(routesOverlay1);
But now there is no line drawn on the screen when there's mouse movement (after the first mouse click). I have this in the MouseMove event:
           route.Points[1] = pos;
            gMapControl1.UpdateRouteLocalPosition(route);
The "route.Points[1] = pos" does update with mouse movement.
Am I missing something else ?

Thanks
Aw
Apr 20, 2013 at 9:12 AM
Hi!

Do you have a class variable declared for route? (private GMapRoute route;)

Your line " GMapRoute route = new GMapRoute(new List<PointLatLng>(), "Route");" in the form_load event should be probably changed to:
"route = new GMapRoute(new List<PointLatLng>(), "Route");" because otherwise your route exists only in the scope of the load event...

I would need more or the entire code, to pinpoint the problem.