/*
* 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);
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
;
}
}