# Manipulating arcobjects

The arcpy module provides classes and methods a variety of tasks, including manipulating geometries. These classes and methods can be accessed in R as well using functionality provided by the reticulate package.

This document demonstrates an example task where the geometry of polygons in a shapefile is shifted 5000 linear units in the y-direction and 2500 linear units in the x-direction. This contrived task demonstrates an approach to manipulating the `arcobjects` class instances.

    library(arcpy)

    # set workspace
    arcpy$env$workspace = tempdir()
    arcpy$env$scratchWorkspace = tempdir()

    # copy feature for example
    fc = arcpy$management$CopyFeatures(system.file("CA_Counties",
      "CA_Counties_TIGER2016.shp", package = "arcpy"), "CA_Counties")

    # get the width and height of the layer
    arcpy$Describe(fc)$extent$XMin

    ## [1] -13857275

    arcpy$Describe(fc)$extent$YMin

    ## [1] 3832931

The reticulate package provides functions for manipulating python objects. These tools include iterators and the `%as%` operator for aliasing. The code below uses `%as%` to create an instance of the `fc` layer, which we can use to iterate over the polygons.

    # create iterable instance of the SHAPE@ objects in the layer
    with(arcpy$da$UpdateCursor(fc, "SHAPE@") %as% rows, {
      # move to first row
      row = iter_next(rows)
      # iterate over the features
      while (!is.null(row)) {
        # create an arcpy array for storing features
        arr_feat = arcpy$Array()
        # get the parts of the current feature
        feat = row[[1]]$getPart()
        # get the first part of the current feature
        part = iter_next(feat)
        # iterate over the parts of this feature
        while (!is.null(part)) {
          # create another arcpy array to store feature parts
          arr_part = arcpy$Array()
          # get the first point of this part
          pnt = iter_next(part)
          # iterate over the points in this part
          while (!is.null(pnt)) {
            # get the point X coordinate and subtract 5000
            x = pnt$X + 2500
            # get the point Y coordinate and add 5000
            y = pnt$Y + 5000
            # create a new point
            arr_part$add(arcpy$Point(x, y))
            # iterate to next point
            pnt = iter_next(part)
          }
          # add the modified part to the feature array
          arr_feat$add(arr_part)
          # iterate to next part
          part = iter_next(feat)
        }
        # create a new polygon from the feature array
        polygon = arcpy$Polygon(arr_feat)
        # overwrite the original polygon with the new polygon
        row[[1]] = polygon
        # update the cursor
        rows$updateRow(row)
        # iterate to next feature
        row = iter_next(rows)
      }
    })

    # recalculate the width and height of the layer
    arcpy$management$RecalculateFeatureClassExtent(paste(fc))
    arcpy$Describe(fc)$extent$XMin
    arcpy$Describe(fc)$extent$YMin

    ## <Result ''>
    ## [1] -13854775
    ## [1] 3837931

It's also possible to mix and match iterators and R functions, often with significantly faster results. Here's an alternative approach that makes judicious use of reticulate's `iterate()` function in combination with `lapply()` and the `da_read()` and `da_update()` functions:

    translate_geom = function(g, dx, dy) {
      arr_feat = arcpy$Array()
      feat = g$getPart()
      parts = iterate(feat, function(part) {
        arr_part = arcpy$Array()
        pnts = iterate(part, function(pnt) {
          x = pnt$X + dx
          y = pnt$Y + dy
          arcpy$Point(x, y)
        })
        lapply(pnts, function(part) arr_part$add(part))
        arr_part
      })
      lapply(parts, function(feat) arr_feat$add(feat))
      arcpy$Polygon(arr_feat)
    }

    fc.d = da_read(fc, "SHAPE@")
    fc.d[["SHAPE@"]] = lapply(fc.d[["SHAPE@"]][1], translate_geom,
      dx = 2500, dy = 5000)

    da_update(fc, fc.d)

    arcpy$management$RecalculateFeatureClassExtent(fc)
    arcpy$Describe(fc)$extent$XMin
    arcpy$Describe(fc)$extent$YMin

    ## <Result ''>
    ## [1] -13471139
    ## [1] 4787916

Handling python objects can be challenging in some cases, but `reticulate` provides virtually all the tools needed to manipulate arcpy classes.