Пример #1
0
def bbox_polygon(bbox, options=None):
    """
    Takes a bounding box and returns an equivalent Polygon feature.

    :param bbox: bounding box extent in [minX, minY, maxX, maxY] order
    :param options: optional parameters
        [options["properties"]={}] Translate GeoJSON Properties to Point
        [options["id"]={}] Translate GeoJSON Id to Point
    :return: a Polygon representation of the bounding box
    """

    if not options:
        options = {}

    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])

    low_left = [west, south]
    top_left = [west, north]
    top_right = [east, north]
    low_right = [east, south]

    return polygon(
        [[low_left, low_right, top_right, top_left, low_left,]],
        options.get("properties", None),
        {"bbox": bbox, "id": options.get("id", None)},
    )
Пример #2
0
    def test_input_mutation_prevention(self):

        pnt = point([61, 5])
        poly = polygon([[[11, 0], [22, 4], [31, 0], [31, 11], [21, 15],
                         [11, 11], [11, 0]]])
        result = polygon_tangents(pnt.get("geometry"), poly.get("geometry"))

        assert result != None
Пример #3
0
def hexagon(center, rx, ry, properties, cosines, sines):
    vertices = []
    for i in range(6):
        x = center[0] + rx * cosines[i]
        y = center[1] + ry * sines[i]
        vertices.append([x, y])

    vertices.append(vertices[0])
    return polygon([vertices], properties)
Пример #4
0
def calculate_area(geometry):
    """
    Calculate geometry area

    :param geometry: GeoJSON geometry
    :return: the geometry area
    """

    coords = get_coords_from_geometry(geometry, ["Polygon", "MultiPolygon"],
                                      raise_exception=False)

    if get_input_dimensions(coords) >= 4:
        areas = list(map(lambda sub_item: calculate_area(sub_item), coords))
        return sum(areas)

    elif get_input_dimensions(coords) == 3:
        polygon(coords)
        return polygon_area(coords)

    else:
        return 0
Пример #5
0
def is_poly_in_poly(feature_1: Sequence, feature_2: Sequence) -> bool:
    """
    Checks if polygon feature_1 is inside polygon feature_2 and either way
    See http://stackoverflow.com/a/4833823/1979085

    :param feature: Coordinates of polygon feature 1
    :param feature: Coordinates of polygon feature 1
    :return: bool if there is an intersection
    """
    feature_1_line = polygon_to_line(polygon(feature_1))
    feature_2_line = polygon_to_line(polygon(feature_2))

    for coord1 in feature_1_line["geometry"]["coordinates"]:
        if boolean_point_in_polygon(coord1, feature_2):
            return True

    for coord2 in feature_2_line["geometry"]["coordinates"]:
        if boolean_point_in_polygon(coord2, feature_1):
            return True

    if is_line_on_line(feature_1_line, feature_2_line):
        return True

    return False
Пример #6
0
def hex_triangles(center, rx, ry, properties, cosines, sines):
    triangles = []
    for i in range(6):
        vertices = []
        vertices.append(center)
        vertices.append(
            [center[0] + rx * cosines[i], center[1] + ry * sines[i]])
        vertices.append([
            center[0] + rx * cosines[(i + 1) % 6],
            center[1] + ry * sines[(i + 1) % 6]
        ])
        vertices.append(center)
        triangles.append(polygon([vertices], properties))

    return triangles
Пример #7
0
def is_line_in_poly(feature_1: Sequence, feature_2: Sequence) -> bool:
    """
    Checks if a linestring feature is inside or intersects a polygon feature

    :param feature_1: Coordinates of polygon feature
    :param feature_2: Coordinates of linestring feature
    :return: bool if there is an intersection
    """
    feature_1_line = polygon_to_line(polygon(feature_1))

    if is_line_on_line(feature_2, feature_1_line):
        return True

    for coord in feature_2:
        if boolean_point_in_polygon(coord, feature_1):
            return True

    return False
