python填充多边形,获取所有内部点

发布于:2025-09-02 ⋅ 阅读:(16) ⋅ 点赞:(0)

shepely

在这里插入图片描述
简单示例

from shapely.geometry import Polygon

# 创建一个多边形
polygon = Polygon([(0, 0), (0, 5), (5, 5), (5, 0)])

# 提取多边形中的点
points = list(polygon.exterior.coords)

# 打印结果
print(points)

实际示例

import numpy as np
from shapely.geometry import Polygon, Point, MultiPolygon
import matplotlib.pyplot as plt
from matplotlib.path import Path
plt.rcParams['figure.facecolor'] = 'white'
plt.rcParams['axes.facecolor'] = 'silver'
# 网格线
plt.rcParams['xtick.direction'] = 'in'
print(plt.rcParams["font.sans-serif"])
# 设置全局字体为黑体
# plt.rcParams['font.sans-serif'] = ['simhei'] # 替换为其他中文字体如

def get_integer_points_in_polygon(polygon, include_boundary=True):
    """
    获取多边形内部的所有整数坐标点
    
    参数:
        polygon: Shapely Polygon对象(可以包含孔洞)
        include_boundary: 是否包含边界上的点
        
    返回:
        list: 多边形内部的整数坐标点列表 [(x1, y1), (x2, y2), ...]
    """
    # 获取多边形的边界框
    min_x, min_y, max_x, max_y = polygon.bounds
    
    # 将边界框坐标转换为整数
    min_x, min_y, max_x, max_y = map(int, [min_x, min_y, max_x, max_y])
    
    # 生成边界框内的所有整数点
    x_coords = np.arange(min_x, max_x + 1)
    y_coords = np.arange(min_y, max_y + 1)
    
    # 创建网格点
    xx, yy = np.meshgrid(x_coords, y_coords)
    grid_points = np.vstack((xx.flatten(), yy.flatten())).T
    
    # 检查每个点是否在多边形内部
    interior_points = []
    for point in grid_points:
        shapely_point = Point(point)
        if polygon.contains(shapely_point) or (include_boundary and polygon.touches(shapely_point)):
            interior_points.append(tuple(point))
    
    return interior_points


def visualize_polygon_and_points(polygon, points):
    """
    可视化多边形和内部点
    
    参数:
        polygon: Shapely Polygon对象
        points: 点列表
    """
    fig, ax = plt.subplots(figsize=(10, 8), facecolor='white')
    
    # 绘制多边形
    if hasattr(polygon, 'exterior'):
        # 单个多边形
        x, y = polygon.exterior.xy
        ax.plot(x, y, 'b-', linewidth=2)
        
        # 绘制孔洞
        for interior in polygon.interiors:
            x, y = interior.xy
            ax.plot(x, y, 'r-', linewidth=2)
    else:
        # 多多边形
        for poly in polygon.geoms:
            x, y = poly.exterior.xy
            ax.plot(x, y, 'b-', linewidth=2)
            
            for interior in poly.interiors:
                x, y = interior.xy
                ax.plot(x, y, 'r-', linewidth=2)
    
    # 绘制点
    if points:
        x_pts, y_pts = zip(*points)
        ax.plot(x_pts, y_pts, 'go', markersize=2)
    
    ax.set_aspect('equal')
    plt.title('多边形及其内部整数点')
    plt.xlabel('X坐标')
    plt.ylabel('Y坐标')
    plt.grid(True)
    plt.show()

# 使用示例
if __name__ == "__main__":
    # 创建一个带孔洞的多边形示例
    exterior = [(0, 0), (10, 0), (10, 10), (0, 10), (0, 0)]
    interior = [(2, 2), (8, 2), (8, 8), (2, 8), (2, 2)]
    
    polygon = Polygon(exterior, [interior])
    
    # 获取多边形内部的所有整数点
    points = get_integer_points_in_polygon(polygon)
    
    print(f"找到 {len(points)} 个内部整数点")
    print("前10个点:", points[:10])
    
    # 可视化
    visualize_polygon_and_points(polygon, points)
    

geopandas

在这里插入图片描述

处理非标准geojson

import json
import geopandas as gpd
import pandas as pd
from shapely.geometry import Polygon, GeometryCollection
import matplotlib.pyplot as plt

