package org.osm2world.core.map_data.data;
import java.util.Collection;
import java.util.List;
import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.osm.data.OSMData;
import org.osm2world.core.world.data.TerrainBoundaryWorldObject;
import org.osm2world.core.world.data.WorldObject;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
/**
* OSM2World's abstraction of {@link OSMData}, consists of {@link MapElement}s.
* Initially contains only a slightly altered representation of OSM
* map data. During later conversion steps, additional information is
* added to the {@link MapElement}s.
*/
public class MapData {
final List<MapNode> mapNodes;
final List<MapWaySegment> mapWaySegments;
final List<MapArea> mapAreas;
AxisAlignedBoundingBoxXZ fileBoundary;
AxisAlignedBoundingBoxXZ dataBoundary;
public MapData(List<MapNode> mapNodes, List<MapWaySegment> mapWaySegments,
List<MapArea> mapAreas, AxisAlignedBoundingBoxXZ fileBoundary) {
this.mapNodes = mapNodes;
this.mapWaySegments = mapWaySegments;
this.mapAreas = mapAreas;
this.fileBoundary = fileBoundary;
calculateDataBoundary();
}
private void calculateDataBoundary() {
double minX = Double.POSITIVE_INFINITY;
double maxX = Double.NEGATIVE_INFINITY;
double minZ = Double.POSITIVE_INFINITY;
double maxZ = Double.NEGATIVE_INFINITY;
if (fileBoundary != null) {
// use the file boundary as the minimum extent of the data boundary
minX = fileBoundary.minX;
maxX = fileBoundary.maxX;
minZ = fileBoundary.minZ;
maxZ = fileBoundary.maxZ;
}
for (MapNode node : mapNodes) {
final double nodeX = node.getPos().x;
final double nodeZ = node.getPos().z;
if (nodeX < minX) { minX = nodeX; }
if (nodeX > maxX) { maxX = nodeX; }
if (nodeZ < minZ) { minZ = nodeZ; }
if (nodeZ > maxZ) { maxZ = nodeZ; }
}
dataBoundary = new AxisAlignedBoundingBoxXZ(minX, minZ, maxX, maxZ);
}
public Iterable<MapElement> getMapElements() {
return Iterables.concat(mapNodes, mapWaySegments, mapAreas);
}
public Collection<MapArea> getMapAreas() {
return mapAreas;
}
public Collection<MapWaySegment> getMapWaySegments() {
return mapWaySegments;
}
public Collection<MapNode> getMapNodes() {
return mapNodes;
}
/**
* returns a rectangular boundary polygon from the minimum/maximum of
* coordinates in the map data
*/
public AxisAlignedBoundingBoxXZ getDataBoundary() {
return dataBoundary;
}
/**
* returns a boundary based on the bounds in the input file if available,
* otherwise returns the same as {@link #getDataBoundary()}
*/
public AxisAlignedBoundingBoxXZ getBoundary() {
if (fileBoundary != null) {
return fileBoundary;
} else {
return dataBoundary;
}
}
/**
* calculates the center from the {@link MapNode}s' positions
*/
public VectorXZ getCenter() {
int nodeCount = getMapNodes().size();
double avgX = 0, avgZ = 0;
for (MapNode node : getMapNodes()) {
avgX += node.getPos().x / nodeCount; // need to divide before
// numbers get too large
avgZ += node.getPos().z / nodeCount;
}
return new VectorXZ(avgX, avgZ);
}
/**
* returns all {@link WorldObject}s from elements in this data set.
*/
public Iterable<WorldObject> getWorldObjects() {
return Iterables.concat(
Iterables.transform(getMapElements(),
new Function<MapElement, Iterable<? extends WorldObject>>() {
@Override public Iterable<? extends WorldObject> apply(MapElement e) {
return e.getRepresentations();
}
}));
}
/**
* returns all {@link WorldObject}s from elements in this data set
* that are instances of a certain type.
* Can be used, for example, to access all
* {@link TerrainBoundaryWorldObject}s in the grid.
*/
public <T> Iterable<T> getWorldObjects(Class<T> type) {
return Iterables.filter(getWorldObjects(), type);
}
}