01-地理数据介绍
源代码请看此处
1.1 Python地理空间矢量数据简介
%matplotlib inline
import pandas as pd
import geopandas
导入地理数据
地理空间数据通常可以从特定的GIS文件格式或数据存储中获得,如ESRI shapefiles、GeoJSON文件、geopackage文件、PostGIS(PostgreSQL)数据库、...
我们可以使用GeoPandas库中的geopandas.read_file函数,来读取其中的很多GIS文件格式(依靠的是fiona库,它是GDAL/OGR的python接口)。
例如,我们先读取一个包含世界所有国家边界的shapefile文件,zip文件放到在/data目录下)。
countries=geopandas.read_file("zip://data/ne_110m_admin_0_countries.zip")
countries.head()
featurecla | scalerank | LABELRANK | SOVEREIGNT | SOV_A3 | ADM0_DIF | LEVEL | TYPE | ADMIN | ADM0_A3 | ... | NAME_KO | NAME_NL | NAME_PL | NAME_PT | NAME_RU | NAME_SV | NAME_TR | NAME_VI | NAME_ZH | geometry | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | Admin-0 country | 1 | 6 | Fiji | FJI | 0 | 2 | Sovereign country | Fiji | FJI | ... | 피지 | Fiji | Fidżi | Fiji | Фиджи | Fiji | Fiji | Fiji | 斐濟 | MULTIPOLYGON (((180.00000 -16.06713, 180.00000... |
1 | Admin-0 country | 1 | 3 | United Republic of Tanzania | TZA | 0 | 2 | Sovereign country | United Republic of Tanzania | TZA | ... | 탄자니아 | Tanzania | Tanzania | Tanzânia | Танзания | Tanzania | Tanzanya | Tanzania | 坦桑尼亚 | POLYGON ((33.90371 -0.95000, 34.07262 -1.05982... |
2 | Admin-0 country | 1 | 7 | Western Sahara | SAH | 0 | 2 | Indeterminate | Western Sahara | SAH | ... | 서사하라 | Westelijke Sahara | Sahara Zachodnia | Saara Ocidental | Западная Сахара | Västsahara | Batı Sahra | Tây Sahara | 西撒哈拉 | POLYGON ((-8.66559 27.65643, -8.66512 27.58948... |
3 | Admin-0 country | 1 | 2 | Canada | CAN | 0 | 2 | Sovereign country | Canada | CAN | ... | 캐나다 | Canada | Kanada | Canadá | Канада | Kanada | Kanada | Canada | 加拿大 | MULTIPOLYGON (((-122.84000 49.00000, -122.9742... |
4 | Admin-0 country | 1 | 2 | United States of America | US1 | 1 | 2 | Country | United States of America | USA | ... | 미국 | Verenigde Staten van Amerika | Stany Zjednoczone | Estados Unidos | Соединённые Штаты Америки | USA | Amerika Birleşik Devletleri | Hoa Kỳ | 美国 | MULTIPOLYGON (((-122.84000 49.00000, -120.0000... |
5 rows × 95 columns
countries.plot()
我们可以观察到:
使用 .head()可以得到数据集的前五列,就像使用Pandas一样
有一个 geometry列,用于存储不同的国家的边界多边形
我们可以使用 .plot()方法来快速得到数据的基本可视化效果
1.2 GeoDataFrame 介绍
我们使用GeoPandas库来读取地理空间数据,然后返回一个GeoDataFrame对象。
type(countries)
geopandas.geodataframe.GeoDataFrame
一个GeoDataFrame包含一个表格式的地理空间数据集
它有一个geometry列,用来保存几何信息(或GeoJSON要素)
其他列是描述每个几何体的属性(或GeoJSON属性)
这样的GeoDataFrame就像pandas.DataFrame一样,但它具有一些额外的功能,用于处理地理空间数据
一个.geometry属性,总是返回带有几何信息的列(返回一个GeoSeries)。列名本身不一定是 geometry,但它总是作为.geometry属性被访问。
它有一些额外的方法来处理空间数据(面积、距离、缓冲区、交叉点......),我们将在后面的教程中看到
countries.geometry
0 MULTIPOLYGON (((180.00000 -16.06713, 180.00000...
1 POLYGON ((33.90371 -0.95000, 34.07262 -1.05982...
2 POLYGON ((-8.66559 27.65643, -8.66512 27.58948...
3 MULTIPOLYGON (((-122.84000 49.00000, -122.9742...
4 MULTIPOLYGON (((-122.84000 49.00000, -120.0000...
...
172 POLYGON ((18.82982 45.90887, 18.82984 45.90888...
173 POLYGON ((20.07070 42.58863, 19.80161 42.50009...
174 POLYGON ((20.59025 41.85541, 20.52295 42.21787...
175 POLYGON ((-61.68000 10.76000, -61.10500 10.890...
176 POLYGON ((30.83385 3.50917, 29.95350 4.17370, ...
Name: geometry, Length: 177, dtype: geometry
type(countries.geometry)
geopandas.geoseries.GeoSeries
countries.geometry.area
0 1.639511
1 76.301964
2 8.603984
3 1712.995228
4 1122.281921
...
172 8.604719
173 1.479321
174 1.231641
175 0.639000
176 51.196106
Length: 177, dtype: float64
它仍然是一个DataFrame,所以我们可以在地理空间数据集上使用所有的pandas功能,并对属性和几何信息一起进行数据操作
例如,我们可以计算所有国家的平均人口数量(通过访问'POP_EST'列,并调用平均值方法)。
countries['POP_EST'].mean() # 计算所有国家的平均人口数量
41712369.84180791
或者,我们可以使用布尔筛选器,根据条件选择数据框的一个子集
africa=countries[countries['CONTINENT']=='Africa']
africa.plot()
本教程的其余部分将假设你已经知道一些Pandas的基础知识,但我们会尽量为那些不熟悉的人提供这部分的提示
如果你想了解更多关于pandas的知识,可以参考一些资源
知识点:
GeoDataFrame 可以将典型的表格数据分析与空间操作结合起来进行
GeoDataFrame(或要素集,Feature Collection)由以下部分组成:
Geometries或features:空间对象
Attributes或properties:包含每个空间对象的非空间信息的列
1.3 矢量要素:点、线、面
空间矢量数据可以包括不同的类型,3种基本类型是:
点数据:表示空间中的一个点
线条数据(LineString):表示构成一条线的点的序列
多边形数据:表示一个填充区域
它们中的每一个都可以组合成多部分的矢量数据(参见https://shapely.readthedocs.io/en/stable/manual.html#geometric-objects 查看详细的概述)。在我们目前看到的例子中,各个矢量对象都是多边形
print(countries.geometry[2])
POLYGON ((-8.665589565454809 27.65642588959236, -8.665124477564191 27.58947907155823, -8.684399786809053 27.39574412689601, -8.6872936670174 25.88105621998891, -11.96941891117116 25.93335276946827, -11.93722449385332 23.37459422453617, -12.87422156416958 23.28483226164518, -13.11875444177471 22.77122020109626, -12.92910193526353 21.32707062426756, -16.84519365077399 21.33332347257488, -17.06342322434257 20.99975210213083, -17.02042843267577 21.42231028898158, -17.00296179856109 21.42073415779658, -14.75095455571353 21.50060008390366, -14.63083268885107 21.8609398462749, -14.22116777185725 22.31016307218816, -13.89111039880905 23.6910090194593, -12.50096269372537 24.7701162785782, -12.03075883630163 26.03086619720307, -11.71821977380036 26.10409170176062, -11.39255489749701 26.88342397715439, -10.55126257978527 26.99080760345689, -10.18942420087758 26.86094472910741, -9.735343390328879 26.86094472910741, -9.41303748212448 27.08847606048857, -8.794883999049077 27.12069631602251, -8.817828334986672 27.65642588959236, -8.665589565454809 27.65642588959236))
让我们导入一些其他具有不同类型几何对象的数据集:
一个世界各城市数据集,由Point数据组成
cities = geopandas.read_file("zip://data/ne_110m_populated_places.zip")
print(cities.geometry[0])
POINT (12.45338654497177 41.90328217996012)
还有一个世界河流的数据集,zip文件在/data目录下,每条河流都是一条LineString
# ! wget https://naciscdn.org/naturalearth/50m/physical/ne_50m_rivers_lake_centerlines.zip
--2020-08-12 00:23:33-- https://naciscdn.org/naturalearth/50m/physical/ne_50m_rivers_lake_centerlines.zip
Resolving naciscdn.org (naciscdn.org)... 146.201.97.163
Connecting to naciscdn.org (naciscdn.org)|146.201.97.163|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 457951 (447K) [application/zip]
Saving to: ‘ne_50m_rivers_lake_centerlines.zip’
ne_50m_rivers_lake_ 100%[===================>] 447.22K 8.16KB/s in 53s
2020-08-12 00:24:31 (8.43 KB/s) - ‘ne_50m_rivers_lake_centerlines.zip’ saved [457951/457951]
rivers = geopandas.read_file("zip://data/ne_50m_rivers_lake_centerlines.zip")
print(rivers.geometry[0])
LINESTRING (51.9371337598152 55.70106609892139, 51.88086646731369 55.68625891701544, 51.82031249962222 55.69745514553858, 51.7476018274624 55.69366250841807, 51.6628417966117 55.60817291874525, 51.57871093775964 55.59943268477065, 51.51342773400279 55.58312409100404, 51.50854492161091 55.52948639548083, 51.48583984403365 55.49640534033426, 51.36914062543957 55.46796295772435, 51.21306254869774 55.50264985760492, 51.13452148447897 55.48273346527725, 51.07934570274205 55.46759674659262, 50.98022460947817 55.46637604371949, 50.83445217522774 55.45630956063775, 50.6883789060617 55.42011139502489, 50.4118652342932 55.40119049644431, 50.07802734358711 55.38112213757665, 49.82216796867687 55.33466217681809, 49.53222656260584 55.260614325191, 49.38232421848795 55.17182037990665, 49.24808475131027 55.11301870345045)
1.4 shapely库简介
各个Geometry对象由shapely库提供支持
type(countries.geometry[0])
shapely.geometry.multipolygon.MultiPolygon
下面是使用shapely构建矢量要素的方法
from shapely.geometry import Point, Polygon, LineString
p=Point(0,0)
print(p)
POINT (0 0)
polygon=Polygon([(1,1),(2,2),(2,1)])
polygon.area
0.5
polygon.distance(p) # 多边形到点的最近距离
1.4142135623730951
知识点:
GeoPandas中的矢量对象是由shapely库继承得到的
如果访问一个GeoDataFrame的geometry对象,则得到一个shapely几何体对象
这些对象具有类似于geopandas对象(GeoDataFrame/GeoSeries)的功能
1.5 图层叠加显示
ax=countries.plot(edgecolor='k',facecolor='none',figsize=(15,10))
rivers.plot(ax=ax)
cities.plot(ax=ax,color='red')
ax.set(xlim=(73,135),ylim=(0,55))
[(0.0, 55.0), (73.0, 135.0)]
参见04-更多关于可视化,了解更多关于地理空间数据集可视化的细节
1.6 本章练习
在本课程的练习中,我们将使用几个关于巴黎市的数据集:
巴黎的行政区划: paris_districts_utm.geojson
巴黎公共自行车站点数据:paris_bike_stations_mercator.gpkg。
1.6.1 练习一
探索自行车站点数据data/paris_bike_stations_mercator.gpkg
读取stations数据集
检查读取返回对象的类型type()
检查数据框的第一行,探索这个数据集包含什么样的矢量要素及其字段
检查数据集中的要素的数量
stations=geopandas.read_file('data/paris_bike_stations_mercator.gpkg')
type(stations)
geopandas.geodataframe.GeoDataFrame
stations.head()
name | bike_stands | available_bikes | geometry | |
---|---|---|---|---|
0 | 14002 - RASPAIL QUINET | 44 | 4 | POINT (259324.887 6247620.771) |
1 | 20503 - COURS DE VINCENNES PYRÉNÉES | 21 | 3 | POINT (267824.377 6249062.894) |
2 | 20011 - PYRÉNÉES-DAGORNO | 21 | 0 | POINT (267742.135 6250378.469) |
3 | 31008 - VINCENNES (MONTREUIL) | 56 | 0 | POINT (271326.638 6250750.824) |
4 | 43006 - MINIMES (VINCENNES) | 28 | 27 | POINT (270594.689 6248007.705) |
stations.shape
(1226, 4)
1.6.2 练习二
station的快速可视化实现
使用figsize参数调整可视化图片大小
stations.plot()
stations.plot(figsize=(12,6))
一个只显示点的图,如果没有任何空间背景,是很难解释的。因此,在接下来的练习中,我们将学习如何添加背景图。
我们要利用contextily包,这个包的add_basemap()函数可以很容易地给我们的绘图添加一个网络地图背景。我们首先绘制数据,然后将matplotlib axes对象(由dataframe的plot()方法返回)传递给add_basemap()函数,然后contextily将下载你的绘图地理范围所需的地图瓦片
# ! pip3 install contextily
import contextily
axes=stations.plot(figsize=(12,6), markersize=5)
contextily.add_basemap(axes)
1.6.3 练习三
做一个统计直方图,显示的自行车站数量分布情况
stations['bike_stands'].hist()
1.6.4 练习四
绘制stations数据集的分布图(figsize=(12,6))
使用available_bikes列确定点的颜色(使用column=参数)
使用 legend=True 参数显示色带(color bar)
stations.plot(column='available_bikes',legend=True,figsize=(12,6))
1.6.5 练习五
探索巴黎行政区的数据集(作为GeoJSON文件提供:"data/paris_districts_utm.geojson")
将数据集读入名为的GeoDataFramedistricts
检查GeoDataFrame的第一行,该数据集包含哪些属性字段?
检查数据集中有多少个要素
districts快速可视化,figsize=(12,6)
districts=geopandas.read_file("data/paris_districts_utm.geojson")
districts.head()
id | district_name | population | geometry | |
---|---|---|---|---|
0 | 1 | St-Germain-l'Auxerrois | 1672 | POLYGON ((451922.133 5411438.484, 451922.080 5... |
1 | 2 | Halles | 8984 | POLYGON ((452278.419 5412160.893, 452192.407 5... |
2 | 3 | Palais-Royal | 3195 | POLYGON ((451553.806 5412340.522, 451528.058 5... |
3 | 4 | Place-Vendôme | 3044 | POLYGON ((451004.908 5412654.095, 450960.640 5... |
4 | 5 | Gaillon | 1345 | POLYGON ((451328.752 5412991.278, 451294.721 5... |
districts.shape
(80, 4)
districts.plot(figsize=(12,6))
1.6.6 练习六 找出面积最大的区
计算每个区的面积
将面积列作为新属性添加到districts dataframe中
按从大到小的区域列对dataframe进行排序(降序)
districts.geometry.area
0 8.685379e+05
1 4.122371e+05
2 2.735494e+05
3 2.693111e+05
4 1.879097e+05
...
75 1.294254e+06
76 8.061191e+05
77 1.486139e+06
78 1.598127e+06
79 2.089783e+06
Length: 80, dtype: float64
# 转换为km² 10^6
districts['area'] = districts.geometry.area / 1e6
districts.sort_values(by='area', ascending=False)
id | district_name | population | geometry | area | |
---|---|---|---|---|---|
45 | 46 | Picpus | 62947 | POLYGON ((456790.759 5408686.978, 456841.941 5... | 7.201383 |
60 | 61 | Auteuil | 67967 | POLYGON ((444930.499 5411923.067, 444957.444 5... | 6.380679 |
44 | 45 | Bel-Air | 33976 | POLYGON ((456987.121 5409120.599, 456996.502 5... | 5.967841 |
61 | 62 | Muette | 45214 | POLYGON ((444686.860 5413985.234, 445358.893 5... | 5.475037 |
62 | 63 | Porte-Dauphine | 27423 | POLYGON ((446548.869 5414236.010, 447025.036 5... | 3.085061 |
... | ... | ... | ... | ... | ... |
9 | 10 | Enfants-Rouges | 8562 | POLYGON ((453580.220 5412266.849, 453591.609 5... | 0.271603 |
3 | 4 | Place-Vendôme | 3044 | POLYGON ((451004.908 5412654.095, 450960.640 5... | 0.269311 |
5 | 6 | Vivienne | 2917 | POLYGON ((451686.936 5412747.032, 451682.879 5... | 0.243418 |
11 | 12 | Sainte-Avoie | 7501 | POLYGON ((452928.277 5412227.550, 452830.786 5... | 0.213201 |
4 | 5 | Gaillon | 1345 | POLYGON ((451328.752 5412991.278, 451294.721 5... | 0.187910 |
80 rows × 5 columns
EXERCISE:
增加一列population_density 代表人口密度,即每平方公里的居民数量(注意:面积函数计算得到的面积以平方米为单位,所以需要将结果乘以10**6)
根据population_density字段对多边形进行上色,使用column=参数
使用 legend=True 参数显示色带(color bar)
districts['population_density'] = districts['population'] / districts.geometry.area * 10**6
districts.plot(column='population_density', figsize=(12, 6), legend=True)
districts.plot(column='population', figsize=(12, 6), legend=True)
1.7 关于导入和创建GeoDataFrames的更多信息
GeoPandas使用Fiona库(与GDAL/OGR的python接口)来读写数据。GeoPandas提供了一个更加用户友好的函数接口,这对于大多数用例来说已经足够了。但有时你需要更多的控制,在这种情况下,要用Fiona读取文件,你可以执行以下操作:
import fiona
from shapely.geometry import shape
with fiona.Env():
with fiona.open("zip://data/ne_110m_admin_0_countries.zip") as collection:
for feature in collection:
geom=shape(feature['geometry'])
print(feature['properties']['NAME'])
Fiji
Tanzania
W. Sahara
Canada
United States of America
Kazakhstan
Uzbekistan
Papua New Guinea
Indonesia
Argentina
Chile
Dem. Rep. Congo
Somalia
Kenya
Sudan
Chad
Haiti
Dominican Rep.
Russia
Bahamas
Falkland Is.
Norway
Greenland
Fr. S. Antarctic Lands
Timor-Leste
South Africa
Lesotho
Mexico
Uruguay
Brazil
Bolivia
Peru
Colombia
Panama
Costa Rica
Nicaragua
Honduras
El Salvador
Guatemala
Belize
Venezuela
Guyana
Suriname
France
Ecuador
Puerto Rico
Jamaica
Cuba
Zimbabwe
Botswana
Namibia
Senegal
Mali
Mauritania
Benin
Niger
Nigeria
Cameroon
Togo
Ghana
Côte d'Ivoire
Guinea
Guinea-Bissau
Liberia
Sierra Leone
Burkina Faso
Central African Rep.
Congo
Gabon
Eq. Guinea
Zambia
Malawi
Mozambique
eSwatini
Angola
Burundi
Israel
Lebanon
Madagascar
Palestine
Gambia
Tunisia
Algeria
Jordan
United Arab Emirates
Qatar
Kuwait
Iraq
Oman
Vanuatu
Cambodia
Thailand
Laos
Myanmar
Vietnam
North Korea
South Korea
Mongolia
India
Bangladesh
Bhutan
Nepal
Pakistan
Afghanistan
Tajikistan
Kyrgyzstan
Turkmenistan
Iran
Syria
Armenia
Sweden
Belarus
Ukraine
Poland
Austria
Hungary
Moldova
Romania
Lithuania
Latvia
Estonia
Germany
Bulgaria
Greece
Turkey
Albania
Croatia
Switzerland
Luxembourg
Belgium
Netherlands
Portugal
Spain
Ireland
New Caledonia
Solomon Is.
New Zealand
Australia
Sri Lanka
China
Taiwan
Italy
Denmark
United Kingdom
Iceland
Azerbaijan
Georgia
Philippines
Malaysia
Brunei
Slovenia
Finland
Slovakia
Czechia
Eritrea
Japan
Paraguay
Yemen
Saudi Arabia
Antarctica
N. Cyprus
Cyprus
Morocco
Egypt
Libya
Ethiopia
Djibouti
Somaliland
Uganda
Rwanda
Bosnia and Herz.
Macedonia
Serbia
Montenegro
Kosovo
Trinidad and Tobago
S. Sudan
1.7.1 手动构建一个GeoDataFrame
geopandas.GeoDataFrame({
'geometry':[Point(1,1),Point(2,2)],
'attribute1':[1,2],
'attribute2':[0.1,0.2]
})
geometry | attribute1 | attribute2 | |
---|---|---|---|
0 | POINT (1.00000 1.00000) | 1 | 0.1 |
1 | POINT (2.00000 2.00000) | 2 | 0.2 |
1.7.2 根据已有的dataframe创建一个GeoDataFrame
df = pd.DataFrame(
{'City': ['Buenos Aires', 'Brasilia', 'Santiago', 'Bogota', 'Caracas'],
'Country': ['Argentina', 'Brazil', 'Chile', 'Colombia', 'Venezuela'],
'Latitude': [-34.58, -15.78, -33.45, 4.60, 10.48],
'Longitude': [-58.66, -47.91, -70.66, -74.08, -66.86]})
df
City | Country | Latitude | Longitude | |
---|---|---|---|---|
0 | Buenos Aires | Argentina | -34.58 | -58.66 |
1 | Brasilia | Brazil | -15.78 | -47.91 |
2 | Santiago | Chile | -33.45 | -70.66 |
3 | Bogota | Colombia | 4.60 | -74.08 |
4 | Caracas | Venezuela | 10.48 | -66.86 |
gdf=geopandas.GeoDataFrame(df,geometry=geopandas.points_from_xy(df.Longitude,df.Latitude))
gdf
City | Country | Latitude | Longitude | geometry | |
---|---|---|---|---|---|
0 | Buenos Aires | Argentina | -34.58 | -58.66 | POINT (-58.66000 -34.58000) |
1 | Brasilia | Brazil | -15.78 | -47.91 | POINT (-47.91000 -15.78000) |
2 | Santiago | Chile | -33.45 | -70.66 | POINT (-70.66000 -33.45000) |
3 | Bogota | Colombia | 4.60 | -74.08 | POINT (-74.08000 4.60000) |
4 | Caracas | Venezuela | 10.48 | -66.86 | POINT (-66.86000 10.48000) |
浏览http://geopandas.readthedocs.io/en/latest/gallery/create_geopandas_from_pandas.html 查看更多例子