LINK:[GoogleEarthとR][ShapeFileライブラリ][kmlラボ][空間的なデータの分析][Rでジオコーディング][RでGPS]
目次
論文引用・書籍等に引用する場合の著作権についての連絡>>okinawa
GoogleMapやGoogleEarthのおかげで、最近ジオコーディングという言葉がインターネットで頻繁に使われだしている。
ジオコーディングとは簡単に言えば、住所や郵便番号情報から、緯度・経度の位置情報に変換する技術のことであるが、WebAPIを利用したものや完全にフリーで配布されているもの、有償でかなりお高いものなど多々存在する。
最近になって勢力を拡大してきているのが、WebAPIを利用したジオコーダー(ジオコーディング用のプログラム)で、もっとも有名なのがGoogleMapAPIに含まれているジオコーダーである。
また、国から「街区レベル位置参照情報ダウンロードサービス」が開始されたり、郵便番号データがダウンロードできるようになってからは、これらのデータを組み合わせてジオコーディングを行うものが出てきている。
ここでは、ジオコーディングの情報を収集するとともに、Rを用いたジオコーディングについて検証していくこととする。
基本的には、Rのみでジオコーディングを可能にすることが目標である。
GoogleEarthのジオコーディングはすばらしいが、なにしろ変換スピードが遅いのがネックである。(ちなみにGoogleMapAPIの日本版ジオコーディング部分はゼンリンのものが使われていると言われている。)
(注意)
ジオコーディングを業務用に利用するならば、ジオコーディングする前に「住所情報の正規化」という作業が必要になることが多い。
つまり、業務で入力されている住所がきちんとした住所になっていない(たとえば、**市を端折って市内と書いてあったりする)ので、まずきちんとした住所になるように文字列を整える必要がある。
そういう意味では、文書としての住所を解析して正規化する文書解析という分野の知識も必要になる。
RCurl
XML
rjson
SSOAP
ジオコーディングツール
無償版:
[GoogleMapAPI] http://www.google.com/apis/maps/documentation/
[CSVアドレスマッチングサービス] http://www.tkl.iis.u-tokyo.ac.jp/~sagara/geocode/overview.html
[gコンテンツ流通推進協議会] http://www.g-contents.jp/
[geocoder.ja] http://www.postlbs.org/
[JNS住所認識システム]http://nlftp.mlit.go.jp/isj/jns_agreement.html
有償版:
[jasminesoft住所正規化コンバータ] http://anorm.jp/
[アルプス社] http://www.alpsmap.jp/mapinfo/data/geocoding/
ジオコーディング用データ
[街区レベル位置参照情報ダウンロードサービス] http://nlftp.mlit.go.jp/isj/
[郵便番号ダウンロード] http://www.post.japanpost.jp/zipcode/download.html
文字列検索アルゴリズム
[1]http://fujimiya.net/?Boyer-Moore%20Fast%20String%20Searching%20Algorithm
[2]http://d.hatena.ne.jp/siokoshou/20060325
[3]http://www.dbsj.org/Japanese/DBSJLetters/vol1/no1/ronbun/Sagara.pdf
日本語形態素解析
[1]YahooJapan:http://developer.yahoo.co.jp/jlp/MAService/V1/parse.html
事前に街区レベル位置参照情報ダウンロードサービスから対象となるデータをダウンロードして解凍してください。
下記の例は、沖縄県全域データを利用したものです。
x<- read.csv("c:\\47_2006.csv") y<- paste(x$都道府県名,x$市区町村名,x$大字.町丁目,x$街区符号.地番,sep="") target<- "沖縄県島尻郡八重瀬町後原566" xnum<-pmatch(target,y) z<-x[xnum,8:9] print(z) > print(z) 緯度 経度 95660 26.14762 127.7343
(注)まったく役に立ちませんが、とりあえず住所完全一致でのジオコーディング。一度変数に入れてしまえば、3行目以降のみで可能。さて、住所の正規化をどうするか・・・。
options(digits=10) target<- "浦添市当山二丁目" xnum<-grep(target,y) z<-x[xnum,1:9] mean(z[8:9]) > mean(z[8:9]) 緯度 経度 26.2515489 127.7348568
(注)grepだと結構いい感じであいまい検索してくれる。
# hospdは $Addrに住所, $Nameに名前が入っているデータフレーム。 library(XML) makePlacemark <- function(x) { addr=xmlNode("address", xmlTextNode(hospd$Addr[x])); name=xmlNode("name", xmlTextNode(hospd$Name[x])); return(xmlNode("Placemark",addr,name)); } addrList <- lapply(1:nrow(hospd), makePlacemark) d<-xmlNode("Document") for(i in 1:length(addrList)) { d$children[[i]] <- addrList[[i]] } p <- xmlNode("kml", d) sink(file("hospd.kml",encoding="utf-8",open="w")) #なかまさんのものをくっつけました print(p) sink()
出力されたhospd.kmlをGoogleEarthに読み込むと、GEが自動的にジオコーディングして表示してくれます。(遅いです)想像ですが、GoogkeMapAPIのジオコーディングを使っていると思いますので、ゼンリンのデータを用いた独自解析のものでしょう。
placemark2GeoData <- function(rootNode) { findDoc <- function(x) { if(xmlName(x) == "Document") { return(x) } for(m in xmlChildren(x)) { n <- findDoc(m) if(!is.null(n)) { return(n) } } return(NULL) } ininode <- findDoc(rootNode) namev <- vector() addrv <- vector() longv <- vector() lattv <- vector() for(node in xmlChildren(ininode)) { if(xmlName(node) == "Placemark") { n <- NULL a <- NULL gd <- NULL for(i in xmlChildren(node)) { if(xmlName(i) == "name") { n <- xmlValue(i$children[[1]]) } else if(xmlName(i) == "address") { a <- xmlValue(i$children[[1]]) } else if(xmlName(i) == "Point") { gd <- strsplit(xmlValue(i$children[[1]]$children[[1]]),",")[[1]] } } if(!(is.null(gd) | is.null(a) | is.null(n)) ) { namev <- append(namev, n) addrv <- append(addrv, a) longv <- append(longv, gd[1]) lattv <- append(lattv, gd[2]) } } } return(data.frame(Name=namev, Address=addrv, Longitude=longv, Lattitude=lattv)) }
こんな風に使います
doc <- xmlTreeParse("ih.kml") hospg <- placemark2GeoData(xmlRoot(doc)) # これでLong, Latが入ったデータフレームができる merge(hospg,hospd,by="Name")
##GoogleMapAPIを用いたジオコーディング address<-"沖縄県那覇市久米2丁目4番14号" returnform<-"xml"#xmlorjson key<-"ABQIAAAA7Qa-RE_JYtVliR9OTauOexScAlgT1OMB91Iojh4cvnPDirRWVBRkKoWJQNoMN19LbzuRx7z0aIWHTQ"#ここはGoogleAPIでもらったkeyを登録 text1<-"http://maps.google.com/maps/geo?q=" text2<-"&output=" text3<-"&key=" savefile<-"c:\\geocode.xml" EncodeAddress<-paste(c("",charToRaw(iconv(address,"CP932","UTF-8"))),collapse="%")#なかまさんのURLエンコード URLText<-paste(text1,EncodeAddress,text2,returnform,text3,key,sep="") download.file(URLText,savefile)
で、c:直下にgeocode.xmlで落ちてきます。returnform<-"xml"#xmlorjsonのところで、返り値のフォームを指定してます。(XML形式かJSON形式か)xmlのデコードは次の機会に・・・。
##YahooMapAPIを用いたジオコーディング address<-"沖縄県那覇市久米2丁目4番14号" #returnform<-"xml"#xmlorjson appid<-"YahooDemo" text1<-"http://api.map.yahoo.co.jp/LocalSearchService/V1/LocalSearch?" text2<-"&p=" text3<-"appid=" savefile<-"c:\\geocode.xml" EncodeAddress<-paste(c("",charToRaw(iconv(address,"CP932","UTF-8"))),collapse="%")#なかまさんのURLエンコード URLText<-paste(text1,text3,appid,text2,EncodeAddress,sep="") download.file(URLText,savefile)
で、c:直下にgeocode.xmlで落ちてきます。xml構造はYahooの方がきれいかも。
library(RCurl) # <= バイナリは腐ってる事もあります addr<-"石川県金沢市広坂1丁目1ー1" encaddr<-paste(c("",charToRaw(iconv(addr,"","UTF8"))),collapse="%") url<-paste("http://maps.google.com/maps?q=",encaddr,"&output=kml",sep="") kml<-iconv(getURL(url),"UTF8","") # iconvの""はカレントのエンコーディングになります
といっしょ?
addr<-"石川県金沢市広坂1丁目1ー1" encaddr<-paste(c("",charToRaw(iconv(addr,"","UTF8"))),collapse="%") url<-paste("http://maps.google.com/maps?q=",encaddr,"&output=kml",sep="") con<-url(url,encoding="UTF8") kml<-readLines(con,warn=F)
でもいいか. 保存するなら
con<-file("hoge.kml",open="w",encoding="UTF8") writeLines(kml,con) close(con)
(注:okinawa)RCurlはBioConductorからインストールできます。
# ライセンスはGPL!! ken<-c("富山市新総曲輪1番7号","金沢市鞍月1丁目1番地","福井市大手3丁目17の1") library(XML) library(RCurl) encaddr<-function(x){sapply(x,function(x)paste(c("",charToRaw(iconv(x,"","UTF8"))),collapse="%"))} makeurl<-function(x)sapply(x,function(x)paste("http://maps.google.com/maps?q=",x,"&output=kml",sep="")) kmls<-getURIAsynchronous(makeurl(encaddr(ken))) # encode強制(utf-8の人は何も考えなくて良い) kmls<-sapply(kmls,function(x)iconv(x,"UTF-8")) kmls<-sapply(kmls,function(x)gsub("encoding=\\\"UTF-8\\\"", paste("encoding=\\\"", localeToCharset(), "\\\"", sep=""),x)) #nsが見当たらないのでさくっと消す kmls<-sapply(kmls,function(x)gsub("<kml xmlns=\\\"http:\\/\\/earth.google.com\\/kml\\/2.0\\\">", "<kml>", x)) docs<-sapply(kmls,function(x)xmlTreeParse(x,useInternalNodes=T)) # 内部形式はUTF-8 sapply(docs,function(x)xpathApply(x,"//longitude",xmlValue)) sapply(docs,function(x)xpathApply(x,"//latitude",xmlValue)) sapply(docs,function(x)xpathApply(x,"//coordinates",xmlValue)) sapply(docs,function(x)iconv(xpathApply(x,"//name",xmlValue),"UTF8")) # 日本語はこうして sapply(docs,free) # 開放
library(RCurl) addr<-"沖縄県那覇市久米2丁目4番14号JB-NAHAビル8階" encaddr<-paste(c("",charToRaw(iconv(addr,"","UTF-8"))),collapse="%") url<-paste("http://api.jlp.yahoo.co.jp/MAService/V1/parse?appid=YahooDemo&results=ma,uniq&uniq_filter=9|10&sentence=",encaddr,sep="") kml<-iconv(getURL(url),"UTF-8","") # iconvの""はカレントのエンコーディングになります print(kml)
実行結果(見やすいように整形してあります)
<?xml version=\"1.0\" encoding=\"UTF-8\" ?> <ResultSet xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"urn:yahoo:jp:jlp\" xsi:schemaLocation=\"urn:yahoo:jp:jlp http://api.jlp.yahoo.co.jp/MAService/V1/parseResponse.xsd\"> <ma_result><total_count>13</total_count> <filtered_count>13</filtered_count> <word_list> <word><surface>沖縄県</surface><reading>おきなわけん</reading><pos>名詞</pos></word> <word><surface>那覇市</surface><reading>なはし</reading><pos>名詞</pos></word> <word><surface>久米</surface><reading>くめ</reading><pos>名詞</pos></word> <word><surface>2</surface><reading>2</reading><pos>名詞</pos></word> <word><surface>丁目</surface><reading>ちょうめ</reading><pos>接尾辞</pos></word> <word><surface>4</surface><reading>4</reading><pos>名詞</pos></word> <word><surface>番</surface><reading>ばん</reading><pos>接尾辞</pos></word> <word><surface>14</surface><reading>14</reading><pos>名詞</pos></word> <word><surface>号</surface><reading>ごう</reading><pos>接尾辞</pos></word> <word><surface>JB-NAHA</surface><reading>JB-NAHA</reading><pos>名詞</pos></word> <word><surface>ビル</surface><reading>びる</reading><pos>名詞</pos></word> <word><surface>8</surface><reading>8</reading><pos>名詞</pos></word> <word><surface>階</surface><reading>かい</reading><pos>接尾辞</pos></word> </word_list> </ma_result><uniq_result><total_count>13</total_count>
住所もきちんと分解してくれてるようですね。更に、指定した名詞・動詞をフィルタリングして抽出してきてます。
<filtered_count>9</filtered_count> <word_list> <word><count>1</count><surface>JB-NAHA</surface><reading/><pos>名詞</pos></word> <word><count>1</count><surface>ビル</surface><reading/><pos>名詞</pos></word> <word><count>1</count><surface>久米</surface><reading/><pos>名詞</pos></word> <word><count>1</count><surface>沖縄県</surface><reading/><pos>名詞</pos></word> <word><count>1</count><surface>那覇市</surface><reading/><pos>名詞</pos></word> <word><count>1</count><surface>14</surface><reading/><pos>名詞</pos></word> <word><count>1</count><surface>2</surface><reading/><pos>名詞</pos></word> <word><count>1</count><surface>4</surface><reading/><pos>名詞</pos></word> <word><count>1</count><surface>8</surface><reading/><pos>名詞</pos></word> </word_list></uniq_result></ResultSet>
並びはどうやって決めてるんだろう?
##GoogleMapAPIを用いた逆ジオコーディング URLText<-"http://maps.google.com/maps/geo?oe=utf-8&ll=35.620519%2C139.438892&key=ABQIAAAA7Qa-RE_JYtVliR9OTauOexScAlgT1OMB91Iojh4cvnPDirRWVBRkKoWJQNoMN19LbzuRx7z0aIWHTQ&output=json&callback=gmap" savefile<-"c:\\geocode.xml" download.file(URLText,savefile)
#10Mbps(GoogleMapAPI) #10 0.70 0.50 0.58 0.40 0.44 #20 0.55 0.56 0.51 0.56 0.53 #30 0.69 0.66 0.68 0.61 0.61 #40 0.76 0.73 1.08 0.83 0.97 #50 0.86 0.83 0.87 0.89 1.00 #60 1.26 1.21 1.11 1.11 1.26 #70 1.19 1.36 1.52 1.48 1.41 #80 1.47 1.56 4.05 1.66 1.61 #90 4.08 4.25 4.24 4.22 4.31 #100 4.44 4.42 4.54 4.49 4.36 #150 4.83 5.00 4.78 4.80 5.64 #200 7.03 5.69 5.42 5.41 5.43 #300 11.45 13.64 23.22 17.22 11.68
[1] 0.597 0.103 4.385 0.000 0.000 #光100M 100件 [1] 0.949 0.222 3.303 0.000 0.000 #光100M 200件 [1] 1.400 0.193 3.712 0.000 0.000 #光100M 300件 [1] 1.833 0.229 3.030 0.000 0.000 #光100M 400件 [1] 2.211 0.182 2.791 0.000 0.000 #光100M 500件
ken<-c("富山市新総曲輪1番7号","金沢市鞍月1丁目1番地","福井市大手3丁目17の1") library(RCurl) encaddr<- function(x){sapply(x,function(x)paste(c("",charToRaw(iconv(x,"","UTF8"))),collapse="%"))} makeurl<- function(x)sapply(x,function(x)paste("http://maps.google.com/maps?q=",x,"&output=kml",sep="")) kmls<-getURIAsynchronous(makeurl(encaddr(ken)))
> test<-function(){library(RCurl) + addr<-"沖縄県那覇市久米2丁目4番14号" + encaddr<-paste(c("",charToRaw(iconv(addr,"","UTF8"))),collapse="%") + url<-paste("http://maps.google.com/maps?q=",encaddr,"&output=kml",sep="") + kml<-iconv(getURL(url),"UTF8","") # iconvの""はカレントのエンコーディングになります + print(kml) + } > system.time(test()) [1] "<?xml version=\"1.0\" encoding=\"UTF-8\"?><kml xmlns=\"http:(中略) [1] 0.008 0.002 0.292 0.000 0.000
34436 人