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 ArrayList<Location> mLocations = new ArrayList<Location>(); 31 private HashMap<String, Long> mNewHistogram = new HashMap<String, Long>(); 32 33 private String mSemanticClusterId = null; 34 35 public void setSemanticClusterId(String semanticClusterId) { 36 mSemanticClusterId = semanticClusterId; 37 } 38 39 public String getSemanticClusterId() { 40 return mSemanticClusterId; 41 } 42 43 public boolean hasSemanticClusterId() { 44 return mSemanticClusterId != null; 45 } 46 47 // TODO: make it a singleton class 48 public LocationCluster(Location location, long duration) { 49 super(location); 50 addSample(location, duration); 51 } 52 53 public void addSample(Location location, long duration) { 54 updateTemporalHistogram(location.getTime(), duration); 55 56 // use time field to store duation of this location 57 // TODO: extend Location class with additional field for this. 58 location.setTime(duration); 59 mLocations.add(location); 60 } 61 62 public void consolidate() { 63 // If there is no new location added during this period, do nothing. 64 if (mLocations.size() == 0) { 65 return; 66 } 67 68 double[] newCenter = {0f, 0f, 0f}; 69 long newDuration = 0l; 70 // update cluster center 71 for (Location location : mLocations) { 72 double[] vector = getLocationVector(location); 73 long duration = location.getTime(); // in seconds 74 75 if (duration == 0) { 76 throw new RuntimeException("location duration is zero"); 77 } 78 79 newDuration += duration; 80 for (int i = 0; i < VECTOR_LENGTH; ++i) { 81 newCenter[i] += vector[i] * duration; 82 } 83 } 84 if (newDuration == 0l) { 85 throw new RuntimeException("new duration is zero!"); 86 } 87 for (int i = 0; i < VECTOR_LENGTH; ++i) { 88 newCenter[i] /= newDuration; 89 } 90 // remove location data 91 mLocations.clear(); 92 93 // The updated center is the weighted average of the existing and the new 94 // centers. Note that if the cluster is consolidated for the first time, 95 // the weight for the existing cluster would be zero. 96 averageCenter(newCenter, newDuration); 97 98 // update histogram 99 for (Map.Entry<String, Long> entry : mNewHistogram.entrySet()) { 100 String timeLabel = entry.getKey(); 101 long duration = entry.getValue(); 102 if (mHistogram.containsKey(timeLabel)) { 103 duration += mHistogram.get(timeLabel); 104 } 105 mHistogram.put(timeLabel, duration); 106 } 107 mDuration += newDuration; 108 mNewHistogram.clear(); 109 } 110 111 /* 112 * if the new created cluster whose covered area overlaps with any existing 113 * cluster move the center away from that cluster till there is no overlap. 114 */ 115 public void moveAwayCluster(LocationCluster cluster, float distance) { 116 double[] vector = new double[VECTOR_LENGTH]; 117 118 double dot = 0f; 119 for (int i = 0; i < VECTOR_LENGTH; ++i) { 120 dot += mCenter[i] * cluster.mCenter[i]; 121 } 122 double norm = 0f; 123 for (int i = 0; i < VECTOR_LENGTH; ++i) { 124 vector[i] = mCenter[i] - dot * cluster.mCenter[i]; 125 norm += vector[i] * vector[i]; 126 } 127 norm = Math.sqrt(norm); 128 129 double radian = distance / EARTH_RADIUS; 130 for (int i = 0; i < VECTOR_LENGTH; ++i) { 131 mCenter[i] = cluster.mCenter[i] * Math.cos(radian) + 132 (vector[i] / norm) * Math.sin(radian); 133 } 134 } 135 136 private void updateTemporalHistogram(long time, long duration) { 137 HashMap<String, String> timeFeatures = TimeStatsAggregator.getAllTimeFeatures(time); 138 139 String timeOfWeek = timeFeatures.get(TimeStatsAggregator.TIME_OF_WEEK); 140 long totalDuration = (mNewHistogram.containsKey(timeOfWeek)) ? 141 mNewHistogram.get(timeOfWeek) + duration : duration; 142 mNewHistogram.put(timeOfWeek, totalDuration); 143 144 String timeOfDay = timeFeatures.get(TimeStatsAggregator.TIME_OF_DAY); 145 totalDuration = (mNewHistogram.containsKey(timeOfDay)) ? 146 mNewHistogram.get(timeOfDay) + duration : duration; 147 mNewHistogram.put(timeOfDay, totalDuration); 148 } 149} 150