6

I am wanting to take a symbolized layer in an mxd, and save it's symbol color (like a polygon fill) into its attribute table as an RGB value. My goal is to use this value in MapInfo to symbolize it's features via Discover.

I can't see any obvious way of using the ArcPy mapping module to read the particular values. Has anyone had a hack at building a simple modern solution?

GISI
  • 1,169
  • 1
  • 9
  • 30
  • How's your .net? Access to the IColor from the UniqueValuesRenderer is available from .net (vb.net, c#). There are ways of wrapping these objects for python but that's way advanced python! – Michael Stimson Dec 04 '14 at 04:53
  • My .net is dodgy. But it is food for thought. Thank you for the key words, I will look into it. – GISI Dec 04 '14 at 05:34
  • If you've got a VBA license it might be easier. Should this be the case I could put a little bit of code together. – Michael Stimson Dec 04 '14 at 05:35
  • Alas, no we don't have a licence. But, it was very kind of you to offer. Thank you. – GISI Dec 04 '14 at 05:43
  • This is a good question but I think it is a duplicate of http://gis.stackexchange.com/questions/71352/read-layer-symbology-class-color-with-arcpy and that you appear to be asking it to meet requirements similar to these http://gis.stackexchange.com/questions/27807/creating-summary-table-of-symbology-of-all-datasets-in-multiple-mxds – PolyGeo Dec 04 '14 at 05:50
  • And what you need seems to be the opposite of this ArcGIS Idea http://ideas.arcgis.com/ideaView?id=087300000008FolAAE – PolyGeo Dec 04 '14 at 05:56
  • From the IMap you get ILayer which you make into IFeatureLayer then to IGeoFeatureLayer which has the property Renderer, from the renderer you can get the IColor objects for each class which is returned packed as a long, then unpack that to RGB values. There are many renderers so make sure that you've got the right one before trying to get colours out of it. – Michael Stimson Dec 04 '14 at 06:05
  • Is this before or after you slay a dragon ;) – Nathan W Dec 04 '14 at 06:22
  • PolyGeo you have an excellent memory. Those particular posts did not pop up when I was searching the forums. It's a shame the only reply in the first one was a single line recommending ArcObjects. The second post's ConvertToMSD and reading the XML method looks interesting... I will test some ideas, and post my results. – GISI Dec 04 '14 at 06:27
  • Possibly related, but opposite - several questions about how to symbolize based on RGB values stored in the attribute table, among them http://gis.stackexchange.com/questions/123606/ I would think a way to get them out would be related to getting them in, but of course some things only work one way so maybe not. Wild idea - what if you export the layer as an RGB raster (no compression), create centroids/points within each shape, then get point values in each band of the raster to write as attributes and join it all back together? – Chris W Dec 04 '14 at 19:42
  • A VBA license is free from ESRI, you just need to ask your local distributor. It's worth getting as its a great environment to knock a bit of ArcObjects code together which is not exposed by ArcPy. Worth the effort... – Hornbydd Dec 04 '14 at 21:16

1 Answers1

6

I only need to get RGB values for polygon features. In particular solid fill vegetation and geology. I made a rough as guts ArcPy tool that fits my purpose.

The tool gets the user to nominate their layer and symbology field in ArcMap. The tool then loops through groups of "like sybology" values exporting them as rasters and interrogating them for their RGB values. These are then placed in the attribute table.

3 parameters must be passed into a script tool, which is used in a current mxd:

  1. 0: A Folder (used for temp files including new file gdb & layer files)
  2. 1: A layer (from an open mxd)
  3. 2: The nominated layers symbolology field

