function privatesquare_checkins_utils_geo_stats($checkins, $more = array())
{
    $defaults = array('enbiggen_bbox' => 1);
    $more = array_merge($defaults, $more);
    if (count($checkins) == 1) {
        $lat = $checkins[0]['latitude'];
        $lon = $checkins[0]['longitude'];
        $bbox = geo_utils_bbox_from_point($lat, $lon, 1);
        return array('centroid' => array($lat, $lon), 'bounding_box' => $bbox);
    }
    $swlat = null;
    $swlon = null;
    $nelat = null;
    $nelon = null;
    foreach ($checkins as $row) {
        $lat = $row['latitude'];
        $lon = $row['longitude'];
        $swlat = isset($swlat) ? min($swlat, $lat) : $lat;
        $swlon = isset($swlon) ? min($swlon, $lon) : $lon;
        $nelat = isset($nelat) ? max($nelat, $lat) : $lat;
        $nelon = isset($nelon) ? max($nelon, $lon) : $lon;
    }
    # typically so that when creating a map by extent we don't
    # crop items (dots) that hug the edge of the map/bbox
    if (isset($more['enbiggen_bbox'])) {
        $sw_bbox = geo_utils_bbox_from_point($swlat, $swlon, 0.5);
        $ne_bbox = geo_utils_bbox_from_point($nelat, $nelon, 0.5);
        $swlat = $sw_bbox[0];
        $swlon = $sw_bbox[1];
        $nelat = $ne_bbox[2];
        $nelon = $ne_bbox[3];
    }
    $ctr_lat = $swlat + ($nelat - $swlat) / 2;
    $ctr_lon = $swlon + ($nelon - $swlon) / 2;
    return array('centroid' => array($ctr_lat, $ctr_lon), 'bounding_box' => array($swlat, $swlon, $nelat, $nelon));
}
function api_flickr_photos_geo_possibleCorrections()
{
    $photo_id = get_int64("photo_id");
    $photo = _api_flickr_photos_geo_get_photo($photo_id);
    $type = get_str("place_type");
    if (!$type) {
        api_output_error(999, "Missing place type");
    }
    if (!flickr_places_is_valid_placetype($type)) {
        api_output_error(999, "Invalid place type");
    }
    # TO DO: calculate based on $type
    $radius = 1.5;
    $bbox = geo_utils_bbox_from_point($photo['latitude'], $photo['longitude'], $radius, 'km');
    $bbox = implode(",", array($bbox[1], $bbox[0], $bbox[3], $bbox[2]));
    $method = 'flickr.places.placesForBoundingBox';
    $args = array('bbox' => $bbox, 'place_type' => $type);
    $rsp = flickr_api_call($method, $args);
    if (!$rsp['ok']) {
        api_output_error(999, "Flickr API error");
    }
    $possible = array();
    if ($rsp['rsp']['places']['total']) {
        foreach ($rsp['rsp']['places']['place'] as $place) {
            $possible[] = array('woeid' => $place['woeid'], 'placetype' => $place['place_type'], 'name' => $place['_content']);
        }
    }
    $parent = flickr_places_parent_placetype($type);
    $out = array('place_type' => $type, 'parent_place_type' => $parent, 'places' => $possible);
    return api_output_ok($out);
}
function privatesquare_checkins_for_user_nearby(&$user, $lat, $lon, $more = array())
{
    loadlib("geo_utils");
    $dist = isset($more['dist']) ? floatval($more['dist']) : 0.2;
    $unit = geo_utils_is_valid_unit($more['unit']) ? $more['unit'] : 'm';
    # TO DO: sanity check to ensure max $dist
    $bbox = geo_utils_bbox_from_point($lat, $lon, $dist, $unit);
    $cluster_id = $user['cluster_id'];
    $enc_user = AddSlashes($user['id']);
    # TO DO: group by venue_id in memory since the following will always
    # result in a filesort (20120301/straup)
    $sql = "SELECT venue_id, COUNT(id) AS count FROM PrivatesquareCheckins WHERE user_id='{$enc_user}'";
    $sql .= " AND latitude BETWEEN {$bbox[0]} AND {$bbox[2]} AND longitude BETWEEN {$bbox[1]} AND {$bbox[3]}";
    $sql .= " GROUP BY venue_id";
    $rsp = db_fetch_users($cluster_id, $sql, $more);
    if (!$rsp['ok']) {
        return $rsp;
    }
    $tmp = array();
    foreach ($rsp['rows'] as $row) {
        $tmp[$row['venue_id']] = $row['count'];
    }
    arsort($tmp);
    $venues = array();
    foreach ($tmp as $venue_id => $count) {
        $venue = foursquare_venues_get_by_venue_id($venue_id);
        $venue['count_checkins'] = $count;
        $venues[] = $venue;
    }
    return okay(array('rows' => $venues));
}