Android Open Source - downtown Non Hierarchical Distance Based Algorithm






From Project

Back to project page downtown.

License

The source code is released under:

GNU General Public License

If you think the Android project downtown listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.google.maps.android.clustering.algo;
// w  w w .  jav a 2  s . c  om
import com.google.android.gms.maps.model.LatLng;
import com.google.maps.android.clustering.Cluster;
import com.google.maps.android.clustering.ClusterItem;
import com.google.maps.android.geometry.Bounds;
import com.google.maps.android.geometry.Point;
import com.google.maps.android.projection.SphericalMercatorProjection;
import com.google.maps.android.quadtree.PointQuadTree;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * A simple clustering algorithm with O(nlog n) performance. Resulting clusters are not
 * hierarchical.
 * <p/>
 * High level algorithm:<br>
 * 1. Iterate over items in the order they were added (candidate clusters).<br>
 * 2. Create a cluster with the center of the item. <br>
 * 3. Add all items that are within a certain distance to the cluster. <br>
 * 4. Move any items out of an existing cluster if they are closer to another cluster. <br>
 * 5. Remove those items from the list of candidate clusters.
 * <p/>
 * Clusters have the center of the first element (not the centroid of the items within it).
 */
public class NonHierarchicalDistanceBasedAlgorithm<T extends ClusterItem> implements Algorithm<T> {
    public static final int MAX_DISTANCE_AT_ZOOM = 100; // essentially 100 dp.

    /**
     * Any modifications should be synchronized on mQuadTree.
     */
    private final Collection<QuadItem<T>> mItems = new ArrayList<QuadItem<T>>();

    /**
     * Any modifications should be synchronized on mQuadTree.
     */
    private final PointQuadTree<QuadItem<T>> mQuadTree = new PointQuadTree<QuadItem<T>>(0, 1, 0, 1);

    private static final SphericalMercatorProjection PROJECTION = new SphericalMercatorProjection(1);

    @Override
    public void addItem(T item) {
        final QuadItem<T> quadItem = new QuadItem<T>(item);
        synchronized (mQuadTree) {
            mItems.add(quadItem);
            mQuadTree.add(quadItem);
        }
    }

    @Override
    public void addItems(Collection<T> items) {
        for (T item : items) {
            addItem(item);
        }
    }

    @Override
    public void clearItems() {
        synchronized (mQuadTree) {
            mItems.clear();
            mQuadTree.clear();
        }
    }

    @Override
    public void removeItem(T item) {
        // TODO: delegate QuadItem#hashCode and QuadItem#equals to its item.
        throw new UnsupportedOperationException("NonHierarchicalDistanceBasedAlgorithm.remove not implemented");
    }

    @Override
    public Set<? extends Cluster<T>> getClusters(double zoom) {
        final int discreteZoom = (int) zoom;

        final double zoomSpecificSpan = MAX_DISTANCE_AT_ZOOM / Math.pow(2, discreteZoom) / 256;

        final Set<QuadItem<T>> visitedCandidates = new HashSet<QuadItem<T>>();
        final Set<Cluster<T>> results = new HashSet<Cluster<T>>();
        final Map<QuadItem<T>, Double> distanceToCluster = new HashMap<QuadItem<T>, Double>();
        final Map<QuadItem<T>, StaticCluster<T>> itemToCluster = new HashMap<QuadItem<T>, StaticCluster<T>>();

        synchronized (mQuadTree) {
            for (QuadItem<T> candidate : mItems) {
                if (visitedCandidates.contains(candidate)) {
                    // Candidate is already part of another cluster.
                    continue;
                }

                Bounds searchBounds = createBoundsFromSpan(candidate.getPoint(), zoomSpecificSpan);
                Collection<QuadItem<T>> clusterItems;
                clusterItems = mQuadTree.search(searchBounds);
                if (clusterItems.size() == 1) {
                    // Only the current marker is in range. Just add the single item to the results.
                    results.add(candidate);
                    visitedCandidates.add(candidate);
                    distanceToCluster.put(candidate, 0d);
                    continue;
                }
                StaticCluster<T> cluster = new StaticCluster<T>(candidate.mClusterItem.getPosition());
                results.add(cluster);

                for (QuadItem<T> clusterItem : clusterItems) {
                    Double existingDistance = distanceToCluster.get(clusterItem);
                    double distance = distanceSquared(clusterItem.getPoint(), candidate.getPoint());
                    if (existingDistance != null) {
                        // Item already belongs to another cluster. Check if it's closer to this cluster.
                        if (existingDistance < distance) {
                            continue;
                        }
                        // Move item to the closer cluster.
                        itemToCluster.get(clusterItem).remove(clusterItem.mClusterItem);
                    }
                    distanceToCluster.put(clusterItem, distance);
                    cluster.add(clusterItem.mClusterItem);
                    itemToCluster.put(clusterItem, cluster);
                }
                visitedCandidates.addAll(clusterItems);
            }
        }
        return results;
    }

