Skip to content

Commit

Permalink
Merge pull request #270 from ajkannan/geopoint-value
Browse files Browse the repository at this point in the history
Add GeoPoint value
  • Loading branch information
aozarov committed Oct 20, 2015
2 parents 8245607 + 241960f commit 9e4692d
Show file tree
Hide file tree
Showing 10 changed files with 393 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static com.google.gcloud.datastore.DateTimeValue.of;
import static com.google.gcloud.datastore.DoubleValue.of;
import static com.google.gcloud.datastore.EntityValue.of;
import static com.google.gcloud.datastore.LatLngValue.of;
import static com.google.gcloud.datastore.KeyValue.of;
import static com.google.gcloud.datastore.ListValue.of;
import static com.google.gcloud.datastore.LongValue.of;
Expand Down Expand Up @@ -159,6 +160,11 @@ public B set(String name, DateTime value) {
return self();
}

public B set(String name, LatLng value) {
properties.put(name, of(value));
return self();
}

public B set(String name, Key value) {
properties.put(name, of(value));
return self();
Expand Down Expand Up @@ -320,6 +326,17 @@ public DateTime getDateTime(String name) {
return ((Value<DateTime>) getValue(name)).get();
}

/**
* Returns the property value as a LatLng.
*
* @throws DatastoreException if not such property.
* @throws ClassCastException if value is not a LatLng.
*/
@SuppressWarnings("unchecked")
public LatLng getLatLng(String name) {
return ((Value<LatLng>) getValue(name)).get();
}

/**
* Returns the property value as a Key.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright 2015 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.gcloud.datastore;

import static com.google.common.base.Preconditions.checkArgument;

import com.google.protobuf.InvalidProtocolBufferException;

import java.util.Objects;

/**
* A Google Cloud Datastore LatLng (represented by latitude and longitude in degrees).
* This class is immutable.
*
* @see <a href="https://cloud.google.com/datastore/docs/concepts/entities">Google Cloud Datastore
* Entities, Properties, and Keys</a>
*/
public final class LatLng extends Serializable<com.google.type.LatLng> {

private static final long serialVersionUID = 9077060962655752073L;

private final transient double latitude;
private final transient double longitude;

LatLng(double latitude, double longitude) {
checkArgument(
latitude >= -90.0 && latitude <= 90.0, "latitude must be in the range [-90, 90] degrees");
checkArgument(
longitude >= -180.0 && longitude <= 180.0,
"latitude must be in the range [-180, 180] degrees");
this.latitude = latitude;
this.longitude = longitude;
}

public double latitude() {
return latitude;
}

public double longitude() {
return longitude;
}

@Override
public String toString() {
return Double.toString(latitude) + ", " + Double.toString(longitude);
}

@Override
public int hashCode() {
return Objects.hash(latitude, longitude);
}

@Override
public boolean equals(Object obj) {
return obj == this || (obj instanceof LatLng && this.latitude == ((LatLng) obj).latitude
&& this.longitude == ((LatLng) obj).longitude);
}

public static LatLng of(double latitude, double longitude) {
return new LatLng(latitude, longitude);
}

@Override
protected com.google.type.LatLng toPb() {
return com.google.type.LatLng.newBuilder()
.setLatitude(latitude)
.setLongitude(longitude)
.build();
}

@Override
protected Object fromPb(byte[] bytesPb) throws InvalidProtocolBufferException {
com.google.type.LatLng parsedLatLng = com.google.type.LatLng.parseFrom(bytesPb);
return new LatLng(parsedLatLng.getLatitude(), parsedLatLng.getLongitude());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright 2015 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.gcloud.datastore;

import static com.google.datastore.v1beta3.Value.GEO_POINT_VALUE_FIELD_NUMBER;

public final class LatLngValue extends Value<LatLng> {

private static final long serialVersionUID = -5810614280642405898L;

static final BaseMarshaller<LatLng, LatLngValue, Builder> MARSHALLER =
new BaseMarshaller<LatLng, LatLngValue, Builder>() {

private static final long serialVersionUID = -3550567536035178649L;

@Override
public int getProtoFieldId() {
return GEO_POINT_VALUE_FIELD_NUMBER;
}

@Override
public Builder newBuilder(LatLng value) {
return builder(value);
}

@Override
protected LatLng getValue(com.google.datastore.v1beta3.Value from) {
return new LatLng(
from.getGeoPointValue().getLatitude(), from.getGeoPointValue().getLongitude());
}

@Override
protected void setValue(LatLngValue from, com.google.datastore.v1beta3.Value.Builder to) {
to.setGeoPointValue(from.get().toPb());
}
};

public static final class Builder extends Value.BaseBuilder<LatLng, LatLngValue, Builder> {

private Builder() {
super(ValueType.LAT_LNG);
}

@Override
public LatLngValue build() {
return new LatLngValue(this);
}
}

public LatLngValue(LatLng value) {
this(builder(value));
}

private LatLngValue(Builder builder) {
super(builder);
}

@Override
public Builder toBuilder() {
return new Builder().mergeFrom(this);
}

public static LatLngValue of(LatLng value) {
return new LatLngValue(value);
}

public static Builder builder(LatLng value) {
return new Builder().set(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,12 @@ public enum ValueType {
/**
* Represents a raw/unparsed value.
*/
RAW_VALUE(RawValue.MARSHALLER);
RAW_VALUE(RawValue.MARSHALLER),

/**
* TODO(ajaykannan): add GEO_POINT_VALUE
* Will represent a geolocation value in latitude/longitude
* Represents a {@link LatLng} value
*/
LAT_LNG(LatLngValue.MARSHALLER);

private static final ImmutableMap<Integer, ValueType> DESCRIPTOR_TO_TYPE_MAP;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class BaseEntityTest {

private static final Blob BLOB = Blob.copyFrom(new byte[]{1, 2});
private static final DateTime DATE_TIME = DateTime.now();
private static final LatLng LAT_LNG = new LatLng(37.422035, -122.084124);
private static final Key KEY = Key.builder("ds1", "k1", "n1").build();
private static final Entity ENTITY = Entity.builder(KEY).set("name", "foo").build();
private static final IncompleteKey INCOMPLETE_KEY = IncompleteKey.builder("ds1", "k1").build();
Expand Down Expand Up @@ -62,9 +63,9 @@ public void setUp() {
builder = new Builder();
builder.set("blob", BLOB).set("boolean", true).set("dateTime", DATE_TIME);
builder.set("double", 1.25).set("key", KEY).set("string", "hello world");
builder.set("long", 125).setNull("null").set("entity", ENTITY);
builder.set("long", 125).setNull("null").set("entity", ENTITY).set("latLng", LAT_LNG);
builder.set("partialEntity", PARTIAL_ENTITY).set("stringValue", StringValue.of("bla"));
builder.set("list1", NullValue.of(), StringValue.of("foo"));
builder.set("list1", NullValue.of(), StringValue.of("foo"), LatLngValue.of(LAT_LNG));
builder.set("list2", ImmutableList.of(LongValue.of(10), DoubleValue.of(2)));
builder.set("list3", Collections.singletonList(BooleanValue.of(true)));
}
Expand Down Expand Up @@ -149,6 +150,12 @@ public void testGetDateTime() throws Exception {
assertEquals(dateTime, entity.getDateTime("dateTime"));
}

@Test
public void testGetLatLng() throws Exception {
BaseEntity<Key> entity = builder.build();
assertEquals(LAT_LNG, entity.getLatLng("latLng"));
}

@Test
public void testGetKey() throws Exception {
BaseEntity<Key> entity = builder.build();
Expand All @@ -171,9 +178,10 @@ public void testGetEntity() throws Exception {
public void testGetList() throws Exception {
BaseEntity<Key> entity = builder.build();
List<? extends Value<?>> list = entity.getList("list1");
assertEquals(2, list.size());
assertEquals(3, list.size());
assertEquals(NullValue.of(), list.get(0));
assertEquals("foo", list.get(1).get());
assertEquals(LAT_LNG, list.get(2).get());
list = entity.getList("list2");
assertEquals(2, list.size());
assertEquals(Long.valueOf(10), list.get(0).get());
Expand All @@ -196,9 +204,10 @@ public void testGetBlob() throws Exception {

@Test
public void testNames() throws Exception {
Set<String> names = ImmutableSet.<String>builder()
.add("string", "stringValue", "boolean", "double", "long", "list1", "list2", "list3")
.add("entity", "partialEntity", "null", "dateTime", "blob", "key")
Set<String> names =
ImmutableSet.<String>builder()
.add("string", "stringValue", "boolean", "double", "long", "list1", "list2", "list3")
.add("entity", "partialEntity", "null", "dateTime", "latLng", "blob", "key")
.build();
BaseEntity<Key> entity = builder.build();
assertEquals(names, entity.names());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ public class DatastoreTest {
.build();
private static final ListValue LIST_VALUE2 = ListValue.of(Collections.singletonList(KEY_VALUE));
private static final DateTimeValue DATE_TIME_VALUE = new DateTimeValue(DateTime.now());
private static final LatLngValue LAT_LNG_VALUE =
new LatLngValue(new LatLng(37.422035, -122.084124));
private static final FullEntity<IncompleteKey> PARTIAL_ENTITY1 =
FullEntity.builder(INCOMPLETE_KEY2).set("str", STR_VALUE).set("bool", BOOL_VALUE)
.set("list", LIST_VALUE1).build();
Expand All @@ -83,13 +85,15 @@ public class DatastoreTest {
private static final FullEntity<IncompleteKey> PARTIAL_ENTITY3 =
FullEntity.builder(PARTIAL_ENTITY1).key(IncompleteKey.builder(PROJECT_ID, KIND3).build())
.build();
private static final Entity ENTITY1 = Entity.builder(KEY1)
.set("str", STR_VALUE)
.set("date", DATE_TIME_VALUE)
.set("bool", BOOL_VALUE)
.set("partial1", EntityValue.of(PARTIAL_ENTITY1))
.set("list", LIST_VALUE2)
.build();
private static final Entity ENTITY1 =
Entity.builder(KEY1)
.set("str", STR_VALUE)
.set("date", DATE_TIME_VALUE)
.set("latLng", LAT_LNG_VALUE)
.set("bool", BOOL_VALUE)
.set("partial1", EntityValue.of(PARTIAL_ENTITY1))
.set("list", LIST_VALUE2)
.build();
private static final Entity ENTITY2 = Entity.builder(ENTITY1).key(KEY2).remove("str")
.set("name", "Dan").setNull("null").set("age", 20).build();
private static final Entity ENTITY3 = Entity.builder(ENTITY1).key(KEY3).remove("str")
Expand Down Expand Up @@ -504,9 +508,11 @@ public void testGet() {
assertEquals(LIST_VALUE2, value3);
DateTimeValue value4 = entity.getValue("date");
assertEquals(DATE_TIME_VALUE, value4);
FullEntity<IncompleteKey> value5 = entity.getEntity("partial1");
assertEquals(PARTIAL_ENTITY1, value5);
assertEquals(5, entity.names().size());
LatLngValue value5 = entity.getValue("latLng");
assertEquals(LAT_LNG_VALUE, value5);
FullEntity<IncompleteKey> value6 = entity.getEntity("partial1");
assertEquals(PARTIAL_ENTITY1, value6);
assertEquals(6, entity.names().size());
assertFalse(entity.contains("bla"));
}

Expand All @@ -528,7 +534,8 @@ public void testGetArray() {
assertEquals(PARTIAL_ENTITY2, partial1);
assertEquals(ENTITY2, partial2);
assertEquals(ValueType.BOOLEAN, entity3.getValue("bool").type());
assertEquals(6, entity3.names().size());
assertEquals(LAT_LNG_VALUE, entity3.getValue("latLng"));
assertEquals(7, entity3.names().size());
assertFalse(entity3.contains("bla"));
try {
entity3.getString("str");
Expand Down
Loading

0 comments on commit 9e4692d

Please sign in to comment.