Source code for alphashape.optimizealpha

__all__ = ['optimizealpha']
import sys
import logging
import shapely
from shapely.geometry import MultiPoint
import trimesh
from typing import Union, Tuple, List
import rtree  # Needed by trimesh
import numpy as np
try:
    import geopandas
    USE_GP = True
except ImportError:
    USE_GP = False


def _testalpha(points: Union[List[Tuple[float]], np.ndarray], alpha: float):
    """
    Evaluates an alpha parameter.

    This helper function creates an alpha shape with the given points and alpha
    parameter.  It then checks that the produced shape is a Polygon and that it
    intersects all the input points.

    Args:
        points: data points
        alpha: alpha value

    Returns:
        bool: True if the resulting alpha shape is a single polygon that
            intersects all the input data points.
    """
    try:
        from alphashape import alphashape
    except ImportError:
        from .alphashape import alphashape
    polygon = alphashape(points, alpha)
    if isinstance(polygon, shapely.geometry.polygon.Polygon):
        if not isinstance(points, MultiPoint):
            points = MultiPoint(list(points))
        return all([polygon.intersects(point) for point in points])
    elif isinstance(polygon, trimesh.base.Trimesh):
        return len(polygon.faces) > 0 and all(
            trimesh.proximity.signed_distance(polygon, list(points)) >= 0)
    else:
        return False


[docs]def optimizealpha(points: Union[List[Tuple[float]], np.ndarray], max_iterations: int = 10000, lower: float = 0., upper: float = sys.float_info.max, silent: bool = False): """ Solve for the alpha parameter. Attempt to determine the alpha parameter that best wraps the given set of points in one polygon without dropping any points. Note: If the solver fails to find a solution, a value of zero will be returned, which when used with the alphashape function will safely return a convex hull around the points. Args: points: an iterable container of points max_iterations (int): maximum number of iterations while finding the solution lower: lower limit for optimization upper: upper limit for optimization silent: silence warnings Returns: float: The optimized alpha parameter """ # Convert to a shapely multipoint object if not one already if USE_GP and isinstance(points, geopandas.GeoDataFrame): points = points['geometry'] # Set the bounds assert lower >= 0, "The lower bounds must be at least 0" # Ensure the upper limit bounds the solution assert upper <= sys.float_info.max, ( f'The upper bounds must be less than or equal to {sys.float_info.max} ' 'on your system') if _testalpha(points, upper): if not silent: logging.error('the max float value does not bound the alpha ' 'parameter solution') return 0. # Begin the bisection loop counter = 0 while (upper - lower) > np.finfo(float).eps * 2: # Bisect the current bounds test_alpha = (upper + lower) * .5 # Update the bounds to include the solution space if _testalpha(points, test_alpha): lower = test_alpha else: upper = test_alpha # Handle exceeding maximum allowed number of iterations counter += 1 if counter > max_iterations: if not silent: logging.warning('maximum allowed iterations reached while ' 'optimizing the alpha parameter') lower = 0. break return lower