/* -------------------------------------------------------------------------
OpenTripPlanner GWT Client
Copyright (C) 2015 Mecatran - info@mecatran.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
------------------------------------------------------------------------- */
package com.mecatran.otp.gwt.client.view;
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.dom.client.BodyElement;
import com.google.gwt.dom.client.Element;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Widget;
import com.mecatran.otp.gwt.client.i18n.I18nUtils;
import com.mecatran.otp.gwt.client.model.ItineraryBean;
import com.mecatran.otp.gwt.client.model.ItineraryLegBean;
import com.mecatran.otp.gwt.client.model.ItineraryRoadLegBean;
import com.mecatran.otp.gwt.client.model.ItineraryRoadStepBean;
import com.mecatran.otp.gwt.client.model.ItineraryTransitLegBean;
import com.mecatran.otp.gwt.client.model.TransitRouteBean;
import com.mecatran.otp.gwt.client.model.Wgs84LatLonBean;
import com.mecatran.otp.gwt.client.utils.FormatUtils;
import com.mecatran.otp.gwt.client.utils.PolylineEncoder;
/*
* TODO Use html templating?
*/
public class PrintWidgetWindowImpl implements PrintWidget {
private PrintWidgetListener printWidgetListener;
private String printCss = "";
public PrintWidgetWindowImpl(String customCssUrl) {
if (customCssUrl != null)
downloadPrintCss(customCssUrl);
}
@Override
public Widget getAsWidget() {
return null;
}
@Override
public void setPrintWidgetListener(PrintWidgetListener printWidgetListener) {
this.printWidgetListener = printWidgetListener;
}
@Override
public void printItinerary(ItineraryBean itinerary) {
FlowPanel rootPanel = new FlowPanel();
/* Compute some values */
int nTransfers = 0;
long walkDistanceMeter = 0;
long bikeDistanceMeter = 0;
for (ItineraryLegBean leg : itinerary.getLegs()) {
switch (leg.getTravelType()) {
case ROAD:
switch (leg.getAsRoadLeg().getMode()) {
case BICYCLE:
case BICYCLE_RENTAL:
bikeDistanceMeter += leg.getAsRoadLeg().getDistanceMeters();
break;
case WALK:
walkDistanceMeter += leg.getAsRoadLeg().getDistanceMeters();
break;
default:
// pass
}
break;
case TRANSIT:
nTransfers++;
break;
}
}
/* Display summary */
rootPanel.add(new HTML("<h1>"
+ I18nUtils.tr("print.itinerary.header",
itinerary.getStartAddress(), itinerary.getEndAddress())
+ "</h1>"));
rootPanel.add(new HTML(I18nUtils.tr("print.summary.departure",
FormatUtils.formatDateTime(itinerary.getDepartureTime()),
itinerary.getStartAddress())));
rootPanel.add(new HTML(I18nUtils.tr("print.summary.arrival",
FormatUtils.formatDateTime(itinerary.getArrivalTime()),
itinerary.getEndAddress())));
rootPanel.add(new HTML(I18nUtils.tr("print.summary.duration",
FormatUtils.formatDuration(itinerary.getDurationSeconds()),
FormatUtils.formatDistance(itinerary.getDistanceMeters()))));
StringBuffer infoHtml = new StringBuffer();
if (nTransfers > 0) {
infoHtml.append(
I18nUtils.tr("print.summary.transfers", nTransfers - 1))
.append(", ");
}
if (walkDistanceMeter > 0) {
infoHtml.append(
I18nUtils.tr("print.summary.walk.distance",
FormatUtils.formatDistance(walkDistanceMeter)))
.append(", ");
}
if (bikeDistanceMeter > 0) {
infoHtml.append(
I18nUtils.tr("print.summary.bike.distance",
FormatUtils.formatDistance(bikeDistanceMeter)))
.append(", ");
}
// Remove last comma
if (infoHtml.length() > 0)
infoHtml.setLength(infoHtml.length() - 2);
rootPanel.add(new HTML(infoHtml.toString()));
rootPanel.add(new HTML("<h2>"
+ I18nUtils.tr("print.itinerary.details.header") + "</h2>"));
/* Departure */
rootPanel.add(new HTML("<h3>"
+ I18nUtils.tr("print.step.depart",
FormatUtils.formatTime(itinerary.getDepartureTime()),
FormatUtils.formatAddress(itinerary.getStartAddress()))
+ "</h3>"));
/* Details */
for (ItineraryLegBean leg : itinerary.getLegs()) {
switch (leg.getTravelType()) {
case ROAD:
rootPanel.add(getRoadDetails((ItineraryRoadLegBean) leg));
break;
case TRANSIT:
rootPanel.add(getTransitDetails((ItineraryTransitLegBean) leg));
break;
default:
throw new IllegalArgumentException("Unsupported leg type: "
+ leg.getTravelType());
}
}
/* Arrival */
rootPanel.add(new HTML("<h3>"
+ I18nUtils.tr("print.step.arrival",
FormatUtils.formatTime(itinerary.getArrivalTime()),
FormatUtils.formatAddress(itinerary.getEndAddress()))
+ "</h3>"));
openWindow(rootPanel.getElement());
}
private void downloadPrintCss(String cssUrl) {
RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET,
cssUrl);
try {
requestBuilder.sendRequest(null, new RequestCallback() {
@Override
public void onResponseReceived(Request request,
Response response) {
printCss = response.getText();
}
@Override
public void onError(Request request, Throwable exception) {
}
});
} catch (RequestException e) {
}
}
private void openWindow(Element rootElement) {
/* This is the hackish area ... */
BodyElement printBody = openWindow(I18nUtils.tr("print.button"),
printCss);
printBody.getOwnerDocument().getElementById("printarea")
.appendChild(rootElement);
}
/**
* Implementation note: Including a CSS as script with href in the HTML
* document below does not work. We need to include the CSS inline,
* pre-downloaded.
*
* @param printLabel
* @param cssContent
* CSS CONTENT, not URL.
* @return
*/
public static native BodyElement openWindow(String printLabel,
String cssContent) /*-{
var win = window.open("", "win", "width=940,height=400,status=1,resizeable=1,scrollbars=1");
win.document.open("text/html", "replace");
win.document.write("<html><head><style>\n" + cssContent
+ "\n</style></head><body><div><button onclick='window.print()'>" + printLabel
+ "</button></div><div id='printarea'/></body></html>");
win.document.close();
win.focus();
return win.document.body;
}-*/;
private Widget getRoadDetails(ItineraryRoadLegBean leg) {
FlowPanel line = new FlowPanel();
// Main instruction
line.add(new HTML("<h3>" + leg.getInstructions() + " ("
+ FormatUtils.formatDistance(leg.getDistanceMeters()) + " - "
+ FormatUtils.formatDuration(leg.getDurationSeconds())
+ ")</h3>"));
// Steps
StringBuilder stepsHtml = new StringBuilder();
stepsHtml.append("<ul>");
for (ItineraryRoadStepBean step : leg.getRoadSteps()) {
if (step.getInstructions() == null) {
// Can happen in case of transfers between two transit legs
continue;
}
stepsHtml
.append("<li>")
.append(step.getInstructions())
.append((step.getDistanceMeters() >= 5 ? " ("
+ FormatUtils.formatDistance(step
.getDistanceMeters())
+ " - "
+ FormatUtils.formatDuration(step
.getDurationSeconds()) + ")" : ""))
.append("</li>");
}
// Map
StringBuilder mapsHtml = new StringBuilder();
if (leg.getPath() != null && leg.getPath().length > 0) {
for (String url : getMapUrl(leg.getPath())) {
mapsHtml.append("<img style='width: 8cm' src='").append(url)
.append("'><br/><hr/>");
}
}
// Table
StringBuilder tableHtml = new StringBuilder();
tableHtml.append("<table><tr><td>").append(mapsHtml.toString())
.append("</td><td>").append(stepsHtml.toString())
.append("</td></tr></table>");
line.add(new HTML(tableHtml.toString()));
return line;
}
private Widget getTransitDetails(ItineraryTransitLegBean leg) {
FlowPanel line = new FlowPanel();
TransitRouteBean route = leg.getRoute();
String codeHtml = "<b><span class='route-code' style='color:"
+ route.getForegroundColor() + "; background-color:"
+ route.getBackgroundColor() + "'> " + route.getCode()
+ " </span></b>";
line.add(new HTML("<h3>"
+ I18nUtils.tr("print.take.the.route", codeHtml, FormatUtils
.formatTime(leg.getDepartureTime()), leg
.getDepartureStop().getName(), leg.getHeadsign())
+ "</h3>"));
line.add(new HTML("<h3>"
+ I18nUtils.tr("print.hop.off", "<span class='time'>"
+ FormatUtils.formatTime(leg.getArrivalTime())
+ "</span>", leg.getArrivalStop().getName())
+ "\"</h3>"));
return line;
}
private List<String> getMapUrl(Wgs84LatLonBean[] path) {
final int MAP_PATH_SIZE = 100;
NumberFormat dec5Format = NumberFormat.getFormat("#.00000");
List<String> retval = new ArrayList<String>();
int n = (path.length - 1) / MAP_PATH_SIZE + 1;
int m = path.length / n + 1;
int js = 0;
for (int i = 0; i < n; i++) {
StringBuilder urlBuilder = new StringBuilder();
int je = js + m;
if (je > path.length)
je = path.length;
boolean start = i == 0;
boolean end = je == path.length;
urlBuilder
.append("http://maps.googleapis.com/maps/api/staticmap?sensor=false&scale=2&size=800x800&maptype=roadmap");
if (start)
urlBuilder.append("&markers=")
.append(dec5Format.format(path[0].getLat()))
.append(",")
.append(dec5Format.format(path[0].getLon()));
if (end && !start)
urlBuilder.append("&markers=");
else
urlBuilder.append("|");
if (end)
urlBuilder
.append(dec5Format.format(path[path.length - 1]
.getLat()))
.append(",")
.append(dec5Format.format(path[path.length - 1]
.getLon()));
urlBuilder.append("&path=color:blue|enc:");
Wgs84LatLonBean[] subPath = new Wgs84LatLonBean[je - js];
// GWT does not have Arrays.copyOfRange() ...
for (int k = js; k < je; k++) {
subPath[k - js] = path[k];
}
urlBuilder.append(PolylineEncoder.encode(subPath));
retval.add(urlBuilder.toString());
js = je;
}
assert (js == path.length);
return retval;
}
}