From 4122293e8b3ba7562fffcf3debed00bc59330fe6 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <piotr.gawron@uni.lu>
Date: Thu, 19 Apr 2018 10:44:58 +0200
Subject: [PATCH] OpenLayers support different layers and initial center point
 and zoom

---
 frontend-js/package-lock.json                 |  54 +++++-----
 frontend-js/src/main/js/GuiConnector.js       |   5 +
 .../canvas/GoogleMaps/GoogleMapsApiCanvas.js  |  82 +++++---------
 .../src/main/js/map/canvas/MapCanvas.js       |  62 +++++++++++
 .../map/canvas/OpenLayers/OpenLayerCanvas.js  | 102 +++++++++++++++---
 frontend-js/src/test/js/google-map-mock.js    |   1 +
 .../OpenLayers/OpenLayersCanvas-test.js       |  52 +++++++++
 7 files changed, 263 insertions(+), 95 deletions(-)
 create mode 100644 frontend-js/src/test/js/map/canvas/OpenLayers/OpenLayersCanvas-test.js

diff --git a/frontend-js/package-lock.json b/frontend-js/package-lock.json
index 3538b0f218..36ceaef53d 100644
--- a/frontend-js/package-lock.json
+++ b/frontend-js/package-lock.json
@@ -39,30 +39,38 @@
         "litemol": "github:dsehnal/LiteMol#67556b0de0d2428f9494136758cbf8a662f66412"
       },
       "dependencies": {
+        "ProtVista": {
+          "version": "git://github.com/davidhoksza/protvista.git#4e4bb737ba1e183291505bd25f8bae2e651ce21e",
+          "dev": true,
+          "requires": {
+            "d3": "3.5.17",
+            "file-saver": "1.3.3",
+            "jquery": "2.2.4",
+            "jszip": "3.1.4",
+            "underscore": "1.8.3"
+          },
+          "dependencies": {
+            "jquery": {
+              "version": "2.2.4",
+              "resolved": "https://registry.npmjs.org/jquery/-/jquery-2.2.4.tgz",
+              "integrity": "sha1-LInWiJterFIqfuoywUUhVZxsvwI=",
+              "dev": true
+            }
+          }
+        },
         "jquery": {
           "version": "3.3.1",
           "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz",
           "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==",
           "dev": true
-        }
-      }
-    },
-    "ProtVista": {
-      "version": "git://github.com/davidhoksza/protvista.git#4e4bb737ba1e183291505bd25f8bae2e651ce21e",
-      "dev": true,
-      "requires": {
-        "d3": "3.5.17",
-        "file-saver": "1.3.3",
-        "jquery": "2.2.4",
-        "jszip": "3.1.4",
-        "underscore": "1.8.3"
-      },
-      "dependencies": {
-        "jquery": {
-          "version": "2.2.4",
-          "resolved": "https://registry.npmjs.org/jquery/-/jquery-2.2.4.tgz",
-          "integrity": "sha1-LInWiJterFIqfuoywUUhVZxsvwI=",
-          "dev": true
+        },
+        "litemol": {
+          "version": "github:dsehnal/LiteMol#67556b0de0d2428f9494136758cbf8a662f66412",
+          "dev": true,
+          "requires": {
+            "@types/react": "15.6.15",
+            "@types/react-dom": "15.5.7"
+          }
         }
       }
     },
@@ -3058,14 +3066,6 @@
         "immediate": "3.0.6"
       }
     },
-    "litemol": {
-      "version": "github:dsehnal/LiteMol#67556b0de0d2428f9494136758cbf8a662f66412",
-      "dev": true,
-      "requires": {
-        "@types/react": "15.6.15",
-        "@types/react-dom": "15.5.7"
-      }
-    },
     "lodash": {
       "version": "4.17.4",
       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
diff --git a/frontend-js/src/main/js/GuiConnector.js b/frontend-js/src/main/js/GuiConnector.js
index d46ae636c4..08e2eae6af 100644
--- a/frontend-js/src/main/js/GuiConnector.js
+++ b/frontend-js/src/main/js/GuiConnector.js
@@ -134,6 +134,11 @@ GuiConnector.prototype.getLoadingImg = function () {
   return "icons/ajax-loader.gif";
 };
 
+
+GuiConnector.prototype.getEmptyTileUrl = function () {
+  return "resources/images/empty_tile.png";
+};
+
 /**
  * Returns home directory for images in the application.
  */
diff --git a/frontend-js/src/main/js/map/canvas/GoogleMaps/GoogleMapsApiCanvas.js b/frontend-js/src/main/js/map/canvas/GoogleMaps/GoogleMapsApiCanvas.js
index 60f993b41e..c1f58c7b99 100644
--- a/frontend-js/src/main/js/map/canvas/GoogleMaps/GoogleMapsApiCanvas.js
+++ b/frontend-js/src/main/js/map/canvas/GoogleMaps/GoogleMapsApiCanvas.js
@@ -17,15 +17,6 @@ var GoogleMapsApiRectangle = require('./GoogleMapsApiRectangle');
 function GoogleMapsApiCanvas(element, options) {
   MapCanvas.call(this, element, options);
 
-  // following fields are used in conversion between x,y coordinates and lat,lng
-  // coordinates
-  this.pixelOrigin_ = new Point(options.tileSize / 2, options.tileSize / 2);
-  this.pixelsPerLonDegree_ = options.tileSize / 360;
-  this.pixelsPerLonRadian_ = options.tileSize / (2 * Math.PI);
-  /* jshint bitwise: false */
-  this.zoomFactor = Math.max(options.width, options.height) / (options.tileSize / (1 << options.minZoom));
-
-
   var self = this;
   self.setGoogleMap(new google.maps.Map(element, self.prepareGoogleMapOptions(options)));
   self.setupBackgroundOverlays(options.backgroundOverlays);
@@ -161,26 +152,6 @@ GoogleMapsApiCanvas.prototype.addCenterButton = function () {
 
 };
 
-/**
- * Transforms coordinates on the map from {Point} to
- * google.maps.LatLng
- *
- * @param point
- *          coordinates in standard x,y format
- * @return coordinates in lat,lng format
- */
-GoogleMapsApiCanvas.prototype.fromPointToLatLng = function (point) {
-  var me = this;
-
-  // rescale the point (all computations are done assuming that we work on
-  // TILE_SIZE square)
-  var p = new Point(point.x / me.zoomFactor, point.y / me.zoomFactor);
-  var origin = me.pixelOrigin_;
-  var lng = (p.x - origin.x) / me.pixelsPerLonDegree_;
-  var latRadians = (p.y - origin.y) / -me.pixelsPerLonRadian_;
-  var lat = Functions.radiansToDegrees(2 * Math.atan(Math.exp(latRadians)) - Math.PI / 2);
-  return new google.maps.LatLng(lat, lng);
-};
 
 /**
  * Transforms google.maps.LatLng to tile coordinate (for instance on which tile
@@ -202,33 +173,6 @@ GoogleMapsApiCanvas.prototype.latLngToTile = function (latLng, z) {
   return tileCoordinate;
 };
 
-/**
- * Transforms coordinates on the map from google.maps.LatLng to
- * {Point}
- *
- * @param latLng
- *          in lat,lng format
- * @param {Point} coordinates in x,y format
- *
- */
-GoogleMapsApiCanvas.prototype.fromLatLngToPoint = function (latLng) {
-  var me = this;
-  var point = new Point(0, 0);
-  var origin = me.pixelOrigin_;
-
-  point.x = origin.x + latLng.lng() * me.pixelsPerLonDegree_;
-
-  // Truncating to 0.9999 effectively limits latitude to 89.189. This is
-  // about a third of a tile past the edge of the world tile.
-  var siny = Functions.bound(Math.sin(Functions.degreesToRadians(latLng.lat())), -0.9999, 0.9999);
-  point.y = origin.y + 0.5 * Math.log((1 + siny) / (1 - siny)) * -me.pixelsPerLonRadian_;
-
-  // rescale the point (all computations are done assuming that we work on
-  // TILE_SIZE square)
-  point.x *= me.zoomFactor;
-  point.y *= me.zoomFactor;
-  return point;
-};
 
 GoogleMapsApiCanvas.prototype.createMarker = function (options) {
   if (!(options.position instanceof Point)) {
@@ -452,5 +396,31 @@ GoogleMapsApiCanvas.prototype.removeSelection = function () {
   }
 };
 
+/**
+ * Transforms coordinates on the map from {Point} to
+ * {google.maps.LatLng}
+ *
+ * @param {Point} point
+ *          coordinates in standard x,y format
+ * @return {google.maps.LatLng} coordinates in lat,lng format
+ */
+GoogleMapsApiCanvas.prototype.fromPointToLatLng = function (point) {
+  var tmp = MapCanvas.prototype.pointToLatLng.call(this, point);
+  return new google.maps.LatLng(tmp[0], tmp[1]);
+};
+
+/**
+ * Transforms coordinates on the map from {google.maps.LatLng} to
+ * {Point}
+ *
+ * @param {google.maps.LatLng} latLng
+ *          in lat,lng format
+ * @returns {Point} coordinates in x,y format
+ *
+ */
+GoogleMapsApiCanvas.prototype.fromLatLngToPoint = function (latLng) {
+  return MapCanvas.prototype.latLngToPoint.call(this, [latLng.lat(), latLng.lng()]);
+};
+
 
 module.exports = GoogleMapsApiCanvas;
diff --git a/frontend-js/src/main/js/map/canvas/MapCanvas.js b/frontend-js/src/main/js/map/canvas/MapCanvas.js
index c85c14d238..7637d62171 100644
--- a/frontend-js/src/main/js/map/canvas/MapCanvas.js
+++ b/frontend-js/src/main/js/map/canvas/MapCanvas.js
@@ -3,7 +3,9 @@
 // noinspection JSUnusedLocalSymbols
 var logger = require('../../logger');
 
+var Functions = require('../../Functions');
 var ObjectWithListeners = require('../../ObjectWithListeners');
+var Point = require('./Point');
 
 
 function MapCanvas(element, options) {
@@ -21,6 +23,15 @@ function MapCanvas(element, options) {
   this.registerListenerType("shape-rightclick");
 
   this.setOptions(options);
+
+  // following fields are used in conversion between x,y coordinates and lat,lng
+  // coordinates
+  this.pixelOrigin_ = new Point(options.tileSize / 2, options.tileSize / 2);
+  this.pixelsPerLonDegree_ = options.tileSize / 360;
+  this.pixelsPerLonRadian_ = options.tileSize / (2 * Math.PI);
+  /* jshint bitwise: false */
+  this.zoomFactor = Math.max(options.width, options.height) / (options.tileSize / (1 << options.minZoom));
+
 }
 
 MapCanvas.prototype = Object.create(ObjectWithListeners.prototype);
@@ -153,4 +164,55 @@ MapCanvas.prototype.triggerListeners = function (type, data) {
   throw new Error("Not implemented");
 };
 
+/**
+ * Transforms coordinates on the map from {Point} to
+ * google.maps.LatLng
+ *
+ * @param {Point} point
+ *          coordinates in standard x,y format
+ * @return {Number[]} coordinates in lat,lng format
+ */
+MapCanvas.prototype.pointToLatLng = function (point) {
+  var me = this;
+
+  // rescale the point (all computations are done assuming that we work on
+  // TILE_SIZE square)
+  var p = new Point(point.x / me.zoomFactor, point.y / me.zoomFactor);
+  var origin = me.pixelOrigin_;
+  var lng = (p.x - origin.x) / me.pixelsPerLonDegree_;
+  var latRadians = (p.y - origin.y) / -me.pixelsPerLonRadian_;
+  var lat = Functions.radiansToDegrees(2 * Math.atan(Math.exp(latRadians)) - Math.PI / 2);
+  return [lat, lng];
+};
+
+/**
+ * Transforms coordinates on the map from google.maps.LatLng to
+ * {Point}
+ *
+ * @param {Number[]} latLng
+ *          in lat,lng format
+ * @returns {Point} coordinates in x,y format
+ *
+ */
+MapCanvas.prototype.latLngToPoint = function (latLng) {
+  var lat = latLng[0];
+  var lng = latLng[1];
+  var me = this;
+  var point = new Point(0, 0);
+  var origin = me.pixelOrigin_;
+
+  point.x = origin.x + lng * me.pixelsPerLonDegree_;
+
+  // Truncating to 0.9999 effectively limits latitude to 89.189. This is
+  // about a third of a tile past the edge of the world tile.
+  var siny = Functions.bound(Math.sin(Functions.degreesToRadians(lat)), -0.9999, 0.9999);
+  point.y = origin.y + 0.5 * Math.log((1 + siny) / (1 - siny)) * -me.pixelsPerLonRadian_;
+
+  // rescale the point (all computations are done assuming that we work on
+  // TILE_SIZE square)
+  point.x *= me.zoomFactor;
+  point.y *= me.zoomFactor;
+  return point;
+};
+
 module.exports = MapCanvas;
diff --git a/frontend-js/src/main/js/map/canvas/OpenLayers/OpenLayerCanvas.js b/frontend-js/src/main/js/map/canvas/OpenLayers/OpenLayerCanvas.js
index be30aad6a6..ee98806e63 100644
--- a/frontend-js/src/main/js/map/canvas/OpenLayers/OpenLayerCanvas.js
+++ b/frontend-js/src/main/js/map/canvas/OpenLayers/OpenLayerCanvas.js
@@ -5,6 +5,7 @@ var ol;
 var logger = require('../../../logger');
 
 var Bounds = require('../Bounds');
+var GuiConnector = require('../../../GuiConnector');
 var Functions = require('../../../Functions');
 var MapCanvas = require('../MapCanvas');
 var Point = require('../Point');
@@ -13,19 +14,19 @@ function OpenLayerCanvas(element, options) {
   ol = require('openlayers/dist/ol-debug');
 
   MapCanvas.call(this, element, options);
+  $(element).css("background-color", "#e4e2de");
   var self = this;
   self.setOpenLayersMap(new ol.Map({
     target: element,
-    layers: [
-      new ol.layer.Tile({
-        source: new ol.source.OSM()
-      })
-    ],
+    layers: self.createLayers(options),
     view: new ol.View({
-      center: [0, 0],
-      zoom: 0
+      center: self.fromPointToProjection(options.center),
+      zoom: options.zoom,
+      minZoom: options.minZoom,
+      maxZoom: options.maxZoom
     })
   }));
+
 }
 
 OpenLayerCanvas.prototype = Object.create(MapCanvas.prototype);
@@ -35,6 +36,57 @@ OpenLayerCanvas.prototype.setOpenLayersMap = function (map) {
   this._map = map;
 };
 
+OpenLayerCanvas.prototype.createLayers = function (options) {
+  var self = this;
+  var result = [];
+  console.log(options);
+  self._layers = [];
+  options.backgroundOverlays.forEach(function (overlay, index) {
+    var layer = new ol.layer.Tile({
+      visible: (index === 0),
+      source: new ol.source.XYZ({
+        minZoom: options.minZoom,
+        maxZoom: options.maxZoom,
+        tileLoadFunction: function (imageTile, src) {
+          if (src !== null) {
+            imageTile.getImage().src = src;
+          }
+        },
+        tileUrlFunction: function (coordinate) {
+
+          var zoom = coordinate[0];
+          // we have 1 tile on self.getConfiguration().MIN_ZOOM and
+          // therefore must limit tails according to this
+          /* jshint bitwise: false */
+          var maxTileRange = 1 << (zoom - self.getMinZoom());
+          var maxTileXRange = maxTileRange;
+          var maxTileYRange = maxTileRange;
+
+          //transform the coordinates to something that was used in google maps
+          var x = coordinate[1];
+          var y = -coordinate[2] - 1;
+
+          var width = self.getWidth();
+          var height = self.getHeight();
+          if (width > height) {
+            maxTileYRange = height / width * maxTileRange;
+          } else if (width < height) {
+            maxTileXRange = width / height * maxTileRange;
+          }
+          if (y < 0 || y >= maxTileYRange || x < 0 || x >= maxTileXRange) {
+            return null;
+          }
+
+          return overlay.directory + "/" + zoom + "/" + x + "/" + y + ".PNG";
+        }
+      })
+    });
+    self._layers[overlay.id] = layer;
+    result.push(layer);
+  });
+  return result;
+};
+
 OpenLayerCanvas.prototype.setOptions = function (options) {
   this._options = options;
 };
@@ -107,13 +159,30 @@ OpenLayerCanvas.prototype.setZoom = function (zoom) {
  * @returns {string}
  */
 OpenLayerCanvas.prototype.getBackgroundId = function () {
-  logger.fatal("Not implemented");
-  return this._backgroundId;
+  var layers = this._layers;
+  for (var id in layers) {
+    if (layers.hasOwnProperty(id)) {
+      var layer = layers[id];
+      if (layer.getVisible()) {
+        return id;
+      }
+    }
+  }
+  return null;
 };
 
 OpenLayerCanvas.prototype.setBackgroundId = function (backgroundId) {
-  this._backgroundId = backgroundId;
-  logger.fatal("Not implemented");
+  backgroundId = parseInt(backgroundId);
+  console.log("background: ", backgroundId);
+  var layers = this._layers;
+  for (var id in layers) {
+    if (layers.hasOwnProperty(id)) {
+      var layer = layers[id];
+      var visible = (parseInt(id) === backgroundId);
+      console.log(id, "show", visible);
+      layer.setVisible(visible);
+    }
+  }
 };
 
 /**
@@ -161,8 +230,17 @@ OpenLayerCanvas.prototype.removeSelection = function () {
   logger.fatal("Not implemented");
 };
 
-MapCanvas.prototype.triggerListeners = function (type, data) {
+OpenLayerCanvas.prototype.triggerListeners = function (type, data) {
   logger.fatal("Not implemented");
 };
 
+OpenLayerCanvas.prototype.fromPointToProjection = function (point) {
+  var latLng = this.pointToLatLng(point);
+  return ol.proj.fromLonLat([latLng[1], latLng[0]]);
+};
+OpenLayerCanvas.prototype.fromProjectionToPoint = function (projection) {
+  var lngLat = ol.proj.toLonLat(projection);
+  return this.latLngToPoint([lngLat[1], lngLat[0]]);
+};
+
 module.exports = OpenLayerCanvas;
diff --git a/frontend-js/src/test/js/google-map-mock.js b/frontend-js/src/test/js/google-map-mock.js
index da044a1d7b..7871ff5cfd 100644
--- a/frontend-js/src/test/js/google-map-mock.js
+++ b/frontend-js/src/test/js/google-map-mock.js
@@ -71,6 +71,7 @@ var google = {
       this.lng = function () {
         return this.longitude;
       };
+      return this;
     },
     LatLngBounds: function (ne, sw) {
       var data = {
diff --git a/frontend-js/src/test/js/map/canvas/OpenLayers/OpenLayersCanvas-test.js b/frontend-js/src/test/js/map/canvas/OpenLayers/OpenLayersCanvas-test.js
new file mode 100644
index 0000000000..f7f009e785
--- /dev/null
+++ b/frontend-js/src/test/js/map/canvas/OpenLayers/OpenLayersCanvas-test.js
@@ -0,0 +1,52 @@
+"use strict";
+require("../../../mocha-config");
+
+// noinspection JSUnusedLocalSymbols
+var logger = require('../../../logger');
+
+var OpenLayerCanvas = require('../../../../../main/js/map/canvas/OpenLayers/OpenLayerCanvas');
+var Point = require('../../../../../main/js/map/canvas/Point');
+
+var SelectionContextMenu = require('../../../../../main/js/gui/SelectionContextMenu');
+
+var chai = require('chai');
+var assert = chai.assert;
+
+describe('OpenLayerCanvas', function () {
+  var testOptions = {
+    center: new Point(0, 0),
+    tileSize: 256,
+    width: 300,
+    height: 600,
+    backgroundOverlays: [{
+      id: 1,
+      name: "overlay",
+      directory: "overlay_dir"
+    }]
+  };
+  it("constructor", function () {
+    var canvas = new OpenLayerCanvas(testDiv, testOptions);
+    assert.ok(canvas.pixelOrigin_);
+    assert.ok(canvas.pixelsPerLonDegree_);
+    assert.ok(canvas.pixelsPerLonRadian_);
+    assert.ok(canvas.zoomFactor);
+
+  });
+
+  it("Point - Projection conversion", function () {
+
+    var canvas = new OpenLayerCanvas(testDiv, testOptions);
+
+    var x = 199;
+    var y = 110;
+    var point = new Point(x, y);
+
+    var coordinates = canvas.fromPointToProjection(point);
+
+    var point2 = canvas.fromProjectionToPoint(coordinates);
+
+    assert.closeTo(point2.x, point.x, 0.5, "X coordinate is invalid after transformation");
+    assert.closeTo(point2.y, point.y, 0.5, "Y coordinate is invalid after transformation");
+
+  });
+});
-- 
GitLab