Commit d24c9752 authored by Matija Obreza's avatar Matija Obreza
Browse files

Refactored code for TileController

- Moved internal class CoordUtil to org.genesys2.utils
parent 60725168
......@@ -151,7 +151,7 @@ public class ShortFilterServiceImpl implements ShortFilterService {
@Override
public <T> T filterByCode(String code, Class<T> clazz) throws IOException {
ShortFilter shortFilter = shortFilterRepository.findByCode(code);
ShortFilter shortFilter = shortFilterRepository.findByCode(code == null ? "" : code);
return mapper.readValue(shortFilter.getJson(), clazz);
}
......
/*
* Copyright 2018 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.genesys2.server.api.v1;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import org.genesys.catalog.service.ShortFilterService;
import org.genesys2.server.service.MappingService;
import org.genesys2.server.service.filter.AccessionFilter;
import org.genesys2.spring.ResourceNotFoundException;
import org.genesys2.util.ColorUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
......@@ -14,12 +30,11 @@ import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//@RestController("tileApi1")
/**
* This is not an API controller, it's accessible to anonymous users.
*/
@Controller
//@Api(tags = {"tile"})
public class TileController {
@Autowired
......@@ -28,29 +43,20 @@ public class TileController {
@Autowired
private ShortFilterService shortFilterService;
private static final Logger LOG = LoggerFactory.getLogger(TileController.class);
@RequestMapping(value = "/explore/tile/{zoom}/{x}/{y}/{filterCode}", produces = MediaType.IMAGE_PNG_VALUE)
public void tile(@PathVariable(value = "filterCode", required = true) String filterCode,
@RequestMapping(value = "/acn/tile/{zoom:\\d+}/{x:\\d+}/{y:\\d+}", produces = MediaType.IMAGE_PNG_VALUE)
public void tile(@RequestParam(value = "f", required = false, defaultValue="") String filterCode,
@RequestParam(value = "color", required = false) String color,
@PathVariable("x") int x, @PathVariable("y") int y, @PathVariable("zoom") int zoom,
HttpServletResponse response) {
try {
AccessionFilter appliedFilters = shortFilterService.filterByCode(filterCode, AccessionFilter.class);
HttpServletResponse response) throws IOException {
byte[] image = mappingService.getTile(appliedFilters, zoom, x, y);
image = ColorUtil.changeColor(color, image);
AccessionFilter appliedFilters = shortFilterService.filterByCode(filterCode, AccessionFilter.class);
response.setHeader(HttpHeaders.CACHE_CONTROL, "max-age=3600, s-maxage=3600, public, no-transform");
response.getOutputStream().write(image, 0, image.length);
} catch (final IOException e) {
LOG.warn(e.getMessage());
throw new RuntimeException("Could not render image", e);
} catch (final Throwable e) {
LOG.error(e.getMessage(), e);
throw new ResourceNotFoundException(e.getMessage());
}
byte[] image = mappingService.getTile(appliedFilters, zoom, x, y);
image = ColorUtil.changeColor(color, image);
response.setContentType(MediaType.IMAGE_PNG_VALUE);
response.setContentLength(image.length);
response.setHeader(HttpHeaders.CACHE_CONTROL, "max-age=3600, s-maxage=3600, public, no-transform");
response.getOutputStream().write(image, 0, image.length);
}
}
......@@ -16,10 +16,6 @@
package org.genesys2.server.mvc;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.OutputStream;
......@@ -34,7 +30,6 @@ import java.util.Map;
import java.util.ResourceBundle;
import java.util.TreeMap;
import javax.imageio.ImageIO;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
......@@ -98,7 +93,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.support.RequestContextUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jhlabs.image.MapColorsFilter;
@Controller
public class ExplorerController extends BaseController implements InitializingBean {
......@@ -795,9 +789,11 @@ public class ExplorerController extends BaseController implements InitializingBe
try {
AppliedFilters appliedFilters = mapper.readValue(jsonFilter, AppliedFilters.class);
byte[] image = mappingService.getTile(appliedFilters, zoom, x, y);
byte[] image = mappingService.getTile(AccessionFilter.convert(appliedFilters), zoom, x, y);
image = ColorUtil.changeColor(color, image);
response.setContentType(MediaType.IMAGE_PNG_VALUE);
response.setContentLength(image.length);
response.setHeader(HttpHeaders.CACHE_CONTROL, "max-age=3600, s-maxage=3600, public, no-transform");
response.getOutputStream().write(image, 0, image.length);
......
......@@ -18,15 +18,16 @@ package org.genesys2.server.service;
import java.util.List;
import org.genesys.blocks.model.filters.BasicModelFilter;
import org.genesys2.server.model.elastic.AccessionDetails;
import org.genesys2.server.model.genesys.Accession;
import org.genesys2.server.service.filter.AccessionFilter;
import org.genesys2.server.service.impl.FilterHandler.AppliedFilters;
import org.genesys2.server.service.impl.GenesysFilterServiceImpl.LabelValue;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.jdbc.core.RowCallbackHandler;
// TODO Deprecate
public interface GenesysFilterService {
Page<Accession> listAccessions(AppliedFilters filters, Pageable pageable);
......@@ -42,7 +43,7 @@ public interface GenesysFilterService {
void listGeoTile(boolean distinct, AppliedFilters filters, Integer limit, int zoom, int xtile, int ytile, RowCallbackHandler rowHandler);
List<Object[]> listGeoTile(BasicModelFilter filters, Integer limit, int zoom, int xtile, int ytile);
List<Double[]> listGeoTile(AccessionFilter filter, Integer limit, int zoom, int xtile, int ytile);
AppliedFilters transformFiltersIfNeed(AppliedFilters appliedFilters);
}
......@@ -20,7 +20,7 @@ import java.awt.Color;
import java.io.IOException;
import java.io.OutputStream;
import org.genesys.blocks.model.filters.BasicModelFilter;
import org.genesys2.server.service.filter.AccessionFilter;
import org.genesys2.server.service.impl.FilterHandler.AppliedFilters;
public interface MappingService {
......@@ -67,156 +67,5 @@ public interface MappingService {
* the ytile
* @return the tile
*/
byte[] getTile(AppliedFilters filters, int zoom, int xtile, int ytile);
byte[] getTile(BasicModelFilter filters, int zoom, int xtile, int ytile);
/**
* Coordinate utilities
*/
public static class CoordUtil {
/**
* Convert tile index at zoom level to longitude.
*
* @param zoom
* zoom level
* @param xtile
* tile index
* @return longitude of left side of tile
*/
public static double tileToLon(final int zoom, final int xtile) {
return 360.0 * xtile / (1 << zoom) - 180.0;
}
/**
* Convert tile index at zoom level to latitude
*
* @param zoom
* zoom level
* @param ytile
* tile index
* @return latitude of the top side of the tile
*/
public static double tileToLat(final int zoom, final int ytile) {
final double n = Math.PI - 2.0 * Math.PI * ytile / (1 << zoom);
return Math.toDegrees(Math.atan(Math.sinh(n)));
}
/**
* Lon to tile.
*
* @param zoom
* the zoom
* @param lon
* the lon
* @return the int
*/
public static int lonToTile(final int zoom, final double lon) {
return (int) Math.floor((lon + 180.0) / 360.0 * (1 << zoom));
}
/**
* Lat to tile.
*
* @param zoom
* the zoom
* @param lat
* the lat
* @return the int
*/
public static int latToTile(final int zoom, final double lat) {
final double latr = Math.toRadians(lat);
return (int) Math.floor((1 - Math.log(Math.tan(latr) + 1 / Math.cos(latr)) / Math.PI) / 2 * (1 << zoom));
}
/**
* Lon to img.
*
* @param zoom
* the zoom
* @param lon
* the lon
* @return the int
*/
public static int lonToImg(final int zoom, double lon) {
// n = 2 ^ zoom
final int xtile = lonToTile(zoom, lon);
final double a = tileToLon(zoom, xtile);
final double b = tileToLon(zoom, xtile + 1);
// System.err.println("a=" + a + " b=" + b);
// Translate to start of tile
lon -= a;
// Scale by (ba)*256;
// System.err.println("b-a="+(b-a));
lon = 256.0 * lon / (b - a);
return (int) Math.floor(lon);
}
/**
* Lat to img.
*
* @param zoom
* the zoom
* @param lat
* the lat
* @return the int
*/
public static int latToImg(final int zoom, double lat) {
// n = 2 ^ zoom
final int ytile = latToTile(zoom, lat);
// System.err.println("ytile=" + ytile);
final double a = tileToLat(zoom, ytile);
final double b = tileToLat(zoom, ytile + 1);
// System.err.println("a=" + a + " b=" + b);
// Translate to start of tile
lat -= a;
// Scale by (ba)*256;
// System.err.println("b-a=" + (b - a));
lat = 256.0 * lat / (b - a);
return (int) Math.floor(lat);
}
/**
* Mercator projection of decimal latitude to a y-pixel on tile at
* specific zoom level.
*
* @param zoom
* Zoom level
* @param tile
* Tile index
* @param lat
* Latitude
* @return the int
*/
public static int latToImg3(final int zoom, final int tile, double lat) {
lat = Math.min(85.0511287, Math.max(lat, -85.0511287));
final double pixAtZoom = (1 << zoom) * 256;
final double latr = Math.toRadians(lat);
final double y1 = Math.floor((1 - Math.log(Math.tan(latr) + 1 / Math.cos(latr)) / Math.PI) / 2 * pixAtZoom);
return (int) y1 - tile * 256;
}
/**
* Convert decimal longitude to an x-pixel on tile at specific zoom
* level.
*
* @param zoom
* Zoom level
* @param tile
* Tile index
* @param lng
* Longitude
* @return the int
*/
public static int lonToImg3(final int zoom, final int tile, final double lng) {
final double totalPixelsAtZoom = (1 << zoom) * 256;
final double longitudePixelAtZoom = Math.floor((180.0 + lng) / 360.0 * totalPixelsAtZoom);
return (int) (longitudePixelAtZoom - tile * 256);
}
}
byte[] getTile(AccessionFilter filter, int zoom, int xtile, int ytile);
}
......@@ -28,7 +28,6 @@ import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.genesys2.server.model.genesys.Method;
import org.genesys2.server.service.FilterConstants;
import org.genesys2.server.service.MappingService.CoordUtil;
import org.genesys2.server.service.impl.FilterHandler.AppliedFilter;
import org.genesys2.server.service.impl.FilterHandler.AppliedFilters;
import org.genesys2.server.service.impl.FilterHandler.FilterValue;
......@@ -37,6 +36,7 @@ import org.genesys2.server.service.impl.FilterHandler.MaxValueFilter;
import org.genesys2.server.service.impl.FilterHandler.MinValueFilter;
import org.genesys2.server.service.impl.FilterHandler.StartsWithFilter;
import org.genesys2.server.service.impl.FilterHandler.ValueRangeFilter;
import org.genesys2.util.CoordUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Pageable;
......
......@@ -28,8 +28,10 @@ import java.util.stream.Collectors;
import javax.persistence.EntityManager;
import javax.sql.DataSource;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.QueryResults;
import com.querydsl.core.Tuple;
import com.querydsl.core.types.Predicate;
import com.querydsl.jpa.JPQLQuery;
import com.querydsl.jpa.impl.JPAQuery;
import org.genesys.blocks.model.filters.BasicModelFilter;
......@@ -52,6 +54,7 @@ import org.genesys2.server.service.impl.FilterHandler.AppliedFilter;
import org.genesys2.server.service.impl.FilterHandler.AppliedFilters;
import org.genesys2.server.service.impl.FilterHandler.FilterValue;
import org.genesys2.server.service.impl.FilterHandler.LiteralValueFilter;
import org.genesys2.util.CoordUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -387,6 +390,9 @@ public class GenesysFilterServiceImpl implements GenesysFilterService {
listGeoTile(false, filters, limit, -1, 0, 0, rowHandler);
}
/**
* @deprecated Use {@link #listGeoTile(AccessionFilter, Integer, int, int, int)}
*/
@Override
public void listGeoTile(final boolean distinct, AppliedFilters filters, Integer limit, int zoom, int xtile, int ytile, RowCallbackHandler rowHandler) {
if (LOG.isDebugEnabled()) {
......@@ -430,22 +436,43 @@ public class GenesysFilterServiceImpl implements GenesysFilterService {
}
@Override
// TODO check query - should be something like in the method above
public List<Object[]> listGeoTile(BasicModelFilter filters, Integer limit, int zoom, int xtile, int ytile) {
public List<Double[]> listGeoTile(AccessionFilter filter, Integer limit, int zoom, int xtile, int ytile) {
QAccession accession = QAccession.accession;
// migrate functionality from DirectMysqlQuery.filterTile
QAccessionGeo accessionGeo = accession.accessionId.geo;
JPQLQuery<Tuple> query = new JPAQuery<>(entityManager);
query.select(accession.accessionId.geo.longitude, accession.accessionId.geo.latitude).distinct().from(accession);
query.where(filters.buildQuery());
query.select(accessionGeo.longitude, accessionGeo.latitude).distinct().from(accession);
BooleanBuilder filt = new BooleanBuilder();
if (filter != null) {
filt.and(filter.buildQuery());
}
// The tile query
{
final double lonW = CoordUtil.tileToLon(zoom, xtile);
final double lonE = CoordUtil.tileToLon(zoom, xtile + 1);
final double diffLon = lonE - lonW;
final double latN = CoordUtil.tileToLat(zoom, ytile);
final double latS = CoordUtil.tileToLat(zoom, ytile + 1);
final double diffLat = latN - latS;
if (LOG.isDebugEnabled()) {
LOG.debug("{} <= lon <= {} corr={}", lonW, lonE, diffLon * .2);
LOG.debug("{} <= lat <= {} corr={}", latS, latN, diffLat * .2);
}
filt.and(accessionGeo.longitude.between(lonW - zoom * diffLon * .2, lonE + zoom * diffLon * .2));
filt.and(accessionGeo.latitude.between(latS - zoom * diffLat * .2, latN + zoom * diffLat * .2));
}
query.where(filt);
QueryResults<Tuple> results = query.fetchResults();
return results.getResults().stream()
.map(item -> new Object[]{
item.get(accession.accessionId.geo.longitude),
item.get(accession.accessionId.geo.latitude),
.map(item -> new Double[]{
item.get(accessionGeo.longitude),
item.get(accessionGeo.latitude),
}).collect(Collectors.toList());
}
......
......@@ -30,7 +30,9 @@ import javax.imageio.ImageIO;
import org.genesys.blocks.model.filters.BasicModelFilter;
import org.genesys2.server.service.GenesysFilterService;
import org.genesys2.server.service.MappingService;
import org.genesys2.server.service.filter.AccessionFilter;
import org.genesys2.server.service.impl.FilterHandler.AppliedFilters;
import org.genesys2.util.CoordUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -161,79 +163,7 @@ public class MappingServiceImpl implements MappingService {
@Override
@Cacheable(value = "tileserver", key = "'tile-' + #zoom + '-' + #xtile + '-' + #ytile + '-' + #filters")
public byte[] getTile(AppliedFilters filters, final int zoom, final int xtile, final int ytile) {
filterService.transformFiltersIfNeed(filters);
final BufferedImage bi = new BufferedImage(256, 256, BufferedImage.TYPE_INT_ARGB);
// final int pixelSize = (int) Math.round(1.0 + (1 << (zoom / 2))) / 2;
final int[] pixelSizes = new int[] { 0, 0, 0, 1, 1, 2, 2, 2, 3, 2, 2, 3 };
final int pixelSize = pixelSizes[zoom >= pixelSizes.length ? pixelSizes.length - 1 : zoom];
if (LOG.isDebugEnabled()) {
LOG.debug(filters.toString());
LOG.debug("PIXELSIZE={} zoom={}", pixelSize, zoom);
}
// // Border
// for (int i = 0; i < 256; i++) {
// bi.setRGB(0, i, Color.red.getRGB());
// bi.setRGB(i, 0, Color.red.getRGB());
// bi.setRGB(255, i, Color.red.getRGB());
// bi.setRGB(i, 255, Color.red.getRGB());
// }
final int colorWithoutAlpha = MappingService.DEFAULT_TILE_COLOR.getRGB();
filterService.listGeoTile(true, filters, null, zoom, xtile, ytile, new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
try {
final int longitude = CoordUtil.lonToImg3(zoom, xtile, rs.getDouble("longitude"));
final int latitude = CoordUtil.latToImg3(zoom, ytile, rs.getDouble("latitude"));
if (LOG.isTraceEnabled()) {
LOG.trace("Adding geo={}, {} as pixe=[{}, {}]", rs.getDouble("longitude"), rs.getDouble("latitude"), longitude, latitude);
}
for (int i = -pixelSize / 2; i <= pixelSize / 2; i++) {
for (int j = -pixelSize / 2; j <= pixelSize / 2; j++) {
if (longitude + i >= 0 && latitude + j >= 0 && longitude + i < 256 && latitude + j < 256) {
bi.setRGB(longitude + i, latitude + j, colorWithoutAlpha);
}
}
}
} catch (final SQLException e) {
LOG.warn(e.getMessage());
throw e;
}
}
});
// for (int j = -80; j < 81; j += 10) {
// int latitude = CoordUtil.latToImg3(zoom, ytile, j);
// if (latitude >= 0 && latitude < 256)
// for (int i = 0; i < 256; i++) {
// bi.setRGB(i, latitude, Color.blue.getRGB());
// }
// }
//
try {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
// if (zoom < -20) {
// ImageIO.write(op.filter(bi, null), "png", baos);
// } else {
ImageIO.write(bi, "png", baos);
// }
return baos.toByteArray();
} catch (final IOException e) {
LOG.warn(e.getMessage(), e);
throw new RuntimeException("Could not render image", e);
}
}
@Override
public byte[] getTile(BasicModelFilter filters, int zoom, int xtile, int ytile) {
public byte[] getTile(AccessionFilter filters, int zoom, int xtile, int ytile) {
final BufferedImage bufferedImage = new BufferedImage(256, 256, BufferedImage.TYPE_INT_ARGB);
final int[] pixelSizes = new int[] { 0, 0, 0, 1, 1, 2, 2, 2, 3, 2, 2, 3 };
......@@ -241,11 +171,11 @@ public class MappingServiceImpl implements MappingService {
final int colorWithoutAlpha = MappingService.DEFAULT_TILE_COLOR.getRGB();
List<Object[]> geoTiles = filterService.listGeoTile(filters, null, zoom, xtile, ytile);
List<Double[]> geoTiles = filterService.listGeoTile(filters, null, zoom, xtile, ytile);
geoTiles.forEach(item -> {
final int longitude = MappingService.CoordUtil.lonToImg3(zoom, xtile, (Double) item[0]);
final int latitude = MappingService.CoordUtil.latToImg3(zoom, ytile, (Double) item[1]);
final int longitude = CoordUtil.lonToImg3(zoom, xtile, item[0]);
final int latitude = CoordUtil.latToImg3(zoom, ytile, item[1]);
for (int i = -pixelSize / 2; i <= pixelSize / 2; i++) {
for (int j = -pixelSize / 2; j <= pixelSize / 2; j++) {
......
/*
* Copyright 2018 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.genesys2.util;
/**
* Coordinate utilities
*/
public class CoordUtil {
/**
* Convert tile index at zoom level to longitude.
*
* @param zoom
* zoom level
* @param xtile
* tile index
* @return longitude of left side of tile
*/
public static double tileToLon(final int zoom, final int xtile) {
return 360.0 * xtile / (1 << zoom) - 180.0;
}
/**
* Convert tile index at zoom level to latitude
*
* @param zoom
* zoom level
* @param ytile
* tile index
* @return latitude of the top side of the tile
*/
public static double tileToLat(final int zoom, final int ytile) {
final double n = Math.PI - 2.0 * Math.PI * ytile