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
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)
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