/*
* Copyright (c) 2015 Daimler AG / Moovel GmbH
*
* All rights reserved
*/
package com.car2go.maps.osm.drawable;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Region;
import com.car2go.maps.model.LatLng;
import com.car2go.maps.model.Polygon;
import com.car2go.maps.model.PolygonOptions;
import org.osmdroid.api.IGeoPoint;
import org.osmdroid.views.MapView;
import org.osmdroid.views.Projection;
import org.osmdroid.views.overlay.Overlay;
import java.util.ArrayList;
import java.util.List;
import static com.car2go.maps.osm.util.OsmUtils.toGeoPoints;
/**
* Draws polygon on OpenStreetMaps.
* Associated with given {@link com.car2go.maps.AnyMap} object, so it should not be cached.
*/
public class OsmPolygon implements Polygon {
private final MapView map;
private final PolygonOverlay overlay;
private final PolygonOptions options;
OsmPolygon(MapView map, PolygonOptions options) {
this.map = map;
this.options = options;
overlay = new PolygonOverlay(map, options);
map.getOverlays().add(0, overlay);
map.invalidate();
}
@Override
public void setHoles(List<List<LatLng>> holes) {
overlay.setHoles(holes);
}
@Override
public List<LatLng> getPoints() {
return options.getPoints();
}
@Override
public void setVisible(boolean visible) {
overlay.setEnabled(visible);
map.invalidate();
}
@Override
public void remove() {
map.getOverlays().remove(overlay);
map.invalidate();
}
/**
* Overlay which draws a single polygon (with holes) on OpenStreetMap
*/
private static class PolygonOverlay extends Overlay {
private final MapView map;
private final boolean outsider;
private final List<IGeoPoint> points;
private final List<List<IGeoPoint>> holes = new ArrayList<>();
private final Path polygonPath = new Path();
private final Paint fillPaint;
private final Paint strokePaint;
private final Point auxPoint = new Point();
public PolygonOverlay(MapView map, PolygonOptions options) {
super(map.getContext());
this.map = map;
outsider = options.isOutsider();
points = toGeoPoints(options.getPoints());
fillPaint = new Paint();
fillPaint.setStyle(Paint.Style.FILL);
fillPaint.setColor(options.getFillColor());
fillPaint.setAlpha(
Color.alpha(options.getFillColor()) / 2
);
strokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
strokePaint.setStyle(Paint.Style.STROKE);
strokePaint.setColor(options.getStrokeColor());
strokePaint.setStrokeWidth(options.getStrokeWidth());
}
void setHoles(List<List<LatLng>> holes) {
this.holes.clear();
for (List<LatLng> hole : holes) {
this.holes.add(
toGeoPoints(hole)
);
}
map.invalidate();
}
@Override
protected void draw(Canvas c, MapView osmv, boolean shadow) {
Projection projection = osmv.getProjection();
c.save();
if (!holes.isEmpty()) {
polygonPath.rewind();
populateHoles(projection);
// Draw outline of the holes
c.drawPath(polygonPath, strokePaint);
// Clip holes so that when we'll draw polygon on top of them the area under the
// holes will be "cropped"
c.clipPath(polygonPath, Region.Op.DIFFERENCE);
}
if (!points.isEmpty()) {
if (!outsider) {
polygonPath.rewind();
populateOutline(projection);
// Draw and fill polygon itself
c.drawPath(polygonPath, fillPaint);
c.drawPath(polygonPath, strokePaint);
} else {
// Optimization for outsider-polygons. Just fill the whole screen with the color.
// We cropped holes before, so it will appear as "transparent" polygons are
// drawn.
c.drawPaint(fillPaint);
}
}
c.restore();
}
private void populateOutline(Projection projection) {
populatePath(projection, points, polygonPath);
}
private void populateHoles(Projection projection) {
for (List<IGeoPoint> hole : holes) {
populatePath(projection, hole, polygonPath);
}
}
private void populatePath(Projection projection, List<IGeoPoint> points, Path path) {
Point startPoint = projection.toPixels(points.get(0), auxPoint);
path.moveTo(startPoint.x, startPoint.y);
for (int i = 1; i < points.size(); i++) {
Point point = projection.toPixels(points.get(i), auxPoint);
path.lineTo(point.x, point.y);
}
path.close();
}
}
}