-
Notifications
You must be signed in to change notification settings - Fork 168
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
126 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from populartimes.crawler import cover_rect_with_cicles, rect_circle_collision | ||
import random | ||
from math import pi | ||
|
||
def generate_testcases(): | ||
for w in [0.1, 1, 2, 2.1, 5, 7, 10]: | ||
for h in [0.08, 0.1, 0.2, 1, 1.5, 5, 6, 8]: | ||
for r in [0.1, 0.6, 1, 2, 4, 5, 100]: | ||
circles = cover_rect_with_cicles(w, h, r) | ||
yield (w, h, r, circles) | ||
|
||
def calc_bounding_box(circles, r): | ||
xs = [c[0] for c in circles] | ||
ys = [c[1] for c in circles] | ||
return ((min(xs) - r, min(ys) - r), (max(xs) + r, max(ys) + r)) | ||
|
||
def test_cover_rect_with_cicles_all_in(): | ||
# test if calculated circles are all at least partly contained in the rect | ||
# (otherwise some circles would be superfluous) | ||
for w, h, r, circles in generate_testcases(): | ||
assert all([rect_circle_collision(0,w,0,h,c[0],c[1],r) for c in circles]) | ||
|
||
def test_cover_rect_with_cicles_coverage(): | ||
# test if circles fully cover the rect | ||
for w, h, r, circles in generate_testcases(): | ||
# test with 1000 random points | ||
for tst in range(1000): | ||
# choose random point within rect | ||
p = (random.uniform(0,w), random.uniform(0,h)) | ||
# check if point is contained in any of the calculated circles | ||
# (distance to center is <= radius) | ||
assert any([(p[0]-c[0])**2 + (p[1]-c[1])**2 <= r**2 for c in circles]) | ||
|
||
def test_cover_rect_with_cicles_area(): | ||
# test if area of returned circles is reaonable compared to rect area | ||
for w, h, r, circles in generate_testcases(): | ||
if w > 2*r and h > 2*r: | ||
# calculate bounding box | ||
lower_left, upper_right = calc_bounding_box(circles, r) | ||
|
||
area_bounding_box = (upper_right[0] - lower_left[0]) * (upper_right[1] - lower_left[1]) | ||
area_circ_total = len(circles) * r * r * pi | ||
area_rect = w * h | ||
|
||
# use Monte Carlo method to approximate combined circle area (union of all circles) | ||
# 1000 sample points should give about 99% accuracy | ||
points = [(random.uniform(lower_left[0], upper_right[0]), random.uniform(lower_left[1], upper_right[1])) for tst in range(1000)] | ||
inside = [any([(p[0]-c[0])**2 + (p[1]-c[1])**2 <= r**2 for c in circles]) for p in points] | ||
area_circ = inside.count(True) / len(inside) * area_bounding_box | ||
|
||
area_overlap = area_circ_total - area_circ | ||
|
||
ratio_circ_rect = area_circ / area_rect | ||
ratio_total = area_circ_total / area_rect | ||
ratio_overlap = area_overlap / area_circ_total | ||
|
||
if len(circles) > 1000: | ||
assert(ratio_circ_rect < 1.1) # max 10% outside of rect | ||
assert(ratio_total < 1.3) # max 30% overhead | ||
elif len(circles) > 100: | ||
assert(ratio_circ_rect < 1.3) # max 30% outside of rect | ||
assert(ratio_total < 1.5) # max 50% overhead | ||
assert(ratio_overlap < 0.2) # max 20% overlap |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,37 @@ | ||
from populartimes.crawler import get_circle_centers | ||
import random | ||
from geopy import Point | ||
from geopy.distance import vincenty, VincentyDistance | ||
|
||
|
||
def test_get_circle_centers(): | ||
circle_centers = get_circle_centers( | ||
b1=[48.132986, 11.566126], b2=[48.142199, 11.580047], radius=180 | ||
) | ||
def generate_testcases(): | ||
# origin (south-west) | ||
sw = [48.132986, 11.566126] | ||
# width, height, radius in meters | ||
for w in [1, 10, 80, 200, 1000, 5000]: | ||
for h in [1, 20, 300, 1000, 20000]: | ||
for r in [180, 500]: | ||
# north-east (se + width/height) | ||
ne = VincentyDistance(meters=w).destination( | ||
VincentyDistance(meters=h) | ||
.destination(point=sw, bearing=90), | ||
bearing=0 | ||
)[:2] | ||
circles = get_circle_centers(sw, ne, r) | ||
yield (sw, ne, w, h, r, circles) | ||
|
||
assert circle_centers == [ | ||
(48.13438792255016, 11.567335135224921), | ||
(48.137191779339744, 11.567335135224921), | ||
(48.139995634754655, 11.567335135224921), | ||
(48.14279948879493, 11.567335135224921), | ||
(48.13298589823778, 11.570962540893714), | ||
(48.135789755714754, 11.570962540893714), | ||
(48.13859361181704, 11.570962540893714), | ||
(48.14139746654468, 11.570962540893714), | ||
(48.13438761726354, 11.574589946541003), | ||
(48.13719147405328, 11.574589946541003), | ||
(48.139995329468334, 11.574589946541003), | ||
(48.14279918350877, 11.574589946541003), | ||
(48.13298536398607, 11.578217352150666), | ||
(48.13578922146331, 11.578217352150666), | ||
(48.138593077565865, 11.578217352150666), | ||
(48.14139693229376, 11.578217352150666), | ||
(48.13438685404702, 11.581844757706575), | ||
(48.13719071083713, 11.581844757706575), | ||
(48.139994566252575, 11.581844757706575), | ||
(48.14279842029337, 11.581844757706575) | ||
] | ||
def test_get_circle_centers(): | ||
# test if circles fully cover the rect | ||
for sw, ne, w, h, r, circles in generate_testcases(): | ||
# test with 1000 random points | ||
for tst in range(1000): | ||
# choose random point within rect | ||
p = (random.uniform(0,w), random.uniform(0,h)) | ||
# translate to lat/lng | ||
pp = VincentyDistance(meters=p[0]).destination( | ||
VincentyDistance(meters=p[1]) | ||
.destination(point=sw, bearing=90), | ||
bearing=0 | ||
) | ||
# check if point is contained in any of the calculated circles | ||
assert any([vincenty(pp, Point(c[0], c[1])).meters <= r for c in circles]) |