使用直接聚类方法实现地图上点的距离聚类

实验目的

使用C#和arcengine,结合直接聚类方法实现地图上点的聚类,并可以让使用者自定义聚类的级别

实验数据

全国省会shapefile数据

主要界面

 

界面1

图片2

实现逻辑

利用ArcEngine接口,计算点之间的距离,生成距离矩阵,并利用Clustering类进行距离聚类,生成聚类图,并在PictureBox的canvas中绘制聚类图,然后根据用户自定义的距离,对点进行距离分类。

实现代码

二叉树类 BinaryTree

    public class BinaryTree //二叉树结

    {

        public int key;

        public double value;

        public BinaryTree right;

        public BinaryTree left;

        public int getDepth()

        { //得二叉树实例的深度

            return getDepth(this);

        }

        private int getDepth(BinaryTree br)

        {

            if (br != null)

            {

                int rightdepth = getDepth(br.right);

                int leftdepth = getDepth(br.left);

                return (rightdepth > leftdepth ? rightdepth : leftdepth) + 1;

            }

            else

            {

                return 0;

            }

        }

        public void getCutBNList(BinaryTree bt, double cutValue, List<BinaryTree> BNList)

        {   //小于树节点列表

            if (bt.value <= cutValue)

            {

                BNList.Add(bt);

            }

            else

            {

                getCutBNList(bt.left, cutValue, BNList);

                getCutBNList(bt.right, cutValue, BNList);

            }

        }

        public void getLeafList(BinaryTree bt, List<int> leafList)

        {    //树节对应的叶子

            if (bt.left == null && bt.right == null)

            {

                leafList.Add(bt.key);

            }

            else

            {

                if (bt.left != null) getLeafList(bt.left, leafList);

                if (bt.right != null) getLeafList(bt.right, leafList);

            }

        }

        public List<List<int>> getCutLists(double cutValue)

        {   //取分割后,每一个树节对应的叶子 {1:{叶子1, 叶子2, 叶子3}, 2:{叶子1}}

            List<BinaryTree> BNList = new List<BinaryTree>();

            getCutBNList(this, cutValue, BNList);

            List<List<int>> cutLists = new List<List<int>>();

            foreach (BinaryTree bt in BNList)

            {

                List<int> cutList = new List<int>();

                getLeafList(bt, cutList);

                cutLists.Add(cutList);

            }

            return cutLists;

        }

        public string btToJson()    //将二叉树转json格式

        {

            return btToJson(this);

        }

        public string btToJson(BinaryTree bt)

        {

            string value = "value:" + bt.key;

            string right = "right:";

            string left = "left:";

            if (bt.right != null) { right += btToJson(bt.right); }

            if (bt.left != null) { left += btToJson(bt.left); }

            return "{" + value + "," + right + "," + left + "}";

        }

}

 

 

聚类类 Clustering

public class Clustering //使用最短距离聚类法行聚类

    {

        public int n { get; set; }  //聚类的点的个数

        public BinaryTree br { get; set; }  //聚类使用的二叉树结

        public int root { get;set; }    //当前聚类的根

        private double[,] matrix { get; set; }  //距离矩

        public int[] dirt { get; set; } //保存需要聚类的点的字典

        public Clustering(int n, double[,] matrix)

        {

            this.n = n;

            this.root=n-1;

            if (matrix.GetLength(1) != n || matrix.GetLength(0) != n)

            {

                throw new MatrixExpection("入的距离矩与元素个数不匹配哦!");

            }

            else

            {

                dirt=new int[n];

                for (int i = 0; i < n; i++) {

                    dirt[i] = i;

                }

                this.matrix = new double[2 * n - 1, 2 * n - 1];

                for (int i = 0; i < n; i++) {

                    for (int j = 0; j < n; j++) {

                        this.matrix[i, j] = matrix[i, j];

                    }

                }

            }

        }

        public void run()   //运行聚类方法,生成聚类二叉

        {

            BinaryTree br=new BinaryTree();

            List<BinaryTree> brs = new List<BinaryTree>();

            for (int point = n; point > 1; point--) {

                int minX = 0;

                int minY = 0;

                double value=getMin(matrix, point, ref minX, ref minY); //取距离矩中的最小

                resetdirt(minX, minY, point);   //重新置需要聚类的点的字典

                setNewRoot(minX, minY, point);  //在距离矩中加入新的点的距离

                br = new BinaryTree() { key = root, value = value };

                br.right = minX >= n ? brs.Find(i => i.key == minX) : new BinaryTree() { key = minX };  //判断当前点的右点是树节是叶子

                br.left = minY >= n ? brs.Find(i => i.key == minY) : new BinaryTree() { key = minY };

                brs.Add(br);

            }

            this.br = br;

        }

        public double getMin(double[,] matrix,int length, ref int x, ref int y) {

            double min = double.MaxValue;

            for (int i = 0; i < length; i++) {

                for (int j = 0; j < length; j++) {

                    if (matrix[dirt[i], dirt[j]] < min && matrix[dirt[i], dirt[j]]!=0)

                    {

                        x = dirt[i]; y = dirt[j];

                        min = matrix[dirt[i], dirt[j]];

                    }

                }

            }

            return min;

        }

        public void resetdirt(int x,int y,int length){

            for (int i = 0; i < length-2; i++) {

                if(this.dirt[i]==x||this.dirt[i]==y){

                    for(int j=i;j<length-1;j++){

                        this.dirt[j]=this.dirt[j+1];

                    }

                    if(i<length-1)i--;

                }

            }

            this.dirt[length-2]=++this.root;

        }

        public void setNewRoot(int x, int y,int length) {

            for (int i = 0; i < length - 2; i++) {

                double ix = this.matrix[this.dirt[i], x];

                double iy = this.matrix[this.dirt[i], y];

                this.matrix[dirt[i], root] = ix < iy ? ix : iy;

                this.matrix[root, dirt[i]] = ix < iy ? ix : iy;

            }

        }

    }

使用递归方法绘制聚类图

private int draw(Graphics gp,Pen pen, int height, int width, double spacingX, double treeheight, int padding, BinaryTree br)    //过递归方法制聚类

        {

            if (br.left == null && br.right == null) {

                int fontsize = spacingX / 3 < 20 ? (int)(spacingX / 3) : 20;

                Font font = new Font("宋体", fontsize, FontStyle.Regular);

                Brush bush = new SolidBrush(Color.Black);

                gp.DrawString("p" + (br.key+1), font, bush, (int)(padding + n * spacingX-fontsize/2), (int)(height - padding ));    //制底部点序号

                n++;

                return (int)((n-1)*spacingX);

            }else{

                int fontsize = spacingX / 3 < 15 ? (int)(spacingX / 3) : 15;

                int left = draw(gp,pen, height, width, spacingX, treeheight, padding, br.left); //递归左起点,返回左子的横向位置

                int right = draw(gp,pen, height, width, spacingX, treeheight, padding, br.right);   //同上

                gp.DrawLine(pen,padding+left,(int)(height-padding-treeheight*br.left.value),padding+left,(int)(height-padding-treeheight*br.value));    //制当前点到左点的线

                gp.DrawLine(pen,padding+right,(int)(height-padding-treeheight*br.right.value),padding+right,(int)(height-padding-treeheight*br.value)); //同上

                gp.DrawLine(pen,padding+left,(int)(height-padding-treeheight*br.value),padding+right,(int)(height-padding-treeheight*br.value));    //制横线

                Font font = new Font("宋体", fontsize, FontStyle.Regular);

                Brush bush = new SolidBrush(Color.Blue);

                gp.DrawString(string.Format("{0:###.##}", br.value), font, bush, padding + (left + right) / 2 - fontsize * string.Format("{0:###.##}", br.value).Length/2,

                    (int)(height - padding - treeheight * br.value + 3));   //制当前点的聚类距离

                return (left+right)/2;

            }

        }

 

pictureBox的绘制方法

