Python スクリプトを作成して、Web ツールを複数の方法で実行および利用できます。 スクリプトを実行する主な手段としては ArcPy を使用します。ArcPy には、サービスからの結果に接続し、実行して処理するメソッドが組み込まれています。 代わりに、ArcGIS REST API からサービスにアクセスすると、組み込みの Python モジュールを使って、JSON 構造を使用した REST 呼び出しを行い、結果を転送することができます。 この方法を利用するには、Python コードによってクライアントを最初から構築する必要があります。 スクリプトの大部分は、ArcPy を介してジオプロセシング サービスに接続し使用します。
注意:
以下に示す例では、フェデレーション ArcGIS Server のジオプロセシング サービスを使用しています。 ポータルからは対応する Web ツール アイテムを使用しません。
ArcPy の使用
Web ツールには、ArcGIS Pro の [Python] ウィンドウ、スクリプト ツール、またはスタンドアロン スクリプトを使用してアクセスできます。 Web ツールに接続して使用するには URL が使用されます。
ImportToolbox または AddToolbox を使用してサービスに接続します。
# arcpy.ImportToolbox("https://machine.domain.com/webadaptor/services;<optional folder>/<service name>;{username};{password}")
arcpy.ImportToolbox("https://machine.domain.com/webadaptor/services;GPFolder/BufferService;serverusername;serverpassword")
ツールのインポートに使用されるパスは Web ツールの URL です。 ImportToolbox and AddToolbox にはサーバーの情報を指定し、これはセミコロン (;) によって区切られた 4 つの部分から成ります。 1 つ目の部分はサービス エンド ポイントへの URL (リンク)、2 つ目の部分はサービス名 (オプションで、サービス名の前にフォルダー名が付加される)、3 つ目の部分はサーバー ユーザー名 (オプション)、最後の部分はサーバー パスワード (オプション) です。 Web ツールにアクセスする権限がない場合、ユーザー名とパスワードを入力します。
Web ツールは同期または非同期のどちらでも実行できます。 Python スクリプトを使用するには、サービスの実行方法を理解している必要があります。 IsSynchronous プロパティを使用して、サービスを同期または非同期のどちらで実行するかを指定できます。 サービスが同期して実行されている場合、結果は自動的に返されますが、サービスが完了するまで他のアクションを行うことはできません。 サービスが非同期で実行されている場合、現在のステータスを定期的に照会する必要があります。 サービスの実行が完了すると、結果へのアクセスが可能になります。
次の Python コードは、非同期ジオプロセシング サービスに接続し、ArcPy 関数を使用してサービスを実行し、結果を取得してその結果をさらに処理する方法を示しています。 タスクの実行時に result 変数を設定することで、while ループをステータスの確認に使用できます。 タスクは、ステータス コード 4 (succeeded) 以上が返されると終了します。
非同期サービスを使用してバッファーを作成し、結果をローカルに保存します。
import arcpy
import time
arcpy.ImportToolbox("http://machine.domain.com/webadaptor/services;Elevation/viewshedAlias;serverusername;serverpassword")
result = arcpy.Viewshed_viewshedAlias(r'c:\data\inputPoint.shp', "10000 Kilometers")
while result.status < 4:
print(result.status)
time.sleep(0.2)
print("Execution Finished")
print(result.getMessages())
arcpy.CopyFeatures_management(result[0], 'localResult.shp')
Result オブジェクトとステータス コード、およびラスターとマップ イメージ出力の作成に関する詳細については、「Python でのツールの使用」ヘルプ トピックをご参照ください。
REST の使用
Web ツールを使用するもう 1 つの方法 (ただし、使用頻度は少ない) は、データ交換形式として JSON を使用して REST 呼び出しを行うスクリプトの作成です。 この方法では、ユーザーが、リクエストの送信と応答の処理の両方についてコードを記述する必要があります。
REST メッセージの送受信がより深く関与することになり、ユーザーは入出力構文のすべての内容を自分自身で処理しなければなりません。 REST メッセージを送受信するメリットは、それらのメッセージが一貫した方法で返される点にあります。 リクエストは、HTTP GET または HTTP POST メソッドを介して送信でき、応答は、JSON として構造化して返すことができます。 コア Python ライブラリは、リクエストの送信と関数の送信を両方ともサポートしているため、JSON メッセージの読み取りと解析が容易になっています。
次の例では、サービスに接続し、リクエストを送信し、応答を処理する方法を示します。
リクエストの送信
たとえば、可視領域を作成するジオプロセシング サービスには https://machine.domain.com/webadaptor/rest/services/Elevation からアクセスできます。 このサービスはポイントと距離を入力として受け取り、フィーチャ セットを返します。 サービスをさらに理解するために、次の入力を使用できます。
パラメーター名 | 入力値 |
---|---|
入力観測ポイント (GPFeatureRecordSetLayer) | {"geometryType" : "esriGeometryPoint", "spatialReference" : {"wkid" : 54003}, 'features':[{'geometry':{'x': -13308192.1956127, 'y': 4221903.58555983}}]} |
可視領域距離 (GPLinearUnit) | { 'distance' : 8.5, 'units' : 'esriMiles' } |
フォーマット (出力のフォーマット) | JSON |
このリクエストでは、サービスから見通せる場所の JSON が返されます。 リクエストの生成に使用される完全 URL は、Web ブラウザーのアドレス バーで使用できます。
https://machine.domain.com/webadaptor/rest/services/Elevation/ESRI_Elevation_World/GPServer/Viewshed/execute?Input_Observation_Point={%22features%22%3A[{%22geometry%22%3A{%22x%22%3A-13308192.1956127%2C%22y%22%3A4221903.58555983}}]}&Viewshed_Distance={+%27distance%27+%3A+8.5%2C+%27units%27+%3A+%27esriMiles%27+}&env%3AoutSR=&env%3AprocessSR=&f=pjson
この URL は、Python モジュール urllib または urllib2 を使用して送信できます。 次の Python コードは、Service Directory を使用するか、リンクを Web ブラウザーのアドレス バーにコピーした場合と同様な方法で上記のリクエストを実行します。
# Requires Python 3+
import urllib.request as urlopen
import urllib.parse as urlencode
import urllib.request as request
import json
inPts = {"geometryType" : "esriGeometryPoint",
"spatialReference" : {"wkid" : 54003},
'features':[{'geometry': {'x': -13308192.1956127, 'y': 4221903.58555983}}]}
dist = {'distance':8.5,'units':'esriMiles'}
data = {'Input_Observation_Point': inPts,
'Viewshed_Distance': dist,
'f': 'pjson'}
URL = 'https://machine.domain.com/webadaptor/rest/services/Elevation/ESRI_Elevation_World/GPServer/Viewshed/execute'
req = request.Request(URL, urlencode.urlencode(data).encode('UTF-8'))
response = urlopen.urlopen(req)
response_bytes = response.read()
print(json.loads(response_bytes.decode('UTF-8')))
Result オブジェクトは文字列として返されます。 リクエストの作成と結果の解析に使用できる各種メソッドが用意されており、上記の例は、メソッドの 1 つを示しています。 Web ツールから返される JSON は、上記の例のように json.loads() を使用してディレクトリに配置できます。 Web ツールの出力によっては、これが最適な手法であることもあれば、REST を介して Python から利用される出力を処理するために、他のオプションを検討する必要があることもあります。
注意:
結果のフィーチャを操作するときは、実際の x,y 座標ペアの使用がワークフローで機能していることを確認してください。この理由は、実際のフィーチャは、ArcGIS でジオメトリをサポートするフォーマットでは受信されないからです。
非同期で実行されるサービスの場合は、ArcPy を使用した上記の例と同様に、定期的にタスクのステータスをチェックして、タスクが終了したかどうかを確認する必要があります。 Python コードにより、jobStatus ステータス メッセージ内のフレーズ esriJobSucceeded または esriJobFailed を探すことができます。これらのフレーズは、ジョブ ステータスの確認時に返されます。 次のサンプル コードは、非同期 Web ツールを操作する手法の 1 つ (submitJob を使用) を示しています。
import urllib.request as urlopen
import urllib.parse as urlencode
import urllib.request as request
import json
import time
def sendReq(URL, data=None):
req = request.Request(URL, urlencode.urlencode(data).encode('UTF-8'))
response = urlopen.urlopen(req)
response_bytes = response.read()
return json.loads(response_bytes.decode('UTF-8'))
inPts = {"geometryType": "esriGeometryPoint",
"spatialReference": {"wkid" : 54003},
'features': [{'geometry': {'x': -13308192.1956127, 'y': 4221903.58555983}}]}
dist = {'distance': 8.5, 'units': 'esriMiles'}
data = {'Input_Observation_Point': inPts,
'Viewshed_Distance': dist,
'f': 'pjson'}
taskUrl = "https://machine.domain.com/webadaptor/rest/services/Elevation/GPServer/viewshed"
submitUrl = taskUrl + "/submitJob"
submitJson = sendReq(submitUrl, data)
if 'jobId' in submitJson:
jobID = submitJson['jobId']
status = submitJson['jobStatus']
jobUrl = taskUrl + "/jobs/" + jobID
while status == "esriJobSubmitted" or status == "esriJobExecuting":
print("checking to see if job is completed...")
time.sleep(1)
jobJson = sendReq(jobUrl, {"f": "json"})
if 'jobStatus' in jobJson.keys():
status = jobJson['jobStatus']
if status == "esriJobSucceeded":
if 'results' in jobJson:
resultsJson = jobJson['results']
for paramName in resultsJson.keys():
resultsUrl = jobUrl + "/" + resultsJson[paramName]['paramUrl']
resultJson = sendReq(resultsUrl, {"f": "json"})
print(resultJson)
if status == "esriJobFailed":
if 'messages' in jobJson.keys():
print(jobJson['messages'])
else:
print("no jobId found in the response")