package org.androad.sys.ors.rs.openrouteservice;
import java.util.ArrayList;
import org.osmdroid.util.BoundingBoxE6;
import org.osmdroid.util.GeoPoint;
import org.androad.sys.ors.adt.Error;
import org.androad.sys.ors.adt.rs.Route;
import org.androad.sys.ors.adt.rs.RouteInstruction;
import org.androad.sys.ors.exceptions.ORSException;
import org.androad.util.TimeUtils;
import org.androad.util.constants.Constants;
import org.androad.util.constants.TimeConstants;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import android.util.Log;
public class OpenRouteServiceRSParser extends DefaultHandler implements TimeConstants, Constants {
// ====================================
// Constants
// ====================================
// ====================================
// Fields
// ====================================
private final ArrayList<Error> mErrors = new ArrayList<Error>();
private Route mRoute;
private ArrayList<GeoPoint> mPolyline;
private GeoPoint tmp;
private boolean inXLS = false;
private boolean inResponseHeader = false;
private boolean inResponse = false;
private boolean inDetermineRouteResponse = false;
private boolean inRouteSummary = false;
private boolean inTotalTime = false;
private boolean inTotalDistance = false;
private boolean inBoundingBox = false;
private boolean inPos = false;
private boolean inRouteGeometry = false;
private boolean inRouteHandle = false;
private boolean inLineString = false;
private boolean inRouteInstructionsList = false;
private boolean inRouteInstruction = false;
private boolean inInstruction = false;
private boolean inDistance = false;
private boolean inRouteInstructionGeometry = false;
private RouteInstruction mTmpRouteInstruction;
// ===========================================================
// Constructors
// ===========================================================
public OpenRouteServiceRSParser() {
}
// ===========================================================
// Getter & Setter
// ===========================================================
public ArrayList<Error> getErrors(){
return this.mErrors;
}
public Route getRoute() throws ORSException{
if(this.mErrors != null && this.mErrors.size() > 0) {
throw new ORSException(this.mErrors);
}
return this.mRoute;
}
// ====================================
// Methods from Superclasses
// ====================================
@Override
public void startDocument() throws SAXException {
this.mRoute = new Route();
this.mRoute.setRouteInstructions(new ArrayList<RouteInstruction>());
this.mPolyline = new ArrayList<GeoPoint>();
super.startDocument();
}
@Override
public void startElement(final String uri, final String localName, final String name, final Attributes attributes) throws SAXException {
if(localName.equals("Error") || name.equals("Error")){
final String errorCode = attributes.getValue("", "errorCode");
final String severity = attributes.getValue("", "severity");
final String locationPath = attributes.getValue("", "locationPath");
final String message = attributes.getValue("", "message");
this.mErrors.add(new Error(errorCode, severity, locationPath, message));
}
this.sb.setLength(0);
if(localName.equals("XLS")){
this.inXLS = true;
} else if(localName.equals("ResponseHeader")){
this.inResponseHeader = true;
} else if(localName.equals("Response")){
this.inResponse = true;
} else if(localName.equals("DetermineRouteResponse")){
this.inDetermineRouteResponse = true;
} else if(localName.equals("RouteSummary")){
this.inRouteSummary = true;
} else if(localName.equals("TotalTime")){
this.inTotalTime = true;
} else if(localName.equals("TotalDistance")){
this.inTotalDistance = true;
final String uom = attributes.getValue("", "uom");
float factor = 0;
if(uom != null){
if(uom.equals("M")){
factor = 1;
}else if(uom.equals("KM")){
factor = 1000;
}
}
this.mRoute.setDistanceMeters((int)(factor * Float.parseFloat(attributes.getValue("", "value"))));
} else if(localName.equals("BoundingBox")){
this.inBoundingBox = true;
} else if(localName.equals("RouteHandle")){
this.mRoute.setRouteHandleID(Long.parseLong(attributes.getValue("", "routeID")));
this.inRouteHandle = true;
} else if(localName.equals("pos")){
this.inPos = true;
} else if(localName.equals("RouteGeometry")){
this.inRouteGeometry = true;
} else if(localName.equals("LineString")){
this.inLineString = true;
} else if(localName.equals("RouteInstructionsList")){
this.inRouteInstructionsList = true;
} else if(localName.equals("RouteInstruction")){
this.inRouteInstruction = true;
this.mTmpRouteInstruction = new RouteInstruction();
this.mRoute.getRouteInstructions().add(this.mTmpRouteInstruction);
this.mTmpRouteInstruction.setDurationSeconds(TimeUtils.durationTimeString(attributes.getValue("", "duration")));
} else if(localName.equals("Instruction")){
this.inInstruction = true;
} else if(localName.equals("distance")){
this.inDistance = true;
final String uom = attributes.getValue("", "uom");
float factor = 0;
if(uom != null){
if(uom.equals("M")){
factor = 1;
}else if(uom.equals("KM")){
factor = 1000;
}
}
this.mTmpRouteInstruction.setLengthMeters((int)(factor * Float.parseFloat(attributes.getValue("", "value"))));
} else if(localName.equals("RouteInstructionGeometry")){
this.inRouteInstructionGeometry = true;
} else {
Log.w(DEBUGTAG, "Unexpected tag: '" + name + "'");
}
super.startElement(uri, localName, name, attributes);
}
protected StringBuilder sb = new StringBuilder();
private int mLastFirstMotherPolylineIndex = 0;
@Override
public void characters(final char[] chars, final int start, final int length) throws SAXException {
this.sb.append(chars, start, length);
super.characters(chars, start, length);
}
@Override
public void endElement(final String uri, final String localName, final String name) throws SAXException {
if(localName.equals("XLS")){
this.inXLS = false;
} else if(localName.equals("ResponseHeader")){
this.inResponseHeader = false;
} else if(localName.equals("Response")){
this.inResponse = false;
} else if(localName.equals("DetermineRouteResponse")){
this.inDetermineRouteResponse = false;
} else if(localName.equals("RouteSummary")){
this.inRouteSummary = false;
} else if(localName.equals("TotalTime")){
this.inTotalTime = false;
this.mRoute.setDurationSeconds(TimeUtils.durationTimeString(this.sb.toString()));
} else if(localName.equals("TotalDistance")){
this.inTotalDistance = false;
} else if(localName.equals("BoundingBox")){
this.inBoundingBox = false;
} else if(localName.equals("RouteHandle")){
this.inRouteHandle = false;
} else if(localName.equals("pos")){
this.inPos = false;
final GeoPoint gp = GeoPoint.fromInvertedDoubleString(this.sb.toString(), ' ');
if(this.inRouteGeometry){
this.mPolyline.add(gp);
} else if(this.inRouteInstructionGeometry){
this.mTmpRouteInstruction.getPartialPolyLine().add(gp);
// If this was the first element, we will determine its position in the OverallPolyline
if(this.mTmpRouteInstruction.getPartialPolyLine().size() == 1) {
this.mLastFirstMotherPolylineIndex = this.mRoute.findInPolyLine(gp, this.mLastFirstMotherPolylineIndex);
this.mTmpRouteInstruction.setFirstMotherPolylineIndex(this.mLastFirstMotherPolylineIndex);
}
} else if(this.inBoundingBox){
if(this.tmp == null){ // First GeoPoint
this.tmp = gp;
}else{ // Second one
final int mFirstLatE6 = this.tmp.getLatitudeE6();
final int mFirstLonE6 = this.tmp.getLongitudeE6();
this.tmp = gp;
final int mSecondLatE6 = this.tmp.getLatitudeE6();
final int mSecondLonE6 = this.tmp.getLongitudeE6();
this.mRoute.setBoundingBoxE6(new BoundingBoxE6(Math.max(mFirstLatE6, mSecondLatE6),
Math.max(mFirstLonE6, mSecondLonE6),
Math.min(mFirstLatE6, mSecondLatE6),
Math.min(mFirstLonE6, mSecondLonE6)));
}
}
} else if(localName.equals("RouteGeometry")){
this.inRouteGeometry = false;
this.mRoute.setPolyLine(this.mPolyline);
} else if(localName.equals("LineString")){
this.inLineString = false;
} else if(localName.equals("RouteInstructionsList")){
this.inRouteInstructionsList = false;
} else if(localName.equals("RouteInstruction")){
this.inRouteInstruction = false;
} else if(localName.equals("Instruction")){
this.inInstruction = false;
if(this.mTmpRouteInstruction.getDescriptionHtml() == null) {
this.mTmpRouteInstruction.setDescriptionHtml(this.sb.toString());
} else {
this.mTmpRouteInstruction.setDescriptionHtml(this.mTmpRouteInstruction.getDescriptionHtml() + this.sb.toString());
}
} else if(localName.equals("distance")){
this.inDistance = false;
} else if(localName.equals("RouteInstructionGeometry")){
this.inRouteInstructionGeometry = false;
} else {
Log.w(DEBUGTAG, "Unexpected end-tag: '" + name + "'");
}
// Reset the stringbuffer
this.sb.setLength(0);
super.endElement(uri, localName, name);
}
@Override
public void endDocument() throws SAXException {
if(this.mErrors == null || this.mErrors.size() == 0){
this.mRoute.setStart(this.mPolyline.get(0));
this.mRoute.setDestination(this.mPolyline.get(this.mPolyline.size() - 1));
this.mRoute.setStartInstruction(this.mRoute.getRouteInstructions().remove(0));
// Modify the arrival-instruction that is just shows
final RouteInstruction last = this.mRoute.getRouteInstructions().get(this.mRoute.getRouteInstructions().size() - 1);
last.setFirstMotherPolylineIndex(this.mPolyline.size() - 1);
}
super.endDocument();
}
}