Light12306/TrainInfomationProviderService/TrainInfo/Entities/TrainTransitOnceResultCollection.cs

450 lines
14 KiB
C#
Raw Normal View History

2014-11-27 23:25:36 +08:00
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TrainInfomationProviderService.StationInfo;
using TrainInfomationProviderService.StationInfo.Entities;
namespace TrainInfomationProviderService.TrainInfo.Entities
{
public class TrainTransitOnceResultCollection : ICollection<TrainTransitOnceResult>
{
List<TrainTransitOnceResult> _list = new List<TrainTransitOnceResult>();
TrainTransitSearchOptions _options;
2014-12-01 02:17:13 +08:00
/// <summary>
/// 最推荐的几个线路
/// </summary>
public List<TrainTransitOnceResult> TopMostRecommandLines { get; private set; }
2014-11-27 23:25:36 +08:00
/// <summary>
/// 日期
/// </summary>
public DateTime Date { get; private set; }
/// <summary>
/// 出发城市编码
/// </summary>
public string FromCode { get; private set; }
/// <summary>
/// 到达城市编码
/// </summary>
public string ToCode { get; private set; }
/// <summary>
/// 出发城市信息
/// </summary>
public StationDetailInfo From { get; private set; }
/// <summary>
/// 到达城市信息
/// </summary>
public StationDetailInfo To { get; private set; }
/// <summary>
/// 创建 <see cref="TrainTransitOnceResultCollection"/> 的新实例(TrainTransitOnceResultCollection)
/// </summary>
/// <param name="options"></param>
/// <param name="date"></param>
/// <param name="fromCode"></param>
/// <param name="toCode"></param>
public TrainTransitOnceResultCollection(DateTime date, TrainTransitSearchOptions options, string fromCode, string toCode)
{
ToCode = toCode;
FromCode = fromCode;
Date = date;
_options = options;
From = StationManager.Instance.Storage.Stations.GetValue(fromCode);
To = StationManager.Instance.Storage.Stations.GetValue(toCode);
}
#region Implementation of IEnumerable
/// <summary>
/// 返回一个循环访问集合的枚举器。
/// </summary>
/// <returns>
/// 可用于循环访问集合的 <see cref="T:System.Collections.Generic.IEnumerator`1"/>。
/// </returns>
public IEnumerator<TrainTransitOnceResult> GetEnumerator()
{
return _list.GetEnumerator();
}
/// <summary>
/// 返回一个循环访问集合的枚举数。
/// </summary>
/// <returns>
/// 一个可用于循环访问集合的 <see cref="T:System.Collections.IEnumerator"/> 对象。
/// </returns>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region Implementation of ICollection<TrainTransitOnceResult>
/// <summary>
/// 将某项添加到 <see cref="T:System.Collections.Generic.ICollection`1"/> 中。
/// </summary>
/// <param name="item">要添加到 <see cref="T:System.Collections.Generic.ICollection`1"/> 的对象。</param><exception cref="T:System.NotSupportedException"><see cref="T:System.Collections.Generic.ICollection`1"/> 为只读。</exception>
public void Add(TrainTransitOnceResult item)
{
if (item == null || !CheckTransitLineAvailable(item))
return;
_list.Add(item);
}
/// <summary>
/// 从 <see cref="T:System.Collections.Generic.ICollection`1"/> 中移除所有项。
/// </summary>
/// <exception cref="T:System.NotSupportedException"><see cref="T:System.Collections.Generic.ICollection`1"/> 为只读。</exception>
public void Clear()
{
_list.Clear();
}
/// <summary>
/// 确定 <see cref="T:System.Collections.Generic.ICollection`1"/> 是否包含特定值。
/// </summary>
/// <returns>
/// 如果在 <see cref="T:System.Collections.Generic.ICollection`1"/> 中找到 <paramref name="item"/>,则为 true否则为 false。
/// </returns>
/// <param name="item">要在 <see cref="T:System.Collections.Generic.ICollection`1"/> 中定位的对象。</param>
public bool Contains(TrainTransitOnceResult item)
{
return _list.Contains(item);
}
/// <summary>
/// 从特定的 <see cref="T:System.Array"/> 索引开始,将 <see cref="T:System.Collections.Generic.ICollection`1"/> 的元素复制到一个 <see cref="T:System.Array"/> 中。
/// </summary>
/// <param name="array">作为从 <see cref="T:System.Collections.Generic.ICollection`1"/> 复制的元素的目标的一维 <see cref="T:System.Array"/>。 <see cref="T:System.Array"/> 必须具有从零开始的索引。</param><param name="arrayIndex"><paramref name="array"/> 中从零开始的索引,从此索引处开始进行复制。</param><exception cref="T:System.ArgumentNullException"><paramref name="array"/> 为 null。</exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="arrayIndex"/> 小于 0。</exception><exception cref="T:System.ArgumentException">源 <see cref="T:System.Collections.Generic.ICollection`1"/> 中的元素数目大于从 <paramref name="arrayIndex"/> 到目标 <paramref name="array"/> 末尾之间的可用空间。</exception>
public void CopyTo(TrainTransitOnceResult[] array, int arrayIndex)
{
_list.CopyTo(array, arrayIndex);
}
/// <summary>
/// 从 <see cref="T:System.Collections.Generic.ICollection`1"/> 中移除特定对象的第一个匹配项。
/// </summary>
/// <returns>
/// 如果已从 <see cref="T:System.Collections.Generic.ICollection`1"/> 中成功移除 <paramref name="item"/>,则为 true否则为 false。 如果在原始 <see cref="T:System.Collections.Generic.ICollection`1"/> 中没有找到 <paramref name="item"/>,该方法也会返回 false。
/// </returns>
/// <param name="item">要从 <see cref="T:System.Collections.Generic.ICollection`1"/> 中移除的对象。</param><exception cref="T:System.NotSupportedException"><see cref="T:System.Collections.Generic.ICollection`1"/> 为只读。</exception>
public bool Remove(TrainTransitOnceResult item)
{
return _list.Remove(item);
}
/// <summary>
/// 获取 <see cref="T:System.Collections.Generic.ICollection`1"/> 中包含的元素数。
/// </summary>
/// <returns>
/// <see cref="T:System.Collections.Generic.ICollection`1"/> 中包含的元素个数。
/// </returns>
public int Count { get { return _list.Count; } }
/// <summary>
/// 获取一个值,该值指示 <see cref="T:System.Collections.Generic.ICollection`1"/> 是否为只读。
/// </summary>
/// <returns>
/// 如果 <see cref="T:System.Collections.Generic.ICollection`1"/> 为只读,则为 true否则为 false。
/// </returns>
public bool IsReadOnly { get { return false; } }
public TrainTransitSearchOptions Options
{
get { return _options; }
}
#endregion
#region
static int GetMinimalSpace(Train train, bool notRecommand)
{
var extraTime = notRecommand ? 60 : 0;
switch (train.TrainClass)
{
case 'C':
case 'G':
2014-11-28 19:04:24 +08:00
return 30 + extraTime;
2014-11-27 23:25:36 +08:00
case 'D':
return 30 + extraTime;
case 'Z':
return 60 + extraTime;
case 'T':
return 60 + extraTime;
default:
return 60 + extraTime;
}
}
static int GetMaxiumSpace(Train train, bool notRecommand)
{
2015-08-10 20:46:29 +08:00
var extraTime = notRecommand ? 180 : 0;
2014-11-27 23:25:36 +08:00
switch (train.TrainClass)
{
case 'C':
case 'G':
return 150 + extraTime;
case 'D':
return 150 + extraTime;
case 'Z':
return 180 + extraTime;
case 'T':
return 180 + extraTime;
default:
return 240 + extraTime;
}
}
/// <summary>
/// 获得比较理想中位时间
/// </summary>
/// <param name="train"></param>
/// <returns></returns>
static int GetHighQualityTimeSpace(Train train, bool notRecommand)
{
2015-08-10 20:46:29 +08:00
var extraTime = notRecommand ? 120 : 0;
2014-11-27 23:25:36 +08:00
switch (train.TrainClass)
{
case 'C':
case 'G':
return 60 + extraTime;
case 'D':
return 60 + extraTime;
case 'Z':
return 120 + extraTime;
case 'T':
return 120 + extraTime;
default:
return 120 + extraTime;
}
}
#endregion
#region
bool CheckTransitLineAvailable(TrainTransitOnceResult item)
{
//总共时间
if (item.First.CalculatedMinutesBase + item.Second.CalculatedMinutesBase > _options.MaxExtraMinutes)
//时间过久
return false;
//时间衔接
var arriveFirst = Date.Add(item.First.To.Arrive.Value);
var leftSecond = arriveFirst.Date.Add(item.Second.From.Left.Value);
//早于发车
if (leftSecond <= arriveFirst)
return false;
var space = (int)(leftSecond - arriveFirst).TotalMinutes;
if (space < GetMinimalSpace(item.First.Train, item.NotRecommand) || space > GetMaxiumSpace(item.Second.Train, item.NotRecommand))
return false;
return true;
}
#endregion
#region
/// <summary>
/// 二次优化,排除小站等。
/// </summary>
internal void SecondaryAnalyze()
{
//对于前车进行分组优化
RemoveSmallStation();
//排序
ProcessPriority();
}
/// <summary>
/// 移除小站
/// </summary>
void RemoveSmallStation()
{
2015-06-05 18:35:25 +08:00
var groups = this.GroupBy(s => s.First.Train.Id).Select(s => new { s.Key, Array = s.ToArray() }).ToArray();
2014-11-27 23:25:36 +08:00
foreach (var g in groups)
{
if (g.Array.Length < 2)
continue;
Array.Sort(g.Array, (s1, s2) =>
{
var st1 = s1.First.To.StopTime.TotalMinutes;
var st2 = s2.First.To.StopTime.TotalMinutes;
if (st1 < st2) return 1;
if (st1 > st2) return -1;
return 0;
});
foreach (var line in g.Array.Skip(Options.SameLineMaxKeepStations))
{
_list.Remove(line);
}
}
2015-06-05 18:35:25 +08:00
groups = this.GroupBy(s => s.Second.Train.Id).Select(s => new { s.Key, Array = s.ToArray() }).ToArray();
2014-11-27 23:25:36 +08:00
foreach (var g in groups)
{
if (g.Array.Length < 2)
continue;
Array.Sort(g.Array, (s1, s2) =>
{
var st1 = s1.Second.From.StopTime.TotalMinutes;
var st2 = s2.Second.From.StopTime.TotalMinutes;
if (st1 < st2) return 1;
if (st1 > st2) return -1;
return 0;
});
foreach (var line in g.Array.Skip(Options.SameLineMaxKeepStations))
{
_list.Remove(line);
}
}
2014-11-28 19:04:24 +08:00
//如果线路数超过五条,则砍掉时间太多的
if (Options.StartCutLongRunningCount <= _list.Count)
{
//基础时间
2014-12-01 02:17:13 +08:00
var times = _list.Select(s => s.FirstElapsedTime.Add(s.SecondElapsedTime)).OrderBy(s => s).Min().TotalMinutes;
var maxTime = times * Options.CutLongRunningRate;
2014-11-28 19:04:24 +08:00
2014-12-01 02:17:13 +08:00
Array.ForEach(_list.Where(s => (s.SecondElapsedTime + s.FirstElapsedTime).TotalMinutes > maxTime).ToArray(), _ => _list.Remove(_));
2014-11-28 19:04:24 +08:00
}
2014-11-27 23:25:36 +08:00
}
#endregion
#region
2014-12-01 21:50:08 +08:00
public string[] CityOrder { get; private set; }
public int[] TopLinesIndices { get; private set; }
2014-11-27 23:25:36 +08:00
void ProcessPriority()
{
2014-11-28 19:04:24 +08:00
//对换乘站点计算优先级
2015-12-03 19:35:30 +08:00
var stData = _list.GroupBy(s => s.First.ToStation).Select(s => new Tuple<StationDetailInfo, TrainTransitOnceResult[]>(s.Key, s.ToArray())).ToList();
2014-11-28 19:04:24 +08:00
stData.Sort((x, y) =>
{
//前车高铁/动车等,优先推荐
2015-12-03 19:35:30 +08:00
var fhc1 = x.Item2.Count(_ => _.First.Train.IsHighSpeedClass);
var shc1 = y.Item2.Count(_ => _.First.Train.IsHighSpeedClass);
2014-11-28 19:04:24 +08:00
if (shc1 != fhc1)
2014-12-01 02:17:13 +08:00
return shc1 > fhc1 ? 1 : -1;
2014-11-28 19:04:24 +08:00
//否则推荐后车的高铁/动车
2015-12-03 19:35:30 +08:00
var fhc2 = x.Item2.Count(_ => _.Second.Train.IsHighSpeedClass);
var shc2 = y.Item2.Count(_ => _.Second.Train.IsHighSpeedClass);
2014-11-28 19:04:24 +08:00
if (shc2 != fhc2)
2015-12-03 19:35:30 +08:00
return shc2 > fhc2 ? 1 : -1;
2014-11-28 19:04:24 +08:00
//如果都米有,则按线路数推荐
2015-12-03 19:35:30 +08:00
if (x.Item2.Length != y.Item2.Length)
return y.Item2.Length - x.Item2.Length;
2014-11-28 19:04:24 +08:00
//线路数也相同。。。。那好像没啥再区别的了
2015-12-03 19:35:30 +08:00
return string.CompareOrdinal(x.Item1.PyFull, y.Item1.PyFull);
2014-11-28 19:04:24 +08:00
});
2015-12-03 19:35:30 +08:00
CityOrder = stData.Select(s => s.Item1.Code).ToArray();
2014-11-28 19:04:24 +08:00
//对应起排序
var stationWeight = new Dictionary<StationDetailInfo, int>(stData.Count);
for (var i = 0; i < stData.Count; i++)
{
2015-12-03 19:35:30 +08:00
stationWeight.Add(stData[i].Item1, i);
2014-11-28 19:04:24 +08:00
}
//分组排序
_list.Sort((x, y) =>
{
//不推荐,按不推荐来排序
if (x.NotRecommand ^ y.NotRecommand)
return x.NotRecommand ? 1 : -1;
2014-12-01 02:17:13 +08:00
//按始发终到排序
var wx = (x.First.IsBegin ? 10 : 0) + (x.First.IsEnd ? 10 : 0) + (x.Second.IsBegin ? 10 : 0) + (x.Second.IsEnd ? 10 : 0);
var wy = (y.First.IsBegin ? 10 : 0) + (y.First.IsEnd ? 10 : 0) + (y.Second.IsBegin ? 10 : 0) + (y.Second.IsEnd ? 10 : 0);
if (wx != wy)
return wx < wy ? 1 : -1;
2014-11-28 19:04:24 +08:00
//按前车是否是高速车排序
if (x.First.Train.IsHighSpeedClass ^ y.First.Train.IsHighSpeedClass)
return x.First.Train.IsHighSpeedClass ? -1 : 1;
//按后车是否是高速车排序
if (x.Second.Train.IsHighSpeedClass ^ y.Second.Train.IsHighSpeedClass)
return x.Second.Train.IsHighSpeedClass ? -1 : 1;
//如果到的时间是不推荐的时间
var xIsTimeNotRecommand = x.FirstTrainArriveDate.Hour < 6 || x.FirstTrainArriveDate.Hour >= 23;
var yIsTimeNotRecommand = y.FirstTrainArriveDate.Hour < 6 || y.FirstTrainArriveDate.Hour >= 23;
if (xIsTimeNotRecommand ^ yIsTimeNotRecommand)
return xIsTimeNotRecommand ? 1 : -1;
2014-12-01 02:17:13 +08:00
//如果发车时间是不推荐的时间
var xIsBeginTimeNotRecommand = x.FirstTrainLeftDate.Hour < 6 || x.FirstTrainLeftDate.Hour >= 23;
var yIsBeginTimeNotRecommand = y.FirstTrainLeftDate.Hour < 6 || y.FirstTrainLeftDate.Hour >= 23;
if (xIsBeginTimeNotRecommand ^ yIsBeginTimeNotRecommand)
return xIsBeginTimeNotRecommand ? 1 : -1;
//如果到的时间是不推荐的时间
var xIsEndTimeNotRecommand = x.SecondTrainArriveDate.Hour < 6 || x.SecondTrainArriveDate.Hour >= 23;
var yIsEndTimeNotRecommand = y.SecondTrainArriveDate.Hour < 6 || y.SecondTrainArriveDate.Hour >= 23;
if (xIsEndTimeNotRecommand ^ yIsEndTimeNotRecommand)
return xIsEndTimeNotRecommand ? 1 : -1;
2014-11-28 19:04:24 +08:00
//按总耗时排序
var ttl1 = x.First.ElapsedTime + x.SecondElapsedTime;
var ttl2 = y.First.ElapsedTime + y.SecondElapsedTime;
if (ttl1 != ttl2)
return ttl1 < ttl2 ? -1 : 1;
return 0;
});
2014-12-01 02:17:13 +08:00
TopMostRecommandLines = _list.Take(Options.TopMostRecommandCount).ToList();
_list.Sort((x, y) =>
{
//不推荐,按不推荐来排序
if (x.NotRecommand ^ y.NotRecommand)
return x.NotRecommand ? 1 : -1;
//车站不同,按车站来排序
if (x.First.To != y.First.To)
{
return stationWeight[x.First.ToStation] - stationWeight[y.First.ToStation];
}
return 0;
});
2014-12-01 21:50:08 +08:00
TopLinesIndices = TopMostRecommandLines.Select(s => _list.IndexOf(s)).ToArray();
2014-11-27 23:25:36 +08:00
}
#endregion
}
}