Esempio n. 1
0
def combine_spaces_fcs(community_specs):
    pp_c.log_debug('Combining spaces feature classes')
    communities = [c[0] for c in community_specs]
    community_fcs = [
        pp_c.get_community_fc_name(c, pp_c.COMMUNITY_SPACES_FC)
        for c in communities
    ]
    community_ids = [str(c[2]) for c in community_specs]

    out_fc = pp_c.SPACES_FC

    if not pp_c.IS_SCRATCH_OUTPUT_DATA:
        pp_c.log_debug(
            'Deleting existing features in combined spaces feature class')
        where = "%s IN (%s)" % (pp_c.SPACES_COMMUNITY_COL,
                                ','.join(community_ids))
        old_records = arcpy.SelectLayerByAttribute_management(
            out_fc, 'NEW_SELECTION', where)[0]
        arcpy.management.DeleteFeatures(old_records)

    if len(communities) > 10:
        pp_c.remove_indexes(out_fc, pp_c.SPACES_INDEX_SPEC)

    pp_c.log_info('Write to combined spaces feature class')
    arcpy.management.Append(community_fcs, out_fc)

    if len(communities) > 10:
        pp_c.add_indexes(out_fc, pp_c.SPACES_INDEX_SPEC)

    return
def run():
    pp_c.log_info("Logging to %s" % pp.logger.LOG_FILE)

    arcpy.env.overwriteOutput = True

    if os.path.isdir(pp_c.TEMP_DIR):
        shutil.rmtree(pp_c.TEMP_DIR)

    os.makedirs(pp_c.TEMP_DIR, exist_ok=True)
    os.makedirs(pp_c.COMMUNITIES_DIR, exist_ok=True)

    # Prepare the output databases
    for workspace in [
            pp_c.SPACES_GDB, pp_c.TREES_AND_STATS_GDB, pp_c.CANOPIES_GDB
    ]:
        if not arcpy.Exists(workspace):
            raise Exception("%s geodatabase does not exist" % (workspace))
        if pp_c.IS_SCRATCH_OUTPUT_DATA:
            arcpy.env.workspace = workspace
            pp_c.delete(arcpy.ListFeatureClasses())
        pp_c.create_domains(workspace, pp_c.DOMAIN_ASSIGNMENTS[workspace])

    # Prepare the output feature classes
    pp.spaces.prepare_fc()
    pp.trees.prepare_fc()
    pp.canopies.prepare_fc()
    pp.stats.prepare_fc()

    community_specs = __get_communities(pp_c.SUBSET_START_POINT,
                                        pp_c.SUBSET_COUNT, pp_c.SUBSET_LIST)

    if pp_c.PROCESSORS > 1:
        p = multiprocessing.Pool(pp_c.PROCESSORS)
        p.map(create_spaces_and_trees_and_canopies, community_specs, 1)
        p.close()
    else:
        # Process each community past the alphabetical starting point
        for community_spec in community_specs:
            create_spaces_and_trees_and_canopies(community_spec)

    if pp_c.IS_COMBINE_SPACES:
        pp.spaces.combine_spaces_fcs(community_specs)

    if pp_c.IS_COMBINE_TREES:
        pp.trees.combine_trees_fcs(community_specs)

    if pp_c.IS_COMBINE_CANOPIES:
        pp.canopies.combine_canopies_fcs(community_specs)

    pp.stats.combine_stats(community_specs)

    pp_c.log_info('Complete: %s' % ([c[0] for c in community_specs]))
    return
Esempio n. 3
0
def create_canopies(community_spec):
    try:

        # Process the input community
        community_name, acres, community_id = community_spec
        pp_c.log_info('Generating canopies', community_name)

        input_fc = pp_c.get_community_fc_name(community_name,
                                              pp_c.COMMUNITY_TREES_FC)
        output_fc = pp_c.get_community_fc_name(community_name,
                                               pp_c.COMMUNITY_CANOPIES_FC)

        intermediate_output_gdb = pp_c.prepare_intermediate_output_gdb(
            pp_c.USE_IN_MEM)
        trees_buffered = pp_c.get_intermediate_name(intermediate_output_gdb,
                                                    'tbuffered_int',
                                                    community_id,
                                                    pp_c.USE_IN_MEM)

        pp_c.log_debug('Populate the "radius" field', community_name)
        arcpy.management.CalculateField(
            input_fc, 'radius', "get_radius(!code!)", "PYTHON3",
            r"""def get_radius (code):
            if code == 0:
                return %1.2f
            elif code == 1:
                return %1.2f
            else:
                return %1.2f""" %
            (pp_c.TREE_RADIUS[pp_c.SMALL], pp_c.TREE_RADIUS[pp_c.MEDIUM],
             pp_c.TREE_RADIUS[pp_c.BIG]), "FLOAT")

        pp_c.log_debug('Buffer the points', community_name)
        arcpy.analysis.Buffer(input_fc, trees_buffered, "radius", "FULL",
                              "ROUND", "NONE", None, "PLANAR")

        arcpy.management.DeleteField(input_fc, 'radius')

        pp_c.log_debug('Writing community canopies', community_name)
        sr = arcpy.Describe(pp_c.CANOPIES_TEMPLATE_FC).spatialReference
        arcpy.CreateFeatureclass_management(os.path.dirname(output_fc),
                                            os.path.basename(output_fc),
                                            'POLYGON',
                                            pp_c.CANOPIES_TEMPLATE_FC,
                                            "DISABLED", "DISABLED", sr)
        arcpy.management.Append(trees_buffered, output_fc, "NO_TEST")

        pp_c.delete([trees_buffered])

    except Exception as ex:
        pp_c.log_debug('Exception: %s' % (str(ex)))
        raise ex
def create_spaces_and_trees_and_canopies(community_spec):
    try:
        arcpy.env.outputZFlag = "Disabled"
        arcpy.env.outputMFlag = "Disabled"
        arcpy.overwriteOutput = True

        __prepare_community_gdb(community_spec)

        if pp_c.IS_CREATE_SPACES:
            pp.spaces.find_spaces(community_spec)

        if pp_c.IS_CREATE_TREES:
            pp.trees.site_trees(community_spec)

        if pp_c.IS_CREATE_CANOPIES:
            pp.canopies.create_canopies(community_spec)

        pp_c.log_info('Complete', community_spec[0])
        return

    except Exception as ex:
        pp_c.log_debug('Exception: %s' % (str(ex)))
        raise ex
