1 /*
2 This file is part of darktable,
3 Copyright (C) 2010-2021 darktable developers.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "common/geo.h"
20 #include "common/map_locations.h"
21 #include "common/darktable.h"
22 #include "common/debug.h"
23 #include "common/tags.h"
24
25 // root for location geotagging
26 const char *location_tag = "darktable|locations";
27 const char *location_tag_prefix = "darktable|locations|";
28
29 // create a new location
dt_map_location_new(const char * const name)30 guint dt_map_location_new(const char *const name)
31 {
32 char *loc_name = g_strconcat(location_tag_prefix, name, NULL);
33 guint locid = -1;
34 dt_tag_new(loc_name, &locid);
35 g_free(loc_name);
36 return locid;
37 }
38
39 // remove a location
dt_map_location_delete(const guint locid)40 void dt_map_location_delete(const guint locid)
41 {
42 if(locid == -1) return;
43 char *name = dt_tag_get_name(locid);
44 if(name)
45 {
46 if(g_str_has_prefix(name, location_tag_prefix))
47 {
48 sqlite3_stmt *stmt;
49 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
50 "DELETE FROM data.locations WHERE tagid=?1",
51 -1, &stmt, NULL);
52 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, locid);
53 sqlite3_step(stmt);
54 sqlite3_finalize(stmt);
55 dt_tag_remove(locid, TRUE);
56 }
57 g_free(name);
58 }
59 }
60
61 // rename a location
dt_map_location_rename(const guint locid,const char * const name)62 void dt_map_location_rename(const guint locid, const char *const name)
63 {
64 if(locid == -1 || !name || !name[0]) return;
65 char *old_name = dt_tag_get_name(locid);
66 if(old_name)
67 {
68 if(g_str_has_prefix(old_name, location_tag_prefix))
69 {
70 char *new_name = g_strconcat(location_tag_prefix, name, NULL);
71 dt_tag_rename(locid, new_name);
72 g_free(new_name);
73 }
74 g_free(old_name);
75 }
76 }
77
78 // does the location name already exist
dt_map_location_name_exists(const char * const name)79 gboolean dt_map_location_name_exists(const char *const name)
80 {
81 char *new_name = g_strconcat(location_tag_prefix, name, NULL);
82 const gboolean exists = dt_tag_exists(new_name, NULL);
83 g_free(new_name);
84 return exists;
85 }
86
87 // gets location's images number
dt_map_location_get_images_count(const guint locid)88 int dt_map_location_get_images_count(const guint locid)
89 {
90 int count = 0;
91 sqlite3_stmt *stmt;
92 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
93 "SELECT COUNT (*)"
94 " FROM main.tagged_images"
95 " WHERE tagid = ?1",
96 -1, &stmt, NULL);
97 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, locid);
98 if(sqlite3_step(stmt) == SQLITE_ROW)
99 count = sqlite3_column_int(stmt, 0);
100 sqlite3_finalize(stmt);
101 return count;
102 }
103
104 // retrieve list of tags which are on that path
dt_map_location_get_locations_by_path(const gchar * path,const gboolean remove_root)105 GList *dt_map_location_get_locations_by_path(const gchar *path,
106 const gboolean remove_root)
107 {
108 if(!path) return NULL;
109
110 gchar *path1, *path2;
111 if(!path[0])
112 {
113 path1 = g_strdup(location_tag);
114 path2 = g_strdup_printf("%s|", path1);
115 }
116 else
117 {
118 path1 = g_strconcat(location_tag_prefix, path, NULL);
119 path2 = g_strdup_printf("%s|", path1);
120 }
121 GList *locs = NULL;
122
123 sqlite3_stmt *stmt;
124 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
125 "SELECT t.id, t.name, ti.count"
126 " FROM data.tags AS t"
127 " LEFT JOIN (SELECT tagid,"
128 " COUNT(DISTINCT imgid) AS count"
129 " FROM main.tagged_images"
130 " GROUP BY tagid) AS ti"
131 " ON ti.tagid = t.id"
132 " WHERE name = ?1 OR SUBSTR(name, 1, LENGTH(?2)) = ?2",
133 -1, &stmt, NULL);
134 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 1, path1, -1, SQLITE_TRANSIENT);
135 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, path2, -1, SQLITE_TRANSIENT);
136 while(sqlite3_step(stmt) == SQLITE_ROW)
137 {
138 const char *name = (const char *)sqlite3_column_text(stmt, 1);
139 const int lgth = remove_root ? strlen(path1) + 1 : strlen(location_tag_prefix);
140 if(name && strlen(name) > lgth)
141 {
142 dt_map_location_t *t = g_malloc0(sizeof(dt_map_location_t));
143 if(t)
144 {
145 name += lgth;
146 t->tag = g_strdup(name);
147 t->id = sqlite3_column_int(stmt, 0);
148 t->count = sqlite3_column_int(stmt, 2);
149 locs = g_list_prepend(locs, t);
150 }
151 }
152 }
153 sqlite3_finalize(stmt);
154
155 g_free(path1);
156 g_free(path2);
157 return locs;
158 }
159
dt_map_location_get_locations_on_map(const dt_map_box_t * const bbox)160 GList *dt_map_location_get_locations_on_map(const dt_map_box_t *const bbox)
161 {
162 GList *locs = NULL;
163
164 sqlite3_stmt *stmt;
165 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
166 "SELECT *"
167 " FROM data.locations AS t"
168 " WHERE latitude IS NOT NULL"
169 " AND (latitude + delta2) > ?2"
170 " AND (latitude - delta2) < ?1"
171 " AND (longitude + delta1) > ?3"
172 " AND (longitude - delta1) < ?4",
173 -1, &stmt, NULL);
174
175 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 1, bbox->lat1);
176 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 2, bbox->lat2);
177 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 3, bbox->lon1);
178 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 4, bbox->lon2);
179
180 while(sqlite3_step(stmt) == SQLITE_ROW)
181 {
182 dt_location_draw_t *t = g_malloc0(sizeof(dt_location_draw_t));
183 if(t)
184 {
185 t->id = sqlite3_column_int(stmt, 0);
186 t->data.shape = sqlite3_column_int(stmt, 1);
187 t->data.lon = sqlite3_column_double(stmt, 2);
188 t->data.lat = sqlite3_column_double(stmt, 3);
189 t->data.delta1 = sqlite3_column_double(stmt, 4);
190 t->data.delta2 = sqlite3_column_double(stmt, 5);
191 t->data.ratio = sqlite3_column_double(stmt, 6);
192 locs = g_list_prepend(locs, t);
193 }
194 }
195 sqlite3_finalize(stmt);
196
197 return locs;
198 }
199
dt_map_location_get_polygons(dt_location_draw_t * ld)200 void dt_map_location_get_polygons(dt_location_draw_t *ld)
201 {
202 if(ld->data.shape != MAP_LOCATION_SHAPE_POLYGONS)
203 return;
204 sqlite3_stmt *stmt;
205 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
206 "SELECT polygons FROM data.locations AS t"
207 " WHERE tagid = ?1",
208 -1, &stmt, NULL);
209
210 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, ld->id);
211 if(sqlite3_step(stmt) == SQLITE_ROW)
212 {
213 ld->data.plg_pts = sqlite3_column_bytes(stmt, 0);
214 dt_geo_map_display_point_t *p = malloc(ld->data.plg_pts);
215 memcpy(p, sqlite3_column_blob(stmt, 0), ld->data.plg_pts);
216 ld->data.plg_pts /= sizeof(dt_geo_map_display_point_t);
217 GList *pol = NULL;
218 for(int i = 0; i < ld->data.plg_pts; i++, p++)
219 pol = g_list_prepend(pol, p);
220 pol = g_list_reverse(pol);
221 ld->data.polygons = pol;
222 }
223 sqlite3_finalize(stmt);
224 }
225
dt_map_location_free_polygons(dt_location_draw_t * ld)226 void dt_map_location_free_polygons(dt_location_draw_t *ld)
227 {
228 if(ld->data.shape == MAP_LOCATION_SHAPE_POLYGONS && ld->data.polygons)
229 {
230 g_free(ld->data.polygons->data);
231 g_list_free(ld->data.polygons);
232 }
233 ld->data.polygons = NULL;
234 ld->data.plg_pts = 0;
235 }
236
_is_point_in_polygon(const dt_geo_map_display_point_t * pt,const gint plg_pts,const dt_geo_map_display_point_t * plp)237 static gboolean _is_point_in_polygon(const dt_geo_map_display_point_t *pt,
238 const gint plg_pts, const dt_geo_map_display_point_t *plp)
239 {
240 gboolean inside = FALSE;
241 dt_geo_map_display_point_t *p = (dt_geo_map_display_point_t *)plp;
242 float lat1 = plp->lat;
243 float lon1 = plp->lon;
244 float lat2, lon2;
245 for(int i = 0; i < plg_pts; i++)
246 {
247 if(i < plg_pts - 1)
248 {
249 p++;
250 lat2 = p->lat;
251 lon2 = p->lon;
252 }
253 else
254 {
255 lat2 = plp->lat;
256 lon2 = plp->lon;
257 }
258 if(!(((lat1 > pt->lat) && (lat2 > pt->lat)) ||
259 ((lat1 < pt->lat) && (lat2 < pt->lat))))
260 {
261 const float sl = lon1 + (lon2 - lon1) * (pt->lat - lat1) / (lat2 - lat1);
262 if(pt->lon > sl)
263 inside = !inside;
264 }
265 lat1 = lat2;
266 lon1 = lon2;
267 }
268 return inside;
269 }
270
_free_result_item(dt_map_location_t * t,gpointer unused)271 static void _free_result_item(dt_map_location_t *t, gpointer unused)
272 {
273 g_free(t->tag);
274 g_free(t);
275 }
276
277 // free map location list
dt_map_location_free_result(GList ** result)278 void dt_map_location_free_result(GList **result)
279 {
280 if(result && *result)
281 {
282 g_list_free_full(*result, (GDestroyNotify)_free_result_item);
283 }
284 }
285
_sort_by_path(gconstpointer a,gconstpointer b)286 static gint _sort_by_path(gconstpointer a, gconstpointer b)
287 {
288 const dt_map_location_t *tuple_a = (const dt_map_location_t *)a;
289 const dt_map_location_t *tuple_b = (const dt_map_location_t *)b;
290
291 return g_strcmp0(tuple_a->tag, tuple_b->tag);
292 }
293
294 // sort the tag list considering the '|' character
dt_map_location_sort(GList * tags)295 GList *dt_map_location_sort(GList *tags)
296 {
297 // order such that sub tags are coming directly behind their parent
298 GList *sorted_tags;
299 for(GList *taglist = tags; taglist; taglist = g_list_next(taglist))
300 {
301 gchar *tag = ((dt_map_location_t *)taglist->data)->tag;
302 for(char *letter = tag; *letter; letter++)
303 if(*letter == '|') *letter = '\1';
304 }
305 sorted_tags = g_list_sort(tags, _sort_by_path);
306 for(GList *taglist = sorted_tags; taglist; taglist = g_list_next(taglist))
307 {
308 gchar *tag = ((dt_map_location_t *)taglist->data)->tag;
309 for(char *letter = tag; *letter; letter++)
310 if(*letter == '\1') *letter = '|';
311 }
312 return sorted_tags;
313 }
314
315 // get location's data
dt_map_location_get_data(const guint locid)316 dt_map_location_data_t *dt_map_location_get_data(const guint locid)
317 {
318 if(locid == -1) return NULL;
319 dt_map_location_data_t *g = NULL;
320 sqlite3_stmt *stmt;
321 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
322 "SELECT type, longitude, latitude, delta1, delta2, ratio"
323 " FROM data.locations"
324 " JOIN data.tags ON id = tagid"
325 " WHERE tagid = ?1 AND longitude IS NOT NULL"
326 " AND SUBSTR(name, 1, LENGTH(?2)) = ?2",
327 -1, &stmt, NULL);
328 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, locid);
329 DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, location_tag_prefix, -1, SQLITE_STATIC);
330
331 if(sqlite3_step(stmt) == SQLITE_ROW)
332 {
333 g = (dt_map_location_data_t *)g_malloc0(sizeof(dt_map_location_data_t));
334 g->shape = sqlite3_column_int(stmt, 0);
335 g->lon = sqlite3_column_double(stmt, 1);
336 g->lat = sqlite3_column_double(stmt, 2);
337 g->delta1 = sqlite3_column_double(stmt, 3);
338 g->delta2 = sqlite3_column_double(stmt, 4);
339 g->ratio = sqlite3_column_double(stmt, 5);
340 }
341 sqlite3_finalize(stmt);
342 return g;
343 }
344
345 // set locations's data
dt_map_location_set_data(const guint locid,const dt_map_location_data_t * g)346 void dt_map_location_set_data(const guint locid, const dt_map_location_data_t *g)
347 {
348 if(locid == -1) return;
349 sqlite3_stmt *stmt;
350 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
351 "INSERT OR REPLACE INTO data.locations"
352 " (tagid, type, longitude, latitude, delta1, delta2, ratio, polygons)"
353 " VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
354 -1, &stmt, NULL);
355 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, locid);
356 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, g->shape);
357 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 3, g->lon);
358 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 4, g->lat);
359 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 5, g->delta1);
360 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 6, g->delta2);
361 DT_DEBUG_SQLITE3_BIND_DOUBLE(stmt, 7, g->ratio);
362 if(g->shape != MAP_LOCATION_SHAPE_POLYGONS)
363 {
364 DT_DEBUG_SQLITE3_BIND_BLOB(stmt, 8, NULL, 0, SQLITE_STATIC);
365 }
366 else
367 {
368 DT_DEBUG_SQLITE3_BIND_BLOB(stmt, 8, g->polygons->data,
369 g->plg_pts * (int)sizeof(dt_geo_map_display_point_t), SQLITE_STATIC);
370 }
371 sqlite3_step(stmt);
372 sqlite3_finalize(stmt);
373 }
374
375 // find locations which match with that image
dt_map_location_find_locations(const guint imgid)376 GList *dt_map_location_find_locations(const guint imgid)
377 {
378 GList *tags = NULL;
379 sqlite3_stmt *stmt;
380 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
381 "SELECT l.tagid, l.type, i.longitude, i.latitude FROM main.images AS i"
382 " JOIN data.locations AS l"
383 " ON (l.type = ?2"
384 " AND ((((i.longitude-l.longitude)*(i.longitude-l.longitude))/"
385 "(delta1*delta1) +"
386 " ((i.latitude-l.latitude)*(i.latitude-l.latitude))/"
387 "(delta2*delta2)) <= 1)"
388 " OR ((l.type = ?3 OR l.type = ?4)"
389 " AND i.longitude>=(l.longitude-delta1)"
390 " AND i.longitude<=(l.longitude+delta1)"
391 " AND i.latitude>=(l.latitude-delta2)"
392 " AND i.latitude<=(l.latitude+delta2)))"
393 " WHERE i.id = ?1 "
394 " AND i.latitude IS NOT NULL AND i.longitude IS NOT NULL",
395 -1, &stmt, NULL);
396 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
397 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, MAP_LOCATION_SHAPE_ELLIPSE);
398 DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, MAP_LOCATION_SHAPE_RECTANGLE);
399 DT_DEBUG_SQLITE3_BIND_INT(stmt, 4, MAP_LOCATION_SHAPE_POLYGONS);
400
401 while(sqlite3_step(stmt) == SQLITE_ROW)
402 {
403 const int id = sqlite3_column_int(stmt, 0);
404 if(sqlite3_column_int(stmt, 1) == MAP_LOCATION_SHAPE_POLYGONS)
405 {
406 dt_geo_map_display_point_t pt;
407 pt.lon = sqlite3_column_double(stmt, 2);
408 pt.lat = sqlite3_column_double(stmt, 3);
409 sqlite3_stmt *stmt2;
410 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
411 "SELECT polygons FROM data.locations "
412 " WHERE tagid = ?1",
413 -1, &stmt2, NULL);
414 DT_DEBUG_SQLITE3_BIND_INT(stmt2, 1, id);
415 if(sqlite3_step(stmt2) == SQLITE_ROW)
416 {
417 const gint plg_pts = sqlite3_column_bytes(stmt2, 0) / sizeof(dt_geo_map_display_point_t);
418 if(_is_point_in_polygon(&pt, plg_pts, sqlite3_column_blob(stmt2, 0)))
419 {
420 tags = g_list_prepend(tags, GINT_TO_POINTER(id));
421 }
422 }
423 sqlite3_finalize(stmt2);
424 }
425 else
426 {
427 tags = g_list_prepend(tags, GINT_TO_POINTER(id));
428 }
429 }
430 sqlite3_finalize(stmt);
431 return tags;
432 }
433
434 // find images which match with that location
_map_location_find_images(dt_location_draw_t * ld)435 GList *_map_location_find_images(dt_location_draw_t *ld)
436 {
437 GList *imgs = NULL;
438 sqlite3_stmt *stmt;
439 if(ld->data.shape == MAP_LOCATION_SHAPE_ELLIPSE)
440 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
441 "SELECT i.id FROM main.images AS i"
442 " JOIN data.locations AS l"
443 " ON (l.type = ?2"
444 " AND ((((i.longitude-l.longitude)*(i.longitude-l.longitude))/"
445 "(delta1*delta1) +"
446 " ((i.latitude-l.latitude)*(i.latitude-l.latitude))/"
447 "(delta2*delta2)) <= 1))"
448 " WHERE l.tagid = ?1 ",
449 -1, &stmt, NULL);
450 else if(ld->data.shape == MAP_LOCATION_SHAPE_RECTANGLE)
451 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
452 "SELECT i.id FROM main.images AS i"
453 " JOIN data.locations AS l"
454 " ON (l.type = ?2"
455 " AND i.longitude>=(l.longitude-delta1)"
456 " AND i.longitude<=(l.longitude+delta1)"
457 " AND i.latitude>=(l.latitude-delta2)"
458 " AND i.latitude<=(l.latitude+delta2))"
459 " WHERE l.tagid = ?1 ",
460 -1, &stmt, NULL);
461 else // MAP_LOCATION_SHAPE_POLYGONS
462 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
463 "SELECT i.id, i.longitude, i.latitude FROM main.images AS i"
464 " JOIN data.locations AS l"
465 " ON (l.type = ?2"
466 " AND i.longitude>=(l.longitude-delta1)"
467 " AND i.longitude<=(l.longitude+delta1)"
468 " AND i.latitude>=(l.latitude-delta2)"
469 " AND i.latitude<=(l.latitude+delta2))"
470 " WHERE l.tagid = ?1 ",
471 -1, &stmt, NULL);
472 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, ld->id);
473 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, ld->data.shape);
474
475 int i = 0;
476 while(sqlite3_step(stmt) == SQLITE_ROW)
477 {
478 const int id = sqlite3_column_int(stmt, 0);
479 if(ld->data.shape == MAP_LOCATION_SHAPE_POLYGONS)
480 {
481 dt_geo_map_display_point_t pt;
482 pt.lon = sqlite3_column_double(stmt, 1);
483 pt.lat = sqlite3_column_double(stmt, 2);
484 if(_is_point_in_polygon(&pt, ld->data.plg_pts, ld->data.polygons->data))
485 imgs = g_list_prepend(imgs, GINT_TO_POINTER(id));
486 }
487 else
488 imgs = g_list_prepend(imgs, GINT_TO_POINTER(id));
489 i++;
490 }
491 sqlite3_finalize(stmt);
492 return imgs;
493 }
494
495 // update image's locations - remove old ones and add new ones
dt_map_location_update_locations(const guint imgid,const GList * tags)496 void dt_map_location_update_locations(const guint imgid, const GList *tags)
497 {
498 // get current locations
499 GList *old_tags = NULL;
500 sqlite3_stmt *stmt;
501 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
502 "SELECT t.id FROM main.tagged_images ti"
503 " JOIN data.tags AS t ON t.id = ti.tagid"
504 " JOIN data.locations AS l ON l.tagid = t.id"
505 " WHERE imgid = ?1",
506 -1, &stmt, NULL);
507 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
508
509 while(sqlite3_step(stmt) == SQLITE_ROW)
510 {
511 const int id = sqlite3_column_int(stmt, 0);
512 old_tags = g_list_prepend(old_tags, GINT_TO_POINTER(id));
513 }
514 sqlite3_finalize(stmt);
515
516 // clean up locations which are not valid anymore
517 for(GList *tag = old_tags; tag; tag = g_list_next(tag))
518 {
519 if(!g_list_find((GList *)tags, tag->data))
520 {
521 dt_tag_detach(GPOINTER_TO_INT(tag->data), imgid,
522 FALSE, FALSE);
523 }
524 }
525
526 // add new locations
527 for(GList *tag = (GList *)tags; tag; tag = g_list_next(tag))
528 {
529 if(!g_list_find(old_tags, tag->data))
530 {
531 dt_tag_attach(GPOINTER_TO_INT(tag->data), imgid,
532 FALSE, FALSE);
533 }
534 }
535 g_list_free(old_tags);
536 }
537
538 // update location's images - remove old ones and add new ones
dt_map_location_update_images(dt_location_draw_t * ld)539 gboolean dt_map_location_update_images(dt_location_draw_t *ld)
540 {
541 // get previous images
542 GList *imgs = dt_tag_get_images(ld->id);
543
544 // find images in that location
545 GList *new_imgs = _map_location_find_images(ld);
546
547 gboolean res = FALSE;
548 // detach images which are not in location anymore
549 for(GList *img = imgs; img; img = g_list_next(img))
550 {
551 if(!g_list_find(new_imgs, img->data))
552 {
553 dt_tag_detach(ld->id, GPOINTER_TO_INT(img->data), FALSE, FALSE);
554 res = TRUE;
555 }
556 }
557
558 // add new images to location
559 for(GList *img = new_imgs; img; img = g_list_next(img))
560 {
561 if(!g_list_find(imgs, img->data))
562 {
563 dt_tag_attach(ld->id, GPOINTER_TO_INT(img->data), FALSE, FALSE);
564 res = TRUE;
565 }
566 }
567 g_list_free(new_imgs);
568 g_list_free(imgs);
569 return res;
570 }
571
572 // return root tag for location geotagging
dt_map_location_data_tag_root()573 const char *dt_map_location_data_tag_root()
574 {
575 return location_tag;
576 }
577
578 // tell if the point (lon, lat) belongs to location
dt_map_location_included(const float lon,const float lat,dt_map_location_data_t * g)579 gboolean dt_map_location_included(const float lon, const float lat,
580 dt_map_location_data_t *g)
581 {
582 gboolean included = FALSE;
583 if((g->shape == MAP_LOCATION_SHAPE_ELLIPSE &&
584 (((g->lon - lon) * (g->lon - lon) / (g->delta1 * g->delta1) +
585 (g->lat - lat) * (g->lat - lat) / (g->delta2 * g->delta2)) <= 1.0))
586 ||
587 (g->shape == MAP_LOCATION_SHAPE_RECTANGLE &&
588 lon > g->lon - g->delta1 && lon < g->lon + g->delta1 &&
589 lat > g->lat - g->delta2 && lat < g->lat + g->delta2))
590 {
591 included = TRUE;
592 }
593 return included;
594 }
595
596 // get the map box containing the polygon + flat polygons
dt_map_location_convert_polygons(void * polygons,dt_map_box_t * bbox,int * nb_pts)597 GList *dt_map_location_convert_polygons(void *polygons, dt_map_box_t *bbox, int *nb_pts)
598 {
599 const int nb = g_list_length(polygons);
600 dt_geo_map_display_point_t *points = malloc(nb * sizeof(dt_geo_map_display_point_t));
601 dt_geo_map_display_point_t *p = points;
602 dt_map_box_t bb = {180.0, -90.0, -180, 90.0};
603 GList *npol = NULL;
604
605 for(GList *pol = polygons; pol; pol = g_list_next(pol), p++)
606 {
607 dt_geo_map_display_point_t *pt = (dt_geo_map_display_point_t *)pol->data;
608 p->lat = pt->lat;
609 p->lon = pt->lon;
610 npol = g_list_prepend(npol, p);
611 if(bbox)
612 {
613 bb.lon1 = (pt->lon < bb.lon1) ? pt->lon : bb.lon1;
614 bb.lon2 = (pt->lon > bb.lon2) ? pt->lon : bb.lon2;
615 bb.lat1 = (pt->lat > bb.lat1) ? pt->lat : bb.lat1;
616 bb.lat2 = (pt->lat < bb.lat2) ? pt->lat : bb.lat2;
617 }
618 }
619 npol = g_list_reverse(npol);
620 if(bbox)
621 memcpy(bbox, &bb, sizeof(dt_map_box_t));
622 if(nb_pts)
623 *nb_pts = nb;
624 return (npol);
625 }
626
627 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
628 // vim: shiftwidth=2 expandtab tabstop=2 cindent
629 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
630