How to programatically add symbols to a .stylx (Styles) file and apply that symbology to a feature layer

Document created by jknowles@usbr.gov_USBR on Mar 27, 2020Last modified by jknowles@usbr.gov_USBR on Apr 13, 2020
Version 2Show Document
  • View in full screen mode

I was recently working on a project that needed something like 100 unique symbols. Each symbol was a circle or square with a four letter code on it. This is something that lent itself perfectly to being done programatically. I really didn't want to have to generate those symbols one by one, so I started searching of ways to do this programatically, but didn't find much information on how I might get this done using Python.

 

The first glimmer of hope was when I found how to apply symbols from style gallery (i.e. Favorites or project Styles .stylx). I had accepted the fact that I'd just programatically create a bunch of png's, add them to my Favorites, and then write some code to apply the symbols in my Pro project. Then I stumbled upon an Esri blog post that mentioned that .stylx files are just SQLite databases. This got me thinking that I'd be able to further program the generation of these symbols.

 

I used DB Browser for SQLite to take a look at the structure of the .stylx db. Most of what you need is stored in the ITEMS table. There's a content field where the json representation of the symbol is stored. For some reason, the json string has a character at the end that makes DB Browser think it's a blob instead of text. If you delete the 00 at the end, it will then display as text and you can get an idea of what the json looks like for a CIMPointSymbol for example.

 

Things to know about the .stylx file:

  • both ID and KEY have unique constraints, so make sure the rows you add to the database comply with these constraints
  • CLASS is the type of things your adding to the .stylx - I didn't investigate all the options, but I do know 1 is a color and 3 is a symbol

 

I broke my code up into two python scripts. Here's the pseudocode and some real code for each script:

 

Generate symbols - programmatically generate symbols and add to your Favorites .stylx

  • read symbol information from excel file into a pandas df (things like fill color, border color, text for symbol, etc.)
  • read in a template json from a text file - instead of generating all the json, I just modify this template by changing things like fill color, border color, etc
    • I created this template by creating a symbol in my Favorites.stylx, viewing it in DB Browser, copying the json, and saving it to a .json file
  • connect to the .stylx using sqlite3 (conn= sqlite3.connect(db))
  • iterate through the rows in my df
    • modify my json from the template
    • create a new row to add to the db
      • new_row = (new_id,3,'',code,'rgb;black',json.dumps(new_sym),code)
        execute sql code to insert row into db
      • cursor.execute('INSERT INTO ITEMS(ID, CLASS, CATEGORY, NAME, TAGS, CONTENT, KEY) VALUES(?,?,?,?,?,?,?)', new_row)
        commit changes to db and close db

 

At this point, I now have a bunch of new symbols in my .stylx file. While testing I worked on a copy of my Favorites.stylx, but as long as Pro is closed, I don't think there's any issues with working on the real file in its default location ("C:\Users\userxxxx\AppData\Roaming\ESRI\ArcGISPro\Favorites.stylx"). Just make sure you have a backup copy of it just in case you ruin it.

 

Apply symbols - as long as the name of your new symbols matches the symbol item values in your Pro project, it's relatively easy to apply the symbology programmatically

  • read in map, layer, symbology from a Pro project
    • m = p.listMaps('Map')[0]
      lyr = m.listLayers("layer name")[0]
      sym = lyr.symbology
  • iterate through symbol groups and symbol items in each group and apply symbol, as long as there is symbol in the gallery with the name of your item's value, this should work
    • for grp in sym.renderer.groups:
          for item in grp.items:
          item.symbol.applySymbolFromGallery(item.values[0][0])
  • set lyr.symbology to the sym that was just modified (I forgot to do this and it took me forever to figure out why my symbology wasn't getting applied)
  • save the project

 

I hope this helps someone that found themselves in the same position that I was. If anyone needs any more details on how this was done, just let me know.

 

Here's a list of posts that I found while searching how to do this

Importing Custom Point Symbols 

Custom Symbols for AGOL 

Creating a .style file 

Assigning an icon from a .stylx file and rotating it, in arcpy 

Attachments

    Outcomes