澳大利亞的首都和首府城市的人口數量

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np

# city population in 2017
locations = {"Sydney":5131326,"Melbourne":4850740,
             "Brisbane":2408223,"Adelaide":1333927,
             "Perth":2043138,"Hobart":226884,
             "Darwin":146612,"Canberra":410301}

# Latitude and Longitude in degrees
names = {"Sydney":(-33.86785,151.20732),"Melbourne":(-37.8142,144.96332),
         "Brisbane":(-27.46794,153.02809),"Adelaide":(-34.92866,138.59863),
         "Perth":(-31.95224,115.8614),"Hobart":(-42.87936,147.32941),
         "Darwin":(-12.46113,130.84185),"Canberra":(-35.28346,149.12807)}

# setup mercator map projection
basemap = Basemap(projection="merc",
              resolution="h",
              area_thresh=0.1,
              llcrnrlon=112,llcrnrlat=-45,
              urcrnrlon=155,urcrnrlat=-8)

# draw several map elements
basemap.drawcoastlines(linewidth=0.6,linestyle="-",color="#b7cfe9",zorder=3)
basemap.drawrivers(linewidth=0.8,linestyle="-",color="#689CD2",zorder=2)

basemap.fillcontinents(color="#BF9E30",lake_color="#689CD2",zorder=1)
basemap.drawmapboundary(color="gray",fill_color="#689CD2")

basemap.drawmeridians(np.arange(0,360,15),color="#4e8bca",labels=[0,0,0,1],labelstyle="+/-")
basemap.drawparallels(np.arange(-90,90,15),color="#4e8bca",labels=[1,1,0,0],labelstyle="+/-")


# convert lon/lat (in degrees) to x/y map projection coordinates (in meters)
# longitude is transformed into x and latitude is transformed into y
names_values = []
names_keys = list(names.keys())
for i,name in enumerate(names_keys):
    names_values.append(names[name])

lat_x,long_y = list(zip(*names_values))
x,y = basemap(long_y,lat_x)

# draw city markers and add text to markers
size_factor = 80.0
offset_factor = 21000
rotation = 30
max_population = max(locations.values())

for city_name,city_x,city_y in zip(names_keys,x,y):
    size = (size_factor/max_population)*locations[city_name]
    x_offset = offset_factor
    y_offset = offset_factor
    basemap.scatter(city_x,
                    city_y,
                    s=size,
                    facecolor="w",
                    edgecolors="r",
                    linewidths=2.0,
                    zorder=10)
    plt.text(city_x+x_offset,city_y+y_offset,city_name)

# setup map title
font = dict(family="serif",fontsize=15,weight="bold")
plt.title("Australian Population of Capital City",**font)

plt.show()

澳大利亞是大洋洲的主要組成部分,以其特有的氣候、地貌和動植物聞名遐邇。最近,澳大利亞人口數量增長迅猛,尤其是首都和首府城市最爲明顯。下面,我們就藉助地圖來概覽各大城市的人口數量。同時,以Python代碼的形式具體講解使用類Basemap繪製地圖的方法。