Esempio n. 5
0
def site_trees(community_spec):

    community_name, acres, community_id = community_spec
    pp_c.log_info('Siting trees.', community_name)

    size_stats = [0, 0, 0]
    landuse_stats = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    public_private_stats = [0, 0]

    input_fc = pp_c.get_community_fc_name(community_spec[0],
                                          pp_c.COMMUNITY_SPACES_FC)
    output_fc = pp_c.get_community_fc_name(community_spec[0],
                                           pp_c.COMMUNITY_TREES_FC)

    if arcpy.Exists(output_fc):
        arcpy.Delete_management(output_fc)
    arcpy.CreateFeatureclass_management(os.path.dirname(output_fc),
                                        os.path.basename(output_fc), "POINT",
                                        pp_c.TREES_TEMPLATE_FC, "DISABLED",
                                        "DISABLED", pp_c.TREES_TEMPLATE_FC)

    intermediate_output_gdb = pp_c.prepare_intermediate_output_gdb(
        pp_c.USE_IN_MEM)
    intermediate_trees = pp_c.get_intermediate_name(intermediate_output_gdb,
                                                    'trees_int', community_id,
                                                    pp_c.USE_IN_MEM)
    intermediate_trees_lu = pp_c.get_intermediate_name(intermediate_output_gdb,
                                                       'tlu_int', community_id,
                                                       pp_c.USE_IN_MEM)
    intermediate_trees_lu_public = pp_c.get_intermediate_name(
        intermediate_output_gdb, 'tlpub_int', community_id, pp_c.USE_IN_MEM)

    arcpy.CreateFeatureclass_management(os.path.dirname(intermediate_trees),
                                        os.path.basename(intermediate_trees),
                                        "POINT", pp_c.TREES_TEMPLATE_FC,
                                        "DISABLED", "DISABLED",
                                        pp_c.TREES_TEMPLATE_FC)
    arcpy.DeleteField_management(intermediate_trees, ['land_use'])

    community_stats_tbl = pp.stats.prepare_community_stats_tbl(
        community_name, community_id, pp_c.COMMUNITY_TREE_STATS_TBL,
        pp_c.TREE_STATS_SPEC)

    if WRITE_TO_DEBUG_MESH_FC:
        arcpy.management.DeleteFeatures(MESH_FC)

    pp_c.log_info("Calculating points", community_name)
    query = "Shape_Area > 2.5"
    with arcpy.da.SearchCursor(input_fc,
                               ['OBJECTID', 'SHAPE@', 'community_id'],
                               query) as cursor:
        for oid, polygon, community in cursor:
            x_min, y_min, x_max, y_max = polygon.extent.XMin, polygon.extent.YMin, polygon.extent.XMax, polygon.extent.YMax

            center = arcpy.Point((x_min + x_max) / 2, (y_min + y_max) / 2)
            tiers = math.ceil(
                max((x_max - x_min) / 2, (y_max - y_min) / 2) / MIN_DIAMETER)

            # The mesh orgin is the NW corner and indexed row major as  [row][col]
            mesh_row_dim, mesh_col_dim = __get_mesh_dim(polygon, center, tiers)
            nw_corner = arcpy.Point(
                center.X - (mesh_col_dim * MIN_DIAMETER) / 2,
                center.Y + (mesh_row_dim * MIN_DIAMETER) / 2)
            center_row, center_col = __point_to_mesh(center, nw_corner)

            mesh_type = __get_mesh_algorithm(mesh_row_dim, mesh_col_dim,
                                             polygon)
            if mesh_type == MESH_ALGORITHM_SMALL:
                mesh = [m[:] for m in [[VACANT] * mesh_col_dim] * mesh_row_dim]
            elif mesh_type == MESH_ALGORITHM_BIG:
                mesh = __get_mesh(mesh_row_dim, mesh_col_dim, polygon,
                                  nw_corner, input_fc)

            plant_points = dict()

            for tree_category in TREE_CATEGORIES:
                for tier_idx in range(0, tiers + 1):
                    for row, col in __get_tier_vacancies(
                            center_row, center_col, tier_idx, mesh,
                            mesh_row_dim, mesh_col_dim):
                        fp = __get_footprint(row, col,
                                             TREE_FOOTPRINT_DIM[tree_category],
                                             mesh_row_dim, mesh_col_dim)
                        if __is_footprint_clean(mesh, *fp):
                            if is_point_in_polygon(row, col, polygon,
                                                   nw_corner, mesh, mesh_type,
                                                   plant_points):
                                __occupy_footprint(mesh, *fp, row, col,
                                                   tree_category)

            with arcpy.da.InsertCursor(
                    intermediate_trees,
                ['SHAPE@', 'code', 'p_oid', 'community_id']) as cursor:
                for row, col in plant_points.keys():
                    cursor.insertRow([
                        plant_points[(row, col)], mesh[row][col], oid,
                        community
                    ])

            if WRITE_TO_DEBUG_MESH_FC:
                with arcpy.da.InsertCursor(
                        MESH_FC,
                    ['SHAPE@', 'code', 'row', 'col', 'x', 'y', 'dim'
                     ]) as cursor:
                    for r in range(0, mesh_row_dim):
                        for c in range(0, mesh_col_dim):
                            p = __mesh_to_point(r, c, nw_corner)
                            cursor.insertRow(
                                [p, mesh[r][c], r, c, p.X, p.Y, mesh_row_dim])

    pp_c.log_debug('Identify land use', community_name)
    arcpy.Identity_analysis(intermediate_trees, pp_c.LAND_USE_2015,
                            intermediate_trees_lu, "ALL", "",
                            "NO_RELATIONSHIPS")
    pp_c.delete([intermediate_trees])
    arcpy.management.AlterField(intermediate_trees_lu, 'LandUse', 'land_use')

    pp_c.log_debug('Identify public land', community_name)
    arcpy.Identity_analysis(intermediate_trees_lu, pp_c.PUBLIC_LAND,
                            intermediate_trees_lu_public, "ONLY_FID", "",
                            "NO_RELATIONSHIPS")
    pp_c.delete([intermediate_trees_lu])

    pp_c.log_debug('Populate the "is_public" field', community_name)
    arcpy.management.CalculateField(
        intermediate_trees_lu_public, pp_c.SPACES_PUBLIC_PRIVATE_COL,
        "is_public(!FID_%s!)" % (os.path.basename(pp_c.PUBLIC_LAND)),
        "PYTHON3", r"""def is_public (fid):
        if fid == -1:
            return 0
        else:
            return 1""", "SHORT")

    # __downsize (intermediate_output_gdb, intermediate_trees_lu_public, community_name, community_id)

    pp_c.log_debug('Find overlaps', community_name)
    overlap_oids = __find_overlaps(intermediate_output_gdb,
                                   intermediate_trees_lu_public,
                                   community_name, community_id)

    pp_c.log_debug(
        'Collecting tree statistics, fixing bad land uses, and downsizing overlaps',
        community_name)
    big_to_medium, medium_to_small, small = 0, 0, 0
    with arcpy.da.UpdateCursor(intermediate_trees_lu_public, [
            'objectid',
            'code',
            'land_use',
            'is_public',
    ]) as cursor:
        for oid, tree_size, land_use, is_public in cursor:
            if land_use not in pp_c.LANDUSE_DOMAIN.values():
                # Fix up unrecognized land use
                land_use = pp_c.LANDUSE_DOMAIN['Other']
                cursor.updateRow([oid, tree_size, land_use, is_public])
            if oid in overlap_oids:
                if tree_size == BIG:
                    tree_size = MEDIUM
                    big_to_medium = big_to_medium + 1
                    cursor.updateRow([oid, tree_size, land_use, is_public])
                elif tree_size == MEDIUM:
                    tree_size = SMALL
                    medium_to_small = medium_to_small + 1
                    cursor.updateRow([oid, tree_size, land_use, is_public])
                else:
                    tree_size = SMALL
                    small = small + 1
            size_stats[tree_size] = size_stats[tree_size] + 1
            landuse_stats[land_use] = landuse_stats[land_use] + 1
            public_private_stats[
                is_public] = public_private_stats[is_public] + 1
    pp_c.log_debug(
        "Updated feature class with new sizes. L->M=%i, M->S=%i, S=%i" %
        (big_to_medium, medium_to_small, small), community_name)

    pp_c.log_debug("Writing points to '%s'" % output_fc, community_name)
    arcpy.management.Append(intermediate_trees_lu_public, output_fc, "NO_TEST")
    pp_c.delete([intermediate_trees_lu_public])

    pp.stats.update_stats(
        community_stats_tbl, community_id,
        size_stats + landuse_stats[1:] + public_private_stats,
        pp_c.TREE_STATS_SPEC)

    return
