/**
* *****************************************************************************
* Copyright 2013 Johannes Mitlmeier
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
* ****************************************************************************
*/
package de.fub.agg2graph.agg;
import de.fub.agg2graph.agg.tiling.DefaultCachingStrategy;
import de.fub.agg2graph.agg.tiling.Tile;
import de.fub.agg2graph.agg.tiling.TileCache;
import de.fub.agg2graph.agg.tiling.TileManager;
import de.fub.agg2graph.structs.AbstractEdge;
import de.fub.agg2graph.structs.ILocation;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A connection between two {@link AggNode}s. The connection can have
* annotations such as their weight.
*
* @author Johannes Mitlmeier
*
*/
public class AggConnection extends AbstractEdge<AggNode> implements Comparable<AggConnection> {
// properties
public double trackCounter = 0;
private AggContainer aggContainer;
private float weight = 1;
private double avgDist = 0;
/**
* Constructor with given {@link AbstractLocation} and distance
*
* @param from
* @param to
* @param distance
*/
public AggConnection(AggNode from, AggNode to, float distance) {
this.from = from;
this.to = to;
this.distance = distance;
}
/**
* Copy constructor.
*
* @param conn
*/
public AggConnection(AggConnection conn) {
this(conn.getFrom(), conn.getTo(), conn.getAggContainer());
}
public AggConnection(AggNode from, AggNode to, AggContainer aggContainer) {
this(from, to, aggContainer, false);
}
public AggConnection(AggNode from, AggNode to, AggContainer aggContainer,
boolean virtual) {
if (from == null || to == null) {
return;
}
this.from = from;
this.to = to;
if (aggContainer != null) {
setAggContainer(aggContainer);
}
if (!virtual) {
if (from.getOut() != null) {
from.addOut(AggConnection.this);
}
if (to.getIn() != null) {
to.addIn(AggConnection.this);
}
}
}
public AggConnection(ILocation from, ILocation to, AggContainer aggContainer) {
init(from.getLat(), from.getLon(), to.getLat(), to.getLon(), aggContainer);
this.from.setRelevant(from.isRelevant());
}
/**
* Initialize AggConnection. Sets lat, lon and {@link AggContainer}.
*
* @param ID
* @param lat
* @param lon
* @param aggContainer
*/
private void init(double fromLat, double fromLon, double toLat, double toLon, AggContainer aggContainer) {
this.aggContainer = aggContainer;
this.from = new AggNode(fromLat, fromLon, this.aggContainer);
this.to = new AggNode(toLat, toLon, this.aggContainer);
setID(ID);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
AggConnection other = (AggConnection) obj;
return this.getFrom() != null && other.getFrom() != null
&& this.getFrom().getID() != null
&& other.getFrom().getID() != null
&& this.getFrom().getID().equals(other.getFrom().getID())
&& this.getTo() != null && other.getTo() != null
&& this.getTo().getID() != null
&& other.getTo().getID() != null
&& this.getTo().getID().equals(other.getTo().getID());
}
public void fillFrom(AggNode from) {
this.from = from;
from.getOut().add(this);
}
public void fillTo(AggNode to) {
this.to = to;
to.getIn().add(this);
}
public AggContainer getAggContainer() {
return aggContainer != null ? aggContainer : from.getAggContainer();
}
public double getAvgDist() {
return avgDist;
}
@Override
public AggNode getFrom() {
return from;
}
@Override
public AggNode getTo() {
return to;
}
public float getWeight() {
return weight;
}
@Override
public int hashCode() {
// return getFrom().getID().hashCode() ^ getTo().getID().hashCode();
int a = 0, b = 0;
if (getFrom() != null) {
a = (int) (getFrom().getLat() + getFrom().getLon() * 1000);
if (getFrom().getID() != null) {
a = getFrom().getID().hashCode();
}
}
if (getTo() != null) {
b = (int) (getTo().getLat() + getTo().getLon() * 1000);
if (getTo().getID() != null) {
b = getTo().getID().hashCode();
}
}
return a ^ b;
}
public void inheritPropertiesFrom(AggConnection conn) {
setWeight(conn.getWeight());
setAvgDist(conn.getAvgDist());
}
public boolean isComplete() {
return !ShallowAggNode.class.isInstance(from)
&& !ShallowAggNode.class.isInstance(to);
}
@Override
public boolean isVisible() {
return visible;
}
public void makeComplete() {
}
public void setAggContainer(AggContainer aggContainer) {
this.aggContainer = aggContainer;
}
public void setAvgDist(double avgDist) {
this.avgDist = avgDist;
LOG.log(Level.INFO, "{0} avgDistance: {1}", new Object[]{toString(), avgDist});
}
private static final Logger LOG = Logger.getLogger(AggConnection.class.getName());
/**
* Change the AggNode the connection originates from. The old from node is
* diconnected before the new one is put in place. If the new connection
* already exists, both connections are merged via
* {@link IAggregationStrategy#mergeConnections(AggConnection, AggConnection)}
* .
*
* @param from
* @return
*/
@Override
public AggConnection setFrom(AggNode from) {
// disconnect old
AggConnection changedConn = new AggConnection(from, getTo(), aggContainer, true);
AggConnection oldConn = null;
for (AggConnection conn : from.getOut()) {
if (changedConn.equals(conn)) {
oldConn = conn;
break;
}
}
this.from.getOut().remove(this);
this.to.getIn().remove(this);
this.from = from;
if (oldConn == null) {
// set new
this.to.addIn(this);
this.from.addOut(this);
return this;
} else {
oldConn.getFrom().getOut().remove(oldConn);
oldConn.getTo().getIn().remove(oldConn);
AggContainer container = getAggContainer();
AggConnection mergedConn = container.getAggregationStrategy()
.mergeConnections(changedConn, oldConn);
container.removeConnection(oldConn);
// set new
this.to.getIn().add(mergedConn);
this.from.getOut().add(mergedConn);
return changedConn;
}
}
/**
* @see AggConnection#setFrom(AggNode)
* @param to
* @return
*/
@Override
public AggConnection setTo(AggNode to) {
// disconnect old
AggConnection changedConn = new AggConnection(getFrom(), to, aggContainer, true);
AggConnection oldConn = null;
for (AggConnection conn : to.getIn()) {
if (changedConn.equals(conn)) {
oldConn = conn;
break;
}
}
if (this.from != null) {
this.from.getOut().remove(this);
}
if (this.to != null) {
this.to.getIn().remove(this);
}
this.to = to;
if (oldConn == null) {
// set new
this.from.getOut().add(this);
this.to.addIn(this);
return this;
} else {
oldConn.getFrom().getOut().remove(oldConn);
oldConn.getTo().getIn().remove(oldConn);
AggContainer container = getAggContainer();
AggConnection mergedConn = container.getAggregationStrategy()
.mergeConnections(changedConn, oldConn);
container.removeConnection(oldConn);
// set new
this.from.addOut(mergedConn);
this.to.addIn(mergedConn);
return mergedConn;
}
}
@Override
public void setVisible(boolean visible) {
this.visible = visible;
}
public void setWeight(float weight) {
this.weight = weight;
}
@Override
public String toString() {
return MessageFormat.format("AggConnection [from={0}, to={1}]", getFrom(), getTo());
}
public void tryToFill() {
if (isComplete()) {
return;
}
TileCache tc = ((DefaultCachingStrategy) aggContainer.getCachingStrategy()).getTc();
TileManager tm = ((DefaultCachingStrategy) aggContainer.getCachingStrategy()).getTm();
if (ShallowAggNode.class.isInstance(from)) {
// incoming connection for to-node -> shallow connection
// Tile<AggNode> tile = tm.getTile(from);
// if (tc.isTileLoaded(tile)) {
// for (AggNode node : tile.elements) {
// if (node.equals(from)) {
// aggContainer.removeConnection(this);
// aggContainer.addConnection(new AggConnection(node, to));
// return;
// }
// }
// }
} else {
// outgoing connection for from-node -> this should already have
// options loaded
Tile<AggNode> tile = tm.getTile(to);
if (tc.isTileLoaded(tile)) {
for (AggNode node : tile.getElements()) {
if (node.equals(to)) {
fillTo(node);
return;
}
}
}
}
}
public void unloadFrom() {
}
public void unloadTo() {
}
public AggConnection[] divide(AggNode middle) {
AggConnection[] ret = new AggConnection[2];
//TODO check whether middle is in line
setTo(middle);
ret[0] = this;
ret[1] = new AggConnection(middle, to, this.aggContainer);
return ret;
}
public AggNode at(double t) {
return new AggNode((1 - t) * from.getLat() + t * to.getLat(), (1 - t)
* from.getLon() + t * to.getLon(), aggContainer);
}
@Override
public int compareTo(AggConnection o) {
if (o == null) {
throw new NullPointerException();
}
int r = getFrom().compareTo(o.getFrom());
if (r == 0) {
return getTo().compareTo(o.getTo());
} else {
return r;
}
}
public static List<AggNode> listToPoints(AggConnection conn) {
if (conn == null) {
return null;
}
List<AggNode> result = new ArrayList<AggNode>(2);
result.add(conn.getFrom());
result.add(conn.getTo());
return result;
}
public static List<AggNode> listToPoints(List<AggConnection> conns) {
ArrayList<AggNode> result = new ArrayList<AggNode>();
AggNode lastNode = null, node = null;
for (AggConnection conn : conns) {
if (conn == null) {
continue;
}
if (conn.getFrom() != null) {
node = conn.getFrom();
if (!node.equals(lastNode)) {
result.add(node);
lastNode = node;
}
}
if (conn.getTo() != null) {
node = conn.getTo();
if (!node.equals(lastNode)) {
result.add(node);
lastNode = node;
}
}
}
return result;
}
}