利用python生成Voronoi图

前言

生成Voronoi图,有2个库是最常用的:scipygeovoronoi

scipy

可能是因为我用的是实际的地图数据,边界比较复杂,用这个库老是会有很多小bug,比如边缘会有部分面积没有被划入任何一块区域。和AI对话了好多轮,还是没有完全搞定。

和AI的部分对话截取如下:

最后AI都劝我用geovoronoi了。

geovoronoi

这个库是更专业的,事实证明和AI迭代了几轮之后就满足需求了,也是我最终采用的。

实际区域行政边界数据

以汕尾市为例,数据来源。界面如下,可以直接下载json格式的文件。

json文件直接喂给Deepseek就可以解读出来:

json 复制代码
"properties": {
    "adcode": 441500,           // 中国行政区划代码,441500 唯一代表汕尾市
    "name": "汕尾市",           // 行政区名称
    "center": [115.364238, 22.774485], // 行政中心坐标(经纬度)
    "centroid": [115.53778, 23.004558], // 几何中心坐标(经纬度)
    "childrenNum": 4,           // 下属子区域数量(指市辖区、县级市的数量)
    "level": "city",            // 行政区级别(城市)
    "acroutes": [100000, 440000], // 行政路径(100000代表中国,440000代表广东省)
    "parent": {                 // 上级行政区
        "adcode": 440000        // 上级行政区代码(440000 是广东省)
    }
}

"geometry": {
    "type": "MultiPolygon",     // 几何类型:多重多边形
    "coordinates": [ ... ]       // 定义多边形轮廓的坐标点数组
}

一个小问题

汕尾市的数据除了陆地上的主体,还有一些小岛屿,也包含在了json文件里,我实际上用的时候把岛屿的数据(也就是coordinates里面排在后面的几个数组)给删掉了。

截取AI回答如下:

程序

采用geovoronoi库的代码如下。

核心逻辑:1、读取json文件的边界;2、随机生成50个点;3、画图。其中第2步可以替换为给定的经纬值。

python 复制代码
import json
import random
import numpy as np
import matplotlib.pyplot as plt
from shapely.geometry import shape, Point
from shapely.ops import unary_union
import geopandas as gpd
from geovoronoi import voronoi_regions_from_coords

# ---------------------------
# 1. 读取汕尾市行政边界 JSON
# ---------------------------
with open("shanwei.json", "r", encoding="utf-8") as f:
    data = json.load(f)

# 兼容多 Polygon 情况
geoms = []
for feature in data["features"]:
    geom = shape(feature["geometry"])
    geoms.append(geom)
boundary = unary_union(geoms)

# ---------------------------
# 2. 在边界内随机生成 50 个点
# ---------------------------
def generate_random_points_within(polygon, num_points):
    points = []
    minx, miny, maxx, maxy = polygon.bounds
    while len(points) < num_points:
        p = Point(random.uniform(minx, maxx), random.uniform(miny, maxy))
        if polygon.contains(p):
            points.append(p)
    return points

points = generate_random_points_within(boundary, 50)

# ---------------------------
# 3. 使用 geovoronoi 生成 Voronoi 区域
# ---------------------------
coords = np.array([(p.x, p.y) for p in points])
region_polys, region_pts = voronoi_regions_from_coords(coords, boundary)

# ---------------------------
# 4. 随机生成颜色
# ---------------------------
colors = []
for _ in region_polys:
    # RGB 取值范围 0~1
    colors.append((random.random(), random.random(), random.random()))

# ---------------------------
# 5. 绘制结果
# ---------------------------
fig, ax = plt.subplots(figsize=(8, 8))

# 绘制 Voronoi 区域,每个区域一个颜色
for poly, color in zip(region_polys.values(), colors):
    gpd.GeoSeries([poly]).plot(ax=ax, facecolor=color, edgecolor='black', alpha=0.7)

# 绘制随机点
ax.plot(coords[:, 0], coords[:, 1], 'ro', markersize=3, label="Random Points")

