Home | History | Annotate | Download | only in services
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package android.bordeaux.services;
     17 import android.location.Location;
     18 import android.text.format.Time;
     19 import android.util.Log;
     20 
     21 import java.lang.Math;
     22 import java.util.ArrayList;
     23 import java.util.HashMap;
     24 import java.util.Map;
     25 
     26 public class BaseCluster {
     27 
     28     public static String TAG = "BaseCluster";
     29 
     30     public double[] mCenter;
     31   // protected double[] mCenter;
     32 
     33     protected static final int VECTOR_LENGTH = 3;
     34 
     35     private static final long FORGETTING_ENUMERATOR = 95;
     36     private static final long FORGETTING_DENOMINATOR = 100;
     37 
     38     // Histogram illustrates the pattern of visit during time of day,
     39     protected HashMap<String, Long> mHistogram = new HashMap<String, Long>();
     40 
     41     protected long mDuration;
     42 
     43     protected String mSemanticId;
     44 
     45     protected static final double EARTH_RADIUS = 6378100f;
     46 
     47     public BaseCluster(Location location) {
     48         mCenter = getLocationVector(location);
     49         mDuration = 0;
     50     }
     51 
     52     public BaseCluster(String semanticId, double longitude, double latitude,
     53                      long duration) {
     54         mSemanticId = semanticId;
     55         mCenter = getLocationVector(longitude, latitude);
     56         mDuration = duration;
     57     }
     58 
     59     public String getSemanticId() {
     60         return mSemanticId;
     61     }
     62 
     63     public void generateSemanticId(long index) {
     64         mSemanticId = "cluser: " + String.valueOf(index);
     65     }
     66 
     67     public long getDuration() {
     68         return mDuration;
     69     }
     70 
     71     public boolean hasSemanticId() {
     72         return mSemanticId != null;
     73     }
     74 
     75     protected double[] getLocationVector(Location location) {
     76         return getLocationVector(location.getLongitude(), location.getLatitude());
     77     }
     78 
     79     protected double[] getLocationVector(double longitude, double latitude) {
     80         double vector[] = new double[VECTOR_LENGTH];
     81         double lambda = Math.toRadians(longitude);
     82         double phi = Math.toRadians(latitude);
     83 
     84         vector[0] = Math.cos(lambda) * Math.cos(phi);
     85         vector[1] = Math.sin(lambda) * Math.cos(phi);
     86         vector[2] = Math.sin(phi);
     87         return vector;
     88     }
     89 
     90     protected double getCenterLongitude() {
     91         // Because latitude ranges from -90 to 90 degrees, cosPhi >= 0.
     92         double cosPhi = Math.cos(Math.asin(mCenter[2]));
     93         double longitude = Math.toDegrees(Math.asin(mCenter[1] / cosPhi));
     94         if (mCenter[0] < 0) {
     95             longitude = (longitude > 0) ? 180f - longitude : -180 - longitude;
     96         }
     97         return longitude;
     98     }
     99 
    100     protected double getCenterLatitude() {
    101         return Math.toDegrees(Math.asin(mCenter[2]));
    102     }
    103 
    104     private double computeDistance(double[] vector1, double[] vector2) {
    105         double product = 0f;
    106         for (int i = 0; i < VECTOR_LENGTH; ++i) {
    107             product += vector1[i] * vector2[i];
    108         }
    109         double radian = Math.acos(Math.min(product, 1f));
    110         return radian * EARTH_RADIUS;
    111     }
    112 
    113     /*
    114      * This computes the distance from loation to the cluster center in meter.
    115      */
    116     public float distanceToCenter(Location location) {
    117         return (float) computeDistance(mCenter, getLocationVector(location));
    118     }
    119 
    120     public float distanceToCluster(BaseCluster cluster) {
    121         return (float) computeDistance(mCenter, cluster.mCenter);
    122     }
    123 
    124     public void absorbCluster(BaseCluster cluster) {
    125         averageCenter(cluster.mCenter, cluster.mDuration);
    126         absorbHistogram(cluster);
    127     }
    128 
    129     public void setCluster(BaseCluster cluster) {
    130         for (int i = 0; i < VECTOR_LENGTH; ++i) {
    131             mCenter[i] = cluster.mCenter[i];
    132         }
    133         mHistogram.clear();
    134         mHistogram.putAll(cluster.mHistogram);
    135         mDuration = cluster.mDuration;
    136     }
    137 
    138     private void absorbHistogram(BaseCluster cluster) {
    139         for (Map.Entry<String, Long> entry : cluster.mHistogram.entrySet()) {
    140             String timeLabel = entry.getKey();
    141             long duration = entry.getValue();
    142 
    143             if (mHistogram.containsKey(timeLabel)) {
    144                 duration += mHistogram.get(timeLabel);
    145             }
    146             mHistogram.put(timeLabel, duration);
    147         }
    148         mDuration += cluster.mDuration;
    149     }
    150 
    151     public boolean passThreshold(long durationThreshold) {
    152         // TODO: might want to keep semantic cluster
    153         return mDuration > durationThreshold;
    154     }
    155 
    156     public final HashMap<String, Long> getHistogram() {
    157         return mHistogram;
    158     }
    159 
    160     public void setHistogram(Map<String, Long> histogram) {
    161         mHistogram.clear();
    162         mHistogram.putAll(histogram);
    163 
    164         mDuration = 0;
    165         if (mHistogram.containsKey(TimeStatsAggregator.WEEKEND)) {
    166             mDuration += mHistogram.get(TimeStatsAggregator.WEEKEND);
    167         }
    168         if (mHistogram.containsKey(TimeStatsAggregator.WEEKDAY)) {
    169             mDuration += mHistogram.get(TimeStatsAggregator.WEEKDAY);
    170         }
    171     }
    172 
    173     public void forgetPastHistory() {
    174         mDuration *= FORGETTING_ENUMERATOR;
    175         mDuration /= FORGETTING_DENOMINATOR;
    176 
    177         for (Map.Entry<String, Long> entry : mHistogram.entrySet()) {
    178             String key = entry.getKey();
    179             long value = entry.getValue();
    180 
    181             value *= FORGETTING_ENUMERATOR;
    182             value /= FORGETTING_DENOMINATOR;
    183 
    184             mHistogram.put(key, value);
    185         }
    186     }
    187 
    188     protected void normalizeCenter() {
    189         double norm = 0;
    190         for (int i = 0; i < VECTOR_LENGTH; ++i) {
    191             norm += mCenter[i] * mCenter[i];
    192         }
    193         norm = Math.sqrt(norm);
    194         for (int i = 0; i < VECTOR_LENGTH; ++i) {
    195             mCenter[i] /= norm;
    196         }
    197     }
    198 
    199     protected void averageCenter(double[] newCenter, long newDuration) {
    200         double weight = ((double) mDuration) / (mDuration + newDuration);
    201         double newWeight = 1f - weight;
    202 
    203         double norm = 0;
    204         for (int i = 0; i < VECTOR_LENGTH; ++i) {
    205             mCenter[i] = weight * mCenter[i] + newWeight * newCenter[i];
    206             norm += mCenter[i] * mCenter[i];
    207         }
    208         norm = Math.sqrt(norm);
    209         for (int i = 0; i < VECTOR_LENGTH; ++i) {
    210             mCenter[i] /= norm;
    211         }
    212     }
    213 }
    214