Example #1
0
def cluster_areas(areas, lookups, min_age=0):
    """
    Cluster areas, treat each area as its own cluster.
    """
    now = util.utcnow()
    today = now.date()

    # Create a dict of area ids mapped to their age and signal strength.
    obs_data = {}
    for lookup in lookups:
        obs_data[decode_cellarea(lookup.areaid)] = (
            max(abs(lookup.age or min_age), 1000),
            lookup.signalStrength or MIN_CELL_SIGNAL[lookup.radioType])

    clusters = []
    for area in areas:
        clusters.append(numpy.array([(
            area.lat, area.lon, area.radius,
            obs_data[area.areaid][0],
            obs_data[area.areaid][1],
            area_score(area, now),
            encode_cellarea(*area.areaid),
            bool(area.last_seen >= today))],
            dtype=NETWORK_DTYPE))

    return clusters
Example #2
0
def cluster_areas(areas, lookups, min_age=0):
    """
    Cluster areas, treat each area as its own cluster.
    """
    now = util.utcnow()
    today = now.date()

    # Create a dict of area ids mapped to their age and signal strength.
    obs_data = {}
    for lookup in lookups:
        obs_data[decode_cellarea(lookup.areaid)] = (
            max(abs(lookup.age or min_age), 1000),
            lookup.signalStrength or MIN_CELL_SIGNAL[lookup.radioType],
        )

    clusters = []
    for area in areas:
        clusters.append(
            numpy.array(
                [(
                    area.lat,
                    area.lon,
                    area.radius,
                    obs_data[area.areaid][0],
                    obs_data[area.areaid][1],
                    area_score(area, now),
                    encode_cellarea(*area.areaid, codec="base64"),
                    bool(area.last_seen is not None
                         and area.last_seen >= today),
                )],
                dtype=NETWORK_DTYPE,
            ))

    return clusters
Example #3
0
    def update_areas(self, areaids):
        """Update the cell areas based on cell station records."""
        areaid_set = set(areaids)
        with self.task.db_session() as session:
            # Fetch and lock existing cellarea rows
            rows = session.execute(
                select([self.area_table.c.areaid])
                .where(self.area_table.c.areaid.in_(areaid_set))
                .with_for_update()
            ).fetchall()
            existing_areas = set(row[0] for row in rows)

            # Update each cellarea row from cell tables
            for areaid in areaid_set:
                area_is_new = decode_cellarea(areaid) not in existing_areas
                self.update_area(session, areaid, area_is_new)
Example #4
0
    def update_area(self, areaid):
        # Select all cells in this area and derive a bounding box for them
        radio, mcc, mnc, lac = decode_cellarea(areaid)
        load_fields = ('lat', 'lon', 'radius', 'region',
                       'max_lat', 'max_lon', 'min_lat', 'min_lon')

        shard = self.cell_model.shard_model(radio)
        cells = (self.session.query(shard)
                             .options(load_only(*load_fields))
                             .filter(shard.radio == radio)
                             .filter(shard.mcc == mcc)
                             .filter(shard.mnc == mnc)
                             .filter(shard.lac == lac)
                             .filter(shard.lat.isnot(None))
                             .filter(shard.lon.isnot(None))).all()

        area_query = (self.session.query(self.area_model)
                                  .filter(self.area_model.areaid == areaid))

        if len(cells) == 0:
            # If there are no more underlying cells, delete the area entry
            area_query.delete()
        else:
            # Otherwise update the area entry based on all the cells
            area = area_query.first()

            cell_extremes = numpy.array([
                (numpy.nan if cell.max_lat is None else cell.max_lat,
                 numpy.nan if cell.max_lon is None else cell.max_lon)
                for cell in cells] + [
                (numpy.nan if cell.min_lat is None else cell.min_lat,
                 numpy.nan if cell.min_lon is None else cell.min_lon)
                for cell in cells
            ], dtype=numpy.double)

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

            ctr_lat, ctr_lon = centroid(
                numpy.array([(c.lat, c.lon) for c in cells],
                            dtype=numpy.double))
            radius = circle_radius(
                ctr_lat, ctr_lon, max_lat, max_lon, min_lat, min_lon)

            cell_radii = numpy.array([
                (numpy.nan if cell.radius is None else cell.radius)
                for cell in cells
            ], dtype=numpy.int32)
            avg_cell_radius = int(round(numpy.nanmean(cell_radii)))
            num_cells = len(cells)
            region = self.region(ctr_lat, ctr_lon, mcc, cells)

            if area is None:
                stmt = self.area_model.__table__.insert(
                    mysql_on_duplicate='num_cells = num_cells'  # no-op
                ).values(
                    areaid=areaid,
                    radio=radio,
                    mcc=mcc,
                    mnc=mnc,
                    lac=lac,
                    created=self.utcnow,
                    modified=self.utcnow,
                    lat=ctr_lat,
                    lon=ctr_lon,
                    radius=radius,
                    region=region,
                    avg_cell_radius=avg_cell_radius,
                    num_cells=num_cells,
                )
                self.session.execute(stmt)
            else:
                area.modified = self.utcnow
                area.lat = ctr_lat
                area.lon = ctr_lon
                area.radius = radius
                area.region = region
                area.avg_cell_radius = avg_cell_radius
                area.num_cells = num_cells
