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

Rich, HTML representations for Jupyter Notebook #573

Closed
wants to merge 5 commits into from
Closed
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Added

- Rich HTML representations for Jupyter Notebook display ([#573](https://github.com/stac-utils/pystac/pull/573)

### Removed

### Changed
Expand Down
559 changes: 559 additions & 0 deletions Untitled.ipynb

Large diffs are not rendered by default.

12 changes: 10 additions & 2 deletions pystac/catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import pystac
from pystac.stac_object import STACObject, STACObjectType
from pystac.html import jinja_env
from pystac.layout import (
BestPracticesLayoutStrategy,
HrefLayoutStrategy,
Expand Down Expand Up @@ -179,6 +180,10 @@ def __init__(
def __repr__(self) -> str:
return "<Catalog id={}>".format(self.id)

def _repr_html_(self) -> str:
template = jinja_env.get_template("Catalog.jinja2")
return str(template.render(catalog=self, catalog_type="Catalog"))

def set_root(self, root: Optional["Catalog"]) -> None:
STACObject.set_root(self, root)
if root is not None:
Expand Down Expand Up @@ -401,8 +406,11 @@ def get_items(self) -> Iterable["Item_Type"]:
Return:
Iterable[Item]: Generator of items whose parent is this catalog.
"""
return map(
lambda x: cast(pystac.Item, x), self.get_stac_objects(pystac.RelType.ITEM)
return list(
map(
lambda x: cast(pystac.Item, x),
self.get_stac_objects(pystac.RelType.ITEM),
)
)

def clear_items(self) -> None:
Expand Down
5 changes: 5 additions & 0 deletions pystac/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from pystac import STACObjectType, CatalogType
from pystac.asset import Asset
from pystac.catalog import Catalog
from pystac.html import jinja_env
from pystac.layout import HrefLayoutStrategy
from pystac.link import Link
from pystac.utils import datetime_to_str
Expand Down Expand Up @@ -506,6 +507,10 @@ def __init__(
def __repr__(self) -> str:
return "<Collection id={}>".format(self.id)

def _repr_html_(self) -> str:
template = jinja_env.get_template("Catalog.jinja2")
return str(template.render(catalog=self, catalog_type="Collection"))

def add_item(
self,
item: "Item_Type",
Expand Down
51 changes: 51 additions & 0 deletions pystac/html/Catalog.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<div class="jp-RenderedHTMLCommon jp-RenderedHTML jp-mod-trusted jp-OutputArea-output">
<div style="
width: 24px;
height: 24px;
background-color: #e1e1e1;
border: 3px solid #9D9D9D;
border-radius: 5px;
position: absolute;">
</div>
<div>
<details style="margin-left: 48px;">
<summary style="margin-bottom: 20px;">
<h3 style="margin-bottom: 0px; display: inline;">{{catalog_type}}</h3>
</summary>
<table style="width: 100%; text-align: left;">
<tr><td style="text-align: left;">ID: {{catalog.id}} </td></tr>
{% if catalog.title %}
<tr><td style="text-align: left;"><strong>Title:</strong> {{catalog.title}} </td></tr>
{% endif %}
{% if catalog.description %}
<tr><td style="text-align: left;"><strong>Description:</strong> {{catalog.description}} </td></tr>
{% endif %}
{% if catalog.extra_fields %}
{% for key in catalog.extra_fields %}
<tr><td style="text-align: left;"><strong>{{ key }}:</strong> {{ catalog.extra_fields[key] }} </td></tr>
{% endfor %}
{% endif %}
</table>
{% if catalog.get_items()|length >= 1 %}
<details>
<summary style="margin-bottom: 20px;">
<h4 style="margin-bottom: 0px; display: inline;">Items</h4>
</summary>
{% for item in catalog.get_items() %}
{{ item._repr_html_() }}
{% endfor %}
</details>
{% endif %}
{% if catalog.links|length >= 1 %}
<details>
<summary style="margin-bottom: 20px;">
<h4 style="margin-bottom: 0px; display: inline;">Links</h4>
</summary>
{% for link in catalog.links %}
{{ link._repr_html_() }}
{% endfor %}
</details>
{% endif %}
</details>
</div>
</div>
61 changes: 61 additions & 0 deletions pystac/html/Item.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<div class="jp-RenderedHTMLCommon jp-RenderedHTML jp-mod-trusted jp-OutputArea-output">
<div style="
width: 24px;
height: 24px;
background-color: #DBF5FF;
border: 3px solid #4CC9FF;
border-radius: 5px;
position: absolute;">
</div>
<div>
<details style="margin-left: 48px;">
<summary style="margin-bottom: 20px;">
<h3 style="margin-bottom: 0px; display: inline;">Item</h4>
</summary>
<table style="width: 100%; text-align: left;">
<tr><td style="text-align: left;"><strong>ID</strong>: {{item.id}} </td></tr>
{% if item.title %}
<tr><td style="text-align: left;"><strong>Title:</strong> {{item.title}} </td></tr>
{% endif %}
{% if item.bbox %}
<tr><td style="text-align: left;"><strong>Bounding Box:</strong> {{item.bbox}} </td></tr>
{% endif %}
{% if item.datetime %}
<tr><td style="text-align: left;"><strong>Datetime:</strong> {{item.datetime}} </td></tr>
{% endif %}
{% if item.properties %}
{% for key in item.properties %}
<tr><td style="text-align: left;"><strong>{{ key }}:</strong> {{ item.properties[key] }} </td></tr>
{% endfor %}
{% endif %}
{% if item.extra_fields %}
{% for key in item.extra_fields %}
<tr><td style="text-align: left;"><strong>{{ key }}:</strong> {{ item.extra_fields[key] }} </td></tr>
{% endfor %}
{% endif %}
</table>
{% if item.stac_extensions|length > 0 %}
<details>
<summary style="margin-bottom: 10px; margin-top: 10px;">
<h4 style="margin-bottom: 0px; display: inline;">STAC Extensions</h4>
</summary>
<table style="width: 100%; text-align: left;">
{% for stac_extension in item.stac_extensions %}
<tr><td style="text-align: left;"><a href="{{stac_extension}}">{{stac_extension}}</a></td></tr>
{% endfor %}
</table>
</details>
{% endif %}
{% if item.links|length > 0 %}
<details>
<summary style="margin-bottom: 10px; margin-top: 10px;">
<h4 style="margin-bottom: 0px; display: inline;">Links</h4>
</summary>
{% for link in item.links %}
{{ link._repr_html_() }}
{% endfor %}
</details>
{% endif %}
</details>
</div>
</div>
32 changes: 32 additions & 0 deletions pystac/html/Link.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<div class="jp-RenderedHTMLCommon jp-RenderedHTML jp-mod-trusted jp-OutputArea-output">
<div style="
width: 24px;
height: 24px;
background-color: #FFF7E5;
border: 3px solid #FF6132;
border-radius: 5px;
position: absolute;">
</div>
<div style="margin-left: 48px;">
<h4 style="margin-bottom: 0px;">Link</h4>
{% if link.title %}
<p style="color: #9D9D9D; margin-bottom: 0px;">{{ link.title }}</p>
{% endif %}
<table style="width: 100%; text-align: left;">
<tr><td style="text-align: left;"><strong>Rel:</strong> {{ link.rel }} </td></tr>
<tr><td style="text-align: left;"><strong>Target:</strong> {{ link.target }} </td></tr>
{% if link.media_type %}
<tr><td style="text-align: left;"><strong>Media Type:</strong> {{ link.media_type }} </td></tr>
{% endif %}
{% if link.extra_fields %}
{% for key in link.extra_fields %}
<tr><td style="text-align: left;"><strong>{{ key }}:</strong> {{ link.extra_fields[key] }} </td></tr>
{% endfor %}
{% endif %}
</table>
{% if link.extra_fields %}
<table style="width: 100%; text-align: left;">
</table>
{% endif %}
</div>
</div>
1 change: 1 addition & 0 deletions pystac/html/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .jinja_env import jinja_env
5 changes: 5 additions & 0 deletions pystac/html/jinja_env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from jinja2 import Environment, PackageLoader, select_autoescape # type: ignore

jinja_env = Environment(
loader=PackageLoader("pystac", "html"), autoescape=select_autoescape()
)
5 changes: 5 additions & 0 deletions pystac/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import pystac
from pystac import STACError, STACObjectType
from pystac.asset import Asset
from pystac.html import jinja_env
from pystac.link import Link
from pystac.serialization import (
identify_stac_object_type,
Expand Down Expand Up @@ -129,6 +130,10 @@ def __init__(
def __repr__(self) -> str:
return "<Item id={}>".format(self.id)

def _repr_html_(self) -> str:
template = jinja_env.get_template("Item.jinja2")
return str(template.render(item=self))

def set_self_href(self, href: Optional[str]) -> None:
"""Sets the absolute HREF that is represented by the ``rel == 'self'``
:class:`~pystac.Link`.
Expand Down
5 changes: 5 additions & 0 deletions pystac/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from typing import Any, Dict, Optional, TYPE_CHECKING, Union

import pystac
from pystac.html import jinja_env
from pystac.utils import make_absolute_href, make_relative_href, is_absolute_href

if TYPE_CHECKING:
Expand Down Expand Up @@ -210,6 +211,10 @@ def has_target_href(self) -> bool:
def __repr__(self) -> str:
return "<Link rel={} target={}>".format(self.rel, self.target)

def _repr_html_(self) -> str:
template = jinja_env.get_template("Link.jinja2")
return str(template.render(link=self))

def resolve_stac_object(self, root: Optional["Catalog_Type"] = None) -> "Link":
"""Resolves a STAC object from the HREF of this link, if the link is not
already resolved.
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
install_requires=[
"python-dateutil>=2.7.0",
'typing_extensions >= 3.7; python_version < "3.8"',
"Jinja2",
],
extras_require={"validation": ["jsonschema>=3.0"], "orjson": ["orjson>=3.5"]},
license="Apache Software License 2.0",
Expand Down