示例#1
0
def explode(features: GeoJson) -> FeatureCollection:
    """
    Takes a feature or set of features and returns all positions as {Point|points}.

    :param features: any GeoJSON feature or feature collection
    :return: {FeatureCollection} points representing the exploded input features
    """
    points = []

    try:
        geojson_type = features.get("type")
    except AttributeError:
        raise InvalidInput(error_code_messages["InvalidGeometry"](all_geometry_types))

    if geojson_type in ["FeatureCollection", "GeometryCollection"]:

        key = "features" if geojson_type == "FeatureCollection" else "geometries"

        for feature in features[key]:

            properties = feature.get("properties", {})
            coords = get_coords_from_features(feature)
            points.extend(reduce_coordinates_to_points(coords, properties))

    else:

        properties = features.get("properties", {})
        coords = get_coords_from_geometry(features)
        points.extend(reduce_coordinates_to_points(coords, properties))

    return feature_collection(points)
示例#2
0
def line_intersect(input_1: LinePolyFeature,
                   input_2: LinePolyFeature) -> Feature:
    """
    Takes any LineString or Polygon GeoJSON and returns the intersecting point(s).

    :param line_1: {GeoJSON} line1 any LineString or Polygon
    :param line_2:{GeoJSON} line2 any LineString or Polygon
    :returns: {FeatureCollection<Point>} point(s) that intersect both
    """
    line_1_segments = get_line_segments(input_1)
    line_2_segments = get_line_segments(input_2)

    possible_intersects = []
    intersects = []

    possible_intersects.extend(
        spatial_filtering(line_1_segments, line_2_segments))

    for i in possible_intersects:
        pnt = calculate_intersect(*i)

        if pnt:
            intersects.append(pnt)

    return feature_collection(intersects)
示例#3
0
def normalize_to_feature_collection(geojson: GeoJSON) -> FeatureCollection:
    """
    Normalizes any GeoJSON to a FeatureCollection

    :param geojson: any GeoJSON
    :return: FeatureCollection
    """
    geojson_type = geojson.get("type")

    if geojson_type == "FeatureCollection":
        pass

    elif geojson_type == "Feature":

        geojson = feature_collection([geojson])

    else:
        geojson = feature_collection([feature(geojson)])

    return geojson
示例#4
0
def prepare_response(nearest_point, target_point, fixture_in):

    nearest_point["properties"].update(
        {"marker-color": "#F00", "marker-symbol": "star"}
    )

    target_point["properties"].update(
        {"marker-color": "#00F", "marker-symbol": "circle"}
    )

    result = feature_collection([*fixture_in["features"], target_point, nearest_point])

    return result
示例#5
0
    def test_input_mutation(self):

        point_1 = point([40, 50], {"featureIndex": "foo"})
        point_2 = point([20, -10], {"distanceToPoint": "bar"})
        points = feature_collection([point_1, point_2])

        result = nearest_point([0, 0], points)

        # Check if featureIndex properties was added to properties
        assert result["properties"]["featureIndex"] == 1

        # Check if previous input points have been modified
        assert point_1["properties"] == {"featureIndex": "foo"}
        assert point_2["properties"] == {"distanceToPoint": "bar"}
示例#6
0
def prepare_response(destination_point, fixture_in):

    coords = get_coords_from_features(fixture_in)
    coords = [round(coord, 6) for coord in coords]

    dest_coords = get_coords_from_features(destination_point)
    dest_coords = [round(coord, 6) for coord in dest_coords]

    line = line_string([coords, dest_coords], {"stroke": "#F00", "stroke-width": 4})

    fixture_in["properties"]["marker-color"] = "#F00"

    result = feature_collection([line, fixture_in, destination_point])

    return result
示例#7
0
def polygon_to_line(polygon: PolygonFeature,
                    options: Dict = {}) -> LineFeature:
    """ Converts a {Polygon} to a {LineString} or a {MultiPolygon} to a {FeatureCollection}
    or {MultiLineString}.

    :param polygon: Feature to convert
    :param options: Optional parameters

    :return:
        {Feature Collection|LineString|MultiLineString} of converted (Multi)Polygon to (Multi)LineString
    """
    if not options:
        properties = polygon.get("properties", {})
    else:
        properties = options.get("properties", {})

    geometry_type = get_geometry_type(polygon, ("Polygon", "MultiPolygon"))

    polygon_coords = get_coords_from_features(polygon,
                                              ("Polygon", "MultiPolygon"))

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

    for geo_type, poly_coords in zip(geometry_type, polygon_coords):

        if geo_type == "MultiPolygon":

            line_coords = []

            for poly_coord in poly_coords:

                line_coords.append(coords_to_line(poly_coord, properties))

            line_feature = feature_collection(line_coords)

        else:

            line_feature = coords_to_line(poly_coords, properties)

    return line_feature
示例#8
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
示例#9
0
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)
示例#10
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)
示例#11
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)
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
示例#13
0
def polygon_tangents(
    start_point: PointFeature, polygon: PolygonFeature
) -> FeatureCollection:
    """ Finds the tangents of a {Polygon or(MultiPolygon} from a {Point}.

    more:
    http://geomalgorithms.com/a15-_tangents.html

    :param point: point [lng, lat] or Point feature to calculate the tangent points from
    :param polygon: polygon to get tangents from

    :return:
        Feature Collection containing the two tangent points
    """
    point_features = []

    point_coord = get_coords_from_features(start_point, ("Point",))
    polygon_coords = get_coords_from_features(polygon, ("Polygon", "MultiPolygon"))

    geometry_type = get_geometry_type(polygon)

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

    box = bbox(polygon)

    near_point_index = 0
    near_point = False

    # If the point lies inside the polygon bbox then it's a bit more complicated
    # points lying inside a polygon can reflex angles on concave polygons
    if (
        (point_coord[0] > box[0])
        and (point_coord[0] < box[2])
        and (point_coord[1] > box[1])
        and (point_coord[1] < box[3])
    ):

        near_point = nearest_point(start_point, explode(polygon))
        near_point_index = near_point["properties"]["featureIndex"]

    for geo_type, poly_coords in zip(geometry_type, polygon_coords):

        if geo_type == "Polygon":

            tangents = process_polygon(
                poly_coords, point_coord, near_point, near_point_index
            )

        # bruteforce approach
        # calculate both tangents for each polygon
        # define all tangents as a new polygon and calculate tangetns out of those coordinates
        elif geo_type == "MultiPolygon":

            multi_tangents = []

            for poly_coord in poly_coords:

                tangents = process_polygon(
                    poly_coord, point_coord, near_point, near_point_index
                )
                multi_tangents.extend(tangents)

            tangents = process_polygon(
                [multi_tangents], point_coord, near_point, near_point_index
            )

        r_tangents = tangents[0]
        l_tangents = tangents[1]

        point_features.extend([point(r_tangents), point(l_tangents)])

    return feature_collection(point_features)
示例#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