├── README.md ├── spnspecs ├── fonts │ ├── Univers-Condensed.otf │ ├── Univers-CondensedBold.otf │ ├── Univers-CondensedLight.otf │ ├── Univers-CondensedOblique.otf │ ├── Univers-CondensedBoldOblique.otf │ └── Univers-CondensedLightOblique.otf ├── version.py ├── __init__.py ├── spnspecs.py ├── graphs.py ├── maps.py └── utils.py └── .gitignore /README.md: -------------------------------------------------------------------------------- 1 | # usgs_figure_specifications 2 | Simple class that defines USGS figure specifications for matplotlib 3 | -------------------------------------------------------------------------------- /spnspecs/fonts/Univers-Condensed.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhughes-dev/usgs_figure_specifications/HEAD/spnspecs/fonts/Univers-Condensed.otf -------------------------------------------------------------------------------- /spnspecs/fonts/Univers-CondensedBold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhughes-dev/usgs_figure_specifications/HEAD/spnspecs/fonts/Univers-CondensedBold.otf -------------------------------------------------------------------------------- /spnspecs/fonts/Univers-CondensedLight.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhughes-dev/usgs_figure_specifications/HEAD/spnspecs/fonts/Univers-CondensedLight.otf -------------------------------------------------------------------------------- /spnspecs/fonts/Univers-CondensedOblique.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhughes-dev/usgs_figure_specifications/HEAD/spnspecs/fonts/Univers-CondensedOblique.otf -------------------------------------------------------------------------------- /spnspecs/fonts/Univers-CondensedBoldOblique.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhughes-dev/usgs_figure_specifications/HEAD/spnspecs/fonts/Univers-CondensedBoldOblique.otf -------------------------------------------------------------------------------- /spnspecs/fonts/Univers-CondensedLightOblique.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdhughes-dev/usgs_figure_specifications/HEAD/spnspecs/fonts/Univers-CondensedLightOblique.otf -------------------------------------------------------------------------------- /spnspecs/version.py: -------------------------------------------------------------------------------- 1 | # flopy version file automatically created using...make-release.py 2 | # created on...March 22, 2019 22:10:55 3 | 4 | major = 0 5 | minor = 0 6 | micro = 1 7 | __version__ = '{:d}.{:d}.{:d}'.format(major, minor, micro) 8 | -------------------------------------------------------------------------------- /spnspecs/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | The spnspecs package implements SPN figure specifications for Graphs and 3 | Diagrams, ... 4 | 5 | """ 6 | 7 | __name__ = 'spnspecs' 8 | __author__ = 'Joseph D. Hughes' 9 | 10 | from .version import __version__ 11 | 12 | # imports 13 | from . import spnspecs 14 | from .graphs import set_graph_specifications 15 | from .maps import set_map_specifications 16 | from .utils import graph_legend, graph_legend_title, \ 17 | heading, set_font, add_text, add_annotation, \ 18 | remove_edge_ticks 19 | 20 | #from .mbase import run_model, which 21 | -------------------------------------------------------------------------------- /spnspecs/spnspecs.py: -------------------------------------------------------------------------------- 1 | import os 2 | import matplotlib.font_manager as mplfm 3 | 4 | import spnspecs 5 | 6 | print('{} - initializing fonts'.format(spnspecs.__name__)) 7 | 8 | # load files 9 | pth = os.path.dirname(os.path.abspath(spnspecs.__file__)) 10 | pth = os.path.join(pth, 'fonts') 11 | print('loading fonts from..."{}"'.format(pth)) 12 | 13 | # get list of available font files 14 | font_files = mplfm.findSystemFonts(fontpaths=pth) 15 | 16 | # create font list 17 | font_list = mplfm.createFontList(font_files, fontext='otf') 18 | 19 | # add Oblique to oblique (italic) fonts 20 | for font in font_list: 21 | if font.style == 'oblique': 22 | font.name += ' Oblique' 23 | 24 | # write summary of available fonts 25 | print('\navailable fonts:') 26 | for idx, font in enumerate(font_list): 27 | print(' {} - {}'.format(idx+1, font)) 28 | 29 | # add files to matplotlib 30 | mplfm.fontManager.ttflist.extend(font_list) -------------------------------------------------------------------------------- /spnspecs/graphs.py: -------------------------------------------------------------------------------- 1 | import matplotlib as mpl 2 | 3 | def set_graph_specifications(): 4 | rc_dict = {'font.family': 'Univers 57 Condensed', 5 | 'axes.labelsize': 9, 6 | 'axes.titlesize': 9, 7 | 'axes.linewidth': 0.5, 8 | 'xtick.labelsize': 8, 9 | 'xtick.top': True, 10 | 'xtick.bottom': True, 11 | 'xtick.major.size': 7.2, 12 | 'xtick.minor.size': 3.6, 13 | 'xtick.major.width': 0.5, 14 | 'xtick.minor.width': 0.5, 15 | 'xtick.direction': 'in', 16 | 'ytick.labelsize': 8, 17 | 'ytick.left': True, 18 | 'ytick.right': True, 19 | 'ytick.major.size': 7.2, 20 | 'ytick.minor.size': 3.6, 21 | 'ytick.major.width': 0.5, 22 | 'ytick.minor.width': 0.5, 23 | 'ytick.direction': 'in', 24 | 'pdf.fonttype': 42, 25 | 'savefig.dpi': 300, 26 | 'savefig.transparent': True, 27 | 'legend.fontsize': 9, 28 | 'legend.frameon': False, 29 | 'legend.markerscale': 1. 30 | } 31 | mpl.rcParams.update(rc_dict) 32 | -------------------------------------------------------------------------------- /spnspecs/maps.py: -------------------------------------------------------------------------------- 1 | import matplotlib as mpl 2 | 3 | def set_map_specifications(): 4 | rc_dict = {'font.family': 'Univers 47 Condensed Light', 5 | 'axes.labelsize': 9, 6 | 'axes.titlesize': 9, 7 | 'axes.linewidth': 0.5, 8 | 'xtick.labelsize': 7, 9 | 'xtick.top': True, 10 | 'xtick.bottom': True, 11 | 'xtick.major.size': 7.2, 12 | 'xtick.minor.size': 3.6, 13 | 'xtick.major.width': 0.5, 14 | 'xtick.minor.width': 0.5, 15 | 'xtick.direction': 'in', 16 | 'ytick.labelsize': 7, 17 | 'ytick.left': True, 18 | 'ytick.right': True, 19 | 'ytick.major.size': 7.2, 20 | 'ytick.minor.size': 3.6, 21 | 'ytick.major.width': 0.5, 22 | 'ytick.minor.width': 0.5, 23 | 'ytick.direction': 'in', 24 | 'pdf.fonttype': 42, 25 | 'savefig.dpi': 300, 26 | 'savefig.transparent': True, 27 | 'legend.fontsize': 9, 28 | 'legend.frameon': False, 29 | 'legend.markerscale': 1. 30 | } 31 | mpl.rcParams.update(rc_dict) 32 | 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # pyCharm project settings 107 | .idea 108 | -------------------------------------------------------------------------------- /spnspecs/utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | """ 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | 7 | def graph_legend(ax, handles=None, labels=None, **kwargs): 8 | font = {'size': 9, 'weight': 'bold', 9 | 'family': 'Univers 67 Condensed'} 10 | if handles is None or labels is None: 11 | handles, labels = ax.get_legend_handles_labels() 12 | leg = ax.legend(handles, labels, prop=font, **kwargs) 13 | 14 | # add title to legend 15 | if 'title' in kwargs: 16 | title = kwargs.pop('title') 17 | else: 18 | title = None 19 | leg = graph_legend_title(leg, title=title) 20 | 21 | return leg 22 | 23 | 24 | def graph_legend_title(leg, title=None): 25 | if title is None: 26 | title = 'EXPLANATION' 27 | elif title.lower() == 'none': 28 | title = None 29 | leg.set_title(title, prop={'size': 9, 'weight': 'bold', 30 | 'family': 'Univers 67 Condensed'}) 31 | return leg 32 | 33 | 34 | def heading(ax, letter=None, heading=None, x=0.00, y=1.01): 35 | text = None 36 | if letter is not None: 37 | font = {'family': 'Univers 67 Condensed Oblique', 38 | 'size': 9, 39 | 'weight': 'bold', 40 | 'style': 'oblique'} 41 | if heading is None: 42 | letter = letter.replace('.', '') 43 | else: 44 | letter = letter.rstrip() 45 | if letter[-1] is not '.': 46 | letter += '.' 47 | letter += ' ' 48 | ax.text(x, y, letter, 49 | va='bottom', ha='left', 50 | fontdict=font, 51 | transform=ax.transAxes) 52 | bbox = ax.get_window_extent().transformed( 53 | plt.gcf().dpi_scale_trans.inverted()) 54 | width = bbox.width * 25.4 # inches to mm 55 | x += len(letter) * 1. / width 56 | if heading is not None: 57 | font = {'family': 'Univers 67 Condensed', 58 | 'size': 9, 59 | 'weight': 'bold', 60 | 'style': 'normal'} 61 | text = ax.text(x, y, heading, 62 | va='bottom', ha='left', 63 | fontdict=font, 64 | transform=ax.transAxes) 65 | return text 66 | 67 | def set_font(bold=True, italic=True, fontsize=9): 68 | if bold: 69 | weight = 'bold' 70 | family = 'Univers 67' 71 | else: 72 | weight = 'normal' 73 | family = 'Univers 57' 74 | 75 | if italic: 76 | family += ' Condensed Oblique' 77 | style = 'oblique' 78 | else: 79 | family += ' Condensed' 80 | style = 'normal' 81 | font = {'family': family, 82 | 'size': fontsize, 83 | 'weight': weight, 84 | 'style': style} 85 | 86 | return font 87 | 88 | def add_text(ax=None, text='', x=0., y=0., transform=True, 89 | bold=True, italic=True, fontsize=9, 90 | ha='left', va='bottom', **kwargs): 91 | if ax is None: 92 | return None 93 | 94 | if transform: 95 | transform = ax.transAxes 96 | else: 97 | transform = ax.transData 98 | 99 | font = set_font(bold=bold, italic=italic, 100 | fontsize=fontsize) 101 | 102 | text_obj = ax.text(x, y, text, 103 | va=va, ha=ha, 104 | fontdict=font, 105 | transform=transform, **kwargs) 106 | return text_obj 107 | 108 | def add_annotation(ax=None, text='', xy=None, xytext=None, 109 | bold=True, italic=True, fontsize=9, 110 | ha='left', va='bottom', **kwargs): 111 | if ax is None: 112 | return None 113 | 114 | if xy is None: 115 | xy = (0., 0.) 116 | 117 | if xytext is None: 118 | xytext = (0., 0.) 119 | 120 | font = set_font(bold=bold, italic=italic, 121 | fontsize=fontsize) 122 | 123 | # add font information to kwargs 124 | if kwargs is None: 125 | kwargs = font 126 | else: 127 | for key, value in font.items(): 128 | kwargs[key] = value 129 | 130 | # create annotation 131 | ann_obj = ax.annotate(text, xy, xytext, 132 | va=va, ha=ha, 133 | **kwargs) 134 | 135 | return ann_obj 136 | 137 | def remove_edge_ticks(ax, verbose=False): 138 | # update tick objects 139 | plt.draw() 140 | 141 | # get min and max value and ticks 142 | ymin, ymax = ax.get_ylim() 143 | # check for condition where y-axis values are reversed 144 | if ymax < ymin: 145 | y = ymin 146 | ymin = ymax 147 | ymax = y 148 | yticks = ax.get_yticks() 149 | if verbose: 150 | print('y-axis: ', ymin, ymax) 151 | print(yticks) 152 | 153 | # remove edge ticks on y-axis 154 | ticks = ax.yaxis.majorTicks 155 | for iloc in [0, -1]: 156 | if np.allclose(float(yticks[iloc]), ymin): 157 | ticks[iloc].tick1line.set_visible = False 158 | ticks[iloc].tick2line.set_visible = False 159 | if np.allclose(float(yticks[iloc]), ymax): 160 | ticks[iloc].tick1line.set_visible = False 161 | ticks[iloc].tick2line.set_visible = False 162 | 163 | # get min and max value and ticks 164 | xmin, xmax = ax.get_xlim() 165 | # check for condition where x-axis values are reversed 166 | if xmax < xmin: 167 | x = xmin 168 | xmin = xmax 169 | xmax = x 170 | xticks = ax.get_xticks() 171 | if verbose: 172 | print('x-axis: ', xmin, xmax) 173 | print(xticks) 174 | 175 | # remove edge ticks on y-axis 176 | ticks = ax.xaxis.majorTicks 177 | for iloc in [0, -1]: 178 | if np.allclose(float(xticks[iloc]), xmin): 179 | ticks[iloc].tick1line.set_visible = False 180 | ticks[iloc].tick2line.set_visible = False 181 | if np.allclose(float(xticks[iloc]), xmax): 182 | ticks[iloc].tick1line.set_visible = False 183 | ticks[iloc].tick2line.set_visible = False 184 | 185 | return ax --------------------------------------------------------------------------------