Android Open Source - Mapyst Route Map Overlay






From Project

Back to project page Mapyst.

License

The source code is released under:

Apache License

If you think the Android project Mapyst listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (C) 2013 Mapyst// w  w w  . j  a  va2 s .  c  o  m
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.mapyst.android.ui;

import java.io.IOException;
import java.util.ArrayList;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.DashPathEffect;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Path.FillType;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Shader.TileMode;
import android.graphics.Typeface;
import android.os.SystemClock;
import android.view.MotionEvent;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;
import com.mapyst.android.MainScreen;
import com.mapyst.android.Mapyst;
import com.mapyst.campus.Campus;
import com.mapyst.route.Direction;
import com.mapyst.route.LatLngPoint;
import com.mapyst.route.Waypoint2D;

public class RouteMapOverlay extends Overlay {

  private Paint paint;
  private Paint insidePaint;

  private Paint bitmapPaint;
  private Paint shadePaint;

  private RectF destination;
  private Projection projection;
  private Bitmap blueprintBitmap;
  private int floor, building;

  private ArrayList<Icon> icons;

  private MainScreen main;

  // the list of directions for the route that is currently being displayed
  private Direction[] directions;
  // indicates the index of the current direction (the direction highlighted
  // with the blue line)
  private int curDir;

  // single points used for one point mode (just displaying a point on the
  // map, not a route)
  private GeoPoint singleGeoPoint;
  private Point singlePixelPoint;

  // stores an array of points for each direction
  private GeoPoint[][] geoPoints;
  private Point[][] pixelPoints;

  private int mode;

  private int screenWidth;
  private int screenHeight;

  private float gradient;

  public static final int ROUTE = 0;
  public static final int POINT = 1;
  public static final int BLANK = 2;

  private static final int PATH_COLOR1 = Color.BLUE;
  private static final int PATH_COLOR2 = Color.rgb(152, 224, 224);
  private static final int OUTSIDE_COLOR = Color.BLACK;

  public static final int UP = 1;
  public static final int DOWN = -1;

  public static final int DOWN_ARROW = 1;
  public static final int UP_ARROW = 0;
  private static final float GRADIENT_MAX = 100.0f;
  private static final float GRADIENT_SPACE = 30.0f;

  private int dashPathBase = 20;

  private Mapyst app;

  public RouteMapOverlay(MainScreen main, Mapyst app, Projection projection, int mode, int screenWidth, int screenHeight) {
    this.app = app;
    this.main = main;

    this.screenWidth = screenWidth;
    this.screenHeight = screenHeight;

    setupPaints();

    this.projection = projection;
    this.mode = mode;

    floor = -1;
    building = 0;
    curDir = 0;
  }

  public RouteMapOverlay(MainScreen main, Mapyst app, Direction[] directions,
      Projection projection, int screenWidth, int screenHeight) {

    this(main, app, projection, ROUTE, screenWidth, screenHeight);

    this.directions = directions;
    icons = Icon.collectIcons(main, directions);

    calculateGeoPoints();
    updateBitmaps();
  }

  public RouteMapOverlay(MainScreen main, Mapyst app, Waypoint2D waypoint,
      int icon, Projection projection, int screenWidth, int screenHeight) {

    this(main, app, projection, POINT, screenWidth, screenHeight);

    setPoint(waypoint, icon);
  }

  private void setupPaints() {
    paint = new Paint();
    paint.setAntiAlias(true);
    paint.setStrokeWidth(8.0f);
    paint.setStyle(Style.STROKE);
    paint.setPathEffect(new CornerPathEffect(5.0f));
    paint.setColor(OUTSIDE_COLOR);

    bitmapPaint = new Paint();

    shadePaint = new Paint();
    shadePaint.setColor(Color.argb(255 / 3, 89, 89, 89));

    insidePaint = new Paint();
    insidePaint.setAntiAlias(true);
    insidePaint.setStrokeWidth(8.0f);
    insidePaint.setStyle(Style.STROKE);
    insidePaint.setPathEffect(new CornerPathEffect(5.0f));
    insidePaint.setColor(OUTSIDE_COLOR);
  }

  private void calculateGeoPoints() {
    pixelPoints = new Point[directions.length][];
    geoPoints = new GeoPoint[directions.length][];
    for (int i = 0; i < directions.length; i++) {
      if (directions[i].getType() == Direction.SAME_FLOOR) {
        LatLngPoint[] dirLatLngPoints = directions[i].getPoints();
        pixelPoints[i] = new Point[dirLatLngPoints.length];

        geoPoints[i] = new GeoPoint[dirLatLngPoints.length];
        for (int j = 0; j < dirLatLngPoints.length; j++) {
          geoPoints[i][j] = DrawingHelpers.convertPointToGeo(dirLatLngPoints[j]);
        }
      } else {
        LatLngPoint[] dirLatLngPoints = directions[i].getPoints();
        pixelPoints[i] = new Point[1];
        geoPoints[i] = new GeoPoint[1];
        geoPoints[i][0] = DrawingHelpers.convertPointToGeo(dirLatLngPoints[0]);
      }
    }
  }

  // path dash works with absolute pixel size, but we want
  // that to change, and dash size to be relative with zoom
  // level
  // - Evan
  public void setDashPathInnerPaint(int dirIndex) {
    float scalar = dashPathBase;

    Point[] curPixelPoints = pixelPoints[dirIndex];

    double pathLength = DrawingHelpers.distance(curPixelPoints[0], curPixelPoints[curPixelPoints.length - 1]);

    scalar = (float) (pathLength / scalar);

    DashPathEffect dashPath = new DashPathEffect(new float[] { scalar, scalar }, 1);
    insidePaint.setPathEffect(dashPath);
  }

  // this is where we're going to check if we should change the current
  // direction, based on the distance from the center point to a path
  // TODO: add a bool that toggles this mode, with a setting in the ui
  // - Evan
  @Override
  public boolean onTouchEvent(MotionEvent event, MapView mapView) {

    if (directions == null)
      return super.onTouchEvent(event, mapView);

    Point center = new Point(mapView.getWidth() / 2, mapView.getHeight() / 2); // simple calc of center pixel

    int closestDir = curDir; // assume its the same
    double bestMin = Double.MAX_VALUE; // closest direction so far

    // the algorithm is as follows: find the shortest distance from the
    // center
    // of the screen to each direction. Then, change to the direction that
    // is
    // closest to the center

    for (int i = 0; i < directions.length; i++) {
      Point[] dirPixelPoints = pixelPoints[i];
      if (directions[i].getType() == Direction.SAME_FLOOR) {
        double avgDist = (DrawingHelpers.distance(dirPixelPoints[0],center)
            + DrawingHelpers.distance(dirPixelPoints[dirPixelPoints.length / 2],center) + DrawingHelpers.distance(
            dirPixelPoints[dirPixelPoints.length - 1], center)) / 2.0;
        if (avgDist < bestMin) {
          bestMin = avgDist;
          closestDir = i;
        }
      }
    }

    // only changes the direction if the direction being automatically
    // switched to is and outside direction
    if (curDir != closestDir && // inefficient to setui if we don't need to
        app.campus.buildingIsOutside(directions[closestDir].getBuilding()) && // only change if moving between outside points
        app.campus.buildingIsOutside(directions[curDir].getBuilding())) {
      if (main != null) {
        main.setUI(closestDir, false); // calling this, we set the bitmaps + the icons
      } else {
        main.setUI(closestDir, false);
      }
    }
    return super.onTouchEvent(event, mapView); // not stealing the event
  }

  public Shader getPathShader(Point[] curPixelPoints) {
    float speed = 90.f; // INVERSE!! HIGHER VALUES = SLOWER SPEED
    gradient = (SystemClock.uptimeMillis() % (GRADIENT_SPACE * 2 * speed)) / speed + GRADIENT_MAX - GRADIENT_SPACE * 2;
    float dx = (int) (curPixelPoints[curPixelPoints.length - 1].x - curPixelPoints[0].x);
    float dy = (int) (curPixelPoints[curPixelPoints.length - 1].y - curPixelPoints[0].y);
    Shader shader = new LinearGradient(((gradient - GRADIENT_SPACE) / GRADIENT_MAX) * (dx)
            + curPixelPoints[0].x, ((gradient - GRADIENT_SPACE) / GRADIENT_MAX) * (dy)
            + curPixelPoints[0].y, (gradient / GRADIENT_MAX) * (dx)
            + curPixelPoints[0].x, (gradient / GRADIENT_MAX) * (dy)
            + curPixelPoints[0].y, PATH_COLOR1, PATH_COLOR2, TileMode.MIRROR);
    return shader;
  }

  public void drawPath(Canvas canvas) {
    Point[] curPixelPoints = pixelPoints[curDir];

    // setup gradient
    paint.setShader(getPathShader(curPixelPoints));

    // draw path
    Path path = new Path();
    path.setFillType(FillType.WINDING);
    path.moveTo(curPixelPoints[0].x, curPixelPoints[0].y);
    for (int i = 1; i < curPixelPoints.length; i += 1)
      path.lineTo(curPixelPoints[i].x, curPixelPoints[i].y);
    canvas.drawPath(path, paint);
  }

  public static ArrayList<Rect> debugRects = new ArrayList<Rect>();

  private static Paint debugPaint;

  private static void drawDebugRects(Canvas canvas) {
    if (debugPaint == null) {
      debugPaint = new Paint();
      debugPaint.setColor(Color.argb(100, 0, 0, 0));
    }
    if (debugRects != null) {
      for (int i = 0; i < debugRects.size(); i++) {
        canvas.drawRect(debugRects.get(i), debugPaint);
      }
    }
  }

  /*
   * Function: draw Draws the outside points and arcs over the Google MapView.
   */
  public void draw(Canvas canvas, MapView mapView, boolean shadow) {
    super.draw(canvas, mapView, shadow);

    drawDebugRects(canvas);

    applyProjection();

    if (mode == ROUTE) {
      // if inside draw gray background and floor's blueprint
      if (!app.campus.buildingIsOutside(building)) {
        // canvas.drawARGB(255/3, 89, 89, 89);//33% opaque dark gray
        // background
        // canvas.drawRect(entireScreenRect, shadePaint);
        canvas.drawBitmap(blueprintBitmap, null, destination, bitmapPaint);
      }
      drawDirections(canvas);

      drawPath(canvas);

      // if (main != null)
      // main.getMapView().postInvalidateDelayed(ANIMATION_DELAY);
    } else if (mode == POINT && singlePixelPoint != null) {
      if (!app.campus.buildingIsOutside(building)) {
        canvas.drawARGB(255 / 3, 89, 89, 89);// 33% opaque dark gray
                            // background
        canvas.drawBitmap(blueprintBitmap, null, destination, paint);
      }
    }

    drawIcons(canvas);

    if (directions != null && directions[curDir].getType() != Direction.SAME_FLOOR) {
      drawTransitionDirection(canvas);
    }

  }

  private void drawTransitionDirection(Canvas canvas) {
    Paint rectPaint = new Paint();
    rectPaint.setColor(Color.argb(200, 89, 89, 89));

    float rectWidth = screenWidth * (2.0f / 3.0f);
    float rectHeight = screenHeight * (1.0f / 4.0f);
    RectF rect = new RectF(screenWidth / 2.0f - rectWidth / 2.0f, 75, screenWidth / 2.0f + rectWidth / 2.0f, 75 + rectHeight);

    canvas.drawRoundRect(rect, 15, 15, rectPaint);

    // draws border
    rectPaint.setColor(Color.argb(255, 0, 0, 0));
    rectPaint.setStyle(Paint.Style.STROKE);
    rectPaint.setStrokeWidth(4);
    canvas.drawRoundRect(rect, 15, 15, rectPaint);

    Paint textPaint = new Paint();
    textPaint.setColor(Color.WHITE);
    textPaint.setTypeface(Typeface.create("Arial", Typeface.BOLD));
    textPaint.setTextSize(screenHeight / 10);

    int startFloorIndex = directions[curDir].getStart().getFloorIndex();
    int startBuildingIndex = directions[curDir].getStart().getBuildingIndex();
    String startFloorText = app.campus.getFloor(startBuildingIndex, startFloorIndex).name;

    int endFloorIndex = directions[curDir].getEnd().getFloorIndex();
    int endBuildingIndex = directions[curDir].getEnd().getBuildingIndex();
    String endFloorText = app.campus.getFloor(endBuildingIndex, endFloorIndex).name;

    float row1Width = textPaint.measureText("Floor");
    float row2Width = textPaint.measureText(startFloorText + " to "  + endFloorText);

    Rect bounds = new Rect();

    textPaint.getTextBounds("Floor", 0, 5, bounds);

    int textHeight = bounds.height();

    canvas.drawText("Floor", screenWidth / 2 - row1Width / 2, rect.centerY() - textHeight * .2f, textPaint);
    canvas.drawText(startFloorText + " to " + endFloorText, screenWidth / 2  - row2Width / 2, rect.centerY() + textHeight * 1.2f, textPaint);
  }

