Search code examples
c#winformscoordinatespanelscaling

Drawing sets of coordinates to pixels so they mutually scale


So I have a List<object> of longitude and latitude coordinates of two points, and I need to connect the line between them. The trick is to display all of the lines within a panel so that they are scaled within the panel's dimensions (converting coordinate numbers to match the pixels) and I almost got it. However I'm confounded by some unknown problem. The code is:

int canvasWidth = panel1.Width,
            canvasHeight = panel1.Height;

        var minX1 = tockeKoordinate.Min(x => x.startX);
        var minX2 = tockeKoordinate.Min(x => x.endX);
        var minX = Math.Min(minX1, minX2);

        var maxX1 = tockeKoordinate.Max(x => x.startX);
        var maxX2 = tockeKoordinate.Max(x => x.endX);
        var maxX = Math.Max(maxX1, maxX2);

        var maxY1 = tockeKoordinate.Max(x => x.startY);
        var maxY2 = tockeKoordinate.Max(x => x.endY);
        var maxY = Math.Max(maxY1, maxY2);

        var minY1 = tockeKoordinate.Min(x => x.startY);
        var minY2 = tockeKoordinate.Min(x => x.endY);
        var minY = Math.Min(minY1, minY2);


        double coordinatesWidth = Math.Abs(maxX - minX),
               coordinatesHeight = Math.Abs(maxY - minY);

        float coefWidth = (float)coordinatesWidth / canvasWidth,
              coefHeight = (float)coordinatesHeight / canvasHeight;

Basically I check the List for minimum and maximum XY coordinates, so I know what the extreme values are. Then I use a coeficient value to recalculate the coords in pixels so that are within the panel. When I use this:

 drawLine(Math.Abs((float)(line.startX - minX) / coefWidth),
                Math.Abs((float)(line.startY - minY) / coefHeight),
                Math.Abs((float)(line.endX - maxX) / coefWidth),
                Math.Abs((float)(line.endY - maxY) / coefHeight));

which is in foreach loop that iterates trough all the elements from the List . The drawline() method is as follows:

private void drawLine(float startX, float startY, float endX, float endY)
    {

        PointF[] points =
        {
            new PointF(startX, startY),
            new PointF(endX, endY),
        };
        g.DrawLine(myPen, points[0], points[1]);
    }

WHen all of this is put together, I get this picture:

enter image description here

I know for a fact that the "lines" should be connected and form shapes, in this case they represent roads in a suburban area. I figured that it treats every coordinate set like it is the only one and then scales it to the panel dimensions. Actually it should scale it in reference to all of the other coordinates

It should "zoom" them out and connect with each other, because that is the way I defined the panel dimensions and everything else.

EDIT: ToW's solution did the trick, with this line of code changed to use my List:

foreach (var line in tockeKoordinate)
        {

            gp.AddLine((float)(line.startX), (float)(line.startY), (float)(line.endX), (float)(line.endY));
gp.CloseFigure();

        }

End result when working properly: enter image description here


Solution

  • As far as I can see your best bet would be to add all those lines to a GraphicsPath.

    After it is complete you can look at its bounding rectangle and compare it to the size your Panel offers.

    Then you can calculate a scale for the Graphics object to draw with and also a translation.

    Finally you draw the lines with Graphics.DrawPath.

    All with just 2 division on your side :-)

    Here is an example:

    enter image description here

    private void panel1_Paint(object sender, PaintEventArgs e)
    {
        Graphics G = e.Graphics;
        Random R = new Random(13);
        GraphicsPath gp = new GraphicsPath();
        for (int i = 0; i < 23; i++)
        {
            gp.AddLine(R.Next(1234), R.Next(1234), R.Next(1234), R.Next(1234));
            gp.CloseFigure();  // disconnect lines
        }
        RectangleF rect = gp.GetBounds();
    
        float scale = Math.Min(1f * panel1.Width / rect.Width, 
                               1f * panel1.Height / rect.Height);
    
    
        using (Pen penUnscaled = new Pen(Color.Blue, 4f))
        using (Pen penScaled = new Pen(Color.Red, 4f))
        {
            G.Clear(Color.White);
            G.DrawPath(penUnscaled, gp);
            G.ScaleTransform(scale, scale);
            G.TranslateTransform(-rect.X, -rect.Y);
            G.DrawPath(penScaled, gp);
        }
    }
    

    A few notes:

    • The blue lines do not fit onto the panel
    • The red lines are scaled down to fit
    • The Pen is scaled along with the rest of the Graphics but won't go under 1f.
    • To create connected lines do add a PointF[] or, more convenient a List<PointF>.ToArray().
    • I really should have used panel1.ClientSize.Width instead of panel1.Width etc..; now it is off a tiny bit at the bottom; bad boy me ;-)