package de.fub.agg2graph.structs.frechet;
import de.fub.agg2graph.agg.AggConnection;
import de.fub.agg2graph.agg.AggContainer;
import de.fub.agg2graph.agg.AggNode;
import de.fub.agg2graph.structs.GPSEdge;
import de.fub.agg2graph.structs.GPSPoint;
import de.fub.agg2graph.structs.GPSRegion;
import de.fub.agg2graph.structs.ILocation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.TreeMap;
public class TreeAggMap extends TreeMap<AggNode, AggNode> implements
IAggregatedMap {
private static final long serialVersionUID = -1827387769631060525L;
class GPSEdgeIterator implements Iterator<AggConnection> {
final WeightFunc weightFunc;
final HashSet<AggNode> notVisited;
AggNode position = null;
public AggContainer aggContainer;
public GPSEdgeIterator(GPSPoint start, WeightFunc weightFunc,
AggContainer aggContainer) {
if (!containsKey(start)) {
throw new NoSuchElementException();
}
position = get(start);
this.weightFunc = weightFunc;
this.notVisited = new HashSet<>(getAllEdgeStarts());
this.aggContainer = aggContainer;
}
@Override
public boolean hasNext() {
return !(position == null || weightFunc.isFinish() || notVisited
.isEmpty());
}
@Override
public AggConnection next() {
if (position.isSink()) {
if (notVisited.isEmpty()) {
assert (false); // Here shouldn't we are!
} else {
// Look for more nodes. Optimally we start with a source
// node.
position = searchNextSource();
}
}
// GPSEdge edgeToReturn = Collections.min(position.out, new
// Comparator<GPSEdge>() {
HashSet<GPSEdge> posOut = new HashSet<GPSEdge>();
for (AggConnection agg : position.out) {
posOut.add(new GPSEdge(agg));
}
GPSEdge edgeToReturn = Collections.min(posOut,
new Comparator<GPSEdge>() {
@Override
public int compare(GPSEdge o1, GPSEdge o2) {
return Double.compare(weightFunc.weight(o1),
weightFunc.weight(o2));
}
});
// Determine next position.
// First follow the path. If we hopped to a sink - search for good
// new start.
position = get(edgeToReturn.getTo());
notVisited.remove(edgeToReturn.getFrom());
return new AggConnection(new AggNode(edgeToReturn.getFrom(),
this.aggContainer), new AggNode(edgeToReturn.getTo(),
this.aggContainer), this.aggContainer);
// edgeToReturn, this.aggContainer);
}
@Override
public void remove() {
throw new UnsupportedOperationException("Remove not supported.");
}
private AggNode searchNextSource() {
for (ILocation key : notVisited) {
if (get(key).isSource()) {
return get(key);
}
}
// No source found return first element found or null.
return get(notVisited.iterator().next());
}
}
// ---- 2D Search index
// ------------------------------------------------------------
KdTreeIndex<AggNode> searchIndex;
private boolean searchIndexNeedsRebuild = true;
public AggContainer aggContainer;
public TreeAggMap(AggContainer aggContainer) {
this.aggContainer = aggContainer;
}
public boolean isSource(ILocation location) {
if (containsKey(location)) {
return ((AggNode) get(location)).isSource();
} else {
throw new NoSuchElementException();
}
}
public boolean isSink(ILocation location) {
if (containsKey(location)) {
return ((AggNode) get(location)).isSink();
} else {
throw new NoSuchElementException();
}
}
public boolean isRegular(ILocation location) {
if (containsKey(location)) {
return ((AggNode) get(location)).isRegular();
} else {
throw new NoSuchElementException();
}
}
private Collection<AggNode> getAllEdgeStarts() {
ArrayList<AggNode> result = new ArrayList<>();
for (AggNode node : values()) {
// Enough to return all start points of outgoing edges to get a
// complete list of all edges in the graph.
for (AggConnection edge : node.out) {
result.add(edge.getFrom());
}
}
return result;
}
@Override
public AggNode searchNN(AggNode point) {
if (searchIndexNeedsRebuild) {
searchIndex = new KdTreeIndex<AggNode>(keySet());
searchIndexNeedsRebuild = false;
}
return searchIndex.searchNN(point);
}
@Override
public List<AggNode> searchKnn(AggNode point, int k) {
if (searchIndexNeedsRebuild) {
searchIndex = new KdTreeIndex<AggNode>(keySet());
searchIndexNeedsRebuild = false;
}
return searchIndex.searchKnn(point, k);
}
@Override
public List<AggNode> searchRegion(GPSRegion region) {
if (searchIndexNeedsRebuild) {
searchIndex = new KdTreeIndex<AggNode>(keySet());
searchIndexNeedsRebuild = false;
}
return searchIndex.searchRegion(region);
}
@Override
public Iterator<AggNode> iterator() {
return keySet().iterator();
}
@Override
public void insertConnection(AggConnection conn) {
AggNode from = new AggNode(conn.getFrom(), this.aggContainer);
AggNode to = new AggNode(conn.getTo(), this.aggContainer);
AggConnection aggEdge = new AggConnection(from, to, this.aggContainer);
AggNode nodeFrom = null;
if (containsKey(from)) {
nodeFrom = get(from);
aggEdge.setFrom(nodeFrom);
} else {
nodeFrom = new AggNode(from, this.aggContainer);
put(from, nodeFrom);
}
nodeFrom.out.add(new AggConnection(aggEdge));
AggNode nodeTo = null;
if (containsKey(to)) {
nodeTo = get(to);
aggEdge.setTo(nodeTo);
} else {
nodeTo = new AggNode(to, this.aggContainer);
put(to, nodeTo);
}
nodeTo.in.add(new AggConnection(aggEdge));
}
@Override
public Collection<AggConnection> getInConnections(AggNode location,
Collection<AggConnection> inConnection) {
Collection<AggConnection> result = Collections.emptyList();
if (inConnection != null) {
result = inConnection;
result.addAll(get(location).in);
} else if (containsKey(location) && get(location) != null) {
result = get(location).in;
}
return result;
}
@Override
public Collection<AggConnection> getOutConnections(AggNode location,
Collection<AggConnection> outConnection) {
Collection<AggConnection> result = Collections.emptyList();
if (outConnection != null) {
result = outConnection;
result.addAll(get(location).out);
} else if (containsKey(location) && get(location) != null) {
result = get(location).out;
}
return result;
}
@Override
public Collection<AggConnection> connection() {
ArrayList<AggConnection> result = new ArrayList<AggConnection>();
for (AggNode node : values()) {
// Enough to return all start points of outgoing edges to get a
// complete
// list of all edges in the graph.
result.addAll(node.out);
}
return result;
}
@Override
public boolean removeConnections(AggConnection conn) {
assert (containsKey(conn.getFrom()) && containsKey(conn.getTo()));
boolean isRemoveComplete = false;
AggNode nodeFrom = get(conn.getFrom());
Iterator<AggConnection> it = nodeFrom.out.iterator();
while (it.hasNext()) {
AggConnection outConn = new AggConnection(it.next());
if (outConn.compareTo(conn) == 0) {
it.remove();
}
}
if (nodeFrom.isEmpty()) {
isRemoveComplete &= (remove(nodeFrom) != null);
}
AggNode nodeTo = get(conn.getTo());
while (it.hasNext()) {
AggConnection inConn = new AggConnection(it.next());
// it.next();
if (inConn.compareTo(conn) == 0) {
it.remove();
}
}
if (nodeTo.isEmpty()) {
isRemoveComplete &= (remove(nodeTo) != null);
}
searchIndexNeedsRebuild = true;
return isRemoveComplete;
}
@Override
public void updateLocation(AggNode location, double dLongitude,
double dLatitude) {
assert (containsKey(location));
if (!containsKey(location)) {
return;
}
AggNode node = remove(location);
if (node == null) {
throw new NoSuchElementException();
}
// The edges should be connected well, such that their locations moved
// also.
node.setLon(location.getLon() + dLongitude);
node.setLat(location.getLat() + dLatitude);
// Add again with new position.
put(node, node);
searchIndexNeedsRebuild = true;
}
@Override
public void updateLocation(AggNode old, AggNode neu) {
double dLongitude = neu.getLon() - old.getLon();
double dLatitude = neu.getLat() - old.getLat();
updateLocation(old, dLongitude, dLatitude);
}
@Override
public Iterator<AggConnection> connectionsIterator(AggNode start,
WeightFunc weightFunc) {
return new GPSEdgeIterator(start, weightFunc, this.aggContainer);
}
@Override
public void decorate(AggNode location, Object decoration) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public void decorate(AggConnection edge, Object decoration) {
throw new UnsupportedOperationException("not implemented");
}
}