def test_find(self):
        with ZipcodeSearchEngine() as search:
            # Find most people living zipcode in New York
            res = search.find(
                city="new york",
                sort_by="Population",
                ascending=False,
            )
            is_all_descending([z.Population for z in res])

            # Find all zipcode in California that prefix is "999"
            res = search.find(
                state="califor",
                prefix="95",
                sort_by="HouseOfUnits",
                ascending=False,
                returns=100,
            )
            assert len(res) == 100
            for z in res:
                assert z.State == "CA"
                assert z.Zipcode.startswith("95")
            is_all_descending([z.HouseOfUnits for z in res])

            # Find top 10 richest zipcode near Silicon Valley
            lat, lng = 37.391184, -122.082235
            radius = 100
            res = search.find(
                lat=lat,
                lng=lng,
                radius=radius,
                sort_by="Wealthy",
                ascending=False,
                returns=10,
            )
            assert len(res) == 10
            for z in res:
                assert great_circle((lat, lng),
                                    (z.Latitude, z.Longitude)) <= radius
            is_all_descending([z.Wealthy for z in res])

            # Find zipcode that average personal annual income greater than
            # 100000 near Silicon Valley, order by distance
            lat, lng = 37.391184, -122.082235
            radius = 100
            res = search.find(
                lat=lat,
                lng=lng,
                radius=radius,
                wealthy_lower=60000,
                sort_by=None,
                returns=0,
            )
            assert len(res) > 5
            for z in res:
                assert z.Wealthy >= 60000
            is_all_ascending([
                great_circle((lat, lng), (z.Latitude, z.Longitude))
                for z in res
            ])
def test_all():
    lyon = (45.7597, 4.8422)
    paris = (48.8567, 2.3508)

    delta = 0.01
    assert abs(great_circle(lyon, paris, miles=False)/392.001248 - 1.0) <= delta
    assert abs(great_circle(lyon, paris, miles=True)/243.589575 - 1.0) <= delta
    def test_search_by_coordinate(self):
        with ZipcodeSearchEngine() as search:
            # 在马里兰选一个坐标, 返回1000条, 但实际上不到1000条
            lat, lng = 39.114407, -77.205758

            # 返回的结果必须按照距离是从小到大的
            res1 = search.by_coordinate(lat, lng, ascending=True, returns=1000)
            len(res1) < 1000
            dist_array = [
                great_circle((lat, lng), (z.Latitude, z.Longitude), miles=True)
                for z in res1
            ]
            is_all_ascending(dist_array)

            res2 = search.by_coordinate(lat,
                                        lng,
                                        ascending=False,
                                        returns=1000)
            dist_array = [
                great_circle((lat, lng), (z.Latitude, z.Longitude), miles=True)
                for z in res2
            ]
            is_all_descending(dist_array)

            # 当returns = 0时, 返回所有符合条件的
            res3 = search.by_coordinate(lat, lng, returns=0)
            assert len(res1) == len(res3)

            # 当没有符合条件的zipcode时, 返回空列表
            res3 = search.by_coordinate(lat, lng, radius=-1)
            assert len(res3) == 0
    def test_search_by_coordinate(self):
        with ZipcodeSearchEngine() as search:
            # 在马里兰选一个坐标, 返回1000条, 但实际上不到1000条
            lat, lng = 39.114407, -77.205758

            # 返回的结果必须按照距离是从小到大的
            res1 = search.by_coordinate(lat, lng, ascending=True, returns=1000)
            len(res1) < 1000
            dist_array = [
                great_circle((lat, lng), (z.Latitude, z.Longitude), miles=True) for z in res1]
            is_all_ascending(dist_array)

            res2 = search.by_coordinate(
                lat, lng, ascending=False, returns=1000)
            dist_array = [
                great_circle((lat, lng), (z.Latitude, z.Longitude), miles=True) for z in res2]
            is_all_descending(dist_array)

            # 当returns = 0时, 返回所有符合条件的
            res3 = search.by_coordinate(lat, lng, returns=0)
            assert len(res1) == len(res3)

            # 当没有符合条件的zipcode时, 返回空列表
            res3 = search.by_coordinate(lat, lng, radius=-1)
            assert len(res3) == 0
    def test_all(self):
        lyon = (45.7597, 4.8422)
        paris = (48.8567, 2.3508)

        self.assertAlmostEqual(
            great_circle(lyon, paris, miles=False)/392.001248, 1.0, delta=0.01)
        self.assertAlmostEqual(
            great_circle(lyon, paris, miles=True)/243.589575, 1.0, delta=0.01)
