1. Change tag style for error log
[platform/core/location/lbs-location.git] / location / manager / location-boundary.c
1 /*
2  * libslp-location
3  *
4  * Copyright (c) 2010-2013 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Youngae Kang <youngae.kang@samsung.com>, Minjune Kim <sena06.kim@samsung.com>
7  *          Genie Kim <daejins.kim@samsung.com>
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21
22 /*
23  * location_boundary_if_inside(LocationBoundary* boundary,
24  *                      LocationPosition* position)
25  * code from
26  *
27  * Copyright (c) 1970-2003, Wm. Randolph Franklin
28  *
29  * Permission is hereby granted, free of charge, to any person obtaining a copy
30  * of this software and associated documentation files (the "Software"),
31  * to deal in the Software without restriction, including without limitation
32  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
33  * and/or sell copies of the Software, and to permit persons to whom
34  * the Software is furnished to do so, subject to the following conditions:
35  *
36  * 1.Redistributions of source code must retain the above copyright notice,
37  * this list of conditions and the following disclaimers.
38  * 2.Redistributions in binary form must reproduce the above copyright notice
39  * in the documentation and/or other materials provided with the distribution.
40  * 3.The name of W. Randolph Franklin may not be used to endorse or promote
41  * products derived from this Software without specific prior written permission.
42  *
43  *
44  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
45  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
46  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
47  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
48  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
49  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
50  * IN THE SOFTWARE.
51  */
52
53 /*
54  * location_boundary_get_center_position (LocationBoundary *boundary)
55  * algorithm from http://en.wikipedia.org/wiki/Centroid
56  *
57  */
58
59 #ifdef HAVE_CONFIG_H
60 #include "config.h"
61 #endif
62
63 #include <math.h>
64 #include <string.h>
65 #include "location-boundary.h"
66 #include "location-common-util.h"
67 #include "location-log.h"
68
69 GType
70 location_boundary_get_type(void)
71 {
72         static volatile gsize type_volatile = 0;
73         if (g_once_init_enter(&type_volatile)) {
74                 GType type = g_boxed_type_register_static(
75                                                 g_intern_static_string("LocationBoundary"),
76                                                 (GBoxedCopyFunc) location_boundary_copy,
77                                                 (GBoxedFreeFunc) location_boundary_free);
78                 g_once_init_leave(&type_volatile, type);
79         }
80         return type_volatile;
81 }
82
83 static void _append_polygon_position(gpointer data, gpointer user_data)
84 {
85         g_return_if_fail(data);
86         g_return_if_fail(user_data);
87
88         LocationBoundary *boundary = (LocationBoundary *)user_data;
89         LocationPosition *position = (LocationPosition *)data;
90         LocationPosition *new_position = location_position_copy(position);
91
92         boundary->polygon.position_list = g_list_append(boundary->polygon.position_list, new_position);
93 }
94
95 static void _free_polygon_position(gpointer data)
96 {
97         g_return_if_fail(data);
98
99         LocationPosition *position = (LocationPosition *)data;
100         location_position_free(position);
101 }
102
103 EXPORT_API LocationBoundary *
104 location_boundary_new_for_rect(LocationPosition *left_top, LocationPosition *right_bottom)
105 {
106         g_return_val_if_fail(left_top, NULL);
107         g_return_val_if_fail(right_bottom, NULL);
108
109         gdouble lon_interval = right_bottom->longitude - left_top->longitude;
110
111         if (lon_interval < 180 && lon_interval > -180) {
112                 if (right_bottom->longitude <= left_top->longitude || right_bottom->latitude >= left_top->latitude)
113                         return NULL;
114         } else {
115                 if (right_bottom->latitude >= left_top->latitude)
116                         return NULL;
117         }
118
119         LocationBoundary *boundary = g_slice_new0(LocationBoundary);
120         g_return_val_if_fail(boundary, NULL);
121
122         boundary->type = LOCATION_BOUNDARY_RECT;
123         boundary->rect.left_top = location_position_copy(left_top);
124         boundary->rect.right_bottom = location_position_copy(right_bottom);
125         return boundary;
126 }
127
128 EXPORT_API LocationBoundary *
129 location_boundary_new_for_circle(LocationPosition *center, gdouble radius)
130 {
131         g_return_val_if_fail(center, NULL);
132         g_return_val_if_fail(radius > 0, NULL);
133         LocationBoundary *boundary = g_slice_new0(LocationBoundary);
134         g_return_val_if_fail(boundary, NULL);
135
136         boundary->type = LOCATION_BOUNDARY_CIRCLE;
137         boundary->circle.center = location_position_copy(center);
138         boundary->circle.radius = radius;
139         return boundary;
140 }
141
142 EXPORT_API LocationBoundary *
143 location_boundary_new_for_polygon(GList *position_list)
144 {
145         g_return_val_if_fail(position_list, NULL);
146         g_return_val_if_fail(g_list_length(position_list) > 2, NULL);
147
148         LocationBoundary *boundary = g_slice_new0(LocationBoundary);
149         g_return_val_if_fail(boundary, NULL);
150
151         g_list_foreach(position_list, (GFunc)_append_polygon_position, boundary);
152         boundary->type = LOCATION_BOUNDARY_POLYGON;
153         boundary->polygon.position_list = g_list_first(boundary->polygon.position_list);
154
155         return boundary;
156 }
157
158 EXPORT_API void
159 location_boundary_free(LocationBoundary *boundary)
160 {
161         g_return_if_fail(boundary);
162
163         if (boundary->type == LOCATION_BOUNDARY_RECT) {
164                 location_position_free(boundary->rect.left_top);
165                 location_position_free(boundary->rect.right_bottom);
166         } else if (boundary->type == LOCATION_BOUNDARY_CIRCLE) {
167                 location_position_free(boundary->circle.center);
168         } else if (boundary->type == LOCATION_BOUNDARY_POLYGON) {
169                 g_list_free_full(boundary->polygon.position_list, (GDestroyNotify)_free_polygon_position);
170         }
171         g_slice_free(LocationBoundary, boundary);
172 }
173
174 EXPORT_API LocationBoundary *
175 location_boundary_copy(const LocationBoundary *boundary)
176 {
177         g_return_val_if_fail(boundary, NULL);
178         if (boundary->type == LOCATION_BOUNDARY_RECT)
179                 return location_boundary_new_for_rect(boundary->rect.left_top, boundary->rect.right_bottom);
180         else if (boundary->type == LOCATION_BOUNDARY_CIRCLE)
181                 return location_boundary_new_for_circle(boundary->circle.center, boundary->circle.radius);
182         else if (boundary->type == LOCATION_BOUNDARY_POLYGON)
183                 return location_boundary_new_for_polygon(boundary->polygon.position_list);
184
185         return NULL;
186 }
187
188 EXPORT_API gboolean
189 location_boundary_if_inside(LocationBoundary *boundary, LocationPosition *position)
190 {
191         g_return_val_if_fail(boundary, FALSE);
192         g_return_val_if_fail(position, FALSE);
193
194         gboolean is_inside = FALSE;
195
196         switch (boundary->type) {
197         case LOCATION_BOUNDARY_RECT: {
198                                 gdouble y = position->latitude;
199                                 gdouble x = position->longitude;
200
201                                 gdouble lt_y = boundary->rect.left_top->latitude;
202                                 gdouble lt_x = boundary->rect.left_top->longitude;
203                                 gdouble rb_y = boundary->rect.right_bottom->latitude;
204                                 gdouble rb_x = boundary->rect.right_bottom->longitude;
205
206                                 if (lt_x - rb_x < 180 && lt_x - rb_x > -180) {
207                                         if ((rb_y < y && y < lt_y) && (lt_x < x && x < rb_x)) {
208                                                 LOCATION_LOGD("\tInside of Rectangular boundary");
209                                                 is_inside = TRUE;
210                                         }
211                                 } else {
212                                         if ((rb_y < y && y < lt_y) && (lt_x < x || x < rb_x)) {
213                                                 LOCATION_LOGD("\tInside of Rectangular boundary near 180th meridian");
214                                                 is_inside = TRUE;
215                                         }
216                                 }
217                                 break;
218                         }
219         case LOCATION_BOUNDARY_CIRCLE: {
220
221                                 LocationPosition center;
222                                 gulong distance = 0;
223
224                                 center.latitude = boundary->circle.center->latitude;
225                                 center.longitude = boundary->circle.center->longitude;
226
227                                 location_get_distance(&center, position, &distance);
228                                 if (distance < boundary->circle.radius) {
229                                         LOCATION_LOGD("\tInside Circle boundary");
230                                         is_inside = TRUE;
231                                 }
232                                 break;
233                         }
234         case LOCATION_BOUNDARY_POLYGON: {
235
236                                 double interval_x = 0.0, interval_y = 0.0;
237                                 double x0 = 0.0, y0 = 0.0;
238                                 gboolean edge_area;
239                                 int crossing_num = 0;
240                                 GList *position_list = boundary->polygon.position_list;
241                                 GList *pos1_list = NULL;
242                                 GList *pos2_list = NULL;
243                                 LocationPosition *pos1 = NULL;
244                                 LocationPosition *pos2 = NULL;
245
246                                 pos1_list = g_list_first(position_list);
247                                 pos2_list = g_list_last(position_list);
248                                 while (pos1_list) {
249                                         edge_area = FALSE;
250                                         pos1 = pos1_list->data;
251                                         pos2 = pos2_list->data;
252                                         interval_y = pos1->longitude - pos2->longitude;
253                                         interval_x = pos1->latitude - pos2->latitude;
254                                         /**
255                                          * Case 1. -180 < longitude2 - longitude1 < 180 : normal case
256                                          * Case 2. longitude2 - longitude1 < -180               : interval_y = longitude2 - longitude1 + 360
257                                          * Case 3. longitude2 - longitude1 > 180                : intreval_y = longitude2 - longitude1 - 360
258                                          */
259                                         if (interval_y > 180) {
260                                                 interval_y = interval_y - 360;
261                                                 edge_area = TRUE;
262                                         } else if (interval_y < -180) {
263                                                 interval_y = interval_y + 360;
264                                                 edge_area = TRUE;
265                                         }
266
267                                         if (edge_area && (pos1->longitude > position->longitude) == (pos2->longitude > position->longitude)) {
268                                                 if (pos2->longitude * position->longitude > 0) {
269                                                         x0 = pos2->latitude;
270                                                         y0 = pos2->longitude;
271                                                 } else {
272                                                         x0 = pos1->latitude;
273                                                         y0 = pos1->longitude;
274                                                 }
275
276                                                 if (position->latitude < ((interval_x / interval_y) * (position->longitude - y0) + x0)) {
277
278                                                         LOCATION_LOGD("Reverse");
279                                                         crossing_num++;
280                                                 }
281                                         } else if (!edge_area && (pos1->longitude > position->longitude) != (pos2->longitude > position->longitude)) {
282                                                 x0 = pos2->latitude;
283                                                 y0 = pos2->longitude;
284                                                 if (position->latitude < ((interval_x / interval_y) * (position->longitude - y0) + x0)) {
285                                                         LOCATION_LOGD("Common");
286                                                         crossing_num++;
287                                                 }
288                                         } else LOCATION_LOGD("It is not crossed.");
289
290                                         pos2_list = pos1_list;
291                                         pos1_list = g_list_next(pos1_list);
292                                 }
293                                 LOCATION_LOGW("num[%d]", crossing_num);
294                                 is_inside = crossing_num & 1; /* Odd : inside, Even : outside */
295
296                                 break;
297                         }
298         default: {
299                                 LOCATION_LOGW("\tboundary type is undefined.[%d]", boundary->type);
300                                 break;
301                         }
302         }
303
304         return is_inside;
305 }
306
307 EXPORT_API int
308 location_boundary_add(const LocationObject *obj, const LocationBoundary *boundary)
309 {
310         g_return_val_if_fail(obj, LOCATION_ERROR_PARAMETER);
311         g_return_val_if_fail(boundary, LOCATION_ERROR_PARAMETER);
312
313         GList *boundary_priv_list = NULL;
314
315         LocationBoundaryPrivate *priv = g_slice_new0(LocationBoundaryPrivate);
316         g_return_val_if_fail(priv, LOCATION_ERROR_PARAMETER);
317
318         priv->boundary = location_boundary_copy(boundary);
319         priv->zone_status = ZONE_STATUS_NONE;
320
321         boundary_priv_list = g_list_append(boundary_priv_list, (gpointer) priv);
322
323         g_object_set(G_OBJECT(obj), "boundary", boundary_priv_list, NULL);
324
325         g_list_free(boundary_priv_list);
326
327         return LOCATION_ERROR_NONE;
328 }
329
330 EXPORT_API int
331 location_boundary_remove(const LocationObject *obj, const LocationBoundary *boundary)
332 {
333         g_return_val_if_fail(obj, LOCATION_ERROR_PARAMETER);
334         g_return_val_if_fail(boundary, LOCATION_ERROR_PARAMETER);
335
336         g_object_set(G_OBJECT(obj), "removal-boundary", boundary, NULL);
337
338         return LOCATION_ERROR_NONE;
339 }
340
341 EXPORT_API int
342 location_boundary_foreach(const LocationObject *obj, LocationBoundaryFunc func, gpointer user_data)
343 {
344         g_return_val_if_fail(obj, LOCATION_ERROR_PARAMETER);
345         g_return_val_if_fail(func, LOCATION_ERROR_PARAMETER);
346
347         int index = 0;
348         GList *boundary_priv_list = NULL;
349         GList *boundary_list = NULL;
350         LocationBoundaryPrivate *priv;
351
352         g_object_get(G_OBJECT(obj), "boundary", &boundary_priv_list, NULL);
353
354         if (boundary_priv_list == NULL) {
355                 LOCATION_LOGD("There is no boundary.");
356                 return LOCATION_ERROR_UNKNOWN;
357         }
358
359         while ((priv = (LocationBoundaryPrivate *)g_list_nth_data(boundary_priv_list, index)) != NULL) {
360                 boundary_list = g_list_append(boundary_list, (gpointer) priv->boundary);
361                 index++;
362         }
363
364         g_list_foreach(boundary_list, (GFunc)func, user_data);
365         g_list_free(boundary_list);
366
367         return LOCATION_ERROR_NONE;
368 }
369
370 EXPORT_API LocationBoundary *
371 location_boundary_get_bounding_box(LocationBoundary *boundary)
372 {
373         g_return_val_if_fail(boundary, NULL);
374         LocationBoundary *bbox = NULL;
375
376         return bbox;
377 }
378
379 EXPORT_API LocationPosition *
380 location_boundary_get_center_position(LocationBoundary *boundary)
381 {
382         g_return_val_if_fail(boundary, NULL);
383
384         LocationPosition *center = NULL;
385
386         switch (boundary->type) {
387         case LOCATION_BOUNDARY_RECT: {
388                                 gdouble latitude, longitude, altitude;
389                                 latitude = (boundary->rect.left_top->latitude + boundary->rect.right_bottom->latitude) / 2.0;
390                                 longitude = (boundary->rect.left_top->longitude + boundary->rect.right_bottom->longitude) / 2.0;
391                                 altitude = (boundary->rect.left_top->altitude + boundary->rect.right_bottom->altitude) / 2.0;
392
393                                 center = location_position_new(boundary->rect.left_top->timestamp, latitude, longitude, altitude, boundary->rect.left_top->status);
394                                 break;
395                         }
396         case LOCATION_BOUNDARY_CIRCLE: {
397                                 center = location_position_copy(boundary->circle.center);
398                                 break;
399                         }
400         case LOCATION_BOUNDARY_POLYGON: {
401                                 gdouble center_latitude = 0.0;
402                                 gdouble center_longitude = 0.0;
403                                 gdouble area = 0.0;
404
405                                 gdouble x1, y1;
406                                 gdouble x2, y2;
407                                 GList *position_list = boundary->polygon.position_list;
408                                 GList *pos1_list = g_list_first(position_list);
409                                 GList *pos2_list = g_list_next(pos1_list);
410                                 LocationPosition *pos1 = NULL;
411                                 LocationPosition *pos2 = NULL;
412
413                                 while (pos2_list) {
414                                         pos1 = pos1_list->data;
415                                         pos2 = pos2_list->data;
416
417                                         x1 = pos1->latitude + 90.0;
418                                         y1 = pos1->longitude + 180.0;
419                                         x2 = pos2->latitude + 90.0;
420                                         y2 = pos2->longitude + 180.0;
421
422                                         center_latitude += (x1 + x2) * (x1 * y2 - x2 * y1);
423                                         center_longitude += (y1 + y2) * (x1 * y2 - x2 * y1);
424                                         area += x1 * y2 - x2 * y1;
425
426                                         pos1_list = pos2_list;
427                                         pos2_list = g_list_next(pos2_list);
428                                 }
429
430                                 pos2_list = g_list_first(position_list);
431                                 pos1 = pos1_list->data;
432                                 pos2 = pos2_list->data;
433
434                                 x1 = pos1->latitude + 90.0;
435                                 y1 = pos1->longitude + 180.0;
436                                 x2 = pos2->latitude + 90.0;
437                                 y2 = pos2->longitude + 180.0;
438
439                                 center_latitude += (x1 + x2) * (x1 * y2 - x2 * y1);
440                                 center_longitude += (y1 + y2) * (x1 * y2 - x2 * y1);
441                                 area += x1 * y2 - x2 * y1;
442
443                                 area = fabs(area / 2.0);
444                                 if (area != 0) {
445                                         center_latitude = (center_latitude - 90.0) / (6.0 * area);
446                                         center_longitude = (center_longitude - 180.0) / (6.0 * area);
447                                         center = location_position_new(0, center_latitude, center_longitude, 0, LOCATION_STATUS_2D_FIX);
448                                 }
449                                 break;
450                         }
451         default:
452                         break;
453         }
454         return center;
455 }