聲明:
1.當初自己在找緩衝區產生演算法時,非常難找,網上實現的代碼更是沒有,只有寥寥的幾句理論,在N天之後在網上搜到了兩篇論文,是《一種GIS緩衝區向量產生演算法及實現》和《GIS緩衝區和疊加分析》,自己根據自己的理解,寫出了這個實現的代碼,希望能給需要的人員一點協助,當然,寫的非常簡單,沒有處理銳角情況(下一步計劃將會實現),也沒有處理自相交情況(正在解決......),大家若是有什麼好的意見,請告訴我。謝謝。
2.本代碼只實現了產生緩衝區邊界點,沒有處理銳角情況(下一步計劃將會實現),也沒有處理自相交情況(正在解決......),希望大家能給寫協助。謝謝。
3.代碼之中有很多的不足,和考慮不到的情況,希望大家能給我一些指點,謝謝。
/***********************************************************************
* 文檔作者: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);
leftBufferCoords = leftBufferCoords.Substring(leftBufferCoords.IndexOf(';') + 1);
coords.Reverse();
string rightBufferCoords = GetLeftBufferEdgeCoords(coords, radius);
rightBufferCoords = rightBufferCoords.Substring(rightBufferCoords.IndexOf(';') + 1);
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 (Math.Abs(l) < 1E-15)//由於C#中double的精度位元為15位,第16位是不準確的,加上此調整值,可以在一定程度上減小第16位隨機數的影響。
{
coords.Remove(coords[i]);//當三點共線時刪除這個中間點
}
else 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)//加入了一個調整C#double類型的值
{
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
}
}