この例では、ArcGIS REST API と ArcGIS ジオプロセシング関数を組み合わせて、サーバー ログを読み取り、サーバーに対するすべてのマップ リクエストの範囲を含むフィーチャクラスを構築する方法を示します。この方法は、ユーザーがサービスをどのように表示しているかについての空間パターンを理解する際に役立ちます。
このスクリプトを使用するには、最初に ArcGIS Server のログ レベルを少なくとも [詳細] に設定する必要があります。続いて、サーバーがクライアントからのマップ リクエストに応答した後に、このスクリプトを実行してリクエストされた範囲をフィーチャクラスに書き込むことができます。ログに記録される範囲ごとにタイムスタンプ情報が含まれ、この情報をフィーチャクラスに書き込むことで、ArcGIS でタイム スライダーを使用してリクエストを再生することもできます。
このスクリプトでは、既存のファイル ジオデータベースが必要です。ただし、フィーチャクラスは自動的に作成されます。
このスクリプトでは、カスタム空間参照 (既知の ID (wkid)) で表すことができない空間参照)は処理されません。
# Queries the logs to find the map extents requested for a given map service
# For output Featureclass creation
print "\nImporting ArcPy..."
import arcpy
# Set Script arguments
arcpy.env.overwriteOutput = True
# For Http calls
import httplib, urllib, json
# For system tools
import sys, datetime, os
# For reading passwords without echoing
import getpass
#Defines the entry point into the script
def main(argv=None):
# Print some info
print
print "This tool is a sample script that queries the ArcGIS Server logs."
print
# Ask for admin/publisher user name and password
username = raw_input("Enter user name: ")
password = getpass.getpass("Enter password: ")
# Ask for server name
serverName = raw_input("Enter Server name: ")
serverPort = 6080
# Ask for map service name
mapService = raw_input("Enter map service name, using a forward slash / to denote a folder: ")
if mapService.endswith(".MapServer"):
pass
else:
mapService += ".MapServer"
# Ask for output workspace
outputWorkspace = raw_input("Enter output Workspace (Geodatabase location): ")
# Ask for output featureclass name
outputFeatureclass = raw_input("Enter output Featureclass name: ")
# Construct REST service URL
serviceURL = "/arcgis/rest/services/{0}".format( mapService.replace( ".", "/"))
# Get a token
print "Requesting Token..."
token = getToken(username, password, serverName, serverPort)
if token == "":
print "Could not generate a token with the username and password provided."
return
# Get Extent detail for service
print "\nLooking up Service details..."
serviceURL = serviceURL + "/?Token=" + token
fullExtent = getFullExtent( serverName, serverPort, serviceURL)
if not fullExtent:
return
# Construct URL to query the logs
logQueryURL = "/arcgis/admin/logs/query"
logFilter = "{'services': ['" + mapService + "']}"
# Supply the log level, filter, token, and return format
params = urllib.urlencode({'level': 'FINE', 'filter': logFilter, 'token': token, 'f': 'json'})
headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
# Connect to URL and post parameters
print "Accessing Logs..."
httpConn = httplib.HTTPConnection(serverName, serverPort)
httpConn.request("POST", logQueryURL, params, headers)
# Read response
response = httpConn.getresponse()
if (response.status != 200):
httpConn.close()
print " Error while querying logs."
return
else:
data = response.read()
# Check that data returned is not an error object
if not assertJsonSuccess(data):
print " Error returned by operation. " + data
else:
print " Operation completed successfully!"
# Deserialize response into Python object
dataObj = json.loads(data)
httpConn.close()
# Open Insert Cursor on output
output = openCursor( outputWorkspace, outputFeatureclass, fullExtent[ "spatialReference"][ "wkid"])
if not output:
return
# Need this variable to track number of events found for ExportMapImage call
logEvents = 0
# Need Array to hold Shape
shapeArray = arcpy.Array()
# Iterate over messages
for item in dataObj[ "logMessages"]:
eventDateTime = datetime.datetime.fromtimestamp( float( item[ "time"]) / 1000)
if item[ "message"].startswith( "Extent:"):
eventScale = None # Scale
eventInvScale = None # Inverse-Scale
eventWidth = None # Width
eventHeight = None # Height
# Cycle through message details
for pair in item[ "message"].replace(" ", "").split( ";"):
if pair.count( ":") == 1:
key, val = pair.split( ":")
# Pick out Extent
if key == "Extent" and val.count( ",") == 3:
# Split into ordinate values
MinX, MinY, MaxX, MaxY = val.split( ",")
MinX = float( MinX)
MinY = float( MinY)
MaxX = float( MaxX)
MaxY = float( MaxY)
# Make sure extent is within range
if MinX > fullExtent[ "xmin"] and MaxX < fullExtent[ "xmax"] and MinY > fullExtent[ "ymin"] and MaxY < fullExtent[ "ymax"]:
shapeArray.add( arcpy.Point( MinX, MinY))
shapeArray.add( arcpy.Point( MinX, MaxY))
shapeArray.add( arcpy.Point( MaxX, MaxY))
shapeArray.add( arcpy.Point( MaxX, MinY))
shapeArray.add( arcpy.Point( MinX, MinY))
# Pick out Size
if key == "Size" and val.count( ",") == 1:
eventWidth, eventHeight = val.split( ",")
eventWidth = float( eventWidth)
eventHeight = float( eventHeight)
# Pick out Scale
if key == "Scale":
eventScale = float( val)
eventInvScale = 1 / eventScale
# Save if Shape created
if shapeArray.count > 0:
# Create new row
newRow = output.newRow()
# Add Shape and Event Date
newRow.setValue( "Shape", shapeArray)
newRow.setValue( "EventDate", eventDateTime)
newRow.setValue( "Scale", eventScale)
newRow.setValue( "InvScale", eventInvScale)
newRow.setValue( "Width", eventWidth)
newRow.setValue( "Height", eventHeight)
output.insertRow( newRow)
# Clear out Array points
shapeArray.removeAll()
logEvents += 1
print "\nDone!\n\nTotal number of events found in logs: {0}".format( logEvents)
return
#A function to query service for Extent and Spatial Reference details
def getFullExtent( serverName, serverPort, serviceURL):
# Supply the return format
params = urllib.urlencode({'f': 'json'})
headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
# Connect to URL and post parameters
httpConn = httplib.HTTPConnection(serverName, serverPort)
httpConn.request("POST", serviceURL, params, headers)
# Read response
response = httpConn.getresponse()
if (response.status != 200):
httpConn.close()
print "Error while querying Service details."
return
else:
data = response.read()
# Check that data returned is not an error object
if not assertJsonSuccess(data):
print "Error returned by Service Query operation. " + data
# Deserialize response into Python object
dataObj = json.loads(data)
httpConn.close()
if not 'fullExtent' in dataObj:
print "Unable to find Extent detail for '{0}'!".format( serviceURL)
print dataObj
elif not 'spatialReference' in dataObj[ 'fullExtent']:
print "Unable to find Spatial Reference for '{0}'!".format( serviceURL)
print "dataObj"
else:
return dataObj[ 'fullExtent']
return
#A function to create new Featureclass and return an Insert Cursor, used to store Map Query Extents.
def openCursor( workspace, featureclassName, srid):
if not arcpy.Exists( workspace):
print "Unable to find Workspace '{0}'...".format( workspace)
return
print "Creating output Featureclass..."
arcpy.CreateFeatureclass_management( workspace, featureclassName, "POLYGON", None, None, None, srid)
Featureclass = workspace + os.sep + featureclassName
print " Adding field(s)..."
arcpy.AddField_management( Featureclass, "EventDate", "DATE", None, None, None, None, "NULLABLE", "NON_REQUIRED")
arcpy.AddField_management( Featureclass, "Scale", "DOUBLE", 19, 2, None, None, "NULLABLE", "NON_REQUIRED")
arcpy.AddField_management( Featureclass, "InvScale", "DOUBLE", 19, 12, None, None, "NULLABLE", "NON_REQUIRED")
arcpy.AddField_management( Featureclass, "Width", "LONG", 9, None, None, None, "NULLABLE", "NON_REQUIRED")
arcpy.AddField_management( Featureclass, "Height", "LONG", 9, None, None, None, "NULLABLE", "NON_REQUIRED")
print " Opening Insert Cursor..."
return arcpy.InsertCursor( Featureclass)
#A function to generate a token given username, password and the adminURL.
def getToken(username, password, serverName, serverPort):
# Token URL is typically http://server[:port]/arcgis/admin/generateToken
tokenURL = "/arcgis/admin/generateToken"
# URL-encode the token parameters
params = urllib.urlencode({'username': username, 'password': password, 'client': 'requestip', 'f': 'json'})
headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
# Connect to URL and post parameters
httpConn = httplib.HTTPConnection(serverName, serverPort)
httpConn.request("POST", tokenURL, params, headers)
# Read response
response = httpConn.getresponse()
if (response.status != 200):
httpConn.close()
print "Error while fetching tokens from admin URL. Please check the URL and try again."
return
else:
data = response.read()
httpConn.close()
# Check that data returned is not an error object
if not assertJsonSuccess(data):
return
# Extract the token from it
token = json.loads(data)
return token['token']
#A function that checks that the input JSON object
# is not an error object.
def assertJsonSuccess(data):
obj = json.loads(data)
if 'status' in obj and obj['status'] == "error":
print "Error: JSON object returns an error. " + str(obj)
return False
else:
return True
# Script start
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))