Skip to content

Commit

Permalink
Merge branch 'master' into fix/invalid-collection-tde-1216
Browse files Browse the repository at this point in the history
  • Loading branch information
paulfouquet committed Jul 29, 2024
2 parents b3befe5 + e63cdbc commit c6ebc52
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 18 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/containers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ jobs:

- name: Setup docker tags
id: tags
uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v6
with:
result-encoding: string
script: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ jobs:

- name: Setup docker tags
id: tags
uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v6
with:
result-encoding: string
script: |
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM ghcr.io/osgeo/gdal:ubuntu-small-3.9.0@sha256:d1a38af532e5d9e3991c4a6bddc2f2cb52644dc30a4eb8242101e8e23c3f83f6 as builder
FROM ghcr.io/osgeo/gdal:ubuntu-small-3.9.0@sha256:d1a38af532e5d9e3991c4a6bddc2f2cb52644dc30a4eb8242101e8e23c3f83f6 AS builder

# Avoid blocking `apt-get install` commands
ARG DEBIAN_FRONTEND=noninteractive
Expand Down
35 changes: 29 additions & 6 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,7 @@ pytest-dependency = "^0.5.1"
pytest-mock = "^3.12.0"
pytest-subtests = "*"
shellcheck-py = "*"
types-python-dateutil = "*"
types-shapely = "*"
types-urllib3 = "*"
vulture = "^2.10"
14 changes: 9 additions & 5 deletions scripts/collection_from_items.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import argparse
import json
import os
from argparse import Namespace
from typing import List

import shapely.geometry
import shapely.ops
Expand All @@ -17,10 +19,7 @@
from scripts.stac.imagery.provider import Provider, ProviderRole


# TODO: Refactor this function to avoid these pylint disable comments and improve testability
# pylint: disable=too-many-locals
# pylint: disable=too-many-statements
def main() -> None:
def parse_args(args: List[str] | None) -> Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("--uri", dest="uri", help="s3 path to items and collection.json write location", required=True)
parser.add_argument("--collection-id", dest="collection_id", help="Collection ID", required=True)
Expand Down Expand Up @@ -87,7 +86,12 @@ def main() -> None:
"--concurrency", dest="concurrency", help="The number of files to limit concurrent reads", required=True, type=int
)

arguments = parser.parse_args()
return parser.parse_args(args)


# pylint: disable=too-many-locals
def main(args: List[str] | None = None) -> None:
arguments = parse_args(args)
uri = arguments.uri

providers: list[Provider] = []
Expand Down
6 changes: 5 additions & 1 deletion scripts/stac/imagery/capture_area.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from linz_logger import get_log
from shapely import BufferCapStyle, BufferJoinStyle, to_geojson, union_all
from shapely.geometry.base import BaseGeometry
from shapely.ops import orient

from scripts.logging.time_helper import time_in_ms

Expand Down Expand Up @@ -76,8 +77,11 @@ def merge_polygons(polygons: Sequence[BaseGeometry], buffer_distance: float) ->
# Negative buffer back in the polygons
union_unbuffered = union_buffered.buffer(-buffer_distance, cap_style=BufferCapStyle.flat, join_style=BufferJoinStyle.mitre)
union_simplified = union_unbuffered.simplify(buffer_distance)
# Apply right-hand rule winding order (exterior rings should be counter-clockwise) to the geometry
# Ref: https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6
oriented_union_simplified = orient(union_simplified, sign=1.0)

return union_simplified
return oriented_union_simplified


def generate_capture_area(polygons: Sequence[BaseGeometry], gsd: float) -> dict[str, Any]:
Expand Down
70 changes: 69 additions & 1 deletion scripts/stac/imagery/tests/capture_area_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from shapely.geometry import Polygon
from typing import cast

from shapely import get_exterior_ring, is_ccw
from shapely.geometry import MultiPolygon, Polygon, shape

from scripts.stac.imagery.capture_area import generate_capture_area, merge_polygons, to_feature

Expand Down Expand Up @@ -121,3 +124,68 @@ def test_generate_capture_area_not_rounded() -> None:
# This gap should not be covered in the capture-area
# as the GSD is 0.2m * a buffer of 2 * 2 < 0.88m
assert capture_area_expected != capture_area_result


def test_capture_area_orientation_polygon() -> None:
# Test the orientation of the capture area
# The polygon capture area should be the same as the polygon and not reversed
polygons = []
polygons.append(
shape(
{
"type": "MultiPolygon",
"coordinates": [
[
[
[174.673418475601466, -37.051277768264598],
[174.673425023818197, -37.051550327851878],
[174.673479832051271, -37.051280958541774],
[174.673418475601466, -37.051277768264598],
]
]
],
}
)
)
capture_area = generate_capture_area(polygons, 0.05)
assert is_ccw(get_exterior_ring(shape(capture_area["geometry"])))


def test_capture_area_orientation_multipolygon() -> None:
# Test the orientation of the capture area
# The multipolygon capture area polygons should be the same as the polygon and not reversed
polygons = []
polygons.append(
shape(
{
"type": "MultiPolygon",
"coordinates": [
[
[
[175.01786467173446, -36.80914409510033],
[175.0154395531313, -36.80918523526587],
[175.01524959114974, -36.80950706997984],
[175.01312143626905, -36.81369683085496],
[175.01801611134528, -36.81491172712502],
[175.01786467173446, -36.80914409510033],
]
],
[
[
[174.99635270693256, -36.809507300287486],
[174.99160937900407, -36.80958686195598],
[174.9916815185804, -36.80966722253352],
[174.9909875028488, -36.810093453911804],
[174.99113976763834, -36.815970642382126],
[174.99336131537976, -36.81582975812777],
[174.99645096077896, -36.81328982536313],
[174.99635270693256, -36.809507300287486],
]
],
],
}
)
)
capture_area = generate_capture_area(polygons, 0.05)
mp_geom = cast(MultiPolygon, shape(capture_area["geometry"]))
assert is_ccw(get_exterior_ring(mp_geom.geoms[0]))
3 changes: 1 addition & 2 deletions scripts/stac/imagery/tests/collection_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,7 @@ def test_capture_area_added(metadata: CollectionMetadata, subtests: SubTests) ->

with subtests.test():
assert collection.stac["assets"]["capture_area"]["file:checksum"] in (
"1220b15694be7495af38e0f70af67cfdc4f19b8bc415a2eb77d780e7a32c6e5b42c2", # geos 3.11
"122040fc8700d5d2d04600f730e10677b19d33f3b1e43b02c7867f4cfc2101930863", # geos 3.12
"1220369cd5d4179f5f68ca0fd9be70b9f66033fcc6bb2f3305c0ad977adc79d7ad53", # geos 3.11 - geos 3.12 as yet untested
)

with subtests.test():
Expand Down
60 changes: 60 additions & 0 deletions scripts/tests/collection_from_items_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from boto3 import client, resource
from moto import mock_aws
from moto.s3.responses import DEFAULT_REGION_NAME

from scripts.collection_from_items import main
from scripts.datetimes import utc_now
from scripts.files.fs_s3 import write
from scripts.json_codec import dict_to_json_bytes
from scripts.stac.imagery.item import ImageryItem


@mock_aws
def test_should_create_collection_file() -> None:
# Mock AWS S3
s3 = resource("s3", region_name=DEFAULT_REGION_NAME)
boto3_client = client("s3", region_name=DEFAULT_REGION_NAME)
s3.create_bucket(Bucket="stacfiles")
# Create mocked STAC Item
item = ImageryItem("123", "./scripts/tests/data/empty.tiff", utc_now)
geometry = {
"type": "Polygon",
"coordinates": [[1799667.5, 5815977.0], [1800422.5, 5815977.0], [1800422.5, 5814986.0], [1799667.5, 5814986.0]],
}
bbox = (1799667.5, 5815977.0, 1800422.5, 5814986.0)
start_datetime = "2021-01-27T00:00:00Z"
end_datetime = "2021-01-27T00:00:00Z"
item.update_spatial(geometry, bbox)
item.update_datetime(start_datetime, end_datetime)
item.add_collection("abc")
write("s3://stacfiles/item.json", dict_to_json_bytes(item.stac))
# CLI arguments
args = [
"--uri",
"s3://stacfiles/",
"--collection-id",
"abc",
"--category",
"urban-aerial-photos",
"--region",
"hawkes-bay",
"--gsd",
"1m",
"--start-date",
"2023-09-20",
"--end-date",
"2023-09-20",
"--lifecycle",
"ongoing",
"--producer",
"Placeholder",
"--licensor",
"Placeholder",
"--concurrency",
"25",
]
# Call script's main function
main(args)
# Verify collection.json has been created
resp = boto3_client.get_object(Bucket="stacfiles", Key="collection.json")
assert '"type": "Collection"' in resp["Body"].read().decode("utf-8")

0 comments on commit c6ebc52

Please sign in to comment.