chore: formatting and use modern javadoc style

This commit is contained in:
js0ny 2025-10-18 21:43:31 +01:00
parent 79db47459a
commit c2a39936ec
3 changed files with 69 additions and 40 deletions

View file

@ -6,17 +6,21 @@ import java.util.Objects;
/**
* Represents the data transfer object for a region definition
* <p>
* This record encapsulates the data for calculating if a coordinate is inside the region
* This record encapsulates the data for calculating if a coordinate is inside
* the region
* <p>
* A built-in method <code>isClosedTo</code> is defined to check this DTO is valid or not in the mean of closing polygon
* A built-in method {@code isClosedTo} is defined to check this DTO is valid or
* not in the mean of closing polygon
*
* @param name The human-readable name for the region
* @param vertices list of coordinates that forms a polygon as a region.
* <p>
* In order to define a valid region, the last element of the list should be the same as the first, or
* In order to define a valid region, the last element of the
* list should be the same as the first, or
* known as closed
* @see RegionCheckRequestDto
* @see io.github.js0ny.ilp_coursework.service.GpsCalculationService#checkIsInRegion(LngLatDto, RegionDto)
* @see io.github.js0ny.ilp_coursework.service.GpsCalculationService#checkIsInRegion(LngLatDto,
* RegionDto)
*/
public record RegionDto(String name, List<LngLatDto> vertices) {
/**
@ -27,9 +31,11 @@ public record RegionDto(String name, List<LngLatDto> vertices) {
private static final int MINIMUM_VERTICES = 4;
/**
* Method to check if the region has a valid polygon by checking if the <code>vertices</code> forms a closed polygon
* Method to check if the region has a valid polygon by checking if the
* {@code vertices} forms a closed polygon
*
* @return {@code true} if the <code>vertices</code> are able to form a polygon and form a closed polygon
* @return {@code true} if the {@code vertices} are able to form a polygon and
* form a closed polygon
*/
public boolean isClosed() {
if (vertices == null || vertices.size() < MINIMUM_VERTICES) {

View file

@ -29,29 +29,38 @@ public class GpsCalculationService {
private static final double CLOSE_THRESHOLD = 0.00015;
/**
* Calculate the Euclidean distance between <code>position1</code> and <code>position2</code>, which are coordinates
* Calculate the Euclidean distance between {@code position1} and
* {@code position2}, which are coordinates
* defined as {@link LngLatDto}
*
* @param position1 The coordinate of the first position
*
* @param position2 The coordinate of the second position
* @return The Euclidean distance between <code>position1</code> and <code>position2</code>
* @see io.github.js0ny.ilp_coursework.controller.ApiController#getDistance(DistanceRequestDto)
* @return The Euclidean distance between {@code position1} and
* {@code position2}
* @see
* io.github.js0ny.ilp_coursework.controller.ApiController#getDistance(DistanceRequestDto)
*/
public double calculateDistance(LngLatDto position1, LngLatDto position2) {
double lngDistance = position2.lng() - position1.lng();
double latDistance = position2.lat() - position1.lat();
// Euclidean: \sqrt{a^2 + b^2}
// Euclidean: \sqrt{a^2 + b^2}
return Math.sqrt(lngDistance * lngDistance + latDistance * latDistance);
}
/**
* Check if <code>position1</code> and <code>position2</code> are close to each other, the threshold is < 0.00015
* Check if {@code position1} and
* {@code position2} are close to each other, the threshold is < 0.00015
*
* <p>
* Note that = 0.00015 will be counted as not close to and will return <code>false</code>
* Note that = 0.00015 will be counted as not close to and will return {@code
* false}
*
* @param position1 The coordinate of the first position
*
* @param position2 The coordinate of the second position
* @return True if <code>position1</code> and <code>position2</code> are close to each other
* @return {@code true} if {@code position1} and
* {@code position2} are close to each other
* @see #CLOSE_THRESHOLD
* @see io.github.js0ny.ilp_coursework.controller.ApiController#getIsCloseTo(DistanceRequestDto)
*/
@ -61,12 +70,14 @@ public class GpsCalculationService {
}
/**
* Returns the next position moved from <code>start</code> in the direction with <code>angle</code>, with step size
* Returns the next position moved from {@code start} in the direction with
* {@code angle}, with step size
* 0.00015
*
* @param start The coordinate of the original start point.
*
* @param angle The direction to be moved in angle.
* @return The next position moved from <code>start</code>
* @return The next position moved from {@code start}
* @see #STEP
* @see io.github.js0ny.ilp_coursework.controller.ApiController#getNextPosition(MovementRequestDto)
*/
@ -78,13 +89,14 @@ public class GpsCalculationService {
}
/**
* Used to check if the given <code>position</code>
* is inside the <code>region</code>, on edge and vertex is considered as inside.
* Used to check if the given {@code position}
* is inside the {@code region}, on edge and vertex is considered as inside.
*
* @param position The coordinate of the position.
* @param region A {@link RegionDto} that contains name and a list of <code>LngLatDto</code>
* @return true if <code>position</code> is inside the <code>region</code>.
* @throws IllegalArgumentException If <code>region</code> is not closed
* @param region A {@link RegionDto} that contains name and a list of
* {@code LngLatDto}
* @return {@code true} if {@code position} is inside the {@code region}.
* @throws IllegalArgumentException If {@code region} is not closed
* @see io.github.js0ny.ilp_coursework.controller.ApiController#getIsInRegion(RegionCheckRequestDto)
* @see RegionDto#isClosed()
*/
@ -95,16 +107,15 @@ public class GpsCalculationService {
return rayCasting(position, region.vertices());
}
/**
* Helper function to <code>checkIsInRegion</code>, use of ray-casting algorithm
* Helper function to {@code checkIsInRegion}, use of ray-casting algorithm
* to check if inside the polygon
*
* @param point The point to check
* @param polygon The region that forms a polygon to check if <code>point</code>
* @param polygon The region that forms a polygon to check if {@code point}
* sits inside.
* @return If the <code>point</code> sits inside the <code>polygon</code> then
* return True
* @return If the {@code point} sits inside the {@code polygon} then
* return {@code true}
* @see #isPointOnEdge(LngLatDto, LngLatDto, LngLatDto)
* @see #checkIsInRegion(LngLatDto, RegionDto)
*/
@ -138,7 +149,6 @@ public class GpsCalculationService {
double xIntersection = a.lng() + ((point.lat() - a.lat()) * (b.lng() - a.lng())) / (b.lat() - a.lat());
if (xIntersection > point.lng()) {
++intersections;
}
@ -150,13 +160,15 @@ public class GpsCalculationService {
}
/**
* Helper function from <code>rayCasting</code> that used to simply calculation <br>
* Used to check if point <code>p</code> is on the edge formed by <code>a</code> and <code>b</code>
* Helper function from {@code rayCasting} that used to simply calculation <br>
* Used to check if point {@code p} is on the edge formed by
* {@code a} and {@code b}
*
* @param p point to be checked on the edge
*
* @param a point that forms the edge
* @param b point that forms the edge
* @return boolean, if <code>p</code> is on <code>ab</code> then true
* @return {@code true} if {@code p} is on {@code ab}
* @see #rayCasting(LngLatDto, List)
*/
private boolean isPointOnEdge(LngLatDto p, LngLatDto a, LngLatDto b) {

View file

@ -13,11 +13,10 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.AssertionsForClassTypes.within;
public class GpsCalculationServiceTest {
private static final double STEP = 0.00015;
// private static final double CLOSE_THRESHOLD = STEP;
// private static final double CLOSE_THRESHOLD = STEP;
private static final double PRECISION = 1e-9;
private GpsCalculationService service;
@ -180,7 +179,8 @@ public class GpsCalculationServiceTest {
void nextPosition_shouldMoveWest_forAngle180() {
var start = new LngLatDto(0.0, 0.0);
double angle = 180;
// For 180 degrees, cos(180)=-1, sin(180)=0. Move happens entirely on negative lng axis.
// For 180 degrees, cos(180)=-1, sin(180)=0. Move happens entirely on negative
// lng axis.
var expected = new LngLatDto(-STEP, 0.0);
var actual = service.nextPosition(start, angle);
@ -194,7 +194,8 @@ public class GpsCalculationServiceTest {
void nextPosition_shouldMoveSouth_forAngle270() {
var start = new LngLatDto(0.0, 0.0);
double angle = 270;
// For 270 degrees, cos(270)=0, sin(270)=-1. Move happens entirely on negative lat axis.
// For 270 degrees, cos(270)=0, sin(270)=-1. Move happens entirely on negative
// lat axis.
var expected = new LngLatDto(0.0, -STEP);
var actual = service.nextPosition(start, angle);
@ -257,13 +258,17 @@ public class GpsCalculationServiceTest {
@DisplayName("Test for checkIsInRegion(LngLatDto, RegionDto) -> boolean")
class CheckIsInRegionTests {
public static final RegionDto RECTANGLE_REGION = new RegionDto("rectangle", List.of(new LngLatDto(0.0, 0.0), new LngLatDto(2.0, 0.0), new LngLatDto(2.0, 2.0), new LngLatDto(0.0, 2.0), new LngLatDto(0.0, 0.0)));
public static final RegionDto RECTANGLE_REGION = new RegionDto("rectangle", List.of(new LngLatDto(0.0, 0.0),
new LngLatDto(2.0, 0.0), new LngLatDto(2.0, 2.0), new LngLatDto(0.0, 2.0), new LngLatDto(0.0, 0.0)));
@Test
@DisplayName("General Case: Given Example for Testing")
void isInRegion_shouldReturnFalse_givenPolygonCentral() {
var position = new LngLatDto(1.234, 1.222);
var region = new RegionDto("central", List.of(new LngLatDto(-3.192473, 55.946233), new LngLatDto(-3.192473, 55.942617), new LngLatDto(-3.184319, 55.942617), new LngLatDto(-3.184319, 55.946233), new LngLatDto(-3.192473, 55.946233)));
var region = new RegionDto("central",
List.of(new LngLatDto(-3.192473, 55.946233), new LngLatDto(-3.192473, 55.942617),
new LngLatDto(-3.184319, 55.942617), new LngLatDto(-3.184319, 55.946233),
new LngLatDto(-3.192473, 55.946233)));
boolean expected = false;
boolean actual = service.checkIsInRegion(position, region);
assertThat(actual).isEqualTo(expected);
@ -291,7 +296,10 @@ public class GpsCalculationServiceTest {
@DisplayName("General Case: Simple Hexagon")
void isInRegion_shouldReturnTrue_forSimpleHexagon() {
var position = new LngLatDto(2.0, 2.0);
var region = new RegionDto("hexagon", List.of(new LngLatDto(1.0, 0.0), new LngLatDto(4.0, 0.0), new LngLatDto(5.0, 2.0), new LngLatDto(4.0, 4.0), new LngLatDto(1.0, 4.0), new LngLatDto(0.0, 2.0), new LngLatDto(1.0, 0.0)));
var region = new RegionDto("hexagon",
List.of(new LngLatDto(1.0, 0.0), new LngLatDto(4.0, 0.0), new LngLatDto(5.0, 2.0),
new LngLatDto(4.0, 4.0), new LngLatDto(1.0, 4.0), new LngLatDto(0.0, 2.0),
new LngLatDto(1.0, 0.0)));
boolean expected = true;
boolean actual = service.checkIsInRegion(position, region);
assertThat(actual).isEqualTo(expected);
@ -301,7 +309,8 @@ public class GpsCalculationServiceTest {
@DisplayName("Edge Case: Small Triangle")
void isInRegion_shouldReturnTrue_forSmallTriangle() {
var position = new LngLatDto(0.00001, 0.00001);
var region = new RegionDto("triangle", List.of(new LngLatDto(0.0, 0.0), new LngLatDto(0.0001, 0.0), new LngLatDto(0.00005, 0.0001), new LngLatDto(0.0, 0.0)));
var region = new RegionDto("triangle", List.of(new LngLatDto(0.0, 0.0), new LngLatDto(0.0001, 0.0),
new LngLatDto(0.00005, 0.0001), new LngLatDto(0.0, 0.0)));
boolean expected = true;
boolean actual = service.checkIsInRegion(position, region);
assertThat(actual).isEqualTo(expected);
@ -356,7 +365,8 @@ public class GpsCalculationServiceTest {
@DisplayName("Edge Case: Region not forming polygon")
void isInRegion_shouldThrowExceptions_whenRegionNotFormingPolygon() {
var position = new LngLatDto(2.0, 2.0);
var region = new RegionDto("line", List.of(new LngLatDto(0.0, 0.0), new LngLatDto(0.0001, 0.0), new LngLatDto(0.0, 0.0)));
var region = new RegionDto("line",
List.of(new LngLatDto(0.0, 0.0), new LngLatDto(0.0001, 0.0), new LngLatDto(0.0, 0.0)));
assertThatThrownBy(() -> {
service.checkIsInRegion(position, region);
}).isInstanceOf(IllegalArgumentException.class).hasMessage("Region is not closed.");
@ -366,7 +376,8 @@ public class GpsCalculationServiceTest {
@DisplayName("Edge Case: Region is not closed")
void isInRegion_shouldThrowExceptions_whenRegionNotClose() {
var position = new LngLatDto(2.0, 2.0);
var region = new RegionDto("rectangle", List.of(new LngLatDto(0.0, 0.0), new LngLatDto(2.0, 0.0), new LngLatDto(2.0, 2.0), new LngLatDto(0.0, 2.0), new LngLatDto(0.0, -1.0)));
var region = new RegionDto("rectangle", List.of(new LngLatDto(0.0, 0.0), new LngLatDto(2.0, 0.0),
new LngLatDto(2.0, 2.0), new LngLatDto(0.0, 2.0), new LngLatDto(0.0, -1.0)));
assertThatThrownBy(() -> {
service.checkIsInRegion(position, region);
}).isInstanceOf(IllegalArgumentException.class).hasMessage("Region is not closed.");