コード例 #1
0
    def test_midpoint(self, fixture):

        points = fixture["in"]

        mid_point = midpoint(points[0], points[1])

        assert truncate(distance(points[0], mid_point),
                        2) == truncate(distance(points[1], mid_point), 2)
コード例 #2
0
    def test_exception(self):

        wrong_units = "foo"

        with pytest.raises(Exception) as excinfo:
            distance(point([0, 0]), point([10, 10]), {"units": wrong_units})

        assert excinfo.type == InvalidInput
        assert str(
            excinfo.value) == error_code_messages["InvalidUnits"](wrong_units)
コード例 #3
0
ファイル: _length.py プロジェクト: pyturf/pyturf
def length(features, options=None):
    """
    Calculates the total length of the input Feature / FeatureCollection in the specified units.

    :param features: a Feature / FeatureCollection of types LineString, MultiLineString, Polygon or MultiPolygon
    :param options: optional parameters
        [options["units"]=kilometers] can be degrees, radians, miles, or kilometers
    :return: the measured distance
    """

    coords = get_coords_from_features(features, [
        "LineString",
        "MultiLineString",
        "Polygon",
        "MultiPolygon",
    ])

    if any(
            isinstance(inner_item, list) for item in coords
            for inner_item in item):
        distances = list(
            map(lambda sub_item: length(sub_item, options), coords))
        return sum(distances)

    total_distance = reduce(
        lambda accum, coord: accum + distance(coord[0], coord[1], options),
        zip(coords, coords[1:]),
        0,
    )

    return total_distance
コード例 #4
0
    def test_distance(self, units):

        pt1 = fixture["features"][0]
        pt2 = fixture["features"][1]

        options = {"units": units}

        assert distance(pt1, pt2, options) == expected_results[units]
コード例 #5
0
ファイル: _square.py プロジェクト: pyturf/pyturf
def square(bbox):
    """
    Takes a bounding box and calculates the minimum square bounding box that
    would contain the input.

    :param bbox: bounding box extent in [minX, minY, maxX, maxY] order

    :return: a square surrounding bbox

    """
    if not isinstance(bbox, list) or len(bbox) != 4:
        raise InvalidInput(error_code_messages["InvalidBoundingBox"])

    west = float(bbox[0])
    south = float(bbox[1])
    east = float(bbox[2])
    north = float(bbox[3])

    horizontal_distance = distance([west, south], [east, south])
    vertical_distance = distance([west, south], [west, north])

    if horizontal_distance >= vertical_distance:
        vertical_midpoint = (south + north) / 2
        bounding_box = [
            west,
            vertical_midpoint - ((east - west) / 2),
            east,
            vertical_midpoint + ((east - west) / 2),
        ]
    else:

        horizontal_midpoint = (west + east) / 2
        bounding_box = [
            horizontal_midpoint - ((north - south) / 2),
            south,
            horizontal_midpoint + ((north - south) / 2),
            north,
        ]

    return bounding_box
コード例 #6
0
def midpoint(point1, point2):
    """
    Takes two point features and returns a point midway between them.
    The midpoint is calculated geodesically, meaning the curvature of the earth is taken into account.

    :param point1: first point
    :param point2: second point
    :return: a point midway between point 1 and point 2
    """

    dist = distance(point1, point2)
    heading = bearing(point1, point2)
    mid_point = destination(point1, dist / 2, heading)

    return mid_point
コード例 #7
0
    def test_rhumb_distance(self, fixture):

        pt1 = fixture["in"]["features"][0]
        pt2 = fixture["in"]["features"][1]

        distances = {
            "miles": round(rhumb_distance(pt1, pt2, {"units": "miles"}), 6),
            "nauticalmiles": round(
                rhumb_distance(pt1, pt2, {"units": "nautical_miles"}), 6
            ),
            "kilometers": round(rhumb_distance(pt1, pt2, {"units": "kilometers"}), 6),
            "greatCircleDistance": round(
                distance(pt1, pt2, {"units": "kilometers"}), 6
            ),
            "radians": round(rhumb_distance(pt1, pt2, {"units": "radians"}), 6),
            "degrees": round(rhumb_distance(pt1, pt2, {"units": "degrees"}), 6),
        }

        assert distances == fixture["out"]
