Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
location_repository.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2026 Aurélien PIERRE.
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
20
21#include "database/database.h"
22#include "database/sql_debug.h"
23#include "system/macros.h"
24#include "system/mem_alloc.h"
25
26#include <sqlite3.h>
27
29{
31 // clang-format off
33 "DELETE FROM data.locations WHERE tagid=?1", -1, &stmt, NULL);
34 // clang-format on
38}
39
41{
43 /* Columns named rather than `SELECT *`. The old query read columns 0..6 positionally,
44 * which is only correct while the schema keeps `ratio` seventh -- and `ratio` was added
45 * by an ALTER TABLE, so its position is a migration artefact rather than a decision. */
46 // clang-format off
48 "SELECT tagid, type, longitude, latitude, delta1, delta2, ratio"
49 " FROM data.locations AS t"
50 " WHERE latitude IS NOT NULL"
51 " AND (latitude + delta2) > ?2"
52 " AND (latitude - delta2) < ?1"
53 " AND (longitude + delta1) > ?3"
54 " AND (longitude - delta1) < ?4",
55 -1, &stmt, NULL);
56 // clang-format on
61
62 GList *locs = NULL;
64 {
66 if(t)
67 {
68 t->id = sqlite3_column_int(stmt, 0);
69 t->data.shape = sqlite3_column_int(stmt, 1);
70 t->data.lon = sqlite3_column_double(stmt, 2);
71 t->data.lat = sqlite3_column_double(stmt, 3);
72 t->data.delta1 = sqlite3_column_double(stmt, 4);
73 t->data.delta2 = sqlite3_column_double(stmt, 5);
74 t->data.ratio = sqlite3_column_double(stmt, 6);
76 }
77 }
79
80 return locs; // not reversed: the caller has never depended on the order
81}
82
84{
86 // clang-format off
88 "SELECT type, longitude, latitude, delta1, delta2, ratio"
89 " FROM data.locations"
90 " JOIN data.tags ON id = tagid"
91 " WHERE tagid = ?1 AND longitude IS NOT NULL"
92 " AND SUBSTR(name, 1, LENGTH(?2)) = ?2",
93 -1, &stmt, NULL);
94 // clang-format on
97
100 {
103 g->lon = sqlite3_column_double(stmt, 1);
104 g->lat = sqlite3_column_double(stmt, 2);
105 g->delta1 = sqlite3_column_double(stmt, 3);
106 g->delta2 = sqlite3_column_double(stmt, 4);
107 g->ratio = sqlite3_column_double(stmt, 5);
108 }
110 return g;
111}
112
114{
116 // clang-format off
118 "INSERT OR REPLACE INTO data.locations"
119 " (tagid, type, longitude, latitude, delta1, delta2, ratio, polygons)"
120 " VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
121 -1, &stmt, NULL);
122 // clang-format on
124 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, g->shape);
130 if(g->shape != MAP_LOCATION_SHAPE_POLYGONS)
131 {
133 }
134 else
135 {
136 DT_DEBUG_SQLITE3_BIND_BLOB(stmt, 8, g->polygons->data,
137 g->plg_pts * (int)sizeof(dt_geo_map_display_point_t), SQLITE_STATIC);
138 }
141}
142
143gboolean dt_location_repository_get_polygon(const guint locid, void **blob, gint *bytes)
144{
145 if(IS_NULL_PTR(blob) || IS_NULL_PTR(bytes)) return FALSE;
146 *blob = NULL;
147 *bytes = 0;
148
150 // clang-format off
152 "SELECT polygons FROM data.locations WHERE tagid = ?1",
153 -1, &stmt, NULL);
154 // clang-format on
156
157 gboolean found = FALSE;
159 {
160 const int n = sqlite3_column_bytes(stmt, 0);
161 const void *src = sqlite3_column_blob(stmt, 0);
162 if(n > 0 && src)
163 {
164 /* Copied out: sqlite owns the column memory only until the statement is stepped or
165 * finalised, and the caller keeps this well past both.
166 *
167 * g_malloc, not malloc: the caller releases it with dt_free(), which is g_free().
168 * The original paired malloc() with dt_free() -- harmless on glibc, where g_free is
169 * free, and undefined the moment GLib is built with a custom allocator. */
170 *blob = g_malloc(n);
171 if(*blob)
172 {
173 memcpy(*blob, src, n);
174 *bytes = n;
175 found = TRUE;
176 }
177 }
178 }
180 return found;
181}
182
184{
186 // clang-format off
188 "SELECT l.tagid, l.type, i.longitude, i.latitude FROM main.images AS i"
189 " JOIN data.locations AS l"
190 " ON (l.type = ?2"
191 " AND ((((i.longitude-l.longitude)*(i.longitude-l.longitude))/"
192 "(delta1*delta1) +"
193 " ((i.latitude-l.latitude)*(i.latitude-l.latitude))/"
194 "(delta2*delta2)) <= 1)"
195 " OR ((l.type = ?3 OR l.type = ?4)"
196 " AND i.longitude>=(l.longitude-delta1)"
197 " AND i.longitude<=(l.longitude+delta1)"
198 " AND i.latitude>=(l.latitude-delta2)"
199 " AND i.latitude<=(l.latitude+delta2)))"
200 " WHERE i.id = ?1 "
201 " AND i.latitude IS NOT NULL AND i.longitude IS NOT NULL",
202 -1, &stmt, NULL);
203 // clang-format on
208
210 while(sqlite3_step(stmt) == SQLITE_ROW)
211 {
213 if(c)
214 {
215 c->id = sqlite3_column_int(stmt, 0);
216 c->shape = sqlite3_column_int(stmt, 1);
217 c->lon = sqlite3_column_double(stmt, 2);
218 c->lat = sqlite3_column_double(stmt, 3);
220 }
221 }
223
225}
226
228{
230
231 if(shape == MAP_LOCATION_SHAPE_ELLIPSE)
232 {
233 // clang-format off
235 "SELECT i.id, i.longitude, i.latitude FROM main.images AS i"
236 " JOIN data.locations AS l"
237 " ON (l.type = ?2"
238 " AND ((((i.longitude-l.longitude)*(i.longitude-l.longitude))/"
239 "(delta1*delta1) +"
240 " ((i.latitude-l.latitude)*(i.latitude-l.latitude))/"
241 "(delta2*delta2)) <= 1))"
242 " WHERE l.tagid = ?1 ",
243 -1, &stmt, NULL);
244 // clang-format on
245 }
246 else if(shape == MAP_LOCATION_SHAPE_RECTANGLE)
247 {
248 // clang-format off
250 "SELECT i.id, i.longitude, i.latitude FROM main.images AS i"
251 " JOIN data.locations AS l"
252 " ON (l.type = ?2"
253 " AND i.longitude>=(l.longitude-delta1)"
254 " AND i.longitude<=(l.longitude+delta1)"
255 " AND i.latitude>=(l.latitude-delta2)"
256 " AND i.latitude<=(l.latitude+delta2))"
257 " WHERE l.tagid = ?1 ",
258 -1, &stmt, NULL);
259 // clang-format on
260 }
261 else // MAP_LOCATION_SHAPE_POLYGONS
262 {
263 // clang-format off
265 "SELECT i.id, i.longitude, i.latitude FROM main.images AS i"
266 " JOIN data.locations AS l"
267 " ON (l.type = ?2"
268 " AND i.longitude>=(l.longitude-delta1)"
269 " AND i.longitude<=(l.longitude+delta1)"
270 " AND i.latitude>=(l.latitude-delta2)"
271 " AND i.latitude<=(l.latitude+delta2))"
272 " WHERE l.tagid = ?1 ",
273 -1, &stmt, NULL);
274 // clang-format on
275 }
276
279
281 while(sqlite3_step(stmt) == SQLITE_ROW)
282 {
284 if(c)
285 {
286 c->id = sqlite3_column_int(stmt, 0);
287 c->shape = shape;
288 c->lon = sqlite3_column_double(stmt, 1);
289 c->lat = sqlite3_column_double(stmt, 2);
291 }
292 }
294
295 // Row order, NOT reverse-row: _map_location_find_images() filters this list and prepends into
296 // its own, and that second prepend is what reproduces the original's single prepend off the
297 // cursor. Returning reverse-row here would flip the imgid list.
299}
300
302{
304 // clang-format off
306 "SELECT t.id FROM main.tagged_images ti"
307 " JOIN data.tags AS t ON t.id = ti.tagid"
308 " JOIN data.locations AS l ON l.tagid = t.id"
309 " WHERE imgid = ?1",
310 -1, &stmt, NULL);
311 // clang-format on
313
314 GList *ids = NULL;
315 while(sqlite3_step(stmt) == SQLITE_ROW)
318
319 return ids; // not reversed: matches the previous prepend-only order
320}
321
322// clang-format off
323// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
324// vim: shiftwidth=2 expandtab tabstop=2 cindent
325// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
326// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
const int t
sqlite3 * dt_database_get_sqlite3_global(void)
Definition database.c:3753
dt_map_box_t bbox
Definition location.c:4
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
Checked wrappers around the sqlite3_* calls: trace the statement, assert the return code,...
#define DT_DEBUG_SQLITE3_BIND_BLOB(a, b, c, d, e)
Definition sql_debug.h:149
#define DT_DEBUG_SQLITE3_PREPARE_V2(a, b, c, d, e)
Definition sql_debug.h:137
#define DT_DEBUG_SQLITE3_BIND_TEXT(a, b, c, d, e)
Definition sql_debug.h:148
#define DT_DEBUG_SQLITE3_BIND_INT(a, b, c)
Definition sql_debug.h:145
#define DT_DEBUG_SQLITE3_BIND_DOUBLE(a, b, c)
Definition sql_debug.h:147
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