Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package libshumate for openSUSE:Factory 
checked in at 2026-10-30 18:07:59
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libshumate (Old)
 and      /work/SRC/openSUSE:Factory/.libshumate.new.1255 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libshumate"

Fri Oct 30 18:07:59 2026 rev:28 rq:1385448 version:1.7.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/libshumate/libshumate.changes    2026-08-09 
21:32:09.679680029 +0200
+++ /work/SRC/openSUSE:Factory/.libshumate.new.1255/libshumate.changes  
2026-10-30 18:10:11.030951378 +0100
@@ -1,0 +2,33 @@
+Wed Sep 30 16:31:18 UTC 2026 - Dominique Leuenberger <[email protected]>
+
+- Update version dependencies according to meson.build.
+
+-------------------------------------------------------------------
+Fri Sep 11 14:29:32 UTC 2026 - Dominique Leuenberger <[email protected]>
+
+- Update to version 1.7.0:
+  + No changes since 1.7.0.
+
+-------------------------------------------------------------------
+Sat Aug 29 13:27:34 UTC 2026 - Bjørn Lie <[email protected]>
+
+- Update to version 1.7.rc:
+  + No changes since 1.7.beta.
+
+-------------------------------------------------------------------
+Wed Aug 19 23:57:11 CEST 2026 - Bjørn Lie <[email protected]>
+
+- Update to version 1.7.beta:
+  + Deselect markers before removing from layer
+  + Stop using G_GNUC_CONST in _get_type functions
+  + gi-docgen: List more dependencies
+
+-------------------------------------------------------------------
+Wed Aug 19 23:57:10 CEST 2026 - Bjørn Lie <[email protected]>
+
+- Update to version 1.7.alpha:
+  + Added new APIs to refresh and get refresh notifications for map
+    sources
+  + Added missing translation text domains
+
+-------------------------------------------------------------------
@@ -341,0 +375 @@
+

Old:
----
  libshumate-1.6.3.tar.xz

New:
----
  libshumate-1.7.0.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ libshumate.spec ++++++
--- /var/tmp/diff_new_pack.iXIyI6/_old  2026-10-30 18:10:13.238043771 +0100
+++ /var/tmp/diff_new_pack.iXIyI6/_new  2026-10-30 18:10:13.246044106 +0100
@@ -22,7 +22,7 @@
 %bcond_with profiling
 
 Name:           libshumate
-Version:        1.6.3
+Version:        1.7.0
 Release:        0
 Summary:        C library providing a GtkWidget to display maps
 License:        Apache-2.0 AND LGPL-2.1-or-later AND BSD-3-Clause AND CC0-1.0 
AND CC-BY-3.0 AND GFDL-1.2-only
@@ -35,12 +35,12 @@
 BuildRequires:  pkgconfig
 BuildRequires:  pkgconfig(cairo) >= 1.4
 BuildRequires:  pkgconfig(gi-docgen) >= 2021.1
-BuildRequires:  pkgconfig(gio-2.0) >= 2.16.0
-BuildRequires:  pkgconfig(glib-2.0) >= 2.16.0
-BuildRequires:  pkgconfig(gobject-2.0) >= 2.16.0
+BuildRequires:  pkgconfig(gio-2.0) >= 2.74.0
+BuildRequires:  pkgconfig(glib-2.0) >= 2.74.0
+BuildRequires:  pkgconfig(gobject-2.0) >= 2.74.0
 BuildRequires:  pkgconfig(gobject-introspection-1.0) >= 0.6.3
 BuildRequires:  pkgconfig(gtk4)
-BuildRequires:  pkgconfig(json-glib-1.0)
+BuildRequires:  pkgconfig(json-glib-1.0) >= 1.6
 BuildRequires:  pkgconfig(libprotobuf-c)
 BuildRequires:  pkgconfig(libsoup-3.0) >= 3.0
 BuildRequires:  pkgconfig(sqlite3) >= 1.12.0

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.iXIyI6/_old  2026-10-30 18:10:13.318047120 +0100
+++ /var/tmp/diff_new_pack.iXIyI6/_new  2026-10-30 18:10:13.324047371 +0100
@@ -1,6 +1,7 @@
-mtime: 1785585138
-commit: 6438bec2a941e9de6899259514cb6b48ed6dfdfa0c0c9e010bfaca03773cf4aa
+mtime: 1790785882
+commit: e133a032bae6289b5217c2ff8cac4255ee07eddf3d65515fca746ddd6c82992b
 url: https://src.opensuse.org/GNOME/libshumate
-revision: 6438bec2a941e9de6899259514cb6b48ed6dfdfa0c0c9e010bfaca03773cf4aa
+revision: e133a032bae6289b5217c2ff8cac4255ee07eddf3d65515fca746ddd6c82992b
+trackingbranch: factory
 projectscmsync: https://src.opensuse.org/GNOME/_ObsPrj
 

++++++ _service ++++++
--- /var/tmp/diff_new_pack.iXIyI6/_old  2026-10-30 18:10:13.383049841 +0100
+++ /var/tmp/diff_new_pack.iXIyI6/_new  2026-10-30 18:10:13.397050428 +0100
@@ -4,7 +4,7 @@
     <param name="url">https://gitlab.gnome.org/GNOME/libshumate.git</param>
     <param name="scm">git</param>
     <param name="versionformat">@PARENT_TAG@+@TAG_OFFSET@</param>
-    <param name="revision">1.6.3</param>
+    <param name="revision">1.7.0</param>
     <param name="versionrewrite-pattern">(.*)\+0</param>
     <param name="versionrewrite-replacement">\1</param>
   </service>

++++++ build.specials.obscpio ++++++

++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore      1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore      2026-09-30 18:31:22.000000000 +0200
@@ -0,0 +1,5 @@
+*.obscpio
+*.osc
+_build.*
+.pbuild
+osc-collab.*

++++++ libshumate-1.6.3.tar.xz -> libshumate-1.7.0.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/CONTRIBUTING.md 
new/libshumate-1.7.0/CONTRIBUTING.md
--- old/libshumate-1.6.3/CONTRIBUTING.md        1970-01-01 01:00:00.000000000 
+0100
+++ new/libshumate-1.7.0/CONTRIBUTING.md        2026-09-11 09:50:19.000000000 
+0200
@@ -0,0 +1,16 @@
+## Issue Tracker
+
+Issues labeled as ~"1. Feature" or ~"1. Enhancement" are accepted for 
implementation. Feature requests that still need a decission are labeld as ~"2. 
RFC".
+
+## Code of Conduct
+
+When interacting with the project, the [GNOME Code Of 
Conduct](https://conduct.gnome.org/) applies.
+
+## Use of Generative AI
+
+This project does not allow contributions generated by large languages models 
(LLMs) and chatbots. This ban includes, but is not limited to, tools like 
ChatGPT, Claude, Copilot, DeepSeek, and Devin AI. We are taking these steps as 
precaution due to the potential negative influence of AI generated content on 
quality, as well as likely copyright violations.
+
+This ban of AI generated content applies to all parts of the projects, 
including, but not limited to, code, documentation, issues, and artworks. An 
exception applies for purely translating texts for issues and comments to 
English.
+
+AI tools can be used to answer questions and find information. However, we 
encourage contributors to avoid them in favor of using [existing 
documentation](https://developer.gnome.org) and our [chats and 
forums](https://welcome.gnome.org). Since AI generated information is 
frequently misleading or false, we cannot supply support on anything 
referencing AI output.
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/NEWS new/libshumate-1.7.0/NEWS
--- old/libshumate-1.6.3/NEWS   2026-08-01 13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/NEWS   2026-09-11 09:50:19.000000000 +0200
@@ -1,17 +1,25 @@
-==================
-Version 1.6.3
-==================
- - Deselect markers before removing from layer
+===================
+Version 1.7.0
+===================
+ - No changes since 1.7.0
 
-==================
-Version 1.6.2
-==================
- - Avoid spurious drag events following pinch zoom and rotate gestures on touch
+===================
+Version 1.7.rc
+===================
+ - No changes since 1.7.beta
 
-==================
-Version 1.6.1
-==================
- - Add missing gettext domains
+===================
+Version 1.7.beta
+===================
+ - Deselect markers before removing from layer
+ - Stop using G_GNUC_CONST in _get_type functions
+ - gi-docgen: List more dependencies
+
+===================
+Version 1.7.alpha
+===================
+ - Added new APIs to refresh and get refresh notifications for map sources
+ - Added missing translation text domains
 
 ===================
 Version 1.6.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/demos/shumate-demo-window.c 
new/libshumate-1.7.0/demos/shumate-demo-window.c
--- old/libshumate-1.6.3/demos/shumate-demo-window.c    2026-08-01 
13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/demos/shumate-demo-window.c    2026-09-11 
09:50:19.000000000 +0200
@@ -161,7 +161,7 @@
                       GVariant      *parameter,
                       gpointer       user_data)
 {
-  shumate_map_go_to_full (SHUMATE_MAP (user_data), 49.531565, 17.532806, 4.5);
+  shumate_map_go_to_full (shumate_simple_map_get_map (SHUMATE_SIMPLE_MAP 
(user_data)), 49.531565, 17.532806, 4.5);
 }
 
 static void
@@ -169,7 +169,7 @@
                    GVariant      *parameter,
                    gpointer       user_data)
 {
-  shumate_map_go_to_full (SHUMATE_MAP (user_data), 40.718820, -74.001605, 9);
+  shumate_map_go_to_full (shumate_simple_map_get_map (SHUMATE_SIMPLE_MAP 
(user_data)), 40.718820, -74.001605, 9);
 }
 
 static void
@@ -177,7 +177,23 @@
                             GVariant      *parameter,
                             gpointer       user_data)
 {
-  shumate_map_go_to_full (SHUMATE_MAP (user_data), 48.858279, 2.294486, 18);
+  shumate_map_go_to_full (shumate_simple_map_get_map (SHUMATE_SIMPLE_MAP 
(user_data)), 48.858279, 2.294486, 18);
+}
+
+static void
+activate_refresh (GSimpleAction *simple,
+                  GVariant      *parameter,
+                  gpointer       user_data)
+{
+  shumate_map_layer_refresh (shumate_simple_map_get_base_map_layer 
(SHUMATE_SIMPLE_MAP (user_data)));
+}
+
+static void
+activate_retry_failed (GSimpleAction *simple,
+                       GVariant      *parameter,
+                       gpointer       user_data)
+{
+  shumate_map_layer_retry_failed (shumate_simple_map_get_base_map_layer 
(SHUMATE_SIMPLE_MAP (user_data)));
 }
 
 static void
@@ -194,6 +210,8 @@
     { "goto-europe", activate_goto_europe },
     { "goto-nyc", activate_goto_nyc },
     { "goto-eiffel-tower", activate_goto_eiffel_tower },
+    { "refresh", activate_refresh },
+    { "retry-failed", activate_retry_failed },
   };
 
   gtk_widget_init_template (GTK_WIDGET (self));
@@ -202,7 +220,7 @@
   g_action_map_add_action_entries (G_ACTION_MAP (action_map),
                                    action_entries,
                                    G_N_ELEMENTS (action_entries),
-                                   shumate_simple_map_get_map (self->map));
+                                   self->map);
   gtk_widget_insert_action_group (GTK_WIDGET (self), "win", G_ACTION_GROUP 
(action_map));
 
   self->registry = shumate_map_source_registry_new_with_defaults ();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/demos/shumate-demo-window.ui 
new/libshumate-1.7.0/demos/shumate-demo-window.ui
--- old/libshumate-1.6.3/demos/shumate-demo-window.ui   2026-08-01 
13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/demos/shumate-demo-window.ui   2026-09-11 
09:50:19.000000000 +0200
@@ -13,6 +13,16 @@
         </child>
         <child type="end">
           <object class="GtkMenuButton">
+            <property name="label" translatable="true">Actions</property>
+            <property name="popover">
+              <object class="GtkPopoverMenu">
+                <property name="menu-model">actions_menu</property>
+              </object>
+            </property>
+          </object>
+        </child>
+        <child type="end">
+          <object class="GtkMenuButton">
             <property name="label" translatable="true">Go To</property>
             <property name="popover">
               <object class="GtkPopoverMenu">
@@ -45,4 +55,14 @@
       <attribute name="action">win.goto-eiffel-tower</attribute>
     </item>
   </menu>
+  <menu id="actions_menu">
+    <item>
+      <attribute name="label" translatable="true">Refresh</attribute>
+      <attribute name="action">win.refresh</attribute>
+    </item>
+    <item>
+      <attribute name="label" translatable="true">Retry Failed</attribute>
+      <attribute name="action">win.retry-failed</attribute>
+    </item>
+  </menu>
 </interface>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/demos/shumate-test-tile-source.c 
new/libshumate-1.7.0/demos/shumate-test-tile-source.c
--- old/libshumate-1.6.3/demos/shumate-test-tile-source.c       2026-08-01 
13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/demos/shumate-test-tile-source.c       2026-09-11 
09:50:19.000000000 +0200
@@ -32,6 +32,7 @@
   return g_object_new (SHUMATE_TYPE_TEST_TILE_SOURCE,
                        "id", SHUMATE_MAP_SOURCE_TEST,
                        "name", "Test Pattern",
+                       "tile-size", 512,
                        NULL);
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/docs/libshumate.toml.in 
new/libshumate-1.7.0/docs/libshumate.toml.in
--- old/libshumate-1.6.3/docs/libshumate.toml.in        2026-08-01 
13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/docs/libshumate.toml.in        2026-09-11 
09:50:19.000000000 +0200
@@ -20,11 +20,26 @@
 description = "The base type system library"
 docs_url = "https://docs.gtk.org/gobject/";
 
+[dependencies."Gio-2.0"]
+name = "Gio"
+description = "GObject Interfaces and Objects, Networking, IPC, and I/O"
+docs_url = "https://docs.gtk.org/gio/";
+
+[dependencies."Gdk-4.0"]
+name = "Gdk"
+description = "The GTK windowing system abstraction"
+docs_url = "https://docs.gtk.org/gdk4/";
+
 [dependencies."Gtk-4.0"]
-name = "GTK"
+name = "Gtk"
 description = "The GTK toolkit"
 docs_url = "https://docs.gtk.org/gtk4/";
 
+[dependencies."Gdk-Pixbuf-2.0"]
+name = "GdkPixbuf"
+description = "Image loading library"
+docs_url = "https://docs.gtk.org/gdk-pixbuf/";
+
 [theme]
 name = "basic"
 show_index_summary = true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/libshumate.doap 
new/libshumate-1.7.0/libshumate.doap
--- old/libshumate-1.6.3/libshumate.doap        2026-08-01 13:10:51.000000000 
+0200
+++ new/libshumate-1.7.0/libshumate.doap        2026-09-11 09:50:19.000000000 
+0200
@@ -19,7 +19,7 @@
   <maintainer>
     <foaf:Person>
       <foaf:name>Marcus Lundblad</foaf:name>
-      <foaf:mbox rdf:resource="mailto:[email protected]"; />
+      <foaf:mbox rdf:resource="mailto:[email protected]"; />
       <gnome:userid>mlundblad</gnome:userid>
     </foaf:Person>
   </maintainer>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/meson.build 
new/libshumate-1.7.0/meson.build
--- old/libshumate-1.6.3/meson.build    2026-08-01 13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/meson.build    2026-09-11 09:50:19.000000000 +0200
@@ -1,6 +1,6 @@
 project(
   'libshumate', 'c',
-  version: '1.6.3',
+  version: '1.7.0',
   license: 'LGPLv2.1+',
   meson_version: '>= 0.62.0',
   default_options: [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/po/LINGUAS 
new/libshumate-1.7.0/po/LINGUAS
--- old/libshumate-1.6.3/po/LINGUAS     2026-08-01 13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/po/LINGUAS     2026-09-11 09:50:19.000000000 +0200
@@ -26,6 +26,7 @@
 ie
 is
 it
+ja
 ka
 kab
 kk
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/po/ja.po 
new/libshumate-1.7.0/po/ja.po
--- old/libshumate-1.6.3/po/ja.po       1970-01-01 01:00:00.000000000 +0100
+++ new/libshumate-1.7.0/po/ja.po       2026-09-11 09:50:19.000000000 +0200
@@ -0,0 +1,68 @@
+# Japanese translation for libshumate.
+# Copyright (C) 2026 libshumate's COPYRIGHT HOLDER
+# This file is distributed under the same license as the libshumate package.
+# Ryo Nakano <[email protected]>,  2026.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libshumate main\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libshumate/issues/\n";
+"POT-Creation-Date: 2025-12-28 21:17+0000\n"
+"PO-Revision-Date: 2026-03-16 22:58+0900\n"
+"Last-Translator: Ryo Nakano <[email protected]>\n"
+"Language-Team: Japanese <[email protected]>\n"
+"Language: ja\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: data/shumate-inspector-page.ui:32
+msgid "Show Debug Overlay"
+msgstr "デバッグオーバーレイを表示"
+
+#: data/shumate-inspector-page.ui:62
+msgid "Show Tile Bounds"
+msgstr "タイルの境界を表示"
+
+#: data/shumate-inspector-page.ui:92
+msgid "Show Collision Boxes"
+msgstr "バウンディングボックスを表示"
+
+#: data/shumate-simple-map.ui:39
+msgid "Zoom In"
+msgstr "ズームイン"
+
+#: data/shumate-simple-map.ui:53
+msgid "Zoom Out"
+msgstr "ズームアウト"
+
+#. m is the unit for meters
+#: shumate/shumate-scale.c:197
+#, c-format
+msgid "%d m"
+msgstr "%d m"
+
+#. km is the unit for kilometers
+#: shumate/shumate-scale.c:200
+#, c-format
+msgid "%d km"
+msgstr "%d km"
+
+# Google マップに合わせて「フィート」としました
+#. ft is the unit for feet
+#: shumate/shumate-scale.c:206
+#, c-format
+msgid "%d ft"
+msgstr "%d フィート"
+
+# Google マップに合わせて「マイル」としました
+#. mi is the unit for miles
+#: shumate/shumate-scale.c:209
+#, c-format
+msgid "%d mi"
+msgstr "%d マイル"
+
+#: shumate/shumate-inspector-page.c:61
+msgid "Shumate"
+msgstr "Shumate"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/po/kw.po 
new/libshumate-1.7.0/po/kw.po
--- old/libshumate-1.6.3/po/kw.po       2026-08-01 13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/po/kw.po       2026-09-11 09:50:19.000000000 +0200
@@ -8,8 +8,8 @@
 msgstr ""
 "Project-Id-Version: libshumate main\n"
 "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/libshumate/issues/\n";
-"POT-Creation-Date: 2025-12-28 21:17+0000\n"
-"PO-Revision-Date: 2026-01-26 13:48+0000\n"
+"POT-Creation-Date: 2026-04-23 21:39+0000\n"
+"PO-Revision-Date: 2026-05-11 22:19+0100\n"
 "Last-Translator: Flynn Peck <[email protected]>\n"
 "Language-Team: kw\n"
 "Language: kw\n"
@@ -23,7 +23,7 @@
 "X-DL-Domain: po\n"
 "X-DL-State: Translating\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Gtranslator 49.0\n"
+"X-Generator: Gtranslator 50.0\n"
 
 #: data/shumate-inspector-page.ui:32
 msgid "Show Debug Overlay"
@@ -35,7 +35,7 @@
 
 #: data/shumate-inspector-page.ui:92
 msgid "Show Collision Boxes"
-msgstr "Diskwedhes Boksys Kesskwat"
+msgstr "Diskwedhes Boksys Gesskwat"
 
 #: data/shumate-simple-map.ui:39
 msgid "Zoom In"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/shumate/shumate-enum-types.h.in 
new/libshumate-1.7.0/shumate/shumate-enum-types.h.in
--- old/libshumate-1.6.3/shumate/shumate-enum-types.h.in        2026-08-01 
13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/shumate/shumate-enum-types.h.in        2026-09-11 
09:50:19.000000000 +0200
@@ -13,7 +13,7 @@
 /*** END file-production ***/
 
 /*** BEGIN value-header ***/
-GType @enum_name@_get_type (void) G_GNUC_CONST;
+GType @enum_name@_get_type (void);
 #define SHUMATE_TYPE_@ENUMSHORT@ @enum_name@_get_type()
 /*** END value-header ***/
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/shumate/shumate-map-layer.c 
new/libshumate-1.7.0/shumate/shumate-map-layer.c
--- old/libshumate-1.6.3/shumate/shumate-map-layer.c    2026-08-01 
13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/shumate/shumate-map-layer.c    2026-09-11 
09:50:19.000000000 +0200
@@ -60,6 +60,11 @@
   gint64 defer_frame_time;
   gboolean deferring;
 
+  gboolean refreshing;
+  gboolean retrying_failed;
+
+  GSignalGroup *source_signal_group;
+
   ShumateVectorSymbolContainer *symbols;
 };
 
@@ -98,15 +103,19 @@
 }
 
 typedef struct {
-  ShumateTile *tile;
+  ShumateMapLayer *self;
+  ShumateTile *current_tile;
+  ShumateTile *loading_tile;
   GCancellable *cancellable;
-  guint error;
+  ShumateGridPosition pos;
+  gboolean failed;
 } TileChild;
 
 static void
 tile_child_free (TileChild *child)
 {
-  g_clear_object (&child->tile);
+  g_clear_object (&child->current_tile);
+  g_clear_object (&child->loading_tile);
   g_clear_object (&child->cancellable);
   g_free (child);
 }
@@ -116,7 +125,6 @@
   ShumateMapLayer *self;
   TileChild *tile_child;
   char *source_id;
-  ShumateGridPosition pos;
 } TileFilledData;
 
 static void
@@ -139,6 +147,8 @@
   g_assert (SHUMATE_IS_MAP_LAYER (self));
   g_assert (SHUMATE_IS_TILE (tile));
 
