org.onebusaway.webapp.gwt.common.control.PlaceSearch.java Source code

Java tutorial

Introduction

Here is the source code for org.onebusaway.webapp.gwt.common.control.PlaceSearch.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.common.control;

import org.onebusaway.utility.collections.TreeUnionFind;

import com.google.gwt.core.client.JsArray;
import com.google.gwt.maps.client.geocode.Geocoder;
import com.google.gwt.maps.client.geocode.LocationCallback;
import com.google.gwt.maps.client.geocode.Placemark;
import com.google.gwt.maps.client.geom.LatLngBounds;
import com.google.gwt.search.client.AddressLookupMode;
import com.google.gwt.search.client.LocalResult;
import com.google.gwt.search.client.LocalSearch;
import com.google.gwt.search.client.Result;
import com.google.gwt.search.client.SearchCompleteHandler;
import com.google.gwt.search.client.SearchCompleteHandler.SearchCompleteEvent;
import com.google.gwt.user.client.Timer;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;

public class PlaceSearch {

    private static final PlaceComparator _comparator = new PlaceComparator();

    private double _mergeDistance = 25.0;

    public void query(String query, PlaceSearchListener listener) {
        query(query, listener, null);
    }

    /**
     * Results with
     * 
     * @param mergeDistance in meters
     */
    public void setMergeDistance(double mergeDistance) {
        _mergeDistance = mergeDistance;
    }

    public void query(String query, PlaceSearchListener listener, LatLngBounds view) {

        final LocationHandler handler = new LocationHandler(listener, view);

        // Google Local Search
        LocalSearch search = new LocalSearch();

        search.setAddressLookupMode(AddressLookupMode.ENABLED);
        if (view != null)
            search.setCenterPoint(view.getCenter());
        search.addSearchCompleteHandler(handler);
        search.execute(query);

        // Google Maps Geocoder Search
        Geocoder geocoder = new Geocoder();
        if (view != null)
            geocoder.setViewport(view);
        geocoder.getLocations(query, handler);

        handler.scheduleRepeating(1000);
    }

    public static List<Place> getSeachCompleteEventAsPlaces(SearchCompleteEvent event, List<Place> places) {

        JsArray<? extends Result> results = event.getSearch().getResults();

        for (int i = 0; i < results.length(); i++) {
            Result result = results.get(i);
            if (result instanceof LocalResult) {
                LocalResult lsr = (LocalResult) result;
                places.add(new LocalResultPlaceImpl(lsr));
            }
        }

        return places;
    }

    public static List<Place> mergeNearbyEntries(List<Place> places, double mergeDistance) {

        TreeUnionFind<Place> unionFind = new TreeUnionFind<Place>();

        for (int i = 0; i < places.size(); i++) {

            Place place = places.get(i);
            unionFind.find(place);

            for (int j = i + 1; j < places.size(); j++) {
                Place other = places.get(j);
                double distance = place.getLocation().distanceFrom(other.getLocation());
                if (distance < mergeDistance)
                    unionFind.union(place, other);
            }
        }

        List<Place> reduced = new ArrayList<Place>();

        for (Set<Place> cluster : unionFind.getSetMembers()) {
            List<Place> go = new ArrayList<Place>(cluster);
            Collections.sort(go, _comparator);
            reduced.add(go.get(0));
        }

        return reduced;
    }

    private class LocationHandler extends Timer implements LocationCallback, SearchCompleteHandler {

        private List<Place> _places = new ArrayList<Place>();

        private int _count = 0;

        private boolean _flushed = false;

        private PlaceSearchListener _listener;

        private LatLngBounds _view;

        private boolean _seenFirstOnSearchComplete = false;

        public LocationHandler(PlaceSearchListener listener, LatLngBounds view) {
            _listener = listener;
            _view = view;
        }

        public void onSuccess(JsArray<Placemark> placemarks) {

            for (int i = 0; i < placemarks.length(); i++) {
                Placemark mark = placemarks.get(i);
                addPlace(new PlacemarkPlaceImpl(mark));
            }

            handleResult(false);
        }

        public void onFailure(int statusCode) {
            handleResult(true);
        }

        public void onSearchComplete(SearchCompleteEvent event) {

            if (_seenFirstOnSearchComplete)
                return;
            _seenFirstOnSearchComplete = true;

            List<Place> places = getSeachCompleteEventAsPlaces(event, new ArrayList<Place>());

            for (Place place : places)
                addPlace(place);

            handleResult(false);
        }

        private void addPlace(Place place) {

            if (place.getName() == null)
                return;

            if (_view != null && !_view.containsLatLng(place.getLocation()))
                return;

            _places.add(place);
        }

        private void handleResult(boolean isError) {
            _count++;

            if (_count >= 2)
                flush();
        }

        public void flush() {

            if (_flushed)
                return;

            _flushed = true;

            if (_mergeDistance > 0)
                _places = mergeNearbyEntries(_places, _mergeDistance);

            if (_places.isEmpty()) {
                _listener.handleNoResult();
            } else if (_places.size() == 1) {
                _listener.handleSingleResult(_places.get(0));
            } else {
                _listener.handleMultipleResults(_places);
            }
        }

        /****
         * {@link Timer} Interface
         ****/

        @Override
        public void run() {
            if (_count >= 2 || (_count > 0 && _places.size() > 0)) {
                this.cancel();
                flush();
            }
        }

    }

    private static class PlaceComparator implements Comparator<Place> {

        public int compare(Place o1, Place o2) {

            boolean placemarkA = o1 instanceof PlacemarkPlaceImpl;
            boolean placemarkB = o2 instanceof PlacemarkPlaceImpl;

            if (placemarkA && !placemarkB)
                return -1;

            if (placemarkB && !placemarkA)
                return 1;

            int accuracyA = o1.getAccuracy();
            int accuracyB = o2.getAccuracy();

            if (accuracyA < accuracyB)
                return -1;
            if (accuracyB < accuracyA)
                return 1;

            return 0;
        }
    }
}