Last active
January 31, 2018 11:08
-
-
Save tamhuynhit/ecb322643455f721c06bca759c0c2ef2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.os.Parcel; | |
import android.os.Parcelable; | |
import com.google.android.gms.maps.model.Dash; | |
import com.google.android.gms.maps.model.Gap; | |
import com.google.android.gms.maps.model.LatLng; | |
import com.google.android.gms.maps.model.LatLngBounds; | |
import com.google.android.gms.maps.model.PatternItem; | |
import com.google.maps.android.PolyUtil; | |
import com.google.maps.android.SphericalUtil; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.List; | |
import java.util.Locale; | |
/** | |
* Created by tamhuynh on 9/5/17. | |
* | |
*/ | |
public class Route implements Parcelable { | |
public final static String LONG_ROUTE_DEFAULT_TEXT = "long_route"; | |
public final static int LONG_ROUTE_DEFAULT_VALUE = -1; | |
private final static int LONG_ROUTING_DISTANCE_LIMIT = 1000000; | |
private final static double DEFAULT_CURVE_ROUTE_CURVATURE = 0.3f; | |
private final static int DEFAULT_CURVE_POINTS = 60; | |
private final static List<PatternItem> CURVE_ROUTE_PATTERNS = Arrays.asList(new Dash(30), new Gap(20)); | |
private LatLngBounds mLatLngBounds; | |
private String mDurationText; | |
private int mDurationValue; | |
private String mDistanceText; | |
private int mDistanceValue; | |
private List<LatLng> mPoints; | |
private List<PatternItem> mPatterns; | |
private Route(Builder builder) { | |
mLatLngBounds = builder.mLatLngBounds; | |
mDurationText = builder.mDurationText; | |
mDurationValue = builder.mDurationValue; | |
mDistanceText = builder.mDistanceText; | |
mDistanceValue = builder.mDistanceValue; | |
mPoints = builder.mPoints; | |
mPatterns = builder.mPatterns; | |
} | |
private Route(Parcel in) { | |
mLatLngBounds = in.readParcelable(LatLngBounds.class.getClassLoader()); | |
mDurationText = in.readString(); | |
mDurationValue = in.readInt(); | |
mDistanceText = in.readString(); | |
mDistanceValue = in.readInt(); | |
mPoints = in.createTypedArrayList(LatLng.CREATOR); | |
mPatterns = in.createTypedArrayList(PatternItem.CREATOR); | |
} | |
public static Route createFromDirectionResponse(DirectionApiResponse response) { | |
DirectionApiResponse.RouteResponse route = response.getFirstRoute(); | |
if (route == null) { | |
return null; | |
} | |
DirectionApiResponse.Leg leg = route.getFirstLeg(); | |
if (leg == null) { | |
return null; | |
} | |
return new Route.Builder() | |
.setDistanceText(leg.mDistance.getText()) | |
.setDistanceValue(leg.mDistance.getValue()) | |
.setDurationText(leg.mDuration.getText()) | |
.setDurationValue(leg.mDuration.getValue()) | |
.setLatLngBounds(route.getBounds()) | |
.setPoints(route.getPolyline()) | |
.build(); | |
} | |
public static Route createLongDistanceRoute(LatLng origin, LatLng dest) { | |
double distance = SphericalUtil.computeDistanceBetween(origin, dest); | |
if (distance <= LONG_ROUTING_DISTANCE_LIMIT) | |
return createCurveRoute(origin, dest, distance); | |
return createCrowFlightRoute(origin, dest, distance); | |
} | |
/** | |
* Create a curve route between origin and dest | |
*/ | |
private static Route createCurveRoute(LatLng origin, LatLng dest, double distance) { | |
if (distance == 0) | |
distance = SphericalUtil.computeDistanceBetween(origin, dest); | |
double heading = SphericalUtil.computeHeading(origin, dest); | |
double halfDistance = distance / 2; | |
// Calculate midpoint position | |
LatLng midPoint = SphericalUtil.computeOffset(origin, halfDistance, heading); | |
// Calculate position of the curve center point | |
double sqrCurvature = DEFAULT_CURVE_ROUTE_CURVATURE * DEFAULT_CURVE_ROUTE_CURVATURE; | |
double extraParam = distance / (4 * DEFAULT_CURVE_ROUTE_CURVATURE); | |
double midPerpendicularLength = (1 - sqrCurvature) * extraParam; | |
double r = (1 + sqrCurvature) * extraParam; | |
LatLng circleCenterPoint = SphericalUtil.computeOffset(midPoint, midPerpendicularLength, heading + 90.0); | |
// Calculate heading between circle center and two points | |
double headingToOrigin = SphericalUtil.computeHeading(circleCenterPoint, origin); | |
// Calculate positions of points on the curve | |
double step = Math.toDegrees(Math.atan(halfDistance / midPerpendicularLength)) * 2 / DEFAULT_CURVE_POINTS; | |
List<LatLng> points = new ArrayList<>(); | |
for (int i = 0; i < DEFAULT_CURVE_POINTS; ++i) { | |
points.add(SphericalUtil.computeOffset(circleCenterPoint, r, headingToOrigin + i * step)); | |
} | |
return new Route.Builder() | |
.setLatLngBounds(origin, dest) | |
.setPoints(points) | |
.setDistanceText(String.format(Locale.getDefault(), "> %.2f km", distance / 1000)) | |
.setDistanceValue((int) distance) | |
.setPatterns(CURVE_ROUTE_PATTERNS) | |
.build(); | |
} | |
/** | |
* Create crowflight route | |
*/ | |
private static Route createCrowFlightRoute(LatLng origin, LatLng dest, double distance) { | |
if (distance == 0) | |
distance = SphericalUtil.computeDistanceBetween(origin, dest); | |
return new Route.Builder() | |
.setLatLngBounds(origin, dest) | |
.setPoints(origin, dest) | |
.setDistanceText(String.format(Locale.getDefault(), "> %.2f km", distance / 1000)) | |
.setDistanceValue((int) distance) | |
.setPatterns(CURVE_ROUTE_PATTERNS) | |
.build(); | |
} | |
public String getDurationText() { | |
return mDurationText; | |
} | |
public String getDistanceText() { | |
return mDistanceText; | |
} | |
public int getDurationValue() { | |
return mDurationValue; | |
} | |
public List<LatLng> getPoints() { | |
return mPoints; | |
} | |
public int getDistanceValue() { | |
return mDistanceValue; | |
} | |
public LatLngBounds getLatLngBounds() { | |
return mLatLngBounds; | |
} | |
public List<PatternItem> getPatterns() { | |
return mPatterns; | |
} | |
@Override | |
public int describeContents() { | |
return 0; | |
} | |
@Override | |
public void writeToParcel(Parcel dest, int flags) { | |
dest.writeParcelable(mLatLngBounds, flags); | |
dest.writeString(mDurationText); | |
dest.writeInt(mDurationValue); | |
dest.writeString(mDistanceText); | |
dest.writeInt(mDistanceValue); | |
dest.writeTypedList(mPoints); | |
dest.writeTypedList(mPatterns); | |
} | |
public static final Creator<Route> CREATOR = new Creator<Route>() { | |
@Override | |
public Route createFromParcel(Parcel source) { | |
return new Route(source); | |
} | |
@Override | |
public Route[] newArray(int size) { | |
return new Route[size]; | |
} | |
}; | |
public static class Builder { | |
private LatLngBounds mLatLngBounds; | |
private String mDurationText = LONG_ROUTE_DEFAULT_TEXT; | |
private int mDurationValue = LONG_ROUTE_DEFAULT_VALUE; | |
private String mDistanceText = LONG_ROUTE_DEFAULT_TEXT; | |
private int mDistanceValue = LONG_ROUTE_DEFAULT_VALUE; | |
private List<LatLng> mPoints; | |
private List<PatternItem> mPatterns; | |
Builder setLatLngBounds(LatLng... points) { | |
if (points == null) | |
return this; | |
LatLngBounds.Builder builder = new LatLngBounds.Builder(); | |
for (LatLng point : points) | |
builder.include(point); | |
mLatLngBounds = builder.build(); | |
return this; | |
} | |
Builder setDurationText(String durationText) { | |
mDurationText = durationText; | |
return this; | |
} | |
Builder setDurationValue(int durationValue) { | |
mDurationValue = durationValue; | |
return this; | |
} | |
Builder setDistanceText(String distanceText) { | |
mDistanceText = distanceText; | |
return this; | |
} | |
Builder setDistanceValue(int distanceValue) { | |
mDistanceValue = distanceValue; | |
return this; | |
} | |
Builder setPoints(String encodedPolyline) { | |
mPoints = PolyUtil.decode(encodedPolyline); | |
return this; | |
} | |
Builder setPoints(List<LatLng> points) { | |
mPoints = points; | |
return this; | |
} | |
Builder setPoints(LatLng... points) { | |
mPoints = Arrays.asList(points); | |
return this; | |
} | |
Builder setPatterns(List<PatternItem> patterns) { | |
mPatterns = patterns; | |
return this; | |
} | |
public Route build() { | |
return new Route(this); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment