Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to add arrows heads on poly line using Android maps V2 [duplicate]

How to draw poly line with arrow head(It indicates the direction) using Android Maps V2 API. In Android Maps Documentation this option is not available.Is it possible to add arrows on polyline?

like image 915
Ramprasad Avatar asked Mar 26 '13 06:03

Ramprasad


3 Answers

Here's a working example I've developed using the same concept as what Doug suggested, by taking a JavaScript example (http://econym.org.uk/gmap/example_arrows.htm) and converting it to Java code. For handiness it uses images from Google Maps servers, but these could images of your design or could be scraped off the web and stored locally in the app. I'm downloading these on main thread for sake of demonstration, but don't do that if using in a live app!!

The key difference to the JavaScript example though is that you have to project the arrow head image onto a larger image four times the size, working out where to translate the image to based on the bearing from A to B, and finally center that image over the existing B marker by adding another anchored marker with your larger image for an icon.

Firstly add your polylines:

PolylineOptions polylines = new PolylineOptions();

LatLng from = new LatLng(f.getLatitude(), f.getLongitude());
LatLng to = new LatLng(t.getLatitude(), t.getLongitude());

polylines.add(from, to).color(polyColor).width(2);

mMap.addPolyline(polylines);

DrawArrowHead(mMap, from, to);

Then add your arrow heads:

private final double degreesPerRadian = 180.0 / Math.PI;

private void DrawArrowHead(GoogleMap mMap, LatLng from, LatLng to){
    // obtain the bearing between the last two points
    double bearing = GetBearing(from, to);

    // round it to a multiple of 3 and cast out 120s
    double adjBearing = Math.round(bearing / 3) * 3;
    while (adjBearing >= 120) {
        adjBearing -= 120;
    }

    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(policy); 

    // Get the corresponding triangle marker from Google        
    URL url;
    Bitmap image = null;

    try {
        url = new URL("http://www.google.com/intl/en_ALL/mapfiles/dir_" + String.valueOf((int)adjBearing) + ".png");
        try {
            image = BitmapFactory.decodeStream(url.openConnection().getInputStream());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    } catch (MalformedURLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    if (image != null){

        // Anchor is ratio in range [0..1] so value of 0.5 on x and y will center the marker image on the lat/long
        float anchorX = 0.5f;
        float anchorY = 0.5f;

        int offsetX = 0;
        int offsetY = 0;

        // images are 24px x 24px
        // so transformed image will be 48px x 48px

        //315 range -- 22.5 either side of 315
        if (bearing >= 292.5 && bearing < 335.5){
            offsetX = 24;
            offsetY = 24;
        }
        //270 range
        else if (bearing >= 247.5 && bearing < 292.5){
            offsetX = 24;
            offsetY = 12;
        }
        //225 range
        else if (bearing >= 202.5 && bearing < 247.5){
            offsetX = 24;
            offsetY = 0;
        }
        //180 range
        else if (bearing >= 157.5 && bearing < 202.5){
            offsetX = 12;
            offsetY = 0;
        }
        //135 range
        else if (bearing >= 112.5 && bearing < 157.5){
            offsetX = 0;
            offsetY = 0;
        }
        //90 range
        else if (bearing >= 67.5 && bearing < 112.5){
            offsetX = 0;
            offsetY = 12;
        }
        //45 range
        else if (bearing >= 22.5 && bearing < 67.5){
            offsetX = 0;
            offsetY = 24;
        }
        //0 range - 335.5 - 22.5
        else {
            offsetX = 12;
            offsetY = 24;
        }

        Bitmap wideBmp;
        Canvas wideBmpCanvas;
        Rect src, dest;

        // Create larger bitmap 4 times the size of arrow head image
        wideBmp = Bitmap.createBitmap(image.getWidth() * 2, image.getHeight() * 2, image.getConfig());

        wideBmpCanvas = new Canvas(wideBmp); 

        src = new Rect(0, 0, image.getWidth(), image.getHeight());
        dest = new Rect(src); 
        dest.offset(offsetX, offsetY); 

        wideBmpCanvas.drawBitmap(image, src, dest, null);

        mMap.addMarker(new MarkerOptions()
        .position(to)
        .icon(BitmapDescriptorFactory.fromBitmap(wideBmp))
        .anchor(anchorX, anchorY));
    }
}

private double GetBearing(LatLng from, LatLng to){
    double lat1 = from.latitude * Math.PI / 180.0;
    double lon1 = from.longitude * Math.PI / 180.0;
    double lat2 = to.latitude * Math.PI / 180.0;
    double lon2 = to.longitude * Math.PI / 180.0;

    // Compute the angle.
    double angle = - Math.atan2( Math.sin( lon1 - lon2 ) * Math.cos( lat2 ), Math.cos( lat1 ) * Math.sin( lat2 ) - Math.sin( lat1 ) * Math.cos( lat2 ) * Math.cos( lon1 - lon2 ) );

    if (angle < 0.0)
        angle += Math.PI * 2.0;

    // And convert result to degrees.
    angle = angle * degreesPerRadian;

    return angle;
}
like image 69
Breeno Avatar answered Nov 09 '22 11:11

Breeno


As of Feb. 15, 2017 release of Android Maps API v2 you can now add custom Line caps to the end of polylines on Android Maps API v2, which can be arrows.

From the Line caps documentation:

The following snippet specifies a custom bitmap for the end cap:

mPolyline.setEndCap(
        new CustomCap(BitmapDescriptorFactory.fromResource(R.drawable.arrow),
                16));

When you use a custom bitmap, you should specify a reference stroke width in pixels. The API scales the bitmap accordingly. The reference stroke width is the stroke width that you used when designing the bitmap image for the cap, at the original dimension of the image. The default reference stroke width is 10 pixels. Hint: To determine the reference stroke width, open your bitmap image at 100% zoom in an image editor, and plot the desired width of the line stroke relative to the image.

If you use BitmapDescriptorFactory.fromResource() to create the bitmap, make sure you use a density-independent resource (nodpi).

The post from Google for the related resolved issue here says:

We have added the ability to customize the start and end caps of Polylines with a custom bitmap. Using this you will be able to add arrowheads to your Polylines.

See information about Line Caps in the Shapes Guide here: https://developers.google.com/maps/documentation/android-api/shapes#line_caps

See an example in the new Polylines and Polygons tutorial here: https://developers.google.com/maps/documentation/android-api/polygon-tutorial#add_custom_styling_to_your_polyline

See the release notes here: https://developers.google.com/maps/documentation/android-api/releases#february_15_2017

See the blog post here: https://maps-apis.googleblog.com/2017/02/styling-and-custom-data-for-polylines.html

like image 4
Sean Barbeau Avatar answered Nov 09 '22 09:11

Sean Barbeau


I've not tried this but I think that it should be possible by using markers. You will need to create a series of, say 20, small arrows each pointing in different directions, 18, 36, 54 degrees etc. Then either when constructing the polyline or, probably better, as a separate process after the polyline has been built, run through all of the coordinates at whatever spacing you want and, at each chosen point get the bearing using locationn.getbearing and, from that bearing, determine which directional arrow to use as a marker and lay the appropriate marker at that point

like image 1
Doug Conran Avatar answered Nov 09 '22 11:11

Doug Conran