diff --git a/src/main/java/org/springframework/data/geo/Box.java b/src/main/java/org/springframework/data/geo/Box.java new file mode 100644 index 000000000..f3b67d34d --- /dev/null +++ b/src/main/java/org/springframework/data/geo/Box.java @@ -0,0 +1,99 @@ +/* + * Copyright 2010-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + +import org.springframework.util.Assert; + +/** + * Represents a geospatial box value + * + * @author Mark Pollack + * @author Oliver Gierke + * @author Thomas Darimont + * @since 1.8 + */ +public class Box implements Shape { + + private final Point first; + private final Point second; + + /** + * Creates a new Box spanning from the given first to the second {@link Point}. + * + * @param first must not be {@literal null}. + * @param second must not be {@literal null}. + */ + public Box(Point first, Point second) { + + Assert.notNull(first); + Assert.notNull(second); + + this.first = first; + this.second = second; + } + + /** + * Creates a new Box from the given {@code first} to the {@code second} point represented as the {@literal double[]}. + * + * @param first must not be {@literal null} and contain exactly 2 doubles. + * @param second must not be {@literal null} and contain exactly 2 doubles. + */ + public Box(double[] first, double[] second) { + + Assert.isTrue(first.length == 2, "Point array has to have 2 elements!"); + Assert.isTrue(second.length == 2, "Point array has to have 2 elements!"); + + this.first = new Point(first[0], first[1]); + this.second = new Point(second[0], second[1]); + } + + public Point getFirst() { + return first; + } + + public Point getSecond() { + return second; + } + + @Override + public String toString() { + return String.format("Box [%s, %s]", first, second); + } + + @Override + public int hashCode() { + + int result = 31; + result += 17 * first.hashCode(); + result += 17 * second.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + + if (this == obj) { + return true; + } + + if (obj == null || !(obj instanceof Box)) { + return false; + } + + Box that = (Box) obj; + return this.first.equals(that.first) && this.second.equals(that.second); + } +} diff --git a/src/main/java/org/springframework/data/geo/Circle.java b/src/main/java/org/springframework/data/geo/Circle.java new file mode 100644 index 000000000..d4051528b --- /dev/null +++ b/src/main/java/org/springframework/data/geo/Circle.java @@ -0,0 +1,131 @@ +/* + * Copyright 2010-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + +import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.util.Assert; + +/** + * Represents a geospatial circle value + * + * @author Mark Pollack + * @author Oliver Gierke + * @author Thomas Darimont + * @since 1.8 + */ +public class Circle implements Shape { + + private final Point center; + private final Distance radius; + + /** + * Creates a new {@link Circle} from the given {@link Point} and radius. + * + * @param center must not be {@literal null}. + * @param radius must not be {@literal null} and it's value greater or equal to zero. + */ + @PersistenceConstructor + public Circle(Point center, Distance radius) { + + Assert.notNull(center); + Assert.notNull(radius); + Assert.isTrue(radius.getValue() >= 0, "Radius must not be negative!"); + + this.center = center; + this.radius = radius; + } + + /** + * Creates a new {@link Circle} from the given {@link Point} and radius. + * + * @param center must not be {@literal null}. + * @param radius's value must be greater or equal to zero. + */ + @PersistenceConstructor + public Circle(Point center, double radius) { + this(center, new Distance(radius)); + } + + /** + * Creates a new {@link Circle} from the given coordinates and radius as {@link Distance} with a + * {@link Metrics#NEUTRAL}. + * + * @param centerX + * @param centerY + * @param radius must be greater or equal to zero. + */ + public Circle(double centerX, double centerY, double radius) { + this(new Point(centerX, centerY), new Distance(radius)); + } + + /** + * Returns the center of the {@link Circle}. + * + * @return will never be {@literal null}. + */ + public Point getCenter() { + return center; + } + + /** + * Returns the radius of the {@link Circle}. + * + * @return + */ + public Distance getRadius() { + return radius; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return String.format("Circle [center=%s, radius=%f]", center, radius); + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + + if (this == obj) { + return true; + } + + if (obj == null || !(obj instanceof Circle)) { + return false; + } + + Circle that = (Circle) obj; + + return this.center.equals(that.center) && this.radius.equals(that.radius); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + int result = 17; + result += 31 * center.hashCode(); + result += 31 * radius.hashCode(); + return result; + } +} diff --git a/src/main/java/org/springframework/data/geo/CustomMetric.java b/src/main/java/org/springframework/data/geo/CustomMetric.java new file mode 100644 index 000000000..c77c66ca8 --- /dev/null +++ b/src/main/java/org/springframework/data/geo/CustomMetric.java @@ -0,0 +1,45 @@ +/* + * Copyright 2013-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + +/** + * Value object to create custom {@link Metric}s on the fly. + * + * @author Oliver Gierke + * @author Thomas Darimont + * @since 1.8 + */ +public class CustomMetric implements Metric { + + private final double multiplier; + + /** + * Creates a custom {@link Metric} using the given multiplier. + * + * @param multiplier + */ + public CustomMetric(double multiplier) { + this.multiplier = multiplier; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.geo.Metric#getMultiplier() + */ + public double getMultiplier() { + return multiplier; + } +} diff --git a/src/main/java/org/springframework/data/geo/Distance.java b/src/main/java/org/springframework/data/geo/Distance.java new file mode 100644 index 000000000..e671c3b03 --- /dev/null +++ b/src/main/java/org/springframework/data/geo/Distance.java @@ -0,0 +1,147 @@ +/* + * Copyright 2010-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + +import org.springframework.util.ObjectUtils; + +/** + * Value object to represent distances in a given metric. + * + * @author Oliver Gierke + * @author Thomas Darimont + * @since 1.8 + */ +public class Distance { + + private final double value; + private final Metric metric; + + /** + * Creates a new {@link Distance}. + * + * @param value + */ + public Distance(double value) { + this(value, Metrics.NEUTRAL); + } + + /** + * Creates a new {@link Distance} with the given {@link Metric}. + * + * @param value + * @param metric + */ + public Distance(double value, Metric metric) { + this.value = value; + this.metric = metric == null ? Metrics.NEUTRAL : metric; + } + + /** + * @return the value + */ + public double getValue() { + return value; + } + + /** + * Returns the normalized value regarding the underlying {@link Metric}. + * + * @return + */ + public double getNormalizedValue() { + return value / metric.getMultiplier(); + } + + /** + * @return the metric + */ + public Metric getMetric() { + return metric; + } + + /** + * Adds the given distance to the current one. The resulting {@link Distance} will be in the same metric as the + * current one. + * + * @param other + * @return + */ + public Distance add(Distance other) { + double newNormalizedValue = getNormalizedValue() + other.getNormalizedValue(); + return new Distance(newNormalizedValue * metric.getMultiplier(), metric); + } + + /** + * Adds the given {@link Distance} to the current one and forces the result to be in a given {@link Metric}. + * + * @param other + * @param metric + * @return + */ + public Distance add(Distance other, Metric metric) { + double newLeft = getNormalizedValue() * metric.getMultiplier(); + double newRight = other.getNormalizedValue() * metric.getMultiplier(); + return new Distance(newLeft + newRight, metric); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + + if (this == obj) { + return true; + } + + if (obj == null || !getClass().isInstance(obj)) { + return false; + } + + Distance that = (Distance) obj; + + return this.value == that.value && ObjectUtils.nullSafeEquals(this.metric, that.metric); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + int result = 17; + result += 31 * Double.doubleToLongBits(value); + result += 31 * ObjectUtils.nullSafeHashCode(metric); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + + StringBuilder builder = new StringBuilder(); + builder.append(value); + + if (metric != Metrics.NEUTRAL) { + builder.append(" ").append(metric.toString()); + } + + return builder.toString(); + } +} diff --git a/src/main/java/org/springframework/data/geo/GeoPage.java b/src/main/java/org/springframework/data/geo/GeoPage.java new file mode 100644 index 000000000..f716307ee --- /dev/null +++ b/src/main/java/org/springframework/data/geo/GeoPage.java @@ -0,0 +1,65 @@ +/* + * Copyright 2011-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; + +/** + * Custom {@link Page} to carry the average distance retrieved from the {@link GeoResults} the {@link GeoPage} is set up + * from. + * + * @author Oliver Gierke + * @author Thomas Darimont + * @since 1.8 + */ +public class GeoPage extends PageImpl> { + + private static final long serialVersionUID = -5655267379242128600L; + private final Distance averageDistance; + + /** + * Creates a new {@link GeoPage} from the given {@link GeoResults}. + * + * @param content must not be {@literal null}. + */ + public GeoPage(GeoResults results) { + super(results.getContent()); + this.averageDistance = results.getAverageDistance(); + } + + /** + * Creates a new {@link GeoPage} from the given {@link GeoResults}, {@link Pageable} and total. + * + * @param results must not be {@literal null}. + * @param pageable must not be {@literal null}. + * @param total + */ + public GeoPage(GeoResults results, Pageable pageable, long total) { + super(results.getContent(), pageable, total); + this.averageDistance = results.getAverageDistance(); + } + + /** + * Returns the average distance of the underlying results. + * + * @return the averageDistance + */ + public Distance getAverageDistance() { + return averageDistance; + } +} diff --git a/src/main/java/org/springframework/data/geo/GeoResult.java b/src/main/java/org/springframework/data/geo/GeoResult.java new file mode 100644 index 000000000..e269a8695 --- /dev/null +++ b/src/main/java/org/springframework/data/geo/GeoResult.java @@ -0,0 +1,106 @@ +/* + * Copyright 2011-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + +import org.springframework.util.Assert; + +/** + * Calue object capturing some arbitrary object plus a distance. + * + * @author Oliver Gierke + * @author Thomas Darimont + * @since 1.8 + */ +public class GeoResult { + + private final T content; + private final Distance distance; + + /** + * Creates a new {@link GeoResult} for the given content and distance. + * + * @param content must not be {@literal null}. + * @param distance must not be {@literal null}. + */ + public GeoResult(T content, Distance distance) { + + Assert.notNull(content); + Assert.notNull(distance); + + this.content = content; + this.distance = distance; + } + + /** + * Returns the actual content object. + * + * @return the content + */ + public T getContent() { + return content; + } + + /** + * Returns the distance the actual content object has from the origin. + * + * @return the distance + */ + public Distance getDistance() { + return distance; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + + if (this == obj) { + return true; + } + + if (obj == null || !getClass().isInstance(obj)) { + return false; + } + + GeoResult that = (GeoResult) obj; + + return this.content.equals(that.content) && this.distance.equals(that.distance); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + + int result = 17; + result += 31 * distance.hashCode(); + result += 31 * content.hashCode(); + return result; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return String.format("GeoResult [content: %s, distance: %s, ]", content.toString(), distance.toString()); + } +} diff --git a/src/main/java/org/springframework/data/geo/GeoResults.java b/src/main/java/org/springframework/data/geo/GeoResults.java new file mode 100644 index 000000000..4c9d8d7a1 --- /dev/null +++ b/src/main/java/org/springframework/data/geo/GeoResults.java @@ -0,0 +1,148 @@ +/* + * Copyright 2011-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Value object to capture {@link GeoResult}s as well as the average distance they have. + * + * @author Oliver Gierke + * @author Thomas Darimont + * @since 1.8 + */ +public class GeoResults implements Iterable> { + + private final List> results; + private final Distance averageDistance; + + /** + * Creates a new {@link GeoResults} instance manually calculating the average distance from the distance values of the + * given {@link GeoResult}s. + * + * @param results must not be {@literal null}. + */ + public GeoResults(List> results) { + this(results, (Metric) null); + } + + public GeoResults(List> results, Metric metric) { + this(results, calculateAverageDistance(results, metric)); + } + + /** + * Creates a new {@link GeoResults} instance from the given {@link GeoResult}s and average distance. + * + * @param results must not be {@literal null}. + * @param averageDistance + */ + @PersistenceConstructor + public GeoResults(List> results, Distance averageDistance) { + Assert.notNull(results); + this.results = results; + this.averageDistance = averageDistance; + } + + /** + * Returns the average distance of all {@link GeoResult}s in this list. + * + * @return the averageDistance + */ + public Distance getAverageDistance() { + return averageDistance; + } + + /* + * (non-Javadoc) + * @see java.lang.Iterable#iterator() + */ + @SuppressWarnings("unchecked") + public Iterator> iterator() { + return (Iterator>) results.iterator(); + } + + /** + * Returns the actual + * + * @return + */ + public List> getContent() { + return Collections.unmodifiableList(results); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + + if (this == obj) { + return true; + } + + if (obj == null || !getClass().isInstance(obj)) { + return false; + } + + GeoResults that = (GeoResults) obj; + + return this.results.equals(that.results) && this.averageDistance == that.averageDistance; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + int result = 17; + result += 31 * results.hashCode(); + result += 31 * averageDistance.hashCode(); + return result; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return String.format("GeoResults: [averageDistance: %s, results: %s]", averageDistance.toString(), + StringUtils.collectionToCommaDelimitedString(results)); + } + + private static Distance calculateAverageDistance(List> results, Metric metric) { + + if (results.isEmpty()) { + return new Distance(0, metric); + } + + double averageDistance = 0; + + for (GeoResult result : results) { + averageDistance += result.getDistance().getValue(); + } + + return new Distance(averageDistance / results.size(), metric); + } +} diff --git a/src/main/java/org/springframework/data/geo/Metric.java b/src/main/java/org/springframework/data/geo/Metric.java new file mode 100644 index 000000000..e4f457e74 --- /dev/null +++ b/src/main/java/org/springframework/data/geo/Metric.java @@ -0,0 +1,33 @@ +/* + * Copyright 2011-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + +/** + * Interface for {@link Metric}s that can be applied to a base scale. + * + * @author Oliver Gierke + * @author Thomas Darimont + * @since 1.8 + */ +public interface Metric { + + /** + * Returns the multiplier to calculate metrics values from a base scale. + * + * @return + */ + double getMultiplier(); +} diff --git a/src/main/java/org/springframework/data/geo/Metrics.java b/src/main/java/org/springframework/data/geo/Metrics.java new file mode 100644 index 000000000..0fb4345d0 --- /dev/null +++ b/src/main/java/org/springframework/data/geo/Metrics.java @@ -0,0 +1,45 @@ +/* + * Copyright 2011-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + +/** + * Commonly used {@link Metric}s. + * + * @author Oliver Gierke + * @author Thomas Darimont + * @since 1.8 + */ +public enum Metrics implements Metric { + + KILOMETERS(6378.137), MILES(3963.191), NEUTRAL(1); + + private final double multiplier; + + /** + * @param multiplier the earth radius at equator. + */ + private Metrics(double multiplier) { + this.multiplier = multiplier; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.geo.Metric#getMultiplier() + */ + public double getMultiplier() { + return multiplier; + } +} diff --git a/src/main/java/org/springframework/data/geo/Point.java b/src/main/java/org/springframework/data/geo/Point.java new file mode 100644 index 000000000..e5f21fb93 --- /dev/null +++ b/src/main/java/org/springframework/data/geo/Point.java @@ -0,0 +1,104 @@ +/* + * Copyright 2010-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + +import org.springframework.data.annotation.PersistenceConstructor; +import org.springframework.util.Assert; + +/** + * Represents a geospatial point value. + * + * @author Mark Pollack + * @author Oliver Gierke + * @author Thomas Darimont + * @since 1.8 + */ +public class Point { + + private final double x; + private final double y; + + /** + * Creates a {@link Point} from the given {@code x}, {@code y} coordinate. + * + * @param x + * @param y + */ + @PersistenceConstructor + public Point(double x, double y) { + this.x = x; + this.y = y; + } + + /** + * Creates a {@link Point} from the given {@link Point} coordinate. + * + * @param point must not be {@literal null}. + */ + public Point(Point point) { + + Assert.notNull(point); + + this.x = point.x; + this.y = point.y; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(x); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(y); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Point)) { + return false; + } + Point other = (Point) obj; + if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x)) { + return false; + } + if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y)) { + return false; + } + return true; + } + + @Override + public String toString() { + return String.format("Point [latitude=%f, longitude=%f]", x, y); + } +} diff --git a/src/main/java/org/springframework/data/geo/Polygon.java b/src/main/java/org/springframework/data/geo/Polygon.java new file mode 100644 index 000000000..7cc3b0094 --- /dev/null +++ b/src/main/java/org/springframework/data/geo/Polygon.java @@ -0,0 +1,120 @@ +/* + * Copyright 2011-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import org.springframework.util.Assert; + +/** + * Simple value object to represent a {@link Polygon}. + * + * @author Oliver Gierke + * @author Thomas Darimont + * @since 1.8 + */ +public class Polygon implements Iterable, Shape { + + private final List points; + + /** + * Creates a new {@link Polygon} for the given Points. + * + * @param x + * @param y + * @param z + * @param others + */ + public

Polygon(P x, P y, P z, P... others) { + + Assert.notNull(x); + Assert.notNull(y); + Assert.notNull(z); + Assert.notNull(others); + + this.points = new ArrayList(3 + others.length); + this.points.addAll(Arrays.asList(x, y, z)); + this.points.addAll(Arrays.asList(others)); + } + + /** + * Creates a new {@link Polygon} for the given Points. + * + * @param points + */ + public

Polygon(List

points) { + + Assert.notNull(points); + + this.points = new ArrayList(points.size()); + + for (Point point : points) { + Assert.notNull(point); + this.points.add(point); + } + } + + public List getPoints() { + + List result = new ArrayList(); + + for (Point point : points) { + result.add(point); + } + + return result; + } + + /* + * (non-Javadoc) + * @see java.lang.Iterable#iterator() + */ + public Iterator iterator() { + return this.points.iterator(); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + + if (this == obj) { + return true; + } + + if (obj == null || !(obj instanceof Polygon)) { + return false; + } + + Polygon that = (Polygon) obj; + + return this.points.equals(that.points); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return points.hashCode(); + } +} diff --git a/src/main/java/org/springframework/data/geo/Shape.java b/src/main/java/org/springframework/data/geo/Shape.java new file mode 100644 index 000000000..560afdc96 --- /dev/null +++ b/src/main/java/org/springframework/data/geo/Shape.java @@ -0,0 +1,25 @@ +/* + * Copyright 2011-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + + +/** + * Common interface for all shapes. Allows building external representations of them. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +public interface Shape {} diff --git a/src/main/java/org/springframework/data/geo/package-info.java b/src/main/java/org/springframework/data/geo/package-info.java new file mode 100644 index 000000000..d9ce1acdb --- /dev/null +++ b/src/main/java/org/springframework/data/geo/package-info.java @@ -0,0 +1,7 @@ +/** + * Support for reusable geospatial structures. + * + * @author Thomas Darimont + * @since 1.8 + */ +package org.springframework.data.geo; \ No newline at end of file diff --git a/src/test/java/org/springframework/data/geo/BoxUnitTests.java b/src/test/java/org/springframework/data/geo/BoxUnitTests.java new file mode 100644 index 000000000..5343aefea --- /dev/null +++ b/src/test/java/org/springframework/data/geo/BoxUnitTests.java @@ -0,0 +1,55 @@ +/* + * Copyright 2011-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; + +/** + * Unit tests for {@link Box}. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +public class BoxUnitTests { + + Box first = new Box(new Point(1d, 1d), new Point(2d, 2d)); + Box second = new Box(new Point(1d, 1d), new Point(2d, 2d)); + Box third = new Box(new Point(3d, 3d), new Point(1d, 1d)); + + /** + * @see DATACMNS-437 + */ + @Test + public void equalsWorksCorrectly() { + + assertThat(first.equals(second), is(true)); + assertThat(second.equals(first), is(true)); + assertThat(first.equals(third), is(false)); + } + + /** + * @see DATACMNS-437 + */ + @Test + public void hashCodeWorksCorrectly() { + + assertThat(first.hashCode(), is(second.hashCode())); + assertThat(first.hashCode(), is(not(third.hashCode()))); + } +} diff --git a/src/test/java/org/springframework/data/geo/CircleUnitTests.java b/src/test/java/org/springframework/data/geo/CircleUnitTests.java new file mode 100644 index 000000000..c9e42ba98 --- /dev/null +++ b/src/test/java/org/springframework/data/geo/CircleUnitTests.java @@ -0,0 +1,64 @@ +/* + * Copyright 2011-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; + +/** + * Unit tests for {@link Circle}. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +public class CircleUnitTests { + + /** + * @see DATACMNS-437 + */ + @Test(expected = IllegalArgumentException.class) + public void rejectsNullOrigin() { + new Circle(null, new Distance(0)); + } + + /** + * @see DATACMNS-437 + */ + @Test(expected = IllegalArgumentException.class) + public void rejectsNegativeRadius() { + new Circle(1, 1, -1); + } + + /** + * @see DATACMNS-437 + */ + @Test + public void considersTwoCirclesEqualCorrectly() { + + Circle left = new Circle(1, 1, 1); + Circle right = new Circle(1, 1, 1); + + assertThat(left, is(right)); + assertThat(right, is(left)); + + right = new Circle(new Point(1, 1), new Distance(1)); + + assertThat(left, is(right)); + assertThat(right, is(left)); + } +} diff --git a/src/test/java/org/springframework/data/geo/DistanceUnitTests.java b/src/test/java/org/springframework/data/geo/DistanceUnitTests.java new file mode 100644 index 000000000..684e1da29 --- /dev/null +++ b/src/test/java/org/springframework/data/geo/DistanceUnitTests.java @@ -0,0 +1,65 @@ +/* + * Copyright 2011-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.springframework.data.geo.Metrics.*; + +import org.junit.Test; + +/** + * Unit tests for {@link Distance}. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +public class DistanceUnitTests { + + /** + * @see DATACMNS-437 + */ + @Test + public void defaultsMetricToNeutralOne() { + + assertThat(new Distance(2.5).getMetric(), is((Metric) Metrics.NEUTRAL)); + assertThat(new Distance(2.5, null).getMetric(), is((Metric) Metrics.NEUTRAL)); + } + + /** + * @see DATACMNS-437 + */ + @Test + public void addsDistancesWithoutExplicitMetric() { + + Distance left = new Distance(2.5, KILOMETERS); + Distance right = new Distance(2.5, KILOMETERS); + + assertThat(left.add(right), is(new Distance(5.0, KILOMETERS))); + } + + /** + * @see DATACMNS-437 + */ + @Test + public void addsDistancesWithExplicitMetric() { + + Distance left = new Distance(2.5, KILOMETERS); + Distance right = new Distance(2.5, KILOMETERS); + + assertThat(left.add(right, MILES), is(new Distance(3.106856281073925, MILES))); + } +} diff --git a/src/test/java/org/springframework/data/geo/GeoResultUnitTests.java b/src/test/java/org/springframework/data/geo/GeoResultUnitTests.java new file mode 100644 index 000000000..e16843615 --- /dev/null +++ b/src/test/java/org/springframework/data/geo/GeoResultUnitTests.java @@ -0,0 +1,67 @@ +/* + * Copyright 2011-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; + +/** + * Unit tests for {@link GeoResult}. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +public class GeoResultUnitTests { + + GeoResult first = new GeoResult("Foo", new Distance(2.5)); + GeoResult second = new GeoResult("Foo", new Distance(2.5)); + GeoResult third = new GeoResult("Bar", new Distance(2.5)); + GeoResult fourth = new GeoResult("Foo", new Distance(5.2)); + + /** + * @see DATACMNS-437 + */ + @Test + public void considersSameInstanceEqual() { + + assertThat(first.equals(first), is(true)); + } + + /** + * @see DATACMNS-437 + */ + @Test + public void considersSameValuesAsEqual() { + + assertThat(first.equals(second), is(true)); + assertThat(second.equals(first), is(true)); + assertThat(first.equals(third), is(false)); + assertThat(third.equals(first), is(false)); + assertThat(first.equals(fourth), is(false)); + assertThat(fourth.equals(first), is(false)); + } + + /** + * @see DATACMNS-437 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Test(expected = IllegalArgumentException.class) + public void rejectsNullContent() { + new GeoResult(null, new Distance(2.5)); + } +} diff --git a/src/test/java/org/springframework/data/geo/GeoResultsUnitTests.java b/src/test/java/org/springframework/data/geo/GeoResultsUnitTests.java new file mode 100644 index 000000000..0f97c8b8e --- /dev/null +++ b/src/test/java/org/springframework/data/geo/GeoResultsUnitTests.java @@ -0,0 +1,46 @@ +/* + * Copyright 2011-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.util.Arrays; + +import org.junit.Test; + +/** + * Unit tests for {@link GeoResults}. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +public class GeoResultsUnitTests { + + /** + * @see DATACMNS-437 + */ + @Test + @SuppressWarnings("unchecked") + public void calculatesAverageForGivenGeoResults() { + + GeoResult first = new GeoResult(new Object(), new Distance(2)); + GeoResult second = new GeoResult(new Object(), new Distance(5)); + GeoResults geoResults = new GeoResults(Arrays.asList(first, second)); + + assertThat(geoResults.getAverageDistance(), is(new Distance(3.5))); + } +} diff --git a/src/test/java/org/springframework/data/geo/PointUnitTests.java b/src/test/java/org/springframework/data/geo/PointUnitTests.java new file mode 100644 index 000000000..daad11596 --- /dev/null +++ b/src/test/java/org/springframework/data/geo/PointUnitTests.java @@ -0,0 +1,57 @@ +/* + * Copyright 2011-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; + +/** + * Unit tests for {@link Point}. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +public class PointUnitTests { + + /** + * @see DATACMNS-437 + */ + @Test(expected = IllegalArgumentException.class) + public void rejectsNullforCopyConstructor() { + new Point(null); + } + + /** + * @see DATACMNS-437 + */ + @Test + public void equalsIsImplementedCorrectly() { + + assertThat(new Point(1.5, 1.5), is(equalTo(new Point(1.5, 1.5)))); + assertThat(new Point(1.5, 1.5), is(not(equalTo(new Point(2.0, 2.0))))); + assertThat(new Point(2.0, 2.0), is(not(equalTo(new Point(1.5, 1.5))))); + } + + /** + * @see DATACMNS-437 + */ + @Test + public void invokingToStringWorksCorrectly() { + new Point(1.5, 1.5).toString(); + } +} diff --git a/src/test/java/org/springframework/data/geo/PolygonUnitTests.java b/src/test/java/org/springframework/data/geo/PolygonUnitTests.java new file mode 100644 index 000000000..68d2677ba --- /dev/null +++ b/src/test/java/org/springframework/data/geo/PolygonUnitTests.java @@ -0,0 +1,66 @@ +/* + * Copyright 2011-2014 the original author or authors. + * + * 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 org.springframework.data.geo; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; + +/** + * Unit tests for {@link Polygon}. + * + * @author Oliver Gierke + * @author Thomas Darimont + */ +public class PolygonUnitTests { + + Point first = new Point(1, 1); + Point second = new Point(2, 2); + Point third = new Point(3, 3); + + /** + * @see DATACMNS-437 + */ + @Test(expected = IllegalArgumentException.class) + public void rejectsNullPoints() { + new Polygon(null, null, null); + } + + /** + * @see DATACMNS-437 + */ + @Test + public void createsSimplePolygon() { + + Polygon polygon = new Polygon(third, second, first); + + assertThat(polygon, is(notNullValue())); + } + + /** + * @see DATACMNS-437 + */ + @Test + public void isEqualForSamePoints() { + + Polygon left = new Polygon(third, second, first); + Polygon right = new Polygon(third, second, first); + + assertThat(left, is(right)); + assertThat(right, is(left)); + } +}