Esempio n. 1
0
def points_to_multipoints(
    dataset_path: Union[Path, str],
    *,
    output_path: Union[Path, str],
    dataset_where_sql: Optional[str] = None,
    log_level: int = logging.INFO,
) -> Counter:
    """Convert geometry from points to multipoints.

    Args:
        dataset_path: Path to dataset.
        output_path: Path to output dataset.
        dataset_where_sql: SQL where-clause for dataset subselection.
        log_level: Level to log the function at.

    Returns:
        Feature counts for original and output datasets.
    """
    dataset_path = Path(dataset_path)
    output_path = Path(output_path)
    LOG.log(
        log_level,
        "Start: Convert points in `%s` to multipoints in output `%s`.",
        dataset_path,
        output_path,
    )
    _dataset = Dataset(dataset_path)
    # ArcPy2.8.0: Convert Path to str (2x).
    arcpy.management.CreateFeatureclass(
        out_path=str(output_path.parent),
        out_name=output_path.name,
        geometry_type="MULTIPOINT",
        template=str(dataset_path),
        spatial_reference=_dataset.spatial_reference.object,
    )
    field_names = _dataset.user_field_names + ["SHAPE@"]
    # ArcPy2.8.0: Convert Path to str.
    multipoint_cursor = arcpy.da.InsertCursor(in_table=str(output_path),
                                              field_names=field_names)
    # ArcPy2.8.0: Convert Path to str.
    point_cursor = arcpy.da.SearchCursor(
        in_table=str(dataset_path),
        field_names=field_names,
        where_clause=dataset_where_sql,
    )
    states = Counter()
    states["in original dataset"] = dataset.feature_count(dataset_path)
    with multipoint_cursor, point_cursor:
        for point_feature in point_cursor:
            multipoint_geometry = arcpy.Multipoint(
                point_feature[-1].firstPoint)
            multipoint_feature = point_feature[:-1] + (multipoint_geometry, )
            multipoint_cursor.insertRow(multipoint_feature)
    states["in output"] = dataset.feature_count(output_path)
    log_entity_states("features", states, logger=LOG, log_level=log_level)
    LOG.log(log_level, "End: Convert.")
    return states
Esempio n. 2
0
def table_to_points(
    dataset_path: Union[Path, str],
    *,
    x_field_name: str,
    y_field_name: str,
    z_field_name: Optional[str] = None,
    output_path: Union[Path, str],
    dataset_where_sql: Optional[str] = None,
    spatial_reference_item: SpatialReferenceSourceItem = 4326,
    log_level: int = logging.INFO,
) -> Counter:
    """Convert coordinate table to a new point dataset.

    Args:
        dataset_path: Path to dataset.
        output_path: Path to output dataset.
        dataset_where_sql: SQL where-clause for dataset subselection.
        x_field_name: Name of field with x-coordinate.
        y_field_name: Name of field with y-coordinate.
        z_field_name: Name of field with z-coordinate.
        spatial_reference_item: Item from which the spatial reference of the output
            geometry will be derived. Default is 4326 (EPSG code for unprojected WGS84).
        log_level: Level to log the function at.

    Returns:
        Feature counts for original and output datasets.
    """
    dataset_path = Path(dataset_path)
    output_path = Path(output_path)
    LOG.log(
        log_level,
        "Start: Convert table rows `%s` to points in output `%s`.",
        dataset_path,
        output_path,
    )
    layer_name = unique_name()
    states = Counter()
    states["in original dataset"] = dataset.feature_count(dataset_path)
    view = DatasetView(dataset_path, dataset_where_sql=dataset_where_sql)
    arcpy.management.MakeXYEventLayer(
        table=view.name,
        out_layer=layer_name,
        in_x_field=x_field_name,
        in_y_field=y_field_name,
        in_z_field=z_field_name,
        spatial_reference=SpatialReference(spatial_reference_item).object,
    )
    dataset.copy(layer_name, output_path=output_path, log_level=logging.DEBUG)
    arcpy.management.Delete(layer_name)
    states["in output"] = dataset.feature_count(output_path)
    log_entity_states("features", states, logger=LOG, log_level=log_level)
    LOG.log(log_level, "End: Convert.")
    return states
Esempio n. 3
0
def project(
    dataset_path: Union[Path, str],
    *,
    output_path: Union[Path, str],
    dataset_where_sql: Optional[str] = None,
    spatial_reference_item: SpatialReferenceSourceItem = 4326,
    log_level: int = logging.INFO,
) -> Counter:
    """Project dataset features to a new dataset.

    Args:
        dataset_path: Path to dataset.
        output_path: Path to output dataset.
        dataset_where_sql: SQL where-clause for dataset subselection.
        spatial_reference_item: Item from which the spatial reference of the output
            geometry will be derived. Default is 4326 (EPSG code for unprojected WGS84).
        log_level: Level to log the function at.

    Returns:
        Feature counts for original and output datasets.
    """
    dataset_path = Path(dataset_path)
    output_path = Path(output_path)
    spatial_reference = SpatialReference(spatial_reference_item)
    LOG.log(
        log_level,
        "Start: Project `%s` to %s in output `%s`.",
        dataset_path,
        spatial_reference.name,
        output_path,
    )
    _dataset = Dataset(dataset_path)
    states = Counter()
    states["in original dataset"] = dataset.feature_count(dataset_path)
    # Project tool ignores view selections, so we create empty output & insert features.
    dataset.create(
        dataset_path=output_path,
        field_metadata_list=_dataset.user_fields,
        geometry_type=_dataset.geometry_type,
        spatial_reference_item=spatial_reference,
        log_level=logging.DEBUG,
    )
    features.insert_from_path(
        output_path,
        field_names=_dataset.user_field_names,
        source_path=dataset_path,
        source_where_sql=dataset_where_sql,
        log_level=logging.DEBUG,
    )
    states["in output"] = dataset.feature_count(output_path)
    log_entity_states("features", states, logger=LOG, log_level=log_level)
    LOG.log(log_level, "End: Project.")
    return states
Esempio n. 4
0
def erase_features(
    dataset_path: Union[Path, str],
    *,
    erase_path: Union[Path, str],
    dataset_where_sql: Optional[str] = None,
    erase_where_sql: Optional[str] = None,
    output_path: Union[Path, str],
    log_level: int = logging.INFO,
) -> Counter:
    """Erase feature geometry where it overlaps erase-dataset geometry.

    Args:
        dataset_path: Path to dataset.
        erase_path: Path to erase-dataset.
        dataset_where_sql: SQL where-clause for dataset subselection.
        erase_where_sql: SQL where-clause for erase-dataset subselection.
        output_path: Path to output dataset.
        log_level: Level to log the function at.

    Returns:
        Feature counts for original and output datasets.
    """
    dataset_path = Path(dataset_path)
    erase_path = Path(erase_path)
    output_path = Path(output_path)
    LOG.log(
        log_level,
        "Start: Erase features in `%s` where overlapping features in `%s`.",
        dataset_path,
        erase_path,
    )
    states = Counter()
    states["in original dataset"] = dataset.feature_count(dataset_path)
    view = DatasetView(dataset_path, dataset_where_sql=dataset_where_sql)
    erase_view = DatasetView(erase_path,
                             field_names=[],
                             dataset_where_sql=erase_where_sql)
    with view, erase_view:
        # ArcPy2.8.0: Convert Path to str.
        arcpy.analysis.Erase(
            in_features=view.name,
            erase_features=erase_view.name,
            out_feature_class=str(output_path),
        )
    states["in output"] = dataset.feature_count(output_path)
    log_entity_states("features", states, logger=LOG, log_level=log_level)
    LOG.log(log_level, "End: Erase.")
    return states
Esempio n. 5
0
def delete(
    dataset_path: Union[Path, str],
    *,
    dataset_where_sql: Optional[str] = None,
    use_edit_session: bool = False,
    log_level: int = logging.INFO,
) -> Counter:
    """Delete features in dataset.

    Args:
        dataset_path: Path to dataset.
        dataset_where_sql: SQL where-clause for dataset subselection.
        use_edit_session: True if edits are to be made in an edit session.
        log_level: Level to log the function at.

    Returns:
        Feature counts for each delete-state.
    """
    dataset_path = Path(dataset_path)
    LOG.log(log_level, "Start: Delete features from `%s`.", dataset_path)
    session = Editing(Dataset(dataset_path).workspace_path, use_edit_session)
    states = Counter()
    view = DatasetView(dataset_path, dataset_where_sql=dataset_where_sql)
    with view, session:
        states["deleted"] = view.count
        arcpy.management.DeleteRows(in_rows=view.name)
        states["remaining"] = dataset.feature_count(dataset_path)
    log_entity_states("features", states, logger=LOG, log_level=log_level)
    LOG.log(log_level, "End: Delete.")
    return states
Esempio n. 6
0
def clip(
    dataset_path: Union[Path, str],
    *,
    clip_path: Union[Path, str],
    dataset_where_sql: Optional[str] = None,
    clip_where_sql: Optional[str] = None,
    output_path: Union[Path, str],
    log_level: int = logging.INFO,
) -> Counter:
    """Clip feature geometry where it overlaps clip-dataset geometry.

    Args:
        dataset_path: Path to dataset.
        clip_path: Path to clip-dataset.
        dataset_where_sql: SQL where-clause for dataset subselection.
        clip_where_sql: SQL where-clause for clip-dataset subselection.
        output_path: Path to output dataset.
        log_level: Level to log the function at.

    Returns:
        Feature counts for original and output datasets.
    """
    clip_path = Path(clip_path)
    dataset_path = Path(dataset_path)
    output_path = Path(output_path)
    LOG.log(
        log_level,
        "Start: Clip features in `%s` where overlapping `%s`.",
        dataset_path,
        clip_path,
    )
    states = Counter()
    states["in original dataset"] = dataset.feature_count(dataset_path)
    view = DatasetView(dataset_path, dataset_where_sql=dataset_where_sql)
    clip_view = DatasetView(clip_path, dataset_where_sql=clip_where_sql)
    with view, clip_view:
        # ArcPy2.8.0: Convert Path to str.
        arcpy.analysis.Clip(
            in_features=view.name,
            clip_features=clip_view.name,
            out_feature_class=str(output_path),
        )
    states["in output"] = dataset.feature_count(output_path)
    log_entity_states("features", states, logger=LOG, log_level=log_level)
    LOG.log(log_level, "End: Clip.")
    return states
Esempio n. 7
0
def planarize(
    dataset_path: Union[Path, str],
    *,
    output_path: Union[Path, str],
    dataset_where_sql: Optional[str] = None,
    log_level: int = logging.INFO,
) -> Counter:
    """Planarize feature geometry into lines.

    Note:
        This method does not make topological linework. However it does carry all
        attributes with it, rather than just an ID attribute.

        Since this method breaks the new line geometry at intersections, it can be
        useful to break line geometry features that cross.

    Args:
        dataset_path: Path to dataset.
        output_path: Path to output dataset.
        dataset_where_sql: SQL where-clause for dataset subselection.
        log_level: Level to log the function at.

    Returns:
        Feature counts for original and output datasets.
    """
    dataset_path = Path(dataset_path)
    output_path = Path(output_path)
    LOG.log(
        log_level,
        "Start: Planarize geometry in `%s` to lines in output `%s`.",
        dataset_path,
        output_path,
    )
    states = Counter()
    states["in original dataset"] = dataset.feature_count(dataset_path)
    view = DatasetView(dataset_path, dataset_where_sql=dataset_where_sql)
    with view:
        # ArcPy2.8.0: Convert Path to str.
        arcpy.management.FeatureToLine(in_features=view.name,
                                       out_feature_class=str(output_path),
                                       attributes=True)
    states["in output"] = dataset.feature_count(output_path)
    log_entity_states("features", states, logger=LOG, log_level=log_level)
    LOG.log(log_level, "End: Planarize.")
    return states
