How to draw free hand polygon in Google map V2 in Android? How to draw free hand polygon in Google map V2 in Android? android android

How to draw free hand polygon in Google map V2 in Android?


After spending a whole day in Rnd and testing some alternatives I have found a solution. Actually I have found two alternatives for the same issue but I would like to suggest the using of Alternative 2 because that is really very easy compared to Alternative 1.

Actually I have found Alternative 1 with the help of TheLittleNaruto , AndroidHacker and some other developers & Alternative 2 with the help of Khan so thanks to all.

Alternative 1

How to Draw Free style polygon in Map V2 (as we can do with Map V1) ? Is it feasible in Map V2 ?

Yes, that is feasible but you can't get directly OnTouch() & OnDraw() on the map. So we must have to think some other way to achieve this.

Is there any trick or alternative way to achieve this thing , if yes how ?

Yes, Google Map V2 doesn't support OnTouch() or OnDraw() on a Map using class="com.google.android.gms.maps.SupportMapFragment" so we have to plan for a custom Fragment.

Is it possible to return array of lat-long with touch event ?

Yes, if we create any custom map fragment and use it we can get that Touch or Drag event over the map.

How can I get Lat-long base on screen coordinates on setOnDragListener ?

setOnDragListener will return screen coordinates (x,y). Now for that, there are some techniques to convert (x,y) to LatLng and they include Projection along with Point & LatLng.

