import numpy as np
import pytest

from pandas import DataFrame, Index, IndexSlice, MultiIndex, Series, concat, date_range
import pandas._testing as tm
import pandas.core.common as com

@pytest.fixture
def four_level_index_dataframe():
    arr = np.array(
        [
            [-0.5109, -2.3358, -0.4645, 0.05076, 0.364],
            [0.4473, 1.4152, 0.2834, 1.00661, 0.1744],
            [-0.6662, -0.5243, -0.358, 0.89145, 2.5838],
        ]
    )
    index = MultiIndex(
        levels=[["a", "x"], ["b", "q"], [10.0032, 20.0, 30.0], [3, 4, 5]],
        codes=[[0, 0, 1], [0, 1, 1], [0, 1, 2], [2, 1, 0]],
        names=["one", "two", "three", "four"],
    )
    return DataFrame(arr, index=index, columns=list("ABCDE"))

@pytest.mark.parametrize(
    "key, level, exp_arr, exp_index",
    [
        ("a", "lvl0", lambda x: x[:, 0:2], Index(["bar", "foo"], name="lvl1")),
        ("foo", "lvl1", lambda x: x[:, 1:2], Index(["a"], name="lvl0")),
    ],
)
def test_xs_named_levels_axis_eq_1(key, level, exp_arr, exp_index):
    # see gh-2903
    arr = np.random.randn(4, 4)
    index = MultiIndex(
        levels=[["a", "b"], ["bar", "foo", "hello", "world"]],
        codes=[[0, 0, 1, 1], [0, 1, 2, 3]],
        names=["lvl0", "lvl1"],
    )
    df = DataFrame(arr, columns=index)
    result = df.xs(key, level=level, axis=1)
    expected = DataFrame(exp_arr(arr), columns=exp_index)
    tm.assert_frame_equal(result, expected)

def test_xs_values(multiindex_dataframe_random_data):
    df = multiindex_dataframe_random_data
    result = df.xs(("bar", "two")).values
    expected = df.values[4]
    tm.assert_almost_equal(result, expected)

def test_xs_loc_equality(multiindex_dataframe_random_data):
    df = multiindex_dataframe_random_data
    result = df.xs(("bar", "two"))
    expected = df.loc[("bar", "two")]
    tm.assert_series_equal(result, expected)

def test_xs_missing_values_in_index():
    # see gh-6574
    # missing values in returned index should be preserved
    acc = [
        ("a", "abcde", 1),
        ("b", "bbcde", 2),
        ("y", "yzcde", 25),
        ("z", "xbcde", 24),
        ("z", None, 26),
        ("z", "zbcde", 25),
        ("z", "ybcde", 26),
    ]
    df = DataFrame(acc, columns=["a1", "a2", "cnt"]).set_index(["a1", "a2"])
    expected = DataFrame(
        {"cnt": [24, 26, 25, 26]},
        index=Index(["xbcde", np.nan, "zbcde", "ybcde"], name="a2"),
    )

    result = df.xs("z", level="a1")
    tm.assert_frame_equal(result, expected)

@pytest.mark.parametrize("key, level", [("one", "second"), (["one"], ["second"])])
def test_xs_with_duplicates(key, level, multiindex_dataframe_random_data):
    # see gh-13719
    frame = multiindex_dataframe_random_data
    df = concat([frame] * 2)
    assert df.index.is_unique is False
    expected = concat([frame.xs("one", level="second")] * 2)

    result = df.xs(key, level=level)
    tm.assert_frame_equal(result, expected)

def test_xs_level(multiindex_dataframe_random_data):
    df = multiindex_dataframe_random_data
    result = df.xs("two", level="second")
    expected = df[df.index.get_level_values(1) == "two"]
    expected.index = Index(["foo", "bar", "baz", "qux"], name="first")
    tm.assert_frame_equal(result, expected)

def test_xs_level_eq_2():
    arr = np.random.randn(3, 5)
    index = MultiIndex(
        levels=[["a", "p", "x"], ["b", "q", "y"], ["c", "r", "z"]],
        codes=[[2, 0, 1], [2, 0, 1], [2, 0, 1]],
    )
    df = DataFrame(arr, index=index)
    expected = DataFrame(arr[1:2], index=[["a"], ["b"]])
    result = df.xs("c", level=2)
    tm.assert_frame_equal(result, expected)

@pytest.mark.parametrize(
    "indexer",
    [
        lambda df: df.xs(("a", 4), level=["one", "four"]),
        lambda df: df.xs("a").xs(4, level="four"),
    ],
)
def test_xs_level_multiple(indexer, four_level_index_dataframe):
    df = four_level_index_dataframe
    expected_values = [[0.4473, 1.4152, 0.2834, 1.00661, 0.1744]]
    expected_index = MultiIndex(
        levels=[["q"], [20.0]], codes=[[0], [0]], names=["two", "three"]
    )
    expected = DataFrame(expected_values, index=expected_index, columns=list("ABCDE"))
    result = indexer(df)
    tm.assert_frame_equal(result, expected)

def test_xs_setting_with_copy_error(multiindex_dataframe_random_data):
    # this is a copy in 0.14
    df = multiindex_dataframe_random_data
    result = df.xs("two", level="second")

    # setting this will give a SettingWithCopyError
    # as we are trying to write a view
    msg = "A value is trying to be set on a copy of a slice from a DataFrame"
    with pytest.raises(com.SettingWithCopyError, match=msg):
        result[:] = 10

def test_xs_setting_with_copy_error_multiple(four_level_index_dataframe):
    # this is a copy in 0.14
    df = four_level_index_dataframe
    result = df.xs(("a", 4), level=["one", "four"])

    # setting this will give a SettingWithCopyError
    # as we are trying to write a view
    msg = "A value is trying to be set on a copy of a slice from a DataFrame"
    with pytest.raises(com.SettingWithCopyError, match=msg):
        result[:] = 10

def test_xs_integer_key():
    # see gh-2107
    dates = range(20111201, 20111205)
    ids = list("abcde")
    index = MultiIndex.from_product([dates, ids], names=["date", "secid"])
    df = DataFrame(np.random.randn(len(index), 3), index, ["X", "Y", "Z"])

    result = df.xs(20111201, level="date")
    expected = df.loc[20111201, :]
    tm.assert_frame_equal(result, expected)

@pytest.mark.parametrize(
    "indexer", [lambda df: df.xs("a", level=0), lambda df: df.xs("a")]
)
def test_xs_level0(indexer, four_level_index_dataframe):
    df = four_level_index_dataframe
    expected_values = [
        [-0.5109, -2.3358, -0.4645, 0.05076, 0.364],
        [0.4473, 1.4152, 0.2834, 1.00661, 0.1744],
    ]
    expected_index = MultiIndex(
        levels=[["b", "q"], [10.0032, 20.0], [4, 5]],
        codes=[[0, 1], [0, 1], [1, 0]],
        names=["two", "three", "four"],
    )
    expected = DataFrame(expected_values, index=expected_index, columns=list("ABCDE"))

    result = indexer(df)
    tm.assert_frame_equal(result, expected)

def test_xs_level_series(multiindex_dataframe_random_data):
    # this test is not explicitly testing .xs functionality
    # TODO: move to another module or refactor
    df = multiindex_dataframe_random_data
    s = df["A"]
    result = s[:, "two"]
    expected = df.xs("two", level=1)["A"]
    tm.assert_series_equal(result, expected)

def test_xs_level_series_ymd(multiindex_year_month_day_dataframe_random_data):
    # this test is not explicitly testing .xs functionality
    # TODO: move to another module or refactor
    df = multiindex_year_month_day_dataframe_random_data
    s = df["A"]
    result = s[2000, 5]
    expected = df.loc[2000, 5]["A"]
    tm.assert_series_equal(result, expected)

def test_xs_level_series_slice_not_implemented(
    multiindex_year_month_day_dataframe_random_data,
):
    # this test is not explicitly testing .xs functionality
    # TODO: move to another module or refactor
    # not implementing this for now
    df = multiindex_year_month_day_dataframe_random_data
    s = df["A"]

    msg = r"\(2000, slice\(3, 4, None\)\)"
    with pytest.raises(TypeError, match=msg):
        s[2000, 3:4]

def test_xs_IndexSlice_argument_not_implemented():
    # GH 35301

    index = MultiIndex(
        levels=[[("foo", "bar", 0), ("foo", "baz", 0), ("foo", "qux", 0)], [0, 1]],
        codes=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]],
    )

    series = Series(np.random.randn(6), index=index)
    frame = DataFrame(np.random.randn(6, 4), index=index)

    msg = (
        "Expected label or tuple of labels, got "
        r"\(\('foo', 'qux', 0\), slice\(None, None, None\)\)"
    )
    with pytest.raises(TypeError, match=msg):
        frame.xs(IndexSlice[("foo", "qux", 0), :])
    with pytest.raises(TypeError, match=msg):
        series.xs(IndexSlice[("foo", "qux", 0), :])

def test_series_getitem_multiindex_xs():
    # GH6258
    dt = list(date_range("20130903", periods=3))
    idx = MultiIndex.from_product([list("AB"), dt])
    s = Series([1, 3, 4, 1, 3, 4], index=idx)
    expected = Series([1, 1], index=list("AB"))

    result = s.xs("20130903", level=1)
    tm.assert_series_equal(result, expected)

def test_series_getitem_multiindex_xs_by_label():
    # GH5684
    idx = MultiIndex.from_tuples(
        [("a", "one"), ("a", "two"), ("b", "one"), ("b", "two")]
    )
    s = Series([1, 2, 3, 4], index=idx)
    return_value = s.index.set_names(["L1", "L2"], inplace=True)
    assert return_value is None
    expected = Series([1, 3], index=["a", "b"])
    return_value = expected.index.set_names(["L1"], inplace=True)
    assert return_value is None

    result = s.xs("one", level="L2")
    tm.assert_series_equal(result, expected)

def test_xs_levels_raises():
    df = DataFrame({"A": [1, 2, 3]})

    msg = "Index must be a MultiIndex"
    with pytest.raises(TypeError, match=msg):
        df.xs(0, level="as")

    s = df.A
    with pytest.raises(TypeError, match=msg):
        s.xs(0, level="as")


# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Multi-output tests."""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import random

import numpy as np

import tensorflow as tf
from tensorflow.contrib.learn.python import learn
from tensorflow.contrib.learn.python.learn.estimators._sklearn import mean_squared_error

