05-几何运算操作符

第五章:几何运算操作符

5.1 概述

几何运算操作符用于对几何对象进行变换和计算,生成新的几何对象或计算几何属性。这些操作符是空间分析的核心工具。

5.1.1 操作符分类

类别 操作符 说明
创建型 BufferOperator 创建缓冲区
创建型 ConvexHullOperator 计算凸包
创建型 BoundaryOperator 获取边界
创建型 OffsetOperator 创建偏移几何
计算型 AreaOperator 计算面积
计算型 LengthOperator 计算长度
计算型 CentroidOperator 计算质心
变换型 SimplifyOperator 简化几何
变换型 GeneralizeOperator 概化几何
变换型 DensifyOperator 密化几何
变换型 ClipOperator 裁剪几何

5.2 Buffer(缓冲区)

5.2.1 概念

缓冲区操作在几何对象周围创建指定距离的区域。这是 GIS 分析中最常用的操作之一。

应用场景

  • 创建服务区域(如配送范围)
  • 环境影响分析(如噪音影响范围)
  • 安全距离分析(如河流泛滥区)

5.2.2 API

public class BufferOperator : IGeometryOperator<Polygon>
{
    public static BufferOperator Instance { get; }
    
    public Polygon Execute(Geometry geometry, double distance, 
                          SpatialReference? spatialRef = null);
}

5.2.3 使用示例

using Esri.Geometry.Core;
using Esri.Geometry.Core.Geometries;

// 点缓冲区
var point = new Point(100, 100);
var buffer = GeometryEngine.Buffer(point, 10);

Console.WriteLine($"缓冲区类型:{buffer.Type}");  // Polygon
Console.WriteLine($"环数:{buffer.RingCount}");   // 1

// 包络矩形缓冲区
var envelope = new Envelope(0, 0, 50, 30);
var envBuffer = GeometryEngine.Buffer(envelope, 5);

// 缓冲区扩展了各方向 5 个单位
var bufferEnv = envBuffer.GetEnvelope();
Console.WriteLine($"缓冲区范围:({bufferEnv.XMin}, {bufferEnv.YMin}) - ({bufferEnv.XMax}, {bufferEnv.YMax})");
// 输出:(-5, -5) - (55, 35)

5.2.4 实现原理

当前实现为简化版本,创建矩形缓冲区:

private Polygon BufferPoint(Point point, double distance)
{
    // 在点周围创建正方形缓冲区
    var polygon = new Polygon();
    var ring = new List<Point>
    {
        new Point(point.X - distance, point.Y - distance),
        new Point(point.X + distance, point.Y - distance),
        new Point(point.X + distance, point.Y + distance),
        new Point(point.X - distance, point.Y + distance),
        new Point(point.X - distance, point.Y - distance)  // 闭合
    };
    polygon.AddRing(ring);
    return polygon;
}

private Polygon BufferEnvelope(Envelope envelope, double distance)
{
    // 在各个方向上扩展包络
    var polygon = new Polygon();
    var ring = new List<Point>
    {
        new Point(envelope.XMin - distance, envelope.YMin - distance),
        new Point(envelope.XMax + distance, envelope.YMin - distance),
        new Point(envelope.XMax + distance, envelope.YMax + distance),
        new Point(envelope.XMin - distance, envelope.YMax + distance),
        new Point(envelope.XMin - distance, envelope.YMin - distance)
    };
    polygon.AddRing(ring);
    return polygon;
}

5.2.5 应用示例

// 场景:创建商店的配送范围
var storeLocation = new Point(116.4074, 39.9042);
var deliveryRadius = 0.02;  // 约 2 公里(经纬度)

var deliveryZone = GeometryEngine.Buffer(storeLocation, deliveryRadius);

// 检查客户是否在配送范围内
var customerLocation = new Point(116.41, 39.91);
bool canDeliver = GeometryEngine.Contains(deliveryZone, customerLocation);
Console.WriteLine($"可以配送:{canDeliver}");

5.3 ConvexHull(凸包)

5.3.1 概念

