org.onebusaway.webapp.gwt.where_library.view.stops.TransitMapManager.java Source code

Java tutorial

Introduction

Here is the source code for org.onebusaway.webapp.gwt.where_library.view.stops.TransitMapManager.java

Source

/**
 * 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.stops;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.onebusaway.geospatial.model.CoordinateBounds;
import org.onebusaway.geospatial.model.EncodedPolylineBean;
import org.onebusaway.transit_data.model.RouteBean;
import org.onebusaway.transit_data.model.StopBean;
import org.onebusaway.transit_data.model.StopsForRouteBean;
import org.onebusaway.webapp.gwt.common.MapOverlayManager;
import org.onebusaway.webapp.gwt.common.control.Place;
import org.onebusaway.webapp.gwt.common.resources.map.StopIconFactory;
import org.onebusaway.webapp.gwt.common.resources.map.StopIconFactory.ESize;
import org.onebusaway.webapp.gwt.where_library.GeocoderAccuracyToBounds;
import org.onebusaway.webapp.gwt.where_library.impl.StopsForRegionServiceImpl;
import org.onebusaway.webapp.gwt.where_library.view.events.StopClickedEvent;
import org.onebusaway.webapp.gwt.where_library.view.events.StopClickedHandler;

import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.maps.client.MapWidget;
import com.google.gwt.maps.client.event.MapMoveEndHandler;
import com.google.gwt.maps.client.event.MarkerClickHandler;
import com.google.gwt.maps.client.geom.LatLng;
import com.google.gwt.maps.client.geom.LatLngBounds;
import com.google.gwt.maps.client.overlay.EncodedPolyline;
import com.google.gwt.maps.client.overlay.Icon;
import com.google.gwt.maps.client.overlay.Marker;
import com.google.gwt.maps.client.overlay.MarkerOptions;
import com.google.gwt.maps.client.overlay.Overlay;
import com.google.gwt.maps.client.overlay.Polyline;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.rpc.AsyncCallback;

public class TransitMapManager {

    private StopsForRegionServiceImpl _stopsForRegionService = new StopsForRegionServiceImpl();

    private Map<String, StopAndOverlays> _visibleStopsById = new HashMap<String, StopAndOverlays>();

    private StopsHandler _stopsHandler = new StopsHandler();

    private Set<String> _stopIdsToAlwaysShow = new HashSet<String>();

    private Set<String> _selectedStopIds = new HashSet<String>();

    private List<Overlay> _otherOverlays = new ArrayList<Overlay>();

    private boolean _showStopsInCurrentView = true;

    private MapWidget _map;

    private MapOverlayManager _manager;

    private HandlerManager _handlerManager;

    private RouteBean _selectedRoute = null;

    public TransitMapManager(MapWidget map) {
        _map = map;
        _manager = new MapOverlayManager(_map);

        _map.addMapMoveEndHandler(new MapMoveEndHandlerImpl());
    }

    public HandlerRegistration addStopClickedHandler(StopClickedHandler handler) {
        return ensureHandlers().addHandler(StopClickedEvent.TYPE, handler);
    }

    public MapWidget getMap() {
        return _map;
    }

    public RouteBean getSelectedRoute() {
        return _selectedRoute;
    }

    /****
     * Visualization Modes
     ****/

    public void showStopsInCurrentView() {
        reset(true);
    }

    public void showStopsAtLocation(LatLng point, int zoomLevel) {
        _map.setCenter(point, zoomLevel);
        reset(true);
    }

    public void showStops(List<StopBean> stops) {
        reset(false, stops);
    }

    public void showStopsForRoute(RouteBean route, StopsForRouteBean stopsForRoute, boolean centerViewOnRoute) {

        reset(false, stopsForRoute.getStops());
        _selectedRoute = route;

        for (EncodedPolylineBean path : stopsForRoute.getPolylines()) {

            EncodedPolyline epl = EncodedPolyline.newInstance(path.getPoints(), 32, path.getLevels(3), 4);
            epl.setColor("#4F64BA");
            epl.setWeight(3);
            epl.setOpacity(1.0);
            Polyline polyline = Polyline.fromEncoded(epl);
            _manager.addOverlay(polyline);
            _otherOverlays.add(polyline);
        }

        if (centerViewOnRoute) {
            System.out.println("center on route");
            // Center the map on the bounds of the route
            LatLngBounds bounds = LatLngBounds.newInstance();
            for (StopBean stop : stopsForRoute.getStops())
                bounds.extend(LatLng.newInstance(stop.getLat(), stop.getLon()));

            if (!bounds.isEmpty()) {
                int zoomLevel = _map.getBoundsZoomLevel(bounds);
                _map.setCenter(bounds.getCenter(), zoomLevel);
            }
        }
    }

    public void showStop(StopBean stop, boolean showStopsInCurrentView) {
        LatLng point = LatLng.newInstance(stop.getLat(), stop.getLon());
        _map.setCenter(point, 17);
        Set<String> selectedStopIds = new HashSet<String>();
        selectedStopIds.add(stop.getId());
        reset(showStopsInCurrentView, Arrays.asList(stop), selectedStopIds);
    }

    public void showPlace(Place place, boolean showStopsInCurrentView, PlaceClickHandler clickHandler) {
        System.out.println("accuracy=" + place.getAccuracy());
        int zoomLevel = GeocoderAccuracyToBounds.getZoomLevelForAccuracy(place.getAccuracy());
        _map.setCenter(place.getLocation(), zoomLevel);
        reset(true);
        addPlaceToMap(place, clickHandler);
    }

    public void showPlaces(List<Place> places, boolean showStopsInCurrentView, PlaceClickHandler clickHandler) {

        LatLngBounds bounds = LatLngBounds.newInstance();
        for (Place place : places)
            bounds.extend(place.getLocation());
        if (!bounds.isEmpty())
            _map.setCenter(bounds.getCenter(), _map.getBoundsZoomLevel(bounds));

        reset(showStopsInCurrentView);
        for (Place place : places)
            addPlaceToMap(place, clickHandler);
    }

    /****
     * Private Methods
     ****/

    private HandlerManager ensureHandlers() {
        return _handlerManager == null ? _handlerManager = new HandlerManager(this) : _handlerManager;
    }

    private void reset(boolean showStopsInCurrentView) {
        reset(showStopsInCurrentView, new ArrayList<StopBean>());
    }

    private void reset(boolean showStopsInCurrentView, List<StopBean> stopsToAlwaysShow) {
        reset(showStopsInCurrentView, stopsToAlwaysShow, new HashSet<String>());
    }

    private void reset(boolean showStopsInCurrentView, List<StopBean> stopsToAlwaysShow,
            Set<String> selectedStopIds) {

        _showStopsInCurrentView = showStopsInCurrentView;

        _stopIdsToAlwaysShow.clear();
        for (StopBean stop : stopsToAlwaysShow)
            _stopIdsToAlwaysShow.add(stop.getId());

        for (Overlay overlay : _otherOverlays)
            _manager.removeOverlay(overlay);
        _otherOverlays.clear();

        for (StopAndOverlays stopAndOverlay : _visibleStopsById.values()) {
            List<Overlay> overlays = stopAndOverlay.getOverlays();
            for (Overlay overlay : overlays)
                _manager.removeOverlay(overlay);
        }

        _visibleStopsById.clear();
        _selectedStopIds.clear();
        _selectedStopIds.addAll(selectedStopIds);

        _stopsHandler.onSuccess(stopsToAlwaysShow);

        _selectedRoute = null;

        refresh();
    }

    private void refresh() {

        if (_showStopsInCurrentView) {

            int zoom = _map.getZoomLevel();

            if (zoom < 17) {
                return;
            }

            final CoordinateBounds bounds = computeBounds();

            for (Iterator<Map.Entry<String, StopAndOverlays>> it = _visibleStopsById.entrySet().iterator(); it
                    .hasNext();) {
                Entry<String, StopAndOverlays> entry = it.next();
                StopAndOverlays stopAndOverlays = entry.getValue();
                StopBean stop = stopAndOverlays.getStop();
                if (!bounds.contains(stop.getLat(), stop.getLon())) {
                    it.remove();
                    for (Overlay overlay : stopAndOverlays.getOverlays())
                        _manager.removeOverlay(overlay);
                }
            }

            // Update the map
            DeferredCommand.addCommand(new Command() {
                @Override
                public void execute() {
                    _stopsForRegionService.getStopsForRegion(bounds, _stopsHandler);
                }
            });

        } else {

            for (StopAndOverlays stopAndOverlays : _visibleStopsById.values()) {
                StopBean stop = stopAndOverlays.getStop();
                if (_stopIdsToAlwaysShow.contains(stop.getId()))
                    continue;
                for (Overlay overlay : stopAndOverlays.getOverlays())
                    _manager.removeOverlay(overlay);
            }
            _visibleStopsById.keySet().retainAll(_stopIdsToAlwaysShow);
        }
    }

    private CoordinateBounds computeBounds() {
        LatLngBounds r = _map.getBounds();
        LatLng ne = r.getNorthEast();
        LatLng sw = r.getSouthWest();

        double latMin = sw.getLatitude();
        double lonMin = sw.getLongitude();
        double latMax = ne.getLatitude();
        double lonMax = ne.getLongitude();

        return new CoordinateBounds(latMin, lonMin, latMax, lonMax);
    }

    private MarkerClickHandler getClickHandlerForStop(final StopBean stop) {
        MarkerClickHandler clickHandler = new MarkerClickHandler() {
            public void onClick(MarkerClickEvent event) {
                ensureHandlers().fireEvent(new StopClickedEvent(stop));
            }
        };
        return clickHandler;
    }

    private Marker getStopMarker(final StopBean stop, final LatLng p, ESize size) {

        MarkerOptions opts = MarkerOptions.newInstance();
        boolean isSelected = false;

        Icon icon = StopIconFactory.getStopIcon(stop, size, isSelected);
        opts.setIcon(icon);
        return new Marker(p, opts);
    }

    private void addOverlayAtZoom(Overlay overlay, int from, int to) {
        _manager.addOverlay(overlay, from, to);
    }

    private void addPlaceToMap(final Place place, final PlaceClickHandler clickHandler) {
        LatLng point = place.getLocation();
        Marker marker = new Marker(point);

        if (clickHandler != null) {
            marker.addMarkerClickHandler(new MarkerClickHandler() {
                @Override
                public void onClick(MarkerClickEvent event) {
                    clickHandler.onPlaceClicked(place);
                }
            });
        }
        _manager.addOverlay(marker);
        _otherOverlays.add(marker);
    }

    /****
     * Internal Classes
     ****/

    private class MapMoveEndHandlerImpl implements MapMoveEndHandler {
        @Override
        public void onMoveEnd(MapMoveEndEvent event) {
            refresh();
        }
    }

    private class StopsHandler implements AsyncCallback<List<StopBean>> {

        public void onSuccess(List<StopBean> stops) {

            for (StopBean stop : stops) {

                if (_visibleStopsById.containsKey(stop.getId()))
                    continue;

                StopAndOverlays stopAndOverlays = new StopAndOverlays(stop);
                _visibleStopsById.put(stop.getId(), stopAndOverlays);

                LatLng p = LatLng.newInstance(stop.getLat(), stop.getLon());

                MarkerClickHandler clickHandler = getClickHandlerForStop(stop);

                // Show some close up stops by default
                Marker largerMarker = getStopMarker(stop, p, ESize.LARGE);

                largerMarker.addMarkerClickHandler(clickHandler);

                addOverlayAtZoom(largerMarker, 17, 20);

                stopAndOverlays.addOverlays(largerMarker);

                // If we're not showing stops in our current view, it must mean we're in
                // route view, so we show the stops more zoomed out
                if (!_showStopsInCurrentView) {

                    Marker mediumMarker = getStopMarker(stop, p, ESize.MEDIUM);
                    mediumMarker.addMarkerClickHandler(clickHandler);
                    addOverlayAtZoom(mediumMarker, 16, 17);

                    Marker smallMarker = getStopMarker(stop, p, ESize.SMALL);
                    smallMarker.addMarkerClickHandler(clickHandler);
                    addOverlayAtZoom(smallMarker, 13, 16);

                    Marker tinyMarker = getStopMarker(stop, p, ESize.TINY);
                    tinyMarker.addMarkerClickHandler(clickHandler);
                    addOverlayAtZoom(tinyMarker, 9, 13);

                    stopAndOverlays.addOverlays(mediumMarker, smallMarker, tinyMarker);
                }

                if (_selectedStopIds.contains(stop.getId())) {
                    Marker big = StopIconFactory.getStopSelectionCircle(p, true);
                    Marker small = StopIconFactory.getStopSelectionCircle(p, false);
                    addOverlayAtZoom(small, 16, 17);
                    addOverlayAtZoom(big, 17, 20);
                    stopAndOverlays.addOverlays(big, small);
                }
            }
        }

        @Override
        public void onFailure(Throwable arg0) {
            arg0.printStackTrace();
        }
    }

    private class StopAndOverlays {

        private StopBean _stop;

        private List<Overlay> _overlays = new ArrayList<Overlay>();

        public StopAndOverlays(StopBean stop) {
            _stop = stop;
        }

        public StopBean getStop() {
            return _stop;
        }

        public List<Overlay> getOverlays() {
            return _overlays;
        }

        public void addOverlays(Overlay... overlays) {
            for (Overlay overlay : overlays)
                _overlays.add(overlay);
        }
    }

}