+  shumate_vector_symbol_container_remove_symbols (self->symbols, pos->x, 
pos->y, pos->zoom);
+
   if ((symbols = shumate_tile_get_symbols (tile)))
     shumate_vector_symbol_container_add_symbols (self->symbols,
                                                  symbols,
@@ -150,11 +160,13 @@
 static void recompute_grid (ShumateMapLayer *self);
 
 static void
-on_tile_notify_state (ShumateMapLayer          *self,
-                      G_GNUC_UNUSED GParamSpec *pspec,
-                      ShumateTile              *tile)
-{
-  gtk_widget_queue_draw (GTK_WIDGET (self));
+on_tile_notify_paintable (TileChild                *tile_child,
+                          G_GNUC_UNUSED GParamSpec *pspec,
+                          ShumateTile              *tile)
+{
+  g_set_object (&tile_child->current_tile, tile);
+  add_symbols (tile_child->self, tile, &tile_child->pos);
+  gtk_widget_queue_draw (GTK_WIDGET (tile_child->self));
 }
 
 static void
@@ -168,40 +180,57 @@
 
   success = shumate_map_source_fill_tile_finish (SHUMATE_MAP_SOURCE 
(source_object), res, &error);
 
+  if (!success && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+    return;
+
+  g_set_object (&data->tile_child->current_tile, 
data->tile_child->loading_tile);
+
+  data->tile_child->failed = !success;
+
   if (!success)
     {
-      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-        {
-          data->tile_child->error = TRUE;
-          g_signal_emit (data->self, signals[TILE_ERROR], 0, 
data->tile_child->tile, error);
-        }
+      g_signal_emit (data->self, signals[TILE_ERROR], 0, 
data->tile_child->current_tile, error);
     }
   else
     {
-      add_symbols (data->self, data->tile_child->tile, &data->pos);
-
       shumate_memory_cache_store_tile (data->self->memcache,
-                                       data->tile_child->tile,
+                                       data->tile_child->current_tile,
                                        data->source_id);
     }
 
   recompute_grid (data->self);
 }
 
-static TileChild *
-add_tile (ShumateMapLayer     *self,
-          ShumateTile         *tile,
-          ShumateGridPosition *pos)
+static void
+load_tile (ShumateMapLayer *self,
+           TileChild       *tile_child)
 {
-  TileChild *tile_child = g_new0 (TileChild, 1);
+  g_autoptr(ShumateTile) tile = NULL;
   const char *source_id = shumate_map_source_get_id (self->map_source);
 
-  self->profile_all_tiles_filled_begin = SHUMATE_PROFILE_CURRENT_TIME;
-  self->profile_all_tiles_done_begin = SHUMATE_PROFILE_CURRENT_TIME;
+  guint64 source_rows = shumate_map_source_get_row_count (self->map_source, 
tile_child->pos.zoom);
+  guint64 source_columns = shumate_map_source_get_column_count 
(self->map_source, tile_child->pos.zoom);
+  int tile_size = shumate_map_source_get_tile_size (self->map_source);
+
+  tile = shumate_tile_new_full (positive_mod (tile_child->pos.x, 
source_columns),
+                                positive_mod (tile_child->pos.y, source_rows),
+                                tile_size,
+                                tile_child->pos.zoom);
+  shumate_tile_set_scale_factor (tile, gtk_widget_get_scale_factor (GTK_WIDGET 
(self)));
+
+  g_cancellable_cancel (tile_child->cancellable);
+  g_clear_object (&tile_child->cancellable);
+
+  if (tile_child->loading_tile != NULL)
+    g_signal_handlers_disconnect_by_func (tile_child->loading_tile, 
on_tile_notify_paintable, tile_child);
+  g_signal_connect_swapped (tile, "notify::paintable", 
(GCallback)on_tile_notify_paintable, tile_child);
+
+  g_set_object (&tile_child->loading_tile, tile);
 
   if (shumate_memory_cache_try_fill_tile (self->memcache, tile, source_id))
     {
-      add_symbols (self, tile, pos);
+      g_set_object (&tile_child->current_tile, tile);
+      tile_child->failed = FALSE;
     }
   else
     {
@@ -212,31 +241,42 @@
       data->self = g_object_ref (self);
       data->tile_child = tile_child;
       data->source_id = g_strdup (source_id);
-      data->pos = *pos;
 
-      shumate_tile_set_paintable (tile, NULL);
       shumate_map_source_fill_tile_async (self->map_source, tile, 
tile_child->cancellable, on_tile_filled, data);
     }
+}
+
+static TileChild *
+add_tile (ShumateMapLayer     *self,
+          ShumateGridPosition *pos)
+{
+  TileChild *tile_child = g_new0 (TileChild, 1);
 
-  tile_child->tile = tile;
+  if (self->profile_all_tiles_filled_begin == 0)
+    self->profile_all_tiles_filled_begin = SHUMATE_PROFILE_CURRENT_TIME;
+  if (self->profile_all_tiles_done_begin == 0)
+    self->profile_all_tiles_done_begin = SHUMATE_PROFILE_CURRENT_TIME;
+
+  tile_child->self = self;
+  tile_child->pos = *pos;
 
   g_hash_table_insert (self->tile_children, pos, tile_child);
   gtk_widget_queue_draw (GTK_WIDGET (self));
-  g_signal_connect_object (tile, "notify::state", 
(GCallback)on_tile_notify_state, self, G_CONNECT_SWAPPED);
+
+  load_tile (self, tile_child);
 
   return tile_child;
 }
 
 static void
-remove_tile (ShumateMapLayer     *self,
-             TileChild           *tile_child,
-             ShumateGridPosition *pos)
+remove_tile (ShumateMapLayer *self,
+             TileChild       *tile_child)
 {
   g_cancellable_cancel (tile_child->cancellable);
 
-  shumate_vector_symbol_container_remove_symbols (self->symbols, pos->x, 
pos->y, pos->zoom);
+  shumate_vector_symbol_container_remove_symbols (self->symbols, 
tile_child->pos.x, tile_child->pos.y, tile_child->pos.zoom);
 
-  g_signal_handlers_disconnect_by_func (tile_child->tile, 
on_tile_notify_state, self);
+  g_signal_handlers_disconnect_by_func (tile_child->loading_tile, 
on_tile_notify_paintable, tile_child);
 }
 
 static double
@@ -330,41 +370,61 @@
   GHashTableIter iter;
   gpointer key, value;
 
-  int width = gtk_widget_get_width (GTK_WIDGET (self));
-  int height = gtk_widget_get_height (GTK_WIDGET (self));
-  ShumateViewport *viewport = shumate_layer_get_viewport (SHUMATE_LAYER 
(self));
-  int tile_size = shumate_map_source_get_tile_size (self->map_source);
-  int zoom_level = (int)floor (get_effective_zoom_level (self));
-  double latitude = shumate_location_get_latitude (SHUMATE_LOCATION 
(viewport));
-  double longitude = shumate_location_get_longitude (SHUMATE_LOCATION 
(viewport));
-  double latitude_y = shumate_map_source_get_y (self->map_source, zoom_level, 
latitude);
-  double longitude_x = shumate_map_source_get_x (self->map_source, zoom_level, 
longitude);
-  guint64 source_rows = shumate_map_source_get_row_count (self->map_source, 
zoom_level);
-  guint64 source_columns = shumate_map_source_get_column_count 
(self->map_source, zoom_level);
+  int width, height;
+  ShumateViewport *viewport;
+  int tile_size;
+  int zoom_level;
+  double latitude, longitude;
+  double latitude_y, longitude_x;
+  guint64 source_rows, source_columns;
 
-  double rotation = shumate_viewport_get_rotation (viewport);
+  double rotation;
   int n_tiles = 0;
 
-  int size_x = MAX (
+  int size_x, size_y;
+
+  gint64 tile_initial_column, tile_initial_row, tile_final_column, 
tile_final_row;
+  int required_columns, required_rows;
+
+  gboolean defer;
+
+  gboolean all_filled = TRUE, all_done = TRUE, all_succeeded = TRUE;
+
+  if (self->map_source == NULL)
+    return;
+
+  width = gtk_widget_get_width (GTK_WIDGET (self));
+  height = gtk_widget_get_height (GTK_WIDGET (self));
+  viewport = shumate_layer_get_viewport (SHUMATE_LAYER (self));
+  tile_size = shumate_map_source_get_tile_size (self->map_source);
+  zoom_level = (int)floor (get_effective_zoom_level (self));
+  latitude = shumate_location_get_latitude (SHUMATE_LOCATION (viewport));
+  longitude = shumate_location_get_longitude (SHUMATE_LOCATION (viewport));
+  latitude_y = shumate_map_source_get_y (self->map_source, zoom_level, 
latitude);
+  longitude_x = shumate_map_source_get_x (self->map_source, zoom_level, 
longitude);
+  source_rows = shumate_map_source_get_row_count (self->map_source, 
zoom_level);
+  source_columns = shumate_map_source_get_column_count (self->map_source, 
zoom_level);
+
+  rotation = shumate_viewport_get_rotation (viewport);
+
+  size_x = MAX (
     abs ((int) (cos (rotation) *  width/2.0 - sin (rotation) * height/2.0)),
     abs ((int) (cos (rotation) * -width/2.0 - sin (rotation) * height/2.0))
   );
-  int size_y = MAX (
+  size_y = MAX (
     abs ((int) (sin (rotation) *  width/2.0 + cos (rotation) * height/2.0)),
     abs ((int) (sin (rotation) * -width/2.0 + cos (rotation) * height/2.0))
   );
 
   // This is the (column, row) of the top left tile
-  gint64 tile_initial_column = floor ((longitude_x - size_x) / (double) 
tile_size) - 1;
-  gint64 tile_initial_row = floor ((latitude_y - size_y) / (double) tile_size) 
- 1;
-  gint64 tile_final_column = ceil ((longitude_x + size_x) / (double) 
tile_size) + 1;
-  gint64 tile_final_row = ceil ((latitude_y + size_y) / (double) tile_size) + 
1;
-  int required_columns = tile_final_column - tile_initial_column;
-  int required_rows = tile_final_row - tile_initial_row;
+  tile_initial_column = floor ((longitude_x - size_x) / (double) tile_size) - 
1;
+  tile_initial_row = floor ((latitude_y - size_y) / (double) tile_size) - 1;
+  tile_final_column = ceil ((longitude_x + size_x) / (double) tile_size) + 1;
+  tile_final_row = ceil ((latitude_y + size_y) / (double) tile_size) + 1;
+  required_columns = tile_final_column - tile_initial_column;
+  required_rows = tile_final_row - tile_initial_row;
 
-  gboolean defer = should_defer (self);
-
-  gboolean all_filled = TRUE, all_done = TRUE, all_succeeded = TRUE;
+  defer = should_defer (self);
 
   /* First, remove all the tiles that aren't in bounds, or that are on the
    * wrong zoom level and haven't finished loading */
@@ -381,9 +441,9 @@
           || x >= tile_initial_column + required_columns
           || y + size <= tile_initial_row
           || y >= tile_initial_row + required_rows
-          || (pos->zoom != zoom_level && shumate_tile_get_state 
(tile_child->tile) != SHUMATE_STATE_DONE))
+          || (pos->zoom != zoom_level && shumate_tile_get_state 
(tile_child->loading_tile) != SHUMATE_STATE_DONE))
         {
-          remove_tile (self, tile_child, pos);
+          remove_tile (self, tile_child);
           g_hash_table_iter_remove (&iter);
         }
     }
@@ -398,20 +458,21 @@
 
           n_tiles ++;
 
-          if (!tile_child && !defer)
+          if (!defer)
             {
-              ShumateTile *tile = shumate_tile_new_full (positive_mod (x, 
source_columns), positive_mod (y, source_rows), tile_size, zoom_level);
-              shumate_tile_set_scale_factor (tile, gtk_widget_get_scale_factor 
(GTK_WIDGET (self)));
-              tile_child = add_tile (self, tile, g_steal_pointer (&pos));
+              if (!tile_child)
+                tile_child = add_tile (self, g_steal_pointer (&pos));
+              else if (self->refreshing || (self->retrying_failed && 
tile_child->failed))
+                load_tile (self, tile_child);
             }
 
-          if (tile_child == NULL || shumate_tile_get_paintable 
(tile_child->tile) == NULL)
+          if (tile_child == NULL || shumate_tile_get_paintable 
(tile_child->loading_tile) == NULL)
             all_filled = FALSE;
 
-          if (tile_child == NULL || shumate_tile_get_state (tile_child->tile) 
!= SHUMATE_STATE_DONE)
+          if (tile_child == NULL || shumate_tile_get_state 
(tile_child->loading_tile) != SHUMATE_STATE_DONE)
             all_done = FALSE;
 
-          if (tile_child != NULL && tile_child->error)
+          if (tile_child != NULL && tile_child->failed)
             all_succeeded = FALSE;
         }
     }
