From d2db56337a3d236decca94e6ffbda8f4bef26324 Mon Sep 17 00:00:00 2001 From: D Fournier Date: Sun, 6 Mar 2016 16:53:46 -0800 Subject: [PATCH] Updated notebook example --- ...SimPEG Tutorial - MAG Linear Problem.ipynb | 1133 +++++++++++++---- 1 file changed, 899 insertions(+), 234 deletions(-) diff --git a/simpegPF/notebooks/SimPEG Tutorial - MAG Linear Problem.ipynb b/simpegPF/notebooks/SimPEG Tutorial - MAG Linear Problem.ipynb index eb97d84c..1b640c16 100644 --- a/simpegPF/notebooks/SimPEG Tutorial - MAG Linear Problem.ipynb +++ b/simpegPF/notebooks/SimPEG Tutorial - MAG Linear Problem.ipynb @@ -34,21 +34,16 @@ "\n", "$$\\vec b(P) = \\sum_{j=1}^{nc} \\mathbf{T}_j \\cdot \\vec H_0 \\; \\kappa_j$$\n", "\n", - "giving rise to a linear problem.\n" + "giving rise to a linear problem.\n", + "\n", + "The remaining of this notebook goes through all the important components of a 3D magnetic experiment. From mesh creation, topography, data and inverse problem. \n", + "\n", + "Enjoy.\n" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 1, + "execution_count": 133, "metadata": { "collapsed": false }, @@ -65,42 +60,21 @@ "name": "stderr", "output_type": "stream", "text": [ - "C:\\Users\\dominiquef.MIRAGEOSCIENCE\\AppData\\Local\\Continuum\\Anaconda\\lib\\site-packages\\IPython\\kernel\\__init__.py:13: ShimWarning: The `IPython.kernel` package has been deprecated. You should import from ipykernel or jupyter_client instead.\n", - " \"You should import from ipykernel or jupyter_client instead.\", ShimWarning)\n" + "WARNING: pylab import has clobbered these variables: ['linalg', 'beta', 'inv']\n", + "`%matplotlib` prevents importing * from pylab and numpy\n" ] } ], "source": [ "%matplotlib notebook\n", - "%pylab" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Efficiency Warning: Interpolation will be slow, use setup.py!\n", - "\n", - " python setup.py build_ext --inplace\n", - " \n" - ] - } - ], - "source": [ + "%pylab\n", "from SimPEG import *\n", "import simpegPF as PF" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 135, "metadata": { "collapsed": false, "scrolled": true @@ -129,47 +103,37 @@ "midx = int(mesh.nCx/2)\n", "midy = int(mesh.nCy/2)\n", "\n", - "# Assume flat topo for now, so all cells are active\n", - "nC = mesh.nC \n", - "actv = np.ones(nC)\n", + "# Lets create a simple Gaussian topo and set the active cells\n", + "[xx,yy] = np.meshgrid(mesh.vectorNx,mesh.vectorNy)\n", + "zz = -np.exp( ( xx**2 + yy**2 )/ 75**2 ) + mesh.vectorNz[-1]\n", "\n", + "topo = np.c_[mkvc(xx),mkvc(yy),mkvc(zz)] # We would usually load a topofile\n", + "\n", + "actv = PF.Magnetics.getActiveTopo(mesh,topo,'N') # Go from topo to actv cells\n", + "\n", + "#nC = mesh.nC \n", + "#actv = np.asarray(range(mesh.nC))\n", + "\n", + "# Create active map to go from reduce space to full\n", + "actvMap = Maps.ActiveCells(mesh, actv, -100)\n", + "nC = len(actv)\n", "\n", "# Create and array of observation points\n", "xr = np.linspace(-20., 20., 20)\n", "yr = np.linspace(-20., 20., 20)\n", "X, Y = np.meshgrid(xr, yr)\n", - "Z = np.ones(X.size)*(mesh.vectorNz[-1]+dx) # Let just put the observation flat\n", + "\n", + "# Let just put the observation above the topo\n", + "Z = -np.exp( ( X**2 + Y**2 )/ 75**2 ) + mesh.vectorNz[-1] + 5. \n", "\n", "rxLoc = np.c_[Utils.mkvc(X.T), Utils.mkvc(Y.T), Utils.mkvc(Z.T)]\n" ] }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "data": { - "text/plain": [ - "59.390075000000003" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "Z.max()" - ] - }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now that we have all our spatial components, we can create our linear system. For a single location and single component of the data, the system would looks like this:\n", + "Now that we have all our spatial components, we can create our linear system. For a single location and single component of the data, the system would look like this:\n", "\n", "$$ b_x =\n", "\t\\begin{bmatrix}\n", @@ -200,7 +164,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 136, "metadata": { "collapsed": false }, @@ -227,8 +191,8 @@ ], "source": [ "# First, convert the magnetization direction to Cartesian\n", - "mi = np.ones(mesh.nC) * M[0]\n", - "md = np.ones(mesh.nC) * M[1]\n", + "mi = np.ones(nC) * M[0]\n", + "md = np.ones(nC) * M[1]\n", "M_xyz = PF.Magnetics.dipazm_2_xyz( mi , md ) # Ouputs an nc x 3 array\n", "\n", "# Create the forward model operator\n", @@ -237,7 +201,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 137, "metadata": { "collapsed": false }, @@ -1000,7 +964,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -1013,11 +977,12 @@ "source": [ "# Generate a distance weighting\n", "wr = PF.Magnetics.get_dist_wgt(mesh,rxLoc,actv,3.,np.min(mesh.hx)/4)\n", - "wrMap = PF.BaseMag.WeightMap(mesh, wr)\n", + "wrMap = PF.BaseMag.WeightMap(nC, wr)\n", "\n", + "wr_FULL = actvMap * wr\n", "plt.figure()\n", "ax = subplot()\n", - "mesh.plotSlice(wr, ax = ax, normal = 'Y', ind=midx)\n", + "mesh.plotSlice(wr_FULL, ax = ax, normal = 'Y', ind=midx, grid=True, clim = (0, wr.max()))\n", "title('Distance weighting')\n", "xlabel('x');ylabel('z')\n", "plt.gca().set_aspect('equal', adjustable='box')" @@ -1025,7 +990,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 138, "metadata": { "collapsed": false }, @@ -1769,7 +1734,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -1781,10 +1746,10 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 39, + "execution_count": 138, "metadata": {}, "output_type": "execute_result" } @@ -1795,29 +1760,35 @@ "model = np.zeros((mesh.nCx,mesh.nCy,mesh.nCz))\n", "model[(midx-2):(midx+2),(midy-2):(midy+2),-6:-2] = 0.01\n", "model = mkvc(model)\n", + "model = model[actv]\n", "\n", - "# Create a few models\n", + "# Plot the model\n", "figure()\n", - "ax = subplot(211)\n", - "mesh.plotSlice(model, ax = ax, normal = 'Y', ind=midx, grid=True)\n", + "ax = subplot(212)\n", + "mesh.plotSlice(actvMap * model, ax = ax, normal = 'Y', ind=midy, grid=True, clim = (-1e-3, model.max()))\n", "title('A simple block model.')\n", "xlabel('x');ylabel('z')\n", "plt.gca().set_aspect('equal', adjustable='box')\n", "\n", "# We can now generate data\n", - "data = F.dot(model) #: this is matrix multiplication!!\n", - "subplot(212)\n", + "d = F.dot(model) #: this is matrix multiplication!!\n", + "data = d + randn(len(d)) # We add some random Gaussian noise (1nT)\n", + "wd = np.ones(len(data))*1. # Assign flat uncertainties\n", + "\n", + "\n", + "subplot(221)\n", + "imshow(d.reshape(X.shape), extent=[xr.min(), xr.max(), yr.min(), yr.max()])\n", + "title('True data.')\n", + "plt.gca().set_aspect('equal', adjustable='box')\n", + "plt.colorbar()\n", + "\n", + "subplot(222)\n", "imshow(data.reshape(X.shape), extent=[xr.min(), xr.max(), yr.min(), yr.max()])\n", - "title('Predicted data.')\n", + "title('Data + Noise')\n", "plt.gca().set_aspect('equal', adjustable='box')\n", "plt.colorbar()\n" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, { "cell_type": "markdown", "metadata": {}, @@ -1827,7 +1798,7 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 141, "metadata": { "collapsed": false }, @@ -1835,50 +1806,48 @@ "source": [ "beta_in = 1e+4\n", "\n", - "prob = PF.Magnetics.MagneticIntegral(mesh, F)\n", + "# Creat reduced identity map for topography\n", + "idenMap = Maps.IdentityMap(nP = nC)\n", + "\n", + "prob = PF.Magnetics.MagneticIntegral(mesh, F, mapping = idenMap)\n", "prob.solverOpts['accuracyTol'] = 1e-4\n", "survey = Survey.LinearSurvey()\n", "survey.pair(prob)\n", - "\n", "survey.dobs=data\n", "\n", - "# Create pre-conditioner \n", - "diagA = np.sum(F**2.,axis=0) + beta_in*np.ones(nC)\n", - "PC = sp.spdiags(diagA**-1., 0, nC, nC);\n", - "\n", - "reg = Regularization.Simple(mesh, mapping=wrMap)\n", - "reg.mref = np.zeros(mesh.nC)\n", + "# Initiate a simple Tikonov regularization\n", + "reg = Regularization.Simple(mesh, indActive = actv, mapping=wrMap)\n", + "reg.mref = np.zeros(nC)\n", "reg.alpha_s = 1.\n", "\n", + "# Create pre-conditioner \n", + "diagA = np.sum(F**2.,axis=0) + beta_in*(reg.W.T*reg.W).diagonal()*(wr**2.0)\n", + "PC = Utils.sdiag(diagA**-1.)\n", "\n", + "# Creat reduced identity map\n", + "idenMap = Maps.IdentityMap(nP = nC)\n", + "\n", + "# Set up the misfit function and pre-conditioner\n", "dmis = DataMisfit.l2_DataMisfit(survey)\n", - "dmis.Wd = np.ones(F.shape[0])\n", + "dmis.Wd = wd\n", "opt = Optimization.ProjectedGNCG(maxIter=10,lower=0.,upper=1.)\n", - "# opt = Optimization.InexactGaussNewton(maxIter=6)\n", "opt.approxHinv = PC\n", "\n", - "# betaest = Directives.BetaEstimate_ByEig()\n", - "\n", + "# Set up directives for the inverse problem\n", "invProb = InvProblem.BaseInvProblem(dmis, reg, opt, beta = beta_in)\n", "beta = Directives.BetaSchedule(coolingFactor=2, coolingRate=1)\n", - "\n", "target = Directives.TargetMisfit()\n", "\n", + "# The final inversion object controling all the parts above\n", "inv = Inversion.BaseInversion(invProb, directiveList=[beta, target])\n", "\n", - "m0 = np.ones(mesh.nC) * 1e-4" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Explore the documentation to see what other parameters you can tweak in the different elements of the inversion, but let's check how well we recovered the model just by using the default parameters:" + "# Define a starting model (small)\n", + "m0 = np.ones(nC) * 1e-4" ] }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 142, "metadata": { "collapsed": false }, @@ -1892,15 +1861,17 @@ "=============================== Projected GNCG ===============================\n", " # beta phi_d phi_m f |proj(x-g)-x| LS Comment \n", "-----------------------------------------------------------------------------\n", - " 0 1.00e+04 5.58e+04 2.30e-04 5.58e+04 8.41e+01 0 \n", - " 1 5.00e+03 3.97e+03 9.46e-03 4.02e+03 6.95e+01 0 \n", - " 2 2.50e+03 3.63e+02 2.51e-02 4.26e+02 7.78e+01 0 Skip BFGS \n", + " 0 1.00e+04 6.75e+04 3.37e-04 6.75e+04 8.30e+01 0 \n", + " 1 5.00e+03 2.77e+03 3.19e-02 2.93e+03 6.90e+01 0 \n", + " 2 2.50e+03 4.03e+02 5.53e-02 5.41e+02 7.04e+01 0 \n", + " 3 1.25e+03 2.15e+02 5.75e-02 2.87e+02 6.99e+01 0 Skip BFGS \n", + " 4 6.25e+02 2.00e+02 5.82e-02 2.37e+02 7.31e+01 0 Skip BFGS \n", "------------------------- STOP! -------------------------\n", - "1 : |fc-fOld| = 0.0000e+00 <= tolF*(1+|f0|) = 5.5849e+03\n", - "1 : |xc-x_last| = 8.2961e-03 <= tolX*(1+|x0|) = 1.0116e-01\n", - "0 : |proj(x-g)-x| = 7.7747e+01 <= tolG = 1.0000e-01\n", - "0 : |proj(x-g)-x| = 7.7747e+01 <= 1e3*eps = 1.0000e-02\n", - "0 : maxIter = 10 <= iter = 3\n", + "1 : |fc-fOld| = 0.0000e+00 <= tolF*(1+|f0|) = 6.7516e+03\n", + "1 : |xc-x_last| = 3.1475e-03 <= tolX*(1+|x0|) = 1.0111e-01\n", + "0 : |proj(x-g)-x| = 7.3120e+01 <= tolG = 1.0000e-01\n", + "0 : |proj(x-g)-x| = 7.3120e+01 <= 1e3*eps = 1.0000e-02\n", + "0 : maxIter = 10 <= iter = 5\n", "------------------------- DONE! -------------------------\n" ] } @@ -1909,21 +1880,20 @@ "mrec = inv.run(m0)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Inversion has converged. We can plot sections through the model." + ] + }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 143, "metadata": { "collapsed": false }, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\dominiquef.MIRAGEOSCIENCE\\AppData\\Local\\Continuum\\Anaconda\\lib\\site-packages\\matplotlib\\pyplot.py:424: RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (`matplotlib.pyplot.figure`) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam `figure.max_open_warning`).\n", - " max_open_warning, RuntimeWarning)\n" - ] - }, { "data": { "application/javascript": [ @@ -2663,7 +2633,791 @@ { "data": { "text/html": [ - "" + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Here is the recovered susceptibility model\n", + "plt.figure()\n", + "ax = subplot(121)\n", + "mesh.plotSlice(actvMap * mrec, ax = ax, normal = 'Z', ind=-2, clim = (-1e-3, model.max()))\n", + "title('Recovered model.')\n", + "xlabel('x');ylabel('y')\n", + "plt.gca().set_aspect('equal', adjustable='box')\n", + "\n", + "\n", + "# Horizontalsection\n", + "ax = subplot(122)\n", + "mesh.plotSlice(actvMap * mrec, ax = ax, normal = 'Y', ind=midx, clim = (-1e-3, model.max()))\n", + "title('Recovered model.')\n", + "xlabel('x');ylabel('z')\n", + "plt.gca().set_aspect('equal', adjustable='box')\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Great, we have a 3D model of susceptibility, but the job is not done yet.\n", + "A VERY important step of the inversion workflow is to look at how well the model can predict the observed data.\n", + "The figure below compares the observed, predicted and normalized residual.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 144, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function() {\n", + " if (typeof(WebSocket) !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof(MozWebSocket) !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert('Your browser does not have WebSocket support.' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.');\n", + " };\n", + "}\n", + "\n", + "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = (this.ws.binaryType != undefined);\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById(\"mpl-warnings\");\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent = (\n", + " \"This browser does not support binary websocket messages. \" +\n", + " \"Performance may be slow.\");\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = $('
');\n", + " this._root_extra_style(this.root)\n", + " this.root.attr('style', 'display: inline-block');\n", + "\n", + " $(parent_element).append(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", + " fig.send_message(\"send_image_mode\", {});\n", + " fig.send_message(\"refresh\", {});\n", + " }\n", + "\n", + " this.imageObj.onload = function() {\n", + " if (fig.image_mode == 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " fig.waiting = false;\n", + " };\n", + "\n", + " this.imageObj.onunload = function() {\n", + " this.ws.close();\n", + " }\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "}\n", + "\n", + "mpl.figure.prototype._init_header = function() {\n", + " var titlebar = $(\n", + " '
');\n", + " var titletext = $(\n", + " '
');\n", + " titlebar.append(titletext)\n", + " this.root.append(titlebar);\n", + " this.header = titletext[0];\n", + "}\n", + "\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._init_canvas = function() {\n", + " var fig = this;\n", + "\n", + " var canvas_div = $('
');\n", + "\n", + " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", + "\n", + " function canvas_keyboard_event(event) {\n", + " return fig.key_event(event, event['data']);\n", + " }\n", + "\n", + " canvas_div.keydown('key_press', canvas_keyboard_event);\n", + " canvas_div.keyup('key_release', canvas_keyboard_event);\n", + " this.canvas_div = canvas_div\n", + " this._canvas_extra_style(canvas_div)\n", + " this.root.append(canvas_div);\n", + "\n", + " var canvas = $('');\n", + " canvas.addClass('mpl-canvas');\n", + " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", + "\n", + " this.canvas = canvas[0];\n", + " this.context = canvas[0].getContext(\"2d\");\n", + "\n", + " var rubberband = $('');\n", + " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", + "\n", + " var pass_mouse_events = true;\n", + "\n", + " canvas_div.resizable({\n", + " start: function(event, ui) {\n", + " pass_mouse_events = false;\n", + " },\n", + " resize: function(event, ui) {\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " stop: function(event, ui) {\n", + " pass_mouse_events = true;\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " });\n", + "\n", + " function mouse_event_fn(event) {\n", + " if (pass_mouse_events)\n", + " return fig.mouse_event(event, event['data']);\n", + " }\n", + "\n", + " rubberband.mousedown('button_press', mouse_event_fn);\n", + " rubberband.mouseup('button_release', mouse_event_fn);\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband.mousemove('motion_notify', mouse_event_fn);\n", + "\n", + " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", + " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", + "\n", + " canvas_div.on(\"wheel\", function (event) {\n", + " event = event.originalEvent;\n", + " event['data'] = 'scroll'\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " mouse_event_fn(event);\n", + " });\n", + "\n", + " canvas_div.append(canvas);\n", + " canvas_div.append(rubberband);\n", + "\n", + " this.rubberband = rubberband;\n", + " this.rubberband_canvas = rubberband[0];\n", + " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", + " this.rubberband_context.strokeStyle = \"#000000\";\n", + "\n", + " this._resize_canvas = function(width, height) {\n", + " // Keep the size of the canvas, canvas container, and rubber band\n", + " // canvas in synch.\n", + " canvas_div.css('width', width)\n", + " canvas_div.css('height', height)\n", + "\n", + " canvas.attr('width', width);\n", + " canvas.attr('height', height);\n", + "\n", + " rubberband.attr('width', width);\n", + " rubberband.attr('height', height);\n", + " }\n", + "\n", + " // Set the figure to an initial 600x600px, this will subsequently be updated\n", + " // upon first draw.\n", + " this._resize_canvas(600, 600);\n", + "\n", + " // Disable right mouse context menu.\n", + " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", + " return false;\n", + " });\n", + "\n", + " function set_focus () {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('
')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " // put a spacer in here.\n", + " continue;\n", + " }\n", + " var button = $('