【源代码】GIS 点、线缓冲区生成算法的C#实现(V0.95)

原创 【源代码】GIS 点、线缓冲区生成算法的C#实现(V0.95)

今天将源代码都贴上,请大家多多批评赐教。

广泛希望大家能指出一些错误,谢谢。

以后我会抽空学习一下特殊情况的处理,然后加上去,希望大家多多支持。

源代码结构:

源代码分别附上:

1.MathTool.cs

/*******************************************************
 * 文档作者:dxj
 * 创建时间:2010.3.7
 * 文档说明:
 *      在GIS中常用的通用数学函数。
 ******************************************************/
using System;
using System.Collections.Generic;
using System.Text;

using DXJ.Teresa.GIS.GeoObject;

namespace DXJ.Teresa.GIS.Utility
{
    /// <summary>
    /// 常用的通用数学函数
    /// </summary>
    public static class MathTool
    {
        /// <summary>
        /// 获取由两个点所形成的向量的象限角度
        /// </summary>
        /// <param name="preCoord">第一个点的坐标</param>
        /// <param name="nextCoord">第二个点的坐标</param>
        /// <returns></returns>
        public static double GetQuadrantAngle(Coordinate preCoord, Coordinate nextCoord)
        {
            return GetQuadrantAngle(nextCoord.X - preCoord.X, nextCoord.Y - preCoord.Y);
        }
        /// <summary>
        /// 由增量X和增量Y所形成的向量的象限角度
        /// </summary>
        /// <param name="x">增量X</param>
        /// <param name="y">增量Y</param>
        /// <returns>象限角</returns>
        public static double GetQuadrantAngle(double x, double y)
        {
            double theta = Math.Atan(y / x);
            if (x > 0 && y > 0) return theta;
            if (x > 0 && y < 0) return Math.PI * 2 + theta;
            if (x < 0 && y > 0) return theta + Math.PI;
            if (x < 0 && y < 0) return theta + Math.PI;
            return theta;
        }
        /// <summary>
        /// 获取由相邻的三个点所形成的两个向量之间的夹角
        /// </summary>
        /// <param name="preCoord"></param>
        /// <param name="midCoord"></param>
        /// <param name="nextCoord"></param>
        /// <returns></returns>
        public static double GetIncludedAngel(Coordinate preCoord, Coordinate midCoord, Coordinate nextCoord)
        {
            double innerProduct = (midCoord.X - preCoord.X) * (nextCoord.X - midCoord.X) + (midCoord.Y - preCoord.Y) * (nextCoord.Y - midCoord.Y);
            double mode1 = Math.Sqrt(Math.Pow((midCoord.X - preCoord.X), 2.0) + Math.Pow((midCoord.Y - preCoord.Y), 2.0));
            double mode2 = Math.Sqrt(Math.Pow((nextCoord.X - midCoord.X), 2.0) + Math.Pow((nextCoord.Y - midCoord.Y), 2.0));
            return Math.Acos(innerProduct / (mode1 * mode2));
        }
        /// <summary>
        /// 获取由两个点所形成的向量的模(长度)
        /// </summary>
        /// <param name="preCoord">第一个点</param>
        /// <param name="nextCoord">第二个点</param>
        /// <returns>由两个点所形成的向量的模(长度)</returns>
        public static double GetDistance(Coordinate preCoord, Coordinate nextCoord)
        {
            return Math.Sqrt(Math.Pow((nextCoord.X - preCoord.X), 2) + Math.Pow((nextCoord.Y - preCoord.Y), 2));
        }
    }
}

2.Coordinate.cs

/*******************************************************
 * 文档作者:dxj
 * 创建时间:2010.3.7
 * 文档说明:
 *      本文件是坐标类。
 ******************************************************/
using System;
using System.Collections.Generic;
using System.Text;

namespace DXJ.Teresa.GIS.GeoObject
{
    /// <summary>
    /// GEOObject坐标类
    /// </summary>
    public class Coordinate
    {
        #region Private Members
        private double _x = 0.0;
        private double _y = 0.0;
        #endregion

        #region Public Construtors
        public Coordinate()
        {
            //
        }
        public Coordinate(double x, double y)
        {
            this._x = x;
            this._y = y;
        }
        public Coordinate(string x, string y)
        {
            try
            {
                this._x = x.Trim() == "" ? 0.0 : Convert.ToDouble(x.Trim());
                this._y = y.Trim() == "" ? 0.0 : Convert.ToDouble(y.Trim());
            }
            catch (Exception)
            {

            }
        }
        public Coordinate(string coord)
        {
            if (coord.Trim().Length > 0)
            {
                string[] coords = coord.Split(new char[] { ',' });
                if (coords.Length == 2)
                {
                    this._x = coords[0].Trim().Length > 0 ? Convert.ToDouble(coords[0].Trim()) : 0.0;
                    this._y = coords[1].Trim().Length > 0 ? Convert.ToDouble(coords[1].Trim()) : 0.0;
                }
            }
        }
        #endregion

        #region Public Properities
        public double X
        {
            get
            {
                return this._x;
            }
            set
            {
                this._x = value;
            }
        }
        public double Y
        {
            get
            {
                return this._y;
            }
            set
            {
                this._y = value;
            }
        }
        #endregion

        #region Public Override Methods
        public override string ToString()
        {
            return "(" + this._x.ToString() + "," + this._y.ToString() + ")";
        }
        #endregion
    }
}

3.PointBuffer.cs

/************************************************************
 *  文档作者:dxj
 *  创建时间:2010.3.7
 *  文档说明:
 *      本文件是点缓冲区边界生成算法的C#实现。
 *
 ************************************************************/

using System;
using System.Collections.Generic;
using System.Text;

using DXJ.Teresa.GIS.GeoObject;

namespace DXJ.Teresa.GIS.Buffer
{
    /// <summary>
    /// 点缓冲区边界生成算法
    /// </summary>
    public class PointBuffer
    {
        #region Public Members
        /// <summary>
        /// 用于近似表示点缓冲区边界的内接正多边形的边数N
        /// </summary>
        public static int N = 12;
        #endregion

        #region Public Static Methods
        /// <summary>
        /// 根据一个给定点的坐标,生成基于这个点的点缓冲区边界点坐标串(逆时针)
        /// </summary>
        /// <param name="center">一个给定点的坐标</param>
        /// <param name="radius">缓冲区的半径</param>
        /// <returns>点缓冲区边界点坐标串(逆时针)</returns>
        public static string GetBufferEdgeCoords(Coordinate center, double radius)
        {
            double alpha = 0.0;//Math.PI / 6;
            double gamma = (2 * Math.PI) / N;

            StringBuilder strCoords = new StringBuilder();
            double x = 0.0, y = 0.0;
            for (double phi = 0; phi < (N - 1) * gamma; phi += gamma)
            {
                x = center.X + radius * Math.Cos(alpha + phi);
                y = center.Y + radius * Math.Sin(alpha + phi);
                if (strCoords.Length > 0) strCoords.Append(";");
                strCoords.Append(x.ToString()+","+y.ToString());
            }
            return strCoords.ToString();
        }
        #endregion
    }
}
4.PolylineBuffer.cs

