├── content.sqlite ├── ExoplanetsBubbleChartAll.png ├── ExoplanetsByYearHistogramD3.png ├── ExoplanetsBubbleChartHabitable.png ├── ExoplanetsBubbleChartAll_logScale.png ├── ExoplanetsBubbleChartHabitable_logScale.png ├── khistogram.py ├── khistogram.js ├── LICENSE ├── kbchart.htm ├── kbchart.py ├── README.md ├── .gitignore ├── khistogram.htm ├── kbchart.js └── kmane.py /content.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keen2/Habitable-exoplanets-visualisation/HEAD/content.sqlite -------------------------------------------------------------------------------- /ExoplanetsBubbleChartAll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keen2/Habitable-exoplanets-visualisation/HEAD/ExoplanetsBubbleChartAll.png -------------------------------------------------------------------------------- /ExoplanetsByYearHistogramD3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keen2/Habitable-exoplanets-visualisation/HEAD/ExoplanetsByYearHistogramD3.png -------------------------------------------------------------------------------- /ExoplanetsBubbleChartHabitable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keen2/Habitable-exoplanets-visualisation/HEAD/ExoplanetsBubbleChartHabitable.png -------------------------------------------------------------------------------- /ExoplanetsBubbleChartAll_logScale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keen2/Habitable-exoplanets-visualisation/HEAD/ExoplanetsBubbleChartAll_logScale.png -------------------------------------------------------------------------------- /ExoplanetsBubbleChartHabitable_logScale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keen2/Habitable-exoplanets-visualisation/HEAD/ExoplanetsBubbleChartHabitable_logScale.png -------------------------------------------------------------------------------- /khistogram.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | # Open the main content (Read only) 3 | conn = sqlite3.connect('file:content.sqlite?mode=ro', uri=True) 4 | cur = conn.cursor() 5 | 6 | cur.execute('SELECT pl_disc FROM Exoplanets') 7 | years = {} 8 | for row in cur: 9 | years[row[0]] = years.get(row[0],0) + 1 10 | years_list = sorted(years) 11 | 12 | fhand = open('khistogram.js','w') 13 | fhand.write("khistogram = [") 14 | first = True 15 | for year in years_list: 16 | if not first : fhand.write( ",\n") 17 | first = False 18 | fhand.write("{year: '"+str(year)+"', amount: "+str(years[year])+"}") 19 | fhand.write( "\n];\n") 20 | fhand.close() 21 | 22 | print("Output written to khistogram.js") 23 | print("Open khistogram.htm in a browser to see the vizualization") 24 | 25 | cur.close() 26 | -------------------------------------------------------------------------------- /khistogram.js: -------------------------------------------------------------------------------- 1 | khistogram = [{year: '1989', amount: 1}, 2 | {year: '1992', amount: 2}, 3 | {year: '1994', amount: 1}, 4 | {year: '1995', amount: 1}, 5 | {year: '1996', amount: 6}, 6 | {year: '1997', amount: 1}, 7 | {year: '1998', amount: 6}, 8 | {year: '1999', amount: 13}, 9 | {year: '2000', amount: 16}, 10 | {year: '2001', amount: 12}, 11 | {year: '2002', amount: 30}, 12 | {year: '2003', amount: 23}, 13 | {year: '2004', amount: 27}, 14 | {year: '2005', amount: 36}, 15 | {year: '2006', amount: 31}, 16 | {year: '2007', amount: 53}, 17 | {year: '2008', amount: 67}, 18 | {year: '2009', amount: 95}, 19 | {year: '2010', amount: 98}, 20 | {year: '2011', amount: 136}, 21 | {year: '2012', amount: 137}, 22 | {year: '2013', amount: 124}, 23 | {year: '2014', amount: 861}, 24 | {year: '2015', amount: 156}, 25 | {year: '2016', amount: 1505}, 26 | {year: '2017', amount: 71} 27 | ]; 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Andrey Ermishin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /kbchart.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 24 | 25 | 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /kbchart.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | # Open the main content (Read only) 3 | conn = sqlite3.connect('file:content.sqlite?mode=ro', uri=True) 4 | cur = conn.cursor() 5 | 6 | cur.execute('SELECT pl_name,pl_bmasse,pl_rade,pl_eqt,st_dist FROM Exoplanets') 7 | 8 | with open('kbchart.js','w') as fhand: 9 | 10 | fhand.write("kbchart = [") 11 | 12 | first = True 13 | for row in cur: 14 | if first : 15 | fhand.write("[ 'Planet Name', 'Distance'," + 16 | " 'Mass', 'Temp.', 'Radius']") 17 | first = False 18 | continue 19 | 20 | # if Radius and Temperature are not null 21 | if row[2] == 'null' or row[3] == 'null' : continue 22 | # Find habitabale planets (similar to Earth) 23 | if 0.5 < row[2] < 5 and 150 < row[3] < 300: 24 | fhand.write(",\n['"+row[0].rjust(52)+"', "+str(row[4]).rjust(11)+", " + 25 | str(row[1]).rjust(13)+", "+str(row[3]).rjust(8)+", "+str(row[2]).rjust(10)+"]") 26 | # Add Earth to file (on chart) as example with test distance 27 | fhand.write(",\n[' EARTH'," + 28 | " 3.0, 1.0, 288, 1.0]") 29 | fhand.write("\n];\n") 30 | 31 | print("Output written to kbchart.js") 32 | print("Open kbchart.htm in a browser to see the vizualization") 33 | 34 | cur.close() 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Habitable-exoplanets-visualisation 2 | 3 | The project can be found also here [GSFC Exoplanet Modeling and Analysis Center (EMAC)](https://emac.gsfc.nasa.gov/?q=habitable) 4 |


