Repository: matplotlib/matplotlib

Issue:
[Bug]: set_url without effect for instances of Line2D with linestyle 'none'
### Bug summary

Bug summary
Using SVG-backend, set_url does nothing for for instances of Line2D with linestyle='none', whereas it works for other objects.
Related to closed issue: https://github.com/matplotlib/matplotlib/issues/17336

### Code for reproduction

```Python
from matplotlib import pyplot as plt

f = plt.figure()
s = plt.scatter([1, 2, 3], [4, 5, 6])
s.set_urls(['http://www.bbc.co.uk/news', 'http://www.google.com', None])
p = plt.plot([1, 3], [6, 5], linestyle='none', color='green', marker='x')
p[0].set_url('http://www.duckduckgo.com')

print(s.get_urls())
print(p[0].get_url())

import io
svg = io.StringIO()
f.savefig(svg, format='svg')
f.savefig('test.svg', format='svg')

assert "http://www.google.com" in svg.getvalue()
assert "http://www.duckduckgo.com" in svg.getvalue()
```


### Actual outcome

Exception has occurred: AssertionError
exception: no description
  File "/home/peunting/from matplotlib import pyplot as plt.py", line 18, in <module>
    assert "http://www.duckduckgo.com" in svg.getvalue()
AssertionError: 

### Expected outcome

Both http://www.google.com and http://www.duckduckgo.com should be present in the svg. When the svg is opened in a browser, the plot markers of the line plot should be clickable as well.

### Additional information

_No response_

### Operating system

Rocky

### Matplotlib Version

3.8.2

### Matplotlib Backend

QtAgg

### Python version

3.10.12

### Jupyter version

_No response_

### Installation

pip


Discussion:
I think in lines.py, line 812, the urls for the markers should be set:
https://github.com/matplotlib/matplotlib/blob/ed17e00da9b8119c0a01290ebcace64bc0120b84/lib/matplotlib/lines.py#L809-L814
@QuLogic : Do you have an idea why it is not working?
Yes, that code means the backend receives the URL, but doesn't necessarily mean the backend does anything with it. For SVG, I don't see any handling of URLs for plain markers:
https://github.com/matplotlib/matplotlib/blob/e6f2b0c72be421f3b324a55fca1dbc367405cce5/lib/matplotlib/backends/backend_svg.py#L692-L729



Patch:
diff --git a/lib/matplotlib/backends/backend_svg.py b/lib/matplotlib/backends/backend_svg.py
index 84e4f96ad4a7..623e1eb9ad82 100644
--- a/lib/matplotlib/backends/backend_svg.py
+++ b/lib/matplotlib/backends/backend_svg.py
@@ -715,6 +715,8 @@ def draw_markers(
             self._markers[dictkey] = oid
 
         writer.start('g', **self._get_clip_attrs(gc))
+        if gc.get_url() is not None:
+            self.writer.start('a', {'xlink:href': gc.get_url()})
         trans_and_flip = self._make_flip_transform(trans)
         attrib = {'xlink:href': f'#{oid}'}
         clip = (0, 0, self.width*72, self.height*72)
@@ -726,6 +728,8 @@ def draw_markers(
                 attrib['y'] = _short_float_fmt(y)
                 attrib['style'] = self._get_style(gc, rgbFace)
                 writer.element('use', attrib=attrib)
+        if gc.get_url() is not None:
+            self.writer.end('a')
         writer.end('g')
 
     def draw_path_collection(self, gc, master_transform, paths, all_transforms,


Test patch:
diff --git a/lib/matplotlib/tests/test_backend_svg.py b/lib/matplotlib/tests/test_backend_svg.py
index 689495eb31ac..b850a9ab6ff5 100644
--- a/lib/matplotlib/tests/test_backend_svg.py
+++ b/lib/matplotlib/tests/test_backend_svg.py
@@ -343,13 +343,17 @@ def test_url():
     s.set_urls(['https://example.com/foo', 'https://example.com/bar', None])
 
     # Line2D
-    p, = plt.plot([1, 3], [6, 5])
+    p, = plt.plot([2, 3, 4], [4, 5, 6])
     p.set_url('https://example.com/baz')
 
+    # Line2D markers-only
+    p, = plt.plot([3, 4, 5], [4, 5, 6], linestyle='none', marker='x')
+    p.set_url('https://example.com/quux')
+
     b = BytesIO()
     fig.savefig(b, format='svg')
     b = b.getvalue()
-    for v in [b'foo', b'bar', b'baz']:
+    for v in [b'foo', b'bar', b'baz', b'quux']:
         assert b'https://example.com/' + v in b