<commit_before>
import collections

from six import string_types
import ipywidgets as widgets
from traitlets import Unicode, Int, List, observe, HasTraits, Float

import gmaps.geotraitlets as geotraitlets
import gmaps.bounds as bounds

from .maps import DEFAULT_CENTER

__all__ = ["Symbol", "Marker", "Markers", "marker_layer", "symbol_layer"]


class _BaseMarkerMixin(HasTraits):
    _view_module = Unicode("jupyter-gmaps").tag(sync=True)
    _model_module = Unicode("jupyter-gmaps").tag(sync=True)
    location = geotraitlets.Point(DEFAULT_CENTER).tag(sync=True)
    hover_text = Unicode("").tag(sync=True)
    info_html = Unicode("").tag(sync=True)


class Symbol(_BaseMarkerMixin, widgets.Widget):
    """
    Class representing a single symbol.

    Symbols are like markers, but the point is represented by
    an SVG symbol, rather than the default inverted droplet.
    Symbols should be added to the map via the 'Markers'
    widget.
    """
    _view_name = Unicode("SymbolView").tag(sync=True)
    _model_name = Unicode("SymbolModel").tag(sync=True)

    fill_color = geotraitlets.ColorAlpha(
        allow_none=True, default_value=None
    ).tag(sync=True)
    fill_opacity = Float(min=0.0, max=1.0, default_value=1.0).tag(sync=True)
    stroke_color = geotraitlets.ColorAlpha(
        allow_none=True, default_value=None
    ).tag(sync=True)
    stroke_opacity = Float(min=0.0, max=1.0, default_value=1.0).tag(sync=True)
    scale = Int(
        default_value=4, allow_none=True, min=1
    ).tag(sync=True)


class Marker(_BaseMarkerMixin, widgets.Widget):
    """
    Class representing a marker.

    Markers should be added to the map via the 'Markers'
    widget.
    """
    _view_name = Unicode("MarkerView").tag(sync=True)
    _model_name = Unicode("MarkerModel").tag(sync=True)
    label = Unicode("").tag(sync=True)


class Markers(widgets.Widget):
    has_bounds = True
    _view_name = Unicode("MarkerLayerView").tag(sync=True)
    _view_module = Unicode("jupyter-gmaps").tag(sync=True)
    _model_name = Unicode("MarkerLayerModel").tag(sync=True)
    _model_module = Unicode("jupyter-gmaps").tag(sync=True)

    markers = List(minlen=1).tag(sync=True,  **widgets.widget_serialization)
    data_bounds = List().tag(sync=True)

    @observe("markers")
    def _calc_bounds(self, change):
        markers = change["new"]
        locations = [marker.location for marker in markers]
        latitudes = [location[0] for location in locations]
        longitudes = [location[1] for location in locations]
        min_latitude, max_latitude = bounds.latitude_bounds(latitudes)
        min_longitude, max_longitude = bounds.longitude_bounds(longitudes)
        self.data_bounds = [
            (min_latitude, min_longitude),
            (max_latitude, max_longitude)
        ]


def _is_atomic(elem):
    return (
        isinstance(elem, string_types) or
        not isinstance(elem, collections.Iterable)
    )


def _is_color_atomic(color):
    """
    Determine whether the argument is a singe color or an iterable of colors
    """
    if isinstance(color, string_types):
        is_atomic = True
    elif isinstance(color, collections.Sequence):
        if isinstance(color[0], string_types):
            is_atomic = False
        elif isinstance(color[0], (int, float)) and len(color) in (3, 4):
            is_atomic = True
        else:
            is_atomic = False
    else:
        is_atomic = True
    return is_atomic


def _merge_option_dicts(option_dicts):
    """
    Create a list of options for marker and symbol layers

    This helper function takes a dictionary of (key -> list) and
    returns a list of dictionaries of (key -> value).
    """
    option_values_lengths = [
        len(option_values) for option_values in option_dicts.values()
    ]
    # assert all the list values are the same length
    number_items = option_values_lengths[0]
    assert all(
        length == number_items
        for length in option_values_lengths
    )
    option_lists = []
    for item in range(number_items):
        item_options = {
            option_name: option_values[item]
            for (option_name, option_values)
            in option_dicts.items()
        }
        option_lists.append(item_options)
    return option_lists


