From 96347c9806535af12b739fd853492fc5ccb6d19e Mon Sep 17 00:00:00 2001 From: Markus Konrad Date: Fri, 29 Jan 2021 11:53:10 +0100 Subject: [PATCH] added brandenburg example, removed unnecessary is_simple tests --- examples/brandenburg.json | 7 ++ examples/random_points_across_italy.py | 12 -- examples/random_points_brandenburg.png | Bin 0 -> 44826 bytes examples/random_points_brandenburg.py | 85 +++++++++++++ geovoronoi/_voronoi.py | 22 +++- tests/test_main.py | 157 +++++++++++++++++-------- 6 files changed, 216 insertions(+), 67 deletions(-) create mode 100644 examples/brandenburg.json create mode 100644 examples/random_points_brandenburg.png create mode 100644 examples/random_points_brandenburg.py diff --git a/examples/brandenburg.json b/examples/brandenburg.json new file mode 100644 index 0000000..b386131 --- /dev/null +++ b/examples/brandenburg.json @@ -0,0 +1,7 @@ +{ +"type": "FeatureCollection", +"name": "Germany_AL4", +"features": [ +{ "type": "Feature", "properties": { "srid": "4326", "id": "62504", "name": "Brandenburg", "localname": "Brandenburg", "official_name": "", "boundary": "adminstrative", "admin_level": "4", "note": "", "wikidata": "Q1208", "wikipedia": "de:Brandenburg", "timestamp": "2018\/07\/19 01:55:20", "rpath": "62504,51477,0", "alltags": { "ref": "BB", "name": "Brandenburg", "name:am": "ብራንደንቡርግ", "name:an": "Brandemburgo", "name:ar": "براندنبورغ", "name:ay": "Brandenburg suyu", "name:az": "Brandenburq", "name:ba": "Бранденбург", "name:be": "Брандэнбург", "name:bg": "Бранденбург", "name:bn": "ব্রান্ডেনবুর্গ", "name:ca": "Brandenburg", "name:ce": "Бранденбург", "name:cs": "Braniborsko", "name:cv": "Бранденбург", "name:de": "Brandenburg", "name:el": "Βρανδεμβούργο", "name:eo": "Brandenburgio", "name:es": "Brandemburgo", "name:eu": "Brandenburgo", "name:fa": "براندنبورگ", "name:fr": "Brandebourg", "name:fy": "Brandenburch", "name:gl": "Brandeburgo", "name:gn": "Brandeburgo", "name:he": "ברנדנבורג", "name:hi": "ब्रैंडेनबर्ग", "name:hy": "Բրանդենբուրգ", "name:ia": "Brandeburgo", "name:ie": "Brandenburgia", "name:is": "Brandenborg", "name:it": "Brandeburgo", "name:ja": "ブランデンブルク州", "name:ka": "ბრანდენბურგი", "name:kk": "Бранденбург", "name:ko": "브란덴부르크 주", "name:la": "Brandenburgum", "name:lb": "Brandenburg", "name:li": "Brandeburg", "name:lt": "Brandenburgas", "name:lv": "Brandenburga", "name:mk": "Бранденбург", "name:mn": "Бранденбург", "name:mr": "ब्रांडेनबुर्ग", "name:ne": "ब्रान्डेनबर्ग", "name:nl": "Brandenburg", "name:oc": "Brandeborg", "name:os": "Бранденбург", "name:pa": "ਬ੍ਰਾਂਡਨਬੁਰਕ", "name:pl": "Brandenburgia", "name:ps": "براندنبورگ", "name:pt": "Brandemburgo", "name:ru": "Бранденбург", "name:sk": "Brandenbursko", "name:sq": "Brandenburgu", "name:sr": "Бранденбург", "name:th": "รัฐบรันเดินบวร์ค", "name:tl": "Brandeburgo", "name:uk": "Бранденбург", "name:ur": "برندنبرگ", "name:vo": "Brandänburgän", "name:yi": "בראנדנבורג", "name:zh": "勃兰登堡", "boundary": "administrative", "name:hsb": "Braniborska", "wikidata": "Q1208", "ISO3166-2": "DE-BB", "wikipedia": "de:Brandenburg", "admin_level": "4", "alt_name:cs": "Země Braniborsko", "border_type": "state", "name:prefix": "Bundesland", "de:regionalschluessel": "12", "TMC:cid_58:tabcd_1:Class": "Area", "TMC:cid_58:tabcd_1:LCLversion": "12.0", "TMC:cid_58:tabcd_1:LocationCode": "267", "de:amtlicher_gemeindeschluessel": "12" } }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 11.267612, 53.121972 ], [ 11.393711, 53.10984 ], [ 11.395417, 53.140179 ], [ 11.42153, 53.141106 ], [ 11.53464, 53.125218 ], [ 11.569435, 53.165502 ], [ 11.550948, 53.20823 ], [ 11.629009, 53.241997 ], [ 11.699607, 53.244091 ], [ 11.728497, 53.21695 ], [ 11.827851, 53.227466 ], [ 11.795949, 53.253312 ], [ 11.861657, 53.248446 ], [ 11.892566, 53.278955 ], [ 11.948175, 53.271419 ], [ 11.973662, 53.297528 ], [ 12.018622, 53.299422 ], [ 12.014026, 53.33423 ], [ 12.055753, 53.349153 ], [ 12.045029, 53.370756 ], [ 12.061792, 53.371256 ], [ 12.079668, 53.366857 ], [ 12.081924, 53.346194 ], [ 12.106835, 53.34353 ], [ 12.139637, 53.361395 ], [ 12.168702, 53.339156 ], [ 12.229785, 53.358135 ], [ 12.260042, 53.323897 ], [ 12.310277, 53.327242 ], [ 12.393245, 53.302036 ], [ 12.392042, 53.284889 ], [ 12.428612, 53.274005 ], [ 12.445349, 53.249652 ], [ 12.529791, 53.264621 ], [ 12.605645, 53.244335 ], [ 12.660175, 53.254649 ], [ 12.674905, 53.248638 ], [ 12.67066, 53.229694 ], [ 12.757725, 53.224094 ], [ 12.739304, 53.199295 ], [ 12.764253, 53.188972 ], [ 12.848072, 53.200874 ], [ 12.879718, 53.179359 ], [ 12.965443, 53.200246 ], [ 12.979795, 53.19081 ], [ 12.942102, 53.174467 ], [ 12.980444, 53.162734 ], [ 13.078718, 53.200462 ], [ 13.10653, 53.214958 ], [ 13.104125, 53.236278 ], [ 13.149808, 53.25066 ], [ 13.180433, 53.251228 ], [ 13.203488, 53.22126 ], [ 13.229785, 53.216611 ], [ 13.252443, 53.262103 ], [ 13.298454, 53.28097 ], [ 13.360916, 53.277759 ], [ 13.386071, 53.244784 ], [ 13.405712, 53.246824 ], [ 13.44252, 53.286519 ], [ 13.43781, 53.299583 ], [ 13.48359, 53.291228 ], [ 13.502451, 53.327389 ], [ 13.523603, 53.319917 ], [ 13.516355, 53.352135 ], [ 13.556168, 53.376039 ], [ 13.550068, 53.399166 ], [ 13.624767, 53.408976 ], [ 13.64059, 53.447112 ], [ 13.711784, 53.48137 ], [ 13.812113, 53.484135 ], [ 13.826096, 53.498866 ], [ 13.778005, 53.513742 ], [ 13.791149, 53.558362 ], [ 13.82185, 53.518072 ], [ 13.87974, 53.504012 ], [ 13.875533, 53.473292 ], [ 13.918138, 53.45541 ], [ 13.902906, 53.43181 ], [ 13.918155, 53.421914 ], [ 14.001263, 53.434914 ], [ 14.081912, 53.410847 ], [ 14.121929, 53.441704 ], [ 14.179376, 53.422526 ], [ 14.224816, 53.434292 ], [ 14.242334, 53.421925 ], [ 14.230912, 53.367779 ], [ 14.099028, 53.26147 ], [ 14.264627, 53.259103 ], [ 14.261981, 53.276502 ], [ 14.302061, 53.286208 ], [ 14.316346, 53.312279 ], [ 14.402259, 53.330064 ], [ 14.415453, 53.324377 ], [ 14.406803, 53.308729 ], [ 14.421205, 53.276137 ], [ 14.449841, 53.260722 ], [ 14.404364, 53.209488 ], [ 14.376247, 53.199131 ], [ 14.366777, 53.168451 ], [ 14.38642, 53.140675 ], [ 14.349985, 53.057392 ], [ 14.257073, 53.001979 ], [ 14.143936, 52.961109 ], [ 14.160814, 52.886771 ], [ 14.123005, 52.837765 ], [ 14.216389, 52.816773 ], [ 14.28029, 52.774268 ], [ 14.350431, 52.751291 ], [ 14.46523, 52.661411 ], [ 14.596211, 52.610643 ], [ 14.639014, 52.573317 ], [ 14.603838, 52.531031 ], [ 14.633926, 52.491484 ], [ 14.548115, 52.432337 ], [ 14.534376, 52.395 ], [ 14.584829, 52.306347 ], [ 14.575288, 52.289048 ], [ 14.689475, 52.256758 ], [ 14.71555, 52.236128 ], [ 14.685814, 52.193906 ], [ 14.705567, 52.168914 ], [ 14.680016, 52.143383 ], [ 14.681812, 52.11564 ], [ 14.759094, 52.065383 ], [ 14.748422, 52.031928 ], [ 14.714029, 52.003683 ], [ 14.721659, 51.994475 ], [ 14.704855, 51.976008 ], [ 14.721287, 51.951315 ], [ 14.694184, 51.901895 ], [ 14.611105, 51.857131 ], [ 14.590107, 51.821362 ], [ 14.645475, 51.795528 ], [ 14.668163, 51.725857 ], [ 14.7474, 51.675669 ], [ 14.76512, 51.60747 ], [ 14.729824, 51.581589 ], [ 14.696636, 51.596762 ], [ 14.70554, 51.576932 ], [ 14.674128, 51.550852 ], [ 14.609559, 51.550107 ], [ 14.599006, 51.571187 ], [ 14.567686, 51.581087 ], [ 14.516481, 51.55428 ], [ 14.387123, 51.541567 ], [ 14.332329, 51.504899 ], [ 14.273278, 51.532376 ], [ 14.136602, 51.543333 ], [ 14.142298, 51.522966 ], [ 14.108512, 51.521879 ], [ 14.073806, 51.492216 ], [ 14.08894, 51.477779 ], [ 14.032744, 51.475003 ], [ 14.062587, 51.445421 ], [ 14.036877, 51.434537 ], [ 14.044861, 51.419073 ], [ 14.015904, 51.404489 ], [ 14.028122, 51.396264 ], [ 14.003784, 51.395536 ], [ 14.000596, 51.372537 ], [ 13.970463, 51.375699 ], [ 13.972375, 51.393434 ], [ 13.955527, 51.397329 ], [ 13.882903, 51.374429 ], [ 13.83426, 51.383752 ], [ 13.763328, 51.359064 ], [ 13.767724, 51.371696 ], [ 13.599149, 51.368673 ], [ 13.570333, 51.385659 ], [ 13.542538, 51.369405 ], [ 13.523314, 51.380931 ], [ 13.520726, 51.403668 ], [ 13.462982, 51.412214 ], [ 13.475845, 51.418519 ], [ 13.446034, 51.430853 ], [ 13.421893, 51.421568 ], [ 13.428913, 51.431774 ], [ 13.400668, 51.454248 ], [ 13.374004, 51.438229 ], [ 13.378692, 51.425301 ], [ 13.320841, 51.437384 ], [ 13.328584, 51.426473 ], [ 13.285623, 51.410778 ], [ 13.286452, 51.399297 ], [ 13.260211, 51.401434 ], [ 13.268686, 51.384585 ], [ 13.216339, 51.395353 ], [ 13.201536, 51.431875 ], [ 13.174032, 51.428282 ], [ 13.20328, 51.451605 ], [ 13.182092, 51.49191 ], [ 13.201199, 51.491789 ], [ 13.208042, 51.524272 ], [ 13.185566, 51.557986 ], [ 13.142584, 51.567988 ], [ 13.154377, 51.600253 ], [ 13.120197, 51.619986 ], [ 13.08593, 51.608095 ], [ 13.050548, 51.647505 ], [ 13.154312, 51.686403 ], [ 13.154189, 51.710474 ], [ 13.186882, 51.715693 ], [ 13.152096, 51.744237 ], [ 13.163733, 51.75484 ], [ 13.14744, 51.766448 ], [ 13.17055, 51.787018 ], [ 13.123927, 51.85429 ], [ 13.150508, 51.859678 ], [ 13.149145, 51.872145 ], [ 13.118628, 51.883107 ], [ 13.040988, 51.870573 ], [ 13.026893, 51.880785 ], [ 13.045574, 51.900435 ], [ 12.973468, 51.900767 ], [ 12.976643, 51.920968 ], [ 12.955935, 51.922641 ], [ 12.960558, 51.934642 ], [ 12.852415, 51.935165 ], [ 12.844325, 51.967582 ], [ 12.776118, 51.964935 ], [ 12.756244, 51.986499 ], [ 12.668292, 52.01306 ], [ 12.597264, 51.98152 ], [ 12.539432, 51.984922 ], [ 12.535818, 52.002928 ], [ 12.494043, 52.011615 ], [ 12.480728, 52.033175 ], [ 12.429478, 52.018333 ], [ 12.276527, 52.103891 ], [ 12.216113, 52.170543 ], [ 12.247333, 52.183879 ], [ 12.248787, 52.211782 ], [ 12.282094, 52.216715 ], [ 12.296648, 52.22846 ], [ 12.24541, 52.249863 ], [ 12.262923, 52.295045 ], [ 12.30809, 52.344632 ], [ 12.284122, 52.364199 ], [ 12.306555, 52.377714 ], [ 12.291711, 52.386111 ], [ 12.302269, 52.405309 ], [ 12.27461, 52.416183 ], [ 12.297198, 52.423694 ], [ 12.289413, 52.430579 ], [ 12.330787, 52.478274 ], [ 12.308522, 52.479194 ], [ 12.329612, 52.496642 ], [ 12.271845, 52.487501 ], [ 12.257958, 52.518197 ], [ 12.23607, 52.523873 ], [ 12.221768, 52.500231 ], [ 12.184673, 52.495722 ], [ 12.167122, 52.514696 ], [ 12.18874, 52.532226 ], [ 12.142172, 52.528285 ], [ 12.18152, 52.575218 ], [ 12.166939, 52.627464 ], [ 12.23646, 52.628728 ], [ 12.23259, 52.688766 ], [ 12.197363, 52.718902 ], [ 12.222162, 52.739826 ], [ 12.205848, 52.76049 ], [ 12.220406, 52.78989 ], [ 12.244407, 52.786606 ], [ 12.25762, 52.80487 ], [ 12.233428, 52.859922 ], [ 12.197054, 52.877878 ], [ 12.123719, 52.853635 ], [ 12.125539, 52.89439 ], [ 12.112775, 52.875695 ], [ 12.022564, 52.89038 ], [ 11.979908, 52.876112 ], [ 11.83449, 52.910145 ], [ 11.823288, 52.92125 ], [ 11.846216, 52.95132 ], [ 11.788134, 52.960208 ], [ 11.743077, 52.987857 ], [ 11.692056, 52.979161 ], [ 11.676296, 53.008221 ], [ 11.627038, 53.011963 ], [ 11.637715, 53.039368 ], [ 11.510955, 53.047305 ], [ 11.446347, 53.078476 ], [ 11.340537, 53.054726 ], [ 11.273544, 53.098469 ], [ 11.267612, 53.121972 ] ], [ [ 13.08835, 52.41964 ], [ 13.138103, 52.397856 ], [ 13.12722, 52.391666 ], [ 13.13108, 52.387236 ], [ 13.245947, 52.421126 ], [ 13.249725, 52.404957 ], [ 13.296755, 52.41625 ], [ 13.312082, 52.399139 ], [ 13.34328, 52.411273 ], [ 13.388437, 52.377865 ], [ 13.420834, 52.376127 ], [ 13.41878, 52.409995 ], [ 13.463563, 52.421065 ], [ 13.479767, 52.395947 ], [ 13.53843, 52.400603 ], [ 13.5355, 52.38899 ], [ 13.59275, 52.39383 ], [ 13.605798, 52.37361 ], [ 13.642682, 52.377509 ], [ 13.63632, 52.346819 ], [ 13.64843, 52.338276 ], [ 13.699975, 52.375599 ], [ 13.686815, 52.385306 ], [ 13.739034, 52.407335 ], [ 13.740057, 52.432333 ], [ 13.729895, 52.433924 ], [ 13.726043, 52.435298 ], [ 13.723017, 52.436721 ], [ 13.723066, 52.437057 ], [ 13.76116, 52.43771 ], [ 13.701267, 52.468224 ], [ 13.704616, 52.454756 ], [ 13.648568, 52.478744 ], [ 13.611369, 52.470653 ], [ 13.65695, 52.529883 ], [ 13.625691, 52.530182 ], [ 13.63739, 52.54225 ], [ 13.586412, 52.549782 ], [ 13.581446, 52.571081 ], [ 13.508126, 52.592159 ], [ 13.497131, 52.606708 ], [ 13.52287, 52.644812 ], [ 13.489883, 52.655517 ], [ 13.47951, 52.67551 ], [ 13.450794, 52.662706 ], [ 13.469909, 52.651915 ], [ 13.424448, 52.635545 ], [ 13.394554, 52.647554 ], [ 13.357644, 52.622977 ], [ 13.302609, 52.62719 ], [ 13.310177, 52.65737 ], [ 13.282752, 52.660761 ], [ 13.283772, 52.641113 ], [ 13.262141, 52.64072 ], [ 13.264234, 52.626865 ], [ 13.22057, 52.628242 ], [ 13.201643, 52.606479 ], [ 13.217397, 52.587477 ], [ 13.164528, 52.598793 ], [ 13.128966, 52.587302 ], [ 13.153538, 52.57324 ], [ 13.145686, 52.552791 ], [ 13.130503, 52.556023 ], [ 13.117393, 52.517033 ], [ 13.168632, 52.509253 ], [ 13.117693, 52.47732 ], [ 13.109278, 52.450632 ], [ 13.123153, 52.438709 ], [ 13.08835, 52.41964 ] ], [ [ 13.503438, 52.618993 ], [ 13.505442, 52.619687 ], [ 13.505449, 52.619884 ], [ 13.503814, 52.619223 ], [ 13.503438, 52.618993 ] ] ], [ [ [ 12.265677, 52.231337 ], [ 12.272055, 52.231249 ], [ 12.275011, 52.230527 ], [ 12.272864, 52.228934 ], [ 12.265677, 52.231337 ] ] ], [ [ [ 12.282741, 52.227261 ], [ 12.283196, 52.228181 ], [ 12.286261, 52.227816 ], [ 12.285645, 52.227026 ], [ 12.282741, 52.227261 ] ] ], [ [ [ 12.276428, 52.227809 ], [ 12.278846, 52.22944 ], [ 12.282289, 52.228014 ], [ 12.280859, 52.227598 ], [ 12.276428, 52.227809 ] ] ], [ [ [ 12.286549, 52.225852 ], [ 12.286796, 52.226286 ], [ 12.290414, 52.225202 ], [ 12.290055, 52.22474 ], [ 12.286549, 52.225852 ] ] ], [ [ [ 11.267355, 53.12452 ], [ 11.269086, 53.12301 ], [ 11.268632, 53.12211 ], [ 11.267954, 53.122929 ], [ 11.267355, 53.12452 ] ] ], [ [ [ 12.218076, 52.86133 ], [ 12.221282, 52.863301 ], [ 12.222102, 52.862805 ], [ 12.219041, 52.861117 ], [ 12.218076, 52.86133 ] ] ], [ [ [ 12.260021, 52.229894 ], [ 12.260818, 52.230771 ], [ 12.26174, 52.230569 ], [ 12.26097, 52.229695 ], [ 12.260021, 52.229894 ] ] ] ] } } +] +} diff --git a/examples/random_points_across_italy.py b/examples/random_points_across_italy.py index 5a9ac0d..fb6df4e 100644 --- a/examples/random_points_across_italy.py +++ b/examples/random_points_across_italy.py @@ -17,18 +17,6 @@ from geovoronoi import coords_to_points, voronoi_regions_from_coords from geovoronoi.plotting import subplot_for_map, plot_voronoi_polys_with_points_in_area -import traceback -import warnings -import sys - -def warn_with_traceback(message, category, filename, lineno, file=None, line=None): - - log = file if hasattr(file,'write') else sys.stderr - traceback.print_stack(file=log) - log.write(warnings.formatwarning(message, category, filename, lineno, line)) - -warnings.showwarning = warn_with_traceback - logging.basicConfig(level=logging.INFO) geovoronoi_log = logging.getLogger('geovoronoi') geovoronoi_log.setLevel(logging.INFO) diff --git a/examples/random_points_brandenburg.png b/examples/random_points_brandenburg.png new file mode 100644 index 0000000000000000000000000000000000000000..b2999701a92ca1132a0ce5a18a3a5ac89a2b4e82 GIT binary patch literal 44826 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 +January 2021 +""" + + +import logging +from pprint import pprint + +import matplotlib.pyplot as plt +import numpy as np +import fiona +import pyproj +from shapely.geometry import shape +from shapely.ops import transform + +from geovoronoi import coords_to_points, voronoi_regions_from_coords +from geovoronoi.plotting import subplot_for_map, plot_voronoi_polys_with_points_in_area + +logging.basicConfig(level=logging.INFO) +geovoronoi_log = logging.getLogger('geovoronoi') +geovoronoi_log.setLevel(logging.INFO) +geovoronoi_log.propagate = True + +#%% + +INPUT_FILE = 'brandenburg.json' +N_POINTS = 20 + +np.random.seed(20210129) + +print('loading %s' % INPUT_FILE) + +with fiona.open(INPUT_FILE) as f: + src_crs = pyproj.CRS.from_dict(f.meta['crs']) + target_crs = pyproj.CRS.from_epsg(3395) # World Mercator CRS + print('source CRS:', src_crs) + print('target CRS:', target_crs) + crs_transformer = pyproj.Transformer.from_crs(src_crs, target_crs) + brandenburg = shape(f[0]['geometry']) + # note that we also apply ".buffer(0)", otherwise the shape is not valid + # see https://shapely.readthedocs.io/en/stable/manual.html#object.buffer on the "buffer(0)" trick + brandenburg = transform(crs_transformer.transform, brandenburg).buffer(0) + +#%% + +# generate some random points within the bounds +minx, miny, maxx, maxy = brandenburg.bounds + +randx = np.random.uniform(minx, maxx, N_POINTS) +randy = np.random.uniform(miny, maxy, N_POINTS) +coords = np.vstack((randx, randy)).T + +# use only the points inside the geographic area +pts = [p for p in coords_to_points(coords) if p.within(brandenburg)] # converts to shapely Point +del coords # not used any more + +print('will use %d of %d randomly generated points that are inside geographic area' % (len(pts), N_POINTS)) + +#%% + +# +# calculate the Voronoi regions, cut them with the geographic area shape and assign the points to them +# + +poly_shapes, poly_to_pt_assignments = voronoi_regions_from_coords(pts, brandenburg) + +print('Voronoi region to point assignments:') +pprint(poly_to_pt_assignments) + +#%% plotting + +fig, ax = subplot_for_map() + +plot_voronoi_polys_with_points_in_area(ax, brandenburg, poly_shapes, pts, poly_to_pt_assignments, + point_labels=list(map(str, range(len(pts))))) + +ax.set_title('%d random points and their Voronoi regions in Brandenburg' % len(pts)) + +plt.tight_layout() +plt.savefig('random_points_brandenburg.png') +plt.show() diff --git a/geovoronoi/_voronoi.py b/geovoronoi/_voronoi.py index e6378e0..36f101f 100644 --- a/geovoronoi/_voronoi.py +++ b/geovoronoi/_voronoi.py @@ -68,6 +68,10 @@ def voronoi_regions_from_coords(coords, geo_shape, per_geom=True, return_unassig if not isinstance(geo_shape, (Polygon, MultiPolygon)): raise ValueError('`geo_shape` must be a Polygon or MultiPolygon') + if not geo_shape.is_valid: + raise ValueError('`geo_shape` is not a valid shape; try applying `.buffer(b)` where `b` is zero ' + 'or a very small number') + if not per_geom or isinstance(geo_shape, Polygon): geoms = [geo_shape] else: # Multipolygon @@ -196,14 +200,14 @@ def region_polygons_from_voronoi(vor, geom, return_point_assignments=False): if not isinstance(p, Polygon): raise RuntimeError('generated convex hull is not a polygon') - if not p.is_valid or not p.is_simple or p.is_empty: - raise RuntimeError('generated polygon is not valid, not simple or empty') + if not p.is_valid or p.is_empty: + raise RuntimeError('generated polygon is not valid or is empty') if not geom.contains(p): p = p.intersection(geom) - if not p.is_valid or not p.is_simple or p.is_empty: - raise RuntimeError('generated polygon is not valid, not simple or empty after intersection with the' + if not p.is_valid or p.is_empty: + raise RuntimeError('generated polygon is not valid or is empty after intersection with the' ' surrounding geometry `geom`') covered_area += p.area @@ -253,13 +257,19 @@ def region_polygons_from_voronoi(vor, geom, return_point_assignments=False): def get_points_to_poly_assignments(poly_to_pt_assignments): """ - Reverse of poly to points assignments: Returns a list of size N, which is the number of unique points in - `poly_to_pt_assignments`. Each list element is an index into the list of Voronoi regions. + Reverse Voronoi region polygon IDs to point ID assignments by returning a dict that maps each point ID to its + Voronoi region polygon ID. All IDs should be integers. + + :param poly_to_pt_assignments: dict mapping Voronoi region polygon IDs to list of point IDs + :return: dict mapping point ID to Voronoi region polygon ID """ pt_to_poly = {} for poly, pts in poly_to_pt_assignments.items(): for pt in pts: + if pt in pt_to_poly.keys(): + raise ValueError('invalid assignments in `poly_to_pt_assignments`: ' + 'point %d is assigned to several polygons' % pt) pt_to_poly[pt] = poly return pt_to_poly diff --git a/tests/test_main.py b/tests/test_main.py index 18d8c75..4fe6c25 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -20,97 +20,108 @@ @given(coords=coords_2d_array()) def test_coords_to_points_and_points_to_coords(coords): + # test for bijectivity of points_to_coords and coords_to_points assert np.array_equal(points_to_coords(coords_to_points(coords)), coords) +@pytest.mark.parametrize( + 'poly_to_pts, expected', + [ + ({5: [1], 2: [3]}, {1: 5, 3: 2}), + ({5: [], 2: [3]}, {3: 2}), + ({5: [], 2: []}, {}), + ({1: [1]}, {1: 1}), + ({1: [4, 5]}, {4: 1, 5: 1}), + ({5: [1], 2: [1]}, None) + ] +) +def test_get_points_to_poly_assignments(poly_to_pts, expected): + if expected is None: + with pytest.raises(ValueError): + get_points_to_poly_assignments(poly_to_pts) + else: + assert get_points_to_poly_assignments(poly_to_pts) == expected + @given(available_points=st.permutations(list(range(10))), n_poly=st.integers(0, 10)) -def test_get_points_to_poly_assignments(available_points, n_poly): +def test_get_points_to_poly_assignments_hypothesis(available_points, n_poly): + # generate poly to point assignments n_pts = len(available_points) if n_poly == 0: - poly_to_pts = [] - elif n_poly == 10: - poly_to_pts = [[x] for x in available_points] - else: + poly_to_pts = {} + elif n_poly == 10: # one to one assignment + poly_to_pts = dict(zip(range(n_poly), [[x] for x in available_points])) + else: # one to N assignment (we have duplicate points) pts_per_poly = n_pts // n_poly - poly_to_pts = [] + poly_to_pts = {} n_assigned = 0 + # try to evenly distribute point IDs to polys for p in range(0, n_poly): - poly_to_pts.append([available_points[i] for i in range(p * pts_per_poly, (p+1) * pts_per_poly)]) + poly_to_pts[p] = [available_points[i] for i in range(p * pts_per_poly, (p+1) * pts_per_poly)] n_assigned += pts_per_poly + # fill up if n_assigned < n_pts: - poly_to_pts[-1].extend([available_points[i] for i in range(n_assigned, n_pts)]) + poly_to_pts[n_poly-1].extend([available_points[i] for i in range(n_assigned, n_pts)]) if n_poly > 0: - assert set(sum(poly_to_pts, [])) == set(available_points) + assert set(sum(list(poly_to_pts.values()), [])) == set(available_points) pts_to_poly = get_points_to_poly_assignments(poly_to_pts) - assert isinstance(pts_to_poly, list) + assert isinstance(pts_to_poly, dict) if n_poly == 0: assert len(pts_to_poly) == 0 else: assert len(pts_to_poly) == n_pts - assert all([0 <= i_poly < n_poly for i_poly in pts_to_poly]) - - -#%% tests against fixed issues - -def test_issue_7a(): - centroids = np.array([[537300, 213400], [538700, 213700], [536100, 213400]]) - polygon = Polygon([[540000, 214100], [535500, 213700], [535500, 213000], [539000, 213200]]) - poly_shapes, pts, poly_to_pt_assignments = voronoi_regions_from_coords(centroids, polygon) - - assert isinstance(poly_shapes, list) - assert 0 < len(poly_shapes) <= len(centroids) - assert all([isinstance(p, (Polygon, MultiPolygon)) for p in poly_shapes]) - - assert np.array_equal(points_to_coords(pts), centroids) - - assert isinstance(poly_to_pt_assignments, list) - assert len(poly_to_pt_assignments) == len(poly_shapes) - assert all([isinstance(assign, list) for assign in poly_to_pt_assignments]) - assert all([len(assign) == 1 for assign in poly_to_pt_assignments]) # in this case there is a 1:1 correspondance + assert set(list(pts_to_poly.keys())) == set(available_points) + assert set(list(pts_to_poly.values())) == set(list(range(n_poly))) +@pytest.mark.parametrize( + 'n_pts, per_geom, return_unassigned_pts', + [ + (100, True, False), + ] +) @pytest.mark.mpl_image_compare -def test_issue_7b(): - centroids = np.array([[496712, 232672], [497987, 235942], [496425, 230252], [497482, 234933], - [499331, 238351], [496081, 231033], [497090, 233846], [496755, 231645], - [498604, 237018]]) - polygon = Polygon([[495555, 230875], [496938, 235438], [499405, 239403], [499676, 239474], - [499733, 237877], [498863, 237792], [499120, 237335], [498321, 235010], - [497295, 233185], [497237, 231359], [496696, 229620], [495982, 230047], - [496154, 230347], [496154, 230347], [495555, 230875]]) - - poly_shapes, pts, poly_to_pt_assignments = voronoi_regions_from_coords(centroids, polygon) - - assert isinstance(poly_shapes, list) - assert 0 < len(poly_shapes) <= len(centroids) +def test_voronoi_regions_from_coords_italy(n_pts, per_geom, return_unassigned_pts): + area_shape = _get_country_shape('Italy') + coords = _rand_coords_in_shape(area_shape, n_pts) + n_pts = len(coords) # number of random points inside shape + poly_shapes, poly_to_pt_assignments = voronoi_regions_from_coords(coords, area_shape, + per_geom=per_geom, + return_unassigned_points=return_unassigned_pts) + + assert isinstance(poly_shapes, dict) + assert len(poly_shapes) == n_pts assert all([isinstance(p, (Polygon, MultiPolygon)) for p in poly_shapes]) - assert np.array_equal(points_to_coords(pts), centroids) + assert np.array_equal(points_to_coords(pts), coords) - assert isinstance(poly_to_pt_assignments, list) + assert isinstance(poly_to_pt_assignments, dict) assert len(poly_to_pt_assignments) == len(poly_shapes) assert all([isinstance(assign, list) for assign in poly_to_pt_assignments]) assert all([len(assign) == 1 for assign in poly_to_pt_assignments]) # in this case there is a 1:1 correspondance fig, ax = subplot_for_map() - plot_voronoi_polys_with_points_in_area(ax, polygon, poly_shapes, centroids, poly_to_pt_assignments) + plot_voronoi_polys_with_points_in_area(ax, area_shape, poly_shapes, coords, poly_to_pt_assignments) return fig + #%% realistic full tests with plotting +@pytest.mark.parametrize( + 'n_pts', [5, 10, 20, 50, 100, 105, 1000] +) @pytest.mark.mpl_image_compare -def test_voronoi_italy_with_plot(): +def test_voronoi_italy_with_plot(n_pts): area_shape = _get_country_shape('Italy') - coords = _rand_coords_in_shape(area_shape, 100) - poly_shapes, pts, poly_to_pt_assignments = voronoi_regions_from_coords(coords, area_shape) + coords = _rand_coords_in_shape(area_shape, n_pts) + poly_shapes, poly_to_pt_assignments = voronoi_regions_from_coords(coords, area_shape) assert isinstance(poly_shapes, list) assert 0 < len(poly_shapes) <= 100 @@ -246,6 +257,54 @@ def test_voronoi_sweden_duplicate_points_with_plot(): return fig +#%% tests against fixed issues + +def test_issue_7a(): + centroids = np.array([[537300, 213400], [538700, 213700], [536100, 213400]]) + polygon = Polygon([[540000, 214100], [535500, 213700], [535500, 213000], [539000, 213200]]) + poly_shapes, pts, poly_to_pt_assignments = voronoi_regions_from_coords(centroids, polygon) + + assert isinstance(poly_shapes, list) + assert 0 < len(poly_shapes) <= len(centroids) + assert all([isinstance(p, (Polygon, MultiPolygon)) for p in poly_shapes]) + + assert np.array_equal(points_to_coords(pts), centroids) + + assert isinstance(poly_to_pt_assignments, list) + assert len(poly_to_pt_assignments) == len(poly_shapes) + assert all([isinstance(assign, list) for assign in poly_to_pt_assignments]) + assert all([len(assign) == 1 for assign in poly_to_pt_assignments]) # in this case there is a 1:1 correspondance + + +@pytest.mark.mpl_image_compare +def test_issue_7b(): + centroids = np.array([[496712, 232672], [497987, 235942], [496425, 230252], [497482, 234933], + [499331, 238351], [496081, 231033], [497090, 233846], [496755, 231645], + [498604, 237018]]) + polygon = Polygon([[495555, 230875], [496938, 235438], [499405, 239403], [499676, 239474], + [499733, 237877], [498863, 237792], [499120, 237335], [498321, 235010], + [497295, 233185], [497237, 231359], [496696, 229620], [495982, 230047], + [496154, 230347], [496154, 230347], [495555, 230875]]) + + poly_shapes, pts, poly_to_pt_assignments = voronoi_regions_from_coords(centroids, polygon) + + assert isinstance(poly_shapes, list) + assert 0 < len(poly_shapes) <= len(centroids) + assert all([isinstance(p, (Polygon, MultiPolygon)) for p in poly_shapes]) + + assert np.array_equal(points_to_coords(pts), centroids) + + assert isinstance(poly_to_pt_assignments, list) + assert len(poly_to_pt_assignments) == len(poly_shapes) + assert all([isinstance(assign, list) for assign in poly_to_pt_assignments]) + assert all([len(assign) == 1 for assign in poly_to_pt_assignments]) # in this case there is a 1:1 correspondance + + fig, ax = subplot_for_map() + plot_voronoi_polys_with_points_in_area(ax, polygon, poly_shapes, centroids, poly_to_pt_assignments) + + return fig + + #%% a few helper functions def _get_country_shape(country):