Пример #8
0
def is_poly_in_poly(feature_1: Sequence, feature_2: Sequence) -> bool:
    """
    Checks if feature_1 polygon feature is in feature_2 polygon

    :param feature_1: Coordinates of polygon feature 1
    :param feature_2: Coordinates of polygon feature 2

    :return: boolean True/False if feature 1 is within feature 2
    """
    poly_bbox_1 = bbox(feature_1)
    poly_bbox_2 = bbox(feature_2)

    if not bbox_overlap(poly_bbox_2, poly_bbox_1):
        return False

    feature_1 = polygon_to_line(polygon(feature_1))
    line_coords = get_coords_from_features(feature_1)

    for coords in line_coords:

        if not boolean_point_in_polygon(coords, feature_2):
            return False

    return True
Пример #9
0
class TestLineIntersectss:
    @pytest.mark.parametrize(
        "fixture",
        [
            pytest.param(fixture, id=fixture_name)
            for fixture_name, fixture in fixtures.items()
        ],
    )
    def test_line_intersect(self, fixture):

        line_1 = fixture["in"]["features"][0]
        line_2 = fixture["in"]["features"][1]

        result = line_intersect(line_1, line_2)

        assert result.keys() == fixture["out"].keys()
        assert len(result["features"]) == len(fixture["out"]["features"])
        assert (all([
            i in result["features"] for i in fixture["out"]["features"]
        ]) == True)
        assert (all([
            i in fixture["out"]["features"] for i in result["features"]
        ]) == True)

    def test_input_mutation_prevention(self):

        line_1 = line_string([[7, 50], [8, 50], [9, 50]])
        line_2 = line_string([[8, 49], [8, 50], [8, 51]])

        line_1_cpy = deepcopy(line_1)
        line_2_cpy = deepcopy(line_2)

        _ = line_intersect(line_1, line_2)

        assert line_1 == line_1_cpy
        assert line_2 == line_2_cpy

    @pytest.mark.parametrize(
        "input_value,expected_value",
        [
            pytest.param(
                (
                    line_string([[7, 50], [8, 50], [9, 50]])["geometry"],
                    line_string([[8, 49], [8, 50], [8, 51]])["geometry"],
                ),
                [8, 50],
                id="ListHandling",
            ),
            pytest.param(
                (
                    feature_collection(
                        [line_string([[7, 50], [8, 50], [9, 50], [7, 50]])]),
                    feature_collection(
                        [line_string([[8, 49], [8, 50], [8, 51], [8, 49]])]),
                ),
                [8, 50],
                id="FeatureCollectionHandling",
            ),
            pytest.param(
                (
                    polygon([[[7, 50], [8, 50], [9, 50], [7, 50]]]),
                    polygon([[[8, 49], [8, 50], [8, 51], [8, 49]]]),
                ),
                [8, 50],
                id="PolygonHandling",
            ),
        ],
    )
    def test_geometric_objects(self, input_value, expected_value):

        result = line_intersect(*input_value)

        assert result["features"][0]["geometry"][
            "coordinates"] == expected_value
