Wednesday, January 12, 2011

Create a Radius Around a Point in Google Map

Question : How do I create a 9000 meters buffer radius from a point and display placemarks that intersect that buffer.
Answer : Google Map API for android has a class that reads and draw your current location in the map.
That class is "MyLocationOverlay". The class has a "draw" method that will be called everytime user pan or zoom on the map.
So to draw a circle radius of your current location, we need to extends this class and override its draw method.
package com.tukangandroid.tutorial;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Paint.Style;
import android.graphics.drawable.Drawable;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.MyLocationOverlay;
import com.google.android.maps.Projection;
public class MyOwnLocationOverlay extends MyLocationOverlay{
private MapView mapView;
private Paint circlePainter;
private Point screenCurrentPoint;
private GeoPoint geoCurrentPoint;
private int meters;
public MyOwnLocationOverlay(Context context, MapView mapView) {
super(context, mapView);
this.mapView = mapView;
}
// This method is used to get user submitted radius from our application
public void setMeters(int meters) {
this.meters = meters;
}
@Override
public synchronized boolean draw(Canvas canvas, MapView mapView,
boolean shadow, long when) {
// Set the painter to paint our circle. setColor = blue, setAlpha = 70 so the background
// can still be seen. Feel free to change these settings
circlePainter = new Paint();
circlePainter.setAntiAlias(true);
circlePainter.setStrokeWidth(2.0f);
circlePainter.setColor(0xff6666ff);
circlePainter.setStyle(Style.FILL_AND_STROKE);
circlePainter.setAlpha(70);
// Get projection from the mapView.
Projection projection = mapView.getProjection();
// Get current location
geoCurrentPoint = getMyLocation();
screenCurrentPoint = new Point();
// Project the gps coordinate to screen coordinate
projection.toPixels(geoCurrentPoint, screenCurrentPoint);
int radius = metersToRadius(geoCurrentPoint.getLatitudeE6() /1000000);
// draw the blue circle
canvas.drawCircle(screenCurrentPoint.x, screenCurrentPoint.y, radius, circlePainter);
return super.draw(canvas, mapView, shadow, when);
}
// hack to get more accurate radius, because the accuracy is changing as the location
// getting further away from the equator
public int metersToRadius(double latitude) {
return (int) (mapView.getProjection().metersToEquatorPixels(meters) * (1/ Math.cos(Math.toRadians(latitude))));
}
}

Then, we create our view, one map view, one textbox and one button

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="1">
<TableRow>
<EditText
android:id="@+id/txtRadius"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Radius"
/>
<Button android:id="@+id/btnSearch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Search" />
</TableRow>
<TableRow>
<com.google.android.maps.MapView
android:layout_span="2"
android:id="@+id/mapview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="true"
android:apiKey="your_api_key_here"
/>
</TableRow>
</TableLayout>
view raw main.xml hosted with ❤ by GitHub

After that, we need to create our main class. TukangAndroidApp.java
package com.tukangandroid.tutorial;
import java.util.List;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
public class TukangAndroidApp extends MapActivity{
private MapView mapView;
private List<Overlay> mapOverlays;
private MyOwnLocationOverlay myLocationOverlay;
private MapController mapController;
private TextView txtRadius;
private Button btnSearch;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mapView = (MapView) findViewById(R.id.mapview);
mapView.setBuiltInZoomControls(true);
mapController = mapView.getController();
btnSearch = (Button) findViewById(R.id.btnSearch);
btnSearch.setOnClickListener(new SearchListener());
txtRadius = (TextView) findViewById(R.id.txtRadius);
}
private class SearchListener implements android.view.View.OnClickListener {
@Override
public void onClick(View v) {
mapView.getOverlays().clear();
mapOverlays = mapView.getOverlays();
myLocationOverlay = new MyOwnLocationOverlay(TukangAndroidApp.this, mapView);
myLocationOverlay.setMeters(Integer.parseInt(txtRadius.getText().toString()));
myLocationOverlay.enableCompass();
myLocationOverlay.enableMyLocation();
myLocationOverlay.runOnFirstFix(new Runnable() {
public void run() {
mapController.animateTo(myLocationOverlay.getMyLocation());
}
});
mapView.getOverlays().add(myLocationOverlay);
displayResults();
}
}
private void displayResults() {
}
@Override
protected boolean isRouteDisplayed() {
return false;
}
}

Dont forget to edit AndroidManifest.xml to add permissions and google map library
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tukangandroid.tutorial"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<uses-library android:name="com.google.android.maps" />
<activity android:name=".TukangAndroidApp"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>


Run your application.
Tips : Set the GPS location from the DDMS first before you press Search, otherwise you will encounter NullPointerException
If all goes well, your application will be like this

Notice in TukangAndroidApp.java, our displayResult method didnt do anything, so we will implement the method to return some placemarks that intersect our buffer.
First, we should extends ItemizedOverlay class to create our placemark
package com.tukangandroid.tutorial;
import java.util.ArrayList;
import android.graphics.drawable.Drawable;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.OverlayItem;
public class HelloItemizedOverlay extends ItemizedOverlay<OverlayItem> {
private ArrayList<OverlayItem> mOverlays = new ArrayList<OverlayItem>();
public HelloItemizedOverlay(Drawable defaultMarker) {
super(boundCenterBottom(defaultMarker));
}
@Override
protected OverlayItem createItem(int i) {
return mOverlays.get(i);
}
@Override
public int size() {
return mOverlays.size();
}
public void addOverlay(OverlayItem overlay) {
mOverlays.add(overlay);
populate();
}
}


