Example #1
0
 def test_multiple(self):
     self.assertEqual(country_for_location(31.522, 34.455), None)
Example #2
0
 def test_no_match(self):
     self.assertEqual(country_for_location(0.0, 0.0), None)
Example #3
0
 def test_single(self):
     self.assertEqual(country_for_location(51.5142, -0.0931), 'GB')
Example #4
0
    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