xmpedit

GTK+ editor for XMP metadata embedded in images
git clone https://code.djc.id.au/git/xmpedit/
commit eb3a46863d572b7813956194873dd70a028131e3
parent 4ff0033d1aa233fce75c7e22c3a4ded135d94c8a
Author: Dan Callaghan <djc@djc.id.au>
Date:   Sun, 21 Nov 2010 10:54:17 +1000

initial location support, with (useless) map view

Diffstat:
MREADME.markdown | 1+
Mbuild | 4++--
Msrc/ImageMetadata.vala | 203+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/PropertyDetailView.vala | 89++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Avapi/osmgpsmap.vapi | 270+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 475 insertions(+), 92 deletions(-)
diff --git a/README.markdown b/README.markdown
@@ -17,6 +17,7 @@ xmpedit depends on the following packages:
 * [Libxml2](http://xmlsoft.org/)
 * [libsoup](http://live.gnome.org/LibSoup), although this dependency should really go away
 * [Exiv2](http://www.exiv2.org/)
+* [osm-gps-map](http://nzjrs.github.com/osm-gps-map/) version 0.7.0 or higher
 
 The following additional dependencies are required to build xmpedit:
 
diff --git a/build b/build
@@ -125,7 +125,7 @@ class Program(object):
                ['-o', os.path.join('target', self.target)])
 
 def compile():
-    pkgs = ['gtk+-2.0', 'gee-1.0', 'libxml-2.0', 'libsoup-2.4', 'exiv2']
+    pkgs = ['gtk+-2.0', 'gee-1.0', 'libxml-2.0', 'libsoup-2.4', 'exiv2', 'osmgpsmap']
     xmpedit = Program('xmpedit') \
               .from_sources_under('src', 'lib') \
               .with_vapi_dirs('vapi') \
@@ -138,7 +138,7 @@ def compile():
 
 def test():
     # unit tests
-    pkgs = ['gtk+-2.0', 'gee-1.0', 'libxml-2.0', 'libsoup-2.4', 'exiv2']
+    pkgs = ['gtk+-2.0', 'gee-1.0', 'libxml-2.0', 'libsoup-2.4', 'exiv2', 'osmgpsmap']
     xmpedit_test = Program('xmpedit_test') \
                    .from_sources_under('src', 'lib') \
                    .with_vapi_dirs('vapi') \
diff --git a/src/ImageMetadata.vala b/src/ImageMetadata.vala
@@ -6,24 +6,24 @@
 
 namespace Xmpedit {
 
-public interface ImageProperty : Object {
+public abstract class ImageProperty : Object {
 
     public static Type[] all_types() {
-        return { typeof(Description) };
+        return { typeof(Description), typeof(Location) };
     }
 
     public abstract string name { get; }
-    public abstract RDF.Graph graph { get; construct; }
-    public abstract RDF.URIRef subject { get; construct; }
-    
+    public RDF.Graph graph { get; construct; }
+    public RDF.URIRef subject { get; construct; }
+
     public signal void changed();
-    
+
     /**
      * Examine the graph and return a one-line summary of the value found
      * (or "<i>not set</i>" if no value was found).
      */
     protected abstract string value_summary();
-    
+
     public string display_name() {
         return name.substring(0, 1).up() + name.substring(1);
     }
@@ -31,20 +31,57 @@ public interface ImageProperty : Object {
     public string list_markup() {
 	    return @"<b>$(display_name())</b>\n$(value_summary())";
 	}
-	
+
+    protected RDF.PlainLiteral? find_literal(RDF.URIRef predicate) {
+        var obj = graph.find_object(subject, predicate);
+        if (obj == null)
+            return null;
+        if (obj is RDF.SubjectNode) {
+            var node = (RDF.SubjectNode) obj;
+            if (graph.has_matching_statement(node,
+                    new RDF.URIRef(RDF.RDF_NS + "type"),
+                    new RDF.URIRef(RDF.RDF_NS + "Alt"))) {
+                obj = select_alternative(node);
+                if (obj == null) {
+                    warning("found rdf:Alt with no alternatives for %s", name);
+                    return null;
+                }
+            }
+        }
+        if (!(obj is RDF.PlainLiteral)) {
+            warning("found non-literal node for %s", name);
+            return null;
+        }
+        return (RDF.PlainLiteral) obj;
+    }
+
+    /** Given an rdf:Alt node, returns the "best" alternative */
+    protected RDF.Node? select_alternative(RDF.SubjectNode alt_node) {
+        var preferred_lang = get_preferred_lang();
+        var alternatives = graph.find_objects(alt_node, new RDF.URIRef(RDF.RDF_NS + "li"));
+        if (alternatives.size == 0)
+            return null;
+        foreach (var alternative in alternatives) {
+            if (alternative is RDF.PlainLiteral) {
+                var literal = (RDF.PlainLiteral) alternative;
+                if (literal.lang == preferred_lang)
+                    return literal;
+            }
+        }
+        return alternatives[0];
+    }
+
 }
 
-public class Description : Object, ImageProperty {
+public class Description : ImageProperty {
 
     private static RDF.URIRef DC_DESCRIPTION = new RDF.URIRef("http://purl.org/dc/elements/1.1/description");
-    
-    public string name { get { return "description"; } }
-    public RDF.Graph graph { get; construct; }
-    public RDF.URIRef subject { get; construct; }
+
+    public override string name { get { return "description"; } }
 
     private string _value;
     private string _lang;
-    
+
     public string value {
         get { return _value; }
         set { _value = value; update(); }
@@ -53,9 +90,9 @@ public class Description : Object, ImageProperty {
         get { return _lang; }
         set { _lang = value; update(); }
     }
-        
+
     construct {
-        var literal = find_literal();
+        var literal = find_literal(DC_DESCRIPTION);
         if (literal == null) {
             _value = "";
             _lang = "";
@@ -65,33 +102,10 @@ public class Description : Object, ImageProperty {
         }
     }
 
-    protected string value_summary() {
+    protected override string value_summary() {
         return _value;
     }
 
-    private RDF.PlainLiteral? find_literal() {
-        var description = graph.find_object(subject, DC_DESCRIPTION);
-        if (description == null)
-            return null;
-        if (description is RDF.SubjectNode) {
-            var node = (RDF.SubjectNode) description;
-            if (graph.has_matching_statement(node,
-                    new RDF.URIRef(RDF.RDF_NS + "type"),
-                    new RDF.URIRef(RDF.RDF_NS + "Alt"))) {
-                description = select_alternative(node, graph);
-                if (description == null) {
-                    warning("found rdf:Alt with no alternatives for %s", name);
-                    return null;
-                }
-            }
-        }
-        if (!(description is RDF.PlainLiteral)) {
-            warning("found non-literal node for %s", name);
-            return null;
-        }
-        return (RDF.PlainLiteral) description;
-    }
-    
     private void update() {
         graph.remove_matching_statements(subject, DC_DESCRIPTION, null);
         if (_value.length > 0) {
@@ -113,20 +127,53 @@ public class Description : Object, ImageProperty {
 
 }
 
-/** Given an rdf:Alt node, returns the "best" alternative */
-private RDF.Node? select_alternative(RDF.SubjectNode alt_node, RDF.Graph graph) {
-    var preferred_lang = get_preferred_lang();
-    var alternatives = graph.find_objects(alt_node, new RDF.URIRef(RDF.RDF_NS + "li"));
-    if (alternatives.size == 0)
-        return null;
-    foreach (var alternative in alternatives) {
-        if (alternative is RDF.PlainLiteral) {
-            var literal = (RDF.PlainLiteral) alternative;
-            if (literal.lang == preferred_lang)
-                return literal;
+public class Location : ImageProperty {
+
+    private static RDF.URIRef IPTCCORE_LOCATION = new RDF.URIRef("http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/Location");
+
+    public override string name { get { return "location"; } }
+
+    private string _location;
+    // XXX double bad
+    private double? _latitude;
+    private double? _longitude;
+
+    public string location {
+        get { return _location; }
+        set { _location = value; update(); }
+    }
+    public double? latitude {
+        get { return _latitude; }
+        set { _latitude = value; update(); }
+    }
+    public double? longitude {
+        get { return _longitude; }
+        set { _longitude = value; update(); }
+    }
+
+    construct {
+        var location_literal = find_literal(IPTCCORE_LOCATION);
+        if (location_literal != null) {
+            _location = location_literal.lexical_value;
+        } else {
+            _location = "";
         }
     }
-    return alternatives[0];
+
+    protected override string value_summary() {
+        if (_location.length > 0)
+            return _location;
+        else if (_latitude != null && _longitude != null)
+            return @"$(_latitude), $(_longitude)";
+        else
+            return "";
+    }
+
+    private void update() {
+        // XXX
+        changed();
+    }
+
 }
 
 // XXX make this a user pref somehow?
@@ -144,34 +191,34 @@ public class ImageMetadata : Object, Gtk.TreeModel {
     private size_t xmp_packet_size;
     private RDF.Graph graph;
     private RDF.URIRef subject;
-    
+
     // TreeModel stuff
     private static int last_stamp = 1;
     private int stamp;
-    
+
     public ImageMetadata(string path) {
         Object(path: path, dirty: false);
     }
-    
+
     construct {
         properties = new Gee.LinkedList<ImageProperty>();
         lock (last_stamp) {
             stamp = last_stamp ++;
         }
     }
-    
+
     public void load() {
         return_if_fail(image == null); // only call this once
         image = new Exiv2.Image.from_path(path);
         image.read_metadata();
         read_xmp();
     }
-    
+
     public void revert() {
         return_if_fail(image != null);
         read_xmp();
     }
-    
+
     private void read_xmp() {
         unowned string xmp = image.xmp_packet;
         xmp_packet_size = xmp.length;
@@ -194,7 +241,7 @@ public class ImageMetadata : Object, Gtk.TreeModel {
         }
         dirty = false;
     }
-    
+
     public void save() {
         return_if_fail(image != null); // only call after successful loading
         return_if_fail(dirty); // only call if dirty
@@ -225,14 +272,14 @@ public class ImageMetadata : Object, Gtk.TreeModel {
         image.write_metadata();
         dirty = false;
     }
-    
+
     private void clear_properties() {
         for (var i = properties.size - 1; i >= 0; i --) {
             properties.remove_at(i);
             row_deleted(path_for_index(i));
         }
     }
-    
+
     private void add_property(Type type) {
         var index = properties.size;
         var p = (ImageProperty) Object.new(type, graph: graph, subject: subject);
@@ -243,30 +290,30 @@ public class ImageMetadata : Object, Gtk.TreeModel {
             row_changed(path_for_index(index), iter_for_index(index));
         });
     }
-    
+
     /****** TREEMODEL IMPLEMENTATION STUFF **********/
-    
+
     private Gtk.TreePath path_for_index(int index) {
         return new Gtk.TreePath.from_indices(index);
     }
-    
+
     private Gtk.TreeIter iter_for_index(int index) {
         return { stamp, (void*) properties[index], null, null };
     }
-    
+
     public Type get_column_type(int column) {
         return_val_if_fail(column == 0, 0);
         return typeof(ImageProperty);
     }
-    
+
     public int get_n_columns() {
         return 1;
     }
-    
+
     public Gtk.TreeModelFlags get_flags() {
         return Gtk.TreeModelFlags.LIST_ONLY;
     }
-    
+
     public bool get_iter(out Gtk.TreeIter iter, Gtk.TreePath path) {
         if (path.get_depth() > 1) return false;
         var index = path.get_indices()[0];
@@ -280,14 +327,14 @@ public class ImageMetadata : Object, Gtk.TreeModel {
         var p = (ImageProperty) iter.user_data;
         return path_for_index(properties.index_of(p));
     }
-    
+
     public void get_value(Gtk.TreeIter iter, int column, out Value value) {
         return_if_fail(iter.stamp == stamp);
         return_if_fail(column == 0);
         value = Value(typeof(ImageProperty));
         value.set_object((ImageProperty) iter.user_data);
     }
-    
+
     public bool iter_children(out Gtk.TreeIter iter, Gtk.TreeIter? parent) {
         if (parent == null && !properties.is_empty) {
             iter = iter_for_index(0);
@@ -295,17 +342,17 @@ public class ImageMetadata : Object, Gtk.TreeModel {
         }
         return false;
     }
-    
+
     public bool iter_has_child(Gtk.TreeIter iter) {
         return false;
     }
-    
+
     public int iter_n_children(Gtk.TreeIter? iter) {
         if (iter == null)
             return properties.size;
         return 0;
     }
-    
+
     public bool iter_next(ref Gtk.TreeIter iter) {
         return_val_if_fail(iter.stamp == stamp, false);
         var index = properties.index_of((ImageProperty) iter.user_data);
@@ -315,7 +362,7 @@ public class ImageMetadata : Object, Gtk.TreeModel {
         }
         return false;
     }
-    
+
     public bool iter_nth_child(out Gtk.TreeIter iter, Gtk.TreeIter? parent, int n) {
         if (parent == null && n <= properties.size - 1) {
             iter = { stamp, (void*) properties[n], null, null };
@@ -323,17 +370,17 @@ public class ImageMetadata : Object, Gtk.TreeModel {
         }
         return false;
     }
-    
+
     public bool iter_parent(out Gtk.TreeIter iter, Gtk.TreeIter child) {
         return false;
     }
-    
+
     public void ref_node(Gtk.TreeIter iter) {
     }
-    
+
     public void unref_node(Gtk.TreeIter iter) {
     }
-    
+
 }
 
 }
diff --git a/src/PropertyDetailView.vala b/src/PropertyDetailView.vala
@@ -9,19 +9,19 @@ namespace Xmpedit {
 private class DescriptionView : Gtk.Table {
 
     public Description description { get; construct; }
-    
+
     public DescriptionView(Description description) {
         Object(description: description);
     }
-    
+
     construct {
         n_rows = 2;
         n_columns = 2;
         homogeneous = false;
-        
+
         var text_view = new Gtk.TextView();
         text_view.wrap_mode = Gtk.WrapMode.WORD;
-        
+
         var label = new Gtk.Label(description.display_name());
         label.xalign = 0;
         label.mnemonic_widget = text_view;
@@ -30,7 +30,7 @@ private class DescriptionView : Gtk.Table {
                 Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND, 0,
                 0, 0);
         set_row_spacing(0, 4);
-        
+
         var lang_hbox = new Gtk.HBox(/* homogeneous */ false, /* spacing */ 4);
         var lang_entry = new Gtk.Entry(); // XXX make a combo
         lang_entry.width_chars = 8;
@@ -44,7 +44,7 @@ private class DescriptionView : Gtk.Table {
                 0, 0,
                 0, 0);
         set_col_spacing(0, 10);
-        
+
         var text_scrolled = new Gtk.ScrolledWindow(null, null);
         text_scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
         text_scrolled.shadow_type = Gtk.ShadowType.ETCHED_IN;
@@ -53,8 +53,8 @@ private class DescriptionView : Gtk.Table {
                 0, 2, 1, 2,
                 Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND, Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND,
                 0, 0);
-        
-        
+
+
         text_view.buffer.text = description.value;
         lang_entry.text = description.lang;
         text_view.buffer.changed.connect(() => {
@@ -67,6 +67,65 @@ private class DescriptionView : Gtk.Table {
 
 }
 
+private class CustomMap : OsmGpsMap.Map {
+
+    public CustomMap() {
+        Object(repo_uri: "http://mt#R.google.com/vt/lyrs=m@138&hl=en&x=#X&s=&y=#Y&z=#Z");
+    }
+
+    construct {
+        var osd = new OsmGpsMap.Osd();
+        osd.show_scale = true;
+        osd.show_coordinates = false;
+        layer_add(osd);
+    }
+
+}
+
+private class LocationView : Gtk.Table {
+
+    public Location location { get; construct; }
+
+    public LocationView(Location location) {
+        Object(location: location);
+    }
+
+    construct {
+        n_rows = 2;
+        n_columns = 1;
+        homogeneous = false;
+        set_row_spacing(0, 8);
+
+        var map = new CustomMap();
+        attach(map,
+                0, 1, 0, 1,
+                Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND,
+                Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND,
+                0, 0);
+
+        var location_hbox = new Gtk.HBox(/* homogeneous */ false, /* spacing */ 4);
+        var location_entry = new Gtk.Entry(); // XXX make a combo
+        //location_entry.width_chars = 8;
+        var location_label = new Gtk.Label("Location:");
+        location_label.xalign = 0;
+        location_label.mnemonic_widget = location_entry;
+        location_hbox.pack_start(location_label,
+                /* expand */ false, /* fill */ false, /* padding */ 0);
+        location_hbox.pack_start(location_entry,
+                /* expand */ true, /* fill */ true, /* padding */ 0);
+        attach(location_hbox,
+                0, 1, 1, 2,
+                Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND, 0,
+                0, 0);
+
+        location_entry.text = location.location;
+        location_entry.changed.connect(() => {
+            location.location = location_entry.text;
+        });
+    }
+
+}
+
 public class PropertyDetailView : Gtk.Alignment {
 
     public MetadataTreeView tree_view { get; construct; }
@@ -86,13 +145,19 @@ public class PropertyDetailView : Gtk.Alignment {
             if (child != null) {
                 remove(child);
             }
-            // XXX
-            var d = new DescriptionView((Description) p);
-            add(d);
-            d.show_all();
+            add(construct_view(p));
+            child.show_all();
         });
     }
 
+    private static Gtk.Widget construct_view(ImageProperty p) {
+        if (p is Description)
+            return new DescriptionView((Description) p);
+        if (p is Location)
+            return new LocationView((Location) p);
+        return_val_if_reached(null);
+    }
+
 }
 
 }
diff --git a/vapi/osmgpsmap.vapi b/vapi/osmgpsmap.vapi
@@ -0,0 +1,270 @@
+/* osmgpsmap.vapi generated by vapigen-0.12, do not modify. */
+
+[CCode (cprefix = "OsmGpsMap", lower_case_cprefix = "osm_gps_map_")]
+namespace OsmGpsMap {
+	[CCode (cheader_filename = "osm-gps-map.h")]
+	public class Image : GLib.Object {
+		[CCode (has_construct_function = false)]
+		public Image ();
+		public static unowned OsmGpsMap.Image add (OsmGpsMap.Map map, float latitude, float longitude, Gdk.Pixbuf image);
+		public static unowned OsmGpsMap.Image add_with_alignment (OsmGpsMap.Map map, float latitude, float longitude, Gdk.Pixbuf image, float xalign, float yalign);
+		public void draw (Gdk.Drawable drawable, Gdk.GC gc, Gdk.Rectangle rect);
+		public unowned OsmGpsMap.Point get_point ();
+		public static bool remove (OsmGpsMap.Map map, OsmGpsMap.Image image);
+		public static void remove_all (OsmGpsMap.Map map);
+		[NoAccessorMethod]
+		public Gdk.Pixbuf pixbuf { owned get; set construct; }
+		[NoAccessorMethod]
+		public OsmGpsMap.Point point { owned get; set construct; }
+		[NoAccessorMethod]
+		public float x_align { get; set construct; }
+		[NoAccessorMethod]
+		public float y_align { get; set construct; }
+	}
+	[CCode (cname="OsmGpsMap", type_id="OSM_TYPE_GPS_MAP", cheader_filename = "osm-gps-map.h")]
+	public class Map : Gtk.DrawingArea, Atk.Implementor, Gtk.Buildable {
+		[CCode (cname = "osm_gps_map_new", type = "GtkWidget*", has_construct_function = false)]
+		public Map ();
+		[CCode (cname = "osm_gps_map_add_image")]
+		public void add_image (float latitude, float longitude, Gdk.Pixbuf image);
+		[CCode (cname = "osm_gps_map_add_image_with_alignment")]
+		public void add_image_with_alignment (float latitude, float longitude, Gdk.Pixbuf image, float xalign, float yalign);
+		[CCode (cname = "osm_gps_map_layer_add")]
+		public void layer_add (OsmGpsMap.Layer layer);
+		[CCode (cname = "osm_gps_map_add_track")]
+		public void add_track (GLib.SList track);
+		[CCode (cname = "osm_gps_map_clear_gps")]
+		public void clear_gps ();
+		[CCode (cname = "osm_gps_map_clear_images")]
+		public void clear_images ();
+		[CCode (cname = "osm_gps_map_clear_tracks")]
+		public void clear_tracks ();
+		[CCode (cname = "osm_gps_map_convert_geographic_to_screen")]
+		public void convert_geographic_to_screen (OsmGpsMap.Point pt, int pixel_x, int pixel_y);
+		[CCode (cname = "osm_gps_map_convert_screen_to_geographic")]
+		public void convert_screen_to_geographic (int pixel_x, int pixel_y, OsmGpsMap.Point pt);
+		[CCode (cname = "osm_gps_map_download_cancel_all")]
+		public void download_cancel_all ();
+		[CCode (cname = "osm_gps_map_download_maps")]
+		public void download_maps (OsmGpsMap.Point pt1, OsmGpsMap.Point pt2, int zoom_start, int zoom_end);
+		[CCode (cname = "osm_gps_map_draw_gps")]
+		public void draw_gps (float latitude, float longitude, float heading);
+		[NoWrapper]
+		public virtual void draw_gps_point (Gdk.Drawable drawable);
+		[CCode (cname = "osm_gps_map_geographic_to_screen")]
+		public void geographic_to_screen (float latitude, float longitude, int pixel_x, int pixel_y);
+		[CCode (cname = "osm_gps_map_get_bbox")]
+		public void get_bbox (OsmGpsMap.Point pt1, OsmGpsMap.Point pt2);
+		[CCode (cname = "osm_gps_map_get_co_ordinates")]
+		public unowned OsmGpsMap.Point get_co_ordinates (int pixel_x, int pixel_y);
+		[CCode (cname = "osm_gps_map_get_default_cache_directory")]
+		public static unowned string get_default_cache_directory ();
+		[CCode (cname = "osm_gps_map_get_event_location")]
+		public unowned OsmGpsMap.Point get_event_location (Gdk.EventButton event);
+		[CCode (cname = "osm_gps_map_get_scale")]
+		public float get_scale ();
+		[CCode (cname = "osm_gps_map_gps_add")]
+		public void gps_add (float latitude, float longitude, float heading);
+		[CCode (cname = "osm_gps_map_gps_clear")]
+		public void gps_clear ();
+		[CCode (cname = "osm_gps_map_gps_get_track")]
+		public unowned OsmGpsMap.Track gps_get_track ();
+		[CCode (cname = "osm_gps_map_remove_image")]
+		public bool remove_image (Gdk.Pixbuf image);
+		[CCode (cname = "osm_gps_map_replace_track")]
+		public void replace_track (GLib.SList old_track, GLib.SList new_track);
+		[CCode (cname = "osm_gps_map_screen_to_geographic")]
+		public void screen_to_geographic (int pixel_x, int pixel_y, float latitude, float longitude);
+		[CCode (cname = "osm_gps_map_scroll")]
+		public void scroll (int dx, int dy);
+		[CCode (cname = "osm_gps_map_set_center")]
+		public void set_center (float latitude, float longitude);
+		[CCode (cname = "osm_gps_map_set_center_and_zoom")]
+		public void set_center_and_zoom (float latitude, float longitude, int zoom);
+		[CCode (cname = "osm_gps_map_set_keyboard_shortcut")]
+		public void set_keyboard_shortcut (OsmGpsMap.Key key, uint keyval);
+		[CCode (cname = "osm_gps_map_set_mapcenter")]
+		public void set_mapcenter (float latitude, float longitude, int zoom);
+		[CCode (cname = "osm_gps_map_set_zoom")]
+		public int set_zoom (int zoom);
+		[CCode (cname = "osm_gps_map_source_get_friendly_name")]
+		public static unowned string source_get_friendly_name (OsmGpsMap.Source source);
+		[CCode (cname = "osm_gps_map_source_get_image_format")]
+		public static unowned string source_get_image_format (OsmGpsMap.Source source);
+		[CCode (cname = "osm_gps_map_source_get_max_zoom")]
+		public static int source_get_max_zoom (OsmGpsMap.Source source);
+		[CCode (cname = "osm_gps_map_source_get_min_zoom")]
+		public static int source_get_min_zoom (OsmGpsMap.Source source);
+		[CCode (cname = "osm_gps_map_source_get_repo_uri")]
+		public static unowned string source_get_repo_uri (OsmGpsMap.Source source);
+		[CCode (cname = "osm_gps_map_source_is_valid")]
+		public static bool source_is_valid (OsmGpsMap.Source source);
+		[CCode (cname = "osm_gps_map_zoom_in")]
+		public static int zoom_in (OsmGpsMap.Map map);
+		[CCode (cname = "osm_gps_map_zoom_out")]
+		public static int zoom_out (OsmGpsMap.Map map);
+		[NoAccessorMethod]
+		public bool auto_center { get; set construct; }
+		[NoAccessorMethod]
+		public float auto_center_threshold { get; set construct; }
+		[NoAccessorMethod]
+		public bool auto_download { get; set construct; }
+		[NoAccessorMethod]
+		public int drag_limit { get; construct; }
+		[NoAccessorMethod]
+		public int gps_track_highlight_radius { get; set construct; }
+		[NoAccessorMethod]
+		public int gps_track_point_radius { get; set construct; }
+		[NoAccessorMethod]
+		public float gps_track_width { get; set construct; }
+		[NoAccessorMethod]
+		public string image_format { owned get; construct; }
+		[NoAccessorMethod]
+		public float latitude { get; }
+		[NoAccessorMethod]
+		public float longitude { get; }
+		[NoAccessorMethod]
+		public int map_source { get; set construct; }
+		[NoAccessorMethod]
+		public int map_x { get; construct; }
+		[NoAccessorMethod]
+		public int map_y { get; construct; }
+		[NoAccessorMethod]
+		public int max_zoom { get; construct; }
+		[NoAccessorMethod]
+		public int min_zoom { get; construct; }
+		[NoAccessorMethod]
+		public string proxy_uri { owned get; construct; }
+		[NoAccessorMethod]
+		public bool record_trip_history { get; set construct; }
+		[NoAccessorMethod]
+		public string repo_uri { owned get; construct; }
+		[NoAccessorMethod]
+		public bool show_gps_point { get; set construct; }
+		[NoAccessorMethod]
+		public bool show_trip_history { get; set construct; }
+		[NoAccessorMethod]
+		public string tile_cache { owned get; set construct; }
+		[NoAccessorMethod]
+		public string tile_cache_base { owned get; construct; }
+		[NoAccessorMethod]
+		public bool tile_cache_is_full_path { get; set; }
+		[NoAccessorMethod]
+		public int tiles_queued { get; }
+		[NoAccessorMethod]
+		public int zoom { get; construct; }
+		public virtual signal void changed ();
+	}
+	[CCode (cheader_filename = "osm-gps-map.h")]
+	public class Osd : GLib.Object, OsmGpsMap.Layer {
+		[CCode (has_construct_function = false)]
+		public Osd ();
+		[NoAccessorMethod]
+		public uint dpad_radius { get; set construct; }
+		[NoAccessorMethod]
+		public int osd_x { get; set construct; }
+		[NoAccessorMethod]
+		public int osd_y { get; set construct; }
+		[NoAccessorMethod]
+		public bool show_coordinates { get; set construct; }
+		[NoAccessorMethod]
+		public bool show_crosshair { get; set construct; }
+		[NoAccessorMethod]
+		public bool show_dpad { get; set construct; }
+		[NoAccessorMethod]
+		public bool show_gps_in_dpad { get; set construct; }
+		[NoAccessorMethod]
+		public bool show_gps_in_zoom { get; set construct; }
+		[NoAccessorMethod]
+		public bool show_scale { get; set construct; }
+		[NoAccessorMethod]
+		public bool show_zoom { get; set construct; }
+	}
+	[Compact]
+	[CCode (copy_function = "osm_gps_map_point_copy", type_id = "OSM_GPS_MAP_TYPE_POINT", cheader_filename = "osm-gps-map.h")]
+	public class Point {
+		public float rlat;
+		public float rlon;
+		public unowned OsmGpsMap.Point copy ();
+		[CCode (has_construct_function = false)]
+		public Point.degrees (float lat, float lon);
+		public void get_degrees (float lat, float lon);
+		public void get_radians (float rlat, float rlon);
+		[CCode (has_construct_function = false)]
+		public Point.radians (float rlat, float rlon);
+		public void set_degrees (float lat, float lon);
+		public void set_radians (float rlat, float rlon);
+	}
+	[CCode (cheader_filename = "osm-gps-map.h")]
+	public class Track : GLib.Object {
+		[CCode (has_construct_function = false)]
+		public Track ();
+		public static void add (OsmGpsMap.Map map, OsmGpsMap.Track track);
+		public void add_point (OsmGpsMap.Point point);
+		public void get_color (Gdk.Color color);
+		public unowned GLib.SList get_points ();
+		public static bool remove (OsmGpsMap.Map map, OsmGpsMap.Track track);
+		public static void remove_all (OsmGpsMap.Map map);
+		[NoAccessorMethod]
+		public float alpha { get; set construct; }
+		[NoAccessorMethod]
+		public Gdk.Color color { get; set; }
+		[NoAccessorMethod]
+		public float line_width { get; set construct; }
+		[NoAccessorMethod]
+		public void* track { get; construct; }
+		[NoAccessorMethod]
+		public bool visible { get; set construct; }
+		public virtual signal void point_added (OsmGpsMap.Point p0);
+	}
+	[CCode (cheader_filename = "osm-gps-map.h")]
+	public interface Layer {
+		public static void add (OsmGpsMap.Map map, OsmGpsMap.Layer layer);
+		public abstract bool busy ();
+		public abstract bool button_press (OsmGpsMap.Map map, Gdk.EventButton event);
+		public abstract void draw (OsmGpsMap.Map map, Gdk.Drawable drawable);
+		public static bool remove (OsmGpsMap.Map map, OsmGpsMap.Layer layer);
+		public static void remove_all (OsmGpsMap.Map map);
+		public abstract void render (OsmGpsMap.Map map);
+	}
+	[CCode (cname="OsmGpsMapKey_t", cprefix = "OSM_GPS_MAP_KEY_", has_type_id = false, cheader_filename = "osm-gps-map.h")]
+	public enum Key {
+		FULLSCREEN,
+		ZOOMIN,
+		ZOOMOUT,
+		UP,
+		DOWN,
+		LEFT,
+		RIGHT,
+		MAX
+	}
+	[CCode (cname="OsmGpsMapSource_t", cprefix = "OSM_GPS_MAP_SOURCE_", has_type_id = false, cheader_filename = "osm-gps-map.h")]
+	public enum Source {
+		NULL,
+		OPENSTREETMAP,
+		OPENSTREETMAP_RENDERER,
+		OPENAERIALMAP,
+		MAPS_FOR_FREE,
+		OPENCYCLEMAP,
+		OSM_PUBLIC_TRANSPORT,
+		GOOGLE_STREET,
+		GOOGLE_SATELLITE,
+		GOOGLE_HYBRID,
+		VIRTUAL_EARTH_STREET,
+		VIRTUAL_EARTH_SATELLITE,
+		VIRTUAL_EARTH_HYBRID,
+		YAHOO_STREET,
+		YAHOO_SATELLITE,
+		YAHOO_HYBRID,
+		OSMC_TRAILS,
+		LAST
+	}
+	[CCode (cheader_filename = "osm-gps-map.h")]
+	public const string CACHE_AUTO;
+	[CCode (cheader_filename = "osm-gps-map.h")]
+	public const string CACHE_DISABLED;
+	[CCode (cheader_filename = "osm-gps-map.h")]
+	public const string CACHE_FRIENDLY;
+	[CCode (cheader_filename = "osm-gps-map.h")]
+	public const int INVALID;
+}