package com.spatialdev.osm.renderer; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import com.mapbox.mapboxsdk.views.MapView; import com.mapbox.mapboxsdk.views.util.Projection; import com.spatialdev.osm.model.OSMNode; import com.spatialdev.osm.model.OSMElement; import com.spatialdev.osm.model.OSMWay; import java.util.List; /** * Created by Nicholas Hallahan on 1/22/15. * nhallahan@spatialdev.com */ public abstract class OSMPath { /** * Paint Settings * */ protected Paint paint = new Paint(); protected final Path path = new Path(); // This is the real stroke width. // The paint's stroke width gets adjusted for approximate zooms. private float strokeWidth = 10.0f; /** * This gets reused by Projection#toMapPixelsTranslated so * that the returned Point is not constantly reallocated. * * * */ protected final double[] tempPoint = new double[2]; // These are the points for a path converted to an "intermediate" // pixel space of the entire earth. protected double[][] projectedPoints; protected MapView mapView; // gets set in draw, the bounds of the viewport in Mercator Projected Pixels protected Rect viewPortBounds; /** * When drawing, this gets set to true when * we know that we wan to call path.lineTo next. * * * * */ protected boolean pathLineToReady = false; public static OSMPath createOSMPath(OSMElement element, MapView mv) { if (element instanceof OSMWay) { OSMWay w = (OSMWay) element; // polygon if (w.isClosed()) { return new OSMPolygon(w, mv); } // line return new OSMLine(w, mv); } // TODO Point return null; } /** * We only want to construct subclasses. This is ultimately created via * OSMPath.createOSMPath * * * * * @param w Way, MapView mv */ protected OSMPath(OSMWay w, MapView mv) { List<OSMNode> nodes = w.getNodes(); projectNodes(nodes); mapView = mv; paint.setAntiAlias(true); } /** * Do the expensive projection straight up upon construction rather than draw. * * @param nodes */ private void projectNodes(List<OSMNode> nodes) { projectedPoints = new double[nodes.size()][2]; int i = 0; for (OSMNode n : nodes) { projectedPoints[i++] = Projection.latLongToPixelXY(n.getLat(), n.getLng()); } } public Paint getPaint() { return paint; } public OSMPath setPaint(final Paint pPaint) { paint = pPaint; return this; } public void setStrokeWidth(float width) { strokeWidth = width; } public float getStrokeWidth() { return strokeWidth; } public abstract void select(); public abstract void deselect(); public void setMapView(MapView mv) { mapView = mv; } public void draw(final Canvas c) { int size = projectedPoints.length; // nothing to paint if (size < 2) { return; } final Projection pj = mapView.getProjection(); viewPortBounds = pj.fromPixelsToProjected(pj.getScreenRect()); double[] screenPoint; // points on screen double[] projectedPoint; // points from the points list path.rewind(); // Looping downward is the fastest loop you can do in Dalvik. for (int i = size - 1; i > 0; --i) { // every one but the 0th projectedPoint = projectedPoints[i]; screenPoint = pj.toMapPixelsTranslated(projectedPoint, tempPoint); clipOrDrawPath(path, projectedPoint, projectedPoints[i-1], screenPoint); } // that 0th projected point has no next projected point... projectedPoint = projectedPoints[0]; screenPoint = pj.toMapPixelsTranslated(projectedPoint, tempPoint); clipOrDrawPath(path, projectedPoint, null, screenPoint); pathLineToReady = false; paint.setStrokeWidth(strokeWidth / mapView.getScale()); c.drawPath(path, paint); } abstract void clipOrDrawPath(Path path, double[] projectedPoint, double[] nextProjectedPoint, double[] screenPoint1); }