From b052e6c6b1a742d3fb99409427ac3dfb02f77505 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <piotr.gawron@uni.lu>
Date: Fri, 3 Feb 2017 15:29:35 +0100
Subject: [PATCH] overview dialog extracted to separate class

---
 .../src/main/js/gui/AbstractGuiElement.js     |  42 ++++
 frontend-js/src/main/js/gui/OverviewDialog.js | 221 ++++++++++++++++
 frontend-js/src/main/js/gui/Panel.js          |   6 +-
 .../src/main/js/map/AbstractCustomMap.js      |   2 +-
 frontend-js/src/main/js/map/ControlType.js    |   1 -
 frontend-js/src/main/js/map/CustomMap.js      | 235 ------------------
 .../src/main/js/map/CustomMapOptions.js       |   2 +-
 frontend-js/src/main/js/map/Submap.js         |   3 +-
 frontend-js/src/main/js/minerva.js            |  15 ++
 .../src/test/js/gui/OverviewDialog-test.js    |  54 ++++
 frontend-js/src/test/js/helper.js             |  18 ++
 .../src/test/js/map/AbstractCustomMap-test.js |   7 +
 .../src/test/js/map/CustomMapOptions-test.js  |  11 +
 frontend-js/src/test/js/minerva-test.js       |  19 ++
 frontend-js/testFiles/projectWithImages.json  |   1 +
 .../model/map/OverviewModelLink.java          |   8 +-
 .../api/project/ProjectMetaData.java          |  35 ---
 .../api/project/ProjectRestImpl.java          |  76 +++++-
 .../lcsb/mapviewer/api/RestTestFunctions.java |  47 +++-
 .../api/project/AllProjectTests.java          |   1 -
 .../api/project/ProjectMetaDataTest.java      |  41 ---
 .../api/project/ProjectRestImplTest.java      |  29 +++
 .../model/complex_model_with_submaps.zip      | Bin 0 -> 8240 bytes
 .../view/OverviewLinkViewFactory.java         |  11 +-
 web/src/main/webapp/index.xhtml               |  17 +-
 web/src/main/webapp/resources/css/global.css  |   7 +
 web/src/main/webapp/resources/css/style.css   |   3 -
 27 files changed, 562 insertions(+), 350 deletions(-)
 create mode 100644 frontend-js/src/main/js/gui/AbstractGuiElement.js
 create mode 100644 frontend-js/src/main/js/gui/OverviewDialog.js
 create mode 100644 frontend-js/src/test/js/gui/OverviewDialog-test.js
 create mode 100644 frontend-js/testFiles/projectWithImages.json
 delete mode 100644 rest-api/src/test/java/lcsb/mapviewer/api/project/ProjectMetaDataTest.java
 create mode 100644 rest-api/testFiles/model/complex_model_with_submaps.zip

diff --git a/frontend-js/src/main/js/gui/AbstractGuiElement.js b/frontend-js/src/main/js/gui/AbstractGuiElement.js
new file mode 100644
index 0000000000..fe83a4a46f
--- /dev/null
+++ b/frontend-js/src/main/js/gui/AbstractGuiElement.js
@@ -0,0 +1,42 @@
+"use strict";
+
+/* exported logger */
+
+var ObjectWithListeners = require('../ObjectWithListeners');
+
+var logger = require('../logger');
+
+function AbstractGuiElement(params) {
+  ObjectWithListeners.call(this, params);
+
+  var self = this;
+
+  self.setElement(params.element);
+  self.setMap(params.customMap);
+}
+
+AbstractGuiElement.prototype = Object.create(ObjectWithListeners.prototype);
+AbstractGuiElement.prototype.constructor = AbstractGuiElement;
+
+AbstractGuiElement.prototype.setMap = function(map) {
+  if (map===undefined || map===null) {
+    throw new Error("map must be defined");
+  }
+  this._map = map;
+};
+
+AbstractGuiElement.prototype.getMap = function() {
+  return this._map;
+};
+
+AbstractGuiElement.prototype.setElement = function(element) {
+  if (element === undefined || element === null) {
+    throw new Error("DOM Element must be defined");
+  }
+  this._element = element;
+};
+
+AbstractGuiElement.prototype.getElement = function() {
+  return this._element;
+};
+module.exports = AbstractGuiElement;
diff --git a/frontend-js/src/main/js/gui/OverviewDialog.js b/frontend-js/src/main/js/gui/OverviewDialog.js
new file mode 100644
index 0000000000..20245945eb
--- /dev/null
+++ b/frontend-js/src/main/js/gui/OverviewDialog.js
@@ -0,0 +1,221 @@
+"use strict";
+
+/* exported logger */
+
+var AbstractGuiElement = require('./AbstractGuiElement');
+var GuiConnector = require('../GuiConnector');
+
+var functions = require('../functions');
+var logger = require('../logger');
+
+function OverviewDialog(params) {
+  AbstractGuiElement.call(this, params);
+  var self = this;
+  $(self.getElement()).dialog({
+    autoOpen : false,
+    resizable : false,
+  });
+}
+
+OverviewDialog.prototype = Object.create(AbstractGuiElement.prototype);
+OverviewDialog.prototype.constructor = OverviewDialog;
+
+OverviewDialog.prototype.showOverview = function(overviewImageId) {
+  var self = this;
+  var map = self.getMap();
+
+  var project = map.getProject();
+
+  // resize dialog
+  var htmlTag = self.getElement();
+
+  var width = Math.floor(window.innerWidth * 2 / 3);
+  var height = Math.floor(window.innerHeight * 2 / 3);
+
+  $(self.getElement()).dialog("option", "width", width + 60);
+  $(self.getElement()).dialog("option", "height", height + 60);
+
+  // remove all child nodes from overview div
+  while (htmlTag.hasChildNodes()) {
+    htmlTag.removeChild(htmlTag.lastChild);
+  }
+
+  var content = document.createElement("div");
+  htmlTag.appendChild(content);
+
+  var canvasDebug = document.createElement("canvas");
+  canvasDebug.className = "canvasDebugClass";
+  canvasDebug.style.display = "none";
+  htmlTag.appendChild(canvasDebug);
+
+  if (overviewImageId === undefined) {
+    this.overviewImage = project.getTopOverviewImage();
+  } else {
+    this.overviewImage = null;
+    var images = project.getOverviewImages();
+    for (var i = 0; i < images.length; i++) {
+      if (images[i].idObject === overviewImageId) {
+        this.overviewImage = images[i];
+      }
+    }
+
+    if (this.overviewImage === null) {
+      logger.warn("Unknown overview image with id = " + overviewImageId);
+      this.overviewImage = project.getTopOverviewImage();
+    }
+  }
+
+  // add image to overview div
+  this.overviewImageTag = document.createElement("IMG");
+  this.overviewImageTag.src = "../map_images/" + this.overviewImage.filename;
+  content.appendChild(this.overviewImageTag);
+
+  var ratio = 1.0;
+
+  // check how image should be resized to fit dialog and resize it manually!!!
+  if (width / this.overviewImage.width > height / this.overviewImage.height) {
+    this.overviewImageTag.style.height = height + "px";
+    ratio = height / this.overviewImage.height;
+    width = this.overviewImage.width * ratio;
+    $(self.getElement()).dialog("option", "width", width + 60);
+  } else {
+    this.overviewImageTag.style.width = width + "px";
+    ratio = width / this.overviewImage.width;
+    height = this.overviewImage.height * ratio;
+    $(self.getElement()).dialog("option", "height", height + 60);
+  }
+
+  // on click event (what should happen when we click on the image)
+  var onclickevent = function getClickPosition(e) {
+    var parentPosition = functions.getPosition(e.currentTarget);
+    var xPosition = e.clientX - parentPosition.x;
+    var yPosition = e.clientY - parentPosition.y;
+
+    var imgWidth = self.overviewImageTag.offsetWidth;
+
+    var currentRatio = imgWidth / self.overviewImage.width;
+
+    var xNormal = xPosition / currentRatio;
+    var yNormal = yPosition / currentRatio;
+    var point = {
+      x : xNormal,
+      y : yNormal
+    };
+
+    var link = null;
+    for (var i = 0; i < self.overviewImage.links.length; i++) {
+      if (functions.pointInsidePolygon(point, self.overviewImage.links[i].polygon)) {
+        if (link === null) {
+          link = self.overviewImage.links[i];
+        } else {
+          logger.warn("More than one link found. Skipping");
+        }
+      }
+    }
+    if (link !== null) {
+      if (link.type === "OverviewModelLink") {
+        logger.debug("Opening model from overview. ModelId: " + link.modelLinkId);
+        logger.debug("link coordinates [" + link.idObject + "]: " + link.latLng);
+        // TODO min zoom value can be different for every map, it should be
+        // changed in the future
+        map.showModel(link.modelLinkId, link.latLng, link.zoomLevel + map.getMinZoom());
+        overviewDialog.hide();
+      } else if (link.type === "OverviewImageLink") {
+        logger.debug("Opening image from overview. ImageId: " + link.imageLinkId);
+        self.showOverview(link.imageLinkId);
+      } else if (link.type === "OverviewSearchLink") {
+        logger.debug("Sending search query. Query: " + link.query);
+        GuiConnector.search(link.query);
+        overviewDialog.hide();
+      } else {
+        logger.warn("Unknown type of link: " + link.type + ". Don't know what to do... LinkId: " + link.idObject);
+      }
+    }
+  };
+
+  this.overviewImageTag.onclick = onclickevent;
+
+  // resize canvas where on mouse over highligh will appear
+
+  // in debug mode draw clickable shapes
+  if (map.isDebug()) {
+    canvasDebug.style.display = "";
+    canvasDebug.width = width;
+    canvasDebug.height = height;
+    canvasDebug.onclick = onclickevent;
+    this.drawClickableShapes(canvasDebug, ratio);
+  }
+
+  this.overviewImage.mousePos = {
+    x : 0,
+    y : 0
+  };
+
+  // this listener should be called when mouse moves over image, it purpose is
+  // to change coursor to pointer when mouse enters clickable polygon and back
+  // to normal when mouse leaves such region
+  var onmousemove = function getMouseOverPosition(e) {
+    var position = functions.getPosition(e.currentTarget);
+    position.x = e.clientX - position.x;
+    position.y = e.clientY - position.y;
+
+    var imgWidth = self.overviewImageTag.offsetWidth;
+
+    var currentRatio = imgWidth / self.overviewImage.width;
+
+    var xNormal = position.x / currentRatio;
+    var yNormal = position.y / currentRatio;
+    var point = {
+      x : xNormal,
+      y : yNormal
+    };
+
+    if (self.overviewImage.mousePos.x !== position.x || self.overviewImage.mousePos.y !== position.y) {
+      self.overviewImage.mousePos = position;
+      var link = null;
+      for (var i = 0; i < self.overviewImage.links.length; i++) {
+        if (functions.pointInsidePolygon(point, self.overviewImage.links[i].polygon)) {
+          link = self.overviewImage.links[i];
+        }
+      }
+      if (link === null) {
+        e.currentTarget.style.cursor = "auto";
+      } else {
+        e.currentTarget.style.cursor = "pointer";
+      }
+    }
+  };
+
+  // onmousemove listener should be assigned to canvas (which is on top of the
+  // image) and overviewimage (just in case something went wrong with resizing
+  // canvas)
+  canvasDebug.onmousemove = onmousemove;
+  this.overviewImageTag.onmousemove = onmousemove;
+
+  $(self.getElement()).dialog("open");
+};
+
+OverviewDialog.prototype.drawClickableShapes = function(canvas, ratio) {
+  var ctx = canvas.getContext("2d");
+  // clear canvas
+  ctx.clearRect(0, 0, canvas.width, canvas.height);
+  for (var i = 0; i < this.overviewImage.links.length; i++) {
+    ctx.beginPath();
+    var polygon = this.overviewImage.links[i].polygon;
+    for (var j = 0; j < polygon.length; j++) {
+      var x = polygon[j].x * ratio;
+      var y = polygon[j].y * ratio;
+      ctx.moveTo(x, y);
+      x = polygon[(j + 1) % polygon.length].x * ratio;
+      y = polygon[(j + 1) % polygon.length].y * ratio;
+      ctx.lineTo(x, y);
+    }
+    ctx.stroke();
+  }
+};
+
+OverviewDialog.prototype.destroy = function() {
+  $(this.getElement()).dialog("destroy");
+};
+
+module.exports = OverviewDialog;
diff --git a/frontend-js/src/main/js/gui/Panel.js b/frontend-js/src/main/js/gui/Panel.js
index 4d0f5e111d..370fcb4096 100644
--- a/frontend-js/src/main/js/gui/Panel.js
+++ b/frontend-js/src/main/js/gui/Panel.js
@@ -3,12 +3,12 @@
 /* exported logger */
 
 var GuiConnector = require('../GuiConnector');
