Download ArcPy and ArcGIS – Geospatial Analysis with Python

Document related concepts
no text concepts found
Transcript
ArcPyandArcGIS–GeospatialAnalysis
withPython
TableofContents
ArcPyandArcGIS–GeospatialAnalysiswithPython
Credits
AbouttheAuthor
AbouttheReviewers
www.PacktPub.com
Supportfiles,eBooks,discountoffers,andmore
Whysubscribe?
FreeaccessforPacktaccountholders
Preface
Whatthisbookcovers
Whatyouneedforthisbook
Whothisbookisfor
Conventions
Readerfeedback
Customersupport
Downloadingtheexamplecode
Downloadingthecolorimagesofthisbook
Errata
Piracy
Questions
1.IntroductiontoPythonforArcGIS
OverviewofPython
Pythonasaprogramminglanguage
Interpretedlanguage
Standard(built-in)library
Thegluelanguage
Wrappermodules
ThebasicsofPython
Importstatements
Variables
Forloops
If/Elif/Elsestatements
Whilestatements
Comments
Datatypes
Strings
Integers
Floats
Lists
Tuples
Dictionaries
Iterabledatatypes
Otherimportantconcepts
Indentation
Functions
Keywords
Namespaces
Zero-basedindexing
ImportantPythonModulesforGISAnalysis
TheArcPymodule
TheOperatingSystem(OS)module
ThePythonSystem(SYS)module
TheXLRDandXLWTmodules
Commonlyusedbuilt-infunctions
Commonlyusedstandardlibrarymodules
Summary
2.ConfiguringthePythonEnvironment
WhatisaPythonscript?
HowPythonexecutesascript
WhatisthePythoninterpreter?
WhereisthePythoninterpreterlocated?
WhichPythoninterpretershouldbeused?
Howdoesthecomputerknowwheretheinterpreteris?
MakePythonscriptsexecutablewhenclickedon
IntegratedDevelopmentEnvironments(IDEs)
IDLE
PythonWin
AptanaStudio3
IDEsummary
Pythonfolderstructure
Wheremodulesreside
UsingPython’ssysmoduletoaddamodule
Thesys.path.append()method
Summary
3.CreatingtheFirstPythonScript
Prerequisites
ModelBuilder
CreatingamodelandexportingtoPython
ModelingtheSelectandBuffertools
AddingtheIntersecttool
Tallyingtheanalysisresults
Exportingthemodelandadjustingthescript
Theautomaticallygeneratedscript
FilepathsinPython
Continuingthescriptanalysis:theArcPytools
TheIntersecttoolandstringmanipulation
Thestringmanipulationmethod1–stringaddition
Thestringmanipulationmethod2–stringformatting#1
Thestringmanipulationmethod3–stringformatting#2
AdjustingtheScript
AddingtheCSVmoduletothescript
Accessingthedata:Usingacursor
Thefinalscript
Summary
4.ComplexArcPyScriptsandGeneralizingFunctions
Pythonfunctions–Avoidrepeatingcode
Technicaldefinitionoffunctions
Afirstfunction
Functionswithparameters
Usingfunctionstoreplacerepetitivecode
Moregeneralizationofthefunctions
Summary
5.ArcPyCursors–Search,Insert,andUpdate
Thedataaccessmodule
Attributefieldinteractions
Updatecursors
Updatingtheshapefield
Adjustingapointlocation
DeletingarowusinganUpdateCursor
UsinganInsertCursor
Insertingapolylinegeometry
Insertingapolygongeometry
Summary
6.WorkingwithArcPyGeometryObjects
ArcPygeometryobjectclasses
ArcPyPointobjects
ArcPyArrayobjects
ArcPyPolylineobjects
ArcPyPolygonobjects
Polygonobjectbuffers
OtherPolygonobjectmethods
ArcPygeometryobjects
ArcPyPointGeometryobjects
Summary
7.CreatingaScriptTool
Addingdynamicparameterstoascript
Displayingscriptmessagesusingarcpy.AddMessage
Addingdynamiccomponentstothescript
CreatingaScripttool
Labellinganddefiningparameters
Addingdatatypes
AddingtheBusStopfeatureclassasaparameter
AddingtheCensusBlockfeatureclassasaparameter
AddingtheCensusBlockfieldasaparameter
Addingtheoutputspreadsheetasaparameter
Addingthespreadsheetfieldnamesasaparameter
AddingtheSQLStatementasaparameter
Addingthebusstopfieldsasaparameter
Inspectingthefinalscript
RunningtheScriptTool
Summary
8.IntroductiontoArcPy.Mapping
UsingArcPywithmapdocuments
Inspectingandreplacinglayersources
Fixingthebrokenlinks
Fixingthelinksofindividuallayers
ExportingtoPDFfromanMXD
Adjustingmapdocumentelements
Automatedmapdocumentadjustment
Thevariables
Themapdocumentobjectandthetextelements
Thelayerobjects
Replacingthedatasources
Adjustinglayervisibility
Generatingabufferfromthebusstopsfeatureclass
Intersectingthebusstopbufferandcensusblocks
Populatingtheselectedbusstopandbufferfeatureclasses
Updatingthetextelements
ExportingtheadjustedmaptoPDF
RunningthescriptinthePythonWindow
Summary
9.MoreArcPy.MappingTechniques
Usingarcpy.mappingtocontrolLayerobjects
Layerobjectmethodsandproperties
Definitionqueries
Controllingthedataframewindowextentandscale
AddingaLayerobject
Exportingthemaps
Summary
10.AdvancedGeometryObjectMethods
CreatingaPythonmodule
The__init__.pyfile
Addingadvancedanalysiscomponents
AdvancedPolygonobjectmethods
Generatingrandompointstorepresentpopulation
Usingthefunctionswithinascript
CreatinganXLSusingXLWT
Summary
11.NetworkAnalystandSpatialAnalystwithArcPy
TheNetworkAnalystextension
UsingNetworkAnalyst
CreatingaFeatureDataset
Importingthedatasets
CreatingtheNetworkDataset
AccessingtheNetworkDatasetusingArcPy
Breakingdownthescript
TheNetworkAnalystmodule
AccessingtheSpatialAnalystExtension
Addingelevationtothebusstops
UsingMapAlgebratogenerateelevationinfeet
Addinginthebusstopsandgettingelevationvalues
Thefinalresult
Summary
12.TheEndoftheBeginning
Gettingfieldinformationfromfeatureclasses
AccessingtheListFields’properties
Listcomprehensions
Creatingthefieldinformationfunctions
Queryingfeatureclassinformation
GeneratingFileGeodatabasesandfeatureclasses
Generatingafeatureclass
Settingupthescripttoolparameters
Environmentalsettings
Resolutionandtolerancesettings
Summary
Index
ArcPyandArcGIS–GeospatialAnalysis
withPython
ArcPyandArcGIS–GeospatialAnalysis
withPython
Copyright©2015PacktPublishing
Allrightsreserved.Nopartofthisbookmaybereproduced,storedinaretrievalsystem,
ortransmittedinanyformorbyanymeans,withoutthepriorwrittenpermissionofthe
publisher,exceptinthecaseofbriefquotationsembeddedincriticalarticlesorreviews.
Everyefforthasbeenmadeinthepreparationofthisbooktoensuretheaccuracyofthe
informationpresented.However,theinformationcontainedinthisbookissoldwithout
warranty,eitherexpressorimplied.Neithertheauthor,norPacktPublishing,andits
dealersanddistributorswillbeheldliableforanydamagescausedorallegedtobecaused
directlyorindirectlybythisbook.
PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthe
companiesandproductsmentionedinthisbookbytheappropriateuseofcapitals.
However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.
Firstpublished:February2015
Productionreference:1210215
PublishedbyPacktPublishingLtd.
LiveryPlace
35LiveryStreet
BirminghamB32PB,UK.
ISBN978-1-78398-866-2
www.packtpub.com
Credits
Author
SilasToms
Reviewers
AlessioDiLorenzo
DaraO’Beirne
MarkPazolli
MarjorieRoswell
CommissioningEditor
AshwinNair
AcquisitionEditor
HarshaBharwani
ContentDevelopmentEditor
AkashdeepKundu
TechnicalEditor
DeeptiTuscano
CopyEditors
AartiSaldanha
AdithiShetty
ProjectCoordinator
MiltonDsouza
Proofreaders
SimranBhogal
JoannaMcMahon
BernadetteWatkins
Indexer
PriyaSane
ProductionCoordinator
AlwinRoy
CoverWork
AlwinRoy
AbouttheAuthor
SilasTomsisageospatialprogrammerandanalystwithaloveofgeography,history,
food,andsports.HeresidesintheSanFranciscoBayAreaandcan’tdecidewhichsideof
theBayismorebeautiful.Hereceivedabachelor’sdegreeinGeographyfromHumboldt
StateUniversityandiscurrentlypursuingamaster’sdegreeinGISatSanFranciscoState
University.WithabackgroundinGISanalysisforcitygovernmentsandenvironmental
consulting,SilaslovesthecombinationofGISandPythonforanalysisautomationand
datamanipulation.
WorkingforAriniGeographics,SilasishelpinggovernmentsunderstandhowGIScan
organizeandsimplifythemanagementofinfrastructureandtheenvironment.Thisdual
roleasaprogrammerandanalystallowshimtousePythonandGIStoquicklyproduce
geospatialdataandtools.Combinedwithwebmapping,thesetoolsaretransforminghow
governmentsworktoservethepublic.HealsoteachesworkshopsonArcPyandweb
mappingattheCityCollegeofSanFrancisco,whilehopingtoonedayfinishhismaster’s
thesis.
SilashasworkedasarevieweronthebookPythonGeospatialAnalysis,PacktPublishing
andisworkingonthebookPythonGeospatialDevelopment,PacktPublishingtobe
publishedin2015.
Iwouldliketothankmygirlfriend,Christine,forherencouragementandpatience.I
wouldliketothankmyboss,GabrielPaun,forhisinspirationandforpushingmeto
becomeatrueGISprofessional.IwouldliketothankthefacultyatHSUandSFSUfor
theirhelpalongtheway,andIwouldliketothankmyfamilyfortheirbeliefinmeandfor
neveraskingmeifIwasgoingtobecomeateacherwithmygeographydegree(even
thoughIhaveandIloveit!).
AbouttheReviewers
AlessioDiLorenzoisamarinebiologistandhasanMScinGeographicalInformation
Systems(GIS)andRemoteSensing.Since2006,hehasbeendealingwiththeanalysisand
developmentofGISapplicationsdedicatedtothestudyandspreadofenvironmentaland
epidemiologicaldata.Heisexperiencedintheuseofthemainproprietaryandopensource
GISsoftwareandprogramminglanguages.
DaraO’BeirneisacertifiedGISProfessional(GISP)withovereightyearsofGISand
Pythonexperience.DaraearnedbothhisBachelorsandMastersofArtsdegreesin
geographyfromSanFranciscoStateUniversity.DaraiscurrentlyaGISAnalystworking
atAriniGeographicsinSantaClara,CA.BeforejoiningAriniGeographics,Darawasa
GISAnalystandtechnicalleadatTowillInc.,aGISandLandSurveyingcompanyin
NorthernCalifornia.AtTowill,Daraplayedacentralroleindevelopingandimplementing
proceduresrelatedtothecollectionandanalysisofLiDARdataforenvironmentaland
engineeringapplications.PriortoTowill,DaragainedhisprofessionalGISexperience
workingfortheGoldenGateNationalRecreationAreamanagedbytheNationalPark
Service,oneofthelargesturbanparksystemsintheworld,whichincludesNational
treasures,suchasAlcatraz,MuirWoods,andtheMarinHeadlands.HisMaster’sThesis
examinedtheerrorsassociatedwithmeasuringtreeheightsinanurbanenvironmentwith
bothtraditionalfieldmethodsandairborneLiDARdata.
Iwouldliketothankmywife,Kate,anddaughter,AnyaO’Beirne,fortheirpatienceand
assistanceduringthereviewofthisbook.
MarjorieRoswellisawebdeveloperandmapmakerfromBaltimore,MD.She
purchasedherfirstGISin1991,andbuiltanapplicationtoassistcitizencallerstothe
BaltimoreOfficeofRecycling.Recentprojectsincludeinteractivemapsoflegislative
scores,politicalendorsements,committees,electiondata,andadvocacyinterests.
Hersitehttp://committeemaps.org/detailsCongressionalcommitteemembership,while
thesitehttp://farmbillprimer.org/isdevotedtomappingandchartingfederalfoodand
farmpolicy.
MarjorieistheauthorofDrupal5ViewsRecipes,PacktPublishing.Shewasthetechnical
reviewerofjQueryUI1.10,TheUserInterfaceLibraryforjQuery,PacktPublishing.
MarkPazolliisanengineeranddatascientistwhousesArcGISandPythontohelphis
employersdecipherthemountainsofdatatheykeepontheassetsoftheWestern
Australianelectricalnetwork.HehasqualificationsinElectricalEngineering,Computer
Science,andAppliedMathematics.Heappreciatesexcellentdesignandenjoysbuilding
interestingthings.
www.PacktPub.com
Supportfiles,eBooks,discountoffers,and
more
Forsupportfilesanddownloadsrelatedtoyourbook,pleasevisitwww.PacktPub.com.
DidyouknowthatPacktofferseBookversionsofeverybookpublished,withPDFand
ePubfilesavailable?YoucanupgradetotheeBookversionatwww.PacktPub.comandas
aprintbookcustomer,youareentitledtoadiscountontheeBookcopy.Getintouchwith
usat<[email protected]>formoredetails.
Atwww.PacktPub.com,youcanalsoreadacollectionoffreetechnicalarticles,signup
forarangeoffreenewslettersandreceiveexclusivediscountsandoffersonPacktbooks
andeBooks.
https://www2.packtpub.com/books/subscription/packtlib
DoyouneedinstantsolutionstoyourITquestions?PacktLibisPackt’sonlinedigital
booklibrary.Here,youcansearch,access,andreadPackt’sentirelibraryofbooks.
Whysubscribe?
FullysearchableacrosseverybookpublishedbyPackt
Copyandpaste,print,andbookmarkcontent
Ondemandandaccessibleviaawebbrowser
FreeaccessforPacktaccountholders
IfyouhaveanaccountwithPacktatwww.PacktPub.com,youcanusethistoaccess
PacktLibtodayandview9entirelyfreebooks.Simplyuseyourlogincredentialsfor
immediateaccess.
Preface
ArcGIS,theGISsoftwarefromindustryleaderESRI,allowsfortheanalysisand
presentationofgeospatialdata.
TheintegrationofPythonintoArcGIShasmadetheArcPymoduleanimportanttoolfor
GISstudentsandprofessionals.TheArcPymoduleprovidesapowerfulwaytoimprove
productivitywhenperforminggeospatialanalysis.FrombasicPythonscriptingthrough
advancedArcPymethodsandproperties,ArcPyandotherPythonmoduleswillimprove
thespeedandrepeatabilityofanyGISworkflow.
ThisbookwillguideyoufrombasicPythonscriptingtoadvancedscriptingtools.It
focusesongeospatialanalysisscriptingandtouchesonautomatingcartographicoutput.
Bytheendofthisbook,youwillbeabletocreatereusablemodules,addrepeatable
analysesasscripttoolsinArcToolbox,andexportmapsautomatically.Byreducingthe
time-consumingnatureofGISfromdaystohours,oneGISprofessionalcanbecomeas
powerfulasawholeteam.
Whatthisbookcovers
Chapter1,IntroductiontoPythonforArcGIS,offersaquickintroductiontothebasicsof
Python,includingotherusesfortheprogramminglanguage.ItcoversPythondatatypes
andimportantmodulesusedthroughoutthebook.
Chapter2,ConfiguringthePythonEnvironment,isanintroductiontohowPythonworks:
itsfolderstructure,executables,andmodules.Italsoexplainsimportingmodulesinto
scripts,thebuilt-inmodules,andcoversIntegratedDevelopmentEnvironments(IDEs),
whicharepowerfulprogrammingaids.
Chapter3,CreatingtheFirstPythonScript,demonstrateshowtouseArcGIS
ModelBuildertomodelthefirstanalysisandthenexportitasaPythonscript.String
manipulationsandhowtousefilepathsinPythonarealsointroduced.
Chapter4,ComplexArcPyScriptsandGeneralizingFunctions,examineshowtoperform
analysesandproduceoutputsthatarenotpossibleusingModelBuilder.Byusing
functions,orreusablecodeblocks,repeatingcodeisavoided.
Chapter5,ArcPyCursors–Search,Insert,andUpdate,coversArcPydataaccesscursors
andhowtheyareusedtosearch,update,orinsertrecordsinfeatureclassesandtables.It
explainsthequirksofiteratingusingcursors,andhowtoonlyselectorupdatetherecords
ofinterest.
Chapter6,WorkingwithArcPyGeometryObjects,exploresArcPyGeometryobjectsand
howtheyarecombinedwithcursorstoperformspatialanalysis.Itdemonstrateshowto
buffer,clip,reproject,andmoreusingthedatacursorsandtheArcpygeometrytypes
withoutusingArcToolbox.
Chapter7,CreatingaScriptTool,explainshowtomakescriptsintotoolsthatappearin
ArcToolboxandaredynamicinnature.Itexplainshowthetoolsandscriptscommunicate
andhowtosetuptheArcTooldialogtocorrectlypassparameterstothescript.
Chapter8,IntroductiontoArcPy.Mapping,exploresthepowerfulArcpy.Mappingmodule
andhowtofixbrokenlayerlinks,turnlayersonandoff,anddynamicallyadjusttitlesand
text.Itshowshowtocreatedynamicmapoutputbasedonageospatialanalysis.
Chapter9,MoreArcPy.MappingTechniques,introducesLayerobjects,andtheirmethods
andproperties.Itdemonstrateshowtocontrolmapscalesandextentsfordataframes,and
coversautomatedmapexport.
Chapter10,AdvancedGeometryObjectMethods,expandsontheArcPyGeometryobject
methodsandproperties.Italsoexplainshowtocreateamoduletosavecodeforreusein
subsequentscripts,anddemonstrateshowtocreateExcelspreadsheetscontainingresults
fromageospatialanalysis.
Chapter11,NetworkAnalystandSpatialAnalystwithArcPy,introducesthebasicsof
usingArcPyforadvancedgeospatialanalysisusingtheArcGISforDesktopNetwork
AnalystandSpatialAnalystExtensions.
Chapter12,TheEndoftheBeginning,coversotherimportanttopicsthatneedtobe
understoodtohaveafullgraspofArcPy.ThesetopicsincludetheEnvironmentSettings,
XYvaluesandZandMresolutions,SpatialReferenceSystems(Projections),theDescribe
functions,andmore.
Whatyouneedforthisbook
YouwillneedtheproprietaryorfreeversionofArcGIS10.1/10.2/10.3.Tosupportyour
environment,youwillneed2GBRAM,32-bitor64bitmachinehardwareconfiguration,
andWindows7/8.Python2.7isrequiredtodotheprogrammingandisinstalledalong
withArcGIS.
Whothisbookisfor
ThisbookisintendedforGISstudentsandprofessionalswhoneedanunderstandingof
howtouseArcPytoreducerepetitivetasksandperformanalysisfaster.Itisalsoa
valuablebookforPythonprogrammerswhowouldliketounderstandhowtoautomate
geospatialanalysisusingtheindustrystandardArcGISsoftwareanditsArcPymodule.
Conventions
Inthisbook,youwillfindanumberofstylesoftextthatdistinguishbetweendifferent
kindsofinformation.Herearesomeexamplesofthesestyles,andanexplanationoftheir
meaning.
Codewordsintext,databasetablenames,foldernames,filenames,fileextensions,
pathnames,dummyURLs,userinput,andTwitterhandlesareshownasfollows:“Thetwo
datapieces,theBusStopIDandtheaveratePopvariablearethenaddedtoalist.”
Ablockofcodeissetasfollows:
witharcpy.da.SearchCursor(Intersect71Census,["STOPID","POP10"])as
cursor:
forrowincursor:
busStopID=row[0]
pop10=row[1]
ifbusStopIDnotindataDictionary.keys():
dataDictionary[busStopID]=[pop10]
else:
dataDictionary[busStopID].append(pop10)
Anycommand-lineinputoroutputiswrittenasfollows:
>>>aString="Thisisastring"
>>>bString="andthisisanotherstring"
>>>aString+bString
Newtermsandimportantwordsareshowninbold.Wordsthatyouseeonthescreen,in
menusordialogboxesforexample,appearinthetextlikethis:“Selectitbyclickingonit,
andthenclickingontheEditbutton.”
Note
Warningsorimportantnotesappearinaboxlikethis.
Tip
Tipsandtricksappearlikethis.
Readerfeedback
Feedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthis
book—whatyoulikedormayhavedisliked.Readerfeedbackisimportantforusto
developtitlesthatyoureallygetthemostoutof.
Tosendusgeneralfeedback,simplysendane-mailto<[email protected]>,and
mentionthebooktitleviathesubjectofyourmessage.
Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingor
contributingtoabook,seeourauthorguideonwww.packtpub.com/authors.
Customersupport
NowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelp
youtogetthemostfromyourpurchase.
Downloadingtheexamplecode
YoucandownloadtheexamplecodefilesforallPacktbooksyouhavepurchasedfrom
youraccountathttp://www.packtpub.com.Ifyoupurchasedthisbookelsewhere,youcan
visithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlyto
you.
Downloadingthecolorimagesofthisbook
WealsoprovideyouwithaPDFfilethathascolorimagesofthescreenshots/diagrams
usedinthisbook.Thecolorimageswillhelpyoubetterunderstandthechangesinthe
output.Youcandownloadthisfilefrom
http://www.packtpub.com/sites/default/files/downloads/8662OS_ColorImages.pdf.
Errata
Althoughwehavetakeneverycaretoensuretheaccuracyofourcontent,mistakesdo
happen.Ifyoufindamistakeinoneofourbooks—maybeamistakeinthetextorthe
code—wewouldbegratefulifyoucouldreportthistous.Bydoingso,youcansaveother
readersfromfrustrationandhelpusimprovesubsequentversionsofthisbook.Ifyoufind
anyerrata,pleasereportthembyvisitinghttp://www.packtpub.com/submit-errata,
selectingyourbook,clickingontheErrataSubmissionFormlink,andenteringthe
detailsofyourerrata.Onceyourerrataareverified,yoursubmissionwillbeacceptedand
theerratawillbeuploadedtoourwebsiteoraddedtoanylistofexistingerrataunderthe
Erratasectionofthattitle.
Toviewthepreviouslysubmittederrata,goto
https://www.packtpub.com/books/content/supportandenterthenameofthebookinthe
searchfield.TherequiredinformationwillappearundertheErratasection.
Piracy
PiracyofcopyrightmaterialontheInternetisanongoingproblemacrossallmedia.At
Packt,wetaketheprotectionofourcopyrightandlicensesveryseriously.Ifyoucome
acrossanyillegalcopiesofourworks,inanyform,ontheInternet,pleaseprovideuswith
thelocationaddressorwebsitenameimmediatelysothatwecanpursuearemedy.
Pleasecontactusat<[email protected]>withalinktothesuspectedpirated
material.
Weappreciateyourhelpinprotectingourauthors,andourabilitytobringyouvaluable
content.
Questions
Youcancontactusat<[email protected]>ifyouarehavingaproblemwithany
aspectofthebook,andwewilldoourbesttoaddressit.
Chapter1.IntroductiontoPythonfor
ArcGIS
Inthischapter,wewilldiscussthedevelopmentofPythonasaprogramminglanguage,
fromitsbeginninginthelate1980stoitscurrentstate.Wewilldiscussthephilosophyof
designthatspurreditsdevelopment,andtouchonimportantmodulesthatwillbeused
throughoutthebook,especiallyfocusingonthemodulesbuiltintothePythonstandard
library.Thisoverviewofthelanguageanditsfeatureswillhelpexplainwhatmakes
PythonagreatlanguageforArcGISautomation.
Thischapterwillcover:
AquickoverviewofPython:Whatitisanddoes,whocreatedit,andwhereitisnow
TheArcPymoduleandotherimportantmodules
Pythonasageneralpurposeprogramminglanguage
OverviewofPython
Python,createdbyGuidovanRossumin1989,wasnamedafterhisfavoritecomedy
troupe,MontyPython.Hisworkgroupatthetimehadatraditionofnamingprograms
afterTVshows,andhewantedsomethingirreverentanddifferentfromitspredecessorsABC,Pascal,Ada,Eiffel,FORTRAN,andothers.SohesettledonPython,feelingitwasa
bitedgyandcatchyaswell.It’scertainlymorefuntosaythanC,thelanguageonwhich
Pythonisbased.
Today,Pythonisamajorprogramminglanguage.Itisusedinwebdevelopment,database
administration,andeventoprogramrobots.MostimportantlytoGISAnalysts,Pythoncan
beusedtocontrolArcGIStoolsandMapDocumentstoproducegeospatialdataandmaps
inanorganizedandspeedymannerusingtheexcellentArcPymodule.
ArcPyisinstalledwithArcGISfordesktopandArcGISforserver.ArcPyhasbeenthe
officialArcGISscriptinglanguagesinceArcGIS10.0andhassteadilyimprovedin
functionalityandimplementation.ThisbookwilltargetArcGISforDesktop10.1and
later,andwilldemonstratehowtomakeuseofPythonanditspowerfulprogramming
libraries(ormodules)whencraftingcomplexgeospatialanalyses.
Pythonasaprogramminglanguage
Overthepast40years,programminglanguageshavedevelopedfromassemblyand
machinecodetowardshigh-levelabstractedlanguagesthataremuchclosertoEnglish.
ThePythonprogramminglanguagewasdesignedtoovercomemanyissuesthat
programmerswerecomplainingaboutinthe1980s:slowdevelopmenttime,overly
complicatedsyntax,andhorriblereadability.VanRossumwantedtodevelopalanguage
thatcouldenablerapidcodedevelopmentandtesting,havesimpleoratleastreadable)
syntax,andproduceresultswithfewerlinesofcode,inlesstime.Thefirstversionof
Python(0.9.0)wasreleasedin1991andwasfreelyobtainablefromthestart;Pythonwas
opensourcebeforethetermopensourcewasinvented.
Interpretedlanguage
Pythonisaninterpretedlanguage.ItiswritteninC,acompiledlanguage,andthecodeis
interpretedfromPythonintoCbeforeitisexecuted.Practically,thismeansthatthecode
isexecutedassoonasitisconvertedandcompiled.Whilecodeinterpretationcanhave
speedimplicationsfortheexecutionofPython-basedprograms,thefasterdevelopment
timeallowedbyPythonmakesthisdrawbackeasytoignore.Testingofcodesnippetsis
muchfasterinaninterpretiveenvironment,anditisperfecttocreatescriptstoautomate
basic,repeatablecomputingtasks.Pythonscriptshavethe.pyextentions.Oncethecode
hasbeeninterpreted,asecondPythonscript(withthe.pycextentions)isgeneratedto
savethecompiledcode.The.pycscriptwillbeautomaticallyrecompiledwhenchanges
aremadeintheoriginal.pyscript.
Standard(built-in)library
Python,wheninstalled,hasabasicsetoffunctionalitythatisreferredtoasthestandard
library.ThesetoolsallowPythontoperformstringmanipulations,mathcomputations,and
HTTPcallsandURLparsing,alongwithmanyotherfunctions.Someofthetoollibraries,
knowntoPythonprogrammersasmodules,arebuilt-inandavailableassoonasPythonis
started,whileothersmustbeexplicitlycalledusingtheimportkeywordtomaketheir
functionsandclassesavailable.Othermoduleshavebeendevelopedbythirdpartiesand
canbedownloadedandinstalledontothePythoninstallationasneeded.
ManynewprogrammerswonderifPythonisarealprogramminglanguage,whichisa
loadedquestion.Theanswerisyes;Pythoncanbeusedtocreatecompleteprograms,
buildwebsites,runcomputernetworks,andmuchmore.Thebuilt-inmodulesandadd-on
modulesmakePythonverypowerful,anditcanbe(andhasbeen)usedfornearlyanypart
ofacomputer—operatingsystems,databases,webservers,desktopapplications,andso
on.Itisnotalwaysthebestchoiceforthedevelopmentofthesetools,butthathasnot
stoppedprogrammersfromtryingandevensucceeding.
Thegluelanguage
Pythonisatitsbestwhenitisusedasagluelanguage.Thistermdescribestheuseof
Pythontocontrolotherprograms,sendinginputstothemandcollectingoutputs,whichare
thensenttoanotherprogramorwrittentodisk.AnArcGISexamplewouldbetouse
Pythontodownloadzippedshapefilesfromawebsite,unzippingthefiles,processingthe
filesusingArcToolbox,andcompilingtheresultsintoanExcelspreadsheet.Allofthisis
accomplishedusingfreelyavailablemodulesthatareeitherincludedinPython’sstandard
library,oraddedwhenArcGISisinstalled.
Wrappermodules
TheArcPymoduleisawrappermodule.WrappermodulesarecommoninPython,andare
sonamedbecausetheywrapPythonontothetoolswewillneed.Theyallowustouse
PythontointerfacewithotherprogramswritteninCorotherprogramminglanguages,
usingtheApplicationProgrammingInterface(API)ofthoseprograms.Forexample,
wrappersmakeitpossibletoextractdatafromanExcelspreadsheetandtransformorload
thedataintoanotherprogram,suchasArcGIS.Notallmodulesarewrappers;some
modulesarewritteninpurePythonandperformtheiranalysisandcomputationsusingthe
Pythonsyntax.Eitherway,theendresultisthatacomputeranditsprogramsareavailable
tobemanipulatedandcontrolledusingPython.
TheZenofPythonwascreatedtobestraightforward,readable,andsimplified,compared
tootherlanguagesthatexistedpreviously.Thisgoverningphilosophywasorganizedintoa
poembyTimPeters,anearlyPythondevelopercalledtheZenofPython;itisanEaster
egg(ahiddenfeature)includedineveryPythoninstallationandisshownwhenimport
thisistypedinthePythoninterpreter:
TheZenofPython,byTimPeters:
Beautifulisbetterthanugly.
Explicitisbetterthanimplicit.
Simpleisbetterthancomplex.
Complexisbetterthancomplicated.
Flatisbetterthannested.
Sparseisbetterthandense.
Readabilitycounts.
Specialcasesaren'tspecialenoughtobreaktherules.
Althoughpracticalitybeatspurity.
Errorsshouldneverpasssilently.
Unlessexplicitlysilenced.
Inthefaceofambiguity,refusethetemptationtoguess.
Thereshouldbeone--andpreferablyonlyone--obviouswaytodo
Althoughthatwaymaynotbeobviousatfirstunlessyou'reDutch.
Nowisbetterthannever.
Althoughneverisoftenbetterthan*right*now.
Iftheimplementationishardtoexplain,it'sabadidea.
Iftheimplementationiseasytoexplain,itmaybeagoodidea.
Namespacesareonehonkinggreatidea—let'sdomoreofthose!
Note
Gotohttps://www.python.org/doc/humor/formoreinformation.
ThebasicsofPython
Pythonhasanumberoflanguagerequirementsandconventionsthatallowforthecontrol
ofmodulesandstructuringofcode.Thefollowingareanumberofimportantbasic
concepts,whichwillbeusedthroughoutthisbookandwhencraftingscriptsforusewith
geospatialanalyses.
Importstatements
ImportstatementsareusedtoaugmentthepowerofPythonbycallingothermodulesfor
useinthescript.ThesemodulescanbepartofthestandardPythonlibraryofmodules,
suchasthemathmodule(usedtodohighermathematicalcalculations)or,importantly,
ArcPy,whichwillallowustointeractwithArcGIS.
Note
Importstatementscanbelocatedanywherebeforethemoduleisused,butbyconvention,
theyarelocatedatthetopofascript.
Therearethreewaystocreateanimportstatement.Thefirst,andmoststandard,isto
importthewholemoduleasfollows:
importarcpy
Usingthismethod,wecanevenimportmorethanonemoduleonthesameline.The
followingimportsthreemodules:arcpy,os(theoperatingsystemmodule),andsys
(thePythonsystemmodule):
importarcpy,os,sys
Thenextmethodofimportingascriptistoimportaspecificportionofamodule,
insteadofimportingtheentiremodule,usingthefrom<module>import
<submodule>syntax:
fromarcpyimportmapping
ThismethodisusedwhenonlyaportionofthecodefromArcPywillbeneeded;it
hasthepracticaleffectoflimitingtheamountofmemoryusedbythemodulewhenit
iscalled.Wecanalsoimportmultipleportionsofthemoduleinthesamefashion:
fromarcpyimportmapping,da
Thethirdwaytoimportamoduleisthefrom<module>import<submodule>
syntax,butbyusinganasterisktoimportallpartsofthemodule:
fromarcpyimport*
Thislastmethodisstillusedbutitisdiscouragedasitcanhaveunforeseenconsequences.
Forinstance,thenamesofthevariablesinthemodulemightconflictwithanothervariable
inanothermoduleiftheyarenotexplicitlyimported.Forthisreason,itisbesttoavoid
thisthirdmethod.However,lotsofexistingscriptsincludeimportstatementsofthistype
sobeawareoftheseconsequences.
Variables
Variablesareapartofallprogramminglanguages.Theyareusedtoreferencedataand
storeitinmemoryforuselaterinascript.Therearealotofargumentsoverthebest
methodtonamevariables.NostandardhasbeendevelopedforPythonscriptingfor
ArcGIS.Thefollowingaresomebestpracticestousewhennamingvariables.
Makethemdescriptive:Don’tjustnameavariablex;thatvariablewillbeuseless
laterwhenthescriptisreviewedandthereisnowaytoknowwhatitisusedfor,or
why.Theyshouldbelongerratherthanshorter,andshouldhintatthedatathey
referenceoreventhedatatypeoftheobjecttheyreference:
shapefilePath='C:/Data/shapefile.shp'
Usecamelcasetomakethevariablereadable:Camelcaseisatermusedfor
variablesthatstartwithalowercaseletterbuthaveuppercaselettersinthemiddle,
resemblingacamel’shump:
camelCase='thisisastring'
Includethedatatypeinthevariablename:Ifthevariablecontainsastring,callit
variableString.Thisisnotrequired,andwillnotbeuseddogmaticallyinthisbook,
butitcanhelporganizethescriptandishelpfulforotherswhowillreadthesescripts.
Pythonisdynamicallytypedinsteadofstatically.Aprogramminglanguage
distinctionmeansthatavariabledoesnothavetobedeclaredbeforeitcanbeused,
unlikeVisualBasicorotherstaticallytypedlanguages.Thisimprovesthespeedof
writingascript,butitcanbeproblematicinlongscriptsasthedatatypeofavariable
willnotbeobvious.
Note
TheArcGISdoesnotusecamelcasewhenitexportsPythonscripts,andmanyexamples
willnotincludeit;nevertheless,itisrecommendedwhenwritingnewscripts.Also,
variablescannotstartwithanumber.
Forloops
Builtintoprogramminglanguagesistheabilitytoiterate,orperformarepeatingprocess,
overadatasettotransformorextractdatathatmeetsspecificcriteria.Python’smain
iterationtoolisknownasaforloop.Thetermforloopmeansthatanoperationwillloop,
oriterate,overtheitemsinadatasettoperformtheoperationoneachitem.Thedataset
mustbeiterabletobeusedinaforloop,adistinctiondiscussedfurtherahead.
Wewillbeusingforloopsthroughoutthisbook.Hereisasimpleexamplethatusesthe
PythonInterpretertotakestringvaluesandprinttheminanuppercaseformat,usingafor
loop:
>>>newlist=['a','b','c','d']
>>>foriteminnewlist:
printitem.upper()
Theoutputisshownasfollows:
A
B
C
D
Thevariableitemisagenericvariableassignedtoeachobjectasitisenteredintothefor
loop,andnotatermrequiredbyPython.Itcouldhavebeenxorvalueinstead.Withinthe
loop,thefirstobject(a)isassignedtothegenericvariableitemandhastheupperstring
functionappliedtoittoproducetheoutputA.Oncethisactionhasbeenperformed,the
nextobject(b)isassignedtothegenericvariabletoproduceanoutput.Thisloopis
repeatedforallmembersofthedatasetnewlist;oncecompleted,thevariableitemwillstill
carrythevalueofthelastmemberofthedataset(dinthiscase).
Tip
Downloadingtheexamplecode
YoucandownloadtheexamplecodefilesforallPacktbooksyouhavepurchasedfrom
youraccountathttp://www.packtpub.com.Ifyoupurchasedthisbookelsewhere,youcan
visithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlyto
you.
If/Elif/Elsestatements
Conditionalstatements,calledif/elsestatementsinPython,arealsostandardin
programminglanguages.Theyareusedwhenevaluatingdata;whencertainconditionsare
met,oneactionwillbetaken(theinitialifstatement;ifanotherconditionismet,another
actionistaken;thisisanelifstatement),andifthedatadoesnotmeetthecondition,a
finalactionisassignedtodealwiththosecases(theelsestatement).Thesearesimilartoa
whereconditionalinaSQLstatementusedwiththeSelecttoolinArcToolbox.Hereisan
exampleofhowtouseanif/elsestatementtoevaluatedatainalist(adatatypediscussed
furtherahead)andfindtheremainderwhendividedusingthemodulusoperator(%)and
Python’sisequaltooperator(==):
>>>data=[1,2,4,5,6,7,10]
>>>forvalindata:
ifval%2==0:
printval,"noremainder"
elifval%3==2:
printval,"remainderoftwo"
else:
print"finalcase"
Theoutputisshownasfollows:
finalcase
2noremainder
4noremainder
5remainderoftwo
6noremainder
finalcase
10noremainder
Whilestatements
Anotherimportantevaluationtoolisthewhilestatement.Itisusedtoperformanaction
whileaconditionistrue;whentheconditionisfalse,theevaluationwillstop.Notethat
theconditionmustbecomefalse,ortheactionwillbealwaysperformed,creatingan
infiniteloopthatwillnotstopuntilthePythoninterpreterisshutoffexternally.Hereisan
exampleofusingawhilelooptoperformanactionuntilatrueconditionbecomesfalse:
>>>x=0
>>>whilex<5:
printx
x+=1
Theoutputisshownasfollows:
0
1
2
3
4
Comments
CommentsinPythonareusedtoaddnoteswithinascript.Theyaremarkedbyapound
sign,andareignoredbythePythoninterpreterwhenthescriptisrun.Commentsare
usefultoexplainwhatacodeblockdoeswhenitisexecuted,ortoaddhelpfulnotesthat
scriptauthorswouldlikefuturescriptuserstoread:
#Thisisacomment
Whileitisaprogrammingtruismthatgoodcodeiswell-commentedcode,many
programmersskipthisvaluablestep.Also,toomanycommentscanreducetheir
usefulnessandthescript’sreadability.Ifvariablesaredescriptiveenough,andcodeis
well-organized,commentsarelessnecessary;writingthecodeasverboseandaswellorganizedaspossiblewillrequirelesstimetobespentoncomments.
Datatypes
GISusespoints,lines,polygons,coverages,andrasterstostoredata.EachoftheseGIS
datatypescanbeusedindifferentwayswhenperformingananalysisandhavedifferent
attributesandtraits.Python,similartoGIS,hasdatatypesthatorganizedata.Themain
datatypesinPythonarestrings,integers,floats,lists,tuples,anddictionaries.Theyeach
havetheirownattributesandtraits(orproperties),andareusedforspecificpartsofcode
automation.Therearealsobuilt-infunctionsthatallowfordatatypestobeconverted(or
casted)fromonetypetoanother;forinstance,theinteger1canbeconvertedtothestring
1usingthestr()function:
>>>variable=1
>>>newvar=str(variable)
>>>newvar
Theoutputisshownasfollows:
1
Strings
Stringsareusedtocontainanykindofcharacter.Theybeginandendwithquotation
marks,witheithersingleordoublequotesused,thoughthestringmustbeginandendwith
thesametypeofquotationmarks.Withinastring,quotedtextcanappear;itmustusethe
oppositequotationmarkstoavoidconflictingwiththestring.Checkthefollowing
example:
>>>quote='Thisstringcontainsaquote:"Hereisthequote"'
Athirdtypeofstringisalsoemployed,amultiplelinestringthatstartsandendswiththree
singlequotemarks:
>>>multiString='''Thisstringhas
multiplelinesandcangofor
aslongasIwantittoo'''
Integers
Integersarewholenumbersthatdonothaveanydecimalplaces.Thereisaspecial
consequencetotheuseofintegersinmathematicaloperations;ifintegersareusedfor
division,anintegerresultwillbereturned.Checkoutthiscodesnippetbelowtoseean
exampleofthis:
>>>5/2
Theoutputisshownasfollows:
2
Insteadofanaccurateresultof2.5,Pythonwillreturnthefloorvalue,orthelowestwhole
integerforanyintegerdivisioncalculation.Thiscanobviouslybeproblematicandcan
causesmallbugsinscriptsthatcanhavemajorconsequences.
Tip
Pleasebeawareofthisissuewhenwritingscriptsandusefloatstoavoiditasdescribedin
thefollowingsection.
Floats
Floatingpointvalues,orfloats,areusedbyPythontorepresentdecimalvalues.Theuseof
floatswhenperformingdivisionisrecommended:
>>>5.0/2
Theoutputisshownasfollows:
2.5
Becausecomputersstorevaluesinabase2binarysystem,therecanbeissuesrepresenting
afloatingvaluethatwouldnormallyberepresentedinabase10system.Read
docs.python.org/2/tutorial/floatingpoint.htmlforafurtherdiscussionoftheramifications
ofthislimitation.
Lists
Listsareorderedsetsofdatathatarecontainedinsquarebrackets([]).Listscancontain
anyothertypeofdata,includingotherlists.Datatypescanbemixedwithinasinglelist.
Listsalsohaveasetofmethodsthatallowthemtobeextended,reversed,sorted,summed,
orextractthemaximumorminimumvalue,alongwithmanyothermethods.Datapieces
withinalistareseparatedbycommas.
Listmembersarereferencedbytheirindex,orpositioninthelist,andtheindexalways
startsatzero.Lookatthefollowingexampletounderstandthisbetter:
>>>alist=['a','b','c','d']
>>>alist[0]
Theoutputisshownasfollows:
'a'
Thisexampleshowsushowtoextractthefirstvalue(attheindex0)fromthelistcalled
alist.Oncealisthasbeenpopulated,thedatawithinitisreferencedbyitsindex,which
ispassedtothelistinsquarebrackets.Togetthesecondvalueinalist(thevalueatindex
1),thesamemethodisused:
>>>alist[1]
Theoutputisshownasfollows:
'b'
Tomergetwolists,theextendmethodisused:
>>>blist=[2,5,6]
>>>alist.extend(blist)
>>>alist
Theoutputisshownasfollows:
['a','b','c','d',2,5,6]
Tuples
Tuplesarerelatedtolistsandaredenotedbyparentheses(()).Unlikelists,tuplesare
immutable—theycannotbeadjustedorextendedoncetheyhavebeencreated.Datawithin
atupleisreferencedinthesamewayasalist,usingindexreferencesstartingatzero:
>>>atuple=('e','d','k')
>>>atuple[0]
Theoutputisshownasfollows:
'e'
Dictionaries
Dictionariesaredenotedbycurlybrackets({})andareusedtocreatekey:valuepairs.
Thisallowsustomapvaluesfromakeytoavalue,sothatthevaluecanreplacethekey
anddatafromthevaluecanbeusedinprocessing.Hereisasimpleexample:
>>>adic={'key':'value'}
>>>adic['key']
Theoutputisshownasfollows:
'value'
Notethatinsteadofreferringtoanindexposition,suchaslistsortuples,thevaluesare
referencedusingakey.Also,keyscanbeanyothertypeofdataexceptlists(becauselists
aremutable).
Thiscanbeveryvaluablewhenreadingashapefileorfeatureclass.UsinganObjectIDas
akey,thevaluewouldbealistofrowattributesassociatedwithObjectID.Lookatthe
followingexampletobetterunderstandthisbehavior:
>>>objectIDdic={1:['100','Main','St']}
>>>objectIDdic[1]
Theoutputisshownasfollows:
['100','Main','St']
Dictionariesareveryvaluableforreadinginfeatureclassesandeasilyparsingthroughthe
databycallingonlytherowsofinterest,amongotheroperations.Theyaregreatfor
orderingandreorderingdataforuselaterinascript,sobesuretopayattentiontothem
movingforward.
Iterabledatatypes
Lists,tuples,andstringsarealliterabledatatypesthatcanbeusedinforloops.When
enteredintoaforloop,thesedatatypesareoperatedoninorder,unlessotherwise
specified.Forlistsandtuples,thisiseasytounderstand,astheyhaveanobviousorder:
>>>aList=[1,3,5,7]
>>>forvalueinaList:
printvalue*2
Theoutputisshownasfollows:
2
6
10
14
Forstrings,eachcharacterislooped:
>>>aString="esri"
>>>forvalueinaString:
printvalue.upper()
Theoutputisshownasfollows:
E
S
R
I
Dictionariesarealsoiterable,butwithaspecificimplementationthatwillonlyallow
directaccesstothekeysofthedictionary(whichcanthenbeusedtoaccessthevalues).
Also,thekeysarenotreturnedinaspecificorder:
>>>aDict={"key1":"value1",
"key2":"value2"}
>>>forvalueinaDict:
printvalue,aDict[value]
Theoutputisshownasfollows:
key2value2
key1value1
Otherimportantconcepts
TheuseofPythonforprogrammingrequiresanintroductiontoanumberofconceptsthat
areeitheruniquetoPythonbutrequiredorcommonprogrammingconceptsthatwillbe
invokedrepeatedlywhencreatingscripts.Includedfollowingareanumberofthese
conceptsthatmustbecoveredtobefluentinPython.
Indentation
Python,unlikemostotherprogramminglanguages,enforcesstrictrulesonindentinglines
ofcode.ThisconceptisderivedagainfromGuido’sdesiretoproduceclean,readable
code.Whencreatingfunctionsorusingforloops,orif/elsestatements,indentationis
requiredonthesucceedinglinesofcode.Ifaforloopisincludedinsideanif/else
statement,therewillbetwolevelsofindentation.Veteranprogrammersofotherlanguages
havecomplainedaboutthestrictnatureofPython’sindentation.Newprogrammers
generallyfindittobehelpfulasitmakesiteasytoorganizecode.Notethatalotof
programmersnewtoPythonwillcreateanindentationerroratsomepoint,somakesureto
payattentiontotheindentationlevels.
Functions
Functionsareusedtotakecodethatisrepeatedoverandoverwithinascript,oracross
scripts,andmakeformaltoolsoutofthem.Usingthekeyworddef,shortforthedefine
function,functionsarecreatedwithdefinedinputsandoutputs.Theideaofafunctionin
computingisthatittakesdatainonestateandconvertsitintodatainanotherstate,
withoutaffectinganyotherpartofthescript.ThiscanbeveryvaluabletoautomateaGIS
analysis.
Hereisanexampleofafunctionthatreturnsthesquareofanynumbersupplied:
defsquare(inVal):
returninVal**2
>>>square(3)
Theoutputisshownasfollows:
9
Whilethisofcourseduplicatesasimilarfunctionbuiltintothemathmodule,itshowsthe
basicsofafunction.Afunction(generally)acceptsdata,transformsitasneeded,andthen
returnsthenewstateofthedatausingthereturnkeyword.
Keywords
ThereareanumberofkeywordsbuiltintoPythonthatshouldbeavoidedwhennaming
variables.Theseincludemax,min,sum,return,list,tuple,def,del,from,
not,in,as,if,else,elif,or,while,and,with,amongmanyothers.Using
thesekeywordswillresultinanerror.
Namespaces
Namespacesarealogicalwaytoorganizevariablenameswhenavariableinsidea
function(alocalvariable)sharesthesamenameasavariableoutsideofthefunction(a
globalvariable).Localvariablescontainedwithinafunction(eitherinthescriptorwithin
animportedmodule)andglobalvariablescanshareanameaslongastheydonotsharea
namespace.
Thisissueoftenariseswhenavariablewithinanimportedmoduleunexpectedlyhasthe
samenameofavariableinthescript.PythonInterpreterwillusenamespacerulesto
decidewhichvariablehasbeencalled,whichcanleadtoundesirableresults.
Zero-basedindexing
Asmentionedintheprecedingsectionthatdescribeslistsandtuples,Pythonindexingand
countingstartsatzero,insteadofone.Thismeansthatthefirstmemberofagroupofdata
isatthezeroposition,andthesecondmemberisatthefirstposition,andsoontillthelast
position.
Thisrulealsoapplieswhenthereisaforloopiterationwithinascript.Whentheiteration
starts,thefirstmemberofthedatabeingiteratedisinthezeroposition.
Also,indexingcanbeperformedwhencountingfromthelastmemberofaniterable
object.Inthiscase,theindexofthelastmemberis-1,andthesecondtolastis-2,andso
onbacktothefirstmemberoftheobject.
ImportantPythonModulesforGIS
Analysis
Modules,orcodelibrariesthatcanbecalledbyascripttoincreaseitsprogramming
potential,areeitherbuiltintoPythonorarecreatedbythirdpartiesandaddedlaterto
Python.MostofthesearewritteninPython,butanumberofthemarealsowritteninother
programminglanguagesandthenwrappedinPythontomakethemavailablewithin
Pythonscripts.ModulesarealsousedtomakeotherprogramsavailabletoPython,suchas
thetoolsbuiltinMicrosoftWord.
TheArcPymodule
TheArcPymoduleisbothawrappermoduleusedtointeractwiththeArcGIStools,
whicharethenexecutedbyArcGISinitsinternalcodeformat,andacodebasethatallows
foradditionalcontrolofgeospatialanalysesandmapproduction.ArcPyisusedtocontrol
thetoolsinArcToolbox,butthetoolshavenotbeenrewritteninPython;instead,weare
abletousetheArcGIStoolsusingArcPy.ArcPyalsogivesustheabilitytocontrol
ArcGISMapDocuments(MXDs)andtheobjectsthatMXDsinclude:legends,titles,
images,layers,andthemapviewitself.ArcPyalsohastoolsthatarenotavailablein
ArcToolbox.Themostpowerfulofthesearethedatacursors,especiallythenewData
AnalysisCursorsthatcreateamorePythonicinterfacewithGISdata.Thedatacursors,
coveredextensivelyinChapters5,ArcPyCursors:Search,InsertandUpdateandChapter
6,WorkingwithArcPyGeometryObjectsareveryusefultoextractrowsofdatafromdata
sourcesforanalysis.
TheabilitytocontrolgeospatialanalysesusingArcPyallowsfortheintegrationof
ArcGIStoolsintoworkflowsthatcontainotherpowerfulPythonmodules.Python’sglue
languageabilitiesincreasetheusefulnessofArcGISbyreducingtheneedtotreat
geospatialdatainaspecialmanner.
TheOperatingSystem(OS)module
TheOSmodule,partofthestandardlibrary,allowsPythontoaccessoperatingsystem
functionality.Acommonuseofthemoduleistousetheos.pathmethodtocontrolfile
pathsbydividingthemintodirectorypaths(thatis,folders)andbasepaths(thatis,files).
Thereisalsoausefulmethod,os.walk,whichwillwalk-throughadirectoryandreturnall
fileswithinthefoldersandsubfolders.TheOSmoduleisaccessedconstantlywhen
performingGISanalysis.
ThePythonSystem(SYS)module
Thesysmodule,partofthestandardlibrary,referstothePythoninstallationitself.Ithasa
numberofmethodsthatwillgetinformationabouttheversionofPythoninstalled,aswell
asinformationaboutthescriptandanyarguments(orparameters)suppliedtothescript,
usingthesys.argvmethod.Thesys.pathmethodisveryusefultoappendthePythonfile
path;practically,thismeansthatfolderscontainingscriptscanbereferencedbyother
scriptstomakethefunctionstheycontainimportabletootherscripts.
TheXLRDandXLWTmodules
TheXLRDandXLWTmodulesareusedtoreadandwriteExcelspreadsheets,respectively.
Themodulescanbeveryusefultoextractdatafromlegacyspreadsheetsandconvertthem
intousabledataforGISanalysis,ortowriteanalysisresultswhenageospatialanalysisis
completed.TheyarenotpartofthePythonstandardlibrary,butareinstalledalongwith
ArcGIS10.2andPython2.7.
Commonlyusedbuilt-infunctions
Thereareanumberofbuilt-infunctionsthatwewillusethroughoutthebook.Themain
onesarelistedasfollows:
str:Thestringfunctionisusedtoconvertanyothertypeofdataintoastring
int:Theintegerfunctionisusedtoconvertastringorfloatintoaninteger.Tonot
createanerror,anystringpassedtotheintegerfunctionmustbeanumbersuchas1.
float:Thefloatfunctionisusedtoconvertastringoranintegerintoafloat,much
liketheintegerfunction.
Commonlyusedstandardlibrarymodules
Thefollowingstandardlibrarymodulesmustbeimported:
datetime:Thedatetimemoduleisusedtogetinformationaboutthedateandtime,
andconvertstringdatesintoPythondates.
math:Themathmoduleisusedforhigherlevelmathfunctionsthatarenecessaryat
times,suchasgettingavalueforPiorcalculatingthesquareofanumber.
string:Thestringmoduleisusedforstringmanipulations.
csv:TheCSVmoduleisusedtocreateandeditcomma-separatedvaluetypefiles.
Checkouthttps://docs.python.org/2/libraryforacompletelistofthebuilt-inmodulesin
thestandardlibrary.
Summary
Inthischapter,wediscussedabouttheZenofPythonandcoveredthebasicsof
programmingusingPython.WebeganourexplorationofArcPyandhowitcanbe
integratedwithotherPythonmodulestoproducecompleteworkflows.Wealsodiscussed
thePythonstandardlibraryandthebasicdatatypesofPython.
Next,wewilldiscusshowtoconfigurePythonforusewithArcGIS,andexplorehowto
useIntegratedDevelopmentEnvironments(IDEs)towritescripts.
Chapter2.ConfiguringthePython
Environment
Inthischapter,wewillconfigurebothPythonandourcomputertoworktogetherto
executePythonscripts.Pathvariablesandenvironmentvariableswillbeconfiguredto
ensurethatimportstatementsworkasexpected,andthatscriptsrunwhentheyareclicked
on.ThestructureofthePythonfolderwillbediscussed,aswillthelocationoftheArcPy
modulewithintheArcGISfolderstructure.WewillalsodiscussIntegratedDevelopment
Environments(IDEs),programsdesignedtoassistincodecreationandcodeexecution,
andcompareandcontrastexistingIDEstodeterminewhatbenefitseachIDEcanoffer
whenscriptingPythoncode.
Thischapterwillcover:
ThelocationofthePythoninterpreter,andhowitiscalledtoexecuteascript
Adjustingthecomputer’senvironmentvariablestoensurecorrectcodeexecution
IntegratedDevelopmentEnvironments
Python’sfolderstructure,withafocusonwheremodulesarestored
WhatisaPythonscript?
Let’sstartwiththeverybasicsofwritingandexecutingaPythonscript.WhatisaPython
script?Itisasimpletextfilethatcontainsaseriesoforganizedcommandswrittenina
formalizedlanguage.Thetextfilehastheextension.py,butotherthanthat,thereis
nothingtodistinguishitfromanyothertextfile.Itcanbeopenedusingatexteditorsuch
asNotepadorWordpad,butthemagicthatisPythondoesnotresideinaPythonscript.
WithoutthePythoninterpreter,aPythonscriptcannotberunandthecommandsit
containscannotbeexecuted.
HowPythonexecutesascript
UnderstandinghowPythonworkstointerpretascriptandthenexecutethecommands
withinisasimportantasunderstandingthePythonlanguageitself.Hoursofdebugging
anderrorcheckingcanbeavoidedbytakingthetimetosetupPythoncorrectly.The
interpretivenatureofPythonmeansthatascriptwillhavetobefirstconvertedinto
bytecodebeforeitcanbeexecuted.WewillcoverthestepsthatPythontakestoachieve
ourgoalofautomatingGISanalysis.
WhatisthePythoninterpreter?
ThePythoninterpreter,onaWindowsenvironment,isaprogramthathasbeencompiled
intoaWindowsexecutable,whichhastheextension.exe.ThePythoninterpreter,
python.exe,hasbeenwritteninC,anolderandextensivelyusedprogramminglanguage
withamoredifficultsyntax.
ProgramswritteninC,whicharealsoinitiallywrittenastextfiles,mustbeconvertedinto
executablesbyacompiler,aspecializedprogramthatconvertsthetextcommandsinto
machinecodetocreateexecutableprograms.Thisisaslowprocessthatcanmake
producingsimpleprogramsinCalaboriousprocess.Thebenefitisthattheprograms
producedarestandaloneprogramscapableofrunningwithoutanydependencies.Python,
ontheotherhand,interpretsandexecutesthePythoncommandsquickly,whichmakesita
greatscriptinglanguage,butthescriptsmustberunthroughaninterpreterandcannotbe
executedbythemselves.
ThePythoninterpreter,asitsnameimplies,interpretscommandscontainedwithina
Pythonscript.WhenaPythonscriptisrun,orexecuted,thesyntaxisfirstcheckedto
makesurethatitconformstotherulesofPython(forexample,indentationrulesare
followedandthevariablesfollownamingconventions).Then,ifthescriptisvalid,the
commandscontainedwithinareconvertedintobytecode,aspecializedcodethatis
executedbythebytecodeinterpreter,avirtualmachinewritteninC.Thebytecode
interpreterfurtherconvertsthebytecode(whichiscontainedwithinfilesthatendwiththe
extension.pyc)intothecorrectmachinecodeforthecomputerbeingused,andthenthe
CPUexecutesthescript.Thisisacomplexprocess,whichallowsPythontomaintaina
semblanceofsimplicity.
ThereareotherversionsofthePythoninterpreterthathavebeenwritteninJava(knownas
Jython)andin.NET(knownasIronPython);thesevariantsareusedtowritePython
scriptsinothercomputingenvironmentsandwillnotbeaddressedinthisbook.The
ArcGISinstallerincludesthestandardimplementationofPython,whichisalsocalled
CPythontodistinguishitfromthesevariants.
WhereisthePythoninterpreterlocated?
ThelocationofthePythoninterpreterwithinthefolderstructureofacomputerisan
importantdetailtomaster.Pythonisoftendownloadeddirectlyfromwww.python.organd
installedseparatelyfromArcGIS.However,eachArcGISversionwillrequireaspecific
versionofPython;giventhisrequirement,theinclusionofPythonwithintheArcGIS
installationpackageishelpful.Forthisbook,wewillbeusingArcGIS10.2,andthiswill
requirePython2.7.
OnaWindowsmachine,thePythonfolderstructureisplaceddirectlyontheC:drive,
unlessitisexplicitlyloadedonanotherdrive.TheinstallationprocessforArcGIS10.2
willcreateafolderatC:\Python27,whichwillcontainanotherfoldercalledeither
ArcGIS10.2orArcGIS10.2x64,dependingontheoperatingsystemandtheversionof
ArcGISthathasbeeninstalled.Forthisbook,Iwillbeusingthe32-bitversionofArcGIS,
sothefinalfolderpathwillbeatC:\Python27\ArcGIS10.2.
Withinthisfolderareanumberofsubfolders,aswellaspython.exe(thePython
interpreter).Alsoincludedisasecondversionoftheinterpretercalledpythonw.exe.
Pythonw.exewillexecuteascriptwithoutaterminalwindowwithprogramfeedback
appearing.Bothpython.exeandpythonw.execontaincompletecopiesofallPython
commandsandcanbeusedtoexecuteascript.
WhichPythoninterpretershouldbeused?
ThegeneralruletoexecuteascriptdirectlyusingthePythoninterpretersistouse
pythonw.exe,asnoterminalwindowwillappear.Whenthereisaneedtotestcode
snippets,ortoseetheoutputwithinaterminalwindow,startpython.exebydoubleclickingontheexecutable.
Whenpython.exeisstarted,aPythoninterpreterconsolewillappear:
Notethedistinctivethreechevrons(>>>)thatappearbelowtheheaderexplainingversion
information.ThatisthePythonprompt,wherecodeisenteredtobeexecutedlinebyline,
insteadofinacompletedscript.Thisdirectaccesstotheinterpreterisusefultotestcode
snippetsandunderstandsyntax.Aversionofthisinterpreter,thePythonWindow,hasbeen
builtintoArcMapandArcCatalogsinceArcGIS10.Itwillbediscussedmoreinlater
chapters.
Howdoesthecomputerknowwheretheinterpreter
is?
TobeabletoexecutePythonscriptsdirectly(thatis,tomakethescriptsrunbydoubleclickingonthem),thecomputerwillalsoneedtoknowwheretheinterpretersitswithinits
folderstructure.Toaccomplishthisrequiresbothadministrativeaccountaccessand
advancedknowledgeofhowWindowssearchesforaprogram.Wewillhavetoadjustan
environmentvariablewithintheadvancedsystemsettingsdialoguetoregisterthe
interpreterwiththesystempath.
OnaWindows7machine,clickonthestartmenuandright-clickonComputer,then
selectPropertiesfromthemenu.OnaWindows8machine,clickonWindowsexplorer
andrightclickonThisPC,andselectPropertiesfromthemenu.Thesecommandsare
shortcutstogettotheControlPanel’sSystemandSecurity/Systemmenus.Select
Advancedsystemsettingsfromthepanelontheleft.ClickontheEnvironment
VariablesbuttonatthebottomoftheSystemPropertiesmenuthatappears.Inthelower
portionoftheEnvironmentVariablesmenu,scrollthroughtheSystemvariables
windowuntilthePathvariableappears.Selectitbyclickingonit,andthenclickingon
theEditbutton.Thefollowingwindowwillappear:
Thisvariablehastwocomponents:Variablename(path)andVariablevalue.Thevalue
isaseriesoffolderpathsseparatedbysemicolons.Thisisthepaththatissearchedwhen
Windowslooksforspecificexecutablesthathavebeenassociatedwithafileextension.In
ourcase,wewillbeaddingthefolderpaththatcontainsthePythoninterpreter.Type
C:\Python27\ArcGIS10.2(ortheequivalentonyourmachine)intotheVariablevalue
field,makingsuretoseparateitfromthevaluebeforeitwithasemicolon.ClickonOKto
exittheEditdialogue,andOKtoexittheEnvironmentVariablesmenu,andOKtoexit
theSystemPropertiesmenu.ThemachinewillnowknowwherethePythoninterpreter
is,asitwillsearchallfolderscontainedwithinthePathvariabletolookforanexecutable
calledPython.Totestthatthepathadjustmentworkedcorrectly,openupacommand
window(Startmenu/runcmd)andtypepython.Theinterpretershoulddirectlyruninthe
commandwindow:
IfthePythonheaderwithversioninformationandthetriplechevronappears,thepath
adjustmenthasworkedcorrectly.
Note
Ifthereisnoadminaccessavailable,thereisaworkaround.Inacommand-linewindow,
passtheentirepathtothePythoninterpreter(C:\Python27\ArcGIS10.2\python.exe)to
starttheinterpreter.
MakePythonscriptsexecutablewhen
clickedon
Thefinalstepinmakingthescriptsrunwhendouble-clicked(whichalsomeanstheycan
runoutsideoftheArcGISenvironment,savinglotsofmemoryoverhead)istoassociate
fileswiththe.pyextensionwiththePythoninterpreter.Ifthescriptshavenotalready
beenassociatedwiththeinterpreter,theywillappearasfilesofanunknowntypeorasa
textfile.
Tochangethis,right-clickonaPythonscript.SelectOpenWith,andthenselectChoose
DefaultProgram.Ifpython.exeorpythonw.exedoesnotappearasachoice,navigateto
thefolderthatholdsthem(C:\Python27\ArcGIS10.2,inthiscase)andselecteither
python.exeorpythonw.exe.Again,thedifferencebetweenthetwoistheappearanceofa
terminalwindowwhenthescriptsarerunusingpython.exe,whichwillcontainany
outputfromthescript(butthiswindowwilldisappearwhenthescriptisdone).I
recommendusingpythonw.exewhenexecutingscripts,andpython.exetotestcode.
Note
Pythonscriptscanalsoexplicitlycallpythonw.exebyadjustingtheextensionto.pyw
insteadof.py.
IntegratedDevelopmentEnvironments
(IDEs)
ThePythoninterpretercontainseverythingthatisneededtoexecuteaPythonscriptorto
testPythoncodebyinteractingwiththePythoninterpreter.However,writingscripts
requiresatexteditor.Thereareusuallyatleasttwosimpletexteditorsincludedona
Windowsmachine(NotepadandWordpad)andtheyworkinanemergencytoeditascript
orevenwriteawholescript.Unfortunately,theyareverysimpleanddonotallowtheuser
functionalitythatwouldmakeiteasiertowritemultiplescriptsorverylongscripts.
Tobridgethegap,aseriesofprogramscollectivelyknownasIntegratedDevelopment
Environmentshavebeendeveloped.IDEsexistforallprogramminglanguages,and
includefunctionssuchasvariablelisting,codeassist,andmore,thatmakethemidealto
craftprogrammingscripts.Wewillreviewafewofthemtoassesstheirusefulnesstowrite
Pythonscripts.Thethreediscussedasfollowsareallfreeandwell-establishedwithin
differentPythoncommunities.
IDLE
PythonincludesanIDEwhenitisinstalled.TheIDEiscalledIDLE,whichisawordplay
onbothIDEandthenameofaprominentmemberofMontyPython,EricIdle.Itcanbe
startedinWindows7bygoingtotheStartmenuandfindingtheArcGISfolderwithinthe
Programsmenu.WithinthePythonfolder,IDLEwillbeoneofthechoiceswithinthat
folder.SelectittostartIDLE.
IDLEcontainsaninteractiveinterpreter(i.e.thetriplechevron)andtheabilitytorun
completePythonscripts.ItisalsowrittenusingPython’sbuilt-inGUImodule,called
Tkinter,soithastheadvantageofbeingwritteninthesamelanguagethatitexecutes.
AnotheradvantageofusingIDLEoverthePythonconsole(python.exe)isthatanyprint
statementsorotherscriptoutputisdirectedtotheIDLEinteractivewindow,whichdoes
notdisappearafterexecutingthescript.IDLEisalsolightweightwithrespecttomemory
use.ScriptsareopenedusingafiledialoguecontainedwithintheFilemenu,andrecently
runscriptsarelistedwithintheFilemenu’s,RecentFiles.
DisadvantagesofIDLEincludealimitedcodeassist(orcodeauto-complete),auseful
IDEtool,andhavingnowaytoorganizescriptsintologicalprojects.Thereisnowayto
findallvariablescontainedwithinascript,anotherusefulfeatureofotherIDEs.Also,the
RecentFilesmenuhasalimitonthenumberofscriptsthatitwilllist,makingitharderto
findascriptthathasnotbeenruninmonths(whichisacommonoccurrence,believe
me!).IDLEisapassableIDEthatisusefulifnootherprogramscanbeinstalledonthe
machine.Itisalsoveryusefulforrapidtestingofcodesnippets.Whileitisnotmymain
IDE,IfindmyselfusingIDLEalmostdaily.
PythonWin
PythonWin(shortforPythonforWindows)isavailableat
http://sourceforge.net/projects/pywin32/files/pywin32,andincludesbothanIDEand
helpfulmodulestousePythoninaWindowsenvironment.Selectthenewestbuildof
PythonWin,andthenselectthecorrectversion32modulebasedontheinstalledversion
ofPython(formymachine,Iselectedpywin32-218.win32-py2.7.exe,thecorrectversion
formy32-bitPython2.7installation).Runtheexecutable,andifthecorrectversionhas
beendownloaded,theinstallationGUIwillrecognizePython2.7inthesystemregistry
andwillinstallitself.
PythonWinincludesanInteractiveWindowwheretheusercandirectlyinteractwiththe
Pythoninterpreter.ScriptscanalsobeopenedwithinPythonWin,anditincludesasetof
tilingcommandsintheWindowsmenuthatallowstheusertoorganizethedisplayofall
openscriptsandtheInteractiveWindow.
AnotherniceadvantagethatPythonWinhasoverIDLEistheabilitytodisplaydifferent
portionsofascriptwithinthesamescriptwindow.Ifascripthasgrowntoolong,itcanbe
apaintoscrollupanddownthescriptwhenediting.PythonWinallowstheusertopull
downfromthetopofthescripttocreateasecondscriptwindow,whichcanfocusona
separatepartofthescript.Also,ontheleftside,anotherwindowcanbeopenedthatwill
listPythonclassesandvariables,makingiteasiertonavigatetoaparticularsectionofthe
script.
OnesmallbuthelpfulfeaturebuiltintoPythonWin’sInteractiveWindowistheabilityto
searchthroughpreviouslyenteredcodestatements.Atthetriplechevronprompt,hold
downtheCtrlkeyandusetheupanddownarrowkeystonavigatethroughthelinesto
findoneofinterest.Thissavesalotoftimewhentestingaparticularsnippetofcode.
Allinall,PythonWinisausefulandeasy-to-useIDE,andmostArcGISprofessionalswho
createPythonscriptsusePythonWin.ThedrawbacksIfindwithPythonWinincludeits
lackofabilitytoorganizescriptsintoprojects,anditslackofalistofvariablesthatexist
withinthescript,whichcanbeveryhelpfulwhennavigatinglargerscripts.
AptanaStudio3
Sometimesthetoolsofthegreaterprogrammingcommunitycanseemdauntingtonew
scripters,whoaremorefocusedonsimplycreatingascriptthatwillsavetimeonaGIS
analysisthanusingthecorrecttoolforprogrammingdaily.Itremindsmeofinexperienced
computerusers,whodon’tfeelliketheyneedthefullpowerofatop-of-the-linecomputer
becausetheyonlywanttobrowsetheinternetandsende-mails.
However,theexactoppositeistrue:thecomputeradverseisbetteroffhavinganeasierto
usetop-of-the-linecomputer,whileanexperiencedcomputerusercouldmakedowitha
netbook.
Thesamecanbesaidforprogrammersandscripters.Sometimes,it’sbettertohavean
over-the-topIDEthatwillactuallymakeascriptermoreproductive,whileanexperienced
programmercouldmakedowithNotepad.AllofthebellsandwhistlesincludedinanIDE
suchasAptanaStudio3willsavescripterstimeandtakeremarkablylittletimetolearn.
AptanaStudio3isavailableathttp://aptana.com.Downloadandruntheinstallerprovided
toinstallit.Chooseadefaultmainprojectfolderthatcancontainallofthescriptsprojects;
forthisbook,IcreatedafoldercalledC:\Projects.Foreachprojectcreated,Aptanawill
createaprojectfileholdinginformationabouteachproject.WhenusingAptanaStudioat
work,usinganetworkfoldercanbeusefulasotherscanthenaccesstheprojectswiththeir
respectiveAptanainstallations.
Onceithasbeeninstalled,thenextstepistocreateaPyDevproject.GototheFilemenu
andselectNew,andthenselectPyDevproject.Whencreatingthisfirstproject,Python
InterpreterwillhavetobeaddedtoAptana’sPythonpath.Aptanacansupportmorethan
oneinterpreter;forourpurposes,onewilldo.GotothebottomofthePyDevprojectmenu
andclickonClickheretoconfigureaninterpreter.WhenthePreferences/Python
Interpretersmenuappears,makesuretoselectInterpreter-Pythonontheleft,andthen
clickonNewinthetop-rightmenu.
OnceNewhasbeenselected,asmalldialogwillappearaskingforanameforthe
interpreterandthepathtotheexecutable.Clickonbrowseandnavigatetothefolderwith
python.exe.NoterminalwindowwillbegeneratedwhenrunningaPythonscriptusing
AptanaStudioasalloutputisredirectedtotheAptanaStudioconsole.Selectpython.exe
andclickonOK.Next,clickonOKintheSelectInterpretermenu,andthenclickon
OKinthePreferencesmenu.BackinthePyDevProjectmenu,givetheprojectaname,
andeitherusethedefaultworkspacelocationoracustomone(forexample,
C:\Projects).
Allofthisconfigurationonlyhastohappenthefirsttime;oncethatisdone,creatinga
PyDevprojectwillonlyrequiregivinganameandlocation.Now,allofthescripts
associatedwiththatprojectwillalwaysbelistedintheleftmenu(PyDevPackage
Explorer),whichisaverypowerfulwaytoorganizeprojectsandscripts.
MakingsurethatAptanaStudioisinthePyDevperspective(intheWindows/Open
Perspective/Othermenu,choosePyDev)willgivethreemainwindows–Package
Explorerontheleft,Scriptwindowinthemiddle,andOutlinewindowontheright,
wherevariablescontainedwithinascriptarelisted.Clickingononeofthevariablesonthe
rightwillmovethescriptwindowtothatsectionofthecode,makingscriptnavigation
fast.Also,IliketoaddtheConsolewindowinthemiddlebelowtheScriptwindow,where
theoutputofthescriptcanbedisplayed.
OpenscriptseachhaveatabwithintheScriptwindow,makingiteasytoswitchbetween
thescripts.Also,thewindowscanbeclosedtogivemoreroomtotheScriptwindowas
needed.Hoveringoveravariablewithinascriptwillcallupapop-upmenuthatdescribes
wherethevariablewasfirstcreated,whichcanbealifesaverasitiseasytoforgetattimes
whichvariableiswhich(unless,ofcourse,theyareclearlynamedaccordingtotherules
describedinthepreviouschapter;eventhen,itcanbeapainattimes).
IDEsummary
TherearemanyotherIDEs,bothcommercialandfree,availableforcodinginPython.In
theend,eachGISanalystmustchoosethetoolthatmakesthemfeelproductiveand
comfortable.Thismaychangeasprogrammingbecomesabiggerpartoftheirdailywork
flow.BesuretotestoutafewdifferentIDEstofindonethatiseasytouseandintuitive.
Pythonfolderstructure
Python’sfolderstructureholdsmorethanjustthePythonInterpreter.Withinthe
subfoldersresideanumberofimportantscripts,digitallinklibraries,andevenClanguage
modules.Notallofthescriptsareusedallthetime,buteachhasaroleinmakingthe
Pythonprogrammingenvironmentpossible.Themostimportantfoldertoknowaboutis
thesite-packagesfolder,wheremostmodulesthatwillbeimportedinPythonscriptsare
contained.
Wheremodulesreside
WithineveryPythonfolderisafoldercalledLib,andwithinthatfolderisafoldercalled
site-packages.Onmymachine,thefoldersitsatC:\Python27\ArcGIS10.2\Lib\sitepackages.Almostallthird-partymodulesarecopiedintothisfoldertobeimportedas
needed.Themainexceptiontothisrule,forourpurposes,istheArcPymodule,whichis
storedwithintheArcGISfolderintheProgramFilesfolder(forexample,C:\Program
Files(x86)\ArcGIS\Desktop10.2\arcpy).Tomakethatpossible,theArcGISinstaller
adjuststhePythonsystempath(usingthesysmodule)tomakethearcPymodule
importable.
UsingPython’ssysmoduletoaddamodule
Python’ssysmoduleisamodulethatallowstheusertotakeadvantageofsystemtools
builtintothePythonInterpreter.Oneofthemostusefulofthefunctionsinthesysmodule
issys.path.Itisalistoffilepaths,whichtheusercanmodifytoadjustwherePythonwill
lookforamoduletoimport,withoutneedingadministrativeaccess.
WhenPython2.7isinstalledbytheArcGIS10.2installer,theinstallertakesadvantageof
thesys.pathfunctionstoaddC:\ProgramFiles(x86)\ArcGIS\Desktop10.2\arcpyto
thesystempath.Totestthis,startthePythonInterpreteroranIDEandtypethefollowing:
>>>importsys
>>>printsys.path
Theoutputisasfollows:
['','C:\\WINDOWS\\SYSTEM32\\python27.zip',
'C:\\Python27\\ArcGIS10.2\\Dlls','C:\\Python27\\ArcGIS10.2\\lib',
'C:\\Python27\\ArcGIS10.2\\lib\\plat-win',
'C:\\Python27\\ArcGIS10.2\\lib\\lib-tk',
'C:\\Python27\\ArcGIS10.2\\Lib\\site-packages\\pythonwin',
'C:\\Python27\\ArcGIS10.2','C:\\Python27\\ArcGIS10.2\\lib\\site-packages',
'C:\\ProgramFiles(x86)\\ArcGIS\\Desktop10.2\\bin','C:\\ProgramFiles
(x86)\\ArcGIS\\Desktop10.2\\arcpy','C:\\ProgramFiles
(x86)\\ArcGIS\\Desktop10.2\\ArcToolbox\\Scripts',
'C:\\Python27\\ArcGIS10.2\\lib\\site-packages\\win32',
'C:\\Python27\\ArcGIS10.2\\lib\\site-packages\\win32\\lib']
Thesystempath(storedinthevariablesys.path)includesallofthefoldersthatArcPy
requirestoautomateArcGIS.Thesystempathincorporatesalldirectorieslistedinthe
PYTHONPATHenvironmentvariable(ifonehasbeencreated);thisisseparatefromthe
Windowspathenvironmentvariablediscussedpreviously.Thetwoseparatepathvariables
worktogethertohelpPythonlocatemodules.
Thesys.path.append()method
Thesys.pathfunctionisalist(didyounoticethesquarebracketsintheprecedingcode
output?)andassuchcanbeappendedorextendedtoincludenewfilepathsthatwillpoint
tomodulestheuserwantstoimport.Toavoidtheneedtoadjustsys.path,copythe
moduleintothesite-packagesfolder.Whenthisisnotpossible,usethe
sys.path.append()methodinstead:
>>>sys.path.append("C:\\Projects\\Requests")
>>>sys.path
['','C:\\WINDOWS\\SYSTEM32\\python27.zip',
'C:\\Python27\\ArcGIS10.2\\Dells',
'C:\\Python27\\ArcGIS10.2\\lib',
..'C:\\Python27\\ArcGIS10.2\\lib\\plat-win',
..'C:\\Python27\\ArcGIS10.2\\lib\\lib-tk',
..'C:\\Python27\\ArcGIS10.2\\Lib\\site-packages\\pythonwin',
..'C:\\Python27\\ArcGIS10.2',..'C:\\Python27\\ArcGIS10.2\\lib\\sitepackages','C:\\Program
..Files(x86)\\ArcGIS\\Desktop10.2\\bin','C:\\ProgramFiles
..(x86)\\ArcGIS\\Desktop10.2\\arcpy','C:\\ProgramFiles
..(x86)\\ArcGIS\\Desktop10.2\\ArcToolbox\\Scripts',
..'C:\\Python27\\ArcGIS10.2\\lib\\site-packages\\win32',
..'C:\\Python27\\ArcGIS10.2\\lib\\site-packages\\win32\\lib',
..'C:\\Projects\\Requests']
Whenthesys.path.append()methodisused,theadjustmentistemporary.Adjustthe
PYTHONPATHenvironmentvariableintheWindowsSystemPropertiesmenu
(discussedinthepathenvironmentvariablesection)tomakeapermanentchange(and
createthePYTHONPATHifithasnotbeencreated).
Onelastnoteisthattoimportamodulewithoutadjustingthesystempathorcopyingthe
moduleintothesite-packagesfolder,placethemoduleinthefolderwiththescriptthatis
importingit.Aslongasthemoduleisconfiguredcorrectly,itwillworknormally.Thisis
usefulwhenthereisnoadministrativeaccessavailabletoamachine.
Summary
Inthischapter,wecoveredalotabouthowPythonworkstoexecutescriptsand
commands,andaboutdevelopmentenvironmentsusedtocraftscripts.Inparticular,we
discussedhowaPythonscriptisreadandexecutedbythePythonInterpreter,wherethe
PythonInterpreterislocatedwithinthePythonfolderstructure,andwhatthedifferent
Pythonscriptextensionsmean(.py,.pyc,.pyw).WealsocoveredIntegrated
DevelopmentEnvironmentsandhowtheycompareandcontrast.
Inthenextchapter,wewillcoverhowtouseModelBuildertoconvertamodeledanalysis
intoaPythonscript,andhowtomakeitmorepowerfulthantheexportedversion.
Chapter3.CreatingtheFirstPython
Script
NowthatwehavePythonconfiguredtofitourneeds,wecancreatePythonscripts.This
chapterwillexplorehowtouseArcGISModelBuildertomodelasimpleanalysisasthe
basisforourscript.ModelBuilderisveryusefulonitsownandforcreatingPythonscripts
asithasanoperationalandavisualcomponent,andallmodelscanbeoutputtedasPython
scripts.ThiswillallowustocomparehowthemorefamiliarModelBuilderutilizestools
intheArcToolboxtohowPythonhandlesthesametools.Wewillalsodiscussiteration
andwhenitisbesttousePythonoverModelBuilder.
Inthischapter,wewillcoverthefollowingtopics:
ModelingasimpleanalysisusingModelBuilder
ExportingthemodelouttoaPythonscript
Prerequisites
“AlongwithArcGISModelBuilder,adatasetandscriptsarerequired.”
Forthischapter,theaccompanyingdataandscriptsshouldbedownloadedfromPackt
Publishing’swebsite.Thecompletedscriptsareavailableforcomparisonpurposesandthe
datawillbeusedforthischapter’sanalysis.
ModelBuilder
ArcGIShasbeenindevelopmentsincethe1970s.Duringthattime,itincludedavarietyof
programminglanguagesandtoolstohelpGISanalystsautomateanalysesandmap
production.TheseincludetheAvenuescriptinglanguageintheArcGIS3xseriesandthe
ARCMacroLanguage(AML)intheARC/Infoworkstationdays,aswellasVBScript
upuntilArcGIS10xwhenPythonwasintroduced.Anotherusefultoolintroducedin
ArcGIS9xwasModelBuilder,avisualprogrammingenvironmentusedforbothmodeling
analysisandcreatingtoolsthatcanbeusedrepeatedlywithdifferentinputfeatureclasses.
AnotherusefulfeatureofModelBuilderisanexportfunctionthatallowsmodelersto
createPythonscriptsdirectlyfromamodel.Thiswillmakeiteasiertocomparehow
inputsinaModelBuildertoolareacceptedversushowaPythonscriptcallsthesametool
andsuppliestheinputstoit,orhowthefeatureclassesthatarecreatedarenamedand
placedwithinthefilestructure.ModelBuilderisafantastictoolthatwillmakeiteasyfora
GISanalysttobridgethegapfromnormalGISworkflowstoautomatedPython-based
workflows.
CreatingamodelandexportingtoPython
ThischapterwilldependonthedownloadableSanFrancisco.gdbfilegeodatabase,
availablefromthePacktPublishingwebsite.TheSanFranciscoGDBcontainsdata
downloadedfromdata.sfgov.organdtheUSCensus’AmericanFactfinderwebsite
availableatfactfinder2.census.gov.Allcensusandgeographicdataincludedinthe
geodatabaseisfromthe2010census.Thedataiscontainedwithinafeaturedatasetcalled
SanFrancisco.ThedatainthisfeaturedatasetisinNAD83CaliforniaStatePlaneZone3
andthelinearunitofmeasureistheUSFoot(thiscorrespondstoSRID2227inthe
EuropeanPetroleumSurveyGroup,orEPSG,format).
Theanalysiswewillcreatewiththemodel,andeventuallyexporttoPythonforfurther
refinement,willusebusstopsalongaspecificlineinSanFrancisco.Thesebusstopswill
bebufferedtocreatearepresentativeregionaroundeachbusstop.Thebufferedareaswill
thenbeintersectedwithcensusblockstofindouthowmanypeoplearewithineach
representativeregionaroundthebusstops.
ModelingtheSelectandBuffertools
UsingModelBuilder,wewillfirstmodelthebasisofthebusstopanalysis.Onceithas
beenmodeled,itwillbeexportedasanautomaticallygeneratedPythonscript.Follow
thesestepstobegintheanalysis:
1. OpenupArcCatalogandcreateafolderconnectiontothefoldercontaining
SanFrancisco.gdb.Right-clickongeodatabaseandaddanewtoolboxcalled
Chapter3Tools.
2. Next,openModelBuilderandcreateaModel,savingitintheChapter3Tools
toolboxasChapter3Model1.
3. DragtheBus_StopsfeatureclassandtheSelecttoolfromtheAnalysis/Extract
toolsetinArcToolbox.
4. OpentheSelecttoolandnametheoutputfeatureclassInbound71.Makesurethatthe
featureclassiswrittentotheChapter3Resultsfeaturedatasetintothemodel.
5. OpentheExpressionSQLQueryBuilderandcreatethefollowingSQLexpression:
NAME=‘71IB’ANDBUS_SIGNAG=‘FerryPlaza’.
6. ThenextstepistoaddaBuffertoolfromtheAnalysis/Proximitytoolset.The
Buffertoolwillbeusedtocreatebuffersaroundeachbusstop.Thebufferedbus
stopsallowustointersectwithcensusdataintheformofcensusblocks,creatingthe
representativeregionsaroundeachbusstop.
7. ConnecttheoutputoftheSelecttool(Inbound71)totheBuffertool.Openupthe
Buffertoolandadd400totheDistancefield,andmaketheunitsFeet.Leavethe
restoftheoptionsblank.ClickonOKandreturntothemodel.
AddingtheIntersecttool
Nowthatwehaveselectedthebuslineofinterest,andbufferedthestopstocreate
representativeregions,wewillneedtointersecttheregionswiththecensusblockstofind
thepopulationofeachrepresentativeregion:
1. First,addtheCensusBlocks2010featureclassfromtheSanFranciscofeaturedataset
tothemodel.
2. Next,addtheIntersecttool,locatedintheAnalysis/OverlaytoolsetinArcToolbox.
WhilewecoulduseSpatialJointoachieveasimilarresult,IamusingtheIntersect
tooltocapturetheareaofintersectforuselaterinthemodelandscript.
Atthispoint,ourmodelshouldlooklikethis:
Tallyingtheanalysisresults
Afterwecreatedthissimpleanalysis,thenextstepistodeterminetheresultsforeachbus
stop.Findingthenumberofpeoplethatliveincensusblockstouchedbythe400feet
bufferofeachbusstopinvolvesexaminingeachrowofdatainthefinalfeatureclassand
selectingrowsthatcorrespondtothebusstop.Oncetheseareselected,asumofthe
selectedrowswouldbecalculatedeitherusingtheFieldCalculatorortheSummarize
tool.Allofthesemethodswillwork,andyetnoneareperfect.Theytaketoolong,and
worse,arenotrepeatableautomaticallyifanassumptioninthemodelisadjusted(ifthe
bufferisadjustedfrom400feetto500feet,forinstance).
ThisiswherethetraditionalusesofModelBuilderbegintofailanalysts.Itshouldbeeasy
toinstructthemodeltoselectallrowsassociatedwitheachbusstop,andthengeneratea
summedpopulationfigureforeachbusstop’srepresentativeregion.Itwouldbeeven
bettertohavethemodelcreateaspreadsheettocontainthefinalresultsoftheanalysis.It’s
timetousePythontotakethisanalysistothenextlevel.
Exportingthemodelandadjustingthe
script
WhilemodelinganalysisinModelBuilderhasitsdrawbacks,thereisonefantasticoption
builtintoModelBuilder;theabilitytocreateamodelandthenexportthemodeltoPython.
AlongwiththeArcGIShelpdocumentation,itisthebestwaytodiscoverthecorrect
PythonsyntaxtousewhenwritingArcPyscripts.
CreateafolderthatcanholdtheexportedscriptsnexttotheSanFranciscogeodatabase
(forexample,C:\Projects\Scripts).Thiswillholdboththeexportedscriptsthat
ArcGISautomaticallygenerates,andtheversionsthatwewillbuildfromthosegenerated
scripts.
OpenthemodelcalledChapter3Model1andclickontheModelmenuintheupperleft.
SelectExportfromthemenu,andthenselectToPythonScript.Savethescriptinthe
scriptfolderasChapter3Model1.py.
Note
Notethatthereisalsotheoptiontoexportthemodelasagraphic.Creatingagraphicof
themodelisagoodwaytosharewhatthemodelisdoingwithotheranalystswithoutthe
needtosharethemodelandthedata,andcanalsobeusefulwhensharingPythonscripts
aswell.
Theautomaticallygeneratedscript
OpentheautomaticallygeneratedscriptinanIDE.Itshouldlooklikethis:
#-*-coding:utf-8-*#-------------------------------------------------------------------------#8662_Chapter3Model1.py
#Createdon:2014-04-2221:59:31.00000
#(generatedbyArcGIS/ModelBuilder)
#Description:
#-------------------------------------------------------------------------#Importarcpymodule
importarcpy
#Localvariables:
Bus_Stops="C:\\Projects\\PacktDB.gdb\\SanFrancisco\\Bus_Stops"
CensusBlocks2010=
"C:\\Projects\\PacktDB.gdb\\SanFrancisco\\CensusBlocks2010"
Inbound71="C:\\Projects\\PacktDB.gdb\\Chapter3Results\\Inbound71"
Inbound71_400ft_buffer=
"C:\\Projects\\PacktDB.gdb\\Chapter3Results\\Inbound71_400ft_buffer"
Intersect71Census=
"C:\\Projects\\PacktDB.gdb\\Chapter3Results\\Intersect71Census"
#Process:Select
arcpy.Select_analysis(Bus_Stops,
Inbound71,
"NAME='71IB'ANDBUS_SIGNAG='FerryPlaza'")
#Process:Buffer
arcpy.Buffer_analysis(Inbound71,
Inbound71_400ft_buffer,
"400Feet","FULL","ROUND","NONE","")
#Process:Intersect
arcpy.Intersect_analysis("C:\\Projects\\PacktDB.gdb\\Chapter3Results\\Inbou
nd71_400ft_buffer
#;C:\\Projects\\PacktDB.gdb\\SanFrancisco\\CensusBlocks2010#",
Intersect71Census,"ALL","","INPUT")
Let’sexaminethisscriptlinebyline.Thefirstlineisprecededbyapoundsign(#),which
againmeansthatthislineisacomment;however,itisnotignoredbythePython
interpreterwhenthescriptisexecutedasusualbutisusedtohelpPythoninterpretthe
encodingofthescriptasdescribedhere:http://legacy.python.org/dev/peps/pep-0263.
Thesecondcommentedlineandthethirdlineareincludedfordecorativepurposes.The
nextfourlines,allcommented,areusedtoprovidereaderswithinformationaboutthe
script,whatitiscalledandwhenitwascreated,alongwithadescriptionthatispulled
fromthemodel’sproperties.Anotherdecorativelineisincludedtoseparateoutthe
informativeheaderfromthebodyofthescriptvisually.Whilethecommentedinformation
sectionisnicetoincludeinascriptforotherusersofthescript,itisnotnecessary.
Thebodyofthescript,ortheexecutableportionofthescript,startswiththeimport
arcpyline.Importstatementsare,byconvention,includedatthetopofthebodyofthe
script.Inthisinstance,theonlymodulethatisbeingimportedisArcPy.
ModelBuilder’sexportfunctioncreatesnotonlyanexecutablescript,butalsocomments
eachsectiontohelpmarkthedifferentsectionsofthescript.Thecommentslettheuser
knowwherethevariablesarelocatedandwheretheArcToolboxtoolsarebeingexecuted.
Thecommentswillgrowtobesuperfluousasthereadergrowstounderstandthecode,but
itwasniceofESRItoincludethecomments.
Belowtheimportstatementsarethevariables.Inthiscase,thevariablesrepresentthefile
pathstotheinputandoutputfeatureclasses.Thevariablenamesarederivedfromthe
namesofthefeatureclasses(thebasenamesofthefilepaths).Thefilepathsareassigned
tothevariablesusingtheassignmentoperator(=),andthepartsofthefilepathsare
separatedbytwobackslashes.
FilepathsinPython
ItwouldbegoodtoreviewhowfilepathsareusedinPythoncomparedtohowtheyare
representedinWindows.InPython,filepathsarestrings,andstringsinPythonhave
specialcharactersusedtorepresenttabs(\t),newlines(\n),orcarriagereturns(\r),among
manyothers.Thesespecialcharactersallincorporatesinglebackslashes,makingitvery
hardtocreateafilepaththatusessinglebackslashes.Thiswouldnotbeabigdeal,except
thatfilepathsinWindowsExplorerallusesinglebackslashes.
Thereareanumberofmethodsusedtoavoidthisissue.Pythonwasdevelopedwithinthe
Linuxenvironment,wherefilepathshaveforwardslashes.ThismorePythonic
representationisalsoavailablewhenusingPythoninaWindowsenvironment,
demonstratedasfollows:
WindowsExplorer:
"C:\Projects\PacktDB.gdb\Chapter3Results\Intersect71Census"
Pythonicversion:
"C:/Projects/PacktDB.gdb/Chapter3Results/Intersect71Census"
WithinaPythonscript,thefilepathwiththeforwardslasheswillwork,whilethe
WindowsExplorerversionmightcausethescripttothrowanexception.
Anothermethodusedtoavoidtheissuewithspecialcharactersistheoneemployedby
ModelBuilderwhenitautomaticallycreatesthePythonscriptsfromamodel.Inthiscase,
thebackslashesareescapedusingasecondbackslash.Theprecedingscriptusesthis
secondmethodtoproducethefollowingresults:
Pythonescapedversion:
"C:\\Projects\\PacktDB.gdb\\Chapter3Results\\Intersect71Census"
Thethirdmethod,whichIprefer,istocreatewhatisknownasarawstring.Thisisthe
sameasaregularstring,butitincludesanrbeforethescriptbegins.Thisralertsthe
PythonInterpreterthatthefollowingscriptdoesnotcontainanyspecialcharactersor
escapecharacters.Hereisanexampleofhowitisused:
Pythonrawstring:
r"C:\Projects\PacktDB.gdb\Chapter3Results\Intersect71Census"
UsingrawstringswillmakeiteasiertograbafilepathfromWindowsExplorerandaddit
toastringinsideascript.Itwillalsomakeiteasiertoavoidaccidentallyforgettingto
includeasetofdoublebackslashesinafilepath,whichhappensallthetimeandisthe
causeofmanyscriptbugs.
Continuingthescriptanalysis:theArcPy
tools
Thenext,andmostimportant,sectionofthescriptiswheretheanalysisisexecuted.The
sametoolsthatwecreatedinthemodel,theSelect,theBuffer,andtheIntersecttools,
areincludedinthissection.Thesameparametersthatwesuppliedinthemodelarealso
includedhere:theinputsandoutputs,plustheSQLstatementintheSelecttool,andthe
bufferdistanceintheBuffertool.
Thetoolparametersaresuppliedtothetoolsinthescriptinthesameorderastheyappear
inthetoolinterfacesinthemodel.HereistheSelecttoolinthescript:
arcpy.Select_analysis(Bus_Stops,Inbound71,"NAME='71IB'ANDBUS_SIGNAG
='FerryPlaza'")
Itworkslikethis.ThearcPymodulehasamethod,oraspecialproperty,called
Select_analysis.Thismethod,whencalled,requiresthreeparameters:theinputfeature
class(orshapefile),theoutputfeatureclass,andtheSQLstatement.Inthisexample,the
inputisrepresentedbythevariableBus_Stopsandtheoutputfeatureclassisrepresented
bythevariableInbound71,bothofwhicharedefinedinthevariablesection.TheSQL
statementisincludedasthethirdparameter.Notethatitcouldalsoberepresentedbya
variable,ifthevariablewasdefinedabovethisline;theSQLstatement,asastring,could
beassignedtoavariableandthevariablecouldreplacetheSQLstatementasthethird
parameter.Hereisanexampleofparameterreplacementusingavariable:
sqlStatement="NAME='71IB'ANDBUS_SIGNAG='FerryPlaza'"
arcpy.Select_analysis(Bus_Stops,Inbound71,sqlStatement)
WhileModelBuilderisgoodaboutassigninginputandoutputfeatureclassestovariables,
itdoesnotassignvariablestoeveryportionoftheparameter.Thiswillbeanimportant
thingtocorrectwhenweadjustandbuildourownscripts.
TheBuffertoolacceptsasimilarsetofparametersastheSelecttool.Thereisaninput
featureclassrepresentedbyavariable,anoutputfeatureclassvariable,andthedistance
thatweprovided(400feetinthiscase),alongwithaseriesofparametersthataresupplied
bydefault.Notethattheparametersrelyonkeywords,andthesekeywordscanbe
adjustedwithinthetextofthescripttoadjusttheresultingbufferoutput.Forinstance,
FeetcouldbeadjustedtoMetersandthebufferwouldmuchlarger.Checkthehelpsection
ofthetooltobetterunderstandhowtheotherparameterswillaffectthebufferandtofind
thekeywordsargumentsthatwillbeacceptedbytheBuffertoolinArcPy.Also,asnoted
earlier,alloftheparameterscouldbeassignedtovariables,whichcansavetimeifthe
sameparametersareusedrepeatedlythroughoutascript.
Sometimesthesuppliedparameterismerelyanemptystring,asisthecaseherewiththe
lastparameter:
arcpy.Buffer_analysis(Inbound71,Inbound71_400ft_buffer,
"400Feet","FULL","ROUND","NONE","")
Theemptystring,whichinthiscasesignifiesthatthereisnotadissolvefieldforthis
buffer,isfoundquitefrequentlywithinArcPy.Itcouldalsoberepresentedbytwosingle
quotes,butModelBuilderhasbeenbuilttousedoublequotestoencasestrings.
TheIntersecttoolandstringmanipulation
Thelasttool,theIntersecttool,usesadifferentmethodtorepresentthefilesthatneedto
beintersectedtogetherwhenthetoolisexecuted.Becausethetoolacceptsmultiplefilesin
theinputsection(meaningthereisnolimittothenumberoffilesthatcanbeintersected
togetherinoneoperation),itstoresallofthefilepathswithinonestring.Thestringuses
thehashorpoundsign(#)toseparatethefilepathswithintheinputstring.Thisslight
deviationmustbedealtwithifwearetousetheIntersecttoolinaScripttool.Ifweare
buildingatoolfromthisscript,wewillnotknowthefilesthatwillbeintersectedbefore
theyarerun,soweneedtoknowthemethodstodealwithinsertingvariablesintostrings.
Therearethreemethodstoinsertvariablesintostrings.Eachmethodhasdifferent
advantagesanddisadvantagesofatechnicalnature.It’sgoodtoknowaboutallthreeof
themastheyhaveusesbeyondourneedshere,solet’sreviewthem.
Thestringmanipulationmethod1–stringaddition
Stringadditionisanoddconceptatfirstasitwouldnotseempossibletoaddstrings
together,unlikeintegersorfloats,whicharenumbers.However,withinPythonandother
programminglanguages,thisisanormalstep.Usingtheplussign(+),stringsareadded
togethertomakelongerstringsorallowvariablestobeaddedtothemiddleofexisting
strings.Herearesomeexamplesofthisprocess:
>>>aString="Thisisastring"
>>>bString="andthisisanotherstring"
>>>aString+bString
Theoutputisasfollows:
'Thisisastringandthisisanotherstring'
>>>cString=aString+bString
>>>cString
Theoutputisasfollows:
'Thisisastringandthisisanotherstring'
Twoormorestringscanbeaddedtogether,andcanevenbeassignedtoathirdvariable.
ThisprocesscanbeusefulforsituationssuchastheinputstringfortheIntersecttool.The
stringcanbebrokenupandvariablesrepresentingthefilepathscanbeinsertedintothe
middleofthestring:
filePath1=r"C:\Projects\Inbound71_400ft_buffer"
filePath2=r"C:\Projects\CensusBlocks2010"
arcpy.Intersect_analysis(filePath1+"#;"+filePath2+"#",
Intersect71Census,"ALL","","INPUT")
Thisisapowerfulandusefulwaytoinsertthefilepathsintotheinputstring.Aslongas
theseparatorsarestillincludedinthestring,thestringwillstillbevalidandtheIntersect
toolwillrunasexpected.Hereiswhatthestringwilllooklikewhenthestringadditionis
completed:
>>>filePath1=r"C:\Projects\Inbound71_400ft_buffer"
>>>filePath2=r"C:\Projects\CensusBlocks2010"
>>>inputString=filePath1+"#;"+filePath2+"#"
>>>printinputString
Theoutputisasfollows:
C:\Projects\Inbound71_400ft_buffer#;C:\Projects\CensusBlocks2010#
Anothersimilaroffshootofstringadditionisstringmultiplication,wherestringsare
multipliedbyanintegertoproducerepeatedversionsofthestring:
>>>"string"*3
Theoutputisasfollows:
'stringstringstring'
Thestringmanipulationmethod2–stringformatting#1
Thesecondmethodofstringmanipulation,knownasstringformatting,involvesadding
placeholdersintothestringthatwillacceptspecifickindsofdata.Thismeansthatthese
specialstringscanacceptotherstringsaswellasintegersandfloatvalues.These
placeholdersusethemodulo(%)andakeylettertoindicatethetypeofdatatoexpect.
Stringsarerepresentedusing%s,floatsarerepresentedusing%f,andintegersare
representedusing%d.Thefloatscanalsobeadjustedtolimitthedigitsincludedby
addingamodifyingnumberafterthemodulo.Ifthereismorethanoneplaceholderina
string,thevaluesarepassedtothestringinatuple.
Thismethodhasbecomelesspopularsincethethirdmethoddiscussedinthefollowing
sectionwasintroducedinPython2.6,butitisstillvaluabletoknowasmanyolderscripts
useit.Hereisanexampleofthismethod:
>>>origString="Thisstringhasasaplaceholder%s"
>>>newString=origString%"andthistextwasadded"
>>>printnewString
Theoutputisasfollows:
Thisstringhasasaplaceholderandthistextwasadded
Hereisanexamplewhenusingafloatplaceholder:
>>>floatString1="Thisstringhasafloathere:%f"
>>>newString=floatString1%1.0
>>>printnewString
Theoutputisasfollows:
Thisstringhasafloathere:1.000000
>>>floatString2="Thisstringhasafloathere:%.1f"
>>>newString2=floatString2%1.0
>>>printnewString2
Theoutputisasfollows:
Thisstringhasafloathere:1.0
Hereisanexampleusinganintegerplaceholder:
>>>intString="Hereisaninteger:%d"
>>>newString=intString%1
>>>printnewString
Theoutputisasfollows:
Hereisaninteger:1
FortheIntersecttool,the%ssymbolcanbeusedtoacceptthefilepathstringvariables:
filePath1=r"C:\Projects\Inbound71_400ft_buffer"
filePath2=r"C:\Projects\CensusBlocks2010"
arcpy.Intersect_analysis("%s#;%s#"%(filePath1,filePath2),
Intersect71Census,"ALL","","INPUT")
Thestringmanipulationmethod3–stringformatting#2
Thefinalmethod,themostrecentlyintroduced,isalsoknownasstringformatting.Itis
similartothestringformattingdiscussedearlier,withtheaddedbenefitofnotrequiringa
specifictypeofplaceholder.Theplaceholders,ortokensastheyarealsoknown,areonly
requiredtobeinordertobeaccepted.Theformatfunctionisbuiltintostrings;byadding
.formattothestring,andpassinginparameters,thestringacceptsthevalues:
>>>formatString="Thisstringhas3tokens:{0},{1},{2}"
>>>newString=formatString.format("String",2.5,4)
>>>printnewString
Theoutputisasfollows:
Thisstringhas3tokens:String,2.5,4
Thetokensdon’thavetobeinorderwithinthestring,andcanevenberepeated.Theorder
isderivedfromtheparameterssuppliedtothe.formatfunctionthatpassesthevaluesto
thestring.
FortheIntersecttool,thestringformattingwouldlooklikethis:
filePath1=r"C:\Projects\Inbound71_400ft_buffer"
filePath2=r"C:\Projects\CensusBlocks2010"
arcpy.Intersect_analysis("{0}#;{1}#".format(filePath1,filePath2),
Intersect71Census,"ALL","","INPUT")
Thethirdmethodhasbecomemygo-tomethodforstringmanipulationbecauseofthe
abilitytoaddthevaluesrepeatedlyandmakeitpossibletoavoidsupplyingthewrongtype
ofdatatoaspecificplaceholder,unlikethesecondmethod.
AdjustingtheScript
Nowisthetimetotaketheautomaticallygeneratedscriptandadjustittofitourneeds.We
wantthescripttobothproducetheoutputdata,andtohaveitanalyzethedataandtally
theresultsintoaspreadsheet.Thisspreadsheetwillholdanaveragedpopulationvaluefor
eachbusstop.Theaveragewillbederivedfromeachcensusblockthatthebuffered
representativeregionsurroundingthestopsintersected.Savetheoriginalscriptas
Chapter3Model1Modified.py.
AddingtheCSVmoduletothescript
Forthisscript,wewillusetheCSVmodule,ausefulmoduletocreateCommaSeparated
Valuespreadsheets.Itssimplesyntaxwillmakeitausefultooltocreatescriptoutputs.It
shouldbenotedthatArcGISforDesktopalsoinstallsthexlrdandxlwtmodules,usedto
readorgenerateExcelspreadsheetsrespectively,whenitisinstalled.
JustbelowtheimportarcPyline,addimportcsv.Thiswillallowustousethecsv
moduletocreatethespreadsheet:
#Importarcpymodule
importarcpy
importcsv
ThenextadjustmentismadetotheIntersecttool.Noticethatthetwopathsincludedin
theinputstringarealsodefinedasvariablesinthevariablesection.Removethefilepaths
fromtheinputstringsandreplacethemwithnumberedplaceholdertokens,andthenadd
theformatfunctionandsupplythevariablesasplaceholders:
#Process:Intersect
arcpy.Intersect_analysis("{0}#;{1}#".format(..............
Inbound71_400ft_buffer,CensusBlocks2010),
Intersect71Census,"ALL","","INPUT")
Accessingthedata:Usingacursor
Nowthatthescriptisinplacetogeneratetherawdataweneed,weneedawaytoaccess
thedataheldintheoutputfeatureclassfromtheIntersecttool.Thisaccesswillallowus
toaggregatetherowsofdatarepresentingeachbusstop.Wealsoneedsomethingtohold
theaggregatedatainthememory,tobewrittentothespreadsheet.
Toaccomplishthesecondpart,wewilluseaPythondictionary.Toaccomplishthefirst
part,wewilluseamethodbuiltintotheArcPymodule:theDataAccessSearchCursor.
ThePythondictionarywillbeaddedbelowtheIntersecttool.AdictionaryinPythonis
createdusingcurlybrackets.Addthefollowinglinetothescript:
dataDictionary={}
ThisscriptwillusetheBusStopIDsaskeysforthedictionary.Thevalueswillbelists,
whichwillholdallofthepopulationvaluesassociatedwitheachBusStopID.Addthe
followinglinestogenerateaDataCursor:
witharcpy.da.SearchCursor(Intersect71Census,["STOPID","POP10"])as
cursor:
forrowincursor:
busStopID=row[0]
pop10=row[1]
ifbusStopIDnotindataDictionary.keys():
dataDictionary[busStopID]=[pop10]
else:
dataDictionary[busStopID].append(pop10)
ThisiterationcombinesafewideasinPythonandArcPy.Thewith…asstatementisused
tocreateavariable(cursor)thatrepresentsthearcpy.da.SearchCursorobject.Itcould
alsobewrittenlikethis:
cursor=arcpy.da.SearchCursor(Intersect71Census,["STOPID","POP10"])
Note
Theadvantageofthewith…asstructureisthatthecursorobjectiserasedfrommemory
whentheiterationiscompleted,whicheliminateslocksonthefeatureclassesbeing
evaluated.
Thearcpy.da.SearchCursor()functionrequiresaninputfeatureclass,andalistoffields
tobereturned.Optionally,aSQLstatementcanlimitthenumberofrowsreturned.
Thenextline,forrowincursor,istheiterationthroughthedata.Itisnotanormal
Pythoniciteration,adistinctionthatwillhaveramificationsincertaininstances.For
instance,however,itdoesallowforrow-by-rowaccesstodatacontainedwithinthe
suppliedfeatureclass.NotethatwhenusingaSearchCursor,eachrowofdataisreturned
asatuple,whichcannotbemodified.Thedatacanbeaccessedusingindexes,asshownin
theprecedingcode,wherethetwomembersofthetupleareassignedtovariables.
Theif/elseconditionalallowsthedatatobesorted.Asnotedearlier,theBusStopIDs,
whicharethefirstmemberofthedataincludedinthetuple,willbeusedasakey.The
conditionalevaluateswhethertheBusStopIDisincludedinthedictionary’sexistingkeys
(whicharecontainedinalistandaccessedusingthedictionary.keys()method).Ifitis
not,itisaddedtothekeys,andassignedavaluethatisalistcontaining(atfirst)onepiece
ofdata,thepopulationvaluecontainedinthatrow.Ifitdoesexistinthekeys,thelistis
appendedwiththenextpopulationvalueassociatedwiththatBusStopID.Withthiscode,
wehavenowsortedeachcensusblockpopulationaccordingtotheBusStopwithwhichit
isassociated.
Next,weneedtoaddcodetocreatethespreadsheet.Thiscodewillusethesamewith…
asstructure,andwillgenerateanaveragepopulationvaluebyusingtwobuilt-inPython
functions,sum,whichcreatesasumfromalistofnumbers,andlen,whichwillgetthe
lengthofalist,tuple,orstring:
withopen(r'C:\Projects\Output\Averages.csv','wb')ascsvfile:
csvwriter=csv.writer(csvfile,delimiter=',')
forbusStopIDindataDictionary.keys():
popList=dataDictionary[busStopID]
averagePop=sum(popList)/len(popList)
data=[busStopID,averagePop]
csvwriter.writerow(data)
TheaveragepopulationvalueisretrievedfromthedictionaryusingtheBusStopIDkey,
andthenassignedtothevariableaveragePop.Thetwodatapieces,theBusStopIDandthe
averatePopvariablearethenaddedtoalist,whichissuppliedtoaCSVwriterobject,
whichknowshowtoacceptthedataandwriteittoafilelocatedatthefilepathsupplied
tothebuilt-inPythontheopen()function,usedtocreatesimplefiles.
Thescriptiscomplete,althoughitisnicetoaddonemorelineattheendtogiveusvisual
confirmationthatthescripthasrun:
print"DataAnalysisComplete"
Thiswillcreateanoutputindicatingthatthescripthasrun.Onceitisdone,gotothe
locationoftheoutputcsvfileandopenit,usingExcelorNotepad,andseetheresultsof
theanalysis.Ourfirstscriptiscomplete!
Thefinalscript
Hereishowthescriptshouldlookintheend:
#-*-coding:utf-8-*#-------------------------------------------------------------------------#8662_Chapter3Model1.py
#Createdon:2014-04-2221:59:31.00000
#(generatedbyArcGIS/ModelBuilder)
#Description:
#-------------------------------------------------------------------------#Importarcpymodule
importarcpy
importcsv
#Localvariables:
Bus_Stops=r"C:\Projects\PacktDB.gdb\SanFrancisco\Bus_Stops"
CensusBlocks2010=r"C:\Projects\PacktDB.gdb\SanFrancisco\CensusBlocks2010"
Inbound71=r"C:\Projects\PacktDB.gdb\Chapter3Results\Inbound71"
Inbound71_400ft_buffer=
r"C:\Projects\PacktDB.gdb\Chapter3Results\Inbound71_400ft_buffer"
Intersect71Census=
r"C:\Projects\PacktDB.gdb\Chapter3Results\Intersect71Census"
#Process:Select
arcpy.Select_analysis(Bus_Stops,
Inbound71,
"NAME='71IB'ANDBUS_SIGNAG='FerryPlaza'")
#Process:Buffer
arcpy.Buffer_analysis(Inbound71,
Inbound71_400ft_buffer,
"400Feet","FULL","ROUND","NONE","")
#Process:Intersect
arcpy.Intersect_analysis("{0}#;{1}
#".format(Inbound71_400ft_buffer,CensusBlocks2010),
Intersect71Census,"ALL","","INPUT")
dataDictionary={}
witharcpy.da.SearchCursor(Intersect71Census,["STOPID","POP10"])as
cursor:
forrowincursor:
busStopID=row[0]
pop10=row[1]
ifbusStopIDnotindataDictionary.keys():
dataDictionary[busStopID]=[pop10]
else:
dataDictionary[busStopID].append(pop10)
withopen(r'C:\Projects\Output\Averages2.csv','wb')ascsvfile:
spamwriter=csv.writer(csvfile,delimiter=',')
forbusStopIDindataDictionary.keys():
popList=dataDictionary[busStopID]
averagePop=sum(popList)/len(popList)
data=[busStopID,averagePop]
spamwriter.writerow(data)
print"DataAnalysisComplete"
Summary
Inthischapter,wecoveredhowtocraftamodelofananalysisandexportittoascript.
Afterdiscussingthescript,weadjustedthescripttoincludearesultsanalysisand
summation,whichwasoutputtedtoaCSVfile.Inparticular,wediscussedhowtouse
ModelBuildertocreateananalysisandexportitasascript,andhowtoadjustthescriptto
bemorePythonic.WealsobrieflytouchedontheuseofSearchCursors,whichwillbe
coveredingreaterdetailinChapter5,ArcPyCursors–Search,Insert,andUpdate.Also,
wesawhowbuilt-inmodulessuchastheCSVmodulecanbeusedalongwithArcPyto
captureanalysisoutputinformattedspreadsheets.
Inthenextchapter,wewilldiscusshowtocreatemorecomplexscriptsandbuild
functionstoavoidrepeatingcode.Thesefunctionswillmakeitpossibletowritecodeonce
anduseitforever.ThisreuseofcodewilldemonstratehowPythongoesbeyond
automationofanalysistobecomeanewproductivitytoolset.
Chapter4.ComplexArcPyScriptsand
GeneralizingFunctions
Inthischapter,wewillmovefromcreatingsimplescriptsbasedonautogeneratedscripts
fromModelBuildertocomplexscriptsthatincorporateadvancedPythonandArcPy
concepts,suchasfunctions.Functionscanimprovecodeandsavetimewhenwriting
scripts.Theyarealsousefulwhencreatingmodulesorotherreusablecode,allowingfor
standardprogrammingoperationstobescriptedandreadyforfutureuse.
Inthischapter,willcoverthefollowingtopics:
Creatingfunctionstoavoidrepeatingcode
CreatinghelperfunctionstoworkwithArcPylimitations
Generalizingfunctionstomakethemreusable
Pythonfunctions–Avoidrepeatingcode
Programminglanguagesshareaconceptthathasaidedprogrammersfordecades:
functions.Theideaofafunction,looselyspeaking,istocreateblocksofcodethatwill
performanactiononapieceofdata,transformingitasrequiredbytheprogrammerand
returningthetransformeddatabacktothemainbodyofcode.We’vealreadybeen
introducedtosomeofPython’sbuilt-infunctionsinthelastfewchapters,theint
function,forinstance,willconvertastringorafloatingnumberintoaninteger;nowit’s
timetowriteourown.
Functionsareusedbecausetheysolvemanydifferentneedswithinprogramming.
Functionsreducetheneedtowriterepetitivecode,whichinturnreducesthetimeneeded
tocreateascript.Theycanbeusedtocreaterangesofnumbers(therange()function),or
todeterminethemaximumvalueofalist(themaxfunction),ortocreateaSQLstatement
toselectasetofrowsfromafeatureclass.Theycanevenbecopiedandusedinanother
scriptorincludedaspartofamodulethatcanbeimportedintoscripts.Functionreusehas
theaddedbonusofmakingprogrammingmoreusefulandlessofachore.Whenascripter
startswritingfunctions,itisamajorsteptowardsmakingprogrammingpartofaGIS
workflow.
Technicaldefinitionoffunctions
Functions,alsocalledsubroutinesorproceduresinotherprogramminglanguages,are
blocksofcodethathavebeendesignedtoeitheracceptinputdataandtransformit,or
providedatatothemainprogramwhencalledwithoutanyinputrequired.Intheory,
functionswillonlytransformdatathathasbeenprovidedtothefunctionasaparameter;it
shouldnotchangeanyotherpartofthescriptthathasnotbeenincludedinthefunction.
Tomakethispossible,theconceptofnamespacesisinvoked.AsdiscussedinChapter1,
IntroductiontoPythonforArcGIS,namespacesareusedtoisolatevariableswithina
script;variablesareeitherglobal,andavailabletobeusedinthemainbodyofascriptas
wellasinafunction,orarelocalandonlyavailablewithinafunction.
Namespacesmakeitpossibletouseavariablenamewithinafunction,andallowitto
representavalue,whilealsousingthesamevariablenameinanotherpartofthescript.
Thisbecomesespeciallyimportantwhenimportingmodulesfromotherprogrammers;
withinthatmoduleanditsfunctions,thevariablesthatitcontainsmighthaveavariable
namethatisthesameasavariablenamewithinthemainscript.
Inahigh-levelprogramminglanguagesuchasPython,thereisbuilt-insupportfor
functions,includingtheabilitytodefinefunctionnamesandthedatainputs(alsoknown
asparameters).Functionsarecreatedusingthekeyworddefplusafunctionname,along
withparenthesesthatmayormaynotcontainparameters.Parameterscanalsobedefined
withdefaultvalues,soparametersonlyneedtobepassedtothefunctionwhentheydiffer
fromthedefault.Thevaluesthatarereturnedfromthefunctionarealsoeasilydefined.
Afirstfunction
Let’screateafunctiontogetafeelforwhatispossiblewhenwritingfunctions.First,we
needtoinvokethefunctionbyprovidingthedefkeywordandprovidinganamealong
withtheparentheses.ThefirstFunction()willreturnastringwhencalled:
deffirstFunction():
'asimplefunctionreturningastring'
return"MyFirstFunction"
>>>firstFunction()
Theoutputisasfollows:
'MyFirstFunction'
Noticethatthisfunctionhasadocumentationstringordocstring(asimplefunction
returningastring)thatdescribeswhatthefunctiondoes;thisstringcanbecalledlaterto
findoutwhatthefunctiondoes,usingthe__doc__internalfunction:
>>>printfirstFunction.__doc__
Theoutputisasfollows:
'asimplefunctionreturningastring'
Thefunctionisdefinedandgivenaname,andthentheparenthesesareaddedfollowedby
acolon.Thefollowinglinesmustthenbeindented(agoodIDEwilladdtheindention
automatically).Thefunctiondoesnothaveanyparameters,sotheparenthesesareempty.
Thefunctionthenusesthekeywordreturntoreturnavalue,inthiscaseastring,fromthe
function.
Next,thefunctioniscalledbyaddingparenthesestothefunctionname.Whenitiscalled,
itwilldowhatithasbeeninstructedtodo:returnthestring.
Functionswithparameters
Nowlet’screateafunctionthatacceptsparametersandtransformsthemasneeded.This
functionwillacceptanumberandmultiplyitby3:
defsecondFunction(number):
'thisfunctionmultiplesnumbersby3'
returnnumber*3
>>>secondFunction(4)
Theoutputisasfollows:
12
Thefunctionhasoneflaw,however;thereisnoassurancethatthevaluepassedtothe
functionisanumber.Weneedtoaddaconditionaltothefunctiontomakesureitdoesnot
throwanexception:
defsecondFunction(number):
'thisfunctionmultiplesnumbersby3'
iftype(number)==type(1)ortype(number)==type(1.0):
returnnumber*3
>>>secondFunction(4.0)
Theoutputisasfollows:
12.0
>>>secondFunction(4)
Theoutputisasfollows:
12
>>>secondFunction("String")
>>>
Thefunctionnowacceptsaparameter,checkswhattypeofdataitis,andreturnsa
multipleoftheparameterwhetheritisanintegerorafunction.Ifitisastringorsome
otherdatatype,asshowninthelastexample,novalueisreturned.
Thereisonemoreadjustmenttothesimplefunctionthatweshoulddiscuss:parameter
defaults.Byincludingdefaultvaluesinthedefinitionofthefunction,weavoidhavingto
provideparametersthatrarelychange.If,forinstance,wewantedadifferentmultiplier
than3inthesimplefunction,wewoulddefineitlikethis:
defthirdFunction(number,multiplier=3):
'thisfunctionmultiplesnumbersby3'
iftype(number)==type(1)ortype(number)==type(1.0):
returnnumber*multiplier
>>>thirdFunction(4)
Theoutputisasfollows:
12
>>>thirdFunction(4,5)
Theoutputisasfollows:
20
Thefunctionwillworkwhenonlythenumbertobemultipliedissupplied,asthe
multiplierhasadefaultvalueof3.However,ifweneedanothermultiplier,thevaluecan
beadjustedbyaddinganothervaluewhencallingthefunction.Notethatthesecondvalue
doesn’thavetobeanumberasthereisnotypecheckingonit.Also,thedefaultvalue(s)in
afunctionmustfollowtheparameterswithnodefaults(orallparameterscanhavea
defaultvalueandtheparameterscanbesuppliedtothefunctioninorderorbyname).
Thesesimplefunctionscombinemanyoftheconceptsthatwediscussedinearlier
chapters,includingbuilt-infunctionssuchastype,conditionals,parameters,
parameterdefaults,andfunctionreturns.Wecannowmoveontocreatingfunctions
withArcPy.
Usingfunctionstoreplacerepetitivecode
Oneofthemainusesoffunctionsistoensurethatthesamecodedoesnothavetobe
writtenoverandover.Let’sreturntoourexamplefromthelastchapterandmakea
functionfromthescripttomakeitpossibletoperformthesameanalysisforanybusline
inSanFrancisco.
ThefirstportionofthescriptthatwecouldconvertintoafunctionisthethreeArcPy
functions.DoingsowillallowthescripttobeapplicabletoanyofthestopsintheBus
Stopfeatureclassandhaveanadjustablebufferdistance:
bufferDist=400
buffDistUnit="Feet"
lineName='71IB'
busSignage='FerryPlaza'
sqlStatement="NAME='{0}'ANDBUS_SIGNAG='{1}'"
defselectBufferIntersect(selectIn,selectOut,bufferOut,intersectIn,
intersectOut,sqlStatement,bufferDist,buffDistUnit,lineName,
busSignage):
'afunctiontoperformabusstopanalysis'
arcpy.Select_analysis(selectIn,selectOut,
sqlStatement.format(lineName,busSignage))
arcpy.Buffer_analysis(selectOut,bufferOut,"{0}
{1}".format(bufferDist),"FULL","ROUND","NONE","")
arcpy.Intersect_analysis("{0}#;{1}#".format(bufferOut,intersectIn),
intersectOut,"ALL","","INPUT")
returnintersectOut
Thisfunctiondemonstrateshowtheanalysiscanbeadjustedtoaccepttheinputandoutput
featureclassvariablesasparameters,alongwithsomenewvariables.
ThefunctionaddsavariabletoreplacetheSQLstatementandvariablestoadjustthebus
stop,andalsotweaksthebufferdistancestatementsothatboththedistanceandtheunit
canbeadjusted.Thefeatureclassnamevariables,definedearlierinthescript,haveall
beenreplacedwithlocalvariablenames;whiletheglobalvariablenamescouldhavebeen
retained,itreducestheportabilityofthefunction.
ThenextfunctionwillaccepttheresultoftheselectBufferIntersect()functionand
searchitusingtheSearchCursor,passingtheresultsintoadictionary.Thedictionarywill
thenbereturnedfromthefunctionforlateruse:
defcreateResultDic(resultFC):
'searchresultsofanalysisandcreateresultsdictionary'
dataDictionary={}
witharcpy.da.SearchCursor(resultFC,["STOPID","POP10"])ascursor:
forrowincursor:
busStopID=row[0]
pop10=row[1]
ifbusStopIDnotindataDictionary.keys():
dataDictionary[busStopID]=[pop10]
else:
dataDictionary[busStopID].append(pop10)
returndataDictionary
Thisfunctiononlyrequiresoneparameter:thefeatureclassreturnedfromthe
searchBufferIntersect()function.Theresultsholdingdictionaryisfirstcreated,then
populatedbythesearchcursor,withthebusStopidattributeusedasakey,andthecensus
blockpopulationattributeaddedtoalistassignedtothekey.
Thedictionary,havingbeenpopulatedwithsorteddata,isreturnedfromthefunctionfor
useinthefinalfunction,createCSV().Thisfunctionacceptsthedictionaryandthename
oftheoutputCSVfileasastring:
defcreateCSV(dictionary,csvname):
'afunctiontakesadictionaryandcreatesaCSVfile'
withopen(csvname,'wb')ascsvfile:
csvwriter=csv.writer(csvfile,delimiter=',')
forbusStopIDindictionary.keys():
popList=dictionary[busStopID]
averagePop=sum(popList)/len(popList)
data=[busStopID,averagePop]
csvwriter.writerow(data)
ThefinalfunctioncreatestheCSVusingthecsvmodule.Thenameofthefile,astring,is
nowacustomizableparameter(meaningthescriptnamecanbeanyvalidfilepathandtext
filewiththeextension.csv).ThecsvfileparameterispassedtotheCSVmodule’s
writermethodandassignedtothevariablecsvwriter,andthedictionaryisaccessedand
processed,andpassedasalisttocsvwritertobewrittentotheCSVfile.The
csv.writer()methodprocesseseachiteminthelistintotheCSVformatandsavesthe
finalresult.OpentheCSVfilewithExceloratexteditorsuchasNotepad.
Torunthefunctions,wewillcalltheminthescriptfollowingthefunctiondefinitions:
analysisResult=selectBufferIntersect(Bus_Stops,Inbound71,
Inbound71_400ft_buffer,CensusBlocks2010,Intersect71Census,bufferDist,
lineName,busSignage)
dictionary=createResultDic(analysisResult)
createCSV(dictionary,r'C:\Projects\Output\Averages.csv')
Now,thescripthasbeendividedintothreefunctions,whichreplacethecodeofthefirst
modifiedscript.Themodifiedscriptlookslikethis:
#-*-coding:utf-8-*#-------------------------------------------------------------------------#8662_Chapter4Modified1.py
#Createdon:2014-04-2221:59:31.00000
#(generatedbyArcGIS/ModelBuilder)
#Description:
#AdjustedbySilasToms
#20140505
#-------------------------------------------------------------------------#Importarcpymodule
importarcpy
importcsv
#Localvariables:
Bus_Stops=r"C:\Projects\PacktDB.gdb\SanFrancisco\Bus_Stops"
CensusBlocks2010=r"C:\Projects\PacktDB.gdb\SanFrancisco\CensusBlocks2010"
Inbound71=r"C:\Projects\PacktDB.gdb\Chapter3Results\Inbound71"
Inbound71_400ft_buffer=
r"C:\Projects\PacktDB.gdb\Chapter3Results\Inbound71_400ft_buffer"
Intersect71Census=
r"C:\Projects\PacktDB.gdb\Chapter3Results\Intersect71Census"
bufferDist=400
lineName='71IB'
busSignage='FerryPlaza'
defselectBufferIntersect(selectIn,selectOut,bufferOut,intersectIn,
intersectOut,bufferDist,lineName,busSignage):
arcpy.Select_analysis(selectIn,
selectOut,
"NAME='{0}'ANDBUS_SIGNAG=
'{1}'".format(lineName,busSignage))
arcpy.Buffer_analysis(selectOut,
bufferOut,
"{0}Feet".format(bufferDist),
"FULL","ROUND","NONE","")
arcpy.Intersect_analysis("{0}#;{1}#".format(bufferOut,intersectIn),
intersectOut,"ALL","","INPUT")
returnintersectOut
defcreateResultDic(resultFC):
dataDictionary={}
witharcpy.da.SearchCursor(resultFC,
["STOPID","POP10"])ascursor:
forrowincursor:
busStopID=row[0]
pop10=row[1]
ifbusStopIDnotindataDictionary.keys():
dataDictionary[busStopID]=[pop10]
else:
dataDictionary[busStopID].append(pop10)
returndataDictionary
defcreateCSV(dictionary,csvname):
withopen(csvname,'wb')ascsvfile:
csvwriter=csv.writer(csvfile,delimiter=',')
forbusStopIDindictionary.keys():
popList=dictionary[busStopID]
averagePop=sum(popList)/len(popList)
data=[busStopID,averagePop]
csvwriter.writerow(data)
analysisResult=selectBufferIntersect(Bus_Stops,Inbound71,
Inbound71_400ft_buffer,CensusBlocks2010,Intersect71Census,
bufferDist,lineName,busSignage)
dictionary=createResultDic(analysisResult)
createCSV(dictionary,r'C:\Projects\Output\Averages.csv')
print"DataAnalysisComplete"
Furthergeneralizationofthefunctions,whilewehavecreatedfunctionsfromtheoriginal
scriptthatcanbeusedtoextractmoredataaboutbusstopsinSanFrancisco,ournew
functionsarestillveryspecifictothedatasetandanalysisforwhichtheywerecreated.
Thiscanbeveryusefulforlongandlaboriousanalysisforwhichcreatingreusable
functionsisnotnecessary.Thefirstuseoffunctionsistogetridoftheneedtorepeat
code.Thenextgoalistothenmakethatcodereusable.Let’sdiscusssomewaysinwhich
wecanconvertthefunctionsfromone-offsintoreusablefunctionsorevenmodules.
First,let’sexaminethefirstfunction:
defselectBufferIntersect(selectIn,selectOut,bufferOut,intersectIn,
intersectOut,bufferDist,lineName,busSignage):
arcpy.Select_analysis(selectIn,
selectOut,
"NAME='{0}'ANDBUS_SIGNAG=
'{1}'".format(lineName,busSignage))
arcpy.Buffer_analysis(selectOut,
bufferOut,
"{0}Feet".format(bufferDist),
"FULL","ROUND","NONE","")
arcpy.Intersect_analysis("{0}#;{1}#".format(bufferOut,intersectIn),
intersectOut,"ALL","","INPUT")
returnintersectOut
Thisfunctionappearstobeprettyspecifictothebusstopanalysis.It’ssospecific,infact,
thatwhilethereareafewwaysinwhichwecantweakittomakeitmoregeneral(thatis,
usefulinotherscriptsthatmightnothavethesamestepsinvolved),weshouldnotconvert
itintoaseparatefunction.Whenwecreateaseparatefunction,weintroducetoomany
variablesintothescriptinanefforttosimplifyit,whichisacounterproductiveeffort.
Instead,let’sfocusonwaystogeneralizetheArcPytoolsthemselves.
ThefirststepwillbetosplitthethreeArcPytoolsandexaminewhatcanbeadjustedwith
eachofthem.TheSelecttoolshouldbeadjustedtoacceptastringastheSQLselect
statement.TheSQLstatementcanthenbegeneratedbyanotherfunctionorbyparameters
acceptedatruntime(forexample,passedtothescriptbyaScripttool,whichwillbe
discussedinalaterchapter).
Forinstance,ifwewantedtomakethescriptacceptmultiplebusstopsforeachrunofthe
script(forexample,theinboundandoutboundstopsforeachline),wecouldcreatea
functionthatwouldacceptalistofthedesiredstopsandaSQLtemplate,andwouldreturn
aSQLstatementtoplugintotheSelecttool.Hereisanexampleofhowitwouldlook:
defformatSQLIN(dataList,sqlTemplate):
'afunctiontogenerateaSQLstatement'
sql=sqlTemplate#"OBJECTIDIN"
step="("
fordataindataList:
step+=str(data)
sql+=step+")"
returnsql
defformatSQL(dataList,sqlTemplate):
'afunctiontogenerateaSQLstatement'
sql=''
forcount,datainenumerate(dataList):
ifcount!=len(dataList)-1:
sql+=sqlTemplate.format(data)+'OR'
else:
sql+=sqlTemplate.format(data)
returnsql
>>>dataVals=[1,2,3,4]
>>>sqlOID="OBJECTID={0}"
>>>sql=formatSQL(dataVals,sqlOID)
>>>printsql
Theoutputisasfollows:
OBJECTID=1OROBJECTID=2OROBJECTID=3OROBJECTID=4
Thisnewfunction,formatSQL(),isaveryusefulfunction.Let’sreviewwhatitdoesby
comparingthefunctiontotheresultsfollowingit.Thefunctionisdefinedtoaccepttwo
parameters:alistofvaluesandaSQLtemplate.Thefirstlocalvariableistheemptystring
sql,whichwillbeaddedtousingstringaddition.Thefunctionisdesignedtoinsertthe
valuesintothevariablesql,creatingaSQLstatementbytakingtheSQLtemplateand
usingstringformattingtoaddthemtothetemplate,whichinturnisaddedtotheSQL
statementstring(notethatsql+=isequivelenttosql=sql+).Also,anoperator(OR)is
usedtomaketheSQLstatementinclusiveofalldatarowsthatmatchthepattern.This
functionusesthebuilt-inenumeratefunctiontocounttheiterationsofthelist;onceithas
reachedthelastvalueinthelist,theoperatorisnotaddedtotheSQLstatement.
Notethatwecouldalsoaddonemoreparametertothefunctiontomakeitpossibletouse
anANDoperatorinsteadofOR,whilestillkeepingORasthedefault:
defformatSQL2(dataList,sqlTemplate,operator="OR"):
'afunctiontogenerateaSQLstatement'
sql=''
forcount,datainenumerate(dataList):
ifcount!=len(dataList)-1:
sql+=sqlTemplate.format(data)+operator
else:
sql+=sqlTemplate.format(data)
returnsql
>>>sql=formatSQL2(dataVals,sqlOID,"AND")
>>>printsql
Theoutputisasfollows:
OBJECTID=1ANDOBJECTID=2ANDOBJECTID=3ANDOBJECTID=4
WhileitwouldmakenosensetouseanANDoperatoronObjectIDs,thereareothercases
whereitwouldmakesense,henceleavingORasthedefaultwhileallowingforAND.Either
way,thisfunctioncannowbeusedtogenerateourbusstopSQLstatementformultiple
stops(ignoring,fornow,thebussignagefield):
>>>sqlTemplate="NAME='{0}'"
>>>lineNames=['71IB','71OB']
>>>sql=formatSQL2(lineNames,sqlTemplate)
>>>printsql
Theoutputisasfollows:
NAME='71IB'ORNAME='71OB'
However,wecan’tignoretheBusSignagefieldfortheinboundline,astherearetwo
startingpointsfortheline,sowewillneedtoadjustthefunctiontoacceptmultiplevalues:
defformatSQLMultiple(dataList,sqlTemplate,operator="OR"):
'afunctiontogenerateaSQLstatement'
sql=''
forcount,datainenumerate(dataList):
ifcount!=len(dataList)-1:
sql+=sqlTemplate.format(*data)+operator
else:
sql+=sqlTemplate.format(*data)
returnsql
>>>sqlTemplate="(NAME='{0}'ANDBUS_SIGNAG='{1}')"
>>>lineNames=[('71IB','FerryPlaza'),('71OB','48thAvenue')]
>>>sql=formatSQLMultiple(lineNames,sqlTemplate)
>>>printsql
Theoutputisasfollows:
(NAME='71IB'ANDBUS_SIGNAG='FerryPlaza')OR(NAME='71OB'AND
BUS_SIGNAG='48thAvenue')
Theslightdifferenceinthisfunction,theasteriskbeforethedatavariable,allowsthe
valuesinsidethedatavariabletobecorrectlyformattedintotheSQLtemplateby
explodingthevalueswithinthetuple.NoticethattheSQLtemplatehasbeencreatedto
segregateeachconditionalbyusingparentheses.Thefunction(s)arenowreadyforreuse,
andtheSQLstatementisnowreadyforinsertionintotheSelecttool:
sql=formatSQLMultiple(lineNames,sqlTemplate)
arcpy.Select_analysis(Bus_Stops,Inbound71,sql)
NextupistheBuffertool.Wehavealreadytakenstepstowardsmakingitgeneralizedby
addingavariableforthedistance.Inthiscase,wewillonlyaddonemorevariabletoit,a
unitvariablethatwillmakeitpossibletoadjustthebufferunitfromfeettometerorany
otherallowedunit.Wewillleavetheotherdefaultsalone.
HereisanadjustedversionoftheBuffertool:
bufferDist=400
bufferUnit="Feet"
arcpy.Buffer_analysis(Inbound71,
Inbound71_400ft_buffer,
"{0}{1}".format(bufferDist,bufferUnit),
"FULL","ROUND","NONE","")
Now,boththebufferdistanceandbufferunitarecontrolledbyavariabledefinedinthe
previousscript,andthiswillmakeiteasilyadjustableifitisdecidedthatthedistancewas
notsufficientandthevariablesmightneedtobeadjusted.
ThenextsteptowardsadjustingtheArcPytoolsistowriteafunction,whichwillallowfor
anynumberoffeatureclassestobeintersectedtogetherusingtheIntersecttool.Thisnew
functionwillbesimilartotheformatSQLfunctionsasprevious,astheywillusestring
formattingandadditiontoallowforalistoffeatureclassestobeprocessedintothe
correctstringformatfortheIntersecttooltoacceptthem.However,asthisfunctionwill
bebuilttobeasgeneralaspossible,itmustbedesignedtoacceptanynumberoffeature
classestobeintersected:
defformatIntersect(features):
'afunctiontogenerateanintersectstring'
formatString=''
forcount,featureinenumerate(features):
ifcount!=len(features)-1:
formatString+=feature+"#;"
else:
formatString+=feature+"#"
returnformatString
>>>shpNames=["example.shp","example2.shp"]
>>>iString=formatIntersect(shpNames)
>>>printiString
Theoutputisasfollows:
example.shp#;example2.shp#
NowthatwehavewrittentheformatIntersect()function,allthatneedstobecreatedis
alistofthefeatureclassestobepassedtothefunction.Thestringreturnedbythefunction
canthenbepassedtotheIntersecttool:
intersected=[Inbound71_400ft_buffer,CensusBlocks2010]
iString=formatIntersect(intersected)
#Process:Intersect
arcpy.Intersect_analysis(iString,
Intersect71Census,"ALL","","INPUT")
Becauseweavoidedcreatingafunctionthatonlyfitsthisscriptoranalysis,wenowhave
two(ormore)usefulfunctionsthatcanbeappliedinlateranalyses,andweknowhowto
manipulatetheArcPytoolstoacceptthedatathatwewanttosupplytothem.
Moregeneralizationofthefunctions
Theotherfunctionsthatweinitiallycreatedtosearchtheresults,andgeneratethe
spreadsheetofresults,canalsobemanipulatedintobeingmoregeneralizedwithafew
tweaks.
Ifwewanttogeneratemoreinformationabouteachcensusblockwithinadistancetoa
busstop(forexample,ifwehadacensusblockdatasetwithincomedataaswellas
populationdata),wewouldpasstothefunctionalistofattributestobeextractedfromthe
finalfeatureclass.Tomakethispossible,itwouldbenecessarytoadjustthe
createResultDic()functiontoacceptthislistofattributes:
defcreateResultDic(resultFC,key,values):
dataDictionary={}
fields=[key]
fields.extend(values)
witharcpy.da.SearchCursor(resultFC,fields)ascursor:
forrowincursor:
busStopID=row[0]
data=row[1:]
ifbusStopIDnotindataDictionary.keys():
dataDictionary[busStopID]=[data]
else:
dataDictionary[busStopID].append(data)
returndataDictionary
ThisnewversionofthecreateResultDic()functionwillgeneratealistoflists(thatis,
thevaluesfromeachrowarecontainedwithinalistandareaddedtoamasterlist)for
eachbusstop,whichcanthenbeparsedlaterbyknowingthepositionofeachvalueinthe
list.Thissolutionisusefulwhenneedingtosortdataintoadictionary.
However,thisisanunsatisfactorywaytosorttheresults.Whatifthelistoffieldsisnot
passedontothedictionaryandthereisnowayofknowingtheorderofthedatainthe
lists?Instead,wewanttobeabletousethefunctionalityofPythondictionariestosortthe
databyfieldname.Inthiscase,wewillusenesteddictionariestocreatelistsofresults
accessiblebythetypeofdatatheycontain(thatis,population,income,oranotherfield):
defcreateResultDic(resultFC,key,values):
dataDic={}
fields=[]
iftype(key)==type((1,2))ortype(key)==type([1,2]):
fields.extend(key)
length=len(key)
else:
fields=[key]
length=1
fields.extend(values)
witharcpy.da.SearchCursor(resultFC,fields)ascursor:
forrowincursor:
busStopID=row[:length]
data=row[length:]
ifbusStopIDnotindataDictionary.keys():
dataDictionary[busStopID]={}
forcounter,fieldinenumerate(values):
iffieldnotindataDictionary[busStopID].keys():
dataDictionary[busStopID][field]=[data[counter]]
else:
dataDictionary[busStopID][field].append(data[counter])
returndataDictionary
>>>rFC=r'C:\Projects\PacktDB.gdb\Chapter3Results\Intersect71Census'
>>>key='STOPID'
>>>values='HOUSING10','POP10'
>>>dic=createResultDic(rFC,key,values)
>>>dic[1122023]
Theoutputisasfollows:
{'HOUSING10':[104,62,113,81,177,0,52,113,0,104,81,177,52],
'POP10':[140,134,241,138,329,0,118,241,0,140,138,329,118]}
Inthisexample,thefunctionispassedasparameterstoafeatureclass,theSTOPID,andthe
fieldstobeconglomerated.Thefieldsvariableiscreatedtopasstherequiredfieldsonto
theSearchCursor.Thecursorreturnseachrowasatuple;thefirstmemberofthetupleis
busStopID,andtherestofthetupleisthedataassociatedwiththatbusstop.Thefunction
thenusesaconditiontoassesswhetherthebusstophasbeenpreviouslyanalyzed;ifnot,it
isaddedtothedictionaryandassignedasecondinternaldictionary,whichwillbeusedto
storetheresultsassociatedwiththatstop.Byusingadictionary,wecanthensortthrough
theresultsandassignthemtothecorrectfieldtowhichtheybelong.
Thepreviousexampleshowstheresultsofrequestingdataforoneparticularbusstop
(1122023).Astherearetwofieldspassedhere,thedatahasbeenorganizedintotwosets,
andthefieldnamesarenowkeysfortheinternaldictionary.Becauseofthisorganization,
wecannowcreateaveragesforeachfieldinsteadofjustone.
Speakingofaverages,weleftthejobofaveragingtheresultsofthesearchcursoranalysis
tothecreateCSV()function.Thisshouldalsobeavoided,asitreducestheusefulnessof
thecreateCSV()functionbyaddingadditionaldatamanipulationdutiesthatshouldbethe
responsibilityofanotherfunction.Let’saddressthisissuebyadjustingthecreateCSV()
functionfirst:
defcreateCSV(data,csvname,mode='ab'):
withopen(csvname,mode)ascsvfile:
csvwriter=csv.writer(csvfile,delimiter=',')
csvwriter.writerow(data)
Thisisastrippeddownversionofthefunction,butitisinfinitelymoreuseful.By
adjustingthefunctionlikethis,wearelimitingittoonlydoingtwothings:openingthe
CSVfileandaddingarowofdatatoit.Becauseweusedtheabmode,iftheCSVfile
exists,wewillonlybeaddingdatatoitinsteadofwritingoverit(ifitdoesn’texist,itwill
becreated).Thisaddingmodecanbeoverriddenbypassingwbasthemode,whichwill
generateanewscripteachtime.
Nowwecansortthroughtheresultsoftheanalysis,averagethem,andpassthemtoour
newcreateCSVscript.Todothis,wewilliteratethroughthedictionarycreatedbythe
createResultDic()function:
csvname=r'C:\Projects\Output\Averages.csv'
dataKey='STOPID'
fields='HOUSING10','POP10'
dictionary=createResultDic(Intersect71Census,dataKey,fields)
header=[dataKey]
forfieldinfields:
header.append(field)
createCSV(header,csvname,'wb')
forcounter,busStopinenumerate(dictionary.keys()):
datakeys=dictionary[busStop]
averages=[busStop]
forkeyindatakeys:
data=datakeys[key]
average=sum(data)/len(data)
averages.append(average)
createCSV(averages,csvname)
ThislaststepshowshowtheCSVfileiscreated:byiteratingthroughthedatacontainedin
thedictionaryandthenaveragingthevaluesforeachbusstop.Then,theseaveragesare
addedtoalistthatcontainsthenameofeachbusstop(andthelineitbelongstointhis
instance)andpassedtothecreateCSV()functiontobewrittenintotheCSVfile.
Hereisthefinalcode.NotethatIhaveconvertedmanyoftheautogeneratedcomments
intoprintstatementstogivesomefeedbackonthestateofthescript:
#-*-coding:utf-8-*#-------------------------------------------------------------------------#8662_Chapter4Modified2.py
#Createdon:2014-04-2221:59:31.00000
#(generatedbyArcGIS/ModelBuilder)
#Description:
#AdjustedbySilasToms
#20140423
#-------------------------------------------------------------------------#Importarcpymodule
importarcpy
importcsv
Bus_Stops=r"C:\Projects\PacktDB.gdb\SanFrancisco\Bus_Stops"
CensusBlocks2010=r"C:\Projects\PacktDB.gdb\SanFrancisco\CensusBlocks2010"
Inbound71=r"C:\Projects\PacktDB.gdb\Chapter4Results\Inbound71"
Inbound71_400ft_buffer=
r"C:\Projects\PacktDB.gdb\Chapter4Results\Inbound71_400ft_buffer"
Intersect71Census=
r"C:\Projects\PacktDB.gdb\Chapter4Results\Intersect71Census"
bufferDist=400
bufferUnit="Feet"
lineNames=[('71IB','FerryPlaza'),('71OB','48thAvenue')]
sqlTemplate="NAME='{0}'ANDBUS_SIGNAG='{1}'"
intersected=[Inbound71_400ft_buffer,CensusBlocks2010]
dataKey='NAME','STOPID'
fields='HOUSING10','POP10'
csvname=r'C:\Projects\Output\Averages.csv'
defformatSQLMultiple(dataList,sqlTemplate,operator="OR"):
'afunctiontogenerateaSQLstatement'
sql=''
forcount,datainenumerate(dataList):
ifcount!=len(dataList)-1:
sql+=sqlTemplate.format(*data)+operator
else:
sql+=sqlTemplate.format(*data)
returnsql
defformatIntersect(features):
'afunctiontogenerateanintersectstring'
formatString=''
forcount,featureinenumerate(features):
ifcount!=len(features)-1:
formatString+=feature+"#;"
else:
formatString+=feature+"#"
returnformatString
defcreateResultDic(resultFC,key,values):
dataDictionary={}
fields=[]
iftype(key)==type((1,2))ortype(key)==type([1,2]):
fields.extend(key)
length=len(key)
else:
fields=[key]
length=1
fields.extend(values)
witharcpy.da.SearchCursor(resultFC,fields)ascursor:
forrowincursor:
busStopID=row[:length]
data=row[length:]
ifbusStopIDnotindataDictionary.keys():
dataDictionary[busStopID]={}
forcounter,fieldinenumerate(values):
iffieldnotindataDictionary[busStopID].keys():
dataDictionary[busStopID][field]=[data[counter]]
else:
dataDictionary[busStopID][field].append(data[counter])
returndataDictionary
defcreateCSV(data,csvname,mode='ab'):
withopen(csvname,mode)ascsvfile:
csvwriter=csv.writer(csvfile,delimiter=',')
csvwriter.writerow(data)
sql=formatSQLMultiple(lineNames,sqlTemplate)
print'Process:Select'
arcpy.Select_analysis(Bus_Stops,
Inbound71,
sql)
print'Process:Buffer'
arcpy.Buffer_analysis(Inbound71,
Inbound71_400ft_buffer,
"{0}{1}".format(bufferDist,bufferUnit),
"FULL","ROUND","NONE","")
iString=formatIntersect(intersected)
printiString
print'Process:Intersect'
arcpy.Intersect_analysis(iString,
Intersect71Census,"ALL","","INPUT")
print'ProcessResults'
dictionary=createResultDic(Intersect71Census,dataKey,fields)
print'CreateCSV'
header=[dataKey]
forfieldinfields:
header.append(field)
createCSV(header,csvname,'wb')
forcounter,busStopinenumerate(dictionary.keys()):
datakeys=dictionary[busStop]
averages=[busStop]
forkeyindatakeys:
data=datakeys[key]
average=sum(data)/len(data)
averages.append(average)
createCSV(averages,csvname)
print"DataAnalysisComplete"
Summary
Inthischapter,wediscussedhowtotakeautogeneratedcodeandmakeitgeneralized,
whileaddingfunctionsthatcanbereusedinotherscriptsandwillmakethegenerationof
thenecessarycodecomponents,suchasSQLstatements,mucheasier.Wealsoaddressed
whenitisbestnottogotoofarwiththecreationoffunctionstoavoidmakingthemtoo
specific.
Inthenextchapter,wewillinvestigatethepowerfulDataAccessmoduleanditsSearch
Cursors,UpdateCursors,andInsertCursors.
Chapter5.ArcPyCursors–Search,
Insert,andUpdate
NowthatweunderstandhowtointeractwithArcToolboxtoolsusingArcPy,andwehave
alsocoveredusingPythontocreatefunctionsandimportmodules,wehaveabasic
understandingofhowtoimproveGISworkflowsusingPython.Inthischapterwewill
coverdatacursorsandtheDataAccessmodule,introducedin10.1.Thesedataaccess
cursorsareavastimprovementonthecursorsusedinthearcgisscriptingmodule(the
precursortoArcPy)andinearlierversionsofArcPy.Notonlycanthecursorssearchdata,
aswehaveseen,buttheycanupdatedatausingtheUpdateCursorsandcanaddnewrows
ofdatausingtheInsertCursor.
Datacursorsareusedtoaccessdatarecordscontainedwithindatatables,usingarowby
rowiterativeapproach.Theconceptwasborrowedfromrelationaldatabases,wheredata
cursorsareusedtoextractdatafromtablesreturnedfromaSQLexpression.Cursorsare
usedtosearchfordata,butalsotoupdatedataortoaddnewdata.
WhenwediscusscreatingdatasearchesusingArcPycursors,wearenotjusttalkingabout
attributeinformation.Thenewdataaccessmodelcursorscaninteractdirectlywiththe
shapefield,andwhencombinedwithArcPyGeometryobjects,canperformgeospatial
functionsandreplacetheneedtopassdatatoArcToolboxtools.Dataaccesscursors
representthemostusefulinnovationyetintherealmofPythonautomationforGIS.
Inthischapterwewillcover:
UsingSearchCursorstoaccessattributeandspatialdata
UsingUpdateCursorstoadjustvalueswithinrows
Usinginsertcursorstoaddnewdatatoadataset
UsingcursorsandtheArcPyGeometryobjecttypestoperformgeospatialanalysesin
memory
Thedataaccessmodule
IntroducedwiththereleaseofArcGIS10.1,thenewdataaccessmoduleknownas
arcpy.dahasmadedatainteractioneasier,andfaster,thanallowedbypreviousdata
cursors.Byallowingfordirectaccesstotheshapefieldinavarietyofforms(shape
object,Xvalues,Yvalues,centroid,area,length,andmore),andavarietyofformats
(JavaScriptObjectNotation(JSON),KeyholeMarkupLanguage(KML),WellKnown
Binary(WKB),Well-KnownText(WKT)),thedataaccessmodulegreatlyincreasesthe
abilityofaGISanalysttoextractandcontrolshapefielddata.
Thedataaccesscursorsacceptanumberofrequiredandoptionalparameters.The
requiredparametersarethepathtothefeatureclassasastring(oravariablerepresenting
thepath)andthefieldstobereturned.Ifallfieldsaredesired,usingtheasterisknotation
andprovidealistwithanasteriskasastringasthefield’sparameter([*]).Ifonlyafew
fieldsarerequired,providethosefieldsasstringfieldnames(forexample[“NAME”,
“DATE”]).
Theotherparametersareoptionalbutareveryimportant,forbothsearchandUpdate
Cursors.AwhereclauseintheformofaSQLexpressioncanbeprovidednext;thisclause
willlimitthenumberofrowsreturnedfromthedataset(asdemonstratedbytheSQL
expressioninthescriptsinthelastchapter).TheSQLexpressionsusedbythesearchand
updatecursorsarenotcompleteSQLexpressions,astheSELECTorUPDATEcommandsare
providedautomaticallybythechoiceofcursor.OnlythewhereclauseoftheSQL
expressionisrequiredforthisparameter.
AspatialreferencecanbeprovidednextintheArcPySpatialReferenceformat;thisisnot
necessaryifthedataisinthecorrectformatbutcanbeusedtotransformdataintoanother
projectiononthefly.Thereisnowaytospecifythespatialtransformationused,however.
ThethirdoptionalparameterisaBoolean(orTrue/False)valuethatdeclareswhetherdata
shouldbereturnedinexplodedpoints(thatis,alistoftheindividualvertices)orinthe
originalgeometryformat.Thefinaloptionalparameterisanotherlistthatcanbeusedto
organizethedatareturnedbythecursor;thislistwouldincludeSQLkeywordssuchas
DISTINCT,ORBERBY,orGROUPBY.However,thisfinalparameterisonlyavailable
whenworkingwithageodatabase.
Let’stakealookatusingarcpy.da.SearchCursorforshapefieldinteractions.Ifwe
neededtoproduceaspreadsheetlistingallbusstopsalongaparticularroute,andinclude
thelocationofthedatainanX/Yformat,wecouldusetheAddXYtoolfromthe
ArcToolbox.However,thishastheeffectofaddingtwonewfieldstoourdata,whichis
notalwaysallowed,especiallywhenthedataisstoredinenterprisegeodatabaseswith
fixedschemas.Instead,we’llusetheSHAPE@XYtokenbuiltintothedataaccessmodule
toeasilyextractthedataandpassittothecreateCSV()functionfromChapter4,Complex
ArcPyScriptsandGeneralizingFunctions,alongwiththeSQLexpressionlimitingresults
tothestopsofinterest:
csvname="C:\Projects\Output\StationLocations.csv"
headers='BusLineName','BusStopID','X','Y'
createCSV(headers,csvname,'wb')
sql="(NAME='71IB'ANDBUS_SIGNAG='FerryPlaza')OR(NAME='71OB'
ANDBUS_SIGNAG='48thAvenue')"
witharcpy.da.SearchCursor(Bus_Stops,['NAME','STOPID','SHAPE@XY'],sql)
ascursor:
forrowincursor:
linename=row[0]
stopid=row[1]
locationX=row[2][0]
locationY=row[2][1]
locationY=row[2][1]
data=linename,stopid,locationX,locationY
createCSV(data,csvname)
Notethateachrowofdataisreturnedasatuple;thismakessenseastheSearchCursor
doesnotallowanydatamanipulationandtuplesareimmutableassoonastheyare
created.Incontrast,datareturnedfromUpdateCursorsisinlistformat,aslistscanbe
updated.Bothcanbeaccessedusingtheindexingasshownpreviously.
Eachrowreturnedbythecursorisatuplewiththreeobjects:thenameofthebusstop,the
busstopID,andfinallyanothertuplecontainingtheX/Ylocationofthestop.Theobjects
inthetuple,containedinthevariablerow,areaccessibleusingindexing:thebusstop
nameisatindex0,theIDisatindex1,andthelocationtupleisatindex2.
Withinthelocationtuple,theXvalueisatindex0andtheYvalueisatindex1;this
makesiteasytoaccessthedatainthelocationtuplebypassingavalueasshowninthe
following:
locationX=row[2][0]
Theabilitytoaddlistsandtuplesandevendictionariestoanotherlistortupleor
dictionaryisastrongcomponentofPython,makingdataaccesslogicalanddata
organizationeasy.
However,thespreadsheetreturnedfromthepreviouscodehasafewissues:thelocationis
returnedinthenativeprojectionofthefeatureclass(inthiscase,aStatePlaneprojection),
andtherearerowsofdatathatarerepeated.Itwouldbemuchmorehelpfulifwecould
providelatitudeandlongitudevaluesinthespreadsheetandtheduplicatevalueswere
removed.Let’susetheoptionalspatialreferenceparameterandalisttosortthedata
beforewepassittothecreateCSV()function:
spatialReference=arcpy.SpatialReference(4326)
sql="(NAME='71IB'ANDBUS_SIGNAG='FerryPlaza')OR(NAME='71OB'
ANDBUS_SIGNAG='48thAvenue')"
dataList=[]
witharcpy.da.SearchCursor(Bus_Stops,['NAME','STOPID','SHAPE@XY'],sql,
spatialReference)ascursor:
forrowincursor:
linename=row[0]
stopid=row[1]
locationX=row[2][0]
locationY=row[2][1]
data=linename,stopid,locationX,locationY
ifdatanotindataList:
dataList.append(data)
csvname="C:\Projects\Output\StationLocations.csv"
headers='BusLineName','BusStopID','X','Y'
createCSV(headers,csvname,'wb')
fordataindataList:
Thespatialreferenceiscreatedbypassingacoderepresentingthedesiredprojection
system.InthiscasethecodefortheWGS1984LatitudeandLongitudegeographicsystem
is4326andispassedtothearcpy.SpatialReference()methodtocreateaspatial
referenceobjectthatcanbepassedtotheSearchCursor.Also,theifconditionalisused
tofilterthedata,acceptingonlyonelistperstopintothelistcalleddataList.Thisnew
versionofthecodewillproduceaCSVfilewiththedesireddata.ThisCSVcouldthenbe
convertedintoaKMLwiththeserviceprovidedbywww.convertcsv.com/csv-to-kml.htm,
orevenbetter,usingPython.UsestringformattingandloopstoinsertthedataintoprebuiltKMLstrings.
Attributefieldinteractions
Apartfromtheshapefieldinteractions,anotherimprovementofferedbythedataaccess
modulecursorsistheabilitytocallthefieldsinafeatureclassbyusingalist,asdiscussed
previously.Earlierdatacursorsrequiredtheuseofalessefficientgetvaluefunctioncall,
orrequiredthefieldstobecalledasiftheyweremethodsavailabletothefunction.The
newmethodallowsforallfieldstobecalledbypassinganasterisk,avaluablemethodto
accessfieldsinfeatureclassesthathavenotbeeninspectedpreviously.
OneofthemorevaluableimprovementsistheabilitytoaccesstheUniqueIDfield
withoutneedingtoknowwhetherthedatasetisafeatureclassorashapefile.Because
shapefileshadafeatureIDorFID,andfeatureclasseshadanobjectID,itwasharderto
programaScripttooltoaccesstheuniqueIDfield.Dataaccessmodulecursorsallowfor
theuseoftheOID@stringtorequesttheuniqueIDfromeithertypeofinput.Thismakes
theneedtoknowthetypeofuniqueIDirrelevant.
Asdemonstratedpreviously,otherattributefieldsarerequestedbyastringinalist.The
fieldnamesmustmatchthetruenameofthefield;aliasnamescannotbepassedtothe
cursor.Thefieldscanbeinthelistinanyorderdesired,andwillbereturnedintheorder
requested.Onlytherequiredfieldshavetobeincludedinthelist.
Hereisademonstrationofrequestingfieldinformation:
sql="OBJECTID=1"
witharcpy.da.SearchCursor(Bus_Stops,
['STOPID','NAME','OID@'],
sql)ascursor:
forrowincursor:
Ifthefieldsinthefieldslistwereadjusted,thedataintheresultingrowwouldreflectthe
adjustment.Also,allofthemembersofthetuplereturnedbythecursorareaccessibleby
zero-basedindexing.
Updatecursors
Updatecursorsareusedtoadjustdatawithinexistingrowsofdata.Updatesbecomevery
importantwhencalculatingdataorconvertingnullvaluestoanon-nullvalue.Combined
withspecificSQLexpressions,datacanbetargetedforupdatingwithnewlycollectedor
calculatedvalues.
NotethatrunningcodecontaininganUpdateCursorwillchange,orupdate,thedataon
whichitoperates.Itisagoodideatomakeacopyofthedatatotestoutthecodebefore
runningitontheoriginaldata.
AlldataaccessmoduleSearchCursorparametersdiscussedpreviouslyarevalidfor
UpdateCursors.ThemaindifferenceisthatdatarowsreturnedbyUpdateCursorsare
returnedaslists.Becauselistsaremutable,theycanbeadjustedusingalistvalue
assignment.
Asanexample,let’simaginethatthebusline71willberenamedtothe75.Bothinbound
andoutboundlineswillbeaffected,soaSQLexpressionmustbeincludedtogetallrows
ofdataassociatedwiththeline.Oncethedatacursoriscreated,therowsreturnedmust
havethenameadjusted,addedbackintothelist,andtheUpdatecursor’supdateRow
methodmustbeinvoked.Hereishowthisscenariowouldlookincode:
sql="NAMELIKE'71%'"
witharcpy.da.UpdateCursor(Bus_Stops,['NAME'],sql),)ascursor:
forrowincursor:
lineName=row[0]
newName=lineName.replace('71','75')
row[0]=newName
TheSQLexpressionwillreturnallrowsofdatawithanamestartingwith71;thiswill
include71IBand71OB.NotethattheSQLexpressionmustbeenclosedindouble
quotes,astheattributevalueneedstobeinsinglequotes.
Foreachrowofdata,thenameatpositionzerointherowreturnedisassignedtothe
variablelineName.Thisvariable,astring,usesthereplace()methodtoreplacethe
characters71withthecharacters75.Thiscouldalsojustbereplacing1with5butI
wantedtobeexplicitastowhatisbeingreplaced.
Oncethenewstringhasbeengenerated,itisassignedtothevariablenewName.This
variableisthenaddedtothelistreturnedbythecursorusinglistassignment;thiswill
replacethedatavaluethatinitiallyoccupiedthezeropositioninthelist.Oncetherow
valuehasbeenassigned,itisthenpassedtothecursor’supdateRow()method.This
methodacceptstherowandupdatesthevalueinthefeatureclassforthatparticularrow.
Updatingtheshapefield
Foreachrow,allvaluesincludedinthelistreturnedbythecursorareavailableforupdate,
excepttheuniqueID(whilenoexceptionwillbethrown,theUIDvalueswillnotbe
updated).Eventheshapefieldcanbeadjusted,withafewcaveats.Themaincaveatisthat
theupdatedshapefieldmustbethesamegeometrytypeastheoriginalrow,apointcanbe
replacedwithapoint,alinewithaline,andapolygonwithanotherpolygon.
Adjustingapointlocation
Ifabusstopwasmoveddownthestreetfromitscurrentposition,itwouldneedtobe
updatedusinganUpdateCursor.ThisoperationwillrequireanewlocationinanX/Y
format,preferablyinthesameprojectionasthefeatureclasstoavoidanylossoflocation
fidelityinaspatialtransformation.Therearetwomethodsavailabletousforcreatinga
newpointlocation,dependingonthemethodusedtoaccessthedata.Thefirstmethodis
usedwhenthelocationdataisrequestedusingtheSHAPE@tokens,andrequirestheuseof
anArcPyGeometrytype,inthiscasethePointtype.TheArcPyGeometrytypesare
discussedindetailinthenextchapter.
sql='OBJECTID<5'
witharcpy.da.UpdateCursor(Bus_Stops,['OID@','SHAPE@'],sql)ascursor:
forrowincursor:
row[1]=arcpy.Point(5999783.78657,2088532.563956)
BypassinganXandYvaluetotheArcPyPointGeometry,aPointshapeobjectiscreated
andpassedtothecursorintheupdatedlistreturnedbythecursor.Assigninganew
locationtotheshapefieldinatuple,thenusingthecursor’supdateRow()methodallows
theshapefieldvaluetobeadjustedtothenewlocation.Becausethefirstfourbusstops
areatthesamelocation,theyareallmovedtothenewlocation.
Thesecondmethodappliestoallotherformsofshapefieldinteractions,includingthe
SHAPE@XY,SHAPE@JSON,SHAPE@KML,SHAPE@WKT,[email protected]
bypassingthenewlocationintheformatrequestedbacktothecursorandupdatingthe
list:
sql='OBJECTID<5'
witharcpy.da.UpdateCursor(Bus_Stops,['OID@','SHAPE@XY'],sql)ascursor:
forrowincursor:
row[1]=(5999783.786500007,2088532.5639999956)
HereisthesamecodeusingtheSHAPE@JSONkeywordandaJSONrepresentationofthe
data:
sql='OBJECTID<5'
witharcpy.da.UpdateCursor(Bus_Stops,['OID@','SHAPE@JSON'],sql)as
cursor:
forrowincursor:
printrow
row[1]=u'{"x":5999783.7865000069,"y":2088532.5639999956,
"spatialReference":{"wkid":102643}}'
Aslongasthekeyword,thedataformat,andthegeometrytypematch,thelocationis
updatedtothenewcoordinates.Thekeywordmethodisveryusefulwhenupdatingpoints,
however,theSHAPE@XYkeyworddoesnotworkwithlinesorpolygonsasthelocation
returnedrepresentsthecentroidoftherequestedgeometry.
DeletingarowusinganUpdateCursor
Ifweneedtoremovearowofdata,theUpdateCursorhasadeleteRowmethodthatworks
toremovetherow.Notethatthiswillcompletelyremovethedatarow,makingit
unrecoverable.Thismethoddoesnotrequireaparametertobepassedtoit;instead,itwill
removethecurrentrow:
sql='OBJECTID<2'
Bus_Stops=r'C:\Projects\PacktDB.gdb\Bus_Stops'
witharcpy.da.UpdateCursor(Bus_Stops,
['OID@',
'SHAPE@XY'],sql)ascursor:
forrowincursor:
UsinganInsertCursor
Nowthatwehaveagrasponhowtoupdateexistingdata,let’sinvestigateusingInsert
Cursorstocreatenewdataandaddittoafeatureclass.Themethodsinvolvedarevery
similartousingotherdataaccesscursors,exceptthatwedonotneedtocreateaniterable
cursortoextractrowsofdata;instead,wewillcreateacursorthatwillhavethespecial
insertRowmethodthatiscapableofaddingdatatothefeatureclassrowbyrow.
TheInsertCursorcanbecalledusingthesamewith..assyntaxbutgenerallyitiscreated
asavariableintheflowofthescript.
Note
Notethatonlyonecursorcanbeinvokedatatime;anexception(aPythonerror)willbe
generatedwhencreatingtwoinsert(orupdate)cursorswithoutfirstremovingtheinitial
cursorusingthePythondelkeywordtoremovethecursorvariablefrommemory.Thisis
whythewith..assyntaxispreferredbymany.
Thedataaccessmodule’sInsertCursorrequiressomeofthesameparametersastheother
cursors.Thefeatureclasstobewrittentoandthelistoffieldsthatwillhavedatainserted
(thisincludestheshapefield)arerequired.Thespatialreferencewillnotbeusedasthe
newshapedatamustbeinthesamespatialreferenceasthefeatureclass.NoSQL
expressionisallowedforanInsertCursor.
Thedatatobeaddedtothefeatureclasswillbeintheformofatupleoralist,inthesame
orderasthefieldsthatarelistedinthefieldslistparameter.Onlyfieldsofinterestneedto
beincludedinthelistoffields,meaningnoteveryfieldneedsavalueinthelisttobe
added.Whenaddinganewrowofdatatoafeatureclass,theuniqueIDwillautomatically
begenerated,makingitunnecessarytoexplicitlyincludetheuniqueID(intheformofthe
OID@keyword)inthelistoffieldstobeadded.
Let’sexplorecodethatcouldbeusedtogenerateanewbusstop.We’llwritetoatest
datasetcalledTestBusStops.WeareonlyinterestedintheNameandStopIDfields,so
thosefieldsalongwiththeshapefield(whichisinaStatePlaneprojectionsystem)willbe
includedinthedatalisttobeadded:
Bus_Stops=r'C:\Projects\PacktDB.gdb\TestBusStops'
insertCursor=arcpy.da.InsertCursor(Bus_Stops,['SHAPE@',
'NAME','STOPID'])
coordinatePair=(6001672.5869999975,2091447.0435000062)
newPoint=arcpy.Point(*coordinatePair)
dataList=[newPoint,'NewStop1',112121]
insertCursor.insertRow(dataList)
delinsertCursor
Ifthereisaniterablelistofdatatobeinsertedintothefeatureclass,createtheInsert
Cursorvariablebeforeenteringtheiteration,anddeletetheInsertCursorvariableoncethe
datahasbeeniteratedthrough,orusethewith..asmethodtoautomaticallydeletetheInsert
Cursorvariablewhentheiterationiscomplete:
Bus_Stops=r'C:\Projects\PacktDB.gdb\TestBusStops'
listOfLists=[[(6002672.58675,2092447.04362),'NewStop2',112122],
[(6003672.58675,2093447.04362),'NewStop3',112123],
[(6004672.58675,2094447.04362),'NewStop4',112124]
]
witharcpy.da.InsertCursor(Bus_Stops,
['SHAPE@',
'NAME',
'STOPID'])asiCursor:
fordataListinlistOfLists:
newPoint=arcpy.Point(*dataList[0])
dataList[0]=newPoint
Asalist,thelistOfListsvariableisiterable.EachlistwithinitisconsideredasdataList
intheiteration,andthefirstvalueindataList(thecoordinatepair)ispassedtothe
arcpy.Point()functiontocreateaPointobject.Thearcpy.Point()functionrequires
twoparameters,XandY;theseareextractedfromthecoordinatepairtupleusingthe
asterisk,which‘explodes’thetupleandpassesthevaluesitcontainstothefunction.The
PointobjectisthenaddedbackintodataListusinganindex-basedlistassignment,
whichwouldnotbeavailabletousifthedataListvariablewasatuple(wewouldinstead
havetocreateanewlistandaddinthePointobjectandtheotherdatavalues).
Insertingapolylinegeometry
Tocreateandinsertapolyline-typeshapefieldfromaseriesofpoints,it’sbesttousethe
[email protected],whichwillbe
discussedinthenextchapter.WhenworkingwiththeSHAPE@keyword,wehavetowork
withdatainESRI’sspatialbinaryformats,andthedatamustbewrittenbacktothefieldin
thesameformatusingtheArcPyGeometrytypes.
Tocreateapolyline,thereisonerequirement,atleasttwovalidpointsmadeoftwo
coordinatepairs.WhenworkingwiththeSHAPE@keyword,thereisamethodologyto
convertingthecoordinatepairsintoanArcPyPointandthenaddingittoanArcPyArray,
whichisthenconvertedintoanArcPyPolylinetobewrittenbacktotheshapefield:
listOfPoints=[(6002672.58675,2092447.04362),
(6003672.58675,2093447.04362),
(6004672.58675,2094447.04362)
]
line='NewBusLine'
lineID=12345
busLine=r'C:\Projects\PacktDB.gdb\TestBusLine'
insertCursor=arcpy.da.InsertCursor(busLine,['SHAPE@',
'LINE','LINEID'])
lineArray=arcpy.Array()
forpointsPairinlistOfPoints:
newPoint=arcpy.Point(*pointsPair)
lineArray.add(newPoint)
newLine=arcpy.Polyline(lineArray)
insertData=newLine,line,lineID
ThethreecoordinatepairsintuplesareiteratedandconvertedintoPointobjects,which
areinturnaddedtotheArrayobjectcalledlineArray.TheArrayobjectisthenaddedto
thePolylineobjectcallednewLine,whichisthenaddedtoatuplewiththeotherdata
attributesandinsertedintothefeatureclassbytheInsertCursor.
Insertingapolygongeometry
Polygonsarealsoinserted,orupdated,usingcursors.TheArcPyPolygonGeometrytype
doesnotrequirethecoordinatepairstoincludethefirstpointtwice(thatis,asthefirst
pointandasthelastpoint).Thepolygonisclosedautomaticallybythearcpy.Polygon()
function:
listOfPoints=[(6002672.58675,2092447.04362),
(6003672.58675,2093447.04362),
(6004672.58675,2093447.04362),
(6004672.58675,2091447.04362)
]
polyName='NewPolygon'
polyID=54321
blockPoly=r'C:\Projects\PacktDB.gdb\Chapter5Results\TestPolygon'
insertCursor=arcpy.da.InsertCursor(blockPoly,['SHAPE@','BLOCK',
'BLOCKID'])
polyArray=arcpy.Array()
forpointsPairinlistOfPoints:
newPoint=arcpy.Point(*pointsPair)
polyArray.add(newPoint)
newPoly=arcpy.Polygon(polyArray)
insertData=newPoly,polyName,polyID
insertCursor.insertRow(insertData)
Hereisavisualizationoftheresultoftheinsertoperation:
Summary
Inthischapterwecoveredthebasicusesofdataaccessmodulecursors.Search,update
andInsertCursorswereexploredanddemonstrated,andaspecialfocuswasplacedonthe
useofthesecursorsforextractingshapedatafromtheshapefield.Cursorparameterswere
alsointroduced,includingthespatialreferenceparameterandtheSQLexpressionwhere
clauseparameter.Inthenextchapter,wewillfurtherexploretheuseofcursors,especially
withtheuseofArcPyGeometrytypes.
Chapter6.WorkingwithArcPy
GeometryObjects
Theessenceofgeospatialanalysisisusinggeometricshapes–points,lines,andpolygons
–tomodelthegeographyofrealworldobjectsandtheirlocation-basedrelationships.The
simpleshapesandtheirgeometricpropertiesoflocation,lengthandareaareprocessed
usinggeospatialoperationstogenerateanalysisresults.Itisthecombinationofmodeled
geographicdataandtheassociatedattributeinformationthatseparategeospatial
informationsystemsfromallotherinformationsystems.
UntilArcPy,processingthefeatureclassgeometryusingthegeospatialoperationswas
dependedonthepre-builttoolswithinArcToolbox.ArcPyhasmadeitpossibletodirectly
accessthegeometricshapeswhicharestoredasmathematicalrepresentationsintheshape
fieldoffeatureclasses.Onceaccessed,thisgeometricdataisloadedintoArcPygeometry
objectstomakethedataavailableforanalysiswithinanArcPyscript.Becauseofthis
advance,writingscriptsthataccessgeometryfieldsandusethemtoperformanalysishas
transformedArcGISgeospatialanalysis.Inthischapter,we’llexplorehowtogenerateand
usetheArcPygeometryobjectstoperformgeospatialoperations,andapplythemtothe
busstopsanalysis.
Inthischapter,wewillcover:PointandArrayconstructorobjectsandPointGeometry,
Polyline,andPolygongeometryobjects
Howtousethegeometryobjectstoperformgeospatialoperations
Howtointegratethegeometryobjectsintoscripts
Howtoperformcommongeospatialoperationsusingthegeometryobjects
HowtoreplacetheuseofArcToolboxtoolsinthescriptwithgeometryobject
methods
ArcPygeometryobjectclasses
Indesigninggeometryobjects,theauthorsofArcPymadeitpossibletoperform
geospatialoperationsinmemory,reducingtheneedtousetoolsintheArcToolboxfor
theseoperations.Thiswillresultinspeedgainsasthereisnoneedtowritetheresultsof
thecalculationstodiskateachstepoftheanalysis.Instead,theresultsofthestepscanbe
passedfromfunctiontofunctionwithinthescript.Thefinalresultsoftheanalysiscanbe
writtentotheharddriveasafeatureclass,ortheycanbewrittenintoaspreadsheetor
passedtoanotherprogram.
ThegeometryobjectsarewrittenasPythonclasses-specialblocksofcodethatcontain
internalfunctions.Theinternalfunctionsarethemethodsandpropertiesofthegeometry
objects;whencalledtheyallowtheobjecttoperformanoperation(amethod)ortoreveal
informationaboutthegeometryobject(aproperty).Pythonclassesarewrittenwithamain
classthatcontainssharedmethodsandproperties,andwithsub-classesthatreferencethe
mainclassbutalsohavespecificmethodsandpropertiesthatarenotshared.Here,the
mainclassistheArcPyGeometryobject,whilethesub-classesarethePointGeometry,
Multipoint,PolylineandPolygonobjects.
Thegeometryobjectsaregeneratedinthreeways.Thefirstrequiresusingdatacursorsto
readexistingfeatureclassesandpassingaspecialkeywordasafieldname.Theshape
datareturnedbythecursorisageometryobject.Thesecondmethodistocreatenewdata
bypassingrawcoordinatestoaconstructorobject(eitheraPointorArrayobject),which
isthenpassedtoageometryobject.Thethirdmethodistoreaddatafromafeatureclass
usingtheCopyFeaturestoolfromtheArcToolbox.
Eachgeometryobjecthasmethodsthatallowforreadaccessandwriteaccess.Theread
accessmethodsareimportantforaccessingthecoordinatepointsthatconstitutethepoints,
linesandpolygons.Thewriteaccessmethodsareimportantwhengeneratingnewdata
objectsthatcanbeanalyzedorwrittentodisk.
ThePointGeometry,Multipoint,Polyline,andPolygongeometryobjectsareusedfor
performinganalysisupontheirrespectivegeometrytypes.Thegenericgeometryobject
canacceptanygeometrytypeandanoptionalspatialreferencetoperformgeospatial
operationswhenthereisnoneedtodiscernthegeometrytype.
TwootherArcPyclasseswillbeusedforperforminggeospatialoperationsinmemory:the
ArrayobjectandthePointobject.Theyareconstructorobjects,astheyarenotsubclassedfromthegeometryclass,butareinsteadusedtoconstructthegeometryobjects.
ThePointobjectisusedtocreatecoordinatepointsfromrawcoordinates.TheArray
objectisalistofcoordinatepointsthatcanbepassedtoaPolylineorPolygonobject,as
aregularPythonlistofArcPyPointobjectscannotbeusedtogeneratethosegeometry
objects.
ArcPyPointobjects
Pointobjectsarethebuildingblocksusedtogenerategeometryobjects.Also,allofthe
geometryobjectswillreturncomponentcoordinatesasPointobjectswhenusingread
accessmethods.PointobjectsallowforsimplegeometryaccessusingitsX,YandZ
properties,andalimitednumberofgeospatialmethods,suchascontains,overlaps,
within,touches,crosses,equals,anddisjoint.Let’suseIDLEtoexploresomeof
thesemethodswithtwoPointgeometryobjectswiththesamecoordinates:
>>>Point=arcpy.Point(4,5)
>>>point1=arcpy.Point(4,5)
>>>Point.equals(point1)
True
>>>Point.contains(point1)
True
>>>Point.crosses(point1)
False
>>>Point.overlaps(point1)
False
>>>Point.disjoint(point1)
False
>>>Point.within(point1)
True
>>>point.X,Point.Y
(4.0,5.0)
Intheseexamples,weseesomeoftheidiosyncrasiesofthePointobject.Withtwopoints
thathavethesamecoordinates,theresultsoftheequalsmethodandthedisjointmethod
areasexpected.ThedisjointmethodwillreturnTruewhenthetwoobjectsdonotshare
coordinates,whiletheoppositeistruewiththeequalsmethod.Thecontainsmethodwill
workwiththetwoPointobjectsandreturnTrue.Thecrossesmethodandoverlaps
methodaresomewhatsurprisingresults,asthetwoPointobjectsdooverlapinlocation
andcouldbeconsideredtocross;however,thosemethodsdonotreturntheexpected
resultastheyarenotbuilttocomparetwopoints.
ArcPyArrayobjects
BeforeweprogressuptoPolylineandPolygonobjects,weneedtounderstandtheArcPy
Arrayobject.ItisthebridgebetweenthePointobjectsandthosegeometryobjectsthat
requiremultiplecoordinatepoints.ArrayobjectsacceptPointobjectsasparameters,and
theArrayobjectisinturnpassedasaparametertothegeometryobjecttobecreated.
Let’susePointobjectswithanArrayobjecttounderstandbetterhowtheyworktogether.
TheArrayobjectissimilartoaPythonlist,withextend,append,andreplacemethods,
andalsohasuniquemethodssuchasaddandclone.Theaddmethodwillbeusedtoadd
Pointobjectsindividually:
>>>Point=arcpy.Point(4,5)
>>>point1=arcpy.Point(7,9)
>>>Array=arcpy.Array()
>>>Array.add(point)
>>>Array.add(point1)
Theextend()methodwouldaddalistofPointobjectsallatonce:
>>>Point=arcpy.Point(4,5)
>>>point1=arcpy.Point(7,9)
>>>pList=[Point,point1]
>>>Array=arcpy.Array()
>>>Array.extend(pList)
TheinsertmethodwillputaPointobjectintheArrayataspecificindex,whilethe
replacemethodisusedtoreplaceaPointobjectinanArraybypassinganindexanda
newPointobject:
>>>Point=arcpy.Point(4,5)
>>>point1=arcpy.Point(7,9)
>>>point2=arcpy.Point(11,13)
>>>pList=[Point,point1]
>>>Array=arcpy.Array()
>>>Array.extend(pList)
>>>Array.replace(1,point2)
>>>point3=arcpy.Point(17,15)
>>>Array.insert(2,point3)
TheArrayobject,whenloadedwithPointobjects,canthenbeusedtogeneratetheother
geometryobjects.
ArcPyPolylineobjects
ThePolylineobjectisgeneratedwithanArrayobjectthathasatleasttwoPointobjects.
AsgiveninthefollowingIDLEexample,onceanArrayobjecthasbeengeneratedand
loadedwiththePointobjects,itcanthenbepassedasaparametertoaPolylineobject:
>>>Point=arcpy.Point(4,5)
>>>point1=arcpy.Point(7,9)
>>>pList=[Point,point1]
>>>Array=arcpy.Array()
>>>Array.extend(pList)
>>>pLine=arcpy.Polyline(Array)
NowthatthePolylineobjecthasbeencreated,itsmethodscanbeaccessed.Thisincludes
methodstorevealtheconstituentcoordinatepointswithinthepolyline,andotherrelevant
information:
>>>pLine.firstPoint
<Point(4.0,5.0,#,#)>
>>>pLine.lastPoint
<Point(7.0,9.0,#,#)>
pLine.getPart()
<Array[<Array[<Point(4.0,5.0,#,#)>,<Point(7.0,9.0,#,#)>]>]>
>>>pLine.trueCentroid
<Point(5.5,7.0,#,#)>
>>>pLine.length
5.0
>>>pLine.pointCount
2
ThisexamplePolylineobjecthasnotbeenassignedaspatialreferencesystem,sothe
lengthisunitless.Whenageometryobjectdoeshaveaspatialreferencesystem,thelinear
andarealunitswillbereturnedinthelinearunitofthesystem.
ThePolylineobjectisalsoourfirstgeometryobjectwithwhichwecaninvokegeometry
classmethodsthatperformgeospatialoperations,suchasbuffers,distanceanalyses,and
clips:
>>>bufferOfLine=pLine.buffer(10)
>>>bufferOfLine.area
413.93744395
>>>bufferOfLine.contains(pLine)
True
>>>newPoint=arcpy.Point(25,19)
>>>pLine.distanceTo(newPoint)
20.591260281974
AnotherusefulmethodofPolylineobjectsisthepositionAlongLinemethod.Itisused
toreturnaPointGeometryobject,discussedinthefollowing,ataspecificpositionalong
theline.ThispositionalongthelinecaneitherbeanumericdistancefromthefirstPoint
orasapercentage(expressedasafloatfrom0-1),whenusingtheoptionalsecond
parameter:
>>>nPoint=pLine.positionAlongLine(3)
>>>nPoint.firstPoint.X,nPoint.firstPoint.Y
(5.8,7.4)>>>pPoint=pLine.positionAlongLine(.5,True)
>>>pPoint.firstPoint.X,pPoint.firstPoint.Y
(5.5,7.0)
ThereareanumberofothermethodsavailabletoPolylineobjects.Moreinformationis
availablehere:
http://resources.arcgis.com/en/help/main/10.2/index.html#//018z00000008000000
ArcPyPolygonobjects
TocreateaPolygonobject,anArrayobjectmustbeloadedwithPointobjectsandthen
passedasaparametertothePolygonobject.OncethePolygonobjecthasbeengenerated,
themethodsavailabletoitareveryusefulforperforminggeospatialoperations.The
geometryobjectscanalsobesavedtodiskusingtheArcToolboxCopyFeaturestool.This
IDLEexampledemonstrateshowtogenerateashapefilebypassingaPolygonobject
andarawstringfilenametothetool:
>>>importarcpy
>>>point1=arcpy.Point(12,16)
>>>point2=arcpy.Point(14,18)
>>>point3=arcpy.Point(11,20)
>>>Array=arcpy.Array()
>>>Points=[point1,point2,point3]
>>>Array.extend(points)
>>>Polygon=arcpy.Polygon(array)
>>>arcpy.CopyFeatures_management(polygon,r'C:\Projects\Polygon.shp')
<Result'C:\\Projects\\Polygon.shp'>
Polygonobjectbuffers
Polygonobjects,likePolylineobjects,havemethodsthatmakeiteasytoperform
geospatialoperationssuchasbuffers.Bypassinganumbertothebuffermethodasa
parameter,abufferwillbegeneratedinmemory.Theunitofthenumberisdeterminedby
theSpatialReferencesystem.Internalbufferscanbegeneratedbysupplyingnegative
buffernumbers;thebuffergeneratedbeingtheareawithinthePolygonobjectatthe
specifieddistancefromthePolygonperimeter.Clips,unions,symmetricaldifferences,and
moreoperationsareavailableasmethods,asarewithinorcontainsoperations;even
projectionscanbeperformedusingthePolygonobjectmethodsaslongasithasa
SpatialReferencesystemobjectpassedasaparameter.Followingisascriptthatwill
createtwoshapefileswithtwoseparateSpatialReferencesystems,eachidentifiedbya
numericcode(2227and4326)fromtheEPSGcodingsystem:
importarcpyPoint=arcpy.Point(6004548.231,2099946.033)
point1=arcpy.Point(6008673.935,2105522.068)
point2=arcpy.Point(6003351.355,2100424.783)Array=arcpy.Array()
Array.add(point1)
Array.add(point)
array.add(point2)
Polygon=arcpy.Polygon(array,2227)
buffPoly=Polygon.buffer(50)
features=[Polygon,buffPoly]
arcpy.CopyFeatures_management(features,
r'C:\Projects\Polygons.shp')
spatialRef=arcpy.SpatialReference(4326)
polygon4326=Polygon.projectAs(spatialRef)
arcpy.CopyFeatures_management(polygon4326,
r'C:\Projects\polygon4326.shp')
HereishowthesecondshapefilelooksintheArcCatalogPreviewwindow:
OtherPolygonobjectmethods
UnlikethecliptoolintheArcToolbox,whichcanclipaPolygonusinganotherpolygon,
theclipmethodrequiresanextentobject(anotherArcPyclass)andislimitedtoa
rectangularenvelopearoundtheareatobeclipped.Toremoveareasfromapolygon,the
differencemethodcanworklikethecliporerasetoolintheArcToolbox:
buffPoly=Polygon.buffer(500)
donutHole=buffPoly.difference(Polygon)
features=[Polygon,donutHole]
arcpy.CopyFeatures_management(features,
r"C:\Projects\Polygons2.shp")
Hereisthedonuthole-likeresultofthebufferanddifferenceoperation.Thebufferwith
thedonutholesurroundstheoriginalPolygonobject:
ArcPygeometryobjects
Thegenericgeometryobjectisquiteusefulforcreatinginmemoryacopyofthegeometry
ofafeatureclass,withoutfirstneedingtoknowwhichtypeofgeometrythefeatureclass
contains.LikealloftheArcPygeometryobjects,itsreadmethodsincludetheextraction
ofthedatainmanyformatssuchasJSON,WKT,andWKB.Thearea(ifitisapolygon),
thecentroid,theextent,andtheconstituentpointsofeachgeometryarealsoavailable,as
demonstratedpreviously.
Hereisanexampleofreadingthegeometryofafeatureclassintomemoryusingthe
CopyFeaturestool:
importarcpy
cen2010=r'C:\Projects\ArcPy.gdb\SanFrancisco\CensusBlocks2010'
blockPolys=arcpy.CopyFeatures_management(cen2010,
arcpy.Geometry())
ThevariableblockPolysisaPythonlistcontainingallofthegeometriesloadedintoit;in
thiscaseitiscensusblocks.Thelistcanthenbeiteratedtobeanalyzed.
ArcPyPointGeometryobjects
ThePointGeometryobjectisveryusefulforperformingthesesamegeospatialoperations
withpoints,whicharenotavailablewiththePointobjects.Whenacursorisusedto
retrieveshapedatafromafeatureclasswithaPointGeometrytype,theshapedatais
returnedasaPointGeometryobject.WhilePointobjectsarerequiredtoconstructall
othergeometryobjectswhenacursorisnotusedtoretrievedatafromafeatureclass,it’s
thePointGeometryobjectthatisusedtoperformpointgeospatialoperations.
Let’sexploregettingPointGeometryobjectsfromadataaccessmoduleSearchCursor
andusingthereturneddatarowstocreatebufferedpoints.Inourbusstopanalysis,this
willreplacetheneedtousetheArcToolboxBuffertooltocreatethe400footbuffers
aroundeachstop.Thescriptinthefollowingusesadictionarytocollectthebufferobjects
andthensearchesthecensusblocksusinganotherSearchCursor.Toaccesstheshapefield
usingtheSearchCursor()method,[email protected],
thescriptwilliteratethroughthebusstopsandfindallcensusblockswithwhicheachstop
intersects:
#Generate400footbuffersaroundeachbusstop
importarcpy,csv
busStops=r"C:\Projects\PacktDB.gdb\SanFrancisco\Bus_Stops"
censusBlocks2010=r"C:\Projects\PacktDB.gdb\SanFrancisco\CensusBlocks2010"
sql="NAME='71IB'ANDBUS_SIGNAG='FerryPlaza'"
dataDic={}
witharcpy.da.SearchCursor(busStops,['NAME','STOPID','SHAPE@'],sql)as
cursor:
forrowincursor:
linename=row[0]
stopid=row[1]
shape=row[2]
dataDic[stopid]=shape.buffer(400),linename
Nowthatthedatahasbeenretrievedandthebuffershavebeengeneratedusingthebuffer
methodofthePointGeometryobjects,thebufferscanbecomparedagainstthecensus
blockgeometryusingiterationandaSearchCursor.Therewillbetwogeospatialmethods
usedinthisanalysis:overlapandintersect.Theoverlapsmethodisaboolean
operation,returningavalueoftrueorfalsewhenonegeometryiscomparedagainst
another.Theintersectmethodisusedtogettheactualareaoftheintersectaswellas
identifyingthepopulationofeachblock.Usingtheintersectrequirestwoparameters:a
secondgeometryobject,andanintegerindicatingwhichtypeofgeometrytoreturn(1for
point,2forline,4forpolygon).Wewantthepolygonalareaofintersectreturnedtohave
anareaofintersectionavailablealongwiththepopulationdata:
#Intersectcensusblocksandbusstopbuffers
processedDataDic={}={}
forstopidindataDic.keys():
values=dataDic[stopid]
busStopBuffer=values[0]
linename=values[1]
blocksIntersected=[]
witharcpy.da.SearchCursor(censusBlocks2010,
['BLOCKID10','POP10','SHAPE@'])ascursor:
forrowincursor:
block=row[2]
population=row[1]
blockid=row[0]
ifbusStopBuffer.overlaps(block)==True:
interPoly=busStopBuffer.intersect(block,4)
data=row[0],row[1],interPoly,block
blocksIntersected.append(data)
processedDataDic[stopid]=values,blocksIntersected
Thisportionofthescriptiteratesthroughtheblocksandintersectsagainstthebufferedbus
stops.Nowthatwecanidentifytheblocksthattouchthebufferaroundeachstopandthe
dataofinteresthasbeencollectedintothedictionary,itcanbeprocessedandtheaverage
populationofalloftheblockstouchedbythebuffercanbecalculated:
#Createanaveragepopulationforeachbusstop
dataList=[]
forstopidinprocessedDataDic.keys():
allValues=processedDataDic[stopid]
popValues=[]
blocksIntersected=allValues[1]
forblocksinblocksIntersected:
popValues.append(blocks[1])
averagePop=sum(popValues)/len(popValues)
busStopLine=allValues[0][1]
busStopID=stopid
finalData=busStopLine,busStopID,averagePop
dataList.append(finalData)
Nowthatthedatahasbeencreatedandaddedtoalist,itcanbeoutputtedtoaspreadsheet
usingthecreateCSVmodulewecreatedinChapter4,ComplexArcPyScriptsand
GeneralizingFunctions:
#Generateaspreadsheetwiththeanalysisresults
defcreateCSV(data,csvname,mode='ab'):
withopen(csvname,mode)ascsvfile:
csvwriter=csv.writer(csvfile,delimiter=',')
csvwriter.writerow(data)
csvname="C:\Projects\Output\StationPopulations.csv"
headers='BusLineName','BusStopID','AveragePopulation'
createCSV(headers,csvname,'wb')
fordataindataList:
createCSV(data,csvname)
Thedatahasbeenprocessedandwrittentothespreadsheet.Thereisonemorestepthatwe
cantakewiththedataandthatistousetheareaoftheintersectiontocreateaproportional
populationvalueforeachbuffer.Let’sredotheprocessingofthedatatoincludethe
proportionalareas:
dataList=[]
forstopidinprocessedDataDic.keys():
allValues=processedDataDic[stopid]
popValues=[]
blocksIntersected=allValues[1]
forblocksinblocksIntersected:
pop=blocks[1]
totalArea=blocks[-1].area
interArea=blocks[-2].area
finalPop=pop*(interArea/totalArea)
popValues.append(finalPop)
averagePop=round(sum(popValues)/len(popValues),2)
busStopLine=allValues[0][1]
busStopID=stopid
finalData=busStopLine,busStopID,averagePop
dataList.append(finalData)
NowthescriptistakingfulladvantageofthepowerofArcPygeometryobjects,andthe
scriptisrunningcompletelyinmemorywhichavoidsproducinganyintermediatedatasets.
Summary
Inthischapter,wediscussedindetailtheuseofArcPygeometryobjects.Thesevaried
objectshavesimilarmethodsandare,infact,sub-classedfromthesamePythonclass.
Theyareusefulforperformingin-memorygeospatialanalyses,whichavoidshavingto
readandwritedatafromtheharddriveandalsoskipscreatinganyintermediatedata.
ArcPygeometryobjectswillbecomeanimportantpartofautomatinggeospatial
workflows.CombiningthemwithSearchCursorsmakesArcPymoreusefulthanany
earlierimplementationofPythonscriptingtoolsforArcGIS.Next,wewillconverttheraw
scriptintoascripttoolthatcanbeexecuteddirectlyfromtheArcToolboxorapersonal
toolboxinageodatabase.
Chapter7.CreatingaScriptTool
NowthatthebasicsofcreatingandexecutingArcPyscriptshavebeencovered,weneed
totakethenextstepandcreatere-useableScripttools.CreatingScripttoolswillallowfor
greatercodereuse,andwillmakeiteasytocreatecustomtoolsforotherGISanalystsand
customers.WithaPythonscriptbackendorcode,andafamiliarArcGIStoolfrontendor
userinterface,theparticularsofthecodearehiddenfromtheuser;itbecomesjustanother
tool,albeitatoolthatcansavedaysandweeksofwork.
Thischapterwillcoverthefollowingtopics:
Addingparameterstoscriptstoacceptinputandproduceoutputasrequiredbythe
user
Creatingacustomtoolfrontendandacustomtoolbox
Settingtheparametersofthetoolfrontendtoallowittopassargumentstothecode
backend
Addingdynamicparameterstoascript
Thescriptswehavegeneratedinpreviouschaptershaveallhadhard-codedinputs.The
inputvalueswerewritteninthescriptasstringsornumbersandassignedtovariables.
WhiletheycanbeupdatedmanuallytoreplacetheinputandoutputfilepathsandSQL
statements,programmersshouldaimtocreatescriptsthatwillnotrequireeditingeach
timetheyareused.Instead,scriptsshouldbedesignedtobedynamicandacceptfilepaths
andotherinputsasparametersorarguments,inmuchthesamemannerthatthefunctions
wehavecreatedacceptparameters.
Pythonwasdesignedwiththisinmind,andthesysmodulehasamethodcalledsys.argv
thatacceptsinputspassedtothescriptwhenitisexecuted.WhilethedesignersofArcPy
anditspredecessorarcgisscriptingmoduleinitiallytookadvantageofthesys.argv
method,intimetheydesignedanArcPymethodforacceptingscriptparameters.Aseither
methodcanbeusedwhenwritingArcPyscripts,andbotharefoundinexamplescriptson
theweb,itisimportanttorecognizetheminutedifferencesbetweenthesys.argvmethod
andarcpy.GetParameterAsText().Themajordifferencebetweenthetwomethodsisthat
sys.argvacceptsthedynamicargumentsasalist.Membersofthelistareaccessedusing
listindexingandassignedtovariables.Arcpy.GetParameterAsText()isafunctionthat
acceptsanindexnumberparameter.Theindexnumberpassedreflectstheorderofthe
parameterwithinthetool’sfrontend;thefirstparameteriszero,thenextisone,andsoon.
Note
Iftheorderoftheparametersisadjustedinthetoolfrontend,thisadjustmentmustbe
reflectedinthecodebackend.
Displayingscriptmessagesusing
arcpy.AddMessage
Itisimportanttoreceivefeedbackfromscriptstoassesstheprogressofthescriptasit
performsananalysis.Asbasicasthiswouldseem,Pythonscriptsandprogramming
languagesingeneraldonot,bydefault,provideanyfeedbackexceptforerrorsandthe
terminationofthescript.Thiscanbeabitdiscouragingtothenoviceprogrammer,asall
built-infeedbackisnegative.
Toalleviatethislackoffeedback,theuseofprintstatementsallowsthescripttogive
reportsontheprogressoftheanalysisasitruns.However,whenusingaScripttool,print
statementsdonothaveanyeffect.Theywillnotbedisplayedanywhere,andareignored
bythePythonexecutable.TodisplaymessagesinthescriptconsolewhenScripttoolsare
executed,ArcPyhasaarcpy.AddMessage()method.
Arcpy.AddMessagestatementsareaddedtoscriptswhereverfeedbackisrequiredbythe
programmer.Thefeedbackrequiredispassedtothemethodasaparameteranddisplayed;
whetheritbealist,string,floatorinteger.Arcpy.AddMessagemakesiteasytocheckon
theresultsofanalysiscalculations,toensurethatthecorrectinputsareusedandthatthe
correctoutputsareproduced.Asthisfeedbackfromthescriptcanbeapowerful
debuggingtool,usearcpy.AddMessagewheneverthereisaneedforfeedbackfromthe
Scripttool.
Note
Notethatstatementspassedtoarcpy.AddMessagewillonlydisplaywhenthescriptisrun
asaScripttool.
Addingdynamiccomponentstothescript
TostartmakingthescriptintoaScripttool,weshouldfirstcopythescriptthatwecreated
inChapter6,WorkingwithArcPyGeometryObjectsChapter6_1.py,asChapter7_1.pyin
anewfoldercalledChapter7.Wecanthenstartreplacingthehard-codedvariableswith
dynamicvariablesusingarcpy.GetParameterAsText.ThereisanotherArcPymethod
calledGetParameterthatacceptstheinputsasanobject,butforourpurposes,
GetParameterAsTextisthemethodtouse.
Byaddingarcpy.GetParameterAsTextandarcpy.AddMessagetothescript,wewillhave
takenthefirststeptowardscreatingaScripttool.Caremustbetakentoensurethatthe
variablescreatedfromthedynamicparametersareinthecorrectorder,asreorderingthem
canbetime-consuming.Oncetheparametersareaddedtothescriptandthehard-coded
portionsofthescriptreplacedwithvariables,thescriptisreadytobecomeaScripttool.
First,moveallofthevariablesthatarehard-codedintothetopofthescript.Then,replace
alloftheassignedvalueswitharcpy.GetParameterAsText,makingthemdynamicvalues.
Eachparameterisreferredtousingzero-basedindexing;however,theyarepassedtoa
functionindividuallyinsteadofasamemberofalist:
#Chapter7.py
importarcpy,csv
busStops=arcpy.GetParameterAsText(0)
censusBlocks2010=arcpy.GetParameterAsText(1)
censusBlockField=arcpy.GetParameterAsText(2)
csvname=arcpy.GetParameterAsText(3)
headers=arcpy.GetParameterAsText(4).split(',')
sql=arcpy.GetParameterAsText(5)
keyfields=arcpy.GetParameterAsText(6).split(';')
dataDic={}
censusFields=['BLOCKID10',censusBlockField,'SHAPE@']
if"SHAPE@"notinkeyfields:
keyfields.append("SHAPE@")
arcpy.AddMessage(busStops)
arcpy.AddMessage(censusBlocks2010)
arcpy.AddMessage(censusBlockField)
arcpy.AddMessage(csvname)
arcpy.AddMessage(sql)
arcpy.AddMessage(keyfields)
Asyoucanseefromthevariablekeyfieldsandthevariableheaders,somefurther
processingmustbeappliedtocertainvariables,asnotallofthemaregoingtobeusedas
strings.Inthiscase,alistiscreatedfromthestringpassedtothevariablekeyfieldsby
usingthestringfunctionssplitandsplittingthestringoneverysemi-colon,whilethe
headersvariableiscreatedbysplittingonthecommas.Toothervariables,suchasthe
censusBlockFieldvariableandthevariablekeyfields,theSHAPE@keywordisadded
becauseitwillberequiredeachtimetheanalysisisrun.Ifaparticularfieldisrequiredfor
eachrunofthedata,suchastheBLOCKID10field,itcanremainhard-codedinthescript,or
optionallycouldbecomeitsownselectablefieldparameterintheScripttool.
Thevariableswillthenbeaddedtotheremainderofthescriptinthecorrectplaces,
makingthescriptreadyfortheScripttooltobecomepartofacustomToolboxina
geodatabaseorinArcToolbox.However,wemustfirstcreatethetoolpartoftheScript
toolforthevaluestobecollectedandpassedtothescript.
CreatingaScripttool
CreatingascripttoolisapowerfulcombinationofthepowerofArcPyandtheeaseofuse
ofthetoolsinArcToolbox.
Thefirststepistocreateacustomtoolboxtoholdthescripttool.Toachievethis,dothe
following:
1. OpenupArcCatalogandrightclickintheSanFrancisco.gdbFileGeodatabase.
2. SelectNewandthenToolboxfromthemenu.
3. CallthetoolboxChapter8Tools.
4. RightclickonChapter8Tools,selectAdd,andthenselectScript.
Thefollowingmenuwillappearallowingyoutosetupthescripttool.Addatitlewithno
spacesandalabel,aswellasadescription.Iprefertorunscripttoolsintheforegroundto
seethemessagesitpasses,butitisnotnecessaryandcanbeannoyingwhenneedingto
startascriptandstillworkonothertasks.ClickNextoncethemenuhasbeenfilledout.
Thenextmenucontainsanentryfieldandafiledialogbutton,allowingtheusertofind
thescripttowhichtheparameterscollectedwillbepassed.Usethefiledialogtonavigate
toandselectthescript,andmakesurethatRunPythonscriptinprocessischecked.
Now,pushNextoncethescripthasbeenidentified.
Labellinganddefiningparameters
Thenextdialogboxisthemostimportantone.Itiswheretheparameterstobepassedare
labeledandtheirdatatypesaredefined.Caremustbetakentochoosethecorrectdatatype
foreachparameterastherearemultipledatatypesthatcanbesuppliedforsomeofthe
parameters.Also,propertiesforeachparameterwillbedefined;thisinformationwill
characterizethedatatobecollectedandhelptomakeitpossibleforthedatatobeinthe
correctformataswellasthecorrectdatatype.
Startbyaddingthedisplaynameforeachparametertobecollected.Thedisplayname
shouldexplainthetypeofinputthatisrequired.Forinstance,thefirstparameterwillbe
thebusstop’sfeatureclass,sothedisplaynamecouldbeBusStopFeatureClass.
Note
Makesuretoaddthedisplaynamesintheorderthattheywillbepassedtovariablesinthe
script.
Addingdatatypes
Next,addintheDataTypesforeachparameter.Thisisintricatebecausetherewillbea
largelistofdatatypestochoosefrom,andoftenthereareafewtypesthatwouldworkfor
manyparameters.Forinstance,ifashapefileparameteriscreated,itwouldallowtheuser
toselectashapefileasexpected.However,itmightbebettertousetheFeatureClassdata
type,asthenbothshapefilesandfeatureclassescouldbeusedintheanalysis.
AddingtheBusStopfeatureclassasaparameter
ThefirstparameteristheBusStopfeatureclass,anditshouldbeaFeatureClassdata
type.ClickontheDataTypeFieldnexttothedisplaynameandadrop-downlistwill
appear.Oncethedatatypeisselected,checkouttheparameterpropertiesbelowthelist
ofparameters.FortheBusStopfeatureclass,thedefaultswillbeacceptable,asthe
featureclassisrequired,isnotamulti-value,hasnodefaultorenvironmentsettings,and
isnotobtainedfromanyotherparameter.
Note
Someoftheparameterswillrequireanotherparametertobeselectedfirstastheyare
derivedvaluesobtainedfromthefirstparameter.TheCensusBlockFieldparameterand
theSQLstatementparameterderivevaluesfromtheCensusBlockfeatureclassandBus
Stopfeatureclassparameters,respectively.
AddingtheCensusBlockfeatureclassasaparameter
TheCensusBlockfeatureclassissimilartotheBusStopfeatureclass.ItwillbeaFeature
Classdatatype,allowingbothshapefilesandfeatureclassestobeselected,andthereisno
needtoadjustthedefaultparameterproperties.Oncethedatatypehasbeenset,the
CensusBlockparameterisreadyforuse.
AddingtheCensusBlockfieldasaparameter
TheCensusBlockfieldparameterhasanewtwist;itisobtainedfromtheCensusBlock
featureclassparameter,andwillonlybepopulatedoncethatfirstparameterhasbeen
created.Tomakethispossible,theObtainedfromparameterpropertywillhavetobe
set.SelectFieldasthedatatype,andthenclickontheblankareanexttotheObtained
fromparameterpropertyandselectCensus_Block_Feature_Class.Thiswillcreatea
listofthefieldscontainedwithintheCensusBlockfeatureclass.
Addingtheoutputspreadsheetasaparameter
Asthespreadsheetthatwillbeproducedfromtheanalysisrunbythescripttoolisa
CommaSeparatedValue(CSV)file,selectTextFileastheDataType.Settingthe
Defaultparameterpropertytoafilenamecansavetime,andwillmaketherequiredfile
extensioneasiertoidentify.Also,asthespreadsheetwillbeproducedbytheScripttoolas
anoutput,theDirectionparameterpropertyshouldbeOutput.
Addingthespreadsheetfieldnamesasaparameter
ThefieldnameschosenasheadersfortheoutputspreadsheetshouldbeaStringdatatype,
withthefieldnamesseparatedbycommasandnospaces.Thescriptusesthestringdata
type’ssplitmethodtoseparatethefieldnames.Passingacommatothesplitmethod
separatestheparameterbysplittingtheinputstringonthecommastocreatealistoffield
names.Thelistoffieldnameswillbeusedasaheaderbythecsvmodulewhencreating
thespreadsheet.
AddingtheSQLStatementasaparameter
TheSQLStatementparameterwillrequirethehelpfulSQLExpressionBuildermenuand
shouldthereforebeaSQLExpressiondatatype.TheSQLExpressionBuilderwillusea
fieldobtainedfromtheBusStopfeatureclass.SettheObtainedfromparameter
propertytotheBusStopfeatureclassbyclickingonthatpropertyandselecting
Bus_Stop_Feature_Classfromthedrop-downlist.
Addingthebusstopfieldsasaparameter
Thefinalparameteristhebusstopfieldsparameter,whichisaFielddatatypethatwillbe
obtainedfromtheBusStopfeatureclass.ChangetheMultiValueparameterproperty
fromNotoYestoallowtheusertoselectmultiplefields.Alsoremembertosetthe
ObtainedfromparameterpropertytoBus_Stop_Feature_Classsothatthefieldsare
populatedfromtheBusStopfeatureclassparameter.
Nowthatalltheparametershavebeendescribedandtheirpropertieshavebeenset,the
scripttoolisready.ClickonFinishtoclosethemenu.
Inspectingthefinalscript
Oncealloftheparametershavebeencollected,thevariablesarethenusedtoreplacethe
hard-codedfieldlistsorotherstaticscriptelementswiththenewdynamicparameters
collectedfromthescripttool.Inthismanner,thescripthasbecomeavaluabletoolthat
canbeusedformultipledataanalyses,asthefieldstobeanalyzedarenowdynamic:
importarcpy,csv
busStops=arcpy.GetParameterAsText(0)
censusBlocks2010=arcpy.GetParameterAsText(1)
censusBlockField=arcpy.GetParameterAsText(2)
csvname=arcpy.GetParameterAsText(3)
headers=arcpy.GetParameterAsText(4).split(',')
sql=arcpy.GetParameterAsText(5)
keyfields=arcpy.GetParameterAsText(6).split(';')
dataDic={}
censusFields=['BLOCKID10',censusBlockField,'SHAPE@']
if"SHAPE@"notinkeyfields:
keyfields.append("SHAPE@")
arcpy.AddMessage(busStops)
arcpy.AddMessage(censusBlocks2010)
arcpy.AddMessage(censusBlockField)
arcpy.AddMessage(csvname)
arcpy.AddMessage(sql)
arcpy.AddMessage(keyfields)
x=0
witharcpy.da.SearchCursor(busStops,keyfields,sql)ascursor:
forrowincursor:
stopid=x
shape=row[-1]
dataDic[stopid]=[]
dataDic[stopid].append(shape.buffer(400))
dataDic[stopid].extend(row[:-1])
x+=1
processedDataDic={}
forstopidindataDic.keys():
values=dataDic[stopid]
busStopBuffer=values[0]
blocksIntersected=[]
witharcpy.da.SearchCursor(censusBlocks2010,censusFields)ascursor:
forrowincursor:
block=row[-1]
population=row[1]
blockid=row[0]
ifbusStopBuffer.overlaps(block)==True:
interPoly=busStopBuffer.intersect(block,4)
data=population,interPoly,block
blocksIntersected.append(data)
processedDataDic[stopid]=values,blocksIntersected
dataList=[]
forstopidinprocessedDataDic.keys():
allValues=processedDataDic[stopid]
popValues=[]
blocksIntersected=allValues[-1]
forblocksinblocksIntersected:
pop=blocks[0]
totalArea=blocks[-1].area
interArea=blocks[-2].area
finalPop=pop*(interArea/totalArea)
popValues.append(finalPop)
averagePop=round(sum(popValues)/len(popValues),2)
busStopLine=allValues[0][1]
busStopID=stopid
finalData=busStopLine,busStopID,averagePop
dataList.append(finalData)
defcreateCSV(data,csvname,mode='ab'):
withopen(csvname,mode)ascsvfile:
csvwriter=csv.writer(csvfile,delimiter=',')
csvwriter.writerow(data)
headers.insert(0,"ID")
createCSV(headers,csvname,'wb')
fordataindataList:
createCSV(data,csvname)
ThevariablexwasaddedtokeeptrackofthemembersofthedictionarydataDic,which
inthescriptinChapter6,WorkingwithArcPyGeometryObjectshadreliedontheSTOPID
field.Toeliminatethisdependency,xwasintroduced.
RunningtheScriptTool
Nowthatthefrontendhasbeendesignedtoacceptparametersfromauser,andthe
backendscriptisreadytoaccepttheparametersfromthefrontend,thetoolisreadytobe
executed.DoubleclickontheScriptToolinthetoolboxtoopenthetooldialogbox.
SelecttheparametersaswithanyArcToolboxtool(forexampleusingthefiledialogto
navigateafiletreetotheBusStopfeatureclass).Oncetheparametershavebeenset,
clickonOKtoexecutethescript.
Oneoptionaladjustmentwouldbetoaddanarcpy.AddMessagelinewheretheaverage
populationiscalculated.Bydoingthis,theindividualblockpopulationwouldbe
displayedandthescriptconsolewouldgivefeedbackabouttheprogressofthescript.
InsertinthescriptjustbelowthelinewherethevariablefinalDataisdefined:
arcpy.AddMessage(finalData)
Thefeedbackprovidedbythislinewillmakeitobviousthatthescriptisworking,which
isusefulwhenthescriptexecutesalonganalysis.Whenperforminglonganalyses,itis
goodpracticetoprovidefeedbacktotheusersothattheycanseethatthescriptisworking
asexpected.Passnewlinecharacters(\n)asparameterstoarcpy.AddMessagewhenthere
isalargeamountofdatabeingpassedtoarcpy.AddMessage.Thiswillbreakupthedata
intodiscretechunksandmakeiteasiertoread.
Summary
Inthischapter,welearnedhowtoconvertascriptintoapermanentandsharablescript
toolthatcanbeusedbyanArcGISuserwithnoprogrammingexperience.Bycreatinga
frontendusingthefamiliarArcGIStoolinterface,andthenpassingparameterstocustom
builttoolsthatemployArcPy,GISprogrammerscancombinetheeaseofuseofthe
ArcToolboxwiththepowerofPython.
Inthenextchapter,wewillexplorehowtouseArcPytocontroltheexportofmapsfrom
mapdocuments.Byadjustingmapelementssuchastitlesandlegends,wecancreate
dynamicmapoutputstoreflectthenatureofthedataproducedbymapanalysis.In
Chapter9,MoreArcpy.MappingTechniqueswewilladdtheoutputofmapstoourscript
toolcreatedinthischapter.
Chapter8.Introductionto
ArcPy.Mapping
Creatingmapsisanart,onethatcanbelearnedthroughyearsofdedicatedstudyof
cartography.Thevisualdisplayofinformationisbothexcitinganddifficult,andcanbea
rewardingpartofthedailyworkflowofgeospatialprofessionals.Oncethebasicshave
beenlearnedandmastered,cartographicoutputbecomesaconstantbattletoproducemore
mapsatafasterpace.ArcPy,onceagain,hasapowerfulsolution:thearcpy.mapping
module.
Byallowingfortheautomaticmanipulationofallmapcomponents,includingthemap
window,thelayers,thelegend,andalltextelements,arcpy.mappingmakescreating,
modifying,andoutputtingmultiplemapsfastandsimple.Mapbookcreation–another
importantskillforgeospatialprofessionals,isalsomadeeasyusingthemodule.Inthis
chapterwewilldiscusssomebasicfunctionalitiesavailablethrougharcpy.mappingand
useittooutputamapbookofbusstopsandtheirsurroundingcensusblocks.
Thischapterwillcoverthefollowingtopics:
InspectingandupdatingMapDocument(MXD)layerdatasources
ExportingMXDstoPDForotherimageformats
Adjustingmapdocumentelements
UsingArcPywithmapdocuments
Recognizingthelimitationsofthepreviousarcgisscriptingmodule,ESRIdesignedthe
ArcPymoduletonotonlyworkwithdatabutalsoincludedthearcpy.mappingmoduleto
allowdirectinteractionwithmapdocuments(MXDs)andthelayerstheycontain.This
newmoduleopenedupamultitudeofmapautomationpossibilities.Ascriptmightaidin
identifyingbrokenlayerlinks,updatethedatasourceoftheselayers,andapplynewcolor
schemestolayers.Anotherscriptmightuseamaptemplateandcreateasetofmaps,one
fromeachfeatureclassinafeaturedataset.Athirdscriptcouldcreateamapbookfroman
MXD,movingfromcelltocellinagridlayertooutputthepagesofthebook,oreven
calculatingthecoordinatesonthefly.Dynamicallycreatedmaps,basedondatafroma
freshanalysis,canbeoutputtedatthesametimethedataisproduced.Arcpy.mapping
movestheArcPymodulefromhelpfultoinstrumental,inanygeospatialworkflow.
Toinvestigatetheutilityofthearcpy.mappingmodule,we’llneedthehelpofanMXD
template.I’vepreparedamappackagecontainingthedataandMXDthatwewillusefor
theexercisesinthischapter.ItincludesthedatafromourSanFranciscobusstop’s
analysis,whichwewillcontinueandextendtoincludemaps.
Inspectingandreplacinglayersources
Thefirstandmostimportantarcpy.mappingmoduleuseistoidentifyandfixthebroken
linksbetweenlayersinamapdocumentandtheirdatasources.LayersymbologyandGIS
datastorageareseparated,meaningthatlayerdatasourcesareoftenmoved.
Arcpy.mappingoffersaquicksolution,thoughimperfect.
Thissolutiondependsonanumberofmethodsincludedinthearcpy.mappingmodule.
First,wewillneedtoidentifythebrokenlinks,andthenwewillfixthem.Toidentifythe
brokenlinkswewillusetheListBrokenDataSources()methodincludedin
arcpy.mapping.
TheListBrokenDataSources()methodrequiresanMXDpathtobepassedtothe
MapDocument()methodofarcpy.mapping.Oncethemapdocumentobjecthasbeen
created,itispassedtotheListBrokenDataSources()method,andalistwillbegenerated
containinglayerobjects,oneforeachlayerwithabrokenlink.Thelayerobjectshavea
numberofpropertiesavailabletothem.Usingtheseproperties,let’sprintoutthename
anddatasourceofeachlayerusingthenameanddatasourcepropertiesofeachobject:
importarcpy
mxdPath='C:\Projects\MXDs\Chapter8\BrokenLinks.mxd'
mxdObject=arcpy.mapping.MapDocument(mxdPath)
brokenLinks=arcpy.mapping.ListBrokenDataSources(mxdObject)
forlinkinbrokenLinks:
printlink.name,link.dataSource
Fixingthebrokenlinks
Nowthatwehaveidentifiedthebrokenlinks,thenextstepistofixthem.Inthiscase,it
wasrevealedthatthedatasourcesshouldbeinafoldercalledData,buttheyarenot
containedwithinthatfolder.Thescriptmustthenbesteppeduptoreplacethedatasources
ofeachlayer,sothattheypointattheactuallocationofthedatasource.
Therearemethodsincludedinbothlayerobjectsandmapdocumentobjectsthatcan
accomplishthisnextstep.IfallofthedatasourcesforanMXDhavebeenmoved,itis
bettertousetheMXDobjectanditsmethodstofixthesources.IntheexampleMXD,the
datasourceshaveallbeenmovedintoanewfoldercalledNewData,sowewillemploythe
findAndReplaceWorkspacePaths()methodtorepairthelinks:
oldPath=r'C:\Projects\MXDs\Data'
newPath=r'C:\Projects'
mxdObject.findAndReplaceWorkspacePaths(oldPath,newPath)
mxdObject.save()
Aslongasthedatasourcesarestillinthesameformat(suchthatshapefilesarestill
shapefilesorfeatureclassesarestillfeatureclasses),the
findAndReplaceWorkspacePaths()methodwillwork.Ifthedatasourcetypeshavebeen
changed(suchthat,shapefilesareimportedintoafilegeodatabase),the
replaceWorkspaces()methodwillhavetobeusedinsteadasitrequiresworkspacetype
asaparameter:
oldPath=r'C:\Projects\MXDs\Data'
oldType='SHAPEFILE_WORKSPACE'
newPath=r'C:\Projects'
newType='FILEGDB_WORKSPACE'
mxdObject.replaceWorkspaces(oldPath,oldType,newPath,newType)
mxdObject.save()
Fixingthelinksofindividuallayers
Iftheindividuallayersdonotshareadatasource,thelayerobjectswillneedtobe
adjustedusingthefindAndReplaceWorkspacePath()methodavailabletolayers.This
methodissimilartothemethodusedpreviously,butitwillonlyreplacethedatasourceof
thelayerobjectitisappliedtoinsteadofallofthelayers.Whencombinedwitha
dictionary,thelayerdatasourcescanbeupdatedusingthelayernameproperty:
importarcpy
layerDic={'Bus_Stops':[r'C:\Projects\OldDataPath',r'C:\Projects'],
'stclines_streets':[r'C:\Projects\OtherPath',r'C:\Projects']}
mxdPath=r'C:\Projects\MXDs\Chapter8\BrokenLinks.mxd'
mxdObject=arcpy.mapping.MapDocument(mxdPath)
brokenLinks=arcpy.mapping.ListBrokenDataSources(mxdObject)
forlayerinbrokenLinks:
oldPath,newPath=layerDic[layer.name]
layer.findAndReplaceWorkspacePath(oldPath,newPath)
mxdObject.save()
Thesesolutionsworkwellforindividualmapdocumentsandlayers.Theycanalsobe
extendedtofoldersfullofMXDsbyusingtheglob.glob()methodofthebuilt-inglob
module(whichhelpstogeneratealistoffilesthatmatchacertainfileextension)andthe
os.path.join()methodoftheosmodule:
importarcpy,glob,os
oldPath=r'C:\Projects\MXDs\Data'
newPath=r'C:\Projects'
folderPath=r'C:\Projects\MXDs\Chapter8'
mxdPathList=glob.glob(os.path.join(folderPath,'*.mxd'))
forpathinmxdPathList:
mxdObject=arcpy.mapping.MapDocument(mxdPath)
mxdObject.findAndReplaceWorkspacePaths(oldPath,newPath)
mxdObject.save()
ExportingtoPDFfromanMXD
Thenextmostimportantuseofarcpy.mappingistoautomaticallyexportMXDs.The
followingcodewillhighlighttheexportofPDFs,butnotethatthemodulealsosupports
theexportofJPEGsandotherimageformats.Usingarcpy.mappingforthisprocessisa
joy,astheusualprocessofopeningandexportingtheMXDsinvolvesalotofwaitingfor
ArcMaptostartandthemaptoload,whichcanbeatimesink:
importarcpy,glob,os
mxdFolder=r'C:\Projects\MXDs\Chapter8'
pdfFolder=r'C:\Projects\PDFs\Chapter8'
mxdPathList=glob.glob(os.path.join(mxdFolder,'*.mxd'))
formxdPathinmxdPathList:
mxdObject=arcpy.mapping.MapDocument(mxdPath)
arcpy.mapping.ExportToPDF(mxdObject,
os.path.join(pdfFolder,
basepath(
mxdPath.replace('mxd','pdf')
)))
Note
Notethattheoutputfoldermustexistforthiscodetoruncorrectly.Whilethereareos
modulemethodstocheckwhetherapathexists(os.path.exists)andtocreateafolder
(os.mkdir),thatisnotincludedinthiscodesnippetandthe
arcpy.mapping.ExportToPDF()methodwillthrowanexceptioniftheinputoroutput
pathsdonotexist.
Thisexamplecodeisveryusefulandcanbeconvertedintoafunctionthatwouldaccept
thefolderpathasaparameter.Thefunctioncouldthenbeaddedtoascripttool,as
discussedinthelastchapter.
Adjustingmapdocumentelements
Arcpy.mappingincludesimportantmethodsthatwillfacilitatetheautomationofmap
documentmanipulation.Theseincludetheabilitytoaddnewlayersorturnlayersonand
offwithinMXDs,theabilitytoadjustthescaleofthedataframeormoveadataframeto
focusonaspecificregion,andtheabilitytoadjusttextcomponentsofthemap(suchas
titlesorsubtitles).Thesemethodswillbeaddressedaswecontinueourbusstopanalysis.
OpenuptheMXDcalledMapAdjust.mxd.Thisrepresentsourbasemapdocument,with
layersandelementsthatwewilladjusttoourneeds.Itcontainslayersthatwehave
generatedfromouranalysis,andthebaselayersthatfilloutthemap.Therearealsoa
numberoftextelementsthatwillbeautomaticallyreplacedbythescripttofitthespecific
needsofeachmap.However,itdoesnotdoagoodjobofrepresentingtheresultsofthe
analysisasthecensusblocksthatintersectthebusstopbuffersoverlap,makingithardto
interpretthecartography.
Thescriptwillreplacethedatasourceofthecensusblocklayerandthebusstoplayerto
makeitpossibletoonlyproduceonemapforeachbusstop,andthecensusblocksthatare
intersectedwitheachbuffersurroundingthestops.
Tomakethispossible,wewillhavetocreatetwoemptyfeatureclasses:one,withallof
theattributesofthecensusblocks,andtheother,withtheattributesofthebusstops.This
willallowthedatasourcetobereplacedwiththedataproducedbytheanalysis.
OpenuptheSanFrancisco.gdbFileGeodatabaseandrightclickontheChapter8Results
featuredataset.SelectNewfromthedrop-downmenuandthenselectFeatureClass.
NamethefirstfeatureclassSelectedCensusBlocksandmakeitapolygon.Selectthe
defaultskeywordonthenextmenu,andthenonthefollowingmenu,pushtheimport
button.SelecttheCensusBlocksfeatureclassfromtheSanFranciscofeaturedataset;this
willloadthefieldsintothenewfeatureclass.Dothesamethingforasecondfeatureclass
calledSelectedBusStops,butmakesurethatitisapointgeometrytypeandimportthe
schemafromtheBusStopsfeatureclass.Repeatthesameprocessforathirdfeatureclass
calledSelectedStopBuffers,butmakesurethatitisapointgeometrytypeandimport
theschemafromtheBuffersfeatureclass.
Oncethefeatureclasseshavebeencreated,itisnowpossibletousethemtoloadthe
resultsoftheanalysis.Wewillberedoingtheanalysisinmemoryandwritingoutthe
resultstothenewlycreatedfeatureclasses,sothattheentirecensusblockwillbe
captured,insteadofonlytheportionthatintersectswiththebuffer,asitwillbetter
illustratetheresultsoftheanalysis.
TheinitialstateoftheMapAdjust.mxdmapdocumentfeaturesanumberoffeatureclasses
withwhichwearenowfamiliar:thedownloadedfeatureclassBus_Stops,thegenerated
featureclassBuffers,theintersectedandclippedCensusBlocks,andfourfeatureclasses
usedforcartographicpurposes,namelytheStreetsfeatureclass,theParksfeatureclass,
aNeighborhoodsfeatureclass,andanoutlineofSanFrancisco.Therearetwodata
frames,onewiththedefaultnameLayersandanothercalledInset,thatareusedtocreate
thesmallinsetthatwillshowthepositionoftheLayersdataframeasitmovesaroundSan
Francisco.ThesmallrectanglethatdepictstheextentoftheLayersdataframeisanExtent
framecreatedinthepropertiesoftheInsetdataframe.
Hereisanexportedviewoftheinitialstateofthemapdocument:
Theideahere,istousetheinitialresultsofouranalysistogeneratethesymbologyofthe
populationlayeraswellasthebusstoplayerandthebufferlayer.Oncetheyhavebeenset
andsaved,theycanbeusedasabasisfortheindividualmappagesthatwewillbe
producingfromthisbasicmapdocument.
Note
Notethetextelementsthatmakeupthetitleandsubtitle,aswellasthelegendand
attributiontextatthebottomoftherightpane.Theseelementsareavailableforadjustment
alongwiththelayersanddatasourcesthatmakeupthemapdocumentbyusingthe
arcpy.mapping.ListElements()method.
Automatedmapdocumentadjustment
Nowthatweunderstandtheinitialconfigurationofthemapdocument,wewillintroducea
scriptthatwillautomatetheadjustment.Thisscriptwillincludeanumberofconceptsthat
wehavecoveredinthischapterandearlierchapters,andwillalsointroducesomenew
methodsformapdocumentadjustmentsthatwewilldetailinthefollowing:
importarcpy,os
dirpath=os.path.dirname
basepath=os.path.basename
Bus_Stops=r"C:\Projects\SanFrancisco.gdb\Bus_Stops"
selectedBusStop=
r'C:\Projects\SanFrancisco.gdb\Chapter8Results\SelectedBusStop'
selectedStopBuffer=
r'C:\Projects\SanFrancisco.gdb\Chapter8Results\SelectedStopBuffer'
CensusBlocks2010=r"C:\Projects\SanFrancisco.gdb\CensusBlocks2010"
selectedBlock=
r'C:\Projects\SanFrancisco.gdb\Chapter8Results\SelectedCensusData'
pdfFolder=r'C:\Projects\PDFs\Chapter8\Map_{0}'
bufferDist=400
sql="(NAME='71IB'ANDBUS_SIGNAG='FerryPlaza')"
mxdObject=arcpy.mapping.MapDocument("CURRENT")
dataFrame=arcpy.mapping.ListDataFrames(mxdObject,"Layers")[0]
elements=arcpy.mapping.ListLayoutElements(mxdObject)
forelinelements:
ifel.type=="TEXT_ELEMENT":
ifel.text=='TitleElement':
titleText=el
elifel.text=='SubtitleElement':
subTitleText=el
arcpy.MakeFeatureLayer_management(CensusBlocks2010,'blocks_lyr')
layersList=arcpy.mapping.ListLayers(mxdObject,"",dataFrame)
layerStops=layersList[0]
layerCensus=layersList[1]
layerBuffer=layersList[2]
layerBlocks=layersList[3]
iflayerBlocks.dataSource!=selectedBlock:
layerBlocks.replaceDataSource(dirpath(dirpath(layerBlocks.dataSource)),
'FILEGDB_WORKSPACE',basepath(selectedBlock))
iflayerStops.dataSource!=selectedBusStop:
layerStops.replaceDataSource(dirpath(dirpath(layerStops.dataSource)),
'FILEGDB_WORKSPACE',basepath(selectedBusStop))
iflayerBuffer.dataSource!=selectedStopBuffer:
layerBuffer.replaceDataSource(dirpath(dirpath(layerBuffer.dataSource)),
'FILEGDB_WORKSPACE',basepath(selectedStopBuffer))
layerStops.visible=True
layerBuffer.visible=True
layerCensus.visible=False
witharcpy.da.SearchCursor(Bus_Stops,['SHAPE@','STOPID','NAME',
'BUS_SIGNAG','OID@','SHAPE@XY'],sql)
ascursor:
forrowincursor:
stopPointGeometry=row[0]
stopBuffer=stopPointGeometry.buffer(bufferDist)
witharcpy.da.UpdateCursor(layerBlocks,['OID@'])asdcursor:
fordrowindcursor:
dcursor.deleteRow()
arcpy.SelectLayerByLocation_management('blocks_lyr','intersect',
stopBuffer,"","NEW_SELECTION")
witharcpy.da.SearchCursor('blocks_lyr',['SHAPE@','POP10','OID@'])
asbcursor:
inCursor=arcpy.da.InsertCursor(selectedBlock,
['SHAPE@','POP10'])
fordrowinbcursor:
data=drow[0],drow[1]
inCursor.insertRow(data)
delinCursor
witharcpy.da.UpdateCursor(selectedBusStop,['OID@'])asdcursor:
fordrowindcursor:
dcursor.deleteRow()
inBusStopCursor=arcpy.da.InsertCursor(selectedBusStop,['SHAPE@'])
data=[row[0]]
inBusStopCursor.insertRow(data)
delinBusStopCursor
witharcpy.da.UpdateCursor(selectedStopBuffer,['OID@'])asdcursor:
fordrowindcursor:
dcursor.deleteRow()
inBufferCursor=arcpy.da.InsertCursor(selectedStopBuffer,
['SHAPE@'])
data=[stopBuffer]
inBufferCursor.insertRow(data)
delinBufferCursor
layerStops.name="Stop#{0}".format(row[1])
arcpy.RefreshActiveView()
dataFrame.extent=arcpy.Extent(row[-1][0]-1200,row[-1][1]-1200,
row[-1][0]+1200,row[-1][1]-1200)
subTitleText.text="Route{0}".format(row[2])
titleText.text="BusStop{0}".format(row[1])
outPath=pdfFolder.format(str(row[1])+"_"+str(row[-2]))+
'.pdf'
printoutPath
arcpy.mapping.ExportToPDF(mxdObject,outPath)
titleText.text='TitleElement'
subTitleText.text='SubtitleElement'
arcpy.RefreshActiveView()
Wow!That’salotofcode.Let’sreviewitsectionbysectiontoaddresswhateachpartof
thescriptisdoing.
ThiscodewillberuninthePythonWindowoftheMXD,somakesuretoopentheMXD.
Onceitis,openthePythonWindowandrightclickinit,andthenselectLoadfromthe
right-clickmenu.Usingthefilenavigationbrowser,findthescriptcalled
Chapter8_6_AdjustmapCURRENT.pyandselectitbyclickingonit.PushOKanditwill
loadinthePythonWindow.PushingEnterwillexecutethescript,orusethescrollbarto
perusetheloadedlines.
Thevariables
Withinthescript,anumberofvariablesarefirstcreatedtoholdthestringfilepaths,the
integerbufferdistance,andthesqlstatementusedtoidentifythebuslineofinterest:
importarcpy,os
Bus_Stops=r"C:\Projects\SanFrancisco.gdb\Bus_Stops"
selectedBusStop=
r'C:\Projects\SanFrancisco.gdb\Chapter8Results\SelectedBusStop'
selectedStopBuffer=
r'C:\Projects\SanFrancisco.gdb\Chapter8Results\SelectedStopBuffer'
CensusBlocks2010=r"C:\Projects\SanFrancisco.gdb\CensusBlocks2010"
selectedBlock=
r'C:\Projects\SanFrancisco.gdb\Chapter8Results\SelectedCensusData'
pdfFolder=r'C:\Projects\PDFs\Chapter8\Map_{0}'
bufferDist=400
sql="(NAME='71IB'ANDBUS_SIGNAG='FerryPlaza')"
Thesewillbeusedlatertoallowustosearchthelayersandperformanalysisonthem.
Themapdocumentobjectandthetextelements
Becausethiscodewillbeexecutedinanopenmapdocument,wedon’thavetopassan
MXDfilepathtothearcpy.mapping.MapDocument()method.Instead,wewillusethe
keywordCURRENTtoindicatethatwearereferencingtheopenmapdocument:
mxdObject=arcpy.mapping.MapDocument("CURRENT")
dataFrame=arcpy.mapping.ListDataFrames(mxdObject,"Layers")[0]
elements=arcpy.mapping.ListLayoutElements(mxdObject)
forelinelements:
ifel.type=="TEXT_ELEMENT":
ifel.text=='TitleElement':
titleText=el
elifel.text=='SubtitleElement':
subTitleText=el
Oncethemapdocumentobjecthasbeencreated,theLayersdataframeisselectedfroma
listofdataframesusingtheListDataFrames()methodandpassedtothevariablecalled
dataFrame.
Next,thelayoutelementsarepassedasalisttotheelementsvariableusingthe
ListLayoutElements()method.Thelayoutelementsincludethevariouselementsofthe
mapdocumentlayoutview:thelegend,theneatlines,thenortharrow,thescalebar,and
thetextelementsusedastitlesanddescriptions.Unfortunately,thereisnoniceorderto
thelistreturned,astheirpositionthroughoutthelayoutisundetermined.Accesstothetext
elements,whichwewouldliketoassigntoavariableforlateruse,mustbeidentified
usingtwopropertiesoftheelementobjects:thetypeandthetext.Wewanttoadjustthe
titleandsubtitleelements,soaforloopisusedtosearchthroughthelistofelementsand
thepropertiesareusedtofindtheelementsofinterest.
Thelayerobjects
TheMakeFeatureLayertool,partoftheDataManagementtoolset,isusedtocopydata
fromdiskintomemoryasalayer.ArcGISrequiresthegenerationoflayerstoperform
selectionsandoperationsondata,insteadofoperatingonthefeatureclassesdirectly.By
usinglayerstoperformtheseoperations,thesourcefeatureclassesareprotected.
TheMakeFeatureLayertoolisaccessedusingArcPy’s
MakeFeatureLayer_management()method.WhenusingthistoolinthePythonWindow,
theresultisaddedtothemapdocumentasalayerthatwillbevisibleintheTableof
Contents.WhenthetoolisnotusedinthePythonWindowinArcMap,theresultinglayer
isonlygeneratedinmemoryandisnotaddedtothemapdocument.
Intheportionofthefollowingcode,alayercalledblocks_lyrisgeneratedinmemoryby
passingthefilepathofthecensusblocksfeatureclass.Thelayerobjectscontainedwithin
theinitialMXDarethenaccessedusingtheListLayers()methodofthe
arcpy.mapping()module.TheyarereturnedintheorderthattheyarelistedintheTable
ofContentsofthemapdocumentandareassignedtovariablesusinglistindexing,
includingthenewlycreatedblocks_lyr:
arcpy.MakeFeatureLayer_management(CensusBlocks2010,'blocks_lyr')
layersList=arcpy.mapping.ListLayers(mxdObject,"",dataFrame)
layerStops=layersList[0]
layerCensus=layersList[1]
layerBuffer=layersList[2]
layerBlocks=layersList[3]
Replacingthedatasources
Nowthatwehaveassignedthelayerobjectstovariables,wewillcheckwhethertheirdata
sourcesarethecorrectfeatureclassesthatweuseformapproduction.Usingthe
dataSourcepropertyofeachlayerobject,theyarecomparedtothefilepathvariablesthat
wewanttouseasdatasources:
iflayerBlocks.dataSource!=selectedBlock:
layerBlocks.replaceDataSource(dirpath(dirpath(layerBlocks.dataSource)),
'FILEGDB_WORKSPACE',basepath(selectedBlock))
iflayerStops.dataSource!=selectedBusStop:
layerStops.replaceDataSource(dirpath(dirpath(layerStops.dataSource)),
'FILEGDB_WORKSPACE',basepath(selectedBusStop))
iflayerBuffer.dataSource!=selectedStopBuffer:
layerBuffer.replaceDataSource(dirpath(dirpath(layerBuffer.dataSource)),
'FILEGDB_WORKSPACE',basepath(selectedStopBuffer))
Ifstatementsareusedtocheckwhetherthedatasourcesarecorrect.Ifnot,theyare
replacedwiththecorrectdatasourcesusingthereplaceDataSource()layermethod.This
methodrequiresthreeparameters:theworkspace(inthiscase,theFileGeodatabase),the
workspacetype,andthenameofthenewfeatureclassdatasource,whichmustbeinthe
sameworkspaceforthereplaceDataSource()methodtowork(thoughitdoesnotneedto
beinthesamefeaturedataset).
Adjustinglayervisibility
Thelayerobjectshaveapropertythatallowsustoadjusttheirvisibility.Settingthis
BooleanpropertytoTrueorFalsewilladjustthelayer’svisibilityon(True)oroff
(False):
layerStops.visible=True
layerBuffer.visible=True
layerCensus.visible=False
WewantthelayervariablelayerCensus,whichisthenewblocks_lyrobject,tobe
turnedoff,soitissettoFalse,butthebusstopsandbufferlayerobjectsneedtobe
visible,sotheyaresettoTrue.
Generatingabufferfromthebusstopsfeature
class
Allofthevariableshavebeengeneratedorassigned,sothenextstepistousea
SearchCursortosearchthroughtheselectedbusstops.Foreachbusstop,bufferobjects
willbegeneratedtofindcensusblocksthatintersectwiththeseindividualbusstops:
witharcpy.da.SearchCursor(Bus_Stops,['SHAPE@','STOPID','NAME',
'BUS_SIGNAG','OID@','SHAPE@XY'],sql)
ascursor:
forrowincursor:
stopPointGeometry=row[0]
stopBuffer=stopPointGeometry.buffer(bufferDist)
witharcpy.da.UpdateCursor(layerBlocks,['OID@'])as
dcursor:
fordrowindcursor:
dcursor.deleteRow()
ForeachrowofdataretrievedfromtheBusStopsfeatureclass,anumberofattributesare
returned,containedinatuple.Thefirstofthese,row[0],isaPointGeometryobject.This
objecthasabuffermethodthatisusedtogenerateabufferPolygonobjectinmemory,
whichisthenassignedtothestopBuffervariable.Oncethebufferobjectiscreated,the
dataaccessUpdateCursor’sdeleteRow()methodisusedtoerasetherowsinthecensus
blockslayer.Oncetherowshavebeendeleted,thelayercanthenberepopulatedwith
newlyselectedcensusblocksthatwillbeidentifiedinthenextsection.
Intersectingthebusstopbufferandcensusblocks
Toidentifythecensusblocksintersectingwiththebufferaroundeachbusstop,the
ArcToolboxtoolSelectLayerByLocationisinvokedusingtheArcPymethod
SelectLayerByLocation_management():
arcpy.SelectLayerByLocation_management('blocks_lyr','intersect',
stopBuffer,"","NEW_SELECTION")
witharcpy.da.SearchCursor('blocks_lyr',['SHAPE@',
'POP10','OID@'])asbcursor:
inCursor=arcpy.da.InsertCursor(selectedBlock,['SHAPE@',
'POP10'])
fordrowinbcursor:
data=drow[0],drow[1]
inCursor.insertRow(data)
delinCursor
Thismethodrequiresthein-memoryblocks_lyrlayerobjectandthenewlycreatedbuffer
objectassignedtothevariablestopBuffer.Italsorequiresthetypeofselection
intersectandanotherparameterthatcontrolswhethertheselectionwillbeaddedtoan
existingselectionorwillbeanewselection.Inthiscase,wewantanewselection,asonly
thecensusblocksthatintersectthecurrentbusstopareneeded.
Oncethecensusblockshavebeenselectedandidentified,theshapedataandpopulation
dataispassedtothefeatureclassrepresentedbythevariableselectedBlockusingan
InsertCursor.TheInsertCursormustbedeletedusingthedelkeyword,asonlyone
InsertCursororUpdateCursorcanbeinmemoryatatime.
Populatingtheselectedbusstopandbufferfeatureclasses
Inasimilarmanner,thenextstepistopopulatethebusstopandbufferfeatureclassesthat
willbeusedinthemapproduction.Thebusstopsfeatureclassisfirstmadeblankusing
thedeleteRow()method,andthentheselectedbusstopshapefielddataisinsertedinto
thefeatureclass.Thesamestepsarethentakenwiththebusstopbuffersfeatureclassand
thebuffergeometryobject:
witharcpy.da.UpdateCursor(selectedBusStop,['OID@'])asdcursor:
fordrowindcursor:
dcursor.deleteRow()
inBusStopCursor=arcpy.da.InsertCursor(selectedBusStop,['SHAPE@'])
data=[row[0]]
inBusStopCursor.insertRow(data)
delinBusStopCursor
witharcpy.da.UpdateCursor(selectedStopBuffer,['OID@'])asdcursor:
fordrowindcursor:
dcursor.deleteRow()
inBufferCursor=arcpy.da.InsertCursor(selectedStopBuffer,['SHAPE@'])
data=[stopBuffer]
inBufferCursor.insertRow(data)
delinBufferCursor
Updatingthetextelements
Nowthatthedatahasbeengeneratedandwrittentothefeatureclassescreatedtohold
them,thenextstepistoupdatethelayoutelements.Thisincludeslayerpropertiesthatwill
affectthelegend,theextentofthedataframe,andthetextelements:
layerStops.name="Stop#{0}".format(row[1])
dataFrame.extent=arcpy.Extent(row[-1][0]-1200,row[-1][1]-1200,
row[-1][0]+1200,row[-1][1]-1200)
subTitleText.text="Route{0}".format(row[2])
titleText.text="BusStop{0}".format(row[1])
arcpy.RefreshActiveView()
Thenameofthebusstopslayerisadjustedusingitsnamepropertytoreflectthecurrent
busstop.Thedataframeextentisadjustedbycreatinganarcpy.Extentobjectand
passingitfourparameters:Xmin,Ymin,Xmax,Ymax.TogeneratethesevaluesIhaveused
thesomewhatarbitraryvalueof1200feettocreateasquarearoundthebusstop.Thetext
elementsareupdatedusingtheirtextproperty.Finally,theRefreshActiveView()method
isusedtoensurethatthemapdocumentwindowiscorrectlyupdatedtothenewextent.
ExportingtheadjustedmaptoPDF
ThefinalstepistopassthenewlyadjustedmapdocumentobjecttoArcPy’sExportToPDF
method.Thismethodrequirestwoparameters,themapdocumentobjectandastringthat
representsthefilepathofthePDF:
outPath=pdfFolder.format(str(row[1])+"_"+str(row[-2]))+'.pdf'
arcpy.mapping.ExportToPDF(mxdObject,outPath)
titleText.text='TitleElement'
subTitleText.text='SubtitleElement'
arcpy.RefreshActiveView()
ThePDFfilepathstringisgeneratedfromthepdfFolderstringtemplateandtheIDofthe
busstop,alongwiththeobjectIDandthefileextension.pdf.Oncethatandthemap
documentobjectrepresentedbythevariablemxdObjectarepassedtotheExportToPDF
method,thePDFwillbegenerated.Thetextelementsarethenresetandtheviewis
refreshedtoensurethatthemapdocumentwillbereadyforthenexttimethescriptis
used.
RunningthescriptinthePythonWindow
OpenupthemapdocumentcalledMapAdjust.mxdifitisnotopenalready.Openthe
PythonWindowandrightclickinthewindow.SelectLoadfromthemenu.Whenthefile
dialogopens,findthescriptcalledChapter8_6_AdjustmapCURRENT.pyandselectit,
makingsurethatthefilepathswithinitarecorrect.PushOKanditwillloadinthePython
Window.PushEnteroncethescriptisloadedtorunthescript.Itcantakeafewseconds
ormoreforittobeobviousthatthescriptisrunning.
Note
NotethatthePythonWindowisnotagreatplacetoexecuteArcPyscriptsinmostcases,
asitissomewhatlimitedwhencomparedtoIDEs.Usingittoloadandexecuteascript
thatperformsthesemapdocumentadjustmentsisoneofthebestusesofthePython
Window.
Oncethescriptisrunning,theadjustmentstothemapdocumentwillbegintoappearand
repeat.Thisisafascinatingprocess,astheeffectsofrunningthescriptarevisibleina
mannerthatisnotreadilyavailablewhenrunningPythonscripts.OncethePDFsbeginto
begenerated,openoneuptoviewtheoutput.Thescriptwillgenerateamapforeachbus
stopontheselectedbusline,sofeelfreetoshutdownthemapdocumentaftergenerating
asetnumberofthePDFs.
Hereisanexampleoftheoutput:
Themapsgeneratedbythescriptshoweachbusstopatthecenter,surroundedbythe
bufferandthesymbolizedcensusblockswithwhichthebufferintersects.Thetitle,
subtitleandthelegendhavebeenadjustedtoindicatethebusstopdepictedinthemap.
WithArcPy,wearenowincontrolofboththepartsofgeospatialanalysis:theanalysis
itself,andthecartographicproductiondepictingtheresultoftheoutput.
Summary
Inthischapterarcpy.mappingwasintroducedandusedtocontroltheelementsofmap
documentsthatneedtobeadjustedtocreatecustommaps.Byjoininggeospatialanalysis
andmapproductiontogether,weareclosertoutilizingthefullpowerofArcPy.
Inthenextchapter,wewillgofurtherwitharcpy.mappingandcreateascripttoolthatcan
beaddedtoArcToolbox,whichwillruntheanalysisaswellasgeneratemapsfromthe
resultingdata.WewillalsorefinethescriptandintroduceDataDrivenPagestodiscuss
howthatpowerfultoolcanbeusedinanArcPyscript.
Chapter9.MoreArcPy.Mapping
Techniques
Theabilitytocontrolmapdocumentcartography,whilealsorunninggeospatialanalyses,
increasesthepowerandusefulnessofArcPy.Thepropertiesandmethodsof
arcpy.mappingcanbeutilizedtomanipulatelayerobjects,mapscalesanddataframe
extents,oreventosetdefinitionqueries.Bycombiningautomatedgeospatialanalysis
withdynamicmapproduction,scriptedmappingsystemsaremadepossible.Thischapter
willcoverthefollowingtopics:
Arcpy.mappingLayerobjects
Layerobjectdefinitionqueriesandextents
Arcpy.mappingDataFrameobjects
Creatingdynamicallyscaledmaps
Usingarcpy.mappingtocontrolLayer
objects
Arcpy.mappingLayerobjectsareusedtocontrolthepropertiesoflayerswithinmap
documentdataframes.Turninglayervisibilityonandoff,addingnewlayers,and
adjustinglayerordercanallbeaccomplishedusingLayerobjectproperties.
CreatingLayerobjectsinvolvespassingparameterstothearcpy.mapping.ListLayers()
method.AsdiscussedinChapter8,IntroductiontoArcPy.Mapping,whenreferencingan
arcpy.mapping.MapDocumentobject,thelayerswithinthemapdocumentcanbeaccessed
usingzero-basedindexing.ThiscodewillprintthelistofLayerobjectscontainedwithin
thedataframecalledLayersinanMXD:
importarcpy
mxdPath=r'C:\Projects\MXDs\Chapter9\MapDocument1.mxd'
mxdObject=arcpy.mapping.MapDocument(mxdPath)
dataFrame=arcpy.mapping.ListDataFrames(mxdObject,"Layers")[0]
layersList=arcpy.mapping.ListLayers(mxdObject,"",dataFrame)
printlayersList
ThelayerswithinthedataframecalledLayers,havebeenassignedtothevariable
layersListusingtheListLayers()method.EachlayerinlayersListcanbeaccessed
usingzero-basedindexing.Oncethelayershavebeenaccessedwithinthelistandeither
assignedtoavariableorplacedinsideaforloop,thepropertiesandmethodsoftheLayer
objectscanbeutilized.
Note
ThesecondparameteroftheListLayersmethodisemptyhere,butdoesnothavetobe.It
isawildcardparameterthatwilllimitthereturnedLayerobjectstothosethatmatchthe
patternofthewildcard.Forinstance,*StopswouldreturnalllayerswiththenameStops
attheend.Multipleasteriskscanbeusedtofindlayerswiththewordatthebeginning,
middle,orendofthelayername.
Layerobjectmethodsandproperties
Layerobjectpropertiesandmethodscaneitherbereadonly,meaningtheycanbechecked
butnotadjusted,ortheyarereadandwrite,meaningtheycanbeadjustedwithinthe
script.Let’sexploreanumberofthesepropertiesandmethods,andseehowtheycanbe
usedtocontrolthelookandfeelofthemapsproducedfromthemapdocument,aswellas
thedatafromthescriptanalysis.
Definitionqueries
AnimportantpropertyofLayerobjectsistheabilitytodynamicallysetdefinitionqueries.
AdefinitionqueryisaSQLstatementwhereclausethatlimitsthedataavailablefor
display,query,orotherdataoperations(buffers,intersections,etc.)toonlytherowsthat
matchthewhereclause.DefinitionqueriescouldbesetinanMXDbyopeningalayer’s
propertiesmenuandusingtheDefinitionQuerytab,buthereweareconcernedwithhow
toaddthemprogrammatically.Followingisanexampleofhowtodothis:
layersList=arcpy.mapping.ListLayers(mxdObject,"",dataFrame)
busStops=layersList[0]
busStops.definitionQuery="NAME='71IB'ANDBUS_SIGNAG='FerryPlaza'"
ThisvaluablepropertycanbeutilizedtoreformatthecodefromChapter8,Introductionto
ArcPy.Mapping.RememberthecomplicatedsecondportionoftheChapter8_6.pyscript,
whereeachbusstopalongthe71Inboundlineisselectedanditsgeometryiswrittento
anotherfeatureclass?Instead,wecanuseLayerobjectsanddefinitionqueriestoperform
thesametypeofgeometryoperation.Let’sexaminehowthefirstpartofthatoperation
(selectingthebusstopgeometryandcreatingabufferaroundit)lookswhenadefinition
queryisused:
importarcpy
bufferDist=400
mxdPath=r'C:\Projects\MXDs\Chapter9\MapDocument1.mxd'
mxdObject=arcpy.mapping.MapDocument(mxdPath)
dataFrame=arcpy.mapping.ListDataFrames(mxdObject,"Layers")[0]
layersList=arcpy.mapping.ListLayers(mxdObject,"",dataFrame)
busStops=layersList[0]
defQuery="NAME='71IB'ANDBUS_SIGNAG='FerryPlaza'"
busStops.definitionQuery=defQuery
idList=[]
witharcpy.da.SearchCursor(busStops,['OID@'])ascursor:
forrowincursor:
idList.append(row[0])
foroidinidList:
newQuery="OBJECTID={0}".format(oid)
printnewQuery
busStops.definitionQuery=newQuery
witharcpy.da.SearchCursor(busStops,
['SHAPE@','STOPID','NAME','BUS_SIGNAG','OID@','SHAPE@XY'])ascursor:
forrowincursor:
stopPointGeometry=row[0]
stopBuffer=stopPointGeometry.buffer(bufferDist)
Inthisexample,thedefinitionqueryisusedtolimitthepotentialresultsfromthe
SearchCursortothebusstopspecifiedbythequery.However,thisisoverlycumbersome
andthedefinitionquerydoesn’taddmuch,asfirstanotherSearchCursorisneededto
extracttheObjectIDinformationfromthebusStopslayer.Thiscomplicatesthecode
whenonlyoneSearchCursorisnecessary.
Definitionqueriesshouldbeusedtoselecttheblocksthatintersectwiththebuffer,asthis
willeliminatetheneedtousethecomplicatedSearchCursorandInsertCursorsetupthat
wasemployedinChapter8,IntroductiontoArcPy.Mapping.Let’sreformulatethecodeso
thatdefinitionqueriesareproperlyusedonthecensusblockLayerobject.
ThefirststepistoaddsomecodethatwillgeneratetheSQLstatementthatwillbeusedas
thedefinitionquery:
importarcpy
bufferDist=400
mxdPath=r'C:\Projects\MXDs\Chapter9\MapDocument1.mxd'
mxdObject=arcpy.mapping.MapDocument(mxdPath)
dataFrame=arcpy.mapping.ListDataFrames(mxdObject,
"Layers")[0]
layersList=arcpy.mapping.ListLayers(mxdObject,"",dataFrame)
busStops=layersList[0]
censusBlocks=layersList[3]
sql="NAME='71IB'ANDBUS_SIGNAG='FerryPlaza'"
witharcpy.da.SearchCursor(busStops,['SHAPE@','STOPID','NAME',
'BUS_SIGNAG','OID@'],sql)ascursor:
forrowincursor:
busQuery='OBJECTID={0}'.format(row[-1])
busStops.definitionQuery=busQuery
stopPointGeometry=row[0]
stopBuffer=stopPointGeometry.Buffer(bufferDist)
arcpy.SelectLayerByLocation_management(censusBlocks,'intersect',stopBuffer,
"","NEW_SELECTION")
blockList=[]
witharcpy.da.SearchCursor(censusBlocks,
['OID@'])asbcursor:
forbrowinbcursor:
blockList.append(brow[0])
newQuery='OBJECTIDIN('forCOUNTER,oidinenumerate(blockList):
ifCOUNTER<len(blockList)-1:
newQuery+=str(oid)+','
else:
newQuery+=str(oid)+')'
printnewQuery
Inthissection,thecodeassignsthecensusblockslayerintheMXDtothevariable
censusBlocks.ThebusstopsSearchCursoristhencreated,andthe400footbufferis
generatedforeachrowtoselectthecensusblockssurroundingthebusstop.Oncethe
correctblockshavebeenselected,asecondSearchCursorisusedonthecensusBlocks
LayerobjecttofindtheObjectID(usingtheOID@token)oftheselectedblocks.The
ObjectIDsarethenappendedtothelistcalledblockList.
ThislististheniteratedinaforlooptogenerateastringSQLstatement.Usingtheinitial
stringassignedtothevariablenewQuery,theforloopwilladdtheObjectIDsofeach
selectblocktothestringtocreateavalidSQLstatement.Theforloopusesthefunction
enumeratetocountthenumberofloopsthattheforloopperforms;thisallowsforan
if/thenstatementtobeused.Theif/thenstatementdetermineswhatcomesafterthe
ObjectIDinthestring,aseachObjectIDmustbeseparatedbyacomma,exceptforthe
finalObjectID,whichmustbefollowedbytheclosingparenthesis.Theforloopproduces
aSQLstatementsimilartothisexample:
OBJECTIDIN(910,1664,1812,1813,2725,6382)
Theprintstatementattheendisusedtodemonstratetheresultsofthissectionofthe
code,andalsotogivethatwarmfuzzyfeelingthatcomesfromseeingtheresultsofthe
codeworking.OncewearesurethatthecodeisgeneratingvalidSQLstatements(closed
parenthesisandcommaseparatedObjectIDs),thenextstepistoassignthedefinition
querytothecensusBlocksLayerobjectandusetheresulttogenerateamapofthearea.
Controllingthedataframewindowextent
andscale
InChapter8,IntroductiontoArcPy.Mappingwestartedtoexplorethepropertiesand
methodsofthedataframe.Usingthearcpy.Extentobject,wewereabletosettheextent
ofthedataframetoanextentthatwashard-codedintothescript.However,thisdoesnot
alwayscapturetheentireextentoflargecensusblocks.Usingacombinationofdefinition
queriesandthedataframeextentandscaleproperties,wecanavoidtheseunwanted
results.
Therearetwodataframeobjectmethodsusedtoshiftthedataframewindowtothearea
ofinterest,inthiscasetheselectedcensusblocks.Thefirst,whichwearenotusinghere,
isdataFrame.zoomToSelectedFeatures.Thesecond,istoassignthedataframe’sextent
propertytotheextentofthecensusblocklayerafterthedefinitionqueryhasbeen
assignedtoit.
Ipreferthesecondmethod,asitwillworkevenwhenthereisnoselectedcensusblocks.
Also,asthemapsthatareproducedbythisscriptshouldnotshowtheselectionofthe
blocks,wewillhavetoaddcodetoexplicitlycleartheselectiononcethecorrectcensus
blockshavebeenidentified:
censusBlocks.definitionQuery=newQuery
dataFrame.extent=censusBlocks.getExtent()
arcpy.SelectLayerByAttribute_management(censusBlocks,
"CLEAR_SELECTION")
Thedefinitionqueryhasmadeiteasytomovethedataframewindowtotheareaof
interest,astheextentrectangle(orenvelope)ofthelayerisnowonlyaroundthespecified
blocksandthedataFrameextentpropertycanbesettotheextentrectangle.However,this
isnotalwayscartographicallydesirableasitseemsbettertomovethedataframewindow
backfromtheextentrectangle.Todothat,we’llaccessthedataframetheobject’sscale
property.
Thescalepropertycanbesettobeamultiplierofthecurrentscaletoavoidhard-coding
anyspecificdistanceswhenadjustingthedataframeextent.Whenusingthescale
property,itisimportanttoremembertousethearcpy.RefreshActiveView()method,as
itwillrefreshthedataframewindowtothenewscale.
dataFrame.scale=dataFrame.scale*1.1
arcpy.RefreshActiveView()
Asthedataframeextentwassetinthefewlinesbeforethis,thecurrentscalerepresents
theenvelopeoftheselectedcensusblocks.Toadjustit,assessthepropertyandapplya
multiplier.Inthiscase,themultiplieris1.1,butitcouldbeanyvalue.Thismakesthe
resultingmaplookbetterbygivingtheanalysisresultssomebackgroundcontext.
AddingaLayerobject
Thelaststepbeforeexportingoutthemapsistoaddthe400footbufferscreatedaboveas
alayertothedataframeobject.Toaccomplishthis,weneedtocreateasymbolizedlayer
aheadoftimeandcopyitssymbologytoensureitlooksasdesired.Thiswillbeaddedto
theMXDasaplaceholderlayer,andassignedtothebufferLayervariableinthescript.
1. OpenupanMXDandaddthebusstopfeatureclass.
2. RuntheBufferToolintheProximitytoolsetintheAnalysistoolsetofthe
ArcToolbox,addingthebusstopfeatureclassastheinputandsettingthebuffer
sizeto400feet.Afterthetoolhasrun,openthepropertiesofthebufferlayerand
symbolizethelayerasdesired.
3. Oncethelayerhasbeensymbolized,right-clickonthelayerandselectSaveAs
LayerFile.
4. SavethelayerinafolderandclosetheMXD.
5. OpenuptheMapDocument1.mxdmapdocumentandaddthelayerusingtheAdd
Databutton.
6. Makesuretochangethenameto400FootBufferandtoaddittothelegendabove
thePopulationsection.
7. Inthescript,assignthebufferlayertothevariablebufferLayer.
8. Lowerinthescript,inthebusstopSearchCursor,addtheselinesbelowwherethe
bufferisgeneratedaroundthebusstopgeometry:
arcpy.CopyFeatures_management(stopBuffer,
r"C:\Projects\Output\400Buffer.shp")
bufferLayer.replaceDataSource(r"C:\Projects\Output","SHAPEFILE_WORKSPAC
E","400Buffer")
Thesetwolinescopythebuffergeneratedtodiskasashapefileandthenreplacethedata
sourceofthebufferLayerLayerobjectwiththenewlycreatedbuffer.Notethatthename
oftheshapefiledoesnotincludethe.shpextension;theSHAPEFILE_WORKSPACEparameter
makesthisunnecessary.
Note
Tomakesurethateachnewbuffershapefilecanbewrittenoveranexistingshapefile,add
thefollowinglinebelowtheimportarcpylinetomakesurethatfilescanbeoverwritten:
arcpy.env.overwriteOutput=1
Exportingthemaps
Thefinalstepofthisscriptistoexportthemapsoftheareasurroundingeachbusstop.To
dothis,wewillborrowsomecodefromthescriptChapter8_6_AdjustMap.pyandaddthe
wholescripttoafilecalledChapter9.py.Thiscodewillidentifyandadjustthetitleand
subtitleelements,makingitpossibletocustomizeeachresultingPDF:
importarcpy
arcpy.env.overwriteOutput=1
bufferDist=400
pdfFolder=r'C:\Projects\PDFs\Chapter9\Map_{0}'
mxdPath=r'C:\Projects\MXDs\Chapter9\MapDocument1.mxd'
mxdObject=arcpy.mapping.MapDocument(mxdPath)
dataFrame=arcpy.mapping.ListDataFrames(mxdObject,"Layers")[0]
elements=arcpy.mapping.ListLayoutElements(mxdObject)
forelinelements:
ifel.type=="TEXT_ELEMENT":
ifel.text=='TitleElement':
titleText=el
elifel.text=='SubtitleElement':
subTitleText=el
layersList=arcpy.mapping.ListLayers(mxdObject,
"",dataFrame)
busStops=layersList[0]
bufferLayer=layersList[2]
censusBlocks=layersList[4]
sql="NAME='71IB'ANDBUS_SIGNAG='FerryPlaza'"
witharcpy.da.SearchCursor(busStops,['SHAPE@',
'STOPID',
'NAME',
'BUS_SIGNAG',
'OID@'],sql)ascursor:
forrowincursor:
busQuery='OBJECTID={0}'.format(row[-1])
busStops.definitionQuery=busQuery
stopPointGeometry=row[0]
stopBuffer=stopPointGeometry.buffer(bufferDist)
arcpy.CopyFeatures_management(stopBuffer,r"C:\Projects\Output\400Buffer.shp
")
bufferLayer.replaceDataSource(r"C:\Projects\Output",
"SHAPEFILE_WORKSPACE",
"400Buffer")
arcpy.SelectLayerByLocation_management(censusBlocks,
'intersect',
stopBuffer,
"",
"NEW_SELECTION")
blockList=[]
witharcpy.da.SearchCursor(censusBlocks,
['OID@'])asbcursor:
forbrowinbcursor:
blockList.append(brow[0])
newQuery='OBJECTIDIN('
forCOUNTER,oidinenumerate(blockList):
ifCOUNTER<len(blockList)-1:
newQuery+=str(oid)+','
else:
newQuery+=str(oid)+')'
printnewQuery
censusBlocks.definitionQuery=newQuery
dataFrame.extent=censusBlocks.getExtent()
arcpy.SelectLayerByAttribute_management(censusBlocks,
"CLEAR_SELECTION")
dataFrame.scale=dataFrame.scale*1.1
arcpy.RefreshActiveView()
subTitleText.text="Route{0}".format(row[2])
titleText.text="BusStop{0}".format(row[1])
outPath=pdfFolder.format(str(row[1]))+'.pdf'
printoutPath
arcpy.mapping.ExportToPDF(mxdObject,outPath)
titleText.text='TitleElement'
subTitleText.text='SubtitleElement'
censusBlocks.definitionQuery=''
busStops.definitionQuery=''
Summary
Inthischapter,wecoveredtheuseoflayerdefinitionqueries,dataframeextentsand
scales,andlayersourcereplacementtoeasetheproductionofmaps.Byusingdefinition
queries,thelayerscanbemodifiedtonewextents,makingiteasiertozoomintothelayer
extentandtosetthescaleofthedataframe.Thedefinitionqueriesalsolimitwhich
membersofalayeraredisplayedwithinthedataframe.Layersourcereplacementwas
usedasacartographiccontrol,allowingustopre-generatethestyleofalayerandadjust
thedatathatitrepresenteddynamically.
Inthenextchapter,wewillcombinethelessonsfromthelastthreechapters,allowingus
tocreateascripttoolthatwillrunanalysisandproducespreadsheetsandmapsfromthe
analysisresults.
Chapter10.AdvancedGeometryObject
Methods
Inthischapter,wewilldiscussadvancedGeometryobjectmethods,previouslydiscussed
inChapter6,WorkingwithArcPyGeometryObjects.Thegoalofthisbookistogivean
introductiontoArcPyanditsmodules,whilealsodemonstratinghowtoapplythesetools
whencreatingenduringGISworkflows.Performingananalysisonceisgood,butdoingit
overandover,withtheclickofabutton,isbetter.Makingtheanalysisresultssharablein
anindustrystandardformatisalsodesirable.IntheArcGISworld,thebestwaytodothis
iswithArcPyandscripttoolsthattakeadvantageofGeometryobjectmethods.
Thischapterwillcoverthefollowingtopics:
AddingcommonfunctionstoamoduleinthePythonpath
Makingtheanalysismoreadvancedbyaddingpointgeneration
AdvancedPolygonobjectmethods
UsingtheXLWTtocreateExcelspreadsheets
CreatingaPythonmodule
Animportantsteptowardscreatingreusablecodeistopackageitscomponentfunctions
intoamodulethatcanbecalledfromthePythonpathbyanyscript.Tostart,weneedto
createafolderinthesite-packagesfolderwherePythonmodulesareplacedwhen
downloadedandextractedusingthePythonmoduleprocess,orwhenrunningthe
setup.pyscriptincludedwithsharedmodules.
Modulespackagetogetherfunctionsinoneormorescriptsintoafolderthatcanbeshared
withothers(thoughtheyoftendependonothermodulestorun).Wehaveusedsomeofthe
built-inmodulessuchasthecsvmoduleandthird-partymodulessuchasArcPy.Let’s
exploretheirconstructiontogetafeelofhowamoduleispackagedforuseandsharing.
Note
Manymodulesarenotplacedwithinthesite-packagesfolder,buttheyrequirethePython
pathtobemodifiedtomakethemimportable.Placingmoduleswithinthesite-packages
foldereliminatesthisrequirement.
Openupthesite-packagesfolderinWindowsExplorerbynavigatingto
C:\Python27\ArcGIS10.2\Lib\site-packages(orC:\Python27\Lib\site-packagesif
you’reusingthestandardPython2.7installation)folder.Onceinthefolder,createanew
foldercalledcommon,asshowninthefollowingscreenshot:
The__init__.pyfile
Withinthisfolder,aspecialfileneedstobeaddedtoletPythonrecognizethefolderasa
module.Thisfile,called__init__.py,takesadvantageofthespecialpropertyofPython
calledmagicobjectsorattributesthatarebuiltintoPython.Thesemagicobjectsusethe
leadingandtrailingdoubleunderscoretoavoidanyconfusionwithcustomfunctions.
Note
Notethatthesearedoubleunderscores;singleunderscoresareusuallyusedforso-called
privatefunctionswithincustomPythonclasses.
The__init__.pyfileisusedtoindicatethatthefolderisamodule(makingitimportable
usingtheimportkeyword),andtoinitiatethemodulebycallinganymodulesthatitmay
inturnrelyon.However,thereisnorequirementtoaddimportcommandstothe
__init__.pyfile;itcanbeanemptyfileandwillstillperformthemodulerecognition
functionalitythatwerequire.
1. OpenupIDLEorAptanaoryourfavoriteIDE,andinthefoldercalledcommon,
addanewPythonfileandcallit__init__.py.Thisfilewillremainemptyfornow.
2. Nowthatwehaveinitiatedthemodule,weneedtocreateascriptthatwillholdour
commonfunctions.Let’scallituseful.pybecausethesefunctionswillbemost
usefulforthisanalysisandothers.
3. Thenextstepistotransferfunctionsthatwehadcreatedinearlierchapters.These
valuablefunctionsarelockedintothosescripts,sobyaddingthemtouseful.py,we
willmakethemavailabletoallotherscriptswecraft.
Note
OneimportantfunctionistheformatSQLMultiplefromChapter4,ComplexArcPy
ScriptsandGeneralizingFunctions,whichgeneratesSQLstatementsusinga
templateandalistofdata.Byaddingittouseful.py,wewillbeabletocallthe
functionanytimeaSQLstatementisrequired.
4. OpenthescriptChapter4Modified2.pyandcopythefunction,andthenpasteitinto
useful.py.Ithasnodependencies,soitdoesnothavetobemodified.
AnotherusefulfunctionfromthatscriptistheformatIntersectfunctionthatgeneratesa
stringoffilepathsthatareusedwhenrunningtheArcToolboxIntersecttool.Whilewe
havereacheddeeperintoArcPysincethatfunctionwasdesigned,andnolongerneedto
calltheIntersecttoolinourbusstopanalysis,itdoesnotmeanthatwewillneverneedto
callitinthefuture.Itisstillusefulandshouldbeaddedtouseful.py.
ThelastfunctionthatwecanraidisthecreateCSV()function.Copyandpasteitfrom
Chapter4Modified.pyintouseful.py.However,toavoidtheneedtoimporttheCSV
moduleseparately,wewillneedtomodifythefunctionslightly.Hereishowitshould
look:
defcreateCSV(data,csvname,mode='ab'):
'createsacsvfile'
importcsv
withopen(csvname,mode)ascsvfile:
csvwriter=csv.writer(csvfile,delimiter=',')
csvwriter.writerow(data)
delcsv
Byimportingandthendeletingthecsvmodule,weareabletouseittogeneratethecsv
fileandthenremovethemodulefrommemoryusingthedelkeyword.
Nowthatwehavethefunctionswewillbereusingsavedintheuseful.pyscript,inside
thecommonmodule,let’sexplorehowtocallthemusingPython’simportmethod.Open
upaPythonexecutable,usingeitherPython.exeorIDLE,orthebuilt-interminalin
Aptana.Atthetriplechevronprompt(>>>),writethefollowingline:
>>>fromcommon.usefulimportcreateCSV>>>
Ifthesecondtriplechevron-shapedpromptappears,thefunctionwascorrectlyimported
fromthemodule.Toimportthefunctionsinthismoduleinascript,usethesameimport
structureandlistthefunctionsdesired,separatingthemusingacomma:
fromcommon.usefulimportcreateCSV,formatSQLMultiple
Thefunctionsinthescriptuseful.pywerecalledusingPythondotnotation.Thisismade
possiblebecausethe__init__.pyfileindicatestoPythonthatthefoldercommonisnowa
module,andthatitshouldexpectamethodcalledusefultobepresent,withthefunctions
createCSVandformatSQLMultipleinsideit.
Addingadvancedanalysiscomponents
ThebusstopanalysiswehaveusedtointroduceArcPycanbefurtherextendedto
generatemorerefinedresults.Tobetterestimatethetruenumberofpeoplethateachbus
stopserves,let’saddafunctionthatwillgeneraterandompointswithintheblocks
considered,whileeliminatingparksandotherareasthatdonotcontainhousing.
Todothis,weneedtointroduceanewdatasetfromtheSanFranciscogeodatabase,the
RPD_Parksfeatureclass.Byusingthisfeatureclasstoreducetheareaconsideredforour
analysis,wecangenerateamorerealisticassessmentoftheserviceareapopulationfor
eachbusstop.
WhileusingtheArcToolboxErasetooltoerasethearearepresentedintheRPD_Parks
polygonswouldbeausualstepwhenrunningaspatialanalysis,therearedrawbacksto
thisoption.ThefirstisthattheErasetoolisonlyavailablewiththeArcGISforDesktop
Advancedlicenselevel,makingitavailableonlytocertainusers.Theseconddrawbackis
thatthetoolproducesanintermediatedataset,somethingtobeavoidedwherever
possible.
UsingArcPywillgiveustheabilitytoavoidbothofthesedrawbacks.Wecancreatea
scriptthatwillgeneraterandompointsonlywithinthefractionofthecensusblock
polygonsthatdonotintersectwiththeRPD_Parksfeatureclass.Todothis,wewillreach
deeperintothemethodsoftheArcPyPolygonobject.
AdvancedPolygonobjectmethods
InChapter6,WorkingwithArcPyGeometryObjectswestartedexploringtheArcPy
Geometryobjectsandhowtousetheirmethodstoperformin-memoryspatialanalysis.
TheBufferandIntersectmethodsoftheseobjectswereintroducedandusedtogenerate
analysisresults.Next,wewilldiscussmoreofthesemethodsandshowhowtheycanhelp
improvein-memoryspatialanalysis.
ThePolygonobjecthasamethodcalledDifferencethatallowsustofindtheareaofnonintersectionwhentwopolygonsintersect.Passingacensusblockpolygonandapark
polygonasparameterswillreturn(asapolygonobject)thefractionofthefirstparameter
wherenooverlapoccurs.AnotherimportantmethodiscalledOverlaps,whichiscalledto
testwhethertwoGeometryobjects(points,lines,orpolygons)intersect.Ifthereisan
overlap,theOverlapsmethodwillreturnTrue,whilereturningFalseifthereisno
overlapbetweenthetwoobjects.Unionisalsoanimportantmethodthatwillbeused
withinthischapter,itallowsfortwoGeometryobjectstobeunionedintooneobject.
Let’sexploretheseimportantmethods.Tofindthenon-intersectareaoftwopolygon
objects,thefollowingfunctioncombinestheOverlapsandDifferencemethods:
defnonIntersect(poly1,poly2):
'returnsareaofnon-intersectbetweentwopolygons'
ifpoly1.overlaps(poly2)==True:
returnpoly1.difference(poly2)
ThefunctionnonIntersectacceptstwoPolygonobjectsasparameters.Thefirst
parameter,poly1,isthepolygonofintersect(thecensusblockpolygon)andthesecond
parameter,poly2,isthepolygontobecheckedforoverlap.Theifconditionalusesthe
OverlapsmethodandreturnsTrueifthereisanoverlapbetweenthetwoparameters.If
thereisanyoverlap,thedifference()methodreturnsthenon-intersectareaasapolygon
object.However,thisfunctionshouldbeextendedtocoversituationswherethe
Overlaps()methodreturnsFalse:
defnonIntersect(poly1,poly2):
'returnsareaofnon-intersectbetweentwopolygons'
ifpoly1.overlaps(poly2)==True:
returnpoly1.difference(poly2)
else:
returnpoly1
ThefunctionwillnowreturnthefirstparameterwhentheOverlapsmethodreturnsFalse,
indicatingthatthereisnooverlapbetweenthetwopolygonobjects.Thisfunctionisnow
completeandavailabletobeusedinananalysis.BecausenonIntersect()isafunction
thatcanbeusedinotherspatialanalyses,copyitandaddittouseful.py.
Generatingrandompointstorepresentpopulation
Thenextsteptoimprovethebusstopanalysisistogeneratepointstorepresentthe
populationofeachcensusblock.Whilerandompointswillnotprovideaperfect
representationofthepopulation,itwillserveasagoodmodelofthepopulationandallow
ustoavoidareaaveragingtofindtheroughpopulationofeachcensusblockservedbya
busstop.TheCreateRandomPointstoolintheArcToolboxDataManagementtoolset
makesitsimpletogeneratethepoints.
TheCreateRandomPointstoolacceptsanumberofrequiredandoptionalparameters.As
thetoolgeneratesafeatureclass,therequiredparametersaretheworkspacewherethe
featureclasswillbeplacedandthenameofthefeatureclass.Theoptionalparametersof
interestaretheconstrainingfeatureclassandthenumberofpointstobegenerated.Aswe
arelookingtoavoidcreatingnewfeatureclassesintheintermediatestepsofouranalysis,
wecanutilizethein_memoryworkspace,whichallowsfeatureclassestobegeneratedin
memory,meaningtheyarenotwrittentotheharddrive.
Becausethereisaneedtogenerateaspecificnumberofrandompointsforeachcensus
block,weshouldcreateafunctionthatwillacceptaconstrainingpolygonandpopulation
figurethatrepresentseachcensusblock.Thein_memoryworkspacewon’tworkforevery
situation,however,sowe’llprovidetheworkspaceparameterwithadefaultvalue:
defgeneratePoints(fc,pop,constrant,workspace='in_memory'):
'generaterandompoints'
importos,arcpy
arcpy.CreateRandomPoints_management(workspace,fc,
constrant,"",pop,"")
returnos.path.join(workspace,fc)
Thefunctionwillcreatethefeatureclassintheworkspacedesiredandwillreturnthepath
(joinedusingtheosmodule)tothefeatureclassforuseintherestofthescript.This
functionisalsoreusableandshouldbecopiedintouseful.py.
Usingthefunctionswithinascript
Nowthatwehavecreatedthefunctionsthatwillhelpustorunamoreadvancedspatial
analysis,let’saddthemtoascriptalongwithsomeSearchCursorstoiteratethroughthe
data:
#Importthenecessarymodules
importarcpy,os
fromcommon.usefulimportnonIntersect,generatePoints,createCSV
#Addanoverwritestatement
arcpy.env.overwriteOutput=True
#Definethedatainputs
busStops=r'C:\Projects\SanFrancisco.gdb\SanFrancisco\Bus_Stops'
parks=r'C:\Projects\SanFrancisco.gdb\SanFrancisco\RPD_Parks'
censusBlocks=
r'C:\Projects\SanFrancisco.gdb\SanFrancisco\CensusBlocks2010'
csvName=r'C:\Projects\Output\Chapter10Analysis.csv'
#Createthespreadsheetinmemoryandaddfieldheaders
headers='LineName','StopID','TotalPopulationServed'
createCSV(headers,csvName,mode='wb')
#Copythecensusblockdataintoafeaturelayer
arcpy.MakeFeatureLayer_management(censusBlocks,'census_lyr')
#CopytheparkdatageometriesintoalistandunionthemallparkGeoms=
arcpy.CopyFeatures_management(parks,arcpy.Geometry())
parkUnion=parkGeoms[0]
forparkinparkGeoms[1:]:
parkUnion=parkUnion.union(park)
#Createasearchcursortoiteratethebusstopdata
sql="NAME='71IB'ANDBUS_SIGNAG='FerryPlaza'"
witharcpy.da.SearchCursor(busStops,['NAME','STOPID','SHAPE@'],sql)as
cursor:
forrowincursor:
lineName=row[0]
stopID=row[1]
stop=row[2]
busBuf=stop.buffer(400)
#Selectcensusblocksthatintersectthebusbuffer
arcpy.SelectLayerByLocation_management("census_lyr","intersect",
busBuf,'','NEW_SELECTION')
#UseasecondCursortofindtheselectedpopulation
totalPopulation=0
witharcpy.da.SearchCursor("census_lyr",['SHAPE@','POP10',
'BLOCKID10'])asncursor:
fornrowinncursor:
block=nrow[0]
checkedBlock=nonIntersect(block,parkUnion)
blockName=nrow[2]
population=nrow[1]
ifpopulation!=0:
points=generatePoints("PopPoints",
population,checkedBlock)
pointsGeoms=
arcpy.CopyFeatures_management(points,arcpy.Geometry())
pointsUnion=pointsGeoms[0]
forpointinpointsGeoms[1:]:
pointsUnion=pointsUnion.union(point)
pointsInBuffer=busBuf.intersect(pointsUnion,1)
intersectedPoints=pointsInBuffer.pointCount
totalPopulation+=intersectedPoints
#Addthetallieddatatothespreadsheet
data=lineName,stopID,totalPopulation
print'datawritten',data
createCSV(data,csvName)
#Startthespreadsheettoseetheresults
os.startfile(csvName)
Let’sreviewthecode,sectionbysection,asthatisalottotakeinatfirst.
Theimportportioniswherewecalltheusualmodules,arcpyandos,alongwithour
customfunctionsinthecommonmodule:
importarcpy,os
fromcommon.usefulimportnonIntersect
fromcommon.usefulimportgeneratePoints
fromcommon.usefulimportformatSQLMultiple
fromcommon.usefulimportnonIntersectcreateCSV
Asdiscussedpreviously,thefunctionsinthecommonmodule’susefulmethodarecalled
usingthePythondotnotationandthefrom…import…importationstyle,makingthem
availabledirectly.Manyfunctionscanbeimportedononeline,separatedbycommas,or
individuallyasshownhere.
Thenextline,whichsetstheArcPyEnvironmentoverwritepropertytoTrue,isvery
importantbecauseitallowsustooverwritetheresultsoftheCreaterandompoints
operation.Iftheresultswerenotoverwritten,thefunctionresults,whichotherwisewould
useallavailablememoryandcausethescripttofail:
arcpy.env.overwriteOutput=True
Note
Itisimportanttobecarefulwiththisoverwritesettingbecauseitwillallowforanyfeature
classtobeoverwritten.Allofouroutputisinmemoryandonlygeneratedfortheanalysis,
sothereislittleneedtoworryhere,buttakecaretomakesurethatnothingimportantis
overwrittenwhenrunningascript.
Thenextportionisthesetofvariablesthatwillbeusedinthisscript,andwillinitiatethe
spreadsheetthatwillbeusedtocollecttheresultsoftheanalysis:
busStops=r'C:\PacktDB.gdb\SanFrancisco\Bus_Stops'
parks=r'C:\PacktDB.gdb\SanFrancisco\RPD_Parks'
censusBlocks=r'C:\PacktDB.gdb\SanFrancisco\CensusBlocks2010'
csvName=r'C:\Projects\Output\Chapter10Analysis.csv'
headers='LineName','StopID','TotalPopulationServed'
createCSV(headers,csvName,mode='wb')
ThefilepathsassignedtovariablesherecouldbereplacedwithArcPyparametersifwe
weretoturnthisintoascripttool,butfornow,thehard-codedpathsarefine.Belowthe
variables,theresultsspreadsheetiscreatedandthecolumnfieldheadersareadded.
Itisworthnotingthatthespreadsheetiscreatedusingthewbmode.Thismodeofbinary
fileopening,knownaswb(writebinary),isusedforcreatinganewfile.Itmustbe
explicitlypassedintothecreateCSV()functionasthedefaultmodeparameterisab
(appendbinary),whichwillcreateanewfileifitdoesnotexist,oraddtoonethatalready
exists(athirdbinarymodeisrborreadbinary,whichisusedforopeninganexisting
file).
Thenextfewlinesmakedatainthefeatureclassesavailableinmemory.Thecensusblock
dataisconvertedintoaFeatureLayer,whiletheRPD_Parksdataisreadintomemoryas
alistofPolygonobjectsthatisthenunionedintoasingle,unifiedPolygonobjectcalled
parkUnion:
arcpy.MakeFeatureLayer_management(censusBlocks,'census_lyr')parkGeoms=
arcpy.CopyFeatures_management(parks,arcpy.Geometry())
parkUnion=parkGeoms[0]
forparkinparkGeoms[1:]:
parkUnion=parkUnion.union(park)
ByusingtheCopyFeaturestoolintheDataManagementtoolset,theparkGeomsvariable
ispassedalistofthegeometriesforeachrowofdataintheRPD_Parksfeatureclass.
However,wedon’twanttohavetoiteratethroughtheparkgeometriestocomparethemto
eachcensusblock,sotheUnionmethodisinvokedtocreateonePolygonobjectfromthe
entirelist.ByassigningthefirstmemberofthelisttotheparkUnionvariable,andthen
iteratingthroughtheparkGeomslisttouniontheothergeometriesonebyone,theresultis
onePolygonobjectthatrepresentsallparkswithintheRPD_Parksdataset.
Onceallofthemoduleshavebeenimportedandthevariableshavebeenassigned,wecan
entertheforloopofthedataaccessSearchCursortobegintheanalysis.However,we
don’twanttorunthisforallofthebusstops,sowewilluseaSQLstatementwhere
clause,tolimittheanalysistoasinglebusline:
sql="NAME='71IB'ANDBUS_SIGNAG='FerryPlaza'"
witharcpy.da.SearchCursor(busStops,['NAME','STOPID','SHAPE@'],sql)as
cursor:
forrowincursor:
lineName=row[0]
stopID=row[1]
stop=row[2]
busBuf=stop.buffer(400)
arcpy.SelectLayerByLocation_management("census_lyr","intersect,busBuf,'','N
EW_SELECTION')
totalPopulation=0
Thefirstportionoftheiterationinvolvesenteringtheforloopandassigningthevaluesof
eachrowtoavariable.APolygonobjectbufferof400feetiscreatedaroundthe
PointGeometryobjectreturnedbytheSearchCursor.Thisbufferisthenusedtointersect
withthecensusblocksFeatureLayertofindandselectallofthecensusblocksthat
intersectthebuffer.Totallythepopulationservedbyeachbuffer,thevariable
totalPopulationiscreated.
Oncetheselectionhasbeenperformed,asecondSearchCursorcanbeusedtoiterate
throughtheselectedblockstoretrievetheirpopulationvaluesandPolygonobjectsfor
randompointgeneration:
witharcpy.da.SearchCursor("census_lyr",['SHAPE@','POP10',
'BLOCKID10'])asncursor:
fornrowinncursor:
block=nrow[0]
checkedBlock=nonIntersect(block,parkUnion)
blockName=nrow[2]
population=nrow[1]
Inthisiteration,onceeachcensusblockhasbeenretrieved(intheformofaPolygon
object),theblockisthencheckedagainsttheunionedparkgeometryusingthe
nonIntersectfunctioncreatedpreviously.Thisensuresthatthepointswillonlybe
createdwithinareasthatarenotparks,thatis,morelikelytorepresentwherepeople
wouldlive.Thepopulationvaluesarealsoretrieved.
Oncetheconstrainingpolygon(forexamplethecensusblock)hasbeenevaluatedandany
potentialparkportionhasbeenremoved,andthepopulationvalueisavailable,therandom
pointscanbegeneratedusingthegeneratePoints()function:
ifpopulation!=0:
points=generatePoints("PopPoints",population,checkedBlock)
pointsGeoms=arcpy.CopyFeatures_management(points,arcpy.Geometry())
pointsUnion=pointsGeoms[0]
forpointinpointsGeoms[1:]:
pointsUnion=pointsUnion.union(point)
pointsInBuffer=busBuf.intersect(pointsUnion,1)
intersectedPoints=pointsInBuffer.pointCount
totalPopulation+=intersectedPoints
ThegeneratePoints()functionrequiresthreeparameters.Thefirstisthenameofthe
featureclasstobegenerated;thiswillbeoverwritteneachtimeitisgenerated,thus
avoidingtheoveruseofmemorybycreatinganin_memoryfeatureclassforeachcensus
block.TheothertwoparametersarethepopulationvalueandtheconstrainingPolygon
object.
Oncethesehavebeenpassedtothefunction,itreturnsafilepathtothenewlycreated
featureclassandassignsthefilepathtothevariablepoints.Thegeometriesinpointsare
thenextractedusingtheCopyFeaturestoolandassignedtothevariablepoints.The
Unionmethodisagainusedtocreateasingle,unifiedpopulationPointGeometryobject
thatwillbeintersectedwiththebusstopbuffer.Oncethisintersectionhasbeenrun,the
resultinggeometriesareassignedtothepointsInBuffervariableandthepointCount
methodisusedtofindthenumberofpointsthatweregeneratedwithinthebufferedarea.
Thisisourestimateofpopulationwithinthecensusblock,andthisvalueisaddedtothe
totalPopulationvariabletoeventuallyyieldthetotalestimatedpopulationwithin400
feetofthebusstop.
Thefinallinesofthescriptdemonstratehowthedataiscollectedintoatupleandpassed
tothecreateCSV()moduletobewrittentoourfinalspreadsheet:
data=lineName,stopID,totalPopulation
print'datawritten',data
createCSV(data,csvName)
os.startfile(csvName)
Thelastline,os.startfile(csvName),usesthestartfilemethodoftheosmoduleto
automaticallyopenthespreadsheetoncetheanalysisiscompleted.Inthiscase,the
spreadsheetC:\Projects\Output\Chapter10Analysis.csvhasbeenpopulatedwiththe
resultsoftheanalysisandisopenedtodisplaytheseresults.However,theusermayhave
toindicatethatthelinesarecommaseparatedvaluestoopenthescript.
Insteadofcreatingacommaseparatedvalue,wecantakeadvantageofanotherPython
modulethatisinstalledwhenArcGIS10.2andArcPyisinstalled.Thismodule,called
XLWT,isusedtogenerateExcelspreadsheets,andalongwiththeExcelspreadsheetreading
moduleXLRD,isoneofthemostusefulmodulesavailabletousersofPython.
CreatinganXLSusingXLWT
XLWTisapowerfulmodulethatallowsforamultitudeofstylingoptions.However,for
ourpurposeswecanignorethoseoptionsandcreateafunctionthatwillgeneratea
spreadsheetwiththeresultsofourspatialanalysis.Thisfunctioncanofcoursebeaddedto
common.useful:
defgenerateXLS(indatas,sheetName,fileName):
importxlwt
workbook=xlwt.Workbook()
sheet=workbook.add_sheet(sheetName)
forYCOUNTER,datainenumerate(indatas):
forXCOUNTER,valueinenumerate(data):
sheet.write(YCOUNTER,XCOUNTER,value)
workbook.save(fileName)
Thisfunctionrequiresthreeparameters,indatas-alistcontainingrowsofiterabledata,a
stringsheetname,andastringfilenamethatendswiththe.xlsextension.
Tousethisfunction,addittocommon.useful.Onceithasbeenadded,copyandrename
theolderanalysisscriptsothatitcanbeadjusted:
importarcpy,os
fromcommon.usefulimportnonIntersect,generatePoints,generateXLS
arcpy.env.overwriteOutput=True
busStops=r'C:\Projects\PacktDB.gdb\SanFrancisco\Bus_Stops'
parks=r'C:\Projects\PacktDB.gdb\SanFrancisco\RPD_Parks'
censusBlocks=r'C:\Projects\PacktDB.gdb\SanFrancisco\CensusBlocks2010'
xlsName=r'C:\Projects\Output\Chapter10Analysis.xls'
headers='LineName','StopID','TotalPopulationServed'
indatas=[headers]
arcpy.MakeFeatureLayer_management(censusBlocks,'census_lyr')parkGeoms=
arcpy.CopyFeatures_management(parks,arcpy.Geometry())
parkUnion=parkGeoms[0]
forparkinparkGeoms[1:]:
parkUnion=parkUnion.union(park)
sql="NAME='71IB'ANDBUS_SIGNAG='FerryPlaza'"
witharcpy.da.SearchCursor(busStops,['NAME','STOPID',
'SHAPE@'],sql)ascursor:
forrowincursor:
lineName=row[0]
stopID=row[1]
stop=row[2]
busBuf=stop.buffer(400)
arcpy.SelectLayerByLocation_management("census_lyr","intersect",busBuf,'','
NEW_SELECTION')
totalPopulation=0
witharcpy.da.SearchCursor("census_lyr",['SHAPE@','POP10',
'BLOCKID10'])asncursor:
fornrowinncursor:
block=nrow[0]
checkedBlock=nonIntersect(block,parkUnion)
blockName=nrow[2]
population=nrow[1]
ifpopulation!=0:
points=
generatePoints("PopPoints",population,checkedBlock)
pointsGeoms=arcpy.CopyFeatures_management(points,arcpy.Geometry())
pointsUnion=pointsGeoms[0]
forpointinpointsGeoms[1:]:
pointsUnion=pointsUnion.union(point)
pointsInBuffer=busBuf.intersect(pointsUnion,1)
intersectedPoints=pointsInBuffer.pointCount
totalPopulation+=intersectedPoints
data=lineName,stopID,totalPopulation
indatas.append(data)
generateXLS(indatas,"Results",xlsName)
os.startfile(xlsName)
WecannowgenerateExcelspreadsheetsjustaseasilyaswehavegeneratedCSVfiles
whileemployingareusablefunction.Wenowhavetheabilitytoperformrepeatable
spatialanalysisfastandcanproduceresultsinindustrystandardformats.
Summary
Inthischapter,wehaveexploredhowtocreatemodulesandreusablefunctionsthatwill
savescriptingtimeinthefuturebyallowingustoavoidrewritingtheseusefulfunctions.
WefurtherexploredthemethodsavailablethroughArcPyGeometryobjects,includingthe
Intersect,Overlaps,andUnionmethods.Wecreatedaspatialanalysisthatwritesno
featureclassestodisk,makingitsothattheanalysistimeisreducedandunnecessaryfiles
areavoided.Finally,weexploredhowtogenerateExcelspreadsheetsusingtheXLWT
modulesothatanalysisresultscanbesharedinindustrystandardformats.
Inthenextchapter,wewillexplorehowtouseArcPytointeractwiththeArcGISfor
desktopextensionssuchasNetworkAnalystandSpatialAnalyst.Byincorporatingtheir
functionalitywithinascript,wefurtherincreaseourabilitytocreatefastandrepeatable
spatialanalysisworkflows.
Chapter11.NetworkAnalystandSpatial
AnalystwithArcPy
UseoftheArcGISforDesktopextensionsalsobenefitsfromthepowerofPythonand
ArcPy.Theabilitytomodelroutesusingastreetsdatasetorabusroutesdatasetusing
ArcPywillhelpusconvertentireworkflowsintoscripttools.BothNetworkAnalystsand
SpatialAnalystshaveaccessmodulesbuiltintoArcPyforimprovedcontroloftheir
availabletools,methods,andproperties.
Thischapterwillcoverthefollowingtopics:
Creatingasimplenetworkdataset
Checkingouttheextensions
TheArcPyNetworkAnalystmodule
TheArcPySpatialAnalystmodule
TheNetworkAnalystextension
TheESRI’sNetworkAnalystextensionisapowerfultooltoenableroutingandnetwork
connectivityfunctionalitywithinArcGIS.Theextension,whenusedforstreetrouting,
allowsuserstofindthequickestpathbetweentwopointsalongaroadnetwork.Theroute
canbeconstrainedbyanumberoffactors,suchastrafficorleftturns,tobettermodelroad
travel.Similaranalysiscanberunusingothertypesofnetworks,suchaswaterpipe
networksorelectricalnetworks.
UsingNetworkAnalyst
TousetheNetworkAnalystextension,theArcGISforDesktopAdvancedlicenseis
required.InArcCatalogorArcMap,clickontheCustomizemenuandselectExtensions.
OncetheExtensionsmenuisopen,clickonthecheckboxnexttoturnontheNetwork
AnalystExtension.
CreatingaFeatureDataset
Thefirststeptousinganetworkdatasetistocreateonewithinafeaturedataset.Todoso,
wewillgenerateafeaturedatasettoholdthedataofinterest.Right-clickontheFile
geodatabasethathousestheBusStopdataandselectNew,andthenselectFeature
DatasetfromtheNewmenu.NameitChapter11ResultsandclickonNext.
Next,selecttheSpatialReferenceSystem(SRS).Inthiscase,wewillbeusingtheSRS
ofthelocalStatePlanezoneforSanFrancisco.Itisaprojectedcoordinatesystem,so
selectthatfolder,andthenclickontheStatePlanefolder.Onceitisopened,selectthe
foldercalledNAD1983(USFeet).Fromtheavailablereferencesystems,selecttheone
calledNAD1983StatePlaneCaliforniaIIIFIPS0403(USFeet).ClickonNexttogoto
thenextmenu.
Note
Thissystemisalsoknownas2227inWellKnownID(WKID)orEuropeanPetroleum
SurveyGroup(EPSG)systems.Moreinformationaboutthesecodesisavailableat
http://spatialreference.org,awebsiteusedtofindthethousandsofspatialreference
systemsusedthroughouttheworld.
ClickontheVerticalCoordinateSystemsfolderandthenselecttheNorthAmerica
folder.SelecttheNorthAmericanVerticalDatumof1988infeet(NAVD1988US
surveyfeet).Thiswillmakeitpossibletohavetheverticalandhorizontallinearunitsin
thesamemeasurementsystem.ClickonNexttogotothenextmenu.
Thetolerancesonthenextpagearealsoveryimportant,butwewillnotcoverthemin
detailhere.AcceptthedefaultsandclickonFinishtofinalizetheFeatureDataset.
Importingthedatasets
Importthebusstops,streets,andbusroutesfeatureclassesintotheChapter11Results
FeatureDataset.Right-clickonthedatasetandselectImport,andthenFeatureClass
(Single).Addthefeatureclassesonebyonetogivethemanewnamethatwillkeepthem
separatedfromtheversionscontainedwithintheSanFranciscoFeatureDataset.
ImportingthemwillmakesurethattheyareinthecorrectSRSandthatanetworkdataset
canbecreated.
CreatingtheNetworkDataset
Nowthatwehaveadatacontainer,wecancreateanetworkdatasetfromthestreets
featureclass.Right-clickontheChapter11ResultsfeaturedatasetandselectNew,and
thenchooseNetworkDataset.
CalltheNetworkDatasetStreet_NetworkandclickonNext.SelecttheStreetsfeature
classastheclassthatwillparticipateinthenetworkdatasetandclickonNexttomoveto
thenextmenu.SelectGlobalTurnstomodelturnswithinthenetwork.Inthenextmenu,
usethedefaultconnectivitysettings.Then,accepttheUsingZCoordinateValuesfrom
Geometrysetting.Acceptthedefaultcostrestrictionanddrivingdirectionssettings,and
finallyclickonFinishtogeneratethenetworkdataset.Then,buildthenetworkdataset
usingthefinalmenu.Thenetworkdatasetisreadytobeused.
AccessingtheNetworkDatasetusingArcPy
Nowthatthenecessarysetuphasbeencompleted,thestreet_networknetworkdatasetcan
beaddedtoascriptforuseingeneratingroutes.Becausethisisasimpleanalysis,theonly
impedancevaluetobeusedwillbethelengthofthestreetsegments.Throughtheuseofa
SearchCursor,PointGeometryobjectsfromthebusstopscanbeaccessedandaddedas
locationstobesearched:
importarcpy
arcpy.CheckOutExtension("Network")
busStops=r'C:\Projects\PacktDB.gdb\Chapter11Results\BusStops'
networkDataset=r'C:\Projects\PacktDB.gdb\Chapter11Results\street_network'
networkLayer="streetRoute"
impedance="Length"
routeFile="C:\Projects\Layer\{0}.lyr".format(networkLayer)
arcpy.MakeRouteLayer_na(networkDataset,
networkLayer,impedance)
print'layercreated'
sql="NAME='71IB'ANDBUS_SIGNAG='FerryPlaza'"
witharcpy.da.SearchCursor(busStops,['SHAPE@','STOPID'],sql)ascursor:
forrowincursor:
stopShape=row[0]
printrow[1]
arcpy.AddLocations_na(networkLayer,'Stops',stopShape,"","")
arcpy.Solve_na(networkLayer,"SKIP")
arcpy.SaveToLayerFile_management(networkLayer,routeLayerFile,"RELATIVE")
print'finished'
Breakingdownthescript
Let’sdissectthescript,whichoncefinished,willgeneratealayerfilecontainingtheadded
Stops,andtheRoutesalongstreetstobestgetfromtheoriginstoptothedestinationstop.
ThescriptbeginsbyimportingthearcPymodule.Thenextlineallowsustousethe
NetworkAnalystextension:
arcpy.CheckOutExtension("Network")
Usingthearcpy.CheckOutExtension()methodtoinvoketheNetworkAnalystextension
involvespassingthecorrectkeywordtothemethodasaparameter.Onceithasbeen
invoked,thetoolsoftheextensioncanbecalledandexecutedinthescript.
Assigningthebusstopsfeatureclassandthestreet_networknetworkdatasettovariables,
theycanthenbepassedtoArcPy’sMakeRouteLayer_na()method,alongwithavariable
representingtheimpedancevalue:
arcpy.MakeRouteLayer_na(networkDataset,
networkLayer,impedance)
TheMakeRouteLayer_natoolproducesaRouteLayerinmemory.Thisblanklayerneeds
tobepopulatedwithstopstoproducetheroute(s)betweenthem.Forthispurpose,we
needaSearchCursortoaccessthePointGeometryobjectsandaSQLstatementthatwill
limitthereturnedresultstothelineofinterest:
sql="NAME='71IB'ANDBUS_SIGNAG='FerryPlaza'"
witharcpy.da.SearchCursor(busStops,['SHAPE@','STOPID'],sql)ascursor:
forrowincursor:
stopShape=row[0]
printrow[1]
arcpy.AddLocations_na(networkLayer,'Stops',stopShape,"","")
TheSearchCursorwillallowtheStopssublayerofthelayerproducedbythe
MakeRouteLayertooltobepopulatedwhenusedinconjunctionwiththeAddLocations
tool.Oncepopulated,theRouteLayercanbepassedtotheSolvetooltofindtheroutes
betweenthepointsofinterest.Again,theroutesaresolvedbasedonfindingthelowest
impedancebetweenthetwopoints.Inthisexample,theonlyimpedanceisthesegment
length,butitcouldbetrafficorelevationorotherrestrictiontypes,ifthatdataisavailable:
arcpy.Solve_na(networkLayer,"SKIP")
arcpy.SaveToLayerFile_management(networkLayer,routeLayerFile,"RELATIVE")
ThefinalresultisalayerfilethatiswrittentodiskusingtheSaveToLayerFiletool.
TheNetworkAnalystmodule
InanefforttomaketheuseoftheNetworkAnalystextensionmorePythonic,thenewer
NetworkAnalyst(na)moduleadjustshowthemethodsthatcorrespondtotheArcToolbox
NetworkAnalysttoolsareaccessed.InsteadofcallingthetoolsdirectlyfromArcPy,the
toolsarenowmethodsofthenamodule.RemovingtheinitialsoftheNetworkAnalyst
toolsetalsoreducesconfusionandmakesiteasiertorememberthenameofthemethod.
Seethedifferenceasfollows:
importarcpy
arcpy.CheckOutExtension("Network")
busStops=r'C:\Projects\SanFrancisco.gdb\SanFrancisco\Bus_Stops
networkDataset=
r'C:\Projects\SanFrancisco.gdb\Chapter11Results\street_network'
networkLayer="streetRoute"
impedance="Length"
routeLayerFile="C:\Projects\Layer\
{0}_2.lyr".format(networkLayer)arcpy.na.MakeRouteLayer(networkDataset,
networkLayer,impedance)
print'layercreated'
sql="NAME='71IB'ANDBUS_SIGNAG='FerryPlaza'"
witharcpy.da.SearchCursor(busStops,['SHAPE@','STOPID'],sql)ascursor:
forrowincursor:
stopShape=row[0]
printrow[1]
arcpy.na.AddLocations(networkLayer,'Stops',stopShape,"","")
arcpy.na.Solve(networkLayer,"SKIP")
arcpy.management.SaveToLayerFile(networkLayer,routeLayerFile,"RELATIVE")
print'finished'
Thetoolwillproducethesamelayeroutputastheoriginalscript,butthereorganizationof
theNetworkAnalysttoolsintothenamodulehasmadethecodemorelogical.For
instance,itmakesmoresensetocallSolveusingarcpy.na.Solve(),insteadof
arcpy.Solve_na(),asitreinforcesthatSolveisamethodoftheNetworkAnalyst(na)
module.AsArcPycontinuestobedeveloped,IexpectmorePythoniccodereorganization
tooccur.
AccessingtheSpatialAnalystExtension
TheSpatialAnalystExtensionisveryimportanttoperformanalysisonbothrasterand
vectordatasets,butitisgenerallyusedtoperformsurfaceanalysisandrastermath.These
operationsaremadeeveneasierbytheuseofArcPy,asallofthetoolsavailableinthe
SpatialAnalystToolboxareexposedwiththeSpatialAnalystaccessmodule.This
includestheRasterCalculatortools,makingmapalgebraeasybyusingthetoolsand
operatorsinsimpleexpressions.
Addingelevationtothebusstops
Theelevationraster“sf_elevation”hasbeendownloadedfromNOAAandaddedtothe
FileGeodatabase.However,itcoverstheentireBayArea,andweshouldwriteascriptto
onlyextractanareaofthecityofSanFranciscoasitwillreducethetimeneededtorun
ourscripts.We’lluseaSQLstatementasthewhereclausetolimittheresultstotheSouth
ofMarket(SoMa)neighborhood.Todoso,let’stakeadvantageofaSearchCursorandthe
SpatialAnalystaccessmodule’sExtractbyPolygonproperty:
importarcpy
arcpy.CheckOutExtension("Spatial")
busStops=r'C:\Projects\PacktDB.gdb\SanFrancisco\Bus_Stops'
sanFranciscoHoods=
r'C:\Projects\PacktDB.gdb\SanFrancisco\SFFind_Neighborhoods'
sfElevation=r'C:\Projects\PacktDB.gdb\sf_elevation'
somaGeometry=[]
sql="name='SouthofMarket'"
witharcpy.da.SearchCursor(sanFranciscoHoods,['SHAPE@XY'],sql,None,True)
ascursor:
forrowincursor:
X=row[0][0]
Y=row[0][1]
somaGeometry.append(arcpy.Point(X,Y))
somaElev=arcpy.sa.ExtractByPolygon(sfElevation,somaGeometry,"INSIDE")
somaOutPath=sfElevation.replace('sf_elevation','SOMA_elev')
somaElev.save(somaOutPath)
print'extractionfinished'
TheExtractByPolygon()methodisabitmisleading,asitdoesnotacceptaPolygon
objectasaparameter.Instead,itrequiresalistofPointobjectsthatrepresentthevertices
oftheareathatwewanttoextract.AstheSearchCursorisiteratingthroughthe
neighborhoodsdataset,aPolygonobjectisreturnedbythecursor.Fortunately,the
SearchCursorhasafinalparameter,whichwehavenotyetexplored,thatallowsusto
extracttheindividualpointsorverticesthatmakeuptheSomaneighborhoodpolygon.By
settingtheSearchCursor’soptionalExplodetoPointsparameter(whichconvertsPolygon
objectsintocoordinatepairsforeachvertex)toTrue,Pointobjectscanbegeneratedby
passingtheXYvaluesofeachreturnedvertextothearcpy.Pointmethod.ThesePoint()
objectsareappendedtothesomaGeometrylistandthenpassedtotheSpatialAnalyst
accessmodule’sExtractByPolygonmethod.
Note
PassingaPolygonObjectinsteadofPointObjectswillreturnanerror.
UsingMapAlgebratogenerateelevationinfeet
Wenowhavearastertousetoextractelevationvalues.However,boththeoriginalraster
andthegeneratedSoManeighborhoodrastercontainelevationvaluesinmeters,andit
wouldbebettertoconvertthemtofeettokeepthemconsistentwiththeprojectionofthe
busstops.Let’suserastermathandtheTimes()methodtoconvertthevaluesfrommeters
tofeet:
somaOutPath=sfElevation.replace('sf_elevation','SOMA_elev')
outTimes=arcpy.sa.Times(somaOutPath,3.28084)
somaFeetOutPath=sfElevation.replace('sf_elevation','SOMA_feet')
outTimes.save(somaFeetOutPath)
TheTimes()methodgeneratesanewrastertogleantheelevationvaluesweneedforthe
busstopsofinterest.
Addinginthebusstopsandgettingelevation
values
Nowthatwehavegeneratedarasterthatwecanusetofindelevationvaluesinfeet,we
needtoaddanewarcpy.sa()methodtogeneratethepoints.The
ExtractValuesToPoints()methodwillgenerateanewbusstopsfeatureclasswithanew
fieldthatholdstheelevationvalues:
witharcpy.da.SearchCursor(sanFranciscoHoods,['SHAPE@'],sql)ascursor:
forrowincursor:
somaPoly=row[0]
arcpy.MakeFeatureLayer_management(busStops,'soma_stops')
arcpy.SelectLayerByLocation_management("soma_stops","INTERSECT",somaPoly)
outStops=r'C:\Projects\PacktDB.gdb\Chapter11Results\SoMaStops'
arcpy.sa.ExtractValuesToPoints("soma_stops",
somaOutFeet,outStops,"INTERPOLATE","VALUE_ONLY")
print'pointsgenerated'
Thefinalresult
Weproducedasubsetfeatureclassofthebusstopsthathastheelevationvaluesaddedas
afield.Thisprocesscouldberepeatedfortheentirecity,oneneighborhoodatatime,orit
couldbeperformedwiththeoriginalelevationrasterontheentirebusstopsfeatureclass
togenerateavalueforeachstop:
importarcpy
arcpy.CheckOutExtension("Spatial")
arcpy.env.overwriteOutput=True
busStops=r'C:\Projects\PacktDB.gdb\SanFrancisco\Bus_Stops'
sanFranciscoHoods=
r'C:\Projects\SanFrancisco.gdb\SanFrancisco\SFFind_Neighborhoods'
sfElevation=r'C:\Projects\SanFrancisco.gdb\sf_elevation'
somaGeometry=[]
sql="name='SouthofMarket'"
witharcpy.da.SearchCursor(sanFranciscoHoods,['SHAPE@XY'],sql,None,True)
ascursor:
forrowincursor:
somaGeometry.append(arcpy.Point(row[0][0],row[0][1]))
somaElev=arcpy.sa.ExtractByPolygon(sfElevation,somaGeometry,"INSIDE")
somaOutput=sfElevation.replace('sf_elevation','SOMA_elev')
somaElev.save(somaOutput)
print'extractionfinished'
somaOutput=sfElevation.replace('sf_elevation','SOMA_elev')
outTimes=arcpy.sa.Times(somaOutput,3.28084)
somaOutFeet=sfElevation.replace('sf_elevation','SOMA_feet')
outTimes.save(somaOutFeet)
print'conversioncomplete'
witharcpy.da.SearchCursor(sanFranciscoHoods,['SHAPE@'],sql)ascursor:
forrowincursor:
somaPoly=row[0]
arcpy.MakeFeatureLayer_management(busStops,'soma_stops')
arcpy.SelectLayerByLocation_management("soma_stops","INTERSECT",somaPoly)
outStops=r'C:\Projects\SanFrancisco.gdb\Chapter11Results\SoMaStops'
arcpy.sa.ExtractValuesToPoints("soma_stops",
somaOutFeet,outStops,"INTERPOLATE","VALUE_ONLY")
print'pointsgenerated'
ThisscriptdemonstrateswellthevalueofaccessingtheadvancedextensionsinArcPyand
combiningthemwithSearchCursorsandGeometryobjects.Thescriptcouldbetaken
evenfurtherbyaddingaSearchCursortolookthroughtheoutstopsdatasetand
exportingtheresultstoaspreadsheet,orevenaddinganewfieldtotheoriginalbusstops
datasettopopulatewiththeelevationvalues.Itcouldevenbeusedasimpedancevaluesto
beenteredintoaNetworkAnalystextensionanalysis—afuncodingtaskthatIhopeyou
willattempt.
Summary
Inthischapter,wecoveredthebasicsofusingcommonArcGISforDesktopAdvanced
extensionswithinArcPy,withafocusontheNetworkAnalystaccessmoduleandthe
SpatialAnalystaccessmodule.Weexploredhowtogenerateanetworkandhowtocreate
networkpathsusingArcPy.WealsoexploredhowtoaccessSpatialAnalysttoolsanduse
theminconjunctionwithSearchCursorstoworkwithrastersandvectorsforspatial
analysis.
Inthenextchapter,wewillexploresomefinalpiecestotheArcPypuzzlethatwillallow
thecreationofadvancedscriptsandscripttools.
Chapter12.TheEndoftheBeginning
Thisbookisalmostdone,butthereissomuchmoretoknowaboutwritingcodeinPython
andArcPy.Unfortunately,Ican’tfititallintoonebook,butthatalsomeansthatyouget
tohavefunexploringallofthemethodsandpropertiesofArcPy.Asaconclusiontothe
book,wewillcoversomeotherimportanttopicsthatcancropupwhenwritingArcPy
scripts.Combinedwiththelessonsfromearlierchapters,Ihopeyou’llsoonbeusing
ArcPyatwork,atschool,orjustforfun(whynot?).
Thischapterwillcoverthefollowingtopics:
Workingwithfieldinformation–types,aliases,domains,spatialtypes,andmore
AccessinginformationdescribingaFeatureClass
AutomaticallygeneratingaFeatureClassandpopulatingitwithfields
AutomaticallycreatingFileGeodatabasesandFeatureDatasets
CreatingaScripttoolthatwillrunthebusstopanalysisandgenerateresultsinan
automaticallygeneratedFileGeodatabase,FeatureDataset,andFeatureClass
Gettingfieldinformationfromfeature
classes
Whencreatingscripttools,orjustrunningascript,therecanbetimesthatextractingfield
informationfromafeatureclass(orshapefile)isnecessary.Thisinformationcaninclude
fieldnamesandaliases,fieldtypeandlength,scale,domains,orsubtypes.Theseareall
propertiesavailablethroughthearcpy.ListFieldsmethod.We’llexplorethemany
properties,howtoextractthem,andhowtousetheminascript.
ByorganizingtheArcPymethodsintoafunction,thedataisorganizedinaformthatwe
prefer,insteadofrelyingonthedefaultorganizationusedbythedesignersofArcPy.It’s
importanttorememberthatscriptsyoucreateshouldreflectyourneeds,andcreatingthese
functionwrappersisonestepforwardtowardspolishingtherawArcPytoolstoworkin
yourworkflows.
AccessingtheListFields’properties
TheListFieldstoolisavailableasanArcPymethod.Arcpy.ListFieldsacceptsonlyone
parameter,afeatureclass,orshapefile.Oncetheparameterhasbeenpassed,aseriesof
importantpropertiesareavailableusingdotnotation.Totakefurtheradvantageofthese
properties,wewillcreatefunctionsthatmakeiteasytogettheinformationwewant,in
theformatwerequire.
Listcomprehensions
Withinthesefieldinformationfunctions,wewilltakeadvantageofaPythondatastructure
knownaslistcomprehensions.Theysimplifytheforloopstructuretomakeiteasierto
populatealistwiththevaluesrequired(thefieldinformationinthiscase).
Tocreatealistcomprehension,aforloopisgeneratedinsideasetofbrackets,andthelist
ispopulatedwiththegeneratedvalues.Hereisanexampleofalistcomprehensionthat
createsalistwiththesquarevaluesofthenumbersfrom1to10,asruninthePython
interpreter:
>>>originalList=range(1,11)
>>>printoriginalList
[1,2,3,4,5,6,7,8,9,10]
>>>newList=[x**2forxinoriginalList]
>>>printnewList
[1,4,9,16,25,36,49,64,81,100]
Listcomprehensionsareusedbecausetheyarefasterandeasiertowrite,thoughitmay
takesometimetogetusedtothesyntax.Experimentwiththemtobetterunderstandtheir
useandlimitations,andalsoconsultsomeofthemanyresourcesavailableonline.
Creatingthefieldinformationfunctions
Eachofthefunctionswillbeaseparateentity,buttheywillallhaveasimilarstructure.
Oneparameterwillbeacceptedbyeachfunction,thefeatureclassofinterest.ArcPywill
beimported,andlaterdeletedfrommemory,tomakesurethattheListFields()method
canbecalledwithoutanerror.OncethefeatureclassispassedtotheListFields()
method,thevaluesdesiredwillpopulatealistinsidealistcomprehension.Onceithas
beenpopulated,itisreturnedfromthefunctionusingthereturnkeyword.
Hereisthesetoffunctionsforthefieldnames:
defreturnfieldnames(fc):
importarcpy
fieldnames=[f.nameforfinarcpy.ListFields(fc)]
delarcpy
returnfieldnames
defreturnfieldalias(fc):
importarcpy
fieldalias=[f.aliasNameforfinarcpy.ListFields(fc)]
delarcpy
returnfieldalias
defreturnfieldbasename(fc):
importarcpy
fieldtypes=[f.baseNameforfinarcpy.ListFields(fc)]
delarcpy
returnfieldtypes
Thesenamefunctionsareusefulwhencreatinganewfeatureclassbasedonanother
featureclass.Sometimesthereisaneedtopreservetheexactnamesandaliasesfromthe
originalfeatureclass,andusingthesefunctionswillmakethispossible.Whendoingthis,
thereisaneedtoprovideotherfieldinformationaswell.Herearethefunctionsrelatedto
fieldtypes,lengths,precision,andscale:
defreturnfieldtypes(fc):
importarcpy
fieldtypes=[f.typeforfinarcpy.ListFields(fc)]
delarcpy
returnfieldtypes
defreturnfieldlength(fc):
importarcpy
fieldlengths=[f.lengthforfinarcpy.ListFields(fc)]
delarcpy
returnfieldlengths
defreturnfieldprecision(fc):
importarcpy
fieldprecise=[f.precisionforfinarcpy.ListFields(fc)]
delarcpy
returnfieldprecise
defreturnfieldscale(fc):
importarcpy
fieldscales=[f.scaleforfinarcpy.ListFields(fc)]
delarcpy
returnfieldscales
Thereisevenapropertyusedtorequestdomaininformation:
defreturnfielddomain(fc):
importarcpy
fielddomains=[f.domainforfinarcpy.ListFields(fc)]
delarcpy
returnfielddomains
Thesefunctionsallsharethestructurediscussedearlier,andhavetheadvantageofbeing
simpletouseandeasytosearchthroughout.Becausefieldsinafeatureclasshavea
specificorder,eachlistreturnedbythefunctionswillhaveanordertotheinformation
returned,accessiblebyaspecificindexnumber.
Thefieldsubtypesarealsoavailablethroughthedataaccessmodule.Becausetheyare
relatedtothefields,theyarereturnedasadictionary:
defreturnfieldsubtypes(fc):
importarcpy
fieldsubdic={}
subtypes=arcpy.da.ListSubtypes(fc)
forstcode,stdictinsubtypes.iteritems():
forstkeyinstdict.iterkeys():
ifstkey=='FieldValues':
fields=stdict[stkey]
forfield,fieldvalsinfields.iteritems():
sub=fieldvals[0]
desc=fieldvals[1]
fieldsubdic[field]=sub,desc
delarcpy
returnfieldsubdic
Note
Addingthesefunctionstotheuseful.pyscriptinthecommonmodulewillmakethem
availabletoanyscriptorscripttool.Usetheimportkeywordtoaddthemtoanynew
script.Theyareself-containedfunctionsthatonlyrequirethefilepathtothefeatureclass
ofinterest.
Queryingfeatureclassinformation
Someimportantpiecesofinformationaboutanincomingfeatureclasscannotbeaccessed
usingtheListFields()method.Instead,anumberofdifferentmethodswillbeusedto
findtheGeometrytype,orSpatialReference,orthefieldsubtypeofeachfeatureclass.
SomeofthesearediscoveredusingArcPy’sDescribemethod,builttoprovide
FortheGeometrytype,wewillusetheshapeTypepropertyoftheDescribe()method:
defreturngeometrytype(fc):
importarcpy
arcInfo=arcpy.Describe(fc)
geomtype=arcInfo.shapeType
delarcpy
returnstr(geomtype)
ThenameoftheShapefield(whichusuallydefaultstoShape)canalsoberequestedusing
theDescribemethodandreturnsastringdatatype:
defreturngeometryname(fc):
importarcpy
arcInfo=arcpy.Describe(fc)
geomname=arcInfo.shapeFieldName
delarcpy
returnstr(geomname)
Thefeatureclassspatial_referenceisalsoavailablethroughtheDescribemethod.The
dataisreturnedasaspatial_referenceobject:
defreturnspatialreference(fc):
importarcpy
spatial_reference=arcpy.Describe(fc).spatialReference
delarcpy
returnspatial_reference
Aspatial_referenceobjecthasanumberofimportantproperties.Theprojectionname
andprojectioncodeareamongtheimportant
defreturnprojectioncode(fc):
importarcpy
spatial_reference=arcpy.Describe(fc).spatialReference
proj_code=spatial_reference.projectionCode
delarcpy
returnproj_code
defreturnprojectionname(fc):
importarcpy
spatial_reference=arcpy.Describe(fc).spatialReference
proj_name=spatial_reference.name
delarcpy
returnproj_name
Manyotherpropertiesandmethodscanbesimilarlyutilizedtomakethemavailable
withinscriptsorscripttools.ExploretheArcGIShelpdocumentsforfurtherinsightsinto
thepropertiesavailablethroughtheDescribemethod.
GeneratingFileGeodatabasesandfeatureclasses
FileGeodatabasesdonothavetoexistbeforeascriptisrun;instead,theycanbe
generatedwhenascriptisexecutedusingtheCreateFileGDBtool,whichisalsoanArcPy
method.OncetheFileGeodatabasehasbeencreated,FeatureDatasetscanbeadded.
GeneratingtheFileGeodatabaseisveryeasy.Theonlyparametersarethefolderstoplace
itinside,andthenameoftheGeodatabase:
importarcpy
folderPath=r"C:\Projects"
gdbName="ArcPy.gdb"
arcpy.CreateFileGDB_management(folderPath,gdbName)
TheFeatureDatasetsaremoredifficulttocreate,asthereisanoptionalspatialreference
parameterthatrequiresaSpatialReferenceobjecttobegenerated.WhiletheSpatial
Referenceobjectisoptional,itishighlyrecommended.
ThereareafewoptionstogeneratetheSpatialReferenceobject.Oneofthemusesthe
returnspecialReference()functiondefinedearlier;bypassingafeatureclasstothe
function,aSpatialReferenceobjectiscreated.Anothermethodwouldbetopassafile
pathtoaprojectionfile.prjastheoptionalthirdparameter.Athirdmethodistogenerate
aSpatialReferenceobjectbyusingthearcpy.SpatialReferencemethodandpassingita
projectioncodeoraprojectionstring:
spatialReference=arcpy.SpatialReference(2227)
Howeveritisgenerated,itisthenpassedtothearcpy.CreateFeatureDatasetmethod
alongwiththefilepathoftheFileGeodatabaseandthenameoftheFeatureDataset:
spatialReference=arcpy.SpatialReference(2227)
fileGDB=r"{0}\{1}".format(folderPath,gdbName)
featureDataset="Chapter12Results"
arcpy.CreateFeatureDataset_management(fileGDB,featureDataset,
spatialReference)
Generatingafeatureclass
NowthataFileGeodatabaseandaFeatureDatasethavebeencreated,let’sgeneratea
FeatureClassinsidetheFeatureDataset.Thisisdoneusingthe
arcpy.CreateFeatureClassmethod.Thismethodhasanumberofoptionalparameters,
includingaFeatureClasstouseasatemplateandaSpatialReference.Forthisexample,
thereisnoneedtousetheSpatialReferenceparameterasitisbeingwrittentoaFeature
Dataset,whichdictatestheSpatialReferenceused.Thetemplateparameterwillcopythe
fieldsofthetemplateFeatureClass,butfornow,wewillonlycreatetheShapefield:
featureClass="BufferArea"
geometryType="POLYGON"
featurePath=r"{0}\{1}".format(fileGDB,featureDataset)
arcpy.CreateFeatureclass_management(featurePath,featureClass,
geometryType)
ThecreatedFeatureClasswillneedsomefieldswiththeattributeinformationthatwillbe
populatedlater.Thefieldshaveanumberofparametersthatdependonthefieldtype,
includinglength,precision,andalias,amongothers:
fieldName="STOPID"
fieldAlias="BusStopIdentifier"
fieldType="LONG"
fieldPrecision=9
featureClassPath=r"{0}\{1}".format(featurePath,featureClass)
arcpy.AddField_management(featureClassPath,fieldName,fieldType,
fieldPrecision,
"","",fieldAlias)
Let’saddasecondfieldtoholdtheaveragedpopulationvaluesproducedbytheBusStop
analysis:
fieldName2="AVEPOP"
fieldAlias2="AverageCensusPopulation"
fieldType2="FLOAT"
featureClassPath=r"{0}\{1}".format(featurePath,featureClass)
arcpy.AddField_management(featureClassPath,fieldName2,fieldType2,"","",
"",fieldAlias2)
TheFileGeodatabase,FeatureDataset,andFeatureClassfieldshavenowbeengenerated.
Let’sextendthescriptintoascripttoolbyaddingtheBusStopanalysisfunctions,while
writingtheresultstothegeneratedFeatureClass.Creating,ascripttoolthatpopulatesa
featureclass.
ThisscripttoolwillborrowfromtheideasoutlinedinChapter10,AdvancedGeometry
ObjectMethodsandwillcreateaunionofthePolygonGeometryobjectsthatintersect
withthebufferedbusstopstopopulatetheShapefield,alongwiththebusstopIDand
theaveragedpopulationfortheblocksintersectedwitheachbuffer.
OpenthescriptChapter12_3.pyandexploreitscontents.Coupledwiththecodesnippets
mentionedearlierandtheuseofarcpy.GetParameterAsTexttogetdatafromthescript
tool,thedatageneratedwillbewritteninafeatureclassbythefollowingcode:
arcpy.AddMessage("BeginningAnalysis")
insertCursor=arcpy.da.InsertCursor(featureClassPath,['SHAPE@',fieldName,
fieldName2])
arcpy.MakeFeatureLayer_management(censusBlocks2010,"census_lyr")
witharcpy.da.SearchCursor(busStops,['SHAPE@',busStopField],sql)as
cursor:
forrowincursor:
stop=row[0]
stopID=row[1]
busBuffer=stop.buffer(400)
arcpy.SelectLayerByLocation_management("census_lyr","intersect",busBuffer,'
','NEW_SELECTION')
censusShapes=[]
censusPopList=[]
witharcpy.da.SearchCursor("census_lyr",
['SHAPE@',censusBlockPopField])asncursor:
fornrowinncursor:
censusShapes.append(nrow[0])
censusPopList.append(nrow[1])
censusUnion=censusShapes[0]
forblockincensusShapes[1:]:
censusUnion=censusUnion.union(block)
censusPop=sum(censusPopList)/len(censusPopList)
finalData=(censusUnion,stopID,censusPopulation)
insertCursor.insertRow(finalData)
arcpy.AddMessage("AnalysisComplete")
Thescriptcombinesmanyoftheideasthathavebeenintroducedthroughoutthebookto
allowtheusertorunacompleteworkflowthatgeneratesafeatureclasscontainingthe
resultsoftheanalysis.Byaddingonlythefieldsofinterestandpopulatingthemwiththe
unionedPolygonobjects,thescripteliminatesmostofthecruft,normallycreatedwhen
runningaspatialanalysis,andproducesaresultsdatasetthatcanbeviewedinArcMap.
Settingupthescripttoolparameters
Hereishowtheparametersofthescripttoollookwhensetup:
Thelistofparametersislong,soIamusingtwoimagestoportraythem.Itisimportantto
choosethecorrectdatatypeforeachparameterasitwillcontrolthedialoggeneratedto
retrievethedata.
ThebusstopIDfieldandthePopulationfieldarebothobtainedfromtheirrespective
featureclasses.TheFileGeodatabasenameisastringandthecodewillappend.gdbto
theendoftheinputstringifitisnotenteredinitially,tomakesurethatitcanbecorrectly
generated.Itshouldnotalreadyexist;itwillnotbegeneratedifitdoes(ifdesired,thiscan
bechangedbysettingthearcpy.env.overwriteOutputpropertytoTrueaftertheimport
statement).
Oncetheparametershavebeenset,andthetoolhasanameanddescription,saveitand
thenopenthetool.Itshouldlooklikethisonceithasbeenfilledout:
ClickonOKtorunthetool.OpenArcMapandaddtheresults,alongwiththeSan
FranciscopolygonandtheInbound71featureclassfromChapter4,ComplexArcPy
ScriptsandGeneralizingFunctions.Theresultswilllooksimilartothis,afterabitof
cartographicsymbolizing:
Thefinalresultwillhaveonerowperbusstopselected,alongwiththeaveraged
populationandthebusstopIDvalue.Insteadofusingaspreadsheetasanoutput,the
featureclasswillallowtomakemapsorproducefurtherspatialanalysis.Producing
customdatausingcustomscripttoolsputsyouinthedriver’sseatwhenperforming
geospatialanalysesandmakesyourtools,andyou,avaluableassettoanyteam.
Environmentalsettings
TheArcPymoduleallowsforthecontrolofglobalsettingsthatcontrolsinputandoutput
processesusingArcPy’senvclass.Thesesettingswillhaveaneffectontheaccuracyof
dataproducedusinggeospatialanalysistools.ResolutionandtolerancesettingsforX,Y,
Z,andMcoordinatescanbecontrolled,alongwithoutputextent,rastercellsize,analysis
workspace,andmanyothersettings.
ToaccesstheenvironmentalsettingsusingArcPy,theclassenvisimportedfromarcpy:
>>>fromarcpyimportenv
Itcanalsobecalledusingdotnotationshownasfollows.Settingtheworkspaceremoves
theneedtopassafilepathtoanysubsequentmethodscalledontheworkspace.Hereisan
exampleofsettingtheworkspaceandcallingtheListDatasets()methodwithoutpassing
afilepathasaparameter:
>>>importarcpy
>>>arcpy.env.workspace=r"C:\Projects\SanFrancisco.gdb"
>>>arcpy.ListDatasets()
[u'SanFrancisco',u'Chapter3Results',u'Chapter4Results',
u'Chapter5Results',u'Chapter7Results',u'Chapter11Results']
Resolutionandtolerancesettings
Theresolutionandtolerancesettingscontroltheaccuracyoftheoutputofanydata
producedbyatoolinArcToolboxorwhenrunningascriptusingArcPy.Thesecan(and
should)besetforFeatureDatasetsinFileGeodatabasesorEnterpriseGeodatabases,butit
isimportanttosetthemforanalysisruninthememoryorwhenusingshapefiles,orifthe
geospatialanalysisrequiresgreateraccuracythanusedbythoseGeodatabases.
Settingtheresolutionsandtolerancesrequireanunderstandingoftheaccuracyrequired
foryourprojects.Thesesettingswilllimittheabilitytosnaptoalineorfindpointsthat
intersectwithaline.Thelinearunitwillneedtoreflectthecoordinatesystemofchoice:
importarcpy
arcpy.env.MResolution=0.0005
arcpy.env.MTolerance=0.005
arcpy.env.ZResolution="0.0025Feet"
arcpy.env.ZTolerance="0.001Feet"
arcpy.env.XYResolution="0.00025Feet"
arcpy.env.XYTolerance="0.0005Feet"
Otherimportantenvironmentalsettingsinclude:
TheExtentsetting,whichlimitstheextentofanydataproducedfromananalysisby
settingarectangleofinterestusinganExtentobject,orastringwithspacedelimited
coordinates(Xmin,Ymin,Xmax,Ymax)inthecurrentcoordinatesystem.
TheMasksetting,whichlimitsrasteranalysistoareasthatintersectwithafeature
classorarasterpassedasastringfilepathparametertothesetting.
TheCellSizesetting,whichcontrolsthecellsizeofthedataproducedusingraster
analysis.
TaketimeandexplorethepowerfulArcPyEnvironmentalSettingstoreducethetime
neededtowritecodeandensurehigh-qualitydataproduction.
Summary
ThischapterandthisbookhavedemonstratedsomeofthemanywaysthatArcPycanbe
usedtoautomategeospatialanalysis.Byapplyingthelessons,andbybeingcreativewith
themanymethodsandpropertiesofArcPy,repetitiveandslowgeospatialprocessescan
bescriptedandmadeintocustomtoolsthatwillsavealotoftime.
IhopethatyouenjoyedlearningthebasicsofscriptingwithArcPyandPython.Ireally
hopethatyou’veevencometoliketheideaofprogramming,asitispowerfuland
empowering.Thereismuchmoretomaster,butIthinkyouwillfindthatthemore
scriptingyoudo,theeasieritistounderstand.
ThebestresourceforfurtherunderstandingofArcPyistheArcGISHelpDocuments,
availablethroughtheHelpmenuinArcCatalogorArcMap.Thedocumentationisalso
availableathttp://resources.arcgis.com/en/help/main/10.2/index.html.Workingon
enteringthecorrectquestionintoGooglecanbeveryhelpfulaswell.Programming
forumssuchasStackExchange(http://gis.stackexchange.com/)orESRI’sGeoNet
(https://geonet.esri.com/welcome)arevaluableresourcestoaskallkindsofprogramming
questions.Thereisananswerforalmosteveryquestionyoumayhave(butneverbeafraid
toaskquestionsyourself!).
Havefuncreatingsolutionsandtools,andgoodluckinallyourfuturegeospatial
programmingchallenges!
Index
A
adjustedmap
exporting,toPDF/ExportingtheadjustedmaptoPDF
analysiscomponents
adding/Addingadvancedanalysiscomponents
Polygonobjectmethods/AdvancedPolygonobjectmethods
randompoints,generatingtorepresentpopulation/Generatingrandompointsto
representpopulation
functionsused,withinscript/Usingthefunctionswithinascript
XLScreating,XLWTused/CreatinganXLSusingXLWT
analysisresults
tallying/Tallyingtheanalysisresults
ApplicationProgrammingInterface(API)/Wrappermodules
AptanaStudio3/AptanaStudio3
URL/AptanaStudio3
ARCMacroLanguage(AML)
about/ModelBuilder
ArcPy
about/OverviewofPython
used,withmapdocuments/UsingArcPywithmapdocuments
used,foraccessingnetworkdataset/AccessingtheNetworkDatasetusing
ArcPy
arcpy.AddMessage
used,fordisplayingscriptmessages/Displayingscriptmessagesusing
arcpy.AddMessage
arcpy.mapping
used,forcontrollingLayerobjects/Usingarcpy.mappingtocontrolLayer
objects
arcpy.Pointfunction/UsinganInsertCursor
arcpy.SpatialReference()method/Thedataaccessmodule
ArcPygeometryobjectclasses
about/ArcPygeometryobjectclasses,ArcPyPointobjects,ArcPyArray
objects,ArcPyPolylineobjects
Polygonobjects/ArcPyPolygonobjects,Polygonobjectbuffers,Other
Polygonobjectmethods
geometryobjects/ArcPygeometryobjects
PointGeometryobjects/ArcPyPointGeometryobjects
ArcPymodule
about/OverviewofPython,TheArcPymodule
attributefieldinteractions/Attributefieldinteractions
automatedmapdocumentadjustment
about/Automatedmapdocumentadjustment
variables/Thevariables
mapdocumentobject/Themapdocumentobjectandthetextelements
textelements/Themapdocumentobjectandthetextelements
layervisibility,adjusting/Adjustinglayervisibility
buffer,generatingfrombusstopsfeatureclass/Generatingabufferfromthebus
stopsfeatureclass
busstopbufferandcensusblocks,intersecting/Intersectingthebusstopbuffer
andcensusblocks
textelements,updating/Updatingthetextelements
B
brokenlinks
fixing/Fixingthebrokenlinks
buffer
generating,frombusstopsfeatureclass/Generatingabufferfromthebusstops
featureclass
Buffertool
modeling/ModelingtheSelectandBuffertools
built-infunctions
str/Commonlyusedbuilt-infunctions
int/Commonlyusedbuilt-infunctions
float/Commonlyusedbuilt-infunctions
built-inmodules
URL/Commonlyusedstandardlibrarymodules
busstopbufferblock
andcensusblock,intersecting/Intersectingthebusstopbufferandcensus
blocks
busstopclass
andbufferfeatureclass,populating/Populatingtheselectedbusstopandbuffer
featureclasses
BusStopfeatureclass
adding,asparameter/AddingtheBusStopfeatureclassasaparameter
busstopfields
adding,asparameter/Addingthebusstopfieldsasaparameter
C
censusblock
andbusstopbufferblock,intersecting/Intersectingthebusstopbufferand
censusblocks
CensusBlockfeatureclass
adding,asparameter/AddingtheCensusBlockfeatureclassasaparameter
CensusBlockfield
adding,asparameter/AddingtheCensusBlockfieldasaparameter
CommaSeparatedValue(CSV)/Addingtheoutputspreadsheetasaparameter
comments
about/Comments
CSVmodule
adding,toscript/AddingtheCSVmoduletothescript
cursor
used,foraccessingdata/Accessingthedata:Usingacursor
D
data
accessing,cursorused/Accessingthedata:Usingacursor
dataaccessmodule
about/Thedataaccessmodule
attributefieldinteractions/Attributefieldinteractions
updatecursor/Updatecursors
shapefield,updating/Updatingtheshapefield
pointlocation,adjusting/Adjustingapointlocation
row,deletingwithupdatecursor/DeletingarowusinganUpdateCursor
insertcursor,using/UsinganInsertCursor
dataframewindowextent
controlling/Controllingthedataframewindowextentandscale
datasets
importing/Importingthedatasets
datasources
replacing/Replacingthedatasources
datatypes
about/Datatypes
strings/Strings
integers/Integers
floats/Floats
lists/Lists
tuples/Tuples
dictionaries/Dictionaries
iterabledatatypes/Iterabledatatypes
adding/Addingdatatypes
definitionquery
about/Definitionqueries
defkeyword
about/Functions
deleteRowmethod/DeletingarowusinganUpdateCursor
dictionaries/Dictionaries
dynamiccomponents
adding,toscript/Addingdynamiccomponentstothescript
dynamicparameters
adding,toscript/Addingdynamicparameterstoascript
E
environmentalsettings
about/Environmentalsettings
resolutionsetting/Resolutionandtolerancesettings
tolerancesetting/Resolutionandtolerancesettings
Extentsetting/Resolutionandtolerancesettings
Masksetting/Resolutionandtolerancesettings
CellSizesetting/Resolutionandtolerancesettings
F
featureclasses
fieldinformation,obtainingfrom/Gettingfieldinformationfromfeatureclasses
ListFieldstool/AccessingtheListFields’properties
Listcomprehensions/Listcomprehensions
fieldinformationfunctions,creating/Creatingthefieldinformationfunctions
featureclassinformation,querying/Queryingfeatureclassinformation
FileGeodatabases,generating/GeneratingFileGeodatabasesandfeature
classes
generating/GeneratingFileGeodatabasesandfeatureclasses,Generatinga
featureclass
scripttoolparameters,settingup/Settingupthescripttoolparameters
environmentalsettings/Environmentalsettings
featureclassinformation
querying/Queryingfeatureclassinformation
FeatureDataset
creating/CreatingaFeatureDataset
fieldinformation
obtaining,fromfeatureclasses/Gettingfieldinformationfromfeatureclasses
fieldinformationfunctions
creating/Creatingthefieldinformationfunctions
filepaths,inPython
about/FilepathsinPython
finalscript
about/Thefinalscript
inspecting/Inspectingthefinalscript,RunningtheScriptTool
floats/Floats
forloops/Forloops
functions
about/Functions
used,withinscript/Usingthefunctionswithinascript
G
geometryobjects/ArcPygeometryobjects
GIS
about/Datatypes
H
hard-codedinputs
about/Addingdynamicparameterstoascript
I
IDEs
about/IntegratedDevelopmentEnvironments(IDEs),IDEsummary
IDLE/IDLE
PythonWin/PythonWin
AptanaStudio3/AptanaStudio3
automaticallygeneratedscript/Theautomaticallygeneratedscript
IDLE
about/IDLE
If/Elif/Elsestatements
about/If/Elif/Elsestatements
importstatements/Importstatements
indentation
about/Indentation
individuallayers
fixing/Fixingthelinksofindividuallayers,ExportingtoPDFfromanMXD
insertcursor
using/UsinganInsertCursor
integers/Integers
Intersecttool
adding/AddingtheIntersecttool
iterabledatatypes/Iterabledatatypes
K
keywordmethod/Adjustingapointlocation
keywords
about/Keywords
L
Layerobject
adding/AddingaLayerobject
Layerobjects
controlling,arcpy.mappingused/Usingarcpy.mappingtocontrolLayerobjects
methods/Layerobjectmethodsandproperties
properties/Layerobjectmethodsandproperties
layerobjects
about/Thelayerobjects
Layers
about/Usingarcpy.mappingtocontrolLayerobjects
layersources
inspecting/Inspectingandreplacinglayersources
replacing/Inspectingandreplacinglayersources
brokenlinks,fixing/Fixingthebrokenlinks
individuallayers,fixing/Fixingthelinksofindividuallayers,ExportingtoPDF
fromanMXD
mapdocumentelements,adjusting/Adjustingmapdocumentelements
layervisibility
adjusting/Adjustinglayervisibility
Listcomprehensions/Listcomprehensions
ListFieldstool/AccessingtheListFields’properties
lists/Lists
M
mapdocumentelements
adjusting/Adjustingmapdocumentelements
mapdocuments
ArcPy,usingwith/UsingArcPywithmapdocuments
maps
exporting/Exportingthemaps
model
creating/CreatingamodelandexportingtoPython
exporting,toPython/CreatingamodelandexportingtoPython
Selecttool,modeling/ModelingtheSelectandBuffertools
Buffertool,modeling/ModelingtheSelectandBuffertools
Intersecttool,adding/AddingtheIntersecttool
analysisresults,tallying/Tallyingtheanalysisresults
exporting/Exportingthemodelandadjustingthescript
ModelBuilder
about/ModelBuilder
module
adding,sysmoduleused/UsingPython’ssysmoduletoaddamodule
modules
residing/Wheremodulesreside
N
namespaces
about/Namespaces
namingvariables
using,bestpractices/Variables
NetworkAnalyst
using/UsingNetworkAnalyst
FeatureDataset,creating/CreatingaFeatureDataset
datasets,importing/Importingthedatasets
networkdataset,creating/CreatingtheNetworkDataset
networkdatasetaccess,ArcPyused/AccessingtheNetworkDatasetusing
ArcPy
NetworkAnalystextension
about/TheNetworkAnalystextension
NetworkAnalystmodule
about/TheNetworkAnalystmodule
networkdataset
creating/CreatingtheNetworkDataset
O
OperatingSystem(OS)module
about/TheOperatingSystem(OS)module
outputspreadsheet
adding,asparameter/Addingtheoutputspreadsheetasaparameter
P
parameter
CensusBlockfeatureclass,addingas/AddingtheCensusBlockfeatureclass
asaparameter
CensusBlockfield,addingas/AddingtheCensusBlockfieldasaparameter
outputspreadsheet,addingas/Addingtheoutputspreadsheetasaparameter
spreadsheetfieldnames,addingas/Addingthespreadsheetfieldnamesasa
parameter
SQLStatement,addingas/AddingtheSQLStatementasaparameter
busstopfields,addingas/Addingthebusstopfieldsasaparameter
parameters
datatypes,adding/Addingdatatypes
BusStopfeatureclass,addingas/AddingtheBusStopfeatureclassasa
parameter
PDF
adjustedmap,exportingto/ExportingtheadjustedmaptoPDF
PointGeometryobjects/ArcPyPointGeometryobjects
pointlocation
adjusting/Adjustingapointlocation
polygongeometry
inserting/Insertingapolygongeometry
Polygonobjectmethods/AdvancedPolygonobjectmethods
Polygonobjects/ArcPyPolygonobjects,Polygonobjectbuffers,OtherPolygon
objectmethods
polyLinegeometry
inserting/Insertingapolylinegeometry
programmingjargon
about/Forloops
Python
about/OverviewofPython
Python,asprogramminglanguage
about/Pythonasaprogramminglanguage
interpretedlanguage/Interpretedlanguage
standard(built-in)library/Standard(built-in)library
gluelanguage/Thegluelanguage
wrappermodules/Wrappermodules
Python,basics
about/ThebasicsofPython
importstatements/Importstatements
variables/Variables
forloops/Forloops
If/Elif/Elsestatements/If/Elif/Elsestatements
whilestatement/Whilestatements
comments/Comments
Pythonfolderstructure
about/Pythonfolderstructure
modules,residing/Wheremodulesreside
sysmoduleused,foraddingmodule/UsingPython’ssysmoduletoadda
module
Pythonfunctions
about/Pythonfunctions–Avoidrepeatingcode
defining/Technicaldefinitionoffunctions
writing/Afirstfunction
withparameters/Functionswithparameters
used,forreplacingrepetitivecode/Usingfunctionstoreplacerepetitivecode
generalization/Moregeneralizationofthefunctions
Pythoninterpreter
about/WhatisthePythoninterpreter?
location/WhereisthePythoninterpreterlocated?
using/WhichPythoninterpretershouldbeused?
locating/Howdoesthecomputerknowwheretheinterpreteris?
Pythonmodule
creating/CreatingaPythonmodule
__init__.pyfile/The__init__.pyfile
PythonModules,forGISAnalysis
about/ImportantPythonModulesforGISAnalysis
Pythonscript
about/WhatisaPythonscript?
executing/HowPythonexecutesascript
PythonSystem(SYS)module
about/ThePythonSystem(SYS)module
PythonWin/PythonWin
URL/PythonWin
PythonWindow
script,runningin/RunningthescriptinthePythonWindow
R
randompoints
generating,torepresentpopulation/Generatingrandompointstorepresent
population
replace()method/Updatecursors
row
deleting,withupdatecursor/DeletingarowusinganUpdateCursor
S
scaleproperties
controlling/Controllingthedataframewindowextentandscale
script
adjusting/Exportingthemodelandadjustingthescript,AdjustingtheScript
CSVmodule,addingto/AddingtheCSVmoduletothescript
dynamicparameters,addingto/Addingdynamicparameterstoascript
dynamiccomponents,addingto/Addingdynamiccomponentstothescript
running,inPythonWindow/RunningthescriptinthePythonWindow
breaking/Breakingdownthescript
ScriptAnalysis,ArcPyTools
continuing/Continuingthescriptanalysis:theArcPytools
IntersectTool/TheIntersecttoolandstringmanipulation
StringManipulation/TheIntersecttoolandstringmanipulation
scriptmessages
displaying,arcpy.AddMessageused/Displayingscriptmessagesusing
arcpy.AddMessage
scripttool
creating/CreatingaScripttool
parameters,defining/Labellinganddefiningparameters
parameters,labelling/Labellinganddefiningparameters
scripttoolparameters
settingup/Settingupthescripttoolparameters
Selecttool
modeling/ModelingtheSelectandBuffertools
shapefield
updating/Updatingtheshapefield
SpatialAnalystExtension
accessing/AccessingtheSpatialAnalystExtension
elevation,addingtobusstops/Addingelevationtothebusstops
MapAlgebraused,forgeneratingelevationinfeet/UsingMapAlgebrato
generateelevationinfeet
busstops,adding/Addinginthebusstopsandgettingelevationvalues,The
finalresult
elevationvalues,obtaining/Addinginthebusstopsandgettingelevation
values,Thefinalresult
spreadsheetfieldnames
adding,asparameter/Addingthespreadsheetfieldnamesasaparameter
SQLStatement
adding,asparameter/AddingtheSQLStatementasaparameter
standardlibrarymodules
datetime/Commonlyusedstandardlibrarymodules
math/Commonlyusedstandardlibrarymodules
string/Commonlyusedstandardlibrarymodules
stringaddition/Thestringmanipulationmethod1–stringaddition
stringformatting/Thestringmanipulationmethod2–stringformatting#1,Thestring
manipulationmethod3–stringformatting#2
StringManipulation
stringaddition/Thestringmanipulationmethod1–stringaddition
stringformatting/Thestringmanipulationmethod2–stringformatting#1,The
stringmanipulationmethod3–stringformatting#2
strings/Strings
subroutines
about/Technicaldefinitionoffunctions
Sys.path.append()method
about/Thesys.path.append()method
sysmodule
used,foraddingmodule/UsingPython’ssysmoduletoaddamodule
T
textelements
updating/Updatingthetextelements
adjustedmap,exportingtoPDF/ExportingtheadjustedmaptoPDF
Tkinter
about/IDLE
tuples/Tuples
U
updatecursor
about/Updatecursors
used,fordeletingrow/DeletingarowusinganUpdateCursor
updateRow()method/Updatecursors,Adjustingapointlocation
W
whilestatement/Whilestatements
X
XLRDmodule
about/TheXLRDandXLWTmodules
XLS
creating,XLWTused/CreatinganXLSusingXLWT
XLWTmodule
about/TheXLRDandXLWTmodules
Z
zero-basedindexing
about/Zero-basedindexing