From: <md...@us...> - 2007-12-12 15:01:37
|
Revision: 4708 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4708&view=rev Author: mdboom Date: 2007-12-12 07:01:20 -0800 (Wed, 12 Dec 2007) Log Message: ----------- Merged revisions 4689-4706 via svnmerge from http://matplotlib.svn.sf.net/svnroot/matplotlib/trunk/matplotlib ........ r4697 | pkienzle | 2007-12-11 12:14:44 -0500 (Tue, 11 Dec 2007) | 1 line remove wx specific gui_repaint comment ........ r4698 | jdh2358 | 2007-12-11 16:17:59 -0500 (Tue, 11 Dec 2007) | 2 lines fixed text with dash bug ........ r4706 | mdboom | 2007-12-12 09:08:15 -0500 (Wed, 12 Dec 2007) | 2 lines Support alpha-blended text in the Agg and Svg backends. ........ Modified Paths: -------------- branches/transforms/examples/dashpointlabel.py branches/transforms/examples/lasso_demo.py branches/transforms/lib/matplotlib/backend_bases.py branches/transforms/lib/matplotlib/backends/backend_svg.py branches/transforms/lib/matplotlib/cbook.py branches/transforms/lib/matplotlib/patches.py branches/transforms/lib/matplotlib/text.py branches/transforms/src/_backend_agg.cpp Property Changed: ---------------- branches/transforms/ Property changes on: branches/transforms ___________________________________________________________________ Name: svnmerge-integrated - /trunk/matplotlib:1-4688 + /trunk/matplotlib:1-4706 Modified: branches/transforms/examples/dashpointlabel.py =================================================================== --- branches/transforms/examples/dashpointlabel.py 2007-12-12 14:52:01 UTC (rev 4707) +++ branches/transforms/examples/dashpointlabel.py 2007-12-12 15:01:20 UTC (rev 4708) @@ -1,4 +1,4 @@ -from matplotlib import pylab +import pylab DATA = ((1, 3), (2, 4), @@ -15,26 +15,27 @@ (1, 20, 30, 60, 10), ) -def test_dashpointlabel(save=False): - pylab.clf() - (x,y) = zip(*DATA) - pylab.plot(x, y, marker='o') - for i in xrange(len(DATA)): - (x,y) = DATA[i] - (dd, dl, r, dr, dp) = dash_style[i] - pylab.text(x, y, str((x,y)), withdash=True, - dashdirection=dd, - dashlength=dl, - rotation=r, - dashrotation=dr, - dashpush=dp, - ) - axis = pylab.gca() - axis.set_xlim((0.0, 5.0)) - axis.set_ylim((0.0, 5.0)) - if save: - pylab.savefig('dashpointlabel') - pylab.show() +fig = pylab.figure() +ax = fig.add_subplot(111) -if __name__ == '__main__': - test_dashpointlabel() + +(x,y) = zip(*DATA) +ax.plot(x, y, marker='o') +for i in xrange(len(DATA)): + (x,y) = DATA[i] + (dd, dl, r, dr, dp) = dash_style[i] + #print 'dashlen call', dl + t = ax.text(x, y, str((x,y)), withdash=True, + dashdirection=dd, + dashlength=dl, + rotation=r, + dashrotation=dr, + dashpush=dp, + ) + +ax.set_xlim((0.0, 5.0)) +ax.set_ylim((0.0, 5.0)) +#if save: +# pylab.savefig('dashpointlabel') +pylab.show() + Modified: branches/transforms/examples/lasso_demo.py =================================================================== --- branches/transforms/examples/lasso_demo.py 2007-12-12 14:52:01 UTC (rev 4707) +++ branches/transforms/examples/lasso_demo.py 2007-12-12 15:01:20 UTC (rev 4708) @@ -59,6 +59,7 @@ self.canvas.draw_idle() self.canvas.widgetlock.release(self.lasso) del self.lasso + def onpress(self, event): if self.canvas.widgetlock.locked(): return if event.inaxes is None: return Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007-12-12 14:52:01 UTC (rev 4707) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007-12-12 15:01:20 UTC (rev 4708) @@ -843,7 +843,6 @@ a.set_facecolor('lightblue') else: self._active[a] = None self.draw_idle() - #self.gui_repaint() def pick(self, mouseevent): if not self.widgetlock.locked(): Modified: branches/transforms/lib/matplotlib/backends/backend_svg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_svg.py 2007-12-12 14:52:01 UTC (rev 4707) +++ branches/transforms/lib/matplotlib/backends/backend_svg.py 2007-12-12 15:01:20 UTC (rev 4708) @@ -296,7 +296,7 @@ color = rgb2hex(gc.get_rgb()[:3]) if rcParams['svg.embed_char_paths']: - svg = ['<g style="fill: %s" transform="' % color] + svg = ['<g style="fill: %s; opacity: %s" transform="' % (color, gc.get_alpha())] if angle != 0: svg.append('translate(%s,%s)rotate(%1.1f)' % (x,y,-angle)) elif x != 0 or y != 0: @@ -335,7 +335,8 @@ fontfamily = font.family_name fontstyle = prop.get_style() - style = 'font-size: %f; font-family: %s; font-style: %s; fill: %s;'%(fontsize, fontfamily,fontstyle, color) + style = ('font-size: %f; font-family: %s; font-style: %s; fill: %s; opacity: %s' % + (fontsize, fontfamily,fontstyle, color, gc.get_alpha())) if angle!=0: transform = 'transform="translate(%s,%s) rotate(%1.1f) translate(%s,%s)"' % (x,y,-angle,-x,-y) # Inkscape doesn't support rotate(angle x y) Modified: branches/transforms/lib/matplotlib/cbook.py =================================================================== --- branches/transforms/lib/matplotlib/cbook.py 2007-12-12 14:52:01 UTC (rev 4707) +++ branches/transforms/lib/matplotlib/cbook.py 2007-12-12 15:01:20 UTC (rev 4708) @@ -11,7 +11,7 @@ set = set except NameError: from sets import Set as set - + major, minor1, minor2, s, tmp = sys.version_info @@ -974,9 +974,9 @@ .get(). The objects being joined must be hashable. - + For example: - + >>> g = grouper.Grouper() >>> g.join('a', 'b') >>> g.join('b', 'c') @@ -989,7 +989,7 @@ True >>> g.joined('a', 'd') False - """ + """ def __init__(self, init=[]): mapping = self._mapping = {} for x in init: @@ -997,7 +997,7 @@ def __contains__(self, item): return item in self._mapping - + def join(self, a, *args): """ Join given arguments into the same set. @@ -1045,7 +1045,7 @@ """ return self._mapping.get(a, [a]) - + def simple_linear_interpolation(a, steps): steps = npy.floor(steps) new_length = ((len(a) - 1) * steps) + 1 @@ -1061,7 +1061,7 @@ for i in range(1, int(steps)): result[i::steps] = delta * i + a0 result[steps::steps] = a1 - + return result if __name__=='__main__': Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2007-12-12 14:52:01 UTC (rev 4707) +++ branches/transforms/lib/matplotlib/patches.py 2007-12-12 15:01:20 UTC (rev 4708) @@ -1006,7 +1006,6 @@ dx = x1 - x0 dy = y1 - y0 dr2 = dx*dx + dy*dy - dr = npy.sqrt(dr2) D = x0*y1 - x1*y0 D2 = D*D discrim = dr2 - D2 @@ -1017,7 +1016,9 @@ y = (-D*dx) / dr2 yield x, y elif discrim > 0.0: - if dy < 0: + # The definition of "sign" here is different from + # npy.sign: we never want to get 0.0 + if dy < 0.0: sign_dy = -1.0 else: sign_dy = 1.0 @@ -1057,10 +1058,10 @@ x0, y0 = p0 x1, y1 = p1 for x, y in iter_circle_intersect_on_line_seg(x0, y0, x1, y1): - # Convert radians to angles theta = npy.arccos(x) if y < 0: theta = TWOPI - theta + # Convert radians to angles theta *= RAD2DEG if theta > theta1 and theta < theta2: thetas[theta] = None @@ -1072,7 +1073,6 @@ last_theta = theta1 theta1_rad = theta1 * DEG2RAD inside = box_path.contains_point((npy.cos(theta1_rad), npy.sin(theta1_rad))) - for theta in thetas: if inside: self._path = Path.arc(last_theta, theta, 8) Modified: branches/transforms/lib/matplotlib/text.py =================================================================== --- branches/transforms/lib/matplotlib/text.py 2007-12-12 14:52:01 UTC (rev 4707) +++ branches/transforms/lib/matplotlib/text.py 2007-12-12 15:01:20 UTC (rev 4708) @@ -368,6 +368,11 @@ "Return the horizontal alignment as string" return self._horizontalalignment + + def _get_xy_display(self): + 'get the (possibly unit converted) transformed x,y in display coords' + return self.get_transform().transform_point((self._x, self._y)) + def get_position(self): "Return x, y as tuple" x = float(self.convert_xunits(self._x)) @@ -631,6 +636,8 @@ ACCEPTS: a matplotlib.font_manager.FontProperties instance """ + if is_string_like(fp): + fp = FontProperties(fp) self._fontproperties = fp artist.kwdocd['Text'] = artist.kwdoc(Text) @@ -731,6 +738,24 @@ #self.set_bbox(dict(pad=0)) + def get_position(self): + "Return x, y as tuple" + x = float(self.convert_xunits(self._dashx)) + y = float(self.convert_yunits(self._dashy)) + return x, y + + def get_prop_tup(self): + """ + Return a hashable tuple of properties + + Not intended to be human readable, but useful for backends who + want to cache derived information about text (eg layouts) and + need to know if the text has changed + """ + props = [p for p in Text.get_prop_tup(self)] + props.extend([self._x, self._y, self._dashlength, self._dashdirection, self._dashrotation, self._dashpad, self._dashpush]) + return tuple(props) + def draw(self, renderer): self.update_coords(renderer) Text.draw(self, renderer) @@ -808,7 +833,8 @@ cwd *= 1+dashpad/npy.sqrt(npy.dot(cwd,cwd)) cw = c2+(dashdirection*2-1)*cwd - self._x, self._y = inverse.transform_point(tuple(cw)) + newx, newy = inverse.transform_point(tuple(cw)) + self._x, self._y = newx, newy # Now set the window extent # I'm not at all sure this is the right way to do this. @@ -892,9 +918,6 @@ """ self._dashpush = dp - def get_position(self): - "Return x, y as tuple" - return self._dashx, self._dashy def set_position(self, xy): """ Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007-12-12 14:52:01 UTC (rev 4707) +++ branches/transforms/src/_backend_agg.cpp 2007-12-12 15:01:20 UTC (rev 4708) @@ -625,7 +625,7 @@ do { *output_span = _color; - output_span->a = input_span->v; + output_span->a = ((unsigned int)_color.a * (unsigned int)input_span->v) >> 8; ++output_span; ++input_span; } while (--len); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-12-12 19:16:46
|
Revision: 4715 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4715&view=rev Author: mdboom Date: 2007-12-12 11:15:46 -0800 (Wed, 12 Dec 2007) Log Message: ----------- Merged revisions 4707-4714 via svnmerge from http://matplotlib.svn.sf.net/svnroot/matplotlib/trunk/matplotlib ........ r4707 | mdboom | 2007-12-12 09:52:01 -0500 (Wed, 12 Dec 2007) | 2 lines Support fontconfig syntax in Text constructor. ........ r4713 | mdboom | 2007-12-12 14:07:03 -0500 (Wed, 12 Dec 2007) | 2 lines Fix reference-counting leak when saving an Agg Png to a file-like object. ........ Modified Paths: -------------- branches/transforms/src/_backend_agg.cpp Property Changed: ---------------- branches/transforms/ Property changes on: branches/transforms ___________________________________________________________________ Name: svnmerge-integrated - /trunk/matplotlib:1-4706 + /trunk/matplotlib:1-4714 Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007-12-12 19:11:44 UTC (rev 4714) +++ branches/transforms/src/_backend_agg.cpp 2007-12-12 19:15:46 UTC (rev 4715) @@ -1289,17 +1289,21 @@ static void write_png_data(png_structp png_ptr, png_bytep data, png_size_t length) { PyObject* py_file_obj = (PyObject*)png_get_io_ptr(png_ptr); PyObject* write_method = PyObject_GetAttrString(py_file_obj, "write"); - PyObject_CallFunction(write_method, "s#", data, length); - - // MGDTODO: Check NULL on failure + PyObject* result = NULL; + if (write_method) + result = PyObject_CallFunction(write_method, "s#", data, length); + Py_XDECREF(write_method); + Py_XDECREF(result); } static void flush_png_data(png_structp png_ptr) { PyObject* py_file_obj = (PyObject*)png_get_io_ptr(png_ptr); PyObject* flush_method = PyObject_GetAttrString(py_file_obj, "flush"); - if (flush_method) { - PyObject_CallFunction(flush_method, ""); - } + PyObject* result = NULL; + if (flush_method) + result = PyObject_CallFunction(flush_method, ""); + Py_XDECREF(flush_method); + Py_XDECREF(result); } // this code is heavily adapted from the paint license, which is in @@ -1322,8 +1326,11 @@ } else { PyObject* write_method = PyObject_GetAttrString(py_fileobj.ptr(), "write"); - if (!(write_method && PyCallable_Check(write_method))) + if (!(write_method && PyCallable_Check(write_method))) { + Py_XDECREF(write_method); throw Py::TypeError("Object does not appear to be a path or a Python file-like object"); + } + Py_XDECREF(write_method); } png_bytep *row_pointers = NULL; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-12-12 20:06:44
|
Revision: 4716 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4716&view=rev Author: mdboom Date: 2007-12-12 12:06:30 -0800 (Wed, 12 Dec 2007) Log Message: ----------- Save images to Svg files without writing the image data out as a temporary file. Modified Paths: -------------- branches/transforms/lib/matplotlib/backends/backend_svg.py branches/transforms/src/_image.cpp Modified: branches/transforms/lib/matplotlib/backends/backend_svg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_svg.py 2007-12-12 19:15:46 UTC (rev 4715) +++ branches/transforms/lib/matplotlib/backends/backend_svg.py 2007-12-12 20:06:30 UTC (rev 4716) @@ -245,30 +245,33 @@ h,w = im.get_size_out() + self._svgwriter.write ( + '<image x="%s" y="%s" width="%s" height="%s" ' + '%s xlink:href="'%(x/trans[0], (self.height-y)/trans[3]-h, w, h, transstr) + ) + if rcParams['svg.image_inline']: - filename = os.path.join (tempfile.gettempdir(), - tempfile.gettempprefix() + '.png' - ) + class Base64Writer(object): + def __init__(self, write_method): + self._write_method = write_method + self._buffer = '' + def write(self, data): + self._buffer += data + while len(self._buffer) >= 64: + self._write_method(base64.encodestring(buffer[:64])) + self._write_method('\n') + self._buffer = self._buffer[64:] + def flush(self): + self._write_method(base64.encodestring(self._buffer)) + self._write_method('\n') - verbose.report ('Writing temporary image file for inlining: %s' % filename) - # im.write_png() accepts a filename, not file object, would be - # good to avoid using files and write to mem with StringIO + self._svgwriter.write("data:image/png;base64,\n") + base64writer = Base64Writer(self._svgwriter.write) - # JDH: it *would* be good, but I don't know how to do this - # since libpng seems to want a FILE* and StringIO doesn't seem - # to provide one. I suspect there is a way, but I don't know - # it - im.flipud_out() - im.write_png(filename) + im.write_png(base64writer) im.flipud_out() - - imfile = file (filename, 'rb') - image64 = base64.encodestring (imfile.read()) - imfile.close() - os.remove(filename) - hrefstr = 'data:image/png;base64,\n' + image64 - + base64writer.flush() else: self._imaged[self.basename] = self._imaged.get(self.basename,0) + 1 filename = '%s.image%d.png'%(self.basename, self._imaged[self.basename]) @@ -276,12 +279,9 @@ im.flipud_out() im.write_png(filename) im.flipud_out() - hrefstr = filename + self._svgwriter.write(filename) - self._svgwriter.write ( - '<image x="%s" y="%s" width="%s" height="%s" ' - 'xlink:href="%s" %s/>\n'%(x/trans[0], (self.height-y)/trans[3]-h, w, h, hrefstr, transstr) - ) + self._svgwriter.write('"/>\n') def draw_text(self, gc, x, y, s, prop, angle, ismath): if ismath: Modified: branches/transforms/src/_image.cpp =================================================================== --- branches/transforms/src/_image.cpp 2007-12-12 19:15:46 UTC (rev 4715) +++ branches/transforms/src/_image.cpp 2007-12-12 20:06:30 UTC (rev 4716) @@ -575,7 +575,25 @@ } +static void write_png_data(png_structp png_ptr, png_bytep data, png_size_t length) { + PyObject* py_file_obj = (PyObject*)png_get_io_ptr(png_ptr); + PyObject* write_method = PyObject_GetAttrString(py_file_obj, "write"); + PyObject* result = NULL; + if (write_method) + result = PyObject_CallFunction(write_method, "s#", data, length); + Py_XDECREF(write_method); + Py_XDECREF(result); +} +static void flush_png_data(png_structp png_ptr) { + PyObject* py_file_obj = (PyObject*)png_get_io_ptr(png_ptr); + PyObject* flush_method = PyObject_GetAttrString(py_file_obj, "flush"); + PyObject* result = NULL; + if (flush_method) + result = PyObject_CallFunction(flush_method, ""); + Py_XDECREF(flush_method); + Py_XDECREF(result); +} // this code is heavily adapted from the paint license, which is in // the file paint.license (BSD compatible) included in this @@ -593,79 +611,90 @@ args.verify_length(1); - std::pair<agg::int8u*,bool> bufpair = _get_output_buffer(); + FILE *fp = NULL; + Py::Object py_fileobj = Py::Object(args[0]); + if (py_fileobj.isString()) { + std::string fileName = Py::String(py_fileobj); + const char *file_name = fileName.c_str(); + if ((fp = fopen(file_name, "wb")) == NULL) + throw Py::RuntimeError( Printf("Could not open file %s", file_name).str() ); + } + else { + PyObject* write_method = PyObject_GetAttrString(py_fileobj.ptr(), "write"); + if (!(write_method && PyCallable_Check(write_method))) { + Py_XDECREF(write_method); + throw Py::TypeError("Object does not appear to be a path or a Python file-like object"); + } + Py_XDECREF(write_method); + } - std::string fileName = Py::String(args[0]); - const char *file_name = fileName.c_str(); - FILE *fp; png_structp png_ptr; png_infop info_ptr; - struct png_color_8_struct sig_bit; + struct png_color_8_struct sig_bit; png_uint_32 row=0; //todo: allocate on heap - png_bytep *row_pointers = new png_bytep[rowsOut]; + png_bytep *row_pointers = NULL; + std::pair<agg::int8u*,bool> bufpair; + bufpair.first = NULL; + bufpair.second = false; - for (row = 0; row < rowsOut; ++row) - row_pointers[row] = bufpair.first + row * colsOut * 4; + try { + row_pointers = new png_bytep[rowsOut]; + if (!row_pointers) + throw Py::RuntimeError("Out of memory"); - fp = fopen(file_name, "wb"); - if (fp == NULL) { - if (bufpair.second) delete [] bufpair.first; - delete [] row_pointers; - throw Py::RuntimeError(Printf("Could not open file %s", file_name).str()); - } + bufpair = _get_output_buffer(); + for (row = 0; row < rowsOut; ++row) + row_pointers[row] = bufpair.first + row * colsOut * 4; + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png_ptr == NULL) + throw Py::RuntimeError("Could not create write struct"); - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (png_ptr == NULL) { - if (bufpair.second) delete [] bufpair.first; - fclose(fp); - delete [] row_pointers; - throw Py::RuntimeError("Could not create write struct"); - } + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + throw Py::RuntimeError("Could not create info struct"); - info_ptr = png_create_info_struct(png_ptr); - if (info_ptr == NULL) { - if (bufpair.second) delete [] bufpair.first; - fclose(fp); + if (setjmp(png_ptr->jmpbuf)) + throw Py::RuntimeError("Error building image"); + + if (fp) { + png_init_io(png_ptr, fp); + } else { + png_set_write_fn(png_ptr, (void*)py_fileobj.ptr(), + &write_png_data, &flush_png_data); + } + png_set_IHDR(png_ptr, info_ptr, + colsOut, rowsOut, 8, + PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + // this a a color image! + sig_bit.gray = 0; + sig_bit.red = 8; + sig_bit.green = 8; + sig_bit.blue = 8; + /* if the image has an alpha channel then */ + sig_bit.alpha = 8; + png_set_sBIT(png_ptr, info_ptr, &sig_bit); + + png_write_info(png_ptr, info_ptr); + png_write_image(png_ptr, row_pointers); + png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); - delete [] row_pointers; - throw Py::RuntimeError("Could not create info struct"); - } - - if (setjmp(png_ptr->jmpbuf)) { + } catch (...) { if (bufpair.second) delete [] bufpair.first; - fclose(fp); + if (fp) fclose(fp); png_destroy_write_struct(&png_ptr, &info_ptr); delete [] row_pointers; - throw Py::RuntimeError("Error building image"); + throw; } - png_init_io(png_ptr, fp); - png_set_IHDR(png_ptr, info_ptr, - colsOut, rowsOut, 8, - PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - - // this a a color image! - sig_bit.gray = 0; - sig_bit.red = 8; - sig_bit.green = 8; - sig_bit.blue = 8; - /* if the image has an alpha channel then */ - sig_bit.alpha = 8; - png_set_sBIT(png_ptr, info_ptr, &sig_bit); - - png_write_info(png_ptr, info_ptr); - png_write_image(png_ptr, row_pointers); - png_write_end(png_ptr, info_ptr); - png_destroy_write_struct(&png_ptr, &info_ptr); - fclose(fp); - + if (fp) fclose(fp); delete [] row_pointers; + if (bufpair.second) delete [] bufpair.first; - if (bufpair.second) delete [] bufpair.first; return Py::Object(); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-12-13 18:21:34
|
Revision: 4727 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4727&view=rev Author: mdboom Date: 2007-12-13 10:21:25 -0800 (Thu, 13 Dec 2007) Log Message: ----------- Merged revisions 4715-4725 via svnmerge from http://matplotlib.svn.sf.net/svnroot/matplotlib/trunk/matplotlib ........ r4718 | mdboom | 2007-12-13 08:40:40 -0500 (Thu, 13 Dec 2007) | 2 lines Updated to numpy names. ........ r4720 | jdh2358 | 2007-12-13 11:06:59 -0500 (Thu, 13 Dec 2007) | 2 lines moved optional rec2* packages out of mlab and into toolkits ........ r4722 | jdh2358 | 2007-12-13 13:12:11 -0500 (Thu, 13 Dec 2007) | 2 lines added gtktools and exceltools to toolkits ........ Modified Paths: -------------- branches/transforms/API_CHANGES branches/transforms/CODING_GUIDE branches/transforms/examples/figimage_demo.py branches/transforms/examples/logo.py branches/transforms/examples/mri_demo.py branches/transforms/lib/matplotlib/image.py branches/transforms/lib/matplotlib/mlab.py branches/transforms/src/_backend_agg.cpp branches/transforms/src/_image.cpp Added Paths: ----------- branches/transforms/lib/matplotlib/toolkits/exceltools.py branches/transforms/lib/matplotlib/toolkits/gtktools.py Property Changed: ---------------- branches/transforms/ Property changes on: branches/transforms ___________________________________________________________________ Name: svnmerge-integrated - /trunk/matplotlib:1-4714 + /trunk/matplotlib:1-4725 Modified: branches/transforms/API_CHANGES =================================================================== --- branches/transforms/API_CHANGES 2007-12-13 18:17:33 UTC (rev 4726) +++ branches/transforms/API_CHANGES 2007-12-13 18:21:25 UTC (rev 4727) @@ -169,6 +169,10 @@ END OF TRANSFORMS REFACTORING + Moved rec2gtk to matplotlib.toolkits.gtktools + + Moved rec2excel to matplotlib.toolkits.exceltools + Removed, dead/experimental ExampleInfo, Namespace and Importer code from matplotlib/__init__.py 0.91.1 Released Modified: branches/transforms/CODING_GUIDE =================================================================== --- branches/transforms/CODING_GUIDE 2007-12-13 18:17:33 UTC (rev 4726) +++ branches/transforms/CODING_GUIDE 2007-12-13 18:21:25 UTC (rev 4727) @@ -113,6 +113,16 @@ .emacs will cause emacs to strip trailing white space on save for python, C and C++ + +When importing modules from the matplotlib namespace + + import matplotlib.cbook as cbook # DO + from matplotlib import cbook #DONT + +because the latter is ambiguous whether cbook is a module or a +function to the new developer. The former makes it explcit that you +are importing a module or package. + ; and similarly for c++-mode-hook and c-mode-hook (add-hook 'python-mode-hook (lambda () Modified: branches/transforms/examples/figimage_demo.py =================================================================== --- branches/transforms/examples/figimage_demo.py 2007-12-13 18:17:33 UTC (rev 4726) +++ branches/transforms/examples/figimage_demo.py 2007-12-13 18:21:25 UTC (rev 4727) @@ -13,7 +13,7 @@ im1 = figimage(Z, xo=50, yo=0) im2 = figimage(Z, xo=100, yo=100, alpha=.8) #gray() # overrides current and sets default -#savefig('figimage_demo') +savefig('figimage_demo') show() Modified: branches/transforms/examples/logo.py =================================================================== --- branches/transforms/examples/logo.py 2007-12-13 18:17:33 UTC (rev 4726) +++ branches/transforms/examples/logo.py 2007-12-13 18:21:25 UTC (rev 4727) @@ -5,7 +5,7 @@ # convert data to mV x = 1000*0.1*fromstring( - file('data/membrane.dat', 'rb').read(), Float32) + file('data/membrane.dat', 'rb').read(), float32) # 0.0005 is the sample interval t = 0.0005*arange(len(x)) figure(1, figsize=(7,1), dpi=100) Modified: branches/transforms/examples/mri_demo.py =================================================================== --- branches/transforms/examples/mri_demo.py 2007-12-13 18:17:33 UTC (rev 4726) +++ branches/transforms/examples/mri_demo.py 2007-12-13 18:21:25 UTC (rev 4727) @@ -3,7 +3,7 @@ # data are 256x256 16 bit integers dfile = 'data/s1045.ima' -im = fromstring(file(dfile, 'rb').read(), UInt16).astype(Float) +im = fromstring(file(dfile, 'rb').read(), uint16).astype(float) im.shape = 256, 256 #imshow(im, ColormapJet(256)) Modified: branches/transforms/lib/matplotlib/image.py =================================================================== --- branches/transforms/lib/matplotlib/image.py 2007-12-13 18:17:33 UTC (rev 4726) +++ branches/transforms/lib/matplotlib/image.py 2007-12-13 18:21:25 UTC (rev 4727) @@ -23,6 +23,7 @@ from matplotlib._image import * class AxesImage(martist.Artist, cm.ScalarMappable): + zorder = 1 def __init__(self, ax, cmap = None, @@ -508,18 +509,21 @@ self.update_dict['array'] = True class FigureImage(martist.Artist, cm.ScalarMappable): + zorder = 1 def __init__(self, fig, cmap = None, norm = None, offsetx = 0, offsety = 0, origin=None, + **kwargs ): """ cmap is a colors.Colormap instance norm is a colors.Normalize instance to map luminance to 0-1 + kwargs are an optional list of Artist keyword args """ martist.Artist.__init__(self) cm.ScalarMappable.__init__(self, norm, cmap) @@ -528,6 +532,7 @@ self.figure = fig self.ox = offsetx self.oy = offsety + self.update(kwargs) def contains(self, mouseevent): """Test whether the mouse event occured within the image. Modified: branches/transforms/lib/matplotlib/mlab.py =================================================================== --- branches/transforms/lib/matplotlib/mlab.py 2007-12-13 18:17:33 UTC (rev 4726) +++ branches/transforms/lib/matplotlib/mlab.py 2007-12-13 18:21:25 UTC (rev 4727) @@ -48,13 +48,13 @@ * rec2csv : store record array in CSV file * rec2excel : store record array in excel worksheet - required pyExcelerator - * rec2gtk : put record array in GTK treeview - requires gtk + * csv2rec : import record array from CSV file with type inspection * rec_append_field : add a field/array to record array * rec_drop_fields : drop fields from record array * rec_join : join two record arrays on sequence of fields -For the rec viewer clases (rec2csv, rec2excel and rec2gtk), there are +For the rec viewer clases (rec2csv, rec2excel), there are a bunch of Format objects you can pass into the functions that will do things like color negative values red, set percent formatting and scaling, etc. @@ -1978,7 +1978,7 @@ join record arrays r1 and r2 on key; key is a tuple of field names. if r1 and r2 have equal values on all the keys in the key tuple, then their fields will be merged into a new record array - containing the union of the fields of r1 and r2 + containing the intersection of the fields of r1 and r2 """ for name in key: @@ -2343,373 +2343,5 @@ writer.writerow([func(val) for func, val in zip(funcs, row)]) fh.close() -# if pyExcelerator is installed, provide an excel view -try: - import pyExcelerator as excel -except ImportError: - pass -else: - def xlformat_factory(format): - """ - copy the format, perform any overrides, and attach an xlstyle instance - copied format is returned - """ - format = copy.deepcopy(format) - - - xlstyle = excel.XFStyle() - if isinstance(format, FormatFloat): - zeros = ''.join(['0']*format.precision) - xlstyle.num_format_str = '#,##0.%s;[RED]-#,##0.%s'%(zeros, zeros) - elif isinstance(format, FormatInt): - xlstyle.num_format_str = '#,##;[RED]-#,##' - elif isinstance(format, FormatPercent): - zeros = ''.join(['0']*format.precision) - xlstyle.num_format_str = '0.%s%%;[RED]-0.%s%%'%(zeros, zeros) - format.scale = 1. - else: - xlstyle = None - - format.xlstyle = xlstyle - - return format - - def rec2excel(r, ws, formatd=None, rownum=0, colnum=0): - """ - save record array r to excel pyExcelerator worksheet ws - starting at rownum. if ws is string like, assume it is a - filename and save to it - - start writing at rownum, colnum - - formatd is a dictionary mapping dtype name -> FormatXL instances - - The next rownum after writing is returned - """ - - autosave = False - if cbook.is_string_like(ws): - filename = ws - wb = excel.Workbook() - ws = wb.add_sheet('worksheet') - autosave = True - - - if formatd is None: - formatd = dict() - - formats = [] - font = excel.Font() - font.bold = True - - stylehdr = excel.XFStyle() - stylehdr.font = font - - for i, name in enumerate(r.dtype.names): - dt = r.dtype[name] - format = formatd.get(name) - if format is None: - format = defaultformatd.get(dt.type, FormatObj()) - - format = xlformat_factory(format) - ws.write(rownum, colnum+i, name, stylehdr) - formats.append(format) - - rownum+=1 - - - ind = npy.arange(len(r.dtype.names)) - for row in r: - for i in ind: - val = row[i] - format = formats[i] - val = format.toval(val) - if format.xlstyle is None: - ws.write(rownum, colnum+i, val) - else: - if safe_isnan(val): - ws.write(rownum, colnum+i, 'NaN') - else: - ws.write(rownum, colnum+i, val, format.xlstyle) - rownum += 1 - - if autosave: - wb.save(filename) - return rownum - - - - -# if gtk is installed, provide a gtk view -try: - import gtk, gobject -except ImportError: - pass -except RuntimeError: - pass -else: - - - def gtkformat_factory(format, colnum): - """ - copy the format, perform any overrides, and attach an gtk style attrs - - - xalign = 0. - cell = None - - """ - - format = copy.copy(format) - format.xalign = 0. - format.cell = None - - def negative_red_cell(column, cell, model, thisiter): - val = model.get_value(thisiter, colnum) - try: val = float(val) - except: cell.set_property('foreground', 'black') - else: - if val<0: - cell.set_property('foreground', 'red') - else: - cell.set_property('foreground', 'black') - - - if isinstance(format, FormatFloat) or isinstance(format, FormatInt): - format.cell = negative_red_cell - format.xalign = 1. - elif isinstance(format, FormatDate): - format.xalign = 1. - return format - - - - class SortedStringsScrolledWindow(gtk.ScrolledWindow): - """ - A simple treeview/liststore assuming all columns are strings. - Supports ascending/descending sort by clicking on column header - """ - - def __init__(self, colheaders, formatterd=None): - """ - xalignd if not None, is a dict mapping col header to xalignent (default 1) - - formatterd if not None, is a dict mapping col header to a ColumnFormatter - """ - - - gtk.ScrolledWindow.__init__(self) - self.colheaders = colheaders - self.seq = None # not initialized with accts - self.set_shadow_type(gtk.SHADOW_ETCHED_IN) - self.set_policy(gtk.POLICY_AUTOMATIC, - gtk.POLICY_AUTOMATIC) - - types = [gobject.TYPE_STRING] * len(colheaders) - model = self.model = gtk.ListStore(*types) - - - treeview = gtk.TreeView(self.model) - treeview.show() - treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE) - treeview.set_rules_hint(True) - - - class Clicked: - def __init__(self, parent, i): - self.parent = parent - self.i = i - self.num = 0 - - def __call__(self, column): - ind = [] - dsu = [] - for rownum, thisiter in enumerate(self.parent.iters): - val = model.get_value(thisiter, self.i) - try: val = float(val.strip().rstrip('%')) - except ValueError: pass - if npy.isnan(val): val = npy.inf # force nan to sort uniquely - dsu.append((val, rownum)) - dsu.sort() - if not self.num%2: dsu.reverse() - - vals, otherind = zip(*dsu) - ind.extend(otherind) - - self.parent.model.reorder(ind) - newiters = [] - for i in ind: - newiters.append(self.parent.iters[i]) - self.parent.iters = newiters[:] - for i, thisiter in enumerate(self.parent.iters): - key = tuple([self.parent.model.get_value(thisiter, j) for j in range(len(colheaders))]) - self.parent.rownumd[i] = key - - self.num+=1 - - - if formatterd is None: - formatterd = dict() - - formatterd = formatterd.copy() - - for i, header in enumerate(colheaders): - renderer = gtk.CellRendererText() - if header not in formatterd: - formatterd[header] = ColumnFormatter() - formatter = formatterd[header] - - column = gtk.TreeViewColumn(header, renderer, text=i) - renderer.set_property('xalign', formatter.xalign) - column.connect('clicked', Clicked(self, i)) - column.set_property('clickable', True) - - if formatter.cell is not None: - column.set_cell_data_func(renderer, formatter.cell) - - treeview.append_column(column) - - - - self.formatterd = formatterd - self.lastcol = column - self.add(treeview) - self.treeview = treeview - self.clear() - - def clear(self): - self.iterd = dict() - self.iters = [] # an ordered list of iters - self.rownumd = dict() # a map from rownum -> symbol - self.model.clear() - self.datad = dict() - - - def flat(self, row): - seq = [] - for i,val in enumerate(row): - formatter = self.formatterd.get(self.colheaders[i]) - seq.extend([i,formatter.tostr(val)]) - return seq - - def __delete_selected(self, *unused): # untested - - - keyd = dict([(thisiter, key) for key, thisiter in self.iterd.values()]) - for row in self.get_selected(): - key = tuple(row) - thisiter = self.iterd[key] - self.model.remove(thisiter) - del self.datad[key] - del self.iterd[key] - self.iters.remove(thisiter) - - for i, thisiter in enumerate(self.iters): - self.rownumd[i] = keyd[thisiter] - - - - def delete_row(self, row): - key = tuple(row) - thisiter = self.iterd[key] - self.model.remove(thisiter) - - - del self.datad[key] - del self.iterd[key] - self.rownumd[len(self.iters)] = key - self.iters.remove(thisiter) - - for rownum, thiskey in self.rownumd.items(): - if thiskey==key: del self.rownumd[rownum] - - def add_row(self, row): - thisiter = self.model.append() - self.model.set(thisiter, *self.flat(row)) - key = tuple(row) - self.datad[key] = row - self.iterd[key] = thisiter - self.rownumd[len(self.iters)] = key - self.iters.append(thisiter) - - def update_row(self, rownum, newrow): - key = self.rownumd[rownum] - thisiter = self.iterd[key] - newkey = tuple(newrow) - - self.rownumd[rownum] = newkey - del self.datad[key] - del self.iterd[key] - self.datad[newkey] = newrow - self.iterd[newkey] = thisiter - - - self.model.set(thisiter, *self.flat(newrow)) - - def get_row(self, rownum): - key = self.rownumd[rownum] - return self.datad[key] - - def get_selected(self): - selected = [] - def foreach(model, path, iter, selected): - selected.append(model.get_value(iter, 0)) - - self.treeview.get_selection().selected_foreach(foreach, selected) - return selected - - - - def rec2gtk(r, formatd=None, rownum=0, autowin=True): - """ - save record array r to excel pyExcelerator worksheet ws - starting at rownum. if ws is string like, assume it is a - filename and save to it - - formatd is a dictionary mapping dtype name -> FormatXL instances - - This function creates a SortedStringsScrolledWindow (derived - from gtk.ScrolledWindow) and returns it. if autowin is True, - a gtk.Window is created, attached to the - SortedStringsScrolledWindow instance, shown and returned. If - autowin=False, the caller is responsible for adding the - SortedStringsScrolledWindow instance to a gtk widget and - showing it. - """ - - - - if formatd is None: - formatd = dict() - - formats = [] - for i, name in enumerate(r.dtype.names): - dt = r.dtype[name] - format = formatd.get(name) - if format is None: - format = defaultformatd.get(dt.type, FormatObj()) - #print 'gtk fmt factory', i, name, format, type(format) - format = gtkformat_factory(format, i) - formatd[name] = format - - - colheaders = r.dtype.names - scroll = SortedStringsScrolledWindow(colheaders, formatd) - - ind = npy.arange(len(r.dtype.names)) - for row in r: - scroll.add_row(row) - - - if autowin: - win = gtk.Window() - win.set_default_size(800,600) - win.add(scroll) - win.show_all() - scroll.win = win - - return scroll - - Copied: branches/transforms/lib/matplotlib/toolkits/exceltools.py (from rev 4722, trunk/matplotlib/lib/matplotlib/toolkits/exceltools.py) =================================================================== --- branches/transforms/lib/matplotlib/toolkits/exceltools.py (rev 0) +++ branches/transforms/lib/matplotlib/toolkits/exceltools.py 2007-12-13 18:21:25 UTC (rev 4727) @@ -0,0 +1,120 @@ +""" +Some io tools for excel -- requires pypyExcelerator + +Example usage: + + import matplotlib.mlab as mlab + import matplotlib.toolkits.exceltools as exceltools + + r = mlab.csv2rec('somefile.csv', checkrows=0) + + formatd = dict( + weight = mlab.FormatFloat(2), + change = mlab.FormatPercent(2), + cost = mlab.FormatThousands(2), + ) + + + exceltools.rec2excel(r, 'test.xls', formatd=formatd) + mlab.rec2csv(r, 'test.csv', formatd=formatd) + +""" +import copy +import numpy as npy +import pyExcelerator as excel +import matplotlib.cbook as cbook +import matplotlib.mlab as mlab + + +def xlformat_factory(format): + """ + copy the format, perform any overrides, and attach an xlstyle instance + copied format is returned + """ + format = copy.deepcopy(format) + + + + xlstyle = excel.XFStyle() + if isinstance(format, mlab.FormatPercent): + zeros = ''.join(['0']*format.precision) + xlstyle.num_format_str = '0.%s%%;[RED]-0.%s%%'%(zeros, zeros) + format.scale = 1. + elif isinstance(format, mlab.FormatFloat): + zeros = ''.join(['0']*format.precision) + xlstyle.num_format_str = '#,##0.%s;[RED]-#,##0.%s'%(zeros, zeros) + elif isinstance(format, mlab.FormatInt): + xlstyle.num_format_str = '#,##;[RED]-#,##' + else: + xlstyle = None + + format.xlstyle = xlstyle + + return format + +def rec2excel(r, ws, formatd=None, rownum=0, colnum=0): + """ + save record array r to excel pyExcelerator worksheet ws + starting at rownum. if ws is string like, assume it is a + filename and save to it + + start writing at rownum, colnum + + formatd is a dictionary mapping dtype name -> mlab.Format instances + + The next rownum after writing is returned + """ + + autosave = False + if cbook.is_string_like(ws): + filename = ws + wb = excel.Workbook() + ws = wb.add_sheet('worksheet') + autosave = True + + + if formatd is None: + formatd = dict() + + formats = [] + font = excel.Font() + font.bold = True + + stylehdr = excel.XFStyle() + stylehdr.font = font + + for i, name in enumerate(r.dtype.names): + dt = r.dtype[name] + format = formatd.get(name) + if format is None: + format = mlab.defaultformatd.get(dt.type, mlab.FormatObj()) + + format = xlformat_factory(format) + ws.write(rownum, colnum+i, name, stylehdr) + formats.append(format) + + rownum+=1 + + + ind = npy.arange(len(r.dtype.names)) + for row in r: + for i in ind: + val = row[i] + format = formats[i] + val = format.toval(val) + if format.xlstyle is None: + ws.write(rownum, colnum+i, val) + else: + if mlab.safe_isnan(val): + ws.write(rownum, colnum+i, 'NaN') + else: + ws.write(rownum, colnum+i, val, format.xlstyle) + rownum += 1 + + if autosave: + wb.save(filename) + return rownum + + + + Copied: branches/transforms/lib/matplotlib/toolkits/gtktools.py (from rev 4722, trunk/matplotlib/lib/matplotlib/toolkits/gtktools.py) =================================================================== --- branches/transforms/lib/matplotlib/toolkits/gtktools.py (rev 0) +++ branches/transforms/lib/matplotlib/toolkits/gtktools.py 2007-12-13 18:21:25 UTC (rev 4727) @@ -0,0 +1,299 @@ +""" + +Some gtk specific tools and widgets + + * rec2gtk : put record array in GTK treeview - requires gtk + +Example usage + + import matplotlib.mlab as mlab + import matplotlib.toolkits.gtktools as gtktools + + r = mlab.csv2rec('somefile.csv', checkrows=0) + + formatd = dict( + weight = mlab.FormatFloat(2), + change = mlab.FormatPercent(2), + cost = mlab.FormatThousands(2), + ) + + + exceltools.rec2excel(r, 'test.xls', formatd=formatd) + mlab.rec2csv(r, 'test.csv', formatd=formatd) + + + import gtk + scroll = gtktools.rec2gtk(r, formatd=formatd) + win = gtk.Window() + win.set_size_request(600,800) + win.add(scroll) + win.show_all() + gtk.main() + +""" +import copy +import gtk, gobject +import numpy as npy +import matplotlib.cbook as cbook +import matplotlib.mlab as mlab + +def gtkformat_factory(format, colnum): + """ + copy the format, perform any overrides, and attach an gtk style attrs + + + xalign = 0. + cell = None + + """ + + format = copy.copy(format) + format.xalign = 0. + format.cell = None + + def negative_red_cell(column, cell, model, thisiter): + val = model.get_value(thisiter, colnum) + try: val = float(val) + except: cell.set_property('foreground', 'black') + else: + if val<0: + cell.set_property('foreground', 'red') + else: + cell.set_property('foreground', 'black') + + + if isinstance(format, mlab.FormatFloat) or isinstance(format, mlab.FormatInt): + format.cell = negative_red_cell + format.xalign = 1. + elif isinstance(format, mlab.FormatDate): + format.xalign = 1. + return format + + + +class SortedStringsScrolledWindow(gtk.ScrolledWindow): + """ + A simple treeview/liststore assuming all columns are strings. + Supports ascending/descending sort by clicking on column header + """ + + def __init__(self, colheaders, formatterd=None): + """ + xalignd if not None, is a dict mapping col header to xalignent (default 1) + + formatterd if not None, is a dict mapping col header to a ColumnFormatter + """ + + + gtk.ScrolledWindow.__init__(self) + self.colheaders = colheaders + self.seq = None # not initialized with accts + self.set_shadow_type(gtk.SHADOW_ETCHED_IN) + self.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + + types = [gobject.TYPE_STRING] * len(colheaders) + model = self.model = gtk.ListStore(*types) + + + treeview = gtk.TreeView(self.model) + treeview.show() + treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE) + treeview.set_rules_hint(True) + + + class Clicked: + def __init__(self, parent, i): + self.parent = parent + self.i = i + self.num = 0 + + def __call__(self, column): + ind = [] + dsu = [] + for rownum, thisiter in enumerate(self.parent.iters): + val = model.get_value(thisiter, self.i) + try: val = float(val.strip().rstrip('%')) + except ValueError: pass + if npy.isnan(val): val = npy.inf # force nan to sort uniquely + dsu.append((val, rownum)) + dsu.sort() + if not self.num%2: dsu.reverse() + + vals, otherind = zip(*dsu) + ind.extend(otherind) + + self.parent.model.reorder(ind) + newiters = [] + for i in ind: + newiters.append(self.parent.iters[i]) + self.parent.iters = newiters[:] + for i, thisiter in enumerate(self.parent.iters): + key = tuple([self.parent.model.get_value(thisiter, j) for j in range(len(colheaders))]) + self.parent.rownumd[i] = key + + self.num+=1 + + + if formatterd is None: + formatterd = dict() + + formatterd = formatterd.copy() + + for i, header in enumerate(colheaders): + renderer = gtk.CellRendererText() + if header not in formatterd: + formatterd[header] = ColumnFormatter() + formatter = formatterd[header] + + column = gtk.TreeViewColumn(header, renderer, text=i) + renderer.set_property('xalign', formatter.xalign) + column.connect('clicked', Clicked(self, i)) + column.set_property('clickable', True) + + if formatter.cell is not None: + column.set_cell_data_func(renderer, formatter.cell) + + treeview.append_column(column) + + + + self.formatterd = formatterd + self.lastcol = column + self.add(treeview) + self.treeview = treeview + self.clear() + + def clear(self): + self.iterd = dict() + self.iters = [] # an ordered list of iters + self.rownumd = dict() # a map from rownum -> symbol + self.model.clear() + self.datad = dict() + + + def flat(self, row): + seq = [] + for i,val in enumerate(row): + formatter = self.formatterd.get(self.colheaders[i]) + seq.extend([i,formatter.tostr(val)]) + return seq + + def __delete_selected(self, *unused): # untested + + + keyd = dict([(thisiter, key) for key, thisiter in self.iterd.values()]) + for row in self.get_selected(): + key = tuple(row) + thisiter = self.iterd[key] + self.model.remove(thisiter) + del self.datad[key] + del self.iterd[key] + self.iters.remove(thisiter) + + for i, thisiter in enumerate(self.iters): + self.rownumd[i] = keyd[thisiter] + + + + def delete_row(self, row): + key = tuple(row) + thisiter = self.iterd[key] + self.model.remove(thisiter) + + + del self.datad[key] + del self.iterd[key] + self.rownumd[len(self.iters)] = key + self.iters.remove(thisiter) + + for rownum, thiskey in self.rownumd.items(): + if thiskey==key: del self.rownumd[rownum] + + def add_row(self, row): + thisiter = self.model.append() + self.model.set(thisiter, *self.flat(row)) + key = tuple(row) + self.datad[key] = row + self.iterd[key] = thisiter + self.rownumd[len(self.iters)] = key + self.iters.append(thisiter) + + def update_row(self, rownum, newrow): + key = self.rownumd[rownum] + thisiter = self.iterd[key] + newkey = tuple(newrow) + + self.rownumd[rownum] = newkey + del self.datad[key] + del self.iterd[key] + self.datad[newkey] = newrow + self.iterd[newkey] = thisiter + + + self.model.set(thisiter, *self.flat(newrow)) + + def get_row(self, rownum): + key = self.rownumd[rownum] + return self.datad[key] + + def get_selected(self): + selected = [] + def foreach(model, path, iter, selected): + selected.append(model.get_value(iter, 0)) + + self.treeview.get_selection().selected_foreach(foreach, selected) + return selected + + + +def rec2gtk(r, formatd=None, rownum=0, autowin=True): + """ + save record array r to excel pyExcelerator worksheet ws + starting at rownum. if ws is string like, assume it is a + filename and save to it + + formatd is a dictionary mapping dtype name -> mlab.Format instances + + This function creates a SortedStringsScrolledWindow (derived + from gtk.ScrolledWindow) and returns it. if autowin is True, + a gtk.Window is created, attached to the + SortedStringsScrolledWindow instance, shown and returned. If + autowin=False, the caller is responsible for adding the + SortedStringsScrolledWindow instance to a gtk widget and + showing it. + """ + + + + if formatd is None: + formatd = dict() + + formats = [] + for i, name in enumerate(r.dtype.names): + dt = r.dtype[name] + format = formatd.get(name) + if format is None: + format = mlab.defaultformatd.get(dt.type, mlab.FormatObj()) + #print 'gtk fmt factory', i, name, format, type(format) + format = gtkformat_factory(format, i) + formatd[name] = format + + + colheaders = r.dtype.names + scroll = SortedStringsScrolledWindow(colheaders, formatd) + + ind = npy.arange(len(r.dtype.names)) + for row in r: + scroll.add_row(row) + + + if autowin: + win = gtk.Window() + win.set_default_size(800,600) + win.add(scroll) + win.show_all() + scroll.win = win + + return scroll + Modified: branches/transforms/src/_backend_agg.cpp =================================================================== --- branches/transforms/src/_backend_agg.cpp 2007-12-13 18:17:33 UTC (rev 4726) +++ branches/transforms/src/_backend_agg.cpp 2007-12-13 18:21:25 UTC (rev 4727) @@ -253,7 +253,7 @@ alphaMaskRenderingBuffer = new agg::rendering_buffer; alphaMaskRenderingBuffer->attach(alphaBuffer, width, height, stride); alphaMask = new alpha_mask_type(*alphaMaskRenderingBuffer); - //jdh + pixfmtAlphaMask = new agg::pixfmt_gray8(*alphaMaskRenderingBuffer); rendererBaseAlphaMask = new renderer_base_alpha_mask_type(*pixfmtAlphaMask); rendererAlphaMask = new renderer_alpha_mask_type(*rendererBaseAlphaMask); Modified: branches/transforms/src/_image.cpp =================================================================== --- branches/transforms/src/_image.cpp 2007-12-13 18:17:33 UTC (rev 4726) +++ branches/transforms/src/_image.cpp 2007-12-13 18:21:25 UTC (rev 4727) @@ -299,7 +299,7 @@ Py::Object Image::get_matrix(const Py::Tuple& args) { - _VERBOSE("Image::get_size"); + _VERBOSE("Image::get_matrix"); args.verify_length(0); @@ -520,7 +520,7 @@ Py::Object Image::get_size_out(const Py::Tuple& args) { - _VERBOSE("Image::get_size"); + _VERBOSE("Image::get_size_out"); args.verify_length(0); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-12-14 20:08:47
|
Revision: 4733 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4733&view=rev Author: mdboom Date: 2007-12-14 12:07:59 -0800 (Fri, 14 Dec 2007) Log Message: ----------- First pass at symmetrical log plots. Expose xscale() and yscale() through pyplot. Modified Paths: -------------- branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/pyplot.py branches/transforms/lib/matplotlib/scale.py branches/transforms/lib/matplotlib/ticker.py Added Paths: ----------- branches/transforms/examples/symlog_demo.py Added: branches/transforms/examples/symlog_demo.py =================================================================== --- branches/transforms/examples/symlog_demo.py (rev 0) +++ branches/transforms/examples/symlog_demo.py 2007-12-14 20:07:59 UTC (rev 4733) @@ -0,0 +1,29 @@ +#!/usr/bin/env python +from pylab import * + +dt = 1.0 +x = arange(-50.0, 50.0, dt) +y = arange(0, 100.0, dt) + +subplot(311) +plot(x, y) +xscale('symlog') +ylabel('symlogx') +grid(True) +gca().xaxis.grid(True, which='minor') # minor grid on too + +subplot(312) +plot(y, x) +yscale('symlog') +ylabel('symlogy') + + +subplot(313) +plot(x, npy.sin(x / 3.0)) +xscale('symlog') +yscale('symlog') +grid(True) +ylabel('symlog both') + +savefig('log_demo') +show() Property changes on: branches/transforms/examples/symlog_demo.py ___________________________________________________________________ Name: svn:executable + * Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-12-14 16:28:34 UTC (rev 4732) +++ branches/transforms/lib/matplotlib/axes.py 2007-12-14 20:07:59 UTC (rev 4733) @@ -741,8 +741,6 @@ self.xaxis.cla() self.yaxis.cla() - self.set_xscale('linear') - self.set_yscale('linear') self.ignore_existing_data_limits = True self.callbacks = cbook.CallbackRegistry(('xlim_changed', 'ylim_changed')) @@ -768,6 +766,8 @@ self.collections = [] # collection.Collection instances self._autoscaleon = True + self.set_xscale('linear') + self.set_yscale('linear') self.grid(self._gridOn) props = font_manager.FontProperties(size=rcParams['axes.titlesize']) @@ -1654,6 +1654,7 @@ ", ".join(mscale.get_scale_names())) return self.xaxis.get_scale() + # MGDTODO: Update docstring def set_xscale(self, value, **kwargs): """ SET_XSCALE(value) @@ -1673,6 +1674,7 @@ ACCEPTS: [%(scale)s] """ % {'scale': ' | '.join([repr(x) for x in mscale.get_scale_names()])} self.xaxis.set_scale(value, **kwargs) + self.autoscale_view() self._update_transScale() def get_xticks(self, minor=False): @@ -1833,6 +1835,7 @@ ACCEPTS: %(scale)s """ % {'scale': ' | '.join([repr(x) for x in mscale.get_scale_names()])} self.yaxis.set_scale(value, **kwargs) + self.autoscale_view() self._update_transScale() def get_yticks(self, minor=False): @@ -1992,6 +1995,7 @@ lim = self.viewLim.frozen(), trans = self.transData.frozen(), trans_inverse = self.transData.inverted().frozen(), + bbox = self.bbox.frozen(), x = x, y = y ) @@ -2044,9 +2048,11 @@ p = self._pan_start dx = x - p.x dy = y - p.y + if dx == 0 and dy == 0: + return if button == 1: dx, dy = format_deltas(key, dx, dy) - result = self.bbox.frozen().translated(-dx, -dy) \ + result = p.bbox.translated(-dx, -dy) \ .transformed(p.trans_inverse) elif button == 3: try: Modified: branches/transforms/lib/matplotlib/pyplot.py =================================================================== --- branches/transforms/lib/matplotlib/pyplot.py 2007-12-14 16:28:34 UTC (rev 4732) +++ branches/transforms/lib/matplotlib/pyplot.py 2007-12-14 20:07:59 UTC (rev 4733) @@ -12,6 +12,7 @@ from matplotlib.axes import Axes from matplotlib.projections import PolarAxes from matplotlib import mlab # for csv2rec in plotfile +from matplotlib.scale import get_scale_names from matplotlib import cm from matplotlib.cm import get_cmap @@ -726,8 +727,32 @@ return ret +# MGDTODO: Update docstring +def xscale(*args, **kwargs): + """ + SET_XSCALE(value) + Set the xscaling: %(scale)s + """ % {'scale': ' | '.join([repr(x) for x in get_scale_names()])} + ax = gca() + ret = ax.set_xscale(*args, **kwargs) + draw_if_interactive() + return ret + +# MGDTODO: Update docstring +def yscale(*args, **kwargs): + """ + SET_YSCALE(value) + + Set the yscaling: %(scale)s + """ % {'scale': ' | '.join([repr(x) for x in get_scale_names()])} + ax = gca() + ret = ax.set_yscale(*args, **kwargs) + draw_if_interactive() + return ret + + def xticks(*args, **kwargs): """ Set/Get the xlimits of the current ticklocs, labels Modified: branches/transforms/lib/matplotlib/scale.py =================================================================== --- branches/transforms/lib/matplotlib/scale.py 2007-12-14 16:28:34 UTC (rev 4732) +++ branches/transforms/lib/matplotlib/scale.py 2007-12-14 20:07:59 UTC (rev 4733) @@ -3,7 +3,7 @@ MaskedArray = ma.MaskedArray from ticker import NullFormatter, ScalarFormatter, LogFormatterMathtext -from ticker import NullLocator, LogLocator, AutoLocator +from ticker import NullLocator, LogLocator, AutoLocator, SymmetricalLogLocator from transforms import Transform, IdentityTransform class ScaleBase(object): @@ -12,10 +12,10 @@ def limit_range_for_scale(self, vmin, vmax, minpos): return vmin, vmax - + class LinearScale(ScaleBase): name = 'linear' - + def __init__(self, axis, **kwargs): pass @@ -24,7 +24,7 @@ axis.set_major_formatter(ScalarFormatter()) axis.set_minor_locator(NullLocator()) axis.set_minor_formatter(NullFormatter()) - + def get_transform(self): return IdentityTransform() @@ -33,7 +33,7 @@ if mask.any(): return ma.MaskedArray(a, mask=mask) return a - + class LogScale(ScaleBase): name = 'log' @@ -41,13 +41,14 @@ input_dims = 1 output_dims = 1 is_separable = True - + base = 10.0 + def transform(self, a): a = _mask_non_positives(a * 10.0) if isinstance(a, MaskedArray): return ma.log10(a) return npy.log10(a) - + def inverted(self): return LogScale.InvertedLog10Transform() @@ -55,7 +56,8 @@ input_dims = 1 output_dims = 1 is_separable = True - + base = 10.0 + def transform(self, a): return ma.power(10.0, a) / 10.0 @@ -66,13 +68,14 @@ input_dims = 1 output_dims = 1 is_separable = True - + base = 2.0 + def transform(self, a): a = _mask_non_positives(a * 2.0) if isinstance(a, MaskedArray): return ma.log2(a) return npy.log2(a) - + def inverted(self): return LogScale.InvertedLog2Transform() @@ -80,7 +83,8 @@ input_dims = 1 output_dims = 1 is_separable = True - + base = 2.0 + def transform(self, a): return ma.power(2.0, a) / 2.0 @@ -91,13 +95,14 @@ input_dims = 1 output_dims = 1 is_separable = True - + base = npy.e + def transform(self, a): a = _mask_non_positives(a * npy.e) if isinstance(a, MaskedArray): return ma.log(a) return npy.log(a) - + def inverted(self): return LogScale.InvertedNaturalLogTransform() @@ -105,54 +110,55 @@ input_dims = 1 output_dims = 1 is_separable = True - + base = npy.e + def transform(self, a): return ma.power(npy.e, a) / npy.e def inverted(self): return LogScale.Log2Transform() - + class LogTransform(Transform): input_dims = 1 output_dims = 1 is_separable = True - + def __init__(self, base): Transform.__init__(self) - self._base = base - + self.base = base + def transform(self, a): - a = _mask_non_positives(a * self._base) + a = _mask_non_positives(a * self.base) if isinstance(a, MaskedArray): - return ma.log10(a) / npy.log(self._base) - return npy.log(a) / npy.log(self._base) - + return ma.log(a) / npy.log(self.base) + return npy.log(a) / npy.log(self.base) + def inverted(self): - return LogScale.InvertedLogTransform(self._base) + return LogScale.InvertedLogTransform(self.base) class InvertedLogTransform(Transform): input_dims = 1 output_dims = 1 is_separable = True - + def __init__(self, base): Transform.__init__(self) - self._base = base + self.base = base def transform(self, a): - return ma.power(self._base, a) / self._base + return ma.power(self.base, a) / self.base def inverted(self): - return LogScale.LogTransform(self._base) + return LogScale.LogTransform(self.base) - + def __init__(self, axis, **kwargs): if axis.axis_name == 'x': base = kwargs.pop('basex', 10.0) - subs = kwargs.pop('subsx', []) + subs = kwargs.pop('subsx', None) else: base = kwargs.pop('basey', 10.0) - subs = kwargs.pop('subsy', []) + subs = kwargs.pop('subsy', None) if base == 10.0: self._transform = self.Log10Transform() @@ -163,25 +169,104 @@ else: self._transform = self.LogTransform(base) - self._base = base - self._subs = subs + self.base = base + self.subs = subs def set_default_locators_and_formatters(self, axis): - axis.set_major_locator(LogLocator(self._base)) - axis.set_major_formatter(LogFormatterMathtext(self._base)) - axis.set_minor_locator(LogLocator(self._base, self._subs)) + axis.set_major_locator(LogLocator(self.base)) + axis.set_major_formatter(LogFormatterMathtext(self.base)) + axis.set_minor_locator(LogLocator(self.base, self.subs)) axis.set_minor_formatter(NullFormatter()) - + def get_transform(self): return self._transform def limit_range_for_scale(self, vmin, vmax, minpos): return (vmin <= 0.0 and minpos or vmin, vmax <= 0.0 and minpos or vmax) - + +class SymmetricalLogScale(ScaleBase): + name = 'symlog' + + class SymmetricalLogTransform(Transform): + input_dims = 1 + output_dims = 1 + is_separable = True + + def __init__(self, base, linthresh): + Transform.__init__(self) + self.base = base + self.linthresh = linthresh + self._log_base = npy.log(base) + self._linadjust = (npy.log(linthresh) / self._log_base) / linthresh + + def transform(self, a): + sign = npy.sign(npy.asarray(a)) + masked = ma.masked_inside(a, -self.linthresh, self.linthresh, copy=False) + log = sign * ma.log(npy.abs(masked)) / self._log_base + if masked.mask.any(): + return npy.asarray(ma.where(masked.mask, + a * self._linadjust, + log)) + else: + return npy.asarray(log) + + def inverted(self): + return SymmetricalLogScale.InvertedSymmetricalLogTransform(self.base, self.linthresh) + + class InvertedSymmetricalLogTransform(Transform): + input_dims = 1 + output_dims = 1 + is_separable = True + + def __init__(self, base, linthresh): + Transform.__init__(self) + self.base = base + self.linthresh = linthresh + self._log_base = npy.log(base) + self._log_linthresh = npy.log(linthresh) / self._log_base + self._linadjust = linthresh / (npy.log(linthresh) / self._log_base) + + def transform(self, a): + return npy.where(a <= self._log_linthresh, + npy.where(a >= -self._log_linthresh, + a * self._linadjust, + -(npy.power(self.base, -a))), + npy.power(self.base, a)) + + def inverted(self): + return SymmetricalLogScale.SymmetricalLogTransform(self.base) + + def __init__(self, axis, **kwargs): + if axis.axis_name == 'x': + base = kwargs.pop('basex', 10.0) + linthresh = kwargs.pop('linthreshx', 2.0) + subs = kwargs.pop('subsx', None) + else: + base = kwargs.pop('basey', 10.0) + linthresh = kwargs.pop('linthreshy', 2.0) + subs = kwargs.pop('subsy', None) + + self._transform = self.SymmetricalLogTransform(base, linthresh) + + self.base = base + self.linthresh = linthresh + self.subs = subs + + def set_default_locators_and_formatters(self, axis): + axis.set_major_locator(SymmetricalLogLocator(self.get_transform())) + axis.set_major_formatter(LogFormatterMathtext(self.base)) + axis.set_minor_locator(SymmetricalLogLocator(self.get_transform(), self.subs)) + axis.set_minor_formatter(NullFormatter()) + + def get_transform(self): + return self._transform + + _scale_mapping = { - 'linear' : LinearScale, - 'log' : LogScale + 'linear' : LinearScale, + 'log' : LogScale, + 'symlog' : SymmetricalLogScale } def scale_factory(scale, axis, **kwargs): scale = scale.lower() @@ -190,7 +275,7 @@ if not _scale_mapping.has_key(scale): raise ValueError("Unknown scale type '%s'" % scale) - + return _scale_mapping[scale](axis, **kwargs) def get_scale_names(): Modified: branches/transforms/lib/matplotlib/ticker.py =================================================================== --- branches/transforms/lib/matplotlib/ticker.py 2007-12-14 16:28:34 UTC (rev 4732) +++ branches/transforms/lib/matplotlib/ticker.py 2007-12-14 20:07:59 UTC (rev 4733) @@ -131,14 +131,14 @@ def set_data_interval(self, vmin, vmax): self.dataLim.intervalx = vmin, vmax - + def set_axis(self, axis): self.axis = axis def create_dummy_axis(self): if self.axis is None: self.axis = self.DummyAxis() - + def set_view_interval(self, vmin, vmax): self.axis.set_view_interval(vmin, vmax) @@ -149,7 +149,7 @@ self.set_view_interval(vmin, vmax) self.set_data_interval(vmin, vmax) - + class Formatter(TickHelper): """ Convert the tick location to a string @@ -459,13 +459,18 @@ vmin, vmax = self.axis.get_view_interval() d = abs(vmax - vmin) b=self._base + if x == 0.0: + return '0' + sign = npy.sign(x) # only label the decades - fx = math.log(x)/math.log(b) + fx = math.log(abs(x))/math.log(b) isDecade = self.is_decade(fx) if not isDecade and self.labelOnlyBase: s = '' elif x>10000: s= '%1.0e'%x elif x<1: s = '%1.0e'%x else : s = self.pprint_val(x,d) + if sign == -1: + return '-%s' % s return s def format_data(self,value): @@ -516,8 +521,11 @@ self.verify_intervals() d = abs(self.viewInterval.span()) b=self._base + if x == 0: + return '0' + sign = npy.sign(x) # only label the decades - fx = math.log(x)/math.log(b) + fx = math.log(abs(x))/math.log(b) isDecade = self.is_decade(fx) if not isDecade and self.labelOnlyBase: s = '' #if 0: pass @@ -526,6 +534,8 @@ #elif x<1: s = '10^%d'%fx elif fx<1: s = '%1.0e'%fx else : s = self.pprint_val(fx,d) + if sign == -1: + return '-%s' % s return s @@ -538,22 +548,30 @@ 'Return the format for tick val x at position pos' b = self._base # only label the decades - fx = math.log(x)/math.log(b) + if x == 0: + return '$0$' + sign = npy.sign(x) + fx = math.log(abs(x))/math.log(b) isDecade = self.is_decade(fx) usetex = rcParams['text.usetex'] + if sign == -1: + sign_string = '-' + else: + sign_string = '' + if not isDecade and self.labelOnlyBase: s = '' elif not isDecade: if usetex: - s = r'$%d^{%.2f}$'% (b, fx) + s = r'$%s%d^{%.2f}$'% (sign_string, b, fx) else: - s = '$\mathdefault{%d^{%.2f}}$'% (b, fx) + s = '$\mathdefault{%s%d^{%.2f}}$'% (sign_string, b, fx) else: if usetex: - s = r'$%d^{%d}$'% (b, self.nearest_long(fx)) + s = r'$%s%d^{%d}$'% (sign_string, b, self.nearest_long(fx)) else: - s = r'$\mathdefault{%d^{%d}}$'% (b, self.nearest_long(fx)) + s = r'$\mathdefault{%s%d^{%d}}$'% (sign_string, b, self.nearest_long(fx)) return s @@ -928,13 +946,12 @@ if vmin <= 0.0: raise ValueError( "Data has no positive values, and therefore can not be log-scaled.") - + vmin = math.log(vmin)/math.log(b) vmax = math.log(vmax)/math.log(b) if vmax<vmin: vmin, vmax = vmax, vmin - ticklocs = [] numdec = math.floor(vmax)-math.ceil(vmin) @@ -949,9 +966,14 @@ while numdec/stride+1 > self.numticks: stride += 1 - for decadeStart in b**npy.arange(math.floor(vmin), - math.ceil(vmax)+stride, stride): - ticklocs.extend( subs*decadeStart ) + decades = npy.arange(math.floor(vmin), + math.ceil(vmax)+stride, stride) + if len(subs) > 1 or subs[0] != 1.0: + ticklocs = [] + for decadeStart in b**decades: + ticklocs.extend( subs*decadeStart ) + else: + ticklocs = b**decades return npy.array(ticklocs) @@ -979,6 +1001,80 @@ result = mtransforms.nonsingular(vmin, vmax) return result +class SymmetricalLogLocator(Locator): + """ + Determine the tick locations for log axes + """ + + def __init__(self, transform, subs=[1.0]): + """ + place ticks on the location= base**i*subs[j] + """ + self._transform = transform + self._subs = subs + self.numticks = 15 + + def _set_numticks(self): + self.numticks = 15 # todo; be smart here; this is just for dev + + def __call__(self): + 'Return the locations of the ticks' + b = self._transform.base + + vmin, vmax = self.axis.get_view_interval() + vmin, vmax = self._transform.transform_point((vmin, vmax)) + if vmax<vmin: + vmin, vmax = vmax, vmin + numdec = math.floor(vmax)-math.ceil(vmin) + + if self._subs is None: + if numdec>10: subs = npy.array([1.0]) + elif numdec>6: subs = npy.arange(2.0, b, 2.0) + else: subs = npy.arange(2.0, b) + else: + subs = npy.asarray(self._subs) + + stride = 1 + while numdec/stride+1 > self.numticks: + stride += 1 + + decades = npy.arange(math.floor(vmin), math.ceil(vmax)+stride, stride) + if len(subs) > 1 or subs[0] != 1.0: + ticklocs = [] + for decade in decades: + ticklocs.extend(subs * (npy.sign(decade) * b ** npy.abs(decade))) + else: + ticklocs = npy.sign(decades) * b ** npy.abs(decades) + return npy.array(ticklocs) + + def autoscale(self): + 'Try to choose the view limits intelligently' + b = self._transform.base + vmin, vmax = self.axis.get_data_interval() + if vmax<vmin: + vmin, vmax = vmax, vmin + + if not is_decade(abs(vmin), b): + if vmin < 0: + vmin = -decade_up(-vmin, b) + else: + vmin = decade_down(vmin, b) + if not is_decade(abs(vmax), b): + if vmax < 0: + vmax = -decade_down(-vmax, b) + else: + vmax = decade_up(vmax, b) + + if vmin == vmax: + if vmin < 0: + vmin = -decade_up(-vmin, b) + vmax = -decade_down(-vmax, b) + else: + vmin = decade_down(vmin, b) + vmax = decade_up(vmax, b) + result = mtransforms.nonsingular(vmin, vmax) + return result + class AutoLocator(MaxNLocator): def __init__(self): MaxNLocator.__init__(self, nbins=9, steps=[1, 2, 5, 10]) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-12-14 20:12:20
|
Revision: 4735 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4735&view=rev Author: mdboom Date: 2007-12-14 12:12:18 -0800 (Fri, 14 Dec 2007) Log Message: ----------- Merged revisions 4726-4734 via svnmerge from http://matplotlib.svn.sf.net/svnroot/matplotlib/trunk/matplotlib ........ r4726 | jdh2358 | 2007-12-13 13:17:33 -0500 (Thu, 13 Dec 2007) | 2 lines removed namespace declaration from toolkits __init__ ........ r4728 | mdboom | 2007-12-13 13:30:45 -0500 (Thu, 13 Dec 2007) | 2 lines Update API_CHANGES and CHANGELOG for stuff since 0.91.1 ........ r4730 | jdh2358 | 2007-12-13 17:40:33 -0500 (Thu, 13 Dec 2007) | 2 lines noted rec2gtk and recexcel moves in CHANGELOG ........ Modified Paths: -------------- branches/transforms/API_CHANGES branches/transforms/CHANGELOG branches/transforms/lib/matplotlib/toolkits/__init__.py Property Changed: ---------------- branches/transforms/ Property changes on: branches/transforms ___________________________________________________________________ Name: svnmerge-integrated - /trunk/matplotlib:1-4725 + /trunk/matplotlib:1-4734 Modified: branches/transforms/API_CHANGES =================================================================== --- branches/transforms/API_CHANGES 2007-12-14 20:08:22 UTC (rev 4734) +++ branches/transforms/API_CHANGES 2007-12-14 20:12:18 UTC (rev 4735) @@ -169,6 +169,9 @@ END OF TRANSFORMS REFACTORING + A warning is issued when an image is drawn on log-scaled + axes, since it will not log-scale the image data. + Moved rec2gtk to matplotlib.toolkits.gtktools Moved rec2excel to matplotlib.toolkits.exceltools @@ -182,8 +185,6 @@ Changed cbook.is_file_like to cbook.is_writable_file_like and corrected behavior. - Moved mlab.csv2rec -> recutils.csv2rec - Added ax kwarg to pyplot.colorbar and Figure.colorbar so that one can specify the axes object from which space for the colorbar is to be taken, if one does not want to make the colorbar axes Modified: branches/transforms/CHANGELOG =================================================================== --- branches/transforms/CHANGELOG 2007-12-14 20:08:22 UTC (rev 4734) +++ branches/transforms/CHANGELOG 2007-12-14 20:12:18 UTC (rev 4735) @@ -1,9 +1,16 @@ -2007-12-10 Fix SVG text rendering bug. +2007-12-13 Moved rec2gtk to matplotlib.toolkits.gtktools and rec2excel + to matplotlib.toolkits.exceltools - JDH -2007-12-10 Increase accuracy of circle and ellipse drawing by using an 8-piece - bezier approximation, rather than a 4-piece one. Fix PDF, SVG and - Cairo backends so they can draw paths (meaning ellipses as well). +2007-12-12 Support alpha-blended text in the Agg and Svg backends - + MGD +2007-12-10 Fix SVG text rendering bug. - MGD + +2007-12-10 Increase accuracy of circle and ellipse drawing by using an + 8-piece bezier approximation, rather than a 4-piece one. + Fix PDF, SVG and Cairo backends so they can draw paths + (meaning ellipses as well). - MGD + 2007-12-07 Issue a warning when drawing an image on a non-linear axis. - MGD 2007-12-06 let widgets.Cursor initialize to the lower x and y bounds @@ -18,6 +25,10 @@ (This was a regression since 0.90 caused by the refactoring of font_manager.py) - MGD +2007-12-05 Support arbitrary rotation of usetex text in Agg backend. - MGD + +2007-12-04 Support '|' as a character in mathtext - MGD + =============================================================== 2007-11-27 Released 0.91.1 at revision 4517 Modified: branches/transforms/lib/matplotlib/toolkits/__init__.py =================================================================== --- branches/transforms/lib/matplotlib/toolkits/__init__.py 2007-12-14 20:08:22 UTC (rev 4734) +++ branches/transforms/lib/matplotlib/toolkits/__init__.py 2007-12-14 20:12:18 UTC (rev 4735) @@ -1,4 +1,4 @@ -try: - __import__('pkg_resources').declare_namespace(__name__) -except ImportError: - pass # must not have setuptools +#try: +# __import__('pkg_resources').declare_namespace(__name__) +#except ImportError: +# pass # must not have setuptools This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-12-17 15:43:08
|
Revision: 4758 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4758&view=rev Author: mdboom Date: 2007-12-17 07:43:03 -0800 (Mon, 17 Dec 2007) Log Message: ----------- Merged revisions 4735-4757 via svnmerge from http://matplotlib.svn.sf.net/svnroot/matplotlib/trunk/matplotlib ........ r4745 | jdh2358 | 2007-12-15 16:33:38 -0500 (Sat, 15 Dec 2007) | 1 line changed %g to %r for rec2csv ........ r4747 | astraw | 2007-12-16 14:28:46 -0500 (Sun, 16 Dec 2007) | 1 line Add test for rec2csv and csv2rec roundtrip not losing precision. ........ r4748 | astraw | 2007-12-16 15:53:35 -0500 (Sun, 16 Dec 2007) | 1 line rec2csv does not close filehandles passed in open ........ r4749 | astraw | 2007-12-16 18:19:59 -0500 (Sun, 16 Dec 2007) | 1 line fix csv2rec roundtrip for funky strings, too ........ r4755 | jdh2358 | 2007-12-16 23:37:38 -0500 (Sun, 16 Dec 2007) | 1 line mods to support dates in csv2rec and friends ........ Modified Paths: -------------- branches/transforms/CHANGELOG branches/transforms/lib/matplotlib/cbook.py branches/transforms/lib/matplotlib/mlab.py branches/transforms/lib/matplotlib/mpl-data/matplotlib.conf.template Added Paths: ----------- branches/transforms/unit/mlab_unit.py Property Changed: ---------------- branches/transforms/ Property changes on: branches/transforms ___________________________________________________________________ Name: svnmerge-integrated - /trunk/matplotlib:1-4734 + /trunk/matplotlib:1-4757 Modified: branches/transforms/CHANGELOG =================================================================== --- branches/transforms/CHANGELOG 2007-12-17 15:41:47 UTC (rev 4757) +++ branches/transforms/CHANGELOG 2007-12-17 15:43:03 UTC (rev 4758) @@ -1,3 +1,6 @@ +2007-12-16 rec2csv saves doubles without losing precision. Also, it + does not close filehandles passed in open. - JDH,ADS + 2007-12-13 Moved rec2gtk to matplotlib.toolkits.gtktools and rec2excel to matplotlib.toolkits.exceltools - JDH Modified: branches/transforms/lib/matplotlib/cbook.py =================================================================== --- branches/transforms/lib/matplotlib/cbook.py 2007-12-17 15:41:47 UTC (rev 4757) +++ branches/transforms/lib/matplotlib/cbook.py 2007-12-17 15:43:03 UTC (rev 4758) @@ -225,7 +225,7 @@ except TypeError: return False else: return True -def to_filehandle(fname, flag='r'): +def to_filehandle(fname, flag='r', return_opened=False): """ fname can be a filename or a file handle. Support for gzipped files is automatic, if the filename ends in .gz. flag is a @@ -237,10 +237,14 @@ fh = gzip.open(fname, flag) else: fh = file(fname, flag) + opened = True elif hasattr(fname, 'seek'): fh = fname + opened = False else: raise ValueError('fname must be a string or file handle') + if return_opened: + return fh, opened return fh def flatten(seq, scalarp=is_scalar): Modified: branches/transforms/lib/matplotlib/mlab.py =================================================================== --- branches/transforms/lib/matplotlib/mlab.py 2007-12-17 15:41:47 UTC (rev 4757) +++ branches/transforms/lib/matplotlib/mlab.py 2007-12-17 15:43:03 UTC (rev 4758) @@ -2129,6 +2129,7 @@ process_skiprows(reader) + dateparser = dateutil.parser.parse def myfloat(x): if x==missing: @@ -2136,9 +2137,18 @@ else: return float(x) + def mydate(x): + # try and return a date object + d = dateparser(x) + + if d.hour>0 or d.minute>0 or d.second>0: + raise ValueError('not a date') + return d.date() + + def get_func(item, func): # promote functions in this order - funcmap = {int:myfloat, myfloat:dateutil.parser.parse, dateutil.parser.parse:str} + funcmap = {int:myfloat, myfloat:mydate, mydate:dateparser, dateparser:str} try: func(item) except: if func==str: @@ -2238,8 +2248,12 @@ class FormatString(FormatObj): def tostr(self, x): - return '"%s"'%self.toval(x) + val = repr(x) + return val[1:-1] +#class FormatString(FormatObj): +# def tostr(self, x): +# return '"%r"'%self.toval(x) class FormatFormatStr(FormatObj): def __init__(self, fmt): @@ -2297,7 +2311,7 @@ npy.float32 : FormatFloat(), npy.float64 : FormatFloat(), npy.object_ : FormatObj(), - npy.string_ : FormatObj(), + npy.string_ : FormatString(), } def get_formatd(r, formatd=None): @@ -2317,7 +2331,7 @@ format = copy.deepcopy(format) if isinstance(format, FormatFloat): format.scale = 1. # override scaling for storage - format.fmt = '%g' # maximal precision + format.fmt = '%r' return format def rec2csv(r, fname, delimiter=',', formatd=None): @@ -2335,13 +2349,14 @@ for i, name in enumerate(r.dtype.names): funcs.append(csvformat_factory(formatd[name]).tostr) - fh = cbook.to_filehandle(fname, 'w') + fh, opened = cbook.to_filehandle(fname, 'w', return_opened=True) writer = csv.writer(fh, delimiter=delimiter) header = r.dtype.names writer.writerow(header) for row in r: writer.writerow([func(val) for func, val in zip(funcs, row)]) - fh.close() + if opened: + fh.close() Modified: branches/transforms/lib/matplotlib/mpl-data/matplotlib.conf.template =================================================================== --- branches/transforms/lib/matplotlib/mpl-data/matplotlib.conf.template 2007-12-17 15:41:47 UTC (rev 4757) +++ branches/transforms/lib/matplotlib/mpl-data/matplotlib.conf.template 2007-12-17 15:43:03 UTC (rev 4758) @@ -3,32 +3,32 @@ # This is a sample matplotlib configuration file. It should be placed # in HOME/.matplotlib/matplotlibrc (unix/linux like systems) and # C:\Documents and Settings\yourname\.matplotlib (win32 systems) -# +# # By default, the installer will overwrite the existing file in the install # path, so if you want to preserve yours, please move it to your HOME dir and # set the environment variable if necessary. -# +# # This file is best viewed in a editor which supports ini or conf mode syntax # highlighting. -# +# # Blank lines, or lines starting with a comment symbol, are ignored, # as are trailing comments. Other lines must have the format -# +# # key = val optional comment -# +# # val should be valid python syntax, just as you would use when setting # properties using rcParams. This should become more obvious by inspecting # the default values listed herein. -# +# # Colors: for the color values below, you can either use # - a matplotlib color string, such as r | k | b # - an rgb tuple, such as (1.0, 0.5, 0.0) # - a hex string, such as #ff00ff or ff00ff # - a scalar grayscale intensity such as 0.75 # - a legal html color name, eg red | blue | darkslategray -# +# # Interactivity: see http://matplotlib.sourceforge.net/interactive.html. -# +# # ### CONFIGURATION BEGINS HERE ### # a value of type 'str' @@ -42,7 +42,7 @@ # 'Africa/Abidjan' or 'Africa/Accra' or 'Africa/Addis_Ababa' or # 'Africa/Algiers' or 'Africa/Asmara' or 'Africa/Asmera' or 'Africa/Bamako' or # 'Africa/Bangui' or 'Africa/Banjul' or 'Africa/Bissau' or 'Africa/Blantyre' -# <...snipped 156 lines...> +# <...snipped 156 lines...> # 'US/Michigan' or 'US/Mountain' or 'US/Pacific' or 'US/Pacific-New' or # 'US/Samoa' or 'UTC' or 'Universal' or 'W-SU' or 'WET' or 'Zulu' or # 'posixrules' @@ -108,10 +108,10 @@ [[ps]] # 3 or 42 fonttype = 3 - # 'auto' or 'letter' or 'legal' or 'ledger' or 'A0' or 'A1' or 'A2' or - # 'A3' or 'A4' or 'A5' or 'A6' or 'A7' or 'A8' or 'A9' or 'A10' or - # 'B0' or 'B1' or 'B2' or 'B3' or 'B4' or 'B5' or 'B6' or 'B7' or 'B8' - # or 'B9' or 'B10' + # auto | letter | legal | ledger | A0 | A1 | A2 | + # A3 | A4 | A5 | A6 | A7 | A8 | A9 | A10 | + # B0 | B1 | B2 | B3 | B4 | B5 | B6 | B7 | B8 + # | B9 | B10 papersize = 'letter' # a value of type 'bool' useafm = False @@ -216,7 +216,7 @@ # 'Accent' or 'Accent_r' or 'Blues' or 'Blues_r' or 'BrBG' or 'BrBG_r' or # 'BuGn' or 'BuGn_r' or 'BuPu' or 'BuPu_r' or 'Dark2' or 'Dark2_r' or # 'GnBu' or 'GnBu_r' or 'Greens' or 'Greens_r' or 'Greys' or 'Greys_r' or - # <...snipped 16 lines...> + # <...snipped 16 lines...> # 'pink_r' or 'prism' or 'prism_r' or 'spectral' or 'spectral_r' or # 'spring' or 'spring_r' or 'summer' or 'summer_r' or 'winter' or # 'winter_r' @@ -404,4 +404,4 @@ # a value of type 'float' pad = 4.0 # a value of type 'float' - size = 2.0 \ No newline at end of file + size = 2.0 Copied: branches/transforms/unit/mlab_unit.py (from rev 4755, trunk/matplotlib/unit/mlab_unit.py) =================================================================== --- branches/transforms/unit/mlab_unit.py (rev 0) +++ branches/transforms/unit/mlab_unit.py 2007-12-17 15:43:03 UTC (rev 4758) @@ -0,0 +1,59 @@ +import datetime, StringIO, unittest +import matplotlib.mlab as mlab +import numpy + +class TestMlab(unittest.TestCase): + def test_csv2rec_closefile(self): + # If passed a file-like object, rec2csv should not close it. + ra=numpy.rec.array([(123, 1197346475.0137341), (456, 123.456)], + dtype=[('a', '<i8'), ('b', '<f8')]) + fh = StringIO.StringIO() + mlab.rec2csv( ra, fh ) + self.failIf( fh.closed ) + + def test_csv2rec_roundtrip(self): + + # Make sure double-precision floats and strings pass through a + # roundtrip unaltered. + + # A bug in numpy (fixed in r4602) meant that numpy scalars + # lost precision when passing through repr(). csv2rec was + # affected by this. This test will only pass on numpy >= + # 1.0.5. + delta = datetime.timedelta(days=1) + date0 = datetime.date(2007,12,16) + date1 = date0 + delta + date2 = date1 + delta + + delta = datetime.timedelta(days=1) + datetime0 = datetime.datetime(2007,12,16,22,29,34,924122) + datetime1 = datetime0 + delta + datetime2 = datetime1 + delta + ra=numpy.rec.fromrecords([ + (123, date0, datetime0, 1197346475.0137341, 'a,bc'), + (456, date1, datetime1, 123.456, 'd\'ef'), + (789, date2, datetime2, 0.000000001, 'ghi'), + ], + names='intdata,datedata,datetimedata,floatdata,stringdata') + + fh = StringIO.StringIO() + mlab.rec2csv( ra, fh ) + fh.seek(0) + if 0: + print 'CSV contents:','-'*40 + print fh.read() + print '-'*40 + fh.seek(0) + ra2 = mlab.csv2rec(fh) + fh.close() + #print 'ra', ra + #print 'ra2', ra2 + for name in ra.dtype.names: + if 0: + print name, repr(ra[name]), repr(ra2[name]) + dt = ra.dtype[name] + print 'repr(dt.type)',repr(dt.type) + self.failUnless( numpy.all(ra[name] == ra2[name]) ) # should not fail with numpy 1.0.5 + +if __name__=='__main__': + unittest.main() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-12-18 17:46:03
|
Revision: 4767 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4767&view=rev Author: mdboom Date: 2007-12-18 09:46:01 -0800 (Tue, 18 Dec 2007) Log Message: ----------- Preliminary version of "adding new scales and projections" document. (In reST, since that appears to be where mpl documentation is heading.) Added Paths: ----------- branches/transforms/doc/ branches/transforms/doc/devel/ branches/transforms/doc/devel/add_new_projection.rst Added: branches/transforms/doc/devel/add_new_projection.rst =================================================================== --- branches/transforms/doc/devel/add_new_projection.rst (rev 0) +++ branches/transforms/doc/devel/add_new_projection.rst 2007-12-18 17:46:01 UTC (rev 4767) @@ -0,0 +1,192 @@ +=============================================== +Adding new scales and projections to matplotlib +=============================================== + +.. ::author Michael Droettboom + +Matplotlib supports the addition of new transformations that transform +the data before it is displayed. Separable transformations, that work +on a single dimension are called "scales", and non-separable +transformations, that take data in two or more dimensions as input are +called "projections". + +This document is intended for developers and advanced users who need +to add more scales and projections to matplotlib. + +From the user's perspective, the scale of a plot can be set with +``set_xscale`` and ``set_yscale``. Choosing the projection +currently has no *standardized* method. [MGDTODO] + +Creating a new scale +==================== + +Adding a new scale consists of defining a subclass of ``ScaleBase``, +that brings together the following elements: + + - A transformation from data space into plot space. + + - An inverse of that transformation. For example, this is used to + convert mouse positions back into data space. + + - A function to limit the range of the axis to acceptable values. A + log scale, for instance, would prevent the range from including + values less than or equal to zero. + + - Locators (major and minor) that determine where to place ticks in + the plot, and optionally, how to adjust the limits of the plot to + some "good" values. + + - Formatters (major and minor) that specify how the tick labels + should be drawn. + +There are a number of ``Scale`` classes in ``scale.py`` that may be +used as starting points for new scales. As an example, this document +presents adding a new scale ``MercatorLatitudeScale`` which can be +used to plot latitudes in a Mercator_ projection. For simplicity, +this scale assumes that it has a fixed center at the equator. The +code presented here is a simplification of actual code in +``matplotlib``, with complications added only for the sake of +optimization removed. + +First define a new subclass of ``ScaleBase``:: + + class MercatorLatitudeScale(ScaleBase): + """ + Scales data in range -pi/2 to pi/2 (-90 to 90 degrees) using + the system used to scale latitudes in a Mercator projection. + + The scale function: + ln(tan(y) + sec(y)) + + The inverse scale function: + atan(sinh(y)) + + Since the Mercator scale tends to infinity at +/- 90 degrees, + there is user-defined threshold, above and below which nothing + will be plotted. This defaults to +/- 85 degrees. + + source: + http://en.wikipedia.org/wiki/Mercator_projection + """ + name = 'mercator_latitude' + +This class must have a member ``name`` that defines the string used to +select the scale. For example, +``gca().set_yscale("mercator_latitude")`` would be used to select the +Mercator latitude scale. + +Next define two nested classes: one for the data transformation and +one for its inverse. Both of these classes must be subclasses of +``Transform`` (defined in ``transforms.py``).:: + + class MercatorLatitudeTransform(Transform): + input_dims = 1 + output_dims = 1 + +There are two class-members that must be defined. ``input_dims`` and +``output_dims`` specify number of input dimensions and output +dimensions to the transformation. These are used by the +transformation framework to do some error checking and prevent +incompatible transformations from being connected together. When +defining transforms for a scale, which are by definition separable and +only have one dimension, these members should always be 1. + +``MercatorLatitudeTransform`` has a simple constructor that takes and +stores the *threshold* for the Mercator projection (to limit its range +to prevent plotting to infinity).:: + + def __init__(self, thresh): + Transform.__init__(self) + self.thresh = thresh + +The ``transform`` method is where the real work happens: It takes an N +x 1 ``numpy`` array and returns a transformed copy. Since the range +of the Mercator scale is limited by the user-specified threshold, the +input array must be masked to contain only valid values. +``matplotlib`` will handle masked arrays and remove the out-of-range +data from the plot. Importantly, the transformation should return an +array that is the same shape as the input array, since these values +need to remain synchronized with values in the other dimension.:: + + def transform(self, a): + masked = ma.masked_where((a < -self.thresh) | (a > self.thresh), a) + return ma.log(ma.abs(ma.tan(masked) + 1.0 / ma.cos(masked))) + +Lastly for the transformation class, define a method to get the +inverse transformation:: + + def inverted(self): + return MercatorLatitudeScale.InvertedMercatorLatitudeTransform(self.thresh) + +The inverse transformation class follows the same pattern, but +obviously the mathematical operation performed is different:: + + class InvertedMercatorLatitudeTransform(Transform): + input_dims = 1 + output_dims = 1 + + def __init__(self, thresh): + Transform.__init__(self) + self.thresh = thresh + + def transform(self, a): + return npy.arctan(npy.sinh(a)) + + def inverted(self): + return MercatorLatitudeScale.MercatorLatitudeTransform(self.thresh) + +Now we're back to methods for the ``MercatorLatitudeScale`` class. +Any keyword arguments passed to ``set_xscale`` and ``set_yscale`` will +be passed along to the scale's constructor. In the case of +``MercatorLatitudeScale``, the ``thresh`` keyword argument specifies +the degree at which to crop the plot data. The constructor also +creates a local instance of the ``Transform`` class defined above, +which is made available through its ``get_transform`` method:: + + def __init__(self, axis, **kwargs): + thresh = kwargs.pop("thresh", (85 / 180.0) * npy.pi) + if thresh >= npy.pi / 2.0: + raise ValueError("thresh must be less than pi/2") + self.thresh = thresh + self._transform = self.MercatorLatitudeTransform(thresh) + + def get_transform(self): + return self._transform + +The ``limit_range_for_scale`` method must be provided to limit the +bounds of the axis to the domain of the function. In the case of +Mercator, the bounds should be limited to the threshold that was +passed in. Unlike the autoscaling provided by the tick locators, this +range limiting will always be adhered to, whether the axis range is set +manually, determined automatically or changed through panning and +zooming:: + + def limit_range_for_scale(self, vmin, vmax, minpos): + return max(vmin, -self.thresh), min(vmax, self.thresh) + +Lastly, the ``set_default_locators_and_formatters`` method sets up the +locators and formatters to use with the scale. It may be that the new +scale requires new locators and formatters. Doing so is outside the +scope of this document, but there are many examples in ``ticker.py``. +The Mercator example uses a fixed locator from -90 to 90 degrees and a +custom formatter class to put convert the radians to degrees and put a +degree symbol after the value:: + + def set_default_locators_and_formatters(self, axis): + class DegreeFormatter(Formatter): + def __call__(self, x, pos=None): + # \u00b0 : degree symbol + return u"%d\u00b0" % ((x / npy.pi) * 180.0) + + deg2rad = npy.pi / 180.0 + axis.set_major_locator(FixedLocator( + npy.arange(-90, 90, 10) * deg2rad)) + axis.set_major_formatter(DegreeFormatter()) + axis.set_minor_formatter(DegreeFormatter()) + +Now that the Scale class has been defined, it must be registered so +that ``matplotlib`` can find it:: + + register_scale(MercatorLatitudeScale) + +.. _Mercator: http://en.wikipedia.org/wiki/Mercator_projection \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-12-18 21:10:22
|
Revision: 4773 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4773&view=rev Author: mdboom Date: 2007-12-18 13:10:20 -0800 (Tue, 18 Dec 2007) Log Message: ----------- Merged revisions 4758-4772 via svnmerge from http://matplotlib.svn.sf.net/svnroot/matplotlib/trunk/matplotlib ........ r4768 | mdboom | 2007-12-18 14:17:13 -0500 (Tue, 18 Dec 2007) | 2 lines Update information about the backends (Ps and Pdf were not mentioned.) ........ r4769 | mdboom | 2007-12-18 15:57:40 -0500 (Tue, 18 Dec 2007) | 2 lines Update list of supported math symbols. ........ r4770 | mdboom | 2007-12-18 15:59:56 -0500 (Tue, 18 Dec 2007) | 2 lines Update list of supported backends. ........ Modified Paths: -------------- branches/transforms/lib/matplotlib/mathtext.py branches/transforms/setup.cfg.template Property Changed: ---------------- branches/transforms/ Property changes on: branches/transforms ___________________________________________________________________ Name: svnmerge-integrated - /trunk/matplotlib:1-4757 + /trunk/matplotlib:1-4772 Modified: branches/transforms/lib/matplotlib/mathtext.py =================================================================== --- branches/transforms/lib/matplotlib/mathtext.py 2007-12-18 21:09:25 UTC (rev 4772) +++ branches/transforms/lib/matplotlib/mathtext.py 2007-12-18 21:10:20 UTC (rev 4773) @@ -81,48 +81,85 @@ Allowed TeX symbols: - [MGDTODO: This list is no longer exhaustive and needs to be updated] + $ \% \AA \AE \BbbC \BbbN \BbbP \BbbQ \BbbR \BbbZ \Bumpeq \Cap \Colon + \Cup \Delta \Doteq \Downarrow \Equiv \Finv \Gamma \H \Im \L \Lambda + \Ldsh \Leftarrow \Leftrightarrow \Lleftarrow \Lsh \Nearrow \Nwarrow + \O \OE \Omega \P \Phi \Pi \Psi \Rdsh \Re \Rightarrow \Rrightarrow + \Rsh \S \Searrow \Sigma \Subset \Supset \Swarrow \Theta \Uparrow + \Updownarrow \Upsilon \Vdash \Vert \Vvdash \Xi \_ \__sqrt__ \ac + \acute \acwopencirclearrow \adots \ae \aleph \alpha \angle \approx + \approxeq \approxident \arceq \ast \asymp \backcong \backprime + \backsim \backsimeq \backslash \bar \barleftarrow \barwedge \because + \beta \beth \between \bigcap \bigcirc \bigcup \bigodot \bigoplus + \bigotimes \bigstar \bigtriangledown \bigtriangleup \biguplus + \bigvee \bigwedge \blacksquare \blacktriangle \blacktriangledown + \blacktriangleleft \blacktriangleright \bot \bowtie \boxbar \boxdot + \boxminus \boxplus \boxtimes \breve \bullet \bumpeq \c \candra \cap + \carriagereturn \cdot \cdotp \cdots \check \checkmark \chi \circ + \circeq \circledR \circledS \circledast \circledcirc \circleddash + \circumflexaccent \clubsuit \clubsuitopen \colon \coloneq + \combiningacuteaccent \combiningbreve \combiningdiaeresis + \combiningdotabove \combininggraveaccent \combiningoverline + \combiningrightarrowabove \combiningtilde \complement \cong \coprod + \copyright \cup \cupdot \curlyeqprec \curlyeqsucc \curlyvee + \curlywedge \curvearrowleft \curvearrowright \cwopencirclearrow \d + \dag \daleth \danger \dashv \ddag \ddddot \dddot \ddot \ddots + \degree \delta \diamond \diamondsuit \digamma \div \divideontimes + \dot \doteq \dotminus \dotplus \dots \doublebarwedge ? \downarrow + \downdownarrows \downharpoonleft \downharpoonright \downzigzagarrow + \ell \emdash \emptyset \endash \enspace \epsilon \eqcirc \eqcolon + \eqdef \eqgtr \eqless \eqsim \equiv \eta \eth \exists \fallingdotseq + \flat \forall \frakC \frakZ \frown \gamma \geq \geqq \gg \ggg \gimel + \gneqq \gnsim \grave \greater \gtrdot \gtreqless \gtrless \gtrsim + \hat \heartsuit \hookleftarrow \hookrightarrow \i \iiint \iint + \imageof \imath \in \infty \int \intercal \invnot \iota \jmath \k + \kappa \kernelcontraction \l \lambda \lambdabar \lasp \lbrace + \lbrack \lceil \leftangle \leftarrow \leftarrowtail \leftbrace + \leftharpoonaccent \leftharpoondown \leftharpoonup \leftleftarrows + \leftparen \leftrightarrow \leftrightarrows \leftrightharpoons + \leftthreetimes \leq \leqq \less \lessdot \lesseqgtr \lessgtr + \lesssim \lfloor \ll \llcorner \lll \lneqq \lnsim \looparrowleft + \looparrowright \lq \lrcorner \ltimes \maltese \mapsdown \mapsfrom + \mapsto \mapsup \measeq \measuredangle \mho \mid \minus \models \mp + \mu \multimap \nLeftarrow \nLeftrightarrow \nRightarrow \nVDash + \nVdash \nabla \napprox \natural \ncong \ne \nearrow \neg \nequiv + \nexists \ngeq \ngtr \ni \nleftarrow \nleftrightarrow \nleq \nless + \nmid \not \notin \nparallel \nprec \nrightarrow \nsim \nsime + \nsubset \nsubseteq \nsucc \nsupset \nsupseteq \ntriangleleft + \ntrianglelefteq \ntriangleright \ntrianglerighteq \nu \nvDash + \nvdash \nwarrow \o \obar \ocirc \odot \oe \oiiint \oiint \oint + \omega \ominus \oplus \origof \oslash \otimes \overarc + \overleftarrow \overleftrightarrow \parallel \partial \phi \pi + \pitchfork \pm \prec \preccurlyeq \preceq \precnsim \precsim \prime + \prod \propto \prurel \psi \quad \questeq \rasp \rbrace \rbrack + \rceil \rfloor \rho \rightangle \rightarrow \rightarrowbar + \rightarrowtail \rightbrace \rightharpoonaccent \rightharpoondown + \rightharpoonup \rightleftarrows \rightleftharpoons \rightparen + \rightrightarrows \rightthreetimes \rightzigzagarrow \risingdotseq + \rq \rtimes \scrB \scrE \scrF \scrH \scrI \scrL \scrM \scrR \scre + \scrg \scro \scurel \searrow \sharp \sigma \sim \simeq \slash + \smallsetminus \smile \solbar \spadesuit \spadesuitopen + \sphericalangle \sqcap \sqcup \sqsubset \sqsubseteq \sqsupset + \sqsupseteq \ss \star \stareq \sterling \subset \subseteq \subsetneq + \succ \succcurlyeq \succeq \succnsim \succsim \sum \supset \supseteq + \supsetneq \swarrow \t \tau \textasciiacute \textasciicircum + \textasciigrave \textasciitilde \textexclamdown \textquestiondown + \textquotedblleft \textquotedblright \therefore \theta \thickspace + \thinspace \tilde \times \to \top \triangledown \triangleleft + \trianglelefteq \triangleq \triangleright \trianglerighteq + \turnednot \twoheaddownarrow \twoheadleftarrow \twoheadrightarrow + \twoheaduparrow \ulcorner \underbar \uparrow \updownarrow + \updownarrowbar \updownarrows \upharpoonleft \upharpoonright \uplus + \upsilon \upuparrows \urcorner \vDash \varepsilon \varkappa + \varnothing \varphi \varpi \varrho \varsigma \vartheta \vartriangle + \vartriangleleft \vartriangleright \vdash \vdots \vec \vee \veebar + \veeeq \vert \wedge \wedgeq \widehat \widetilde \wp \wr \xi \yen + \zeta \{ \| \} - \/ \Delta \Downarrow \Gamma \Im \LEFTangle \LEFTbrace \LEFTbracket - \LEFTparen \Lambda \Leftarrow \Leftbrace \Leftbracket \Leftparen - \Leftrightarrow \Omega \P \Phi \Pi \Psi \RIGHTangle \RIGHTbrace - \RIGHTbracket \RIGHTparen \Re \Rightarrow \Rightbrace \Rightbracket - \Rightparen \S \SQRT \Sigma \Sqrt \Theta \Uparrow \Updownarrow - \Upsilon \Vert \Xi \aleph \alpha \approx \angstrom \ast \asymp - \backslash \beta \bigcap \bigcirc \bigcup \bigodot \bigoplus - \bigotimes \bigtriangledown \bigtriangleup \biguplus \bigvee - \bigwedge \bot \bullet \cap \cdot \chi \circ \clubsuit \coprod \cup - \dag \dashv \ddag \delta \diamond \diamondsuit \div \downarrow \ell - \emptyset \epsilon \equiv \eta \exists \flat \forall \frown \gamma - \geq \gg \heartsuit \hspace \imath \in \infty \int \iota \jmath - \kappa \lambda \langle \lbrace \lceil \leftangle \leftarrow - \leftbrace \leftbracket \leftharpoondown \leftharpoonup \leftparen - \leftrightarrow \leq \lfloor \ll \mid \mp \mu \nabla \natural - \nearrow \neg \ni \nu \nwarrow \odot \oint \omega \ominus \oplus - \oslash \otimes \phi \pi \pm \prec \preceq \prime \prod \propto \psi - \rangle \rbrace \rceil \rfloor \rho \rightangle \rightarrow - \rightbrace \rightbracket \rightharpoondown \rightharpoonup - \rightparen \searrow \sharp \sigma \sim \simeq \slash \smile - \spadesuit \sqcap \sqcup \sqrt \sqsubseteq \sqsupseteq \subset - \subseteq \succ \succeq \sum \supset \supseteq \swarrow \tau \theta - \times \top \triangleleft \triangleright \uparrow \updownarrow - \uplus \upsilon \varepsilon \varphi \varphi \varrho \varsigma - \vartheta \vdash \vee \vert \wedge \wp \wr \xi \zeta - - BACKENDS - mathtext currently works with GTK, Agg, GTKAgg, TkAgg and WxAgg and - PS, though only horizontal and vertical rotations are supported in - *Agg + mathtext currently works with all backends. - mathtext now embeds the TrueType computer modern fonts into the PS - file, so what you see on the screen should be what you get on paper. - - Backends which don't support mathtext will just render the TeX - string as a literal. Stay tuned. - - KNOWN ISSUES: - Certainly there are some... Modified: branches/transforms/setup.cfg.template =================================================================== --- branches/transforms/setup.cfg.template 2007-12-18 21:09:25 UTC (rev 4772) +++ branches/transforms/setup.cfg.template 2007-12-18 21:10:20 UTC (rev 4773) @@ -13,12 +13,12 @@ #verbose = True [provide_packages] -# By default, matplotlib checks for a few dependencies and -# installs them if missing. This feature can be turned off +# By default, matplotlib checks for a few dependencies and +# installs them if missing. This feature can be turned off # by uncommenting the following lines. Acceptible values are: # True: install, overwrite an existing installation # False: do not install -# auto: install only if the package is unavailable. This +# auto: install only if the package is unavailable. This # is the default behavior # ## Date/timezone support: @@ -30,28 +30,28 @@ #configobj = False [gui_support] -# Matplotlib supports multiple GUI toolkits, including Cocoa, -# GTK, Fltk, Qt, Qt4, Tk, and WX. Support for many of these -# toolkits requires AGG, the Anti-Grain Geometry library, which +# Matplotlib supports multiple GUI toolkits, including Cocoa, +# GTK, Fltk, Qt, Qt4, Tk, and WX. Support for many of these +# toolkits requires AGG, the Anti-Grain Geometry library, which # is provided by matplotlib and built by default. # -# Some backends are written in pure Python, and others require -# extension code to be compiled. By default, matplotlib checks -# for these GUI toolkits during installation and, if present, -# compiles the required extensions to support the toolkit. GTK -# support requires the GTK runtime environment and PyGTK. Wx -# support requires wxWidgets and wxPython. Tk support requires -# Tk and Tkinter. The other GUI toolkits do not require any -# extension code, and can be used as long as the libraries are +# Some backends are written in pure Python, and others require +# extension code to be compiled. By default, matplotlib checks +# for these GUI toolkits during installation and, if present, +# compiles the required extensions to support the toolkit. GTK +# support requires the GTK runtime environment and PyGTK. Wx +# support requires wxWidgets and wxPython. Tk support requires +# Tk and Tkinter. The other GUI toolkits do not require any +# extension code, and can be used as long as the libraries are # installed on your system. -# +# # You can uncomment any the following lines if you know you do # not want to use the GUI toolkit. Acceptible values are: -# True: build the extension. Exits with a warning if the +# True: build the extension. Exits with a warning if the # required dependencies are not available # False: do not build the extension # auto: build if the required dependencies are available, -# otherwise skip silently. This is the default +# otherwise skip silently. This is the default # behavior # #gtk = False @@ -62,18 +62,20 @@ [rc_options] # User-configurable options # -# Default backend, one of: Agg, Cairo, CocoaAgg, GTK, GTKAgg, -# GTKCairo, FltkAgg, QtAgg, Qt4Agg, SVG, TkAgg, WX, WXAgg. -# Only the Agg and SVG backends do not require external -# dependencies. Do not choose GTK, GTKAgg, GTKCairo, TkAgg or -# WXAgg if you have disabled the relevent extension modules. -# Agg will be used by default. +# Default backend, one of: Agg, Cairo, CocoaAgg, GTK, GTKAgg, +# GTKCairo, FltkAgg, Pdf, Ps, QtAgg, Qt4Agg, SVG, TkAgg, WX, WXAgg. +# +# The Agg, Ps, Pdf and SVG backends do not require external +# dependencies. Do not choose GTK, GTKAgg, GTKCairo, TkAgg or WXAgg if +# you have disabled the relevent extension modules. Agg will be used +# by default. +# #backend = Agg # -# The numerix module was historically used to provide +# The numerix module was historically used to provide # compatibility between the Numeric, numarray, and NumPy array -# packages. Now that NumPy has emerge as the universal array -# package for python, numerix is not really necessary and is -# maintained to provide backward compatibility. Do not change +# packages. Now that NumPy has emerge as the universal array +# package for python, numerix is not really necessary and is +# maintained to provide backward compatibility. Do not change # this unless you have a compelling reason to do so. #numerix = numpy This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-12-20 13:00:24
|
Revision: 4776 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4776&view=rev Author: mdboom Date: 2007-12-20 05:00:20 -0800 (Thu, 20 Dec 2007) Log Message: ----------- Add very preliminary and experimental support for some geo projections. Modified Paths: -------------- branches/transforms/lib/matplotlib/projections/__init__.py Added Paths: ----------- branches/transforms/examples/geo_demo.py branches/transforms/lib/matplotlib/projections/geo.py Added: branches/transforms/examples/geo_demo.py =================================================================== --- branches/transforms/examples/geo_demo.py (rev 0) +++ branches/transforms/examples/geo_demo.py 2007-12-20 13:00:20 UTC (rev 4776) @@ -0,0 +1,16 @@ +import numpy as npy +npy.seterr("raise") + +from pylab import * + +subplot(221, projection="aitoff") +grid(True) + +subplot(222, projection="hammer") +grid(True) + +subplot(223, projection="lambert") +grid(True) + + +show() Modified: branches/transforms/lib/matplotlib/projections/__init__.py =================================================================== --- branches/transforms/lib/matplotlib/projections/__init__.py 2007-12-20 12:49:14 UTC (rev 4775) +++ branches/transforms/lib/matplotlib/projections/__init__.py 2007-12-20 13:00:20 UTC (rev 4776) @@ -1,3 +1,4 @@ +from geo import AitoffAxes, HammerAxes, MolleweideAxes, LambertAxes from polar import PolarAxes from matplotlib import axes @@ -21,7 +22,11 @@ projection_registry.register( axes.Axes, - PolarAxes) + PolarAxes, + AitoffAxes, + HammerAxes, + MolleweideAxes, + LambertAxes) def get_projection_class(projection): if projection is None: Added: branches/transforms/lib/matplotlib/projections/geo.py =================================================================== --- branches/transforms/lib/matplotlib/projections/geo.py (rev 0) +++ branches/transforms/lib/matplotlib/projections/geo.py 2007-12-20 13:00:20 UTC (rev 4776) @@ -0,0 +1,597 @@ +import math + +import numpy as npy +from matplotlib.numerix import npyma as ma + +import matplotlib +rcParams = matplotlib.rcParams +from matplotlib.artist import kwdocd +from matplotlib.axes import Axes +from matplotlib import cbook +from matplotlib.patches import Circle +from matplotlib.path import Path +from matplotlib.ticker import Formatter, Locator, NullLocator, FixedLocator, NullFormatter +from matplotlib.transforms import Affine2D, Affine2DBase, Bbox, \ + BboxTransformTo, IdentityTransform, Transform, TransformWrapper + +class GeoAxes(Axes): + """ + An abstract base class for geographic projections + """ + class ThetaFormatter(Formatter): + """ + Used to format the theta tick labels. Converts the native + unit of radians into degrees and adds a degree symbol. + """ + def __init__(self, round_to=1.0): + self._round_to = round_to + + def __call__(self, x, pos=None): + degrees = (x / npy.pi) * 180.0 + degrees = round(degrees / self._round_to) * self._round_to + # \u00b0 : degree symbol + return u"%d\u00b0" % degrees + + RESOLUTION = 75 + + def cla(self): + Axes.cla(self) + + self.set_longitude_grid(30) + self.set_latitude_grid(15) + self.set_longitude_grid_ends(75) + self.xaxis.set_minor_locator(NullLocator()) + self.yaxis.set_minor_locator(NullLocator()) + self.xaxis.set_ticks_position('none') + self.yaxis.set_ticks_position('none') + + self.grid(rcParams['axes.grid']) + + Axes.set_xlim(self, -npy.pi, npy.pi) + Axes.set_ylim(self, -npy.pi / 2.0, npy.pi / 2.0) + + def _set_lim_and_transforms(self): + self.dataLim = Bbox.unit() + self.viewLim = Bbox.unit() + self.transAxes = BboxTransformTo(self.bbox) + + # Transforms the x and y axis separately by a scale factor + # It is assumed that this part will have non-linear components + self.transScale = TransformWrapper(IdentityTransform()) + + # A (possibly non-linear) projection on the (already scaled) data + self.transProjection = self._get_core_transform(self.RESOLUTION) + + self.transAffine = self._get_affine_transform() + + # The complete data transformation stack -- from data all the + # way to display coordinates + self.transData = \ + self.transProjection + \ + self.transAffine + \ + self.transAxes + + # This is the transform for longitude ticks. + self._xaxis_pretransform = \ + Affine2D() \ + .scale(1.0, self._longitude_cap * 2.0) \ + .translate(0.0, -self._longitude_cap) + self._xaxis_transform = \ + self._xaxis_pretransform + \ + self.transData + self._xaxis_text1_transform = \ + Affine2D().scale(1.0, 0.0) + \ + self.transData + \ + Affine2D().translate(0.0, 4.0) + self._xaxis_text2_transform = \ + Affine2D().scale(1.0, 0.0) + \ + self.transData + \ + Affine2D().translate(0.0, -4.0) + + # This is the transform for latitude ticks. + yaxis_stretch = Affine2D().scale(npy.pi * 2.0, 1.0).translate(-npy.pi, 0.0) + yaxis_space = Affine2D().scale(1.0, 1.1) + self._yaxis_transform = \ + yaxis_stretch + \ + self.transData + yaxis_text_base = \ + yaxis_stretch + \ + self.transProjection + \ + (yaxis_space + \ + self.transAffine + \ + self.transAxes) + self._yaxis_text1_transform = \ + yaxis_text_base + \ + Affine2D().translate(-8.0, 0.0) + self._yaxis_text2_transform = \ + yaxis_text_base + \ + Affine2D().translate(8.0, 0.0) + + def _get_affine_transform(self): + transform = self._get_core_transform(1) + xscale, _ = transform.transform_point((npy.pi, 0)) + _, yscale = transform.transform_point((0, npy.pi / 2.0)) + return Affine2D() \ + .scale(0.5 / xscale, 0.5 / yscale) \ + .translate(0.5, 0.5) + + def update_layout(self, renderer): + t_text, b_text = self.xaxis.get_text_heights(renderer) + l_text, r_text = self.yaxis.get_text_widths(renderer) + originalPosition = self.get_position(True) + title_offset = (b_text - originalPosition.transformed( + self.figure.transFigure).height) / 2.0 + self.titleOffsetTrans.clear().translate(0, title_offset) + + def get_xaxis_transform(self): + return self._xaxis_transform + + def get_xaxis_text1_transform(self, pixelPad): + return self._xaxis_text1_transform, 'bottom', 'center' + + def get_xaxis_text2_transform(self, pixelPad): + return self._xaxis_text2_transform, 'top', 'center' + + def get_yaxis_transform(self): + return self._yaxis_transform + + def get_yaxis_text1_transform(self, pixelPad): + return self._yaxis_text1_transform, 'center', 'right' + + def get_yaxis_text2_transform(self, pixelPad): + return self._yaxis_text2_transform, 'center', 'left' + + def get_axes_patch(self): + return Circle((0.5, 0.5), 0.5) + + def set_yscale(self, *args, **kwargs): + if args[0] != 'linear': + raise NotImplementedError + + set_xscale = set_yscale + + def set_xlim(self, *args, **kwargs): + Axes.set_xlim(self, -npy.pi, npy.pi) + Axes.set_ylim(self, -npy.pi / 2.0, npy.pi / 2.0) + + set_ylim = set_xlim + + def format_coord(self, long, lat): + 'return a format string formatting the coordinate' + long = long * (180.0 / npy.pi) + lat = lat * (180.0 / npy.pi) + if lat >= 0.0: + ns = 'N' + else: + ns = 'S' + if long >= 0.0: + ew = 'E' + else: + ew = 'W' + return u'%f\u00b0%s, %f\u00b0%s' % (abs(lat), ns, abs(long), ew) + + def set_longitude_grid(self, degrees): + """ + Set the number of degrees between each longitude grid. + """ + number = (360.0 / degrees) + 1 + self.xaxis.set_major_locator( + FixedLocator( + npy.linspace(-npy.pi, npy.pi, number, True)[1:-1])) + self._logitude_degrees = degrees + self.xaxis.set_major_formatter(self.ThetaFormatter(degrees)) + + def set_latitude_grid(self, degrees): + """ + Set the number of degrees between each longitude grid. + """ + number = (180.0 / degrees) + 1 + self.yaxis.set_major_locator( + FixedLocator( + npy.linspace(-npy.pi / 2.0, npy.pi / 2.0, number, True)[1:-1])) + self._latitude_degrees = degrees + self.yaxis.set_major_formatter(self.ThetaFormatter(degrees)) + + def set_longitude_grid_ends(self, degrees): + """ + Set the latitude(s) at which to stop drawing the longitude grids. + """ + self._longitude_cap = degrees * (npy.pi / 180.0) + self._xaxis_pretransform \ + .clear() \ + .scale(1.0, self._longitude_cap * 2.0) \ + .translate(0.0, -self._longitude_cap) + + def get_data_ratio(self): + ''' + Return the aspect ratio of the data itself. + ''' + return 1.0 + + ### Interactive panning + + def can_zoom(self): + """ + Return True if this axes support the zoom box + """ + return False + + def start_pan(self, x, y, button): + pass + + def end_pan(self): + pass + + def drag_pan(self, button, key, x, y): + pass + + +class AitoffAxes(GeoAxes): + name = 'aitoff' + + class AitoffTransform(Transform): + """ + The base Aitoff transform. + """ + input_dims = 2 + output_dims = 2 + is_separable = False + + def __init__(self, resolution): + """ + Create a new Aitoff transform. Resolution is the number of steps + to interpolate between each input line segment to approximate its + path in curved Aitoff space. + """ + Transform.__init__(self) + self._resolution = resolution + + def transform(self, ll): + longitude = ll[:, 0:1] + latitude = ll[:, 1:2] + + # Pre-compute some values + half_long = longitude / 2.0 + cos_latitude = npy.cos(latitude) + + alpha = npy.arccos(cos_latitude * npy.cos(half_long)) + # Mask this array, or we'll get divide-by-zero errors + alpha = ma.masked_where(alpha == 0.0, alpha) + # We want unnormalized sinc. numpy.sinc gives us normalized + sinc_alpha = ma.sin(alpha) / alpha + + x = (cos_latitude * npy.sin(half_long)) / sinc_alpha + y = (npy.sin(latitude) / sinc_alpha) + x.set_fill_value(0.0) + y.set_fill_value(0.0) + return npy.concatenate((x.filled(), y.filled()), 1) + transform.__doc__ = Transform.transform.__doc__ + + transform_non_affine = transform + transform_non_affine.__doc__ = Transform.transform_non_affine.__doc__ + + def transform_path(self, path): + vertices = path.vertices + ipath = path.interpolated(self._resolution) + return Path(self.transform(ipath.vertices), ipath.codes) + transform_path.__doc__ = Transform.transform_path.__doc__ + + transform_path_non_affine = transform_path + transform_path_non_affine.__doc__ = Transform.transform_path_non_affine.__doc__ + + def inverted(self): + return AitoffAxes.InvertedAitoffTransform(self._resolution) + inverted.__doc__ = Transform.inverted.__doc__ + + class InvertedAitoffTransform(Transform): + input_dims = 2 + output_dims = 2 + is_separable = False + + def __init__(self, resolution): + Transform.__init__(self) + self._resolution = resolution + + def transform(self, xy): + # MGDTODO: Math is hard ;( + return xy + transform.__doc__ = Transform.transform.__doc__ + + def inverted(self): + return AitoffAxes.AitoffTransform(self._resolution) + inverted.__doc__ = Transform.inverted.__doc__ + + def __init__(self, *args, **kwargs): + self._longitude_cap = npy.pi / 2.0 + GeoAxes.__init__(self, *args, **kwargs) + self.set_aspect(0.5, adjustable='box', anchor='C') + self.cla() + + def _get_core_transform(self, resolution): + return self.AitoffTransform(resolution) + + +class HammerAxes(GeoAxes): + name = 'hammer' + + class HammerTransform(Transform): + """ + The base Hammer transform. + """ + input_dims = 2 + output_dims = 2 + is_separable = False + + def __init__(self, resolution): + """ + Create a new Hammer transform. Resolution is the number of steps + to interpolate between each input line segment to approximate its + path in curved Hammer space. + """ + Transform.__init__(self) + self._resolution = resolution + + def transform(self, ll): + longitude = ll[:, 0:1] + latitude = ll[:, 1:2] + + # Pre-compute some values + half_long = longitude / 2.0 + cos_latitude = npy.cos(latitude) + sqrt2 = npy.sqrt(2.0) + + alpha = 1.0 + cos_latitude * npy.cos(half_long) + x = (2.0 * sqrt2) * (cos_latitude * npy.sin(half_long)) / alpha + y = (sqrt2 * npy.sin(latitude)) / alpha + return npy.concatenate((x, y), 1) + transform.__doc__ = Transform.transform.__doc__ + + transform_non_affine = transform + transform_non_affine.__doc__ = Transform.transform_non_affine.__doc__ + + def transform_path(self, path): + vertices = path.vertices + ipath = path.interpolated(self._resolution) + return Path(self.transform(ipath.vertices), ipath.codes) + transform_path.__doc__ = Transform.transform_path.__doc__ + + transform_path_non_affine = transform_path + transform_path_non_affine.__doc__ = Transform.transform_path_non_affine.__doc__ + + def inverted(self): + return HammerAxes.InvertedHammerTransform(self._resolution) + inverted.__doc__ = Transform.inverted.__doc__ + + class InvertedHammerTransform(Transform): + input_dims = 2 + output_dims = 2 + is_separable = False + + def __init__(self, resolution): + Transform.__init__(self) + self._resolution = resolution + + def transform(self, xy): + x = xy[:, 0:1] + y = xy[:, 1:2] + + quarter_x = 0.25 * x + half_y = 0.5 * y + z = npy.sqrt(1.0 - quarter_x*quarter_x - half_y*half_y) + longitude = 2 * npy.arctan((z*x) / (2.0 * (2.0*z*z - 1.0))) + latitude = npy.arcsin(y*z) + return npy.concatenate((longitude, latitude), 1) + transform.__doc__ = Transform.transform.__doc__ + + def inverted(self): + return HammerAxes.HammerTransform(self._resolution) + inverted.__doc__ = Transform.inverted.__doc__ + + def __init__(self, *args, **kwargs): + self._longitude_cap = npy.pi / 2.0 + GeoAxes.__init__(self, *args, **kwargs) + self.set_aspect(0.5, adjustable='box', anchor='C') + self.cla() + + def _get_core_transform(self, resolution): + return self.HammerTransform(resolution) + + +class MolleweideAxes(GeoAxes): + name = 'molleweide' + + class MolleweideTransform(Transform): + """ + The base Molleweide transform. + """ + input_dims = 2 + output_dims = 2 + is_separable = False + + def __init__(self, resolution): + """ + Create a new Molleweide transform. Resolution is the number of steps + to interpolate between each input line segment to approximate its + path in curved Molleweide space. + """ + Transform.__init__(self) + self._resolution = resolution + + def transform(self, ll): + longitude = ll[:, 0:1] + latitude = ll[:, 1:2] + + aux = 2.0 * npy.arcsin((2.0 * latitude) / npy.pi) + x = (2.0 * npy.sqrt(2.0) * longitude * npy.cos(aux)) / npy.pi + y = (npy.sqrt(2.0) * npy.sin(aux)) + + return npy.concatenate((x, y), 1) + transform.__doc__ = Transform.transform.__doc__ + + transform_non_affine = transform + transform_non_affine.__doc__ = Transform.transform_non_affine.__doc__ + + def transform_path(self, path): + vertices = path.vertices + ipath = path.interpolated(self._resolution) + return Path(self.transform(ipath.vertices), ipath.codes) + transform_path.__doc__ = Transform.transform_path.__doc__ + + transform_path_non_affine = transform_path + transform_path_non_affine.__doc__ = Transform.transform_path_non_affine.__doc__ + + def inverted(self): + return MolleweideAxes.InvertedMolleweideTransform(self._resolution) + inverted.__doc__ = Transform.inverted.__doc__ + + class InvertedMolleweideTransform(Transform): + input_dims = 2 + output_dims = 2 + is_separable = False + + def __init__(self, resolution): + Transform.__init__(self) + self._resolution = resolution + + def transform(self, xy): + # MGDTODO: Math is hard ;( + return xy + transform.__doc__ = Transform.transform.__doc__ + + def inverted(self): + return MolleweideAxes.MolleweideTransform(self._resolution) + inverted.__doc__ = Transform.inverted.__doc__ + + def __init__(self, *args, **kwargs): + self._longitude_cap = npy.pi / 2.0 + GeoAxes.__init__(self, *args, **kwargs) + self.set_aspect(0.5, adjustable='box', anchor='C') + self.cla() + + def _get_core_transform(self, resolution): + return self.MolleweideTransform(resolution) + + +class LambertAxes(GeoAxes): + name = 'lambert' + + class LambertTransform(Transform): + """ + The base Lambert transform. + """ + input_dims = 2 + output_dims = 2 + is_separable = False + + def __init__(self, center_longitude, center_latitude, resolution): + """ + Create a new Lambert transform. Resolution is the number of steps + to interpolate between each input line segment to approximate its + path in curved Lambert space. + """ + Transform.__init__(self) + self._resolution = resolution + self._center_longitude = center_longitude + self._center_latitude = center_latitude + + def transform(self, ll): + longitude = ll[:, 0:1] + latitude = ll[:, 1:2] + clong = self._center_longitude + clat = self._center_latitude + cos_lat = npy.cos(latitude) + sin_lat = npy.sin(latitude) + diff_long = longitude - clong + cos_diff_long = npy.cos(diff_long) + + inner_k = (1.0 + + npy.sin(clat)*sin_lat + + npy.cos(clat)*cos_lat*cos_diff_long) + # Prevent divide-by-zero problems + inner_k = npy.where(inner_k == 0.0, 1e-15, inner_k) + k = npy.sqrt(2.0 / inner_k) + x = k*cos_lat*npy.sin(diff_long) + y = k*(npy.cos(clat)*sin_lat - + npy.sin(clat)*cos_lat*cos_diff_long) + + return npy.concatenate((x, y), 1) + transform.__doc__ = Transform.transform.__doc__ + + transform_non_affine = transform + transform_non_affine.__doc__ = Transform.transform_non_affine.__doc__ + + def transform_path(self, path): + vertices = path.vertices + ipath = path.interpolated(self._resolution) + return Path(self.transform(ipath.vertices), ipath.codes) + transform_path.__doc__ = Transform.transform_path.__doc__ + + transform_path_non_affine = transform_path + transform_path_non_affine.__doc__ = Transform.transform_path_non_affine.__doc__ + + def inverted(self): + return LambertAxes.InvertedLambertTransform( + self._center_longitude, + self._center_latitude, + self._resolution) + inverted.__doc__ = Transform.inverted.__doc__ + + class InvertedLambertTransform(Transform): + input_dims = 2 + output_dims = 2 + is_separable = False + + def __init__(self, center_longitude, center_latitude, resolution): + Transform.__init__(self) + self._resolution = resolution + self._center_longitude = center_longitude + self._center_latitude = center_latitude + + def transform(self, xy): + x = xy[:, 0:1] + y = xy[:, 1:2] + clong = self._center_longitude + clat = self._center_latitude + p = npy.sqrt(x*x + y*y) + p = npy.where(p == 0.0, 1e-9, p) + c = 2.0 * npy.arcsin(0.5 * p) + sin_c = npy.sin(c) + cos_c = npy.cos(c) + + lat = npy.arcsin(cos_c*npy.sin(clat) + + ((y*sin_c*npy.cos(clat)) / p)) + long = clong + npy.arctan( + (x*sin_c) / (p*npy.cos(clat)*cos_c - y*npy.sin(clat)*sin_c)) + + return npy.concatenate((long, lat), 1) + transform.__doc__ = Transform.transform.__doc__ + + def inverted(self): + return LambertAxes.LambertTransform( + self._center_longitude, + self._center_latitude, + self._resolution) + inverted.__doc__ = Transform.inverted.__doc__ + + def __init__(self, *args, **kwargs): + self._longitude_cap = npy.pi / 2.0 + self._center_longitude = kwargs.pop("center_longitude", 0.0) + self._center_latitude = kwargs.pop("center_latitude", 0.0) + GeoAxes.__init__(self, *args, **kwargs) + self.set_aspect('equal', adjustable='box', anchor='C') + self.cla() + + def cla(self): + GeoAxes.cla(self) + self.yaxis.set_major_formatter(NullFormatter()) + + def _get_core_transform(self, resolution): + return self.LambertTransform( + self._center_longitude, + self._center_latitude, + resolution) + + def _get_affine_transform(self): + return Affine2D() \ + .scale(0.25) \ + .translate(0.5, 0.5) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2007-12-21 19:41:07
|
Revision: 4787 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4787&view=rev Author: mdboom Date: 2007-12-21 11:40:59 -0800 (Fri, 21 Dec 2007) Log Message: ----------- Merged revisions 4773-4785 via svnmerge from http://matplotlib.svn.sf.net/svnroot/matplotlib/trunk/matplotlib ........ r4783 | mdboom | 2007-12-21 10:08:38 -0500 (Fri, 21 Dec 2007) | 2 lines Add size-dependent highly-accurate arc drawing. ........ r4785 | jdh2358 | 2007-12-21 11:22:42 -0500 (Fri, 21 Dec 2007) | 2 lines added unit support to arc ........ Modified Paths: -------------- branches/transforms/API_CHANGES branches/transforms/CODING_GUIDE branches/transforms/examples/units/ellipse_with_units.py branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/legend.py branches/transforms/lib/matplotlib/mlab.py branches/transforms/lib/matplotlib/patches.py branches/transforms/unit/ellipse_large.py Property Changed: ---------------- branches/transforms/ Property changes on: branches/transforms ___________________________________________________________________ Name: svnmerge-integrated - /trunk/matplotlib:1-4772 + /trunk/matplotlib:1-4785 Modified: branches/transforms/API_CHANGES =================================================================== --- branches/transforms/API_CHANGES 2007-12-21 18:53:29 UTC (rev 4786) +++ branches/transforms/API_CHANGES 2007-12-21 19:40:59 UTC (rev 4787) @@ -169,6 +169,9 @@ END OF TRANSFORMS REFACTORING + For csv2rec, checkrows=0 is the new default indicating all rows + will be checked for type inference + A warning is issued when an image is drawn on log-scaled axes, since it will not log-scale the image data. Modified: branches/transforms/CODING_GUIDE =================================================================== --- branches/transforms/CODING_GUIDE 2007-12-21 18:53:29 UTC (rev 4786) +++ branches/transforms/CODING_GUIDE 2007-12-21 19:40:59 UTC (rev 4787) @@ -12,6 +12,9 @@ # checking out the main src svn co https://matplotlib.svn.sourceforge.net/svnroot/matplotlib/trunk/matplotlib matplotlib --username=youruser --password=yourpass +# branch checkouts, eg the transforms branch +svn co https://matplotlib.svn.sourceforge.net/svnroot/matplotlib/branches/transforms transbranch + == Committing changes == When committing changes to matplotlib, there are a few things to bear @@ -27,12 +30,6 @@ support 2.3, so avoid 2.4 only features like decorators until we remove 2.3 support - * Are your changes Numeric, numarray and numpy compatible? Try - running simple_plot.py or image_demo.py with --Numeric, --numarray - and --numpy (Note, someone should add examples to - backend_driver.py which explicitly require numpy, numarray and - Numeric so we can automatically catch these) - * Can you pass examples/backend_driver.py? This is our poor man's unit test. @@ -49,9 +46,8 @@ For numpy, use: import numpy as npy - ... a = npy.array([1,2,3]) - ... + For masked arrays, use: import matplotlib.numerix.npyma as ma @@ -64,16 +60,20 @@ For matplotlib main module, use: import matplotlib as mpl - ... mpl.rcParams['xtick.major.pad'] = 6 -For matplotlib modules, use: +For matplotlib modules (or any other modules), use: - import matplotlib.cbook as cbook as mpl_cbook - ... - if mpl_cbook.iterable(z): - ... + import matplotlib.cbook as cbook + + if cbook.iterable(z): + pass + We prefer this over the equivalent 'from matplotlib import cbook' + because the latter is ambiguous whether cbook is a module or a + function to the new developer. The former makes it explcit that + you are importing a module or package. + == Naming, spacing, and formatting conventions == In general, we want to hew as closely as possible to the standard @@ -114,15 +114,6 @@ python, C and C++ -When importing modules from the matplotlib namespace - - import matplotlib.cbook as cbook # DO - from matplotlib import cbook #DONT - -because the latter is ambiguous whether cbook is a module or a -function to the new developer. The former makes it explcit that you -are importing a module or package. - ; and similarly for c++-mode-hook and c-mode-hook (add-hook 'python-mode-hook (lambda () Modified: branches/transforms/examples/units/ellipse_with_units.py =================================================================== --- branches/transforms/examples/units/ellipse_with_units.py 2007-12-21 18:53:29 UTC (rev 4786) +++ branches/transforms/examples/units/ellipse_with_units.py 2007-12-21 19:40:59 UTC (rev 4787) @@ -1,5 +1,5 @@ """ -Compare the ellipse generated with arcs versus a polygonal approximation +Compare the ellipse generated with arcs versus a polygonal approximation """ from basic_units import cm import numpy as npy @@ -46,4 +46,24 @@ #fig.savefig('ellipse_compare.png') fig.savefig('ellipse_compare') +fig = figure() +ax = fig.add_subplot(211, aspect='auto') +ax.fill(x, y, alpha=0.2, facecolor='yellow', edgecolor='yellow', linewidth=1, zorder=1) + +e1 = patches.Arc((xcenter, ycenter), width, height, + angle=angle, linewidth=2, fill=False, zorder=2) + +ax.add_patch(e1) + +ax = fig.add_subplot(212, aspect='equal') +ax.fill(x, y, alpha=0.2, facecolor='green', edgecolor='green', zorder=1) +e2 = patches.Arc((xcenter, ycenter), width, height, + angle=angle, linewidth=2, fill=False, zorder=2) + + +ax.add_patch(e2) + +#fig.savefig('arc_compare.png') +fig.savefig('arc_compare') + show() Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-12-21 18:53:29 UTC (rev 4786) +++ branches/transforms/lib/matplotlib/axes.py 2007-12-21 19:40:59 UTC (rev 4787) @@ -321,6 +321,8 @@ ret.append(seg) def makefill(x, y): + x = self.axes.convert_xunits(x) + y = self.axes.convert_yunits(y) facecolor = self._get_next_cycle_color() seg = mpatches.Polygon(zip(x, y), facecolor = facecolor, @@ -363,6 +365,8 @@ def makefill(x, y): facecolor = color + x = self.axes.convert_xunits(x) + y = self.axes.convert_yunits(y) seg = mpatches.Polygon(zip(x, y), facecolor = facecolor, fill=True, Modified: branches/transforms/lib/matplotlib/legend.py =================================================================== --- branches/transforms/lib/matplotlib/legend.py 2007-12-21 18:53:29 UTC (rev 4786) +++ branches/transforms/lib/matplotlib/legend.py 2007-12-21 19:40:59 UTC (rev 4787) @@ -140,7 +140,7 @@ self._offsetTransform = Affine2D() self._parentTransform = BboxTransformTo(parent.bbox) Artist.set_transform(self, self._offsetTransform + self._parentTransform) - + if loc is None: loc = rcParams["legend.loc"] if not self.isaxes and loc in [0,'best']: @@ -226,7 +226,7 @@ bboxesAll = bboxesText bboxesAll.extend(bboxesHandles) bbox = Bbox.union(bboxesAll) - + self.save = bbox ibox = bbox.inverse_transformed(self.get_transform()) @@ -328,7 +328,7 @@ if isinstance(handle, Rectangle): transform = handle.get_data_transform() + inverse_transform - bboxes.append(handle._bbox.transformed(transform)) + bboxes.append(handle.get_bbox().transformed(transform)) else: transform = handle.get_transform() + inverse_transform bboxes.append(handle.get_path().get_extents(transform)) @@ -499,7 +499,7 @@ bbox = bbox.expanded(1 + self.pad, 1 + self.pad) l, b, w, h = bbox.bounds self.legendPatch.set_bounds(l, b, w, h) - + ox, oy = 0, 0 # center if iterable(self._loc) and len(self._loc)==2: Modified: branches/transforms/lib/matplotlib/mlab.py =================================================================== --- branches/transforms/lib/matplotlib/mlab.py 2007-12-21 18:53:29 UTC (rev 4786) +++ branches/transforms/lib/matplotlib/mlab.py 2007-12-21 19:40:59 UTC (rev 4787) @@ -2045,7 +2045,7 @@ return newrec.view(npy.recarray) -def csv2rec(fname, comments='#', skiprows=0, checkrows=5, delimiter=',', +def csv2rec(fname, comments='#', skiprows=0, checkrows=0, delimiter=',', converterd=None, names=None, missing=None): """ Load data from comma/space/tab delimited file in fname into a @@ -2075,7 +2075,7 @@ names, if not None, is a list of header names. In this case, no header will be read from the file - if no rows are found, None is returned See examples/loadrec.py + if no rows are found, None is returned -- see examples/loadrec.py """ if converterd is None: Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2007-12-21 18:53:29 UTC (rev 4786) +++ branches/transforms/lib/matplotlib/patches.py 2007-12-21 19:40:59 UTC (rev 4787) @@ -366,10 +366,11 @@ Patch.__init__(self, **kwargs) - left, right = self.convert_xunits((xy[0], xy[0] + width)) - bottom, top = self.convert_yunits((xy[1], xy[1] + height)) - self._bbox = transforms.Bbox.from_extents(left, bottom, right, top) - self._rect_transform = transforms.BboxTransformTo(self._bbox) + self._x = xy[0] + self._y = xy[1] + self._width = width + self._height = height + self._rect_transform = transforms.IdentityTransform() __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd def get_path(self): @@ -378,6 +379,19 @@ """ return Path.unit_rectangle() + def _update_patch_transform(self): + x = self.convert_xunits(self._x) + y = self.convert_yunits(self._y) + width = self.convert_xunits(self._width) + height = self.convert_yunits(self._height) + bbox = transforms.Bbox.from_bounds(x, y, width, height) + self._rect_transform = transforms.BboxTransformTo(bbox) + self._combined_transform = self._rect_transform + artist.Artist.get_transform(self) + + def draw(self, renderer): + self._update_patch_transform() + Patch.draw(self, renderer) + def get_patch_transform(self): return self._rect_transform @@ -388,19 +402,19 @@ def get_x(self): "Return the left coord of the rectangle" - return self._bbox.x0 + return self._x def get_y(self): "Return the bottom coord of the rectangle" - return self._bbox.y0 + return self._y def get_width(self): "Return the width of the rectangle" - return self._bbox.width + return self._width def get_height(self): "Return the height of the rectangle" - return self._bbox.height + return self._height def set_x(self, x): """ @@ -408,9 +422,7 @@ ACCEPTS: float """ - w = self._bbox.width - x = self.convert_xunits(x) - self._bbox.intervalx = (x, x + w) + self._x = x def set_y(self, y): """ @@ -418,9 +430,7 @@ ACCEPTS: float """ - h = self._bbox.height - y = self.convert_yunits(y) - self._bbox.intervaly = (y, y + h) + self._y = y def set_width(self, w): """ @@ -428,8 +438,7 @@ ACCEPTS: float """ - w = self.convert_xunits(w) - self._bbox.x1 = self._bbox.x0 + w + self._width = w def set_height(self, h): """ @@ -437,8 +446,7 @@ ACCEPTS: float """ - h = self.convert_yunits(h) - self._bbox.y1 = self._bbox.y0 + h + self._height = h def set_bounds(self, *args): """ @@ -450,10 +458,13 @@ l,b,w,h = args[0] else: l,b,w,h = args - l, w = self.convert_xunits((l, w)) - b, h = self.convert_yunits((b, h)) - self._bbox.bounds = l,b,w,h + self._x = l + self._y = b + self._width = w + self._height = h + def get_bbox(self): + return transforms.Bbox.from_bounds(self._x, self._y, self._width, self._height) class RegularPolygon(Patch): """ @@ -494,7 +505,6 @@ def _get_xy(self): return self._xy def _set_xy(self, xy): - self._xy = xy self._update_transform() xy = property(_get_xy, _set_xy) @@ -588,9 +598,23 @@ """ Patch.__init__(self, **kwargs) - self._path = Path.wedge(theta1, theta2) + self.center = center + self.r = r + self.theta1 = theta1 + self.theta2 = theta2 + self._patch_transform = transforms.IdentityTransform() + self._path = Path.wedge(self.theta1, self.theta2) + + def draw(self, renderer): + x = self.convert_xunits(self.center[0]) + y = self.convert_yunits(self.center[1]) + rx = self.convert_xunits(self.r) + ry = self.convert_yunits(self.r) self._patch_transform = transforms.Affine2D() \ - .scale(r).translate(*center) + .scale(rx, ry).translate(x, y) + self._combined_transform = self._patch_transform + \ + artist.Artist.get_transform(self) + Patch.draw(self, renderer) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd def get_path(self): @@ -806,8 +830,6 @@ %(Patch)s """ - self.center = xy - self.radius = radius RegularPolygon.__init__(self, xy, resolution, radius, @@ -821,7 +843,7 @@ A scale-free ellipse """ def __str__(self): - return "Ellipse(%d,%d;%dx%d)"%(self.center[0],self.center[1],self.width,self.height) + return "Ellipse(%s,%s;%sx%s)"%(self.center[0],self.center[1],self.width,self.height) def __init__(self, xy, width, height, angle=0.0, **kwargs): """ @@ -835,18 +857,28 @@ """ Patch.__init__(self, **kwargs) - self._center = xy - self._width, self._height = width, height - self._angle = angle - self._recompute_transform() + self.center = xy + self.width, self.height = width, height + self.angle = angle self._path = Path.unit_circle() + self._patch_transform = transforms.IdentityTransform() def _recompute_transform(self): + center = (self.convert_xunits(self.center[0]), + self.convert_yunits(self.center[1])) + width = self.convert_xunits(self.width) + height = self.convert_yunits(self.height) self._patch_transform = transforms.Affine2D() \ - .scale(self._width * 0.5, self._height * 0.5) \ - .rotate_deg(self._angle) \ - .translate(*self._center) + .scale(width * 0.5, height * 0.5) \ + .rotate_deg(self.angle) \ + .translate(*center) + self._combined_transform = self._patch_transform + \ + artist.Artist.get_transform(self) + def draw(self, renderer): + self._recompute_transform() + Patch.draw(self, renderer) + def get_path(self): """ Return the vertices of the rectangle @@ -861,27 +893,7 @@ x, y = self.get_transform().inverted().transform_point((ev.x, ev.y)) return (x*x + y*y) <= 1.0, {} - def _get_center(self): - return self._center - def _set_center(self, center): - self._center = center - self._recompute_transform() - center = property(_get_center, _set_center) - def _get_xy(self): - return self._xy - def _set_xy(self, xy): - self._xy = xy - self._recompute_transform() - xy = property(_get_xy, _set_xy) - - def _get_angle(self): - return self._angle - def _set_angle(self, angle): - self._angle = angle - self._recompute_transform() - angle = property(_get_angle, _set_angle) - class Circle(Ellipse): """ A circle patch @@ -912,9 +924,14 @@ """ An elliptical arc. Because it performs various optimizations, it can not be filled. + + The arc must be used in an Axes instance it cannot be added + directly to a Figure) because it is optimized to only render the + segments that are inside the axes bounding box with high + resolution. """ def __str__(self): - return "Arc(%d,%d;%dx%d)"%(self.center[0],self.center[1],self.width,self.height) + return "Arc(%s,%s;%sx%s)"%(self.center[0],self.center[1],self.width,self.height) def __init__(self, xy, width, height, angle=0.0, theta1=0.0, theta2=360.0, **kwargs): """ @@ -938,8 +955,8 @@ Ellipse.__init__(self, xy, width, height, angle, **kwargs) - self._theta1 = theta1 - self._theta2 = theta2 + self.theta1 = theta1 + self.theta2 = theta2 def draw(self, renderer): """ @@ -984,13 +1001,20 @@ pairs of vertices are drawn using the bezier arc approximation technique implemented in Path.arc(). """ + if not hasattr(self, 'axes'): + raise RuntimeError('Arcs can only be used in Axes instances') + + self._recompute_transform() + # Get the width and height in pixels + width = self.convert_xunits(self.width) + height = self.convert_yunits(self.height) width, height = self.get_transform().transform_point( - (self._width, self._height)) - inv_error = (1.0 / 1.89818e-6) + (width, height)) + inv_error = (1.0 / 1.89818e-6) * 0.5 if width < inv_error and height < inv_error: - self._path = Path.arc(self._theta1, self._theta2) + self._path = Path.arc(self.theta1, self.theta2) return Patch.draw(self, renderer) def iter_circle_intersect_on_line(x0, y0, x1, y1): @@ -1037,7 +1061,6 @@ if x >= x0e and x <= x1e and y >= y0e and y <= y1e: yield x, y - # Transforms the axes box_path so that it is relative to the unit # circle in the same way that it is relative to the desired # ellipse. @@ -1050,8 +1073,8 @@ TWOPI = PI * 2.0 RAD2DEG = 180.0 / PI DEG2RAD = PI / 180.0 - theta1 = self._theta1 - theta2 = self._theta2 + theta1 = self.theta1 + theta2 = self.theta2 thetas = {} # For each of the point pairs, there is a line segment for p0, p1 in zip(box_path.vertices[:-1], box_path.vertices[1:]): Modified: branches/transforms/unit/ellipse_large.py =================================================================== --- branches/transforms/unit/ellipse_large.py 2007-12-21 18:53:29 UTC (rev 4786) +++ branches/transforms/unit/ellipse_large.py 2007-12-21 19:40:59 UTC (rev 4787) @@ -6,7 +6,7 @@ import math from pylab import * -from matplotlib.patches import Arc +from matplotlib.patches import Ellipse, Arc # given a point x, y x = 2692.440 @@ -47,39 +47,75 @@ ellipseLine = ax.plot( xs, ys, **kwargs ) + + ################################################## # make the axes -ax = subplot( 211, aspect='equal' ) -ax.set_aspect( 'equal', 'datalim' ) +ax1 = subplot( 311, aspect='equal' ) +ax1.set_aspect( 'equal', 'datalim' ) # make the lower-bound ellipse diam = (r - delta) * 2.0 -lower_ellipse = Arc( (0.0, 0.0), diam, diam, 0.0, fill=False, edgecolor="darkgreen" ) -ax.add_patch( lower_ellipse ) +lower_ellipse = Ellipse( (0.0, 0.0), diam, diam, 0.0, fill=False, edgecolor="darkgreen" ) +ax1.add_patch( lower_ellipse ) # make the target ellipse diam = r * 2.0 -target_ellipse = Arc( (0.0, 0.0), diam, diam, 0.0, fill=False, edgecolor="darkred" ) -ax.add_patch( target_ellipse ) +target_ellipse = Ellipse( (0.0, 0.0), diam, diam, 0.0, fill=False, edgecolor="darkred" ) +ax1.add_patch( target_ellipse ) # make the upper-bound ellipse diam = (r + delta) * 2.0 -upper_ellipse = Arc( (0.0, 0.0), diam, diam, 0.0, fill=False, edgecolor="darkblue" ) -ax.add_patch( upper_ellipse ) +upper_ellipse = Ellipse( (0.0, 0.0), diam, diam, 0.0, fill=False, edgecolor="darkblue" ) +ax1.add_patch( upper_ellipse ) # make the target diam = delta * 2.0 +target = Ellipse( (x, y), diam, diam, 0.0, fill=False, edgecolor="#DD1208" ) +ax1.add_patch( target ) + +# give it a big marker +ax1.plot( [x], [y], marker='x', linestyle='None', mfc='red', mec='red', markersize=10 ) + +################################################## +# make the axes +ax = subplot( 312, aspect='equal' , sharex=ax1, sharey=ax1) +ax.set_aspect( 'equal', 'datalim' ) + +# make the lower-bound arc +diam = (r - delta) * 2.0 +lower_arc = Arc( (0.0, 0.0), diam, diam, 0.0, fill=False, edgecolor="darkgreen" ) +ax.add_patch( lower_arc ) + +# make the target arc +diam = r * 2.0 +target_arc = Arc( (0.0, 0.0), diam, diam, 0.0, fill=False, edgecolor="darkred" ) +ax.add_patch( target_arc ) + +# make the upper-bound arc +diam = (r + delta) * 2.0 +upper_arc = Arc( (0.0, 0.0), diam, diam, 0.0, fill=False, edgecolor="darkblue" ) +ax.add_patch( upper_arc ) + +# make the target +diam = delta * 2.0 target = Arc( (x, y), diam, diam, 0.0, fill=False, edgecolor="#DD1208" ) ax.add_patch( target ) # give it a big marker ax.plot( [x], [y], marker='x', linestyle='None', mfc='red', mec='red', markersize=10 ) + + + + ################################################## # now lets do the same thing again using a custom ellipse function + + # make the axes -ax = subplot( 212, aspect='equal', sharex=ax, sharey=ax ) +ax = subplot( 313, aspect='equal', sharex=ax1, sharey=ax1 ) ax.set_aspect( 'equal', 'datalim' ) # make the lower-bound ellipse @@ -97,11 +133,17 @@ # give it a big marker ax.plot( [x], [y], marker='x', linestyle='None', mfc='red', mec='red', markersize=10 ) + +# give it a big marker +ax.plot( [x], [y], marker='x', linestyle='None', mfc='red', mec='red', markersize=10 ) + ################################################## # lets zoom in to see the area of interest -ax.set_xlim(2650, 2735) -ax.set_ylim(6705, 6735) +ax1.set_xlim(2650, 2735) +ax1.set_ylim(6705, 6735) + +savefig("ellipse") show() -# savefig("ellipse") + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-01-04 15:00:09
|
Revision: 4801 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4801&view=rev Author: mdboom Date: 2008-01-04 06:59:50 -0800 (Fri, 04 Jan 2008) Log Message: ----------- Merged revisions 4786-4800 via svnmerge from http://matplotlib.svn.sf.net/svnroot/matplotlib/trunk/matplotlib ........ r4788 | efiring | 2007-12-26 02:23:27 -0500 (Wed, 26 Dec 2007) | 7 lines Make numerix.ma and numerix.npyma work with numpy 1.05 The numpy maskedarray branch is scheduled to become the trunk for 1.05. It includes a change from ma.py being in numpy/core to ma being a module under numpy, so the import syntax is different in numerix.ma and numerix.npyma. ........ r4789 | efiring | 2007-12-26 02:51:19 -0500 (Wed, 26 Dec 2007) | 2 lines Fix bug in errorbar, reported by Noriko Minakawa ........ r4790 | efiring | 2007-12-26 12:10:34 -0500 (Wed, 26 Dec 2007) | 2 lines Warning instead of exception if matplotlib.use() is called too late. ........ Modified Paths: -------------- branches/transforms/CHANGELOG branches/transforms/lib/matplotlib/__init__.py branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/numerix/ma/__init__.py branches/transforms/lib/matplotlib/numerix/npyma/__init__.py Property Changed: ---------------- branches/transforms/ Property changes on: branches/transforms ___________________________________________________________________ Name: svnmerge-integrated - /trunk/matplotlib:1-4785 + /trunk/matplotlib:1-4800 Modified: branches/transforms/CHANGELOG =================================================================== --- branches/transforms/CHANGELOG 2008-01-01 15:13:48 UTC (rev 4800) +++ branches/transforms/CHANGELOG 2008-01-04 14:59:50 UTC (rev 4801) @@ -1,3 +1,12 @@ +2007-12-26 Reduce too-late use of matplotlib.use() to a warning + instead of an exception, for backwards compatibility - EF + +2007-12-25 Fix bug in errorbar, identified by Noriko Minakawa - EF + +2007-12-25 Changed masked array importing to work with the upcoming + numpy 1.05 (now the maskedarray branch) as well as with + earlier versions. - EF + 2007-12-16 rec2csv saves doubles without losing precision. Also, it does not close filehandles passed in open. - JDH,ADS Modified: branches/transforms/lib/matplotlib/__init__.py =================================================================== --- branches/transforms/lib/matplotlib/__init__.py 2008-01-01 15:13:48 UTC (rev 4800) +++ branches/transforms/lib/matplotlib/__init__.py 2008-01-04 14:59:50 UTC (rev 4801) @@ -727,8 +727,11 @@ except: from config import rcParams, rcdefaults -_use_error_msg = """ matplotlib.use() must be called *before* pylab -or matplotlib.backends is imported for the first time.""" +_use_error_msg = """ This call to matplotlib.use() has no effect +because the the backend has already been chosen; +matplotlib.use() must be called *before* pylab, matplotlib.pyplot, +or matplotlib.backends is imported for the first time. +""" def use(arg): """ @@ -747,7 +750,7 @@ be called before importing matplotlib.backends. """ if 'matplotlib.backends' in sys.modules: - raise RuntimeError(_use_error_msg) + warnings.warn(_use_error_msg) be_parts = arg.split('.') name = validate_backend(be_parts[0]) rcParams['backend'] = name Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2008-01-01 15:13:48 UTC (rev 4800) +++ branches/transforms/lib/matplotlib/axes.py 2008-01-04 14:59:50 UTC (rev 4801) @@ -3779,6 +3779,8 @@ lines_kw['linewidth']=kwargs['linewidth'] if 'lw' in kwargs: lines_kw['lw']=kwargs['lw'] + if 'transform' in kwargs: + lines_kw['transform'] = kwargs['transform'] # arrays fine here, they are booleans and hence not units if not iterable(lolims): @@ -3814,6 +3816,8 @@ plot_kw['markeredgewidth']=kwargs['markeredgewidth'] if 'mew' in kwargs: plot_kw['mew']=kwargs['mew'] + if 'transform' in kwargs: + plot_kw['transform'] = kwargs['transform'] if xerr is not None: if iterable(xerr) and len(xerr)==2 and iterable(xerr[0]) and iterable(xerr[1]): Modified: branches/transforms/lib/matplotlib/numerix/ma/__init__.py =================================================================== --- branches/transforms/lib/matplotlib/numerix/ma/__init__.py 2008-01-01 15:13:48 UTC (rev 4800) +++ branches/transforms/lib/matplotlib/numerix/ma/__init__.py 2008-01-04 14:59:50 UTC (rev 4801) @@ -13,7 +13,10 @@ from maskedarray import * print "using maskedarray" else: - from numpy.core.ma import * + try: + from numpy.ma import * # numpy 1.05 and later + except ImportError: + from numpy.core.ma import * # earlier #print "using ma" def getmaskorNone(obj): _msk = getmask(obj) Modified: branches/transforms/lib/matplotlib/numerix/npyma/__init__.py =================================================================== --- branches/transforms/lib/matplotlib/numerix/npyma/__init__.py 2008-01-01 15:13:48 UTC (rev 4800) +++ branches/transforms/lib/matplotlib/numerix/npyma/__init__.py 2008-01-04 14:59:50 UTC (rev 4801) @@ -4,5 +4,8 @@ from maskedarray import * print "using maskedarray" else: - from numpy.core.ma import * + try: + from numpy.ma import * # numpy 1.05 and later + except ImportError: + from numpy.core.ma import * # earlier #print "using ma" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-01-07 21:16:07
|
Revision: 4803 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4803&view=rev Author: mdboom Date: 2008-01-07 13:15:58 -0800 (Mon, 07 Jan 2008) Log Message: ----------- Provide heavily-documented examples for adding new scales and projections. Fix various bugs related to non-rectangular clipping. Remove MercatorLatitude scale from core and put it in an example. Modified Paths: -------------- branches/transforms/doc/devel/add_new_projection.rst branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/patches.py branches/transforms/lib/matplotlib/projections/__init__.py branches/transforms/lib/matplotlib/projections/geo.py branches/transforms/lib/matplotlib/projections/polar.py branches/transforms/lib/matplotlib/scale.py Added Paths: ----------- branches/transforms/examples/custom_projection_example.py branches/transforms/examples/custom_scale_example.py Modified: branches/transforms/doc/devel/add_new_projection.rst =================================================================== --- branches/transforms/doc/devel/add_new_projection.rst 2008-01-06 18:28:17 UTC (rev 4802) +++ branches/transforms/doc/devel/add_new_projection.rst 2008-01-07 21:15:58 UTC (rev 4803) @@ -4,190 +4,99 @@ .. ::author Michael Droettboom -Matplotlib supports the addition of new transformations that transform -the data before it is displayed. In ``matplotlib`` nomenclature, -separable transformations, working on a single dimension, are called -"scales", and non-separable transformations, that take handle data in -two or more dimensions at a time, are called "projections". +Matplotlib supports the addition of custom procedures that transform +the data before it is displayed. +There is an important distinction between two kinds of +transformations. Separable transformations, working on a single +dimension, are called "scales", and non-separable transformations, +that handle data in two or more dimensions at a time, are called +"projections". + From the user's perspective, the scale of a plot can be set with -``set_xscale`` and ``set_yscale``. Choosing the projection -currently has no *standardized* method. [MGDTODO] +``set_xscale()`` and ``set_yscale()``. Projections can be chosen using +the ``projection`` keyword argument to the ``plot()`` or ``subplot()`` +functions:: + plot(x, y, projection="custom") + This document is intended for developers and advanced users who need -to add more scales and projections to matplotlib. +to create new scales and projections for matplotlib. The necessary +code for scales and projections can be included anywhere: directly +within a plot script, in third-party code, or in the matplotlib source +tree itself. Creating a new scale ==================== -Adding a new scale consists of defining a subclass of ``ScaleBase``, -that brings together the following elements: +Adding a new scale consists of defining a subclass of ``ScaleBase`` +(in the ``matplotlib.scale`` module), that includes the following +elements: - - A transformation from data space into plot space. + - A transformation from data coordinates into display coordinates. - - An inverse of that transformation. For example, this is used to - convert mouse positions back into data space. + - An inverse of that transformation. This is used, for example, to + convert mouse positions from screen space back into data space. - - A function to limit the range of the axis to acceptable values. A - log scale, for instance, would prevent the range from including - values less than or equal to zero. + - A function to limit the range of the axis to acceptable values + (``limit_range_for_scale()``). A log scale, for instance, would + prevent the range from including values less than or equal to + zero. - Locators (major and minor) that determine where to place ticks in the plot, and optionally, how to adjust the limits of the plot to - some "good" values. + some "good" values. Unlike ``limit_range_for_scale()``, which is + always enforced, the range setting here is only used when + automatically setting the range of the plot. - Formatters (major and minor) that specify how the tick labels should be drawn. -There are a number of ``Scale`` classes in ``scale.py`` that may be -used as starting points for new scales. As an example, this document -presents adding a new scale ``MercatorLatitudeScale`` which can be -used to plot latitudes in a Mercator_ projection. For simplicity, -this scale assumes that it has a fixed center at the equator. The -code presented here is a simplification of actual code in -``matplotlib``, with complications added only for the sake of -optimization removed. +Once the class is defined, it must be registered with ``matplotlib`` +so that the user can select it. -First define a new subclass of ``ScaleBase``:: +A full-fledged and heavily annotated example is in +``examples/custom_scale_example.py``. There are also some ``Scale`` +classes in ``scale.py`` that may be used as starting points. - class MercatorLatitudeScale(ScaleBase): - """ - Scales data in range -pi/2 to pi/2 (-90 to 90 degrees) using - the system used to scale latitudes in a Mercator projection. - The scale function: - ln(tan(y) + sec(y)) +Creating a new projection +========================= - The inverse scale function: - atan(sinh(y)) +Adding a new projection consists of defining a subclass of ``Axes`` +(in the ``matplotlib.axes`` module), that includes the following +elements: - Since the Mercator scale tends to infinity at +/- 90 degrees, - there is user-defined threshold, above and below which nothing - will be plotted. This defaults to +/- 85 degrees. + - A transformation from data coordinates into display coordinates. - source: - http://en.wikipedia.org/wiki/Mercator_projection - """ - name = 'mercator_latitude' + - An inverse of that transformation. This is used, for example, to + convert mouse positions from screen space back into data space. -This class must have a member ``name`` that defines the string used to -select the scale. For example, -``gca().set_yscale("mercator_latitude")`` would be used to select the -Mercator latitude scale. + - Transformations for the gridlines, ticks and ticklabels. Custom + projections will often need to place these elements in special + locations, and ``matplotlib`` has a facility to help with doing so. -Next define two nested classes: one for the data transformation and -one for its inverse. Both of these classes must be subclasses of -``Transform`` (defined in ``transforms.py``).:: + - Setting up default values (overriding ``cla()``), since the + defaults for a rectilinear axes may not be appropriate. - class MercatorLatitudeTransform(Transform): - input_dims = 1 - output_dims = 1 + - Defining the shape of the axes, for example, an elliptical axes, + that will be used to draw the background of the plot and for + clipping any data elements. -There are two class-members that must be defined. ``input_dims`` and -``output_dims`` specify number of input dimensions and output -dimensions to the transformation. These are used by the -transformation framework to do some error checking and prevent -incompatible transformations from being connected together. When -defining transforms for a scale, which are by definition separable and -only have one dimension, these members should always be 1. + - Defining custom locators and formatters for the projection. For + example, in a geographic projection, it may be more convenient to + display the grid in degrees, even if the data is in radians. -``MercatorLatitudeTransform`` has a simple constructor that takes and -stores the *threshold* for the Mercator projection (to limit its range -to prevent plotting to infinity).:: + - Set up interactive panning and zooming. This is left as an + "advanced" feature left to the reader, but there is an example of + this for polar plots in ``polar.py``. - def __init__(self, thresh): - Transform.__init__(self) - self.thresh = thresh + - Any additional methods for additional convenience or features. -The ``transform`` method is where the real work happens: It takes an N -x 1 ``numpy`` array and returns a transformed copy. Since the range -of the Mercator scale is limited by the user-specified threshold, the -input array must be masked to contain only valid values. -``matplotlib`` will handle masked arrays and remove the out-of-range -data from the plot. Importantly, the transformation should return an -array that is the same shape as the input array, since these values -need to remain synchronized with values in the other dimension.:: +Once the class is defined, it must be registered with ``matplotlib`` +so that the user can select it. - def transform(self, a): - masked = ma.masked_where((a < -self.thresh) | (a > self.thresh), a) - return ma.log(ma.abs(ma.tan(masked) + 1.0 / ma.cos(masked))) - -Lastly for the transformation class, define a method to get the -inverse transformation:: - - def inverted(self): - return MercatorLatitudeScale.InvertedMercatorLatitudeTransform(self.thresh) - -The inverse transformation class follows the same pattern, but -obviously the mathematical operation performed is different:: - - class InvertedMercatorLatitudeTransform(Transform): - input_dims = 1 - output_dims = 1 - - def __init__(self, thresh): - Transform.__init__(self) - self.thresh = thresh - - def transform(self, a): - return npy.arctan(npy.sinh(a)) - - def inverted(self): - return MercatorLatitudeScale.MercatorLatitudeTransform(self.thresh) - -Now we're back to methods for the ``MercatorLatitudeScale`` class. -Any keyword arguments passed to ``set_xscale`` and ``set_yscale`` will -be passed along to the scale's constructor. In the case of -``MercatorLatitudeScale``, the ``thresh`` keyword argument specifies -the degree at which to crop the plot data. The constructor also -creates a local instance of the ``Transform`` class defined above, -which is made available through its ``get_transform`` method:: - - def __init__(self, axis, **kwargs): - thresh = kwargs.pop("thresh", (85 / 180.0) * npy.pi) - if thresh >= npy.pi / 2.0: - raise ValueError("thresh must be less than pi/2") - self.thresh = thresh - self._transform = self.MercatorLatitudeTransform(thresh) - - def get_transform(self): - return self._transform - -The ``limit_range_for_scale`` method must be provided to limit the -bounds of the axis to the domain of the function. In the case of -Mercator, the bounds should be limited to the threshold that was -passed in. Unlike the autoscaling provided by the tick locators, this -range limiting will always be adhered to, whether the axis range is set -manually, determined automatically or changed through panning and -zooming:: - - def limit_range_for_scale(self, vmin, vmax, minpos): - return max(vmin, -self.thresh), min(vmax, self.thresh) - -Lastly, the ``set_default_locators_and_formatters`` method sets up the -locators and formatters to use with the scale. It may be that the new -scale requires new locators and formatters. Doing so is outside the -scope of this document, but there are many examples in ``ticker.py``. -The Mercator example uses a fixed locator from -90 to 90 degrees and a -custom formatter class to put convert the radians to degrees and put a -degree symbol after the value:: - - def set_default_locators_and_formatters(self, axis): - class DegreeFormatter(Formatter): - def __call__(self, x, pos=None): - # \u00b0 : degree symbol - return u"%d\u00b0" % ((x / npy.pi) * 180.0) - - deg2rad = npy.pi / 180.0 - axis.set_major_locator(FixedLocator( - npy.arange(-90, 90, 10) * deg2rad)) - axis.set_major_formatter(DegreeFormatter()) - axis.set_minor_formatter(DegreeFormatter()) - -Now that the Scale class has been defined, it must be registered so -that ``matplotlib`` can find it:: - - register_scale(MercatorLatitudeScale) - -.. _Mercator: http://en.wikipedia.org/wiki/Mercator_projection \ No newline at end of file +A full-fledged and heavily annotated example is in +``examples/custom_projection_example.py``. The polar plot +functionality in ``polar.py`` may also be interest. Added: branches/transforms/examples/custom_projection_example.py =================================================================== --- branches/transforms/examples/custom_projection_example.py (rev 0) +++ branches/transforms/examples/custom_projection_example.py 2008-01-07 21:15:58 UTC (rev 4803) @@ -0,0 +1,473 @@ +from matplotlib.axes import Axes +from matplotlib import cbook +from matplotlib.patches import Circle +from matplotlib.path import Path +from matplotlib.ticker import Formatter, Locator, NullLocator, FixedLocator, NullFormatter +from matplotlib.transforms import Affine2D, Affine2DBase, Bbox, \ + BboxTransformTo, IdentityTransform, Transform, TransformWrapper +from matplotlib.projections import register_projection + +# This example projection class is rather long, but it is designed to +# illustrate many features, not all of which will be used every time. +# It is also common to factor out a lot of these methods into common +# code used by a number of projections with similar characteristics +# (see geo.py). + +class HammerAxes(Axes): + """ + A custom class for the Aitoff-Hammer projection, an equal-area map + projection. + + http://en.wikipedia.org/wiki/Hammer_projection + """ + # The projection must specify a name. This will be used be the + # user to select the projection, i.e. ``subplot(111, + # projection='hammer')``. + name = 'hammer' + + # The number of interpolation steps when converting from straight + # lines to curves. (See ``transform_path``). + RESOLUTION = 75 + + def __init__(self, *args, **kwargs): + Axes.__init__(self, *args, **kwargs) + self.set_aspect(0.5, adjustable='box', anchor='C') + self.cla() + + def cla(self): + """ + Override to set up some reasonable defaults. + """ + # Don't forget to call the base class + Axes.cla(self) + + # Set up a default grid spacing + self.set_longitude_grid(30) + self.set_latitude_grid(15) + self.set_longitude_grid_ends(75) + + # Turn off minor ticking altogether + self.xaxis.set_minor_locator(NullLocator()) + self.yaxis.set_minor_locator(NullLocator()) + + # Do not display ticks -- we only want gridlines and text + self.xaxis.set_ticks_position('none') + self.yaxis.set_ticks_position('none') + + # The limits on this projection are fixed -- they are not to + # be changed by the user. This makes the math in the + # transformation itself easier, and since this is a toy + # example, the easier, the better. + Axes.set_xlim(self, -npy.pi, npy.pi) + Axes.set_ylim(self, -npy.pi / 2.0, npy.pi / 2.0) + + def cla(self): + """ + Initialize the Axes object to reasonable defaults. + """ + Axes.cla(self) + + self.set_longitude_grid(30) + self.set_latitude_grid(15) + self.set_longitude_grid_ends(75) + self.xaxis.set_minor_locator(NullLocator()) + self.yaxis.set_minor_locator(NullLocator()) + self.xaxis.set_ticks_position('none') + self.yaxis.set_ticks_position('none') + + # self.grid(rcParams['axes.grid']) + + Axes.set_xlim(self, -npy.pi, npy.pi) + Axes.set_ylim(self, -npy.pi / 2.0, npy.pi / 2.0) + + def _set_lim_and_transforms(self): + """ + This is called once when the plot is created to set up all the + transforms for the data, text and grids. + """ + # There are three important coordinate spaces going on here: + # + # 1. Data space: The space of the data itself + # + # 2. Axes space: The unit rectangle (0, 0) to (1, 1) + # covering the entire plot area. + # + # 3. Display space: The coordinates of the resulting image, + # often in pixels or dpi/inch. + + # This function makes heavy use of the Transform classes in + # ``lib/matplotlib/transforms.py.`` For more information, see + # the inline documentation there. + + # The goal of the first two transformations is to get from the + # data space (in this case longitude and latitude) to axes + # space. It is separated into a non-affine and affine part so + # that the non-affine part does not have to be recomputed when + # a simple affine change to the figure has been made (such as + # resizing the window or changing the dpi). + + # 1) The core transformation from data space into + # rectilinear space defined in the HammerTransform class. + self.transProjection = self.HammerTransform(self.RESOLUTION) + + # 2) The above has an output range that is not in the unit + # rectangle, so scale and translate it so it fits correctly + # within the axes. The peculiar calculations of xscale and + # yscale are specific to a Aitoff-Hammer projection, so don't + # worry about them too much. + xscale = 2.0 * npy.sqrt(2.0) * npy.sin(0.5 * npy.pi) + yscale = npy.sqrt(2.0) * npy.sin(0.5 * npy.pi) + self.transAffine = Affine2D() \ + .scale(0.5 / xscale, 0.5 / yscale) \ + .translate(0.5, 0.5) + + # 3) This is the transformation from axes space to display + # space. + self.transAxes = BboxTransformTo(self.bbox) + + # Now put these 3 transforms together -- from data all the way + # to display coordinates. Using the '+' operator, these + # transforms will be applied "in order". The transforms are + # automatically simplified, if possible, by the underlying + # transformation framework. + self.transData = \ + self.transProjection + \ + self.transAffine + \ + self.transAxes + + # The main data transformation is set up. Now deal with + # gridlines and tick labels. + + # Longitude gridlines and ticklabels. The input to these + # transforms are in display space in x and axes space in y. + # Therefore, the input values will be in range (-xmin, 0), + # (xmax, 1). The goal of these transforms is to go from that + # space to display space. The tick labels will be offset 4 + # pixels from the equator. + self._xaxis_pretransform = \ + Affine2D() \ + .scale(1.0, npy.pi) \ + .translate(0.0, -npy.pi) + self._xaxis_transform = \ + self._xaxis_pretransform + \ + self.transData + self._xaxis_text1_transform = \ + Affine2D().scale(1.0, 0.0) + \ + self.transData + \ + Affine2D().translate(0.0, 4.0) + self._xaxis_text2_transform = \ + Affine2D().scale(1.0, 0.0) + \ + self.transData + \ + Affine2D().translate(0.0, -4.0) + + # Now set up the transforms for the latitude ticks. The input to + # these transforms are in axes space in x and display space in + # y. Therefore, the input values will be in range (0, -ymin), + # (1, ymax). The goal of these transforms is to go from that + # space to display space. The tick labels will be offset 4 + # pixels from the edge of the axes ellipse. + yaxis_stretch = Affine2D().scale(npy.pi * 2.0, 1.0).translate(-npy.pi, 0.0) + yaxis_space = Affine2D().scale(1.0, 1.1) + self._yaxis_transform = \ + yaxis_stretch + \ + self.transData + yaxis_text_base = \ + yaxis_stretch + \ + self.transProjection + \ + (yaxis_space + \ + self.transAffine + \ + self.transAxes) + self._yaxis_text1_transform = \ + yaxis_text_base + \ + Affine2D().translate(-8.0, 0.0) + self._yaxis_text2_transform = \ + yaxis_text_base + \ + Affine2D().translate(8.0, 0.0) + + def get_xaxis_transform(self): + """ + Override this method to provide a transformation for the + x-axis grid and ticks. + """ + return self._xaxis_transform + + def get_xaxis_text1_transform(self, pixelPad): + """ + Override this method to provide a transformation for the + x-axis tick labels. + + Returns a tuple of the form (transform, valign, halign) + """ + return self._xaxis_text1_transform, 'bottom', 'center' + + def get_xaxis_text2_transform(self, pixelPad): + """ + Override this method to provide a transformation for the + secondary x-axis tick labels. + + Returns a tuple of the form (transform, valign, halign) + """ + return self._xaxis_text2_transform, 'top', 'center' + + def get_yaxis_transform(self): + """ + Override this method to provide a transformation for the + y-axis grid and ticks. + """ + return self._yaxis_transform + + def get_yaxis_text1_transform(self, pixelPad): + """ + Override this method to provide a transformation for the + y-axis tick labels. + + Returns a tuple of the form (transform, valign, halign) + """ + return self._yaxis_text1_transform, 'center', 'right' + + def get_yaxis_text2_transform(self, pixelPad): + """ + Override this method to provide a transformation for the + secondary y-axis tick labels. + + Returns a tuple of the form (transform, valign, halign) + """ + return self._yaxis_text2_transform, 'center', 'left' + + def get_axes_patch(self): + """ + Override this method to define the shape that is used for the + background of the plot. It should be a subclass of Patch. + + In this case, it is a Circle (that may be warped by the axes + transform into an ellipse). Any data and gridlines will be + clipped to this shape. + """ + return Circle((0.5, 0.5), 0.5) + + # Prevent the user from applying scales to one or both of the + # axes. In this particular case, scaling the axes wouldn't make + # sense, so we don't allow it. + def set_xscale(self, *args, **kwargs): + if args[0] != 'linear': + raise NotImplementedError + Axes.set_xscale(self, *args, **kwargs) + + def set_yscale(self, *args, **kwargs): + if args[0] != 'linear': + raise NotImplementedError + Axes.set_yscale(self, *args, **kwargs) + + # Prevent the user from changing the axes limits. In our case, we + # want to display the whole sphere all the time, so we override + # set_xlim and set_ylim to ignore any input. This also applies to + # interactive panning and zooming in the GUI interfaces. + def set_xlim(self, *args, **kwargs): + Axes.set_xlim(self, -npy.pi, npy.pi) + Axes.set_ylim(self, -npy.pi / 2.0, npy.pi / 2.0) + set_ylim = set_xlim + + def format_coord(self, long, lat): + """ + Override this method to change how the values are displayed in + the status bar. + + In this case, we want them to be displayed in degrees N/S/E/W. + """ + long = long * (180.0 / npy.pi) + lat = lat * (180.0 / npy.pi) + if lat >= 0.0: + ns = 'N' + else: + ns = 'S' + if long >= 0.0: + ew = 'E' + else: + ew = 'W' + # \u00b0 : degree symbol + return u'%f\u00b0%s, %f\u00b0%s' % (abs(lat), ns, abs(long), ew) + + class DegreeFormatter(Formatter): + """ + This is a custom formatter that converts the native unit of + radians into (truncated) degrees and adds a degree symbol. + """ + def __init__(self, round_to=1.0): + self._round_to = round_to + + def __call__(self, x, pos=None): + degrees = (x / npy.pi) * 180.0 + degrees = round(degrees / self._round_to) * self._round_to + # \u00b0 : degree symbol + return u"%d\u00b0" % degrees + + def set_longitude_grid(self, degrees): + """ + Set the number of degrees between each longitude grid. + + This is an example method that is specific to this projection + class -- it provides a more convenient interface to set the + ticking than set_xticks would. + """ + # Set up a FixedLocator at each of the points, evenly spaced + # by degrees. + number = (360.0 / degrees) + 1 + self.xaxis.set_major_locator( + FixedLocator( + npy.linspace(-npy.pi, npy.pi, number, True)[1:-1])) + # Set the formatter to display the tick labels in degrees, + # rather than radians. + self.xaxis.set_major_formatter(self.DegreeFormatter(degrees)) + + def set_latitude_grid(self, degrees): + """ + Set the number of degrees between each longitude grid. + + This is an example method that is specific to this projection + class -- it provides a more convenient interface than + set_yticks would. + """ + # Set up a FixedLocator at each of the points, evenly spaced + # by degrees. + number = (180.0 / degrees) + 1 + self.yaxis.set_major_locator( + FixedLocator( + npy.linspace(-npy.pi / 2.0, npy.pi / 2.0, number, True)[1:-1])) + # Set the formatter to display the tick labels in degrees, + # rather than radians. + self.yaxis.set_major_formatter(self.DegreeFormatter(degrees)) + + def set_longitude_grid_ends(self, degrees): + """ + Set the latitude(s) at which to stop drawing the longitude grids. + + Often, in geographic projections, you wouldn't want to draw + longitude gridlines near the poles. This allows the user to + specify the degree at which to stop drawing longitude grids. + + This is an example method that is specific to this projection + class -- it provides an interface to something that has no + analogy in the base Axes class. + """ + longitude_cap = degrees * (npy.pi / 180.0) + # Change the xaxis gridlines transform so that it draws from + # -degrees to degrees, rather than -pi to pi. + self._xaxis_pretransform \ + .clear() \ + .scale(1.0, longitude_cap * 2.0) \ + .translate(0.0, -longitude_cap) + + def get_data_ratio(self): + """ + Return the aspect ratio of the data itself. + + This method should be overridden by any Axes that have a + fixed data ratio. + """ + return 1.0 + + # Interactive panning and zooming is not supported with this projection, + # so we override all of the following methods to disable it. + def can_zoom(self): + """ + Return True if this axes support the zoom box + """ + return False + def start_pan(self, x, y, button): + pass + def end_pan(self): + pass + def drag_pan(self, button, key, x, y): + pass + + # Now, the transforms themselves. + + class HammerTransform(Transform): + """ + The base Hammer transform. + """ + input_dims = 2 + output_dims = 2 + is_separable = False + + def __init__(self, resolution): + """ + Create a new Hammer transform. Resolution is the number of steps + to interpolate between each input line segment to approximate its + path in curved Hammer space. + """ + Transform.__init__(self) + self._resolution = resolution + + def transform(self, ll): + """ + Override the transform method to implement the custom transform. + + The input and output are Nx2 numpy arrays. + """ + longitude = ll[:, 0:1] + latitude = ll[:, 1:2] + + # Pre-compute some values + half_long = longitude / 2.0 + cos_latitude = npy.cos(latitude) + sqrt2 = npy.sqrt(2.0) + + alpha = 1.0 + cos_latitude * npy.cos(half_long) + x = (2.0 * sqrt2) * (cos_latitude * npy.sin(half_long)) / alpha + y = (sqrt2 * npy.sin(latitude)) / alpha + return npy.concatenate((x, y), 1) + + # This is where things get interesting. With this projection, + # straight lines in data space become curves in display space. + # This is done by interpolating new values between the input + # values of the data. Since ``transform`` must not return a + # differently-sized array, any transform that requires + # changing the length of the data array must happen within + # ``transform_path``. + def transform_path(self, path): + vertices = path.vertices + ipath = path.interpolated(self._resolution) + return Path(self.transform(ipath.vertices), ipath.codes) + + def inverted(self): + return HammerAxes.InvertedHammerTransform(self._resolution) + inverted.__doc__ = Transform.inverted.__doc__ + + class InvertedHammerTransform(Transform): + input_dims = 2 + output_dims = 2 + is_separable = False + + def __init__(self, resolution): + Transform.__init__(self) + self._resolution = resolution + + def transform(self, xy): + x = xy[:, 0:1] + y = xy[:, 1:2] + + quarter_x = 0.25 * x + half_y = 0.5 * y + z = npy.sqrt(1.0 - quarter_x*quarter_x - half_y*half_y) + longitude = 2 * npy.arctan((z*x) / (2.0 * (2.0*z*z - 1.0))) + latitude = npy.arcsin(y*z) + return npy.concatenate((longitude, latitude), 1) + transform.__doc__ = Transform.transform.__doc__ + + def inverted(self): + # The inverse of the inverse is the original transform... ;) + return HammerAxes.HammerTransform(self._resolution) + inverted.__doc__ = Transform.inverted.__doc__ + +# Now register the projection with matplotlib so the user can select +# it. +register_projection(HammerAxes) + +# Now make a simple example using the custom projection. +from pylab import * + +subplot(111, projection="hammer") +grid(True) + +show() Added: branches/transforms/examples/custom_scale_example.py =================================================================== --- branches/transforms/examples/custom_scale_example.py (rev 0) +++ branches/transforms/examples/custom_scale_example.py 2008-01-07 21:15:58 UTC (rev 4803) @@ -0,0 +1,165 @@ +from matplotlib import scale as mscale +from matplotlib import transforms as mtransforms + +class MercatorLatitudeScale(mscale.ScaleBase): + """ + Scales data in range -pi/2 to pi/2 (-90 to 90 degrees) using + the system used to scale latitudes in a Mercator projection. + + The scale function: + ln(tan(y) + sec(y)) + + The inverse scale function: + atan(sinh(y)) + + Since the Mercator scale tends to infinity at +/- 90 degrees, + there is user-defined threshold, above and below which nothing + will be plotted. This defaults to +/- 85 degrees. + + source: + http://en.wikipedia.org/wiki/Mercator_projection + """ + + # The scale class must have a member ``name`` that defines the + # string used to select the scale. For example, + # ``gca().set_yscale("mercator")`` would be used to select this + # scale. + name = 'mercator' + + + def __init__(self, axis, **kwargs): + """ + Any keyword arguments passed to ``set_xscale`` and + ``set_yscale`` will be passed along to the scale's + constructor. + + thresh: The degree above which to crop the data. + """ + mscale.ScaleBase.__init__(self) + thresh = kwargs.pop("thresh", (85 / 180.0) * npy.pi) + if thresh >= npy.pi / 2.0: + raise ValueError("thresh must be less than pi/2") + self.thresh = thresh + + def get_transform(self): + """ + Override this method to return a new instance that does the + actual transformation of the data. + + The MercatorLatitudeTransform class is defined below as a + nested class of this one. + """ + return self.MercatorLatitudeTransform(self.thresh) + + def set_default_locators_and_formatters(self, axis): + """ + Override to set up the locators and formatters to use with the + scale. This is only required if the scale requires custom + locators and formatters. Writing custom locators and + formatters is rather outside the scope of this example, but + there are many helpful examples in ``ticker.py``. + + In our case, the Mercator example uses a fixed locator from + -90 to 90 degrees and a custom formatter class to put convert + the radians to degrees and put a degree symbol after the + value:: + """ + class DegreeFormatter(Formatter): + def __call__(self, x, pos=None): + # \u00b0 : degree symbol + return u"%d\u00b0" % ((x / npy.pi) * 180.0) + + deg2rad = npy.pi / 180.0 + axis.set_major_locator(FixedLocator( + npy.arange(-90, 90, 10) * deg2rad)) + axis.set_major_formatter(DegreeFormatter()) + axis.set_minor_formatter(DegreeFormatter()) + + def limit_range_for_scale(self, vmin, vmax, minpos): + """ + Override to limit the bounds of the axis to the domain of the + transform. In the case of Mercator, the bounds should be + limited to the threshold that was passed in. Unlike the + autoscaling provided by the tick locators, this range limiting + will always be adhered to, whether the axis range is set + manually, determined automatically or changed through panning + and zooming. + """ + return max(vmin, -self.thresh), min(vmax, self.thresh) + + class MercatorLatitudeTransform(mtransforms.Transform): + # There are two value members that must be defined. + # ``input_dims`` and ``output_dims`` specify number of input + # dimensions and output dimensions to the transformation. + # These are used by the transformation framework to do some + # error checking and prevent incompatible transformations from + # being connected together. When defining transforms for a + # scale, which are, by definition, separable and have only one + # dimension, these members should always be set to 1. + input_dims = 1 + output_dims = 1 + is_separable = True + + def __init__(self, thresh): + mtransforms.Transform.__init__(self) + self.thresh = thresh + + def transform(self, a): + """ + This transform takes an Nx1 ``numpy`` array and returns a + transformed copy. Since the range of the Mercator scale + is limited by the user-specified threshold, the input + array must be masked to contain only valid values. + ``matplotlib`` will handle masked arrays and remove the + out-of-range data from the plot. Importantly, the + ``transform`` method *must* return an array that is the + same shape as the input array, since these values need to + remain synchronized with values in the other dimension. + """ + masked = ma.masked_where((a < -self.thresh) | (a > self.thresh), a) + if masked.mask.any(): + return ma.log(npy.abs(ma.tan(masked) + 1.0 / ma.cos(masked))) + else: + return npy.log(npy.abs(npy.tan(a) + 1.0 / npy.cos(a))) + + def inverted(self): + """ + Override this method so matplotlib knows how to get the + inverse transform for this transform. + """ + return MercatorLatitudeScale.InvertedMercatorLatitudeTransform(self.thresh) + + class InvertedMercatorLatitudeTransform(mtransforms.Transform): + input_dims = 1 + output_dims = 1 + is_separable = True + + def __init__(self, thresh): + mtransforms.Transform.__init__(self) + self.thresh = thresh + + def transform(self, a): + return npy.arctan(npy.sinh(a)) + + def inverted(self): + return MercatorLatitudeScale.MercatorLatitudeTransform(self.thresh) + +# Now that the Scale class has been defined, it must be registered so +# that ``matplotlib`` can find it. +mscale.register_scale(MercatorLatitudeScale) + +from pylab import * +import numpy as npy + +t = arange(-180.0, 180.0, 0.1) +s = t / 360.0 * npy.pi + +plot(t, s, '-', lw=2) +gca().set_yscale('mercator') + +xlabel('Longitude') +ylabel('Latitude') +title('Mercator: Projection of the Oppressor') +grid(True) + +show() Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2008-01-06 18:28:17 UTC (rev 4802) +++ branches/transforms/lib/matplotlib/axes.py 2008-01-07 21:15:58 UTC (rev 4803) @@ -550,6 +550,10 @@ self.bbox = mtransforms.TransformedBbox(self._position, fig.transFigure) #these will be updated later as data is added + self.dataLim = mtransforms.Bbox.unit() + self.viewLim = mtransforms.Bbox.unit() + self.transScale = mtransforms.TransformWrapper(mtransforms.IdentityTransform()) + self._set_lim_and_transforms() def _set_lim_and_transforms(self): @@ -558,8 +562,6 @@ transScale, transData, transLimits and transAxes transformations. """ - self.dataLim = mtransforms.Bbox.unit() - self.viewLim = mtransforms.Bbox.unit() self.transAxes = mtransforms.BboxTransformTo(self.bbox) # Transforms the x and y axis separately by a scale factor Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2008-01-06 18:28:17 UTC (rev 4802) +++ branches/transforms/lib/matplotlib/patches.py 2008-01-07 21:15:58 UTC (rev 4803) @@ -371,6 +371,7 @@ self._width = width self._height = height self._rect_transform = transforms.IdentityTransform() + self._update_patch_transform() __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd def get_path(self): @@ -862,6 +863,7 @@ self.angle = angle self._path = Path.unit_circle() self._patch_transform = transforms.IdentityTransform() + self._recompute_transform() def _recompute_transform(self): center = (self.convert_xunits(self.center[0]), Modified: branches/transforms/lib/matplotlib/projections/__init__.py =================================================================== --- branches/transforms/lib/matplotlib/projections/__init__.py 2008-01-06 18:28:17 UTC (rev 4802) +++ branches/transforms/lib/matplotlib/projections/__init__.py 2008-01-07 21:15:58 UTC (rev 4803) @@ -26,7 +26,11 @@ AitoffAxes, HammerAxes, LambertAxes) +) +def register_projection(cls): + projection_registry.register(cls) + def get_projection_class(projection): if projection is None: projection = 'rectilinear' Modified: branches/transforms/lib/matplotlib/projections/geo.py =================================================================== --- branches/transforms/lib/matplotlib/projections/geo.py 2008-01-06 18:28:17 UTC (rev 4802) +++ branches/transforms/lib/matplotlib/projections/geo.py 2008-01-07 21:15:58 UTC (rev 4803) @@ -51,19 +51,13 @@ Axes.set_ylim(self, -npy.pi / 2.0, npy.pi / 2.0) def _set_lim_and_transforms(self): - self.dataLim = Bbox.unit() - self.viewLim = Bbox.unit() - self.transAxes = BboxTransformTo(self.bbox) - - # Transforms the x and y axis separately by a scale factor - # It is assumed that this part will have non-linear components - self.transScale = TransformWrapper(IdentityTransform()) - # A (possibly non-linear) projection on the (already scaled) data self.transProjection = self._get_core_transform(self.RESOLUTION) self.transAffine = self._get_affine_transform() + self.transAxes = BboxTransformTo(self.bbox) + # The complete data transformation stack -- from data all the # way to display coordinates self.transData = \ Modified: branches/transforms/lib/matplotlib/projections/polar.py =================================================================== --- branches/transforms/lib/matplotlib/projections/polar.py 2008-01-06 18:28:17 UTC (rev 4802) +++ branches/transforms/lib/matplotlib/projections/polar.py 2008-01-07 21:15:58 UTC (rev 4803) @@ -186,8 +186,6 @@ self.yaxis.set_ticks_position('none') def _set_lim_and_transforms(self): - self.dataLim = Bbox.unit() - self.viewLim = Bbox.unit() self.transAxes = BboxTransformTo(self.bbox) # Transforms the x and y axis separately by a scale factor Modified: branches/transforms/lib/matplotlib/scale.py =================================================================== --- branches/transforms/lib/matplotlib/scale.py 2008-01-06 18:28:17 UTC (rev 4802) +++ branches/transforms/lib/matplotlib/scale.py 2008-01-07 21:15:58 UTC (rev 4803) @@ -307,94 +307,11 @@ return self._transform -class MercatorLatitudeScale(ScaleBase): - """ - Scales data in range -pi/2 to pi/2 (-90 to 90 degrees) using - the system used to scale latitudes in a Mercator projection. - The scale function: - ln(tan(y) + sec(y)) - - The inverse scale function: - atan(sinh(y)) - - Since the Mercator scale tends to infinity at +/- 90 degrees, - there is user-defined threshold, above and below which nothing - will be plotted. This defaults to +/- 85 degrees. - - source: - http://en.wikipedia.org/wiki/Mercator_projection - """ - name = 'mercator_latitude' - - class MercatorLatitudeTransform(Transform): - input_dims = 1 - output_dims = 1 - is_separable = True - - def __init__(self, thresh): - Transform.__init__(self) - self.thresh = thresh - - def transform(self, a): - masked = ma.masked_where((a < -self.thresh) | (a > self.thresh), a) - if masked.mask.any(): - return ma.log(npy.abs(ma.tan(masked) + 1.0 / ma.cos(masked))) - else: - return npy.log(npy.abs(npy.tan(a) + 1.0 / npy.cos(a))) - - def inverted(self): - return MercatorLatitudeScale.InvertedMercatorLatitudeTransform(self.thresh) - - class InvertedMercatorLatitudeTransform(Transform): - input_dims = 1 - output_dims = 1 - is_separable = True - - def __init__(self, thresh): - Transform.__init__(self) - self.thresh = thresh - - def transform(self, a): - return npy.arctan(npy.sinh(a)) - - def inverted(self): - return MercatorLatitudeScale.MercatorLatitudeTransform(self.thresh) - - def __init__(self, axis, **kwargs): - """ - thresh: The degree above which to crop the data. - """ - thresh = kwargs.pop("thresh", (85 / 180.0) * npy.pi) - if thresh >= npy.pi / 2.0: - raise ValueError("thresh must be less than pi/2") - self.thresh = thresh - self._transform = self.MercatorLatitudeTransform(thresh) - - def set_default_locators_and_formatters(self, axis): - class DegreeFormatter(Formatter): - def __call__(self, x, pos=None): - # \u00b0 : degree symbol - return u"%d\u00b0" % ((x / npy.pi) * 180.0) - - deg2rad = npy.pi / 180.0 - axis.set_major_locator(FixedLocator( - npy.arange(-90, 90, 10) * deg2rad)) - axis.set_major_formatter(DegreeFormatter()) - axis.set_minor_formatter(DegreeFormatter()) - - def get_transform(self): - return self._transform - - def limit_range_for_scale(self, vmin, vmax, minpos): - return max(vmin, -self.thresh), min(vmax, self.thresh) - - _scale_mapping = { 'linear' : LinearScale, 'log' : LogScale, - 'symlog' : SymmetricalLogScale, - 'mercator_latitude' : MercatorLatitudeScale + 'symlog' : SymmetricalLogScale } def scale_factory(scale, axis, **kwargs): scale = scale.lower() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-01-07 21:18:02
|
Revision: 4804 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4804&view=rev Author: mdboom Date: 2008-01-07 13:18:00 -0800 (Mon, 07 Jan 2008) Log Message: ----------- Merged revisions 4801-4803 via svnmerge from http://matplotlib.svn.sf.net/svnroot/matplotlib/trunk/matplotlib ........ r4802 | cmoad | 2008-01-06 13:28:17 -0500 (Sun, 06 Jan 2008) | 1 line minor rev bump ........ Modified Paths: -------------- branches/transforms/API_CHANGES branches/transforms/CHANGELOG branches/transforms/setupext.py Property Changed: ---------------- branches/transforms/ Property changes on: branches/transforms ___________________________________________________________________ Name: svnmerge-integrated - /trunk/matplotlib:1-4800 + /trunk/matplotlib:1-4803 Modified: branches/transforms/API_CHANGES =================================================================== --- branches/transforms/API_CHANGES 2008-01-07 21:15:58 UTC (rev 4803) +++ branches/transforms/API_CHANGES 2008-01-07 21:18:00 UTC (rev 4804) @@ -169,6 +169,8 @@ END OF TRANSFORMS REFACTORING +0.91.2 Released + For csv2rec, checkrows=0 is the new default indicating all rows will be checked for type inference @@ -181,6 +183,7 @@ Removed, dead/experimental ExampleInfo, Namespace and Importer code from matplotlib/__init__.py + 0.91.1 Released 0.91.0 Released Modified: branches/transforms/CHANGELOG =================================================================== --- branches/transforms/CHANGELOG 2008-01-07 21:15:58 UTC (rev 4803) +++ branches/transforms/CHANGELOG 2008-01-07 21:18:00 UTC (rev 4804) @@ -1,3 +1,6 @@ +=============================================================== +2008-01-06 Released 0.91.2 at revision 4802 + 2007-12-26 Reduce too-late use of matplotlib.use() to a warning instead of an exception, for backwards compatibility - EF Modified: branches/transforms/setupext.py =================================================================== --- branches/transforms/setupext.py 2008-01-07 21:15:58 UTC (rev 4803) +++ branches/transforms/setupext.py 2008-01-07 21:18:00 UTC (rev 4804) @@ -903,9 +903,9 @@ # First test for a MacOSX/darwin framework install from os.path import join, exists framework_dirs = [ + join(os.getenv('HOME'), '/Library/Frameworks'), + '/Library/Frameworks', '/System/Library/Frameworks/', - '/Library/Frameworks', - join(os.getenv('HOME'), '/Library/Frameworks') ] # Find the directory that contains the Tcl.framework and Tk.framework This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |