# Draw grid map in Visual Basic 6

I am trying to make a small map program in Visual basic 6 and as starting point to make the working space as the worldmap grid but can not find anywhere any code to guide me in right direction.
What I need help with is:
1. Drawing grid line x-axes from left to right -180 to 180 and y-axes from top to bottom 90 to -90
2. Keep constrained aspect radio when sizing the form.
3. Convert the x y to latitude and longitude mercator projection.
4. Zoom in and out.

Does any of you expert have some guidlines for me or sample code in Visual Basic 6?

LVL 6
###### Who is Participating?

x

Commented:
I'd go back to the original code sample I provided in comment ID: 24129217, then add what I wrote in comment ID: 24139375, only change the latitude degree intervals to whatever you need, but just prior to writing that line, you would convert the Y axis degree into radians, then put it through your formula, then, I'm not sure if you have to convert the result back to degrees or not, maybe.  Then draw the line at that calculated Y coordinate.  See if it works!

The scaling system for the picturebox should be set to user defined as we originally did.  The native scaling unit in VB is a "twip" which is a 20th of a pixel, but which varies depending on what screen resolution mode a computer is in.  You can set the scalling mode of the picturebox to pixels, but, in cases like this where you are trying to plot by a coordinate system, it is best to make it user defined.

I have a couple of routines for converting to/from radians attached below.
``````Private Function DegreesToRadians(X As Double) As Double
Const pi = 3.14159265358979
'To convert degrees to radians, multiply degrees by pi/180.
DegreesToRadians = X * pi / 180
End Function

Private Function RadiansToDegrees(X As Double) As Double
Const pi = 3.14159265358979
RadiansToDegrees = X * (180 / pi)
End Function
``````
0

Commented:
Hi,  well, you're asking for a bunch of code, let's at least see if we can get you started in the right direction.

First, drawing the gridlines is pretty easy.

You need to add a Picturebox control to your form, let's say you name this picMap.  Set the AutoRedraw property to True, Set the ScaleMode property to 0 - User, Set the ScaleLeft property to -180, set the ScaleWidth property to 360, set the ScaleTop property to 90, set the ScaleHeight to -180. That should set up your picturebox to plot various lines and points.

If the Form resizes, you have to resize the height and width of the picturebox, however, the coordinates inside of the picturebox will always remain the same.

To draw the axis lines, you would simply say:

' Draw the X axis
picMap.Line (picMap.ScaleLeft, 0) - ((picMap.ScaleWidth + picMap.ScaleLeft), 0)
' Draw the Y axis
picMap.Line (0, picMap.ScaleTop) - (0, (picMap.ScaleHeight + picMap.ScaleTop))

Or, you can hardcode the dimensions

' Draw the X axis
picMap.Line (-180, 0) - (180, 0)
' Draw the Y axis
picMap.Line (0, 90) - (0, -90)

To keep a constrained aspect ratio when sizing the form, you need to calculate the ratios for both the width and the height.

In your form's General Declarations section, define a couple of variables.
Dim width_ratio as Single
Dim height_ratio as Single

width_ratio = picMap.width/picMap.height
height_ratio = picMap.height/picMap.width

Now, the tricky part is that when your form is resized, you have to decide whether to stretch the picture box to fit the width of the form or the height of the form.  You can do this by calculating what will fit.  If the ratio of the windows width/height is less than the ratio of the pictureboxes width/height then you have to set the width of the picturebox to fit in the form, then set the height to the ratio.

Let's say that you placed the upper left corner of your picture box at location 100, 100 on the form, and then stretched it out so that the right side is 100 from the right edge of the form and down so that the bottom edge is 100 from the bottom of the form.

For example, in the Form's Resize event you might say:

if Me.width/Me.height < width_ratio Then
picMap.Width = Me.Width - 200
picMap.Height = picMap.Width * height_ratio
else
picMap.Height = Me.Height - 200
picMap.Width = Me.Height * width_ratio
end if

I'm not sure what is meant by convert the XY to lat/lon mercator projection... any X coordiate between -180 and +180 is a valid longitude and any Y coordinate between 90 and -90 is a valid latitude.

Zooming probably just means changing the ScaleLeft, ScaleTop, ScaleWidth, ScaleHeight values in your picturebox, but, I'm not exactly sure how this is supposed to be implemented.

Hopefully, that gets you started!
0

Author Commented:
Hi mdougan

Thank you for reply to my question, let me look at your code and get back to you, about the Mercator convertation I found this topic http://www.experts-exchange.com/Programming/Languages/C/Q_23435832.html?sfQueryTermInfo=1+10+mercat and I was wondering if that solution could help me.

