ArcPy Migration

The Migration Playbook

File-by-file strategy: which ArcPy functions map to what, the 12 that have no equivalent, and how to run old and new in parallel.

PUBLISHEDFEB 2026
SERIESARCPY MIGRATION
PART2 OF 3
Sumi-e ink painting representing the transition from legacy ArcPy scripts to modern open-source geospatial tools
  • 80% of ArcPy functions have direct open-source equivalents (GeoPandas, rasterio, Shapely, Fiona)
  • 12 ArcPy functions have NO equivalent in open source - you need workarounds or must keep ArcPy for these
  • Migration is not all-or-nothing: the hybrid approach (keep ArcPy for what it does best, migrate the rest) is the most practical path
  • Average migration timeline: 4-8 weeks for a 20-script inventory, with 2 weeks of parallel testing

Part 1 made the business case for migration. You're convinced the numbers work. Now what?

This post is the actual playbook: how to inventory your scripts, which functions map to what, the 12 functions that have no open-source equivalent (and what to do about them), and how to run old and new in parallel until you're confident.

Most migration guides say "use GeoPandas instead of ArcPy" and leave it there. That's not enough. You need to know which ArcPy functions translate cleanly, which require workarounds, and which mean you should keep ArcPy for those specific workflows. For a detailed function-by-function translation, see our ArcPy to GeoPandas guide.

The Inventory-First Approach

Before migrating anything, inventory everything. This step takes one week and saves months of wasted effort on scripts that should be deleted, not migrated.

Step 1: Find All ArcPy Scripts

Start with a simple recursive search across your scripts directory for any Python file that contains an import arcpy statement. This gives you a definitive list of every file that depends on ArcPy, along with a total count. Most teams are surprised - the real number is usually 20-40% higher than their initial estimate, because scripts live in project folders, shared drives, and archived directories that nobody has touched in years.

Step 2: Categorise Each Script

CategoryDefinitionAction
Green (easy)Standard geoprocessing (Buffer, Intersect, Dissolve, Join)Migrate to GeoPandas
Yellow (medium)Uses arcpy.env settings, cursor operations, or field calculationsMigrate with refactoring
Red (hard)Network Analyst, Spatial Analyst, 3D Analyst, or Enterprise integrationKeep in ArcPy or find workarounds
xGrey (obsolete)No longer used, duplicate, or supersededDelete

REAL-WORLD DISTRIBUTION

In a typical 20-script inventory, we see 8-10 Green, 5-7 Yellow, 2-4 Red, and 1-3 Grey. That means 65-85% of scripts can be migrated to open source. The Grey scripts are the hidden win - deleting dead code reduces maintenance burden immediately, at zero cost.

Function Mapping Table

This is the reference table your team will use daily during migration. For each ArcPy function, the open-source equivalent and any caveats. See our side-by-side benchmark comparison for performance data on these equivalents.

Function mapping reference chart for ArcPy to open-source migration showing equivalent libraries
ArcPy FunctionOpen-Source EquivalentNotes
arcpy.analysis.Buffergdf.buffer(distance)GeoPandas. Ensure projected CRS for metre units
arcpy.analysis.Intersectgpd.overlay(gdf1, gdf2, how='intersection')GeoPandas overlay
arcpy.analysis.Uniongpd.overlay(gdf1, gdf2, how='union')GeoPandas overlay
arcpy.analysis.SpatialJoingpd.sjoin(gdf1, gdf2, how='inner')GeoPandas spatial join
arcpy.management.Dissolvegdf.dissolve(by='column')GeoPandas dissolve
arcpy.management.Mergepd.concat([gdf1, gdf2])pandas concat
arcpy.management.Clipgpd.clip(gdf, mask)GeoPandas clip
arcpy.management.Projectgdf.to_crs(epsg=4326)GeoPandas CRS transformation
arcpy.management.AddFieldgdf['new_col'] = valuepandas column assignment
arcpy.management.CalculateFieldgdf['col'] = gdf['col'].apply(func)pandas apply
arcpy.management.SelectByAttributegdf[gdf['col'] == value]pandas filtering
arcpy.management.SelectByLocationgdf[gdf.intersects(geometry)]GeoPandas spatial filtering
arcpy.conversion.FeatureClassToFCgdf.to_file('output.gpkg')Fiona via GeoPandas
arcpy.conversion.TableToTabledf.to_csv('output.csv')pandas export
arcpy.da.SearchCursorfor idx, row in gdf.iterrows()pandas iteration (or vectorised)
arcpy.da.UpdateCursorgdf.loc[condition, 'col'] = valuepandas vectorised update
arcpy.da.InsertCursorgdf = pd.concat([gdf, new_rows])pandas concat
arcpy.Describegdf.crs, gdf.total_bounds, gdf.dtypesGeoPandas properties
arcpy.ListFeatureClassesglob.glob('*.gpkg')Python glob
arcpy.sa.Rasterrasterio.open('file.tif')rasterio
arcpy.sa.ZonalStatisticsAsTablerasterstats.zonal_stats()rasterstats package
arcpy.sa.ExtractByMaskrasterio.mask.mask()rasterio.mask
arcpy.sa.Sloperichdem.TerrainAttribute(dem, 'slope')richdem or custom numpy
arcpy.sa.Aspectrichdem.TerrainAttribute(dem, 'aspect')richdem

