/* -------------------------------------------------------------------------
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.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Widget;
import com.mecatran.otp.gwt.client.PlannerWidgetConfig;
import com.mecatran.otp.gwt.client.i18n.I18nUtils;
import com.mecatran.otp.gwt.client.model.AlertBean;
import com.mecatran.otp.gwt.client.model.ItineraryBean;
import com.mecatran.otp.gwt.client.model.LocationBean;
import com.mecatran.otp.gwt.client.model.ModeCapabilitiesBean;
import com.mecatran.otp.gwt.client.model.POIBean;
import com.mecatran.otp.gwt.client.model.PlanRequestBean;
import com.mecatran.otp.gwt.client.model.TransportMode;
import com.mecatran.otp.gwt.client.model.Wgs84BoundsBean;
import com.mecatran.otp.gwt.client.model.Wgs84LatLonBean;
import com.mecatran.otp.gwt.client.proxies.AlertsSourceProxy;
import com.mecatran.otp.gwt.client.proxies.AlertsSourceProxy.AlertsListener;
import com.mecatran.otp.gwt.client.proxies.GeocoderMultiplexer;
import com.mecatran.otp.gwt.client.proxies.GeocoderProxy;
import com.mecatran.otp.gwt.client.proxies.GeocoderProxy.ReverseGeocoderListener;
import com.mecatran.otp.gwt.client.proxies.POIGeocoder;
import com.mecatran.otp.gwt.client.proxies.POISource;
import com.mecatran.otp.gwt.client.proxies.POISource.POIListener;
import com.mecatran.otp.gwt.client.proxies.TransitPlannerProxy;
import com.mecatran.otp.gwt.client.proxies.TransitPlannerProxy.TransitPlannerListener;
import com.mecatran.otp.gwt.client.proxies.osm.NominatimGeocoderProxy;
import com.mecatran.otp.gwt.client.proxies.otp.OtpBikeRentalProxy;
import com.mecatran.otp.gwt.client.proxies.otp.OtpPlannerProxy;
import com.mecatran.otp.gwt.client.utils.FormatUtils;
import com.mecatran.otp.gwt.client.view.PlannerWidget;
import com.mecatran.otp.gwt.client.view.PlannerWidget.PlannerWidgetListener;
import com.mecatran.otp.gwt.client.view.PlannerWidgetImpl;
import com.mecatran.otp.gwt.client.view.PrintWidget;
import com.mecatran.otp.gwt.client.view.PrintWidget.PrintWidgetListener;
import com.mecatran.otp.gwt.client.view.PrintWidgetWindowImpl;
public class PlannerController implements PlannerWidgetListener,
PrintWidgetListener, TransitPlannerListener {
private PlannerWidgetConfig config;
private PlannerWidget plannerWidget;
private PrintWidget printWidget;
private TransitPlannerProxy plannerProxy;
private GeocoderProxy geocoderProxy;
private AlertsSourceProxy alertsSourceProxy;
private List<POISource> poiSources;
private ReverseGeocoderListener startAddressReverseGeocoderListener;
private ReverseGeocoderListener endAddressReverseGeocoderListener;
private boolean busyPlanning = false;
private int autoPlanAfterGeocode = 0;
public PlannerController(PlannerWidgetConfig config) {
this.config = config;
I18nUtils.setLocale(config.getLang());
FormatUtils.setTimeFormat(I18nUtils.tr("time.format.small"));
FormatUtils.setDateFormat(I18nUtils.tr("date.format.small"));
PlannerWidgetImpl theWidget = new PlannerWidgetImpl();
plannerWidget = theWidget;
plannerWidget.setPlannerWidgetListener(this);
// Use the "external window" print form
printWidget = new PrintWidgetWindowImpl(null);
printWidget.setPrintWidgetListener(this);
buildProxiesFromConfiguration(config);
for (POISource poiSource : poiSources) {
poiSource.addListener(new POIListener() {
@Override
public void poiUpdate(POISource source,
Map<String, POIBean> pois) {
plannerWidget.updatePOIs(source, pois);
}
});
}
if (alertsSourceProxy != null)
alertsSourceProxy.setListener(new AlertsListener() {
@Override
public void generalAlertPublished(AlertBean alert) {
plannerWidget.publishGeneralAlert(alert);
}
});
plannerWidget.setGeocoderProxy(geocoderProxy);
startAddressReverseGeocoderListener = new ReverseGeocoderListener() {
@Override
public void onReverseGeocodingDone(LocationBean location, int factor) {
plannerWidget.setStartLocation(location, false);
if (autoPlanAfterGeocode > 0)
autoPlanAfterGeocode--;
if (autoPlanAfterGeocode == 0)
planRouteIfPossible();
}
};
endAddressReverseGeocoderListener = new ReverseGeocoderListener() {
@Override
public void onReverseGeocodingDone(LocationBean location, int factor) {
plannerWidget.setEndLocation(location, false);
if (autoPlanAfterGeocode > 0)
autoPlanAfterGeocode--;
if (autoPlanAfterGeocode == 0)
planRouteIfPossible();
}
};
if (!Double.isNaN(config.getMinLat())
&& !Double.isNaN(config.getMinLon())
&& !Double.isNaN(config.getMaxLat())
&& !Double.isNaN(config.getMaxLon())) {
Wgs84BoundsBean bounds = new Wgs84BoundsBean();
bounds.extend(new Wgs84LatLonBean(config.getMinLat(), config
.getMinLon()));
bounds.extend(new Wgs84LatLonBean(config.getMaxLat(), config
.getMaxLon()));
setBounds(bounds);
}
}
public Widget getPlannerWidget() {
return plannerWidget.getAsWidget();
}
public void restoreState(PlannerState state) {
PlanRequestBean planRequestBean = state.getPlanRequestBean();
LocationBean departure = planRequestBean.getDeparture();
if (departure != null) {
plannerWidget.setStartLocation(departure, false);
autoPlanAfterGeocode++;
// Reverse geocode if needed
if (departure.getLocation() != null) {
onStartPointSelected(departure.getLocation());
}
}
LocationBean arrival = planRequestBean.getArrival();
if (arrival != null) {
plannerWidget.setEndLocation(arrival, false);
autoPlanAfterGeocode++;
// Reverse geocode if needed
if (arrival.getLocation() != null) {
onEndPointSelected(arrival.getLocation());
}
}
plannerWidget.setDateDeparture(planRequestBean.isDateDeparture());
if (planRequestBean.getDate() != null)
plannerWidget.setDate(planRequestBean.getDate());
if (planRequestBean.getModes() != null)
plannerWidget.setModes(planRequestBean.getModes());
plannerWidget.setWheelchairAccessible(planRequestBean
.isWheelchairAccessible());
}
private void buildProxiesFromConfiguration(PlannerWidgetConfig config) {
/* Planner proxy */
if (config.getProxyType().equals(PlannerWidgetConfig.PROXY_OTP)) {
plannerProxy = new OtpPlannerProxy(this, config.getOtpUrl(),
config.getRouterId(), config.getMaxItineraries());
} else {
Window.alert("Invalid proxy type: " + config.getProxyType());
throw new RuntimeException("Invalid proxy type: "
+ config.getProxyType());
}
/* Alert sources */
// alertsSourceProxy = new XyzAlertsSourceProxy();
// alertsSourceProxy.configure(config.getLang());
poiSources = new ArrayList<POISource>();
/* Bike rental sources */
if (config.getProxyType().equals(PlannerWidgetConfig.PROXY_OTP)
&& config.isHasBikeRental()) {
poiSources.add(new OtpBikeRentalProxy(config.getOtpUrl(), config
.getRouterId()));
}
/* POIs sources */
// poiSources.add(new XyzPOISource());
/* Geocoder(s) */
GeocoderMultiplexer geocoderMultiplexer = new GeocoderMultiplexer();
geocoderMultiplexer.addGeocoder(new NominatimGeocoderProxy(
NominatimGeocoderProxy.OSM_NOMINATIM_URL, 10));
if (config.getProxyType().equals(PlannerWidgetConfig.PROXY_OTP)) {
// TODO Parametrize
// geocoderMultiplexer.addGeocoder(new OtpGeocoderProxy(config
// .getOtpUrl(), config.getRouterId()));
}
// Add a geocoder for each POI source
for (POISource poiSource : poiSources) {
// TODO Configure "useBounds" if needed. How?
geocoderMultiplexer.addGeocoder(new POIGeocoder(poiSource, false));
}
geocoderProxy = geocoderMultiplexer;
}
private void setBounds(Wgs84BoundsBean bounds) {
plannerWidget.setBounds(bounds);
geocoderProxy.configure(bounds, config.getMainCountryName());
}
private void setModeCapabilities(ModeCapabilitiesBean modeCapabilities) {
plannerWidget.setModeCapabilities(modeCapabilities);
plannerWidget.switchMapBackground(plannerWidget.getPlanRequestBean()
.getModes());
}
/* === PlannerWidgetListener === */
@Override
public void onPlanningRequested() {
PlanRequestBean planRequest = plannerWidget.getPlanRequestBean();
boolean okDeparture = planRequest.getDeparture().isSet(
plannerProxy.isRequireGeocoding());
boolean okArrival = planRequest.getArrival().isSet(
plannerProxy.isRequireGeocoding());
if (!okDeparture || !okArrival) {
String message = "";
if (!okDeparture)
message += I18nUtils.tr("departure.address.not.found",
planRequest.getDeparture().getAddress());
if (!okArrival)
message += I18nUtils.tr("arrival.address.not.found",
planRequest.getDeparture().getAddress());
if (!okDeparture || !okArrival)
message += "\n\n" + I18nUtils.tr("address.not.found");
plannerWidget.showError(message);
} else {
planRouteIfPossible();
}
}
private void planRouteIfPossible() {
PlanRequestBean planRequest = plannerWidget.getPlanRequestBean();
if (!busyPlanning
&& planRequest.getDeparture().isSet(
plannerProxy.isRequireGeocoding())
&& planRequest.getArrival().isSet(
plannerProxy.isRequireGeocoding())) {
busyPlanning = true;
plannerWidget.setBusy(true);
plannerWidget.clearItineraries();
plannerProxy.planRoute(planRequest);
}
}
@Override
public void onItineraryRemoved(ItineraryBean itinerary) {
// Nothing special for the moment.
}
@Override
public void onItinerarySelected(ItineraryBean itinerary) {
plannerWidget.showItineraryOnMap(itinerary, false);
}
@Override
public void onItineraryHover(ItineraryBean itinerary) {
plannerWidget.showItineraryOnMap(itinerary, true);
}
@Override
public void onItineraryPinned(ItineraryBean itinerary, boolean pinned) {
// Nothing special for the moment.
}
@Override
public void onItineraryPrintRequest(ItineraryBean itinerary) {
printWidget.printItinerary(itinerary);
}
@Override
public void onStartPointSelected(Wgs84LatLonBean location) {
plannerWidget.unselectItinerary();
geocoderProxy.reverseGeocode(location,
startAddressReverseGeocoderListener);
}
@Override
public void onEndPointSelected(Wgs84LatLonBean location) {
plannerWidget.unselectItinerary();
geocoderProxy.reverseGeocode(location,
endAddressReverseGeocoderListener);
}
@Override
public void onStartLocationSelected(LocationBean location) {
plannerWidget.setStartLocation(location, true);
if (autoPlanAfterGeocode > 0) {
autoPlanAfterGeocode--;
if (autoPlanAfterGeocode == 0)
planRouteIfPossible();
}
}
@Override
public void onEndLocationSelected(LocationBean location) {
plannerWidget.setEndLocation(location, true);
if (autoPlanAfterGeocode > 0) {
autoPlanAfterGeocode--;
if (autoPlanAfterGeocode == 0)
planRouteIfPossible();
}
}
@Override
public void onLocationHover(LocationBean location) {
plannerWidget.previewLocation(location);
}
@Override
public void onTransportModeChange(Set<TransportMode> modes) {
if (plannerWidget != null)
plannerWidget.switchMapBackground(modes);
}
/* === TransitPlannerListener === */
@Override
public void onItineraryFound(List<ItineraryBean> itineraries) {
busyPlanning = false;
plannerWidget.setBusy(false);
// It is already sorted, but add first last element
for (int i = itineraries.size() - 1; i >= 0; i--) {
ItineraryBean itinerary = itineraries.get(i);
if (alertsSourceProxy != null)
alertsSourceProxy.fillAlerts(itinerary);
plannerWidget.addItinerary(itinerary, i == 0);
}
}
@Override
public void onItineraryError(String errorMessage) {
busyPlanning = false;
plannerWidget.setBusy(false);
plannerWidget.showError(errorMessage);
}
@Override
public void onPlannerConfigured(Wgs84BoundsBean bounds,
ModeCapabilitiesBean modeCapabilities) {
if (bounds != null) {
setBounds(bounds);
}
if (modeCapabilities != null) {
modeCapabilities.setHasWalkOnly(modeCapabilities.isHasWalkOnly()
&& config.isHasWalkOnly());
modeCapabilities.setHasBikeOnly(modeCapabilities.isHasBikeOnly()
&& config.isHasBikeOnly());
modeCapabilities.setHasTransit(modeCapabilities.isHasTransit()
&& config.isHasTransit());
modeCapabilities.setHasBikeRental(modeCapabilities
.isHasBikeRental() && config.isHasBikeRental());
modeCapabilities.setHasBikeAndTransit(modeCapabilities
.isHasBikeAndTransit() && config.isHasBikeAndTransit());
setModeCapabilities(modeCapabilities);
}
}
/* === PrintWidgetListener === */
@Override
public void onPrintDone() {
}
}