use of com.google.maps.android.geometry.Point 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);
}
use of com.google.maps.android.geometry.Point in project android-maps-utils by googlemaps.
the class PointQuadTreeTest method testVeryDeepTree.
/**
* Tests 30,000 items at the same point. Timing results are averaged.
*/
@Test
public void testVeryDeepTree() {
System.gc();
for (int i = 0; i < 30000; i++) {
mTree.add(new Item(0, 0));
}
Assert.assertEquals(30000, searchAll().size());
Assert.assertEquals(30000, mTree.search(new Bounds(0, .1, 0, .1)).size());
Assert.assertEquals(0, mTree.search(new Bounds(.1, 1, .1, 1)).size());
mTree.clear();
System.gc();
}
use of com.google.maps.android.geometry.Point in project wigle-wifi-wardriving by wiglenet.
the class GridBasedAlgorithm method getClusters.
@Override
public Set<? extends Cluster<T>> getClusters(double zoom) {
long numCells = (long) Math.ceil(256 * Math.pow(2, zoom) / GRID_SIZE);
SphericalMercatorProjection proj = new SphericalMercatorProjection(numCells);
HashSet<Cluster<T>> clusters = new HashSet<Cluster<T>>();
LongSparseArray<StaticCluster<T>> sparseArray = new LongSparseArray<StaticCluster<T>>();
synchronized (mItems) {
for (T item : mItems) {
Point p = proj.toPoint(item.getPosition());
long coord = getCoord(numCells, p.x, p.y);
StaticCluster<T> cluster = sparseArray.get(coord);
if (cluster == null) {
cluster = new StaticCluster<T>(proj.toLatLng(new Point(Math.floor(p.x) + .5, Math.floor(p.y) + .5)));
sparseArray.put(coord, cluster);
clusters.add(cluster);
}
cluster.add(item);
}
}
return clusters;
}
use of com.google.maps.android.geometry.Point in project wigle-wifi-wardriving by wiglenet.
the class HeatmapTileProvider method getMaxValue.
/**
* Calculate a reasonable maximum intensity value to map to maximum color intensity
*
* @param points Collection of LatLngs to put into buckets
* @param bounds Bucket boundaries
* @param radius radius of convolution
* @param screenDim larger dimension of screen in pixels (for scale)
* @return Approximate max value
*/
static double getMaxValue(Collection<WeightedLatLng> points, Bounds bounds, int radius, int screenDim) {
// Approximate scale as if entire heatmap is on the screen
// ie scale dimensions to larger of width or height (screenDim)
double minX = bounds.minX;
double maxX = bounds.maxX;
double minY = bounds.minY;
double maxY = bounds.maxY;
double boundsDim = (maxX - minX > maxY - minY) ? maxX - minX : maxY - minY;
// Number of buckets: have diameter sized buckets
int nBuckets = (int) (screenDim / (2 * radius) + 0.5);
// Scaling factor to convert width in terms of point distance, to which bucket
double scale = nBuckets / boundsDim;
// Make buckets
// Use a sparse array - use LongSparseArray just in case
LongSparseArray<LongSparseArray<Double>> buckets = new LongSparseArray<LongSparseArray<Double>>();
// double[][] buckets = new double[nBuckets][nBuckets];
// Assign into buckets + find max value as we go along
double x, y;
double max = 0;
for (WeightedLatLng l : points) {
x = l.getPoint().x;
y = l.getPoint().y;
int xBucket = (int) ((x - minX) * scale);
int yBucket = (int) ((y - minY) * scale);
// Check if x bucket exists, if not make it
LongSparseArray<Double> column = buckets.get(xBucket);
if (column == null) {
column = new LongSparseArray<Double>();
buckets.put(xBucket, column);
}
// Check if there is already a y value there
Double value = column.get(yBucket);
if (value == null) {
value = 0.0;
}
value += l.getIntensity();
// Yes, do need to update it, despite it being a Double.
column.put(yBucket, value);
if (value > max)
max = value;
}
return max;
}
use of com.google.maps.android.geometry.Point in project wigle-wifi-wardriving by wiglenet.
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);
}
Aggregations