iscode

0

Author Commented:
mdougan

I put your code together but it will not scale correctly so I must be doing something wrong here
and how would you make the draw part go along with the scaling part when resize?
I attach the code here so you can correct me.

iscode
``````'General Declaration
Dim width_ratio As Single
Dim height_ratio As Single

picMap.AutoRedraw = True
picMap.ScaleMode = 0
picMap.ScaleLeft = -180
picMap.ScaleWidth = 360
picMap.ScaleTop = 90
picMap.ScaleHeight = -180
width_ratio = picMap.Width / picMap.Height
height_ratio = picMap.Height / picMap.Width

' Draw the X axis
picMap.Line (picMap.ScaleLeft, 0)-((picMap.ScaleWidth + picMap.ScaleLeft), 0)
' Draw the Y axis
picMap.Line (0, picMap.ScaleTop)-(0, (picMap.ScaleHeight + picMap.ScaleTop))
End Sub

Private Sub Form_Resize()
If Me.Width / Me.Height < width_ratio Then
picMap.Width = Me.Width - 200
picMap.Height = picMap.Width * height_ratio
Else
picMap.Height = Me.Height - 200
picMap.Width = Me.Height * width_ratio
End If
End Sub
``````
0

Commented:
Hi, I see a mistake I made in line 27 of your sample code... that should have been:

picMap.Width = picMap.Height * width_ratio

I also noticed that when we change the size of the picturebox, the internal Scale dimensions don't stay the same as I thought they would.  So, it is necessary to reset the Scale dimensions after resizing the picture box.  Here is some sample code that I tried that seems to work as I would expect.

If I'm understanding correctly, you want to keep the ratio of the height/width of the picture box the same, no matter how they size the form, and so, sometimes the picturebox won't fill the entire form.
``````Dim width_ratio As Single
Dim height_ratio As Single

Me.Width = 15000
Me.Height = 7500
picMap.Left = 500
picMap.Width = Me.Width - 1000
picMap.Top = 500
picMap.Height = Me.Height - 1000
picMap.AutoRedraw = True

SetPicScaleMode

width_ratio = picMap.Width / picMap.Height
height_ratio = picMap.Height / picMap.Width

' Draw the X axis
picMap.Line (picMap.ScaleLeft, 0)-((picMap.ScaleWidth + picMap.ScaleLeft), 0)
' Draw the Y axis
picMap.Line (0, picMap.ScaleTop)-(0, (picMap.ScaleHeight + picMap.ScaleTop))
End Sub

Private Sub Form_Resize()
If Me.Width / Me.Height < width_ratio Then
picMap.Width = Me.Width - 1000
picMap.Height = picMap.Width * height_ratio
Else
picMap.Height = Me.Height - 1000
picMap.Width = picMap.Height * width_ratio
End If

' Reset the pictureboxes scales
SetPicScaleMode

' Clear the picturebox and redraw it
picMap.Cls
' Draw the X axis
picMap.Line (picMap.ScaleLeft, 0)-((picMap.ScaleWidth + picMap.ScaleLeft), 0)
' Draw the Y axis
picMap.Line (0, picMap.ScaleTop)-(0, (picMap.ScaleHeight + picMap.ScaleTop))

End Sub

Private Sub SetPicScaleMode()
picMap.ScaleMode = 0
picMap.ScaleLeft = -180
picMap.ScaleWidth = 360
picMap.ScaleTop = 90
picMap.ScaleHeight = -180

End Sub
``````
0

Author Commented:
Hi mdougan

Yes I want to keep the ratio of the height/width of the picture box the same but within the
-180 to 180  and -90 to 90 degrees limits and keep it all visible as the worldmap.
Now the picturebox is not all visible. What is the trick to make it stay correct and smaller at startup?

Did you have a look at the link I posted for the mercator projection?

iscode
0

Commented:
Hi, in my code, I sized the form so that it was twice as wide as it was tall, and I also positioned and sized the picturebox so that it was similar in dimensions... twice as wide as it is tall... that keeps the same ratio as the Scale dimensions for the world map.  Are you doing the same?

I'm not quite sure why sometimes the edge of the picturebox comes right to the edge or bottom of the window... it's always supposed to have 500 TWIPS of a border on all sides.  I suggest you set a breakpoint in the resize event and step through it a bunch of times and see if you can determine where the calculations are going off.  The Width of the picturebox should never be greater than the Width of the form minus 1000 and the Height of the picturebox should never be greater than the Height of the form minus 1000.