/***********************************************************************
 *  文档作者:dxj
 *  创建时间:2010.3.7 20:17
 *  文档说明:
 *      本文件是线缓冲区边界生成算法的C#实现。
 **********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;

using DXJ.Teresa.GIS.GeoObject;
using DXJ.Teresa.GIS.Utility;

namespace DXJ.Teresa.GIS.Buffer
{
    /// <summary>
    /// 线缓冲区边界生成算法
    /// </summary>
    public class PolylineBuffer
    {
        /// <summary>
        /// 根据给定的一系列有顺序的坐标,逆时针生成缓冲区的边界坐标。
        /// </summary>
        /// <param name="strPolyLineCoords">一系列有顺序的坐标</param>
        /// <param name="radius">缓冲区半径</param>
        /// <returns>缓冲区的边界坐标</returns>
        public static string GetBufferEdgeCoords(string strPolyLineCoords, double radius)
        {
            //参数处理
            if (strPolyLineCoords.Trim().Length < 1) return "";
            string[] strCoords = strPolyLineCoords.Split(new char[] { ';' });
            List<Coordinate> coords = new List<Coordinate>();
            foreach (string coord in strCoords)
            {
                coords.Add(new Coordinate(coord));
            }

            //分别生成左侧和右侧的缓冲区边界点坐标串
            string leftBufferCoords = GetLeftBufferEdgeCoords(coords, radius);
            coords.Reverse();
            string rightBufferCoords = GetLeftBufferEdgeCoords(coords, radius);
            return leftBufferCoords + ";" + rightBufferCoords;
        }
        #region Private Methods
        /// <summary>
        /// 根据给定的一系列有顺序的坐标,逆时针生成轴线左侧的缓冲区边界点
        /// </summary>
        /// <param name="coords">一系列有顺序的坐标</param>
        /// <param name="radius">缓冲区半径</param>
        /// <returns>缓冲区的边界坐标</returns>
        private static string GetLeftBufferEdgeCoords(IList<Coordinate> coords, double radius)
        {
            //参数处理
            if (coords.Count < 1) return "";
            else if (coords.Count < 2) return PointBuffer.GetBufferEdgeCoords(coords[0], radius);

            //计算时所需变量
            double alpha = 0.0;//向量绕起始点沿顺时针方向旋转到X轴正半轴所扫过的角度
            double delta = 0.0;//前后线段所形成的向量之间的夹角
            double l = 0.0;//前后线段所形成的向量的叉积

            //辅助变量
            StringBuilder strCoords = new StringBuilder();
            double startRadian = 0.0;
            double endRadian = 0.0;
            double beta = 0.0;
            double x = 0.0, y = 0.0;

            //第一节点的缓冲区
            {
                alpha = MathTool.GetQuadrantAngle(coords[0], coords[1]);
                startRadian = alpha + Math.PI;
                endRadian = alpha + (3 * Math.PI) / 2;
                strCoords.Append(GetBufferCoordsByRadian(coords[0], startRadian, endRadian, radius));
            }

            //中间节点
            for (int i = 1; i < coords.Count - 1; i++)
            {
                alpha = MathTool.GetQuadrantAngle(coords[i], coords[i + 1]);
                delta = MathTool.GetIncludedAngel(coords[i - 1], coords[i], coords[i + 1]);
                l = GetVectorProduct(coords[i - 1], coords[i], coords[i + 1]);
                if (l > 0)
                {
                    startRadian = alpha + (3 * Math.PI) / 2 - delta;
                    endRadian = alpha + (3 * Math.PI) / 2;
                    if (strCoords.Length > 0) strCoords.Append(";");
                    strCoords.Append(GetBufferCoordsByRadian(coords[i], startRadian, endRadian, radius));
                }
                else if (l < 0)
                {
                    beta = alpha - (Math.PI - delta) / 2;
                    x = coords[i].X + radius * Math.Cos(beta);
                    y = coords[i].Y + radius * Math.Sin(beta);
                    if (strCoords.Length > 0) strCoords.Append(";");
                    strCoords.Append(x.ToString() + "," + y.ToString());
                }
            }

            //最后一个点
            {
                alpha = MathTool.GetQuadrantAngle(coords[coords.Count - 2], coords[coords.Count - 1]);
                startRadian = alpha + (3 * Math.PI) / 2;
                endRadian = alpha + 2 * Math.PI;
                if (strCoords.Length > 0) strCoords.Append(";");
                strCoords.Append(GetBufferCoordsByRadian(coords[coords.Count - 1], startRadian, endRadian, radius));
            }

            return strCoords.ToString();
        }

        /// <summary>
        /// 获取指定弧度范围之间的缓冲区圆弧拟合边界点
        /// </summary>
        /// <param name="center">指定拟合圆弧的原点</param>
        /// <param name="startRadian">开始弧度</param>
        /// <param name="endRadian">结束弧度</param>
        /// <param name="radius">缓冲区半径</param>
        /// <returns>缓冲区的边界坐标</returns>
        private static string GetBufferCoordsByRadian(Coordinate center, double startRadian, double endRadian, double radius)
        {
            double gamma = Math.PI / 6;

            StringBuilder strCoords = new StringBuilder();
            double x = 0.0, y = 0.0;
            for (double phi = startRadian; phi <= endRadian + 0.000000000000001; phi += gamma)
            {
                x = center.X + radius * Math.Cos(phi);
                y = center.Y + radius * Math.Sin(phi);
                if (strCoords.Length > 0) strCoords.Append(";");
                strCoords.Append(x.ToString() + "," + y.ToString());
            }
            return strCoords.ToString();
        }
        /// <summary>
        /// 获取相邻三个点所形成的两个向量的交叉乘积
        /// </summary>
        /// <param name="preCoord">第一个节点坐标</param>
        /// <param name="midCoord">第二个节点坐标</param>
        /// <param name="nextCoord">第三个节点坐标</param>
        /// <returns>相邻三个点所形成的两个向量的交叉乘积</returns>
        private static double GetVectorProduct(Coordinate preCoord, Coordinate midCoord, Coordinate nextCoord)
        {
            return (midCoord.X - preCoord.X) * (nextCoord.Y - midCoord.Y) - (nextCoord.X - midCoord.X) * (midCoord.Y - preCoord.Y);
        }
        #endregion
    }
}

4.测试代码

/************************************************************
 *  文档作者:dxj
 *  创建时间:2010.3.7
 *  文档说明:
 *      本文件是测试程序,根据一系列点生成边界值。
 *
 ************************************************************/
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

using DXJ.Teresa.GIS.Buffer;
using DXJ.Teresa.GIS.GeoObject;

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        //Coordinate coord = new Coordinate(117.9761419921875,36.7177825);
        double radius = 0.0058633144143721562925090295041981;
        //string strCoords = PointBuffer.GetBufferEdgeCoords(coord, radius);
        //Response.Write(strCoords);

        string coords = "117.3469162109375,36.552475;118.77600527343749,36.56047375;118.49871933593751,37.11772;117.5442158203125,37.000405;117.680192578125,37.405675;119.1386099609375,37.15238125;119.162605859375,36.45649;118.89865097656251,36.28851625;118.63736230468748,36.19253125;118.5093841796875,36.189865";
        string strCoords = PolylineBuffer.GetBufferEdgeCoords(coords, radius);
        Response.Write(strCoords);
    }
}

【注:】本算法是我根据网上的一篇论文提供的一种思路,自己编写代码实现的缓冲区算法,编写本算法的初衷只是为了服务自己的项目,现在发布出来也是为了方便大家,以使大家免得再周折一番,但请务必不要作为学术论文发表。

下载:https://files.cnblogs.com/longshaoye/BufferV1.0.0.7z

原文地址:https://www.cnblogs.com/longshaoye/p/1794320.html