/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2009-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.style.labeling.candidate;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.geotoolkit.display2d.canvas.RenderingContext2D;
/**
* Utility classes to compare label candidates.
*
* @author Johann Sorel (Geomatys)
* @module
*/
public class LabelingUtilities {
public static final Comparator<Candidate> XY_COMPARATOR = new Comparator<Candidate>() {
@Override
public int compare(Candidate c1, Candidate c2) {
final int diff = c2.getPriority() - c1.getPriority();
if(diff == 0){
if(c1 instanceof LinearCandidate){
return 1;
}else if(c2 instanceof LinearCandidate){
return -1;
}else{
final PointCandidate pc1 = (PointCandidate) c1;
final PointCandidate pc2 = (PointCandidate) c2;
int d = (int) ((pc2.x + pc2.y) - (pc1.x + pc1.y) +0.5);
if(d==0){
//we need to dissociate them
return System.identityHashCode(c1) - System.identityHashCode(c2);
}
return d;
}
}else{
return diff;
}
}
};
private LabelingUtilities(){}
public static List<Candidate> clipOutofBounds(final RenderingContext2D context, final List<Candidate> candidates){
final Rectangle bounds = context.getCanvasDisplayBounds();
final List<Candidate> correctCandidates = new ArrayList<Candidate>();
for(Candidate candidate : candidates){
if(candidate instanceof PointCandidate){
PointCandidate pc = (PointCandidate) candidate;
if(bounds.contains(pc.getBounds())){
correctCandidates.add(candidate);
}
}else{
correctCandidates.add(candidate);
}
}
return correctCandidates;
}
public static List<Candidate> sortByXY(final List<Candidate> candidates){
Collections.sort(candidates, XY_COMPARATOR);
return candidates;
}
public static List<Candidate> sortByCost(final List<Candidate> candidates){
Collections.sort(candidates, new Comparator<Candidate>() {
@Override
public int compare(Candidate c1, Candidate c2) {
int diff = c2.getPriority() - c1.getPriority();
if(diff == 0){
if(c1 instanceof LinearCandidate){
return -1;
}else if(c2 instanceof LinearCandidate){
return 1;
}else{
PointCandidate pc1 = (PointCandidate) c1;
PointCandidate pc2 = (PointCandidate) c2;
return pc2.getCost() - pc1.getCost();
}
}else{
return diff;
}
}
});
return candidates;
}
public static boolean intersects(final Candidate candidate, final Collection<? extends Candidate> candidates){
for(Candidate c : candidates){
if(intersects(candidate, c)){
return true;
}
}
return false;
}
public static boolean intersects(final Candidate candidate1, final Candidate candidate2){
if(candidate1 instanceof PointCandidate){
if(candidate2 instanceof PointCandidate){
return intersects((PointCandidate)candidate1, (PointCandidate)candidate2);
}else if(candidate2 instanceof LinearCandidate){
return intersects((LinearCandidate)candidate2, (PointCandidate)candidate1);
}
}else if(candidate1 instanceof LinearCandidate){
if(candidate2 instanceof PointCandidate){
return intersects((LinearCandidate)candidate1, (PointCandidate)candidate2);
}else if(candidate2 instanceof LinearCandidate){
return intersects((LinearCandidate)candidate1, (LinearCandidate)candidate2);
}
}
throw new IllegalArgumentException("Unexpected Candidate classes.");
}
public static boolean intersects(final PointCandidate label1, final PointCandidate label2){
return intersects(label1, null, label2, null,true);
}
public static boolean intersects(final PointCandidate label1, final Point combine1, final PointCandidate label2, final Point combine2, final boolean useCorrection){
final AffineTransform trs = new AffineTransform();
trs.translate(0, label1.upper);
trs.rotate(-Math.toRadians(label1.getDescriptor().getRotation()));
trs.translate(-label1.x, -label1.y);
if(useCorrection){
trs.translate(-label1.correctionX, -label1.correctionY);
}
if(combine1 != null){
trs.translate(-combine1.x, -combine1.y);
}
// at this step the label1 is horizontal and with start coordinate at (0,0)
trs.translate(label2.x, label2.y);
if(useCorrection){
trs.translate(label2.correctionX, label2.correctionY);
}
if(combine2 != null){
trs.translate(combine2.x, combine2.y);
}
trs.rotate(Math.toRadians(label2.getDescriptor().getRotation()));
trs.translate(0, -label2.upper);
//at this step the label2 is in the label1 normalize area
final int label1Height = label1.upper+label1.lower;
final int label2Height = label2.upper+label2.lower;
Point2D p1 = new Point2D.Double(0, 0);
Point2D p2 = new Point2D.Double(0, label2Height);
Point2D p3 = new Point2D.Double(label2.width, 0);
Point2D p4 = new Point2D.Double(label2.width, label2Height);
p1 = trs.transform(p1, p1);
p2 = trs.transform(p2, p2);
p3 = trs.transform(p3, p3);
p4 = trs.transform(p4, p4);
if( (p1.getX()<=0 && p2.getX()<=0 && p3.getX()<=0 && p4.getX()<=0)
|| (p1.getX()>=label1.width && p2.getX()>=label1.width && p3.getX()>=label1.width && p4.getX()>=label1.width)){
//label2 is on the left or on the right of label1
return false;
}
if( (p1.getY()<=0 && p2.getY()<=0 && p3.getY()<=0 && p4.getY()<=0)
|| (p1.getY()>=label1Height && p2.getY()>=label1Height && p3.getY()>=label1Height && p4.getY()>=label1Height)){
//label2 is above or under label1
return false;
}
final Rectangle2D rect = new Rectangle2D.Double(0, 0, label1.width, label1Height);
//test points within label1
if(rect.contains(p1)) return true;
if(rect.contains(p2)) return true;
if(rect.contains(p3)) return true;
if(rect.contains(p4)) return true;
//test border intersection
if(rect.intersectsLine(p1.getX(), p1.getY(), p2.getX(), p2.getY())) return true;
if(rect.intersectsLine(p2.getX(), p2.getY(), p4.getX(), p4.getY())) return true;
if(rect.intersectsLine(p4.getX(), p4.getY(), p3.getX(), p3.getY())) return true;
if(rect.intersectsLine(p3.getX(), p3.getY(), p1.getX(), p1.getY())) return true;
//check that the polygon is not contained in the other
double minx = Math.min( Math.min(p1.getX(), p2.getX()), Math.min(p3.getX(), p4.getX()) );
double maxx = Math.max( Math.max(p1.getX(), p2.getX()), Math.max(p3.getX(), p4.getX()) );
double miny = Math.min( Math.min(p1.getY(), p2.getY()), Math.min(p3.getY(), p4.getY()) );
double maxy = Math.max( Math.max(p1.getY(), p2.getY()), Math.max(p3.getY(), p4.getY()) );
if(minx <=0 && maxx >= label1.width && miny <=0 && maxy >= label1Height){
return true;
}
return false;
}
public static boolean intersects(final LinearCandidate linear, final PointCandidate point){
return false;
}
public static boolean intersects(final LinearCandidate linear1, final LinearCandidate linear2){
return false;
}
}