I did look at the mercator projection thread that you posted.  But I don't have much experience with mercator projections, so, don't have any code to offer you.  I've written a lot of navigation code to calculate distance and heading given two sets of lat/lons but that's using the standard trig functions that are based on the Earth as a sphere, not the mercator projection.
0

Author Commented:

I will look more into this scaling matter to get the sizing work better
but with the projection do know how I can use this
double phi;      /* <--- the latitude */
double y = log(tan(phi) + (1.0 / cos(phi)));
and where I put it in your code?

iscode
0

Commented:
So far, nowhere in the code I've provided are we trying to plot any position points.  So, the code you have would not go anywhere in the code I've provided so far.

I'm guessing that you will have one or more sets of lat/lons that you will want to plot on your picturebox.  If so, then under a command button or menu item, you will draw a point or, I usually draw a small circle using the Picturebox.Circle function, passing the X and Y coordiates of the spot I want to draw, and giving a diameter of 1 for the circle.

What you might want to do, is load your world map into the picturebox at form load time (and also right after the statement that says picMap.Cls), and try drawing a point or circle at the coordinates of various known places around the world, such as the tip of South America, the tip of Baja California, Greenwich England etc., try using their LAT coordinate for Y and their LON coordinate for X see where the point is actually drawn on the map.  If the point is not drawing in the right location, then, you will have to apply the mercator projection to the coordinates to get the right X,Y to plot the point.

That is what the code you showed above is doing... it is modifying the LAT coordinate and giving  you a Y coordinate to use when you draw the point or  circle.

Do you have a world map jpg or bmp that you are planning to use ,that you can attach here... I can then play with what we've done so far and maybe give you an example.

0

Author Commented:
I think are misunderstanding me when I am talking about the mercator projection because what it does is it stretch the latitude lines on the map more and more from 0 latitude to the each pole so thats what I am trying to do and get that into the map with the formula. So when I start drawing the latitude lines I want to add this formula into the loop like:
'-------------------
For i = -90 To 90 Step 30 '<-- can be any step 30,15,10 for the lines
'Add here the formula to change the latitude to mercator
'This is C+++ code and need to change to VB code
'double phi;      /* <--- the latitude */
'double y = log(tan(phi) + (1.0 / cos(phi)));
picMap.Line (picMap.ScaleLeft, i)-((picMap.ScaleWidth + picMap.ScaleLeft), i)
Next i
'-------------------
I hope you understand me

iscode
0

Commented:
If I'm thinking of the right map, when talking about a mercator projection, all the latitude lines are parallel as are all of the longitude lines.  Actually, even in a sherical projection, all the latitude lines are parallel, it is the longitude lines that converge at the poles.  In a mercator projection, the land is distorted so that the lines can come out parallel.

So, you don't have to do anything special to draw the lines:

' For latitude lines
For i = -90 To 90 Step 30 '<-- can be any step 30,15,10 for the lines
picMap.Line (picMap.ScaleLeft, i)-((picMap.ScaleWidth + picMap.ScaleLeft), i)
Next i

' For longitude lines
For i = -180 To 180 Step 30 '<-- can be any step 30,15,10 for the lines
picMap.Line (i, picMap.ScaleTop)-(i, (picMap.ScaleHeight + picMap.ScaleTop))
Next i

See this map for an example:
http://www.mapsofworld.com/projection-maps/mercator/world-mercator-political-light-map-enlarged.jpg
0

Author Commented:
mdougan
Because I am trying to do the Mercator projection I need to change the latitude line as says below.

Mercator map is conformal, which means that at every point the north/south scale must be the same as the east/west scale  So the problem is to determine the (variable) distance scale at each point. Since the distance scale does not vary from east to west in the Mercator projection, it must vary as latitude changes (in fact, it must be a function of the latitude).

See this article so you understand what I am trying to do:
0

Commented:
OK, so, looking at the mercator map example I posted above, I can see that the latitude lines are not evenly spaced from the equator.  I'm used to the spherical nautical charts where the latitude lines are always exactly 60 nautical miles (about 70 statute miles) apart.  In those charts, it is the longitude lines you have to worry about, because the distance between one degree of longitude and another is different at the equator than it is at another latitude.

I think when drawing the latitude lines, if you are doing so according to the link you gave, you'd be stepping from 0 to 85 at increments of 5 degrees, then, you would put it through this formula:

70/(S*cos(theta))

