Repository: altair-viz/altair
Pull request #3252 (2023-11-05 11:19:58)

Support converting a Chart to a Vega editor URL
Closes https://github.com/altair-viz/altair/issues/3188

This PR adds a `Chart.to_url` method that uses VlConvert to generate a Vega editor URL that opens the chart's specification in the online Vega editor.

```python
import altair as alt
from vega_datasets import data

chart = alt.Chart(data.cars.url).mark_point().encode(
    x='Horsepower:Q',
    y='Miles_per_Gallon:Q',
    color='Origin:N'
)

print(chart.to_url())
```
https://vega.github.io/editor/#/url/vega-lite/N4Igxg9gdgZglgcxALlANzgUwO4tJKAFzigFcJSBnAdTgBNCALFAZgAY2AacaYsiygAlMiRoVYcAvpO50AhoTl4QpAE4AbFCDGEADpWQB6Q2DpQAdACtKdTOrhpV5qJkKGougLaG0mBHIBaeUVKV0oAATQARnMAJgBOczZDYLkTOVVKK0poEBkQTwyAa2VCAE9dTC1dCBJxfMwoSDoSJFQedQhVZXg7Oi0AeVVEEhBucsqtKAhPEjlNfIAPHqx1fuQQQS7QmuxMbvGKqo2AR1I5IjhFYl887jKVvq0AWTh1TEoAfUrVT4BxeadKBjEATY4gM4XYjXBxVaTcAAklDAjEwhS0On0Rh8fjk5gQV0YpAARuY4BBDMjUYUcf4AvZCJgfABWcxRVkxay5SRAA

cc @NickCrews 



Patch:
diff --git a/altair/vegalite/v5/api.py b/altair/vegalite/v5/api.py
index 259cce0e..a5e28087 100644
--- a/altair/vegalite/v5/api.py
+++ b/altair/vegalite/v5/api.py
@@ -1055,6 +1055,25 @@ class TopLevelMixin(mixins.ConfigMethodMixin):
             requirejs=requirejs,
         )
 
+    def to_url(self, *, fullscreen: bool = False) -> str:
+        """Convert a chart to a URL that opens the chart specification in the Vega chart editor
+        The chart specification (including any inline data) is encoded in the URL.
+
+        This method requires that the vl-convert-python package is installed.
+
+        Parameters
+        ----------
+        fullscreen : bool
+            If True, editor will open chart in fullscreen mode. Default False
+        """
+        from ...utils._importers import import_vl_convert
+
+        vlc = import_vl_convert()
+        if _using_vegafusion():
+            return vlc.vega_to_url(self.to_dict(format="vega"), fullscreen=fullscreen)
+        else:
+            return vlc.vegalite_to_url(self.to_dict(), fullscreen=fullscreen)
+
     def save(
         self,
         fp: Union[str, IO],
diff --git a/doc/releases/changes.rst b/doc/releases/changes.rst
index db4ad5c8..85244762 100644
--- a/doc/releases/changes.rst
+++ b/doc/releases/changes.rst
@@ -8,6 +8,8 @@ Version 5.2.0 (unreleased month date, year)
 
 Enhancements
 ~~~~~~~~~~~~
+- Support saving charts as PDF files using the vl-convert export engine (#3244)
+- Support converting charts to sharable Vega editor URLs with ``chart.to_url()`` (#3252)
 
 Bug Fixes
 ~~~~~~~~~
diff --git a/doc/user_guide/saving_charts.rst b/doc/user_guide/saving_charts.rst
index e265c85d..fad5864d 100644
--- a/doc/user_guide/saving_charts.rst
+++ b/doc/user_guide/saving_charts.rst
@@ -224,6 +224,26 @@ size at the default resolution of 72 ppi::
 
     chart.save('chart.png', scale_factor=2)
 
+Sharable URL
+~~~~~~~~~~~~
+The :meth:`Chart.to_url` method can be used to build a sharable URL that opens the chart
+specification in the online Vega editor_.
+
+.. altair-plot::
+    :output: repr
+
+    import altair as alt
+    from vega_datasets import data
+
+    chart = alt.Chart(data.cars.url).mark_point().encode(
+        x='Horsepower:Q',
+        y='Miles_per_Gallon:Q',
+        color='Origin:N'
+    )
+
+    chart.to_url()
+
 .. _vl-convert: https://github.com/vega/vl-convert
 .. _altair_saver: http://github.com/altair-viz/altair_saver/
 .. _vegaEmbed: https://github.com/vega/vega-embed
+.. _editor: https://vega.github.io/editor/
\ No newline at end of file


Test patch:
diff --git a/tests/vegalite/v5/test_api.py b/tests/vegalite/v5/test_api.py
index c61d466d..fa220557 100644
--- a/tests/vegalite/v5/test_api.py
+++ b/tests/vegalite/v5/test_api.py
@@ -380,6 +380,30 @@ def test_save_html(basic_chart, inline):
         assert 'src="https://cdn.jsdelivr.net/npm/vega-embed@6' in content
 
 
+def test_to_url(basic_chart):
+    share_url = basic_chart.to_url()
+    expected_vegalite_encoding = (
+        "N4Igxg9gdgZglgcxALlANzgUwO4tJKAFzigFcJSBnAdTgBNCALFAZgAY2AacaYsiygAlMiRoVYcAvpO50AhoTl4QU"
+        "OQFtMKEPMUBaMACY5LTAA4AnACM55ugFY6ARgBspgOz2zh03Wfs5bCwsIDIganIATgDWyoQAngAOmsgg1hEh3JhQk"
+        "HQkSKggAB7K8JgANnRaStzxSVpQEGokcmUZIHElWBValiA1ickgAI6kckRwisRomtLcACSUYIyY4VpihAmUyAD029"
+        "MIcgB0CBOMpJaHcBDbi8vhe5gHumUTmHt2h44fjocAVpTQPraBRySiYQiUZQ6OT6IwmCzWWwOFzuTymby+fyBYLIA"
+        "DaoCUKQAgkDesgDKYZAStAAhUkoOx2KkgQkgADC9OQABYWMzWQARTnmRx8rQAUU5phFnGpKQAYpy7LyZSytABxTmO"
+        "cyilKCSVuHUgACSioMkgAutIgA"
+    )
+
+    assert (
+        share_url
+        == f"https://vega.github.io/editor/#/url/vega-lite/{expected_vegalite_encoding}"
+    )
+
+    # Check fullscreen
+    fullscreen_share_url = basic_chart.to_url(fullscreen=True)
+    assert (
+        fullscreen_share_url
+        == f"https://vega.github.io/editor/#/url/vega-lite/{expected_vegalite_encoding}/view"
+    )
+
+
 def test_facet_basic():
     # wrapped facet
     chart1 = (