package org.osm2world.core.map_data.data;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.openstreetmap.josm.plugins.graphview.core.data.TagGroup;
import org.osm2world.core.map_data.data.overlaps.MapOverlap;
import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.math.InvalidGeometryException;
import org.osm2world.core.math.PolygonWithHolesXZ;
import org.osm2world.core.math.SimplePolygonXZ;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.osm.data.OSMElement;
import org.osm2world.core.world.data.AreaWorldObject;
public class MapArea implements MapElement {
private final OSMElement objectWithTags;
private final List<MapNode> nodes;
private final List<List<MapNode>> holes;
private final PolygonWithHolesXZ polygon;
private Collection<MapAreaSegment> areaSegments;
@SuppressWarnings("unchecked") //is later checked for EMPTY_LIST using ==
private Collection<MapOverlap<?,?>> overlaps = Collections.EMPTY_LIST;
private List<AreaWorldObject> representations =
new ArrayList<AreaWorldObject>(1);
//TODO: contained / intersecting nodes/lines
public MapArea(OSMElement objectWithTags, List<MapNode> nodes) {
this(objectWithTags, nodes, Collections.<List<MapNode>>emptyList());
}
public MapArea(OSMElement objectWithTags, List<MapNode> nodes,
List<List<MapNode>> holes) {
this.objectWithTags = objectWithTags;
this.nodes = nodes;
this.holes = holes;
try {
this.polygon = convertToPolygon(nodes, holes);
} catch (InvalidGeometryException e) {
throw new InvalidGeometryException(
"outer polygon is not simple for this object: "
+ objectWithTags, e);
}
}
public MapArea(OSMElement objectWithTags, List<MapNode> nodes,
List<List<MapNode>> holes, PolygonWithHolesXZ polygon) {
this.objectWithTags = objectWithTags;
this.nodes = nodes;
this.holes = holes;
this.polygon = polygon;
}
private static final PolygonWithHolesXZ convertToPolygon(
List<MapNode> nodes, List<List<MapNode>> holes) {
SimplePolygonXZ outerPolygon =
polygonFromMapNodeLoop(nodes);
List<SimplePolygonXZ> holePolygons =
new ArrayList<SimplePolygonXZ>(holes.size());
for (List<MapNode> hole : holes) {
holePolygons.add(polygonFromMapNodeLoop(hole));
}
return new PolygonWithHolesXZ(outerPolygon, holePolygons);
}
public static final SimplePolygonXZ polygonFromMapNodeLoop(
List<MapNode> nodes) {
List<VectorXZ> vertices = new ArrayList<VectorXZ>(nodes.size());
for (MapNode node : nodes) {
vertices.add(node.getPos());
}
return new SimplePolygonXZ(vertices);
}
public List<MapNode> getBoundaryNodes() {
return nodes;
}
@Override
public int getLayer() {
if (objectWithTags.tags.containsKey("layer")) {
try {
return Integer.parseInt(objectWithTags.tags.getValue("layer"));
} catch (NumberFormatException nfe) {
return 0;
}
}
return 0;
}
public Collection<List<MapNode>> getHoles() {
return holes;
}
/** returns the way or relation with the tags for this area */
public OSMElement getOsmObject() {
return objectWithTags;
}
@Override
public TagGroup getTags() {
return objectWithTags.tags;
}
/**
* returns the area as a polygon.
*/
public PolygonWithHolesXZ getPolygon() {
return polygon;
}
public SimplePolygonXZ getOuterPolygon() {
return getPolygon().getOuter();
}
/**
* returns the segments making up this area's outer and inner boundaries
*/
public Collection<MapAreaSegment> getAreaSegments() {
if (areaSegments == null) {
areaSegments = new ArrayList<MapAreaSegment>();
for (int v = 0; v+1 < nodes.size(); v++) {
//relies on duplication of first/last node
areaSegments.add(new MapAreaSegment(this,
getPolygon().getOuter().isClockwise(),
nodes.get(v), nodes.get(v+1)));
}
for (int h = 0; h < holes.size(); h++) {
List<MapNode> holeNodes = holes.get(h);
SimplePolygonXZ holePolygon = polygon.getHoles().get(h);
for (int v = 0; v+1 < holeNodes.size(); v++) {
//relies on duplication of first/last node
areaSegments.add(new MapAreaSegment(this,
!holePolygon.isClockwise(),
holeNodes.get(v), holeNodes.get(v+1)));
}
}
}
return areaSegments;
}
@Override
public List<AreaWorldObject> getRepresentations() {
return representations;
}
@Override
public AreaWorldObject getPrimaryRepresentation() {
if (representations.isEmpty()) {
return null;
} else {
return representations.get(0);
}
}
/**
* adds a visual representation for this area
*/
public void addRepresentation(AreaWorldObject representation) {
this.representations.add(representation);
}
public void addOverlap(MapOverlap<?, ?> overlap) {
assert overlap.e1 == this || overlap.e2 == this;
if (overlaps == Collections.EMPTY_LIST) {
overlaps = new ArrayList<MapOverlap<?,?>>();
}
overlaps.add(overlap);
}
@Override
public Collection<MapOverlap<?,?>> getOverlaps() {
return overlaps;
}
@Override
public AxisAlignedBoundingBoxXZ getAxisAlignedBoundingBoxXZ() {
return new AxisAlignedBoundingBoxXZ(getOuterPolygon().getVertexCollection());
}
@Override
public String toString() {
return objectWithTags.toString();
}
}