凸包是包含给定点集的最小凸多边形。可以想象为用橡皮筋套住一组钉子形成的形状。

应用场景

  • 计算点集的最小外接多边形
  • 碰撞检测的预处理
  • 地理范围估计

5.3.2 API

public class ConvexHullOperator : IGeometryOperator<Geometry>
{
    public static ConvexHullOperator Instance { get; }
    
    public Geometry Execute(Geometry geometry, 
                           SpatialReference? spatialRef = null);
}

5.3.3 使用示例

using Esri.Geometry.Core;
using Esri.Geometry.Core.Geometries;

// 多点的凸包
var multiPoint = new MultiPoint();
multiPoint.Add(new Point(0, 0));
multiPoint.Add(new Point(10, 0));
multiPoint.Add(new Point(5, 5));
multiPoint.Add(new Point(10, 10));
multiPoint.Add(new Point(0, 10));
multiPoint.Add(new Point(5, 3));  // 内部点

var hull = GeometryEngine.ConvexHull(multiPoint);

if (hull is Polygon polygon)
{
    Console.WriteLine($"凸包顶点数:{polygon.GetRing(0).Count}");
    // 内部点 (5, 3) 不在凸包边界上
}

// 折线的凸包
var polyline = new Polyline();
polyline.AddPath(new List<Point>
{
    new Point(0, 0),
    new Point(5, 10),
    new Point(10, 0)
});

var lineHull = GeometryEngine.ConvexHull(polyline);  // 返回三角形

5.3.4 实现原理:Graham 扫描算法

private List<Point> GrahamScan(List<Point> points)
{
    if (points.Count < 3) return points;

    // 1. 找到 Y 坐标最小的点(最低点)
    var lowestPoint = points.OrderBy(p => p.Y).ThenBy(p => p.X).First();

    // 2. 按极角排序其他点
    var sortedPoints = points
        .Where(p => p != lowestPoint)
        .OrderBy(p => Math.Atan2(p.Y - lowestPoint.Y, p.X - lowestPoint.X))
        .ThenBy(p => p.Distance(lowestPoint))
        .ToList();

    // 3. 构建凸包
    var hull = new List<Point> { lowestPoint };

    foreach (var point in sortedPoints)
    {
        // 移除产生右转的点
        while (hull.Count > 1 && !IsLeftTurn(hull[hull.Count - 2], hull[hull.Count - 1], point))
        {
            hull.RemoveAt(hull.Count - 1);
        }
        hull.Add(point);
    }

    return hull;
}

private bool IsLeftTurn(Point p1, Point p2, Point p3)
{
    // 使用叉积判断是否左转
    var cross = (p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X);
    return cross > 0;
}

算法复杂度:O(n log n),其中 n 是点的数量。

5.3.5 返回类型

根据输入几何的不同,凸包返回不同类型:

输入点数 返回类型
0 空 Polygon
1 Point
2 Line
≥3 Polygon

5.4 Area 和 Length(面积和长度)

5.4.1 概念

  • Area(面积):计算多边形或包络矩形的二维面积
  • Length(长度):计算折线的总长度或多边形的周长

5.4.2 API

public class AreaOperator : IGeometryOperator<double>
{
    public static AreaOperator Instance { get; }
    
    public double Execute(Geometry geometry, 
                         SpatialReference? spatialRef = null);
}

public class LengthOperator : IGeometryOperator<double>
{
    public static LengthOperator Instance { get; }
    
    public double Execute(Geometry geometry, 
                         SpatialReference? spatialRef = null);
}

5.4.3 使用示例

using Esri.Geometry.Core;
using Esri.Geometry.Core.Geometries;

// 计算多边形面积
var polygon = new Polygon();
polygon.AddRing(new List<Point>
{
    new Point(0, 0),
    new Point(10, 0),
    new Point(10, 10),
    new Point(0, 10),
    new Point(0, 0)
});

double area = GeometryEngine.Area(polygon);
Console.WriteLine($"多边形面积:{area}");  // 100

// 计算包络矩形面积
var envelope = new Envelope(0, 0, 100, 50);
double envArea = GeometryEngine.Area(envelope);
Console.WriteLine($"包络面积:{envArea}");  // 5000