Example #5
0
    def update_area(self, session, areaid, area_is_new):
        # Select all cells in this area and derive a bounding box for them
        radio, mcc, mnc, lac = decode_cellarea(areaid)
        load_fields = (
            "cellid",
            "lat",
            "lon",
            "radius",
            "region",
            "last_seen",
            "max_lat",
            "max_lon",
            "min_lat",
            "min_lon",
        )

        shard = self.cell_model.shard_model(radio)
        fields = [getattr(shard.__table__.c, f) for f in load_fields]

        cells = session.execute(
            select(fields)
            .where(shard.__table__.c.radio == radio)
            .where(shard.__table__.c.mcc == mcc)
            .where(shard.__table__.c.mnc == mnc)
            .where(shard.__table__.c.lac == lac)
            .where(shard.__table__.c.lat.isnot(None))
            .where(shard.__table__.c.lon.isnot(None))
        ).fetchall()

        if len(cells) == 0:
            # If there are no more underlying cells, delete the area entry
            session.execute(
                delete(self.area_table).where(self.area_table.c.areaid == areaid)
            )
            return

        # Otherwise update the area entry based on all the cells
        cell_extremes = numpy.array(
            [
                (
                    numpy.nan if cell.max_lat is None else cell.max_lat,
                    numpy.nan if cell.max_lon is None else cell.max_lon,
                )
                for cell in cells
            ]
            + [
                (
                    numpy.nan if cell.min_lat is None else cell.min_lat,
                    numpy.nan if cell.min_lon is None else cell.min_lon,
                )
                for cell in cells
            ],
            dtype=numpy.double,
        )

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

        ctr_lat, ctr_lon = numpy.array(
            [(c.lat, c.lon) for c in cells], dtype=numpy.double
        ).mean(axis=0)
        ctr_lat = float(ctr_lat)
        ctr_lon = float(ctr_lon)

        radius = circle_radius(ctr_lat, ctr_lon, max_lat, max_lon, min_lat, min_lon)

        cell_radii = numpy.array(
            [(numpy.nan if cell.radius is None else cell.radius) for cell in cells],
            dtype=numpy.int32,
        )
        avg_cell_radius = int(round(numpy.nanmean(cell_radii)))
        num_cells = len(cells)
        region = self.region(ctr_lat, ctr_lon, mcc, cells)

        last_seen = None
        cell_last_seen = set(
            [cell.last_seen for cell in cells if cell.last_seen is not None]
        )
        if cell_last_seen:
            last_seen = max(cell_last_seen)

        if area_is_new:
            session.execute(
                self.area_table.insert().values(
                    areaid=areaid,
                    radio=radio,
                    mcc=mcc,
                    mnc=mnc,
                    lac=lac,
                    created=self.utcnow,
                    modified=self.utcnow,
                    lat=ctr_lat,
                    lon=ctr_lon,
                    radius=radius,
                    region=region,
                    avg_cell_radius=avg_cell_radius,
                    num_cells=num_cells,
                    last_seen=last_seen,
                )
                # If there was an unexpected insert, log warning instead of error
                .prefix_with("IGNORE", dialect="mysql")
            )
        else:
            session.execute(
                self.area_table.update()
                .where(self.area_table.c.areaid == areaid)
                .values(
                    modified=self.utcnow,
                    lat=ctr_lat,
                    lon=ctr_lon,
                    radius=radius,
                    region=region,
                    avg_cell_radius=avg_cell_radius,
                    num_cells=num_cells,
                    last_seen=last_seen,
                )
            )
Example #6
0
    def update_area(self, session, areaid):
        # Select all cells in this area and derive a bounding box for them
        radio, mcc, mnc, lac = decode_cellarea(areaid)
        load_fields = ('cellid', 'lat', 'lon', 'radius', 'region', 'last_seen',
                       'max_lat', 'max_lon', 'min_lat', 'min_lon')

        shard = self.cell_model.shard_model(radio)
        fields = [getattr(shard.__table__.c, f) for f in load_fields]

        cells = session.execute(
            select(fields)
            .where(shard.__table__.c.radio == radio)
            .where(shard.__table__.c.mcc == mcc)
            .where(shard.__table__.c.mnc == mnc)
            .where(shard.__table__.c.lac == lac)
            .where(shard.__table__.c.lat.isnot(None))
            .where(shard.__table__.c.lon.isnot(None))
        ).fetchall()

        if len(cells) == 0:
            # If there are no more underlying cells, delete the area entry
            session.execute(
                delete(self.area_table)
                .where(self.area_table.c.areaid == areaid)
            )
            return

        # Otherwise update the area entry based on all the cells
        area = session.execute(
            select([self.area_table.c.areaid,
                    self.area_table.c.modified,
                    self.area_table.c.lat,
                    self.area_table.c.lon,
                    self.area_table.c.radius,
                    self.area_table.c.region,
                    self.area_table.c.avg_cell_radius,
                    self.area_table.c.num_cells,
                    self.area_table.c.last_seen,
                    ])
            .where(self.area_table.c.areaid == areaid)
        ).fetchone()

        cell_extremes = numpy.array([
            (numpy.nan if cell.max_lat is None else cell.max_lat,
             numpy.nan if cell.max_lon is None else cell.max_lon)
            for cell in cells] + [
            (numpy.nan if cell.min_lat is None else cell.min_lat,
             numpy.nan if cell.min_lon is None else cell.min_lon)
            for cell in cells
        ], dtype=numpy.double)

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

        ctr_lat, ctr_lon = numpy.array(
            [(c.lat, c.lon) for c in cells],
            dtype=numpy.double).mean(axis=0)
        ctr_lat = float(ctr_lat)
        ctr_lon = float(ctr_lon)

        radius = circle_radius(
            ctr_lat, ctr_lon, max_lat, max_lon, min_lat, min_lon)

        cell_radii = numpy.array([
            (numpy.nan if cell.radius is None else cell.radius)
            for cell in cells
        ], dtype=numpy.int32)
        avg_cell_radius = int(round(numpy.nanmean(cell_radii)))
        num_cells = len(cells)
        region = self.region(ctr_lat, ctr_lon, mcc, cells)

        last_seen = None
        cell_last_seen = set([cell.last_seen for cell in cells
                              if cell.last_seen is not None])
        if cell_last_seen:
            last_seen = max(cell_last_seen)

        if area is None:
            session.execute(
                self.area_table.insert(
                    mysql_on_duplicate='num_cells = num_cells'  # no-op
                ).values(
                    areaid=areaid,
                    radio=radio,
                    mcc=mcc,
                    mnc=mnc,
                    lac=lac,
                    created=self.utcnow,
                    modified=self.utcnow,
                    lat=ctr_lat,
                    lon=ctr_lon,
                    radius=radius,
                    region=region,
                    avg_cell_radius=avg_cell_radius,
                    num_cells=num_cells,
                    last_seen=last_seen,
                )
            )
        else:
            session.execute(
                self.area_table.update()
                .where(self.area_table.c.areaid == areaid)
                .values(
                    modified=self.utcnow,
                    lat=ctr_lat,
                    lon=ctr_lon,
                    radius=radius,
                    region=region,
                    avg_cell_radius=avg_cell_radius,
                    num_cells=num_cells,
                    last_seen=last_seen,
                )
            )