Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clicked points to shape file #23

Merged
merged 18 commits into from
Nov 6, 2024
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
4 changes: 4 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

All notable changes to this project will be documented below.

#### geospatial.points_to_geojson

* v0.1dev: **geosptial.points_to_geojson**(*img, viewer, out_path*)

#### geospatial.read_geotif

* v0.1dev: spectral = **geospatial.read_geotif**(*filename, bands="B,G,R", cropto=None*)
Expand Down
Binary file added docs/documentation_images/napari_clicks.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions docs/points_to_geojson.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
## Output clicked points as a geojson shapefile

Using a Napari or PlantCV-annotate viewer object with clicked points, output a shapefile.

**geospatial.points_to_geojson**(*img, viewer, out_path*)

- **Parameters:**
- img - Spectral image object, likely read in with [`geo.read_geotif`](read_geotif.md)
- viewer - Napari viewer class object, possible created with PlantCV-Annotate.
- out_path - Path to save the geojson shapefile. Must be ".geojson" file type.

- **Context:**
- Saved points can be used downstream for generating circular ROIs or circles for use with rasterstats.
- **Example use:**
- below to click plant locations


```python
import plantcv.geospatial as geo
import plantcv.annotate as an

# Read geotif in
img = geo.read_geotif("../read_geotif/rgb.tif", bands="R,G,B")
viewer = an.napari_open(img=img.pseudo_rgb)
viewer.add_points()

# A napari viewer window will pop up, use the points function to add clicks
```
```python
# In a separate cell, save the output after clicking:
geo.points_to_geojson(img, viewer, out_path="./points_example.geojson")
```

![Screenshot](documentation_images/napari_clicks.png)

**Source Code:** [Here](https://github.com/danforthcenter/plantcv-geospatial/blob/main/plantcv/geospatial/points_to_geojson.py)
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ nav:
- Read Geo-tif Data: read_geotif.md
- Transform coordinate points: transform_points.md
- Transform coordinate polygons: transform_polygons.md
- Save clicked points as geojson: points_to_geojson.md
markdown_extensions:
- toc:
permalink: True
Expand Down
4 changes: 3 additions & 1 deletion plantcv/geospatial/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
from plantcv.geospatial.read_geotif import read_geotif
from plantcv.geospatial.transform_points import transform_points
from plantcv.geospatial.transform_polygons import transform_polygons
from plantcv.geospatial.points_to_geojson import points_to_geojson

# Auto versioning
__version__ = version("plantcv-geospatial")

__all__ = [
"read_geotif",
"transform_points",
"transform_polygons"
"transform_polygons",
"points_to_geojson"
]
41 changes: 41 additions & 0 deletions plantcv/geospatial/points_to_geojson.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Save clicked points from Napari or PlantCV-annotate as a geojson points file.

import geojson
import rasterio
from plantcv.plantcv import fatal_error


def points_to_geojson(img, viewer, out_path):
"""Use clicks from a Napari or plantcv-annotate viewer to output a geojson shapefile.

Parameters
----------
img : PlantCV spectral_data class object
The image used for clicking on points, should be from read_geotif.
viewer: Napari viewer class object or plantcv-annotate Points class object.
The viewer used to make the clicks.
out_path : str
Path to save to shapefile. Must have "geojson" file extension
"""
# Napari output, points must be reversed
if hasattr(viewer, 'layers'):
points = [(img.metadata["transform"]*reversed(i)) for i in viewer.layers["Points"].data]
# Annotate output
elif hasattr(viewer, 'coords'):
points = [(img.metadata["transform"]*i) for i in viewer.coords['default']]
else:
fatal_error("Viewer class type not recognized. Currently, Napari and PlantCV-annotate viewers supported.")
features = [geojson.Feature(geometry=geojson.Point((lon, lat))) for lon, lat in points]
feature_collection = geojson.FeatureCollection(features)
# Make sure the coordinate system is the same as the original image
feature_collection['crs'] = {
"type": "name",
"properties": {
"name": rasterio.crs.CRS.to_string(img.metadata["crs"])
}
}
if ".geojson" in out_path:
with open(out_path, 'w') as f:
geojson.dump(feature_collection, f)
else:
fatal_error("File type not supported.")
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ dependencies = [
"rasterio",
"fiona",
"shapely",
"geojson",
"napari",
]
requires-python = ">=3.6"
authors = [
Expand Down
54 changes: 54 additions & 0 deletions tests/test_geospatial_points_to_geojson.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""Tests for geospatial.points_to_geojson"""

import pytest
import os
import napari
from plantcv.geospatial import read_geotif
from plantcv.geospatial import points_to_geojson

# Set up fake class just for testing the annotate output
# Don't want to have to have annotate as a dependency

class FakePoints:
def __init__(self):
self.coords = {}

def test_geospatial_points_to_geojson_napari(test_data, tmpdir):
"""Test for plantcv-geospatial."""
cache_dir = tmpdir.mkdir("cache")
img = read_geotif(filename=test_data.rgb_tif, bands="R,G,B")
viewer = napari.Viewer(show=False)
viewer.add_image(img.pseudo_rgb)
viewer.add_points()
filename = os.path.join(cache_dir, 'test_out.geojson')
points_to_geojson(img, viewer, out_path=filename)
assert os.path.exists(filename)

def test_geospatial_points_to_geojson_an(test_data, tmpdir):
"""Test for plantcv-geospatial."""
cache_dir = tmpdir.mkdir("cache")
img = read_geotif(filename=test_data.rgb_tif, bands="R,G,B")
viewer = FakePoints()
viewer.coords["default"] = []
filename = os.path.join(cache_dir, 'test_out.geojson')
points_to_geojson(img, viewer, out_path=filename)
assert os.path.exists(filename)

def test_geospatial_points_to_geojson_badviewer(test_data, tmpdir):
"""Test for plantcv-geospatial."""
cache_dir = tmpdir.mkdir("cache")
img = read_geotif(filename=test_data.rgb_tif, bands="R,G,B")
viewer = []
filename = os.path.join(cache_dir, 'test_out.geojson')
with pytest.raises(RuntimeError):
points_to_geojson(img, viewer, out_path=filename)

def test_geospatial_points_to_geojson_badfilename(test_data, tmpdir):
"""Test for plantcv-geospatial."""
cache_dir = tmpdir.mkdir("cache")
img = read_geotif(filename=test_data.rgb_tif, bands="R,G,B")
viewer = FakePoints()
viewer.coords["default"] = []
filename = os.path.join(cache_dir, 'test_out.txt')
with pytest.raises(RuntimeError):
points_to_geojson(img, viewer, out_path=filename)