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