Free cookie consent management tool by TermsFeed Policy Generator

source: addons/HeuristicLab.Problems.BioBoost/HeuristicLab.Problems.BioBoost.Views/3.3/BioBoostCompoundSolutionEditor.cs @ 17460

Last change on this file since 17460 was 16447, checked in by jkarder, 6 years ago

#2845: adapted BioBoost addon

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