  private void drawIcons(Canvas canvas) {
    for (int i = 0; i < icons.size(); i++) {
      Point point = new Point();
      projection.toPixels(icons.get(i).geoPoint, point);
      RectF rect = new RectF(point.x - 20, point.y - 20, point.x + 20, point.y + 20);

      if (singleGeoPoint == null && curDir != icons.get(i).dirIndex) {
        paint.setAlpha(150);
        canvas.drawBitmap(icons.get(i).bitmap, null, rect, paint);
        paint.setAlpha(255);
      } else
        canvas.drawBitmap(icons.get(i).bitmap, null, rect, paint);
    }
  }

  private void drawDirections(Canvas canvas) {
    paint.setShader(null);
    for (int i = 0; i < directions.length; i++) {
      Point[] dirPixelPoints = pixelPoints[i];
      Path path = new Path();
      path.setFillType(FillType.WINDING);
      path.moveTo(dirPixelPoints[0].x, dirPixelPoints[0].y);
      for (int j = 1; j < dirPixelPoints.length; j++)
        path.lineTo(dirPixelPoints[j].x, dirPixelPoints[j].y);

      if (directions[i].isOutside(app.campus) && i != curDir) {
        canvas.drawPath(path, paint);
      } else if (!directions[i].isOutside(app.campus) && i != curDir) {
        setDashPathInnerPaint(i);
        canvas.drawPath(path, insidePaint);
      }
    }
  }

