001/*
002 * Licensed under the Apache License, Version 2.0 (the "License");
003 * you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at
005 *
006 *     http://www.apache.org/licenses/LICENSE-2.0
007 *
008 * Unless required by applicable law or agreed to in writing, software
009 * distributed under the License is distributed on an "AS IS" BASIS,
010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011 * See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014package org.gbif.api.model.predicate;
015
016import com.fasterxml.jackson.annotation.JsonIgnore;
017
018import org.gbif.api.model.occurrence.geo.DistanceUnit;
019import org.gbif.api.util.SearchTypeValidator;
020
021import java.util.Objects;
022import java.util.StringJoiner;
023
024import javax.validation.constraints.NotNull;
025
026import com.fasterxml.jackson.annotation.JsonCreator;
027import com.fasterxml.jackson.annotation.JsonIgnore;
028import com.fasterxml.jackson.annotation.JsonProperty;
029
030import io.swagger.v3.oas.annotations.Hidden;
031import io.swagger.v3.oas.annotations.media.Schema;
032
033/**
034 * This predicate checks if an occurrence location falls within a distance of a location.
035 */
036@Schema(
037  description = "This predicate checks if an occurrence falls within a distance of a location."
038)
039public class GeoDistancePredicate implements Predicate {
040
041  @Hidden
042  @NotNull
043  private final DistanceUnit.GeoDistance geoDistance;
044
045  @Schema(
046    description = "The latitude of the location."
047  )
048  @NotNull
049  private final String latitude;
050
051  @Schema(
052    description = "The longitude of the location."
053  )
054  @NotNull
055  private final String longitude;
056
057  @Schema(
058    description = "The distance as a number and unit, e.g. `5km` or `5000m`."
059  )
060  @NotNull
061  private final String distance;
062
063  /**
064   * Builds a new geodistance predicate that matches records within a given distance of a geopoint..
065   *
066   * @param latitude
067   * @param longitude
068   * @param distance
069   */
070  @JsonCreator
071  public GeoDistancePredicate(@JsonProperty("latitude") String latitude,
072                              @JsonProperty("longitude") String longitude,
073                              @JsonProperty("distance") String distance) {
074    Objects.requireNonNull(latitude, "<latitude> may not be null");
075    Objects.requireNonNull(longitude, "<longitude> may not be null");
076    Objects.requireNonNull(distance, "<distance> may not be null");
077    this.latitude = latitude;
078    this.longitude = longitude;
079    this.distance = distance;
080    // test if it is a valid GeoDistance
081    SearchTypeValidator.validateGeoDistance(latitude, longitude, distance);
082
083    this.geoDistance = DistanceUnit.GeoDistance.parseGeoDistance(latitude, longitude, distance);
084  }
085
086  public GeoDistancePredicate(@NotNull DistanceUnit.GeoDistance geoDistance) {
087    this.geoDistance = geoDistance;
088
089    this.latitude = Double.toString(geoDistance.getLatitude());
090    this.longitude = Double.toString(geoDistance.getLongitude());
091    this.distance = geoDistance.getDistance().toString();
092  }
093
094  public String getLatitude() {
095    return latitude;
096  }
097
098  public String getLongitude() {
099    return longitude;
100  }
101
102  public String getDistance() {
103    return distance;
104  }
105
106  @JsonIgnore
107  public DistanceUnit.GeoDistance getGeoDistance() {
108    return geoDistance;
109  }
110
111  @Override
112  public boolean equals(Object o) {
113    if (this == o) return true;
114    if (o == null || getClass() != o.getClass()) return false;
115    GeoDistancePredicate that = (GeoDistancePredicate) o;
116    return Objects.equals(geoDistance, that.geoDistance);
117  }
118
119  @Override
120  public int hashCode() {
121    return Objects.hash(geoDistance);
122  }
123
124  @Override
125  public String toString() {
126    return new StringJoiner(", ", GeoDistancePredicate.class.getSimpleName() + "[", "]")
127            .add("geoDistance=" + geoDistance).toString();
128  }
129}