-var ObjectWithListeners = require('../ObjectWithListeners');
+var AbstractGuiElement = require('./AbstractGuiElement');
 
 var logger = require('../logger');
 
 function Panel(params) {
-  ObjectWithListeners.call(this, params);
+  AbstractGuiElement.call(this, params);
 
   var self = this;
 
@@ -18,7 +18,7 @@ function Panel(params) {
 
 }
 
-Panel.prototype = Object.create(ObjectWithListeners.prototype);
+Panel.prototype = Object.create(AbstractGuiElement.prototype);
 Panel.prototype.constructor = Panel;
 
 Panel.prototype.disablePanel = function(message) {
diff --git a/frontend-js/src/main/js/map/AbstractCustomMap.js b/frontend-js/src/main/js/map/AbstractCustomMap.js
index fda3320aa4..e91fee19c9 100644
--- a/frontend-js/src/main/js/map/AbstractCustomMap.js
+++ b/frontend-js/src/main/js/map/AbstractCustomMap.js
@@ -963,7 +963,7 @@ AbstractCustomMap.prototype.setDebug = function(debug) {
 };
 
 AbstractCustomMap.prototype.isDebug = function() {
-  return this.debug === true;
+  return this._debug === true;
 };
 
 AbstractCustomMap.prototype.getTopLeftLatLng = function() {
diff --git a/frontend-js/src/main/js/map/ControlType.js b/frontend-js/src/main/js/map/ControlType.js
index 2612e7d1b8..a6b80995c1 100644
--- a/frontend-js/src/main/js/map/ControlType.js
+++ b/frontend-js/src/main/js/map/ControlType.js
@@ -1,7 +1,6 @@
 "use strict";
 
 var ControlType = {
-  COMMENT_CHECKBOX : "COMMENT_CHECKBOX",
   SUBMAP_DIALOGS : "SUBMAP_DIALOGS",
   LOGO_IMG : "LOGO_IMG",
   LOGO_2_IMG : "LOGO_2_IMG",
diff --git a/frontend-js/src/main/js/map/CustomMap.js b/frontend-js/src/main/js/map/CustomMap.js
index 322dee81b1..1bbcea3e86 100644
--- a/frontend-js/src/main/js/map/CustomMap.js
+++ b/frontend-js/src/main/js/map/CustomMap.js
@@ -45,10 +45,6 @@ function CustomMap(options) {
 
   this.customizeGoogleMapView(options.getMapDiv());
 
-  this.createBelt();
-  
-  this.createMapMenu();
-  
   this.createMapChangedCallbacks();
 
   this.overlayCollections = [];
@@ -127,15 +123,6 @@ CustomMap.prototype.createLogo = function() {
   this.getGoogleMap().controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(logoControlDiv);
 };
 
-CustomMap.prototype.createBelt = function() {
-  var self = this;
-
-  this.divBelt = document.createElement('DIV');
-  this.divBelt.className = "headerBelt";
-
-  this.getGoogleMap().controls[google.maps.ControlPosition.TOP_LEFT].push(this.divBelt);
-};
-
 CustomMap.prototype.clearOverlays = function() {
   for ( var overlayName in this.overlayCollections) {
     if (this.overlayCollections.hasOwnProperty(overlayName)) {
@@ -271,25 +258,6 @@ CustomMap.prototype.openLayoutByName = function(name) {
   }
 };
 
-CustomMap.prototype.createMapMenu = function() {
-  var selfMap = this;
-
-  // create a button for overview images when the image is available
-  if (this.getTopOverviewImage() !== undefined && this.getTopOverviewImage() !== null) {
-    var submenuButtonDiv2 = document.createElement('button');
-    submenuButtonDiv2.id = "overview_button";
-    submenuButtonDiv2.innerHTML = "<i class='fa fa-sitemap' style='font-size:18px; font-weight:400; padding-right:10px;'></i> SHOW OVERVIEW";
-    submenuButtonDiv2.className = "overview_button";
-    submenuButtonDiv2.onclick = (function() {
-      return function() {
-        selfMap.showOverview();
-        return false;
-      };
-    })();
-    this.divBelt.appendChild(submenuButtonDiv2);
-  }
-};
-
 CustomMap.prototype.registerSource = function(overlayCollection) {
   var self = this;
 
@@ -569,209 +537,6 @@ CustomMap.prototype.removeSelection = function() {
   }
 };
 
-/**
- * This method will hide google map view and will present single image overview
- * of the data.
- */
-CustomMap.prototype.showOverview = function(overviewImageId) {
-  var overviewDialog = GuiConnector.getOverviewDialog();
-  overviewDialog.syncWindowResize();
-  if (this.getOverviewDiv() === undefined) {
-    logger.warn("Cannot show overview, because overview div is  undefined");
-  } else {
-    logger.debug("Show overview");
-    overviewDialog.show();
-
-    // resize dialog
-    var htmlTag = GuiConnector.getOverviewHtmlTag();
-
-    var width = Math.floor(window.innerWidth * 2 / 3);
-    var height = Math.floor(window.innerHeight * 2 / 3);
-
-    htmlTag.style.height = (height + 50) + "px";
-    htmlTag.style.width = (width + 20) + "px";
-
-    var self = this;
-
-    // remove all child nodes from overview div
-    while (this.getOverviewDiv().hasChildNodes()) {
-      this.getOverviewDiv().removeChild(this.getOverviewDiv().lastChild);
-    }
-
-    if (overviewImageId === undefined || overviewImageId === null) {
-      this.overviewImage = this.getConfiguration().TOP_OVERVIEW_IMAGE;
-    } else {
-      this.overviewImage = null;
-      for (var i = 0; i < this.getConfiguration().OVERVIEW_IMAGES.length; i++) {
-        if (this.getConfiguration().OVERVIEW_IMAGES[i].idObject === overviewImageId) {
-          this.overviewImage = this.getConfiguration().OVERVIEW_IMAGES[i];
-        }
-      }
-
-      if (this.overviewImage === null) {
-        logger.warn("Unknown overview image with id = " + overviewImageId);
-        this.overviewImage = this.getConfiguration().TOP_OVERVIEW_IMAGE;
-      }
-    }
-
-    // add image to overview div
-    this.overviewImageTag = document.createElement("IMG");
-    this.overviewImageTag.src = "../map_images/" + this.overviewImage.filename;
-    this.getOverviewDiv().appendChild(this.overviewImageTag);
-
-    var ratio = 1.0;
-
-    // check how image should be resized to fit dialog and resize it manually!!!
-    if (width / this.overviewImage.width > height / this.overviewImage.height) {
-      this.overviewImageTag.style.height = height + "px";
-      ratio = height / this.overviewImage.height;
-      width = this.overviewImage.width * ratio;
-
-      htmlTag.style.width = (width + 20) + "px";
-    } else {
-      this.overviewImageTag.style.width = width + "px";
-      ratio = width / this.overviewImage.width;
-      height = this.overviewImage.height * ratio;
-
-      htmlTag.style.height = (height + 50) + "px";
-    }
-
-    // center dialog
-    overviewDialog.jq.css("top", Math.max(0, (($(window).height() - overviewDialog.jq.outerHeight()) / 2)
-        + $(window).scrollTop())
-        + "px");
-    overviewDialog.jq.css("left", Math.max(0, (($(window).width() - overviewDialog.jq.outerWidth()) / 2)
-        + $(window).scrollLeft())
-        + "px");
-
-    // on click event (what should happen when we click on the image)
-    var onclickevent = function getClickPosition(e) {
-      var parentPosition = functions.getPosition(e.currentTarget);
-      var xPosition = e.clientX - parentPosition.x;
-      var yPosition = e.clientY - parentPosition.y;
-
-      var imgWidth = self.overviewImageTag.offsetWidth;
-
-      var currentRatio = imgWidth / self.overviewImage.width;
-
-      var xNormal = xPosition / currentRatio;
-      var yNormal = yPosition / currentRatio;
-      var point = {
-        x : xNormal,
-        y : yNormal
-      };
-
-      var link = null;
-      for (var i = 0; i < self.overviewImage.links.length; i++) {
-        if (functions.pointInsidePolygon(point, self.overviewImage.links[i].polygon)) {
-          if (link === null) {
-            link = self.overviewImage.links[i];
-          } else {
-            logger.warn("More than one link found. Skipping");
-          }
-        }
-      }
-      if (link !== null) {
-        if (link.type === "OverviewModelLink") {
-          logger.debug("Opening model from overview. ModelId: " + link.modelLinkId);
-          logger.debug("link coordinates [" + link.idObject + "]: " + link.latLng);
-          // TODO min zoom value can be different for every map, it should be
-          // changed in the future
-          self.showModel(link.modelLinkId, link.latLng, link.zoomLevel + self.getConfiguration().MIN_ZOOM);
-          overviewDialog.hide();
-        } else if (link.type === "OverviewImageLink") {
-          logger.debug("Opening image from overview. ImageId: " + link.imageLinkId);
-          self.showOverview(link.imageLinkId);
-        } else if (link.type === "OverviewSearchLink") {
-          logger.debug("Sending search query. Query: " + link.query);
-          GuiConnector.search(link.query);
-          overviewDialog.hide();
-        } else {
-          logger.warn("Unknown type of link: " + link.type + ". Don't know what to do... LinkId: " + link.idObject);
-        }
-      }
-    };
-
-    this.overviewImageTag.onclick = onclickevent;
-
-    // resize canvas where on mouse over highligh will appear
-    var canvas = document.getElementById("canvasDebug");
-    canvas.width = width;
-    canvas.height = height;
-    canvas.onclick = onclickevent;
-
-    // in debug mode draw clickable shapes
-    if (this.isDebug()) {
-      this.drawClickableShapes(canvas, ratio);
-    }
-
-    this.overviewImage.mousePos = {
-      x : 0,
-      y : 0
-    };
-
-    // this listener should be called when mouse moves over image, it purpose is
-    // to change coursor to pointer when mouse enters clickable polygon and back
-    // to normal when mouse leaves such region
-    var onmousemove = function getMouseOverPosition(e) {
-      var position = functions.getPosition(e.currentTarget);
-      position.x = e.clientX - position.x;
-      position.y = e.clientY - position.y;
-
-      var imgWidth = self.overviewImageTag.offsetWidth;
-
-      var currentRatio = imgWidth / self.overviewImage.width;
-
-      var xNormal = position.x / currentRatio;
-      var yNormal = position.y / currentRatio;
-      var point = {
-        x : xNormal,
-        y : yNormal
-      };
-
-      if (self.overviewImage.mousePos.x !== position.x || self.overviewImage.mousePos.y !== position.y) {
-        self.overviewImage.mousePos = position;
-        var link = null;
-        for (var i = 0; i < self.overviewImage.links.length; i++) {
-          if (functions.pointInsidePolygon(point, self.overviewImage.links[i].polygon)) {
-            link = self.overviewImage.links[i];
-          }
-        }
-        if (link === null) {
-          e.currentTarget.style.cursor = "auto";
-        } else {
-          e.currentTarget.style.cursor = "pointer";
-        }
-      }
-    };
-
-    // onmousemove listener should be assigned to canvas (which is on top of the
-    // image) and overviewimage (just in case something went wrong with resizing
-    // canvas)
-    canvas.onmousemove = onmousemove;
-    this.overviewImageTag.onmousemove = onmousemove;
-  }
-};
-
-CustomMap.prototype.drawClickableShapes = function(canvas, ratio) {
-  var ctx = canvas.getContext("2d");
-  // clear canvas
-  ctx.clearRect(0, 0, canvas.width, canvas.height);
-  for (var i = 0; i < this.overviewImage.links.length; i++) {
-    ctx.beginPath();
-    var polygon = this.overviewImage.links[i].polygon;
-    for (var j = 0; j < polygon.length; j++) {
-      var x = polygon[j].x * ratio;
-      var y = polygon[j].y * ratio;
-      ctx.moveTo(x, y);
-      x = polygon[(j + 1) % polygon.length].x * ratio;
-      y = polygon[(j + 1) % polygon.length].y * ratio;
-      ctx.lineTo(x, y);
-    }
-    ctx.stroke();
-  }
-};
-
 CustomMap.prototype.showModel = function(id, point, zoomLevel) {
   if (point !== undefined) {
     this.setCenter(id, point);
diff --git a/frontend-js/src/main/js/map/CustomMapOptions.js b/frontend-js/src/main/js/map/CustomMapOptions.js
index 6a16eb0a6a..262e7748ad 100644
--- a/frontend-js/src/main/js/map/CustomMapOptions.js
+++ b/frontend-js/src/main/js/map/CustomMapOptions.js
@@ -110,7 +110,7 @@ CustomMapOptions.prototype.setMapDiv = function(mapDiv) {
 };
 
 CustomMapOptions.prototype.isDebug = function() {
-  return this.debug === true;
+  return this._debug === true;
 };
 
 module.exports = CustomMapOptions;
diff --git a/frontend-js/src/main/js/map/Submap.js b/frontend-js/src/main/js/map/Submap.js
index f24f4a01e3..18c3b3e7d6 100644
--- a/frontend-js/src/main/js/map/Submap.js
+++ b/frontend-js/src/main/js/map/Submap.js
@@ -24,7 +24,8 @@ function Submap(customMap, model) {
     markerOptimization : customMap.isMarkerOptimization(),
     bigLogo : customMap.isBigLogo(),
     customTouchInterface : customMap.isCustomTouchInterface(),
-    project : null
+    project : null,
+    debug: customMap.isDebug()
   }));
 
   this.initialized = false;
diff --git a/frontend-js/src/main/js/minerva.js b/frontend-js/src/main/js/minerva.js
index 1c463a5d8d..5bdc7b08a3 100644
--- a/frontend-js/src/main/js/minerva.js
+++ b/frontend-js/src/main/js/minerva.js
@@ -14,6 +14,7 @@ var DrugPanel = require('./gui/DrugPanel');
 var MiRnaDbOverlay = require('./map/overlay/MiRnaDbOverlay');
 var MiRnaPanel = require('./gui/MiRnaPanel');
 var OverlayPanel = require('./gui/OverlayPanel');
+var OverviewDialog = require('./gui/OverviewDialog');
 var SearchDbOverlay = require('./map/overlay/SearchDbOverlay');
 var SearchPanel = require('./gui/SearchPanel');
 var SubmapPanel = require('./gui/SubmapPanel');
@@ -292,6 +293,20 @@ function create(params) {
         };
       })();
       
+      if (project.getTopOverviewImage()!== undefined && project.getTopOverviewImage()!== null) {
+        var overviewDialog = new OverviewDialog({
+          customMap: result,
+          element: document.getElementsByName("overviewDialog")[0]
+        });
+        var showOverviewButton = document.getElementsByName("showOverviewButton")[0];
+        showOverviewButton.onclick = (function() {
+          return function() {
+            overviewDialog.showOverview();
+            return false;
+          };
+        })();
+        showOverviewButton.style.display="";
+      }
 
       if (ServerConnector.getSessionData().getShowComments()) {
         result.getControl(ControlType.COMMENT_CHECKBOX).checked=true;
diff --git a/frontend-js/src/test/js/gui/OverviewDialog-test.js b/frontend-js/src/test/js/gui/OverviewDialog-test.js
new file mode 100644
index 0000000000..3168c97275
--- /dev/null
+++ b/frontend-js/src/test/js/gui/OverviewDialog-test.js
@@ -0,0 +1,54 @@
+"use strict";
+
+var Helper = require('../helper');
+
+require("../mocha-config.js");
+
+var CustomMap = require('../../../main/js/map/CustomMap');
+var OverviewDialog = require('../../../main/js/gui/OverviewDialog');
+var Project = require('../../../main/js/map/data/Project');
+
+var chai = require('chai');
+var assert = chai.assert;
+var logger = require('../logger');
+
+describe('OverviewDialog', function() {
+
+  var helper;
+  before(function() {
+    helper = new Helper();
+  });
+
+  it('open image', function() {
+    return ServerConnector.readFile("testFiles/projectWithImages.json").then(function(fileContent) {
+      var project = new Project(JSON.parse(fileContent));
+      var options = helper.createOptions(project);
+      var map = new CustomMap(options);
+
+      var dialog = new OverviewDialog({
+        element : testDiv,
+        customMap : map
+      });
+
+      dialog.showOverview(project.getOverviewImages()[1].idObject);
+      dialog.destroy();
+    });
+  });
+
+  it('open invalid image', function() {
+    return ServerConnector.readFile("testFiles/projectWithImages.json").then(function(fileContent) {
+      var project = new Project(JSON.parse(fileContent));
+      var options = helper.createOptions(project);
+      var map = new CustomMap(options);
+
+      var dialog = new OverviewDialog({
+        element : testDiv,
+        customMap : map
+      });
+
+      dialog.showOverview(-1123);
+      dialog.destroy();
+    });
+  });
+
+});
diff --git a/frontend-js/src/test/js/helper.js b/frontend-js/src/test/js/helper.js
index 18d101d871..9990242cb1 100644
--- a/frontend-js/src/test/js/helper.js
+++ b/frontend-js/src/test/js/helper.js
@@ -65,6 +65,24 @@ Helper.prototype.createMenuDiv = function() {
   clearButton.setAttribute("name", "clearButton");
   result.appendChild(clearButton);
 
+  var showOverviewButton = document.createElement("button");
+  showOverviewButton.setAttribute("name", "showOverviewButton");
+  result.appendChild(showOverviewButton);
+
+  
+  return result;
+};
+
+Helper.prototype.createDialogsDiv = function() {
+  var result = document.createElement("div");
+  result.setAttribute("name", "dialogs");
+  result.appendChild(this.createOverviewDialogDiv());
+  return result;
+};
+
+Helper.prototype.createOverviewDialogDiv = function() {
+  var result = document.createElement("div");
+  result.setAttribute("name", "overviewDialog");
   return result;
 };
 
diff --git a/frontend-js/src/test/js/map/AbstractCustomMap-test.js b/frontend-js/src/test/js/map/AbstractCustomMap-test.js
index 50bfabb18b..47e295e032 100644
--- a/frontend-js/src/test/js/map/AbstractCustomMap-test.js
+++ b/frontend-js/src/test/js/map/AbstractCustomMap-test.js
@@ -368,4 +368,11 @@ describe('AbstractCustomMap', function() {
     });
 
   });
+
+  it("getDebug", function() {
+    var map = helper.createAbstractCustomMap();
+    map.setDebug(true);
+    assert.ok(map.isDebug());
+  });
+
 });
diff --git a/frontend-js/src/test/js/map/CustomMapOptions-test.js b/frontend-js/src/test/js/map/CustomMapOptions-test.js
index 42060a7a44..e5e6a1243e 100644
--- a/frontend-js/src/test/js/map/CustomMapOptions-test.js
+++ b/frontend-js/src/test/js/map/CustomMapOptions-test.js
@@ -48,4 +48,15 @@ describe('CustomMapOptions', function() {
     assert.equal(0, logger.getErrors().length);
     assert.equal(0, logger.getWarnings().length);
   });
+  
+  it("getDebug", function() {
+    var project = helper.createProject();
+    var options = new CustomMapOptions({
+      mapDiv : testDiv,
+      project : project,
+      debug : true,
+    });
+
+    assert.ok(options.isDebug());
+  });
 });
