Skip to content

Commit

Permalink
Merge pull request #51 from siapy/feature
Browse files Browse the repository at this point in the history
Feature
  • Loading branch information
janezlapajne authored Jul 4, 2024
2 parents 1320969 + 8fa8cb0 commit 6a65658
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ Closes organization/repo#number

<!-- List issues for GitHub to automatically close after merge to default branch. Type `#` and select from the list. See the [GitHub docs on linking PRs to issues](https://help.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue) for more. -->

- [ ] I have reviewed the [Guidelines for Contributing](CONTRIBUTING.md) and the [Code of Conduct](CODE_OF_CONDUCT.md).
- [ ] I have reviewed the [Guidelines for Contributing](../CONTRIBUTING.md) and the [Code of Conduct](../CODE_OF_CONDUCT.md).
File renamed without changes.
File renamed without changes.
38 changes: 38 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Security Policy

Security is very important for SiaPy and its community. 🔒

Learn more about it below. 👇

## Versions

The latest version of SiaPy is supported.

You are encouraged to update your SiaPy version frequently. This way you will benefit from the latest features, bug fixes, and **security fixes**.

## Reporting a Vulnerability

We take the security of our project seriously. If you have discovered a security vulnerability, we appreciate your cooperation in disclosing it to us in a responsible manner.

Please report any security vulnerabilities by emailing us at [[email protected]]([email protected]).

We will acknowledge receipt of your vulnerability report, assess it for validity and severity, and decide on the next steps. We ask that you do not publicly disclose the vulnerability until we have had a chance to address it.

## What to include in your report

To help us triage and prioritize the issue, please include as much information as possible, such as:

- The version of our project you are using
- A step-by-step description of how to reproduce the vulnerability
- Any relevant logs or output
- Any other information you think might be relevant

## Public Discussions

Please refrain from publicly discussing a potential security vulnerability.

Discussing vulnerabilities in public forums before they are properly assessed and fixed can significantly increase the risk to the project and its users. It's better to discuss issues privately and work together to find a solution first, to limit the potential impact as much as possible. We appreciate your cooperation and understanding in handling sensitive matters with discretion.

---

Thank you for helping to keep SiaPy and its users safe. 🏅
46 changes: 40 additions & 6 deletions siapy/entities/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,18 +165,23 @@ def bands(self) -> int:

@property
def default_bands(self) -> list[int]:
if "default bands" not in self.metadata.keys():
return []
db = self.metadata["default bands"]
db = self.metadata.get("default bands", [])
return list(map(int, db))

@property
def wavelengths(self) -> list[float]:
if "wavelength" not in self.metadata.keys():
return []
wavelength_data = self.metadata["wavelength"]
wavelength_data = self.metadata.get("wavelength", [])
return list(map(float, wavelength_data))

@property
def description(self) -> dict[str, Any]:
description_str = self.metadata.get("description", {})
return _parse_description(description_str)

@property
def camera_id(self) -> str:
return self.description.get("ID", "")

@property
def geometric_shapes(self) -> GeometricShapes:
return self._geometric_shapes
Expand Down Expand Up @@ -212,3 +217,32 @@ def _remove_nan(self, image: np.ndarray, nan_value: float = 0.0) -> np.ndarray:
image_mask = np.bitwise_not(np.bool_(np.isnan(image).sum(axis=2)))
image[~image_mask] = nan_value
return image


def _parse_description(description: str) -> dict[str, Any]:
def _parse():
data_dict = {}
for line in description.split("\n"):
key, value = line.split("=", 1)
key = key.strip()
value = value.strip()
if "," in value: # Special handling for values with commas
value = [
float(v) if v.replace(".", "", 1).isdigit() else v
for v in value.split(",")
]
elif value.isdigit():
value = int(value)
elif value.replace(".", "", 1).isdigit():
value = float(value)
data_dict[key] = value
return data_dict

try:
return _parse()
except ValueError as e:
raise ValueError(f"Error parsing description: {e}") from e
except KeyError as e:
raise KeyError(f"Missing key in description: {e}") from e
except Exception as e:
raise Exception(f"Unexpected error parsing description: {e}") from e
51 changes: 49 additions & 2 deletions tests/entities/test_entities_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from PIL import Image

from siapy.entities import Pixels, Shape, SpectralImage
from siapy.entities.images import GeometricShapes
from siapy.entities.images import GeometricShapes, _parse_description
from tests.configs import (
image_swir_hdr_path,
image_swir_img_path,
Expand Down Expand Up @@ -61,7 +61,7 @@ def test_metadata(spectral_images):
swir_meta = spectral_images.swir.metadata
assert isinstance(vnir_meta, dict)
assert isinstance(swir_meta, dict)
required_keys = ["default bands", "wavelength"]
required_keys = ["default bands", "wavelength", "description"]
assert all(key in vnir_meta.keys() for key in required_keys)
assert all(key in swir_meta.keys() for key in required_keys)

Expand Down Expand Up @@ -113,6 +113,23 @@ def test_wavelengths(spectral_images):
assert len(swir_wave) == 288


def test_description(spectral_images):
vnir_desc = spectral_images.vnir.description
swir_desc = spectral_images.swir.description
assert isinstance(vnir_desc, dict)
assert isinstance(swir_desc, dict)
required_keys = ["ID"]
assert all(key in vnir_desc.keys() for key in required_keys)
assert all(key in swir_desc.keys() for key in required_keys)


def test_camera_id(spectral_images):
vnir_cam_id = spectral_images.vnir.camera_id
swir_cam_id = spectral_images.swir.camera_id
assert vnir_cam_id == "VNIR_1600_SN0034"
assert swir_cam_id == "SWIR_384me_SN3109"


def test_to_numpy(spectral_images):
spectral_image_vnir = spectral_images.vnir.to_numpy()
spectral_image_swir = spectral_images.swir.to_numpy()
Expand Down Expand Up @@ -442,3 +459,33 @@ def test_geometric_shapes_get_by_name_not_found(spectral_images, corresponding_p
spectral_images.vnir.geometric_shapes.shapes = [rect]
found_shape = spectral_images.vnir.geometric_shapes.get_by_name("NonExistent")
assert found_shape is None


def test_parse_description_simple():
description = "Frameperiod = 20060\nIntegration time = 20000"
expected = {"Frameperiod": 20060, "Integration time": 20000}
assert _parse_description(description) == expected


def test_parse_description_with_floats_and_ints():
description = "Binning = 2\nPixelsize x = 0.000187"
expected = {"Binning": 2, "Pixelsize x": 0.000187}
assert _parse_description(description) == expected


def test_parse_description_with_commas():
description = "Rotating stage position = 0.000000,15.700000,degrees"
expected = {"Rotating stage position": [0.000000, 15.700000, "degrees"]}
assert _parse_description(description) == expected


def test_parse_description_empty_value():
description = "Comment ="
expected = {"Comment": ""}
assert _parse_description(description) == expected


def test_parse_description_invalid_format_raises_value_error():
description = "This is not a valid format"
with pytest.raises(ValueError):
_parse_description(description)

0 comments on commit 6a65658

Please sign in to comment.