/**
* OrbisGIS is a java GIS application dedicated to research in GIScience.
* OrbisGIS is developed by the GIS group of the DECIDE team of the
* Lab-STICC CNRS laboratory, see <http://www.lab-sticc.fr/>.
*
* The GIS group of the DECIDE team is located at :
*
* Laboratoire Lab-STICC – CNRS UMR 6285
* Equipe DECIDE
* UNIVERSITÉ DE BRETAGNE-SUD
* Institut Universitaire de Technologie de Vannes
* 8, Rue Montaigne - BP 561 56017 Vannes Cedex
*
* OrbisGIS is distributed under GPL 3 license.
*
* Copyright (C) 2007-2014 CNRS (IRSTV FR CNRS 2488)
* Copyright (C) 2015-2017 CNRS (Lab-STICC UMR CNRS 6285)
*
* This file is part of OrbisGIS.
*
* OrbisGIS is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OrbisGIS 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* OrbisGIS. If not, see <http://www.gnu.org/licenses/>.
*
* For more information, please consult: <http://www.orbisgis.org/>
* or contact directly:
* info_at_ orbisgis.org
*/
package org.orbisgis.coremap.renderer.se.fill;
import net.opengis.se._2_0.core.GraphicFillType;
import net.opengis.se._2_0.core.ObjectFactory;
import net.opengis.se._2_0.core.TileGapType;
import org.orbisgis.coremap.map.MapTransform;
import org.orbisgis.coremap.renderer.se.SeExceptions.InvalidStyle;
import org.orbisgis.coremap.renderer.se.SymbolizerNode;
import org.orbisgis.coremap.renderer.se.common.Uom;
import org.orbisgis.coremap.renderer.se.graphic.GraphicCollection;
import org.orbisgis.coremap.renderer.se.parameter.ParameterException;
import org.orbisgis.coremap.renderer.se.parameter.SeParameterFactory;
import org.orbisgis.coremap.renderer.se.parameter.real.RealParameter;
import org.orbisgis.coremap.renderer.se.parameter.real.RealParameterContext;
import javax.xml.bind.JAXBElement;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* A "GraphicFill" defines repeated-graphic filling (stippling) pattern for an area geometry.
* It is defined with a GraphicCollection (that will be used to draw the filling symbol), a Uom,
* and a gap vector. The gap vector is represented by its two (X and Y) coordinates,
* stored as <code>RealParameter</code> instances.
* @author Alexis Guéganno, Maxence Laurent
*/
public final class GraphicFill extends Fill {
private GraphicCollection graphic;
/**
* Distance between two graphics in the fill, in X direction.
*/
private RealParameter gapX;
/**
* Distance between two graphics in the fill, in Y direction.
*/
private RealParameter gapY;
/**
* Creates a new GraphicFill, with the gap's measures set to null.
*/
public GraphicFill() {
this.setGapX(null);
this.setGapY(null);
}
/**
* Creates a new GraphicFill directly from the Jaxb representation of the style.
* @param gft
* @throws org.orbisgis.coremap.renderer.se.SeExceptions.InvalidStyle
*/
public GraphicFill(GraphicFillType gft) throws InvalidStyle {
if (gft.getGraphic() != null) {
this.setGraphic(new GraphicCollection(gft.getGraphic(), this));
}
TileGapType gap = gft.getTileGap();
if (gap != null) {
if (gap.getX() != null) {
this.setGapX(SeParameterFactory.createRealParameter(gap.getX()));
}
if (gap.getY() != null) {
this.setGapY(SeParameterFactory.createRealParameter(gap.getY()));
}
}
if (gft.getUom() != null) {
this.setUom(Uom.fromOgcURN(gft.getUom()));
}
}
/**
* Creates a new GraphicFill directly from the Jaxb representation of the style.
* @param f
* @throws org.orbisgis.coremap.renderer.se.SeExceptions.InvalidStyle
*/
GraphicFill(JAXBElement<GraphicFillType> f) throws InvalidStyle {
this(f.getValue());
}
/**
* Set the GraphicCollection embedded in this GraphicFill. This is set as the parent of <code>graphic</code>
* @return
*/
public void setGraphic(GraphicCollection graphic) {
this.graphic = graphic;
graphic.setParent(this);
}
/**
* Get the GraphicCollection embedded in this GraphicFill.
* @return
*/
public GraphicCollection getGraphic() {
return graphic;
}
/**
* Set the gap, upon X direction, between two symbols.
* @param gap
*/
public void setGapX(RealParameter gap) {
gapX = gap;
if (gap != null) {
gap.setContext(RealParameterContext.NON_NEGATIVE_CONTEXT);
gap.setParent(this);
}
}
/**
* Set the gap, upon Y direction, between two symbols.
* @param gap
*/
public void setGapY(RealParameter gap) {
gapY = gap;
if (gap != null) {
gap.setContext(RealParameterContext.NON_NEGATIVE_CONTEXT);
gap.setParent(this);
}
}
/**
* Get the gap, upon X direction, between two symbols.
* @param gap
*/
public RealParameter getGapX() {
return gapX;
}
/**
* Get the gap, upon Y direction, between two symbols.
* @param gap
*/
public RealParameter getGapY() {
return gapY;
}
/**
* see Fill
*/
@Override
public void draw(Graphics2D g2, Map<String,Object> map, Shape shp, boolean selected, MapTransform mt) throws ParameterException, IOException {
Paint stipple = this.getPaint(map, selected, mt);
// TODO handle selected !
if (stipple != null) {
g2.setPaint(stipple);
g2.fill(shp);
}
}
/**
* Create a new TexturePaint according to this GraphicFill
*
* @param ds DataSet
* @param fid feature id
* @return a TexturePain ready to be used
* @throws ParameterException
* @throws IOException
*/
@Override
public Paint getPaint(Map<String,Object> map, boolean selected, MapTransform mt) throws ParameterException, IOException {
double gX = 0.0;
double gY = 0.0;
if (gapX != null) {
gX = gapX.getValue(map);
if (gX < 0.0) {
gX = 0.0;
}
}
if (gapY != null) {
gY = gapY.getValue(map);
if (gY < 0.0) {
gY = 0.0;
}
}
Rectangle2D bounds = graphic.getBounds(map, selected, mt);
gX = Uom.toPixel(gX, getUom(), mt.getDpi(), mt.getScaleDenominator(), bounds.getWidth());
gY = Uom.toPixel(gY, getUom(), mt.getDpi(), mt.getScaleDenominator(), bounds.getHeight());
return getPaint(map, selected, mt, graphic, gX, gY, bounds);
}
public static Paint getPaint(Map<String,Object> map, boolean selected,
MapTransform mt, GraphicCollection graphic, double gX, double gY, Rectangle2D bounds)
throws ParameterException, IOException {
if (bounds != null) {
Point2D.Double geoRef = new Point2D.Double(0, 0);
Point2D ref = mt.getAffineTransform().transform(geoRef, null);
int tWidth = (int) (bounds.getWidth() + gX);
int tHeight = (int) (bounds.getHeight() + gY);
int deltaX = (int) (ref.getX() - Math.ceil(ref.getX() / tWidth) * tWidth);
int deltaY = (int) (ref.getY() - Math.ceil(ref.getY() / tHeight) * tHeight);
BufferedImage i = new BufferedImage(tWidth, tHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D tile = i.createGraphics();
tile.setRenderingHints(mt.getRenderingHints());
int ix;
int iy;
for (ix = 0; ix < 2; ix++) {
for (iy = 0; iy < 2; iy++) {
graphic.draw(tile, map, selected, mt,
AffineTransform.getTranslateInstance(
-bounds.getMinX() + gX / 2.0 + deltaX + tWidth * ix,
-bounds.getMinY() + gY / 2.0 + deltaY + tHeight * iy));
}
}
return new TexturePaint(i, new Rectangle2D.Double(0, 0, i.getWidth(), i.getHeight()));
} else {
return null;
}
}
@Override
public GraphicFillType getJAXBType() {
GraphicFillType f = new GraphicFillType();
if (getOwnUom() != null) {
f.setUom(getOwnUom().toURN());
}
if (graphic != null) {
f.setGraphic(graphic.getJAXBElement());
}
if (gapX != null || gapY != null) {
TileGapType tile = new TileGapType();
if (gapX != null) {
tile.setX(gapX.getJAXBParameterValueType());
}
if (gapY != null) {
tile.setY(gapY.getJAXBParameterValueType());
}
f.setTileGap(tile);
}
return f;
}
@Override
public List<SymbolizerNode> getChildren() {
List<SymbolizerNode> ls = new ArrayList<SymbolizerNode>();
if (graphic != null) {
ls.add(graphic);
}
if (gapX != null) {
ls.add(gapX);
}
if (gapY != null) {
ls.add(gapY);
}
return ls;
}
@Override
public JAXBElement<GraphicFillType> getJAXBElement() {
ObjectFactory of = new ObjectFactory();
return of.createGraphicFill(this.getJAXBType());
}
}