diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..b71cfbb --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: python +python: + - "3.6" + - "3.7-dev" # 3.7 development branch +before_install: + - cd fields_overlap +# command to install dependencies +install: + - pip install -r requirements.txt +# command to run tests +script: + - pytest diff --git a/fields_overlap/overlap.py b/fields_overlap/overlap.py new file mode 100644 index 0000000..b024be3 --- /dev/null +++ b/fields_overlap/overlap.py @@ -0,0 +1,65 @@ +import matplotlib.pyplot as plt +from matplotlib.path import Path +import matplotlib.patches as patches + +def show_fields(field1, field2): + def vertices(left, bottom, right, top): + verts = [(left, bottom), (left, top), (right, top), (right, bottom), (left, bottom)] + return verts + + codes = [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY] + path1 = Path(vertices(*field1), codes) + path2 = Path(vertices(*field2), codes) + fig = plt.figure() + ax = fig.add_subplot(111) + patch1 = patches.PathPatch(path1, facecolor='orange', lw=2) + patch2 = patches.PathPatch(path2, facecolor='blue', lw=2) + ax.add_patch(patch1) + ax.add_patch(patch2) + ax.set_xlim(0, max(*field1, *field2) + 1) + ax.set_ylim(0, max(*field1, *field2) + 1) + fig.show() + + +def overlap_area(field1, field2): + ''' + Calculates the area of overlapping fields from the coordinates + of their corners. + + parameters + ---------- + field1: (tuple | list) of (int | float) + Coordinates of the first field. Order should be: (left, bottom, right, top) + + field2: (tuple | list) of (int | float) + Coordinates of the second field. Order should be: (left, bottom, right, top) + + Returns + ------- + int or float + Area in the coordinates entered unit. + + Example + ------- + >>> from overlap import overlap_area + >>> field_a = (1, 1, 4, 4) # position in kms as (x_0, y_0, x_1, y_1) + >>> field_b = (2, 2, 3, 3) # smaller field inside field_a + >>> overlap_area(field_a, field_b) + 1 + + ''' + + left1, bottom1, right1, top1 = field1 + left2, bottom2, right2, top2 = field2 + + if (left1 > right1 or bottom1 > top1 or + left2 > right2 or bottom2 > top2): + raise ValueError(" Coordinates need to be entered (left, bottom, right, top)") + + overlap_left = max(left1, left2) + overlap_bottom = max(bottom1, bottom2) + overlap_right = min(right1, right2) + overlap_top = min(top1, top2) + overlap_height = max(0, overlap_top - overlap_bottom) + overlap_width = max(0, overlap_right - overlap_left) + return overlap_height * overlap_width diff --git a/fields_overlap/pytest.ini b/fields_overlap/pytest.ini new file mode 100644 index 0000000..446b3ff --- /dev/null +++ b/fields_overlap/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +addopts = --cov=overlap --cov-report html --doctest-modules diff --git a/fields_overlap/requirements.txt b/fields_overlap/requirements.txt new file mode 100644 index 0000000..5e9b7af --- /dev/null +++ b/fields_overlap/requirements.txt @@ -0,0 +1,3 @@ +pytest==3.2.0 +pytest-cov==2.6.0 +matplotlib==2.0.2 diff --git a/fields_overlap/test_overlap.py b/fields_overlap/test_overlap.py new file mode 100644 index 0000000..a5811cd --- /dev/null +++ b/fields_overlap/test_overlap.py @@ -0,0 +1,80 @@ +from pytest import approx, raises + +from overlap import overlap_area + +def test_basic(): + ''' Tests that basic example works ''' + big_field = (1, 1, 4, 4) + inner_field = (2, 2, 3, 3) + assert overlap_area(big_field, inner_field) == 1 + + +def test_partial_overlap(): + ''' Tests when there's a partial overlap''' + base_field = (1, 1, 4, 3) + over_field = (2, 2, 3, 4) + assert overlap_area(base_field, over_field) == 1 + + +def test_corner_overlap(): + ''' Test when there is a box in a corner''' + base_field = (1, 0, 3, 5) + over_field = (2, 4, 4, 6) + assert overlap_area(base_field, over_field) == 1 + + +def test_edge_touching(): + ''' Test when there is an edge ''' + base_field = (1, 1, 4, 4) + over_field = (2, 2, 3, 4) + assert overlap_area(base_field, over_field) == 2 + + +def test_2opposite_edge_touching(): + ''' Test when there is an edge ''' + base_field = (1, 1, 4, 4) + over_field = (2, 1, 3, 4) + assert overlap_area(base_field, over_field) == 3 + + +def test_outside_edge_touching(): + ''' Test when they are touching on the outside ''' + base_field = (1, 1, 4, 4) + over_field = (2, 4, 3, 5) + assert overlap_area(base_field, over_field) == 0 + + +def test_no_overlap(): + ''' Test when they are not touching each other ''' + base_field = (0, 0, 3, 3) + over_field = (4, 4, 5, 5) + assert overlap_area(base_field, over_field) == 0 + + +def test_floats(): + ''' Test that still works when using floats ''' + base_field = (1, 1., 3.5, 3.5) + over_field = (3, 3, 5, 5) + assert overlap_area(base_field, over_field) == 0.5 * 0.5 + + +def test_floats_again(): + ''' Test that still works when using floats ''' + base_field = (1, 1., 3.3, 3.1) + over_field = (3, 3, 5, 5) + assert overlap_area(base_field, over_field) == approx(0.3 * 0.1, rel=1e-3) + + +def test_negative_basic(): + ''' Tests that basic example works ''' + big_field = (-1, -1, -4, -4) + inner_field = (-2, -2, -3, -3) + with raises(ValueError, message=" Coordinates need to be entered (left, bottom, right, top) "): + overlap_area(big_field, inner_field) + + +def test_negative_basic2(): + ''' Tests that basic example works ''' + big_field = (-1, -1, 1, 1) + inner_field = (0, -2, 1, 2) + assert overlap_area(big_field, inner_field) == 2