本示例显示了如何组合 ArcGIS REST API 和 ArcGIS 地理处理功能以读取服务器日志,从而构造一个包含对服务器进行的所有地图请求的范围的要素类。这可帮助您了解人们查看您的服务的整体空间模式。
要使用此脚本,您必须首先将 ArcGIS Server 日志记录至少设置为“精细 (FINE)”级别。然后,在您的服务器满足了一些来自客户端的地图请求后,您可以运行此脚本将请求的所有范围写入要素类。所记录的每个范围都有时间戳信息,该信息也将写入到要素类,可允许您在 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:]))