Match places with four decimal precision

pull/935/head
Alex Baker 4 years ago
parent c05386f18f
commit 84c83a9fac

@ -0,0 +1,46 @@
package org.tasks.caldav
import androidx.test.ext.junit.runners.AndroidJUnit4
import net.fortuna.ical4j.model.property.Geo
import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
import org.tasks.caldav.GeoUtils.equalish
import org.tasks.caldav.GeoUtils.latitudeLike
import org.tasks.caldav.GeoUtils.longitudeLike
@RunWith(AndroidJUnit4::class)
class GeoUtilsTest {
@Test
fun getLatitudeLike() =
assertEquals("42.4347%", newGeo(42.434722, -83.985).latitudeLike())
@Test
fun getLatitudeLikeShort() =
assertEquals("-37.3", newGeo(-37.3, -12.68).latitudeLike())
@Test
fun getLongitudeLike() =
assertEquals("-122.3736%", newGeo(45.43, -122.373611).longitudeLike())
@Test
fun getLongitudeLikeShort() =
assertEquals("-12.68", newGeo(-37.3, -12.68).longitudeLike())
@Test
fun compareGeo() =
assertTrue(newGeo(-37.3, -12.68).equalish(newGeo(-37.3, -12.68)))
@Test
fun compareGeoWithLatTruncation() =
assertTrue(newGeo(42.434722, -83.985).equalish(newGeo(42.4347, -83.985)))
@Test
fun compareGeoWithLongTruncation() =
assertTrue(newGeo(45.43, -122.373611).equalish(newGeo(45.43, -122.3736)))
@Test
fun compareGeoRightSideNull() = assertFalse(newGeo(63.4444, 10.9227).equalish(null))
private fun newGeo(latitude: Double, longitude: Double) = Geo("${latitude};${longitude}")
}

@ -0,0 +1,48 @@
package org.tasks.data
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.natpryce.makeiteasy.MakeItEasy.with
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.tasks.caldav.GeoUtils.toLikeString
import org.tasks.injection.InjectingTestCase
import org.tasks.injection.TestComponent
import org.tasks.makers.PlaceMaker.LATITUDE
import org.tasks.makers.PlaceMaker.LONGITUDE
import org.tasks.makers.PlaceMaker.newPlace
import javax.inject.Inject
@RunWith(AndroidJUnit4::class)
class LocationDaoTest : InjectingTestCase() {
@Inject lateinit var locationDao: LocationDao
@Test
fun getExistingPlace() {
locationDao.insert(newPlace(with(LATITUDE, 48.067222), with(LONGITUDE, 12.863611)))
val place = locationDao.findPlace(48.067222.toLikeString(), 12.863611.toLikeString())
assertEquals(48.067222, place?.latitude)
assertEquals(12.863611, place?.longitude)
}
@Test
fun getPlaceWithLessPrecision() {
locationDao.insert(newPlace(with(LATITUDE, 50.7547), with(LONGITUDE, -2.2279)));
val place = locationDao.findPlace(50.754712.toLikeString(), (-2.227945).toLikeString())
assertEquals(50.7547, place?.latitude)
assertEquals(-2.2279, place?.longitude)
}
@Test
fun getPlaceWithMorePrecision() {
locationDao.insert(newPlace(with(LATITUDE, 36.246944), with(LONGITUDE, -116.816944)))
locationDao.places.forEach { println(it) }
val place = locationDao.findPlace(36.2469.toLikeString(), (-116.8169).toLikeString())
assertEquals(36.246944, place?.latitude)
assertEquals(-116.816944, place?.longitude)
}
override fun inject(component: TestComponent) = component.inject(this)
}

@ -17,6 +17,7 @@ import dagger.Component;
import org.tasks.data.CaldavDaoTests;
import org.tasks.data.DeletionDaoTests;
import org.tasks.data.GoogleTaskDaoTests;
import org.tasks.data.LocationDaoTest;
import org.tasks.data.TagDataDaoTest;
import org.tasks.jobs.BackupServiceTests;
@ -59,4 +60,6 @@ public interface TestComponent extends ApplicationComponent {
void inject(CaldavDaoTests caldavDaoTests);
void inject(TaskMoverTest taskMoverTest);
void inject(LocationDaoTest locationDaoTest);
}

