def test_multiple(self): self.assertEqual(country_for_location(31.522, 34.455), None)
def test_no_match(self): self.assertEqual(country_for_location(0.0, 0.0), None)
def test_single(self): self.assertEqual(country_for_location(51.5142, -0.0931), 'GB')
def station_values(self, station_key, shard_station, observations): """ Return two-tuple of status, value dict where status is one of: `new`, `new_moving`, `moving`, `changed`. """ # cases: # we always get a station key and observations # 0. observations disagree # 0.a. no shard station, return new_moving # 0.b. shard station, return moving # 1. no shard station # 1.a. obs agree -> return new # 2. shard station # 2.a. obs disagree -> return moving # 2.b. obs agree -> return changed created = self.utcnow values = { 'mac': station_key, 'modified': self.utcnow, } obs_length = len(observations) obs_positions = numpy.array( [(obs.lat, obs.lon) for obs in observations], dtype=numpy.double) obs_new_lat, obs_new_lon = centroid(obs_positions) obs_max_lat, obs_max_lon = numpy.nanmax(obs_positions, axis=0) obs_min_lat, obs_min_lon = numpy.nanmin(obs_positions, axis=0) obs_box_dist = distance(obs_min_lat, obs_min_lon, obs_max_lat, obs_max_lon) if obs_box_dist > self.max_dist_meters: # the new observations are already too far apart if not shard_station: values.update({ 'created': created, 'block_first': self.today, 'block_last': self.today, 'block_count': 1, }) return ('new_moving', values) else: block_count = shard_station.block_count or 0 values.update({ 'lat': None, 'lon': None, 'max_lat': None, 'min_lat': None, 'max_lon': None, 'min_lon': None, 'country': shard_station.country, 'radius': None, 'samples': None, 'source': None, 'block_last': self.today, 'block_count': block_count + 1, }) return ('moving', values) if shard_station is None: # totally new station, only agreeing observations radius = circle_radius( obs_new_lat, obs_new_lon, obs_max_lat, obs_max_lon, obs_min_lat, obs_min_lon) values.update({ 'created': created, 'lat': obs_new_lat, 'lon': obs_new_lon, 'max_lat': float(obs_max_lat), 'min_lat': float(obs_min_lat), 'max_lon': float(obs_max_lon), 'min_lon': float(obs_min_lon), 'country': country_for_location(obs_new_lat, obs_new_lon), 'radius': radius, 'samples': obs_length, 'source': None, }) return ('new', values) else: # shard_station + new observations positions = numpy.append(obs_positions, [ (numpy.nan if shard_station.lat is None else shard_station.lat, numpy.nan if shard_station.lon is None else shard_station.lon), (numpy.nan if shard_station.max_lat is None else shard_station.max_lat, numpy.nan if shard_station.max_lon is None else shard_station.max_lon), (numpy.nan if shard_station.min_lat is None else shard_station.min_lat, numpy.nan if shard_station.min_lon is None else shard_station.min_lon), ], axis=0) max_lat, max_lon = numpy.nanmax(positions, axis=0) min_lat, min_lon = numpy.nanmin(positions, axis=0) box_dist = distance(min_lat, min_lon, max_lat, max_lon) if box_dist > self.max_dist_meters: # shard_station + disagreeing observations block_count = shard_station.block_count or 0 values.update({ 'lat': None, 'lon': None, 'max_lat': None, 'min_lat': None, 'max_lon': None, 'min_lon': None, 'country': shard_station.country, 'radius': None, 'samples': None, 'source': None, 'block_last': self.today, 'block_count': block_count + 1, }) return ('moving', values) else: # shard_station + agreeing observations if shard_station.lat is None or shard_station.lon is None: old_weight = 0 else: old_weight = min((shard_station.samples or 0), self.MAX_OLD_OBSERVATIONS) new_lat = ((obs_new_lat * obs_length + (shard_station.lat or 0.0) * old_weight) / (obs_length + old_weight)) new_lon = ((obs_new_lon * obs_length + (shard_station.lon or 0.0) * old_weight) / (obs_length + old_weight)) samples = (shard_station.samples or 0) + obs_length radius = circle_radius( new_lat, new_lon, max_lat, max_lon, min_lat, min_lon) country = shard_station.country if (country and not country_matches_location( new_lat, new_lon, country)): # reset country if it no longer matches country = None if not country: country = country_for_location(new_lat, new_lon) values.update({ 'lat': new_lat, 'lon': new_lon, 'max_lat': float(max_lat), 'min_lat': float(min_lat), 'max_lon': float(max_lon), 'min_lon': float(min_lon), 'country': country, 'radius': radius, 'samples': samples, 'source': None, # use the exact same keys as in the moving case 'block_last': shard_station.block_last, 'block_count': shard_station.block_count, }) return ('changed', values) return (None, None) # pragma: no cover