text:
module js

import GR

pxwidth = 640
pxheight = 480

@static if VERSION < v"0.7.0-DEV.4762"
    macro cfunction(f, rt, tup)
        :(Base.cfunction($(esc(f)), $(esc(rt)), Tuple{$(esc(tup))...}))
    end
end

id_count = 0
js_running = false

mutable struct JSTermWidget
    identifier::Int
    width::Int
    height::Int
    visible::Bool
end

function inject_js()
  global comm
  comm = nothing
  _js_fallback = "https://gr-framework.org/downloads/gr-latest.js"
  _gr_js = if isfile(joinpath(ENV["GRDIR"], "lib", "gr.js"))
    _gr_js = try
      _gr_js = open(joinpath(ENV["GRDIR"], "lib", "gr.js")) do f
        _gr_js = read(f, String)
        _gr_js
      end
    catch e
      nothing
    end
    _gr_js
  end
  _jsterm = """
    if (typeof grJSTermRunning === 'undefined' || !grJstermReady) {
      BOXZOOM_THRESHOLD = 3;  // Minimal size in pixels of the boxzoom-box to trigger a boxzoom-event
      BOXZOOM_TRIGGER_THRESHHOLD = 1000;  // Time to wait (in ms) before triggering boxzoom event instead
                              // of panning when pressing the left mouse button without moving the mouse
      MAX_KERNEL_CONNECTION_ATTEMPTS = 25;  // Maximum number of kernel initialisation attempts
      KERNEL_CONNECT_WAIT_TIME = 100; // Time to wait between kernel initialisation attempts
      RECONNECT_PLOT_TIMEOUT = 100; // Time to wait between attempts to connect to a plot's canvas
      RECONNECT_PLOT_MAX_ATTEMPTS = 50; // Maximum number of canvas reconnection attempts
      BOXZOOM_FILL_STYLE = '#FFAAAA'; // Fill style of the boxzoom box
      BOXZOOM_STROKE_STYLE = '#FF0000'; // Outline style of the boxzoom box

      var gr, comm, idcount = 0, widgets = [], jupyterRunning = false;

      /**
       * Loads a javascript file from `url` and calls `callback` when loading has been finished.
       * @param  {string}   url      URL to load script from
       * @param  {Function} callback Function to call when loading is finished
       * @param  {number}   maxtime  Maximum time in ms to wait for script-loading
       */
      saveLoad = function(url, callback, maxtime) {
        let script = document.createElement('script');
        script.onload = function() {
          callback();
        };
        script.onerror = function() {
          console.error(url + ' can not be loaded.');
        };
        script.src = url;
        document.head.appendChild(script);
        setTimeout(function() {
          if (!grJstermReady) {
            console.error(url + ' can not be loaded.');
          }
        }, maxtime);
      };

      /**
       * Sends an object describing a event via jupyter-comm
       * @param  {Object} data Data describing the event
       * @param  {string} id   Identifikator of the calling plot
       */
      sendEvt = function(data, id) {
        if (jupyterRunning) {
          comm.send({
            "type": "evt",
            "content": data,
            "id": id
          });
        }
      };

      /**
       * Runs draw-events cached during javascript startup
       */
      jsLoaded = function() {
        grJstermReady = true;
        for (let or in onready) {
          or();
          onready = [];
        }
      };

      /**
       * Sends a canvas-removed-event via jupyter-comm
       * @param  {number} id The removed plot's id
       */
      createCanvas = function(id, width, height) {
        if (jupyterRunning) {
          comm.send({
            "type": "createCanvas",
            "id": id,
            "width": width,
            "height": height
          });
        }
      };

      /**
       * Sends a save-data-event via jupyter-comm
       * @param  {Object} data Data to save
       * @param  {string} id   plot identificator
       */
      saveData = function(data, id) {
        if (jupyterRunning) {
          comm.send({
            "type": "save",
            "content": {
              "id": id,
              "data": JSON.stringify(data)
            }
          });
        }
      };

      /**
       * Registration/initialisation of the jupyter-comm
       * @param  {[type]} kernel Jupyter kernel object
       */
      registerComm = function(kernel) {
        kernel.comm_manager.register_target('jsterm_comm', function(c) {
          c.on_msg(function(msg) {
            let data = msg.content.data;
            if (data.type == 'evt') {
              if (typeof widgets[data.id] !== 'undefined') {
                widgets[data.id].msgHandleEvent(data);
              }
            } else if (msg.content.data.type == 'cmd') {
              if (typeof data.id !== 'undefined') {
                if (typeof widgets[data.id] !== 'undefined') {
                  widgets[data.id].msgHandleCommand(data);
                }
              } else {
                for (let key in widgets) {
                  widgets[key].msgHandleCommand(data);
                }
              }
            } else if (data.type == 'draw') {
              draw(msg);
            }
          });
          c.on_close(function() {});
          window.addEventListener('beforeunload', function(e) {
            c.close();
          });
          comm = c;
        });
      };

      /**
       * Function to call when page has been loaded.
       * Determines if running in a jupyter environment.
       */
      onLoad = function() {
        if (typeof Jupyter !== 'undefined') {
          jupyterRunning = true;
          initKernel(1);
        } else {
          drawSavedData();
        }
      };

      /**
       * Jupyter specific initialisation.
       * Retrying maximum `MAX_KERNEL_CONNECTION_ATTEMPTS` times
       * @param  {number} attempt number of attempt
       */
      initKernel = function(attempt) {
        let kernel = Jupyter.notebook.kernel;
        if (typeof kernel === 'undefined' || kernel == null) {
          if (attempt < MAX_KERNEL_CONNECTION_ATTEMPTS) {
            setTimeout(function() {
              initKernel(attempt + 1);
            }, KERNEL_CONNECT_WAIT_TIME);
          }
        } else {
          registerComm(kernel);
          Jupyter.notebook.events.on('kernel_ready.Kernel', function() {
            registerComm(kernel);
            for (let key in widgets) {
              widgets[key].connectCanvas();
            }
          });
          drawSavedData();
        }
      };

      /**
       * Handles a draw command.
       * @param  {[type]} msg The input message containing the draw command
       */
      draw = function(msg) {
        if (!grJstermReady) {
          onready.push(function() {
            return draw(msg);
          });
        } else if (!GR.is_ready) {
          GR.ready(function() {
            return draw(msg);
          });
        } else {
          if (typeof gr === 'undefined') {
            let canvas = document.createElement('canvas');
            canvas.id = 'jsterm-hidden-canvas';
            canvas.width = 640;
            canvas.height = 480;
            canvas.style = 'display: none;';
            document.body.appendChild(canvas);
            gr = new GR('jsterm-hidden-canvas');
            gr.registermeta(gr.GR_META_EVENT_SIZE, sizeCallback);
            gr.registermeta(gr.GR_META_EVENT_NEW_PLOT, newPlotCallback);
            gr.registermeta(gr.GR_META_EVENT_UPDATE_PLOT, updatePlotCallback);
          }
          let arguments = gr.newmeta();
          gr.readmeta(arguments, msg.content.data.json);
          gr.mergemeta(arguments);
        }
      };

      /**
       * Draw data that has been saved in the loaded page
       */
      drawSavedData = function() {
        let data = document.getElementsByClassName("jsterm-data");
        for (let i = 0; i < data.length; i++) {
          let msg = data[i].innerText;
          draw(JSON.parse(msg));
        }
      };

      if (document.readyState!='loading') {
        onLoad();
      } else if (document.addEventListener) {
        document.addEventListener('DOMContentLoaded', onLoad);
      } else document.attachEvent('onreadystatechange', function() {
        if (document.readyState=='complete') {
          onLoad();
        }
      });

      /**
       * Callback for gr-meta's size event. Handles event and resizes canvas if required.
       */
      sizeCallback = function(evt) {
        widgets[evt.plot_id].resize(evt.width, evt.height);
      };

      /**
       * Callback for gr-meta's new plot event. Handles event and creates new canvas.
       */
      newPlotCallback = function(evt) {
        if (typeof widgets[evt.plot_id] === 'undefined') {
          widgets[evt.plot_id] = new JSTermWidget(evt.plot_id);
        }
        widgets[evt.plot_id].draw();
      };

      /**
       * Callback for gr-meta's update plot event. Handles event and creates canvas id needed.
       */
      updatePlotCallback = function(evt) {
        if (typeof widgets[evt.plot_id] === 'undefined') {
          console.error('Updated plot does not exist, creating new object. (id', evt.plot_id, ')');
          widgets[evt.plot_id] = new JSTermWidget(evt.plot_id);
        }
        widgets[evt.plot_id].draw();
      };

      /**
       * Creates a JSTermWidget-Object describing and managing a canvas
       * @param       {number} id     The widget's numerical identificator (belonging context in `meta.c`)
       * @constructor
       */
      JSTermWidget = function(id) {

        /**
         * Initialize the JSTermWidget
         */
        this.init = function() {
          this.canvas = undefined;
          this.overlayCanvas = undefined;
          this.div = undefined;
          this.id = id;  // context id for meta.c (switchmeta)

          this.waiting = false;
          this.oncanvas = function() {};

          // event handling
          this.pinching = false;
          this.panning = false;
          this.prevMousePos = undefined;
          this.boxzoom = false;
          this.keepAspectRatio = true;
          this.boxzoomTriggerTimeout = undefined;
          this.boxzoomPoint = [undefined, undefined];
          this.pinchDiff = 0;
          this.prevTouches = undefined;

          this.sendEvents = false;
          this.handleEvents = true;

          this.width = 640;
          this.height = 480;
        };

        this.init();

        /**
         * Resizes the JSTermWidget
         * @param  {number} width  new canvas width in pixels
         * @param  {number} height new canvas height in pixels
         */
        this.resize = function(width, height) {
          this.width = width;
          this.height = height;
          if (this.canvas !== undefined) {
            this.canvas.width = width;
            this.canvas.height = height;
            this.overlayCanvas.width = width;
            this.overlayCanvas.height = height;
            this.div.style = "position: relative; width: " + width + "px; height: " + height + "px;";
          }
          this.draw();
        };

        /**
         * Send a event fired by widget via jupyter-comm
         * @param  {Object} data Event description
         */
        this.sendEvt = function(data) {
          if (this.sendEvents) {
            sendEvt(data, this.id);
          }
        };

        /**
         * Calculate coordinates on the canvas of the mouseevent.
         * @param  {Event} event    The mouse event to process
         * @return {[number, number]}       The calculated [x, y]-coordinates
         */
        this.getCoords = function(event) {
          let rect = this.canvas.getBoundingClientRect();
          //TODO mind the canvas-padding if necessary!
          return [Math.floor(event.clientX - rect.left), Math.floor(event.clientY - rect.top)];
        };

        /**
         * Send a event to `meta.c`
         * @param  {number} mouseargs (Emscripten) address of the argumentcontainer describing a event
         */
        this.grEventinput = function(mouseargs) {
          gr.switchmeta(this.id);
          gr.inputmeta(mouseargs);
          gr.current_canvas = this.canvas;
          gr.current_context = gr.current_canvas.getContext('2d');
          gr.select_canvas();
          gr.plotmeta();
        };

        /**
         * Handles a wheel event (zoom)
         * @param  {number} x       x-coordinate on the canvas of the mouse
         * @param  {number} y       y-coordinate on the canvas of the mouse
         * @param  {number} angle_delta angle the wheel has been turned
         */
        this.handleWheel = function(x, y, angle_delta) {
          if (typeof this.boxzoomTriggerTimeout !== 'undefined') {
            clearTimeout(this.boxzoomTriggerTimeout);
          }
          let mouseargs = gr.newmeta();
          gr.meta_args_push(mouseargs, "x", "i", [x]);
          gr.meta_args_push(mouseargs, "y", "i", [y]);
          gr.meta_args_push(mouseargs, "angle_delta", "d", [angle_delta]);
          this.grEventinput(mouseargs);
        };

        /**
         * Handles a wheel event triggered by the mouse
         * @param  {Event} event The fired mouse event
         */
        this.mouseHandleWheel = function (event) {
          let coords = this.getCoords(event);
          this.sendEvt({
            "x": coords[0],
            "y": coords[1],
            "angle_delta": event.deltaY,
            "event": "mousewheel",
          });
          if (this.handleEvents) {
            this.handleWheel(coords[0], coords[1], event.deltaY);
          }
          event.preventDefault();
        };

        /**
         * Handles a mousedown event
         * @param  {number} x       x-coordinate on the canvas of the mouse
         * @param  {number} y       y-coordinate on the canvas of the mouse
         * @param  {number} button  Integer indicating the button pressed (0: left, 1: middle/wheel, 2: right)
         * @param  {Boolean} ctrlKey Boolean indicating if the ctrl-key is pressed
         */
        this.handleMousedown = function(x, y, button, ctrlKey) {
          if (typeof this.boxzoomTriggerTimeout !== 'undefined') {
            clearTimeout(this.boxzoomTriggerTimeout);
          }
          if (button == 0) {
            this.overlayCanvas.style.cursor = 'move';
            this.panning = true;
            this.boxzoom = false;
            this.prevMousePos = [x, y];
            this.boxzoomTriggerTimeout = setTimeout(function() {this.startBoxzoom(x, y, ctrlKey);}.bind(this), BOXZOOM_TRIGGER_THRESHHOLD);
          } else if (button == 2) {
            this.startBoxzoom(x, y, ctrlKey);
          }
        };

        /**
         * Handles a mousedown event triggered by the mouse
         * @param  {Event} event The fired mouse event
         */
        this.mouseHandleMousedown = function (event) {
          let coords = this.getCoords(event);
          this.sendEvt({
            "x": coords[0],
            "y": coords[1],
            "button": event.button,
            "ctrlKey": event.ctrlKey,
            "event": "mousedown",
          });
          if (this.handleEvents) {
            this.handleMousedown(coords[0], coords[1], event.button, event.ctrlKey);
          }
          event.preventDefault();
        };

        /**
         * Initiate the boxzoom on the canvas.
         * @param  {number} x       x-coordinate of the mouse
         * @param  {number} y       y-coordinate of the mouse
         * @param  {Boolean} ctrlKey Boolean indicating if the ctrl-key is pressed
         */
        this.startBoxzoom = function(x, y, ctrlKey) {
          this.panning = false;
          this.boxzoom = true;
          if (ctrlKey) {
            this.keepAspectRatio = false;
          }
          this.boxzoomPoint = [x, y];
          this.overlayCanvas.style.cursor = 'nwse-resize';
        };

        /**
         * Handles a mouseup event
         * @param  {number} x       x-coordinate on the canvas of the mouse
         * @param  {number} y       y-coordinate on the canvas of the mouse
         * @param  {number} button  Integer indicating the button pressed (0: left, 1: middle/wheel, 2: right)
         */
        this.handleMouseup = function(x, y, button) {
          if (typeof this.boxzoomTriggerTimeout !== 'undefined') {
            clearTimeout(this.boxzoomTriggerTimeout);
          }
          if (this.boxzoom) {
            if ((Math.abs(this.boxzoomPoint[0] - x) >= BOXZOOM_THRESHOLD) && (Math.abs(this.boxzoomPoint[1] - y) >= BOXZOOM_THRESHOLD)) {
              let mouseargs = gr.newmeta();
              let diff = [x - this.boxzoomPoint[0], y - this.boxzoomPoint[1]];
              gr.meta_args_push(mouseargs, "x1", "i", [this.boxzoomPoint[0]]);
              gr.meta_args_push(mouseargs, "x2", "i", [this.boxzoomPoint[0] + diff[0]]);
              gr.meta_args_push(mouseargs, "y1", "i", [this.boxzoomPoint[1]]);
              gr.meta_args_push(mouseargs, "y2", "i", [this.boxzoomPoint[1] + diff[1]]);
              if (this.keepAspectRatio) {
                gr.meta_args_push(mouseargs, "keep_aspect_ratio", "i", [1]);
              } else {
                gr.meta_args_push(mouseargs, "keep_aspect_ratio", "i", [0]);
              }
              this.grEventinput(mouseargs);
            }
          }
          this.prevMousePos = undefined;
          this.overlayCanvas.style.cursor = 'auto';
          this.panning = false;
          this.boxzoom = false;
          this.boxzoomPoint = [undefined, undefined];
          this.keepAspectRatio = true;
          let context = this.overlayCanvas.getContext('2d');
          context.clearRect(0, 0, this.overlayCanvas.width, this.overlayCanvas.height);
        };

        /**
         * Handles a mouseup event triggered by the mouse
         * @param  {Event} event The fired mouse event
         */
        this.mouseHandleMouseup = function(event) {
          let coords = this.getCoords(event);
          this.sendEvt({
            "x": coords[0],
            "y": coords[1],
            "button": event.button,
            "event": "mouseup",
          });
          if (this.handleEvents) {
            this.handleMouseup(coords[0], coords[1], event.button);
          }
          event.preventDefault();
        };

        /**
         * Handles a touchstart event triggered by tapping the touchscreen
         * @param  {Event} event The fired touch event
         */
        this.touchHandleTouchstart = function(event) {
          if (event.touches.length == 1) {
            let coords = this.getCoords(event.touches[0]);
            this.handleMousedown(coords[0], coords[1], 0, false);
          } else if (event.touches.length == 2) {
            this.pinching = true;
            this.pinchDiff = Math.abs(event.touches[0].clientX - event.touches[1].clientX) + Math.abs(event.touches[0].clientY - event.touches[1].clientY);
            let c1 = this.getCoords(event.touches[0]);
            let c2 = this.getCoords(event.touches[1]);
            this.prevTouches = [c1, c2];
          } else if (event.touches.length == 3) {
            let coords1 = this.getCoords(event.touches[0]);
            let coords2 = this.getCoords(event.touches[1]);
            let coords3 = this.getCoords(event.touches[2]);
            let x = 1 / 3 * coords1[0] + coords2[0] + coords3[0];
            let y = 1 / 3 * coords1[1] + coords2[1] + coords3[1];
            this.handleDoubleclick(x, y);
          }
          event.preventDefault();
        };

        /**
         * Handles a touchend event
         * @param  {Event} event The fired touch event
         */
        this.touchHandleTouchend = function(event) {
          this.handleMouseleave();
        };

        /**
         * Handles a touchmove event triggered by moving fingers on the touchscreen
         * @param  {Event} event The fired touch event
         */
        this.touchHandleTouchmove = function(event) {
          if (event.touches.length == 1) {
            let coords = this.getCoords(event.touches[0]);
            this.handleMousemove(coords[0], coords[1]);
          } else if (this.pinching && event.touches.length == 2) {
            let c1 = this.getCoords(event.touches[0]);
            let c2 = this.getCoords(event.touches[1]);
            let diff = Math.sqrt(Math.pow(Math.abs(c1[0] - c2[0]), 2) + Math.pow(Math.abs(c1[1] - c2[1]), 2));
            if (typeof this.pinchDiff !== 'undefined' && typeof this.prevTouches !== 'undefined') {
              let factor = this.pinchDiff / diff;

              let mouseargs = gr.newmeta();
              gr.meta_args_push(mouseargs, "x", "i", [(c1[0] + c2[0]) / 2]);
              gr.meta_args_push(mouseargs, "y", "i", [(c1[1] + c2[1]) / 2]);
              gr.meta_args_push(mouseargs, "factor", "d", [factor]);
              this.grEventinput(mouseargs);

              let panmouseargs = gr.newmeta();
              gr.meta_args_push(panmouseargs, "x", "i", [(c1[0] + c2[0]) / 2]);
              gr.meta_args_push(panmouseargs, "y", "i", [(c1[1] + c2[1]) / 2]);
              gr.meta_args_push(panmouseargs, "xshift", "i", [(c1[0] - this.prevTouches[0][0] + c2[0] - this.prevTouches[1][0]) / 2.0]);
              gr.meta_args_push(panmouseargs, "yshift", "i", [(c1[1] - this.prevTouches[0][1] + c2[1] - this.prevTouches[1][1]) / 2.0]);
              this.grEventinput(panmouseargs);
            }
            this.pinchDiff = diff;
            this.prevTouches = [c1, c2];
          }
          event.preventDefault();
        };

        /**
         * Handles a mouseleave event
         */
        this.handleMouseleave = function() {
          if (typeof this.boxzoomTriggerTimeout !== 'undefined') {
            clearTimeout(this.boxzoomTriggerTimeout);
          }
          this.overlayCanvas.style.cursor = 'auto';
          this.panning = false;
          this.prevMousePos = undefined;
          if (this.boxzoom) {
            let context = this.overlayCanvas.getContext('2d');
            context.clearRect(0, 0, this.overlayCanvas.width, this.overlayCanvas.height);
          }
          this.boxzoom = false;
          this.boxzoomPoint = [undefined, undefined];
          this.keepAspectRatio = true;
        };

        /**
         * Handles a mouseleave event triggered by the mouse
         * @param  {Event} event The fired mouse event
         */
        this.mouseHandleMouseleave = function(event) {
          this.pinchDiff = undefined;
          this.prevTouches = undefined;
          this.sendEvt({
            "event": "mouseleave",
          });
          if (this.handleEvents) {
            this.handleMouseleave();
          }
        };

        /**
         * Handles a mousemove event
         * @param  {number} x       x-coordinate on the canvas of the mouse
         * @param  {number} y       y-coordinate on the canvas of the mouse
         */
        this.handleMousemove = function(x, y) {
          if (this.panning) {
            if (typeof this.boxzoomTriggerTimeout !== 'undefined') {
              clearTimeout(this.boxzoomTriggerTimeout);
            }
            let mouseargs = gr.newmeta();
            gr.meta_args_push(mouseargs, "x", "i", [this.prevMousePos[0]]);
            gr.meta_args_push(mouseargs, "y", "i", [this.prevMousePos[1]]);
            gr.meta_args_push(mouseargs, "xshift", "i", [x - this.prevMousePos[0]]);
            gr.meta_args_push(mouseargs, "yshift", "i", [y - this.prevMousePos[1]]);
            this.grEventinput(mouseargs);
            this.prevMousePos = [x, y];
          } else if (this.boxzoom) {
            let context = this.overlayCanvas.getContext('2d');
            let diff = [x - this.boxzoomPoint[0], y - this.boxzoomPoint[1]];
            gr.switchmeta(this.id);
            let box = gr.meta_get_box(this.boxzoomPoint[0], this.boxzoomPoint[1], this.boxzoomPoint[0] + diff[0], this.boxzoomPoint[1] + diff[1], this.keepAspectRatio);
            context.clearRect(0, 0, this.overlayCanvas.width, this.overlayCanvas.height);
            if (diff[0] * diff[1] >= 0) {
              this.overlayCanvas.style.cursor = 'nwse-resize';
            } else {
              this.overlayCanvas.style.cursor = 'nesw-resize';
            }
            context.fillStyle = BOXZOOM_FILL_STYLE;
            context.strokeStyle = BOXZOOM_STROKE_STYLE;
            context.beginPath();
            context.rect(box[0], box[1], box[2], box[3]);
            context.globalAlpha = 0.2;
            context.fill();
            context.globalAlpha = 1.0;
            context.stroke();
            context.closePath();
          }
        };

        /**
         * Handles a mousemove event triggered by the mouse
         * @param  {Event} event The fired mouse event
         */
        this.mouseHandleMousemove = function (event) {
          let coords = this.getCoords(event);
          this.sendEvt({
            "x": coords[0],
            "y": coords[1],
            "event": "mousemove",
          });
          if (this.handleEvents) {
            this.handleMousemove(coords[0], coords[1]);
          }
          event.preventDefault();
        };

        /**
         * Handles a doubleclick event
         * @param  {number} x       x-coordinate on the canvas of the mouse
         * @param  {number} y       y-coordinate on the canvas of the mouse
         */
        this.handleDoubleclick = function(x, y) {
          let mouseargs = gr.newmeta();
          gr.meta_args_push(mouseargs, "x", "i", [x]);
          gr.meta_args_push(mouseargs, "y", "i", [y]);
          gr.meta_args_push(mouseargs, "key", "s", "r");
          this.grEventinput(mouseargs);
          this.boxzoomPoint = [undefined, undefined];
        };

        /**
         * Handles a doubleclick event triggered by the mouse
         * @param  {Event} event The fired mouse event
         */
        this.mouseHandleDoubleclick = function(event) {
          let coords = this.getCoords(event);
          this.sendEvt({
            "x": coords[0],
            "y": coords[1],
            "event": "doubleclick",
          });
          if (this.handleEvents) {
            this.handleDoubleclick(coords[0], coords[1]);
          }
          event.preventDefault();
        };

        /**
         * Handles a event triggered by a Jupyter Comm message
         * @param  {Object} msg The message describing the event
         */
        this.msgHandleEvent = function(msg) {
          switch(msg.event) {
            case "mousewheel":
              this.handleWheel(msg.x, msg.y, msg.angle_delta);
              break;
            case "mousedown":
              this.handleMousedown(msg.x, msg.y, msg.button, msg.ctrlKey);
              break;
            case "mouseup":
              this.handleMouseup(msg.x, msg.y, msg.button);
              break;
            case "mousemove":
              this.handleMousemove(msg.x, msg.y);
              break;
            case "doubleclick":
              this.handleDoubleclick(msg.x, msg.y);
              break;
            case "mouseleave":
              this.handleMouseleave();
              break;
            default:
              break;
          }
        };

        /**
         * Handles a command received cia jupyter comm
         * @param  {Object} msg Received msg containing the command
         */
        this.msgHandleCommand = function(msg) {
          switch(msg.command) {
            case 'enable_events':
              this.sendEvents = true;
              break;
            case 'disable_events':
              this.sendEvents = false;
              break;
            case 'enable_jseventhandling':
              this.handleEvents = true;
              break;
            case 'disable_jseventhandling':
              this.handleEvents = false;
              break;
            default:
              break;
          }
        };

        /**
         * Draw a plot described by a message received via jupyter comm
         * @param  {Object} msg message containing the draw-command
         */
        this.draw = function() {
          if (this.waiting) {
            this.oncanvas = function() {
              return this.draw();
            };
          } else {
            console.log(document.getElementById('jsterm-' + this.id));
            if (document.getElementById('jsterm-' + this.id) == null) {
              createCanvas(this.id, this.width, this.height);
              this.canvas = undefined;
              this.waiting = true;
              this.oncanvas = function() {
                return this.draw();
              };
              setTimeout(function() {
                this.refreshPlot(this.id, 0);
              }.bind(this), RECONNECT_PLOT_TIMEOUT);
            } else {
              //if (document.getElementById('jsterm-data-' + this.id) == null) {
                //saveData(msg, msg.content.data.id);
              //}
              //Jupyter.notebook.get_selected_cell().metadata.jsterm = msg;
              if (document.getElementById('jsterm-' + this.id) !== this.canvas || typeof this.canvas === 'undefined' || typeof this.overlayCanvas === 'undefined') {
                this.connectCanvas();
              }

              gr.switchmeta(this.id);
              gr.current_canvas = this.canvas; //TODO is this always set? (check)
              gr.current_context = gr.current_canvas.getContext('2d');
              gr.select_canvas();
              gr.plotmeta();
            }
          }
        };

        /**
         * Connects a canvas to a JSTermWidget object.
         */
        this.connectCanvas = function() {
          if (document.getElementById('jsterm-' + this.id) != null) {
            this.div = document.getElementById('jsterm-div-' + this.id);
            this.canvas = document.getElementById('jsterm-' + this.id);
            this.overlayCanvas = document.getElementById('jsterm-overlay-' + this.id);
            this.overlayCanvas.addEventListener('DOMNodeRemoved', function() {
              createCanvas(this.id, this.width, this.height);
              this.canvas = undefined;
              this.waiting = true;
              this.oncanvas = function() {};
            });
            this.overlayCanvas.style.cursor = 'auto';

            //registering event handler
            this.overlayCanvas.addEventListener('wheel', function(evt) { this.mouseHandleWheel(evt); }.bind(this));
            this.overlayCanvas.addEventListener('mousedown', function(evt) { this.mouseHandleMousedown(evt); }.bind(this));
            this.overlayCanvas.addEventListener('touchstart', function(evt) { this.touchHandleTouchstart(evt); }.bind(this));
            this.overlayCanvas.addEventListener('touchmove', function(evt) { this.touchHandleTouchmove(evt); }.bind(this));
            this.overlayCanvas.addEventListener('touchend', function(evt) { this.touchHandleTouchend(evt); }.bind(this));
            this.overlayCanvas.addEventListener('mousemove', function(evt) { this.mouseHandleMousemove(evt); }.bind(this));
            this.overlayCanvas.addEventListener('mouseup', function(evt) { this.mouseHandleMouseup(evt); }.bind(this));
            this.overlayCanvas.addEventListener('mouseleave', function(evt) { this.mouseHandleMouseleave(evt); }.bind(this));
            this.overlayCanvas.addEventListener('dblclick', function(evt) { this.mouseHandleDoubleclick(evt); }.bind(this));
            this.overlayCanvas.addEventListener('contextmenu', function(event) {
              event.preventDefault();
              return false;
            });
          }
        };

        /**
         * Check if a deleted canvas has been recreated.
         * Calls itself after REFRESH_PLOT_TIMEOUTms if no canvas is found
         * @param  {number} count [description]
         */
        this.refreshPlot = function(count) {
          if (document.getElementById('jsterm-' + this.id) == null) {
            if (count < RECONNECT_PLOT_MAX_ATTEMPTS) {
              setTimeout(function() {
                this.refreshPlot( count + 1);
              }.bind(this), RECONNECT_PLOT_TIMEOUT);
            }
          } else {
            this.waiting = false;
            if (typeof this.oncanvas !== 'undefined') {
              this.oncanvas();
            }
          }
        };
      };
    }
    var grJSTermRunning = true;"""
  if _gr_js === nothing
      _gr_js = string("""
        JSTerm.saveLoad('""", _js_fallback, """', jsLoaded, 10000);
        var grJstermReady = false;
      """)
  else
    _gr_js = string(_gr_js, "var grJstermReady = true;")
  end
  display(HTML(string("""
    <script type="text/javascript">
      """, _gr_js, """
      """, _jsterm, """
    </script>
  """)))
end

function JSTermWidget(id::Int, width::Int, height::Int)
  global id_count, js_running
  if GR.isijulia()
    id_count += 1
    if !js_running
      inject_js()
      js_running = true
    end
    JSTermWidget(id, width, height, false)
  else
    error("JSTermWidget is only available in IJulia environments")
  end
end

function jsterm_display(widget::JSTermWidget)
  if GR.isijulia()
    display(HTML(string("<div id=\"jsterm-div-", widget.identifier, "\" style=\"position: relative; width: ", widget.width, "px; height: ", widget.height, "px;\"><canvas id=\"jsterm-overlay-", widget.identifier, "\" style=\"position:absolute; top: 0; right: 0; z-index: 1;\" width=\"", widget.width, "\" height=\"", widget.height, "\"></canvas>
        <canvas id=\"jsterm-", widget.identifier, "\" style=\"position: absolute; top: 0; right: 0; z-index: 0;\"width=\"", widget.width, "\" height=\"", widget.height, "\"></canvas>")))
    widget.visible = true
  else
    error("jsterm_display is only available in IJulia environments")
  end
end

comm = nothing

evthandler = Dict()
global_evthandler = nothing

function register_evthandler(f::Function, device, port)
  global evthandler
  if GR.isijulia()
    send_command(Dict("command" => "enable_events"), "cmd", string(device, port))
    evthandler[string(device, port)] = f
  else
    error("register_evthandler is only available in IJulia environments")
  end