コード例 #8
0
def calculate_distance_by_method(point_1: List, point_2: List,
                                 method: str) -> float:
    """
    Wrapper to calculate the distance between two points. Depending on the
    method, if calls rhumb distance or haversine distance

    :param point_1: Point coordinates
    :param point_2: Point coordinates
    :param method: wether to calculate the distance based on geodesic
                          (spheroid) or planar (flat) method.
                          Valid options are: 'geodesic' or 'planar

    :return: distance between point and both segments in radians
    """

    if method == "planar":
        dist = rhumb_distance(point_1, point_2, {"units": "degrees"})
    else:
        dist = distance(point_1, point_2, {"units": "degrees"})

    return dist
コード例 #9
0
def nearest_point(target: Union[Sequence, Dict, Feature],
                  features: GeoJson) -> Point:
    """
    Calculates the closest reference point from a feature collection towards a target point
    This calculation is geodesic.

    :param target: targetPoint the reference point
    :param features: points against input point set
    :return: the closest point in the features set to the reference point
    """
    min_distance = float("inf")
    nearest_point = None
    feature_index = None

    features = explode(features)
    points = features.get("features")

    target = get_coords_from_features(target, ["Point"])

    for i, point in enumerate(points):
        dist = distance(target, point)
        if dist < min_distance:
            min_distance = dist
            nearest_point = deepcopy(point)
            feature_index = i

    if "properties" in nearest_point:
        nearest_point["properties"].update({
            "featureIndex": feature_index,
            "distanceToPoint": min_distance
        })
    else:
        nearest_point.update({
            "properties": {
                "featureIndex": feature_index,
                "distanceToPoint": min_distance,
            }
        })

    return nearest_point
コード例 #10
0
def along(line, dist, options=None):
    """
    Takes a LineString and returns a Point at a specified distance along the line

    :param line: input LineString
    :param dist: distance along the line
    :param options: optional parameters
        [options["units"]="kilometers"] can be degrees, radians, miles, or kilometers
    :return: Point `dist` `units` along the line
    """

    if not options or not isinstance(options, dict):
        options = {}

    if not isinstance(dist, (float, int)) or dist < 0:
        raise InvalidInput(error_code_messages["InvalidDistance"])

    coords = get_coords_from_features(line, ["LineString"])

    travelled = 0
    for i in range(len(coords)):
        if dist >= travelled and i == len(coords) - 1:
            break
        elif travelled >= dist:
            overshot = dist - travelled
            if not overshot:
                return point([truncate(coord, 6) for coord in coords[i]])
            else:
                direction = bearing(coords[i], coords[i - 1]) - 180
                interpolated = destination(coords[i], overshot, direction,
                                           options)
                return interpolated
        else:
            travelled += distance(coords[i], coords[i + 1])

    return point([truncate(coord, 6) for coord in coords[-1]])
コード例 #11
0
    def test_distance_particular_case(self):

        assert distance(point([-180, -90]), point([180, -90])) == 0
