Move cluster experiment in samples (#858)
This commit is contained in:
@@ -0,0 +1,436 @@
|
||||
/*
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
* Copyright 2016 Izumi Kawashima
|
||||
* Copyright 2017 Longri
|
||||
* Copyright 2017-2018 devemux86
|
||||
* Copyright 2017 nebular
|
||||
* Copyright 2017 Wolfgang Schramm
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.android.cluster;
|
||||
|
||||
import org.oscim.backend.canvas.Bitmap;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
import org.oscim.core.PointF;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.layers.marker.*;
|
||||
import org.oscim.layers.marker.utils.SparseIntArray;
|
||||
import org.oscim.renderer.GLViewport;
|
||||
import org.oscim.renderer.bucket.SymbolItem;
|
||||
import org.oscim.utils.FastMath;
|
||||
import org.oscim.utils.geom.GeometryUtils;
|
||||
|
||||
/**
|
||||
* An extension to the MarkerRenderer with item clustering support.
|
||||
*/
|
||||
public class ClusterMarkerRenderer extends MarkerRenderer {
|
||||
|
||||
/**
|
||||
* Max number to display inside a cluster icon
|
||||
*/
|
||||
protected static final int CLUSTER_MAXSIZE = 10;
|
||||
|
||||
/**
|
||||
* default color of number inside the icon. Would be super-cool to cook this into the map theme
|
||||
*/
|
||||
private static final int CLUSTER_COLORTEXT = 0xff8000c0;
|
||||
|
||||
/**
|
||||
* default color of circle background
|
||||
*/
|
||||
private static final int CLUSTER_COLORBACK = 0xffffffff;
|
||||
|
||||
/**
|
||||
* Map Cluster Icon Size. This is the biggest size for clusters of CLUSTER_MAXSIZE elements. Smaller clusters will be slightly smaller
|
||||
*/
|
||||
protected static final int MAP_MARKER_CLUSTER_SIZE_DP = 64;
|
||||
|
||||
/**
|
||||
* Clustering grid square size, decrease to cluster more aggresively. Ideally this value is the typical marker size
|
||||
*/
|
||||
private static final int MAP_GRID_SIZE_DP = 64;
|
||||
|
||||
/**
|
||||
* cached bitmaps database, we will cache cluster bitmaps from 1 to MAX_SIZE
|
||||
* and always use same bitmap for efficiency
|
||||
*/
|
||||
protected static Bitmap[] mClusterBitmaps = new Bitmap[CLUSTER_MAXSIZE + 1];
|
||||
|
||||
protected int mStyleBackground = CLUSTER_COLORBACK, mStyleForeground = CLUSTER_COLORTEXT;
|
||||
|
||||
/**
|
||||
* Discrete scale step, used to trigger reclustering on significant scale change
|
||||
*/
|
||||
private int mScalePow = 0;
|
||||
|
||||
/**
|
||||
* Map scale to cluster the marker
|
||||
*/
|
||||
private double mClusterScale = 0;
|
||||
|
||||
/**
|
||||
* We use a flat Sparse array to calculate the clusters. The sparse array models a 2D map where every (x,y) denotes
|
||||
* a grid slot, ie. 64x64dp. For efficiency I use a linear sparsearray with ARRindex = SLOTypos * max_x + SLOTxpos"
|
||||
*/
|
||||
private final SparseIntArray mGridMap = new SparseIntArray(200); // initial space for 200 markers, that's not a lot of memory, and in most cases will avoid resizing the array
|
||||
|
||||
/**
|
||||
* Whether to enable clustering or disable the functionality
|
||||
*/
|
||||
private final boolean mClusteringEnabled;
|
||||
|
||||
/**
|
||||
* Constructs a clustered marker renderer
|
||||
*
|
||||
* @param markerLayer The owner layer
|
||||
* @param defaultSymbol The default symbol
|
||||
* @param style The desired style, or NULL to disable clustering
|
||||
*/
|
||||
public ClusterMarkerRenderer(MarkerLayer markerLayer, MarkerSymbol defaultSymbol, ClusterMarkerRenderer.ClusterStyle style) {
|
||||
super(markerLayer, defaultSymbol);
|
||||
|
||||
mClusteringEnabled = style != null;
|
||||
|
||||
if (mClusteringEnabled) {
|
||||
setClusterStyle(style.foreground, style.background);
|
||||
for (int k = 0; k <= CLUSTER_MAXSIZE; k++) {
|
||||
// cache bitmaps so render thread never creates them
|
||||
// we create CLUSTER_MAXSIZE bitmaps. Bigger clusters will show like "+"
|
||||
getClusterBitmap(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the cluster icon style. This is called by the constructor and cannot be made public because
|
||||
* we pre-cache the icons at construction time so the renderer does not have to create them while rendering
|
||||
*
|
||||
* @param backgroundColor Background color
|
||||
* @param foregroundColor text & border color
|
||||
*/
|
||||
private void setClusterStyle(int foregroundColor, int backgroundColor) {
|
||||
mStyleBackground = backgroundColor;
|
||||
mStyleForeground = foregroundColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populate(int size) {
|
||||
repopulateCluster(size, mClusterScale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Repopulates item list clustering close markers. This is triggered from update() when
|
||||
* a significant change in scale has happened.
|
||||
*
|
||||
* @param size Item list size
|
||||
* @param scale current map scale
|
||||
*/
|
||||
private void repopulateCluster(int size, double scale) {
|
||||
/* the grid slot size in px. increase to group more aggressively. currently set to marker size */
|
||||
final int GRIDSIZE = ClusterUtils.getPixels(MAP_GRID_SIZE_DP);
|
||||
|
||||
/* the factor to map into Grid Coordinates (discrete squares of GRIDSIZE x GRIDSIZE) */
|
||||
final double factor = (scale / GRIDSIZE);
|
||||
|
||||
Clustered[] tmp = new Clustered[size];
|
||||
|
||||
// clear grid map to count items that share the same "grid slot"
|
||||
mGridMap.clear();
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
Clustered it = tmp[i] = new Clustered();
|
||||
|
||||
it.item = mMarkerLayer.createItem(i);
|
||||
|
||||
/* pre-project points */
|
||||
MercatorProjection.project(it.item.getPoint(), mMapPoint);
|
||||
it.px = mMapPoint.x;
|
||||
it.py = mMapPoint.y;
|
||||
|
||||
// items can be declared non-clusterable
|
||||
if (!(it.item instanceof NonClusterable)) {
|
||||
|
||||
final int
|
||||
absposx = (int) (it.px * factor), // absolute item X position in the grid
|
||||
absposy = (int) (it.py * factor), // absolute item Y position
|
||||
maxcols = (int) factor, // Grid number of columns
|
||||
itemGridIndex = absposx + absposy * maxcols; // Index in the sparsearray map
|
||||
|
||||
// we store in the linear sparsearray the index of the marker,
|
||||
// ie, index = y * maxcols + x; array[index} = markerIndex
|
||||
|
||||
// Lets check if there's already an item in the grid slot
|
||||
final int storedIndexInGridSlot = mGridMap.get(itemGridIndex, -1);
|
||||
|
||||
if (storedIndexInGridSlot == -1) {
|
||||
// no item at that grid position. The grid slot is free so let's
|
||||
// store this item "i" (we identify every item by its InternalItem index)
|
||||
|
||||
mGridMap.put(itemGridIndex, i);
|
||||
//Log.v(TAG, "UNclustered item at " + itemGridIndex);
|
||||
} else {
|
||||
// at that grid position there's already a marker index
|
||||
// mark this item as clustered out, so it will be skipped in the update() call
|
||||
|
||||
it.clusteredOut = true;
|
||||
|
||||
// and increment the count on its "parent" that will from now on act as a cluster
|
||||
tmp[storedIndexInGridSlot].clusterSize++;
|
||||
|
||||
//Log.v(TAG, "Clustered item at " + itemGridIndex + ", \'parent\' size " + (tmp[storedIndexInGridSlot].clusterSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* All ready for update. */
|
||||
synchronized (this) {
|
||||
mUpdate = true;
|
||||
mItems = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void update(GLViewport v) {
|
||||
final double scale = Tile.SIZE * v.pos.scale;
|
||||
|
||||
if (mClusteringEnabled) {
|
||||
/*
|
||||
Clustering check: If clustering is enabled and there's been a significant scale change
|
||||
trigger repopulation and return. After repopulation, this will be called again
|
||||
*/
|
||||
|
||||
// (int) log of scale gives us adequate steps to trigger clustering
|
||||
int scalePow = FastMath.log2((int) scale);
|
||||
|
||||
if (scalePow != mScalePow) {
|
||||
mScalePow = scalePow;
|
||||
mClusterScale = scale;
|
||||
|
||||
// post repopulation to the main thread
|
||||
mMarkerLayer.map().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
repopulateCluster(mItems.length, scale);
|
||||
}
|
||||
});
|
||||
|
||||
// and get out of here
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!v.changed() && !mUpdate)
|
||||
return;
|
||||
|
||||
mUpdate = false;
|
||||
|
||||
double mx = v.pos.x;
|
||||
double my = v.pos.y;
|
||||
|
||||
//int changesInvisible = 0;
|
||||
//int changedVisible = 0;
|
||||
int numVisible = 0;
|
||||
|
||||
// Increase view to show items that are partially visible
|
||||
mMarkerLayer.map().viewport().getMapExtents(mBox, Tile.SIZE >> 1);
|
||||
|
||||
long flip = (long) (Tile.SIZE * v.pos.scale) >> 1;
|
||||
|
||||
if (mItems == null) {
|
||||
if (buckets.get() != null) {
|
||||
buckets.clear();
|
||||
compile();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
double angle = Math.toRadians(v.pos.bearing);
|
||||
float cos = (float) Math.cos(angle);
|
||||
float sin = (float) Math.sin(angle);
|
||||
|
||||
/* check visibility */
|
||||
for (InternalItem itm : mItems) {
|
||||
Clustered it = (Clustered) itm;
|
||||
|
||||
it.changes = false;
|
||||
it.x = (float) ((it.px - mx) * scale);
|
||||
it.y = (float) ((it.py - my) * scale);
|
||||
|
||||
if (it.x > flip)
|
||||
it.x -= (flip << 1);
|
||||
else if (it.x < -flip)
|
||||
it.x += (flip << 1);
|
||||
|
||||
if ((it.clusteredOut) || (!GeometryUtils.pointInPoly(it.x, it.y, mBox, 8, 0))) {
|
||||
// either properly invisible, or clustered out. Items marked as clustered out mean there's another item
|
||||
// on the same-ish position that will be promoted to cluster marker, so this particular item is considered
|
||||
// invisible
|
||||
|
||||
if (it.visible && (!it.clusteredOut)) {
|
||||
// it was previously visible, but now it won't
|
||||
it.changes = true;
|
||||
// changes to invible
|
||||
//changesInvisible++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// item IS definitely visible
|
||||
it.dy = sin * it.x + cos * it.y;
|
||||
|
||||
if (!it.visible) {
|
||||
it.visible = true;
|
||||
//changedVisible++;
|
||||
}
|
||||
numVisible++;
|
||||
}
|
||||
|
||||
//log.debug(numVisible + " " + changedVisible + " " + changesInvisible);
|
||||
|
||||
/* only update when zoomlevel changed, new items are visible
|
||||
* or more than 10 of the current items became invisible */
|
||||
//if ((numVisible == 0) && (changedVisible == 0 && changesInvisible < 10))
|
||||
// return;
|
||||
buckets.clear();
|
||||
|
||||
if (numVisible == 0) {
|
||||
compile();
|
||||
return;
|
||||
}
|
||||
/* keep position for current state */
|
||||
mMapPosition.copy(v.pos);
|
||||
mMapPosition.bearing = -mMapPosition.bearing;
|
||||
|
||||
// why do we sort ? z-index?
|
||||
sort(mItems, 0, mItems.length);
|
||||
//log.debug(Arrays.toString(mItems));
|
||||
|
||||
for (InternalItem itm : mItems) {
|
||||
Clustered it = (Clustered) itm;
|
||||
|
||||
// skip invisible AND clustered-out
|
||||
if ((!it.visible) || (it.clusteredOut))
|
||||
continue;
|
||||
|
||||
if (it.changes) {
|
||||
it.visible = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
SymbolItem s = SymbolItem.pool.get();
|
||||
|
||||
if (it.clusterSize > 0) {
|
||||
// this item will act as a cluster, just use a proper bitmap
|
||||
// depending on cluster size, instead of its marker
|
||||
|
||||
Bitmap bitmap = getClusterBitmap(it.clusterSize + 1);
|
||||
s.set(it.x, it.y, bitmap, true);
|
||||
s.offset = new PointF(0.5f, 0.5f);
|
||||
s.billboard = true; // could be a parameter
|
||||
|
||||
} else {
|
||||
// normal item, use its marker
|
||||
|
||||
MarkerSymbol symbol = it.item.getMarker();
|
||||
|
||||
if (symbol == null)
|
||||
symbol = mDefaultMarker;
|
||||
|
||||
s.set(it.x, it.y, symbol.getBitmap(), true);
|
||||
s.offset = symbol.getHotspot();
|
||||
s.billboard = symbol.isBillboard();
|
||||
}
|
||||
|
||||
mSymbolLayer.pushSymbol(s);
|
||||
}
|
||||
|
||||
buckets.set(mSymbolLayer);
|
||||
buckets.prepare();
|
||||
|
||||
compile();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a bitmap for a given cluster size
|
||||
*
|
||||
* @param size The cluster size. Can be greater than CLUSTER_MAXSIZE.
|
||||
* @return A somewhat cool bitmap to be used as the cluster marker
|
||||
*/
|
||||
protected Bitmap getClusterBitmap(int size) {
|
||||
final String strValue;
|
||||
|
||||
if (size >= CLUSTER_MAXSIZE) {
|
||||
// restrict cluster indicator size. Bigger clusters will show as "+" instead of ie. "45"
|
||||
size = CLUSTER_MAXSIZE;
|
||||
strValue = "+";
|
||||
} else {
|
||||
strValue = String.valueOf(size);
|
||||
}
|
||||
|
||||
// return cached bitmap if exists. cache hit !
|
||||
if (mClusterBitmaps[size] != null)
|
||||
return mClusterBitmaps[size];
|
||||
|
||||
// create and cache bitmap. This is unacceptable inside the GL thread,
|
||||
// so we'll call this routine at the beginning to pre-cache all bitmaps
|
||||
|
||||
ClusterUtils.ClusterDrawable drawable = new ClusterUtils.ClusterDrawable(
|
||||
MAP_MARKER_CLUSTER_SIZE_DP - CLUSTER_MAXSIZE + size, // make size dependent on cluster size
|
||||
mStyleForeground,
|
||||
mStyleBackground,
|
||||
strValue
|
||||
);
|
||||
|
||||
mClusterBitmaps[size] = drawable.getBitmap();
|
||||
return mClusterBitmaps[size];
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to wrap the cluster icon style properties
|
||||
*/
|
||||
public static class ClusterStyle {
|
||||
final int background;
|
||||
final int foreground;
|
||||
|
||||
/**
|
||||
* Creates the Cluster style
|
||||
*
|
||||
* @param fore Foreground (border and text) color
|
||||
* @param back Background (circle) color
|
||||
*/
|
||||
|
||||
public ClusterStyle(int fore, int back) {
|
||||
foreground = fore;
|
||||
background = back;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for instantiating this renderer via a factory, so the layer construction semantic is more pleasing to the eye
|
||||
*
|
||||
* @param defaultSymbol Default symbol to use if the Marker is not assigned a symbol
|
||||
* @param style Cluster icon style, or NULL to disable clustering functionality
|
||||
* @return A factory to be passed to the ItemizedLayer constructor in order to enable the cluster functionality
|
||||
*/
|
||||
public static MarkerRendererFactory factory(final MarkerSymbol defaultSymbol, final ClusterStyle style) {
|
||||
return new MarkerRendererFactory() {
|
||||
@Override
|
||||
public MarkerRenderer create(MarkerLayer markerLayer) {
|
||||
return new ClusterMarkerRenderer(markerLayer, defaultSymbol, style);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2017 nebular
|
||||
* Copyright 2017 devemux86
|
||||
* Copyright 2017 Wolfgang Schramm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.android.cluster;
|
||||
|
||||
import org.oscim.backend.CanvasAdapter;
|
||||
import org.oscim.backend.canvas.Bitmap;
|
||||
import org.oscim.backend.canvas.Canvas;
|
||||
import org.oscim.backend.canvas.Paint;
|
||||
|
||||
/**
|
||||
* A simple utility class to make clustered markers functionality self-contained.
|
||||
* Includes a method to translate between DPs and PXs and a circular icon generator.
|
||||
*/
|
||||
public class ClusterUtils {
|
||||
|
||||
/**
|
||||
* Get pixels from DPs
|
||||
*
|
||||
* @param dp Value in DPs
|
||||
* @return Value in PX according to screen density
|
||||
*/
|
||||
public static int getPixels(float dp) {
|
||||
return (int) (CanvasAdapter.getScale() * dp);
|
||||
}
|
||||
|
||||
public static class ClusterDrawable {
|
||||
private final Paint mPaintText = CanvasAdapter.newPaint();
|
||||
private final Paint mPaintCircle = CanvasAdapter.newPaint();
|
||||
private final Paint mPaintBorder = CanvasAdapter.newPaint();
|
||||
private int mSize;
|
||||
private String mText;
|
||||
|
||||
/**
|
||||
* Generates a circle with a number inside
|
||||
*
|
||||
* @param sizedp Size in DPs
|
||||
* @param foregroundColor Foreground
|
||||
* @param backgroundColor Background
|
||||
* @param text Text inside. Will only work for a single character!
|
||||
*/
|
||||
public ClusterDrawable(int sizedp, int foregroundColor, int backgroundColor, String text) {
|
||||
setup(sizedp, foregroundColor, backgroundColor);
|
||||
setText(text);
|
||||
}
|
||||
|
||||
private void setup(int sizedp, int foregroundColor, int backgroundColor) {
|
||||
mSize = ClusterUtils.getPixels(sizedp);
|
||||
mPaintText.setTextSize(ClusterUtils.getPixels((int) (sizedp * 0.6666666)));
|
||||
mPaintText.setColor(foregroundColor);
|
||||
|
||||
mPaintCircle.setColor(backgroundColor);
|
||||
mPaintCircle.setStyle(Paint.Style.FILL);
|
||||
|
||||
mPaintBorder.setColor(foregroundColor);
|
||||
mPaintBorder.setStyle(Paint.Style.STROKE);
|
||||
mPaintBorder.setStrokeWidth(2.0f * CanvasAdapter.getScale());
|
||||
}
|
||||
|
||||
private void setText(String text) {
|
||||
mText = text;
|
||||
}
|
||||
|
||||
private void draw(Canvas canvas) {
|
||||
int halfsize = mSize >> 1;
|
||||
final int noneClippingRadius = halfsize - getPixels(2);
|
||||
|
||||
// fill
|
||||
canvas.drawCircle(halfsize, halfsize, noneClippingRadius, mPaintCircle);
|
||||
// outline
|
||||
canvas.drawCircle(halfsize, halfsize, noneClippingRadius, mPaintBorder);
|
||||
// draw the number at the center
|
||||
canvas.drawText(mText,
|
||||
(canvas.getWidth() - mPaintText.getTextWidth(mText)) * 0.5f,
|
||||
(canvas.getHeight() + mPaintText.getTextHeight(mText)) * 0.5f,
|
||||
mPaintText);
|
||||
}
|
||||
|
||||
public Bitmap getBitmap() {
|
||||
int width = mSize, height = mSize;
|
||||
width = width > 0 ? width : 1;
|
||||
height = height > 0 ? height : 1;
|
||||
|
||||
Bitmap bitmap = CanvasAdapter.newBitmap(width, height, 0);
|
||||
Canvas canvas = CanvasAdapter.newCanvas();
|
||||
canvas.setBitmap(bitmap);
|
||||
draw(canvas);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2017 nebular
|
||||
* Copyright 2017 devemux86
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.android.cluster;
|
||||
|
||||
import org.oscim.layers.marker.InternalItem;
|
||||
|
||||
/**
|
||||
* Extension for clustered items.
|
||||
*/
|
||||
public class Clustered extends InternalItem {
|
||||
/**
|
||||
* If this is >0, this item will be displayed as a cluster circle, with size clusterSize+1.
|
||||
*/
|
||||
public int clusterSize;
|
||||
|
||||
/**
|
||||
* If this is true, this item is hidden (because it's represented by another InternalItem acting as cluster.
|
||||
*/
|
||||
public boolean clusteredOut;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2017 nebular
|
||||
* Copyright 2017 devemux86
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.android.cluster;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.layers.marker.MarkerItem;
|
||||
|
||||
/**
|
||||
* If a MarkerItem is created using this convenience class instead of MarkerItem,
|
||||
* this specific item will not be clusterable.
|
||||
*/
|
||||
public class NonClusterable extends MarkerItem {
|
||||
public NonClusterable(String title, String description, GeoPoint geoPoint) {
|
||||
super(null, title, description, geoPoint);
|
||||
}
|
||||
|
||||
public NonClusterable(Object uid, String title, String description, GeoPoint geoPoint) {
|
||||
super(uid, title, description, geoPoint);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package org.oscim.android.test;
|
||||
|
||||
import android.graphics.BitmapFactory;
|
||||
import org.oscim.android.canvas.AndroidBitmap;
|
||||
import org.oscim.android.cluster.ClusterMarkerRenderer;
|
||||
import org.oscim.backend.canvas.Bitmap;
|
||||
import org.oscim.backend.canvas.Color;
|
||||
import org.oscim.core.GeoPoint;
|
||||
|
||||
Reference in New Issue
Block a user