Code Sample:

    import arcpy
    from arcpy import env
    arcpy.gp.overwriteOutput = True

    arcpy.AddMessage("STARTED")
    arcpy.AddMessage(" ")
    arcpy.AddMessage("======================================================================")
    ##############
    Folder = arcpy.GetParameterAsText(0)
    if Folder == '#' or not Folder:
        Folder = "C:\\Data"

    ##############
    GDB = Folder+"\\TempGDB.gdb"
    if arcpy.Exists(GDB):
        arcpy.AddMessage(GDB)
    else:
        arcpy.CreateFileGDB_management(Folder, "TempGDB")
        arcpy.AddMessage("GDB MADE")

    # MUST BE IN ARCMAP
    ##############
    InPolyLayer = arcpy.GetParameterAsText(1)
    desc = arcpy.Describe(InPolyLayer)
    LayerPath = desc.catalogPath
    arcpy.AddMessage("Path: "+ LayerPath)
    spatial_ref = arcpy.Describe(LayerPath).spatialReference
    arcpy.AddMessage("Spatial Reference: "+spatial_ref.name)
    sr = arcpy.SpatialReference(spatial_ref.factoryCode)

    # Must Be The Symbology Field From "InPolyLayer"
    ##############
    FieldName = arcpy.GetParameterAsText(2)
    arcpy.AddMessage(FieldName)

    fields = arcpy.ListFields(LayerPath)
    for field in fields:
        if field.name == FieldName:
            FType = (field.type)
            FPrecision = (field.precision)
            FScale = (field.scale)
            FLength = (field.length)
            FNull = (field.isNullable)
            arcpy.AddMessage("Type: "+str(FType))
            arcpy.AddMessage("Precision: "+str(FPrecision))
            arcpy.AddMessage("Scale: "+str(FScale))
            arcpy.AddMessage("Lenght: "+str(FLength))
            arcpy.AddMessage("Null: "+ str(FNull))

    TempTileFC = GDB+"\\TempTileFC"
    if arcpy.Exists(TempTileFC):
        arcpy.Delete_management(TempTileFC)

    ### Varibles
    # Temps
    TempFrequency = GDB+"\\TempFrequency"
    if arcpy.Exists(TempFrequency):
        arcpy.Delete_management(TempFrequency)

    TempP = GDB+"\\TempP"
    if arcpy.Exists(TempP):
        arcpy.Delete_management(TempP)

    TRaster = Folder+"\\TempRaster.tif"
    arcpy.AddMessage("TEMPS SET")

    # Layers
    mxd = arcpy.mapping.MapDocument ("CURRENT")
    df = arcpy.mapping.ListDataFrames(mxd)[0]
    TempLayerName = "TempLayer"
    FreqView = "FreqView"
    TempLayerFile = Folder+"\\TempLayerFile.lyr"
    TempLayerFileLayer = "TempLayerFileLayer"
    TempLayerCloneName = "TempLayerClone"

    #Lyr for raster
    arcpy.SaveToLayerFile_management(InPolyLayer, TempLayerFile, "ABSOLUTE")


    # Fields
    FieldNameR = str(FieldName)+"_R" #Check for, and create RGB Field
    FieldNameG = str(FieldName)+"_G" #Check for, and create RGB Field
    FieldNameB = str(FieldName)+"_B" #Check for, and create RGB Field

    if len(arcpy.ListFields(InPolyLayer,FieldNameR)) == 0:
        arcpy.AddField_management (InPolyLayer, FieldNameR, "short")
        arcpy.AddMessage(FieldNameR +" field created")
    else:
        arcpy.AddMessage(FieldNameR +" field exists")

    if len(arcpy.ListFields(InPolyLayer,FieldNameG)) == 0:
        arcpy.AddField_management (InPolyLayer, FieldNameG, "short")
        arcpy.AddMessage(FieldNameG +" field created")
    else:
        arcpy.AddMessage(FieldNameG +" field exists")

    if len(arcpy.ListFields(InPolyLayer,FieldNameB)) == 0:
        arcpy.AddField_management (InPolyLayer, FieldNameB, "short")
        arcpy.AddMessage(FieldNameB +" field created")
    else:
        arcpy.AddMessage(FieldNameB +" field exists")
    arcpy.AddMessage(" ")
    arcpy.AddMessage("======================================================================")
    arcpy.AddMessage(" ")
    arcpy.AddMessage(" * FREQUENCIES & TEMP CARTO TILES")

    arcpy.Frequency_analysis(LayerPath, TempFrequency, FieldName, "")
    arcpy.AddMessage("   - Symbology Frequency Analysis Completed")

    # Make Polygon For Layer Symbol Export
    xExtent = df.extent
    pnt1 = arcpy.Point(xExtent.XMin , xExtent.YMin)
    pnt2 = arcpy.Point(xExtent.XMax , xExtent.YMin)
    pnt3 = arcpy.Point(xExtent.XMax , xExtent.YMax)
    pnt4 = arcpy.Point(xExtent.XMin , xExtent.YMax)
    array = arcpy.Array()
    array.add(pnt1)
    array.add(pnt2)
    array.add(pnt3)
    array.add(pnt4)
    array.add(pnt1)
    SYMExtent = arcpy.Polygon(array)
    arcpy.FeatureClassToFeatureClass_conversion (SYMExtent, GDB, "TempTileFC", "", "", "")
    arcpy.AddMessage("   - Temp Tile Feature Class: "+str(TempTileFC))
    arcpy.AddField_management (TempTileFC, FieldName, FType, FPrecision, FScale, FLength, "", FNull, "", "")
    arcpy.FeatureToPoint_management (SYMExtent, TempP, "CENTROID")
    TempPCen = arcpy.Describe(TempP)
    CentroidE = TempPCen.extent
    CentroidX = CentroidE.XMin
    CentroidY = CentroidE.YMin
    arcpy.AddMessage("   - Temp Tile Feature Class Field Added:"+str(FieldName))
    arcpy.AddMessage("   - Centroid Feature Class For Raster:"+str(CentroidX)+str(CentroidY))
    sourceLayer = arcpy.mapping.Layer(TempLayerFile)


    arcpy.AddMessage(" ")
    arcpy.AddMessage("======================================================================")
    arcpy.AddMessage(" ")
    arcpy.AddMessage(" * LOOPING VALUES")

    cursor = arcpy.SearchCursor(TempFrequency)
    for row in cursor:
        arcpy.AddMessage(" ")
        arcpy.AddMessage(" --------------")
        rec = row.getValue(FieldName)
        arcpy.AddMessage("   - Frequency Field Name:"+str(FieldName))
        arcpy.AddMessage("   - Frequency Record: "+str(rec))
        TempLayerClone = TempLayerCloneName+str(rec)
        arcpy.AddMessage("   - Temp Layer:"+str(TempLayerClone))

        ### We don't need to delete the geom. just write the new value.
        FieldExp ="'"+str(rec)+"'"
        arcpy.AddMessage("   - Calculate Tile Field Expression: "+str(FieldExp))
        arcpy.AddMessage("   - Temp Tile: " +str(TempTileFC))

        arcpy.CalculateField_management(TempTileFC, FieldName, FieldExp, "PYTHON_9.3","")

        # Remove Layer Test
        for df in arcpy.mapping.ListDataFrames(mxd):
            for lyr in arcpy.mapping.ListLayers(mxd, "", df):
                if str(lyr)==TempLayerClone:
                    arcpy.mapping.RemoveLayer(df, lyr)
        if arcpy.Exists(TempLayerClone):
            arcpy.Delete_management(TempLayerClone)

        # Make Layer
        arcpy.MakeFeatureLayer_management (TempTileFC, TempLayerClone, "", "", "") #  Make Layer
        FeatureClassLayerMapping = arcpy.mapping.Layer(TempLayerClone)
        arcpy.mapping.AddLayer(df, FeatureClassLayerMapping, "TOP") # Layer At Top
        arcpy.AddMessage("   - Layer Added")

        # Update
        df2 = arcpy.mapping.ListDataFrames(mxd)[0]
        updateLayer2 = arcpy.mapping.ListLayers(mxd, TempLayerClone, df)[0]
        arcpy.mapping.UpdateLayer(df2, updateLayer2, sourceLayer, "True") # Carto Matches

        ################ NUMERIC OR TEXT
        if FType == "String":
            arcpy.AddMessage("   - IS String")
            ## 10.1
            DefQURY = (("""\"{0}"='{1}'""").format(FieldName,rec)) #TEXT FIELDS
        elif FType == "Text":
            arcpy.AddMessage("   - IS TEXT")
            ## 10.1
            DefQURY = (("""\"{0}"='{1}'""").format(FieldName,rec)) #TEXT FIELDS
        else:
            arcpy.AddMessage("   - IS NOT TEXT")
            DefQURY = (("""\"{0}"={1}""").format(FieldName,rec))   #NUMERIC FIELDS

        arcpy.AddMessage("   - Layer Updated")
        arcpy.AddMessage("   - Selection Qu:" +str(DefQURY))
        arcpy.SelectLayerByAttribute_management (updateLayer2, "NEW_SELECTION", DefQURY ) # Select & Zoom

        df.zoomToSelectedFeatures()
        arcpy.SelectLayerByAttribute_management (updateLayer2, "CLEAR_SELECTION")
        arcpy.RefreshActiveView()
        arcpy.AddMessage("   - Zoomed")

        #Raster
        if arcpy.Exists(TRaster):
            arcpy.Delete_management(TRaster)
        arcpy.mapping.ExportToTIFF(mxd, TRaster, df2,  df_export_width=600,df_export_height=600,geoTIFF_tags=True)
        arcpy.AddMessage("   - RGB Raster Exported")
        arcpy.AddMessage("   - RGB Raster: "+TRaster)

        Centroidf = ("{0} {1}".format(CentroidX,CentroidY))
        RValues = arcpy.GetCellValue_management(TRaster, Centroidf, "1")
        GValues = arcpy.GetCellValue_management(TRaster, Centroidf, "2")
        BValues = arcpy.GetCellValue_management(TRaster, Centroidf, "3")
        arcpy.AddMessage("   - R: "+str(RValues))
        arcpy.AddMessage("   - G: "+str(GValues))
        arcpy.AddMessage("   - B: "+str(BValues))

        #Selection
        arcpy.SelectLayerByAttribute_management (InPolyLayer, "NEW_SELECTION", DefQURY )
        arcpy.AddMessage("   - Selection: "+ InPolyLayer)
        arcpy.AddMessage("   - Def: "+ DefQURY)

        arcpy.CalculateField_management(InPolyLayer, FieldNameR, RValues, "PYTHON_9.3")
        arcpy.CalculateField_management(InPolyLayer, FieldNameG, GValues, "PYTHON_9.3")
        arcpy.CalculateField_management(InPolyLayer, FieldNameB, BValues, "PYTHON_9.3")
        arcpy.SelectLayerByAttribute_management (InPolyLayer, "CLEAR_SELECTION", DefQURY )
        arcpy.AddMessage("   - Calculated & Cleared")

        # Remove Layer
        for df3 in arcpy.mapping.ListDataFrames(mxd):
            for lyr in arcpy.mapping.ListLayers(mxd, "", df3):
                if str(lyr)==TempLayerClone:
                    arcpy.mapping.RemoveLayer(df3, lyr)

        del TempLayerClone
        del FeatureClassLayerMapping
        del df2, df3

        arcpy.AddMessage("   - Removed & Cleared")

    ## if current is not saved then the tool will fail
        mxd.save()

Example: enter image description here

GISI
  • 1,169
  • 1
  • 9
  • 30
  • 1
    I wrote the above py to be a script tool. The 3 exposed parameters plug into the toolbox tool nicely. Here's how you make a script tool: http://help.arcgis.com/EN/arcgisdesktop/10.0/help/index.html#/Adding_a_script_tool/00150000001r000000/ – GISI Aug 28 '15 at 07:22
  • Can you write down some information what types of parameters did you use for Symbology field when creating a script tool. – MihaK Feb 06 '17 at 17:27