澳大利亚的首都和首府城市的人口数量

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

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