/*
* Copyright (C) 2014.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 or
* version 2 as published by the Free Software Foundation.
*
* 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.
*/
package uk.me.parabola.mkgmap.filters;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.mkgmap.general.MapShape;
//import uk.me.parabola.util.GpxCreator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class ShapeMergeFilterTest {
// create one Coord instance for each point in a small test grid
private static final HashMap<Integer,Coord> map = new HashMap<Integer,Coord>(){
{
for (int lat30 = 0; lat30 < 100; lat30 +=5){
for (int lon30 = 0; lon30 < 100; lon30 += 5){
Coord co = Coord.makeHighPrecCoord(lat30, lon30);
put(lat30*1000 + lon30,co);
}
}
}
};
@Test
public void testAreaTestVal(){
List<Coord> points = new ArrayList<Coord>(){{
add(getPoint(10,10));
add(getPoint(30,10));
add(getPoint(30,30));
add(getPoint(10,30));
add(getPoint(10,10)); // close
}};
assertEquals(2 * (20 * 20),ShapeMergeFilter.calcAreaSizeTestVal(points));
}
/**
* two simple shapes, sharing one point
*/
@Test
public void testSimpleSharingOne(){
List<Coord> points1 = new ArrayList<Coord>(){{
add(getPoint(15,10));
add(getPoint(30,25));
add(getPoint(25,30));
add(getPoint(10,30));
add(getPoint(5,20));
add(getPoint(15,10)); // close
}};
List<Coord> points2 = new ArrayList<Coord>(){{
add(getPoint(25,30));
add(getPoint(30,35));
add(getPoint(20,40));
add(getPoint(15,35));
add(getPoint(25,30));
}};
testVariants("simple shapes sharing one point", points1, points2,1,10);
}
/**
* two simple shapes, sharing one edge
*/
@Test
public void testSimpleNonOverlapping(){
List<Coord> points1 = new ArrayList<Coord>(){{
add(getPoint(15,10));
add(getPoint(30,25));
add(getPoint(25,30));
add(getPoint(15,35));
add(getPoint(5,20));
add(getPoint(15,10)); // close
}};
List<Coord> points2 = new ArrayList<Coord>(){{
add(getPoint(25,30));
add(getPoint(30,35));
add(getPoint(20,40));
add(getPoint(15,35));
add(getPoint(25,30));
}};
testVariants("simple shapes", points1, points2,1,8);
}
/**
* two simple shapes, sharing three consecutive points
*/
@Test
public void test3SharedPointsNonOverlapping(){
List<Coord> points1 = new ArrayList<Coord>(){{
add(getPoint(15,10));
add(getPoint(30,25));
add(getPoint(25,30));
add(getPoint(20,35));
add(getPoint(15,35));
add(getPoint(5,20));
add(getPoint(15,10));// close
}};
List<Coord> points2 = new ArrayList<Coord>(){{
add(getPoint(25,30));
add(getPoint(30,35));
add(getPoint(20,40));
add(getPoint(15,35));
add(getPoint(20,35));
add(getPoint(25,30));// close
}};
testVariants("test 3 consecutive shared points", points1, points2, 1, 8);
}
/**
* two simple shapes, sharing three consecutive points
*/
@Test
public void test2SharedPointsNoEdge(){
List<Coord> points1 = new ArrayList<Coord>(){{
add(getPoint(15,10));
add(getPoint(30,25));
add(getPoint(25,30));
add(getPoint(15,35));
add(getPoint(5,20));
add(getPoint(15,10));// close
}};
List<Coord> points2 = new ArrayList<Coord>(){{
add(getPoint(25,30));
add(getPoint(30,35));
add(getPoint(20,40));
add(getPoint(15,35));
add(getPoint(20,35));
add(getPoint(25,30));// close
}};
testVariants("test 2 non-consecutive shared points", points1, points2, 1, 11);
}
/**
* one u-formed shape, the other closes it to a rectangular shape with a hole
* They are sharing 4 points.
*/
@Test
public void testCloseUFormed(){
List<Coord> points1 = new ArrayList<Coord>(){{
// u-formed shaped (open at top)
add(getPoint(15,50));
add(getPoint(30,50));
add(getPoint(30,55));
add(getPoint(20,55));
add(getPoint(20,65));
add(getPoint(30,65));
add(getPoint(30,70));
add(getPoint(15,70));
add(getPoint(15,50));// close
}};
List<Coord> points2 = new ArrayList<Coord>(){{
add(getPoint(35,50));
add(getPoint(35,70));
add(getPoint(30,70));
add(getPoint(30,65));
add(getPoint(30,55));
add(getPoint(30,50));
add(getPoint(35,50)); // close
}};
testVariants("test close U formed shape", points1, points2, 1, 11);
}
/**
* one u-formed shape, the fits into the u and shares all points
*/
@Test
public void testFillUFormed(){
List<Coord> points1 = new ArrayList<Coord>(){{
// u-formed shaped (open at top)
add(getPoint(15,50));
add(getPoint(30,50));
add(getPoint(30,55));
add(getPoint(20,55));
add(getPoint(20,65));
add(getPoint(30,65));
add(getPoint(30,70));
add(getPoint(15,70));
add(getPoint(15,50)); // close
}};
List<Coord> points2 = new ArrayList<Coord>(){{
add(getPoint(30,55));
add(getPoint(30,65));
add(getPoint(20,65));
add(getPoint(20,55));
add(getPoint(30,55)); // close
}};
testVariants("test fill U-formed shape", points1, points2, 1, 5);
}
/**
* one u-formed shape, the fits into the u and shares all points
*/
@Test
public void testFillHole(){
List<Coord> points1 = new ArrayList<Coord>(){{
// a rectangle with a hole
add(getPoint(35,50));
add(getPoint(35,70));
add(getPoint(15,70));
add(getPoint(15,50));
add(getPoint(30,50));
add(getPoint(30,55));
add(getPoint(20,55));
add(getPoint(20,65));
add(getPoint(30,65));
add(getPoint(30,50));
add(getPoint(35,50));// close
}};
List<Coord> points2 = new ArrayList<Coord>(){{
add(getPoint(30,55));
add(getPoint(30,65));
add(getPoint(20,65));
add(getPoint(20,55));
add(getPoint(30,55)); // close
}};
testVariants("test-fill-hole", points1, points2, 1, 6); // expect 8 points if spike is not removed
}
@Test
public void testDuplicate(){
List<Coord> points1 = new ArrayList<Coord>(){{
add(getPoint(30,55));
add(getPoint(30,65));
add(getPoint(20,65));
add(getPoint(20,55));
add(getPoint(30,55)); // close
}};
List<Coord> points2 = new ArrayList<Coord>(points1);
testVariants("test duplicate", points1, points2, 1, 5);
}
@Test
public void testOverlap(){
List<Coord> points1 = new ArrayList<Coord>(){{
add(getPoint(30,55));
add(getPoint(30,65));
add(getPoint(20,65));
add(getPoint(20,55));
add(getPoint(30,55)); // close
}};
List<Coord> points2 = new ArrayList<Coord>(){{
add(getPoint(30,55));
add(getPoint(30,65));
add(getPoint(25,65));
add(getPoint(25,55));
add(getPoint(30,55)); // close
}};
// no merge expected
testVariants("test overlap", points1, points2, 2, 5);
}
/*
* shapes are connected at multiple edges like two
* w-formed shapes.
*/
@Test
public void testTwoWShaped(){
List<Coord> points1 = new ArrayList<Coord>(){{
add(getPoint(0,5));
add(getPoint(35,5));
add(getPoint(35,20));
add(getPoint(30,15));
add(getPoint(25,20));
add(getPoint(25,10));
add(getPoint(15,10));
add(getPoint(15,20));
add(getPoint(10,15));
add(getPoint(5,20));
add(getPoint(0,20));
add(getPoint(0,5)); // close
}};
List<Coord> points2 = new ArrayList<Coord>(){{
add(getPoint(35,35));
add(getPoint(35,20));
add(getPoint(30,15));
add(getPoint(25,20));
add(getPoint(25,25));
add(getPoint(15,25));
add(getPoint(15,20));
add(getPoint(10,15));
add(getPoint(5,20));
add(getPoint(0,20));
add(getPoint(5,35));
add(getPoint(35,35)); // close
}};
// wanted: merge that removes at least the longer shared sequence
testVariants("test two w-shaped", points1, points2, 1, 16);
}
/**
* Test all variants regarding clockwise/ccw direction and positions of the points
* in the list and the order of shapes.
* @param list1
* @param list2
*/
void testVariants(String msg, List<Coord> list1, List<Coord> list2, int expectedNumShapes, int expectedNumPoints){
MapShape s1 = new MapShape(1);
MapShape s2 = new MapShape(2);
s1.setMinResolution(22);
s2.setMinResolution(22);
for (int i = 0; i < 4; i++){
for (int j = 0; j < list1.size(); j++){
List<Coord> points1 = new ArrayList<>(list1);
if ((i & 1) != 0)
Collections.reverse(points1);
points1.remove(points1.size()-1);
Collections.rotate(points1, j);
points1.add(points1.get(0));
s1.setPoints(points1);
for (int k = 0; k < list2.size(); k++){
List<Coord> points2 = new ArrayList<>(list2);
if ((i & 2) != 0)
Collections.reverse(points2);
points2.remove(points2.size()-1);
Collections.rotate(points2, k);
points2.add(points2.get(0));
s2.setPoints(points2);
for (int l = 0; l < 2; l++){
String testId = msg+" i="+i+",j="+j+",k="+k+",l="+l;
if (l == 0)
testOneVariant(testId, s1, s2, expectedNumShapes,expectedNumPoints);
else
testOneVariant(testId, s2, s1, expectedNumShapes,expectedNumPoints);
}
}
}
}
return;
}
void testOneVariant(String testId, MapShape s1, MapShape s2, int expectedNumShapes, int expectedNumPoints){
ShapeMergeFilter smf = new ShapeMergeFilter(24, false);
List<MapShape> res = smf.merge(Arrays.asList(s1,s2));
assertTrue(testId, res != null);
assertEquals(testId,expectedNumShapes, res.size() );
// if (res.get(0).getPoints().size() != expectedNumPoints){
// GpxCreator.createGpx("e:/ld/s1", s1.getPoints());
// GpxCreator.createGpx("e:/ld/s2", s2.getPoints());
// GpxCreator.createGpx("e:/ld/res", res.get(0).getPoints());
// }
assertEquals(testId, expectedNumPoints, res.get(0).getPoints().size());
// TODO: test shape size
}
Coord getPoint(int lat, int lon){
Coord co = map.get(lat*1000+lon);
assert co != null;
return co;
}
}