Spaces:
Running
Running
File size: 12,178 Bytes
b72ab63 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 |
from typing import Literal, Optional, Union, cast, Tuple
from .deprecation import AltairDeprecationWarning
from .html import spec_to_html
from ._importers import import_vl_convert, vl_version_for_vl_convert
import struct
import warnings
def spec_to_mimebundle(
spec: dict,
format: Literal["html", "json", "png", "svg", "pdf", "vega", "vega-lite"],
mode: Optional[Literal["vega-lite"]] = None,
vega_version: Optional[str] = None,
vegaembed_version: Optional[str] = None,
vegalite_version: Optional[str] = None,
embed_options: Optional[dict] = None,
engine: Optional[Literal["vl-convert", "altair_saver"]] = None,
**kwargs,
) -> Union[dict, Tuple[dict, dict]]:
"""Convert a vega-lite specification to a mimebundle
The mimebundle type is controlled by the ``format`` argument, which can be
one of the following ['html', 'json', 'png', 'svg', 'pdf', 'vega', 'vega-lite']
Parameters
----------
spec : dict
a dictionary representing a vega-lite plot spec
format : string {'html', 'json', 'png', 'svg', 'pdf', 'vega', 'vega-lite'}
the file format to be saved.
mode : string {'vega-lite'}
The rendering mode.
vega_version : string
The version of vega.js to use
vegaembed_version : string
The version of vegaembed.js to use
vegalite_version : string
The version of vegalite.js to use. Only required if mode=='vega-lite'
embed_options : dict (optional)
The vegaEmbed options dictionary. Defaults to the embed options set with
alt.renderers.set_embed_options().
(See https://github.com/vega/vega-embed for details)
engine: string {'vl-convert', 'altair_saver'}
the conversion engine to use for 'png', 'svg', 'pdf', and 'vega' formats
**kwargs :
Additional arguments will be passed to the generating function
Returns
-------
output : dict
a mime-bundle representing the image
Note
----
The png, svg, pdf, and vega outputs require the altair_saver package
"""
# Local import to avoid circular ImportError
from altair.utils.display import (
compile_with_vegafusion,
using_vegafusion,
)
from altair import renderers
if mode != "vega-lite":
raise ValueError("mode must be 'vega-lite'")
internal_mode: Literal["vega-lite", "vega"] = mode
if using_vegafusion():
spec = compile_with_vegafusion(spec)
internal_mode = "vega"
# Default to the embed options set by alt.renderers.set_embed_options
if embed_options is None:
final_embed_options = renderers.options.get("embed_options", {})
else:
final_embed_options = embed_options
embed_options = preprocess_embed_options(final_embed_options)
if format in ["png", "svg", "pdf", "vega"]:
format = cast(Literal["png", "svg", "pdf", "vega"], format)
return _spec_to_mimebundle_with_engine(
spec,
format,
internal_mode,
engine=engine,
format_locale=embed_options.get("formatLocale", None),
time_format_locale=embed_options.get("timeFormatLocale", None),
**kwargs,
)
if format == "html":
html = spec_to_html(
spec,
mode=internal_mode,
vega_version=vega_version,
vegaembed_version=vegaembed_version,
vegalite_version=vegalite_version,
embed_options=embed_options,
**kwargs,
)
return {"text/html": html}
if format == "vega-lite":
if vegalite_version is None:
raise ValueError("Must specify vegalite_version")
return {"application/vnd.vegalite.v{}+json".format(vegalite_version[0]): spec}
if format == "json":
return {"application/json": spec}
raise ValueError(
"format must be one of "
"['html', 'json', 'png', 'svg', 'pdf', 'vega', 'vega-lite']"
)
def _spec_to_mimebundle_with_engine(
spec: dict,
format: Literal["png", "svg", "pdf", "vega"],
mode: Literal["vega-lite", "vega"],
format_locale: Optional[Union[str, dict]] = None,
time_format_locale: Optional[Union[str, dict]] = None,
**kwargs,
) -> Union[dict, Tuple[dict, dict]]:
"""Helper for Vega-Lite to mimebundle conversions that require an engine
Parameters
----------
spec : dict
a dictionary representing a vega-lite plot spec
format : string {'png', 'svg', 'pdf', 'vega'}
the format of the mimebundle to be returned
mode : string {'vega-lite', 'vega'}
The rendering mode.
engine: string {'vl-convert', 'altair_saver'}
the conversion engine to use
format_locale : str or dict
d3-format locale name or dictionary. Defaults to "en-US" for United States English.
See https://github.com/d3/d3-format/tree/main/locale for available names and example
definitions.
time_format_locale : str or dict
d3-time-format locale name or dictionary. Defaults to "en-US" for United States English.
See https://github.com/d3/d3-time-format/tree/main/locale for available names and example
definitions.
**kwargs :
Additional arguments will be passed to the conversion function
"""
# Normalize the engine string (if any) by lower casing
# and removing underscores and hyphens
engine = kwargs.pop("engine", None)
normalized_engine = _validate_normalize_engine(engine, format)
if normalized_engine == "vlconvert":
vlc = import_vl_convert()
vl_version = vl_version_for_vl_convert()
if format == "vega":
if mode == "vega":
vg = spec
else:
vg = vlc.vegalite_to_vega(spec, vl_version=vl_version)
return {"application/vnd.vega.v5+json": vg}
elif format == "svg":
if mode == "vega":
svg = vlc.vega_to_svg(
spec,
format_locale=format_locale,
time_format_locale=time_format_locale,
)
else:
svg = vlc.vegalite_to_svg(
spec,
vl_version=vl_version,
format_locale=format_locale,
time_format_locale=time_format_locale,
)
return {"image/svg+xml": svg}
elif format == "png":
scale = kwargs.get("scale_factor", 1)
# The default ppi for a PNG file is 72
default_ppi = 72
ppi = kwargs.get("ppi", default_ppi)
if mode == "vega":
png = vlc.vega_to_png(
spec,
scale=scale,
ppi=ppi,
format_locale=format_locale,
time_format_locale=time_format_locale,
)
else:
png = vlc.vegalite_to_png(
spec,
vl_version=vl_version,
scale=scale,
ppi=ppi,
format_locale=format_locale,
time_format_locale=time_format_locale,
)
factor = ppi / default_ppi
w, h = _pngxy(png)
return {"image/png": png}, {
"image/png": {"width": w / factor, "height": h / factor}
}
elif format == "pdf":
scale = kwargs.get("scale_factor", 1)
if mode == "vega":
pdf = vlc.vega_to_pdf(
spec,
scale=scale,
format_locale=format_locale,
time_format_locale=time_format_locale,
)
else:
pdf = vlc.vegalite_to_pdf(
spec,
vl_version=vl_version,
scale=scale,
format_locale=format_locale,
time_format_locale=time_format_locale,
)
return {"application/pdf": pdf}
else:
# This should be validated above
# but raise exception for the sake of future development
raise ValueError("Unexpected format {fmt!r}".format(fmt=format))
elif normalized_engine == "altairsaver":
warnings.warn(
"The altair_saver export engine is deprecated and will be removed in a future version.\n"
"Please migrate to the vl-convert engine",
AltairDeprecationWarning,
stacklevel=1,
)
import altair_saver
return altair_saver.render(spec, format, mode=mode, **kwargs)
else:
# This should be validated above
# but raise exception for the sake of future development
raise ValueError(
"Unexpected normalized_engine {eng!r}".format(eng=normalized_engine)
)
def _validate_normalize_engine(
engine: Optional[Literal["vl-convert", "altair_saver"]],
format: Literal["png", "svg", "pdf", "vega"],
) -> str:
"""Helper to validate and normalize the user-provided engine
engine : {None, 'vl-convert', 'altair_saver'}
the user-provided engine string
format : string {'png', 'svg', 'pdf', 'vega'}
the format of the mimebundle to be returned
"""
# Try to import vl_convert
try:
vlc = import_vl_convert()
except ImportError:
vlc = None
# Try to import altair_saver
try:
import altair_saver
except ImportError:
altair_saver = None
# Normalize engine string by lower casing and removing underscores and hyphens
normalized_engine = (
None if engine is None else engine.lower().replace("-", "").replace("_", "")
)
# Validate or infer default value of normalized_engine
if normalized_engine == "vlconvert":
if vlc is None:
raise ValueError(
"The 'vl-convert' conversion engine requires the vl-convert-python package"
)
elif normalized_engine == "altairsaver":
if altair_saver is None:
raise ValueError(
"The 'altair_saver' conversion engine requires the altair_saver package"
)
elif normalized_engine is None:
if vlc is not None:
normalized_engine = "vlconvert"
elif altair_saver is not None:
normalized_engine = "altairsaver"
else:
raise ValueError(
"Saving charts in {fmt!r} format requires the vl-convert-python or altair_saver package: "
"see http://github.com/altair-viz/altair_saver/".format(fmt=format)
)
else:
raise ValueError(
"Invalid conversion engine {engine!r}. Expected one of {valid!r}".format(
engine=engine, valid=("vl-convert", "altair_saver")
)
)
return normalized_engine
def _pngxy(data):
"""read the (width, height) from a PNG header
Taken from IPython.display
"""
ihdr = data.index(b"IHDR")
# next 8 bytes are width/height
return struct.unpack(">ii", data[ihdr + 4 : ihdr + 12])
def preprocess_embed_options(embed_options: dict) -> dict:
"""Preprocess embed options to a form compatible with Vega Embed
Parameters
----------
embed_options : dict
The embed options dictionary to preprocess.
Returns
-------
embed_opts : dict
The preprocessed embed options dictionary.
"""
embed_options = (embed_options or {}).copy()
# Convert locale strings to objects compatible with Vega Embed using vl-convert
format_locale = embed_options.get("formatLocale", None)
if isinstance(format_locale, str):
vlc = import_vl_convert()
embed_options["formatLocale"] = vlc.get_format_locale(format_locale)
time_format_locale = embed_options.get("timeFormatLocale", None)
if isinstance(time_format_locale, str):
vlc = import_vl_convert()
embed_options["timeFormatLocale"] = vlc.get_time_format_locale(
time_format_locale
)
return embed_options
|