class MultiOutputTest(tf.test.TestCase):
  """Multi-output tests."""

  def testMultiRegression(self):
    random.seed(42)
    rng = np.random.RandomState(1)
    x = np.sort(200 * rng.rand(100, 1) - 100, axis=0)
    y = np.array([np.pi * np.sin(x).ravel(), np.pi * np.cos(x).ravel()]).T
    regressor = learn.TensorFlowLinearRegressor(
        feature_columns=learn.infer_real_valued_columns_from_input(x),
        learning_rate=0.01, target_dimension=2)
    regressor.fit(x, y)
    score = mean_squared_error(regressor.predict(x), y)
    self.assertLess(score, 10, "Failed with score = {0}".format(score))

if __name__ == "__main__":
  tf.test.main()


# (C) British Crown Copyright 2014 - 2016, Met Office
#
# This file is part of cartopy.
#
# cartopy is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# cartopy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with cartopy.  If not, see <https://www.gnu.org/licenses/>.
"""This module contains tools for handling tick marks in cartopy."""

from __future__ import (absolute_import, division, print_function)

from matplotlib.ticker import Formatter

import cartopy.crs as ccrs
from cartopy.mpl.geoaxes import GeoAxes

class _PlateCarreeFormatter(Formatter):
    """
    Base class for formatting ticks on geographical axes using a
    rectangular projection (e.g. Plate Carree, Mercator).

    """

    _target_projection = ccrs.PlateCarree()

    def __init__(self, degree_symbol=u'\u00B0', number_format='g',
                 transform_precision=1e-8):
        """
        Base class for simpler implementation of specialised formatters
        for latitude and longitude axes.

        """
        self._degree_symbol = degree_symbol
        self._number_format = number_format
        self._transform_precision = transform_precision

    def __call__(self, value, pos=None):
        if not isinstance(self.axis.axes, GeoAxes):
            raise TypeError("This formatter can only be "
                            "used with cartopy axes.")
        # We want to produce labels for values in the familiar Plate Carree
        # projection, so convert the tick values from their own projection
        # before formatting them.
        source = self.axis.axes.projection
        if not isinstance(source, (ccrs._RectangularProjection,
                                   ccrs.Mercator)):
            raise TypeError("This formatter cannot be used with "
                            "non-rectangular projections.")
        projected_value = self._apply_transform(value, self._target_projection,
                                                source)
        # Round the transformed value using a given precision for display
        # purposes. Transforms can introduce minor rounding errors that make
        # the tick values look bad, these need to be accounted for.
        f = 1. / self._transform_precision
        projected_value = round(f * projected_value) / f
        # Return the formatted values, the formatter has both the re-projected
        # tick value and the original axis value available to it.
        return self._format_value(projected_value, value)

    def _format_value(self, value, original_value):
        hemisphere = self._hemisphere(value, original_value)
        fmt_string = u'{value:{number_format}}{degree}{hemisphere}'
        return fmt_string.format(value=abs(value),
                                 number_format=self._number_format,
                                 degree=self._degree_symbol,
                                 hemisphere=hemisphere)

    def _apply_transform(self, value, target_proj, source_crs):
        """
        Given a single value, a target projection and a source CRS,
        transforms the value from the source CRS to the target
        projection, returning a single value.

        """
        raise NotImplementedError("A subclass must implement this method.")

    def _hemisphere(self, value, value_source_crs):
        """
        Given both a tick value in the Plate Carree projection and the
        same value in the source CRS returns a string indicating the
        hemisphere that the value is in.

        Must be over-ridden by the derived class.

        """
        raise NotImplementedError("A subclass must implement this method.")

class LatitudeFormatter(_PlateCarreeFormatter):
    """Tick formatter for latitude axes."""
    def __init__(self, degree_symbol=u'\u00B0', number_format='g',
                 transform_precision=1e-8):
        """
        Tick formatter for a latitude axis.

        The axis must be part of an axes defined on a rectangular
        projection (e.g. Plate Carree, Mercator).

        .. note::

           A formatter can only be used for one axis. A new formatter
           must be created for every axis that needs formatted labels.

        Kwargs:

        * degree_symbol (string):
            The character(s) used to represent the degree symbol in the
            tick labels. Defaults to u'\u00B0' which is the unicode
            degree symbol. Can be an empty string if no degree symbol is
            desired.

        * number_format (string):
            Format string to represent the tick values. Defaults to 'g'.

        * transform_precision (float):
            Sets the precision (in degrees) to which transformed tick
            values are rounded. The default is 1e-7, and should be
            suitable for most use cases. To control the appearance of
            tick labels use the *number_format* keyword.

        Examples:

        Label latitudes from -90 to 90 on a Plate Carree projection::

            ax = plt.axes(projection=PlateCarree())
            ax.set_global()
            ax.set_yticks([-90, -60, -30, 0, 30, 60, 90],
                          crs=ccrs.PlateCarree())
            lat_formatter = LatitudeFormatter()
            ax.yaxis.set_major_formatter(lat_formatter)

        Label latitudes from -80 to 80 on a Mercator projection, this
        time omitting the degree symbol::

            ax = plt.axes(projection=Mercator())
            ax.set_global()
            ax.set_yticks([-90, -60, -30, 0, 30, 60, 90],
                          crs=ccrs.PlateCarree())
            lat_formatter = LatitudeFormatter(degree_symbol='')
            ax.yaxis.set_major_formatter(lat_formatter)

        """
        super(LatitudeFormatter, self).__init__(
            degree_symbol=degree_symbol,
            number_format=number_format,
            transform_precision=transform_precision)

    def _apply_transform(self, value, target_proj, source_crs):
        return target_proj.transform_point(0, value, source_crs)[1]

    def _hemisphere(self, value, value_source_crs):
        if value > 0:
            hemisphere = 'N'
        elif value < 0:
            hemisphere = 'S'
        else:
            hemisphere = ''
        return hemisphere

class LongitudeFormatter(_PlateCarreeFormatter):
    """Tick formatter for a longitude axis."""

    def __init__(self,
                 zero_direction_label=False,
                 dateline_direction_label=False,
                 degree_symbol=u'\u00B0',
                 number_format='g',
                 transform_precision=1e-8):
        """
        Create a formatter for longitude values.

        The axis must be part of an axes defined on a rectangular
        projection (e.g. Plate Carree, Mercator).

        .. note::

           A formatter can only be used for one axis. A new formatter
           must be created for every axis that needs formatted labels.

        Kwargs:

        * zero_direction_label (False | True):
            If *True* a direction label (E or W) will be drawn next to
            longitude labels with the value 0. If *False* then these
            labels will not be drawn. Defaults to *False* (no direction
            labels).

        * dateline_direction_label (False | True):
            If *True* a direction label (E or W) will be drawn next to
            longitude labels with the value 180. If *False* then these
            labels will not be drawn. Defaults to *False* (no direction
            labels).

        * degree_symbol (string):
            The symbol used to represent degrees. Defaults to u'\u00B0'
            which is the unicode degree symbol.

        * number_format (string):
            Format string to represent the longitude values. Defaults to
            'g'.

        * transform_precision (float):
            Sets the precision (in degrees) to which transformed tick
            values are rounded. The default is 1e-7, and should be
            suitable for most use cases. To control the appearance of
            tick labels use the *number_format* keyword.

        Examples:

        Label longitudes from -180 to 180 on a Plate Carree projection
        with a central longitude of 0::

            ax = plt.axes(projection=PlateCarree())
            ax.set_global()
            ax.set_xticks([-180, -120, -60, 0, 60, 120, 180],
                          crs=ccrs.PlateCarree())
            lon_formatter = LongitudeFormatter()
            ax.xaxis.set_major_formatter(lon_formatter)

        Label longitudes from 0 to 360 on a Plate Carree projection
        with a central longitude of 180::

            ax = plt.axes(projection=PlateCarree(central_longitude=180))
            ax.set_global()
            ax.set_xticks([0, 60, 120, 180, 240, 300, 360],
                          crs=ccrs.PlateCarree())
            ont_formatter = LongitudeFormatter()
            ax.xaxis.set_major_formatter(lon_formatter)

        """
        super(LongitudeFormatter, self).__init__(
            degree_symbol=degree_symbol,
            number_format=number_format,
            transform_precision=transform_precision)
        self._zero_direction_labels = zero_direction_label
        self._dateline_direction_labels = dateline_direction_label

    def _apply_transform(self, value, target_proj, source_crs):
        return target_proj.transform_point(value, 0, source_crs)[0]

    def _hemisphere(self, value, value_source_crs):
        # Perform basic hemisphere detection.
        if value < 0:
            hemisphere = 'W'
        elif value > 0:
            hemisphere = 'E'
        else:
            hemisphere = ''
        # Correct for user preferences:
        if value == 0 and self._zero_direction_labels:
            # Use the original tick value to determine the hemisphere.
            if value_source_crs < 0:
                hemisphere = 'E'
            else:
                hemisphere = 'W'
        if value in (-180, 180) and not self._dateline_direction_labels:
            hemisphere = ''
        return hemisphere


#!/usr/bin/env python

# This is a visualizer which pulls TPC-C benchmark results from the MySQL
# databases and visualizes them. Four graphs will be generated, latency graph on
# sinigle node and multiple nodes, and throughput graph on single node and
# multiple nodes.
#
# Run it without any arguments to see what arguments are needed.

import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +
                os.sep + 'tests/scripts/')

import time
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from voltdbclient import *

STATS_SERVER = 'volt2'

def COLORS(k):
    return (((k ** 3) % 255) / 255.0,
            ((k * 100) % 255) / 255.0,
            ((k * k) % 255) / 255.0)

MARKERS = ['+', '*', '<', '>', '^', '_',
           'D', 'H', 'd', 'h', 'o', 'p']

def get_stats(hostname, port, days):
    """Get statistics of all runs

    Example return value:
    { u'VoltKV': [ { 'lat95': 21,
                 'lat99': 35,
                 'nodes': 1,
                 'throughput': 104805,
                 'date': datetime object}],
      u'Voter': [ { 'lat95': 20,
                    'lat99': 47,
                    'nodes': 1,
                    'throughput': 66287,
                    'date': datetime object}]}
    """

    conn = FastSerializer(hostname, port)
    proc = VoltProcedure(conn, 'BestOfPeriod',
                         [FastSerializer.VOLTTYPE_SMALLINT])
    resp = proc.call([days])
    conn.close()

    # keyed on app name, value is a list of runs sorted chronologically
    stats = dict()
    run_stat_keys = ['nodes', 'date', 'tps', 'lat95', 'lat99']
    for row in resp.tables[0].tuples:
        app_stats = []
        if row[0] not in stats:
            stats[row[0]] = app_stats
        else:
            app_stats = stats[row[0]]
        run_stats = dict(zip(run_stat_keys, row[1:]))
        app_stats.append(run_stats)

    # sort each one
    for app_stats in stats.itervalues():
        app_stats.sort(key=lambda x: x['date'])

    return stats

