diff --git a/.github/workflows/testsuite.yaml b/.github/workflows/testsuite.yaml index 3da1421..3fadf55 100644 --- a/.github/workflows/testsuite.yaml +++ b/.github/workflows/testsuite.yaml @@ -86,8 +86,7 @@ jobs: python ./ci/cache_kernels.py load - name: Install run: | - pip install pytest pytest-cov - pip install . + pip install .[test] pytest-cov - name: Run Tests run: | python -m pytest --cov=lunarsky --cov-config=.coveragerc --cov-report xml:./coverage.xml --junitxml=test-reports/xunit.xml diff --git a/lunarsky/moon.py b/lunarsky/moon.py index 6bc3dd2..f08acfa 100644 --- a/lunarsky/moon.py +++ b/lunarsky/moon.py @@ -1,3 +1,5 @@ +from functools import lru_cache + import numpy as np from astropy import units as u from astropy.units.quantity import QuantityInfoBase @@ -166,6 +168,34 @@ def new_like(self, cols, length, metadata_conflicts="warn", name=None): return out +@lru_cache +def _get_sites(cache=False): + import zipfile + from astropy.utils.data import get_readable_fileobj + from fastkml import KML + from fastkml import Placemark + from fastkml.utils import find, find_all + + with get_readable_fileobj( + "https://asc-planetarynames-data.s3.us-west-2.amazonaws.com/" + "MOON_nomenclature_center_pts.kmz", + encoding="binary", + cache=cache, + ) as downloaded: + path = zipfile.Path(downloaded) + (kml_path,) = path.glob("*.kml") + with kml_path.open() as kml_file: + kml = KML.parse(kml_file) + + return { + place.name.lower(): ( + float(find(place.extended_data, name="center_lon").value), + float(find(place.extended_data, name="center_lat").value), + ) + for place in find_all(kml, of_type=Placemark) + } + + class MoonLocation(u.Quantity): """ Location on the Moon. @@ -230,6 +260,30 @@ def __new__(cls, *args, **kwargs): ) return self + @classmethod + def of_site(cls, site_name: str): + """Resolve a place name to a location on the moon. + + Look up a surface feature in the USGS Gazetteer of Planetary + Nomenclature (https://planetarynames.wr.usgs.gov/Page/MOON/target). + + Notes + ----- + - This data source provides the longitude and latitude, but not the + height, of the approximate centers of craters and other features. + - Locations are referenced to the IAU2000 selenoid. + - This function downloads a KML file that is 11M in size the first time + that you call it in a given Python session. + + Examples + -------- + >>> MoonLocation.of_site("Shackleton") + + """ + sites = _get_sites() + lon_lat = sites[site_name.lower()] + return cls.from_selenodetic(*lon_lat, ellipsoid="IAU2000") + @classmethod def _set_site_id(cls, inst): """ diff --git a/pyproject.toml b/pyproject.toml index 6be577e..eaf220e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,12 +26,16 @@ dependencies = [ "astropy>=6.0.0", "spiceypy", "jplephem", + "fastkml[lxml]", ] requires-python = ">=3.8" dynamic = [ "version" ] [project.optional-dependencies] -test = ["pytest"] +test = [ + "pytest", + "pytest-doctestplus", +] [project.urls] source = "https://github.com/aelanman/lunarsky" @@ -45,4 +49,7 @@ local_scheme = "no-local-version" version_file = "lunarsky/version.py" [tool.pytest] -addopts = [ "--ignore=scripts" ] +addopts = [ + "--doctest-plus", + "--ignore=scripts", +]