Source code for highcharts_maps.chart

from typing import Optional

from validator_collection import validators, checkers

from highcharts_core.chart import Chart as ChartBase

from highcharts_maps import constants, errors, utility_functions
from highcharts_maps.options import HighchartsOptions, HighchartsMapsOptions
from highcharts_maps.decorators import validate_types
from highcharts_maps.js_literal_functions import serialize_to_js_literal
from highcharts_maps.headless_export import ExportServer
from highcharts_maps.options.series.series_generator import (create_series_obj,
                                                             SERIES_CLASSES,
                                                             MAPS_SERIES_LIST)
from highcharts_maps.global_options.shared_options import SharedMapsOptions, SharedOptions
from highcharts_maps.options.chart import ChartOptions
from highcharts_maps.options.map_views import MapViewOptions
from highcharts_maps.options.series.data.map_data import MapData
from highcharts_maps.utility_classes.projections import ProjectionOptions, CustomProjection


[docs]class Chart(ChartBase): """Python representation of a Highcharts ``Chart`` object.""" def __init__(self, **kwargs): self._is_maps_chart = None self.is_maps_chart = kwargs.get('is_maps_chart', False) super().__init__(**kwargs) def _jupyter_include_scripts(self): """Return the JavaScript code that is used to load the Highcharts JS libraries. .. note:: Currently includes *all* `Highcharts JS <https://www.highcharts.com/>`_ modules in the HTML. This issue will be addressed when roadmap issue :issue:`2` is released. :rtype: :class:`str <python:str>` """ js_str = '' if self.is_maps_chart: for item in constants.MAPS_INCLUDE_LIBS: js_str += utility_functions.jupyter_add_script(item) js_str += """.then(() => {""" for item in constants.MAPS_INCLUDE_LIBS: js_str += """});""" else: for item in constants.INCLUDE_LIBS: js_str += utility_functions.jupyter_add_script(item) js_str += """.then(() => {""" for item in constants.INCLUDE_LIBS: js_str += """});""" return js_str def _jupyter_javascript(self, global_options = None, container = None, retries = 3, interval = 1000): """Return the JavaScript code which Jupyter Labs will need to render the chart. :param global_options: The :term:`shared options` to use when rendering the chart. Defaults to :obj:`None <python:None>` :type global_options: :class:`SharedOptions <highcharts_stock.global_options.shared_options.SharedOptions>` or :obj:`None <python:None>` :param container: The ID to apply to the HTML container when rendered in Jupyter Labs. Defaults to :obj:`None <python:None>`, which applies the :meth:`.container <highcharts_core.chart.Chart.container>` property if set, and ``'highcharts_target_div'`` if not set. :type container: :class:`str <python:str>` or :obj:`None <python:None>` :param retries: The number of times to retry rendering the chart. Used to avoid race conditions with the Highcharts script. Defaults to 3. :type retries: :class:`int <python:int>` :param interval: The number of milliseconds to wait between retrying rendering the chart. Defaults to 1000 (1 seocnd). :type interval: :class:`int <python:int>` :rtype: :class:`str <python:str>` """ original_container = self.container self.container = container or self.container or 'highcharts_target_div' if global_options is not None: global_options = validate_types(global_options, types = (SharedMapsOptions, SharedOptions)) js_str = '' js_str += utility_functions.get_retryHighcharts() if global_options: js_str += '\n' + utility_functions.prep_js_for_jupyter(global_options.to_js_literal()) + '\n' js_str += utility_functions.prep_js_for_jupyter(self.to_js_literal(), container = self.container, retries = retries, interval = interval) self.container = original_container return js_str @property def is_maps_chart(self) -> bool: """If ``True``, indicates that the chart should be rendered as a `Highcharts Maps <https://www.highcharts.com/products/maps/>`__ chart. If ``False``, the chart will be rendered using the standard `Highcharts JS <https://www.highcharts.com/products/highcharts/>`__ constructor. Defaults to ``False``. :rtype: :class:`bool <python:bool>` """ return self._is_maps_chart @is_maps_chart.setter def is_maps_chart(self, value): self._is_maps_chart = bool(value) @property def options(self) -> Optional[HighchartsOptions | HighchartsMapsOptions]: """The Python representation of the `Highcharts Maps <https://www.highcharts.com/products/maps/>`__ ``options`` `configuration object <https://api.highcharts.com/highmaps/>`_ Defaults to :obj:`None <python:None>`. :rtype: :class:`HighchartsOptions` or :class:`HighchartsMapsOptions` or :obj:`None <python:None>` """ return self._options @options.setter def options(self, value): if not value: self._options = None elif self.is_maps_chart: self._options = validate_types(value, HighchartsMapsOptions) self.is_maps_chart = True else: if checkers.is_type(value, 'HighchartsMapsOptions'): self._options = value self.is_maps_chart = True elif checkers.is_type(value, 'HighchartsOptions'): self._options = value elif ('mapNavigation' in value or 'map_navigation' in value or 'mapView' in value or 'map_view' in value): self._options = validate_types(value, HighchartsMapsOptions) self.is_maps_chart = True else: self._options = validate_types(value, HighchartsOptions) @classmethod def _get_kwargs_from_dict(cls, as_dict): kwargs = { 'callback': as_dict.get('callback', None), 'container': as_dict.get('container', None) or as_dict.get('renderTo', None), 'options': as_dict.get('options', None) or as_dict.get('userOptions', None), 'variable_name': as_dict.get('variable_name', None) or as_dict.get('variableName', None), 'is_maps_chart': as_dict.get('is_maps_chart', None) or as_dict.get('isMapsChart', False) } return kwargs
[docs] def to_js_literal(self, filename = None, encoding = 'utf-8') -> Optional[str]: """Return the object represented as a :class:`str <python:str>` containing the JavaScript object literal. :param filename: The name of a file to which the JavaScript object literal should be persisted. Defaults to :obj:`None <python:None>` :type filename: Path-like :param encoding: The character encoding to apply to the resulting object. Defaults to ``'utf-8'``. :type encoding: :class:`str <python:str>` .. note:: If :meth:`variable_name <Chart.variable_name>` is set, will render a string as a new JavaScript instance invocation in the (pseudo-code) form: .. code-block:: javascript new VARIABLE_NAME = new Chart(...); If :meth:`variable_name <Chart.variable_name>` is not set, will simply return the ``new Chart(...)`` portion in the string. :rtype: :class:`str <python:str>` or :obj:`None <python:None>` """ if filename: filename = validators.path(filename) untrimmed = self._to_untrimmed_dict() as_dict = {} for key in untrimmed: item = untrimmed[key] serialized = serialize_to_js_literal(item, encoding = encoding) if serialized is not None: as_dict[key] = serialized signature_elements = 0 fetch_as_str = '' if self.is_async: urls = [] topologies = [] if self.options.chart and self.options.chart.map: url = self.options.chart.map.url urls.append(url) self.options.chart.map.fetch_counter = 1 map_data_as_str = self.options.chart.map.to_js_literal(encoding = encoding) topologies.append(map_data_as_str) for index, series in enumerate(self.options.series): if series.is_async: url = series.map_data.url if url in urls: continue urls.append(url) if len(urls) > 1: self.options.series[index].map_data.fetch_counter += 1 map_data_as_str = series.map_data.to_js_literal(encoding = encoding) topologies.append(map_data_as_str) fetch_as_str = '\n'.join(topologies) custom_projection_as_str = '' if self.uses_custom_projection: custom_projection_as_str = self.options.map_view.projection.custom.to_js_literal(encoding = encoding) custom_projection_as_str += f"""\nHighcharts.Projection.add('{self.options.map_view.projection.custom.name}', {self.options.map_view.projection.custom.class_name})\n""" container_as_str = '' if self.container: container_as_str = f"""'{self.container}'""" else: container_as_str = """null""" signature_elements += 1 options_as_str = '' if self.options: options_as_str = self.options.to_js_literal(encoding = encoding) if self.options.chart and self.options.chart.map and self.options.chart.is_async: chart_map_str = self.options.chart.map.to_js_literal(encoding = encoding) chart_map_str = f"""'{chart_map_str}'""" fetch_counter = self.options.chart.map.fetch_counter options_as_str = options_as_str.replace(chart_map_str, f'topology{fetch_counter}') options_as_str = f"""{options_as_str}""" else: options_as_str = """{}""" signature_elements += 1 callback_as_str = '' if self.callback: callback_as_str = self.callback.to_js_literal(encoding = encoding) callback_as_str = f"""{callback_as_str}""" signature_elements += 1 signature = """new Highcharts.chart(""" if self.is_maps_chart: signature = """new Highcharts.mapChart(""" if container_as_str: signature += container_as_str if signature_elements > 1: signature += ',\n' if options_as_str: signature += options_as_str if signature_elements > 1: signature += ',\n' if callback_as_str: signature += callback_as_str signature += ');' constructor_prefix = '' if self.variable_name: constructor_prefix = f'var {self.variable_name} = ' as_str = constructor_prefix + signature if self.is_async: prefix = """document.addEventListener('DOMContentLoaded', function() {\n""" if custom_projection_as_str: prefix += custom_projection_as_str prefix += """(async () => { """ suffix = """})()});""" as_str = fetch_as_str + '\n' + as_str else: prefix = """document.addEventListener('DOMContentLoaded', function() {\n""" if custom_projection_as_str: prefix += custom_projection_as_str suffix = """});""" as_str = prefix + as_str + '\n' + suffix if filename: with open(filename, 'w', encoding = encoding) as file_: file_.write(as_str) return as_str
[docs] def download_chart(self, format = 'png', scale = 1, width = None, filename = None, auth_user = None, auth_password = None, timeout = 0.5, server_instance = None, **kwargs): """Export a downloaded form of the chart using a Highcharts :term:`Export Server`. :param filename: The name of the file where the exported chart should (optionally) be persisted. Defaults to :obj:`None <python:None>`. :type filename: Path-like or :obj:`None <python:None>` :param auth_user: The username to use to authenticate against the Export Server, using :term:`basic authentication`. Defaults to :obj:`None <python:None>`. :type auth_user: :class:`str <python:str>` or :obj:`None <python:None>` :param auth_password: The password to use to authenticate against the Export Server (using :term:`basic authentication`). Defaults to :obj:`None <python:None>`. :type auth_password: :class:`str <python:str>` or :obj:`None <python:None>` :param timeout: The number of seconds to wait before issuing a timeout error. The timeout check is passed if bytes have been received on the socket in less than the ``timeout`` value. Defaults to ``0.5``. :type timeout: numeric or :obj:`None <python:None>` :param server_instance: Provide an already-configured :class:`ExportServer` instance to use to programmatically produce the exported chart. Defaults to :obj:`None <python:None>`, which causes Highcharts for Python to instantiate a new :class:`ExportServer` instance. :type server_instance: :class:`ExportServer` or :obj:`None <python:None>` .. note:: All other keyword arguments are as per the :class:`ExportServer` constructor. :returns: The exported chart image, either as a :class:`bytes <python:bytes>` binary object or as a base-64 encoded string (depending on the ``use_base64`` keyword argument). :rtype: :class:`bytes <python:bytes>` or :class:`str <python:str>` """ if self.is_maps_chart: constructor = 'Stock' else: constructor = 'Chart' if not server_instance: return ExportServer.get_chart(filename = filename, auth_user = auth_user, auth_password = auth_password, timeout = timeout, options = self.options, constructor = constructor, scale = scale, width = width, **kwargs) if not isinstance(server_instance, ExportServer): raise errors.HighchartsValueError(f'server_instance is expected to be an ' f'ExportServer instance. Was: ' f'{server_instance.__class__.__name__}') return server_instance.request_chart(filename = filename, auth_user = auth_user, auth_password = auth_password, timeout = timeout, options = self.options, constructor = constructor, **kwargs)
[docs] def add_series(self, *series): """Adds ``series`` to the :meth:`Chart.options.series <highcharts_core.options.HighchartsOptions.series>` property. :param series: One or more :term:`series` instances (descended from :class:`SeriesBase <highcharts_core.options.series.base.SeriesBase>`) or an instance (e.g. :class:`dict <python:dict>`, :class:`str <python:str>`, etc.) coercable to one :type series: one or more :class:`SeriesBase <highcharts_core.options.series.base.SeriesBase>` or coercable """ new_series = [] for item in series: item_series = create_series_obj(item) new_series.append(item_series) if self.options and self.options.series: existing_series = [x for x in self.options.series] elif self.options: existing_series = [] else: existing_series = [] if self.is_maps_chart: self.options = HighchartsMapsOptions() else: self.options = HighchartsOptions() updated_series = existing_series + new_series self.options.series = updated_series
[docs] @classmethod def from_series(cls, *series, kwargs = None): """Creates a new :class:`Chart <highcharts_core.chart.Chart>` instance populated with ``series``. :param series: One or more :term:`series` instances (descended from :class:`SeriesBase <highcharts_core.options.series.base.SeriesBase>`) or an instance (e.g. :class:`dict <python:dict>`, :class:`str <python:str>`, etc.) coercable to one :type series: one or more :class:`SeriesBase <highcharts_core.options.series.base.SeriesBase>` or :class:`IndicatorSeriesBase <highcharts_maps.options.series.base.IndicatorSeriesBase>` coercable :param kwargs: Other properties to use as keyword arguments for the instance to be created. .. warning:: If ``kwargs`` sets the :meth:`options.series <highcharts_maps.options.HighchartsOptions.series>` property, that setting will be *overridden* by the contents of ``series``. :type kwargs: :class:`dict <python:dict>` :returns: A new :class:`Chart <highcharts_maps.chart.Chart>` instance :rtype: :class:`Chart <highcharts_maps.chart.Chart>` """ kwargs = validators.dict(kwargs, allow_empty = True) or {} instance = cls(**kwargs) instance.add_series(series)
[docs] def display(self, global_options = None): """Display the chart in `Jupyter Labs <https://jupyter.org/>`_ or `Jupyter Notebooks <https://jupyter.org/>`_. :raises HighchartsDependencyError: if `ipython <https://ipython.readthedocs.io/en/stable/>`_ is not available in the runtime environment """ try: from IPython import display except ImportError: raise errors.HighchartsDependencyError('Unable to import IPython.display. ' 'Make sure that it is available in ' 'your runtime environment. To install,' 'use: pip install ipython') if global_options is not None: global_options = validate_types(global_options, types = SharedMapsOptions) if self.is_maps_chart: include_str = constants.MAPS_INCLUDE_STR else: include_str = constants.INCLUDE_STR html_str = include_str + '\n' if global_options: html_str += global_options._repr_html_() + '\n' html_str += self._repr_html_() display.display_html(html_str, raw = True)
@staticmethod def _get_options_obj(series_type, options_kwargs): """Return an :class:`Options` descendent based on the series type. :param series_type: Indicates the series type that should be created from the CSV data. :type series_type: :class:`str <python:str>` :param options_kwargs: A :class:`dict <python:dict>` containing keyword arguments that should be used when instantiating the :class:`Options` instance. Defaults to :obj:`None <python:None>`. .. warning:: If ``options_kwargs`` contains a ``series`` key, the ``series`` value will be *overwritten*. The ``series`` value will be created from the CSV file instead. :type options_kwargs: :class:`dict <python:dict>` or :obj:`None <python:None>` :returns: An :class:`Options` descendent. :rtype: :class:`HighchartsOptions` or :class:`HighchartsMapsOptions` """ series_type = validators.string(series_type, allow_empty = False) series_type = series_type.lower() if series_type not in SERIES_CLASSES: raise errors.HighchartsValueError(f'series_type expects a valid Highcharts ' f'series type. Received: {series_type}') options_kwargs = validators.dict(options_kwargs, allow_empty = True) or {} if series_type not in MAPS_SERIES_LIST: options = HighchartsOptions(**options_kwargs) else: options = HighchartsMapsOptions(**options_kwargs) return options
[docs] @classmethod def from_csv(cls, as_string_or_file, property_column_map, series_type, has_header_row = True, series_kwargs = None, options_kwargs = None, chart_kwargs = None, delimiter = ',', null_text = 'None', wrapper_character = "'", line_terminator = '\r\n', wrap_all_strings = False, double_wrapper_character_when_nested = False, escape_character = "\\", is_maps_chart = False): """Create a new :class:`Chart <highcharts_core.chart.Chart>` instance with data populated from a CSV string or file. .. note:: For an example :class:`LineSeries <highcharts_core.options.series.area.LineSeries>`, the minimum code required would be: .. code-block:: python my_chart = Chart.from_csv('some-csv-file.csv', property_column_map = { 'x': 0, 'y': 3, 'id': 'id' }, series_type = 'line') As the example above shows, data is loaded into the ``my_chart`` instance from the CSV file with a filename ``some-csv-file.csv``. The :meth:`x <CartesianData.x>` values for each data point will be taken from the first (index 0) column in the CSV file. The :meth:`y <CartesianData.y>` values will be taken from the fourth (index 3) column in the CSV file. And the :meth:`id <CartesianData.id>` values will be taken from a column whose header row is labeled ``'id'`` (regardless of its index). :param as_string_or_file: The CSV data to use to pouplate data. Accepts either the raw CSV data as a :class:`str <python:str>` or a path to a file in the runtime environment that contains the CSV data. .. tip:: Unwrapped empty column values are automatically interpreted as null (:obj:`None <python:None>`). :type as_string_or_file: :class:`str <python:str>` or Path-like :param property_column_map: A :class:`dict <python:dict>` used to indicate which data point property should be set to which CSV column. The keys in the :class:`dict <python:dict>` should correspond to properties in the data point class, while the value can either be a numerical index (starting with 0) or a :class:`str <python:str>` indicating the label for the CSV column. .. warning:: If the ``property_column_map`` uses :class:`str <python:str>` values, the CSV file *must* have a header row (this is expected, by default). If there is no header row and a :class:`str <python:str>` value is found, a :exc:`HighchartsCSVDeserializationError` will be raised. :type property_column_map: :class:`dict <python:dict>` :param series_type: Indicates the series type that should be created from the CSV data. :type series_type: :class:`str <python:str>` :param has_header_row: If ``True``, indicates that the first row of ``as_string_or_file`` contains column labels, rather than actual data. Defaults to ``True``. :type has_header_row: :class:`bool <python:bool>` :param series_kwargs: An optional :class:`dict <python:dict>` containing keyword arguments that should be used when instantiating the series instance. Defaults to :obj:`None <python:None>`. .. warning:: If ``series_kwargs`` contains a ``data`` key, its value will be *overwritten*. The ``data`` value will be created from the CSV file instead. :type series_kwargs: :class:`dict <python:dict>` or :obj:`None <python:None>` :param options_kwargs: An optional :class:`dict <python:dict>` containing keyword arguments that should be used when instantiating the :class:`HighchartsOptions` instance. Defaults to :obj:`None <python:None>`. .. warning:: If ``options_kwargs`` contains a ``series`` key, the ``series`` value will be *overwritten*. The ``series`` value will be created from the CSV file instead. :type options_kwargs: :class:`dict <python:dict>` or :obj:`None <python:None>` :param chart_kwargs: An optional :class:`dict <python:dict>` containing keyword arguments that should be used when instantiating the :class:`Chart` instance. Defaults to :obj:`None <python:None>`. .. warning:: If ``chart_kwargs`` contains an ``options`` key, ``options`` will be *overwritten*. The ``options`` value will be created from the ``options_kwargs`` and CSV file instead. :type chart_kwargs: :class:`dict <python:dict>` or :obj:`None <python:None>` :param delimiter: The delimiter used between columns. Defaults to ``,``. :type delimiter: :class:`str <python:str>` :param wrapper_character: The string used to wrap string values when wrapping is applied. Defaults to ``'``. :type wrapper_character: :class:`str <python:str>` :param null_text: The string used to indicate an empty value if empty values are wrapped. Defaults to `None`. :type null_text: :class:`str <python:str>` :param line_terminator: The string used to indicate the end of a line/record in the CSV data. Defaults to ``'\\r\\n'``. .. note:: The Python :mod:`csv <python:csv>` currently ignores the ``line_terminator`` parameter and always applies ``'\\r\\n'``, by design. The Python docs say this may change in the future, so for future backwards compatibility we are including it here. :type line_terminator: :class:`str <python:str>` :param wrap_all_strings: If ``True``, indicates that the CSV file has all string data values wrapped in quotation marks. Defaults to ``False``. .. warning:: If set to ``True``, the :mod:`csv <python:csv>` module will try to coerce any value that is *not* wrapped in quotation marks to a :class:`float <python:float>`. This can cause unexpected behavior, and typically we recommend leaving this as ``False`` and then re-casting values after they have been parsed. :type wrap_all_strings: :class:`bool <python:bool>` :param double_wrapper_character_when_nested: If ``True``, quote character is doubled when appearing within a string value. If ``False``, the ``escape_character`` is used to prefix quotation marks. Defaults to ``False``. :type double_wrapper_character_when_nested: :class:`bool <python:bool>` :param escape_character: A one-character string that indicates the character used to escape quotation marks if they appear within a string value that is already wrapped in quotation marks. Defaults to ``\\\\`` (which is Python for ``'\\'``, which is Python's native escape character). :type escape_character: :class:`str <python:str>` :param is_maps_chart: If ``True``, indicates that the chart should be instantiated as a **Highcharts Stock for Python** chart. Defaults to ``False``. :type is_maps_chart: :class:`bool <python:bool>` :returns: A :class:`Chart <highcharts_core.chart.Chart>` instance with its data populated from the CSV data. :rtype: :class:`Chart <highcharts_core.chart.Chart>` :raises HighchartsCSVDeserializationError: if ``property_column_map`` references CSV columns by their label, but the CSV data does not contain a header row """ chart_kwargs = validators.dict(chart_kwargs, allow_empty = True) or {} options = cls._get_options_obj(series_type, options_kwargs) series_cls = SERIES_CLASSES.get(series_type, None) series = series_cls.from_csv(as_string_or_file, property_column_map, has_header_row = has_header_row, series_kwargs = series_kwargs, delimiter = delimiter, null_text = null_text, wrapper_character = wrapper_character, line_terminator = line_terminator, wrap_all_strings = wrap_all_strings, double_wrapper_character_when_nested = double_wrapper_character_when_nested, escape_character = escape_character) options.series = [series] instance = cls(**chart_kwargs) instance.options = options return instance
[docs] @classmethod def from_pandas(cls, df, property_map, series_type, series_kwargs = None, options_kwargs = None, chart_kwargs = None): """Create a :class:`Chart <highcharts_core.chart.Chart>` instance whose data is populated from a `pandas <https://pandas.pydata.org/>`_ :class:`DataFrame <pandas:DataFrame>`. :param df: The :class:`DataFrame <pandas:DataFrame>` from which data should be loaded. :type df: :class:`DataFrame <pandas:DataFrame>` :param property_map: A :class:`dict <python:dict>` used to indicate which data point property should be set to which column in ``df``. The keys in the :class:`dict <python:dict>` should correspond to properties in the data point class, while the value should indicate the label for the :class:`DataFrame <pandas:DataFrame>` column. :type property_map: :class:`dict <python:dict>` :param series_type: Indicates the series type that should be created from the data in ``df``. :type series_type: :class:`str <python:str>` :param series_kwargs: An optional :class:`dict <python:dict>` containing keyword arguments that should be used when instantiating the series instance. Defaults to :obj:`None <python:None>`. .. warning:: If ``series_kwargs`` contains a ``data`` key, its value will be *overwritten*. The ``data`` value will be created from ``df`` instead. :type series_kwargs: :class:`dict <python:dict>` :param options_kwargs: An optional :class:`dict <python:dict>` containing keyword arguments that should be used when instantiating the :class:`HighchartsOptions` instance. Defaults to :obj:`None <python:None>`. .. warning:: If ``options_kwargs`` contains a ``series`` key, the ``series`` value will be *overwritten*. The ``series`` value will be created from the data in ``df``. :type options_kwargs: :class:`dict <python:dict>` or :obj:`None <python:None>` :param chart_kwargs: An optional :class:`dict <python:dict>` containing keyword arguments that should be used when instantiating the :class:`Chart` instance. Defaults to :obj:`None <python:None>`. .. warning:: If ``chart_kwargs`` contains an ``options`` key, ``options`` will be *overwritten*. The ``options`` value will be created from the ``options_kwargs`` and the data in ``df`` instead. :type chart_kwargs: :class:`dict <python:dict>` or :obj:`None <python:None>` :returns: A :class:`Chart <highcharts_core.chart.Chart>` instance with its data populated from the data in ``df``. :rtype: :class:`Chart <highcharts_core.chart.Chart>` :raises HighchartsPandasDeserializationError: if ``property_map`` references a column that does not exist in the data frame :raises HighchartsDependencyError: if `pandas <https://pandas.pydata.org/>`_ is not available in the runtime environment """ chart_kwargs = validators.dict(chart_kwargs, allow_empty = True) or {} options = cls._get_options_obj(series_type, options_kwargs) series_cls = SERIES_CLASSES.get(series_type, None) series = series_cls.from_pandas(df, property_map, series_kwargs) options = HighchartsOptions(**options_kwargs) options.series = [series] instance = cls(**chart_kwargs) instance.options = options return instance
[docs] @classmethod def from_geopandas(cls, gdf, property_map, series_type, series_kwargs = None, options_kwargs = None, chart_kwargs = None): """Create a :class:`Chart <highcharts_core.chart.Chart>` instance whose data is populated from a `geopandas <https://geopandas.org/>`__ :class:`GeoDataFrame <geopandas:GeoDataFrame>`. :param gdf: The :class:`GeoDataFrame <geopandas:GeoDataFrame>` from which data should be loaded. :type gdf: :class:`GeoDataFrame <geopandas:GeoDataFrame>` :param property_map: A :class:`dict <python:dict>` used to indicate which data point property should be set to which column in ``gdf``. The keys in the :class:`dict <python:dict>` should correspond to properties in the data point class, while the value should indicate the label for the :class:`GeoDataFrame <geopandas:GeoDataFrame>` column. :type property_map: :class:`dict <python:dict>` :param series_type: Indicates the series type that should be created from the data in ``gdf``. :type series_type: :class:`str <python:str>` :param series_kwargs: An optional :class:`dict <python:dict>` containing keyword arguments that should be used when instantiating the series instance. Defaults to :obj:`None <python:None>`. .. warning:: If ``series_kwargs`` contains a ``data`` key, its value will be *overwritten*. The ``data`` value will be created from ``gdf`` instead. :type series_kwargs: :class:`dict <python:dict>` :param options_kwargs: An optional :class:`dict <python:dict>` containing keyword arguments that should be used when instantiating the :class:`HighchartsOptions` instance. Defaults to :obj:`None <python:None>`. .. warning:: If ``options_kwargs`` contains a ``series`` key, the ``series`` value will be *overwritten*. The ``series`` value will be created from the data in ``gdf``. :type options_kwargs: :class:`dict <python:dict>` or :obj:`None <python:None>` :param chart_kwargs: An optional :class:`dict <python:dict>` containing keyword arguments that should be used when instantiating the :class:`Chart` instance. Defaults to :obj:`None <python:None>`. .. warning:: If ``chart_kwargs`` contains an ``options`` key, ``options`` will be *overwritten*. The ``options`` value will be created from the ``options_kwargs`` and the data in ``gdf`` instead. :type chart_kwargs: :class:`dict <python:dict>` or :obj:`None <python:None>` :returns: A :class:`Chart <highcharts_core.chart.Chart>` instance with its data populated from the data in ``gdf``. :rtype: :class:`Chart <highcharts_core.chart.Chart>` :raises HighchartsPandasDeserializationError: if ``property_map`` references a column that does not exist in the data frame :raises HighchartsDependencyError: if `pandas <https://pandas.pydata.org/>`_ is not available in the runtime environment """ chart_kwargs = validators.dict(chart_kwargs, allow_empty = True) or {} options = cls._get_options_obj(series_type, options_kwargs) series_cls = SERIES_CLASSES.get(series_type, None) series = series_cls.from_pandas(gdf, property_map, series_kwargs) options = HighchartsMapsOptions(**options_kwargs) options.chart = ChartOptions() options.chart.map_data = MapData.from_geodataframe(as_gdf = gdf) options.series = [series] instance = cls(**chart_kwargs) instance.options = options return instance
[docs] @classmethod def from_pyspark(cls, df, property_map, series_type, series_kwargs = None, options_kwargs = None, chart_kwargs = None): """Create a :class:`Chart <highcharts_core.chart.Chart>` instance whose data is populated from a `PySpark <https://spark.apache.org/docs/latest/api/python/>`_ :class:`DataFrame <pyspark:pyspark.sql.DataFrame>`. :param df: The :class:`DataFrame <pyspark:pyspark.sql.DataFrame>` from which data should be loaded. :type df: :class:`DataFrame <pyspark:pyspark.sql.DataFrame>` :param property_map: A :class:`dict <python:dict>` used to indicate which data point property should be set to which column in ``df``. The keys in the :class:`dict <python:dict>` should correspond to properties in the data point class, while the value should indicate the label for the :class:`DataFrame <pyspark:pyspark.sql.DataFrame>` column. :type property_map: :class:`dict <python:dict>` :param series_type: Indicates the series type that should be created from the data in ``df``. :type series_type: :class:`str <python:str>` :param series_kwargs: An optional :class:`dict <python:dict>` containing keyword arguments that should be used when instantiating the series instance. Defaults to :obj:`None <python:None>`. .. warning:: If ``series_kwargs`` contains a ``data`` key, its value will be *overwritten*. The ``data`` value will be created from ``df`` instead. :type series_kwargs: :class:`dict <python:dict>` :param options_kwargs: An optional :class:`dict <python:dict>` containing keyword arguments that should be used when instantiating the :class:`HighchartsOptions` instance. Defaults to :obj:`None <python:None>`. .. warning:: If ``options_kwargs`` contains a ``series`` key, the ``series`` value will be *overwritten*. The ``series`` value will be created from the data in ``df``. :type options_kwargs: :class:`dict <python:dict>` or :obj:`None <python:None>` :param chart_kwargs: An optional :class:`dict <python:dict>` containing keyword arguments that should be used when instantiating the :class:`Chart` instance. Defaults to :obj:`None <python:None>`. .. warning:: If ``chart_kwargs`` contains an ``options`` key, ``options`` will be *overwritten*. The ``options`` value will be created from the ``options_kwargs`` and the data in ``df`` instead. :type chart_kwargs: :class:`dict <python:dict>` or :obj:`None <python:None>` :returns: A :class:`Chart <highcharts_core.chart.Chart>` instance with its data populated from the data in ``df``. :rtype: :class:`Chart <highcharts_core.chart.Chart>` :raises HighchartsPySparkDeserializationError: if ``property_map`` references a column that does not exist in the data frame :raises HighchartsDependencyError: if `PySpark <https://spark.apache.org/docs/latest/api/python/>`_ is not available in the runtime environment """ chart_kwargs = validators.dict(chart_kwargs, allow_empty = True) or {} options = cls._get_options_obj(series_type, options_kwargs) series_cls = SERIES_CLASSES.get(series_type, None) series = series_cls.from_pyspark(df, property_map, series_kwargs) options = HighchartsOptions(**options_kwargs) options.series = [series] instance = cls(**chart_kwargs) instance.options = options return instance
[docs] @classmethod def from_options(cls, options, chart_kwargs = None): """Create a :class:`Chart <highcharts_maps.chart.Chart>` instance from a :class:`HighchartsOptions <highcharts_maps.options.HighchartsOptions>` or :class:`HighchartsMapsOptions <highcharts_maps.options.HighchartsMapsOptions>` object. :param options: The configuration options to use to instantiate the chart. :type options: :class:`HighchartsOptions <highcharts_core.options.HighchartsOptions>` or related or coercable :param chart_kwargs: An optional :class:`dict <python:dict>` containing keyword arguments that should be used when instantiating the instance. Defaults to :obj:`None <python:None>`. .. warning:: If ``chart_kwargs`` contains an ``options`` key, ``options`` will be *overwritten* by the contents of ``options``. :type chart_kwargs: :class:`dict <python:dict>` or :obj:`None <python:None>` :returns: The :class:`Chart <highcharts_core.chart.Chart>` instance :rtype: :class:`Chart <highcharts_core.chart.Chart>` """ chart_kwargs = validators.dict(chart_kwargs, allow_empty = True) or {} if checkers.is_type(options, 'HighchartsMapsOptions'): options = options chart_kwargs['is_maps_chart'] = True elif checkers.is_type(options, 'HighchartsOptions'): options = options elif ('mapNavigation' in options or 'map_navigation' in options or 'mapView' in options or 'map_view' in options): options = validate_types(options, HighchartsMapsOptions) chart_kwargs['is_maps_chart'] = True else: options = validate_types(options, HighchartsOptions) instance = cls(**chart_kwargs) instance.options = options return instance
@property def is_async(self) -> bool: """Read-only property which indicates whether the data visualization should be rendered using asynchronous logic. .. note:: This property will only return ``True`` if one or more series rely on :class:`AsyncMapData <highcharts_maps.options.series.data.map_data.AsyncMapData>` :rtype: :class:`bool <python:bool>` """ if not self.options or not self.options.series: return False if self.options.chart and self.options.chart.is_async: return True for series in self.options.series: if hasattr(series, 'is_async') and series.is_async: return True return False @property def uses_custom_projection(self) -> bool: """Read-only property which indicates whether the map visualization applies a custom projection. .. note:: This property will only return ``True`` if the ``options.map_views.projection.custom`` property is set. :rtype: :class:`bool <python:bool>` """ if (not self.options or not hasattr(self.options, 'map_views') or not self.options.map_views or not self.options.map_views.projection): return False return self.options.map_views.projection.custom is not None
[docs] def set_map_data(self, map_data): """Sets the default :term:`map geometries <map geometry>` for the chart. :param map_data: The :term:`map geometries <map geometry>` to set. Accepts: * :class:`MapData <highcharts_maps.options.series.data.map_data.MapData>` * :class:`AsyncMapData <highcharts_maps.options.series.data.map_data.AsyncMapData>` * :class:`VariableName <highcharts_maps.utility_classes.javascript_functions.VariableName>` * :class:`GeoJSONBase <highcharts_maps.utility_classes.geojson.GeoJSONBase>` or descendant * :class:`Topology <highcharts_maps.utility_classes.topojson.Topology>` * a :class:`str <python:str>` URL, which will be coerced to :class:`AsyncMapData <highcharts_maps.options.series.data.map_data.AsyncMapData>` :type map_data: :class:`MapData <highcharts_maps.options.series.data.map_data.MapData>` or :class:`AsyncMapData <highcharts_maps.options.series.data.map_data.AsyncMapData>` or :obj:`None <python:None>` """ if self.options.chart: self.options.chart.map = map_data else: chart_options = ChartOptions(map = map_data) if self.options: self.options.chart = chart_options else: self.options = HighchartsMapsOptions(chart = chart_options)
[docs] @classmethod def from_map_data(cls, map_data, options_kwargs = None, chart_kwargs = None): """Create a :class:`Chart <highcharts_maps.chart.Chart>` instance from a :class:`MapData <highcharts_maps.options.series.data.map_data.MapData>` or :class:`AsyncMapData <highcharts_maps.options.series.data.map_data.AsyncMapData>` object. :param map_data: The :term:`map geometries <map geometry>` to set. Accepts: * :class:`MapData <highcharts_maps.options.series.data.map_data.MapData>` * :class:`AsyncMapData <highcharts_maps.options.series.data.map_data.AsyncMapData>` * :class:`VariableName <highcharts_maps.utility_classes.javascript_functions.VariableName>` * :class:`GeoJSONBase <highcharts_maps.utility_classes.geojson.GeoJSONBase>` or descendant * :class:`Topology <highcharts_maps.utility_classes.topojson.Topology>` * a :class:`str <python:str>` URL, which will be coerced to :class:`AsyncMapData <highcharts_maps.options.series.data.map_data.AsyncMapData>` :type map_data: :class:`MapData <highcharts_maps.options.series.data.map_data.MapData>` or :class:`AsyncMapData <highcharts_maps.options.series.data.map_data.AsyncMapData>` or :obj:`None <python:None>` :param options_kwargs: An optional :class:`dict <python:dict>` containing keyword arguments that should be used when instanting the options for the :class:`Chart` instance. Defaults to :obj:`None <python:None>` .. warning:: If ``options_kwargs`` contains a ``chart.map`` setting, that value will be *overwritten* by the contents of ``map_data``. :type options_kwargs: :class:`dict <python:dict>` or :obj:`None <python:None>` :param chart_kwargs: An optional :class:`dict <python:dict>` containing keyword arguments that should be used when instantiating the instance. Defaults to :obj:`None <python:None>`. .. warning:: If ``chart_kwargs`` contains an ``options`` setting, that value will be *overwritten* by the options implied by ``options_kwargs`` :type chart_kwargs: :class:`dict <python:dict>` or :obj:`None <python:None>` :returns: The :class:`Chart <highcharts_core.chart.Chart>` instance :rtype: :class:`Chart <highcharts_core.chart.Chart>` """ options_kwargs = validators.dict(options_kwargs, allow_empty = True) or {} chart_kwargs = validators.dict(chart_kwargs, allow_empty = True) or {} chart_kwargs['is_maps_chart'] = True options = HighchartsMapsOptions(**options_kwargs) if not options.chart: options.chart = ChartOptions() options.chart.map_data = map_data instance = cls(**chart_kwargs) instance.options = options return instance
[docs] def set_custom_projection(self, projection): """Applies a custom map :term:`projection` to the chart. :param projection: The custom :term:`projection` definition to apply. :type projection: :class:`CustomProjection <highcharts_maps.utility_classes.projections.CustomProjection>` .. seealso:: * :ref:`Using Highcharts Maps for Python <using>` > :ref:`Using Custom Projections <custom_projections>` """ projection = validate_types(projection, CustomProjection) if not self.options: self.options = HighchartsMapsOptions() if not self.options.map_view: self.options.map_view = MapViewOptions() if not self.options.map_view.projection: self.options.map_view.projection = ProjectionOptions() self.options.map_view.projection.custom = projection