Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 5 additions & 15 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
FROM osgeo/gdal:ubuntu-small-3.0.3
FROM python:3.12-slim

RUN : \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential \
libjpeg-dev \
libxml2-dev \
libxslt1-dev \
zlib1g-dev \
python3.6 \
python3-dev \
python3-pip \
python3-venv \
libexpat1 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& pip3 install --no-cache-dir --upgrade pip setuptools \
&& pip3 install --no-cache-dir rasterio==1.1.3 tox tox-venv --no-binary rasterio \
&& :

&& rm -rf /var/lib/apt/lists/*
WORKDIR /hls_vi

COPY ./ ./
RUN pip install '.[test]' && pip install tox

ENTRYPOINT [ "tox" ]
26 changes: 13 additions & 13 deletions hls_vi/generate_indices.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,15 +188,15 @@ def read_band(tif_path: Path) -> np.ma.masked_array:
# atmospheric compensation where it's possible to have values >100% reflectance
# due to unmet assumptions about topography.
# See, https://github.com/NASA-IMPACT/hls-vi/issues/44#issuecomment-2520592212
return np.ma.masked_less_equal(data, 0)
return np.ma.masked_less_equal(data, 0) # type: ignore[no-any-return, no-untyped-call]


def apply_fmask(data: np.ndarray, fmask: np.ndarray) -> np.ma.masked_array:
# Per Table 9 in https://lpdaac.usgs.gov/documents/1698/HLS_User_Guide_V2.pdf
# we wish to mask data where the Fmask has any one of the following bits set:
# cloud shadow (bit 3), adjacent to cloud/shadow (bit 2), cloud (bit 1).
cloud_like = int("00001110", 2)
return np.ma.masked_array(data, fmask & cloud_like != 0)
return np.ma.masked_array(data, fmask & cloud_like != 0) # type: ignore[no-untyped-call]


def apply_union_of_masks(bands: List[np.ma.masked_array]) -> List[np.ma.masked_array]:
Expand All @@ -214,7 +214,7 @@ def apply_union_of_masks(bands: List[np.ma.masked_array]) -> List[np.ma.masked_a
# so bitwise "or" will mask if "any" band has a masked value for that pixel
mask = np.ma.nomask
for band in bands:
mask = np.ma.mask_or(mask, band.mask)
mask = np.ma.mask_or(mask, band.mask) # type: ignore[no-untyped-call]

for band in bands:
band.mask = mask
Expand Down Expand Up @@ -296,7 +296,7 @@ def write_granule_index(

def evi(data: BandData) -> np.ma.masked_array:
b, r, nir = data[Band.B], data[Band.R], data[Band.NIR]
return 2.5 * (nir - r) / (nir + 6 * r - 7.5 * b + 1)
return 2.5 * (nir - r) / (nir + 6 * r - 7.5 * b + 1) # type: ignore[no-any-return]


def msavi(data: BandData) -> np.ma.masked_array:
Expand All @@ -307,45 +307,45 @@ def msavi(data: BandData) -> np.ma.masked_array:
sqrt_term >= 0,
(2 * nir + 1 - np.sqrt(sqrt_term)) / 2,
np.nan,
)
) # type: ignore[no-untyped-call]
result.fill_value = r.fill_value

return result


def nbr(data: BandData) -> np.ma.masked_array:
nir, swir2 = data[Band.NIR], data[Band.SWIR2]
return (nir - swir2) / (nir + swir2)
return (nir - swir2) / (nir + swir2) # type: ignore[no-any-return]


def nbr2(data: BandData) -> np.ma.masked_array:
swir1, swir2 = data[Band.SWIR1], data[Band.SWIR2]
return (swir1 - swir2) / (swir1 + swir2)
return (swir1 - swir2) / (swir1 + swir2) # type: ignore[no-any-return]


def ndmi(data: BandData) -> np.ma.masked_array:
nir, swir1 = data[Band.NIR], data[Band.SWIR1]
return (nir - swir1) / (nir + swir1)
return (nir - swir1) / (nir + swir1) # type: ignore[no-any-return]


def ndvi(data: BandData) -> np.ma.masked_array:
r, nir = data[Band.R], data[Band.NIR]
return (nir - r) / (nir + r)
return (nir - r) / (nir + r) # type: ignore[no-any-return]


def ndwi(data: BandData) -> np.ma.masked_array:
g, nir = data[Band.G], data[Band.NIR]
return (g - nir) / (g + nir)
return (g - nir) / (g + nir) # type: ignore[no-any-return]


def savi(data: BandData) -> np.ma.masked_array:
r, nir = data[Band.R], data[Band.NIR]
return 1.5 * (nir - r) / (nir + r + 0.5)
return 1.5 * (nir - r) / (nir + r + 0.5) # type: ignore[no-any-return]


def tvi(data: BandData) -> np.ma.masked_array:
g, r, nir = data[Band.G], data[Band.R], data[Band.NIR]
return (120 * (nir - g) - 200 * (r - g)) / 2 # pyright: ignore[reportReturnType]
return (120 * (nir - g) - 200 * (r - g)) / 2 # type: ignore[no-any-return]


class Index(Enum):
Expand Down Expand Up @@ -390,7 +390,7 @@ def __call__(self, data: BandData) -> np.ma.masked_array:
# We need to round to whole numbers (i.e., 0 decimal places, which is
# the default for np.round) because we convert to integer values, but
# numpy's conversion to integer types performs truncation, not rounding.
return np.ma.round(scaled_index).astype(np.int16)
return np.ma.round(scaled_index).astype(np.int16) # type: ignore[no-untyped-call, no-any-return]


def parse_args() -> Tuple[Path, Path, str]:
Expand Down
15 changes: 9 additions & 6 deletions hls_vi/generate_stac_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@
from pystac.extensions.projection import ProjectionExtension
from pystac.extensions.scientific import ScientificExtension
from pystac.extensions.view import ViewExtension
from pystac.version import STACVersion
from shapely.geometry import shape

STACVersion.set_stac_version("1.0.0")

# HLS_VI band information
hls_vi_band_info = {
"NDVI": {
Expand Down Expand Up @@ -101,8 +104,8 @@ def get_geometry(granule: untangle.Element) -> MultiPolygon:
ring.append(geojson_point)

closing_point = [
float(poly.Boundary.Point[0].PointLongitude.cdata),
float(poly.Boundary.Point[0].PointLatitude.cdata),
float(poly.Boundary.Point[0].PointLongitude.cdata), # type: ignore[union-attr, index]
float(poly.Boundary.Point[0].PointLatitude.cdata), # type: ignore[union-attr, index]
]
ring.append(closing_point)
ringtuple = (ring,)
Expand Down Expand Up @@ -174,13 +177,13 @@ def add_assets(
url = f"https://{endpoint}/lp-prod-protected/HLSL30_VI.{version}/{item_id}/"
public_url = f"https://{endpoint}/lp-prod-public/HLSL30_VI.{version}/{item_id}/"

for band_id, band_info in band_info.items():
for band_id, band_info_ in band_info.items():
band_url = f"{url}{item_id}.{band_id}.tif"
asset = pystac.Asset(
href=band_url, media_type=pystac.MediaType.COG, roles=["data"]
)
bands = [band_info["band"]]
EOExtension.ext(asset).bands = bands
bands = [band_info_["band"]]
EOExtension.ext(asset).bands = bands # type: ignore
item.add_asset(band_id, asset)

thumbnail_url = f"{public_url}{item_id}.jpg"
Expand Down Expand Up @@ -283,7 +286,7 @@ def cmr_to_item(hls_vi_metadata: str, endpoint: str, version: str) -> Mapping[st
process_view_geometry(item, granule)
process_scientific(item, granule)

return item.to_dict() # type: ignore
return item.to_dict()


def create_item(
Expand Down
12 changes: 6 additions & 6 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@

setup(
name="hls_vi",
version="1.19",
version="1.20",
packages=["hls_vi"],
include_package_data=True,
install_requires=[
"dataclasses;python_version<'3.7'",
"geojson",
"importlib_resources",
"lxml>=3.6.0,<6",
"numpy>=1.19,<2",
"pystac[validation]==1.0.0rc2",
"numpy",
# we can't use pystac>=1.12.0 because they did a major/breaking bump to
# the projection extension (v1.x to v2) that renamed proj:epsg -> proj:code.
"pystac[validation]>=1.0.0rc2,<1.12.0",
"rasterio",
"shapely",
"typing-extensions",
"untangle",
],
extras_require={
"test": [
"black[jupyter]==22.8.0", # Last version to support Python 3.6 runtime
"black[jupyter]",
"flake8",
"mypy",
"pytest",
Expand Down
4 changes: 2 additions & 2 deletions tests/fixtures/HLS-VI_stac_item.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@
},
"bbox": [-149.114568, 63.924491, -146.793616, 64.923996],
"stac_extensions": [
"https://stac-extensions.github.io/eo/v1.0.0/schema.json",
"https://stac-extensions.github.io/projection/v1.0.0/schema.json",
"https://stac-extensions.github.io/eo/v1.1.0/schema.json",
"https://stac-extensions.github.io/projection/v1.1.0/schema.json",
"https://stac-extensions.github.io/view/v1.0.0/schema.json",
"https://stac-extensions.github.io/scientific/v1.0.0/schema.json"
]
Expand Down
8 changes: 6 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,23 @@
# and then run "tox" from this directory.

[tox]
envlist = py36
envlist = py312

[flake8]
ignore = D203, W503
exclude = .git,__pycache__,docs/source/conf.py,old,build,dist,site-packages,venv,bin
max-complexity = 12
max-line-length = 90
max-line-length = 120

[testenv]
envdir = venv
sitepackages = True
extras =
test
allowlist_externals =
flake8
mypy
pytest
commands =
flake8
mypy
Expand Down