class Plot:
    DPI = 100.0

    def __init__(self, title, xlabel, ylabel, filename, w, h):
        self.filename = filename
        self.legends = {}
        w = w == None and 800 or w
        h = h == None and 300 or h
        fig = plt.figure(figsize=(w / self.DPI, h / self.DPI),
                         dpi=self.DPI)
        self.ax = fig.add_subplot(111)
        self.ax.set_title(title)
        plt.xticks(fontsize=10)
        plt.yticks(fontsize=10)
        plt.ylabel(ylabel, fontsize=8)
        plt.xlabel(xlabel, fontsize=8)
        fig.autofmt_xdate()

    def plot(self, x, y, color, marker_shape, legend):
        self.ax.plot(x, y, linestyle="-", label=str(legend),
                     marker=marker_shape, markerfacecolor=color, markersize=4)

    def close(self):
        formatter = matplotlib.dates.DateFormatter("%b %d")
        self.ax.xaxis.set_major_formatter(formatter)
        plt.legend(prop={'size': 10}, loc=0)
        plt.savefig(self.filename, format="png", transparent=False,
                    bbox_inches="tight", pad_inches=0.2)

def plot(title, xlabel, ylabel, filename, nodes, width, height, data,
         data_type):
    plot_data = dict()
    for app, runs in data.iteritems():
        for v in runs:
            if v['nodes'] != nodes:
                continue

            if app not in plot_data:
                plot_data[app] = {'time': [], data_type: []}

            datenum = matplotlib.dates.date2num(v['date'])
            plot_data[app]['time'].append(datenum)

            if data_type == 'tps':
                value = v['tps']/v['nodes']
            else:
                value = v[data_type]
            plot_data[app][data_type].append(value)

    if len(plot_data) == 0:
        return

    i = 0
    pl = Plot(title, xlabel, ylabel, filename, width, height)
    sorted_data = sorted(plot_data.items(), key=lambda x: x[0])
    for k, v in sorted_data:
        pl.plot(v['time'], v[data_type], COLORS(i), MARKERS[i], k)
        i += 3

    pl.close()

def usage():
    print "Usage:"
    print "\t", sys.argv[0], "output_dir filename_base" \
        " [width] [height]"
    print
    print "\t", "width in pixels"
    print "\t", "height in pixels"

def main():
    if len(sys.argv) < 3:
        usage()
        exit(-1)

    if not os.path.exists(sys.argv[1]):
        print sys.argv[2], "does not exist"
        exit(-1)

    path = os.path.join(sys.argv[1], sys.argv[2])
    width = None
    height = None
    if len(sys.argv) >= 4:
        width = int(sys.argv[3])
    if len(sys.argv) >= 5:
        height = int(sys.argv[4])

    stats = get_stats(STATS_SERVER, 21212, 30)

    # Plot single node stats for all apps
    plot("Average Latency on Single Node", "Time", "Latency (ms)",
         path + "-latency-single.png", 1, width, height, stats, 'lat99')

    plot("Single Node Performance", "Time", "Throughput (txns/sec)",
         path + "-throughput-single.png", 1, width, height, stats, 'tps')

    # Plot 3 node stats for all apps
    plot("Average Latency on 3 Nodes", "Time", "Latency (ms)",
         path + "-latency-3.png", 3, width, height, stats, 'lat99')

    plot("3 Node Performance", "Time", "Throughput (txns/sec)",
         path + "-throughput-3.png", 3, width, height, stats, 'tps')

    # Plot 6 node stats for all apps
    plot("Average Latency on 6 Node", "Time", "Latency (ms)",
         path + "-latency-6.png", 6, width, height, stats, 'lat99')

    plot("6 Node Performance", "Time", "Throughput (txns/sec)",
         path + "-throughput-6.png", 6, width, height, stats, 'tps')

if __name__ == "__main__":
    main()


# Copyright (c) 2014, Georgios Is. Detorakis (gdetor@gmail.com) and
#                     Nicolas P. Rougier (nicolas.rougier@inria.fr)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# This file is part of the source code accompany the peer-reviewed article:
# [1] "Structure of Receptive Fields in a Computational Model of Area 3b of
# Primary Sensory Cortex", Georgios Is. Detorakis and Nicolas P. Rougier,
# Frontiers in Computational Neuroscience, 2014.
#
# This script applies all the filters and cleaning techniques to the ncRFs. You
# have to use this script before any further statistical analysis to the data.
import numpy as np
from matplotlib import rc
import matplotlib.pylab as plt
from scipy.stats.stats import pearsonr
from scipy.stats.mstats import gmean
from scipy.ndimage import gaussian_filter

def locate_noise( input ):
	n = input.shape[0]
	data = input.copy()

	count = 0
	for i in range( 1,n-1 ):
		for j in range( 1,n-1 ):
			if data[i,j] != 0:
				if data[i+1,j] != 0 and np.sign(data[i+1,j])==np.sign(data[i,j]):
					count += 1
				if data[i-1,j] != 0 and np.sign(data[i-1,j])==np.sign(data[i,j]):
					count += 1
				if data[i,j-1] != 0 and np.sign(data[i,j-1])==np.sign(data[i,j]):
					count += 1
				if data[i,j+1] != 0 and np.sign(data[i,j+1])==np.sign(data[i,j]):
					count += 1
				if count < 2:
					data[i,j] = 0
				count = 0
	return data

# Computing the area of the receptive fields according to Dicarlo's
# protocol described in article "Structure of Receptive Fields in area 3b...
def clear_data( RFs, n ):
    p = 25
    Z, T = [], []
    Noise = np.load( 'noise.npy' ).reshape(n*n,p,p)
    cRFs = np.zeros((n*n,p,p))
    for i in range( n ):
        for j in range( n ):
            RF = RFs[i,j,...]

            # WARNING : Centering the RF
            s0,s1 = np.unravel_index(np.argmax(RF),RF.shape)
            RF = np.roll(RF,13-s0,axis=0)
            RF = np.roll(RF,13-s1,axis=1)
            # WARNING : Centering the RF

            # RF += Noise[i*n+j]
            # RF = gaussian_filter( RF, sigma=2.2 )

            RF += 1.5*Noise[i*n+j]
            RF = gaussian_filter( RF, sigma=1.5 )

            abs_max = np.max( np.abs( RF ) )
            RF[np.where( ( ( RF < +0.10*abs_max ) & (RF>0) ) | ( ( RF > -0.10*abs_max ) & (RF < 0) ) ) ]=0
            RF = locate_noise( RF )
            cRFs[i*n+j,...] = RF
            exc = 50.0 * ( RF > 0).sum()/( p * p )
            inh = 50.0 * ( RF < 0).sum()/( p * p )
            Z.append([exc,inh])

    Z = np.array(Z)
    np.nan_to_num(Z)
    print '------ Excitatory ------- Inhibitory -------'
    print 'Minimum :', Z[:,0].min(), Z[:,1].min()
    print 'Maximum :', Z[:,0].max(), Z[:,1].max()
    print 'Mean :', np.mean( Z[:,0] ), np.mean( Z[:,1] )
    print 'Mean :', np.mean( np.log10(Z[:,0]) ), np.mean( np.log10(Z[:,1]) )
    print 'SD : ', np.std( np.log10(Z[:,0]) ), np.std( np.log10(Z[:,1]) )
    print 'GMean :', gmean( Z[:,0] ), gmean( Z[:,1] )
    print "Pearson cor: ", pearsonr( Z[:,0], np.abs(Z[:,1]) )
    return Z, cRFs

# Computing the SNR of the receptive fields.
def snr( signal, sigma ):
    k = signal.shape[0]
    # Filtering the input signal
    filtered_s = gaussian_filter( signal, sigma )

    # Computing background noise
    noise = signal - filtered_s
    # Computing noise variance
    noise_var = np.var( noise )

    # Computing signal and noise power
    signalPow = np.sum( signal**2 )/k
    noisePow = np.sum( noise**2 )/k

    # Computing snr and noise index
    snr = 10.0 * np.log10( signalPow/noisePow )
    noise_index = noise_var/np.abs(signal).max() *100.0

    return snr, noise_index, filtered_s

# Main :p
if __name__=='__main__':
    np.random.seed(137)

    RFs = np.load('real-rfs-ref.npy').reshape(32,32,25,25)
    n, size, bins = RFs.shape[0], RFs.shape[2], 70
    Z, cRFs = clear_data( RFs, n )

    np.save('areas-ref', Z)
    np.save('cleared-rfs', cRFs)


import sys
from collections import OrderedDict
from functools import reduce

import numpy
import sklearn.cross_validation

from PyQt4.QtGui import QTableWidget, QTableWidgetItem

import Orange.data
import Orange.classification

from Orange.widgets import widget, gui, settings
from Orange.evaluation.testing import Results

class OWLearningCurveB(widget.OWWidget):
    name = "Learning Curve (B)"
    description = ("Takes a data set and a set of learners and shows a "
                   "learning curve in a table")
    icon = "icons/LearningCurve.svg"
    priority = 1010

# [start-snippet-1]
    inputs = [("Data", Orange.data.Table, "set_dataset", widget.Default),
              ("Test Data", Orange.data.Table, "set_testdataset"),
              ("Learner", Orange.classification.Learner, "set_learner",
               widget.Multiple + widget.Default)]
