--- title: "Manipulating arcobjects" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Manipulating ArcObjects} %\VignetteEngine{knitr::rmarkdown} \usepackage[utf8]{inputenc} --- 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](https://cran.r-project.org/package=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. ```r library(arcpy) ``` ```r # 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 ``` ```r 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. ```r # 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 ``` ``` ## ## [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: ```r 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 ``` ``` ## ## [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.