@ -0,0 +1,23 @@
package org.tasks.makers
import com.natpryce.makeiteasy.Instantiator
import com.natpryce.makeiteasy.Property
import com.natpryce.makeiteasy.PropertyLookup
import com.natpryce.makeiteasy.PropertyValue
import org.tasks.data.Place
object PlaceMaker {
val LATITUDE: Property<Place, Double> = Property.newProperty()
val LONGITUDE: Property<Place, Double> = Property.newProperty()
private val instantiator = Instantiator { lookup: PropertyLookup<Place> ->
val place = Place()
place.latitude = lookup.valueOf(LATITUDE, 0.0)
place.longitude = lookup.valueOf(LONGITUDE, 0.0)
place
}
fun newPlace(vararg properties: PropertyValue<in Place?, *>): Place {
return Maker.make(instantiator, *properties)
}
}

@ -0,0 +1,41 @@
package org.tasks.caldav
import net.fortuna.ical4j.model.property.Geo
import org.tasks.data.Location
import java.math.BigDecimal
import kotlin.math.min
object GeoUtils {
fun toGeo(location: Location?) = if (location == null) {
null
} else {
Geo("${location.latitude};${location.longitude}")
}
fun Geo.latitudeLike() = latitude.toLikeString()
fun Geo.longitudeLike() = longitude.toLikeString()
fun Double.toLikeString(): String = BigDecimal(toString()).toLikeString()
fun BigDecimal.toLikeString(): String {
val string = truncate()
return if (string.numDecimalPlaces() < 4) string else "${string}%"
}
fun Geo.equalish(other: Geo?): Boolean {
return latitude.truncate() == other?.latitude?.truncate()
&& longitude.truncate() == other?.longitude?.truncate()
}
private fun String.numDecimalPlaces(): Int {
val index = indexOf(".")
return if (index < 0) 0 else length - index - 1
}
private fun BigDecimal.truncate(): String {
val string = stripTrailingZeros().toPlainString()
val index = string.indexOf(".")
return if (index < 0) string else string.substring(0.until(min(string.length, index + 5)))
}
}

@ -60,6 +60,6 @@ public interface LocationDao {
"SELECT places.*, IFNULL(COUNT(geofence_id),0) AS count FROM places LEFT OUTER JOIN geofences ON geofences.place = places.uid GROUP BY uid ORDER BY COUNT(geofence_id) DESC")
LiveData<List<PlaceUsage>> getPlaceUsage();
@Query("SELECT * FROM places WHERE latitude = :latitude AND longitude = :longitude LIMIT 1")
Place findPlace(double latitude, double longitude);
@Query("SELECT * FROM places WHERE latitude LIKE :latitude AND longitude LIKE :longitude")
Place findPlace(String latitude, String longitude);
}

@ -44,8 +44,6 @@ public class Place implements Serializable, Parcelable {
}
};
private static final Pattern pattern = Pattern.compile("(\\d+):(\\d+):(\\d+\\.\\d+)");
private static final Pattern COORDS =
Pattern.compile("^\\d+°\\d+'\\d+\\.\\d+\"[NS] \\d+°\\d+'\\d+\\.\\d+\"[EW]$");
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "place_id")
@ -213,10 +211,13 @@ public class Place implements Serializable, Parcelable {
}
public String getDisplayName() {
if (Strings.isNullOrEmpty(address) || !COORDS.matcher(name).matches()) {
return Strings.isNullOrEmpty(name) ? formatCoordinates(this) : name;
if (!Strings.isNullOrEmpty(address)) {
return address;
}
return address;
if (!Strings.isNullOrEmpty(name)) {
return name;
}
return formatCoordinates(this);
}
public String getDisplayAddress() {

@ -50,6 +50,7 @@ import javax.inject.Inject;
import org.tasks.Event;
import org.tasks.R;
import org.tasks.billing.Inventory;
import org.tasks.caldav.GeoUtils;
import org.tasks.data.LocationDao;
import org.tasks.data.Place;
import org.tasks.data.PlaceUsage;
@ -331,7 +332,9 @@ public class LocationPickerActivity extends InjectingAppCompatActivity
}
if (place.getId() <= 0) {
org.tasks.data.Place existing =
locationDao.findPlace(place.getLatitude(), place.getLongitude());
locationDao.findPlace(
GeoUtils.INSTANCE.toLikeString(place.getLatitude()),
GeoUtils.INSTANCE.toLikeString(place.getLongitude()));
if (existing == null) {
place.setId(locationDao.insert(place));
} else {

Loading…
Cancel
Save