[FEATURE] Hiking Duration Calculator
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
package de.pitfriedrich.hikebuddy;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class HikebuddyApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(HikebuddyApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package de.pitfriedrich.hikebuddy.calculator;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
|
||||
@Controller
|
||||
public class HikeDurationController {
|
||||
|
||||
private final HikeDurationService hikeDurationService;
|
||||
|
||||
public HikeDurationController(HikeDurationService hikeDurationService) {
|
||||
this.hikeDurationService = hikeDurationService;
|
||||
}
|
||||
|
||||
@GetMapping("/")
|
||||
public String index() {
|
||||
return "redirect:/calculator";
|
||||
}
|
||||
|
||||
@GetMapping("/calculator")
|
||||
public String calculatorForm(Model model) {
|
||||
model.addAttribute("form", new HikeDurationForm());
|
||||
return "calculator";
|
||||
}
|
||||
|
||||
@PostMapping("/calculator")
|
||||
public String calculate(@ModelAttribute HikeDurationForm form, Model model) {
|
||||
model.addAttribute("form", form);
|
||||
if (form.getAltitudeDifference() != null && form.getDistance() != null) {
|
||||
HikeDurationService.DurationResult result = hikeDurationService.calculate(
|
||||
form.getAltitudeDifference(), form.getDistance());
|
||||
model.addAttribute("result", result);
|
||||
}
|
||||
return "calculator";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package de.pitfriedrich.hikebuddy.calculator;
|
||||
|
||||
public class HikeDurationForm {
|
||||
|
||||
private Integer altitudeDifference;
|
||||
private Double distance;
|
||||
|
||||
public Integer getAltitudeDifference() {
|
||||
return altitudeDifference;
|
||||
}
|
||||
|
||||
public void setAltitudeDifference(Integer altitudeDifference) {
|
||||
this.altitudeDifference = altitudeDifference;
|
||||
}
|
||||
|
||||
public Double getDistance() {
|
||||
return distance;
|
||||
}
|
||||
|
||||
public void setDistance(Double distance) {
|
||||
this.distance = distance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package de.pitfriedrich.hikebuddy.calculator;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class HikeDurationService {
|
||||
|
||||
public record DurationResult(long hours, long minutes) {}
|
||||
|
||||
public DurationResult calculate(int altitudeDifferenceMeters, double distanceKm) {
|
||||
double v1 = altitudeDifferenceMeters / 300.0;
|
||||
double v2 = distanceKm / 4.0;
|
||||
double min = Math.min(v1, v2);
|
||||
double max = Math.max(v1, v2);
|
||||
double durationHours = max + 0.5 * min;
|
||||
long totalMinutes = Math.round(durationHours * 60);
|
||||
return new DurationResult(totalMinutes / 60, totalMinutes % 60);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
spring:
|
||||
application:
|
||||
name: hikebuddy
|
||||
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>HikeBuddy — Duration Calculator</title>
|
||||
<style>
|
||||
body { font-family: sans-serif; max-width: 480px; margin: 48px auto; padding: 0 16px; }
|
||||
h1 { font-size: 1.5rem; margin-bottom: 8px; }
|
||||
h2 { font-size: 1.1rem; margin-bottom: 24px; color: #555; font-weight: normal; }
|
||||
label { display: block; margin-top: 16px; font-weight: bold; }
|
||||
input[type=number] { display: block; width: 100%; padding: 8px; margin-top: 4px; font-size: 1rem; box-sizing: border-box; border: 1px solid #ccc; border-radius: 4px; }
|
||||
button { margin-top: 24px; padding: 10px 24px; font-size: 1rem; background: #2d6a2d; color: white; border: none; border-radius: 4px; cursor: pointer; }
|
||||
button:hover { background: #235023; }
|
||||
.result { margin-top: 32px; padding: 16px; background: #f0f7f0; border-left: 4px solid #2d6a2d; border-radius: 4px; }
|
||||
.result span { font-size: 1.4rem; font-weight: bold; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>HikeBuddy</h1>
|
||||
<h2>Hike Duration Calculator</h2>
|
||||
|
||||
<form th:action="@{/calculator}" th:object="${form}" method="post">
|
||||
<label for="altitudeDifference">Altitude Difference (m)</label>
|
||||
<input type="number" id="altitudeDifference" th:field="*{altitudeDifference}" min="0" placeholder="e.g. 500" required>
|
||||
|
||||
<label for="distance">Distance (km)</label>
|
||||
<input type="number" id="distance" th:field="*{distance}" min="0" step="0.1" placeholder="e.g. 10" required>
|
||||
|
||||
<button type="submit">Calculate</button>
|
||||
</form>
|
||||
|
||||
<div class="result" th:if="${result}">
|
||||
Estimated duration:
|
||||
<span th:text="${result.hours() + 'h ' + result.minutes() + 'm'}"></span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,13 @@
|
||||
package de.pitfriedrich.hikebuddy;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class HikebuddyApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user