package maps.gml.generator; import maps.gml.GMLMap; import maps.gml.GMLNode; //import maps.gml.GMLEdge; import maps.gml.GMLDirectedEdge; import maps.gml.GMLBuilding; //import maps.gml.GMLRoad; import maps.gml.GMLCoordinates; import rescuecore2.config.Config; import rescuecore2.misc.geometry.Point2D; import rescuecore2.misc.geometry.GeometryTools2D; import rescuecore2.log.Logger; import org.uncommons.maths.number.NumberGenerator; import org.uncommons.maths.random.Probability; import org.uncommons.maths.random.ContinuousUniformGenerator; import java.util.Collection; import java.util.List; import java.util.ArrayList; import java.util.HashSet; /** A MapGenerator that generates a grid world. */ public class ManhattanGenerator implements MapGenerator { private static final String GRID_WIDTH_KEY = "generator.manhattan.grid.width"; private static final String GRID_HEIGHT_KEY = "generator.manhattan.grid.height"; private static final String GRID_SIZE_KEY = "generator.manhattan.grid.size"; private static final String ROAD_WIDTH_KEY = "generator.manhattan.road.width"; private static final String BUILDING_WIDTH_MIN_KEY = "generator.manhattan.building.width.min"; private static final String BUILDING_HEIGHT_MIN_KEY = "generator.manhattan.building.height.min"; private static final String BUILDING_SEPARATION_MIN_KEY = "generator.manhattan.building.separation.min"; private static final String BUILDING_SEPARATION_MAX_KEY = "generator.manhattan.building.separation.max"; private static final String BUILDING_MIN_SIZE_KEY = "generator.manhattan.building.split.min-size"; private static final String BUILDING_MAX_SIZE_KEY = "generator.manhattan.building.split.max-size"; private static final String BUILDING_SPLIT_CHANCE_KEY = "generator.manhattan.building.split.chance"; private Config config; // private NumberGenerator<Double> widthGenerator; // private NumberGenerator<Double> heightGenerator; private NumberGenerator<Double> separationGenerator; private Probability split; private double minSize; private double maxSize; private double minWidth; private double minHeight; private GMLMap map; /** Construct a ManhattanGenerator. @param config The configuration to use. */ public ManhattanGenerator(Config config) { this.config = config; // widthGenerator = new ContinuousUniformGenerator(config.getFloatValue(BUILDING_WIDTH_MIN_KEY), config.getFloatValue(BUILDING_WIDTH_MAX_KEY), config.getRandom()); // heightGenerator = new ContinuousUniformGenerator(config.getFloatValue(BUILDING_HEIGHT_MIN_KEY), config.getFloatValue(BUILDING_HEIGHT_MAX_KEY), config.getRandom()); // Logger.debug("separation min: " + config.getFloatValue(BUILDING_SEPARATION_MIN_KEY)); // Logger.debug("separation max: " + config.getFloatValue(BUILDING_SEPARATION_MAX_KEY)); separationGenerator = new ContinuousUniformGenerator(config.getFloatValue(BUILDING_SEPARATION_MIN_KEY), config.getFloatValue(BUILDING_SEPARATION_MAX_KEY), config.getRandom()); // Logger.debug("Generator: "+ separationGenerator); split = new Probability(config.getFloatValue(BUILDING_SPLIT_CHANCE_KEY)); minSize = config.getFloatValue(BUILDING_MIN_SIZE_KEY); maxSize = config.getFloatValue(BUILDING_MAX_SIZE_KEY); minWidth = config.getFloatValue(BUILDING_WIDTH_MIN_KEY); minHeight = config.getFloatValue(BUILDING_HEIGHT_MIN_KEY); } @Override public void populate(GMLMap gmlMap) { this.map = gmlMap; int gridWidth = config.getIntValue(GRID_WIDTH_KEY); int gridHeight = config.getIntValue(GRID_HEIGHT_KEY); double gridSize = config.getIntValue(GRID_SIZE_KEY); double roadWidth = config.getIntValue(ROAD_WIDTH_KEY); Logger.debug("Generating manhattan map: grid size " + gridWidth + " x " + gridHeight); Logger.debug("Grid cell size: " + gridSize + "m"); Logger.debug("Road width: " + roadWidth + "m"); Collection<GMLBuilding> allBuildings = new ArrayList<GMLBuilding>(); for (int gridX = 0; gridX < gridWidth; ++gridX) { for (int gridY = 0; gridY < gridHeight; ++gridY) { double cellXMin = (gridX * gridSize) + roadWidth; double cellYMin = (gridY * gridSize) + roadWidth; double cellXMax = ((gridX + 1) * gridSize) - roadWidth; double cellYMax = ((gridY + 1) * gridSize) - roadWidth; GMLBuilding base = createBuilding(cellXMin, cellYMin, cellXMax, cellYMax); allBuildings.addAll(divide(base)); } } map.removeAllNodes(); map.removeAllEdges(); map.removeAllBuildings(); for (GMLBuilding next : allBuildings) { map.add(next); for (GMLDirectedEdge edge : next.getEdges()) { map.add(edge.getEdge()); map.add(edge.getEdge().getStart()); map.add(edge.getEdge().getEnd()); } } } private Collection<GMLBuilding> divide(GMLBuilding b) { // Logger.debug("Possibly dividing building " + b); Collection<GMLBuilding> result = new HashSet<GMLBuilding>(); List<Point2D> vertices = coordinatesToVertices(b.getUnderlyingCoordinates()); double area = GeometryTools2D.computeArea(vertices); // Logger.debug("Area: " + area + " sqm"); if (area <= minSize) { result.add(b); } else { if (area > maxSize || split.nextEvent(config.getRandom())) { // Split the building double xMin = b.getBounds().getMinX(); double xMax = b.getBounds().getMaxX(); double yMin = b.getBounds().getMinY(); double yMax = b.getBounds().getMaxY(); double width = xMax - xMin; double height = yMax - yMin; // Logger.debug("Width: " + width); // Logger.debug("Height: " + height); // Logger.debug("width * height = " + (width * height)); // Logger.debug("Area = " + area); if (height > width) { // Logger.debug("Splitting horizontally"); // Horizontal split double splitY = (yMax + yMin) / 2; double topOffset = separationGenerator.nextValue(); double bottomOffset = separationGenerator.nextValue(); double topY = splitY + topOffset; double bottomY = splitY - bottomOffset; // Logger.debug("yMin = " + yMin); // Logger.debug("yMax = " + yMax); // Logger.debug("splitY = " + splitY); // Logger.debug("topOffset = " + topOffset); // Logger.debug("bottomOffset = " + bottomOffset); // Logger.debug("topY = " + topY); // Logger.debug("bottomY = " + bottomY); if (yMax - topY < minHeight || bottomY - yMin < minHeight) { // Logger.debug("Split too thin"); // Logger.debug("Top piece: " + (yMax - topY)); // Logger.debug("Bottom piece: " + (bottomY - yMin)); // Logger.debug("Minimum height: " + minHeight); result.add(b); } else { result.addAll(divide(createBuilding(xMin, yMin, xMax, bottomY))); result.addAll(divide(createBuilding(xMin, topY, xMax, yMax))); } } else { // Logger.debug("Splitting vertically"); // Vertical split double splitX = (xMax + xMin) / 2; double leftOffset = separationGenerator.nextValue(); double rightOffset = separationGenerator.nextValue(); double leftX = splitX - leftOffset; double rightX = splitX + rightOffset; // Logger.debug("xMin = " + xMin); // Logger.debug("xMax = " + xMax); // Logger.debug("splitX = " + splitX); // Logger.debug("leftOffset = " + leftOffset); // Logger.debug("rightOffset = " + rightOffset); // Logger.debug("leftX = " + leftX); // Logger.debug("rightX = " + rightX); if (xMax - rightX < minWidth || leftX - xMin < minWidth) { // Logger.debug("Split too thin"); // Logger.debug("Left piece: " + (leftX - xMin)); // Logger.debug("Right piece: " + (xMax - rightX)); // Logger.debug("Minimum width: " + minWidth); result.add(b); } else { result.addAll(divide(createBuilding(xMin, yMin, leftX, yMax))); result.addAll(divide(createBuilding(rightX, yMin, xMax, yMax))); } } } else { // Logger.debug("Not splitting"); result.add(b); } } return result; } private List<Point2D> coordinatesToVertices(List<GMLCoordinates> coords) { List<Point2D> result = new ArrayList<Point2D>(coords.size()); for (GMLCoordinates c : coords) { result.add(new Point2D(c.getX(), c.getY())); } return result; } private GMLBuilding createBuilding(double xMin, double yMin, double xMax, double yMax) { List<GMLNode> nodes = new ArrayList<GMLNode>(); nodes.add(map.createNode(xMin, yMin)); nodes.add(map.createNode(xMax, yMin)); nodes.add(map.createNode(xMax, yMax)); nodes.add(map.createNode(xMin, yMax)); return map.createBuildingFromNodes(nodes); } }