物件探しも自動化! ArcGIS API for Python で近隣検索ツールの開発

542
0
07-14-2021 11:31 PM
Labels (1)

物件探しも自動化! ArcGIS API for Python で近隣検索ツールの開発

はじめに

 米国Esri社では、ArcGIS API for Python ArcGIS Developer の一つとして提供しています。これは、Web GIS での空間データの管理・操作に用いられることが多い API で、主に GIS と連動した空間データの処理の自動化に用いられるなどしています。また、 Python という言語の豊富なライブラリを用いてそれらのデータのビジュアライズや分析を行なうことに長けているのが特長です。

 今回は、ArcGIS API for Python を使って、Jupyter Notebook にて、ある地点の最も近くにある道路の幅員と 500m 圏内にある周辺の建物情報を取得する近隣検索を自動化したツールを作成しました。今回使用しているデータや各モジュールの使用状況、構成については、以下に示している図を参考にしていただければと思います。

pic1.png

利用イメージ

 ここで作成しているサンプルノートブックは、物件探しや土地の管理をする方をサポートすることを想定しています。物件を探している方は、物件の室内情報もさることながら周辺環境も注目されると思います。例えば、病院に通うことが多い方にとって、病院との距離について気になるところです。近くに通える病院があるかということは物件を決める意思決定に大きく影響を与えると考えられます。また、車体の大きい車を保有している方や引っ越しの際にトラックが停車できるスペースがあるかなど確認する際には、道路の幅員は必要な情報となり得ます。

 土地の管理をする方に関しても CSV からマップに物件の位置を描画すること、それらの近隣検索を行うことができるため、その土地が持つ強みについて可視化することができます。また、それらのデータを CSV で保存することができるため、常時このツールを起動しなくてもローカルもしくは別のアプリ等で管理することができるようになります。

pic2.png

 このように近隣検索を行うことで、物件や土地に関する調査ができます。このサンプルノートブックでは、ArcGIS API for Python の強みである空間データの管理・処理の自動化を用いて、土地・物件の調査に関する困りごとをサポートすることができると考えられます。

サンプルノートブックの概要

 マップ上の目的の場所から最近接の道路の幅員と 500m 圏内の建物情報を検索し、検索した結果を CSV に出力することができます。また、検索結果は、マップ上に描画され、描画されたアイコンをクリックするとポップアップで道路の幅員と建物の名前が表示されます。

 マップ上での目的の場所の指定ついては、① マップ上をマウスなどでクリックした地点、② 住所の情報が記載された CSV の住所情報をジオコーディングした結果を使用する2つの方法があります。以下ではそれらの結果について図表で示しています。

①マップ上でクリックした場合(gif)

output_blog.gifクリックした地点と近隣検索の結果を CSV へ出力した結果(病院(hospital)の件数を出力)クリックした地点と近隣検索の結果を CSV へ出力した結果(病院(hospital)の件数を出力)

②CSV からジオコーディングした結果

ジオコーディングを行う「住所」カラムを含んだ CSV の例ジオコーディングを行う「住所」カラムを含んだ CSV の例ジオコーディングを行った地点をマップへ描画した結果ジオコーディングを行った地点をマップへ描画した結果近隣検索の結果を CSV に出力した結果(公園(park)の件数を出力)近隣検索の結果を CSV に出力した結果(公園(park)の件数を出力)

サンプルノートブックで使用したモジュールとデータの説明

 このサンプルノートブックでは、複数の arcgis モジュールを使って近隣検索を実現しています。ここでは、arcgis モジュールをどのように使用し、実現しているかを次の4つの処理に分けて紹介します。1つ目の処理では、複数の地点の近隣検索を行うために住所情報を含んでいる CSV からジオコーディングを行い、2つ目の処理では、最近接の道路の幅員を取得します。3つ目の処理は、特定の建物情報の検索を行う方法を示し、4つ目の処理で、バッファー内に含まれている道路の幅員や建物情報を取得することで任意の範囲での近隣検索を実現しています。

 なお、道路データについては、ESRIジャパンで販売している ArcGIS Geo Suite 道路網データを使用しています。また、以下のサンプルでマップ上への描画に使用している gis モジュールの変数設定は以下の通りとなっています。

 

import arcgis # arcgis api for python を使うため
from arcgis.gis import GIS # gis モジュールの使用

gis = GIS()
maps = gis.map("東京都千代田区麹町2丁目4−20" ,16)
maps.basemap='streets-relief-vector' # basemap をベクター タイルに変更

 

 

1.住所情報を含む CSV からジオコーディングを行う

 住所の情報が含まれる CSV から近隣検索を行います。これには、Python ライブラリの一つである pandas read_csv() geocoding モジュールの geocode() の二つのメソッドを使用しています。ここで使用する geocode() は、住所から緯度経度に変換するジオコーディングの役割を果たしています。

 

import pandas as pd
from arcgis.geocoding import geocode

df_csv = pd.read_csv('<CSV_File>',encoding="<任意の文字コード>")

for i,address in enumerate(df_csv['住所']):
    # csv の内容を一つずつジオコーディング
    csv_geocode=geocode(address, out_sr=3857)[0] 

 

 

2.最近接の道路の幅員を取得する

最近接の道路を取得するために geometry モジュールの distance() メソッドを使用しています。このメソッドは、計算する際に Python ライブラリの一つである NumPy を使用しているため、import します。これらを用いて、道路データの一つ一つのポリラインと任意の地点との距離を測り、最近接の道路の情報を返しています。この時、浮動小数点以下の値を比較するために Python decimal モジュールを使用しています。

 

import numpy as np # numpy によって計算
from decimal import Decimal # 浮動小数点以下の比較を行うために使用
from arcgis.geometry import distance 

road=<道路データ>
point=<任意のポイント>

for i in range(len(road)):

dist2road = distance(spatial_ref=3857, 
                         geometry1=road[i], 
                         geometry2=point, 
                         distance_unit='9001',  
                         geodesic=True,
                         gis=gis
                     )

if i==0 : 
roadId=keyid 
dis=Decimal(dist2road["distance"]) 

if 0<i and Decimal(radius["distance"])<dis: 
roadId=keyed
	     dis=Decimal(dist2road["distance"])

 

 

3.特定の建物を検索する

geocode() メソッドには、ジオコーディング以外にも POI(Point Of Interest) を指定することで特定の地物(目標物)を検索することができます。同時に location パラメーターに位置情報(住所や座標)を指定するとその地点から近隣検索を行います。以下の例では、任意の地点(point)location パラメーターに付与し、病院を目標物として検索しています。

 

from arcgis.geocoding import geocode

point=”<任意の地点>”

excode = geocode(“hospital”, location=point, out_sr=3857)

 

 

4.バッファー内に含まれる情報のみを取得

 2.と 3. でご紹介した機能ですが、2. をそのまま実行するとすべての道路データから最も近い距離のものを取得することになるため、道路データの量が多いとかなり時間がかかってしまいます。3. geocode() も検索の範囲を指定していないため、どこまでの範囲で検索をかけているかがわかりません。geocode() メソッドでの範囲指定検索の方法として、search_extent パラメーターがありますが、矩形範囲での指定かつ距離を設定することができません。

 これらを解決するためには、任意の地点を中心に任意の距離圏内にある建物や道路情報を抽出する必要があります。そのため、任意の距離で円を描くことができる geometry モジュールの buffer() メソッドを使用し、それぞれ検索する範囲を指定します。そして、抽出には intersect() メソッドを使用し、バッファーに重なっている建物や道路を抽出するようにしています。なお、intersect() メソッドで扱われるデータは、ジオメトリ タイプ(Point, Polygon, Polyline) で指定する必要があるので、それぞれにあうジオメトリ タイプに変換してから実行しています。以下では、それぞれのコードに付与した例を示します。

 

道路の幅員の検索に追加した例

 

import numpy as np 
from arcgis.geometry import distance , buffer, Polygon 

road_data=<道路データ>
point=<任意のポイント>

point_buffer=buffer ( geometries =[point], 
                     in_sr=3857, 
                     distances=50, 
                     unit="9001", #9001:m 9036:km 
                     out_sr=3857, 
                     buffer_sr=3857, 
                     geodesic=True,  
                     gis=gis)
point_buffer= Polygon(point_buffer[0])
in_buff={}
for i,road in enumerate(road_data["SHAPE"]) : # SHAPE 属性から取得
	if point_buffer.intersect(second_geometry=road,dimension=2):
          in_buff[i]=point_buffer.intersect(second_geometry=road,dimension=2)
       