コード例 #12
0
ファイル: _point_grid.py プロジェクト: pyturf/pyturf
def point_grid(
    bbox: List[float],
    n_cells: Union[int, float],
    options: Dict = {},
) -> FeatureCollection:
    """
    Creates a square of rectangles from a bounding box, Feature or FeatureCollection.

    :param bbox: Array extent in [minX, minY, maxX, maxY] order
    :param n_cells: number of each cell, in units
    :param options: Optional parameters
        [options["units"]]: units ("degrees", "radians", "miles", "kilometers")
                            of the given cell_width and cell_height
        [options["mask"]]: if passed a Polygon or MultiPolygon here,
                           the grid Points will be created only inside it
        [options["properties"]]: passed to each point of the grid

    :returns: FeatureCollection of a grid of polygons
    """
    if not isinstance(options, dict):
        options = {}

    results = []
    west = bbox[0]
    south = bbox[1]
    east = bbox[2]
    north = bbox[3]

    x_fraction = n_cells / (distance([west, south], [east, south], options))
    cell_width_deg = x_fraction * (east - west)
    y_fraction = n_cells / (distance([west, south], [west, north], options))
    cell_height_deg = y_fraction * (north - south)

    # rows & columns
    bbox_width = east - west
    bbox_height = north - south
    columns = int(bbox_width // cell_width_deg)
    rows = int(bbox_height // cell_height_deg)

    # if the grid does not fill the bbox perfectly, center it.
    delta_x = (bbox_width - columns * cell_width_deg) / 2
    delta_y = (bbox_height - rows * cell_height_deg) / 2

    # iterate over columns & rows
    current_x = west + delta_x
    while current_x <= east:
        current_y = south + delta_y
        while current_y <= north:
            cell_point = point([current_x, current_y],
                               options.get("properties", {}))

            if "mask" in options:
                if boolean_within(cell_point, options["mask"]):
                    results.append(cell_point)
            else:
                results.append(cell_point)

            current_y += cell_height_deg

        current_x += cell_width_deg

    return feature_collection(results)
コード例 #13
0
ファイル: _triangle_grid.py プロジェクト: pyturf/pyturf
def triangle_grid(
    bbox: List[float],
    cell_side: Union[int, float],
    options: Dict = {},
) -> FeatureCollection:
    """
    Creates a square of rectangles from a bounding box, Feature or FeatureCollection.

    :param bbox: Array extent in [minX, minY, maxX, maxY] order
    :param cell_side: dimension of each cell
    :param options: Optional parameters
        [options["units"]]: units ("degrees", "radians", "miles", "kilometers")
                            of the given cell_width and cell_height
        [options["mask"]]: if passed a Polygon or MultiPolygon here,
                           the grid Points will be created only inside it
        [options["properties"]]: passed to each point of the grid

    :returns: FeatureCollection of a grid of polygons
    """
    if not isinstance(options, dict):
        options = {}

    results = []
    west = bbox[0]
    south = bbox[1]
    east = bbox[2]
    north = bbox[3]

    x_fraction = cell_side / (distance([west, south], [east, south], options))
    cell_width_deg = x_fraction * (east - west)
    y_fraction = cell_side / (distance([west, south], [west, north], options))
    cell_height_deg = y_fraction * (north - south)

    # if the grid does not fill the bbox perfectly, center it.
    xi = 0
    current_x = west
    while current_x <= east:
        yi = 0
        current_y = south
        while current_y <= north:
            cell_triangle1 = None
            cell_triangle2 = None

            if (xi % 2 == 0) and (yi % 2 == 0):

                cell_triangle1 = polygon(
                    [[
                        [current_x, current_y],
                        [current_x, current_y + cell_height_deg],
                        [current_x + cell_width_deg, current_y],
                        [current_x, current_y],
                    ]],
                    options.get("properties", {}),
                )

                cell_triangle2 = polygon(
                    [[
                        [current_x, current_y + cell_height_deg],
                        [
                            current_x + cell_width_deg,
                            current_y + cell_height_deg
                        ],
                        [current_x + cell_width_deg, current_y],
                        [current_x, current_y + cell_height_deg],
                    ]],
                    options.get("properties", {}),
                )

            elif (xi % 2 == 0) and (yi % 2 == 1):

                cell_triangle1 = polygon(
                    [[
                        [current_x, current_y],
                        [
                            current_x + cell_width_deg,
                            current_y + cell_height_deg
                        ],
                        [current_x + cell_width_deg, current_y],
                        [current_x, current_y],
                    ]],
                    options.get("properties", {}),
                )

                cell_triangle2 = polygon(
                    [[
                        [current_x, current_y],
                        [current_x, current_y + cell_height_deg],
                        [
                            current_x + cell_width_deg,
                            current_y + cell_height_deg
                        ],
                        [current_x, current_y],
                    ]],
                    options.get("properties", {}),
                )

            elif (yi % 2 == 0) and (xi % 2 == 1):

                cell_triangle1 = polygon(
                    [[
                        [current_x, current_y],
                        [current_x, current_y + cell_height_deg],
                        [
                            current_x + cell_width_deg,
                            current_y + cell_height_deg
                        ],
                        [current_x, current_y],
                    ]],
                    options.get("properties", {}),
                )

                cell_triangle2 = polygon(
                    [[
                        [current_x, current_y],
                        [
                            current_x + cell_width_deg,
                            current_y + cell_height_deg
                        ],
                        [current_x + cell_width_deg, current_y],
                        [current_x, current_y],
                    ]],
                    options.get("properties", {}),
                )

            elif (yi % 2 == 1) and (xi % 2 == 1):

                cell_triangle1 = polygon(
                    [[
                        [current_x, current_y],
                        [current_x, current_y + cell_height_deg],
                        [current_x + cell_width_deg, current_y],
                        [current_x, current_y],
                    ]],
                    options.get("properties", {}),
                )

                cell_triangle2 = polygon(
                    [[
                        [current_x, current_y + cell_height_deg],
                        [
                            current_x + cell_width_deg,
                            current_y + cell_height_deg
                        ],
                        [current_x + cell_width_deg, current_y],
                        [current_x, current_y + cell_height_deg],
                    ]],
                    options.get("properties", {}),
                )

            if "mask" in options:
                if boolean_intersects(options["mask"], cell_triangle1):
                    results.append(cell_triangle1)
                if boolean_intersects(options["mask"], cell_triangle2):
                    results.append(cell_triangle2)
            else:
                results.append(cell_triangle1)
                results.append(cell_triangle2)

            current_y += cell_height_deg
            yi += 1

        current_x += cell_width_deg
        xi += 1

    return feature_collection(results)
コード例 #14
0
def hex_grid(
    bbox: List[float],
    cell_side: Union[int, float],
    options: Dict = {},
) -> FeatureCollection:
    """
    Takes a bounding box and the diameter of the cell and returns a FeatureCollection of flat-topped
    hexagons or triangles aligned in an "odd-q" vertical grid as
    described in [Hexagonal Grids](http://www.redblobgames.com/grids/hexagons/).

    :param bbox: Array extent in [minX, minY, maxX, maxY] order
    :param n_cells: length of the side of the the hexagons or triangles, in units. It will also coincide with the
                    radius of the circumcircle of the hexagons
    :param options: Optional parameters
        [options["units"]]: units ("degrees", "radians", "miles", "kilometers")
                            of the given cell_width and cell_height
        [options["mask"]]: if passed a Polygon or MultiPolygon here,
                           the grid Points will be created only inside it
        [options["properties"]]: passed to each point of the grid
        [options["triangles"]]: whether to return as triangles instead of hexagons

    :returns: FeatureCollection of a grid of polygons
    """
    if not isinstance(options, dict):
        options = {}

    has_triangles = options.get("triangles", None)

    results = []
    west = bbox[0]
    south = bbox[1]
    east = bbox[2]
    north = bbox[3]

    center_y = (south + north) / 2
    center_x = (west + east) / 2

    x_fraction = (cell_side * 2) / (distance([west, center_y],
                                             [east, center_y], options))
    cell_width_deg = x_fraction * (east - west)
    y_fraction = (cell_side * 2 /
                  (distance([center_x, south], [center_x, north], options)))
    cell_height_deg = y_fraction * (north - south)
    radius = cell_width_deg / 2

    hex_width = radius * 2
    hex_height = math.sqrt(3) / 2 * cell_height_deg

    # rows & columns
    bbox_width = east - west
    bbox_height = north - south

    x_interval = 3 / 4 * hex_width
    y_interval = hex_height

    x_span = (bbox_width - hex_width) / (hex_width - radius / 2)
    x_count = int(x_span)

    x_adjust = (((x_count * x_interval - radius / 2) - bbox_width) / 2 -
                radius / 2 + x_interval / 2)

    y_count = int((bbox_height - hex_height) / hex_height)
    y_adjust = (bbox_height - y_count * hex_height) / 2

    has_offset_y = (y_count * hex_height - bbox_height) > (hex_height / 2)

    if has_offset_y:
        y_adjust -= hex_height / 4

    cosines = []
    sines = []

    for i in range(6):
        angle = 2 * math.pi / 6 * i
        cosines.append(math.cos(angle))
        sines.append(math.sin(angle))

    results = []

    for x in range(x_count + 1):
        for y in range(y_count + 1):

            is_odd = x % 2 == 1

            if (y == 0) and is_odd:
                continue

            if (y == 0) and has_offset_y:
                continue

            center_x = x * x_interval + west - x_adjust
            center_y = y * y_interval + south + y_adjust

            if is_odd:
                center_y -= hex_height / 2

            if has_triangles:

                triangles = hex_triangles(
                    [center_x, center_y],
                    cell_width_deg / 2,
                    cell_height_deg / 2,
                    options.get("properties", {}).copy(),
                    cosines,
                    sines,
                )

                for triangle in triangles:
                    if "mask" in options:
                        if boolean_intersects(options["mask"], triangle):
                            results.append(triangle)
                    else:
                        results.append(triangle)

            else:

                hex = hexagon(
                    [center_x, center_y],
                    cell_width_deg / 2,
                    cell_height_deg / 2,
                    options.get("properties", {}).copy(),
                    cosines,
                    sines,
                )

                if "mask" in options:
                    if boolean_intersects(options["mask"], hex):
                        results.append(hex)
                else:
                    results.append(hex)

    return feature_collection(results)