@@ -443,7 +504,7 @@
 
           if (pos->zoom != zoom_level)
             {
-              remove_tile (self, tile_child, pos);
+              remove_tile (self, tile_child);
               g_hash_table_iter_remove (&iter);
             }
         }
@@ -452,6 +513,12 @@
   self->last_recompute_y = latitude_y / (double) (tile_size * source_rows);
   self->last_recompute_x = longitude_x / (double) (tile_size * source_columns);
 
+  if (!defer)
+    {
+      self->refreshing = FALSE;
+      self->retrying_failed = FALSE;
+    }
+
   gtk_widget_queue_draw (GTK_WIDGET (self));
 }
 
@@ -510,7 +577,7 @@
   switch (property_id)
     {
     case PROP_MAP_SOURCE:
-      g_set_object (&self->map_source, g_value_get_object (value));
+      shumate_map_layer_set_map_source (self, g_value_get_object (value));
       break;
 
     default:
@@ -554,6 +621,7 @@
   g_clear_pointer (&self->tile_children, g_hash_table_unref);
   g_clear_object (&self->map_source);
   g_clear_object (&self->memcache);
+  g_clear_object (&self->source_signal_group);
 
   G_OBJECT_CLASS (shumate_map_layer_parent_class)->dispose (object);
 }
@@ -642,28 +710,41 @@
 static void
 shumate_map_layer_snapshot (GtkWidget *widget, GtkSnapshot *snapshot)
 {
+  GHashTableIter iter;
+  gpointer key, value;
+
   ShumateMapLayer *self = SHUMATE_MAP_LAYER (widget);
   ShumateViewport *viewport = shumate_layer_get_viewport (SHUMATE_LAYER 
(self));
-  double zoom_level = get_effective_zoom_level (self);
-  int width = gtk_widget_get_width (GTK_WIDGET (self));
-  int height = gtk_widget_get_height (GTK_WIDGET (self));
-  double rotation = shumate_viewport_get_rotation (viewport);
-  double latitude = shumate_location_get_latitude (SHUMATE_LOCATION 
(viewport));
-  double longitude = shumate_location_get_longitude (SHUMATE_LOCATION 
(viewport));
-  double latitude_y = shumate_map_source_get_y (self->map_source, zoom_level, 
latitude);
-  double longitude_x = shumate_map_source_get_x (self->map_source, zoom_level, 
longitude);
-  int tile_size = shumate_map_source_get_tile_size (self->map_source);
-  double tile_size_for_zoom = shumate_map_source_get_tile_size_at_zoom 
(self->map_source, zoom_level);
-  double map_width = shumate_map_source_get_column_count (self->map_source, 
zoom_level)
-    * tile_size_for_zoom;
-  double map_height = shumate_map_source_get_row_count (self->map_source, 
zoom_level)
-    * tile_size_for_zoom;
-  gboolean show_tile_bounds = shumate_inspector_settings_get_show_tile_bounds 
(shumate_inspector_settings_get_default ());
-  double scale_factor = gtk_widget_get_scale_factor (widget);
+  double zoom_level;
+  int width, height;
+  double rotation;
+  double latitude, longitude, latitude_y, longitude_x;
+  int tile_size;
+  double tile_size_for_zoom;
+  double map_width, map_height;
+  gboolean show_tile_bounds;
+  double scale_factor;
 
-  GHashTableIter iter;
-  gpointer key;
-  gpointer value;
+  if (self->map_source == NULL)
+    {
+      gtk_widget_snapshot_child (widget, GTK_WIDGET (self->symbols), snapshot);
+      return;
+    }
+
+  zoom_level = get_effective_zoom_level (self);
+  width = gtk_widget_get_width (GTK_WIDGET (self));
+  height = gtk_widget_get_height (GTK_WIDGET (self));
+  rotation = shumate_viewport_get_rotation (viewport);
+  latitude = shumate_location_get_latitude (SHUMATE_LOCATION (viewport));
+  longitude = shumate_location_get_longitude (SHUMATE_LOCATION (viewport));
+  latitude_y = shumate_map_source_get_y (self->map_source, zoom_level, 
latitude);
+  longitude_x = shumate_map_source_get_x (self->map_source, zoom_level, 
longitude);
+  tile_size = shumate_map_source_get_tile_size (self->map_source);
+  tile_size_for_zoom = shumate_map_source_get_tile_size_at_zoom 
(self->map_source, zoom_level);
+  map_width = shumate_map_source_get_column_count (self->map_source, 
zoom_level) * tile_size_for_zoom;
+  map_height = shumate_map_source_get_row_count (self->map_source, zoom_level) 
* tile_size_for_zoom;
+  show_tile_bounds = shumate_inspector_settings_get_show_tile_bounds 
(shumate_inspector_settings_get_default ());
+  scale_factor = gtk_widget_get_scale_factor (widget);
 
   /* Because Earth is round [citation needed], cylindrical projections like
    * Mercator wrap around at the antimeridian. Moving across the antimeridian
@@ -698,13 +779,13 @@
     {
       ShumateGridPosition *pos = key;
       TileChild *tile_child = value;
-      GdkPaintable *paintable = shumate_tile_get_paintable (tile_child->tile);
+      GdkPaintable *paintable = NULL;
       double size = tile_size * pow (2, zoom_level - pos->zoom);
       double x = -(longitude_x - width/2.0) + size * pos->x;
       double y = -(latitude_y - height/2.0) + size * pos->y;
 
-      if (paintable == NULL)
-        continue;
+      if (tile_child->current_tile != NULL)
+        paintable = shumate_tile_get_paintable (tile_child->current_tile);
 
       gtk_snapshot_save (snapshot);
 
@@ -713,18 +794,21 @@
         round_px (y, scale_factor)
       ));
 
-      gdk_paintable_snapshot (
-        paintable,
-        snapshot,
-        round_px (x + size, scale_factor) - round_px (x, scale_factor),
-        round_px (y + size, scale_factor) - round_px (y, scale_factor)
-      );
+      if (paintable != NULL)
+        {
+          gdk_paintable_snapshot (
+            paintable,
+            snapshot,
+            round_px (x + size, scale_factor) - round_px (x, scale_factor),
+            round_px (y + size, scale_factor) - round_px (y, scale_factor)
+          );
+        }
 
       if (show_tile_bounds)
         {
           g_autofree char *text = NULL;
           g_autoptr(PangoLayout) layout = NULL;
-          int x = shumate_tile_get_x (tile_child->tile);
+          int x = shumate_tile_get_x (tile_child->loading_tile);
           GdkRGBA color = {1, 0, 1, 1};
 
           if (x == pos->x)
@@ -761,7 +845,7 @@
   g_hash_table_iter_init (&iter, self->tile_children);
   while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&tile_child))
     {
-      if (shumate_tile_get_state (tile_child->tile) != SHUMATE_STATE_DONE)
+      if (shumate_tile_get_state (tile_child->loading_tile) != 
SHUMATE_STATE_DONE)
         n_loading ++;
     }
 
@@ -797,12 +881,17 @@
 
   layer_class->get_debug_text = shumate_map_layer_get_debug_text;
 
+  /**
+   * ShumateMapLayer:map-source:
+   *
+   * The source of the tiles this map layer displays.
+   */
   obj_properties[PROP_MAP_SOURCE] =
     g_param_spec_object ("map-source",
                          "Map Source",
                          "The Map Source",
                          SHUMATE_TYPE_MAP_SOURCE,
-                         G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | 
G_PARAM_STATIC_STRINGS);
+                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | 
G_PARAM_EXPLICIT_NOTIFY);
 
   g_object_class_install_properties (object_class,
                                      N_PROPERTIES,
@@ -871,6 +960,13 @@
 }
 
 static void
+on_source_modified (ShumateMapLayer *self,
+                    G_GNUC_UNUSED ShumateMapSource *source)
+{
+  shumate_map_layer_refresh (self);
+}
+
+static void
 shumate_map_layer_init (ShumateMapLayer *self)
 {
   self->tile_children = g_hash_table_new_full (
@@ -880,6 +976,10 @@
     (GDestroyNotify)tile_child_free
   );
   self->memcache = shumate_memory_cache_new_full (100);
+
+  self->source_signal_group = g_signal_group_new (SHUMATE_TYPE_MAP_SOURCE);
+  g_signal_group_connect_object (self->source_signal_group, "modified", 
G_CALLBACK (on_source_modified), self, G_CONNECT_SWAPPED);
+  g_object_bind_property (self, "map-source", self->source_signal_group, 
"target", G_BINDING_SYNC_CREATE);
 }
 
 ShumateMapLayer *
@@ -892,3 +992,111 @@
                        NULL);
 }
 
+
+/**
+ * shumate_map_layer_get_map_source:
+ * @self: a [class@MapLayer]
+ *
+ * Gets the [class@MapSource] providing tiles for this map layer.
+ *
+ * Returns: (transfer none): the [class@MapSource]
+ * Since: 1.7
+ */
+ShumateMapSource *
+shumate_map_layer_get_map_source (ShumateMapLayer *self)
+{
+  g_return_val_if_fail (SHUMATE_IS_MAP_LAYER (self), NULL);
+  return self->map_source;
+}
+
+/**
+ * shumate_map_layer_set_map_source:
+ * @self: a [class@MapLayer]
+ * @map_source: a [class@MapSource]
+ *
+ * Sets the [class@MapSource] that provides tiles for this map layer to 
display.
+ *
+ * Setting a new map source will refresh all tiles from the new source.
+ *
+ * Since: 1.7
+ */
+void
+shumate_map_layer_set_map_source (ShumateMapLayer *self,
+                                  ShumateMapSource *map_source)
+{
+  gboolean tile_size_differs = TRUE;
+
+  g_return_if_fail (SHUMATE_IS_MAP_LAYER (self));
+  g_return_if_fail (map_source == NULL || SHUMATE_IS_MAP_SOURCE (map_source));
+
+  if (self->map_source == map_source)
+    return;
+
+  if (self->map_source != NULL && map_source != NULL)
+    tile_size_differs = shumate_map_source_get_tile_size (self->map_source) != 
shumate_map_source_get_tile_size (map_source);
+
+  g_set_object (&self->map_source, map_source);
+
+  shumate_vector_symbol_container_set_map_source (self->symbols, map_source);
+
+  if (tile_size_differs)
+    {
+      /* If the tile size differs, remove all existing tiles, since they are 
invalid on the new grid. If the sizes are
+         the same, we can keep the existing tiles until new ones are loaded 
over them, which eliminates the flash of
+         background color while new tiles load. */
+
+      GHashTableIter iter;
+      gpointer key, value;
+
+      g_hash_table_iter_init (&iter, self->tile_children);
+      while (g_hash_table_iter_next (&iter, &key, &value))
+        {
+          remove_tile (self, (TileChild *) value);
+          g_hash_table_iter_remove (&iter);
+        }
+    }
+
+  shumate_map_layer_refresh (self);
+
+  g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_MAP_SOURCE]);
+}
+
+/**
+ * shumate_map_layer_refresh:
+ * @self: a [class@MapLayer]
+ *
+ * Refreshes all tiles from the current map source.
+ *
+ * Since: 1.7
+ */
+void
+shumate_map_layer_refresh (ShumateMapLayer *self)
+{
+  g_return_if_fail (SHUMATE_IS_MAP_LAYER (self));
+
+  self->refreshing = TRUE;
+
+  shumate_memory_cache_clean (self->memcache);
+  queue_recompute_grid_in_idle (self);
+}
+
+/**
+ * shumate_map_layer_retry_failed:
+ * @self: a [class@MapLayer]
+ *
+ * Attempts to reload any tiles in an error state.
+ *
+ * This can be used to recover from temporary errors. For example, you could 
use [[email protected]] to listen
+ * for changes in network connectivity, and call this function when the 
network becomes available again.
+ *
+ * Since: 1.7
+ */
+void
+shumate_map_layer_retry_failed (ShumateMapLayer *self)
+{
+  g_return_if_fail (SHUMATE_IS_MAP_LAYER (self));
+
+  self->retrying_failed = TRUE;
+
+  queue_recompute_grid_in_idle (self);
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/shumate/shumate-map-layer.h 
new/libshumate-1.7.0/shumate/shumate-map-layer.h
--- old/libshumate-1.6.3/shumate/shumate-map-layer.h    2026-08-01 
13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/shumate/shumate-map-layer.h    2026-09-11 
09:50:19.000000000 +0200
@@ -35,6 +35,13 @@
 ShumateMapLayer *shumate_map_layer_new (ShumateMapSource *map_source,
                                         ShumateViewport  *viewport);
 
+ShumateMapSource *shumate_map_layer_get_map_source (ShumateMapLayer *self);
+void shumate_map_layer_set_map_source (ShumateMapLayer *self,
+                                       ShumateMapSource *map_source);
+
+void shumate_map_layer_refresh (ShumateMapLayer *self);
+void shumate_map_layer_retry_failed (ShumateMapLayer *self);
+
 G_END_DECLS
 
 #endif /* __SHUMATE_MAP_LAYER_H__ */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/shumate/shumate-map-source.c 
new/libshumate-1.7.0/shumate/shumate-map-source.c
--- old/libshumate-1.6.3/shumate/shumate-map-source.c   2026-08-01 
13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/shumate/shumate-map-source.c   2026-09-11 
09:50:19.000000000 +0200
@@ -62,6 +62,14 @@
 
 static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
 
+enum
+{
+  MODIFIED,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
 static void
 shumate_map_source_finalize (GObject *object)
 {
@@ -290,6 +298,26 @@
   g_object_class_install_properties (object_class,
                                      N_PROPERTIES,
                                      obj_properties);
+
+  /**
+   * ShumateMapSource::modified:
+   * @self: the [class@MapSource] emitting the signal
+   *
+   * Emitted when the source is modified in such a way that tiles previously 
filled by it should be refreshed.
+   *
+   * It is the [class@MapSource] implementation's responsibility to emit this 
signal when appropriate, such as when a
+   * data source or rendering parameter changes.
+   *
+   * Since: 1.7
+   */
+  signals[MODIFIED] =
+    g_signal_new ("modified",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE,
+                  0);
 }
 
 static double
@@ -805,7 +833,7 @@
   /* FIXME: support other projections */
   dy = 0.5 - y / map_size (map_source, zoom_level);
   latitude = 90.0 - 360.0 / G_PI * atan (exp (-dy * 2.0 * G_PI));
-  
+
   return CLAMP (latitude, SHUMATE_MIN_LATITUDE, SHUMATE_MAX_LATITUDE);
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/shumate/shumate-memory-cache.c 
new/libshumate-1.7.0/shumate/shumate-memory-cache.c
--- old/libshumate-1.6.3/shumate/shumate-memory-cache.c 2026-08-01 
13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/shumate/shumate-memory-cache.c 2026-09-11 
09:50:19.000000000 +0200
@@ -255,8 +255,8 @@
 
   move_queue_member_to_head (self->queue, link);
 
-  shumate_tile_set_paintable (tile, member->paintable);
   shumate_tile_set_symbols (tile, member->symbols);
+  shumate_tile_set_paintable (tile, member->paintable);
   shumate_tile_set_fade_in (tile, FALSE);
   shumate_tile_set_state (tile, SHUMATE_STATE_DONE);
   return TRUE;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/shumate/shumate-simple-map.c 
new/libshumate-1.7.0/shumate/shumate-simple-map.c
--- old/libshumate-1.6.3/shumate/shumate-simple-map.c   2026-08-01 
13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/shumate/shumate-simple-map.c   2026-09-11 
09:50:19.000000000 +0200
@@ -342,7 +342,15 @@
 static void
 shumate_simple_map_init (ShumateSimpleMap *self)
 {
+  ShumateViewport *viewport;
+
   gtk_widget_init_template (GTK_WIDGET (self));
+
+  viewport = shumate_simple_map_get_viewport (self);
+
+  self->map_layer = shumate_map_layer_new (NULL, viewport);
+  shumate_map_add_layer (self->map, SHUMATE_LAYER (self->map_layer));
+  g_signal_connect_object (self->map_layer, "symbol-clicked", 
(GCallback)on_symbol_clicked, self, G_CONNECT_SWAPPED);
 }
 
 
@@ -391,7 +399,6 @@
                                    ShumateMapSource *map_source)
 {
   ShumateViewport *viewport;
-  ShumateMapLayer *map_layer;
 
   g_return_if_fail (SHUMATE_IS_SIMPLE_MAP (self));
   g_return_if_fail (map_source == NULL || SHUMATE_IS_MAP_SOURCE (map_source));
@@ -409,17 +416,7 @@
 
   shumate_viewport_set_reference_map_source (viewport, map_source);
   shumate_map_set_map_source (self->map, map_source);
-
-  map_layer = shumate_map_layer_new (map_source, viewport);
-  shumate_map_insert_layer_behind (self->map, SHUMATE_LAYER (map_layer), 
SHUMATE_LAYER (self->map_layer));
-  g_signal_connect_object (map_layer, "symbol-clicked", 
(GCallback)on_symbol_clicked, self, G_CONNECT_SWAPPED);
-
-  if (self->map_layer) {
-    g_signal_handlers_disconnect_by_func (self->map_layer, on_symbol_clicked, 
self);
-    shumate_map_remove_layer (self->map, SHUMATE_LAYER (self->map_layer));
-  }
-  self->map_layer = map_layer;
-
+  shumate_map_layer_set_map_source (self->map_layer, map_source);
   shumate_license_append_map_source (self->license, map_source);
 
   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MAP_SOURCE]);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/shumate/shumate-vector-renderer.c 
