ParseApplication/ExternalLibs/Parse-SDK-Android/Parse/src/main/java/com/parse/ParsePolygon.java

229 lines
6.5 KiB
Java

/*
* Copyright (c) 2015-present, Parse, LLC.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.parse;
import android.location.Criteria;
import android.location.Location;
import android.os.Parcel;
import android.os.Parcelable;
import org.json.JSONArray;
import org.json.JSONException;
import java.util.Locale;
import java.util.List;
import bolts.Continuation;
import bolts.Task;
/**
* {@code ParsePolygon} represents a set of coordinates that may be associated with a key
* in a {@link ParseObject} or used as a reference point for geo queries. This allows proximity
* based queries on the key.
*
* Example:
* <pre>
* List<ParseGeoPoint> points = new ArrayList<ParseGeoPoint>();
* points.add(new ParseGeoPoint(0,0));
* points.add(new ParseGeoPoint(0,1));
* points.add(new ParseGeoPoint(1,1));
* points.add(new ParseGeoPoint(1,0));
* ParsePolygon polygon = new ParsePolygon(points);
* ParseObject object = new ParseObject("PlaceObject");
* object.put("area", polygon);
* object.save();
* </pre>
*/
public class ParsePolygon implements Parcelable {
private List<ParseGeoPoint> coordinates;
/**
* Creates a new polygon with the specified {@link ParseGeoPoint}.
*
* @param coords
* The polygon's coordinates.
*/
public ParsePolygon(List<ParseGeoPoint> coords) {
setCoordinates(coords);
}
/**
* Creates a copy of {@code polygon};
*
* @param polygon
* The polygon to copy.
*/
public ParsePolygon(ParsePolygon polygon) {
this(polygon.getCoordinates());
}
/**
* Creates a new point instance from a {@link Parcel} source. This is used when unparceling a
* ParsePolygon. Subclasses that need Parcelable behavior should provide their own
* {@link android.os.Parcelable.Creator} and override this constructor.
*
* @param source The recovered parcel.
*/
protected ParsePolygon(Parcel source) {
this(source, ParseParcelDecoder.get());
}
/**
* Creates a new point instance from a {@link Parcel} using the given {@link ParseParcelDecoder}.
* The decoder is currently unused, but it might be in the future, plus this is the pattern we
* are using in parcelable classes.
*
* @param source the parcel
* @param decoder the decoder
*/
ParsePolygon(Parcel source, ParseParcelDecoder decoder) {
setCoordinates(source.readArrayList(null));
}
/**
* Set coordinates. Valid are Array of GeoPoint, ParseGeoPoint or Location
* at least 3 points
*
* @param coords
* The polygon's coordinates.
*/
public void setCoordinates(List<ParseGeoPoint> coords) {
this.coordinates = ParsePolygon.validate(coords);
}
/**
* Get coordinates.
*/
public List<ParseGeoPoint> getCoordinates() {
return coordinates;
}
/**
* Get converts coordinate to JSONArray.
*/
protected JSONArray coordinatesToJSONArray() throws JSONException{
JSONArray points = new JSONArray();
for (ParseGeoPoint coordinate : coordinates) {
JSONArray point = new JSONArray();
point.put(coordinate.getLatitude());
point.put(coordinate.getLongitude());
points.put(point);
}
return points;
}
/**
* Checks if this {@code ParsePolygon}; contains {@link ParseGeoPoint}.
*/
public boolean containsPoint(ParseGeoPoint point) {
double minX = coordinates.get(0).getLatitude();
double maxX = coordinates.get(0).getLatitude();
double minY = coordinates.get(0).getLongitude();
double maxY = coordinates.get(0).getLongitude();
for ( int i = 1; i < coordinates.size(); i += 1) {
ParseGeoPoint geoPoint = coordinates.get(i);
minX = Math.min(geoPoint.getLatitude(), minX);
maxX = Math.max(geoPoint.getLatitude(), maxX);
minY = Math.min(geoPoint.getLongitude(), minY);
maxY = Math.max(geoPoint.getLongitude(), maxY);
}
boolean outside = point.getLatitude() < minX || point.getLatitude() > maxX || point.getLongitude() < minY || point.getLongitude() > maxY;
if (outside) {
return false;
}
boolean inside = false;
for (int i = 0, j = coordinates.size() - 1 ; i < coordinates.size(); j = i++) {
double startX = coordinates.get(i).getLatitude();
double startY = coordinates.get(i).getLongitude();
double endX = coordinates.get(j).getLatitude();
double endY = coordinates.get(j).getLongitude();
boolean intersect = (( startY > point.getLongitude() ) != ( endY > point.getLongitude() ) &&
point.getLatitude() < ( endX - startX ) * ( point.getLongitude() - startY ) / ( endY - startY ) + startX);
if (intersect) {
inside = !inside;
}
}
return inside;
}
/**
* Throws exception for invalid coordinates.
*/
static List<ParseGeoPoint> validate(List<ParseGeoPoint> coords) {
if (coords.size() < 3) {
throw new IllegalArgumentException("Polygon must have at least 3 GeoPoints");
}
return coords;
}
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof ParsePolygon)) {
return false;
}
if (obj == this) {
return true;
}
ParsePolygon other = (ParsePolygon) obj;
if (coordinates.size() != other.getCoordinates().size()) {
return false;
}
boolean isEqual = true;
for (int i = 0; i < coordinates.size(); i += 1) {
if (coordinates.get(i).getLatitude() != other.getCoordinates().get(i).getLatitude() ||
coordinates.get(i).getLongitude() != other.getCoordinates().get(i).getLongitude()) {
isEqual = false;
break;
}
}
return isEqual;
}
@Override
public String toString() {
return String.format(Locale.US, "ParsePolygon: %s", coordinates);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
writeToParcel(dest, ParseParcelEncoder.get());
}
void writeToParcel(Parcel dest, ParseParcelEncoder encoder) {
dest.writeList(coordinates);
}
public final static Creator<ParsePolygon> CREATOR = new Creator<ParsePolygon>() {
@Override
public ParsePolygon createFromParcel(Parcel source) {
return new ParsePolygon(source, ParseParcelDecoder.get());
}
@Override
public ParsePolygon[] newArray(int size) {
return new ParsePolygon[size];
}
};
}