Python绘制热力图实现草原火场模拟

359 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

本文介绍了如何使用Python的seaborn和matplotlib库绘制热力图,以仿真草原火灾演化的过程。

背景描述

  • 假设你现在经营一块东西方向宽度500单位,南北方向长度1000单位的麦田。

  • 初始时刻,在麦田的最北端,随机产生了5处起火点。

  • 我们描述火情有以下几种情况:无火→起火→大火→燃尽。各种情况之间可以互相转化,其转化规则为:

    • 无火→起火:在“大火”格的下风处,每分钟有80%的概率转化,在“大火”格的风向90度方向位置,每分钟有30%的概率转化,在“大火”格的背风处,每分钟有1%的概率转化。

    • 起火→大火:某一单元格“起火”后2分钟,自动变为“大火”。

    • 大火→燃尽:某一单元格转化为“大火”后10分钟,自动变为“燃尽”。

    • 燃尽的格子不会转化为任何其他格子。

  • 初始风向为北风(自北向南刮),一定时间后转化为东风,转化的时间为7分钟。

模拟的要求

产生随机的五个“起火”格,然后模拟火情传播的情况,以分钟为单位进行计算。并给出起火后30分钟,60分钟,100分钟的农田火情图,并计算最终完全烧尽需要多长时间(完全烧尽指的是农田中不再有“起火”和“大火”格)。

核心问题 如何绘制热力图

由参考链接,不难得知绘制热力图的函数为sns.heatmap

  • vminvmax参数控制热力图表示的最小值和最大值范围
  • cmap控制做图的颜色
  • cbar控制是否显示图例

相关代码如下

import matplotlib.pyplot as plt
import seaborn as sns
f, ax = plt.subplots(figsize=(5, 10))
sns.heatmap(farmMap, vmin=100, vmax=400, cmap='YlOrRd', cbar=True)
plt.xticks(fontsize=0)
plt.yticks(fontsize=0)
ax.set_title('Field when t=' + timeTile + 'min')
plt.show()

程序设计思路及完整代码

用二维数组表示整个农田的所有单元格,用数组中元素的值代表单元格的状态,根据规则编写状态转换的语句,利用循环来更新每一分钟过去后,新的农田状态。

农田的状态按以下规则表示,这种方法可以满足起火和大火状态时间在1-100秒内波动,算是本方案的亮点。

  • 100 无火
  • 200-201 起火
  • 301-310 大火
  • 400 燃尽

完整的程序如下。

import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
# 假设1个单元格长度为10个单位长度
sns.set()
# 初始化随机种子
np.random.seed(4)
# 定义仿真图区域
maxX = 100
maxY = 50
# 初始化地图状态
# 100 无火 200-201 起火 301-310 大火 400 燃尽
farmMap = np.ones((maxX, maxY), dtype=int) * 100

# 随机生成5个起火点
start = np.random.randint(0, maxY, 5)
print(start)
for thisStart in start:
    farmMap[0, thisStart] = 200

time = 0
# 如果有状态为2xx 3xx findFire = 1
findFire = 1
# 当存在2xx 3xx起火点时
while findFire == 1:
    time = time + 1
    # t=31 61 101时做图
    if time == 31 or time == 61 or time == 101:
        f, ax = plt.subplots(figsize=(5, 10))
        timeTile = str(time - 1)
        sns.heatmap(farmMap, vmin=100, vmax=400, cmap='YlOrRd', cbar=True)
        plt.xticks(fontsize=0)
        plt.yticks(fontsize=0)
        ax.set_title('Field when t=' + timeTile + 'min')
        plt.show()
    findFire = 0
    # 遍历地图上的所有点
    for x in range(0, maxX):
        for y in range(0, maxY):
            # 处理2xx 状态 起火 如果有起火点  findFire 设置为1
            if round(farmMap[x, y] / 100) == 2:
                findFire = 1
                # 燃烧2秒后进行状态转换 -> 大火
                if farmMap[x, y] == 202:
                    farmMap[x, y] = 301
                else:
                # 不足2秒累计时间
                    farmMap[x, y] += 1
                continue
            # 处理3xx 状态 大火 如果有大火点  findFire 设置为1
            if round(farmMap[x, y] / 100) == 3:
                findFire = 1
                # 燃烧10秒后进行状态转换 ->燃尽
                if farmMap[x, y] == 310:
                    farmMap[x, y] = 400
                    continue
                else:
                    # 对两种传播状态进行处理 t<7 纵向传播为主 北风
                    if time < 7:
                        # 对于每一种状态,生成随机种子,判断概率是否满足将周围区域由100状态转为200状态
                        p = np.random.random(1)
                        if x + 1 < maxX:
                            if farmMap[x + 1, y] == 100 and p <= 0.8:
                                farmMap[x + 1, y] = 200
                        p = np.random.random(1)
                        if x - 1 > 0:
                            if farmMap[x - 1, y] == 100 and p <= 0.01 and x - 1 > 0:
                                farmMap[x - 1, y] = 200
                        p = np.random.random(1)
                        if y + 1 < maxY:
                            if farmMap[x, y + 1] == 100 and p <= 0.3 and y + 1 < maxY:
                                farmMap[x, y + 1] = 200
                        p = np.random.random(1)
                        if y - 1 > 0:
                            if farmMap[x, y - 1] == 100 and p <= 0.3 and y - 1 > 0:
                                farmMap[x, y - 1] = 200
                    # 对两种传播状态进行处理 t>7时 横向传播为主 东风
                    else:
                        p = np.random.random(1)
                        if y - 1 > 0:
                            if farmMap[x, y - 1] == 100 and p <= 0.8 and y - 1 > 0:
                                farmMap[x, y - 1] = 200
                        p = np.random.random(1)
                        if y + 1 < maxY:
                            if farmMap[x, y + 1] == 100 and p <= 0.01 and y + 1 < maxY:
                                farmMap[x, y + 1] = 200
                        p = np.random.random(1)
                        if x - 1 > 0:
                            if farmMap[x - 1, y] == 100 and p <= 0.3 and x - 1 > 0:
                                farmMap[x - 1, y] = 200
                        p = np.random.random(1)
                        if x + 1 < maxX:
                            if farmMap[x + 1, y] == 100 and p <= 0.3 and x + 1 < maxX:
                                farmMap[x + 1, y] = 200
                    farmMap[x, y] = farmMap[x, y] + 1
                    continue
    # 如果不存在 2xx 3xx 点 程序终止
    if findFire == 0:
        print('完全烧尽需要'+str(time) + '分钟')
        break

效果如下。

t=30min时火场热力图。

image-t=30min时火场热力图

t=99min时火场热力图

image-t=99min时火场热力图

t=299min时火场热力图

image-t=299min时火场热力图

参考链接

Python-Seaborn热力图绘制