refractor(dto): Use nested data package

This commit is contained in:
js0ny 2025-11-24 00:35:29 +00:00
parent ec0d9087dd
commit 69d9e0d736
29 changed files with 405 additions and 383 deletions

View file

@ -6,11 +6,11 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.github.js0ny.ilp_coursework.data.DistanceRequestDto;
import io.github.js0ny.ilp_coursework.data.LngLatDto;
import io.github.js0ny.ilp_coursework.data.MovementRequestDto;
import io.github.js0ny.ilp_coursework.data.RegionCheckRequestDto;
import io.github.js0ny.ilp_coursework.data.RegionDto;
import io.github.js0ny.ilp_coursework.data.request.DistanceRequest;
import io.github.js0ny.ilp_coursework.data.common.LngLat;
import io.github.js0ny.ilp_coursework.data.request.MovementRequest;
import io.github.js0ny.ilp_coursework.data.request.RegionCheckRequest;
import io.github.js0ny.ilp_coursework.data.common.Region;
import io.github.js0ny.ilp_coursework.service.GpsCalculationService;
/**
@ -48,39 +48,39 @@ public class ApiController {
/**
* Handles POST requests to get the distance between two positions
*
* @param request A {@link DistanceRequestDto} containing the two coordinates
* @param request A {@link DistanceRequest} containing the two coordinates
* @return A {@code double} representing the calculated distance
*/
@PostMapping("/distanceTo")
public double getDistance(@RequestBody DistanceRequestDto request) {
public double getDistance(@RequestBody DistanceRequest request) {
LngLatDto position1 = request.position1();
LngLatDto position2 = request.position2();
LngLat position1 = request.position1();
LngLat position2 = request.position2();
return gpsService.calculateDistance(position1, position2);
}
/**
* Handles POST requests to check if the two coordinates are close to each other
*
* @param request A {@link DistanceRequestDto} containing the two coordinates
* @param request A {@link DistanceRequest} containing the two coordinates
* @return {@code true} if the distance is less than the predefined threshold, {@code false} otherwise
*/
@PostMapping("/isCloseTo")
public boolean getIsCloseTo(@RequestBody DistanceRequestDto request) {
LngLatDto position1 = request.position1();
LngLatDto position2 = request.position2();
public boolean getIsCloseTo(@RequestBody DistanceRequest request) {
LngLat position1 = request.position1();
LngLat position2 = request.position2();
return gpsService.isCloseTo(position1, position2);
}
/**
* Handles POST requests to get the next position after an angle of movement
*
* @param request A {@link MovementRequestDto} containing the start coordinate and angle of the movement.
* @return A {@link LngLatDto} representing the destination
* @param request A {@link MovementRequest} containing the start coordinate and angle of the movement.
* @return A {@link LngLat} representing the destination
*/
@PostMapping("/nextPosition")
public LngLatDto getNextPosition(@RequestBody MovementRequestDto request) {
LngLatDto start = request.start();
public LngLat getNextPosition(@RequestBody MovementRequest request) {
LngLat start = request.start();
double angle = request.angle();
return gpsService.nextPosition(start, angle);
}
@ -88,13 +88,13 @@ public class ApiController {
/**
* Handles POST requests to check if a point is inside a given region
*
* @param request A {@link RegionCheckRequestDto} containing the coordinate and the region
* @param request A {@link RegionCheckRequest} containing the coordinate and the region
* @return {@code true} if the coordinate is inside the region, {@code false} otherwise
*/
@PostMapping("/isInRegion")
public boolean getIsInRegion(@RequestBody RegionCheckRequestDto request) {
LngLatDto position = request.position();
RegionDto region = request.region();
public boolean getIsInRegion(@RequestBody RegionCheckRequest request) {
LngLat position = request.position();
Region region = request.region();
return gpsService.checkIsInRegion(position, region);
}
}

View file

@ -1,10 +1,10 @@
package io.github.js0ny.ilp_coursework.controller;
import io.github.js0ny.ilp_coursework.data.AttrComparatorDto;
import io.github.js0ny.ilp_coursework.data.DeliveryPathDto;
import io.github.js0ny.ilp_coursework.data.DroneDto;
import io.github.js0ny.ilp_coursework.data.DronePathDto;
import io.github.js0ny.ilp_coursework.data.MedDispatchRecDto;
import io.github.js0ny.ilp_coursework.data.response.DeliveryPathResponse;
import io.github.js0ny.ilp_coursework.data.external.Drone;
import io.github.js0ny.ilp_coursework.data.common.DronePathDto;
import io.github.js0ny.ilp_coursework.data.request.MedDispatchRecRequest;
import io.github.js0ny.ilp_coursework.data.request.AttrQueryRequest;
import io.github.js0ny.ilp_coursework.service.DroneInfoService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@ -55,13 +55,13 @@ public class DroneController {
* Handles GET requests to retrieve the drone detail identified by id
*
* @param id The id of the drone to be queried.
* @return 200 with {@link DroneDto}-style json if success, 404 if {@code id}
* @return 200 with {@link Drone}-style json if success, 404 if {@code id}
* not found, 400 otherwise
*/
@GetMapping("/droneDetails/{id}")
public ResponseEntity<DroneDto> getDroneDetail(@PathVariable String id) {
public ResponseEntity<Drone> getDroneDetail(@PathVariable String id) {
try {
DroneDto drone = droneService.droneDetail(id);
Drone drone = droneService.droneDetail(id);
return ResponseEntity.ok(drone);
} catch (IllegalArgumentException ex) {
return ResponseEntity.notFound().build();
@ -84,22 +84,22 @@ public class DroneController {
}
@PostMapping("/query")
public String[] getIdByAttrMapPost(@RequestBody AttrComparatorDto[] attrComparators) {
public String[] getIdByAttrMapPost(@RequestBody AttrQueryRequest[] attrComparators) {
return droneService.dronesSatisfyingAttributes(attrComparators);
}
@PostMapping("/queryAvailableDrones")
public String[] queryAvailableDrones(@RequestBody MedDispatchRecDto[] records) {
public String[] queryAvailableDrones(@RequestBody MedDispatchRecRequest[] records) {
return droneService.dronesMatchesRequirements(records);
}
@PostMapping("/calcDeliveryPath")
public DeliveryPathDto calculateDeliveryPath(@RequestBody MedDispatchRecDto[] record) {
return new DeliveryPathDto(0.0f, 0, new DronePathDto[]{});
public DeliveryPathResponse calculateDeliveryPath(@RequestBody MedDispatchRecRequest[] record) {
return new DeliveryPathResponse(0.0f, 0, new DronePathDto[]{});
}
@PostMapping("/calcDeliveryPathAsGeoJson")
public String calculateDeliveryPathAsGeoJson(@RequestBody MedDispatchRecDto[] record) {
public String calculateDeliveryPathAsGeoJson(@RequestBody MedDispatchRecRequest[] record) {
return "{}";
}

View file

@ -1,7 +0,0 @@
package io.github.js0ny.ilp_coursework.data;
// TODO: Convert operator to Enum
// import io.github.js0ny.ilp_coursework.util.AttrOperator;
public record AttrComparatorDto(String attribute, String operator, String value) {
}

View file

@ -1,7 +0,0 @@
package io.github.js0ny.ilp_coursework.data;
public record DeliveryPathDto(
float totalCost,
int totalMoves,
DronePathDto[] dronePaths) {
}

View file

@ -1,13 +0,0 @@
package io.github.js0ny.ilp_coursework.data;
/**
* Represents the data transfer object for a distance operation request.
* <p>
* This record encapsulates the data for several endpoints that involves two {@code LngLatDto}
* and serves as the data contract for those API operation
*
* @param position1 Nested object of {@link LngLatDto}
* @param position2 Nested object of {@link LngLatDto}
*/
public record DistanceRequestDto(LngLatDto position1, LngLatDto position2) {
}

View file

@ -1,10 +0,0 @@
package io.github.js0ny.ilp_coursework.data;
/**
* Represents the data transfer object for a drone, gained from the endpoints
*/
public record DroneDto(
String name,
String id,
DroneCapabilityDto capability) {
}

View file

@ -1,4 +0,0 @@
package io.github.js0ny.ilp_coursework.data;
public record DronePathDto() {
}

View file

@ -1,12 +0,0 @@
package io.github.js0ny.ilp_coursework.data;
import java.time.LocalDate;
import java.time.LocalTime;
public record MedDispatchRecDto(
int id,
LocalDate date,
LocalTime time,
MedRequirementDto requirements,
LngLatDto delivery) {
}

View file

@ -1,8 +0,0 @@
package io.github.js0ny.ilp_coursework.data;
public record MedRequirementDto(
float capacity,
boolean cooling,
boolean heating,
float maxCost
) {}

View file

@ -1,18 +0,0 @@
package io.github.js0ny.ilp_coursework.data;
import org.springframework.lang.Nullable;
public record ServicePointDronesDto(
String servicePointId,
DroneAvailabilityDto[] drones) {
@Nullable
public DroneAvailabilityDto locateDroneById(String droneId) {
for (var drone : drones) {
if (drone.id().equals(droneId)) {
return drone;
}
}
return null;
}
}

View file

@ -1,11 +1,11 @@
package io.github.js0ny.ilp_coursework.data;
package io.github.js0ny.ilp_coursework.data.common;
import java.time.DayOfWeek;
import java.time.LocalTime;
public record DroneAvailabilityDto(
public record DroneAvailability(
String id,
AvailabilityTimeSegDto[] availability) {
TimeWindow[] availability) {
public boolean checkAvailability(DayOfWeek day, LocalTime time) {

View file

@ -1,6 +1,6 @@
package io.github.js0ny.ilp_coursework.data;
package io.github.js0ny.ilp_coursework.data.common;
public record DroneCapabilityDto(
public record DroneCapability(
boolean cooling,
boolean heating,
float capacity,

View file

@ -0,0 +1,4 @@
package io.github.js0ny.ilp_coursework.data.common;
public record DronePathDto() {
}

View file

@ -1,4 +1,4 @@
package io.github.js0ny.ilp_coursework.data;
package io.github.js0ny.ilp_coursework.data.common;
/**
* Represents the data transfer object for a point or coordinate
@ -7,5 +7,5 @@ package io.github.js0ny.ilp_coursework.data;
* @param lng longitude of the coordinate/point
* @param lat latitude of the coordinate/point
*/
public record LngLatDto(double lng, double lat) {
public record LngLat(double lng, double lat) {
}

View file

@ -0,0 +1,9 @@
package io.github.js0ny.ilp_coursework.data.common;
public record MedRequirement(
float capacity,
boolean cooling,
boolean heating,
float maxCost
) {
}

View file

@ -1,4 +1,6 @@
package io.github.js0ny.ilp_coursework.data;
package io.github.js0ny.ilp_coursework.data.common;
import io.github.js0ny.ilp_coursework.data.request.RegionCheckRequest;
import java.util.List;
import java.util.Objects;
@ -18,11 +20,11 @@ import java.util.Objects;
* 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 RegionCheckRequest
* @see io.github.js0ny.ilp_coursework.service.GpsCalculationService#checkIsInRegion(LngLat,
* Region)
*/
public record RegionDto(String name, List<LngLatDto> vertices) {
public record Region(String name, List<LngLat> vertices) {
/**
* Magic number 4: For a polygon, 3 edges is required.
* <p>
@ -41,8 +43,8 @@ public record RegionDto(String name, List<LngLatDto> vertices) {
if (vertices == null || vertices.size() < MINIMUM_VERTICES) {
return false;
}
LngLatDto first = vertices.getFirst();
LngLatDto last = vertices.getLast();
LngLat first = vertices.getFirst();
LngLat last = vertices.getLast();
return Objects.equals(last, first);
}

View file

@ -1,9 +1,9 @@
package io.github.js0ny.ilp_coursework.data;
package io.github.js0ny.ilp_coursework.data.common;
import java.time.DayOfWeek;
import java.time.LocalTime;
public record AvailabilityTimeSegDto(
public record TimeWindow(
DayOfWeek dayOfWeek,
LocalTime from,
LocalTime until) {

View file

@ -0,0 +1,12 @@
package io.github.js0ny.ilp_coursework.data.external;
import io.github.js0ny.ilp_coursework.data.common.DroneCapability;
/**
* Represents the data transfer object for a drone, gained from the endpoints
*/
public record Drone(
String name,
String id,
DroneCapability capability) {
}

View file

@ -0,0 +1,19 @@
package io.github.js0ny.ilp_coursework.data.external;
import io.github.js0ny.ilp_coursework.data.common.DroneAvailability;
import org.springframework.lang.Nullable;
public record ServicePointDrones(
String servicePointId,
DroneAvailability[] drones) {
@Nullable
public DroneAvailability locateDroneById(String droneId) {
for (var drone : drones) {
if (drone.id().equals(droneId)) {
return drone;
}
}
return null;
}
}

View file

@ -0,0 +1,7 @@
package io.github.js0ny.ilp_coursework.data.request;
// TODO: Convert operator to Enum
// import io.github.js0ny.ilp_coursework.util.AttrOperator;
public record AttrQueryRequest(String attribute, String operator, String value) {
}

View file

@ -0,0 +1,15 @@
package io.github.js0ny.ilp_coursework.data.request;
import io.github.js0ny.ilp_coursework.data.common.LngLat;
/**
* Represents the data transfer object for a distance operation request.
* <p>
* This record encapsulates the data for several endpoints that involves two {@code LngLatDto}
* and serves as the data contract for those API operation
*
* @param position1 Nested object of {@link LngLat}
* @param position2 Nested object of {@link LngLat}
*/
public record DistanceRequest(LngLat position1, LngLat position2) {
}

View file

@ -0,0 +1,15 @@
package io.github.js0ny.ilp_coursework.data.request;
import io.github.js0ny.ilp_coursework.data.common.MedRequirement;
import io.github.js0ny.ilp_coursework.data.common.LngLat;
import java.time.LocalDate;
import java.time.LocalTime;
public record MedDispatchRecRequest(
int id,
LocalDate date,
LocalTime time,
MedRequirement requirements,
LngLat delivery) {
}

View file

@ -1,4 +1,6 @@
package io.github.js0ny.ilp_coursework.data;
package io.github.js0ny.ilp_coursework.data.request;
import io.github.js0ny.ilp_coursework.data.common.LngLat;
/**
* Represents the data transfer object for a movement action request.
@ -9,7 +11,7 @@ package io.github.js0ny.ilp_coursework.data;
* @param start The starting coordinate of the movement
* @param angle The angle to movement in degree. This corresponds to compass directions.
* For example: 0 for East, 90 for North
* @see io.github.js0ny.ilp_coursework.controller.ApiController#getNextPosition(MovementRequestDto)
* @see io.github.js0ny.ilp_coursework.controller.ApiController#getNextPosition(MovementRequest)
*/
public record MovementRequestDto(LngLatDto start, double angle) {
public record MovementRequest(LngLat start, double angle) {
}

View file

@ -1,4 +1,7 @@
package io.github.js0ny.ilp_coursework.data;
package io.github.js0ny.ilp_coursework.data.request;
import io.github.js0ny.ilp_coursework.data.common.LngLat;
import io.github.js0ny.ilp_coursework.data.common.Region;
/**
* Represents the data transfer object for a region check request.
@ -9,8 +12,8 @@ package io.github.js0ny.ilp_coursework.data;
*
* @param position The coordinate to be checked
* @param region The region for the check.
* This is a nested object represented by {@link RegionDto}
* @see io.github.js0ny.ilp_coursework.controller.ApiController#getIsInRegion(RegionCheckRequestDto)
* This is a nested object represented by {@link Region}
* @see io.github.js0ny.ilp_coursework.controller.ApiController#getIsInRegion(RegionCheckRequest)
*/
public record RegionCheckRequestDto(LngLatDto position, RegionDto region) {
public record RegionCheckRequest(LngLat position, Region region) {
}

View file

@ -0,0 +1,9 @@
package io.github.js0ny.ilp_coursework.data.response;
import io.github.js0ny.ilp_coursework.data.common.DronePathDto;
public record DeliveryPathResponse(
float totalCost,
int totalMoves,
DronePathDto[] dronePaths) {
}

View file

@ -1,10 +1,10 @@
package io.github.js0ny.ilp_coursework.service;
import io.github.js0ny.ilp_coursework.data.AttrComparatorDto;
import io.github.js0ny.ilp_coursework.data.DroneDto;
import io.github.js0ny.ilp_coursework.data.MedDispatchRecDto;
import io.github.js0ny.ilp_coursework.data.ServicePointDronesDto;
import io.github.js0ny.ilp_coursework.data.external.Drone;
import io.github.js0ny.ilp_coursework.data.request.MedDispatchRecRequest;
import io.github.js0ny.ilp_coursework.data.external.ServicePointDrones;
import io.github.js0ny.ilp_coursework.util.AttrOperator;
import io.github.js0ny.ilp_coursework.data.request.AttrQueryRequest;
import static io.github.js0ny.ilp_coursework.util.AttrComparator.isValueMatched;
@ -59,9 +59,9 @@ public class DroneInfoService {
*/
public String[] dronesWithCooling(boolean state) {
URI droneUrl = URI.create(baseUrl).resolve(dronesEndpoint);
DroneDto[] drones = restTemplate.getForObject(
Drone[] drones = restTemplate.getForObject(
droneUrl,
DroneDto[].class);
Drone[].class);
if (drones == null) {
return new String[]{};
@ -69,12 +69,12 @@ public class DroneInfoService {
return Arrays.stream(drones)
.filter(drone -> drone.capability().cooling() == state)
.map(DroneDto::id)
.map(Drone::id)
.toArray(String[]::new);
}
/**
* Return a {@link DroneDto}-style json data structure with the given {@code id}
* Return a {@link Drone}-style json data structure with the given {@code id}
* <p>
* Associated service method with {@code /droneDetails/{id}}
*
@ -87,11 +87,11 @@ public class DroneInfoService {
* this should lead to a 404
* @see io.github.js0ny.ilp_coursework.controller.DroneController#getDroneDetail(String)
*/
public DroneDto droneDetail(String id) {
public Drone droneDetail(String id) {
URI droneUrl = URI.create(baseUrl).resolve(dronesEndpoint);
DroneDto[] drones = restTemplate.getForObject(
Drone[] drones = restTemplate.getForObject(
droneUrl,
DroneDto[].class);
Drone[].class);
if (drones == null) {
throw new NullPointerException("drone cannot be found");
@ -131,7 +131,7 @@ public class DroneInfoService {
* @param attrComparators The filter rule with Name, Value and Operator
* @return array of drone ids that matches all rules
*/
public String[] dronesSatisfyingAttributes(AttrComparatorDto[] attrComparators) {
public String[] dronesSatisfyingAttributes(AttrQueryRequest[] attrComparators) {
Set<String> matchingDroneIds = null;
for (var comparator : attrComparators) {
String attribute = comparator.attribute();
@ -170,9 +170,9 @@ public class DroneInfoService {
private String[] dronesWithAttributeCompared(String attrName, String attrVal, AttrOperator op) {
URI droneUrl = URI.create(baseUrl).resolve(dronesEndpoint);
// This is required to make sure the response is valid
DroneDto[] drones = restTemplate.getForObject(
Drone[] drones = restTemplate.getForObject(
droneUrl,
DroneDto[].class);
Drone[].class);
if (drones == null) {
return new String[]{};
@ -193,7 +193,7 @@ public class DroneInfoService {
return false;
}
})
.map(DroneDto::id)
.map(Drone::id)
.toArray(String[]::new);
}
@ -207,11 +207,11 @@ public class DroneInfoService {
* @return array of drone ids that match all the requirements
* @see io.github.js0ny.ilp_coursework.controller.DroneController#queryAvailableDrones
*/
public String[] dronesMatchesRequirements(MedDispatchRecDto[] rec) {
public String[] dronesMatchesRequirements(MedDispatchRecRequest[] rec) {
URI droneUrl = URI.create(baseUrl).resolve(dronesEndpoint);
DroneDto[] drones = restTemplate.getForObject(
Drone[] drones = restTemplate.getForObject(
droneUrl,
DroneDto[].class);
Drone[].class);
if (drones == null || rec == null || rec.length == 0) {
return new String[]{};
@ -226,7 +226,7 @@ public class DroneInfoService {
.filter(record -> record != null && record.requirements() != null)
// Every record must be met
.allMatch(record -> meetsRequirement(drone, record)))
.map(DroneDto::id)
.map(Drone::id)
.toArray(String[]::new);
}
@ -240,7 +240,7 @@ public class DroneInfoService {
* is invalid (capacity and id cannot be null
* in {@code MedDispathRecDto})
*/
private boolean meetsRequirement(DroneDto drone, MedDispatchRecDto record) {
private boolean meetsRequirement(Drone drone, MedDispatchRecRequest record) {
var requirements = record.requirements();
if (requirements == null) {
throw new IllegalArgumentException("requirements cannot be null");
@ -284,11 +284,11 @@ public class DroneInfoService {
* time
* @return true if the drone is available, false otherwise
*/
private boolean checkAvailability(String droneId, MedDispatchRecDto record) {
private boolean checkAvailability(String droneId, MedDispatchRecRequest record) {
URI droneUrl = URI.create(baseUrl).resolve(dronesForServicePointsEndpoint);
ServicePointDronesDto[] servicePoints = restTemplate.getForObject(
ServicePointDrones[] servicePoints = restTemplate.getForObject(
droneUrl,
ServicePointDronesDto[].class);
ServicePointDrones[].class);
LocalDate requiredDate = record.date();
DayOfWeek requiredDay = requiredDate.getDayOfWeek();

View file

@ -1,16 +1,19 @@
package io.github.js0ny.ilp_coursework.service;
import io.github.js0ny.ilp_coursework.data.*;
import java.util.List;
import io.github.js0ny.ilp_coursework.data.common.LngLat;
import io.github.js0ny.ilp_coursework.data.common.Region;
import io.github.js0ny.ilp_coursework.data.request.DistanceRequest;
import io.github.js0ny.ilp_coursework.data.request.MovementRequest;
import io.github.js0ny.ilp_coursework.data.request.RegionCheckRequest;
import org.springframework.stereotype.Service;
/**
* Class that handles calculations about Coordinates
*
* @see LngLatDto
* @see RegionDto
* @see LngLat
* @see Region
*/
@Service
public class GpsCalculationService {
@ -18,30 +21,28 @@ public class GpsCalculationService {
/**
* Given step size
*
* @see #nextPosition(LngLatDto, double)
* @see #nextPosition(LngLat, double)
*/
private static final double STEP = 0.00015;
/**
* Given threshold to judge if two points are close to each other
*
* @see #isCloseTo(LngLatDto, LngLatDto)
* @see #isCloseTo(LngLat, LngLat)
*/
private static final double CLOSE_THRESHOLD = 0.00015;
/**
* Calculate the Euclidean distance between {@code position1} and
* {@code position2}, which are coordinates
* defined as {@link LngLatDto}
* defined as {@link LngLat}
*
* @param position1 The coordinate of the first position
*
* @param position2 The coordinate of the second position
* @return The Euclidean distance between {@code position1} and
* {@code position2}
* @see
* io.github.js0ny.ilp_coursework.controller.ApiController#getDistance(DistanceRequestDto)
* @see io.github.js0ny.ilp_coursework.controller.ApiController#getDistance(DistanceRequest)
*/
public double calculateDistance(LngLatDto position1, LngLatDto position2) {
public double calculateDistance(LngLat position1, LngLat position2) {
double lngDistance = position2.lng() - position1.lng();
double latDistance = position2.lat() - position1.lat();
// Euclidean: \sqrt{a^2 + b^2}
@ -57,14 +58,13 @@ public class GpsCalculationService {
* false}
*
* @param position1 The coordinate of the first position
*
* @param position2 The coordinate of the second position
* @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)
* @see io.github.js0ny.ilp_coursework.controller.ApiController#getIsCloseTo(DistanceRequest)
*/
public boolean isCloseTo(LngLatDto position1, LngLatDto position2) {
public boolean isCloseTo(LngLat position1, LngLat position2) {
double distance = calculateDistance(position1, position2);
return distance < CLOSE_THRESHOLD;
}
@ -75,17 +75,16 @@ public class GpsCalculationService {
* 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}
* @see #STEP
* @see io.github.js0ny.ilp_coursework.controller.ApiController#getNextPosition(MovementRequestDto)
* @see io.github.js0ny.ilp_coursework.controller.ApiController#getNextPosition(MovementRequest)
*/
public LngLatDto nextPosition(LngLatDto start, double angle) {
public LngLat nextPosition(LngLat start, double angle) {
double rad = Math.toRadians(angle); // Convert to radian for Java triangle function calculation
double newLng = Math.cos(rad) * STEP + start.lng();
double newLat = Math.sin(rad) * STEP + start.lat();
return new LngLatDto(newLng, newLat);
return new LngLat(newLng, newLat);
}
/**
@ -93,14 +92,14 @@ public class GpsCalculationService {
* 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
* @param region A {@link Region} 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()
* @see io.github.js0ny.ilp_coursework.controller.ApiController#getIsInRegion(RegionCheckRequest)
* @see Region#isClosed()
*/
public boolean checkIsInRegion(LngLatDto position, RegionDto region) throws IllegalArgumentException {
public boolean checkIsInRegion(LngLat position, Region region) throws IllegalArgumentException {
if (!region.isClosed()) { // call method from RegionDto to check if not closed
throw new IllegalArgumentException("Region is not closed.");
}
@ -116,15 +115,15 @@ public class GpsCalculationService {
* sits inside.
* @return If the {@code point} sits inside the {@code polygon} then
* return {@code true}
* @see #isPointOnEdge(LngLatDto, LngLatDto, LngLatDto)
* @see #checkIsInRegion(LngLatDto, RegionDto)
* @see #isPointOnEdge(LngLat, LngLat, LngLat)
* @see #checkIsInRegion(LngLat, Region)
*/
private boolean rayCasting(LngLatDto point, List<LngLatDto> polygon) {
private boolean rayCasting(LngLat point, List<LngLat> polygon) {
int intersections = 0;
int n = polygon.size();
for (int i = 0; i < n; ++i) {
LngLatDto a = polygon.get(i);
LngLatDto b = polygon.get((i + 1) % n); // Next vertex
LngLat a = polygon.get(i);
LngLat b = polygon.get((i + 1) % n); // Next vertex
if (isPointOnEdge(point, a, b)) {
return true;
@ -132,7 +131,7 @@ public class GpsCalculationService {
// Ensure that `a` is norther than `b`, in order to easy classification
if (a.lat() > b.lat()) {
LngLatDto temp = a;
LngLat temp = a;
a = b;
b = temp;
}
@ -165,13 +164,12 @@ public class GpsCalculationService {
* {@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 {@code true} if {@code p} is on {@code ab}
* @see #rayCasting(LngLatDto, List)
* @see #rayCasting(LngLat, List)
*/
private boolean isPointOnEdge(LngLatDto p, LngLatDto a, LngLatDto b) {
private boolean isPointOnEdge(LngLat p, LngLat a, LngLat b) {
// Cross product: (p - a) × (b - a)
double crossProduct = (p.lng() - a.lng()) * (b.lat() - a.lat())
- (p.lat() - a.lat()) * (b.lng() - a.lng());

View file

@ -8,9 +8,15 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.js0ny.ilp_coursework.data.*;
import io.github.js0ny.ilp_coursework.data.common.LngLat;
import io.github.js0ny.ilp_coursework.data.common.Region;
import io.github.js0ny.ilp_coursework.data.request.DistanceRequest;
import io.github.js0ny.ilp_coursework.data.request.MovementRequest;
import io.github.js0ny.ilp_coursework.data.request.RegionCheckRequest;
import io.github.js0ny.ilp_coursework.service.GpsCalculationService;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@ -58,13 +64,13 @@ public class ApiControllerTest {
throws Exception {
double expected = 5.0;
String endpoint = "/api/v1/distanceTo";
LngLatDto p1 = new LngLatDto(0, 4.0);
LngLatDto p2 = new LngLatDto(3.0, 0);
var req = new DistanceRequestDto(p1, p2);
LngLat p1 = new LngLat(0, 4.0);
LngLat p2 = new LngLat(3.0, 0);
var req = new DistanceRequest(p1, p2);
when(
service.calculateDistance(
any(LngLatDto.class),
any(LngLatDto.class)
any(LngLat.class),
any(LngLat.class)
)
).thenReturn(expected);
var mock = mockMvc.perform(
@ -90,7 +96,7 @@ public class ApiControllerTest {
}
""";
when(
service.calculateDistance(any(LngLatDto.class), isNull())
service.calculateDistance(any(LngLat.class), isNull())
).thenThrow(new NullPointerException());
mockMvc
.perform(
@ -112,11 +118,11 @@ public class ApiControllerTest {
throws Exception {
boolean expected = false;
String endpoint = "/api/v1/isCloseTo";
LngLatDto p1 = new LngLatDto(0, 4.0);
LngLatDto p2 = new LngLatDto(3.0, 0);
var req = new DistanceRequestDto(p1, p2);
LngLat p1 = new LngLat(0, 4.0);
LngLat p2 = new LngLat(3.0, 0);
var req = new DistanceRequest(p1, p2);
when(
service.isCloseTo(any(LngLatDto.class), any(LngLatDto.class))
service.isCloseTo(any(LngLat.class), any(LngLat.class))
).thenReturn(expected);
var mock = mockMvc.perform(
post(endpoint)
@ -157,11 +163,11 @@ public class ApiControllerTest {
@DisplayName("POST /nextPosition -> 200 OK")
void getNextPosition_shouldReturn200AndCoordinate_whenCorrectInput()
throws Exception {
LngLatDto expected = new LngLatDto(0.00015, 0.0);
LngLatDto p = new LngLatDto(0, 0);
var req = new MovementRequestDto(p, 0);
LngLat expected = new LngLat(0.00015, 0.0);
LngLat p = new LngLat(0, 0);
var req = new MovementRequest(p, 0);
when(
service.nextPosition(any(LngLatDto.class), anyDouble())
service.nextPosition(any(LngLat.class), anyDouble())
).thenReturn(expected);
var mock = mockMvc.perform(
post(endpoint)
@ -209,22 +215,22 @@ public class ApiControllerTest {
throws Exception {
boolean expected = false;
String endpoint = "/api/v1/isInRegion";
var position = new LngLatDto(1.234, 1.222);
var region = new RegionDto(
var position = new LngLat(1.234, 1.222);
var region = new Region(
"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)
new LngLat(-3.192473, 55.946233),
new LngLat(-3.192473, 55.942617),
new LngLat(-3.184319, 55.942617),
new LngLat(-3.184319, 55.946233),
new LngLat(-3.192473, 55.946233)
)
);
var req = new RegionCheckRequestDto(position, region);
var req = new RegionCheckRequest(position, region);
when(
service.checkIsInRegion(
any(LngLatDto.class),
any(RegionDto.class)
any(LngLat.class),
any(Region.class)
)
).thenReturn(expected);
var mock = mockMvc.perform(
@ -243,13 +249,13 @@ public class ApiControllerTest {
)
void getIsInRegion_shouldReturn400_whenPassingIllegalArguments()
throws Exception {
var position = new LngLatDto(1, 1);
var region = new RegionDto("illegal", List.of());
var request = new RegionCheckRequestDto(position, region);
var position = new LngLat(1, 1);
var region = new Region("illegal", List.of());
var request = new RegionCheckRequest(position, region);
when(
service.checkIsInRegion(
any(LngLatDto.class),
any(RegionDto.class)
any(LngLat.class),
any(Region.class)
)
).thenThrow(new IllegalArgumentException("Region is not closed."));
mockMvc
@ -267,22 +273,22 @@ public class ApiControllerTest {
)
void getIsInRegion_shouldReturn400_whenPassingNotClosingVertices()
throws Exception {
var position = new LngLatDto(1, 1);
var region = new RegionDto(
var position = new LngLat(1, 1);
var region = new Region(
"illegal",
List.of(
new LngLatDto(1, 2),
new LngLatDto(3, 4),
new LngLatDto(5, 6),
new LngLatDto(7, 8),
new LngLatDto(9, 10)
new LngLat(1, 2),
new LngLat(3, 4),
new LngLat(5, 6),
new LngLat(7, 8),
new LngLat(9, 10)
)
);
var request = new RegionCheckRequestDto(position, region);
var request = new RegionCheckRequest(position, region);
when(
service.checkIsInRegion(
any(LngLatDto.class),
any(RegionDto.class)
any(LngLat.class),
any(Region.class)
)
).thenThrow(new IllegalArgumentException("Region is not closed."));
mockMvc

View file

@ -1,7 +1,7 @@
package io.github.js0ny.ilp_coursework.service;
import io.github.js0ny.ilp_coursework.data.LngLatDto;
import io.github.js0ny.ilp_coursework.data.RegionDto;
import io.github.js0ny.ilp_coursework.data.common.LngLat;
import io.github.js0ny.ilp_coursework.data.common.Region;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
@ -32,8 +32,8 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("False: Given Example For Testing")
void isCloseTo_shouldReturnFalse_givenExample() {
var p1 = new LngLatDto(-3.192473, 55.946233);
var p2 = new LngLatDto(-3.192473, 55.942617);
var p1 = new LngLat(-3.192473, 55.946233);
var p2 = new LngLat(-3.192473, 55.942617);
double expected = 0.0036;
double actual = service.calculateDistance(p1, p2);
assertThat(actual).isCloseTo(expected, within(1e-4));
@ -42,8 +42,8 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("General Case: 3-4-5 Triangle")
void calculateDistance_shouldReturnCorrectEuclideanDistance_forGeneralCase() {
var p1 = new LngLatDto(0, 3.0);
var p2 = new LngLatDto(4.0, 0);
var p1 = new LngLat(0, 3.0);
var p2 = new LngLat(4.0, 0);
double expected = 5.0;
double actual = service.calculateDistance(p1, p2);
assertThat(actual).isCloseTo(expected, within(PRECISION));
@ -52,7 +52,7 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("Edge Case: Points are Identical")
void calculateDistance_shouldReturnZero_whenPointsAreIdentical() {
var p1 = new LngLatDto(123.85, 983.2119);
var p1 = new LngLat(123.85, 983.2119);
double expected = 0.0;
double actual = service.calculateDistance(p1, p1);
assertThat(actual).isEqualTo(expected);
@ -61,8 +61,8 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("Edge Case: Longitudinal-only movement")
void calculateDistance_shouldReturnCorrectDistance_forPointsWithSameLatitude() {
var p1 = new LngLatDto(123.85, 983.2119);
var p2 = new LngLatDto(133.85, 983.2119);
var p1 = new LngLat(123.85, 983.2119);
var p2 = new LngLat(133.85, 983.2119);
double expected = 10.0;
double actual = service.calculateDistance(p1, p2);
assertThat(actual).isCloseTo(expected, within(PRECISION));
@ -71,8 +71,8 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("Edge Case: Latitude-only movement")
void calculateDistance_shouldReturnCorrectDistance_forPointsWithSameLongitude() {
var p1 = new LngLatDto(123.85, 983.2119);
var p2 = new LngLatDto(123.85, 973.2119);
var p1 = new LngLat(123.85, 983.2119);
var p2 = new LngLat(123.85, 973.2119);
double expected = 10.0;
double actual = service.calculateDistance(p1, p2);
assertThat(actual).isCloseTo(expected, within(PRECISION));
@ -81,8 +81,8 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("General Case: Calculate with negative Coordinates")
void calculateDistance_shouldReturnCorrectEuclideanDistance_forNegativeCoordinates() {
LngLatDto p1 = new LngLatDto(-1.0, -2.0);
LngLatDto p2 = new LngLatDto(2.0, 2.0); // lngDiff = 3, latDiff = 4
LngLat p1 = new LngLat(-1.0, -2.0);
LngLat p2 = new LngLat(2.0, 2.0); // lngDiff = 3, latDiff = 4
double expected = 5.0;
double actual = service.calculateDistance(p1, p2);
assertThat(actual).isCloseTo(expected, within(PRECISION));
@ -95,8 +95,8 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("False: Given Example For Testing")
void isCloseTo_shouldReturnFalse_givenExample() {
var p1 = new LngLatDto(-3.192473, 55.946233);
var p2 = new LngLatDto(-3.192473, 55.942617);
var p1 = new LngLat(-3.192473, 55.946233);
var p2 = new LngLat(-3.192473, 55.942617);
boolean expected = false;
boolean actual = service.isCloseTo(p1, p2);
assertThat(actual).isEqualTo(expected);
@ -105,7 +105,7 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("True: Two points are the same")
void isCloseTo_shouldReturnTrue_whenPointsAreIdentical() {
var p1 = new LngLatDto(151.86, 285.37);
var p1 = new LngLat(151.86, 285.37);
boolean expected = true;
boolean actual = service.isCloseTo(p1, p1);
assertThat(actual).isEqualTo(expected);
@ -114,8 +114,8 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("True: Two points are close to each other and near threshold")
void isCloseTo_shouldReturnTrue_whenCloseAndSmallerThanThreshold() {
var p1 = new LngLatDto(0.0, 0.0);
var p2 = new LngLatDto(0.0, 0.00014);
var p1 = new LngLat(0.0, 0.0);
var p2 = new LngLat(0.0, 0.00014);
boolean expected = true;
boolean actual = service.isCloseTo(p1, p2);
assertThat(actual).isEqualTo(expected);
@ -124,8 +124,8 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("False: Distance nears the threshold")
void isCloseTo_shouldReturnFalse_whenEqualsToThreshold() {
var p1 = new LngLatDto(0.0, 0.0);
var p2 = new LngLatDto(0.0, 0.00015);
var p1 = new LngLat(0.0, 0.0);
var p2 = new LngLat(0.0, 0.00015);
boolean expected = false;
boolean actual = service.isCloseTo(p1, p2);
assertThat(actual).isEqualTo(expected);
@ -134,8 +134,8 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("False: Distance larger to threshold")
void isCloseTo_shouldReturnFalse_whenNotCloseAndLargerThanThreshold() {
var p1 = new LngLatDto(0.0, 0.0);
var p2 = new LngLatDto(0.0, 0.00016);
var p1 = new LngLat(0.0, 0.0);
var p2 = new LngLat(0.0, 0.00016);
boolean expected = false;
boolean actual = service.isCloseTo(p1, p2);
assertThat(actual).isEqualTo(expected);
@ -149,10 +149,10 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("General Case: nextPosition in East direction (0 degrees)")
void nextPosition_shouldMoveEast_forAngleZero() {
var start = new LngLatDto(0.0, 0.0);
var start = new LngLat(0.0, 0.0);
double angle = 0;
// For 0 degrees, cos(0)=1, sin(0)=0. Move happens entirely on lng axis.
var expected = new LngLatDto(STEP, 0.0);
var expected = new LngLat(STEP, 0.0);
var actual = service.nextPosition(start, angle);
@ -163,10 +163,10 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("Cardinal Direction: nextPosition in North direction (90 degrees)")
void nextPosition_shouldMoveNorth_forAngle90() {
var start = new LngLatDto(0.0, 0.0);
var start = new LngLat(0.0, 0.0);
double angle = 90;
// For 90 degrees, cos(90)=0, sin(90)=1. Move happens entirely on lat axis.
var expected = new LngLatDto(0.0, STEP);
var expected = new LngLat(0.0, STEP);
var actual = service.nextPosition(start, angle);
@ -177,11 +177,11 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("Cardinal Direction: nextPosition in West direction (180 degrees)")
void nextPosition_shouldMoveWest_forAngle180() {
var start = new LngLatDto(0.0, 0.0);
var start = new LngLat(0.0, 0.0);
double angle = 180;
// For 180 degrees, cos(180)=-1, sin(180)=0. Move happens entirely on negative
// lng axis.
var expected = new LngLatDto(-STEP, 0.0);
var expected = new LngLat(-STEP, 0.0);
var actual = service.nextPosition(start, angle);
@ -192,11 +192,11 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("Cardinal Direction: nextPosition in South direction (270 degrees)")
void nextPosition_shouldMoveSouth_forAngle270() {
var start = new LngLatDto(0.0, 0.0);
var start = new LngLat(0.0, 0.0);
double angle = 270;
// For 270 degrees, cos(270)=0, sin(270)=-1. Move happens entirely on negative
// lat axis.
var expected = new LngLatDto(0.0, -STEP);
var expected = new LngLat(0.0, -STEP);
var actual = service.nextPosition(start, angle);
@ -207,12 +207,12 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("Intercardinal Direction: nextPosition in Northeast direction (45 degrees)")
void nextPosition_shouldMoveNortheast_forAngle45() {
var start = new LngLatDto(0.0, 0.0);
var start = new LngLat(0.0, 0.0);
double angle = 45;
// Δlng = step * cos(45°), Δlat = step * sin(45°)
double expectedLng = STEP * Math.cos(Math.toRadians(angle));
double expectedLat = STEP * Math.sin(Math.toRadians(angle));
var expected = new LngLatDto(expectedLng, expectedLat);
var expected = new LngLat(expectedLng, expectedLat);
var actual = service.nextPosition(start, angle);
@ -223,13 +223,13 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("Edge Case: Angle larger than 360 should wrap around")
void nextPosition_shouldHandleAngleGreaterThan360() {
var start = new LngLatDto(0.0, 0.0);
var start = new LngLat(0.0, 0.0);
// 405 degrees is equivalent to 45 degrees (405 % 360 = 45).
double angle = 405;
double equivalentAngle = 45;
double expectedLng = STEP * Math.cos(Math.toRadians(equivalentAngle));
double expectedLat = STEP * Math.sin(Math.toRadians(equivalentAngle));
var expected = new LngLatDto(expectedLng, expectedLat);
var expected = new LngLat(expectedLng, expectedLat);
var actual = service.nextPosition(start, angle);
@ -240,12 +240,12 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("Edge Case: Negative angle should work correctly")
void nextPosition_shouldHandleNegativeAngle() {
var start = new LngLatDto(0.0, 0.0);
var start = new LngLat(0.0, 0.0);
// A negative angle of -45° corresponds to the Southeast direction.
double angle = -45;
double expectedLng = STEP * Math.cos(Math.toRadians(angle));
double expectedLat = STEP * Math.sin(Math.toRadians(angle));
var expected = new LngLatDto(expectedLng, expectedLat);
var expected = new LngLat(expectedLng, expectedLat);
var actual = service.nextPosition(start, angle);
@ -258,17 +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 Region RECTANGLE_REGION = new Region("rectangle", List.of(new LngLat(0.0, 0.0),
new LngLat(2.0, 0.0), new LngLat(2.0, 2.0), new LngLat(0.0, 2.0), new LngLat(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 position = new LngLat(1.234, 1.222);
var region = new Region("central",
List.of(new LngLat(-3.192473, 55.946233), new LngLat(-3.192473, 55.942617),
new LngLat(-3.184319, 55.942617), new LngLat(-3.184319, 55.946233),
new LngLat(-3.192473, 55.946233)));
boolean expected = false;
boolean actual = service.checkIsInRegion(position, region);
assertThat(actual).isEqualTo(expected);
@ -277,7 +277,7 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("General Case: Simple Rectangle")
void isInRegion_shouldReturnTrue_forSimpleRectangle() {
var position = new LngLatDto(1.0, 1.0);
var position = new LngLat(1.0, 1.0);
boolean expected = true;
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
assertThat(actual).isEqualTo(expected);
@ -286,7 +286,7 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("General Case: Simple Rectangle")
void isInRegion_shouldReturnFalse_forSimpleRectangle() {
var position = new LngLatDto(3.0, 1.0);
var position = new LngLat(3.0, 1.0);
boolean expected = false;
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
assertThat(actual).isEqualTo(expected);
@ -295,11 +295,11 @@ public class GpsCalculationServiceTest {
@Test
@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 position = new LngLat(2.0, 2.0);
var region = new Region("hexagon",
List.of(new LngLat(1.0, 0.0), new LngLat(4.0, 0.0), new LngLat(5.0, 2.0),
new LngLat(4.0, 4.0), new LngLat(1.0, 4.0), new LngLat(0.0, 2.0),
new LngLat(1.0, 0.0)));
boolean expected = true;
boolean actual = service.checkIsInRegion(position, region);
assertThat(actual).isEqualTo(expected);
@ -308,9 +308,9 @@ public class GpsCalculationServiceTest {
@Test
@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 position = new LngLat(0.00001, 0.00001);
var region = new Region("triangle", List.of(new LngLat(0.0, 0.0), new LngLat(0.0001, 0.0),
new LngLat(0.00005, 0.0001), new LngLat(0.0, 0.0)));
boolean expected = true;
boolean actual = service.checkIsInRegion(position, region);
assertThat(actual).isEqualTo(expected);
@ -319,7 +319,7 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("Edge Case: Point on Lower Edge of Rectangle")
void isInRegion_shouldReturnTrue_whenPointOnLowerEdge() {
var position = new LngLatDto(0.0, 1.0);
var position = new LngLat(0.0, 1.0);
boolean expected = true;
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
assertThat(actual).isEqualTo(expected);
@ -328,7 +328,7 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("Edge Case: Point on Upper Edge of Rectangle")
void isInRegion_shouldReturnTrue_whenPointOnUpperEdge() {
var position = new LngLatDto(2.0, 1.0);
var position = new LngLat(2.0, 1.0);
boolean expected = true;
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
assertThat(actual).isEqualTo(expected);
@ -337,7 +337,7 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("Edge Case: Point on Left Edge")
void isInRegion_shouldReturnTrue_whenPointOnLeftEdge() {
var position = new LngLatDto(0.0, 1.0);
var position = new LngLat(0.0, 1.0);
boolean expected = true;
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
assertThat(actual).isEqualTo(expected);
@ -346,7 +346,7 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("Edge Case: Point on Lower Vertex")
void isInRegion_shouldReturnTrue_whenPointOnLowerVertex() {
var position = new LngLatDto(0.0, 0.0);
var position = new LngLat(0.0, 0.0);
boolean expected = true;
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
assertThat(actual).isEqualTo(expected);
@ -355,7 +355,7 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("Edge Case: Point on Upper Vertex")
void isInRegion_shouldReturnTrue_whenPointOnUpperVertex() {
var position = new LngLatDto(2.0, 2.0);
var position = new LngLat(2.0, 2.0);
boolean expected = true;
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
assertThat(actual).isEqualTo(expected);
@ -364,9 +364,9 @@ public class GpsCalculationServiceTest {
@Test
@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 position = new LngLat(2.0, 2.0);
var region = new Region("line",
List.of(new LngLat(0.0, 0.0), new LngLat(0.0001, 0.0), new LngLat(0.0, 0.0)));
assertThatThrownBy(() -> {
service.checkIsInRegion(position, region);
}).isInstanceOf(IllegalArgumentException.class).hasMessage("Region is not closed.");
@ -375,9 +375,9 @@ public class GpsCalculationServiceTest {
@Test
@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 position = new LngLat(2.0, 2.0);
var region = new Region("rectangle", List.of(new LngLat(0.0, 0.0), new LngLat(2.0, 0.0),
new LngLat(2.0, 2.0), new LngLat(0.0, 2.0), new LngLat(0.0, -1.0)));
assertThatThrownBy(() -> {
service.checkIsInRegion(position, region);
}).isInstanceOf(IllegalArgumentException.class).hasMessage("Region is not closed.");
@ -386,8 +386,8 @@ public class GpsCalculationServiceTest {
@Test
@DisplayName("Edge Case: Vertex list is empty")
void isInRegion_shouldThrowExceptions_whenListIsEmpty() {
var position = new LngLatDto(2.0, 2.0);
var region = new RegionDto("rectangle", List.of());
var position = new LngLat(2.0, 2.0);
var region = new Region("rectangle", List.of());
assertThatThrownBy(() -> {
service.checkIsInRegion(position, region);
}).isInstanceOf(IllegalArgumentException.class).hasMessage("Region is not closed.");