# 绘制边界
gpd.GeoSeries([boundary]).plot(ax=ax, edgecolor='blue', facecolor='none', linewidth=1)

plt.title("Voronoi Diagram within Shanwei Boundary (Random Colors)", fontsize=14)
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.axis('equal')
plt.legend()
plt.show()

print(points)

本来想让AI再生成指定50个数据点而非随机的版本,无奈对话次数达到上限了。

不过这个需求也不复杂,输出程序里的points,格式如下。直接给50对经纬值,然后让AI按照下面的格式生成数组,就可以套用上面的程序了。

python 复制代码
[<POINT (115.395 23.1)>, <POINT (115.675 23.207)>, <POINT (115.469 23.175)>, <POINT (115.72 23.296)>, <POINT (115.736 23.086)>, <POINT (116.056 22.853)>, <POINT (115.877 22.985)>, <POINT (114.987 22.784)>, <POINT (115.582 23.246)>, <POINT (115.841 22.763)>, <POINT (115.868 23.011)>, <POINT (115.299 22.843)>, <POINT (115.412 23.12)>, <POINT (115.388 23.011)>, <POINT (115.766 22.894)>, <POINT (114.969 22.836)>, <POINT (115.091 22.897)>, <POINT (115.633 22.989)>, <POINT (115.823 22.844)>, <POINT (115.773 23.124)>, <POINT (114.941 22.867)>, <POINT (115.467 23.112)>, <POINT (115.009 22.917)>, <POINT (115.534 22.678)>, <POINT (115.409 22.803)>, <POINT (115.359 22.982)>, <POINT (115.455 23.266)>, <POINT (115.334 23.141)>, <POINT (115.586 22.968)>, <POINT (115.747 23.206)>, <POINT (115.428 23.112)>, <POINT (115.817 23.257)>, <POINT (115.598 23.198)>, <POINT (115.374 23.035)>, <POINT (115.288 22.903)>, <POINT (115.829 23.05)>, <POINT (114.981 22.893)>, <POINT (115.899 22.806)>, <POINT (115.507 22.835)>, <POINT (115.984 22.878)>, <POINT (115.156 22.895)>, <POINT (115.561 23.259)>, <POINT (115.719 23.11)>, <POINT (115.566 23.194)>, <POINT (115.444 22.85)>, <POINT (115.362 22.801)>, <POINT (115.352 23.047)>, <POINT (115.721 23.037)>, <POINT (115.857 22.995)>, <POINT (115.988 22.832)>]

程序运行效果如下,看起来可以满足需求,就先这样吧。

如果有样式上调整的需求,直接告诉AI就可以了,真的是相当方便了。

后记

  • AI真的是太强大了,必须充分利用起来。

  • 感觉chatgpt还是要好用一点。

  • 好想有一个无限制的账号...

相关推荐
ColderYY4 小时前
Python中的正则表达式
开发语言·python·正则表达式
小叮当⇔4 小时前
知识就是力量——EMQX Dashboard核心规则编写方法及示例
python·iot
星期天要睡觉4 小时前
提示词(Prompt)——链式思维提示词(Chain-of-Thought Prompting)在大模型中的调用(以 Qwen 模型为例)
开发语言·人工智能·python·语言模型·prompt
CLubiy5 小时前
【研究生随笔】Pytorch中的卷积神经网络(2)
人工智能·pytorch·python·深度学习·cnn·卷积神经网络·池化
程序员爱钓鱼5 小时前
Python编程实战 - 函数与模块化编程 - 导入与使用模块
后端·python·ipython
程序员爱钓鱼5 小时前
Python编程实战 - 函数与模块化编程 - 匿名函数(lambda)
后端·python·ipython
清空mega5 小时前
Flask入门学习指南
后端·python·flask
万邦科技Lafite10 小时前
京东按图搜索京东商品(拍立淘) API (.jd.item_search_img)快速抓取数据
开发语言·前端·数据库·python·电商开放平台·京东开放平台
丁浩66611 小时前
Python机器学习---6.集成学习与随机森林
python·随机森林·机器学习