Skip to content

Instantly share code, notes, and snippets.

@tamhuynhit
Last active January 31, 2018 11:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tamhuynhit/ecb322643455f721c06bca759c0c2ef2 to your computer and use it in GitHub Desktop.
Save tamhuynhit/ecb322643455f721c06bca759c0c2ef2 to your computer and use it in GitHub Desktop.
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