LocationCluster.java revision 83954e853dc1e1a28b2c3efbe1585f188266df02
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 */ 16package android.bordeaux.services; 17 18import android.location.Location; 19import android.text.format.Time; 20import android.util.Log; 21 22import java.lang.Math; 23import java.util.ArrayList; 24import java.util.HashMap; 25import java.util.Map; 26 27public class LocationCluster extends BaseCluster { 28 public static String TAG = "LocationCluster"; 29 30 private static double FORGETTING_FACTOR = 0.1; 31 32 private boolean mIsNewCluster; 33 34 private ArrayList<Location> mLocations = new ArrayList<Location>(); 35 private HashMap<String, Long> mNewHistogram = new HashMap<String, Long>(); 36 37 // TODO: make it a singleton class 38 public LocationCluster(Location location, long duration, long avgInterval) { 39 super(location, avgInterval); 40 mIsNewCluster = true; 41 addSample(location, duration); 42 } 43 44 public void addSample(Location location, long duration) { 45 updateTemporalHistogram(location.getTime(), duration); 46 47 // use time field to store duation of this location 48 // TODO: extend Location class with additional field for this. 49 location.setTime(duration); 50 mLocations.add(location); 51 } 52 53 public void consolidate(long interval) { 54 // TODO: add check on interval 55 double[] newCenter = {0f, 0f, 0f}; 56 long newDuration = 0l; 57 58 // update cluster center 59 for (Location location : mLocations) { 60 double[] vector = getLocationVector(location); 61 long duration = location.getTime(); // in seconds 62 63 newDuration += duration; 64 for (int i = 0; i < 3; ++i) { 65 newCenter[i] += vector[i] * duration; 66 } 67 } 68 for (int i = 0; i < 3; ++i) { 69 newCenter[i] /= newDuration; 70 } 71 // remove location data 72 mLocations.clear(); 73 74 if (mIsNewCluster) { 75 for (int i = 0; i < 3; ++i) { 76 mCenter[i] = newCenter[i]; 77 } 78 mDuration = newDuration; 79 mHistogram.clear(); 80 mHistogram.putAll(mNewHistogram); 81 mNewHistogram.clear(); 82 83 mIsNewCluster = false; 84 } else { 85 // the new center is weight average over latest and existing centers. 86 // fine tune the weight of new center 87 double newWeight = ((double) newDuration) / (newDuration + mDuration); 88 newWeight *= FORGETTING_FACTOR; 89 double currWeight = 1f - newWeight; 90 double norm = 0; 91 for (int i = 0; i < 3; ++i) { 92 mCenter[i] = currWeight * mCenter[i] + newWeight * newCenter[i]; 93 norm += mCenter[i] * mCenter[i]; 94 } 95 // normalize 96 for (int i = 0; i < 3; ++i) { 97 mCenter[i] /= norm; 98 } 99 consolidateHistogram(newWeight, newDuration); 100 mNewHistogram.clear(); 101 } 102 } 103 104 /* 105 * if the new created cluster whose covered area overlaps with any existing 106 * cluster move the center away from that cluster till there is no overlap. 107 */ 108 public void moveAwayCluster(LocationCluster cluster, float distance) { 109 double[] vector = new double[3]; 110 111 double dot = 0f; 112 for (int i = 0; i < 3; ++i) { 113 dot += mCenter[i] * cluster.mCenter[i]; 114 } 115 double norm = 0f; 116 for (int i = 0; i < 3; ++i) { 117 vector[i] = mCenter[i] - dot * cluster.mCenter[i]; 118 norm += vector[i] * vector[i]; 119 } 120 norm = Math.sqrt(norm); 121 122 double radian = distance / EARTH_RADIUS; 123 for (int i = 0; i < 3; ++i) { 124 mCenter[i] = cluster.mCenter[i] * Math.cos(radian) + 125 (vector[i] / norm) * Math.sin(radian); 126 } 127 } 128 129 private void updateTemporalHistogram(long time, long duration) { 130 HashMap<String, String> timeFeatures = TimeStatsAggregator.getAllTimeFeatures(time); 131 132 String timeOfWeek = timeFeatures.get(TimeStatsAggregator.TIME_OF_WEEK); 133 long totalDuration = (mNewHistogram.containsKey(timeOfWeek)) ? 134 mNewHistogram.get(timeOfWeek) + duration : duration; 135 mNewHistogram.put(timeOfWeek, totalDuration); 136 137 String timeOfDay = timeFeatures.get(TimeStatsAggregator.TIME_OF_DAY); 138 totalDuration = (mNewHistogram.containsKey(timeOfDay)) ? 139 mNewHistogram.get(timeOfDay) + duration : duration; 140 mNewHistogram.put(timeOfDay, totalDuration); 141 } 142 143 private void consolidateHistogram(double weight, long newDuration) { 144 long base = 1000; 145 long newWeight = (long) (weight * base); 146 long currWeight = base - newWeight; 147 148 for (Map.Entry<String, Long> entry : mHistogram.entrySet()) { 149 String timeLabel = entry.getKey(); 150 long duration = entry.getValue() * currWeight; 151 if (mNewHistogram.containsKey(timeLabel)) { 152 duration += mNewHistogram.get(timeLabel) * newWeight; 153 } 154 duration /= base; 155 mHistogram.put(timeLabel, duration); 156 } 157 158 for (Map.Entry<String, Long> entry : mNewHistogram.entrySet()) { 159 String timeLabel = entry.getKey(); 160 if (!mHistogram.containsKey(timeLabel)) { 161 long duration = newWeight * entry.getValue(); 162 duration /= base; 163 mHistogram.put(timeLabel, duration); 164 } 165 } 166 mDuration = (mDuration * currWeight + newDuration * newWeight) / base; 167 } 168} 169