Free cookie consent management tool by TermsFeed Policy Generator

source: branches/1073_FitnessLandscapeAnalysis-VRP/VRPProblemAnalyzer/PictureGenerator.cs @ 17328

Last change on this file since 17328 was 7398, checked in by epitzer, 13 years ago

#1696: tune picture generator and add additional problems

File size: 10.1 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using System.Drawing;
6using System.Windows.Forms.DataVisualization.Charting;
7using System.IO;
8
9namespace VRPProblemAnalyzer {
10  public class PictureGenerator {
11
12    private static int TourWidth = 400;
13    private static int TourHeight = 500;
14    private static double MinDemandSize = 2;
15    private static double MaxDemandSize = Math.Min(TourWidth, TourHeight) / 25.0;
16
17    private static int TourBorder = (int)Math.Ceiling(MaxDemandSize);
18
19    private static int ChartWidth = 300;
20    private static int ChartHeight = TourHeight/2;
21   
22    private static int DemandSegments = 20;
23
24    private static int DistanceSegments = 20;
25
26    public static Image GeneratePicture(TSPLIBParser problemInstance, SolutionParser solution) {   
27      Image tourVisualization = GenerateTourVisualization(problemInstance, solution);
28      Image demandDistribution = GenerateDemandDistribution(problemInstance);
29      Image distanceDistribution = GenerateDistanceDistribution(problemInstance);
30
31      SizeF titleSize;
32      using (var font = new Font("Helvetica", 20)) {
33        using (var b = new Bitmap(1, 1)) {
34          using (Graphics g = Graphics.FromImage(b)) {
35            titleSize = g.MeasureString(problemInstance.Name, font);
36          }
37        }
38        Bitmap bmp = new Bitmap(TourWidth+ChartWidth,
39                                Math.Max(TourHeight,  2*ChartHeight) + (int)titleSize.Height);
40        using (Graphics g = Graphics.FromImage(bmp)) {
41          g.FillRectangle(Brushes.White, 0, 0, bmp.Width, bmp.Height);
42          g.DrawString(problemInstance.Name, font, Brushes.Black, (bmp.Width-titleSize.Width)/2, 0);
43          g.DrawImage(tourVisualization, (TourWidth-tourVisualization.Width)/2, titleSize.Height+(TourHeight-tourVisualization.Height)/2);
44          g.DrawImage(demandDistribution, TourWidth, titleSize.Height);
45          g.DrawImage(distanceDistribution, TourWidth, ChartHeight + titleSize.Height);
46        }
47        return bmp;
48      }
49    }
50
51    private static Color[] TourColors = new[] {
52      Color.FromArgb(100, 92, 20,237), Color.FromArgb(100,237,183, 20), Color.FromArgb(100,237, 20,219), Color.FromArgb(100, 20,237, 76),
53      Color.FromArgb(100,237, 61, 20), Color.FromArgb(100,115, 78, 26), Color.FromArgb(100, 20,237,229), Color.FromArgb(100, 39,101, 19),
54      Color.FromArgb(100,230,170,229), Color.FromArgb(100,142,136, 89), Color.FromArgb(100,157,217,166), Color.FromArgb(100, 31, 19,101),
55      Color.FromArgb(100,173,237, 20), Color.FromArgb(100,230,231,161), Color.FromArgb(100,142, 89, 89), Color.FromArgb(100, 93, 89,142),
56      Color.FromArgb(100,146,203,217), Color.FromArgb(100,101, 19, 75), Color.FromArgb(100,198, 20,237), Color.FromArgb(100,185,185,185),
57      Color.FromArgb(100,179, 32, 32), Color.FromArgb(100, 18,119,115), Color.FromArgb(100,104,158,239), Color.Black};
58
59    private static Image GenerateTourVisualization(TSPLIBParser problemInstance, SolutionParser solution) {
60      double[,] coordinates = problemInstance.Vertices;
61
62      double xMin = double.MaxValue, yMin = double.MaxValue, xMax = double.MinValue, yMax = double.MinValue;
63      for (int i = 0; i < coordinates.GetLength(0); i++) {
64        if (xMin > coordinates[i, 0]) xMin = coordinates[i, 0];
65        if (yMin > coordinates[i, 1]) yMin = coordinates[i, 1];
66        if (xMax < coordinates[i, 0]) xMax = coordinates[i, 0];
67        if (yMax < coordinates[i, 1]) yMax = coordinates[i, 1];
68      }
69      double xRange = xMax - xMin;
70      double yRange = yMax - yMin;
71      double scaling = Math.Min((TourWidth-TourBorder*2)/xRange, (TourHeight-TourBorder*2)/yRange);
72      Bitmap bitmap = new Bitmap((int)Math.Ceiling(xRange*scaling+TourBorder*2), (int)Math.Ceiling(yRange*scaling+TourBorder*2));
73      var pens = TourColors.Select(c => new Pen(c, 2)).ToList();
74      using (Graphics g = Graphics.FromImage(bitmap)) {
75        using (var brush = new SolidBrush(Color.FromArgb(100, 0, 0, 0))) {
76          g.FillRectangle(Brushes.White, 0, 0, bitmap.Width, bitmap.Height);
77
78          int currentTour = 0;
79          foreach (List<int> tour in solution.Routes) {
80            Point[] tourPoints = new Point[tour.Count + 2];
81            int[] customerSizes = new int[tour.Count];
82
83            for (int i = -1; i <= tour.Count; i++) {
84              int location = (i == -1 || i == tour.Count) ? 0 : tour[i];
85              Point locationPoint = new Point(TourBorder + ((int) ((coordinates[location, 0] - xMin)*scaling)),
86                                              bitmap.Height -
87                                              (TourBorder + ((int) ((coordinates[location, 1] - yMin)*scaling))));
88              tourPoints[i + 1] = locationPoint;
89              if (i != -1 && i != tour.Count) {
90                customerSizes[i] =
91                  (int)
92                  Math.Round(MinDemandSize +
93                             problemInstance.Demands[location]/problemInstance.Capacity*(MaxDemandSize - MinDemandSize));
94              }
95            }
96            g.DrawPolygon(pens[((currentTour >= pens.Count) ? (pens.Count - 1) : (currentTour))], tourPoints);
97            for (int i = 0; i < tour.Count; i++) {
98              int size = customerSizes[i];
99              g.FillEllipse(brush, tourPoints[i + 1].X - size, tourPoints[i + 1].Y - size, size*2, size*2);
100            }
101            g.FillRectangle(Brushes.Blue, tourPoints[0].X - 5, tourPoints[0].Y - 5, 10, 10);
102            currentTour++;
103          }
104        }
105      }
106      foreach (var p in pens) p.Dispose();
107      return bitmap;
108    }
109
110    private static List<Tuple<double, int>> GetDemandDistribution(TSPLIBParser problemInstance) {
111      var result = new List<Tuple<double, int>>();
112
113      double step = problemInstance.Capacity / (double)DemandSegments;
114      double current = 0;
115
116      while(current < problemInstance.Capacity) {
117        int count = problemInstance.Demands.Where(d => d > current && d <= current + step).Count();
118        result.Add(new Tuple<double, int>(current + step / 2, count));
119        current += step;           
120      }
121     
122      return result;
123    }
124
125    private static Image GenerateDemandDistribution(TSPLIBParser problemInstance) {     
126      Chart chart = new Chart();
127      chart.Size = new Size(ChartWidth, ChartHeight);
128
129      chart.ChartAreas.Add("");
130      ConfigureChartAppearance(chart);
131      chart.ChartAreas[0].AxisX.Title = "Demands";
132      chart.ChartAreas[0].AxisX.Minimum = 0;
133      chart.ChartAreas[0].AxisX.Maximum = problemInstance.Capacity;
134      chart.ChartAreas[0].AxisX.Interval = problemInstance.Capacity/DemandSegments;
135      chart.ChartAreas[0].AxisX.LabelStyle.Interval = problemInstance.Capacity/DemandSegments*4;
136      chart.ChartAreas[0].AxisY.Title = "Customers";
137      chart.ChartAreas[0].AxisY.Minimum = 0;
138      chart.ChartAreas[0].AxisY.Maximum = problemInstance.Vertices.GetLength(0);
139
140      chart.Series.Add("");
141      chart.Series[0].ChartType = SeriesChartType.Column;
142
143      foreach (Tuple<double, int> entry in GetDemandDistribution(problemInstance)) {
144        chart.Series[0].Points.AddXY(entry.Item1, entry.Item2);
145      }
146
147      Image image;
148      using (var chartimage = new MemoryStream()) {
149        chart.SaveImage(chartimage, ChartImageFormat.Png);
150        image = Image.FromStream(chartimage);
151      }
152      return image;
153    }
154
155    private static void ConfigureChartAppearance(Chart chart) {
156      chart.ChartAreas[0].BackColor = Color.White;
157      chart.ChartAreas[0].AxisX.TitleFont = new Font("Helvetica", 16);
158      chart.ChartAreas[0].AxisX.MajorGrid.Enabled = false;
159      chart.ChartAreas[0].AxisY.TitleFont = new Font("Helvetica", 16);
160      chart.ChartAreas[0].AxisY.MajorGrid.Enabled = true;
161      chart.ChartAreas[0].AxisY.MajorGrid.LineColor = Color.FromArgb(100, Color.LightBlue);
162    }
163
164    private static double GetAverageDistance(int customer, double[,] vertices) {
165      double dist = 0;
166      int count = 0;
167
168      for (int i = 0; i < vertices.GetLength(0); i++) {
169        if (i != customer) {
170          dist += Utils.GetDistance(vertices, customer, i);
171          count++;
172        }
173      }
174
175      return dist / count;
176    }
177
178    private static List<Tuple<double, int>> GetDistanceDistribution(TSPLIBParser problemInstance) {
179      var result = new List<Tuple<double, int>>();
180
181      double step = 1.0 / (double)DistanceSegments;
182      double current = 0;
183
184      List<double> averageDistances = new List<double>();
185      for (int i = 0; i < problemInstance.Vertices.GetLength(0); i++) {
186        averageDistances.Add(GetAverageDistance(i, problemInstance.Vertices));
187      }
188
189        while (current < 1.0) {
190          int count = averageDistances.Where(d => d > current && d <= current + step).Count();
191          result.Add(new Tuple<double, int>(current + step / 2, count));
192          current += step;
193        }
194
195      return result;
196    }
197
198    private static Image GenerateDistanceDistribution(TSPLIBParser problemInstance) {
199      Chart chart = new Chart();
200      chart.Size = new Size(ChartWidth, ChartHeight);
201
202      chart.ChartAreas.Add("");
203      ConfigureChartAppearance(chart);
204      chart.ChartAreas[0].AxisX.Title = "Distances";
205      chart.ChartAreas[0].AxisX.Minimum = 0;
206      chart.ChartAreas[0].AxisX.Maximum = 1;
207      chart.ChartAreas[0].AxisX.Interval = 1.0/DistanceSegments;
208      chart.ChartAreas[0].AxisX.LabelStyle.Interval = 1.0/DistanceSegments*4;
209      chart.ChartAreas[0].AxisY.Title = "Customers";
210      chart.ChartAreas[0].AxisY.Minimum = 0;
211      chart.ChartAreas[0].AxisY.Maximum = problemInstance.Vertices.GetLength(0);
212
213      chart.Series.Add("");
214      chart.Series[0].ChartType = SeriesChartType.Column;
215
216      foreach (Tuple<double, int> entry in GetDistanceDistribution(problemInstance)) {
217        chart.Series[0].Points.AddXY(entry.Item1, entry.Item2);
218      }
219
220      Image image;
221      using (var chartimage = new MemoryStream()) {
222        chart.SaveImage(chartimage, ChartImageFormat.Png);
223        image = Image.FromStream(chartimage);
224      }
225      return image;
226    }
227  }
228}
Note: See TracBrowser for help on using the repository browser.