def reorganizeAtomicBlockBetweenRedistrictingGroups(redistrictingGroups):
    for redistrictingGroup in redistrictingGroups:
        for borderBlock in redistrictingGroup.borderChildren:
            borderBlock.removeNonIntersectingNeighbors()

    atomicBlockGroupDict = {}
    for redistrictingGroup in redistrictingGroups:
        atomicBlockGroupDict[redistrictingGroup.graphId] = redistrictingGroup.children.copy()

    atomicBlockGroups = atomicBlockGroupDict.values()
    for atomicBlockGroup in atomicBlockGroups:
        contiguousRegions = findContiguousGroupsOfGraphObjects(atomicBlockGroup)
        while len(contiguousRegions) > 1:
            smallestContiguousRegion = min(contiguousRegions,
                                           key=lambda contiguousRegion: len(contiguousRegion))
            smallestContiguousRegionPolygon = polygonFromMultipleGeometries(smallestContiguousRegion)

            otherRegion = None
            otherSplitChildrenList = [x for x in atomicBlockGroups if x is not atomicBlockGroup]
            for otherSplitChildren in otherSplitChildrenList:
                otherSplitChildrenPolygon = polygonFromMultipleGeometries(otherSplitChildren)
                if intersectingPolygons(smallestContiguousRegionPolygon, otherSplitChildrenPolygon):
                    otherRegion = otherSplitChildren
                    break

            if otherRegion is None:
                allBlocksInOtherSplits = [block for blockList in otherSplitChildrenList for block in blockList]
                closestBlock = findClosestGeometry(smallestContiguousRegionPolygon, allBlocksInOtherSplits)
                closestSplit = next((blockList for blockList in otherSplitChildrenList if closestBlock in blockList),
                                    None)
                otherRegion = closestSplit

            for childBlock in smallestContiguousRegion:
                atomicBlockGroup.remove(childBlock)
                childBlock.removeNeighborConnections()

                otherRegion.append(childBlock)
                assignNeighborBlocksFromCandidateBlocks(block=childBlock, candidateBlocks=otherRegion)
            contiguousRegions = findContiguousGroupsOfGraphObjects(atomicBlockGroup)

    for key, value in atomicBlockGroupDict.items():
        groupWithId = next((redistrictingGroup for redistrictingGroup in redistrictingGroups
                            if redistrictingGroup.graphId == key), None)
        groupWithId.children = value

    for redistrictingGroup in redistrictingGroups:
        redistrictingGroup.attachOrphanBlocksToClosestNeighbor()

    return redistrictingGroups
Пример #2
0
            def polsbyPopperScoreOfCombinedGeometry(currentGroupPolygon,
                                                    remainingGroups,
                                                    candidateGroups,
                                                    fastCalculations=True):
                candidateGroupsPolygon = polygonFromMultipleGeometries(
                    candidateGroups, useEnvelope=fastCalculations)
                # never useEnvelope here, because currentGroupPolygon is our cached shape
                candidatePolygon = polygonFromMultiplePolygons(
                    [currentGroupPolygon, candidateGroupsPolygon])
                combinedRemainingPolygon = polygonFromMultipleGeometries(
                    remainingGroups, useEnvelope=fastCalculations)

                score = polsbyPopperScoreOfPolygon(candidatePolygon)
                remainingScore = polsbyPopperScoreOfPolygon(
                    combinedRemainingPolygon)
                minimumPolsbyPopperScore = min(score, remainingScore)

                return minimumPolsbyPopperScore
Пример #3
0
            def distanceScoreOfCombinedGeometry(currentGroupPolygon,
                                                remainingGroups,
                                                candidateGroups,
                                                fastCalculations=True):
                candidateGroupsPolygon = polygonFromMultipleGeometries(
                    candidateGroups, useEnvelope=fastCalculations)
                distance = currentGroupPolygon.centroid.distance(
                    candidateGroupsPolygon.centroid)
                score = 1 / distance

                return score
Пример #4
0
            def cardinalDirectionScoreOfCandidateGroups(
                    currentGroupPolygon,
                    remainingGroups,
                    candidateGroups,
                    fastCalculations=True):
                boundsIndex = boundsIndexFromDirection(fillOriginDirection)
                directionReferenceValue = self.geometry.bounds[boundsIndex]
                candidateGroupsPolygon = polygonFromMultipleGeometries(
                    candidateGroups, useEnvelope=fastCalculations)
                candidateGroupsValue = candidateGroupsPolygon.bounds[
                    boundsIndex]
                difference = directionReferenceValue - candidateGroupsValue
                difference = math.fabs(difference)
                if difference == 0:
                    score = math.inf
                else:
                    score = 1 / difference

                return score
    def getPopulationEnergyPolygonSplit(self, alignment, shouldDrawGraph=False):
        finishingBlocksToAvoid = []
        while True:
            lowestEnergySeamResult = self.getLowestPopulationEnergySeam(alignment=alignment,
                                                                        finishingBlocksToAvoid=finishingBlocksToAvoid)
            if lowestEnergySeamResult is None:
                return SplitType.NoSplit, None
            lowestEnergySeam = lowestEnergySeamResult[0]
            energySeamFinishingBlock = lowestEnergySeamResult[1]
            energySeamStartingEnergy = lowestEnergySeamResult[2]

            seamSplitPolygon = polygonFromMultipleGeometries(geometryList=lowestEnergySeam)
            polygonWithoutSeam = self.geometry.difference(seamSplitPolygon)

            # if the polygon without the seam is empty, that means we have a small enough redistricting group where
            # we need to break it up completely. Because our seams can no longer break up any further.
            if polygonWithoutSeam.is_empty:
                return SplitType.ForceSplitAllBlocks, None

            if type(polygonWithoutSeam) is MultiPolygon:
                seamOnEdge = False
                splitPolygons = list(polygonWithoutSeam)
            else:
                seamOnEdge = True
                splitPolygons = [polygonWithoutSeam, seamSplitPolygon]

            if alignment is Alignment.northSouth:
                aSplitRepresentativeBlockDirection = CardinalDirection.north
                bSplitRepresentativeBlockDirection = CardinalDirection.south
            else:
                aSplitRepresentativeBlockDirection = CardinalDirection.west
                bSplitRepresentativeBlockDirection = CardinalDirection.east

            # Identify which polygon is in which direction
            # Note: Need to make sure we don't select a block in the seam so we supply a list without those blocks
            #   If the seam is completely on the edge though, let's include the seam
            if seamOnEdge:
                borderChildrenRepresentativeCandidates = self.borderChildren
            else:
                borderChildrenRepresentativeCandidates = [child for child in self.borderChildren if
                                                          child not in lowestEnergySeam]

            if len(borderChildrenRepresentativeCandidates) == 0:
                return SplitType.NoSplit, None

            aSplitRepresentativeBlock = mostCardinalOfGeometries(geometryList=borderChildrenRepresentativeCandidates,
                                                                 direction=aSplitRepresentativeBlockDirection)

            bSplitRepresentativeBlock = mostCardinalOfGeometries(geometryList=borderChildrenRepresentativeCandidates,
                                                                 direction=bSplitRepresentativeBlockDirection)

            aSplitPolygon = getPolygonThatContainsGeometry(polygonList=splitPolygons,
                                                           targetGeometry=aSplitRepresentativeBlock,
                                                           useTargetRepresentativePoint=True)
            bSplitPolygon = getPolygonThatContainsGeometry(polygonList=splitPolygons,
                                                           targetGeometry=bSplitRepresentativeBlock,
                                                           useTargetRepresentativePoint=True)
            leftOverPolygons = [geometry for geometry in splitPolygons if
                                geometry is not aSplitPolygon and geometry is not bSplitPolygon]
            if aSplitPolygon is None or bSplitPolygon is None:
                plotPolygons(splitPolygons + [aSplitRepresentativeBlock.geometry, bSplitRepresentativeBlock.geometry])
                saveDataToFileWithDescription(data=self,
                                              censusYear='',
                                              stateName='',
                                              descriptionOfInfo='ErrorCase-AorBSplitIsNone')
                raise RuntimeError('Split a or b not found')

            if aSplitPolygon is bSplitPolygon:
                finishingBlocksToAvoid.append(energySeamFinishingBlock)
                continue

            if len(leftOverPolygons) is not len(splitPolygons) - 2:
                saveDataToFileWithDescription(data=self,
                                              censusYear='',
                                              stateName='',
                                              descriptionOfInfo='ErrorCase-MissingPolygons')
                raise RuntimeError('Missing some polygons for mapping. Split polygons: {0} Left over polygon: {1}'
                                   .format(len(splitPolygons), len(leftOverPolygons)))

            polygonSplits = (aSplitPolygon, bSplitPolygon)

            if shouldDrawGraph:
                plotPolygons(polygonSplits)

            if seamOnEdge:
                return SplitType.SplitIncludedInSeam, polygonSplits, None, energySeamStartingEnergy
            else:
                seamSplitPolygon = polygonFromMultiplePolygons(polygonList=[seamSplitPolygon] + leftOverPolygons)
                return SplitType.NormalSplit, polygonSplits, seamSplitPolygon, energySeamStartingEnergy
Пример #6
0
 def updateBlockContainerData(self):
     self.geometry = polygonFromMultipleGeometries(self.children)
     self.population = censusBlock.populationFromBlocks(self.children)