2021-06-21

地图着色问题

数据中附带了画图的源代码,这次使用的是python自带的tkinter库,所以不用安装任何库就可以直接运行。

代码中包括了读取数据的方法和画多边形的方法,都比较简单。

这里重点介绍一下填色算法。这个算法当然会用到递归。其实凡是用了递归的算法,从概念上都相当简明。

算法是一个函数:填色(setColor),传递三个参数:地图的邻接性信息(以及已填色的区域信息也暂存在其中),可用的颜色列表(避免用全局变量,所以当作参数传递),准备填色的区域。

前两个参数是好理解的,第三个参数,实际上是递归算法的一个标志。

由于填色结果保存在了第一个参数中,所以函数只有一个返回值,就是填色是否成功True/False。

算法的逻辑是这样设计的:

如果这个“准备填色的区域”已经有颜色了,当然就是成功的,直接返回成功True;
本区域尚未填色的情况下,查看这个区域周围所有已经填过颜色的邻接区域,那些区域用过的颜色肯定是不能用的。如果在颜色列表中已经没有剩余颜色了,填色失败,返回False;
本区域尚未填色,并且在列表中有剩余颜色,那就是有成功的机会了,但究竟填哪个颜色合适呢?用循环的方法,一个一个地试。试的标准就是:周围所有邻接区域都能填色成功。注意:这里就出现递归了;
在循环中,给本区域试填列表中剩下的一种颜色,然后逐一给周围尚未填色的邻接区域填色——调用填色算法:setColor——当然这里第三个参数就变成了对应的区域;
如果每个邻接区域都返回填色成功(返回True),本区域选择这个颜色也就没有问题了。于是也返回成功True;
如果有一个邻接区域无法填色(返回False),就说明本区域不能选择这个颜色,换填循环中的下一个颜色,继续试验。转到步骤4;
如果循环之后,无论使用哪个颜色,都无法给本区域成功填色,只能返回填色失败False。在返回之前,把本区域试填的颜色擦除。

完整代码如下:
from tkinter import *
import random

def rgb(*args):
if len(args)==1:
value= args[0]
elif len(args)==3:
value= args
else:
raise Exception(‘参数错误:’+str(args))
digit = [str(i) for i in range(10)] + list(“ABCDEF”)
string = ‘#’
for i in value:
i= int(i)
a1 = i // 16
a2 = i % 16
string += digit[a1] + digit[a2]
return string

def setColor(adj, clist, theReg):
cclist= clist.copy()
if adj[theReg].get(“color”, None):
# 有颜色,正常
return True
else:
# 查看所有相邻区域的颜色,选取未用过的颜色
for reg in adj[theReg][‘naber’]:
c= adj[reg].get(“color”, None)
# print(reg, c)
if c in cclist:
cclist.remove©
if len(cclist)>0:
# 有可用的颜色,逐个试验
random.shuffle(cclist)
for color in cclist:
adj[theReg][‘color’]= color
can= True
# 检测周边所有相邻的区域是否可以正常填色
regList= []
for reg in adj[theReg][‘naber’]:
if adj[reg].get(“color”, None):
# 只考虑未填色的地方
continue
regList.append(reg)
can= setColor(adj, clist, reg)
if not can:
# 有区域不能正常填色,则无须继续尝试
break
if can:
# 如果能够染色成功,不再尝试其它颜色
break
if not can:
# 本区域填色失败,将颜色擦除
print(‘颜色回退’)
adj[theReg][‘color’]= None
return can
else:
# 所有颜色都不可用,回退
return False

读入邻接信息

with open(‘ProvNaber.txt’, ‘r’, encoding=‘utf-8’) as f1:
provadj= eval(f1.read())

格式调整

provColor={}
for prov in provadj:
provColor[prov]= {}
provColor[prov][‘naber’]= provadj[prov].copy()

常用地图色调

rgblist=[
(255,253,218),
(223,237,214),
(255,210,227),
(220,217,234),
(255,228,195),
]
clist= [rgb(x[0], x[1], x[2]) for x in rgblist]

进行填色

ret= setColor(provColor, clist, ‘北京’)
if not ret:
exit()

省界地图填色显示

root = Tk()
root.title(‘地图填色’)
cv = Canvas(root,bg = ‘white’,width=800,height=800)
with open(‘chinaProv.txt’,‘r’, encoding=‘utf-8’) as f1:
china= eval(f1.read())
for prov in china:
name= prov[‘shortName’]
color= provColor[name][‘color’]
shp= [((p[0]- 70)*12, 800- (p[1]-10)*14) for p in prov[‘shape’]]
cv.create_polygon(shp,fill=color,outline=‘grey’)
cv.pack()
root.mainloop()
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值