package org.osm2world.core.map_data.creation; import static java.lang.Math.min; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.openstreetmap.josm.plugins.graphview.core.data.EmptyTagGroup; import org.openstreetmap.josm.plugins.graphview.core.data.MapBasedTagGroup; import org.openstreetmap.josm.plugins.graphview.core.data.Tag; import org.osm2world.core.map_data.data.MapArea; import org.osm2world.core.map_data.data.MapNode; import org.osm2world.core.math.AxisAlignedBoundingBoxXZ; import org.osm2world.core.math.VectorGridXZ; import org.osm2world.core.math.VectorXZ; import org.osm2world.core.osm.data.OSMNode; import org.osm2world.core.osm.data.OSMWay; /** * utility class for building geometry representing empty terrain. */ public class EmptyTerrainBuilder { /** prevents instantiation */ private EmptyTerrainBuilder() { } /** tag to be internally used on faked ways around "empty terrain" */ public static final Tag EMPTY_SURFACE_TAG = new Tag("surface", "osm2world:empty_terrain"); /** faked outline node for the terrain areas */ private static final OSMNode EMPTY_SURFACE_NODE = new OSMNode( Double.NaN, Double.NaN, EmptyTagGroup.EMPTY_TAG_GROUP, 0); /** faked outline way for the terrain areas */ private static final OSMWay EMPTY_SURFACE_WAY = new OSMWay( new MapBasedTagGroup(EMPTY_SURFACE_TAG), 0, Collections.<OSMNode>emptyList()); public static final double POINT_GRID_DIST = 30; public static final int PATCH_SIZE_POINTS = 10; /** * creates a grid of square {@link MapArea}s to represent empty terrain. * The areas are connected with each other, but do not overlap, * and cover the entire data bounds. * * These areas do not come from OSM data, but they are treated the same * as mapped areas later on to avoid unnecessary special case handling. */ static void createAreasForEmptyTerrain(List<MapNode> mapNodes, List<MapArea> mapAreas, AxisAlignedBoundingBoxXZ dataBounds) { VectorGridXZ posGrid = new VectorGridXZ( dataBounds.pad(POINT_GRID_DIST), POINT_GRID_DIST); /* create a grid of nodes (leaving points within the future patches blank) */ MapNode[][] nodeGrid = new MapNode[posGrid.sizeX()][posGrid.sizeZ()]; for (int x = 0; x < posGrid.sizeX(); x++) { for (int z = 0; z < posGrid.sizeZ(); z++) { if (x % PATCH_SIZE_POINTS == 0 || x == posGrid.sizeX() - 1 || z % PATCH_SIZE_POINTS == 0 || z == posGrid.sizeZ() - 1) { VectorXZ pos = posGrid.get(x, z); MapNode mapNode = new MapNode(pos, EMPTY_SURFACE_NODE); nodeGrid[x][z] = mapNode; mapNodes.add(mapNode); } } } /* create a grid of areas based on the nodes */ // calculate the number of patches, but always round up int numPatchesX = (nodeGrid.length + PATCH_SIZE_POINTS - 2) / PATCH_SIZE_POINTS; int numPatchesZ = (nodeGrid[0].length + PATCH_SIZE_POINTS - 2) / PATCH_SIZE_POINTS; for (int patchX = 0; patchX < numPatchesX; patchX++) { for (int z = 0; z < numPatchesZ; z++) { mapAreas.add(createAreaForPatch(nodeGrid, patchX * PATCH_SIZE_POINTS, z * PATCH_SIZE_POINTS)); } } } private static MapArea createAreaForPatch(MapNode[][] nodeGrid, int startX, int startZ) { int endX = min(startX + PATCH_SIZE_POINTS + 1, nodeGrid.length); int endZ = min(startZ + PATCH_SIZE_POINTS + 1, nodeGrid[0].length); List<MapNode> nodes = new ArrayList<MapNode>(); // first row for (int x = startX; x < endX; x++) { nodes.add(nodeGrid[x][startZ]); } // last column for (int z = startZ + 1; z < endZ - 1; z++) { nodes.add(nodeGrid[endX - 1][z]); } // last row for (int x = endX - 1; x >= startX; x--) { nodes.add(nodeGrid[x][endZ - 1]); } // first column for (int z = endZ - 2; z >= startZ /* start will be added again */; z--) { nodes.add(nodeGrid[startX][z]); } return new MapArea(EMPTY_SURFACE_WAY, nodes); } }