From 28961caa79a127903fb4451fc1e3291d459713dd Mon Sep 17 00:00:00 2001 From: Emux Date: Sat, 4 Mar 2017 17:17:24 +0200 Subject: [PATCH] Marker clustering improvements #312 --- docs/Changelog.md | 1 + .../test/ClusterMarkerOverlayActivity.java | 80 +++++++++-------- .../oscim/test/ClusterMarkerLayerTest.java | 89 +++++++++++++++++++ .../layers/marker/utils/ScreenUtils.java | 20 ++--- 4 files changed, 141 insertions(+), 49 deletions(-) create mode 100644 vtm-playground/src/org/oscim/test/ClusterMarkerLayerTest.java diff --git a/docs/Changelog.md b/docs/Changelog.md index fecd6436..153724b9 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -3,6 +3,7 @@ ## New since 0.7.0 - Symbol rotation [#294](https://github.com/mapsforge/vtm/issues/294) +- Marker clustering [#312](https://github.com/mapsforge/vtm/issues/312) - Osmagray theme [#300](https://github.com/mapsforge/vtm/issues/300) - OkHttp external cache [#135](https://github.com/mapsforge/vtm/issues/135) - Texture atlas improvements [#301](https://github.com/mapsforge/vtm/pull/301) [#304](https://github.com/mapsforge/vtm/pull/304) diff --git a/vtm-android-example/src/org/oscim/android/test/ClusterMarkerOverlayActivity.java b/vtm-android-example/src/org/oscim/android/test/ClusterMarkerOverlayActivity.java index 184aad96..bc4f432a 100644 --- a/vtm-android-example/src/org/oscim/android/test/ClusterMarkerOverlayActivity.java +++ b/vtm-android-example/src/org/oscim/android/test/ClusterMarkerOverlayActivity.java @@ -15,70 +15,76 @@ */ package org.oscim.android.test; -import android.graphics.drawable.BitmapDrawable; -import android.os.Bundle; - -import org.oscim.android.canvas.AndroidBitmap; +import org.oscim.backend.canvas.Bitmap; +import org.oscim.backend.canvas.Color; import org.oscim.core.GeoPoint; -import org.oscim.core.MapPosition; +import org.oscim.layers.TileGridLayer; import org.oscim.layers.marker.ClusterMarkerRenderer; import org.oscim.layers.marker.ItemizedLayer; import org.oscim.layers.marker.MarkerItem; import org.oscim.layers.marker.MarkerSymbol; +import org.oscim.layers.tile.buildings.BuildingLayer; +import org.oscim.layers.tile.vector.VectorTileLayer; +import org.oscim.layers.tile.vector.labeling.LabelLayer; import org.oscim.theme.VtmThemes; +import org.oscim.tiling.source.oscimap4.OSciMap4TileSource; import java.util.ArrayList; +import java.util.List; -public class ClusterMarkerOverlayActivity extends BaseMapActivity { +import static org.oscim.android.canvas.AndroidGraphics.drawableToBitmap; + +public class ClusterMarkerOverlayActivity extends MarkerOverlayActivity { private static final int COUNT = 5; private static final float STEP = 100f / 110000f; // roughly 100 meters @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + void createLayers() { + // Map events receiver + mMap.layers().add(new MapEventsReceiver(mMap)); + VectorTileLayer l = mMap.setBaseMap(new OSciMap4TileSource()); + mMap.layers().add(new BuildingLayer(mMap, l)); + mMap.layers().add(new LabelLayer(mMap, l)); mMap.setTheme(VtmThemes.DEFAULT); - MapPosition pos = new MapPosition(); - mMap.getMapPosition(pos); - pos.setZoomLevel(2); - mMap.setMapPosition(pos); + Bitmap bitmapPoi = drawableToBitmap(getResources(), R.drawable.marker_poi); + MarkerSymbol symbol; + if (BILLBOARDS) + symbol = new MarkerSymbol(bitmapPoi, MarkerSymbol.HotspotPlace.BOTTOM_CENTER); + else + symbol = new MarkerSymbol(bitmapPoi, MarkerSymbol.HotspotPlace.CENTER, false); - ItemizedLayer layer = new ItemizedLayer<>( + mMarkerLayer = new ItemizedLayer<>( mMap, - ClusterMarkerRenderer.factory(null, new ClusterMarkerRenderer.ClusterStyle(0xffffffff, 0xff123456)) - ); - - // add it top the map - mMap.layers().add(layer); - - // create a symbol, for simplicity we will use this symbol for all created markers - MarkerSymbol symbol = new MarkerSymbol( - new AndroidBitmap(((BitmapDrawable) (getResources().getDrawable(R.drawable.marker_poi))).getBitmap()), - MarkerSymbol.HotspotPlace.CENTER - ); - - // create some markers spaced STEP degrees - GeoPoint center = pos.getGeoPoint(); - ArrayList list = new ArrayList<>(); + new ArrayList(), + ClusterMarkerRenderer.factory(symbol, new ClusterMarkerRenderer.ClusterStyle(Color.WHITE, Color.BLUE)), + this); + mMap.layers().add(mMarkerLayer); + // Create some markers spaced STEP degrees + List pts = new ArrayList<>(); + GeoPoint center = mMap.getMapPosition().getGeoPoint(); for (int x = -COUNT; x < COUNT; x++) { for (int y = -COUNT; y < COUNT; y++) { double random = STEP * Math.random() * 2; - - MarkerItem item = new MarkerItem( - "Demo Marker " + ((x * COUNT) + y), - "Your typical marker in your typical map", + MarkerItem item = new MarkerItem(y + ", " + x, "", new GeoPoint(center.getLatitude() + y * STEP + random, center.getLongitude() + x * STEP + random) ); - - item.setMarker(symbol); - list.add(item); + pts.add(item); } } + mMarkerLayer.addItems(pts); - // add'em all at once - layer.addItems(list); + mMap.layers().add(new TileGridLayer(mMap, getResources().getDisplayMetrics().density)); + } + + @Override + protected void onResume() { + super.onResume(); + + /* ignore saved position */ + mMap.setMapPosition(53.08, 8.83, 1 << 15); } } diff --git a/vtm-playground/src/org/oscim/test/ClusterMarkerLayerTest.java b/vtm-playground/src/org/oscim/test/ClusterMarkerLayerTest.java new file mode 100644 index 00000000..4214e6ba --- /dev/null +++ b/vtm-playground/src/org/oscim/test/ClusterMarkerLayerTest.java @@ -0,0 +1,89 @@ +/* + * Copyright 2016-2017 devemux86 + * Copyright 2017 nebular + * + * 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 . + */ +package org.oscim.test; + +import org.oscim.backend.CanvasAdapter; +import org.oscim.backend.canvas.Bitmap; +import org.oscim.backend.canvas.Color; +import org.oscim.core.GeoPoint; +import org.oscim.gdx.GdxMapApp; +import org.oscim.layers.TileGridLayer; +import org.oscim.layers.marker.ClusterMarkerRenderer; +import org.oscim.layers.marker.ItemizedLayer; +import org.oscim.layers.marker.MarkerItem; +import org.oscim.layers.marker.MarkerSymbol; +import org.oscim.layers.tile.buildings.BuildingLayer; +import org.oscim.layers.tile.vector.VectorTileLayer; +import org.oscim.layers.tile.vector.labeling.LabelLayer; +import org.oscim.theme.VtmThemes; +import org.oscim.tiling.source.oscimap4.OSciMap4TileSource; + +import java.util.ArrayList; +import java.util.List; + +public class ClusterMarkerLayerTest extends MarkerLayerTest { + + private static final int COUNT = 5; + private static final float STEP = 100f / 110000f; // roughly 100 meters + + @Override + public void createLayers() { + // Map events receiver + mMap.layers().add(new MapEventsReceiver(mMap)); + + VectorTileLayer l = mMap.setBaseMap(new OSciMap4TileSource()); + mMap.layers().add(new BuildingLayer(mMap, l)); + mMap.layers().add(new LabelLayer(mMap, l)); + mMap.setTheme(VtmThemes.DEFAULT); + + mMap.setMapPosition(53.08, 8.83, 1 << 15); + + Bitmap bitmapPoi = CanvasAdapter.decodeBitmap(getClass().getResourceAsStream("/res/marker_poi.png")); + MarkerSymbol symbol; + if (BILLBOARDS) + symbol = new MarkerSymbol(bitmapPoi, MarkerSymbol.HotspotPlace.BOTTOM_CENTER); + else + symbol = new MarkerSymbol(bitmapPoi, MarkerSymbol.HotspotPlace.CENTER, false); + + mMarkerLayer = new ItemizedLayer<>( + mMap, + new ArrayList(), + ClusterMarkerRenderer.factory(symbol, new ClusterMarkerRenderer.ClusterStyle(Color.WHITE, Color.BLUE)), + this); + mMap.layers().add(mMarkerLayer); + + // Create some markers spaced STEP degrees + List pts = new ArrayList<>(); + GeoPoint center = mMap.getMapPosition().getGeoPoint(); + for (int x = -COUNT; x < COUNT; x++) { + for (int y = -COUNT; y < COUNT; y++) { + double random = STEP * Math.random() * 2; + MarkerItem item = new MarkerItem(y + ", " + x, "", + new GeoPoint(center.getLatitude() + y * STEP + random, center.getLongitude() + x * STEP + random) + ); + pts.add(item); + } + } + mMarkerLayer.addItems(pts); + + mMap.layers().add(new TileGridLayer(mMap)); + } + + public static void main(String[] args) { + GdxMapApp.init(); + GdxMapApp.run(new ClusterMarkerLayerTest()); + } +} diff --git a/vtm/src/org/oscim/layers/marker/utils/ScreenUtils.java b/vtm/src/org/oscim/layers/marker/utils/ScreenUtils.java index e96df75e..31d3004d 100644 --- a/vtm/src/org/oscim/layers/marker/utils/ScreenUtils.java +++ b/vtm/src/org/oscim/layers/marker/utils/ScreenUtils.java @@ -1,5 +1,6 @@ /* * 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 @@ -25,11 +26,6 @@ import org.oscim.backend.canvas.Paint; */ public class ScreenUtils { - /** - * https://developer.android.com/reference/android/util/DisplayMetrics.html#DENSITY_DEFAULT - */ - private static final float REFERENCE_DPI = 160; - /** * Get pixels from DPs * @@ -37,7 +33,7 @@ public class ScreenUtils { * @return Value in PX according to screen density */ public static int getPixels(float dp) { - return (int) (CanvasAdapter.dpi / REFERENCE_DPI * dp); + return (int) (CanvasAdapter.dpi / CanvasAdapter.DEFAULT_DPI * dp); } public static class ClusterDrawable { @@ -64,15 +60,12 @@ public class ScreenUtils { mPaintText.setTextSize(ScreenUtils.getPixels((int) (sizedp * 0.6666666))); mPaintText.setColor(foregroundColor); - // NOT SUPPORTED on current backends (Feb 2017) - // mPaintText.setTextAlign(Paint.Align.CENTER); - mPaintCircle.setColor(backgroundColor); mPaintCircle.setStyle(Paint.Style.FILL); mPaintBorder.setColor(foregroundColor); mPaintBorder.setStyle(Paint.Style.STROKE); - mPaintBorder.setStrokeWidth(2.0f); + mPaintBorder.setStrokeWidth(2.0f * CanvasAdapter.dpi / CanvasAdapter.DEFAULT_DPI); } private void setText(String text) { @@ -86,8 +79,11 @@ public class ScreenUtils { canvas.drawCircle(halfsize, halfsize, halfsize, mPaintCircle); // fill canvas.drawCircle(halfsize, halfsize, halfsize, mPaintBorder); - // draw the number, the centering is not perfect without a measureText or alignment - canvas.drawText(mText, halfsize * 0.6f, halfsize + (halfsize >> 1), mPaintText); + // 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() {