Create Bookmarks from Features

1074
5
06-14-2023 02:01 PM
JohannesLindner
MVP Frequent Contributor

@JoeBryant1 posted an Idea earlier about being able to create bookmarks from the features in a feature class / layer. It then got merged into this really old Idea: https://community.esri.com/t5/arcgis-pro-ideas/layer-to-bookmarks-tool-amp-bookmarks-to-layer/idc-p/...

 

Seeing as the original Idea is from 2011, I don't think this will be implemented any time soon, but it seemed like a fun little project to implement myself...

The function in the following Python script will read a feature class or layer (honoring definition queries and selection) and create a bookmark file using the feature extents. The bookmark file can then be imported into an ArcGIS Pro map.

  • The bookmarks can be named by a field or with an incrementing number.
  • The features can be dissolved by a field (in that case the naming field gets switched to the dissolve field).
  • The features can be buffered.
  • You can specify the minimum length of the smallest dimension of the resulting bookmarks.

Using this function, it's easy to turn the script into a script tool, which is attached to this post (as zipped atbx).

 

This was a fun little project, and if it can help  some people, all the better 🙂

 

 

import arcpy
from pathlib import Path


def _create_bookmark_dict(in_geometry, min_dist):
    """Returns a dict defining a bookmark for the input arcpy.Geometry object."""
    e = in_geometry.extent
    d = min([e.width, e.height])
    if d < min_dist:
        buffered_geometry = in_geometry.buffer((min_dist - d) / 2)
        e = buffered_geometry.extent
    return {
        "type": "CIMBookmark",
        "location": {
            "xmin": e.XMin,
            "xmax": e.XMax,
            "ymin": e.YMin,
            "ymax": e.YMax,
            "spatialReference": {"wkid": in_geometry.spatialReference.factoryCode}
            }
        }
    
def create_bookmarks(in_features, out_path, name_field=None, dissolve_field=None, buffer_dist=None, min_dist=None):
    """Creates bookmarks from all features in a layer.
    in_features (path to feature class as str / arcpy.mp.Layer object): The input features. If this is a layer, definition queries and selection will be honored.
    out_path (str): Path of the output bkmx file.
    name_field (str): The field used to name the bookmarks. Optional, default is incrementing number.
    dissolve_field (str): The field by which to dissolve the features before creating the bookmarks. Optional, by default there is no dissolving. If both name and dissolve field are specified, the name will be ignored and the features will be named by the dissolve field.
    buffer_dist: distance by which the input features are buffered, measured in the default units of the in_feature's coordinate system, important for small features. Optional, defaults to zero.
    min_dist: minimum extent width or height, measured in the default units of the in_feature's coordinate system, important for small features. Optional, defaults to zero.
    
    """
    # dissolve features
    if dissolve_field not in [None, "", "#"]:
        in_features = arcpy.management.Dissolve(in_features, "memory/dissolve", [dissolve_field])
        if name_field not in [None, "", "#"]:
            name_field = dissolve_field
    # buffer features
    if buffer_dist is not None and buffer_dist > 0:
        in_features = arcpy.analysis.Buffer(in_features, "memory/buffer", buffer_dist)
    # create the bookmarks
    if min_dist is None or min_dist < 0:
        min_dist = 0
    bookmarks = []
    read_fields = ["SHAPE@"]
    if name_field not in [None, "", "#"]:
        read_fields.append(name_field)
    for i, row in enumerate(arcpy.da.SearchCursor(in_features, read_fields)):
        try:
            bm = _create_bookmark_dict(row[0], min_dist)
        except:  # eg null geometry
            continue
        if name_field not in [None, "", "#"] and row[1] is not None:
            bm["name"] = row[1]
        else:
            bm["name"] = str(i)
        bookmarks.append(bm)
    # write bkmx file
    out_dict = {"bookmarks": bookmarks}
    f = Path(str(out_path))
    f.write_text(str(out_dict))



if __name__ == "__main__":
    in_features = arcpy.GetParameter(0)
    out_path = arcpy.GetParameterAsText(1)
    name_field = arcpy.GetParameterAsText(2)
    dissolve_field = arcpy.GetParameterAsText(3)
    buffer_dist = arcpy.GetParameter(4)
    min_dist = arcpy.GetParameter(5)
    create_bookmarks(in_features, out_path, name_field, dissolve_field, buffer_dist, min_dist)

 

 

 


Have a great day!
Johannes
5 Replies
JoeBryant1
Occasional Contributor III

Wow! Mad props, Johannes! I feel like you wrote that code in the same amount of time it took me to write the original post!

Just tested and it works as expected. I'll be adding it to our company tool list.

THANKS!

0 Kudos
hgnudas
New Contributor II

Hello,

Thanks so much for creating this tool. I am having some issues when attempting to create a map series with the script. The script creates the correct number of bookmarks from the name, however, the extent is completely off. All of my points are within Ontario, however, once I run the tool all of the outputted bookmarks jump to the middle of the ocean near Sardigna and Barcelona. I presume it is something to do with the projection, but I can't seem to figure it out. Thanks in advance.

0 Kudos
hgnudas
New Contributor II

I figured it out! It had to do with the coordinate system in my map properties being incorrect. After updating, all facility points now have a associated bookmark!

0 Kudos
Trisarahtops
New Contributor II

I've been looking for this exact functionality, thanks so much Johannes! I tweaked the code slightly to allow a negative buffer distance, because I'd like the bookmark to be centered and zoomed in to the feature. Works great!

0 Kudos
TCKauhi
New Contributor II

@JohannesLindner that worked great, thank you!

0 Kudos