22'''
33 MHN.py
44 Author: npeterson
5- Revised: 9/4 /18
5+ Revised: 12/19 /18
66 ---------------------------------------------------------------------------
77 A class for importing into MHN processing scripts, containing frequently
88 used methods and variables.
@@ -27,21 +27,6 @@ class MasterHighwayNetwork(object):
2727 }
2828
2929 centroid_ranges = {
30- # ## zones09
31- # 'CBD': range( 1, 48), # NB. range(i,j) includes i & excludes j
32- # 'Chicago': range( 1, 310),
33- # 'Cook': range( 1, 855),
34- # 'McHenry': range( 855, 959),
35- # 'Lake': range( 959, 1134),
36- # 'Kane': range(1134, 1279),
37- # 'DuPage': range(1279, 1503),
38- # 'Will': range(1503, 1691),
39- # 'Kendall': range(1691, 1712),
40- # 'CMAP': range( 1, 1712),
41- # 'MHN': range( 1, 1962),
42- # 'POE': range(1945, 1962)
43-
44- ## zones17
4530 'CBD' : range ( 1 , 48 ), # NB. range(i,j) includes i & excludes j
4631 'Chicago' : range ( 1 , 718 ),
4732 'Cook' : range ( 1 , 1733 ),
@@ -58,6 +43,20 @@ class MasterHighwayNetwork(object):
5843 'Grundy_Part' : range (2949 , 2950 ),
5944 'DeKalb_Part' : range (2977 , 2978 )
6045 }
46+
47+ # ## zones09 (C18Q3 and earlier)
48+ # 'CBD': range( 1, 48), # NB. range(i,j) includes i & excludes j
49+ # 'Chicago': range( 1, 310),
50+ # 'Cook': range( 1, 855),
51+ # 'McHenry': range( 855, 959),
52+ # 'Lake': range( 959, 1134),
53+ # 'Kane': range(1134, 1279),
54+ # 'DuPage': range(1279, 1503),
55+ # 'Will': range(1503, 1691),
56+ # 'Kendall': range(1691, 1712),
57+ # 'CMAP': range( 1, 1712),
58+ # 'MHN': range( 1, 1962),
59+ # 'POE': range(1945, 1962)
6160 }
6261
6362 min_node_id = 5001 # 1-5000 reserved for zone centroids/POEs
@@ -108,7 +107,7 @@ class MasterHighwayNetwork(object):
108107 '"AM_SHARE" >= 0.5' )
109108 }
110109
111- ampm_tods = {
110+ ampm_tods = {
112111 '1' : ('1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , 'am' ), # All periods
113112 '2' : ('2' , '3' , '4' , '5' , 'am' ), # AM periods
114113 '3' : ('1' , '6' , '7' , '8' ), # PM periods
@@ -267,7 +266,7 @@ def __init__(self, mhn_gdb_path, zone_gdb_path=None):
267266 }
268267 self .pnr_name = 'parknride'
269268 self .pnr = os .path .join (self .gdb , self .pnr_name )
270- self .projection = arcpy .Describe (self .hwynet ).spatialReference
269+ self .projection = arcpy .Describe (self .arc ).spatialReference
271270
272271 # Zone geodatabase structure (default to zone_systems.gdb in same dir as MHN gdb)
273272 if zone_gdb_path :
@@ -372,25 +371,46 @@ def calculate_itin_measures(self, itin_table):
372371 return itin_table
373372
374373
374+ @staticmethod
375+ def check_edit_session (lyr ):
376+ ''' Check for an active edit session on a specified layer by trying to
377+ open two cursors simultaneously. '''
378+ edit_session = True
379+ try :
380+ OID = arcpy .Describe (lyr ).OIDFieldName
381+ with arcpy .da .UpdateCursor (lyr , OID ) as rows :
382+ row = next (rows )
383+ with arcpy .da .UpdateCursor (lyr , OID ) as rows2 :
384+ row2 = next (rows2 )
385+ except RuntimeError as e :
386+ if str (e ) == "workspace already in transaction mode" :
387+ edit_session = False # No active edit session
388+ else :
389+ raise # Some other RuntimeError
390+ return edit_session
391+
392+
375393 @staticmethod
376394 def check_selection (lyr ):
377395 ''' Check whether specified layer has a selection. '''
378- desc = arcpy .Describe (lyr )
379- selected = desc .FIDSet
380- if len (selected ) == 0 :
396+ try :
397+ if len (arcpy .Describe (lyr ).FIDSet ) == 0 :
398+ return False
399+ else :
400+ return True
401+ except AttributeError :
402+ # `lyr` is not a feature layer or table view
381403 return False
382- else :
383- return True
384404
385405
386406 @staticmethod
387407 def delete_if_exists (filepath ):
388408 ''' Check if a file exists, and delete it if so. '''
389409 if arcpy .Exists (filepath ):
390410 arcpy .Delete_management (filepath )
391- message = filepath + ' successfully deleted.'
411+ message = '{} successfully deleted.'. format ( filepath )
392412 else :
393- message = filepath + ' does not exist.'
413+ message = '{} does not exist.'. format ( filepath )
394414 return message
395415
396416
@@ -417,9 +437,7 @@ def determine_arc_bearing(line_geom):
417437 @staticmethod
418438 def determine_OID_fieldname (fc ):
419439 ''' Determines the Object ID fieldname for the specified fc/table. '''
420- describe = arcpy .Describe (fc )
421- OID_name = describe .OIDFieldName
422- return OID_name
440+ return arcpy .Describe (fc ).OIDFieldName
423441
424442
425443 @staticmethod
@@ -439,7 +457,7 @@ def determine_tolltype(vdf, cost):
439457 @staticmethod
440458 def die (error_message = '' ):
441459 ''' End processing prematurely. '''
442- arcpy .AddError ('\n ' + error_message + ' \n ' )
460+ arcpy .AddError ('\n {} \n ' . format ( error_message ) )
443461 sys .exit ()
444462 return None
445463
@@ -487,7 +505,7 @@ def get_yearless_hwyproj(self):
487505 ''' Check hwyproj completion years and return list of invalid projects'
488506 TIPIDs. '''
489507 common_id_field = self .route_systems [self .hwyproj ][1 ]
490- invalid_year_query = ' "{0}" = 0 OR " {0}" IS NULL' .format ('COMPLETION_YEAR' )
508+ invalid_year_query = "{0} = 0 OR {0} IS NULL" .format ('COMPLETION_YEAR' )
491509 invalid_year_lyr = self .make_skinny_table_view (self .hwyproj , 'invalid_year_lyr' , ['COMPLETION_YEAR' , common_id_field ], invalid_year_query )
492510 invalid_year_count = int (arcpy .GetCount_management (invalid_year_lyr ).getOutput (0 ))
493511 if invalid_year_count > 0 :
@@ -520,12 +538,11 @@ def make_attribute_dict(fc, key_field, attr_list=['*']):
520538 - NOTE 2: using attr_list=[] will essentially build a list of unique
521539 key_field values. '''
522540 attr_dict = {}
523- fc_field_objects = arcpy .ListFields (fc )
524- fc_fields = [field .name for field in fc_field_objects if field .type != 'Geometry' ]
541+ fc_fields = [f .name for f in arcpy .ListFields (fc ) if f .type != 'Geometry' ]
525542 if attr_list == ['*' ]:
526543 valid_fields = fc_fields
527544 else :
528- valid_fields = [field for field in attr_list if field in fc_fields ]
545+ valid_fields = [f for f in attr_list if f in fc_fields ]
529546 # Ensure that key_field is always the first field in the field list
530547 cursor_fields = [key_field ] + list (set (valid_fields ) - set ([key_field ]))
531548 with arcpy .da .SearchCursor (fc , cursor_fields ) as cursor :
@@ -555,9 +572,9 @@ def make_skinny(is_geo, in_obj, out_obj, keep_fields_list=None, where_clause=Non
555572 keep_fields_list = []
556573 for field in input_fields :
557574 if field .name in keep_fields_list :
558- field_info_str += field . name + ' ' + field . name + ' VISIBLE;'
575+ field_info_str += '{0} {0} VISIBLE;'. format ( field . name )
559576 else :
560- field_info_str += field . name + ' ' + field . name + ' HIDDEN;'
577+ field_info_str += '{0} {0} HIDDEN;'. format ( field . name )
561578 field_info_str .rstrip (';' ) # Remove trailing semicolon
562579 if is_geo :
563580 arcpy .MakeFeatureLayer_management (in_obj , out_obj , where_clause , field_info = field_info_str )
@@ -580,14 +597,14 @@ def set_nulls(value, fc, fields):
580597 valid_types = ['String' ]
581598 else :
582599 valid_types = ['String' , 'SmallInteger' , 'Integer' , 'Single' , 'Double' ]
583- matched_fields = [field for field in arcpy .ListFields (fc ) if field .name in fields and field .type in valid_types ]
600+ matched_fields = [f for f in arcpy .ListFields (fc ) if f .name in fields and f .type in valid_types ]
584601 for field in matched_fields :
585602 if field .type == 'String' :
586- expression = "'{0 }'" .format (value )
603+ expression = "'{}'" .format (value )
587604 else :
588- expression = '{0}' .format (value )
605+ expression = "{}" .format (value )
589606 null_view = 'null_view'
590- arcpy .MakeTableView_management (fc , null_view , '"{0}" IS NULL' .format (field .name ))
607+ arcpy .MakeTableView_management (fc , null_view , "{} IS NULL" .format (field .name ))
591608 if int (arcpy .GetCount_management (null_view ).getOutput (0 )) > 0 :
592609 arcpy .CalculateField_management (null_view , field .name , expression , 'PYTHON' )
593610 arcpy .Delete_management (null_view )
@@ -611,7 +628,7 @@ def submit_sas(self, sas_file, sas_log, sas_lst, arg_list=None):
611628 if not arg_list :
612629 arg_str = ''
613630 else :
614- arg_str = '$' .join (( str (arg ) for arg in arg_list ) )
631+ arg_str = '$' .join (str (arg ) for arg in arg_list )
615632 bat = os .path .join (self .prog_dir , 'sasrun.bat' )
616633 cmd = [bat , sas_file , arg_str , sas_log , sas_lst ]
617634 return subprocess .check_call (cmd , startupinfo = startupinfo )
@@ -654,7 +671,7 @@ def tipid_from_int(self, n):
654671 ''' Format an integer < 100,000,000 as a TIPID string. '''
655672 try :
656673 n_str = str (int (n )).zfill (8 )
657- tipid = '-' .join (( n_str [:2 ], n_str [2 :4 ], n_str [4 :]) )
674+ tipid = '-' .join ([ n_str [:2 ], n_str [2 :4 ], n_str [4 :]] )
658675 except :
659676 return None
660677 return tipid if self .is_tipid (tipid ) else None
@@ -725,15 +742,15 @@ def write_arc_flag_file(self, flag_file, flag_query, csv_mode=False):
725742 w .write ('ANODE,BNODE\n ' )
726743 else :
727744 row_prefix = 'l='
728- w .write ('~# {0 } links\n ' .format (flag_query .strip ()))
745+ w .write ('~# {} links\n ' .format (flag_query .strip ()))
729746 with arcpy .da .SearchCursor (flag_lyr , ['ANODE' , 'BNODE' , 'DIRECTIONS' ]) as cursor :
730747 for row in cursor :
731748 anode = row [0 ]
732749 bnode = row [1 ]
733750 directions = int (row [2 ])
734- w .write ('{2}{0 },{1 }\n ' .format (anode , bnode , row_prefix ))
751+ w .write ('{0}{1 },{2 }\n ' .format (row_prefix , anode , bnode ))
735752 if directions > 1 :
736- w .write ('{2}{1 },{0 }\n ' .format (anode , bnode , row_prefix ))
753+ w .write ('{0}{2 },{1 }\n ' .format (row_prefix , anode , bnode ))
737754 arcpy .Delete_management (flag_lyr )
738755 return flag_file
739756
@@ -743,12 +760,11 @@ def write_attribute_csv(in_obj, textfile, field_list=None, include_headers=True)
743760 ''' Write attributes of a feature class/table to a specified text file.
744761 Input field_list allows output field order to be specified. Defaults to
745762 all non-shape fields. '''
746- all_field_objects = arcpy .ListFields (in_obj )
747- valid_field_names = [field .name for field in all_field_objects if field .name != '' and field .type != 'Geometry' ]
763+ valid_field_names = [f .name for f in arcpy .ListFields (in_obj ) if f .name != '' and f .type != 'Geometry' ]
748764 if not field_list :
749765 fields = valid_field_names
750766 else :
751- fields = [field for field in field_list if field in valid_field_names ]
767+ fields = [f for f in field_list if f in valid_field_names ]
752768 csv = open (textfile ,'w' )
753769 if include_headers :
754770 csv .write (',' .join (fields ) + '\n ' )
0 commit comments