/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2010, 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.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.beans.PropertyChangeEvent;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import javax.media.jai.JAI;
import javax.media.jai.TileFactory;
import javax.media.jai.TileRecycler;
import org.apache.sis.measure.NumberRange;
import org.geotoolkit.storage.coverage.CoverageReference;
import org.geotoolkit.storage.coverage.PyramidalCoverageReference;
import org.geotoolkit.display.canvas.RenderingContext;
import org.geotoolkit.display.VisitFilter;
import org.geotoolkit.display.primitive.SceneNode;
import org.geotoolkit.display.SearchArea;
import org.geotoolkit.display2d.GO2Hints;
import org.geotoolkit.display2d.canvas.J2DCanvas;
import org.geotoolkit.display2d.canvas.RenderingContext2D;
import org.geotoolkit.display2d.primitive.GraphicJ2D;
import org.geotoolkit.map.CollectionMapLayer;
import org.geotoolkit.map.CoverageMapLayer;
import org.geotoolkit.map.FeatureMapLayer;
import org.geotoolkit.map.ItemListener;
import org.geotoolkit.map.MapContext;
import org.geotoolkit.map.MapItem;
import org.geotoolkit.map.MapLayer;
import org.geotoolkit.util.collection.CollectionChangeEvent;
import org.opengis.display.primitive.Graphic;
import org.opengis.geometry.Envelope;
/**
*
* @author Johann Sorel (Geomatys)
* @module
*/
public class StatelessMapItemJ2D<T extends MapItem> extends GraphicJ2D implements ItemListener {
private static final TileRecycler TILE_RECYCLER = (TileRecycler)JAI.getDefaultInstance().getRenderingHint(JAI.KEY_TILE_RECYCLER);
private static final TileFactory TILE_FACTORY = (TileFactory)JAI.getDefaultInstance().getRenderingHint(JAI.KEY_TILE_FACTORY);
private static final Point pt = new Point(0, 0);
private final ItemListener.Weak weakListener = new ItemListener.Weak(this);
//childs
private final Map<MapItem, GraphicJ2D> itemGraphics = new HashMap<>();
protected final T item;
public StatelessMapItemJ2D(final J2DCanvas canvas, final T item, boolean allowChildren){
super(canvas,allowChildren);
this.item = item;
//build children nodes
final List<MapItem> childs = item.items();
for(int i=0,n=childs.size(); i<n; i++){
final MapItem child = childs.get(i);
final GraphicJ2D gj2d = parseChild(child);
itemGraphics.put(child, gj2d);
getChildren().add(gj2d);
}
//listen to mapitem changes
weakListener.registerSource(item);
}
@Override
public void setVisible(boolean visible) {
item.setVisible(visible);
}
@Override
public boolean isVisible() {
return item.isVisible();
}
@Override
public T getUserObject() {
return item;
}
@Override
public Envelope getEnvelope() {
if(item instanceof MapContext){
try {
return ((MapContext)item).getBounds(true);
} catch (IOException ex) {
getLogger().log(Level.WARNING, ex.getMessage(), ex);
return null;
}
}else if(item instanceof MapLayer){
return ((MapLayer)item).getBounds();
}
return null;
}
@Override
public void dispose() {
super.dispose();
weakListener.dispose();
for(GraphicJ2D graphic : itemGraphics.values()){
graphic.dispose();
}
itemGraphics.clear();
}
// create graphics ---------------------------------------------------------
protected GraphicJ2D parseChild(final MapItem child){
//TODO simplify
final StatelessMapItemJ2D g2d;
if (child instanceof FeatureMapLayer) {
g2d = new StatelessFeatureLayerJ2D(getCanvas(), (FeatureMapLayer)child);
} else if (child instanceof CollectionMapLayer) {
g2d = new StatelessCollectionLayerJ2D(getCanvas(), (CollectionMapLayer)child);
} else if (child instanceof CoverageMapLayer) {
final CoverageMapLayer layer = (CoverageMapLayer) child;
final CoverageReference ref = layer.getCoverageReference();
if (ref != null && ref instanceof PyramidalCoverageReference
&& Boolean.TRUE.equals(canvas.getRenderingHint(GO2Hints.KEY_VIEW_TILE))) { //-- if view tile by tile is activate.
//-- pyramidal model, we can improve rendering
g2d = new StatelessPyramidalCoverageLayerJ2D(getCanvas(), (CoverageMapLayer) child);
} else {
//-- normal coverage
g2d = new StatelessCoverageLayerJ2D(getCanvas(), (CoverageMapLayer) child);
}
} else if (child instanceof MapLayer) {
g2d = new StatelessMapLayerJ2D(getCanvas(), (MapLayer)child, false);
} else {
g2d = new StatelessMapItemJ2D(getCanvas(), child, true);
}
return g2d;
}
@Override
public void paint(final RenderingContext2D renderingContext) {
//do not render children
}
/**
* {@inheritDoc }
*/
@Override
public List<Graphic> getGraphicAt(final RenderingContext rdcontext, final SearchArea mask, final VisitFilter filter, List<Graphic> graphics) {
//do not loop on children
return graphics;
}
// item listener ----------------------------------------------
@Override
public void propertyChange(final PropertyChangeEvent event) {
if(getCanvas().isAutoRepaint()){
final String propName = event.getPropertyName();
if(MapItem.VISIBILITY_PROPERTY.equals(propName)){
//TODO should call a repaint only on this graphic
getCanvas().repaint();
}
}
}
@Override
public void itemChange(final CollectionChangeEvent<MapItem> event) {
final int type = event.getType();
if(CollectionChangeEvent.ITEM_ADDED == type){
final NumberRange range = event.getRange();
int index = (int) range.getMinDouble();
for(final MapItem child : event.getItems()){
final GraphicJ2D gj2d = parseChild(child);
getChildren().add(index,(SceneNode)gj2d);
itemGraphics.put(child, gj2d);
index++;
}
//TODO should call a repaint only on this graphic
getCanvas().repaint();
}else if(CollectionChangeEvent.ITEM_REMOVED == type){
for(final MapItem child : event.getItems()){
//remove the graphic
final GraphicJ2D gra = itemGraphics.remove(child);
if(gra != null){
getChildren().remove(gra);
gra.dispose();
}
}
//TODO should call a repaint only on this graphic
getCanvas().repaint();
}
}
//tyling utilities ---------------------------------------------------------
protected static BufferedImage createBufferedImage(final ColorModel cm, final SampleModel model){
final WritableRaster raster = TILE_FACTORY.createTile(model, pt);
return new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
}
protected static void recycleBufferedImage(final BufferedImage img){
if(img != null){
TILE_RECYCLER.recycleTile(img.getRaster());
}
}
}