package org.osm2world.core.world.modules.common;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static org.osm2world.core.map_elevation.creation.EleConstraintEnforcer.ConstraintType.*;
import static org.osm2world.core.map_elevation.data.GroundState.*;
import static org.osm2world.core.math.GeometryUtil.isBetween;
import java.util.List;
import org.osm2world.core.map_data.data.MapAreaSegment;
import org.osm2world.core.map_data.data.MapElement;
import org.osm2world.core.map_data.data.MapWaySegment;
import org.osm2world.core.map_data.data.overlaps.MapIntersectionWW;
import org.osm2world.core.map_data.data.overlaps.MapOverlap;
import org.osm2world.core.map_data.data.overlaps.MapOverlapType;
import org.osm2world.core.map_data.data.overlaps.MapOverlapWA;
import org.osm2world.core.map_elevation.creation.EleConstraintEnforcer;
import org.osm2world.core.map_elevation.data.EleConnector;
import org.osm2world.core.math.SimplePolygonXZ;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.world.data.AbstractAreaWorldObject;
import org.osm2world.core.world.data.WaySegmentWorldObject;
import org.osm2world.core.world.data.WorldObject;
import org.osm2world.core.world.modules.TreeModule.Forest;
import org.osm2world.core.world.network.AbstractNetworkWaySegmentWorldObject;
/**
* common superclass for bridges and tunnels
*/
public abstract class BridgeOrTunnel implements WaySegmentWorldObject {
protected final MapWaySegment segment;
protected final AbstractNetworkWaySegmentWorldObject primaryRep;
public BridgeOrTunnel(MapWaySegment segment,
AbstractNetworkWaySegmentWorldObject primaryRepresentation) {
this.segment = segment;
this.primaryRep = primaryRepresentation;
}
@Override
public MapWaySegment getPrimaryMapElement() {
return segment;
}
@Override
public VectorXZ getEndPosition() {
return primaryRep.getEndPosition();
}
@Override
public VectorXZ getStartPosition() {
return primaryRep.getStartPosition();
}
@Override
public Iterable<EleConnector> getEleConnectors() {
return emptyList();
}
@Override
public void defineEleConstraints(EleConstraintEnforcer enforcer) {
@SuppressWarnings("unchecked")
List<List<VectorXZ>> lines = asList(
primaryRep.getCenterlineXZ(),
primaryRep.getOutlineXZ(true),
primaryRep.getOutlineXZ(false));
SimplePolygonXZ outlinePolygonXZ = primaryRep.getOutlinePolygonXZ();
/* ensure a minimum vertical distance to ways and areas below,
* at intersections */
for (MapOverlap<?,?> overlap : segment.getOverlaps()) {
MapElement other = overlap.getOther(segment);
WorldObject otherWO = other.getPrimaryRepresentation();
if (otherWO == null
|| otherWO.getGroundState() != ON) //TODO remove the ground state check
continue;
boolean thisIsUpper = this.getGroundState() == ABOVE; //TODO check layers
double distance = 10.0; //TODO base on clearing
if (overlap instanceof MapIntersectionWW) {
MapIntersectionWW intersection = (MapIntersectionWW) overlap;
if (otherWO instanceof AbstractNetworkWaySegmentWorldObject) {
AbstractNetworkWaySegmentWorldObject otherANWSWO =
((AbstractNetworkWaySegmentWorldObject)otherWO);
EleConnector thisConn = primaryRep.getEleConnectors()
.getConnector(intersection.pos);
EleConnector otherConn = otherANWSWO.getEleConnectors()
.getConnector(intersection.pos);
if (thisIsUpper) {
enforcer.requireVerticalDistance(
MIN, distance, thisConn, otherConn);
} else {
enforcer.requireVerticalDistance(
MIN, distance, otherConn, thisConn);
}
}
} else if (overlap instanceof MapOverlapWA) {
/*
* require minimum distance at intersection points
* (these have been inserted into this segment,
* but not into the area)
*/
MapOverlapWA overlapWA = (MapOverlapWA) overlap;
if (overlap.type == MapOverlapType.INTERSECT
&& otherWO instanceof AbstractAreaWorldObject) {
AbstractAreaWorldObject otherAAWO =
((AbstractAreaWorldObject)otherWO);
for (int i = 0; i < overlapWA.getIntersectionPositions().size(); i++) {
VectorXZ pos =
overlapWA.getIntersectionPositions().get(i);
MapAreaSegment areaSegment =
overlapWA.getIntersectingAreaSegments().get(i);
EleConnector thisConn = primaryRep.getEleConnectors()
.getConnector(pos);
EleConnector base1 = otherAAWO.getEleConnectors()
.getConnector(areaSegment.getStartNode().getPos());
EleConnector base2 = otherAAWO.getEleConnectors()
.getConnector(areaSegment.getEndNode().getPos());
if (thisConn != null && base1 != null && base2 != null) {
if (thisIsUpper) {
enforcer.requireVerticalDistance(MIN, distance,
thisConn, base1, base2);
} else {
enforcer.requireVerticalDistance(MAX, -distance,
thisConn, base1, base2);
}
}
}
}
/*
* require minimum distance to the area's elevation connectors.
* There is usually no direct counterpart for these in this segment.
* Examples include trees on terrain above tunnels.
*/
if (!(otherWO instanceof Forest)) continue; //TODO enable and debug for other WO classes
eleConnectors:
for (EleConnector c : otherWO.getEleConnectors()) {
if (outlinePolygonXZ == null ||
!outlinePolygonXZ.contains(c.pos))
continue eleConnectors;
for (List<VectorXZ> line : lines) {
for (int i = 0; i+1 < line.size(); i++) {
VectorXZ v1 = line.get(i);
VectorXZ v2 = line.get(i+1);
if (isBetween(c.pos, v1, v2)) {
EleConnector base1 = primaryRep.getEleConnectors().getConnector(v1);
EleConnector base2 = primaryRep.getEleConnectors().getConnector(v2);
if (base1 != null && base2 != null) {
if (thisIsUpper) {
enforcer.requireVerticalDistance(
MAX, -distance,
c, base1, base2);
} else {
enforcer.requireVerticalDistance(
MIN, distance,
c, base1, base2);
}
}
continue eleConnectors;
}
}
}
}
}
}
}
@Override
public String toString() {
return this.getClass().getSimpleName() + "(" + segment + ")";
}
}