diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectController.java b/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectController.java index c2171f9fb93d19d327cf7e73f3fde0e92724ce88..974a5950987f032f73315b455f72e281681ed551 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectController.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectController.java @@ -22,13 +22,8 @@ import org.springframework.web.bind.annotation.RestController; import lcsb.mapviewer.api.BaseController; import lcsb.mapviewer.api.ObjectNotFoundException; import lcsb.mapviewer.api.QueryException; -import lcsb.mapviewer.commands.CommandExecutionException; import lcsb.mapviewer.common.Configuration; -import lcsb.mapviewer.converter.ConverterException; -import lcsb.mapviewer.converter.graphics.DrawingException; import lcsb.mapviewer.model.cache.FileEntry; -import lcsb.mapviewer.model.map.InconsistentModelException; -import lcsb.mapviewer.model.map.layout.InvalidColorSchemaException; import lcsb.mapviewer.services.SecurityException; @RestController @@ -120,50 +115,6 @@ public class ProjectController extends BaseController { .body(file.getFileContent()); } - @RequestMapping(value = "/projects/{projectId}/models/{modelId}:downloadImage", method = { - RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE }) - public ResponseEntity<byte[]> getModelAsImage(// - @CookieValue(value = Configuration.AUTH_TOKEN) String token, // - @PathVariable(value = "projectId") String projectId, // - @PathVariable(value = "modelId") String modelId, // - @RequestParam(value = "handlerClass") String handlerClass, // - @RequestParam(value = "backgroundOverlayId", defaultValue = "") String backgroundOverlayId, // - @RequestParam(value = "overlayIds", defaultValue = "") String overlayIds, // - @RequestParam(value = "zoomLevel", defaultValue = "") String zoomLevel, // - @RequestParam(value = "polygonString", defaultValue = "") String polygonString// - ) throws SecurityException, QueryException, IOException, InvalidColorSchemaException, CommandExecutionException, - DrawingException { - - FileEntry file = projectController.getModelAsImage(token, projectId, modelId, handlerClass, backgroundOverlayId, - overlayIds, zoomLevel, polygonString); - MediaType type = MediaType.APPLICATION_OCTET_STREAM; - return ResponseEntity.ok().contentLength(file.getFileContent().length).contentType(type) - .header("Content-Disposition", "attachment; filename=" + file.getOriginalFileName()) - .body(file.getFileContent()); - } - - @RequestMapping(value = "/projects/{projectId}/models/{modelId}:downloadModel", method = { - RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE }) - public ResponseEntity<byte[]> getModelAsModelFile(// - @CookieValue(value = Configuration.AUTH_TOKEN) String token, // - @PathVariable(value = "projectId") String projectId, // - @PathVariable(value = "modelId") String modelId, // - @RequestParam(value = "handlerClass") String handlerClass, // - @RequestParam(value = "backgroundOverlayId", defaultValue = "") String backgroundOverlayId, // - @RequestParam(value = "overlayIds", defaultValue = "") String overlayIds, // - @RequestParam(value = "zoomLevel", defaultValue = "") String zoomLevel, // - @RequestParam(value = "polygonString", defaultValue = "") String polygonString// - ) throws SecurityException, QueryException, IOException, InvalidColorSchemaException, CommandExecutionException, - ConverterException, InconsistentModelException { - - FileEntry file = projectController.getModelAsModelFile(token, projectId, modelId, handlerClass, backgroundOverlayId, - overlayIds, zoomLevel, polygonString); - MediaType type = MediaType.APPLICATION_OCTET_STREAM; - return ResponseEntity.ok().contentLength(file.getFileContent().length).contentType(type) - .header("Content-Disposition", "attachment; filename=" + file.getOriginalFileName()) - .body(file.getFileContent()); - } - @RequestMapping(value = "/projects/{projectId}/logs/", method = { RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE }) public Map<String, Object> getLogs(// diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectRestImpl.java b/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectRestImpl.java index b0d4cee6b5a1daaa65835f322cb113bcb8e89719..528c189b5d01beacf58f447900357a3f34efa075 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectRestImpl.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectRestImpl.java @@ -1,19 +1,12 @@ package lcsb.mapviewer.api.projects; -import java.awt.Color; -import java.awt.geom.Path2D; -import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; import java.io.Serializable; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; -import java.util.Collection; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedHashMap; @@ -22,7 +15,6 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; -import org.apache.commons.io.IOUtils; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; @@ -34,21 +26,9 @@ import lcsb.mapviewer.api.ObjectNotFoundException; import lcsb.mapviewer.api.OperationNotAllowedException; import lcsb.mapviewer.api.QueryException; import lcsb.mapviewer.api.projects.models.publications.PublicationsRestImpl; -import lcsb.mapviewer.commands.ClearColorModelCommand; -import lcsb.mapviewer.commands.ColorExtractor; -import lcsb.mapviewer.commands.ColorModelCommand; -import lcsb.mapviewer.commands.CommandExecutionException; -import lcsb.mapviewer.commands.CopyCommand; -import lcsb.mapviewer.commands.SetFixedHierarchyLevelCommand; -import lcsb.mapviewer.commands.SubModelCommand; -import lcsb.mapviewer.common.Configuration; import lcsb.mapviewer.common.exception.InvalidArgumentException; import lcsb.mapviewer.common.exception.NotImplementedException; -import lcsb.mapviewer.converter.ConverterException; import lcsb.mapviewer.converter.IConverter; -import lcsb.mapviewer.converter.graphics.AbstractImageGenerator.Params; -import lcsb.mapviewer.converter.graphics.DrawingException; -import lcsb.mapviewer.converter.graphics.ImageGenerators; import lcsb.mapviewer.converter.zip.ImageZipEntryFile; import lcsb.mapviewer.converter.zip.LayoutZipEntryFile; import lcsb.mapviewer.converter.zip.ModelZipEntryFile; @@ -58,7 +38,6 @@ import lcsb.mapviewer.model.cache.FileEntry; import lcsb.mapviewer.model.cache.UploadedFileEntry; import lcsb.mapviewer.model.graphics.MapCanvasType; import lcsb.mapviewer.model.map.BioEntity; -import lcsb.mapviewer.model.map.InconsistentModelException; import lcsb.mapviewer.model.map.MiriamData; import lcsb.mapviewer.model.map.MiriamType; import lcsb.mapviewer.model.map.OverviewImage; @@ -66,9 +45,6 @@ import lcsb.mapviewer.model.map.OverviewImageLink; import lcsb.mapviewer.model.map.OverviewLink; import lcsb.mapviewer.model.map.OverviewModelLink; import lcsb.mapviewer.model.map.OverviewSearchLink; -import lcsb.mapviewer.model.map.layout.ColorSchema; -import lcsb.mapviewer.model.map.layout.InvalidColorSchemaException; -import lcsb.mapviewer.model.map.layout.Layout; import lcsb.mapviewer.model.map.model.Model; import lcsb.mapviewer.model.map.model.SubmodelType; import lcsb.mapviewer.model.map.reaction.Reaction; @@ -79,24 +55,12 @@ import lcsb.mapviewer.model.user.User; import lcsb.mapviewer.persist.dao.ProjectDao; import lcsb.mapviewer.persist.dao.cache.UploadedFileEntryDao; import lcsb.mapviewer.services.SecurityException; -import lcsb.mapviewer.services.interfaces.ILayoutService; import lcsb.mapviewer.services.interfaces.IProjectService; -import lcsb.mapviewer.services.utils.ColorSchemaReader; import lcsb.mapviewer.services.utils.CreateProjectParams; -import lcsb.mapviewer.services.utils.data.BuildInLayout; @Transactional(value = "txManager") public class ProjectRestImpl extends BaseRestImpl { - /** - * Constant defining size of the array returned by - * {@link PathIterator#currentSegment(double[])} method. More information can be - * found <a href= - * "http://docs.oracle.com/javase/7/docs/api/java/awt/geom/PathIterator.html#currentSegment(double[])" - * >here</a> - */ - private static final int PATH_ITERATOR_SEGMENT_SIZE = 6; - /** * Default class logger. */ @@ -105,9 +69,6 @@ public class ProjectRestImpl extends BaseRestImpl { @Autowired private PublicationsRestImpl publicationsRestImpl; - @Autowired - private ILayoutService layoutService; - @Autowired private IProjectService projectService; @@ -240,224 +201,6 @@ public class ProjectRestImpl extends BaseRestImpl { return project.getInputData(); } - public FileEntry getModelAsImage(String token, String projectId, String modelId, String handlerClass, - String backgroundOverlayId, String overlayIds, String zoomLevel, String polygonString) throws SecurityException, - QueryException, IOException, InvalidColorSchemaException, CommandExecutionException, DrawingException { - User user = getUserService().getUserByToken(token); - - Model topModel = getModelService().getLastModelByProjectId(projectId, token); - if (topModel == null) { - throw new ObjectNotFoundException("Project with given id doesn't exist"); - } - - Model originalModel = topModel.getSubmodelById(modelId); - - if (originalModel == null) { - throw new ObjectNotFoundException("Model with given id doesn't exist"); - } - - Layout overlay = null; - if (!backgroundOverlayId.equals("")) { - overlay = topModel.getLayoutByIdentifier(Integer.valueOf(backgroundOverlayId)); - - if (overlay == null) { - throw new ObjectNotFoundException("Unknown overlay in model. Layout.id=" + backgroundOverlayId); - } - } else { - if (topModel.getLayouts().size() > 0) { - overlay = topModel.getLayouts().get(0); - } - } - - Model colorModel = new CopyCommand(originalModel).execute(); - if (overlay != null) { - if (overlay.getInputData() != null) { - ColorSchemaReader reader = new ColorSchemaReader(); - Collection<ColorSchema> schemas = reader.readColorSchema(overlay.getInputData().getFileContent()); - - new ColorModelCommand(colorModel, schemas, getUserService().getColorExtractorForUser(user)).execute(); - } else if (overlay.getTitle().equals(BuildInLayout.CLEAN.getTitle())) { - // this might not return true if we change CLEAN.title in future... - - // if it's clean then remove coloring - new ClearColorModelCommand(colorModel).execute(); - } else if (overlay.isHierarchicalView()) { - new SetFixedHierarchyLevelCommand(colorModel, overlay.getHierarchyViewLevel()).execute(); - } - } - - Integer level = Configuration.MIN_ZOOM_LEVEL; - if (!zoomLevel.equals("")) { - level = Integer.valueOf(zoomLevel); - } - - Path2D polygon = stringToPolygon(polygonString, colorModel); - - Double minX = originalModel.getWidth(); - Double minY = originalModel.getHeight(); - Double maxX = 0.0; - Double maxY = 0.0; - - PathIterator pathIter = polygon.getPathIterator(null); - while (!pathIter.isDone()) { - final double[] segment = new double[PATH_ITERATOR_SEGMENT_SIZE]; - if (pathIter.currentSegment(segment) != PathIterator.SEG_CLOSE) { - minX = Math.min(minX, segment[0]); - maxX = Math.max(maxX, segment[0]); - minY = Math.min(minY, segment[1]); - maxY = Math.max(maxY, segment[1]); - } - pathIter.next(); - } - - maxX = Math.min(originalModel.getWidth(), maxX); - maxY = Math.min(originalModel.getHeight(), maxY); - minX = Math.max(0.0, minX); - minY = Math.max(0.0, minY); - - Double scale = Math.max(originalModel.getHeight(), originalModel.getWidth()) / (originalModel.getTileSize()); - - for (int i = level; i > Configuration.MIN_ZOOM_LEVEL; i--) { - scale /= 2; - } - - ColorExtractor colorExtractor = getUserService().getColorExtractorForUser(user); - - Params params = new Params().// - x(minX).// - y(minY).// - height((maxY - minY) / scale).// - width((maxX - minX) / scale).// - level(level - Configuration.MIN_ZOOM_LEVEL).// - nested(false).// automatically set nested view as invalid - scale(scale).// - colorExtractor(colorExtractor).// - sbgn(topModel.getProject().isSbgnFormat()).// - model(colorModel); - if (overlay != null) { - params.nested(overlay.isHierarchicalView()); - } - List<Integer> visibleLayoutIds = deserializeIdList(overlayIds); - for (Integer integer : visibleLayoutIds) { - Map<Object, ColorSchema> map = layoutService.getElementsForLayout(colorModel, integer, token); - params.addVisibleLayout(map); - } - - ImageGenerators imageGenerator = new ImageGenerators(); - String extension = imageGenerator.getExtension(handlerClass); - File file = File.createTempFile("map", "." + extension); - - imageGenerator.generate(handlerClass, params, file.getAbsolutePath()); - - UploadedFileEntry entry = new UploadedFileEntry(); - entry.setOriginalFileName("map." + extension); - entry.setFileContent(IOUtils.toByteArray(new FileInputStream(file))); - entry.setLength(entry.getFileContent().length); - file.delete(); - return entry; - - } - - private Path2D stringToPolygon(String polygonString, Model colorModel) { - String[] stringPointArray = polygonString.split(";"); - - List<Point2D> points = new ArrayList<>(); - for (String string : stringPointArray) { - if (!string.trim().equals("")) { - double x = Double.valueOf(string.split(",")[0]); - double y = Double.valueOf(string.split(",")[1]); - points.add(new Point2D.Double(x, y)); - } - } - - if (points.size() <= 2) { - points.clear(); - points.add(new Point2D.Double(0, 0)); - points.add(new Point2D.Double(colorModel.getWidth(), 0)); - points.add(new Point2D.Double(colorModel.getWidth(), colorModel.getHeight())); - points.add(new Point2D.Double(0, colorModel.getHeight())); - } - - Path2D polygon = new Path2D.Double(); - polygon.moveTo(points.get(0).getX(), points.get(0).getY()); - for (int i = 1; i < points.size(); i++) { - Point2D point = points.get(i); - polygon.lineTo(point.getX(), point.getY()); - } - polygon.closePath(); - return polygon; - } - - private List<Integer> deserializeIdList(String overlayIds) { - List<Integer> result = new ArrayList<>(); - String[] tmp = overlayIds.split(","); - for (String string : tmp) { - if (!string.equals("")) { - result.add(Integer.valueOf(string)); - } - } - return result; - } - - public FileEntry getModelAsModelFile(String token, String projectId, String modelId, String handlerClass, - String backgroundOverlayId, String overlayIds, String zoomLevel, String polygonString) - throws SecurityException, QueryException, IOException, InvalidColorSchemaException, CommandExecutionException, - ConverterException, InconsistentModelException { - User user = getUserService().getUserByToken(token); - Model topModel = getModelService().getLastModelByProjectId(projectId, token); - if (topModel == null) { - throw new ObjectNotFoundException("Project with given id doesn't exist"); - } - - Model originalModel = topModel.getSubmodelById(modelId); - - if (originalModel == null) { - throw new ObjectNotFoundException("Model with given id doesn't exist"); - } - - Path2D polygon = stringToPolygon(polygonString, originalModel); - - // create model bounded by the polygon - SubModelCommand subModelCommand = new SubModelCommand(originalModel, polygon); - Model part = subModelCommand.execute(); - - // Get list of overlay ids - String[] overlayIdsList; - if (overlayIds == null || overlayIds.trim().isEmpty()) { - overlayIdsList = new String[0]; - } else { - overlayIdsList = overlayIds.split(","); - } - // Remove all colors - if (overlayIdsList.length > 0) { - - for (Element element : part.getElements()) { - element.setColor(Color.WHITE); - } - } - // Color with overlays - for (String overlayId : overlayIdsList) { - Layout overlay = layoutService.getLayoutById(Integer.parseInt(overlayId.trim()), token); - - ColorSchemaReader reader = new ColorSchemaReader(); - Collection<ColorSchema> schemas = reader.readColorSchema(overlay.getInputData().getFileContent()); - - new ColorModelCommand(part, schemas, getUserService().getColorExtractorForUser(user)).execute(); - } - - IConverter parser = getModelParser(handlerClass); - InputStream is = parser.exportModelToInputStream(part); - - String fileExtension = parser.getFileExtension(); - - UploadedFileEntry entry = new UploadedFileEntry(); - entry.setOriginalFileName("model." + fileExtension); - entry.setFileContent(IOUtils.toByteArray(is)); - entry.setLength(entry.getFileContent().length); - return entry; - - } - public Map<String, Object> getStatistics(String projectId, String token) throws SecurityException, ObjectNotFoundException { Map<String, Object> result = new TreeMap<>(); @@ -740,7 +483,8 @@ public class ProjectRestImpl extends BaseRestImpl { public Map<String, Object> removeProject(String token, String projectId, String path) throws SecurityException, QueryException { Project project = getProjectService().getProjectByProjectId(projectId, token); - if (getConfigurationService().getConfigurationValue(ConfigurationElementType.DEFAULT_MAP).equals(project.getProjectId())) { + if (getConfigurationService().getConfigurationValue(ConfigurationElementType.DEFAULT_MAP) + .equals(project.getProjectId())) { throw new OperationNotAllowedException("You cannot remove default map"); } getProjectService().removeProject(project, path, true, token); diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/projects/models/ModelController.java b/rest-api/src/main/java/lcsb/mapviewer/api/projects/models/ModelController.java index aa9b1dbe903dcf34c32f13e835d5ec62cb9d3841..d350d59b6131c28a72bc47bb2f8ba281a0018a74 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/projects/models/ModelController.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/projects/models/ModelController.java @@ -6,11 +6,13 @@ import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.fasterxml.jackson.core.JsonParseException; @@ -19,7 +21,13 @@ import com.fasterxml.jackson.databind.JsonMappingException; import lcsb.mapviewer.api.BaseController; import lcsb.mapviewer.api.ObjectNotFoundException; import lcsb.mapviewer.api.QueryException; +import lcsb.mapviewer.commands.CommandExecutionException; import lcsb.mapviewer.common.Configuration; +import lcsb.mapviewer.converter.ConverterException; +import lcsb.mapviewer.converter.graphics.DrawingException; +import lcsb.mapviewer.model.cache.FileEntry; +import lcsb.mapviewer.model.map.InconsistentModelException; +import lcsb.mapviewer.model.map.layout.InvalidColorSchemaException; import lcsb.mapviewer.services.SecurityException; @RestController @@ -63,4 +71,48 @@ public class ModelController extends BaseController { return modelController.updateModel(projectId, modelId, data, token); } + @RequestMapping(value = "/projects/{projectId}/models/{modelId}:downloadImage", method = { + RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE }) + public ResponseEntity<byte[]> getModelAsImage(// + @CookieValue(value = Configuration.AUTH_TOKEN) String token, // + @PathVariable(value = "projectId") String projectId, // + @PathVariable(value = "modelId") String modelId, // + @RequestParam(value = "handlerClass") String handlerClass, // + @RequestParam(value = "backgroundOverlayId", defaultValue = "") String backgroundOverlayId, // + @RequestParam(value = "overlayIds", defaultValue = "") String overlayIds, // + @RequestParam(value = "zoomLevel", defaultValue = "") String zoomLevel, // + @RequestParam(value = "polygonString", defaultValue = "") String polygonString// + ) throws SecurityException, QueryException, IOException, InvalidColorSchemaException, CommandExecutionException, + DrawingException { + + FileEntry file = modelController.getModelAsImage(token, projectId, modelId, handlerClass, backgroundOverlayId, + overlayIds, zoomLevel, polygonString); + MediaType type = MediaType.APPLICATION_OCTET_STREAM; + return ResponseEntity.ok().contentLength(file.getFileContent().length).contentType(type) + .header("Content-Disposition", "attachment; filename=" + file.getOriginalFileName()) + .body(file.getFileContent()); + } + + @RequestMapping(value = "/projects/{projectId}/models/{modelId}:downloadModel", method = { + RequestMethod.GET }, produces = { MediaType.APPLICATION_JSON_VALUE }) + public ResponseEntity<byte[]> getModelAsModelFile(// + @CookieValue(value = Configuration.AUTH_TOKEN) String token, // + @PathVariable(value = "projectId") String projectId, // + @PathVariable(value = "modelId") String modelId, // + @RequestParam(value = "handlerClass") String handlerClass, // + @RequestParam(value = "backgroundOverlayId", defaultValue = "") String backgroundOverlayId, // + @RequestParam(value = "overlayIds", defaultValue = "") String overlayIds, // + @RequestParam(value = "zoomLevel", defaultValue = "") String zoomLevel, // + @RequestParam(value = "polygonString", defaultValue = "") String polygonString// + ) throws SecurityException, QueryException, IOException, InvalidColorSchemaException, CommandExecutionException, + ConverterException, InconsistentModelException { + + FileEntry file = modelController.getModelAsModelFile(token, projectId, modelId, handlerClass, backgroundOverlayId, + overlayIds, zoomLevel, polygonString); + MediaType type = MediaType.APPLICATION_OCTET_STREAM; + return ResponseEntity.ok().contentLength(file.getFileContent().length).contentType(type) + .header("Content-Disposition", "attachment; filename=" + file.getOriginalFileName()) + .body(file.getFileContent()); + } + } \ No newline at end of file diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/projects/models/ModelRestImpl.java b/rest-api/src/main/java/lcsb/mapviewer/api/projects/models/ModelRestImpl.java index 5e3659dbc1a4d3ece5da1918825bc07267448108..f735bb5937aa81e1c578ebfb0d15a1142caa8d6e 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/projects/models/ModelRestImpl.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/projects/models/ModelRestImpl.java @@ -1,29 +1,77 @@ package lcsb.mapviewer.api.projects.models; +import java.awt.Color; +import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; +import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import org.apache.commons.io.IOUtils; import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import lcsb.mapviewer.api.BaseRestImpl; import lcsb.mapviewer.api.ObjectNotFoundException; import lcsb.mapviewer.api.QueryException; +import lcsb.mapviewer.commands.ClearColorModelCommand; +import lcsb.mapviewer.commands.ColorExtractor; +import lcsb.mapviewer.commands.ColorModelCommand; +import lcsb.mapviewer.commands.CommandExecutionException; +import lcsb.mapviewer.commands.CopyCommand; +import lcsb.mapviewer.commands.SetFixedHierarchyLevelCommand; +import lcsb.mapviewer.commands.SubModelCommand; import lcsb.mapviewer.common.Configuration; import lcsb.mapviewer.common.exception.InvalidArgumentException; +import lcsb.mapviewer.converter.ConverterException; +import lcsb.mapviewer.converter.IConverter; +import lcsb.mapviewer.converter.graphics.AbstractImageGenerator.Params; +import lcsb.mapviewer.converter.graphics.DrawingException; +import lcsb.mapviewer.converter.graphics.ImageGenerators; import lcsb.mapviewer.model.Project; +import lcsb.mapviewer.model.cache.FileEntry; +import lcsb.mapviewer.model.cache.UploadedFileEntry; +import lcsb.mapviewer.model.map.InconsistentModelException; +import lcsb.mapviewer.model.map.layout.ColorSchema; +import lcsb.mapviewer.model.map.layout.InvalidColorSchemaException; +import lcsb.mapviewer.model.map.layout.Layout; import lcsb.mapviewer.model.map.model.Model; import lcsb.mapviewer.model.map.model.ModelData; import lcsb.mapviewer.model.map.model.SubmodelType; +import lcsb.mapviewer.model.map.species.Element; import lcsb.mapviewer.model.user.PrivilegeType; +import lcsb.mapviewer.model.user.User; import lcsb.mapviewer.services.SecurityException; +import lcsb.mapviewer.services.interfaces.ILayoutService; +import lcsb.mapviewer.services.utils.ColorSchemaReader; +import lcsb.mapviewer.services.utils.data.BuildInLayout; @Transactional(value = "txManager") public class ModelRestImpl extends BaseRestImpl { + + + /** + * Constant defining size of the array returned by + * {@link PathIterator#currentSegment(double[])} method. More information can be + * found <a href= + * "http://docs.oracle.com/javase/7/docs/api/java/awt/geom/PathIterator.html#currentSegment(double[])" + * >here</a> + */ + private static final int PATH_ITERATOR_SEGMENT_SIZE = 6; + + @Autowired + private ILayoutService layoutService; + /** * Default class logger. */ @@ -156,4 +204,225 @@ public class ModelRestImpl extends BaseRestImpl { throw new QueryException("Don't know how to change " + value.getClass() + " into Double"); } } + + public FileEntry getModelAsModelFile(String token, String projectId, String modelId, String handlerClass, + String backgroundOverlayId, String overlayIds, String zoomLevel, String polygonString) + throws SecurityException, QueryException, IOException, InvalidColorSchemaException, CommandExecutionException, + ConverterException, InconsistentModelException { + User user = getUserService().getUserByToken(token); + Model topModel = getModelService().getLastModelByProjectId(projectId, token); + if (topModel == null) { + throw new ObjectNotFoundException("Project with given id doesn't exist"); + } + + Model originalModel = topModel.getSubmodelById(modelId); + + if (originalModel == null) { + throw new ObjectNotFoundException("Model with given id doesn't exist"); + } + + Path2D polygon = stringToPolygon(polygonString, originalModel); + + // create model bounded by the polygon + SubModelCommand subModelCommand = new SubModelCommand(originalModel, polygon); + Model part = subModelCommand.execute(); + + // Get list of overlay ids + String[] overlayIdsList; + if (overlayIds == null || overlayIds.trim().isEmpty()) { + overlayIdsList = new String[0]; + } else { + overlayIdsList = overlayIds.split(","); + } + // Remove all colors + if (overlayIdsList.length > 0) { + + for (Element element : part.getElements()) { + element.setColor(Color.WHITE); + } + } + // Color with overlays + for (String overlayId : overlayIdsList) { + Layout overlay = layoutService.getLayoutById(Integer.parseInt(overlayId.trim()), token); + + ColorSchemaReader reader = new ColorSchemaReader(); + Collection<ColorSchema> schemas = reader.readColorSchema(overlay.getInputData().getFileContent()); + + new ColorModelCommand(part, schemas, getUserService().getColorExtractorForUser(user)).execute(); + } + + IConverter parser = getModelParser(handlerClass); + InputStream is = parser.exportModelToInputStream(part); + + String fileExtension = parser.getFileExtension(); + + UploadedFileEntry entry = new UploadedFileEntry(); + entry.setOriginalFileName("model." + fileExtension); + entry.setFileContent(IOUtils.toByteArray(is)); + entry.setLength(entry.getFileContent().length); + return entry; + + } + + + public FileEntry getModelAsImage(String token, String projectId, String modelId, String handlerClass, + String backgroundOverlayId, String overlayIds, String zoomLevel, String polygonString) throws SecurityException, + QueryException, IOException, InvalidColorSchemaException, CommandExecutionException, DrawingException { + User user = getUserService().getUserByToken(token); + + Model topModel = getModelService().getLastModelByProjectId(projectId, token); + if (topModel == null) { + throw new ObjectNotFoundException("Project with given id doesn't exist"); + } + + Model originalModel = topModel.getSubmodelById(modelId); + + if (originalModel == null) { + throw new ObjectNotFoundException("Model with given id doesn't exist"); + } + + Layout overlay = null; + if (!backgroundOverlayId.equals("")) { + overlay = topModel.getLayoutByIdentifier(Integer.valueOf(backgroundOverlayId)); + + if (overlay == null) { + throw new ObjectNotFoundException("Unknown overlay in model. Layout.id=" + backgroundOverlayId); + } + } else { + if (topModel.getLayouts().size() > 0) { + overlay = topModel.getLayouts().get(0); + } + } + + Model colorModel = new CopyCommand(originalModel).execute(); + if (overlay != null) { + if (overlay.getInputData() != null) { + ColorSchemaReader reader = new ColorSchemaReader(); + Collection<ColorSchema> schemas = reader.readColorSchema(overlay.getInputData().getFileContent()); + + new ColorModelCommand(colorModel, schemas, getUserService().getColorExtractorForUser(user)).execute(); + } else if (overlay.getTitle().equals(BuildInLayout.CLEAN.getTitle())) { + // this might not return true if we change CLEAN.title in future... + + // if it's clean then remove coloring + new ClearColorModelCommand(colorModel).execute(); + } else if (overlay.isHierarchicalView()) { + new SetFixedHierarchyLevelCommand(colorModel, overlay.getHierarchyViewLevel()).execute(); + } + } + + Integer level = Configuration.MIN_ZOOM_LEVEL; + if (!zoomLevel.equals("")) { + level = Integer.valueOf(zoomLevel); + } + + Path2D polygon = stringToPolygon(polygonString, colorModel); + + Double minX = originalModel.getWidth(); + Double minY = originalModel.getHeight(); + Double maxX = 0.0; + Double maxY = 0.0; + + PathIterator pathIter = polygon.getPathIterator(null); + while (!pathIter.isDone()) { + final double[] segment = new double[PATH_ITERATOR_SEGMENT_SIZE]; + if (pathIter.currentSegment(segment) != PathIterator.SEG_CLOSE) { + minX = Math.min(minX, segment[0]); + maxX = Math.max(maxX, segment[0]); + minY = Math.min(minY, segment[1]); + maxY = Math.max(maxY, segment[1]); + } + pathIter.next(); + } + + maxX = Math.min(originalModel.getWidth(), maxX); + maxY = Math.min(originalModel.getHeight(), maxY); + minX = Math.max(0.0, minX); + minY = Math.max(0.0, minY); + + Double scale = Math.max(originalModel.getHeight(), originalModel.getWidth()) / (originalModel.getTileSize()); + + for (int i = level; i > Configuration.MIN_ZOOM_LEVEL; i--) { + scale /= 2; + } + + ColorExtractor colorExtractor = getUserService().getColorExtractorForUser(user); + + Params params = new Params().// + x(minX).// + y(minY).// + height((maxY - minY) / scale).// + width((maxX - minX) / scale).// + level(level - Configuration.MIN_ZOOM_LEVEL).// + nested(false).// automatically set nested view as invalid + scale(scale).// + colorExtractor(colorExtractor).// + sbgn(topModel.getProject().isSbgnFormat()).// + model(colorModel); + if (overlay != null) { + params.nested(overlay.isHierarchicalView()); + } + List<Integer> visibleLayoutIds = deserializeIdList(overlayIds); + for (Integer integer : visibleLayoutIds) { + Map<Object, ColorSchema> map = layoutService.getElementsForLayout(colorModel, integer, token); + params.addVisibleLayout(map); + } + + ImageGenerators imageGenerator = new ImageGenerators(); + String extension = imageGenerator.getExtension(handlerClass); + File file = File.createTempFile("map", "." + extension); + + imageGenerator.generate(handlerClass, params, file.getAbsolutePath()); + + UploadedFileEntry entry = new UploadedFileEntry(); + entry.setOriginalFileName("map." + extension); + entry.setFileContent(IOUtils.toByteArray(new FileInputStream(file))); + entry.setLength(entry.getFileContent().length); + file.delete(); + return entry; + + } + + private Path2D stringToPolygon(String polygonString, Model colorModel) { + String[] stringPointArray = polygonString.split(";"); + + List<Point2D> points = new ArrayList<>(); + for (String string : stringPointArray) { + if (!string.trim().equals("")) { + double x = Double.valueOf(string.split(",")[0]); + double y = Double.valueOf(string.split(",")[1]); + points.add(new Point2D.Double(x, y)); + } + } + + if (points.size() <= 2) { + points.clear(); + points.add(new Point2D.Double(0, 0)); + points.add(new Point2D.Double(colorModel.getWidth(), 0)); + points.add(new Point2D.Double(colorModel.getWidth(), colorModel.getHeight())); + points.add(new Point2D.Double(0, colorModel.getHeight())); + } + + Path2D polygon = new Path2D.Double(); + polygon.moveTo(points.get(0).getX(), points.get(0).getY()); + for (int i = 1; i < points.size(); i++) { + Point2D point = points.get(i); + polygon.lineTo(point.getX(), point.getY()); + } + polygon.closePath(); + return polygon; + } + + private List<Integer> deserializeIdList(String overlayIds) { + List<Integer> result = new ArrayList<>(); + String[] tmp = overlayIds.split(","); + for (String string : tmp) { + if (!string.equals("")) { + result.add(Integer.valueOf(string)); + } + } + return result; + } + + } diff --git a/rest-api/src/test/java/lcsb/mapviewer/api/projects/ProjectRestImplTest.java b/rest-api/src/test/java/lcsb/mapviewer/api/projects/ProjectRestImplTest.java index 79afcc01ea2719718cb3cd963afb71ac545c066d..60688c8ccacebe8141366abac7056c762baf23ea 100644 --- a/rest-api/src/test/java/lcsb/mapviewer/api/projects/ProjectRestImplTest.java +++ b/rest-api/src/test/java/lcsb/mapviewer/api/projects/ProjectRestImplTest.java @@ -4,9 +4,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.times; import java.util.ArrayList; @@ -28,17 +27,12 @@ import org.springframework.util.MultiValueMap; import com.google.gson.Gson; import lcsb.mapviewer.api.ObjectNotFoundException; -import lcsb.mapviewer.api.QueryException; import lcsb.mapviewer.api.RestTestFunctions; -import lcsb.mapviewer.common.exception.InvalidArgumentException; -import lcsb.mapviewer.converter.graphics.PdfImageGenerator; -import lcsb.mapviewer.converter.model.celldesigner.CellDesignerXmlParser; import lcsb.mapviewer.converter.zip.ImageZipEntryFile; import lcsb.mapviewer.converter.zip.LayoutZipEntryFile; import lcsb.mapviewer.converter.zip.ModelZipEntryFile; import lcsb.mapviewer.converter.zip.ZipEntryFile; import lcsb.mapviewer.model.Project; -import lcsb.mapviewer.model.cache.FileEntry; import lcsb.mapviewer.model.map.MiriamType; import lcsb.mapviewer.model.map.model.Model; import lcsb.mapviewer.persist.dao.ProjectDao; @@ -71,32 +65,6 @@ public class ProjectRestImplTest extends RestTestFunctions { public void tearDown() throws Exception { } - @Test - public void testGetModelAsImageForInvalidConverter() throws Exception { - try { - ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml"); - projectRest.getModelAsImage(token, "sample", "0", "", "", "", "", ""); - fail("Exception expected"); - } catch (InvalidArgumentException e) { - } catch (Exception e) { - e.printStackTrace(); - throw e; - } - } - - @Test - public void testGetModelAsImage() throws Exception { - try { - ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml"); - FileEntry result = projectRest.getModelAsImage(token, "sample", "0", PdfImageGenerator.class.getCanonicalName(), - "", "", "", ""); - assertNotNull(result); - } catch (Exception e) { - e.printStackTrace(); - throw e; - } - } - @Test public void testGetModelDataDependencies() throws Exception { try { @@ -117,55 +85,6 @@ public class ProjectRestImplTest extends RestTestFunctions { projectRest.getProject("unknown_model_id", token); } - @Test - public void testGetModelAsImage2() throws Exception { - try { - ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml"); - projectRest.getModelAsImage(token, "sample", "0", PdfImageGenerator.class.getCanonicalName(), "", "", "", ""); - } catch (Exception e) { - e.printStackTrace(); - throw e; - } - } - - @Test - public void testGetModelAsFileModel() throws Exception { - try { - ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml"); - projectRest.getModelAsModelFile(token, "sample", "0", "", "", "", "", ""); - fail("Exception expected"); - } catch (QueryException e) { - } catch (Exception e) { - e.printStackTrace(); - throw e; - } - } - - @Test - public void testGetModelAsFileModel2() throws Exception { - try { - ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml"); - projectRest.getModelAsModelFile(token, "sample", "0", CellDesignerXmlParser.class.getCanonicalName(), "", "", "", - "0,0;90,0;90,90;90,0"); - } catch (Exception e) { - e.printStackTrace(); - throw e; - } - } - - @Test - public void testGetModelAsFileModel3() throws Exception { - try { - ProjectRestImpl projectRest = createMockProjectRest("testFiles/model/sample.xml"); - projectRest.getModelAsModelFile(token, "sample", "0", "", "", "", "", "0,0;90,0;90,90;90,0"); - fail("Exception expected"); - } catch (QueryException e) { - } catch (Exception e) { - e.printStackTrace(); - throw e; - } - } - @Test public void testGetProject() throws Exception { try { diff --git a/rest-api/src/test/java/lcsb/mapviewer/api/projects/models/ModelRestImplTest.java b/rest-api/src/test/java/lcsb/mapviewer/api/projects/models/ModelRestImplTest.java index d3435cc35db0867789a427c34393ba3d98e1d809..75e4f09a45a7bffdb355311eaf3b0e8891a6f6e1 100644 --- a/rest-api/src/test/java/lcsb/mapviewer/api/projects/models/ModelRestImplTest.java +++ b/rest-api/src/test/java/lcsb/mapviewer/api/projects/models/ModelRestImplTest.java @@ -1,6 +1,8 @@ package lcsb.mapviewer.api.projects.models; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.ArgumentMatchers.refEq; @@ -14,8 +16,13 @@ import org.junit.Test; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; +import lcsb.mapviewer.api.QueryException; import lcsb.mapviewer.api.RestTestFunctions; +import lcsb.mapviewer.common.exception.InvalidArgumentException; +import lcsb.mapviewer.converter.graphics.PdfImageGenerator; +import lcsb.mapviewer.converter.model.celldesigner.CellDesignerXmlParser; import lcsb.mapviewer.model.Project; +import lcsb.mapviewer.model.cache.FileEntry; import lcsb.mapviewer.model.map.model.Model; import lcsb.mapviewer.services.interfaces.IModelService; import lcsb.mapviewer.services.interfaces.IProjectService; @@ -93,4 +100,80 @@ public class ModelRestImplTest extends RestTestFunctions { return _modelRestImpl; } + @Test + public void testGetModelAsImageForInvalidConverter() throws Exception { + try { + ModelRestImpl modelRest = createMockProjectRest("testFiles/model/sample.xml"); + modelRest.getModelAsImage(token, "sample", "0", "", "", "", "", ""); + fail("Exception expected"); + } catch (InvalidArgumentException e) { + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + + @Test + public void testGetModelAsImage() throws Exception { + try { + ModelRestImpl modelRest = createMockProjectRest("testFiles/model/sample.xml"); + FileEntry result = modelRest.getModelAsImage(token, "sample", "0", PdfImageGenerator.class.getCanonicalName(), + "", "", "", ""); + assertNotNull(result); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + + @Test + public void testGetModelAsImage2() throws Exception { + try { + ModelRestImpl modelRest = createMockProjectRest("testFiles/model/sample.xml"); + modelRest.getModelAsImage(token, "sample", "0", PdfImageGenerator.class.getCanonicalName(), "", "", "", ""); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + + @Test + public void testGetModelAsFileModel() throws Exception { + try { + ModelRestImpl modelRest = createMockProjectRest("testFiles/model/sample.xml"); + modelRest.getModelAsModelFile(token, "sample", "0", "", "", "", "", ""); + fail("Exception expected"); + } catch (QueryException e) { + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + + @Test + public void testGetModelAsFileModel2() throws Exception { + try { + ModelRestImpl modelRest = createMockProjectRest("testFiles/model/sample.xml"); + modelRest.getModelAsModelFile(token, "sample", "0", CellDesignerXmlParser.class.getCanonicalName(), "", "", "", + "0,0;90,0;90,90;90,0"); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + + @Test + public void testGetModelAsFileModel3() throws Exception { + try { + ModelRestImpl modelRest = createMockProjectRest("testFiles/model/sample.xml"); + modelRest.getModelAsModelFile(token, "sample", "0", "", "", "", "", "0,0;90,0;90,90;90,0"); + fail("Exception expected"); + } catch (QueryException e) { + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + + }