def hatching(ax,
             lat,
             lon,
             condition,
             hatch='/////',
             force=False,
             wrap_lon=False):
    """Adds a hatching layer to an axis object.

    Parameters
    ----------
    ax : matplotlib.pyplot.axis object
    lat : sequence of float, shape (M,)
    lon : sequence of float, shape (N,)
    condition : sequence of bool, shape (M, N)
        Hatching will be drawn where condition is True.
    hatch : valid hatch string
        Note that multiple equivalent characters will lead to a finer hatching.
    force : bool, optional
        If True also work with unevenly spaced lat and lon. This might lead to
        unexpected behavior if the gird is too uneven.
    wrap_lon : bool, optional
        Wrap longitude to [-180, 180).

    Returns
    -------
    None"""
    if isinstance(lat, xr.core.dataarray.DataArray):
        lat = lat.data
    if isinstance(lon, xr.core.dataarray.DataArray):
        lon = lon.data

    dlat = np.unique(lat[1:] - lat[:-1])
    dlon = np.unique(lon[1:] - lon[:-1])

    if force:
        dlat = [np.mean(dlat)]
        dlon = [np.mean(dlon)]

    assert len(dlat) == 1, 'must be evenly spaced'
    assert len(dlon) == 1, 'must be evenly spaced'
    dxx = dlon[0] / 2
    dyy = dlat[0] / 2
    assert np.shape(condition) == (len(lat), len(lon))
    ii, jj = np.where(condition)
    lat_sel = lat[ii]
    lon_sel = lon[jj]

    if wrap_lon:
        lon_sel = [ll if ll < 180 else ll - 360 for ll in lon_sel]

    patches = [
        Polygon([[xx - dxx, yy + dyy], [xx - dxx, yy - dyy],
                 [xx + dxx, yy - dyy], [xx + dxx, yy + dyy]])
        for xx, yy in zip(lon_sel, lat_sel)
    ]
    pp = PatchCollection(patches)
    pp.set_alpha(0.)
    pp.set_hatch(hatch)
    ax.add_collection(pp)
