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