Search in sources :

Example 6 with Bounds

use of com.google.maps.android.geometry.Bounds in project android-maps-utils by googlemaps.

the class PointQuadTreeTest method testVeryDeepTree.

/**
     * Tests 30,000 items at the same point.
     * Timing results are averaged.
     */
public void testVeryDeepTree() {
    for (int i = 0; i < 30000; i++) {
        mTree.add(new Item(0, 0));
    }
    assertEquals(30000, searchAll().size());
    assertEquals(30000, mTree.search(new Bounds(0, .1, 0, .1)).size());
    assertEquals(0, mTree.search(new Bounds(.1, 1, .1, 1)).size());
    mTree.clear();
}
Also used : Bounds(com.google.maps.android.geometry.Bounds) Point(com.google.maps.android.geometry.Point)

Example 7 with Bounds

use of com.google.maps.android.geometry.Bounds in project android-maps-utils by googlemaps.

the class PointQuadTreeTest method testManyPoints.

/**
     * Tests 400,000 points relatively uniformly distributed across the space.
     * Timing results are averaged.
     */
public void testManyPoints() {
    for (double i = 0; i < 200; i++) {
        for (double j = 0; j < 2000; j++) {
            mTree.add(new Item(i / 200.0, j / 2000.0));
        }
    }
    // searching bounds that are exact subtrees of the main quadTree
    assertEquals(400000, searchAll().size());
    assertEquals(100000, mTree.search(new Bounds(0, .5, 0, .5)).size());
    assertEquals(100000, mTree.search(new Bounds(.5, 1, 0, .5)).size());
    assertEquals(25000, mTree.search(new Bounds(0, .25, 0, .25)).size());
    assertEquals(25000, mTree.search(new Bounds(.75, 1, .75, 1)).size());
    // searching bounds that do not line up with main quadTree
    assertEquals(399800, mTree.search(new Bounds(0, 0.999, 0, 0.999)).size());
    assertEquals(4221, mTree.search(new Bounds(0.8, 0.9, 0.8, 0.9)).size());
    assertEquals(4200, mTree.search(new Bounds(0, 1, 0, 0.01)).size());
    assertEquals(16441, mTree.search(new Bounds(0.4, 0.6, 0.4, 0.6)).size());
    // searching bounds that are small / have very exact end points
    assertEquals(1, mTree.search(new Bounds(0, .001, 0, .0001)).size());
    assertEquals(26617, mTree.search(new Bounds(0.356, 0.574, 0.678, 0.987)).size());
    assertEquals(44689, mTree.search(new Bounds(0.123, 0.456, 0.456, 0.789)).size());
    assertEquals(4906, mTree.search(new Bounds(0.111, 0.222, 0.333, 0.444)).size());
    mTree.clear();
    assertEquals(0, searchAll().size());
}
Also used : Bounds(com.google.maps.android.geometry.Bounds)

Example 8 with Bounds

use of com.google.maps.android.geometry.Bounds in project android-maps-utils by googlemaps.

the class PointQuadTreeTest method testRandomPoints.

/**
     * Runs a test with 100,000 points.
     * Timing results are averaged.
     */
public void testRandomPoints() {
    Random random = new Random();
    for (int i = 0; i < 100000; i++) {
        mTree.add(new Item(random.nextDouble(), random.nextDouble()));
    }
    searchAll();
    mTree.search(new Bounds(0, 0.5, 0, 0.5));
    mTree.search(new Bounds(0, 0.25, 0, 0.25));
    mTree.search(new Bounds(0, 0.125, 0, 0.125));
    mTree.search(new Bounds(0, 0.999, 0, 0.999));
    mTree.search(new Bounds(0, 1, 0, 0.01));
    mTree.search(new Bounds(0.4, 0.6, 0.4, 0.6));
    mTree.search(new Bounds(0.356, 0.574, 0.678, 0.987));
    mTree.search(new Bounds(0.123, 0.456, 0.456, 0.789));
    mTree.search(new Bounds(0.111, 0.222, 0.333, 0.444));
    mTree.clear();
}
Also used : Random(java.util.Random) Bounds(com.google.maps.android.geometry.Bounds) Point(com.google.maps.android.geometry.Point)

Example 9 with Bounds

use of com.google.maps.android.geometry.Bounds in project android-maps-utils by googlemaps.

the class NonHierarchicalDistanceBasedAlgorithm method getClusters.

@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;
}
Also used : HashMap(java.util.HashMap) Bounds(com.google.maps.android.geometry.Bounds) Cluster(com.google.maps.android.clustering.Cluster) Point(com.google.maps.android.geometry.Point) HashSet(java.util.HashSet)

Example 10 with Bounds

use of com.google.maps.android.geometry.Bounds in project android-maps-utils by googlemaps.

the class HeatmapTileProvider method getTile.

/**
     * Creates tile.
     *
     * @param x    X coordinate of tile.
     * @param y    Y coordinate of tile.
     * @param zoom Zoom level.
     * @return image in Tile format
     */