new/libshumate-1.7.0/shumate/shumate-vector-renderer.c
--- old/libshumate-1.6.3/shumate/shumate-vector-renderer.c      2026-08-01 
13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/shumate/shumate-vector-renderer.c      2026-09-11 
09:50:19.000000000 +0200
@@ -666,7 +666,10 @@
   g_return_if_fail (SHUMATE_IS_DATA_SOURCE (data_source));
 
   if (g_strcmp0 (name, self->source_name) == 0)
-    g_set_object (&self->data_source, data_source);
+    {
+      if (g_set_object (&self->data_source, data_source))
+        g_signal_emit_by_name (self, "modified");
+    }
 }
 
 
@@ -692,6 +695,7 @@
 {
   g_autoptr(GMutexLocker) locker = NULL;
   ShumateVectorValue *val;
+  ShumateVectorValue *current_value;
 
   g_return_if_fail (SHUMATE_IS_VECTOR_RENDERER (self));
   g_return_if_fail (key != NULL);
@@ -701,9 +705,15 @@
   if (self->global_state == NULL)
     self->global_state = g_hash_table_new_full (g_str_hash, g_str_equal, 
g_free, (GDestroyNotify)shumate_vector_value_free);
 
+  current_value = g_hash_table_lookup (self->global_state, key);
+  if (shumate_vector_value_equal (current_value, value))
+    return;
+
   val = g_new0 (ShumateVectorValue, 1);
   shumate_vector_value_copy (value, val);
   g_hash_table_insert (self->global_state, g_strdup (key), val);
+
+  g_signal_emit_by_name (self, "modified");
 }
 
 
@@ -764,6 +774,7 @@
 {
   g_autoptr(GMutexLocker) locker = NULL;
   ShumateVectorValue *default_value;
+  ShumateVectorValue *current_value;
 
   g_return_if_fail (SHUMATE_IS_VECTOR_RENDERER (self));
   g_return_if_fail (key != NULL);
@@ -775,15 +786,27 @@
 
   if (self->default_global_state == NULL)
     {
-      g_hash_table_remove (self->global_state, key);
+      if (g_hash_table_remove (self->global_state, key))
+        g_signal_emit_by_name (self, "modified");
       return;
     }
 
   default_value = g_hash_table_lookup (self->default_global_state, key);
   if (default_value != NULL)
-    g_hash_table_insert (self->global_state, g_strdup (key), 
shumate_vector_value_dup (default_value));
+    {
+      current_value = g_hash_table_lookup (self->global_state, key);
+      if (shumate_vector_value_equal (current_value, default_value))
+        return;
+
+      g_hash_table_insert (self->global_state, g_strdup (key), 
shumate_vector_value_dup (default_value));
+
+      g_signal_emit_by_name (self, "modified");
+    }
   else
-    g_hash_table_remove (self->global_state, key);
+    {
+      if (g_hash_table_remove (self->global_state, key))
+        g_signal_emit_by_name (self, "modified");
+    }
 }
 
 
@@ -1054,8 +1077,10 @@
 
   if (!g_cancellable_is_cancelled (job->cancellable))
     {
-      shumate_tile_set_paintable (data->tile, job->paintable);
+      /* Note: The order of these is important, because ShumateMapLayer relies 
on notify::paintable to refresh everything
+         (since symbols isn't a property) */
       shumate_tile_set_symbols (data->tile, job->symbols);
+      shumate_tile_set_paintable (data->tile, job->paintable);
     }
 
   if (data->current_job == job)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/shumate/shumate-vector-value.c 
new/libshumate-1.7.0/shumate/shumate-vector-value.c
--- old/libshumate-1.6.3/shumate/shumate-vector-value.c 2026-08-01 
13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/shumate/shumate-vector-value.c 2026-09-11 
09:50:19.000000000 +0200
@@ -54,17 +54,23 @@
  * shumate_vector_value_new_from_value:
  * @value: a [[email protected]]
  *
- * Creates a new [struct@VectorValue] from a [[email protected]].
+ * Creates a new [struct@VectorValue] from a [[email protected]]. Returns
+ * `NULL` if the GValue does not contain a compatible value.
  *
- * Returns: (transfer full): a new [struct@VectorValue] with the value from 
@value
+ * Returns: (transfer full) (nullable): a new [struct@VectorValue] with the 
value from @value
  * Since: 1.6
  */
 ShumateVectorValue *
 shumate_vector_value_new_from_value (const GValue *value)
 {
   ShumateVectorValue *self = g_new0 (ShumateVectorValue, 1);
-  shumate_vector_value_set_from_g_value (self, value);
-  return self;
+  if (shumate_vector_value_set_from_g_value (self, value))
+      return self;
+  else
+    {
+      g_free (self);
+      return NULL;
+    }
 }
 
 /**
@@ -712,17 +718,26 @@
 
 /**
  * shumate_vector_value_equal:
- * @a: a [struct@VectorValue]
- * @b: a [struct@VectorValue]
+ * @a: (nullable): a [struct@VectorValue]
+ * @b: (nullable): a [struct@VectorValue]
  *
  * Compares two [struct@VectorValue]s for equality.
  *
+ * Either argument may be `NULL`, which is treated as equal to a 
[struct@VectorValue] of type [[email protected]].
+ *
  * Returns: %TRUE if @a and @b are equal, %FALSE otherwise
  * Since: 1.6
  */
 gboolean
 shumate_vector_value_equal (ShumateVectorValue *a, ShumateVectorValue *b)
 {
+  if (a == NULL && b == NULL)
+    return TRUE;
+  else if (a == NULL)
+    return b->type == SHUMATE_VECTOR_VALUE_TYPE_NULL;
+  else if (b == NULL)
+    return a->type == SHUMATE_VECTOR_VALUE_TYPE_NULL;
+
   if (a->type != b->type)
     return FALSE;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/shumate/shumate-vector-value.h 
new/libshumate-1.7.0/shumate/shumate-vector-value.h
--- old/libshumate-1.6.3/shumate/shumate-vector-value.h 2026-08-01 
13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/shumate/shumate-vector-value.h 2026-09-11 
09:50:19.000000000 +0200
@@ -54,7 +54,7 @@
 
 #define SHUMATE_TYPE_VECTOR_VALUE shumate_vector_value_get_type()
 
-GType shumate_vector_value_get_type (void) G_GNUC_CONST;
+GType shumate_vector_value_get_type (void);
 
 ShumateVectorValue *shumate_vector_value_new (void);
 ShumateVectorValue *shumate_vector_value_new_from_value (const GValue *value);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libshumate-1.6.3/shumate/vector/shumate-vector-symbol-container-private.h 
new/libshumate-1.7.0/shumate/vector/shumate-vector-symbol-container-private.h
--- 
old/libshumate-1.6.3/shumate/vector/shumate-vector-symbol-container-private.h   
    2026-08-01 13:10:51.000000000 +0200
+++ 
new/libshumate-1.7.0/shumate/vector/shumate-vector-symbol-container-private.h   
    2026-09-11 09:50:19.000000000 +0200
@@ -42,6 +42,8 @@
                                                      int                       
    zoom);
 
 ShumateMapSource *shumate_vector_symbol_container_get_map_source 
(ShumateVectorSymbolContainer *self);
+void shumate_vector_symbol_container_set_map_source 
(ShumateVectorSymbolContainer *self,
+                                                     ShumateMapSource          
   *map_source);
 
 ShumateVectorCollision *shumate_vector_symbol_container_get_collision 
(ShumateVectorSymbolContainer *self);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libshumate-1.6.3/shumate/vector/shumate-vector-symbol-container.c 
new/libshumate-1.7.0/shumate/vector/shumate-vector-symbol-container.c
--- old/libshumate-1.6.3/shumate/vector/shumate-vector-symbol-container.c       
2026-08-01 13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/shumate/vector/shumate-vector-symbol-container.c       
2026-09-11 09:50:19.000000000 +0200
@@ -239,8 +239,7 @@
   switch (property_id)
     {
     case PROP_MAP_SOURCE:
-      g_set_object (&self->map_source, g_value_get_object (value));
-      gtk_widget_queue_allocate (GTK_WIDGET (self));
+      shumate_vector_symbol_container_set_map_source (self, g_value_get_object 
(value));
       break;
 
     default:
@@ -305,18 +304,29 @@
 
   ShumateVectorSymbolContainer *self = SHUMATE_VECTOR_SYMBOL_CONTAINER 
(widget);
   GtkAllocation alloc;
-  double tile_size = shumate_map_source_get_tile_size (self->map_source);
-  ShumateViewport *viewport = shumate_layer_get_viewport (SHUMATE_LAYER 
(self));
-  double zoom_level = get_effective_zoom_level (self->map_source, viewport);
-  double rotation = shumate_viewport_get_rotation (viewport);
-  double center_x = shumate_map_source_get_x (self->map_source, zoom_level, 
shumate_location_get_longitude (SHUMATE_LOCATION (viewport)));
-  double center_y = shumate_map_source_get_y (self->map_source, zoom_level, 
shumate_location_get_latitude (SHUMATE_LOCATION (viewport)));
-
-  gboolean recalc = self->labels_changed
-                    || self->last_zoom != zoom_level
-                    || self->last_rotation != rotation
-                    || self->last_width != width
-                    || self->last_height != height;
+  double tile_size;
+  ShumateViewport *viewport;
+  double zoom_level;
+  double rotation;
+  double center_x, center_y;
+
+  gboolean recalc;
+
+  if (self->map_source == NULL)
+    return;
+
+  tile_size = shumate_map_source_get_tile_size (self->map_source);
+  viewport = shumate_layer_get_viewport (SHUMATE_LAYER (self));
+  zoom_level = get_effective_zoom_level (self->map_source, viewport);
+  rotation = shumate_viewport_get_rotation (viewport);
+  center_x = shumate_map_source_get_x (self->map_source, zoom_level, 
shumate_location_get_longitude (SHUMATE_LOCATION (viewport)));
+  center_y = shumate_map_source_get_y (self->map_source, zoom_level, 
shumate_location_get_latitude (SHUMATE_LOCATION (viewport)));
+
+  recalc = self->labels_changed
+            || self->last_zoom != zoom_level
+            || self->last_rotation != rotation
+            || self->last_width != width
+            || self->last_height != height;
 
   if (recalc)
     {
@@ -593,6 +603,18 @@
 }
 
 
+void
+shumate_vector_symbol_container_set_map_source (ShumateVectorSymbolContainer 
*self,
+                                                ShumateMapSource             
*map_source)
+{
+  g_return_if_fail (SHUMATE_IS_VECTOR_SYMBOL_CONTAINER (self));
+  g_return_if_fail (map_source == NULL || SHUMATE_IS_MAP_SOURCE (map_source));
+
+  if (g_set_object (&self->map_source, map_source))
+    gtk_widget_queue_allocate (GTK_WIDGET (self));
+}
+
+
 ShumateVectorCollision *
 shumate_vector_symbol_container_get_collision (ShumateVectorSymbolContainer 
*self)
 {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libshumate-1.6.3/shumate/vector/shumate-vector-symbol-info-private.h 
new/libshumate-1.7.0/shumate/vector/shumate-vector-symbol-info-private.h
--- old/libshumate-1.6.3/shumate/vector/shumate-vector-symbol-info-private.h    
2026-08-01 13:10:51.000000000 +0200
+++ new/libshumate-1.7.0/shumate/vector/shumate-vector-symbol-info-private.h    
2026-09-11 09:50:19.000000000 +0200
@@ -138,7 +138,7 @@
                                                  ShumateVectorLineString 
*linestring,
                                                  float                    
position);
 
-GType                        shumate_vector_symbol_info_get_type (void) 
G_GNUC_CONST;
+GType                        shumate_vector_symbol_info_get_type (void);
 ShumateVectorSymbolInfo     *shumate_vector_symbol_info_ref      
(ShumateVectorSymbolInfo *self);
 void                         shumate_vector_symbol_info_unref    
(ShumateVectorSymbolInfo *self);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libshumate-1.6.3/tests/vector-value.c 
new/libshumate-1.7.0/tests/vector-value.c
--- old/libshumate-1.6.3/tests/vector-value.c   2026-08-01 13:10:51.000000000 
+0200
+++ new/libshumate-1.7.0/tests/vector-value.c   2026-09-11 09:50:19.000000000 
+0200
@@ -94,6 +94,34 @@
 
 
 static void
+test_vector_new_from_value (void)
+{
+  ShumateVectorValue *value1;
+  ShumateVectorValue *value2;
+  double number;
+  g_auto(GValue) gvalue1 = G_VALUE_INIT;
+  g_auto(GValue) gvalue2 = G_VALUE_INIT;
+
+  g_value_init (&gvalue1, G_TYPE_DOUBLE);
+  g_value_set_double (&gvalue1, 3.1415);
+
+  // G_TYPE_OBJECT is not supported by new_from_value
+  g_value_init (&gvalue2, G_TYPE_OBJECT);
+  g_value_set_object (&gvalue2, NULL);
+
+  value1 = shumate_vector_value_new_from_value (&gvalue1);
+  value2 = shumate_vector_value_new_from_value (&gvalue2);
+
+  g_assert_true (shumate_vector_value_get_number (value1, &number));
+  g_assert_cmpfloat (3.1415, ==, number);
+
+  g_assert_null (value2);
+
+  shumate_vector_value_free (value1);
+}
+
+
+static void
 test_vector_value_equal (void)
 {
   g_auto(ShumateVectorValue) value1 = SHUMATE_VECTOR_VALUE_INIT;
@@ -148,6 +176,17 @@
   shumate_vector_value_unset (&value1);
   shumate_vector_value_set_number (&value2, 0.0);
   g_assert_false (shumate_vector_value_equal (&value1, &value2));
+
+  shumate_vector_value_unset (&value1);
+  shumate_vector_value_unset (&value2);
+  g_assert_true (shumate_vector_value_equal (NULL, NULL));
+  g_assert_true (shumate_vector_value_equal (&value1, NULL));
+  g_assert_true (shumate_vector_value_equal (NULL, &value2));
+  g_assert_true (shumate_vector_value_equal (&value1, &value2));
+  shumate_vector_value_set_string (&value1, "Hello, world!");
+  g_assert_false (shumate_vector_value_equal (&value1, NULL));
+  g_assert_false (shumate_vector_value_equal (NULL, &value1));
+  g_assert_false (shumate_vector_value_equal (&value1, &value2));
 }
 
 
@@ -181,6 +220,7 @@
   g_test_add_func ("/vector/value/get-color", test_vector_value_get_color);
   g_test_add_func ("/vector/value/equal", test_vector_value_equal);
   g_test_add_func ("/vector/value/copy", test_vector_value_copy);
+  g_test_add_func ("/vector/value/new-from-value", test_vector_new_from_value);
 
   return g_test_run ();
 }

++++++ libshumate.obsinfo ++++++
--- /var/tmp/diff_new_pack.iXIyI6/_old  2026-10-30 18:10:14.776108158 +0100
+++ /var/tmp/diff_new_pack.iXIyI6/_new  2026-10-30 18:10:14.783108451 +0100
@@ -1,5 +1,5 @@
 name: libshumate
-version: 1.6.3
-mtime: 1785582651
-commit: 4222507d5a2aff1f3357385ef7c44e14e7a8f45f
+version: 1.7.0
+mtime: 1789113019
+commit: e345c805a0dfc46cdbff46cac287b0b60a386f36
 

Reply via email to