  private void updateBitmaps() {
    if (blueprintBitmap != null)
      blueprintBitmap.recycle();

    if (!app.campus.buildingIsOutside(building)) {
      String path;
      try {
        path = "blueprints/"
            + app.campus.getFloorFile(building, floor, "png");
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        blueprintBitmap = BitmapFactory.decodeStream(Campus.fileHandler.getInputStream(path), null,  options);

      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }

  // TODO this function is called too often, only call when google maps moves
  private void applyProjection() {
    if (mode != BLANK && !app.campus.buildingIsOutside(building)) {
      Point topLeft = new Point();
      Point botRight = new Point();
      projection.toPixels(DrawingHelpers.convertPointToGeo(app.campus.getFloor(building, floor).northWest), topLeft);
      projection.toPixels(DrawingHelpers.convertPointToGeo(app.campus.getFloor(building, floor).southEast), botRight);
      destination = new RectF(topLeft.x, topLeft.y, botRight.x, botRight.y);
    }

    if (mode == ROUTE) {
      for (int i = 0; i < directions.length; i++) {
        if (i == curDir) {
          GeoPoint[] curGeoPoints = geoPoints[i];
          Point[] curPixelPoints = pixelPoints[i];
          for (int j = 0; j < curGeoPoints.length; j++) {
            if (curPixelPoints[j] == null)
              curPixelPoints[j] = new Point();
            projection.toPixels(curGeoPoints[j], curPixelPoints[j]);
          }
        } else {
          GeoPoint[] dirGeoPoints = geoPoints[i];
          Point[] dirPixelsPoints = pixelPoints[i];
          for (int j = 0; j < dirGeoPoints.length; j++) {
            if (dirPixelsPoints[j] == null)
              dirPixelsPoints[j] = new Point();
            projection.toPixels(dirGeoPoints[j], dirPixelsPoints[j]);
          }
        }
      }
    } else if (mode == POINT) {
      if (singlePixelPoint == null)
        singlePixelPoint = new Point();
      projection.toPixels(singleGeoPoint, singlePixelPoint);
    }
  }

  public void setFloor(int curDir) {
    this.curDir = curDir;
    this.floor = directions[curDir].getFloor();
    this.building = directions[curDir].getBuilding();

    updateBitmaps();
  }

  public void setPoint(Waypoint2D waypoint, int icon) {
    this.singleGeoPoint = DrawingHelpers.convertPointToGeo(waypoint.getPoint());
    this.building = waypoint.getId().getBuildingIndex();
    this.floor = waypoint.getId().getFloorIndex();

    Context context = main;

    icons = new ArrayList<Icon>();
    Icon newIcon = new Icon();
    newIcon.bitmap = BitmapFactory.decodeResource(context.getResources(), icon);
    newIcon.geoPoint = singleGeoPoint;
    icons.add(newIcon);

    updateBitmaps();
  }

  public void recycleBitmap() {
    if (blueprintBitmap != null)
      blueprintBitmap.recycle();
    System.gc();
  }
}




Java Source Code List

com.mapyst.FileHandlerInterface.java
com.mapyst.android.AndroidFileHandler.java
com.mapyst.android.CampusLoader.java
com.mapyst.android.Compass.java
com.mapyst.android.DirectionsList.java
com.mapyst.android.Images.java
com.mapyst.android.LocationFinder.java
com.mapyst.android.MainScreen.java
com.mapyst.android.Mapyst.java
com.mapyst.android.Settings.java
com.mapyst.android.Splash.java
com.mapyst.android.asynctask.CampusLoaderTaskPrefs.java
com.mapyst.android.asynctask.CampusLoaderTask.java
com.mapyst.android.asynctask.RouteMakerTaskPrefs.java
com.mapyst.android.asynctask.RouteMakerTask.java
com.mapyst.android.ui.CenteredToastFactory.java
com.mapyst.android.ui.CompassOverlay.java
com.mapyst.android.ui.DirectionIcon.java
com.mapyst.android.ui.DirectionsListAdapter.java
com.mapyst.android.ui.DirectionsListItem.java
com.mapyst.android.ui.DrawingHelpers.java
com.mapyst.android.ui.Icon.java
com.mapyst.android.ui.LocationsAdapter.java
com.mapyst.android.ui.LocationsListView.java
com.mapyst.android.ui.RouteMapOverlay.java
com.mapyst.android.ui.SlidingScrollView.java
com.mapyst.android.ui.map.AnimatedMapZoomer.java
com.mapyst.android.ui.map.LocationBalloon.java
com.mapyst.android.ui.map.LocationChooserOverlay.java
com.mapyst.android.ui.map.MapUtils.java
com.mapyst.android.ui.map.MapViewLimiter.java
com.mapyst.android.ui.map.MapViewMover.java
com.mapyst.android.ui.map.OnMapTouchLimiterListener.java
com.mapyst.android.ui.map.PriorityMapView.java
com.mapyst.android.ui.map.ViewItemOverlay.java
com.mapyst.campus.Building.java
com.mapyst.campus.Campus.java
com.mapyst.campus.Floor.java
com.mapyst.campus.ListOfCampuses.java
com.mapyst.campus.Location_Type.java
com.mapyst.campus.Location.java
com.mapyst.route.Arc.java
com.mapyst.route.DataParser.java
com.mapyst.route.Direction.java
com.mapyst.route.DistanceCalculator.java
com.mapyst.route.GraphNode.java
com.mapyst.route.InterpretResult.java
com.mapyst.route.InterpretedInfo.java
com.mapyst.route.Interpreter.java
com.mapyst.route.LatLngPoint.java
com.mapyst.route.Prioritizable.java
com.mapyst.route.PriorityQ.java
com.mapyst.route.RouteFinder.java
com.mapyst.route.RoutePreferences.java
com.mapyst.route.Route.java
com.mapyst.route.ShortestPath.java
com.mapyst.route.Waypoint2D.java
com.mapyst.route.WaypointID.java
com.markupartist.android.widget.ActionBar.java
com.markupartist.android.widget.ScrollingTextView.java