24 functions mapped. This covers the vast majority of scripts in a typical GIS team's inventory.

The 12 Gaps (No Direct Equivalent)

This is the section nobody else writes. These 12 ArcPy functions have no clean open-source replacement. Knowing this upfront prevents the worst migration outcome: discovering gaps after you've committed to a timeline.

ArcPy FunctionWhy No EquivalentWorkaround
arcpy.na.MakeServiceAreaLayerNetwork Analyst is proprietarypgRouting (PostGIS) or OSRM
arcpy.na.MakeClosestFacilityLayerNetwork AnalystpgRouting or NetworkX
arcpy.na.MakeODCostMatrixLayerNetwork AnalystpgRouting or OSRM API
arcpy.un.TraceProprietary data modelNo equivalent - keep ArcPy
arcpy.management.CreateTopologyArcGIS topology rulesPostGIS topology (partial)
arcpy.management.ValidateTopologyArcGIS topology validationPostGIS ST_IsValid + custom rules
arcpy.cartography.SimplifyLineCartographic generalisationShapely simplify (less sophisticated)
arcpy.cartography.CollapseHydroPolyCartographic specialisationNo equivalent
arcpy.ia.ClassifyRasterImage Analyst extensionscikit-learn + rasterio (more code)
arcpy.ddd.ViewShed3D Analyst extensionGRASS GIS r.viewshed (command-line)
arcpy.ddd.SurfaceVolume3D Analyst extensionCustom numpy calculation
arcpy.management.MakeQueryLayerArcGIS Enterprise integrationDirect SQL via SQLAlchemy

The Honest Assessment

Of these 12, only Utility Network tracing has genuinely no workaround. The rest have alternatives that require more code but are functional. The question is whether the extra development effort is worth the licence savings. For Network Analyst functions, pgRouting is a capable alternative but requires PostGIS setup and a different data model - budget 2-3 weeks for the learning curve.

File-by-File Strategy

Each script gets migrated individually, not as a batch. This is the step-by-step process for a single script. Repeat for each Green, then Yellow.

1

Read the script

Understand what it does, not just what functions it calls. Talk to the analyst who runs it

2

Map the functions

Use the mapping table above to identify equivalents for every ArcPy call

3

Check for gaps

Any Red functions? If so, decide: workaround, keep in ArcPy, or redesign the workflow

4

Rewrite from scratch

Use GeoPandas/rasterio idioms. Do not transliterate ArcPy line-by-line - idiomatic open-source code is vectorised, not row-by-row

5

Test with the same data

Run old (ArcPy) and new (GeoPandas) on identical input datasets

6

Compare outputs

Geometry count, attribute values, CRS, spatial extent. Use the comparison script below

7

Document differences

Some differences are expected: floating-point precision (1e-6), sort order, field name casing

8

Validate with the analyst

The person who runs this workflow must confirm the output is correct. Technical equivalence is not enough - business equivalence matters

DO NOT TRANSLITERATE

The biggest migration mistake is converting ArcPy line-by-line into GeoPandas. ArcPy uses cursor-based, row-by-row processing. GeoPandas is vectorised. A transliterated script will be slower than both the original ArcPy and idiomatic GeoPandas. Rewrite from scratch using GeoPandas patterns.

Parallel Testing

Never switch over without running old and new in parallel. Automate the validation rather than comparing outputs by eye - human spot-checks miss edge cases.

A well-structured comparison function reads both the ArcPy output and the GeoPandas output as GeoDataFrames, then runs four checks: row count equality, CRS match, column set equality, and spatial extent agreement within a configurable tolerance (typically 1mm for projected coordinate systems). Any failed check prints a clear diagnostic. Run this function as part of your CI pipeline so failures are caught immediately, not during analyst review a week later.

