[FEATURE] Hiking Duration Calculator

This commit is contained in:
Pit Friedrich
2026-05-25 19:07:08 +02:00
commit 85ae5952a9
16 changed files with 781 additions and 0 deletions
@@ -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);
}
}
+3
View File
@@ -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() {
}
}