public Tile getTile(int x, int y, int zoom) {
    // Convert tile coordinates and zoom into Point/Bounds format
    // Know that at zoom level 0, there is one tile: (0, 0) (arbitrary width 512)
    // Each zoom level multiplies number of tiles by 2
    // Width of the world = WORLD_WIDTH = 1
    // x = [0, 1) corresponds to [-180, 180)
    // calculate width of one tile, given there are 2 ^ zoom tiles in that zoom level
    // In terms of world width units
    double tileWidth = WORLD_WIDTH / Math.pow(2, zoom);
    // how much padding to include in search
    // is to tileWidth as mRadius (padding in terms of pixels) is to TILE_DIM
    // In terms of world width units
    double padding = tileWidth * mRadius / TILE_DIM;
    // padded tile width
    // In terms of world width units
    double tileWidthPadded = tileWidth + 2 * padding;
    // padded bucket width - divided by number of buckets
    // In terms of world width units
    double bucketWidth = tileWidthPadded / (TILE_DIM + mRadius * 2);
    // Make bounds: minX, maxX, minY, maxY
    double minX = x * tileWidth - padding;
    double maxX = (x + 1) * tileWidth + padding;
    double minY = y * tileWidth - padding;
    double maxY = (y + 1) * tileWidth + padding;
    // Deal with overlap across lat = 180
    // Need to make it wrap around both ways
    // However, maximum tile size is such that you wont ever have to deal with both, so
    // hence, the else
    // Note: Tile must remain square, so cant optimise by editing bounds
    double xOffset = 0;
    Collection<WeightedLatLng> wrappedPoints = new ArrayList<WeightedLatLng>();
    if (minX < 0) {
        // Need to consider "negative" points
        // (minX to 0) ->  (512+minX to 512) ie +512
        // add 512 to search bounds and subtract 512 from actual points
        Bounds overlapBounds = new Bounds(minX + WORLD_WIDTH, WORLD_WIDTH, minY, maxY);
        xOffset = -WORLD_WIDTH;
        wrappedPoints = mTree.search(overlapBounds);
    } else if (maxX > WORLD_WIDTH) {
        // Cant both be true as then tile covers whole world
        // Need to consider "overflow" points
        // (512 to maxX) -> (0 to maxX-512) ie -512
        // subtract 512 from search bounds and add 512 to actual points
        Bounds overlapBounds = new Bounds(0, maxX - WORLD_WIDTH, minY, maxY);
        xOffset = WORLD_WIDTH;
        wrappedPoints = mTree.search(overlapBounds);
    }
    // Main tile bounds to search
    Bounds tileBounds = new Bounds(minX, maxX, minY, maxY);
    // If outside of *padded* quadtree bounds, return blank tile
    // This is comparing our bounds to the padded bounds of all points in the quadtree
    // ie tiles that don't touch the heatmap at all
    Bounds paddedBounds = new Bounds(mBounds.minX - padding, mBounds.maxX + padding, mBounds.minY - padding, mBounds.maxY + padding);
    if (!tileBounds.intersects(paddedBounds)) {
        return TileProvider.NO_TILE;
    }
    // Search for all points within tile bounds
    Collection<WeightedLatLng> points = mTree.search(tileBounds);
    // If no points, return blank tile
    if (points.isEmpty()) {
        return TileProvider.NO_TILE;
    }
    // Quantize points
    double[][] intensity = new double[TILE_DIM + mRadius * 2][TILE_DIM + mRadius * 2];
    for (WeightedLatLng w : points) {
        Point p = w.getPoint();
        int bucketX = (int) ((p.x - minX) / bucketWidth);
        int bucketY = (int) ((p.y - minY) / bucketWidth);
        intensity[bucketX][bucketY] += w.getIntensity();
    }
    // Quantize wraparound points (taking xOffset into account)
    for (WeightedLatLng w : wrappedPoints) {
        Point p = w.getPoint();
        int bucketX = (int) ((p.x + xOffset - minX) / bucketWidth);
        int bucketY = (int) ((p.y - minY) / bucketWidth);
        intensity[bucketX][bucketY] += w.getIntensity();
    }
    // Convolve it ("smoothen" it out)
    double[][] convolved = convolve(intensity, mKernel);
    // Color it into a bitmap
    Bitmap bitmap = colorize(convolved, mColorMap, mMaxIntensity[zoom]);
    // Convert bitmap to tile and return
    return convertBitmap(bitmap);
}
Also used : Bitmap(android.graphics.Bitmap) Bounds(com.google.maps.android.geometry.Bounds) ArrayList(java.util.ArrayList) Point(com.google.maps.android.geometry.Point) Point(com.google.maps.android.geometry.Point)

Aggregations

Bounds (com.google.maps.android.geometry.Bounds)10 Point (com.google.maps.android.geometry.Point)7 ArrayList (java.util.ArrayList)2 Bitmap (android.graphics.Bitmap)1 LatLng (com.google.android.gms.maps.model.LatLng)1 Cluster (com.google.maps.android.clustering.Cluster)1 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1 Random (java.util.Random)1