Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I want to draw a Free Hand Polygon on the Map in Google Map V2.

This task was possible with Overlay Map V1 but Google Map has removed that class from V2. (Per this Google Map V2 has Remove Overlay Class).

Good Example for Google Map V1 to draw free style polygon.

In Map V2, we can draw a polygon programmatically with the help of Google Official Doc but what should a user do? I have found Unclear answer for Map V2

I started with simple Google Map & draw polygon to do this programmatically & it is working properly but now I am looking for how a user can draw? I don't want to draw based on the marker on the polygon.

// Instantiates a new Polygon object and adds points to define a rectangle PolygonOptions rectOptions = new PolygonOptions()               .add(new LatLng(37.35, -122.0),                    new LatLng(37.45, -122.0),                    new LatLng(37.45, -122.2),                    new LatLng(37.35, -122.2),                    new LatLng(37.35, -122.0));  // Get back the mutable Polygon Polygon polygon = myMap.addPolygon(rectOptions); 

I have done lots of Research and Development on this topic but didn't get a perfect way to implement such a thing in Map V2.

Some Questions

  • How to draw freestyle polygon in Map V2 (as we can do with Map V1)?
  • Is there any trick or alternative to achieve this? If yes how?
  • Can we get a touch event on the map & draw polygon?
  • Is it feasible in Map V2?
  • Is it possible with a touch event which returns array of lat-long?
  • How can I get Lat-long based on screen coordinates on setOnDragListener?

Each new version has something extra compared to the older one so I am expecting that I can achieve the same thing in Map v2 also.

I am not asking to give me some sample code or post your code, just some proper direction & documentation.

I have provided all documents and evidence I found during the Research and Development.

like image 456
Chintan Khetiya Avatar asked Jan 03 '14 10:01

Chintan Khetiya


People also ask

How do you draw on Google Maps Mobile?

My Maps in a browser: Plot the points > Draw a line > Add Driving Route. Use mouse to draw route. Mark current location: Create a map; type your address in the search bar. The Android My Maps app is no longer available; however, you can use My Maps in a mobile browser on your device.

What is polygon in Android map?

A polygon can be convex or concave, it may span the 180 meridian and it can have holes that are not filled in. It has the following properties: Outline. The outline is specified by a list of vertices in clockwise or counterclockwise order.


2 Answers

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.

like image 166
Chintan Khetiya Avatar answered Sep 23 '22 12:09

Chintan Khetiya


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

like image 31
AndroidHacker Avatar answered Sep 21 '22 12:09

AndroidHacker