// 计算折线长度
var polyline = new Polyline();
polyline.AddPath(new List<Point>
{
    new Point(0, 0),
    new Point(3, 4),  // 距离原点 5
    new Point(3, 14)  // 向上 10
});

double length = GeometryEngine.Length(polyline);
Console.WriteLine($"折线长度:{length}");  // 15

// 计算多边形周长
double perimeter = GeometryEngine.Length(polygon);
Console.WriteLine($"多边形周长:{perimeter}");  // 40

5.4.4 面积计算:鞋带公式

public double Execute(Geometry geometry, SpatialReference? spatialRef = null)
{
    if (geometry is Polygon polygon)
    {
        double area = 0;
        foreach (var ring in polygon.GetRings())
        {
            var count = ring.Count;
            if (count < 3) continue;
            
            double ringArea = 0;
            for (var i = 0; i < count - 1; i++)
            {
                // 鞋带公式
                ringArea += ring[i].X * ring[i + 1].Y - ring[i + 1].X * ring[i].Y;
            }
            // 闭合环
            ringArea += ring[count - 1].X * ring[0].Y - ring[0].X * ring[count - 1].Y;
            area += Math.Abs(ringArea) * 0.5;
        }
        return area;
    }
    
    if (geometry is Envelope envelope)
    {
        return envelope.Width * envelope.Height;
    }
    
    return 0;  // 点和线没有面积
}

5.4.5 长度计算

public double Execute(Geometry geometry, SpatialReference? spatialRef = null)
{
    if (geometry is Polyline polyline)
    {
        double length = 0;
        foreach (var path in polyline.GetPaths())
        {
            for (var i = 0; i < path.Count - 1; i++)
            {
                length += path[i].Distance(path[i + 1]);
            }
        }
        return length;
    }
    
    if (geometry is Polygon polygon)
    {
        double length = 0;
        foreach (var ring in polygon.GetRings())
        {
            for (var i = 0; i < ring.Count - 1; i++)
            {
                length += ring[i].Distance(ring[i + 1]);
            }
            // 闭合段
            if (ring.Count > 1)
            {
                length += ring[ring.Count - 1].Distance(ring[0]);
            }
        }
        return length;
    }
    
    return 0;  // 点没有长度
}

5.5 Simplify(简化)

5.5.1 概念

使用 Douglas-Peucker 算法减少几何对象的顶点数量,同时保持其基本形状。常用于减少数据量,提高渲染性能。

应用场景

  • 地图缩放时简化显示
  • 减少存储和传输的数据量
  • 提高地图渲染速度

5.5.2 API

public class SimplifyOperator : IGeometryOperator<Geometry>
{
    public static SimplifyOperator Instance { get; }
    
    public Geometry Execute(Geometry geometry, double tolerance, 
                           SpatialReference? spatialRef = null);
}

5.5.3 使用示例

using Esri.Geometry.Core;
using Esri.Geometry.Core.Geometries;

// 创建包含多个点的折线
var polyline = new Polyline();
polyline.AddPath(new List<Point>
{
    new Point(0, 0),
    new Point(1, 0.1),   // 几乎在直线上
    new Point(2, 0.05),  // 几乎在直线上
    new Point(3, 0.08),  // 几乎在直线上
    new Point(4, 0),
    new Point(5, 5),     // 明显转折
    new Point(6, 4.9),   // 几乎在直线上
    new Point(7, 5.1),   // 几乎在直线上
    new Point(8, 5),
    new Point(9, 0),
    new Point(10, 0)
});

Console.WriteLine($"原始点数:{polyline.GetPath(0).Count}");  // 11

// 使用容差 0.2 简化
var simplified = GeometryEngine.Simplify(polyline, 0.2);

if (simplified is Polyline simplifiedLine)
{
    Console.WriteLine($"简化后点数:{simplifiedLine.GetPath(0).Count}");  // 减少
}

// 使用更大的容差会更激进地简化
var moreSimplified = GeometryEngine.Simplify(polyline, 1.0);