    @Override
    public Collection<T> getItems() {
        final List<T> items = new ArrayList<T>();
        synchronized (mQuadTree) {
            for (QuadItem<T> quadItem : mItems) {
                items.add(quadItem.mClusterItem);
            }
        }
        return items;
    }

    private double distanceSquared(Point a, Point b) {
        return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
    }

    private Bounds createBoundsFromSpan(Point p, double span) {
        // TODO: Use a span that takes into account the visual size of the marker, not just its
        // LatLng.
        double halfSpan = span / 2;
        return new Bounds(
                p.x - halfSpan, p.x + halfSpan,
                p.y - halfSpan, p.y + halfSpan);
    }

    private static class QuadItem<T extends ClusterItem> implements PointQuadTree.Item, Cluster<T> {
        private final T mClusterItem;
        private final Point mPoint;
        private final LatLng mPosition;
        private Set<T> singletonSet;

        private QuadItem(T item) {
            mClusterItem = item;
            mPosition = item.getPosition();
            mPoint = PROJECTION.toPoint(mPosition);
            singletonSet = Collections.singleton(mClusterItem);
        }

        @Override
        public Point getPoint() {
            return mPoint;
        }

        @Override
        public LatLng getPosition() {
            return mPosition;
        }

        @Override
        public Set<T> getItems() {
            return singletonSet;
        }

        @Override
        public int getSize() {
            return 1;
        }
    }
}




Java Source Code List

