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 "../utils/Similarity.h"
21 #include "PlacesDetector.h"
22 #include "PlaceCateger.h"
23 #include "../utils/Median.h"
24 #ifdef TIZEN_ENGINEER_MODE
25 #include "../utils/Gmap.h"
26 #endif /* TIZEN_ENGINEER_MODE */
27 #include "../facade/UserPlacesTypes.h"
30 #include "../facade/UserPlacesParams.h"
31 #include "../utils/DebugUtils.h"
33 #define __DELETE_PLACES_QUERY "DELETE FROM " PLACE_TABLE
35 #ifdef TIZEN_ENGINEER_MODE
36 #define __USER_PLACES_FILE "/tmp/user_places.txt" // TODO: Only for debug purposes -> Remove in final solution
37 #endif /* TIZEN_ENGINEER_MODE */
39 #define __GET_VISITS_QUERY "SELECT "\
40 VISIT_COLUMN_END_TIME ", "\
41 VISIT_COLUMN_START_TIME ", "\
42 VISIT_COLUMN_WIFI_APS ", "\
43 VISIT_COLUMN_LOCATION_VALID ", "\
44 VISIT_COLUMN_LOCATION_LATITUDE ", "\
45 VISIT_COLUMN_LOCATION_LONGITUDE ", "\
46 VISIT_COLUMN_LOCATION_ACCURACY ", "\
47 VISIT_COLUMN_CATEG_HOME ", "\
48 VISIT_COLUMN_CATEG_WORK ", "\
49 VISIT_COLUMN_CATEG_OTHER \
52 #define __GET_PLACES_QUERY "SELECT "\
53 PLACE_COLUMN_CATEG_ID ", "\
54 PLACE_COLUMN_CATEG_CONFIDENCE ", "\
55 PLACE_COLUMN_NAME ", "\
56 PLACE_COLUMN_LOCATION_VALID ", "\
57 PLACE_COLUMN_LOCATION_LATITUDE ", "\
58 PLACE_COLUMN_LOCATION_LONGITUDE ", "\
59 PLACE_COLUMN_LOCATION_ACCURACY ", "\
60 PLACE_COLUMN_WIFI_APS ", "\
61 PLACE_COLUMN_CREATE_DATE \
64 #define __GET_WIFI_APS_MAP_QUERY "SELECT "\
65 WIFI_APS_MAP_COLUMN_MAC ", "\
66 WIFI_APS_MAP_COLUMN_NETWORK_NAME \
67 " FROM " WIFI_APS_MAP_TABLE
69 #define __PLACE_TABLE_COLUMNS \
70 PLACE_COLUMN_CATEG_ID " INTEGER, "\
71 PLACE_COLUMN_CATEG_CONFIDENCE " REAL, "\
72 PLACE_COLUMN_NAME " TEXT, "\
73 PLACE_COLUMN_LOCATION_VALID " INTEGER, "\
74 PLACE_COLUMN_LOCATION_LATITUDE " REAL, "\
75 PLACE_COLUMN_LOCATION_LONGITUDE " REAL, "\
76 PLACE_COLUMN_LOCATION_ACCURACY " REAL, "\
77 PLACE_COLUMN_WIFI_APS " STRING, "\
78 PLACE_COLUMN_CREATE_DATE " timestamp"
80 bool ctx::PlacesDetector::onTimerExpired(int timerId)
84 __dbDeleteOldEntries();
85 std::vector<Json> records = __dbGetVisits();
86 Visits visits = __visitsFromJsons(records);
88 __processVisits(visits);
93 std::vector<ctx::Json> ctx::PlacesDetector::__dbGetVisits()
95 std::vector<Json> records;
96 bool ret = __dbManager->executeSync(__GET_VISITS_QUERY, &records);
97 _D("load visits execute query result: %s", ret ? "SUCCESS" : "FAIL");
101 std::vector<ctx::Json> ctx::PlacesDetector::__dbGetPlaces()
103 std::vector<Json> records;
104 bool ret = __dbManager->executeSync(__GET_PLACES_QUERY, &records);
105 _D("load places execute query result: %s", ret ? "SUCCESS" : "FAIL");
109 void ctx::PlacesDetector::__dbGetWifiAPsMap()
111 std::vector<Json> records;
112 bool ret = __dbManager->executeSync(__GET_WIFI_APS_MAP_QUERY, &records);
113 _D("load Wifi APs map (size = %d), result: %s", records.size(), ret ? "SUCCESS" : "FAIL");
114 std::string mac = "";
115 std::string networkName = "";
116 for (Json record : records) {
117 record.get(NULL, WIFI_APS_MAP_COLUMN_MAC, &mac);
118 record.get(NULL, WIFI_APS_MAP_COLUMN_NETWORK_NAME, &networkName);
119 __wifiAPsMap.insert(std::pair<std::string, std::string>(mac, networkName));
123 double ctx::PlacesDetector::__doubleValueFromJson(Json &row, const char* key)
126 row.get(NULL, key, &value);
127 _D("__doubleValueFromJson, key:%s, value: %lf", key, value);
131 ctx::Categs ctx::PlacesDetector::__visitCategsFromJson(Json &row)
134 categs[PLACE_CATEG_ID_HOME] = __doubleValueFromJson(row, VISIT_COLUMN_CATEG_HOME);
135 categs[PLACE_CATEG_ID_WORK] = __doubleValueFromJson(row, VISIT_COLUMN_CATEG_WORK);
136 categs[PLACE_CATEG_ID_OTHER] = __doubleValueFromJson(row, VISIT_COLUMN_CATEG_OTHER);
140 void ctx::PlacesDetector::__visitLocationFromJson(Json &row, ctx::Visit &visit)
142 int locationValidInt;
143 row.get(NULL, VISIT_COLUMN_LOCATION_VALID, &locationValidInt);
144 visit.locationValid = (bool) locationValidInt;
145 row.get(NULL, VISIT_COLUMN_LOCATION_LATITUDE, &(visit.location.latitude));
146 row.get(NULL, VISIT_COLUMN_LOCATION_LONGITUDE, &(visit.location.longitude));
147 row.get(NULL, VISIT_COLUMN_LOCATION_ACCURACY, &(visit.location.accuracy));
150 ctx::Visit ctx::PlacesDetector::__visitFromJson(Json &row)
155 row.get(NULL, VISIT_COLUMN_START_TIME, &startTime);
156 row.get(NULL, VISIT_COLUMN_END_TIME, &endTime);
157 row.get(NULL, VISIT_COLUMN_WIFI_APS, &str);
159 std::stringstream ss;
161 std::shared_ptr<MacSet> macSet = std::make_shared<MacSet>();
164 Interval interval(startTime, endTime);
165 Categs categs = __visitCategsFromJson(row);
167 Visit visit(interval, macSet, categs);
169 __visitLocationFromJson(row, visit);
174 ctx::Visits ctx::PlacesDetector::__visitsFromJsons(std::vector<Json>& records)
177 _D("db_result: number of all visits: %d", records.size());
179 for (Json &row : records) {
180 Visit visit = __visitFromJson(row);
181 visits.push_back(visit);
183 _D("number of all visits in vector: %d", visits.size());
187 std::shared_ptr<ctx::Place> ctx::PlacesDetector::__placeFromJson(Json &row)
189 std::shared_ptr<Place> place = std::make_shared<Place>();
190 __placeCategoryFromJson(row, *place);
191 row.get(NULL, PLACE_COLUMN_NAME, &(place->name));
192 __placeLocationFromJson(row, *place);
193 __placeWifiAPsFromJson(row, *place);
194 __placeCreateDateFromJson(row, *place);
195 _D("db_result: categId: %d; place: name: %s; locationValid: %d; latitude: %lf, longitude: %lf, createDate: %d", place->categId, place->name.c_str(), place->locationValid, place->location.latitude, place->location.longitude, place->createDate);
199 void ctx::PlacesDetector::__placeCategoryFromJson(Json &row, ctx::Place &place)
202 row.get(NULL, PLACE_COLUMN_CATEG_ID, &categId);
203 // This is due to the fact the JSON module API interface doesn't handle enum
204 place.categId = static_cast<PlaceCategId>(categId);
207 void ctx::PlacesDetector::__placeLocationFromJson(Json &row, ctx::Place &place)
209 int locationValidInt;
210 row.get(NULL, PLACE_COLUMN_LOCATION_VALID, &locationValidInt);
211 place.locationValid = (bool) locationValidInt;
212 row.get(NULL, PLACE_COLUMN_LOCATION_LATITUDE, &(place.location.latitude));
213 row.get(NULL, PLACE_COLUMN_LOCATION_LONGITUDE, &(place.location.longitude));
214 row.get(NULL, PLACE_COLUMN_LOCATION_ACCURACY, &(place.location.accuracy));
217 void ctx::PlacesDetector::__placeWifiAPsFromJson(Json &row, ctx::Place &place)
220 row.get(NULL, PLACE_COLUMN_WIFI_APS, &wifiAps);
221 std::stringstream ss;
223 std::shared_ptr<MacSet> macSet = std::make_shared<MacSet>();
225 for (ctx::Mac mac : *macSet) {
226 place.wifiAps.insert(std::pair<std::string, std::string>(mac, __wifiAPsMap[mac]));
230 void ctx::PlacesDetector::__placeCreateDateFromJson(Json &row, ctx::Place &place)
233 row.get(NULL, PLACE_COLUMN_CREATE_DATE, &(createDate));
234 // This is due to the fact the JSON module API interface doesn't handle time_t
235 place.createDate = static_cast<time_t>(createDate);
238 std::vector<std::shared_ptr<ctx::Place>> ctx::PlacesDetector::__placesFromJsons(std::vector<Json>& records)
240 std::vector<std::shared_ptr<Place>> places;
241 _D("db_result: number of all places: %d", records.size());
243 for (Json &row : records) {
244 std::shared_ptr<Place> place = __placeFromJson(row);
245 places.push_back(place);
247 _D("number of all places in vector: %d", places.size());
251 void ctx::PlacesDetector::__reduceOutliers(ctx::Visits &visits)
253 int size = visits.size();
254 visits.erase(std::remove_if(
258 int minutes = (v.interval.end - v.interval.start) / 60;
259 return (minutes < PLACES_DETECTOR_MIN_VISIT_DURATION_MINUTES)
260 || (minutes > PLACES_DETECTOR_MAX_VISIT_DURATION_MINUTES);
263 int newSize = visits.size();
265 _D("Visits number from %d to %d (to short and to long reduction)", size, newSize);
268 void ctx::PlacesDetector::__processVisits(ctx::Visits &visits)
270 __reduceOutliers(visits);
272 _D("__testMode = %d", __testMode);
273 auto components = __mergeVisits(visits);
274 std::vector<std::shared_ptr<Place>> newDetectedPlaces;
275 #ifdef TIZEN_ENGINEER_MODE
276 std::vector<Visits> placesVisits; // TODO: remove from final solution.
277 #endif /* TIZEN_ENGINEER_MODE */
278 for (std::shared_ptr<graph::Component> component : *components) {
279 // Small places outliers reduction
280 if (!__testMode && component->size() < PLACES_DETECTOR_MIN_VISITS_PER_BIG_PLACE)
282 std::shared_ptr<Visits> merged = std::make_shared<Visits>();
283 for (graph::Node i : *component) {
284 merged->push_back(visits[i]);
286 std::shared_ptr<Place> place = __placeFromMergedVisits(*merged);
287 if (place->categId == PLACE_CATEG_ID_NONE)
289 newDetectedPlaces.push_back(place);
291 __dbInsertPlace(*place);
293 #ifdef TIZEN_ENGINEER_MODE
294 { // TODO: Only for debug -> remove in final solution
296 for (graph::Node i : *component) {
297 placeVisits.push_back(visits[i]);
299 placesVisits.push_back(placeVisits);
301 #endif /* TIZEN_ENGINEER_MODE */
304 __detectedPlacesUpdate(newDetectedPlaces);
306 #ifdef TIZEN_ENGINEER_MODE
307 { // Print to file TODO: Only for debug -> remove in final solution
308 std::ofstream out(__USER_PLACES_FILE);
309 for (size_t i = 0; i < newDetectedPlaces.size(); i++) {
310 DebugUtils::printPlace2Stream(*newDetectedPlaces[i], out);
311 Visits placeVisits = placesVisits[i];
312 for (Visit visit : placeVisits) {
313 visit.printShort2Stream(out);
317 Gmap::writeMap(newDetectedPlaces);
319 #endif /* TIZEN_ENGINEER_MODE */
323 * Replace old places by new ones.
325 void ctx::PlacesDetector::__detectedPlacesUpdate(std::vector<std::shared_ptr<Place>> &newPlaces)
328 // XXX: In case of thread safety issues use std::mutex to protect places list.
329 __detectedPlaces = newPlaces;
332 void ctx::PlacesDetector::__mergeLocation(const Visits &visits, Place &place)
334 std::vector<double> latitudes;
335 std::vector<double> longitudes;
336 std::vector<double> accuracy;
337 place.locationValid = false;
338 for (const Visit& visit : visits) {
339 if (visit.locationValid) {
340 latitudes.push_back(visit.location.latitude);
341 longitudes.push_back(visit.location.longitude);
342 accuracy.push_back(visit.location.accuracy);
343 place.locationValid = true;
346 if (place.locationValid) {
347 place.location = medianLocation(latitudes, longitudes, accuracy);
348 _D("place location set: lat=%.8f, lon=%.8f, acc=%.8f",
349 place.location.latitude,
350 place.location.longitude,
351 place.location.accuracy);
353 _D("place location not set");
357 std::shared_ptr<ctx::Place> ctx::PlacesDetector::__placeFromMergedVisits(Visits &mergedVisits)
359 std::shared_ptr<Place> place = std::make_shared<Place>();
360 place->createDate = std::time(nullptr);
361 std::vector<std::shared_ptr<MacSet>> macSets;
362 for (const Visit &visit : mergedVisits) {
363 macSets.push_back(visit.macSet);
365 std::shared_ptr<MacSet> allMacs = macSetsUnion(macSets);
366 for (ctx::Mac mac : *allMacs) {
367 place->wifiAps.insert(std::pair<std::string, std::string>(mac, __wifiAPsMap[mac]));
369 __mergeLocation(mergedVisits, *place);
371 PlaceCateger::categorize(mergedVisits, *place);
376 void ctx::PlacesDetector::__reduceOutliers(std::shared_ptr<ctx::graph::Components> &cc)
378 int size = cc->size();
379 cc->erase(std::remove_if(cc->begin(),
381 [](std::shared_ptr<graph::Component> &c)->bool {
382 return c->size() < PLACES_DETECTOR_MIN_VISITS_PER_PLACE;
385 int newSize = cc->size();
387 _D("Connected components from %d to %d (min visit per place)", size, newSize);
390 std::shared_ptr<ctx::graph::Components> ctx::PlacesDetector::__mergeVisits(const std::vector<Visit> &visits)
392 auto graph = __graphFromVisits(visits);
393 auto cc = graph::connectedComponents(*graph);
394 __reduceOutliers(cc);
398 std::shared_ptr<ctx::graph::Graph> ctx::PlacesDetector::__graphFromVisits(const std::vector<Visit> &visits)
400 std::shared_ptr<graph::Graph> graph = std::make_shared<graph::Graph>();
401 graph->resize(visits.size());
402 for (size_t i = 0; i < visits.size(); i++) {
403 (*graph)[i] = std::make_shared<graph::NeighbourNodes>();
404 for (size_t j = 0; j < i; j++) {
405 if (similarity::isJoint(*visits[i].macSet, *visits[j].macSet)) {
406 (*graph)[i]->insert(j);
407 (*graph)[j]->insert(i);
414 void ctx::PlacesDetector::__dbDeletePlaces()
416 std::vector<Json> records;
417 bool ret = __dbManager->executeSync(__DELETE_PLACES_QUERY, &records);
418 _D("delete places execute query result: %s", ret ? "SUCCESS" : "FAIL");
421 void ctx::PlacesDetector::__dbDeleteOldEntries()
425 time_t thresholdTime = currentTime - PLACES_DETECTOR_RETENTION_SECONDS;
426 __dbDeleteOlderVisitsThan(thresholdTime);
427 __dbDeleteOlderWifiAPsThan(thresholdTime);
430 void ctx::PlacesDetector::__dbDeleteOlderVisitsThan(time_t threshold)
432 std::stringstream query;
433 query << "DELETE FROM " << VISIT_TABLE;
434 query << " WHERE " << VISIT_COLUMN_END_TIME << " < " << threshold;
435 // query << " AND 0"; // XXX: Always false condition. Uncomment it for not deleting any visit during development.
436 std::vector<Json> records;
437 bool ret = __dbManager->executeSync(query.str().c_str(), &records);
438 _D("deleting visits older than: %d, result: %s", threshold, ret ? "SUCCESS" : "FAIL");
441 void ctx::PlacesDetector::__dbDeleteOlderWifiAPsThan(time_t threshold)
443 std::stringstream query;
444 query << "DELETE FROM " << WIFI_APS_MAP_TABLE;
445 query << " WHERE " << WIFI_APS_MAP_COLUMN_INSERT_TIME << " < " << threshold;
446 std::vector<Json> records;
447 bool ret = __dbManager->executeSync(query.str().c_str(), &records);
448 _D("deleting Wifi APs older than: %d, result: %s", threshold, ret ? "SUCCESS" : "FAIL");
451 ctx::PlacesDetector::PlacesDetector(bool testMode):
452 __testMode(testMode),
453 __dbManager(testMode ? nullptr : new DatabaseManager())
458 std::vector<Json> records = __dbGetPlaces();
460 std::vector<std::shared_ptr<Place>> dbPlaces = __placesFromJsons(records);
461 __wifiAPsMap.clear();
462 __detectedPlacesUpdate(dbPlaces);
465 void ctx::PlacesDetector::__dbCreateTable()
467 bool ret = __dbManager->createTable(0, PLACE_TABLE, __PLACE_TABLE_COLUMNS);
468 _D("db: place Table Creation Result: %s", ret ? "SUCCESS" : "FAIL");
471 void ctx::PlacesDetector::__dbInsertPlace(const Place &place)
474 data.set(NULL, PLACE_COLUMN_CATEG_ID, place.categId);
475 data.set(NULL, PLACE_COLUMN_CATEG_CONFIDENCE, place.categConfidence);
476 data.set(NULL, PLACE_COLUMN_NAME, place.name);
478 data.set(NULL, PLACE_COLUMN_LOCATION_VALID, place.locationValid);
479 data.set(NULL, PLACE_COLUMN_LOCATION_LATITUDE, place.location.latitude);
480 data.set(NULL, PLACE_COLUMN_LOCATION_LONGITUDE, place.location.longitude);
481 data.set(NULL, PLACE_COLUMN_LOCATION_ACCURACY, place.location.accuracy);
483 for (std::pair<std::string, std::string> ap : place.wifiAps) {
484 wifiAps.append(ap.first);
487 wifiAps = wifiAps.substr(0, wifiAps.size()-1);
488 data.set(NULL, PLACE_COLUMN_WIFI_APS, wifiAps);
489 data.set(NULL, PLACE_COLUMN_CREATE_DATE, static_cast<int>(place.createDate));
492 bool ret = __dbManager->insertSync(PLACE_TABLE, data, &rowId);
493 _D("insert place execute query result: %s", ret ? "SUCCESS" : "FAIL");
496 std::vector<std::shared_ptr<ctx::Place>> ctx::PlacesDetector::getPlaces()
498 // XXX: In case of thread safety issues use std::mutex to protect places list.
499 return __detectedPlaces;