diff --git a/README.md b/README.md index e4bff8a..73ad111 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ Markus Konrad , February 2021 ![Voronoi regions of random points across Spain and their respective area](https://raw.githubusercontent.com/WZBSocialScienceCenter/geovoronoi/master/examples/random_points_and_area.png) -*geovoronoi* helps generating [Voronoi regions](https://en.wikipedia.org/wiki/Voronoi_diagram) for geographic data, for example coordinates of public universities in a certain country. This in turn may be used to estimate some kind of "coverage". +*geovoronoi* helps generating [Voronoi regions](https://en.wikipedia.org/wiki/Voronoi_diagram) for geographic data, for example coordinates of public universities in a certain country. This in turn may be used to estimate some kind of "coverage". The usage is not confined to geographic data, though. This package allows you to generate finite Voronoi regions inside any valid surrounding polygonal shape. -It takes a list of coordinates and calculates the Voronoi regions from them using [SciPy](https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.Voronoi.html#scipy.spatial.Voronoi). At the edges, these regions go to infinity. We can then take the shape of the surrounding area (e.g. the shape of a country as polygon) to cut the Voronoi regions so that they fit into the provided shape, making the regions at the edges finite. *geovoronoi* uses [shapely](http://toblerity.org/shapely/) for these operations. The package furthermore implements some functions for easy plotting of the resulting Voronoi regions. +The main function of this package, `voronoi_regions_from_coords()`, takes a list of coordinates and calculates the Voronoi regions from them using [SciPy](https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.Voronoi.html#scipy.spatial.Voronoi). At the edges, these regions go to infinity. We can then take the shape of the surrounding area (e.g. the shape of a country as polygon) to cut the Voronoi regions so that they fit into the provided shape, making the regions at the edges finite. *geovoronoi* uses [shapely](http://toblerity.org/shapely/) for these operations. The package furthermore implements some functions for easy plotting of the resulting Voronoi regions. ## Installation diff --git a/examples/duplicate_points.png b/examples/duplicate_points.png index c750757..ef5cdce 100644 Binary files a/examples/duplicate_points.png and b/examples/duplicate_points.png differ diff --git a/examples/random_points_brandenburg.png b/examples/random_points_brandenburg.png index 10fe913..b916499 100644 Binary files a/examples/random_points_brandenburg.png and b/examples/random_points_brandenburg.png differ diff --git a/geovoronoi/__init__.py b/geovoronoi/__init__.py index b4d55c8..f2d511b 100644 --- a/geovoronoi/__init__.py +++ b/geovoronoi/__init__.py @@ -14,6 +14,6 @@ __title__ = 'geovoronoi' -__version__ = '0.3.0dev' +__version__ = '0.3.0' __author__ = 'Markus Konrad' __license__ = 'Apache License 2.0' diff --git a/geovoronoi/_voronoi.py b/geovoronoi/_voronoi.py index 034191a..70bda96 100644 --- a/geovoronoi/_voronoi.py +++ b/geovoronoi/_voronoi.py @@ -167,7 +167,10 @@ def voronoi_regions_from_coords(coords, geo_shape, per_geom=True, return_unassig # iterate through sub-geometries in `geo_shape` for i_geom, geom in enumerate(geoms): # get point indices of points that lie within `geom` - pts_in_geom = [i for i in pts_indices if geom.contains(pts[i])] + if len(geoms) == 1 or i_geom == len(geoms) - 1: + pts_in_geom = list(pts_indices) # no need to check if we only have one geom or only one geom left + else: + pts_in_geom = [i for i in pts_indices if geom.contains(pts[i])] # start with empty data for this sub-geometry geom_region_polys[i_geom] = {} @@ -179,7 +182,8 @@ def voronoi_regions_from_coords(coords, geo_shape, per_geom=True, return_unassig continue # remove the points that we're about to use (point - geometry assignment is bijective) - pts_indices.difference_update(pts_in_geom) + if i_geom < len(geoms) - 1: # no need to do this on last iteration + pts_indices.difference_update(pts_in_geom) # generate the Voronoi regions using the SciPy Voronoi class logger.info('generating Voronoi regions') diff --git a/geovoronoi/plotting.py b/geovoronoi/plotting.py index 856fac2..21c6c7e 100644 --- a/geovoronoi/plotting.py +++ b/geovoronoi/plotting.py @@ -253,7 +253,7 @@ def plot_voronoi_polys_with_points_in_area(ax, area_shape, region_polys, points, as `plot_area_opts`, `plot_voronoi_opts` or `plot_points_opts` respectively. :param ax: matplotlib Axes object to plot on - :param area_shape: geographic shape surrounding the Voronoi regions + :param area_shape: geographic shape surrounding the Voronoi regions; can be None to disable plotting of geogr. shape :param region_polys: dict mapping region IDs to Voronoi region geometries :param points: NumPy array or list of Shapely Point objects :param region_pts: dict mapping Voronoi region IDs to point indices of `points` @@ -281,10 +281,11 @@ def plot_voronoi_polys_with_points_in_area(ax, area_shape, region_polys, points, plot_voronoi_opts = plot_voronoi_opts or {'alpha': 0.5} plot_points_opts = plot_points_opts or {} - plot_polygon_collection_with_color(ax, [area_shape], color=area_color, edgecolor=area_edgecolor, **plot_area_opts) + if area_shape is not None: + plot_polygon_collection_with_color(ax, [area_shape], color=area_color, edgecolor=area_edgecolor, + **plot_area_opts) - if voronoi_and_points_cmap and region_pts and \ - not all(map(bool, (voronoi_color, voronoi_edgecolor, points_color))): + if voronoi_and_points_cmap and region_pts and (voronoi_color is None or points_color is None): voronoi_color, points_color = colors_for_voronoi_polys_and_points(region_polys, region_pts, point_indices=list(range(len(points))), cmap_name=voronoi_and_points_cmap) diff --git a/setup.py b/setup.py index d62d27c..0f8733c 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ GITHUB_URL = 'https://github.com/WZBSocialScienceCenter/geovoronoi' __title__ = 'geovoronoi' -__version__ = '0.3.0dev' +__version__ = '0.3.0' __author__ = 'Markus Konrad' __license__ = 'Apache License 2.0'