/** * 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.federations.impl; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.onebusaway.exceptions.MultipleServiceAreasServiceException; import org.onebusaway.exceptions.NoSuchAgencyServiceException; import org.onebusaway.exceptions.OutOfServiceAreaServiceException; import org.onebusaway.exceptions.ServiceAreaServiceException; import org.onebusaway.federations.FederatedService; import org.onebusaway.federations.FederatedServiceCollection; import org.onebusaway.geospatial.model.CoordinateBounds; import org.onebusaway.geospatial.model.CoordinatePoint; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.index.ItemVisitor; import com.vividsolutions.jts.index.strtree.STRtree; /** * A basic {@link FederatedServiceCollection} implementation that provides * methods for query a set of {@link FederatedService} instances. See the static * factory method {@link #create(List, Class)} for a convenient way of * constructing a {@link FederatedServiceCollectionImpl} from a set of * {@link FederatedService} instances of a particular type. * * @author bdferris * @see FederatedService * @see FederatedServiceCollection */ public class FederatedServiceCollectionImpl implements FederatedServiceCollection { private Set<FederatedService> _services; private Map<String, FederatedService> _servicesByAgencyId = new HashMap<String, FederatedService>(); private STRtree _tree; public static <T extends FederatedService> FederatedServiceCollectionImpl create( List<T> serviceProviders, Class<T> serviceInterface) { Map<FederatedService, Map<String, List<CoordinateBounds>>> map = FederatedServiceLibrary.getFederatedServiceAgencyCoverage( serviceProviders, serviceInterface); return new FederatedServiceCollectionImpl(map); } public FederatedServiceCollectionImpl( Map<FederatedService, Map<String, List<CoordinateBounds>>> services) { _services = Collections.unmodifiableSet(services.keySet()); _tree = new STRtree(); for (Map.Entry<FederatedService, Map<String, List<CoordinateBounds>>> entry : services.entrySet()) { FederatedService service = entry.getKey(); Map<String, List<CoordinateBounds>> agencyIdsAndCoverage = entry.getValue(); for (Map.Entry<String, List<CoordinateBounds>> acEntry : agencyIdsAndCoverage.entrySet()) { String agencyId = acEntry.getKey(); List<CoordinateBounds> coverage = acEntry.getValue(); _servicesByAgencyId.put(agencyId, service); for (CoordinateBounds rc : coverage) { Envelope env = new Envelope(rc.getMinLon(), rc.getMaxLon(), rc.getMinLat(), rc.getMaxLat()); _tree.insert(env, service); } } } _tree.build(); } public FederatedServiceCollectionImpl() { this(new HashMap<FederatedService, Map<String, List<CoordinateBounds>>>()); } /**** * {@link FederatedServiceCollection} Interface ****/ @Override public Set<FederatedService> getAllServices() { return _services; } @Override public FederatedService getServiceForAgencyId(String agencyId) throws ServiceAreaServiceException { FederatedService provider = _servicesByAgencyId.get(agencyId); if (provider == null) throw new NoSuchAgencyServiceException(agencyId); return provider; } @Override public FederatedService getServiceForAgencyIds(Iterable<String> agencyIds) throws ServiceAreaServiceException { Set<FederatedService> providers = new HashSet<FederatedService>(); for (String id : agencyIds) { FederatedService provider = getServiceForAgencyId(id); if (provider == null) throw new NoSuchAgencyServiceException(id); providers.add(provider); } return getProviderFromProviders(providers); } @Override public FederatedService getServiceForBounds(CoordinateBounds bounds) throws ServiceAreaServiceException { return getServiceForBounds(bounds.getMinLat(), bounds.getMinLon(), bounds.getMaxLat(), bounds.getMaxLon()); } @Override public FederatedService getServiceForBounds(double lat1, double lon1, double lat2, double lon2) throws ServiceAreaServiceException { Envelope rectangle = new Envelope(lon1, lon2, lat1, lat2); return getProviderForRectangle(rectangle); } @Override public FederatedService getServiceForLocation(double lat, double lon) throws ServiceAreaServiceException { return getServiceForBounds(lat, lon, lat, lon); } @Override public FederatedService getServiceForLocations(List<CoordinatePoint> points) throws ServiceAreaServiceException { FederatedService service = null; for (CoordinatePoint point : points) { FederatedService provider = getServiceForLocation(point.getLat(), point.getLon()); if (service == null) { service = provider; } else if (service != provider) { throw new MultipleServiceAreasServiceException(); } } if (service == null) throw new OutOfServiceAreaServiceException(); return service; } /**** * Private Methods ****/ private FederatedService getProviderForRectangle(Envelope env) throws ServiceAreaServiceException { ProviderCollector collector = new ProviderCollector(); if (_tree.size() != 0) _tree.query(env, collector); Set<FederatedService> providers = collector.getProviders(); return getProviderFromProviders(providers); } private FederatedService getProviderFromProviders( Set<FederatedService> providers) throws OutOfServiceAreaServiceException, MultipleServiceAreasServiceException { if (providers.size() == 1) return providers.iterator().next(); if (providers.size() == 0) throw new OutOfServiceAreaServiceException(); throw new MultipleServiceAreasServiceException(); } private class ProviderCollector implements ItemVisitor { private Set<FederatedService> _providers = new HashSet<FederatedService>(); public Set<FederatedService> getProviders() { return _providers; } @Override public void visitItem(Object item) { _providers.add((FederatedService) item); } } }