About the GPoint - Programming question on mission planner

Greetings, Open source community.

As I’m not professional with programming, I’m having hard time using mission planner customization with plugin. I saw here many professionals have high-grade skills and knowledges, so I want to share my code and get help.

[What I want to do]

I think I can use open-sourced Mission planner for global productivity with plugin function.

If I use VTOL on ships and deep sea, I can draw searched area on sea with plugin function(I can overlay text and color on sea) and draw circle that camera is pointing(calculated with aircraft’s height and angle of gimbal). even I can mark text ship’s anchorage area on mission planner.
and because ship is using different coordinates(deg, min, secs), mission planner should use that coordinates when used in ship.

I saw possibility on the plugin function. I already implemented some functions already. I was even able to run web pages on mission planner.

It would be helpful to VTOL operator. at least I will also use this on my operation.

So Now I’m digging on how to overlay text and colors on the map.

[The Problem]

The coordinate system is not straightforward and is different than I initially thought.

I successed to display polygon or text panel or mark on the map, but displaying only text on the map was not successful

I used new PointLatLng() to create [lat, long] coordinate and used Control.FromLatLngToLocal() to make GPoint object.

that should work for overlay text on the map, but won’t work.

If I use the coordinates, the text is displayed in weird positions.

[Code that regenerate Problem]

Regenerate.zip (2.9 KB)

using System;
using System.Windows.Forms;

using MissionPlanner;
using MissionPlanner.Utilities;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using MissionPlanner.Controls;

using GMap.NET;
using GMap.NET.WindowsForms;
using System.Drawing.Drawing2D;

namespace RegenarateErrorPlugins
{
    public class RegenerateErrorPlugins : MissionPlanner.Plugin.Plugin
    {
        private AnchorageTextOverlay AnchorageTextOverlayFD;
        private AnchorageTextOverlay AnchorageTextOverlayFP;

        public override string Name
        {
            get { return "Regenerate Error Plugins"; }
        }

        public override string Version
        {
            get { return "0.30"; }
        }

        public override string Author
        {
            get { return "Open Source Community"; }
        }

        public override bool Init()
        {
            return true;
        }

        public override bool Loaded()
        {
            //1. Adding the menu button items.
            var rootbut_FD = new ToolStripMenuItem("Regenerate Error Plugins");
            ToolStripItemCollection col_FD = Host.FDMenuMap.Items; 
            col_FD.Add(rootbut_FD);

            var but = new ToolStripMenuItem("Start/Stop Displaying Anchorage Text Overlay on FD");
            but.Click += DisplayingAnchorageTextOverlay_FD_Click;
            rootbut_FD.DropDownItems.Add(but);

            //2. Adding the overlays for displaying text and polygons on the map.
            AnchorageTextOverlayFD = new AnchorageTextOverlay(Host.FDGMapControl);//gave parameter FDGMapControl object for possible future use. but I'm not sure it is right way to programming.

            AnchorageTextOverlayFD.IsVisibile = true;

            Host.FDGMapControl.Overlays.Add(AnchorageTextOverlayFD);

            MarkingAnchorageArea(); //Draw polygons on the map.

            //3. loop rate should be fast when I get anchored ship's datas
            loopratehz = 12.0f;

            return true;
        }

        public override bool Loop()
        {
            loopratehz = 20.0f; //loop rate should be fast when I get anchored ship's datas
            return true;
        }

        public override bool Exit()
        {
            return true;
        }

        //Define the function executed when clicks the button.(Event handler)
        private void DisplayingAnchorageTextOverlay_FD_Click(object sender, EventArgs e)
        {
            if (AnchorageTextOverlayFD.IsVisibile)
            {
                AnchorageTextOverlayFD.IsVisibile = false;
                AnchorageTextOverlayFD.IsVisibile = false;
                MessageBox.Show("Stopped displaying anchorage area text", "Display anchorage area options");
            }
            else
            {
                AnchorageTextOverlayFD.IsVisibile = true;
                AnchorageTextOverlayFD.IsVisibile = true;
                MessageBox.Show("Started displaying anchorage area text", "Display anchorage area options");
            }
        }

