├── 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 | 
20 |
21 | 
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 |
--------------------------------------------------------------------------------