Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
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