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.
949 lines
65 KiB
949 lines
65 KiB
{ |
|
"cells": [ |
|
{ |
|
"cell_type": "code", |
|
"execution_count": 6, |
|
"metadata": { |
|
"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,iVBORw0KGgoAAAANSUhEUgAAA8AAAALQCAYAAABfdxm0AAAgAElEQVR4nOzde5xddX3v/+9MbpAMGXKZBDIJuUAgGY8gFxO8EemvWhEQi1qPgofaesFK6ymIIFUp4K2oqEgiCipGEB2Pp6Ve0Cpg0aqUxEvJQS1EpYKUFAFJIBly+fz+mEd2M8nksjIz+X732s+Xj+fjIckk2bP3rD3rnb1mkkKSJEmSpBYo5b4BkiRJkiTtiwxgSZIkSVJLZABLkiRJkloiA1iSJEmS1BIZwJIkSZKklsgAliRJkiS1RAawJEmSJKklMoAlSZIkSS2RASxJkiRJaokMYEmSJElSS2QAS5IkSZJaIgNYkiRJktQSGcCSJEmSpJbIAJYkSZIktUQGsCRJkiSpJTKAJUmSJEktkQEsSZIkSWqJDGBJkiRJUktkAEuSJEmSWiIDWJIkSZLUEhnAkiRJkqSWyACWJEmSJLVEBrAkSZIkqSUygCVJkiRJLZEBLEmSJElqiQxgSZIkSVJLZABLkiRJkloiA1iSJEmS1BIZwJIkSZKklsgAliRJkiS1RAawJEmSJKklMoAlSZIkSS2RASxJkiRJaokMYEmSJElSS2QAS5IkSZJaIgNYkiRJktQSGcCSJEmSpJbIAJYkSZIktUQGsCRJkiSpJTKAJUmSJEktkQEsSZIkSWqJDGBJkiRJUktkAEuSJEmSWiIDWJIkSZLUEhnAkiRJkqSWyACWJEmSJLVEBrAkSZIkqSUygCVJkiRJLZEBLEmSJElqiQxgSZIkSVJLZABLkiRJkloiA1iSJEmS1BIZwJIkSZKklsgAliRJkiS1RAawJEmSJKklMoAlSZIkSS2RASxJkiRJaokMYEmSJElSS2QAS5IkSZJaIgNYkiRJktQSGcCSJEmSpJbIAJYkSZIktUQGsCRJkiSpJTKAJUlN0ZIlS+LDH/7wTn9+9uzZ8fd///f78Ba1Trfffnt0d3fnvhmSJA05A1hSLdvdWGqWrrvuunjmM58ZEydOjIMOOij+7M/+LB599NEBb3P11VfHrFmzYvz48fHiF784fvvb3zZ+7tZbb43nP//5MXHixOjs7Nzh93/ooYfila98ZUydOjWmTp0a5513XmzatGmnt2fJkiWRUopvfetbA3788ssvj5RSvOUtbxnie9zfWWedtcPvtS8G8Iknnhj77bdfPPLII0P6fUrrrLPOijFjxsSECROis7Mzjj322PjGN76xz/78JUuWxNixY2PChAkxadKkOOGEE+LOO+/c41//mc98Jo466qgRvIWSpFbJAJZUy0ocwBs3bowtW7ZU+jXLli2L2267LdavXx+/+93v4qSTTopXvepVjZ+/5ZZborOzM374wx/GunXr4rWvfW2ceOKJjZ+/4447Yvny5XHttdcOOoBf+MIXxv/6X/8rnnjiiXjggQfiGc94RrznPe/Z6e1ZsmRJHHHEEfHqV796wI8vXLgwFixY0NQDePXq1dHW1haTJ0+Oj33sY3v9++yup556asR+75217f25adOm+OhHPxodHR3x2GOP7ZM/f9vHrq+vL84///yYNWvWHv96A1iSNFwZwJJq2dYT7rVr18ZLXvKS6OrqiokTJ8bznve8+MlPftJ4u4svvjhOOeWUePOb3xydnZ0xa9as+MIXvrDD77O1H//4x5HSfz91fu5zn4unPe1p0dHREbNmzYp3vOMdA0ZuSik+9rGPxdOe9rQYO3ZsfOQjH4klS5YMuK033nhjLFy4cI/er5tuumnAcDjzzDPjzW9+c+O///M//zPa29tj9erVA37dbbfdtsMAXrduXbS1tQ142+uuuy5mz5690z9/yZIlcfHFF8ekSZMa4+mHP/xhLFy4cIfReuedd8azn/3s6OzsjIULF8bnP//5xs/t6n7/6Ec/GqNHj268YtnT09P4sy+88MJ44QtfGB0dHXH00UfHv/3bvzV+z60D+Kmnnopp06bFbbfdNuC2L1iwYMBju31/8zd/E0cffXRcfPHF8YxnPKPx4w899FCMGTMmfv3rXzd+bMOGDXHggQfG97///YiIuPfee+OUU06JqVOnxiGHHBKXXXZZbN68OSL+e7y9613viunTp8fpp5++24/LzZs3x9/8zd/EtGnT4uCDD46rrroqOjs7B7xPN954Yzz96U+Pzs7OOO644+Jf/uVfdvq+bf/YrFu3LlJK8a//+q+NHzv//PPjkEMOiY6Ojli4cGH09vY2fm77j5/dPRbbt/1xtGrVqkgpxZo1axo/dsYZZ8TBBx8cBxxwQBxzzDFx6623RkTEj370oxg3bly0t7fHhAkTYsKECXHfffdVvg8kSYowgCXVtK0n3L///e/jC1/4Qqxbty7Wr18ff/VXfxWHH354Y6RefPHFMWbMmPjiF78YmzZtis9+9rPR0dERjz/++IDfZ2vbD+Cvf/3r8Ytf/CK2bNkSP/7xj2PatGlx/fXXN34+pRTPetaz4oEHHogNGzbEww8/HPvtt1/88pe/bLzNC1/4wrj88sv36P0699xz4+STT27895FHHhnXXHPNgLeZMWNG/MM//MOAHxtsAK9duzZSSnHvvfc2fuzTn/50pJTi97///aB//tb741WvelV8/OMfj4iIN7zhDXH55ZcPGFmPPvpoTJkyJa688sp46qmn4jvf+U5MmDAhvve970XE7u/3nb0C3N3dHT/5yU9i48aN8frXv37AXyZs+wrweeedF2eddVbj577//e/HpEmTYsOGDYO+X5s2bYru7u746Ec/2ngleOXKlY2fP/nkk+Pd735347+/9KUvxfz58yMi4oknnojZs2fHhz/84ejr64v77rsvnva0p8W1114bEf0DeNSoUXHppZdGX19fPPHEE7v9uLz22mtjzpw58Ytf/CKefPLJ+LM/+7Nob29vDOCvfe1r0d3dHStXrozNmzfHl7/85Zg8eXI8/PDDg75/296fTz31VHzoQx+KsWPHxkMPPdR4m+uvvz4eeuih2LRpU9x4440xbty4xsfpYAN4V4/F9m17HD355JPx13/91zF16tTYuHFj420+/elPx2OPPRZPPfVUXH755TF58uTGx8NgrwBXvQ8kSYowgCXVtJ1dLvvoo49GSinuv//+iOgfYosXL278/JYtW2Ls2LGxYsWKQX+f7Qfw9r3lLW+J173udY3/TintcFnun/zJn8TFF18cERH3339/jBs3Lh588MHdvk9f//rXY+LEiQNeaZs3b1586UtfGvB2PT098bnPfW7Ajw02gCMiTjjhhDjjjDNi7dq1cd9998VRRx0VKaX4zW9+M+ht2Hp//NM//VMsWrQonnzyyZgyZUo8+OCDA0bW9ddfHwsWLBjwa1//+tfH61//+ojY/f2+swF8wQUXNP77e9/7XnR0dDT+e9sBfPfdd0dHR0esXbs2IvpH+ravlG/f1772tRgzZkz813/9V0REPPe5z42/+Iu/aPz8F7/4xTjiiCMa//2Sl7wkLr300oiI6O3tHfCKcUTEJz/5yfiDP/iDiOgfb5MnT268IjxY239c/sEf/EF84AMfaPz8mjVrIqXUGMAvfvGL4yMf+ciA3+PZz352LF++fNDf/6yzzoqxY8dGZ2dntLe3R0dHR/zf//t/d3p7IiKOOuqoxl/mDDaAd/VYbN+SJUtiv/32i87Ozmhra4vp06fH7bffvss//8ADD2z8hclgA7jqfSBJUoQBLKmmbR1qTz75ZLzpTW+K2bNnxwEHHBCdnZ2RUmpcbnrxxRfHaaedNuDXbnup6e4G8De+8Y141rOeFVOmTImJEyfGuHHj4qUvfWnj51NKA15J3Ppr5s6dG1u2bIn3vve9ceqpp+72/bnlllti8uTJ8e1vf3vAjx955JGNVxq31t3dvUevAEdE/OY3v4nTTz89pk+fHvPnz4+/+7u/i7a2tli3bt2gt2Pr/bF58+aYNWtWvP3tb49TTjklIgaO1ve///3xohe9aMCvfd/73hcnnXRSROz+ft+TrwHe/rHY/muAjz/++PjMZz4T69evj87Ozh0eh207/fTTB9yea665Jg488MBYv359RETj97jjjjviv/7rv2Ls2LHxq1/9KiL6vwHY6NGjo7Ozs+GAAw5oXLr9mc98Jp7+9KcP+PN293E52OXa++23X+P+6enpifHjxw/4M8ePHx/ve9/7Bn3/tr0/t34t+Vvf+tYBb3PFFVdET09P4xumjRo1qjEwBxvAVf5iaNu3v//+++OYY46Jq666qvHzmzdvjosuuigOO+ywxv3R1tbW+DgebABXvQ8kSYowgCXVtK0n3JdddlksWrSo8Yrm1lfafvzjH0fE7ofYySefPOCbQn39619vnOj39fXF+PHj47rrrmtcWvuWt7xlwO+37Z+1ta3j8bbbbosjjjhit6/E3XLLLTFp0qRBv2vvmWeeGeecc07jvx966KE9/hrgwVq2bFk885nP3OnPbztk3vGOd0RbW1t8+ctfjojY7SvAb3zjGwe8Aryr+/21r33tkAfwNddcE0uWLInPf/7zceSRR+70fVqzZk2MGTMmOjo6Yvr06TF9+vSYMmVKpJQGXM7+ute9Ls4555z42Mc+Fs973vMaP37jjTcOeDV7+wYbb7v7uNzdK8B/9Ed/1LgEfU/a/i8UHnjggZgwYUL86Ec/ioiI7373uzFx4sRYsWJF45Xqo446qnF/D+cAjohYuXJlTJgwIR544IGI6P9a+q2XfG+9DPzAAw9sPJ6f/exnd7gPq94HkiRFGMCSatrWE+7zzz8/TjjhhFi3bl2sXbs23vSmN1UawO985zvj2GOPjcceeyweeuiheP7zn9840X/88cejvb09vvKVr0RE/zeD6urq2u0Ajoh417veFUcddVR0dXXt8rsC33bbbXHggQfGV7/61UF//pZbbokDDzww7rjjjnjiiSfiz//8zwd8F+jNmzfH+vXr45vf/GZ0dnbG+vXrG69qRkT87Gc/i0cffTQ2bdoUt912W8yYMSNuvvnm3d6vEf2vJH7rW99q3P5tR9YjjzwSkydPjqVLl8bGjRvj9ttvj46Ojvjud78bEbu/3y+88MI49dRTB3xDsaoD+PHHH4+Ojo448sgjd7hUdts++MEPxvTp0+P++++PBx98sOFP//RPB9yXt99+e0ydOjWOOeaYAV93vXbt2pgzZ04sXbo01q9fH5s2bYqf//znjfdlsAG8u4/La665JubOnRv33HNPPPnkk/G6171uwNcA/+M//mPMmzcvVqxYEVu2bIknnngivvWtb+300vXBXlF/85vf3Ph68q997WsxadKkuO+++2Ljxo3xqU99KkaNGjViAzii/y+Xtl6WvnTp0jj88MPjd7/7XWzYsCEuueSSGDVqVOPx/MY3vhHTpk2LJ598svHrq94HkiRFGMCSatqSJUviIx/5SDz44INx4oknxoQJE2L27NmxfPnySgP4kUceiVNOOSUOOOCAeNrTnhYf//jHB5zof/zjH29859pTTz01zjnnnD0awL/61a+ira0t/vqv/3qX78fzn//8Ad/9dqtt+/jHPx7d3d0xfvz4OOmkkwb8O8C33XZbpJR2sLVly5bFtGnTYv/9948jjzxyh0unB7tfd/ZPEW0/su6444541rOeFRMnTowFCxYM+Lrk3d3v9957bxxzzDFx4IEHNi4frjqAI/pfSR47dmzja3sHa+HChfG3f/u3O/z4XXfdFW1tbY1vErZly5aYO3du7Lfffjv880H33ntv41Lyzs7OOOaYY+LGG2+MiMEH8O4+Ljdv3hxvf/vbo6urq/FdoMePH9/4rtMR/V97fPTRR0dnZ2dMmzYtTjnllMZ3R96+wQbwfffdF2PGjIk77rgjNm/eHK9//etj4sSJ0dXVFeeee26ccMIJIzqAv//978e4cePiP/7jP2LdunXxx3/8x9HR0REzZsyIyy+/fMDj+dRTT8VLXvKSmDRpUnR2djbezyr3gSRJEQawpJp29NFHD7h8tbSeeOKJmDBhQtx11125b0qtu+SSS+JlL3tZ7psx5H77298O+CZZkiRp7zKAJdWun/70pzF27Nj4+c9/nvumDNqWLVvi/e9/fzz3uc/NfVNq3Zo1a2LmzJk7fOOwZmjjxo2Nf9P4kUceiT/5kz+JZz/72blvliRJTZ8BLKlWveENb4ju7u6dXqabu02bNkVHR0fMmTNnl9+VWEPr3e9+d4wfPz7e+MY35r4pe9VTTz0VixcvjgMOOCAmTZoUJ598cvz617/OfbMkSWr6DGBJkiRJUktkAEuSJEmSWiIDWJIkSZLUEhnAkiRJkqSWyACWJEmSJLVEBrAkSZIkqSUygCVJkiRJLZEBrOxt2bIl1q1bBwBQpC1btuQ+XZI0TBnAyt66desipQQAUKR169blPl2SNEwZwMqeAQwAlMwAluqTAazsbTuAr3noA7F83ZXQ0m598JV75LEHr2xq274vue/zOqrLxwnk8sDqDxjAUg0zgJW9bQfw8nVXRm98AtgDP3j8jKaX+z6sux88fkZsfPwTwF547MErDWCphhnAyp4BDDAytv5FQ+4hAc3IAJbqmQGs7BnAACPHAN4zu7pSIfdtIw8DWKpnBrCyZwADjBwjbtd2d0m+Udy6DGCpnhnAyp4BDDCyDLYdDfVr0Q3j+jOApXpmACt7BjDAyDLOBhrJb8JmGNeHASzVMwNY2TOAAUaeETb0V32Hct8bxs3HAJbqmQGs7BnAAPtGKw+uEv/pLcO4bAawVM8MYGXPAAbYN1pxWJU4fPfkcTKK8zOApXpmACt7BjDAvtMqQyrX5c4j/dgZxvuOASzVMwNY2TOAAfadVhhNdRu+e/qYGsbDywCW6pkBrOwZwAD7Vl3HUasN3909xkaxASxpxwxgZc8ABti36jaG6ni580g+7nV67A1gSVUzgJU9Axhg36vLCDJ89+6xN4QNYKlVM4CVPQMYYN9r9gFk+A7fx0AzfxwYwJKqZgArewYwQB7NOHxc7jwyHweGsAEstUoGsLJnAAPk0Wyjx/DdNx8PzfQxYQBLqpoBrOwZwAD5NMPYMXzzfFw0w8eGASypagawsmcAA+RT8tBxuXN+JX98GMCS9iYDWNkzgAHyKnHgGL5lacUhbABL9cwAVvYMYID8Shk3hm/ZWunrhA1gqZ4ZwMqeAQyQX+5B43Ln5tIKQ9gAluqZAazsGcAAZcg1Zgzf5lXnIWwAS/XMAFb2DGCAMuzrIWP41kcdh7ABLNUzA1jZM4AByrEvBozLneurTkPYAJbqmQGs7BnAAOUY6fFi+LaGOgxhA1iqZwawsmcAA5RlOIfLtkPI8G092z/+uUetASzJAFb2DGCA8uztaNl+8Bi9bNVsQ9gAluqZAazsGcAA5drdaDF42RvNMIYNYKmeGcDKngEMUL7Bhq7By3AodQwbwFI9M4CVPQMYoHkYvIyUkr5e+AePnxG3PvhKA1iqYQawsmcAAwDb2pdjeGdXNixf5xVgqY4ZwMqeAQwA7MxwD+E9vYzfAJbqmQGs7BnAAMCeqDqGh/I16wawVM8MYGXPAAYAqtp+DA/3N2kzgKV6ZgArewYwALC3Ruo7khvAUj0zgJU9AxgAKI0BLNUzA1jZM4ABgNIYwFI9M4CVPQMYACiNASzVMwNY2TOAAYDSGMBSPTOAlT0DGAAojQEs1TMDWNkzgAGA0hjAUj0zgJU9AxgAKI0BLNUzA1jZM4ABgNIYwFI9M4CVPQMYACiNASzVMwNY2TOAAYDSGMBSPTOAlT0DGAAojQEs1TMDWNkzgAGA0hjAUj0zgJU9AxgAKI0BLNUzA1jZM4ABgNIYwFI9M4CVPQMYACiNASzVMwNY2TOAAYDSGMBSPTOAlT0DGAAojQEs1TMDWNkzgAGA0hjAUj0zgJU9AxgAKI0BLNUzA1jZM4ABgNIYwFI9M4CVPQMYACiNASzVMwNY2TOAAYDSGMBSPTOAlT0DGAAojQEs1TMDWNkzgAGA0hjAUj0zgJU9AxiAEvzg8TOy3wbKYQBL9cwAVvYMYABK8IPHzzCCaTCApXpmACt7BjAAuW0dvwYwWxnAUj0zgJU9AxiA3H7w+Bmx8fFPxMbHXQpNPwNYqmcGsLJnAAOQmwHM9gxgqZ4ZwMqeAQxATlsvfd46gI1gesMAluqaAazsGcAA5LT9+N06gI3g1mYAS/XMAFb2DGAAchpsAHsVGANYqmcGsLJnAAOQ064GsBHcugxgqZ4ZwMqeAQxALoN9/a9XgekNA1iqawawsmcAA5DLrsavV4FbmwEs1TMDWNkzgAHIZXcD2KvArcsAluqZAazsGcAA5LC7y5+9CtzaDGCpnhnAyp4BDEAOezJ+vQrcugxgqZ4ZwMqeAQxADlUHsBHcWgxgqZ4ZwMqeAQxADlUGsFeBW48BLNUzA1jZM4AB2Nf29Ot/vQrcugxgqZ4ZwMqeAQzAvlZ1/HoVuPUYwFI9M4CVPQMYgH1tKAPYCG4NBrBUzwxgZc8ABmBf29sB7FXg1mEAS/XMAFb2DGAA9qWhjF+vArcOA1iqZwawsmcAA7AvDXUAexW4NRjAUj0zgJU9AxiAfWlvvgO08dt6DGCpnhnAyp4BDMC+5p9BYncMYKmeGcDKngEMQA5VBrDx23oMYKmeGcDKngEMQA57+iqw8duaDGCpnhnAyp4BDEAuuxvAxm/rMoClemYAK3sGMAA57WwEG7+tzQCW6pkBrOwZwADkNNil0MYvBrBUzwxgZc8ABiC3rQPY8GUrA1iqZwawsmcAA5Db1uFr/LKVASzVMwNY2TOAAYDSGMBSPTOAlT0DGAAojQEs1TMDWNkzgAGA0hjAUj0zgJU9AxgAKI0BLNUzA1jZM4ABgNIYwFI9M4CVPQMYACiNASzVMwNY2TOAAYDSGMBSPTOAlT0DGAAojQEs1TMDWNkzgAGA0hjAUj0zgJU9AxgAKI0BLNUzA1jZM4ABgNIYwFI9M4CVPQMYACiNASzVMwNY2TOAAYDSGMBSPTOAlT0DGAAojQEs1TMDWNkzgAGA0hjAUj0zgJU9AxgAKI0BLNUzA1jZM4ABgNIYwFI9M4CVPQMYACiNASzVMwNY2TOAAYDSGMBSPTOAlT0DGAAojQEs1TMDWNkzgAGA0hjAUj0zgJU9AxgAKI0BLNUzA1jZM4ABgNIYwFI9M4CVPQMYACiNASzVMwNY2TOAAYDSGMBSPTOAlT0DGAAojQEs1TMDWNkzgAGA0hjAUj0zgJU9AxgAKI0BLNUzA1jZM4ABgNIYwFI9M4CVPQMYACiNASzVMwNY2TOAAYDSGMBSPTOAlT0DGAAojQEs1TMDWNkzgAGA0hjAUj0zgJU9AxgAKI0BLNUzA1jZM4ABgNIYwFI9M4CVPQMYACiNASzVMwNY2TOAAYDSGMBSPTOAlT0DGAAojQEs1TMDWNkzgAGA0hjAUj0zgJU9AxgAKI0BLNUzA1jZM4ABgNIYwFI9M4CVPQMYACiNASzVMwNY2TOAAYDSGMA79ld/9VfR3d0dKaWYPXv2Tt/u4YcfjtNPPz3Gjx8fU6dOjbe97W2xefPmfXdDpV1kACt7BjAAUBoDeMfOOeecuOiii3Y7gF/5yldGe3t7XHrppXHmmWdGSimuvvrqfXdDpV1kACt7BjAAUBoDePA2bty4ywH86KOPRnt7exx//PER0X+eN2rUqDjmmGP24a2Udp4BrOwZwABAaUodwKeeemr09PTslY6Ojp3+3J62uwH8ox/9KFJK8YpXvKLxY1OnTo3Ozs6hvuvSsGQAK3sGMABQmlIHcE9PT6SUYt68eZWklGLcuHH7bAC//OUvb/zYlClTDGAVkwGs7BnAAEBpSh7A8+bNixUrVlQyb968SkN3Zw02gPv6+mL9+vWxZcuWxiXQixcvjoiItWvXugRaRWUAK3sGMABQmqIH8OzZseLb365k3uzZQx7AX/3qV+MTn/hEpJRiypQpcc0118TKlStjyZIlkVKKe+65JyIiXvGKV0R7e3tcdtlljW+CtWzZsuF496UhZwArewYwAFCaogfwzJmx4v/8n0rmzZw55AG8dehu6+KLL95hAK9ZsyZe+tKXxv777x+TJ0+Ot771rbFp06bhePelIWcAK3sGMABQmqIH8IwZseLaayuZN2PGsFwCLTV7BrCyZwADAKUpegBPnx4rPvjBSuZNn24AS2EAq4AMYACgNEUP4KlTY8U73lHJvKlTDWApDGAVkAEMAJSm6AE8eXKsOOecSuZNnmwAS2EAq4AMYACgNEUP4M7OWHHmmZXM6+w0gKUwgFVABjAAUJqiB3BHR6w45ZRK5nV0GMBSGMAqIAMYAChN0QN4/PhY8dznVjJv/HgDWAoDWAVkAAMApSl6AI8bFyv+x/+oZN64cQawFAawCsgABgBKU/QAHj06VsycWcm80aMNYCkMYBWQAQwAlKboAdzeHis6OiqZ195uAEthAKuADGAAoDQGsFTPDGBlzwAGAEpT9ABOKVZUNC8lA1gKA1gFZAADAKUpfQD/qCIDWOrPAFb2DGAAoDSlD+AfV2QAS/0ZwMqeAQwAlKb0AfyTigxgqT8DWNkzgAGA0pQ8gA9NKf6tokMNYCkiDGAVkAEMAJSm9AF8V0UGsNSfAazsGcAAQGlKH8CrKjKApf4MYGXPAAYASlP6AL67IgNY6s8AVvYMYACgNKUP4J9VZABL/RnAyp4BDACUpuQBfFhK8fOKDjOApYgwgFVABjAAUJrSB/C/V2QAS/0ZwMqeAQwAlKb0AXxPRQaw1J8BrOwZwABAaQxgqZ4ZwMqeAQwAlKb0AXxvRQaw1J8BrOwZwABAaUoewPNTil9WNN8AliLCAFYBGcAAQGlKH8D3VWQAS/0ZwMqeAQwAlKbkAXx4SnF/RYcbwFJEGMAqIAMYAChN6QP4txUZwFJ/BrCyZwADAKUpeQAfkVI8VNERBrAUEQawCsgABgBKU/oAfrgiA1jqzwBW9gxgAKA0pQ/gR3CPxkYAACAASURBVCoygKX+DGBlzwAGAEpT8gBekFL8vqIFBrAUEQawCsgABgBKU/oAXleRASz1ZwArewYwAFCakgfwwpTiyYoWGsBSRBjAKiADGAAoTekDuK8iA1jqzwBW9gxgAKA0BrBUzwxgZc8ABgBKU/IA7kkpNlXUYwBLEWEAq4AMYACgNKUP4C0VGcBSfwawsmcAAwClKXoAt6WI/avpaTOApQgDWAVkAAMApSl6AI9KEV3V9IwygKUIA1gFZAADAKUpegCPSRFzq+kZYwBLEQawCsgABgBKU/QAHpcinl5NzzgDWIowgFVABjAAUJqiB/D+KeJZ1fTsbwBLEQawCsgABgBKU/QAnpAiXlBNzwQDWIowgFVABjAAUJqiB/ABKeKPq+k5wACWIgxgFZABDACUpugB3JkiXlNNT6cBLEUYwCogAxgAKE3RA3hSinhTNT2Thj6AV65cGYsWLYqxY8fG3Llz44Ybbhj07W699dY49thjY//9949JkybFS1/60lizZs2Q/mxpuDKAlT0DGAAoTdEDeEqKOL+anilDG8B9fX0xa9as6OrqiqVLl8aiRYuivb097r777h3etru7O9ra2uLv/u7v4uUvf3mklOK8884byrstDVsGsLJnAAMApTGAB3bTTTdFSikuvPDCiIi4+eabI6UU55577g5vO2PGjBg7dmysXLky3vve90ZKKS699NK9/rOl4cwAVvYMYACgNEUP4K4UcUk1PV0pxo0b1//rB7G7rrjiikgpxdKlSyMiYtWqVZFSitNOO22Ht/3Od74TkyZNatx/z3nOc2LDhg3Dfl9Ie5MBrOwZwABAaYoewNNTxIeq6Zk+PAP4qquuioiIu+66a6cD+MUvfnGMGzcurrvuurjooosipRRvfetbh/2+kPYmA1jZM4ABgNIUPYAPThGfqKbn4OG5BPqCCy6IiIGXQPf19cX69etjy5YtsWbNmkgpxbHHHhsREY8//nik5DtQq5wMYGXPAAYASlP0AJ6RIj5VTc+MoX8TrJkzZ0ZXV1csW7YsFi9eHG1tbbFq1apYsmRJpJTinnvuiU2bNsWkSZNi9OjR8cEPfjDOPvvsSCnFy172smG8F6S9zwBW9gxgAKA0xQ/gz1Yz1AEcEXHnnXfGcccdF2PGjIk5c+bE8uXLIyIGDOCIiFtuuSWe+cxnxoQJE2LSpElx2mmnxf333z/k910ajgxgZc8ABgBKU/QA7k4RN1TT0+0yZCnCAFYBGcAAQGmKHsAzU8QXq+mZaQBLEQawCsgABgBKU/wA/nI1BrDUnwGs7BnAAEBpih7As1LETdX0zDKApQgDWAVkAAMApSl6AB+SIr5WTc8hBrAUYQCrgAxgAKA0xQ/gb1ZjAEv9GcDKngEMAJSm6AE8O0XcUk3PbANYijCAVUAGMABQGgNYqmcGsLJnAAMApSl6AM9JEf9cTc8cA1iKMIBVQAYwAFCa4gfwv1RjAEv9GcDKngEMAJSm6AE8N0XcUU3PXANYijCAVUAGMABQmqIH8LwUsbKannkGsBRhAKuADGAAoDSlD+DNP67GAJb6M4CVPQMYAChN0QP40BSb/q2ankMNYCnCAFYBGcAAQGlKH8Ab/181BrDUnwGs7BnAAEBpSh7ACw9N0fezahYawFJEGMAqIAMYAChN0QP4sBQb/r2ahYcZwFKEAawCMoABgNIUPYDnp1i/upqF8w1gKcIAVgEZwABAaUofwE/8qhoDWOrPAFb2DGAAoDQlD+AF81Os+49qFhjAUkQYwCogAxgAKI0BLNUzA1jZM4ABgNIUPYAPT7H2gWoWHG4ASxEGsArIAAYASlP6AP79g9UYwFJ/BrCyZwADAKUpegAfkeKxNdUsOMIAliIMYBWQAQwAlKb0Afzo76oxgKX+DGBlzwAGAEpT8gA+YkGKhx+t5ogFBrAUYQCrgAxgAKA0JQ/gwxek+M/HqjncAJYiwgBWARnAAEBpSh/A9/++GgNY6s8AVvYMYACgNCUP4PkLUvx6bTXzDWApIgxgFZABDACUpuQBfNiCFPeuq+YwA1iKCANYBWQAAwClKXoAL0zx8yeqOWyhASxFGMAqIAMYAChNyQP40IUpVq2v5lADWIoIA1gFZAADUIK/fOSM7LeBcpQ8gOctTPGTDdXMM4CliDCAVUAGMAA5/eUjZ8RfPnJGfOJ+I5j/ZgBL9cwAVvYMYABy2Xb8bmUE0xvlD+AVfdUYwFJ/BrCyZwADkMNg49cIZquSB/DcnhQ/3FjN3B4DWIowgFVABjAA+9quxq8RTG+UPYDn9KT43qZq5hjAUkQYwCogAxiAfWlPxq8RTOkD+DubqzGApf4MYGXPAAZgX6gyfI1gSh7As3tSfDuqmW0ASxFhAKuADGAARtrejt9tR7Ah3FpKHsCzelL8Q1QzywCWIsIAVgEZwACMpKGOXyO4NZU8gGf0pPhsVDPDAJYiwgBWARnAAIyU4Rq/RnDrKXkAH9ST4mNRzUEGsBQRBrAKyAAGYCQM9/g1gltLyQN4Wk+Kd0c10wxgKSIMYBWQAQzAcBqp4WsEt5aSB/CUnhTnRzVTDGApIgxgFZABDMBw2Rfj1whuDSUP4Ek9Kd4Y1UwygKWIMIBVQAYwAMNhX45fI7j+Sh7AE3tS/M+oZqIBLEWEAawCMoABGKoc49cIrjcDWKpnBrCyZwADsLdyDl8juN5KHsAdPSlOimo6DGApIgxgFZABDMDeKGX8GsH1VPIA3r8nxXOimv2HYQCvXLkyFi1aFGPHjo25c+fGDTfcsNO3vfzyy2P27NmNt/3mN785pD9bGq4MYGXPAAagqtLGrxFcPyUP4HE9Kf5HVDNuiAO4r68vZs2aFV1dXbF06dJYtGhRtLe3x913373D21599dWRUornPe95cc0118TFF18cX/nKV4bybkvDlgGs7BnAAFRR6vg1guul5AE8uifFrKhm9BAH8E033RQppbjwwgsjIuLmm2+OlFKce+65O7zt7Nmzo6OjIx577LF48skn9/rPlEYiA1jZM4AB2FOlj18juD5KHsDtPSk6o5r2nhTjxo2Lnp6eQe2uK664IlJKsXTp0oiIWLVqVaSU4rTTThvwdmvXro2UUkyePDkOOuigSCnFscceG/fcc8+I3B9S1QxgZc8ABmBPNMv43XYE577P2HslD+DUk6Ly/4ZpAF911VUREXHXXXcNOoAffvjhxv323ve+N9773vdGSilOPvnkEbk/pKoZwMqeAQzAnmim8WsAN7/iB/CmiobpEugLLrggIgZeAt3X1xfr16+PLVu2RETExIkTI6UUGzZsiA0bNkRKKRYsWDAs77801AxgZc8ABmBPNOMANoKbV9EDeGGK9FRFC4f+TbBmzpwZXV1dsWzZsli8eHG0tbXFqlWrYsmSJZFSalzm/Ja3vCVSSnHeeefFeeedFymlOPvss4frLpCGlAGs7BnAAOyJZhvAXgVubsUP4PUVDXEAR0Tceeedcdxxx8WYMWNizpw5sXz58oiIHQbw2rVr4zWveU10dHTElClT4rWvfW38/ve/H/L7Lg1HBrCyZwADsDvNOH69Ctzcih7AC1KkdRUtGPoAluqQAazsGcAA7E6zDmCvAjev4gfw4xUZwFJEGMAqIAMYgN0xgNnXih/Aj1ZkAEsRYQCrgAxgAHal2f75o8EGsBHcfAxgqZ4ZwMqeAQzArjTz+PUqcPMqegAfkSI9XNERBrAUYQCrgAxgAHbFACaH4gfwmooMYCkiDGAVkAEMwK7UYQAbwc2n6AF8eIr0YEWHG8BShAGsAjKAAdiZZv/6XwO4eRU/gO+vyACWIsIAVgEZwADsTF3G79YBbAQ3j6IH8PwU6T8qmm8ASxEGsArIAAZgZ+o0gL0K3FyKH8C/qsgAliLCAFYBGcAA7IwBTC5FD+DDUqR7KzrMAJYiDGAVkAEMwGDq9PW/2w5gI7g5FD+A/72iJhzAfX198fnPfz7e8573xCWXXNIgDSUDWNkzgAEYTN3Gr1eBm0vRA/jQFOlnFR3afAP4xS9+cZxyyilxySWXxLvf/e4GaSgZwMqeAQzAYOo8gI3g8hU/gFdV1IQDuNlur5ojA1jZM4ABGExdB7BXgZtD8QP43ypqwgH86le/On7961/nvhmqWQawsmcAA7C9Oo9fA7g5FD2A56VIP65oXvMM4D/8wz+MF7zgBfGc5zwnOjo64oQTTogXvOAFDdJQMoCVPQMYgO21wgA2gstmAOfrO9/5zi5JQ8kAVvYMYAC2V/cB7FXg8hU/gFdU1EQD+A1veEN85StfiSeffDL3TVENM4CVPQMYgG3V8Z8/MoCbT9EDeG6K9K8VzW2eAXzrrbfGeeedF894xjPipJNOiquuuip+9atf5b5ZqkkGsLJnAAOwrVYYv0Zw+YofwD+oqIkG8LatXr06rrzyyvijP/qjOProo+P88893GbSGlAGs7BnAAGzLAKYERQ/gOSnS9yqa05wDeNueeOKJuOmmm+KNb3xj7puiJs4AVvYMYAC21WoD2AguU/ED+PaKmnQA33777fHZz342PvWpTzVIQ8kAVvYMYAC2apWv//UqcPmKHsCzU6TbKprdfAP4Va96VRx//PHxhje8Ic4+++w4++yz401velPum6UmzwBW9gxgALZqtfFrAJer+AH87YqacAAvXLgw901QDTOAlT0DGICtWnUAG8HlKX4A/1NFTTiAzzjjjPj1r3+d+2aoZhnAyp4BDMBWrTiAvQpcpqIH8CEp0s0VHdJ8A/jOO++MqVOnxrOf/ew48cQTG6ShZAArewYwAL3Rml//61XgchU/gL9aURMO4AULFsSVV14Zt99+e3zve99rkIaSAazsGcAA9EbrvvrrVeAyFT2AZ6VI/1jRrOYbwMcee2zum6AaZgArewYwAL1hABvAZSl+AP99RU04gC+66KL40Ic+FL/85S/jgQceaJCGkgGs7BnAAPSGAewy6LIYwPmbM2fODubOnZv7ZqnJM4CVPQMYgFYfv14FLk/RA3hmivR/KprZfANYGokMYGXPAKbutr6qszdy33bYVwxgA7g0xQ/g3oqadADfd9998c///M9xyy23NEhDyQBW9gxgSjWU4bqtoZwIOxmmVRjARnBpih7A3SnSjRV1N98APu+88+KQQw6JF77whfGiF70oXvSiF8VJJ52U+2apyTOAlT0DmJGQe7wO54mwk2HqrpTjrRSO+TIUP4Cvr6gJB/Dhhx8efX19uW+GapYBrOwZwOytkoerEQx7rm7HrGO+HoofwMsrasIB/JKXvCQee+yx3DdDNcsAVvYMYPZGHYeuE2JaVSsdy1WO+dyPS6sregDPSJE+U9GM5hvAP/3pT2P+/Pnx6le/Ol772tc2SEPJAFb2DGD2RqueMDsppo5a9Xh2rJet+AF8bUVNOICPPfbYOPfcc2P58uVx/fXXN0hDyQBW9gxgqmq1V38HOzF2ckxdtPrx7DgvV9ED+OAU6ZMVHdx8A/ioo47KfRNUwwxgZc8Apiony06OqQ/H866P89yPTysrewC3RfrkftUc3NZ0A/hd73pXfPnLX47NmzfnvimqUQawsmcAU4VXiwaeHDtBptk5nh3jpSp7AI+KdM2Uag4e1XQDePTo0dHW1hajRo2KMWPGxOjRo2PMmDG5b5aaPANY2TOAqcLJshNk6sUxvftjPPdj1KqKHsAzRkf61KxqZoxuugEsjUQGsLJnALOnvPq78xNkJ8k0I8f0nh3fuR+nVlX2AB4b6dMLqpkx1gCWwgBWARnA7Cknyrs+SXaiTLNxTDu2S2YAS/XMAFb2DGD2lJNlJ8rUi2N6z4/t3I9VKyp7AO8X6TPHVjNjPwNYCgNYBWQAsyecKO/5ibKTZZqF43rPj+vcj1UrKnsAj4/02edVM2O8ASyFAawCMoDZE06Uq50sO2GmdI7p6sd17ses1RQ9gLs7Ii1/UTXdHQawFAawCsgAZnecKO/dybITZkrmuK5+TOd+zFpN2QP4gEife1k13QcYwFIYwCogA5jdcaK89yfMTpoplePa8Vy6sgdwZ6QbXlNNd+eQB/DKlStj0aJFMXbs2Jg7d27ccMMNu3z7t73tbY37cOPGjUP6s6XhygBW9gxgdsU/kzI8J865H0fYluPasdwMyh7AkyJ9/uxquicNaQD39fXFrFmzoqurK5YuXRqLFi2K9vb2uPvuuwd9+zvuuCPGjh0b++23nwGsojKAlT0DmF1xkuzEmfpxXDuOm0HZA3hKpBvPq6Z7ypAG8E033RQppbjwwgsjIuLmm2+OlFKce+65O7zthg0boqenJ84777yYPXu2AayiMoCVPQOYnfEq0fCfPDuBpgSOa8dwMyh6AM+cGumL76xm5tQYN25c9PT0DGp3XXHFFZFSiqVLl0ZExKpVqyKlFKeddtoOb3vhhRfGEUccEevXrzeAVVwGsLJnALOtrSd4xu/InUDnfozBse0YbgZlD+BpkXrfX83MacMygK+66qqIiLjrrrsGHcC/+MUvYvTo0fG5z30u7rnnnuju7o6UUvz85z+PTZs2jch9IlXJAFb2DODWtf3YNXqdPNMaHOeO4WZQ9gCeHqn3A9XMnD4sl0BfcMEFETHwEui+vr5Yv359bNmyJW677bbG/ba9Bx98cLjuBmmvM4CVPQO4dRi7+Tl5pgSOfcdwMyh/AH+omiEO4L6+vpg5c2Z0dXXFsmXLYvHixdHW1harVq2KJUuWREop7rnnnlizZk186Utfaujq6oqUUnzxi1+MDRs2DOM9Ie1dBrCyZwDXk7FbJifPlMDzgWO4GZQ9gA+K1PuRamYeNOR/BunOO++M4447LsaMGRNz5syJ5cuXR0QMGMDb52uAVVoGsLJnADc/lzI3DyfPlMDzg2O4GZQ/gD9WzTAMYKkOGcDKngHcfIzd5uXkmRJ4znAMNwMDWKpnBrCyZwCXzditF/+MCiXwPDK0Yzj349cqyh/AS6sxgKWIMIBVQAZwOVzK3BqcQJOb5xXHbzMoewAfHKn36mpmHmwAS2EAq4AM4HyM3dbkBJrcPNc4hptB+QP4mmoMYCkiDGAVkAG8bxi7OHmmFJ5/HMPNoOwBPCNS76eqmTnDAJbCAFYBGcAjx+BlME6eyc3zkWO4GZQ/gK+rxgCWIsIAVgEZwCPD6GVnnDyTm+cmx3AzKHsAd0fq/Vw1M7sNYCkMYBWQATz8jF92xckzuXl+cgw3g/IH8A3VGMBSRBjAKiADePgYvuwJJ8/k5nnKMdwMyh7AMyP1fqGamTMNYCkMYBWQATw8jF/2lJNncvNc5RhuBuUP4C9VYwBLEWEAq4AM4KEzfqnCyTO5eb5yDDeDsgfwrEhf+PtqZs4ygKUwgFVABvDQGL9U5eSZ3DxnOYabQdEDuHtWpBu/Uk23ASxFGMAqIAN47xi+7C0nz+Tmucsx3AzKHsCHRPr8zdV0H2IAS2EAq4AM4OqMX4bCyTO5ef5yDDcDA1iqZwawsmcAV2P8MhycQJOT5zDHbzMoewDPjnT9t6rpnm0AS2EAq4AM4D1n/DJcnECTk+cxx28zKH4Af+62agxgKSIMYBWQAbxnjF+GkxNocvJc5hhuBmUP4DmRln+3mu45BrAUBrAKyADeNcOXkeDkmZw8pzmGm0HRA3jG3EjX/aCaGXMNYCkMYBWQAbxzxi8jxckzOXlecww3g+IH8GfurMYAliLCAFYBGcCDM34ZSU6eyclzm2O4GZQ9gOdF+vSPq5kxzwCWwgBWARnAOzJ+GWlOnsnJ85tjuBmUPYAPjXTtXdXMONQAlsIAVgEZwAMZv+wLTp7JyXOcY7gZFD2ADz400jU/q+ZgA1iKMIBVQAZwP8OXfcnJMzl5rnMMN4OyB/BhkT55TzUHH2YAS2EAq4AMYOOXfc/JMzl5vnMMN4OyB/D8SFevrubg+QawFAawCqjVB7DxSw5OnsnJc55juBkUPYAPmh9p6epqDjKApQgDWAXUygPY+CUXJ8/k5HnPMdwMih/AV66uxgCWIsIAVgG14gA2fMlt68dg7mOB1uT5b3iO4dyPY90ZwFI9M4CVvVYbwMYvpXACTS6eAx2/zaD4AfyR1dUYwFJEGMAqoFYawMYvJXECTS6eBx2/zaDoATx9fqQPra5mugEsRRjAKqBWGcDGL6VxAk0ungsdw82g+AF8+epqDGApIgxgFVArDGDjlxI5eSYXz4eO4WZQ9ACeNj/S+1dXM80AliIMYBVQnQew4UvJnDyTi+dFx3AzKH4Av2d1NQawFBEGsAqorgPY+KV0Tp7JxXOjY7gZFD2Au+ZHunR1NV0GsBRhAKuA6jiAjV+agZNncvH86BhuBsUP4L9dXY0BLEWEAawCqtsANn5pFk6eycVzpGO4GRQ9gKfOj/TO1dVMNYClCANYBVSXAbx1+Dqxo1k4eSYXz5OO4WZQ/AC+aHU1BrAUEQawCqgOA9jwpRk5eSYXz5eO4WZQ/AC+cHU1BrAUEQawCqjZB7DxS7Ny8kwunjMdw82g6AE8eX6k/726mskGsBRhAKuAmnkAG780MyfP5OJ50zHcDIoewJPmR3rj6momGcBShAGsAmrWAWz8UgdOoMnBc6fjtxkYwFI9M4CVvWYbwIYvdeIEmhw8hzp+m0HRA7hzfqTXrK6m0wCWIgxgFVAzDWDjl7pxAk0Onkcdv82g6AE8cX6kl6+uZqIBLEUYwCqgbQfw2b95ZfZPeLs6YXPSRt04gSYHz6WO4WZQ9ADumB/p5NXVdBjAUoQBrALadgBf+e9XDvj3dEv5xG78UlelHGO0Fs+njuFmUPQAHj8/0omrqxlvAEsRBrAKaPsBvP0n9txj2Pilzpw8k4PnVMdwMyh6AO8/P9Lxq6vZ3wCWIgxgFdCuBvD2n+T35Rg2fGkFTp7JwXOrY7gZFD2Ax82PdNTqasYZwFKEAawC2tMBvP0n/JEcw8YvrcLJMzl4fnUMN4OiB/DY+ZHmr65mrAEsRRjAKqC9GcDbf/IfzjFs/NJKnDyTg+dYx3AzKHoAj54faebqakYPfQCvXLkyFi1aFGPHjo25c+fGDTfcMOjbnXPOOTF37tzYb7/9YtasWfGe97xnSH+uNJwZwMreUAfwYCcDezuIjV9ajZNncvA86xhuBkUP4FHzI01ZXc2ooQ3gvr6+mDVrVnR1dcXSpUtj0aJF0d7eHnffffcOb3vcccfFRRddFJ/4xCdi7ty5kVKKm266aSjvtjRsGcDK3nAP4O1PDPZkDBu+tConz+Tg+dYx3AyKHsBt8yPtv7qatqEN4JtuuilSSnHhhRdGRMTNN98cKaU499xzd3jbDRs2NP7/Rz/60UgpeRVYxWQAK3sjOYC3P0nY2RB2MkarcvJMDp5zHcPNoOgBnOZHSqsrmh/jxo2Lnp6eQe2uK664IlJKsXTp0oiIWLVqVaSU4rTTTtvlr3vRi14UKaX47ne/OyzvvzTUDGBlb6QG8Pav/u7qVWAnY7Sikv6tbVrLts/JuY+DZpX7nwhsBWUP4MMipV9UdNiwDOCrrroqIiLuuuuuXQ7gLVu2xF/8xV9ESine+c53Dut9IA0lA1jZG44BXGXs7upkLPcJDewrTpwpgefevT92Hb8jr44DeDgugb7gggsiYuAl0H19fbF+/frYsmVLRERs2rQpXvOa10RKKd7xjncMy/stDVcGsLJXdQAPdew6EaPVOXmmNJ5/9/y4dezuO2UP4EMjpf9X0aFD/iZYM2fOjK6urli2bFksXrw42traYtWqVbFkyZJIKcU999wTERGvfvWrI6UUz33uc+PGG2+MG2+8MX74wx8O110gDSkDWNnb1QAeiaG7u5Ow3Cc5MJKcQFMql0Xv+ph13O57ZQ/geZHSTyuaN+R/BunOO++M4447LsaMGRNz5syJ5cuXR0TsMIBnz57duO+2Ouuss4b6rkvDkgGs7G07gM/+zSuzf7J38kWdOYmmdEbwfx+rhm9e5Q/glRUNfQBLdcgAVva2HcDL112Z/ROeky/qyok0zaLVXw02fMtQ9gCeGyndUdFcA1gKA1gFVNoA7g0jmPpxMk0zarXnYsO3LGUP4DmR0r9UNMcAlsIAVgGVOIB7w6XQ1IcTappd3Yew4Vum8gfwP1dkAEsRBrAKqOQBXOcTLlqDE2vqoo7Pyb7Ot2xlD+DZkdItFc02gKUwgFVApQ7g3qjnCRetw4k1dVSH52XDtzmUPYAPiZS+WdEhBrAUBrAKqOQB3BsuhaY5Obmmzpr1m2QZvs2l/AH8tYoMYCnCAFYBlT6Ae8MIprk4waZVNMsINnybU9kDeFakdFNFswxgKQxgFVCzDOBmOMkCJ9m0mpJfDTZ8m1vZA3hmpPTlimYawFIYwCqgZhjAvWEEUz4n2rSy0p6jHY/Nr/QBPCr1VmIAS/0ZwMpeswzg3nApNOVysg39cg9hx2J9GMBSPTOAlb1mGsC9YQRTHifcMFCOy6Idh/VT8gBuTzPjgPSFStoNYCkiDGAVUDMOYCOYUjjphp3bF8/Xvs63vkoewKNTdxyUbqxkdOo2gKUwgFVAzTaAe8MIpgxOumH3RurVYMO3/koewGNSdxyaPl/JGANYiggDWAXUjAO4N1wKTV5OvKGa4RrBhm/rKHkAj0vd8fR0fSXjDGApIgxgFVAzD2AjmBycfMPe29vnbsO39ZQ8gPdP3XF8+lwl+xvAUkQYwCqgZh3AvWEEs+85AYehq3JZtOHbukoewBNSd/x/aXklEwxgKSIMYBVQMw/g3nApNPuOk3AYXrsbwY651lbyAO5IM+LUdF0lHWmGASyFAawCavYB3BtGMCPPiTiMjMFeDXa80RtlD+CJaUb8z/SZSiYawFJEGMAqoLoMYCOYkeJkHEaeS53ZXskD+MA0I/48fbqSAw1gKSIMYBVQHQZwbxjBjBwn5AD7XskDeHKaEX+Zrq1ksgEsRYQBrAKqywDuDZdCA1A3KgAAEGpJREFUM/yMX4A8Sh7AU9PBcWG6ppKp6WADWAoDWAVUtwFsBDNcjF+AfEoewF3p4LgsfbKSrmQASxEGsAqoTgO4N4xghs7XIQLkZwBL9cwAVvbqNoB7w6XQ7D3jF6AMJQ/gaengeF+6upJpyQCWIgxgFVAdB3BvGMFUZ/wClKP0Afze9PFKDGCpPwNY2avzADaC2RP++RWA8pQ+gN+TllViAEv9GcDKXl0HcG8Yweye4QtQppIHcFc6KC5LV1XSlQ4ygKUwgFVAdR7AveFSaHbO+AUoV+kD+NL0sUoMYKk/A1jZq/sA7g0jmIFc8gxQvtIH8CXpykoMYKk/A1jZa5UBbATzifu96gvQLEofwBenj1RiAEv9GcDKXisM4N4wgjF+AZpJ2QN4erwrfbiSrjTdAJbCAFYBtcoA7g2XQrcy4xeguZQ8gKem6fHOdEUlUw1gKSIMYBVQqw1gI7i1GL4Azan0Afw36YOVGMBSfwawstdKA7g3jOBWYvwCNK/SB/BF6QOVGMBSfwawstdqA7g3XArdCoxfgOZW+gB+e7q8EgNY6s8AVvZacQD3hhFcV4YvQD0YwFI9M4CVvVYewEZwvRi/APVR8gCekqbFBen9lUxJ0wxgKQxgFVCrDuDeMILrxPgFqJeSB/Dk1BX/O/1tJZNTlwEshQGsAmrlAdwbLoVudoYvQD2VPIAnpSnxxnR+JZPSFANYCgNYBWQAexW4WRm/APVV8gDuTJPiNenNlXSmSQawFAawCqjVB3BvGMHNyPgFqLeSB/DEdGC8PP1pJRPTgQawFAawCsgA7mcANwfDF6A1lDyAO9LEODm9opKONNEAlsIAVgEZwP/NCC6b8QvQOkoewONTR5yYTq5kfOowgKUwgFVABvB/cyl0uYxfgHKNxHN0yQN4/zQ+jk/Pr2T/NN4AlsIAVgEZwIN/Es89+DB8AZrB1s+Zw/18XfIAHpf2i6PSMysZl/YzgKUwgFVABvDOP5lj/AKwc9v/pfFwPm+XPIDHpnExPz2tkrFpnAEshQGsAjKAd/5JPfcAbGXGL0DZdnXF1HA8h5c8gEenMTEzzalkdBoz5AG8cuXKWLRoUYwdOzbmzp0bN9xww6Bv9/DDD8fpp58e48ePj6lTp8bb3va22Lx585D+bGm4MoCVPQO4+id2Rn74Gr8A5dqTz5FDfS4veQCPSqNiSuqqZFQaNaQB3NfXF7NmzYqurq5YunRpLFq0KNrb2+Puu+/e4W1f+cpXRnt7e1x66aVx5plnRkoprr766qG829KwZQArewbw0D7BM/zjN/fjDsCu7ennxqH8paYBPLCbbropUkpx4YUXRkTEzTffHCmlOPfccwe83aOPPhrt7e1x/PHHR0T/ed6oUaPimGOO2ft3WhrGDGBlzwAenk/yGL8ArWBvPi/uzXN8yQM4pRRtqa2SlFKMG9f/dcCD2V1XXHFFpJRi6dKlERGxatWqSCnFaaedNuDtfvSjH0VKKV7xilc0fmzq1KnR2dk5vHeEtJcZwMqeAbz7T/ReCR7Z0Wv8AjSPKp8Th/I8X+oAPvXUU3c6Yneno6NjyAP4qquuioiIu+66a5cD+OUvf3njx6ZMmWIAq5gMYGXPAB7+T/js+QlR7scVgL2zs8+Lw/WXm6UO4FxtvQT6ggsuiIiBl0D39fXF+vXrY8uWLY1LoBcvXhwREWvXrnUJtIrKAFb2DODqn+wNYaMXgH4jdTWPATywvr6+mDlzZnR1dcWyZcti8eLF0dbWFqtWrYolS5ZESinuueeeiIh4xSteEe3t7XHZZZc1vgnWsmXLMr8HUn8GsLJnAO/9J/vco7J0LnEGYG8ZwDt25513xnHHHRdjxoyJOXPmxPLlyyMidhjAa9asiZe+9KWx//77x+TJk+Otb31rbNq0KedNlxoZwMqeAbx3vBps9AIwcgxgqZ4ZwMqeATw0RvCOwzf3YwJA8zOApXpmACt7BvDQteqrwUYvACPFAJbqmQGs7BnAw6eZRvD2lyrvrdz3OQD1ZABL9cwAVvYM4OE10q8GG64AtAIDWKpnBrCyZwCPjMFGsPEKAHvGAJbqmQGs7BnAI8dwBYC9YwBL9cwAVvYMYACgNAawVM8MYGXPAAYASmMAS/XMAFb2DGAAoDQGsFTPDGBlzwAGAEpjAEv/f7v2jiPVGUVhdJgEHpIzMqceCKlHQYg6RYjMEjhAoKJd/ayq3ufus5b0TaD7HvW/BZ0MYOIMYEmSNC0DGDoZwMQZwJIkaVoGMHQygIkzgCVJ0rQMYOhkABNnAEuSpGkZwNDJACbOAJYkSdMygKGTAUycASxJkqZlAEMnA5g4A1iSJE3LAIZOBjBxBrAkSZqWAQydDGDiDGBJkjQtAxg6GcDEGcCSJGlaBjB0MoCJM4AlSdK0DGDoZAATZwBLkqRpGcDQyQAmzgCWJEnTMoChkwFMnAEsSZKmZQBDJwOYOANYkiRNywCGTgYwcQawJEmalgEMnQxg4gxgSZI0LQMYOhnAxBnAkiRpWgYwdDKAiTOAJUnStAxg6GQAE2cAS5KkaRnA0MkAJs4AliRJ0zKAoZMBTJwBLEmSpmUAQycDmDgDWJIkTcsAhk4GMHEGsCRJmpYBDJ0MYOIMYEmSNC0DGDoZwMQZwJIkaVoGMHQygIkzgCVJ0rQMYOhkABNnAEuSpGkZwNDJACbOAJYkSdMygKGTAUycASxJkqZlAEMnA5g4A1iSJE3LAIZOBjBxBrAkSZqWAQydDGDiDGBJkjQtAxg6GcDEGcCSJGlaBjB0MoCJM4AlSdK0DGDoZAATZwBLkqRpGcDQyQAmzgCWJEnTMoChkwFMnAEsSZKmZQBDJwOYOANYkiRNywCGTgYwcQawJEmalgEMnQxg4gxgSZI0LQMYOhnAxBnAkiRpWgYwdDKAiTOAJUnStAxg6GQAE2cAS5KkaRnA0MkAJs4AliRJ0zKAoZMBTJwBLEmSpmUAQycDmDgDWJIkTcsAhk4GMHEGsCRJmpYBDJ0MYOIMYEmSNC0DGDoZwMQZwJIkaVoGMHQygIkzgCVJ0rQMYOhkABNnAEuSpGkZwNDJACbOAJYkSdMygKGTAUycASxJkqZlAEMnA5g4A1iSJE3LAIZOBjBxBrAkSZqWAQydDGDiDGBJkjQtAxg6GcDEGcCSJGlaBjB0MoCJOx3AH+7exf/gSZIkGcDQyQAm7nQAf757//2fL3/8Kv3HT5Ik7cwAhk4GMHH3B/C/X/76lTEsSZISGcDQyQAm7rEBbAxLkqREBjB0MoCJe+4ANoYlSdJbZQBDJwOYuNcM4HND2BiWJEnXygCGTgYwcZcMYGNYkiTdIgMYOhnAxF1rABvDkiTpWhnA0MkAJu4WA9gYliRJl2QAQycDmLhbD2BjWJIkvTQDGDoZwMS95QB+bAwbxJIk6WcGMHQygIlLDeDnDGLjWJKknRnA0MkAJm7KADaOJUnSzwxg6GQAE3eEAWwcS5K0KwMYOhnAxB19AF8ykNN/3CVJ0vkMYOhkABPXPoCNY0mSjpcBDJ0MYOI2D+DXjmMDWZKk22YAQycDmDgD2DiWJGlaBjB0MoCJM4BvP47TjwhJko6WAQydDGDiDOC3G8Xpx4QkSUfJAIZOBjBxBvDbD2FjWJKkxzOAoZMBTJwBbAhLkjQtAxg6GcDEGcCGsCRJ0zKAoZMBTJwBnM8QliTp9wxg6GQAE2cAz8kQliTpRwYwdDKAiTOA52UIS5K2ZwBDJwOYOAN4boawJGlrBjB0MoCJM4DnZwhLkrZlAEMnA5g4A/g4GcKSpC0ZwNDJACbOAD5ehrAkqT0DGDoZwMQZwMfNEJYktWYAQycDmDgDuCNDWJLUlAEMnQxg4gzgrgxhSVJDBjB0MoCJM4A789+jJUlHzgCGTgYwcQZwd4awJOmIGcDQyQAmzgDekSEsSTpSBjB0MoCJM4B3ZQhLx+z0dt2vNmQAQycDmDgDeGce0tIxOr1V96tNGcDQyQAmzgDenUe0NLf7w9f9alMGMHQygIkzgOURLc3rqfHrhtWeAQydDGDiDGB5QEtzesnwPXfD7lgtGcDQyQAmzgDW/Qd0+tEjbe214/fcHbtlHT0DGDoZwMQZwDr3eE4/fKRtXWP8umU1ZQBDJwOYOANY9/Nwlt62a49f96yGDGDoZAATZwDrXB7N0u275fB10zp6BjB0MoCJM4D1UB7M0u16y/F7/6bdtY6QAQydDGDiDGA9lseydP0S4/fcXbttTc4Ahk4GMHEGsJ7KQ1m6Xunx67Z1lAxg6GQAE2cA6zl5KEuXNWn4um8dIQMYOhnAxBnAem4eydLrmjx+79+3G9eUDGDoZAATZwDrJXkgSy/rCOP33I27c6UzgKGTAUycAayX5nEsPd3pkEzf7CV37taVygCGTgYwcQawXtPp49gjWfq9Iw/fh249/TPVjk7/pny4e2cAQyEDmDgDWNfo3CA2jrWxpvF77sbTP18dv8f+Vpx+c5/v/AswNDKAiTOAdeuMY22pdfyeu+X0z1qze+7IfSwDGDoZwMQZwEpmHKuh9uH72N2mf/bKd+nQfSgDGDoZwMQZwJqacawjtG38nrvR9O9And+/AQydDGDiDGAdMeNYE9o8fs/dY/r3oa5v3wCGTgYwcQaw2jKO9RYZvw/fXfp3o47v3gCGTgYwcQawNmUc69IM3+ffWPp3pet/92/57RvA0MkAJs4Aln5kFOupjN/X3VP696bLv/nEd28AQycDmDgDWHo8g1inQyD9PR4td3Pc0t+8AQydDGDiDGDp+fkX4p2lh0BD7uU4TfneDWDoZAATZwBLr88g7m7KEGjJncxu2vduAEMnA5i40wH86eOf3z/fvZf0yj7cvftff399rwP28/eX/qYacx+zOv19pL+N0z59/NMAhkIGMHGnA1iSJGlaBjD0MIAZI/3HTZIk6VwGMPQwgBnj69evkiRJ4/r27Vv6mQRciQEMAADACgYwAAAAKxjAAAAArGAAAwAAsIIBDAAAwAoGMAAAACsYwAAAAKxgAAMAALCCAQwAAMAKBjAAAAArGMAAAACsYAADAACwggEMAADACgYwAAAAKxjAAAAArGAAAwAAsIIBDAAAwAoGMAAAACsYwAAAAKxgAAMAALCCAQwAAMAKBjAAAAArGMAAAACsYAADAACwggEMAADACgYwAAAAKxjAAAAArGAAAwAAsIIBDAAAwAoGMAAAACsYwAAAAKxgAAMAALCCAQwAAMAKBjAAAAArGMAAAACsYAADAACwggEMAADACgYwAAAAKxjAAAAArGAAAwAAsIIBDAAAwAoGMAAAACsYwAAAAKxgAAMAALCCAQwAAMAKBjAAAAArGMAAAACsYAADAACwggEMAADACgYwAAAAKxjAAAAArGAAAwAAsIIBDAAAwAoGMAAAACsYwAAAAKxgAAMAALCCAQwAAMAKBjAAAAArGMAAAACsYAADAACwggEMAADACgYwAAAAKxjAAAAArGAAAwAAsIIBDAAAwAoGMAAAACsYwAAAAKxgAAMAALCCAQwAAMAKBjAAAAArGMAAAACsYAADAACwggEMAADACgYwAAAAKxjAAAAArGAAAwAAsIIBDAAAwAr/AXCXwzzVD9dQAAAAAElFTkSuQmCC\" width=\"960\">" |
|
], |
|
"text/plain": [ |
|
"<IPython.core.display.HTML object>" |
|
] |
|
}, |
|
"metadata": {}, |
|
"output_type": "display_data" |
|
} |
|
], |
|
"source": [ |
|
"%matplotlib notebook\n", |
|
"%matplotlib notebook\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", |
|
"projection = ccrs.PlateCarree()\n", |
|
"\n", |
|
"fig = plt.figure(dpi=150)\n", |
|
"\n", |
|
"ax = plt.axes(projection = projection)\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, crs=ccrs.PlateCarree())" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": 7, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"fname = './resources/T23MNT_20190525T132241_TCI_60m.jp2'\n", |
|
"\n", |
|
"img = plt.imread(fname)" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": 8, |
|
"metadata": {}, |
|
"outputs": [], |
|
"source": [ |
|
"# fig = plt.figure(dpi=150)\n", |
|
"\n", |
|
"plt.xlim(-2.805, -1.809)\n", |
|
"plt.ylim(-45, -44)\n", |
|
"\n", |
|
"ax = plt.axes(projection = projection)\n", |
|
"\n", |
|
"ax.coastlines('10m')\n", |
|
"ax.gridlines()\n", |
|
"\n", |
|
"ax.set_extent(extent, crs=ccrs.PlateCarree())\n", |
|
"\n", |
|
"img = plt.imread(fname)\n", |
|
"\n", |
|
"ax.imshow(img, extent=extent, origin='upper', transform=ccrs.PlateCarree())\n", |
|
"\n", |
|
"# mark a known place to help us geo-locate ourselves\n", |
|
"ax.plot(-44.243317, -2.565823, 'bo', markersize=7, color='red', transform=ccrs.Geodetic())\n", |
|
"ax.text(-44.23, -2.54, 'São Luíz', color='black', size=10, transform=ccrs.Geodetic())\n", |
|
"\n", |
|
"plt.title('January 2019 Monthly Average Rain Rate')\n", |
|
"\n", |
|
"font = {'weight' : 'bold', 'size' : 5}\n", |
|
"\n", |
|
"plt.rc('font', **font)" |
|
] |
|
}, |
|
{ |
|
"cell_type": "code", |
|
"execution_count": 9, |
|
"metadata": {}, |
|
"outputs": [ |
|
{ |
|
"data": { |
|
"text/plain": [ |
|
"<matplotlib.colorbar.Colorbar at 0x7f5eadbe3f60>" |
|
] |
|
}, |
|
"execution_count": 9, |
|
"metadata": {}, |
|
"output_type": "execute_result" |
|
} |
|
], |
|
"source": [ |
|
"# fig = plt.figure(dpi=150)\n", |
|
"\n", |
|
"hdf5 = '/notebooks/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,1.26,0.125)\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.xlim(-2.805, -1.809)\n", |
|
"plt.ylim(-45, -44)\n", |
|
"\n", |
|
"cs = ax.contourf(x, y, precip, clevs, transform=ccrs.PlateCarree(), 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 |
|
}
|
|
|