gov.wa.wsdot.mobile.client.activities.trafficmap.TrafficMapActivity.java Source code

Java tutorial

Introduction

Here is the source code for gov.wa.wsdot.mobile.client.activities.trafficmap.TrafficMapActivity.java

Source

/*
 * Copyright (c) 2016 Washington State Department of Transportation
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>
 *
 */

package gov.wa.wsdot.mobile.client.activities.trafficmap;

import com.google.code.gwt.database.client.GenericRow;
import com.google.code.gwt.database.client.service.DataServiceException;
import com.google.code.gwt.database.client.service.ListCallback;
import com.google.code.gwt.database.client.service.RowIdListCallback;
import com.google.code.gwt.database.client.service.VoidCallback;
import com.google.gwt.core.client.JsonUtils;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.jsonp.client.JsonpRequestBuilder;
import com.google.gwt.maps.client.base.LatLng;
import com.google.gwt.maps.client.base.LatLngBounds;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.core.java.lang.Double_CustomFieldSerializer;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.googlecode.gwtphonegap.client.PhoneGap;
import com.googlecode.gwtphonegap.client.geolocation.GeolocationCallback;
import com.googlecode.gwtphonegap.client.geolocation.Position;
import com.googlecode.gwtphonegap.client.geolocation.PositionError;
import com.googlecode.gwtphonegap.client.notification.AlertCallback;
import com.googlecode.gwtphonegap.client.notification.PromptCallback;
import com.googlecode.gwtphonegap.client.notification.PromptResults;
import com.googlecode.mgwt.mvp.client.MGWTAbstractActivity;
import gov.wa.wsdot.mobile.client.ClientFactory;
import gov.wa.wsdot.mobile.client.activities.alert.AlertPlace;
import gov.wa.wsdot.mobile.client.activities.callout.CalloutPlace;
import gov.wa.wsdot.mobile.client.activities.camera.CameraPlace;
import gov.wa.wsdot.mobile.client.activities.trafficmap.menu.TrafficMenuPlace;
import gov.wa.wsdot.mobile.client.activities.trafficmap.restarea.RestAreaPlace;
import gov.wa.wsdot.mobile.client.activities.trafficmap.trafficincidents.TrafficAlertsPlace;
import gov.wa.wsdot.mobile.client.css.AppBundle;
import gov.wa.wsdot.mobile.client.event.ActionEvent;
import gov.wa.wsdot.mobile.client.event.ActionNames;
import gov.wa.wsdot.mobile.client.plugins.accessibility.Accessibility;
import gov.wa.wsdot.mobile.client.plugins.analytics.Analytics;
import gov.wa.wsdot.mobile.client.service.WSDOTContract.CachesColumns;
import gov.wa.wsdot.mobile.client.service.WSDOTContract.CamerasColumns;
import gov.wa.wsdot.mobile.client.service.WSDOTContract.HighwayAlertsColumns;
import gov.wa.wsdot.mobile.client.service.WSDOTDataService;
import gov.wa.wsdot.mobile.client.service.WSDOTDataService.Tables;
import gov.wa.wsdot.mobile.client.util.Consts;
import gov.wa.wsdot.mobile.shared.*;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class TrafficMapActivity extends MGWTAbstractActivity implements TrafficMapView.Presenter {

    private final ClientFactory clientFactory;
    private TrafficMapView view;

    private EventBus eventBus;
    private WSDOTDataService dbService;
    private PhoneGap phoneGap;
    private Analytics analytics;
    private Accessibility accessibility;
    private static List<Integer> starred = new ArrayList<>();
    private static List<CameraItem> cameraItems = new ArrayList<>();
    private static List<HighwayAlertItem> highwayAlertItems = new ArrayList<>();
    private static List<RestAreaItem> restAreaItems = new ArrayList<>();
    private static List<CalloutItem> calloutItems = new ArrayList<>();
    private static final String CAMERAS_URL = Consts.HOST_URL + "/traveler/api/cameras";
    private static final String HIGHWAY_ALERTS_URL = Consts.HOST_URL + "/traveler/api/highwayalerts";
    private static DateTimeFormat dateFormat = DateTimeFormat.getFormat("MMMM d, yyyy h:mm a");

    public TrafficMapActivity(ClientFactory clientFactory) {
        this.clientFactory = clientFactory;
    }

    @Override
    public void start(AcceptsOneWidget panel, final EventBus eventBus) {
        view = clientFactory.getTrafficMapView();
        dbService = clientFactory.getDbService();
        phoneGap = clientFactory.getPhoneGap();
        analytics = clientFactory.getAnalytics();
        accessibility = clientFactory.getAccessibility();
        this.eventBus = eventBus;
        view.setPresenter(this);
        view.setMapLocation(); // Set initial map location.

        if (Consts.ANALYTICS_ENABLED) {
            analytics.trackScreen("/Traffic Map");
        }

        panel.setWidget(view);

        accessibility.postScreenChangeNotification();

        view.refreshMap();
    }

    private void getCameras() {

        /** 
         * Check the cache table for the last time data was downloaded. If we are within
         * the allowed time period, don't sync, otherwise get fresh data from the server.
         */
        dbService.getCacheLastUpdated(Tables.CAMERAS, new ListCallback<GenericRow>() {

            @Override
            public void onFailure(DataServiceException error) {
            }

            @Override
            public void onSuccess(List<GenericRow> result) {
                boolean shouldUpdate = true;

                if (!result.isEmpty()) {
                    double now = System.currentTimeMillis();
                    double lastUpdated = result.get(0).getDouble(CachesColumns.CACHE_LAST_UPDATED);
                    shouldUpdate = (Math.abs(now - lastUpdated) > (7 * 86400000)); // Refresh every 7 days.
                }

                view.showProgressIndicator();

                if (shouldUpdate) {
                    /**
                     * Check the cameras table for any starred entries. If we find some,
                     * save them to a list so we can re-star those after we flush the database.
                     */
                    dbService.getStarredCameras(new ListCallback<GenericRow>() {

                        @Override
                        public void onFailure(DataServiceException error) {
                        }

                        @Override
                        public void onSuccess(List<GenericRow> result) {
                            starred.clear();

                            if (!result.isEmpty()) {
                                for (GenericRow row : result) {
                                    starred.add(row.getInt(CamerasColumns.CAMERA_ID));
                                }
                            }

                            JsonpRequestBuilder jsonp = new JsonpRequestBuilder();
                            // Set timeout for 30 seconds (30000 milliseconds)
                            jsonp.setTimeout(30000);
                            jsonp.requestObject(CAMERAS_URL, new AsyncCallback<CamerasFeed>() {

                                @Override
                                public void onFailure(Throwable caught) {
                                    view.hideProgressIndicator();
                                    phoneGap.getNotification().alert("Can't load data. Check your connection.",
                                            new AlertCallback() {
                                                @Override
                                                public void onOkButtonClicked() {
                                                    // TODO Auto-generated method stub
                                                }
                                            }, "Connection Error");
                                }

                                @Override
                                public void onSuccess(CamerasFeed result) {

                                    if (result.getCameras() != null) {
                                        cameraItems.clear();
                                        CameraItem item;

                                        int numCameras = result.getCameras().getItems().length();

                                        for (int i = 0; i < numCameras; i++) {
                                            item = new CameraItem();

                                            item.setCameraId(result.getCameras().getItems().get(i).getId());
                                            item.setTitle(result.getCameras().getItems().get(i).getTitle());
                                            item.setImageUrl(result.getCameras().getItems().get(i).getUrl());
                                            item.setLatitude(result.getCameras().getItems().get(i).getLat());
                                            item.setLongitude(result.getCameras().getItems().get(i).getLon());
                                            item.setHasVideo(result.getCameras().getItems().get(i).getHasVideo());
                                            item.setRoadName(result.getCameras().getItems().get(i).getRoadName());

                                            if (starred.contains(result.getCameras().getItems().get(i).getId())) {
                                                item.setIsStarred(1);
                                            }

                                            cameraItems.add(item);
                                        }

                                        // Purge existing cameras covered by incoming data
                                        dbService.deleteCameras(new VoidCallback() {

                                            @Override
                                            public void onFailure(DataServiceException error) {
                                            }

                                            @Override
                                            public void onSuccess() {
                                                // Bulk insert all the new cameras
                                                dbService.insertCameras(cameraItems, new RowIdListCallback() {

                                                    @Override
                                                    public void onFailure(DataServiceException error) {
                                                    }

                                                    @Override
                                                    public void onSuccess(List<Integer> rowIds) {
                                                        // Update the cache table with the time we did the update
                                                        List<CacheItem> cacheItems = new ArrayList<CacheItem>();
                                                        cacheItems.add(new CacheItem(Tables.CAMERAS,
                                                                System.currentTimeMillis()));

                                                        dbService.updateCachesTable(cacheItems, new VoidCallback() {

                                                            @Override
                                                            public void onFailure(DataServiceException error) {
                                                            }

                                                            @Override
                                                            public void onSuccess() {
                                                                view.hideProgressIndicator();
                                                                drawCamerasLayer();
                                                            }
                                                        });
                                                    }
                                                });
                                            }
                                        });
                                    }
                                }
                            });
                        }
                    });

                } else {
                    view.hideProgressIndicator();
                    drawCamerasLayer();
                }
            }
        });
    }

    private void drawCamerasLayer() {

        dbService.getCameras(new ListCallback<GenericRow>() {

            @Override
            public void onFailure(DataServiceException error) {
            }

            @Override
            public void onSuccess(List<GenericRow> result) {
                LatLngBounds bounds = view.getViewportBounds();
                LatLng swPoint = bounds.getSouthWest();
                LatLng nePoint = bounds.getNorthEast();

                ArrayList<LatLonItem> viewableMapArea = new ArrayList<LatLonItem>();
                viewableMapArea.add(new LatLonItem(nePoint.getLatitude(), swPoint.getLongitude()));
                viewableMapArea.add(new LatLonItem(nePoint.getLatitude(), nePoint.getLongitude()));
                viewableMapArea.add(new LatLonItem(swPoint.getLatitude(), nePoint.getLongitude()));
                viewableMapArea.add(new LatLonItem(swPoint.getLatitude(), swPoint.getLongitude()));

                List<CameraItem> cameras = new ArrayList<CameraItem>();
                int numRows = result.size();

                for (int i = 0; i < numRows; i++) {
                    if (inPolygon(viewableMapArea, result.get(i).getDouble(CamerasColumns.CAMERA_LATITUDE),
                            result.get(i).getDouble(CamerasColumns.CAMERA_LONGITUDE))) {

                        cameras.add(new CameraItem(result.get(i).getInt(CamerasColumns.CAMERA_ID),
                                result.get(i).getString(CamerasColumns.CAMERA_TITLE),
                                result.get(i).getString(CamerasColumns.CAMERA_URL),
                                result.get(i).getDouble(CamerasColumns.CAMERA_LATITUDE),
                                result.get(i).getDouble(CamerasColumns.CAMERA_LONGITUDE),
                                result.get(i).getInt(CamerasColumns.CAMERA_HAS_VIDEO)));
                    }
                }

                if (!result.isEmpty()) {
                    view.drawCameras(cameras);
                }
            }
        });

    }

    private void getHighwayAlerts() {
        /** 
         * Check the cache table for the last time data was downloaded. If we are within
         * the allowed time period, don't sync, otherwise get fresh data from the server.
         */
        dbService.getCacheLastUpdated(Tables.HIGHWAY_ALERTS, new ListCallback<GenericRow>() {

            @Override
            public void onFailure(DataServiceException error) {
            }

            @Override
            public void onSuccess(List<GenericRow> result) {
                boolean shouldUpdate = true;

                if (!result.isEmpty()) {
                    double now = System.currentTimeMillis();
                    double lastUpdated = result.get(0).getDouble(CachesColumns.CACHE_LAST_UPDATED);
                    shouldUpdate = (Math.abs(now - lastUpdated) > (5 * 60000)); // Refresh every 5 minutes.
                }

                if (shouldUpdate) {
                    try {
                        JsonpRequestBuilder jsonp = new JsonpRequestBuilder();
                        // Set timeout for 30 seconds (30000 milliseconds)
                        jsonp.setTimeout(30000);
                        jsonp.requestObject(HIGHWAY_ALERTS_URL, new AsyncCallback<HighwayAlerts>() {

                            @Override
                            public void onFailure(Throwable caught) {
                            }

                            @Override
                            public void onSuccess(HighwayAlerts result) {
                                highwayAlertItems.clear();

                                if (result.getAlerts() != null) {
                                    HighwayAlertItem item;
                                    int size = result.getAlerts().getItems().length();

                                    for (int i = 0; i < size; i++) {
                                        item = new HighwayAlertItem();

                                        item.setAlertId(result.getAlerts().getItems().get(i).getAlertID());
                                        item.setHeadlineDescription(
                                                result.getAlerts().getItems().get(i).getHeadlineDescription());
                                        item.setEventCategory(
                                                result.getAlerts().getItems().get(i).getEventCategory());
                                        item.setPriority(result.getAlerts().getItems().get(i).getPriority());
                                        item.setStartLatitude(result.getAlerts().getItems().get(i)
                                                .getStartRoadwayLocation().getLatitude());
                                        item.setStartLongitude(result.getAlerts().getItems().get(i)
                                                .getStartRoadwayLocation().getLongitude());
                                        item.setStartRoadName(result.getAlerts().getItems().get(i)
                                                .getStartRoadwayLocation().getRoadName());
                                        item.setLastUpdatedTime(dateFormat
                                                .format(new Date(Long.parseLong(result.getAlerts().getItems().get(i)
                                                        .getLastUpdatedTime().substring(6, 19)))));

                                        highwayAlertItems.add(item);
                                    }

                                    // Purge existing highway alerts covered by incoming data
                                    dbService.deleteHighwayAlerts(new VoidCallback() {

                                        @Override
                                        public void onFailure(DataServiceException error) {
                                        }

                                        @Override
                                        public void onSuccess() {
                                            // Bulk insert all the new highway alerts
                                            dbService.insertHighwayAlerts(highwayAlertItems,
                                                    new RowIdListCallback() {

                                                        @Override
                                                        public void onFailure(DataServiceException error) {
                                                        }

                                                        @Override
                                                        public void onSuccess(List<Integer> rowIds) {
                                                            // Update the cache table with the time we did the update
                                                            List<CacheItem> cacheItems = new ArrayList<CacheItem>();
                                                            cacheItems.add(new CacheItem(Tables.HIGHWAY_ALERTS,
                                                                    System.currentTimeMillis()));
                                                            dbService.updateCachesTable(cacheItems,
                                                                    new VoidCallback() {

                                                                        @Override
                                                                        public void onFailure(
                                                                                DataServiceException error) {
                                                                        }

                                                                        @Override
                                                                        public void onSuccess() {
                                                                            drawHighwayAlertsLayer();
                                                                        }

                                                                    });
                                                        }
                                                    });
                                        }
                                    });
                                }
                            }
                        });

                    } catch (Exception e) {

                    }
                } else {
                    drawHighwayAlertsLayer();
                }
            }
        });

    }

    private void drawHighwayAlertsLayer() {

        dbService.getHighwayAlerts(new ListCallback<GenericRow>() {

            @Override
            public void onFailure(DataServiceException error) {
            }

            @Override
            public void onSuccess(List<GenericRow> result) {
                LatLngBounds bounds = view.getViewportBounds();
                LatLng swPoint = bounds.getSouthWest();
                LatLng nePoint = bounds.getNorthEast();

                ArrayList<LatLonItem> viewableMapArea = new ArrayList<LatLonItem>();
                viewableMapArea.add(new LatLonItem(nePoint.getLatitude(), swPoint.getLongitude()));
                viewableMapArea.add(new LatLonItem(nePoint.getLatitude(), nePoint.getLongitude()));
                viewableMapArea.add(new LatLonItem(swPoint.getLatitude(), nePoint.getLongitude()));
                viewableMapArea.add(new LatLonItem(swPoint.getLatitude(), swPoint.getLongitude()));

                List<HighwayAlertItem> alerts = new ArrayList<HighwayAlertItem>();

                for (GenericRow alert : result) {
                    if (inPolygon(viewableMapArea, alert.getDouble(HighwayAlertsColumns.HIGHWAY_ALERT_LATITUDE),
                            alert.getDouble(HighwayAlertsColumns.HIGHWAY_ALERT_LONGITUDE))) {

                        alerts.add(new HighwayAlertItem(alert.getInt(HighwayAlertsColumns.HIGHWAY_ALERT_ID),
                                alert.getString(HighwayAlertsColumns.HIGHWAY_ALERT_CATEGORY),
                                alert.getString(HighwayAlertsColumns.HIGHWAY_ALERT_HEADLINE),
                                alert.getDouble(HighwayAlertsColumns.HIGHWAY_ALERT_LATITUDE),
                                alert.getDouble(HighwayAlertsColumns.HIGHWAY_ALERT_LONGITUDE),
                                alert.getString(HighwayAlertsColumns.HIGHWAY_ALERT_PRIORITY)));
                    }
                }

                if (!result.isEmpty()) {
                    view.drawAlerts(alerts);
                }
            }
        });
    }

    private void getRestAreas() {
        String jsonString = AppBundle.INSTANCE.restAreaData().getText();
        RestAreaFeed restAreas = JsonUtils.safeEval(jsonString);
        RestAreaItem item;

        for (int i = 0; i < restAreas.getRestAreas().length(); i++) {
            item = new RestAreaItem();
            item.setId(i);
            item.setRoute(restAreas.getRestAreas().get(i).getRoute());
            item.setLocation(restAreas.getRestAreas().get(i).getLocation());
            item.setDescription(restAreas.getRestAreas().get(i).getDescription());
            item.setMilepost(restAreas.getRestAreas().get(i).getMilepost());
            item.setDirection(restAreas.getRestAreas().get(i).getDirection());
            item.setLatitude(restAreas.getRestAreas().get(i).getLatitude());
            item.setLongitude(restAreas.getRestAreas().get(i).getLongitude());
            item.setNotes(restAreas.getRestAreas().get(i).getNotes());
            item.setHasDump(restAreas.getRestAreas().get(i).hasDump());
            item.setOpen(restAreas.getRestAreas().get(i).isOpen());
            item.setAmenities(restAreas.getRestAreas().get(i).getAmenities());
            restAreaItems.add(item);
        }
        drawRestAreasLayer();
    }

    private void drawRestAreasLayer() {

        LatLngBounds bounds = view.getViewportBounds();
        LatLng swPoint = bounds.getSouthWest();
        LatLng nePoint = bounds.getNorthEast();

        ArrayList<LatLonItem> viewableMapArea = new ArrayList<LatLonItem>();
        viewableMapArea.add(new LatLonItem(nePoint.getLatitude(), swPoint.getLongitude()));
        viewableMapArea.add(new LatLonItem(nePoint.getLatitude(), nePoint.getLongitude()));
        viewableMapArea.add(new LatLonItem(swPoint.getLatitude(), nePoint.getLongitude()));
        viewableMapArea.add(new LatLonItem(swPoint.getLatitude(), swPoint.getLongitude()));

        List<RestAreaItem> visibleRestAreas = new ArrayList<>();

        for (RestAreaItem restArea : restAreaItems) {
            if (inPolygon(viewableMapArea, Double.valueOf(restArea.getLatitude()),
                    Double.valueOf(restArea.getLongitude()))) {

                visibleRestAreas.add(restArea);
            }
        }

        if (!visibleRestAreas.isEmpty()) {
            view.drawRestAreas(visibleRestAreas);
        }
    }

    /**
     * 
     */
    private void getCallouts() {
        calloutItems.clear();

        CalloutItem item = new CalloutItem();
        item.setTitle("JBLM");
        item.setImageUrl("http://images.wsdot.wa.gov/traffic/flowmaps/jblm.png");
        item.setLatitude(47.103033);
        item.setLongitude(-122.584394);

        calloutItems.add(item);

        drawCalloutsLayer();
    }

    /**
     * 
     */
    private void drawCalloutsLayer() {
        LatLngBounds bounds = view.getViewportBounds();
        LatLng swPoint = bounds.getSouthWest();
        LatLng nePoint = bounds.getNorthEast();

        ArrayList<LatLonItem> viewableMapArea = new ArrayList<LatLonItem>();
        viewableMapArea.add(new LatLonItem(nePoint.getLatitude(), swPoint.getLongitude()));
        viewableMapArea.add(new LatLonItem(nePoint.getLatitude(), nePoint.getLongitude()));
        viewableMapArea.add(new LatLonItem(swPoint.getLatitude(), nePoint.getLongitude()));
        viewableMapArea.add(new LatLonItem(swPoint.getLatitude(), swPoint.getLongitude()));

        List<CalloutItem> callouts = new ArrayList<CalloutItem>();

        for (CalloutItem item : calloutItems) {
            if (inPolygon(viewableMapArea, item.getLatitude(), item.getLongitude())) {

                callouts.add(new CalloutItem(item.getTitle(), item.getImageUrl(), item.getLatitude(),
                        item.getLongitude()));
            }
        }

        if (!callouts.isEmpty()) {
            view.drawCallouts(callouts);
        }
    }

    /**
     * Iterate through collection of LatLon objects in ArrayList and see if
     * passed latitude and longitude point is within the collection.
     * 
     * @param points
     * @param latitude
     * @param longitude
     * @return
     */
    private boolean inPolygon(ArrayList<LatLonItem> points, double latitude, double longitude) {

        int j = points.size() - 1;
        double lat = latitude;
        double lon = longitude;
        boolean inPoly = false;

        for (int i = 0; i < points.size(); i++) {
            if ((points.get(i).getLongitude() < lon && points.get(j).getLongitude() >= lon)
                    || (points.get(j).getLongitude() < lon && points.get(i).getLongitude() >= lon)) {
                if (points.get(i).getLatitude() + (lon - points.get(i).getLongitude())
                        / (points.get(j).getLongitude() - points.get(i).getLongitude())
                        * (points.get(j).getLatitude() - points.get(i).getLatitude()) < lat) {
                    inPoly = !inPoly;
                }
            }
            j = i;
        }
        return inPoly;
    }

    @Override
    public void onStop() {
        view.setPresenter(null);
    }

    @Override
    public void onBackButtonPressed() {
        ActionEvent.fire(eventBus, ActionNames.BACK);
    }

    @Override
    public void onMenuButtonPressed() {
        if (Consts.ANALYTICS_ENABLED) {
            analytics.trackScreen("/Traffic Map/Menu");
        }
        clientFactory.getPlaceController().goTo(new TrafficMenuPlace());
    }

    @Override
    public void onCameraButtonPressed(boolean showCameras) {
        if (showCameras) {
            if (Consts.ANALYTICS_ENABLED) {
                analytics.trackEvent("Traffic", "Cameras", "Hide Cameras");
            }
            view.hideCameras();
        } else {
            if (Consts.ANALYTICS_ENABLED) {
                analytics.trackEvent("Traffic", "Cameras", "Show Cameras");
            }
            view.showCameras();
        }
    }

    @Override
    public void onCameraSelected(int cameraId) {
        if (Consts.ANALYTICS_ENABLED) {
            analytics.trackScreen("/Traffic Map/Cameras");
        }
        clientFactory.getPlaceController().goTo(new CameraPlace(Integer.toString(cameraId)));
    }

    @Override
    public void onAlertSelected(int alertId) {
        if (Consts.ANALYTICS_ENABLED) {
            analytics.trackScreen("/Traffic Map/Map Alert");
        }
        clientFactory.getPlaceController().goTo(new AlertPlace(Integer.toString(alertId)));
    }

    @Override
    public void onRestAreaSelected(int restAreaId) {
        if (Consts.ANALYTICS_ENABLED) {
            analytics.trackScreen("/Traffic Map/Rest Area");
        }
        clientFactory.getPlaceController().goTo(new RestAreaPlace(Integer.toString(restAreaId)));
    }

    @Override
    public void onCalloutSelected(String url) {
        if (Consts.ANALYTICS_ENABLED) {
            analytics.trackScreen("/Traffic Map/Callout");
        }
        clientFactory.getPlaceController().goTo(new CalloutPlace(url));
    }

    @Override
    public void onLocateButtonPressed() {
        if (Consts.ANALYTICS_ENABLED) {
            analytics.trackScreen("/Traffic Map/My Location");
        }
        phoneGap.getGeolocation().getCurrentPosition(new GeolocationCallback() {

            @Override
            public void onSuccess(Position position) {
                double latitude = position.getCoordinates().getLatitude();
                double longitude = position.getCoordinates().getLongitude();

                view.addMapMarker(position);

                view.setMapLocation(latitude, longitude, 12);
            }

            @Override
            public void onFailure(PositionError error) {
                switch (error.getCode()) {
                case PositionError.PERMISSION_DENIED:
                    phoneGap.getNotification().alert(
                            "You can turn Location Services on at Settings > Privacy > Location Services.",
                            new AlertCallback() {
                                @Override
                                public void onOkButtonClicked() {
                                    // TODO Auto-generated method stub
                                }
                            }, "Location Services Off");
                    break;
                default:
                    break;
                }

            }
        });
    }

    @Override
    public void onTrafficAlertsButtonPressed(LatLngBounds bounds) {
        // Check if map has loaded
        if (bounds != null) {
            if (Consts.ANALYTICS_ENABLED) {
                analytics.trackScreen("/Traffic Map/Alerts In This Area");
            }
            clientFactory.getPlaceController().goTo(new TrafficAlertsPlace(bounds));
        }
    }

    /**
     * Creates a prompt dialog to collect the new favorite location name
     * and confirm it's addition.
     */
    @Override
    public void onStarButtonPressed() {
        if (Consts.ANALYTICS_ENABLED) {
            analytics.trackScreen("/Traffic Map/Star Location");
        }

        // Collect Location information
        phoneGap.getNotification().prompt("Enter a name for this location.", new PromptCallback() {
            @Override
            public void onPrompt(PromptResults results) {
                if (results.getButtonIndex() == 2) {

                    LatLng center = view.getMapWidget().getCenter();
                    int zoom = view.getMapWidget().getZoom();

                    LocationItem locationItem = new LocationItem(results.getInput1(), center.getLatitude(),
                            center.getLongitude(), zoom);

                    // Add location item to Database
                    dbService.insertLocation(locationItem, new VoidCallback() {
                        @Override
                        public void onFailure(DataServiceException error) {
                            phoneGap.getNotification().alert("Location was not added to favorites",
                                    new AlertCallback() {
                                        @Override
                                        public void onOkButtonClicked() {
                                            // TODO Auto-generated method stub
                                        }
                                    }, "Failed");
                        }

                        @Override
                        public void onSuccess() {

                        }
                    });
                }
            }
        }, "New Favorite Location", "", new String[] { "Cancel", "Ok" });
    }

    @Override
    public void onMapIsIdle() {
        captureClickEvents();
        getCameras();
        getHighwayAlerts();
        getRestAreas();
        getCallouts();
    }

    @Override
    public void onRefreshMapButtonPressed() {
        view.refreshMap();
        // getViewportBounds() will return null in drawHighwayAlertsLayer()
        // before map has loaded on screen
        if (view.getViewportBounds() != null) {
            getHighwayAlerts();
        }
    }

    /**
    * JSNI method to capture click events and open urls in PhoneGap
    * InAppBrowser.
    * 
    * Tapping external links on the Google map like the Google logo and 'Terms
    * of Use' will cause those links to open in the same browser window as the
    * app with no way for the user to return to the app.
    * 
    * http://docs.phonegap.com/en/2.4.0/cordova_inappbrowser_inappbrowser.md.html
    */
    public static native void captureClickEvents() /*-{
                                                   var anchors = $doc.getElementsByTagName('a');
                                                   for ( var i = 0; i < anchors.length; i++) {
                                                   anchors[i].addEventListener('click', function(e) {
                                                   e.preventDefault();
                                                   $wnd.open(this.href, '_blank',
                                                   'location=yes,enableViewportScale=yes');
                                                   });
                                                   }
                                                   }-*/;
}