5.5.4 实现原理:Douglas-Peucker 算法

private List<Point> DouglasPeucker(List<Point> points, double tolerance)
{
    if (points.Count < 3) return points;

    // 1. 找到距离首尾连线最远的点
    double maxDistance = 0;
    int maxIndex = 0;
    int end = points.Count - 1;

    for (int i = 1; i < end; i++)
    {
        double distance = PerpendicularDistance(points[i], points[0], points[end]);
        if (distance > maxDistance)
        {
            maxDistance = distance;
            maxIndex = i;
        }
    }

    // 2. 如果最大距离超过容差,递归处理两段
    if (maxDistance > tolerance)
    {
        var left = DouglasPeucker(points.GetRange(0, maxIndex + 1), tolerance);
        var right = DouglasPeucker(points.GetRange(maxIndex, end - maxIndex + 1), tolerance);

        // 合并结果(去掉重复的中间点)
        var result = new List<Point>(left);
        result.AddRange(right.Skip(1));
        return result;
    }

    // 3. 否则只保留端点
    return new List<Point> { points[0], points[end] };
}

// 计算点到线段的垂直距离
private double PerpendicularDistance(Point point, Point lineStart, Point lineEnd)
{
    double dx = lineEnd.X - lineStart.X;
    double dy = lineEnd.Y - lineStart.Y;
    double mag = Math.Sqrt(dx * dx + dy * dy);
    
    if (mag < GeometryConstants.Epsilon)
        return point.Distance(lineStart);

    // 参数化位置
    double u = ((point.X - lineStart.X) * dx + (point.Y - lineStart.Y) * dy) / (mag * mag);

    if (u < 0) return point.Distance(lineStart);
    if (u > 1) return point.Distance(lineEnd);

    // 最近点
    double ix = lineStart.X + u * dx;
    double iy = lineStart.Y + u * dy;
    return point.Distance(new Point(ix, iy));
}

算法复杂度:平均 O(n log n),最坏 O(n²)。

5.6 Centroid(质心)

5.6.1 概念

质心是几何对象的几何中心点(质量中心)。

5.6.2 API

public class CentroidOperator : IGeometryOperator<Point>
{
    public static CentroidOperator Instance { get; }
    
    public Point Execute(Geometry geometry, 
                        SpatialReference? spatialRef = null);
}

5.6.3 使用示例

using Esri.Geometry.Core;
using Esri.Geometry.Core.Geometries;

// 多边形的质心
var polygon = new Polygon();
polygon.AddRing(new List<Point>
{
    new Point(0, 0),
    new Point(10, 0),
    new Point(10, 10),
    new Point(0, 10),
    new Point(0, 0)
});

var centroid = GeometryEngine.Centroid(polygon);
Console.WriteLine($"质心:({centroid.X}, {centroid.Y})");  // (5, 5)

// 多点的质心(平均值)
var multiPoint = new MultiPoint();
multiPoint.Add(new Point(0, 0));
multiPoint.Add(new Point(10, 0));
multiPoint.Add(new Point(10, 10));
multiPoint.Add(new Point(0, 10));

var mpCentroid = GeometryEngine.Centroid(multiPoint);
Console.WriteLine($"多点质心:({mpCentroid.X}, {mpCentroid.Y})");  // (5, 5)

// 三角形的质心
var triangle = new Polygon();
triangle.AddRing(new List<Point>
{
    new Point(0, 0),
    new Point(6, 0),
    new Point(3, 6),
    new Point(0, 0)
});

var triangleCentroid = GeometryEngine.Centroid(triangle);
Console.WriteLine($"三角形质心:({triangleCentroid.X}, {triangleCentroid.Y})");  // (3, 2)

5.6.4 实现原理

public Point Execute(Geometry geometry, SpatialReference? spatialRef = null)
{
    if (geometry.IsEmpty)
        return new Point();

    switch (geometry.Type)
    {
        case GeometryType.Point:
            return (Point)geometry;

        case GeometryType.MultiPoint:
            return CalculateMultiPointCentroid((MultiPoint)geometry);

        case GeometryType.Polygon:
            return CalculatePolygonCentroid((Polygon)geometry);

        case GeometryType.Envelope:
            return ((Envelope)geometry).Center;

        default:
            // 对于其他类型,返回包络的中心
            return geometry.GetEnvelope().Center;
    }
}

