def _run_trilateration(self, rounding_classes):
        """Runs a variance of the trilateration attack on the search_area

        Args:
            rouding_classes: a list of intervals with the radiuses

        Returns:
            A polygon in projected coordinates containing the target
            """

        vb.vb_print(self.verbose,
                    "Running trilateration",
                    "UDP",
                    True)
        # get minimum radius in the rounding classes
        MIN_R = sorted([cl[0] for cl in rounding_classes])[0][0]

        #initially the intersection is the whole area
        inter = self.search_area
        # place the attacker in the center
        if self.attacker.loc is None or self.attacker.loc[0] is None:
            starting_loc = cells.poly_centroid(inter, self.proj)
            self._place_at_coords(self.attacker,
                                  starting_loc[0],
                                  starting_loc[1],
                                  self.test_id)

        if self.oracle is None:
            self.oracle = apo.RoundingProximityOracle(self.auditor,
                                                      rounding_classes,
                                                      self.verbose)

        # initially get a rough estimate with 3 queries from
        # three locations at a distance of 120 degrees.
        # Initiate intersection rings
        rings = []
        for i in range(3):
            # get a ring and check if we are switching to binary
            ring_response = self.__get_ring(MIN_R)
            if ring_response is None:
                raise SystemExit("victim not found")
            else:
                distance_range, ring = ring_response
            self._log_kml("ring", ring)
            rings.append(ring)

            #XXX no need to check for multipolygon in case of DUDP
            # as we are working inside a polygonal area
            inter_new = None
            if inter.geom_type == "MultiPolygon":
                for p in inter:
                    cut_inter = p.intersection(ring)
                    if inter_new is None:
                        inter_new = cut_inter
                    else:
                        inter_new = inter_new.union(cut_inter)
            else:
                inter_new = inter.intersection(ring)

            # update the intersection
            inter = ring if inter_new.is_empty else inter_new

            # log kml files
            self.json_out["RUDP"].append({"query": self.attack_queries,
                                          "ring": self._log_kml("ring", ring),
                                          "active_area": self._log_kml("inter",
                                                                       inter),
                                         })

            # update attacker location
            dist = float(distance_range[0] + distance_range[1]) / 2
            # calculate new location for the next query
            new_loc = earth.point_on_earth(self.attacker.loc[0],
                                           self.attacker.loc[1],
                                           dist,
                                           i * 120)

            self._place_at_coords(self.attacker,
                                  new_loc[0],
                                  new_loc[1],
                                  self.test_id)

        # switch to binary
        return inter
    def auditor_handled_place_at_dist(self, user, dist, bear, test_id,
                                      query_id=None):
        """Place a user of the auditing testsuite in a distance @dist km from
        their current location, at a bearing of @bearing, if it is allowed by
        the speed constraints.

        Notice that @auditor_user is not the user passed by the inherited class
        but an instance of the AuditorUser class used by the auditing framework

        Args:
            user: the user to be placed in a new location (AuditorUser instance)
            dist: distance further from the user's location in km
            bear: bearing in degrees
            test: the id of the test being run
            query: the query id for the current query

        Return Value:
            Returns a tuple (@result, @queries) where @result is True or False
            depending on whether the location was updated successfully or not.
            @queries is the total queries towards the service required to
            perform the update.

        Raises:
            AuditorException(with optional log data) in case an error occurs.
        """

        # get the max distance the user is allowed to travel
        max_distance = self._get_max_distance(user)

        # If we have a speed limit and the distance is bigger than what
        # we are allowed to cross, sleep until we are allowed
        if dist > max_distance:
            sleep_time = (dist - max_distance) / self.speed_limit
            sleep(sleep_time + 1)

        # find new position at distance and angle
        new_pos = earth.point_on_earth(user.loc[0],
                                       user.loc[1],
                                       dist,
                                       bear)

        try:
            # given that the clocks won't change and we don't
            # run stuff in parallel, have time as primary key
            if query_id is None:
                query_id = int(time())

            # if we have full logging create query record
            # create it here in case any exception is raised
            if self.logging == const.LOG.ALL:
                query_info = "auditor_set_location ["
                query_info += str(new_pos[0]) + "," + str(new_pos[1]) + "]"
                self._db.insert_query(query_id,
                                     test_id,
                                     user.user_id,
                                     user.service_id,
                                     query_info)

            set_loc_rspn = self.auditor_set_location(user.user,
                                                     new_pos[0],
                                                     new_pos[1])
            if len(set_loc_rspn) != 2:
                raise SystemExit("auditor_set_location must return a tuple!")

            sleep(5)
            (result, queries) = set_loc_rspn

            if not isinstance(result, bool) and not isinstance(queries, int):
                error = "Wrong return type: Expecting (bool, int) or None"
                raise TypeError(error)

            # if no exception was raised but we failed log it
            if result is False and self.logging == const.LOG.ALL:
                self._db.log_query_fail(query_id)

        except AuditorException:
            if self.logging == const.LOG.ALL:
                self._db.log_query_fail(query_id)
                # handle any data that has been passed by the user
                self._db.exception_recovery(query_id)
            return (False, 1)
        except Exception as exception:
            # remove user from active users
            self.users.remove(user.user)
            self._db.log_query_fail(query_id)
            # else raise exception and record failure
            raise AuditorExceptionUnknown(str(exception), user.user_id)

        # update user info
        user.update_queries(queries)
        user.loc = new_pos

        return (result, queries)