blob: 2d25cf1fecc8480be5ac7e27c59f4a4a94a539a6 [file] [log] [blame]
Ruei-sung Linf0f78442012-08-13 19:04:29 -07001/*
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;
Ruei-sung Lin5d42ffa2012-08-23 16:01:57 -070024import java.util.HashMap;
25import java.util.Map;
Ruei-sung Linf0f78442012-08-13 19:04:29 -070026
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>();
Ruei-sung Lin5d42ffa2012-08-23 16:01:57 -070035 private HashMap<String, Long> mNewHistogram = new HashMap<String, Long>();
Ruei-sung Linf0f78442012-08-13 19:04:29 -070036
Ruei-sung Lin5d42ffa2012-08-23 16:01:57 -070037 // TODO: make it a singleton class
38 public LocationCluster(Location location, long duration, long avgInterval) {
Ruei-sung Linf0f78442012-08-13 19:04:29 -070039 super(location, avgInterval);
40 mIsNewCluster = true;
Ruei-sung Lin5d42ffa2012-08-23 16:01:57 -070041 addSample(location, duration);
Ruei-sung Linf0f78442012-08-13 19:04:29 -070042 }
43
Ruei-sung Lin5d42ffa2012-08-23 16:01:57 -070044 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);
Ruei-sung Linf0f78442012-08-13 19:04:29 -070050 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);
Ruei-sung Lin83954e82012-08-28 18:00:48 -070061 long duration = location.getTime(); // in seconds
Ruei-sung Linf0f78442012-08-13 19:04:29 -070062
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;
Ruei-sung Lin5d42ffa2012-08-23 16:01:57 -070079 mHistogram.clear();
80 mHistogram.putAll(mNewHistogram);
81 mNewHistogram.clear();
82
Ruei-sung Linf0f78442012-08-13 19:04:29 -070083 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 }
Ruei-sung Lin5d42ffa2012-08-23 16:01:57 -070099 consolidateHistogram(newWeight, newDuration);
100 mNewHistogram.clear();
Ruei-sung Linf0f78442012-08-13 19:04:29 -0700101 }
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 }
Ruei-sung Lin5d42ffa2012-08-23 16:01:57 -0700128
129 private void updateTemporalHistogram(long time, long duration) {
130 HashMap<String, String> timeFeatures = TimeStatsAggregator.getAllTimeFeatures(time);
131
Ruei-sung Lin83954e82012-08-28 18:00:48 -0700132 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
Ruei-sung Lin5d42ffa2012-08-23 16:01:57 -0700137 String timeOfDay = timeFeatures.get(TimeStatsAggregator.TIME_OF_DAY);
Ruei-sung Lin83954e82012-08-28 18:00:48 -0700138 totalDuration = (mNewHistogram.containsKey(timeOfDay)) ?
Ruei-sung Lin5d42ffa2012-08-23 16:01:57 -0700139 mNewHistogram.get(timeOfDay) + duration : duration;
140 mNewHistogram.put(timeOfDay, totalDuration);
Ruei-sung Lin5d42ffa2012-08-23 16:01:57 -0700141 }
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 }
Ruei-sung Linf0f78442012-08-13 19:04:29 -0700168}