from os import remove, path, listdir, mkdir import shutil import arcpy from numpy import nan import pandas import AppendBook import CreateStreetIndex import settings def delete_if_exists(layer): if arcpy.Exists(layer): arcpy.Delete_management(layer) def get_dispatch_info(): # Erode each dispatch area so that there aren't false positives arcpy.Buffer_analysis(settings.dispatch_grid, 'in_memory\\eroded_dispatch', '-80 feet') # Filter the dispatch areas down to those that intersect a fire boundary arcpy.SpatialJoin_analysis( 'in_memory\\eroded_dispatch', settings.dispatch_fire_boundaries, 'in_memory\\dispatch_needed', 'JOIN_ONE_TO_ONE', 'KEEP_COMMON', match_option='INTERSECT', ) eroded_cur = arcpy.da.SearchCursor('in_memory\\dispatch_needed', [ 'SHAPE@', 'DISPATCHNUM', 'NPAGENUM', 'NEPAGENUM', 'EPAGENUM', 'SEPAGENUM', 'SPAGENUM', 'SWPAGENUM', 'WPAGENUM', 'NWPAGENUM', ]) fire_boundary_cur = arcpy.da.SearchCursor(settings.dispatch_fire_boundaries, [ 'SHAPE@', 'STATIONNO', ]) # Create a list of dispatch area dicts dispatch_areas = [] for eroded_area in eroded_cur: dispatch_areas.append({ 'shape': eroded_area[0], 'dispatch_num': eroded_area[1], 'n': eroded_area[2], 'ne': eroded_area[3], 'e': eroded_area[4], 'se': eroded_area[5], 's': eroded_area[6], 'sw': eroded_area[7], 'w': eroded_area[8], 'nw': eroded_area[9], }) # station_dict has a key of the station number, and a value of a list of dicts. The dicts inside each station number # list define the metadata for the dispatch area. The metadata is used to generate the definition queries and the # labels for each dispatch area map. station_dict = {} # Fill `station_dict` with the dispatch areas that go in each fire station print("Finding the dispatch areas for each station") for fire_area in fire_boundary_cur: for dispatch_area in dispatch_areas: if dispatch_area['shape'].overlaps(fire_area[0]) or dispatch_area['shape'].within(fire_area[0]): if fire_area[1] in station_dict.keys(): station_dict[fire_area[1]].append(dispatch_area) else: station_dict[fire_area[1]] = [dispatch_area] eroded_cur.reset() return dispatch_areas, station_dict def export_covers(station_dict): print("Exporting Covers") logo_element = arcpy.mapping.ListLayoutElements(settings.cover_mxd, 'PICTURE_ELEMENT', 'logo_station')[0] title_element = arcpy.mapping.ListLayoutElements(settings.cover_mxd, 'TEXT_ELEMENT', 'page_title')[0] for station_num in station_dict.keys(): print("\tStation " + str(station_num)) cover_logo_path = '\\\\stpfs1msc\\Depts\\GIS_Shared\\Esri\\Images\\Fire\\logo_station{0}.png'.format( station_num) logo_element.sourceImage = cover_logo_path title_element.text = "St. Petersburg Fire Department - Fire Run Book (Station #{0})".format(station_num) arcpy.mapping.ExportToPDF( settings.cover_mxd, path.join(settings.pdf_temp_dir, 'Cover{0}.pdf'.format(station_num)), compress_vectors=False, ) def export_indexes(station_dict): print("Exporting Index Maps") station_ref = dict([ values for values in arcpy.da.SearchCursor(settings.index_fire_boundaries, ['STATIONNO', 'NAME']) ]) # Loop through all the stations for station_num in station_ref.keys(): print("\tStation " + str(station_num)) station_name = station_ref[station_num] # Set definition queries dispatch_areas = [dispatch['dispatch_num'] for dispatch in station_dict[station_num]] definition_query = "DISPATCHNUM = '" + "' OR DISPATCHNUM = '".join(dispatch_areas) + "'" settings.index_dispatch.definitionQuery = definition_query settings.index_fire_boundaries.definitionQuery = "SUBTYPEFIELD = 1 AND STATIONNO = " + str(station_num) # Pan DataFrame to station extent settings.index_df.extent = settings.index_dispatch.getExtent() settings.index_df.scale = round(settings.index_df.scale * 1.03, 50) # Change page elements map_title = arcpy.mapping.ListLayoutElements(settings.index_mxd, 'TEXT_ELEMENT', 'map_title')[0] map_title.text = "{0} (STATION #{1}) MAP INDEX".format(station_name.upper(), station_num) arcpy.mapping.ExportToPDF(settings.index_mxd, path.join(settings.pdf_temp_dir, 'Index{0}.pdf'.format(station_num)), compress_vectors=False) settings.index_dispatch.definitionQuery = 'SUBTYPEFIELD = 1' def export_streets(station_dict): print("Exporting Street Index") roads_layer = arcpy.mapping.Layer(path.join(settings.base_dir, 'roads.lyr')) for station_num in station_dict.keys(): print("\tStation " + str(station_num)) station_areas = [station_data['dispatch_num'] for station_data in station_dict[station_num]] settings.dispatch_grid.definitionQuery = "DISPATCHNUM = '" + "' OR DISPATCHNUM = '".join(station_areas) + "'" output_path = path.join(settings.pdf_temp_dir, 'Streets{0}.pdf'.format(station_num)) CreateStreetIndex.main( road_centerlines=roads_layer, full_name='FULLNAME', fire_map_index=settings.dispatch_grid, page_name='DISPATCHNUM', orientation='LANDSCAPE', output_pdf=output_path, ) # Reset the dispatch grid settings.dispatch_grid.definitionQuery = "" def export_maps(dispatch_areas): print("Exporting Dispatch Area Maps") # Select the map elements legend = arcpy.mapping.ListLayoutElements(settings.dispatch_mxd, "LEGEND_ELEMENT", "Legend")[0] def get_text_el(name): return arcpy.mapping.ListLayoutElements(settings.dispatch_mxd, "TEXT_ELEMENT", name)[0] dispatch_label = get_text_el("dispatch_grid") north_label = get_text_el("north") northeast_label = get_text_el("northeast") east_label = get_text_el("east") southeast_label = get_text_el("southeast") south_label = get_text_el("south") southwest_label = get_text_el("southwest") west_label = get_text_el("west") northwest_label = get_text_el("northwest") # Export the individual dispatch area maps that are needed for i, page in enumerate(dispatch_areas): print("\tDispatch area {0} ({1}/{2})".format( page['dispatch_num'], i + 1, len(dispatch_areas) )) arcpy.SelectLayerByAttribute_management( settings.dispatch_grid, "NEW_SELECTION", "DISPATCHNUM = '{0}'".format(page['dispatch_num']) ) # Zoom to correct area settings.dispatch_df.zoomToSelectedFeatures() arcpy.SelectLayerByAttribute_management(settings.dispatch_grid, "CLEAR_SELECTION") settings.dispatch_df.scale *= 0.95 # Change map labels def stringify(d): return str(d) if d else 'None' dispatch_label.text = page['dispatch_num'] north_label.text = stringify(page['n']) northeast_label.text = stringify(page['ne']) east_label.text = stringify(page['e']) southeast_label.text = stringify(page['se']) south_label.text = stringify(page['s']) southwest_label.text = stringify(page['sw']) west_label.text = stringify(page['w']) northwest_label.text = stringify(page['nw']) # Make sure legend is aligned correctly legend.elementPositionY = 0.75 # Export the PDF out_pdf_path = path.join(settings.pdf_temp_dir, 'Dispatch{0}.pdf'.format(page['dispatch_num'])) arcpy.mapping.ExportToPDF(settings.dispatch_mxd, out_pdf_path, compress_vectors=False) def merge_pdfs(station_dict): print("Merging PDFs") # Combine the maps into books for individual stations for station_num in station_dict.keys(): cover_path = path.join(settings.pdf_temp_dir, 'Cover{0}.pdf'.format(station_num)) index_path = path.join(settings.pdf_temp_dir, 'Index{0}.pdf'.format(station_num)) streets_path = path.join(settings.pdf_temp_dir, 'Streets{0}.pdf'.format(station_num)) dispatch_paths = [] for area in station_dict[station_num]: dispatch_num = area['dispatch_num'] dispatch_paths.append( path.join(settings.pdf_temp_dir, 'Dispatch{0}.pdf'.format(dispatch_num)) ) dispatch_paths.sort() all_pages = ';'.join([cover_path, index_path, streets_path, ';'.join(dispatch_paths)]) AppendBook.add_pages(path.join(settings.pdf_export_dir, "HydrantMapsStation{0}.pdf".format(station_num)), all_pages) def export_data(station_dict): print("Exporting Excel data") # Write out the dispatch areas per station dispatch_writer = pandas.ExcelWriter(path.join(settings.pdf_export_dir, 'DispatchAreasPerStation.xlsx'), engine='xlsxwriter') print("\tDispatch area per station") # Create a string for each station that shows their respective dispatch areas station_listing = {} for station_num in station_dict.keys(): dispatch_nums = [] for dispatch_area in station_dict[station_num]: dispatch_nums.append(int(dispatch_area['dispatch_num'])) dispatch_nums.sort() station_listing[station_num] = str(dispatch_nums)[1:-1] # Export to Excel station_df = pandas.DataFrame({ 'StationNum': station_listing.keys(), 'DispatchNums': station_listing.values(), }).set_index('StationNum') station_df.to_excel(dispatch_writer, sheet_name='DispatchAreasPerStation') dispatch_writer.save() # Create hydrant Excel doc print("\tHydrant data") if path.exists(settings.temp_gdb_path): shutil.rmtree(settings.temp_gdb_path) arcpy.CreateFileGDB_management(settings.base_dir, settings.temp_gdb_name) else: arcpy.CreateFileGDB_management(settings.base_dir, settings.temp_gdb_name) def get_layer(name): return arcpy.mapping.ListLayers(settings.dispatch_mxd, name, settings.dispatch_df)[0] city_hydrants = get_layer("City Water Hydrants") county_hydrants = get_layer("County Water Hydrants") private_hydrants = get_layer("Private Hydrants") def add_fields(layer, out_path): temp_fc = path.join(settings.temp_gdb_path, "temp") delete_if_exists(temp_fc) delete_if_exists(out_path) arcpy.SpatialJoin_analysis( layer, settings.dispatch_grid, temp_fc, 'JOIN_ONE_TO_MANY', 'KEEP_ALL', match_option='INTERSECT', ) arcpy.SpatialJoin_analysis( temp_fc, settings.dispatch_fire_boundaries, out_path, 'JOIN_ONE_TO_MANY', 'KEEP_ALL', match_option='INTERSECT', ) arcpy.AddGeometryAttributes_management( Input_Features=out_path, Geometry_Properties="POINT_X_Y_Z_M", Coordinate_System=4326 ) # Join the layers to the dispatch area and the fire station area city_joined = path.join(settings.temp_gdb_path, 'city_joined') county_joined = path.join(settings.temp_gdb_path, 'county_joined') private_joined = path.join(settings.temp_gdb_path, 'private_joined') add_fields(city_hydrants, city_joined) add_fields(county_hydrants, county_joined) add_fields(private_hydrants, private_joined) hydrant_writer = pandas.ExcelWriter(path.join(settings.pdf_export_dir, 'Hydrants.xlsx'), engine='xlsxwriter') def export_2_excel(layer, sheet_name, fields): np_array = arcpy.da.FeatureClassToNumPyArray( in_table=layer, field_names=fields, skip_nulls=False, null_value=-999999, ) pandas.DataFrame(np_array)\ .replace([-999999, "-999999"], nan)\ .rename(index=str, columns={ "POINT_Y": "latitude", "POINT_X": "longitude", })\ .to_excel(hydrant_writer, sheet_name=sheet_name) city_private_fields = [ "OBJECTID", "FACILITYID", "INSTALLDATE", "LOCDESC", "ROTATION", "MANUFACTURER", "ENABLED", "OWNEDBY", "MAINTBY", "WATERTYPE", "HYDTTYPE", "NOTES", "HYDRANTID", "STATUS", "PROJECTNUM", "WAMID", "PLAT", "DWGNUM", "BARRELLENGTH", "JURISDICTION", "CREATED_USER", "CREATED_DATE", "LAST_EDITED_USER", "LAST_EDITED_DATE", "FLOWTYPE", "DISPATCHNUM", "STATIONNO", "POINT_Y", "POINT_X", ] county_fields = [ "OBJECTID", "FACILITYID", "NAME", "OWNER", "FULLADDR", "USNGADDRES", "TESTDATE", "WATERSOURC", "MAINSIZE", "DISCHARGEC", "ORIFICESIZ", "ORIFICES_1", "ORIFICES_2", "ORIFICES_3", "NOOUTLETSF", "ORIFICETES", "STATICPRES", "RESIDUALPR", "PIPOTPRESS", "FLOWATPITO", "FLOWATRES1", "FLOWATRES2", "FLOWATRES3", "HASFIREINF", "LASTUPDATE", "LASTEDITOR", "GLOBALID", "GRID", "HYDRANTTYP", "CREATED_US", "CREATED_DA", "LAST_EDITE", "LAST_EDI_1", "DISPATCHNUM", "STATIONNO", "POINT_Y", "POINT_X", ] export_2_excel(city_joined, "CityHydrants", city_private_fields) export_2_excel(county_joined, "CountyHydrants", county_fields) export_2_excel(private_joined, "PrivateHydrants", city_private_fields) hydrant_writer.save() def refresh_path(dirname): if path.exists(dirname): shutil.rmtree(dirname) mkdir(dirname) else: mkdir(dirname) def main(): refresh_path(settings.pdf_export_dir) refresh_path(settings.pdf_temp_dir) dispatch_areas, station_dict = get_dispatch_info() # Create PDFs export_covers(station_dict) export_indexes(station_dict) export_streets(station_dict) export_maps(dispatch_areas) # Make PDF books and delete temporary PDFs merge_pdfs(station_dict) shutil.rmtree(settings.pdf_temp_dir) # Export dispatch areas and hydrant data export_data(station_dict) # Try to copy and replace all files in '\\\\stpLadderFR\\FireHydrants' if not settings.DEV: for file_name in listdir(settings.pdf_export_dir): print("Copying " + file_name) try: remove(path.join(settings.destination_dir, file_name)) shutil.copy2(path.join(settings.pdf_export_dir, file_name), path.join(settings.destination_dir, file_name)) except IOError: print(file_name + " failed to copy.") if __name__ == '__main__': main()