customMapFragment.setOnDragListener(new MapWrapperLayout.OnDragListener() {@Override    public void onDrag(MotionEvent motionEvent) {        Log.i("ON_DRAG", "X:" + String.valueOf(motionEvent.getX()));        Log.i("ON_DRAG", "Y:" + String.valueOf(motionEvent.getY()));        float x = motionEvent.getX(); // get screen x position or coordinate         float y = motionEvent.getY();  // get screen y position or coordinate         int x_co = Integer.parseInt(String.valueOf(Math.round(x))); // casting float to int         int y_co = Integer.parseInt(String.valueOf(Math.round(y))); // casting float to int         projection = mMap.getProjection(); // Will convert your x,y to LatLng        Point x_y_points = new Point(x_co, y_co);// accept int x,y value        LatLng latLng = mMap.getProjection().fromScreenLocation(x_y_points); // convert x,y to LatLng        latitude = latLng.latitude; // your latitude         longitude = latLng.longitude; // your longitude         Log.i("ON_DRAG", "lat:" + latitude);        Log.i("ON_DRAG", "long:" + longitude);        // Handle motion event:    }});

How does it work ?

As I have already mentioned before, we have to create a custom root view and using that we can get Touch or Drag Events over the map.

Step 1: We Create MySupportMapFragment extends SupportMapFragment and we will use that as our .xml file

 <fragment        android:id="@+id/map"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        class="pkg_name.MySupportMapFragment" /> 

Step 2: Create a MapWrapperLayout extends FrameLayout so that we can set a Touch or Drag listener inside and embed its view with map view. So, we need one Interface which we will use in Root_Map.java

MySupportMapFragment.Java

public class MySupportMapFragment extends SupportMapFragment {    public View mOriginalContentView;    public MapWrapperLayout mMapWrapperLayout;    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {        mOriginalContentView = super.onCreateView(inflater, parent, savedInstanceState);        mMapWrapperLayout = new MapWrapperLayout(getActivity());        mMapWrapperLayout.addView(mOriginalContentView);        return mMapWrapperLayout;    }    @Override    public View getView() {        return mOriginalContentView;    }    public void setOnDragListener(MapWrapperLayout.OnDragListener onDragListener) {        mMapWrapperLayout.setOnDragListener(onDragListener);    }}

MapWrapperLayout.java

    public class MapWrapperLayout extends FrameLayout {     private OnDragListener mOnDragListener;     public MapWrapperLayout(Context context) {         super(context);     }     public interface OnDragListener {         public void onDrag(MotionEvent motionEvent);     }     @Override     public boolean dispatchTouchEvent(MotionEvent ev) {         if (mOnDragListener != null) {             mOnDragListener.onDrag(ev);         }         return super.dispatchTouchEvent(ev);     }     public void setOnDragListener(OnDragListener mOnDragListener) {         this.mOnDragListener = mOnDragListener;     } }

Root_Map.Java

public class Root_Map extends FragmentActivity {    private GoogleMap mMap;    public static boolean mMapIsTouched = false;    MySupportMapFragment customMapFragment;    Projection projection;    public double latitude;    public double longitude;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.root_map);        MySupportMapFragment customMapFragment = ((MySupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map));        mMap = customMapFragment.getMap();        customMapFragment.setOnDragListener(new MapWrapperLayout.OnDragListener() {               @Override            public void onDrag(MotionEvent motionEvent) {                Log.i("ON_DRAG", "X:" + String.valueOf(motionEvent.getX()));                Log.i("ON_DRAG", "Y:" + String.valueOf(motionEvent.getY()));                float x = motionEvent.getX();                float y = motionEvent.getY();                int x_co = Integer.parseInt(String.valueOf(Math.round(x)));                int y_co = Integer.parseInt(String.valueOf(Math.round(y)));                projection = mMap.getProjection();                Point x_y_points = new Point(x_co, y_co);                LatLng latLng = mMap.getProjection().fromScreenLocation(x_y_points);                latitude = latLng.latitude;                longitude = latLng.longitude;                Log.i("ON_DRAG", "lat:" + latitude);                Log.i("ON_DRAG", "long:" + longitude);                // Handle motion event:            }        });    }}

Reference Link1 , Link2

Up to here I am able to get LatLong based on X,Y screen coordinates. Now I just have to store it in Array. That array will be used for drawing on the map and finally it will look like a free shape polygon.

enter image description here

I hope this will definitely help you.

Update:

Alternative 2

As we know, Frame layout is a transparent layout so I have achieved this using Frame Layout.In this case, there is no need to create a custom fragment. I have just used Frame Layout as root layout. So basically I will get Touch Events in the root layout and that will return screen coordinates, as we got in custom fragment previously.

Now, I have created a Button inside the "Free Draw". So when you click on that you can move your fingers on the map and draw a free hand polygon and that will disable your map being movable on screen. When you re-click the same button, the screen goes in ideal mode.

root_map.xml

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="fill_parent" >    <fragment        android:id="@+id/map"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        class="com.google.android.gms.maps.SupportMapFragment" />    <FrameLayout        android:id="@+id/fram_map"        android:layout_width="fill_parent"        android:layout_height="fill_parent" >        <Button            android:id="@+id/btn_draw_State"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="Free Draw" />    </FrameLayout></FrameLayout>

Root_Map.java

FrameLayout fram_map = (FrameLayout) findViewById(R.id.fram_map);Button btn_draw_State = (Button) findViewById(R.id.btn_draw_State);Boolean Is_MAP_Moveable = false; // to detect map is movable 

// Button will change Map movable state

btn_draw_State.setOnClickListener(new View.OnClickListener() {    @Override    public void onClick(View v) {        Is_MAP_Moveable = !Is_MAP_Moveable;    }});

Touch Click of Frame Layout and with the help of the do some task

fram_map.setOnTouchListener(new View.OnTouchListener() {     @Override    public boolean onTouch(View v, MotionEvent event) {        float x = event.getX();        float y = event.getY();        int x_co = Math.round(x);        int y_co = Math.round(y);        projection = mMap.getProjection();        Point x_y_points = new Point(x_co, y_co);        LatLng latLng = mMap.getProjection().fromScreenLocation(x_y_points);        latitude = latLng.latitude;        longitude = latLng.longitude;        int eventaction = event.getAction();        switch (eventaction) {            case MotionEvent.ACTION_DOWN:                // finger touches the screen                val.add(new LatLng(latitude, longitude));            case MotionEvent.ACTION_MOVE:                // finger moves on the screen                val.add(new LatLng(latitude, longitude));            case MotionEvent.ACTION_UP:                // finger leaves the screen                Draw_Map();                break;        }        return Is_MAP_Moveable;    }});

// Draw your map

public void Draw_Map() {    rectOptions = new PolygonOptions();    rectOptions.addAll(val);    rectOptions.strokeColor(Color.BLUE);    rectOptions.strokeWidth(7);    rectOptions.fillColor(Color.CYAN);    polygon = mMap.addPolygon(rectOptions);}

Yet, now you have to maintain your list while you draw, so you have to clear your previous list data.


Check this out .. I believe U are capable of showing Google Map v2

Check out "decodePoly" and "drawPath" method in AsyncTask

Main Imphasis in "drawPath" ..

PolylineOptions options = new PolylineOptions().width(5).color(Color.BLUE).geodesic(true);for (int z = 0; z < list.size(); z++) {    LatLng point = list.get(z);    options.add(point);}line = myMap.addPolyline(options);

Complete class for your reference ..

package com.example.androidhackergooglemap;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.UnsupportedEncodingException;import java.util.ArrayList;import java.util.List;import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.client.ClientProtocolException;import org.apache.http.client.methods.HttpPost;import org.apache.http.impl.client.DefaultHttpClient;import org.json.JSONArray;import org.json.JSONObject;import com.google.android.gms.maps.CameraUpdateFactory;import com.google.android.gms.maps.GoogleMap;import com.google.android.gms.maps.SupportMapFragment;import com.google.android.gms.maps.model.BitmapDescriptorFactory;import com.google.android.gms.maps.model.LatLng;import com.google.android.gms.maps.model.Marker;import com.google.android.gms.maps.model.MarkerOptions;import com.google.android.gms.maps.model.Polyline;import com.google.android.gms.maps.model.PolylineOptions;import android.app.ProgressDialog;import android.content.Context;import android.content.Intent;import android.graphics.Color;import android.location.Location;import android.location.LocationManager;import android.os.AsyncTask;import android.os.Bundle;import android.provider.Settings;import android.support.v4.app.FragmentActivity;import android.util.Log;import android.view.View;import android.view.View.OnClickListener;import android.widget.Toast;public class MainActivity extends FragmentActivity implements OnClickListener {    private GoogleMap myMap;    Polyline line;    Context context;    Location location;    boolean check_provider_enabled = false;    // Static LatLng    LatLng startLatLng = new LatLng(30.707104, 76.690749);    LatLng endLatLng = new LatLng(30.721419, 76.730017);    public void onCreate(Bundle bd) {        super.onCreate(bd);        setContentView(R.layout.activity_main);        context = MainActivity.this;        // GoogleMap myMap        myMap = ((SupportMapFragment) getSupportFragmentManager()            .findFragmentById(R.id.map)).getMap();        myMap.setMyLocationEnabled(true);        myMap.moveCamera(CameraUpdateFactory.newLatLng(startLatLng));        myMap.animateCamera(CameraUpdateFactory.zoomTo(12));        LocationManager service = (LocationManager) getSystemService(LOCATION_SERVICE);        boolean enabled = service.isProviderEnabled(LocationManager.GPS_PROVIDER);        location = service.getLastKnownLocation(LocationManager.GPS_PROVIDER);        // check if enabled and if not send user to the GSP settings        // Better solution would be to display a dialog and suggesting to         // go to the settings        if (!enabled) {            /*Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);            startActivity(intent);*/            Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);            startActivity(intent);            Toast.makeText(getApplicationContext(), "Enable GPS servcies to use this app.", Toast.LENGTH_LONG).show();        } else {            try {                String urlTopass = makeURL(startLatLng.latitude,                    startLatLng.longitude, endLatLng.latitude,                    endLatLng.longitude);                new connectAsyncTask(urlTopass).execute();            } catch (Exception e) {                e.printStackTrace();            }        }        // Now auto clicking the button        // btntemp.performClick();    }    private class connectAsyncTask extends AsyncTask < Void, Void, String > {        private ProgressDialog progressDialog;        String url;        connectAsyncTask(String urlPass) {            url = urlPass;        }        @Override        protected void onPreExecute() {            // TODO Auto-generated method stub            super.onPreExecute();            progressDialog = new ProgressDialog(context);            progressDialog.setMessage("Fetching route, Please wait...");            progressDialog.setIndeterminate(true);            progressDialog.show();        }        @Override        protected String doInBackground(Void...params) {            JSONParser jParser = new JSONParser();            String json = jParser.getJSONFromUrl(url);            return json;        }        @Override        protected void onPostExecute(String result) {            super.onPostExecute(result);            progressDialog.hide();            if (result != null) {                drawPath(result);            }        }    }    public String makeURL(double sourcelat, double sourcelog, double destlat,        double destlog) {        StringBuilder urlString = new StringBuilder();        urlString.append("http://maps.googleapis.com/maps/api/directions/json");        urlString.append("?origin="); // from        urlString.append(Double.toString(sourcelat));        urlString.append(",");        urlString.append(Double.toString(sourcelog));        urlString.append("&destination="); // to        urlString.append(Double.toString(destlat));        urlString.append(",");        urlString.append(Double.toString(destlog));        urlString.append("&sensor=false&mode=driving&alternatives=true");        return urlString.toString();    }    public class JSONParser {        InputStream is = null;        JSONObject jObj = null;        String json = "";        // constructor        public JSONParser() {}        public String getJSONFromUrl(String url) {            // Making HTTP request            try {                // defaultHttpClient                DefaultHttpClient httpClient = new DefaultHttpClient();                HttpPost httpPost = new HttpPost(url);                HttpResponse httpResponse = httpClient.execute(httpPost);                HttpEntity httpEntity = httpResponse.getEntity();                is = httpEntity.getContent();            } catch (UnsupportedEncodingException e) {                e.printStackTrace();            } catch (ClientProtocolException e) {                e.printStackTrace();            } catch (IOException e) {                e.printStackTrace();            }            try {                BufferedReader reader = new BufferedReader(                    new InputStreamReader(is, "iso-8859-1"), 8);                StringBuilder sb = new StringBuilder();                String line = null;                while ((line = reader.readLine()) != null) {                    sb.append(line + "\n");                }                json = sb.toString();                is.close();            } catch (Exception e) {                Log.e("Buffer Error", "Error converting result " + e.toString());            }            return json;        }    }    public void drawPath(String result) {        if (line != null) {            myMap.clear();        }        myMap.addMarker(new MarkerOptions().position(endLatLng).icon(            BitmapDescriptorFactory.fromResource(R.drawable.ic_launcher)));        myMap.addMarker(new MarkerOptions().position(startLatLng).icon(            BitmapDescriptorFactory.fromResource(R.drawable.ic_launcher)));        try {            // Tranform the string into a json object            final JSONObject json = new JSONObject(result);            JSONArray routeArray = json.getJSONArray("routes");            JSONObject routes = routeArray.getJSONObject(0);            JSONObject overviewPolylines = routes                .getJSONObject("overview_polyline");            String encodedString = overviewPolylines.getString("points");            List < LatLng > list = decodePoly(encodedString);            PolylineOptions options = new PolylineOptions().width(5).color(Color.BLUE).geodesic(true);            for (int z = 0; z < list.size(); z++) {                LatLng point = list.get(z);                options.add(point);            }            line = myMap.addPolyline(options);            /*for (int z = 0; z < list.size() - 1; z++) {                LatLng src = list.get(z);                LatLng dest = list.get(z + 1);                line = myMap.addPolyline(new PolylineOptions()                        .add(new LatLng(src.latitude, src.longitude),                                new LatLng(dest.latitude, dest.longitude))                        .width(5).color(Color.BLUE).geodesic(true));            }*/        } catch (Exception e) {            e.printStackTrace();        }    }    private List < LatLng > decodePoly(String encoded) {        List < LatLng > poly = new ArrayList < LatLng > ();        int index = 0, len = encoded.length();        int lat = 0, lng = 0;        while (index < len) {            int b, shift = 0, result = 0;            do {                b = encoded.charAt(index++) - 63;                result |= (b & 0x1f) << shift;                shift += 5;            } while (b >= 0x20);            int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));            lat += dlat;            shift = 0;            result = 0;            do {                b = encoded.charAt(index++) - 63;                result |= (b & 0x1f) << shift;                shift += 5;            } while (b >= 0x20);            int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));            lng += dlng;            LatLng p = new LatLng((((double) lat / 1E5)),                (((double) lng / 1E5)));            poly.add(p);        }        return poly;    }    @Override    public void onClick(View arg0) {        // TODO Auto-generated method stub    }}

Hope it helps. Cheers!

UPDATE

check out this .. Creating OnDragListener for Google Map v2 Fragment

Also check this one ..How to draw a shape on the map fragment by touching it using google map V2

Some more reference .. How to get screen coordinates from marker in google maps v2 android


So we do have some solution for free hand draw on map v2.Implement GoogleMap.OnMarkerDragListener in your map activity . It'll override onMarkerDrag function.

@Overridepublic void onMarkerDrag(Marker marker) {   //add the marker's latlng in a arraylist of LatLng and pass it to the loop    for (int i = 0; i < arraylistoflatlng.size(); i++) {         myMap.addPolyline(new PolylineOptions()        .addAll(arraylistoflatlng)        .width(5)        .color(Color.RED));    }    }

you can pass some kind of hack for free hand like as soon as the user will touch the map, you'll have to detect that coordinates and pass it to the the onMarkerDrag . As you'll have to use the area's information for further process. For touch event you can implement GoogleMap.OnMapClickListener and get the coordinates from it's parameter. Hope this will help :)