# [end-snippet-1]

    #: cross validation folds
    folds = settings.Setting(5)
    #: points in the learning curve
    steps = settings.Setting(10)
    #: index of the selected scoring function
    scoringF = settings.Setting(0)
    #: compute curve on any change of parameters
    commitOnChange = settings.Setting(True)

    def __init__(self):
        super().__init__()

        # sets self.curvePoints, self.steps equidistant points from
        # 1/self.steps to 1
        self.updateCurvePoints()

        self.scoring = [
            ("Classification Accuracy", Orange.evaluation.scoring.CA),
            ("AUC", Orange.evaluation.scoring.AUC),
            ("Precision", Orange.evaluation.scoring.Precision),
            ("Recall", Orange.evaluation.scoring.Recall)
        ]
        #: input data on which to construct the learning curve
        self.data = None
        #: optional test data
        self.testdata = None
        #: A {input_id: Learner} mapping of current learners from input channel
        self.learners = OrderedDict()
        #: A {input_id: List[Results]} mapping of input id to evaluation
        #: results list, one for each curve point
        self.results = OrderedDict()
        #: A {input_id: List[float]} mapping of input id to learning curve
        #: point scores
        self.curves = OrderedDict()

        # GUI
        box = gui.widgetBox(self.controlArea, "Info")
        self.infoa = gui.widgetLabel(box, 'No data on input.')
        self.infob = gui.widgetLabel(box, 'No learners.')

        gui.separator(self.controlArea)

        box = gui.widgetBox(self.controlArea, "Evaluation Scores")
        gui.comboBox(box, self, "scoringF",
                     items=[x[0] for x in self.scoring],
                     callback=self._invalidate_curves)

        gui.separator(self.controlArea)

        box = gui.widgetBox(self.controlArea, "Options")
        gui.spin(box, self, 'folds', 2, 100, step=1,
                 label='Cross validation folds:  ', keyboardTracking=False,
                 callback=lambda:
                    self._invalidate_results() if self.commitOnChange else None
        )
        gui.spin(box, self, 'steps', 2, 100, step=1,
                 label='Learning curve points:  ', keyboardTracking=False,
                 callback=[self.updateCurvePoints,
                           lambda: self._invalidate_results() if self.commitOnChange else None])
        gui.checkBox(box, self, 'commitOnChange', 'Apply setting on any change')
        self.commitBtn = gui.button(box, self, "Apply Setting",
                                    callback=self._invalidate_results,
                                    disabled=True)

        gui.rubber(self.controlArea)

        # table widget
        self.table = gui.table(self.mainArea,
                               selectionMode=QTableWidget.NoSelection)

    ##########################################################################
    # slots: handle input signals

    def set_dataset(self, data):
        """Set the input train dataset."""
        # Clear all results/scores
        for id in list(self.results):
            self.results[id] = None
        for id in list(self.curves):
            self.curves[id] = None

        self.data = data

        if data is not None:
            self.infoa.setText('%d instances in input data set' % len(data))
        else:
            self.infoa.setText('No data on input.')

        self.commitBtn.setEnabled(self.data is not None)

    def set_testdataset(self, testdata):
        """Set a separate test dataset."""
        # Clear all results/scores
        for id in list(self.results):
            self.results[id] = None
        for id in list(self.curves):
            self.curves[id] = None

        self.testdata = testdata

    def set_learner(self, learner, id):
        """Set the input learner for channel id."""
        if id in self.learners:
            if learner is None:
                # remove a learner and corresponding results
                del self.learners[id]
                del self.results[id]
                del self.curves[id]
            else:
                # update/replace a learner on a previously connected link
                self.learners[id] = learner
                # invalidate the cross-validation results and curve scores
                # (will be computed/updated in `_update`)
                self.results[id] = None
                self.curves[id] = None
        else:
            if learner is not None:
                self.learners[id] = learner
                # initialize the cross-validation results and curve scores
                # (will be computed/updated in `_update`)
                self.results[id] = None
                self.curves[id] = None

        if len(self.learners):
            self.infob.setText("%d learners on input." % len(self.learners))
        else:
            self.infob.setText("No learners.")

        self.commitBtn.setEnabled(len(self.learners))

    def handleNewSignals(self):
        if self.data is not None:
            self._update()
            self._update_curve_points()
        self._update_table()

    def _invalidate_curves(self):
        if self.data is not None:
            self._update_curve_points()
        self._update_table()

    def _invalidate_results(self):
        for id in self.learners:
            self.curves[id] = None
            self.results[id] = None

        if self.data is not None:
            self._update()
            self._update_curve_points()
        self._update_table()

    def _update(self):
        assert self.data is not None
        # collect all learners for which results have not yet been computed
        need_update = [(id, learner) for id, learner in self.learners.items()
                       if self.results[id] is None]
        if not need_update:
            return
        learners = [learner for _, learner in need_update]

        self.progressBarInit()
        if self.testdata is None:
            # compute the learning curve result for all learners in one go
            results = learning_curve(
                learners, self.data, folds=self.folds,
                proportions=self.curvePoints,
                callback=lambda value: self.progressBarSet(100 * value)
            )
        else:
            results = learning_curve_with_test_data(
                learners, self.data, self.testdata, times=self.folds,
                proportions=self.curvePoints,
                callback=lambda value: self.progressBarSet(100 * value)
            )

        self.progressBarFinished()
        # split the combined result into per learner/model results
        results = [list(Results.split_by_model(p_results)) for p_results in results]

        for i, (id, learner) in enumerate(need_update):
            self.results[id] = [p_results[i] for p_results in results]

    def _update_curve_points(self):
        for id in self.learners:
            curve = [self.scoring[self.scoringF][1](x)[0]
                     for x in self.results[id]]
            self.curves[id] = curve

    def _update_table(self):
        self.table.setRowCount(0)
        self.table.setRowCount(len(self.curvePoints))
        self.table.setColumnCount(len(self.learners))

        self.table.setHorizontalHeaderLabels(
            [learner.name for _, learner in self.learners.items()])
        self.table.setVerticalHeaderLabels(
            ["{:.2f}".format(p) for p in self.curvePoints])

        if self.data is None:
            return

        for column, curve in enumerate(self.curves.values()):
            for row, point in enumerate(curve):
                self.table.setItem(
                    row, column, QTableWidgetItem("{:.5f}".format(point)))

        for i in range(len(self.learners)):
            sh = self.table.sizeHintForColumn(i)
            cwidth = self.table.columnWidth(i)
            self.table.setColumnWidth(i, max(sh, cwidth))

    def updateCurvePoints(self):
        self.curvePoints = [(x + 1.)/self.steps for x in range(self.steps)]

def learning_curve(learners, data, folds=10, proportions=None,
                   random_state=None, callback=None):

    if proportions is None:
        proportions = numpy.linspace(0.0, 1.0, 10 + 1, endpoint=True)[1:]

    def select_proportion_preproc(data, p, rstate=None):
        assert 0 < p <= 1
        rstate = numpy.random.RandomState(None) if rstate is None else rstate
        indices = rstate.permutation(len(data))
        n = int(numpy.ceil(len(data) * p))
        return data[indices[:n]]

    if callback is not None:
        parts_count = len(proportions)
        callback_wrapped = lambda part: \
            lambda value: callback(value / parts_count + part / parts_count)
    else:
        callback_wrapped = lambda part: None

    results = [
        Orange.evaluation.CrossValidation(
            data, learners, k=folds,
            preprocessor=lambda data, p=p:
                select_proportion_preproc(data, p),
            callback=callback_wrapped(i)
        )
        for i, p in enumerate(proportions)
    ]
    return results

def learning_curve_with_test_data(learners, traindata, testdata, times=10,
                                  proportions=None, random_state=None,
                                  callback=None):
    if proportions is None:
        proportions = numpy.linspace(0.0, 1.0, 10 + 1, endpoint=True)[1:]

    def select_proportion_preproc(data, p, rstate=None):
        assert 0 < p <= 1
        rstate = numpy.random.RandomState(None) if rstate is None else rstate
        indices = rstate.permutation(len(data))
        n = int(numpy.ceil(len(data) * p))
        return data[indices[:n]]

    if callback is not None:
        parts_count = len(proportions) * times
        callback_wrapped = lambda part: \
            lambda value: callback(value / parts_count + part / parts_count)
    else:
        callback_wrapped = lambda part: None

    results = [
        [Orange.evaluation.TestOnTestData(
             traindata, testdata, learners,
             preprocessor=lambda data, p=p:
                 select_proportion_preproc(data, p),
             callback=callback_wrapped(i * times + t))
         for t in range(times)]
        for i, p in enumerate(proportions)
    ]
    results = [reduce(results_add, res, Orange.evaluation.Results())
               for res in results]
    return results

def results_add(x, y):
    def is_empty(res):
        return (getattr(res, "models", None) is None
                and getattr(res, "row_indices", None) is None)

    if is_empty(x):
        return y
    elif is_empty(y):
        return x

    assert x.data is y.data
    assert x.domain is y.domain
    assert x.predicted.shape[0] == y.predicted.shape[0]

    row_indices = numpy.hstack((x.row_indices, y.row_indices))
    predicted = numpy.hstack((x.predicted, y.predicted))
    actual = numpy.hstack((x.actual, y.actual))

    xprob = getattr(x, "probabilities", None)
    yprob = getattr(y, "probabilities", None)

    if xprob is None and yprob is None:
        prob = None
    elif xprob is not None and yprob is not None:
        prob = numpy.concatenate((xprob, yprob), axis=1)
    else:
        raise ValueError()

    res = Orange.evaluation.Results()
    res.data = x.data
    res.domain = x.domain
    res.row_indices = row_indices
    res.actual = actual
    res.predicted = predicted
    res.folds = None
    if prob is not None:
        res.probabilities = prob

    if x.models is not None and y.models is not None:
        res.models = [xm + ym for xm, ym in zip(x.models, y.models)]

    nmodels = predicted.shape[0]
    xfailed = getattr(x, "failed", None) or [False] * nmodels
    yfailed = getattr(y, "failed", None) or [False] * nmodels
    assert len(xfailed) == len(yfailed)
    res.failed = [xe or ye for xe, ye in zip(xfailed, yfailed)]

    return res

def main(argv=sys.argv):
    from PyQt4.QtGui import QApplication
    app = QApplication(argv)
    argv = app.argv()
    if len(argv) > 1:
        filename = argv[1]
    else:
        filename = "iris"

    data = Orange.data.Table(filename)
    indices = numpy.random.permutation(len(data))

    traindata = data[indices[:-20]]
    testdata = data[indices[-20:]]

    ow = OWLearningCurveB()
    ow.show()
    ow.raise_()

    ow.set_dataset(traindata)
    ow.set_testdataset(testdata)

    l1 = Orange.classification.NaiveBayesLearner()
    l1.name = 'Naive Bayes'
    ow.set_learner(l1, 1)

    l2 = Orange.classification.LogisticRegressionLearner()
    l2.name = 'Logistic Regression'
    ow.set_learner(l2, 2)

    l4 = Orange.classification.SklTreeLearner()
    l4.name = "Decision Tree"
    ow.set_learner(l4, 3)

    ow.handleNewSignals()

    app.exec_()

    ow.set_dataset(None)
    ow.set_testdataset(None)
    ow.set_learner(None, 1)
    ow.set_learner(None, 2)
    ow.set_learner(None, 3)
    ow.handleNewSignals()
    return 0

if __name__=="__main__":
    sys.exit(main())


