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