for i,keyid in enumerate(in_buff):

	dist2road = distance(spatial_ref=3857, 
                         geometry1=in_buff[keyid], 
                         geometry2=point, 
                         distance_unit='9001',  
                         geodesic=True, 
                         gis=gis
                     )
	if i==0 : 
		roadid=keyid 
		dist=Decimal(dist2road["distance"]) 
	if 0<i and Decimal(dist2road["distance"])<dis: 
		roadid=keyid
		dist=Decimal(dist2road["distance"])

 

 

実行から結果が出るまでの時間を iPython time マジックコマンドを使用して比較してみます。それぞれ、距離が一行目、実行時間が2行目に来ています。

 

全件検索する場合全件検索する場合バッファーの範囲内で検索をかけた場合バッファーの範囲内で検索をかけた場合

 

建物の検索に追加した例

 

from arcgis.geocoding import geocode
from arcgis.geometry import distance , buffer, Polygon, Point

point=”<任意の地点>”

poi_search = geocode(“park”, location=point, out_sr=3857)

point_buffer=buffer(	geometries = [point],
		      	in_sr=3857, 
			distances=500,
			unit="9001", #9001:m 9036:km
			out_sr=3857,
			buffer_sr=3857,
			geodesic=True, 
			gis=gis)
point_buffer=Polygon(point_buffer[0])

for address in poi_search:
	to_point=Point({'spatialReference' :{'latestWkid': 3857, 'wkid': 102100},
			'x': address['location']['x'] ,
			'y':  address['location']['y']})
 if point_buffer.intersect(second_geometry=to_point,dimension=1).x!="NaN":
	print(address['attributes']['Match_addr'])
	maps.draw(to_point, 
		popup= {'title':address['attributes']['Match_addr'], 
			'content': "建物種別:"+ address['attributes']['Type'] + 				"<br>どこから500m圏内:" + place_name},
		symbol={"angle":0,
			"xoffset":0,
			"yoffset":0,
			"type":"esriPMS",
			"url":"http://static.arcgis.com/images/Symbols					/Cartographic/esriCartographyMarker_70_Yellow. png",
			"contentType":"image/png",
			"width":14,
			"height":14}
		)

 

 実行結果を比較すると以下のようになります。地図中の黒い円が 500m 範囲で黄色のピンが公園となります。

全件の建物(公園)をそのまま検索した場合全件の建物(公園)をそのまま検索した場合500m 圏内にある建物(公園)を検索した場合500m 圏内にある建物(公園)を検索した場合

まとめ

 このサンプルノートブックを使うと道路の幅員と指定した建物の近隣検索を行うことができます。特にこれまでに紹介した4つの処理を用いることによって、これらの近隣検索を実現することができています。

1つ目の処理では、住所を緯度経度にするジオコーディングを行うことで緯度経度の情報がなくても、住所の情報を使っての近隣検索を可能にしています。

2つ目の処理では、任意の地点に最も近い道路の幅員を取得することで、道路の幅員を確認することができています。

3つ目の処理では、任意の地点を中心に建物を指定した検索ができることを確認しています。

4つ目の処理では、任意の地点を中心に円を作り、その円の中に含まれている近隣情報の抽出を行っています。これは、2つ目と3つ目の処理をより使いやすくする役目を果たしています。

 このサンプルノートブックの全容と詳細については、GitHub  にて公開していますので良ければご参照いただければと思います。

最後に

今回は、ArcGIS API for Python を使用して、近隣検索を行えるサンプルノートブックを作成してみました。今回使用したモジュール以外にも Python の強みを活かしたツールが多くありますので、ぜひお試しいただければ幸いです。その際には、 使用方法などを詳しく書いたブログ記事もありますので、ぜひご参考にしてください。

 

参考

ArcGIS Developers(ESRIジャパン製品ページ)

ArcGIS Online(ESRIジャパン製品ページ)

ArcGIS Developers 開発リソース集

ArcGIS API for Python

ArcGIS Geo Suite 道路網データ

シリーズブログ

ArcGIS API for Python を使ってみよう シリーズブログ

さわって覚える ArcGIS API for Python シリーズブログ

開発リソース集

ArcGIS for API for Pythonのコンセプト

インストールガイド

ArcGIS for API for Pythonのための基礎環境:conda入門

 

 

 

Labels (1)
Version history
Revision #:
2 of 2
Last update:
3 weeks ago
Updated by: