Skip to content

Commit

Permalink
Add support for displaying field data (#106)
Browse files Browse the repository at this point in the history
  • Loading branch information
giswqs authored Aug 5, 2024
1 parent f3a7863 commit 920e13e
Show file tree
Hide file tree
Showing 4 changed files with 307 additions and 0 deletions.
148 changes: 148 additions & 0 deletions docs/examples/field_data.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/opengeos/HyperCoast/blob/main/docs/examples/field_data.ipynb)\n",
"\n",
"# Visualizing Spectral Data from Field Measurements\n",
"\n",
"This notebook demonstrates how to visualize spectral data from field measurements. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# %pip install \"hypercoast[extra]\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import hypercoast\n",
"import pandas as pd"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Download a sample filed dataset."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"url = \"https://github.com/opengeos/datasets/releases/download/hypercoast/pace_sample_points.csv\"\n",
"data = pd.read_csv(url)\n",
"data.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Download PACE data."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"url = \"https://github.com/opengeos/datasets/releases/download/hypercoast/PACE_OCI.20240730T181157.L2.OC_AOP.V2_0.NRT.nc\"\n",
"filepath = \"../../private/data/PACE_OCI.20240730T181157.L2.OC_AOP.V2_0.NRT.nc\"\n",
"hypercoast.download_file(url, filepath)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Read the PACE dataset."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"dataset = hypercoast.read_pace(filepath)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Run the following cell to show the map. Click on the markers to see the spectral data."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"m = hypercoast.Map(center=[27.235094, -87.791748], zoom=6)\n",
"\n",
"m.add_basemap(\"Hybrid\")\n",
"wavelengths = [450, 550, 650]\n",
"m.add_pace(\n",
" dataset, wavelengths, indexes=[3, 2, 1], vmin=0, vmax=0.02, layer_name=\"PACE\"\n",
")\n",
"m.add(\"spectral\")\n",
"\n",
"m.add_field_data(\n",
" data,\n",
" x_col=\"wavelength\",\n",
" y_col_prefix=\"(\",\n",
" x_label=\"Wavelength (nm)\",\n",
" y_label=\"Reflectance\",\n",
" use_marker_cluster=True,\n",
")\n",
"m.set_center(-87.791748, 27.235094, zoom=6)\n",
"m"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"![](https://i.imgur.com/Q8pBQXg.png)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "hyper",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.14"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
108 changes: 108 additions & 0 deletions hypercoast/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -1007,3 +1007,111 @@ def save_geotiff(file_path, image, profile):
image, profile = load_geotiff(input_file)
pca_image = perform_pca(image, n_components, **kwargs)
save_geotiff(output_file, pca_image, profile)


def show_field_data(
data: Union[str],
x_col: str = "wavelength",
y_col_prefix: str = "(",
x_label: str = "Wavelengths (nm)",
y_label: str = "Reflectance",
use_marker_cluster: bool = True,
min_width: int = 400,
max_width: int = 600,
min_height: int = 200,
max_height: int = 250,
layer_name: str = "Marker Cluster",
m: object = None,
center: Tuple[float, float] = (20, 0),
zoom: int = 2,
):
"""
Displays field data on a map with interactive markers and popups showing time series data.
Args:
data (Union[str, pd.DataFrame]): Path to the CSV file or a pandas DataFrame containing the data.
x_col (str): Column name to use for the x-axis of the charts. Default is "wavelength".
y_col_prefix (str): Prefix to identify the columns that contain the location-specific data. Default is "(".
x_label (str): Label for the x-axis of the charts. Default is "Wavelengths (nm)".
y_label (str): Label for the y-axis of the charts. Default is "Reflectance".
use_marker_cluster (bool): Whether to use marker clustering. Default is True.
min_width (int): Minimum width of the popup. Default is 400.
max_width (int): Maximum width of the popup. Default is 600.
min_height (int): Minimum height of the popup. Default is 200.
max_height (int): Maximum height of the popup. Default is 250.
layer_name (str): Name of the marker cluster layer. Default is "Marker Cluster".
m (Map, optional): An ipyleaflet Map instance to add the markers to. Default is None.
center (Tuple[float, float]): Center of the map as a tuple of (latitude, longitude). Default is (20, 0).
zoom (int): Zoom level of the map. Default is 2.
Returns:
Map: An ipyleaflet Map with the added markers and popups.
"""
import pandas as pd
import matplotlib.pyplot as plt
from ipyleaflet import Map, Marker, Popup, MarkerCluster
from ipywidgets import Output, VBox

# Read the CSV file
if isinstance(data, str):
data = pd.read_csv(data)
elif isinstance(data, pd.DataFrame):
pass
else:
raise ValueError("data must be a path to a CSV file or a pandas DataFrame")

# Extract locations from columns
locations = [col for col in data.columns if col.startswith(y_col_prefix)]
coordinates = [tuple(map(float, loc.strip("()").split())) for loc in locations]

# Create the map
if m is None:
m = Map(center=center, zoom=zoom)

# Function to create the chart
def create_chart(data, title):
fig, ax = plt.subplots(figsize=(10, 6)) # Adjust the figure size here
ax.plot(data[x_col], data["values"])
ax.set_title(title)
ax.set_xlabel(x_label)
ax.set_ylabel(y_label)
output = Output() # Adjust the output widget size here
with output:
plt.show()
return output

# Define a callback function to create and show the popup
def callback_with_popup_creation(location, values):
def f(**kwargs):
marker_center = kwargs["coordinates"]
output = create_chart(values, f"Location: {location}")
popup = Popup(
location=marker_center,
child=VBox([output]),
min_width=min_width,
max_width=max_width,
min_height=min_height,
max_height=max_height,
)
m.add_layer(popup)

return f

markers = []

# Add points to the map
for i, coord in enumerate(coordinates):
location = f"{coord}"
values = pd.DataFrame({x_col: data[x_col], "values": data[locations[i]]})
marker = Marker(location=coord, title=location, name=f"Marker {i + 1}")
marker.on_click(callback_with_popup_creation(location, values))
markers.append(marker)

if use_marker_cluster:
marker_cluster = MarkerCluster(markers=markers, name=layer_name)
m.add_layer(marker_cluster)
else:
for marker in markers:
m.add_layer(marker)

return m
50 changes: 50 additions & 0 deletions hypercoast/hypercoast.py
Original file line number Diff line number Diff line change
Expand Up @@ -824,3 +824,53 @@ def find_nearest_indices(
self.cog_layer_dict[layer_name]["vis_bands"] = vis_bands
except Exception as e:
print(e)

def add_field_data(
self,
data: Union[str],
x_col: str = "wavelength",
y_col_prefix: str = "(",
x_label: str = "Wavelengths (nm)",
y_label: str = "Reflectance",
use_marker_cluster: bool = True,
min_width: int = 400,
max_width: int = 600,
min_height: int = 200,
max_height: int = 250,
layer_name: str = "Marker Cluster",
**kwargs,
):
"""
Displays field data on a map with interactive markers and popups showing time series data.
Args:
data (Union[str, pd.DataFrame]): Path to the CSV file or a pandas DataFrame containing the data.
x_col (str): Column name to use for the x-axis of the charts. Default is "wavelength".
y_col_prefix (str): Prefix to identify the columns that contain the location-specific data. Default is "(".
x_label (str): Label for the x-axis of the charts. Default is "Wavelengths (nm)".
y_label (str): Label for the y-axis of the charts. Default is "Reflectance".
use_marker_cluster (bool): Whether to use marker clustering. Default is True.
min_width (int): Minimum width of the popup. Default is 400.
max_width (int): Maximum width of the popup. Default is 600.
min_height (int): Minimum height of the popup. Default is 200.
max_height (int): Maximum height of the popup. Default is 250.
layer_name (str): Name of the marker cluster layer. Default is "Marker Cluster".
Returns:
Map: An ipyleaflet Map with the added markers and popups.
"""
show_field_data(
data,
x_col,
y_col_prefix,
x_label=x_label,
y_label=y_label,
use_marker_cluster=use_marker_cluster,
min_width=min_width,
max_width=max_width,
min_height=min_height,
max_height=max_height,
layer_name=layer_name,
m=self,
**kwargs,
)
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ nav:
- examples/pace_oci_l2.ipynb
- examples/multispectral.ipynb
- examples/pca.ipynb
- examples/field_data.ipynb
- Workshops:
- workshops/emit.ipynb
- API Reference:
Expand Down

0 comments on commit 920e13e

Please sign in to comment.