Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.Problems.BioBoost/HeuristicLab.Problems.BioBoost.Views/3.3/BioBoostCompoundSolutionEditor.cs @ 13072

Last change on this file since 13072 was 13072, checked in by gkronber, 7 years ago

#2499: added code from HeuristicLab.BioBoost.Views (from private repository) nothing much has been changed

File size: 31.4 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.ComponentModel;
4using System.Diagnostics;
5using System.Drawing;
6using System.Linq;
7using System.Runtime.Remoting.Messaging;
8using System.Text;
9using System.Threading;
10using System.Threading.Tasks;
11using System.Windows.Forms;
12using System.Windows.Forms.VisualStyles;
13using System.Xml.Schema;
14using GeoAPI.Geometries;
15using HeuristicLab.BioBoost.Data;
16using HeuristicLab.BioBoost.Evaluators;
17using HeuristicLab.BioBoost.ProblemDescription;
18using HeuristicLab.BioBoost.Representation;
19using HeuristicLab.BioBoost.Utils;
20using HeuristicLab.BioBoost.Views.MapViews;
21using HeuristicLab.Core.Views;
22using HeuristicLab.MainForm;
23using HeuristicLab.PluginInfrastructure;
24using NetTopologySuite.Geometries;
25using SharpMap;
26using SharpMap.Data;
27using SharpMap.Forms;
28using SharpMap.Layers;
29using SharpMap.Rendering.Decoration;
30using SharpMap.Rendering.Decoration.ScaleBar;
31using Point = System.Drawing.Point;
32
33namespace HeuristicLab.BioBoost.Views {
34
35  [Content(typeof(BioBoostCompoundSolution))]
36  public partial class BioBoostCompoundSolutionEditor : ItemView {
37
38    private readonly GradientLegend utilizationLegend;
39
40    private SharpMap.Rendering.Thematics.ColorBlend vectorColorBlend =
41      new SharpMap.Rendering.Thematics.ColorBlend(new[] {
42        Color.FromArgb(0, 0, 255),
43        Color.FromArgb(128, 0, 128),
44        Color.FromArgb(255, 0, 0),
45      }, new[] { 0f, .5f, 1f });
46
47    public const string AllRegionsName = "All";
48
49    public new BioBoostCompoundSolution Content {
50      get { return (BioBoostCompoundSolution)base.Content; }
51      set { base.Content = value; }
52    }
53
54    public BioBoostCompoundSolutionEditor() {
55      InitializeComponent();
56
57      mapBox.Map.Decorations.Add(new ScaleBar {
58        Anchor = MapDecorationAnchor.RightBottom,
59        BarColor1 = Color.White,
60        BarColor2 = Color.Black,
61        BarStyle = ScaleBarStyle.Meridian,
62        MapUnit = (int)Unit.Degree,
63        BarUnit = (int)Unit.Kilometer,
64      });
65
66      utilizationLegend = new GradientLegend();
67      mapBox.Map.Decorations.Add(utilizationLegend);
68      mapBox.Map.Decorations.Add(new NorthArrow());
69      mapBox.ActiveTool = MapBox.Tools.None;
70    }
71
72
73
74    protected override void SetEnabledStateOfControls() {
75      base.SetEnabledStateOfControls();
76      ReadOnly = Locked || Content == null;
77      mapBox.Enabled = !Locked && Content != null;
78      transportLayersListBox.Enabled = !Locked && Content != null;
79      utilizationLayersListBox.Enabled = !Locked && Content != null;
80    }
81
82    #region content events
83    protected override void OnContentChanged() {
84      if (InvokeRequired) {
85        Invoke((Action)OnContentChanged);
86      } else {
87        base.OnContentChanged();
88        if (Content == null) {
89          Clear();
90        } else {
91          Update();
92        }
93      }
94    }
95    protected override void DeregisterContentEvents() {
96      Content.SolutionChanged -= SolutionChanged;
97      base.DeregisterContentEvents();
98    }
99
100    protected override void RegisterContentEvents() {
101      base.RegisterContentEvents();
102      Content.SolutionChanged += SolutionChanged;
103    }
104
105    private void SolutionChanged(object sender, EventArgs e) {
106      if (InvokeRequired) {
107        Invoke(new EventHandler<EventArgs>(SolutionChanged), sender, e);
108      } else {
109        Update();
110      }
111    }
112    #endregion
113
114
115    #region Main Logic
116    private void Clear() {
117      qualityLabel.Text = string.Empty;
118      feedstockCostsLabel.Text = string.Empty;
119      transportCostsLabel.Text = string.Empty;
120      utilizationLayersListBox.Items.Clear();
121      transportLayersListBox.Items.Clear();
122      mapBox.Map.Layers.Clear(); // only two layers are active at any time (representing the layers selected in the list boxes on the left)
123    }
124
125    private void Update() {
126      // store currently selected items
127      var selectedValueLayerName = GetSelectedLayer(utilizationLayersListBox);
128      var selectedVectorLayerName = GetSelectedLayer(transportLayersListBox);
129
130      // clear everything
131      Clear();
132      FillQualityInformation();
133      FillValueLayersListBox();
134      FillVectorLayersListBox();
135
136      // restore selection if possible
137      // when we couldn't restore the value or vector layer then there has been a larger change (unexpected) and we need to reset the zoom level
138      // this also makes sure that the zoom level is set correctly when the solution is initially displayed
139      if (!TrySetSelectedLayer(utilizationLayersListBox, selectedValueLayerName) |
140          !TrySetSelectedLayer(transportLayersListBox, selectedVectorLayerName)) {
141        ResetZoom();
142      }
143      mapBox.Refresh();
144    }
145
146    private void FillQualityInformation() {
147      qualityLabel.Text = string.Format("Solution quality: {0:F4}", Content.Quality);
148      feedstockCostsLabel.Text = string.Format("Feedstock [Mio€/a]: {0:N1}", Content.TotalFeedstockCosts / 1E6);
149      transportCostsLabel.Text = string.Format("Transport [Mio€/a]: {0:N1}", Content.TotalTransportCosts / 1E6);
150      costPerFuelLabel.Text = string.Format("Transport Fuel Costs [€/t]: {0:N1}", Content.TotalCosts / Content.TotalTransportFuelAmount);
151    }
152
153
154    private void FillValueLayersListBox() {
155      // we only add items for layers that we are allowed to change (layers that correspond to a vector in the representation)
156      // add utilizations for all feedstock
157      utilizationLayersListBox.BeginUpdate();
158      foreach (var pair in Content.UtilizationVectors) {
159        var newItem = new ListBoxItem<double[]> {
160          Color = Color.White,
161          Text = pair.Item1,
162          Value = pair.Item2
163        };
164        utilizationLayersListBox.Items.Add(newItem);
165      }
166      utilizationLayersListBox.EndUpdate();
167    }
168
169    private void FillVectorLayersListBox() {
170      // we only add items for vectors that we are allowed to change
171      // add transport vector layers for all products
172      transportLayersListBox.BeginUpdate();
173      int i = 0;
174      int n = Content.TransportVectors.Count();
175      foreach (var pair in Content.TransportVectors) {
176        var newItem = new ListBoxItem<int[]> {
177          Color = vectorColorBlend.GetColor(1f * i++ / (float)n),
178          Text = pair.Item1,
179          Value = pair.Item2
180        };
181        transportLayersListBox.Items.Add(newItem);
182      }
183      transportLayersListBox.EndUpdate();
184    }
185
186    private void UpdateMapLayers() {
187      mapBox.Map.Layers.Clear();
188      AddSelectedValueLayer(); // values first ...
189      AddSelectedVectorLayer(); // ... then vectors (because they need to be drawn last)
190      AddWorstTransportsVectorLayer();
191      mapBox.Refresh();
192    }
193
194    private void AddWorstTransportsVectorLayer() {
195      var geom = Content.Geometry;
196      var locs = Content.LocationNames;
197      var selectedProduct = GetSelectedLayer(transportLayersListBox);
198      if (selectedProduct == string.Empty) return;
199      int[] worstTransports = new int[locs.Length];
200      for (int i = 0; i < worstTransports.Length; i++) {
201        worstTransports[i] = -1;
202      }
203      foreach (var transport in CalcNonDominatedTransports()) {
204        if (transport.Product != selectedProduct) continue;
205        worstTransports[transport.SrcRegionIdx] = transport.DestRegionIdx;
206      }
207
208      var allTransports = Content.Transports.Where(t => t.Product == selectedProduct).ToArray();
209
210      // for all regions find the matching transport (if there is one)
211      var transportAmounts = locs
212        .Select(loc => allTransports.FirstOrDefault(t => t.SrcRegion == loc))
213        .Select(trans => trans == null ? 0.0 : trans.Amount)
214        .ToArray();
215
216      var generator = new LazyVectorLayerGenerator(geom, locs, worstTransports, transportAmounts, Color.Red, "Worst Transports");
217
218      mapBox.Map.Layers.Add(generator.Layer);
219    }
220
221
222    private void AddSelectedVectorLayer() {
223      var selectedVectorLayerItem = transportLayersListBox.SelectedItem as ListBoxItem<int[]>;
224      if (selectedVectorLayerItem == null) return;
225
226      var selectedProduct = GetSelectedLayer(transportLayersListBox);
227
228      var geom = Content.Geometry;
229      var locs = Content.LocationNames;
230
231      var allTranports = Content.Transports.Where(t => t.Product == selectedProduct).ToArray();
232
233      // foreach location find the transport amounts (if there is any)
234      var amounts = locs
235        .Select(loc => allTranports.FirstOrDefault(trans => trans.SrcRegion == loc))
236        .Select(trans => trans == null ? 0.0 : trans.Amount)
237        .ToArray();
238
239      var generator = new LazyVectorLayerGenerator(geom, locs, selectedVectorLayerItem.Value, amounts, selectedVectorLayerItem.Color, "Transports");
240
241      mapBox.Map.Layers.Add(generator.Layer);
242    }
243
244    private void AddSelectedValueLayer() {
245      var selectedValueLayerItem = utilizationLayersListBox.SelectedItem as ListBoxItem<double[]>;
246      if (selectedValueLayerItem == null) return;
247
248      var geom = Content.Geometry;
249      var locs = Content.LocationNames;
250
251      var generator = new LazyValueLayerGenerator(geom, locs, selectedValueLayerItem.Value, "Utilizations");
252
253      mapBox.Map.Layers.Add(generator.Layer);
254    }
255
256
257    // clicking on the map changes the underlying solution and causes a reevaluation
258    // this is called by the mouse-up event handler
259    private async void DispatchMouseUp(string startRegion, string endRegion, Tuple<string, string> arrow, Point mousePos) {
260      // some operations on the content might fail ... show an error dialog in this case
261      try {
262        if (arrow != null && startRegion == endRegion) {
263          // an vector was clicked => remove it
264          Content.SetTransport(GetSelectedLayer(transportLayersListBox), arrow.Item1, arrow.Item1);
265          // remove the vector
266        } else if (startRegion != string.Empty && endRegion == startRegion &&
267                   Util.Distance(startPos, mousePos) < 4) {
268          // no vector but
269          // region was clicked (no dragging) => update utilization
270          var currentUtil = Content.GetUtilization(GetSelectedLayer(utilizationLayersListBox), endRegion);
271          var newUtil = await ShowTrackBar(new Point(mousePos.X - 10, mousePos.Y - 10), currentUtil);
272          Content.SetUtilization(GetSelectedLayer(utilizationLayersListBox), endRegion, newUtil);
273        } else if (startRegion != string.Empty && endRegion != string.Empty) {
274          // we dragged from one region to another (possibly the same region
275          Content.SetTransport(GetSelectedLayer(transportLayersListBox), startRegion, endRegion);
276        }
277      } catch (SystemException e) {
278        var dialog = new ErrorDialog(e);
279        dialog.ShowDialog(this);
280      }
281    }
282
283    public void OptimizeTransports() {
284      // FullLocalSearchForTransports();
285      // NearestPlantLocalSearchForTransports();
286      PruneLongAndSmallTransports();
287    }
288
289    private IEnumerable<BioBoostCompoundSolution.TransportInfo> CalcNonDominatedTransports() {
290      var transports = Content.Transports.ToArray();
291      var transportDistances = transports.Select(t => t.Distance).ToArray();
292      var transportAmounts = transports.Select(t => t.Amount).ToArray();
293
294      for (int i = 0; i < transports.Length; i++) {
295        if (!IsDominated(i, transportDistances, transportAmounts)) {
296          yield return transports[i];
297        }
298      }
299    }
300
301    private void PruneLongAndSmallTransports() {
302
303      var nonDominatedTransports = CalcNonDominatedTransports().ToArray();
304
305      var oldQ = Content.Quality;
306      var curQ = Content.Quality;
307      var curContent = (BioBoostCompoundSolution)Content.Clone();
308      foreach (var transport in nonDominatedTransports) {
309        curContent.SetTransport(transport.Product, transport.SrcRegionIdx, transport.SrcRegionIdx);
310      }
311      curQ = curContent.Quality;
312
313      SetStatusInfo(string.Format("Deleted {0} transports. Total quality improvement: {1:F4}", nonDominatedTransports.Length,
314        curQ - Content.Quality));
315      Content.UpdateTo(curContent);
316    }
317
318    private bool IsDominated(int idx, double[] transportDistances, double[] transportAmounts) {
319      // check if there is any other transport that has a larger distance and a smaller amount
320      var dominatingIdx =
321        Enumerable.Range(0, transportDistances.Length)
322        .Where((otherIdx) => idx != otherIdx)
323        .Where((otherIdx) => transportDistances[otherIdx] > transportDistances[idx])
324        .Where((otherIdx) => transportAmounts[otherIdx] < transportAmounts[idx]);
325      return (dominatingIdx.Any());
326    }
327
328    /*
329    private void NearestPlantLocalSearchForTransports() {
330      // for each transport check if we can reroute it to a nearer plant (with minor loss in quality 1%)
331      const double maxRelativeQualityChange = -0.001;
332      var oldQ = Content.Quality;
333      var curQ = Content.Quality;
334      var curContent = (BioBoostCompoundSolution)Content.Clone();
335      // TODO: assumes that there is only one distance matrix and all products are transported using this matrix!!
336      Debug.Assert(Content.ProblemDataReference.Parameters.Count(p => p.Name.EndsWith("DistanceMatrix")) == 1);
337      var distanceMatrix = (DistanceMatrix)Content.ProblemDataReference.Parameters.First(p => p.Name.EndsWith("DistanceMatrix")).ActualValue;
338
339      // for each product a list of possible targets (possible targets stay fixed in optimization)
340      var curTransportTargets = curContent.TransportVectors
341        .Select(kvp => new KeyValuePair<string, int[]>(kvp.Item1, kvp.Item2.Distinct().Where(i => i >= 0).OrderBy(t => t).ToArray()))
342        .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
343
344      // for each product and for each region a list of the max 5 nearest plants
345      var nearestPlantIdx = new Dictionary<string, int[][]>();
346      foreach (var product in curTransportTargets.Keys) {
347        nearestPlantIdx.Add(product, new int[curContent.LocationNames.Length][]);
348        var nIdx = nearestPlantIdx[product];
349        for (int src = 0; src < nIdx.Length; src++) {
350          var plantsByDistance =
351            curTransportTargets[product].OrderBy(possibleTarget => distanceMatrix[src, possibleTarget]);
352          var nPlants = Math.Min(plantsByDistance.Count(), 3);
353          nIdx[src] = plantsByDistance.Take(nPlants).ToArray();
354        }
355      }
356
357      // generate possible changes
358      // either change the transport to the nearest existing plant ...
359      // ... or if the transport is within the same region then to the second nearest plant (reduces plants)
360      var changes = new List<Tuple<string, int, int>>(); // product x source x dest
361      foreach (var kvp in curContent.TransportVectors) {
362        var prod = kvp.Item1;
363        var curDest = kvp.Item2;
364        for (int src = 0; src < curDest.Length; src++) {
365          var dest = curDest[src];
366          if (dest == -1)
367            continue; // don't allow transports if the current transport target is -1
368          foreach (var alternativeDest in nearestPlantIdx[prod][src]) {
369            if (alternativeDest != dest)
370              changes.Add(Tuple.Create(prod, src, alternativeDest));
371          }
372        }
373      }
374
375      var improvements = new List<double>();
376      // evalute changes (full evaluation)
377      for (int i = 0; i < changes.Count; i++) {
378        // apply each change in turn to a clone of the solution and check the change in quality
379        var change = changes[i];
380        var tmpContent = (BioBoostCompoundSolution)curContent.Clone();
381        tmpContent.SetTransport(change.Item1, change.Item2, change.Item3); // product, src, dest
382        improvements.Add(tmpContent.Quality - curQ); // if quality after change is worse than current quality improvement is negative
383      }
384
385      var sortedChanges =
386        changes.Zip(improvements, (change, improvement) => new { change, improvement })
387        .OrderByDescending(p => p.improvement) // positive improvements first
388        .ToArray();
389
390      int changeCount = 0;
391      int changeIdx = 0;
392
393      bool changed = true; // has there been a change in this iteration
394      // while there is a total deterioation not worse than -1%
395      var totalChange = 0.0;
396      while (changeIdx < sortedChanges.Length && changed && totalChange > (oldQ * maxRelativeQualityChange)) {
397        changed = false;
398        var change = sortedChanges[changeIdx].change;
399        var improvement = sortedChanges[changeIdx].improvement;
400        // and each step is not worse than -1%
401        if (improvement > curQ * maxRelativeQualityChange) {
402          changeCount++;
403          totalChange += improvement;
404          curContent.SetTransport(change.Item1, change.Item2, change.Item3); // product, src, dest
405          curQ = curContent.Quality; // update current quality
406          changed = true;
407        }
408        changeIdx++;
409      }
410
411      Content.UpdateTo(curContent);
412      SetStatusInfo(string.Format("Changed {0} transports. Total quality improvement: {1:F4}", changeCount,
413        Content.Quality - oldQ));
414    }
415    */
416
417    /*
418    private void FullLocalSearchForTransports() {
419      // for each transport check if we can reroute the transport to any other location (which already is a transport target for this product type)
420      // local search using best improvement
421      double oldQ;
422      var curQ = Content.Quality;
423      var curContent = (BioBoostCompoundSolution)Content.Clone();
424
425      // for each product a list of possible targets (possible targets stay fixed in optimization)
426      var curTransportTargets = curContent.TransportVectors
427        .Select(kvp => new KeyValuePair<string, int[]>(kvp.Item1, kvp.Item2.Distinct().OrderBy(t => t).ToArray()))
428        .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
429
430      int changeCount = 0;
431      do {
432        // store current quality (to check if we found an improvement)
433        oldQ = curQ;
434
435        // generate possible changes
436        var changes = new List<Tuple<string, int, int>>(); // product x source x dest
437        foreach (var kvp in curContent.TransportVectors) {
438          var prod = kvp.Item1;
439          var curDest = kvp.Item2;
440          foreach (var alternativeDest in curTransportTargets[prod]) {
441            if (alternativeDest == -1) continue; // don't allow transport to new locations (without plants)
442            for (int src = 0; src < curDest.Length; src++) {
443              var dest = curDest[src];
444              if (dest == -1) continue; // don't allow transports if the current transport target is -1 (TODO: check with erik)
445
446              if (dest != alternativeDest) {
447                changes.Add(Tuple.Create(prod, src, alternativeDest));
448              }
449            }
450          }
451        }
452
453        // evalute changes (full evaluation)
454        var bestImprovement = double.NegativeInfinity;
455        var bestChangeIdx = -1;
456        for (int i = 0; i < changes.Count; i++) {
457          // apply each change in turn to a clone of the solution and check the change in quality
458          var change = changes[i];
459          var tmpContent = (BioBoostCompoundSolution)curContent.Clone();
460          tmpContent.SetTransport(change.Item1, change.Item2, change.Item3); // product, src, dest
461          var improvement = tmpContent.Quality - curQ;
462          if (improvement > bestImprovement) {
463            bestImprovement = improvement;
464            bestChangeIdx = i;
465          }
466        }
467        if (bestImprovement > 0) {
468          changeCount++;
469          var bestChange = changes[bestChangeIdx];
470          curContent.SetTransport(bestChange.Item1, bestChange.Item2, bestChange.Item3); // product, src, dest
471          curQ = curContent.Quality; // update current quality
472        }
473
474        // apply best change
475      } while (oldQ < curQ);
476
477      SetStatusInfo(string.Format("Changed {0} transports. Total quality improvement: {1:F4}", changeCount,
478        curQ - Content.Quality));
479      Content.UpdateTo(curContent);
480    }*/
481
482
483    public void OptimizeUtilizations() {
484      double oldQ;
485      var curQ = Content.Quality;
486      var curContent = (BioBoostCompoundSolution)Content.Clone();
487      int changeCount = 0;
488      do {
489        oldQ = curQ;
490
491        // for each product and region check if the quality increases if we set the utilization to zero
492        // local search using first improvement
493        var changes = new List<Tuple<string, int, double>>(); // product x region x utilization
494        foreach (var pair in curContent.UtilizationVectors) {
495          var prod = pair.Item1;
496          var util = pair.Item2;
497          for (int regIdx = 0; regIdx < util.Length; regIdx++) {
498            if (util[regIdx] > 0) {
499              changes.Add(Tuple.Create(prod, regIdx, 0.0));
500            }
501          }
502        }
503
504        var bestImprovement = double.NegativeInfinity;
505        var bestChange = changes.First();
506        foreach (var change in changes) {
507          var tmpContent = (BioBoostCompoundSolution)curContent.Clone();
508          tmpContent.SetUtilization(change.Item1, change.Item2, change.Item3);
509          var newQ = tmpContent.Quality;
510          var improvement = newQ - curQ; // positive improvements (new quality is higher than current quality
511          if (improvement > bestImprovement) {
512            bestImprovement = improvement;
513            bestChange = change;
514          }
515        }
516
517        // apply if we found a possible improvement
518        if (bestImprovement > 0) {
519          curContent.SetUtilization(bestChange.Item1, bestChange.Item2, bestChange.Item3);
520          curQ = curContent.Quality;
521          changeCount++;
522        }
523      } while (oldQ < curQ);
524
525      SetStatusInfo(string.Format("Changed {0} utilizations. Total quality improvement: {1:F4}", changeCount,
526        curQ - Content.Quality));
527      Content.UpdateTo(curContent);
528    }
529
530    private void SetStatusInfo(string message) {
531      if (InvokeRequired) {
532        Invoke((Action<string>)SetStatusInfo, message);
533      } else {
534        toolStripStatusLabel.Text = message;
535      }
536    }
537
538    private void UpdateTransportInfo(Tuple<string, string> vector) {
539      transportInfoLabel.Text = string.Empty; // clear
540      if (vector != null) {
541        var transportProduct = GetSelectedLayer(transportLayersListBox);
542        var transportInfo =
543          Content.Transports.FirstOrDefault(
544            info => info.Product == transportProduct && info.SrcRegion == vector.Item1 && info.DestRegion == vector.Item2);
545        if (transportInfo != null) {
546
547          var sb = new StringBuilder();
548          sb.AppendFormat("{0} -> {1}", transportInfo.SrcRegion, transportInfo.DestRegion).AppendLine();
549          sb.AppendFormat("{0} (Mode: {1})", transportInfo.Product, transportInfo.Mode).AppendLine();
550          sb.AppendFormat("{0:N0} kt km", transportInfo.Distance * transportInfo.Amount / 1000.0).AppendLine();
551          sb.AppendFormat("{0:N1} kt {1:N0} km", transportInfo.Amount / 1000.0, transportInfo.Distance);
552          transportInfoLabel.Text = sb.ToString();
553        }
554      }
555    }
556    #endregion
557
558
559
560    #region Event Handlers (child controls)
561    private void vectorLayerNamesListBox_SelectedIndexChanged(object sender, EventArgs e) {
562      UpdateMapLayers();
563    }
564
565    private void valueLayerNamesListBox_SelectedIndexChanged(object sender, EventArgs e) {
566      UpdateMapLayers();
567    }
568
569    private void optimizeTransportsButton_Click(object sender, EventArgs e) {
570      using (BackgroundWorker bg = new BackgroundWorker()) {
571        MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>().AddOperationProgressToView(this, "Optimizing Transports...");
572        bg.DoWork += (o, a) => OptimizeTransports();
573        bg.RunWorkerCompleted += (o, a) => MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>().RemoveOperationProgressFromView(this);
574        bg.RunWorkerAsync();
575      }
576    }
577
578    private void optimizeUtilizationsButton_Click(object sender, EventArgs e) {
579      using (BackgroundWorker bg = new BackgroundWorker()) {
580        MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>().AddOperationProgressToView(this, "Optimizing Utilizations...");
581        bg.DoWork += (o, a) => OptimizeUtilizations();
582        bg.RunWorkerCompleted += (o, a) => MainFormManager.GetMainForm<MainForm.WindowsForms.MainForm>().RemoveOperationProgressFromView(this);
583        bg.RunWorkerAsync();
584      }
585    }
586    #endregion
587
588    #region mouse events
589    // TODO: it should be possible to allow panning and drawing arrows at the same time by implementing a CustomTool
590    private void mapBox_MouseMove(Coordinate worldPos, MouseEventArgs imagePos) {
591      var nuts_id = GetRegionAtWorldPos(worldPos);
592      var nuts_name = GetRegionDescription(nuts_id);
593      regionNameLabel.Text = nuts_id;
594      regionDescriptionLabel.Text = nuts_name;
595      mapBox.Cursor = String.IsNullOrEmpty(nuts_id) ? Cursors.Default : Cursors.Hand;
596      var arrow = GetArrowAtWorldPos(worldPos);
597      UpdateTransportInfo(arrow);
598    }
599
600    private Point startPos = new Point(0, 0);
601    private Coordinate startWorldPos;
602    private void mapBox_MouseDown(Coordinate worldPos, MouseEventArgs mouseEventArgs) {
603      if (mouseEventArgs.Button == MouseButtons.Left) {
604        startPos = mouseEventArgs.Location;
605        startWorldPos = worldPos;
606      }
607    }
608
609    private void mapBox_MouseUp(Coordinate endWorldPos, MouseEventArgs mousePos) {
610      if (mousePos.Button == MouseButtons.Left) {
611        var arrow = GetArrowAtWorldPos(startWorldPos);
612
613        var startRegion = GetRegionAtWorldPos(startWorldPos);
614        var endRegion = GetRegionAtWorldPos(endWorldPos);
615
616        DispatchMouseUp(startRegion, endRegion, arrow, mousePos.Location);
617      }
618    }
619
620    // custom drawing method for vector list box (for coloring of layers)
621    private void vectorLayerNamesListBox_DrawItem(object sender, DrawItemEventArgs e) {
622      var item = transportLayersListBox.Items[e.Index];
623      var listBoxItem = item as ListBoxItem<int[]>;
624      var backColor = listBoxItem.Color;
625      var foreColor = Color.White;
626
627      using (var backBrush = new SolidBrush(backColor)) {
628        e.Graphics.FillRectangle(backBrush, e.Bounds);
629      }
630      using (var foreBrush = new SolidBrush(foreColor)) {
631        e.Graphics.DrawString(
632          listBoxItem.Text,
633          transportLayersListBox.Font, foreBrush, e.Bounds);
634      }
635    }
636    #endregion
637
638    #region drag-and-drop of problem data
639
640    private bool dropAllowed = false;
641    protected override void OnDragDrop(DragEventArgs e) {
642      if (e.Effect != DragDropEffects.None) {
643        var problemData = (BioBoostProblemData)e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
644        Content.ProblemDataReference = (BioBoostProblemData)problemData.Clone();
645      }
646    }
647
648    protected override void OnDragEnter(DragEventArgs e) {
649      var problemData = (BioBoostProblemData)e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
650      dropAllowed = !ReadOnly && !Locked && problemData != null;
651    }
652
653    protected override void OnDragOver(DragEventArgs e) {
654      e.Effect = dropAllowed ?
655        DragDropEffects.Copy :
656        DragDropEffects.None;
657    }
658
659    #endregion
660
661
662    #region Auxiliar Methods
663    private Task<double> ShowTrackBar(Point location, double value) {
664
665      var wh = new AutoResetEvent(false);
666      var tb = new TrackBar {
667        TickStyle = TickStyle.None,
668        Minimum = 0,
669        Maximum = 100,
670        Value = (int)Math.Round(value * 100),
671        Location = location
672      };
673      var label = new Label();
674      label.Location = new Point(location.X, location.Y - label.Height);
675      label.Text = string.Format("Utilization: {0:F2}", value);
676      label.Width = tb.Width;
677
678      mapBox.Controls.Add(tb);
679      mapBox.Controls.Add(label);
680
681      tb.Focus();
682      tb.ValueChanged += (sender, args) => {
683        value = tb.Value / 100.0;
684        label.Text = string.Format("Utilization: {0:F2}", value);
685      };
686      tb.LostFocus += (sender, args) => {
687        mapBox.Controls.Remove(tb);
688        mapBox.Controls.Remove(label);
689        wh.Set();
690      };
691
692
693      return Task.Run(() => {
694        wh.WaitOne();
695        wh.Dispose();
696        return value;
697      });
698    }
699
700    // returns the NUTS_ID of the region if it has been hit
701    private string GetRegionAtWorldPos(Coordinate loc) {
702      var ds = new FeatureDataSet();
703      var layer = mapBox.Map.Layers.OfType<VectorLayer>().SingleOrDefault(l => l.LayerName == "Utilizations");
704      if (layer == null) return string.Empty;
705
706      layer.ExecuteIntersectionQuery(new NetTopologySuite.Geometries.Point(loc), ds);
707      return (from FeatureDataRow row in ds.Tables[0]
708              select row["NUTS_ID"] as string).FirstOrDefault() ?? string.Empty; // default is string.Empty
709    }
710
711    // returns the source and target NUTS_ID (Tuple(src, trgt)) for an vector if it has been hit
712    private Tuple<string, string> GetArrowAtWorldPos(Coordinate loc) {
713      var ds = new FeatureDataSet();
714      var layer = mapBox.Map.Layers.OfType<VectorLayer>().SingleOrDefault(l => l.LayerName == "Transports");
715      if (layer == null) return null;
716
717      var rect = new Coordinate[5];
718      var delta = mapBox.Map.PixelWidth * 2; // for bounding box arount the click point
719      rect[0] = new Coordinate(loc.X - delta, loc.Y - delta);
720      rect[1] = new Coordinate(loc.X + delta, loc.Y - delta);
721      rect[3] = new Coordinate(loc.X + delta, loc.Y + delta);
722      rect[2] = new Coordinate(loc.X - delta, loc.Y + delta);
723      rect[4] = new Coordinate(loc.X - delta, loc.Y - delta);
724      layer.ExecuteIntersectionQuery(new NetTopologySuite.Geometries.Polygon(new LinearRing(rect)), ds);
725
726      var arrow = (from FeatureDataRow row in ds.Tables[0]
727                   select Tuple.Create((string)row["NUTS_ID_SRC"], (string)row["NUTS_ID_DEST"])
728        ).FirstOrDefault();
729      return arrow;
730    }
731
732
733    private string GetRegionDescription(string regionName) {
734      if (regionName != null && Content.Geometry.Features.Columns.Contains("name")) {
735        var row = Content.Geometry.Features.Rows.Cast<FeatureDataRow>().FirstOrDefault(r => (string)r["NUTS_ID"] == regionName);
736        if (row != null)
737          return row["name"].ToString();
738      }
739      return string.Empty;
740    }
741
742
743    private void ResetZoom() {
744      if (mapBox.Map.Layers.Count > 0)
745        mapBox.Map.ZoomToExtents();
746      // mapBox.ActiveTool = MapBox.Tools.DrawLine;
747      mapBox.Refresh();
748    }
749
750
751    private string GetSelectedLayer(ListBox listBox) {
752      return
753      listBox.SelectedItem == null ?
754        string.Empty
755        : listBox.SelectedItem.ToString();
756    }
757
758
759    private bool TrySetSelectedLayer(ListBox listBox, string text) {
760      bool success = false;
761      if (string.IsNullOrEmpty(text)) {
762        listBox.SelectedIndex = 0;
763      } else {
764        int selectedIndex = 0;
765        for (int i = 0; i < listBox.Items.Count; i++) {
766          if (listBox.Items[i].ToString() == text) {
767            selectedIndex = i;
768            success = true;
769            break;
770          }
771        }
772        listBox.SelectedIndex = selectedIndex;
773      }
774      return success;
775    }
776    #endregion
777
778    // only necessary for drawing of vector layer elements with color
779    private class ListBoxItem<T> {
780      public Color Color { get; set; }
781      public string Text { get; set; }
782      public T Value { get; set; }
783
784      public override string ToString() {
785        return Text;
786      }
787    }
788  }
789}
Note: See TracBrowser for help on using the repository browser.