'''
This module implements the function `plotmap` which plots scattered points on a map
retrieved using ArgGIS Server REST API. The function is highly customizable and is basically a
wrapper around the `Basemap` library (for the map background)
plus matplotlib utilities (for plotting points, shapes, labels and legend)

Created on Mar 10, 2016

@author: riccardo
'''
import numpy as np
import re
from itertools import izip, chain
from urllib2 import URLError, HTTPError
import socket
import matplotlib.pyplot as plt
import matplotlib.patheffects as PathEffects
from mpl_toolkits.basemap import Basemap
from matplotlib.font_manager import FontProperties
from matplotlib import rcParams

def parse_margins(obj, parsefunc=lambda margins: [float(val) for val in margins]):
    """Parses obj returning a 4 element numpy array denoting the top, right, bottom and left
    values. This function first converts obj to a 4 element list L, and then
    calls `parsefunc`, which by default converts all L values into float
    :param obj: either None, a number, a list of numbers (allowed lengths: 1 to 4),
    a comma/semicolon/spaces separated string (e.g. "4deg 0.0", "1, 1.2", "2km,4deg", "1 ; 2")
    :param parsefunc: a function to be applied to obj converted to list. By default, returns
    float(v) for any v in L
    :return: a 4 element numpy array of floats denoting the top, right, bottom, left values of
    the margins. The idea is the same as css margins, as depicted in the table below.
    :Examples:
        Called f `parsefunc`, then:
        ============= =========================
        obj is     returns
        ============= =========================
        None          [0, 0, 0, 0]
        ------------- -------------------------
        string        the list obtained after
                      splitting string via
                      regexp where comma,
                      semicolon and spaces
                      are valid separators
        ------------- -------------------------
        x or [x]      parsefunc([x, x, x, x])
        ------------- -------------------------
        [x, y]        parsefunc([x, y ,x, y])
        ------------- -------------------------
        [x, y, z]     parsefunc([x, y, z, y])
        ------------- -------------------------
        [x, y, z, t]  parsefunc([x, y, z, t])
        ============= =========================
    """
    if obj is None:
        margins = [0] * 4
    elif hasattr(obj, "__iter__") and not isinstance(obj, str):
        # is an iterable not string. Note the if above is py2 py3 compatible
        margins = list(obj)
    else:
        try:
            margins = [float(obj)] * 4
        except (TypeError, ValueError):
            margins = re.compile("(?:\\s*,\\s*|\\s*;\\s*|\\s+)").split(obj)

    if len(margins) == 1:
        margins *= 4
    elif len(margins) == 2:
        margins *= 2
    elif len(margins) == 3:
        margins.append(margins[1])
    elif len(margins) != 4:
        raise ValueError("unable to parse margins on invalid value '%s'" % obj)

    return np.asarray(parsefunc(margins) if hasattr(parsefunc, "__call__") else margins)
    # return margins

def parse_distance(dist, lat_0=None):
    """Returns the distance in degrees. If dist is in km or m, and lat_0 is not None,
    returns w2lon, else h2lat. dist None defaults to 0
    :param dist: float, int None, string. If string and has a unit, see above
    """
    try:
        return 0 if dist is None else float(dist)
    except ValueError:
        if dist[-3:].lower() == 'deg':
            return float(dist[:-3])
        elif dist[-2:] == 'km':
            dst = 1000 * float(dist[:-2])
        elif dist[-1:] == 'm':
            dst = float(dist[:1])
        else:
            raise

        return w2lon(dst, lat_0) if lat_0 is not None else h2lat(dst)

def get_lon0_lat0(min_lons, min_lats, max_lons, max_lats):
    """ Calculates lat_0, lon_0, i.e., the mid point of the bounding box denoted by the
    arguments
    :param min_lons: the minimum of longitudes
    :param min_lats: the maximum of latitudes
    :param max_lons: the minimum of longitudes
    :param max_lats: the maximum of latitudes
    :return: the 2-element tuple denoting the mid point lon_0, lat_0
    """
    lat_0 = max_lats / 2. + min_lats / 2.
    lon_0 = max_lons / 2. + min_lons / 2.
    if lon_0 > 180:  # FIXME: necessary?? see self.get_normalized... above
        lon_0 -= 360
    return lon_0, lat_0

def getbounds(min_lon, min_lat, max_lon, max_lat, margins):
    """Calculates the bounds given the bounding box identified by the arguments and
    given optional margins
    :param min_lon: the minimum longitude (numeric, scalar)
    :param min_lat: the maximum latitude (numeric, scalar)
    :param max_lon: the minimum longitude (numeric, scalar)
    :param max_lat: the maximum latitude (numeric, scalar)
    :param margins: the margins as a css-like string (with units 'deg', 'km' or 'm'), or as
    a 1 to 4 element array of numeric values (in that case denoting degrees).
    As in css, a 4 element array denotes the [top, right, bottom, left] values.
    None defaults to [0, 0, 0, 0].
    :return: the 6-element tuple denoting lon_0, lat_0, min_lon, min_lat, max_lon, max_lat.
    where min_lon, min_lat, max_lon, max_lat are the new bounds and lon_0 and lat_0 are
    their midpoints (x and y, respectively)
    """

    def parsefunc(mrgns):
        """parses mrgns as array of strings into array of floats
        """
        return parse_distance(mrgns[0]), parse_distance(mrgns[1], max_lat), \
            parse_distance(mrgns[2]), parse_distance(mrgns[3], min_lat)

    top, right, btm, left = parse_margins(margins, parsefunc)
    min_lon, min_lat, max_lon, max_lat = min_lon-left, min_lat-btm, max_lon+right, max_lat+top

    if min_lon == max_lon:
        min_lon -= 10  # in degrees
        max_lon += 10  # in degrees

    if min_lat == max_lat:
        min_lat -= 10  # in degrees
        max_lat += 10  # in degrees

    # minima must be within bounds:
    min_lat = max(-90, min_lat)
    max_lat = min(90, max_lat)
    min_lon = max(-180, min_lon)
    max_lon = min(180, max_lon)

    lon_0, lat_0 = get_lon0_lat0(min_lon, min_lat, max_lon, max_lat)
    return lon_0, lat_0, min_lon, min_lat, max_lon, max_lat

# static constant converter (degree to meters and viceversa) for latitudes
DEG2M_LAT = 2 * np.pi * 6371 * 1000 / 360

def lat2h(distance_in_degrees):
    """converts latitude distance from degrees to height in meters
    :param distance_in_degrees: a distance (python scalar or numpy array) along the great circle
    espressed in degrees"""
    deg2m_lat = DEG2M_LAT  # 2 * np.pi * 6371 * 1000 / 360
    return distance_in_degrees * deg2m_lat

def h2lat(distance_in_meters):
    """converts latitude distance from height in meters to degrees
    :param distance_in_degrees: a distance  (python scalar or numpy array) along the great circle
    espressed in degrees"""
    deg2m_lat = DEG2M_LAT  # deg2m_lat = 2 * np.pi * 6371 * 1000 / 360
    return distance_in_meters / deg2m_lat

def lon2w(distance_in_degrees, lat_0):
    """converts longitude distance from degrees to width in meters
    :param distance_in_degrees: a distance  (python scalar or numpy array)
    along the lat_0 circle expressed in degrees
    :param lat_0: if missing or None, defaults to the internal lat_0, which is set as the mean
    of all points passed to this object. Otherwise, expresses the latitude of the circle along
    which the lon2w(distance_in_degrees) must be converted to meters"""
    deg2m_lat = DEG2M_LAT
    deg2m_lon = deg2m_lat * np.cos(lat_0 / 180 * np.pi)
    return distance_in_degrees * deg2m_lon

def w2lon(distance_in_meters, lat_0):
    """converts longitude distance from width in meters to degrees
    :param distance_in_meters: a distance  (python scalar or numpy array)
    along the lat_0 circle expressed in meters
    :param lat_0: if missing or None, defaults to the internal lat_0, which is set as the mean
    of all points passed to this object. Otherwise, expresses the latitude (in degrees) of the
    circle along which w2lon(distance_in_meters) must be converted to degrees"""
    deg2m_lat = DEG2M_LAT  # deg2m_lat = 2 * np.pi * 6371 * 1000 / 360
    deg2m_lon = deg2m_lat * np.cos(lat_0 / 180 * np.pi)
    return distance_in_meters / deg2m_lon

class MapHandler(object):
    """
        Class handling bounds of a map given points (lons and lats)
    """
    def __init__(self, lons, lats, map_margins):
        """Initializes a new MapHandler. If figure here is None, you **MUST**
        call self.set_fig(fig) to calculate bounds and other stuff
        when you have a ready figure"""
        self.lons = lons if len(lons) else [0]  # FIXME: use numpy arrays!!
        self.lats = lats if len(lats) else [0]
        self.max_lons, self.min_lons = max(self.lons), min(self.lons)
        self.max_lats, self.min_lats = max(self.lats), min(self.lats)
        self.lon_0, self.lat_0, self.llcrnrlon, self.llcrnrlat, self.urcrnrlon, self.urcrnrlat = \
            getbounds(self.min_lons, self.min_lats, self.max_lons, self.max_lats, map_margins)

    def _get_map_dims(self):  # , fig_size_in_inches, colorbar=False):
        """Returns the map dimension width, height, in meters"""

        max_lons, min_lons = self.urcrnrlon, self.llcrnrlon
        max_lats, min_lats = self.urcrnrlat, self.llcrnrlat
        height = lat2h(max_lats - min_lats)
        width = lon2w(max_lons - min_lons, self.lat_0)
        return width, height

    def get_parallels(self, max_labels_count=8):
        width, height = self._get_map_dims()
        lat_0 = self.lat_0
        N1 = int(np.ceil(height / max(width, height) * max_labels_count))
        parallels = MapHandler._linspace(lat_0 - h2lat(height / 2),
                                         lat_0 + h2lat(height / 2), N1)
        return parallels

    def get_meridians(self, max_labels_count=8):
        width, height = self._get_map_dims()
        lon_0 = self.lon_0
        lat_0 = self.lat_0
        N2 = int(np.ceil(width / max(width, height) * max_labels_count))
        meridians = MapHandler._linspace(lon_0 - w2lon(width / 2, lat_0),
                                         lon_0 + w2lon(width / 2, lat_0), N2)
        meridians[meridians > 180] -= 360
        return meridians

    @staticmethod
    def _linspace(val1, val2, N):
        """
        returns around N 'nice' values between val1 and val2. Copied from obspy.plot_map
        """
        dval = val2 - val1
        round_pos = int(round(-np.log10(1. * dval / N)))
        # Fake negative rounding as not supported by future as of now.
        if round_pos < 0:
            factor = 10 ** (abs(round_pos))
            delta = round(2. * dval / N / factor) * factor / 2
        else:
            delta = round(2. * dval / N, round_pos) / 2
        new_val1 = np.ceil(val1 / delta) * delta
        new_val2 = np.floor(val2 / delta) * delta
        N = (new_val2 - new_val1) / delta + 1
        return np.linspace(new_val1, new_val2, N)

