ch.sahits.game.openpatrician.engine.sea.AStarGraphProvider Maven / Gradle / Ivy
package ch.sahits.game.openpatrician.engine.sea;
import ch.sahits.game.openpatrician.engine.sea.model.GraphAStar;
import ch.sahits.game.openpatrician.event.data.GraphInitialisationComplete;
import ch.sahits.game.openpatrician.event.data.HeuristicGraphInitialisationComplete;
import ch.sahits.game.openpatrician.model.city.ICity;
import ch.sahits.game.openpatrician.utilities.annotation.ClassCategory;
import ch.sahits.game.openpatrician.utilities.annotation.EClassCategory;
import ch.sahits.game.openpatrician.utilities.annotation.MapType;
import com.carrotsearch.hppc.ObjectDoubleMap;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.Subscribe;
import javafx.geometry.Point2D;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
/**
* Factory class to provide the graph for the AStar path finding calculation
* @author Andi Hotz, (c) Sahits GmbH, 2016
* Created on Jan 01, 2016
*/
@ClassCategory(EClassCategory.SINGLETON_BEAN)
@Component
@Lazy
public class AStarGraphProvider extends BaseGraphCalulationService {
@Autowired
private AStarHeuristicProvider heuristicProvider;
private final Logger logger = LogManager.getLogger(getClass());
@Autowired
@Qualifier("serverThreadPool")
private Executor serverThreadPool;
@Autowired
@Qualifier("serverClientEventBus")
private AsyncEventBus clientServerEventBus;
private final Object lock = new Object();
private int width;
private int height;
private GraphAStar graph;
@MapType(key = Point2D.class, value = Map.class)
private Map> heuristic;
@PostConstruct
private void init(){
clientServerEventBus.register(this);
try {
initImage();
} catch (IOException e) {
logger.error("Failed to create black and white image from the map", e);
}
}
@Subscribe
public void initGraph(HeuristicGraphInitialisationComplete event) {
serverThreadPool.execute(this::createGraph);
}
@VisibleForTesting
void createGraph() {
long timestamp = System.currentTimeMillis();
synchronized (lock) {
heuristic = heuristicProvider.getHeuristic();
while(heuristic.size() == 0) {
Thread.yield();
heuristic = heuristicProvider.getHeuristic();
}
graph = new GraphAStar<>(heuristic);
List cities = map.getCities();
for (int x = 0; x < width; x += CHECK_DISTANCE) { // TODO: andi 1/3/16 split to differnt threads
for (int y = 0; y < height; y += CHECK_DISTANCE) {
// add edge from x,y -> x+dist,y and back
Point2D p = getPoint(x, y);
if (isOnSea(p)) {
if (!graph.containsNode(p)) {
graph.addNodeInternal(p);
}
if (x + CHECK_DISTANCE <= width) { // destination point is still on the map
Point2D p2 = getPoint(x + CHECK_DISTANCE, y);
addEdges(p, p2, false, false);
}
// add edge from x,y -> x,y+dist and back
if (y + CHECK_DISTANCE <= height) { // destination point is still on the map
Point2D p2 = getPoint(x, y + CHECK_DISTANCE);
addEdges(p, p2, false, false);
}
// add edge from x,y -> x+dist,y+dist and back
if (x + CHECK_DISTANCE <= width && y + CHECK_DISTANCE <= height) {
Point2D p2 = getPoint(x + CHECK_DISTANCE, y + CHECK_DISTANCE);
addEdges(p, p2, false, false);
}
// add edge from x,y -> x+dist,y-dist and back
if (x + CHECK_DISTANCE <= width && y - CHECK_DISTANCE >= 0) {
Point2D p2 = getPoint(x + CHECK_DISTANCE, y - CHECK_DISTANCE);
addEdges(p, p2, false, false);
}
// check if a city is near ( nearest = new ArrayList<>();
for (Point2D node : graph) {
double distance = node.distance(newPoint);
if (distance <= DIAG_CHECK_DISTANCE) {
nearest.add(node);
}
}
graph.addNode(newPoint, true);
for (Point2D point : nearest) {
addEdges(newPoint, point, isCity, false);
}
}
}
@VisibleForTesting
void addSourcePointInternal(Point2D source, boolean isCity) {
synchronized (lock) {
if (!heuristic.containsKey(source)) {
heuristicProvider.addSourceNodeToHeuristic(source);
}
if (graph.containsNode(source)) {
return;
}
List nearest = new ArrayList<>();
for (Point2D node : graph) {
double distance = node.distance(source);
if (distance <= DIAG_CHECK_DISTANCE) {
nearest.add(node);
}
}
graph.addNode(source, false);
for (Point2D point : nearest) {
addEdges(source, point, isCity, false);
}
}
}
@Override
protected double calculateWeight(Point2D from, Point2D to) {
if (from.equals(to)) {
return 0;
}
double distance = from.distance(to);
try {
int landPixels = imageService.countLandPixels(to, CHECK_DISTANCE*2, getSegments(from, to));
int tangentialLandPixels = imageService.countLandPixels(to, (int) Math.round(DIAG_CHECK_DISTANCE), getTangentialSegments(from, to));
double recastLandPixels = landPixels/ (9.0 * 3);
double recastTangentialPixels = tangentialLandPixels / (15.0);
return distance + recastLandPixels + recastTangentialPixels;
} catch (IOException e) {
logger.error("Failed to count the land pixels near "+to, e);
}
return distance;
}
private void addEdges(Point2D from, Point2D to, boolean isCity, boolean initial) {
if (isCity || isOnSea(to)) {
if (!graph.containsNode(to)) {
if (initial) {
graph.addNodeInternal(to);
} else {
graph.addNode(to, true);
}
}
heuristicProvider.getHeuristic();
double weight = calculateWeight(from, to);
if (initial) {
graph.addEdgeInternal(from, to, weight);
} else {
graph.addEdge(from, to, weight);
}
weight = calculateWeight(to, from);
if (initial) {
graph.addEdgeInternal(to, from, weight);
} else {
graph.addEdge(to, from, weight);
}
}
}
@VisibleForTesting
void initImage() throws IOException {
width = imageService.getWidth();
height = imageService.getHeight();
}
public GraphAStar getGraph() {
synchronized (lock) {
return graph;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy