/**
* Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org>
*
* Licensed 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 org.onebusaway.webapp.gwt.where_library.view;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import org.onebusaway.geospatial.model.CoordinateBounds;
import org.onebusaway.webapp.gwt.common.context.Context;
import org.onebusaway.webapp.gwt.common.context.ContextImpl;
import org.onebusaway.webapp.gwt.common.context.ContextListener;
import org.onebusaway.webapp.gwt.common.context.ContextManager;
import org.onebusaway.webapp.gwt.common.context.DirectContextManager;
import org.onebusaway.webapp.gwt.where_library.WhereLibrary;
import org.onebusaway.webapp.gwt.where_library.WhereMessages;
import org.onebusaway.webapp.gwt.where_library.view.constraints.DefaultOperationHandler;
import org.onebusaway.webapp.gwt.where_library.view.constraints.LocationOperationHandler;
import org.onebusaway.webapp.gwt.where_library.view.constraints.OperationContext;
import org.onebusaway.webapp.gwt.where_library.view.constraints.OperationHandler;
import org.onebusaway.webapp.gwt.where_library.view.constraints.QueryOperationHandler;
import org.onebusaway.webapp.gwt.where_library.view.constraints.RouteOperationHandler;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.maps.client.MapWidget;
import com.google.gwt.maps.client.geom.LatLng;
import com.google.gwt.maps.client.geom.LatLngBounds;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
public class StopFinderPresenter implements StopFinderInterface,
StopFinderConstants, ContextListener {
private static NumberFormat _format = NumberFormat.getFormat("0.0000");
private int _unique = 0;
private ContextManager _contextManager = null;
private StopFinderWidget _widget;
private OperationHandler _defaultOperationHandler = new DefaultOperationHandler();
/*****************************************************************************
* Public Methods
****************************************************************************/
public StopFinderPresenter() {
this(new DirectContextManager());
}
public StopFinderPresenter(ContextManager contextManager) {
setContextManager(contextManager);
}
public void setContextManager(ContextManager contextManager) {
if (_contextManager != null)
_contextManager.removeContextListener(this);
_contextManager = contextManager;
contextManager.addContextListener(this);
}
public void setWidget(StopFinderWidget widget) {
_widget = widget;
}
public void initialize() {
DeferredCommand.addCommand(new Command() {
@Override
public void execute() {
Context context = _contextManager.getContext();
if (context == null)
context = new ContextImpl();
onContextChanged(context);
}
});
}
public Context getCoordinateBoundsAsContext(CoordinateBounds bounds) {
if (bounds.isEmpty())
return new ContextImpl();
double latCenter = (bounds.getMinLat() + bounds.getMaxLat()) / 2;
double lonCenter = (bounds.getMinLon() + bounds.getMaxLon()) / 2;
double latSpan = bounds.getMaxLat() - bounds.getMinLat();
double lonSpan = bounds.getMaxLon() - bounds.getMinLon();
Map<String, String> m = new HashMap<String, String>();
addBoundsToParams(m, latCenter, lonCenter, latSpan, lonSpan);
return new ContextImpl(m);
}
public void setDefaultOperationHandler(OperationHandler handler) {
_defaultOperationHandler = handler;
}
/****
* {@link ContextListener} Interface
****/
public void onContextChanged(Context context) {
_widget.resetContents();
boolean locationSet = setMapCenter(context);
handleOperation(context, locationSet);
}
/****
* {@link StopFinderInterface}
****/
@Override
public String getCurrentViewAsUrl() {
Context context = _contextManager.getContext();
if (context == null)
context = new ContextImpl();
context = buildContext(context.getParams(), true);
return "#" + _contextManager.getContextAsString(context);
}
@Override
public void queryCurrentView() {
internalQuery(true, KEY_UNIQUE, _unique++);
}
@Override
public void query(String query) {
MapWidget map = _widget.getMapWidget();
LatLng center = map.getCenter();
String qll = format(center.getLatitude()) + ","
+ format(center.getLongitude());
internalQuery(false, KEY_MODE, MODE_QUERY, KEY_QUERY, query,
KEY_QUERY_LATLON, qll, KEY_UNIQUE, _unique++);
}
@Override
public void queryLocation(LatLng location, int accuracy) {
internalQuery(false, KEY_MODE, MODE_LOCATION, "lat",
location.getLatitude(), "lon", location.getLongitude(), "accuracy",
accuracy);
}
@Override
public void queryRoute(String routeId) {
internalQuery(false, KEY_MODE, MODE_ROUTE, KEY_ROUTE, routeId);
}
@Override
public String getStopQueryLink(String stopId) {
return "stop.action?id=" + stopId;
}
/*****************************************************************************
* Protected Methods
****************************************************************************/
protected OperationHandler getDefaultOperationHandler() {
return _defaultOperationHandler;
}
/*****************************************************************************
* Private Methods
*
* @param includeView
****************************************************************************/
private Context buildContext(Map<String, String> params, boolean includeView) {
Map<String, String> m = new LinkedHashMap<String, String>();
m.putAll(params);
if (includeView) {
MapWidget map = _widget.getMapWidget();
LatLng center = map.getCenter();
LatLngBounds bounds = map.getBounds();
LatLng ne = bounds.getNorthEast();
LatLng sw = bounds.getSouthWest();
double latSpan = Math.abs(ne.getLatitude() - sw.getLatitude());
double lonSpan = Math.abs(ne.getLongitude() - sw.getLongitude());
addBoundsToParams(m, center.getLatitude(), center.getLongitude(),
latSpan, lonSpan);
}
return new ContextImpl(m);
}
private void internalQuery(boolean includeView, Object... params) {
Map<String, String> m = new LinkedHashMap<String, String>();
if (params.length % 2 != 0)
throw new IllegalArgumentException(
"Number of params must be even (key-value pairs)");
for (int i = 0; i < params.length; i += 2)
m.put(params[i].toString(), params[i + 1].toString());
Context context = buildContext(m, includeView);
_contextManager.setContext(context);
}
private void addBoundsToParams(Map<String, String> m, double latCenter,
double lonCenter, double latSpan, double lonSpan) {
m.put(KEY_LATLON, format(latCenter) + "," + format(lonCenter));
m.put(KEY_SPAN, format(latSpan) + "," + format(lonSpan));
}
private String format(double dv) {
return _format.format(dv);
}
private boolean setMapCenter(Context context) {
try {
String latlon = context.getParam(KEY_LATLON);
LatLng center = getStringAsLatLng(latlon);
if( center == null)
return false;
int zoomLevel = getZoomLevelForContextAndCenter(context, center);
MapWidget map = _widget.getMapWidget();
map.setCenter(center, zoomLevel);
return true;
} catch (NumberFormatException ex) {
return false;
}
}
private LatLng getStringAsLatLng(String latlon) {
if (latlon == null)
return null;
String[] tokens = latlon.split(",");
if (tokens.length != 2)
return null;
try {
double lat = Double.parseDouble(tokens[0]);
double lon = Double.parseDouble(tokens[1]);
return LatLng.newInstance(lat, lon);
} catch (NumberFormatException ex) {
return null;
}
}
private int getZoomLevelForContextAndCenter(Context context, LatLng center) {
int zoom = 16;
String param = context.getParam(KEY_SPAN);
if (param == null)
return zoom;
String[] tokens = param.split(",");
if (tokens.length != 2)
return zoom;
try {
double latRadius = Double.parseDouble(tokens[0]) / 2;
double lonRadius = Double.parseDouble(tokens[1]) / 2;
LatLngBounds bounds = LatLngBounds.newInstance();
bounds.extend(LatLng.newInstance(center.getLatitude() + latRadius,
center.getLongitude() + lonRadius));
bounds.extend(LatLng.newInstance(center.getLatitude() - latRadius,
center.getLongitude() - lonRadius));
MapWidget map = _widget.getMapWidget();
return map.getBoundsZoomLevel(bounds);
} catch (NumberFormatException ex) {
return zoom;
}
}
private void handleOperation(Context context, boolean locationSet) {
OperationHandler handler = getOperartionHandler(context, locationSet);
OperationContext opContext = new OperationContext(_widget, locationSet);
handler.handleOperation(opContext);
}
private OperationHandler getOperartionHandler(Context context,
boolean locationSet) {
String mode = context.getParam(KEY_MODE);
if (MODE_QUERY.equals(mode)) {
String query = context.getParam(KEY_QUERY);
if (query == null || query.length() == 0)
return getDefaultOperationHandler();
String qll = context.getParam(KEY_QUERY_LATLON);
LatLng point = getStringAsLatLng(qll);
return new QueryOperationHandler(query,point);
} else if (MODE_LOCATION.equals(mode)) {
try {
double lat = Double.parseDouble(context.getParam("lat"));
double lon = Double.parseDouble(context.getParam("lon"));
int accuracy = Integer.parseInt(context.getParam("accuracy"));
return new LocationOperationHandler(LatLng.newInstance(lat, lon),
accuracy);
} catch (NumberFormatException ex) {
WhereMessages msgs = WhereLibrary.MESSAGES;
throw new IllegalStateException(
msgs.standardIndexPageInvalidLocationSpecified());
}
} else if (MODE_ROUTE.equals(mode)) {
String route = context.getParam(KEY_ROUTE);
return new RouteOperationHandler(route);
}
return getDefaultOperationHandler();
}
}