def _symbol_layer_options(
        locations, hover_text, fill_color, fill_opacity,
        stroke_color, stroke_opacity, scale, info_html):
    number_markers = len(locations)
    if _is_atomic(hover_text):
        hover_text = [hover_text] * number_markers
    if _is_atomic(info_html):
        info_html = [info_html] * number_markers
    if _is_atomic(scale):
        scale = [scale] * number_markers
    if _is_color_atomic(fill_color):
        fill_color = [fill_color] * number_markers
    if _is_color_atomic(stroke_color):
        stroke_color = [stroke_color] * number_markers
    if _is_atomic(stroke_opacity):
        stroke_opacity = [stroke_opacity] * number_markers
    if _is_atomic(fill_opacity):
        fill_opacity = [fill_opacity] * number_markers
    options = {
        "location": locations,
        "hover_text": hover_text,
        "info_html": info_html,
        "fill_color": fill_color,
        "stroke_color": stroke_color,
        "scale": scale,
        "stroke_opacity": stroke_opacity,
        "fill_opacity": fill_opacity
    }
    return _merge_option_dicts(options)


def _marker_layer_options(locations, hover_text, label, info_html):
    number_markers = len(locations)
    if _is_atomic(hover_text):
        hover_text = [hover_text] * number_markers
    if _is_atomic(info_html):
        info_html = [info_html] * number_markers
    if _is_atomic(label):
        label = [label] * number_markers
    options = {
        "location": locations,
        "hover_text": hover_text,
        "info_html": info_html,
        "label": label
    }
    return _merge_option_dicts(options)


