diff --git a/src/frontend/src/components/layout/error/ErrorModal.vue b/src/frontend/src/components/layout/error/ErrorModal.vue
index 18e12e4..be18870 100644
--- a/src/frontend/src/components/layout/error/ErrorModal.vue
+++ b/src/frontend/src/components/layout/error/ErrorModal.vue
@@ -49,7 +49,7 @@ export default {
props: {isSelected: false, error: this.error},
},
{
- title: 'Pinia store',
+ title: 'Frontend storage',
component: markRaw(ErrorModalPiniaStore),
props: {isSelected: false, error: this.error},
},
diff --git a/src/frontend/src/components/layout/error/ErrorModalPiniaStore.vue b/src/frontend/src/components/layout/error/ErrorModalPiniaStore.vue
index 468aab9..a04264f 100644
--- a/src/frontend/src/components/layout/error/ErrorModalPiniaStore.vue
+++ b/src/frontend/src/components/layout/error/ErrorModalPiniaStore.vue
@@ -5,7 +5,7 @@
-
No pinia data
+
No frontend data available
@@ -82,9 +82,13 @@ export default {
}
.no-data {
- padding: 1rem;
- text-align: center;
- color: #6b7280;
- font-style: italic;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-size: 1.6rem;
+}
+
+.space-around {
+ margin: 3rem;
}
\ No newline at end of file
diff --git a/src/frontend/src/pages/ErrorLog.vue b/src/frontend/src/pages/ErrorLog.vue
index edf8459..8af885b 100644
--- a/src/frontend/src/pages/ErrorLog.vue
+++ b/src/frontend/src/pages/ErrorLog.vue
@@ -65,7 +65,7 @@ export default {
const query = {
searchTerm: '',
page: 1,
- pageSize: 10,
+ pageSize: 20,
}
await this.fetchData(query);
}
diff --git a/src/main/java/de/avatic/lcc/model/db/nodes/Distance.java b/src/main/java/de/avatic/lcc/model/db/nodes/Distance.java
index 9f7a316..27546d2 100644
--- a/src/main/java/de/avatic/lcc/model/db/nodes/Distance.java
+++ b/src/main/java/de/avatic/lcc/model/db/nodes/Distance.java
@@ -40,12 +40,15 @@ public class Distance {
private DistanceMatrixState state;
- @NotNull
+
private Integer fromNodeId;
- @NotNull
private Integer toNodeId;
+ private Integer fromUserNodeId;
+
+ private Integer toUserNodeId;
+
public Integer getFromNodeId() {
return fromNodeId;
}
@@ -126,4 +129,19 @@ public class Distance {
this.state = state;
}
+ public Integer getFromUserNodeId() {
+ return fromUserNodeId;
+ }
+
+ public void setFromUserNodeId(Integer fromUserNodeId) {
+ this.fromUserNodeId = fromUserNodeId;
+ }
+
+ public Integer getToUserNodeId() {
+ return toUserNodeId;
+ }
+
+ public void setToUserNodeId(Integer toUserNodeId) {
+ this.toUserNodeId = toUserNodeId;
+ }
}
diff --git a/src/main/java/de/avatic/lcc/model/db/nodes/Node.java b/src/main/java/de/avatic/lcc/model/db/nodes/Node.java
index c04e8d8..baf9b0a 100644
--- a/src/main/java/de/avatic/lcc/model/db/nodes/Node.java
+++ b/src/main/java/de/avatic/lcc/model/db/nodes/Node.java
@@ -56,6 +56,8 @@ public class Node {
private Collection outboundCountries;
+ private boolean isUserNode = false;
+
public Integer getId() {
return id;
}
@@ -176,6 +178,14 @@ public class Node {
this.outboundCountries = outboundCountries;
}
+ public boolean isUserNode() {
+ return isUserNode;
+ }
+
+ public void setUserNode(boolean userNode) {
+ isUserNode = userNode;
+ }
+
public String getDebugText() {
return externalMappingId == null ? "\uD83D\uDC64" + name : externalMappingId;
}
diff --git a/src/main/java/de/avatic/lcc/repositories/DistanceMatrixRepository.java b/src/main/java/de/avatic/lcc/repositories/DistanceMatrixRepository.java
index 8cbafef..24f5307 100644
--- a/src/main/java/de/avatic/lcc/repositories/DistanceMatrixRepository.java
+++ b/src/main/java/de/avatic/lcc/repositories/DistanceMatrixRepository.java
@@ -3,7 +3,6 @@ package de.avatic.lcc.repositories;
import de.avatic.lcc.model.db.nodes.Distance;
import de.avatic.lcc.model.db.nodes.DistanceMatrixState;
import de.avatic.lcc.model.db.nodes.Node;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -24,12 +23,12 @@ public class DistanceMatrixRepository {
this.jdbcTemplate = jdbcTemplate;
}
- public Optional getDistance(Node src, Node dest) {
- String query = "SELECT * FROM distance_matrix WHERE from_node_id = ? AND to_node_id = ? AND state = ?";
+ public Optional getDistance(Node src, boolean isUsrFrom, Node dest, boolean isUsrTo) {
+ String query = "SELECT * FROM distance_matrix WHERE ? = ? AND ? = ? AND state = ?";
- var distance = jdbcTemplate.query(query, new DistanceMapper(), src.getId(), dest.getId(), DistanceMatrixState.VALID.name());
+ var distance = jdbcTemplate.query(query, new DistanceMapper(), isUsrFrom ? "from_user_node_id" : "from_node_id", src.getId(), isUsrTo ? "to_user_node_id" : "to_node_id", dest.getId(), DistanceMatrixState.VALID.name());
- if(distance.isEmpty())
+ if (distance.isEmpty())
return Optional.empty();
return Optional.of(distance.getFirst());
@@ -37,26 +36,29 @@ public class DistanceMatrixRepository {
public void saveDistance(Distance distance) {
try {
+
// First, check if an entry already exists
- String checkQuery = "SELECT id FROM distance_matrix WHERE from_node_id = ? AND to_node_id = ?";
+ String checkQuery = "SELECT id FROM distance_matrix WHERE ? = ? AND ? = ?";
var existingIds = jdbcTemplate.query(checkQuery,
(rs, rowNum) -> rs.getInt("id"),
- distance.getFromNodeId(),
- distance.getToNodeId());
+ distance.getFromUserNodeId() != null ? "from_user_node_id" : "from_node_id",
+ distance.getFromUserNodeId() != null ? distance.getFromUserNodeId() : distance.getFromNodeId(),
+ distance.getToUserNodeId() != null ? "to_user_node_id" : "to_node_id",
+ distance.getToUserNodeId() != null ? distance.getToUserNodeId() : distance.getToNodeId());
if (!existingIds.isEmpty()) {
// Update existing entry
String updateQuery = """
- UPDATE distance_matrix
- SET from_geo_lat = ?,
- from_geo_lng = ?,
- to_geo_lat = ?,
- to_geo_lng = ?,
- distance = ?,
- state = ?,
- updated_at = ?
- WHERE from_node_id = ? AND to_node_id = ?
- """;
+ UPDATE distance_matrix
+ SET from_geo_lat = ?,
+ from_geo_lng = ?,
+ to_geo_lat = ?,
+ to_geo_lng = ?,
+ distance = ?,
+ state = ?,
+ updated_at = ?
+ WHERE ? = ? AND ? = ?
+ """;
jdbcTemplate.update(updateQuery,
distance.getFromGeoLat(),
@@ -66,22 +68,26 @@ public class DistanceMatrixRepository {
distance.getDistance(),
distance.getState().name(),
distance.getUpdatedAt(),
- distance.getFromNodeId(),
- distance.getToNodeId());
+ distance.getFromUserNodeId() != null ? "from_user_node_id" : "from_node_id",
+ distance.getFromUserNodeId() != null ? distance.getFromUserNodeId() : distance.getFromNodeId(),
+ distance.getToUserNodeId() != null ? "to_user_node_id" : "to_node_id",
+ distance.getToUserNodeId() != null ? distance.getToUserNodeId() : distance.getToNodeId());
logger.info("Updated existing distance entry for nodes {} -> {}",
distance.getFromNodeId(), distance.getToNodeId());
} else {
// Insert new entry
String insertQuery = """
- INSERT INTO distance_matrix
- (from_node_id, to_node_id, from_geo_lat, from_geo_lng, to_geo_lat, to_geo_lng, distance, state, updated_at)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
- """;
+ INSERT INTO distance_matrix
+ (from_node_id, to_node_id, from_user_node_id, to_user_node_id, from_geo_lat, from_geo_lng, to_geo_lat, to_geo_lng, distance, state, updated_at)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ """;
jdbcTemplate.update(insertQuery,
distance.getFromNodeId(),
distance.getToNodeId(),
+ distance.getFromUserNodeId(),
+ distance.getToUserNodeId(),
distance.getFromGeoLat(),
distance.getFromGeoLng(),
distance.getToGeoLat(),
@@ -107,6 +113,8 @@ public class DistanceMatrixRepository {
entity.setFromNodeId(rs.getInt("from_node_id"));
entity.setToNodeId(rs.getInt("to_node_id"));
+ entity.setFromNodeId(rs.getInt("from_user_node_id"));
+ entity.setToNodeId(rs.getInt("to_user_node_id"));
entity.setDistance(rs.getBigDecimal("distance"));
entity.setFromGeoLng(rs.getBigDecimal("from_geo_lng"));
entity.setFromGeoLat(rs.getBigDecimal("from_geo_lat"));
diff --git a/src/main/java/de/avatic/lcc/repositories/users/UserNodeRepository.java b/src/main/java/de/avatic/lcc/repositories/users/UserNodeRepository.java
index 9708fc4..311e83c 100644
--- a/src/main/java/de/avatic/lcc/repositories/users/UserNodeRepository.java
+++ b/src/main/java/de/avatic/lcc/repositories/users/UserNodeRepository.java
@@ -169,6 +169,8 @@ public class UserNodeRepository {
node.setIntermediate(false);
node.setSource(true);
+ node.setUserNode(true);
+
return node;
}
diff --git a/src/main/java/de/avatic/lcc/service/api/DistanceApiService.java b/src/main/java/de/avatic/lcc/service/api/DistanceApiService.java
index 2f6176d..a659cce 100644
--- a/src/main/java/de/avatic/lcc/service/api/DistanceApiService.java
+++ b/src/main/java/de/avatic/lcc/service/api/DistanceApiService.java
@@ -3,6 +3,7 @@ package de.avatic.lcc.service.api;
import de.avatic.lcc.model.azuremaps.route.RouteDirectionsResponse;
import de.avatic.lcc.model.db.nodes.Distance;
import de.avatic.lcc.model.db.nodes.DistanceMatrixState;
+import de.avatic.lcc.model.db.nodes.Location;
import de.avatic.lcc.model.db.nodes.Node;
import de.avatic.lcc.repositories.DistanceMatrixRepository;
import org.slf4j.Logger;
@@ -13,7 +14,6 @@ import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.math.BigDecimal;
-import java.net.URI;
import java.time.LocalDateTime;
import java.util.Optional;
@@ -35,7 +35,27 @@ public class DistanceApiService {
this.restTemplate = restTemplate;
}
- public Optional getDistance(Node from, Node to) {
+ public Optional getDistance(Location from, Location to) {
+ if (from == null || to == null) {
+ logger.warn("Source or destination location is null");
+ return Optional.empty();
+ }
+
+ if (from.getLatitude() == null || from.getLongitude() == null ||
+ to.getLatitude() == null || to.getLongitude() == null) {
+ return Optional.empty();
+ }
+
+ RouteDirectionsResponse response = fetchDistanceFromAzureMaps(BigDecimal.valueOf(from.getLatitude()), BigDecimal.valueOf(from.getLongitude()), BigDecimal.valueOf(to.getLatitude()), BigDecimal.valueOf(to.getLongitude()));
+
+ if (response != null && response.getRoutes() != null && !response.getRoutes().isEmpty()) {
+ return Optional.of(response.getRoutes().getFirst().getSummary().getLengthInMeters());
+ }
+
+ return Optional.empty();
+ }
+
+ public Optional getDistance(Node from, boolean isUsrFrom, Node to, boolean isUsrTo) {
if (from == null || to == null) {
logger.warn("Source or destination node is null");
@@ -48,7 +68,7 @@ public class DistanceApiService {
return Optional.empty();
}
- Optional cachedDistance = distanceMatrixRepository.getDistance(from, to);
+ Optional cachedDistance = distanceMatrixRepository.getDistance(from, isUsrFrom, to, isUsrTo);
if (cachedDistance.isPresent()) {
logger.debug("Found cached distance from node {} to node {}", from.getId(), to.getId());
@@ -56,7 +76,7 @@ public class DistanceApiService {
}
logger.debug("Fetching distance from Azure Maps for nodes {} to {}", from.getId(), to.getId());
- Optional fetchedDistance = fetchDistanceFromAzureMaps(from, to);
+ Optional fetchedDistance = fetchDistanceFromAzureMaps(from, isUsrFrom, to, isUsrTo);
if (fetchedDistance.isPresent()) {
distanceMatrixRepository.saveDistance(fetchedDistance.get());
@@ -66,25 +86,45 @@ public class DistanceApiService {
return Optional.empty();
}
- private Optional fetchDistanceFromAzureMaps(Node from, Node to) {
- try {
- String url = UriComponentsBuilder.fromUriString(AZURE_MAPS_ROUTE_API)
- .queryParam("api-version", "1.0")
- .queryParam("subscription-key", subscriptionKey)
- .queryParam("query", String.format("%s,%s:%s,%s",
- from.getGeoLat(), from.getGeoLng(),
- to.getGeoLat(), to.getGeoLng()))
- .encode()
- .toUriString();
+ private RouteDirectionsResponse fetchDistanceFromAzureMaps(BigDecimal fromLat, BigDecimal fromLng, BigDecimal toLat, BigDecimal toLng) {
+ String url = UriComponentsBuilder.fromUriString(AZURE_MAPS_ROUTE_API)
+ .queryParam("api-version", "1.0")
+ .queryParam("subscription-key", subscriptionKey)
+ .queryParam("query", String.format("%s,%s:%s,%s",
+ fromLat, fromLng,
+ toLat, toLng))
+ .encode()
+ .toUriString();
- RouteDirectionsResponse response = restTemplate.getForObject(url, RouteDirectionsResponse.class);
+ return restTemplate.getForObject(url, RouteDirectionsResponse.class);
+ }
+
+ private Optional fetchDistanceFromAzureMaps(Node from, boolean isUsrFrom, Node to, boolean isUsrTo) {
+ try {
+
+ RouteDirectionsResponse response = fetchDistanceFromAzureMaps(from.getGeoLat(), from.getGeoLng(), to.getGeoLat(), to.getGeoLng());
if (response != null && response.getRoutes() != null && !response.getRoutes().isEmpty()) {
Integer distanceInMeters = response.getRoutes().getFirst().getSummary().getLengthInMeters();
Distance distance = new Distance();
- distance.setFromNodeId(from.getId());
- distance.setToNodeId(to.getId());
+
+ if (isUsrFrom) {
+ distance.setFromUserNodeId(from.getId());
+ distance.setFromNodeId(null);
+ } else {
+ distance.setFromUserNodeId(null);
+ distance.setFromNodeId(from.getId());
+ }
+
+ if (isUsrTo) {
+ distance.setToUserNodeId(to.getId());
+ distance.setToNodeId(null);
+ } else {
+ distance.setToUserNodeId(null);
+ distance.setToNodeId(from.getId());
+ }
+
distance.setFromGeoLat(from.getGeoLat());
distance.setFromGeoLng(from.getGeoLng());
distance.setToGeoLat(to.getGeoLat());
diff --git a/src/main/java/de/avatic/lcc/service/calculation/DistanceService.java b/src/main/java/de/avatic/lcc/service/calculation/DistanceService.java
index 869c669..774013c 100644
--- a/src/main/java/de/avatic/lcc/service/calculation/DistanceService.java
+++ b/src/main/java/de/avatic/lcc/service/calculation/DistanceService.java
@@ -13,26 +13,34 @@ import org.springframework.stereotype.Service;
public class DistanceService {
private static final double EARTH_RADIUS = 6371.0;
- private final DistanceMatrixRepository distanceMatrixRepository;
private final CountryRepository countryRepository;
private final DistanceApiService distanceApiService;
- public DistanceService(DistanceMatrixRepository distanceMatrixRepository, CountryRepository countryRepository, DistanceApiService distanceApiService) {
- this.distanceMatrixRepository = distanceMatrixRepository;
+ public DistanceService(CountryRepository countryRepository, DistanceApiService distanceApiService) {
this.countryRepository = countryRepository;
this.distanceApiService = distanceApiService;
}
+ public double getDistance(Integer srcCountryId, Location src, Integer destCountryId, Location dest, boolean fast) {
+ if (fast) return getDistanceFast(srcCountryId, src, destCountryId, dest);
+
+ var distance = distanceApiService.getDistance(src, dest);
+ if (distance.isEmpty()) return getDistanceFast(srcCountryId, src, destCountryId, dest);
+
+ return distance.map(value -> value/1000).orElse(0);
+ }
+
public double getDistance(Node src, Node dest, boolean fast) {
if (fast) return getDistanceFast(src, dest);
- var distance = distanceApiService.getDistance(src, dest);
+ var distance = distanceApiService.getDistance(src, src.isUserNode(), dest, dest.isUserNode());
if (distance.isEmpty()) return getDistanceFast(src, dest);
return distance.map(value -> value.getDistance().intValue()/1000).orElse(0);
}
- public double getDistanceFast(Integer srcCountryId, Location src, Integer destCountryId, Location dest ) {
+
+ public double getDistanceFast(Integer srcCountryId, Location src, Integer destCountryId, Location dest ) {
double srcLatitudeRadians = Math.toRadians(src.getLatitude());
double srcLongitudeRad = Math.toRadians(src.getLongitude());
double destLatitudeRadians = Math.toRadians(dest.getLatitude());
diff --git a/src/main/java/de/avatic/lcc/service/calculation/RoutingService.java b/src/main/java/de/avatic/lcc/service/calculation/RoutingService.java
index b4bab92..e4767ae 100644
--- a/src/main/java/de/avatic/lcc/service/calculation/RoutingService.java
+++ b/src/main/java/de/avatic/lcc/service/calculation/RoutingService.java
@@ -43,7 +43,7 @@ public class RoutingService {
public List findRoutes(Node destination, Node source, boolean sourceIsUserNode) {
List routeInformation = new ArrayList<>();
- TemporaryContainer container = new TemporaryContainer(source, destination);
+ TemporaryContainer container = new TemporaryContainer(source, destination, sourceIsUserNode);
/*
* Get the source and destination node from database.
@@ -740,6 +740,7 @@ public class RoutingService {
* Source and destination node
*/
private final Node source;
+ private final boolean sourceIsUserNode;
private final Node destination;
private final Map>> chains = new HashMap<>();
/*
@@ -752,8 +753,9 @@ public class RoutingService {
private Map> postRuns;
private MatrixRate sourceMatrixRate;
- public TemporaryContainer(Node source, Node destination) {
+ public TemporaryContainer(Node source, Node destination, boolean sourceIsUserNode) {
this.source = source;
+ this.sourceIsUserNode = sourceIsUserNode;
this.destination = destination;
this.mainRuns = null;
this.postRuns = null;
@@ -821,6 +823,10 @@ public class RoutingService {
public void setChain(Integer id, List> chain) {
this.chains.put(id, chain);
}
+
+ public boolean isSourceIsUserNode() {
+ return sourceIsUserNode;
+ }
}
@Renderer(text = "getFullDebugText()")
diff --git a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/RouteSectionCostCalculationService.java b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/RouteSectionCostCalculationService.java
index cf7a482..257751c 100644
--- a/src/main/java/de/avatic/lcc/service/calculation/execution/steps/RouteSectionCostCalculationService.java
+++ b/src/main/java/de/avatic/lcc/service/calculation/execution/steps/RouteSectionCostCalculationService.java
@@ -21,6 +21,7 @@ import de.avatic.lcc.repositories.premise.RouteNodeRepository;
import de.avatic.lcc.repositories.properties.PropertyRepository;
import de.avatic.lcc.repositories.rates.ContainerRateRepository;
import de.avatic.lcc.repositories.rates.MatrixRateRepository;
+import de.avatic.lcc.repositories.users.UserNodeRepository;
import de.avatic.lcc.service.calculation.DistanceService;
import org.springframework.stereotype.Service;
@@ -38,8 +39,9 @@ public class RouteSectionCostCalculationService {
private final PropertyRepository propertyRepository;
private final ChangeRiskFactorCalculationService changeRiskFactorCalculationService;
private final NodeRepository nodeRepository;
+ private final UserNodeRepository userNodeRepository;
- public RouteSectionCostCalculationService(ContainerRateRepository containerRateRepository, MatrixRateRepository matrixRateRepository, RouteNodeRepository routeNodeRepository, DistanceService distanceService, PropertyRepository propertyRepository, ChangeRiskFactorCalculationService changeRiskFactorCalculationService, NodeRepository nodeRepository) {
+ public RouteSectionCostCalculationService(ContainerRateRepository containerRateRepository, MatrixRateRepository matrixRateRepository, RouteNodeRepository routeNodeRepository, DistanceService distanceService, PropertyRepository propertyRepository, ChangeRiskFactorCalculationService changeRiskFactorCalculationService, NodeRepository nodeRepository, UserNodeRepository userNodeRepository) {
this.containerRateRepository = containerRateRepository;
this.matrixRateRepository = matrixRateRepository;
this.routeNodeRepository = routeNodeRepository;
@@ -47,6 +49,7 @@ public class RouteSectionCostCalculationService {
this.propertyRepository = propertyRepository;
this.changeRiskFactorCalculationService = changeRiskFactorCalculationService;
this.nodeRepository = nodeRepository;
+ this.userNodeRepository = userNodeRepository;
}
public CalculationJobRouteSection doD2dCalculation(Integer setId, Integer periodId, Premise premise, Destination destination, ContainerCalculationResult containerCalculation) {
@@ -274,9 +277,25 @@ public class RouteSectionCostCalculationService {
}
private double getDistance(RouteNode fromNode, RouteNode toNode) {
- return distanceService.getDistanceFast(
- fromNode.getCountryId(), new Location(fromNode.getGeoLng().doubleValue(), fromNode.getGeoLat().doubleValue()),
- toNode.getCountryId(), new Location(toNode.getGeoLng().doubleValue(), toNode.getGeoLat().doubleValue()));
+
+ if(fromNode.getOutdated() || toNode.getOutdated()) {
+ return distanceService.getDistance(
+ fromNode.getCountryId(), new Location(fromNode.getGeoLng().doubleValue(), fromNode.getGeoLat().doubleValue()),
+ toNode.getCountryId(), new Location(toNode.getGeoLng().doubleValue(), toNode.getGeoLat().doubleValue()), false);
+ }
+
+ var optSrcNode = fromNode.getNodeId() == null ? userNodeRepository.getById(fromNode.getUserNodeId()) : nodeRepository.getById(fromNode.getNodeId());
+ var optDestNode = toNode.getNodeId() == null ? userNodeRepository.getById(toNode.getUserNodeId()) : nodeRepository.getById(toNode.getNodeId());
+
+ if(optSrcNode.isEmpty() ) {
+ throw new NoSuchElementException("Source node not found for route section " + fromNode.getName());
+ }
+ if(optDestNode.isEmpty() ) {
+ throw new NoSuchElementException("Destination node not found for route section" + toNode.getName());
+ }
+
+ return distanceService.getDistance(optSrcNode.get(), optDestNode.get(), false);
+
}