Minimum 2 Weeks Parallel

Run both old (ArcPy) and new (GeoPandas) scripts on the same data for at least 2 weeks. Compare every output. Only switch over when you've had zero discrepancies for 2 consecutive weeks. This catches edge cases that unit tests miss - unusual geometries, encoding issues, projection edge cases.

Common Migration Patterns

Three patterns cover 90% of what you'll encounter. Each shows the ArcPy original and the idiomatic GeoPandas replacement.

Pattern 1: arcpy.env.workspace to working directory

ARCPY

ArcPy scripts typically open by setting arcpy.env.workspace to a file geodatabase path and enabling overwriteOutput. These two global settings implicitly govern every subsequent geoprocessing call in the script, creating hidden state that can cause unexpected behaviour when scripts are called from automated pipelines.

GEOPANDAS

Replace with a plain Python path constant - typically a string or pathlib.Path variable set at the top of the script. No equivalent of overwriteOutput is needed: GeoPandas writes to whatever path you specify and overwrites by default. Explicit beats implicit.

Pattern 2: Cursors to pandas operations

ARCPY (ROW-BY-ROW)

The ArcPy pattern opens an UpdateCursor as a context manager, iterates through every row in a feature class, checks a field value, conditionally updates another field, and commits the change row by row. For a 100,000-record parcel dataset, this means 100,000 individual write operations. The overhead is substantial.

GEOPANDAS (VECTORISED)

The GeoPandas equivalent uses a boolean mask to select all rows where the area exceeds the threshold, then assigns the value to the category column in a single vectorised operation. No loop. No cursor. The entire dataset updates in one pass at the C library level - typically 10-100x faster than the cursor approach on datasets above 10,000 records.

Pattern 3: Geoprocessing to GeoPandas methods

ARCPY

ArcPy chains geoprocessing calls by passing string layer names between tools. A Buffer writes to an intermediate layer by name, then Intersect reads that named layer as input. The workspace is the implicit link. This works well inside ArcGIS Pro, but makes scripts difficult to test in isolation and impossible to run outside an ArcGIS environment.

GEOPANDAS

GeoPandas passes GeoDataFrame objects directly between operations - no intermediate files, no string references. Call .buffer() on the roads GeoDataFrame (ensuring a projected CRS first), then pass the resulting geometry directly into gpd.overlay() with the parcels layer. The intersection runs in-memory. The critical detail: buffer distance is in the CRS units - always reproject to metres before buffering.

Handling arcpy.env

arcpy.env provides global settings that affect every geoprocessing operation. Open-source tools don't have this concept - each operation is explicit. Here's how to handle each setting.

arcpy.env SettingOpen-Source EquivalentNotes
workspaceWorking directory variableManual path management
overwriteOutputNo equivalent (always overwrites)Delete old files explicitly if needed
outputCoordinateSystemgdf.to_crs()Explicit per-operation
extentgdf.clip(extent_gdf)Explicit clipping
cellSizerasterio resolution parameterPer-operation
maskrasterio.mask.mask()Explicit masking
parallelProcessingFactordask-geopandasDifferent parallelism model

The shift from implicit (arcpy.env sets it once, everything inherits) to explicit (pass CRS, extent, and mask to each function) feels more verbose initially. In practice, explicit is better: you can see exactly what each operation does without hunting for global state set 200 lines earlier.

When to Keep ArcPy

Migration is not all-or-nothing. Keeping ArcPy for specific workflows is a legitimate engineering decision, not a failure. Here are the six scenarios where ArcPy remains the right tool.

1

Network Analysis Workflows

Service areas, closest facility, OD cost matrices. pgRouting exists but requires PostGIS setup and a different data model. If you depend on ESRI network datasets with turn restrictions and one-way streets, the migration effort outweighs the benefit.

2

Complex Raster Processing Chains

Multi-step raster workflows using Spatial Analyst - weighted overlay, cost distance, viewshed chaining. Replicating these in rasterio requires significantly more code and extensive testing. If the chain works and runs infrequently, leave it.

3

Cartographic Production

Map layouts, symbology, annotation. ArcGIS Pro's cartographic engine has no open-source equivalent at the same quality level. QGIS covers basic map production, but enterprise cartographic output still favours ArcGIS Pro.

4

Utility Network Integration