Пример #10
0
class TestPolygonToLine:
    @pytest.mark.parametrize(
        "fixture",
        [
            pytest.param(fixture, id=fixture_name)
            for fixture_name, fixture in fixtures.items()
        ],
    )
    def test_polygon_to_line(self, fixture):

        result = polygon_to_line(fixture["in"])

        assert result == fixture["out"]

    @pytest.mark.parametrize(
        "input_value,expected_value",
        [
            pytest.param(
                (
                    polygon([[[0, 1], [1, 1], [1, 0], [0, 0], [0, 1]]]),
                    {
                        "properties": {
                            "stroke": "#F00",
                            "stroke-width": 6
                        }
                    },
                ),
                {
                    "type": "Feature",
                    "properties": {
                        "stroke": "#F00",
                        "stroke-width": 6
                    },
                    "geometry": {
                        "type": "LineString",
                        "coordinates": [[0, 1], [1, 1], [1, 0], [0, 0], [0, 1]
                                        ],
                    },
                },
                id="PropertiesHandling",
            )
        ],
    )
    def test_properties_handling(self, input_value, expected_value):

        result = polygon_to_line(*input_value)

        assert result == expected_value

    @pytest.mark.parametrize(
        "input_value,exception_value",
        [
            pytest.param(
                (
                    {
                        "type": "Feature",
                        "properties": {},
                        "geometry": {
                            "coordinates": [[[0, 1], [1, 1], [1, 0], [0, 0],
                                             [0, 1]]]
                        },
                    },
                    {},
                ),
                error_code_messages["InvalidGeometry"](
                    ("Polygon", "MultiPolygon")),
                id="InvalidPolygon",
            ),
            pytest.param(
                (
                    {
                        "coordinates": [[[0, 1], [1, 1], [1, 0], [0, 0],
                                         [0, 1]]]
                    },
                    {
                        "properties": {
                            "stroke": "#F00",
                            "stroke-width": 6
                        }
                    },
                ),
                error_code_messages["InvalidGeometry"](
                    ("Polygon", "MultiPolygon")),
                id="InvalidPolygon",
            ),
        ],
    )
    def test_exception(self, input_value, exception_value):
        with pytest.raises(Exception) as excinfo:
            polygon_to_line(*input_value)

        assert excinfo.type == InvalidInput
        assert str(excinfo.value) == exception_value
Пример #11
0
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)
Пример #12
0
def rectangle_grid(
    bbox: List[float],
    cell_width: Union[int, float],
    cell_height: Union[int, float],
    options: Dict = {},
) -> FeatureCollection:
    """
    Creates a grid of rectangles from a bounding box, Feature or FeatureCollection.

    :param bbox: Array extent in [minX, minY, maxX, maxY] order
    :param cell_width: of each cell, in units
    :param cell_height: 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 = cell_width / (distance([west, south], [east, south], options))
    cell_width_deg = x_fraction * (east - west)
    y_fraction = cell_height / (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
    for _ in range(columns):
        current_y = south + delta_y
        for _ in range(rows):
            cell_poly = polygon(
                [
                    [
                        [current_x, current_y],
                        [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],
                    ]
                ],
                options.get("properties", {}),
            )

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

            current_y += cell_height_deg

        current_x += cell_width_deg

    return feature_collection(results)
class TestGeometryTypeFromFeatures:
    @pytest.mark.parametrize(
        "fixture",
        [
            pytest.param(fixture, id=fixture_name)
            for fixture_name, fixture in fixtures.items()
        ],
    )
    def test_get_geometry_type_from_features_geojson(self, fixture):

        result = get_geometry_type(fixture["in"])

        if isinstance(result, tuple):
            assert result == tuple(fixture["out"])
        else:
            assert result == fixture["out"][0]

    @pytest.mark.parametrize(
        "input_value,output_value",
        [
            pytest.param(
                point([4.83, 45.75], as_geojson=False),
                "Point",
                id="point_feature_object",
            ),
            pytest.param(
                line_string([[4.86, 45.76], [4.85, 45.74]], as_geojson=False),
                "LineString",
                id="line_string_feature_object",
            ),
            pytest.param(
                polygon(
                    [[
                        [4.82, 45.79],
                        [4.88, 45.79],
                        [4.91, 45.76],
                        [4.89, 45.72],
                        [4.82, 45.71],
                        [4.77, 45.74],
                        [4.77, 45.77],
                        [4.82, 45.79],
                    ]],
                    as_geojson=False,
                ),
                "Polygon",
                id="polygon_feature_object",
            ),
            pytest.param(
                feature_collection(
                    [
                        polygon(
                            [[
                                [4.82, 45.79],
                                [4.88, 45.79],
                                [4.91, 45.76],
                                [4.89, 45.72],
                                [4.82, 45.71],
                                [4.77, 45.74],
                                [4.77, 45.77],
                                [4.82, 45.79],
                            ]],
                            as_geojson=False,
                        ),
                        line_string([[4.86, 45.76], [4.85, 45.74]],
                                    as_geojson=False),
                        point([4.83, 45.75]),
                    ],
                    as_geojson=False,
                ),
                ("Polygon", "LineString", "Point"),
                id="feature_collection_object",
            ),
            pytest.param(
                Point([4.83, 45.75]),
                "Point",
                id="Point_object",
            ),
            pytest.param(
                LineString([[4.86, 45.76], [4.85, 45.74]]),
                "LineString",
                id="LineString_object",
            ),
        ],
    )
    def test_get_geometry_from_features_objects(self, input_value,
                                                output_value):
        assert get_geometry_type(input_value) == output_value

    @pytest.mark.parametrize(
        "input_value,output_value",
        [
            pytest.param(
                ([4.83, 45.75], ["Point"]),
                "Point",
                id="point_feature_object",
            ),
            pytest.param(
                ([[4.86, 45.76], [4.85, 45.74]], ["LineString"]),
                "LineString",
                id="line_string_feature_object",
            ),
            pytest.param(
                (
                    [[
                        [4.82, 45.79],
                        [4.88, 45.79],
                        [4.91, 45.76],
                        [4.89, 45.72],
                        [4.82, 45.71],
                        [4.77, 45.74],
                        [4.77, 45.77],
                        [4.82, 45.79],
                    ]],
                    ["Polygon"],
                ),
                "Polygon",
                id="polygon_feature_object",
            ),
        ],
    )
    def test_get_geometry_from_lists(self, input_value, output_value):

        assert get_geometry_type(*input_value) == output_value

    @pytest.mark.parametrize(
        "input_value, exception_value",
        [
            pytest.param(
                (4.83),
                error_code_messages["InvalidGeometry"](allowed_types_default),
                id="InvalidFloat",
            ),
            pytest.param(
                (4),
                error_code_messages["InvalidGeometry"](allowed_types_default),
                id="InvalidInt",
            ),
            pytest.param(
                ("4.83"),
                error_code_messages["InvalidGeometry"](allowed_types_default),
                id="InvalidString",
            ),
            pytest.param(
                (None),
                error_code_messages["InvalidGeometry"](allowed_types_default),
                id="InvalidNone",
            ),
            pytest.param(
                ({}),
                error_code_messages["InvalidGeometry"](allowed_types_default),
                id="InvalidFeaturesInput",
            ),
        ],
    )
    def test_general_exception(self, input_value, exception_value):

        with pytest.raises(Exception) as excinfo:
            get_geometry_type(input_value)

        assert excinfo.type == InvalidInput
        assert str(excinfo.value) == exception_value

    @pytest.mark.parametrize(
        "input_value,exception_value",
        [
            pytest.param(
                (point([4.83, 45.75], as_geojson=False), ("LineString", )),
                error_code_messages["InvalidGeometry"](["LineString"]),
                id="point_vs_linestring_feature_object",
            ),
            pytest.param(
                (
                    line_string([[4.86, 45.76], [4.85, 45.74]],
                                as_geojson=False),
                    ["Point", "MultiPoint"],
                ),
                error_code_messages["InvalidGeometry"](["Point", "MultiPoint"
                                                        ]),
                id="point_vs_line_string_feature_object",
            ),
            pytest.param(
                (
                    polygon(
                        [[
                            [4.82, 45.79],
                            [4.88, 45.79],
                            [4.91, 45.76],
                            [4.89, 45.72],
                            [4.82, 45.71],
                            [4.77, 45.74],
                            [4.77, 45.77],
                            [4.82, 45.79],
                        ]],
                        as_geojson=False,
                    ),
                    ["Point"],
                ),
                error_code_messages["InvalidGeometry"](["Point"]),
                id="point_vs_polygon_feature_object",
            ),
            pytest.param(
                (
                    feature_collection(
                        [
                            polygon(
                                [[
                                    [4.82, 45.79],
                                    [4.88, 45.79],
                                    [4.91, 45.76],
                                    [4.89, 45.72],
                                    [4.82, 45.71],
                                    [4.77, 45.74],
                                    [4.77, 45.77],
                                    [4.82, 45.79],
                                ]],
                                as_geojson=False,
                            ),
                            line_string([[4.86, 45.76], [4.85, 45.74]],
                                        as_geojson=False),
                            point([4.83, 45.75]),
                        ],
                        as_geojson=False,
                    ),
                    ["Polygon", "Point"],
                ),
                error_code_messages["InvalidGeometry"](["Polygon", "Point"]),
                id="one_wrong_feature_collection_object",
            ),
            pytest.param(
                (Point([4.83, 45.75]), ["MultiPoint"]),
                error_code_messages["InvalidGeometry"](["MultiPoint"]),
                id="No_Point_object",
            ),
            pytest.param(
                (LineString([[4.86, 45.76], [4.85, 45.74]
                             ]), ["MultiLineString"]),
                error_code_messages["InvalidGeometry"](["MultiLineString"]),
                id="No_LineString_object",
            ),
        ],
    )
    def test_specifid_exception(self, input_value, exception_value):

        with pytest.raises(Exception) as excinfo:
            get_geometry_type(*input_value)

        assert excinfo.type == InvalidInput
        assert str(excinfo.value) == exception_value
Пример #14
0
class TestGeometryFromFeatures:
    @pytest.mark.parametrize(
        "fixture",
        [
            pytest.param(fixture, id=fixture_name)
            for fixture_name, fixture in fixtures.items()
        ],
    )
    def test_get_geometry_from_features_geojson(self, fixture):

        try:
            allowed_types = fixture["in"]["properties"]["allowed_types"]
        except TypeError:

            allowed_types = fixture["in"][0]
            fixture["in"] = fixture["in"][1]

        assert (
            get_geometry_from_features(fixture["in"], allowed_types) == fixture["out"]
        )

    @pytest.mark.parametrize(
        "input_value,output_value",
        [
            pytest.param(
                (point([4.83, 45.75], as_geojson=False), ["Point"]),
                Point([4.83, 45.75]),
                id="point_feature_object",
            ),
            pytest.param(
                (
                    line_string([[4.86, 45.76], [4.85, 45.74]], as_geojson=False),
                    ["LineString"],
                ),
                LineString([[4.86, 45.76], [4.85, 45.74]]),
                id="line_string_feature_object",
            ),
            pytest.param(
                (
                    polygon(
                        [
                            [
                                [4.82, 45.79],
                                [4.88, 45.79],
                                [4.91, 45.76],
                                [4.89, 45.72],
                                [4.82, 45.71],
                                [4.77, 45.74],
                                [4.77, 45.77],
                                [4.82, 45.79],
                            ]
                        ],
                        as_geojson=False,
                    ),
                    ["Polygon"],
                ),
                Polygon(
                    [
                        [
                            [4.82, 45.79],
                            [4.88, 45.79],
                            [4.91, 45.76],
                            [4.89, 45.72],
                            [4.82, 45.71],
                            [4.77, 45.74],
                            [4.77, 45.77],
                            [4.82, 45.79],
                        ]
                    ]
                ),
                id="polygon_feature_object",
            ),
            pytest.param(
                (
                    feature_collection(
                        [
                            polygon(
                                [
                                    [
                                        [4.82, 45.79],
                                        [4.88, 45.79],
                                        [4.91, 45.76],
                                        [4.89, 45.72],
                                        [4.82, 45.71],
                                        [4.77, 45.74],
                                        [4.77, 45.77],
                                        [4.82, 45.79],
                                    ]
                                ],
                                as_geojson=False,
                            ),
                            line_string(
                                [[4.86, 45.76], [4.85, 45.74]], as_geojson=False
                            ),
                            point([4.83, 45.75]),
                        ],
                        as_geojson=False,
                    ),
                    ["LineString", "Polygon", "Point"],
                ),
                [
                    Polygon(
                        [
                            [
                                [4.82, 45.79],
                                [4.88, 45.79],
                                [4.91, 45.76],
                                [4.89, 45.72],
                                [4.82, 45.71],
                                [4.77, 45.74],
                                [4.77, 45.77],
                                [4.82, 45.79],
                            ]
                        ]
                    ),
                    LineString([[4.86, 45.76], [4.85, 45.74]]),
                    Point([4.83, 45.75]),
                ],
                id="feature_collection_object",
            ),
            pytest.param(
                (Point([4.83, 45.75]), ["Point"]),
                Point([4.83, 45.75]),
                id="Point_object",
            ),
            pytest.param(
                (LineString([[4.86, 45.76], [4.85, 45.74]]), ["LineString"],),
                LineString([[4.86, 45.76], [4.85, 45.74]]),
                id="LineString_object",
            ),
        ],
    )
    def test_get_geometry_from_features_objects(self, input_value, output_value):

        assert get_geometry_from_features(*input_value) == output_value

    @pytest.mark.parametrize(
        "input_value, exception_value",
        [
            pytest.param(
                (point([4.83, 45.75], as_geojson=False), ["LineString"]),
                error_code_messages["InvalidGeometry"](["LineString"]),
                id="InvalidGeometry-geojson",
            ),
            pytest.param(
                ([4.83, 45.75], ["LineString"]),
                error_code_messages["InvalidGeometry"](["LineString"]),
                id="InvalidGeometry-list",
            ),
            pytest.param(
                ([4.83], ["Point"]),
                error_code_messages["InvalidPointInput"],
                id="InvalidPoint",
            ),
            pytest.param(
                ({}, ["Point"]),
                error_code_messages["InvalidGeometry"](["Point"]),
                id="InvalidFeaturesInput",
            ),
        ],
    )
    def test_exception(self, input_value, exception_value):

        with pytest.raises(Exception) as excinfo:
            get_geometry_from_features(*input_value)

        assert excinfo.type == InvalidInput
        assert str(excinfo.value) == exception_value
Пример #15
0
def point_on_feature(features: GeoJSON) -> Point:
    """
    Takes a Feature or FeatureCollection and returns a {Point} guaranteed to be on the surface of the feature.

    Given a {Polygon}, the point will be in the area of the polygon
    Given a {LineString}, the point will be along the string
    Given a {Point}, the point will the same as the input

    :param features: any GeoJSON feature or feature collection
    :return: Point GeoJSON Feature on the surface of `input`
    """
    feature_collection = normalize_to_feature_collection(features)

    center_point = center(feature_collection)
    center_coords = center_point.get("geometry").get("coordinates")

    # check to see if centroid is on surface
    center_on_surface = False

    geometry_type = get_geometry_type(feature_collection)
    geometry_coords = get_coords_from_features(feature_collection)

    if isinstance(geometry_type, str):
        geometry_type = [geometry_type]

    for geo_type, geo_coords in zip(geometry_type, geometry_coords):

        if geo_type in ["Point", "MultiPoint"]:

            if geo_type == "Point":
                geo_coords = [geo_coords]

            for point_coords in geo_coords:

                if (center_coords[0]
                        == point_coords[0]) and (center_coords[1]
                                                 == point_coords[1]):

                    center_on_surface = True
                    break

        elif geo_type in ["LineString", "MultiLineString"]:

            if geo_type == "LineString":
                geo_coords = [geo_coords]

            for line_coords in geo_coords:
                if boolean_point_on_line(center_coords, line_coords):
                    center_on_surface = True
                    break

        elif geo_type in ["Polygon", "MultiPolygon"]:

            if geo_type == "Polygon":
                geo_coords = polygon(geo_coords)
            else:
                geo_coords = multi_polygon(geo_coords)
            if boolean_point_in_polygon(center_point, geo_coords):
                center_on_surface = True
                break

    if center_on_surface:
        point_on_surface = center_point

    else:

        point_on_surface = nearest_point(center_point, feature_collection)

    return point_on_surface
Пример #16
0
class TestBooleanPointInPolygon:

    allowed_types_polygon = [
        "Polygon",
        "MultiPolygon",
    ]

    @pytest.mark.parametrize(
        "fixture,points",
        [
            pytest.param(
                fixtures["poly-with-hole"],
                [
                    (point([-86.69208526611328, 36.20373274711739]), False),
                    (point([-86.72229766845702, 36.20258997094334]), True),
                    (point([-86.75079345703125, 36.18527313913089]), False),
                ],
                id="poly-with-hole",
            ),
            pytest.param(
                fixtures["multipoly-with-hole"],
                [
                    (point([-86.69208526611328, 36.20373274711739]), False),
                    (point([-86.72229766845702, 36.20258997094334]), True),
                    (point([-86.75079345703125, 36.18527313913089]), True),
                    (point([-86.75302505493164, 36.23015046460186]), False),
                ],
                id="multipoly-with-hole",
            ),
        ],
    )
    def test_boolean_point_in_polygon(self, fixture, points):

        poly = fixture["in"]

        for pt, result in points:
            assert boolean_point_in_polygon(pt, poly) is result

    @pytest.mark.parametrize(
        "poly,point_in,point_out",
        [
            pytest.param(
                polygon([[[0, 0], [0, 100], [100, 100], [100, 0], [0, 0]]]),
                point([50, 50]),
                point([140, 150]),
                id="simple_polygon",
            ),
            pytest.param(
                polygon([[[0, 0], [50, 50], [0, 100], [100, 100], [100, 0], [0, 0]]]),
                point([75, 75]),
                point([25, 50]),
                id="concave_polygon",
            ),
        ],
    )
    def test_boolean_point_in_polygon_simple(self, poly, point_in, point_out):

        assert boolean_point_in_polygon(point_in, poly)
        assert not boolean_point_in_polygon(point_out, poly)

    @pytest.mark.parametrize(
        "poly,points",
        [
            pytest.param(
                polygon([[[10, 10], [30, 20], [50, 10], [30, 0], [10, 10]]]),
                [
                    [point([10, 10]), lambda ignore_boundary: ignore_boundary is False],
                    [point([30, 20]), lambda ignore_boundary: ignore_boundary is False],
                    [point([50, 10]), lambda ignore_boundary: ignore_boundary is False],
                    [point([30, 10]), lambda ignore_boundary: True],
                    [point([0, 10]), lambda ignore_boundary: False],
                    [point([60, 10]), lambda ignore_boundary: False],
                    [point([30, -10]), lambda ignore_boundary: False],
                    [point([30, 30]), lambda ignore_boundary: False],
                ],
                id="poly-1",
            ),
            pytest.param(
                polygon([[[10, 0], [30, 20], [50, 0], [30, 10], [10, 0]]]),
                [
                    [point([30, 0]), lambda ignore_boundary: False],
                    [point([0, 0]), lambda ignore_boundary: False],
                    [point([60, 0]), lambda ignore_boundary: False],
                ],
                id="poly-2",
            ),
            pytest.param(
                polygon([[[10, 0], [30, 20], [50, 0], [30, -20], [10, 0]]]),
                [
                    [point([30, 0]), lambda ignore_boundary: True],
                    [point([0, 0]), lambda ignore_boundary: False],
                    [point([60, 0]), lambda ignore_boundary: False],
                ],
                id="poly-3",
            ),
            pytest.param(
                polygon(
                    [
                        [
                            [0, 0],
                            [0, 20],
                            [50, 20],
                            [50, 0],
                            [40, 0],
                            [30, 10],
                            [30, 0],
                            [20, 10],
                            [10, 10],
                            [10, 0],
                            [0, 0],
                        ]
                    ]
                ),
                [
                    [point([0, 20]), lambda ignore_boundary: ignore_boundary is False],
                    [point([10, 20]), lambda ignore_boundary: ignore_boundary is False],
                    [point([50, 20]), lambda ignore_boundary: ignore_boundary is False],
                    [point([0, 10]), lambda ignore_boundary: ignore_boundary is False],
                    [point([5, 10]), lambda ignore_boundary: True],
                    [point([25, 10]), lambda ignore_boundary: True],
                    [point([35, 10]), lambda ignore_boundary: True],
                    [point([0, 0]), lambda ignore_boundary: ignore_boundary is False],
                    [point([20, 0]), lambda ignore_boundary: False],
                    [point([35, 0]), lambda ignore_boundary: False],
                    [point([50, 0]), lambda ignore_boundary: ignore_boundary is False],
                    [point([50, 10]), lambda ignore_boundary: ignore_boundary is False],
                    [point([5, 0]), lambda ignore_boundary: ignore_boundary is False],
                    [point([10, 0]), lambda ignore_boundary: ignore_boundary is False],
                ],
                id="poly-4",
            ),
            pytest.param(
                polygon(
                    [
                        [[0, 20], [20, 40], [40, 20], [20, 0], [0, 20]],
                        [[10, 20], [20, 30], [30, 20], [20, 10], [10, 20]],
                    ]
                ),
                [
                    [point([20, 30]), lambda ignore_boundary: ignore_boundary is False],
                    [point([25, 25]), lambda ignore_boundary: ignore_boundary is False],
                    [point([30, 20]), lambda ignore_boundary: ignore_boundary is False],
                    [point([25, 15]), lambda ignore_boundary: ignore_boundary is False],
                    [point([20, 10]), lambda ignore_boundary: ignore_boundary is False],
                    [point([15, 15]), lambda ignore_boundary: ignore_boundary is False],
                    [point([10, 20]), lambda ignore_boundary: ignore_boundary is False],
                    [point([15, 25]), lambda ignore_boundary: ignore_boundary is False],
                    [point([20, 20]), lambda ignore_boundary: False],
                ],
                id="poly-5",
            ),
        ],
    )
    @pytest.mark.parametrize(
        "boundary",
        [
            pytest.param(True, id="include-boundary"),
            pytest.param(False, id="ignore-boundary"),
        ],
    )
    def test_boolean_point_in_polygon_boundary(self, boundary, poly, points):

        options = {"ignoreBoundary": boundary}

        for pt, result in points:
            assert boolean_point_in_polygon(pt, poly, options) is result(boundary)

    @pytest.mark.parametrize(
        "pt,poly,exception_value",
        [
            pytest.param(
                "xyz",
                polygon([[[10, 0], [30, 20], [50, 0], [30, 10], [10, 0]]]),
                error_code_messages["InvalidGeometry"](["Point"]),
                id="InvalidGeometry",
            ),
            pytest.param(
                point([0, 1]),
                "",
                error_code_messages["InvalidGeometry"](allowed_types_polygon),
                id="InvalidGeometry",
            ),
            pytest.param(
                point([0, 1]),
                [[0, 1], [1, 2], [2, 3], [0, 1]],
                error_code_messages["InvalidGeometry"](allowed_types_polygon),
                id="InvalidGeometry-input_must_have_a_geometry",
            ),
        ],
    )
    def test_exception(self, pt, poly, exception_value):

        with pytest.raises(Exception) as excinfo:
            boolean_point_in_polygon(pt, poly)

        assert excinfo.type == InvalidInput
        assert str(excinfo.value) == exception_value