You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1044 lines
67 KiB
1044 lines
67 KiB
{ |
|
"cells": [ |
|
{ |
|
"cell_type": "code", |
|
"execution_count": 1, |
|
"metadata": { |
|
"ExecuteTime": { |
|
"end_time": "2019-06-03T15:36:41.380974Z", |
|
"start_time": "2019-06-03T15:36:40.811967Z" |
|
}, |
|
"scrolled": false |
|
}, |
|
"outputs": [ |
|
{ |
|
"data": { |
|
"application/javascript": [ |
|
"/* Put everything inside the global mpl namespace */\n", |
|
"window.mpl = {};\n", |
|
"\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 = $('<div/>');\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", |
|
" if (mpl.ratio != 1) {\n", |
|
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", |
|
" }\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", |
|
" };\n", |
|
"\n", |
|
" this.imageObj.onunload = function() {\n", |
|
" fig.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", |
|
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", |
|
" 'ui-helper-clearfix\"/>');\n", |
|
" var titletext = $(\n", |
|
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", |
|
" 'text-align: center; padding: 3px;\"/>');\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 = $('<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 = $('<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 backingStore = this.context.backingStorePixelRatio ||\n", |
|
"\tthis.context.webkitBackingStorePixelRatio ||\n", |
|
"\tthis.context.mozBackingStorePixelRatio ||\n", |
|
"\tthis.context.msBackingStorePixelRatio ||\n", |
|
"\tthis.context.oBackingStorePixelRatio ||\n", |
|
"\tthis.context.backingStorePixelRatio || 1;\n", |
|
"\n", |
|
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", |
|
"\n", |
|
" var rubberband = $('<canvas/>');\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 * mpl.ratio);\n", |
|
" canvas.attr('height', height * mpl.ratio);\n", |
|
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\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 = $('<div/>')\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 = $('<button/>');\n", |
|
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", |
|
" 'ui-button-icon-only');\n", |
|
" button.attr('role', 'button');\n", |
|
" button.attr('aria-disabled', 'false');\n", |
|
" button.click(method_name, toolbar_event);\n", |
|
" button.mouseover(tooltip, toolbar_mouse_event);\n", |
|
"\n", |
|
" var icon_img = $('<span/>');\n", |
|
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", |
|
" icon_img.addClass(image);\n", |
|
" icon_img.addClass('ui-corner-all');\n", |
|
"\n", |
|
" var tooltip_span = $('<span/>');\n", |
|
" tooltip_span.addClass('ui-button-text');\n", |
|
" tooltip_span.html(tooltip);\n", |
|
"\n", |
|
" button.append(icon_img);\n", |
|
" button.append(tooltip_span);\n", |
|
"\n", |
|
" nav_element.append(button);\n", |
|
" }\n", |
|
"\n", |
|
" var fmt_picker_span = $('<span/>');\n", |
|
"\n", |
|
" var fmt_picker = $('<select/>');\n", |
|
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", |
|
" fmt_picker_span.append(fmt_picker);\n", |
|
" nav_element.append(fmt_picker_span);\n", |
|
" this.format_dropdown = fmt_picker[0];\n", |
|
"\n", |
|
" for (var ind in mpl.extensions) {\n", |
|
" var fmt = mpl.extensions[ind];\n", |
|
" var option = $(\n", |
|
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", |
|
" fmt_picker.append(option)\n", |
|
" }\n", |
|
"\n", |
|
" // Add hover states to the ui-buttons\n", |
|
" $( \".ui-button\" ).hover(\n", |
|
" function() { $(this).addClass(\"ui-state-hover\");},\n", |
|
" function() { $(this).removeClass(\"ui-state-hover\");}\n", |
|
" );\n", |
|
"\n", |
|
" var status_bar = $('<span class=\"mpl-message\"/>');\n", |
|
" nav_element.append(status_bar);\n", |
|
" this.message = status_bar[0];\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", |
|
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", |
|
" // which will in turn request a refresh of the image.\n", |
|
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype.send_message = function(type, properties) {\n", |
|
" properties['type'] = type;\n", |
|
" properties['figure_id'] = this.id;\n", |
|
" this.ws.send(JSON.stringify(properties));\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype.send_draw_message = function() {\n", |
|
" if (!this.waiting) {\n", |
|
" this.waiting = true;\n", |
|
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", |
|
" }\n", |
|
"}\n", |
|
"\n", |
|
"\n", |
|
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", |
|
" var format_dropdown = fig.format_dropdown;\n", |
|
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", |
|
" fig.ondownload(fig, format);\n", |
|
"}\n", |
|
"\n", |
|
"\n", |
|
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n", |
|
" var size = msg['size'];\n", |
|
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", |
|
" fig._resize_canvas(size[0], size[1]);\n", |
|
" fig.send_message(\"refresh\", {});\n", |
|
" };\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", |
|
" var x0 = msg['x0'] / mpl.ratio;\n", |
|
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", |
|
" var x1 = msg['x1'] / mpl.ratio;\n", |
|
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", |
|
" x0 = Math.floor(x0) + 0.5;\n", |
|
" y0 = Math.floor(y0) + 0.5;\n", |
|
" x1 = Math.floor(x1) + 0.5;\n", |
|
" y1 = Math.floor(y1) + 0.5;\n", |
|
" var min_x = Math.min(x0, x1);\n", |
|
" var min_y = Math.min(y0, y1);\n", |
|
" var width = Math.abs(x1 - x0);\n", |
|
" var height = Math.abs(y1 - y0);\n", |
|
"\n", |
|
" fig.rubberband_context.clearRect(\n", |
|
" 0, 0, fig.canvas.width, fig.canvas.height);\n", |
|
"\n", |
|
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", |
|
" // Updates the figure title.\n", |
|
" fig.header.textContent = msg['label'];\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", |
|
" var cursor = msg['cursor'];\n", |
|
" switch(cursor)\n", |
|
" {\n", |
|
" case 0:\n", |
|
" cursor = 'pointer';\n", |
|
" break;\n", |
|
" case 1:\n", |
|
" cursor = 'default';\n", |
|
" break;\n", |
|
" case 2:\n", |
|
" cursor = 'crosshair';\n", |
|
" break;\n", |
|
" case 3:\n", |
|
" cursor = 'move';\n", |
|
" break;\n", |
|
" }\n", |
|
" fig.rubberband_canvas.style.cursor = cursor;\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype.handle_message = function(fig, msg) {\n", |
|
" fig.message.textContent = msg['message'];\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n", |
|
" // Request the server to send over a new figure.\n", |
|
" fig.send_draw_message();\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", |
|
" fig.image_mode = msg['mode'];\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype.updated_canvas_event = function() {\n", |
|
" // Called whenever the canvas gets updated.\n", |
|
" this.send_message(\"ack\", {});\n", |
|
"}\n", |
|
"\n", |
|
"// A function to construct a web socket function for onmessage handling.\n", |
|
"// Called in the figure constructor.\n", |
|
"mpl.figure.prototype._make_on_message_function = function(fig) {\n", |
|
" return function socket_on_message(evt) {\n", |
|
" if (evt.data instanceof Blob) {\n", |
|
" /* FIXME: We get \"Resource interpreted as Image but\n", |
|
" * transferred with MIME type text/plain:\" errors on\n", |
|
" * Chrome. But how to set the MIME type? It doesn't seem\n", |
|
" * to be part of the websocket stream */\n", |
|
" evt.data.type = \"image/png\";\n", |
|
"\n", |
|
" /* Free the memory for the previous frames */\n", |
|
" if (fig.imageObj.src) {\n", |
|
" (window.URL || window.webkitURL).revokeObjectURL(\n", |
|
" fig.imageObj.src);\n", |
|
" }\n", |
|
"\n", |
|
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", |
|
" evt.data);\n", |
|
" fig.updated_canvas_event();\n", |
|
" fig.waiting = false;\n", |
|
" return;\n", |
|
" }\n", |
|
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", |
|
" fig.imageObj.src = evt.data;\n", |
|
" fig.updated_canvas_event();\n", |
|
" fig.waiting = false;\n", |
|
" return;\n", |
|
" }\n", |
|
"\n", |
|
" var msg = JSON.parse(evt.data);\n", |
|
" var msg_type = msg['type'];\n", |
|
"\n", |
|
" // Call the \"handle_{type}\" callback, which takes\n", |
|
" // the figure and JSON message as its only arguments.\n", |
|
" try {\n", |
|
" var callback = fig[\"handle_\" + msg_type];\n", |
|
" } catch (e) {\n", |
|
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", |
|
" return;\n", |
|
" }\n", |
|
"\n", |
|
" if (callback) {\n", |
|
" try {\n", |
|
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", |
|
" callback(fig, msg);\n", |
|
" } catch (e) {\n", |
|
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", |
|
" }\n", |
|
" }\n", |
|
" };\n", |
|
"}\n", |
|
"\n", |
|
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", |
|
"mpl.findpos = function(e) {\n", |
|
" //this section is from http://www.quirksmode.org/js/events_properties.html\n", |
|
" var targ;\n", |
|
" if (!e)\n", |
|
" e = window.event;\n", |
|
" if (e.target)\n", |
|
" targ = e.target;\n", |
|
" else if (e.srcElement)\n", |
|
" targ = e.srcElement;\n", |
|
" if (targ.nodeType == 3) // defeat Safari bug\n", |
|
" targ = targ.parentNode;\n", |
|
"\n", |
|
" // jQuery normalizes the pageX and pageY\n", |
|
" // pageX,Y are the mouse positions relative to the document\n", |
|
" // offset() returns the position of the element relative to the document\n", |
|
" var x = e.pageX - $(targ).offset().left;\n", |
|
" var y = e.pageY - $(targ).offset().top;\n", |
|
"\n", |
|
" return {\"x\": x, \"y\": y};\n", |
|
"};\n", |
|
"\n", |
|
"/*\n", |
|
" * return a copy of an object with only non-object keys\n", |
|
" * we need this to avoid circular references\n", |
|
" * http://stackoverflow.com/a/24161582/3208463\n", |
|
" */\n", |
|
"function simpleKeys (original) {\n", |
|
" return Object.keys(original).reduce(function (obj, key) {\n", |
|
" if (typeof original[key] !== 'object')\n", |
|
" obj[key] = original[key]\n", |
|
" return obj;\n", |
|
" }, {});\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype.mouse_event = function(event, name) {\n", |
|
" var canvas_pos = mpl.findpos(event)\n", |
|
"\n", |
|
" if (name === 'button_press')\n", |
|
" {\n", |
|
" this.canvas.focus();\n", |
|
" this.canvas_div.focus();\n", |
|
" }\n", |
|
"\n", |
|
" var x = canvas_pos.x * mpl.ratio;\n", |
|
" var y = canvas_pos.y * mpl.ratio;\n", |
|
"\n", |
|
" this.send_message(name, {x: x, y: y, button: event.button,\n", |
|
" step: event.step,\n", |
|
" guiEvent: simpleKeys(event)});\n", |
|
"\n", |
|
" /* This prevents the web browser from automatically changing to\n", |
|
" * the text insertion cursor when the button is pressed. We want\n", |
|
" * to control all of the cursor setting manually through the\n", |
|
" * 'cursor' event from matplotlib */\n", |
|
" event.preventDefault();\n", |
|
" return false;\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", |
|
" // Handle any extra behaviour associated with a key event\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype.key_event = function(event, name) {\n", |
|
"\n", |
|
" // Prevent repeat events\n", |
|
" if (name == 'key_press')\n", |
|
" {\n", |
|
" if (event.which === this._key)\n", |
|
" return;\n", |
|
" else\n", |
|
" this._key = event.which;\n", |
|
" }\n", |
|
" if (name == 'key_release')\n", |
|
" this._key = null;\n", |
|
"\n", |
|
" var value = '';\n", |
|
" if (event.ctrlKey && event.which != 17)\n", |
|
" value += \"ctrl+\";\n", |
|
" if (event.altKey && event.which != 18)\n", |
|
" value += \"alt+\";\n", |
|
" if (event.shiftKey && event.which != 16)\n", |
|
" value += \"shift+\";\n", |
|
"\n", |
|
" value += 'k';\n", |
|
" value += event.which.toString();\n", |
|
"\n", |
|
" this._key_event_extra(event, name);\n", |
|
"\n", |
|
" this.send_message(name, {key: value,\n", |
|
" guiEvent: simpleKeys(event)});\n", |
|
" return false;\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", |
|
" if (name == 'download') {\n", |
|
" this.handle_save(this, null);\n", |
|
" } else {\n", |
|
" this.send_message(\"toolbar_button\", {name: name});\n", |
|
" }\n", |
|
"};\n", |
|
"\n", |
|
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", |
|
" this.message.textContent = tooltip;\n", |
|
"};\n", |
|
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", |
|
"\n", |
|
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", |
|
"\n", |
|
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", |
|
" // Create a \"websocket\"-like object which calls the given IPython comm\n", |
|
" // object with the appropriate methods. Currently this is a non binary\n", |
|
" // socket, so there is still some room for performance tuning.\n", |
|
" var ws = {};\n", |
|
"\n", |
|
" ws.close = function() {\n", |
|
" comm.close()\n", |
|
" };\n", |
|
" ws.send = function(m) {\n", |
|
" //console.log('sending', m);\n", |
|
" comm.send(m);\n", |
|
" };\n", |
|
" // Register the callback with on_msg.\n", |
|
" comm.on_msg(function(msg) {\n", |
|
" //console.log('receiving', msg['content']['data'], msg);\n", |
|
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n", |
|
" ws.onmessage(msg['content']['data'])\n", |
|
" });\n", |
|
" return ws;\n", |
|
"}\n", |
|
"\n", |
|
"mpl.mpl_figure_comm = function(comm, msg) {\n", |
|
" // This is the function which gets called when the mpl process\n", |
|
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n", |
|
"\n", |
|
" var id = msg.content.data.id;\n", |
|
" // Get hold of the div created by the display call when the Comm\n", |
|
" // socket was opened in Python.\n", |
|
" var element = $(\"#\" + id);\n", |
|
" var ws_proxy = comm_websocket_adapter(comm)\n", |
|
"\n", |
|
" function ondownload(figure, format) {\n", |
|
" window.open(figure.imageObj.src);\n", |
|
" }\n", |
|
"\n", |
|
" var fig = new mpl.figure(id, ws_proxy,\n", |
|
" ondownload,\n", |
|
" element.get(0));\n", |
|
"\n", |
|
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", |
|
" // web socket which is closed, not our websocket->open comm proxy.\n", |
|
" ws_proxy.onopen();\n", |
|
"\n", |
|
" fig.parent_element = element.get(0);\n", |
|
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", |
|
" if (!fig.cell_info) {\n", |
|
" console.error(\"Failed to find cell for figure\", id, fig);\n", |
|
" return;\n", |
|
" }\n", |
|
"\n", |
|
" var output_index = fig.cell_info[2]\n", |
|
" var cell = fig.cell_info[0];\n", |
|
"\n", |
|
"};\n", |
|
"\n", |
|
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", |
|
" var width = fig.canvas.width/mpl.ratio\n", |
|
" fig.root.unbind('remove')\n", |
|
"\n", |
|
" // Update the output cell to use the data from the current canvas.\n", |
|
" fig.push_to_output();\n", |
|
" var dataURL = fig.canvas.toDataURL();\n", |
|
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n", |
|
" // the notebook keyboard shortcuts fail.\n", |
|
" IPython.keyboard_manager.enable()\n", |
|
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n", |
|
" fig.close_ws(fig, msg);\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype.close_ws = function(fig, msg){\n", |
|
" fig.send_message('closing', msg);\n", |
|
" // fig.ws.close()\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", |
|
" // Turn the data on the canvas into data in the output cell.\n", |
|
" var width = this.canvas.width/mpl.ratio\n", |
|
" var dataURL = this.canvas.toDataURL();\n", |
|
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype.updated_canvas_event = function() {\n", |
|
" // Tell IPython that the notebook contents must change.\n", |
|
" IPython.notebook.set_dirty(true);\n", |
|
" this.send_message(\"ack\", {});\n", |
|
" var fig = this;\n", |
|
" // Wait a second, then push the new image to the DOM so\n", |
|
" // that it is saved nicely (might be nice to debounce this).\n", |
|
" setTimeout(function () { fig.push_to_output() }, 1000);\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype._init_toolbar = function() {\n", |
|
" var fig = this;\n", |
|
"\n", |
|
" var nav_element = $('<div/>')\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) { continue; };\n", |
|
"\n", |
|
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", |
|
" button.click(method_name, toolbar_event);\n", |
|
" button.mouseover(tooltip, toolbar_mouse_event);\n", |
|
" nav_element.append(button);\n", |
|
" }\n", |
|
"\n", |
|
" // Add the status bar.\n", |
|
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", |
|
" nav_element.append(status_bar);\n", |
|
" this.message = status_bar[0];\n", |
|
"\n", |
|
" // Add the close button to the window.\n", |
|
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", |
|
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", |
|
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", |
|
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", |
|
" buttongrp.append(button);\n", |
|
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", |
|
" titlebar.prepend(buttongrp);\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype._root_extra_style = function(el){\n", |
|
" var fig = this\n", |
|
" el.on(\"remove\", function(){\n", |
|
"\tfig.close_ws(fig, {});\n", |
|
" });\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype._canvas_extra_style = function(el){\n", |
|
" // this is important to make the div 'focusable\n", |
|
" el.attr('tabindex', 0)\n", |
|
" // reach out to IPython and tell the keyboard manager to turn it's self\n", |
|
" // off when our div gets focus\n", |
|
"\n", |
|
" // location in version 3\n", |
|
" if (IPython.notebook.keyboard_manager) {\n", |
|
" IPython.notebook.keyboard_manager.register_events(el);\n", |
|
" }\n", |
|
" else {\n", |
|
" // location in version 2\n", |
|
" IPython.keyboard_manager.register_events(el);\n", |
|
" }\n", |
|
"\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", |
|
" var manager = IPython.notebook.keyboard_manager;\n", |
|
" if (!manager)\n", |
|
" manager = IPython.keyboard_manager;\n", |
|
"\n", |
|
" // Check for shift+enter\n", |
|
" if (event.shiftKey && event.which == 13) {\n", |
|
" this.canvas_div.blur();\n", |
|
" event.shiftKey = false;\n", |
|
" // Send a \"J\" for go to next cell\n", |
|
" event.which = 74;\n", |
|
" event.keyCode = 74;\n", |
|
" manager.command_mode();\n", |
|
" manager.handle_keydown(event);\n", |
|
" }\n", |
|
"}\n", |
|
"\n", |
|
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", |
|
" fig.ondownload(fig, null);\n", |
|
"}\n", |
|
"\n", |
|
"\n", |
|
"mpl.find_output_cell = function(html_output) {\n", |
|
" // Return the cell and output element which can be found *uniquely* in the notebook.\n", |
|
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", |
|
" // IPython event is triggered only after the cells have been serialised, which for\n", |
|
" // our purposes (turning an active figure into a static one), is too late.\n", |
|
" var cells = IPython.notebook.get_cells();\n", |
|
" var ncells = cells.length;\n", |
|
" for (var i=0; i<ncells; i++) {\n", |
|
" var cell = cells[i];\n", |
|
" if (cell.cell_type === 'code'){\n", |
|
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n", |
|
" var data = cell.output_area.outputs[j];\n", |
|
" if (data.data) {\n", |
|
" // IPython >= 3 moved mimebundle to data attribute of output\n", |
|
" data = data.data;\n", |
|
" }\n", |
|
" if (data['text/html'] == html_output) {\n", |
|
" return [cell, data, j];\n", |
|
" }\n", |
|
" }\n", |
|
" }\n", |
|
" }\n", |
|
"}\n", |
|
"\n", |
|
"// Register the function which deals with the matplotlib target/channel.\n", |
|
"// The kernel may be null if the page has been refreshed.\n", |
|
"if (IPython.notebook.kernel != null) {\n", |
|
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", |
|
"}\n" |
|
], |
|
"text/plain": [ |
|
"<IPython.core.display.Javascript object>" |
|
] |
|
}, |
|
"metadata": {}, |
|
"output_type": "display_data" |
|
}, |
|
{ |
|
"data": { |
|
"text/html": [ |
|
"<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAA10dzkAAAgAElEQVR4nOyde3wU5aG/XzbcEkICCKjcNKKCiqZWsFqqHKVCUFtEvPVAFaSC+vPgrYJ3rGBFqsgtKjeDctAaKJVWqYgU1CoqHEEDxAoxQBCQ+4JKAoHv7w/dbTY7m2xCdnbe3efx8/yRye4wO3kn7+PMzsYIAAAAAJIKE+8NAAAAAAB3IQABAAAAkgwCEAAAACDJIAABAAAAkgwCEAAAACDJIAABAAAAkgwCEAAAACDJIAABAAAAkgwCEAAAACDJIAABAAAAkgwCEAAAACDJIAABAAAAkgwCEAAAACDJIAABAAAAkgwCEAAAACDJIAABAAAAkgwCEAAAACDJIAABAAAAkgwCEAAAACDJIAABAAAAkgwCEAAAACDJIAABAAAAkgwCEAAAACDJIAABAAAAkgwCEAAAACDJIAABAAAAkgwCEAAAACDJIAABAAAAkgwCEAAAACDJIAABAAAAkgwCEAAAACDJIAABAAAAkgwCEAAAACDJIAABAAAAkgwCEABiQl5enowxWrFiRbWP7dGjh3r06BH7jQJX6N69u3r27BnvzQCAKiAAk4iaTMiJTmFhoe677z5lZ2crPT1dJ5xwgi6//PKI+2bLli269tprlZmZqaZNm+rXv/61ioqKwh733HPP6ZprrlH79u1ljNFNN90UcRvefvttde/eXampqWrWrJn69++v4uLiqLa/R48eMsbo1FNPjbhuY4yMMZo7d25U66wtubm5ysvLC1vuhQDcu3evGjVqJGOM1q1bV+frt52HHnooOE6MMWrQoIFOPvlk3Xnnndq3b1+t11vXATh9+vSQ7UxJSVHbtm01ePBgff3117Ve7+zZszVx4sQ6204AmyAAkwgC8D/ce++9atasmYYMGaKpU6dq3Lhx6tixo1JSUrR48eKQxx44cECnnXaaWrduraeeekrjx49X+/bt1a5dO+3atSvksSeddJJatGihnJwc1a9fP2IA/v3vf5fP51PXrl01ceJEjR49Wi1btlTbtm21Y8eOare/R48eaty4sYwx+vjjj8O+f9NNNwW/H+sAPOussxzjzQsBOG3aNDVu3FgnnHCCHnrooTpfv+0EAnDq1KmaPXu2nn/+efXv31/GmGP6eZSVlenQoUN1tp2BAHziiSc0e/ZsTZ8+XTfffLN8Pp9OO+00lZaW1mq9vXv3VseOHetsOwFsggBMIhIlAL/99ttjXsfKlSt14MCBkGW7du1Sq1at1L1795DlTz31lIwx+uSTT4LLCgsLlZKSogceeCDksRs3btTRo0clSU2aNIkYgGeeeaZOPfVUlZWVBZetXr1aPp9P99xzT7Xb36NHD5111lnq1KmT7rrrrpDvHTx4UBkZGcGJPJkD8OKLL9bVV1+tu+++W1lZWXW+/mj57rvv4vZvV0UgAPfu3RuyPDB2/u///i9OWxZKIABXrVoVsvzee++VMUZ/+ctfarVeAhCSGQIwiag8IX/22We66aablJWVpUaNGun444/X4MGDw85qjRo1SsYYrV+/XjfddJMyMzOVkZGhQYMGhUxsxcXFMsY4Xg40xmjUqFHBrzdu3KjbbrtNp59+uho3bqwWLVrommuuCbsEGtjmZcuW6bbbblOrVq3UrFkz/fOf/5QxRvPnzw/7t+bMmSNjjD788MMa76Orr75aLVq0CFnWrVs3devWLeyxvXr1qnLyiBSAu3fvljFG9913X9j3zjrrLLVp06ba7QwE4GOPPaYTTzxRR44cCX4vPz9f9evX12uvveYYgJ9++qlycnLUtGlTNWnSRJdeeqmWL18e8pjAfv/Xv/6lu+++Wy1btlRaWpquuuqqkDOUJ510UsiluYpnjqJdR+D1BJ534MABpaWlafjw4WGvu6SkRD6fT3/84x+r3UebNm1SvXr1lJ+fr48//ljGGH3wwQchjxk2bJiaNm2qgwcPhj3/mmuuUZs2bUL27RtvvKHu3bsrLS1NTZs21ZVXXhl2aXnAgAHKzMzUl19+qZycHKWnp6t///6SpKVLl6p///5q166dGjZsqPbt2+uee+5x/PdfffVVde7cWY0aNVKXLl20YMECDRgwIGzMHTlyRM8884zOOOMMNWzYUMcff7xuvfXWqC7hRgrACRMmyBij/Pz8kOUzZszQJZdcolatWqlRo0Y688wzNXXq1LD1Vr4EvHjx4mCoPf7442rTpo0aN26snj17Or6VojKRAvD111+XMUbjxo0LWT5//nz16dNHJ554oho2bKiOHTtqzJgxIT/L7t27h43divv24MGDeuSRR3TKKacEf1YjR44M+Z82AJshAJOIygH49NNP66KLLtLjjz+uadOm6c4771RqaqrOP//84Fks6T8BeO655+rqq6/Wc889p9/97ncyxmjEiBHBx9UkAOfOnavs7Gw9+uijmjZtmh588EE1b95cJ510UkhUBrb5zDPPVI8ePTR58mSNHTtWR48eVfv27YMTa0Uuv/zyWv9f/c9//nOdfvrpwa+PHDmiRo0a6bbbbgt77MMPPyxjjPbv3++4rkgBuHXrVhlj9Oijj4Z9r1u3bjLGaNu2bVVuZyAAv/zySxljtGTJkuD3rrrqKvXu3VtLly4NC8A1a9aoSZMmOvHEEzV69GiNHTs2+D8AH330UfBxgf1+7rnn6tJLL9XkyZN17733KiUlRdddd13wcX/961/Vrl07de7cWbNnz9bs2bP19ttv12gdgddT8QzggAEDdPzxx6u8vDzkcePGjVO9evW0adOmKvePJI0dO1bp6en6/vvvJUkdO3bU7bffHvKYSP8jceDAAaWmpurOO+8M2Sf16tXTFVdcERyHHTp0UIsWLUK2Z8CAAUpLS1NWVpYGDx6sqVOn6n//938lSbfddpuuuOIKPfnkk5o6daoGDx6slJQU3XDDDSH//oIFC1SvXj395Cc/0bPPPquHH35YzZs311lnnRU2tgcNGqQGDRpo6NCheuGFFzRixAilpaXpggsu0OHDh6vcR5EC8K677pIxJuztED/96U91880369lnn9XkyZP1y1/+UsYYvfDCCyGPixSA5557rrp166YJEybo0UcfVWpqqn7+859XuY1S5AAMhOr06dNDlv/qV7/S9ddfrz/96U967rnngmc077///uBjFi1apHPOOUfHH398cOy+/vrrkn447nv27KkmTZronnvu0dSpU3X77berfv36jr9zAGyEAEwiKgdgYGKsyKuvvipjjN57773gskAA3nzzzSGP7devn4477rjg1zUJQKd/e/ny5TLG6OWXXw7b5l/84hdhMfDAAw+oUaNGIWc6duzYofr164f8W9Hy3nvvqV69enrkkUeCy3bu3CljjB5//PGwx+fm5soYoy+++MJxfZEC8MiRI2rWrFnYm+R37dqlJk2ayBijlStXVrmtgQCUpK5du2rIkCGSfrjpoWHDhnrppZccA/Cqq65Sw4YNQ866bN26VU2bNtXFF18cXBbY77/85S9D/mfg7rvvVkpKSsg+r+4ScDTrqByAixYtkjFG//jHP0LWec4550R9qfjss8/WgAEDgl8/+OCDatmyZUgUHTlyRCeccIKuv/76kOe+8sorIWeR/X6/MjIywv5HYOvWrWHLBwwYIGOMHn744bBtchr3o0ePVr169bRly5bgsjPOOEMnnXRSyNsd3nnnnbCzVIGf8WuvvRayzjfeeMNxeWUCAVhUVKSdO3equLhYM2bMCL5vsvKZSaft79mzZ8j/NEmRA7BLly4h7w185plnZIxRYWFhldsZCMClS5dq586dKikpUX5+vlq2bKnU1FRt3bq12u0cMmSI0tPTQ/79SJeA8/Ly5PP5wq4iTJkyJeL7bgFsgwBMIqp6T9bBgweDE4AxRhMmTAh+LxCAFd8DJ0njx4+XMUZ+v19SzQKwIocOHdKuXbu0c+dONWvWLOQ9bYFtfumll8KeV1hYKGOMZsyYEVw2efLk4OXqmvDNN9+oXbt2OuWUU0LeG7h582YZY/TUU0+FPWfmzJmOZyUCVPUewJEjRwbPSHz55ZdauXKlLr30UjVo0EDGGL3//vtVbm/FABw/fryaN2+usrIyTZ8+Xampqdq/f39YAJaXlystLS3s7Jv0w6VQn88X/FkG9nvlS4Dz58+XMUafffZZcFl1ARjNOioH4JEjR9SmTRsNHDgwuKygoMDxbI8Tn332mYwxeuONN8KeX3GZJN1xxx1q0qRJyJnnvn37qkOHDsFwzc/PD/6P0c6dO0Ps2bOnOnfuHHxuIACruzv122+/1c6dO4NnIQPbtWnTpohniM8444yQYLn99tvVokWLsG3asWOHUlNTdeutt1a5DZXvAg6YnZ2tTz/9tMrn7tu3Tzt37tTo0aNljAmJ1UgBOH78+JB1fPLJJzLG6M0336zy36p8F3DAU045JewsZWX279+vnTt3atasWTLGaM2aNcHvRQrAyy+/XNnZ2WH7dd26dTLGaOzYsVX+mwA2QAAmEZUDcPfu3Ro+fLhat24d9ov1D3/4Q/B5gQDcvn274/o2btwoqeZnAB955BG1a9dO9erVC/m3Bw8eHPZvVDwjWZFu3brpkksuCX59wQUX6IILLqjRfvn222/VrVs3ZWZmqqCgIOR7sTgDKP1wl+SQIUPk8/mCr7tXr1669dZbq4zKABUDcOvWrfL5fHr99df1X//1X7r22mslKSwAt23bJmNMyBnOAIFLaYHJMbDfK14WrrjOZcuWBZdVF4DRrMPpJpD77rtP6enpwTC7//771bhx46je23bfffepSZMmWrdundavXx/05JNP1m9+85uQx77//vshZ8v8fr8aNWqk3//+98HHPPHEE44BErDi+0YHDBigRo0aOW5XcXGxfvvb36p58+Zh65gzZ46kH85EVz4THuBXv/pVSLBcdtllVW7X1VdfXeV+CgTg66+/rsWLF2vOnDk6//zzdeKJJzqelXvvvfd0ySWXKC0tLezfqhi8kQJw3rx5Ietbv369jDHBS+SRCATgCy+8oMWLF2vu3LnKyclRRkaG4++Gzz//XL/+9a/VtGnTsO2s+D7QSAF42mmnVblfo7lRC8DrEIBJROUA7Nmzp1JTU/Xoo49q/vz5evvtt/XWW2+FxVogAHfu3Om4vsCNGxs3bnQMwPLy8rB1BuLnnnvu0dy5c/X2229r8eLFOu6440Kiqbo7SSdPniyfz6eSkhJt2LBBxhhNmTIl6n1SVlamXr16qVGjRiFBEiAW7wGsyPbt2/Xee+/p3//+tyTpN7/5jXw+X9gdypWpGICSdOmll+qiiy6Sz+fTX//6V0l1E4CV93tgnUuXLg0uq+ldwE7rcArAwBm7OXPm6OjRo+rQoYOuueaaKveLJB09elTt2rWLOHmnpaWF7N/A4wPv7Xr55ZfDtjtwluuVV17R4sWLw6w4dgI3gVTm8OHDOvXUU9WqVSs99dRTWrBggRYvXhw8kzx79mxJNQvAnj176sQTT3TcpsWLF4ecZXXC6T2A+/fvV4cOHXTeeeeFXLr/97//rYYNG+rcc8/VCy+8oDfffFOLFy/W8OHDZYxRSUlJ8LGRAjAwNgMEAjDw2iPh9B7Aw4cPq1u3bmrXrl3I2dvdu3erefPmOuWUUzRp0iT9/e9/1+LFi/XHP/4x7Ox6pADs2LGjfvKTn0Tcr4HjFcBmCMAkouKEvGfPnrAzfZKCNxXUJgD9fr+MMXr22WdDHldUVBS2zszMzJAzfdIPl6FTUlJqFIA7d+5UgwYNNG7cOP3hD39QgwYNwrYzEkeOHNH111+vlJSUKj9GomvXro53AV922WU65ZRTIj4vmgCsSHl5uU488URdeOGF1T62cgDOmDFDxhg1a9YseJdiTS4B33rrrY6XgKOJty5dusQkACXp3HPP1eWXX653331XxhgtWLCgyv1Scf2PP/645s6dG+K0adMcg+Oee+5RamqqDhw4oCuuuCIsCgLvCax4s00kIgXgihUrQs70BVi4cGHINtXkEvDQoUPVoEGDWn8OXqSbQALBVfH9o3/605/CzvRJ0ogRI+ISgBXX+6c//Sm4bO7cuWFn+qQfPqS9cgDm5OQ4BmCvXr3UoUOHKrcJwHYIwCSi4oQciLXHHnss5DG33357rQNQklq2bKl+/fqFPC7wWV0V19miRQsNGjQo5HHjxo2TMaZGAShJv/71r3XOOefo9NNP169+9atq9kL4a3X6GIuKjB07NmwbvvjiC6WkpGjkyJERn1fTAAz8O5UvkzlROQD37dunUaNG6ZVXXgkui3QTSKNGjUJ+Ztu3b1dGRobjTSDRxNvPfvYzZWdnh21jXQTg+PHjVb9+/eANR9F8uPCQIUPUpEkTx49WkX64vJeTkxOyLPAxMVOmTFGDBg304IMPhnx/7969atq0qS699FLHO2srfqxNpAD89NNPwy53HjlyRDk5OWER1Llz56huAgksczqre+jQoWovl0cKwLKyMp144ok677zzgssC7/mteLPKnj17dPzxx8ctAKUf7kxu06ZN8H98Au8x/de//hV8TGlpqc4+++ywAOzfv3/IjWwBAv9DNXPmzLDvfffdd579XEeAmkAAJhGVJ+SLL75YaWlpeuihh/Tcc8/pqquuUnZ29jEF4P333y9jjIYMGaLnn39ev/nNb3TeeeeFrfPGG29USkqK7rzzTk2dOlWDBg1Su3btanwJWJLmzZsXvLxX3V2PAZ599lkZY3ThhRcGPwKiohUn3v3796tjx45q3bq1xo0bp2effVbt27dXmzZtwj7P7m9/+5tGjx6t0aNHBy+XBb6ueDlu9uzZuuqqqzR+/HhNmzZN1113nYwx+t3vfhfV9lcOQCeq+hiYtm3b6oknntBTTz2lU045JeLHwEQTb7fffrvq1aun0aNH69VXXw2eJauLANy+fbvq168vY4zjZfjKlJaWqlmzZrrqqqsiPubee+9V/fr19c0334Qsz8rKCr5nzOnS6UsvvSSfz6ezzz5bTzzxhKZOnaoHH3xQ2dnZIR8XEykAy8rKlJWVpVatWumPf/yjJk2apIsvvjh4zFWMoEDEVP4YmC5duoT9+b8hQ4bIGKMrrrgi+PEsw4cP14knnhgWXJWJFICS9OSTT8qY/3wUzNq1a9WgQQNlZ2drypQpevLJJ5WVlRXc/ngFYOCTCwI3B+3YsUOZmZnKysrS+PHj9cwzzyg7Ozu4nRUDMHBZ+N5779Urr7wSvBGnvLxcvXv3Vr169fSb3/xGkydP1rPPPqthw4apefPm1b5HF8AGCMAk4sUXX5QxJnh335YtW9SvXz81a9ZMmZmZuvbaa4OfUVfbAPz+++81ZMiQ4N/Mve6667Rjx46wde7du1eDBw9Wy5YtlZ6ert69e+uLL77QSSedVOMALCsrU/PmzZWZmRnxrE9lbrrppojvEav8mqQfPoD4mmuuUUZGhtLT03XllVc63mlc1Xorvjfy448/1sUXX6zmzZurcePGys7O1gsvvBDynquqqG0ASj+cierdu7fS09OVlpamSy65JOzjLmoSb9u3b9cVV1wRjKfKHwR9LAEo/XBHpjHRfbD3X/7yl4hnbgIsW7ZMxpiwvwEbuDO74h29lVmyZIkuu+wyZWRkKDU1VaeeeqoGDx4c8hczIgWg9EOAX3rppWrSpIlatWqlYcOGBc8MVo6gOXPm6PTTT1fDhg119tln64033lDfvn3VpUuXkMcdPXpUL7zwgn76058qNTVVGRkZOuecczRy5MhqP0+yqgAMnPWsGHKvv/66unTpokaNGikrK0tPP/108LJ6vAKwvLxcJ598sk4//fTgBz2///77Ov/885Wamqo2bdrogQceCF5qrxiA+/fv1w033KBmzZqFnV09dOiQnnzySZ155plq2LChmjdvrq5du+rxxx+P+L5fAJsgAJOIiRMnyhijDRs2xHtT6pTDhw+rVatWYZ9TCInBVVddxZ/r+pGzzjor7PI1AEBtIACTiIEDB6pJkybV/nUA2wi86dvpLl6wm61bt6pBgwZh71VNdA4dOhT2weeBs2h8Bh0A1AUEYBIwb9483XHHHapXr55uueWWeG9OnfHRRx9p2rRpat++vc4999x4bw7UIV999ZVmz56tHj16KC0trdpLmYnG+vXrddppp+mxxx7T1KlTddddd6lx48Zq27at9uzZE+/NA4AEgABMAk4++WQdd9xx+t3vfhdyc4Pt3HTTTUpJSdF5550X9gHOYDeB9w926NAh7D2MycCePXt07bXXqk2bNmrQoIFatGiha6+9NuRP+AEAHAsEYBWUlpYG7xyLdNfX+vXrlZ6eHvFN3xXZtGmTLr/8cqWmpqpVq1b6/e9/n3CXYwEAAMD7EIBVMHz4cPXp0ydiAB46dEhdu3ZVnz59qg3A8vJydenSRb/85S+1atUqLVy4UC1bttQDDzwQq80HAAAAcIQAjMDChQvVuXNnrV27NmIAjhgxQgMHDlReXl61Abhw4UL5fL6Qv6f7/PPPKyMjI/gBpgAAAABuQAA6sH37drVt21YrVqxQcXGxYwAuWbJEWVlZ8vv9UQXgI488EvbXEr766quQz+UDAAAAcAMCsBJHjx5VTk6ORo8eLUmOAbhr1y61b99e7777riRFFYC33HKLevXqFbLsu+++kzFGCxcujPi80tJS+f3+oHv37lVRUZH27dsXshwREb3tvn37VFJSEvzAaoB4kjQBGPiU/6osLCzUxIkT1b179+BncDkFYL9+/UL+BmwsAzDwVzgQETExrPhXUwDiRdIE4I4dO1RYWFilZWVl6tu3r3w+n1JSUoIaY5SSkqIbb7xRkpSZmRnyfZ/PF3xMpD9BVdtLwJXPAG7evFnGGD1fMlaz/BMQY+47W66rsfHeZrf3z+4tExCrtbhwrIwx2rdvX91NbgC1JGkCMFo2bdqkgoKCoIsWLZIxRvPmzQv+X9u6detCHjNmzBg1bdpUBQUFET+kNXATSMU/QD916lRlZGSotLQ06u3z+/0yxmiWf4LyNRUR4+zy/QN0eP9Uq12+f0BE471tieTuLRNkjJHf7z+2iQqgDiAAqyHSTSAVcboEPH/+fHXq1Cn4deBjYHr16qXVq1frrbfeUqtWrWr8MTAEIKL3tDWUKoZeVa+NMCQAIfEgAKuhtgEY+EsGFdm4caP69Omj1NRUtWzZUvfee2+NPwiaAET0njZGUXXhF83rJQwJQLAXAtAyCEBEb2pL/BxL+EWzDwhDAhDsgAC0DAIQ0Zt6PXSiudwb632T7FFIAIKXIAAtgwBE9K5ejZp4hV80+yuZwpAABC9BAFoGAYjoXb0WMF4Nv2j3o9f2JwEIiQQBaBkEIKK39UKwxPNybyz2Z6KEIAEIXoIAtAwCENHbxjtWEiX8Iu1Xm0OQAAQvQQBaBgGI6H3jESmJGn5O+9bWECQAwUsQgJZBACJ6XzcDJZEu93p5PxOAkGgQgJZBACLaoRthkqzh57SvbQhBAhC8BAFoGQQgoj3GKkoIv8j728sxSACClyAALYMARLTHuo6RZL7cW5v97rUQJADBSxCAlkEAItplXUUI4Ve7fe+lECQAwUsQgJZBACLa5bEGCOFXdz+DeIcgAQheggC0DAIQ0T5rEh9Ofxot3tufKMY7BAlA8BIEoGUQgIh2Gik+CD5v/TwIQEgWCEDLIAAR7Zbg855uxeA7W64jAMEzEICWQQAiIsbGymF+rMFXeX0EIHgJAtAyCEBExNhb0xiM5szuLD+XgME7EICWQQAiIrpr5RCs7WV8AhC8BAFoGQQgImL8PJb3bRKA4CUIQMsgABER7ZQABC9BAFoGAYiIaKcEIHgJAtAyCEBERDslAMFLEICWQQAiItopAQheggC0DAIQEdFOCUDwEgSgZRCAiIh2SgCClyAALYMARES0UwIQvAQBaBkEICKinRKA4CUIQMsgABER7ZQABC9BAFoGAYiIaKcEIHgJAtAyCEBERDslAMFLEICWQQAiItopAQheggC0DAIQEdFOCUDwEgSgZRCAiIh2SgCClyAALYMARES0UwIQvAQBaBkEICLGwuX7B8R9GxJdAhC8BAFoGQQgIsbC5fsHEIExlgAEL0EAWgYBiIixkACMvQQgeAkC0DIIQESsawPxRwTGVgIQvAQBaBkEICLWtcv3D9Dh/VN1eD/vBYylBCB4CQLQMghARKxrKwcgERgbCUDwEgSgZRCAiFjXVgxAzgLGTgIQvAQBaBkEICLWpZXjj7OAsZMABC9BAFoGAYiIdalTAHIWMDYSgOAlCEDLIAARsS6tKgCJwLqVAAQvQQBaBgGIiHVlIPKcApCzgHUvAQheggC0DAIQEevKquKPCKx7CUDwEgSgZRCAiFhXEoDuSgCClyAALYMARMS6MpoAJALrTgIQvAQBaBkEICLWhdHGHwFYdxKA4CUIQMsgABGxLqzuBhACsO4lAMFLEICWQQAiYl1ZXQDyUTB1KwEIXoIAtAwCEBHryqrOAhJ/dS8BCF6CALQMAhAR61KnCCT+YiMBCF6CALQMAhAR69qKAUj8xU4CELwEAWgZBCAi1rWB6CP+YisBCF6CALQMAhARYyHxF3sJQPASBKBlEICIiHZKAIKXIAAtgwBERLRTAhC8BAFoGQQgIqKdEoDgJQhAyyAAERHtlAAEL0EAWgYBiIhopwQgeAkC0DIIQEREOyUAwUsQgJZBACIi2ikBCF6CAKyC0tJSZWdnyxijVatWOT5m/fr1Sk9PV2ZmZpXrWr16tW644Qa1a9dOjRs3VufOnTVhwoQabxMBiIhopwQgeAkCsAqGDx+uPn36RAzAQ4cOqWvXrurTp0+1AThz5kwNHz5cy5YtU1FRkWbPnq3U1FRNnjy5RttEACIi2ikBCF6CAIzAwoUL1blzZ61duzZiAI4YMUIDBw5UXl5etQHoxO23365LLrmkRs8hABER7ZQABC9BAK3nNuQAACAASURBVDqwfft2tW3bVitWrFBxcbFjAC5ZskRZWVny+/21DsABAwaof//+NXoOAYiIaKcEIHgJArASR48eVU5OjkaPHi1JjgG4a9cutW/fXu+++64k1SoAP/jgA9WvX1+LFi2q8nGlpaXy+/1BS0pKCEBERAslAMFLJE0Ajhw5UsaYKi0sLNTEiRPVvXt3lZeXS3IOwH79+mnkyJHBr2sagAUFBWrZsmUwMqti1KhRjttKACIi2iUBCF4iaQJwx44dKiwsrNKysjL17dtXPp9PKSkpQY0xSklJ0Y033ihJyszMDPm+z+cLPmbmzJlVbsfatWvVunVrPfjgg1FtN2cAERETQwIQvETSBGC0bNq0SQUFBUEXLVokY4zmzZunkpISSdK6detCHjNmzBg1bdpUBQUF2rNnT8R1r1mzRq1bt9Z9991X6+3jPYCIiHZKAIKXIACrIdJNIBVxugQ8f/58derUKfh1QUGBWrVqpYEDB2rbtm1Bd+zYUaPtIQAREe2UAAQvQQBWQ20DMC8vT8b8Z/dGei/fSSedVKPtIQAREe2UAAQvQQBaBgGIiGinBCB4CQLQMghAREQ7JQDBSxCAlkEAIiLaKQEIXoIAtAwCEBHRTglA8BIEoGUQgIiIdkoAgpcgAC2DAEREtFMCELwEAWgZBCAiop0SgOAlCEDLIAAREe2UAAQvQQBaBgGIiGinBCB4CQLQMghAREQ7JQDBSxCAlkEAIiLaKQEIXoIAtAwCEBHRTglA8BIEoGUQgIiIdupWAB48eFB+v79K9+3bF7astLQ0bF2vv/66zjnnHEnS/v37dd111+naa6/VgQMHYvoaIPYQgJZBACIi2qkbAXjw4EEdd9xxMsZUaXp6etiyUaNGOa6zR48ekqTly5fr4Ycf1kMPPaSPPvooZq8B3IEAtAwCEBHRTt0IwMAc8earr2rZggWOvvnqqzLGqKSkpNozgO+8846ysrI0ZswY7du3L3gGcP/+/TF7DeAOBKBlEICIiHbqZgAue/llrZw3z9FlL7/MexGBALQNAhAR0U5dDcAxY7Ty6acdXTZmDAEIBKBtEICIiHbqagDed59WPvywo8vuu48ABALQNghAREQ7dTUAhw7VyjvucHTZ0KEEIBCAtkEAIiLaqasB2Lu3Vl55paPLevcmAIEAtA0CEBHRTl0NwAsu0Mpf/MLRZRdcQAACAWgbBCAiop26GoBt2mhlu3aOLmvThgAEAtA2CEBERDt1NQCbNNHK9HRHlzVpQgACAWgbBCAiop26GoDGaGUEl/34lz8IwOSGALQMAhAR0U7dDMD3jdGqCL5PAIIIQOsgABER7dTNAPyXMVodwX8RgCAC0DoIQEREO3UzAJcbo4IILicAQQSgdRCAiIh26mYAfmSM1kTwIwIQRABaBwGIiGinbgbgJ8ZoXQQ/IQBBBKB1EICIiHbqZgCuNEZfRHAlAQgiAK2DAEREtFM3A/BTY/RlBD8lAEEEoHUQgIiIdup2AK6PIAEIEgFoHQQgIqKduhmAnxmjryL4GQEIIgCtgwBERLRTNwNwjTHaFME1BCCIALQOAhAR0U7dDMB/G6OtEfw3AQgiAK2DAEREtFM3A3CDMfomghsIQBABaB0EICKinboZgMXGaFcEiwlAEAFoHQQgIqKduhmAJcbIH8ESAhBEAFoHAYiIaKduBuBWY/RtBLcSgCAC0DoIQEREO3UzAHcao7II7iQAQQSgdRCAiIh26mYA7jFG5RHcQwCCCEDrIAAREe3UzQDcZ4yORnAfAQgiAK2DAEREtFM3A9B/nJFaOes/jgAEAtA6CEBERDt1NQBPMlKWs/6TCEAgAK2DAEREtFNXA/BMI53trP9MAhAIQOsgABER7dTVALzESJc567+EAAQC0DoIQEREO3U1AK80Uj9n/VcSgEAAWgcBiIhop64G4BAj3easfwgBCASgdRCAiIh26moADjfSfc76h0cfgLm5uRo2bJhyc3NVXl6unj176vLLL9f27dtj9hrAHQhAyyAAERHt1NUAfMBIf3DW/8APAVhSUiK/3x+0tLQ0bH29e/dWaWmpcnJydOTIEeXk5KhXr16cPUwACEDLIAAREe3U1QCcYKSpzvon/BCAlR01alTY+nJzczV06FBNmTJFJSUluuKKK9S3b18VFRXF7DWAOxCAlkEAIiLaqasBOMVIM531T4n+DCAkLgSgZRCAiIh26moATjfSHGf907kJBAhA6yAAERHt1NUAzDPSa8768whAIACtgwBERLRTVwNwtpH+4qx/NgEIBKB1EICIiHbqagDONdKbzvrnEoBAAFoHAYiIaKeuBuB8Iy1y1j+fAAQC0DoIQEREO3U1AP9mpCXO+v9GAAIBaB0EICKinboagG8b6QNn/W8TgEAAWgcBiIhop64G4BIjfeysfwkBCASgdRCAiIh26mYA7n3f6MgqZ/e+TwACAWgdBCAiop26GYB7PjQq/9zZPR8SgEAAWgcBiIhop24G4O6PjQ6vdXb3xwQgEIDWQQAiItqpmwG441Oj0i+d3fEpAQgEYJWUlpYqOztbxhitWrXK8THr169Xenq6MjMzo17vrl271LZt2x/ep7F3b422iQBERLRTNwPwm9VGB4uc/WY1AQgEYJUMHz5cffr0iRiAhw4dUteuXdWnT58aBWDfvn2D6yUAERGTQzcDcOtao283O7t1LQEIBGBEFi5cqM6dO2vt2rURA3DEiBEaOHCg8vLyog7A5557Tj169NCSJUsIQETEJNLNAPz6C6MDXzv79RcEIBCAjmzfvl1t27bVihUrVFxc7BiAS5YsUVZWlvx+f9QBuHbtWp1wwgnatGmTli5dGlUAlpaWyu/3By0pKSEAEREt1M0ALPnSyL/N2ZIvCUAgAMM4evSocnJyNHr0aElyDMBdu3apffv2evfddyUpqgAsLS3VOeeco9mzZ0tS1AE4atQoGWPCJAAREe3SzQDcVGy0d7ezm4oJQEiiABw5cqRjSFW0sLBQEydOVPfu3VVeXi7JOQD79eunkSNHBr+OJgDvvvtuXX/99cGvOQOIiJhcuhmAxZuMdu11tngTAQhJFIA7duxQYWFhlZaVlalv377y+XxKSUkJaoxRSkqKbrzxRklSZmZmyPd9Pl/wMTNnznT897Ozs0PWW/E5jz76aNSvg/cAIiLaqZsBuH6z0fZ9zq7fTABCEgVgtGzatEkFBQVBFy1aJGOM5s2bp5KSEknSunXrQh4zZswYNW3aVAUFBdqzZ4/jejds2BDynBdffFHGGH344Yf65ptvot4+AhAR0U7dDMCCr402HnC24GsCEAjAaol0E0hFnC4Bz58/X506dYr4nGgvAVeGAEREtFM3A3DVVqMN3zq7aisBCARgtdQ2APPy8mRM5N1LACIiJpduBuBH3xitOejsR98QgEAAWgcBiIh17f/sGRA03tuSyLoZgP/aYbS61Nl/7SAAgQC0DgIQEevSQPhN3TKVCIyxbgbgsp1GK8ucXbaTAAQC0DoIQESsKyvGX0AiMHa6GYCL9hj9q9zZRXsIQCAArYMARMS60Cn+iMDY6mYAvrnXaNkRZ9/cSwACAWgdBCAiHotVhR8RGFvdDMBX/Eavy9lX/AQgEIDWQQAiYm2NNv6IwNjoZgC+4Dd6Sc6+QACCCEDrIAARsTbWNP6IwLrXzQAc5zeaLGfHEYAgAtA6CEBErKm1jT8isG51MwCH+43uk7PDCUAQAWgdBCAiRuuxhh8RWLe6GYCD/UbD5OxgAhBEAFoHAYiI0ViX8UcE1o1uBmB/v9ENcrY/AQgiAK2DAETE6oxF/BGBx66bAfgzv1F3OfszAhBEAFoHAYiIVRnL+CMCj003A/AMv1EXOXtGDQIwNzdXw4YNU25uro4ePapBgwZpzJgx+v7772P2GsAdCEDLIAARMZJuxB8RWHvdDMAMv1GmnM34MQBLSkrk9/uDlpaWhq2vd+/eKi0tVU5OjrZv366zzjpLDz30kPbu3Ruz1wDuQABaBgGIiE66GX9EYO10MwCN3yjifz8GYGVHjRoVtr7c3FwNHTpUU6ZM0ddff63rr79egwYN0saNG2P2GsAdCEDLIAAR0Um3469iBMb7tduiqwG4x8iUR3BP9GcAIXEhAC2DAEREJwlA7+tqAH5jZA5G8BtuAgEC0DoIQER0Mp4BSARGp6sBuNXIfBvBrQQgEIDWQQAiYmXjFX+cBayZrgbgJiOzN4KbCEAgAK2DAETEynohAInA6nU1AIuNzK4IFhOAQABaBwGIiJWNdwByFjA6XQ3AIiOzI4JFBCAQgNZBACJiRePx8S8EYO10NQALjcyWCBYSgEAAWgcBiIgV9UL8cRk4Ol0NwLVGZnME1xKAQABaBwGIiBX1SgByFrB6XQ3Az398r5+TnxOAQABaBwGIiBUlAO3R1QD81Mh8GcFPCUAgAK2DAETEgF55/x8RGJ2uBuCKH9/r5+QKAhAIQOsgABExoNfijwCsWlcD8MMfL/U6+SEBCASgdRCAiBjQqwFIBDrragC+b2RWRfB9AhAIQOsgABExoBcDkLOAkXU1AJcZmZURXEYAAgFoHQQgIubLm+//IwCr1tUAfMfILI/gO7ENwEmTJmnmzJkxWTfUHQSgZRCAiJgv75794zJwZF0NwEVG5l8RXBS7AJwxY4b+3//7fwSgBRCAlkEAImK+vB2AnAV01tUAfMPILI3gG7ELwPz8fG3evLnO1wt1DwFoGQQgIubLjgAkAkN1NQAX/Hip18kFsb0EvHz5cr322msxWTfUHQSgZRCAiOj1+OMsoLOuBuBfjczbEfxrbANwypQpXAK2AALQMghARCQA7dTVAMz/8VKvk/mxDcCxY8fqpZdeism6oe4gAC2DAEREmwKQCPyPrgbgn43M3yL459gG4GuvvaYjR47EZN1QdxCAlkEAIia3Xv74F84CVq2rATjnx0u9Ts6JbQA+88wzXAK2AALQMgjA5DEw0VdnvLcT3R8X8Y46ArB2uhqAs3681OvkrNgG4LRp0/Tuu+/GZN1QdxCAlkEAet9ow606ucSGkcZXvKOOCKydrgbgTCPzagRnxjYAn3/+eRUVFcVk3VB3EICWQQDGx7oOt7qeXJlgk0cC0F5dDcCpRublCE6NbQA++eSTXAK2AALQMgjA+Oj1SZcITA5te/8f4zNUVwPwOSOTF8Hn+FvAQABaBwHovrZMukyyia8N47Cq8Rnv/RdvXQ3AyUZmRgQnE4BAAFoHAei+Nk26RGBia9NYJADDdTUAJzaSmdbY2YmNCEAgAG2DAHRXW87+EYHJoW1jkXEZqqsBOKm5zPTjnJ3UnAAEAtA2CEB3tX3Cjff+w7odizaPR8akywGYe5rMi52dzT2NAAQC0DYIQHdlwkWvaPtYDIzHZB6T7t4E8hOZvPOcfe4nBCAQgLZBALpnIky4TLqJYyKNx3jvy3jpagC+cKHMSxc5+8KFBCAQgLZBALpnoky4RGBimCjjMZnHoasBOO3XMrP7Ozvt1wQgEIC2QQC6YyK834oITBwTaSwm8zh0NQCnXy8z57fOTr+eAAQC0DYIQHdMpAmXydd+E208JusYdDUAZ9ws88qtzs64OertyM3N1bBhw5SbmytJ+sc//qFTTz01ZtsP7kEAWgYBGHsT8ewfEWi3iTYek3X8uRqAeSNkXnvE2bwRMsaopKREfr8/aGlpadj6evfurdLSUuXk5Ki8vFwjRoxQjx49Yrb94B4EoGUQgHWrF/6WLxMwRjNO4z1mGH/HrqsBOOsxmfyxzs567IfHVHLUqFFh68vNzdXQoUM1ZcoUvffee7rtttt00kknqbCwMGavAdyBALQMAvDYTNbgYwK220Qcp8k4Bt0NwCdk8p9xdtYTUZ8BhMSFALQMAjB6iT0m30QxEcduMo5BdwNwrEz+BGdnjeUmECAAbYMAjDxBEntMvolqIo7nZByD7gbgOJn8yc7OGkcAAgFoGwTgfyZEgo/JN1lMxPGdjGPQ3QB8Vib/BWdnPUsAAgFoG8kYgMQek2+ym4hjPhnHoLsBOEkmf7qzsyYRgEAA2kYyBCCxx+SL4cdEvMcMY/DYdTcAn5fJn+XsrOcJQCAAbSPRA5DoY/JF5+Mi3mOGMXjsuhuA02TyZzs7axoBCASgbSRyABJ/TMAY+diI93hh/B277gbgdJn8Oc7Omk4AAgFoG4kagMQfEzBWfXzEe7ww/o5ddwPwJZn8uc7OeokABALQNhItAAk/JmCM7jiJ93hh/B27rgbgi3Nk/vxXZ1+cQwACAWgbiRSAxB8TMEZ/rMR7vDD+jl1XA3DGPJlX/uHsjHkEIBCAtpEoAUj8xd9knIBtNRGPlWQcf64G4LTXZf53sbPTXicAgQC0jUQIQOLPGybjBGyriXi8JOP4czcA35CZvdTZaW8QgEAAVkVpaamys7NljNGqVascH7N+/Xqlp6crMzMzqnXm5eXp7LPPVqNGjdSqVSvdfvvtNdommwOQ8POWyTgB22qiHjfJNgZdDcDn35GZtdzZ598hAIEArIrhw4erT58+EQPw0KFD6tq1q/r06RNVAD7zzDNq06aN5syZow0bNuizzz7TggULarRNtgYg8ec9k23ytdlEPXaSbQy6GoDPLZXJW+Hsc0sJQCAAI7Fw4UJ17txZa9eujRiAI0aM0MCBA5WXl1dtAO7Zs0epqal65513jmm7bAxA4s+bJtvka7OJevwk2xh0NQBz35d5cZWzue8TgEAAOrF9+3a1bdtWK1asUHFxsWMALlmyRFlZWfL7/VEF4GuvvaZGjRrppZdeUufOndW2bVtde+212rx5c5XPKy0tld/vD1pSUmJVABJ/3jXZJl+bTdRjKNnGoKsBOGmFzPRCZyetIACBAKzM0aNHlZOTo9GjR0uSYwDu2rVL7du317vvvitJUQXgk08+qQYNGqhTp0566623tHz5cvXs2VOdOnVSWVlZxOeNGjXqh4O5kjYEIPHnbZNt8rXZRD2Okm0MuhqAEz+Vmbbe2YmfEoCQPAE4cuRIx5CqaGFhoSZOnKju3burvLxcknMA9uvXTyNHjgx+HU0APvHEEzLGaNGiRcFlO3bskM/n01tvvRXxeTaeAST87DDZJl+bTdTjKdnGoKsB+PRqmdwiZ59eTQBC8gTgjh07VFhYWKVlZWXq27evfD6fUlJSghpjlJKSohtvvFGSlJmZGfJ9n88XfMzMmTMd//0XX3xRxhiVlJSELG/durWmTZsW9evw+nsAiT97TLbJ12YT9ZhKtjHoagA+tVpmUpGzTxGAkEQBGC2bNm1SQUFB0EWLFskYo3nz5gXjbd26dSGPGTNmjJo2baqCggLt2bPHcb3//ve/ZYwJuQlk9+7d8vl8IWcFq8PLAUj82WeyTcC2mqjHVbKNP1cDcOxqmQlFzo4lAIEArJZIN4FUxOkS8Pz589WpU6eQZX379tVZZ52lDz74QAUFBbryyit15pln6tChQ1Fvj1cDkPiz02SbgG01UY+tZBt/rgbg46tlxhU5+zgBCARgtdQ2APPy8mRM6O71+/26+eab1axZM7Vo0UL9+vWr9i7gyngxAIk/e022CdhWE/X4Srbx52oAPrZaZmyRs48RgEAAWoeXApDws99km4BtNVGPs2Qbf64G4IOrZR4vcvZBAhAIQOvwSgASf4lhsk3Atpqox1qyjT9XA/D+1TKPFTl7PwEIBKB1eCEAib/EMdkmYFtN1OMt2cafqwE4YrXMI0XOjiAAgQC0jngHIPGXWCbbBGyriXzMJdMYdDUA714tc3+Rs3cTgEAAWke8ApDwS0yTafK12UQ+9pJpDLoagLetlrmryNnbCEAgAK0jHgFI/CWuyTT52mwiH3/JNAZdDcDBq2WGFTk7mAAEAtA63A5A4i+xTabJ12YT+RhMpjHoagD2XS1zTZGzfQlAIACtI3BwD9t4nSuTTiJPPJhck6/NJvJxmExj0NUA7LVa5ooiZ3sRgEAAWkfg4J5QOCEYaAHresJJ5EkHk2/ytdlEPhaTaQy6GoBdV8tcUORsVwIQCEDrqBiAlX+J1kUMEn7JZzJNwLaayMdkMo0/VwOwy2qZ7CJnuxCAQABaR6QArPwLtTYxSPwlp8k0AdtqIh+XyTT+XA3AU1bLnFbk7CkEIBCA1hFNAFb+5RpNDBJ/yWsyTcC2msjHZjKNP1cDsPlqmeOKnG0efQDm5uZq2LBhys3NVWlpqR599FHl5OTo22+/jdlrAHcgAC2jpgHo9Mu2cgwSf8ltMk3AtprIx2cyjT9XA7DxapnUImcb/xCAJSUl8vv9QUtLS8PW17t3b5WWlionJ0eStHnzZl155ZUqLy+P2WsAdyAALeNYzwA6nQkkAJPXWNxAhHVvxWM33mOmrsdeMo0/VwPQ/J+M+XcE/+/Hx4Q6atSosPXl5uZq6NChmjJlijZt2qSsrCzdeeed2r17d8xeA7gDAWgZVQVgNLFX1QQT7wkB4zMBx3tSxOhNhAhMxvAL6G4Afixj1kbw46jPAELiQgBaRsXPAaxt7CXqxII1n4TjPSFi7Y9V247XZA6/gO4G4Acy5rMIfsBNIEAA2kYs/xKIjZMK1n4ijvdkiMlzvDLmftDdAFzy45k+J5cQgEAA2kas/xScLRMKMhGj988GMt5CdTcA3/7xTJ+TbxOAQADahht/C9irkwkyGWPkY9ZLxy1jzVl3A3ChjHk3ggsJQCAAbcOtAPTSZIJMyGjHscv7/KrW3QCcL2MWRXA+AQgEoG24EYBemEiw7ifleE9+6I7xuCxM+EWnuwE4V8a8GcG5BCAQgLbhVgAGJpJ4xwvWzcQc74kP3deNCCT8aqa7AThbxvwlgrMJQCAAbcPtACQC7ZWJGWN1NpDwq51uBqDPzFKKyXfUZ2YRgEAA2oabAVhxAol3zCDxh7W3ro5jwu/YdDMA082Lamr+7Gi6eZEABALQNtwOwMDkEe+gQeIPj91jCUHG1bHrZgBmmRnqaF5xNMvMIACBALSNeARgYOKId9ggkzQeuzW9LMyYqjvdDMAzzTSdbf7X0TPNNAIQCEDbiGcAEoHelokaa2J1xzTjqe51MwB7mKnqaV52tIeZSgACAWgb8QrAaCYMJP7QLp3OBvI+v9jpZgDmmOf1KzPL0RzzPAEIBKBtxDMAAxNGvGMHnQMw3pMb2mvF6GMsxU43A7C/eU43mDxH+5vnCEAgAG0j3gEYmCziHTxI/CHappsBOMxM1v+YGY4OM5MJQCAAbcMrAUgEekPiD9Ee3QzAu80k3W+mO3q3mUQAAgFoG14IwHwRgfGWS3WI9ulmAD5kJmq0meboQ2YiAQgEoG14JQDzxaVg4g8Ra6KbAfioGa8/mucdfdSMJwCBALQNrwUgEUj8IWJ0uhmAj5hn9IR5ztFHzDMEIBCAtuGlAMwXEeh2+BF/iPbqZgA+aMbpcTPZ0QfNOAIQCEDb8FoA5otLwW7FX7x/zoh4bLoZgA+Yp/QHM8nRB8xTBCAQgLbhxQDMFxFI/CFidboZgPebsRplJjh6vxlLAAIBaBteDkAisO7Dj/hDTBzdDMAR5o96xIx3dIT5IwEIBKBteDUA80UE1nX8xfvniYh1q5sB+HszRg+Zpx39vRlDAAIBaBteDsB8cSmY+EPESLoZgPeYx/WAGefoPeZxAhAIQNuwIQCJwNqHH/GHmLi6GYB3mcc00ox19C7zGAEIBKBteD0A80UE1jb+4v1zQ8TY6mYA3mbu113mMUdvM/cTgEAA2oYNAZgvLgUTf4hYWTcD8Hpzi35r/p+j15tbCEAgAG3DlgDMFxFI/CFiRd0MwL7mv3WNGeRoX/PfBCAQgLZhWwASgYQfIv6gmwHYy1ylK8y1jvYyVxGAQADahk0BmC8ikPhDTCyP5Rh2MwC7ml/oAvNfjnY1vyAAgQC0DdsCMPALM97h5QWJP0S7Dfwuq+2x7GYAdjE/Vbbp5mgX81MCEAhA27AxACv+4kxGCT9E+3W6mlHTY9vNAGxjOqidOdnRNqYDAQgEoG3YHIDJGIHEH6L9VvX7qybHuJsB2Nwcp+NMK0ebm+Oi3o7c3FwNGzZMubm5OnLkiAYMGKAbbrhBGzdujNlrAHcgAC3D1gCs7pdooskHOyMmjtX93or2WHczABubVDU2aRFMlTFGJSUl8vv9QUtLS8PW17t3b5WWlionJ0fbtm3TwIEDNX36dP35z3+O2WsAdyAALcPmAAz8Ik3kCCT8EBPPise107HupTOABw8e1AknnCBjTJWmp6eHLRs1alTY+nJzczV06FBNmTJFX3/9NWcAEwgC0DJsD8BIv0xtluhDTA5rE30VdSMApR8isOKZPSf37dsXtszpDCAkLgSgZSRCAFb+ZRrvgDvW6CP8EDEa3QpAgGggAC0jkQIwX3adDST6EPFYJADBSxCAlpFoARgwHhFYOeiiMd77CRHtlQAEL0EAWkaiBmC+anY2sDbxRswhYjwlAMFLEICWkcgBGJB4Q8RElAAEL0EAWkYyBCAiYiJKAIKXIAAtgwBERLRTAhC8BAFoGQQgIqKdEoDgJQhAyyAAERHtlAAEL0EAWgYBiIhopwQgeAkC0DIIQEREOyUAwUsQgJZBACIi2ikBCF6CAKyC0tJSZWdnyxijVatWOT5m/fr1Sk9PV2ZmZrXr++STT3TppZcqMzNTzZo1U69evbR69eoabRMBiIhopwQgeAkCsAqGDx+uPn36RAzAQ4cOqWvXrurTp0+1AXjgwAG1aNFCgwYN0hdffKE1a9aof//+Ov7443Xo0KGot4kARES0UwIQvAQBGIGFCxeqc+fOWrt2bcQAHDFihAYOHKi8vLxqA3DFihUyxmjz5s3BZZ9//rmMMVq/fn3U20UAIiLaKQEIXoIAdGD79u1q27atVqxYoeLiYscAXLJkibKysuT3+6MKwP379+u4447TqFGjHgfY3gAAD8hJREFUVFZWpu+//1533nmnzjjjDB0+fDji80pLS+X3+4OWlJQQgIiIFkoAgpcgACtx9OhR5eTkaPTo0ZLkGIC7du1S+/bt9e6770pSVAEoSQUFBerYsaN8Pp98Pp86deqkjRs3VvmcUaNGyRgTJgGIiGiXBCB4iaQJwJEjRzqGVEULCws1ceJEde/eXeXl5ZKcA7Bfv34aOXJk8OtoAvD777/X+eefrxtvvFGffPKJli9frv79++uss87S999/H/F5nAFEREwMCUDwEkkTgDt27FBhYWGVlpWVqW/fvvL5fEpJSQlqjFFKSopuvPFGSVJmZmbI930+X/AxM2fOdPz3Z8yYodatW+vIkSPBZWVlZUpLS9Orr74a9evgPYCIiHZKAIKXSJoAjJZNmzapoKAg6KJFi2SM0bx581RSUiJJWrduXchjxowZo6ZNm6qgoEB79uxxXO+kSZN0wgkn6OjRo8Flhw8fVpMmTTRnzpyot48ARES0UwIQvAQBWA2RbgKpiNMl4Pnz56tTp07BrwsLC9WoUSPddtttWrdundasWaOBAwcqMzNTW7dujXp7CEBERDslAMFLEIDVUNsAzMvLkzGhu/ftt99W9+7dlZmZqebNm+vSSy/V8uXLa7Q9BCAiop0SgOAlCEDLIAAREe2UAAQvQQBaBgGIiGinBCB4CQLQMghAREQ7JQDBSxCAlkEAIiLaKQEIXoIAtAwCEBHRTglA8BIEoGUQgIiIdkoAgpcgAC2DAEREtFMCELwEAWgZBCAiop0SgOAlCEDLIAAREe2UAAQvQQBaBgGIiGinBCB4CQLQMghAREQ7JQDBSxCAlkEAIiLaKQEIXoIAtAwCEBHRTglA8BIEoGUQgIiIdkoAgpcgAC2DAEREtFMCELwEAWgZBCAiop0SgOAlCEDLIAAREe2UAAQvQQBaBgGIiGinBCB4CQLQMghAREQ7JQDBSxCAlkEAIiLaKQEIXoIAtAwCEBHRTglA8BIEoGUQgIiIdkoAgpcgAC2DAEREtFMCELwEAWgZBCAiop0SgOAlCEDLIAAREe2UAAQvQQBaRiAA39lynZbvH6Dl+wfE/ZcaIiJWLwEIXoIAtIxAAO7eMkGH90/V4f1TgyFIDCIielcCELwEAWgZTgFYUWIQEdGbEoDgJQhAy6guAJ1CkBhERIy/BCB4CQLQMqINQGIQEdFbEoDgJQhAy6hNABKDiIjxlwAEL0EAWsaxBmBVMUgUIiLGTgIQvAQBaBl1GYA1iULiEBHx2CQAwUsQgJYR6wA8lkCM9y9XREQvSwCClyAALSPeAUgcIiLWTgIQvAQBaBleDkAuLSMiRpYABC9BAFqGrQFYmziM9y9rRMS6lAAEL0EAWkYiBmB1URjvX9qIiHUhAQheggC0jGQJQEIQERNNAhC8BAFoGckWgIQgIiaKBCB4CQLQMpI1AJ1iMN6/zBERayIBCF6CALQMApAQREQ7JQDBSxCAlkEARg5BYhARvSwBCF6CALQMApAQREQ7JQDBSxCAlkEAEoKIaKcEIHgJAtAyCEBCEBHtlAAEL0EAWgYBSAgiop0SgOAlCEDLIAAJQUS0UwIQvAQBaBkEICGIdsv4S14JQPASBKBlEICEINprYLwx/pJTAhC8BAFoGQRg7EIw3pMDJq4Vw4//EUleCUDwEgSgZRCARCDaZVXxxxhMLglA8BIEoGUQgEQg2mO08cc4TA4JQPASBKBlEIBEIHrfipd1j2UcMhYTSwIQvAQBaBkEIBGI3vZYwo8QTGwJQPASBKBlEIBEIHrXuow/xmPiSQCClyAALYMAJALRm8Yq/hiTiSMBCF6CALQMApAIRG/pRvg5jUnGpX0SgOAlCEDLIACJQPSObscfIWi3BCB4CQLQMgjA+EYgky4GjGf8EYLe1+l3xjtbriMAwTMQgJZBAHpHp1/wTMbJoVfiz2k8xnvfJJNVHf9OP6PdWzgDCN6BALQMAtAOicPE1IvhF2nsxXtfJaLRhl4kCUDwEgSgZRCA9ksc2qkN8ec0xuK93xLBY/1g74AEIHgJAtAyCMDEljj0pjbFX6TxFO99aKt1+bMnAMFLEIBVUFpaquzsbBljtGrVquDy4uJiGWPCXL58eZXr27Rpky6//HKlpqaqVatW+v3vf6/Dhw/XaJsIwOSVMLQ/AOI9duK9L20yFj93AhC8BAFYBcOHD1efPn0iBuA777yjbdu2BT106FDEdZWXl6tLly765S9/qVWrVmnhwoVq2bKlHnjggRptEwGIlSUI7QmAeMv4iP7nHoufPQEIXoIAjMDChQvVuXNnrV27NmIAVlwWzfp8Pp+2b98eXPb8888rIyNDZWVlUa+HAMTqJAbrLgLi/bOM9RiJ9372krEMv4AEIHgJAtCB7du3q23btlqxYoVj7AWWtW/fXq1atVL37t21YMGCKtf5yCOPKDs7O2TZV199JWOMPv3004jPKy0tld/vD7p582YZY1RcOFa7t0xArNZ3tlwX4iz/BKzCwH6K98/NzXER733uhZ+3Gz/z4sKxMsZo3759xzZJAdQBBGAljh49qpycHI0ePVqS89m+nTt36plnntFHH32kTz75RCNHjlS9evWqjMBbbrlFvXr1Cln23XffyRijhQsXRnzeqFGjHN9viIiIdlpSUnKMMxXAsZM0AThy5MhqD8rCwkJNnDhR3bt3V3l5uaToL/f+9re/1S9+8YuI369tAFY+A7h3714VFRUFzwSWlJSEfB//Y0lJCfuI/cR+Yj95xn379qmkpERHjhypwewFEBuSJgB37NihwsLCKi0rK1Pfvn3l8/mUkpIS1BijlJQU3XjjjRHXP2XKFJ1wwgkRv1/bS8CR8Pt/eC+g3897SSLBPooO9lN0sJ+ig/0EYAdJE4DRsmnTJhUUFARdtGiRjDGaN29elaftf/e73+ncc8+N+P3ATSDffPNNcNnUqVOVkZGh0tLSGm8nv2Srh30UHeyn6GA/RQf7CcAOCMBqcLoEPGvWLL3yyivBM4dPPPGEfD6fXnzxxeBj5s+fr06dOgW/DnwMTK9evbR69Wq99dZbatWqVY0/BiYAv2Srh30UHeyn6GA/RQf7CcAOCMBqiBSAZ5xxhtLS0pSRkaHzzz9fc+fODXleXl6ejAndvRs3blSfPn2Umpqqli1b6t57763xB0EHKC0t1ahRo2p19jBZYB9FB/spOthP0cF+ArADAhAAAAAgySAAAQAAAJIMAhAAAAAgySAAAQAAAJIMAhAAAAAgySAALaC4uFg333yzTj75ZDVu3FinnHKKHn30UZWVlVX5vG3btmngwIE6/vjjlZaWpnPPPVfz5s1zaavdp7b7SZI+/PBDXXLJJUpLS1PTpk110UUX6fvvv3dhq93nWPaT9J8/l2iM0V//+tcYb238qM1+2r17t+644w6dfvrpaty4sdq3b6//+Z//Sei//Vrb8XTw4EHdfvvtatGihZo0aaKrr75a27dvd2mrAYAAtIB//OMfGjRokBYtWqSioiItWLBArVu31r333lvl8y677DJ169ZNH3/8sYqKijR69Gj5fL5a/eURG6jtfvrwww+VkZGhJ598UmvWrNEXX3yh1157LWE/xqK2+ynA+PHj1adPn4QPwNrsp4KCAl199dX629/+pg0bNmjJkiU67bTT1L9/fxe33F1qO55uvfVWtW/fXkuWLNHKlSt1wQUX6Oc//7lLWw0ABKCljBs3TllZWVU+pkmTJnr55ZdDlrVo0ULTp0+P5aZ5imj2089+9jM9/PDDLm2RN4lmP0nSqlWr1LZtW23bti3hA9CJaPdTRfLz89WwYcNaf+anjVS3n/bt26cGDRqEfH5qYWGhjDFavny5G5sIkPQQgJby0EMP6bzzzqvyMZdddpmuuOIK7d69W0eOHNGrr76qtLQ0rV+/3qWtjD/V7advvvlGxhhNmjRJF154oVq3bq2LL75Y77//votbGX+iGU/fffedzjjjDL3++uuSlJQBGM1+qsz06dPVsmXLGG2RN6luPy1ZskTGGO3duzdkeYcOHTR+/PhYbx4AiAC0kvXr1ysjI0PTpk2r8nF79+5Vr169ZIxR/fr1lZGRoUWLFrm0lfEnmv20fPlyGWPUokULvfjii/r000911113qWHDhvryyy9d3Nr4Ee14Gjp0qIYMGRL8OtkCMNr9VJGdO3eqQ4cOevDBB2O4Zd4imv00Z84cNWzYMGx5t27dNGLEiFhuHgD8CAEYR0aOHCljTJUWFhaGPGfLli3q2LFjyEQciTvuuEPnn3++3nnnHa1evVqPPfaYMjMz9fnnn8fqJcWEWO6nDz74QMaYsL/JfPbZZ+v++++v89cSS2K5nxYsWKBTTz1VBw4cCC6zNQBjfdwF8Pv9Ov/885WTk6NDhw7V9cuIObHcTwQgQPwhAOPIjh07VFhYWKUV76T7+uuvddppp+m3v/2tjhw5UuW6N2zYIGOM1qxZE7K8Z8+eGjZsWExeT6yI5X766quvZIzR7NmzQ5Zfd911+u///u+YvJ5YEcv9dOedd6pevXpKSUkJaoyRz+dTjx49YvzK6pZY7qcA+/fv14UXXqiePXvq4MGDsXopMSWW+4lLwADxhwC0hC1btui0007TDTfcoPLy8mof//nnn8sYo3Xr1oUs79Wrl2655ZZYbWbcqel+Onr0qNq0aRN2E8hPfvKTsLOCiURN99O2bdtUUFAQojFGEydO1FdffeXCFseHmu4n6YczfxdccIF69Oih7777LsZb6A1qup8CN4FU/FiqL774gptAAFyEALSALVu26NRTT1XPnj21ZcsWbdu2LWjFx3Tq1Ekff/yxJOnQoUM69dRTddFFF+njjz/Whg0b9PTTT6tevXp688034/VSYkpt9pMkPfvss8rIyNDcuXO1fv16Pfzww2rcuLE2bNgQj5cRc2q7nypj6yXgaKnNfvL7/frZz36ms88+Wxs2bAh5TrQBaRu1HU+33nqrOnTooH/+859auXKlLrzwQl144YXxeAkASQkBaAF5eXkR34MToLi4WMYYLV26NLjsyy+/1NVXX63WrVsrLS1N55xzTtjHwiQStd1PkvTkk0+qXbt2SktL04UXXpjQdwEfy36qSKIHYG3209KlSyM+p7i4OD4vJMbUdjwFPgi6efPmSktL+//t1jEBACAABKGM3z+MzgZwOkjB2fakEfhLAAEAYgQQACBGAAEAYgQQACBGAAEAYgQQACBGAAEAYgQQACBGAAEAYgQQACBGAAEAYgQQACBGAAEAYgQQACBGAAEAYgQQACBGAAEAYgQQACBGAAEAYgQQACBGAAEAYgQQACBGAAEAYgQQACBGAAEAYgQQACBGAAEAYgQQACBGAAEAYgQQACBGAAEAYgQQACDmAg2qGuLVk3qvAAAAAElFTkSuQmCC\" width=\"640\">" |
|
], |
|
"text/plain": [ |
|
"<IPython.core.display.HTML object>" |
|
] |
|
}, |
|
"metadata": {}, |
|
"output_type": "display_data" |
|
} |
|
], |
|
"source": [ |
|
"%matplotlib notebook\n", |
|
"%matplotlib notebook\n", |
|
"\n", |
|
"import cartopy.crs as ccrs\n", |
|
"import matplotlib.pyplot as plt\n", |
|
"import numpy as np\n", |
|
"import h5py as h5py\n", |
|
"import cartopy.feature as cfeature\n", |
|
"from cartopy.io.shapereader import Reader\n", |
|
"from cartopy.feature import ShapelyFeature\n", |
|
"from osgeo import gdal, osr\n", |
|
"\n", |
|
"fig = plt.figure(dpi=100)\n", |
|
"\n", |
|
"ax = plt.axes()\n", |
|
"\n", |
|
"#ax.coastlines('10m')\n", |
|
"#ax.gridlines()\n", |
|
"\n", |
|
"extent = [-44.996, -44.009, -2.805, -1.809]\n", |
|
"\n", |
|
"#ax.set_extent(extent)" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": 2, |
|
"metadata": { |
|
"ExecuteTime": { |
|
"end_time": "2019-06-03T15:36:51.507924Z", |
|
"start_time": "2019-06-03T15:36:51.226119Z" |
|
} |
|
}, |
|
"outputs": [ |
|
{ |
|
"data": { |
|
"text/plain": [ |
|
"0" |
|
] |
|
}, |
|
"execution_count": 2, |
|
"metadata": {}, |
|
"output_type": "execute_result" |
|
} |
|
], |
|
"source": [ |
|
"# First: read the geotiff image with GDAL.\n", |
|
"\n", |
|
"gdal.UseExceptions()\n", |
|
"\n", |
|
"fname = './resources/T23MNT_20190525T132241_TCI_60m.jp2'\n", |
|
"\n", |
|
"ds = gdal.Open(fname)\n", |
|
"data = ds.ReadAsArray()\n", |
|
"gt = ds.GetGeoTransform()\n", |
|
"proj = ds.GetProjection()\n", |
|
"\n", |
|
"inproj = osr.SpatialReference()\n", |
|
"inproj.ImportFromWkt(proj)\n", |
|
"\n", |
|
"# print('\\n\\n## ds ##:\\n\\n' + str(ds))\n", |
|
"# print('\\n\\n## data ##:\\n\\n' + str(data))\n", |
|
"# print('\\n\\n## gt ##:\\n\\n' + str(gt))\n", |
|
"# print('\\n\\n## proj ##:\\n\\n' + str(proj))\n", |
|
"# print('\\n\\n## inproj ##:\\n\\n' + str(inproj))" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": 3, |
|
"metadata": { |
|
"ExecuteTime": { |
|
"end_time": "2019-06-03T15:36:52.299009Z", |
|
"start_time": "2019-06-03T15:36:52.296671Z" |
|
} |
|
}, |
|
"outputs": [], |
|
"source": [ |
|
"projcs = inproj.GetAuthorityCode('PROJCS')\n", |
|
"# print('\\n\\n## projcs ##:\\n\\n' + str(projcs))\n", |
|
"\n", |
|
"# image_projection = ccrs.epsg(projcs)\n", |
|
"# print('\\n\\n## image_projection ##:\\n\\n' + str(image_projection))" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": 4, |
|
"metadata": { |
|
"ExecuteTime": { |
|
"end_time": "2019-06-03T15:36:54.127761Z", |
|
"start_time": "2019-06-03T15:36:53.393362Z" |
|
} |
|
}, |
|
"outputs": [], |
|
"source": [ |
|
"img = plt.imread(fname)" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": 5, |
|
"metadata": { |
|
"ExecuteTime": { |
|
"end_time": "2019-06-03T15:37:31.418553Z", |
|
"start_time": "2019-06-03T15:37:30.318656Z" |
|
} |
|
}, |
|
"outputs": [ |
|
{ |
|
"name": "stderr", |
|
"output_type": "stream", |
|
"text": [ |
|
"/usr/local/lib/python3.5/dist-packages/matplotlib/figure.py:98: MatplotlibDeprecationWarning: \n", |
|
"Adding an axes using the same arguments as a previous axes currently reuses the earlier instance. In a future version, a new instance will always be created and returned. Meanwhile, this warning can be suppressed, and the future behavior ensured, by passing a unique label to each axes instance.\n", |
|
" \"Adding an axes using the same arguments as a previous axes \"\n" |
|
] |
|
}, |
|
{ |
|
"data": { |
|
"text/plain": [ |
|
"Text(-44.23, -2.54, 'São Luíz')" |
|
] |
|
}, |
|
"execution_count": 5, |
|
"metadata": {}, |
|
"output_type": "execute_result" |
|
} |
|
], |
|
"source": [ |
|
"#projection = ccrs.PlateCarree()\n", |
|
"\n", |
|
"# fig = plt.figure(dpi=100)\n", |
|
"\n", |
|
"ax = plt.axes()\n", |
|
"\n", |
|
"#ax.coastlines('10m')\n", |
|
"#ax.gridlines()\n", |
|
"\n", |
|
"#ax.set_extent(extent, crs=ccrs.PlateCarree())\n", |
|
"img = plt.imread(fname)\n", |
|
"ax.imshow(img, extent=extent)\n", |
|
"\n", |
|
"# mark a known place to help us geo-locate ourselves\n", |
|
"ax.plot(-44.243317, -2.565823, 'bo', markersize=7, color='red')\n", |
|
"ax.text(-44.23, -2.54, 'São Luíz', color='black', size=10)" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": 6, |
|
"metadata": { |
|
"ExecuteTime": { |
|
"end_time": "2019-06-03T15:39:10.777218Z", |
|
"start_time": "2019-06-03T15:39:08.072224Z" |
|
} |
|
}, |
|
"outputs": [ |
|
{ |
|
"data": { |
|
"text/plain": [ |
|
"<matplotlib.colorbar.Colorbar at 0x7f184c265898>" |
|
] |
|
}, |
|
"execution_count": 6, |
|
"metadata": {}, |
|
"output_type": "execute_result" |
|
} |
|
], |
|
"source": [ |
|
"# fig = plt.figure(dpi=100)\n", |
|
"\n", |
|
"hdf5 = './resources/3B-MO.MS.MRG.3IMERG.20190101-S000000-E235959.01.V06A.HDF5'\n", |
|
"dataset = h5py.File(hdf5,'r')\n", |
|
"\n", |
|
"precip = dataset['Grid/precipitation'][:]\n", |
|
"precip = np.transpose(precip[0])\n", |
|
"\n", |
|
"theLats = dataset['Grid/lat'][:]\n", |
|
"theLons = dataset['Grid/lon'][:]\n", |
|
"\n", |
|
"clevs = np.arange(0,0.1,0.01)\n", |
|
"\n", |
|
"x, y = np.float32(np.meshgrid(theLons, theLats))\n", |
|
"\n", |
|
"masked_array = np.ma.masked_where(precip < 0,precip)\n", |
|
"\n", |
|
"cmap = 'nipy_spectral'\n", |
|
"\n", |
|
"plt.title('January 2019 Monthly Average Rain Rate')\n", |
|
"\n", |
|
"font = {'weight' : 'bold', 'size' : 3}\n", |
|
"\n", |
|
"plt.rc('font', **font)\n", |
|
"\n", |
|
"plt.xlim(-2.805, -1.809)\n", |
|
"plt.ylim(-45, -44)\n", |
|
"\n", |
|
"ax = plt.axes()\n", |
|
"\n", |
|
"cs = ax.contourf(x, y, precip, clevs, alpha=0.4, cmap=cmap)\n", |
|
"\n", |
|
"sm = plt.cm.ScalarMappable(cmap=cmap,norm=plt.Normalize(0,1))\n", |
|
"sm._A = []\n", |
|
"plt.colorbar(sm, ax=ax, label='mm/h', shrink=0.5)" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": null, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [] |
|
} |
|
], |
|
"metadata": { |
|
"kernelspec": { |
|
"display_name": "Python 3", |
|
"language": "python", |
|
"name": "python3" |
|
}, |
|
"language_info": { |
|
"codemirror_mode": { |
|
"name": "ipython", |
|
"version": 3 |
|
}, |
|
"file_extension": ".py", |
|
"mimetype": "text/x-python", |
|
"name": "python", |
|
"nbconvert_exporter": "python", |
|
"pygments_lexer": "ipython3", |
|
"version": "3.5.2" |
|
} |
|
}, |
|
"nbformat": 4, |
|
"nbformat_minor": 2 |
|
}
|
|
|