package com.github.davidmoten.rtree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import com.github.davidmoten.guavamini.Preconditions;
import com.github.davidmoten.guavamini.annotations.VisibleForTesting;
import com.github.davidmoten.rtree.geometry.HasGeometry;
import com.github.davidmoten.rtree.geometry.ListPair;
public final class SplitterRStar implements Splitter {
private final Comparator<ListPair<?>> comparator;
public SplitterRStar() {
this.comparator = new Comparator<ListPair<?>>() {
@Override
public int compare(ListPair<?> p1, ListPair<?> p2) {
//check overlap first then areaSum
int value = Float.compare(overlap(p1), overlap(p2));
if (value == 0) {
return Float.compare(p1.areaSum(), p2.areaSum());
} else {
return value;
}
}};
}
@Override
public <T extends HasGeometry> ListPair<T> split(List<T> items, int minSize) {
Preconditions.checkArgument(!items.isEmpty());
// sort nodes into increasing x, calculate min overlap where both groups
// have more than minChildren
// compute S the sum of all margin-values of the lists above
// the list with the least S is then used to find minimum overlap
List<ListPair<T>> pairs = null;
float lowestMarginSum = Float.MAX_VALUE;
List<T> list = null;
for (SortType sortType : SortType.values()) {
if (list == null) {
list = new ArrayList<T>(items);
}
Collections.sort(list, comparator(sortType));
List<ListPair<T>> p = getPairs(minSize, list);
float marginSum = marginValueSum(p);
if (marginSum < lowestMarginSum) {
lowestMarginSum = marginSum;
pairs = p;
// because p uses subViews of list we need to create a new one
// for further comparisons
list = null;
}
}
return Collections.min(pairs, comparator);
}
private static Comparator<HasGeometry> comparator(SortType sortType) {
switch (sortType) {
case X_LOWER:
return INCREASING_X_LOWER;
case X_UPPER:
return INCREASING_X_UPPER;
case Y_LOWER:
return INCREASING_Y_LOWER;
case Y_UPPER:
return INCREASING_Y_UPPER;
default:
throw new IllegalArgumentException("unknown SortType " + sortType);
}
}
private enum SortType {
X_LOWER, X_UPPER, Y_LOWER, Y_UPPER;
}
private static <T extends HasGeometry> float marginValueSum(List<ListPair<T>> list) {
float sum = 0;
for (ListPair<T> p : list)
sum += p.marginSum();
return sum;
}
@VisibleForTesting
static <T extends HasGeometry> List<ListPair<T>> getPairs(int minSize, List<T> list) {
List<ListPair<T>> pairs = new ArrayList<ListPair<T>>(list.size() - 2 * minSize + 1);
for (int i = minSize; i < list.size() - minSize + 1; i++) {
// Note that subList returns a view of list so creating list1 and
// list2 doesn't
// necessarily incur array allocation costs.
List<T> list1 = list.subList(0, i);
List<T> list2 = list.subList(i, list.size());
ListPair<T> pair = new ListPair<T>(list1, list2);
pairs.add(pair);
}
return pairs;
}
private static final Comparator<HasGeometry> INCREASING_X_LOWER = new Comparator<HasGeometry>() {
@Override
public int compare(HasGeometry n1, HasGeometry n2) {
return Float.compare(n1.geometry().mbr().x1(), n2.geometry().mbr().x1());
}
};
private static final Comparator<HasGeometry> INCREASING_X_UPPER = new Comparator<HasGeometry>() {
@Override
public int compare(HasGeometry n1, HasGeometry n2) {
return Float.compare(n1.geometry().mbr().x2(), n2.geometry().mbr().x2());
}
};
private static final Comparator<HasGeometry> INCREASING_Y_LOWER = new Comparator<HasGeometry>() {
@Override
public int compare(HasGeometry n1, HasGeometry n2) {
return Float.compare(n1.geometry().mbr().y1(), n2.geometry().mbr().y1());
}
};
private static final Comparator<HasGeometry> INCREASING_Y_UPPER = new Comparator<HasGeometry>() {
@Override
public int compare(HasGeometry n1, HasGeometry n2) {
return Float.compare(n1.geometry().mbr().y2(), n2.geometry().mbr().y2());
}
};
private static float overlap(ListPair<? extends HasGeometry> pair) {
return pair.group1().geometry().mbr()
.intersectionArea(pair.group2().geometry().mbr());
}
}