refractor(dto): Use nested data package
This commit is contained in:
parent
ec0d9087dd
commit
69d9e0d736
29 changed files with 405 additions and 383 deletions
|
|
@ -6,11 +6,11 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import io.github.js0ny.ilp_coursework.data.DistanceRequestDto;
|
import io.github.js0ny.ilp_coursework.data.request.DistanceRequest;
|
||||||
import io.github.js0ny.ilp_coursework.data.LngLatDto;
|
import io.github.js0ny.ilp_coursework.data.common.LngLat;
|
||||||
import io.github.js0ny.ilp_coursework.data.MovementRequestDto;
|
import io.github.js0ny.ilp_coursework.data.request.MovementRequest;
|
||||||
import io.github.js0ny.ilp_coursework.data.RegionCheckRequestDto;
|
import io.github.js0ny.ilp_coursework.data.request.RegionCheckRequest;
|
||||||
import io.github.js0ny.ilp_coursework.data.RegionDto;
|
import io.github.js0ny.ilp_coursework.data.common.Region;
|
||||||
import io.github.js0ny.ilp_coursework.service.GpsCalculationService;
|
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
|
* 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
|
* @return A {@code double} representing the calculated distance
|
||||||
*/
|
*/
|
||||||
@PostMapping("/distanceTo")
|
@PostMapping("/distanceTo")
|
||||||
public double getDistance(@RequestBody DistanceRequestDto request) {
|
public double getDistance(@RequestBody DistanceRequest request) {
|
||||||
|
|
||||||
LngLatDto position1 = request.position1();
|
LngLat position1 = request.position1();
|
||||||
LngLatDto position2 = request.position2();
|
LngLat position2 = request.position2();
|
||||||
return gpsService.calculateDistance(position1, position2);
|
return gpsService.calculateDistance(position1, position2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles POST requests to check if the two coordinates are close to each other
|
* 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
|
* @return {@code true} if the distance is less than the predefined threshold, {@code false} otherwise
|
||||||
*/
|
*/
|
||||||
@PostMapping("/isCloseTo")
|
@PostMapping("/isCloseTo")
|
||||||
public boolean getIsCloseTo(@RequestBody DistanceRequestDto request) {
|
public boolean getIsCloseTo(@RequestBody DistanceRequest request) {
|
||||||
LngLatDto position1 = request.position1();
|
LngLat position1 = request.position1();
|
||||||
LngLatDto position2 = request.position2();
|
LngLat position2 = request.position2();
|
||||||
return gpsService.isCloseTo(position1, position2);
|
return gpsService.isCloseTo(position1, position2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles POST requests to get the next position after an angle of movement
|
* 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.
|
* @param request A {@link MovementRequest} containing the start coordinate and angle of the movement.
|
||||||
* @return A {@link LngLatDto} representing the destination
|
* @return A {@link LngLat} representing the destination
|
||||||
*/
|
*/
|
||||||
@PostMapping("/nextPosition")
|
@PostMapping("/nextPosition")
|
||||||
public LngLatDto getNextPosition(@RequestBody MovementRequestDto request) {
|
public LngLat getNextPosition(@RequestBody MovementRequest request) {
|
||||||
LngLatDto start = request.start();
|
LngLat start = request.start();
|
||||||
double angle = request.angle();
|
double angle = request.angle();
|
||||||
return gpsService.nextPosition(start, 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
|
* 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
|
* @return {@code true} if the coordinate is inside the region, {@code false} otherwise
|
||||||
*/
|
*/
|
||||||
@PostMapping("/isInRegion")
|
@PostMapping("/isInRegion")
|
||||||
public boolean getIsInRegion(@RequestBody RegionCheckRequestDto request) {
|
public boolean getIsInRegion(@RequestBody RegionCheckRequest request) {
|
||||||
LngLatDto position = request.position();
|
LngLat position = request.position();
|
||||||
RegionDto region = request.region();
|
Region region = request.region();
|
||||||
return gpsService.checkIsInRegion(position, region);
|
return gpsService.checkIsInRegion(position, region);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
package io.github.js0ny.ilp_coursework.controller;
|
package io.github.js0ny.ilp_coursework.controller;
|
||||||
|
|
||||||
import io.github.js0ny.ilp_coursework.data.AttrComparatorDto;
|
import io.github.js0ny.ilp_coursework.data.response.DeliveryPathResponse;
|
||||||
import io.github.js0ny.ilp_coursework.data.DeliveryPathDto;
|
import io.github.js0ny.ilp_coursework.data.external.Drone;
|
||||||
import io.github.js0ny.ilp_coursework.data.DroneDto;
|
import io.github.js0ny.ilp_coursework.data.common.DronePathDto;
|
||||||
import io.github.js0ny.ilp_coursework.data.DronePathDto;
|
import io.github.js0ny.ilp_coursework.data.request.MedDispatchRecRequest;
|
||||||
import io.github.js0ny.ilp_coursework.data.MedDispatchRecDto;
|
import io.github.js0ny.ilp_coursework.data.request.AttrQueryRequest;
|
||||||
import io.github.js0ny.ilp_coursework.service.DroneInfoService;
|
import io.github.js0ny.ilp_coursework.service.DroneInfoService;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
@ -55,13 +55,13 @@ public class DroneController {
|
||||||
* Handles GET requests to retrieve the drone detail identified by id
|
* Handles GET requests to retrieve the drone detail identified by id
|
||||||
*
|
*
|
||||||
* @param id The id of the drone to be queried.
|
* @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
|
* not found, 400 otherwise
|
||||||
*/
|
*/
|
||||||
@GetMapping("/droneDetails/{id}")
|
@GetMapping("/droneDetails/{id}")
|
||||||
public ResponseEntity<DroneDto> getDroneDetail(@PathVariable String id) {
|
public ResponseEntity<Drone> getDroneDetail(@PathVariable String id) {
|
||||||
try {
|
try {
|
||||||
DroneDto drone = droneService.droneDetail(id);
|
Drone drone = droneService.droneDetail(id);
|
||||||
return ResponseEntity.ok(drone);
|
return ResponseEntity.ok(drone);
|
||||||
} catch (IllegalArgumentException ex) {
|
} catch (IllegalArgumentException ex) {
|
||||||
return ResponseEntity.notFound().build();
|
return ResponseEntity.notFound().build();
|
||||||
|
|
@ -84,22 +84,22 @@ public class DroneController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/query")
|
@PostMapping("/query")
|
||||||
public String[] getIdByAttrMapPost(@RequestBody AttrComparatorDto[] attrComparators) {
|
public String[] getIdByAttrMapPost(@RequestBody AttrQueryRequest[] attrComparators) {
|
||||||
return droneService.dronesSatisfyingAttributes(attrComparators);
|
return droneService.dronesSatisfyingAttributes(attrComparators);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/queryAvailableDrones")
|
@PostMapping("/queryAvailableDrones")
|
||||||
public String[] queryAvailableDrones(@RequestBody MedDispatchRecDto[] records) {
|
public String[] queryAvailableDrones(@RequestBody MedDispatchRecRequest[] records) {
|
||||||
return droneService.dronesMatchesRequirements(records);
|
return droneService.dronesMatchesRequirements(records);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/calcDeliveryPath")
|
@PostMapping("/calcDeliveryPath")
|
||||||
public DeliveryPathDto calculateDeliveryPath(@RequestBody MedDispatchRecDto[] record) {
|
public DeliveryPathResponse calculateDeliveryPath(@RequestBody MedDispatchRecRequest[] record) {
|
||||||
return new DeliveryPathDto(0.0f, 0, new DronePathDto[]{});
|
return new DeliveryPathResponse(0.0f, 0, new DronePathDto[]{});
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/calcDeliveryPathAsGeoJson")
|
@PostMapping("/calcDeliveryPathAsGeoJson")
|
||||||
public String calculateDeliveryPathAsGeoJson(@RequestBody MedDispatchRecDto[] record) {
|
public String calculateDeliveryPathAsGeoJson(@RequestBody MedDispatchRecRequest[] record) {
|
||||||
return "{}";
|
return "{}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
package io.github.js0ny.ilp_coursework.data;
|
|
||||||
|
|
||||||
public record DeliveryPathDto(
|
|
||||||
float totalCost,
|
|
||||||
int totalMoves,
|
|
||||||
DronePathDto[] dronePaths) {
|
|
||||||
}
|
|
||||||
|
|
@ -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) {
|
|
||||||
}
|
|
||||||
|
|
@ -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) {
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
package io.github.js0ny.ilp_coursework.data;
|
|
||||||
|
|
||||||
public record DronePathDto() {
|
|
||||||
}
|
|
||||||
|
|
@ -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) {
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
package io.github.js0ny.ilp_coursework.data;
|
|
||||||
|
|
||||||
public record MedRequirementDto(
|
|
||||||
float capacity,
|
|
||||||
boolean cooling,
|
|
||||||
boolean heating,
|
|
||||||
float maxCost
|
|
||||||
) {}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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.DayOfWeek;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
|
|
||||||
public record DroneAvailabilityDto(
|
public record DroneAvailability(
|
||||||
String id,
|
String id,
|
||||||
AvailabilityTimeSegDto[] availability) {
|
TimeWindow[] availability) {
|
||||||
|
|
||||||
public boolean checkAvailability(DayOfWeek day, LocalTime time) {
|
public boolean checkAvailability(DayOfWeek day, LocalTime time) {
|
||||||
|
|
||||||
|
|
@ -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 cooling,
|
||||||
boolean heating,
|
boolean heating,
|
||||||
float capacity,
|
float capacity,
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
package io.github.js0ny.ilp_coursework.data.common;
|
||||||
|
|
||||||
|
public record DronePathDto() {
|
||||||
|
}
|
||||||
|
|
@ -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
|
* 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 lng longitude of the coordinate/point
|
||||||
* @param lat latitude 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) {
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package io.github.js0ny.ilp_coursework.data.common;
|
||||||
|
|
||||||
|
public record MedRequirement(
|
||||||
|
float capacity,
|
||||||
|
boolean cooling,
|
||||||
|
boolean heating,
|
||||||
|
float maxCost
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
@ -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.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
@ -18,11 +20,11 @@ import java.util.Objects;
|
||||||
* In order to define a valid region, the last element of the
|
* In order to define a valid region, the last element of the
|
||||||
* list should be the same as the first, or
|
* list should be the same as the first, or
|
||||||
* known as closed
|
* known as closed
|
||||||
* @see RegionCheckRequestDto
|
* @see RegionCheckRequest
|
||||||
* @see io.github.js0ny.ilp_coursework.service.GpsCalculationService#checkIsInRegion(LngLatDto,
|
* @see io.github.js0ny.ilp_coursework.service.GpsCalculationService#checkIsInRegion(LngLat,
|
||||||
* RegionDto)
|
* 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.
|
* Magic number 4: For a polygon, 3 edges is required.
|
||||||
* <p>
|
* <p>
|
||||||
|
|
@ -35,14 +37,14 @@ public record RegionDto(String name, List<LngLatDto> vertices) {
|
||||||
* {@code vertices} forms a closed polygon
|
* {@code vertices} forms a closed polygon
|
||||||
*
|
*
|
||||||
* @return {@code true} if the {@code vertices} are able to form a polygon and
|
* @return {@code true} if the {@code vertices} are able to form a polygon and
|
||||||
* form a closed polygon
|
* form a closed polygon
|
||||||
*/
|
*/
|
||||||
public boolean isClosed() {
|
public boolean isClosed() {
|
||||||
if (vertices == null || vertices.size() < MINIMUM_VERTICES) {
|
if (vertices == null || vertices.size() < MINIMUM_VERTICES) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
LngLatDto first = vertices.getFirst();
|
LngLat first = vertices.getFirst();
|
||||||
LngLatDto last = vertices.getLast();
|
LngLat last = vertices.getLast();
|
||||||
return Objects.equals(last, first);
|
return Objects.equals(last, first);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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.DayOfWeek;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
|
|
||||||
public record AvailabilityTimeSegDto(
|
public record TimeWindow(
|
||||||
DayOfWeek dayOfWeek,
|
DayOfWeek dayOfWeek,
|
||||||
LocalTime from,
|
LocalTime from,
|
||||||
LocalTime until) {
|
LocalTime until) {
|
||||||
12
src/main/java/io/github/js0ny/ilp_coursework/data/external/Drone.java
vendored
Normal file
12
src/main/java/io/github/js0ny/ilp_coursework/data/external/Drone.java
vendored
Normal 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) {
|
||||||
|
}
|
||||||
19
src/main/java/io/github/js0ny/ilp_coursework/data/external/ServicePointDrones.java
vendored
Normal file
19
src/main/java/io/github/js0ny/ilp_coursework/data/external/ServicePointDrones.java
vendored
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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) {
|
||||||
|
}
|
||||||
|
|
@ -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) {
|
||||||
|
}
|
||||||
|
|
@ -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) {
|
||||||
|
}
|
||||||
|
|
@ -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.
|
* 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 start The starting coordinate of the movement
|
||||||
* @param angle The angle to movement in degree. This corresponds to compass directions.
|
* @param angle The angle to movement in degree. This corresponds to compass directions.
|
||||||
* For example: 0 for East, 90 for North
|
* 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) {
|
||||||
}
|
}
|
||||||
|
|
@ -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.
|
* 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 position The coordinate to be checked
|
||||||
* @param region The region for the check.
|
* @param region The region for the check.
|
||||||
* This is a nested object represented by {@link RegionDto}
|
* This is a nested object represented by {@link Region}
|
||||||
* @see io.github.js0ny.ilp_coursework.controller.ApiController#getIsInRegion(RegionCheckRequestDto)
|
* @see io.github.js0ny.ilp_coursework.controller.ApiController#getIsInRegion(RegionCheckRequest)
|
||||||
*/
|
*/
|
||||||
public record RegionCheckRequestDto(LngLatDto position, RegionDto region) {
|
public record RegionCheckRequest(LngLat position, Region region) {
|
||||||
}
|
}
|
||||||
|
|
@ -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) {
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
package io.github.js0ny.ilp_coursework.service;
|
package io.github.js0ny.ilp_coursework.service;
|
||||||
|
|
||||||
import io.github.js0ny.ilp_coursework.data.AttrComparatorDto;
|
import io.github.js0ny.ilp_coursework.data.external.Drone;
|
||||||
import io.github.js0ny.ilp_coursework.data.DroneDto;
|
import io.github.js0ny.ilp_coursework.data.request.MedDispatchRecRequest;
|
||||||
import io.github.js0ny.ilp_coursework.data.MedDispatchRecDto;
|
import io.github.js0ny.ilp_coursework.data.external.ServicePointDrones;
|
||||||
import io.github.js0ny.ilp_coursework.data.ServicePointDronesDto;
|
|
||||||
import io.github.js0ny.ilp_coursework.util.AttrOperator;
|
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;
|
import static io.github.js0ny.ilp_coursework.util.AttrComparator.isValueMatched;
|
||||||
|
|
||||||
|
|
@ -59,9 +59,9 @@ public class DroneInfoService {
|
||||||
*/
|
*/
|
||||||
public String[] dronesWithCooling(boolean state) {
|
public String[] dronesWithCooling(boolean state) {
|
||||||
URI droneUrl = URI.create(baseUrl).resolve(dronesEndpoint);
|
URI droneUrl = URI.create(baseUrl).resolve(dronesEndpoint);
|
||||||
DroneDto[] drones = restTemplate.getForObject(
|
Drone[] drones = restTemplate.getForObject(
|
||||||
droneUrl,
|
droneUrl,
|
||||||
DroneDto[].class);
|
Drone[].class);
|
||||||
|
|
||||||
if (drones == null) {
|
if (drones == null) {
|
||||||
return new String[]{};
|
return new String[]{};
|
||||||
|
|
@ -69,12 +69,12 @@ public class DroneInfoService {
|
||||||
|
|
||||||
return Arrays.stream(drones)
|
return Arrays.stream(drones)
|
||||||
.filter(drone -> drone.capability().cooling() == state)
|
.filter(drone -> drone.capability().cooling() == state)
|
||||||
.map(DroneDto::id)
|
.map(Drone::id)
|
||||||
.toArray(String[]::new);
|
.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>
|
* <p>
|
||||||
* Associated service method with {@code /droneDetails/{id}}
|
* Associated service method with {@code /droneDetails/{id}}
|
||||||
*
|
*
|
||||||
|
|
@ -87,11 +87,11 @@ public class DroneInfoService {
|
||||||
* this should lead to a 404
|
* this should lead to a 404
|
||||||
* @see io.github.js0ny.ilp_coursework.controller.DroneController#getDroneDetail(String)
|
* @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);
|
URI droneUrl = URI.create(baseUrl).resolve(dronesEndpoint);
|
||||||
DroneDto[] drones = restTemplate.getForObject(
|
Drone[] drones = restTemplate.getForObject(
|
||||||
droneUrl,
|
droneUrl,
|
||||||
DroneDto[].class);
|
Drone[].class);
|
||||||
|
|
||||||
if (drones == null) {
|
if (drones == null) {
|
||||||
throw new NullPointerException("drone cannot be found");
|
throw new NullPointerException("drone cannot be found");
|
||||||
|
|
@ -131,7 +131,7 @@ public class DroneInfoService {
|
||||||
* @param attrComparators The filter rule with Name, Value and Operator
|
* @param attrComparators The filter rule with Name, Value and Operator
|
||||||
* @return array of drone ids that matches all rules
|
* @return array of drone ids that matches all rules
|
||||||
*/
|
*/
|
||||||
public String[] dronesSatisfyingAttributes(AttrComparatorDto[] attrComparators) {
|
public String[] dronesSatisfyingAttributes(AttrQueryRequest[] attrComparators) {
|
||||||
Set<String> matchingDroneIds = null;
|
Set<String> matchingDroneIds = null;
|
||||||
for (var comparator : attrComparators) {
|
for (var comparator : attrComparators) {
|
||||||
String attribute = comparator.attribute();
|
String attribute = comparator.attribute();
|
||||||
|
|
@ -170,9 +170,9 @@ public class DroneInfoService {
|
||||||
private String[] dronesWithAttributeCompared(String attrName, String attrVal, AttrOperator op) {
|
private String[] dronesWithAttributeCompared(String attrName, String attrVal, AttrOperator op) {
|
||||||
URI droneUrl = URI.create(baseUrl).resolve(dronesEndpoint);
|
URI droneUrl = URI.create(baseUrl).resolve(dronesEndpoint);
|
||||||
// This is required to make sure the response is valid
|
// This is required to make sure the response is valid
|
||||||
DroneDto[] drones = restTemplate.getForObject(
|
Drone[] drones = restTemplate.getForObject(
|
||||||
droneUrl,
|
droneUrl,
|
||||||
DroneDto[].class);
|
Drone[].class);
|
||||||
|
|
||||||
if (drones == null) {
|
if (drones == null) {
|
||||||
return new String[]{};
|
return new String[]{};
|
||||||
|
|
@ -193,7 +193,7 @@ public class DroneInfoService {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map(DroneDto::id)
|
.map(Drone::id)
|
||||||
.toArray(String[]::new);
|
.toArray(String[]::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,11 +207,11 @@ public class DroneInfoService {
|
||||||
* @return array of drone ids that match all the requirements
|
* @return array of drone ids that match all the requirements
|
||||||
* @see io.github.js0ny.ilp_coursework.controller.DroneController#queryAvailableDrones
|
* @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);
|
URI droneUrl = URI.create(baseUrl).resolve(dronesEndpoint);
|
||||||
DroneDto[] drones = restTemplate.getForObject(
|
Drone[] drones = restTemplate.getForObject(
|
||||||
droneUrl,
|
droneUrl,
|
||||||
DroneDto[].class);
|
Drone[].class);
|
||||||
|
|
||||||
if (drones == null || rec == null || rec.length == 0) {
|
if (drones == null || rec == null || rec.length == 0) {
|
||||||
return new String[]{};
|
return new String[]{};
|
||||||
|
|
@ -226,7 +226,7 @@ public class DroneInfoService {
|
||||||
.filter(record -> record != null && record.requirements() != null)
|
.filter(record -> record != null && record.requirements() != null)
|
||||||
// Every record must be met
|
// Every record must be met
|
||||||
.allMatch(record -> meetsRequirement(drone, record)))
|
.allMatch(record -> meetsRequirement(drone, record)))
|
||||||
.map(DroneDto::id)
|
.map(Drone::id)
|
||||||
.toArray(String[]::new);
|
.toArray(String[]::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -240,7 +240,7 @@ public class DroneInfoService {
|
||||||
* is invalid (capacity and id cannot be null
|
* is invalid (capacity and id cannot be null
|
||||||
* in {@code MedDispathRecDto})
|
* in {@code MedDispathRecDto})
|
||||||
*/
|
*/
|
||||||
private boolean meetsRequirement(DroneDto drone, MedDispatchRecDto record) {
|
private boolean meetsRequirement(Drone drone, MedDispatchRecRequest record) {
|
||||||
var requirements = record.requirements();
|
var requirements = record.requirements();
|
||||||
if (requirements == null) {
|
if (requirements == null) {
|
||||||
throw new IllegalArgumentException("requirements cannot be null");
|
throw new IllegalArgumentException("requirements cannot be null");
|
||||||
|
|
@ -284,11 +284,11 @@ public class DroneInfoService {
|
||||||
* time
|
* time
|
||||||
* @return true if the drone is available, false otherwise
|
* @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);
|
URI droneUrl = URI.create(baseUrl).resolve(dronesForServicePointsEndpoint);
|
||||||
ServicePointDronesDto[] servicePoints = restTemplate.getForObject(
|
ServicePointDrones[] servicePoints = restTemplate.getForObject(
|
||||||
droneUrl,
|
droneUrl,
|
||||||
ServicePointDronesDto[].class);
|
ServicePointDrones[].class);
|
||||||
|
|
||||||
LocalDate requiredDate = record.date();
|
LocalDate requiredDate = record.date();
|
||||||
DayOfWeek requiredDay = requiredDate.getDayOfWeek();
|
DayOfWeek requiredDay = requiredDate.getDayOfWeek();
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,19 @@
|
||||||
package io.github.js0ny.ilp_coursework.service;
|
package io.github.js0ny.ilp_coursework.service;
|
||||||
|
|
||||||
import io.github.js0ny.ilp_coursework.data.*;
|
|
||||||
|
|
||||||
import java.util.List;
|
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;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that handles calculations about Coordinates
|
* Class that handles calculations about Coordinates
|
||||||
*
|
*
|
||||||
* @see LngLatDto
|
* @see LngLat
|
||||||
* @see RegionDto
|
* @see Region
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class GpsCalculationService {
|
public class GpsCalculationService {
|
||||||
|
|
@ -18,30 +21,28 @@ public class GpsCalculationService {
|
||||||
/**
|
/**
|
||||||
* Given step size
|
* Given step size
|
||||||
*
|
*
|
||||||
* @see #nextPosition(LngLatDto, double)
|
* @see #nextPosition(LngLat, double)
|
||||||
*/
|
*/
|
||||||
private static final double STEP = 0.00015;
|
private static final double STEP = 0.00015;
|
||||||
/**
|
/**
|
||||||
* Given threshold to judge if two points are close to each other
|
* 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;
|
private static final double CLOSE_THRESHOLD = 0.00015;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the Euclidean distance between {@code position1} and
|
* Calculate the Euclidean distance between {@code position1} and
|
||||||
* {@code position2}, which are coordinates
|
* {@code position2}, which are coordinates
|
||||||
* defined as {@link LngLatDto}
|
* defined as {@link LngLat}
|
||||||
*
|
*
|
||||||
* @param position1 The coordinate of the first position
|
* @param position1 The coordinate of the first position
|
||||||
*
|
|
||||||
* @param position2 The coordinate of the second position
|
* @param position2 The coordinate of the second position
|
||||||
* @return The Euclidean distance between {@code position1} and
|
* @return The Euclidean distance between {@code position1} and
|
||||||
* {@code position2}
|
* {@code position2}
|
||||||
* @see
|
* @see io.github.js0ny.ilp_coursework.controller.ApiController#getDistance(DistanceRequest)
|
||||||
* io.github.js0ny.ilp_coursework.controller.ApiController#getDistance(DistanceRequestDto)
|
|
||||||
*/
|
*/
|
||||||
public double calculateDistance(LngLatDto position1, LngLatDto position2) {
|
public double calculateDistance(LngLat position1, LngLat position2) {
|
||||||
double lngDistance = position2.lng() - position1.lng();
|
double lngDistance = position2.lng() - position1.lng();
|
||||||
double latDistance = position2.lat() - position1.lat();
|
double latDistance = position2.lat() - position1.lat();
|
||||||
// Euclidean: \sqrt{a^2 + b^2}
|
// Euclidean: \sqrt{a^2 + b^2}
|
||||||
|
|
@ -51,20 +52,19 @@ public class GpsCalculationService {
|
||||||
/**
|
/**
|
||||||
* Check if {@code position1} and
|
* Check if {@code position1} and
|
||||||
* {@code position2} are close to each other, the threshold is < 0.00015
|
* {@code position2} are close to each other, the threshold is < 0.00015
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Note that = 0.00015 will be counted as not close to and will return {@code
|
* Note that = 0.00015 will be counted as not close to and will return {@code
|
||||||
* false}
|
* false}
|
||||||
*
|
*
|
||||||
* @param position1 The coordinate of the first position
|
* @param position1 The coordinate of the first position
|
||||||
*
|
|
||||||
* @param position2 The coordinate of the second position
|
* @param position2 The coordinate of the second position
|
||||||
* @return {@code true} if {@code position1} and
|
* @return {@code true} if {@code position1} and
|
||||||
* {@code position2} are close to each other
|
* {@code position2} are close to each other
|
||||||
* @see #CLOSE_THRESHOLD
|
* @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);
|
double distance = calculateDistance(position1, position2);
|
||||||
return distance < CLOSE_THRESHOLD;
|
return distance < CLOSE_THRESHOLD;
|
||||||
}
|
}
|
||||||
|
|
@ -75,17 +75,16 @@ public class GpsCalculationService {
|
||||||
* 0.00015
|
* 0.00015
|
||||||
*
|
*
|
||||||
* @param start The coordinate of the original start point.
|
* @param start The coordinate of the original start point.
|
||||||
*
|
|
||||||
* @param angle The direction to be moved in angle.
|
* @param angle The direction to be moved in angle.
|
||||||
* @return The next position moved from {@code start}
|
* @return The next position moved from {@code start}
|
||||||
* @see #STEP
|
* @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 rad = Math.toRadians(angle); // Convert to radian for Java triangle function calculation
|
||||||
double newLng = Math.cos(rad) * STEP + start.lng();
|
double newLng = Math.cos(rad) * STEP + start.lng();
|
||||||
double newLat = Math.sin(rad) * STEP + start.lat();
|
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.
|
* is inside the {@code region}, on edge and vertex is considered as inside.
|
||||||
*
|
*
|
||||||
* @param position The coordinate of the position.
|
* @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}
|
* {@code LngLatDto}
|
||||||
* @return {@code true} if {@code position} is inside the {@code region}.
|
* @return {@code true} if {@code position} is inside the {@code region}.
|
||||||
* @throws IllegalArgumentException If {@code region} is not closed
|
* @throws IllegalArgumentException If {@code region} is not closed
|
||||||
* @see io.github.js0ny.ilp_coursework.controller.ApiController#getIsInRegion(RegionCheckRequestDto)
|
* @see io.github.js0ny.ilp_coursework.controller.ApiController#getIsInRegion(RegionCheckRequest)
|
||||||
* @see RegionDto#isClosed()
|
* @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
|
if (!region.isClosed()) { // call method from RegionDto to check if not closed
|
||||||
throw new IllegalArgumentException("Region is not closed.");
|
throw new IllegalArgumentException("Region is not closed.");
|
||||||
}
|
}
|
||||||
|
|
@ -115,16 +114,16 @@ public class GpsCalculationService {
|
||||||
* @param polygon The region that forms a polygon to check if {@code point}
|
* @param polygon The region that forms a polygon to check if {@code point}
|
||||||
* sits inside.
|
* sits inside.
|
||||||
* @return If the {@code point} sits inside the {@code polygon} then
|
* @return If the {@code point} sits inside the {@code polygon} then
|
||||||
* return {@code true}
|
* return {@code true}
|
||||||
* @see #isPointOnEdge(LngLatDto, LngLatDto, LngLatDto)
|
* @see #isPointOnEdge(LngLat, LngLat, LngLat)
|
||||||
* @see #checkIsInRegion(LngLatDto, RegionDto)
|
* @see #checkIsInRegion(LngLat, Region)
|
||||||
*/
|
*/
|
||||||
private boolean rayCasting(LngLatDto point, List<LngLatDto> polygon) {
|
private boolean rayCasting(LngLat point, List<LngLat> polygon) {
|
||||||
int intersections = 0;
|
int intersections = 0;
|
||||||
int n = polygon.size();
|
int n = polygon.size();
|
||||||
for (int i = 0; i < n; ++i) {
|
for (int i = 0; i < n; ++i) {
|
||||||
LngLatDto a = polygon.get(i);
|
LngLat a = polygon.get(i);
|
||||||
LngLatDto b = polygon.get((i + 1) % n); // Next vertex
|
LngLat b = polygon.get((i + 1) % n); // Next vertex
|
||||||
|
|
||||||
if (isPointOnEdge(point, a, b)) {
|
if (isPointOnEdge(point, a, b)) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -132,7 +131,7 @@ public class GpsCalculationService {
|
||||||
|
|
||||||
// Ensure that `a` is norther than `b`, in order to easy classification
|
// Ensure that `a` is norther than `b`, in order to easy classification
|
||||||
if (a.lat() > b.lat()) {
|
if (a.lat() > b.lat()) {
|
||||||
LngLatDto temp = a;
|
LngLat temp = a;
|
||||||
a = b;
|
a = b;
|
||||||
b = temp;
|
b = temp;
|
||||||
}
|
}
|
||||||
|
|
@ -165,13 +164,12 @@ public class GpsCalculationService {
|
||||||
* {@code a} and {@code b}
|
* {@code a} and {@code b}
|
||||||
*
|
*
|
||||||
* @param p point to be checked on the edge
|
* @param p point to be checked on the edge
|
||||||
*
|
|
||||||
* @param a point that forms the edge
|
* @param a point that forms the edge
|
||||||
* @param b point that forms the edge
|
* @param b point that forms the edge
|
||||||
* @return {@code true} if {@code p} is on {@code ab}
|
* @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)
|
// Cross product: (p - a) × (b - a)
|
||||||
double crossProduct = (p.lng() - a.lng()) * (b.lat() - a.lat())
|
double crossProduct = (p.lng() - a.lng()) * (b.lat() - a.lat())
|
||||||
- (p.lat() - a.lat()) * (b.lng() - a.lng());
|
- (p.lat() - a.lat()) * (b.lng() - a.lng());
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,15 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
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 io.github.js0ny.ilp_coursework.service.GpsCalculationService;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Nested;
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
@ -55,22 +61,22 @@ public class ApiControllerTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("POST /distanceTo -> 200 OK")
|
@DisplayName("POST /distanceTo -> 200 OK")
|
||||||
void getDistance_shouldReturn200AndDistance_whenCorrectInput()
|
void getDistance_shouldReturn200AndDistance_whenCorrectInput()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
double expected = 5.0;
|
double expected = 5.0;
|
||||||
String endpoint = "/api/v1/distanceTo";
|
String endpoint = "/api/v1/distanceTo";
|
||||||
LngLatDto p1 = new LngLatDto(0, 4.0);
|
LngLat p1 = new LngLat(0, 4.0);
|
||||||
LngLatDto p2 = new LngLatDto(3.0, 0);
|
LngLat p2 = new LngLat(3.0, 0);
|
||||||
var req = new DistanceRequestDto(p1, p2);
|
var req = new DistanceRequest(p1, p2);
|
||||||
when(
|
when(
|
||||||
service.calculateDistance(
|
service.calculateDistance(
|
||||||
any(LngLatDto.class),
|
any(LngLat.class),
|
||||||
any(LngLatDto.class)
|
any(LngLat.class)
|
||||||
)
|
)
|
||||||
).thenReturn(expected);
|
).thenReturn(expected);
|
||||||
var mock = mockMvc.perform(
|
var mock = mockMvc.perform(
|
||||||
post(endpoint)
|
post(endpoint)
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(req))
|
.content(objectMapper.writeValueAsString(req))
|
||||||
);
|
);
|
||||||
|
|
||||||
mock.andExpect(status().isOk());
|
mock.andExpect(status().isOk());
|
||||||
|
|
@ -82,23 +88,23 @@ public class ApiControllerTest {
|
||||||
void getDistance_shouldReturn400_whenMissingField() throws Exception {
|
void getDistance_shouldReturn400_whenMissingField() throws Exception {
|
||||||
String endpoint = "/api/v1/distanceTo";
|
String endpoint = "/api/v1/distanceTo";
|
||||||
String req = """
|
String req = """
|
||||||
{
|
{
|
||||||
"position1": {
|
"position1": {
|
||||||
"lng": 3.0,
|
"lng": 3.0,
|
||||||
"lat": 4.0
|
"lat": 4.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
""";
|
||||||
""";
|
|
||||||
when(
|
when(
|
||||||
service.calculateDistance(any(LngLatDto.class), isNull())
|
service.calculateDistance(any(LngLat.class), isNull())
|
||||||
).thenThrow(new NullPointerException());
|
).thenThrow(new NullPointerException());
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(
|
.perform(
|
||||||
post(endpoint)
|
post(endpoint)
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(req)
|
.content(req)
|
||||||
)
|
)
|
||||||
.andExpect(status().isBadRequest());
|
.andExpect(status().isBadRequest());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,19 +115,19 @@ public class ApiControllerTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("POST /isCloseTo -> 200 OK")
|
@DisplayName("POST /isCloseTo -> 200 OK")
|
||||||
void getIsCloseTo_shouldReturn200AndBoolean_whenCorrectInput()
|
void getIsCloseTo_shouldReturn200AndBoolean_whenCorrectInput()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
boolean expected = false;
|
boolean expected = false;
|
||||||
String endpoint = "/api/v1/isCloseTo";
|
String endpoint = "/api/v1/isCloseTo";
|
||||||
LngLatDto p1 = new LngLatDto(0, 4.0);
|
LngLat p1 = new LngLat(0, 4.0);
|
||||||
LngLatDto p2 = new LngLatDto(3.0, 0);
|
LngLat p2 = new LngLat(3.0, 0);
|
||||||
var req = new DistanceRequestDto(p1, p2);
|
var req = new DistanceRequest(p1, p2);
|
||||||
when(
|
when(
|
||||||
service.isCloseTo(any(LngLatDto.class), any(LngLatDto.class))
|
service.isCloseTo(any(LngLat.class), any(LngLat.class))
|
||||||
).thenReturn(expected);
|
).thenReturn(expected);
|
||||||
var mock = mockMvc.perform(
|
var mock = mockMvc.perform(
|
||||||
post(endpoint)
|
post(endpoint)
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(req))
|
.content(objectMapper.writeValueAsString(req))
|
||||||
);
|
);
|
||||||
|
|
||||||
mock.andExpect(status().isOk());
|
mock.andExpect(status().isOk());
|
||||||
|
|
@ -131,19 +137,19 @@ public class ApiControllerTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("POST /isCloseTo -> 400 Bad Request: Malformed JSON ")
|
@DisplayName("POST /isCloseTo -> 400 Bad Request: Malformed JSON ")
|
||||||
void getIsCloseTo_shouldReturn400_whenJsonIsMalformed()
|
void getIsCloseTo_shouldReturn400_whenJsonIsMalformed()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
// json without a bracket
|
// json without a bracket
|
||||||
String malformedJson = """
|
String malformedJson = """
|
||||||
{
|
{
|
||||||
"position1": { "lng": 0.0, "lat": 3.0 }
|
"position1": { "lng": 0.0, "lat": 3.0 }
|
||||||
""";
|
""";
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(
|
.perform(
|
||||||
post("/api/v1/isCloseTo")
|
post("/api/v1/isCloseTo")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(malformedJson)
|
.content(malformedJson)
|
||||||
)
|
)
|
||||||
.andExpect(status().isBadRequest());
|
.andExpect(status().isBadRequest());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,46 +162,46 @@ public class ApiControllerTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("POST /nextPosition -> 200 OK")
|
@DisplayName("POST /nextPosition -> 200 OK")
|
||||||
void getNextPosition_shouldReturn200AndCoordinate_whenCorrectInput()
|
void getNextPosition_shouldReturn200AndCoordinate_whenCorrectInput()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
LngLatDto expected = new LngLatDto(0.00015, 0.0);
|
LngLat expected = new LngLat(0.00015, 0.0);
|
||||||
LngLatDto p = new LngLatDto(0, 0);
|
LngLat p = new LngLat(0, 0);
|
||||||
var req = new MovementRequestDto(p, 0);
|
var req = new MovementRequest(p, 0);
|
||||||
when(
|
when(
|
||||||
service.nextPosition(any(LngLatDto.class), anyDouble())
|
service.nextPosition(any(LngLat.class), anyDouble())
|
||||||
).thenReturn(expected);
|
).thenReturn(expected);
|
||||||
var mock = mockMvc.perform(
|
var mock = mockMvc.perform(
|
||||||
post(endpoint)
|
post(endpoint)
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(req))
|
.content(objectMapper.writeValueAsString(req))
|
||||||
);
|
);
|
||||||
|
|
||||||
mock.andExpect(status().isOk());
|
mock.andExpect(status().isOk());
|
||||||
mock.andExpect(
|
mock.andExpect(
|
||||||
content().json(objectMapper.writeValueAsString(expected))
|
content().json(objectMapper.writeValueAsString(expected))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("POST /nextPosition -> 400 Bad Request: Missing Field")
|
@DisplayName("POST /nextPosition -> 400 Bad Request: Missing Field")
|
||||||
void getNextPosition_shouldReturn400_whenKeyNameError()
|
void getNextPosition_shouldReturn400_whenKeyNameError()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
// "position" should be "start"
|
// "position" should be "start"
|
||||||
String malformedJson = """
|
String malformedJson = """
|
||||||
{
|
{
|
||||||
"position": { "lng": 0.0, "lat": 3.0 },
|
"position": { "lng": 0.0, "lat": 3.0 },
|
||||||
"angle": 180
|
"angle": 180
|
||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
when(service.nextPosition(isNull(), anyDouble())).thenThrow(
|
when(service.nextPosition(isNull(), anyDouble())).thenThrow(
|
||||||
new NullPointerException()
|
new NullPointerException()
|
||||||
);
|
);
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(
|
.perform(
|
||||||
post("/api/v1/nextPosition")
|
post("/api/v1/nextPosition")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(malformedJson)
|
.content(malformedJson)
|
||||||
)
|
)
|
||||||
.andExpect(MockMvcResultMatchers.status().isBadRequest());
|
.andExpect(MockMvcResultMatchers.status().isBadRequest());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,31 +212,31 @@ public class ApiControllerTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("POST /isInRegion -> 200 OK")
|
@DisplayName("POST /isInRegion -> 200 OK")
|
||||||
void getIsInRegion_shouldReturn200AndBoolean_whenCorrectInput()
|
void getIsInRegion_shouldReturn200AndBoolean_whenCorrectInput()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
boolean expected = false;
|
boolean expected = false;
|
||||||
String endpoint = "/api/v1/isInRegion";
|
String endpoint = "/api/v1/isInRegion";
|
||||||
var position = new LngLatDto(1.234, 1.222);
|
var position = new LngLat(1.234, 1.222);
|
||||||
var region = new RegionDto(
|
var region = new Region(
|
||||||
"central",
|
"central",
|
||||||
List.of(
|
List.of(
|
||||||
new LngLatDto(-3.192473, 55.946233),
|
new LngLat(-3.192473, 55.946233),
|
||||||
new LngLatDto(-3.192473, 55.942617),
|
new LngLat(-3.192473, 55.942617),
|
||||||
new LngLatDto(-3.184319, 55.942617),
|
new LngLat(-3.184319, 55.942617),
|
||||||
new LngLatDto(-3.184319, 55.946233),
|
new LngLat(-3.184319, 55.946233),
|
||||||
new LngLatDto(-3.192473, 55.946233)
|
new LngLat(-3.192473, 55.946233)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
var req = new RegionCheckRequestDto(position, region);
|
var req = new RegionCheckRequest(position, region);
|
||||||
when(
|
when(
|
||||||
service.checkIsInRegion(
|
service.checkIsInRegion(
|
||||||
any(LngLatDto.class),
|
any(LngLat.class),
|
||||||
any(RegionDto.class)
|
any(Region.class)
|
||||||
)
|
)
|
||||||
).thenReturn(expected);
|
).thenReturn(expected);
|
||||||
var mock = mockMvc.perform(
|
var mock = mockMvc.perform(
|
||||||
post(endpoint)
|
post(endpoint)
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(req))
|
.content(objectMapper.writeValueAsString(req))
|
||||||
);
|
);
|
||||||
|
|
||||||
mock.andExpect(status().isOk());
|
mock.andExpect(status().isOk());
|
||||||
|
|
@ -239,59 +245,59 @@ public class ApiControllerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName(
|
@DisplayName(
|
||||||
"POST /isInRegion -> 400 Bad Request: Passing a list of empty vertices to isInRegion"
|
"POST /isInRegion -> 400 Bad Request: Passing a list of empty vertices to isInRegion"
|
||||||
)
|
)
|
||||||
void getIsInRegion_shouldReturn400_whenPassingIllegalArguments()
|
void getIsInRegion_shouldReturn400_whenPassingIllegalArguments()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
var position = new LngLatDto(1, 1);
|
var position = new LngLat(1, 1);
|
||||||
var region = new RegionDto("illegal", List.of());
|
var region = new Region("illegal", List.of());
|
||||||
var request = new RegionCheckRequestDto(position, region);
|
var request = new RegionCheckRequest(position, region);
|
||||||
when(
|
when(
|
||||||
service.checkIsInRegion(
|
service.checkIsInRegion(
|
||||||
any(LngLatDto.class),
|
any(LngLat.class),
|
||||||
any(RegionDto.class)
|
any(Region.class)
|
||||||
)
|
)
|
||||||
).thenThrow(new IllegalArgumentException("Region is not closed."));
|
).thenThrow(new IllegalArgumentException("Region is not closed."));
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(
|
.perform(
|
||||||
post("/api/v1/isInRegion")
|
post("/api/v1/isInRegion")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(request))
|
.content(objectMapper.writeValueAsString(request))
|
||||||
)
|
)
|
||||||
.andExpect(status().isBadRequest());
|
.andExpect(status().isBadRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName(
|
@DisplayName(
|
||||||
"POST /isInRegion -> 400 Bad Request: Passing a list of not-closing vertices to isInRegion"
|
"POST /isInRegion -> 400 Bad Request: Passing a list of not-closing vertices to isInRegion"
|
||||||
)
|
)
|
||||||
void getIsInRegion_shouldReturn400_whenPassingNotClosingVertices()
|
void getIsInRegion_shouldReturn400_whenPassingNotClosingVertices()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
var position = new LngLatDto(1, 1);
|
var position = new LngLat(1, 1);
|
||||||
var region = new RegionDto(
|
var region = new Region(
|
||||||
"illegal",
|
"illegal",
|
||||||
List.of(
|
List.of(
|
||||||
new LngLatDto(1, 2),
|
new LngLat(1, 2),
|
||||||
new LngLatDto(3, 4),
|
new LngLat(3, 4),
|
||||||
new LngLatDto(5, 6),
|
new LngLat(5, 6),
|
||||||
new LngLatDto(7, 8),
|
new LngLat(7, 8),
|
||||||
new LngLatDto(9, 10)
|
new LngLat(9, 10)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
var request = new RegionCheckRequestDto(position, region);
|
var request = new RegionCheckRequest(position, region);
|
||||||
when(
|
when(
|
||||||
service.checkIsInRegion(
|
service.checkIsInRegion(
|
||||||
any(LngLatDto.class),
|
any(LngLat.class),
|
||||||
any(RegionDto.class)
|
any(Region.class)
|
||||||
)
|
)
|
||||||
).thenThrow(new IllegalArgumentException("Region is not closed."));
|
).thenThrow(new IllegalArgumentException("Region is not closed."));
|
||||||
mockMvc
|
mockMvc
|
||||||
.perform(
|
.perform(
|
||||||
post("/api/v1/isInRegion")
|
post("/api/v1/isInRegion")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(request))
|
.content(objectMapper.writeValueAsString(request))
|
||||||
)
|
)
|
||||||
.andExpect(status().isBadRequest());
|
.andExpect(status().isBadRequest());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package io.github.js0ny.ilp_coursework.service;
|
package io.github.js0ny.ilp_coursework.service;
|
||||||
|
|
||||||
import io.github.js0ny.ilp_coursework.data.LngLatDto;
|
import io.github.js0ny.ilp_coursework.data.common.LngLat;
|
||||||
import io.github.js0ny.ilp_coursework.data.RegionDto;
|
import io.github.js0ny.ilp_coursework.data.common.Region;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Nested;
|
import org.junit.jupiter.api.Nested;
|
||||||
|
|
@ -32,8 +32,8 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("False: Given Example For Testing")
|
@DisplayName("False: Given Example For Testing")
|
||||||
void isCloseTo_shouldReturnFalse_givenExample() {
|
void isCloseTo_shouldReturnFalse_givenExample() {
|
||||||
var p1 = new LngLatDto(-3.192473, 55.946233);
|
var p1 = new LngLat(-3.192473, 55.946233);
|
||||||
var p2 = new LngLatDto(-3.192473, 55.942617);
|
var p2 = new LngLat(-3.192473, 55.942617);
|
||||||
double expected = 0.0036;
|
double expected = 0.0036;
|
||||||
double actual = service.calculateDistance(p1, p2);
|
double actual = service.calculateDistance(p1, p2);
|
||||||
assertThat(actual).isCloseTo(expected, within(1e-4));
|
assertThat(actual).isCloseTo(expected, within(1e-4));
|
||||||
|
|
@ -42,8 +42,8 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("General Case: 3-4-5 Triangle")
|
@DisplayName("General Case: 3-4-5 Triangle")
|
||||||
void calculateDistance_shouldReturnCorrectEuclideanDistance_forGeneralCase() {
|
void calculateDistance_shouldReturnCorrectEuclideanDistance_forGeneralCase() {
|
||||||
var p1 = new LngLatDto(0, 3.0);
|
var p1 = new LngLat(0, 3.0);
|
||||||
var p2 = new LngLatDto(4.0, 0);
|
var p2 = new LngLat(4.0, 0);
|
||||||
double expected = 5.0;
|
double expected = 5.0;
|
||||||
double actual = service.calculateDistance(p1, p2);
|
double actual = service.calculateDistance(p1, p2);
|
||||||
assertThat(actual).isCloseTo(expected, within(PRECISION));
|
assertThat(actual).isCloseTo(expected, within(PRECISION));
|
||||||
|
|
@ -52,7 +52,7 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Edge Case: Points are Identical")
|
@DisplayName("Edge Case: Points are Identical")
|
||||||
void calculateDistance_shouldReturnZero_whenPointsAreIdentical() {
|
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 expected = 0.0;
|
||||||
double actual = service.calculateDistance(p1, p1);
|
double actual = service.calculateDistance(p1, p1);
|
||||||
assertThat(actual).isEqualTo(expected);
|
assertThat(actual).isEqualTo(expected);
|
||||||
|
|
@ -61,8 +61,8 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Edge Case: Longitudinal-only movement")
|
@DisplayName("Edge Case: Longitudinal-only movement")
|
||||||
void calculateDistance_shouldReturnCorrectDistance_forPointsWithSameLatitude() {
|
void calculateDistance_shouldReturnCorrectDistance_forPointsWithSameLatitude() {
|
||||||
var p1 = new LngLatDto(123.85, 983.2119);
|
var p1 = new LngLat(123.85, 983.2119);
|
||||||
var p2 = new LngLatDto(133.85, 983.2119);
|
var p2 = new LngLat(133.85, 983.2119);
|
||||||
double expected = 10.0;
|
double expected = 10.0;
|
||||||
double actual = service.calculateDistance(p1, p2);
|
double actual = service.calculateDistance(p1, p2);
|
||||||
assertThat(actual).isCloseTo(expected, within(PRECISION));
|
assertThat(actual).isCloseTo(expected, within(PRECISION));
|
||||||
|
|
@ -71,8 +71,8 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Edge Case: Latitude-only movement")
|
@DisplayName("Edge Case: Latitude-only movement")
|
||||||
void calculateDistance_shouldReturnCorrectDistance_forPointsWithSameLongitude() {
|
void calculateDistance_shouldReturnCorrectDistance_forPointsWithSameLongitude() {
|
||||||
var p1 = new LngLatDto(123.85, 983.2119);
|
var p1 = new LngLat(123.85, 983.2119);
|
||||||
var p2 = new LngLatDto(123.85, 973.2119);
|
var p2 = new LngLat(123.85, 973.2119);
|
||||||
double expected = 10.0;
|
double expected = 10.0;
|
||||||
double actual = service.calculateDistance(p1, p2);
|
double actual = service.calculateDistance(p1, p2);
|
||||||
assertThat(actual).isCloseTo(expected, within(PRECISION));
|
assertThat(actual).isCloseTo(expected, within(PRECISION));
|
||||||
|
|
@ -81,8 +81,8 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("General Case: Calculate with negative Coordinates")
|
@DisplayName("General Case: Calculate with negative Coordinates")
|
||||||
void calculateDistance_shouldReturnCorrectEuclideanDistance_forNegativeCoordinates() {
|
void calculateDistance_shouldReturnCorrectEuclideanDistance_forNegativeCoordinates() {
|
||||||
LngLatDto p1 = new LngLatDto(-1.0, -2.0);
|
LngLat p1 = new LngLat(-1.0, -2.0);
|
||||||
LngLatDto p2 = new LngLatDto(2.0, 2.0); // lngDiff = 3, latDiff = 4
|
LngLat p2 = new LngLat(2.0, 2.0); // lngDiff = 3, latDiff = 4
|
||||||
double expected = 5.0;
|
double expected = 5.0;
|
||||||
double actual = service.calculateDistance(p1, p2);
|
double actual = service.calculateDistance(p1, p2);
|
||||||
assertThat(actual).isCloseTo(expected, within(PRECISION));
|
assertThat(actual).isCloseTo(expected, within(PRECISION));
|
||||||
|
|
@ -95,8 +95,8 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("False: Given Example For Testing")
|
@DisplayName("False: Given Example For Testing")
|
||||||
void isCloseTo_shouldReturnFalse_givenExample() {
|
void isCloseTo_shouldReturnFalse_givenExample() {
|
||||||
var p1 = new LngLatDto(-3.192473, 55.946233);
|
var p1 = new LngLat(-3.192473, 55.946233);
|
||||||
var p2 = new LngLatDto(-3.192473, 55.942617);
|
var p2 = new LngLat(-3.192473, 55.942617);
|
||||||
boolean expected = false;
|
boolean expected = false;
|
||||||
boolean actual = service.isCloseTo(p1, p2);
|
boolean actual = service.isCloseTo(p1, p2);
|
||||||
assertThat(actual).isEqualTo(expected);
|
assertThat(actual).isEqualTo(expected);
|
||||||
|
|
@ -105,7 +105,7 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("True: Two points are the same")
|
@DisplayName("True: Two points are the same")
|
||||||
void isCloseTo_shouldReturnTrue_whenPointsAreIdentical() {
|
void isCloseTo_shouldReturnTrue_whenPointsAreIdentical() {
|
||||||
var p1 = new LngLatDto(151.86, 285.37);
|
var p1 = new LngLat(151.86, 285.37);
|
||||||
boolean expected = true;
|
boolean expected = true;
|
||||||
boolean actual = service.isCloseTo(p1, p1);
|
boolean actual = service.isCloseTo(p1, p1);
|
||||||
assertThat(actual).isEqualTo(expected);
|
assertThat(actual).isEqualTo(expected);
|
||||||
|
|
@ -114,8 +114,8 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("True: Two points are close to each other and near threshold")
|
@DisplayName("True: Two points are close to each other and near threshold")
|
||||||
void isCloseTo_shouldReturnTrue_whenCloseAndSmallerThanThreshold() {
|
void isCloseTo_shouldReturnTrue_whenCloseAndSmallerThanThreshold() {
|
||||||
var p1 = new LngLatDto(0.0, 0.0);
|
var p1 = new LngLat(0.0, 0.0);
|
||||||
var p2 = new LngLatDto(0.0, 0.00014);
|
var p2 = new LngLat(0.0, 0.00014);
|
||||||
boolean expected = true;
|
boolean expected = true;
|
||||||
boolean actual = service.isCloseTo(p1, p2);
|
boolean actual = service.isCloseTo(p1, p2);
|
||||||
assertThat(actual).isEqualTo(expected);
|
assertThat(actual).isEqualTo(expected);
|
||||||
|
|
@ -124,8 +124,8 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("False: Distance nears the threshold")
|
@DisplayName("False: Distance nears the threshold")
|
||||||
void isCloseTo_shouldReturnFalse_whenEqualsToThreshold() {
|
void isCloseTo_shouldReturnFalse_whenEqualsToThreshold() {
|
||||||
var p1 = new LngLatDto(0.0, 0.0);
|
var p1 = new LngLat(0.0, 0.0);
|
||||||
var p2 = new LngLatDto(0.0, 0.00015);
|
var p2 = new LngLat(0.0, 0.00015);
|
||||||
boolean expected = false;
|
boolean expected = false;
|
||||||
boolean actual = service.isCloseTo(p1, p2);
|
boolean actual = service.isCloseTo(p1, p2);
|
||||||
assertThat(actual).isEqualTo(expected);
|
assertThat(actual).isEqualTo(expected);
|
||||||
|
|
@ -134,8 +134,8 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("False: Distance larger to threshold")
|
@DisplayName("False: Distance larger to threshold")
|
||||||
void isCloseTo_shouldReturnFalse_whenNotCloseAndLargerThanThreshold() {
|
void isCloseTo_shouldReturnFalse_whenNotCloseAndLargerThanThreshold() {
|
||||||
var p1 = new LngLatDto(0.0, 0.0);
|
var p1 = new LngLat(0.0, 0.0);
|
||||||
var p2 = new LngLatDto(0.0, 0.00016);
|
var p2 = new LngLat(0.0, 0.00016);
|
||||||
boolean expected = false;
|
boolean expected = false;
|
||||||
boolean actual = service.isCloseTo(p1, p2);
|
boolean actual = service.isCloseTo(p1, p2);
|
||||||
assertThat(actual).isEqualTo(expected);
|
assertThat(actual).isEqualTo(expected);
|
||||||
|
|
@ -149,10 +149,10 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("General Case: nextPosition in East direction (0 degrees)")
|
@DisplayName("General Case: nextPosition in East direction (0 degrees)")
|
||||||
void nextPosition_shouldMoveEast_forAngleZero() {
|
void nextPosition_shouldMoveEast_forAngleZero() {
|
||||||
var start = new LngLatDto(0.0, 0.0);
|
var start = new LngLat(0.0, 0.0);
|
||||||
double angle = 0;
|
double angle = 0;
|
||||||
// For 0 degrees, cos(0)=1, sin(0)=0. Move happens entirely on lng axis.
|
// 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);
|
var actual = service.nextPosition(start, angle);
|
||||||
|
|
||||||
|
|
@ -163,10 +163,10 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Cardinal Direction: nextPosition in North direction (90 degrees)")
|
@DisplayName("Cardinal Direction: nextPosition in North direction (90 degrees)")
|
||||||
void nextPosition_shouldMoveNorth_forAngle90() {
|
void nextPosition_shouldMoveNorth_forAngle90() {
|
||||||
var start = new LngLatDto(0.0, 0.0);
|
var start = new LngLat(0.0, 0.0);
|
||||||
double angle = 90;
|
double angle = 90;
|
||||||
// For 90 degrees, cos(90)=0, sin(90)=1. Move happens entirely on lat axis.
|
// 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);
|
var actual = service.nextPosition(start, angle);
|
||||||
|
|
||||||
|
|
@ -177,11 +177,11 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Cardinal Direction: nextPosition in West direction (180 degrees)")
|
@DisplayName("Cardinal Direction: nextPosition in West direction (180 degrees)")
|
||||||
void nextPosition_shouldMoveWest_forAngle180() {
|
void nextPosition_shouldMoveWest_forAngle180() {
|
||||||
var start = new LngLatDto(0.0, 0.0);
|
var start = new LngLat(0.0, 0.0);
|
||||||
double angle = 180;
|
double angle = 180;
|
||||||
// For 180 degrees, cos(180)=-1, sin(180)=0. Move happens entirely on negative
|
// For 180 degrees, cos(180)=-1, sin(180)=0. Move happens entirely on negative
|
||||||
// lng axis.
|
// lng axis.
|
||||||
var expected = new LngLatDto(-STEP, 0.0);
|
var expected = new LngLat(-STEP, 0.0);
|
||||||
|
|
||||||
var actual = service.nextPosition(start, angle);
|
var actual = service.nextPosition(start, angle);
|
||||||
|
|
||||||
|
|
@ -192,11 +192,11 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Cardinal Direction: nextPosition in South direction (270 degrees)")
|
@DisplayName("Cardinal Direction: nextPosition in South direction (270 degrees)")
|
||||||
void nextPosition_shouldMoveSouth_forAngle270() {
|
void nextPosition_shouldMoveSouth_forAngle270() {
|
||||||
var start = new LngLatDto(0.0, 0.0);
|
var start = new LngLat(0.0, 0.0);
|
||||||
double angle = 270;
|
double angle = 270;
|
||||||
// For 270 degrees, cos(270)=0, sin(270)=-1. Move happens entirely on negative
|
// For 270 degrees, cos(270)=0, sin(270)=-1. Move happens entirely on negative
|
||||||
// lat axis.
|
// lat axis.
|
||||||
var expected = new LngLatDto(0.0, -STEP);
|
var expected = new LngLat(0.0, -STEP);
|
||||||
|
|
||||||
var actual = service.nextPosition(start, angle);
|
var actual = service.nextPosition(start, angle);
|
||||||
|
|
||||||
|
|
@ -207,12 +207,12 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Intercardinal Direction: nextPosition in Northeast direction (45 degrees)")
|
@DisplayName("Intercardinal Direction: nextPosition in Northeast direction (45 degrees)")
|
||||||
void nextPosition_shouldMoveNortheast_forAngle45() {
|
void nextPosition_shouldMoveNortheast_forAngle45() {
|
||||||
var start = new LngLatDto(0.0, 0.0);
|
var start = new LngLat(0.0, 0.0);
|
||||||
double angle = 45;
|
double angle = 45;
|
||||||
// Δlng = step * cos(45°), Δlat = step * sin(45°)
|
// Δlng = step * cos(45°), Δlat = step * sin(45°)
|
||||||
double expectedLng = STEP * Math.cos(Math.toRadians(angle));
|
double expectedLng = STEP * Math.cos(Math.toRadians(angle));
|
||||||
double expectedLat = STEP * Math.sin(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);
|
var actual = service.nextPosition(start, angle);
|
||||||
|
|
||||||
|
|
@ -223,13 +223,13 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Edge Case: Angle larger than 360 should wrap around")
|
@DisplayName("Edge Case: Angle larger than 360 should wrap around")
|
||||||
void nextPosition_shouldHandleAngleGreaterThan360() {
|
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).
|
// 405 degrees is equivalent to 45 degrees (405 % 360 = 45).
|
||||||
double angle = 405;
|
double angle = 405;
|
||||||
double equivalentAngle = 45;
|
double equivalentAngle = 45;
|
||||||
double expectedLng = STEP * Math.cos(Math.toRadians(equivalentAngle));
|
double expectedLng = STEP * Math.cos(Math.toRadians(equivalentAngle));
|
||||||
double expectedLat = STEP * Math.sin(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);
|
var actual = service.nextPosition(start, angle);
|
||||||
|
|
||||||
|
|
@ -240,12 +240,12 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Edge Case: Negative angle should work correctly")
|
@DisplayName("Edge Case: Negative angle should work correctly")
|
||||||
void nextPosition_shouldHandleNegativeAngle() {
|
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.
|
// A negative angle of -45° corresponds to the Southeast direction.
|
||||||
double angle = -45;
|
double angle = -45;
|
||||||
double expectedLng = STEP * Math.cos(Math.toRadians(angle));
|
double expectedLng = STEP * Math.cos(Math.toRadians(angle));
|
||||||
double expectedLat = STEP * Math.sin(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);
|
var actual = service.nextPosition(start, angle);
|
||||||
|
|
||||||
|
|
@ -258,17 +258,17 @@ public class GpsCalculationServiceTest {
|
||||||
@DisplayName("Test for checkIsInRegion(LngLatDto, RegionDto) -> boolean")
|
@DisplayName("Test for checkIsInRegion(LngLatDto, RegionDto) -> boolean")
|
||||||
class CheckIsInRegionTests {
|
class CheckIsInRegionTests {
|
||||||
|
|
||||||
public static final RegionDto RECTANGLE_REGION = new RegionDto("rectangle", List.of(new LngLatDto(0.0, 0.0),
|
public static final Region RECTANGLE_REGION = new Region("rectangle", List.of(new LngLat(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)));
|
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
|
@Test
|
||||||
@DisplayName("General Case: Given Example for Testing")
|
@DisplayName("General Case: Given Example for Testing")
|
||||||
void isInRegion_shouldReturnFalse_givenPolygonCentral() {
|
void isInRegion_shouldReturnFalse_givenPolygonCentral() {
|
||||||
var position = new LngLatDto(1.234, 1.222);
|
var position = new LngLat(1.234, 1.222);
|
||||||
var region = new RegionDto("central",
|
var region = new Region("central",
|
||||||
List.of(new LngLatDto(-3.192473, 55.946233), new LngLatDto(-3.192473, 55.942617),
|
List.of(new LngLat(-3.192473, 55.946233), new LngLat(-3.192473, 55.942617),
|
||||||
new LngLatDto(-3.184319, 55.942617), new LngLatDto(-3.184319, 55.946233),
|
new LngLat(-3.184319, 55.942617), new LngLat(-3.184319, 55.946233),
|
||||||
new LngLatDto(-3.192473, 55.946233)));
|
new LngLat(-3.192473, 55.946233)));
|
||||||
boolean expected = false;
|
boolean expected = false;
|
||||||
boolean actual = service.checkIsInRegion(position, region);
|
boolean actual = service.checkIsInRegion(position, region);
|
||||||
assertThat(actual).isEqualTo(expected);
|
assertThat(actual).isEqualTo(expected);
|
||||||
|
|
@ -277,7 +277,7 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("General Case: Simple Rectangle")
|
@DisplayName("General Case: Simple Rectangle")
|
||||||
void isInRegion_shouldReturnTrue_forSimpleRectangle() {
|
void isInRegion_shouldReturnTrue_forSimpleRectangle() {
|
||||||
var position = new LngLatDto(1.0, 1.0);
|
var position = new LngLat(1.0, 1.0);
|
||||||
boolean expected = true;
|
boolean expected = true;
|
||||||
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
|
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
|
||||||
assertThat(actual).isEqualTo(expected);
|
assertThat(actual).isEqualTo(expected);
|
||||||
|
|
@ -286,7 +286,7 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("General Case: Simple Rectangle")
|
@DisplayName("General Case: Simple Rectangle")
|
||||||
void isInRegion_shouldReturnFalse_forSimpleRectangle() {
|
void isInRegion_shouldReturnFalse_forSimpleRectangle() {
|
||||||
var position = new LngLatDto(3.0, 1.0);
|
var position = new LngLat(3.0, 1.0);
|
||||||
boolean expected = false;
|
boolean expected = false;
|
||||||
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
|
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
|
||||||
assertThat(actual).isEqualTo(expected);
|
assertThat(actual).isEqualTo(expected);
|
||||||
|
|
@ -295,11 +295,11 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("General Case: Simple Hexagon")
|
@DisplayName("General Case: Simple Hexagon")
|
||||||
void isInRegion_shouldReturnTrue_forSimpleHexagon() {
|
void isInRegion_shouldReturnTrue_forSimpleHexagon() {
|
||||||
var position = new LngLatDto(2.0, 2.0);
|
var position = new LngLat(2.0, 2.0);
|
||||||
var region = new RegionDto("hexagon",
|
var region = new Region("hexagon",
|
||||||
List.of(new LngLatDto(1.0, 0.0), new LngLatDto(4.0, 0.0), new LngLatDto(5.0, 2.0),
|
List.of(new LngLat(1.0, 0.0), new LngLat(4.0, 0.0), new LngLat(5.0, 2.0),
|
||||||
new LngLatDto(4.0, 4.0), new LngLatDto(1.0, 4.0), new LngLatDto(0.0, 2.0),
|
new LngLat(4.0, 4.0), new LngLat(1.0, 4.0), new LngLat(0.0, 2.0),
|
||||||
new LngLatDto(1.0, 0.0)));
|
new LngLat(1.0, 0.0)));
|
||||||
boolean expected = true;
|
boolean expected = true;
|
||||||
boolean actual = service.checkIsInRegion(position, region);
|
boolean actual = service.checkIsInRegion(position, region);
|
||||||
assertThat(actual).isEqualTo(expected);
|
assertThat(actual).isEqualTo(expected);
|
||||||
|
|
@ -308,9 +308,9 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Edge Case: Small Triangle")
|
@DisplayName("Edge Case: Small Triangle")
|
||||||
void isInRegion_shouldReturnTrue_forSmallTriangle() {
|
void isInRegion_shouldReturnTrue_forSmallTriangle() {
|
||||||
var position = new LngLatDto(0.00001, 0.00001);
|
var position = new LngLat(0.00001, 0.00001);
|
||||||
var region = new RegionDto("triangle", List.of(new LngLatDto(0.0, 0.0), new LngLatDto(0.0001, 0.0),
|
var region = new Region("triangle", List.of(new LngLat(0.0, 0.0), new LngLat(0.0001, 0.0),
|
||||||
new LngLatDto(0.00005, 0.0001), new LngLatDto(0.0, 0.0)));
|
new LngLat(0.00005, 0.0001), new LngLat(0.0, 0.0)));
|
||||||
boolean expected = true;
|
boolean expected = true;
|
||||||
boolean actual = service.checkIsInRegion(position, region);
|
boolean actual = service.checkIsInRegion(position, region);
|
||||||
assertThat(actual).isEqualTo(expected);
|
assertThat(actual).isEqualTo(expected);
|
||||||
|
|
@ -319,7 +319,7 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Edge Case: Point on Lower Edge of Rectangle")
|
@DisplayName("Edge Case: Point on Lower Edge of Rectangle")
|
||||||
void isInRegion_shouldReturnTrue_whenPointOnLowerEdge() {
|
void isInRegion_shouldReturnTrue_whenPointOnLowerEdge() {
|
||||||
var position = new LngLatDto(0.0, 1.0);
|
var position = new LngLat(0.0, 1.0);
|
||||||
boolean expected = true;
|
boolean expected = true;
|
||||||
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
|
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
|
||||||
assertThat(actual).isEqualTo(expected);
|
assertThat(actual).isEqualTo(expected);
|
||||||
|
|
@ -328,7 +328,7 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Edge Case: Point on Upper Edge of Rectangle")
|
@DisplayName("Edge Case: Point on Upper Edge of Rectangle")
|
||||||
void isInRegion_shouldReturnTrue_whenPointOnUpperEdge() {
|
void isInRegion_shouldReturnTrue_whenPointOnUpperEdge() {
|
||||||
var position = new LngLatDto(2.0, 1.0);
|
var position = new LngLat(2.0, 1.0);
|
||||||
boolean expected = true;
|
boolean expected = true;
|
||||||
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
|
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
|
||||||
assertThat(actual).isEqualTo(expected);
|
assertThat(actual).isEqualTo(expected);
|
||||||
|
|
@ -337,7 +337,7 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Edge Case: Point on Left Edge")
|
@DisplayName("Edge Case: Point on Left Edge")
|
||||||
void isInRegion_shouldReturnTrue_whenPointOnLeftEdge() {
|
void isInRegion_shouldReturnTrue_whenPointOnLeftEdge() {
|
||||||
var position = new LngLatDto(0.0, 1.0);
|
var position = new LngLat(0.0, 1.0);
|
||||||
boolean expected = true;
|
boolean expected = true;
|
||||||
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
|
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
|
||||||
assertThat(actual).isEqualTo(expected);
|
assertThat(actual).isEqualTo(expected);
|
||||||
|
|
@ -346,7 +346,7 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Edge Case: Point on Lower Vertex")
|
@DisplayName("Edge Case: Point on Lower Vertex")
|
||||||
void isInRegion_shouldReturnTrue_whenPointOnLowerVertex() {
|
void isInRegion_shouldReturnTrue_whenPointOnLowerVertex() {
|
||||||
var position = new LngLatDto(0.0, 0.0);
|
var position = new LngLat(0.0, 0.0);
|
||||||
boolean expected = true;
|
boolean expected = true;
|
||||||
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
|
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
|
||||||
assertThat(actual).isEqualTo(expected);
|
assertThat(actual).isEqualTo(expected);
|
||||||
|
|
@ -355,7 +355,7 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Edge Case: Point on Upper Vertex")
|
@DisplayName("Edge Case: Point on Upper Vertex")
|
||||||
void isInRegion_shouldReturnTrue_whenPointOnUpperVertex() {
|
void isInRegion_shouldReturnTrue_whenPointOnUpperVertex() {
|
||||||
var position = new LngLatDto(2.0, 2.0);
|
var position = new LngLat(2.0, 2.0);
|
||||||
boolean expected = true;
|
boolean expected = true;
|
||||||
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
|
boolean actual = service.checkIsInRegion(position, RECTANGLE_REGION);
|
||||||
assertThat(actual).isEqualTo(expected);
|
assertThat(actual).isEqualTo(expected);
|
||||||
|
|
@ -364,9 +364,9 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Edge Case: Region not forming polygon")
|
@DisplayName("Edge Case: Region not forming polygon")
|
||||||
void isInRegion_shouldThrowExceptions_whenRegionNotFormingPolygon() {
|
void isInRegion_shouldThrowExceptions_whenRegionNotFormingPolygon() {
|
||||||
var position = new LngLatDto(2.0, 2.0);
|
var position = new LngLat(2.0, 2.0);
|
||||||
var region = new RegionDto("line",
|
var region = new Region("line",
|
||||||
List.of(new LngLatDto(0.0, 0.0), new LngLatDto(0.0001, 0.0), new LngLatDto(0.0, 0.0)));
|
List.of(new LngLat(0.0, 0.0), new LngLat(0.0001, 0.0), new LngLat(0.0, 0.0)));
|
||||||
assertThatThrownBy(() -> {
|
assertThatThrownBy(() -> {
|
||||||
service.checkIsInRegion(position, region);
|
service.checkIsInRegion(position, region);
|
||||||
}).isInstanceOf(IllegalArgumentException.class).hasMessage("Region is not closed.");
|
}).isInstanceOf(IllegalArgumentException.class).hasMessage("Region is not closed.");
|
||||||
|
|
@ -375,9 +375,9 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Edge Case: Region is not closed")
|
@DisplayName("Edge Case: Region is not closed")
|
||||||
void isInRegion_shouldThrowExceptions_whenRegionNotClose() {
|
void isInRegion_shouldThrowExceptions_whenRegionNotClose() {
|
||||||
var position = new LngLatDto(2.0, 2.0);
|
var position = new LngLat(2.0, 2.0);
|
||||||
var region = new RegionDto("rectangle", List.of(new LngLatDto(0.0, 0.0), new LngLatDto(2.0, 0.0),
|
var region = new Region("rectangle", List.of(new LngLat(0.0, 0.0), new LngLat(2.0, 0.0),
|
||||||
new LngLatDto(2.0, 2.0), new LngLatDto(0.0, 2.0), new LngLatDto(0.0, -1.0)));
|
new LngLat(2.0, 2.0), new LngLat(0.0, 2.0), new LngLat(0.0, -1.0)));
|
||||||
assertThatThrownBy(() -> {
|
assertThatThrownBy(() -> {
|
||||||
service.checkIsInRegion(position, region);
|
service.checkIsInRegion(position, region);
|
||||||
}).isInstanceOf(IllegalArgumentException.class).hasMessage("Region is not closed.");
|
}).isInstanceOf(IllegalArgumentException.class).hasMessage("Region is not closed.");
|
||||||
|
|
@ -386,8 +386,8 @@ public class GpsCalculationServiceTest {
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Edge Case: Vertex list is empty")
|
@DisplayName("Edge Case: Vertex list is empty")
|
||||||
void isInRegion_shouldThrowExceptions_whenListIsEmpty() {
|
void isInRegion_shouldThrowExceptions_whenListIsEmpty() {
|
||||||
var position = new LngLatDto(2.0, 2.0);
|
var position = new LngLat(2.0, 2.0);
|
||||||
var region = new RegionDto("rectangle", List.of());
|
var region = new Region("rectangle", List.of());
|
||||||
assertThatThrownBy(() -> {
|
assertThatThrownBy(() -> {
|
||||||
service.checkIsInRegion(position, region);
|
service.checkIsInRegion(position, region);
|
||||||
}).isInstanceOf(IllegalArgumentException.class).hasMessage("Region is not closed.");
|
}).isInstanceOf(IllegalArgumentException.class).hasMessage("Region is not closed.");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue