/******************************************************************************* * Copyright 2013-2016 alladin-IT GmbH * * 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 at.alladin.rmbt.mapServer; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.geom.Ellipse2D; import java.awt.geom.Path2D; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; import java.util.UUID; import javax.imageio.ImageIO; import org.restlet.data.Form; import at.alladin.rmbt.mapServer.MapServerOptions.MapOption; import at.alladin.rmbt.mapServer.MapServerOptions.SQLFilter; import at.alladin.rmbt.mapServer.parameters.PointTileParameters; import at.alladin.rmbt.mapServer.parameters.TileParameters.Path; public class PointTiles extends TileRestlet<PointTileParameters> { static class Dot { public Dot(final double x, final double y, final Color color, final boolean highlight) { this.x = x; this.y = y; this.color = color; this.highlight = highlight; } final double x; final double y; final Color color; final boolean highlight; } @Override protected PointTileParameters getTileParameters(Path path, Form params) { return new PointTileParameters(path, params); } @Override protected byte[] generateTile(PointTileParameters params, final int tileSizeIdx, final int zoom, final DBox box, final MapOption mo, final List<SQLFilter> filters, final float quantile) { final UUID highlightUUID; final byte[] baseTile; if (params.getGenericParameters() != null) { // recursive call to get generic tile w/o highlight probably from cache final PointTileParameters genericParams = params.getGenericParameters(); baseTile = getTile(genericParams); highlightUUID = params.getHighlight(); } else { highlightUUID = null; baseTile = null; } filters.add(MapServerOptions.getAccuracyMapFilter()); final StringBuilder whereSQL = new StringBuilder(mo.sqlFilter); for (final SQLFilter sf : filters) whereSQL.append(" AND ").append(sf.where); final String sql = String.format("SELECT ST_X(t.location) x, ST_Y(t.location) y, \"%s\" val" + " FROM v_test2 t" + (highlightUUID == null ? "" : " JOIN client c ON (t.client_id=c.uid AND c.uuid=?)") + " WHERE " + " %s" + " AND location && ST_SetSRID(ST_MakeBox2D(ST_Point(?,?), ST_Point(?,?)), 900913)" + " ORDER BY" + " t.uid", mo.valueColumn, whereSQL); final double diameter = params.getPointDiameter(); final double radius = diameter / 2d; final double triangleSide = diameter * 1.75; final double triangleHeight = Math.sqrt(3) / 2d * triangleSide; final int transparency = (int) Math.round(params.getTransparency() * 255); final boolean noFill = params.isNoFill(); final boolean noColor = params.isNoColor(); final Color borderColor = new Color(0, 0, 0, transparency); final Color highlightBorderColor = new Color(0, 0, 0, transparency); final Color colorUltraGreen = new Color(0, 153, 0, transparency); final Color colorGreen = new Color(0, 255, 0, transparency); final Color colorYellow = new Color(255, 255, 0, transparency); final Color colorRed = new Color(255, 0, 0, transparency); final Color colorGray = new Color(128, 128, 128, transparency); final List<Dot> dots = new ArrayList<>(); try (Connection con = DbConnection.getConnection(); PreparedStatement ps = con.prepareStatement(sql)) { int i = 1; if (highlightUUID != null) ps.setObject(i++, highlightUUID); for (final SQLFilter sf : filters) i = sf.fillParams(i, ps); final double margin = box.res * triangleSide; ps.setDouble(i++, box.x1 - margin); ps.setDouble(i++, box.y1 - margin); ps.setDouble(i++, box.x2 + margin); ps.setDouble(i++, box.y2 + margin); // System.out.println(ps); if (!ps.execute()) throw new IllegalArgumentException(ps.getWarnings()); try (ResultSet rs = ps.getResultSet()) { boolean _emptyTile = true; while (rs.next()) { _emptyTile = false; final double cx = rs.getDouble(1); final double cy = rs.getDouble(2); final long value = rs.getLong(3); final boolean highlight = highlightUUID != null; final int classification = noColor || noFill ? 0 : mo.getClassification(value); final Color color; switch (classification) { case 4: color = colorUltraGreen; break; case 3: color = colorGreen; break; case 2: color = colorYellow; break; case 1: color = colorRed; break; default: color = colorGray; break; } dots.add(new Dot(cx, cy, color, highlight)); } if (_emptyTile) return baseTile; final Image img = images[tileSizeIdx].get(); final Graphics2D g = img.g; g.setBackground(new Color(0, 0, 0, 0)); g.clearRect(0, 0, img.width, img.height); if (baseTile != null) { final ByteArrayInputStream bais = new ByteArrayInputStream(baseTile); final BufferedImage image = ImageIO.read(bais); g.drawImage(image, 0, 0, null); } g.setComposite(AlphaComposite.Src); g.setStroke((new BasicStroke(((float) diameter / 8f)))); final Path2D.Double triangle = new Path2D.Double(); final Ellipse2D.Double shape = new Ellipse2D.Double(0, 0, diameter, diameter); for (final Dot dot : dots) { final double relX = (dot.x - box.x1) / box.res; final double relY = TILE_SIZES[tileSizeIdx] - (dot.y - box.y1) / box.res; if (dot.highlight) // triangle { triangle.reset(); triangle.moveTo(relX, relY - triangleHeight / 3 * 2); triangle.lineTo(relX - triangleSide / 2, relY + triangleHeight / 3); triangle.lineTo(relX + triangleSide / 2, relY + triangleHeight / 3); triangle.closePath(); if (!noFill) { g.setPaint(dot.color); g.fill(triangle); } g.setPaint(highlightBorderColor); g.draw(triangle); } else // circle { shape.x = relX - radius; shape.y = relY - radius; if (!noFill) { g.setPaint(dot.color); g.fill(shape); } g.setPaint(borderColor); g.draw(shape); } } final ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(img.bi, "png", baos); final byte[] data = baos.toByteArray(); return data; } } catch (final Exception e) { e.printStackTrace(); throw new IllegalStateException(e); } } }