/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2008 - 2014, 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.container.stateless;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import org.geotoolkit.storage.coverage.CoverageStoreContentEvent;
import org.geotoolkit.storage.coverage.CoverageStoreListener;
import org.geotoolkit.storage.coverage.CoverageStoreManagementEvent;
import org.geotoolkit.display.canvas.RenderingContext;
import org.geotoolkit.display.VisitFilter;
import org.geotoolkit.display.PortrayalException;
import org.geotoolkit.display.SearchArea;
import org.geotoolkit.display2d.GO2Utilities;
import org.geotoolkit.display2d.canvas.J2DCanvas;
import org.geotoolkit.display2d.canvas.RenderingContext2D;
import org.geotoolkit.display2d.primitive.DefaultSearchAreaJ2D;
import org.geotoolkit.display2d.primitive.GraphicJ2D;
import org.geotoolkit.display2d.primitive.ProjectedCoverage;
import org.geotoolkit.display2d.primitive.SearchAreaJ2D;
import org.geotoolkit.display2d.style.CachedRule;
import org.geotoolkit.display2d.style.CachedSymbolizer;
import org.geotoolkit.geometry.jts.transform.CoordinateSequenceMathTransformer;
import org.geotoolkit.map.CoverageMapLayer;
import org.geotoolkit.map.GraphicBuilder;
import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
import org.opengis.display.primitive.Graphic;
import org.opengis.util.GenericName;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
/**
*
* @author Johann Sorel (Geomatys)
* @module
*/
public class StatelessCoverageLayerJ2D extends StatelessMapLayerJ2D<CoverageMapLayer> implements CoverageStoreListener{
protected CoverageStoreListener.Weak weakStoreListener = new CoverageStoreListener.Weak(this);
private final ProjectedCoverage projectedCoverage;
private final boolean ignoreBuilders;
//compare values to update caches if necessary
private final StatelessContextParams params;
private CoordinateReferenceSystem lastObjectiveCRS = null;
public StatelessCoverageLayerJ2D(final J2DCanvas canvas, final CoverageMapLayer layer){
this(canvas,layer,false);
}
public StatelessCoverageLayerJ2D(final J2DCanvas canvas, final CoverageMapLayer layer, final boolean ignoreBuilders){
super(canvas, layer, false);
this.ignoreBuilders = ignoreBuilders;
this.params = new StatelessContextParams(canvas,null);
this.projectedCoverage = new ProjectedCoverage(params, layer);
this.weakStoreListener.registerSource(layer.getCoverageReference());
}
private synchronized void updateCache(final RenderingContext2D context){
params.update(context);
params.objectiveCRS = context.getObjectiveCRS();
params.displayCRS = context.getDisplayCRS();
boolean objectiveCleared = false;
//clear objective cache is objective crs changed -----------------------
//todo use only the 2D CRS, the transform parameters are only used for the border
//geometry if needed, the gridcoverageReader will handle itself the transform
final CoordinateReferenceSystem objectiveCRS2D = context.getObjectiveCRS2D();
if(objectiveCRS2D != lastObjectiveCRS){
params.objectiveToDisplay.setToIdentity();
lastObjectiveCRS = objectiveCRS2D;
objectiveCleared = true;
projectedCoverage.clearObjectiveCache();
}
//clear display cache if needed ----------------------------------------
final AffineTransform2D objtoDisp = context.getObjectiveToDisplay();
if(!objtoDisp.equals(params.objectiveToDisplay)){
params.objectiveToDisplay.setTransform(objtoDisp);
((CoordinateSequenceMathTransformer)params.objToDisplayTransformer.getCSTransformer())
.setTransform(objtoDisp);
if(!objectiveCleared){
//no need to clear the display cache if the objective clear has already been called
projectedCoverage.clearDisplayCache();
}
}
}
/**
* {@inheritDoc }
*/
@Override
public void paintLayer(final RenderingContext2D renderingContext) {
final GenericName coverageName = item.getCoverageReference().getName();
final CachedRule[] rules = GO2Utilities.getValidCachedRules(item.getStyle(),
renderingContext.getSEScale(), coverageName,null);
//we perform a first check on the style to see if there is at least
//one valid rule at this scale, if not we just continue.
if (rules.length == 0) {
return;
}
paintRaster(item, rules, renderingContext);
}
private void paintRaster(final CoverageMapLayer item, final CachedRule[] rules,
final RenderingContext2D context) {
updateCache(context);
//search for a special graphic renderer
if(!ignoreBuilders){
final GraphicBuilder<GraphicJ2D> builder = (GraphicBuilder<GraphicJ2D>) item.getGraphicBuilder(GraphicJ2D.class);
if(builder != null){
//this layer has a special graphic rendering, use it instead of normal rendering
final Collection<GraphicJ2D> graphics = builder.createGraphics(item, getCanvas());
for(GraphicJ2D gra : graphics){
gra.paint(context);
}
return;
}
}
//no need to do this here, it may open a coverage reader for nothing
//if(!intersects(context.getCanvasObjectiveBounds2D())){
// //grid not in the envelope, we have finisehd
// return;
//}
for(final CachedRule rule : rules){
for(final CachedSymbolizer symbol : rule.symbolizers()){
try {
GO2Utilities.portray(projectedCoverage, symbol, context);
} catch (PortrayalException ex) {
context.getMonitor().exceptionOccured(ex, Level.WARNING);
}
}
}
}
/**
* {@inheritDoc }
*/
@Override
public List<Graphic> getGraphicAt(final RenderingContext context, final SearchArea mask, final VisitFilter filter, List<Graphic> graphics) {
if(!(context instanceof RenderingContext2D) ) return graphics;
if(!item.isSelectable()) return graphics;
if(!item.isVisible()) return graphics;
final RenderingContext2D renderingContext = (RenderingContext2D) context;
final GenericName coverageName = item.getCoverageReference().getName();
final CachedRule[] rules = GO2Utilities.getValidCachedRules(item.getStyle(),
renderingContext.getSEScale(), coverageName,null);
//we perform a first check on the style to see if there is at least
//one valid rule at this scale, if not we just continue.
if (rules.length == 0) {
return graphics;
}
if(graphics == null) graphics = new ArrayList<>();
if(mask instanceof SearchAreaJ2D){
graphics = searchAt(item,rules,renderingContext,(SearchAreaJ2D)mask,filter,graphics);
}else{
graphics = searchAt(item,rules,renderingContext,new DefaultSearchAreaJ2D(mask),filter,graphics);
}
return graphics;
}
private List<Graphic> searchAt(final CoverageMapLayer layer, final CachedRule[] rules,
final RenderingContext2D renderingContext, final SearchAreaJ2D mask, final VisitFilter filter, List<Graphic> graphics) {
updateCache(renderingContext);
final GraphicBuilder<GraphicJ2D> builder = (GraphicBuilder<GraphicJ2D>) layer.getGraphicBuilder(GraphicJ2D.class);
if(builder != null){
//this layer hasa special graphic rendering, use it instead of normal rendering
final Collection<GraphicJ2D> gras = builder.createGraphics(layer, canvas);
for(final GraphicJ2D gra : gras){
graphics = gra.getGraphicAt(renderingContext, mask, filter,graphics);
}
return graphics;
}
for (final CachedRule rule : rules) {
for (final CachedSymbolizer symbol : rule.symbolizers()) {
if(GO2Utilities.hit(projectedCoverage, symbol, renderingContext, mask, filter)){
graphics.add(projectedCoverage);
break;
}
}
}
return graphics;
}
@Override
public void structureChanged(CoverageStoreManagementEvent event) {
if(item.isVisible() && getCanvas().isAutoRepaint()){
//TODO should call a repaint only on this graphic
projectedCoverage.clearObjectiveCache();
getCanvas().repaint();
}
}
@Override
public void contentChanged(CoverageStoreContentEvent event) {
if(item.isVisible() && getCanvas().isAutoRepaint()){
//TODO should call a repaint only on this graphic
projectedCoverage.clearObjectiveCache();
getCanvas().repaint();
}
}
@Override
public void dispose() {
projectedCoverage.dispose();
super.dispose();
}
}