/*
* Licensed to GraphHopper GmbH under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*
* GraphHopper GmbH licenses this file to you 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 com.graphhopper.util;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class Instruction {
public static final int UNKNOWN = -99;
public static final int LEAVE_ROUNDABOUT = -6; // for future use
public static final int TURN_SHARP_LEFT = -3;
public static final int TURN_LEFT = -2;
public static final int TURN_SLIGHT_LEFT = -1;
public static final int CONTINUE_ON_STREET = 0;
public static final int TURN_SLIGHT_RIGHT = 1;
public static final int TURN_RIGHT = 2;
public static final int TURN_SHARP_RIGHT = 3;
public static final int FINISH = 4;
public static final int REACHED_VIA = 5;
public static final int USE_ROUNDABOUT = 6;
public static final int IGNORE = Integer.MIN_VALUE;
public static final int KEEP_LEFT = -7;
public static final int KEEP_RIGHT = 7;
public static final int PT_START_TRIP = 101;
public static final int PT_TRANSFER = 102;
public static final int PT_END_TRIP = 103;
private static final AngleCalc AC = Helper.ANGLE_CALC;
protected final PointList points;
protected final InstructionAnnotation annotation;
protected boolean rawName;
protected int sign;
protected String name;
protected double distance;
protected long time;
/**
* The points, distances and times have exactly the same count. The last point of this
* instruction is not duplicated here and should be in the next one.
*/
public Instruction(int sign, String name, InstructionAnnotation ia, PointList pl) {
this.sign = sign;
this.name = name;
this.points = pl;
this.annotation = ia;
}
/**
* This method does not perform translation or combination with the sign - it just uses the
* provided name as instruction.
*/
public void setUseRawName() {
rawName = true;
}
public InstructionAnnotation getAnnotation() {
return annotation;
}
/**
* The instruction for the person/driver to execute.
*/
public int getSign() {
return sign;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, Object> getExtraInfoJSON() {
return Collections.<String, Object>emptyMap();
}
public void setExtraInfo(String key, Object value) {
throw new IllegalArgumentException("Key" + key + " is not a valid option");
}
/**
* Distance in meter until no new instruction
*/
public double getDistance() {
return distance;
}
public Instruction setDistance(double distance) {
this.distance = distance;
return this;
}
/**
* Duration until the next instruction, in milliseconds
*/
public long getTime() {
return time;
}
public Instruction setTime(long time) {
this.time = time;
return this;
}
/**
* Latitude of the location where this instruction should take place.
*/
double getFirstLat() {
return points.getLatitude(0);
}
/**
* Longitude of the location where this instruction should take place.
*/
double getFirstLon() {
return points.getLongitude(0);
}
double getFirstEle() {
return points.getElevation(0);
}
public PointList getPoints() {
return points;
}
/**
* This method returns a list of gpx entries where the time (in time) is relative to the first
* which is 0. It does NOT contain the last point which is the first of the next instruction.
* <p>
*
* @return the time offset to add for the next instruction
*/
long fillGPXList(List<GPXEntry> list, long time,
Instruction prevInstr, Instruction nextInstr, boolean firstInstr) {
checkOne();
int len = points.size();
long prevTime = time;
double lat = points.getLatitude(0);
double lon = points.getLongitude(0);
double ele = Double.NaN;
boolean is3D = points.is3D();
if (is3D)
ele = points.getElevation(0);
for (int i = 0; i < len; i++) {
list.add(new GPXEntry(lat, lon, ele, prevTime));
boolean last = i + 1 == len;
double nextLat = last ? nextInstr.getFirstLat() : points.getLatitude(i + 1);
double nextLon = last ? nextInstr.getFirstLon() : points.getLongitude(i + 1);
double nextEle = is3D ? (last ? nextInstr.getFirstEle() : points.getElevation(i + 1)) : Double.NaN;
if (is3D)
prevTime = Math.round(prevTime + this.time * Helper.DIST_3D.calcDist(nextLat, nextLon, nextEle, lat, lon, ele) / distance);
else
prevTime = Math.round(prevTime + this.time * Helper.DIST_3D.calcDist(nextLat, nextLon, lat, lon) / distance);
lat = nextLat;
lon = nextLon;
ele = nextEle;
}
return time + this.time;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('(');
sb.append(sign).append(',');
sb.append(name).append(',');
sb.append(distance).append(',');
sb.append(time);
sb.append(')');
return sb.toString();
}
/**
* Return the direction like 'NE' based on the first tracksegment of the instruction. If
* Instruction does not contain enough coordinate points, an empty string will be returned.
*/
String calcDirection(Instruction nextI) {
double azimuth = calcAzimuth(nextI);
if (Double.isNaN(azimuth))
return "";
return AC.azimuth2compassPoint(azimuth);
}
/**
* Return the azimuth in degree based on the first tracksegment of this instruction. If this
* instruction contains less than 2 points then NaN will be returned or the specified
* instruction will be used if that is the finish instruction.
*/
public double calcAzimuth(Instruction nextI) {
double nextLat;
double nextLon;
if (points.getSize() >= 2) {
nextLat = points.getLatitude(1);
nextLon = points.getLongitude(1);
} else if (nextI != null && points.getSize() == 1) {
nextLat = nextI.points.getLatitude(0);
nextLon = nextI.points.getLongitude(0);
} else {
return Double.NaN;
}
double lat = points.getLatitude(0);
double lon = points.getLongitude(0);
return AC.calcAzimuth(lat, lon, nextLat, nextLon);
}
void checkOne() {
if (points.size() < 1)
throw new IllegalStateException("Instruction must contain at least one point " + toString());
}
public String getTurnDescription(Translation tr) {
if (rawName)
return getName();
String str;
String streetName = getName();
int indi = getSign();
if (indi == Instruction.CONTINUE_ON_STREET) {
str = Helper.isEmpty(streetName) ? tr.tr("continue") : tr.tr("continue_onto", streetName);
} else if (indi == Instruction.PT_START_TRIP) {
str = tr.tr("pt_start_trip", streetName);
} else if (indi == Instruction.PT_TRANSFER) {
str = tr.tr("pt_transfer_to", streetName);
} else if (indi == Instruction.PT_END_TRIP) {
str = tr.tr("pt_end_trip", streetName);
} else {
String dir = null;
switch (indi) {
case Instruction.KEEP_LEFT:
dir = tr.tr("keep_left");
break;
case Instruction.TURN_SHARP_LEFT:
dir = tr.tr("turn_sharp_left");
break;
case Instruction.TURN_LEFT:
dir = tr.tr("turn_left");
break;
case Instruction.TURN_SLIGHT_LEFT:
dir = tr.tr("turn_slight_left");
break;
case Instruction.TURN_SLIGHT_RIGHT:
dir = tr.tr("turn_slight_right");
break;
case Instruction.TURN_RIGHT:
dir = tr.tr("turn_right");
break;
case Instruction.TURN_SHARP_RIGHT:
dir = tr.tr("turn_sharp_right");
break;
case Instruction.KEEP_RIGHT:
dir = tr.tr("keep_right");
break;
}
if (dir == null)
str = tr.tr("unknown", indi);
else
str = Helper.isEmpty(streetName) ? dir : tr.tr("turn_onto", dir, streetName);
}
return str;
}
}