예제 #1
0
    def aggregate_station_obs(self):
        station = self.station
        obs_data = self.obs_data

        def get_nan(name):
            value = getattr(station, name, None)
            return numpy.nan if value is None else value

        positions = numpy.append(
            obs_data["positions"],
            [
                (get_nan("lat"), get_nan("lon")),
                (get_nan("max_lat"), get_nan("max_lon")),
                (get_nan("min_lat"), get_nan("min_lon")),
            ],
            axis=0,
        )

        max_lat, max_lon = numpy.nanmax(positions, axis=0)
        min_lat, min_lon = numpy.nanmin(positions, axis=0)

        if station.lat is None or station.lon is None:
            old_weight = 0.0
        else:
            old_weight = min((station.weight or 0.0), self.MAX_OLD_WEIGHT)

        lat = (
            obs_data["lat"] * obs_data["weight"] + (station.lat or 0.0) * old_weight
        ) / (obs_data["weight"] + old_weight)
        lon = (
            obs_data["lon"] * obs_data["weight"] + (station.lon or 0.0) * old_weight
        ) / (obs_data["weight"] + old_weight)

        radius = circle_radius(lat, lon, max_lat, max_lon, min_lat, min_lon)
        region = station.region
        if region and not GEOCODER.in_region(lat, lon, region):
            # reset region if it no longer matches
            region = None
        if not region:
            region = GEOCODER.region(lat, lon)

        samples, weight = self.bounded_samples_weight(
            (station.samples or 0) + obs_data["samples"],
            (station.weight or 0.0) + obs_data["weight"],
        )

        return {
            "lat": lat,
            "lon": lon,
            "max_lat": float(max_lat),
            "min_lat": float(min_lat),
            "max_lon": float(max_lon),
            "min_lon": float(min_lon),
            "radius": radius,
            "region": region,
            "samples": samples,
            "weight": weight,
        }
    def aggregate_station_obs(self):
        station = self.station
        obs_data = self.obs_data

        def get_nan(name):
            value = getattr(station, name, None)
            return numpy.nan if value is None else value

        positions = numpy.append(obs_data['positions'], [
            (get_nan('lat'), get_nan('lon')),
            (get_nan('max_lat'), get_nan('max_lon')),
            (get_nan('min_lat'), get_nan('min_lon')),
        ],
                                 axis=0)

        max_lat, max_lon = numpy.nanmax(positions, axis=0)
        min_lat, min_lon = numpy.nanmin(positions, axis=0)

        if station.lat is None or station.lon is None:
            old_weight = 0.0
        else:
            old_weight = min((station.weight or 0.0), self.MAX_OLD_WEIGHT)

        lat = ((obs_data['lat'] * obs_data['weight'] +
                (station.lat or 0.0) * old_weight) /
               (obs_data['weight'] + old_weight))
        lon = ((obs_data['lon'] * obs_data['weight'] +
                (station.lon or 0.0) * old_weight) /
               (obs_data['weight'] + old_weight))

        radius = circle_radius(lat, lon, max_lat, max_lon, min_lat, min_lon)
        region = station.region
        if (region and not GEOCODER.in_region(lat, lon, region)):
            # reset region if it no longer matches
            region = None
        if not region:
            region = GEOCODER.region(lat, lon)

        samples, weight = self.bounded_samples_weight(
            (station.samples or 0) + obs_data['samples'],
            (station.weight or 0.0) + obs_data['weight'])

        return {
            'lat': lat,
            'lon': lon,
            'max_lat': float(max_lat),
            'min_lat': float(min_lat),
            'max_lon': float(max_lon),
            'min_lon': float(min_lon),
            'radius': radius,
            'region': region,
            'samples': samples,
            'weight': weight,
        }
예제 #3
0
    def aggregate_station_obs(self):
        station = self.station
        obs_data = self.obs_data

        def get_nan(name):
            value = getattr(station, name, None)
            return numpy.nan if value is None else value

        positions = numpy.append(obs_data['positions'], [
            (get_nan('lat'), get_nan('lon')),
            (get_nan('max_lat'), get_nan('max_lon')),
            (get_nan('min_lat'), get_nan('min_lon')),
        ], axis=0)

        max_lat, max_lon = numpy.nanmax(positions, axis=0)
        min_lat, min_lon = numpy.nanmin(positions, axis=0)

        if station.lat is None or station.lon is None:
            old_weight = 0.0
        else:
            old_weight = min((station.weight or 0.0), self.MAX_OLD_WEIGHT)

        lat = ((obs_data['lat'] * obs_data['weight'] +
                (station.lat or 0.0) * old_weight) /
               (obs_data['weight'] + old_weight))
        lon = ((obs_data['lon'] * obs_data['weight'] +
                (station.lon or 0.0) * old_weight) /
               (obs_data['weight'] + old_weight))

        radius = circle_radius(lat, lon, max_lat, max_lon, min_lat, min_lon)
        region = station.region
        if (region and not GEOCODER.in_region(lat, lon, region)):
            # reset region if it no longer matches
            region = None
        if not region:
            region = GEOCODER.region(lat, lon)

        samples, weight = self.bounded_samples_weight(
            (station.samples or 0) + obs_data['samples'],
            (station.weight or 0.0) + obs_data['weight'])

        return {
            'lat': lat, 'lon': lon,
            'max_lat': float(max_lat), 'min_lat': float(min_lat),
            'max_lon': float(max_lon), 'min_lon': float(min_lon),
            'radius': radius, 'region': region,
            'samples': samples, 'weight': weight,
        }
예제 #4
0
파일: station.py 프로젝트: ingle/ichnaea
    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 = self._base_station_values(station_key, observations)

        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,
                    'radius':
                    None,
                    'region':
                    shard_station.region,
                    'samples':
                    None,
                    'source':
                    None,
                    'block_first':
                    shard_station.block_first or self.today,
                    '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),
                'radius': radius,
                'region': GEOCODER.region(obs_new_lat, obs_new_lon),
                '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,
                    'radius':
                    None,
                    'region':
                    shard_station.region,
                    'samples':
                    None,
                    'source':
                    None,
                    'block_first':
                    shard_station.block_first or self.today,
                    '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)
                region = shard_station.region
                if (region
                        and not GEOCODER.in_region(new_lat, new_lon, region)):
                    # reset region if it no longer matches
                    region = None
                if not region:
                    region = GEOCODER.region(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),
                    'radius': radius,
                    'region': region,
                    'samples': samples,
                    'source': None,
                    # use the exact same keys as in the moving case
                    'block_first': shard_station.block_first,
                    'block_last': shard_station.block_last,
                    'block_count': shard_station.block_count,
                })
                return ('changed', values)

        return (None, None)  # pragma: no cover
예제 #5
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 = self._base_station_values(station_key, observations)

        obs_positions = numpy.array(
            [(obs.lat, obs.lon) for obs in observations],
            dtype=numpy.double)
        obs_length = len(observations)

        obs_weights = numpy.array(
            [obs.weight for obs in observations],
            dtype=numpy.double)
        obs_weight = float(obs_weights.sum())

        obs_new_lat, obs_new_lon = numpy.average(
            obs_positions, axis=0, weights=obs_weights)
        obs_new_lat = float(obs_new_lat)
        obs_new_lon = float(obs_new_lon)

        obs_max_lat, obs_max_lon = obs_positions.max(axis=0)
        obs_min_lat, obs_min_lon = obs_positions.min(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,
                    'radius': None,
                    'region': shard_station.region,
                    'samples': None,
                    'source': None,
                    'weight': None,
                    'block_first': shard_station.block_first or self.today,
                    '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),
                'radius': radius,
                'region': GEOCODER.region(obs_new_lat, obs_new_lon),
                'samples': obs_length,
                'source': None,
                'weight': obs_weight,
            })
            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,
                    'radius': None,
                    'region': shard_station.region,
                    'samples': None,
                    'source': None,
                    'weight': None,
                    'block_first': shard_station.block_first or self.today,
                    '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.weight or 0.0),
                                     self.MAX_OLD_WEIGHT)

                new_lat = ((obs_new_lat * obs_weight +
                            (shard_station.lat or 0.0) * old_weight) /
                           (obs_weight + old_weight))
                new_lon = ((obs_new_lon * obs_weight +
                            (shard_station.lon or 0.0) * old_weight) /
                           (obs_weight + old_weight))

                # put in maximum value to avoid overflow of DB column
                samples = min((shard_station.samples or 0) + obs_length,
                              4294967295)
                weight = min((shard_station.weight or 0.0) + obs_weight,
                             1000000000.0)

                radius = circle_radius(
                    new_lat, new_lon, max_lat, max_lon, min_lat, min_lon)
                region = shard_station.region
                if (region and not GEOCODER.in_region(
                        new_lat, new_lon, region)):
                    # reset region if it no longer matches
                    region = None
                if not region:
                    region = GEOCODER.region(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),
                    'radius': radius,
                    'region': region,
                    'samples': samples,
                    'source': None,
                    'weight': weight,
                    # use the exact same keys as in the moving case
                    'block_first': shard_station.block_first,
                    'block_last': shard_station.block_last,
                    'block_count': shard_station.block_count,
                })
                return ('changed', values)

        return (None, None)  # pragma: no cover