//-------------------------------------------------------------------- // // Copyright 2013 Exelis Inc. // Government Unlimited Rights (DFARs 252.227-7014) // //-------------------------------------------------------------------- namespace STTI.FAC.MapControl2d { using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Drawing.Drawing2D; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Reflection; using System.Text; using System.Threading; using System.Windows.Forms; using ThinkGeo.MapSuite.Core; using ThinkGeo.MapSuite.DesktopEdition; using EGL = STTI.FAC.EllipticalGeometry; using STTI.FAC.CommonCodeLibrary; using STTI.FAC.CommonCodeLibrary.IMsgLogger; using STTI.FAC.CommonCodeLibrary.Forms; using STTI.FAC.CommonCodeLibrary.Map2dCommon.MmMc; using STTI.FAC.CommonCodeLibrary.Map2dCommon.MmWp; using STTI.FAC.MapControl2d.CpExplorer; /// /// This class is used to create contour isolines for a set of data points. /// The isoline level count and cell column and row count are currently set as constants /// but could be passed as parameters or prompted by user. /// The input data is currently read from a CSV (comma separated data file as a Lat,Lng Point /// with a corresponding data value for the contour. /// class IsoLines { #region Logger /// /// Logger used to instrument this class. /// static readonly log4net.ILog logger = log4net.LogManager.GetLogger(typeof(IsoLines).FullName); #endregion Logger #region Fields private delegate void LayerCreationDelegate(); private delegate void LayerUpdateHandler(); private event LayerUpdateHandler LayerUpdateEvent; public const double NoDataValue = -99999999; public const string GridCellsLayerName = "Grid-"; public const string LabelsLayerName = "Label-"; public const string PlotNamesLayerName = "Name-"; public const string PlotLegendLayerName = "Legend-"; private const int GridIsoLineCellColumnCount = 25; private const int GridIsoLineCellRowCount = 25; private const int GridCellOptimizationFaster = 400; /// /// Minimum number of data values required to plot contour lines. /// private const int MinimumValuesRequiredToPlot = 5; /// /// Grid Cell Limit for sync versus asynch thread to generate isolines. /// private const int MaxNoOfCellsForSynchCalls = 500; public const string GridFile = @"tempResult"; public const string GridFileSuffix = @".grid"; private string gridFilePath = @"..\..\Data\tempResult.grid"; private Dictionary contoursPointData; private GridCell[,] gridCellMatrix; private RectangleShape drawingExtent = null; /// /// Transparency of Legend Background. /// public const float LegendTransparency = 200.0F; /// /// Max Characters in Legend Title for Word Wrap. /// public const int MaxCharLegendTitle = 35; /// /// Lock around target. /// private readonly object stateLock = new object(); #endregion #region Properties /// /// Associates a map with this tool strip. /// public STTI.FAC.MapControl2d.MapControl2DMapSuite AssociatedMap { get; set; } /// /// Gets/Sets the Layer Name for the IsoLines. /// private string LayerNameIsoLine { get; set; } /// /// Gets/Sets the Layer Name for the Grid Cells. /// private string LayerNameGridCells { get; set; } /// /// Gets/Sets the Layer Name for the Grid Labels. /// private string LayerNameIsoLineLabels { get; set; } /// /// Gets/Sets the Layer Name for the Grid Plot Names. /// private string LayerNameIsoLinePlotNames { get; set; } /// /// Gets/Sets the Layer Name for the Plot Legend. /// private string LayerNamePlotLegend { get; set; } /// /// Gets/Sets the Layer Plot Legend Adornment Layer. /// private LegendAdornmentLayer IsoLinesLegendLayer { get; set; } /// /// Gets/Sets the Layer Grid IsoLines. /// private Layer IsoLinesLayer { get; set; } /// /// Gets/Sets the Layer for the Grid Labels. /// private IsoLineLayer IsoLineLabelsLayer { get; set; } /// /// Gets/Sets the Layer for the Grid Plot Names. /// private FeatureLayer IsoLinePlotNamesLayer { get; set; } /// /// Gets/Sets the Layer for the Grid Feature. /// private FeatureLayer IsoLineFeatureLayer { get; set; } /// /// Gets/Sets the Line Level Count for the Grid. /// private int GridLineLevelCount { get; set; } /// /// Gets/Sets the Cell Row Count for the Grid. /// private int GridCellRowCount { get; set; } /// /// Gets/Sets the Cell Column Count for the Grid. /// private int GridCellColumnCount { get; set; } /// /// Gets/Sets the Colors for the isoLines. /// private SortedDictionary LineLevelsAndColors { get; set; } /// /// Gets/Sets the Colors for the AreaStyle Fill. /// private SortedDictionary FillLevelsAndColors { get; set; } /// /// Gets/Sets LineWidth for Isoline Pen. /// private int LineWidth { get; set; } /// /// Gets/Sets Line DashStyle for Isoline Pen. /// private LineDashStyle PenLineDashStyle { get; set; } /// /// Gets/Sets the McPlot for the Grid. /// public McPlot Plot { get; set; } /// /// Gets/Sets the Min Variable Value for the DataTable. /// private double MinVariableValue { get; set; } /// /// Gets/Sets the Max Variable Value for the DataTable. /// private double MaxVariableValue { get; set; } /// /// Gets/Sets the Max Latitude for the Coverage Plot. /// private double MaxLatitude { get; set; } /// /// Gets/Sets the Max Longitude for the Coverage Plot. /// private double MaxLongitude { get; set; } /// /// Gets/Sets the Min Latitude for the Coverage Plot. /// private double MinLatitude { get; set; } /// /// Gets/Sets the Min Longitude for the Coverage Plot. /// private double MinLongitude { get; set; } /// /// Gets/Sets the CellWidth for the Coverage Plot. /// private double CellWidth { get; set; } /// /// Gets/Sets the CellWidth Rounding for the Coverage Plot. /// private double RoundCellWidth { get; set; } /// /// Gets/Sets a boolean for more than one data values in plot data. /// private bool IsThereMoreThanOneValue { get; set; } /// /// Gets/Sets a boolean for number of unique data values in plot data. /// private bool IsThereEnoughUniqueValues { get; set; } /// /// Gets/Sets a boolean to PlotResolutionOptForSpeed. /// private bool IsPlotResolutionOptForSpeed { get; set; } /// /// Gets/Sets a boolean to prompt for plot resolution optimization for large grids. /// private bool IsPlotResolutionOptShowDialogPrompt { get; set; } /// /// Gets/Sets value to optimize the Grid size for Map Display speed. /// private int PlotResolutionGridSize { get; set; } #endregion Properties #region Constructors /// /// Constructor with AssociatedMap for the IsoLines Contours. /// /// The associated Map control. /// The plot shape for the Isolines. public IsoLines(MapControl2DMapSuite associatedMap, ref McPlot plot) { try { AssociatedMap = associatedMap; Plot = plot; if (String.IsNullOrEmpty(AssociatedMap.isoLinesOpenSaveInitialFolder)) { //AssociatedMap.isoLinesOpenSaveInitialFolder = // GlobalInteract.Instance.GetWorkspacePath(Environment.UserName); AssociatedMap.isoLinesOpenSaveInitialFolder = GlobalInteract.Instance.GetParameterValue("Temporary directory path.", "TempDirectoryPath"); } gridFilePath = AssociatedMap.isoLinesOpenSaveInitialFolder + "\\" + GridFile + Plot.ObjectId.ToString() + GridFileSuffix; MinLatitude = MinLongitude = double.MaxValue; MaxLatitude = MaxLongitude = double.MinValue; MinVariableValue = double.MaxValue; MaxVariableValue = double.MinValue; double uniqueValueCheck = NoDataValue; IsThereMoreThanOneValue = false; IsThereEnoughUniqueValues = false; double[] valuesRequiredToPlot = new double[MinimumValuesRequiredToPlot]; for (int index = 0; index < MinimumValuesRequiredToPlot; index++) { valuesRequiredToPlot[index] = NoDataValue; } //Load the PlotData into a Dictionary. if (null != Plot && null != Plot.PlotData) { contoursPointData = new Dictionary(); Plot.PlotData.Rewind(); LatLngVal latLngVal = Plot.PlotData.GetLatLngVal(); while (null != latLngVal) { PointShape pointShape = new PointShape(latLngVal.Longitude, latLngVal.Latitude); if (latLngVal.Longitude < MinLongitude) { MinLongitude = latLngVal.Longitude; } if (latLngVal.Longitude > MaxLongitude) { MaxLongitude = latLngVal.Longitude; } if (latLngVal.Latitude < MinLatitude) { MinLatitude = latLngVal.Latitude; } if (latLngVal.Latitude > MaxLatitude) { MaxLatitude = latLngVal.Latitude; } if (latLngVal.Value.HasValue) { if (double.IsNaN(latLngVal.Value.Value) || double.IsNegativeInfinity(latLngVal.Value.Value) || double.IsPositiveInfinity(latLngVal.Value.Value)) { //TODO determine how to handle the "NaN", "NegativeInfinity", //and "PositiveInfinity" input values for now we skip them } else { //bypass all the "NoData" (null) Plot value in the List. contoursPointData.Add(pointShape, latLngVal.Value.Value); if (latLngVal.Value.Value != NoDataValue) { if (latLngVal.Value.Value < MinVariableValue) { MinVariableValue = latLngVal.Value.Value; } if (latLngVal.Value.Value > MaxVariableValue) { MaxVariableValue = latLngVal.Value.Value; } } if (!IsThereMoreThanOneValue) { if (uniqueValueCheck == NoDataValue) { uniqueValueCheck = latLngVal.Value.Value; } else if (uniqueValueCheck != latLngVal.Value.Value) { IsThereMoreThanOneValue = true; } } if (!IsThereEnoughUniqueValues) { IsThereEnoughUniqueValues = true; for (int index = 0; index < MinimumValuesRequiredToPlot; index++ ) { if (valuesRequiredToPlot[index] == latLngVal.Value.Value) { IsThereEnoughUniqueValues = false; break; } if (valuesRequiredToPlot[index] == NoDataValue) { valuesRequiredToPlot[index] = latLngVal.Value.Value; IsThereEnoughUniqueValues = false; break; } } } } } //else //{ // //FIll the "NoData" (null) Plot values with Designated NoDataValue in the List. // contoursPointData.Add(pointShape, NoDataValue); //} latLngVal = Plot.PlotData.GetLatLngVal(); } } double latitudeHeight = System.Math.Abs(MaxLatitude - MinLatitude); double longitudeWidth = System.Math.Abs(MaxLongitude - MinLongitude); CpPlotDataList plotDataList = Plot.PlotData as CpPlotDataList; if (null != plotDataList) { GridCellRowCount = (plotDataList.LatitudeCellCount > 0) ? plotDataList.LatitudeCellCount : GridIsoLineCellRowCount; GridCellColumnCount = (plotDataList.LongitudeCellCount > 0) ? plotDataList.LongitudeCellCount : GridIsoLineCellColumnCount; MinVariableValue = Plot.PlotData.DataTable.MinVariableValue; MaxVariableValue = Plot.PlotData.DataTable.MaxVariableValue; //The CellWidth from the Plot needs units conversion - since it may not be in decimal degrees CellWidth = (Plot.PlotData.CellWidth > 0.0d) ? Plot.PlotData.CellWidth : Math.Max(longitudeWidth / GridCellColumnCount, latitudeHeight / GridCellRowCount); RoundCellWidth = CellWidth / 10000.0d; //Round the index up by one-tenthousand cellWidth. //CpPlotDataList - use the Min and Max value from Plot. MinLatitude = plotDataList.MinLatitude - (CellWidth / 2.0d); //MinCenterLatitude - half cellwidth. MinLongitude = plotDataList.MinLongitude - (CellWidth / 2.0d); //MinCenterLongitude - half cellwidth. MaxLatitude = plotDataList.MaxLatitude + (CellWidth / 2.0d); //MinCenterLatitude + half cellwidth. MaxLongitude = plotDataList.MaxLongitude + (CellWidth / 2.0d); //MinCenterLongitude + half cellwidth. latitudeHeight = System.Math.Abs(MaxLatitude - MinLatitude); longitudeWidth = System.Math.Abs(MaxLongitude - MinLongitude); if (CellWidth > 0.0d && GridCellRowCount > 0 && GridCellColumnCount > 0) { latitudeHeight = CellWidth * GridCellRowCount; longitudeWidth = CellWidth * GridCellColumnCount; } } else { //CpPlotDataDict - use the Min and Max values from from loading the Dict Values above. //Use CellWidth from the loaded PlotData. CellWidth = Plot.PlotData.CellWidth; RoundCellWidth = CellWidth / 10000.0d; //Round the index up(Positive) by one-tenthousand cellWidth. MinLatitude = MinLatitude - (CellWidth / 2.0d); //MinCenterLatitude - half cellwidth. MinLongitude = MinLongitude - (CellWidth / 2.0d); //MinCenterLongitude - half cellwidth. MaxLatitude = MaxLatitude + (CellWidth / 2.0d); //MinCenterLatitude + half cellwidth. MaxLongitude = MaxLongitude + (CellWidth / 2.0d); //MinCenterLongitude + half cellwidth. latitudeHeight = System.Math.Abs(MaxLatitude - MinLatitude); longitudeWidth = System.Math.Abs(MaxLongitude - MinLongitude); GridCellRowCount = (int)((latitudeHeight + RoundCellWidth) / CellWidth); //Round up by CellWidth/10000. GridCellColumnCount = (int)((longitudeWidth + RoundCellWidth) / CellWidth); //Round up by CellWidth/10000. } //Get User Preferences settings for PlotResolutionOptimazation. GetUserPreferencesForPlotResoutionOptimization(); //Get the current drawing extent since we use that to generate the grid. drawingExtent = ExtentHelper.GetDrawingExtent( new RectangleShape( MinLongitude, MaxLatitude, MaxLongitude, MinLatitude), (float)longitudeWidth, (float)latitudeHeight); //Load Collections of Colors for the LineStyle and AreaStyle. //Sets the LineWidth and PenLineDashStyle //Calculate the isoLineLevels. LoadColorCollections(); if (LineLevelsAndColors.Count <= 0) { string errorMsg = String.Format("There are no isoLine levels defined. The Coverage Plot will not have any lines. " + "No plot can be generated for {0}.", plot.Title); this.AssociatedMap.LogMessage(MessageLevel.Info, string.Empty, errorMsg); logger.ErrorFormat(errorMsg); return; } if (GridCellRowCount < 2 || GridCellColumnCount < 2) { string errorMsg = String.Format("The plot data contains only a single latitude or a single longitude value. " + "At least two latitudes and two longitudes are required to draw isolines for a coverage plot. " + "No plot can be generated for {0}.", plot.Title); this.AssociatedMap.LogMessage(MessageLevel.Info, string.Empty, errorMsg); logger.ErrorFormat(errorMsg); return; } if (!IsThereMoreThanOneValue) { string errorMsg = String.Format("The plot data contains only a single value. At least two values are required "+ "to draw isolines for a coverage plot. " + "No plot can be generated for {0}.", plot.Title); this.AssociatedMap.LogMessage(MessageLevel.Info, string.Empty, errorMsg); logger.ErrorFormat(errorMsg); //return; } //if (!IsThereEnoughUniqueValues) //{ // string errorMsg = String.Format("The plot data contains less than {1} unique values. " + // "At least {1} unique values are required to draw isolines for a coverage plot. " + // "No plot can be generated for {0}.", plot.Title, MinimumValuesRequiredToPlot); // this.AssociatedMap.LogMessage(MessageLevel.Info, string.Empty, errorMsg); // logger.ErrorFormat(errorMsg); // return; //} if (null != AssociatedMap.IsoLineOverlay) { //Create the names for the layers and generate the layers. LayerNameIsoLine = plot.Title; LayerNameGridCells = GridCellsLayerName + plot.Title; LayerNameIsoLineLabels = LabelsLayerName + plot.Title; LayerNameIsoLinePlotNames = PlotNamesLayerName + plot.Title; LayerNamePlotLegend = PlotLegendLayerName + plot.Title; //Remove layers from previous construct if they exist. RemovePlotLayersFromOverlay(plot.Title); AssociatedMap.RaiseEventStatusBar( new MapControl2DMap.StatusBarUpdateEventArgs { StatusText = "Generating isolines...", Cancel = false }); if (longitudeWidth / CellWidth < MaxNoOfCellsForSynchCalls && latitudeHeight / CellWidth < MaxNoOfCellsForSynchCalls) { //Should be fast enough for synchronous calls to generate isolines. //Do the actual work of creating the IsoLines layers. CreateIsoLineLayers(); AddIsoLineLayersToOverlay(); } else { //Generate Isolines in asynchronous secondary thread. this.LayerUpdateEvent += new LayerUpdateHandler(AddIsoLineLayersToOverlay); ////Do the actual work of creating the IsoLines layers. LayerCreationDelegate layerCreation = new LayerCreationDelegate(CreateIsoLineLayers); IAsyncResult itfAR = layerCreation.BeginInvoke( new AsyncCallback(CreateIsoLineLayersCompleteAsyncCallback), null); Application.DoEvents(); } } } catch (Exception ex) { string errorMsg = String.Format("Unable to create isolines. Exception: {0}", ex); logger.ErrorFormat(errorMsg); this.AssociatedMap.LogMessage(MessageLevel.Info, string.Empty, errorMsg); } } #endregion #region Methods /// /// Get the User Preferences for Plot Resolution Optimization. /// private void GetUserPreferencesForPlotResoutionOptimization() { //Get User Preference for Plot Resolution Optimization Grid Size. PlotResolutionGridSize = GridCellOptimizationFaster; int plotGridSize; string plotResolutionGridSize = UserParameters.Instance.GetValue (UserParameters.ParamNames.PlotResolutionOptimizationGridKnots); if (!string.IsNullOrEmpty(plotResolutionGridSize)) { if (int.TryParse(plotResolutionGridSize, out plotGridSize)) { PlotResolutionGridSize = Convert.ToInt32(Math.Sqrt(plotGridSize)); } } IsPlotResolutionOptForSpeed = true; // Read the User Parameter settings for plot resolution speed optimization. if (UserParameters.Instance.GetValue (UserParameters.ParamNames.PlotResolutionOptimizationForSpeed) == UserParameters.PlotResolutionOptForSpeed.Details) { IsPlotResolutionOptForSpeed = false; } IsPlotResolutionOptShowDialogPrompt = true; // Read the User Parameter settings for plot resolution speed optimization. if (UserParameters.Instance.GetValue (UserParameters.ParamNames.PlotResolutionOptimizationShowDialogPrompt) == UserParameters.PlotResolutionOptShowDialogPrompt.False) { IsPlotResolutionOptShowDialogPrompt = false; } else { if (GridCellRowCount > PlotResolutionGridSize || GridCellColumnCount > PlotResolutionGridSize) { PlotResolutionPromptDialog dialog = new PlotResolutionPromptDialog(IsPlotResolutionOptForSpeed, !IsPlotResolutionOptShowDialogPrompt); if (dialog.ShowDialog() == DialogResult.Cancel) { return; }; if (IsPlotResolutionOptForSpeed != dialog.radioButtonMapSpeed.Checked) { if (dialog.radioButtonMapSpeed.Checked) { UserParameters.Instance.SaveValue (UserParameters.ParamNames.PlotResolutionOptimizationForSpeed, UserParameters.PlotResolutionOptForSpeed.Speed); } else { UserParameters.Instance.SaveValue (UserParameters.ParamNames.PlotResolutionOptimizationForSpeed, UserParameters.PlotResolutionOptForSpeed.Details); } IsPlotResolutionOptForSpeed = dialog.radioButtonMapSpeed.Checked; } if (IsPlotResolutionOptShowDialogPrompt == dialog.checkBoxShowDialog.Checked) { if (dialog.checkBoxShowDialog.Checked) { UserParameters.Instance.SaveValue (UserParameters.ParamNames.PlotResolutionOptimizationShowDialogPrompt, UserParameters.PlotResolutionOptShowDialogPrompt.False); } else { UserParameters.Instance.SaveValue (UserParameters.ParamNames.PlotResolutionOptimizationShowDialogPrompt, UserParameters.PlotResolutionOptShowDialogPrompt.True); } } } } } /// /// Remove the layers for a plot from the overlay. /// /// Title of the plot. public void RemovePlotLayersFromOverlay(string plotTitle) { LayerOverlay isoLineOverlay = AssociatedMap.IsoLineOverlay; if (isoLineOverlay != null) { LayerNameIsoLine = plotTitle; LayerNameGridCells = GridCellsLayerName + plotTitle; LayerNameIsoLineLabels = LabelsLayerName + plotTitle; LayerNameIsoLinePlotNames = PlotNamesLayerName + plotTitle; LayerNamePlotLegend = PlotLegendLayerName + plotTitle; if (isoLineOverlay.Layers.Count > 0) { if (isoLineOverlay.Layers.Contains(LayerNameIsoLine)) { isoLineOverlay.Layers[LayerNameIsoLine].Close(); isoLineOverlay.Layers.Remove(LayerNameIsoLine); } if (isoLineOverlay.Layers.Contains(LayerNameIsoLineLabels)) { isoLineOverlay.Layers[LayerNameIsoLineLabels].Close(); isoLineOverlay.Layers.Remove(LayerNameIsoLineLabels); } if (isoLineOverlay.Layers.Contains(LayerNameIsoLinePlotNames)) { isoLineOverlay.Layers[LayerNameIsoLinePlotNames].Close(); isoLineOverlay.Layers.Remove(LayerNameIsoLinePlotNames); } if (isoLineOverlay.Layers.Contains(LayerNameGridCells)) { isoLineOverlay.Layers[LayerNameGridCells].Close(); isoLineOverlay.Layers.Remove(LayerNameGridCells); } if (AssociatedMap.WinformsMap1.AdornmentOverlay.Layers.Contains(LayerNamePlotLegend)) { AssociatedMap.WinformsMap1.AdornmentOverlay.Layers[LayerNamePlotLegend].Close(); AssociatedMap.WinformsMap1.AdornmentOverlay.Layers.Remove(LayerNamePlotLegend); } } } } /// /// Create the IsoLine Contours Layers. /// private void CreateIsoLineLayers() { try { MapControl2DMapSuite.IsoLineSemaphore.WaitOne(); lock (stateLock) { logger.InfoFormat("Starting CreateGridFile 2D Plot Data File. {0} ", Plot.ObjectId); //AssociatedMap.ProgressValue += 5; //Application.DoEvents(); //Create a grid file based the current extent. //if (Plot.IsVisibleBoundary) //{ // CreateGridFile(); //} //else //{ StreamDictionaryToGridFile(); //Free up the memory from the dictionary after the GridFile is written. contoursPointData = null; //} //AssociatedMap.ProgressValue += 5; //Application.DoEvents(); if (IsThereEnoughUniqueValues) { logger.InfoFormat("Creating GridIsoLineLayer 2D Plot Data File. {0} ", Plot.ObjectId); IsoLinesLayer = GetGridIsoLineLayer(LayerNameIsoLine); //AssociatedMap.ProgressValue += 5; //Application.DoEvents(); logger.InfoFormat("Creating GridIsoLineLabelsLayer 2D Plot Data File. {0} ", Plot.ObjectId); IsoLineLabelsLayer = GetGridIsoLineLabelsLayer(LayerNameIsoLineLabels); } else { IsoLinesLayer = null; IsoLineLabelsLayer = null; } //AssociatedMap.ProgressValue += 5; //Application.DoEvents(); logger.InfoFormat("Creating GridIsoLinePlotNamesLayer 2D Plot Data File. {0} ", Plot.ObjectId); IsoLinePlotNamesLayer = GetGridIsoLinePlotNamesLayer(LayerNameIsoLinePlotNames); logger.InfoFormat("Creating GridFeatureLayer 2D Plot Data File. {0} ", Plot.ObjectId); IsoLineFeatureLayer = GetGridFeatureLayer(LayerNameGridCells); logger.InfoFormat("Creating 2D Plot Data File completed. {0} ", Plot.ObjectId); //AssociatedMap.ProgressValue += 5; //Application.DoEvents(); //Create a legend for this plot. IsoLinesLegendLayer = CreateLegendLayer(); } //Now Invode the method to add these layer to the IsoLineOverlay. //if (null != LayerUpdateEvent) LayerUpdateEvent.Invoke(); ////Serialize the IsoLines. //FileStream isolineFileStream = null; //try //{ // string isolineFilePath = AssociatedMap.isoLinesOpenSaveInitialFolder + "\\" + // "Isolines-" + Plot.ObjectId.ToString() + ".stream"; // isolineFileStream = new FileStream(isolineFilePath, FileMode.Create, FileAccess.ReadWrite); // //Serialize (save) the ioslines to a file. // iosLinesLayer.Open(); // Collection features = iosLinesLayer.GetIsoLineFeatures(); // BinaryFormatter formatter = new BinaryFormatter(); // formatter.Serialize(isolineFileStream, features); // iosLinesLayer.Close(); //} //finally //{ // if (isolineFileStream != null) { isolineFileStream.Close(); } //} } catch (Exception ex) { string errorMsg = String.Format("Unable to create isolines. Exception: {0}", ex); logger.ErrorFormat(errorMsg); this.AssociatedMap.LogMessage(MessageLevel.Info, string.Empty, errorMsg); MessageBox.Show(string.Format("Unable to create isolines. Please see the STTI log file for additional information. {0}", ex.Message), "IsoLines Error", MessageBoxButtons.OK); } finally { Application.DoEvents(); MapControl2DMapSuite.IsoLineSemaphore.Release(); } } /// /// Add the IsoLine Layers to the overlay. /// private void CreateIsoLineLayersCompleteAsyncCallback(IAsyncResult itfAR) { if (this.AssociatedMap.WinformsMap1.InvokeRequired) { //Fire the event to have the overlays added. this.AssociatedMap.WinformsMap1.Invoke(new MethodInvoker(LayerUpdateEvent)); } } /// /// Add the IsoLine Layers to the overlay. /// private void AddIsoLineLayersToOverlay() { try { lock (stateLock) { LayerOverlay isoLineOverlay = AssociatedMap.IsoLineOverlay; if (null != isoLineOverlay) { if (null != IsoLineFeatureLayer) { isoLineOverlay.Layers.Add(LayerNameGridCells, IsoLineFeatureLayer); } if (null != IsoLinePlotNamesLayer) { isoLineOverlay.Layers.Add(LayerNameIsoLinePlotNames, IsoLinePlotNamesLayer); } if (null != IsoLinesLegendLayer) { AssociatedMap.WinformsMap1.AdornmentOverlay.Layers.Add(LayerNamePlotLegend, IsoLinesLegendLayer); } if (IsThereEnoughUniqueValues) { if (null != IsoLineLabelsLayer) { isoLineOverlay.Layers.Add(LayerNameIsoLineLabels, IsoLineLabelsLayer); } if (null != IsoLinesLayer) { isoLineOverlay.Layers.Add(LayerNameIsoLine, IsoLinesLayer); // add on top of Fill layer } } Plot.IsConstructed = true; AssociatedMap.WinformsMap1.Overlays.MoveToTop(isoLineOverlay); gridCellMatrix = null; } if (!AssociatedMap.RefreshWinformsMap()) { RemovePlotLayersFromOverlay(Plot.Title); Plot.IsConstructed = false; } logger.InfoFormat("Background Thread completed and map refreshed for 2D Plot Data File. {0}", Plot.ObjectId); AssociatedMap.RaiseEventStatusBar( new MapControl2DMap.StatusBarUpdateEventArgs { StatusText = "Completed Generating isolines...", Cancel = false }); } } catch (Exception ex) { string errorMsg = String.Format("Exception while creating Coverage Plot on the Map2D. Exception: {0}", ex); logger.ErrorFormat(errorMsg); this.AssociatedMap.LogMessage(MessageLevel.Info, string.Empty, errorMsg); //if Refresh causes the exception remove this plot to prevent repeated exceptions. //RemovePlotFromMapByName(info.Title); } finally { Application.DoEvents(); } } /// /// Create a grid file for the isoline contours. /// private void CreateGridFile() { //Greate the grid definition based on the extent, cell size etc. GridDefinition gridDefinition = new GridDefinition(drawingExtent, CellWidth, NoDataValue, contoursPointData); FileStream gridFileStream = null; try { gridFileStream = new FileStream(gridFilePath, FileMode.Create, FileAccess.ReadWrite); //Generate the grid based on Inverse Distance Weighted interpolation model. //You can define your own model if needed. GridFeatureSource.GenerateGrid(gridDefinition, new InverseDistanceWeightedGridInterpolationModel(2, double.MaxValue), gridFileStream); } finally { if (gridFileStream != null) { gridFileStream.Close(); } } } /// /// Stream a grid file for the isoline contours from the Dictionary of Contour Points. /// private void StreamDictionaryToGridFile() { StreamWriter gridFileStream = null; try { double cellWidth = CellWidth; int gridCellRowCountOptimized = GridCellRowCount; int gridCellColumnCountOptimized = GridCellColumnCount; int gridCellCountModulus = 0; if (IsPlotResolutionOptForSpeed && (GridCellRowCount > PlotResolutionGridSize || GridCellColumnCount > PlotResolutionGridSize)) { gridCellCountModulus = Math.Max(GridCellRowCount / PlotResolutionGridSize, GridCellColumnCount / PlotResolutionGridSize); gridCellRowCountOptimized = (GridCellRowCount / gridCellCountModulus) + 1; gridCellColumnCountOptimized = (GridCellColumnCount / gridCellCountModulus) + 1; double latitudeHeight = System.Math.Abs(MaxLatitude - MinLatitude); double longitudeWidth = System.Math.Abs(MaxLongitude - MinLongitude); cellWidth = Math.Max(longitudeWidth / gridCellColumnCountOptimized, latitudeHeight / gridCellRowCountOptimized); } gridCellMatrix = new GridCell[gridCellRowCountOptimized, gridCellColumnCountOptimized]; gridFileStream = new StreamWriter(gridFilePath); gridFileStream.WriteLine("ncols " + gridCellColumnCountOptimized.ToString()); gridFileStream.WriteLine("nrows " + gridCellRowCountOptimized.ToString()); gridFileStream.WriteLine("xllcorner " + MinLongitude.ToString()); gridFileStream.WriteLine("yllcorner " + MinLatitude.ToString()); gridFileStream.WriteLine("cellsize " + cellWidth.ToString()); gridFileStream.WriteLine("NODATA_Value " + NoDataValue.ToString()); //Dump the Dictionary into a two dimensional array. double[,] arraypoints = new double[gridCellRowCountOptimized, gridCellColumnCountOptimized]; for (int rindex = 0; rindex < gridCellRowCountOptimized; rindex++) { for (int cindex = 0; cindex < gridCellColumnCountOptimized; cindex++) { //Initialize all array values with NoDataValue. arraypoints[rindex, cindex] = NoDataValue; gridCellMatrix[rindex, cindex] = new GridCell(MinLongitude + (cellWidth * rindex), MinLatitude + (cellWidth * cindex), NoDataValue); } } int gridCellRowOffset = GridCellRowCount - 1; foreach (KeyValuePair contourPoint in contoursPointData) { //Maximum Latitude to the top of the array so flip the latitude index. //Round by CellWidth/10000 int rowIndex = gridCellRowOffset - (int)Math.Abs(((contourPoint.Key.Y - MinLatitude) + RoundCellWidth) / CellWidth); int colIndex = (int)Math.Abs(((contourPoint.Key.X - MinLongitude) + RoundCellWidth) / CellWidth); if (rowIndex < 0 || rowIndex >= GridCellRowCount || colIndex < 0 || colIndex >= GridCellColumnCount) { System.Diagnostics.Debug.WriteLine("Error in coord indices: Lat: {0} Min: {1} Lng: {2} Min: {3} " + "Cell: {4} Offset: {5} row: {6} col: {7}", contourPoint.Key.Y, MinLatitude, contourPoint.Key.X, MinLongitude, CellWidth, gridCellRowOffset, rowIndex, colIndex); } else { if (gridCellCountModulus > 0) { //Skip all the values between Indices with a modulus of 0 //so that the Grid will be every second or third or modulus value. if (rowIndex % gridCellCountModulus == 0 && colIndex % gridCellCountModulus == 0) { int rowIndexMod = rowIndex / gridCellCountModulus; int colIndexMod = colIndex / gridCellCountModulus; arraypoints[rowIndexMod, colIndexMod] = contourPoint.Value; gridCellMatrix[rowIndexMod, colIndexMod] = new GridCell(contourPoint.Key.X, contourPoint.Key.Y, contourPoint.Value); } } else { arraypoints[rowIndex, colIndex] = contourPoint.Value; gridCellMatrix[rowIndex, colIndex] = new GridCell(contourPoint.Key.X, contourPoint.Key.Y, contourPoint.Value); } } } //Stream the array rows with space separator between values. for (int rindex = 0; rindex < gridCellRowCountOptimized; rindex++) { System.Text.StringBuilder line = new StringBuilder(); for (int cindex = 0; cindex < gridCellColumnCountOptimized; cindex++) { if (cindex > 0) line.Append(CpConstants.Spacer); line.Append(arraypoints[rindex, cindex].ToString()); } gridFileStream.WriteLine(line); } } catch (Exception ex) { string errorMsg = String.Format("Exception while streaming Coverage Plot on the Map2D. Exception: {0}", ex); logger.ErrorFormat(errorMsg); this.AssociatedMap.LogMessage(MessageLevel.Info, string.Empty, errorMsg); //We cannot continue without the grid matrix and gridfile //so throw the exception so the caller will bypass generating isolines. throw ex; } finally { if (gridFileStream != null) { gridFileStream.Close(); } } } /// /// Get the layer for the grid IsoLine. /// /// The name used for this Isolines layer. /// Layer for GridIsoLineLayer. private Layer GetGridIsoLineLayer(string layerName) { List boundaries = Plot.PlotData.DataTable.CpReportNode.CpBoundaries; //Load the grid into the GridIsoLineLayer using the number of breaks //defined by the isoLineLevels. if (null == boundaries || (boundaries.Count == 1 && boundaries[0].ShapeEnum == McShapeEnum.Rectangle)) { //GridIsoLineLayer isoLineLayer = new GridIsoLineLayer(gridFilePath, LineLevelsAndColors.Keys); InMemoryGridIsoLineLayer isoLineLayer = new InMemoryGridIsoLineLayer(gridCellMatrix, LineLevelsAndColors.Keys); isoLineLayer.Name = layerName; //Setup a class break style based on the isoline levels and the colors. ClassBreakStyle classBreakLineStyle = new ClassBreakStyle(isoLineLayer.DataValueColumnName); foreach (double index in LineLevelsAndColors.Keys) { classBreakLineStyle.ClassBreaks.Add(new ClassBreak(index, GetLineStyle(index))); } isoLineLayer.CustomStyles.Add(classBreakLineStyle); return isoLineLayer; } else { AreaBaseShape boundaryAreaShape = drawingExtent; foreach (McShape cpBoundary in boundaries) { switch (cpBoundary.ShapeEnum) { case McShapeEnum.Circle: McCircle circle = cpBoundary as McCircle; if (null != circle) { PointShape centerPoint = AssociatedMap.ConvertLngLatToPointShape( circle.Center.Longitude_decdeg, circle.Center.Latitude_decdeg); EllipseShape ellipseShape = new EllipseShape(centerPoint, circle.Radius_km, AssociatedMap.GeographyMapUnit, DistanceUnit.Kilometer); ellipseShape.Id = circle.ObjectId.ToString(); // Adjust the clipping scaleDown percentage based // the coarseness of the Cellwidth. Calculate the percentage // as a ratio of the cellWidth to the circle radius. EGL.Point center = new EGL.Point(circle.Center.Latitude_decdeg*EGL.Math.Degree2Rad, circle.Center.Longitude_decdeg*EGL.Math.Degree2Rad); double distanceRad = center.DistanceRad(circle.Radius_km); //Scale the isolines down by a percentage of the cellwidth size //divided by the radius of the circle in radians //plus a rounding value to assure the percentage is greater than the ratio. double scaleDownPercent = ((CellWidth * EGL.Math.Degree2Rad) / (distanceRad * Math.Sqrt(2.0d))) * 100.0d +1.0; ellipseShape.ScaleDown(scaleDownPercent); boundaryAreaShape = ellipseShape; } break; case McShapeEnum.Polyline: McPolyline polyline = cpBoundary as McPolyline; if (null != polyline) { PolygonShape polygonShape = AssociatedMap. ConvertMmPointTimeListToPolygonShape( polyline.PointTimeList, polyline.ObjectId.ToString()); polygonShape.ScaleDown(2.0); MultipolygonShape multiPolygonShape = boundaryAreaShape as MultipolygonShape; if (null == multiPolygonShape) { multiPolygonShape = new MultipolygonShape(); } multiPolygonShape.Polygons.Add(polygonShape); boundaryAreaShape = multiPolygonShape; } break; case McShapeEnum.Rectangle: McRectangle rectangle = cpBoundary as McRectangle; if (null != rectangle) { boundaryAreaShape = new RectangleShape( AssociatedMap.ConvertLngLatToPointShape( rectangle.LowerLeft.Longitude_decdeg, rectangle.UpperRight.Latitude_decdeg), AssociatedMap.ConvertLngLatToPointShape( rectangle.UpperRight.Longitude_decdeg, rectangle.LowerLeft.Latitude_decdeg)); } break; default: boundaryAreaShape = drawingExtent; break; } } InMemoryFeatureLayer isoFeatureLayer = new InMemoryFeatureLayer(); isoFeatureLayer.FeatureSource.Open(); isoFeatureLayer.Columns.Add(new FeatureSourceColumn("DataValueColumnName")); isoFeatureLayer.Name = layerName; Collection isoFeatures = IsoLineLayer.GetIsoFeatures(gridCellMatrix, LineLevelsAndColors.Keys, "DataValueColumnName", IsoLineType.LinesOnly); foreach (Feature feature in isoFeatures) { LineShape lineShape = (LineShape) feature.GetShape(); MultilineShape multilineShape = lineShape.GetIntersection(boundaryAreaShape); if (multilineShape.Lines.Count < 1) { continue; } Feature multiLineFeature = new Feature(multilineShape); multiLineFeature.ColumnValues.Add("DataValueColumnName", feature.ColumnValues["DataValueColumnName"]); isoFeatureLayer.InternalFeatures.Add(multiLineFeature); } isoFeatureLayer.FeatureSource.Close(); //Setup a class break style based on the isoline levels and the colors. ClassBreakStyle classBreakLineStyle = new ClassBreakStyle("DataValueColumnName"); foreach (double index in LineLevelsAndColors.Keys) { classBreakLineStyle.ClassBreaks.Add(new ClassBreak(index, GetLineStyle(index))); } isoFeatureLayer.ZoomLevelSet.ZoomLevel01.CustomStyles.Add(classBreakLineStyle); isoFeatureLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20; return isoFeatureLayer; } } /// /// Get the layer for the grid IsoLine labels. /// /// The name used for this Isolines layer. /// Layer for GridIsoLineLayer. private IsoLineLayer GetGridIsoLineLabelsLayer(string layerName) { //Create labels on a new layer for the isoLineLevels //GridIsoLineLayer isoLineLayer = new GridIsoLineLayer(gridFilePath, LineLevelsAndColors.Keys); InMemoryGridIsoLineLayer isoLineLayer = new InMemoryGridIsoLineLayer(gridCellMatrix, LineLevelsAndColors.Keys); isoLineLayer.Name = layerName; //Create the text styles to label the lines TextStyle textStyle = TextStyles.CreateSimpleTextStyle(isoLineLayer.DataValueColumnName, "Arial", 8, DrawingFontStyles.Bold, GetTextColor(), 0, 0); textStyle.OverlappingRule = LabelOverlappingRule.NoOverlapping; textStyle.SplineType = SplineType.StandardSplining; textStyle.DuplicateRule = LabelDuplicateRule.UnlimitedDuplicateLabels; textStyle.TextLineSegmentRatio = 9999999; textStyle.FittingLineInScreen = true; textStyle.SuppressPartialLabels = true; double variableRange = LineLevelsAndColors.Count + 1; if (null != Plot) { isoLineLayer.IsVisible = Plot.IsVisibleLabels; if (null != Plot.PlotData && null != Plot.PlotData.VariableGraphicProperties) { variableRange = Math.Abs(Plot.PlotData.VariableGraphicProperties.MaxVariableValue - Plot.PlotData.VariableGraphicProperties.MinVariableValue); } } if (LineLevelsAndColors.Count < variableRange) { textStyle.NumericFormat = "0.#"; } else { textStyle.NumericFormat = "0.###"; } isoLineLayer.CustomStyles.Add(textStyle); return isoLineLayer; } /// /// Get the layer for the grid IsoLine Plot Names. /// /// The name used for this Isolines layer. /// Layer for GridIsoLineLayer. private FeatureLayer GetGridIsoLinePlotNamesLayer(string layerName) { //Load a new GridFeatureLayer based on the current grid file. InMemoryFeatureLayer gridFeatureLayer = new InMemoryFeatureLayer(); gridFeatureLayer.Name = layerName; //Setup a TextStyle for the plot title. TextStyle textStyle = TextStyles.CreateSimpleTextStyle("Title", "Arial", 8, DrawingFontStyles.Regular, GetTextColor(), 0, 4); textStyle.OverlappingRule = LabelOverlappingRule.AllowOverlapping; textStyle.SplineType = SplineType.StandardSplining; textStyle.DuplicateRule = LabelDuplicateRule.UnlimitedDuplicateLabels; textStyle.TextLineSegmentRatio = 9999999; textStyle.FittingLineInScreen = true; textStyle.SuppressPartialLabels = true; textStyle.PointPlacement = PointPlacement.UpperCenter; //Update the first feature in the gridFeatureLayer with the Plot.Title gridFeatureLayer.FeatureSource.Open(); PointShape latLngPointShape = AssociatedMap.ConvertLngLatToPointShape( MinLongitude + (MaxLongitude - MinLongitude) / 2, //Center by longitude. MaxLatitude); // + (MaxLatitude - MinLatitude) / 20); //Lat plus percent lat. gridFeatureLayer.FeatureSource.BeginTransaction(); gridFeatureLayer.FeatureSource.AddFeature(latLngPointShape); gridFeatureLayer.FeatureSource.CommitTransaction(); Collection gridFeatures = gridFeatureLayer.FeatureSource.GetAllFeatures(ReturningColumnsType.AllColumns); if (gridFeatures.Count > 0) { if (null != Plot.PlotData.VariableGraphicProperties && !String.IsNullOrEmpty(Plot.PlotData.VariableGraphicProperties.PlotLabel)) { textStyle.LabelPositions.Add( gridFeatureLayer.FeatureSource.GetAllFeatures( ReturningColumnsType.AllColumns)[0].Id, new WorldLabelingCandidate(Plot.PlotData.VariableGraphicProperties.PlotLabel, latLngPointShape)); } else { textStyle.LabelPositions.Add( gridFeatureLayer.FeatureSource.GetAllFeatures( ReturningColumnsType.AllColumns)[0].Id, new WorldLabelingCandidate( Plot.Title.Substring(0, Plot.Title.IndexOf(CpConstants.StartBracket)), latLngPointShape)); } } gridFeatureLayer.FeatureSource.Close(); gridFeatureLayer.ZoomLevelSet.ZoomLevel01.CustomStyles.Add(textStyle); gridFeatureLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20; gridFeatureLayer.IsVisible = Plot.IsVisiblePlotNames; gridFeatureLayer.Close(); return gridFeatureLayer; } /// /// Get the layer for the grid Feature layer. /// /// The name used for this Isolines layer. /// Layer for GridFeatureLayer. private FeatureLayer GetGridFeatureLayer(string layerName) { //Load a new GridFeatureLayer based on the current grid file. GridFeatureLayer gridFeatureLayer = new GridFeatureLayer(gridFilePath); //InMemoryGridFeatureLayer gridFeatureLayer = new InMemoryGridFeatureLayer(gridCellMatrix); gridFeatureLayer.Name = layerName; //Create a class break style. ClassBreakStyle classBreakLineStyle = new ClassBreakStyle(gridFeatureLayer.DataValueColumnName); if (FillLevelsAndColors.Count > 0) { double holdFillLevelKey = double.MinValue; //Setup a class break style based on the isoline levels and the colors. foreach (double index in FillLevelsAndColors.Keys) { if (index <= MinVariableValue) { //This will bypass all threshold values less than MinVariableValue. holdFillLevelKey = index; } else { AreaStyle fillAreaStyle = GetAreaStyle(index); //for BelowContourLineLevel use color assigned to the previous level. if (holdFillLevelKey == ThresholdDefault.FillAllValue) { classBreakLineStyle.ClassBreaks.Add(new ClassBreak(MaxVariableValue + EGL.Math.Eps, fillAreaStyle)); } else if (holdFillLevelKey == MinVariableValue) { classBreakLineStyle.ClassBreaks.Add(new ClassBreak(MinVariableValue - EGL.Math.Eps, fillAreaStyle)); } else { classBreakLineStyle.ClassBreaks.Add(new ClassBreak(holdFillLevelKey, fillAreaStyle)); } holdFillLevelKey = index; } } //For BelowContourLineLevel add the final LineLevel with the Transparent color. classBreakLineStyle.ClassBreaks.Add(new ClassBreak(holdFillLevelKey, GetAreaStyle(MinVariableValue))); gridFeatureLayer.ZoomLevelSet.ZoomLevel01.CustomStyles.Add(classBreakLineStyle); } gridFeatureLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20; gridFeatureLayer.IsVisible = Plot.IsVisibleFill; gridFeatureLayer.Close(); return gridFeatureLayer; } /// /// Create the Legend Items for the plot individual thresholds /// private LegendAdornmentLayer CreateLegendLayer() { try { MapLegendAdornmentLayer legendAdornmentLayer = new MapLegendAdornmentLayer(250, 250, 15, 0); if (null != AssociatedMap && null != legendAdornmentLayer && null != legendAdornmentLayer.LegendItems && Plot.PlotData.VariableGraphicProperties.AreIndividualLinesDisplayed && null != Plot.PlotData.VariableGraphicProperties.ThresholdDefaultList && Plot.PlotData.VariableGraphicProperties.ThresholdDefaultList.Count > 0) { legendAdornmentLayer.Name = LayerNamePlotLegend; legendAdornmentLayer.BackgroundMask.FillSolidBrush = new GeoSolidBrush(GeoColor.SimpleColors.Silver); legendAdornmentLayer.Transparency = LegendTransparency; legendAdornmentLayer.LegendItems.Clear(); //Add Plot Label as a Legend title with reportComment (RelTime) if defined. legendAdornmentLayer.LegendItems.Add(CreateLegendTitle()); //Add LegendItems for the threshold LineStyles. foreach (ThresholdDefault threshold in Plot.PlotData.VariableGraphicProperties.ThresholdDefaultList) { if (Plot.PlotData.VariableGraphicProperties.CpFillChoices == CpFillChoicesEnum.NoFill || threshold.ContourLineColor != threshold.FillTrajLinkColor || threshold.ContourLineGraphicProperties.Opacity != threshold.FillGraphicProperties.Opacity) { LineStyle lineStyle = GetLineStyle(threshold.ThresholdValue); if (lineStyle.OuterPen.Color.AlphaComponent > 0) { LegendItem legendItem = new LegendItem(); legendItem.TopPadding = 0; legendItem.BottomPadding = 0; legendItem.ImageTopPadding = 2; legendItem.ImageBottomPadding = 2; legendItem.Height = legendItem.ImageTopPadding + legendItem.ImageBottomPadding + legendItem.ImageHeight; legendItem.ImageStyle = lineStyle; legendItem.TextStyle = new TextStyle( FormatThresholdValue(threshold.ThresholdValue), new GeoFont("Arial", 8), new GeoSolidBrush(GeoColor.SimpleColors.DarkBlue)); legendAdornmentLayer.LegendItems.Add(legendItem); } } } //Add LegendItems for the Threshold Fill AreaStyles when IndividualLinesFill is set. if (Plot.PlotData.VariableGraphicProperties.CpFillChoices == CpFillChoicesEnum.IndividualLinesFill && null != Plot.PlotData.VariableGraphicProperties.ThresholdDefaultList && Plot.PlotData.VariableGraphicProperties.ThresholdDefaultList.Count > 0) { string currentThresholdRange = string.Empty; string previousThresholdValue = string.Empty; int thresholdIndex = 0; foreach (ThresholdDefault threshold in Plot.PlotData.VariableGraphicProperties.ThresholdDefaultList) { if (string.IsNullOrEmpty(previousThresholdValue)) { currentThresholdRange = CpConstants.StartAngleBracket + CpConstants.Spacer + FormatThresholdValue(threshold.ThresholdValue); } else if (thresholdIndex >= Plot.PlotData.VariableGraphicProperties.ThresholdDefaultList.Count - 1) { currentThresholdRange = CpConstants.EndAngleBracket + CpConstants.Spacer + previousThresholdValue; } else { currentThresholdRange = CpConstants.StartBracket + previousThresholdValue + CpConstants.ColonSeparator + FormatThresholdValue(threshold.ThresholdValue) + CpConstants.EndBracket; } previousThresholdValue = FormatThresholdValue(threshold.ThresholdValue); thresholdIndex++; AreaStyle fillAreaStyle = GetAreaStyle(threshold.ThresholdValue); if (fillAreaStyle.FillSolidBrush.Color.AlphaComponent > 0) { LegendItem legendItem = new LegendItem(); legendItem.TopPadding = 0; legendItem.BottomPadding = 0; legendItem.ImageTopPadding = 2; legendItem.ImageBottomPadding = 2; legendItem.Height = legendItem.ImageTopPadding + legendItem.ImageBottomPadding + legendItem.ImageHeight; legendItem.ImageStyle = fillAreaStyle; legendItem.TextStyle = new TextStyle( currentThresholdRange, new GeoFont("Arial", 8), new GeoSolidBrush(GeoColor.SimpleColors.DarkBlue)); legendAdornmentLayer.LegendItems.Add(legendItem); } } } if (legendAdornmentLayer.LegendItems.Count > 1) { legendAdornmentLayer.Height = 30 + legendAdornmentLayer.LegendItems.Count * (legendAdornmentLayer.LegendItems[1].Height + legendAdornmentLayer.LegendItems[1].BottomPadding + legendAdornmentLayer.LegendItems[1].TopPadding); legendAdornmentLayer.Width = (legendAdornmentLayer.LegendItems[0].Width + legendAdornmentLayer.LegendItems[0].LeftPadding + legendAdornmentLayer.LegendItems[0].RightPadding); } legendAdornmentLayer.IsVisible = Plot.IsVisibleLegend; return legendAdornmentLayer; } } catch (Exception ex) { string errorMsg = String.Format("Exception while creating Legend for the plot. Exception: {0}", ex); logger.ErrorFormat(errorMsg); this.AssociatedMap.LogMessage(MessageLevel.Info, string.Empty, errorMsg); } return null; } /// /// Determine if threshold value should be exponential notation or just ToString(). /// /// /// private string FormatThresholdValue(double thresholdValue) { if ((thresholdValue != 0.0 && thresholdValue > -.0001 && thresholdValue < .0001) || thresholdValue < -10000 || thresholdValue > 10000) { return thresholdValue.ToString("e3", System.Globalization.CultureInfo.InvariantCulture); } return thresholdValue.ToString(); } /// /// Create the Legend Items for the plot individual thresholds /// private LegendItem CreateLegendTitle() { LegendItem legendTitle = new LegendItem(); try { if (null != Plot.PlotData.DataTable && null != Plot.PlotData.DataTable.CpReportNode) { StringBuilder layerTitle = new StringBuilder(); string title = string.Empty; if (null != Plot.PlotData.VariableGraphicProperties && !String.IsNullOrEmpty(Plot.PlotData.VariableGraphicProperties.PlotLabel)) { int length = Plot.PlotData.VariableGraphicProperties.PlotLabel.IndexOf(CpConstants.StartBracket); if (length > 0) { title = Plot.PlotData.VariableGraphicProperties.PlotLabel.Substring(0, length); } else { title = Plot.PlotData.VariableGraphicProperties.PlotLabel; } } else { int length = Plot.Title.IndexOf(CpConstants.StartBracket); if (length > 0) { title = Plot.Title.Substring(0, length); } else { title = Plot.Title; } } if (null != Plot.PlotData.VariableGraphicProperties && !String.IsNullOrEmpty(Plot.PlotData.VariableGraphicProperties.VariableUnits)) { title = title + CpConstants.Spacer + CpConstants.LeftParens + Plot.PlotData.VariableGraphicProperties.VariableUnits + CpConstants.RightParens; } if (title.Length > MaxCharLegendTitle) { //If title is longer than MaxCharLegendTitle char, split the title into multiple lines by //inserting a newline "/n" at word wrap of less than 30 char. int indexstart = 0; while (indexstart < title.Length) { int indexend = MaxCharLegendTitle; if (indexstart + MaxCharLegendTitle > title.Length) { indexend = title.Length - indexstart; layerTitle.Append(title.Substring(indexstart, indexend)); indexstart = title.Length; } else { int indexendspacer = title.Substring(indexstart, indexend).LastIndexOf(CpConstants.Spacer); if (indexendspacer > 0) { layerTitle.Append(title.Substring(indexstart, indexendspacer)); layerTitle.Append("\n"); indexstart += indexendspacer; } else if (indexendspacer <= 0) { layerTitle.Append(title.Substring(indexstart, indexend)); layerTitle.Append("\n"); indexstart += indexend; } } } } else { layerTitle.Append(title); } if (null != Plot.PlotData.DataTable.CpReportNode && !string.IsNullOrEmpty(Plot.PlotData.DataTable.CpReportNode.ReportComment)) { layerTitle.Append("\n"); //always start time on next line layerTitle.Append(Plot.PlotData.DataTable.CpReportNode.ReportComment); } GeoFont titleFont = new GeoFont("Arial", 7, DrawingFontStyles.Bold); GdiPlusGeoCanvas test = new GdiPlusGeoCanvas(); float textWidth = test.MeasureText(layerTitle.ToString(), titleFont).Width; legendTitle.TextStyle = new TextStyle(layerTitle.ToString(), titleFont, new GeoSolidBrush(GeoColor.SimpleColors.DarkRed)); legendTitle.BackgroundMask = new AreaStyle(new GeoSolidBrush(GeoColor.SimpleColors.PastelBlue)); legendTitle.TextLeftPadding = 3; legendTitle.TextRightPadding = 3; legendTitle.Width = textWidth + legendTitle.TextLeftPadding + (legendTitle.TextRightPadding * 2); legendTitle.Height = 35; legendTitle.LeftPadding = 3; legendTitle.RightPadding = 3; legendTitle.TopPadding = 2; legendTitle.BottomPadding = 5; legendTitle.ImageWidth = 0; legendTitle.ImageHeight = 0; legendTitle.ImageLeftPadding = 0; legendTitle.ImageRightPadding = 0; //This dead code adds an icon to the legned title; //legendTitle.ImageWidth = 16; //legendTitle.ImageHeight = 16; //legendTitle.ImageLeftPadding = 2; //legendTitle.ImageRightPadding = 2; //System.Drawing.Image dragdrop = global::MapControl2d.Properties.Resources.cursor_drag_arrow; //MemoryStream stream = new MemoryStream(); //dragdrop.Save(stream, System.Drawing.Imaging.ImageFormat.Png); //legendTitle.ImageStyle = new PointStyle(new GeoImage(stream)); } } catch (Exception ex) { string errorMsg = String.Format("Exception while creating Legend title for the plot. Exception: {0}", ex); logger.ErrorFormat(errorMsg); } return legendTitle; } /// /// Load the Color Collections for the LineStyle and AreaStyle breaks. /// Sets the LineWidth, PenLineDashStyle, Line Opacity and Fill Opacity. /// Calculate the isoLineLevels. /// private void LoadColorCollections() { LineLevelsAndColors = new SortedDictionary(); FillLevelsAndColors = new SortedDictionary(); GridLineLevelCount = 0; if (Plot.PlotData.VariableGraphicProperties.IsLineSetDisplayed) { //Add the IsoLineLevels and Line Color as Specified by the ContourLineSet. if (null != Plot.PlotData.VariableGraphicProperties.ContourLineSetDefaults && Plot.PlotData.VariableGraphicProperties.ContourLineSetDefaults.LevelCount > 0) { ContourLineSetDefaults contourLineSet = Plot.PlotData.VariableGraphicProperties.ContourLineSetDefaults; GridLineLevelCount += contourLineSet.LevelCount; GeoColor lineColorStart = new GeoColor( contourLineSet.MinLevelColor.R, contourLineSet.MinLevelColor.G, contourLineSet.MinLevelColor.B); GeoColor lineColorEnd = new GeoColor( contourLineSet.MaxLevelColor.R, contourLineSet.MaxLevelColor.G, contourLineSet.MaxLevelColor.B); //If only a single lineColor is given, generate a set of colors in the Hue family. Collection lineColors = GeoColor.GetColorsInHueFamily(lineColorStart, GridLineLevelCount); if (!lineColorStart.Equals(lineColorEnd)) { //Create a series of colors from blue to red that we will use for the isoLine breaks. lineColors = GeoColor.GetColorsInQualityFamily(lineColorStart, lineColorEnd, GridLineLevelCount, ColorWheelDirection.CounterClockwise); } //The IsoLineLevels are calculated by dividing the Range by the NumberOfLineLevels. double incrementLevels = (contourLineSet.MaxLevel - contourLineSet.MinLevel) / (double) contourLineSet.LevelCount; for (int index = 0; index < contourLineSet.LevelCount; index++) { double lineLevel = contourLineSet.MinLevel + (incrementLevels * (double) index); //Use all the same color for the lineLevels for the ContourLineSet. if (!LineLevelsAndColors.ContainsKey(lineLevel)) { LineLevelsAndColors.Add(lineLevel, new GraphicColorProperties() { Color = System.Drawing.Color.FromArgb( lineColors[index].RedComponent, lineColors[index].GreenComponent, lineColors[index].BlueComponent), Pattern = contourLineSet.ContourLineGraphicProperties.Pattern, Opacity = contourLineSet.ContourLineGraphicProperties.Opacity, LineWidth = contourLineSet.ContourLineGraphicProperties.LineWidth, IsFill = contourLineSet.ContourLineGraphicProperties.IsFill, }); } } } } //Now add all the individual ContourLine settings to any already added from ContourLineSet. if (Plot.PlotData.VariableGraphicProperties.AreIndividualLinesDisplayed && null != Plot.PlotData.VariableGraphicProperties.ThresholdDefaultList && Plot.PlotData.VariableGraphicProperties.ThresholdDefaultList.Count > 0) { //Add the count of the Individual ContourLines to LineLevel Count. GridLineLevelCount += Plot.PlotData.VariableGraphicProperties.ThresholdDefaultList.Count; foreach ( ThresholdDefault threshold in Plot.PlotData.VariableGraphicProperties.ThresholdDefaultList) { //Do not add LineLevel if it is a duplicate or if the //DashStyle was the special case of "NoLine". if (!LineLevelsAndColors.ContainsKey(threshold.ThresholdValue) && threshold.ContourLineGraphicProperties.Pattern != GraphicConstants.StyleNoLine) { LineLevelsAndColors.Add(threshold.ThresholdValue, new GraphicColorProperties() { Color = threshold.ContourLineColor, Pattern = threshold.ContourLineGraphicProperties.Pattern, Opacity = threshold.ContourLineGraphicProperties.Opacity, LineWidth = threshold.ContourLineGraphicProperties.LineWidth, IsFill = threshold.ContourLineGraphicProperties.IsFill, }); } } } //Add one extra value for LineColors so that the minimum Line Value will be drawn. LineLevelsAndColors.Add(double.MinValue, new GraphicColorProperties() { Color = GraphicConstants.DefaultLineColor, Pattern = GraphicConstants.DefaultLineDashStyle.ToString(), Opacity = GraphicConstants.DefaultLineOpacityPercent, IsFill = false, }); //Now create the Fill Levels and Colors. //Add one extra value for FillColors to fill outside of lines (below the contour line). if (!FillLevelsAndColors.ContainsKey(MinVariableValue)) { FillLevelsAndColors.Add(MinVariableValue, new GraphicColorProperties() { //We expect these default to be reasonable //i.e. Transparent and Opacity = 0 means no fill will be visible. Color = GraphicConstants.DefaultFillColor, Pattern = GraphicConstants.StyleSolid, Opacity = GraphicConstants.DefaultFillNoOpacityPercent, LineWidth = GraphicConstants.DefaultLineWidth, IsFill = true, }); } if (Plot.PlotData.VariableGraphicProperties.CpFillChoices == CpFillChoicesEnum.LineSetFill) { //Add the IsoLineLevels and Fill Color as Specified by the ContourLineSet. if (null != Plot.PlotData.VariableGraphicProperties.ContourLineSetDefaults && Plot.PlotData.VariableGraphicProperties.ContourLineSetDefaults.LevelCount > 0) { ContourLineSetDefaults contourLineSet = Plot.PlotData.VariableGraphicProperties.ContourLineSetDefaults; GeoColor lineColorStart = new GeoColor( contourLineSet.MinLevelColor.R, contourLineSet.MinLevelColor.G, contourLineSet.MinLevelColor.B); GeoColor lineColorEnd = new GeoColor( contourLineSet.MaxLevelColor.R, contourLineSet.MaxLevelColor.G, contourLineSet.MaxLevelColor.B); //If only a single lineColor is given, generate a set of colors in the Hue family. Collection lineColors = GeoColor.GetColorsInHueFamily(lineColorStart, contourLineSet.LevelCount + 1); if (!lineColorStart.Equals(lineColorEnd)) { //Create a series of colors from blue to red that we will use for the isoLine breaks. lineColors = GeoColor.GetColorsInQualityFamily(lineColorStart, lineColorEnd, contourLineSet.LevelCount + 1, ColorWheelDirection.CounterClockwise); } //The IsoLineLevels are calculated by dividing the Range by the NumberOfLineLevels. double incrementLevels = (contourLineSet.MaxLevel - contourLineSet.MinLevel) / (double) contourLineSet.LevelCount; for (int index = 0; index < contourLineSet.LevelCount + 1; index++) { double lineLevel = contourLineSet.MinLevel + (incrementLevels * (double) index); if (index >= contourLineSet.LevelCount) { lineLevel = double.MaxValue; } //Use all the same fillcolor for the lineLevels for the ContourLineSet. if (!FillLevelsAndColors.ContainsKey(lineLevel)) { FillLevelsAndColors.Add(lineLevel, new GraphicColorProperties() { Color = System.Drawing.Color.FromArgb( lineColors[index].RedComponent, lineColors[index].GreenComponent, lineColors[index].BlueComponent), Pattern = contourLineSet.FillGraphicProperties.Pattern, Opacity = contourLineSet.FillGraphicProperties.Opacity, LineWidth = contourLineSet.FillGraphicProperties.LineWidth, IsFill = contourLineSet.FillGraphicProperties.IsFill, }); } } } } //Now add all the individual ContourLine settings to any already added from ContourLineSet. if (Plot.PlotData.VariableGraphicProperties.CpFillChoices == CpFillChoicesEnum.IndividualLinesFill && null != Plot.PlotData.VariableGraphicProperties.ThresholdDefaultList && Plot.PlotData.VariableGraphicProperties.ThresholdDefaultList.Count > 0) { //These Fill colors are to be created "Below Threshold". foreach ( ThresholdDefault threshold in Plot.PlotData.VariableGraphicProperties.ThresholdDefaultList) { if (!FillLevelsAndColors.ContainsKey(threshold.ThresholdValue)) { FillLevelsAndColors.Add(threshold.ThresholdValue, new GraphicColorProperties() { Color = threshold.FillTrajLinkColor, Pattern = threshold.FillGraphicProperties.Pattern, Opacity = threshold.FillGraphicProperties.Opacity, LineWidth = threshold.FillGraphicProperties.LineWidth, IsFill = threshold.FillGraphicProperties.IsFill, }); } } } } /// /// Get the LineStyle for the IsoLineLevels. /// /// Index to the IsoLineLevel for the color of the LineStyle. /// LineStyle for GridIsoLineLayer. private LineStyle GetLineStyle(Double index) { GraphicColorProperties graphicProperties = LineLevelsAndColors[index]; // Convert Opacity, Color, DashStyle to MapSuite Geo classes. int geoOpacity = CpConstants.ConvertOpacityToAlphaColor( graphicProperties.Opacity); GeoColor geoColor = new GeoColor(geoOpacity, graphicProperties.Color.R, graphicProperties.Color.G, graphicProperties.Color.B); GeoPen lineStylePen = new GeoPen(geoColor, graphicProperties.LineWidth); lineStylePen.DashStyle = CpConstants.ConvertDashStyle(graphicProperties.Pattern); return new LineStyle(lineStylePen); ; } /// /// Get the AreaStyle for the IsoLineLevels. /// /// Index to the IsoLineLevel for the color of the AreaStyle. /// AreaStyle for GridIsoLineLayer. private AreaStyle GetAreaStyle(double index) { GraphicColorProperties graphicProperties = FillLevelsAndColors[index]; // Convert Opacity, Color, DashStyle to MapSuite Geo classes. int geoOpacity = CpConstants.ConvertOpacityToAlphaColor( graphicProperties.Opacity); GeoColor geoColor = new GeoColor(geoOpacity, graphicProperties.Color.B, graphicProperties.Color.R, graphicProperties.Color.G); GeoColor geoColorBack = new GeoColor(geoOpacity, graphicProperties.Color.R, graphicProperties.Color.G, graphicProperties.Color.B); // If Pattern is Solid, then just create GeoSolidBrush AreaStyle. if (graphicProperties.Pattern == GraphicConstants.StyleSolid) { return new AreaStyle(new GeoSolidBrush(geoColorBack)); } // If Pattern is a HatchStyle, then create a FillCustomBrush AreaStyle. GeoHatchStyle geoHatchStyle = CpConstants.ConvertHatchStyle(graphicProperties.Pattern); AreaStyle style = new AreaStyle(); style.Advanced.FillCustomBrush = new GeoHatchBrush(geoHatchStyle, geoColor, geoColorBack); return style; } /// /// Get the TextColor for the IsoLineLevels Labels. /// /// GeoColor for IsoLineLevels labels. private GeoColor GetTextColor() { //GeoColor textColor = LineColors[0]; GeoColor textColor = GeoColor.StandardColors.Black; return textColor; } #endregion } }