/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.eas.client.controls.geopane.cache;
import com.eas.client.controls.geopane.TileUtils;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.MapContent;
import org.geotools.renderer.GTRenderer;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.referencing.CRS;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.ProjectedCRS;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransform2D;
/**
*
* @author mg
*/
public abstract class MapTilesCache extends TilesCache {
public class RenderingTask implements Runnable {
protected RenderingHints renderingHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
/**
* tile coordinates in tile space
*/
protected Point tilePoint;
/**
* Simply bounds of tile's image
*/
protected Rectangle paintArea = new Rectangle(-tileSize / 2, -tileSize / 2, tileSize, tileSize);
/**
* Tile's center coordinatees in screen space.
*/
protected Point tileCenter;
/**
* Tile coordinates in map's cartesian space
*/
protected ReferencedEnvelope tileAoi;
protected GTRenderer taskRenderer;
protected AffineTransform tileTransform;
protected boolean stopped = false;
public RenderingTask(Point aTilePoint) throws NoninvertibleTransformException {
super();
tilePoint = new Point(aTilePoint);
tileCenter = expandPointFromCell(tilePoint);
calcAoi();
taskRenderer = archieveRenderer();
Map<Object, Object> hints = new HashMap<>();
hints.put(StreamingRenderer.ADVANCED_PROJECTION_HANDLING_KEY, Boolean.TRUE);
taskRenderer.setRendererHints(hints);
tileTransform = new AffineTransform(cartesian2ScreenTransform);
}
@Override
public void run() {
if (!isStopped() && isActual()) {
BufferedImage img = (BufferedImage) imagesQueue.poll();
if (img == null) {
//img = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().createCompatibleImage(tileSize, tileSize, imageType==BufferedImage.TYPE_INT_ARGB?Transparency.BITMASK:Transparency.OPAQUE);
//img.setAccelerationPriority(1);
img = new BufferedImage(tileSize, tileSize, imageType);
}
//BufferedImage img = new BufferedImage(tileSize, tileSize, imageType);
prepareImageTile(img);
Graphics g = img.createGraphics();
if (g instanceof Graphics2D) {
((Graphics2D) g).setRenderingHints(renderingHints);
}
g.translate(tileSize / 2, tileSize / 2);
tileTransform.translate(-tileCenter.x / tileTransform.getScaleX(), -tileCenter.y / tileTransform.getScaleY());
assert g instanceof Graphics2D;
assert taskRenderer != null;
if (!isStopped() && isActual()) {
taskRenderer.setMapContent(mapDisplayContext);
try {
taskRenderer.paint((Graphics2D) g, paintArea, tileAoi, tileTransform);
} catch (Exception ex) {
Logger.getLogger(MapTilesCache.class.getName()).severe(ex.getMessage());
}
g.dispose();
// If renderer will be stopped while taskRenderer.paint() we'll get a bad image, so we can't put it to the cache
// All other cases are tolerable.
// For example, if all work is completed, but task is nevertheless stopped.
// It's not good, but it's also not harmful.
if (!isStopped() && isActual()) {
put(tilePoint, img);
} else {
imagesQueue.add(img);
}
} else {
g.dispose();
imagesQueue.add(img);
}
}
}
public synchronized boolean isStopped() {
return stopped;
}
public synchronized void setStopped(boolean stopped) {
this.stopped = stopped;
}
protected boolean isActual() {
return true;
}
protected void prepareImageTile(BufferedImage img) {
Graphics2D g = img.createGraphics();
g.setBackground(background);
g.clearRect(0, 0, tileSize, tileSize);
g.dispose();
}
/**
* Rendered area in screen space related to tiles grid origin
* @return
*/
public Rectangle getRenderedArea() {
return TileUtils.expandRectFromCell(tilePoint, tileSize);
}
private void calcAoi() throws NoninvertibleTransformException {
Point2D.Double[] pts = new Point2D.Double[4];
pts[0] = new Point2D.Double(tileCenter.x - tileHalf, tileCenter.y - tileHalf);
pts[1] = new Point2D.Double(tileCenter.x + tileHalf, tileCenter.y - tileHalf);
pts[2] = new Point2D.Double(tileCenter.x - tileHalf, tileCenter.y + tileHalf);
pts[3] = new Point2D.Double(tileCenter.x + tileHalf, tileCenter.y + tileHalf);
cartesian2ScreenTransform.inverseTransform(pts[0], pts[0]);
cartesian2ScreenTransform.inverseTransform(pts[1], pts[1]);
cartesian2ScreenTransform.inverseTransform(pts[2], pts[2]);
cartesian2ScreenTransform.inverseTransform(pts[3], pts[3]);
double x1 = Double.MAX_VALUE;
double x2 = -Double.MAX_VALUE;
double y1 = Double.MAX_VALUE;
double y2 = -Double.MAX_VALUE;
for (Point2D.Double pt : pts) {
x1 = Math.min(x1, pt.x);
y1 = Math.min(y1, pt.y);
x2 = Math.max(x2, pt.x);
y2 = Math.max(y2, pt.y);
}
tileAoi = new ReferencedEnvelope(x1, x2, y1, y2, mapDisplayContextCrs);
}
protected GTRenderer archieveRenderer() {
return renderer;
}
public Point getTilePoint() {
return tilePoint;
}
}
/**
* Contains local query envelope (area of interest), coordinate reference system and list of layers.
*/
protected MapContent mapDisplayContext;
// Added to avoid synchronization problems with getCoordinateReferenceSystem() method of DefaultMapContext
protected CoordinateReferenceSystem mapDisplayContextCrs;
/**
* Cartesian to screen affine transformation. Initially hold information about user's view point;
*/
protected AffineTransform cartesian2ScreenTransform;
protected GTRenderer renderer = new StreamingRenderer();
protected MathTransform2D mapToCartesianTransform;
protected MathTransform2D cartesian2MapTransform;
public MapTilesCache(MapContent aDisplayContext, AffineTransform aTransform) {
super();
mapDisplayContext = aDisplayContext;
mapDisplayContextCrs = mapDisplayContext.getCoordinateReferenceSystem();
cartesian2ScreenTransform = aTransform;
}
public MapTilesCache(int aCacheSize, MapContent aDisplayContext, AffineTransform aTransform) {
this(aDisplayContext, aTransform);
cacheSize = aCacheSize;
}
@Override
protected abstract Image renderTile(Point ptKey);
public MapContent getMapDisplayContext() {
return mapDisplayContext;
}
public MathTransform2D getMap2CartesianTransform() throws FactoryException {
if (mapToCartesianTransform == null) {
if (mapDisplayContextCrs instanceof ProjectedCRS) {
ProjectedCRS pCrs = (ProjectedCRS) mapDisplayContextCrs;
MathTransform transform = CRS.findMathTransform(pCrs.getBaseCRS(), pCrs);
assert transform instanceof MathTransform2D;
mapToCartesianTransform = (MathTransform2D) transform;
return mapToCartesianTransform;
}
return null;
} else {
return mapToCartesianTransform;
}
}
public MathTransform2D getCartesian2MapTransform() throws FactoryException, org.opengis.referencing.operation.NoninvertibleTransformException {
if (cartesian2MapTransform == null) {
MathTransform2D transform = getMap2CartesianTransform();
cartesian2MapTransform = transform.inverse();
}
return cartesian2MapTransform;
}
}