当前位置: 首页 > news >正文

【科技与狠活】如何利用Python绘制足球场

卡塔尔世界杯赛程近半,朋友圈都在晒中奖的体育彩票,而我在搬砖🧱。

今天我将介绍如何使用Python Matplotlib创建一个足球场,本文设计球场尺寸为105×68。

首先导入所需的依赖包:

import pandas as pd
import numpy as np

import matplotlib.pyplot as plt
from matplotlib.patches import Arc

其实看似复杂的足球场一旦将其分解成各个组成部分,就不是什么特别复杂的事情了。足球场只是线条和形状的集合,下面我将一步步实现其包含的各个元素并在最后将它们全部封装在一个函数中。

首先,设置一些会频繁用到的全局变量。

# 足球场的边界长宽
x_min = 0
x_max = 105
y_min = 0
y_max = 68

line_color = "grey"
line_thickness = 1.5
background = "w" # w表示white
point_size = 20

arc_angle = 0
first = 0
second = 1

接下来创建列表pitch_x、pitch_y标记球场上各个元素的坐标。

pitch_x = [0,5.8,11.5,17,50,83,88.5,94.2,100] # pitch x markings

"""
[goal line, six yard box, penalty spot, edge of box,
 halfway line, edge of box, penalty spot, six yard box, goal line]
"""

pitch_y = [0, 21.1, 36.6, 50, 63.2, 78.9, 100]

"""
[sideline, edge of box, six yard box, centre of pitch,
 six yard box, edge of box, sideline ]
"""

goal_y = [45.2, 54.8] # goal posts

上述定义的x、y为100x100, 需要对球场进行同比例转换为105x68,当然球场元素坐标也会同比例变化:

x_conversion = 105 / 100
y_conversion = 68 / 100

pitch_x = [item * x_conversion for item in pitch_x]
pitch_y = [item * y_conversion for item in pitch_y]
goal_y = [item * y_conversion for item in goal_y]

现在已经对元素的点坐标进行了转换,下面就可以通过 x 和 y 坐标列表来绘制足球场的各个元素了。例如,这是如何绘制球场边线的代码:

lx1 = [x_min, x_max, x_max, x_min, x_min]
ly1 = [y_min, y_min, y_max, y_max, y_min]

fig, ax = plt.subplots(figsize=(11,7))
ax.axis("off")
ax.plot(lx1,ly1, color=line_color, lw=line_thickness, zorder=-1)

plt.tight_layout()
plt.show()

下面可以遍历每个元素的标记并将它们绘制成对应的球场元素。

# side and goal lines
lx1 = [x_min, x_max, x_max, x_min, x_min]
ly1 = [y_min, y_min, y_max, y_max, y_min]

ax.plot(lx1, ly1, color=line_color, lw=line_thickness, zorder=-1)

# outer boxed
lx2 = [x_max, pitch_x[5], pitch_x[5], x_max]
ly2 = [pitch_y[1], pitch_y[1], pitch_y[5], pitch_y[5]]

ax.plot(lx2, ly2, color=line_color, lw=line_thickness, zorder=-1)

lx3 = [0, pitch_x[3], pitch_x[3], 0]
ly3 = [pitch_y[1], pitch_y[1], pitch_y[5], pitch_y[5]]

ax.plot(lx3, ly3, color=line_color, lw=line_thickness, zorder=-1)

# .....

当然这不是最好的方法,因为这意味着如果我想绘制垂直方向足球场,我需要调整每一项数据。与其一一绘制,不如构建坐标列表并将它们添加到主列表中:

# side and goal lines
lx1 = [x_min, x_max, x_max, x_min, x_min]
ly1 = [y_min, y_min, y_max, y_max, y_min]

# outer boxed
lx2 = [x_max, pitch_x[5], pitch_x[5], x_max]
ly2 = [pitch_y[1], pitch_y[1], pitch_y[5], pitch_y[5]]

lx3 = [0, pitch_x[3], pitch_x[3], 0]
ly3 = [pitch_y[1], pitch_y[1], pitch_y[5], pitch_y[5]]

# goals
lx4 = [x_max, x_max+2, x_max+2, x_max]
ly4 = [goal_y[0], goal_y[0], goal_y[1], goal_y[1]]

lx5 = [0, -2, -2, 0]
ly5 = [goal_y[0], goal_y[0], goal_y[1], goal_y[1]]

# 6 yard boxes
lx6 = [x_max, pitch_x[7], pitch_x[7], x_max]
ly6 = [pitch_y[2], pitch_y[2], pitch_y[4], pitch_y[4]]

lx7 = [0, pitch_x[1], pitch_x[1], 0]
ly7 = [pitch_y[2], pitch_y[2], pitch_y[4], pitch_y[4]]

# Halfway line, penalty spots, and kickoff spot
lx8 = [pitch_x[4], pitch_x[4]]
ly8 = [0, y_max]

lines = [
    [lx1, ly1],
    [lx2, ly2],
    [lx3, ly3],
    [lx4, ly4],
    [lx5, ly5],
    [lx6, ly6],
    [lx7, ly7],
    [lx8, ly8],
    ]

通过循环遍历lines列表来绘制上面的图:

fig, ax = plt.subplots(figsize=(11,7))
ax.axis("off") 

for line in lines:
    ax.plot(line[0], line[1],
            color=line_color,
            lw=line_thickness,
            zorder=-1)

plt.tight_layout()
plt.show()

这样绘图代码看起来更清晰,以同样的方式绘制开球点和罚球点:

points = [
    [pitch_x[6], pitch_y[3]],
    [pitch_x[2], pitch_y[3]],
    [pitch_x[4], pitch_y[3]]
    ]

然后遍历绘图代码中的点:

fig, ax = plt.subplots(figsize=(11,7))
ax.axis("off")

for line in lines:
    ax.plot(line[0], line[1],
            color=line_color,
            lw=line_thickness,
            zorder=-1)

for point in points:
    ax.scatter(point[0], point[1],
               color=line_color,
               s=point_size,
               zorder=-1)

plt.tight_layout()
plt.show()

在每个"box"上添加中心圆。

circle_points = [pitch_x[4], pitch_y[3]]
arc_points1 = [pitch_x[6], pitch_y[3]]
arc_points2 = [pitch_x[2], pitch_y[3]]
fig, ax = plt.subplots(figsize=(11,7))
ax.axis("off")
for line in lines:
    ax.plot(line[0], line[1],
            color=line_color,
            lw=line_thickness,
            zorder=-1)

for point in points:
    ax.scatter(point[0], point[1],
               color=line_color,
               s=point_size,
               zorder=-1)

circle = plt.Circle((circle_points[0], circle_points[1]),
                     x_max * 0.088,
                     lw=line_thickness,
                     color=line_color,
                     fill=False,
                     zorder=-1)

ax.add_artist(circle)

arc1 = Arc((arc_points1[0], arc_points1[1]),
           height=x_max * 0.088 * 2,
           width=x_max * 0.088 * 2,
           angle=arc_angle,
           theta1=128.75,
           theta2=231.25,
           color=line_color,
           lw=line_thickness,
           zorder=-1)

ax.add_artist(arc1)

arc2 = Arc((arc_points2[0], arc_points2[1]),
           height=x_max * 0.088 * 2,
           width=x_max * 0.088 * 2,
           angle=arc_angle,
           theta1=308.75,
           theta2=51.25,
           color=line_color,
           lw=line_thickness,
           zorder=-1)

ax.add_artist(arc2)

ax.set_aspect("equal") 
plt.tight_layout()
plt.show()

这就是创建足球场的整个过程!然而,这还没有结束。我添加了几个变量并调整了代码以绘制逻辑更加灵活。

fig, ax = plt.subplots(figsize=(11,7))
ax.axis("off")

for line in lines:
    ax.plot(line[first], line[second],
            color=line_color,
            lw=line_thickness,
            zorder=-1)

for point in points:
    ax.scatter(point[first], point[second],
               color=line_color,
               s=point_size,
               zorder=-1)

circle = plt.Circle((circle_points[first], circle_points[second]),
                     x_max * 0.088,
                     lw=line_thickness,
                     color=line_color,
                     fill=False,
                     zorder=-1)

ax.add_artist(circle)

arc1 = Arc((arc_points1[first], arc_points1[second]),
           height=x_max * 0.088 * 2,
           width=x_max * 0.088 * 2,
           angle=arc_angle,
           theta1=128.75,
           theta2=231.25,
           color=line_color,
           lw=line_thickness,
           zorder=-1)

