728x90
반응형
728x170
▶ Cluster.cs
using System;
using System.Collections.Generic;
using DevExpress.Map;
using DevExpress.XtraMap;
namespace TestProject
{
/// <summary>
/// 클러스터
/// </summary>
public class Cluster
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 지원 좌표 위치 리스트
/// </summary>
private List<ISupportCoordLocation> supportCoordLocationList;
/// <summary>
/// 중심점
/// </summary>
private MapPoint centerPoint;
/// <summary>
/// 가장 가까운 클러스터
/// </summary>
private Cluster closestCluster;
/// <summary>
/// 가장 가까운 거리
/// </summary>
private double distanceToClosest;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 지원 좌표 위치 리스트 - SupportCoordLocationList
/// <summary>
/// 지원 좌표 위치 리스트
/// </summary>
public List<ISupportCoordLocation> SupportCoordLocationList
{
get
{
return this.supportCoordLocationList;
}
}
#endregion
#region 중심점 - CenterPoint
/// <summary>
/// 중심점
/// </summary>
public MapPoint CenterPoint
{
get
{
return this.centerPoint;
}
}
#endregion
#region 가장 가까운 클러스터 - ClosestCluster
/// <summary>
/// 가장 가까운 클러스터
/// </summary>
public Cluster ClosestCluster
{
get
{
return this.closestCluster;
}
set
{
this.closestCluster = value;
this.distanceToClosest = GetDistance(this.closestCluster);
}
}
#endregion
#region 가장 가까운 거리 - DistanceToClosest
/// <summary>
/// 가장 가까운 거리
/// </summary>
public double DistanceToClosest
{
get
{
return this.distanceToClosest;
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - Cluster(supportCoordLocationList)
/// <summary>
/// 생성자
/// </summary>
/// <param name="supportCoordLocationList">지원 좌표 위치 리스트</param>
public Cluster(List<ISupportCoordLocation> supportCoordLocationList)
{
this.supportCoordLocationList = supportCoordLocationList;
this.centerPoint = CalculateCenterPoint(supportCoordLocationList);
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 초기화하기 - Initialize(supportCoordLocation)
/// <summary>
/// 초기화하기
/// </summary>
/// <param name="supportCoordLocation">지원 좌표 위치</param>
/// <returns>클러스터</returns>
public static Cluster Initialize(ISupportCoordLocation supportCoordLocation)
{
List<ISupportCoordLocation> supportCoordLocationList = new List<ISupportCoordLocation>();
supportCoordLocationList.Add(supportCoordLocation);
return new Cluster(supportCoordLocationList);
}
#endregion
#region 병합하기 - Cluster Merge(cluster1, cluster2)
/// <summary>
/// 병합하기
/// </summary>
/// <param name="cluster1">클러스터 1</param>
/// <param name="cluster2">클러스터 2</param>
/// <returns>클러스터</returns>
public static Cluster Merge(Cluster cluster1, Cluster cluster2)
{
List<ISupportCoordLocation> list = new List<ISupportCoordLocation>(cluster1.SupportCoordLocationList);
list.AddRange(cluster2.SupportCoordLocationList);
return new Cluster(list);
}
#endregion
#region 중심점 계산하기 - CalculateCenterPoint(supportCoordLocationList)
/// <summary>
/// 중심점 계산하기
/// </summary>
/// <param name="supportCoordLocationList">지원 좌표 위치 리스트</param>
/// <returns>중심점</returns>
public static MapPoint CalculateCenterPoint(List<ISupportCoordLocation> supportCoordLocationList)
{
double meanX = 0;
double meanY = 0;
foreach(ISupportCoordLocation supportCoordLocation in supportCoordLocationList)
{
meanX += supportCoordLocation.Location.GetX();
meanY += supportCoordLocation.Location.GetY();
}
meanX /= supportCoordLocationList.Count;
meanY /= supportCoordLocationList.Count;
return new MapPoint(meanX, meanY);
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Public
#region 거리 구하기 - GetDistance(cluster)
/// <summary>
/// 거리 구하기
/// </summary>
/// <param name="cluster">클러스터</param>
/// <returns>거리</returns>
public double GetDistance(Cluster cluster)
{
return Math.Sqrt
(
(cluster.CenterPoint.X - CenterPoint.X) * (cluster.CenterPoint.X - CenterPoint.X) +
(cluster.CenterPoint.Y - CenterPoint.Y) * (cluster.CenterPoint.Y - CenterPoint.Y)
);
}
#endregion
}
}
728x90
▶ CureClusterer.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using DevExpress.Map;
using DevExpress.XtraMap;
namespace TestProject
{
/// <summary>
/// 치료 클러스터러
/// </summary>
public class CureClusterer : IClusterer
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 분주함 여부
/// </summary>
private bool isBusy;
/// <summary>
/// 클러스터 카운트
/// </summary>
private int clusterCount = 10;
/// <summary>
/// 맵 항목 컬렉션
/// </summary>
private MapItemCollection mapItemCollection;
/// <summary>
/// 맵 데이터 어댑터
/// </summary>
private IMapDataAdapter mapDataAdapter;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 분주함 여부 - IsBusy
/// <summary>
/// 분주함 여부
/// </summary>
public bool IsBusy
{
get
{
return this.isBusy;
}
}
#endregion
#region 항목 컬렉션 - Items
/// <summary>
/// 항목 컬렉션
/// </summary>
public MapItemCollection Items
{
get
{
return this.mapItemCollection;
}
}
#endregion
#region 클러스터 카운트 - ClusterCount
/// <summary>
/// 클러스터 카운트
/// </summary>
public int ClusterCount
{
get
{
return this.clusterCount;
}
set
{
if(value < 1)
{
throw new ArgumentOutOfRangeException("The ClusterCount should be larger than 1.");
}
this.clusterCount = value;
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - CureClusterer()
/// <summary>
/// 생성자
/// </summary>
public CureClusterer()
{
this.isBusy = false;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 클러스터 만들기 - Clusterize(sourceEnumerable, viewport, sourceChanged)
/// <summary>
/// 클러스터 만들기
/// </summary>
/// <param name="sourceEnumerable">소스 열거 가능 목록</param>
/// <param name="viewport">뷰포트</param>
/// <param name="sourceChanged">소스 변경 여부</param>
public void Clusterize(IEnumerable<MapItem> sourceEnumerable, MapViewport viewport, bool sourceChanged)
{
Thread thread = new Thread
(
() => {
this.isBusy = true;
if(sourceChanged)
{
this.mapItemCollection = GetMapItemCollection(sourceEnumerable);
this.mapDataAdapter.OnClustered();
}
this.isBusy = false;
}
);
thread.Start();
}
#endregion
#region 소유자 설정하기 - SetOwner(mapDataAdapter)
/// <summary>
/// 소유자 설정하기
/// </summary>
/// <param name="mapDataAdapter">맵 데이터 어댑터</param>
public void SetOwner(IMapDataAdapter mapDataAdapter)
{
this.mapDataAdapter = mapDataAdapter;
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 가장 가깝게 배열하기 - AssignClosest(cluster, sourceList)
/// <summary>
/// 가장 가깝게 배열하기
/// </summary>
/// <param name="cluster">클러스터</param>
/// <param name="sourceList">소스 리스트</param>
private void AssignClosest(Cluster cluster, List<Cluster> sourceList)
{
if(sourceList.Count < 2)
{
throw new ArgumentOutOfRangeException("Clusters count should be larger than 2.");
}
Cluster distancableCluster = sourceList[0];
if(distancableCluster == cluster)
{
distancableCluster = sourceList[1];
}
cluster.ClosestCluster = distancableCluster;
for(int i = 0; i < sourceList.Count; ++i)
{
distancableCluster = sourceList[i];
if(distancableCluster == cluster)
{
continue;
}
double distance = cluster.GetDistance(distancableCluster);
if(distance < cluster.DistanceToClosest)
{
cluster.ClosestCluster = distancableCluster;
}
}
}
#endregion
#region 삽입하기 - Insert(targetList, cluster)
/// <summary>
/// 삽입하기
/// </summary>
/// <param name="targetList">타겟 리스트</param>
/// <param name="cluster">클러스터</param>
private void Insert(List<Cluster> targetList, Cluster cluster)
{
for(int i = 0; i < targetList.Count; i++)
{
if(targetList[i].DistanceToClosest > cluster.DistanceToClosest)
{
targetList.Insert(i, cluster);
return;
}
}
targetList.Add(cluster);
}
#endregion
#region 클러스터 리스트 배열하기 - ArrangeClusterList(sourceList)
/// <summary>
/// 클러스터 리스트 배열하기
/// </summary>
/// <param name="sourceList">소스 리스트</param>
/// <returns>클러스터 리스트</returns>
private List<Cluster> ArrangeClusterList(List<Cluster> sourceList)
{
List<Cluster> targetList = new List<Cluster>();
for(int i = 0; i < sourceList.Count; ++i)
{
Cluster cluster = sourceList[i];
AssignClosest(cluster, sourceList);
Insert(targetList, cluster);
}
return targetList;
}
#endregion
#region 가장 가까운 클러스터 병합하기 - MergeCloserstCluster(targetList)
/// <summary>
/// 가장 가까운 클러스터 병합하기
/// </summary>
/// <param name="targetList">타겟 리스트</param>
private void MergeCloserstCluster(ref List<Cluster> targetList)
{
if(targetList.Count < 2)
{
throw new ArgumentOutOfRangeException("Clusters count should be larger than 2.");
}
Cluster cluster1 = targetList[0];
Cluster cluster2 = cluster1.ClosestCluster;
targetList.RemoveAt(0);
targetList.Remove(cluster2);
Cluster newCluster = Cluster.Merge(cluster1, cluster2);
targetList.Add(newCluster);
targetList = ArrangeClusterList(targetList);
}
#endregion
#region 맵 항목 컬렉션 구하기 - GetMapItemCollection(sourceEnumerable)
/// <summary>
/// 맵 항목 컬렉션 구하기
/// </summary>
/// <param name="sourceEnumerable">소스 열거 가능 목록</param>
/// <returns>맵 항목 컬렉션</returns>
private MapItemCollection GetMapItemCollection(IEnumerable<MapItem> sourceEnumerable)
{
List<MapItem> mapItemList = new List<MapItem>();
List<Cluster> clusterList = new List<Cluster>();
foreach(MapItem mapItem in sourceEnumerable)
{
ISupportCoordLocation supportCoordLocation = mapItem as ISupportCoordLocation;
if(supportCoordLocation != null)
{
clusterList.Add(Cluster.Initialize(supportCoordLocation));
}
else
{
mapItemList.Add(mapItem);
}
}
clusterList = ArrangeClusterList(clusterList);
while(clusterList.Count > ClusterCount)
{
MergeCloserstCluster(ref clusterList);
}
MapItemCollection mapItemCollection = new MapItemCollection(mapDataAdapter);
for(int i = 0; i < clusterList.Count; i++)
{
Cluster cluster = clusterList[i];
MapDot mapDot = new MapDot()
{
Location = new GeoPoint(cluster.CenterPoint.Y, cluster.CenterPoint.X),
Size = 100
};
mapDot.ClusteredItems = cluster.SupportCoordLocationList.Select(item => item as MapItem).ToList();
mapDot.TitleOptions.Pattern = mapDot.ClusteredItems.Count.ToString();
mapItemCollection.Add(mapDot);
}
mapItemCollection.AddRange(mapItemList);
return mapItemCollection;
}
#endregion
}
}
300x250
▶ Tree.cs
using System;
namespace TestProject
{
/// <summary>
/// 트리
/// </summary>
public class Tree
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 장소명
/// </summary>
private string locationName;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 위도 - Latitude
/// <summary>
/// 위도
/// </summary>
public double Latitude { get; set; }
#endregion
#region 경도 - Longitude
/// <summary>
/// 경도
/// </summary>
public double Longitude { get; set; }
#endregion
#region 장소명 - LocationName
/// <summary>
/// 장소명
/// </summary>
public string LocationName
{
get
{
return this.locationName;
}
set
{
if(value == null)
{
throw new ArgumentNullException("LocationName");
}
if(value.Equals(this.locationName))
{
return;
}
this.locationName = value;
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - Tree()
/// <summary>
/// 생성자
/// </summary>
public Tree()
{
this.locationName = string.Empty;
}
#endregion
}
}
▶ MainForm.cs
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml.Linq;
using DevExpress.XtraEditors;
using DevExpress.XtraMap;
namespace TestProject
{
/// <summary>
/// 메인 폼
/// </summary>
public partial class MainForm : XtraForm
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 빙 맵 키
/// </summary>
private string bingKey = "INPUT YOUR BING KEY";
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MainForm()
/// <summary>
/// 생성자
/// </summary>
public MainForm()
{
InitializeComponent();
#region 빙 맵 데이터 공급자를 설정한다.
this.bingMapDataProvider.BingKey = this.bingKey;
#endregion
#region 트리 리스트를 설정한다.
List<Tree> treeList = new List<Tree>();
XDocument document = XDocument.Load("DATA\\treesCl.xml");
foreach(XElement element in document.Element("RowSet").Elements("Row"))
{
treeList.Add
(
new Tree
{
Latitude = Convert.ToDouble(element.Element("lat" ).Value, CultureInfo.InvariantCulture),
Longitude = Convert.ToDouble(element.Element("lon" ).Value, CultureInfo.InvariantCulture),
LocationName = element.Element("location").Value
}
);
}
#endregion
#region 리스트 소스 데이터 어댑터를 설정한다.
this.listSourceDataAdapter.DataSource = treeList;
this.listSourceDataAdapter.Clusterer = new CureClusterer();
#endregion
this.vectorItemsLayer.DataLoaded += vectorItemsLayer_DataLoaded;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 벡터 항목 레이어 데이터 로드시 처리하기 - vectorItemsLayer_DataLoaded(sender, e)
/// <summary>
/// 벡터 항목 레이어 데이터 로드시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void vectorItemsLayer_DataLoaded(object sender, DataLoadedEventArgs e)
{
this.mapControl.ZoomToFitLayerItems();
}
#endregion
}
}
728x90
반응형
그리드형(광고전용)
댓글을 달아 주세요