2.代碼精講
從效果圖(請讀者根據以上代碼自行生成效果圖)中可以觀察到澳大利亞的首都和各個首府城市的人口數量情況。標記點越大,表示城市人口數量越多,而且主要集中在墨爾本、悉尼、布里斯班和阿德萊德等首府城市。有關城市人口數量的源數據參見Australia_city_population.xlsx文件。接下來,我們具體講解“代碼實現”部分裏的相關語句。
首先,通過變量locations定義城市的人口數量,使用變量names存儲城市的緯度和經度。這兩個變量的數據結構都是字典。
調用類Basemap生成實例basemap。類Basemap的構造函數的參數含義如下。
● projection:地圖投影模式。
● resolution:地圖邊緣的分辨率。
● area_thresh:海岸線和湖泊顯示的單位面積的大小要求。
● llcrnrlon:左下角的經度。
● llcrnrlat:左下角的緯度。
● urcrnrlon:右上角的經度。
● urcrnrlat:右上角的緯度。
其次,我們調用類Basemap的實例basemap的實例方法,繪製海岸線,繪製河流,填充大陸顏色,繪製地圖邊界,填充大陸之外的地圖背景顏色。同時,繪製經度線和緯度線。通過調用這些實例方法就完成了繪製地圖元素的任務。也就是說,分別調用實例方法drawcoastlines()、drawrivers()、fillcontinents()、drawmapboundary()、drawmeridians()和drawparallels(),實現繪製地圖元素的目標。
這裏需要補充的是,實例方法drawcoastlines()的參數linewidth、linestyle和color的含義和繪製折線圖的實例方法plot()的參數含義相同,分別表示線條寬度、線條風格和線條顏色。
通過調用“basemap.drawmeridians(np.arange(0,360,15),color="#4e8bca",labels=[0,0,1,1],labelstyle="+/-")”語句,也就是調用和執行實例方法drawmeridians(),實現繪製經度線和添加經度度數標籤值的目標。其中,參數labels中的“0”和“1”分別表示不繪製和繪製的意思,列表labels中的4個元素分別表示是否繪製與投影地圖有交叉的左側、右側、頂部和底部的經度值。參數labels的取值“[0,0,0,1]”表示繪製與投影地圖有交叉的底部的經度值。參數labelstyle的取值“+/-”表示西經取負值,東經取正值。
同理,通過調用“basemap.drawparallels(np.arange(-90,90,15),color="#4e8bca",labels=[1,1,0,0],labelstyle="+/-")”語句,繪製緯度線和標記緯度值。參數labels的取值“[1,1,0,0]”表示繪製與投影地圖有交叉的左側和右側的緯度值。
通過調用“names.keys()”語句,也就是調用字典names的keys()方法,將字典names的所有鍵存儲在列表names_keys中。
調用內置函數zip()可以將緯度值和經度值分別放在一個元組裏,最後用一個列表存儲這兩個元組。這個過程是通過“zip(names_values)”語句完成的。符號“”表示將列表 names_values 中的元素全部提取出來。這樣,使用內置函數zip()就可以將緯度值和經度值分別存儲在一個元組裏。最後,賦值給變量lat_x和long_y。
實例basemap可以作爲函數被調用,從而將經度值和緯度值分別轉換成以米爲單位的地圖投影值。也就是說,調用“basemap(long_y,lat_x)”語句,完成經度值和緯度值向數值(以米作爲計量單位)的映射過程,從而將轉換後的經度值存儲在變量x中,將轉換後的緯度值存儲在變量y中。
最後,需要將不同城市的人口數量投射到地圖上,使用散點圖的標記表示人口數量,使用標記大小表示數量多少。這個過程是通過調用實例 basemap 的實例方法 scatter()完成的。爲了使得標記點不被其他地圖元素覆蓋,可以將參數 zorder 的取值設置成 10,遠遠大於其他實例方法中的參數zorder 的取值。這麼設置的原因就是保證散點圖的標記可以顯示在地圖最上面,這樣標記的邊緣就不會被其他地圖元素遮擋了。而且,參數 zorder 的取值越大,相應的地圖元素距離地圖畫布越遠,也就是離觀察者的距離越近。
這裏需要補充的是,我們可以將效果圖理解成多個層的疊加:地圖是第一層,位於所有層的最下面;第二層是地圖上的海岸線、大陸和經緯線等地圖元素;第三層是散點標記,也就是最上面的這一層。
同時,爲了用標記的大小表示人口數量的多少,我們將標記的大小設置成與人口數量的多少成比例的變化模式,也就是通過調用“(size_factor/max_population)*locations[city_name]”語句,完成標記大小的動態設置任務。其中,變量max_population中存儲的是城市人口數量中的最大值,使用內置函數max()獲得這個最大值。調用“locations.values()”語句,也就是調用字典locations的values()方法,獲得字典locations的所有與鍵相對應的鍵值,也就是人口數量。
還需要補充的是有關內置函數zip()的使用方法。內置函數zip()的參數可以是列表或元組,也可以是列表和元組的組合,返回值是以元組爲元素的列表,即元組列表。這樣,就可以使用 for 循環語句,完成每個城市的人口數量和城市名稱迭代標記的任務。在添加不同城市名稱的過程中,我們使用模塊pyplot的API,調用函數text()給每個城市人口數量的標記點添加城市名稱,從而實現更加友好的地圖可視化繪製效果。
通過調用“plt.show()”語句,完整地呈現澳大利亞的首都和各個首府城市人口數量的特徵和差異,完成使用地圖概覽人口數量的可視化目標。
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及以上版本。

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