Example #2
0
class PlotConductors(object):
    # Supported conductor types
    conductor_types = ['Box']

    # Attributes template
    conductor_attributes = {'xcent': None,
                            'ycent': None,
                            'zcent': None,
                            'xsize': None,
                            'ysize': None,
                            'zsize': None,
                            'voltage': None,
                            'permeability': None,
                            'permittivity': None}

    def __init__(self, plot_axes=None, xbounds=None, zbounds=None):
        """
        Class for plotting of conductors in 2D (XZ).
        Will plot conductors in simulation domain on an X vs Z plot.
        Bounds and scaling are automatically determined
        Args:
            plot_axes: Optional matplotlib axes object to pass for plotting. Normally the axes object is
            generated by PlotConductors automatically.
            xbounds (tuple)(xmin, xmax): Optional Set bounds in x for plotting.
            Normally determined from Warp values in memory.
            zbounds (tuple)(zmin, zmax): Optional Set bounds in z for plotting.
            Normally determined from Warp values in memory.
        """
        try:
            self.xmin = w3d.xmmin
            self.xmax = w3d.xmmax
            self.zmin = w3d.zmmin
            self.zmax = w3d.zmmax
        except (NameError, AttributeError):
            try:
                self.xmin = xbounds[0]
                self.xmax = xbounds[1]
                self.zmin = zbounds[0]
                self.zmax = zbounds[1]
            except TypeError:
                raise TypeError("Must assign xbounds and zbounds")

        if xbounds:
            self.xmin = xbounds[0]
            self.xmax = xbounds[1]
        if zbounds:
            self.zmin = zbounds[0]
            self.zmax = zbounds[1]

        # Try to guess an ideal scaling
        if abs(self.xmax - self.xmin) * 1. > 10.:
            self.scale = 1.
        elif abs(self.xmax - self.xmin) * 1e3 > 1.:
            self.scale = 1e3
        elif abs(self.xmax - self.xmin) * 1e6 > 1.:
            self.scale = 1e6
        else:
            self.scale = 1e9

        self.fig = None
        self.plot_axes = plot_axes
        self.legend_axes = None
        self.conductors = []
        self.voltages = []
        self.dielectrics = []
        self.permittivities = []
        self.conductor_patches = None
        self.dielectric_patches = None
        self.conductor_patch_colors = []
        self.dielectric_patch_colors = []
        self.conductor_legend_handles = []
        self.dielectric_legend_handles = []
        self.legend_fontsize = 5
        self.legend_anchor = (2.25, 1.0)

        # Color options
        self.map = plt.cm.seismic
        self.positive_voltage = self.map(15)
        self.negative_voltage = self.map(240)
        self.ground_voltage = 'grey'
        self.variable_voltage_color = True  # If true use color that varies with voltage, else fixed color for +/-

    def __call__(self, solver):
        self.solver = solver
        self.conductor_coordinates(solver)
        self.create_axes()
        self.conductor_collection()
        plt.grid()

    @run_once
    def conductor_coordinates(self, solver):
        """
        Runs logic for finding which conductors can be plotted and run appropriate patch creation functions.
        Args:
            solver: Warp fieldsolver object containing conductors to be plotted.

        Returns:
                None
        """

        # Iterate through all conductor lists in the solver
        for key in solver.installedconductorlists:
            # Iterate through all conductor objects
            for conductor in solver.installedconductorlists[key]:
                # Perform check to make sure this is a conductor the code knows how to handle
                for obj_type in self.conductor_types:
                    if isinstance(conductor, getattr(field_solvers.generateconductors, obj_type)):
                        if conductor.permittivity is None:
                            self.conductors.append(self.set_rectangle_patch(conductor))
                            self.voltages.append(conductor.voltage)
                        if conductor.permittivity is not None:
                            self.dielectrics.append(self.set_rectangle_patch(conductor, dielectric=True))
                            self.permittivities.append(conductor.permittivity)

    def conductor_collection(self):
        # TODO: Once dielectrics register with solver add in loop to append them to dielectric array
        if not self.plot_axes:
            self.create_axes()

        if len(self.voltages) > 0:
            self.set_collection_colors(self.conductor_patch_colors, self.voltages, self.map)

            # Assign patches for conductors to the plot axes
            self.conductor_patches = PatchCollection(self.conductors)
            self.conductor_patches.set_color(self.conductor_patch_colors)
            self.plot_axes.add_collection(self.conductor_patches)

        if len(self.permittivities) > 0:
            self.set_collection_colors(self.dielectric_patch_colors, self.permittivities, plt.cm.viridis)

            # Assign patches for dielectrics to the plot axes
            self.dielectric_patches = PatchCollection(self.dielectrics)
            self.dielectric_patches.set_color(self.dielectric_patch_colors)
            self.dielectric_patches.set_hatch('//')
            self.plot_axes.add_collection(self.dielectric_patches)

            # Setup the legend and set data for legend axes
            self.create_legend()
        if len(self.voltages) > 0:
            cond_legend = self.legend_axes.legend(handles=self.conductor_legend_handles,
                                    bbox_to_anchor=self.legend_anchor,
                                    borderaxespad=0.,
                                    fontsize=self.legend_fontsize,
                                    title='Voltage (V)')
            self.legend_axes.add_artist(cond_legend)
        if len(self.permittivities) > 0:
            diel_legend = self.legend_axes.legend(handles=self.dielectric_legend_handles,
                                    bbox_to_anchor=(self.legend_anchor[0] + 0.05, self.legend_anchor[1] - 0.2),
                                    borderaxespad=0.,
                                    fontsize=self.legend_fontsize,
                                    title='   Relative\nPermittivity')
            self.legend_axes.add_artist(diel_legend)

    def set_collection_colors(self, color_collection, color_values, map):
        # Wanted color scaling to always be red for negative and blue for positive (assuming bl-r colormap)
        # Created custom linear scaling to using halves of color map for +/- consistently, even if only one sign present

        if self.variable_voltage_color:
            # Min/maxes for linear mapping of voltage to colormap
            negative_min = min(color_values)
            try:
                negative_max = max([i for i in color_values if i < 0.])
            except ValueError:
                negative_max = 0.
            try:
                positive_min = min([i for i in color_values if i > 0.])
            except ValueError:
                positive_min = 0.
            positive_max = max(color_values)

            # Perform mapping
            for voltage in color_values:
                if voltage < 0.:
                    try:
                        color = int(-115. / abs(negative_max - negative_min) * voltage - 115. /
                                    abs(negative_max - negative_min) * negative_max + 115.)
                    except ZeroDivisionError:
                        color = 240
                    color_collection.append(map(color))
                elif voltage > 0.:
                    try:
                        color = int(-113. / (positive_max - positive_min) * voltage + 113. /
                                    (positive_max - positive_min) * positive_max + 2)
                    except ZeroDivisionError:
                        color = 15
                    color_collection.append(map(color))
                elif voltage == 0.:
                    color_collection.append('grey')
        else:
            # Just use same color for all + or - voltages
            for voltage in color_values:
                if voltage < 0.:
                    color_collection.append(map(240))
                elif voltage > 0.:
                    color_collection.append(map(15))
                elif voltage == 0.:
                    color_collection.append('grey')

    def set_rectangle_patch(self, conductor, dielectric=False):
        """
        Creates a mpl.patches.Rectangle object to represent a box in the XZ plane.
        Args:
            conductor: Warp conductor object

        Returns:
            mpl.patches.Rectangle object

        """
        try:
            x = conductor.zcent
            y = conductor.xcent
            xlength = conductor.zsize
            ylength = conductor.xsize
        except:
            print "Conductor does not have correct attributes to plot: \n{}".format(conductor)
            return

        xcorner = x - xlength / 2.
        ycorner = y - ylength / 2.
        if dielectric:
            p = patches.Rectangle(
                (xcorner * self.scale, ycorner * self.scale),
                xlength * self.scale,
                ylength * self.scale)
                # fill=False,
                # lw=3,
                # color='k',
                # hatch='/')
        else:
            p = patches.Rectangle(
                (xcorner * self.scale, ycorner * self.scale),
                xlength * self.scale,
                ylength * self.scale)

        return p

    def create_axes(self):
        """
        Sets up the plotting region.
        Returns:
            None
        """

        fig = plt.figure(figsize=(12, 5))
        gs = GridSpec(1, 2, width_ratios=[20, 1])
        ax1 = fig.add_subplot(gs[0, 0])
        ax2 = fig.add_subplot(gs[0, 1])

        ax1.set_xticks(self.solver.zmesh * 1e9)
        ax1.set_yticks(self.solver.xmesh * 1e9)
        ax1.grid()

        ax2.axis('off')

        ax1.set_xlim(self.zmin * self.scale, self.zmax * self.scale)
        ax1.set_ylim(self.xmin * self.scale, self.xmax * self.scale)

        prefix = '($mm$)' * (self.scale == 1e3) + '($\mu m$)' * (self.scale == 1e6) + \
                 '($nm$)' * (self.scale == 1e9) + '($m$)' * (self.scale == 1.)

        ax1.set_xlabel('z ' + prefix)
        ax1.set_ylabel('x ' + prefix)

        self.fig = fig
        self.plot_axes = ax1
        self.legend_axes = ax2

    @run_once
    def create_legend(self):
        voltage_sort = []
        permittivity_sort = []

        for voltage, color in zip(self.voltages, self.conductor_patch_colors):
            if voltage not in voltage_sort:
                legend_artist = patches.Patch(color=color, label=voltage)
                self.conductor_legend_handles.append(legend_artist)
                voltage_sort.append(voltage)

        for permittivity, color in zip(self.permittivities, self.dielectric_patch_colors):
            if permittivity not in permittivity_sort:
                legend_artist = patches.Patch(color=color, label=permittivity, hatch='//')
                self.dielectric_legend_handles.append(legend_artist)
                permittivity_sort.append(permittivity)

        self.conductor_legend_handles = [j for (i, j) in sorted(zip(voltage_sort, self.conductor_legend_handles))]
        self.dielectric_legend_handles = [j for (i, j) in sorted(zip(permittivity_sort,
                                                                     self.dielectric_legend_handles))]

    def set_legend_properties(self, fontsize=5, anchor=(2.25, 1.0)):
        """
        Adjust legend fontsize and anchor position. Can be used to fit legend into plotting region.
        Args:
            fontsize: Fontsize for legend descriptors. Default value: 5.
            anchor (x, y):  Normalized position (0, 1) for legend. Default: (2.25, 1.0)

        Returns:

        """
        self.legend_fontsize = fontsize
        self.legend_anchor = anchor
Example #3
0
class PlotConductors(object):
    # Supported conductor types
    conductor_types = ['Box']

    # Attributes template
    conductor_attributes = {
        'xcent': None,
        'ycent': None,
        'zcent': None,
        'xsize': None,
        'ysize': None,
        'zsize': None,
        'voltage': None,
        'permeability': None,
        'permittivity': None
    }

    def __init__(self, plot_axes=None, xbounds=None, zbounds=None):
        """
        Class for plotting of conductors in 2D (XZ).
        Will plot conductors in simulation domain on an X vs Z plot.
        Bounds and scaling are automatically determined
        Args:
            plot_axes: Optional matplotlib axes object to pass for plotting. Normally the axes object is
            generated by PlotConductors automatically.
            xbounds (tuple)(xmin, xmax): Optional Set bounds in x for plotting.
            Normally determined from Warp values in memory.
            zbounds (tuple)(zmin, zmax): Optional Set bounds in z for plotting.
            Normally determined from Warp values in memory.
        """
        try:
            self.xmin = w3d.xmmin
            self.xmax = w3d.xmmax
            self.zmin = w3d.zmmin
            self.zmax = w3d.zmmax
        except (NameError, AttributeError):
            try:
                self.xmin = xbounds[0]
                self.xmax = xbounds[1]
                self.zmin = zbounds[0]
                self.zmax = zbounds[1]
            except TypeError:
                raise TypeError("Must assign xbounds and zbounds")

        if xbounds:
            self.xmin = xbounds[0]
            self.xmax = xbounds[1]
        if zbounds:
            self.zmin = zbounds[0]
            self.zmax = zbounds[1]

        # Try to guess an ideal scaling
        if abs(self.xmax - self.xmin) * 1. > 10.:
            self.scale = 1.
        elif abs(self.xmax - self.xmin) * 1e3 > 1.:
            self.scale = 1e3
        elif abs(self.xmax - self.xmin) * 1e6 > 1.:
            self.scale = 1e6
        else:
            self.scale = 1e9

        self.fig = None
        self.plot_axes = plot_axes
        self.legend_axes = None
        self.conductors = []
        self.voltages = []
        self.dielectrics = []
        self.permittivities = []
        self.conductor_patches = None
        self.dielectric_patches = None
        self.conductor_patch_colors = []
        self.dielectric_patch_colors = []
        self.conductor_legend_handles = []
        self.dielectric_legend_handles = []
        self.legend_fontsize = 5
        self.legend_anchor = (2.25, 1.0)

        # Color options
        self.map = plt.cm.seismic
        self.positive_voltage = self.map(15)
        self.negative_voltage = self.map(240)
        self.ground_voltage = 'grey'
        self.variable_voltage_color = True  # If true use color that varies with voltage, else fixed color for +/-

    def __call__(self, solver):
        self.solver = solver
        self.conductor_coordinates(solver)
        self.create_axes()
        self.conductor_collection()
        plt.grid()

    @run_once
    def conductor_coordinates(self, solver):
        """
        Runs logic for finding which conductors can be plotted and run appropriate patch creation functions.
        Args:
            solver: Warp fieldsolver object containing conductors to be plotted.

        Returns:
                None
        """

        # Iterate through all conductor lists in the solver
        for key in solver.installedconductorlists:
            # Iterate through all conductor objects
            for conductor in solver.installedconductorlists[key]:
                # Perform check to make sure this is a conductor the code knows how to handle
                for obj_type in self.conductor_types:
                    if isinstance(
                            conductor,
                            getattr(field_solvers.generateconductors,
                                    obj_type)):
                        if conductor.permittivity is None:
                            self.conductors.append(
                                self.set_rectangle_patch(conductor))
                            self.voltages.append(conductor.voltage)
                        if conductor.permittivity is not None:
                            self.dielectrics.append(
                                self.set_rectangle_patch(conductor,
                                                         dielectric=True))
                            self.permittivities.append(conductor.permittivity)

    def conductor_collection(self):
        # TODO: Once dielectrics register with solver add in loop to append them to dielectric array
        if not self.plot_axes:
            self.create_axes()

        if len(self.voltages) > 0:
            self.set_collection_colors(self.conductor_patch_colors,
                                       self.voltages, self.map)

            # Assign patches for conductors to the plot axes
            self.conductor_patches = PatchCollection(self.conductors)
            self.conductor_patches.set_color(self.conductor_patch_colors)
            self.plot_axes.add_collection(self.conductor_patches)

        if len(self.permittivities) > 0:
            self.set_collection_colors(self.dielectric_patch_colors,
                                       self.permittivities, plt.cm.viridis)

            # Assign patches for dielectrics to the plot axes
            self.dielectric_patches = PatchCollection(self.dielectrics)
            self.dielectric_patches.set_color(self.dielectric_patch_colors)
            self.dielectric_patches.set_hatch('//')
            self.plot_axes.add_collection(self.dielectric_patches)

            # Setup the legend and set data for legend axes
            self.create_legend()
        if len(self.voltages) > 0:
            cond_legend = self.legend_axes.legend(
                handles=self.conductor_legend_handles,
                bbox_to_anchor=self.legend_anchor,
                borderaxespad=0.,
                fontsize=self.legend_fontsize,
                title='Voltage (V)')
            self.legend_axes.add_artist(cond_legend)
        if len(self.permittivities) > 0:
            diel_legend = self.legend_axes.legend(
                handles=self.dielectric_legend_handles,
                bbox_to_anchor=(self.legend_anchor[0] + 0.05,
                                self.legend_anchor[1] - 0.2),
                borderaxespad=0.,
                fontsize=self.legend_fontsize,
                title='   Relative\nPermittivity')
            self.legend_axes.add_artist(diel_legend)

    def set_collection_colors(self, color_collection, color_values, map):
        # Wanted color scaling to always be red for negative and blue for positive (assuming bl-r colormap)
        # Created custom linear scaling to using halves of color map for +/- consistently, even if only one sign present

        if self.variable_voltage_color:
            # Min/maxes for linear mapping of voltage to colormap
            negative_min = min(color_values)
            try:
                negative_max = max([i for i in color_values if i < 0.])
            except ValueError:
                negative_max = 0.
            try:
                positive_min = min([i for i in color_values if i > 0.])
            except ValueError:
                positive_min = 0.
            positive_max = max(color_values)

            # Perform mapping
            for voltage in color_values:
                if voltage < 0.:
                    try:
                        color = int(-115. / abs(negative_max - negative_min) *
                                    voltage -
                                    115. / abs(negative_max - negative_min) *
                                    negative_max + 115.)
                    except ZeroDivisionError:
                        color = 240
                    color_collection.append(map(color))
                elif voltage > 0.:
                    try:
                        color = int(-113. /
                                    (positive_max - positive_min) * voltage +
                                    113. / (positive_max - positive_min) *
                                    positive_max + 2)
                    except ZeroDivisionError:
                        color = 15
                    color_collection.append(map(color))
                elif voltage == 0.:
                    color_collection.append('grey')
        else:
            # Just use same color for all + or - voltages
            for voltage in color_values:
                if voltage < 0.:
                    color_collection.append(map(240))
                elif voltage > 0.:
                    color_collection.append(map(15))
                elif voltage == 0.:
                    color_collection.append('grey')

    def set_rectangle_patch(self, conductor, dielectric=False):
        """
        Creates a mpl.patches.Rectangle object to represent a box in the XZ plane.
        Args:
            conductor: Warp conductor object

        Returns:
            mpl.patches.Rectangle object

        """
        try:
            x = conductor.zcent
            y = conductor.xcent
            xlength = conductor.zsize
            ylength = conductor.xsize
        except:
            print "Conductor does not have correct attributes to plot: \n{}".format(
                conductor)
            return

        xcorner = x - xlength / 2.
        ycorner = y - ylength / 2.
        if dielectric:
            p = patches.Rectangle((xcorner * self.scale, ycorner * self.scale),
                                  xlength * self.scale, ylength * self.scale)
            # fill=False,
            # lw=3,
            # color='k',
            # hatch='/')
        else:
            p = patches.Rectangle((xcorner * self.scale, ycorner * self.scale),
                                  xlength * self.scale, ylength * self.scale)

        return p

    def create_axes(self):
        """
        Sets up the plotting region.
        Returns:
            None
        """

        fig = plt.figure(figsize=(12, 5))
        gs = GridSpec(1, 2, width_ratios=[20, 1])
        ax1 = fig.add_subplot(gs[0, 0])
        ax2 = fig.add_subplot(gs[0, 1])

        ax1.set_xticks(self.solver.zmesh * 1e9)
        ax1.set_yticks(self.solver.xmesh * 1e9)
        ax1.grid()

        ax2.axis('off')

        ax1.set_xlim(self.zmin * self.scale, self.zmax * self.scale)
        ax1.set_ylim(self.xmin * self.scale, self.xmax * self.scale)

        prefix = '($mm$)' * (self.scale == 1e3) + '($\mu m$)' * (self.scale == 1e6) + \
                 '($nm$)' * (self.scale == 1e9) + '($m$)' * (self.scale == 1.)

        ax1.set_xlabel('z ' + prefix)
        ax1.set_ylabel('x ' + prefix)

        self.fig = fig
        self.plot_axes = ax1
        self.legend_axes = ax2

    @run_once
    def create_legend(self):
        voltage_sort = []
        permittivity_sort = []

        for voltage, color in zip(self.voltages, self.conductor_patch_colors):
            if voltage not in voltage_sort:
                legend_artist = patches.Patch(color=color, label=voltage)
                self.conductor_legend_handles.append(legend_artist)
                voltage_sort.append(voltage)

        for permittivity, color in zip(self.permittivities,
                                       self.dielectric_patch_colors):
            if permittivity not in permittivity_sort:
                legend_artist = patches.Patch(color=color,
                                              label=permittivity,
                                              hatch='//')
                self.dielectric_legend_handles.append(legend_artist)
                permittivity_sort.append(permittivity)

        self.conductor_legend_handles = [
            j
            for (i,
                 j) in sorted(zip(voltage_sort, self.conductor_legend_handles))
        ]
        self.dielectric_legend_handles = [
            j for (i, j) in sorted(
                zip(permittivity_sort, self.dielectric_legend_handles))
        ]

    def set_legend_properties(self, fontsize=5, anchor=(2.25, 1.0)):
        """
        Adjust legend fontsize and anchor position. Can be used to fit legend into plotting region.
        Args:
            fontsize: Fontsize for legend descriptors. Default value: 5.
            anchor (x, y):  Normalized position (0, 1) for legend. Default: (2.25, 1.0)

        Returns:

        """
        self.legend_fontsize = fontsize
        self.legend_anchor = anchor
