Hi
- What about searching geographically? Well, I just think about that
some time ago but with the map view that might be very nice. (Maybe
such could be add by lua script; see above)
I think that first we need the metadata database.
I'm not too sure what you mean in detail by searching geographically.
I have started on a search facility that I find useful. I have attached
a patch of what I have done so far. It is not complete but is sufficient
to give you an idea of what I'm trying to do.
There is an extra line on the search page. You can search for geo-coded
images within a specified distance of a geographical point.
The geographical point can be defined by:
typing in a lat/lon position
drag-and-drop a location from a www.geonames.org search onto a box on
the search line
drag-and-drop an already-geocoded thumbnail onto the search box
Is this something like the facility you are considering?
Colin Clark..
Index: src/bar_gps.c
===================================================================
--- src/bar_gps.c (revision 1779)
+++ src/bar_gps.c (working copy)
@@ -3,7 +3,7 @@
* (C) 2004 John Ellis
* Copyright (C) 2008 - 2009 The Geeqie Team
*
- * Author: Colin Clark
+ * Author: John Ellis
*
* This software is released under the GNU General Public License (GNU GPL).
* Please read the included file COPYING for more information.
@@ -25,7 +25,11 @@
#include "thumb.h"
#include "ui_menu.h"
+#include "dnd.h"
+#include "uri_utils.h"
+
#include <clutter-gtk/gtk-clutter-embed.h>
+
#include <champlain/champlain.h>
#include <champlain-gtk/champlain-gtk.h>
@@ -48,7 +52,7 @@
FileData *fd;
gchar *map_source;
gint height;
- ClutterActor *gps_view;
+ ChamplainView *gps_view;
ChamplainLayer *icon_layer;
GList *selection_list;
GPtrArray *marker_list;
@@ -59,8 +63,294 @@
gint selection_count;
gboolean centre_map_checked;
gboolean enable_markers_checked;
+ gdouble drag_latitude, drag_longitude;
};
+static void bar_pane_gps_update(PaneGPSData *pgd);
+
+/* Convert lat/lon from a signed double to a string of
+ * the format 12° 34' 56.78'' N
+ */
+GString *bar_pane_gps_convert_coordinate(gdouble coordinate, const char *key)
+{
+ gint deg, min;
+ gdouble sec;
+ GString *message;
+ gchar *ref;
+
+ if (g_strcmp0(key, "Longitude") == 0)
+ if (coordinate < 0)
+ ref = "W";
+ else
+ ref = "E";
+ else if (g_strcmp0(key, "Latitude") == 0)
+ {
+ if (coordinate < 0)
+ {
+ ref = "S";
+ }
+ else
+ ref = "N";
+ }
+
+ if (coordinate < 0)
+ coordinate = -coordinate;
+ deg = coordinate;
+ min = (coordinate * 60) - (deg * 60);
+ sec = (coordinate * 3600) - ((deg * 3600) + (min * 60));
+
+ message = g_string_new("");
+ g_string_append_printf(message, "%i° %i\' %.2lf\'\' %s", deg, min, sec, ref);
+
+ return message;
+}
+
+/*
+ *-------------------------------------------------------------------
+ * drag-and-drop
+ *-------------------------------------------------------------------
+ */
+//enum {
+ //TARGET_APP_COLLECTION_MEMBER,
+ //TARGET_APP_EXIF_ENTRY,
+ //TARGET_APP_KEYWORD_PATH,
+ //TARGET_URI_LIST,
+ //TARGET_TEXT_PLAIN
+//};
+
+static GtkTargetEntry bar_pane_gps_drop_types[] = {
+ { "text/uri-list", 0, TARGET_URI_LIST },
+ { "text/plain", 0, TARGET_TEXT_PLAIN }
+};
+static gint n_gps_entry_drop_types = 2;
+
+static void
+drag_begin_handl
+(GtkWidget *widget, GdkDragContext *context, gpointer data)
+{
+ PaneGPSData *pgd = data;
+
+ const gchar *name = gtk_widget_get_name (widget);
+ g_print ("%s: drag_begin_handl\n", name);
+
+}
+
+static void
+drag_data_get_handl(GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data,
+ guint target_type, guint time, gpointer data)
+{
+ PaneGPSData *pgd = data;
+
+ const gchar *name = gtk_widget_get_name (widget);
+ const gchar *string_data = "http://www.geonames.org/maps/google_50.117_8.683.html";
+ const glong integer_data = 42;
+ GString *message = g_string_new("http://www.geonames.org/maps/google_");
+ #define _BYTE 8
+#define _WORD 16
+#define _DWORD 32
+
+ g_print ("%s: drag_data_get_handl\n", name);
+ g_print ("%lf: drag_data_lat\n", pgd->drag_latitude);
+ g_print ("%lf: drag_data_lon\n", pgd->drag_longitude);
+ g_assert (selection_data != NULL);
+
+ g_string_append_printf(message, "%.3lf_%.3lf.html", pgd->drag_latitude, pgd->drag_longitude);
+ g_print (" Sending ");
+ //switch (target_type)
+ //{
+ /* case TARGET_SOME_OBJECT:
+ * Serialize the object and send as a string of bytes.
+ * Pixbufs, (UTF-8) text, and URIs have their own convenience
+ * setter functions */
+
+ //case TARGET_TEXT_PLAIN:
+ //g_print ("integer: %ld", integer_data);
+ //gtk_selection_data_set
+ //(
+ //selection_data, /* Allocated GdkSelectionData object */
+ //selection_data-> target,/* target type */
+ //_DWORD, /* number of bits per 'unit' */
+ //(guchar*) &integer_data,/* pointer to data to be sent */
+ //sizeof (integer_data) /* length of data in units */
+ //);
+ //break;
+
+ //case TARGET_TEXT_PLAIN:
+ g_print ("string: %s", string_data);
+ gtk_selection_data_set
+ (
+ selection_data,
+ selection_data-> target,
+ _BYTE,
+ (guchar*) message->str,
+ strlen (message->str)
+ );
+ //break;
+
+ //case TARGET_TEXT_PLAIN:
+ //g_print ("Dropped on the root window!\n");
+ //break;
+
+ //default:
+ ///* Default to some a safe target instead of fail. */
+ //g_assert_not_reached ();
+ //}
+
+ g_print (".\n");
+}
+
+static void bar_pane_gps_dnd_receive(GtkWidget *pane, GdkDragContext *context,
+ gint x, gint y,
+ GtkSelectionData *selection_data, guint info,
+ guint time, gpointer data)
+{
+ PaneGPSData *pgd = data;
+ FileData *fd, *geocoded_fd;
+ GList *work, *list;
+ gint count, geocoded_count, result;
+ GtkWidget *dialog;
+ GtkWidget *label;
+ gdouble lat, lon;
+ gdouble latitude, longitude;
+ GString *message;
+
+ pgd = g_object_get_data(G_OBJECT(pane), "pane_data");
+ if (!pgd) return;
+
+ if (g_strrstr(selection_data->data, "geonames") == NULL)
+ {
+ champlain_view_get_coords_at(CHAMPLAIN_VIEW(pgd->gps_view), x, y, &latitude, &longitude);
+
+ count = 0;
+ geocoded_count = 0;
+ list = uri_filelist_from_text((gchar *)selection_data->data, TRUE);
+ work = list;
+ while (work)
+ {
+ fd = work->data;
+ work = work->next;
+ count++;
+ lat = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLatitude", 1000);
+ lon = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLongitude", 1000);
+ if (lat != 1000 && lon != 1000)
+ {
+ geocoded_count++;
+ geocoded_fd = fd;
+ }
+ }
+
+ dialog = gtk_dialog_new_with_buttons ("Geocode image",
+ NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_OK,
+ GTK_RESPONSE_OK,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ NULL);
+
+ message = g_string_new("");
+ if (count == 1)
+ {
+ g_string_append_printf(message, "\n\nDo you want to geocode image %s?", fd->name);
+ }
+ else
+ {
+ g_string_append_printf(message, "\n\nDo you want to geocode %i images?", count);
+ }
+ if (geocoded_count == 1)
+ {
+ g_string_append_printf(message, "\n\nThis image is already geocoded!");
+ }
+ else if (geocoded_count > 1)
+ {
+ g_string_append_printf(message, "\n\n%i Images are already geocoded!", geocoded_count);
+ }
+
+ g_string_append_printf(message, "\n\nPosition: %s : %s\n",
+ bar_pane_gps_convert_coordinate(latitude, "Latitude")->str,
+ bar_pane_gps_convert_coordinate(longitude, "Longitude")->str);
+
+ label = gtk_label_new(message->str);
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
+ gtk_widget_show_all(dialog);
+ result = gtk_dialog_run(GTK_DIALOG(dialog));
+ switch (result)
+ {
+ case GTK_RESPONSE_OK:
+ work = list;
+ while (work)
+ {
+ fd = work->data;
+ work = work->next;
+ metadata_write_GPS_coord(fd, "Xmp.exif.GPSLatitude", latitude);
+ metadata_write_GPS_coord(fd, "Xmp.exif.GPSLongitude", longitude);
+ metadata_write_GPS_coord(fd, "Xmp.exif.GPSLatitude", latitude);
+ metadata_write_GPS_coord(fd, "Xmp.exif.GPSLongitude", longitude);
+ }
+ bar_pane_gps_update(pgd);
+ break;
+ default:
+ //do_nothing_since_dialog_was_cancelled ();
+ break;
+ }
+ gtk_widget_destroy (dialog);
+
+ g_list_free(list);
+
+ }
+ else
+ {
+ gchar **test1, **test2;
+ GString *test3 = g_string_new("");
+ gchar *lat_s;
+ gchar *lond_s;
+ gchar *lons_s;
+
+ test1 = g_strsplit(selection_data->data, "_", 0);
+ lat_s = g_strndup(test1[1], 7);
+ lat = g_ascii_strtod(lat_s, NULL);
+
+ test2 = g_strsplit(test1[2], ".", 0);
+ lond_s = g_strndup(test2[0], 3);
+ lons_s = g_strndup(test2[1], 3);
+ g_string_printf(test3, "%s.%s", lond_s, lons_s);
+ lon = g_ascii_strtod(test3->str, NULL);
+
+ champlain_view_center_on(CHAMPLAIN_VIEW(pgd->gps_view), lat, lon);
+
+ }
+ return;
+}
+
+static void bar_pane_gps_dnd_init(gpointer data)
+{
+ PaneGPSData *pgd = data;
+
+ gtk_drag_dest_set(pgd->widget,
+ GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP,
+ bar_pane_gps_drop_types, n_gps_entry_drop_types,
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+ g_signal_connect(G_OBJECT(pgd->widget), "drag_data_received",
+ G_CALLBACK(bar_pane_gps_dnd_receive), NULL);
+
+ gtk_drag_source_set
+ (
+ pgd->widget, /* widget will be drag-able */
+ GDK_BUTTON1_MASK, /* modifier that will start a drag */
+ bar_pane_gps_drop_types, /* lists of target to support */
+ 2, /* size of list */
+ GDK_ACTION_COPY /* what to do with data after dropped */
+ );
+
+ g_signal_connect (pgd->widget, "drag-data-get",
+ G_CALLBACK (drag_data_get_handl), pgd);
+
+ g_signal_connect (pgd->widget, "drag-begin",
+ G_CALLBACK (drag_begin_handl), pgd);
+
+
+}
+
static void bar_pane_gps_thumb_done_cb(ThumbLoader *tl, gpointer data)
{
FileData *fd;
@@ -342,16 +632,16 @@
ChamplainMapSource *map_source;
ChamplainMapSourceFactory *map_factory;
- map_factory = champlain_map_source_factory_get_default();
+ map_factory = champlain_map_source_factory_dup_default();
map_source = champlain_map_source_factory_create(map_factory, map_id);
if (map_source != NULL)
{
g_object_set(G_OBJECT(pgd->gps_view), "map-source", map_source, NULL);
- g_object_unref(map_factory);
}
g_object_unref(map_source);
+ g_object_unref(map_factory);
}
void bar_pane_gps_enable_markers_checked_toggle_cb(GtkWidget *menu_widget, gpointer data)
@@ -384,15 +674,17 @@
static void bar_pane_gps_change_map_cb(GtkWidget *widget, gpointer data)
{
- PaneGPSData *pgd = data;
+ PaneGPSData *pgd;
gchar *mapsource;
if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
return;
+ pgd = (PaneGPSData *) submenu_item_get_data(widget);
+
if (!pgd) return;
- mapsource = g_object_get_data(G_OBJECT(widget), "menu_item_radio_data");
+ mapsource = data;
bar_pane_gps_set_map_source(pgd, mapsource);
}
@@ -552,28 +844,51 @@
return map_id;
}
+static GtkWidget *bar_pane_gps_add_radio(GtkWidget *menu, GtkWidget *parent,
+ const gchar *label, GCallback func, gchar *value,
+ gboolean show_current, const gchar *current_value)
+{
+ GtkWidget *item;
+
+ if (show_current)
+ {
+ item = menu_item_add_radio(menu, label,
+ parent, (g_strcmp0(value, current_value) == 0), func, value);
+ }
+ else
+ {
+ item = menu_item_add(menu, label, func, value);
+ }
+
+ return item;
+}
+
static GtkWidget *bar_pane_gps_menu(PaneGPSData *pgd)
{
GtkWidget *menu;
GtkWidget *map_centre;
+ static gboolean show_current = TRUE;
GtkWidget *parent;
ChamplainMapSourceFactory *map_factory;
GSList *map_list;
- ChamplainMapSourceDesc *map_desc;
- const gchar *current;
+ ChamplainMapSourceDesc *map_desc;
menu = popup_menu_short_lived();
- map_factory = champlain_map_source_factory_get_default();
- map_list = champlain_map_source_factory_get_list(map_factory);
- current = bar_pane_gps_get_map_id(pgd);
+ map_factory = champlain_map_source_factory_dup_default();
+ map_list = champlain_map_source_factory_dup_list(map_factory);
+ map_desc = CHAMPLAIN_MAP_SOURCE_DESC(map_list->data);
+ map_list = g_slist_next(map_list);
+ g_object_set_data(G_OBJECT(menu), "submenu_data", pgd);
+ parent = bar_pane_gps_add_radio(menu, NULL, (map_desc->name), G_CALLBACK(bar_pane_gps_change_map_cb), map_desc->id, show_current, bar_pane_gps_get_map_id(pgd));
+
while (map_list)
{
- map_desc = (ChamplainMapSourceDesc *)(map_list->data);
-
- menu_item_add_radio(menu, map_desc->name, map_desc->id, strcmp(map_desc->id, current) == 0, G_CALLBACK(bar_pane_gps_change_map_cb), pgd);
-
+ map_desc = CHAMPLAIN_MAP_SOURCE_DESC(map_list->data);
+ bar_pane_gps_add_radio(menu, parent, (map_desc->name), G_CALLBACK(bar_pane_gps_change_map_cb), map_desc->id,
+ show_current, bar_pane_gps_get_map_id(pgd));
+
map_list = g_slist_next(map_list);
}
@@ -594,53 +909,51 @@
return menu;
}
-/* Determine if the map is to be re-centred on the marker when another photo is selected
- */
-void bar_pane_gps_map_centreing(PaneGPSData *pgd)
+static gboolean bar_pane_gps_vbox_keypress_cb(GtkWidget *widget, ClutterButtonEvent *bevent, gpointer data)
{
- GtkWidget *dialog;
- GString *message = g_string_new("");
+ PaneGPSData *pgd = data;
+ GtkWidget *menu;
+ gdouble latitude, longitude;
- if (pgd->centre_map_checked)
+ champlain_view_get_coords_from_event(CHAMPLAIN_VIEW(pgd->gps_view), (ClutterEvent *)bevent, &pgd->drag_latitude, &pgd->drag_longitude);
+ g_print ("%lf: keypress_data_lat\n", pgd->drag_latitude);
+ g_print ("%lf: keypress_data_lon\n", pgd->drag_longitude);
+
+ g_print ("keypress_vbox\n");
+
+ if (bevent->button == MOUSE_BUTTON_RIGHT)
{
- message = g_string_append(message, _("Move map centre to marker\n is disabled"));
- pgd->centre_map_checked = FALSE;
+ menu = bar_pane_gps_menu(pgd);
+ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, bevent->button, bevent->time);
+ return TRUE;
}
+ else if (bevent->button == MOUSE_BUTTON_LEFT)
+ {
+ return TRUE;
+ }
else
{
- message = g_string_append(message, _("Move map centre to marker\n is enabled"));
- pgd->centre_map_checked = TRUE;
+ return TRUE;
}
-
- dialog = gtk_message_dialog_new(NULL,
- GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_MESSAGE_INFO,
- GTK_BUTTONS_CLOSE,
- "%s", message->str);
- gtk_window_set_title(GTK_WINDOW(dialog), _("Map Centreing"));
- gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
- gtk_dialog_run(GTK_DIALOG(dialog));
-
- gtk_widget_destroy(dialog);
- g_string_free(message, TRUE);
}
-static gboolean bar_pane_gps_map_keypress_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
+static gboolean bar_pane_gps_map_keypress_cb(GtkWidget *widget, ClutterButtonEvent *bevent, gpointer data)
{
PaneGPSData *pgd = data;
GtkWidget *menu;
+ gdouble latitude, longitude;
+ champlain_view_get_coords_from_event(CHAMPLAIN_VIEW(pgd->gps_view), (ClutterEvent *)bevent, &pgd->drag_latitude, &pgd->drag_longitude);
+ g_print ("%lf: keypress_data_lat\n", pgd->drag_latitude);
+ g_print ("%lf: keypress_data_lon\n", pgd->drag_longitude);
+ g_print ("keypress_map\n");
+
if (bevent->button == MOUSE_BUTTON_RIGHT)
{
menu = bar_pane_gps_menu(pgd);
gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, bevent->button, bevent->time);
return TRUE;
}
- else if (bevent->button == MOUSE_BUTTON_MIDDLE)
- {
- bar_pane_gps_map_centreing(pgd);
- return TRUE;
- }
else if (bevent->button == MOUSE_BUTTON_LEFT)
{
return FALSE;
@@ -660,7 +973,6 @@
file_data_unref(pgd->fd);
g_free(pgd->map_source);
g_free(pgd->pane.id);
- clutter_actor_destroy(pgd->gps_view);
g_free(pgd);
}
@@ -674,7 +986,7 @@
GtkWidget *gpswidget, *viewport;
GtkWidget *status, *state, *progress, *slider;
ChamplainLayer *layer;
- ClutterActor *view;
+ ChamplainView *view;
const gchar *slider_list[] = {GTK_STOCK_ZOOM_IN, GTK_STOCK_ZOOM_OUT, NULL};
const gchar **slider_icons = slider_list;
@@ -693,15 +1005,11 @@
scrolled = gtk_scrolled_window_new(NULL, NULL);
vbox = gtk_vbox_new(FALSE, 0);
-#ifdef GTK_CHAMPLAIN_EMBED
gpswidget = gtk_champlain_embed_new ();
view = gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (gpswidget));
-#else
- view = champlain_view_new();
- gpswidget = champlain_view_embed_new(CHAMPLAIN_VIEW(view));
-#endif
+
viewport = gtk_viewport_new(NULL, NULL);
-
+
gtk_container_add(GTK_CONTAINER(viewport), gpswidget);
gtk_box_pack_start(GTK_BOX(vbox),viewport, TRUE, TRUE, 0);
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled), vbox);
@@ -721,7 +1029,7 @@
gtk_box_pack_end(GTK_BOX(vbox),GTK_WIDGET(status), FALSE, FALSE, 0);
layer = champlain_layer_new();
- champlain_view_add_layer(CHAMPLAIN_VIEW(view), layer);
+ champlain_view_add_layer(view, layer);
pgd->icon_layer = layer;
pgd->gps_view = view;
@@ -752,11 +1060,16 @@
gtk_widget_set_size_request(pgd->widget, -1, height);
clutter_set_motion_events_enabled(TRUE);
- g_signal_connect(G_OBJECT(vbox), "button_press_event", G_CALLBACK(bar_pane_gps_map_keypress_cb), pgd);
+ g_signal_connect(G_OBJECT(vbox), "button_press_event", G_CALLBACK(bar_pane_gps_vbox_keypress_cb), pgd);
+ clutter_actor_set_reactive(pgd->gps_view, TRUE);
+ g_signal_connect(pgd->gps_view, "button_press_event", G_CALLBACK(bar_pane_gps_map_keypress_cb), pgd);
g_signal_connect(pgd->gps_view, "notify::state", G_CALLBACK(bar_pane_gps_view_state_changed_cb), pgd);
g_signal_connect(pgd->gps_view, "notify::zoom-level", G_CALLBACK(bar_pane_gps_view_state_changed_cb), pgd);
g_signal_connect(G_OBJECT(slider), "value-changed", G_CALLBACK(bar_pane_gps_slider_changed_cb), pgd);
+
+ bar_pane_gps_dnd_init(pgd);
+
file_data_register_notify_func(bar_pane_gps_notify_cb, pgd, NOTIFY_PRIORITY_LOW);
pgd->create_markers_id = 0;
Index: src/metadata.c
===================================================================
--- src/metadata.c (revision 1779)
+++ src/metadata.c (working copy)
@@ -562,6 +562,46 @@
return ret;
}
+gboolean metadata_write_GPS_coord(FileData *fd, const gchar *key, gdouble value)
+{
+ gint deg;
+ gdouble min;
+ gdouble param;
+ char *coordinate;
+ char *ref;
+ gboolean ok = TRUE;
+
+ param = value;
+ if (param < 0)
+ param = -param;
+ deg = param;
+ min = (param * 60) - (deg * 60);
+ if (g_strcmp0(key, "Xmp.exif.GPSLongitude") == 0)
+ if (value < 0)
+ ref = "W";
+ else
+ ref = "E";
+ else if (g_strcmp0(key, "Xmp.exif.GPSLatitude") == 0)
+ if (value < 0)
+ ref = "S";
+ else
+ ref = "N";
+ else
+ {
+ log_printf("unknown GPS parameter key '%s'\n", key);
+ ok = FALSE;
+ }
+
+ if (ok)
+ {
+ coordinate = g_strdup_printf("%i,%lf,%s", deg, min, ref);
+ metadata_write_string(fd, key, coordinate );
+ }
+
+ g_free(coordinate);
+ return ok;
+}
+
gdouble metadata_read_GPS_coord(FileData *fd, const gchar *key, gdouble fallback)
{
gdouble coord;
Index: src/metadata.h
===================================================================
--- src/metadata.h (revision 1779)
+++ src/metadata.h (working copy)
@@ -34,6 +34,7 @@
gchar *metadata_read_string(FileData *fd, const gchar *key, MetadataFormat format);
guint64 metadata_read_int(FileData *fd, const gchar *key, guint64 fallback);
gdouble metadata_read_GPS_coord(FileData *fd, const gchar *key, gdouble fallback);
+gboolean metadata_write_GPS_coord(FileData *fd, const gchar *key, gdouble value);
gboolean metadata_append_string(FileData *fd, const gchar *key, const char *value);
gboolean metadata_append_list(FileData *fd, const gchar *key, const GList *values);
Index: src/search.c
===================================================================
--- src/search.c (revision 1779)
+++ src/search.c (working copy)
@@ -43,6 +43,7 @@
#include <gdk/gdkkeysyms.h> /* for keyboard values */
+#include <math.h>
#define DEF_SEARCH_WIDTH 700
#define DEF_SEARCH_HEIGHT 450
@@ -61,7 +62,8 @@
SEARCH_MATCH_OVER,
SEARCH_MATCH_BETWEEN,
SEARCH_MATCH_ALL,
- SEARCH_MATCH_ANY
+ SEARCH_MATCH_ANY,
+ SEARCH_MATCH_NOT_GEOCODED
} MatchType;
enum {
@@ -161,6 +163,7 @@
MatchType match_dimensions;
MatchType match_keywords;
MatchType match_comment;
+ MatchType match_gps;
gboolean match_name_enable;
gboolean match_size_enable;
@@ -191,6 +194,19 @@
gboolean thumb_enable;
FileData *thumb_fd;
+ /* Used for lat/lon coordinate search
+ */
+ gint search_gps;
+ gdouble search_lat, search_lon;
+ GtkWidget *label_gps_drop, *entry_gps_coord;
+ GtkWidget *check_gps;
+ GtkWidget *type_gps;
+ GtkWidget *spin_gps;
+ GtkWidget *units_gps;
+ gboolean match_gps_enable;
+
+ gboolean match_geocode_enable;
+
/* file list for edit menu */
GList *editmenu_fd_list;
@@ -248,6 +264,12 @@
{ N_("miss"), SEARCH_MATCH_NONE }
};
+static const MatchList text_search_menu_gps[] = {
+ { N_("less than"), SEARCH_MATCH_UNDER },
+ { N_("greater than"), SEARCH_MATCH_OVER },
+ { N_("not geocoded"), SEARCH_MATCH_NOT_GEOCODED }
+};
+
static GList *search_window_list = NULL;
@@ -1431,6 +1453,107 @@
}
}
+/* Convert lat/lon from a signed double to a string of
+ * the format 12° 34' 56.78" N
+ */
+GString *search_gps_convert_coordinate(gdouble coordinate, const char *key)
+{
+ gint deg, min;
+ gdouble sec;
+ GString *message;
+ gchar *ref;
+
+ if (g_strcmp0(key, "Longitude") == 0)
+ if (coordinate < 0)
+ ref = "W";
+ else
+ ref = "E";
+ else if (g_strcmp0(key, "Latitude") == 0)
+ {
+ if (coordinate < 0)
+ {
+ ref = "S";
+ }
+ else
+ ref = "N";
+ }
+
+ if (coordinate < 0)
+ coordinate = -coordinate;
+ deg = coordinate;
+ min = (coordinate * 60) - (deg * 60);
+ sec = (coordinate * 3600) - ((deg * 3600) + (min * 60));
+
+ message = g_string_new("");
+ g_string_append_printf(message, "%i° %i\' %.2lf\" %s", deg, min, sec, ref);
+
+ return message;
+}
+
+/* Get the coordinates from the result of a drag-and-drop, and copy it into the
+ * user editable box. It will either be a string from a geonames.org search,
+ * or a file list from a drag from the geeqie layout window.
+ */
+GString *search_gps_extract_coordinates(const gchar *entry_string, gpointer data)
+{
+ gchar **latlon;
+ GString *message = g_string_new("");
+ gchar *str_ptr;
+ gdouble latitude, longitude;
+ GList *list;
+ FileData *fd;
+ SearchData *sd = data;
+
+ str_ptr = g_strstr_len(entry_string, -1, "http://www.geonames.org/maps/google_");
+ if (str_ptr != NULL)
+ {
+ latlon = g_strsplit(g_strndup(str_ptr + 36, g_strrstr(str_ptr + 36, ".html") - str_ptr + 36), "_", 0);
+ latitude = g_ascii_strtod(latlon[0], NULL);
+ g_string_append(message, search_gps_convert_coordinate(latitude, "Latitude")->str);
+ g_string_append(message, " ");
+
+ longitude = g_ascii_strtod(latlon[1], NULL);
+ g_string_append(message, search_gps_convert_coordinate(longitude, "Longitude")->str);
+
+ gtk_button_set_label(GTK_BUTTON(sd->label_gps_drop), "geonames.org");
+
+ g_strfreev(latlon);
+ }
+ else
+ {
+ list = uri_filelist_from_text((gchar *)entry_string, TRUE);
+ /* If more than one file, use only the first file in a list.
+ */
+ if (list != NULL)
+ {
+ fd = list->data;
+ latitude = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLatitude", 1000);
+ longitude = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLongitude", 1000);
+ if (latitude != 1000 && longitude != 1000)
+ {
+ g_string_append(message, search_gps_convert_coordinate(latitude, "Latitude")->str);
+ g_string_append(message, " ");
+ g_string_append(message, search_gps_convert_coordinate(longitude, "Longitude")->str);
+ }
+
+ gtk_button_set_label(GTK_BUTTON(sd->label_gps_drop), fd->name);
+ }
+ }
+
+ return message;
+
+}
+
+static void search_gps_dnd_received_cb(GtkWidget *pane, GdkDragContext *context,
+ gint x, gint y,
+ GtkSelectionData *selection_data, guint info,
+ guint time, gpointer data)
+{
+ SearchData *sd = data;
+
+ gtk_entry_set_text(GTK_ENTRY(sd->entry_gps_coord), search_gps_extract_coordinates((gchar *)selection_data->data, sd)->str);
+}
+
static void search_dnd_init(SearchData *sd)
{
gtk_drag_source_set(sd->result_view, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
@@ -1445,6 +1568,14 @@
G_CALLBACK(search_dnd_end), sd);
#endif
+ gtk_drag_dest_set(GTK_WIDGET(sd->label_gps_drop),
+ GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP,
+ result_drag_types, n_result_drag_types,
+ GDK_ACTION_COPY);
+
+ g_signal_connect(G_OBJECT(sd->label_gps_drop), "drag_data_received",
+ G_CALLBACK(search_gps_dnd_received_cb), sd);
+
}
/*
@@ -1913,6 +2044,87 @@
}
}
+ /* Check if the image is geocoded
+ */
+ gdouble latitude, longitude, range, conversion;
+
+ if (match && sd->match_geocode_enable)
+ {
+ tested = TRUE;
+ match = FALSE;
+
+ latitude = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLatitude", 1000);
+ longitude = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLongitude", 1000);
+ if (latitude == 1000 && longitude == 1000)
+ {
+ match = TRUE;
+ }
+ }
+
+ /* Calculate the distance the image is from the specified origin.
+ * This is a standard algorithm. A simplified one may be faster.
+ */
+ #define RADIANS 0.0174532925
+ #define KM_EARTH_RADIUS 6371
+ #define MILES_EARTH_RADIUS 3959
+
+ //gdouble latitude, longitude, range, conversion;
+
+ if (g_strcmp0(gtk_combo_box_get_active_text(GTK_COMBO_BOX(sd->units_gps)), _("miles")) != 0)
+ {
+ conversion = KM_EARTH_RADIUS;
+ }
+ else
+ {
+ conversion = MILES_EARTH_RADIUS;
+ }
+
+ if (match && (sd->match_gps == SEARCH_MATCH_NOT_GEOCODED))
+ {
+ tested = TRUE;
+ match = FALSE;
+
+ latitude = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLatitude", 1000);
+ longitude = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLongitude", 1000);
+ if (latitude == 1000 && longitude == 1000)
+ {
+ match = TRUE;
+ }
+ }
+ else if (match && (sd->match_gps == SEARCH_MATCH_UNDER) && sd->search_lat && sd->search_lon)
+ {
+ tested = TRUE;
+ match = FALSE;
+
+ latitude = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLatitude", 1000);
+ longitude = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLongitude", 1000);
+ if (latitude != 1000 && longitude != 1000)
+ {
+ range = conversion * acos ( sin(latitude * RADIANS) * sin(sd->search_lat * RADIANS) +
+ cos(latitude * RADIANS) * cos(sd->search_lat * RADIANS) * cos((sd->search_lon - longitude) * RADIANS));
+
+ if (sd->search_gps >= range)
+ match = TRUE;
+ }
+ }
+ else if (match && sd->match_gps == SEARCH_MATCH_OVER && sd->search_lat && sd->search_lon)
+ {
+ tested = TRUE;
+ match = FALSE;
+
+ latitude = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLatitude", 1000);
+ longitude = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLongitude", 1000);
+ if (latitude != 1000 && longitude != 1000)
+ {
+ range = conversion * acos ( sin(latitude * RADIANS) * sin(sd->search_lat * RADIANS) +
+ cos(latitude * RADIANS) * cos(sd->search_lat * RADIANS) * cos((sd->search_lon - longitude) * RADIANS));
+
+ if (sd->search_gps < range)
+ match = TRUE;
+ }
+ }
+
+
if ((match || extra_only) &&
(sd->match_dimensions_enable || sd->match_similarity_enable))
{
@@ -2146,6 +2358,11 @@
GtkTreeViewColumn *column;
gchar *path;
+ gdouble deg_lat, min_lat, sec_lat, deg_lon, min_lon, sec_lon, latitude, longitude;
+ gchar north_south[35];
+ gchar east_west[35];
+ gchar *entry_text;
+
if (sd->search_folder_list)
{
search_stop(sd);
@@ -2175,6 +2392,54 @@
tab_completion_append_to_history(sd->entry_similarity, sd->search_similarity_path);
}
+ /* Check the coordinate entry. Allow the user plenty of latitude in the input format
+ * e.g. 12 deg 34 min 56 sec N. If the result is not sensible, it should get blocked.
+ */
+ if (sd->match_gps == SEARCH_MATCH_UNDER || sd->match_gps == SEARCH_MATCH_OVER)
+ {
+ entry_text = g_strdup(gtk_entry_get_text(GTK_ENTRY(sd->entry_gps_coord)));
+ g_strstrip(entry_text);
+ if (entry_text != NULL && strlen(entry_text) <= 37)
+ {
+ g_strcanon(entry_text, "NSEW0123456789.", ' ');
+ sscanf(entry_text,"%lf %lf %lf %s %lf %lf %lf %s",
+ °_lat, &min_lat, &sec_lat, north_south,
+ °_lon, &min_lon, &sec_lon, east_west);
+
+ latitude = deg_lat + min_lat / 60 + sec_lat / 3600;
+ longitude = deg_lon + min_lon / 60 + sec_lon / 3600;
+
+ if ((g_strcmp0(north_south, "N") == 0 || g_strcmp0(north_south, "S") == 0) &&
+ (g_strcmp0(east_west, "E") == 0 || g_strcmp0(east_west, "W") == 0) &&
+ (latitude <= 90.0) && (latitude >= -90.0) &&
+ (longitude <= 180.0) && (longitude >= -180.0))
+ {
+ if (g_strcmp0(north_south, "S") == 0)
+ latitude = -latitude;
+ if (g_strcmp0(east_west, "W") == 0)
+ longitude = -longitude;
+ sd->search_lat = latitude;
+ sd->search_lon = longitude;
+ }
+ else
+ {
+ file_util_warning_dialog(_("Entry does not contain a valid lat/lon value"),
+ _("Please enter a coordinate in the form:\n12° 34' 56.78\" N 12° 34' 56.78\" W\n or\n 12 34 56.78 N 12 34 56.78 W\n or drag-and-drop on the adjoining box"),
+ GTK_STOCK_DIALOG_WARNING, sd->window);
+ return;
+ }
+ }
+ else
+ {
+ file_util_warning_dialog(_("Entry does not contain a valid lat/lon value"),
+ _("Please enter a coordinate in the form:\n12° 34' 56.78\" N 12° 34' 56.78\" W\n or\n 12 34 56.78 N 12 34 56.78 W\\n or drag-and-drop on the adjoining box"),
+ GTK_STOCK_DIALOG_WARNING, sd->window);
+ return;
+ }
+ g_free(entry_text);
+ }
+
+
string_list_free(sd->search_keyword_list);
sd->search_keyword_list = keyword_list_pull(sd->entry_keywords);
@@ -2448,6 +2713,16 @@
*value = (gint)gtk_adjustment_get_value(adjustment);
}
+static void menu_choice_gps_cb(GtkWidget *combo, gpointer data)
+{
+ SearchData *sd = data;
+
+ if (!menu_choice_get_match_type(combo, &sd->match_gps)) return;
+
+ menu_choice_set_visible(gtk_widget_get_parent(sd->spin_gps),
+ (sd->match_gps != SEARCH_MATCH_NOT_GEOCODED));
+}
+
static GtkWidget *menu_spin(GtkWidget *box, gdouble min, gdouble max, gint value,
GCallback func, gpointer data)
{
@@ -2788,6 +3063,55 @@
pref_checkbox_new_int(hbox, _("Match case"),
sd->search_comment_match_case, &sd->search_comment_match_case);
+ /* Search for geocoded images that are within a specified range of a lat/lon coordinate
+ */
+ hbox = menu_choice(sd->box_search, &sd->check_gps, NULL,
+ _("Image is"), &sd->match_gps_enable,
+ text_search_menu_gps, sizeof(text_search_menu_gps) / sizeof(MatchList),
+ G_CALLBACK(menu_choice_gps_cb), sd);
+ //sd->type_gps = gtk_combo_box_new_text();
+ //gtk_combo_box_append_text(GTK_COMBO_BOX(sd->type_gps), _("within"));
+ //gtk_combo_box_append_text(GTK_COMBO_BOX(sd->type_gps), _("not within"));
+ //gtk_combo_box_append_text(GTK_COMBO_BOX(sd->type_gps), _("not geocoded"));
+ //gtk_box_pack_start(GTK_BOX(hbox), sd->type_gps, FALSE, FALSE, 0);
+ //gtk_combo_box_set_active(GTK_COMBO_BOX(sd->type_gps), 0);
+ //gtk_widget_show(sd->type_gps);
+ hbox2 = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
+ gtk_box_pack_start(GTK_BOX(hbox), hbox2, FALSE, FALSE, 0);
+
+ sd->spin_gps = menu_spin(hbox2, 1, 9999, sd->search_gps,
+ G_CALLBACK(menu_choice_spin_cb), &sd->search_gps);
+ gtk_drag_dest_unset(sd->spin_gps);
+
+ sd->units_gps = gtk_combo_box_new_text();
+ gtk_combo_box_append_text(GTK_COMBO_BOX(sd->units_gps), _("km"));
+ gtk_combo_box_append_text(GTK_COMBO_BOX(sd->units_gps), _("miles"));
+ gtk_box_pack_start(GTK_BOX(hbox2), sd->units_gps, FALSE, FALSE, 0);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(sd->units_gps), 0);
+ gtk_widget_show(sd->units_gps);
+ gtk_widget_show(hbox2);
+
+ pref_label_new(hbox2, _("from"));
+
+ sd->entry_gps_coord = gtk_entry_new();
+ gtk_editable_set_editable(GTK_EDITABLE(sd->entry_gps_coord), TRUE);
+ gtk_widget_set_has_tooltip(sd->entry_gps_coord, TRUE);
+ gtk_widget_set_tooltip_text(sd->entry_gps_coord, _("Enter a coordinate in the form:\n 12° 34' 56.78\" N 12° 34' 56.78\" W\n or\n 12 34 56.78 N 12 34 56.78 W\n or drag-and-drop on the adjoining box."));
+ gtk_box_pack_start(GTK_BOX(hbox2), sd->entry_gps_coord, TRUE, TRUE, 0);
+ gtk_widget_set_sensitive(sd->entry_gps_coord, TRUE);
+ gtk_drag_dest_unset(sd->entry_gps_coord);
+
+ sd->label_gps_drop = gtk_link_button_new_with_label("http://www.geonames.org", _("Search point"));
+ gtk_widget_set_has_tooltip(sd->label_gps_drop, TRUE);
+ gtk_widget_set_tooltip_text(sd->label_gps_drop, _("Drag a placename from a www.geonames.org search, \n or drag a geocoded thumbnail onto this box.\n\n Click to start a www.geonames.org search."));
+ gtk_box_pack_start(GTK_BOX(hbox2), sd->label_gps_drop, TRUE, TRUE, 0);
+ gtk_widget_set_sensitive(sd->label_gps_drop, FALSE);
+
+ g_signal_connect(G_OBJECT(sd->check_gps), "toggled",
+ G_CALLBACK(menu_choice_check_cb), sd->label_gps_drop);
+ gtk_widget_show(sd->label_gps_drop);
+ gtk_widget_show(sd->entry_gps_coord);
+
/* Done the types of searches */
scrolled = gtk_scrolled_window_new(NULL, NULL);
------------------------------------------------------------------------------
_______________________________________________
Geeqie-devel mailing list
Geeqie-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/geeqie-devel