Esempio n. 6
0
def find_spaces(community_spec):
    try:

        # Process the input community
        community, acres, idx = community_spec
        pp_c.log_info('Finding spaces. %i acres' % (acres), community)

        use_in_mem = pp_c.USE_IN_MEM if community not in PROBLEM_COMMUNITIES else False

        intermediate_output_gdb = pp_c.prepare_intermediate_output_gdb(
            use_in_mem)

        canopy_clipped = pp_c.get_intermediate_name(intermediate_output_gdb,
                                                    'canopy_clipped', idx,
                                                    use_in_mem)
        plantable_region_clipped = pp_c.get_intermediate_name(
            intermediate_output_gdb, 'plantable_region_clipped', idx,
            use_in_mem)
        buildings_clipped = pp_c.get_intermediate_name(intermediate_output_gdb,
                                                       'buildings_clipped',
                                                       idx, use_in_mem)
        minus_trees = pp_c.get_intermediate_name(intermediate_output_gdb,
                                                 'minus_trees', idx,
                                                 use_in_mem)
        minus_trees_buildings = pp_c.get_intermediate_name(
            intermediate_output_gdb, 'minus_trees_buildings', idx, use_in_mem)
        plantable_poly = pp_c.get_intermediate_name(intermediate_output_gdb,
                                                    'plantable_poly', idx,
                                                    use_in_mem)
        plantable_single_poly = pp_c.get_intermediate_name(
            intermediate_output_gdb, 'plantable_single_poly', idx, use_in_mem)
        plantable_muni = pp_c.get_intermediate_name(intermediate_output_gdb,
                                                    'plantable_muni', idx,
                                                    use_in_mem)

        community_fc = pp_c.get_community_fc_name(community,
                                                  pp_c.COMMUNITY_SPACES_FC)
        pp_c.delete([community_fc])

        pp_c.log_debug('Getting community boundary', community)
        community_boundary = arcpy.SelectLayerByAttribute_management(
            pp_c.MUNI_COMMUNITY_AREA, 'NEW_SELECTION',
            "COMMUNITY = '%s'" % (community))[0]

        pp_c.log_debug(
            'Clipping %s' % (os.path.basename(pp_c.CANOPY_EXPAND_TIF)),
            community)
        arcpy.management.Clip(pp_c.CANOPY_EXPAND_TIF,
                              '#',
                              canopy_clipped,
                              community_boundary,
                              nodata_value='',
                              clipping_geometry="ClippingGeometry",
                              maintain_clipping_extent="MAINTAIN_EXTENT")

        pp_c.log_debug(
            'Clipping %s' % (os.path.basename(pp_c.PLANTABLE_REGION_TIF)),
            community)
        arcpy.management.Clip(pp_c.PLANTABLE_REGION_TIF,
                              '#',
                              plantable_region_clipped,
                              community_boundary,
                              clipping_geometry="ClippingGeometry",
                              maintain_clipping_extent="MAINTAIN_EXTENT")

        pp_c.log_debug('Removing trees', community)
        arcpy.gp.RasterCalculator_sa(
            'Con(IsNull("%s"), "%s")' %
            (canopy_clipped, plantable_region_clipped), minus_trees)
        pp_c.delete([canopy_clipped, plantable_region_clipped])

        pp_c.log_debug(
            'Clipping %s' % (os.path.basename(pp_c.BUILDINGS_EXPAND_TIF)),
            community)
        arcpy.management.Clip(pp_c.BUILDINGS_EXPAND_TIF,
                              '#',
                              buildings_clipped,
                              community_boundary,
                              clipping_geometry="ClippingGeometry",
                              maintain_clipping_extent="MAINTAIN_EXTENT")

        pp_c.log_debug('Removing buildings', community)
        arcpy.gp.RasterCalculator_sa(
            'Con(IsNull("%s"), "%s")' % (buildings_clipped, minus_trees),
            minus_trees_buildings)
        pp_c.delete([buildings_clipped, minus_trees, community_boundary])

        pp_c.log_debug('Converting raster to polygon', community)
        arcpy.RasterToPolygon_conversion(minus_trees_buildings, plantable_poly,
                                         "SIMPLIFY", "", "SINGLE_OUTER_PART",
                                         "")
        pp_c.delete([minus_trees_buildings])

        pp_c.log_debug('Repair invalid features', community)
        arcpy.management.RepairGeometry(plantable_poly, "DELETE_NULL", "ESRI")

        pp_c.log_debug('Converting multipart polygons to singlepart',
                       community)
        arcpy.MultipartToSinglepart_management(plantable_poly,
                                               plantable_single_poly)
        pp_c.delete([plantable_poly])

        pp_c.log_debug('Spatial join', community)
        arcpy.SpatialJoin_analysis(plantable_single_poly,
                                   pp_c.MUNI_COMMUNITY_AREA, plantable_muni,
                                   "JOIN_ONE_TO_ONE", "KEEP_ALL", "",
                                   "INTERSECT", "", "")
        pp_c.delete([plantable_single_poly])

        pp_c.log_debug('Add and populate the "CommunityID" field', community)
        arcpy.management.CalculateField(plantable_muni,
                                        pp_c.SPACES_COMMUNITY_COL,
                                        '%i' % (idx), "PYTHON3", "", "SHORT")

        __save_community_spaces(plantable_muni, community_fc)
        pp_c.delete([plantable_muni])

    except Exception as ex:
        pp_c.log_debug('Exception: %s' % (str(ex)))
        raise ex

    return community_fc