private void pictureBox1_Paint(object sender, PaintEventArgs e)

        {

            int height = this.ClientRectangle.Height;

            int width = this.ClientRectangle.Width;

            int padding = 30;

            double treeHeight = (height - padding * 2) / clu.br.value;

            if (isFirst)    //第一次制是使用draw()方法,之后直接制第一次保存的bitmap

            {

                Console.WriteLine("map:" + height + "," + width);

                double spacingX = (width - padding * 2) / (clu.n - 1);

                this.bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);

                Graphics gp = Graphics.FromImage(bitmap);

                gp.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

                Pen redPen = new Pen(Color.Red, 3);

                draw(gp, redPen, height, width, spacingX, treeHeight, padding, clu.br);

                isFirst=false;

            }

            Graphics egp = e.Graphics;

            Pen greenPen=new Pen(Color.Green,1);

            greenPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Custom;

            greenPen.DashPattern = new float[] { 5, 5 };

            egp.DrawImage(bitmap, 0, 0);

            int y = mouseLocation.Y > padding ? mouseLocation.Y : padding;

            y = y < height - padding ? y : height - padding;

            Font font = new Font("宋体", 10, FontStyle.Regular);

            Brush bush = new SolidBrush(Color.Green);

            egp.DrawLine(greenPen, 0, y, width, y); //制鼠所在高度的距离

            egp.DrawString(string.Format("{0:###.##}", clu.br.value * (height - y-padding) / (height - padding * 2)), font, bush, 5, y - 13);

            /*

             **事件,生成聚类图层

             */

            if (isClick) {

                cutHeight = y;

                List<List<int>> cutLists = clu.br.getCutLists(clu.br.value * (height - y - padding) / (height - padding * 2));

                System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();

                stopwatch.Start();

                IFeatureLayer pCluLayer = parentForm.createClusteringLayer(pFeatureLayer, cutLists);    //生成聚类方法

                stopwatch.Stop();

                Console.WriteLine("运行时长" + stopwatch.Elapsed.TotalMilliseconds);

                for (int i = 0; i < parentForm.axMapControl1.LayerCount; i++) {

                    if (parentForm.axMapControl1.get_Layer(i).Name == pCluLayer.Name) {

                        parentForm.axMapControl1.DeleteLayer(i);

                    }

                }

                parentForm.axMapControl1.AddLayer(pCluLayer);

                ProportionalRenderer(pCluLayer, "cluNum", ColorToIColor(Color.Blue), 3);    //按照点个数分级显

                isClick = false;

            }

            if (cutHeight >= padding) {

                egp.DrawLine(new Pen(Color.Yellow, 2), 0, cutHeight, width, cutHeight);

            }

            egp.DrawString(string.Format("{0:###.##}", clu.br.value * (height - y - padding) / (height - padding * 2)), font, bush, 5, y - 13);

        }

创建聚类图层

