城市之间相隔距离的可视化呈现

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

from mpl_toolkits.basemap import Basemap

class MapDisVisualization(Basemap):

    # get city names
    def getCityNames(self,names):
        namesKeys = list(names.keys())
        return namesKeys

    # define distance between cityA and cityB
    def citiesDistance(self,x,y):
        d = np.power(np.power(x[0]-y[0],2)+np.power(x[1]-y[1],2),0.5)
        distance = round(d,4)
        return distance

    # compute distance between target city and every other city
    def centerCityDistance(self,city,names):
        distanceDict = {}
        namesKeys = self.getCityNames(names)
        for i,name in enumerate(namesKeys):
            if name != city:
                distanceDict[name] = self.citiesDistance(names[city],names[name])
        return distanceDict

    # compute line width and line color
    def setcolorandwidth(self,city,names):
        size_factor = 2.0
        namesKeys = self.getCityNames(names)
        distanceDict = self.centerCityDistance(city,names)
        distanceList = list(distanceDict.values())
        maxDistance = max(distanceList)
        for i,name in enumerate(namesKeys):
            if name != city:
                self.drawgreatcircle(names[city][1],names[city][0],
                                     names[name][1],names[name][0],
                                     linewidth=size_factor,
                                     color=mpl.cm.Blues(distanceDict[name]/float(maxDistance)))

    # visualize city distance on the map
    def showmap(self,city,names):
        self.setcolorandwidth(city,names)
        namesKeys = self.getCityNames(names)
        number = len(namesKeys)
        titleContent = "a map of visualizing distance between %s and every other city (%d cities)"
        font = dict(family="serif",fontsize=15,weight="black")
        plt.title(titleContent % (city,(number-1)),fontdict=font)
        plt.show()

def main(projection,city):
    # get a Basemap instance
    m = MapDisVisualization(projection=projection,
                            resolution="h",
                            area_thresh=0.1,
                            llcrnrlon=112,llcrnrlat=-50,
                            urcrnrlon=180,urcrnrlat=-8)

    # draw several elements on the map
    m.drawcoastlines(linewidth=0.6,linestyle="-",zorder=2)
    m.fillcontinents(alpha=0.5,zorder=1)
    m.drawmapboundary(color="gray")
    m.drawmeridians(np.arange(100,180,15),linewidth=0.4,labels=[0,0,0,1])
    m.drawparallels(np.arange(-90,0,15),linewidth=0.4,labels=[1,0,0,0])
        
    # Latitude and Longitude in degrees
    names = {"Sydney":(-33.86785,151.20732),"Wellington":(-41.28664,174.77557),
             "Brisbane":(-27.46794,153.02809),"Adelaide":(-34.92866,138.59863),
             "Perth":(-31.95224,115.8614),"Auckland":(-36.86667,174.76667),
             "Darwin":(-12.46113,130.84185),"Canberra":(-35.28346,149.12807)}

    #show the distance between Sydney and every other city
    m.showmap(city,names)

if __name__ == "__main__":
    # use projection mercator and choose Sydney
    main("merc","Sydney")

很多时候,为了清楚地展示城市之间的相隔距离,我们可以在地图上用曲线的长度表示距离的长度,直观地展示城市之间的相对位置和物理距离。下面,就使用类 Basemap 的实例方法drawgreatcircle(),实现城市之间物理距离的可视化效果。

