diff --git a/.gitignore b/.gitignore index 1dbc687..2455c93 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Ignore the temp folder +tmp/ + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/PointPatternAnalysis.ipynb b/PointPatternAnalysis.ipynb new file mode 100644 index 0000000..a07d614 --- /dev/null +++ b/PointPatternAnalysis.ipynb @@ -0,0 +1,159 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The entire dataset has an average nearest neighbor distance of 0.000240361352928\n", + "The entire dataset is significant\n", + "\n", + "Elements that occurred on 9/13/2014 have an average nn distance of 0.00256619518064\n", + "The date 9/13/2014 is significant\n", + "\n", + "Elements involving animal bites have an average nn distance of 0.0126135423802\n", + "The animal bite issue is significant\n" + ] + }, + { + "data": { + "image/png": "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\nuPP/JitJV9prIakAAAAASUVORK5CYII=\n" + }, + "output_type": "display_data", + "metadata": {} + }, + { + "data": { + "image/png": "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\n0qVLF7KysrjyyisrL143efJkbr31VgDKy8tp1aoVd955JwD79++nRYsWfPXVVwCsWLGisvnPWWed\nxeKwU8ovuugifvGLX3D++efTsmVLPv/882rjWLlyJT179iQzM5OxY8dGtOWsGuPWrVuBhrcZ/eKL\nL8jPz6dNmzZccskllJSUNPjfs7aUGPyg4SSRBlu0aBH33HMPr776Klu3bqVTp05ce+21APTv37/y\nC37VqlV06NCBJUuWALB8+XK6detG27ZtKSoqYujQoUycOJHS0lIefvhhrrnmGnbu3Fn5OTNnzuS5\n555jz549Ec12ws2aNYs///nPfPrpp6xfv57777+/xhivu+66yvc1pM3oyJEjOfvssykpKeEXv/gF\nM2bMqMe/Yv0oMfhBvaAlwQTU2fOYZs2axdixY+nVqxdNmzblwQcf5L333uPLL7+kb9++bNy4kdLS\nUpYsWcLYsWMpKiqirKyMJUuW0L9/fwBefvllhgwZwiWXXALAwIED6d27N2+EHdH/5Cc/oVu3bqSk\npJCamlptLLfccgvZ2dm0bduWn//858yePfu4MdZGTW1GN23axOrVq7nvvvto2rQpF1xwAcOGDWvI\nP2edKDH4QVXQkmAC6ux5TFXbZbZs2ZLMzEyKiopo3rw5vXv3pqCggCVLlpCfn0+/fv1YunQpixcv\nrkwMhYWFzJ07l4yMjMr+0MuWLYvop5Cbm3vcWMI7seXl5bFly5bjxlgbNbUZ3bJlC+np6ZXPVXxu\nrDSJ2Sc1NhXDSRdfHHQkIgmparvMb775hp07d5KTkwPAhRdeyKJFi1izZg1nn302F154IW+//Tar\nVq3iwgsvBLwv/R//+Mc8/fTTNX5Obc702bRpU+X9wsJCsrOzjxljx44dK7/Uy8rKaNWqFVD7NqMn\nnXQSpaWl7Nu3r3I9X375JSkpsflbXkcMflEVtEitHTx4kAMHDlTeDh8+zIgRI/jd737H3/72Nw4c\nOMA999xDnz596NSpE+DNM7z44ov06NGDJk2akJ+fz3PPPUfnzp3JzMwE4Prrr2f+/PksXLiQI0eO\nsH//fhYvXlz5F39tPf744xQVFbFr1y4eeOCBynmEmmLMzc1tUJvRTp060bt3byZNmsShQ4dYunQp\n82M4b6nE4BdVQYvU2pAhQ0hLS6NFixakpaVx7733MnDgQKZMmcLVV19NTk4On3/+OXPmzKl8T79+\n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\n" + }, + "output_type": "display_data", + "metadata": {} + } + ], + "source": [ + "%matplotlib inline\n", + "\n", + "from src.point_pattern import PointPattern\n", + "from src.point import Point\n", + "import pysal as ps\n", + "import matplotlib\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "shapefile = ps.open(ps.examples.get_path('new_haven_merged.shp'))\n", + "dbf = ps.open(ps.examples.get_path('new_haven_merged.dbf'))\n", + "\n", + "points= []\n", + "i = 0\n", + "for geometry, attributes in zip(shapefile, dbf):\n", + " i += 1\n", + " points.append(Point(\n", + " geometry[0], \n", + " geometry[1], \n", + " date=attributes[0],\n", + " problem=attributes[1],\n", + " address=attributes[2],\n", + " location=attributes[3],\n", + " time=attributes[4]))\n", + " '''\n", + " # Uncommenting this section displays the first 50 items in the files.\n", + " print(geometry, attributes)\n", + " if i == 50:\n", + " break\n", + " '''\n", + " \n", + "point_pattern = PointPattern()\n", + "for point in points:\n", + " point_pattern.add_point(point)\n", + " \n", + "c1, c2 = point_pattern.get_critical_points()\n", + "#print(c1, c2)\n", + "\n", + "def is_significant(n):\n", + " if n < c1 or n > c2:\n", + " return 'is significant'\n", + " else:\n", + " return 'is not significant'\n", + "\n", + "nn = point_pattern.average_nearest_neighbor_distance_kdtree()\n", + "print(\"The entire dataset has an average nearest neighbor distance of\", nn)\n", + "print(\"The entire dataset\", is_significant(nn))\n", + "\n", + "nn = point_pattern.average_nearest_neighbor_distance_kdtree(mark_name='date', mark_value=' Sat, Sept. 13th 2014')\n", + "print(\"\\nElements that occurred on 9/13/2014 have an average nn distance of\", nn)\n", + "print(\"The date 9/13/2014\", is_significant(nn))\n", + "\n", + "nn = point_pattern.average_nearest_neighbor_distance_kdtree(mark_name='problem', mark_value='animal-bites')\n", + "print(\"\\nElements involving animal bites have an average nn distance of\", nn)\n", + "print(\"The animal bite issue\", is_significant(nn))\n", + "\n", + "\n", + "def generate_plot(x, y, title, **kwargs):\n", + " fig, axes = plt.subplots()\n", + "\n", + " axes.plot(x, y, **kwargs)\n", + " axes.set_xlabel('x')\n", + " axes.set_ylabel('y')\n", + " axes.set_title(title);\n", + "\n", + "x = []\n", + "y = []\n", + "for point in point_pattern.points:\n", + " x.append(point.x)\n", + " y.append(point.y)\n", + "\n", + "generate_plot(np.array(x), np.array(y), 'Point Pattern', color='red', marker='o', linewidth=0)\n", + "\n", + "x2 = np.array([3, 7, 14, 26, 27])\n", + "y2 = np.array([\n", + " point_pattern.compute_g(3),\n", + " point_pattern.compute_g(7),\n", + " point_pattern.compute_g(14),\n", + " point_pattern.compute_g(26),\n", + " point_pattern.compute_g(27)])\n", + "fig, ax = plt.subplots()\n", + "ax.plot(x2, y2, label=\"G Function\", color='red')\n", + "ax.plot((3, 27), (c1, c1), label=\"Lower bound\", color='blue')\n", + "ax.plot((3, 27), (c2, c2), label=\"Upper bound\", color='blue')\n", + "ax.legend(loc=0); # auto decide location\n", + "ax.set_xlabel('x')\n", + "ax.set_ylabel('y')\n", + "ax.set_title('G Function');" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3.0 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/exit-24.png b/exit-24.png new file mode 100644 index 0000000..cad370a Binary files /dev/null and b/exit-24.png differ diff --git a/openFolder.png b/openFolder.png new file mode 100644 index 0000000..95f7f23 Binary files /dev/null and b/openFolder.png differ diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/analytics.py b/src/analytics.py new file mode 100644 index 0000000..0a2544f --- /dev/null +++ b/src/analytics.py @@ -0,0 +1,108 @@ +from . import utils + + +def find_largest_city(gj): + """ + Iterate through a geojson feature collection and + find the largest city. Assume that the key + to access the maximum population is 'pop_max'. + + Parameters + ---------- + gj : dict + A GeoJSON file read in as a Python dictionary + + Returns + ------- + city : str + The largest city + + population : int + The population of the largest city + """ + city = None + max_population = 0 + for item in gj["features"]: + props = item["properties"] + if props["pop_max"] > max_population: + max_population = props["pop_max"] + city = props["adm1name"] + + return city, max_population + + +def average_nearest_neighbor_distance(points_list, mark = None): + """ + Given a set of points, compute the average nearest neighbor. + + Parameters + ---------- + points_list : list + A list of Point objects. + mark : str + An optional string to filter the inputs by a certain color. + + Returns + ------- + mean_d : float + Average nearest neighbor distance + + References + ---------- + Clark and Evan (1954 Distance to Nearest Neighbor as a + Measure of Spatial Relationships in Populations. Ecology. 35(4) + p. 445-453. + """ + points = None + if mark is None: + # User passed in no mark, we will use the entire points_list. + points = points_list + else: + points = list(filter(lambda current_point: current_point.mark['color'] == mark, points_list)) + + mean_d = 0 + temp_nearest_neighbor = None + # Average the nearest neighbor distance of all points. + for i, point in enumerate(points): + # Find the nearest neighbor to this point. + for j, otherPoint in enumerate(points): + # You are not your own neighbor. + if i == j: + continue + # To avoid multiple calculations, we'll cache the result. + current_distance = utils.euclidean_distance((point.x, point.y), (otherPoint.x, otherPoint.y)) + # nearest neighbor will be None if this is the first neighbor we have iterated over. + if temp_nearest_neighbor is None: + temp_nearest_neighbor = current_distance + elif temp_nearest_neighbor > current_distance: + temp_nearest_neighbor = current_distance + # At this point, we've found point's nearest neighbor distance. + # Add in that distance. + mean_d += temp_nearest_neighbor + temp_nearest_neighbor = None + + # Divide by number of points. + mean_d /= len(points) + + return mean_d + + +def permutations(p=99, mark=None): + n = 100 + to_return = [] + for i in range(p): + to_return.append( + average_nearest_neighbor_distance( + utils.create_random(n), + mark + ) + ) + return to_return + + +def compute_critical(p): + """ + Calculates the critical points (lowest distance and greatest distance) in a set of + randomly generated permutations (created using permutations(p)). + """ + return min(p), max(p) \ No newline at end of file diff --git a/src/io_geojson.py b/src/io_geojson.py new file mode 100644 index 0000000..10b5764 --- /dev/null +++ b/src/io_geojson.py @@ -0,0 +1,28 @@ +import json + + +def read_geojson(input_file): + """ + Read a geojson file + + Parameters + ---------- + input_file : str + The PATH to the data to be read + + Returns + ------- + gj : dict + An in memory version of the geojson + """ + # Please use the python json module (imported above) + # to solve this one. + with open(input_file, 'r') as fp: + gj = json.load(fp) + return gj + + +def read_tweets(tweet_file): + with open(tweet_file, 'r') as fp: + to_return = json.load(fp) + return to_return diff --git a/src/point.py b/src/point.py new file mode 100644 index 0000000..31a69d5 --- /dev/null +++ b/src/point.py @@ -0,0 +1,32 @@ +from . import utils +import random + + +class Point(object): + def __init__(self, x, y, **mark): + self.x = x + self.y = y + self.mark = mark + + def __add__(self, other): + return Point(self.x + other.x, self.y + other.y) + + def __radd__(self, other): + return self.__add__(self, other) + + def __str__(self): + return "({0}, {1})".format(self.x, self.y) + + def __neg__(self): + return Point(-self.x, -self.y) + + def is_coincident(self, other_point): + return utils.check_coincident((self.x, self.y), (other_point.x, other_point.y)) + + def shift_point(self, delta_x, delta_y): + result = utils.shift_point((self.x, self.y), delta_x, delta_y) + self.x = utils.getx(result) + self.y = utils.gety(result) + + def get_array(self): + return [self.x, self.y] diff --git a/src/point_pattern.py b/src/point_pattern.py new file mode 100644 index 0000000..0912a58 --- /dev/null +++ b/src/point_pattern.py @@ -0,0 +1,154 @@ +from .point import Point +from . import analytics +import random +import numpy as np +import scipy.spatial as ss + + +class PointPattern(object): + def __init__(self): + self.points = [] + + def add_point(self, point): + self.points.append(point) + + def remove_point(self, index): + try: + del(self.points[index]) + except IndexError: + print('Index {} not in list'.format(index)) + + def average_nearest_neighbor_distance(self): + return analytics.average_nearest_neighbor_distance(self.points) + + def average_nearest_neighbor_distance_kdtree(self, mark_name=None, mark_value=None): + point_list = [] + points = None + if mark_name is None: + points = self.points + else: + points = list(filter(lambda current_point: current_point.mark[mark_name] == mark_value, self.points)) + for point in points: + point_list.append(point.get_array()) + point_stack = np.vstack(point_list) + kdtree = ss.KDTree(point_stack) + distances = [] + for p in point_stack: + nearest_neighbor_distance, _ = kdtree.query(p, k=2) + distances.append(nearest_neighbor_distance[1]) + nn_distances = np.array(distances) + return np.mean(distances) + + def average_nearest_neighbor_distance_numpy(self): + point_list = [] + for point in self.points: + point_list.append(point.get_array()) + ndarray = np.array(point_list) + nearest_neighbors = [] + temp_nearest_neighbor = None + # Average the nearest neighbor distance of all points. + for i, point in enumerate(ndarray): + # Find the nearest neighbor to this point. + for j, otherPoint in enumerate(ndarray): + # You are not your own neighbor. + if i == j: + continue + current_distance = ss.distance.euclidean(point, otherPoint) + # nearest neighbor will be None if this is the first neighbor we have iterated over. + if temp_nearest_neighbor is None: + temp_nearest_neighbor = current_distance + elif temp_nearest_neighbor > current_distance: + temp_nearest_neighbor = current_distance + # At this point, we've found point's nearest neighbor distance. + # Add in that distance. + nearest_neighbors.append(temp_nearest_neighbor) + temp_nearest_neighbor = None + + return np.mean(nearest_neighbors) + + def count_coincident(self): + """ + Returns the number of coincident points. + If two points are at the same spatial location, + that counts as two coincident points. Three coincident points + means three points at the same location. + """ + to_return = 0 + handled_indices = [] + for i, point_a in enumerate(self.points): + for j, point_b in enumerate(self.points): + if j in handled_indices: + continue + if i == j: + continue + if point_a.is_coincident(point_b): + to_return += 1 + handled_indices.append(j) + return to_return + + def list_marks(self): + marks = [] + for point in self.points: + if 'color' in point.mark and point.mark['color'] not in marks: + marks.append(point.mark['color']) + return marks + + def find_subset_with_mark(self, mark): + return list(filter(lambda current_point: 'color' in current_point.mark and current_point.mark['color'] == mark, self.points)) + + def generate_random_points(self, n=None): + if n is None: + n = len(self.points) + to_return = [] + marks = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'] + + for i in range(n): + to_return.append(Point( + round(random.random(), 2), + round(random.random(), 2), + color=random.choice(marks) + )) + return to_return + + def generate_realizations(self, k): + n = 100 + to_return = [] + for i in range(k): + to_return.append( + analytics.average_nearest_neighbor_distance( + self.generate_random_points(count=n) + ) + ) + return to_return + + def get_critical_points(self): + return analytics.compute_critical(self.generate_realizations(100)) + + def compute_g(self, nsteps): + ds = np.linspace(0, 1, nsteps) + current_sum = 0 + for i in range(nsteps): + o_i = ds[i] + # argsort puts the points in order. We want to ignore the same point each time, + # so we get element at index 1. An array minus a number performs a scalar + # operation on all elements. + current_sum += np.abs(ds[np.argsort(np.abs(ds - o_i))[1]] - o_i) + return current_sum / nsteps + + def generate_random_points(self, count = 2, range_min = 0, range_max = 1, seed = None): + rng = None + if seed is None: + rng = np.random + else: + rng = np.random.RandomState(seed) + random.seed(seed) + pairs = rng.uniform(range_min, range_max, (count, 2)) + to_return = [] + marks = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'] + for i in range(len(pairs)): + to_return.append(Point( + pairs[i][0], + pairs[i][1], + color=random.choice(marks) + )) + return to_return diff --git a/src/tests/__init__.py b/src/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tests/point_pattern_test.py b/src/tests/point_pattern_test.py new file mode 100644 index 0000000..be4697a --- /dev/null +++ b/src/tests/point_pattern_test.py @@ -0,0 +1,60 @@ +import unittest + +from ..point_pattern import PointPattern +from ..point import Point + + +class TestPointPattern(unittest.TestCase): + def setUp(self): + self.point_pattern = PointPattern() + self.point_pattern.add_point(Point(5, 6, color='red')) + self.point_pattern.add_point(Point(6, 5, color='orange')) + self.point_pattern.add_point(Point(5, 6, color='orange')) + self.point_pattern.add_point(Point(5, 6)) + + def test_coincident(self): + self.assertEqual(self.point_pattern.count_coincident(), 3) + + def test_list_marks(self): + self.assertEqual(self.point_pattern.list_marks(), ['red', 'orange']) + + def test_find_subset_with_mark(self): + self.assertEqual(len(self.point_pattern.find_subset_with_mark('orange')), 2) + self.assertEqual(len(self.point_pattern.find_subset_with_mark('red')), 1) + + def test_generate_random(self): + # First test does not pass in n, making n = length of current point pattern. + self.assertEqual(len(self.point_pattern.generate_random_points()), 4) + # Second test explicitly passes in n. + self.assertEqual(len(self.point_pattern.generate_random_points(10)), 10) + + def test_generate_realizations(self): + self.assertEqual(len(self.point_pattern.generate_realizations(100)), 100) + + def test_compute_g(self): + self.assertAlmostEqual(self.point_pattern.compute_g(10), 0.111, places=3) + self.assertAlmostEqual(self.point_pattern.compute_g(50), 0.020, places=3) + self.assertAlmostEqual(self.point_pattern.compute_g(100), 0.010, places=3) + self.assertAlmostEqual(self.point_pattern.compute_g(1000), 0.001, places=3) + + def test_nearest_neighbor(self): + # Test the KDTree implementation against the original implementation. + self.assertEqual( + self.point_pattern.average_nearest_neighbor_distance_kdtree(), + self.point_pattern.average_nearest_neighbor_distance()) + self.assertAlmostEqual(self.point_pattern.average_nearest_neighbor_distance_kdtree(), 0.354, places=3) + self.assertAlmostEqual(self.point_pattern.average_nearest_neighbor_distance_numpy(), 0.354, places=3) + + def test_generate_random(self): + points_list = [] + marks_list = [] + for point in self.point_pattern.generate_random_points(count = 3, seed = 1234): + points_list.append(point.get_array()) + marks_list.append(point.mark['color']) + self.assertAlmostEqual(points_list[0][0], 0.19, places=2) + self.assertAlmostEqual(points_list[0][1], 0.62, places=2) + self.assertAlmostEqual(points_list[1][0], 0.44, places=2) + self.assertAlmostEqual(points_list[1][1], 0.79, places=2) + self.assertAlmostEqual(points_list[2][0], 0.78, places=2) + self.assertAlmostEqual(points_list[2][1], 0.27, places=2) + self.assertEqual(marks_list, ['violet', 'green', 'red']) diff --git a/src/tests/point_test.py b/src/tests/point_test.py new file mode 100644 index 0000000..4f4f703 --- /dev/null +++ b/src/tests/point_test.py @@ -0,0 +1,92 @@ +import unittest +import random + +from ..point import Point + + +class TestPointClass(unittest.TestCase): + def setUp(self): + pass + + def test_add(self): + point_a = Point(10, 27) + point_b = Point(5, 7) + point_c = point_a + point_b + point_d = point_b + point_a + self.assertEqual(point_c.x, 15) + self.assertEqual(point_c.y, 34) + self.assertEqual(point_c.x, point_d.x) + self.assertEqual(point_c.y, point_d.y) + + def test_str(self): + point_a = Point(5, 7) + self.assertEqual(str(point_a), "(5, 7)") + + def test_neg(self): + point_a = Point(5, 7) + point_b = -point_a + self.assertEqual(point_b.x, -5) + self.assertEqual(point_b.y, -7) + + def coordinates_properly_set(self, x, y): + """ + This test checks if the Point constructor correctly + assigns the x and y coordinates to the appropriate variables. + """ + test_point = Point(x, y) + self.assertEqual(test_point.x, x) + self.assertEqual(test_point.y, y) + + def test_coincident(self): + """ + This test checks if the is_coincident method works properly. + """ + point_a = Point(10, 37) + point_b = Point(10, 37) + point_c = Point(10, 36) + point_d = Point(0, 37) + self.assertTrue(point_a.is_coincident(point_b)) + self.assertFalse(point_a.is_coincident(point_c)) + self.assertFalse(point_a.is_coincident(point_d)) + + def test_shift(self): + """ + This test checks if the shift_point method works properly. + """ + test_point = Point(10, 37) + test_point.shift_point(5, 10) + self.assertEqual(test_point.x, 15) + self.assertEqual(test_point.y, 47) + + def test_marking(self): + """ + This test verifies that marked points can be created properly. + """ + + def get_occurrence_count(points, mark): + """ + This is a helper method for test_marking. + Returns the number of occurrences of a certain mark in a list of points. + """ + return len(list(filter(lambda current_point: current_point.mark['color'] == mark, points))) + + random.seed(9631) + marks = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'] + points = [] + for i in range(20): + points.append(Point(0, 0, color=random.choice(marks))) + + self.assertEqual(get_occurrence_count(points, 'red'), 5) + self.assertEqual(get_occurrence_count(points, 'orange'), 1) + self.assertEqual(get_occurrence_count(points, 'yellow'), 2) + self.assertEqual(get_occurrence_count(points, 'green'), 3) + self.assertEqual(get_occurrence_count(points, 'blue'), 1) + self.assertEqual(get_occurrence_count(points, 'indigo'), 5) + self.assertEqual(get_occurrence_count(points, 'violet'), 3) + + def test_get_array(self): + pointa = Point(1, 2) + pointb = Point(2, 3) + pointc = Point(3, 4) + points = [pointa.get_array(), pointb.get_array(), pointc.get_array()] + self.assertEqual(points, [[1, 2], [2, 3], [3, 4]]) diff --git a/src/tweet.py b/src/tweet.py new file mode 100644 index 0000000..911a8a5 --- /dev/null +++ b/src/tweet.py @@ -0,0 +1,64 @@ +from . import utils +import random + + +class Tweet(object): + def __init__(self, tweet_dict): + """ + + Parameters + ---------- + tweet_dict A dictionary containing all the tweet information, as formated by the Twitter API. + + Returns + ------- + + """ + self.tweet = tweet_dict['text'] + # Lat/lon is formatted in tweets as : longitude, latitude + self.bounds = tweet_dict['place']['bounding_box']['coordinates'][0] + self.date = tweet_dict['created_at'] + self.username = tweet_dict['user']['name'] + self.user_description = tweet_dict['user']['description'] + self.tweet_id = tweet_dict['id'] + self.user_followers_count = tweet_dict['user']['followers_count'] + + + def get_lat_n(self, n): + """ + Get the nth latitude in the bounding polygon. + Parameters + ---------- + n + + Returns + ------- + + """ + return self.bounds[n][1] + + def get_lon_n(self, n): + """ + Get the nth longitude in the bounding polygon. + Parameters + ---------- + n + + Returns + ------- + + """ + return self.bounds[n][0] + + def gen_point_in_bounds(self): + """ + Generates a random point inside the bounding polygon. + Returns + ------- + + """ + # The latitudes are the same for points 0 and 2, as well as 1 and 3. + lat = random.uniform(self.get_lat_n(0), self.get_lat_n(1)) + # The longitudes are the same for points 0 and 1, as well as 2 and 3. + lon = random.uniform(self.get_lon_n(0), self.get_lon_n(2)) + return lat, lon diff --git a/src/utils.py b/src/utils.py new file mode 100644 index 0000000..38964f5 --- /dev/null +++ b/src/utils.py @@ -0,0 +1,289 @@ +import math +import random + +from . import point + + +def create_random(n): + """ + Generates n random points. Coordinates are between 0 and 1.00 inclusive. + """ + rng = random.Random() + marks = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'] + to_return = [] + for i in range(n): + to_return.append(point.Point( + round(rng.uniform(0, 1), 2), + round(rng.uniform(0, 1), 2), + color=rng.choice(marks))) + return to_return + + +def check_significant(lower, upper, distance): + return (distance < lower) or (distance > upper) + + +def mean_center(points): + """ + Given a set of points, compute the mean center + + Parameters + ---------- + points : list + A list of points in the form (x,y) + + Returns + ------- + x : float + Mean x coordinate + + y : float + Mean y coordinate + """ + x = 0 + y = 0 + n = 0 + for point in points: + x += point[0] + y += point[1] + n += 1 + + x /= n + y /= n + + return x, y + + +def minimum_bounding_rectangle(points): + """ + Given a set of points, compute the minimum bounding rectangle. + + Parameters + ---------- + points : list + A list of points in the form (x,y) + + Returns + ------- + : list + Corners of the MBR in the form [xmin, ymin, xmax, ymax] + """ + + mbr = [None,None,None,None] + for point in points: + # First iteration, everything is None. The point will + # form the initial boundaries for the rectangle. + if mbr[0] is None: + mbr[0] = point[0] + mbr[1] = point[1] + mbr[2] = point[0] + mbr[3] = point[1] + else: + # Verify that each edge is far enough. If not, extend the rectangle. + if point[0] < mbr[0]: + mbr[0] = point[0] + if point[1] < mbr[1]: + mbr[1] = point[1] + if point[0] > mbr[2]: + mbr[2] = point[0] + if point[1] > mbr[3]: + mbr[3] = point[1] + + return mbr + + +def mbr_area(mbr): + """ + Compute the area of a minimum bounding rectangle + """ + area = (mbr[3] - mbr[1]) * (mbr[2] - mbr[0]) + + return area + + +def expected_distance(area, n): + """ + Compute the expected mean distance given + some study area. + + This makes lots of assumptions and is not + necessarily how you would want to compute + this. This is just an example of the full + analysis pipe, e.g. compute the mean distance + and the expected mean distance. + + Parameters + ---------- + area : float + The area of the study area + + n : int + The number of points + """ + + expected = 0.5 * ((area / n) ** 0.5) + return expected + + +""" +Below are the functions that you created last week. +Your syntax might have been different (which is awesome), +but the functionality is identical. No need to touch +these unless you are interested in another way of solving +the assignment +""" + + +def manhattan_distance(a, b): + """ + Compute the Manhattan distance between two points + + Parameters + ---------- + a : tuple + A point in the form (x,y) + + b : tuple + A point in the form (x,y) + + Returns + ------- + distance : float + The Manhattan distance between the two points + """ + distance = abs(a[0] - b[0]) + abs(a[1] - b[1]) + return distance + + +def euclidean_distance(a, b): + """ + Compute the Euclidean distance between two points + + Parameters + ---------- + a : tuple + A point in the form (x,y) + + b : tuple + A point in the form (x,y) + + Returns + ------- + + distance : float + The Euclidean distance between the two points + """ + distance = math.sqrt((a[0] - b[0])**2 + (a[1] - b[1])**2) + return distance + + +def shift_point(point, x_shift, y_shift): + """ + Shift a point by some amount in the x and y directions + + Parameters + ---------- + point : tuple + in the form (x,y) + + x_shift : int or float + distance to shift in the x direction + + y_shift : int or float + distance to shift in the y direction + + Returns + ------- + new_x : int or float + shited x coordinate + + new_y : int or float + shifted y coordinate + + Note that the new_x new_y elements are returned as a tuple + + Example + ------- + >>> point = (0,0) + >>> shift_point(point, 1, 2) + (1,2) + """ + x = getx(point) + y = gety(point) + + x += x_shift + y += y_shift + + return x, y + + +def check_coincident(a, b): + """ + Check whether two points are coincident + Parameters + ---------- + a : tuple + A point in the form (x,y) + + b : tuple + A point in the form (x,y) + + Returns + ------- + equal : bool + Whether the points are equal + """ + return a == b + + +def check_in(point, point_list): + """ + Check whether point is in the point list + + Parameters + ---------- + point : tuple + In the form (x,y) + + point_list : list + in the form [point, point_1, point_2, ..., point_n] + """ + return point in point_list + + +def getx(point): + """ + A simple method to return the x coordinate of + an tuple in the form(x,y). We will look at + sequences in a coming lesson. + + Parameters + ---------- + point : tuple + in the form (x,y) + + Returns + ------- + : int or float + x coordinate + """ + return point[0] + + +def gety(point): + """ + A simple method to return the x coordinate of + an tuple in the form(x,y). We will look at + sequences in a coming lesson. + + Parameters + ---------- + point : tuple + in the form (x,y) + + Returns + ------- + : int or float + y coordinate + """ + return point[1] \ No newline at end of file diff --git a/view.py b/view.py new file mode 100644 index 0000000..f6adb2f --- /dev/null +++ b/view.py @@ -0,0 +1,153 @@ +import os +import sys +from PyQt4 import QtGui +from PyQt4 import QtWebKit +from PyQt4 import QtCore +import folium +from src import tweet +from src import io_geojson +import random + + +class LoadingWindow(QtGui.QWidget): + def __init__(self): + super(LoadingWindow, self).__init__() + self.setWindowTitle('Loading, please wait...') + self.progress_bar = QtGui.QProgressBar(self) + self.progress_bar.setGeometry(100, 80, 250, 20) + self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) + self.setGeometry(200, 100, 400, 200) + self.setAttribute(QtCore.Qt.WA_DeleteOnClose) + self.show() + + +class View(QtGui.QMainWindow): + + def __init__(self): + super(View, self).__init__() + + self.map = None + self.web_view = None + self.map_dir = 'tmp/map.html' + self.init_ui() + self.popup = None + + def init_ui(self): + # The map will be saved in a temporary directory. Make sure it exists. + os.makedirs('tmp', exist_ok=True) + + self.web_view = QtWebKit.QWebView() + self.map = folium.Map(location=[33.4484, -112.0740]) + self.map.zoom_start = 10 + + self.map.save(self.map_dir) + self.web_view.load(QtCore.QUrl(self.map_dir)) + self.setCentralWidget(self.web_view) + + # Define the exit action for use in the toolbar and file menu. + exit_action = QtGui.QAction(QtGui.QIcon('exit-24.png'), 'Exit', self) + exit_action.setShortcut('Ctrl+Q') + exit_action.setStatusTip('Exit application') + exit_action.triggered.connect(self.close) + + # Define the open action. + open_action = QtGui.QAction(QtGui.QIcon('openFolder.png'), 'Open', self) + open_action.setShortcut('Ctrl+O') + open_action.setStatusTip('Open a Tweet JSON file') + open_action.triggered.connect(self.open) + + # Add a status bar. + self.statusBar() + + # Add a menu bar, and add a file menu to that. + menu_bar = self.menuBar() + # Set the mnemonic to Alt-F. + # Details: https://msdn.microsoft.com/en-us/library/system.windows.forms.label.usemnemonic(v=vs.110).aspx + file_menu = menu_bar.addMenu('&File') + file_menu.addAction(open_action) + file_menu.addAction(exit_action) + + # Add an exit item to the toolbar. + tool_bar = self.addToolBar('Exit') + tool_bar.addAction(open_action) + tool_bar.addAction(exit_action) + + # x, y, width, height + self.setGeometry(300, 300, 750, 650) + self.setWindowTitle('Main Window') + self.show() + + # Put the window in the middle of the screen. + self.center() + + def center(self): + geometry = self.frameGeometry() + center = QtGui.QDesktopWidget().availableGeometry().center() + geometry.moveCenter(center) + self.move(geometry.topLeft()) + + def open(self): + file_name = QtGui.QFileDialog.getOpenFileName(parent=self, caption='Open Tweet JSON file', filter='*.json') + + # Make sure that the user selected a file to load. + if not file_name: + return + + tweets = [] + temp_tweets = io_geojson.read_tweets(file_name) + for _ in temp_tweets: + tweets.append(tweet.Tweet(_)) + self.display_tweets(tweets) + + def display_tweets(self, tweets): + """ + + Parameters + ---------- + tweets + A list of Tweet objects. + + Returns + ------- + + """ + self.popup = LoadingWindow() + average_lat = 0 + average_lon = 0 + handled_tweets = 0 + + # Let the garbage collector take care of the old map, instead of manually removing all the old markers. + self.map = folium.Map(location=[33.4484, -112.0740]) + self.map.zoom_start = 10 + + random.seed(1234) + + # Add all tweet locations as markers to the map. + # Calculate the average lat/lon while iterating. + for tweet in tweets: + lat, lon = tweet.gen_point_in_bounds() + average_lat += lat + average_lon += lon + folium.Marker([lat, lon], popup=tweet.username).add_to(self.map) + handled_tweets += 1 + # Show the percent progress in the popup window. + self.popup.progress_bar.setValue(100 * handled_tweets / len(tweets)) + + average_lon /= len(tweets) + average_lat /= len(tweets) + self.map.location = [average_lat, average_lon] + + self.map.save(self.map_dir) + self.web_view.load(QtCore.QUrl(self.map_dir)) + + self.popup.close() + + +def main(): + app = QtGui.QApplication(sys.argv) + view = View() + sys.exit(app.exec_()) + + +if __name__ == '__main__': + main() \ No newline at end of file