Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
metadata/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 "metadata/geo.h"
26#include "system/macros.h"
27#include "system/mem_alloc.h"
30#include "metadata/tags.h"
31
32// root for location geotagging
33const char *location_tag = "darktable|locations";
34const char *location_tag_prefix = "darktable|locations|";
35
36// create a new location
37guint dt_map_location_new(const char *const name)
38{
40 guint locid = -1;
43 return locid;
44}
45
46// remove a location
48{
49 if(locid == -1) return;
50 char *name = dt_tag_get_name(locid);
51 if(name)
52 {
54 {
57 }
59 }
60}
61
62// rename a location
63void dt_map_location_rename(const guint locid, const char *const name)
64{
65 if(locid == -1 || IS_NULL_PTR(name) || !name[0]) return;
67 if(old_name)
68 {
70 {
74 }
76 }
77}
78
79// does the location name already exist
80gboolean dt_map_location_name_exists(const char *const name)
81{
83 const gboolean exists = dt_tag_exists(new_name, NULL);
85 return exists;
86}
87
88// gets location's images number
90{
91 /* The repository answers -1 when the count cannot be read (dt_tag_remove()'s original
92 * default); this function's original answered 0, and its callers size things by it. */
94 return count < 0 ? 0 : count;
95}
96
97// retrieve list of tags which are on that path
99 const gboolean remove_root)
100{
101 if(IS_NULL_PTR(path)) return NULL;
102
103 gchar *path1, *path2;
104 if(!path[0])
105 {
106 path1 = g_strdup(location_tag);
107 path2 = g_strdup_printf("%s|", path1);
108 }
109 else
110 {
111 path1 = g_strconcat(location_tag_prefix, path, NULL);
112 path2 = g_strdup_printf("%s|", path1);
113 }
114
116
117 /* The repository hands back full tag names; what a location is CALLED is this module's
118 * definition, so the trimming stays here. */
119 GList *locs = NULL;
120 for(GList *l = tags; l; l = g_list_next(l))
121 {
122 const dt_tag_count_t *tc = (const dt_tag_count_t *)l->data;
123 const int lgth = remove_root ? strlen(path1) + 1 : strlen(location_tag_prefix);
124 if(tc->name && strlen(tc->name) > lgth)
125 {
127 if(t)
128 {
129 t->tag = g_strdup(tc->name + lgth);
130 t->id = tc->id;
131 t->count = tc->count;
133 }
134 }
135 }
137
138 dt_free(path1);
139 dt_free(path2);
140 return locs;
141}
142
147
149{
150 if(ld->data.shape != MAP_LOCATION_SHAPE_POLYGONS)
151 return;
152
153 void *blob = NULL;
154 gint bytes = 0;
156 return;
157
159 ld->data.plg_pts = bytes / (gint)sizeof(dt_geo_map_display_point_t);
160
161 GList *pol = NULL;
162 for(int i = 0; i < ld->data.plg_pts; i++, p++)
164 ld->data.polygons = g_list_reverse(pol);
165}
166
168{
169 if(ld->data.shape == MAP_LOCATION_SHAPE_POLYGONS && ld->data.polygons)
170 {
171 dt_free(ld->data.polygons->data);
172 g_list_free(ld->data.polygons);
173 ld->data.polygons = NULL;
174 }
175 ld->data.polygons = NULL;
176 ld->data.plg_pts = 0;
177}
178
180 const gint plg_pts, const dt_geo_map_display_point_t *plp)
181{
182 gboolean inside = FALSE;
184 float lat1 = plp->lat;
185 float lon1 = plp->lon;
186 float lat2, lon2;
187 for(int i = 0; i < plg_pts; i++)
188 {
189 if(i < plg_pts - 1)
190 {
191 p++;
192 lat2 = p->lat;
193 lon2 = p->lon;
194 }
195 else
196 {
197 lat2 = plp->lat;
198 lon2 = plp->lon;
199 }
200 if(!(((lat1 > pt->lat) && (lat2 > pt->lat)) ||
202 {
203 const float sl = lon1 + (lon2 - lon1) * (pt->lat - lat1) / (lat2 - lat1);
204 if(pt->lon > sl)
205 inside = !inside;
206 }
207 lat1 = lat2;
208 lon1 = lon2;
209 }
210 return inside;
211}
212
214{
215 dt_free(t->tag);
216 dt_free(t);
217}
218
219// free map location list
221{
222 if(result && *result)
223 {
224 g_list_free_full(*result, (GDestroyNotify)_free_result_item);
225 *result = NULL;
226 }
227}
228
230{
231 const dt_map_location_t *tuple_a = (const dt_map_location_t *)a;
232 const dt_map_location_t *tuple_b = (const dt_map_location_t *)b;
233
234 return g_strcmp0(tuple_a->tag, tuple_b->tag);
235}
236
237// sort the tag list considering the '|' character
239{
240 // order such that sub tags are coming directly behind their parent
242 for(GList *taglist = tags; taglist; taglist = g_list_next(taglist))
243 {
244 gchar *tag = ((dt_map_location_t *)taglist->data)->tag;
245 for(char *letter = tag; *letter; letter++)
246 if(*letter == '|') *letter = '\1';
247 }
249 for(GList *taglist = sorted_tags; taglist; taglist = g_list_next(taglist))
250 {
251 gchar *tag = ((dt_map_location_t *)taglist->data)->tag;
252 for(char *letter = tag; *letter; letter++)
253 if(*letter == '\1') *letter = '|';
254 }
255 return sorted_tags;
256}
257
258// get location's data
264
265// set locations's data
267{
268 if(locid == -1) return;
270}
271
272// find locations which match with that image
274{
275 /* The SQL bounds a polygon location by its box; whether the image is actually inside
276 * the polygon is geometry, so it is decided here. */
278
279 GList *tags = NULL;
280 for(GList *c = candidates; c; c = g_list_next(c))
281 {
282 const dt_location_candidate_t *cand = (const dt_location_candidate_t *)c->data;
283 gboolean inside = TRUE;
284
286 {
287 void *blob = NULL;
288 gint bytes = 0;
289 inside = FALSE;
291 {
292 const dt_geo_map_display_point_t pt = { .lon = cand->lon, .lat = cand->lat };
293 inside = _is_point_in_polygon(&pt, bytes / (gint)sizeof(dt_geo_map_display_point_t), blob);
294 dt_free(blob);
295 }
296 }
297
298 if(inside) tags = g_list_prepend(tags, GINT_TO_POINTER(cand->id));
299 }
301
302 return tags;
303}
304
305// find images which match with that location
307{
309
310 GList *imgs = NULL;
311 for(GList *c = candidates; c; c = g_list_next(c))
312 {
313 const dt_location_candidate_t *cand = (const dt_location_candidate_t *)c->data;
314 if(ld->data.shape == MAP_LOCATION_SHAPE_POLYGONS)
315 {
316 const dt_geo_map_display_point_t pt = { .lon = cand->lon, .lat = cand->lat };
317 if(_is_point_in_polygon(&pt, ld->data.plg_pts, ld->data.polygons->data))
318 imgs = g_list_prepend(imgs, GINT_TO_POINTER(cand->id));
319 }
320 else
321 imgs = g_list_prepend(imgs, GINT_TO_POINTER(cand->id));
322 }
324
325 return imgs;
326}
327
328// update image's locations - remove old ones and add new ones
329void dt_map_location_update_locations(const int32_t imgid, const GList *tags)
330{
331 // get current locations
333
334 // clean up locations which are not valid anymore
335 for(GList *tag = old_tags; tag; tag = g_list_next(tag))
336 {
337 if(!g_list_find((GList *)tags, tag->data))
338 {
339 dt_tag_detach(GPOINTER_TO_INT(tag->data), imgid,
340 FALSE, FALSE);
341 }
342 }
343
344 // add new locations
345 for(GList *tag = (GList *)tags; tag; tag = g_list_next(tag))
346 {
347 if(!g_list_find(old_tags, tag->data))
348 {
349 dt_tag_attach(GPOINTER_TO_INT(tag->data), imgid,
350 FALSE, FALSE);
351 }
352 }
354 old_tags = NULL;
355}
356
357// update location's images - remove old ones and add new ones
359{
360 // get previous images
361 GList *imgs = dt_tag_get_images(ld->id);
362
363 // find images in that location
365
366 gboolean res = FALSE;
367 // detach images which are not in location anymore
368 for(GList *img = imgs; img; img = g_list_next(img))
369 {
370 if(!g_list_find(new_imgs, img->data))
371 {
372 dt_tag_detach(ld->id, GPOINTER_TO_INT(img->data), FALSE, FALSE);
373 res = TRUE;
374 }
375 }
376
377 // add new images to location
378 for(GList *img = new_imgs; img; img = g_list_next(img))
379 {
380 if(!g_list_find(imgs, img->data))
381 {
382 dt_tag_attach(ld->id, GPOINTER_TO_INT(img->data), FALSE, FALSE);
383 res = TRUE;
384 }
385 }
387 new_imgs = NULL;
388 g_list_free(imgs);
389 imgs = NULL;
390 return res;
391}
392
393// return root tag for location geotagging
395{
396 return location_tag;
397}
398
399// tell if the point (lon, lat) belongs to location
400gboolean dt_map_location_included(const float lon, const float lat,
402{
403 gboolean included = FALSE;
404 if((g->shape == MAP_LOCATION_SHAPE_ELLIPSE &&
405 (((g->lon - lon) * (g->lon - lon) / (g->delta1 * g->delta1) +
406 (g->lat - lat) * (g->lat - lat) / (g->delta2 * g->delta2)) <= 1.0))
407 ||
408 (g->shape == MAP_LOCATION_SHAPE_RECTANGLE &&
409 lon > g->lon - g->delta1 && lon < g->lon + g->delta1 &&
410 lat > g->lat - g->delta2 && lat < g->lat + g->delta2))
411 {
412 included = TRUE;
413 }
414 return included;
415}
416
417// get the map box containing the polygon + flat polygons
419{
420 const int nb = g_list_length(polygons);
423 dt_map_box_t bb = {180.0, -90.0, -180, 90.0};
424 GList *npol = NULL;
425
426 for(GList *pol = polygons; pol; pol = g_list_next(pol), p++)
427 {
429 p->lat = pt->lat;
430 p->lon = pt->lon;
432 if(bbox)
433 {
434 bb.lon1 = (pt->lon < bb.lon1) ? pt->lon : bb.lon1;
435 bb.lon2 = (pt->lon > bb.lon2) ? pt->lon : bb.lon2;
436 bb.lat1 = (pt->lat > bb.lat1) ? pt->lat : bb.lat1;
437 bb.lat2 = (pt->lat < bb.lat2) ? pt->lat : bb.lat2;
438 }
439 }
441 if(bbox)
442 memcpy(bbox, &bb, sizeof(dt_map_box_t));
443 if(nb_pts)
444 *nb_pts = nb;
445 return (npol);
446}
447
448// clang-format off
449// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
450// vim: shiftwidth=2 expandtab tabstop=2 cindent
451// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
452// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
const int t
dt_map_box_t bbox
Definition location.c:4
float lat
Definition location.c:3
float lon
Definition location.c:2
GList * dt_location_repository_get_in_bbox(const dt_map_box_t *const bbox)
Locations whose extent overlaps bbox.
gboolean dt_location_repository_get_polygon(const guint locid, void **blob, gint *bytes)
The raw polygon blob of locid.
GList * dt_location_repository_find_images_for_location(const guint locid, const int shape)
Images whose coordinates fall inside location locid.
GList * dt_location_repository_get_image_locations(const int32_t imgid)
Location (tag) ids currently attached to imgid. GINT_TO_POINTER list.
void dt_location_repository_set_data(const guint locid, const dt_map_location_data_t *g)
Insert or replace the geometry of locid, polygon blob included.
void dt_location_repository_delete(const guint locid)
Drop the geometry row of locid. The tag itself is the tag repository's.
GList * dt_location_repository_find_locations_for_image(const int32_t imgid)
Locations whose shape contains imgid's coordinates.
dt_map_location_data_t * dt_location_repository_get_data(const guint locid, const char *name_prefix)
The geometry of locid, if it is a real location.
data.locations: the geometry of a geotagging location.
#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 macros.h:65
@ MAP_LOCATION_SHAPE_POLYGONS
@ MAP_LOCATION_SHAPE_RECTANGLE
@ MAP_LOCATION_SHAPE_ELLIPSE
#define dt_free(ptr)
Definition mem_alloc.h:97
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)
const char * name
Definition pdf.h:90
One row of a geo query: an id with the position that matched.
float lon1
Definition geo.h:41
float lat2
Definition geo.h:44
float lat1
Definition geo.h:42
float lon2
Definition geo.h:43
int dt_tag_repository_count_attachments(const guint tagid)
How many attachment ROWS tagid has, or -1 if the count could not be read. See dt_tag_repository_count...
GList * dt_tag_repository_get_by_path_with_counts(const char *path, const char *path_prefix)
Tags at path or below it, each with the number of distinct images carrying it.
void dt_tag_count_free(gpointer data)
Release one dt_tag_count_t, name included. Suits g_list_free_full().
data.tags and main.tagged_images.
GList * dt_tag_get_images(const gint tagid)
Definition tags.c:829
gboolean dt_tag_attach(const guint tagid, const int32_t imgid, const gboolean undo_on, const gboolean group_on)
Definition tags.c:377
gboolean dt_tag_detach(const guint tagid, const int32_t imgid, const gboolean undo_on, const gboolean group_on)
Definition tags.c:485
gboolean dt_tag_new(const char *name, guint *tagid)
Definition tags.c:164
void dt_tag_rename(const guint tagid, const gchar *new_tagname)
Definition tags.c:247
gchar * dt_tag_get_name(const guint tagid)
Definition tags.c:242
guint dt_tag_remove(const guint tagid, gboolean final)
Definition tags.c:195
gboolean dt_tag_exists(const char *name, guint *tagid)
Definition tags.c:255