def fillPopulationEnergyGraph(self, alignment): remainingObjects = self.children.copy() if alignment is Alignment.northSouth: blocksToActOn = self.westernChildBlocks else: blocksToActOn = self.northernChildBlocks # work west to east or north to south if len(blocksToActOn) > 0: for blockToActOn in blocksToActOn: blockToActOn.populationEnergy = blockToActOn.population remainingObjects.remove(blockToActOn) filledBlocks = blocksToActOn.copy() while len(remainingObjects) > 0: neighborsOfBlocks = getNeighborsForGraphObjectsInList(graphObjects=blocksToActOn, inList=remainingObjects) if len(neighborsOfBlocks) > 0: blocksToActOn = neighborsOfBlocks else: saveDataToFileWithDescription(data=self, censusYear='', stateName='', descriptionOfInfo='ErrorCase-NoNeighborsForGraphGroups') plotGraphObjectGroups([self.children, blocksToActOn], showDistrictNeighborConnections=True) plotBlocksForRedistrictingGroup(self, showBlockNeighborConnections=True) raise RuntimeError("Can't find neighbors for graph objects") blocksToActOnThisRound = blocksToActOn.copy() while len(blocksToActOnThisRound) > 0: blocksActedUpon = [] for blockToActOn in blocksToActOnThisRound: previousNeighbors = getNeighborsForGraphObjectsInList(graphObjects=[blockToActOn], inList=filledBlocks) if len(previousNeighbors) is not 0: lowestPopulationEnergyNeighbor = min(previousNeighbors, key=lambda block: block.populationEnergy) blockToActOn.populationEnergy = lowestPopulationEnergyNeighbor.populationEnergy + blockToActOn.population remainingObjects.remove(blockToActOn) blocksActedUpon.append(blockToActOn) filledBlocks.append(blockToActOn) blocksToActOnThisRound = [block for block in blocksToActOnThisRound if block not in blocksActedUpon] if len(blocksActedUpon) == 0: saveDataToFileWithDescription(data=self, censusYear='', stateName='', descriptionOfInfo='ErrorCase-BlocksCanNotFindPreviousNeighbor') plotGraphObjectGroups([filledBlocks, blocksToActOnThisRound]) plotBlocksForRedistrictingGroup(self, showBlockNeighborConnections=True, showGraphHeatmap=True, showBlockGraphIds=True) raise ReferenceError("Can't find previous neighbor for {0}".format(blocksToActOnThisRound)) # add population to surrounding neighbors population energy # this is to help create smoother graphs in urban areas for block in self.children: for neighborBlock in block.allNeighbors: neighborBlock.populationEnergy += block.population
def validateBlockNeighbors(self): contiguousRegions = findContiguousGroupsOfGraphObjects(self.children) if len(contiguousRegions) > 1: saveDataToFileWithDescription(data=[self, contiguousRegions], censusYear='', stateName='', descriptionOfInfo='ErrorCase-BlocksNotContiguous') plotGraphObjectGroups(contiguousRegions, showDistrictNeighborConnections=True) plotBlocksForRedistrictingGroup(self, showBlockNeighborConnections=True, showBlockGraphIds=True) raise RuntimeError("Don't have a contiguous set of AtomicBlocks. There are {0} distinct groups.".format( len(contiguousRegions))) for block in self.children: neighborBlocksNotInGroup = [neighborBlock for neighborBlock in block.allNeighbors if neighborBlock not in self.children] if len(neighborBlocksNotInGroup): saveDataToFileWithDescription(data=[self, block], censusYear='', stateName='', descriptionOfInfo='ErrorCase-BlockHasNeighborOutsideRedistrictingGroup') plotBlocksForRedistrictingGroup(self, showBlockNeighborConnections=True, showBlockGraphIds=True) raise RuntimeError("Some blocks have neighbor connections with block outside the redistricting group")
def getGraphSplits(self, alignment=Alignment.all, shouldDrawGraph=False, countForProgress=None): if len(self.children) == 1: raise RuntimeError("Can't split RedistrictingGroup with a single child. GraphId: {0}".format(self.graphId)) if countForProgress is None: pbar = None else: tqdm.write(' *** Finding seams for graph split {0} - GraphId: {1} - Block count: {2} ***' .format(countForProgress, self.graphId, len(self.children))) if alignment is Alignment.all: progressTotal = 4 else: progressTotal = 2 pbar = tqdm(total=progressTotal) northSouthSplit = None westEastSplit = None if alignment is Alignment.all or alignment is Alignment.northSouth: self.fillPopulationEnergyGraph(Alignment.northSouth) if pbar is not None: pbar.update(1) northSouthSplitResult = self.getPopulationEnergySplit(Alignment.northSouth, shouldDrawGraph=shouldDrawGraph) northSouthSplitResultType = northSouthSplitResult[0] if northSouthSplitResultType is SplitType.NoSplit: northSouthSplit = None elif northSouthSplitResultType is SplitType.ForceSplitAllBlocks: return self.createRedistrictingGroupForEachChild() else: northSouthSplit = northSouthSplitResult[1] if pbar is not None: pbar.update(1) self.clearPopulationEnergyGraph() if alignment is Alignment.all or alignment is Alignment.westEast: self.fillPopulationEnergyGraph(Alignment.westEast) if pbar is not None: pbar.update(1) westEastSplitResult = self.getPopulationEnergySplit(Alignment.westEast, shouldDrawGraph=shouldDrawGraph) westEastSplitResultType = westEastSplitResult[0] if westEastSplitResultType is SplitType.NoSplit: westEastSplit = None elif westEastSplitResultType is SplitType.ForceSplitAllBlocks: return self.createRedistrictingGroupForEachChild() else: westEastSplit = westEastSplitResult[1] if pbar is not None: pbar.update(1) self.clearPopulationEnergyGraph() if pbar is not None: pbar.close() if northSouthSplit is None and westEastSplit is None: return self.createRedistrictingGroupForEachChild() if countForProgress is not None: tqdm.write(' *** Creating new Redistricting Groups in {0} ***'.format(countForProgress)) splitGroups = [] if northSouthSplit and not westEastSplit: northSplit = RedistrictingGroup(childrenBlocks=northSouthSplit[0]) splitGroups.append(northSplit) southSplit = RedistrictingGroup(childrenBlocks=northSouthSplit[1]) splitGroups.append(southSplit) elif not northSouthSplit and westEastSplit: westSplit = RedistrictingGroup(childrenBlocks=westEastSplit[0]) splitGroups.append(westSplit) eastSplit = RedistrictingGroup(childrenBlocks=westEastSplit[1]) splitGroups.append(eastSplit) else: northWestSplitChildren = [group for group in northSouthSplit[0] if group in westEastSplit[0]] northEastSplitChildren = [group for group in northSouthSplit[0] if group in westEastSplit[1]] southWestSplitChildren = [group for group in northSouthSplit[1] if group in westEastSplit[0]] southEastSplitChildren = [group for group in northSouthSplit[1] if group in westEastSplit[1]] splitChildrenList = [northWestSplitChildren, northEastSplitChildren, southWestSplitChildren, southEastSplitChildren] for splitChildren in splitChildrenList: if len(splitChildren) > 0: splitGroup = RedistrictingGroup(childrenBlocks=splitChildren) splitGroups.append(splitGroup) if countForProgress is not None: tqdm.write( ' *** Re-assigning neighboring blocks to new Redistricting Groups in {0} ***'.format( countForProgress)) # check for orphan blocks and validate existing neighboring blocks and attach to intersecting group splitGroups = reorganizeAtomicBlockBetweenRedistrictingGroups(redistrictingGroups=splitGroups) for splitGroup in splitGroups: if shouldDrawGraph: plotBlocksForRedistrictingGroup(splitGroup) splitGroup.removeOutdatedNeighborConnections() splitGroup.validateBlockNeighbors() if shouldDrawGraph: plotRedistrictingGroups(redistrictingGroups=splitGroups) return splitGroups