ax.add_artist(arc1)

arc2 = Arc((arc_points2[first], arc_points2[second]),
           height=x_max * 0.088 * 2,
           width=x_max * 0.088 * 2,
           angle=arc_angle,
           theta1=308.75,
           theta2=51.25,
           color=line_color,
           lw=line_thickness,
           zorder=-1)

ax.add_artist(arc2)

ax.set_aspect("equal")
plt.tight_layout()
plt.show()

还记得文章开头设置的几个变量吧:arc_angle = 0、first = 0 和 second = 1。通过将这些设置为变量而不是硬编码,我们可以修改这几个变量为first = 1、second = 0和arc_angle = 90,这样就可以绘制一个垂直方向的足球场了。

first = 1
second = 0
arc_angle = 90

fig, ax = plt.subplots(figsize=(7,11))
ax.axis("off")

for line in lines:
    ax.plot(line[first], line[second],
            color=line_color,
            lw=line_thickness,
            zorder=-1)

for point in points:
    ax.scatter(point[first], point[second],
               color=line_color,
               s=point_size,
               zorder=-1)

circle = plt.Circle((circle_points[first], circle_points[second]),
                     x_max * 0.088,
                     lw=line_thickness,
                     color=line_color,
                     fill=False,
                     zorder=-1)

ax.add_artist(circle)

arc1 = Arc((arc_points1[first], arc_points1[second]),
           height=x_max * 0.088 * 2,
           width=x_max * 0.088 * 2,
           angle=arc_angle,
           theta1=128.75,
           theta2=231.25,
           color=line_color,
           lw=line_thickness,
           zorder=-1)

ax.add_artist(arc1)

arc2 = Arc((arc_points2[first], arc_points2[second]),
           height=x_max * 0.088 * 2,
           width=x_max * 0.088 * 2,
           angle=arc_angle,
           theta1=308.75,
           theta2=51.25,
           color=line_color,
           lw=line_thickness,
           zorder=-1)

ax.add_artist(arc2)

ax.set_aspect("equal")
plt.tight_layout()
plt.show()

代码封装

下面将上面零散的代码封装成函数。

def draw_pitch(x_min=0, x_max=105,
               y_min=0, y_max=68,
               pitch_color="w",
               line_color="grey",
               line_thickness=1.5,
               point_size=20,
               orientation="horizontal",
               aspect="full",
               ax=None
               ):

    if not ax:
        raise TypeError("This function is intended to be used with an existing fig and ax in order to allow flexibility in plotting of various sizes and in subplots.")


    if orientation.lower().startswith("h"):
        first = 0
        second = 1
        arc_angle = 0

        if aspect == "half":
            ax.set_xlim(x_max / 2, x_max + 5)

    elif orientation.lower().startswith("v"):
        first = 1
        second = 0
        arc_angle = 90

        if aspect == "half":
            ax.set_ylim(x_max / 2, x_max + 5)

    
    else:
        raise NameError("You must choose one of horizontal or vertical")

    
    ax.axis("off")

    rect = plt.Rectangle((x_min, y_min),
                         x_max, y_max,
                         facecolor=pitch_color,
                         edgecolor="none",
                         zorder=-2)

    ax.add_artist(rect)

    x_conversion = x_max / 100
    y_conversion = y_max / 100

    pitch_x = [0,5.8,11.5,17,50,83,88.5,94.2,100] # pitch x markings
    pitch_x = [x * x_conversion for x in pitch_x]

    pitch_y = [0, 21.1, 36.6, 50, 63.2, 78.9, 100] # pitch y markings
    pitch_y = [x * y_conversion for x in pitch_y]

    goal_y = [45.2, 54.8] # goal posts
    goal_y = [x * y_conversion for x in goal_y]

    # side and goal lines
    lx1 = [x_min, x_max, x_max, x_min, x_min]
    ly1 = [y_min, y_min, y_max, y_max, y_min]

    # outer boxed
    lx2 = [x_max, pitch_x[5], pitch_x[5], x_max]
    ly2 = [pitch_y[1], pitch_y[1], pitch_y[5], pitch_y[5]]

    lx3 = [0, pitch_x[3], pitch_x[3], 0]
    ly3 = [pitch_y[1], pitch_y[1], pitch_y[5], pitch_y[5]]

    # goals
    lx4 = [x_max, x_max+2, x_max+2, x_max]
    ly4 = [goal_y[0], goal_y[0], goal_y[1], goal_y[1]]

    lx5 = [0, -2, -2, 0]
    ly5 = [goal_y[0], goal_y[0], goal_y[1], goal_y[1]]

    # 6 yard boxes
    lx6 = [x_max, pitch_x[7], pitch_x[7], x_max]
    ly6 = [pitch_y[2],pitch_y[2], pitch_y[4], pitch_y[4]]

    lx7 = [0, pitch_x[1], pitch_x[1], 0]
    ly7 = [pitch_y[2],pitch_y[2], pitch_y[4], pitch_y[4]]


    # Halfway line, penalty spots, and kickoff spot
    lx8 = [pitch_x[4], pitch_x[4]]
    ly8 = [0, y_max]

    lines = [
        [lx1, ly1],
        [lx2, ly2],
        [lx3, ly3],
        [lx4, ly4],
        [lx5, ly5],
        [lx6, ly6],
        [lx7, ly7],
        [lx8, ly8],
        ]

    points = [
        [pitch_x[6], pitch_y[3]],
        [pitch_x[2], pitch_y[3]],
        [pitch_x[4], pitch_y[3]]
        ]

    circle_points = [pitch_x[4], pitch_y[3]]
    arc_points1 = [pitch_x[6], pitch_y[3]]
    arc_points2 = [pitch_x[2], pitch_y[3]]


    for line in lines:
        ax.plot(line[first], line[second],
                color=line_color,
                lw=line_thickness,
                zorder=-1)

    for point in points:
        ax.scatter(point[first], point[second],
                   color=line_color,
                   s=point_size,
                   zorder=-1)

    circle = plt.Circle((circle_points[first], circle_points[second]),
                        x_max * 0.088,
                        lw=line_thickness,
                        color=line_color,
                        fill=False,
                        zorder=-1)

    ax.add_artist(circle)

    arc1 = Arc((arc_points1[first], arc_points1[second]),
               height=x_max * 0.088 * 2,
               width=x_max * 0.088 * 2,
               angle=arc_angle,
               theta1=128.75,
               theta2=231.25,
               color=line_color,
               lw=line_thickness,
               zorder=-1)

    ax.add_artist(arc1)

    arc2 = Arc((arc_points2[first], arc_points2[second]),
               height=x_max * 0.088 * 2,
               width=x_max * 0.088 * 2,
               angle=arc_angle,
               theta1=308.75,
               theta2=51.25,
               color=line_color,
               lw=line_thickness,
               zorder=-1)

    ax.add_artist(arc2)

    ax.set_aspect("equal")

    return ax

现在只需要调用函数来绘制想要的足球场!

background = "#09b309"

fig, ax = plt.subplots(figsize=(11, 7))
fig.set_facecolor(background)

draw_pitch(orientation="h",
           aspect="full",
           pitch_color=background, 
           line_color="lightgrey",
           ax=ax)

plt.tight_layout()
plt.show()

下面这张图是绘制过程gif。



代码仓库:How-To-Python: 人生苦短,我用Python

相关文章:

  • 哈尔滨市建设局网站/软文推广例子
  • 网站开发需要懂哪些/电商seo搜索优化
  • 高端建站费用/国内最好的搜索引擎
  • 如何做网站ppt/网站的营销策略
  • 网站地图导出怎么做/网页设计免费模板
  • 我想创业做网站/推广手段
  • 详解 Go 语言中的 init () 函数
  • 修复 爱普生 EPSON L4156 打印机 无法打印,开关 WIFI 墨水 三个灯同时闪烁的问题
  • 卡尔曼滤波之最优状态估计和最优状态估计算法
  • 【计算机网络】计算机网络复习总结 ----- 链路层
  • JUC并发编程与源码分析笔记06-Java内存模型之JMM
  • 【网关路由测试】——诊断路由测试
  • 软件架构基本功
  • 毫米波传感器原理介绍:测速_1相位
  • 多线程设计模式-全面详解(学习总结---从入门到深化)
  • Springboot面向高校应届毕业生的服务系统39t7k计算机毕业设计-课程设计-期末作业-毕设程序代做
  • java毕业设计“小蜜蜂”校园代取快递系统mybatis+源码+调试部署+系统+数据库+lw
  • OS-调度