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