/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2008 - 2013, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.display2d.canvas;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import org.geotoolkit.display2d.GraphicVisitor;
import org.geotoolkit.display.canvas.RenderingContext;
import org.geotoolkit.display.VisitFilter;
import org.geotoolkit.display2d.GO2Hints;
import org.geotoolkit.display2d.GO2Utilities;
import org.geotoolkit.display2d.canvas.painter.BackgroundPainter;
import org.geotoolkit.display2d.primitive.DefaultSearchAreaJ2D;
import org.geotoolkit.display2d.primitive.GraphicJ2D;
import org.geotoolkit.display2d.primitive.SearchAreaJ2D;
import org.geotoolkit.display2d.style.labeling.LabelRenderer;
import org.geotoolkit.factory.Hints;
import org.geotoolkit.geometry.isoonjts.JTSUtils;
import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
import static org.apache.sis.util.ArgumentChecks.*;
import org.geotoolkit.display.canvas.AbstractCanvas2D;
import org.geotoolkit.display.container.GraphicContainer;
import org.geotoolkit.display.primitive.SceneNode;
import org.geotoolkit.display.primitive.SpatialNode;
import org.opengis.display.primitive.Graphic;
import org.opengis.geometry.Geometry;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform2D;
import org.opengis.referencing.operation.TransformException;
/**
*
* @author Johann Sorel (Geomatys)
* @module
*/
public abstract class J2DCanvas extends AbstractCanvas2D{
protected final RenderingContext2D context2D = new RenderingContext2D(this);
protected BackgroundPainter painter = null;
protected J2DCanvas(final CoordinateReferenceSystem crs,final Hints hints) {
super(crs,hints);
}
public void setBackgroundPainter(final BackgroundPainter painter) {
this.painter = painter;
}
public BackgroundPainter getBackgroundPainter() {
return painter;
}
@Override
public void dispose() {
super.dispose();
context2D.dispose();
}
protected Shape getObjectiveBounds() throws TransformException{
final MathTransform2D transform = (MathTransform2D) getObjectiveToDisplay().inverse();
final Shape bounds = getDisplayBounds();
return transform.createTransformedShape(bounds);
}
/**
* Prepare the renderingContext before painting, this will initialize the context
* with the correct bounds and transform datas.
* You may provide a null Graphic2D if you need to prepare a context for only a "hit"
* operation.
* @param context
* @param output
* @param paintingDisplayShape
* @return
*/
public RenderingContext2D prepareContext(final RenderingContext2D context,
final Graphics2D output, Shape paintingDisplayShape){
final Shape canvasDisplayShape = getDisplayBounds();
final AffineTransform2D objToDisp = getObjectiveToDisplay();
if(output != null) output.addRenderingHints(getHints(true));
final Shape canvasObjectShape;
try {
canvasObjectShape = getObjectiveBounds();
} catch (TransformException ex) {
monitor.exceptionOccured(ex, Level.WARNING);
//we can not continue with this kind of error
// nothing can be redered with this shape
return null;
}
final Shape paintingObjectiveShape;
if(paintingDisplayShape == null){
paintingDisplayShape = canvasDisplayShape;
paintingObjectiveShape = canvasObjectShape;
}else{
try {
AffineTransform dispToObj = objToDisp.createInverse();
paintingObjectiveShape = dispToObj.createTransformedShape(paintingDisplayShape);
} catch (NoninvertibleTransformException ex) {
monitor.exceptionOccured(ex, Level.WARNING);
//we can not continue with this kind of error
// nothing can be redered with this shape
return null;
}
}
//grab the dpi
Number dpi = (Number)getRenderingHint(GO2Hints.KEY_DPI);
if(dpi == null){
dpi = 90;
}
context.initParameters(
objToDisp,
monitor,
paintingDisplayShape,
paintingObjectiveShape,
canvasDisplayShape,
canvasObjectShape,
dpi.doubleValue());
if(output != null) context.initGraphic(output);
return context;
}
protected void render(final RenderingContext2D context2D, final List<SceneNode> graphics){
//TODO update multithreading rendering for scene tree model
// final Boolean mt = (Boolean) context2D.getRenderingHints().get(GO2Hints.KEY_MULTI_THREAD);
//
// if(Boolean.TRUE.equals(mt)){
// final MultiThreadedRendering rendering = new MultiThreadedRendering(this.item, this.itemGraphics, renderingContext);
// rendering.render();
// }else{
// for(final MapItem child : item.items()){
// if(renderingContext.getMonitor().stopRequested()) break;
// final GraphicJ2D gra = itemGraphics.get(child);
// if(gra != null){
// gra.paint(renderingContext);
// }else{
// getLogger().log(Level.WARNING, "GrahicContextJ2D, paint method : strange, no graphic object affected to layer :{0}", child.getName());
// }
// }
// }
/*
* Draw all graphics, starting with the one with the lowest <var>z</var> value. Before
* to start the actual drawing, we will notify all graphics that they are about to be
* drawn. Some graphics may spend one or two threads for pre-computing data.
*/
for(final Graphic graphic : graphics){
if(monitor.stopRequested()){
return;
}
if(graphic instanceof GraphicJ2D){
((GraphicJ2D) graphic).paint(context2D);
}
}
if(monitor.stopRequested()){
return;
}
//draw the labels
final LabelRenderer labelRenderer = context2D.getLabelRenderer(false);
if(labelRenderer != null){
try {
labelRenderer.portrayLabels();
} catch (TransformException ex) {
monitor.exceptionOccured(ex, Level.WARNING);
}
}
}
/**
* Visit the {@code Graphics} that occupy the given shape.
* You should give an Area Object if you can, this will avoid many creation
* while testing.
* @param displayShape
* @param visitor
* @param filter
*/
public void getGraphicsIn(final Shape displayShape, final GraphicVisitor visitor, final VisitFilter filter) {
ensureNonNull("mask", displayShape);
ensureNonNull("visitor", visitor);
ensureNonNull("filter", filter);
visitor.startVisit();
final GraphicContainer container = getContainer();
if(container != null){
final List<Graphic> candidates = new ArrayList<>();
final RenderingContext2D searchContext = context2D;
prepareContext(searchContext,null,null);
final AffineTransform dispToObj = searchContext.getDisplayToObjective();
final Shape objectiveShape = dispToObj.createTransformedShape(displayShape);
final com.vividsolutions.jts.geom.Geometry displayGeometryJTS = GO2Utilities.toJTS(displayShape);
final com.vividsolutions.jts.geom.Geometry objectiveGeometryJTS = GO2Utilities.toJTS(objectiveShape);
final Geometry displayGeometryISO = JTSUtils.toISO(displayGeometryJTS, getDisplayCRS());
final Geometry objectiveGeometryISO = JTSUtils.toISO(objectiveGeometryJTS, getObjectiveCRS2D());
final SearchAreaJ2D searchMask = new DefaultSearchAreaJ2D(
objectiveGeometryISO, displayGeometryISO,
objectiveGeometryJTS, displayGeometryJTS,
objectiveShape, displayShape);
final List<SceneNode> sorted = container.flatten(true);
//reverse the list order
Collections.reverse(sorted);
//see if the visitor request a stop-----------------------------
if(visitor.isStopRequested()){ visitor.endVisit(); return; }
//--------------------------------------------------------------
for(final Graphic graphic : sorted){
search(searchMask,searchContext,graphic,filter,candidates);
//see if the visitor request a stop-------------------------
if(visitor.isStopRequested()){ visitor.endVisit(); return; }
//----------------------------------------------------------
//send the found graphics to the visitor
for(final Graphic candidate : candidates){
visitor.visit(candidate,searchContext,searchMask);
//see if the visitor request a stop---------------------
if(visitor.isStopRequested()){ visitor.endVisit(); return; }
//------------------------------------------------------
}
//empty the list for next search
candidates.clear();
}
}
visitor.endVisit();
}
private void search(final SearchAreaJ2D mask, final RenderingContext context, final Graphic graphic, final VisitFilter filter, final List<Graphic> lst){
if(graphic instanceof SpatialNode){
final SpatialNode ref = (SpatialNode) graphic;
ref.getGraphicAt(context, mask, filter, lst);
}
}
}