2 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 #include <Similarity.h>
21 #include "PlacesDetector.h"
22 #include "PlaceCateger.h"
26 #endif /* DEBUG_MODE */
27 #include <UserPlacesTypes.h>
30 #include <UserPlacesParams.h>
31 #include <DebugUtils.h>
32 #include <DatabaseManager.h>
34 #define __DELETE_PLACES_QUERY "DELETE FROM " PLACE_TABLE
37 #define __USER_PLACES_FILE "/tmp/user_places.txt" // TODO: Only for debug purposes -> Remove in final solution
38 #endif /* DEBUG_MODE */
40 #define __GET_VISITS_QUERY "SELECT "\
41 VISIT_COLUMN_END_TIME ", "\
42 VISIT_COLUMN_START_TIME ", "\
43 VISIT_COLUMN_WIFI_APS ", "\
44 VISIT_COLUMN_LOCATION_VALID ", "\
45 VISIT_COLUMN_LOCATION_LATITUDE ", "\
46 VISIT_COLUMN_LOCATION_LONGITUDE ", "\
47 VISIT_COLUMN_LOCATION_ACCURACY ", "\
48 VISIT_COLUMN_CATEG_HOME ", "\
49 VISIT_COLUMN_CATEG_WORK ", "\
50 VISIT_COLUMN_CATEG_OTHER \
53 #define __PLACE_TABLE_COLUMNS \
54 PLACE_COLUMN_CATEG_ID " INTEGER, "\
55 PLACE_COLUMN_CATEG_CONFIDENCE " REAL, "\
56 PLACE_COLUMN_NAME " TEXT, "\
57 PLACE_COLUMN_LOCATION_VALID " INTEGER, "\
58 PLACE_COLUMN_LOCATION_LATITUDE " REAL, "\
59 PLACE_COLUMN_LOCATION_LONGITUDE " REAL, "\
60 PLACE_COLUMN_LOCATION_ACCURACY " REAL, "\
61 PLACE_COLUMN_WIFI_APS " STRING, "\
62 PLACE_COLUMN_CREATE_DATE " timestamp"
64 void ctx::PlacesDetector::detectPlaces()
69 __dbDeleteOldEntries();
70 std::vector<Json> records = __dbGetVisits();
71 Visits visits = __visitsFromJsons(records);
72 __processVisits(visits);
75 std::vector<ctx::Json> ctx::PlacesDetector::__dbGetVisits()
77 std::vector<Json> records;
78 DatabaseManager dbManager;
79 bool ret = dbManager.executeSync(__GET_VISITS_QUERY, &records);
80 _D("load visits execute query result: %s", ret ? "SUCCESS" : "FAIL");
84 double ctx::PlacesDetector::__doubleValueFromJson(Json &row, const char* key)
87 row.get(NULL, key, &value);
88 _D("key: %s, value: %lf", key, value);
92 ctx::Categs ctx::PlacesDetector::__visitCategsFromJson(Json &row)
95 categs[PLACE_CATEG_ID_HOME] = __doubleValueFromJson(row, VISIT_COLUMN_CATEG_HOME);
96 categs[PLACE_CATEG_ID_WORK] = __doubleValueFromJson(row, VISIT_COLUMN_CATEG_WORK);
97 categs[PLACE_CATEG_ID_OTHER] = __doubleValueFromJson(row, VISIT_COLUMN_CATEG_OTHER);
101 void ctx::PlacesDetector::__visitLocationFromJson(Json &row, ctx::Visit &visit)
103 int locationValidInt;
104 row.get(NULL, VISIT_COLUMN_LOCATION_VALID, &locationValidInt);
105 visit.locationValid = (bool) locationValidInt;
106 row.get(NULL, VISIT_COLUMN_LOCATION_LATITUDE, &(visit.location.latitude));
107 row.get(NULL, VISIT_COLUMN_LOCATION_LONGITUDE, &(visit.location.longitude));
108 row.get(NULL, VISIT_COLUMN_LOCATION_ACCURACY, &(visit.location.accuracy));
111 ctx::Visit ctx::PlacesDetector::__visitFromJson(Json &row)
116 row.get(NULL, VISIT_COLUMN_START_TIME, &startTime);
117 row.get(NULL, VISIT_COLUMN_END_TIME, &endTime);
118 row.get(NULL, VISIT_COLUMN_WIFI_APS, &str);
120 std::stringstream ss;
122 std::shared_ptr<MacSet> macSet = std::make_shared<MacSet>();
125 Interval interval(startTime, endTime);
126 Categs categs = __visitCategsFromJson(row);
128 Visit visit(interval, macSet, categs);
130 __visitLocationFromJson(row, visit);
135 ctx::Visits ctx::PlacesDetector::__visitsFromJsons(std::vector<Json>& records)
138 _D("db_result: number of all visits: %d", records.size());
140 for (Json &row : records) {
141 Visit visit = __visitFromJson(row);
142 visits.push_back(visit);
144 _D("number of all visits in vector: %d", visits.size());
149 void ctx::PlacesDetector::__reduceOutliers(ctx::Visits &visits)
151 int size = visits.size();
152 visits.erase(std::remove_if(
156 int minutes = (v.interval.end - v.interval.start) / 60;
157 return (minutes < PLACES_DETECTOR_MIN_VISIT_DURATION_MINUTES)
158 || (minutes > PLACES_DETECTOR_MAX_VISIT_DURATION_MINUTES);
161 int newSize = visits.size();
163 _D("Visits number from %d to %d (to short and to long reduction)", size, newSize);
166 std::vector<std::shared_ptr<ctx::Place>> ctx::PlacesDetector::__processVisits(ctx::Visits &visits, bool testMode)
169 std::vector<Visits> placesVisits; // TODO: remove from final solution.
170 #endif /* DEBUG_MODE */
172 std::vector<std::shared_ptr<Place>> newDetectedPlaces;
174 __reduceOutliers(visits);
175 auto components = __mergeVisits(visits);
177 for (std::shared_ptr<graph::Component> component : *components) {
178 // Small places outliers reduction
179 if (!testMode && component->size() < PLACES_DETECTOR_MIN_VISITS_PER_BIG_PLACE)
181 std::shared_ptr<Visits> merged = std::make_shared<Visits>();
182 for (graph::Node i : *component) {
183 merged->push_back(visits[i]);
185 std::shared_ptr<Place> place = __placeFromMergedVisits(*merged);
186 if (place->categId == PLACE_CATEG_ID_NONE)
188 newDetectedPlaces.push_back(place);
190 __dbInsertPlace(*place);
193 { // TODO: Only for debug -> remove in final solution
195 for (graph::Node i : *component) {
196 placeVisits.push_back(visits[i]);
198 placesVisits.push_back(placeVisits);
200 #endif /* DEBUG_MODE */
204 { // Print to file TODO: Only for debug -> remove in final solution
205 std::ofstream out(__USER_PLACES_FILE);
206 for (size_t i = 0; i < newDetectedPlaces.size(); i++) {
207 DebugUtils::printPlace2Stream(*newDetectedPlaces[i], out);
208 Visits placeVisits = placesVisits[i];
209 for (Visit visit : placeVisits) {
210 visit.printShort2Stream(out);
214 Gmap::writeMap(newDetectedPlaces);
216 #endif /* DEBUG_MODE */
218 return newDetectedPlaces;
221 void ctx::PlacesDetector::__mergeLocation(const Visits &visits, Place &place)
223 std::vector<double> latitudes;
224 std::vector<double> longitudes;
225 std::vector<double> accuracy;
226 place.locationValid = false;
227 for (const Visit& visit : visits) {
228 if (visit.locationValid) {
229 latitudes.push_back(visit.location.latitude);
230 longitudes.push_back(visit.location.longitude);
231 accuracy.push_back(visit.location.accuracy);
232 place.locationValid = true;
235 if (place.locationValid) {
236 place.location = medianLocation(latitudes, longitudes, accuracy);
237 _D("place location set: lat=%.8f, lon=%.8f, acc=%.8f",
238 place.location.latitude,
239 place.location.longitude,
240 place.location.accuracy);
242 _D("place location not set");
246 std::shared_ptr<ctx::Place> ctx::PlacesDetector::__placeFromMergedVisits(Visits &mergedVisits)
248 std::shared_ptr<Place> place = std::make_shared<Place>();
249 place->createDate = std::time(nullptr);
250 std::vector<std::shared_ptr<MacSet>> macSets;
251 for (const Visit &visit : mergedVisits) {
252 macSets.push_back(visit.macSet);
254 std::shared_ptr<MacSet> allMacs = macSetsUnion(macSets);
255 __mergeLocation(mergedVisits, *place);
256 PlaceCateger::categorize(mergedVisits, *place);
259 * At this stage getting network names associated with Wifi AP MAC addresses is not needed (by looking
260 * to Wifi APs MAC addresses to network names map in database). This additional information for place
261 * is needed only when place is returned for external clients (getting straight from the database).
263 for (ctx::Mac mac : *allMacs) {
264 place->wifiAps.insert(std::pair<std::string, std::string>(mac, ""));
270 void ctx::PlacesDetector::__reduceOutliers(std::shared_ptr<ctx::graph::Components> &cc)
272 int size = cc->size();
273 cc->erase(std::remove_if(cc->begin(),
275 [](std::shared_ptr<graph::Component> &c)->bool {
276 return c->size() < PLACES_DETECTOR_MIN_VISITS_PER_PLACE;
279 int newSize = cc->size();
281 _D("Connected components from %d to %d (min visit per place)", size, newSize);
284 std::shared_ptr<ctx::graph::Components> ctx::PlacesDetector::__mergeVisits(const std::vector<Visit> &visits)
286 auto graph = __graphFromVisits(visits);
287 auto cc = graph::connectedComponents(*graph);
288 __reduceOutliers(cc);
292 std::shared_ptr<ctx::graph::Graph> ctx::PlacesDetector::__graphFromVisits(const std::vector<Visit> &visits)
294 std::shared_ptr<graph::Graph> graph = std::make_shared<graph::Graph>();
295 graph->resize(visits.size());
296 for (size_t i = 0; i < visits.size(); i++) {
297 (*graph)[i] = std::make_shared<graph::NeighbourNodes>();
298 for (size_t j = 0; j < i; j++) {
299 if (similarity::isJoint(*visits[i].macSet, *visits[j].macSet)) {
300 (*graph)[i]->insert(j);
301 (*graph)[j]->insert(i);
308 void ctx::PlacesDetector::__dbDeletePlaces()
310 std::vector<Json> records;
311 DatabaseManager dbManager;
312 bool ret = dbManager.executeSync(__DELETE_PLACES_QUERY, &records);
313 _D("delete places execute query result: %s", ret ? "SUCCESS" : "FAIL");
316 void ctx::PlacesDetector::__dbDeleteOldEntries()
320 time_t thresholdTime = currentTime - PLACES_DETECTOR_RETENTION_SECONDS;
321 __dbDeleteOlderVisitsThan(thresholdTime);
322 __dbDeleteOlderWifiAPsThan(thresholdTime);
325 void ctx::PlacesDetector::__dbDeleteOlderVisitsThan(time_t threshold)
327 std::stringstream query;
328 query << "DELETE FROM " << VISIT_TABLE;
329 query << " WHERE " << VISIT_COLUMN_END_TIME << " < " << threshold;
330 // query << " AND 0"; // XXX: Always false condition. Uncomment it for not deleting any visit during development.
331 std::vector<Json> records;
332 DatabaseManager dbManager;
333 bool ret = dbManager.executeSync(query.str().c_str(), &records);
334 _D("deleting visits older than: %d, result: %s", threshold, ret ? "SUCCESS" : "FAIL");
337 void ctx::PlacesDetector::__dbDeleteOlderWifiAPsThan(time_t threshold)
339 std::stringstream query;
340 query << "DELETE FROM " << WIFI_APS_MAP_TABLE;
341 query << " WHERE " << WIFI_APS_MAP_COLUMN_INSERT_TIME << " < " << threshold;
342 std::vector<Json> records;
343 DatabaseManager dbManager;
344 bool ret = dbManager.executeSync(query.str().c_str(), &records);
345 _D("deleting Wifi APs older than: %d, result: %s", threshold, ret ? "SUCCESS" : "FAIL");
348 void ctx::PlacesDetector::__dbCreateTable()
350 DatabaseManager dbManager;
351 bool ret = dbManager.createTableSync(PLACE_TABLE, __PLACE_TABLE_COLUMNS);
352 _D("db: place Table Creation Result: %s", ret ? "SUCCESS" : "FAIL");
355 void ctx::PlacesDetector::__dbInsertPlace(const Place &place)
358 data.set(NULL, PLACE_COLUMN_CATEG_ID, place.categId);
359 data.set(NULL, PLACE_COLUMN_CATEG_CONFIDENCE, place.categConfidence);
360 data.set(NULL, PLACE_COLUMN_NAME, place.name);
362 data.set(NULL, PLACE_COLUMN_LOCATION_VALID, place.locationValid);
363 data.set(NULL, PLACE_COLUMN_LOCATION_LATITUDE, place.location.latitude);
364 data.set(NULL, PLACE_COLUMN_LOCATION_LONGITUDE, place.location.longitude);
365 data.set(NULL, PLACE_COLUMN_LOCATION_ACCURACY, place.location.accuracy);
367 for (std::pair<std::string, std::string> ap : place.wifiAps) {
368 wifiAps.append(ap.first);
371 wifiAps = wifiAps.substr(0, wifiAps.size()-1);
372 data.set(NULL, PLACE_COLUMN_WIFI_APS, wifiAps);
373 data.set(NULL, PLACE_COLUMN_CREATE_DATE, static_cast<int>(place.createDate));
376 DatabaseManager dbManager;
377 bool ret = dbManager.insertSync(PLACE_TABLE, data, &rowId);
378 _D("insert place execute query result: %s", ret ? "SUCCESS" : "FAIL");
381 extern "C" SO_EXPORT void detectPlaces()
383 ctx::PlacesDetector::detectPlaces();