        //Draw Color-filled-Polygons on Ship's Anchorage area
        private void MarkingAnchorageArea()
        {
            var points_N1 = new List<PointLatLng>();
            points_N1.Add(new PointLatLng(35.07641667, 129.0333333));
            points_N1.Add(new PointLatLng(35.07641667, 129.0393056));
            points_N1.Add(new PointLatLng(35.06713889, 129.0530833));
            points_N1.Add(new PointLatLng(35.06713889, 129.0344167));

            var points_N2 = new List<PointLatLng>();
            points_N2.Add(new PointLatLng(35.07644444, 129.0265278));
            points_N2.Add(new PointLatLng(35.07644444, 129.0315));
            points_N2.Add(new PointLatLng(35.053, 129.0312222));
            points_N2.Add(new PointLatLng(35.05294444, 129.0172778));
            points_N2.Add(new PointLatLng(35.06147222, 129.0249167));
            points_N2.Add(new PointLatLng(35.07413889, 129.0249167));

            var points_N3 = new List<PointLatLng>();
            points_N3.Add(new PointLatLng(35.06713889, 129.0344167));
            points_N3.Add(new PointLatLng(35.06713889, 129.0530833));
            points_N3.Add(new PointLatLng(35.04886111, 129.0800556));
            points_N3.Add(new PointLatLng(35.04430556, 129.0370833));

            var points_N4 = new List<PointLatLng>();
            points_N4.Add(new PointLatLng(35.04430556, 129.0370833));
            points_N4.Add(new PointLatLng(35.04886111, 129.0800556));
            points_N4.Add(new PointLatLng(35.036, 129.0899167));
            points_N4.Add(new PointLatLng(35.02686111, 129.0439167));

            var points_N5 = new List<PointLatLng>();
            points_N5.Add(new PointLatLng(35.02686111, 129.0439167));
            points_N5.Add(new PointLatLng(35.036, 129.0899167));
            points_N5.Add(new PointLatLng(35.00313889, 129.0800278));
            points_N5.Add(new PointLatLng(35.00319444, 129.0439167));

            var points_O2 = new List<PointLatLng>();
            points_O2.Add(new PointLatLng(35.09611111, 129.0683333));
            points_O2.Add(new PointLatLng(35.08944444, 129.075));
            points_O2.Add(new PointLatLng(35.09444444, 129.0805556));
            points_O2.Add(new PointLatLng(35.1, 129.0713889));

            var polygon_N1 = new GMapPolygon(points_N1, "Polygon");
            var polygon_N2 = new GMapPolygon(points_N2, "Polygon");
            var polygon_N3 = new GMapPolygon(points_N3, "Polygon");
            var polygon_N4 = new GMapPolygon(points_N4, "Polygon");
            var polygon_N5 = new GMapPolygon(points_N5, "Polygon");
            var polygon_O2 = new GMapPolygon(points_O2, "Polygon");

            polygon_N1.Fill = new SolidBrush(Color.FromArgb(50, Color.Blue));
            polygon_N1.Stroke = new Pen(Color.Blue, 2);
            polygon_N2.Fill = new SolidBrush(Color.FromArgb(50, Color.Blue));
            polygon_N2.Stroke = new Pen(Color.Blue, 2);
            polygon_N3.Fill = new SolidBrush(Color.FromArgb(50, Color.Blue));
            polygon_N3.Stroke = new Pen(Color.Blue, 2);
            polygon_N4.Fill = new SolidBrush(Color.FromArgb(50, Color.Blue));
            polygon_N4.Stroke = new Pen(Color.Blue, 2);
            polygon_N5.Fill = new SolidBrush(Color.FromArgb(50, Color.Blue));
            polygon_N5.Stroke = new Pen(Color.Blue, 2);
            polygon_O2.Fill = new SolidBrush(Color.FromArgb(50, Color.Blue));
            polygon_O2.Stroke = new Pen(Color.Blue, 2);

            AnchorageTextOverlayFD.Polygons.Add(polygon_N1);
            AnchorageTextOverlayFD.Polygons.Add(polygon_N2);
            AnchorageTextOverlayFD.Polygons.Add(polygon_N3);
            AnchorageTextOverlayFD.Polygons.Add(polygon_N4);
            AnchorageTextOverlayFD.Polygons.Add(polygon_N5);
            AnchorageTextOverlayFD.Polygons.Add(polygon_O2);
            AnchorageTextOverlayFD.Polygons.Add(polygon_N1);
            AnchorageTextOverlayFD.Polygons.Add(polygon_N2);
            AnchorageTextOverlayFD.Polygons.Add(polygon_N3);
            AnchorageTextOverlayFD.Polygons.Add(polygon_N4);
            AnchorageTextOverlayFD.Polygons.Add(polygon_N5);
            AnchorageTextOverlayFD.Polygons.Add(polygon_O2);
        }
    }//end of Class Regenerate Error Plugins

    //I inheritted GMapOverlay class to override rendering logic to overlay text on certain map's coordinates
    public class AnchorageTextOverlay : GMapOverlay
    {
        //Latitude and Longitude Coordinates.
        private PointLatLng N1LatLng;
        private PointLatLng N2LatLng;
        private PointLatLng N3LatLng;
        private PointLatLng N4LatLng;
        private PointLatLng N5LatLng;

        //Pixel Coordinates.
        private GPoint N1GPoint;
        private GPoint N2GPoint;
        private GPoint N3GPoint;
        private GPoint N4GPoint;
        private GPoint N5GPoint;

        private GMapControl MyFDGMapControl;

        public AnchorageTextOverlay(GMapControl FDGMapControl) //get parameter "FDGMapControl" for future use. but I'm not sure it is right way for programming.
        {
            MyFDGMapControl = FDGMapControl;
        }

        // Custom rendering logic here.
        // I put text overlay logic here.
        public override void OnRender(IGraphics g)
        {
            base.OnRender(g);

            N1LatLng = new PointLatLng(35.07187, 129.03975);
            N2LatLng = new PointLatLng(35.05754, 19.0270424);
            N3LatLng = new PointLatLng(35.05529, 129.05107);
            N4LatLng = new PointLatLng(35.03800, 129.06240);
            N5LatLng = new PointLatLng(35.01567, 129.06549);

            N1GPoint = Control.FromLatLngToLocal(N1LatLng);
            N2GPoint = Control.FromLatLngToLocal(N2LatLng);
            N3GPoint = Control.FromLatLngToLocal(N3LatLng);
            N4GPoint = Control.FromLatLngToLocal(N4LatLng);
            N5GPoint = Control.FromLatLngToLocal(N5LatLng);

            //Create color for font
            Color modifiedDarkGray = ControlPaint.DarkDark(Color.DarkGray); // get a darker version of DarkGray
            float brightness = 0.2f; // increase brightness by 20%
            float saturation = 0.3f; // saturation at 0 firstly(grayscale)
            float hue = 0.3f; // keep hue(color) at 0 (gray)
            float alpha = 1.0f; // keep alpha at 1 (opaque)
            modifiedDarkGray = FromAhsb(alpha, hue, saturation, brightness);

            // Add text above the polygons.
            using (Font font = new Font("Arial", 12))
            {
                using (SolidBrush textBrush = new SolidBrush(modifiedDarkGray)) 
                {
                    SizeF textSize = g.MeasureString("N1", font);
                    PointF textLocation = new PointF(N1GPoint.X, N1GPoint.Y);
                    g.DrawString("N1", font, textBrush, textLocation);

                    textSize = g.MeasureString("N2", font);
                    textLocation = new PointF(N2GPoint.X, N2GPoint.Y);
                    g.DrawString("N2", font, textBrush, textLocation);

                    textSize = g.MeasureString("N3", font);
                    textLocation = new PointF(N3GPoint.X, N3GPoint.Y);
                    g.DrawString("N3", font, textBrush, textLocation);

                    textSize = g.MeasureString("N4", font);
                    textLocation = new PointF(N4GPoint.X, N4GPoint.Y);
                    g.DrawString("N4", font, textBrush, textLocation);

                    textSize = g.MeasureString("N5", font);
                    textLocation = new PointF(N5GPoint.X, N5GPoint.Y);
                    g.DrawString("N5", font, textBrush, textLocation);
                }
            }
        }

