/*
* 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.ext.labeling;
import java.awt.Point;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.geotoolkit.display2d.style.labeling.candidate.Candidate;
import org.geotoolkit.display2d.style.labeling.candidate.LabelingUtilities;
import org.geotoolkit.display2d.style.labeling.candidate.PointCandidate;
/**
* Not effective implementation of simulated annealing.
* Really slow and no good result, experimentale.
*
* @author Johann Sorel (Geomatys)
* @module
*/
public class SimulatedAnnealing {
private SimulatedAnnealing(){
}
public static Set<Candidate> simulate(final List<Candidate> cdts, double temperature, final double coolDown){
final Map<Candidate,Point> candidates = new HashMap<Candidate,Point>();
for(Candidate cdt : cdts){
candidates.put(cdt, new Point(
(int) Math.floor(Math.random() * temperature - temperature/2),
(int) Math.floor(Math.random() * temperature - temperature/2)));
}
//calculate the cost without any changes
for(Candidate c : candidates.keySet()){
Point p = candidates.get(c);
c.setCost(solutionCost(c, p, candidates));
}
// double bestSolutionCost = solutionCost(candidates);
int sameResult = 0;
int count = 0;
//when we reach 10 times the same result, that means we haven't find anything better
while(sameResult < 10){
System.out.println(count++ +" "+ sameResult);
boolean change = false;
for(Candidate c : candidates.keySet()){
if(!(c instanceof PointCandidate)) continue;
Point p = candidates.get(c);
//TODO find the best local solution using label intersecting this label
boolean better = findBestLocalCombinaison((PointCandidate)c, p, candidates,temperature);
if(better){
System.out.println("found better");
change = true;
}
}
sameResult = (change) ? 0 : sameResult+1;
//This approach of a global solution cost to much CPU,
//we must find a way to make a local area best solution
// // See if this improved the global solution
// double solutionCost = solutionCost(candidates);
// if (solutionCost < bestSolutionCost) {
// bestSolutionCost = solutionCost;
//
// //solution is better, change the candidate displacements
// for(Candidate c : candidates.keySet()){
// Point p = candidates.get(c);
// if(c instanceof PointCandidate){
// PointCandidate pc = (PointCandidate) c;
// pc.x += pc.correctionX;
// pc.y += pc.correctionY;
// pc.correctionX = 0;
// pc.correctionY = 0;
// }
// }
//
// sameResult = 0;
// } else {
// sameResult++;
// }
//reduce temperature
temperature = coolDown * temperature;
}
for(Candidate c : candidates.keySet()){
Point p = candidates.get(c);
if(c instanceof PointCandidate){
PointCandidate pc = (PointCandidate) c;
pc.correctionX = p.x;
pc.correctionY = p.y;
}
}
return candidates.keySet();
}
private static boolean findBestLocalCombinaison(final PointCandidate c, final Point p, final Map<Candidate,Point> candidates, final double temperature){
Map<PointCandidate,Point> localCandidates = new HashMap<PointCandidate,Point>();
localCandidates.put(c, new Point(p));
//search for the candidates that intersect the current candidate
for(Candidate other : candidates.keySet()){
if(other == c) continue;
if(!(other instanceof PointCandidate)) continue;
Point otherPoint = candidates.get(other);
if(LabelingUtilities.intersects((PointCandidate) other,otherPoint, c, p, false)){
localCandidates.put((PointCandidate) other, new Point(otherPoint));
}
}
if(localCandidates.size() > 1){
final int originalCost = solutionLocalCost(localCandidates,candidates);
int cost = originalCost;
//search the best solution
int sameResult = 0;
while(sameResult < 5){
for(Candidate cdt : localCandidates.keySet()){
Point pt = localCandidates.get(cdt);
pt.x = (int) Math.floor(Math.random() * temperature - temperature/2);
pt.y = (int) Math.floor(Math.random() * temperature - temperature/2);
}
int acost = solutionLocalCost(localCandidates,candidates);
if(acost < cost){
cost = acost;
sameResult = 0;
for(Candidate cdt : localCandidates.keySet()){
Point pt = localCandidates.get(cdt);
candidates.get(cdt).setLocation(pt);
}
}else{
sameResult++;
}
}
return cost < originalCost;
}else{
return false;
}
}
/**
* Called to determine if annealing should take place.
*
* @param d The distance.
* @return True if annealing should take place.
*/
public boolean anneal(final double d, final double temperature) {
if (temperature < 1.0E-4) {
return (d > 0.0);
}
return (Math.random() < Math.exp(d / temperature));
}
private static int solutionCost(final Map<? extends Candidate,Point> candidates){
int d = 0;
for(Candidate c : candidates.keySet()){
Point p = candidates.get(c);
d += solutionCost(c, p, candidates);
}
return d;
}
private static int solutionLocalCost(final Map<? extends Candidate,Point> localCandidates, final Map<? extends Candidate,Point> candidates){
int d = 0;
for(Candidate c : localCandidates.keySet()){
Point p = localCandidates.get(c);
d += solutionCost(c, p, candidates);
}
return d;
}
private static int solutionCost(final Candidate candidate, final Point p, final Map<? extends Candidate,Point> candidates){
int d = Math.abs(p.x) + Math.abs(p.y);
for(Candidate other : candidates.keySet()){
if(other == candidate) continue;
if(candidate instanceof PointCandidate && other instanceof PointCandidate){
Point otherPoint = candidates.get(other);
if(candidate.getPriority() > other.getPriority()){
continue;
}else if(candidate.getPriority() == other.getPriority()){
if(LabelingUtilities.intersects((PointCandidate)candidate, p, (PointCandidate)other, otherPoint,false)){
d += 10;
}
}else{
if(LabelingUtilities.intersects((PointCandidate)candidate, p, (PointCandidate)other, otherPoint,false)){
d += 20;
}
}
}
}
return d;
}
}