diff --git a/annotation/src/test/java/lcsb/mapviewer/annotation/services/ModelAnnotatorTest.java b/annotation/src/test/java/lcsb/mapviewer/annotation/services/ModelAnnotatorTest.java index ced49d86ec0d0a3a8de3791e0fe7191e3c22d467..b343f67f1db7df65a5a46d67d28dcc489d090d83 100644 --- a/annotation/src/test/java/lcsb/mapviewer/annotation/services/ModelAnnotatorTest.java +++ b/annotation/src/test/java/lcsb/mapviewer/annotation/services/ModelAnnotatorTest.java @@ -251,7 +251,7 @@ public class ModelAnnotatorTest extends AnnotationTestFunctions { Reaction reaction = new Reaction(); reaction.addMiriamData(new MiriamData(MiriamType.PUBMED, "12345")); List<ImproperAnnotations> result = modelAnnotator.findImproperAnnotations(reaction, list); - assertEquals(0, result.size()); + assertEquals("Unexpected improper annotations found: "+result, 0, result.size()); } catch (Exception e) { e.printStackTrace(); throw e; diff --git a/frontend-js/src/main/js/ServerConnector.js b/frontend-js/src/main/js/ServerConnector.js index b104a2e9594062f4e1c88a3f90e6cf8d902f96a0..21c3c3ca56bc926bc58855ef363994536867b65d 100644 --- a/frontend-js/src/main/js/ServerConnector.js +++ b/frontend-js/src/main/js/ServerConnector.js @@ -1754,7 +1754,7 @@ ServerConnector.getSessionData = function (project) { * @param {number} [params.count] * @param {string[]} [params.type] * - * @returns {PromiseLike<IdentifiedElement[]>} + * @returns {PromiseLike<IdentifiedElement[]>|Promise<IdentifiedElement[]>} */ ServerConnector.getClosestElementsByCoordinates = function (params) { var self = this; diff --git a/frontend-js/src/main/js/map/overlay/AbstractDbOverlay.js b/frontend-js/src/main/js/map/overlay/AbstractDbOverlay.js index 828dde5883177fab14ab5c3d23ad59d5d60153ce..46e8a6d43509b3c1f1253e1d6daaddb5b2c8fa2d 100644 --- a/frontend-js/src/main/js/map/overlay/AbstractDbOverlay.js +++ b/frontend-js/src/main/js/map/overlay/AbstractDbOverlay.js @@ -111,7 +111,7 @@ AbstractDbOverlay.prototype.encodeQuery = function (type, arg0, arg1, arg2) { /** * * @param {string} query - * @returns {any} + * @returns {Object} */ AbstractDbOverlay.prototype.decodeQuery = function (query) { return JSON.parse(query); @@ -231,6 +231,7 @@ AbstractDbOverlay.prototype.refresh = function () { throw new Error("Refreshing shouldn't be called"); }; +// noinspection JSUnusedLocalSymbols /** * * @param {IdentifiedElement} element @@ -311,6 +312,14 @@ AbstractDbOverlay.prototype.getMap = function () { return this._map; }; +/** + * + * @returns {Configuration} + */ +AbstractDbOverlay.prototype.getConfiguration = function () { + return this.getMap().getConfiguration(); +}; + /** * * @param {string} name diff --git a/frontend-js/src/main/js/map/overlay/SearchDbOverlay.js b/frontend-js/src/main/js/map/overlay/SearchDbOverlay.js index aba0743abf24bdebd69d926d7cb5705308649560..68c15f0929562f79e611c9f576ee69f54d96edf6 100644 --- a/frontend-js/src/main/js/map/overlay/SearchDbOverlay.js +++ b/frontend-js/src/main/js/map/overlay/SearchDbOverlay.js @@ -3,13 +3,16 @@ /* exported logger */ var logger = require('../../logger'); +var Functions = require('../../Functions'); var Promise = require("bluebird"); var AbstractDbOverlay = require('./AbstractDbOverlay'); var Alias = require('../data/Alias'); +var ConfigurationType = require('../../ConfigurationType'); var IdentifiedElement = require('../data/IdentifiedElement'); var InvalidArgumentException = require('../../InvalidArgumentError'); +var Point = require('../canvas/Point'); var Reaction = require('../data/Reaction'); var SearchBioEntityGroup = require('../data/SearchBioEntityGroup'); @@ -91,14 +94,45 @@ SearchDbOverlay.prototype.getElementsByQuery = function (query) { }); }; + +/** + * + * @param {Point} coordinates + * @param {IdentifiedElement} compartment + * @param {number} maxDistance + * @returns {Promise} + * @private + */ +SearchDbOverlay.prototype._returnCompartmentIfClickOnBorder = function (coordinates, compartment, maxDistance) { + var model = this.getMap().getSubmapById(compartment.getModelId()).getModel(); + + return model.getByIdentifiedElement(compartment).then(function (result) { + var p1 = new Point(result.getX(), result.getY()); + var p2 = new Point(result.getX() + result.getWidth(), result.getY()); + var p3 = new Point(result.getX() + result.getWidth(), result.getY() + result.getHeight()); + var p4 = new Point(result.getX(), result.getY() + result.getHeight()); + var distance = Functions.distance(coordinates, {start: p1, end: p2}); + distance = Math.min(distance, Functions.distance(coordinates, {start: p2, end: p3})); + distance = Math.min(distance, Functions.distance(coordinates, {start: p3, end: p4})); + distance = Math.min(distance, Functions.distance(coordinates, {start: p4, end: p1})); + + if (maxDistance>distance) { + return compartment; + } else { + return undefined; + } + }); +}; + /** * * @param {IdentifiedElement} identifiedElement * @param {number} zoomLevel + * @param {Point} coordinates * @returns {Promise} * @private */ -SearchDbOverlay.prototype._getFirstVisibleParentOrObject = function (identifiedElement, zoomLevel) { +SearchDbOverlay.prototype._getFirstVisibleParentOrObject = function (identifiedElement, zoomLevel, coordinates) { if (identifiedElement === undefined) { return Promise.resolve(); } @@ -109,7 +143,8 @@ SearchDbOverlay.prototype._getFirstVisibleParentOrObject = function (identifiedE if (fullElement instanceof Alias) { //transparent compartments shouldn't be clickable if (fullElement.getTransparencyLevel() <= zoomLevel && fullElement.getType() === "Compartment") { - return undefined; + var maxDistance = self.computeMaxDistance(model, zoomLevel); + return self._returnCompartmentIfClickOnBorder(coordinates, identifiedElement, maxDistance); } } return identifiedElement; @@ -128,7 +163,7 @@ SearchDbOverlay.prototype._getFirstVisibleParentOrObject = function (identifiedE type: "ALIAS", modelId: identifiedElement.getModelId() }); - return self._getFirstVisibleParentOrObject(parent, zoomLevel); + return self._getFirstVisibleParentOrObject(parent, zoomLevel, coordinates); } else { logger.warn("Cannot find visible parent for object. (zoomLevel=" + zoomLevel + ")"); logger.warn(fullElement); @@ -138,12 +173,58 @@ SearchDbOverlay.prototype._getFirstVisibleParentOrObject = function (identifiedE }); }; +/** + * + * @param {MapModel} model + * @param {number} zoom + * @returns {number} + */ +SearchDbOverlay.prototype.computeMaxDistance = function (model, zoom) { + var distance = parseFloat(this.getConfiguration().getOption(ConfigurationType.SEARCH_DISTANCE).getValue()); + var maxZoom = model.getMaxZoom(); + var zoomDiff = maxZoom - zoom; + for (var i = 0; i < zoomDiff; i++) { + distance = distance * 1.5; + } + return distance; +}; + +/** + * + * @param {Point} coordinates + * @param {number} zoom + * @param {MapModel} model + * @param {number} maxDistance + * @returns {PromiseLike<T | never> | Promise<T | never>} + */ +SearchDbOverlay.prototype.findCompartmentByCoordinates = function (coordinates, zoom, model, maxDistance) { + var self = this; + return ServerConnector.getClosestElementsByCoordinates({ + modelId: model.getId(), + coordinates: coordinates, + count: 1, + type: ["Compartment"] + }).then(function (elements) { + var nestedOverlay = "Pathways and compartments"; + if (elements.length === 0) { + return undefined; + } else { + if (self.getMap().getBackgroundDataOverlay().getName() === nestedOverlay) { + return self._getFirstVisibleParentOrObject(elements[0], zoom - model.getMinZoom(), coordinates); + } else { + return self._returnCompartmentIfClickOnBorder(coordinates, elements[0], maxDistance); + } + } + }); +}; + /** * * @param {Object} params * @param {number} params.modelId * @param {Point} params.coordinates * @param {number} params.zoom + * @param {boolean} [params.fitBounds] * @returns {Promise| PromiseLike} */ SearchDbOverlay.prototype.searchByCoordinates = function (params) { @@ -167,31 +248,23 @@ SearchDbOverlay.prototype.searchByCoordinates = function (params) { }); } else { var searchResult = null; - var maxDistance; var map = self.getMap().getSubmapById(modelId); if (map === null) { return Promise.reject(new InvalidArgumentException("model doesn't exist for modelId: " + modelId)); } var model = map.getModel(); - return ServerConnector.getMaxSearchDistance().then(function (distance) { - var maxZoom = model.getMaxZoom(); - var zoomDiff = maxZoom - zoom; - for (var i = 0; i < zoomDiff; i++) { - distance = distance * 1.5; - } - maxDistance = distance; - return ServerConnector.getClosestElementsByCoordinates({ - modelId: modelId, - coordinates: coordinates, - count: 1 - }); + var maxDistance = self.computeMaxDistance(model, zoom); + return ServerConnector.getClosestElementsByCoordinates({ + modelId: modelId, + coordinates: coordinates, + count: 1 }).then(function (elements) { var nestedOverlay = "Pathways and compartments"; if (elements.length === 0) { return undefined; } else { if (self.getMap().getBackgroundDataOverlay().getName() === nestedOverlay) { - return self._getFirstVisibleParentOrObject(elements[0], zoom - model.getMinZoom()); + return self._getFirstVisibleParentOrObject(elements[0], zoom - model.getMinZoom(), coordinates); } else { return elements[0]; } @@ -224,23 +297,7 @@ SearchDbOverlay.prototype.searchByCoordinates = function (params) { if (distance <= maxDistance) { self._elementsByQuery[query] = searchResult; } else { - return ServerConnector.getClosestElementsByCoordinates({ - modelId: modelId, - coordinates: coordinates, - count: 1, - type: ["Compartment"] - }).then(function (elements) { - var nestedOverlay = "Pathways and compartments"; - if (elements.length === 0) { - return undefined; - } else { - if (self.getMap().getBackgroundDataOverlay().getName() === nestedOverlay) { - return self._getFirstVisibleParentOrObject(elements[0], zoom - model.getMinZoom()); - } else { - return undefined; - } - } - }).then(function (visibleObject) { + return self.findCompartmentByCoordinates(coordinates, zoom, model, maxDistance).then(function (visibleObject) { if (visibleObject !== undefined) { self._elementsByQuery[query] = [visibleObject]; } else { @@ -372,7 +429,7 @@ SearchDbOverlay.prototype.getIdentifiedElements = function () { /** * * @param element - * @returns {Promise<BioEntity[]>} + * @returns {Promise<BioEntity>} */ SearchDbOverlay.prototype.getDetailDataByIdentifiedElement = function (element) { if (element.getType() === "POINT") { diff --git a/frontend-js/src/test/js/helper.js b/frontend-js/src/test/js/helper.js index 7d7fb45b1ff4f1023dcc6f4e2f5905a3121d025e..ab4c42c742d31a8ddbdfe2f3cfc6cb7af7cb7a2b 100644 --- a/frontend-js/src/test/js/helper.js +++ b/frontend-js/src/test/js/helper.js @@ -462,7 +462,7 @@ Helper.prototype.createAbstractCustomMap = function () { /** * - * @param project + * @param {Project} [project] * @returns {CustomMap} */ Helper.prototype.createCustomMap = function (project) { diff --git a/frontend-js/src/test/js/map/overlay/SearchDbOverlay-test.js b/frontend-js/src/test/js/map/overlay/SearchDbOverlay-test.js index f88dfeda4cdd0a634c2d59dab25d5956d588e91e..1ff3665b028863ea6d0ebbcbc82ee0437ef61eec 100644 --- a/frontend-js/src/test/js/map/overlay/SearchDbOverlay-test.js +++ b/frontend-js/src/test/js/map/overlay/SearchDbOverlay-test.js @@ -153,6 +153,28 @@ describe('SearchDbOverlay', function () { }); }); + it("on compartment border", function () { + helper.setUrl("http://test/?id=complex_model_with_submaps"); + var map; + return ServerConnector.getProject().then(function (project) { + map = helper.createCustomMap(project); + return map.openDataOverlay(14961); + }).then(function () { + + var searchDb = helper.createSearchDbOverlay(map); + + var searchParams = { + modelId: map.getModel().getId(), + coordinates: new Point(104.36, 182.81), + zoom: 4 + }; + return searchDb.searchByCoordinates(searchParams); + }).then(function (result) { + assert.equal(result.length, 1); + }); + + }); + it("on hidden nested object", function () { return ServerConnector.getProject().then(function (project) { @@ -242,4 +264,5 @@ describe('SearchDbOverlay', function () { }); -}); +}) +; diff --git a/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/16728/bioEntities.search/coordinates=104.36,182.81&count=1&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/16728/bioEntities.search/coordinates=104.36,182.81&count=1&token=MOCK_TOKEN_ID& new file mode 100644 index 0000000000000000000000000000000000000000..efcec7f8da674b402d62d9b674c5bfc256cc12ea --- /dev/null +++ b/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/16728/bioEntities.search/coordinates=104.36,182.81&count=1&token=MOCK_TOKEN_ID& @@ -0,0 +1 @@ +[{"id":345331,"modelId":16728,"type":"ALIAS"}] \ No newline at end of file diff --git a/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/16728/bioEntities.search/coordinates=104.36,182.81&count=1&type=Compartment&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/16728/bioEntities.search/coordinates=104.36,182.81&count=1&type=Compartment&token=MOCK_TOKEN_ID& new file mode 100644 index 0000000000000000000000000000000000000000..46a291bd26e929ef7376e3ca227948f78c85d741 --- /dev/null +++ b/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/16728/bioEntities.search/coordinates=104.36,182.81&count=1&type=Compartment&token=MOCK_TOKEN_ID& @@ -0,0 +1 @@ +[{"id":345323,"modelId":16728,"type":"ALIAS"}] \ No newline at end of file diff --git a/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=345323&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=345323&token=MOCK_TOKEN_ID& new file mode 100644 index 0000000000000000000000000000000000000000..392b44d0f9c421325d6eec0f73a72fb35c40b658 --- /dev/null +++ b/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/columns=id,bounds,modelId,linkedSubmodel&id=345323&token=MOCK_TOKEN_ID& @@ -0,0 +1 @@ +[{"bounds":{"height":142.0,"width":144.0,"x":35.0,"y":45.0},"id":345323,"linkedSubmodel":null,"modelId":16728}] \ No newline at end of file diff --git a/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/id=345323&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/id=345323&token=MOCK_TOKEN_ID& new file mode 100644 index 0000000000000000000000000000000000000000..10efe849d6ce6ecef22eeb1cfedfc94ef7ec6d99 --- /dev/null +++ b/frontend-js/testFiles/apiCalls/projects/complex_model_with_submaps/models/all/bioEntities/elements/id=345323&token=MOCK_TOKEN_ID& @@ -0,0 +1 @@ +[{"abbreviation":null,"boundaryCondition":null,"bounds":{"height":142.0,"width":144.0,"x":35.0,"y":45.0},"compartmentId":null,"complexId":null,"constant":null,"elementId":"ca1","formerSymbols":[],"formula":null,"fullName":null,"hierarchyVisibilityLevel":"0","id":345323,"initialAmount":null,"initialConcentration":null,"linkedSubmodel":null,"modelId":16728,"name":"c1","notes":"","other":{"modifications":[],"structuralState":null,"structures":{}},"references":[],"symbol":null,"synonyms":[],"transparencyLevel":"2","type":"Compartment"}] \ No newline at end of file