using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Net; using System.Web.Mvc; using System.Web.UI.DataVisualization.Charting; using System.Web.UI.WebControls; using log4net; using LeafWeb.Core.Charter; using LeafWeb.Core.Entities; using LeafWeb.Core.Parsers; using LeafWeb.Core.Utility; using LeafWeb.WebCms.Models; using LeafWeb.WebCms.Services; using LeafWeb.WebCms.Utility; namespace LeafWeb.WebCms.Controllers { public class ChartController : BaseController { [RequireRequestValue("leafInputId")] public ActionResult Index(int? leafInputId) { if (!leafInputId.HasValue) return View("DataError", model: "Must specify LeafInputId"); //if (leafInputId == 2147483647) // return View(GetTestChartViewModel()); var leafOutputFile = DataService.GetLeafOutput_ChartFile(leafInputId.Value); if (leafOutputFile == null) throw new ArgumentOutOfRangeException(); // TODO: break try { var curveIds = GetCurveIds(leafOutputFile.FileContents.Contents); var viewModel = new ChartViewModel { AvailableCurveId = curveIds, LeafInputId = leafInputId.Value, LeafInputIdentifier = leafOutputFile.LeafInput.Identifier }; return View(viewModel); } catch (ParseException parseException) { var logger = LogManager.GetLogger(GetType()); logger.Warn(parseException); return View("DataError", model: parseException.Message); } catch (Exception e) { var logger = LogManager.GetLogger(GetType()); logger.Warn(e); return View("DataError", model: "Error while loading data. Administrators have been notified."); } } [RequireRequestValue("token")] public ActionResult Index(string token) { var leafInput = DataService.GetLeafInput(token); if (leafInput == null) return View("DataError", model: "Leaf Input Token not found"); return Index(leafInput.Id); } //private ChartViewModel GetTestChartViewModel() //{ // return new ChartViewModel // { // AvailableCurveId = new []{"Valid", "Test"}, // LeafInputId = 2147483647, // LeafInputIdentifier = "Chart Tests" // }; //} public ActionResult ChartCurve(int leafInputId, string curveId, bool base64 = false) { var leafOutputFile = DataService.GetLeafOutput_ChartFile(leafInputId); if (leafOutputFile == null) { var logger = LogManager.GetLogger(GetType()); logger.Warn($"Invalid LeafInputID {leafInputId}"); return new HttpStatusCodeResult(HttpStatusCode.NotFound, $"Invalid LeafInputId {leafInputId}."); } CurveData curveData; try { curveData = GetCurveData(leafOutputFile.FileContents.Contents, curveId); } catch (ParseException e) { return new HttpStatusCodeResult(HttpStatusCode.NotFound, $"Parse exception in {leafOutputFile.Filename}. {e.Message}" ); } catch (Exception) { return new HttpStatusCodeResult(HttpStatusCode.NotFound, $"Unhandled exception thrown while parsing {leafOutputFile.Filename}."); } if (curveData == null || curveData.ParamSets.Count == 0) return new HttpStatusCodeResult(HttpStatusCode.NotFound, $"No curve data was loaded from {leafOutputFile.Filename}."); var charts = GetChartBitmaps(curveData).ToList(); var combinedChart = CombineBitmaps(charts); foreach (var chart in charts) chart.Dispose(); // cleanup using (var ms = new MemoryStream()) { combinedChart.Save(ms, ImageFormat.Png); ms.Seek(0, SeekOrigin.Begin); var base64String = Convert.ToBase64String(ms.ToArray()); if (base64) return Content(base64String, "image/png"); else return File(ms.ToArray(), "image/png", curveId.FilterValidFilename() + ".png"); } } private CurveData GetCurveData(byte[] fileContents, string curveId) { try { LeafGasComparison[] leafGasComparisons; using (var parser = new LeafGasComparisonParser(fileContents)) leafGasComparisons = parser.Parse(curveId); return CurveDataConverter.Convert(leafGasComparisons).FirstOrDefault(); } catch (Exception e) { LogManager.GetLogger(GetType()).Warn(e); throw; } } private string[] GetCurveIds(byte[] fileContents) { using (var parser = new LeafGasComparisonParser(fileContents)) return parser.ExtractCurveIds(); } private IEnumerable GetChartBitmaps(CurveData curveData) { var charts = LeafGasCharter.ProduceCharts(curveData, 1.5); foreach (var chart in charts) { // http://stackoverflow.com/a/336396/99492 var ms = new MemoryStream(); // this gets attached to the bmp, dispose of it later chart.SaveImage(ms, ChartImageFormat.Bmp); ms.Seek(0, SeekOrigin.Begin); yield return new Bitmap(ms); } } private Bitmap CombineBitmaps(IList bitmaps, int columnCount = 2) { if (!bitmaps.Any()) return null; // bitmaps assumed to have same dimensions, use the first one to define that var cellWidth = bitmaps[0].Width; var cellHeight = bitmaps[0].Height; var width = cellWidth * columnCount; var height = cellHeight * bitmaps.Count / columnCount; var combinedBitmap = new Bitmap(width, height); using (var g = Graphics.FromImage(combinedBitmap)) { var currentCol = 0; var currentRow = 0; foreach (var image in bitmaps) { // https://stackoverflow.com/a/41189062/99492 g.DrawImage(image, currentCol * cellWidth, currentRow * cellHeight, cellWidth, cellHeight); currentCol = (currentCol + 1)%columnCount; currentRow += currentCol == 0 ? 1 : 0; } } return combinedBitmap; } public ActionResult ChartSample() { var chart = new Chart { BackColor = Color.FromArgb(255, 255, 255), Width = Unit.Pixel(250), Height = Unit.Pixel(2500) }; var series = new Series { ChartArea = "ca1", ChartType = SeriesChartType.Line }; //series.Font = new Font("Verdana", 8.25f, FontStyle.Regular); var myRandom = new Random(); for (int i = 0; i < 100; i++) { var dp = new DataPoint(); dp.AxisLabel = String.Format("{0}-{1}", i, Guid.NewGuid().ToString().Substring(0, 4)); dp.YValues = new double[] { myRandom.Next(5, 100) }; series.Points.Add(dp); } chart.Series.Add(series); var area = new ChartArea("ca1"); area.Area3DStyle.Enable3D = false; area.AxisX.Interval = 1; //area.BackColor = Color.Transparent; //var labelStyle = new LabelStyle(); //labelStyle.Enabled = true; //labelStyle.Font = new Font("Arial", 3f); area.AxisX.LabelStyle.Font = new Font("Verdana", 8.25f, FontStyle.Underline);//Why does it recognize the style but not the font!!!??? chart.ChartAreas.Add(area); using (var ms = new MemoryStream()) { chart.SaveImage(ms, ChartImageFormat.Png); ms.Seek(0, SeekOrigin.Begin); return File(ms.ToArray(), "image/png", "mychart.png"); } } } }