diff --git a/Plane.FormationCreator/ViewModels/ConfigVirtualIdViewModel.cs b/Plane.FormationCreator/ViewModels/ConfigVirtualIdViewModel.cs
new file mode 100644
index 0000000..b783e52
--- /dev/null
+++ b/Plane.FormationCreator/ViewModels/ConfigVirtualIdViewModel.cs
@@ -0,0 +1,222 @@
+using GalaSoft.MvvmLight;
+using GalaSoft.MvvmLight.Command;
+using Plane.Copters;
+using Plane.FormationCreator.Formation;
+using Plane.Geography;
+using Plane.Windows.Messages;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+
+namespace Plane.FormationCreator.ViewModels
+{
+ public class ConfigVirtualIdViewModel: ViewModelBase
+ {
+ CopterManager _copterManager;
+ public ConfigVirtualIdViewModel(CopterManager copterManager)
+ {
+ _copterManager = copterManager;
+ }
+
+ private int _SingleVirtualId = 0;
+ public int SingleVirtualId
+ {
+ get { return _SingleVirtualId; }
+ set { Set(nameof(SingleVirtualId), ref _SingleVirtualId, value); }
+ }
+
+ private int _SingleRowCount = 0;
+ public int SingleRowCount
+ {
+ get { return _SingleRowCount; }
+ set { Set(nameof(SingleRowCount), ref _SingleRowCount, value); }
+ }
+
+ ///
+ /// 清除所有编号
+ ///
+ private ICommand _ClearVirtualIdCommand;
+ public ICommand ClearVirtualIdCommand
+ {
+ get
+ {
+ return _ClearVirtualIdCommand ?? (_ClearVirtualIdCommand = new RelayCommand(() =>
+ {
+ foreach (var c in _copterManager.Copters)
+ {
+ c.VirtualId = 0;
+ }
+ Message.Show($"已清除所有飞机编号");
+ }));
+ }
+ }
+
+
+
+
+ ///
+ /// 设置单个虚拟ID
+ ///
+ private ICommand _SetSingleVirtualIdCommand;
+ public ICommand SetSingleVirtualIdCommand
+ {
+ get
+ {
+ return _SetSingleVirtualIdCommand ?? (_SetSingleVirtualIdCommand = new RelayCommand(() =>
+ {
+ if (SingleVirtualId <= 0)
+ {
+ System.Windows.MessageBox.Show("编号必须大于0");
+ return;
+ }
+
+ if (_copterManager.AcceptingControlCopters.Count() == 1)
+ {
+ var copter = _copterManager.AcceptingControlCopters.FirstOrDefault();
+ copter.VirtualId = SingleVirtualId;
+ Message.Show($"飞机{copter.Name} 设置编号={SingleVirtualId}");
+ }
+ }));
+ }
+ }
+
+ private ICommand _AutoLocationNumsCommand;
+ public ICommand AutoLocationNumsCommand
+ {
+ get
+ {
+ return _AutoLocationNumsCommand ?? (_AutoLocationNumsCommand = new RelayCommand(async () =>
+ {
+
+ int colCount = SingleRowCount;
+ int copterCount = _copterManager.Copters.Count;
+
+ if (copterCount % colCount != 0)
+ {
+ Alert.Show($"列数设置错误!");
+ return;
+ }
+
+ int rowCount = copterCount / colCount;
+ int num = 1;
+ if (_copterManager.AcceptingControlCopters.Count() == 4)
+ {
+ var copters = _copterManager.AcceptingControlCopters.ToList();
+ var firstCopter = copters[0];
+ var secondCopter = copters[1];
+ var thirdCopter = copters[2];
+ var fourthCopter = copters[3];
+
+ //第一列角度
+ double firstColazimuth = GeographyUtils.RadToDeg(GeographyUtils.CalcDirection2D(
+ firstCopter.Latitude, firstCopter.Longitude,
+ thirdCopter.Latitude, thirdCopter.Longitude));
+ //最后列角度
+ double lastColazimuth = GeographyUtils.RadToDeg(GeographyUtils.CalcDirection2D(
+ secondCopter.Latitude, secondCopter.Longitude,
+ fourthCopter.Latitude, fourthCopter.Longitude));
+
+ ICopter destCopter = null;
+ ICopter rowfirstCopter = null;
+ ICopter rowlastCopter = null;
+ for (int i = 0; i < rowCount; i++)
+ {
+
+ if (i == 0)
+ {
+ rowfirstCopter = firstCopter;
+ rowlastCopter = secondCopter;
+ }
+ else if (i == rowCount - 1)
+ {
+ rowfirstCopter = thirdCopter;
+ rowlastCopter = fourthCopter;
+ }
+ else
+ {
+ rowfirstCopter = FindNextCopter(rowfirstCopter, firstColazimuth);
+ rowlastCopter = FindNextCopter(rowlastCopter, lastColazimuth);
+ }
+
+ if (rowfirstCopter == null || rowlastCopter == null)
+ {
+ Alert.Show($"在确认第{i+1}列时候无法找到起始或结束飞机");
+ return;
+ }
+
+
+ for (int j = 0; j < colCount; j++)
+ {
+
+ double curRowAzimuth = GeographyUtils.RadToDeg(GeographyUtils.CalcDirection2D(
+ rowfirstCopter.Latitude, rowfirstCopter.Longitude,
+ rowlastCopter.Latitude, rowlastCopter.Longitude));
+ if (j == 0)
+ {
+ destCopter = rowfirstCopter;
+ }
+ else if (j == colCount - 1)
+ {
+ destCopter = rowlastCopter;
+ }
+ else
+ {
+ destCopter = FindNextCopter(destCopter, curRowAzimuth);
+
+ if (destCopter == null)
+ {
+ Alert.Show($"在寻找第{num}号位置时候无法找到对应位置飞机");
+ return;
+ }
+
+ }
+ destCopter.VirtualId = num;
+
+ Message.Show($"飞机{destCopter.Name} 虚拟ID = {destCopter.VirtualId}");
+ num++;
+ }
+ }
+ }
+ await Task.Delay(10);
+ }));
+ }
+ }
+
+
+ private ICopter FindNextCopter(ICopter startCopter, double azimuth)
+ {
+ ICopter retCopter = null;
+ bool foundCopter = false;
+ float stepLength = 0;
+ while (!foundCopter)
+ {
+ stepLength += 1.5f;
+ Tuple targetLatLng = GeographyUtils.CalcLatLngSomeMetersAway2D(
+ startCopter.Latitude, startCopter.Longitude, (float)azimuth, stepLength);
+
+ List remainCopters = _copterManager.Copters.Where(o => o.VirtualId == 0).ToList();
+
+ foreach (var copter in remainCopters)
+ {
+ double temp = GeographyUtils.CalcDistance2D(copter.Latitude, copter.Longitude,
+ targetLatLng.Item1, targetLatLng.Item2);
+ if (temp < 1.5)
+ {
+ foundCopter = true;
+ retCopter = copter;
+ break;
+ }
+ }
+
+ if (stepLength > 15)
+ {
+ break;
+ }
+ }
+ return retCopter;
+ }
+ }
+}
diff --git a/Plane.FormationCreator/Views/ConfigVirtualIdView.xaml b/Plane.FormationCreator/Views/ConfigVirtualIdView.xaml
new file mode 100644
index 0000000..92e5e1b
--- /dev/null
+++ b/Plane.FormationCreator/Views/ConfigVirtualIdView.xaml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Plane.FormationCreator/Views/ConfigVirtualIdView.xaml.cs b/Plane.FormationCreator/Views/ConfigVirtualIdView.xaml.cs
new file mode 100644
index 0000000..a2d6710
--- /dev/null
+++ b/Plane.FormationCreator/Views/ConfigVirtualIdView.xaml.cs
@@ -0,0 +1,31 @@
+using Microsoft.Practices.ServiceLocation;
+using Plane.FormationCreator.ViewModels;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace Plane.FormationCreator.Views
+{
+ ///
+ /// ConfigVirtualIdView.xaml 的交互逻辑
+ ///
+ public partial class ConfigVirtualIdView : UserControl
+ {
+ public ConfigVirtualIdView()
+ {
+ InitializeComponent();
+ this.DataContext = ServiceLocator.Current.GetInstance();
+ }
+ }
+}