diff --git a/config/checkstyle/checkstyle-suppressions.xml b/config/checkstyle/checkstyle-suppressions.xml
index d594fa81..6e76047e 100644
--- a/config/checkstyle/checkstyle-suppressions.xml
+++ b/config/checkstyle/checkstyle-suppressions.xml
@@ -12,4 +12,5 @@
+
diff --git a/core/build.gradle b/core/build.gradle
index a325c3d0..86467cb0 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -82,5 +82,12 @@ task verify(dependsOn: ['compileDebugSources',
apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
dependencies {
-
+ compile 'com.mapzen.android:lost:2.1.2'
+ compile 'com.mapzen.android:pelias-android-sdk:1.0.0'
+
+ testCompile 'junit:junit:4.12'
+ testCompile 'org.assertj:assertj-core:1.7.1'
+ testCompile 'org.powermock:powermock:1.6.4'
+ testCompile 'org.powermock:powermock-module-junit4:1.6.4'
+ testCompile 'org.powermock:powermock-api-mockito:1.6.4'
}
diff --git a/mapzen-android-sdk/build.gradle b/mapzen-android-sdk/build.gradle
index 92ca35db..722f7e35 100644
--- a/mapzen-android-sdk/build.gradle
+++ b/mapzen-android-sdk/build.gradle
@@ -101,9 +101,8 @@ dependencies {
compile 'com.mapzen:mapzen-core:0.0.1-SNAPSHOT'
compile "com.mapzen.tangram:tangram:$tangram_version"
- compile 'com.mapzen.android:lost:2.1.2'
compile 'com.mapzen:on-the-road:1.1.1'
- compile 'com.mapzen.android:pelias-android-sdk:1.0.0'
+
compile 'com.google.dagger:dagger:2.0'
compile 'javax.annotation:javax.annotation-api:1.2'
diff --git a/mapzen-places-api/build.gradle b/mapzen-places-api/build.gradle
index 0d4cb4d0..48230636 100644
--- a/mapzen-places-api/build.gradle
+++ b/mapzen-places-api/build.gradle
@@ -31,7 +31,6 @@ release {
afterReleaseBuild.dependsOn uploadArchives
-
android {
compileSdkVersion 24
buildToolsVersion "24.0.3"
@@ -51,6 +50,9 @@ android {
testOptions {
unitTests.returnDefaultValues = true
}
+ lintOptions {
+ abortOnError false
+ }
}
tasks.withType(Test) {
@@ -61,13 +63,29 @@ tasks.withType(Test) {
}
}
+task checkstyle(type: Checkstyle) {
+ configFile file("${project.rootDir}/config/checkstyle/checkstyle.xml")
+ source 'src'
+ include '**/*.java'
+ exclude '**/gen/**'
+
+ classpath = files()
+}
+
+task verify(dependsOn: ['compileDebugSources',
+ 'test',
+ 'checkstyle',
+ 'lint'])
+
dependencies {
compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.mapzen:mapzen-core:0.0.1-SNAPSHOT'
testCompile 'junit:junit:4.12'
testCompile 'org.assertj:assertj-core:1.7.1'
+ testCompile 'org.powermock:powermock:1.6.4'
+ testCompile 'org.powermock:powermock-module-junit4:1.6.4'
+ testCompile 'org.powermock:powermock-api-mockito:1.6.4'
}
-
apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
diff --git a/mapzen-places-api/src/main/java/com/mapzen/places/api/AutocompleteFilter.java b/mapzen-places-api/src/main/java/com/mapzen/places/api/AutocompleteFilter.java
new file mode 100644
index 00000000..a9b9b356
--- /dev/null
+++ b/mapzen-places-api/src/main/java/com/mapzen/places/api/AutocompleteFilter.java
@@ -0,0 +1,8 @@
+package com.mapzen.places.api;
+
+/**
+ * Filter for customizing autocomplete results from {@link GeoDataApi}.
+ */
+public class AutocompleteFilter {
+
+}
diff --git a/mapzen-places-api/src/main/java/com/mapzen/places/api/AutocompletePrediction.java b/mapzen-places-api/src/main/java/com/mapzen/places/api/AutocompletePrediction.java
new file mode 100644
index 00000000..8f26edbc
--- /dev/null
+++ b/mapzen-places-api/src/main/java/com/mapzen/places/api/AutocompletePrediction.java
@@ -0,0 +1,71 @@
+package com.mapzen.places.api;
+
+import android.support.annotation.Nullable;
+import android.text.style.CharacterStyle;
+
+import java.util.List;
+
+/**
+ * Represents a place returned from {@link GeoDataApi#getAutocompletePredictions(
+ * com.mapzen.android.lost.api.LostApiClient, String, LatLngBounds, AutocompleteFilter)}.
+ */
+public class AutocompletePrediction {
+
+ private final String placeId;
+ private final String primaryText;
+
+ /**
+ * Constructs a new prediction given an id an primary text.
+ * @param id
+ * @param text
+ */
+ public AutocompletePrediction(String id, String text) {
+ placeId = id;
+ primaryText = text;
+ }
+
+ /**
+ * Not implemented yet.
+ * @param characterStyle
+ * @return
+ */
+ public CharSequence getFullText(@Nullable CharacterStyle characterStyle) {
+ throw new RuntimeException("Not implemented yet");
+ }
+
+ /**
+ * Returns the prediction's primary text.
+ * @param characterStyle
+ * @return
+ */
+ public CharSequence getPrimaryText(@Nullable CharacterStyle characterStyle) {
+ return primaryText;
+ }
+
+ /**
+ * Not implemented yet.
+ * @param characterStyle
+ * @return
+ */
+ public CharSequence getSecondaryText(@Nullable CharacterStyle characterStyle) {
+ throw new RuntimeException("Not implemented yet");
+ }
+
+ /**
+ * Return's the prediction's id.
+ * @return
+ */
+ @Nullable
+ public String getPlaceId() {
+ return placeId;
+ }
+
+ /**
+ * Not implemented yet.
+ * @return
+ */
+ @Nullable
+ public List getPlaceTypes() {
+ throw new RuntimeException("Not implemented yet");
+ }
+}
diff --git a/mapzen-places-api/src/main/java/com/mapzen/places/api/AutocompletePredictionBuffer.java b/mapzen-places-api/src/main/java/com/mapzen/places/api/AutocompletePredictionBuffer.java
new file mode 100644
index 00000000..cda1c32f
--- /dev/null
+++ b/mapzen-places-api/src/main/java/com/mapzen/places/api/AutocompletePredictionBuffer.java
@@ -0,0 +1,43 @@
+package com.mapzen.places.api;
+
+import com.mapzen.android.lost.api.Result;
+import com.mapzen.android.lost.api.Status;
+
+import java.util.List;
+
+/**
+ * Represents a list of autocomplete results.
+ */
+public class AutocompletePredictionBuffer implements Result, DataBuffer {
+
+ private final Status status;
+ private final List predictions;
+
+ /**
+ * Constructs a new buffer given a status and list of autocomplete results.
+ * @param status
+ * @param predictions
+ */
+ public AutocompletePredictionBuffer(Status status, List predictions) {
+ this.status = status;
+ this.predictions = predictions;
+ }
+
+ @Override public Status getStatus() {
+ return status;
+ }
+
+ @Override public int getCount() {
+ if (predictions == null) {
+ return 0;
+ }
+ return predictions.size();
+ }
+
+ @Override public AutocompletePrediction get(int index) {
+ if (predictions == null || index < 0 || index > predictions.size() - 1) {
+ return null;
+ }
+ return predictions.get(index);
+ }
+}
diff --git a/mapzen-places-api/src/main/java/com/mapzen/places/api/DataBuffer.java b/mapzen-places-api/src/main/java/com/mapzen/places/api/DataBuffer.java
new file mode 100644
index 00000000..f3f80c75
--- /dev/null
+++ b/mapzen-places-api/src/main/java/com/mapzen/places/api/DataBuffer.java
@@ -0,0 +1,20 @@
+package com.mapzen.places.api;
+
+/**
+ * Generic interface for representing data contained in a buffer.
+ * @param
+ */
+public interface DataBuffer {
+ /**
+ * Returns the number of objects in the buffer.
+ * @return
+ */
+ int getCount();
+
+ /**
+ * Returns the object at a given index.
+ * @param index
+ * @return
+ */
+ T get(int index);
+}
diff --git a/mapzen-places-api/src/main/java/com/mapzen/places/api/GeoDataApi.java b/mapzen-places-api/src/main/java/com/mapzen/places/api/GeoDataApi.java
new file mode 100644
index 00000000..1639ecf8
--- /dev/null
+++ b/mapzen-places-api/src/main/java/com/mapzen/places/api/GeoDataApi.java
@@ -0,0 +1,20 @@
+package com.mapzen.places.api;
+
+import com.mapzen.android.lost.api.LostApiClient;
+import com.mapzen.android.lost.api.PendingResult;
+
+/**
+ * Main entry point for the Mapzen Places Geo Data API.
+ */
+public interface GeoDataApi {
+ /**
+ * Returns an object which can be used to retrieve autocomplete results.
+ * @param client
+ * @param query
+ * @param bounds
+ * @param filter
+ * @return
+ */
+ PendingResult getAutocompletePredictions(LostApiClient client,
+ String query, LatLngBounds bounds, AutocompleteFilter filter);
+}
diff --git a/mapzen-places-api/src/main/java/com/mapzen/places/api/LatLng.java b/mapzen-places-api/src/main/java/com/mapzen/places/api/LatLng.java
new file mode 100644
index 00000000..990bc288
--- /dev/null
+++ b/mapzen-places-api/src/main/java/com/mapzen/places/api/LatLng.java
@@ -0,0 +1,46 @@
+package com.mapzen.places.api;
+
+/**
+ * Represents a pair of coordinates as degrees.
+ */
+public class LatLng {
+
+ private static final double LAT_MIN = -90;
+ private static final double LAT_MAX = 90;
+ private static final double LNG_MIN = -180;
+ private static final double LNG_MAX = 180;
+ private static final double ALL_LNGS = 360;
+
+ private final double latitude;
+ private final double longitude;
+
+ /**
+ * Constructs a new object given a latitude and longitude in degrees.
+ * @param lat
+ * @param lng
+ */
+ public LatLng(double lat, double lng) {
+ if (LNG_MIN <= lng && lng < LNG_MAX) {
+ this.longitude = lng;
+ } else {
+ this.longitude = ((lng - LNG_MAX) % ALL_LNGS + ALL_LNGS) % ALL_LNGS - LNG_MAX;
+ }
+ this.latitude = Math.max(LAT_MIN, Math.min(LAT_MAX, lat));
+ }
+
+ /**
+ * Latitude, in degrees. This value is in the range [-90, 90].
+ * @return
+ */
+ public double getLatitude() {
+ return latitude;
+ }
+
+ /**
+ * Longitude, in degrees. This value is in the range [-180, 180].
+ * @return
+ */
+ public double getLongitude() {
+ return longitude;
+ }
+}
diff --git a/mapzen-places-api/src/main/java/com/mapzen/places/api/LatLngBounds.java b/mapzen-places-api/src/main/java/com/mapzen/places/api/LatLngBounds.java
new file mode 100644
index 00000000..18355cd5
--- /dev/null
+++ b/mapzen-places-api/src/main/java/com/mapzen/places/api/LatLngBounds.java
@@ -0,0 +1,142 @@
+package com.mapzen.places.api;
+
+/**
+ * Represents a rectangular area.
+ */
+public class LatLngBounds {
+
+ private final LatLng northeast;
+ private final LatLng southwest;
+
+ /**
+ * Constructs a new object given southwest and northwest points.
+ * @param southwest
+ * @param northeast
+ */
+ public LatLngBounds(LatLng southwest, LatLng northeast) {
+ this.southwest = southwest;
+ this.northeast = northeast;
+ }
+
+ public LatLng getSouthwest() {
+ return southwest;
+ }
+
+ public LatLng getNortheast() {
+ return northeast;
+ }
+
+ /**
+ * Determines whether the given point is contained within the lat/lng's bounds.
+ * @param point
+ * @return
+ */
+ public boolean contains(LatLng point) {
+ return this.includesLat(point.getLatitude()) && this.includesLng(point.getLongitude());
+ }
+
+ /**
+ * Returns a new object which includes the given point.
+ * @param point
+ * @return
+ */
+ public LatLngBounds including(LatLng point) {
+ double swLat = Math.min(this.southwest.getLatitude(), point.getLatitude());
+ double neLat = Math.max(this.northeast.getLatitude(), point.getLatitude());
+ double neLng = this.northeast.getLongitude();
+ double swLng = this.southwest.getLongitude();
+ double ptLng = point.getLongitude();
+ if (!this.includesLng(ptLng)) {
+ if (swLngMod(swLng, ptLng) < neLngMod(neLng, ptLng)) {
+ swLng = ptLng;
+ } else {
+ neLng = ptLng;
+ }
+ }
+
+ return new LatLngBounds(new LatLng(swLat, swLng), new LatLng(neLat, neLng));
+ }
+
+ /**
+ * Returns the center of the lat/lng bounds.
+ * @return
+ */
+ public LatLng getCenter() {
+ double midLat = (this.southwest.getLatitude() + this.northeast.getLatitude()) / 2.0D;
+ double neLng = this.northeast.getLongitude();
+ double swLng = this.southwest.getLongitude();
+ double midLng;
+ if (swLng <= neLng) {
+ midLng = (neLng + swLng) / 2.0D;
+ } else {
+ midLng = (neLng + 360.0D + swLng) / 2.0D;
+ }
+
+ return new LatLng(midLat, midLng);
+ }
+
+ private static double swLngMod(double swLng, double ptLng) {
+ return (swLng - ptLng + 360.0D) % 360.0D;
+ }
+
+ private static double neLngMod(double neLng, double ptLng) {
+ return (ptLng - neLng + 360.0D) % 360.0D;
+ }
+
+ private boolean includesLat(double lat) {
+ return this.southwest.getLatitude() <= lat && lat <= this.northeast.getLatitude();
+ }
+
+ private boolean includesLng(double lng) {
+ return this.southwest.getLongitude() <= this.northeast.getLongitude() ?
+ this.southwest.getLongitude() <= lng && lng <= this.northeast.getLongitude() :
+ this.southwest.getLongitude() <= lng || lng <= this.northeast.getLongitude();
+ }
+
+ /**
+ * Builder class for {@link LatLngBounds}.
+ */
+ public static class Builder {
+
+ private double northeastLat = 0;
+ private double northeastLng = 0;
+ private double southwestLat = 0;
+ private double southwestLng = 0;
+
+ /**
+ * Includes this point for building of the bounds. The bounds will be extended in a minimum way
+ * to include this point.
+ * @param point
+ * @return builder object
+ */
+ public Builder include(LatLng point) {
+ if (northeastLat == 0) {
+ northeastLat = point.getLatitude();
+ northeastLng = point.getLongitude();
+ southwestLat = point.getLatitude();
+ southwestLng = point.getLongitude();
+ }
+ if (point.getLatitude() > northeastLat) {
+ northeastLat = point.getLatitude();
+ } else if (point.getLatitude() < southwestLat) {
+ southwestLat = point.getLatitude();
+ }
+ if (point.getLongitude() > northeastLng) {
+ northeastLng = point.getLongitude();
+ } else if (point.getLongitude() < southwestLng) {
+ southwestLng = point.getLongitude();
+ }
+ return this;
+ }
+
+ /**
+ * Constructs a new {@link LatLngBounds} from current boundaries.
+ * @return
+ */
+ public LatLngBounds build() {
+ LatLng sw = new LatLng(southwestLat, southwestLng);
+ LatLng ne = new LatLng(northeastLat, northeastLng);
+ return new LatLngBounds(sw, ne);
+ }
+ }
+}
diff --git a/mapzen-places-api/src/main/java/com/mapzen/places/api/Places.java b/mapzen-places-api/src/main/java/com/mapzen/places/api/Places.java
new file mode 100644
index 00000000..41cb2fed
--- /dev/null
+++ b/mapzen-places-api/src/main/java/com/mapzen/places/api/Places.java
@@ -0,0 +1,10 @@
+package com.mapzen.places.api;
+
+import com.mapzen.places.api.internal.GeoDataApiImpl;
+
+/**
+ * Main entry point for Mapzen Places API.
+ */
+public class Places {
+ public static final GeoDataApi GeoDataApi = new GeoDataApiImpl();
+}
diff --git a/mapzen-places-api/src/main/java/com/mapzen/places/api/internal/AutocompletePendingResult.java b/mapzen-places-api/src/main/java/com/mapzen/places/api/internal/AutocompletePendingResult.java
new file mode 100644
index 00000000..b7c1b66d
--- /dev/null
+++ b/mapzen-places-api/src/main/java/com/mapzen/places/api/internal/AutocompletePendingResult.java
@@ -0,0 +1,101 @@
+package com.mapzen.places.api.internal;
+
+import com.mapzen.android.lost.api.PendingResult;
+import com.mapzen.android.lost.api.ResultCallback;
+import com.mapzen.android.lost.api.Status;
+import com.mapzen.pelias.Pelias;
+import com.mapzen.pelias.gson.Feature;
+import com.mapzen.pelias.gson.Result;
+import com.mapzen.places.api.AutocompleteFilter;
+import com.mapzen.places.api.AutocompletePrediction;
+import com.mapzen.places.api.AutocompletePredictionBuffer;
+import com.mapzen.places.api.LatLng;
+import com.mapzen.places.api.LatLngBounds;
+
+import android.support.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import retrofit.Callback;
+import retrofit.RetrofitError;
+import retrofit.client.Response;
+
+/**
+ * Object returned by {@link GeoDataApiImpl#getAutocompletePredictions(
+ * com.mapzen.android.lost.api.LostApiClient, String, LatLngBounds, AutocompleteFilter)}.
+ */
+public class AutocompletePendingResult extends PendingResult {
+
+ private final Pelias pelias;
+ private final String query;
+ private final LatLngBounds bounds;
+ private final AutocompleteFilter filter;
+
+ /**
+ * Constructs a new object given a pelias instance, a query, a lat/lng bounds, and an autocomplete
+ * filter.
+ * @param pelias
+ * @param query
+ * @param bounds
+ * @param filter
+ */
+ public AutocompletePendingResult(Pelias pelias, String query, LatLngBounds bounds,
+ AutocompleteFilter filter) {
+ this.pelias = pelias;
+ this.query = query;
+ this.bounds = bounds;
+ this.filter = filter;
+ }
+
+ @NonNull @Override public AutocompletePredictionBuffer await() {
+ throw new RuntimeException("Not implemented yet");
+ }
+
+ @NonNull @Override
+ public AutocompletePredictionBuffer await(long time, @NonNull TimeUnit timeUnit) {
+ throw new RuntimeException("Not implemented yet");
+ }
+
+ @Override public void cancel() {
+ throw new RuntimeException("Not implemented yet");
+ }
+
+ @Override public boolean isCanceled() {
+ throw new RuntimeException("Not implemented yet");
+ }
+
+ @Override public void setResultCallback(
+ @NonNull final ResultCallback super AutocompletePredictionBuffer> callback) {
+ LatLng center = bounds.getCenter();
+ pelias.suggest(query, center.getLatitude(), center.getLongitude(), new Callback() {
+ @Override public void success(Result result, Response response) {
+ Status status = new Status(Status.SUCCESS);
+
+ final ArrayList predictions = new ArrayList<>();
+ final List features = result.getFeatures();
+ for (Feature feature : features) {
+ AutocompletePrediction prediction = new AutocompletePrediction(feature.properties.gid,
+ feature.properties.name);
+ predictions.add(prediction);
+ }
+
+ AutocompletePredictionBuffer buffer = new AutocompletePredictionBuffer(status, predictions);
+ callback.onResult(buffer);
+ }
+
+ @Override public void failure(RetrofitError error) {
+ Status status = new Status(Status.INTERNAL_ERROR);
+ AutocompletePredictionBuffer buffer = new AutocompletePredictionBuffer(status, null);
+ callback.onResult(buffer);
+ }
+ });
+ }
+
+ @Override public void setResultCallback(
+ @NonNull ResultCallback super AutocompletePredictionBuffer> callback, long time,
+ @NonNull TimeUnit timeUnit) {
+ throw new RuntimeException("Not implemented yet");
+ }
+}
diff --git a/mapzen-places-api/src/main/java/com/mapzen/places/api/internal/GeoDataApiImpl.java b/mapzen-places-api/src/main/java/com/mapzen/places/api/internal/GeoDataApiImpl.java
new file mode 100644
index 00000000..52ea24ba
--- /dev/null
+++ b/mapzen-places-api/src/main/java/com/mapzen/places/api/internal/GeoDataApiImpl.java
@@ -0,0 +1,22 @@
+package com.mapzen.places.api.internal;
+
+import com.mapzen.android.lost.api.LostApiClient;
+import com.mapzen.android.lost.api.PendingResult;
+import com.mapzen.pelias.Pelias;
+import com.mapzen.places.api.AutocompleteFilter;
+import com.mapzen.places.api.AutocompletePredictionBuffer;
+import com.mapzen.places.api.GeoDataApi;
+import com.mapzen.places.api.LatLngBounds;
+
+/**
+ * {@link GeoDataApi} implementation for {@link com.mapzen.places.api.Places}.
+ */
+public class GeoDataApiImpl implements GeoDataApi {
+
+ private Pelias pelias = new Pelias();
+
+ @Override public PendingResult getAutocompletePredictions(
+ LostApiClient client, String query, LatLngBounds bounds, AutocompleteFilter filter) {
+ return new AutocompletePendingResult(pelias, query, bounds, filter);
+ }
+}
diff --git a/mapzen-places-api/src/test/java/com/mapzen/places/api/AutocompletePredictionBufferTest.java b/mapzen-places-api/src/test/java/com/mapzen/places/api/AutocompletePredictionBufferTest.java
new file mode 100644
index 00000000..1f41694f
--- /dev/null
+++ b/mapzen-places-api/src/test/java/com/mapzen/places/api/AutocompletePredictionBufferTest.java
@@ -0,0 +1,59 @@
+package com.mapzen.places.api;
+
+import com.mapzen.android.lost.api.Status;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AutocompletePredictionBufferTest {
+
+ AutocompletePredictionBuffer buffer;
+ Status status;
+ List predictions;
+
+ @Before public void setup() {
+ status = new Status(Status.SUCCESS);
+ predictions = new ArrayList<>();
+ predictions.add(new AutocompletePrediction("test", "test"));
+ buffer = new AutocompletePredictionBuffer(status, predictions);
+ }
+
+ @Test public void getStatus_shouldReturnStatus() {
+ Status s = buffer.getStatus();
+ assertThat(s).isEqualTo(status);
+ }
+
+ @Test public void getCount_shouldReturnCorrectCount() {
+ int count = buffer.getCount();
+ assertThat(count).isEqualTo(1);
+ }
+
+ @Test public void getCount_shouldReturnCorrectCount_nullPredictions() {
+ buffer = new AutocompletePredictionBuffer(status, null);
+ int count = buffer.getCount();
+ assertThat(count).isEqualTo(0);
+ }
+
+ @Test public void get_shouldReturnCorrectPrediction() {
+ AutocompletePrediction prediction = buffer.get(0);
+ assertThat(prediction).isEqualTo(predictions.get(0));
+ }
+
+ @Test public void get_shouldReturnCorrectPrediction_nullPredictions() {
+ buffer = new AutocompletePredictionBuffer(status, null);
+ AutocompletePrediction prediction = buffer.get(0);
+ assertThat(prediction).isNull();
+ }
+
+ @Test public void get_shouldReturnCorrectPrediction_badIndex() {
+ AutocompletePrediction prediction = buffer.get(-1);
+ assertThat(prediction).isNull();
+ prediction = buffer.get(100);
+ assertThat(prediction).isNull();
+ }
+}
diff --git a/mapzen-places-api/src/test/java/com/mapzen/places/api/AutocompletePredictionTest.java b/mapzen-places-api/src/test/java/com/mapzen/places/api/AutocompletePredictionTest.java
new file mode 100644
index 00000000..dd2f2eb8
--- /dev/null
+++ b/mapzen-places-api/src/test/java/com/mapzen/places/api/AutocompletePredictionTest.java
@@ -0,0 +1,42 @@
+package com.mapzen.places.api;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AutocompletePredictionTest {
+
+ AutocompletePrediction prediction;
+ String id = "abc123";
+ String primaryText = "Roberta's Pizza";
+
+ @Before public void setup() {
+ prediction = new AutocompletePrediction(id, primaryText);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void getFullText_shouldThrowException() {
+ prediction.getFullText(null);
+ }
+
+ @Test public void getPrimaryText_shouldReturnCorrectText() {
+ CharSequence text = prediction.getPrimaryText(null);
+ assertThat(text.toString()).isEqualTo(primaryText);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void getSecondaryText_shouldThrowException() {
+ prediction.getSecondaryText(null);
+ }
+
+ @Test public void getPlaceId_shouldReturnCorrectId() {
+ String placeId = prediction.getPlaceId();
+ assertThat(placeId).isEqualTo(id);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void getPlaceTypes_shouldThrowException() {
+ prediction.getPlaceTypes();
+ }
+}
diff --git a/mapzen-places-api/src/test/java/com/mapzen/places/api/LatLngBoundsTest.java b/mapzen-places-api/src/test/java/com/mapzen/places/api/LatLngBoundsTest.java
new file mode 100644
index 00000000..a6f2416a
--- /dev/null
+++ b/mapzen-places-api/src/test/java/com/mapzen/places/api/LatLngBoundsTest.java
@@ -0,0 +1,91 @@
+package com.mapzen.places.api;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class LatLngBoundsTest {
+
+ LatLng southWestPoint = new LatLng(-50, -90);
+ LatLng northEastPoint = new LatLng(40, 80);
+ LatLngBounds bounds = new LatLngBounds(southWestPoint, northEastPoint);
+
+ @Test public void shouldSetNorthEastPoint() {
+ assertThat(bounds.getNortheast()).isEqualTo(northEastPoint);
+ }
+
+ @Test public void shouldSetSouthWestPoint() {
+ assertThat(bounds.getSouthwest()).isEqualTo(southWestPoint);
+ }
+
+ @Test public void include_shouldSetNorthEastPoint() {
+ LatLng pointA = new LatLng(40, 50);
+ LatLng pointB = new LatLng(-40, -50);
+ LatLngBounds bounds = new LatLngBounds.Builder()
+ .include(pointA)
+ .include(pointB)
+ .build();
+ assertThat(bounds.getNortheast().getLatitude()).isEqualTo(40);
+ assertThat(bounds.getNortheast().getLongitude()).isEqualTo(50);
+ }
+
+ @Test public void include_shouldSetSouthWestPoint() {
+ LatLng pointA = new LatLng(40, 50);
+ LatLng pointB = new LatLng(-40, -50);
+ LatLngBounds bounds = new LatLngBounds.Builder()
+ .include(pointA)
+ .include(pointB)
+ .build();
+ assertThat(bounds.getSouthwest().getLatitude()).isEqualTo(-40);
+ assertThat(bounds.getSouthwest().getLongitude()).isEqualTo(-50);
+ }
+
+ @Test public void contains_shouldReturnTrue() {
+ LatLng pointA = new LatLng(40, 50);
+ LatLng pointB = new LatLng(-40, -50);
+ LatLngBounds bounds = new LatLngBounds.Builder()
+ .include(pointA)
+ .include(pointB)
+ .build();
+ LatLng point = new LatLng(20, 20);
+ assertThat(bounds.contains(point)).isTrue();
+ }
+
+ @Test public void contains_shouldReturnFalse() {
+ LatLng pointA = new LatLng(40, 50);
+ LatLng pointB = new LatLng(-40, -50);
+ LatLngBounds bounds = new LatLngBounds.Builder()
+ .include(pointA)
+ .include(pointB)
+ .build();
+ LatLng point = new LatLng(20, 120);
+ assertThat(bounds.contains(point)).isFalse();
+ }
+
+ @Test public void including_shouldReturnCorrectBounds() {
+ LatLng pointA = new LatLng(40, 50);
+ LatLng pointB = new LatLng(-40, -50);
+ LatLngBounds bounds = new LatLngBounds.Builder()
+ .include(pointA)
+ .include(pointB)
+ .build();
+ LatLng point = new LatLng(30, 100);
+ LatLngBounds containing = bounds.including(point);
+ assertThat(containing.getNortheast().getLatitude()).isEqualTo(40);
+ assertThat(containing.getNortheast().getLongitude()).isEqualTo(100);
+ assertThat(containing.getSouthwest().getLatitude()).isEqualTo(-40);
+ assertThat(containing.getSouthwest().getLongitude()).isEqualTo(-50);
+ }
+
+ @Test public void getCenter_shouldReturnCorrectCenter() {
+ LatLng pointA = new LatLng(40, 50);
+ LatLng pointB = new LatLng(-40, -50);
+ LatLngBounds bounds = new LatLngBounds.Builder()
+ .include(pointA)
+ .include(pointB)
+ .build();
+ LatLng center = bounds.getCenter();
+ assertThat(center.getLatitude()).isEqualTo(0);
+ assertThat(center.getLongitude()).isEqualTo(0);
+ }
+}
diff --git a/mapzen-places-api/src/test/java/com/mapzen/places/api/LatLngTest.java b/mapzen-places-api/src/test/java/com/mapzen/places/api/LatLngTest.java
new file mode 100644
index 00000000..40400033
--- /dev/null
+++ b/mapzen-places-api/src/test/java/com/mapzen/places/api/LatLngTest.java
@@ -0,0 +1,38 @@
+package com.mapzen.places.api;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class LatLngTest {
+
+ LatLng latLng = new LatLng(40, 70);
+
+ @Test public void shouldSetLat() {
+ assertThat(latLng.getLatitude()).isEqualTo(40);
+ }
+
+ @Test public void shouldSetLng() {
+ assertThat(latLng.getLongitude()).isEqualTo(70);
+ }
+
+ @Test public void shouldSetMinLat() {
+ LatLng minLat = new LatLng(-100, 70);
+ assertThat(minLat.getLatitude()).isEqualTo(-90);
+ }
+
+ @Test public void shouldSetMaxLat() {
+ LatLng maxLat = new LatLng(100, 70);
+ assertThat(maxLat.getLatitude()).isEqualTo(90);
+ }
+
+ @Test public void shouldSetLng_160() {
+ LatLng minLng = new LatLng(40, -200);
+ assertThat(minLng.getLongitude()).isEqualTo(160);
+ }
+
+ @Test public void shouldSetLng_neg160() {
+ LatLng maxLng = new LatLng(40, 200);
+ assertThat(maxLng.getLongitude()).isEqualTo(-160);
+ }
+}
diff --git a/mapzen-places-api/src/test/java/com/mapzen/places/api/internal/AutocompletePendingResultTest.java b/mapzen-places-api/src/test/java/com/mapzen/places/api/internal/AutocompletePendingResultTest.java
new file mode 100644
index 00000000..93ab31f0
--- /dev/null
+++ b/mapzen-places-api/src/test/java/com/mapzen/places/api/internal/AutocompletePendingResultTest.java
@@ -0,0 +1,120 @@
+package com.mapzen.places.api.internal;
+
+import com.mapzen.android.lost.api.ResultCallback;
+import com.mapzen.android.lost.api.Status;
+import com.mapzen.pelias.Pelias;
+import com.mapzen.pelias.gson.Result;
+import com.mapzen.places.api.AutocompletePredictionBuffer;
+import com.mapzen.places.api.LatLng;
+import com.mapzen.places.api.LatLngBounds;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import android.support.annotation.NonNull;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyDouble;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import retrofit.Callback;
+
+public class AutocompletePendingResultTest {
+
+ AutocompletePendingResult result;
+ Pelias pelias;
+ String query;
+ LatLngBounds bounds;
+
+ @Before public void setup() {
+ pelias = spy(new Pelias());
+ query = "test";
+ bounds = new LatLngBounds(new LatLng(-40, -40), new LatLng(40, 40));
+ result = new AutocompletePendingResult(pelias, query, bounds, null);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void await_shouldThrowException() {
+ result.await();
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void awaitTimeout_shouldThrowException() {
+ result.await(1, TimeUnit.SECONDS);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void cancel_shouldThrowException() {
+ result.cancel();
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void isCanceled_shouldThrowException() {
+ result.isCanceled();
+ }
+
+ @Test public void setResultCallback_shouldInvokePeliasSuggest() {
+ TestResultCallback callback = new TestResultCallback();
+ result.setResultCallback(callback);
+ LatLng center = bounds.getCenter();
+ verify(pelias).suggest(eq(query), eq(center.getLatitude()), eq(center.getLongitude()),
+ any(Callback.class));
+ }
+
+ @Test public void setResultCallback_shouldReturnSuccessStatus() {
+ doAnswer(new Answer() {
+ public Object answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ Callback callback = (Callback) args[3];
+ final Result result = new Result();
+ callback.success(result, null);
+ return null;
+ }
+ }).when(pelias).suggest(anyString(), anyDouble(), anyDouble(), any(Callback.class));
+
+ TestResultCallback callback = new TestResultCallback();
+ result.setResultCallback(callback);
+ assertThat(callback.getBuffer().getStatus().getStatusCode()).isEqualTo(Status.SUCCESS);
+ }
+
+ @Test public void setResultCallback_shouldReturnFailureStatus() {
+ doAnswer(new Answer() {
+ public Object answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ Callback callback = (Callback) args[3];
+ callback.failure(null);
+ return null;
+ }
+ }).when(pelias).suggest(anyString(), anyDouble(), anyDouble(), any(Callback.class));
+
+ TestResultCallback callback = new TestResultCallback();
+ result.setResultCallback(callback);
+ assertThat(callback.getBuffer().getStatus().getStatusCode()).isEqualTo(Status.INTERNAL_ERROR);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void setResultCallbackTimeout_shouldThrowException() {
+ result.setResultCallback(null, 1, TimeUnit.SECONDS);
+ }
+
+ class TestResultCallback implements ResultCallback {
+
+ private AutocompletePredictionBuffer buffer;
+
+ @Override public void onResult(@NonNull AutocompletePredictionBuffer result) {
+ buffer = result;
+ }
+
+ public AutocompletePredictionBuffer getBuffer() {
+ return buffer;
+ }
+ }
+}
diff --git a/mapzen-places-api/src/test/java/com/mapzen/places/api/internal/GeoDataApiImplTest.java b/mapzen-places-api/src/test/java/com/mapzen/places/api/internal/GeoDataApiImplTest.java
new file mode 100644
index 00000000..6760c5a7
--- /dev/null
+++ b/mapzen-places-api/src/test/java/com/mapzen/places/api/internal/GeoDataApiImplTest.java
@@ -0,0 +1,19 @@
+package com.mapzen.places.api.internal;
+
+import com.mapzen.android.lost.api.PendingResult;
+import com.mapzen.places.api.AutocompletePredictionBuffer;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class GeoDataApiImplTest {
+
+ GeoDataApiImpl geoDataApi = new GeoDataApiImpl();
+
+ @Test public void getAutocompletePredictions_shouldReturnAutocompletePendingResult() {
+ PendingResult result =
+ geoDataApi.getAutocompletePredictions(null, null, null, null);
+ assertThat(result).isInstanceOf(AutocompletePendingResult.class);
+ }
+}
diff --git a/mapzen-places-api/src/test/java/com/mapzen/places/api/internal/PlacesTest.java b/mapzen-places-api/src/test/java/com/mapzen/places/api/internal/PlacesTest.java
new file mode 100644
index 00000000..9b15f353
--- /dev/null
+++ b/mapzen-places-api/src/test/java/com/mapzen/places/api/internal/PlacesTest.java
@@ -0,0 +1,18 @@
+package com.mapzen.places.api.internal;
+
+import com.mapzen.places.api.Places;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PlacesTest {
+
+ @Test public void geoDataApiShouldNotBeNull() {
+ assertThat(Places.GeoDataApi).isNotNull();
+ }
+
+ @Test public void geoDataApiShouldBeOfCorrectClass() {
+ assertThat(Places.GeoDataApi).isInstanceOf(GeoDataApiImpl.class);
+ }
+}
diff --git a/samples/mapzen-places-api-sample/src/main/AndroidManifest.xml b/samples/mapzen-places-api-sample/src/main/AndroidManifest.xml
index c435371b..b41dd713 100644
--- a/samples/mapzen-places-api-sample/src/main/AndroidManifest.xml
+++ b/samples/mapzen-places-api-sample/src/main/AndroidManifest.xml
@@ -2,13 +2,15 @@
+
+
-
+
diff --git a/samples/mapzen-places-api-sample/src/main/java/com/mapzen/places/api/sample/GeoDataApiActivity.java b/samples/mapzen-places-api-sample/src/main/java/com/mapzen/places/api/sample/GeoDataApiActivity.java
new file mode 100644
index 00000000..e13817b3
--- /dev/null
+++ b/samples/mapzen-places-api-sample/src/main/java/com/mapzen/places/api/sample/GeoDataApiActivity.java
@@ -0,0 +1,112 @@
+package com.mapzen.places.api.sample;
+
+import com.mapzen.android.lost.api.LostApiClient;
+import com.mapzen.android.lost.api.PendingResult;
+import com.mapzen.android.lost.api.ResultCallback;
+import com.mapzen.places.api.AutocompletePrediction;
+import com.mapzen.places.api.AutocompletePredictionBuffer;
+import com.mapzen.places.api.LatLng;
+import com.mapzen.places.api.LatLngBounds;
+import com.mapzen.places.api.Places;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * Demonstrates use of the {@link com.mapzen.places.GeoDataApi}
+ */
+public class GeoDataApiActivity extends AppCompatActivity {
+
+ LostApiClient client;
+
+ ListView listView;
+
+ @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_geodata);
+ listView = (ListView) findViewById(R.id.list_view);
+
+ connectClient();
+ }
+
+ private void connectClient() {
+ client = new LostApiClient.Builder(this).addConnectionCallbacks(
+ new LostApiClient.ConnectionCallbacks() {
+
+ @Override public void onConnected() {
+ queryAutocompleteApi();
+ }
+
+ @Override public void onConnectionSuspended() {
+
+ }
+ }).build();
+ client.connect();
+ }
+
+ private void queryAutocompleteApi() {
+ String query = "pizza";
+ LatLng pointA = new LatLng(40.020451, -105.274679);
+ LatLng pointB = new LatLng(40.012004, -105.289957);
+ LatLngBounds bounds = new LatLngBounds.Builder().include(pointA).include(pointB).build();
+ PendingResult result =
+ Places.GeoDataApi.getAutocompletePredictions(client, query, bounds, null);
+ result.setResultCallback(new ResultCallback() {
+ @Override public void onResult(@NonNull AutocompletePredictionBuffer result) {
+ displayAutocompleteItems(result);
+ }
+ });
+ }
+
+ private void displayAutocompleteItems(final AutocompletePredictionBuffer buffer) {
+ listView.setAdapter(new BaseAdapter() {
+ @Override public int getCount() {
+ return buffer.getCount();
+ }
+
+ @Override public Object getItem(int i) {
+ return buffer.get(i);
+ }
+
+ @Override public long getItemId(int i) {
+ return i;
+ }
+
+ @Override public View getView(int position, View convertView, ViewGroup parent) {
+ View view;
+ ViewHolder holder;
+ if (convertView == null) {
+ view = LayoutInflater.from(GeoDataApiActivity.this)
+ .inflate(R.layout.list_item, parent, false);
+ holder = new ViewHolder(view);
+ view.setTag(holder);
+ } else {
+ view = convertView;
+ holder = (ViewHolder) view.getTag();
+ }
+ AutocompletePrediction prediction = buffer.get(position);
+ holder.title.setText(prediction.getPlaceId());
+ holder.description.setText(prediction.getPrimaryText(null));
+ return view;
+ }
+ });
+ }
+
+ private class ViewHolder {
+ TextView title;
+ TextView description;
+
+ ViewHolder(View view) {
+ title = (TextView) view.findViewById(R.id.title);
+ description = (TextView) view.findViewById(R.id.description);
+ }
+ }
+}
diff --git a/samples/mapzen-places-api-sample/src/main/java/com/mapzen/places/api/sample/MainActivity.java b/samples/mapzen-places-api-sample/src/main/java/com/mapzen/places/api/sample/MainActivity.java
deleted file mode 100644
index 15bef9a8..00000000
--- a/samples/mapzen-places-api-sample/src/main/java/com/mapzen/places/api/sample/MainActivity.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.mapzen.places.api.sample;
-
-import android.support.v7.app.AppCompatActivity;
-import android.os.Bundle;
-
-public class MainActivity extends AppCompatActivity {
-
- @Override protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- }
-}
diff --git a/samples/mapzen-places-api-sample/src/main/res/layout/activity_geodata.xml b/samples/mapzen-places-api-sample/src/main/res/layout/activity_geodata.xml
new file mode 100644
index 00000000..7bbecd7c
--- /dev/null
+++ b/samples/mapzen-places-api-sample/src/main/res/layout/activity_geodata.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/mapzen-places-api-sample/src/main/res/layout/activity_main.xml b/samples/mapzen-places-api-sample/src/main/res/layout/activity_main.xml
deleted file mode 100644
index 57bea024..00000000
--- a/samples/mapzen-places-api-sample/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
diff --git a/samples/mapzen-places-api-sample/src/main/res/layout/list_item.xml b/samples/mapzen-places-api-sample/src/main/res/layout/list_item.xml
new file mode 100644
index 00000000..b4e0639a
--- /dev/null
+++ b/samples/mapzen-places-api-sample/src/main/res/layout/list_item.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/mapzen-places-api-sample/src/main/res/values/strings.xml b/samples/mapzen-places-api-sample/src/main/res/values/strings.xml
index 274f15b6..46d224b4 100644
--- a/samples/mapzen-places-api-sample/src/main/res/values/strings.xml
+++ b/samples/mapzen-places-api-sample/src/main/res/values/strings.xml
@@ -1,3 +1,5 @@
- mapzen-places-api-sample
+ Mapzen Places API
+ GeoDataApi
+ Places.GeoDataApi example usage
diff --git a/settings.gradle b/settings.gradle
index 38d48d9b..43dec25e 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -7,4 +7,4 @@ include ':samples_mapzen-places-api-sample'
project(':samples_mapzen-android-sdk-sample').projectDir =
new File('samples/mapzen-android-sdk-sample')
project(':samples_mapzen-places-api-sample').projectDir =
- new File('samples/mapzen-places-api-sample')
\ No newline at end of file
+ new File('samples/mapzen-places-api-sample')