Example #4
0
def plot_shapefile(f,
                   options='counties',
                   more_options=None,
                   cm='blues',
                   df=None,
                   probas_dict=None,
                   local=False,
                   true_idx=None,
                   show=False,
                   save=False):
    '''
    INPUT:  (1) string: shapefile to use
            (2) string: options to specify that build a nice plot
                        'counties' or 'rocktypes' or 'geologic_history'
                        'counties' will plot the counties of just CO,
                        even though this shapefile includes some counties
                        in neighboring states
                        'rocktypes' will plot all distinct rocktypes in CO
                        'geologic_history' plots the rocktypes by age
                        rather than unique type (4 age ranges rather than
                        29 primary rocktypes)
            (3) string: more options that specify for counties what colors
                        to plot
                        'by_img_color' or 'by_probability'
                        'by_img_color' will plot the avg color of each img
                        'by_probability' will plot scale the colormap to
                        reflect the probability that a given image is
            (4) string: colormap to specify
                        'blues' or 'continuous'
                        'blues' is easy on the eyes for random assignment
                        'continuous' is good for probabilities
            (5) Pandas DataFrame: referenced for plotting purposes with
                        some options
            (6) dictionary: counties as keys, probabilities associated with
                        that county being the true county according to the CNN
                        as values
            (7) boolean: local photos or not? used for lazy purposes when
                        showing iPhone photos
            (8) integer: the index in the dataframe of the true county
            (9) boolean: show the plot?
            (10) boolean: save the plot?
    OUPUT:  (1) The plotted shapefile, saved or to screen as specified
    '''

    fig = plt.figure(figsize=(20, 10))
    ax = fig.add_subplot(111, axisbg='w', frame_on=False)
    m = Basemap(width=800000,
                height=550000,
                resolution='l',
                projection='aea',
                lat_1=37.,
                lat_2=41,
                lon_0=-105.55,
                lat_0=39)
    m.readshapefile(f, name='state', color='none')

    # OPTIONS #
    if options == 'rocktypes':
        rocks = np.unique(
            [shape_info['ROCKTYPE1'] for shape_info in m.state_info])
        num_colors = len(rocks)
    elif options == 'geologic_history':
        geologic_time_dictionary = load_geologic_history()
        ranges = [0, 5, 20, 250, 3000]  # in Myr
        all_ranges = []
        for r, r_plus1 in zip(ranges[:-1], ranges[1:]):
            all_ranges += [str(r) + '-' + str(r_plus1)]
        num_colors = len(all_ranges)
    elif more_options == 'by_probability':
        max_proba = max(probas_dict.values())
        num_colors = 101
        proba_range = np.linspace(0.0, max_proba, num_colors)

    # COLOR MAPS #
    if cm == 'blues':
        cmap = plt.get_cmap('Blues')
    elif cm == 'continuous':
        cmap = make_cmyk_greyscale_continuous_cmap()
    else:
        cmap = plt.get_cmap(cm)
    discrete_colormap = [cmap(1. * i / num_colors) for i in range(num_colors)]

    # LOOP THROUGH SHAPEFILES #
    for info, shape in zip(m.state_info, m.state):
        patches = [Polygon(np.array(shape), True)]
        pc = PatchCollection(patches,
                             edgecolor='k',
                             hatch=None,
                             linewidths=0.5,
                             zorder=2)

        # OPTIONS OF HOW TO PLOT THE SHAPEFILE #
        if options == 'counties':
            county_name = info['COUNTY_NAM']
            state_name = info['STATE_NAME']
            if state_name != 'Colorado':
                continue  # ignore shapefiles from out of CO

            # MORE OPTIONS FOR COUNTIES #
            if more_options == 'by_img_color':
                locs = np.where(df['county'] == county_name)[0]
                r_avg = df['avg_r_low'][locs].mean()
                g_avg = df['avg_g_low'][locs].mean()
                b_avg = df['avg_b_low'][locs].mean()
                pc.set_color((r_avg / 255., g_avg / 255., b_avg / 255.))
            elif more_options == 'by_probability':
                proba = probas_dict[county_name]
                proba_idx = int(proba / max_proba * 100)
                pc.set_color(discrete_colormap[proba_idx])
                pc.set_edgecolor('k')
                if local:
                    if county_name == 'Denver':
                        pc.set_hatch('//')
                        pc.set_edgecolor('w')
                if county_name == df['county'][true_idx] and not local:
                    if proba_idx > 60:
                        pc.set_hatch('//')
                        pc.set_edgecolor('w')
                    else:
                        pc.set_hatch('//')
                        pc.set_edgecolor('k')
            else:
                pc.set_color(random.choice(discrete_colormap))
        elif options == 'rocktypes':
            rocktype = info['ROCKTYPE1']
            idx = np.where(rocks == rocktype)[0][0]
            pc.set_color(discrete_colormap[idx])
        elif options == 'geologic_history':
            rock_age = info['UNIT_AGE']
            for index, (r, r_plus1) in enumerate(zip(ranges[:-1], ranges[1:])):
                try:
                    if ((geologic_time_dictionary[rock_age] > r) &
                        (geologic_time_dictionary[rock_age] < r_plus1)):
                        idx = index
                except:
                    idx = 2  # 20-250 was a good middleground for nans
            pc.set_color(discrete_colormap[idx])
        elif options == 'nofill':
            pc.set_facecolor('none')
        else:
            pc.set_color(random.choice(discrete_colormap))
        if more_options == 'by_probability':
            ax2 = fig.add_axes([0.78, 0.1, 0.03, 0.8])
            cb = matplotlib.colorbar.ColorbarBase(ax2,
                                                  cmap=cmap,
                                                  ticks=proba_range,
                                                  boundaries=proba_range,
                                                  format='%1i')
            labels = [
                str(round(proba, 4)) if idx % 10 == 0 else ''
                for idx, proba in enumerate(proba_range)
            ]
            cb.ax.set_yticklabels(labels)
            cb.ax.set_ylabel('Probability')
        ax.add_collection(pc)

    NESW = ['N', 'E', 'S', 'W']
    if local:
        # limited flexibility here for displaying iPhone photos on the side
        filenums = [5919, 5918, 5917, 5920]
        filenames = [
            '''/Users/jliemansifry/Desktop/outside_galvanize_test/
                     IMG_{} (1).jpg'''.format(num) for num in filenums
        ]
    else:
        filenames = [
            df['base_filename'][true_idx] + cardinal_dir + '.png'
            for cardinal_dir in NESW
        ]
    if more_options == 'by_probability':

        def add_image(filename, y, label):
            ax_to_add = fig.add_axes([0., y, .32, .20])
            ax_to_add.imshow(imread(filename))
            ax_to_add.set_xticks([])
            ax_to_add.set_yticks([])
            ax_to_add.set_ylabel(label)

        ys = [0.7, 0.5, 0.3, 0.1]
        labels = ['North', 'East', 'South', 'West']
        for filename, y, label in zip(filenames, ys, labels):
            add_image(filename, y, label)
    if save:
        if local:
            plt.savefig('model_testing/county_model_v1.2_' + 'Denver.png',
                        dpi=150)
        else:
            true_county = df['county'][true_idx]
            plt.savefig(
                '''model_testing/county_model_v1.3_newtruecolors_{}_idx_{}.png'''
                .format(true_county, true_idx),
                dpi=150)
    if show:
        plt.show()