/* * 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.ferries.schedules.departures; 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.GWT; import com.google.gwt.i18n.client.DateTimeFormat; import com.google.gwt.i18n.client.TimeZone; import com.google.gwt.i18n.client.TimeZoneInfo; import com.google.gwt.i18n.client.constants.TimeZoneConstants; import com.google.gwt.json.client.*; import com.google.gwt.jsonp.client.JsonpRequestBuilder; import com.google.gwt.place.shared.Place; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.AcceptsOneWidget; import com.google.web.bindery.event.shared.EventBus; import com.googlecode.gwtphonegap.client.PhoneGap; import com.googlecode.gwtphonegap.client.notification.AlertCallback; import com.googlecode.mgwt.mvp.client.MGWTAbstractActivity; import com.googlecode.mgwt.ui.client.widget.panel.pull.PullArrowStandardHandler; import com.googlecode.mgwt.ui.client.widget.panel.pull.PullArrowStandardHandler.PullActionHandler; import gov.wa.wsdot.mobile.client.ClientFactory; import gov.wa.wsdot.mobile.client.activities.camera.CameraPlace; 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.FerriesSchedulesColumns; import gov.wa.wsdot.mobile.client.service.WSDOTContract.FerriesTerminalSailingSpaceColumns; 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.*; public class FerriesRouteDeparturesActivity extends MGWTAbstractActivity implements FerriesRouteDeparturesView.Presenter { private final ClientFactory clientFactory; private FerriesRouteDeparturesView view; private EventBus eventBus; private PhoneGap phoneGap; private Analytics analytics; private Accessibility accessibility; private WSDOTDataService dbService; private static ArrayList<String> daysOfWeek = new ArrayList<String>(); private static ArrayList<FerriesScheduleDateItem> scheduleDateItems = new ArrayList<FerriesScheduleDateItem>(); private static List<FerriesTerminalSailingSpaceItem> ferriesTerminalSailingSpaceItems = new ArrayList<FerriesTerminalSailingSpaceItem>(); private static Map<Integer, FerriesTerminalItem> ferriesTerminalMap = new HashMap<Integer, FerriesTerminalItem>(); private static List<Integer> starred = new ArrayList<Integer>(); private static List<CameraItem> cameraItems = new ArrayList<CameraItem>(); private static String routeId; private static int sailingsIndex; private static int terminalId; private static final String TERMINAL_SAILING_SPACE_URL = Consts.HOST_URL + "/traveler/api/ferries/terminalsailingspace"; private static final String CAMERAS_URL = Consts.HOST_URL + "/traveler/api/cameras"; private static int lastTab = 0; public FerriesRouteDeparturesActivity(ClientFactory clientFactory) { this.clientFactory = clientFactory; } @Override public void start(AcceptsOneWidget panel, final EventBus eventBus) { view = clientFactory.getFerriesRouteDeparturesView(); dbService = clientFactory.getDbService(); phoneGap = clientFactory.getPhoneGap(); analytics = clientFactory.getAnalytics(); accessibility = clientFactory.getAccessibility(); this.eventBus = eventBus; view.setPresenter(this); view.getPullHeader().setHTML("pull down"); PullArrowStandardHandler headerHandler = new PullArrowStandardHandler( view.getPullHeader(), view.getPullPanel()); headerHandler.setErrorText("Error"); headerHandler.setLoadingText("Loading"); headerHandler.setNormalText("pull down"); headerHandler.setPulledText("release to load"); headerHandler.setPullActionHandler(new PullActionHandler() { @Override public void onPullAction(final AsyncCallback<Void> callback) { new Timer() { @Override public void run() { createDepartureTimesList(routeId, 0, sailingsIndex); view.refresh(); callback.onSuccess(null); } }.schedule(1); } }); createTerminalLocations(); if (Consts.ANALYTICS_ENABLED) { analytics.trackScreen("/Ferries/Schedules/Sailings/Departures"); } Place place = clientFactory.getPlaceController().getWhere(); if (place instanceof FerriesRouteDeparturesPlace) { FerriesRouteDeparturesPlace ferriesRouteSchedulesDayDeparturesPlace = (FerriesRouteDeparturesPlace) place; routeId = ferriesRouteSchedulesDayDeparturesPlace.getId(); sailingsIndex = ferriesRouteSchedulesDayDeparturesPlace.getIndex(); terminalId = ferriesRouteSchedulesDayDeparturesPlace.getTerminalId(); view.setHeaderPullHandler(headerHandler); createDepartureTimesList(routeId, 0, sailingsIndex); panel.setWidget(view); accessibility.postScreenChangeNotification(); } } private void createTerminalLocations() { ferriesTerminalMap.put(1, new FerriesTerminalItem(1, "Anacortes", 48.507351, -122.677)); ferriesTerminalMap.put(3, new FerriesTerminalItem(3, "Bainbridge Island", 47.622339, -122.509617)); ferriesTerminalMap.put(4, new FerriesTerminalItem(4, "Bremerton", 47.561847, -122.624089)); ferriesTerminalMap.put(5, new FerriesTerminalItem(5, "Clinton", 47.9754, -122.349581)); ferriesTerminalMap.put(11, new FerriesTerminalItem(11, "Coupeville", 48.159008, -122.672603)); ferriesTerminalMap.put(8, new FerriesTerminalItem(8, "Edmonds", 47.813378, -122.385378)); ferriesTerminalMap.put(9, new FerriesTerminalItem(9, "Fauntleroy", 47.5232, -122.3967)); ferriesTerminalMap.put(10, new FerriesTerminalItem(10, "Friday Harbor", 48.535783, -123.013844)); ferriesTerminalMap.put(12, new FerriesTerminalItem(12, "Kingston", 47.794606, -122.494328)); ferriesTerminalMap.put(13, new FerriesTerminalItem(13, "Lopez Island", 48.570928, -122.882764)); ferriesTerminalMap.put(14, new FerriesTerminalItem(14, "Mukilteo", 47.949544, -122.304997)); ferriesTerminalMap.put(15, new FerriesTerminalItem(15, "Orcas Island", 48.597333, -122.943494)); ferriesTerminalMap.put(16, new FerriesTerminalItem(16, "Point Defiance", 47.306519, -122.514053)); ferriesTerminalMap.put(17, new FerriesTerminalItem(17, "Port Townsend", 48.110847, -122.759039)); ferriesTerminalMap.put(7, new FerriesTerminalItem(7, "Seattle", 47.602501, -122.340472)); ferriesTerminalMap.put(18, new FerriesTerminalItem(18, "Shaw Island", 48.584792, -122.92965)); ferriesTerminalMap.put(19, new FerriesTerminalItem(19, "Sidney B.C.", 48.643114, -123.396739)); ferriesTerminalMap.put(20, new FerriesTerminalItem(20, "Southworth", 47.513064, -122.495742)); ferriesTerminalMap.put(21, new FerriesTerminalItem(21, "Tahlequah", 47.331961, -122.507786)); ferriesTerminalMap.put(22, new FerriesTerminalItem(22, "Vashon Island", 47.51095, -122.463639)); } @Override public void onDayOfWeekSelected(int position) { createDepartureTimesList(routeId, position, sailingsIndex); } private void createDepartureTimesList(String routeId, final int dayIndex, final int sailingsIndex) { dbService.getFerriesSchedulesRoute(routeId, new ListCallback<GenericRow>() { @Override public void onFailure(DataServiceException error) { } @Override public void onSuccess(List<GenericRow> result) { scheduleDateItems.clear(); daysOfWeek.clear(); FerriesScheduleDateItem scheduleDate; FerriesTerminalItem terminal; FerriesAnnotationsItem notes; FerriesScheduleTimesItem timesItem; Date now = new Date(); view.showProgressIndicator(); JSONValue value = JSONParser.parseStrict(result.get(0).getString(FerriesSchedulesColumns.FERRIES_SCHEDULE_DATE)); JSONArray dates = value.isArray(); int numDates = dates.size(); for (int i = 0; i < numDates; i++) { scheduleDate = new FerriesScheduleDateItem(); JSONObject date = dates.get(i).isObject(); scheduleDate.setDate(date.get("Date").isString().stringValue().substring(6, 19)); JSONArray sailings = date.get("Sailings").isArray(); int numSailings = sailings.size(); for (int j=0; j < numSailings; j++) { terminal = new FerriesTerminalItem(); JSONObject sailing = sailings.get(j).isObject(); terminal.setArrivingTerminalID(Integer.parseInt(sailing.get("ArrivingTerminalID").toString())); terminal.setArrivingTerminalName(sailing.get("ArrivingTerminalName").isString().stringValue()); terminal.setDepartingTerminalID(Integer.parseInt(sailing.get("DepartingTerminalID").toString())); terminal.setDepartingTerminalName(sailing.get("DepartingTerminalName").isString().stringValue()); JSONArray annotations = sailing.get("Annotations").isArray(); int numAnnotations = annotations.size(); for (int k = 0; k < numAnnotations; k++) { notes = new FerriesAnnotationsItem(); notes.setAnnotation(annotations.get(k).isString().stringValue()); terminal.setAnnotations(notes); } JSONArray times = sailing.get("Times").isArray(); int numTimes = times.size(); for (int l = 0; l < numTimes; l++) { JSONObject time = times.get(l).isObject(); // Don't display past sailing times. Doesn't make sense. if (now.after(new Date(Long.parseLong(time .get("DepartingTime").isString().stringValue() .substring(6, 19))))) { continue; } timesItem = new FerriesScheduleTimesItem(); timesItem.setDepartingTime(time.get("DepartingTime").isString().stringValue().substring(6, 19)); try { timesItem.setArrivingTime(time.get("ArrivingTime").isString().stringValue().substring(6, 19)); } catch (Exception e) { timesItem.setArrivingTime("N/A"); } JSONArray annotationIndexes = time.get("AnnotationIndexes").isArray(); int numIndexes = annotationIndexes.size(); String annotation = ""; for (int m=0; m < numIndexes; m++) { FerriesAnnotationsItem p = terminal.getAnnotations().get(Integer.parseInt(annotationIndexes.get(m).toString())); annotation += p.getAnnotation(); timesItem.setAnnotations(annotation); } terminal.setScheduleTimes(timesItem); } scheduleDate.setFerriesTerminalItem(terminal); } scheduleDateItems.add(scheduleDate); } String terminalTitle = scheduleDateItems.get(0) .getFerriesTerminalItem().get(sailingsIndex) .getDepartingTerminalName() + " to " + scheduleDateItems.get(0) .getFerriesTerminalItem() .get(sailingsIndex) .getArrivingTerminalName(); int numItems = scheduleDateItems.size(); for (int i=0; i < numItems; i++) { // Don't display date in picker if there are no sailings. if (!scheduleDateItems.get(i).getFerriesTerminalItem().isEmpty()) { daysOfWeek.add(scheduleDateItems.get(i).getDate()); } } view.setTitle(terminalTitle); view.renderDaysOfWeek(daysOfWeek); view.setDayOfWeekSelected(dayIndex); createFerryTerminalCameras(); } }); } protected void createFerryTerminalSailingSpaces() { /** * 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.FERRIES_TERMINAL_SAILING_SPACE, new ListCallback<GenericRow>() { @Override public void onFailure(DataServiceException error) { } @Override public void onSuccess(List<GenericRow> result) { boolean shouldUpdate = true; view.showProgressIndicator(); if (!result.isEmpty()) { double now = System.currentTimeMillis(); double lastUpdated = result.get(0).getDouble(CachesColumns.CACHE_LAST_UPDATED); shouldUpdate = (Math.abs(now - lastUpdated) > (15 * 1000)); // Refresh every 15 seconds. } if (shouldUpdate) { JsonpRequestBuilder jsonp = new JsonpRequestBuilder(); // Set timeout for 30 seconds (30000 milliseconds) jsonp.setTimeout(30000); jsonp.requestObject( TERMINAL_SAILING_SPACE_URL, new AsyncCallback<FerriesTerminalSailingSpaceFeed>() { @Override public void onFailure(Throwable caught) { view.hideProgressIndicator(); } @Override public void onSuccess(FerriesTerminalSailingSpaceFeed result) { if (result.getTerminals() != null) { DateTimeFormat dateFormat = DateTimeFormat.getFormat("MMMM d, yyyy h:mm a"); ferriesTerminalSailingSpaceItems.clear(); FerriesTerminalSailingSpaceItem item; int numItems = result.getTerminals().length(); for (int i = 0; i < numItems; i++) { item = new FerriesTerminalSailingSpaceItem(); item.setTerminalId(result.getTerminals().get(i).getTerminalId()); item.setTerminalName(result.getTerminals().get(i).getTerminalName()); item.setTerminalAbbrev(result.getTerminals().get(i).getTerminalAbbrev()); item.setTerminalDepartingSpaces(new JSONArray(result.getTerminals().get(i).getDepartingSpaces()).toString()); item.setLastUpdated(dateFormat.format(new Date(System.currentTimeMillis()))); ferriesTerminalSailingSpaceItems.add(item); } // Purge existing terminal items covered by incoming data dbService.deleteFerriesTerminalSailingSpace(new VoidCallback() { @Override public void onFailure(DataServiceException error) { } @Override public void onSuccess() { // Bulk insert all the new ferries terminal sailing space items dbService.insertFerriesTerminalSailingSpace(ferriesTerminalSailingSpaceItems, 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.FERRIES_TERMINAL_SAILING_SPACE, System.currentTimeMillis())); dbService.updateCachesTable(cacheItems, new VoidCallback() { @Override public void onFailure(DataServiceException error) { } @Override public void onSuccess() { // Get available drive-up vehicle spaces. dbService.getFerriesTerminalSailingSpace( scheduleDateItems .get(view.getDayOfWeekSelected()) .getFerriesTerminalItem() .get(sailingsIndex) .getDepartingTerminalID().toString(), new ListCallback<GenericRow>() { @Override public void onFailure(DataServiceException error) { } @Override public void onSuccess(List<GenericRow> result) { getFerryTerminalSailingSpaces(result); } }); } }); } }); } }); } } }); } else { // Get available drive-up vehicle spaces. dbService.getFerriesTerminalSailingSpace( scheduleDateItems .get(view.getDayOfWeekSelected()) .getFerriesTerminalItem() .get(sailingsIndex) .getDepartingTerminalID().toString(), new ListCallback<GenericRow>() { @Override public void onFailure(DataServiceException error) { } @Override public void onSuccess(List<GenericRow> result) { getFerryTerminalSailingSpaces(result); } }); } } }); } private void getFerryTerminalSailingSpaces(List<GenericRow> result) { if (result.size() != 0) { JSONValue value = JSONParser.parseStrict(result.get(0).getString(FerriesTerminalSailingSpaceColumns.TERMINAL_DEPARTING_SPACES)); JSONArray departingSpaces = value.isArray(); List<FerriesScheduleTimesItem> times = scheduleDateItems.get(view.getDayOfWeekSelected()).getFerriesTerminalItem().get(sailingsIndex).getScheduleTimes(); DateTimeFormat dateFormat = DateTimeFormat.getFormat("MMMM d, yyyy h:mm a"); final TimeZoneConstants timeZoneConstants = GWT.create(TimeZoneConstants.class); final TimeZone usPacific = TimeZone.createTimeZone(TimeZoneInfo.buildTimeZoneData(timeZoneConstants.americaLosAngeles())); for (int i = 0; i < departingSpaces.size(); i++) { JSONObject spaces = departingSpaces.get(i).isObject(); String departure = dateFormat.format(new Date(Long.parseLong(spaces.get("Departure").toString().substring(7, 20)))); JSONArray spaceForArrivalTerminals = spaces.get("SpaceForArrivalTerminals").isArray(); for (int j=0; j < spaceForArrivalTerminals.size(); j++) { JSONObject terminals = spaceForArrivalTerminals.get(j).isObject(); JSONArray arrivalTerminalIDs = terminals.get("ArrivalTerminalIDs").isArray(); if (Integer.parseInt(terminals.get("TerminalID").toString()) == scheduleDateItems .get(view.getDayOfWeekSelected()) .getFerriesTerminalItem().get(sailingsIndex) .getArrivingTerminalID()) { JSONBoolean displayDriveUpSpace = terminals.get("DisplayDriveUpSpace").isBoolean(); boolean showIndicator = displayDriveUpSpace.booleanValue(); if (showIndicator) { int driveUpSpaceCount = Integer.parseInt(terminals.get("DriveUpSpaceCount").toString()); int maxSpaceCount = Integer.parseInt(terminals.get("MaxSpaceCount").toString()); for (FerriesScheduleTimesItem time : times) { if (dateFormat.format(new Date(Long.parseLong(time.getDepartingTime()))).equals(departure)) { time.setDriveUpSpaceCount(driveUpSpaceCount); time.setMaxSpaceCount(maxSpaceCount); time.setLastUpdated(result.get(0).getString(FerriesTerminalSailingSpaceColumns.TERMINAL_LAST_UPDATED)); } } } } for (int k=0; k < arrivalTerminalIDs.size(); k++) { if (Integer.parseInt(arrivalTerminalIDs.get(k) .toString()) != scheduleDateItems .get(view.getDayOfWeekSelected()) .getFerriesTerminalItem().get(sailingsIndex) .getArrivingTerminalID()) { continue; } else { JSONBoolean displayDriveUpSpace = terminals.get("DisplayDriveUpSpace").isBoolean(); boolean showIndicator = displayDriveUpSpace.booleanValue(); if (showIndicator) { int driveUpSpaceCount = Integer.parseInt(terminals.get("DriveUpSpaceCount").toString()); int maxSpaceCount = Integer.parseInt(terminals.get("MaxSpaceCount").toString()); for (FerriesScheduleTimesItem time : times) { if (dateFormat.format(new Date(Long.parseLong(time.getDepartingTime()))).equals(departure)) { time.setDriveUpSpaceCount(driveUpSpaceCount); time.setMaxSpaceCount(maxSpaceCount); time.setLastUpdated(result.get(0).getString(FerriesTerminalSailingSpaceColumns.TERMINAL_LAST_UPDATED)); } } } k = arrivalTerminalIDs.size(); } } } } } view.hideProgressIndicator(); view.render(scheduleDateItems .get(view.getDayOfWeekSelected()) .getFerriesTerminalItem().get(sailingsIndex) .getScheduleTimes()); view.refresh(); accessibility.postScreenChangeNotification(); } private void createFerryTerminalCameras() { /** * 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() { getFerryTerminalCameras(); } }); } }); } }); } } }); } }); } else { getFerryTerminalCameras(); } } }); } private void getFerryTerminalCameras() { dbService.getCameras(new ListCallback<GenericRow>() { @Override public void onFailure(DataServiceException error) { } @Override public void onSuccess(List<GenericRow> result) { cameraItems.clear(); int numRows = result.size(); view.showProgressIndicator(); for (int i = 0; i < numRows; i++) { if (result.get(i).getString(CamerasColumns.CAMERA_ROAD_NAME).toLowerCase() == "ferries") { int distance = getDistanceFromTerminal(terminalId, result.get(i).getDouble(CamerasColumns.CAMERA_LATITUDE), result.get(i).getDouble(CamerasColumns.CAMERA_LONGITUDE)); // If less than a mile from terminal, show the camera if (distance < 5280) { // in feet CameraItem camera = new CameraItem(); camera.setCameraId(result.get(i).getInt(CamerasColumns.CAMERA_ID)); camera.setTitle(result.get(i).getString(CamerasColumns.CAMERA_TITLE)); camera.setImageUrl(result.get(i).getString(CamerasColumns.CAMERA_URL)); camera.setLatitude(result.get(i).getDouble(CamerasColumns.CAMERA_LATITUDE)); camera.setLongitude(result.get(i).getDouble(CamerasColumns.CAMERA_LONGITUDE)); camera.setHasVideo(result.get(i).getInt(CamerasColumns.CAMERA_HAS_VIDEO)); camera.setDistance(distance); cameraItems.add(camera); } } } // If we've already removed the tab don't try and do it again if (cameraItems.size() == 0 && view.getTabCount() != 1) { view.removeTab(1); } else { Collections.sort(cameraItems, CameraItem.cameraDistanceComparator); view.renderCameras(cameraItems); view.refreshCameras(); } createFerryTerminalSailingSpaces(); } }); } @Override public void onCameraSelected(int index) { CameraItem item = cameraItems.get(index); clientFactory.getPlaceController().goTo( new CameraPlace(Integer.toString(item.getCameraId()))); } @Override public void onTabSelected(int index) { int currentTab = index; switch(currentTab) { case 0: if (currentTab != lastTab){ if (Consts.ANALYTICS_ENABLED) { analytics.trackScreen("/Ferries/Schedules/Sailings/Departures"); } } break; case 1: if (currentTab != lastTab) { if (Consts.ANALYTICS_ENABLED) { analytics.trackScreen("/Ferries/Schedules/Sailings/Cameras"); } } break; default: } lastTab = currentTab; } /** * Haversine formula * * Provides great-circle distances between two points on a sphere from * their longitudes and latitudes. * * http://en.wikipedia.org/wiki/Haversine_formula * * @param latitude * @param longitude */ protected int getDistanceFromTerminal(int terminalId, double latitude, double longitude) { FerriesTerminalItem terminal = ferriesTerminalMap.get(terminalId); double earthRadius = 20902200; // feet double dLat = Math.toRadians(terminal.getLatitude() - latitude); double dLng = Math.toRadians(terminal.getLongitude() - longitude); double sindLat = Math.sin(dLat / 2); double sindLng = Math.sin(dLng / 2); double a = Math.pow(sindLat, 2) + Math.pow(sindLng, 2) * Math.cos(Math.toRadians(latitude)) * Math.cos(Math.toRadians(terminal.getLatitude())); double c = 2 * Math.asin(Math.sqrt(a)); int distance = (int) Math.round(earthRadius * c); return distance; } @Override public void onStop() { view.setPresenter(null); } @Override public void onBackButtonPressed() { ActionEvent.fire(eventBus, ActionNames.BACK); } }