Very simple weather app

This commit is contained in:
2024-07-19 19:08:23 +02:00
parent da519290c2
commit 88c8a378c1
5 changed files with 392 additions and 1 deletions
+2
View File
@@ -0,0 +1,2 @@
/.idea
/.env
+249
View File
@@ -1 +1,250 @@
# GoWeather # GoWeather
This is a simple weather application built with Go, featuring a web-based GUI. The app allows users to enter a city name and view the current temperature and weather conditions for that location.
## Table of Contents
1. [Features](#features)
2. [Prerequisites](#prerequisites)
3. [Installation](#installation)
4. [Usage](#usage)
5. [Code Structure](#code-structure)
6. [How It Works](#how-it-works)
7. [Customization](#customization)
8. [Contributing](#contributing)
9. [License](#license)
## Features
- Web-based user interface
- Real-time weather data fetching
- Display of temperature and weather conditions for a specified city
## Prerequisites
- Go (version 1.16 or later)
- OpenWeatherMap API key
## Installation
1. Clone this repository or download the source code.
2. Navigate to the project directory.
3. Replace `"YOUR_API_KEY_HERE"` in `main.go` with your actual OpenWeatherMap API key.
## Usage
1. Open a terminal and navigate to the project directory.
2. Run the following command to start the server:
```bash
go run main.go
```
3. Open a web browser and go to `http://localhost:8080`.
4. Enter a city name in the input field and click "Get Weather" to see the current weather information.
## Code Structure
The project consists of two main files:
1. `main.go`: The Go server that handles API requests and serves the web interface.
2. `index.html`: The HTML file that provides the user interface.
### main.go
This file contains the Go code for the server. Here's a breakdown of its main components:
#### WeatherData struct
```go
type WeatherData struct {
Main struct {
Temp float64 `json:"temp"`
} `json:"main"`
Weather []struct {
Description string `json:"description"`
} `json:"weather"`
}
```
This struct defines the structure for parsing the JSON response from the OpenWeatherMap API. It extracts the temperature and weather description from the API response.
#### main() function
````go
func main() {
http.HandleFunc("/", serveHome)
http.HandleFunc("/weather", handleWeather)
fmt.Println("Server is running on http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
````
The main() function sets up the HTTP server:
It registers two route handlers: one for the home page ("/") and one for weather data requests ("/weather").
It starts the server on port 8080 and logs any fatal errors.
#### serveHome() function
````go
func serveHome(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "index.html")
}
````
This function serves the index.html file when a request is made to the root ("/") of the server.
#### handleWeather() function
````go
func handleWeather(w http.ResponseWriter, r *http.Request) {
city := r.URL.Query().Get("city")
if city == "" {
http.Error(w, "City parameter is required", http.StatusBadRequest)
return
}
err := godotenv.Load(".env")
if err != nil {
log.Fatalf("Error loading .env file: %s", err)
}
apiKey := os.Getenv("openWeatherKey")
url := fmt.Sprintf("http://api.openweathermap.org/data/2.5/weather?q=%s&appid=%s&units=metric", city, apiKey)
resp, err := http.Get(url)
if err != nil {
http.Error(w, "Error fetching weather data", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
http.Error(w, "Error reading response body", http.StatusInternalServerError)
return
}
var weatherData WeatherData
err = json.Unmarshal(body, &weatherData)
if err != nil {
http.Error(w, "Error parsing JSON", http.StatusInternalServerError)
return
}
response := struct {
Temperature float64 `json:"temperature"`
Condition string `json:"condition"`
}{
Temperature: weatherData.Main.Temp,
Condition: weatherData.Weather[0].Description,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
````
The handleWeather() function processes weather data requests:
1) It extracts the city name from the query parameters.
2) It constructs the OpenWeatherMap API URL with the city name and API key.
3) It sends a GET request to the API and reads the response.
4) It parses the JSON response into the WeatherData struct.
5) It creates a new struct with the relevant weather information (temperature and condition).
6) Finally, it sends this data back to the client as a JSON response.
### index.html
This file contains the HTML, CSS, and JavaScript for the user interface. Key components include:
#### HTML Structure
```html
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Head content -->
</head>
<body>
<div class="container">
<h1>Weather App</h1>
<input type="text" id="cityInput" placeholder="Enter city name">
<button onclick="getWeather()">Get Weather</button>
<div id="weatherInfo"></div>
</div>
<script>
// JavaScript code
</script>
</body>
</html>
```
This structure creates a simple interface with an input field for the city name, a button to fetch weather data, and a div to display the weather information.
#### css Styles
```css
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
}
.container {
background-color: white;
padding: 20px;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
input, button {
margin: 10px 0;
padding: 5px;
}
</style>
```
These styles create a centered layout with a white container for the weather app interface.
#### JavaScript Function
```javascript
function getWeather() {
const city = document.getElementById('cityInput').value;
fetch(`/weather?city=${encodeURIComponent(city)}`)
.then(response => response.json())
.then(data => {
const weatherInfo = document.getElementById('weatherInfo');
weatherInfo.innerHTML = `
<h2>Weather in ${city}</h2>
<p>Temperature: ${data.temperature}°C</p>
<p>Condition: ${data.condition}</p>
`;
})
.catch(error => {
console.error('Error:', error);
const weatherInfo = document.getElementById('weatherInfo');
weatherInfo.innerHTML = '<p>Error fetching weather data</p>';
});
}
```
This function is called when the user clicks the "Get Weather" button:
1. It retrieves the city name from the input field.
2. It sends a fetch request to the `/weather` endpoint with the city name.
3. When it receives the response, it updates the `weatherInfo` div with the temperature and weather condition.
4. If there's an error, it displays an error message.
## How It Works
1. The user enters a city name and clicks "Get Weather".
2. The JavaScript function `getWeather()` sends a request to the `/weather` endpoint of the Go server.
3. The Go server receives the request and fetches weather data from the OpenWeatherMap API.
4. The server processes the API response and sends back the relevant weather information as JSON.
5. The JavaScript in the browser receives this data and updates the HTML to display the weather information.
## Customization
You can customize this app by:
- Modifying the HTML and CSS in `index.html` to change the appearance of the app.
- Adding more weather data fields in both the Go code and HTML to display additional information.
- Implementing error handling for cases like invalid city names or API errors.
## Contributing
Contributions to improve the app are welcome. Please feel free to submit a Pull Request.
## License
This project is open source and available under the [MIT License](LICENSE).
+5
View File
@@ -0,0 +1,5 @@
module GoWeather
go 1.22
require github.com/joho/godotenv v1.5.1 // indirect
+57
View File
@@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Weather App</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
}
.container {
background-color: white;
padding: 20px;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
input, button {
margin: 10px 0;
padding: 5px;
}
</style>
</head>
<body>
<div class="container">
<h1>Weather App</h1>
<input type="text" id="cityInput" placeholder="Enter city name">
<button onclick="getWeather()">Get Weather</button>
<div id="weatherInfo"></div>
</div>
<script>
function getWeather() {
const city = document.getElementById('cityInput').value;
fetch(`/weather?city=${encodeURIComponent(city)}`)
.then(response => response.json())
.then(data => {
const weatherInfo = document.getElementById('weatherInfo');
weatherInfo.innerHTML = `
<h2>Weather in ${city}</h2>
<p>Temperature: ${data.temperature}°C</p>
<p>Condition: ${data.condition}</p>
`;
})
.catch(error => {
console.error('Error:', error);
const weatherInfo = document.getElementById('weatherInfo');
weatherInfo.innerHTML = '<p>Error fetching weather data</p>';
});
}
</script>
</body>
</html>
+78
View File
@@ -0,0 +1,78 @@
package main
import (
"encoding/json"
"fmt"
"github.com/joho/godotenv"
"io/ioutil"
"log"
"net/http"
"os"
)
// Struct to hold the weather data
type WeatherData struct {
Main struct {
Temp float64 `json:"temp"`
} `json:"main"`
Weather []struct {
Description string `json:"description"`
} `json:"weather"`
}
func serveHome(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "index.html")
}
func handleWeather(w http.ResponseWriter, r *http.Request) {
city := r.URL.Query().Get("city")
if city == "" {
http.Error(w, "City parameter is required", http.StatusBadRequest)
return
}
err := godotenv.Load(".env")
if err != nil {
log.Fatalf("Error loading .env file: %s", err)
}
apiKey := os.Getenv("openWeatherKey")
url := fmt.Sprintf("http://api.openweathermap.org/data/2.5/weather?q=%s&appid=%s&units=metric", city, apiKey)
resp, err := http.Get(url)
if err != nil {
http.Error(w, "Error fetching weather data", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
http.Error(w, "Error reading response body", http.StatusInternalServerError)
return
}
var weatherData WeatherData
err = json.Unmarshal(body, &weatherData)
if err != nil {
http.Error(w, "Error parsing JSON", http.StatusInternalServerError)
return
}
response := struct {
Temperature float64 `json:"temperature"`
Condition string `json:"condition"`
}{
Temperature: weatherData.Main.Temp,
Condition: weatherData.Weather[0].Description,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
func main() {
http.HandleFunc("/", serveHome)
http.HandleFunc("/weather", handleWeather)
fmt.Println("Server is running on http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}