2.代码精讲
定义基类Basemap的子类MapDisVisualization。在子类MapDisVisualization中,新添加一些实例方法。关于类的继承和实现方法如下所示。
定义一个类NewClass,也称为基类NewClass,基类NewClass包括一些属性和实例方法。如果想要在基类NewClass中添加一些实例方法,则可以创建一个子类SubNewClass。子类SubNewClass不仅具有基类 NewClass 的属性和方法,还具有自己的属性和方法。这就是类的继承。所谓类的继承,就是在基类的基础上,增加一些额外的属性和方法。这就类似于生物进化,如鹰和鸵鸟,虽然都属于鸟类(可以理解成基类),但是鹰具有飞翔的本领,鸵鸟善于奔跑,具有不同的行为;鹰以食肉为主,鸵鸟以食草为主,具有不同的属性,从而形成了不同的子类。实现类的继承的方法是“class SubNewClass(NewClass):”,也就是说,将基类NewClass放在原来放置对象的括号中,而不是用“class SubNewClass(object):”实现类的继承,从而告诉Python,类SubNewClass想要继承类NewClass中的属性和方法。这样,类SubNewClass就实现了对类NewClass的继承,也就产生了子类SubNewClass。关于类的相关内容,可以参考《Python数据可视化之matplotlib实践》中附录A的内容。
下面就具体解释在子类MapDisVisualization中添加实例方法的作用和实现要点。
实例方法 getCityNames(names)的主要作用是获得字典中的所有键,将全部的键存储在列表namesKeys中,返回值是列表namesKeys。获得列表namesKeys是通过调用“names.keys()”语句实现的,也就是调用字典 names 的 keys()方法获得字典中的所有键,将这些键存储在列表namesKeys中。
实例方法 citiesDistance(x,y)的主要作用是计算任何两个城市之间的距离。城市之间的距离可以使用欧几里得距离进行计算,简称为欧氏距离。点 a 和点 b 之间的欧氏距离是,其中,a=(x1, y1),b=(x2, y2)。具体到每个城市的位置,我们使用纬度和经度进行定位。变量distance中存储的是保留4位有效数字的城市之间的欧氏距离。返回值是城市之间的欧氏距离。
实例方法 centerCityDistance(city,names)的主要作用是将城市之间的距离和城市进行对应,也就是说,选择参数city作为中心城市,参数names中的城市依次计算与中心城市的距离,然后将距离中心城市的城市名称作为字典 distanceDict 的键,将城市之间的距离作为键值。返回值是字典distanceDict。
实例方法setcolorandwidth(city,names)的主要作用是绘制使用圆弧表示的城市之间的距离。参数 city 是中心城市,参数 names 是需要计算与中心城市的距离的城市。通过调用“distanceDict.values()”语句,也就是使用字典distanceDict的values()方法,获得由城市之间的距离所组成的列表,存储在变量distanceList中。使用内置函数max()获得城市之间的最远距离,也就是通过调用“max(distanceList)”语句,将最远距离存储在变量 maxDistance中。调用实例方法drawgreatcircle(lon1,lat1,lon2,lat2,**kwargs),可以绘制从经纬度对lon1和lat1到经纬度对lon2和lat2之间的圆弧。参数lon1和lat1分别是城市1的经度和纬度,参数lon2和lat2分别是城市2的经度和纬度。参数kwargs可以使用模块pyplot的函数plot()中的参数,这样,参数linewidth和color就可以作为实例方法drawgreatcircle()的参数。需要补充的是,参数color的取值是调用模块cm中的变量Blues,也就是说,配色方案Blues可以作为变量,用来存储类LinearSegmentedColormap的实例。类 LinearSegmentedColormap 是模块 colors 中的类 Colormap 的子类,类 LinearSegmentedColormap的实例可以作为函数被调用。也就是说,变量Blues可以作为一个函数,可以向函数Blues()中传入[0.0,1.0]范围内的任意浮点数,从而将浮点数值映射成配色方案Blues里的对应颜色。在浮点数的获取上,是通过计算作为分子的每个城市与中心城市的距离和作为分母的城市之间的最远距离的比值获得的,也就是使用表达式“distanceDict[name]/float(maxDistance)”获得函数 Blues()的[0.0,1.0]范围内的浮点数输入值。
实例方法showmap(city,names)的主要作用是展示以圆弧形式表示的城市之间的距离,同时调用模块pyplot中的函数title()绘制地图的标题,使用参数fontdict控制文本的展示格式,包括字体样式、字体大小和字体加粗模式。
接下来,我们讲解函数main()的作用和实现方法。
通过继承自基类Basemap的子类MapDisVisualization的实例化,得到实例m。通过调用继承自基类Basemap的实例方法,绘制海岸线,填充大陆颜色,绘制地图边界,绘制经度线和纬度线。使用变量names存储城市名称和对应的纬度及经度信息的字典。调用“m.showmap(city,names)”语句,实现调用子类MapDisVisualization中的实例方法showmap()的目标。
如果执行这个脚本,if语句的条件表达式“name==“main””的判断值为“True”,就可以调用函数main()。在调用过程中,也可以使用异常语法,实现触发异常和异常处理的任务需求,也就是try和except语句。这样,我们就实现了绘制用圆弧表示城市之间的距离的可视化任务。

3.内容补充
在Python 3.x中,需要使用内置函数list()将可迭代对象转化成列表。另外,由于matplotlib 3.0.1与basemap 1.1.x及以上版本(例如,basemap 1.2.0)不兼容,因此,读者需要使用不同的matplotlib版本(例如,matplotlib 2.2.3),或者使用basemap 1.1.x以下的版本(例如,basemap 1.0.7)。这样,matplotlib 2.2.3搭配basemap 1.2.0,或者matplotlib 3.0.1搭配basemap 1.0.7,或者matplotlib 3.0.2搭配basemap 1.2.0,都是可以考虑的组合方式。在Python 3.5及以上版本的环境下,才可以安装matplotlib 3.0.x。也就是说,matplotlib 3.0.x仅仅支持Python 3.5及以上版本。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章