package org.osm2world.core.map_elevation.creation;
import static java.util.Arrays.asList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.osm2world.core.map_elevation.data.EleConnector;
/**
* enforcer implementation that ignores many of the constraints,
* but is much faster than the typical full implementation.
*
* It tries to produce an output that is "good enough" for some purposes,
* and is therefore a compromise between the {@link NoneEleConstraintEnforcer}
* and a full implementation.
*/
public final class SimpleEleConstraintEnforcer implements EleConstraintEnforcer {
private Collection<EleConnector> connectors = new ArrayList<EleConnector>();
/**
* associates each EleConnector with the {@link StiffConnectorSet}
* it is part of (if any)
*/
private Map<EleConnector, StiffConnectorSet> stiffSetMap =
new HashMap<EleConnector, StiffConnectorSet>();
@Override
public void addConnectors(Iterable<EleConnector> newConnectors) {
for (EleConnector c : newConnectors) {
connectors.add(c);
}
/* connect connectors */
for (EleConnector c1 : newConnectors) {
for (EleConnector c2 : connectors) {
if (c1 != c2 && c1.connectsTo(c2)) {
requireSameEle(c1, c2);
}
}
}
}
@Override
public void requireSameEle(EleConnector c1, EleConnector c2) {
//SUGGEST (performance): a special case implementation would be faster
requireSameEle(asList(c1, c2));
}
@Override
public void requireSameEle(Iterable<EleConnector> cs) {
/* find stiff sets containing any of the affected connectors */
Set<EleConnector> looseConnectors = new HashSet<EleConnector>();
Set<StiffConnectorSet> existingStiffSets = new HashSet<StiffConnectorSet>();
for (EleConnector c : cs) {
StiffConnectorSet stiffSet = stiffSetMap.get(c);
if (stiffSet != null) {
existingStiffSets.add(stiffSet);
} else {
looseConnectors.add(c);
}
}
/* return if the connectors are already in a set together */
if (existingStiffSets.size() == 1 && looseConnectors.isEmpty()) return;
/* merge existing sets (if any) into a single set */
StiffConnectorSet commonStiffSet = null;
if (existingStiffSets.isEmpty()) {
commonStiffSet = new StiffConnectorSet();
} else {
for (StiffConnectorSet stiffSet : existingStiffSets) {
if (commonStiffSet == null) {
commonStiffSet = stiffSet;
} else {
for (EleConnector c : stiffSet) {
stiffSetMap.put(c, commonStiffSet);
}
commonStiffSet.mergeFrom(stiffSet);
}
}
}
/* add remaining (loose) connectors into the common set */
for (EleConnector c : looseConnectors) {
commonStiffSet.add(c);
stiffSetMap.put(c, commonStiffSet);
}
}
@Override
public void requireVerticalDistance(ConstraintType type, double distance,
EleConnector upper, EleConnector lower) {
// TODO Auto-generated method stub
}
@Override
public void requireVerticalDistance(ConstraintType type, double distance,
EleConnector upper, EleConnector base1, EleConnector base2) {
// TODO Auto-generated method stub
}
@Override
public void requireIncline(ConstraintType type, double incline, List<EleConnector> cs) {
// TODO Auto-generated method stub
}
@Override
public void requireSmoothness(EleConnector from, EleConnector via, EleConnector to) {
// TODO Auto-generated method stub
}
@Override
public void enforceConstraints() {
/* assign elevation to stiff sets by averaging terrain elevation */
//TODO what for stiff sets above the ground?
for (StiffConnectorSet stiffSet : stiffSetMap.values()) {
double averageEle = 0;
for (EleConnector connector : stiffSet) {
averageEle += connector.getPosXYZ().y;
}
averageEle /= stiffSet.size();
for (EleConnector connector : stiffSet) {
connector.setPosXYZ(connector.pos.xyz(averageEle));
}
}
/* TODO implement intended algorithm:
* - first assign ground ele to ON
* - then assign ele for ABOVE and BELOW based on min vertical distance constraints, and clearing
*/
for (EleConnector c : connectors) {
//TODO use clearing
switch (c.groundState) {
case ABOVE: c.setPosXYZ(c.getPosXYZ().addY(5)); break;
case BELOW: c.setPosXYZ(c.getPosXYZ().addY(-5)); break;
default: //stay at ground elevation
}
}
}
/**
* a set of connectors that are required to have the same elevation
* TODO or a precise vertical offset
*/
private static class StiffConnectorSet implements Iterable<EleConnector> {
//TODO maybe look for a more efficient set implementation
private Set<EleConnector> connectors = new HashSet<EleConnector>();
/**
* adds a connector to this set, requiring it to be at the set's
* reference elevation
*/
public void add(EleConnector connector) {
connectors.add(connector);
}
/**
* combines this set with another, and makes the other set unusable.
* This set will contain all {@link EleConnector}s from the other set
* afterwards.
*/
public void mergeFrom(StiffConnectorSet otherSet) {
connectors.addAll(otherSet.connectors);
// make sure that the other set cannot be used anymore
otherSet.connectors = null;
}
public double size() {
return connectors.size();
}
@Override
public Iterator<EleConnector> iterator() {
return connectors.iterator();
}
}
}