diff --git a/screenshots/about.png b/screenshots/about.png index 6b3dca3..ae634ce 100644 Binary files a/screenshots/about.png and b/screenshots/about.png differ diff --git a/screenshots/input_1.png b/screenshots/input_1.png index 1624d4c..42d9b79 100644 Binary files a/screenshots/input_1.png and b/screenshots/input_1.png differ diff --git a/screenshots/input_2.png b/screenshots/input_2.png index 61e2d7a..62f3087 100644 Binary files a/screenshots/input_2.png and b/screenshots/input_2.png differ diff --git a/screenshots/landing.png b/screenshots/landing.png index bb7a20b..501448c 100644 Binary files a/screenshots/landing.png and b/screenshots/landing.png differ diff --git a/screenshots/output.png b/screenshots/output.png index 1edc45f..4bb13e0 100644 Binary files a/screenshots/output.png and b/screenshots/output.png differ diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000..65d15e0 --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,526 @@ +/* ========================================= + Exoplanet Classifier - Cosmic Dark Theme + ========================================= */ + +:root { + /* Color Palette */ + --bg-dark: #0a0a0a; + --bg-card: rgba(255, 255, 255, 0.05); + --bg-card-hover: rgba(255, 255, 255, 0.08); + --accent-primary: #00BCFF; + --accent-secondary: #0077ff; + --text-main: #ffffff; + --text-muted: #a0a0a0; + --border-color: rgba(255, 255, 255, 0.1); + --glass-border: rgba(255, 255, 255, 0.15); + + /* Spacing & Layout */ + --container-width: 1200px; + --header-height: 80px; + --radius-lg: 24px; + --radius-md: 12px; + --radius-sm: 8px; + + /* Transitions */ + --transition-fast: 0.2s ease; + --transition-smooth: 0.4s cubic-bezier(0.4, 0, 0.2, 1); +} + +/* Reset & Base Styles */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Poppins', sans-serif; +} + +body { + background-color: var(--bg-dark); + color: var(--text-main); + line-height: 1.6; + overflow-x: hidden; + min-height: 100vh; + background-image: + radial-gradient(circle at 10% 20%, rgba(0, 188, 255, 0.05) 0%, transparent 40%), + radial-gradient(circle at 90% 80%, rgba(0, 119, 255, 0.05) 0%, transparent 40%); +} + +a { + text-decoration: none; + color: inherit; + transition: var(--transition-fast); +} + +ul { + list-style: none; +} + +/* Typography */ +h1, h2, h3, h4, h5, h6 { + font-family: 'Lexend Deca', sans-serif; + font-weight: 700; + line-height: 1.2; +} + +.text-gradient { + background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* ========================================= + Layout Components + ========================================= */ + +/* Header */ +.site-header { + height: var(--header-height); + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 5%; + background: rgba(10, 10, 10, 0.8); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + position: sticky; + top: 0; + z-index: 1000; + border-bottom: 1px solid var(--border-color); +} + +.logo-container { + display: flex; + align-items: center; + gap: 1rem; +} + +.nasa-logo { + height: 40px; + width: auto; +} + +.site-title { + font-size: 1.5rem; + letter-spacing: -0.5px; +} + +.site-title span { + font-size: 0.8rem; + color: var(--text-muted); + font-weight: 400; + display: block; + line-height: 1; + margin-top: 4px; +} + +.nav-link { + color: var(--accent-primary); + font-weight: 600; + position: relative; + padding: 0.5rem 0; +} + +.nav-link::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 0; + height: 2px; + background: var(--accent-primary); + transition: var(--transition-smooth); +} + +.nav-link:hover::after { + width: 100%; +} + +/* Main Container */ +.main-container { + max-width: var(--container-width); + margin: 0 auto; + padding: 2rem; +} + +/* ========================================= + Hero Section + ========================================= */ +.hero-section { + display: flex; + align-items: center; + justify-content: space-between; + min-height: calc(100vh - var(--header-height)); + gap: 4rem; + padding: 4rem 0; +} + +.hero-content { + flex: 1; +} + +.hero-subtitle { + color: var(--accent-primary); + font-weight: 600; + margin-bottom: 1rem; + text-transform: uppercase; + letter-spacing: 2px; + font-size: 0.9rem; +} + +.hero-title { + font-size: 4rem; + margin-bottom: 1.5rem; +} + +.hero-description { + color: var(--text-muted); + font-size: 1.1rem; + margin-bottom: 2.5rem; + max-width: 600px; +} + +.hero-image { + flex: 1; + display: flex; + justify-content: center; + position: relative; +} + +.hero-img-wrapper { + width: 100%; + max-width: 500px; + border-radius: var(--radius-lg); + overflow: hidden; + box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5); + position: relative; +} + +.hero-img-wrapper::before { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient(to bottom, transparent 50%, rgba(0, 188, 255, 0.2)); + pointer-events: none; +} + +.hero-img { + width: 100%; + height: auto; + display: block; + transform: scale(1.05); + transition: transform 10s ease; +} + +.hero-section:hover .hero-img { + transform: scale(1.1); +} + +/* Buttons */ +.btn { + display: inline-flex; + align-items: center; + gap: 0.8rem; + padding: 1rem 2rem; + border-radius: var(--radius-sm); + font-weight: 600; + cursor: pointer; + transition: var(--transition-smooth); + border: none; + font-size: 1rem; +} + +.btn-primary { + background: var(--text-main); + color: var(--bg-dark); + position: relative; + overflow: hidden; + z-index: 1; +} + +.btn-primary::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 0%; + height: 100%; + background: var(--accent-primary); + transition: var(--transition-smooth); + z-index: -1; +} + +.btn-primary:hover::before { + width: 100%; +} + +.btn-primary:hover { + color: var(--text-main); +} + +/* ========================================= + Form Section + ========================================= */ +.form-section { + padding: 6rem 0; + display: flex; + justify-content: center; +} + +.glass-card { + background: var(--bg-card); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border: 1px solid var(--glass-border); + border-radius: 40px; + padding: 4rem; + width: 100%; + max-width: 1000px; + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); +} + +.form-header { + text-align: center; + margin-bottom: 4rem; + position: relative; +} + +.form-title { + font-size: 3rem; +} + +.form-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 2rem; +} + +.input-group { + position: relative; + margin-bottom: 1rem; +} + +.input-field { + width: 100%; + background: transparent; + border: none; + border-bottom: 2px solid var(--border-color); + padding: 1rem 0; + color: var(--text-main); + font-size: 1rem; + transition: var(--transition-fast); +} + +.input-field:focus { + outline: none; + border-bottom-color: var(--accent-primary); +} + +.input-label { + position: absolute; + top: 1rem; + left: 0; + color: var(--text-muted); + pointer-events: none; + transition: var(--transition-fast); + font-size: 0.95rem; +} + +.input-field:focus ~ .input-label, +.input-field:not(:placeholder-shown) ~ .input-label { + top: -0.8rem; + font-size: 0.8rem; + color: var(--accent-primary); +} + +.form-actions { + grid-column: 1 / -1; + display: flex; + justify-content: center; + margin-top: 2rem; +} + +/* ========================================= + Modal + ========================================= */ +.modal-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.8); + backdrop-filter: blur(5px); + display: flex; + align-items: center; + justify-content: center; + z-index: 2000; + opacity: 0; + visibility: hidden; + transition: var(--transition-smooth); +} + +.modal-overlay.active { + opacity: 1; + visibility: visible; +} + +.modal-content { + background: #1a1a1a; + border: 1px solid var(--glass-border); + border-radius: var(--radius-lg); + padding: 2.5rem; + width: 90%; + max-width: 450px; + transform: translateY(20px); + transition: var(--transition-smooth); + position: relative; +} + +.modal-overlay.active .modal-content { + transform: translateY(0); +} + +.close-modal { + position: absolute; + top: 1.5rem; + right: 1.5rem; + background: none; + border: none; + color: var(--text-muted); + font-size: 1.5rem; + cursor: pointer; + transition: var(--transition-fast); +} + +.close-modal:hover { + color: var(--text-main); +} + +.result-header { + margin-bottom: 1.5rem; + text-align: center; +} + +.prediction-result { + font-size: 1.5rem; + color: var(--accent-primary); + margin-top: 0.5rem; +} + +.prob-item { + margin-bottom: 1rem; +} + +.prob-label { + display: flex; + justify-content: space-between; + font-size: 0.9rem; + margin-bottom: 0.4rem; + color: var(--text-muted); +} + +.prob-bar-bg { + height: 8px; + background: rgba(255, 255, 255, 0.1); + border-radius: 4px; + overflow: hidden; +} + +.prob-bar-fill { + height: 100%; + background: var(--accent-primary); + width: 0; + transition: width 1s cubic-bezier(0.4, 0, 0.2, 1); +} + +/* ========================================= + About Page Styles + ========================================= */ +.about-content { + background: var(--bg-card); + border: 1px solid var(--glass-border); + border-radius: var(--radius-lg); + padding: 3rem; + margin-top: 2rem; +} + +.about-section { + margin-bottom: 3rem; +} + +.about-section h2 { + color: var(--accent-primary); + margin-bottom: 1rem; + font-size: 1.8rem; +} + +.data-table { + width: 100%; + border-collapse: collapse; + margin-top: 1.5rem; + background: rgba(0, 0, 0, 0.2); + border-radius: var(--radius-sm); + overflow: hidden; +} + +.data-table th, +.data-table td { + padding: 1rem; + text-align: left; + border-bottom: 1px solid var(--border-color); +} + +.data-table th { + background: rgba(0, 188, 255, 0.1); + color: var(--accent-primary); + font-weight: 600; +} + +.data-table tr:last-child td { + border-bottom: none; +} + +.data-table tr:hover td { + background: rgba(255, 255, 255, 0.02); +} + +/* ========================================= + Responsive Design + ========================================= */ +@media (max-width: 968px) { + .hero-section { + flex-direction: column-reverse; + text-align: center; + padding-top: 2rem; + } + + .hero-content { + display: flex; + flex-direction: column; + align-items: center; + } + + .hero-title { + font-size: 3rem; + } + + .form-grid { + grid-template-columns: 1fr; + } + + .glass-card { + padding: 2rem; + } +} + +@media (max-width: 480px) { + .site-title { + font-size: 1.2rem; + } + + .hero-title { + font-size: 2.5rem; + } + + .form-title { + font-size: 2rem; + } +} diff --git a/static/js/main.js b/static/js/main.js new file mode 100644 index 0000000..de0f46e --- /dev/null +++ b/static/js/main.js @@ -0,0 +1,147 @@ +document.addEventListener('DOMContentLoaded', () => { + // Smooth Scroll + const scrollBtn = document.getElementById('scroll-to-form'); + if (scrollBtn) { + scrollBtn.addEventListener('click', (e) => { + e.preventDefault(); + document.getElementById('form-section').scrollIntoView({ + behavior: 'smooth' + }); + }); + } + + // Input Animation & Label Handling + const inputs = document.querySelectorAll('.input-field'); + inputs.forEach(input => { + // Trigger label animation on load if value exists + if (input.value) { + input.classList.add('has-value'); + } + + input.addEventListener('input', () => { + if (input.value.trim() !== '') { + input.classList.add('has-value'); + } else { + input.classList.remove('has-value'); + } + }); + }); + + // Form Submission + const form = document.getElementById('predictForm'); + const modal = document.getElementById('resultModal'); + const closeModalBtn = document.getElementById('closeModal'); + const resultsContainer = document.getElementById('resultsContainer'); + + if (form) { + form.addEventListener('submit', async (e) => { + e.preventDefault(); + + const submitBtn = form.querySelector('button[type="submit"]'); + const originalBtnText = submitBtn.innerHTML; + + // Loading State + submitBtn.innerHTML = ' Analyzing...'; + submitBtn.disabled = true; + + // Collect Data + const formData = {}; + inputs.forEach(input => { + formData[input.id] = parseFloat(input.value); + }); + + try { + const response = await fetch('/predict', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(formData) + }); + + const data = await response.json(); + + if (data.error) { + throw new Error(data.error); + } + + // Populate Results + displayResults(data); + openModal(); + + } catch (error) { + console.error('Error:', error); + alert('An error occurred during prediction. Please check your inputs.'); + } finally { + // Reset Button + submitBtn.innerHTML = originalBtnText; + submitBtn.disabled = false; + } + }); + } + + // Modal Functions + function openModal() { + modal.classList.add('active'); + document.body.style.overflow = 'hidden'; + } + + function closeModal() { + modal.classList.remove('active'); + document.body.style.overflow = ''; + } + + if (closeModalBtn) { + closeModalBtn.addEventListener('click', closeModal); + } + + // Close on click outside + if (modal) { + modal.addEventListener('click', (e) => { + if (e.target === modal) { + closeModal(); + } + }); + } + + // Helper: Display Results + function displayResults(data) { + const predictionEl = document.getElementById('predictionResult'); + const barsContainer = document.getElementById('probabilityBars'); + + // Set Prediction Text + predictionEl.textContent = data.prediction; + + // Clear previous bars + barsContainer.innerHTML = ''; + + // Sort probabilities + const sortedProbs = Object.entries(data.probabilities) + .sort(([,a], [,b]) => b - a); + + // Create Bars + sortedProbs.forEach(([label, prob]) => { + const percentage = (prob * 100).toFixed(1); + + const item = document.createElement('div'); + item.className = 'prob-item'; + + item.innerHTML = ` +
+ ${label} + ${percentage}% +
+
+
+
+ `; + + barsContainer.appendChild(item); + + // Animate bar after a slight delay + setTimeout(() => { + item.querySelector('.prob-bar-fill').style.width = `${percentage}%`; + }, 100); + }); + } +}); diff --git a/templates/about.html b/templates/about.html index 3d879bb..8ac2735 100644 --- a/templates/about.html +++ b/templates/about.html @@ -1,217 +1,148 @@ - - - - - - About - Exoplanet Classifier - - - - - -

Welcome!

- -

- The Exoplanet Classifier is a project developed by Team Ontohin 4b for the - NASA Space Apps Challenge 2025. -

- -

Objective

-

- The objective of this web app is to predict new exoplanet’s status - (CONFIRMED, CANDIDATE, FALSE POSITIVE) by evaluating user-given inputs. -

- -

How does it work?

-

- The user-given data is sent to the model via a Flask API endpoint. - Then, the model predicts the classes and also returns a probability for each class - (which you can see after hitting the “Predict” button). -

- -

- We have used the widely known Scikit-learn library to build it from the ground up. - More info about the ML part can be found at the README file of the repository. -

- -

- This is one of the most widely used models provided by Scikit-learn and also very suitable for our objective. - It predicts the result in three different classes: -

- - - -

- The model also returns a probability (up to 3 decimal digits) of its prediction for each of those 3 classes. -

- -

User Inputs

-

- The model is trained on 14 different features (including the output column) sourced from - NASA’s Kepler Object of Interest dataset. To run the prediction, the user needs to provide - 13 different values (all in real numbers) as input. Here is a list of them: -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Input FeatureUnitDescription
Orbital PeriodDaysTime the planet takes to complete one orbit around its star.
Transit EpochBJD - 2,454,833.0Reference time when the planet crosses in front of its star.
Transit Depthppm (parts per million)How much the star’s brightness dips during transit.
Planet RadiusEarth radiusSize of the planet compared to Earth.
Semi-Major AxisAU (approx)Average distance between the planet and its star.
InclinationDegreesTilt of the planet’s orbit relative to our view.
Equilibrium TemperatureKelvinEstimated average surface temperature of the planet.
Insolation FluxEarth fluxAmount of starlight the planet receives compared to Earth.
Impact ParameterNoneHow centrally the planet crosses the star (0 = center, 1 = edge).
Planet/Star Radius RatioNoneRatio of planet’s size to its star’s size.
Stellar Densitygm/cm³Average density of the host star.
Planet–Star DistanceR★ (stellar radii)Distance from the planet to the star, in units of stellar radii.
Number of TransitsNoneHow many times the planet has been observed passing in front of its star.
- -

Things to Keep in Mind

- - -

- For more information about the entire project, check the README file of the project repository. -

- -

- Thank you for trying out our work! We hope you enjoyed the concept and our idea to solve this astronomical - challenge with the power of machine learning. -

- -

Have a great day!

- -

- - Click here to visit the GitHub repository. +{% extends "base.html" %} + +{% block title %}About - Exoplanet Classifier{% endblock %} + +{% block content %} +

+
+

About the Project

+

+ The Exoplanet Classifier is an advanced machine learning tool developed by + Team Ontohin 4b for the NASA Space Apps Challenge 2025. + It leverages state-of-the-art ensemble algorithms to assist astronomers in identifying potential exoplanets. +

+
+ +
+

Objective

+

+ The primary goal is to automate the classification of transit signals. By analyzing light curves and orbital parameters, + the model categorizes candidates into three distinct classes: +

+
    +
  • CONFIRMED (2): Verified exoplanets.
  • +
  • CANDIDATE (1): Potential planets requiring further observation.
  • +
  • FALSE POSITIVE (0): Signals caused by other astrophysical phenomena.
  • +
+
+ +
+

Technical Architecture

+

+ Built on Scikit-learn, the system employs a Stacking Ensemble Classifier. + This combines the strengths of Random Forest and XGBoost, + orchestrated by a Logistic Regression meta-classifier. +

+ The backend is powered by Flask, serving a robust API that processes user inputs + and returns real-time predictions with probability confidence scores. +

+
+ +
+

Input Parameters

+

The model requires 13 specific orbital and transit features derived from NASA's Kepler mission data:

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureUnitDescription
Orbital PeriodDaysTime taken to complete one full orbit.
Transit EpochBJDTime of the center of the first transit.
Transit DepthppmFraction of stellar flux lost during transit.
Planet RadiusEarth RadiiEstimated radius of the planet.
Semi-Major AxisAUAverage distance from the host star.
InclinationDegreesAngle of the orbital plane.
Equilibrium TempKelvinTheoretical surface temperature.
Insolation FluxEarth FluxIncident solar radiation.
Impact Parameter-Sky-projected distance at conjunction.
Radius Ratio-Ratio of planet radius to star radius.
Stellar Densityg/cm³Density of the host star.
Planet-Star DistStellar RadiiDistance scaled by star size.
Num TransitsCountTotal number of transit events observed.
+
+
+ +
+

Disclaimer

+

+ While this model achieves high accuracy on the validation set, no ML model is infallible. + Predictions should be used as a preliminary screening tool rather than absolute confirmation. + The model is optimized for Kepler-like data distributions. +

+
+ +
+

+ + View Source on GitHub + +

+

+ © 2025 Ontohin 4b. Licensed under MIT. +

+ + Back to Classifier -

- -
- -

- This project is developed by the members of Ontohin 4b.
- Check the LICENSE file of the repository for copyright - information. -

- - ← Back to Prediction Page - - - \ No newline at end of file +
+
+{% endblock %} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..06055e1 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,43 @@ + + + + + + {% block title %}Exoplanet Classifier{% endblock %} + + + + + + + + + + + + + + + + + + +
+ {% block content %}{% endblock %} +
+ + + + + diff --git a/templates/index.html b/templates/index.html index 68241ec..3546341 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,464 +1,132 @@ - - - - - - - Exoplanet Classifier™ - - - - - - - - - - - -
-
- -
-

- Int. Space Apps Challenge - - Presented by Ontohin 4b - -

-
+{% extends "base.html" %} + +{% block title %}Home - Exoplanet Classifier{% endblock %} + +{% block content %} + +
+
+

Welcome to the Future

+

+ Discover New
+ Worlds +

+

+ Harness the power of Machine Learning to classify exoplanets. + Input transit data and let our advanced ensemble model determine if it's a + Confirmed Planet, Candidate, or False Positive. +

+ +
+ +
+
+ Exoplanet Art
- Learn more -
- -
- -
-
- -
-
-

Welcome, To The

-

Exoplanet-Classifier

-

- Developed by team Ontohin 4b. Here, you will need to provide - some numerical data (such as orbital period, transit depth etc.) and a specially trained ML model - will classify it under 3 classes: - Confirmed planet, Planetary Candidate or False Positive. -

- -
+
+ + + +
+
+
+

Input Parameters

+

Enter the transit details below to generate a prediction.

- -
-
-
-

- The
Exoplanet
Classifier -

-
-
- -
-

- Enter the details

- -
- -
- - -
+ + +
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
- -
-
+
+
+ +
+
+ + +
- - - - - - - - - - + +{% endblock %} \ No newline at end of file