def symbol_layer(
        locations, hover_text="", info_html='', fill_color=None,
        fill_opacity=1.0,
        stroke_color=None, stroke_opacity=1.0, scale=3):
    """
    Symbol layer

    Add this layer to a ``Map`` instance to draw symbols<commit_msg>Fix: Replace single quotes by double quotes<commit_after>
import collections

from six import string_types
import ipywidgets as widgets
from traitlets import Unicode, Int, List, observe, HasTraits, Float

import gmaps.geotraitlets as geotraitlets
import gmaps.bounds as bounds

from .maps import DEFAULT_CENTER

__all__ = ["Symbol", "Marker", "Markers", "marker_layer", "symbol_layer"]


class _BaseMarkerMixin(HasTraits):
    _view_module = Unicode("jupyter-gmaps").tag(sync=True)
    _model_module = Unicode("jupyter-gmaps").tag(sync=True)
    location = geotraitlets.Point(DEFAULT_CENTER).tag(sync=True)
    hover_text = Unicode("").tag(sync=True)
    info_html = Unicode("").tag(sync=True)


class Symbol(_BaseMarkerMixin, widgets.Widget):
    """
    Class representing a single symbol.

    Symbols are like markers, but the point is represented by
    an SVG symbol, rather than the default inverted droplet.
    Symbols should be added to the map via the 'Markers'
    widget.
    """
    _view_name = Unicode("SymbolView").tag(sync=True)
    _model_name = Unicode("SymbolModel").tag(sync=True)

    fill_color = geotraitlets.ColorAlpha(
        allow_none=True, default_value=None
    ).tag(sync=True)
    fill_opacity = Float(min=0.0, max=1.0, default_value=1.0).tag(sync=True)
    stroke_color = geotraitlets.ColorAlpha(
        allow_none=True, default_value=None
    ).tag(sync=True)
    stroke_opacity = Float(min=0.0, max=1.0, default_value=1.0).tag(sync=True)
    scale = Int(
        default_value=4, allow_none=True, min=1
    ).tag(sync=True)


class Marker(_BaseMarkerMixin, widgets.Widget):
    """
    Class representing a marker.

    Markers should be added to the map via the 'Markers'
    widget.
    """
    _view_name = Unicode("MarkerView").tag(sync=True)
    _model_name = Unicode("MarkerModel").tag(sync=True)
    label = Unicode("").tag(sync=True)


class Markers(widgets.Widget):
    has_bounds = True
    _view_name = Unicode("MarkerLayerView").tag(sync=True)
    _view_module = Unicode("jupyter-gmaps").tag(sync=True)
    _model_name = Unicode("MarkerLayerModel").tag(sync=True)
    _model_module = Unicode("jupyter-gmaps").tag(sync=True)

    markers = List(minlen=1).tag(sync=True,  **widgets.widget_serialization)
    data_bounds = List().tag(sync=True)

    @observe("markers")
    def _calc_bounds(self, change):
        markers = change["new"]
        locations = [marker.location for marker in markers]
        latitudes = [location[0] for location in locations]
        longitudes = [location[1] for location in locations]
        min_latitude, max_latitude = bounds.latitude_bounds(latitudes)
        min_longitude, max_longitude = bounds.longitude_bounds(longitudes)
        self.data_bounds = [
            (min_latitude, min_longitude),
            (max_latitude, max_longitude)
        ]


def _is_atomic(elem):
    return (
        isinstance(elem, string_types) or
        not isinstance(elem, collections.Iterable)
    )


def _is_color_atomic(color):
    """
    Determine whether the argument is a singe color or an iterable of colors
    """
    if isinstance(color, string_types):
        is_atomic = True
    elif isinstance(color, collections.Sequence):
        if isinstance(color[0], string_types):
            is_atomic = False
        elif isinstance(color[0], (int, float)) and len(color) in (3, 4):
            is_atomic = True
        else:
            is_atomic = False
    else:
        is_atomic = True
    return is_atomic


def _merge_option_dicts(option_dicts):
    """
    Create a list of options for marker and symbol layers

    This helper function takes a dictionary of (key -> list) and
    returns a list of dictionaries of (key -> value).
    """
    option_values_lengths = [
        len(option_values) for option_values in option_dicts.values()
    ]
    # assert all the list values are the same length
    number_items = option_values_lengths[0]
    assert all(
        length == number_items
        for length in option_values_lengths
    )
    option_lists = []
    for item in range(number_items):
        item_options = {
            option_name: option_values[item]
            for (option_name, option_values)
            in option_dicts.items()
        }
        option_lists.append(item_options)
    return option_lists


def _symbol_layer_options(
        locations, hover_text, fill_color, fill_opacity,
        stroke_color, stroke_opacity, scale, info_html):
    number_markers = len(locations)
    if _is_atomic(hover_text):
        hover_text = [hover_text] * number_markers
    if _is_atomic(info_html):
        info_html = [info_html] * number_markers
    if _is_atomic(scale):
        scale = [scale] * number_markers
    if _is_color_atomic(fill_color):
        fill_color = [fill_color] * number_markers
    if _is_color_atomic(stroke_color):
        stroke_color = [stroke_color] * number_markers
    if _is_atomic(stroke_opacity):
        stroke_opacity = [stroke_opacity] * number_markers
    if _is_atomic(fill_opacity):
        fill_opacity = [fill_opacity] * number_markers
    options = {
        "location": locations,
        "hover_text": hover_text,
        "info_html": info_html,
        "fill_color": fill_color,
        "stroke_color": stroke_color,
        "scale": scale,
        "stroke_opacity": stroke_opacity,
        "fill_opacity": fill_opacity
    }
    return _merge_option_dicts(options)


def _marker_layer_options(locations, hover_text, label, info_html):
    number_markers = len(locations)
    if _is_atomic(hover_text):
        hover_text = [hover_text] * number_markers
    if _is_atomic(info_html):
        info_html = [info_html] * number_markers
    if _is_atomic(label):
        label = [label] * number_markers
    options = {
        "location": locations,
        "hover_text": hover_text,
        "info_html": info_html,
        "label": label
    }
    return _merge_option_dicts(options)


def symbol_layer(
        locations, hover_text="", info_html="", fill_color=None,
        fill_opacity=1.0,
        stroke_color=None, stroke_opacity=1.0, scale=3):
    """
    Symbol layer

    Add this layer to a ``Map`` instance to draw symbols
{'fraction_non_alphanumeric': 0.10059987029831388, 'fraction_numerical': 0.004377431906614786, 'mean_word_length': 3.2235535775419377, 'pattern_counts': {'":': 24, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3342110', 'n_tokens_mistral': 3897, 'n_tokens_neox': 3809, 'n_words': 885}