def _normalize(obj, size=None, dtype=None):
    """"Casts" obj to a numpy array of the given optional size and optional dtype, and returns it.
    If size is not None, the array must have length size. If not, and has length 1, it will be
    resized to the specified size. Otherwise a ValueError is raised
    If size is None, no resize will be in place and the array is returend as it is
    Note: obj=None will be converted to the array [None], apparently in the current version of numpy
    this wouldn't be the default (see argument ndmin=1)
    :return an numpy array resulting to the coinversion of obj into array
    :Examples:
    """
    x = np.array(obj, ndmin=1) if dtype is None else np.array(obj, ndmin=1, dtype=dtype)
    if size is None:
        return np.array([]) if obj is None else x  # if obj is None x is [None], return [] instead
    try:
        if len(x) == 1:
            x = np.resize(x, size)
        elif len(x) != size:
            raise ValueError("invalid array length: %d. Expected %d" % (len(x), size))
    except (ValueError, TypeError) as _err:
        raise ValueError(str(_err))

    return x

def torgba(html_str):
    """Converts html_str into a tuple of rgba colors all in [0, 1]
    Curiously, matplotlib color functions do not provide this functionality for
    '#RGBA' color formats

    :param html_str: a valid html string in hexadecimal format.
    Can have length 4, 7 or 9 such as #F1a, #fa98e3, #fc456a09
    :return: a rgba vector, i.e. a 4-element numpy array of values in [0,1] denoting `html_str`
    :raise: ValueError if html_str is invalid
    """
    if len(html_str) not in (4, 7, 9) or not html_str[0] == '#':
        raise ValueError("'%s' invalid html string" % html_str)
    elif len(html_str) == 4:
        rgb = [html_str[i:i+1]*2 for i in xrange(1, len(html_str))]
    else:
        rgb = [html_str[i:i+2] for i in xrange(1, len(html_str), 2)]

    if len(rgb) == 3:
        rgb += ['FF']

    return np.true_divide(np.array([int(r, 16) for r in rgb]), 255)

def _shapeargs(lons, lats, labels, sizes, colors, markers, legend_labels):
    lons = _normalize(lons, dtype=float)  # basically: convert to float array if scalar (size=0)
    lats = _normalize(lats, dtype=float)  # basically: convert to float array if scalar (size=0)
    if len(lons) != len(lats):
        raise ValueError('mismatch in lengths: lons (%d) and lats (%d)' % (len(lons), len(lats)))
    leng = len(lons)
    labels = _normalize(labels, size=leng)
    colors = _normalize(colors, size=leng)
    markers = _normalize(markers, size=leng)
    legend_labels = _normalize(legend_labels, size=leng)
    # colors[np.isnan(colors) | (colors <= 0)] = 1.0  # nan colors default to 1 (black?)
    sizes = _normalize(sizes, size=leng, dtype=float)
    valid_points = np.logical_not(np.isnan(lons) | np.isnan(lats) | (sizes <= 0))

    # return all points whose corresponding numeric values are not nan:
    return (lons[valid_points],
            lats[valid_points],
            labels[valid_points],
            sizes[valid_points],
            colors[valid_points],
            markers[valid_points],
            legend_labels[valid_points])

# def get_ax_size(ax, fig):
#     bbox = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
#     return bbox.width, bbox.height

def pix2inch(pix, fig):
    """Converts pixel to inches on a given matplotlib figure"""
    return pix / fig.dpi

def inch2pix(inch, fig):
    """Converts inches to pixel on a given matplotlib figure"""
    return inch * fig.dpi

def _joinargs(key_prefix, kwargs, **already_supplied_args):
    '''updates already_supplied_args with kwargs using a given prefix in kwargs to identify
    common keys. Used in plotmap for kwargs'''
    key_prefix += "_"
    len_prefix = len(key_prefix)
    already_supplied_args.update({k[len_prefix:]: v
                                  for k, v in kwargs.iteritems() if k.startswith(key_prefix)})
    return already_supplied_args

def _mp_set_custom_props(drawfunc_retval, lines_props, labels_props):
    """Sets custom properties on drawparallels or drawmeridians return function.
    drawfunc_retval is a dict of numbers mapped to tuples where the first element is a list of
    matplotlib lines, and the second element is a list of matplotlib texts"""
    _setprop(chain.from_iterable((lin for lin, lab in drawfunc_retval.itervalues())), lines_props)
    _setprop(chain.from_iterable((lab for lin, lab in drawfunc_retval.itervalues())), labels_props)

def _setprop(iterator_of_mp_objects, props):
    '''sets the given properties of an iterator of same type matplotlib objects'''
    if not props:
        return
    prp = {}
    for obj in iterator_of_mp_objects:
        if not prp:
            prp = {"set_%s" % name: val for name, val in props.iteritems()
                   if hasattr(obj, "set_%s" % name)}
        for name, val in prp.iteritems():
            getattr(obj, name)(val)

