package org.activityinfo.server.report.generator.map.cluster.genetic;
/*
* #%L
* ActivityInfo Server
* %%
* Copyright (C) 2009 - 2013 UNICEF
* %%
* This program 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.
*
* This program 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 this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
import org.activityinfo.legacy.shared.reports.content.Point;
import org.activityinfo.legacy.shared.reports.model.PointValue;
import java.util.ArrayList;
import java.util.List;
/**
* Graph structure used to determine which groups of PointValues (nodes) could
* potentially overlap with each other.
*/
public class MarkerGraph {
private List<List<Node>> subgraphs;
/**
* Represents an edge between two <code>PointValue</code>s (
* <code>Node</code>s) on the map. Two <code>PointValue</code> are connected
* if they potentially overlap.
*/
public static class Edge {
private Node a;
private Node b;
private int subgraph = -1;
public Edge(Node a, Node b) {
this.a = a;
this.b = b;
}
public Node neighbor(Node node) {
return node == a ? b : a;
}
public double length() {
return a.getPoint().distance(b.getPoint());
}
public Node getA() {
return a;
}
public Node getB() {
return b;
}
}
/**
* Wraps <code>PointValue</code> as a node in the graph
*/
public static class Node {
private List<Edge> edges;
private PointValue pv;
private int subgraph = -1;
public Node(PointValue pv) {
this.pv = pv;
this.edges = new ArrayList<Edge>();
}
public void addEdge(Edge e) {
edges.add(e);
}
public Point getPoint() {
return pv.getPx();
}
public PointValue getPointValue() {
return pv;
}
public List<Edge> getEdges() {
return edges;
}
public double getValue() {
return pv.getValue();
}
}
public interface IntersectionCalculator {
public boolean intersects(Node a, Node b);
}
private List<Node> nodes;
private List<Edge> edges;
/**
* Constructs a graph of <code>PointValue</code>
*
* @param points
*/
public MarkerGraph(List<PointValue> points, IntersectionCalculator icalculator) {
nodes = new ArrayList<Node>();
edges = new ArrayList<Edge>();
for (PointValue pv : points) {
nodes.add(new Node(pv));
}
for (int i = 0; i != nodes.size(); ++i) {
Node ni = nodes.get(i);
int j = i + 1;
while (j < nodes.size()) {
Node nj = nodes.get(j);
if (icalculator.intersects(ni, nj)) {
// check for coincidence, which screws things up
// if we leave in place
if (ni.getPoint().equals(nj.getPoint())) {
// merge nodes in the case of coincidence
ni.getPointValue().setValue(ni.getPointValue().getValue() + nj.getPointValue().getValue());
for (Edge edge : nj.getEdges()) {
edge.neighbor(nj).getEdges().remove(edge);
edges.remove(edge);
}
nodes.remove(j);
} else {
// otherwise connect them in the graph
Edge e = new Edge(ni, nj);
ni.addEdge(e);
nj.addEdge(e);
edges.add(e);
j++;
}
} else {
j++;
}
}
}
subgraphs = new ArrayList<List<Node>>();
int nextSubgraph = 0;
for (Node node : nodes) {
if (node.subgraph < 0) {
List<Node> subgraph = new ArrayList<Node>();
subgraph.add(node);
subgraphs.add(subgraph);
node.subgraph = nextSubgraph++;
assignToSubgraph(node, subgraph);
}
}
}
private void assignToSubgraph(Node node, List<Node> subgraph) {
for (Edge edge : node.getEdges()) {
if (edge.subgraph < 0) {
edge.subgraph = node.subgraph;
Node neighbor = edge.neighbor(node);
if (neighbor.subgraph < 0) {
neighbor.subgraph = node.subgraph;
subgraph.add(neighbor);
assignToSubgraph(neighbor, subgraph);
}
}
}
}
public List<Edge> getEdges() {
return edges;
}
public List<Node> getNodes() {
return nodes;
}
public List<List<Node>> getSubgraphs() {
return subgraphs;
}
}