package cz.agents.dbtokmlexporter;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import org.apache.log4j.Logger;
import org.geotools.geometry.jts.JTS;
import org.geotools.referencing.CRS;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.TransformException;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Point;
import cz.agents.agentpolis.tools.geovisio.util.ColorMap;
import cz.agents.alite.googleearth.updates.Kmz;
import cz.agents.resultsvisio.kml.KmlItem;
import de.micromata.opengis.kml.v_2_2_0.Folder;
import de.micromata.opengis.kml.v_2_2_0.GroundOverlay;
import de.micromata.opengis.kml.v_2_2_0.LatLonBox;
/**
*
* @author Marek Cuchy
*
*/
public class HeatMapKmlItem implements KmlItem {
private static int heatMapCounter = 0;
private static final Logger logger = Logger.getLogger(HeatMapKmlItem.class);
/**
* It doesn't have dependence on result heatmap
*/
private static final Color ORIGINAL_POINT_COLOR = Color.RED;
private static final int FIRST_BLUR_SIZE=10;
private static final int SECOND_BLUR_SIZE=30;
private static final double IMAGE_SCALE = 0.05;
private static final int POINT_DIAMETER = 50;
private final List<Point> points = new ArrayList<>();
private Dimension imageDimension;
private final String schemaName;
/**
* GPS bounding box
*/
private double minx = Double.MAX_VALUE, maxx = Double.NEGATIVE_INFINITY, miny = Double.MAX_VALUE, maxy = Double.NEGATIVE_INFINITY;
private int pointAlpha;
public HeatMapKmlItem(String schemaName) {
super();
this.schemaName = schemaName;
}
public void addPoint(Point point) {
points.add(point);
updateBoundingBox(point);
}
private void updateBoundingBox(Point point) {
double x = point.getX();
double y = point.getY();
maxx = Math.max(maxx, x);
minx = Math.min(minx, x);
maxy = Math.max(maxy, y);
miny = Math.min(miny, y);
}
@Override
public Folder initFeatureForKml(Kmz kmz) {
if(points.isEmpty()){
return new Folder();
}
try {
imageDimension = getImageDimension();
} catch (FactoryException | TransformException e) {
e.printStackTrace();
}
pointAlpha = getPointAlpha();
Folder folder = new Folder();
BufferedImage image = new BufferedImage(imageDimension.width, imageDimension.height,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
g.setColor(createTransparentColor(ORIGINAL_POINT_COLOR, pointAlpha));
for (Point point : points) {
int centerX = getCenterX(point);
int centerY = getCenterY(point);
g.fillOval(centerX - POINT_DIAMETER / 2, centerY - POINT_DIAMETER / 2, POINT_DIAMETER, POINT_DIAMETER);
}
image = filterImage(image);
image.flush();
new File(schemaName).mkdir();
String imagePathInKml = null;
try {
File imageFile = File.createTempFile("heatmap", ".png");
ImageIO.write(image, "png", imageFile);
imagePathInKml = kmz.loadFile("heatmap"+(heatMapCounter++)+".png", imageFile);
} catch (IOException e) {
e.printStackTrace();
}
GroundOverlay groundOverlay = new GroundOverlay().withAltitude(0);
groundOverlay.createAndSetIcon().withHref(imagePathInKml);
LatLonBox ll = groundOverlay.createAndSetLatLonBox();
ll.withNorth(maxy).withSouth(miny).withEast(maxx).withWest(minx);
folder.addToFeature(groundOverlay);
return folder;
}
private int getPointAlpha() {
// int imageArea =imageDimension.width*imageDimension.height;
// double pointArea = ((POINT_DIAMETER+FIRST_BLUR_SIZE-1)* Math.PI)*points.size();
// double pointDensity = pointArea/imageArea;
//
// int alpha = (int) Math.max(1,Math.min(150, 2*1/pointDensity));
//
// logger.debug("Used alpha: " + alpha);
// return alpha;
return 1;
}
private BufferedImage filterImage(BufferedImage image) {
double maxAlpha = getMaxAlpha(image);
// logger.debug(maxAlpha);
ColorMap colorMap = new ColorMap(pointAlpha-1, maxAlpha*0.9, ColorMap.HUE_BLUE_TO_RED);
BufferedImage im = new BufferedImage(image.getWidth(), image.getHeight(), image.getType());
// blur image
int side = FIRST_BLUR_SIZE;
float[] blurMatrix = createBlurMatrix(side, side);
Kernel kernel = new Kernel(side, side, blurMatrix);
BufferedImageOp op = new ConvolveOp(kernel);
im = op.filter(image, null);
// change colors
for (int w = 0; w < im.getWidth(); w++) {
for (int h = 0; h < im.getHeight(); h++) {
int rgb = im.getRGB(w, h);
if (rgb == 0) {
im.setRGB(w, h, rgb);
} else {
int alpha = new Color(rgb, true).getAlpha();
if(alpha>0){
// logger.debug(alpha);
}
im.setRGB(w, h, colorMap.getColor(alpha).getRGB());
}
}
}
// blur again
side = SECOND_BLUR_SIZE;
blurMatrix = createBlurMatrix(side, side);
kernel = new Kernel(side, side, blurMatrix);
op = new ConvolveOp(kernel);
im = op.filter(im, null);
// reverse vertically
AffineTransform tx = AffineTransform.getScaleInstance(1, -1);
tx.translate(0, -im.getHeight(null));
AffineTransformOp opp = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
im = opp.filter(im, null);
return im;
}
private double getMaxAlpha(BufferedImage im) {
int max = Integer.MIN_VALUE;
for (int w = 0; w < im.getWidth(); w++) {
for (int h = 0; h < im.getHeight(); h++) {
int rgb = im.getRGB(w, h);
if (rgb == 0) {
im.setRGB(w, h, rgb);
} else {
int alpha = new Color(rgb, true).getAlpha();
max = Math.max(alpha, max);
}
}
}
return max;
}
private float[] createBlurMatrix(int height, int width) {
float[] matrix = new float[height * width];
for (int i = 0; i < matrix.length; i++) {
matrix[i] = 1f / (float) (height * width);
}
return matrix;
}
private int getCenterY(Point point) {
double bbYSize = maxy - miny;
double yy = point.getCentroid().getY() - miny;
return (int) (yy / bbYSize * imageDimension.height);
}
private int getCenterX(Point point) {
double bbXSize = maxx - minx;
double xx = point.getCentroid().getX() - minx;
return (int) (xx / bbXSize * imageDimension.width);
}
private Dimension getImageDimension() throws FactoryException, TransformException {
minx-=0.01;
miny-=0.01;
maxx+=0.01;
maxy+=0.01;
CoordinateReferenceSystem crs = CRS.decode("EPSG:4326", true);
Coordinate c1 = new Coordinate(minx, miny);
Coordinate c2 = new Coordinate(maxx, miny);
double widthDist = JTS.orthodromicDistance(c1, c2, crs);
c1 = new Coordinate(minx, miny);
c2 = new Coordinate(minx, maxy);
double heightDist = JTS.orthodromicDistance(c1, c2, crs);
return new Dimension((int) (widthDist * IMAGE_SCALE), (int) (heightDist * IMAGE_SCALE));
}
public static Color createTransparentColor(Color c, int alpha) {
return new Color(c.getRed(), c.getGreen(), c.getBlue(), alpha);
}
}