--- /dev/null
+.classpath
+.project
+.settings
+bin
+target
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>de.laktatnebel.service</groupId>
+ <artifactId>runningsplits</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <packaging>war</packaging>
+
+ <name>runningsplits</name>
+ <description>Split Run Times</description>
+
+ <parent>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-parent</artifactId>
+ <version>2.4.3</version>
+ <relativePath /> <!-- lookup parent from repository -->
+ </parent>
+
+ <properties>
+ <java.version>15</java.version>
+ <jaxb-api.version>2.3.1</jaxb-api.version>
+ <json.version>20230227</json.version>
+ <utillib.version>2.1.0</utillib.version>
+
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+
+ <maven.compiler.source>${java.version}</maven.compiler.source>
+ <maven.compiler.target>${java.version}</maven.compiler.target>
+
+ <maven-enforcer-plugin.version>3.3.0</maven-enforcer-plugin.version>
+
+ <maven-source-plugin.version>3.2.1</maven-source-plugin.version>
+ <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
+ <maven-surefire-plugin.version>3.0.0</maven-surefire-plugin.version>
+
+ <maven-jar-plugin.version>3.3.0</maven-jar-plugin.version>
+ <maven-war-plugin.version>3.3.2</maven-war-plugin.version>
+ <maven-ear-plugin.version>3.3.0</maven-ear-plugin.version>
+ <maven-assembly-plugin.version>3.5.0</maven-assembly-plugin.version>
+ <maven-dependency-plugin.version>3.5.0</maven-dependency-plugin.version>
+
+ <maven-release-plugin.version>3.0.0</maven-release-plugin.version>
+ <maven-deploy-plugin.version>3.1.1</maven-deploy-plugin.version>
+ <maven-javadoc-plugin.version>3.5.0</maven-javadoc-plugin.version>
+ <maven-site-plugin.version>4.0.0-M6</maven-site-plugin.version>
+
+ <junit.version>4.13.2</junit.version>
+
+ <tomcat7-maven-plugin.version>2.2</tomcat7-maven-plugin.version>
+
+ <javadoc.opts>-Xdoclint:none</javadoc.opts>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>javax.xml.bind</groupId>
+ <artifactId>jaxb-api</artifactId>
+ <version>${jaxb-api.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>de.laktatnebel.libs</groupId>
+ <artifactId>utillib</artifactId>
+ <version>${utillib.version}</version>
+ </dependency>
+ <!-- https://mvnrepository.com/artifact/org.json/json -->
+ <dependency>
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ <version>${json.version}</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-devtools</artifactId>
+ <scope>runtime</scope>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-tomcat</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api -->
+ <dependency>
+ <groupId>javax.xml.bind</groupId>
+ <artifactId>jaxb-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>de.laktatnebel.libs</groupId>
+ <artifactId>utillib</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <!-- https://mvnrepository.com/artifact/org.json/json -->
+ <dependency>
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>${maven-javadoc-plugin.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>${maven-compiler-plugin.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-enforcer-plugin</artifactId>
+ <version>${maven-enforcer-plugin.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>${maven-source-plugin.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>${maven-deploy-plugin.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-release-plugin</artifactId>
+ <version>${maven-release-plugin.version}</version>
+ <configuration>
+ <branchName>develop</branchName>
+ <pushChanges>false</pushChanges>
+ <localCheckout>true</localCheckout>
+ <!--tagBase>http://svn.obg-it.de/obgit/obgit_repo/tags/${project.scm.dir}</tagBase -->
+ <!--branchBase>http://svn.obg-it.de/obgit/obgit_repo/branches/${project.scm.dir}</branchBase -->
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-site-plugin</artifactId>
+ <version>${maven-site-plugin.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>site</goal>
+ </goals>
+ <configuration>
+ <reportPlugins>
+ <plugin>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ </plugin>
+ </reportPlugins>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>${maven-javadoc-plugin.version}</version>
+ <configuration>
+ <additionalparam>${javadoc.opts}</additionalparam>
+ <doclint>none</doclint>
+ </configuration>
+ <executions>
+ <execution>
+ <id>attach-javadocs</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ <configuration>
+ <additionalparam>${javadoc.opts}</additionalparam>
+ <doclint>none</doclint>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-site-plugin</artifactId>
+ <version>${maven-site-plugin.version}</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <release>${java.version}</release>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-enforcer-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>enforce-versions</id>
+ <goals>
+ <goal>enforce</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <!-- http://maven.apache.org/enforcer/enforcer-rules/versionRanges.html -->
+ <rules>
+ <requireMavenVersion>
+ <version>[3.0,)</version>
+ </requireMavenVersion>
+ <requireJavaVersion>
+ <version>${java.version}</version>
+ </requireJavaVersion>
+ </rules>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.tomcat.maven</groupId>
+ <artifactId>tomcat7-maven-plugin</artifactId>
+ <version>2.0</version>
+ <configuration>
+ <server>localtomcat</server>
+ <url>http://localhost:8080/manager/text</url>
+ <!-- path>/${project.build.finalName}</path -->
+ <update>true</update>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+ <scm>
+ <connection>scm:git:ssh://laktatnebel.de/srv/git/runningsplits.git</connection>
+ <developerConnection>scm:git:ssh://laktatnebel.de/srv/git/runningsplits.git</developerConnection>
+ <tag>HEAD</tag>
+ </scm>
+
+ <distributionManagement>
+ <repository>
+ <id>laktatnebel.release</id>
+ <name>Release Repository</name>
+ <url>file:///home/oleb/.m2/distribution</url>
+ </repository>
+ <snapshotRepository>
+ <id>laktatnebel.snapshots</id>
+ <uniqueVersion>false</uniqueVersion>
+ <name>Snapshot Repository</name>
+ <url>file:///home/oleb/.m2/distribution</url>
+ </snapshotRepository>
+ <site>
+ <id>laktatnebel.site</id>
+ <name>Sites</name>
+ <url>file:///home/oleb/.m2/site</url>
+ </site>
+ </distributionManagement>
+</project>
--- /dev/null
+package de.laktatnebel.service.calculators;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractCalculator {
+
+ /** Logger logger field */
+ private static Logger logger = LoggerFactory.getLogger(AbstractCalculator.class);
+
+ {
+ logger = null;
+ }
+
+ /**
+ * @param clazz Class
+ * @return Logger
+ */
+ public static Logger getLogger(Class<?> clazz) {
+ if (AbstractCalculator.logger == null) {
+ AbstractCalculator.logger = LoggerFactory.getLogger(clazz);
+ }
+ return AbstractCalculator.logger;
+ }
+ /**
+ *
+ * @param logger Logger
+ * @param string String
+ */
+ public static void log(Logger logger, String string) {
+ if (logger.isTraceEnabled()) {
+ logger.trace(string);
+ } else if (logger.isDebugEnabled()) {
+ logger.debug(string);
+ } else if (logger.isInfoEnabled()) {
+ logger.info(string);
+ } else if (logger.isWarnEnabled()) {
+ logger.warn(string);
+ } else if (logger.isErrorEnabled()) {
+ logger.error(string);
+ }
+ }
+
+}
--- /dev/null
+package de.laktatnebel.service.calculators;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Calculator {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Calculator.class, args);
+ }
+
+}
--- /dev/null
+package de.laktatnebel.service.calculators;
+
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+
+public class ServletInitializer extends SpringBootServletInitializer {
+
+ @Override
+ protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
+ return application.sources(Calculator.class);
+ }
+
+}
--- /dev/null
+package de.laktatnebel.service.calculators.run;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import de.laktatnebel.service.calculators.AbstractCalculator;
+import de.laktatnebel.service.calculators.util.Converter;
+import de.laktatnebel.service.calculators.util.Time;
+
+@RestController
+public class RunPrediction extends AbstractCalculator {
+ private static final double VDOT_PRECISION = 0.000125;
+
+ @SuppressWarnings("unused")
+
+ /**
+ * Default constructor.
+ */
+ public RunPrediction() {
+ // TODO Auto-generated constructor stub
+ }
+
+ @GetMapping(value = "/predict/{dist}/{hour}/{min}/{sec}/{disttopredict}", produces = MediaType.APPLICATION_JSON_VALUE)
+ @ResponseBody
+ Map<String, Object> getPace(@PathVariable(name = "dist") Double distance,
+ @PathVariable(name = "hour") Integer hours, @PathVariable(name = "min") Integer minutes,
+ @PathVariable(name = "sec") Integer seconds, @PathVariable(name = "disttopredict") Double distancetopredict) {
+
+ Time originTime = new Time(hours, minutes, seconds);
+ getLogger(RunPrediction.class).debug("originTime\t" + originTime.toString());
+ getLogger(RunPrediction.class).debug("originTime\t" + originTime.toFormattedString());
+
+ JackDaniels originJD = new JackDaniels (originTime, distance);
+ getLogger(RunPrediction.class).debug("originJD\t" + originJD.toString());
+
+ String originPace = Converter.getPace(originTime, distance);
+
+ Time estimatedTime = new Time();
+ estimatedTime.setTimeMinutes(originTime.getMinutes() * Math.pow(distancetopredict / distance, 1.06));
+ getLogger(RunPrediction.class).debug("estimatedTime\t" + estimatedTime.toString());
+ getLogger(RunPrediction.class).debug("estimatedTime\t" + estimatedTime.toFormattedString());
+
+ JackDaniels estimatedJD = new JackDaniels(estimatedTime, distancetopredict);
+ getLogger(RunPrediction.class).debug("estimatedJD\t" + estimatedJD.toString());
+
+ String estimatedPace = Converter.getPace(estimatedTime, distancetopredict);
+
+ int i=0;
+ Time predictedTime = new Time();
+ predictedTime.setTimeHours(estimatedTime.getHours());
+ JackDaniels predictedJD = new JackDaniels(estimatedTime, distancetopredict);
+ while (Math.abs(predictedJD.getVdot() / originJD.getVdot() - 1) > VDOT_PRECISION) {
+ if (estimatedJD.getVdot() > originJD.getVdot()) {
+ predictedTime.addSeconds(1);
+ } else {
+ predictedTime.addSeconds(-1);
+ }
+
+ getLogger(RunPrediction.class).debug("predictedTime\t" + predictedTime.toString());
+ getLogger(RunPrediction.class).debug("predictedTime\t" + predictedTime.toFormattedString());
+
+ predictedJD = new JackDaniels(predictedTime, distancetopredict);
+ getLogger(RunPrediction.class).debug("predictedJD\t" + predictedJD.toString());
+
+ if (++i >= 100 ) break;
+ }
+
+ String predictedPace = Converter.getPace(predictedTime, distancetopredict);
+
+ Double kmPerHours = Converter.getKmPerHours(distance, originTime);
+ Double meterPerMin = Converter.getMeterPerMin(distance, originTime);
+
+ Map<String, Object> predictionMap = new HashMap();
+
+ predictionMap.put("pace_min-km", originPace);
+ predictionMap.put("speed_km-h", Double.toString(kmPerHours));
+ predictionMap.put("meter-per-min", Double.toString(meterPerMin));
+ predictionMap.put("vO2", Double.toString(originJD.getVo2()));
+ predictionMap.put("fvO2", Double.toString(originJD.getFvo2()));
+ predictionMap.put("vdot", Double.toString(originJD.vdot));
+ predictionMap.put("distancetopredict", Double.toString(distancetopredict));
+ predictionMap.put("estimatedTime", estimatedTime.toFormattedString());
+ predictionMap.put("estimatedPace_min-km", estimatedPace);
+ predictionMap.put("predictedTime", predictedTime.toFormattedString());
+ predictionMap.put("predictedPace_min-km", predictedPace);
+
+ return predictionMap;
+
+ }
+
+ private class JackDaniels {
+ private Double vo2 = 0.0;
+ private Double fvo2 = 0.0;
+ private Double vdot = 0.0;
+
+ public JackDaniels(Time originTime, Double distance) {
+ this.vo2 = 0.000104 * Math.pow(Converter.getMeterPerMin(distance, originTime), 2) + 0.182258 * Converter.getMeterPerMin(distance, originTime) - 4.60;
+ this.fvo2 = 0.80 + 0.298956 * Math.pow(Math.E, (-0.193261*originTime.getMinutes())) + 0.189438 * Math.pow(Math.E, (-0.012778*originTime.getMinutes()));
+ this.vdot = getVo2() / getFvo2();
+ }
+
+ public Double getVo2() {
+ return vo2;
+ }
+
+ public Double getFvo2() {
+ return fvo2;
+ }
+
+ public Double getVdot() {
+ return vdot;
+ }
+
+ @Override
+ public String toString() {
+ return "JackDaniels [vo2=" + vo2 + ", fvo2=" + fvo2 + ", vdot=" + vdot + "]";
+ }
+ }
+}
--- /dev/null
+package de.laktatnebel.service.calculators.util;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+public class Converter {
+
+ public static String getPace(Time time, Double distance) {
+ Date dateFromTimeInSeconds = new Date(Math.round(60L * 60L * 1000L * time.getHours() / distance));
+
+ SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss,SS");
+ sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+ return sdf.format(dateFromTimeInSeconds);
+ }
+
+ public static Double getKmPerHours(Double distance, Time time) {
+ return distance / time.getHours();
+ }
+
+ public static Double getMeterPerMin(Double distance, Time time) {
+ return distance * 1000 / time.getMinutes();
+ }
+
+
+}
--- /dev/null
+package de.laktatnebel.service.calculators.util;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+public class Time {
+
+ private Double seconds = 0.0;
+ private Double minutes = 0.0;
+ private Double hours = 0.0;
+
+ public Time() {
+ super();
+ }
+
+ public Time(Integer hours, Integer minutes, Integer seconds) {
+ super();
+ setTime(hours, minutes, seconds);
+ }
+
+ public void addSeconds(int secondsToAdd) {
+ setTimeSeconds(this.seconds + (double) secondsToAdd/10);
+ }
+
+ public String toFormattedString() {
+ Date dateFromTimeInSeconds = new Date(Math.round(1000L * seconds));
+
+ SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss,SS");
+ sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+ return sdf.format(dateFromTimeInSeconds);
+ }
+
+ public Double getSeconds() {
+ return seconds;
+ }
+
+ public Double getMinutes() {
+ return minutes;
+ }
+
+ public Double getHours() {
+ return hours;
+ }
+
+ public void setTimeSeconds(Double seconds) {
+ this.seconds = seconds;
+ this.minutes = seconds / 60;
+ this.hours = seconds / 3600;
+ }
+
+ public void setTimeMinutes(Double minutes) {
+ this.seconds = minutes * 60;
+ this.minutes = minutes;
+ this.hours = minutes / 60;
+ }
+
+ public void setTimeHours(Double hours) {
+ this.seconds = hours * 3600;
+ this.minutes = hours * 60;
+ this.hours = hours;
+ }
+
+ public void setTime(Integer hours, Integer minutes, Integer seconds) {
+ this.seconds = (double) hours * 3600 + (double) minutes * 60 + (double) seconds;
+ this.minutes = (double) hours * 60 + (double) minutes + (double) seconds / 60;
+ this.hours = (double) hours + (double) minutes / 60 + (double) seconds / 3600;
+ }
+
+ @Override
+ public String toString() {
+ return "Time [seconds=" + seconds + ", minutes=" + minutes + ", hours=" + hours + "]";
+ }
+}
\ No newline at end of file
--- /dev/null
+logging.level.root=TRACE
+logging.level.org.springframework.web=DEBUG
+logging.level.org.hibernate=ERROR
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>\r
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">\r
+\r
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">\r
+\r
+ <appender name="appender" class="org.apache.log4j.ConsoleAppender">\r
+ <layout class="org.apache.log4j.PatternLayout">\r
+ <param name="ConversionPattern" value="%d [%t] %p - %m%n" />\r
+ </layout>\r
+ </appender>\r
+\r
+ <root>\r
+ <priority value="trace" />\r
+ <appender-ref ref="appender" />\r
+ </root>\r
+\r
+</log4j:configuration>\r
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Insert title here</title>
+</head>
+<body>
+something ...
+</body>
+</html>
\ No newline at end of file