private Point CalculateMultiPointCentroid(MultiPoint multiPoint)
{
    double sumX = 0, sumY = 0;
    int count = 0;

    foreach (var point in multiPoint.GetPoints())
    {
        sumX += point.X;
        sumY += point.Y;
        count++;
    }

    return new Point(sumX / count, sumY / count);
}

private Point CalculatePolygonCentroid(Polygon polygon)
{
    // 使用多边形质心公式
    double sumX = 0, sumY = 0, sumArea = 0;

    foreach (var ring in polygon.GetRings())
    {
        for (int i = 0; i < ring.Count - 1; i++)
        {
            double cross = ring[i].X * ring[i + 1].Y - ring[i + 1].X * ring[i].Y;
            sumX += (ring[i].X + ring[i + 1].X) * cross;
            sumY += (ring[i].Y + ring[i + 1].Y) * cross;
            sumArea += cross;
        }
    }

    double area = sumArea / 2;
    return new Point(sumX / (6 * area), sumY / (6 * area));
}

5.7 Boundary(边界)

5.7.1 概念

边界操作返回几何对象的边界几何。根据 OGC 规范:

  • 点的边界是空
  • 线的边界是端点
  • 多边形的边界是其环

5.7.2 API

public class BoundaryOperator : IGeometryOperator<Geometry>
{
    public static BoundaryOperator Instance { get; }
    
    public Geometry Execute(Geometry geometry, 
                           SpatialReference? spatialRef = null);
}

5.7.3 使用示例

using Esri.Geometry.Core;
using Esri.Geometry.Core.Geometries;

// 点的边界是空
var point = new Point(10, 20);
var pointBoundary = GeometryEngine.Boundary(point);
Console.WriteLine($"点边界为空:{pointBoundary.IsEmpty}");  // true

// 折线的边界是端点
var polyline = new Polyline();
polyline.AddPath(new List<Point>
{
    new Point(0, 0),
    new Point(10, 10),
    new Point(20, 0)
});

var lineBoundary = GeometryEngine.Boundary(polyline);
if (lineBoundary is MultiPoint boundaryPoints)
{
    Console.WriteLine($"折线边界点数:{boundaryPoints.Count}");  // 2(起点和终点)
}

// 多边形的边界是折线
var polygon = new Polygon();
polygon.AddRing(new List<Point>
{
    new Point(0, 0),
    new Point(10, 0),
    new Point(10, 10),
    new Point(0, 10),
    new Point(0, 0)
});

var polyBoundary = GeometryEngine.Boundary(polygon);
if (polyBoundary is Polyline boundaryLine)
{
    Console.WriteLine($"多边形边界长度:{boundaryLine.Length}");  // 40
}

5.8 Generalize(概化)

5.8.1 概念

概化操作通过删除顶点来简化几何对象,同时保持其总体形状。与 Simplify 类似,但参数含义不同:

  • Simplify:tolerance 是点到简化线的最大距离
  • Generalize:maxDeviation 是允许的最大偏差

5.8.2 API

public class GeneralizeOperator : IGeometryOperator<Geometry>
{
    public static GeneralizeOperator Instance { get; }
    
    public Geometry Execute(Geometry geometry, double maxDeviation, 
                           SpatialReference? spatialRef = null);
}

5.8.3 使用示例

using Esri.Geometry.Core;
using Esri.Geometry.Core.Geometries;

var polyline = new Polyline();
polyline.AddPath(new List<Point>
{
    new Point(0, 0),
    new Point(1, 0.5),
    new Point(2, 0),
    new Point(3, 0.3),
    new Point(4, 0),
    new Point(5, 1),
    new Point(6, 0)
});

Console.WriteLine($"原始点数:{polyline.GetPath(0).Count}");  // 7

var generalized = GeometryEngine.Generalize(polyline, 0.6);
if (generalized is Polyline genLine)
{
    Console.WriteLine($"概化后点数:{genLine.GetPath(0).Count}");  // 减少
}

5.9 Densify(密化)

5.9.1 概念

密化操作在几何对象的线段上添加额外的顶点,确保没有线段超过指定的最大长度。这是概化的逆操作。

应用场景

  • 为投影转换准备几何
  • 沿曲线均匀采样
  • 提高空间分析精度

5.9.2 API

public class DensifyOperator : IGeometryOperator<Geometry>
{
    public static DensifyOperator Instance { get; }
    
    public Geometry Execute(Geometry geometry, double maxSegmentLength, 
                           SpatialReference? spatialRef = null);
}

5.9.3 使用示例

using Esri.Geometry.Core;
using Esri.Geometry.Core.Geometries;

// 简单的两点折线
var polyline = new Polyline();
polyline.AddPath(new List<Point>
{
    new Point(0, 0),
    new Point(10, 0)  // 长度为 10 的线段
});

Console.WriteLine($"原始点数:{polyline.GetPath(0).Count}");  // 2

// 密化,最大线段长度为 2
var densified = GeometryEngine.Densify(polyline, 2);

if (densified is Polyline denLine)
{
    Console.WriteLine($"密化后点数:{denLine.GetPath(0).Count}");  // 6
    // 点:(0,0), (2,0), (4,0), (6,0), (8,0), (10,0)
}

// 多边形密化
var square = new Polygon();
square.AddRing(new List<Point>
{
    new Point(0, 0),
    new Point(10, 0),
    new Point(10, 10),
    new Point(0, 10),
    new Point(0, 0)
});

var densifiedSquare = GeometryEngine.Densify(square, 3);

5.9.4 实现原理

private List<Point> DensifyPath(IReadOnlyList<Point> path, double maxLength)
{
    var result = new List<Point>();

    for (int i = 0; i < path.Count - 1; i++)
    {
        result.Add(path[i]);
        
        var start = path[i];
        var end = path[i + 1];
        double length = start.Distance(end);

        if (length > maxLength)
        {
            // 计算需要插入的点数
            int numSegments = (int)Math.Ceiling(length / maxLength);
            
            for (int j = 1; j < numSegments; j++)
            {
                double t = (double)j / numSegments;
                result.Add(new Point(
                    start.X + t * (end.X - start.X),
                    start.Y + t * (end.Y - start.Y)
                ));
            }
        }
    }

    result.Add(path[path.Count - 1]);
    return result;
}

5.10 Clip(裁剪)

5.10.1 概念

裁剪操作将几何对象裁剪到指定的包络矩形范围内。常用于视口裁剪和数据范围限制。

5.10.2 API

public class ClipOperator : IGeometryOperator<Geometry>
{
    public static ClipOperator Instance { get; }
    
    public Geometry Execute(Geometry geometry, Envelope clipEnvelope);
}

5.10.3 使用示例

using Esri.Geometry.Core;
using Esri.Geometry.Core.Geometries;

// 创建裁剪范围
var clipEnvelope = new Envelope(0, 0, 50, 50);

// 裁剪点
var pointInside = new Point(25, 25);
var pointOutside = new Point(100, 100);

var clippedIn = GeometryEngine.Clip(pointInside, clipEnvelope);   // 返回原点
var clippedOut = GeometryEngine.Clip(pointOutside, clipEnvelope); // 返回空点

// 裁剪折线(Cohen-Sutherland 算法)
var polyline = new Polyline();
polyline.AddPath(new List<Point>
{
    new Point(-10, 25),  // 裁剪区外
    new Point(25, 25),   // 裁剪区内
    new Point(75, 25)    // 裁剪区外
});

var clippedLine = GeometryEngine.Clip(polyline, clipEnvelope);
// 结果:从 (0, 25) 到 (50, 25) 的线段

5.10.4 实现原理:Cohen-Sutherland 算法

// 区域码定义
private const int INSIDE = 0;  // 0000
private const int LEFT = 1;    // 0001
private const int RIGHT = 2;   // 0010
private const int BOTTOM = 4;  // 0100
private const int TOP = 8;     // 1000

private int ComputeOutCode(double x, double y, Envelope clip)
{
    int code = INSIDE;
    
    if (x < clip.XMin) code |= LEFT;
    else if (x > clip.XMax) code |= RIGHT;
    
    if (y < clip.YMin) code |= BOTTOM;
    else if (y > clip.YMax) code |= TOP;
    
    return code;
}

private Line? ClipLine(Point p1, Point p2, Envelope clip)
{
    int outcode1 = ComputeOutCode(p1.X, p1.Y, clip);
    int outcode2 = ComputeOutCode(p2.X, p2.Y, clip);
    
    double x1 = p1.X, y1 = p1.Y;
    double x2 = p2.X, y2 = p2.Y;
    
    while (true)
    {
        if ((outcode1 | outcode2) == 0)
        {
            // 两点都在内部
            return new Line(new Point(x1, y1), new Point(x2, y2));
        }
        else if ((outcode1 & outcode2) != 0)
        {
            // 线段完全在外部
            return null;
        }
        else
        {
            // 需要裁剪
            int outcodeOut = outcode1 != 0 ? outcode1 : outcode2;
            double x, y;
            
            if ((outcodeOut & TOP) != 0)
            {
                x = x1 + (x2 - x1) * (clip.YMax - y1) / (y2 - y1);
                y = clip.YMax;
            }
            else if ((outcodeOut & BOTTOM) != 0)
            {
                x = x1 + (x2 - x1) * (clip.YMin - y1) / (y2 - y1);
                y = clip.YMin;
            }
            else if ((outcodeOut & RIGHT) != 0)
            {
                y = y1 + (y2 - y1) * (clip.XMax - x1) / (x2 - x1);
                x = clip.XMax;
            }
            else
            {
                y = y1 + (y2 - y1) * (clip.XMin - x1) / (x2 - x1);
                x = clip.XMin;
            }
            
            // 更新端点
            if (outcodeOut == outcode1)
            {
                x1 = x; y1 = y;
                outcode1 = ComputeOutCode(x1, y1, clip);
            }
            else
            {
                x2 = x; y2 = y;
                outcode2 = ComputeOutCode(x2, y2, clip);
            }
        }
    }
}

5.11 Offset(偏移)

5.11.1 概念

偏移操作创建几何对象的偏移版本。正值向外偏移,负值向内偏移。

5.11.2 API

public class OffsetOperator : IGeometryOperator<Geometry>
{
    public static OffsetOperator Instance { get; }
    
    public Geometry Execute(Geometry geometry, double distance, 
                           SpatialReference? spatialRef = null);
}

5.11.3 使用示例

using Esri.Geometry.Core;
using Esri.Geometry.Core.Geometries;

// 多边形偏移
var polygon = new Polygon();
polygon.AddRing(new List<Point>
{
    new Point(0, 0),
    new Point(100, 0),
    new Point(100, 100),
    new Point(0, 100),
    new Point(0, 0)
});

// 向外偏移 10
var offsetPolygon = GeometryEngine.Offset(polygon, 10);

// 向内偏移 10
var insetPolygon = GeometryEngine.Offset(polygon, -10);

5.12 小结

本章详细介绍了 geometry-api-net 的几何运算操作符:

操作符 功能 核心算法
Buffer 创建缓冲区 几何扩展
ConvexHull 计算凸包 Graham 扫描
Area 计算面积 鞋带公式
Length 计算长度 欧几里得距离
Simplify 简化几何 Douglas-Peucker
Centroid 计算质心 多边形质心公式
Boundary 获取边界 OGC 规范
Generalize 概化几何 Douglas-Peucker 变体
Densify 密化几何 线段插值
Clip 裁剪几何 Cohen-Sutherland
Offset 偏移几何 几何偏移

这些操作符是进行空间分析的核心工具。在下一章中,我们将学习集合操作符,用于计算几何对象之间的并集、交集和差集。

posted @ 2025-12-03 16:29  我才是银古  阅读(7)  评论(0)    收藏  举报