4 * Copyright (c) 2010-2013 Samsung Electronics Co., Ltd. All rights reserved.
6 * Contact: Youngae Kang <youngae.kang@samsung.com>, Minjune Kim <sena06.kim@samsung.com>
7 * Genie Kim <daejins.kim@samsung.com>
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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.
23 * location_boundary_if_inside(LocationBoundary* boundary,
24 * LocationPosition* position)
27 * Copyright (c) 1970-2003, Wm. Randolph Franklin
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:
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.
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
54 * location_boundary_get_center_position (LocationBoundary *boundary)
55 * algorithm from http://en.wikipedia.org/wiki/Centroid
65 #include "location-boundary.h"
66 #include "location-common-util.h"
67 #include "location-log.h"
70 location_boundary_get_type(void)
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);
83 static void _append_polygon_position(gpointer data, gpointer user_data)
85 g_return_if_fail(data);
86 g_return_if_fail(user_data);
88 LocationBoundary *boundary = (LocationBoundary *)user_data;
89 LocationPosition *position = (LocationPosition *)data;
90 LocationPosition *new_position = location_position_copy(position);
92 boundary->polygon.position_list = g_list_append(boundary->polygon.position_list, new_position);
95 static void _free_polygon_position(gpointer data)
97 g_return_if_fail(data);
99 LocationPosition *position = (LocationPosition *)data;
100 location_position_free(position);
103 EXPORT_API LocationBoundary *
104 location_boundary_new_for_rect(LocationPosition *left_top, LocationPosition *right_bottom)
106 g_return_val_if_fail(left_top, NULL);
107 g_return_val_if_fail(right_bottom, NULL);
109 gdouble lon_interval = right_bottom->longitude - left_top->longitude;
111 if (lon_interval < 180 && lon_interval > -180) {
112 if (right_bottom->longitude <= left_top->longitude || right_bottom->latitude >= left_top->latitude)
115 if (right_bottom->latitude >= left_top->latitude)
119 LocationBoundary *boundary = g_slice_new0(LocationBoundary);
120 g_return_val_if_fail(boundary, NULL);
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);
128 EXPORT_API LocationBoundary *
129 location_boundary_new_for_circle(LocationPosition *center, gdouble radius)
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);
136 boundary->type = LOCATION_BOUNDARY_CIRCLE;
137 boundary->circle.center = location_position_copy(center);
138 boundary->circle.radius = radius;
142 EXPORT_API LocationBoundary *
143 location_boundary_new_for_polygon(GList *position_list)
145 g_return_val_if_fail(position_list, NULL);
146 g_return_val_if_fail(g_list_length(position_list) > 2, NULL);
148 LocationBoundary *boundary = g_slice_new0(LocationBoundary);
149 g_return_val_if_fail(boundary, NULL);
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);
159 location_boundary_free(LocationBoundary *boundary)
161 g_return_if_fail(boundary);
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);
171 g_slice_free(LocationBoundary, boundary);
174 EXPORT_API LocationBoundary *
175 location_boundary_copy(const LocationBoundary *boundary)
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);
189 location_boundary_if_inside(LocationBoundary *boundary, LocationPosition *position)
191 g_return_val_if_fail(boundary, FALSE);
192 g_return_val_if_fail(position, FALSE);
194 gboolean is_inside = FALSE;
196 switch (boundary->type) {
197 case LOCATION_BOUNDARY_RECT: {
198 gdouble y = position->latitude;
199 gdouble x = position->longitude;
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;
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");
212 if ((rb_y < y && y < lt_y) && (lt_x < x || x < rb_x)) {
213 LOCATION_LOGD("\tInside of Rectangular boundary near 180th meridian");
219 case LOCATION_BOUNDARY_CIRCLE: {
221 LocationPosition center;
224 center.latitude = boundary->circle.center->latitude;
225 center.longitude = boundary->circle.center->longitude;
227 location_get_distance(¢er, position, &distance);
228 if (distance < boundary->circle.radius) {
229 LOCATION_LOGD("\tInside Circle boundary");
234 case LOCATION_BOUNDARY_POLYGON: {
236 double interval_x = 0.0, interval_y = 0.0;
237 double x0 = 0.0, y0 = 0.0;
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;
246 pos1_list = g_list_first(position_list);
247 pos2_list = g_list_last(position_list);
250 pos1 = pos1_list->data;
251 pos2 = pos2_list->data;
252 interval_y = pos1->longitude - pos2->longitude;
253 interval_x = pos1->latitude - pos2->latitude;
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
259 if (interval_y > 180) {
260 interval_y = interval_y - 360;
262 } else if (interval_y < -180) {
263 interval_y = interval_y + 360;
267 if (edge_area && (pos1->longitude > position->longitude) == (pos2->longitude > position->longitude)) {
268 if (pos2->longitude * position->longitude > 0) {
270 y0 = pos2->longitude;
273 y0 = pos1->longitude;
276 if (position->latitude < ((interval_x / interval_y) * (position->longitude - y0) + x0)) {
278 LOCATION_LOGD("Reverse");
281 } else if (!edge_area && (pos1->longitude > position->longitude) != (pos2->longitude > position->longitude)) {
283 y0 = pos2->longitude;
284 if (position->latitude < ((interval_x / interval_y) * (position->longitude - y0) + x0)) {
285 LOCATION_LOGD("Common");
288 } else LOCATION_LOGD("It is not crossed.");
290 pos2_list = pos1_list;
291 pos1_list = g_list_next(pos1_list);
293 LOCATION_LOGW("num[%d]", crossing_num);
294 is_inside = crossing_num & 1; /* Odd : inside, Even : outside */
299 LOCATION_LOGW("\tboundary type is undefined.[%d]", boundary->type);
308 location_boundary_add(const LocationObject *obj, const LocationBoundary *boundary)
310 g_return_val_if_fail(obj, LOCATION_ERROR_PARAMETER);
311 g_return_val_if_fail(boundary, LOCATION_ERROR_PARAMETER);
313 GList *boundary_priv_list = NULL;
315 LocationBoundaryPrivate *priv = g_slice_new0(LocationBoundaryPrivate);
316 g_return_val_if_fail(priv, LOCATION_ERROR_PARAMETER);
318 priv->boundary = location_boundary_copy(boundary);
319 priv->zone_status = ZONE_STATUS_NONE;
321 boundary_priv_list = g_list_append(boundary_priv_list, (gpointer) priv);
323 g_object_set(G_OBJECT(obj), "boundary", boundary_priv_list, NULL);
325 g_list_free(boundary_priv_list);
327 return LOCATION_ERROR_NONE;
331 location_boundary_remove(const LocationObject *obj, const LocationBoundary *boundary)
333 g_return_val_if_fail(obj, LOCATION_ERROR_PARAMETER);
334 g_return_val_if_fail(boundary, LOCATION_ERROR_PARAMETER);
336 g_object_set(G_OBJECT(obj), "removal-boundary", boundary, NULL);
338 return LOCATION_ERROR_NONE;
342 location_boundary_foreach(const LocationObject *obj, LocationBoundaryFunc func, gpointer user_data)
344 g_return_val_if_fail(obj, LOCATION_ERROR_PARAMETER);
345 g_return_val_if_fail(func, LOCATION_ERROR_PARAMETER);
348 GList *boundary_priv_list = NULL;
349 GList *boundary_list = NULL;
350 LocationBoundaryPrivate *priv;
352 g_object_get(G_OBJECT(obj), "boundary", &boundary_priv_list, NULL);
354 if (boundary_priv_list == NULL) {
355 LOCATION_LOGD("There is no boundary.");
356 return LOCATION_ERROR_UNKNOWN;
359 while ((priv = (LocationBoundaryPrivate *)g_list_nth_data(boundary_priv_list, index)) != NULL) {
360 boundary_list = g_list_append(boundary_list, (gpointer) priv->boundary);
364 g_list_foreach(boundary_list, (GFunc)func, user_data);
365 g_list_free(boundary_list);
367 return LOCATION_ERROR_NONE;
370 EXPORT_API LocationBoundary *
371 location_boundary_get_bounding_box(LocationBoundary *boundary)
373 g_return_val_if_fail(boundary, NULL);
374 LocationBoundary *bbox = NULL;
379 EXPORT_API LocationPosition *
380 location_boundary_get_center_position(LocationBoundary *boundary)
382 g_return_val_if_fail(boundary, NULL);
384 LocationPosition *center = NULL;
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;
393 center = location_position_new(boundary->rect.left_top->timestamp, latitude, longitude, altitude, boundary->rect.left_top->status);
396 case LOCATION_BOUNDARY_CIRCLE: {
397 center = location_position_copy(boundary->circle.center);
400 case LOCATION_BOUNDARY_POLYGON: {
401 gdouble center_latitude = 0.0;
402 gdouble center_longitude = 0.0;
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;
414 pos1 = pos1_list->data;
415 pos2 = pos2_list->data;
417 x1 = pos1->latitude + 90.0;
418 y1 = pos1->longitude + 180.0;
419 x2 = pos2->latitude + 90.0;
420 y2 = pos2->longitude + 180.0;
422 center_latitude += (x1 + x2) * (x1 * y2 - x2 * y1);
423 center_longitude += (y1 + y2) * (x1 * y2 - x2 * y1);
424 area += x1 * y2 - x2 * y1;
426 pos1_list = pos2_list;
427 pos2_list = g_list_next(pos2_list);
430 pos2_list = g_list_first(position_list);
431 pos1 = pos1_list->data;
432 pos2 = pos2_list->data;
434 x1 = pos1->latitude + 90.0;
435 y1 = pos1->longitude + 180.0;
436 x2 = pos2->latitude + 90.0;
437 y2 = pos2->longitude + 180.0;
439 center_latitude += (x1 + x2) * (x1 * y2 - x2 * y1);
440 center_longitude += (y1 + y2) * (x1 * y2 - x2 * y1);
441 area += x1 * y2 - x2 * y1;
443 area = fabs(area / 2.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);