If your workflows touch Utility Network data, there is no migration path. Full stop. The Utility Network data model is proprietary to ArcGIS. This is not a technical limitation that will be solved - it's a vendor lock-in by design.

5

When the Team Pushes Back

Migration requires buy-in. An analyst forced to use unfamiliar tools is less productive than one using familiar tools willingly. Address resistance through training and gradual adoption, not mandates. Start with the analysts who are interested and let results speak.

6

When Migration Cost Exceeds 2 Years of Licence Savings

Do the arithmetic. If migrating 30 scripts costs $50K in engineering time and your annual ArcPy licence cost is $15K, the payback is 3.3 years. Technology changes in 3.3 years. Team priorities shift. Long payback periods carry execution risk that rarely shows up in the business case spreadsheet.

Migration Timeline

For a typical 20-script inventory, this is the phase-by-phase timeline. Adjust durations based on your team size and script complexity.

Migration timeline phases from inventory through parallel testing to switch-over

MIGRATION PHASES

01

Inventory and Categorise

1 week

Find all scripts, categorise as Green/Yellow/Red/Grey. Document dependencies between scripts. Interview analysts about each workflow.

02

Migrate Green Scripts

2-3 weeks

Start with the easiest wins. Build confidence and establish patterns. Each Green script takes 2-8 hours. This phase also trains the team on GeoPandas idioms.

03

Migrate Yellow Scripts

2-3 weeks

Medium complexity. Requires refactoring arcpy.env patterns, cursor replacements, and field calculation logic. Each Yellow script takes 4-16 hours.

04

Parallel Testing

2 weeks minimum

Run old and new scripts on the same data. Compare every output using the comparison script. Fix discrepancies. Zero failures for 2 consecutive weeks before proceeding.

05

Red Scripts Decision

1 week

For each Red script: find a workaround (pgRouting, GRASS GIS), keep it in ArcPy, or redesign the workflow entirely. Document the decision and rationale.

06

Switch Over

1 week

Decommission old ArcPy scripts. Update documentation. Conduct team training sessions. Set up monitoring for the new workflows.

TOTAL TIMELINE

8-12weeks for a 20-script inventory

Larger inventories (40+ scripts) take 16-20 weeks. Smaller ones (10 scripts) can complete in 5-6 weeks. The parallel testing phase is non-negotiable regardless of inventory size.

Frequently Asked Questions

Can I migrate ArcPy scripts to open source?

Yes, approximately 80% of ArcPy functions have direct open-source equivalents in GeoPandas, rasterio, and Shapely. About 12 functions - mainly Network Analyst, 3D Analyst, and Utility Network - have no direct equivalent and require workarounds or must remain in ArcPy.

How long does it take to migrate from ArcPy?

For a typical 20-script inventory, expect 8-12 weeks: inventory (1 week), easy scripts (2-3 weeks), medium scripts (2-3 weeks), parallel testing (2 weeks), and switch-over (1 week). Individual scripts take 2-16 hours depending on complexity.

What is the best replacement for ArcPy?

GeoPandas is the closest equivalent for vector analysis (buffer, intersect, dissolve, spatial join). rasterio replaces Spatial Analyst for raster operations. Shapely handles geometry operations. For network analysis, pgRouting (PostGIS) or NetworkX are alternatives. No single library replaces all of ArcPy.

Migration is a file-by-file process, not a big-bang switch. Start with the Green scripts, build confidence, and let the parallel testing results justify the next phase.

The function mapping table and 12-gap analysis give you the honest picture upfront. No surprises mid-project. The scripts that can't migrate stay in ArcPy - that's engineering pragmatism, not failure.

If you're starting from the business case, Part 1 covers the ROI analysis. For a deep dive into individual function translations with benchmarks, see our ArcPy to GeoPandas guide and the side-by-side benchmark comparison.

Get Workflow Automation Insights

Monthly tips on automating GIS workflows, open-source tools, and lessons from enterprise deployments. No spam.

SKIP THE MANUAL MIGRATION

Everything in This Guide - Automated

The Axis Spatial platform automates every step of this playbook: inventory scanning, Green/Yellow/Red categorisation, function-by-function translation using the mapping table above, and parallel output comparison. Your team defines the migration priorities; the AI agents do the rewriting, running both versions simultaneously and flagging any discrepancies before you touch a production workflow. No weeks of manual rewriting.

NEXT STEP

Get Your Migration Assessment

We'll inventory your scripts, categorise them by migration complexity, and give you a realistic timeline and cost estimate. Most assessments complete within a week.