public IFeatureLayer createClusteringLayer(IFeatureLayer pFeatureLayer, List<List<int>> lists)

        {

            IFeatureClass pfeatureClass = pFeatureLayer.FeatureClass;

            ISpatialReference pSpatialReference = this.axMapControl1.ActiveView.FocusMap.SpatialReference;

            IWorkspaceFactory pWF = new ShapefileWorkspaceFactory();

            IFeatureWorkspace pFeatureWorkspace = (IFeatureWorkspace)pWF.OpenFromFile("D:/",0);

            IFeatureClass clusteringClass = null;

            if (File.Exists("D:/cluster" + pFeatureLayer.Name + ".shp"))    //判断是否有该图层对应的距离聚类图层

            {

                clusteringClass = pFeatureWorkspace.OpenFeatureClass("cluster" + pFeatureLayer.Name + ".shp");

 

                IFeatureCursor clusteringCursor = clusteringClass.Update(null, true);

                IFeature clusteringFeature = clusteringCursor.NextFeature();

                while (clusteringFeature != null)   //除上一次聚类的点

                {

                    clusteringCursor.DeleteFeature();

                    clusteringFeature = clusteringCursor.NextFeature();

                }

            }

            else

            {

                //建聚类点图层

                IFields pFields = new FieldsClass();

                IFieldsEdit pFieldsEdit = (IFieldsEdit)pFields;

 

                IField pField = new FieldClass();

                IFieldEdit pFieldEdit = (IFieldEdit)pField;

                pFieldEdit.Name_2 = "SHAPE";

                pFieldEdit.Type_2 = esriFieldType.esriFieldTypeGeometry;

                IGeometryDefEdit pGeoDef = new GeometryDefClass();

                IGeometryDefEdit pGeoDefEdit = (IGeometryDefEdit)pGeoDef;

                pGeoDefEdit.GeometryType_2 = esriGeometryType.esriGeometryPoint;

                pGeoDefEdit.SpatialReference_2 = pSpatialReference;

                pFieldEdit.GeometryDef_2 = pGeoDef;

                pFieldsEdit.AddField(pField);

 

                pField = new FieldClass();

                pFieldEdit = (IFieldEdit)pField;

                pFieldEdit.Name_2 = "cluNum";

                pFieldEdit.Type_2 = esriFieldType.esriFieldTypeInteger;

                pFieldsEdit.AddField(pField);

 

                clusteringClass = pFeatureWorkspace.CreateFeatureClass("cluster" + pFeatureLayer.Name + ".shp", pFields, null, null, esriFeatureType.esriFTSimple, "SHAPE", "");

            }

            IFeatureCursor cursor = pfeatureClass.Search(null, true);

            IFeature pFeature = cursor.NextFeature();

            List<IPoint> pointsColl = new List<IPoint>();

            while (pFeature != null)

            {

                //Console.WriteLine((pFeature.Shape as IPoint).X);

                IPoint pt = new PointClass();

                pt.X = (pFeature.Shape as IPoint).X;

                pt.Y = (pFeature.Shape as IPoint).Y;

                pointsColl.Add(pt);

                pFeature = cursor.NextFeature();

            }

            System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();

            stopwatch.Start();

            IFeatureCursor insertCursor = clusteringClass.Insert(true);

            foreach(var list in lists){

                double aveX = list.Average(i => pointsColl[i].X);

                double aveY = list.Average(i => pointsColl[i].Y);

                IPoint pt = new PointClass();

                pt.X = aveX;

                pt.Y = aveY;

                IFeatureBuffer pAddFeature = clusteringClass.CreateFeatureBuffer();

                pAddFeature.Shape = pt;

                pAddFeature.set_Value(pAddFeature.Fields.FindField("cluNum"), list.Count);

                insertCursor.InsertFeature(pAddFeature);

            };

            insertCursor.Flush();

            IFeatureLayer pCluLayer = new FeatureLayerClass();

            pCluLayer.Name = "cluster" + pFeatureLayer.Name;

            pCluLayer.FeatureClass = clusteringClass;

            stopwatch.Stop();

            Console.WriteLine("加入:" + stopwatch.Elapsed.TotalMilliseconds);

            return pCluLayer;

        }

聚类图层分级显示

public void ProportionalRenderer(IFeatureLayer featLayer, string fieldName, IColor pColor, double count)

        {

            IProportionalSymbolRenderer psrender = new ProportionalSymbolRendererClass();

            psrender.Field = fieldName;

            psrender.ValueUnit = esriUnits.esriUnknownUnits;

            psrender.ValueRepresentation = esriValueRepresentations.esriValueRepUnknown;

            //选择渲染的式,与 minsymbol比填内容,否没有效果

            ISimpleMarkerSymbol markersym = new SimpleMarkerSymbol();

            markersym.Size = count;

            markersym.Style = esriSimpleMarkerStyle.esriSMSCircle;

            markersym.Color = pColor;

            psrender.MinSymbol = markersym as ISymbol;

            //IFeatureLayer featLayer = featLayer;

            IGeoFeatureLayer geofeat = featLayer as IGeoFeatureLayer;

            ICursor cursor = ((ITable)featLayer).Search(null, true);

            IDataStatistics datastat = new DataStatisticsClass();

            datastat.Cursor = cursor;

            datastat.Field = fieldName;//千万不能忽

            IStatisticsResults statisticsResult;

 

            try

            {

                statisticsResult = datastat.Statistics;

                psrender.MinDataValue = statisticsResult.Minimum + 0.1;

                psrender.MaxDataValue = statisticsResult.Maximum;

                ////background

                IFillSymbol fillsym = new SimpleFillSymbolClass();

                fillsym.Color = getcolor(201, 201, 251);

                ILineSymbol linesym = new SimpleLineSymbolClass();

                linesym.Width = 2;

                fillsym.Outline = linesym;

                psrender.BackgroundSymbol = fillsym;

                psrender.LegendSymbolCount = 2;//legend的数量

                psrender.CreateLegendSymbols();//TOC的legend

                geofeat.Renderer = (IFeatureRenderer)psrender;

            }

            catch

            {

                MessageBox.Show("错误选择的属性不是数型!");

            }

        }

功能实现效果图

图片3

资源下载

资源链接

 

猜你喜欢

转载自blog.csdn.net/snowfly1153/article/details/86575327