diff --git a/frontend-js/src/test/js/minerva-test.js b/frontend-js/src/test/js/minerva-test.js
index 43ee593d93..8a3499805c 100644
--- a/frontend-js/src/test/js/minerva-test.js
+++ b/frontend-js/src/test/js/minerva-test.js
@@ -5,6 +5,7 @@ var Helper = require('./helper');
 require("./mocha-config.js");
 
 var minerva = require('../../main/js/minerva');
+var Project = require('../../main/js/map/data/Project');
 
 var chai = require('chai');
 var assert = chai.assert;
@@ -23,11 +24,15 @@ describe('minerva global', function() {
 
     global.menuDiv = helper.createMenuDiv();
     document.body.appendChild(global.menuDiv);
+    
+    global.dialogsDiv = helper.createDialogsDiv();
+    document.body.appendChild(global.dialogsDiv);
   });
 
   afterEach(function() {
     document.body.removeChild(global.leftPanelTab);
     document.body.removeChild(global.menuDiv);
+    document.body.removeChild(global.dialogsDiv);
   });
 
   it('create', function() {
@@ -39,6 +44,20 @@ describe('minerva global', function() {
     });
   });
 
+  it('create with overview', function() {
+    return ServerConnector.readFile("testFiles/projectWithImages.json").then(function(fileContent) {
+      var project = new Project(JSON.parse(fileContent));
+      var options = helper.createOptions(project);
+
+      return minerva.create(options);
+    }).then(function(result) {
+      assert.ok(result);
+      assert.equal(logger.getWarnings().length, 1);
+      var showOverviewButton = document.getElementsByName("showOverviewButton")[0];
+      showOverviewButton.onclick();
+    });
+  });
+
   it("showComments", function() {
     var options = helper.createCustomMapOptions();
     return minerva.create(options).then(function() {
diff --git a/frontend-js/testFiles/projectWithImages.json b/frontend-js/testFiles/projectWithImages.json
new file mode 100644
index 0000000000..b989ec48df
--- /dev/null
+++ b/frontend-js/testFiles/projectWithImages.json
@@ -0,0 +1 @@
+{"version":"0","idObject":18115,"name":"UNKNOWN DISEASE MAP","projectId":"complex_model_with_images","description":"","map":{"name":"main","idObject":19397,"tileSize":256,"width":495,"height":357,"minZoom":2,"maxZoom":3,"layouts":[{"modelId":19397,"name":"Pathways and compartments","status":"Not available","progress":"0.00","directory":"fbdbe43de73fe38f62889b89cb863adb/_nested0","creator":"","inputDataAvailable":"false","idObject":17987},{"modelId":19397,"name":"Network","status":"Not available","progress":"0.00","directory":"fbdbe43de73fe38f62889b89cb863adb/_normal0","creator":"","inputDataAvailable":"false","idObject":17988},{"modelId":19397,"name":"Empty","status":"Not available","progress":"0.00","directory":"fbdbe43de73fe38f62889b89cb863adb/_empty0","creator":"","inputDataAvailable":"false","idObject":17989}],"submodels":[{"name":"s2","idObject":19400,"tileSize":256,"width":451,"height":253,"minZoom":2,"maxZoom":3,"layouts":[{"modelId":19400,"name":"Pathways and compartments","status":"Not available","progress":"0.00","directory":"fbdbe43de73fe38f62889b89cb863adb/_nested2","creator":"","inputDataAvailable":"false","idObject":17984},{"modelId":19400,"name":"Network","status":"Not available","progress":"0.00","directory":"fbdbe43de73fe38f62889b89cb863adb/_normal2","creator":"","inputDataAvailable":"false","idObject":17985},{"modelId":19400,"name":"Empty","status":"Not available","progress":"0.00","directory":"fbdbe43de73fe38f62889b89cb863adb/_empty2","creator":"","inputDataAvailable":"false","idObject":17986}],"submodels":[],"centerLatLng":{"lat":79.19006423440219,"lng":-135.09977827050997},"topLeftLatLng":{"lat":85.05112877980659,"lng":-180.0},"bottomRightLatLng":{"lat":78.0903352323621,"lng":-90.0}},{"name":"s3","idObject":19399,"tileSize":256,"width":421,"height":315,"minZoom":2,"maxZoom":3,"layouts":[{"modelId":19399,"name":"Pathways and compartments","status":"Not available","progress":"0.00","directory":"fbdbe43de73fe38f62889b89cb863adb/_nested3","creator":"","inputDataAvailable":"false","idObject":17978},{"modelId":19399,"name":"Network","status":"Not available","progress":"0.00","directory":"fbdbe43de73fe38f62889b89cb863adb/_normal3","creator":"","inputDataAvailable":"false","idObject":17979},{"modelId":19399,"name":"Empty","status":"Not available","progress":"0.00","directory":"fbdbe43de73fe38f62889b89cb863adb/_empty3","creator":"","inputDataAvailable":"false","idObject":17980}],"submodels":[],"centerLatLng":{"lat":79.19139766235872,"lng":-135.10688836104512},"topLeftLatLng":{"lat":85.05112877980659,"lng":-180.0},"bottomRightLatLng":{"lat":74.06362505154212,"lng":-90.0}},{"name":"s1","idObject":19398,"tileSize":256,"width":571,"height":276,"minZoom":2,"maxZoom":4,"layouts":[{"modelId":19398,"name":"Pathways and compartments","status":"Not available","progress":"0.00","directory":"fbdbe43de73fe38f62889b89cb863adb/_nested1","creator":"","inputDataAvailable":"false","idObject":17981},{"modelId":19398,"name":"Network","status":"Not available","progress":"0.00","directory":"fbdbe43de73fe38f62889b89cb863adb/_normal1","creator":"","inputDataAvailable":"false","idObject":17982},{"modelId":19398,"name":"Empty","status":"Not available","progress":"0.00","directory":"fbdbe43de73fe38f62889b89cb863adb/_empty1","creator":"","inputDataAvailable":"false","idObject":17983}],"submodels":[],"centerLatLng":{"lat":79.18613072613702,"lng":-135.07880910683014},"topLeftLatLng":{"lat":85.05112877980659,"lng":-180.0},"bottomRightLatLng":{"lat":79.44906929997262,"lng":-90.0}}],"centerLatLng":{"lat":79.18840067864828,"lng":-135.0909090909091},"topLeftLatLng":{"lat":85.05112877980659,"lng":-180.0},"bottomRightLatLng":{"lat":74.71754541589858,"lng":-90.0}},"overviewImageViews":[{"filename":"fbdbe43de73fe38f62889b89cb863adb/sub_image.png","width":963,"height":639,"links":[{"polygon":[{"x":200.0,"y":200.0},{"x":200.0,"y":400.0},{"x":400.0,"y":400.0},{"x":400.0,"y":200.0}],"zoomLevel":1,"latLng":{"lat":84.89177465079632,"lng":-161.8181818181818},"modelLinkId":19397,"type":"OverviewModelLink","idObject":2369}],"idObject":1261},{"filename":"fbdbe43de73fe38f62889b89cb863adb/test.png","width":963,"height":639,"links":[{"polygon":[{"x":10.0,"y":10.0},{"x":100.0,"y":10.0},{"x":100.0,"y":100.0},{"x":10.0,"y":100.0}],"imageLinkId":1261,"type":"OverviewImageLink","idObject":2370},{"polygon":[{"x":200.0,"y":200.0},{"x":200.0,"y":400.0},{"x":400.0,"y":400.0},{"x":400.0,"y":200.0}],"zoomLevel":0,"latLng":{"lat":84.89177465079632,"lng":-178.1818181818182},"modelLinkId":19397,"type":"OverviewModelLink","idObject":2371}],"idObject":1262}],"topOverviewImage":{"filename":"fbdbe43de73fe38f62889b89cb863adb/test.png","width":963,"height":639,"links":[{"polygon":[{"x":10.0,"y":10.0},{"x":100.0,"y":10.0},{"x":100.0,"y":100.0},{"x":10.0,"y":100.0}],"imageLinkId":1261,"type":"OverviewImageLink","idObject":2370},{"polygon":[{"x":200.0,"y":200.0},{"x":200.0,"y":400.0},{"x":400.0,"y":400.0},{"x":400.0,"y":200.0}],"zoomLevel":0,"latLng":{"lat":84.89177465079632,"lng":-178.1818181818182},"modelLinkId":19397,"type":"OverviewModelLink","idObject":2371}],"idObject":1262}}
\ No newline at end of file
diff --git a/model/src/main/java/lcsb/mapviewer/model/map/OverviewModelLink.java b/model/src/main/java/lcsb/mapviewer/model/map/OverviewModelLink.java
index 60589e09f1..b3ecb4f642 100644
--- a/model/src/main/java/lcsb/mapviewer/model/map/OverviewModelLink.java
+++ b/model/src/main/java/lcsb/mapviewer/model/map/OverviewModelLink.java
@@ -71,12 +71,8 @@ public class OverviewModelLink extends OverviewLink {
 	 * @return the model
 	 * @see #linkedModel
 	 */
-	public Model getLinkedModel() {
-		if (linkedModel != null) {
-			return linkedModel.getModel();
-		} else {
-			return null;
-		}
+	public ModelData getLinkedModel() {
+		return linkedModel;
 	}
 
 	/**
diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/project/ProjectMetaData.java b/rest-api/src/main/java/lcsb/mapviewer/api/project/ProjectMetaData.java
index 6f8f5fcc44..090b6d0dc1 100644
--- a/rest-api/src/main/java/lcsb/mapviewer/api/project/ProjectMetaData.java
+++ b/rest-api/src/main/java/lcsb/mapviewer/api/project/ProjectMetaData.java
@@ -69,41 +69,6 @@ public class ProjectMetaData implements Serializable {
 	protected ProjectMetaData() {
 	}
 
-	public ProjectMetaData(Project project) {
-		OverviewImageViewFactory factory = new OverviewImageViewFactory();
-		ModelData model = null;
-		if (project.getModels().size() > 0) {
-			model = project.getModels().iterator().next();
-		}
-
-		this.setName(project.getName());
-		this.setProjectId(project.getProjectId());
-		this.setIdObject(project.getId());
-
-		if (model != null) {
-			this.setOverviewImageViews(factory.createList(model.getOverviewImages()));
-			this.setVersion(model.getMapVersion());
-			this.setDescription(model.getNotes());
-
-			Set<OverviewImage> set = new HashSet<OverviewImage>();
-			set.addAll(model.getOverviewImages());
-			for (OverviewImage image : model.getOverviewImages()) {
-				for (OverviewLink ol : image.getLinks()) {
-					if (ol instanceof OverviewImageLink) {
-						set.remove(((OverviewImageLink) ol).getLinkedOverviewImage());
-					}
-				}
-			}
-			if (set.size() > 0) {
-				this.setTopOverviewImage(factory.create(set.iterator().next()));
-			} else if (model.getOverviewImages().size() > 0) {
-				logger.warn("Cannot determine top level image. Taking first one. " + model.getOverviewImages().get(0).getFilename());
-				this.setTopOverviewImage(factory.create(model.getOverviewImages().get(0)));
-			}
-			this.setMap(new ModelMetaData(model));
-
-		}
-	}
 
 	/**
 	 * @return the version
diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/project/ProjectRestImpl.java b/rest-api/src/main/java/lcsb/mapviewer/api/project/ProjectRestImpl.java
index 0588b6d4ce..27157aa2be 100644
--- a/rest-api/src/main/java/lcsb/mapviewer/api/project/ProjectRestImpl.java
+++ b/rest-api/src/main/java/lcsb/mapviewer/api/project/ProjectRestImpl.java
@@ -17,7 +17,11 @@ import org.springframework.web.bind.annotation.RequestParam;
 
 import lcsb.mapviewer.common.exception.InvalidStateException;
 import lcsb.mapviewer.model.Project;
+import lcsb.mapviewer.model.map.OverviewImage;
+import lcsb.mapviewer.model.map.OverviewImageLink;
+import lcsb.mapviewer.model.map.OverviewLink;
 import lcsb.mapviewer.model.map.model.Model;
+import lcsb.mapviewer.model.map.model.ModelData;
 import lcsb.mapviewer.model.map.reaction.Modifier;
 import lcsb.mapviewer.model.map.reaction.Product;
 import lcsb.mapviewer.model.map.reaction.Reactant;
@@ -32,31 +36,35 @@ import lcsb.mapviewer.services.interfaces.IUserService;
 import lcsb.mapviewer.services.search.data.ElementIdentifier.ElementIdentifierType;
 import lcsb.mapviewer.services.search.data.LightReactionView;
 import lcsb.mapviewer.services.view.AnnotationViewFactory;
+import lcsb.mapviewer.services.view.OverviewImageViewFactory;
 
 @Transactional(value = "txManager")
 public class ProjectRestImpl {
-	Logger									logger = Logger.getLogger(ProjectRestImpl.class);
+	Logger													 logger	= Logger.getLogger(ProjectRestImpl.class);
 
 	@Autowired
-	private IUserService		userService;
+	private IUserService						 userService;
 
 	@Autowired
-	private IProjectService	projectService;
+	private IProjectService					 projectService;
 
 	@Autowired
-	private IModelService		modelService;
+	private IModelService						 modelService;
 
 	@Autowired
-	private ISearchService	searchService;
+	private ISearchService					 searchService;
 
 	@Autowired
-	AnnotationViewFactory		annotationViewFactory;
+	AnnotationViewFactory						 annotationViewFactory;
+
+	@Autowired
+	private OverviewImageViewFactory factory;
 
 	public ProjectMetaData getMetaData(@RequestParam(value = "projectId") String projectId, @RequestParam(value = "token") String token)
 			throws SecurityException {
 
 		Project project = projectService.getProjectByProjectId(projectId, userService.getToken(token));
-		ProjectMetaData result = new ProjectMetaData(project);
+		ProjectMetaData result = createData(project);
 		if (project.getOrganism() != null) {
 			result.setOrganism(annotationViewFactory.create(project.getOrganism()));
 		}
@@ -66,6 +74,43 @@ public class ProjectRestImpl {
 		return result;
 	}
 
+	private ProjectMetaData createData(Project project) {
+		ProjectMetaData result = new ProjectMetaData();
+		ModelData model = null;
+		if (project.getModels().size() > 0) {
+			model = project.getModels().iterator().next();
+		}
+
+		result.setName(project.getName());
+		result.setProjectId(project.getProjectId());
+		result.setIdObject(project.getId());
+
+		if (model != null) {
+			result.setOverviewImageViews(factory.createList(model.getOverviewImages()));
+			result.setVersion(model.getMapVersion());
+			result.setDescription(model.getNotes());
+
+			Set<OverviewImage> set = new HashSet<OverviewImage>();
+			set.addAll(model.getOverviewImages());
+			for (OverviewImage image : model.getOverviewImages()) {
+				for (OverviewLink ol : image.getLinks()) {
+					if (ol instanceof OverviewImageLink) {
+						set.remove(((OverviewImageLink) ol).getLinkedOverviewImage());
+					}
+				}
+			}
+			if (set.size() > 0) {
+				result.setTopOverviewImage(factory.create(set.iterator().next()));
+			} else if (model.getOverviewImages().size() > 0) {
+				logger.warn("Cannot determine top level image. Taking first one. " + model.getOverviewImages().get(0).getFilename());
+				result.setTopOverviewImage(factory.create(model.getOverviewImages().get(0)));
+			}
+			result.setMap(new ModelMetaData(model));
+		}
+		
+		return result;
+	}
+
 	/**
 	 * @return the userService
 	 * @see #userService
@@ -355,4 +400,21 @@ public class ProjectRestImpl {
 		return searchService.getSuggestedQueryList(model);
 	}
 
+	/**
+	 * @return the factory
+	 * @see #factory
+	 */
+	public OverviewImageViewFactory getFactory() {
+		return factory;
+	}
+
+	/**
+	 * @param factory
+	 *          the factory to set
+	 * @see #factory
+	 */
+	public void setFactory(OverviewImageViewFactory factory) {
+		this.factory = factory;
+	}
+
 }
diff --git a/rest-api/src/test/java/lcsb/mapviewer/api/RestTestFunctions.java b/rest-api/src/test/java/lcsb/mapviewer/api/RestTestFunctions.java
index 336cd7a914..52f66532ff 100644
--- a/rest-api/src/test/java/lcsb/mapviewer/api/RestTestFunctions.java
+++ b/rest-api/src/test/java/lcsb/mapviewer/api/RestTestFunctions.java
@@ -46,11 +46,18 @@ import org.xml.sax.SAXException;
 
 import lcsb.mapviewer.common.Configuration;
 import lcsb.mapviewer.common.exception.InvalidXmlSchemaException;
+import lcsb.mapviewer.converter.ComplexZipConverter;
+import lcsb.mapviewer.converter.ComplexZipConverterParams;
 import lcsb.mapviewer.converter.ConverterParams;
+import lcsb.mapviewer.converter.InvalidInputDataExecption;
 import lcsb.mapviewer.converter.model.celldesigner.CellDesignerXmlParser;
+import lcsb.mapviewer.converter.zip.ModelZipEntryFile;
+import lcsb.mapviewer.converter.zip.ZipEntryFile;
 import lcsb.mapviewer.model.map.model.Model;
+import lcsb.mapviewer.model.map.model.SubmodelType;
 import lcsb.mapviewer.persist.DbUtils;
 import lcsb.mapviewer.services.interfaces.IUserService;
+import lcsb.mapviewer.services.utils.CreateProjectParams;
 import lcsb.mapviewer.services.view.AuthenticationToken;
 
 @Transactional(value = "txManager")
@@ -160,19 +167,43 @@ public abstract class RestTestFunctions {
 	private static Map<String, Model> models = new HashMap<String, Model>();
 
 	protected Model getModelForFile(String fileName, boolean fromCache) throws Exception {
+		Model result = null;
 		if (!fromCache) {
 			logger.debug("File without cache: " + fileName);
-			return new CellDesignerXmlParser().createModel(new ConverterParams().filename(fileName));
+			result = getModelForFile(fileName);
+		} else {
+			result = RestTestFunctions.models.get(fileName);
+			if (result == null) {
+				logger.debug("File to cache: " + fileName);
+
+				result = getModelForFile(fileName);
+				RestTestFunctions.models.put(fileName, result);
+			}
 		}
-		Model result = RestTestFunctions.models.get(fileName);
-		if (result == null) {
-			logger.debug("File to cache: " + fileName);
+		return result;
+	}
 
-			CellDesignerXmlParser parser = new CellDesignerXmlParser();
-			result = parser.createModel(new ConverterParams().filename(fileName).sizeAutoAdjust(false));
-			RestTestFunctions.models.put(fileName, result);
+	private Model getModelForFile(String fileName) throws InvalidInputDataExecption, IOException {
+		if (fileName.endsWith("zip")) {
+			ComplexZipConverter<CellDesignerXmlParser> parser = new ComplexZipConverter<CellDesignerXmlParser>(CellDesignerXmlParser.class);
+			ComplexZipConverterParams complexParams;
+			complexParams = new ComplexZipConverterParams().zipFile(fileName);
+			ZipEntryFile entry1 = new ModelZipEntryFile("main.xml", "main", true, false, SubmodelType.UNKNOWN);
+			ZipEntryFile entry2 = new ModelZipEntryFile("submaps/s1.xml", "s1", false, false, SubmodelType.UNKNOWN);
+			ZipEntryFile entry3 = new ModelZipEntryFile("submaps/s2.xml", "s2", false, false, SubmodelType.UNKNOWN);
+			ZipEntryFile entry4 = new ModelZipEntryFile("submaps/s3.xml", "s3", false, false, SubmodelType.UNKNOWN);
+			ZipEntryFile entry5 = new ModelZipEntryFile("submaps/mapping.xml", "mapping", false, true, SubmodelType.UNKNOWN);
+			complexParams.entry(entry1);
+			complexParams.entry(entry2);
+			complexParams.entry(entry3);
+			complexParams.entry(entry4);
+			complexParams.entry(entry5);
+
+			return parser.createModel(complexParams);
+
+		} else {
+			return new CellDesignerXmlParser().createModel(new ConverterParams().filename(fileName));
 		}
-		return result;
 	}
 
 	protected String createTmpFileName() {
diff --git a/rest-api/src/test/java/lcsb/mapviewer/api/project/AllProjectTests.java b/rest-api/src/test/java/lcsb/mapviewer/api/project/AllProjectTests.java
index befbc95854..0453ff0272 100644
--- a/rest-api/src/test/java/lcsb/mapviewer/api/project/AllProjectTests.java
+++ b/rest-api/src/test/java/lcsb/mapviewer/api/project/AllProjectTests.java
@@ -6,7 +6,6 @@ import org.junit.runners.Suite.SuiteClasses;
 
 @RunWith(Suite.class)
 @SuiteClasses({ ModelMetaDataTest.class,// 
-	ProjectMetaDataTest.class,// 
 	ProjectRestImplTest.class })
 public class AllProjectTests {
 
diff --git a/rest-api/src/test/java/lcsb/mapviewer/api/project/ProjectMetaDataTest.java b/rest-api/src/test/java/lcsb/mapviewer/api/project/ProjectMetaDataTest.java
deleted file mode 100644
index 706e7db2f9..0000000000
--- a/rest-api/src/test/java/lcsb/mapviewer/api/project/ProjectMetaDataTest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package lcsb.mapviewer.api.project;
-
-import static org.junit.Assert.assertNotNull;
-
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.google.gson.Gson;
-
-import lcsb.mapviewer.model.Project;
-
-public class ProjectMetaDataTest {
-
-	@AfterClass
-	public static void tearDownAfterClass() throws Exception {
-	}
-
-	@Before
-	public void setUp() throws Exception {
-	}
-
-	@After
-	public void tearDown() throws Exception {
-	}
-
-	@Test
-	public void testToGson() {
-		try {
-			Project project = new Project();
-			ProjectMetaData data = new ProjectMetaData(project);
-			Gson gson = new Gson();
-			assertNotNull(gson.toJson(data));
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-}
diff --git a/rest-api/src/test/java/lcsb/mapviewer/api/project/ProjectRestImplTest.java b/rest-api/src/test/java/lcsb/mapviewer/api/project/ProjectRestImplTest.java
index f3ffe752fd..6155d0b7a0 100644
--- a/rest-api/src/test/java/lcsb/mapviewer/api/project/ProjectRestImplTest.java
+++ b/rest-api/src/test/java/lcsb/mapviewer/api/project/ProjectRestImplTest.java
@@ -2,6 +2,7 @@ package lcsb.mapviewer.api.project;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 
@@ -17,6 +18,8 @@ import org.junit.Test;
 import org.mockito.Mockito;
 import org.springframework.beans.factory.annotation.Autowired;
 
+import com.google.gson.Gson;
+
 import lcsb.mapviewer.api.RestTestFunctions;
 import lcsb.mapviewer.model.map.model.Model;
 import lcsb.mapviewer.services.interfaces.IModelService;
@@ -75,6 +78,32 @@ public class ProjectRestImplTest extends RestTestFunctions {
 		}
 	}
 
+	@Test
+	public void testGetMetaData() throws Exception {
+		try {
+			ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml");
+			ProjectMetaData result = projectRest.getMetaData("sample", token.getId());
+			Gson gson = new Gson();
+			assertNotNull(gson.toJson(result));
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		}
+	}
+
+	@Test
+	public void testGetMetaDataForComplexWithImages() throws Exception {
+		try {
+			ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/complex_model_with_submaps.zip");
+			ProjectMetaData result = projectRest.getMetaData("sample", token.getId());
+			Gson gson = new Gson();
+			assertNotNull(gson.toJson(result));
+		} catch (Exception e) {
+			e.printStackTrace();
+			throw e;
+		}
+	}
+
 	private ProjectRestImpl createMockProjectRest(String string) throws Exception {
 		Model model = super.getModelForFile(string, true);
 		IModelService mockModelService = Mockito.mock(IModelService.class);
diff --git a/rest-api/testFiles/model/complex_model_with_submaps.zip b/rest-api/testFiles/model/complex_model_with_submaps.zip
new file mode 100644
index 0000000000000000000000000000000000000000..5985c849bbeff096451d99b43a430ec6c266cf5b
GIT binary patch
literal 8240
zcmZ{p1yEeeo5gVp76=ds?oM!r5Zv7%=-|%a3>sX54GzIIxJw8Y+%*gk90CMqAo!B^
zHv4Da+r3jY-`rd0{<^EC>U8&aROI0i&|rRFXgaT?|N8OYU+9n3+yrQ1WN+<Y?O@5K
z@;^$Xe<+b3mDctqmH^i$BDjBuP##5gCSHziz^5vl|3l^KW@>NZ^tbB8<69^&{Vw!U
zp8e3*VMs7As{$}ExQ|c&s2-(GkJDrIw6{CZ*LQ{Rz3^KxT08NfT4`>E2Y!3M3@B3M
zKs~5e7VN{%iHH+UMy>whN2ANrp@suzjoUOWl^k1e^ds}=NBZ~TQ5>cZuL}m~K$>4B
zG!Sv-FR{}uXR8vwhyZ7mgGIf8L%pf=xgfXal3mg;bNf)l@s8sj{#Dd~HL6~uxC&hx
zgSGr;>=)#cu5dkjkkF#t0F^kf2HvEB5%&y<h&(_ixdXYT_I-SCdq*qf^T^ld8Z?(*
zH;Lcg4n*ikUc}p9Q!PovO+>;?o;Ni&1-j)z-+-JoXPG46f7;<i-UqMT*yvAd#NT)2
z)N#>kb%#sw*0DRE3<ZA&!5AV~tA_V2ycLD0;jJ@M$n&@AC7EAG?nRYM>Dj7BH##ix
zd~+iF-U%l_dP5N!vjNXxw&%0>D+BfPBUpHgV;^}5ngSb|At6?F+1YWr2m8_|PTxb{
zhFCBO)8WX0Hvhy8OCt%75rIPd!vINUR~)|}F@ni+71;tl7=S{KA2Vb5A-C&}V(Nm`
zNc+mjK~`Et=%_B|17~YT+HErliK&&ydw8B8VU;nF2(hl|U{7Y?pj01fKFz*wc)U0T
zA~dLtBB{18m+={DlZ)4c3bnzu{uau5^K<XbJu1qCYUHxvXV9YiNKcK0>fMn|`4E<j
zZ2N4Tx`yDnwNga+G@+`3g%P^=RE)Ius;qtzP8BLV7oU73wJ}+v6i6;j@RsUmJ;Ty}
zDz#1QNqhl7s6_$+{FAr@O!(y8_g|n4>m8lka|PYA%U2mM@lW%<Ixv%`v*kfxbM4|o
z)+{BayrsZ8tK>XRl^;NjzUiYDB=;!`^M-Fo1z5cTOAs-q?T9G(e4N2;dRiNe?tUSc
zKe*3u*#d^iA!U(0f#<VW*OKM=Za*8X>jfKU8x{{DLzz4;5EzMc8Hwm}@=T}=X4)7C
za9w_cUL3Zho{gl#S~vgFcRooo;Hop=Qtp&oR@(WRJQr6(VJ6<E$Dyaop;xSvPsCzf
z+&Jr0v$ig{N$Gj`odbWFO+2cA^{1eInOdOSp|Vl-A&a>8+jd^V;_dCQs^Yclwch$f
znpUVeZv!S!{SJoL&G7yx=2-2fw40WysK^?^bedpc@Bjn1<sNl#hBL4ym$<M@891U%
zry>AeRKB1)lSDONtkV_GF-m^Z!t$;-`wbm`1UoKU!}%V=4{pK&y0d&fHk<7n5Bc0O
z$u)}dt}o*Lcr(j7OMTkbZcR=>Sz;xYN8DhmSJj&<`TLX0ziQzw?~`fhWo#T(4=cmm
zB*k$k&`?8s%tZwCU(Vs(M}RKZ(D=_Lbf-AZf8xT&wP{3_`_s_3260NfuX0(|YRNnY
z!5b>3ZACoOEIejF<`^Al$f{3mboGJ)x|icZdXXh``lk9%v~V|V1BK$e%|D2|W1;y(
zO=6Z{6b8FT8}K@^djp%F0;mB<iJS~SH_`bCbRzH1>s0LI*`!{tQi+8PrAU*0ra4$-
z=5)GG?%A3g=7FAv;_P~$#!eJ3f*zG^oI3%H@Ob%BW#_hVU$yIE*%I;@*>Vg`x-8))
zj<clfnACbNfKVZ%$W`pa7sp6-!MWW82kTC~+EmgiT64~{!w^bLQr0`X@%iklYx+3O
z`QGsyK>72?_I14iy>ouvgA`sP2hk55WWM8L{cAL=kbQXS9H<3*fo?R{?QV>!@fB(x
zp`xi=Vrr>ubRTu~?XTTv+L{8{PczgOW;hl&XLhu(Us0u4WpX!Y)!)%imoB`erz43+
z$@PQ^Y?a@p1vB^-0TLW&?J9(uKcds+`_p;!?BAT2`>Oj&r6i6v^c*pM(@RScJVDpf
zPZfo6U&rv64aAe$G?7^fA!CeCKA$~-9=mYd^F;0(LX<A>wYtsPP#m`oS%%4o3kF6B
z#8_{>X=^!q_3_j@0NE~LB5zxv>@Ex0bK(>?>#^P7Y%@Mu(j^%*?E{N%l~B2v(qo)V
z77dlDgwr3Zc%nY!SiMGxXtQODNKhLMXr{36w=7(%^$*wQpEc*O3S_4VvI}YauL_qq
zWoUf?ogV;EmQs1EyA;%ylntLWGZURNK9=eFg#e?|`*vqW=n)e<HAq-usP%{(sq`8-
z+53N0<Q1k-5;)pjAa&9Xe103pxgs4`CpX>}t2%v)7nGTXz<*|s(2`=Llf^WkyFhNq
zZQIRD%&Pxo*mSqqX@OO^Q&38A|K}P`67TGrTcRQ>`yv0yUH{(;<=^WT#pAkVJfa}L
zj|c<P!3YC``DER4W&dl*(lb)t;KTO+qWt^{l);4>LJ85tYSt_ielsEIp-jh{R*xTR
z_?6ZEX!_#@wf%i^GAVLDzV$+lW#4^FKj#IXfv;icjAy6}_Lz6ca%~*!$cBhXBIz~z
z^}#UVHRNIIZ8SI)2ap$5q&7CD=vt#wzkZ1Sf!0DH^+k3d#6loF3Ma%BiykT#hZ<b+
zZn5!G<KX+W6p2vM$}qe7t{ut{noSWEDujrL6>(SEh=NtB{0TLeRCd-HjmWZivTNdG
zoYWGNAGu_S3)c?At*<i{+{&~iM(E!W2Hj%#pR4rO#uYtiOyJokgXy22@-%O*p-Xch
z&>|-p!<t5shivtr3&EMT*{PN=Q@I#?c7kh)kH09>dIjdLrW@ppFBdUMfB&p5+i2?5
z8UI;GjYhdM&QT?paw%2kd?F1GciPigkGmY#i57ZV8e^@!Fphv9szR!D+mdRghc~(W
zL9TLyT;Z-`BdB+g3dyqYn@wQ!)u~w5Yy94nX>L36#8SI%Rt*@{UrOKf$_?pwdL$Np
zI1g!BAq4JxeMq8eXLvvKX*e~vVPyFyku*XOFHdt_e%D+SlAsv2PBP%L-7$U}T(&(N
zils{kIx+liF5AJ}V5uAAfX{uWem~nyFe^RqEU;QU?%Nf4%?h~;<WP4n^`aDno@kbO
z%huC77#4AcWMb;krkk2c+_PDXny8FV>mZ`A#EXJEe^?N^n@ya*`I3f=LvG}Ad5Xlg
ze(z3Q(diEBIu1&C$ykCPNkE;<tPpxw&NEW+N{h&AcaSScv|VD`T~Tn_0ODgKV5?}U
ztmsj(J7=Bl!z|t!)Je48f0H4GYnu~kmy2fgjq;47irmKZUasX5D0xiXom=vDJY~ls
zH&S`V|21V5Ik^I|mfd3eM4jI&?6Vq3(FnYGQu?5Pg8-VDB~@jAOZzqGMgIr`M>dK&
z2}N<-Cok1uW;ko9i;-O+1rq=;h{65<=?62sL_{>E_<O3(fp03PM2bhDg>CPM!)snv
zXF4M(^s(`U!Q^++x<p0`Wd+FV3wGJ+OL~TpCs4c3hssS@dGj(Kphv-c;PmHxZ85!F
zB}BPvwZb(&eS^Kq4<4ZBn(84XJc%~_4C|UICr-ctyyZz%$tbZcUeg+=<bTsL*g%+y
z`Bvn^O@(&H;^>sPd#|g`Y=bQJ7hmf#f{HKY7$D=??Vv4HW{HQxdv~C4J44LJW1J8d
z(l22!lqy$6Rk7L6T-#*$nY7ps(zz8Ue=-R|HP?g$ugiWg-I)Cvk_BltixGn~&%tNx
zzFS2dj7tl#9gNpQAOfSbEqLb7$8Z<5RNQjA4Wd*sSlo&dIbURAHWKJMH9DCLlNCAD
zJg)A0tX{X?+X6+Ky<WzKHf?dU3?$1-Yf@YP;^WVKl@`J#gcMs|zB#Q7%^u4}8p${v
z7~qk|!f-@N$zR)WbK$khG-s;A&n{EDZD|5)d(tnEdpzK6?h-G0;@R!3_jM*>wTAOd
z&5;!Msv~wNQ_e>6%GsU2?~c2wRxoR;)KjCm1<#c(&~S}&$?zncMDGgug_<Zwv+2ZG
zL(MWqEoMeD{pA*f(sizp4~*KjF2(ky+?uuJBKPP9wGq<3Gl3cghpQ}G{U6ZP29z}O
z6S9{oy(Z<_PSHFAz=yC}9maq$TFzl9Xv@@?OCe|ke3ZeIMeMSZ$|UMs3G|CJ*wqqY
zCN(ZRd+t)0xFz@ae1%sV!oPXJuI;yO;B~6LIi$4Z2*#|Qx*vBf)MXh~ypvA$u6HiE
ze?b0^$#OlKtgo~i`}m{D5_12;WI6sm7udV5^I+e9H(Hy#jEc4&7>42HlC4s3rA~~i
zzXO{6<hG(5j;4Xzt*T;PT-AOX8yA{1vLxxqBwRi#0s$XEj0i+#v&lE9jY($srs9!C
zPz}2KNPwcZ2g9h>kh{WBX_r*uAx~t*_o>+?LLbLJ<Zb7Z6K|9RFbKhNJ8@)$m<Dnp
z)4K&GvIke|K_;L`7Q~j)l!}V;Vn}*xb#cAx$gC=($R@Z=!4?YGVqKDYm9+f}(TLsn
zYARZ_>L0t_!ffD?*zARQHdMM5G&zGjz&{Yz)NMp<GjHMj-Bje~;~5`Va)&us(Nw#?
zHh7T!QWtz7@_I7Ace`^Qy`Z`|BS<`;iY$XngSdzDv&}~H@*BM=Mq3Pxq-qke>~POR
zoTA8yzS%p(7S-2gtn>J%PFZ`CC*H|ECXyju$XO-Hvh{+}c#9i|3MF@m-BK+#5uFAb
zQ@yqReU>~dSgNB~E(SHhudgl@J7s|)lt6|?Ir4mCXF3xr*Y(Ij9G<uni5ZifsbG48
z=Z#4=8W<T6iM<~PTyY(1Ua`1$@ySlKtMUzF`Is~zywkQXaE{$SIoXPbKnd`8gvY1d
z4E=T2xV^o^yiqzoMp_muJdeY?p5n71-S01EnYJSNs7F^^q4l*_SNU6~eDtT3mx9MQ
z<?-LPHdm>U=@yjf*z1%`4?ATQm|VeJ;1uHGccxsIgYvPiVSQE|St$~tQ>n;oS0(RE
z0iSyzfhjMG;$Id`3J4p}LQ^7k?00C9c^A4BkINHNJV%;drz-B(e$04{{YFO?NYuoH
zl^M#hWzvYnxH8#$rjguXh{@`Bvx8EV!j+uR$3!z)SOAv0s6rMU^FP8sO3}t!Y<QPz
z&K3Y}vSxlYn`hRjHZ#&)VOZhP(Y)_IQNDPdt<ce;@X2H}mFb4CyLHUSdR6Ehwhu8x
zgw1TY=zuR}xqtmKI{eu&iaII5+Q%)uWJe@lo`jtg{($<o+zW*Oafd@--<Hr0!AjUv
z3XzKs8EPVi-y4eWGwm<N;7g@1UQDEY?35d3FDhsxB&R}@h(Ku)HxDzWve%59EIGB%
z8QI!?^IkIeE0=L+o|Pq&@r!Rd3USFS{i_KOi{np1?e^HHIBB;3Ely{^$5iTE8tq(d
z?J@E;P$T_Z#+u^MRcEO|&Y~@w2U8%)bh-sQV$7(Jm4EnS^fhZmdu4pHeylrZ07n!P
zw;kjxSa+uKVbkY3R|_ai45=KwgwHu@O#?cyM_+jahl`h+B5ps{#@jUCGkl*D!*pF%
z6{>$sjxfk;=tb9Xa!nzr#cT#i;%2XE0>^)2^q3^E12=(B*zEwcjU07Nw>x#3FKX;e
zOXv@E)7~J7+P4=yfE3JWXlrXHSGuxX4qv1{L<p{}e+Kuq7z^$y647oI3X97ea%iH6
zfpUl>8R9I}F-5IwfoZwu6Kx(n-;YoGj~jWL_ptz>6May#sL}$3^cTK@9jUg0u4KMk
zf<uk#Y+_r-nZg&ToTzVEgqb<)3D3vE?!wBLDcc4hV{)Dx^UjuIkdCFBb+nc%+JQvw
zIViY#&@(2G>QF<if~FxO07shkmK7DiB0wKMWrhaY?xs9BD5I7wIwz1TT2Gp=E0&r{
ztEbBt%d;fB7zpAoSE=xJFsPE69a<;`&r(HJ`c3SsH~!?8QD_BTntFap`J!_FKC!fJ
z3LMt{E3*h<G)~U%&d7w<WUe$CrAJiQf5uYJ<xvcfqRXsR<<=%*&?XXGAG+xx3*Hw+
zKI%_)9Opm!nyiJ?;$}RoJd(VfeLUc9r+Xh>8<IRHZ7k0Tkav}~E7dvOzIOLSVGyN|
zC6zhlre3%;dY@FGES;bwmoS%|xVfl4>JwiOFXlijbBdd|d6GTR?@sNdcAb)?Z)NR^
zkYHEz_zJOOlH)kv(O0k|85&vhshP{xZg%y6mchcrCAD4Unw-IXZ}9BHifUbq@70ab
zf2?2r(fWJHAnbbxFfifd|FC|}zpP)^-f@Em{l3G<<j0L<>~fK8{AOIkGDT+v3&fxX
zMD!v<>HUzJF@xjfKHU|~iEVo0{$cf2z&nQcB+^Jq#j?Bcj5uAUaB4i?00_$~-A>^(
zb&*#&j)^NGZj(D}Y#{zwQ@x~N*mS0b=3dXw@0vz{@)DeOC?m#*BVD^Tmh`I4=K<_w
z)LG8qdkrLu4rZ**?{IWaT;MlzggZD3YTkDvTZH7GK#{Em@iftW&GDr4Vx3rDsOqU=
zoyr?^OTngaM|<uTn>VfL4j*n<xM%HggP%z?CKs%%eSqEj#uK?K$_-T9sKv=wTSnBT
zG>DT4>6{H$r4%E@Hm~+F2#^*dO=dv@1I%Mzk!4cbn-|u3&qx6<))`v!Yy=C0Xmda8
zU(tLn)p0S>dv!2y(bDoxAH*sw{t3Cn_LZrFQn9A1P~a+~h$x6Ni_u5Y#9sEmbkt~w
z#zZsT^ICQoOqjI)a1>Io?e`Ms9$A4upkYVKjc%(5;$G=O638g0{i!C69bA=kR1a}8
zefG8?CfoRJzZAA-_SIze@iw+iT_rlzTzCHhcMLrOnVr*)Oz;5#hI=r1wf&;s!YaHw
zB4rx_HtP(&ZZ8aBj`G3WRS0NuzgcM~&tvYuO@RB^4%J#Xz4NK+y6>AL;PMPx>@vUP
zAcLGAFsYqEZpk;Pne2px|EMU|`cjE3Q9boG(<I2qFolN0h_c3j3(x&BUgeFKq+9$*
z|MTW>Aa{QRfAFXjaz2EW>uPKJV%x+w<k&=$P8~eP#J!wlaghBBZ>|6tw!Nkk)J(!%
zBh~sWuC+T1p2KJmKaY01O2Kn{K52v!XNd9K_aWe!y;R<PjJg{J?3T0d%f;l7G{ZB>
z6m`&IGx7i<#<c5_;)}U4t)&WAx4WBOgF9DqVf9M7w_W7?XL<OnkGtqb96Q3IY7T*}
zq}W*anL!x#vb;+Q%IDbT2c-y#3S?nhrXGC8)jHL>D3v~})21EBFX2VhUCp<45ex(b
zFyA$y90`ZN;68+?qo${dV9K-gP<G)iiK5fO2WPPQ7Y@hdgG86?kHp_3D^Qfbmie+U
zz#%xaj5uP#$YLl{zkNcPd!ah7)Kig4?HcaV*6)tZgZ6c5WYN)ASEqn%IAS1h7W$#U
zujRUwpn|QnPsB-6oxv+whQHmvIb$i>@uoRc`{Mu^c>oHqn{ZEAhKvlKq4+Cfk=ZnD
z^5~e0nkAlyJe?69XzP}^!$sW)4=pgUDzd}HKfKC`pQ%Xm1vcC8w5r_EEumOCgBfMe
zFA{f~p|yb5sE$D0#!(4^@SgrbE0?qRO%vb^Uv~7-kfmN6OA}seK<ru~^#L!(v!ir;
zcCg3ksxHg@ud{F;*XOzm0K%M;gC8~=eyy@1d@&is=5yWCg;ih9F;CGP6@50xM1zEu
zH?XUM6t&@rg`h8ab+iOp38KAQug<Isq_^{4=3AooEg?@6=Y`@JG<nR|D2=k1coj+3
zfj{Ay$WP~;S+dTx_tjrFIM)%n3GOWZ1c}@CKTPbu3c3}RF>HNyDR-g?3ilU}`|1HG
zVe`7OrpfjPAu*U2m#Gda9L}{{0iYC65-kBT%f}saCLsUXVAs55ihpZ}EQ4v8*yS>b
z>7l1(>dt6$sBXyhTJRI_0csFYUPv4JU_e^Mg8@DI0{b6VywMGp%9B=TSbKCu+ecUY
z-Fde+v3B^|5B1eIcwYD&RUQ8TQR%xoC?t@e0f$FeRah4f^EZg;i0l~57^ok2!sbk|
zS>HavCvhNJz!;9jcWfN_CNN`(RILwq2S})lg$`IR48Y+ZqXMQWTHEdp)UiOMcO2Me
zfFgyomw9PPXH?NC!vJ~SCQpv9tpQY?0pZ4+>0umU`7n7_a6w9h)U)4v8%ahGSoJ8G
zNF2gZAnRvw-8g5c5?bg}#fcPpuVn!UxaMG+p)t#7c_T(?O$JO50u!o&2CzWff864g
z7mH88ep|VLa%rmcJp||6@t1A5Kx53*DHft3_@q)@BBUK%*;D8emk>U&|2Y&f+_25K
zpE8E~n8X~<TO%s?LWs&Jd~%e`qkrBK-e9u9OmzDh&+|E(@TEqj(1y3!_{ofdLdq!?
ztrB{8Yp+E^nUvCI?(h7{v#tg0+h)J>W*>j$3#v*Aq_N~`wj|eZ;rBL<Vxy+^pNi0M
zEyPHwY40jc_deXWh9`9fo0+^U`FOsD%o%PHt_1AwAOKJ_y_drQz?~RW#&6YG7ubBv
z)M~T!xFJaawt}kWkAP|(xAbfnUQ})E@dTTryHE`0O51QMP(QZxb=F6morFrlfb&5?
zQ%q6Wbp19IEnZg?dK5D~s>|BR${70dqFu`R5ykQ{F!#rFfPB~C8$a@0c<}ynfc0~v
z=GHGfiI5D5JqMgp%M78@pNx#+W9F^jckR9%`Q=nEIW=NG6NT`~W_)3419i-<FtL`r
z8|UU*w@;|oMg)v#^P+_}jBt#@APZy-3Y-VkmdfsLQCyYzm!IPCM%iVmpu7sP%LqRR
zhCd`GbyDnfcp2Jg$fB~uRk<VER1$}@M<EG~vw_C>iwXC-qZ`F8`}<rw4@fO#Vcp#_
zuU>v4YbqhTgr09S3ckAITjnkKIJTWMb0&gtPLbmc>m>>6Rn-jJaOUc}xQG{b1BCq?
z7a;H%LtvdaQ~59otz-qO2MhMj60RHh3pIep2&W@;{xiUk#sCiBQ+f7Dcy?Pz)lvO=
znb)T(VZVhD#NWo(CL${K%UL<WCjnk~d1<=hv=<q{EE~QWk}|p<wb5E>Mq3I)yZSt%
z>Yga!aZgS~y7W=aKmAwDx3G8ZfS$tP=8?NtRWCFOK&cSWE=o<_?<*a?8Q6{)#{58I
z)OF;QoSCE4NIyUSoqIW_kt#?z%_iInVS$s)XN}{rA&n)Z*NZe)lxf1zU=c8Yq#^-0
z6_W6S6RB-1L|4>v2>m)rhH1r&|7kn3ut{T6x0BLXonKt5CNq$;!Q!}`qkV@wL1Vgs
z1z<Jh!pVGqfe#C7nP_3`WO#aH$X95yqII@QhkYF-Ls_oD-o{gV9!;tI#(-01)%;$p
z0}e^)fc#oZc>>k0xV+<C6}DfgxH1=7ChT`d`wq|5w|s70&v6YpWRN$(K~!u+#FYw>
zQ<$ZqW9Eei-qy`{h0FGr@`2#(Y%-F5-`tF!bbZ3SI~n@#0!o!CuG27`;HUR+&#w?t
zKk=<tG_7dQr!-ma5GXqIjc)s3P*CUaM8rd&$iJL%6?->QiEtVBtT}WU<)kB)vEYwV
zX-|2qTrd<1T^ErR`4tZra|SuNy!aKrKg1NRjI**j`WtXxlczceb=%gGd`2Pj^w93!
zB{-R}A})V;fKic$g(HLc{}N5=M=Sh&VgFtJSH6h`L;lxOSeTbE^%hE_MXM9A|Ey2x
z|4u=l(jU9Me=^ah;4{vUf#2YN)~DcqC#O%r%8%ebIqFmL!FT1}-{gPRr{sU9txw5r
zkK{k8>QizWRruU*@}JtVZru6%{%`XCm(czplRr{3o{*p9wND2j4=Ny)Vw!-(eI4}I
zV1Me<LH^xNe>#ZzV+{Rg`hPdqpYn07m(3*~`I;kt^Z(SR{C{_np7NU?1M&ai|J7G|
z%Fp8f`26M*ZTtT<|3CF9|KG=zr~EpcC;Yz-FHiaXtO-fK`Jjn_zxaRZQ~tjf{-^vw
vg8ys-o|1>H)3TPFCtzhQBtQTDTmMuQdBn%K9R>#V@goNh12aMV`|iI0Hh9AN

literal 0
HcmV?d00001

diff --git a/service/src/main/java/lcsb/mapviewer/services/view/OverviewLinkViewFactory.java b/service/src/main/java/lcsb/mapviewer/services/view/OverviewLinkViewFactory.java
index 40d88478e4..c602505e0c 100644
--- a/service/src/main/java/lcsb/mapviewer/services/view/OverviewLinkViewFactory.java
+++ b/service/src/main/java/lcsb/mapviewer/services/view/OverviewLinkViewFactory.java
@@ -25,7 +25,7 @@ public class OverviewLinkViewFactory extends AbstractViewFactory<OverviewLink, O
 	/**
 	 * Default class logger.
 	 */
-	private static Logger	logger	= Logger.getLogger(OverviewLinkViewFactory.class);
+	private static Logger logger = Logger.getLogger(OverviewLinkViewFactory.class);
 
 	@Override
 	public OverviewLinkView create(OverviewLink object) {
@@ -37,10 +37,11 @@ public class OverviewLinkViewFactory extends AbstractViewFactory<OverviewLink, O
 		result.setPolygon(object.getPolygon());
 
 		if (object instanceof OverviewModelLink) {
-			result.setModelLinkId(((OverviewModelLink) object).getLinkedModel().getId());
-			CoordinationConverter cc = new CoordinationConverter(((OverviewModelLink) object).getLinkedModel());
-			result.setLatLng(cc.toLatLng(new Point2D.Double(((OverviewModelLink) object).getxCoord(), ((OverviewModelLink) object).getyCoord())));
-			result.setZoomLevel(((OverviewModelLink) object).getZoomLevel());
+			OverviewModelLink modelLink = (OverviewModelLink) object;
+			result.setModelLinkId(modelLink.getLinkedModel().getId());
+			CoordinationConverter cc = new CoordinationConverter(modelLink.getLinkedModel());
+			result.setLatLng(cc.toLatLng(new Point2D.Double(modelLink.getxCoord(), modelLink.getyCoord())));
+			result.setZoomLevel(modelLink.getZoomLevel());
 
 		} else if (object instanceof OverviewImageLink) {
 			result.setImageLinkId(((OverviewImageLink) object).getLinkedOverviewImage().getId());
diff --git a/web/src/main/webapp/index.xhtml b/web/src/main/webapp/index.xhtml
index f7208909b3..6e0199f6ad 100644
--- a/web/src/main/webapp/index.xhtml
+++ b/web/src/main/webapp/index.xhtml
@@ -47,7 +47,7 @@ function initMap(){
 		    bigLogo : windowsTouchInterface, 
 		    overviewDiv : overviewDiv,
 		    customTouchInterface: windowsTouchInterface,
-		    debug:true,
+		    debug:minerva.GuiConnector.getParams['debug']!==undefined,
 
 				dataCollections: [
 								{name: "search"},
@@ -122,6 +122,14 @@ function initMap(){
 					</button>
 				</div>
 				<div name="versionDiv" class="headerTextBold"/>
+
+				<div style="float: left;">
+					<button name="showOverviewButton" class="overview_button" style="display:none" >
+						<i class='fa fa-sitemap' style='font-size:18px; font-weight:400; padding-right:10px;'></i> 
+						SHOW OVERVIEW
+					</button>
+				</div>
+
 				<div class="rightHeaderMenu">
 					<div class="div4checkboxes">
 						<input type="checkbox" name ="legendCheckbox" />
@@ -130,7 +138,7 @@ function initMap(){
 						<input type="checkbox" name="commentCheckbox"/>
 						<label for ="commentCheckbox" >COMMENTS</label>
 
-						<button class="overview_button" name="refreshCommentButton" style="display:none">
+						<button name="refreshCommentButton" class="overview_button" style="display:none">
 							<i class='fa fa-refresh' style='font-size:21px; font-weight:400;'></i>
 						</button>
 					</div>
@@ -172,6 +180,11 @@ function initMap(){
 	<ui:include src="/WEB-INF/components/map/feedbackDialog.xhtml" />
 	<ui:include src="/WEB-INF/components/map/missingConnectionDialog.xhtml" />
 
+
+<div name="dialogs">
+	<div name="overviewDialog"/>
+</div>
+
 </h:body>
 </f:view>
 </html>
diff --git a/web/src/main/webapp/resources/css/global.css b/web/src/main/webapp/resources/css/global.css
index 64a70301ce..e66f255d45 100644
--- a/web/src/main/webapp/resources/css/global.css
+++ b/web/src/main/webapp/resources/css/global.css
@@ -1,3 +1,10 @@
+.canvasDebugClass {
+	border: 0;
+	position: absolute;
+	top: 0;
+	left: 0
+}
+
 .menuBelt {
 	width: 100%;
 	min-height: 36px;
diff --git a/web/src/main/webapp/resources/css/style.css b/web/src/main/webapp/resources/css/style.css
index 432256d043..f58853b336 100644
--- a/web/src/main/webapp/resources/css/style.css
+++ b/web/src/main/webapp/resources/css/style.css
@@ -1113,9 +1113,6 @@ input[type=file] {width:280px !important; top:15px !important;}
 
 #content p span {font-weight:400}
 
-.overviewContainerClass {width:100%; height:100%;}
-.overviewDivClass {background-color: #ffffff;border:0; margin-left:10px; position: absolute; top:2; left:0}
-.canvasDebugClass {border:0; position: absolute; top:2; left:0}
 
 .labelTextBold {font-size: 13px; font-weight: 900;}
 .labelText {font-size: 13px;}
-- 
GitLab