def netmiles_in_buffer(fc_project, fc_network, project_type): # if project is polygon, then use polygon. If line or point, then make polygon as buffer around line/point. if project_type == p.ptype_area_agg: fc_poly_buff = fc_project else: fc_poly_buff = r"memory\temp_buff_qmi" arcpy.Buffer_analysis(fc_project, fc_poly_buff, p.bikeway_buff) fl_poly = "fl_buff" if not arcpy.Exists(fl_poly): utils.make_fl_conditional(fc_poly_buff, fl_poly) temp_intersect_fc = r"memory\temp_intersect_fc" #run intersect of network lines against buffer arcpy.Intersect_analysis([fc_network, fc_poly_buff], temp_intersect_fc) # get total mileage of network lines-buffer intersection fc net_len = 0 with arcpy.da.SearchCursor(temp_intersect_fc, "SHAPE@LENGTH") as cur: for row in cur: net_len += row[0] arcpy.Delete_management(temp_intersect_fc) if fc_poly_buff != fc_project: arcpy.Delete_management(fc_poly_buff) return net_len
def create_tripshed_poly(in_poly_fc, out_poly_fc, poly_id_field, in_df, df_grouby_field): # convert numpy (pandas) datatypes to ESRI data types {numpy type: ESRI type} dtype_conv_dict = { 'float64': 'FLOAT', 'object': 'TEXT', 'int64': 'LONG', 'String': 'TEXT', 'OID': 'LONG', 'Single': 'DOUBLE', 'Integer': 'LONG' } #make copy of base input poly fc that only has features whose IDs are in the dataframe fl_input_polys = 'fl_input_polys' utils.make_fl_conditional(in_poly_fc, fl_input_polys) df_ids = tuple(in_df[df_grouby_field]) sql = "{} IN {}".format(poly_id_field, df_ids) arcpy.SelectLayerByAttribute_management(fl_input_polys, "NEW_SELECTION", sql) arcpy.CopyFeatures_management(fl_input_polys, out_poly_fc) # add dataframe fields to the trip shed polygon set #dict of {field: field data type} for input dataframe fields_dtype_dict = {col: str(in_df[col].dtype) for col in in_df.columns} # populate those fields with the dataframe data for field in fields_dtype_dict.keys(): print("adding {} column and data...".format(field)) field_vals = list(in_df[field]) # get list of values for desired field fld_dict = dict(zip(df_ids, field_vals)) fdtype_numpy = fields_dtype_dict[field] fdtype_esri = dtype_conv_dict[fdtype_numpy] # add a field, if needed, to the polygon feature class for the values being added if utils.esri_field_exists(out_poly_fc, field): pass else: arcpy.AddField_management(out_poly_fc, field, fdtype_esri) # populate the field with the appropriate values with arcpy.da.UpdateCursor(out_poly_fc, [poly_id_field, field]) as cur: for row in cur: join_id = row[0] if fld_dict.get(join_id) is None: pass else: row[1] = fld_dict[join_id] cur.updateRow(row)
def get_acc_data(fc_project, fc_accdata, project_type, get_ej=False): arcpy.AddMessage("calculating accessibility metrics...") out_dict = {} try: fl_accdata = "fl_accdata" fl_project = "fl_project" utils.make_fl_conditional(fc_project, fl_project) utils.make_fl_conditional(fc_accdata, fl_accdata) # select polygons that intersect with the project line searchdist = 0 if project_type == p.ptype_area_agg else p.bg_search_dist arcpy.SelectLayerByLocation_management(fl_accdata, "INTERSECT", fl_project, searchdist, "NEW_SELECTION") # read accessibility data from selected polygons into a dataframe accdata_fields = [p.col_geoid, p.col_acc_ej_ind, p.col_pop ] + p.acc_cols_ej accdata_df = utils.esri_object_to_df(fl_accdata, accdata_fields) # get pop-weighted accessibility values for all accessibility columns if get_ej: for col in p.acc_cols_ej: col_wtd = "{}_wtd".format(col) col_ej_pop = "{}_EJ".format(p.col_pop) accdata_df[col_wtd] = accdata_df[col] * accdata_df[ p.col_pop] * accdata_df[p.col_acc_ej_ind] accdata_df[col_ej_pop] = accdata_df[p.col_pop] * accdata_df[ p.col_acc_ej_ind] tot_ej_pop = accdata_df[col_ej_pop].sum() out_wtd_acc = accdata_df[col_wtd].sum( ) / tot_ej_pop if tot_ej_pop > 0 else 0 col_out_ej = "{}_EJ".format(col) out_dict[col_out_ej] = out_wtd_acc else: for col in p.acc_cols: col_wtd = "{}_wtd".format(col) accdata_df[col_wtd] = accdata_df[col] * accdata_df[p.col_pop] out_wtd_acc = accdata_df[col_wtd].sum() / accdata_df[ p.col_pop].sum() out_dict[col] = out_wtd_acc except: msg = "{} {}".format(arcpy.GetMessages(2), trace()) arcpy.AddMessage(msg) return out_dict
def point_sum(fc_pclpt, fc_project, project_type, val_fields, buffdist, case_field=None, case_excs_list=[]): arcpy.AddMessage("aggregating land use data...") scratch_gdb = arcpy.env.scratchGDB fl_parcel = "{}/fl_parcel".format(scratch_gdb) fl_project = "{}/fl_project".format(scratch_gdb) utils.make_fl_conditional(fc_pclpt, fl_parcel) utils.make_fl_conditional(fc_project, fl_project) buff_dist = 0 if project_type == p.ptype_area_agg else buffdist arcpy.SelectLayerByLocation_management(fl_parcel, "WITHIN_A_DISTANCE", fl_project, buff_dist) # If there are no points in the buffer (e.g., no collisions on segment, no parcels, etc.), # still add those columns, but make them = 0 file_len = arcpy.GetCount_management(fl_parcel) file_len = int(file_len.getOutput(0)) if case_field is not None: val_fields.append(case_field) # load parcel data into dataframe rows_pcldata = [] with arcpy.da.SearchCursor(fl_parcel, val_fields) as cur: for row in cur: df_row = list(row) rows_pcldata.append(df_row) parcel_df = pd.DataFrame(rows_pcldata, columns=val_fields) if case_field is not None: parcel_df = parcel_df.loc[~parcel_df[case_field].isin( case_excs_list)] #exclude specified categories out_df = parcel_df.groupby( case_field).sum().T # get sum by category (case field) # NEXT ISSUE - need to figure out how to show all case types, even if no parcels with that case type within the buffer else: out_df = pd.DataFrame(parcel_df[val_fields].sum(axis=0)).T out_dict = out_df.to_dict('records')[0] return out_dict
def get_centerline_miles(selection_poly_fc, centerline_fc): fl_selection_poly = "fl_selection_poly" fl_centerline = "fl_centerline" utils.make_fl_conditional(selection_poly_fc, fl_selection_poly) utils.make_fl_conditional(centerline_fc, fl_centerline) arcpy.SelectLayerByLocation_management(fl_centerline, "HAVE_THEIR_CENTER_IN", fl_selection_poly) cline_miles = 0 with arcpy.da.SearchCursor(fl_centerline, "SHAPE@LENGTH") as cur: for row in cur: cline_miles += row[0] return cline_miles / p.ft2mile
def get_model_link_sums(fc_polygon, fc_model_links): fl_polygon = "fl_polygon" fl_model_links = "fl_model_links" utils.make_fl_conditional(fc_polygon, fl_polygon) utils.make_fl_conditional(fc_model_links, fl_model_links) # select model links whose centroid is within the polygon area arcpy.SelectLayerByLocation_management(fl_model_links, "HAVE_THEIR_CENTER_IN", fl_polygon) link_data_cols = [p.col_capclass, p.col_distance, p.col_lanemi, p.col_dayvmt] output_data_cols = [p.col_dayvmt, p.col_distance] # load model links, selected to be near project, into a dataframe df_linkdata = utils.esri_object_to_df(fl_model_links, link_data_cols) # get total VMT for links within the area out_dict = {col: df_linkdata[col].sum() for col in output_data_cols} return out_dict
def get_mix_idx(fc_parcel, fc_project, project_type): arcpy.AddMessage("calculating mix index...") fl_parcel = "fl_parcel" fl_project = "fl_project" utils.make_fl_conditional(fc_parcel, fl_parcel) utils.make_fl_conditional(fc_project, fl_project) in_cols = [ p.col_parcelid, p.col_hh, p.col_k12_enr, p.col_emptot, p.col_empfood, p.col_empret, p.col_empsvc, p.col_area_ac, p.col_lutype ] lu_fac_cols = [ p.col_k12_enr, p.col_emptot, p.col_empfood, p.col_empret, p.col_empsvc, p.col_parkac ] # make parcel feature layer buffer_dist = 0 if project_type == p.ptype_area_agg else p.mix_index_buffdist arcpy.SelectLayerByLocation_management(fl_parcel, "WITHIN_A_DISTANCE", fl_project, buffer_dist, "NEW_SELECTION") summ_df = make_summary_df(fl_parcel, in_cols, lu_fac_cols, p.col_hh, p.park_calc_dict) out_df = calc_mix_index(summ_df, p.params_df, p.col_hh, lu_fac_cols, p.mix_idx_col) # if you want to make CSV. #out_df[[col_hh, mix_idx_col]].to_csv(out_csv, index = False) #print("Done! Output CSV: {}".format(out_csv)) out_val = out_df[p.mix_idx_col][0] return {p.mix_idx_col: out_val}
def intersection_density(fc_project, fc_intersxns, project_type): arcpy.AddMessage("calculating intersection density...") fl_project = "fl_projline" fl_intersxns = "fl_trnstp" utils.make_fl_conditional(fc_project, fl_project) utils.make_fl_conditional(fc_intersxns, fl_intersxns) # analysis area. If project is line or point, then it's a buffer around the line/point. # If it's a polygon (e.g. ctype or region), then no buffer and analysis area is that within the input polygon if project_type == p.ptype_area_agg: fc_buff = fc_project else: p.intersxn_dens_buff fc_buff = r"memory\temp_buff_qmi" arcpy.Buffer_analysis(fl_project, fc_buff, p.intersxn_dens_buff) fl_buff = "fl_buff" utils.make_fl_conditional(fc_buff, fl_buff) buff_acres = get_poly_area(fl_buff) # get count of transit stops within buffer arcpy.SelectLayerByLocation_management(fl_intersxns, "INTERSECT", fl_buff, 0, "NEW_SELECTION") intsxn_34 = 0 col_link_cnt = "LINKS" with arcpy.da.SearchCursor(fl_intersxns, [col_link_cnt]) as cur: for row in cur: if row[0] > 2: intsxn_34 += 1 intersxns_per_acre = intsxn_34 / buff_acres if buff_acres > 0 else 0 return {"Intersxn_34_per_acre": intersxns_per_acre}
def transit_svc_density(fc_project, fc_trnstops, project_type): arcpy.AddMessage("calculating transit service density...") fl_project = "fl_projline" fl_trnstops = "fl_trnstp" utils.make_fl_conditional(fc_project, fl_project) utils.make_fl_conditional(fc_trnstops, fl_trnstops) # analysis area. If project is line or point, then it's a buffer around the line/point. # If it's a polygon (e.g. ctype or region), then no buffer and analysis area is that within the input polygon if project_type == p.ptype_area_agg: fc_buff = fc_project else: p.intersxn_dens_buff fc_buff = r"memory\temp_buff_qmi" arcpy.Buffer_analysis(fl_project, fc_buff, p.trn_buff_dist) fl_buff = "fl_buff" utils.make_fl_conditional(fc_buff, fl_buff) # calculate buffer area buff_acres = get_poly_area(fl_buff) # get count of transit stops within buffer arcpy.SelectLayerByLocation_management(fl_trnstops, "INTERSECT", fl_buff, 0, "NEW_SELECTION") transit_veh_events = 0 with arcpy.da.SearchCursor(fl_trnstops, [p.col_transit_events]) as cur: for row in cur: vehstops = row[0] if row[0] is not None else 0 transit_veh_events += vehstops trnstops_per_acre = transit_veh_events / buff_acres if buff_acres > 0 else 0 return {"TrnVehStop_Acre": trnstops_per_acre}
def get_collision_data(fc_project, project_type, fc_colln_pts, project_adt): arcpy.AddMessage("Aggregating collision data...") fc_model_links = p.model_links_fc() fl_project = 'proj_fl' fl_colln_pts = 'collision_fl' utils.make_fl_conditional(fc_project, fl_project) utils.make_fl_conditional(fc_colln_pts, fl_colln_pts) # if for project segment, get annual VMT for project segment based on user input and segment length df_projlen = utils.esri_object_to_df(fl_project, ["SHAPE@LENGTH"]) proj_len_mi = df_projlen.iloc[0][0] / p.ft2mile # return project length in miles # for aggregate, polygon-based avgs (e.g., community type, whole region), use model for VMT; for # project, the VMT will be based on combo of project length and user-entered ADT for project # approximate annual project VMT, assuming ADT is reflective of weekdays only, but assumes if project_type == p.ptype_area_agg: vmt_dict = get_model_link_sums(fc_project, fc_model_links) dayvmt = vmt_dict[p.col_dayvmt] ann_proj_vmt = dayvmt * 320 proj_len_mi = get_centerline_miles(fc_project, p.reg_centerline_fc) else: ann_proj_vmt = project_adt * proj_len_mi * 320 # get collision totals searchdist = 0 if project_type == p.ptype_area_agg else p.colln_searchdist arcpy.SelectLayerByLocation_management(fl_colln_pts, 'WITHIN_A_DISTANCE', fl_project, searchdist) colln_cols = [p.col_fwytag, p.col_nkilled, p.col_bike_ind, p.col_ped_ind] df_collndata = utils.esri_object_to_df(fl_colln_pts, colln_cols) # filter so that fwy collisions don't get tagged to non-freeway projects, and vice-versa if project_type == p.ptype_fwy: df_collndata = df_collndata.loc[df_collndata[p.col_fwytag] == 1] elif project_type == p.ptype_area_agg: pass # for aggregating at polygon level, like region or community type, we want all collisions on all roads else: df_collndata = df_collndata.loc[df_collndata[p.col_fwytag] == 0] total_collns = df_collndata.shape[0] fatal_collns = df_collndata.loc[df_collndata[p.col_nkilled] > 0].shape[0] bikeped_collns = df_collndata.loc[(df_collndata[p.col_bike_ind] == p.ind_val_true) | (df_collndata[p.col_ped_ind] == p.ind_val_true)].shape[0] pct_bikeped_collns = bikeped_collns / total_collns if total_collns > 0 else 0 bikeped_colln_clmile = bikeped_collns / proj_len_mi # collisions per million VMT (MVMT) = avg annual collisions / (modeled daily VMT * 320 days) * 1,000,000 avg_ann_collisions = total_collns / p.years_of_collndata avg_ann_fatalcolln = fatal_collns / p.years_of_collndata colln_rate_per_vmt = avg_ann_collisions / ann_proj_vmt * 100000000 if ann_proj_vmt > 0 else 0 fatalcolln_per_vmt = avg_ann_fatalcolln / ann_proj_vmt * 100000000 if ann_proj_vmt > 0 else 0 pct_fatal_collns = avg_ann_fatalcolln / avg_ann_collisions if ann_proj_vmt > 0 else 0 out_dict = {"TOT_COLLISNS": total_collns, "TOT_COLLISNS_PER_100MVMT": colln_rate_per_vmt, "FATAL_COLLISNS": fatal_collns, "FATAL_COLLISNS_PER_100MVMT": fatalcolln_per_vmt, "PCT_FATAL_COLLISNS": pct_fatal_collns, "BIKEPED_COLLISNS": bikeped_collns, "BIKEPED_COLLISNS_PER_CLMILE": bikeped_colln_clmile, "PCT_BIKEPED_COLLISNS": pct_bikeped_collns} return out_dict
def get_lutype_acreage(fc_project, projtyp, fc_poly_parcels, lutype): arcpy.AddMessage("Estimating {} acres near project...".format(lutype)) fl_parcels = "fl_parcel" fl_project = "fl_project" for fc, fl in { fc_project: fl_project, fc_poly_parcels: fl_parcels }.items(): utils.make_fl_conditional(fc, fl) # if arcpy.Exists(fl): # arcpy.Delete_management(fl) # arcpy.MakeFeatureLayer_management(fc, fl) # else: # arcpy.MakeFeatureLayer_management(fc, fl) # create temporary buffer IF the input project fc is a line. If it's a polygon, then don't make separate buffer if projtyp == p.ptype_area_agg: fc_buff = fc_project else: buff_dist = p.ilut_sum_buffdist # distance in feet fc_buff = r"memory\temp_buff_qmi" arcpy.Buffer_analysis(fl_project, fc_buff, buff_dist) fl_buff = "fl_buff" arcpy.MakeFeatureLayer_management(fc_buff, fl_buff) """ # calculate buffer area, inclusive of water bodies and rights of way buff_area_ft2 = 0 with arcpy.da.SearchCursor(fl_buff, ["SHAPE@AREA"]) as cur: for row in cur: buff_area_ft2 += row[0] buff_acre = buff_area_ft2 / p.ft2acre # convert from ft2 to acres. may need to adjust for projection-related issues. See PPA1 for more info """ # create intersect layer of buffer with parcels of selected LUTYPE fc_intersect = r"memory\temp_intersect" arcpy.Intersect_analysis([fl_buff, fl_parcels], fc_intersect, "ALL", "", "INPUT") # calculate total area on parcels within buffer (excluding water and rights of way) fl_intersect = "fl_intersect" arcpy.MakeFeatureLayer_management(fc_intersect, fl_intersect) # get total acres within intersect polygons pclarea_inbuff_ft2 = 0 # total on-parcel acres within buffer lutype_intersect_ft2 = 0 # total acres of specified land use type within buffer with arcpy.da.SearchCursor(fl_intersect, ["SHAPE@AREA", p.col_lutype]) as cur: for row in cur: pclarea_inbuff_ft2 += row[0] if row[1] == lutype: lutype_intersect_ft2 += row[0] # get share of on-parcel land within buffer that is of specified land use type pct_lutype = lutype_intersect_ft2 / pclarea_inbuff_ft2 if pclarea_inbuff_ft2 > 0 else 0 # convert to acres buff_acre = pclarea_inbuff_ft2 / p.ft2acre lutype_intersect_acres = lutype_intersect_ft2 / p.ft2acre [ arcpy.Delete_management(item) for item in [fl_parcels, fl_project, fl_buff, fc_intersect, fl_intersect] ] # delete temp buffer feature class only if it's not the same as the project FC if fc_buff != fc_project: arcpy.Delete_management(fc_buff) return { 'total_net_pcl_acres': buff_acre, 'net_{}_acres'.format(lutype): lutype_intersect_acres, 'pct_{}_inbuff'.format(lutype): pct_lutype }