Skip To Content

示例:将请求的地图范围写入要素类

本示例显示了如何组合 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:]))