Markers, Lines and Polygons (Java)
This articles discusses how to perform some common interactions with this following osmdroid capabilities
- Geospatially referenced icons
- ItemizedIconOverlay (oldie but a goodie)
- Marker - near API compatible with Google Maps v2
- Fast Icon Overlays
- Geospatially referenced Lines and Polygons
- Folder overlay
Pushpins, markers, icons, symbols, etc
The itemized icon overlay was based on a Google Maps v1 API.
//your items
ArrayList<OverlayItem> items = new ArrayList<OverlayItem>();
items.add(new OverlayItem("Title", "Description", new GeoPoint(0.0d,0.0d))); // Lat/Lon decimal degrees
//the overlay
ItemizedOverlayWithFocus<OverlayItem> mOverlay = new ItemizedOverlayWithFocus<OverlayItem>(items,
new ItemizedIconOverlay.OnItemGestureListener<OverlayItem>() {
@Override
public boolean onItemSingleTapUp(final int index, final OverlayItem item) {
//do something
return true;
}
@Override
public boolean onItemLongPress(final int index, final OverlayItem item) {
return false;
}
});
mOverlay.setFocusItemsOnTap(true);
mMapView.getOverlays().add(mOverlay);
The Marker overlay was generously donated to osmdroid from osmdroidbonuspack. It mirrors the Google Maps v2 API.
Marker startMarker = new Marker(map);
startMarker.setPosition(startPoint);
startMarker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER);
map.getOverlays().add(startMarker);
setAnchor
is used for specifying the logical point of the image that the location represents. For pushpin style icons, bottom center, is usually what you want. Crosshair or other similar icons center, center may be what you want. Of course, you can specify whatever you need you so that the icon provide an accurate representation of the location.
Clicking on it opens an empty cartoon-bubble.
Changing the icon, and setting a title to be shown in the bubble:
startMarker.setIcon(getResources().getDrawable(R.drawable.ic_launcher));
startMarker.setTitle("Start point");
The bubble can be customized with your own layouts too
The marker class can render a simple text label for icons without an icon. This is an opt in feature and is global. It is super useful for certain KML scenarios (osmbonuspack required for KML parsing).
Marker m = new Marker(mapView);
m.setPosition(new GeoPoint(0d,0d));
m.setTextLabelBackgroundColor(
Color.TRANSPARENT
);
m.setTextLabelForegroundColor(
Color.RED
);
m.setTextLabelFontSize(40);
m.setTextIcon("text");
m.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_TOP);
mapView.getOverlays()
.add(m);
Notice: Must set color and font size before
setTextIcon
, set anchor aftersetTextIcon
.
Easy place to see this in action? The LatLonGridLineOverlay
Note that this just work while scrolling manually. If anyone has a better idea feel free to update it
Overlay mOverlay = new Overlay() {
@Override
public boolean onScroll(MotionEvent pEvent1, MotionEvent pEvent2, float pDistanceX, float pDistanceY, MapView pMapView) {
marker.setPosition(new GeoPoint((float) pMapView.getMapCenter().getLatitude(),
(float) pMapView.getMapCenter().getLongitude()));
return super.onScroll(pEvent1, pEvent2, pDistanceX, pDistanceY, pMapView);
}
};
map.getOverlays().add(mOverlay);
The fast overlay is great if you have a huge number points to render and they all share the same icon. It is optimized to render over 100k points, however performance will vary based on hardware.
// create 10k labelled points
// in most cases, there will be no problems of displaying >100k points, feel free to try
List<IGeoPoint> points = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
points.add(new LabelledGeoPoint(37 + Math.random() * 5, -8 + Math.random() * 5
, "Point #" + i));
}
// wrap them in a theme
SimplePointTheme pt = new SimplePointTheme(points, true);
// create label style
Paint textStyle = new Paint();
textStyle.setStyle(Paint.Style.FILL);
textStyle.setColor(Color.parseColor("#0000ff"));
textStyle.setTextAlign(Paint.Align.CENTER);
textStyle.setTextSize(24);
// set some visual options for the overlay
// we use here MAXIMUM_OPTIMIZATION algorithm, which works well with >100k points
SimpleFastPointOverlayOptions opt = SimpleFastPointOverlayOptions.getDefaultStyle()
.setAlgorithm(SimpleFastPointOverlayOptions.RenderingAlgorithm.MAXIMUM_OPTIMIZATION)
.setRadius(7).setIsClickable(true).setCellSize(15).setTextStyle(textStyle);
// create the overlay with the theme
final SimpleFastPointOverlay sfpo = new SimpleFastPointOverlay(pt, opt);
// onClick callback
sfpo.setOnClickListener(new SimpleFastPointOverlay.OnClickListener() {
@Override
public void onClick(SimpleFastPointOverlay.PointAdapter points, Integer point) {
Toast.makeText(mMapView.getContext()
, "You clicked " + ((LabelledGeoPoint) points.get(point)).getLabel()
, Toast.LENGTH_SHORT).show();
}
});
// add overlay
mMapView.getOverlays().add(sfpo);
osmdroid includes two classes that more or less mirror Google Maps v2 API, the Polyline
and Polygon
.
List<GeoPoint> geoPoints = new ArrayList<>();
//add your points here
Polyline line = new Polyline(); //see note below!
line.setPoints(geoPoints);
line.setOnClickListener(new Polyline.OnClickListener() {
@Override
public boolean onClick(Polyline polyline, MapView mapView, GeoPoint eventPos) {
Toast.makeText(mapView.getContext(), "polyline with " + polyline.getPoints().size() + "pts was tapped", Toast.LENGTH_LONG).show();
return false;
}
});
map.getOverlayManager().add(line);
List<GeoPoint> geoPoints = new ArrayList<>();
//add your points here
Polygon polygon = new Polygon(); //see note below
geoPoints.add(geoPoints.get(0)); //forces the loop to close(connect last point to first point)
polygon.getFillPaint().setColor(Color.parseColor("#1EFFE70E")); //set fill color
polygon.setPoints(geoPoints);
polygon.setTitle("A sample polygon");
//polygons supports holes too, points should be in a counter-clockwise order
List<List<GeoPoint>> holes = new ArrayList<>();
holes.add(geoPoints);
polygon.setHoles(holes);
map.getOverlayManager().add(polygon);
Both Polyline and Polygons support
- InfoWindow (the cartoon bubble that is displayed when clicking on the item). The default (built in) info window is only available when using the alternate constructor of PolyLine(map) and Polygon(map). The default zero args constructor can still be used but you'll have to provide your own info window.
- The location over the InfoWindow anchor point can be overridden by extending both classes and overriding the appropriate method. In both classes, the default is to use the first point.
The Folder Overlay class is just a container of other overlays. Useful for bunching stuff together.
Device | Markers | Itemized Icon Overlay | Fast Overlay | Polylines | Polygons |
---|---|---|---|---|---|
Galaxy S5 | 5k | 3-6k | 100k | 1000s | 2k |
The answer is greatly dependent on what hardware the osmdroid based app is running on. A Samsung S5 (no endorsement intended) ran just fine at 3k icons and was noticeably choppy at 6k icons. Your mileage may vary. X86 Android running on modern hardware will perform great at even higher numbers. However, it's recommended to limit the amount of stuff you're rendering, if at all possible.
If you're also drawing paths, lines, polygons, etc, then this also changes the equation. Drawing multipoint graphics is computationally more expensive and thus negatively affects performance under higher loads. To mitigate performance issues with multipoint graphics, one strategy would be to reduce the number of points handed off to the map engine when at a higher zoom level (numerically lower), then increase the fidelity as the user zoom's in. In effect, you would be clipping the visible data at the map view bounds so that the map view only "knows" about what's on screen and doesn't have to loop through all 10k icons that you want on the map. Although you can give the map view all 10k objects, but every time the map moves or zooms, it will iterate over all 10k items to calculate where to draw them (if at all). Using this mechanism paired with map motion listeners and a database query that supports geographic bounds, you can support a rich experience for users with lots of data and still have reasonable performance.
Reusing drawables for icons will help with memory usage too.
You need to call mapView.invalidate()
after add Marker, Line or Polygon
to the map. If not, these overlay can`t display immediately.