Skip To Content

例: リクエストされたマップの範囲のフィーチャクラスへの書き込み

この例では、ArcGIS REST API と ArcGIS ジオプロセシング関数を組み合わせて、サーバー ログを読み取り、サーバーに対するすべてのマップ リクエストの範囲を含むフィーチャクラスを構築する方法を示します。この方法は、ユーザーがサービスをどのように表示しているかについての空間パターンを理解する際に役立ちます。

このスクリプトを使用するには、最初に ArcGIS Server のログ レベルを少なくとも [詳細] に設定する必要があります。続いて、サーバーがクライアントからのマップ リクエストに応答した後に、このスクリプトを実行してリクエストされた範囲をフィーチャクラスに書き込むことができます。ログに記録される範囲ごとにタイムスタンプ情報が含まれ、この情報をフィーチャクラスに書き込むことで、ArcGIS でタイム スライダーを使用してリクエストを再生することもできます。

このスクリプトでは、既存のファイル ジオデータベースが必要です。ただし、フィーチャクラスは自動的に作成されます。

このスクリプトでは、カスタム空間参照 (well-known 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:]))