School: University of the People
Moodle ID: C110223808
Course: CS 1103 – Programming 2
Assignment: Unit 8 – Advanced GUI programming.
University: University of the People
Weather Information App — Java (JavaFX)
Overview
This app provides real-time weather for a given city using the OpenWeatherMap API. It
shows current temperature, humidity, wind, and conditions, plus a short-term forecast. It
includes unit switching, error handling, search history, and dynamic backgrounds based
on time of day.
How to Run (Eclipse + JavaFX)
1) Install JDK 17 or later. 2) Set up JavaFX in Eclipse. 3) Create a project named
WeatherApp. 4) Copy the provided src files into your project. 5) Add [Link] jar to the
build path. 6) Open [Link] and paste your OpenWeatherMap API key
where indicated. 7) Run [Link].
Grading Alignment (Checklist)
API Integration: Uses OpenWeatherMap with API key and JSON parsing.
GUI Design: JavaFX UI with inputs, lists, and visual icons.
Logic & Computation: JSON parsing, unit selection, history storage.
Program Flow & Structure: Service + model + UI classes.
Output: Current weather, icon, and next 24h forecast list.
Code Style & Readability: Commented classes and clear method names.
Source Code
[Link]
package app;
import [Link];
import [Link];
import [Link];
import [Link];
import [Link].*;
import [Link];
import [Link];
import [Link].*;
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public class WeatherApp extends Application {
private TextField cityInput;
private ComboBox<String> unitCombo;
private Label tempLabel;
private Label humidityLabel;
private Label windLabel;
private Label conditionLabel;
private ImageView iconView;
private ListView<String> forecastList;
private ListView<String> historyList;
private final ObservableList<String> historyItems =
[Link]();
private final WeatherService service = new WeatherService();
private final HistoryManager historyManager = new HistoryManager();
@Override
public void start(Stage stage) {
// Top: input row
cityInput = new TextField();
[Link]("Enter city (e.g., Nairobi)");
[Link](18);
unitCombo = new ComboBox<>();
[Link]().addAll("Celsius / m/s", "Fahrenheit / mph");
[Link]().select(0);
Button fetchBtn = new Button("Get Weather");
[Link](e -> fetchWeather());
HBox inputRow = new HBox(10, new Label("Location:"), cityInput, unitCombo,
fetchBtn);
[Link](Pos.CENTER_LEFT);
[Link](new Insets(12));
// Center: current weather card
tempLabel = new Label("--");
[Link]([Link](28));
humidityLabel = new Label("Humidity: --");
windLabel = new Label("Wind: --");
conditionLabel = new Label("Condition: --");
iconView = new ImageView();
[Link](80);
[Link](80);
[Link](true);
VBox currentBox = new VBox(6, tempLabel, conditionLabel, humidityLabel,
windLabel);
HBox currentRow = new HBox(12, iconView, currentBox);
[Link](Pos.CENTER_LEFT);
TitledPane currentPane = new TitledPane("Current Weather", currentRow);
[Link](false);
// Forecast list
forecastList = new ListView<>();
[Link](220);
TitledPane forecastPane = new TitledPane("Short-term Forecast (next 24h)",
forecastList);
[Link](false);
VBox center = new VBox(10, currentPane, forecastPane);
[Link](new Insets(12));
// Right: history
historyList = new ListView<>(historyItems);
[Link](260);
[Link](e -> {
String sel = [Link]().getSelectedItem();
if (sel != null && [Link](" - ")) {
String city = [Link](" - ")[0];
[Link](city);
fetchWeather();
});
VBox right = new VBox(8, new Label("Search History"), historyList);
[Link](new Insets(12));
BorderPane root = new BorderPane();
[Link](inputRow);
[Link](center);
[Link](right);
Scene scene = new Scene(root, 920, 540);
applyDynamicBackground(scene, [Link]());
[Link]("Weather Information App — JavaFX");
[Link](scene);
[Link]();
// Load history
[Link]([Link]());
private void fetchWeather() {
String city = [Link]().trim();
if ([Link]()) {
showError("Please enter a city name.");
return;
boolean useFahrenheit = [Link]().getSelectedIndex() == 1;
String units = useFahrenheit ? "imperial" : "metric";
try {
WeatherModel current = [Link](city, units);
List<[Link]> forecast = [Link](city, units, 8);
// next 24 hours (8 * 3h)
// Display current
String tempText = useFahrenheit
? [Link]("%.1f °F", [Link])
: [Link]("%.1f °C", [Link]);
[Link](tempText);
[Link]("Humidity: " + [Link] + " %");
String windText = useFahrenheit
? [Link]("Wind: %.1f mph", [Link])
: [Link]("Wind: %.1f m/s", [Link]);
[Link](windText);
[Link]("Condition: " + [Link]);
// Icon
Image icon = [Link]([Link]);
[Link](icon);
// Forecast
[Link]().clear();
DateTimeFormatter fmt = [Link]("EEE HH:mm");
for ([Link] f : forecast) {
String t = useFahrenheit
? [Link]("%.1f °F", [Link])
: [Link]("%.1f °C", [Link]);
LocalDateTime local = [Link]([Link], 0,
[Link]([Link]));
String line = [Link]("%s | %s | %s | wind %.1f%s",
[Link](fmt), t, [Link], [Link],
useFahrenheit ? " mph" : " m/s");
[Link]().add(line);
// Dynamic background based on local time at location
LocalDateTime localNow =
[Link]([Link]().getEpochSecond() +
[Link], 0, [Link]);
applyDynamicBackground([Link](), localNow);
// History
String entry = city + " - " + [Link]("yyyy-MM-dd
HH:mm").format([Link]());
[Link](0, entry);
[Link](entry);
} catch (Exception ex) {
showError("Could not fetch weather. " + [Link]());
private void applyDynamicBackground(Scene scene, LocalDateTime localTime) {
int hour = [Link]();
String style;
if (hour >= 5 && hour < 11) {
style = "-fx-background-color: linear-gradient(to bottom, #cfe9ff, #ffffff);";
} else if (hour >= 11 && hour < 17) {
style = "-fx-background-color: linear-gradient(to bottom, #a8d5ff, #e8f4ff);";
} else if (hour >= 17 && hour < 20) {
style = "-fx-background-color: linear-gradient(to bottom, #ffd1a8, #ffe9d6);";
} else {
style = "-fx-background-color: linear-gradient(to bottom, #0b1b3a, #122a52);";
[Link]().setStyle(style);
private void showError(String msg) {
Alert alert = new Alert([Link], msg, [Link]);
[Link]("Input or Network Error");
[Link]();
public static void main(String[] args) {
launch(args);
}
}
[Link]
package app;
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public class WeatherService {
private static final String API_KEY = "YOUR_OPENWEATHERMAP_API_KEY";
public WeatherModel fetchCurrentWeather(String city, String units) throws Exception
String url = [Link](
"[Link]
encode(city), API_KEY, units
);
JSONObject json = getJson(url);
return parseCurrent(json);
public List<[Link]> fetchForecast(String city, String units, int
maxItems) throws Exception {
String url = [Link](
"[Link]
encode(city), API_KEY, units
);
JSONObject json = getJson(url);
return parseForecast(json, maxItems);
private JSONObject getJson(String urlStr) throws Exception {
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) [Link]();
[Link]("GET");
[Link](8000);
[Link](8000);
int code = [Link]();
if (code != 200) {
throw new RuntimeException("API request failed with HTTP " + code);
BufferedReader reader = new BufferedReader(
new InputStreamReader([Link](), StandardCharsets.UTF_8)
);
StringBuilder sb = new StringBuilder();
String line;
while ((line = [Link]()) != null) [Link](line);
[Link]();
[Link]();
return new JSONObject([Link]());
private WeatherModel parseCurrent(JSONObject json) {
WeatherModel model = new WeatherModel();
JSONObject main = [Link]("main");
JSONObject wind = [Link]("wind");
JSONArray weatherArr = [Link]("weather");
JSONObject weather = [Link](0);
[Link] = [Link]("temp");
[Link] = [Link]("humidity");
[Link] = [Link]("speed", 0.0);
[Link] = capitalize([Link]("description"));
[Link] = [Link]("icon");
[Link] = [Link]("timezone", 0);
[Link] = [Link]("name");
return model;
private List<[Link]> parseForecast(JSONObject json, int
maxItems) {
List<[Link]> out = new ArrayList<>();
JSONArray arr = [Link]("list");
int count = [Link]([Link](), [Link](1, maxItems));
for (int i = 0; i < count; i++) {
JSONObject o = [Link](i);
long dt = [Link]("dt");
JSONObject main = [Link]("main");
double temp = [Link]("temp");
JSONObject wind = [Link]("wind");
double ws = [Link]("speed", 0.0);
JSONArray weatherArr = [Link]("weather");
JSONObject w = [Link](0);
String desc = capitalize([Link]("description"));
[Link](new [Link](dt, temp, ws, desc));
return out;
private String encode(String s) {
return [Link](" ", "%20");
private String capitalize(String s) {
if (s == null || [Link]()) return s;
return [Link](0,1).toUpperCase() + [Link](1);
}
[Link]
package app;
import [Link];
import [Link];
public class WeatherModel {
public String city;
public double temperature;
public int humidity;
public double windSpeed;
public String description;
public String iconCode;
public int timezoneOffset; // seconds
public static class ForecastItem {
public long dt; // epoch seconds (UTC)
public double temp;
public double wind;
public String description;
public ForecastItem(long dt, double temp, double wind, String description) {
[Link] = dt;
[Link] = temp;
[Link] = wind;
[Link] = description;
public List<ForecastItem> forecast = new ArrayList<>();
[Link]
package app;
import [Link];
public class IconMapper {
public static Image iconFromCode(String code) {
try {
String url = [Link]("[Link]
code);
return new Image(url, true);
} catch (Exception e) {
return null;
}
}
[Link]
package app;
import [Link].*;
import [Link];
import [Link];
import [Link];
public class HistoryManager {
private final File store = new File([Link]("[Link]"),
".weatherapp_history.txt");
public List<String> loadHistory() {
List<String> list = new ArrayList<>();
if (![Link]()) return list;
try (BufferedReader br = new BufferedReader(new InputStreamReader(new
FileInputStream(store), StandardCharsets.UTF_8))) {
String line;
while ((line = [Link]()) != null) {
if (![Link]().isEmpty()) [Link](0, [Link]());
}
} catch (IOException ignored) {}
return list;
public void appendEntry(String entry) {
try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new
FileOutputStream(store, true), StandardCharsets.UTF_8))) {
[Link](entry);
[Link]();
} catch (IOException ignored) {}
[Link]
module WeatherApp {
requires [Link];
requires [Link];
requires [Link];
requires [Link];
opens app;
}
README (Quick Reference)
# Weather Information App (JavaFX)
A simple JavaFX app that fetches real-time weather and a short-term forecast using the
OpenWeatherMap API.
It includes:
- City input and unit switch (Celsius/metric or Fahrenheit/imperial)
- Current weather with icon
- Short-term forecast (next ~24 hours)
- Search history with timestamps and click-to-reload
- Dynamic backgrounds by time of day
- Basic error handling for invalid input and network/API failures
## Requirements
- JDK 17 or later
- JavaFX SDK (matching your JDK) added to Eclipse
- [Link] library (e.g., `[Link]`)
## Setup in Eclipse
1. Create a JavaFX project named `WeatherApp`.
2. Add the `src` folder contents into `src/app` in your project.
3. Place `[Link]` at the root `src`.
4. Add JavaFX libraries to your project build path.
5. Download `[Link]` jar and add it to your project (Project Properties > Java Build
Path > Libraries).
6. Open `[Link]` and replace `YOUR_OPENWEATHERMAP_API_KEY`
with your key.
7. Run `[Link]`.
## Usage
- Enter a city like `Nairobi` and click **Get Weather**.
- Switch units from the combo box.
- Click history entries to reload.