Esempio n. 8
0
def lines_to_vertex_points(
    dataset_path: Union[Path, str],
    *,
    output_path: Union[Path, str],
    dataset_where_sql: Optional[str] = None,
    endpoints_only: bool = False,
    log_level: int = logging.INFO,
) -> Counter:
    """Convert geometry from lines to points at every vertex.

    Args:
        dataset_path: Path to dataset.
        output_path: Path to output dataset.
        dataset_where_sql: SQL where-clause for dataset subselection.
        endpoints_only: Output points should include line endpoints only if True.
        log_level: Level to log the function at.

    Returns:
        Feature counts for original and output datasets.
    """
    dataset_path = Path(dataset_path)
    output_path = Path(output_path)
    LOG.log(
        log_level,
        "Start: Convert lines in `%s` to vertex points in output `%s`.",
        dataset_path,
        output_path,
    )
    states = Counter()
    states["in original dataset"] = dataset.feature_count(dataset_path)
    view = DatasetView(dataset_path, dataset_where_sql=dataset_where_sql)
    with view:
        arcpy.management.FeatureVerticesToPoints(
            in_features=view.name,
            # ArcPy2.8.0: Convert Path to str.
            out_feature_class=str(output_path),
            point_location="ALL" if not endpoints_only else "BOTH_ENDS",
        )
    dataset.delete_field(output_path,
                         field_name="ORIG_FID",
                         log_level=logging.DEBUG)
    states["in output"] = dataset.feature_count(output_path)
    log_entity_states("features", states, logger=LOG, log_level=log_level)
    LOG.log(log_level, "End: Convert.")
    return states
Esempio n. 9
0
def delete_by_id(
    dataset_path: Union[Path, str],
    delete_ids: Iterable[Union[Sequence[Any], Any]],
    id_field_names: Iterable[str],
    use_edit_session: bool = False,
    log_level: int = logging.INFO,
) -> Counter:
    """Delete features in dataset with given IDs.

    Note:
        There is no guarantee that the ID field(s) are unique.
        Use ArcPy cursor token names for object IDs and geometry objects/properties.

    Args:
        dataset_path: Path to dataset.
        delete_ids: ID sequences for features to delete. If id_field_names contains only
            one field, IDs may be provided as non-sequence single-value.
        id_field_names: Names of the feature ID fields.
        use_edit_session: True if edits are to be made in an edit session.
        log_level: Level to log the function at.

    Returns:
        Feature counts for each delete-state.
    """
    dataset_path = Path(dataset_path)
    LOG.log(log_level, "Start: Delete features in `%s` with given IDs.",
            dataset_path)
    id_field_names = list(id_field_names)
    if inspect.isgeneratorfunction(delete_ids):
        delete_ids = delete_ids()
    ids = set()
    for _id in delete_ids:
        if isinstance(_id, Iterable) and not isinstance(_id, str):
            ids.add(tuple(_id))
        else:
            ids.add((_id, ))
    states = Counter()
    if ids:
        # ArcPy2.8.0: Convert Path to str.
        cursor = arcpy.da.UpdateCursor(str(dataset_path),
                                       field_names=id_field_names)
        session = Editing(
            Dataset(dataset_path).workspace_path, use_edit_session)
        with session, cursor:
            for row in cursor:
                _id = tuple(row)
                if _id in ids:
                    cursor.deleteRow()
                    states["deleted"] += 1
    else:
        LOG.log(log_level, "No IDs provided.")
        states["deleted"] = 0
    states["unchanged"] = dataset.feature_count(dataset_path)
    log_entity_states("features", states, logger=LOG, log_level=log_level)
    LOG.log(log_level, "End: Delete.")
    return states
Esempio n. 10
0
def split_lines_at_vertices(
    dataset_path: Union[Path, str],
    *,
    output_path: Union[Path, str],
    dataset_where_sql: Optional[str] = None,
    log_level: int = logging.INFO,
) -> Counter:
    """Split lines into smaller lines between vertices.

    The original datasets can be lines or polygons. Polygons will be split along their
    rings.

    Args:
        dataset_path: Path to dataset.
        output_path: Path to output dataset.
        dataset_where_sql: SQL where-clause for dataset subselection.
        log_level: Level to log the function at.

    Returns:
        Feature counts for original and output datasets.
    """
    dataset_path = Path(dataset_path)
    output_path = Path(output_path)
    LOG.log(
        log_level,
        "Start: Split line geometry in `%s` into lines between vertices in output `%s`.",
        dataset_path,
        output_path,
    )
    states = Counter()
    states["in original dataset"] = dataset.feature_count(dataset_path)
    view = DatasetView(dataset_path, dataset_where_sql=dataset_where_sql)
    with view:
        # ArcPy2.8.0: Convert Path to str.
        arcpy.management.SplitLine(in_features=view.name,
                                   out_feature_class=str(output_path))
    states["in output"] = dataset.feature_count(output_path)
    log_entity_states("features", states, logger=LOG, log_level=log_level)
    LOG.log(log_level, "End: Split.")
    return states
Esempio n. 11
0
def points_to_thiessen_polygons(
    dataset_path: Union[Path, str],
    *,
    output_path: Union[Path, str],
    dataset_where_sql: Optional[str] = None,
    log_level: int = logging.INFO,
) -> Counter:
    """Convert geometry from points to Thiessen polygons.

    Args:
        dataset_path: Path to dataset.
        output_path: Path to output dataset.
        dataset_where_sql: SQL where-clause for dataset subselection.
        log_level: Level to log the function at.

    Returns:
        Feature counts for original and output datasets.
    """
    dataset_path = Path(dataset_path)
    output_path = Path(output_path)
    LOG.log(
        log_level,
        "Start: Convert points in `%s` to Thiessen polygons in output `%s`.",
        dataset_path,
        output_path,
    )
    states = Counter()
    states["in original dataset"] = dataset.feature_count(dataset_path)
    view = DatasetView(dataset_path, dataset_where_sql=dataset_where_sql)
    with view:
        # ArcPy2.8.0: Convert Path to str.
        arcpy.analysis.CreateThiessenPolygons(
            in_features=view.name,
            out_feature_class=str(output_path),
            fields_to_copy="ALL",
        )
    states["in output"] = dataset.feature_count(output_path)
    log_entity_states("features", states, logger=LOG, log_level=log_level)
    LOG.log(log_level, "End: Convert.")
    return states
Esempio n. 12
0
def buffer(
    dataset_path: Union[Path, str],
    *,
    dataset_where_sql: Optional[str] = None,
    output_path: Union[Path, str],
    distance: Union[float, int],
    log_level: int = logging.INFO,
) -> Counter:
    """Buffer features a given distance & (optionally) dissolve on given fields.

    Args:
        dataset_path: Path to dataset.
        dataset_where_sql: SQL where-clause for dataset subselection.
        output_path: Path to output dataset.
        distance: Distance to buffer from feature, in the units of the dataset.
        log_level: Level to log the function at.

    Returns:
        Feature counts for original and output datasets.
    """
    dataset_path = Path(dataset_path)
    output_path = Path(output_path)
    LOG.log(log_level, "Start: Buffer features in `%s`.", dataset_path)
    states = Counter()
    states["in original dataset"] = dataset.feature_count(dataset_path)
    view = DatasetView(dataset_path, dataset_where_sql=dataset_where_sql)
    with view:
        # ArcPy2.8.0: Convert Path to str.
        arcpy.analysis.Buffer(
            in_features=view.name,
            out_feature_class=str(output_path),
            buffer_distance_or_field=distance,
        )
    for field_name in ["BUFF_DIST", "ORIG_FID"]:
        dataset.delete_field(output_path, field_name=field_name)
    states["in output"] = dataset.feature_count(output_path)
    log_entity_states("features", states, logger=LOG, log_level=log_level)
    LOG.log(log_level, "End: Buffer.")
    return states
Esempio n. 13
0
def union(
    dataset_path: Union[Path, str],
    *,
    field_name: str,
    union_path: Union[Path, str],
    union_field_name: str,
    dataset_where_sql: Optional[str] = None,
    union_where_sql: Optional[str] = None,
    output_path: Union[Path, str],
    replacement_value: Optional[Any] = None,
    log_level: int = logging.INFO,
) -> Counter:
    """Assign union attributes.

    Notes:
        Features with multiple union-features will be split.

    Args:
        dataset_path: Path to dataset.
        field_name: Name of field to place union values.
        union_path: Path to union-dataset.
        union_field_name: Name of union-field.
        dataset_where_sql: SQL where-clause for dataset subselection.
        union_where_sql: SQL where-clause for the union-dataset subselection.
        replacement_value: Value to replace a present union-field value with. If set to
            None, no replacement will occur.
        log_level: Level to log the function at.

    Returns:
        Feature counts for original and output datasets.
    """
    dataset_path = Path(dataset_path)
    union_path = Path(union_path)
    LOG.log(
        log_level,
        "Start: Union-set attributes in `%s.%s` by features/values in `%s.%s`.",
        dataset_path,
        field_name,
        union_path,
        union_field_name,
    )
    states = Counter()
    states["in original dataset"] = dataset.feature_count(dataset_path)
    view = DatasetView(dataset_path, dataset_where_sql=dataset_where_sql)
    # Do not include any field names - we do not want them added to output.
    union_view = DatasetView(union_path,
                             field_names=[],
                             dataset_where_sql=union_where_sql)
    with view, union_view:
        # ArcPy2.8.0: Convert Path to str.
        arcpy.analysis.Union(
            in_features=[view.name, union_view.name],
            out_feature_class=str(output_path),
            join_attributes="ALL",
        )
    fid_field_names = [
        name for name in Dataset(output_path).field_names
        if name.startswith("FID_")
    ]
    if replacement_value is not None:
        attributes.update_by_value(
            output_path,
            field_name,
            value=replacement_value,
            dataset_where_sql=f"{fid_field_names[-1]} <> -1",
            log_level=logging.DEBUG,
        )
    else:
        attributes.update_by_joined_value(
            output_path,
            field_name,
            key_field_names=[fid_field_names[-1]],
            join_dataset_path=union_path,
            join_field_name=union_field_name,
            join_key_field_names=["OID@"],
            dataset_where_sql=f"{fid_field_names[-1]} <> -1",
            join_dataset_where_sql=union_where_sql,
            log_level=logging.DEBUG,
        )
    attributes.update_by_value(
        output_path,
        field_name,
        value=None,
        dataset_where_sql=f"{fid_field_names[-1]} = -1",
        log_level=logging.DEBUG,
    )
    for name in fid_field_names:
        dataset.delete_field(output_path,
                             field_name=name,
                             log_level=logging.DEBUG)
    states["in output"] = dataset.feature_count(output_path)
    log_entity_states("features", states, logger=LOG, log_level=log_level)
    LOG.log(log_level, "End: Union.")
    return states
Esempio n. 14
0
def spatial_join_by_center(
    dataset_path: Union[Path, str],
    *,
    field_name: str,
    join_path: Union[Path, str],
    join_field_name: str,
    dataset_where_sql: Optional[str] = None,
    join_where_sql: Optional[str] = None,
    output_path: Union[Path, str],
    replacement_value: Optional[Any] = None,
    log_level: int = logging.INFO,
) -> Counter:
    """Spatially-join attributes by their center.

    Notes:
        Features joined with multiple join-features will be duplicated.

    Args:
        dataset_path: Path to dataset.
        field_name: Name of field to place joined values.
        join_path: Path to join-dataset.
        join_field_name: Name of join-field.
        dataset_where_sql: SQL where-clause for dataset subselection.
        join_where_sql: SQL where-clause for the join-dataset subselection.
        output_path: Path to output dataset.
        replacement_value: Value to replace a present join-field value with. If set to
            None, no replacement will occur.
        log_level: Level to log the function at.

    Returns:
        Feature counts for original and output datasets.
    """
    dataset_path = Path(dataset_path)
    join_path = Path(join_path)
    LOG.log(
        log_level,
        "Start: Spatially-join attributes in `%s.%s` by features/values in `%s.%s`.",
        dataset_path,
        field_name,
        join_path,
        join_field_name,
    )
    states = Counter()
    states["in original dataset"] = dataset.feature_count(dataset_path)
    view = DatasetView(dataset_path, dataset_where_sql=dataset_where_sql)
    # Do not include any field names - we do not want them added to output.
    join_view = DatasetView(join_path,
                            field_names=[],
                            dataset_where_sql=join_where_sql)
    with view, join_view:
        # ArcPy2.8.0: Convert Path to str.
        arcpy.analysis.SpatialJoin(
            target_features=view.name,
            join_features=join_view.name,
            out_feature_class=str(output_path),
            join_operation="JOIN_ONE_TO_MANY",
            join_type="KEEP_ALL",
            match_option="HAVE_THEIR_CENTER_IN",
        )
    if replacement_value is not None:
        attributes.update_by_value(
            output_path,
            field_name,
            value=replacement_value,
            dataset_where_sql="JOIN_FID <> -1",
            log_level=logging.DEBUG,
        )
    else:
        attributes.update_by_joined_value(
            output_path,
            field_name,
            key_field_names=["JOIN_FID"],
            join_dataset_path=join_path,
            join_field_name=join_field_name,
            join_key_field_names=["OID@"],
            dataset_where_sql="JOIN_FID <> -1",
            join_dataset_where_sql=join_where_sql,
            log_level=logging.DEBUG,
        )
    attributes.update_by_value(
        output_path,
        field_name,
        value=None,
        dataset_where_sql="JOIN_FID = -1",
        log_level=logging.DEBUG,
    )
    for name in ["Join_Count", "TARGET_FID", "JOIN_FID"]:
        dataset.delete_field(output_path,
                             field_name=name,
                             log_level=logging.DEBUG)
    states["in output"] = dataset.feature_count(output_path)
    log_entity_states("features", states, logger=LOG, log_level=log_level)
    LOG.log(log_level, "End: Join.")
    return states
Esempio n. 15
0
def polygons_to_lines(
    dataset_path: Union[Path, str],
    *,
    output_path: Union[Path, str],
    dataset_where_sql: Optional[str] = None,
    id_field_name: Optional[str] = None,
    make_topological: bool = False,
    log_level: int = logging.INFO,
) -> Counter:
    """Convert geometry from polygons to lines.

    Note:
        If `make_topological` is set to True, shared outlines will be a single, separate
        feature. Note that one cannot pass attributes to a topological transformation
        (as the values would not apply to all adjacent features).

        If an id field name is specified, the output dataset will identify the input
        features that defined the line feature with the name & values from the provided
        field. This option will be ignored if the output is non-topological lines, as
        the field will pass over with the rest of the attributes.

    Args:
        dataset_path: Path to dataset.
        output_path: Path to output dataset.
        dataset_where_sql: SQL where-clause for dataset subselection.
        id_field_name: Name of ID field to apply on topological lines.
        make_topological: Make line output topological, or merged where lines overlap.
        log_level: Level to log the function at.

    Returns:
        Feature counts for original and output datasets.
    """
    dataset_path = Path(dataset_path)
    output_path = Path(output_path)
    LOG.log(
        log_level,
        "Start: Convert polgyons in `%s` to lines in output `%s`.",
        dataset_path,
        output_path,
    )
    states = Counter()
    states["in original dataset"] = dataset.feature_count(dataset_path)
    view = DatasetView(dataset_path, dataset_where_sql=dataset_where_sql)
    with view:
        # ArcPy2.8.0: Convert Path to str.
        arcpy.management.PolygonToLine(
            in_features=view.name,
            out_feature_class=str(output_path),
            neighbor_option=("IDENTIFY_NEIGHBORS"
                             if make_topological else "IGNORE_NEIGHBORS"),
        )
    if make_topological:
        _dataset = Dataset(dataset_path)
        for side in ["left", "right"]:
            oid_key = f"{side.upper()}_FID"
            if id_field_name:
                id_field = next(
                    _field for _field in _dataset.fields
                    if _field.name.lower() == id_field_name.lower())
                id_field.name = f"{side.upper()}_{id_field_name}"
                # Cannot create an OID-type field, so force to long.
                if id_field.type.upper() == "OID":
                    id_field.type = "LONG"
                dataset.add_field(output_path,
                                  log_level=logging.DEBUG,
                                  **id_field.field_as_dict)
                attributes.update_by_joined_value(
                    output_path,
                    field_name=id_field.name,
                    key_field_names=[oid_key],
                    join_dataset_path=dataset_path,
                    join_field_name=id_field_name,
                    join_key_field_names=[_dataset.oid_field_name],
                    log_level=logging.DEBUG,
                )
            dataset.delete_field(output_path,
                                 field_name=oid_key,
                                 log_level=logging.DEBUG)
    else:
        dataset.delete_field(output_path,
                             field_name="ORIG_FID",
                             log_level=logging.DEBUG)
    states["in output"] = dataset.feature_count(output_path)
    log_entity_states("features", states, logger=LOG, log_level=log_level)
    LOG.log(log_level, "End: Convert.")
    return states