Next, we will implement the displayResult method.
Our last TukangAndroidApp.java should look like this
package com.tukangandroid.tutorial;
import java.util.ArrayList;
import java.util.List;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayItem;
public class TukangAndroidApp extends MapActivity{
private MapView mapView;
private List<Overlay> mapOverlays;
private MyOwnLocationOverlay myLocationOverlay;
private MapController mapController;
private TextView txtRadius;
private Button btnSearch;
private boolean isFound;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mapView = (MapView) findViewById(R.id.mapview);
mapView.setBuiltInZoomControls(true);
mapController = mapView.getController();
btnSearch = (Button) findViewById(R.id.btnSearch);
btnSearch.setOnClickListener(new SearchListener());
txtRadius = (TextView) findViewById(R.id.txtRadius);
}
private class SearchListener implements android.view.View.OnClickListener {
@Override
public void onClick(View v) {
mapView.getOverlays().clear();
mapOverlays = mapView.getOverlays();
myLocationOverlay = new MyOwnLocationOverlay(TukangAndroidApp.this, mapView);
myLocationOverlay.setMeters(Integer.parseInt(txtRadius.getText().toString()));
myLocationOverlay.enableCompass();
myLocationOverlay.enableMyLocation();
myLocationOverlay.runOnFirstFix(new Runnable() {
public void run() {
mapController.animateTo(myLocationOverlay.getMyLocation());
}
});
mapView.getOverlays().add(myLocationOverlay);
displayResults();
}
}
private void displayResults() {
// Create dummy list of GeoPoint
GeoPoint point1 = new GeoPoint(35100000, 129100000);
GeoPoint point2 = new GeoPoint(35110000, 129110000);
GeoPoint point3 = new GeoPoint(35120000, 129120000);
List<GeoPoint> points = new ArrayList<GeoPoint>();
points.add(point1);
points.add(point2);
points.add(point3);
mapOverlays = mapView.getOverlays();
Drawable drawable = getResources().getDrawable(R.drawable.icon);
HelloItemizedOverlay itemizedOverlay = new HelloItemizedOverlay(drawable);
for(GeoPoint point : points) {
// Create a location because Location has a distanceTo method that we can
// use for buffering. Notice that distanceTo calculate distance in meter
Location gpsLocation = new Location("current location");
// Get our current gps point and use it to create a location
GeoPoint currentLocation = myLocationOverlay.getMyLocation();
double lat = (double) (currentLocation.getLatitudeE6() / 1000000.0);
double lng = (double) (currentLocation.getLongitudeE6() / 1000000.0);
gpsLocation.setLatitude(lat);
gpsLocation.setLongitude(lng);
Location pointLocation = new Location("point");
pointLocation.setLatitude(point.getLatitudeE6() / 1000000.0);
pointLocation.setLongitude(point.getLongitudeE6() / 1000000.0);
// Calculate the distance between current location and point location
if(gpsLocation.distanceTo(pointLocation) < Float.parseFloat(txtRadius.getText().toString())) {
isFound = true;
OverlayItem overlayitem = new OverlayItem(point, "", "");
itemizedOverlay.addOverlay(overlayitem);
}
}
// If any location found, draw the placemark
if(isFound)
mapOverlays.add(itemizedOverlay);
}
@Override
protected boolean isRouteDisplayed() {
return false;
}
}

Run the application, and you can see that our application will work well.







Have fun!

16 comments:

  1. Perfect........I was looking for hours this code....

    ReplyDelete
  2. How can i get the current location

    ReplyDelete
  3. Why does this application crashes after some time?

    ReplyDelete
  4. @CEC@IT open DDMS view, click Emulator Control button (nearly top of the screen), after that you will see Location Controls.

    ReplyDelete
  5. can some one plz upload the zip file as i cant get the code working

    ReplyDelete
  6. That was awesome
    I follow your step but when I try to implement the displayResult method , It's has stopped.

    ReplyDelete
  7. Awesome! Just what I was looking for..tiny question..I need to show a list of locations surrounded by the circle definigng the radius.
    What can I do to make sure I add the markers and draw the circle at the same time?

    ReplyDelete
  8. why does the circle disappear when I zoom passed a certain zoom level?

    ReplyDelete
  9. Great work.. thanks a lot.... This article did many things to me...

    ReplyDelete
  10. Can you give me a full project of this application ?
    I don't get it how these things work.

    ReplyDelete
  11. Can you help soving this problem http://stackoverflow.com/questions/33035373/show-markers-near-the-device-in-android/33037739#33037739 ?

    ReplyDelete
  12. This comment has been removed by the author.

    ReplyDelete
  13. Hi, is it possible to maybe provide us with a source code, more like a Github link if you do not mind..

    ReplyDelete