// AirScout Aircraft Scatter Prediction // Copyright (C) DL2ALF // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Linq; using System.Text; using System.Windows; using System.Windows.Forms; using System.Windows.Forms.VisualStyles; using GMap.NET; using GMap.NET.MapProviders; using GMap.NET.WindowsForms; using GMap.NET.WindowsForms.Markers; using GMap.NET.WindowsForms.ToolTips; using System.IO; using System.IO.Ports; using System.Net; using System.Net.Sockets; using System.Threading; using System.Globalization; using System.Runtime.InteropServices; using System.Reflection; using System.Configuration; using System.Diagnostics; using AirScout; using AirScout.Core; using AirScout.Aircrafts; using ScoutBase; using ScoutBase.Core; using ScoutBase.Elevation; using ScoutBase.Stations; using ScoutBase.Propagation; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SQLiteDatabase; using System.Xml; using System.Xml.Serialization; using System.Security.Cryptography; using OxyPlot; using OxyPlot.WindowsForms; using OxyPlot.Series; using OxyPlot.Axes; using System.Data.SQLite; namespace AirScoutViewClient { public partial class MapViewDlg : Form { // Map // Overlays GMapOverlay gmo_Routes = new GMapOverlay("Routes"); GMapOverlay gmo_PropagationPaths = new GMapOverlay("PropagationPaths"); GMapOverlay gmo_Objects = new GMapOverlay("Objects"); GMapOverlay gmo_Planes = new GMapOverlay("Planes"); GMapOverlay gmo_Airports = new GMapOverlay("Airports"); GMapOverlay gmo_Callsigns = new GMapOverlay("Callsigns"); GMapOverlay gmo_NearestPaths = new GMapOverlay("PropagationPaths"); GMapOverlay gmo_NearestPlanes = new GMapOverlay("Planes"); // Routes GMapRoute gmr_FullPath; GMapRoute gmr_VisiblePpath; GMapRoute gmr_NearestFull; GMapRoute gmr_NearestVisible; // Markers GMapMarker gmm_MyLoc; GMapMarker gmm_DXLoc; GMapMarker gmm_CurrentMarker; bool isDraggingMarker; // Bitmap indices private int bmindex_darkorange; private int bmindex_lightgreen; private int bmindex_red; private int bmindex_gray; private int bmindex_magenta; // charting // path chart PlotModel pm_Path = new PlotModel(); PlotView pv_Path = new PlotView(); LinearAxis Path_X = new LinearAxis(); LinearAxis Path_Y = new LinearAxis(); AreaSeries Path_Elevation = new AreaSeries(); LineSeries Min_H1 = new LineSeries(); LineSeries Min_H2 = new LineSeries(); LineSeries Max_H = new LineSeries(); AreaSeries Min_H = new AreaSeries(); LineSeries Planes_Hi = new LineSeries(); LineSeries Planes_Lo = new LineSeries(); // elevation chart PlotModel pm_Elevation = new PlotModel(); PlotView pv_Elevation = new PlotView(); LinearAxis Elevation_X = new LinearAxis(); LinearAxis Elevation_Y = new LinearAxis(); AreaSeries Elevation = new AreaSeries(); LineSeries LOS = new LineSeries(); // Tooltip font public Font ToolTipFont; public VarConverter VC = new VarConverter(); public static LogWriter Log; public DateTime CurrentTime; AIRSCOUTPLAYMODE PlayMode = AIRSCOUTPLAYMODE.NONE; CultureInfo CurrentCulture = Application.CurrentCulture; ElevationPathDesignator EPath = null; PropagationPathDesignator PPath = null; SortedList ActivePlanes = new SortedList(); private List SelectedPlanes = new List(); private VIEWCLIENTSTATUS VieClientStatus = VIEWCLIENTSTATUS.NONE; [CategoryAttribute("Directories")] [DescriptionAttribute("Application Directory")] public string AppDirectory { get { return Application.StartupPath.TrimEnd(Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); } } [CategoryAttribute("Directories")] [DescriptionAttribute("Local Application Data Directory")] public string AppDataDirectory { get { return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Application.CompanyName, Application.ProductName).TrimEnd(Path.DirectorySeparatorChar); } } [CategoryAttribute("Directories")] [DescriptionAttribute("Logfile Directory")] public string LogDirectory { get { // get Property string logdir = Properties.Settings.Default.Log_Directory; // replace Windows/Linux directory spearator chars logdir = logdir.Replace('\\', Path.DirectorySeparatorChar); logdir = logdir.Replace('/', Path.DirectorySeparatorChar); // set to default value if empty if (String.IsNullOrEmpty(logdir)) logdir = "Log"; // replace variables, if any logdir = VC.ReplaceAllVars(logdir); // remove directory separator chars at begin and end logdir = logdir.TrimStart(Path.DirectorySeparatorChar); logdir = logdir.TrimEnd(Path.DirectorySeparatorChar); // fully qualify path if (!logdir.Contains(Path.VolumeSeparatorChar)) logdir = Path.Combine(AppDataDirectory, logdir); return logdir; } } [CategoryAttribute("Directories")] [DescriptionAttribute("Tempfile Directory")] public string TmpDirectory { get { // get Property string tmpdir = Properties.Settings.Default.Tmp_Directory; // replace Windows/Linux directory spearator chars tmpdir = tmpdir.Replace('\\', Path.DirectorySeparatorChar); tmpdir = tmpdir.Replace('/', Path.DirectorySeparatorChar); // set to default value if empty if (String.IsNullOrEmpty(tmpdir)) tmpdir = "Tmp"; // replace variables, if any tmpdir = VC.ReplaceAllVars(tmpdir); // remove directory separator chars at begin and end tmpdir = tmpdir.TrimStart(Path.DirectorySeparatorChar); tmpdir = tmpdir.TrimEnd(Path.DirectorySeparatorChar); // fully qualify path if (!tmpdir.Contains(Path.VolumeSeparatorChar)) tmpdir = Path.Combine(AppDataDirectory, tmpdir); return tmpdir; } } public MapViewDlg() { Application.CurrentCulture = CultureInfo.InvariantCulture; // gets an instance of a LogWriter Log = LogWriter.Instance; InitializeComponent(); } private void Say (string msg) { if (tsl_Status.Text != msg) { tsl_Status.Text = msg; ss_Main.Refresh(); } } #region Initialization private void InitializeSettings() { Say("Getting settings..."); if (!SettingsFromJSON()) return; // initialize map // setting User Agent to fix Open Street Map issue 2016-09-20 GMap.NET.MapProviders.GMapProvider.UserAgent = "AirScout"; // set initial settings for main map gm_Main.MapProvider = GMapProviders.Find(Properties.Settings.Default.Map_Provider); gm_Main.IgnoreMarkerOnMouseWheel = true; gm_Main.MinZoom = 0; gm_Main.MaxZoom = 20; gm_Main.Zoom = 6; gm_Main.DragButton = System.Windows.Forms.MouseButtons.Left; gm_Main.CanDragMap = true; gm_Main.ScalePen = new Pen(Color.Black, 3); gm_Main.MapScaleInfoEnabled = true; gm_Main.Overlays.Add(gmo_Airports); gm_Main.Overlays.Add(gmo_Callsigns); gm_Main.Overlays.Add(gmo_PropagationPaths); gm_Main.Overlays.Add(gmo_Routes); gm_Main.Overlays.Add(gmo_Objects); gm_Main.Overlays.Add(gmo_Planes); // center map gm_Main.Position = new PointLatLng(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon); // intially fill dialog box elements and set band string[] bands = Bands.GetStringValuesExceptNoneAndAll(); foreach (string b in bands) cb_Band.Items.Add(b); BAND band = Properties.Settings.Default.Band; cb_Band.SelectedItem = Bands.GetStringValue(band); PlayMode = AIRSCOUTPLAYMODE.PAUSE; VieClientStatus = VIEWCLIENTSTATUS.CONNECTED; UpdateStatus(); Say(""); } private Bitmap CreatePlaneIcon(Color color) { // get the basic icon Bitmap bm = new Bitmap(AppDirectory + Path.DirectorySeparatorChar + Properties.Settings.Default.Planes_IconFileName); // read the content and change color of each pixel for (int j = 0; j < bm.Width; j++) { for (int k = 0; k < bm.Height; k++) { // get the color of each pixel Color c = bm.GetPixel(j, k); // check if not transparent if (c.A > 0) { // change color bm.SetPixel(j, k, Color.FromArgb(c.A, color.R, color.G, color.B)); } } } return bm; } public static Color ColorFromHSV(double hue, double saturation, double value) { int hi = Convert.ToInt32(Math.Floor(hue / 60)) % 6; double f = hue / 60 - Math.Floor(hue / 60); value = value * 255; int v = Convert.ToInt32(value); int p = Convert.ToInt32(value * (1 - saturation)); int q = Convert.ToInt32(value * (1 - f * saturation)); int t = Convert.ToInt32(value * (1 - (1 - f) * saturation)); if (hi == 0) return Color.FromArgb(255, v, t, p); else if (hi == 1) return Color.FromArgb(255, q, v, p); else if (hi == 2) return Color.FromArgb(255, p, v, t); else if (hi == 3) return Color.FromArgb(255, p, q, v); else if (hi == 4) return Color.FromArgb(255, t, p, v); else return Color.FromArgb(255, v, p, q); } public Color GetColor(double power) { double H = power * 0.3; // Hue (note 0.4 = Green, see huge chart below) double S = 0.95; // Saturation double B = 0.95; // Brightness return ColorFromHSV((float)H * 360, (float)S, (float)B); } private Bitmap CreateAirportIcon(int alpha) { // get the basic icon Bitmap bm = new Bitmap(AppDirectory + Path.DirectorySeparatorChar + Properties.Settings.Default.Airports_IconFileName); // read the content and change opacity of each pixel for (int j = 0; j < bm.Width; j++) { for (int k = 0; k < bm.Height; k++) { // get the color of each pixel Color c = bm.GetPixel(j, k); // check if not transparent if (c.A > 0) { // change color bm.SetPixel(j, k, Color.FromArgb(alpha, c.R, c.G, c.B)); } } } return bm; } private void InitializeIcons() { // create extra icons regular size Log.WriteMessage("Started."); try { // now generate 0% - 100% colored planes for (int i = 0; i <= 100; i++) { Bitmap bm = CreatePlaneIcon(GetColor(1.0f - (float)i / 100.0f)); il_Planes_L.Images.Add(bm); il_Planes_M.Images.Add(bm); il_Planes_H.Images.Add(bm); il_Planes_S.Images.Add(bm); } il_Planes_L.Images.Add(CreatePlaneIcon(Color.Gray)); il_Planes_M.Images.Add(CreatePlaneIcon(Color.Gray)); il_Planes_H.Images.Add(CreatePlaneIcon(Color.Gray)); il_Planes_S.Images.Add(CreatePlaneIcon(Color.Gray)); bmindex_gray = il_Planes_M.Images.Count - 1; il_Planes_L.Images.Add(CreatePlaneIcon(Color.LightGreen)); il_Planes_M.Images.Add(CreatePlaneIcon(Color.LightGreen)); il_Planes_H.Images.Add(CreatePlaneIcon(Color.LightGreen)); il_Planes_S.Images.Add(CreatePlaneIcon(Color.LightGreen)); bmindex_lightgreen = il_Planes_M.Images.Count - 1; il_Planes_L.Images.Add(CreatePlaneIcon(Color.DarkOrange)); il_Planes_M.Images.Add(CreatePlaneIcon(Color.DarkOrange)); il_Planes_H.Images.Add(CreatePlaneIcon(Color.DarkOrange)); il_Planes_S.Images.Add(CreatePlaneIcon(Color.DarkOrange)); bmindex_darkorange = il_Planes_M.Images.Count - 1; il_Planes_L.Images.Add(CreatePlaneIcon(Color.Red)); il_Planes_M.Images.Add(CreatePlaneIcon(Color.Red)); il_Planes_H.Images.Add(CreatePlaneIcon(Color.Red)); il_Planes_S.Images.Add(CreatePlaneIcon(Color.Red)); bmindex_red = il_Planes_M.Images.Count - 1; il_Planes_L.Images.Add(CreatePlaneIcon(Color.Magenta)); il_Planes_M.Images.Add(CreatePlaneIcon(Color.Magenta)); il_Planes_H.Images.Add(CreatePlaneIcon(Color.Magenta)); il_Planes_S.Images.Add(CreatePlaneIcon(Color.Magenta)); bmindex_magenta = il_Planes_M.Images.Count - 1; il_Airports.Images.Add(CreateAirportIcon(255)); } catch (Exception ex) { Log.WriteMessage(ex.ToString(), LogLevel.Error); } Log.WriteMessage("Finished."); } private void InitializeCharts() { // propagation path chart pm_Path.Title = String.Empty; pm_Path.DefaultFontSize = 6F; pm_Path.IsLegendVisible = false; pv_Path.BackColor = Color.White; pv_Path.Model = pm_Path; // add axes pm_Path.Axes.Clear(); // add X-axis Path_X.IsZoomEnabled = false; Path_X.Maximum = 1000; Path_X.Minimum = 0; Path_X.MajorGridlineStyle = LineStyle.Solid; Path_X.MinorGridlineStyle = LineStyle.Dot; Path_X.Position = AxisPosition.Bottom; this.pm_Path.Axes.Add(Path_X); // add Y-axis Path_Y.IsZoomEnabled = false; Path_Y.Maximum = 20000; Path_Y.Minimum = 0; Path_Y.MajorGridlineStyle = LineStyle.Solid; Path_Y.MinorGridlineStyle = LineStyle.Dot; Path_Y.Position = AxisPosition.Left; this.pm_Path.Axes.Add(Path_Y); // add series pm_Path.Series.Clear(); Path_Elevation.Title = "Elevation"; Min_H1.Title = "Min_H1"; Min_H1.StrokeThickness = 2; Min_H1.LineStyle = LineStyle.Solid; Min_H1.Color = OxyColors.Red; Min_H2.Title = "Min_H2"; Min_H2.StrokeThickness = 2; Min_H2.LineStyle = LineStyle.Solid; Min_H2.Color = OxyColors.Gold; Max_H.Title = "Max_H"; Max_H.StrokeThickness = 2; Max_H.LineStyle = LineStyle.Dot; Max_H.Color = OxyColors.DarkBlue; Min_H.Title = "Min_H"; Min_H.StrokeThickness = 0; Min_H.LineStyle = LineStyle.Solid; Min_H.Color = OxyColors.Magenta.ChangeSaturation(0.4); Planes_Hi.Title = "Planes_Hi"; Planes_Hi.Color = OxyColors.Transparent; Planes_Hi.MarkerType = MarkerType.Square; Planes_Hi.MarkerFill = OxyColors.Magenta; Planes_Lo.Title = "Planes_Lo"; Planes_Lo.Color = OxyColors.Transparent; Planes_Lo.MarkerType = MarkerType.Square; Planes_Lo.MarkerFill = OxyColors.Gray; pm_Path.Series.Add(Path_Elevation); pm_Path.Series.Add(Min_H1); pm_Path.Series.Add(Min_H2); pm_Path.Series.Add(Max_H); pm_Path.Series.Add(Min_H); pm_Path.Series.Add(Planes_Hi); pm_Path.Series.Add(Planes_Lo); // add legend pm_Path.LegendTitle = ""; pm_Path.LegendPosition = LegendPosition.TopRight; pm_Path.LegendBackground = OxyColors.White; pm_Path.LegendBorder = OxyColors.Black; pm_Path.LegendBorderThickness = 1; // add control this.gb_Path.Controls.Add(pv_Path); pv_Path.Paint += new PaintEventHandler(pv_Path_Paint); // zoomed elevation chart pm_Elevation.Title = String.Empty; pm_Elevation.DefaultFontSize = 6F; pm_Elevation.IsLegendVisible = false; pv_Elevation.BackColor = Color.White; pv_Elevation.Model = pm_Elevation; // add series pm_Elevation.Series.Clear(); Elevation.Title = "Elevation"; LOS.Title = "LOS"; LOS.StrokeThickness = 2; LOS.Color = OxyColors.Black; pm_Elevation.Series.Add(Elevation); pm_Elevation.Series.Add(LOS); // create axes pm_Elevation.Axes.Clear(); // add X-axis Elevation_X.IsZoomEnabled = false; Elevation_X.Maximum = 1000; Elevation_X.Minimum = 0; Elevation_X.MajorGridlineStyle = LineStyle.Solid; Elevation_X.MinorGridlineStyle = LineStyle.Dot; Elevation_X.Position = AxisPosition.Bottom; this.pm_Elevation.Axes.Add(Elevation_X); // add Y-axis Elevation_Y.IsZoomEnabled = false; // auto size maximum // Elevation_Y.Maximum = maxelv, Elevation_Y.Minimum = 0; Elevation_Y.MajorGridlineStyle = LineStyle.Solid; Elevation_Y.MinorGridlineStyle = LineStyle.Dot; Elevation_Y.Position = AxisPosition.Left; this.pm_Elevation.Axes.Add(Elevation_Y); // add legend pm_Elevation.LegendTitle = ""; pm_Elevation.LegendPosition = LegendPosition.TopRight; pm_Elevation.LegendBackground = OxyColors.White; pm_Elevation.LegendBorder = OxyColors.Black; pm_Elevation.LegendBorderThickness = 1; // add control this.gb_Path.Controls.Add(pv_Elevation); gb_Path_Resize(this, null); } #endregion private void UpdateStatus() { // upddate TextBoxes tb_UTC.Text = CurrentTime.ToString("yyyy-MM-dd HH:mm:ss"); tb_UTC.BackColor = Color.LightSalmon; string call = Properties.Settings.Default.MyCall; tb_MyCall.SilentText = Properties.Settings.Default.MyCall; cb_MyLoc.SilentText = MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, Properties.Settings.Default.Locator_SmallLettersForSubsquares, (int)Properties.Settings.Default.Locator_MaxLength / 2, Properties.Settings.Default.Locator_AutoLength); tb_DXCall.Text = Properties.Settings.Default.DXCall; cb_DXLoc.SilentText = MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon, Properties.Settings.Default.Locator_SmallLettersForSubsquares, (int)Properties.Settings.Default.Locator_MaxLength / 2, Properties.Settings.Default.Locator_AutoLength); if (MaidenheadLocator.Check(cb_MyLoc.Text) && MaidenheadLocator.Check(cb_DXLoc.Text)) { tb_QTF.Text = Math.Round(LatLon.Bearing(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon)).ToString("F0"); tb_QRB.Text = Math.Round(LatLon.Distance(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon)).ToString("F0"); } else { tb_QRB.Text = "0"; tb_QTF.Text = "0"; } // colour Textbox if more precise lat/lon information is available if (MaidenheadLocator.IsPrecise(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, 3)) { cb_MyLoc.BackColor = Color.PaleGreen; } else { cb_MyLoc.BackColor = Color.FloralWhite; } // colour Textbox if more precise lat/lon information is available if (MaidenheadLocator.IsPrecise(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon, 3)) { cb_DXLoc.BackColor = Color.PaleGreen; } else { cb_DXLoc.BackColor = Color.FloralWhite; } cb_Band.SelectedItem = Bands.GetStringValue(Properties.Settings.Default.Band); } private string GetServerURL(string server, int port, string filename, string paramstr = "") { if (!server.StartsWith("http://")) server = "http://" + server; server = server.TrimEnd('/'); return (String.IsNullOrEmpty(paramstr)) ? server + ":" + port.ToString() + "/" + filename : server + ":" + port.ToString() + "/" + filename + "?" + paramstr; } private string GetJSONFromURL(string url) { // reads JSON from webserver // returns an empty string or null if webserver is not responding // returns error message from webserver in case of error // returns JSON string if successful string json = ""; // calculate url and get json // replace all vaiables, if any url = VC.ReplaceAllVars(url); try { Console.WriteLine("Creating web request: " + url); HttpWebRequest webrequest = (HttpWebRequest)HttpWebRequest.Create(url); webrequest.Referer = ""; webrequest.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0"; webrequest.Accept = "application/json, text/javascript, */*;q=0.01"; webrequest.AutomaticDecompression = System.Net.DecompressionMethods.Deflate | System.Net.DecompressionMethods.GZip; Console.WriteLine("Getting web response"); HttpWebResponse webresponse = (HttpWebResponse)webrequest.GetResponse(); Console.WriteLine("Reading stream"); using (StreamReader sr = new StreamReader(webresponse.GetResponseStream())) { json = sr.ReadToEnd(); } } catch (Exception ex) { Say(ex.Message); Log.WriteMessage(ex.ToString(), LogLevel.Error); // do nothing } return json; } public T PropertyFromJSON(string propertyName, string json) { var allobjects = JObject.Parse(json); JToken jo = allobjects[propertyName]; return (T)jo.ToObject(typeof(T)); } private bool SettingsFromJSON() { try { string json = ""; // get settings json = GetJSONFromURL(GetServerURL(Properties.Settings.Default.Server_URL, (int)Properties.Settings.Default.Server_Port, "settings.json")); if (String.IsNullOrEmpty(json)) { VieClientStatus = VIEWCLIENTSTATUS.CONNECTING; return false; } if (json.StartsWith("Error:")) { Say(json); return false; } // copy settings Properties.Settings.Default.Path_Band_Settings = PropertyFromJSON("Path_Band_Settings", json); Properties.Settings.Default.Map_Provider = PropertyFromJSON("Map_Provider", json); Properties.Settings.Default.MinLat = PropertyFromJSON("MinLat", json); Properties.Settings.Default.MinLon = PropertyFromJSON("MinLon", json); Properties.Settings.Default.MaxLat = PropertyFromJSON("MaxLat", json); Properties.Settings.Default.MaxLon = PropertyFromJSON("MaxLon", json); Properties.Settings.Default.MyCall = PropertyFromJSON("MyCall", json); Properties.Settings.Default.MyLat = PropertyFromJSON("MyLat", json); Properties.Settings.Default.MyLon = PropertyFromJSON("MyLon", json); // copy DXCall only if empty if (String.IsNullOrEmpty(Properties.Settings.Default.DXCall)) { Properties.Settings.Default.DXCall = PropertyFromJSON("DXCall", json); Properties.Settings.Default.DXLat = PropertyFromJSON("DXLat", json); Properties.Settings.Default.DXLon = PropertyFromJSON("DXLon", json); } // set band only if NONE if (Properties.Settings.Default.Band != BAND.BNONE) Properties.Settings.Default.Band = PropertyFromJSON("Band", json); Properties.Settings.Default.InfoWin_Position = PropertyFromJSON("InfoWin_Position", json); Properties.Settings.Default.InfoWin_Metric = PropertyFromJSON("InfoWin_Metric", json); Properties.Settings.Default.InfoWin_Track = PropertyFromJSON("InfoWin_Track", json); Properties.Settings.Default.InfoWin_Speed = PropertyFromJSON("InfoWin_Speed", json); Properties.Settings.Default.InfoWin_Alt = PropertyFromJSON("InfoWin_Alt", json); Properties.Settings.Default.InfoWin_Type = PropertyFromJSON("InfoWin_Type", json); Properties.Settings.Default.InfoWin_Time = PropertyFromJSON("InfoWin_Time", json); Properties.Settings.Default.InfoWin_Dist = PropertyFromJSON("InfoWin_Dist", json); Properties.Settings.Default.InfoWin_Angle = PropertyFromJSON("InfoWin_Angle", json); Properties.Settings.Default.InfoWin_Epsilon = PropertyFromJSON("InfoWin_Epsilon", json); Properties.Settings.Default.InfoWin_Squint = PropertyFromJSON("InfoWin_Squint", json); Properties.Settings.Default.Locator_AutoLength = PropertyFromJSON("Locator_AutoLength", json); Properties.Settings.Default.Locator_SmallLettersForSubsquares = PropertyFromJSON("Locator_SmallLettersForSubsquares", json); Properties.Settings.Default.Locator_MaxLength = PropertyFromJSON("Locator_MaxLength", json); } catch (Exception ex) { Say(ex.Message); Log.WriteMessage(ex.ToString(), LogLevel.Error); } return true; } private List MyLocationsFromJSON() { string json = ""; List l = null; try { string url = GetServerURL( Properties.Settings.Default.Server_URL, (int)Properties.Settings.Default.Server_Port, "location.json", "call=" + Properties.Settings.Default.MyCall + "&loc=all"); json = GetJSONFromURL(url); if (String.IsNullOrEmpty(json)) { VieClientStatus = VIEWCLIENTSTATUS.CONNECTING; return null; } if (json.StartsWith("Error:")) { Say(json); return null; } JsonSerializerSettings settings = new JsonSerializerSettings(); settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; settings.FloatFormatHandling = FloatFormatHandling.String; settings.Formatting = Newtonsoft.Json.Formatting.Indented; settings.Culture = CultureInfo.InvariantCulture; l = JsonConvert.DeserializeObject>(json, settings); } catch (Exception ex) { Say(ex.Message); Log.WriteMessage(ex.ToString(), LogLevel.Error); } return l; } private LocationDesignator MyLocationFromJSON() { string json = ""; LocationDesignator ld = null; try { string url = GetServerURL( Properties.Settings.Default.Server_URL, (int)Properties.Settings.Default.Server_Port, "location.json", "call=" + Properties.Settings.Default.MyCall + "&loc=" + MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, false, 3)); json = GetJSONFromURL(url); if (String.IsNullOrEmpty(json)) { VieClientStatus = VIEWCLIENTSTATUS.CONNECTING; return null; } if (json.StartsWith("Error:")) { Say(json); return null; } ld = LocationDesignator.FromJSON(json); } catch (Exception ex) { Say(ex.Message); Log.WriteMessage(ex.ToString(), LogLevel.Error); } return ld; } private List DXLocationsFromJSON() { string json = ""; List l = null; try { string url = GetServerURL( Properties.Settings.Default.Server_URL, (int)Properties.Settings.Default.Server_Port, "location.json", "call=" + Properties.Settings.Default.DXCall + "&loc=all"); json = GetJSONFromURL(url); if (String.IsNullOrEmpty(json)) { VieClientStatus = VIEWCLIENTSTATUS.CONNECTING; return null; } if (json.StartsWith("Error:")) { Say(json); return null; } JsonSerializerSettings settings = new JsonSerializerSettings(); settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; settings.FloatFormatHandling = FloatFormatHandling.String; settings.Formatting = Newtonsoft.Json.Formatting.Indented; settings.Culture = CultureInfo.InvariantCulture; l = JsonConvert.DeserializeObject>(json, settings); } catch (Exception ex) { Say(ex.Message); Log.WriteMessage(ex.ToString(), LogLevel.Error); } return l; } private LocationDesignator DXLocationFromJSON() { string json = ""; LocationDesignator ld = null; try { string url = GetServerURL( Properties.Settings.Default.Server_URL, (int)Properties.Settings.Default.Server_Port, "location.json", "&call=" + Properties.Settings.Default.DXCall + "&loc=" + MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon, false, 3)); json = GetJSONFromURL(url); if (String.IsNullOrEmpty(json)) { VieClientStatus = VIEWCLIENTSTATUS.CONNECTING; return null; } if (json.StartsWith("Error:")) { Say(json); return null; } ld = LocationDesignator.FromJSON(json); } catch (Exception ex) { Say(ex.Message); Log.WriteMessage(ex.ToString(), LogLevel.Error); } return ld; } private ElevationPathDesignator ElevationPathFromJSON() { string json = ""; try { json = GetJSONFromURL(GetServerURL( Properties.Settings.Default.Server_URL, (int)Properties.Settings.Default.Server_Port, "elevationpath.json", "mycall=" + Properties.Settings.Default.MyCall + "&myloc=" + MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, false, 3) + "&dxcall=" + Properties.Settings.Default.DXCall + "&dxloc=" + MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon, false, 3))); if (String.IsNullOrEmpty(json)) { VieClientStatus = VIEWCLIENTSTATUS.CONNECTING; return null; } if (json.StartsWith("Error:")) { Say(json); return null; } EPath = ElevationPathDesignator.FromJSON(json); } catch (Exception ex) { Say(ex.Message); Log.WriteMessage(ex.ToString(), LogLevel.Error); } return EPath; } private PropagationPathDesignator PropagationPathFromJSON() { string json = ""; try { json = GetJSONFromURL(GetServerURL( Properties.Settings.Default.Server_URL, (int)Properties.Settings.Default.Server_Port, "propagationpath.json", "mycall=" + Properties.Settings.Default.MyCall + "&myloc=" + MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, false, 3) + "&dxcall=" + Properties.Settings.Default.DXCall + "&dxloc=" + MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon, false, 3))); if (String.IsNullOrEmpty(json)) { VieClientStatus = VIEWCLIENTSTATUS.CONNECTING; return null; } if (json.StartsWith("Error:")) { Say(json); return null; } PPath = PropagationPathDesignator.FromJSON(json); } catch (Exception ex) { Say(ex.Message); Log.WriteMessage(ex.ToString(), LogLevel.Error); } return PPath; } private List NearestPlanesFromJSON() { string json = ""; List l = null; try { json = GetJSONFromURL(GetServerURL( Properties.Settings.Default.Server_URL, (int)Properties.Settings.Default.Server_Port, "nearestplanes.json", "mycall=" + Properties.Settings.Default.MyCall + "&myloc=" + MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, false, 3) + "&dxcall=" + Properties.Settings.Default.DXCall + "&dxloc=" + MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon, false, 3))); if (String.IsNullOrEmpty(json)) { VieClientStatus = VIEWCLIENTSTATUS.CONNECTING; return null; } if (json.StartsWith("Error:")) { Say(json); return null; } JsonSerializerSettings settings = new JsonSerializerSettings(); settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; settings.FloatFormatHandling = FloatFormatHandling.String; settings.Formatting = Newtonsoft.Json.Formatting.Indented; settings.Culture = CultureInfo.InvariantCulture; l = (List)JsonConvert.DeserializeObject>(json, settings); } catch (Exception ex) { Say(ex.Message); Log.WriteMessage(ex.ToString(), LogLevel.Error); } return l; } private static Bitmap RotateImageByAngle(System.Drawing.Image oldBitmap, float angle) { var newBitmap = new Bitmap(oldBitmap.Width, oldBitmap.Height); var graphics = Graphics.FromImage(newBitmap); graphics.TranslateTransform((float)oldBitmap.Width / 2, (float)oldBitmap.Height / 2); graphics.RotateTransform(angle); graphics.TranslateTransform(-(float)oldBitmap.Width / 2, -(float)oldBitmap.Height / 2); graphics.DrawImage(oldBitmap, new System.Drawing.Point(0, 0)); return newBitmap; } private GMarkerGoogle CreatePlaneSimple(PlaneInfo info, bool selected) { // return on empty info if (info == null) return null; // show flight info only // get bitmap according to category Bitmap bm; int bmindex = bmindex_gray; Brush brush = new SolidBrush(Color.FromArgb(180, Color.White)); if (info.Potential == 100) { bmindex = bmindex_magenta; brush = new SolidBrush(Color.FromArgb(150, Color.Plum)); } else if (info.Potential == 75) { bmindex = bmindex_red; brush = new SolidBrush(Color.FromArgb(150, Color.Red)); } else if (info.Potential == 50) { bmindex = bmindex_darkorange; brush = new SolidBrush(Color.FromArgb(150, Color.DarkOrange)); } if (info.Category == PLANECATEGORY.SUPERHEAVY) bm = new Bitmap(il_Planes_S.Images[bmindex]); else if (info.Category == PLANECATEGORY.HEAVY) bm = new Bitmap(il_Planes_H.Images[bmindex]); else if (info.Category == PLANECATEGORY.MEDIUM) bm = new Bitmap(il_Planes_M.Images[bmindex]); else if (info.Category == PLANECATEGORY.LIGHT) bm = new Bitmap(il_Planes_L.Images[bmindex]); else bm = new Bitmap(il_Planes_M.Images[bmindex]); GMarkerGoogle m = new GMarkerGoogle(new PointLatLng(info.Lat, info.Lon), ToolTipFont, RotateImageByAngle(bm, (float)info.Track)); m.Tag = info.Call; string lat = ""; if (info.Lat >= 0) lat = Math.Abs(info.Lat).ToString("00.00") + "°N"; else lat = Math.Abs(info.Lat).ToString("00.00") + "°S"; string lon = ""; if (info.Lon >= 0) lon = Math.Abs(info.Lon).ToString("000.00") + "°E"; else lon = Math.Abs(info.Lon).ToString("000.00") + "°W"; m.ToolTipText = info.Call + "\n--------------------"; if (Properties.Settings.Default.InfoWin_Position) m.ToolTipText += "\nPos: " + lat + " , " + lon; if (Properties.Settings.Default.InfoWin_Alt) { if (Properties.Settings.Default.InfoWin_Metric) m.ToolTipText += "\nAlt: " + (int)info.Alt_m + "m"; else m.ToolTipText += "\nAlt: " + (int)info.Alt + "ft"; } if (Properties.Settings.Default.InfoWin_Track) m.ToolTipText += "\nTrack: " + (int)info.Track + "°"; if (Properties.Settings.Default.InfoWin_Type) m.ToolTipText += "\nType: " + info.Manufacturer + " " + info.Model + " [" + PlaneCategories.GetShortStringValue(info.Category) + "]"; // set tooltip on if hot if (selected) m.ToolTipMode = MarkerTooltipMode.Always; else m.ToolTipMode = MarkerTooltipMode.OnMouseOver; if (m.ToolTip != null) m.ToolTip.Fill = brush; return m; } private GMarkerGoogle CreatePlaneDetailed(PlaneInfo info, bool selected) { // return on empty info if (info == null) return null; // get bitmap according to category Bitmap bm; int bmindex = bmindex_gray; Brush brush = new SolidBrush(Color.FromArgb(180, Color.White)); if (info.Potential == 100) { bmindex = bmindex_magenta; brush = new SolidBrush(Color.FromArgb(150, Color.Plum)); } else if (info.Potential == 75) { bmindex = bmindex_red; brush = new SolidBrush(Color.FromArgb(150, Color.Red)); } else if (info.Potential == 50) { bmindex = bmindex_darkorange; brush = new SolidBrush(Color.FromArgb(150, Color.DarkOrange)); } if (info.Category == PLANECATEGORY.SUPERHEAVY) bm = new Bitmap(il_Planes_S.Images[bmindex]); else if (info.Category == PLANECATEGORY.HEAVY) bm = new Bitmap(il_Planes_H.Images[bmindex]); else if (info.Category == PLANECATEGORY.MEDIUM) bm = new Bitmap(il_Planes_M.Images[bmindex]); else if (info.Category == PLANECATEGORY.LIGHT) bm = new Bitmap(il_Planes_L.Images[bmindex]); else bm = new Bitmap(il_Planes_M.Images[bmindex]); GMarkerGoogle m = new GMarkerGoogle(new PointLatLng(info.Lat, info.Lon), ToolTipFont, RotateImageByAngle(bm, (float)info.Track)); m.Tag = info.Call; string lat = ""; if (info.Lat >= 0) lat = Math.Abs(info.Lat).ToString("00.00") + "°N"; else lat = Math.Abs(info.Lat).ToString("00.00") + "°S"; string lon = ""; if (info.Lon >= 0) lon = Math.Abs(info.Lon).ToString("000.00") + "°E"; else lon = Math.Abs(info.Lon).ToString("000.00") + "°W"; int mins = 0; if (info.Speed > 0) mins = (int)(info.IntQRB / UnitConverter.kts_kmh(info.Speed) * 60.0); // fill tooltip texts m.ToolTipText = info.Call + "\n--------------------"; if (Properties.Settings.Default.InfoWin_Position) m.ToolTipText += "\nPos: " + lat + " , " + lon; if (Properties.Settings.Default.InfoWin_Alt) { if (Properties.Settings.Default.InfoWin_Metric) m.ToolTipText += "\nAlt: " + (int)info.Alt_m + "m [" + info.AltDiff.ToString("+#;-#;0") + "m]"; else m.ToolTipText += "\nAlt: " + (int)info.Alt + "ft [" + UnitConverter.m_ft(info.AltDiff).ToString("+#;-#;0") + "ft]"; } if (Properties.Settings.Default.InfoWin_Track) m.ToolTipText += "\nTrack: " + info.Track + "°"; if (Properties.Settings.Default.InfoWin_Speed) { if (Properties.Settings.Default.InfoWin_Metric) m.ToolTipText += "\nSpeed: " + info.Speed_kmh.ToString("F0") + "km/h"; else m.ToolTipText += "\nSpeed: " + info.Speed.ToString("F0") + "kts"; } if (Properties.Settings.Default.InfoWin_Type) m.ToolTipText += "\nType: " + info.Manufacturer + " " + info.Model + " [" + PlaneCategories.GetShortStringValue(info.Category) + "]"; if (Properties.Settings.Default.InfoWin_Dist) { if (Properties.Settings.Default.InfoWin_Metric) m.ToolTipText += "\nDist: " + info.IntQRB.ToString("F0") + "km"; else m.ToolTipText += "\nDist: " + UnitConverter.km_mi(info.IntQRB).ToString("F0") + "mi"; } if (Properties.Settings.Default.InfoWin_Time) m.ToolTipText += "\nTime: " + (CurrentTime + new TimeSpan(0, mins, 0)).ToString("HH:mm") + " [ " + mins.ToString("") + "min]"; if (Properties.Settings.Default.InfoWin_Angle) m.ToolTipText += "\nAngle: " + (info.Angle / Math.PI * 180.0).ToString("F0") + "°"; if (Properties.Settings.Default.InfoWin_Epsilon) m.ToolTipText += "\nEps: " + (info.Eps1 / Math.PI * 180.0).ToString("00.00") + "° <> " + (info.Eps2 / Math.PI * 180.0).ToString("00.00") + "°"; if (Properties.Settings.Default.InfoWin_Squint) m.ToolTipText += "\nSquint: " + (info.Squint / Math.PI * 180).ToString("00.00") + "°"; if (selected) { m.ToolTipMode = MarkerTooltipMode.Always; } else { m.ToolTipMode = MarkerTooltipMode.OnMouseOver; } if (m.ToolTip != null) m.ToolTip.Fill = brush; return m; } private void DrawPlanes() { bool isselected = false; if (EPath == null) return; if (PPath == null) return; // check for filter settings // and color filter box int planes_filter_minalt = 0; planes_filter_minalt = Properties.Settings.Default.Planes_Filter_Min_Alt; if (planes_filter_minalt < 0) planes_filter_minalt = 0; Stopwatch st = new Stopwatch(); st.Start(); // clear planes overlay in map gmo_Planes.Clear(); // clear all routes except paths gmo_Routes.Clear(); // clear active planes ActivePlanes.Clear(); // clear data points in chart Planes_Hi.Points.Clear(); Planes_Lo.Points.Clear(); // get nearest planes per path List nearestplanes = NearestPlanesFromJSON(); if (nearestplanes == null) return; foreach (PlaneInfo plane in nearestplanes) { // add or update plane in active planes list PlaneInfo activeplane = null; if (ActivePlanes.TryGetValue(plane.Hex, out activeplane)) { // plane found --> update if necessary bool update = false; // plane has higher potential if (plane.Potential > activeplane.Potential) update = true; // plane has same potetial but is nearer to path else if ((plane.Potential == activeplane.Potential) && (plane.IntQRB < activeplane.IntQRB)) update = true; // update if necessary if (update) { ActivePlanes.Remove(activeplane.Hex); ActivePlanes.Add(plane.Hex, plane); } } else { // add plane to list if not foound ActivePlanes.Add(plane.Hex, plane); } } st.Stop(); Say("Get nearest planes: " + ActivePlanes.Count.ToString() + " plane(s), " + st.ElapsedMilliseconds.ToString() + " ms."); st.Reset(); st.Start(); Log.WriteMessage("Drawing planes: " + ActivePlanes.Count.ToString() + " plane(s), " + st.ElapsedMilliseconds.ToString() + " ms."); // check if any plane is on list --> return empty list if ((ActivePlanes == null) || (ActivePlanes.Count == 0)) return; // draw all planes foreach (PlaneInfo plane in ActivePlanes.Values) { try { // show planes if it meets filter criteria if ((plane.Alt_m >= Properties.Settings.Default.Planes_MinAlt) && (plane.Category >= Properties.Settings.Default.Planes_Filter_Min_Category)) { // check selected state isselected = SelectedPlanes.IndexOf(plane.Call) >= 0; // now, show plane according to potential switch (plane.Potential) { case 100: gmo_Planes.Markers.Add(CreatePlaneDetailed(plane, isselected)); break; case 75: gmo_Planes.Markers.Add(CreatePlaneDetailed(plane, isselected)); break; case 50: gmo_Planes.Markers.Add(CreatePlaneDetailed(plane, isselected)); break; default: gmo_Planes.Markers.Add(CreatePlaneSimple(plane, isselected)); break; } // if selected: draw the thin path to crossing point if one if (isselected) { if (plane.IntPoint != null) { GMapRoute intpath = new GMapRoute(plane.Call); intpath.Stroke = new Pen(Color.Black, 1); intpath.Points.Add(new PointLatLng(plane.Lat, plane.Lon)); intpath.Points.Add(new PointLatLng(plane.IntPoint.Lat, plane.IntPoint.Lon)); gmo_Routes.Routes.Add(intpath); // Console.WriteLine(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + ";" + info.IntQRB.ToString("F3")); } } // show planes on chart if in sigle path mode if ((plane.IntPoint != null) && (plane.IntQRB <= Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].MaxDistance)) { // calculate distance from mylat/mylon double dist = LatLon.Distance(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, plane.IntPoint.Lat, plane.IntPoint.Lon); // add new data points if (plane.AltDiff > 0) { Planes_Hi.Points.Add(new DataPoint(dist, plane.Alt_m)); } else { Planes_Lo.Points.Add(new DataPoint(dist, plane.Alt_m)); } } } // invalidate chart pm_Path.InvalidatePlot(true); } catch (Exception ex) { Log.WriteMessage(ex.ToString(), LogLevel.Error); } } } private void DrawPath() { ElevationPathDesignator epath = ElevationPathFromJSON(); if (epath == null) return; // set MyDetails and DXDetails Properties.Settings.Default.MyCall = epath.Location1.Call; Properties.Settings.Default.MyLat = epath.Lat1; Properties.Settings.Default.MyLon = epath.Lon1; Properties.Settings.Default.DXCall = epath.Location2.Call; Properties.Settings.Default.DXLat = epath.Lat2; Properties.Settings.Default.DXLon = epath.Lon2; PropagationPathDesignator ppath = PropagationPathFromJSON(); if (ppath == null) return; // clear charts ClearCharts(); // draw my end on the map gmm_MyLoc = new GMarkerGoogle(new PointLatLng(epath.Location1.Lat, epath.Location1.Lon), ToolTipFont, Properties.Settings.Default.Map_SmallMarkers ? GMarkerGoogleType.red_small : GMarkerGoogleType.red_dot); gmm_MyLoc.ToolTipText = epath.Location1.Call + "\n" + epath.Location1.Lat.ToString("F8", CultureInfo.InvariantCulture) + "\n" + epath.Location1.Lon.ToString("F8", CultureInfo.InvariantCulture) + "\n" + epath.Location1.Loc + "\n" + epath.Location1.Elevation.ToString("F0") + "m"; gmm_MyLoc.ToolTipMode = MarkerTooltipMode.OnMouseOver; gmm_MyLoc.Tag = epath.Location1.Call; gmo_Objects.Markers.Add(gmm_MyLoc); // draws a propagation path to map PropagationPoint[] ppoints = new PropagationPoint[0]; try { // get infopoints for map ppoints = ppath.GetInfoPoints(); // calculate midpoint ScoutBase.Core.LatLon.GPoint midpoint = LatLon.MidPoint(ppath.Lat1, ppath.Lon1, ppath.Lat2, ppath.Lon2); GMapMarker gmmid = new GMarkerGoogle(new PointLatLng(midpoint.Lat, midpoint.Lon), ToolTipFont, Properties.Settings.Default.Map_SmallMarkers ? GMarkerGoogleType.blue_small : GMarkerGoogleType.blue_dot); gmmid.ToolTipText = ppath.Location1.Call + " <> " + ppath.Location2.Call; gmmid.ToolTipMode = MarkerTooltipMode.OnMouseOver; gmo_Objects.Markers.Add(gmmid); // calculate dx end gmm_DXLoc = new GMarkerGoogle(new PointLatLng(epath.Lat2, epath.Lon2), ToolTipFont, Properties.Settings.Default.Map_SmallMarkers ? GMarkerGoogleType.yellow_small : GMarkerGoogleType.yellow_dot); gmm_DXLoc.ToolTipText = epath.Location2.Call + "\n" + epath.Location2.Lat.ToString("F8", CultureInfo.InvariantCulture) + "\n" + epath.Location2.Lon.ToString("F8", CultureInfo.InvariantCulture) + "\n" + epath.Location2.Loc + "\n" + epath.Location2.Elevation.ToString("F0") + "m"; gmm_DXLoc.ToolTipMode = MarkerTooltipMode.OnMouseOver; gmm_DXLoc.Tag = epath.Location2.Call; gmo_Objects.Markers.Add(gmm_DXLoc); // set three small points for hot path, if one if (!Properties.Settings.Default.Map_SmallMarkers) { int i1 = -1; int i3 = -1; for (int i = 0; i < ppoints.Length; i++) { if (Math.Max(ppoints[i].H1, ppoints[i].H2) < Properties.Settings.Default.Planes_MaxAlt) { if (i1 == -1) i1 = i; else i3 = i; } } if ((i1 >= 0) && (i3 >= 0)) { GMapMarker gmi1 = new GMarkerGoogle(new PointLatLng(ppoints[i1].Lat, ppoints[i1].Lon), GMarkerGoogleType.red_small); gmo_Objects.Markers.Add(gmi1); LatLon.GPoint gp = LatLon.MidPoint(ppoints[i1].Lat, ppoints[i1].Lon, ppoints[i3].Lat, ppoints[i3].Lon); GMapMarker gmi2 = new GMarkerGoogle(new PointLatLng(gp.Lat, gp.Lon), GMarkerGoogleType.blue_small); gmo_Objects.Markers.Add(gmi2); GMapMarker gmi3 = new GMarkerGoogle(new PointLatLng(ppoints[i3].Lat, ppoints[i3].Lon), GMarkerGoogleType.yellow_small); gmo_Objects.Markers.Add(gmi3); } } // draw propagation path according to path status // valid: black // invalid: red gmr_FullPath = new GMapRoute("fullpath"); gmr_FullPath.Stroke = (ppath.Valid) ? new Pen(Color.Black, 3) : new Pen(Color.Red, 3); gmo_PropagationPaths.Routes.Add(gmr_FullPath); gmr_NearestFull = new GMapRoute("fullpath"); gmr_NearestFull.Stroke = (ppath.Valid) ? new Pen(Color.Black, 3) : new Pen(Color.Red, 3); gmo_NearestPaths.Routes.Add(gmr_NearestFull); foreach (PropagationPoint ppoint in ppoints) { gmr_FullPath.Points.Add(new PointLatLng(ppoint.Lat, ppoint.Lon)); gmr_NearestFull.Points.Add(new PointLatLng(ppoint.Lat, ppoint.Lon)); } // draw mutual visible path gmr_VisiblePpath = new GMapRoute("visiblepath"); gmr_VisiblePpath.Stroke = new Pen(Color.Magenta, 3); gmr_NearestVisible = new GMapRoute("visiblepath"); gmr_NearestVisible.Stroke = new Pen(Color.Magenta, 3); for (int i = 0; i < ppoints.Length; i++) { if ((Math.Max(ppoints[i].H1, ppoints[i].H2) > 0) && (Math.Max(ppoints[i].H1, ppoints[i].H2) < Properties.Settings.Default.Planes_MaxAlt)) { PointLatLng p = new PointLatLng(ppoints[i].Lat, ppoints[i].Lon); gmr_VisiblePpath.Points.Add(p); gmr_NearestVisible.Points.Add(p); } } gmo_PropagationPaths.Routes.Add(gmr_VisiblePpath); gmo_NearestPaths.Routes.Add(gmr_NearestVisible); // calculate bounds double minlat = Math.Min(epath.Lat1, epath.Lat2); double minlon = Math.Min(epath.Lon1, epath.Lon2); double maxlat = Math.Max(epath.Lat1, epath.Lat2); double maxlon = Math.Max(epath.Lon1, epath.Lon2); // calculate center double centerlat = LatLon.MidPoint(minlat, minlon, maxlat, maxlon).Lat; double centerlon = LatLon.MidPoint(minlat, minlon, maxlat, maxlon).Lon; // ensure that whole path is visible and optionally centered gm_Main.SetZoomToFitRect(RectLatLng.FromLTRB(minlon, maxlat, maxlon, minlat)); gm_Main.Position = new PointLatLng(centerlat, centerlon); UpdateCharts(epath, ppath); } catch (Exception ex) { Log.WriteMessage(ex.ToString(), LogLevel.Error); } } private void ti_Startup_Tick(object sender, EventArgs e) { ti_Startup.Stop(); // start timer ti_Progress.Start(); } private void ti_Progress_Tick(object sender, EventArgs e) { tsl_ConnectionStatus.Text = VieClientStatus.ToString(); switch (VieClientStatus) { case VIEWCLIENTSTATUS.CONNECTING: { tsl_ConnectionStatus.BackColor = Color.Yellow; tsl_ConnectionStatus.ForeColor = Color.Green; InitializeSettings(); break; } case VIEWCLIENTSTATUS.CONNECTED: { tsl_ConnectionStatus.BackColor = Color.Green; tsl_ConnectionStatus.ForeColor = Color.White; if (PlayMode != AIRSCOUTPLAYMODE.FORWARD) return; CurrentTime = DateTime.UtcNow; UpdateStatus(); DrawPlanes(); break; } case VIEWCLIENTSTATUS.ERROR: { tsl_ConnectionStatus.BackColor = Color.Red; tsl_ConnectionStatus.ForeColor = Color.White; break; } default: { tsl_ConnectionStatus.BackColor = SystemColors.Control; tsl_ConnectionStatus.ForeColor = SystemColors.ControlText; break; } } } private void MapViewDlg_Load(object sender, EventArgs e) { this.Text = "AirScout View Client V" + Application.ProductVersion; this.Show(); this.Restart(); } private void Restart() { ti_Progress.Stop(); ti_Startup.Stop(); VieClientStatus = VIEWCLIENTSTATUS.INIT; InitializeIcons(); InitializeCharts(); ti_Progress.Start(); VieClientStatus = VIEWCLIENTSTATUS.CONNECTING; } private void tsi_Exit_Click(object sender, EventArgs e) { this.Close(); } private void MapViewDlg_FormClosing(object sender, FormClosingEventArgs e) { ti_Progress.Stop(); Properties.Settings.Default.Save(); } private double GetMinH(double max_alt, double H1, double H2) { double max = Math.Max(H1, H2); if (max <= max_alt) return max; return max_alt; } private void UpdateCharts(ElevationPathDesignator epath, PropagationPathDesignator ppath) { // updates the diagram area short[] epoints = new short[0]; PropagationPoint[] ppoints = new PropagationPoint[0]; try { ClearCharts(); // adjust diagram axes Path_X.Maximum = ppath.Distance; Elevation_X.Maximum = epath.Distance; // get infopoints for charting epoints = epath.GetInfoPoints(); ppoints = ppath.GetInfoPoints(); // calculate epsilon for LOS double eps_los = Propagation.EpsilonFromHeights(epath.Location1.Elevation + ppath.QRV1.AntennaHeight, ppath.Distance, epath.Location2.Elevation + ppath.QRV2.AntennaHeight, LatLon.Earth.Radius); // fill chart short maxelv = short.MinValue; double myelev = epath.Location1.Elevation; for (int i = 0; i < epoints.Length; i++) { Path_Elevation.Points.Add(new OxyPlot.DataPoint(i, epoints[i])); Min_H1.Points.Add(new OxyPlot.DataPoint(i, ppoints[i].H1)); Min_H2.Points.Add(new OxyPlot.DataPoint(i, ppoints[i].H2)); Max_H.Points.Add(new OxyPlot.DataPoint(i, Properties.Settings.Default.Planes_MaxAlt)); Min_H.Points.Add(new OxyPlot.DataPoint(i, Properties.Settings.Default.Planes_MaxAlt)); Min_H.Points2.Add(new OxyPlot.DataPoint(i, GetMinH(Properties.Settings.Default.Planes_MaxAlt, Min_H1.Points[i].Y, Min_H2.Points[i].Y))); LOS.Points.Add(new OxyPlot.DataPoint(i, Propagation.HeightFromEpsilon(myelev + ppath.QRV1.AntennaHeight, i, eps_los, LatLon.Earth.Radius))); Elevation.Points.Add(new OxyPlot.DataPoint(i, epoints[i])); if (maxelv < epoints[i]) maxelv = epoints[i]; } // adjust Y-axis --> max elv + 10% Elevation_Y.Maximum = maxelv + maxelv * 0.1; // invalidate plots pm_Path.InvalidatePlot(true); pm_Elevation.InvalidatePlot(true); // show path legends Charts_ShowLegends(5000); } catch (Exception ex) { Log.WriteMessage(ex.ToString(), LogLevel.Error); } } private void ClearCharts() { try { // clear all points Path_Elevation.Points.Clear(); Min_H1.Points.Clear(); Min_H2.Points.Clear(); Max_H.Points.Clear(); Min_H.Points.Clear(); Min_H.Points2.Clear(); Planes_Hi.Points.Clear(); Planes_Lo.Points.Clear(); Elevation.Points.Clear(); LOS.Points.Clear(); // update view pm_Path.InvalidatePlot(true); pm_Elevation.InvalidatePlot(true); } catch (Exception ex) { Log.WriteMessage(ex.ToString(), LogLevel.Error); } } private void Charts_ShowLegends(int ms) { // enable path legends for some seconds pm_Path.IsLegendVisible = true; pm_Path.InvalidatePlot(true); pm_Elevation.IsLegendVisible = true; pm_Elevation.InvalidatePlot(true); ti_ShowLegends.Interval = ms; ti_ShowLegends.Start(); } private void ti_ShowLegends_Tick(object sender, EventArgs e) { pm_Path.IsLegendVisible = false; pm_Path.InvalidatePlot(true); pm_Elevation.IsLegendVisible = false; pm_Elevation.InvalidatePlot(true); ti_ShowLegends.Enabled = false; } private void pv_Path_Paint(object sender, PaintEventArgs e) { // draw calsign s on chart string mycall = Properties.Settings.Default.MyCall; string dxcall = Properties.Settings.Default.DXCall; int top = pv_Path.Bottom - pv_Path.Height / 2; int left = pv_Path.Left + 50; int right = pv_Path.Width - 38; Font font = new Font(FontFamily.GenericSansSerif, 8, FontStyle.Bold); Graphics g = e.Graphics; using (StringFormat format = new StringFormat(StringFormatFlags.DirectionVertical)) { // measure text to emulate TextAlign.Middle int mywidth = (int)g.MeasureString(mycall, font).Width; int dxwidth = (int)g.MeasureString(dxcall, font).Width; using (SolidBrush brush = new SolidBrush(Color.Black)) { if (pv_Path.Height - 50 > mywidth) g.DrawString(mycall, font, brush, left, top - mywidth / 2, format); if (pv_Path.Height - 50 > dxwidth) g.DrawString(dxcall, font, brush, right, top - dxwidth / 2, format); /* if (pv_Path.Height > 2 * mywidth + 50) g.DrawString(mycall, font, brush, left, top, format); if (pv_Path.Height > 2 * dxwdith + 50) g.DrawString(dxcall, font, brush, right, top, format); */ } } } private void gb_Path_Resize(object sender, EventArgs e) { // adjust charts pv_Path.Location = new System.Drawing.Point(0, 0); pv_Path.Width = gb_Path.Width; pv_Path.Height = gb_Path.Height / 2; pv_Elevation.Location = new System.Drawing.Point(0, gb_Path.Height / 2); pv_Elevation.Width = gb_Path.Width; pv_Elevation.Height = gb_Path.Height / 2; } private void gm_Main_OnMapZoomChanged() { double midlat = LatLon.MidPoint(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon).Lat; double midlon = LatLon.MidPoint(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon).Lon; if ((PlayMode == AIRSCOUTPLAYMODE.FORWARD) && Properties.Settings.Default.Map_AutoCenter) gm_Main.Position = new PointLatLng(midlat, midlon); tb_Map_Zoom.Text = gm_Main.Zoom.ToString(); } private void gm_Main_OnMarkerClick(GMapMarker item, MouseEventArgs e) { try { if (e.Button == System.Windows.Forms.MouseButtons.Left) { // check if plane clicked if (item.Overlay == gmo_Planes) { // keep the selected state int selectedindex = SelectedPlanes.IndexOf((string)item.Tag); // clear all other selections if tracking is en // toogle selection of the selected plane if (selectedindex >= 0) { // remove item from selected planes list SelectedPlanes.RemoveAt(selectedindex); } else { SelectedPlanes.Add((string)item.Tag); } } } } catch (Exception ex) { // do nothing if item not found in planes list Log.WriteMessage(ex.ToString(), LogLevel.Error); } } private void btn_Map_Zoom_In_Click(object sender, EventArgs e) { if (gm_Main.Zoom < 20) gm_Main.Zoom++; } private void btn_Map_Zoom_Out_Click(object sender, EventArgs e) { if (gm_Main.Zoom > 0) gm_Main.Zoom--; } private void tb_MyCall_TextChanged(object sender, EventArgs e) { if (!Callsign.Check(tb_MyCall.Text)) return; Properties.Settings.Default.MyCall = tb_MyCall.Text; // get callsign info List l = MyLocationsFromJSON(); if (l == null) { cb_MyLoc.Items.Clear(); cb_MyLoc.SilentText = ""; return; } cb_MyLoc.Items.Clear(); cb_MyLoc.DisplayMember = "Loc"; foreach (LocationDesignator ld in l) { cb_MyLoc.Items.Add(ld); } if (cb_MyLoc.Items.Count > 0) { cb_MyLoc.SelectedItem = cb_MyLoc.Items[0]; Properties.Settings.Default.MyLat = ((LocationDesignator)cb_MyLoc.Items[0]).Lat; Properties.Settings.Default.MyLon = ((LocationDesignator)cb_MyLoc.Items[0]).Lon; } } private void tb_DXCall_TextChanged(object sender, EventArgs e) { if (!Callsign.Check(tb_DXCall.Text)) return; Properties.Settings.Default.DXCall = tb_DXCall.Text; // get callsign info List l = DXLocationsFromJSON(); if (l == null) { cb_DXLoc.Items.Clear(); cb_DXLoc.SilentText = ""; return; } cb_DXLoc.Items.Clear(); cb_DXLoc.DisplayMember = "Loc"; foreach (LocationDesignator ld in l) { cb_DXLoc.Items.Add(ld); } if (cb_DXLoc.Items.Count > 0) { cb_DXLoc.SelectedItem = cb_DXLoc.Items[0]; Properties.Settings.Default.DXLat = ((LocationDesignator)cb_DXLoc.Items[0]).Lat; Properties.Settings.Default.DXLon = ((LocationDesignator)cb_DXLoc.Items[0]).Lon; } } private void cb_MyLoc_TextChanged(object sender, EventArgs e) { } private void cb_DXLoc_TextChanged(object sender, EventArgs e) { } private void tsi_Info_Click(object sender, EventArgs e) { MessageBox.Show("AirScout View Client V" + Application.ProductVersion.ToString(), "Info", MessageBoxButtons.OK); } private void btn_Map_PlayPause_Click(object sender, EventArgs e) { if (PlayMode != AIRSCOUTPLAYMODE.FORWARD) { PlayMode = AIRSCOUTPLAYMODE.FORWARD; btn_Map_PlayPause.ImageIndex = 0; // draw path DrawPath(); } else { PlayMode = AIRSCOUTPLAYMODE.PAUSE; btn_Map_PlayPause.ImageIndex = 1; } } private void tsi_Settings_Click(object sender, EventArgs e) { SettingsDlg Dlg = new SettingsDlg(); if (Dlg.ShowDialog() == DialogResult.OK) { Properties.Settings.Default.Save(); this.Restart(); } } } }