def test_all():
    lyon = (45.7597, 4.8422)
    paris = (48.8567, 2.3508)

    delta = 0.01
    assert abs(great_circle(lyon, paris, miles=False) / 392.001248 -
               1.0) <= delta
    assert abs(great_circle(lyon, paris, miles=True) / 243.589575 -
               1.0) <= delta
    def test_find(self):
        with ZipcodeSearchEngine() as search:
            # Find most people living zipcode in New York
            res = search.find(
                city="new york",
                sort_by="Population", ascending=False,
            )
            is_all_descending([z.Population for z in res])

            # Find all zipcode in California that prefix is "999"
            res = search.find(
                state="califor",
                prefix="95",
                sort_by="HouseOfUnits", ascending=False,
                returns=100,
            )
            assert len(res) == 100
            for z in res:
                assert z.State == "CA"
                assert z.Zipcode.startswith("95")
            is_all_descending([z.HouseOfUnits for z in res])

            # Find top 10 richest zipcode near Silicon Valley
            lat, lng = 37.391184, -122.082235
            radius = 100
            res = search.find(
                lat=lat,
                lng=lng,
                radius=radius,
                sort_by="Wealthy", ascending=False,
                returns=10,
            )
            assert len(res) == 10
            for z in res:
                assert great_circle(
                    (lat, lng), (z.Latitude, z.Longitude)) <= radius
            is_all_descending([z.Wealthy for z in res])

            # Find zipcode that average personal annual income greater than
            # 100000 near Silicon Valley, order by distance
            lat, lng = 37.391184, -122.082235
            radius = 100
            res = search.find(
                lat=lat,
                lng=lng,
                radius=radius,
                wealthy_lower=60000,
                sort_by=None,
                returns=0,
            )
            assert len(res) > 5
            for z in res:
                assert z.Wealthy >= 60000
            is_all_ascending([
                great_circle((lat, lng), (z.Latitude, z.Longitude)) for z in res
            ])
    def by_coordinate(self, lat, lng, radius=20, standard_only=True, 
                      returns=_DEFAULT_LIMIT):
        """Search zipcode information near a coordinate on a map. May return
        multiple results.
        
        :param lat: center latitude
        :param lng: center lngitude
        :param radius: for the inside implementation only, search zipcode 
          within #radius units of lat, lng
        :param standard_only: boolean, default True, only returns standard 
          type zipcode
        :param returns: returns at most how many results
        """
        # define lat lng boundary
        dist_btwn_lat_deg = 69.172
        dist_btwn_lon_deg = math.cos(lat) * 69.172
        lat_degr_rad = radius * 1.0/dist_btwn_lat_deg
        lon_degr_rad = radius * 1.0/dist_btwn_lon_deg
    
        lat_lower = lat - lat_degr_rad
        lat_upper = lat + lat_degr_rad
        lng_lower = lng - lon_degr_rad
        lng_upper = lng + lon_degr_rad
        
        # execute query
        select_sql = \
        """
        SELECT * FROM zipcode 
        WHERE
            Latitude >= %s 
            AND Latitude <= %s
            AND Longitude >= %s
            AND Longitude <= %s
        """ % (lat_lower, lat_upper, lng_lower, lng_upper)
        if standard_only:
            select_sql = select_sql + self._standard_only_param

        # use heap sort find 5 closest zipcode
        heap = list()
        for row in self.cursor.execute(select_sql):
            dist = great_circle((row["Latitude"], row["Longitude"]), (lat, lng))
            heappush(heap, [dist,] + list(row))
            
        # generate results
        res = list()
        if returns >= 1:
            for i in range(returns):
                try:
                    res.append(
                        Zipcode(self.all_column, heappop(heap)[1:])
                    )
                except:
                    pass
        elif returns == 0:
            while heap:
                res.append(
                    Zipcode(self.all_column, heappop(heap)[1:])
                )
        return res
示例#9
0
 def gen():
     for row in self.cursor.execute(select_sql):
         dist = great_circle(
             (row["Latitude"], row["Longitude"]), (lat, lng))
         if dist <= radius:
             yield (dist, row)
示例#10
0
    def find(self,
             lat=None, lng=None, radius=None,
             city=None, state=None,
             prefix=None,
             pattern=None,
             population_lower=None, population_upper=None,
             density_lower=None, density_upper=None,
             landarea_lower=None, landarea_upper=None,
             waterarea_lower=None, waterarea_upper=None,
             totalwages_lower=None, totalwages_upper=None,
             wealthy_lower=None, wealthy_upper=None,
             house_lower=None, house_upper=None,
             standard_only=True,
             sort_by="ZipCode", ascending=True,
             returns=DEFAULT_LIMIT,
             ):
        """
        :params sort_by: can be attribute name or 'Dist'.
        """
        where_chunks = list()

        #--- by_coordinate ---
        if isinstance(lat, (integer_types, float)) and \
                isinstance(lat, (integer_types, float)) and \
                isinstance(radius, (integer_types, float)):
            flag_by_coordinate = True
            if radius <= 0:
                return []

            # define lat lng boundary
            dist_btwn_lat_deg = 69.172
            dist_btwn_lon_deg = math.cos(lat) * 69.172
            lat_degr_rad = abs(radius * 1.0 / dist_btwn_lat_deg)
            lon_degr_rad = abs(radius * 1.0 / dist_btwn_lon_deg)

            lat_lower = lat - lat_degr_rad
            lat_upper = lat + lat_degr_rad
            lng_lower = lng - lon_degr_rad
            lng_upper = lng + lon_degr_rad

            where_chunks.append("Latitude >= %s" % lat_lower)
            where_chunks.append("Latitude <= %s" % lat_upper)
            where_chunks.append("Longitude >= %s" % lng_lower)
            where_chunks.append("Longitude <= %s" % lng_upper)

            if (sort_by is None) or (sort_by == "Dist"):
                flag_sort_by = False
            else:
                flag_sort_by = True
        else:
            flag_by_coordinate = False

        #--- by city or state ---
        if (state is not None) and (city is not None):
            state = self._find_state(state, best_match=True)[0]
            city = self._find_city(city, state, best_match=True)[0]
            where_chunks.append("State = '%s' AND City = '%s'" % (state, city))
        elif (state is not None) and (city is None):
            state = self._find_state(state, best_match=True)[0]
            where_chunks.append("State = '%s'" % state)
        elif (state is None) and (city is not None):
            city = self._find_city(city, None, best_match=True)[0]
            where_chunks.append("City = '%s'" % city)
        else:
            pass

        #--- by prefix ---
        if prefix is not None:
            if not isinstance(prefix, string_types):
                raise TypeError("prefix has to be a string")
            if (not prefix.isdigit()) and (1 <= len(prefix) <= 5):
                raise ValueError("prefix has to be a 1-5 letter digits")
            where_chunks.append("Zipcode LIKE '%s%%'" % prefix)

        #--- by pattern ---
        if pattern is not None:
            if not isinstance(pattern, string_types):
                raise TypeError("pattern has to be a string")
            if (not pattern.isdigit()) and (1 <= len(pattern) <= 5):
                raise ValueError("pattern has to be a 1-5 letter digits")
            where_chunks.append("Zipcode LIKE '%%%s%%' " % pattern)

        #--- by population ---
        try:
            sql = self._sql_create_lower_upper(
                "Population", population_lower, population_upper)
            where_chunks.append(sql)
        except ValueError:
            pass

        #--- by density ---
        try:
            sql = self._sql_create_lower_upper(
                "Density", density_lower, density_upper)
            where_chunks.append(sql)
        except ValueError:
            pass

        #--- by land area ---
        try:
            sql = self._sql_create_lower_upper(
                "LandArea", landarea_lower, landarea_upper)
            where_chunks.append(sql)
        except ValueError:
            pass

        #--- by water area ---
        try:
            sql = self._sql_create_lower_upper(
                "WaterArea", waterarea_lower, waterarea_upper)
            where_chunks.append(sql)
        except ValueError:
            pass

        #--- by total wages ---
        try:
            sql = self._sql_create_lower_upper(
                "TotalWages", totalwages_lower, totalwages_upper)
            where_chunks.append(sql)
        except ValueError:
            pass

        #--- by wealthy ---
        try:
            sql = self._sql_create_lower_upper(
                "Wealthy", wealthy_lower, wealthy_upper)
            where_chunks.append(sql)
        except ValueError:
            pass

        #--- by house ---
        try:
            sql = self._sql_create_lower_upper(
                "HouseOfUnits", house_lower, house_upper)
            where_chunks.append(sql)
        except ValueError:
            pass

        select_sql = "SELECT * FROM zipcode \n\tWHERE %s" % " AND ".join(
            where_chunks)
        select_sql = self._sql_modify_standard_only(select_sql, standard_only)
        select_sql = self._sql_modify_order_by(select_sql, sort_by, ascending)

        #--- solve coordinate and other search sort_by conflict ---
        if flag_by_coordinate:
            # has sort_by keyword, order by keyword
            # 有sort_by关键字的情况下, 按关键字排序
            if flag_sort_by:
                res = list()
                for row in self.cursor.execute(select_sql):
                    dist = great_circle(
                        (row["Latitude"], row["Longitude"]), (lat, lng))
                    if dist <= radius:
                        res.append(Zipcode(**row))
                        if len(res) == returns:
                            return res
            # no sort by keyword, then sort from cloest to farturest
            # 没有sort_by关键字, 按距离远近排序
            else:
                # use heap sort find top N closest zipcode
                def gen():
                    for row in self.cursor.execute(select_sql):
                        dist = great_circle(
                            (row["Latitude"], row["Longitude"]), (lat, lng))
                        if dist <= radius:
                            yield (dist, row)

                if returns >= 1:

                    if ascending:
                        data = nsmallest(returns, gen(), key=lambda x: x[0])
                    else:
                        data = nlargest(returns, gen(), key=lambda x: x[0])
                else:
                    if ascending:
                        data = sorted(
                            gen(), key=lambda x: x[0], reverse=not ascending)

                res = [Zipcode(**row) for dist, row in data]
        else:
            select_sql = self._sql_modify_limit(select_sql, returns)
            res = [Zipcode(**row) for row in self.cursor.execute(select_sql)]

        return res
示例#11
0
 def gen():
     for row in self.cursor.execute(select_sql):
         dist = great_circle(
             (row["Latitude"], row["Longitude"]), (lat, lng))
         if dist <= radius:
             yield (dist, row)
示例#12
0
    def find(self,
             lat=None, lng=None, radius=None,
             city=None, state=None,
             prefix=None,
             pattern=None,
             population_lower=None, population_upper=None,
             density_lower=None, density_upper=None,
             landarea_lower=None, landarea_upper=None,
             waterarea_lower=None, waterarea_upper=None,
             totalwages_lower=None, totalwages_upper=None,
             wealthy_lower=None, wealthy_upper=None,
             house_lower=None, house_upper=None,
             standard_only=True,
             sort_by="ZipCode", ascending=True,
             returns=DEFAULT_LIMIT,
             ):
        """
        :params sort_by: can be attribute name or 'Dist'.
        """
        where_chunks = list()

        #--- by_coordinate ---
        if isinstance(lat, (integer_types, float)) and \
                isinstance(lat, (integer_types, float)) and \
                isinstance(radius, (integer_types, float)):
            flag_by_coordinate = True
            if radius <= 0:
                return []

            # define lat lng boundary
            dist_btwn_lat_deg = 69.172
            dist_btwn_lon_deg = math.cos(lat) * 69.172
            lat_degr_rad = abs(radius * 1.0 / dist_btwn_lat_deg)
            lon_degr_rad = abs(radius * 1.0 / dist_btwn_lon_deg)

            lat_lower = lat - lat_degr_rad
            lat_upper = lat + lat_degr_rad
            lng_lower = lng - lon_degr_rad
            lng_upper = lng + lon_degr_rad

            where_chunks.append("Latitude >= %s" % lat_lower)
            where_chunks.append("Latitude <= %s" % lat_upper)
            where_chunks.append("Longitude >= %s" % lng_lower)
            where_chunks.append("Longitude <= %s" % lng_upper)

            if (sort_by is None) or (sort_by == "Dist"):
                flag_sort_by = False
            else:
                flag_sort_by = True
        else:
            flag_by_coordinate = False

        #--- by city or state ---
        if (state is not None) and (city is not None):
            state = self._find_state(state, best_match=True)[0]
            city = self._find_city(city, state, best_match=True)[0]
            where_chunks.append("State = '%s' AND City = '%s'" % (state, city))
        elif (state is not None) and (city is None):
            state = self._find_state(state, best_match=True)[0]
            where_chunks.append("State = '%s'" % state)
        elif (state is None) and (city is not None):
            city = self._find_city(city, None, best_match=True)[0]
            where_chunks.append("City = '%s'" % city)
        else:
            pass

        #--- by prefix ---
        if prefix is not None:
            if not isinstance(prefix, string_types):
                raise TypeError("prefix has to be a string")
            if (not prefix.isdigit()) and (1 <= len(prefix) <= 5):
                raise ValueError("prefix has to be a 1-5 letter digits")
            where_chunks.append("Zipcode LIKE '%s%%'" % prefix)

        #--- by pattern ---
        if pattern is not None:
            if not isinstance(pattern, string_types):
                raise TypeError("pattern has to be a string")
            if (not pattern.isdigit()) and (1 <= len(pattern) <= 5):
                raise ValueError("pattern has to be a 1-5 letter digits")
            where_chunks.append("Zipcode LIKE '%%%s%%' " % pattern)

        #--- by population ---
        try:
            sql = self._sql_create_lower_upper(
                "Population", population_lower, population_upper)
            where_chunks.append(sql)
        except ValueError:
            pass

        #--- by density ---
        try:
            sql = self._sql_create_lower_upper(
                "Density", density_lower, density_upper)
            where_chunks.append(sql)
        except ValueError:
            pass

        #--- by land area ---
        try:
            sql = self._sql_create_lower_upper(
                "LandArea", landarea_lower, landarea_upper)
            where_chunks.append(sql)
        except ValueError:
            pass

        #--- by water area ---
        try:
            sql = self._sql_create_lower_upper(
                "WaterArea", waterarea_lower, waterarea_upper)
            where_chunks.append(sql)
        except ValueError:
            pass

        #--- by total wages ---
        try:
            sql = self._sql_create_lower_upper(
                "TotalWages", totalwages_lower, totalwages_upper)
            where_chunks.append(sql)
        except ValueError:
            pass

        #--- by wealthy ---
        try:
            sql = self._sql_create_lower_upper(
                "Wealthy", wealthy_lower, wealthy_upper)
            where_chunks.append(sql)
        except ValueError:
            pass

        #--- by house ---
        try:
            sql = self._sql_create_lower_upper(
                "HouseOfUnits", house_lower, house_upper)
            where_chunks.append(sql)
        except ValueError:
            pass

        select_sql = "SELECT * FROM zipcode \n\tWHERE %s" % " AND ".join(
            where_chunks)
        select_sql = self._sql_modify_standard_only(select_sql, standard_only)
        select_sql = self._sql_modify_order_by(select_sql, sort_by, ascending)

        #--- solve coordinate and other search sort_by conflict ---
        if flag_by_coordinate:
            # has sort_by keyword, order by keyword
            # 有sort_by关键字的情况下, 按关键字排序
            if flag_sort_by:
                res = list()
                for row in self.cursor.execute(select_sql):
                    dist = great_circle(
                        (row["Latitude"], row["Longitude"]), (lat, lng))
                    if dist <= radius:
                        res.append(Zipcode(**row))
                        if len(res) == returns:
                            return res
            # no sort by keyword, then sort from cloest to farturest
            # 没有sort_by关键字, 按距离远近排序
            else:
                # use heap sort find top N closest zipcode
                def gen():
                    for row in self.cursor.execute(select_sql):
                        dist = great_circle(
                            (row["Latitude"], row["Longitude"]), (lat, lng))
                        if dist <= radius:
                            yield (dist, row)

                if returns >= 1:

                    if ascending:
                        data = nsmallest(returns, gen(), key=lambda x: x[0])
                    else:
                        data = nlargest(returns, gen(), key=lambda x: x[0])
                else:
                    if ascending:
                        data = sorted(
                            gen(), key=lambda x: x[0], reverse=not ascending)

                res = [Zipcode(**row) for dist, row in data]
        else:
            select_sql = self._sql_modify_limit(select_sql, returns)
            res = [Zipcode(**row) for row in self.cursor.execute(select_sql)]

        return res