├── __init__.py ├── moon ├── __init__.py ├── res │ ├── __init__.py │ ├── en │ │ ├── __init__.py │ │ └── ui_messages.py │ └── constants.json ├── tests │ ├── __init__.py │ ├── cache.py │ └── general.py ├── requirements.txt ├── jupyter_ui.py ├── terminal_ui.py ├── custom_image.py └── dialamoon.py ├── setup.cfg ├── dist ├── moon-1.0.1.tar.gz ├── moon-1.0.2.tar.gz ├── moon-1.0.3.tar.gz ├── moon-1.0.4.tar.gz ├── moon-1.0.5.tar.gz ├── moon-1.0.6.tar.gz ├── moon-1.0.7.tar.gz ├── moon-1.0.8.tar.gz ├── moon-1.0.9.tar.gz ├── moon-1.1.0.tar.gz ├── moon-1.1.1.tar.gz ├── moon-1.1.2.tar.gz ├── moon-1.1.3.tar.gz ├── moon-1.1.4.tar.gz ├── moon-1.1.5.tar.gz ├── moon-1.1.6.tar.gz ├── moon-1.1.7.tar.gz ├── moon-1.1.8.tar.gz └── moon-1.1.9.tar.gz ├── tests.py ├── MANIFEST.in ├── .gitignore ├── LICENSE.txt ├── setup.py └── README.md /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /moon/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /moon/res/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /moon/res/en/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /moon/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /moon/requirements.txt: -------------------------------------------------------------------------------- 1 | numpy~=1.16 2 | opencv-python~=4.5.4 3 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | # Inside of setup.cfg 2 | [metadata] 3 | description-file = README.md 4 | -------------------------------------------------------------------------------- /dist/moon-1.0.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.0.1.tar.gz -------------------------------------------------------------------------------- /dist/moon-1.0.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.0.2.tar.gz -------------------------------------------------------------------------------- /dist/moon-1.0.3.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.0.3.tar.gz -------------------------------------------------------------------------------- /dist/moon-1.0.4.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.0.4.tar.gz -------------------------------------------------------------------------------- /dist/moon-1.0.5.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.0.5.tar.gz -------------------------------------------------------------------------------- /dist/moon-1.0.6.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.0.6.tar.gz -------------------------------------------------------------------------------- /dist/moon-1.0.7.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.0.7.tar.gz -------------------------------------------------------------------------------- /dist/moon-1.0.8.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.0.8.tar.gz -------------------------------------------------------------------------------- /dist/moon-1.0.9.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.0.9.tar.gz -------------------------------------------------------------------------------- /dist/moon-1.1.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.1.0.tar.gz -------------------------------------------------------------------------------- /dist/moon-1.1.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.1.1.tar.gz -------------------------------------------------------------------------------- /dist/moon-1.1.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.1.2.tar.gz -------------------------------------------------------------------------------- /dist/moon-1.1.3.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.1.3.tar.gz -------------------------------------------------------------------------------- /dist/moon-1.1.4.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.1.4.tar.gz -------------------------------------------------------------------------------- /dist/moon-1.1.5.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.1.5.tar.gz -------------------------------------------------------------------------------- /dist/moon-1.1.6.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.1.6.tar.gz -------------------------------------------------------------------------------- /dist/moon-1.1.7.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.1.7.tar.gz -------------------------------------------------------------------------------- /dist/moon-1.1.8.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.1.8.tar.gz -------------------------------------------------------------------------------- /dist/moon-1.1.9.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spacerest/moon/HEAD/dist/moon-1.1.9.tar.gz -------------------------------------------------------------------------------- /tests.py: -------------------------------------------------------------------------------- 1 | from moon.tests import cache, general 2 | import unittest 3 | 4 | unittest.main(cache, exit=False) 5 | 6 | unittest.main(general) 7 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.py 2 | include *.txt 3 | recursive-include dist *.gz 4 | recursive-include moon *.json 5 | recursive-include moon *.txt 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | **/__pycache__ 3 | **/*.png 4 | images/ 5 | *.png 6 | *.jpg 7 | **/test.py 8 | **/session.txt 9 | *egg-info 10 | **sublime-project 11 | **sublime-workspace 12 | .DS_Store 13 | -------------------------------------------------------------------------------- /moon/res/en/ui_messages.py: -------------------------------------------------------------------------------- 1 | 2 | YEAR_MISMATCH_ERROR = "You asked for year {year_requested} but "\ 3 | "DialAMoon appears to have returned year {year_returned}." 4 | 5 | HOUR_ERROR = "Hour is not between 0 and 23." 6 | -------------------------------------------------------------------------------- /moon/jupyter_ui.py: -------------------------------------------------------------------------------- 1 | from moon.dialamoon import Moon 2 | import matplotlib.pyplot as plt 3 | 4 | class JupyterUi(Moon): 5 | 6 | def show(self): 7 | plt.imshow(self.image) 8 | plt.show() 9 | 10 | -------------------------------------------------------------------------------- /moon/terminal_ui.py: -------------------------------------------------------------------------------- 1 | from moon.dialamoon import Moon 2 | 3 | class TerminalUi(Moon): 4 | def show(self): 5 | print("☽ This feature has been deprecated. ☽ "\ 6 | "\n☽ If you want to view the moon, save ☽"\ 7 | "\n☽ it and open it with your preferred ☽"\ 8 | "\n☽ program. ☽") 9 | -------------------------------------------------------------------------------- /moon/res/constants.json: -------------------------------------------------------------------------------- 1 | { 2 | "SVS_ID_DICT": { 3 | "2024": 5187, 4 | "2023": 5048, 5 | "2022": 4955, 6 | "2021": 4874, 7 | "2020": 4768, 8 | "2019": 4442, 9 | "2018": 4604, 10 | "2017": 4537, 11 | "2016": 4404, 12 | "2015": 4236, 13 | "2014": 4118, 14 | "2013": 4000, 15 | "2012": 3894, 16 | "2011": 3810 17 | }, 18 | "SVS_URL_BASE": "https://svs.gsfc.nasa.gov/vis/a000000/a00{year_id_modulo}/a00{year_id}/frames/730x730_1x1_30p/moon.{frame_id}.jpg", 19 | "SVS_JSON_URL_BASE": "https://svs.gsfc.nasa.gov/vis/a000000/a00{year_id_modulo}/a00{year_id}/mooninfo_{year}.json", 20 | "GITHUB_CONSTANTS_URL": "https://raw.githubusercontent.com/spacerest/moon/master/moon/res/constants.json", 21 | "DIALAMOON_API_BASE_URL": "https://svs.gsfc.nasa.gov/api/dialamoon/{year}-{month}-{day}T{hour}:{minute}" 22 | } 23 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | Copyright (c) 2018 YOUR NAME 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | The above copyright notice and this permission notice shall be included in all 10 | copies or substantial portions of the Software. 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | from setuptools import setup, find_packages 3 | 4 | # long_description 5 | readme_path = "./README.md" 6 | long_description = open(readme_path, 'r').read() 7 | 8 | setup( 9 | name = 'moon', 10 | packages = find_packages(), 11 | version = '2.0.0', 12 | license='MIT', 13 | description = 'Gets moon visualizations courtesy of SVS, NASA, Ernie Wright', 14 | long_description_content_type="text/markdown", 15 | long_description = long_description, 16 | author = 'Sadie Parker', 17 | author_email = 'sadiemparker@gmail.com', 18 | url = 'https://github.com/spacerest/moon', 19 | download_url = 'https://github.com/spacerest/moon/archive/v_2_0_0.tar.gz', 20 | keywords = ['MOON', 'ART', 'NASA', 'DIALAMOON'], 21 | install_requires=[ 22 | "matplotlib>=3.10,<4.0", 23 | "numpy>=2.2,<2.5", 24 | "opencv-python>=4.11,<5.0", 25 | "setuptools>=58.1,<69.0", 26 | ], 27 | classifiers=[ 28 | 'Development Status :: 4 - Beta', 29 | 'Intended Audience :: Other Audience', 30 | 'Topic :: Software Development :: Build Tools', 31 | 'License :: OSI Approved :: MIT License', 32 | 'Programming Language :: Python :: 3', 33 | 'Programming Language :: Python :: 3.4', 34 | 'Programming Language :: Python :: 3.5', 35 | 'Programming Language :: Python :: 3.6', 36 | 'Programming Language :: Python :: 3.7', 37 | 'Programming Language :: Python :: 3.8', 38 | 'Programming Language :: Python :: 3.9', 39 | 'Programming Language :: Python :: 3.10' 40 | ], 41 | package_data={'constants': ['res/constants.json']}, 42 | include_package_data=True 43 | 44 | ) 45 | -------------------------------------------------------------------------------- /moon/custom_image.py: -------------------------------------------------------------------------------- 1 | import urllib.request, ssl 2 | import io 3 | import cv2 4 | import numpy as np 5 | from copy import deepcopy, copy 6 | from functools import lru_cache 7 | 8 | class CustomImage(): 9 | def __init__(self, img_size, **kwargs): 10 | self.size = img_size 11 | self.height = img_size[0] 12 | self.width = img_size[1] 13 | self.set_image(**kwargs) 14 | return 15 | 16 | def save_to_disk(self, filename): 17 | try: 18 | cv2.imwrite(filename + ".jpg", self.image) 19 | except Exception as e: 20 | print(e) 21 | 22 | def get_image(self): 23 | return self.image 24 | 25 | @lru_cache(maxsize=10)#$todo maybe revisit this maxsize 26 | def set_image(self, url=None, filename=None): 27 | # METHOD #1: OpenCV, NumPy, and urllib 28 | # download the image, convert it to a NumPy array, and then read 29 | # it into OpenCV format 30 | # https://www.pyimagesearch.com/2015/03/02/convert-url-to-image-with-python-and-opencv/ 31 | if url: 32 | # certifi_context = ssl._create_unverified_context() 33 | resp = urllib.request.urlopen(url) #, context=certifi_context) 34 | self.image_src = url 35 | self.image = np.asarray(bytearray(resp.read()), dtype="uint8") 36 | self.image = cv2.imdecode(self.image, cv2.IMREAD_COLOR) 37 | self.resize_image() 38 | elif filename: 39 | self.image_src = filename 40 | self.image = cv2.imread(filename) 41 | self.resize_image() 42 | #$todo not sure easy way to cache image 43 | #without returning the ndarray to child obj...? 44 | return self.image 45 | 46 | def resize_image(self): 47 | #https://medium.com/@manivannan_data/resize-image-using-opencv-python-d2cdbbc480f0 48 | #Preferable interpolation methods are cv.INTER_AREA for shrinking and cv.INTER_CUBIC(slow) & cv.INTER_LINEAR for zooming. By default, interpolation method used is cv.INTER_LINEAR for all resizing purposes. 49 | try: 50 | self.image = cv2.resize(self.image, self.size, interpolation=cv2.INTER_AREA) 51 | except Exception as e: 52 | raise e 53 | self.image = cv2.resize(self.image, self.size, interpolation=cv2.INTER_CUBIC) 54 | -------------------------------------------------------------------------------- /moon/tests/cache.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from ..dialamoon import Moon 3 | from ..terminal_ui import TerminalUi 4 | from datetime import datetime 5 | from time import sleep 6 | import numpy 7 | import copy 8 | 9 | class TestMoonCache(unittest.TestCase): 10 | def test_caches_json_data(self): 11 | m = Moon() 12 | m.set_moon_datetime(date="2019-01-01") 13 | m.make_mooninfo_url() 14 | time1 = datetime.now() 15 | m.set_moon_datetime_info() 16 | time2 = datetime.now() 17 | sleep(5) 18 | time3 = datetime.now() 19 | m.set_moon_datetime_info() 20 | time4 = datetime.now() 21 | print(time3, time4) 22 | assert (time4 - time3).seconds == 0 23 | 24 | 25 | def test_changes_moon_image_on_second_request(self): 26 | m = Moon() 27 | m.set_moon_datetime("2019-01-01") 28 | m.request_moon_image() 29 | url1 = m.url 30 | im1 = copy.deepcopy(m.image) 31 | m.set_moon_datetime("2020-01-01") 32 | url2 = m.url 33 | m.request_moon_image() 34 | im2 = m.image 35 | assert url1 != url2 and not numpy.array_equal(im1,im2) 36 | 37 | def test_caches_moon_image_from_first_request(self): 38 | m = TerminalUi() 39 | m.set_moon_datetime("2020-01-06") 40 | m.request_moon_image() 41 | url1 = m.url 42 | im1 = copy.deepcopy(m.image) 43 | m.set_moon_datetime("2020-01-01") 44 | url2 = m.url 45 | m.request_moon_image() 46 | im2 = copy.deepcopy( m.image) 47 | m.set_moon_datetime("2020-01-06") 48 | url3 = m.url 49 | time2 = datetime.now() 50 | m.request_moon_image() 51 | time3 = datetime.now() 52 | im3 = copy.deepcopy(m.image) 53 | assert url1 == url3 and numpy.array_equal(im1,im3) and (time3 - time2).seconds == 0 54 | assert numpy.array_equal(im1,im3) 55 | 56 | def test_saves_time_getting_same_moon_image(self): 57 | m = Moon() 58 | m.set_moon_datetime("2019-01-01") 59 | m.request_moon_image() 60 | m.set_moon_datetime("2019-01-02") 61 | m.request_moon_image() 62 | time1 = datetime.now() 63 | m.request_moon_image() 64 | time2 = datetime.now() 65 | assert (time2 - time1).seconds == 0 66 | 67 | if __name__ == '__main__': 68 | unittest.main() 69 | -------------------------------------------------------------------------------- /moon/tests/general.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from ..dialamoon import Moon 3 | from ..terminal_ui import TerminalUi 4 | from datetime import datetime 5 | import numpy 6 | import copy 7 | 8 | class TestMoon(unittest.TestCase): 9 | def __init__(self, *args, **kwargs): 10 | super(TestMoon, self).__init__(*args, **kwargs) 11 | self.sm = Moon() 12 | 13 | def test_returns_error_for_datetime_format_issue(self): 14 | m = Moon() 15 | self.assertRaises(ValueError, m.set_moon_datetime, "190-01-01") 16 | 17 | def test_returns_helpful_error_for_datetime_mismatch(self): 18 | m = Moon() 19 | self.assertRaisesRegex(ValueError, r"You asked for year .* but "\ 20 | r"DialAMoon appears to have returned year .*.", 21 | m.set_moon_phase, "1900-01-01") 22 | 23 | def test_makes_a_moon_image_url(self): 24 | m = Moon() 25 | m.set_moon_datetime(date="2019-01-01", hour=0) 26 | assert m.url == "https://svs.gsfc.nasa.gov/vis/a000000/a004400/"\ 27 | "a004442/frames/730x730_1x1_30p/moon.0001.jpg" 28 | 29 | def test_gets_json_for_requested_datetime(self): 30 | m = Moon() 31 | m.set_moon_datetime(date="2019-01-01", hour=1) 32 | m.make_mooninfo_url() 33 | m.set_moon_datetime_info() 34 | assert m.info["time"] == '2019-01-01T01:00' 35 | 36 | def test_gets_moon_image_as_numpy_array(self): 37 | m = Moon() 38 | m.set_moon_datetime("2019-01-01") 39 | m.request_moon_image() 40 | self.assertIs(type(m.image), numpy.ndarray) 41 | 42 | def test_takes_optional_hour_arg(self): 43 | m = Moon() 44 | m.set_moon_datetime(hour=1) 45 | assert m.requested_datetime.hour == 1 46 | 47 | def test_can_get_last_hour_of_nonleap_year(self): 48 | m = Moon() 49 | m.set_moon_datetime(date="2019-12-31", hour=23) 50 | m.request_moon_image() 51 | 52 | assert m.image_src == "https://svs.gsfc.nasa.gov/vis/a000000/a004400/a004442/"\ 53 | "frames/730x730_1x1_30p/moon.8760.jpg" 54 | 55 | def test_can_get_last_hour_of_year(self): 56 | m = Moon() 57 | m.set_moon_datetime(date="2020-12-31", hour=23) 58 | m.request_moon_image() 59 | assert m.image_src == "https://svs.gsfc.nasa.gov/vis/a000000/a004700/a004768/"\ 60 | "frames/730x730_1x1_30p/moon.8784.jpg" 61 | 62 | def test_can_get_moon_for_2024(self): 63 | # note: this is just helpful for testing a version of `moon` where 64 | # the SVS_ID has been added to the github repo but the 65 | # package hasn't been updated to include the new SVS_ID 66 | m = Moon() 67 | m.set_moon_datetime(date="2024-01-15", hour=1) 68 | m.request_moon_image() 69 | assert m.image_src == "https://svs.gsfc.nasa.gov/vis/a000000/a005100/a005187/frames/730x730_1x1_30p/moon.0338.jpg" 70 | 71 | def test_gets_new_moon_info_if_another_year_requested(self): 72 | m = Moon() 73 | m.set_moon_phase("2019-01-15", 9) 74 | m.set_moon_phase("2020-01-15", 9) 75 | assert m.info['time'] == '2020-01-15T09:00' 76 | 77 | def test_no_errors_if_none_argument_for_date(self): 78 | m = Moon() 79 | m.set_moon_phase() 80 | assert m.requested_datetime 81 | 82 | 83 | if __name__ == '__main__': 84 | unittest.main() -------------------------------------------------------------------------------- /moon/dialamoon.py: -------------------------------------------------------------------------------- 1 | from moon.custom_image import CustomImage 2 | from moon.res.en.ui_messages import * 3 | from datetime import datetime, timezone, timedelta 4 | import urllib, urllib.request, json, sys, pkg_resources 5 | from functools import lru_cache 6 | 7 | 8 | # Get strings for URLs and IDs this Moon class will need 9 | # https://stackoverflow.com/questions/60687577/trying-to-read-json-file-within-a-python-package 10 | if sys.version_info >= (3, 7): 11 | import importlib.resources 12 | with importlib.resources.open_text("moon.res", "constants.json") as file: 13 | CONSTANTS_JSON_DICT = json.load(file) 14 | else: 15 | import pkg_resources 16 | resource_package = __name__ 17 | resource_path = '/'.join(('res', 'constants.json')) 18 | constants_string = pkg_resources.resource_string(resource_package, resource_path) 19 | CONSTANTS_JSON_DICT = json.loads(constants_string) 20 | 21 | class Moon(CustomImage): 22 | def __init__(self, size=(730,730)): 23 | self.size = size 24 | self.DIALAMOON_API_BASE_URL = CONSTANTS_JSON_DICT["DIALAMOON_API_BASE_URL"] 25 | super() 26 | return 27 | 28 | def __str__(self): 29 | return requested_datetime.strftime(self.requested_datetime,'%Y%m%d') 30 | 31 | def set_moon_phase(self, date=None, hour=None): 32 | try: 33 | self.set_moon_datetime(date, hour) 34 | self.request_moon_image() 35 | self.make_mooninfo_url() 36 | self.set_moon_datetime_info(requested_datetime=self.requested_datetime) 37 | if self.returned_datetime.year != self.requested_datetime.year: 38 | raise ValueError(YEAR_MISMATCH_ERROR.format( 39 | year_requested=self.requested_datetime.year, 40 | year_returned=self.returned_datetime.year)) 41 | except Exception as e: 42 | raise e 43 | return True 44 | 45 | def set_moon_datetime(self, date=None, hour=None): 46 | """ 47 | Keyword arguments: 48 | date -- UTC date in format YYYY-MM-DD, defaults to current date 49 | hour -- UTC hours 0 through 23, defaults to current hour 50 | """ 51 | try: 52 | self.requested_datetime = self.make_datetime(date, hour) 53 | self.image = None 54 | self.make_mooninfo_url() 55 | self.url = self.make_moon_image_url() 56 | except Exception as e: 57 | raise e 58 | return True 59 | 60 | def request_moon_image(self): 61 | try: 62 | self.image = self.set_image(url=self.url) 63 | except Exception as e: 64 | raise e 65 | return True 66 | 67 | def make_datetime(self, date, hour): 68 | # if hour and hour <= 0 or hour >= 23: 69 | # raise ValueError(HOUR_ERROR) 70 | if hour is None: 71 | hour = datetime.now(timezone.utc).hour 72 | 73 | if date is None: 74 | date = datetime.now(timezone.utc) 75 | else: 76 | date = datetime.strptime(date, '%Y-%m-%d').replace(tzinfo=timezone.utc) 77 | 78 | year, month, day = date.year, date.month, date.day 79 | 80 | return datetime(year=year, month=month, day=day, hour=hour).replace(tzinfo=timezone.utc) 81 | 82 | def make_moon_image_url(self): 83 | # "https://svs.gsfc.nasa.gov/api/dialamoon/{year}-{month}-{day}T{hour}:{minute}" 84 | response = urllib.request.urlopen(self.DIALAMOON_API_BASE_URL.format( 85 | year = "{:04d}".format(self.requested_datetime.year), 86 | month = "{:02d}".format(self.requested_datetime.month), 87 | day = "{:02d}".format(self.requested_datetime.day), 88 | hour = "{:02d}".format(self.requested_datetime.hour), 89 | minute = "{:02d}".format(self.requested_datetime.minute) 90 | )) 91 | 92 | moon_info_json = json.load(response) 93 | return moon_info_json["image"]["url"] 94 | 95 | def save(self, prefix="moon-image-"): 96 | date = datetime.strftime(self.requested_datetime,'%Y%m%d') 97 | self.save_to_disk(prefix + date) 98 | 99 | def get_moon_phase_date(self): 100 | return self.requested_datetime 101 | 102 | def make_mooninfo_url(self): 103 | self.url = self.DIALAMOON_API_BASE_URL.format( 104 | year = "{:04d}".format(self.requested_datetime.year), 105 | month = "{:02d}".format(self.requested_datetime.month), 106 | day = "{:02d}".format(self.requested_datetime.day), 107 | hour = "{:02d}".format(self.requested_datetime.hour), 108 | minute = "{:02d}".format(self.requested_datetime.minute) 109 | ) 110 | 111 | @lru_cache() 112 | def set_moon_datetime_info(self, requested_datetime=None): 113 | self.info = json.load(urllib.request.urlopen(self.url)) 114 | self.returned_datetime = datetime.strptime(self.info["time"], '%Y-%m-%dT%H:%M') 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | _..._ _..._ _..._ _..._ _..._ 3 | .:::::::. .::::. `. .:::: `. .::' `. .' `. 4 | ::::::::::: :::::::. : :::::: : ::: : : : 5 | ::::::::::: :::::::: : :::::: : ::: : : : 6 | `:::::::::' `::::::' .' `::::: .' `::. .' `. .' 7 | `':::'' `'::'-' `'::.-' `':..-' `-...-' 8 | 9 | _..._ _..._ _..._ _..._ _..._ 10 | .' `. .' `::. .' ::::. .' .::::. .:::::::. 11 | : : : ::: : :::::: : :::::::: ::::::::::: 12 | : : : ::: : :::::: : :::::::: ::::::::::: 13 | `. .' `. .::' `. :::::' `. '::::::' `:::::::::' 14 | `-...-' `-..:'' `-.::'' `-.::'' `':::'' 15 | ``` 16 | 17 | 18 | 19 | # moon 20 | 21 | This is a Python package that gets an image of a given date's moon phase. It uses Ernie Wright's moon visualizations from the Dial-a-Moon project at NASA's Scientific Visualization Studio. 22 | 23 | At the time of the last release, this package can access any of the moon visualizations from 2011 onward. 24 | 25 | ## Installation 26 | 27 | To install this package, just run: 28 | 29 | ```bash 30 | pip install moon 31 | ``` 32 | 33 | ## Usage 34 | 35 | This package retrieves a **NumPy.ndarray** representing the lunar phase, along with JSON data containing lunar statistics from NASA's Dial-a-Moon project. The image array can be manipulated with **OpenCV** and/or saved to disk as a `.jpg` file. 36 | 37 | ### Basic Usage 38 | 39 | ```python 40 | from moon.dialamoon import Moon 41 | 42 | moon = Moon() 43 | moon.set_moon_phase() 44 | ``` 45 | 46 | Access the image array with: 47 | 48 | ```python 49 | moon.image 50 | ``` 51 | 52 | Save the image to disk as `filename.jpg` with: 53 | 54 | ```python 55 | moon.save_to_disk('filename') 56 | ``` 57 | 58 | ### Jupyter Notebook Usage 59 | 60 | ```python 61 | from moon.jupyter_ui import JupyterUi 62 | 63 | ui = JupyterUi() 64 | ui.set_moon_phase() # Defaults to today's date 65 | print(ui.info) 66 | ui.show() 67 | ``` 68 | 69 | ### Resizing the Image 70 | 71 | By default, the returned image is **730x730 pixels**. To specify a different size, use the `size` keyword argument: 72 | 73 | ```python 74 | moon = Moon(size=(100, 100)) 75 | ``` 76 | 77 | ## OpenCV & NumPy Integration 78 | 79 | The inclusion of OpenCV and NumPy in this package is a remnant of earlier versions that supported additional image processing features. While currently not necessary for core functionality, these libraries allow for customization of images, such as 80 | 81 | - Adjusting brightness, contrast, applying filters 82 | - Overlaying text or graphics 83 | - Efficient in-memory image processing using NumPy arrays 84 | 85 | For example, you can use OpenCV to apply a grayscale filter: 86 | 87 | ```python 88 | import cv2 89 | import numpy as np 90 | 91 | image = moon.image 92 | gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 93 | cv2.imwrite("gray_moon.jpg", gray_image) 94 | ``` 95 | 96 | Since these dependencies are no longer required for core functionality, they will probably be removed in a future version. 97 | 98 | ## Additional Information 99 | 100 | You can access [more details and related lunar imagery](https://svs.gsfc.nasa.gov/help/#apis-dialamoon) via `Moon.info`. 101 | 102 | 103 | # Updates 104 | 105 | Please feel free to post bugs, suggestions and feature requests on this repo. 106 | 107 | ## 2.0.0 2024-02-01 108 | - use the new API to determine moon image urls, so yearly IDs don't need to be added anymore 109 | - deprecate image viewing via terminal 110 | ## 1.1.5 2021-12-30 111 | - put constants in a `.json` file instead of a `.py` file 112 | - add SVS ID for 2022 113 | - if a SVS ID for a year isn't available, check whether it's available on in `res/constants.json` on the GitHub repo and then remind the user to update the package for next time 114 | ## 1.1.3 2021-05-01 115 | - update numpy and opencv-python versions 116 | - fix lru_cache decorator to fix issue #4 117 | ## 1.1.2 2021-01-24 118 | - can include `hour` parameter in `Moon.set_moon_phase()` 119 | 120 | 121 | # Resources: 122 | - [nasa moon visualization studio](https://svs.gsfc.nasa.gov/4442) 123 | - [how to publish a python package on pypi](https://medium.com/@joel.barmettler/how-to-upload-your-python-package-to-pypi-65edc5fe9c56) 124 | 125 | 126 | moon ascii art courtesy of [jsg](http://www.ascii-art.de/ascii/mno/moon.txt) 127 | ``` 128 | _..._ _..._ _..._ _..._ _..._ 129 | .:::::::. .::::. `. .:::: `. .::' `. .' `. 130 | ::::::::::: :::::::. : :::::: : ::: : : : 131 | ::::::::::: :::::::: : :::::: : ::: : : : 132 | `:::::::::' `::::::' .' `::::: .' `::. .' `. .' 133 | `':::'' `'::'-' `'::.-' `':..-' `-...-' 134 | 135 | _..._ _..._ _..._ _..._ _..._ 136 | .' `. .' `::. .' ::::. .' .::::. .:::::::. 137 | : : : ::: : :::::: : :::::::: ::::::::::: 138 | : : : ::: : :::::: : :::::::: ::::::::::: 139 | `. .' `. .::' `. :::::' `. '::::::' `:::::::::' 140 | `-...-' `-..:'' `-.::'' `-.::'' `':::'' 141 | ``` 142 | 143 | 144 | 145 | 146 | --------------------------------------------------------------------------------