# values below CAN be None but CANNOT be arrays containing None's
def plotmap(lons,
            lats,
            labels=None,
            legendlabels=None,
            markers="o",
            colors="#FF4400",
            sizes=20,
            cmap=None,
            fontsize=None,
            fontweight='regular',
            fontcolor='k',
            labels_h_offset=0,
            labels_v_offset=0,
            mapmargins='0.5deg',
            figmargins=2,
            arcgis_service='World_Street_Map',
            arcgis_xpixels=1500,
            arcgis_dpi=96,
            urlfail='ignore',
            maxmeridians=5,
            maxparallels=5,
            legend_pos='bottom',
            legend_borderaxespad=1.5,
            legend_ncol=1,
            title=None,
            show=False,
            **kwargs):  # @UnusedVariable
    """
    Makes a scatter plot of points on a map background using ArcGIS REST API.

    :param lons: (array-like of length N or scalar) Longitudes of the data points, in degreee
    :param lats: (array-like of length N or scalar) Latitudes of the data points, in degree
    :param labels: (array-like of length N or string. Default: None, no labels) Annotations
    (labels) for the individual data points on the map. If non-array (e.g. string), the same value
    will be applied to all points
    :param legendlabels: (array-like of length N or string. Default: None, no legend)
    Annotations (labels) for the legend. You can supply a sparse array where only some points
    will be displayed on the legend. All points with no legend label will not show up in the
    legend
    :param sizes: (array-like of length N or number. Default: 20) Sizes (in points^2) of the
    individual points in the scatter plot.
    :param markers: (array-like of length N,
    `MarkerStyle<http://matplotlib.org/api/markers_api.html#matplotlib.markers.MarkerStyle>`_  or
    string. Default: 'o' - circle) The markers (shapes) to be drawn for each point on the map.
    See `markers <http://matplotlib.org/api/markers_api.html#module-matplotlib.markers>`_ for
    more information on the different styles of markers scatter supports. Marker can be either
    an instance of the class or the text shorthand for a particular marker.
    :param colors:  (array-like of length N,
    `matplotlib color <http://matplotlib.org/api/colors_api.html>`_, e.g. string.
    Default: "#FF4400")
    Colors for the markers (fill color). You can type color transparency by supplying string of 9
    elements where the last two characters denote the transparency ('00' fully transparent,
    'ff' fully opaque). Note that this is a feature not implemented in `matplotlib` colors, where
    transparency is given as the last element of the numeric tuple (r, g, b, a)
    :param fontsize: (numeric or None. Default: None) The fontsize for all texts drawn on the
    map (labels, axis tick labels, legend). None uses the default figure font size for all. Custom
    values for the individual text types (e.g. legend texts vs labels texts) can be supplied
    via the `kwargs` argument and a given prefix (see below)
    :param fontweight: (string or number. Default: 'regular') The font weight for all texts drawn
    on the map (labels, axis tick labels, legend). Accepts the values (see
    http://matplotlib.org/api/text_api.html#matplotlib.text.Text.set_weight):
    ```
    [a numeric value in range 0-1000 | 'ultralight' | 'light' |
    'normal' | 'regular' | 'book' | 'medium' | 'roman' | 'semibold' | 'demibold' | 'demi' |
    'bold' | 'heavy' | 'extra bold' | 'black' ]
    ```
    Custom
    values for the individual text types (e.g. legend texts vs labels texts) can be supplied
    via the `kwargs` argument and a given prefix (see below)
    :param fontcolor: (`matplotlib color <http://matplotlib.org/api/colors_api.html>`_ or
    string. Default: 'k', black) The font color for all texts drawn on the
    map (labels, axis tick labels, legend). Custom
    values for the individual text types (e.g. legend texts vs labels texts) can be supplied
    via the `kwargs` argument and a given prefix (see below)
    :param labels_h_offset: (string, number. Defaults None=0) The horizontal offset to be applied
    to each label on the map relative to its point coordinates. Negative values will shift the
    labels westward, positive values eastward. Useful for not overlapping
    markers and labels.
    If numeric, it is assumed to be the expressed in degrees. Otherwise, you can supply a string
    with a number followed by one of the units 'm', 'km' or 'deg' (e.g., '5km', '0.5deg').
    Note that this value affects the
    `horizontalalignment` and `multialignment` properties of the labels
    (for info see http://matplotlib.org/api/text_api.html#matplotlib.text.Text). Supplying
    `labels_horizontalalignment` or `labels_ha` as optional argument will override
    this behaviour (see `kwargs` below)
    :param labels_v_offset: (string, number. Defaults None=0) The vertical offset to be applied
    to each label on the map relative to its point coordinates. Negative values will shift the
    labels southhward, positive values northward. See notes on `labels_h_offset` for details
    Note that this value affects the
    `verticalalignment` property of the labels
    (for info see http://matplotlib.org/api/text_api.html#matplotlib.text.Text). Supplying
    `labels_verticalalignment` or `labels_va` as optional argument will override
    this behaviour (see `kwargs` below)
    :param mapmargins: (array-like of 1,2,3,4 elements, numeric or string, or None=0.
    Default: '0.5deg').
    The map margins, i.e. how much the map has to 'expand/shrink' in any direction, relative
    to the bounding box calculated to include all points.
    If array-like, it behaves like the css 'margin' property of html: 4 elements will denote
    [top, right, bottom, left], two elements will denote [top/bottom, left/right], three
    elements [top, right/left, bottom], a single element array (or a single number or a string)
    applies the value to all directions.
    Finally, elements of the array must be expressed as the arguments `labels_h_offset` or
    `labels_v_offset`: numbers denoting degrees or strings with units 'm', 'km', 'deg'. Negative
    values will shrink the map.
    If string, the argument will be first splitted using commas, semicolon or spaces as delimiters
    (if no delimiter is found, the string is taken as a single chunk) and converted to an array-like
    object.
    :param figmargins: (array-like of 1,2,3,4 elements, number or None=0. Default:2) The
    figure margins *in font height units* (e.g., 2 means: twice the font height). This argument
    behaves exactly as `mapmargins` but expands/shrinks the distances between map and figure
    (image) bounds. Useful to include axis tick labels or legend, if they overflow.
    Note also that strings
    are allowed only if they are parsable to float (e.g. "5,6; -12  1")
    :param arcgis_service: (string, default:  'World_Street_Map'). The map image type, or
    more technically the service for the map
    hosted on ArcGIS server. Other values are 'ESRI_Imagery_World_2D'
    (default in
    `Basemap.arcgisimage <http://matplotlib.org/basemap/api/basemap_api.html#mpl_toolkits.basemap.Basemap.arcgisimage>`_),
    'World_Topo_Map', 'World_Terrain_Base'. For details, see:
    http://server.arcgisonline.com/arcgis/rest/services.
    :param arcgis_xpixels: (numeric, default: 3000). Requested number of image pixels
    in x-direction (default is 400 in
    `Basemap.arcgisimage <http://matplotlib.org/basemap/api/basemap_api.html#mpl_toolkits.basemap.Basemap.arcgisimage>`_).
    The documentation is quite unclear but this parameter seems to set the zoom of the image. From
    this `link <http://basemaptutorial.readthedocs.io/en/latest/backgrounds.html#arcgisimage>`_:
    A bigger number will ask a bigger image, so the image will have more detail.
    So when the zoom is bigger, `xsize` must be bigger to maintain the resolution
    :param urlfail: (string, 'raise' or 'ignore'. Default: 'ignore'). Tells what to do if the
    ArcGIS requet fails (URLError, no internet connection etcetera). By default, on failure a raw
    map with continents contour, and oceans will be plotted (good for
    debug). Otherwise, the exception resulting from the web request is raised
    :param maxmeridians: (numeric default: 5). The number of maximum meridians to be drawn. Set to
    <=0 to hide meridians. Note that also x-axis labels are drawn.
    To further manipulate meridians display, use any argument starting with
    'mlabels_', 'mlines_' or 'meridians' (see `kwargs` below). E.g., to show only the labels and not
    the lines, supply as argument `meridians_linewidth=0` or 'mlines_linewidth=0'.
    :param maxparallels: (numeric default: 5). The number of maximum parallels to be drawn. Set to
    <=0 to hide parallels. Note that also y-axis labels are drawn.
    To further manipulate parallels display, use any argument starting with
    'plabels_', 'plines_' or 'parallels' (see `kwargs` below). E.g., to show only the labels and not
    the lines, supply as argument `parallels_linewidth=0` or 'plines_linewidth=0'.
    :param legend_pos: (string in ['top'. 'bottom', 'right', 'left'], default='bottom'). The legend
    location with respect to the map. It also adjusts the bounding box that the legend will be
    anchored to.
    For
    customizing entirely the legend placement overriding this parameter, provide `legend_loc`
    (and optionally `legend_bbox_to_anchor`) in `kwargs` (see below)
    :param legend_borderaxespad: (numeric, default 1.5) The pad between the axes and legend border,
    in font units
    :param legend_ncol: (integer, default=1) The legend number of columns
    :param title (string or None. Default: None): Title above plot (Note: not tested)
    :param show (boolean, default: False): Whether to show the figure after plotting or not
    (Note: not tested). Can be used to do further customization of the plot before showing it.
    :param fig: (matplotlib figure or None, default: None). Note: deprecated, pass None as
    supplying an already existing figure with other axes might break the figure layout
    :param kwargs: any kind of additional argument passed to `matplotlib` and `Basemap` functions
    or objects.
    The name of the argument must be of the form
    ```
    prefix_propertyname=propertyvalue
    ```
    where prefix indicates the function/object to be called with keyword argument:
    ```
    propertyname=propertyvalue
    ```

    Current supported prefixes are (for available property names see links):

    Prefix        Passes `propertyname` to
    ============ ==================================================================================
    arcgis       `Basemap.arcgisimage <http://matplotlib.org/basemap/api/basemap_api.html#mpl_toolkits.basemap.Basemap.arcgisimage>_
                 used to retrieve the background map using ArgGIS Server REST API. See also
                 http://basemaptutorial.readthedocs.io/en/latest/backgrounds.html#arcgisimage
    basemap      `Basemap <http://matplotlib.org/basemap/api/basemap_api.html#mpl_toolkits.basemap.Basemap>`_
                 the object responsible of drawing and managing the map. Note that
                 `basemap_resolution=h` and `basemap_epsg=4326` by default.
    labels       All `texts <http://matplotlib.org/api/text_api.html#matplotlib.text.Text>`_
                 used to display the point labels on the map
    legend       The `legend <http://matplotlib.org/api/legend_api.html#matplotlib.legend.Legend>`_.
                 See the already implemented arguments `legend_borderaxespad`,
                 `legend_ncol`
    legendlabels All `texts <http://matplotlib.org/api/text_api.html#matplotlib.text.Text>`_
                 used to display the text labels of the legend
    meridians    `Basemap.drawmeridians`. For more detailed settings on meridians, see
                 `mlines` and `mlabels`
    parallels    `Basemap.drawparallels`. For more detailed settings on parallels, see
                 `plines` and `plabels`
    plines       All `lines <http://matplotlib.org/api/lines_api.html#matplotlib.lines.Line2D>`_
                 used to display the parallels
    plabels      All `texts <http://matplotlib.org/api/text_api.html#matplotlib.text.Text>`_
                 used to display the parallels labels on the y axis
    mlines       All `lines <http://matplotlib.org/api/lines_api.html#matplotlib.lines.Line2D>`_
                 used to display the meridians
    mlabels      All `texts <http://matplotlib.org/api/text_api.html#matplotlib.text.Text>`_
                 used to display the meridians labels on the x axis
    ============ ==================================================================================

    Examples
    --------

    - `legend_title='abc'` will call `legend(..., title='abc', ...)`
    - `labels_path_effects=[PathEffects.withStroke(linewidth=2, foreground='white')]` will set the
      a white contour around each label text
    - `meridians_labelstyle="+/-"` will call `Basemap.drawmeridians(..., labelstyle="+/-", ...)`

    Notes:
    ------
    The objects referenced by `plines`, `plabels`, `mlines`, `mlabels` and `legendlabels`
    cannot be initialized directly with the given properties, which will be set after they are
    created assuming that for any property `foo` passed as keyword argument in their constructor
    there exist a method `set_foo(...)` (which will be called with the given propertyvalue).
    This is most likely always true according to matplotlib api, but we cannot assure it works
    100% of the times
    """
    lons, lats, labels, sizes, colors, markers, legendlabels =\
        _shapeargs(lons, lats, labels, sizes, colors, markers, legendlabels)

    # convert html strings to tuples of rgba values in [0.1] if the former are in string format,
    # because (maybe too old matplotlib version?) colors in the format '#RGBA' are not supported
    # Also, if cmap is provided, basemap.scatter calls matplotlib.scatter which
    # wants float sequenes in case of color map
    if colors.dtype.char in ('U', 'S'):  # pylint: disable=no-member
        colors = np.array([torgba(c) for c in colors])

    fig = plt.figure()
    map_ax = fig.add_axes([0, 0, 1, 1])  # set axes size the same as figure

    # setup handler for managing basemap coordinates and meridians / parallels calculation:
    handler = MapHandler(lons, lats, mapmargins)

    kwa = _joinargs('basemap', kwargs,
                    llcrnrlon=handler.llcrnrlon,
                    llcrnrlat=handler.llcrnrlat,
                    urcrnrlon=handler.urcrnrlon,
                    urcrnrlat=handler.urcrnrlat,
                    epsg='4326',  # 4326,  # 3395,  # 3857,
                    resolution='i', # 'h',
                    ax=map_ax)
    bmap = Basemap(**kwa)

    try:
        kwa = _joinargs("arcgis", kwargs, service=arcgis_service, xpixels=arcgis_xpixels,
                        dpi=arcgis_dpi)
        # set the map image via a map service. In case you need the returned values, note that
        # This function returns an ImageAxis (or AxisImage, check matplotlib doc)
        bmap.arcgisimage(**kwa)
    except (URLError, HTTPError, socket.error) as exc:
        # failed, maybe there is not internet connection
        if urlfail == 'ignore':
            # Print a simple map offline
            bmap.drawcoastlines()
            watercolor = '#4444bb'
            bmap.fillcontinents(color='#eebb66', lake_color=watercolor)
            bmap.drawmapboundary(fill_color=watercolor)
        else:
            raise

    # draw meridians and parallels. From basemap.drawmeridians / drawparallels doc:
    # returns a dictionary whose keys are the meridian values, and
    # whose values are tuples containing lists of the
    # matplotlib.lines.Line2D and matplotlib.text.Text instances
    # associated with each meridian. Deleting an item from the
    # dictionary removes the correpsonding meridian from the plot.
    if maxparallels > 0:
        kwa = _joinargs("parallels", kwargs, linewidth=1, fontsize=fontsize,
                        labels=[0, 1, 1, 0], fontweight=fontweight)
        parallels = handler.get_parallels(maxparallels)
        # Old basemap versions have problems with non-integer parallels.
        try:
            # Note: the method below # returns a list of text object
            # represeting the tick labels
            _dict = bmap.drawparallels(parallels, **kwa)
        except KeyError:
            parallels = sorted(list(set(map(int, parallels))))
            _dict = bmap.drawparallels(parallels, **kwa)

        # set custom properties:
        kwa_lines = _joinargs("plines", kwargs)
        kwa_labels = _joinargs("plabels", kwargs, color=fontcolor)
        _mp_set_custom_props(_dict, kwa_lines, kwa_labels)

    if maxmeridians > 0:
        kwa = _joinargs("meridians", kwargs, linewidth=1, fontsize=fontsize,
                        labels=[1, 0, 0, 1], fontweight=fontweight)
        meridians = handler.get_meridians(maxmeridians)
        _dict = bmap.drawmeridians(meridians, **kwa)

        # set custom properties:
        kwa_lines = _joinargs("mlines", kwargs)
        kwa_labels = _joinargs("mlabels", kwargs, color=fontcolor)
        _mp_set_custom_props(_dict, kwa_lines, kwa_labels)

    # fig.get_axes()[0].tick_params(direction='out', length=15)  # does not work, check basemap
    fig.bmap = bmap

    # compute the native bmap projection coordinates for events.

    # from the docs (this is kind of outdated, however leave here for the moment):
    # Calling a Basemap class instance with the arguments lon, lat will
    # convert lon/lat (in degrees) to x/y map projection
    # coordinates (in meters).  If optional keyword ``inverse`` is
    # True (default is False), the inverse transformation from x/y
    # to lon/lat is performed.

    # For cylindrical equidistant projection (``cyl``), this
    # does nothing (i.e. x,y == lon,lat).

    # For non-cylindrical projections, the inverse transformation
    # always returns longitudes between -180 and 180 degrees. For
    # cylindrical projections (self.projection == ``cyl``,
    # ``cea``, ``mill``, ``gall`` or ``merc``)
    # the inverse transformation will return longitudes between
    # self.llcrnrlon and self.llcrnrlat.

    # Input arguments lon, lat can be either scalar floats,
    # sequences, or numpy arrays.

    # parse hoffset and voffset and assure they are at least arrays of 1 elements
    # (for aligning text labels, see below)
    hoffset = np.array(parse_distance(labels_h_offset, lats), copy=False, ndmin=1)
    voffset = np.array(parse_distance(labels_v_offset), copy=False, ndmin=1)
    lbl_lons = lons + hoffset
    lbl_lats = lats + voffset

    # convert labels coordinates:
    xlbl, ylbl = bmap(lbl_lons, lbl_lats)

    # plot point labels
    max_points = -1  # negative means: plot all
    if max_points < 0 or len(lons) < max_points:
        # Set alignments which control also the corner point reference when placing labels
        # from (FIXME: add ref?)
        # horizontalalignment controls whether the x positional argument for the text indicates
        # the left, center or right side of the text bounding box.
        # verticalalignment controls whether the y positional argument for the text indicates
        # the bottom, center or top side of the text bounding box.
        # multialignment, for newline separated strings only, controls whether the different lines
        # are left, center or right justified
        ha = 'left' if hoffset[0] > 0 else 'right' if hoffset[0] < 0 else 'center'
        va = 'bottom' if voffset[0] > 0 else 'top' if voffset[0] < 0 else 'center'
        ma = ha
        kwa = _joinargs("labels", kwargs, fontweight=fontweight, color=fontcolor,
                        zorder=100, fontsize=fontsize, horizontalalignment=ha,
                        verticalalignment=va, multialignment=ma)

        for name, xpt, ypt in zip(labels, xlbl, ylbl):
            # Check if the point can actually be seen with the current bmap
            # projection. The bmap object will set the coordinates to very
            # large values if it cannot project a point.
            if xpt > 1e25:
                continue
            map_ax.text(xpt, ypt, name, **kwa)

    # plot points
    x, y = bmap(lons, lats)
    # store handles to points, and relative labels, if any
    leg_handles, leg_labels = [], []
    # bmap.scatter accepts all array-like args except markers. Avoid several useless loops
    # and do only those for distinct markers:
    # unique markers (sorted according to their index in markers, not their value):
    mrks = markers[np.sort(np.unique(markers, return_index=True)[1])]
    for mrk in mrks:
        # Note using masks with '==' (numpy==1.11.3):
        #
        # >>> a = np.array([1,2,3])
        # >>> a == 3
        # array([False, False,  True], dtype=bool)  # OK
        # >>> a == None
        # False                                     # NOT AS EXPECTED!
        # >>> np.equal(a, None)
        # array([False, False, False], dtype=bool)  # OK
        #
        # (Note also that a == None issues:
        # FutureWarning: comparison to `None` will result in an elementwise object
        # comparison in the future.)
        #
        # So the correct way is to write
        # mask = np.equal(array, val) if val is None else (a == val)

        m_mask = np.equal(markers, mrk) if mrk is None else markers == mrk  # see above
        __x = x[m_mask]
        __y = y[m_mask]
        __m = mrk
        __s = sizes[m_mask]
        __c = colors[m_mask]
        __l = legendlabels[m_mask]
        # unique legends (sorted according to their index in __l, not their value):
        for leg in __l[np.sort(np.unique(__l, return_index=True)[1])]:
            l_mask = np.equal(__l, leg) if leg is None else __l == leg  # see above
            _scatter = bmap.scatter(__x[l_mask],
                                    __y[l_mask],
                                    marker=mrk,
                                    s=__s[l_mask],
                                    c=__c[l_mask],
                                    cmap=cmap,
                                    zorder=10)
            if leg:
                leg_handles.append(_scatter)
                leg_labels.append(leg)

    if leg_handles:
        # if we provided `legend_loc`, use that:
        loc = kwargs.get('legend_loc', None)
        bbox_to_anchor = None  # defaults in matplotlib legend
        # we do have legend to show. Adjust legend reference corner:
        if loc is None:
            if legend_pos == 'bottom':
                loc = 'upper center'
                bbox_to_anchor = (0.5, -0.05)
            elif legend_pos == 'top':
                loc = 'lower center'
                bbox_to_anchor = (0.5, 1.05)
            elif legend_pos == 'left':
                loc = 'center right'
                bbox_to_anchor = (-0.05, 0.5)
            elif legend_pos == 'right':
                loc = 'center left'
                bbox_to_anchor = (1, 0.5)
            else:
                raise ValueError('invalid legend_pos value:"%s"' % legend_pos)

        # The plt.legend has the prop argument which sets the font properties:
        # family, style, variant, weight, stretch, size, fname. See
        # http://matplotlib.org/api/font_manager_api.html#matplotlib.font_manager.FontProperties
        # However, that property does not allow to set font color. So we
        # use the get_text method of Legend. Note that we pass font size *now* even if
        # setting it later works as well (the legend frame is resized accordingly)
        kwa = _joinargs("legend", kwargs, scatterpoints=1, ncol=legend_ncol, loc=loc,
                        bbox_to_anchor=bbox_to_anchor, borderaxespad=legend_borderaxespad,
                        fontsize=fontsize)
        # http://stackoverflow.com/questions/17411940/matplotlib-scatter-plot-legend
        leg = map_ax.legend(leg_handles, leg_labels, **kwa)
        # set properties supplied via 'legend_'
        _setprop(leg.get_texts(), _joinargs("legendlabels", kwargs, color=fontcolor))

    # re-position the axes. The REAL map aspect ratio seems to be this:
    realratio_h_w = bmap.aspect
    fig_w, fig_h = fig.get_size_inches()
    figratio_h_w = np.true_divide(fig_h, fig_w)

    if figratio_h_w >= realratio_h_w:
        # we have margins (blank space) above and below
        # thus, we assume:
        map_w = fig_w
        # and we calculate map_h
        map_h = map_w * realratio_h_w
        # assume there is the same amount of space above and below:
        vpad = (fig_h - map_h) / 2.0
        # hpad is zero:
        hpad = 0
    else:
        # we have margins (blank space) left and right
        # thus, we assume:
        map_h = fig_h
        # and consequently:
        map_w = map_h / realratio_h_w
        # assume there is the same amount of space above and below:
        hpad = (fig_w - map_w) / 2.0
        # wpad is zero:
        vpad = 0

    # calculate new fig dimensions EXACTLY as contour of the map
    new_fig_w = fig_w - 2 * hpad
    new_fig_h = fig_h - 2 * vpad

    # now margins:
    marginz = parse_margins(figmargins)  # margins are in fontheight units. Get font height:
    fontsize_inch = 0
    if len(np.nonzero(marginz)[0]):
        # Calculate the font size in pixels.
        # We want to be consistent with matplotlib way of getting fontsize.
        # inspecting matplotlib.legend.Legend.draw we end up with:
        # 1. Get the renderer
        rend = fig.canvas.get_renderer()
        # 2. get the fontsize in points. We might use `fontsize` but it might be None and we want
        # the default in case. There are several 'defaults' (rcParams['font.size'],
        # rcParams["legend.fontsize"])... we don't care for now, use the first. How to get
        # rcParams['font.size'] ? Either this: (see at matplotlib.Legend.__init__):
        # fontsize_pt = FontProperties(size=fontsize, weight=fontweight).get_size_in_points()
        # or simply do:
        fontsize_pt = fontsize or rcParams['font.size']
        # Now use renderer to convert to pixels:
        # For info see matplotlib.text.Text.get_window_extent
        fontsize_px = rend.points_to_pixels(fontsize_pt)
        # finally inches:
        fontsize_inch = pix2inch(rend.points_to_pixels(fontsize_px), fig)

    # calculate insets in inches (top right bottom left)
    insets_inch = marginz * fontsize_inch

    # set to fig dimensions
    new_fig_w += insets_inch[1] + insets_inch[3]
    new_fig_h += insets_inch[0] + insets_inch[2]

    fig.set_size_inches(new_fig_w, new_fig_h, forward=True)
    # (forward necessary if fig is in GUI, let's set for safety)

    # now the axes which are relative to the figure. Thus first normalize inches:
    insets_inch /= [fig_h, fig_w, fig_h, fig_w]

    # pos1 = map_ax.get_position()  # get the original position
    # NOTE: it seems that pos[0], pos[1] indicate the x and y of the LOWER LEFT corner, not
    # upper left!
    pos2 = [insets_inch[3], insets_inch[2],
            1 - (insets_inch[1] + insets_inch[3]),
            1 - (insets_inch[0] + insets_inch[2])]
    map_ax.set_position(pos2)

    if title:
        plt.suptitle(title)

    if show:
        plt.show()

    return fig


"""Fixture module to skip the datasets loading when offline

Mock urllib2 access to mldata.org and create a temporary data folder.
"""

from os import makedirs
from os.path import join
import numpy as np
import tempfile
import shutil

from sklearn import datasets
from sklearn.utils.testing import install_mldata_mock
from sklearn.utils.testing import uninstall_mldata_mock

def globs(globs):
    # Create a temporary folder for the data fetcher
    global custom_data_home
    custom_data_home = tempfile.mkdtemp()
    makedirs(join(custom_data_home, 'mldata'))
    globs['custom_data_home'] = custom_data_home
    return globs

def setup_module():
    # setup mock urllib2 module to avoid downloading from mldata.org
    install_mldata_mock({
        'mnist-original': {
            'data': np.empty((70000, 784)),
            'label': np.repeat(np.arange(10, dtype='d'), 7000),
        },
        'iris': {
            'data': np.empty((150, 4)),
        },
        'datasets-uci-iris': {
            'double0': np.empty((150, 4)),
            'class': np.empty((150,)),
        },
    })

def teardown_module():
    uninstall_mldata_mock()
    shutil.rmtree(custom_data_home)