Esempio n. 16
0
def dissolve_features(
    dataset_path: Union[Path, str],
    *,
    dataset_where_sql: Optional[str] = None,
    output_path: Union[Path, str],
    dissolve_field_names: Optional[Iterable[str]] = None,
    all_fields_in_output: bool = False,
    allow_multipart: bool = True,
    unsplit_lines: bool = False,
    log_level: int = logging.INFO,
) -> Counter:
    """Dissolve feature geometry that share value in given fields.

    Args:
        dataset_path: Path to dataset.
        dataset_where_sql: SQL where-clause for dataset subselection.
        output_path: Path to output dataset.
        dissolve_field_names: Names of fields to base dissolve on.
        all_fields_in_output: All fields in the dataset will persist in the output
            dataset if True. Otherwise, only the dissolve fields will persist. Non-
            dissolve fields will have default values, of course.
        allow_multipart: Allow multipart features in output if True.
        unsplit_lines: Merge line features when endpoints meet without crossing features
            if True.
        log_level: Level to log the function at.

    Returns:
        Feature counts for original and output datasets.
    """
    dataset_path = Path(dataset_path)
    output_path = Path(output_path)
    if dissolve_field_names is not None:
        dissolve_field_names = list(dissolve_field_names)
    LOG.log(
        log_level,
        "Start: Dissolve features in `%s` on fields `%s`.",
        dataset_path,
        dissolve_field_names,
    )
    states = Counter()
    states["in original dataset"] = dataset.feature_count(dataset_path)
    view = DatasetView(
        dataset_path,
        field_names=dissolve_field_names,
        dataset_where_sql=dataset_where_sql,
    )
    with view:
        # ArcPy2.8.0: Convert Path to str.
        arcpy.management.Dissolve(
            in_features=view.name,
            out_feature_class=str(output_path),
            dissolve_field=dissolve_field_names,
            multi_part=allow_multipart,
            unsplit_lines=unsplit_lines,
        )
    if all_fields_in_output:
        for _field in Dataset(dataset_path).user_fields:
            # Cannot add a non-nullable field to existing features.
            _field.is_nullable = True
            dataset.add_field(
                output_path,
                exist_ok=True,
                log_level=logging.DEBUG,
                **_field.field_as_dict,
            )
    states["in output"] = dataset.feature_count(output_path)
    log_entity_states("features", states, logger=LOG, log_level=log_level)
    LOG.log(log_level, "End: Dissolve.")
    return states