        //functions that get color from Ahsb, for the font.
        private static Color FromAhsb(float alpha, float hue, float saturation, float brightness)
        {
            if (0 > alpha || 1 < alpha)
            {
                throw new ArgumentOutOfRangeException("alpha", alpha, "Value must be between 0 and 1.");
            }
            if (0 > hue || 360 < hue)
            {
                throw new ArgumentOutOfRangeException("hue", hue, "Value must be between 0 and 360.");
            }
            if (0 > saturation || 1 < saturation)
            {
                throw new ArgumentOutOfRangeException("saturation", saturation, "Value must be between 0 and 1.");
            }
            if (0 > brightness || 1 < brightness)
            {
                throw new ArgumentOutOfRangeException("brightness", brightness, "Value must be between 0 and 1.");
            }

            if (0 == saturation)
            {
                return Color.FromArgb((int)(alpha * 255), (int)(brightness * 255), (int)(brightness * 255), (int)(brightness * 255));
            }

            float fMax, fMid, fMin;
            int iSextant, iMax, iMid, iMin;

            if (0.5 < brightness)
            {
                fMax = brightness - (brightness * saturation) + saturation;
                fMin = brightness + (brightness * saturation) - saturation;
            }
            else
            {
                fMax = brightness + (brightness * saturation);
                fMin = brightness - (brightness * saturation);
            }

            iSextant = (int)Math.Floor(hue / 60f);
            if (300f <= hue)
            {
                hue -= 360f;
            }
            hue /= 60f;
            hue -= 2f * (float)Math.Floor(((iSextant + 1f) % 6f) / 2f);
            fMid = ((brightness - fMin) * hue) + fMin;
            iMax = (int)(fMax * 255);
            iMid = (int)(fMid * 255);
            iMin = (int)(fMin * 255);

            switch (iSextant)
            {
                case 0:
                    return Color.FromArgb((int)(alpha * 255), iMax, iMid, iMin);
                case 1:
                    return Color.FromArgb((int)(alpha * 255), iMid, iMax, iMin);
                case 2:
                    return Color.FromArgb((int)(alpha * 255), iMin, iMax, iMid);
                case 3:
                    return Color.FromArgb((int)(alpha * 255), iMin, iMid, iMax);
                case 4:
                    // Modified DarkGray color
                    int r = 85, g = 85, b = 85;
                    return Color.FromArgb((int)(alpha * 255), r, g, b);
                default:
                    return Color.FromArgb((int)(alpha * 255), iMax, iMin, iMid);
            }
        }
    }//end of class AnchorageTextOverlay
}// end of Namespace Regenerate Error Plugins

[Screenshot of result of Code]
the text “N1”, “N2”, “N3”… is away from the blue colored polygon.

[Example of successful result of Code]
the text “N1”, “N2” “N3”… is on the blue colored polygon.

[Additional Request]
although I studied programming for long time, it was not my main job and I don’t know how to pull request on github, and have no clue how to edit typo on code.

anyway, I found typo in code : “visibile” in Overlay object. it should be “visible” and it made many errors and troubles when I began to program plugins.

solved problem.

I calculated coordinate value with control position.

Glad you find the bug. However, for displaying text, I would write a marker that displays text at a specific position on the map. This will disconnect the polygon and the text drawing. (although your solution works as well)

Hello everyone,

I am currently developing a plugin for Mission Planner that overlays wind details (received from the UAV) on a map using arrows and text. However, I am encountering a few challenges and would greatly appreciate any insights or suggestions from the community.

1. Text Offset from Arrows: The text, which displays wind direction and speed, is offset from its corresponding arrow. I am drawing the text using the Graphics.DrawString method at a point calculated from each grid box’s location. The text appears to be misaligned with the arrow, possibly due to incorrect coordinate calculation.

Coordinates Conversion
5

2. Continuous Rendering: The render loop seems to be running continuously, even when there is no interaction with the map (like panning or zooming). This is not optimal for performance.

3. Zoom Issue with Text Positioning: Upon changing the zoom level, the text stays at the same screen position but does not correspond to the same LatLng position, leading to misalignment with the arrows.

Attempts to Resolve:
I tried using a tooltip, but positioning it precisely near the arrows proved challenging. I am considering adding markers with text but haven’t found a successful method yet.

Is there anything you can add upon your expertise with the text drawing?

public class textOverlay : GMapOverlay
{
    public override void OnRender(IGraphics g)
    {
        var fontFamily = new FontFamily("Times New Roman");
        font = new Font(fontFamily, 32, FontStyle.Regular, GraphicsUnit.Pixel);
        solidBrush = new SolidBrush(Color.FromArgb(255, 0, 0, 255));
        foreach (var gridBox in OverlayControlsPlugin.gridOverlay.GridBoxDataList)
        {
            var radBearing = gridBox.Direction;
            var windPower = gridBox.Speed;
            var labelText = $"{radBearing}°\n{windPower} m/s";
            var screenPoint = gridBox.Local;
            g.DrawString(labelText, font, solidBrush, screenPoint.X, screenPoint.Y);
        }
    }
}

Look up the waypoint marker, GMapMarkerWP, it shows example of precise positioning of text.
The render loop should work when the vehicle position is updated… so almost continuously,

2 Likes

It looks great plugin. let me check it after a week, and I will share my code

1 Like

I intend to add some more Gui features and maybe some warning when the wind will cause problem returning to the home point.

I will upload to GitHub upon completion and paper submission.

Best Regards
Theodoros