첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
728x90
반응형
728x170

TestProject.zip
다운로드

▶ 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
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요