com.google.maps.android.BuildConfig.java
com.google.maps.android.BuildConfig.java
com.google.maps.android.MarkerManager.java
com.google.maps.android.MathUtil.java
com.google.maps.android.PolyUtil.java
com.google.maps.android.SphericalUtil.java
com.google.maps.android.clustering.ClusterItem.java
com.google.maps.android.clustering.ClusterManager.java
com.google.maps.android.clustering.Cluster.java
com.google.maps.android.clustering.algo.Algorithm.java
com.google.maps.android.clustering.algo.GridBasedAlgorithm.java
com.google.maps.android.clustering.algo.NonHierarchicalDistanceBasedAlgorithm.java
com.google.maps.android.clustering.algo.PreCachingAlgorithmDecorator.java
com.google.maps.android.clustering.algo.StaticCluster.java
com.google.maps.android.clustering.view.ClusterRenderer.java
com.google.maps.android.clustering.view.DefaultClusterRenderer.java
com.google.maps.android.geometry.Bounds.java
com.google.maps.android.geometry.Point.java
com.google.maps.android.projection.Point.java
com.google.maps.android.projection.SphericalMercatorProjection.java
com.google.maps.android.quadtree.PointQuadTree.java
com.google.maps.android.ui.BubbleIconFactory.java
com.google.maps.android.ui.IconGenerator.java
com.google.maps.android.ui.RotationLayout.java
com.google.maps.android.ui.SquareTextView.java
com.sothree.slidinguppanel.SlidingUpPanelLayout.java
com.sothree.slidinguppanel.library.BuildConfig.java
com.sothree.slidinguppanel.library.BuildConfig.java
org.dklisiaris.downtown.Addresses.java
org.dklisiaris.downtown.BuildConfig.java
org.dklisiaris.downtown.FavsActivity.java
org.dklisiaris.downtown.GlobalData.java
org.dklisiaris.downtown.Intro.java
org.dklisiaris.downtown.MainActivity.java
org.dklisiaris.downtown.Manifest.java
org.dklisiaris.downtown.MapActivity.java
org.dklisiaris.downtown.MoreActivity.java
org.dklisiaris.downtown.Products.java
org.dklisiaris.downtown.SearchActivity.java
org.dklisiaris.downtown.Search.java
org.dklisiaris.downtown.SingleListItem.java
org.dklisiaris.downtown.Subcategories.java
org.dklisiaris.downtown.SubcatsAndFilters.java
org.dklisiaris.downtown.Tabs.java
org.dklisiaris.downtown.TestActivity.java
org.dklisiaris.downtown.WebViewActivity.java
org.dklisiaris.downtown.Websites.java
org.dklisiaris.downtown.actionbar.ActionBar.java
org.dklisiaris.downtown.actionbar.ScrollingTextView.java
org.dklisiaris.downtown.adapters.AddressFilterAdapter.java
org.dklisiaris.downtown.adapters.CustomAdapter.java
org.dklisiaris.downtown.adapters.CustomStringAdapter.java
org.dklisiaris.downtown.adapters.CustomSuggestionsAdapter.java
org.dklisiaris.downtown.adapters.MultiSelectionAdapter.java
org.dklisiaris.downtown.adapters.SubcatsAdapter.java
org.dklisiaris.downtown.db.Banner.java
org.dklisiaris.downtown.db.Category.java
org.dklisiaris.downtown.db.Company.java
org.dklisiaris.downtown.db.DBHandler.java
org.dklisiaris.downtown.db.DBInterface.java
org.dklisiaris.downtown.db.Image.java
org.dklisiaris.downtown.db.InitData.java
org.dklisiaris.downtown.db.Keyword.java
org.dklisiaris.downtown.db.Mapping.java
org.dklisiaris.downtown.db.Product.java
org.dklisiaris.downtown.db.QueryBuilder.java
org.dklisiaris.downtown.downloader.DownloadTask.java
org.dklisiaris.downtown.downloader.NotificationHelper.java
org.dklisiaris.downtown.helper.AccessAssets.java
org.dklisiaris.downtown.helper.AlertDialogManager.java
org.dklisiaris.downtown.helper.ConnectionDetector.java
org.dklisiaris.downtown.helper.FileCache.java
org.dklisiaris.downtown.helper.ImageLoader.java
org.dklisiaris.downtown.helper.InfoHelper.java
org.dklisiaris.downtown.helper.KeyboardUtil.java
org.dklisiaris.downtown.helper.MemoryCache.java
org.dklisiaris.downtown.helper.ShareHelper.java
org.dklisiaris.downtown.helper.UpdateConfirmDialog.java
org.dklisiaris.downtown.helper.UpdateHelper.java
org.dklisiaris.downtown.helper.Utils.java
org.dklisiaris.downtown.helper.XMLParser.java
org.dklisiaris.downtown.maps.AbstractMapActivity.java
org.dklisiaris.downtown.maps.CompanyMarker.java
org.dklisiaris.downtown.maps.DirectionsInfo.java
org.dklisiaris.downtown.maps.GMapV2Direction.java
org.dklisiaris.downtown.maps.Nearby.java
org.dklisiaris.downtown.maps.PopupAdapter.java
org.dklisiaris.downtown.providers.KeywordContract.java
org.dklisiaris.downtown.providers.KeywordProvider.java
org.dklisiaris.downtown.widgets.AspectRatioImageView.java
org.dklisiaris.downtown.widgets.CheckableRelativeLayout.java
org.dklisiaris.downtown.widgets.CustomScrollView.java
org.dklisiaris.downtown.widgets.FlipAnimator.java
org.dklisiaris.downtown.widgets.InertCheckBox.java
org.dklisiaris.downtown.widgets.MultiSpinner.java