# 方法1:直接解析GeometryCollection
def parse_geometry_collection(geojson_data):
    """解析GeometryCollection格式的GeoJSON"""
    features = []
    
    if geojson_data.get('type') != 'GeometryCollection':
        raise ValueError("不是GeometryCollection格式")
    
    for geom in geojson_data.get('geometries', []):
        # 提取几何类型和坐标
        geom_type = geom.get('type')
        coords = geom.get('coordinates', [])
        
        # 提取属性
        properties = geom.get('properties', {})
        
        # 创建对应的shapely几何对象
        if geom_type == 'Polygon':
            geometry = Polygon(coords[0])  # 多边形外环
        elif geom_type == 'Point':
            from shapely.geometry import Point
            geometry = Point(coords)
        elif geom_type == 'LineString':
            from shapely.geometry import LineString
            geometry = LineString(coords)
        else:
            continue  # 跳过不支持的几何类型
        
        features.append({
            'type': 'Feature',
            'geometry': geometry,
            'properties': properties
        })
    print(features)
    return features

# 方法2:转换为标准GeoJSON格式
def convert_to_standard_geojson(geojson_data):
    """将GeometryCollection转换为标准FeatureCollection格式"""
    features = parse_geometry_collection(geojson_data)
    
    return {
        'type': 'FeatureCollection',
        'features': features
    }

# 方法3:创建GeoDataFrame
def create_geodataframe(geojson_data):
    """从GeometryCollection创建GeoDataFrame"""
    features = parse_geometry_collection(geojson_data)
    
    # 提取几何和属性
    geometries = [f['geometry'] for f in features]
    properties = [f['properties'] for f in features]
    
    # 创建GeoDataFrame
    gdf = gpd.GeoDataFrame(properties, geometry=geometries)
    
    return gdf

# 使用示例
if __name__ == "__main__":
    # 示例数据(根据您提供的格式)
    geojson_data = {
        "type": "GeometryCollection",
        "geometries": [
            {
                "type": "Polygon", 
                "properties": {"uID": 0, "label": "1"}, 
                "coordinates": [[[10436, 6198], [10481, 6198], [10504, 6221], [10436, 6198]]]
            },
            {
                "type": "Polygon", 
                "properties": {"uID": 1, "label": "2"}, 
                "coordinates": [[[10550, 6250], [10600, 6250], [10600, 6300], [10550, 6300], [10550, 6250]]]
            }
        ]
    }

    # 解析GeometryCollection
    features = parse_geometry_collection(geojson_data)
    print("解析出的要素数量:", len(features))
    
    # 转换为标准GeoJSON
    standard_geojson = convert_to_standard_geojson(geojson_data)
    print("标准GeoJSON格式已创建")
    
    # 创建GeoDataFrame
    gdf = create_geodataframe(geojson_data)
    print("GeoDataFrame信息:")
    print(gdf.head())
    
    # 可视化
    fig, ax = plt.subplots(figsize=(10, 8), facecolor='white')
    gdf.plot(ax=ax, color='lightblue', edgecolor='black')
    
    # 添加标签
    for idx, row in gdf.iterrows():
        centroid = row.geometry.centroid
        ax.text(centroid.x, centroid.y, row['label'], 
                fontsize=12, ha='center', va='center')
    
    plt.title("GeometryCollection中的多边形")
    plt.xlabel("X坐标")
    plt.ylabel("Y坐标")
    plt.grid(True)
    plt.show()
    
    # 保存为标准GeoJSON文件
    gdf.to_file("converted.geojson", driver="GeoJSON")
    print("已保存为标准GeoJSON文件: converted.geojson")

处理实际文件

import json

# 从文件读取GeometryCollection格式的GeoJSON
def read_geometry_collection_file(file_path):
    """从文件读取GeometryCollection格式的GeoJSON"""
    with open(file_path, 'r') as f:
        geojson_data = json.load(f)
    
    return geojson_data

# 使用示例
file_path = "your_file.geojson"  # 替换为实际文件路径
geojson_data = read_geometry_collection_file(file_path)

# 处理数据
gdf = create_geodataframe(geojson_data)
print(gdf.info())

确保多边形坐标闭合

def ensure_polygon_closed(coordinates):
    """确保多边形坐标闭合(首尾点相同)"""
    if not coordinates:
        return coordinates
    
    # 对于多边形,coordinates是一个列表的列表
    # 第一个列表是外环,后续列表是内环(孔)
    closed_coords = []
    for ring in coordinates:
        if len(ring) < 3:
            continue  # 跳过无效环(至少需要3个点)
        
        # 检查是否闭合(第一个点和最后一个点相同)
        first_point = ring[0]
        last_point = ring[-1]
        
        if first_point != last_point:
            # 添加第一个点作为最后一个点以闭合环
            closed_ring = ring + [first_point]
        else:
            closed_ring = ring
            
        closed_coords.append(closed_ring)
    
    return closed_coords

提取坐标信息

def extract_coordinates_from_geometry_collection(geojson_data):
    """从GeometryCollection中提取所有坐标"""
    coordinates = []
    
    if geojson_data.get('type') != 'GeometryCollection':
        return coordinates
    
    for geom in geojson_data.get('geometries', []):
        geom_type = geom.get('type')
        coords = geom.get('coordinates', [])
        properties = geom.get('properties', {})
        
        if geom_type == 'Polygon':
            # 多边形坐标 - 第一个列表是外环,后续列表是内环(孔)
            for ring in coords:
                coordinates.extend(ring)
        elif geom_type == 'Point':
            coordinates.append(coords)
        elif geom_type == 'LineString':
            coordinates.extend(coords)
        elif geom_type == 'MultiPolygon':
            for polygon in coords:
                for ring in polygon:
                    coordinates.extend(ring)
    
    return coordinates

# 使用示例
all_coords = extract_coordinates_from_geometry_collection(geojson_data)
print(f"提取到 {len(all_coords)} 个坐标点")
print("前10个坐标:", all_coords[:10])

生成mask

在这里插入图片描述

import numpy as np
from shapely.geometry import Polygon, MultiPolygon, Point
import matplotlib.pyplot as plt
from matplotlib.path import Path
import matplotlib.patches as patches
import json

def get_integer_points_from_geometry_collection(geojson_data):
    """
    从GeometryCollection中提取所有多边形内部的整数坐标点
    
    参数:
        geojson_data: GeometryCollection格式的GeoJSON数据
        
    返回:
        set: 所有多边形内部的整数坐标点集合
    """
    interior_points = set()
    
    if geojson_data.get('type') != 'GeometryCollection':
        return interior_points
    
    for geom in geojson_data.get('geometries', []):
        geom_type = geom.get('type')
        coords = geom.get('coordinates', [])
        properties = geom.get('properties', {})
        
        if geom_type == 'Polygon':
            # 创建Shapely多边形对象
            polygon = Polygon(coords[0], coords[1:])
            # 获取多边形内部的整数点
            points = get_integer_points_in_polygon(polygon)
            interior_points.update(points)
            
        elif geom_type == 'MultiPolygon':
            # 处理多多边形
            for polygon_coords in coords:
                polygon = Polygon(polygon_coords[0], polygon_coords[1:])
                points = get_integer_points_in_polygon(polygon)
                interior_points.update(points)
    
    return interior_points

def get_integer_points_in_polygon(polygon):
    """
    获取多边形内部的所有整数坐标点
    
    参数:
        polygon: Shapely Polygon对象
        
    返回:
        set: 多边形内部的整数坐标点集合
    """
    # 获取多边形的边界框
    min_x, min_y, max_x, max_y = polygon.bounds
    
    # 将边界框坐标转换为整数
    min_x, min_y, max_x, max_y = map(int, [np.floor(min_x), np.floor(min_y), 
                                          np.ceil(max_x), np.ceil(max_y)])
    
    # 生成边界框内的所有整数点
    x_coords = np.arange(min_x, max_x + 1)
    y_coords = np.arange(min_y, max_y + 1)
    
    # 创建网格点
    xx, yy = np.meshgrid(x_coords, y_coords)
    grid_points = np.vstack((xx.flatten(), yy.flatten())).T

    
    # 检查每个点是否在多边形内部
    include_boundary = True
    interior_points = []
    for point in grid_points:
        shapely_point = Point(point)
        if polygon.contains(shapely_point) or (include_boundary and polygon.touches(shapely_point)):
            interior_points.append(tuple(point))
    
    return interior_points

    # # 修正后的路径创建方法 - 确保代码数组是一维且长度匹配
    # exterior = np.array(polygon.exterior.coords)
    
    # # 创建路径代码 - 确保是一维数组
    # n_points = len(exterior)
    # codes = np.ones(n_points, dtype=Path.code_type) * Path.LINETO
    # codes[0] = Path.MOVETO
    
    # # 创建路径
    # path = Path(exterior, codes)
    
    # # 检查每个点是否在多边形内部
    # contains = path.contains_points(grid_points)
    # interior_points = set(tuple(map(int, point)) for point, inside in zip(grid_points, contains) if inside)
    
    # return interior_points

def create_mask_from_points(points, shape=None):
    """
    从点集合创建二值mask
    
    参数:
        points: 点集合
        shape: mask的形状 (height, width)。如果为None,则根据点坐标自动确定
        
    返回:
        numpy.array: 二值mask
    """
    if not points:
        return np.array([]), (0, 0)
    
    # 如果没有指定形状,则根据点坐标确定
    if shape is None:
        all_x = [p[0] for p in points]
        all_y = [p[1] for p in points]
        min_x, max_x = min(all_x), max(all_x)
        min_y, max_y = min(all_y), max(all_y)
        width = max_x - min_x + 1
        height = max_y - min_y + 1
        origin = (min_x, min_y)
    else:
        height, width = shape
        origin = (0, 0)
    
    # 创建空的mask
    mask = np.zeros((height, width), dtype=np.uint8)
    
    # 填充mask
    for point in points:
        x, y = point
        mask_y = y - origin[1]
        mask_x = x - origin[0]
        
        if 0 <= mask_y < height and 0 <= mask_x < width:
            mask[mask_y, mask_x] = 1
    
    return mask, origin

def visualize_polygon_and_mask(polygon, mask, origin, points=None):
    """
    可视化多边形和mask
    
    参数:
        polygon: Shapely Polygon对象
        mask: 二值mask
        origin: mask的原点坐标
        points: 点集合(可选)
    """
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
    
    # 绘制多边形
    if hasattr(polygon, 'exterior'):
        # 单个多边形
        x, y = polygon.exterior.xy
        ax1.plot(x, y, 'b-', linewidth=2)
        
        # 绘制孔洞
        for interior in polygon.interiors:
            x, y = interior.xy
            ax1.plot(x, y, 'r-', linewidth=2)
    else:
        # 多多边形
        for poly in polygon.geoms:
            x, y = poly.exterior.xy
            ax1.plot(x, y, 'b-', linewidth=2)
            
            for interior in poly.interiors:
                x, y = interior.xy
                ax1.plot(x, y, 'r-', linewidth=2)
    
    # 绘制点
    if points:
        x_pts, y_pts = zip(*points)
        ax1.plot(x_pts, y_pts, 'go', markersize=2)
    
    ax1.set_aspect('equal')
    ax1.set_title('多边形及其内部整数点')
    ax1.set_xlabel('X坐标')
    ax1.set_ylabel('Y坐标')
    ax1.grid(True)
    
    # 绘制mask
    ax2.imshow(mask, cmap='gray', origin='lower')
    ax2.set_title('生成的Mask')
    ax2.set_xlabel(f'X (原点: {origin[0]})')
    ax2.set_ylabel(f'Y (原点: {origin[1]})')
    
    plt.tight_layout()
    plt.show()

def process_geojson_data(geojson_data):
    """
    处理GeoJSON数据并生成mask
    
    参数:
        geojson_data: GeoJSON数据
        
    返回:
        mask: 二值mask
        origin: mask的原点坐标
        interior_points: 内部点集合
    """
    # 提取多边形内部的所有整数点
    interior_points = get_integer_points_from_geometry_collection(geojson_data)
    print(f"找到 {len(interior_points)} 个内部整数点")
    
    # 创建mask
    mask, origin = create_mask_from_points(interior_points)
    print(f"Mask形状: {mask.shape}, 原点: {origin}")
    
    # 可视化
    # 注意:这里简化处理,只可视化第一个多边形
    if geojson_data.get('type') == 'GeometryCollection' and geojson_data.get('geometries'):
        first_geom = geojson_data['geometries'][0]
        if first_geom.get('type') == 'Polygon':
            polygon = Polygon(first_geom['coordinates'][0], first_geom['coordinates'][1:])
            visualize_polygon_and_mask(polygon, mask, origin, interior_points)
    
    return mask, origin, interior_points

# 使用示例
if __name__ == "__main__":
    # 示例GeometryCollection数据
    geojson_data = {
        "type": "GeometryCollection",
        "geometries": [
            {
                "type": "Polygon", 
                "properties": {"uID": 0, "label": "1"}, 
                "coordinates": [
                    [[10, 10], [50, 10], [50, 50], [10, 50], [10, 10]],  # 外环
                    [[20, 20], [40, 20], [40, 40], [20, 40], [20, 20]]   # 内环(孔洞)
                ]
            },
            {
                "type": "Polygon", 
                "properties": {"uID": 1, "label": "2"}, 
                "coordinates": [
                    [[60, 60], [80, 60], [80, 80], [60, 80], [60, 60]]
                ]
            }
        ]
    }
    
    # 处理数据
    mask, origin, interior_points = process_geojson_data(geojson_data)
    
    # 保存mask为图像
    plt.imsave('mask.png', mask, cmap='gray')
    print("Mask已保存为 mask.png")
    
    # 保存点坐标到文件
    with open('interior_points.txt', 'w') as f:
        for point in sorted(interior_points):
            f.write(f"{point[0]}, {point[1]}\n")
    print("点坐标已保存为 interior_points.txt")

网站公告

今日签到

点亮在社区的每一天
去签到