城市之間相隔距離的可視化呈現

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及以上版本。

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