package org.wikibrain.spatial.distance; import com.vividsolutions.jts.geom.*; import org.apache.commons.io.FileUtils; import org.geotools.data.simple.SimpleFeatureIterator; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.opengis.feature.simple.SimpleFeature; import org.wikibrain.core.dao.DaoException; import org.wikibrain.spatial.WikiBrainShapeFile; import org.wikibrain.spatial.constants.Precision; import org.wikibrain.spatial.dao.SpatialDataDao; import org.wikibrain.spatial.util.WikiBrainSpatialUtils; import org.wikibrain.utils.Scoreboard; import org.wikibrain.utils.WpIOUtils; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** * @author Shilad Sen */ public class TestBorderingDistanceMetric { private static final String SHP_PREFIX = "cb_2013_us_state_20m"; private Random random = new Random(); private GeometryFactory factory = new GeometryFactory(new PrecisionModel(),8307); private File shpDir; private List<StateGeom> states; private BorderingDistanceMetric metric; static final class StateGeom { public String name; public int id; public Geometry geometry; } @Before public void prepareMetric() throws IOException, DaoException { shpDir = WpIOUtils.createTempDirectory("wikibrain-state-shp"); for (String ext : WikiBrainShapeFile.EXTENSIONS) { String src = "/states/" + SHP_PREFIX + ext; InputStream is = WpIOUtils.class.getResourceAsStream(src); if (is == null) { throw new FileNotFoundException("Unknown resource: " + src); } File dest = FileUtils.getFile(shpDir, SHP_PREFIX + ext); FileUtils.copyInputStreamToFile(is, dest); } FileUtils.forceDeleteOnExit(shpDir); WikiBrainShapeFile shp = new WikiBrainShapeFile(FileUtils.getFile(shpDir, SHP_PREFIX + ".shp")); SimpleFeatureIterator iter = shp.getFeatureIter(); states = new ArrayList<StateGeom>(); Map<Integer, Geometry> points = new HashMap<Integer, Geometry>(); int i = 0; while (iter.hasNext()) { SimpleFeature f = iter.next(); StateGeom sg = new StateGeom(); sg.name = (String) f.getAttribute("NAME"); sg.geometry = (Geometry) f.getDefaultGeometry(); sg.id = i++; points.put(sg.id, sg.geometry); states.add(sg); } SpatialDataDao dao = mock(SpatialDataDao.class); when(dao.getAllGeometriesInLayer("states", Precision.LatLonPrecision.HIGH)) .thenReturn(points); metric = new BorderingDistanceMetric(dao, "states"); metric.enableCache(true); } @After public void cleanupDao() { FileUtils.deleteQuietly(shpDir); } @Test public void testSimple() throws DaoException { assertEquals(0, metric.distance(g("Minnesota"), g("Minnesota")), 0.01); assertEquals(1, metric.distance(g("Wisconsin"), g("Minnesota")), 0.01); assertEquals(1, metric.distance(g("North Dakota"), g("Minnesota")), 0.01); assertEquals(1, metric.distance(g("South Dakota"), g("Minnesota")), 0.01); assertEquals(1, metric.distance(g("Iowa"), g("Minnesota")), 0.01); assertEquals(2, metric.distance(g("Illinois"), g("Minnesota")), 0.01); assertEquals(4, metric.distance(g("Texas"), g("Minnesota")), 0.01); // MN -> IA -> MO -> MS -> TX // Test the point variant assertEquals(0, metric.distance(g("Minnesota").getCentroid(), g("Minnesota")), 0.01); assertEquals(0, metric.distance(g("Minnesota"), g("Minnesota").getCentroid()), 0.01); assertEquals(4, metric.distance(g("Minnesota").getCentroid(), g("Texas").getCentroid()), 0.01); } @Test public void testForceContains() throws DaoException { metric.setForceContains(true); // Test polygons assertEquals(0, metric.distance(g("Minnesota"), g("Minnesota")), 0.01); assertEquals(1, metric.distance(g("North Dakota"), g("Minnesota")), 0.01); // Test the point variant assertEquals(0, metric.distance(g("Minnesota").getCentroid(), g("Minnesota")), 0.01); assertEquals(1, metric.distance(g("North Dakota").getCentroid(), g("Minnesota")), 0.01); // Points nearby states should behave identically to the nearby states. Point aboveMn = WikiBrainSpatialUtils.getPoint( g("Minnesota").getEnvelopeInternal().getMaxY() + 2, g("Minnesota").getCentroid().getX() ); assertEquals(0, metric.distance(aboveMn, g("Minnesota")), 0.01); assertEquals(1, metric.distance(aboveMn, g("North Dakota")), 0.01); } @Test public void testKnn() throws DaoException { List<SpatialDistanceMetric.Neighbor> neighbors = metric.getNeighbors(g("Minnesota"), 100); for (int i = 0; i < neighbors.size(); i++) { SpatialDistanceMetric.Neighbor n = neighbors.get(i); String name = n(n.conceptId); if (i == 0) { assertEquals(name, "Minnesota"); } else if (i <= 4) { assertTrue(Arrays.asList("North Dakota", "Iowa", "Wisconsin", "South Dakota").contains(name)); } } SpatialDistanceMetric.Neighbor last = neighbors.get(neighbors.size() - 1); assertEquals(8.0, last.distance, 0.01); assertEquals("Maine", n(last.conceptId)); } @Test public void testMatrix() throws DaoException { } private String n(int id) { for (StateGeom sg : states) { if (sg.id == id) { return sg.name; } } throw new IllegalArgumentException("Unknown id: " + id); } private Geometry g(String name) { for (StateGeom sg : states) { if (sg.name.equalsIgnoreCase(name)) { return sg.geometry; } } throw new IllegalArgumentException(name); } }