diff --git a/javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalGridMapCompiler.java b/javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalGridMapCompiler.java new file mode 100644 index 0000000..ba7a4ab --- /dev/null +++ b/javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/HistoricalGridMapCompiler.java @@ -0,0 +1,128 @@ +package com.ft546.wcs.shadow.history.simulation; + +import com.ft546.wcs.demo.model.Site; +import com.ft546.wcs.demo.traffic.GridMap; +import com.ft546.wcs.shadow.history.HistoricalMapCell; +import com.ft546.wcs.shadow.map.EffectiveTrafficMap; +import com.ft546.wcs.shadow.map.TrafficMapCompiler; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public final class HistoricalGridMapCompiler { + public CompiledHistoricalMap compile(List cells) { + if (cells == null || cells.isEmpty()) { + throw new IllegalArgumentException("full map cells are required"); + } + List passable = new ArrayList(); + for (HistoricalMapCell cell : cells) { + if (cell.getValue() == 1 || cell.getValue() == 2) passable.add(cell); + } + Collections.sort(passable, Comparator.comparingInt(HistoricalMapCell::getZ) + .thenComparingInt(HistoricalMapCell::getY) + .thenComparingInt(HistoricalMapCell::getX) + .thenComparing(HistoricalMapCell::getSiteId)); + + GridMap gridMap = new GridMap(); + Map sites = new LinkedHashMap(); + Map coordinates = new LinkedHashMap(); + for (HistoricalMapCell cell : passable) { + if (cell.getSiteId().trim().isEmpty() || sites.put(cell.getSiteId(), cell) != null) { + throw new IllegalArgumentException("passable site ids must be unique and nonblank"); + } + if (coordinates.put(coordinate(cell.getX(), cell.getY(), cell.getZ()), cell) != null) { + throw new IllegalArgumentException("passable coordinates must be unique"); + } + gridMap.addSite(new Site(cell.getSiteId(), cell.getZ(), cell.getX(), cell.getY(), + cell.getAxy().isEmpty() ? "OFFLINE" : cell.getAxy())); + } + + Set trafficEdges = + new LinkedHashSet(); + for (HistoricalMapCell cell : passable) { + connectIfPresent(gridMap, trafficEdges, cell, + coordinates.get(coordinate(cell.getX() + 1, cell.getY(), cell.getZ()))); + connectIfPresent(gridMap, trafficEdges, cell, + coordinates.get(coordinate(cell.getX(), cell.getY() + 1, cell.getZ()))); + } + Set nodeIds = new LinkedHashSet(sites.keySet()); + TrafficMapCompiler.MapDefinition definition = new TrafficMapCompiler.MapDefinition( + "FT546-FULL-GRID-SIM", 1L, "OFFLINE", true, + "full-grid-v1", "full-grid-v1", nodeIds, trafficEdges, + Collections.emptyMap(), Collections.emptySet(), Collections.emptySet()); + EffectiveTrafficMap trafficMap = new TrafficMapCompiler().compile(definition); + return new CompiledHistoricalMap(gridMap, trafficMap, sites, components(gridMap)); + } + + private void connectIfPresent(GridMap map, + Set trafficEdges, + HistoricalMapCell left, HistoricalMapCell right) { + if (right == null) return; + map.connect(left.getSiteId(), right.getSiteId()); + trafficEdges.add(new TrafficMapCompiler.EdgeDefinition( + left.getSiteId(), right.getSiteId())); + } + + private Map components(GridMap map) { + Map result = new LinkedHashMap(); + List ids = new ArrayList(map.getSites().keySet()); + Collections.sort(ids); + int component = 0; + for (String start : ids) { + if (result.containsKey(start)) continue; + component++; + ArrayDeque queue = new ArrayDeque(); + queue.add(start); + result.put(start, component); + while (!queue.isEmpty()) { + String site = queue.removeFirst(); + for (String neighbor : map.getNeighbors(site)) { + if (!result.containsKey(neighbor)) { + result.put(neighbor, component); + queue.addLast(neighbor); + } + } + } + } + return result; + } + + private static String coordinate(int x, int y, int z) { + return x + "," + y + "," + z; + } + + public static final class CompiledHistoricalMap { + private final GridMap gridMap; + private final EffectiveTrafficMap trafficMap; + private final Map passableSites; + private final Map componentIds; + + private CompiledHistoricalMap(GridMap gridMap, EffectiveTrafficMap trafficMap, + Map passableSites, + Map componentIds) { + this.gridMap = gridMap; + this.trafficMap = trafficMap; + this.passableSites = Collections.unmodifiableMap( + new LinkedHashMap(passableSites)); + this.componentIds = Collections.unmodifiableMap( + new LinkedHashMap(componentIds)); + } + + public GridMap getGridMap() { return gridMap; } + public EffectiveTrafficMap getTrafficMap() { return trafficMap; } + public Map getPassableSites() { return passableSites; } + + public int getComponentId(String siteId) { + Integer component = componentIds.get(siteId); + if (component == null) throw new IllegalArgumentException("unknown passable site " + siteId); + return component; + } + } +} diff --git a/javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalGridMapCompilerTest.java b/javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalGridMapCompilerTest.java new file mode 100644 index 0000000..a526fe3 --- /dev/null +++ b/javaTest/src/test/java/com/ft546/wcs/shadow/history/simulation/HistoricalGridMapCompilerTest.java @@ -0,0 +1,38 @@ +package com.ft546.wcs.shadow.history.simulation; + +import com.ft546.wcs.shadow.history.HistoricalMapCell; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class HistoricalGridMapCompilerTest { + @Test + void connectsOnlyPassableSameFloorManhattanNeighbors() { + HistoricalGridMapCompiler.CompiledHistoricalMap compiled = + new HistoricalGridMapCompiler().compile(Arrays.asList( + cell("A", 1, 1, 1, 1), + cell("B", 2, 1, 1, 2), + cell("BLOCK", 1, 2, 1, 0), + cell("DIAGONAL", 2, 2, 1, 1), + cell("UP", 1, 1, 2, 1))); + + assertEquals(4, compiled.getGridMap().getSites().size()); + assertFalse(compiled.getGridMap().getSites().containsKey("BLOCK")); + assertTrue(compiled.getGridMap().getNeighbors("A").contains("B")); + assertFalse(compiled.getGridMap().getNeighbors("A").contains("DIAGONAL")); + assertFalse(compiled.getGridMap().getNeighbors("A").contains("UP")); + assertEquals(2, compiled.getTrafficMap().getEdges().size()); + assertEquals(compiled.getComponentId("A"), compiled.getComponentId("DIAGONAL")); + assertNotEquals(compiled.getComponentId("A"), compiled.getComponentId("UP")); + } + + private HistoricalMapCell cell(String id, int x, int y, int z, int value) { + return new HistoricalMapCell(id, x, y, z, value, x + "," + y, + "W", "AREA"); + } +}