update for beta universally
[framework/location/libslp-location.git] / location / manager / location-boundary.c
1 /*
2  * libslp-location
3  *
4  * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Youngae Kang <youngae.kang@samsung.com>, Yunhan Kim <yhan.kim@samsung.com>,
7  *          Genie Kim <daejins.kim@samsung.com>, Minjune Kim <sena06.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 #ifdef HAVE_CONFIG_H
54 #include "config.h"
55 #endif
56
57 #include <math.h>
58 #include <string.h>
59 #include "location-boundary.h"
60 #include "location-log.h"
61
62 GType
63 location_boundary_get_type (void)
64 {
65         static volatile gsize type_volatile = 0;
66         if(g_once_init_enter(&type_volatile)) {
67                 GType type = g_boxed_type_register_static (
68                         g_intern_static_string ("LocationBoundary"),
69                         (GBoxedCopyFunc) location_boundary_copy,
70                         (GBoxedFreeFunc) location_boundary_free);
71                 g_once_init_leave(&type_volatile, type);
72         }
73         return type_volatile;
74 }
75
76 static void _append_polygon_position(gpointer data, gpointer user_data)
77 {
78         g_return_if_fail(data);
79         g_return_if_fail(user_data);
80
81         LocationBoundary* boundary = (LocationBoundary*)user_data;
82         LocationPosition* position = (LocationPosition *)data;
83         LocationPosition* new_position = location_position_copy(position);
84
85         boundary->polygon.position_list = g_list_append(boundary->polygon.position_list, new_position);
86 }
87
88 static void _free_polygon_position(gpointer data)
89 {
90         g_return_if_fail(data);
91
92         LocationPosition* position = (LocationPosition *)data;
93         location_position_free(position);
94 }
95
96
97 EXPORT_API LocationBoundary *
98 location_boundary_new_for_rect (LocationPosition* left_top,
99         LocationPosition* right_bottom)
100 {
101         g_return_val_if_fail(left_top, NULL);
102         g_return_val_if_fail(right_bottom, NULL);
103
104         gdouble lon_interval = right_bottom->longitude - left_top->longitude;
105
106         if(lon_interval < 180 && lon_interval > -180) {
107                 if(right_bottom->longitude <= left_top->longitude || right_bottom->latitude >= left_top->latitude)
108                         return NULL;
109         }
110         else {
111                 if(right_bottom->longitude >= left_top->longitude || right_bottom->latitude >= left_top->latitude)
112                         return NULL;
113         }
114
115         LocationBoundary* boundary = g_slice_new0 (LocationBoundary);
116         boundary->type = LOCATION_BOUNDARY_RECT;
117         boundary->rect.left_top = location_position_copy(left_top);
118         boundary->rect.right_bottom = location_position_copy(right_bottom);
119         return boundary;
120 }
121
122 EXPORT_API LocationBoundary *
123 location_boundary_new_for_circle (LocationPosition* center,
124         gdouble radius)
125 {
126         g_return_val_if_fail(center, NULL);
127         g_return_val_if_fail(radius > 0, NULL);
128         LocationBoundary* boundary = g_slice_new0 (LocationBoundary);
129         boundary->type = LOCATION_BOUNDARY_CIRCLE;
130         boundary->circle.center = location_position_copy(center);
131         boundary->circle.radius = radius;
132         return boundary;
133 }
134
135 EXPORT_API LocationBoundary *
136 location_boundary_new_for_polygon(GList *position_list)
137 {
138         g_return_val_if_fail(position_list, NULL);
139         g_return_val_if_fail(g_list_length(position_list) > 2, NULL);
140
141         LocationBoundary *boundary = g_slice_new0 (LocationBoundary);
142
143         g_list_foreach(position_list, (GFunc)_append_polygon_position, boundary);
144         boundary->type = LOCATION_BOUNDARY_POLYGON;
145         boundary->polygon.position_list = g_list_first(boundary->polygon.position_list);
146
147         return boundary;
148 }
149
150 EXPORT_API void
151 location_boundary_free (LocationBoundary* boundary)
152 {
153         g_return_if_fail(boundary);
154
155         if (boundary->type == LOCATION_BOUNDARY_RECT) {
156                 location_position_free(boundary->rect.left_top);
157                 location_position_free(boundary->rect.right_bottom);
158         } else if (boundary->type == LOCATION_BOUNDARY_CIRCLE) {
159                 location_position_free(boundary->circle.center);
160         } else if (boundary->type == LOCATION_BOUNDARY_POLYGON) {
161                 g_list_free_full(boundary->polygon.position_list, (GDestroyNotify)_free_polygon_position);
162         }
163         g_slice_free(LocationBoundary, boundary);
164 }
165
166 EXPORT_API LocationBoundary*
167 location_boundary_copy (const LocationBoundary* boundary)
168 {
169         g_return_val_if_fail(boundary, NULL);
170         if (boundary->type == LOCATION_BOUNDARY_RECT) {
171                 return location_boundary_new_for_rect(boundary->rect.left_top, boundary->rect.right_bottom);
172         } else if (boundary->type == LOCATION_BOUNDARY_CIRCLE) {
173                 return location_boundary_new_for_circle(boundary->circle.center, boundary->circle.radius);
174         } else if (boundary->type == LOCATION_BOUNDARY_POLYGON) {
175                 return location_boundary_new_for_polygon(boundary->polygon.position_list);
176         }
177         return NULL;
178 }
179
180
181 EXPORT_API gboolean
182 location_boundary_if_inside (LocationBoundary* boundary,
183         LocationPosition* position)
184 {
185         g_return_val_if_fail(boundary, FALSE);
186         g_return_val_if_fail(position, FALSE);
187
188         gboolean is_inside = FALSE;
189
190         switch(boundary->type) {
191
192                 case LOCATION_BOUNDARY_RECT: {
193                         gdouble y = position->latitude;
194                         gdouble x = position->longitude;
195
196                         gdouble lt_y = boundary->rect.left_top->latitude;
197                         gdouble lt_x = boundary->rect.left_top->longitude;
198                         gdouble rb_y = boundary->rect.right_bottom->latitude;
199                         gdouble rb_x = boundary->rect.right_bottom->longitude;
200
201                         if (lt_x - rb_x < 180 && lt_x - rb_x > -180) {
202                                 if ((rb_y < y && y < lt_y) && ( lt_x < x && x < rb_x)) {
203                                         LOCATION_LOGD("\tInside of Rectangular boundary");
204                                         is_inside = TRUE;
205                                 }
206                         }
207                         else {
208                                 if ((rb_y < y && y < lt_y) && ( lt_x < x || x < rb_x)) {
209                                         LOCATION_LOGD("\tInside of Rectangular boundary near 180th meridian");
210                                         is_inside = TRUE;
211                                 }
212                         }
213                         break;
214                 }
215                 case LOCATION_BOUNDARY_CIRCLE: {
216
217                         LocationPosition center;
218                         gulong distance = 0;
219
220                         center.latitude = boundary->circle.center->latitude;
221                         center.longitude = boundary->circle.center->longitude;
222
223                         location_get_distance(&center, position, &distance);
224                         if (distance  < boundary->circle.radius){
225                                 LOCATION_LOGD("\tInside Circle boundary");
226                                 is_inside = TRUE;
227                         }
228                         break;
229                 }
230                 case LOCATION_BOUNDARY_POLYGON: {
231
232                         int i, j;
233                         double interval_x = 0.0, interval_y = 0.0;
234                         double x0, y0;
235                         gboolean edge_area;
236                         int crossing_num = 0;
237                         GList *position_list = boundary->polygon.position_list;
238                         int count = g_list_length(position_list);
239                         GList *pos1_list = NULL;
240                         GList *pos2_list = NULL;
241                         LocationPosition* pos1 = NULL;
242                         LocationPosition* pos2 = NULL;
243
244                         i = 0;
245                         j = count - 1;
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                                 }
263                                 else if (interval_y < -180) {
264                                         interval_y = interval_y + 360;
265                                         edge_area = TRUE;
266                                 }
267
268                                 if (edge_area && (pos1->longitude > position->longitude) == (pos2->longitude > position->longitude)){
269                                         if (pos2->longitude * position->longitude > 0) {
270                                                 x0 = pos2->latitude;
271                                                 y0 = pos2->longitude;
272                                         }
273                                         else {
274                                                 x0 = pos1->latitude;
275                                                 y0 = pos1->longitude;
276                                         }
277
278                                         if (position->latitude < ((interval_x/interval_y)*(position->longitude - y0) + x0)) {
279
280                                                 LOCATION_LOGD("Reverse");
281                                                 crossing_num++;
282                                         }
283                                 }
284                                 else if (!edge_area && (pos1->longitude > position->longitude) != (pos2->longitude > position->longitude)) {
285                                         x0 = pos2->latitude;
286                                         y0 = pos2->longitude;
287                                         if (position->latitude < ((interval_x/interval_y)*(position->longitude - y0) + x0)) {
288                                                 LOCATION_LOGD("Common");
289                                                 crossing_num++;
290                                         }
291                                 }
292                                 else LOCATION_LOGD("It is not crossed.");
293
294                                 pos2_list = pos1_list;
295                                 pos1_list = g_list_next(pos1_list);
296                         }
297                         LOCATION_LOGW("num[%d]", crossing_num);
298                         is_inside = crossing_num & 1; // Odd : inside, Even : outside
299
300                         break;
301                 }
302                 default: {
303                         LOCATION_LOGW("\tboundary type is undefined.[%d]", boundary->type);
304                         break;
305                 }
306         }
307
308         return is_inside;
309 }
310
311 EXPORT_API int
312 location_boundary_add(const LocationObject *obj, const LocationBoundary *boundary)
313 {
314         g_return_val_if_fail (obj, LOCATION_ERROR_PARAMETER);
315         g_return_val_if_fail (boundary, LOCATION_ERROR_PARAMETER);
316
317         GList *boundary_list = NULL;
318
319         boundary_list = g_list_append(boundary_list, (gpointer) boundary);
320
321         g_object_set(G_OBJECT(obj), "boundary", boundary_list, NULL);
322
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         GList * boundary_list = NULL;
345
346         g_object_get(G_OBJECT(obj), "boundary", &boundary_list, NULL);
347
348         if (boundary_list == NULL) {
349                 LOCATION_LOGD("There is no boundary.");
350                 return LOCATION_ERROR_UNKNOWN;
351         }
352         g_list_foreach(boundary_list, (GFunc)func, user_data);
353
354         return LOCATION_ERROR_NONE;
355 }
356
357 EXPORT_API LocationBoundary *
358 location_boundary_get_bounding_box (LocationBoundary *boundary)
359 {
360         g_return_val_if_fail (boundary, NULL);
361         LocationBoundary *bbox = NULL;
362
363         return bbox;
364 }
365
366 EXPORT_API LocationPosition *
367 location_boundary_get_center_position (LocationBoundary *boundary)
368 {
369         g_return_val_if_fail (boundary, NULL);
370         LocationPosition *center = NULL;
371
372         return center;
373 }