package uk.ac.ox.zoo.seeg.abraid.mp.common.dao;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import org.joda.time.DateTime;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import uk.ac.ox.zoo.seeg.abraid.mp.common.AbstractCommonSpringIntegrationTests;
import uk.ac.ox.zoo.seeg.abraid.mp.common.domain.*;
import uk.ac.ox.zoo.seeg.abraid.mp.common.util.GeometryUtils;
import java.math.BigInteger;
import java.util.List;
import static ch.lambdaj.Lambda.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.offset;
import static org.hamcrest.core.IsEqual.equalTo;
/**
* Tests the NativeSQL class.
*
* Copyright (c) 2014 University of Oxford
*/
public class NativeSQLTest extends AbstractCommonSpringIntegrationTests {
@Autowired
private AdminUnitDiseaseExtentClassDao adminUnitDiseaseExtentClassDao;
@Autowired
private AdminUnitGlobalDao adminUnitGlobalDao;
@Autowired
private DiseaseExtentClassDao diseaseExtentClassDao;
@Autowired
private DiseaseGroupDao diseaseGroupDao;
@Autowired
private CountryDao countryDao;
@Autowired
private NativeSQLImpl nativeSQL;
@Autowired
private LocationDao locationDao;
@Test
public void findAdminUnitGlobalThatContainsPoint() {
Point point = GeometryUtils.createPoint(-124.2, 54.1);
Integer gaulCode = nativeSQL.findAdminUnitThatContainsPoint(point, true);
assertThat(gaulCode).isEqualTo(826);
}
@Test
public void findAdminUnitGlobalThatContainsPointReturnsNullIfNoGaulCodesContainThePoint() {
Point point = GeometryUtils.createPoint(0, 0);
Integer gaulCode = nativeSQL.findAdminUnitThatContainsPoint(point, true);
assertThat(gaulCode).isNull();
}
@Test
public void findAdminUnitGlobalWherePointIsOnBorder() {
Point point = GeometryUtils.createPoint(176, -38);
Integer gaulCode = nativeSQL.findAdminUnitThatContainsPoint(point, true);
assertThat(gaulCode).isEqualTo(179);
}
@Test
public void findAdminUnitTropicalThatContainsPoint() {
Point point = GeometryUtils.createPoint(-124.2, 54.1);
Integer gaulCode = nativeSQL.findAdminUnitThatContainsPoint(point, false);
assertThat(gaulCode).isEqualTo(46);
}
@Test
public void findAdminUnitTropicalWherePointIsOnBorder() {
Point point = GeometryUtils.createPoint(177, -39);
Integer gaulCode = nativeSQL.findAdminUnitThatContainsPoint(point, false);
assertThat(gaulCode).isEqualTo(179);
}
@Test
public void findAdminUnitTropicalThatContainsPointReturnsNullIfNoGaulCodesContainThePoint() {
Point point = GeometryUtils.createPoint(0, 0);
Integer gaulCode = nativeSQL.findAdminUnitThatContainsPoint(point, false);
assertThat(gaulCode).isNull();
}
@Test
public void doesLandSeaBorderContainPointReturnsTrueIfPointOnLand() {
Point point = GeometryUtils.createPoint(-1.43547, 52.41617);
boolean result = nativeSQL.doesLandSeaBorderContainPoint(point);
assertThat(result).isTrue();
}
@Test
public void doesLandSeaBorderContainPointReturnsFalseIfPointNotOnLand() {
Point point = GeometryUtils.createPoint(0, 0);
boolean result = nativeSQL.doesLandSeaBorderContainPoint(point);
assertThat(result).isFalse();
}
@Test
public void updateAggregatedDiseaseExtentForNewTropicalExtent() {
// Arrange - set the disease extent of admin units that have test geometries
int diseaseGroupId = 87;
updateExtentForTropicalDisease(diseaseGroupId);
// Arrange - find the number of polygons in the non-aggregated disease extent (i.e. that in
// admin_unit_disease_extent_class)
int expectedNumGeoms = getNumberOfPolygonsInAdminUnitDiseaseExtentClasses(diseaseGroupId, false);
int expectedNumGeomsOutside = getNumberOfPolygonsInAdminUnitDiseaseExtentClassesOutside(diseaseGroupId, false);
// Act
nativeSQL.updateAggregatedDiseaseExtent(diseaseGroupId, false);
// Assert - Check that number of polygons in the aggregated disease extent is as expected
int actualNumGeoms = getNumberOfPolygonsInDiseaseExtent(diseaseGroupId);
int actualNumGeomsOutside = getNumberOfPolygonsInOutsideDiseaseExtent(diseaseGroupId);
assertThat(actualNumGeoms).isEqualTo(expectedNumGeoms);
assertThat(actualNumGeomsOutside).isEqualTo(expectedNumGeomsOutside);
}
@Test
public void updateAggregatedDiseaseExtentForExistingGlobalExtent() {
// Arrange - insert and calculate an existing extent, for a global disease (cholera)
int diseaseGroupId = 64;
insertExtentForGlobalDisease(diseaseGroupId);
nativeSQL.updateAggregatedDiseaseExtent(diseaseGroupId, true);
int oldNumGeoms = getNumberOfPolygonsInAdminUnitDiseaseExtentClasses(diseaseGroupId, true);
// Arrange - update the extent and find new number of polygons
List<AdminUnitDiseaseExtentClass> dengueDiseaseExtent =
adminUnitDiseaseExtentClassDao.getAllGlobalAdminUnitDiseaseExtentClassesByDiseaseGroupId(diseaseGroupId);
updateAdminUnitDiseaseExtentClass(dengueDiseaseExtent, 179, DiseaseExtentClass.ABSENCE);
flushAndClear();
int expectedNewNumGeoms = getNumberOfPolygonsInAdminUnitDiseaseExtentClasses(diseaseGroupId, true);
int expectedNewNumGeomsOutside = getNumberOfPolygonsInAdminUnitDiseaseExtentClassesOutside(diseaseGroupId, true);
assertThat(oldNumGeoms).isNotEqualTo(expectedNewNumGeoms);
// Act
nativeSQL.updateAggregatedDiseaseExtent(diseaseGroupId, true);
// Assert - Check that number of polygons in the aggregated disease extent is as expected
int actualNewNumGeoms = getNumberOfPolygonsInDiseaseExtent(diseaseGroupId);
int actualNewNumGeomsOutside = getNumberOfPolygonsInOutsideDiseaseExtent(diseaseGroupId);
assertThat(actualNewNumGeoms).isEqualTo(expectedNewNumGeoms);
assertThat(actualNewNumGeomsOutside).isEqualTo(expectedNewNumGeomsOutside);
}
private void insertExtentForGlobalDisease(int diseaseGroupId) {
insertAdminUnitDiseaseExtentClass(153, diseaseGroupId, DiseaseExtentClass.ABSENCE, DiseaseExtentClass.ABSENCE);
insertAdminUnitDiseaseExtentClass(179, diseaseGroupId, DiseaseExtentClass.POSSIBLE_PRESENCE, DiseaseExtentClass.ABSENCE);
insertAdminUnitDiseaseExtentClass(826, diseaseGroupId, DiseaseExtentClass.PRESENCE, DiseaseExtentClass.POSSIBLE_PRESENCE);
flushAndClear();
}
private void updateExtentForTropicalDisease(int diseaseGroupId) {
List<AdminUnitDiseaseExtentClass> diseaseExtent =
adminUnitDiseaseExtentClassDao.getAllTropicalAdminUnitDiseaseExtentClassesByDiseaseGroupId(diseaseGroupId);
updateAdminUnitDiseaseExtentClass(diseaseExtent, 153, DiseaseExtentClass.ABSENCE);
updateAdminUnitDiseaseExtentClass(diseaseExtent, 179, DiseaseExtentClass.POSSIBLE_PRESENCE);
updateAdminUnitDiseaseExtentClass(diseaseExtent, 64, DiseaseExtentClass.PRESENCE);
flushAndClear();
}
private int getNumberOfPolygonsInAdminUnitDiseaseExtentClasses(int diseaseGroupId, boolean isGlobal) {
String globalOrTropical = isGlobal ? "global" : "tropical";
String expectedNumGeomsQuery =
"SELECT SUM(ST_NumGeometries(geom)) FROM admin_unit_%1$s WHERE gaul_code in " +
"(SELECT %1$s_gaul_code FROM admin_unit_disease_extent_class WHERE disease_group_id = " +
diseaseGroupId + " AND disease_extent_class IN ('POSSIBLE_PRESENCE', 'PRESENCE'))";
String formattedQuery = String.format(expectedNumGeomsQuery, globalOrTropical);
return ((BigInteger) uniqueSQLResult(formattedQuery)).intValue();
}
private int getNumberOfPolygonsInAdminUnitDiseaseExtentClassesOutside(int diseaseGroupId, boolean isGlobal) {
String globalOrTropical = isGlobal ? "global" : "tropical";
String expectedNumGeomsQuery =
"SELECT SUM(ST_NumGeometries(geom)) FROM admin_unit_%1$s WHERE gaul_code in " +
"(SELECT %1$s_gaul_code FROM admin_unit_disease_extent_class WHERE disease_group_id = " +
diseaseGroupId + " AND disease_extent_class NOT IN ('POSSIBLE_PRESENCE', 'PRESENCE'))";
String formattedQuery = String.format(expectedNumGeomsQuery, globalOrTropical);
return ((BigInteger) uniqueSQLResult(formattedQuery)).intValue();
}
private int getNumberOfPolygonsInDiseaseExtent(int diseaseGroupId) {
String actualNumGeomsQuery =
"SELECT ST_NumGeometries(geom) FROM disease_extent where disease_group_id = " + diseaseGroupId;
return (Integer) uniqueSQLResult(actualNumGeomsQuery);
}
private int getNumberOfPolygonsInOutsideDiseaseExtent(int diseaseGroupId) {
String actualNumGeomsQuery =
"SELECT ST_NumGeometries(outside_geom) FROM disease_extent where disease_group_id = " + diseaseGroupId;
return (Integer) uniqueSQLResult(actualNumGeomsQuery);
}
private void insertAdminUnitDiseaseExtentClass(int globalGaulCode, int diseaseGroupId, String extentClassName, String validatorExtentClassName) {
AdminUnitDiseaseExtentClass extentClass = new AdminUnitDiseaseExtentClass();
extentClass.setAdminUnitGlobal(adminUnitGlobalDao.getByGaulCode(globalGaulCode));
extentClass.setDiseaseGroup(diseaseGroupDao.getById(diseaseGroupId));
extentClass.setDiseaseExtentClass(diseaseExtentClassDao.getByName(extentClassName));
extentClass.setValidatorDiseaseExtentClass(diseaseExtentClassDao.getByName(validatorExtentClassName));
extentClass.setValidatorOccurrenceCount(0);
extentClass.setClassChangedDate(DateTime.now());
adminUnitDiseaseExtentClassDao.save(extentClass);
}
private void updateAdminUnitDiseaseExtentClass(List<AdminUnitDiseaseExtentClass> dengueDiseaseExtent,
int gaulCode, String extentClassName) {
AdminUnitDiseaseExtentClass extentClass = selectUnique(dengueDiseaseExtent,
having(on(AdminUnitDiseaseExtentClass.class).getAdminUnitGlobalOrTropical().getGaulCode(),
equalTo(gaulCode)));
extentClass.setDiseaseExtentClass(diseaseExtentClassDao.getByName(extentClassName));
adminUnitDiseaseExtentClassDao.save(extentClass);
}
@Test
public void findDistanceToDiseaseExtentPointGlobal() {
insertDiseaseExtent();
int id = setupDistanceTest(LocationPrecision.PRECISE, 1d, 4d, null, true, null, null);
assertThat(nativeSQL.findDistanceOutsideDiseaseExtent(87, true, id)).isEqualTo(156.75914, offset(0.00005));
assertThat(nativeSQL.findDistanceInsideDiseaseExtent(87, true, id)).isEqualTo(4460.23046, offset(0.00005));
}
@Test
public void findDistanceToDiseaseExtentPointTropical() {
insertDiseaseExtent();
int id = setupDistanceTest(LocationPrecision.PRECISE, 1d, 4d, null, false, null, null);
assertThat(nativeSQL.findDistanceOutsideDiseaseExtent(87, false, id)).isEqualTo(156.75914, offset(0.00005));
assertThat(nativeSQL.findDistanceInsideDiseaseExtent(87, false, id)).isEqualTo(4460.23046, offset(0.00005));
}
@Test
public void findDistanceToDiseaseExtentAdmin1Global() {
insertDiseaseExtent();
MultiPolygon geom = GeometryUtils.createMultiPolygon(getTriangle(0, 10), getTriangle(10, 12));
int id = setupDistanceTest(LocationPrecision.ADMIN1, 1d, 4d, geom, true, null, null);
assertThat(nativeSQL.findDistanceOutsideDiseaseExtent(87, true, id)).isEqualTo(891.60176, offset(0.00005));
assertThat(nativeSQL.findDistanceInsideDiseaseExtent(87, true, id)).isEqualTo(3401.72083, offset(0.00005));
}
@Test
public void findDistanceToDiseaseExtentAdmin1Tropical() {
insertDiseaseExtent();
MultiPolygon geom = GeometryUtils.createMultiPolygon(getTriangle(0, 10), getTriangle(10, 12));
int id = setupDistanceTest(LocationPrecision.ADMIN1, 1d, 4d, geom, false, null, null);
assertThat(nativeSQL.findDistanceOutsideDiseaseExtent(87, false, id)).isEqualTo(891.60176, offset(0.00005));
assertThat(nativeSQL.findDistanceInsideDiseaseExtent(87, false, id)).isEqualTo(3401.72083, offset(0.00005));
}
@Test
public void findDistanceToDiseaseExtentAdmin2Global() {
insertDiseaseExtent();
MultiPolygon geom = GeometryUtils.createMultiPolygon(getTriangle(0, 10), getTriangle(10, 12));
int id = setupDistanceTest(LocationPrecision.ADMIN2, 1d, 4d, geom, true, null, null);
assertThat(nativeSQL.findDistanceOutsideDiseaseExtent(87, true, id)).isEqualTo(891.60176, offset(0.00005));
assertThat(nativeSQL.findDistanceInsideDiseaseExtent(87, true, id)).isEqualTo(3401.72083, offset(0.00005));
}
@Test
public void findDistanceToDiseaseExtentAdmin2Tropical() {
insertDiseaseExtent();
MultiPolygon geom = GeometryUtils.createMultiPolygon(getTriangle(0, 10), getTriangle(10, 12));
int id = setupDistanceTest(LocationPrecision.ADMIN2, 1d, 4d, geom, false, null, null);
assertThat(nativeSQL.findDistanceOutsideDiseaseExtent(87, false, id)).isEqualTo(891.60176, offset(0.00005));
assertThat(nativeSQL.findDistanceInsideDiseaseExtent(87, false, id)).isEqualTo(3401.72083, offset(0.00005));
}
@Test
public void findDistanceToDiseaseExtentCountryGlobal() {
insertDiseaseExtent();
MultiPolygon geom = GeometryUtils.createMultiPolygon(getTriangle(0, 10), getTriangle(10, 12));
int id = setupDistanceTest(LocationPrecision.COUNTRY, 1d, 4d, geom, true, null, null);
assertThat(nativeSQL.findDistanceOutsideDiseaseExtent(87, true, id)).isEqualTo(891.60176, offset(0.00005));
assertThat(nativeSQL.findDistanceInsideDiseaseExtent(87, true, id)).isEqualTo(3401.72083, offset(0.00005));
}
@Test
public void findDistanceToDiseaseExtentCountryTropical() {
insertDiseaseExtent();
MultiPolygon geom = GeometryUtils.createMultiPolygon(getTriangle(0, 10), getTriangle(10, 12));
int id = setupDistanceTest(LocationPrecision.COUNTRY, 1d, 4d, geom, false, null, null);
assertThat(nativeSQL.findDistanceOutsideDiseaseExtent(87, false, id)).isEqualTo(891.60176, offset(0.00005));
assertThat(nativeSQL.findDistanceInsideDiseaseExtent(87, false, id)).isEqualTo(3401.72083, offset(0.00005));
}
@Test
public void findDistanceToDiseaseExtentCountrySplitGlobal() {
insertDiseaseExtent();
MultiPolygon geom = GeometryUtils.createMultiPolygon(getTriangle(0, 10), getTriangle(10, 12), getTriangle(20, 82), getTriangle(0, 5));
MultiPolygon subGeom1 = GeometryUtils.createMultiPolygon(getTriangle(0, 10), getTriangle(10, 12));
MultiPolygon subGeom2 = GeometryUtils.createMultiPolygon(getTriangle(20, 82));
int id = setupDistanceTest(LocationPrecision.COUNTRY, 1d, 4d, geom, true, subGeom1, subGeom2);
assertThat(nativeSQL.findDistanceOutsideDiseaseExtent(87, true, id)).isEqualTo(891.60176, offset(0.00005));
assertThat(nativeSQL.findDistanceInsideDiseaseExtent(87, true, id)).isEqualTo(3401.72083, offset(0.00005));
}
@Test
public void findDistanceToDiseaseExtentCountrySplitTropical() {
insertDiseaseExtent();
MultiPolygon geom = GeometryUtils.createMultiPolygon(getTriangle(0, 10), getTriangle(10, 12), getTriangle(20, 82), getTriangle(0, 5));
MultiPolygon subGeom1 = GeometryUtils.createMultiPolygon(getTriangle(0, 10), getTriangle(10, 12));
MultiPolygon subGeom2 = GeometryUtils.createMultiPolygon(getTriangle(20, 82));
int id = setupDistanceTest(LocationPrecision.COUNTRY, 1d, 4d, geom, false, subGeom1, subGeom2);
assertThat(nativeSQL.findDistanceOutsideDiseaseExtent(87, false, id)).isEqualTo(891.60176, offset(0.00005));
assertThat(nativeSQL.findDistanceInsideDiseaseExtent(87, false, id)).isEqualTo(3401.72083, offset(0.00005));
}
@Test
public void findDistanceToDiseaseExtentWithoutExtent() {
insertNullDiseaseExtent();
int id = setupDistanceTest(LocationPrecision.PRECISE, 1d, 4d, null, true, null, null);
assertThat(nativeSQL.findDistanceOutsideDiseaseExtent(87, true, id)).isNull();
assertThat(nativeSQL.findDistanceInsideDiseaseExtent(87, true, id)).isNull();
}
private Polygon getTriangle(double xOffset, double yOffset) {
return GeometryUtils.createPolygon(
xOffset + 1, yOffset + 1,
xOffset + 3, yOffset + 2,
xOffset + 2, yOffset + 3,
xOffset + 1, yOffset + 1);
}
private void insertDiseaseExtent() {
MultiPolygon geom = GeometryUtils.createMultiPolygon(getTriangle(0, 0), getTriangle(10, 0), getTriangle(20, 0), getTriangle(30, 0));
MultiPolygon outsideGeom = GeometryUtils.createMultiPolygon(getTriangle(40, 0), getTriangle(50, 0), getTriangle(60, 0), getTriangle(70, 0));
executeSQLUpdate("UPDATE disease_extent set geom=:geom, outside_geom=:outsideGeom where disease_group_id=:diseaseGroupId",
"diseaseGroupId", 87, "geom", geom, "outsideGeom", outsideGeom);
}
private void insertNullDiseaseExtent() {
executeSQLUpdate("UPDATE disease_extent set geom=:geom, outside_geom=:outsideGeom where disease_group_id=:diseaseGroupId",
"diseaseGroupId", 87, "geom", null, "outsideGeom", null);
}
private int setupDistanceTest(LocationPrecision precision, double x, double y, MultiPolygon linkedGeom, boolean isGlobal, MultiPolygon subLinkedGeom1, MultiPolygon subLinkedGeom2) {
Location location = new Location(x, y, precision);
if (precision == LocationPrecision.COUNTRY) {
executeSQLUpdate("UPDATE country SET geom=:geom WHERE gaul_code=93", "geom", linkedGeom);
location.setCountry(countryDao.getByName("Germany")); // GAUL 93 = "Germany"
if (subLinkedGeom1 != null || subLinkedGeom2 != null) {
executeSQLUpdate("UPDATE admin_unit_" + (isGlobal ? "global" : "tropical") + " SET geom=:geom WHERE gaul_code=27", "geom", subLinkedGeom1);
executeSQLUpdate("UPDATE admin_unit_" + (isGlobal ? "global" : "tropical") + " SET geom=:geom WHERE gaul_code=18", "geom", subLinkedGeom2);
executeSQLUpdate("INSERT INTO admin_unit_country (admin_unit_gaul_code, country_gaul_code) VALUES (27, 93)");
executeSQLUpdate("INSERT INTO admin_unit_country (admin_unit_gaul_code, country_gaul_code) VALUES (18, 93)");
}
} else if (precision == LocationPrecision.ADMIN1) {
executeSQLUpdate("UPDATE admin_unit_qc SET geom=:geom WHERE gaul_code=124", "geom", linkedGeom);
location.setAdminUnitQCGaulCode(124);
} else if (precision == LocationPrecision.ADMIN2) {
executeSQLUpdate("UPDATE admin_unit_qc SET geom=:geom WHERE gaul_code=125", "geom", linkedGeom);
location.setAdminUnitQCGaulCode(125);
}
locationDao.save(location);
return location.getId();
}
}