Geopandas可以讀取各GIS格式進行空間運算,然而實際上我們可能拿到的資料不見得是GIS的格式
雖然不是GIS格式,但它有可能是有坐標資訊的資料
這樣的資料可能會是一個csv或Excel,資料中含有經緯度或xy
對於這樣的資料,我們可以把它先轉為Geopandas
的GeoDataFrame
以方便空間的運算
大綱
為了今天的範例,請先下載[新北市路燈資料]的csv檔,解壓縮後將之命名為Light.csv
拿到資料的第一步先使用Pandas讀取這個csv為DataFrame
import pandas as pd
df=pd.read_csv('data/Light/Light.csv',encoding='utf-8')
ps.如果沒有pandas請先安裝
conda install pandas
首先,瀏覽一下這份資料
可以看到這份資料具有TWD97X, TWD97Y坐標欄位,
很明顯的,我們可以把它轉為點資料
接下來,我們試著把X,Y轉換成GeoDataFrame
前一天提到GeoDataFrame包含geometry的屬性,
因此,要產生GeoDataFrame,需要產生geometry
這時候需要用shapely套件中的geometry
我們使用之中的Point類型,並把TWD97X, TWD97Y包進去,命名為geom
from shapely.geometry import Point
import geopandas as gpd
geom = [Point(xy) for xy in zip(df.TWD97X, df.TWD97Y)]
有了geom
,我們可以用來產生GeoDataFrame
,
其中,需要指定crs,也就是坐標系統
由於TWD97X, TWD97Y的坐標是TWD97,為epsg:3826
crs = {'init': 'epsg:3826'}
gdf = gpd.GeoDataFrame(df, crs=crs, geometry=geom)
gdf
就這樣,透過幾個步驟,我們成功把csv轉成了GeoDataFrame,
然後我們就可以做GeoDataFrame能做的事
例如我們可以把它會在圖上:
將永和區的資料畫圖來,並以address
為color code繪圖,
gdf[gdf['district']=='永和區'].plot(column='address')
像是今天的資料集[新北市路燈資料]是一份具有XY的點資料,
如果今天資料中沒有XY,我們還是可以透過一些Geocoding方法取得坐標
在去年的文章[Day 9] 自己產製資料-地址定位part2中,
提到可以使用TGOS來進行門牌地址的Geocoding。
然而TGOS一般的服務必須先將資料匯出,今天為了一次性的完成資料加工,
Geocoding會以TomTom Developer Portal Geocode提供的服務為例(個人經驗,這個服務的Geocoding會有較好的成果)
首先,必須要取得一個api key,每個key每天都有一些免費的request數量,
取得api key請自行前往該服務申請User account | TomTom Developer Portal
假設已經順利取得api的key,我們就以新北市各圖書館地址電話表為範例來做Geocoding,
一樣,下載csv格式,
先以Pandas讀取csv
import pandas as pd
df=pd.read_csv('data/Library/Library.csv',encoding='utf-8')
df
接著我們寫一個函式取名為rest
處理Geocoding,
api呼叫的方法及回應的格式直接參考TomTom Developer Portal Geocode的說明
以下程式碼範例中,YOUR_KEY
必須填入你的api key!
import requests,json
def rest(address):
url = 'https://api.tomtom.com/search/2/geocode/{}.json?&key=YOUR_KEY&countrySet=TWN&language=zh-TW&limit=1'.format(
address)
response = requests.get(url)
data = response.text
js = json.loads(str(data))
return js['results'][0]['position']
ps. requests需要安裝,
conda install requests
然後,我們使用rest函式,透過其傳回來的x,y產生點資料的geometry(命名為geom)
# 這邊使用df.head()測試避免一天request太多
geom=[Point(rest(row.address)) for idx, row in df.head().iterrows()]
有了geom就可以產生GeoDataFrame了,
要注意的是,這邊的坐標是WGS84,也就是epsg:4326
crs = {'init': 'epsg:4326'}
gdf = gpd.GeoDataFrame(df.head(), crs=crs, geometry=geom)
gdf
看看成果吧
GeoDataFrame也可以輸出成GIS格式,匯出在其他平台檢視與處理,包含常用的shp或是geojson
gdf.to_file(driver = 'ESRI Shapefile', filename = 'output/Light.shp')
今天的相關測試可以參考GitHub
本文同步發表於個人部落格
大大您好,我在跟著大大文章練習的時候
在
import requests,json
def rest(address):
url = 'https://api.tomtom.com/search/2/geocode/{}.json?&key=YOUR_KEY&countrySet=TWN&language=zh-TW&limit=1'.format(
address)
response = requests.get(url)
data = response.text
js = json.loads(str(data))
return js['results'][0]['position']
# 這邊使用df.head()測試避免一天request太多
geom=[Point(rest(row.address)) for idx, row in df.head().iterrows()]
這段出現了error:0
於是去查看了一下,發現是Point出錯
因為rest回傳的資料好像不符合Point所需要的
所以我將程式碼改成這樣
import requests,json
def rest(address):
url = 'https://api.tomtom.com/search/2/geocode/{}.json?&key=YOUR_KEY&countrySet=TWN&language=zh-TW&limit=1'.format(
address)
response = requests.get(url)
data = response.text
js = json.loads(str(data))
# 改成以下這段
dic_lat = js['results'][0]['position']['lat']
dic_lon = js['results'][0]['position']['lon']
dic_tuple = (dic_lon,dic_lat)
return dic_tuple
後面就可以了
可能是小弟本身哪裡有做錯,
也有可能是資料、版本有更新,
所導致這個問題出來
小弟目前是新手,哪裡有做不好的,還請大大多多指教