R語言解析json數據地圖素材
最近幾天推送頻率之所以下降了,不是因為偷懶,是在攻克一個難題~
還記得前一篇推送,關於山東省財政數據可視化那一篇,因為沒有精準、最新的山東省縣級市邊界地圖素材數據,花了好多冤枉功夫,搜地圖素材各種碰壁,最後的得到的地圖數據並不盡如人意。
現在shp的素材相比json整體都不太流行了,無論是製作成本上還是佔用內存上以及與實際行政區劃的更新速度上,json地圖素材輕便、時效、易獲取,很多網站都提供這種輕量級的數據文件。
可是json文件遵循的JS語法,導入R中之後,全部被強制轉化為各種嵌套的list、data.frame、array等混合體,如果沒有對R數據結構很好的把握,基本看上一眼就絕望了。
記事本打開的json數據
R中打開的json數據
網頁渲染後的json數據代碼
雖然難以理解,但是又不得不用,所以再難也得拿下~
這裡先說明一下,Json數據格式分為兩類,一類是geojson,內部的數據類型顯示FeatureCollection,這種類型數據文件裡面直接存儲的是解碼後的經緯度數據,另一類是topojson,這種類型是需要通過坐標轉換後才能使用,因為每一個點不是真實經緯度,所以下載的時候一定要看清楚。
這裡提供給大家三個網址:
http://geojson.io/#map=7/32.064/117.268
以上網址自選、也可以通過導入shp數據轉換格式(其中就可以將topojson轉化為geojson)。
http://mapshaper.org/
http://datav.aliyun.com/static/tools/atlas/#&lat=36.416862115300304&lng=117.5701904296875&zoom=7
以下過程我用兩個示例展示提取json地圖數據的過程:
library("jsonlite")library("ggplot2")library(plyr)library(dplyr)setwd("D:/R/mapdata/City/")
提取濟南市json地圖數據:
json_data <- fromJSON("370100.json")city<-json_data$features$propertiesnames(city)[2]<-"code"city$id<-1:nrow(city)city$sale<-round(rnorm(nrow(city),100,20),0)這裡提取了濟南市各區的名稱、代碼,並生成了虛擬指標
濟南市各區邊界點坐標:
citydata<-json_data$features$geometry$coordinatesmapdata<-data.frame()for( i in 1:length(citydata)){citymapdata<-citydata[[i]]dim(citymapdata)=c(length(citymapdata)/2,2)citymapdata<-data.frame(citymapdata);names(citymapdata)[1:2]<-c("lon","lat")citymapdata$id<-icitymapdata$group<-as.numeric(paste0(i,".",1))citymapdata$order<-1:dim(citymapdata)[1]mapdata<-rbind(mapdata,citymapdata)}以上過程通過循環函數提取了濟南市各區的邊界點經緯度坐標,並生成了分組依據group、指定了單個區邊界點順序,生成id變數便於和各區合併
mymapdata<-merge(mapdata,city)合併邊界點數據和各區名稱與分組依據(主要是ggplot映射時作為分組變數使用)
因為各區的行政中心經緯度未知,這裡暫時提取多邊形中心作為其參考值
midpos <- function(x) mean(range(x,na.rm=TRUE))centres <- ddply(dongsansheng_map_data,.(city),colwise(midpos,.(long,lat)))
以上過程展示了如何從json格式的數據文件中提取我們製作數據地圖所需要的指標(核心指標由三個:lon、lat、group),但是以上只夠我們畫出一幅單色地圖,因為沒有指定任何指標,在素材提取過程中,之所以先提各區的代碼和id,目的是之後與邊界經緯度信息合併,這樣,所有指標都可以通過合併進入整體的邊界點經緯度信息數據文件中,指標(無論是連續還是分類型)可以作為映射規則(大小、顏色、形狀)。
ggplot(dongsansheng_map_data,aes(long,lat)) + geom_polygon(aes(group=group,fill=zhibiao),colour="grey95") + scale_fill_gradient(low="white",high="steelblue") + geom_text(aes(label=city),data=centres) + theme( panel.grid = element_blank(), panel.background = element_blank(), axis.text = element_blank(), axis.ticks = element_blank(), axis.title = element_blank() )
因為只是講解數據提取過程, 這裡就不展示最終的圖形了。
但是針對省級邊界的json數據文件,相對就要複雜得多,因為很多省份內的城市轄區可能地域上是分割開的(比如河北的廊坊、安徽的銅陵等),但是R語言通過多邊形映射的時候,是將分離的多邊形分別定義(依據就是上面的group變數),然後通過將具有相同行政隸屬關係的多邊形指定一個相同的ID(我們的所有指標型數據都是跟id掛鉤的,與group無關,只有在該地區行政轄區內各子行政單位沒有出現地域分割的情況,此時基於行政單位編號的id和基於多邊形編號的group才會一一對應,否則不會出現嚴格對應關係)。
setwd("D:/R/mapdata/Province/")anhui_data <- fromJSON("anhui.json")
接下來以安徽省的json數據結構為例來說明:
我們可以看到經緯度數據都存在名稱為properties的子list裡面,首先提取出來安徽市級行政單位的屬性信息(代碼、名稱)。
anhui_city_data1<-anhui_data$features$properties[,1:2]anhui_city_data2<-anhui_data$features$properties$centeranhui_city<-cbind(anhui_city_data1,anhui_city_data2)names(anhui_city)[2]<-"code"anhui_city$id<-1:nrow(anhui_city)anhui_city$sale<-round(rnorm(nrow(anhui_city),100,20),0)
接下來問題來了,安徽省的各市級單位經緯度信息數據看起來在list不是同級的,即有些城市是單獨一個list,有些城市是一個list裡面嵌套好幾個子list(這就解釋了上面所講過的,有些城市轄區不接壤,需要分別對其進行多邊形描述和定義)。
這裡寫了個自定義函數,具體示意呢,不太好講,全憑感覺寫的,這個還真的看具體情況來分析,如果作為模板使用,換一個省份可能不一定還能用,但是可以作為參考,修修改改也能省不少事兒!
anhui_map_data<-anhui_data $features$geometry$coordinatesmapdata1<-data.frame()mapdata2<-data.frame()for( i in 1:length(anhui_map_data)){citymapdata<-anhui_map_data[[i]] if (length(citymapdata)<50){ for(m in 1:length(citymapdata)){ citymapdata1<-data.frame(citymapdata[[m]]);names(citymapdata1)<-c("lon","lat") citymapdata1$id<-i citymapdata1$group<-as.numeric(paste0(i,".",m,1)) citymapdata1$order<-1:dim(citymapdata1)[1] mapdata1<-rbind(mapdata1,citymapdata1) } }else{dim(citymapdata)=c(length(citymapdata)/2,2)citymapdata2<-data.frame(citymapdata);names(citymapdata2)<-c("lon","lat")citymapdata2$id<-icitymapdata2$group<-as.numeric(paste0(i,".",1))citymapdata2$order<-1:dim(citymapdata2)[1]mapdata2<-rbind(mapdata2,citymapdata2) }mydatanew<-rbind(mapdata1,mapdata2)}
mydatanew<-arrange(mydatanew,id,order)mydatanew_map_data<-merge(mydatanew,anhui_city[,c(-3,-4)],by="id")ggplot(mydatanew_map_data,aes(lon,lat,group=group,fill=sale))+geom_polygon(col="white")+ theme( panel.grid = element_blank(), panel.background = element_blank(), axis.text = element_blank(), axis.ticks = element_blank(), axis.title = element_blank() )
啊噢,完美的搞定json數據,你肯定看不出來這根使用shp導入的地圖數據做出來的圖有啥區別,因為根本就沒有任何區別(排除兩者在經緯度演算法上的差異),因為我們並沒有使用shp或者json中聲明的任何關於地圖素材的格式屬性,我們只是提取了有用的經緯度變數信息。
下一篇,跟大家細講關於ggplot在製作數據地圖過程中的變數映射規則和注意事項。
聯繫方式:
wechat:ljty1991
Mail:578708965@qq.com 個人公眾號:數據小魔方(datamofang) 團隊公眾號:EasyCharts qq交流群:[魔方學院]553270834推薦閱讀: