Skip to content

Commit

Permalink
Merge pull request #93 from mggg/2.0.0
Browse files Browse the repository at this point in the history
Everything updated for version 2.0.0
  • Loading branch information
peterrrock2 authored Dec 7, 2023
2 parents c5d8dd0 + 8c64aff commit 0508cc5
Show file tree
Hide file tree
Showing 66 changed files with 14,431 additions and 481 deletions.
197 changes: 139 additions & 58 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
author = 'Max Hully, Max Fan'

# The full version, including alpha/beta/rc tags
release = '1.1.0'
release = '2.0.0'


# -- General configuration ---------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.cpg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ISO-8859-1
Binary file not shown.
1 change: 1 addition & 0 deletions examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.prj
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PROJCS["NAD_1983_StatePlane_Colorado_Central_FIPS_0502_Feet",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",3000000.0],PARAMETER["False_Northing",1000000.0],PARAMETER["Central_Meridian",-105.5],PARAMETER["Standard_Parallel_1",39.75],PARAMETER["Standard_Parallel_2",38.45],PARAMETER["Latitude_Of_Origin",37.8333333333333],UNIT["US survey foot",0.304800609601219]]
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ISO-8859-1
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PROJCS["NAD_1983_StatePlane_Colorado_Central_FIPS_0502_Feet",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",3000000.0],PARAMETER["False_Northing",1000000.0],PARAMETER["Central_Meridian",-105.5],PARAMETER["Standard_Parallel_1",39.75],PARAMETER["Standard_Parallel_2",38.45],PARAMETER["Latitude_Of_Origin",37.8333333333333],UNIT["US survey foot",0.304800609601219]]
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ISO-8859-1
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PROJCS["NAD_1983_StatePlane_Colorado_Central_FIPS_0502_Feet",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",3000000.0],PARAMETER["False_Northing",1000000.0],PARAMETER["Central_Meridian",-105.5],PARAMETER["Standard_Parallel_1",39.75],PARAMETER["Standard_Parallel_2",38.45],PARAMETER["Latitude_Of_Origin",37.8333333333333],UNIT["US survey foot",0.304800609601219]]
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ISO-8859-1
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PROJCS["NAD_1983_StatePlane_Colorado_Central_FIPS_0502_Feet",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",3000000.0],PARAMETER["False_Northing",1000000.0],PARAMETER["Central_Meridian",-105.5],PARAMETER["Standard_Parallel_1",39.75],PARAMETER["Standard_Parallel_2",38.45],PARAMETER["Latitude_Of_Origin",37.8333333333333],UNIT["US survey foot",0.304800609601219]]
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ISO-8859-1
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PROJCS["NAD_1983_StatePlane_Colorado_Central_FIPS_0502_Feet",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",3000000.0],PARAMETER["False_Northing",1000000.0],PARAMETER["Central_Meridian",-105.5],PARAMETER["Standard_Parallel_1",39.75],PARAMETER["Standard_Parallel_2",38.45],PARAMETER["Latitude_Of_Origin",37.8333333333333],UNIT["US survey foot",0.304800609601219]]
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ISO-8859-1
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PROJCS["NAD_1983_StatePlane_Colorado_Central_FIPS_0502_Feet",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",3000000.0],PARAMETER["False_Northing",1000000.0],PARAMETER["Central_Meridian",-105.5],PARAMETER["Standard_Parallel_1",39.75],PARAMETER["Standard_Parallel_2",38.45],PARAMETER["Latitude_Of_Origin",37.8333333333333],UNIT["US survey foot",0.304800609601219]]
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ISO-8859-1
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PROJCS["NAD_1983_StatePlane_Colorado_Central_FIPS_0502_Feet",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",3000000.0],PARAMETER["False_Northing",1000000.0],PARAMETER["Central_Meridian",-105.5],PARAMETER["Standard_Parallel_1",39.75],PARAMETER["Standard_Parallel_2",38.45],PARAMETER["Latitude_Of_Origin",37.8333333333333],UNIT["US survey foot",0.304800609601219]]
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions examples/Shapefiles/bad_gap_region/bad_gap_region.cpg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ISO-8859-1
Binary file not shown.
1 change: 1 addition & 0 deletions examples/Shapefiles/bad_gap_region/bad_gap_region.prj
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PROJCS["NAD_1983_StatePlane_Colorado_Central_FIPS_0502_Feet",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",3000000.0],PARAMETER["False_Northing",1000000.0],PARAMETER["Central_Meridian",-105.5],PARAMETER["Standard_Parallel_1",39.75],PARAMETER["Standard_Parallel_2",38.45],PARAMETER["Latitude_Of_Origin",37.8333333333333],UNIT["US survey foot",0.304800609601219]]
Binary file not shown.
Binary file not shown.
Binary file added examples/toy_counties.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/toy_precincts.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/toy_precincts_corner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/toy_precincts_corner_repaired.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/toy_precincts_repaired.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/toy_precincts_repaired_county_aware.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 7 additions & 5 deletions maup/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
from .assign import assign
from .indexed_geometries import IndexedGeometries
from .intersections import intersections, prorate
from .repair import close_gaps, autorepair, snap_to_grid, crop_to, doctor, resolve_overlaps
from .repair import close_gaps, resolve_overlaps, quick_repair, snap_to_grid, crop_to, expand_to, doctor
from .smart_repair import smart_repair
from .normalize import normalize
from .progress_bar import progress


# warn about https://github.com/geopandas/geopandas/issues/2199
if geopandas.options.use_pygeos:
raise ImportError(
Expand All @@ -16,19 +16,21 @@
"`geopandas.options.use_pygeos = False` before importing your shapefile."
)

__version__ = "1.1.3"
__version__ = "2.0.0"
__all__ = [
"adjacencies",
"assign",
"IndexedGeometries",
"intersections",
"prorate",
"close_gaps",
"autorepair",
"resolve_overlaps",
"quick_repair",
"snap_to_grid",
"crop_to",
"expand_to",
"doctor",
"smart_repair",
"normalize",
"progress"
]
]
18 changes: 9 additions & 9 deletions maup/adjacencies.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import warnings

from geopandas import GeoSeries, GeoDataFrame

from shapely import make_valid
from shapely import make_valid

from .indexed_geometries import IndexedGeometries, get_geometries
from .progress_bar import progress
Expand All @@ -26,12 +25,13 @@ def iter_adjacencies(geometries):
for j, inter in inters.items():
yield (i, j), inter


def adjacencies(
geometries,
adjacency_type="rook",
output_type="geoseries", *, warn_for_overlaps=True, warn_for_islands=True
):
"""Returns adjacencies between geometries.
"""Returns adjacencies between geometries.
The default return type is a
`GeoSeries` with a `MultiIndex`, whose (i, j)th entry is the pairwise
intersection between geometry `i` and geometry `j`. We ensure that
Expand All @@ -48,20 +48,20 @@ def adjacencies(
orig_crs = geometries.crs
geometries = get_geometries(geometries)
geometries = make_valid(geometries)

adjs = list(iter_adjacencies(geometries))
if adjs:
index, geoms = zip(*adjs)
else:
index, geoms = [[],[]]
index, geoms = [[], []]

if output_type == "geodataframe":
inters = GeoDataFrame({"neighbors" : index, "geometry" : geoms})
inters = GeoDataFrame({"neighbors" : index, "geometry" : geoms}, crs = geometries.crs)
else:
inters = GeoSeries(geoms, index=index)
inters = GeoSeries(geoms, index=index, crs=geometries.crs)

if adjacency_type == "rook":
inters = inters[inters.length > 0].copy()
inters = inters[inters.length > 0]

if warn_for_overlaps:
overlaps = inters[inters.area > 0]
Expand Down
2 changes: 1 addition & 1 deletion maup/assign.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def assign(sources, targets):
dtype="float"
)
assignment.update(assignments_by_area)

# TODO: add a warning here if there are still unassigned source geometries.
return assignment.astype(targets.index.dtype, errors="ignore")

Expand Down
47 changes: 20 additions & 27 deletions maup/indexed_geometries.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import pandas
import geopandas
# Added numpy import to handle output of STRtree query
import numpy
import warnings

from shapely.prepared import prep
from shapely.strtree import STRtree
Expand All @@ -21,8 +19,7 @@ def __init__(self, geometries):
self.spatial_index = STRtree(self.geometries)
self.index = self.geometries.index


def query(self, geometry):
def query(self, geometry):
# IMPORTANT: When "geometry" is multi-part, this query will return a
# (2 x n) array instead of a (1 x n) array, so it's safest to flatten the query
# output before proceeding.
Expand All @@ -31,12 +28,12 @@ def query(self, geometry):
relevant_geometries = self.geometries.iloc[relevant_indices]
return relevant_geometries

def intersections(self, geometry):
relevant_geometries = self.query(geometry)
def intersections(self, geometry):
relevant_geometries = self.query(geometry)
intersections = relevant_geometries.intersection(geometry)
return intersections[-(intersections.is_empty | intersections.isna())]

def covered_by(self, container):
def covered_by(self, container):
relevant_geometries = self.query(container)
prepared_container = prep(container)

Expand All @@ -46,37 +43,33 @@ def covered_by(self, container):
selected_geometries = relevant_geometries.apply(prepared_container.covers)
return relevant_geometries[selected_geometries]

def assign(self, targets):
def assign(self, targets):
target_geometries = get_geometries(targets)
groups = [
self.covered_by(container).apply(lambda x: container_index)
for container_index, container in progress(
target_geometries.items(), len(target_geometries)
)
]
# New in pandas 2.1.2: Only concatenate Series of positive length
groups = [group for group in groups if len(group) > 0]
if groups:
# New in pandas 2.1.2: Only concatenate Series of positive length
groups = [group for group in groups if len(group) > 0]
if groups:
groups_concat = pandas.concat(groups)
# New in pandas 2.1.2: No reindexing allowed with a non-unique Index,
# so we need to find and remove repetitions. (This only happens when the
# targets have overlaps and some source is completely covered by more
# than one target.)
# Any that get removed here will be randomly assigned to one of the
# covering units at the assign_by_area step ub maup.assign.
groups_concat_index_list = list(groups_concat.index)
seen = set()
bad_indices = list(set([x for x in groups_concat_index_list if x in seen or seen.add(x)]))
if len(bad_indices)>0:
groups_concat = groups_concat.drop(bad_indices)
return groups_concat.reindex(self.index)
else:
return geopandas.GeoSeries().reindex(self.index)
groups_concat = pandas.concat(groups)
# New in pandas 2.1.2: No reindexing allowed with a non-unique Index,
# so we need to find and remove repetitions. (This only happens when the
# targets have overlaps and some source is completely covered by more
# than one target.)
# Any that get removed here will be randomly assigned to one of the
# covering units at the assign_by_area step ub maup.assign.
groups_concat_index_list = list(groups_concat.index)
seen = set()
bad_indices = list(set([x for x in groups_concat_index_list if x in seen or seen.add(x)]))
if len(bad_indices) > 0:
groups_concat = groups_concat.drop(bad_indices)
return groups_concat.reindex(self.index)
else:
return geopandas.GeoSeries().reindex(self.index)


def enumerate_intersections(self, targets):
target_geometries = get_geometries(targets)
for i, target in progress(target_geometries.items(), len(target_geometries)):
Expand Down
27 changes: 9 additions & 18 deletions maup/intersections.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,26 @@


@require_same_crs

def intersections(sources, targets, output_type="geoseries", area_cutoff=None):
"""
Computes all of the nonempty intersections between two sets of geometries.
By default, the returned `~geopandas.GeoSeries` will have a MultiIndex, where the
geometry at index *(i, j)* is the intersection of ``sources[i]`` and ``targets[j]``
(if it is not empty).
"""Computes all of the nonempty intersections between two sets of geometries.
By default, the returned `~geopandas.GeoSeries` will have a MultiIndex, where the
geometry at index *(i, j)* is the intersection of ``sources[i]`` and ``targets[j]``
(if it is not empty).
If output_type == "geodataframe", the return type is a range-indexed GeoDataFrame
with "source" and "target" columns containing the indices i,j, respectively, for the
intersection of ``sources[i]`` and ``targets[j]``.
with "source" and "target" columns containing the indices i,j, respectively, for the
intersection of ``sources[i]`` and ``targets[j]``
:param sources: geometries
:type sources: :class:`~geopandas.GeoSeries` or :class:`~geopandas.GeoDataFrame`
:param targets: geometries
:type targets: :class:`~geopandas.GeoSeries` or :class:`~geopandas.GeoDataFrame`
:param output_type: type of output, "geoseries" or "geodataframe"
:type output_type: str
:rtype: :class:`~geopandas.GeoSeries`
:param area_cutoff: (optional) if provided, only return intersections with
area greater than ``area_cutoff``
:type area_cutoff: Number or None
:rtype: :class:`~geopandas.GeoSeries` or :class:`~geopandas.GeoDataFrame`
"""


reindexed_sources = get_geometries_with_range_index(sources)
reindexed_targets = get_geometries_with_range_index(targets)

spatially_indexed_sources = IndexedGeometries(reindexed_sources)

records = [
Expand All @@ -45,11 +36,11 @@ def intersections(sources, targets, output_type="geoseries", area_cutoff=None):
reindexed_targets
)
]

df = GeoDataFrame.from_records(records, columns=["source", "target", "geometry"])
df = df.sort_values(by=["source", "target"]).reset_index(drop=True)
df.crs = sources.crs

geometries = df.set_index(["source", "target"]).geometry
geometries.sort_index(inplace=True)
geometries.crs = sources.crs
Expand Down
Loading

0 comments on commit 0508cc5

Please sign in to comment.