#region License Information
/* HeuristicLab
* Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using GeoAPI.Geometries;
using HeuristicLab.BioBoost.ProblemDescription;
using HeuristicLab.BioBoost.Representation;
using HeuristicLab.BioBoost.Utils;
using HeuristicLab.BioBoost.Views.MapViews;
using HeuristicLab.Core.Views;
using HeuristicLab.MainForm;
using HeuristicLab.PluginInfrastructure;
using NetTopologySuite.Geometries;
using SharpMap.Data;
using SharpMap.Forms;
using SharpMap.Layers;
using SharpMap.Rendering.Decoration;
using SharpMap.Rendering.Decoration.ScaleBar;
using Point = System.Drawing.Point;
namespace HeuristicLab.BioBoost.Views {
[Content(typeof(BioBoostCompoundSolution))]
public partial class BioBoostCompoundSolutionEditor : ItemView {
private readonly GradientLegend utilizationLegend;
private SharpMap.Rendering.Thematics.ColorBlend vectorColorBlend =
new SharpMap.Rendering.Thematics.ColorBlend(new[] {
Color.FromArgb(0, 0, 255),
Color.FromArgb(128, 0, 128),
Color.FromArgb(255, 0, 0),
}, new[] { 0f, .5f, 1f });
public const string AllRegionsName = "All";
public new BioBoostCompoundSolution Content {
get { return (BioBoostCompoundSolution)base.Content; }
set { base.Content = value; }
}
public BioBoostCompoundSolutionEditor() {
InitializeComponent();
mapBox.Map.Decorations.Add(new ScaleBar {
Anchor = MapDecorationAnchor.RightBottom,
BarColor1 = Color.White,
BarColor2 = Color.Black,
BarStyle = ScaleBarStyle.Meridian,
MapUnit = (int)Unit.Degree,
BarUnit = (int)Unit.Kilometer,
});
utilizationLegend = new GradientLegend();
mapBox.Map.Decorations.Add(utilizationLegend);
mapBox.Map.Decorations.Add(new NorthArrow());
mapBox.ActiveTool = MapBox.Tools.None;
}
protected override void SetEnabledStateOfControls() {
base.SetEnabledStateOfControls();
ReadOnly = Locked || Content == null;
mapBox.Enabled = !Locked && Content != null;
transportLayersListBox.Enabled = !Locked && Content != null;
utilizationLayersListBox.Enabled = !Locked && Content != null;
}
#region content events
protected override void OnContentChanged() {
if (InvokeRequired) {
Invoke((Action)OnContentChanged);
} else {
base.OnContentChanged();
if (Content == null) {
Clear();
} else {
Update();
}
}
}
protected override void DeregisterContentEvents() {
Content.SolutionChanged -= SolutionChanged;
base.DeregisterContentEvents();
}
protected override void RegisterContentEvents() {
base.RegisterContentEvents();
Content.SolutionChanged += SolutionChanged;
}
private void SolutionChanged(object sender, EventArgs e) {
if (InvokeRequired) {
Invoke(new EventHandler(SolutionChanged), sender, e);
} else {
Update();
}
}
#endregion
#region Main Logic
private void Clear() {
qualityLabel.Text = string.Empty;
feedstockCostsLabel.Text = string.Empty;
transportCostsLabel.Text = string.Empty;
utilizationLayersListBox.Items.Clear();
transportLayersListBox.Items.Clear();
mapBox.Map.Layers.Clear(); // only two layers are active at any time (representing the layers selected in the list boxes on the left)
}
private void Update() {
// store currently selected items
var selectedValueLayerName = GetSelectedLayer(utilizationLayersListBox);
var selectedVectorLayerName = GetSelectedLayer(transportLayersListBox);
// clear everything
Clear();
FillQualityInformation();
FillValueLayersListBox();
FillVectorLayersListBox();
// restore selection if possible
// 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
// this also makes sure that the zoom level is set correctly when the solution is initially displayed
if (!TrySetSelectedLayer(utilizationLayersListBox, selectedValueLayerName) |
!TrySetSelectedLayer(transportLayersListBox, selectedVectorLayerName)) {
ResetZoom();
}
mapBox.Refresh();
}
private void FillQualityInformation() {
qualityLabel.Text = string.Format("Solution quality: {0:F4}", Content.Quality);
feedstockCostsLabel.Text = string.Format("Feedstock [Mio€/a]: {0:N1}", Content.TotalFeedstockCosts / 1E6);
transportCostsLabel.Text = string.Format("Transport [Mio€/a]: {0:N1}", Content.TotalTransportCosts / 1E6);
costPerFuelLabel.Text = string.Format("Transport Fuel Costs [€/t]: {0:N1}", Content.TotalCosts / Content.TotalTransportFuelAmount);
}
private void FillValueLayersListBox() {
// we only add items for layers that we are allowed to change (layers that correspond to a vector in the representation)
// add utilizations for all feedstock
utilizationLayersListBox.BeginUpdate();
foreach (var pair in Content.UtilizationVectors) {
var newItem = new ListBoxItem {
Color = Color.White,
Text = pair.Item1,
Value = pair.Item2
};
utilizationLayersListBox.Items.Add(newItem);
}
utilizationLayersListBox.EndUpdate();
}
private void FillVectorLayersListBox() {
// we only add items for vectors that we are allowed to change
// add transport vector layers for all products
transportLayersListBox.BeginUpdate();
int i = 0;
int n = Content.TransportVectors.Count();
foreach (var pair in Content.TransportVectors) {
var newItem = new ListBoxItem {
Color = vectorColorBlend.GetColor(1f * i++ / (float)n),
Text = pair.Item1,
Value = pair.Item2
};
transportLayersListBox.Items.Add(newItem);
}
transportLayersListBox.EndUpdate();
}
private void UpdateMapLayers() {
mapBox.Map.Layers.Clear();
AddSelectedValueLayer(); // values first ...
AddSelectedVectorLayer(); // ... then vectors (because they need to be drawn last)
AddWorstTransportsVectorLayer();
mapBox.Refresh();
}
private void AddWorstTransportsVectorLayer() {
var geom = Content.Geometry;
var locs = Content.LocationNames;
var selectedProduct = GetSelectedLayer(transportLayersListBox);
if (selectedProduct == string.Empty) return;
int[] worstTransports = new int[locs.Length];
for (int i = 0; i < worstTransports.Length; i++) {
worstTransports[i] = -1;
}
foreach (var transport in CalcNonDominatedTransports()) {
if (transport.Product != selectedProduct) continue;
worstTransports[transport.SrcRegionIdx] = transport.DestRegionIdx;
}
var allTransports = Content.Transports.Where(t => t.Product == selectedProduct).ToArray();
// for all regions find the matching transport (if there is one)
var transportAmounts = locs
.Select(loc => allTransports.FirstOrDefault(t => t.SrcRegion == loc))
.Select(trans => trans == null ? 0.0 : trans.Amount)
.ToArray();
var generator = new LazyVectorLayerGenerator(geom, locs, worstTransports, transportAmounts, Color.Red, "Worst Transports");
mapBox.Map.Layers.Add(generator.Layer);
}
private void AddSelectedVectorLayer() {
var selectedVectorLayerItem = transportLayersListBox.SelectedItem as ListBoxItem;
if (selectedVectorLayerItem == null) return;
var selectedProduct = GetSelectedLayer(transportLayersListBox);
var geom = Content.Geometry;
var locs = Content.LocationNames;
var allTranports = Content.Transports.Where(t => t.Product == selectedProduct).ToArray();
// foreach location find the transport amounts (if there is any)
var amounts = locs
.Select(loc => allTranports.FirstOrDefault(trans => trans.SrcRegion == loc))
.Select(trans => trans == null ? 0.0 : trans.Amount)
.ToArray();
var generator = new LazyVectorLayerGenerator(geom, locs, selectedVectorLayerItem.Value, amounts, selectedVectorLayerItem.Color, "Transports");
mapBox.Map.Layers.Add(generator.Layer);
}
private void AddSelectedValueLayer() {
var selectedValueLayerItem = utilizationLayersListBox.SelectedItem as ListBoxItem;
if (selectedValueLayerItem == null) return;
var geom = Content.Geometry;
var locs = Content.LocationNames;
var generator = new LazyValueLayerGenerator(geom, locs, selectedValueLayerItem.Value, "Utilizations");
mapBox.Map.Layers.Add(generator.Layer);
}
// clicking on the map changes the underlying solution and causes a reevaluation
// this is called by the mouse-up event handler
private async void DispatchMouseUp(string startRegion, string endRegion, Tuple arrow, Point mousePos) {
// some operations on the content might fail ... show an error dialog in this case
try {
if (arrow != null && startRegion == endRegion) {
// an vector was clicked => remove it
Content.SetTransport(GetSelectedLayer(transportLayersListBox), arrow.Item1, arrow.Item1);
// remove the vector
} else if (startRegion != string.Empty && endRegion == startRegion &&
Util.Distance(startPos, mousePos) < 4) {
// no vector but
// region was clicked (no dragging) => update utilization
var currentUtil = Content.GetUtilization(GetSelectedLayer(utilizationLayersListBox), endRegion);
var newUtil = await ShowTrackBar(new Point(mousePos.X - 10, mousePos.Y - 10), currentUtil);
Content.SetUtilization(GetSelectedLayer(utilizationLayersListBox), endRegion, newUtil);
} else if (startRegion != string.Empty && endRegion != string.Empty) {
// we dragged from one region to another (possibly the same region
Content.SetTransport(GetSelectedLayer(transportLayersListBox), startRegion, endRegion);
}
} catch (SystemException e) {
var dialog = new ErrorDialog(e);
dialog.ShowDialog(this);
}
}
public void OptimizeTransports() {
// FullLocalSearchForTransports();
// NearestPlantLocalSearchForTransports();
PruneLongAndSmallTransports();
}
private IEnumerable CalcNonDominatedTransports() {
var transports = Content.Transports.ToArray();
var transportDistances = transports.Select(t => t.Distance).ToArray();
var transportAmounts = transports.Select(t => t.Amount).ToArray();
for (int i = 0; i < transports.Length; i++) {
if (!IsDominated(i, transportDistances, transportAmounts)) {
yield return transports[i];
}
}
}
private void PruneLongAndSmallTransports() {
var nonDominatedTransports = CalcNonDominatedTransports().ToArray();
var oldQ = Content.Quality;
var curQ = Content.Quality;
var curContent = (BioBoostCompoundSolution)Content.Clone();
foreach (var transport in nonDominatedTransports) {
curContent.SetTransport(transport.Product, transport.SrcRegionIdx, transport.SrcRegionIdx);
}
curQ = curContent.Quality;
SetStatusInfo(string.Format("Deleted {0} transports. Total quality improvement: {1:F4}", nonDominatedTransports.Length,
curQ - Content.Quality));
Content.UpdateTo(curContent);
}
private bool IsDominated(int idx, double[] transportDistances, double[] transportAmounts) {
// check if there is any other transport that has a larger distance and a smaller amount
var dominatingIdx =
Enumerable.Range(0, transportDistances.Length)
.Where((otherIdx) => idx != otherIdx)
.Where((otherIdx) => transportDistances[otherIdx] > transportDistances[idx])
.Where((otherIdx) => transportAmounts[otherIdx] < transportAmounts[idx]);
return (dominatingIdx.Any());
}
/*
private void NearestPlantLocalSearchForTransports() {
// for each transport check if we can reroute it to a nearer plant (with minor loss in quality 1%)
const double maxRelativeQualityChange = -0.001;
var oldQ = Content.Quality;
var curQ = Content.Quality;
var curContent = (BioBoostCompoundSolution)Content.Clone();
// TODO: assumes that there is only one distance matrix and all products are transported using this matrix!!
Debug.Assert(Content.ProblemDataReference.Parameters.Count(p => p.Name.EndsWith("DistanceMatrix")) == 1);
var distanceMatrix = (DistanceMatrix)Content.ProblemDataReference.Parameters.First(p => p.Name.EndsWith("DistanceMatrix")).ActualValue;
// for each product a list of possible targets (possible targets stay fixed in optimization)
var curTransportTargets = curContent.TransportVectors
.Select(kvp => new KeyValuePair(kvp.Item1, kvp.Item2.Distinct().Where(i => i >= 0).OrderBy(t => t).ToArray()))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
// for each product and for each region a list of the max 5 nearest plants
var nearestPlantIdx = new Dictionary();
foreach (var product in curTransportTargets.Keys) {
nearestPlantIdx.Add(product, new int[curContent.LocationNames.Length][]);
var nIdx = nearestPlantIdx[product];
for (int src = 0; src < nIdx.Length; src++) {
var plantsByDistance =
curTransportTargets[product].OrderBy(possibleTarget => distanceMatrix[src, possibleTarget]);
var nPlants = Math.Min(plantsByDistance.Count(), 3);
nIdx[src] = plantsByDistance.Take(nPlants).ToArray();
}
}
// generate possible changes
// either change the transport to the nearest existing plant ...
// ... or if the transport is within the same region then to the second nearest plant (reduces plants)
var changes = new List>(); // product x source x dest
foreach (var kvp in curContent.TransportVectors) {
var prod = kvp.Item1;
var curDest = kvp.Item2;
for (int src = 0; src < curDest.Length; src++) {
var dest = curDest[src];
if (dest == -1)
continue; // don't allow transports if the current transport target is -1
foreach (var alternativeDest in nearestPlantIdx[prod][src]) {
if (alternativeDest != dest)
changes.Add(Tuple.Create(prod, src, alternativeDest));
}
}
}
var improvements = new List();
// evalute changes (full evaluation)
for (int i = 0; i < changes.Count; i++) {
// apply each change in turn to a clone of the solution and check the change in quality
var change = changes[i];
var tmpContent = (BioBoostCompoundSolution)curContent.Clone();
tmpContent.SetTransport(change.Item1, change.Item2, change.Item3); // product, src, dest
improvements.Add(tmpContent.Quality - curQ); // if quality after change is worse than current quality improvement is negative
}
var sortedChanges =
changes.Zip(improvements, (change, improvement) => new { change, improvement })
.OrderByDescending(p => p.improvement) // positive improvements first
.ToArray();
int changeCount = 0;
int changeIdx = 0;
bool changed = true; // has there been a change in this iteration
// while there is a total deterioation not worse than -1%
var totalChange = 0.0;
while (changeIdx < sortedChanges.Length && changed && totalChange > (oldQ * maxRelativeQualityChange)) {
changed = false;
var change = sortedChanges[changeIdx].change;
var improvement = sortedChanges[changeIdx].improvement;
// and each step is not worse than -1%
if (improvement > curQ * maxRelativeQualityChange) {
changeCount++;
totalChange += improvement;
curContent.SetTransport(change.Item1, change.Item2, change.Item3); // product, src, dest
curQ = curContent.Quality; // update current quality
changed = true;
}
changeIdx++;
}
Content.UpdateTo(curContent);
SetStatusInfo(string.Format("Changed {0} transports. Total quality improvement: {1:F4}", changeCount,
Content.Quality - oldQ));
}
*/
/*
private void FullLocalSearchForTransports() {
// for each transport check if we can reroute the transport to any other location (which already is a transport target for this product type)
// local search using best improvement
double oldQ;
var curQ = Content.Quality;
var curContent = (BioBoostCompoundSolution)Content.Clone();
// for each product a list of possible targets (possible targets stay fixed in optimization)
var curTransportTargets = curContent.TransportVectors
.Select(kvp => new KeyValuePair(kvp.Item1, kvp.Item2.Distinct().OrderBy(t => t).ToArray()))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
int changeCount = 0;
do {
// store current quality (to check if we found an improvement)
oldQ = curQ;
// generate possible changes
var changes = new List>(); // product x source x dest
foreach (var kvp in curContent.TransportVectors) {
var prod = kvp.Item1;
var curDest = kvp.Item2;
foreach (var alternativeDest in curTransportTargets[prod]) {
if (alternativeDest == -1) continue; // don't allow transport to new locations (without plants)
for (int src = 0; src < curDest.Length; src++) {
var dest = curDest[src];
if (dest == -1) continue; // don't allow transports if the current transport target is -1 (TODO: check with erik)
if (dest != alternativeDest) {
changes.Add(Tuple.Create(prod, src, alternativeDest));
}
}
}
}
// evalute changes (full evaluation)
var bestImprovement = double.NegativeInfinity;
var bestChangeIdx = -1;
for (int i = 0; i < changes.Count; i++) {
// apply each change in turn to a clone of the solution and check the change in quality
var change = changes[i];
var tmpContent = (BioBoostCompoundSolution)curContent.Clone();
tmpContent.SetTransport(change.Item1, change.Item2, change.Item3); // product, src, dest
var improvement = tmpContent.Quality - curQ;
if (improvement > bestImprovement) {
bestImprovement = improvement;
bestChangeIdx = i;
}
}
if (bestImprovement > 0) {
changeCount++;
var bestChange = changes[bestChangeIdx];
curContent.SetTransport(bestChange.Item1, bestChange.Item2, bestChange.Item3); // product, src, dest
curQ = curContent.Quality; // update current quality
}
// apply best change
} while (oldQ < curQ);
SetStatusInfo(string.Format("Changed {0} transports. Total quality improvement: {1:F4}", changeCount,
curQ - Content.Quality));
Content.UpdateTo(curContent);
}*/
public void OptimizeUtilizations() {
double oldQ;
var curQ = Content.Quality;
var curContent = (BioBoostCompoundSolution)Content.Clone();
int changeCount = 0;
do {
oldQ = curQ;
// for each product and region check if the quality increases if we set the utilization to zero
// local search using first improvement
var changes = new List>(); // product x region x utilization
foreach (var pair in curContent.UtilizationVectors) {
var prod = pair.Item1;
var util = pair.Item2;
for (int regIdx = 0; regIdx < util.Length; regIdx++) {
if (util[regIdx] > 0) {
changes.Add(Tuple.Create(prod, regIdx, 0.0));
}
}
}
var bestImprovement = double.NegativeInfinity;
var bestChange = changes.First();
foreach (var change in changes) {
var tmpContent = (BioBoostCompoundSolution)curContent.Clone();
tmpContent.SetUtilization(change.Item1, change.Item2, change.Item3);
var newQ = tmpContent.Quality;
var improvement = newQ - curQ; // positive improvements (new quality is higher than current quality
if (improvement > bestImprovement) {
bestImprovement = improvement;
bestChange = change;
}
}
// apply if we found a possible improvement
if (bestImprovement > 0) {
curContent.SetUtilization(bestChange.Item1, bestChange.Item2, bestChange.Item3);
curQ = curContent.Quality;
changeCount++;
}
} while (oldQ < curQ);
SetStatusInfo(string.Format("Changed {0} utilizations. Total quality improvement: {1:F4}", changeCount,
curQ - Content.Quality));
Content.UpdateTo(curContent);
}
private void SetStatusInfo(string message) {
if (InvokeRequired) {
Invoke((Action)SetStatusInfo, message);
} else {
toolStripStatusLabel.Text = message;
}
}
private void UpdateTransportInfo(Tuple vector) {
transportInfoLabel.Text = string.Empty; // clear
if (vector != null) {
var transportProduct = GetSelectedLayer(transportLayersListBox);
var transportInfo =
Content.Transports.FirstOrDefault(
info => info.Product == transportProduct && info.SrcRegion == vector.Item1 && info.DestRegion == vector.Item2);
if (transportInfo != null) {
var sb = new StringBuilder();
sb.AppendFormat("{0} -> {1}", transportInfo.SrcRegion, transportInfo.DestRegion).AppendLine();
sb.AppendFormat("{0} (Mode: {1})", transportInfo.Product, transportInfo.Mode).AppendLine();
sb.AppendFormat("{0:N0} kt km", transportInfo.Distance * transportInfo.Amount / 1000.0).AppendLine();
sb.AppendFormat("{0:N1} kt {1:N0} km", transportInfo.Amount / 1000.0, transportInfo.Distance);
transportInfoLabel.Text = sb.ToString();
}
}
}
#endregion
#region Event Handlers (child controls)
private void vectorLayerNamesListBox_SelectedIndexChanged(object sender, EventArgs e) {
UpdateMapLayers();
}
private void valueLayerNamesListBox_SelectedIndexChanged(object sender, EventArgs e) {
UpdateMapLayers();
}
private void optimizeTransportsButton_Click(object sender, EventArgs e) {
using (BackgroundWorker bg = new BackgroundWorker()) {
MainFormManager.GetMainForm().AddOperationProgressToView(this, "Optimizing Transports...");
bg.DoWork += (o, a) => OptimizeTransports();
bg.RunWorkerCompleted += (o, a) => MainFormManager.GetMainForm().RemoveOperationProgressFromView(this);
bg.RunWorkerAsync();
}
}
private void optimizeUtilizationsButton_Click(object sender, EventArgs e) {
using (BackgroundWorker bg = new BackgroundWorker()) {
MainFormManager.GetMainForm().AddOperationProgressToView(this, "Optimizing Utilizations...");
bg.DoWork += (o, a) => OptimizeUtilizations();
bg.RunWorkerCompleted += (o, a) => MainFormManager.GetMainForm().RemoveOperationProgressFromView(this);
bg.RunWorkerAsync();
}
}
#endregion
#region mouse events
// TODO: it should be possible to allow panning and drawing arrows at the same time by implementing a CustomTool
private void mapBox_MouseMove(Coordinate worldPos, MouseEventArgs imagePos) {
var nuts_id = GetRegionAtWorldPos(worldPos);
var nuts_name = GetRegionDescription(nuts_id);
regionNameLabel.Text = nuts_id;
regionDescriptionLabel.Text = nuts_name;
mapBox.Cursor = String.IsNullOrEmpty(nuts_id) ? Cursors.Default : Cursors.Hand;
var arrow = GetArrowAtWorldPos(worldPos);
UpdateTransportInfo(arrow);
}
private Point startPos = new Point(0, 0);
private Coordinate startWorldPos;
private void mapBox_MouseDown(Coordinate worldPos, MouseEventArgs mouseEventArgs) {
if (mouseEventArgs.Button == MouseButtons.Left) {
startPos = mouseEventArgs.Location;
startWorldPos = worldPos;
}
}
private void mapBox_MouseUp(Coordinate endWorldPos, MouseEventArgs mousePos) {
if (mousePos.Button == MouseButtons.Left) {
var arrow = GetArrowAtWorldPos(startWorldPos);
var startRegion = GetRegionAtWorldPos(startWorldPos);
var endRegion = GetRegionAtWorldPos(endWorldPos);
DispatchMouseUp(startRegion, endRegion, arrow, mousePos.Location);
}
}
// custom drawing method for vector list box (for coloring of layers)
private void vectorLayerNamesListBox_DrawItem(object sender, DrawItemEventArgs e) {
var item = transportLayersListBox.Items[e.Index];
var listBoxItem = item as ListBoxItem;
var backColor = listBoxItem.Color;
var foreColor = Color.White;
using (var backBrush = new SolidBrush(backColor)) {
e.Graphics.FillRectangle(backBrush, e.Bounds);
}
using (var foreBrush = new SolidBrush(foreColor)) {
e.Graphics.DrawString(
listBoxItem.Text,
transportLayersListBox.Font, foreBrush, e.Bounds);
}
}
#endregion
#region drag-and-drop of problem data
private bool dropAllowed = false;
protected override void OnDragDrop(DragEventArgs e) {
if (e.Effect != DragDropEffects.None) {
var problemData = (BioBoostProblemData)e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
Content.ProblemDataReference = (BioBoostProblemData)problemData.Clone();
}
}
protected override void OnDragEnter(DragEventArgs e) {
var problemData = (BioBoostProblemData)e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
dropAllowed = !ReadOnly && !Locked && problemData != null;
}
protected override void OnDragOver(DragEventArgs e) {
e.Effect = dropAllowed ?
DragDropEffects.Copy :
DragDropEffects.None;
}
#endregion
#region Auxiliar Methods
private Task ShowTrackBar(Point location, double value) {
var wh = new AutoResetEvent(false);
var tb = new TrackBar {
TickStyle = TickStyle.None,
Minimum = 0,
Maximum = 100,
Value = (int)Math.Round(value * 100),
Location = location
};
var label = new Label();
label.Location = new Point(location.X, location.Y - label.Height);
label.Text = string.Format("Utilization: {0:F2}", value);
label.Width = tb.Width;
mapBox.Controls.Add(tb);
mapBox.Controls.Add(label);
tb.Focus();
tb.ValueChanged += (sender, args) => {
value = tb.Value / 100.0;
label.Text = string.Format("Utilization: {0:F2}", value);
};
tb.LostFocus += (sender, args) => {
mapBox.Controls.Remove(tb);
mapBox.Controls.Remove(label);
wh.Set();
};
return Task.Run(() => {
wh.WaitOne();
wh.Dispose();
return value;
});
}
// returns the NUTS_ID of the region if it has been hit
private string GetRegionAtWorldPos(Coordinate loc) {
var ds = new FeatureDataSet();
var layer = mapBox.Map.Layers.OfType().SingleOrDefault(l => l.LayerName == "Utilizations");
if (layer == null) return string.Empty;
layer.ExecuteIntersectionQuery(new NetTopologySuite.Geometries.Point(loc), ds);
return (from FeatureDataRow row in ds.Tables[0]
select row["NUTS_ID"] as string).FirstOrDefault() ?? string.Empty; // default is string.Empty
}
// returns the source and target NUTS_ID (Tuple(src, trgt)) for an vector if it has been hit
private Tuple GetArrowAtWorldPos(Coordinate loc) {
var ds = new FeatureDataSet();
var layer = mapBox.Map.Layers.OfType().SingleOrDefault(l => l.LayerName == "Transports");
if (layer == null) return null;
var rect = new Coordinate[5];
var delta = mapBox.Map.PixelWidth * 2; // for bounding box arount the click point
rect[0] = new Coordinate(loc.X - delta, loc.Y - delta);
rect[1] = new Coordinate(loc.X + delta, loc.Y - delta);
rect[3] = new Coordinate(loc.X + delta, loc.Y + delta);
rect[2] = new Coordinate(loc.X - delta, loc.Y + delta);
rect[4] = new Coordinate(loc.X - delta, loc.Y - delta);
layer.ExecuteIntersectionQuery(new NetTopologySuite.Geometries.Polygon(new LinearRing(rect)), ds);
var arrow = (from FeatureDataRow row in ds.Tables[0]
select Tuple.Create((string)row["NUTS_ID_SRC"], (string)row["NUTS_ID_DEST"])
).FirstOrDefault();
return arrow;
}
private string GetRegionDescription(string regionName) {
if (regionName != null && Content.Geometry.Features.Columns.Contains("name")) {
var row = Content.Geometry.Features.Rows.Cast().FirstOrDefault(r => (string)r["NUTS_ID"] == regionName);
if (row != null)
return row["name"].ToString();
}
return string.Empty;
}
private void ResetZoom() {
if (mapBox.Map.Layers.Count > 0)
mapBox.Map.ZoomToExtents();
// mapBox.ActiveTool = MapBox.Tools.DrawLine;
mapBox.Refresh();
}
private string GetSelectedLayer(ListBox listBox) {
return
listBox.SelectedItem == null ?
string.Empty
: listBox.SelectedItem.ToString();
}
private bool TrySetSelectedLayer(ListBox listBox, string text) {
bool success = false;
if (string.IsNullOrEmpty(text)) {
listBox.SelectedIndex = 0;
} else {
int selectedIndex = 0;
for (int i = 0; i < listBox.Items.Count; i++) {
if (listBox.Items[i].ToString() == text) {
selectedIndex = i;
success = true;
break;
}
}
listBox.SelectedIndex = selectedIndex;
}
return success;
}
#endregion
// only necessary for drawing of vector layer elements with color
private class ListBoxItem {
public Color Color { get; set; }
public string Text { get; set; }
public T Value { get; set; }
public override string ToString() {
return Text;
}
}
}
}