5 | Visualization of the open data from NASA Exoplanets Archive of planets outside the solar system that are similar to the Earth and habitable. 6 | 7 | Using the open data from NASA Exoplanets Archive (https://exoplanetarchive.ipac.caltech.edu/index.html). By means of NASA API the planets outside the solar system that are similar to the Earth and habitable were parsed and stored to SQLite database: 8 | "content.sqlite" 9 | 10 | There are two visualizations of the parsed data: 11 | 1. "khistogram.py" - via Python 3 it samples data into "khistogram.js"; 12 | 2. "khistogram.htm" - uses D3.js library to visualize and "khistogram.js" with data needed; 13 | 14 | 3. "kbchart.py" - via Python 3 it samples data into "kbchart.js" in special order; 15 | 4. "kbchart.htm" - uses Google BubbleChart to visualize and "kbchart.js" with data needed; 16 | 17 | The Temperature in K. For clarity, the Earth was placed nearby the origin, but so that it could be seen. Axises are logarithmic. 18 | 19 | ![exoplanetsbubblechartall_logscale222](https://user-images.githubusercontent.com/16411126/30125828-6b35ff00-9342-11e7-8328-146ac610f7a4.png) 20 | 21 | ![exoplanetsbubblecharthabitable_logscale222](https://user-images.githubusercontent.com/16411126/30125836-729b72e8-9342-11e7-86c5-5cb08459b694.png) 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /khistogram.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 30 | 31 | 32 | 87 | 88 | -------------------------------------------------------------------------------- /kbchart.js: -------------------------------------------------------------------------------- 1 | kbchart = [[ 'Planet Name', 'Distance', 'Mass', 'Temp.', 'Radius'], 2 | [' K2-18 b', 34.0, null, 272, 2.24], 3 | [' Kepler-22 b', 190.0, 36.0, 262, 2.38], 4 | [' Kepler-62 f', 368.0, 35.0, 208, 1.41], 5 | [' Kepler-69 c', null, null, 299, 1.71], 6 | [' Kepler-1410 b', 367.0, 2.57, 274, 1.78], 7 | [' TRAPPIST-1 d', 12.1, 0.41, 288, 0.772], 8 | [' GJ 832 c', 4.94, 5.4, 253, 1.5], 9 | [' Kepler-442 b', 342.0, 2.3, 233, 1.34], 10 | [' Kepler-421 b', 320.0, null, 185, 4.16], 11 | [' Kepler-61 b', null, null, 273, 2.15], 12 | [' Kepler-62 e', 368.0, 36.0, 270, 1.61], 13 | [' Kepler-438 b', 145.0, 1.3, 276, 1.12], 14 | [' Kepler-452 b', 430.0, 5.0, 265, 1.63], 15 | [' K2-3 d', 42.0, null, 282, 1.51], 16 | [' Kepler-296 e', 226.0, 1.0, 267, 1.53], 17 | [' TRAPPIST-1 e', 12.1, 0.62, 251, 0.918], 18 | [' TRAPPIST-1 f', 12.1, 0.68, 219, 1.045], 19 | [' TRAPPIST-1 g', 12.1, 1.34, 199, 1.127], 20 | [' TRAPPIST-1 h', 12.1, null, 173, 0.752], 21 | [' LHS 1140 b', 12.47, 6.65, 230, 1.43], 22 | [' EARTH', 3.0, 1.0, 288, 1.0] 23 | ]; 24 | -------------------------------------------------------------------------------- /kmane.py: -------------------------------------------------------------------------------- 1 | # \ 2 | # \fixlen = T 3 | # \RowsRetrieved = 3502 4 | # \For detailed descriptions of the columns, go to http://exoplanetarchive.ipac.caltech.edu/docs/documentation.html and choose the appropriate table guide. 5 | # \ 6 | # \ pl_name 7 | # \ ___ Planet Common Name 8 | # ... 9 | # \ pl_disc 10 | # \ ___ Year of Discovery 11 | # \ 12 | # | pl_name| pl_bmasse| pl_rade| pl_eqt| pl_orbper| st_dist| pl_disc| 13 | # | char| double| double| long| double| double| int| 14 | # | | Mearth| Rearth| K| days| pc| | 15 | # | null| null| null| null| null| null| null| 16 | # HD 4732 b 753.23000 null null 360.20000000 56.50 2012 17 | 18 | import sqlite3 19 | import time 20 | import ssl 21 | import urllib.request, urllib.parse, urllib.error 22 | from urllib.parse import urljoin 23 | from urllib.parse import urlparse 24 | import re 25 | 26 | # Ignore SSL certificate errors 27 | ctx = ssl.create_default_context() 28 | ctx.check_hostname = False 29 | ctx.verify_mode = ssl.CERT_NONE 30 | 31 | # Create database for data 32 | conn = sqlite3.connect('content.sqlite') 33 | cur = conn.cursor() 34 | 35 | # Prepare parameters for API query 36 | baseurl = "https://exoplanetarchive.ipac.caltech.edu/cgi-bin/nstedAPI/nph-nstedAPI?table=" 37 | table = "exoplanets" 38 | col_names = "&select=pl_name,pl_bmasse,pl_rade,pl_eqt,pl_orbper,st_dist,pl_disc" 39 | params = "" 40 | out_format = "&format=ascii" 41 | 42 | url = baseurl + table + col_names + params + out_format 43 | 44 | # Get data from URL 45 | text = "None" 46 | try: 47 | # Open with a timeout of 30 seconds 48 | document = urllib.request.urlopen(url, None, 30, context=ctx) 49 | text = document.read().decode() 50 | if document.getcode() != 200 : 51 | print("Error code=",document.getcode(), url) 52 | except KeyboardInterrupt: 53 | print('') 54 | print('Program interrupted by user...') 55 | except Exception as e: 56 | print("Unable to retrieve or parse page",url) 57 | print("Error",e) 58 | 59 | # Form table with SQLite 60 | cur.execute('''DROP TABLE IF EXISTS Exoplanets ''') 61 | cur.execute('''CREATE TABLE IF NOT EXISTS Exoplanets 62 | (id INTEGER UNIQUE, pl_name TEXT, pl_bmasse REAL, pl_rade REAL, 63 | pl_eqt BIGINT, pl_orbper REAL, st_dist REAL, pl_disc INTEGER)''') 64 | 65 | 66 | # Parse data retrieved by rows 67 | count = 0 68 | rows = 0 69 | columns = [] # list of tuples (name, fullname) 70 | name, fullname = "None", "None" 71 | for line in text.splitlines(): 72 | # Line breaks are not included - "splitlines()" 73 | 74 | line = line.strip() 75 | # Parse headers 76 | if line.startswith('\\'): 77 | # Some meta info 78 | if not line.startswith('\\ '): 79 | if line.startswith('\\Rows'): 80 | pos = line.find('=') 81 | try: 82 | # In Python 3 long int is included to int() 83 | rows = int(line[pos+1:].lstrip()) 84 | except: 85 | pass 86 | print("Rows: ", rows) 87 | print("URL: ", url) 88 | print('\nFirst 5 rows of data retrieved:') 89 | 90 | # Column names 91 | else: 92 | if not line.startswith('\\ ___'): 93 | if name == "None": name = line.split()[1] 94 | else: 95 | if fullname == "None": 96 | pos = line.rfind('_') + 1 97 | pos2 = line.rfind('[') 98 | fullname = line[pos:].strip() if pos2 == -1 else line[pos:pos2-1].strip() 99 | 100 | if name != "None" and fullname != "None": 101 | columns.append((name, fullname)) 102 | name, fullname = "None", "None" 103 | continue 104 | 105 | # Parse data after headers 106 | if line.startswith('|'): continue 107 | # Split line if delimiter includes 2 or more whitespaces 108 | s = re.split(' +', line) 109 | 110 | count += 1 111 | # First 5 rows of data retrieved 112 | if count <= 5: print(s) 113 | 114 | # Data update for planets with high Planetary habitability 115 | #1 116 | if s[0] == 'Kepler-438 b': 117 | if s[1] == 'null' : s[1] = '1.30000' 118 | if s[3] == 'null' : s[3] = '276' 119 | #2 120 | if s[0] == 'Kepler-296 e': 121 | if s[1] == 'null' : s[1] = '1.00000' # mass unknown 122 | if s[3] == 'null' : s[3] = '267' 123 | #3 124 | if s[0] == 'GJ 667 C c': 125 | if s[1] == 'null' : s[1] = '3.70900' 126 | if s[3] == 'null' : s[3] = '277' 127 | #4 128 | if s[0] == 'Kepler-442 b': 129 | if s[1] == 'null' : s[1] = '2.30000' 130 | if s[3] == 'null' : s[3] = '233' 131 | #5 132 | if s[0] == 'GJ 832 c': 133 | if s[2] == 'null' : s[2] = '1.50000' 134 | if s[3] == 'null' : s[3] = '253' 135 | #6 136 | if s[0] == 'Kepler-452 b': 137 | if s[1] == 'null' : s[1] = '5.00000' 138 | if s[5] == 'null' : s[5] = '430.00' 139 | #7 140 | if s[0] == 'Kepler-1410 b': 141 | if s[1] == 'null' : s[1] = '2.57000' 142 | if s[3] == 'null' : s[3] = '274' 143 | 144 | # (To paste values to all columns in right order there is no need to write column names) 145 | cur.execute('''INSERT OR IGNORE INTO Exoplanets (id, pl_name, pl_bmasse, pl_rade, 146 | pl_eqt, pl_orbper, st_dist, pl_disc) 147 | VALUES ( ?, ?, ?, ?, ?, ?, ?, ? )''', ( count, s[0], s[1], s[2], s[3], s[4], s[5], s[6])) 148 | # No conversion required: the column type name affects how values are processed before being stored 149 | 150 | if count % 500 == 0 : conn.commit() 151 | 152 | conn.commit() 153 | cur.close() 154 | --------------------------------------------------------------------------------