end

function unregister_evthandler(device, port)
  global evthandler
  if GR.isijulia()
    if global_evthandler === nothing
      send_command(Dict("command" => "disable_events"), "cmd", string(device, port))
    end
    evthandler[string(device, port)] = nothing
  else
    error("unregister_evthandler is only available in IJulia environments")
  end
end

function register_evthandler(f::Function)
  global global_evthandler
  if GR.isijulia()
    send_command(Dict("command" => "enable_events"), "cmd", nothing)
    global_evthandler = f
  else
    error("register_evthandler is only available in IJulia environments")
  end
end

function unregister_evthandler()
  global global_evthandler, evthandler
  if GR.isijulia()
    send_command(Dict("command" => "disable_events"), "cmd", nothing)
    for key in keys(evthandler)
      if evthandler[key] !== nothing
        send_command(Dict("command" => "enable_events"), "cmd", key)
      end
    end
    global_evthandler = nothing
  else
    error("unregister_evthandler is only available in IJulia environments")
  end
end

function send_command(msg, msgtype, id=nothing)
  global comm
  if GR.isijulia()
    if comm === nothing
      error("JSTerm comm not initialized.")
    else
      if id !== nothing
        Main.IJulia.send_comm(comm, merge(msg, Dict("type" => msgtype, "id" => id)))
      else
        Main.IJulia.send_comm(comm, merge(msg, Dict("type" => msgtype)))
      end
    end
  else
    error("send_command is only available in IJulia environments")
  end
end

function send_evt(msg, device, port)
  if GR.isijulia()
    send_command(msg, "evt", string(device, port))
  else
    error("send_evt is only available in IJulia environments")
  end
end

function send_evt(msg, identifier)
  if GR.isijulia()
    send_command(msg, "evt", identifier)
  else
    error("send_evt is only available in IJulia environments")
  end
end

function disable_jseventhandling(device, port)
  if GR.isijulia()
    send_command(Dict("command" => "disable_jseventhandling"), "cmd", string(device, port))
  else
    error("disable_jseventhandling is only available in IJulia environments")
  end
end

function enable_jseventhandling(device, port)
  if GR.isijulia()
    send_command(Dict("command" => "enable_jseventhandling"), "cmd", string(device, port))
  else
    error("enable_jseventhandling is only available in IJulia environments")
  end
end

function disable_jseventhandling()
  if GR.isijulia()
    send_command(Dict("command" => "disable_jseventhandling"), "cmd", nothing)
  else
    error("disable_jseventhandling is only available in IJulia environments")
  end
end

function enable_jseventhandling()
  if GR.isijulia()
    send_command(Dict("command" => "enable_jseventhandling"), "cmd", nothing)
  else
    Main.IJulia.send_comm(comm, Dict("json" => f, "type" => "evt"))
    error("enable_jseventhandling is only available in IJulia environments")
  end
end

function jsterm_send(data::String)
  global js_running, draw_condition, comm, PXWIDTH, PXHEIGHT
  if GR.isijulia()
    if comm === nothing
      comm = Main.IJulia.Comm("jsterm_comm")
      comm.on_close = function comm_close_callback(msg)
        global js_running
        js_running = false
      end
      comm.on_msg = function comm_msg_callback(msg)
        data = msg.content["data"]
        if haskey(data, "type")
          if data["type"] == "createCanvas"
            id = data["id"]
            if haskey(jswidgets, id)
                widget = jswidgets[id]
                widget.width = data["width"]
                widget.height = data["height"]
            else
                widget = JSTermWidget(id, data["width"], data["height"])
                jswidgets[id] = widget
            end
            jswidgets[id].visible = false
            jsterm_display(jswidgets[id])
          elseif data["type"] == "save"
            display(HTML(string("<div style=\"display:none;\" id=\"jsterm-data-", data["content"]["id"], "\" class=\"jsterm-data\">", data["content"]["data"], "</div>")))
          elseif data["type"] == "evt"
            global_evthandler(data["content"])
            if haskey(evthandler, data["id"]) && evthandler[data["id"]] !== nothing
              evthandler[data["id"]](data["content"])
            end
          end
        end
      end
    end
    Main.IJulia.send_comm(comm, Dict("json" => data, "type"=>"draw"))
  else
    error("jsterm_send is only available in IJulia environments")
  end
end

function recv(name::Cstring, id::Int32, msg::Cstring)
    # receives string from C and sends it to JS via Comm
    global js_running
    if !js_running
      inject_js()
      js_running = true
    end
    jsterm_send(unsafe_string(msg))
    return convert(Int32, 1)
end

function send(name::Cstring, id::Int32)
    # Dummy function, not in use
    return convert(Cstring, "String")
end

jswidgets = nothing
send_c = nothing
recv_c = nothing

function initjs()
    global jswidgets, send_c, recv_c

    jswidgets = Dict{Int32, JSTermWidget}()
    send_c = @cfunction(send, Cstring, (Cstring, Int32))
    recv_c = @cfunction(recv, Int32, (Cstring, Int32, Cstring))
    send_c, recv_c
end

end # module


text:
######################################################################
# gaussian_mpc.jl
######################################################################

type GaussianMPC <: Policy
	n::Int   # what it meant before
	N::Int   # planning horizon
	actions::Vector{Action}

	GaussianMPC(x::Vehicle) = GaussianMPC(x, 8)
	GaussianMPC(x::Vehicle, n::Int) = GaussianMPC(x, 8, 4)

	# create list of actions
	function GaussianMPC(x::Vehicle, n::Int, N::Int)
		angles = linspace(0.0, 360 - 360/n, n)
		actions = Array(Action, n)
		for i = 1:n
			ax = x.max_step * sind(angles[i])
			ay = x.max_step * cosd(angles[i])
			actions[i] = (ax, ay, 0.0)
		end

		return new(n, N, actions)
	end

end

function action(m::SearchDomain, x::Vehicle, o::Float64, f::AbstractFilter, p::GaussianMPC)

	# create Gaussian approximation of belief
	mu = centroid(f)
	Sigma = covariance(f)

	# convert Sigma to Omega and calculate Rinv
	Omega = inv(Sigma)
	#try
	#	Omega = inv(Sigma)
	#catch
	#	println("Sigma = ", Sigma)
	#	println("sum = ", sum(f.b))
	#	println("max = ", maximum(f.b))
	#	#println("num = ", maximum(f.b))
	#	error()
	#	return p.actions[1]
	#end
	Rinv = inv(x.sensor.noise_sigma)
	xp = (x.x, x.y, x.heading)

	# do a tree search here..
	max_det = 0.0
	best_action = p.actions[1]
	for a in p.actions
		temp_det = rec_det(m, xp, a, p.N-1, Omega, Rinv, mu, p)
		if temp_det > max_det
			max_det = temp_det
			best_action = a
		end
	end

	return best_action
end

# Recursive determinant function
# xp = pose of vehicle
# a  = action
# N  = planning horizon
# Omega = information matrix
function rec_det(m::SearchDomain, xp::Pose, a::Action, N::Int, Omega::Matrix{Float64}, Rinv::Float64, mu::LocTuple, p::GaussianMPC)
	np = new_pose(m, xp, a)
	Ct = Jacobian(mu, np)
	if N == 0
		return det( Omega + Rinv*(Ct'*Ct) )
	end

	max_det = 0.0
	for a in p.actions
		max_det = max( max_det, rec_det(m, np, a, N-1, Omega+Rinv*(Ct'*Ct), Rinv, mu, p) )
	end

	return max_det
end

function Jacobian(mu::LocTuple, np::Pose)
	xr = mu[1] - np[1]
	yr = mu[2] - np[2]
	if xr == 0.0 && yr === 0.0
		xr = 1e-6
		yr = 1e-6
	end
	Ct = [yr, -xr]' * (180.0 / pi) / (xr^2 + yr^2)
	return Ct
end

ext: jl


text:
# code by dpo
function spy(A::AbstractArray;
             width::Int = 0,
             height::Int = 0,
             color = :automatic,
             labels::Bool = true,
             margin::Int = 3,
             padding::Int = 1,
             maxwidth::Int = 0,
             maxheight::Int = 0,
             title::(@compat AbstractString) = "Sparsity Pattern",
             args...)
  rows, cols, vals = findnz(A)
  nrow, ncol = size(A)
  min_canvheight = safeCeil(nrow / 4)
  min_canvwidth = safeCeil(ncol / 2)
  aspect_ratio = min_canvwidth / min_canvheight
  height_diff = 9
  width_diff = margin + padding + length(string(ncol)) + 6
  min_plotheight = min_canvheight + height_diff
  min_plotwidth = min_canvwidth + width_diff

  # if no size bounds ares specified and the session is in an
  # interactive terminal then use the size of the REPL
  if isinteractive()
    term_height, term_width = Base.tty_size()
    maxheight = maxheight > 0 ? maxheight : term_height - height_diff
    maxwidth = maxwidth > 0 ? maxwidth : term_width - width_diff
  else
    maxheight = maxheight > 0 ? maxheight : 40
    maxwidth = maxwidth > 0 ? maxwidth : 70
  end

  # Check if the size of the plot should be derived from the matrix
  # Note: if both width and height are 0, it means that there are no
  #       constraints and the plot should resemble the structure of
  #       the matrix as close as possible
  if width == 0 && height == 0
    # If the interactive code did not take care of this then try
    # to plot the matrix in the correct aspect ratio (within specified bounds)
    if min_canvheight > min_canvwidth
      # long matrix (according to pixel density)
      height = min_canvheight
      width = height * aspect_ratio
      if width > maxwidth
        width = maxwidth
        height = width / aspect_ratio
      end
      if height > maxheight
        height = maxheight
        width = min(height * aspect_ratio, maxwidth)
      end
    else
      # wide matrix
      width = min_canvwidth
      height = width / aspect_ratio
      if height > maxheight
        height = maxheight
        width = height * aspect_ratio
      end
      if width > maxwidth
        width = maxwidth
        height = min(width / aspect_ratio, maxheight)
      end
    end
  end
  if width == 0 && height > 0
    width = min(height * aspect_ratio, maxwidth)
  elseif width > 0 && height == 0
    height = min(width / aspect_ratio, maxheight)
  elseif width == 0 && height == 0
    width = 40
    height = 20
  end
  width = int(width)
  height = int(height)
  canvas = BrailleCanvas(width, height,
                         plotWidth = float(ncol) + 1,
                         plotHeight = float(nrow) + 1)
  plot = Plot(canvas; showLabels = labels, title = title, margin = margin, padding = padding, args...)
  height = nrows(plot.graphics)
  width = ncols(plot.graphics)
  plot = if color != :automatic
    setPoint!(plot,
              convert(Vector{(@compat AbstractFloat)}, cols),
              nrow + 1 - convert(Vector{(@compat AbstractFloat)}, rows),
              color)
  else
    pos_idx = vals .> 0
    neg_idx = !pos_idx
    pos_cols = cols[pos_idx]
    pos_rows = rows[pos_idx]
    neg_cols = cols[neg_idx]
    neg_rows = rows[neg_idx]
    setPoint!(plot,
              convert(Vector{(@compat AbstractFloat)}, pos_cols),
              nrow + 1 - convert(Vector{(@compat AbstractFloat)}, pos_rows),
              :red)
    setPoint!(plot,
              convert(Vector{(@compat AbstractFloat)}, neg_cols),
              nrow + 1 - convert(Vector{(@compat AbstractFloat)}, neg_rows),
              :blue)
    annotate!(plot, :r, 1, "> 0", :red)
    annotate!(plot, :r, 2, "< 0", :blue)
  end
  annotate!(plot, :l, 1, "1")
  annotate!(plot, :l, height, string(nrow))
  annotate!(plot, :bl, "1")
  annotate!(plot, :br, string(ncol))
  xlabel!(plot, string("nz = ", length(vals)))
  return plot
end


text:
julia> dzdy = TensorDeriv(:(dx4[]), :(dx3[j]), :(w3[i,j]), [])
dx4[]/dx3[j] = w3[i,j]

julia> dydx = TensorDeriv(:(dx3[j]), :(dw2[m,n]), :(x2[n]), [:(j == m)])
dx3[j]/dw2[m,n] = x2[n]  * (j == m)

julia> dzdy
dx4[]/dx3[j] = w3[i,j]

julia> dydx
dx3[j]/dw2[m,n] = x2[n]  * (j == m)

julia> dzdy.ex ⊗ dydx.ex
:(w3[i,j] .* x2[n])

julia> dzdy ⊗ dydx
dx4[]/dw2[m,n] = (w3[i,m] * I[i]) .* x2[n]

julia> fullex = iex
:(tmp23[i] = w3[i,k] * x3[k])

julia>

julia>

julia> idx = 2
2

julia>

julia>

julia>     maybe_rule = tfind_rule(fullex, idx)
Nullable{TensorDiffRule}(TensorDiffRule(Z[i] = V[i,k] * W[k] ==> dZ[i]/dW[j] = V[i,k]  * (k == j)))

julia> get(maybe_rule)
TensorDiffRule(Z[i] = V[i,k] * W[k] ==> dZ[i]/dW[j] = V[i,k]  * (k == j))

julia> get(maybe_rule)


text:
#!/usr/bin/env julia

using Luxor

# coordinates are in RA seconds float and Declination seconds float

constellation_names = Dict(
"AND"=> ("Andromeda","Daughter of Cassiopeia"),
"ANT"=> ("Antlia","The Air Pump"),
"APS"=> ("Apus","Bird of Paradise"),
"AQR"=> ("Aquarius","The Water-Bearer"),
"AQL"=> ("Aquila","The Eagle"),
"ARA"=> ("Ara","The Altar"),
"ARI"=> ("Aries","The Ram"),
"AUR"=> ("Auriga","The Charioteer"),
"BOO"=> ("Bootes","The Herdsman"),
"CAE"=> ("Caelum","The Chisel"),
"CAM"=> ("Camelopardalis","The Giraffe"),
"CNC"=> ("Cancer","The Crab"),
"CVN"=> ("Canes Venatici","The Hunting Dogs"),
"CMA"=> ("Canis Major","The Big Dog"),
"CMI"=> ("Canis Minor","The Little Dog"),
"CAP"=> ("Capricornus","The Goat"),
"CAR"=> ("Carina", "The Keel (of Argo)"),
"CAS"=> ("Cassiopeia","The Queen"),
"CEN"=> ("Centaurus","The Centaur"),
"CEP"=> ("Cepheus","The King"),
"CET"=> ("Cetus","The Whale"),
"CHA"=> ("Chamaeleon","The Chameleon"),
"CIR"=> ("Circinus","The Compasses"),
"COL"=> ("Columba","The Dove"),
"COM"=> ("Coma Berenices","Berenice's Hair"),
"CRA"=> ("Corona Australis","The Southern Crown"),
"CRB"=> ("Corona Borealis","The Northern Crown"),
"CRV"=> ("Corvus", "The Crow"),
"CRT"=> ("Crater","The Cup"),
"CRU"=> ("Crux","The Cross"),
"CYG"=> ("Cygnus","The Swan"),
"DEL"=> ("Delphinus","The Dolphin"),
"DOR"=> ("Dorado","The Swordfish"),
"DRA"=> ("Draco","The Dragon"),
"EQU"=> ("Equuleus","The Little Horse"),
"ERI"=> ("Eridanus","The River"),
"FOR"=> ("Fornax","The Furnace"),
"GEM"=> ("Gemini","The Twins"),
"GRU"=> ("Grus","The Crane (bird)"),
"HER"=> ("Hercules","The Son of Zeus"),
"HOR"=> ("Horologium","The Clock"),
"HYA"=> ("Hydra","The Water Snake (female)"),
"HYI"=> ("Hydrus","The Water Snake (male)"),
"IND"=> ("Indus","The Indian (American)"),
"LAC"=> ("Lacerta","The Lizard"),
"LEO"=> ("Leo","The Lion"),
"LMI"=> ("Leo Minor","The Little Lion"),
"LEP"=> ("Lepus","The Hare"),
"LIB"=> ("Libra","The Balance"),
"LUP"=> ("Lupus","The Wolf"),
"LYN"=> ("Lynx","The Lynx"),
"LYR"=> ("Lyra","The Lyre"),
"MEN"=> ("Mensa","The Table"),
"MIC"=> ("Microscopium","The Microscope"),
"MON"=> ("Monoceros","The Unicorn"),
"MUS"=> ("Musca","The Fly"),
"NOR"=> ("Norma","The Square"),
"OCT"=> ("Octans","The Octant"),
"OPH"=> ("Ophiuchus","The Serpent-Bearer"),
"ORI"=> ("Orion","The Hunter"),
"PAV"=> ("Pavo","The Peacock"),
"PEG"=> ("Pegasus","The Winged Horse"),
"PER"=> ("Perseus","Rescuer of Andromeda"),
"PHE"=> ("Phoenix","The Phoenix"),
"PIC"=> ("Pictor","The Painter"),
"PSC"=> ("Pisces","The Fishes"),
"PSA"=> ("Piscis Austrinus", "The Southern Fish"),
"PUP"=> ("Puppis","The Stern (of Argo)"),
"PYX"=> ("Pyxis","The Compass"),
"RET"=> ("Reticulum","The Reticle"),
"SGE"=> ("Sagitta","The Arrow"),
"SGR"=> ("Sagittarius","The Archer"),
"SCO"=> ("Scorpius","The Scorpion"),
"SCL"=> ("Sculptor","The Sculptor"),
"SCT"=> ("Scutum","The Shield"),
"SER"=> ("Serpens","The Serpent"),
"SER1"=> ("Serpens Caput","The Serpent's Head"),
"SER2"=> ("Serpens Cauda","The Serpent's Tail"),
"SEX"=> ("Sextans","The Sextant"),
"TAU"=> ("Taurus","The Bull"),
"TEL"=> ("Telescopium","The Telescope"),
"TRI"=> ("Triangulum","The Triangle"),
"TRA"=> ("Triangulum Australe",   "The Southern Triangle"),
"TUC"=> ("Tucana","The Toucan"),
"UMA"=> ("Ursa Major","The Great Bear"),
"UMI"=> ("Ursa Minor","The Little Bear"),
"VEL"=> ("Vela","The Sails (of Argo)"),
"VIR"=> ("Virgo","The Maiden"),
"VOL"=> ("Volans","The Flying Fish"),
"VUL"=> ("Vulpecula","The Fox")
)

# these are the polygon boundaries of the constellations.
# Some constellations have two polygons, either side of 0h RA.

constellation_boundaries = Dict{Any,Any}(
"NOR"=>Array[Point[
    Point(55200.0,-216000.0),
    Point(59115.0,-216000.0),
    Point(59115.0,-160200.0),
    Point(59115.0,-151200.0),
    Point(57600.0,-151200.0),
    Point(56400.0,-151200.0),
    Point(56400.0,-172800.0),
    Point(55200.0,-172800.0),
    Point(55200.0,-194400.0),
    Point(54180.0,-194400.0),
    Point(54180.0,-198000.0),
    Point(55200.0,-198000.0)]],
"CIR"=>Array[Point[
    Point(48600.0,-230400.0),
    Point(48600.0,-234000.0),
    Point(49200.0,-234000.0),
    Point(49200.0,-252000.0),
    Point(53100.0,-252000.0),
    Point(53100.0,-239400.0),
    Point(53700.0,-239400.0),
    Point(53700.0,-224700.0),
    Point(54600.0,-224700.0),
    Point(54600.0,-219600.0),
    Point(55200.0,-219600.0),
    Point(55200.0,-216000.0),
    Point(55200.0,-198000.0),
    Point(54180.0,-198000.0),
    Point(52320.0,-198000.0),
    Point(52320.0,-230400.0)]],
"CVN"=>Array[Point[
    Point(43200.0,122400.0),
    Point(43200.0,162000.0),
    Point(43500.0,162000.0),
    Point(43500.0,190800.0),
    Point(48600.0,190800.0),
    Point(48600.0,174600.0),
    Point(50520.0,174600.0),
    Point(50520.0,110700.0),
    Point(50250.0,110700.0),
    Point(50250.0,102600.0),
    Point(48600.0,102600.0),
    Point(47700.0,102600.0),
    Point(47700.0,115200.0),
    Point(44400.0,115200.0),
    Point(44400.0,122400.0)]],
"VUL"=>Array[Point[
    Point(67920.0,75900.0),
    Point(67920.0,91800.0),
    Point(69330.0,91800.0),
    Point(69330.0,99000.0),
    Point(70800.0,99000.0),
    Point(70800.0,104400.0),
    Point(75300.0,104400.0),
    Point(75300.0,100800.0),
    Point(77100.0,100800.0),
    Point(77100.0,84600.0),
    Point(76500.0,84600.0),
    Point(76500.0,70200.0),
    Point(75780.0,70200.0),
    Point(74040.0,70200.0),
    Point(74040.0,73800.0),
    Point(72900.0,73800.0),
    Point(72900.0,76500.0),
    Point(71400.0,76500.0),
    Point(71400.0,69000.0),
    Point(69300.0,69000.0),
    Point(69300.0,75900.0)]],
"FOR"=>Array[Point[
    Point(6000.0,-85020.0),
    Point(9540.0,-85020.0),
    Point(13500.0,-85020.0),
    Point(13500.0,-129600.0),
    Point(12600.0,-129600.0),
    Point(12600.0,-138300.0),
    Point(10800.0,-138300.0),
    Point(10800.0,-144000.0),
    Point(8400.0,-144000.0),
    Point(6000.0,-144000.0),
    Point(6000.0,-88200.0)]],
"RET"=>Array[Point[
    Point(11520.0,-239400.0),
    Point(16500.0,-239400.0),
    Point(16500.0,-212400.0),
    Point(15600.0,-212400.0),
    Point(15600.0,-199800.0),
    Point(14400.0,-199800.0),
    Point(14400.0,-190200.0),
    Point(13800.0,-190200.0),
    Point(12600.0,-190200.0),
    Point(12600.0,-203400.0),
    Point(11520.0,-203400.0)]],
"AQL"=>Array[Point[
    Point(66900.0,0.0),
    Point(66900.0,7200.0),
    Point(67920.0,7200.0),
    Point(67920.0,22500.0),
    Point(67184.0,22500.0),
    Point(67184.0,43200.0),
    Point(67920.0,43200.0),
    Point(67920.0,66600.0),
    Point(68400.0,66600.0),
    Point(68400.0,58200.0),
    Point(71400.0,58200.0),
    Point(71400.0,56700.0),
    Point(72510.0,56700.0),
    Point(72510.0,30600.0),
    Point(73080.0,30600.0),
    Point(73080.0,7200.0),
    Point(73920.0,7200.0),
    Point(73920.0,0.0),
    Point(73920.0,-32400.0),
    Point(72000.0,-32400.0),
    Point(72000.0,-43080.0),
    Point(67920.0,-43080.0),
    Point(67920.0,-14400.0),
    Point(66900.0,-14400.0)]],
"UMA"=>Array[Point[
    Point(34500.0,151200.0),
    Point(33000.0,151200.0),
    Point(33000.0,169200.0),
    Point(30300.0,169200.0),
    Point(30300.0,216000.0),
    Point(28680.0,216000.0),
    Point(28680.0,264600.0),
    Point(33000.0,264600.0),
    Point(40800.0,264600.0),
    Point(40800.0,239400.0),
    Point(43200.0,239400.0),
    Point(43200.0,230400.0),
    Point(48600.0,230400.0),
    Point(48600.0,226800.0),
    Point(51900.0,226800.0),
    Point(51900.0,199800.0),
    Point(50520.0,199800.0),
    Point(50520.0,174600.0),
    Point(48600.0,174600.0),
    Point(48600.0,190800.0),
    Point(43500.0,190800.0),
    Point(43500.0,162000.0),
    Point(43200.0,162000.0),
    Point(43200.0,122400.0),
    Point(43200.0,104400.0),
    Point(42720.0,104400.0),
    Point(39600.0,104400.0),
    Point(39600.0,122400.0),
    Point(38820.0,122400.0),
    Point(38820.0,144000.0),
    Point(36600.0,144000.0),
    Point(36600.0,151200.0)]],
"ARI"=>Array[Point[
    Point(7200.0,35700.0),
    Point(6000.0,35700.0),
    Point(6000.0,90000.0),
    Point(6900.0,90000.0),
    Point(6900.0,98100.0),
    Point(8700.0,98100.0),
    Point(8700.0,110400.0),
    Point(9780.0,110400.0),
    Point(12120.0,110400.0),
    Point(12120.0,68400.0),
    Point(11820.0,68400.0),
    Point(11820.0,35700.0)]],
"CRU"=>Array[Point[
    Point(42600.0,-198000.0),
    Point(42600.0,-230400.0),
    Point(46200.0,-230400.0),
    Point(46200.0,-198000.0)]],
"PHE"=>Array[Point[
    Point(0.0,-207000.0),
    Point(4800.0,-207000.0),
    Point(4800.0,-189000.0),
    Point(5700.0,-189000.0),
    Point(5700.0,-181800.0),
    Point(6600.0,-181800.0),
    Point(6600.0,-172200.0),
    Point(8400.0,-172200.0),
    Point(8400.0,-144000.0),
    Point(0.0,-144000.0)],
    Point[
    Point(86400.0,-144000.0),
    Point(84000.0,-144000.0),
    Point(84000.0,-205200.0),
    Point(86400.0,-205200.0)]],
"AND"=>Array[Point[
    Point(0.0,172800.0),
    Point(0.0,165600.0),
    Point(3120.0,165600.0),
    Point(3120.0,172800.0),
    Point(4020.0,172800.0),
    Point(4020.0,180000.0),
    Point(4920.0,180000.0),
    Point(6000.0,180000.0),
    Point(6000.0,169200.0),
    Point(7350.0,169200.0),
    Point(7350.0,181800.0),
    Point(9060.0,181800.0),
    Point(9060.0,132300.0),
    Point(7200.0,132300.0),
    Point(7200.0,126000.0),
    Point(5070.0,126000.0),
    Point(5070.0,118800.0),
    Point(2580.0,118800.0),
    Point(2580.0,85500.0),
    Point(3060.0,85500.0),
    Point(3060.0,75600.0),
    Point(510.0,75600.0),
    Point(510.0,79200.0),
    Point(240.0,79200.0),
    Point(240.0,100800.0),
    Point(0.0,100800.0),
    Point(0.0,112800.0)],
    Point[
    Point(82320.0,124200.0),
    Point(82320.0,189000.0),
    Point(84000.0,189000.0),
    Point(84000.0,180000.0),
    Point(84900.0,180000.0),
    Point(84900.0,172800.0),
    Point(86400.0,172800.0),
    Point(86400.0,112800.0),
    Point(85500.0,112800.0),
    Point(85500.0,115500.0),
    Point(84600.0,115500.0),
    Point(84600.0,124200.0)
    ]],
"CRB"=>Array[Point[
    Point(54660.0,93600.0),
    Point(54660.0,118800.0),
    Point(55560.0,118800.0),
    Point(55560.0,144000.0),
    Point(56700.0,144000.0),
    Point(58800.0,144000.0),
    Point(58800.0,97200.0),
    Point(58200.0,97200.0),
    Point(58200.0,93600.0),
    Point(57720.0,93600.0)]],
"TRA"=>Array[Point[
    Point(53100.0,-252000.0),
    Point(53100.0,-239400.0),
    Point(53700.0,-239400.0),
    Point(53700.0,-224700.0),
    Point(54600.0,-224700.0),
    Point(54600.0,-219600.0),
    Point(55200.0,-219600.0),
    Point(55200.0,-216000.0),
    Point(59115.0,-216000.0),
    Point(59115.0,-219600.0),
    Point(59700.0,-219600.0),
    Point(59700.0,-224700.0),
    Point(60300.0,-224700.0),
    Point(60300.0,-234000.0),
    Point(60600.0,-234000.0),
    Point(60600.0,-239400.0),
    Point(61200.0,-239400.0),
    Point(61200.0,-252000.0)]],
"SCT"=>Array[Point[
    Point(65700.0,-57600.0),
    Point(67920.0,-57600.0),
    Point(67920.0,-43080.0),
    Point(67920.0,-14400.0),
    Point(66900.0,-14400.0),
    Point(65700.0,-14400.0)]],
"HOR"=>Array[Point[
    Point(15360.0,-144000.0),
    Point(15360.0,-176400.0),
    Point(14700.0,-176400.0),
    Point(14700.0,-183600.0),
    Point(13800.0,-183600.0),
    Point(13800.0,-190200.0),
    Point(12600.0,-190200.0),
    Point(12600.0,-203400.0),
    Point(11520.0,-203400.0),
    Point(11520.0,-239400.0),
    Point(7800.0,-239400.0),
    Point(7800.0,-207000.0),
    Point(7800.0,-194400.0),
    Point(8700.0,-194400.0),
    Point(8700.0,-183600.0),
    Point(9600.0,-183600.0),
    Point(9600.0,-176400.0),
    Point(10800.0,-176400.0),
    Point(10800.0,-165600.0),
    Point(12300.0,-165600.0),
    Point(12300.0,-158400.0),
    Point(13920.0,-158400.0),
    Point(13920.0,-144000.0)]],
"VEL"=>Array[Point[
    Point(39600.0,-199800.0),
    Point(39600.0,-137700.0),
    Point(33720.0,-137700.0),
    Point(33720.0,-126900.0),
    Point(30120.0,-126900.0),
    Point(30120.0,-154800.0),
    Point(28800.0,-154800.0),
    Point(28800.0,-177300.0),
    Point(29400.0,-177300.0),
    Point(29400.0,-190800.0),
    Point(30420.0,-190800.0),
    Point(30420.0,-192600.0),
    Point(31800.0,-192600.0),
    Point(31800.0,-199800.0)]],
"PIC"=>Array[Point[
    Point(21600.0,-154800.0),
    Point(18000.0,-154800.0),
    Point(17400.0,-154800.0),
    Point(17400.0,-163800.0),
    Point(16200.0,-163800.0),
    Point(16200.0,-176400.0),
    Point(16200.0,-194400.0),
    Point(18000.0,-194400.0),
    Point(18000.0,-203400.0),
    Point(19800.0,-203400.0),
    Point(19800.0,-219600.0),
    Point(21600.0,-219600.0),
    Point(21600.0,-230400.0),
    Point(23700.0,-230400.0),
    Point(24600.0,-230400.0),
    Point(24600.0,-208800.0),
    Point(23400.0,-208800.0),
    Point(23400.0,-198000.0),
    Point(22200.0,-198000.0),
    Point(22200.0,-185400.0),
    Point(21600.0,-185400.0),
    Point(21600.0,-177300.0)]],
"MEN"=>Array[Point[
    Point(27600.0,-307800.0),
    Point(12600.0,-307800.0),
    Point(12600.0,-270000.0),
    Point(16500.0,-270000.0),
    Point(16500.0,-252000.0),
    Point(23700.0,-252000.0),
    Point(23700.0,-270000.0),
    Point(27600.0,-270000.0),
    Point(27600.0,-307800.0)]],
"HER"=>Array[Point[
    Point(58560.0,14400.0),
    Point(57900.0,14400.0),
    Point(57900.0,57600.0),
    Point(57300.0,57600.0),
    Point(57300.0,79200.0),
    Point(57720.0,79200.0),
    Point(57720.0,93600.0),
    Point(58200.0,93600.0),
    Point(58200.0,97200.0),
    Point(58800.0,97200.0),
    Point(58800.0,144000.0),
    Point(56700.0,144000.0),
    Point(56700.0,185400.0),
    Point(61200.0,185400.0),
    Point(61200.0,181800.0),
    Point(65640.0,181800.0),
    Point(65640.0,171000.0),
    Point(65430.0,171000.0),
    Point(65430.0,108000.0),
    Point(66120.0,108000.0),
    Point(66120.0,93600.0),
    Point(67920.0,93600.0),
    Point(67920.0,91800.0),
    Point(67920.0,75900.0),
    Point(67920.0,66600.0),
    Point(67920.0,43200.0),
    Point(67184.0,43200.0),
    Point(65700.0,43200.0),
    Point(65700.0,51600.0),
    Point(62100.0,51600.0),
    Point(62100.0,46200.0),
    Point(60300.0,46200.0),
    Point(60300.0,14400.0)]],
"LUP"=>Array[Point[
    Point(51000.0,-198000.0),
    Point(52320.0,-198000.0),
    Point(54180.0,-198000.0),
    Point(54180.0,-194400.0),
    Point(55200.0,-194400.0),
    Point(55200.0,-172800.0),
    Point(56400.0,-172800.0),
    Point(56400.0,-151200.0),
    Point(57600.0,-151200.0),
    Point(57600.0,-102600.0),
    Point(56400.0,-102600.0),
    Point(53700.0,-102600.0),
    Point(53700.0,-151200.0),
    Point(51000.0,-151200.0)]],
"LIB"=>Array[Point[
    Point(54300.0,0.0),
    Point(52800.0,0.0),
    Point(52800.0,-28800.0),
    Point(51300.0,-28800.0),
    Point(51300.0,-79200.0),
    Point(51300.0,-84600.0),
    Point(53700.0,-84600.0),
    Point(53700.0,-102600.0),
    Point(56400.0,-102600.0),
    Point(56400.0,-72000.0),
    Point(57300.0,-72000.0),
    Point(57300.0,-28800.0),
    Point(57300.0,-9900.0),
    Point(54300.0,-9900.0)]],
"MON"=>Array[Point[
    Point(22470.0,0.0),
    Point(22470.0,36000.0),
    Point(22710.0,36000.0),
    Point(22710.0,43200.0),
    Point(24960.0,43200.0),
    Point(24960.0,36000.0),
    Point(25200.0,36000.0),
    Point(25200.0,19800.0),
    Point(25260.0,19800.0),
    Point(25260.0,5400.0),
    Point(25920.0,5400.0),
    Point(25920.0,0.0),
    Point(29100.0,0.0),
    Point(29100.0,-39600.0),
    Point(26520.0,-39600.0),
    Point(22020.0,-39600.0),
    Point(21000.0,-39600.0),
    Point(21000.0,-14400.0),
    Point(22470.0,-14400.0)]],
"TEL"=>Array[Point[
    Point(73200.0,-205200.0),
    Point(73200.0,-160200.0),
    Point(69000.0,-160200.0),
    Point(64800.0,-160200.0),
    Point(64800.0,-205200.0)]],
"DEL"=>Array[Point[
    Point(73920.0,7200.0),
    Point(73080.0,7200.0),
    Point(73080.0,30600.0),
    Point(72510.0,30600.0),
    Point(72510.0,56700.0),
    Point(72900.0,56700.0),
    Point(72900.0,73800.0),
    Point(74040.0,73800.0),
    Point(74040.0,70200.0),
    Point(75780.0,70200.0),
    Point(75780.0,42600.0),
    Point(75150.0,42600.0),
    Point(75150.0,21600.0),
    Point(75000.0,21600.0),
    Point(75000.0,7200.0)]],
"CEN"=>Array[Point[
    Point(39600.0,-126000.0),
    Point(39600.0,-137700.0),
    Point(39600.0,-199800.0),
    Point(40500.0,-199800.0),
    Point(40500.0,-230400.0),
    Point(42600.0,-230400.0),
    Point(42600.0,-198000.0),
    Point(46200.0,-198000.0),
    Point(46200.0,-230400.0),
    Point(48600.0,-230400.0),
    Point(52320.0,-230400.0),
    Point(52320.0,-198000.0),
    Point(51000.0,-198000.0),
    Point(51000.0,-151200.0),
    Point(53700.0,-151200.0),
    Point(53700.0,-102600.0),
    Point(45300.0,-102600.0),
    Point(45300.0,-118800.0),
    Point(44100.0,-118800.0),
    Point(44100.0,-126000.0)]],
"PAV"=>Array[Point[
    Point(64800.0,-270000.0),
    Point(76800.0,-270000.0),
    Point(76800.0,-216000.0),
    Point(73200.0,-216000.0),
    Point(73200.0,-205200.0),
    Point(64800.0,-205200.0),
    Point(63000.0,-205200.0),
    Point(63000.0,-239400.0),
    Point(64800.0,-239400.0)]],
"SEX"=>Array[Point[
    Point(34500.0,0.0),
    Point(34500.0,25200.0),
    Point(38700.0,25200.0),
    Point(38700.0,0.0),
    Point(38700.0,-21600.0),
    Point(38700.0,-39600.0),
    Point(34500.0,-39600.0)]],
"CAS"=>Array[Point[
    Point(1200.0,237600.0),
    Point(1200.0,277200.0),
    Point(12300.0,277200.0),
    Point(12300.0,244800.0),
    Point(11160.0,244800.0),
    Point(11160.0,205200.0),
    Point(8760.0,205200.0),
    Point(8760.0,210600.0),
    Point(6870.0,210600.0),
    Point(6870.0,207000.0),
    Point(6120.0,207000.0),
    Point(6120.0,194400.0),
    Point(4920.0,194400.0),
    Point(4920.0,180000.0),
    Point(4020.0,180000.0),
    Point(4020.0,172800.0),
    Point(3120.0,172800.0),
    Point(3120.0,165600.0),
    Point(0.0,165600.0),
    Point(0.0,172800.0),
    Point(0.0,237600.0)],
    Point[
    Point(84000.0,189000.0),
    Point(84000.0,180000.0),
    Point(84900.0,180000.0),
    Point(84900.0,172800.0),
    Point(86400.0,172800.0),
    Point(86400.0,226800.0),
    Point(83400.0,226800.0),
    Point(83400.0,212700.0),
    Point(82320.0,212700.0),
    Point(82320.0,189000.0)
    ]],
"CNC"=>Array[Point[
    Point(33300.0,25200.0),
    Point(29100.0,25200.0),
    Point(28530.0,25200.0),
    Point(28530.0,36000.0),
    Point(28110.0,36000.0),
    Point(28110.0,48600.0),
    Point(28110.0,72000.0),
    Point(28380.0,72000.0),
    Point(28380.0,100800.0),
    Point(28800.0,100800.0),
    Point(28800.0,120600.0),
    Point(33300.0,120600.0)]],
"CRA"=>Array[Point[
    Point(64200.0,-133200.0),
    Point(69000.0,-133200.0),
    Point(69000.0,-160200.0),
    Point(64800.0,-160200.0),
    Point(64200.0,-160200.0)]],
"ORI"=>Array[Point[
    Point(16620.0,0.0),
    Point(16620.0,55800.0),
    Point(17880.0,55800.0),
    Point(17880.0,57600.0),
    Point(19200.0,57600.0),
    Point(19200.0,55800.0),
    Point(20160.0,55800.0),
    Point(20160.0,45000.0),
    Point(20760.0,45000.0),
    Point(20760.0,64800.0),
    Point(20520.0,64800.0),
    Point(20520.0,82200.0),
    Point(21180.0,82200.0),
    Point(21180.0,77400.0),
    Point(22380.0,77400.0),
    Point(22380.0,63000.0),
    Point(22710.0,63000.0),
    Point(22710.0,43200.0),
    Point(22710.0,36000.0),
    Point(22470.0,36000.0),
    Point(22470.0,0.0),
    Point(22470.0,-14400.0),
    Point(21000.0,-14400.0),
    Point(21000.0,-39600.0),
    Point(18300.0,-39600.0),
    Point(18300.0,-14400.0),
    Point(16800.0,-14400.0),
    Point(16800.0,0.0)]],
"MUS"=>Array[Point[
    Point(40500.0,-230400.0),
    Point(40500.0,-270000.0),
    Point(49200.0,-270000.0),
    Point(49200.0,-252000.0),
    Point(49200.0,-234000.0),
    Point(48600.0,-234000.0),
    Point(48600.0,-230400.0),
    Point(46200.0,-230400.0),
    Point(42600.0,-230400.0)]],
"TAU"=>Array[Point[
    Point(11820.0,-900.0),
    Point(11820.0,0.0),
    Point(11820.0,35700.0),
    Point(11820.0,68400.0),
    Point(12120.0,68400.0),
    Point(12120.0,110400.0),
    Point(16200.0,110400.0),
    Point(16200.0,108000.0),
    Point(17100.0,108000.0),
    Point(17100.0,102600.0),
    Point(21180.0,102600.0),
    Point(21180.0,100800.0),
    Point(21180.0,82200.0),
    Point(20520.0,82200.0),
    Point(20520.0,64800.0),
    Point(20760.0,64800.0),
    Point(20760.0,45000.0),
    Point(20160.0,45000.0),
    Point(20160.0,55800.0),
    Point(19200.0,55800.0),
    Point(19200.0,57600.0),
    Point(17880.0,57600.0),
    Point(17880.0,55800.0),
    Point(16620.0,55800.0),
    Point(16620.0,0.0),
    Point(12900.0,0.0),
    Point(12900.0,-900.0)]],
"TUC"=>Array[Point[
    Point(0.0,-270000.0),
    Point(2700.0,-270000.0),
    Point(2700.0,-273600.0),
    Point(4800.0,-273600.0),
    Point(4800.0,-207000.0),
    Point(0.0,-207000.0)],
    Point[
    Point(86400.0,-270000.0),
    Point(84000.0,-270000.0),
    Point(84000.0,-239400.0),
    Point(79200.0,-239400.0),
    Point(79200.0,-205200.0),
    Point(86400.0,-205200.0)
    ]],
"APS"=>Array[Point[
    Point(49200.0,-293400.0),
    Point(64800.0,-293400.0),
    Point(64800.0,-270000.0),
    Point(64800.0,-239400.0),
    Point(63000.0,-239400.0),
    Point(61200.0,-239400.0),
    Point(61200.0,-252000.0),
    Point(53100.0,-252000.0),
    Point(49200.0,-252000.0),
    Point(49200.0,-270000.0)]],
"PER"=>Array[Point[
    Point(9780.0,110400.0),
    Point(9780.0,122400.0),
    Point(9240.0,122400.0),
    Point(9240.0,132300.0),
    Point(9060.0,132300.0),
    Point(9060.0,181800.0),
    Point(7350.0,181800.0),
    Point(7350.0,169200.0),
    Point(6000.0,169200.0),
    Point(6000.0,180000.0),
    Point(4920.0,180000.0),
    Point(4920.0,194400.0),
    Point(6120.0,194400.0),
    Point(6120.0,207000.0),
    Point(6870.0,207000.0),
    Point(6870.0,210600.0),
    Point(8760.0,210600.0),
    Point(8760.0,205200.0),
    Point(11160.0,205200.0),
    Point(11400.0,205200.0),
    Point(11400.0,198000.0),
    Point(12000.0,198000.0),
    Point(12000.0,189000.0),
    Point(16890.0,189000.0),
    Point(16890.0,129600.0),
    Point(16200.0,129600.0),
    Point(16200.0,110400.0),
    Point(12120.0,110400.0)]],
"ANT"=>Array[Point[
    Point(33720.0,-86400.0),
    Point(33720.0,-126900.0),
    Point(33720.0,-137700.0),
    Point(39600.0,-137700.0),
    Point(39600.0,-126000.0),
    Point(39000.0,-126000.0),
    Point(39000.0,-111000.0),
    Point(38100.0,-111000.0),
    Point(38100.0,-103800.0),
    Point(36900.0,-103800.0),
    Point(36900.0,-91800.0),
    Point(35100.0,-91800.0),
    Point(35100.0,-86400.0)]],
"SGE"=>Array[Point[
    Point(67920.0,66600.0),
    Point(67920.0,75900.0),
    Point(69300.0,75900.0),
    Point(69300.0,69000.0),
    Point(71400.0,69000.0),
    Point(71400.0,76500.0),
    Point(72900.0,76500.0),
    Point(72900.0,73800.0),
    Point(72900.0,56700.0),
    Point(72510.0,56700.0),
    Point(71400.0,56700.0),
    Point(71400.0,58200.0),
    Point(68400.0,58200.0),
    Point(68400.0,66600.0)]],
"LAC"=>Array[Point[
    Point(78750.0,129600.0),
    Point(78750.0,157500.0),
    Point(78870.0,157500.0),
    Point(78870.0,158400.0),
    Point(79080.0,158400.0),
    Point(79080.0,189900.0),
    Point(79680.0,189900.0),
    Point(79680.0,198000.0),
    Point(80340.0,198000.0),
    Point(80340.0,202500.0),
    Point(82320.0,202500.0),
    Point(82320.0,189000.0),
    Point(82320.0,124200.0),
    Point(82140.0,124200.0),
    Point(82140.0,126000.0),
    Point(79200.0,126000.0),
    Point(79200.0,129600.0)]],
"SER2"=>Array[Point[
    Point(65700.0,0.0),
    Point(65700.0,10800.0),
    Point(66330.0,10800.0),
    Point(66330.0,16200.0),
    Point(65700.0,16200.0),
    Point(65700.0,22500.0),
    Point(67184.0,22500.0),
    Point(67920.0,22500.0),
    Point(67920.0,7200.0),
    Point(66900.0,7200.0),
    Point(66900.0,0.0),
    Point(66900.0,-14400.0),
    Point(65700.0,-14400.0),
    Point(65700.0,-57600.0),
    Point(63360.0,-57600.0),
    Point(61800.0,-57600.0),
    Point(61800.0,-36000.0),
    Point(63300.0,-36000.0),
    Point(63300.0,-37200.0),
    Point(63600.0,-37200.0),
    Point(63600.0,-36000.0),
    Point(64680.0,-36000.0),
    Point(64680.0,-14400.0),
    Point(64200.0,-14400.0),
    Point(64200.0,0.0)]],
"SGR"=>Array[Point[
    Point(67920.0,-43080.0),
    Point(67920.0,-57600.0),
    Point(65700.0,-57600.0),
    Point(63360.0,-57600.0),
    Point(63360.0,-108000.0),
    Point(64200.0,-108000.0),
    Point(64200.0,-133200.0),
    Point(69000.0,-133200.0),
    Point(69000.0,-160200.0),
    Point(73200.0,-160200.0),
    Point(73200.0,-100800.0),
    Point(72000.0,-100800.0),
    Point(72000.0,-43080.0)]],
"AUR"=>Array[Point[
    Point(16200.0,110400.0),
    Point(16200.0,129600.0),
    Point(16890.0,129600.0),
    Point(16890.0,189000.0),
    Point(18000.0,189000.0),
    Point(18000.0,201600.0),
    Point(21960.0,201600.0),
    Point(21960.0,194400.0),
    Point(23400.0,194400.0),
    Point(23400.0,180000.0),
    Point(24480.0,180000.0),
    Point(24480.0,160200.0),
    Point(26520.0,160200.0),
    Point(26520.0,127800.0),
    Point(23520.0,127800.0),
    Point(23520.0,100800.0),
    Point(21180.0,100800.0),
    Point(21180.0,102600.0),
    Point(17100.0,102600.0),
    Point(17100.0,108000.0),
    Point(16200.0,108000.0)]],
"SCL"=>Array[Point[
    Point(82800.0,-88200.0),
    Point(85800.0,-88200.0),
    Point(85800.0,-144000.0),
    Point(84000.0,-144000.0),
    Point(84000.0,-133200.0),
    Point(82800.0,-133200.0)],
    Point[
    Point(6000.0,-88200.0),
    Point(6000.0,-144000.0),
    Point(0.0,-144000.0),
    Point(0.0,-88200.0)]],
"LMI"=>Array[Point[
    Point(33300.0,120600.0),
    Point(33300.0,143100.0),
    Point(34500.0,143100.0),
    Point(34500.0,151200.0),
    Point(36600.0,151200.0),
    Point(36600.0,144000.0),
    Point(38820.0,144000.0),
    Point(38820.0,122400.0),
    Point(39600.0,122400.0),
    Point(39600.0,104400.0),
    Point(39600.0,91800.0),
    Point(38700.0,91800.0),
    Point(38700.0,84600.0),
    Point(37800.0,84600.0),
    Point(37800.0,102600.0),
    Point(35580.0,102600.0),
    Point(35580.0,120600.0)]],
"COL"=>Array[Point[
    Point(18000.0,-154800.0),
    Point(18000.0,-96300.0),
    Point(22020.0,-96300.0),
    Point(22020.0,-118800.0),
    Point(23700.0,-118800.0),
    Point(23700.0,-154800.0),
    Point(21600.0,-154800.0)]],
"SER1"=>Array[Point[
    Point(54300.0,0.0),
    Point(54300.0,28800.0),
    Point(54300.0,93600.0),
    Point(54660.0,93600.0),
    Point(57720.0,93600.0),
    Point(57720.0,79200.0),
    Point(57300.0,79200.0),
    Point(57300.0,57600.0),
    Point(57900.0,57600.0),
    Point(57900.0,14400.0),
    Point(58560.0,14400.0),
    Point(58560.0,0.0),
    Point(58560.0,-9900.0),
    Point(57300.0,-9900.0),
    Point(54300.0,-9900.0)]],
"CRV"=>Array[Point[
    Point(46200.0,-39600.0),
    Point(42600.0,-39600.0),
    Point(42600.0,-84600.0),
    Point(45300.0,-84600.0),
    Point(45300.0,-79200.0),
    Point(46200.0,-79200.0)]],
"IND"=>Array[Point[
    Point(76800.0,-270000.0),
    Point(84000.0,-270000.0),
    Point(84000.0,-239400.0),
    Point(79200.0,-239400.0),
    Point(79200.0,-205200.0),
    Point(79200.0,-180000.0),
    Point(76800.0,-180000.0),
    Point(76800.0,-160200.0),
    Point(73200.0,-160200.0),
    Point(73200.0,-205200.0),
    Point(73200.0,-216000.0),
    Point(76800.0,-216000.0)]],
"MIC"=>Array[Point[
    Point(73200.0,-100800.0),
    Point(76800.0,-100800.0),
    Point(76800.0,-133200.0),
    Point(76800.0,-160200.0),
    Point(73200.0,-160200.0)]],
"SCO"=>Array[Point[
    Point(57300.0,-28800.0),
    Point(58560.0,-28800.0),
    Point(58560.0,-63900.0),
    Point(58950.0,-63900.0),
    Point(58950.0,-67500.0),
    Point(58560.0,-67500.0),
    Point(58560.0,-84300.0),
    Point(60300.0,-84300.0),
    Point(60300.0,-108000.0),
    Point(63360.0,-108000.0),
    Point(64200.0,-108000.0),
    Point(64200.0,-133200.0),
    Point(64200.0,-160200.0),
    Point(59115.0,-160200.0),
    Point(59115.0,-151200.0),
    Point(57600.0,-151200.0),
    Point(57600.0,-102600.0),
    Point(56400.0,-102600.0),
    Point(56400.0,-72000.0),
    Point(57300.0,-72000.0)]],
"CET"=>Array[Point[
    Point(0.0,-25200.0),
    Point(1200.0,-25200.0),
    Point(1200.0,7200.0),
    Point(7200.0,7200.0),
    Point(7200.0,35700.0),
    Point(11820.0,35700.0),
    Point(11820.0,0.0),
    Point(11820.0,-900.0),
    Point(9540.0,-900.0),
    Point(9540.0,-85020.0),
    Point(6000.0,-85020.0),
    Point(6000.0,-88200.0),
    Point(0.0,-88200.0)],

    Point[
    Point(86400.0, -144000.0),
    Point(85800.0, -144000.0),
    Point(85800.0, -25200.0),
    Point(86400.0, -25200.0)
    ]],
"GRU"=>Array[Point[
    Point(76800.0,-133200.0),
    Point(76800.0,-160200.0),
    Point(76800.0,-180000.0),
    Point(79200.0,-180000.0),
    Point(79200.0,-205200.0),
    Point(84000.0,-205200.0),
    Point(84000.0,-144000.0),
    Point(84000.0,-133200.0),
    Point(82800.0,-133200.0)]],
"LEO"=>Array[Point[
    Point(38700.0,0.0),
    Point(38700.0,25200.0),
    Point(34500.0,25200.0),
    Point(33300.0,25200.0),
    Point(33300.0,120600.0),
    Point(35580.0,120600.0),
    Point(35580.0,102600.0),
    Point(37800.0,102600.0),
    Point(37800.0,84600.0),
    Point(38700.0,84600.0),
    Point(38700.0,91800.0),
    Point(39600.0,91800.0),
    Point(39600.0,104400.0),
    Point(42720.0,104400.0),
    Point(42720.0,50400.0),
    Point(42720.0,39600.0),
    Point(41460.0,39600.0),
    Point(41460.0,0.0),
    Point(41460.0,-21600.0),
    Point(38700.0,-21600.0)]],
"PUP"=>Array[Point[
    Point(26520.0,-39600.0),
    Point(26520.0,-118800.0),
    Point(23700.0,-118800.0),
    Point(23700.0,-154800.0),
    Point(21600.0,-154800.0),
    Point(21600.0,-177300.0),
    Point(28800.0,-177300.0),
    Point(28800.0,-154800.0),
    Point(30120.0,-154800.0),
    Point(30120.0,-126900.0),
    Point(30120.0,-61200.0),
    Point(30120.0,-39600.0),
    Point(29100.0,-39600.0)]],
"CMI"=>Array[Point[
    Point(29100.0,0.0),
    Point(25920.0,0.0),
    Point(25920.0,5400.0),
    Point(25260.0,5400.0),
    Point(25260.0,19800.0),
    Point(25200.0,19800.0),
    Point(25200.0,36000.0),
    Point(25200.0,45000.0),
    Point(27000.0,45000.0),
    Point(27000.0,48600.0),
    Point(28110.0,48600.0),
    Point(28110.0,36000.0),
    Point(28530.0,36000.0),
    Point(28530.0,25200.0),
    Point(29100.0,25200.0)]],
"ERI"=>Array[Point[
    Point(12900.0,0.0),
    Point(16620.0,0.0),
    Point(16800.0,0.0),
    Point(16800.0,-14400.0),
    Point(18300.0,-14400.0),
    Point(18300.0,-39600.0),
    Point(17700.0,-39600.0),
    Point(17700.0,-48600.0),
    Point(17400.0,-48600.0),
    Point(17400.0,-96300.0),
    Point(16920.0,-96300.0),
    Point(16920.0,-108000.0),
    Point(16500.0,-108000.0),
    Point(16500.0,-133200.0),
    Point(15360.0,-133200.0),
    Point(15360.0,-144000.0),
    Point(13920.0,-144000.0),
    Point(13920.0,-158400.0),
    Point(12300.0,-158400.0),
    Point(12300.0,-165600.0),
    Point(10800.0,-165600.0),
    Point(10800.0,-176400.0),
    Point(9600.0,-176400.0),
    Point(9600.0,-183600.0),
    Point(8700.0,-183600.0),
    Point(8700.0,-194400.0),
    Point(7800.0,-194400.0),
    Point(7800.0,-207000.0),
    Point(4800.0,-207000.0),
    Point(4800.0,-189000.0),
    Point(5700.0,-189000.0),
    Point(5700.0,-181800.0),
    Point(6600.0,-181800.0),
    Point(6600.0,-172200.0),
    Point(8400.0,-172200.0),
    Point(8400.0,-144000.0),
    Point(10800.0,-144000.0),
    Point(10800.0,-138300.0),
    Point(12600.0,-138300.0),
    Point(12600.0,-129600.0),
    Point(13500.0,-129600.0),
    Point(13500.0,-85020.0),
    Point(9540.0,-85020.0),
    Point(9540.0,-900.0),
    Point(11820.0,-900.0),
    Point(12900.0,-900.0)]],
"EQU"=>Array[Point[
    Point(75000.0,7200.0),
    Point(75000.0,21600.0),
    Point(75150.0,21600.0),
    Point(75150.0,42600.0),
    Point(75780.0,42600.0),
    Point(76020.0,42600.0),
    Point(76020.0,45000.0),
    Point(76800.0,45000.0),
    Point(76800.0,7200.0)]],
"BOO"=>Array[Point[
    Point(54300.0,28800.0),
    Point(48600.0,28800.0),
    Point(48600.0,54000.0),
    Point(48600.0,102600.0),
    Point(50250.0,102600.0),
    Point(50250.0,110700.0),
    Point(50520.0,110700.0),
    Point(50520.0,174600.0),
    Point(50520.0,199800.0),
    Point(51900.0,199800.0),
    Point(54900.0,199800.0),
    Point(54900.0,190800.0),
    Point(56700.0,190800.0),
    Point(56700.0,185400.0),
    Point(56700.0,144000.0),
    Point(55560.0,144000.0),
    Point(55560.0,118800.0),
    Point(54660.0,118800.0),
    Point(54660.0,93600.0),
    Point(54300.0,93600.0)]],
"CAP"=>Array[Point[
    Point(73920.0,-32400.0),
    Point(72000.0,-32400.0),
    Point(72000.0,-43080.0),
    Point(72000.0,-100800.0),
    Point(73200.0,-100800.0),
    Point(76800.0,-100800.0),
    Point(76800.0,-88200.0),
    Point(78720.0,-88200.0),
    Point(78720.0,-32400.0),
    Point(76800.0,-32400.0),
    Point(76800.0,-54000.0),
    Point(73920.0,-54000.0)]],
"CAE"=>Array[Point[
    Point(15360.0,-144000.0),
    Point(15360.0,-176400.0),
    Point(16200.0,-176400.0),
    Point(16200.0,-163800.0),
    Point(17400.0,-163800.0),
    Point(17400.0,-154800.0),
    Point(18000.0,-154800.0),
    Point(18000.0,-96300.0),
    Point(17400.0,-96300.0),
    Point(16920.0,-96300.0),
    Point(16920.0,-108000.0),
    Point(16500.0,-108000.0),
    Point(16500.0,-133200.0),
    Point(15360.0,-133200.0)]],
"DOR"=>Array[Point[
    Point(13800.0,-190200.0),
    Point(14400.0,-190200.0),
    Point(14400.0,-199800.0),
    Point(15600.0,-199800.0),
    Point(15600.0,-212400.0),
    Point(16500.0,-212400.0),
    Point(16500.0,-239400.0),
    Point(16500.0,-252000.0),
    Point(23700.0,-252000.0),
    Point(23700.0,-230400.0),
    Point(21600.0,-230400.0),
    Point(21600.0,-219600.0),
    Point(19800.0,-219600.0),
    Point(19800.0,-203400.0),
    Point(18000.0,-203400.0),
    Point(18000.0,-194400.0),
    Point(16200.0,-194400.0),
    Point(16200.0,-176400.0),
    Point(15360.0,-176400.0),
    Point(14700.0,-176400.0),
    Point(14700.0,-183600.0),
    Point(13800.0,-183600.0)]],
"LEP"=>Array[Point[
    Point(17400.0,-96300.0),
    Point(18000.0,-96300.0),
    Point(22020.0,-96300.0),
    Point(22020.0,-39600.0),
    Point(21000.0,-39600.0),
    Point(18300.0,-39600.0),
    Point(17700.0,-39600.0),
    Point(17700.0,-48600.0),
    Point(17400.0,-48600.0)]],
"PSA"=>Array[Point[
    Point(82800.0,-88200.0),
    Point(78720.0,-88200.0),
    Point(76800.0,-88200.0),
    Point(76800.0,-100800.0),
    Point(76800.0,-133200.0),
    Point(82800.0,-133200.0)]],
"CRT"=>Array[Point[
    Point(38700.0,-21600.0),
    Point(38700.0,-39600.0),
    Point(38700.0,-68400.0),
    Point(39000.0,-68400.0),
    Point(39000.0,-84600.0),
    Point(42600.0,-84600.0),
    Point(42600.0,-39600.0),
    Point(42600.0,-21600.0),
    Point(41460.0,-21600.0)]],
"OPH"=>Array[Point[
    Point(58560.0,0.0),
    Point(58560.0,14400.0),
    Point(60300.0,14400.0),
    Point(60300.0,46200.0),
    Point(62100.0,46200.0),
    Point(62100.0,51600.0),
    Point(65700.0,51600.0),
    Point(65700.0,43200.0),
    Point(67184.0,43200.0),
    Point(67184.0,22500.0),
    Point(65700.0,22500.0),
    Point(65700.0,16200.0),
    Point(66330.0,16200.0),
    Point(66330.0,10800.0),
    Point(65700.0,10800.0),
    Point(65700.0,0.0),
    Point(64200.0,0.0),
    Point(64200.0,-14400.0),
    Point(64680.0,-14400.0),
    Point(64680.0,-36000.0),
    Point(63600.0,-36000.0),
    Point(63600.0,-37200.0),
    Point(63300.0,-37200.0),
    Point(63300.0,-36000.0),
    Point(61800.0,-36000.0),
    Point(61800.0,-57600.0),
    Point(63360.0,-57600.0),
    Point(63360.0,-108000.0),
    Point(60300.0,-108000.0),
    Point(60300.0,-84300.0),
    Point(58560.0,-84300.0),
    Point(58560.0,-67500.0),
    Point(58950.0,-67500.0),
    Point(58950.0,-63900.0),
    Point(58560.0,-63900.0),
    Point(58560.0,-28800.0),
    Point(57300.0,-28800.0),
    Point(57300.0,-9900.0),
    Point(58560.0,-9900.0)]],
"VIR"=>Array[Point[
    Point(41460.0,0.0),
    Point(41460.0,39600.0),
    Point(42720.0,39600.0),
    Point(42720.0,50400.0),
    Point(46200.0,50400.0),
    Point(46200.0,54000.0),
    Point(48600.0,54000.0),
    Point(48600.0,28800.0),
    Point(54300.0,28800.0),
    Point(54300.0,0.0),
    Point(52800.0,0.0),
    Point(52800.0,-28800.0),
    Point(51300.0,-28800.0),
    Point(51300.0,-79200.0),
    Point(46200.0,-79200.0),
    Point(46200.0,-39600.0),
    Point(42600.0,-39600.0),
    Point(42600.0,-21600.0),
    Point(41460.0,-21600.0)]],
"CAM"=>Array[Point[
    Point(21960.0,201600.0),
    Point(18000.0,201600.0),
    Point(18000.0,189000.0),
    Point(16890.0,189000.0),
    Point(12000.0,189000.0),
    Point(12000.0,198000.0),
    Point(11400.0,198000.0),
    Point(11400.0,205200.0),
    Point(11160.0,205200.0),
    Point(11160.0,244800.0),
    Point(12300.0,244800.0),
    Point(12300.0,277200.0),
    Point(12630.0,277200.0),
    Point(12630.0,288000.0),
    Point(18000.0,288000.0),
    Point(18000.0,306000.0),
    Point(28800.0,306000.0),
    Point(28800.0,311400.0),
    Point(52200.0,311400.0),
    Point(52200.0,288000.0),
    Point(48900.0,288000.0),
    Point(48900.0,277200.0),
    Point(46800.0,277200.0),
    Point(41400.0,277200.0),
    Point(41400.0,288000.0),
    Point(38400.0,288000.0),
    Point(38400.0,295200.0),
    Point(33000.0,295200.0),
    Point(33000.0,264600.0),
    Point(28680.0,264600.0),
    Point(28680.0,216000.0),
    Point(25200.0,216000.0),
    Point(25200.0,223200.0),
    Point(21960.0,223200.0)]],
"COM"=>Array[Point[
    Point(42720.0,50400.0),
    Point(42720.0,104400.0),
    Point(43200.0,104400.0),
    Point(43200.0,122400.0),
    Point(44400.0,122400.0),
    Point(44400.0,115200.0),
    Point(47700.0,115200.0),
    Point(47700.0,102600.0),
    Point(48600.0,102600.0),
    Point(48600.0,54000.0),
    Point(46200.0,54000.0),
    Point(46200.0,50400.0)]],
"VOL"=>Array[Point[
    Point(23700.0,-230400.0),
    Point(23700.0,-252000.0),
    Point(23700.0,-270000.0),
    Point(27600.0,-270000.0),
    Point(32520.0,-270000.0),
    Point(32520.0,-230400.0),
    Point(24600.0,-230400.0)]],
"CEP"=>Array[Point[
    Point(72000.0,214200.0),
    Point(72000.0,221400.0),
    Point(73500.0,221400.0),
    Point(73500.0,241200.0),
    Point(74400.0,241200.0),
    Point(74400.0,270000.0),
    Point(72600.0,270000.0),
    Point(72600.0,288000.0),
    Point(75600.0,288000.0),
    Point(75600.0,309600.0),
    Point(75600.0,310200.0),
    Point(82800.0,310200.0),
    Point(82800.0,316800.0),
    Point(86400.0,316800.0),
    Point(86400.0,226800.0),
    Point(83400.0,226800.0),
    Point(83400.0,212700.0),
    Point(82320.0,212700.0),
    Point(82320.0,202500.0),
    Point(80340.0,202500.0),
    Point(80340.0,198000.0),
    Point(79680.0,198000.0),
    Point(79680.0,189900.0),
    Point(79080.0,189900.0),
    Point(79080.0,197400.0),
    Point(74160.0,197400.0),
    Point(74160.0,219300.0),
    Point(73932.0,219300.0),
    Point(73932.0,214200.0)],

    Point[
    Point(33588.0,316800.0),
    Point(33588.0,311400.0),
    Point(28800.0,311400.0),
    Point(28800.0,306000.0),
    Point(18000.0,306000.0),
    Point(18000.0,288000.0),
    Point(12630.0,288000.0),
    Point(12630.0,277200.0),
    Point(12300.0,277200.0),
    Point(1200.0,277200.0),
    Point(1200.0,237600.0),
    Point(0.0,237600.0),
    Point(0.0,316800.0)
    ]],
"DRA"=>Array[Point[
    Point(33000.0,264600.0),
    Point(33000.0,295200.0),
    Point(38400.0,295200.0),
    Point(38400.0,288000.0),
    Point(41400.0,288000.0),
    Point(41400.0,277200.0),
    Point(46800.0,277200.0),
    Point(46800.0,252000.0),
    Point(50400.0,252000.0),
    Point(50400.0,237600.0),
    Point(56400.0,237600.0),
    Point(56400.0,252000.0),
    Point(59520.0,252000.0),
    Point(59520.0,270000.0),
    Point(63000.0,270000.0),
    Point(63000.0,288000.0),
    Point(64800.0,288000.0),
    Point(64800.0,309600.0),
    Point(75600.0,309600.0),
    Point(75600.0,288000.0),
    Point(72600.0,288000.0),
    Point(72600.0,270000.0),
    Point(74400.0,270000.0),
    Point(74400.0,241200.0),
    Point(73500.0,241200.0),
    Point(73500.0,221400.0),
    Point(72000.0,221400.0),
    Point(72000.0,214200.0),
    Point(71160.0,214200.0),
    Point(71160.0,208800.0),
    Point(69900.0,208800.0),
    Point(69900.0,199800.0),
    Point(68700.0,199800.0),
    Point(68700.0,171000.0),
    Point(65640.0,171000.0),
    Point(65640.0,181800.0),
    Point(61200.0,181800.0),
    Point(61200.0,185400.0),
    Point(56700.0,185400.0),
    Point(56700.0,190800.0),
    Point(54900.0,190800.0),
    Point(54900.0,199800.0),
    Point(51900.0,199800.0),
    Point(51900.0,226800.0),
    Point(48600.0,226800.0),
    Point(48600.0,230400.0),
    Point(43200.0,230400.0),
    Point(43200.0,239400.0),
    Point(40800.0,239400.0),
    Point(40800.0,264600.0)]],
"CMA"=>Array[Point[
    Point(22020.0,-39600.0),
    Point(26520.0,-39600.0),
    Point(26520.0,-118800.0),
    Point(23700.0,-118800.0),
    Point(22020.0,-118800.0),
    Point(22020.0,-96300.0)]],
"OCT"=>Array[Point[
    Point(0.0,-324000.0),
    Point(0.0,-293400.0),
    Point(12600.0,-293400.0),
    Point(12600.0,-307800.0),
    Point(27600.0,-307800.0),
    Point(27600.0,-293400.0),
    Point(64800.0,-293400.0),
    Point(64800.0,-270000.0),
    Point(86400.0,-270000.0),
    Point(86400.0,-324000.0)]],
"AQR"=>Array[Point[
    Point(73920.0,0.0),
    Point(73920.0,7200.0),
    Point(75000.0,7200.0),
    Point(76800.0,7200.0),
    Point(77280.0,7200.0),
    Point(77280.0,9900.0),
    Point(78000.0,9900.0),
    Point(78000.0,6300.0),
    Point(79200.0,6300.0),
    Point(79200.0,7200.0),
    Point(81900.0,7200.0),
    Point(81900.0,0.0),
    Point(81900.0,-14400.0),
    Point(85800.0,-14400.0),
    Point(85800.0,-25200.0),
    Point(85800.0,-88200.0),
    Point(82800.0,-88200.0),
    Point(78720.0,-88200.0),
    Point(78720.0,-32400.0),
    Point(76800.0,-32400.0),
    Point(76800.0,-54000.0),
    Point(73920.0,-54000.0),
    Point(73920.0,-32400.0)]],
"TRI"=>Array[Point[
    Point(6000.0,90000.0),
    Point(6000.0,100800.0),
    Point(5070.0,100800.0),
    Point(5070.0,118800.0),
    Point(5070.0,126000.0),
    Point(7200.0,126000.0),
    Point(7200.0,132300.0),
    Point(9060.0,132300.0),
    Point(9240.0,132300.0),
    Point(9240.0,122400.0),
    Point(9780.0,122400.0),
    Point(9780.0,110400.0),
    Point(8700.0,110400.0),
    Point(8700.0,98100.0),
    Point(6900.0,98100.0),
    Point(6900.0,90000.0)]],
"HYI"=>Array[Point[
    Point(0.0,-293400.0),
    Point(0.0,-270000.0),
    Point(2700.0,-270000.0),
    Point(2700.0,-273600.0),
    Point(4800.0,-273600.0),
    Point(4800.0,-207000.0),
    Point(7800.0,-207000.0),
    Point(7800.0,-239400.0),
    Point(11520.0,-239400.0),
    Point(16500.0,-239400.0),
    Point(16500.0,-252000.0),
    Point(16500.0,-270000.0),
    Point(12600.0,-270000.0),
    Point(12600.0,-293400.0)]],
"PEG"=>Array[Point[
    Point(76800.0,7200.0),
    Point(76800.0,45000.0),
    Point(76020.0,45000.0),
    Point(76020.0,42600.0),
    Point(75780.0,42600.0),
    Point(75780.0,70200.0),
    Point(76500.0,70200.0),
    Point(76500.0,84600.0),
    Point(77100.0,84600.0),
    Point(77100.0,100800.0),
    Point(78240.0,100800.0),
    Point(78240.0,129600.0),
    Point(78750.0,129600.0),
    Point(79200.0,129600.0),
    Point(79200.0,126000.0),
    Point(82140.0,126000.0),
    Point(82140.0,124200.0),
    Point(82320.0,124200.0),
    Point(84600.0,124200.0),
    Point(84600.0,115500.0),
    Point(85500.0,115500.0),
    Point(85500.0,112800.0),
    Point(86400.0,112800.0),
    Point(86400.0,45000.0),
    Point(85800.0,45000.0),
    Point(85800.0,27000.0),
    Point(81900.0,27000.0),
    Point(81900.0,7200.0),
    Point(79200.0,7200.0),
    Point(79200.0,6300.0),
    Point(78000.0,6300.0),
    Point(78000.0,9900.0),
    Point(77280.0,9900.0),
    Point(77280.0,7200.0)],
    Point[
    Point(0.0,112800.0),
    Point(0.0,100800.0),
    Point(240.0,100800.0),
    Point(240.0,79200.0),
    Point(510.0,79200.0),
    Point(510.0,75600.0),
    Point(510.0,45000.0),
    Point(0.0,45000.0),
    Point(0.0,36000.0)]],
"HYA"=>Array[Point[
    Point(29100.0,0.0),
    Point(29100.0,25200.0),
    Point(33300.0,25200.0),
    Point(34500.0,25200.0),
    Point(34500.0,0.0),
    Point(34500.0,-39600.0),
    Point(38700.0,-39600.0),
    Point(38700.0,-68400.0),
    Point(39000.0,-68400.0),
    Point(39000.0,-84600.0),
    Point(42600.0,-84600.0),
    Point(45300.0,-84600.0),
    Point(45300.0,-79200.0),
    Point(46200.0,-79200.0),
    Point(51300.0,-79200.0),
    Point(51300.0,-84600.0),
    Point(53700.0,-84600.0),
    Point(53700.0,-102600.0),
    Point(45300.0,-102600.0),
    Point(45300.0,-118800.0),
    Point(44100.0,-118800.0),
    Point(44100.0,-126000.0),
    Point(39600.0,-126000.0),
    Point(39000.0,-126000.0),
    Point(39000.0,-111000.0),
    Point(38100.0,-111000.0),
    Point(38100.0,-103800.0),
    Point(36900.0,-103800.0),
    Point(36900.0,-91800.0),
    Point(35100.0,-91800.0),
    Point(35100.0,-86400.0),
    Point(33720.0,-86400.0),
    Point(32700.0,-86400.0),
    Point(32700.0,-68400.0),
    Point(30900.0,-68400.0),
    Point(30900.0,-61200.0),
    Point(30120.0,-61200.0),
    Point(30120.0,-39600.0),
    Point(29100.0,-39600.0)]],
"GEM"=>Array[Point[
    Point(22710.0,43200.0),
    Point(22710.0,63000.0),
    Point(22380.0,63000.0),
    Point(22380.0,77400.0),
    Point(21180.0,77400.0),
    Point(21180.0,82200.0),
    Point(21180.0,100800.0),
    Point(23520.0,100800.0),
    Point(23520.0,127800.0),
    Point(26520.0,127800.0),
    Point(27900.0,127800.0),
    Point(27900.0,120600.0),
    Point(28800.0,120600.0),
    Point(28800.0,100800.0),
    Point(28380.0,100800.0),
    Point(28380.0,72000.0),
    Point(28110.0,72000.0),
    Point(28110.0,48600.0),
    Point(27000.0,48600.0),
    Point(27000.0,45000.0),
    Point(25200.0,45000.0),
    Point(25200.0,36000.0),
    Point(24960.0,36000.0),
    Point(24960.0,43200.0)]],
"CHA"=>Array[Point[
    Point(27600.0,-293400.0),
    Point(49200.0,-293400.0),
    Point(49200.0,-270000.0),
    Point(40500.0,-270000.0),
    Point(32520.0,-270000.0),
    Point(27600.0,-270000.0)]],
"CAR"=>Array[Point[
    Point(40500.0,-199800.0),
    Point(39600.0,-199800.0),
    Point(31800.0,-199800.0),
    Point(31800.0,-192600.0),
    Point(30420.0,-192600.0),
    Point(30420.0,-190800.0),
    Point(29400.0,-190800.0),
    Point(29400.0,-177300.0),
    Point(28800.0,-177300.0),
    Point(21600.0,-177300.0),
    Point(21600.0,-185400.0),
    Point(22200.0,-185400.0),
    Point(22200.0,-198000.0),
    Point(23400.0,-198000.0),
    Point(23400.0,-208800.0),
    Point(24600.0,-208800.0),
    Point(24600.0,-230400.0),
    Point(32520.0,-230400.0),
    Point(32520.0,-270000.0),
    Point(40500.0,-270000.0),
    Point(40500.0,-230400.0)]],
"LYN"=>Array[Point[
    Point(26520.0,127800.0),
    Point(26520.0,160200.0),
    Point(24480.0,160200.0),
    Point(24480.0,180000.0),
    Point(23400.0,180000.0),
    Point(23400.0,194400.0),
    Point(21960.0,194400.0),
    Point(21960.0,201600.0),
    Point(21960.0,223200.0),
    Point(25200.0,223200.0),
    Point(25200.0,216000.0),
    Point(28680.0,216000.0),
    Point(30300.0,216000.0),
    Point(30300.0,169200.0),
    Point(33000.0,169200.0),
    Point(33000.0,151200.0),
    Point(34500.0,151200.0),
    Point(34500.0,143100.0),
    Point(33300.0,143100.0),
    Point(33300.0,120600.0),
    Point(28800.0,120600.0),
    Point(27900.0,120600.0),
    Point(27900.0,127800.0)]],
"LYR"=>Array[Point[
    Point(67920.0,91800.0),
    Point(67920.0,93600.0),
    Point(66120.0,93600.0),
    Point(66120.0,108000.0),
    Point(65430.0,108000.0),
    Point(65430.0,171000.0),
    Point(65640.0,171000.0),
    Point(68700.0,171000.0),
    Point(69000.0,171000.0),
    Point(69000.0,156600.0),
    Point(69840.0,156600.0),
    Point(69840.0,131400.0),
    Point(69690.0,131400.0),
    Point(69690.0,108000.0),
    Point(69330.0,108000.0),
    Point(69330.0,99000.0),
    Point(69330.0,91800.0)]],
"CYG"=>Array[Point[
    Point(69330.0,99000.0),
    Point(69330.0,108000.0),
    Point(69690.0,108000.0),
    Point(69690.0,131400.0),
    Point(69840.0,131400.0),
    Point(69840.0,156600.0),
    Point(69000.0,156600.0),
    Point(69000.0,171000.0),
    Point(68700.0,171000.0),
    Point(68700.0,199800.0),
    Point(69900.0,199800.0),
    Point(69900.0,208800.0),
    Point(71160.0,208800.0),
    Point(71160.0,214200.0),
    Point(72000.0,214200.0),
    Point(73932.0,214200.0),
    Point(73932.0,219300.0),
    Point(74160.0,219300.0),
    Point(74160.0,197400.0),
    Point(79080.0,197400.0),
    Point(79080.0,189900.0),
    Point(79080.0,158400.0),
    Point(78870.0,158400.0),
    Point(78870.0,157500.0),
    Point(78750.0,157500.0),
    Point(78750.0,129600.0),
    Point(78240.0,129600.0),
    Point(78240.0,100800.0),
    Point(77100.0,100800.0),
    Point(75300.0,100800.0),
    Point(75300.0,104400.0),
    Point(70800.0,104400.0),
    Point(70800.0,99000.0)]],
"PYX"=>Array[Point[
    Point(30120.0,-61200.0),
    Point(30900.0,-61200.0),
    Point(30900.0,-68400.0),
    Point(32700.0,-68400.0),
    Point(32700.0,-86400.0),
    Point(33720.0,-86400.0),
    Point(33720.0,-126900.0),
    Point(30120.0,-126900.0)]],
"ARA"=>Array[Point[
    Point(59115.0,-216000.0),
    Point(59115.0,-160200.0),
    Point(64200.0,-160200.0),
    Point(64800.0,-160200.0),
    Point(64800.0,-205200.0),
    Point(63000.0,-205200.0),
    Point(63000.0,-239400.0),
    Point(61200.0,-239400.0),
    Point(60600.0,-239400.0),
    Point(60600.0,-234000.0),
    Point(60300.0,-234000.0),
    Point(60300.0,-224700.0),
    Point(59700.0,-224700.0),
    Point(59700.0,-219600.0),
    Point(59115.0,-219600.0)]],
"PSC"=>Array[Point[
    Point(81900.0,0.0),
    Point(81900.0,7200.0),
    Point(81900.0,27000.0),
    Point(85800.0,27000.0),
    Point(85800.0,45000.0),
    Point(86400.0,45000.0),
    Point(86400.0,-25200.0),
    Point(85800.0,-25200.0),
    Point(85800.0,-14400.0),
    Point(81900.0,-14400.0)],
    Point[
    Point(0.0,36000.0),
    Point(0.0,45000.0),
    Point(510.0,45000.0),
    Point(510.0,75600.0),
    Point(3060.0,75600.0),
    Point(3060.0,85500.0),
    Point(2580.0,85500.0),
    Point(2580.0,118800.0),
    Point(5070.0,118800.0),
    Point(5070.0,100800.0),
    Point(6000.0,100800.0),
    Point(6000.0,90000.0),
    Point(6000.0,35700.0),
    Point(7200.0,35700.0),
    Point(7200.0,7200.0),
    Point(1200.0,7200.0),
    Point(1200.0,0.0),
    Point(1200.0,-25200.0),
    Point(0.0,-25200.0)]],
"UMI"=>Array[Point[
    Point(0.0,324000.0),
    Point(0.0,316800.0),
    Point(33588.0,316800.0),
    Point(33588.0,311400.0),
    Point(52200.0,311400.0),
    Point(52200.0,288000.0),
    Point(48900.0,288000.0),
    Point(48900.0,277200.0),
    Point(46800.0,277200.0),
    Point(46800.0,252000.0),
    Point(50400.0,252000.0),
    Point(50400.0,237600.0),
    Point(56400.0,237600.0),
    Point(56400.0,252000.0),
    Point(59520.0,252000.0),
    Point(59520.0,270000.0),
    Point(63000.0,270000.0),
    Point(63000.0,288000.0),
    Point(64800.0,288000.0),
    Point(64800.0,309600.0),
    Point(75600.0,309600.0),
    Point(75600.0,310200.0),
    Point(82800.0,310200.0),
    Point(82800.0,316800.0),
    Point(86400.0,316800.0),
    Point(86400.0,324000.0)
    ]])