where theta is the degree of latitude... that would tell you how many inches the latitude line needs to be from the equator.  However, this is where I find it confusing, because you have set a scale of degrees in your map, not inches, so, you can't use a unit of measure in inches to tell you where to plot the line in the picturebox.  I think you need to first figure out what scale your map is to be drawn in (how many miles per inch), that will give you S in the equation above.  Then, you need to figure out how many inches wide and tall your map has to be, based on that scale, then, you should set the scaleheight and scalewidth of the picturebox to be that amount.  Then, when you get the answer from the equation that would tell you how many units of the pictureboxes scale to move.

So, that would look something like this:

' approx circumference of the earth in miles 24,900
Dim earth_circumference as Long
earth_circumference = 24900

' arbitrarily setting to 100, you would vary this value when zooming in and out
Dim miles_per_inch as long
miles_per_inch = 100

' Now, we want to figure out how many inches wide our map has to be
Dim inches_wide as long
inches_wide = earth_circumference / miles_per_inch

' Now, we want to figure out how many inches tall our map has to be
' it's going to be half as tall as it is wide
Dim inches_tall as long
inches_tall = ((earth_circumference / miles_per_inch) / 2)

' Now, we can set the properties of the picture box... there really isn't a need to move the origin into the center of the picture box the way we did before with the +180 and -180
picMap.ScaleMode = 0 'User Defined
picMap.ScaleLeft = 0
picMap.ScaleWidth = inches_wide
picMap.ScaleTop = 0
picMap.ScaleHeight = inches_tall

Dim i as long
For i = 10 to 350 step 10
picMap.Line (((picMap.ScaleWidth/360) * i), 0)-(((picMap.ScaleWidth/360) * i), picMap.ScaleHeight)
Next i

picMap.Line (0, (picMap.ScaleHeight/2))-(picMap.ScaleWidth, (picMap.ScaleHeight / 2))

'Now draw your latitude lines... this is somewhat of a guess
Dim inches_away as long
For i = 5 to 85 step 5
inches_away = CLng(70/(miles_per_inch * Cos(i)))

' plot the latitude in the Northern Hemisphere
picMap.Line (0, ((picMap.ScaleHeight/2) + inches_away))-(picMap.ScaleWidth, ((picMap.ScaleHeight / 2) + inches_away))

' plot the latitude in the Southern Hemisphere
picMap.Line (0, ((picMap.ScaleHeight/2) - inches_away))-(picMap.ScaleWidth, ((picMap.ScaleHeight / 2) - inches_away))

next i
0

Author Commented:

I never use inches that is why I wanted to the formula I gave you and use the solution from the link:
http://www.expertsexchange.com/Programming/Languages/C/Q_23435832.htmlsfQueryTermInfo=1+10+mercat
is it possible to use that formula insted of 70/(S*cos(theta))?

0

Commented:
Well, it's possible, but then I don't know what scale you would use when defining the ScaleHeight and Width of your picturebox.

In the example I posted above, "inches" is just an arbitrary unit of measurement... I'm just using it to get a value for the scaleheight and scalewidth.

I never took Calculus, so, can't really comment on what you could do with the other formula.
0

Author Commented:
I thought when we have the x and y to degree we could use the formula
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
double lambda0;  /* <--- the reference longitude */
/* coordinates (in radians) : */
double lambda;   /* <--- the longitude */
double phi;      /* <--- the latitude */
/* corresponding Mercator coordinates : */
double x = lambda - lambda0;
double y = log(tan(phi) + (1.0 / cos(phi)));
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
just convert it to visual basic
would scale mode not be pixel as we are using pixel point for x and y?
0

Commented:
Well, for declaring variables, you would just:

Dim lambda0 as double
lambda0 = .....

I'm sure that Cos is the same in VB, pretty sure Tan is the same, not sure about Log, that would be the only one you'd have to lookup.

But, you've got a host of issues with this.  This is meant to project a lat/lon, so, you probably don't need to do anything with the X coordiate, since you're trying to simply determine what Y coordinate to draw the line on the map, right?

Note that the lambda and phi are lat/lons that have been converted to Radians... it is not hard to convert degrees to radians, but that's another step you need to do, if your coordinates originate as degrees.  Finally, the resulting Y is in Mercator coordinates, but I don't know if that is in Radians or Degrees or if the Mercator coordinate system is different than the standard, so, you'd have to be sure that your picturebox scales match the Mercator scale (which may be the original scale you specified).
0

Author Commented:
Yes all I want is the grid lines to work and I will be using degree as we did first as from -180 to 180 and -90 to 90. But dont you think pixel would be the scaling system?

0

Author Commented:
To convert degrees to radians, multiply degrees by pi / 180
0

Author Commented:
yes this did the trick I think, :-)
looks like a mercator map to me,  I will troubleshoot it more through.
Any idea how best I would do the zoom part?
0

Author Commented:
This is the code
``````Dim width_ratio As Single
Dim height_ratio As Single

Me.Width = 15000
Me.Height = 7500
picMap.Left = 500
picMap.Width = Me.Width - 1000
picMap.Top = 500
picMap.Height = Me.Height - 1000
picMap.AutoRedraw = True

SetPicScaleMode

width_ratio = picMap.Width / picMap.Height
height_ratio = picMap.Height / picMap.Width

' For latitude lines
For i = -90 To 90 Step 30 '<-- can be any step 30,15,10 for the lines
lat = Log(Tan(lat) + (1# / Cos(lat)))
picMap.Line (picMap.ScaleLeft, lat)-((picMap.ScaleWidth + picMap.ScaleLeft), lat)
Next i

' For longitude lines
For i = -180 To 180 Step 30 '<-- can be any step 30,15,10 for the lines
picMap.Line (i, picMap.ScaleTop)-(i, (picMap.ScaleHeight + picMap.ScaleTop))
Next i

' Draw the X axis
'picMap.Line (picMap.ScaleLeft, 0)-((picMap.ScaleWidth + picMap.ScaleLeft), 0)
' Draw the Y axis
'picMap.Line (0, picMap.ScaleTop)-(0, (picMap.ScaleHeight + picMap.ScaleTop))
End Sub

Private Sub Form_Resize()
If Me.Width / Me.Height < width_ratio Then
picMap.Width = Me.Width - 1000
picMap.Height = picMap.Width * height_ratio
Else
picMap.Height = Me.Height - 1000
picMap.Width = picMap.Height * width_ratio
End If

' Reset the pictureboxes scales
SetPicScaleMode

' Clear the picturebox and redraw it
picMap.Cls

' For latitude lines
For i = -90 To 90 Step 30 '<-- can be any step 30,15,10 for the lines
lat = Log(Tan(lat) + (1# / Cos(lat)))
picMap.Line (picMap.ScaleLeft, lat)-((picMap.ScaleWidth + picMap.ScaleLeft), lat)
Next i

' For longitude lines
For i = -180 To 180 Step 30 '<-- can be any step 30,15,10 for the lines
picMap.Line (i, picMap.ScaleTop)-(i, (picMap.ScaleHeight + picMap.ScaleTop))
Next i

' Draw the X axis
'picMap.Line (picMap.ScaleLeft, 0)-((picMap.ScaleWidth + picMap.ScaleLeft), 0)
' Draw the Y axis
'picMap.Line (0, picMap.ScaleTop)-(0, (picMap.ScaleHeight + picMap.ScaleTop))
End Sub

Private Sub SetPicScaleMode()
picMap.ScaleMode = 0
picMap.ScaleLeft = -180
picMap.ScaleWidth = 360
picMap.ScaleTop = 90
picMap.ScaleHeight = -180
End Sub

Private Function DegreesToRadians(X As Double) As Double
Const pi = 3.14159265358979
'To convert degrees to radians, multiply degrees by pi/180.
DegreesToRadians = X * pi / 180
End Function

Private Function RadiansToDegrees(X As Double) As Double
Const pi = 3.14159265358979
RadiansToDegrees = X * (180 / pi)
End Function
``````
0

Commented:
Zooming should be easy, it just involves changing the scale properties.  So, for example, if you wanted to zoom in 50%, you would set the ScaleLeft to -90, ScaleWidth to 180, ScaleTop to 45, ScaleHeight to -90

0

Commented:
By the way, thanks for teaching me that formula...

lat = Log(Tan(lat) + (1# / Cos(lat)))

I don't often work with mercator projections, but it is nice to know how that works.
0

Author Commented:
is it as easy?
0

Author Commented:
0

Commented:
Pan is pretty easy too.  Let's say that you are at the 50% zoom, your ScaleLeft is -90, your ScaleWidth is 180 (which makes your Right positioned at +90), you can pan left or right by changing the ScaleLeft between 0 and -180, but leaving the ScaleWidth set to 180, just be sure that you check to ensure you don't go out of bounds... for example, if you set the ScaleLeft to +10, then with a ScaleWidth of 180 your Right most point would be at 190 which isn't a valid coordinate.

Same thing for up and down.  At 50% zoom, you can move the ScaleTop between 90 and 0 (it is centered at 45), leaving the ScaleHeight at -90.
0

Author Commented:
Thank you mdougan for all the help and clear direction that was easy to follow.
I have still to fix the scaling better, but when I will work on the zoom and pan stuff I know it
will come along the way.
0

Commented:
Terrific, good luck!
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.