Get Latitude,Longtitude by a stable marker? - android

I have to make an application with a marker on the center of a srceen and by moving the map
and not the marker get lat,lon of the point that the marker shows. I have search the internet for something like Drag Marker but I am
not sure if this is what I need.Any Solution?

In your mapActivity wrtie this.
Location l = new Location();
// Location class will be used to calculate distance moved by map
TimerTask tm;
GeoPoint oldCenterOfMap, newCenteOfMap;
// On your on resume method of activity do oldCenterOfMap = mapView.getMapCenter();
Handler mHandler = new Handler();
int isTouched = 0;
MapView mapview = (MapView) findViewById(R.id.yourmapview_from_xml);
mapView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
newCenteOfMap = mapView.getMapCenter();
if (mapchanged(oldCenterOfMap, newCenteOfMap)) {
isTouched++;
// Here is what would you do when you lift your finger fromt he map*****
newCenteOfMap =mapView.getMapCenter());
if (isTouched >= 1) {
if (isTouched > 1) {
mHandler.removeCallbacks(tm_circle);
isTouched = 1;
}
mHandler.postDelayed(tm_circle, 1200);
}
}
if (!mapchanged(oldCenterOfMap, newCenteOfMap))
mHandler.removeCallbacks(tm_circle);
}
return false;
}
});
tm_circle = new TimerTask() {
#Override
public void run() {
isTouched = 0;
// here is what you do after you touched a map moved after 1.2 seconds
oldCenterOfMap = mapView.getMapCenter();
}
};
public boolean mapchanged(GeoPoint oldCenter, GeoPoint newCenter) {
String distance = l.CalclauteDistance(oldCenter.getLongitudeE6() / 1E6,
oldCenter.getLatitudeE6() / 1E6,
newCenter.getLongitudeE6() / 1E6,
newCenter.getLatitudeE6() / 1E6);
if (Double.valueOf(distance) == 0.0)
return false;
else
return true;
}
Previous code would keep the track of the map movement.
Next code will respond in case of map movement.
In the comments present in the code above ( // here is what you do after you touched a map moved after 1.2 seconds AND // Here is what would you do when you lift your finger fromt he map*****) follow the instruction on my answer on another post.
Here Adding multiple overlays on mapview dynamically
Edit
public String CalclauteDistance(double long1, double lat1, double long2,
double lat2) {
String Result;
Point p1 = new Point(long1, lat1);
Point p2 = new Point(long2, lat2);
Result = p1.distanceTo(p2) + "";
return Result;
}
Here is the function (CalclauteDistance) from location class.

Related

How to crop Google Map [duplicate]

I've added GroundOverlay to map and want to limit scrolling and zooming within this area.
How to limit scrolling within some bounds on android google maps?
Is it possible to get instantly motion points from MapFragment?
Please, help me.
Constraining the camera has (finally!) been added as a feature as part of the release of Google Play Services 9.4 -- you can call setLatLngBoundsForCameraTarget(LatLngBounds bounds) to set the allowed panning area.
// Create a LatLngBounds that includes the city of Adelaide in Australia.
final LatLngBounds ADELAIDE = new LatLngBounds(
new LatLng(-35.0, 138.58), new LatLng(-34.9, 138.61));
// Constrain the camera target to the Adelaide bounds.
mMap.setLatLngBoundsForCameraTarget(ADELAIDE);
You can find a thorough explanation in the documentation: Restricting the user's panning to a given area and a sample activity in GitHub.
May be it is too late, but here is my solution:
Disabled built-in GoogleMap's gestures.
Added custom gestures (for scrolling, fling and scaling).
Checking for allowed area when processing events.
Set bounds/zoom manually with standard Map's functions.
And here is my example:
[UPDATED]
There was a problem - when touch events were recieved before map was initialized.
check null values in onInterceptTouchEvent
Also I have discovered that my solution is slightly slowly than build-in function.
import android.content.Context;
import android.graphics.Point;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMapOptions;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.MapsInitializer;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.VisibleRegion;
public class RestrictedMapView extends MapView {
public static float MAX_ZOOM = 20;
public static float MIN_ZOOM = 5;
public static float MIN_ZOOM_FOR_FLING = 7;
public static double MAX_LONGITUDE = 183.61;
public static double MIN_LONGITUDE = 159.31;
public static double MAX_LATITUDE = -32.98;
public static double MIN_LATITUDE = -53.82;
public static double DEF_LATITUDE = -41.78;
public static double DEF_LONGITUDE = 173.02;
public static float DEF_ZOOM = 7;
private Handler mHandler = new Handler();
private Context mContext;
private VisibleRegion mLastCorrectRegion = null;
private boolean mIsInAnimation = false;
public RestrictedMapView(Context c, AttributeSet a, int o) {
super(c, a, o);
init(c);
}
public RestrictedMapView(Context c, AttributeSet a) {
super(c, a);
init(c);
}
public RestrictedMapView(Context c) {
super(c);
init(c);
}
public RestrictedMapView(Context c, GoogleMapOptions o) {
super(c, o);
init(c);
}
private GestureDetector mGestureDetector = null;
private GestureDetector.SimpleOnGestureListener mGestudeListener =
new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (mIsInAnimation) return false;
GoogleMap map = getMap();
LatLng target = map.getCameraPosition().target;
Point screenPoint = map.getProjection().toScreenLocation(target);
Point newPoint = new Point(screenPoint.x + (int)distanceX, screenPoint.y + (int)distanceY);
LatLng mapNewTarget = map.getProjection().fromScreenLocation(newPoint);
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(
mapNewTarget,map.getCameraPosition().zoom);
tryUpdateCamera(update, 0);
return true;
}
#Override
public boolean onFling (MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (mIsInAnimation) return false;
GoogleMap map = getMap();
double zoom = map.getCameraPosition().zoom;
if (zoom < MIN_ZOOM_FOR_FLING)
return false;
int velocity = (int) Math.sqrt(velocityX * velocityX + velocityY * velocityY);
if (velocity < 500) return false;
double k1 = 0.002d; /*exipemental*/
double k2 = 0.002d;/*exipemental*/
LatLng target = map.getCameraPosition().target;
Point screenPoint = map.getProjection().toScreenLocation(target);
Point newPoint = new Point(screenPoint.x - (int)(velocityX * k1 * zoom * zoom/*exipemental*/),
screenPoint.y - (int)(velocityY * k1 * zoom * zoom/*exipemental*/));
LatLng mapNewTarget = map.getProjection().fromScreenLocation(newPoint);
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(
mapNewTarget,map.getCameraPosition().zoom);
tryUpdateCamera(update, (int)(velocity * k2 * zoom * zoom) /*exipemental*/);
return true;
}
};
private ScaleGestureDetector mScaleGestureDetector = null;
private ScaleGestureDetector.SimpleOnScaleGestureListener mScaleGestudeListener =
new ScaleGestureDetector.SimpleOnScaleGestureListener() {
#Override
public boolean onScale (ScaleGestureDetector detector) {
if (mIsInAnimation) return false;
GoogleMap map = getMap();
double zoom = map.getCameraPosition().zoom;
double k = 1d / detector.getScaleFactor();
int x = (int) detector.getFocusX();
int y = (int) detector.getFocusY();
LatLng mapFocus = map.getProjection().
fromScreenLocation(new Point(x, y));
LatLng target = map.getCameraPosition().target;
zoom = zoom + Math.log(detector.getScaleFactor()) / Math.log(2d);
if (zoom < MIN_ZOOM)
if (zoom == MIN_ZOOM) return false;
else zoom = MIN_ZOOM;
if (zoom > MAX_ZOOM)
if (zoom == MAX_ZOOM) return false;
else zoom = MAX_ZOOM;
double dx = norm(mapFocus.longitude) - norm(target.longitude);
double dy = mapFocus.latitude - target.latitude;
double dk = 1d - 1d / k;
LatLng newTarget = new LatLng(target.latitude - dy * dk,
norm(target.longitude) - dx * dk);
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(newTarget, (float) zoom);
tryUpdateCamera(update, 0);
return true;
}
};
private void tryUpdateCamera(CameraUpdate update, int animateTime) {
GoogleMap map = getMap();
final VisibleRegion reg = map.getProjection().getVisibleRegion();
if (animateTime <= 0) {
map.moveCamera(update);
checkCurrentRegion(reg);
} else {
mIsInAnimation = true;
map.animateCamera(update, animateTime, new GoogleMap.CancelableCallback() {
#Override
public void onFinish() {
mIsInAnimation = false;
checkCurrentRegion(reg);
}
#Override
public void onCancel() {
mIsInAnimation = false;
checkCurrentRegion(reg);
}
});
}
}
private void checkCurrentRegion(VisibleRegion oldReg) {
GoogleMap map = getMap();
VisibleRegion regNew = map.getProjection().getVisibleRegion();
if (checkBounds(regNew)) {
mLastCorrectRegion = regNew;
} else {
if (mLastCorrectRegion != null)
oldReg = mLastCorrectRegion;
mIsInAnimation = true;
map.animateCamera(CameraUpdateFactory.newLatLngBounds(
oldReg.latLngBounds, 0),
200, new GoogleMap.CancelableCallback() {
#Override
public void onFinish() {
mIsInAnimation = false;
}
#Override
public void onCancel() {
mIsInAnimation = false;
}
});
}
}
/**
*
*
* #param lonVal
* #return
*/
private double norm(double lonVal) {
while (lonVal > 360d) lonVal -= 360d;
while (lonVal < -360d) lonVal += 360d;
if (lonVal < 0) lonVal = 360d + lonVal;
return lonVal;
}
private double denorm(double lonVal) {
if (lonVal > 180d) lonVal = -360d + lonVal;
return lonVal;
}
private boolean checkBounds(VisibleRegion reg) {
double left = Math.min(
Math.min(norm(reg.farLeft.longitude), norm(reg.nearLeft.longitude)),
Math.min(norm(reg.farRight.longitude), norm(reg.nearRight.longitude)));
double right = Math.max(
Math.max(norm(reg.farLeft.longitude), norm(reg.nearLeft.longitude)),
Math.max(norm(reg.farRight.longitude), norm(reg.nearRight.longitude)));
double top = Math.max(
Math.max(reg.farLeft.latitude, reg.nearLeft.latitude),
Math.max(reg.farRight.latitude, reg.nearRight.latitude));
double bottom = Math.min(
Math.min(reg.farLeft.latitude, reg.nearLeft.latitude),
Math.min(reg.farRight.latitude, reg.nearRight.latitude));
boolean limitBounds = left < MIN_LONGITUDE || right > MAX_LONGITUDE ||
bottom < MIN_LATITUDE || top > MAX_LATITUDE;
return !limitBounds;
}
private void init(Context c) {
try {
MapsInitializer.initialize(c);
} catch (GooglePlayServicesNotAvailableException e) {
e.printStackTrace();
}
mContext = c;
mHandler.post(new Runnable() {
#Override
public void run() {
GoogleMap map = getMap();
if (map != null) {
getMap().getUiSettings().setZoomControlsEnabled(false);
map.getUiSettings().setAllGesturesEnabled(false);
map.moveCamera(CameraUpdateFactory.newLatLngZoom(
new LatLng(DEF_LATITUDE, DEF_LONGITUDE), DEF_ZOOM));
mLastCorrectRegion = map.getProjection().getVisibleRegion();
mGestureDetector = new GestureDetector(mContext, mGestudeListener);
mScaleGestureDetector = new ScaleGestureDetector(mContext, mScaleGestudeListener);
} else mHandler.post(this);
}
});
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mGestureDetector != null) mGestureDetector.onTouchEvent(event);
if (mScaleGestureDetector != null) mScaleGestureDetector.onTouchEvent(event);
return super.onInterceptTouchEvent(event);
}
}
Definition within my xml-layout of fragment:
<com.package....RestrictedMapView
android:id="#+id/mapview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
In xml file it is possible to define custom zoom/location buttons and set click listeners for manual manipulating camera (in this case you have to check MAX_ZOOM and MIN_ZOOM, and check if the current location is within the allowed bounds).
Too bad that google doesn't let us intercept and block the user, i found that MaciejGórski's answer is the one that suits best to my needs.
I wanted to share my solution (Based on his answer) with you.
First i defined the bounds and max/min zoom:
private final LatLngBounds BOUNDS = new LatLngBounds(new LatLng(41.8138,12.3891), new LatLng(41.9667, 12.5938));
private final int MAX_ZOOM = 18;
private final int MIN_ZOOM = 14;
Then i created this little function to test if current camera bounds are outside of max bounds and return the difference in latitude and longitude.
/**
* Returns the correction for Lat and Lng if camera is trying to get outside of visible map
* #param cameraBounds Current camera bounds
* #return Latitude and Longitude corrections to get back into bounds.
*/
private LatLng getLatLngCorrection(LatLngBounds cameraBounds) {
double latitude=0, longitude=0;
if(cameraBounds.southwest.latitude < BOUNDS.southwest.latitude) {
latitude = BOUNDS.southwest.latitude - cameraBounds.southwest.latitude;
}
if(cameraBounds.southwest.longitude < BOUNDS.southwest.longitude) {
longitude = BOUNDS.southwest.longitude - cameraBounds.southwest.longitude;
}
if(cameraBounds.northeast.latitude > BOUNDS.northeast.latitude) {
latitude = BOUNDS.northeast.latitude - cameraBounds.northeast.latitude;
}
if(cameraBounds.northeast.longitude > BOUNDS.northeast.longitude) {
longitude = BOUNDS.northeast.longitude - cameraBounds.northeast.longitude;
}
return new LatLng(latitude, longitude);
}
Then the Handler that controls the overscroll (And overzoom) limiting it every 100ms.
/**
* Bounds the user to the overlay.
*/
private class OverscrollHandler extends Handler {
#Override
public void handleMessage(Message msg) {
CameraPosition position = mMap.getCameraPosition();
VisibleRegion region = mMap.getProjection().getVisibleRegion();
float zoom = 0;
if(position.zoom < MIN_ZOOM) zoom = MIN_ZOOM;
if(position.zoom > MAX_ZOOM) zoom = MAX_ZOOM;
LatLng correction = getLatLngCorrection(region.latLngBounds);
if(zoom != 0 || correction.latitude != 0 || correction.longitude != 0) {
zoom = (zoom==0)?position.zoom:zoom;
double lat = position.target.latitude + correction.latitude;
double lon = position.target.longitude + correction.longitude;
CameraPosition newPosition = new CameraPosition(new LatLng(lat,lon), zoom, position.tilt, position.bearing);
CameraUpdate update = CameraUpdateFactory.newCameraPosition(newPosition);
mMap.moveCamera(update);
}
/* Recursively call handler every 100ms */
sendEmptyMessageDelayed(0,100);
}
}
This handler must be defined as a field inside current class (I did this in a class that extends SupportMapFragment)
private OverscrollHandler mOverscrollHandler = new OverscrollHandler();
And lastly it must be called for the first time, i did it at the end of onActivityCreated to be sure that map exists.
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mContext = getActivity();
mMap = getMap();
mMap.setMapType(GoogleMap.MAP_TYPE_NONE);
mMap.addTileOverlay(new TileOverlayOptions().tileProvider(new VexLocalTileProvider(getResources().getAssets())));
CameraUpdate upd = CameraUpdateFactory.newLatLngZoom(new LatLng(41.87145, 12.52849), 14);
mMap.moveCamera(upd);
mOverscrollHandler.sendEmptyMessageDelayed(0,100);
}
Hope you will find it useful!
Instead of using new and shiny push technology which is onCameraChange, you may try to use old poll technology: map.getCameraPosition() or map.getProjection().getVisibleRegion(). You can then check if returned value is what you like and if not, map.moveCamera(...).
Basically you need a Handler, which will get the value of camera position in handleMessage and you send messages to this handler every 10ms or so. Inside handleMessage do sendEmptyMessageDelayed.
You can also use Runnable instead of Message (but that's a matter of taste).
limit zoom you can user this code
private GoogleMap mMap;
// Set a preference for minimum and maximum zoom.
mMap.setMinZoomPreference(6.0f);
mMap.setMaxZoomPreference(14.0f);
In maps API v2 there's a Min/MaxZoomLevel on the GoogleMap class, but I don't know if you can set it in any way.
Another approach would be to add a
GoogleMap.OnCameraChangeListener
to your map and by implementing
public void onCameraChange(CameraPosition cameraPosition);
To restrict the visible area, using GoogleMap.moveCamera(cameraPosition)
That is if you wish the user to be able to scroll or zoom "some".
You can also totally deactivate the scroll/zoom events via GoogleMapOptions

How to smoothly keep moving current location marker in Google Maps v2 android

I have placed a marker for my location. I would like to move my marker smoothly something like the Google maps app. The blue circle moves very smoothly when I keep moving in my car. I would like implement the same for my app. how do I implement this on my app?
As of now my location marker just jumps for different location on very location change and marks the marker there.
Here is what I have don:
googleMap.setMyLocationEnabled(true);
So onMyLocationChange method is called automatically:
#Override
public void onMyLocationChange(Location location)
{
curlat = location.getLatitude();
curlong = location.getLongitude();
if (markermylocation != null)
{
markermylocation.remove();
}
curlat = location.getLatitude();
curlong = location.getLongitude();
myLocation(curlat, curlong, username, imageURL, ZOOMVALUE);
}
in mylocaiton method:
private void myLocation(double lat, double lng, String name, String url, float zoom)
{
if(firsttime == 1)
{
LatLng ll = new LatLng(lat,lng);
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(ll, zoom);
googleMap.animateCamera(update);
firsttime = 0;
}
final String uname = name;
curlat = lat;
curlong = lng;
LatLng position = new LatLng(curlat, curlong);
markerOptionsmylocaiton = new MarkerOptions().position(position).icon(BitmapDescriptorFactory.fromBitmap(icon)).title(uname).anchor(0.5f, 1f);
markermylocation = googleMap.addMarker(markerOptionsmylocaiton);
LatLng latlang = new LatLng(curlat, curlong);
animateMarker(markermylocation, latlang, false);
}
So everytime mylocation is called the marker gets removed and calls mylocation method and then creates the marker in the new postition. Instead I would like to get a smooth transition of the marker like Google maps? How to implement this?
Update:
After working couple of times on this: I came to this code finally but then my markers are not showing up:
I am using the below method and calling this every time in myLocation method.
public void animateMarker(final Marker marker, final LatLng toPosition,
final boolean hideMarker)
{
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
Projection proj = googleMap.getProjection();
Point startPoint = proj.toScreenLocation(marker.getPosition());
final LatLng startLatLng = proj.fromScreenLocation(startPoint);
final long duration = 500;
final Interpolator interpolator = new LinearInterpolator();
handler.post(new Runnable()
{
#Override
public void run()
{
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed
/ duration);
double lng = t * toPosition.longitude + (1 - t)
* startLatLng.longitude;
double lat = t * toPosition.latitude + (1 - t)
* startLatLng.latitude;
marker.setPosition(new LatLng(lat, lng));
if (t < 1.0)
{
// Post again 16ms later.
handler.postDelayed(this, 16);
}
else
{
if (hideMarker)
{
marker.setVisible(false);
}
else
{
marker.setVisible(true);
}
}
}
});
}
Thanks!
Hi I have also the same thing in one of my project and it works like charm.
Add the below piece of code after this line :
marker.setPosition(new LatLng(lat, lng));
//this code
if (googleMap != null)
googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lat, lng), 15.0f));
Hope it will help you.
Thanks
You'll have to use an interpolator a linear or maybe an acceleration interpolator between the new latlng of the location and the current latlng of the marker. Here's an example with a bounce interpolator.
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
final long duration = 2500;
final Interpolator interpolator = new BounceInterpolator();
marker.setVisible(true);
handler.post(new Runnable() {
#Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - start;
float t = Math.max(
1 - interpolator.getInterpolation((float) elapsed
/ duration), 0);
marker.setAnchor(0.5f, 1.0f + 6 * t);
marker.setPosition(latlng)
if (t > 0.0) {
// Post again 16ms later.
handler.postDelayed(this, 16);
}
}
});

Google Maps Android API v2 limit panning [duplicate]

I've added GroundOverlay to map and want to limit scrolling and zooming within this area.
How to limit scrolling within some bounds on android google maps?
Is it possible to get instantly motion points from MapFragment?
Please, help me.
Constraining the camera has (finally!) been added as a feature as part of the release of Google Play Services 9.4 -- you can call setLatLngBoundsForCameraTarget(LatLngBounds bounds) to set the allowed panning area.
// Create a LatLngBounds that includes the city of Adelaide in Australia.
final LatLngBounds ADELAIDE = new LatLngBounds(
new LatLng(-35.0, 138.58), new LatLng(-34.9, 138.61));
// Constrain the camera target to the Adelaide bounds.
mMap.setLatLngBoundsForCameraTarget(ADELAIDE);
You can find a thorough explanation in the documentation: Restricting the user's panning to a given area and a sample activity in GitHub.
May be it is too late, but here is my solution:
Disabled built-in GoogleMap's gestures.
Added custom gestures (for scrolling, fling and scaling).
Checking for allowed area when processing events.
Set bounds/zoom manually with standard Map's functions.
And here is my example:
[UPDATED]
There was a problem - when touch events were recieved before map was initialized.
check null values in onInterceptTouchEvent
Also I have discovered that my solution is slightly slowly than build-in function.
import android.content.Context;
import android.graphics.Point;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMapOptions;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.MapsInitializer;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.VisibleRegion;
public class RestrictedMapView extends MapView {
public static float MAX_ZOOM = 20;
public static float MIN_ZOOM = 5;
public static float MIN_ZOOM_FOR_FLING = 7;
public static double MAX_LONGITUDE = 183.61;
public static double MIN_LONGITUDE = 159.31;
public static double MAX_LATITUDE = -32.98;
public static double MIN_LATITUDE = -53.82;
public static double DEF_LATITUDE = -41.78;
public static double DEF_LONGITUDE = 173.02;
public static float DEF_ZOOM = 7;
private Handler mHandler = new Handler();
private Context mContext;
private VisibleRegion mLastCorrectRegion = null;
private boolean mIsInAnimation = false;
public RestrictedMapView(Context c, AttributeSet a, int o) {
super(c, a, o);
init(c);
}
public RestrictedMapView(Context c, AttributeSet a) {
super(c, a);
init(c);
}
public RestrictedMapView(Context c) {
super(c);
init(c);
}
public RestrictedMapView(Context c, GoogleMapOptions o) {
super(c, o);
init(c);
}
private GestureDetector mGestureDetector = null;
private GestureDetector.SimpleOnGestureListener mGestudeListener =
new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (mIsInAnimation) return false;
GoogleMap map = getMap();
LatLng target = map.getCameraPosition().target;
Point screenPoint = map.getProjection().toScreenLocation(target);
Point newPoint = new Point(screenPoint.x + (int)distanceX, screenPoint.y + (int)distanceY);
LatLng mapNewTarget = map.getProjection().fromScreenLocation(newPoint);
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(
mapNewTarget,map.getCameraPosition().zoom);
tryUpdateCamera(update, 0);
return true;
}
#Override
public boolean onFling (MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (mIsInAnimation) return false;
GoogleMap map = getMap();
double zoom = map.getCameraPosition().zoom;
if (zoom < MIN_ZOOM_FOR_FLING)
return false;
int velocity = (int) Math.sqrt(velocityX * velocityX + velocityY * velocityY);
if (velocity < 500) return false;
double k1 = 0.002d; /*exipemental*/
double k2 = 0.002d;/*exipemental*/
LatLng target = map.getCameraPosition().target;
Point screenPoint = map.getProjection().toScreenLocation(target);
Point newPoint = new Point(screenPoint.x - (int)(velocityX * k1 * zoom * zoom/*exipemental*/),
screenPoint.y - (int)(velocityY * k1 * zoom * zoom/*exipemental*/));
LatLng mapNewTarget = map.getProjection().fromScreenLocation(newPoint);
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(
mapNewTarget,map.getCameraPosition().zoom);
tryUpdateCamera(update, (int)(velocity * k2 * zoom * zoom) /*exipemental*/);
return true;
}
};
private ScaleGestureDetector mScaleGestureDetector = null;
private ScaleGestureDetector.SimpleOnScaleGestureListener mScaleGestudeListener =
new ScaleGestureDetector.SimpleOnScaleGestureListener() {
#Override
public boolean onScale (ScaleGestureDetector detector) {
if (mIsInAnimation) return false;
GoogleMap map = getMap();
double zoom = map.getCameraPosition().zoom;
double k = 1d / detector.getScaleFactor();
int x = (int) detector.getFocusX();
int y = (int) detector.getFocusY();
LatLng mapFocus = map.getProjection().
fromScreenLocation(new Point(x, y));
LatLng target = map.getCameraPosition().target;
zoom = zoom + Math.log(detector.getScaleFactor()) / Math.log(2d);
if (zoom < MIN_ZOOM)
if (zoom == MIN_ZOOM) return false;
else zoom = MIN_ZOOM;
if (zoom > MAX_ZOOM)
if (zoom == MAX_ZOOM) return false;
else zoom = MAX_ZOOM;
double dx = norm(mapFocus.longitude) - norm(target.longitude);
double dy = mapFocus.latitude - target.latitude;
double dk = 1d - 1d / k;
LatLng newTarget = new LatLng(target.latitude - dy * dk,
norm(target.longitude) - dx * dk);
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(newTarget, (float) zoom);
tryUpdateCamera(update, 0);
return true;
}
};
private void tryUpdateCamera(CameraUpdate update, int animateTime) {
GoogleMap map = getMap();
final VisibleRegion reg = map.getProjection().getVisibleRegion();
if (animateTime <= 0) {
map.moveCamera(update);
checkCurrentRegion(reg);
} else {
mIsInAnimation = true;
map.animateCamera(update, animateTime, new GoogleMap.CancelableCallback() {
#Override
public void onFinish() {
mIsInAnimation = false;
checkCurrentRegion(reg);
}
#Override
public void onCancel() {
mIsInAnimation = false;
checkCurrentRegion(reg);
}
});
}
}
private void checkCurrentRegion(VisibleRegion oldReg) {
GoogleMap map = getMap();
VisibleRegion regNew = map.getProjection().getVisibleRegion();
if (checkBounds(regNew)) {
mLastCorrectRegion = regNew;
} else {
if (mLastCorrectRegion != null)
oldReg = mLastCorrectRegion;
mIsInAnimation = true;
map.animateCamera(CameraUpdateFactory.newLatLngBounds(
oldReg.latLngBounds, 0),
200, new GoogleMap.CancelableCallback() {
#Override
public void onFinish() {
mIsInAnimation = false;
}
#Override
public void onCancel() {
mIsInAnimation = false;
}
});
}
}
/**
*
*
* #param lonVal
* #return
*/
private double norm(double lonVal) {
while (lonVal > 360d) lonVal -= 360d;
while (lonVal < -360d) lonVal += 360d;
if (lonVal < 0) lonVal = 360d + lonVal;
return lonVal;
}
private double denorm(double lonVal) {
if (lonVal > 180d) lonVal = -360d + lonVal;
return lonVal;
}
private boolean checkBounds(VisibleRegion reg) {
double left = Math.min(
Math.min(norm(reg.farLeft.longitude), norm(reg.nearLeft.longitude)),
Math.min(norm(reg.farRight.longitude), norm(reg.nearRight.longitude)));
double right = Math.max(
Math.max(norm(reg.farLeft.longitude), norm(reg.nearLeft.longitude)),
Math.max(norm(reg.farRight.longitude), norm(reg.nearRight.longitude)));
double top = Math.max(
Math.max(reg.farLeft.latitude, reg.nearLeft.latitude),
Math.max(reg.farRight.latitude, reg.nearRight.latitude));
double bottom = Math.min(
Math.min(reg.farLeft.latitude, reg.nearLeft.latitude),
Math.min(reg.farRight.latitude, reg.nearRight.latitude));
boolean limitBounds = left < MIN_LONGITUDE || right > MAX_LONGITUDE ||
bottom < MIN_LATITUDE || top > MAX_LATITUDE;
return !limitBounds;
}
private void init(Context c) {
try {
MapsInitializer.initialize(c);
} catch (GooglePlayServicesNotAvailableException e) {
e.printStackTrace();
}
mContext = c;
mHandler.post(new Runnable() {
#Override
public void run() {
GoogleMap map = getMap();
if (map != null) {
getMap().getUiSettings().setZoomControlsEnabled(false);
map.getUiSettings().setAllGesturesEnabled(false);
map.moveCamera(CameraUpdateFactory.newLatLngZoom(
new LatLng(DEF_LATITUDE, DEF_LONGITUDE), DEF_ZOOM));
mLastCorrectRegion = map.getProjection().getVisibleRegion();
mGestureDetector = new GestureDetector(mContext, mGestudeListener);
mScaleGestureDetector = new ScaleGestureDetector(mContext, mScaleGestudeListener);
} else mHandler.post(this);
}
});
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mGestureDetector != null) mGestureDetector.onTouchEvent(event);
if (mScaleGestureDetector != null) mScaleGestureDetector.onTouchEvent(event);
return super.onInterceptTouchEvent(event);
}
}
Definition within my xml-layout of fragment:
<com.package....RestrictedMapView
android:id="#+id/mapview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
In xml file it is possible to define custom zoom/location buttons and set click listeners for manual manipulating camera (in this case you have to check MAX_ZOOM and MIN_ZOOM, and check if the current location is within the allowed bounds).
Too bad that google doesn't let us intercept and block the user, i found that MaciejGórski's answer is the one that suits best to my needs.
I wanted to share my solution (Based on his answer) with you.
First i defined the bounds and max/min zoom:
private final LatLngBounds BOUNDS = new LatLngBounds(new LatLng(41.8138,12.3891), new LatLng(41.9667, 12.5938));
private final int MAX_ZOOM = 18;
private final int MIN_ZOOM = 14;
Then i created this little function to test if current camera bounds are outside of max bounds and return the difference in latitude and longitude.
/**
* Returns the correction for Lat and Lng if camera is trying to get outside of visible map
* #param cameraBounds Current camera bounds
* #return Latitude and Longitude corrections to get back into bounds.
*/
private LatLng getLatLngCorrection(LatLngBounds cameraBounds) {
double latitude=0, longitude=0;
if(cameraBounds.southwest.latitude < BOUNDS.southwest.latitude) {
latitude = BOUNDS.southwest.latitude - cameraBounds.southwest.latitude;
}
if(cameraBounds.southwest.longitude < BOUNDS.southwest.longitude) {
longitude = BOUNDS.southwest.longitude - cameraBounds.southwest.longitude;
}
if(cameraBounds.northeast.latitude > BOUNDS.northeast.latitude) {
latitude = BOUNDS.northeast.latitude - cameraBounds.northeast.latitude;
}
if(cameraBounds.northeast.longitude > BOUNDS.northeast.longitude) {
longitude = BOUNDS.northeast.longitude - cameraBounds.northeast.longitude;
}
return new LatLng(latitude, longitude);
}
Then the Handler that controls the overscroll (And overzoom) limiting it every 100ms.
/**
* Bounds the user to the overlay.
*/
private class OverscrollHandler extends Handler {
#Override
public void handleMessage(Message msg) {
CameraPosition position = mMap.getCameraPosition();
VisibleRegion region = mMap.getProjection().getVisibleRegion();
float zoom = 0;
if(position.zoom < MIN_ZOOM) zoom = MIN_ZOOM;
if(position.zoom > MAX_ZOOM) zoom = MAX_ZOOM;
LatLng correction = getLatLngCorrection(region.latLngBounds);
if(zoom != 0 || correction.latitude != 0 || correction.longitude != 0) {
zoom = (zoom==0)?position.zoom:zoom;
double lat = position.target.latitude + correction.latitude;
double lon = position.target.longitude + correction.longitude;
CameraPosition newPosition = new CameraPosition(new LatLng(lat,lon), zoom, position.tilt, position.bearing);
CameraUpdate update = CameraUpdateFactory.newCameraPosition(newPosition);
mMap.moveCamera(update);
}
/* Recursively call handler every 100ms */
sendEmptyMessageDelayed(0,100);
}
}
This handler must be defined as a field inside current class (I did this in a class that extends SupportMapFragment)
private OverscrollHandler mOverscrollHandler = new OverscrollHandler();
And lastly it must be called for the first time, i did it at the end of onActivityCreated to be sure that map exists.
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mContext = getActivity();
mMap = getMap();
mMap.setMapType(GoogleMap.MAP_TYPE_NONE);
mMap.addTileOverlay(new TileOverlayOptions().tileProvider(new VexLocalTileProvider(getResources().getAssets())));
CameraUpdate upd = CameraUpdateFactory.newLatLngZoom(new LatLng(41.87145, 12.52849), 14);
mMap.moveCamera(upd);
mOverscrollHandler.sendEmptyMessageDelayed(0,100);
}
Hope you will find it useful!
Instead of using new and shiny push technology which is onCameraChange, you may try to use old poll technology: map.getCameraPosition() or map.getProjection().getVisibleRegion(). You can then check if returned value is what you like and if not, map.moveCamera(...).
Basically you need a Handler, which will get the value of camera position in handleMessage and you send messages to this handler every 10ms or so. Inside handleMessage do sendEmptyMessageDelayed.
You can also use Runnable instead of Message (but that's a matter of taste).
limit zoom you can user this code
private GoogleMap mMap;
// Set a preference for minimum and maximum zoom.
mMap.setMinZoomPreference(6.0f);
mMap.setMaxZoomPreference(14.0f);
In maps API v2 there's a Min/MaxZoomLevel on the GoogleMap class, but I don't know if you can set it in any way.
Another approach would be to add a
GoogleMap.OnCameraChangeListener
to your map and by implementing
public void onCameraChange(CameraPosition cameraPosition);
To restrict the visible area, using GoogleMap.moveCamera(cameraPosition)
That is if you wish the user to be able to scroll or zoom "some".
You can also totally deactivate the scroll/zoom events via GoogleMapOptions

limit scrolling and zooming Google Maps Android API v2

I've added GroundOverlay to map and want to limit scrolling and zooming within this area.
How to limit scrolling within some bounds on android google maps?
Is it possible to get instantly motion points from MapFragment?
Please, help me.
Constraining the camera has (finally!) been added as a feature as part of the release of Google Play Services 9.4 -- you can call setLatLngBoundsForCameraTarget(LatLngBounds bounds) to set the allowed panning area.
// Create a LatLngBounds that includes the city of Adelaide in Australia.
final LatLngBounds ADELAIDE = new LatLngBounds(
new LatLng(-35.0, 138.58), new LatLng(-34.9, 138.61));
// Constrain the camera target to the Adelaide bounds.
mMap.setLatLngBoundsForCameraTarget(ADELAIDE);
You can find a thorough explanation in the documentation: Restricting the user's panning to a given area and a sample activity in GitHub.
May be it is too late, but here is my solution:
Disabled built-in GoogleMap's gestures.
Added custom gestures (for scrolling, fling and scaling).
Checking for allowed area when processing events.
Set bounds/zoom manually with standard Map's functions.
And here is my example:
[UPDATED]
There was a problem - when touch events were recieved before map was initialized.
check null values in onInterceptTouchEvent
Also I have discovered that my solution is slightly slowly than build-in function.
import android.content.Context;
import android.graphics.Point;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMapOptions;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.MapsInitializer;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.VisibleRegion;
public class RestrictedMapView extends MapView {
public static float MAX_ZOOM = 20;
public static float MIN_ZOOM = 5;
public static float MIN_ZOOM_FOR_FLING = 7;
public static double MAX_LONGITUDE = 183.61;
public static double MIN_LONGITUDE = 159.31;
public static double MAX_LATITUDE = -32.98;
public static double MIN_LATITUDE = -53.82;
public static double DEF_LATITUDE = -41.78;
public static double DEF_LONGITUDE = 173.02;
public static float DEF_ZOOM = 7;
private Handler mHandler = new Handler();
private Context mContext;
private VisibleRegion mLastCorrectRegion = null;
private boolean mIsInAnimation = false;
public RestrictedMapView(Context c, AttributeSet a, int o) {
super(c, a, o);
init(c);
}
public RestrictedMapView(Context c, AttributeSet a) {
super(c, a);
init(c);
}
public RestrictedMapView(Context c) {
super(c);
init(c);
}
public RestrictedMapView(Context c, GoogleMapOptions o) {
super(c, o);
init(c);
}
private GestureDetector mGestureDetector = null;
private GestureDetector.SimpleOnGestureListener mGestudeListener =
new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (mIsInAnimation) return false;
GoogleMap map = getMap();
LatLng target = map.getCameraPosition().target;
Point screenPoint = map.getProjection().toScreenLocation(target);
Point newPoint = new Point(screenPoint.x + (int)distanceX, screenPoint.y + (int)distanceY);
LatLng mapNewTarget = map.getProjection().fromScreenLocation(newPoint);
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(
mapNewTarget,map.getCameraPosition().zoom);
tryUpdateCamera(update, 0);
return true;
}
#Override
public boolean onFling (MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (mIsInAnimation) return false;
GoogleMap map = getMap();
double zoom = map.getCameraPosition().zoom;
if (zoom < MIN_ZOOM_FOR_FLING)
return false;
int velocity = (int) Math.sqrt(velocityX * velocityX + velocityY * velocityY);
if (velocity < 500) return false;
double k1 = 0.002d; /*exipemental*/
double k2 = 0.002d;/*exipemental*/
LatLng target = map.getCameraPosition().target;
Point screenPoint = map.getProjection().toScreenLocation(target);
Point newPoint = new Point(screenPoint.x - (int)(velocityX * k1 * zoom * zoom/*exipemental*/),
screenPoint.y - (int)(velocityY * k1 * zoom * zoom/*exipemental*/));
LatLng mapNewTarget = map.getProjection().fromScreenLocation(newPoint);
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(
mapNewTarget,map.getCameraPosition().zoom);
tryUpdateCamera(update, (int)(velocity * k2 * zoom * zoom) /*exipemental*/);
return true;
}
};
private ScaleGestureDetector mScaleGestureDetector = null;
private ScaleGestureDetector.SimpleOnScaleGestureListener mScaleGestudeListener =
new ScaleGestureDetector.SimpleOnScaleGestureListener() {
#Override
public boolean onScale (ScaleGestureDetector detector) {
if (mIsInAnimation) return false;
GoogleMap map = getMap();
double zoom = map.getCameraPosition().zoom;
double k = 1d / detector.getScaleFactor();
int x = (int) detector.getFocusX();
int y = (int) detector.getFocusY();
LatLng mapFocus = map.getProjection().
fromScreenLocation(new Point(x, y));
LatLng target = map.getCameraPosition().target;
zoom = zoom + Math.log(detector.getScaleFactor()) / Math.log(2d);
if (zoom < MIN_ZOOM)
if (zoom == MIN_ZOOM) return false;
else zoom = MIN_ZOOM;
if (zoom > MAX_ZOOM)
if (zoom == MAX_ZOOM) return false;
else zoom = MAX_ZOOM;
double dx = norm(mapFocus.longitude) - norm(target.longitude);
double dy = mapFocus.latitude - target.latitude;
double dk = 1d - 1d / k;
LatLng newTarget = new LatLng(target.latitude - dy * dk,
norm(target.longitude) - dx * dk);
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(newTarget, (float) zoom);
tryUpdateCamera(update, 0);
return true;
}
};
private void tryUpdateCamera(CameraUpdate update, int animateTime) {
GoogleMap map = getMap();
final VisibleRegion reg = map.getProjection().getVisibleRegion();
if (animateTime <= 0) {
map.moveCamera(update);
checkCurrentRegion(reg);
} else {
mIsInAnimation = true;
map.animateCamera(update, animateTime, new GoogleMap.CancelableCallback() {
#Override
public void onFinish() {
mIsInAnimation = false;
checkCurrentRegion(reg);
}
#Override
public void onCancel() {
mIsInAnimation = false;
checkCurrentRegion(reg);
}
});
}
}
private void checkCurrentRegion(VisibleRegion oldReg) {
GoogleMap map = getMap();
VisibleRegion regNew = map.getProjection().getVisibleRegion();
if (checkBounds(regNew)) {
mLastCorrectRegion = regNew;
} else {
if (mLastCorrectRegion != null)
oldReg = mLastCorrectRegion;
mIsInAnimation = true;
map.animateCamera(CameraUpdateFactory.newLatLngBounds(
oldReg.latLngBounds, 0),
200, new GoogleMap.CancelableCallback() {
#Override
public void onFinish() {
mIsInAnimation = false;
}
#Override
public void onCancel() {
mIsInAnimation = false;
}
});
}
}
/**
*
*
* #param lonVal
* #return
*/
private double norm(double lonVal) {
while (lonVal > 360d) lonVal -= 360d;
while (lonVal < -360d) lonVal += 360d;
if (lonVal < 0) lonVal = 360d + lonVal;
return lonVal;
}
private double denorm(double lonVal) {
if (lonVal > 180d) lonVal = -360d + lonVal;
return lonVal;
}
private boolean checkBounds(VisibleRegion reg) {
double left = Math.min(
Math.min(norm(reg.farLeft.longitude), norm(reg.nearLeft.longitude)),
Math.min(norm(reg.farRight.longitude), norm(reg.nearRight.longitude)));
double right = Math.max(
Math.max(norm(reg.farLeft.longitude), norm(reg.nearLeft.longitude)),
Math.max(norm(reg.farRight.longitude), norm(reg.nearRight.longitude)));
double top = Math.max(
Math.max(reg.farLeft.latitude, reg.nearLeft.latitude),
Math.max(reg.farRight.latitude, reg.nearRight.latitude));
double bottom = Math.min(
Math.min(reg.farLeft.latitude, reg.nearLeft.latitude),
Math.min(reg.farRight.latitude, reg.nearRight.latitude));
boolean limitBounds = left < MIN_LONGITUDE || right > MAX_LONGITUDE ||
bottom < MIN_LATITUDE || top > MAX_LATITUDE;
return !limitBounds;
}
private void init(Context c) {
try {
MapsInitializer.initialize(c);
} catch (GooglePlayServicesNotAvailableException e) {
e.printStackTrace();
}
mContext = c;
mHandler.post(new Runnable() {
#Override
public void run() {
GoogleMap map = getMap();
if (map != null) {
getMap().getUiSettings().setZoomControlsEnabled(false);
map.getUiSettings().setAllGesturesEnabled(false);
map.moveCamera(CameraUpdateFactory.newLatLngZoom(
new LatLng(DEF_LATITUDE, DEF_LONGITUDE), DEF_ZOOM));
mLastCorrectRegion = map.getProjection().getVisibleRegion();
mGestureDetector = new GestureDetector(mContext, mGestudeListener);
mScaleGestureDetector = new ScaleGestureDetector(mContext, mScaleGestudeListener);
} else mHandler.post(this);
}
});
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mGestureDetector != null) mGestureDetector.onTouchEvent(event);
if (mScaleGestureDetector != null) mScaleGestureDetector.onTouchEvent(event);
return super.onInterceptTouchEvent(event);
}
}
Definition within my xml-layout of fragment:
<com.package....RestrictedMapView
android:id="#+id/mapview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
In xml file it is possible to define custom zoom/location buttons and set click listeners for manual manipulating camera (in this case you have to check MAX_ZOOM and MIN_ZOOM, and check if the current location is within the allowed bounds).
Too bad that google doesn't let us intercept and block the user, i found that MaciejGórski's answer is the one that suits best to my needs.
I wanted to share my solution (Based on his answer) with you.
First i defined the bounds and max/min zoom:
private final LatLngBounds BOUNDS = new LatLngBounds(new LatLng(41.8138,12.3891), new LatLng(41.9667, 12.5938));
private final int MAX_ZOOM = 18;
private final int MIN_ZOOM = 14;
Then i created this little function to test if current camera bounds are outside of max bounds and return the difference in latitude and longitude.
/**
* Returns the correction for Lat and Lng if camera is trying to get outside of visible map
* #param cameraBounds Current camera bounds
* #return Latitude and Longitude corrections to get back into bounds.
*/
private LatLng getLatLngCorrection(LatLngBounds cameraBounds) {
double latitude=0, longitude=0;
if(cameraBounds.southwest.latitude < BOUNDS.southwest.latitude) {
latitude = BOUNDS.southwest.latitude - cameraBounds.southwest.latitude;
}
if(cameraBounds.southwest.longitude < BOUNDS.southwest.longitude) {
longitude = BOUNDS.southwest.longitude - cameraBounds.southwest.longitude;
}
if(cameraBounds.northeast.latitude > BOUNDS.northeast.latitude) {
latitude = BOUNDS.northeast.latitude - cameraBounds.northeast.latitude;
}
if(cameraBounds.northeast.longitude > BOUNDS.northeast.longitude) {
longitude = BOUNDS.northeast.longitude - cameraBounds.northeast.longitude;
}
return new LatLng(latitude, longitude);
}
Then the Handler that controls the overscroll (And overzoom) limiting it every 100ms.
/**
* Bounds the user to the overlay.
*/
private class OverscrollHandler extends Handler {
#Override
public void handleMessage(Message msg) {
CameraPosition position = mMap.getCameraPosition();
VisibleRegion region = mMap.getProjection().getVisibleRegion();
float zoom = 0;
if(position.zoom < MIN_ZOOM) zoom = MIN_ZOOM;
if(position.zoom > MAX_ZOOM) zoom = MAX_ZOOM;
LatLng correction = getLatLngCorrection(region.latLngBounds);
if(zoom != 0 || correction.latitude != 0 || correction.longitude != 0) {
zoom = (zoom==0)?position.zoom:zoom;
double lat = position.target.latitude + correction.latitude;
double lon = position.target.longitude + correction.longitude;
CameraPosition newPosition = new CameraPosition(new LatLng(lat,lon), zoom, position.tilt, position.bearing);
CameraUpdate update = CameraUpdateFactory.newCameraPosition(newPosition);
mMap.moveCamera(update);
}
/* Recursively call handler every 100ms */
sendEmptyMessageDelayed(0,100);
}
}
This handler must be defined as a field inside current class (I did this in a class that extends SupportMapFragment)
private OverscrollHandler mOverscrollHandler = new OverscrollHandler();
And lastly it must be called for the first time, i did it at the end of onActivityCreated to be sure that map exists.
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mContext = getActivity();
mMap = getMap();
mMap.setMapType(GoogleMap.MAP_TYPE_NONE);
mMap.addTileOverlay(new TileOverlayOptions().tileProvider(new VexLocalTileProvider(getResources().getAssets())));
CameraUpdate upd = CameraUpdateFactory.newLatLngZoom(new LatLng(41.87145, 12.52849), 14);
mMap.moveCamera(upd);
mOverscrollHandler.sendEmptyMessageDelayed(0,100);
}
Hope you will find it useful!
Instead of using new and shiny push technology which is onCameraChange, you may try to use old poll technology: map.getCameraPosition() or map.getProjection().getVisibleRegion(). You can then check if returned value is what you like and if not, map.moveCamera(...).
Basically you need a Handler, which will get the value of camera position in handleMessage and you send messages to this handler every 10ms or so. Inside handleMessage do sendEmptyMessageDelayed.
You can also use Runnable instead of Message (but that's a matter of taste).
limit zoom you can user this code
private GoogleMap mMap;
// Set a preference for minimum and maximum zoom.
mMap.setMinZoomPreference(6.0f);
mMap.setMaxZoomPreference(14.0f);
In maps API v2 there's a Min/MaxZoomLevel on the GoogleMap class, but I don't know if you can set it in any way.
Another approach would be to add a
GoogleMap.OnCameraChangeListener
to your map and by implementing
public void onCameraChange(CameraPosition cameraPosition);
To restrict the visible area, using GoogleMap.moveCamera(cameraPosition)
That is if you wish the user to be able to scroll or zoom "some".
You can also totally deactivate the scroll/zoom events via GoogleMapOptions

Android itemizedOverlay's onTap action overrided

I have a class :
class MapItemizedOverlay extends com.google.android.maps.ItemizedOverlay<OverlayItem> {
private Context context;
private ArrayList items = new ArrayList();
public MapItemizedOverlay(Context aContext, Drawable marker) {
super(boundCenterBottom(marker));
context = aContext;
}
public void addOverlayItem(OverlayItem item) {
items.add(item);
populate();
}
#Override
protected OverlayItem createItem(int i) {
return (OverlayItem) items.get(i);
}
#Override
public int size() {
return items.size();
}
#Override
protected boolean onTap(int index) {
OverlayItem item = (OverlayItem) items.get(index);
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
dialog.setTitle(item.getTitle());
dialog.setMessage(item.getSnippet());
dialog.show();
return true;
}
#Override
public boolean onTap (final GeoPoint p, final MapView mapView) {
Geocoder geoCoder = new Geocoder(getBaseContext(), Locale.getDefault());
try {
List<Address> addresses = geoCoder.getFromLocation(p.getLatitudeE6() / 1E6,
p.getLongitudeE6() / 1E6, 1);
String address = "";
if (addresses.size() > 0) {
for (int i=0; i<addresses.get(0).getMaxAddressLineIndex(); i++)
address += addresses.get(0).getAddressLine(i) + "\n";
}
address.cancel();
address.setText(address);
address.show();
}
catch (IOException e) {
e.printStackTrace();
}
return true;
}
}
I add some overlays on the map with the function:
private void initialiseOverlays() {
// Create an ItemizedOverlay to display a list of markers
Drawable defaultMarker = getResources().getDrawable(R.drawable.marker);
MapItemizedOverlay mapItemizedOverlay = new MapItemizedOverlay(this, defaultMarker);
mapItemizedOverlay.addOverlayItem(new OverlayItem(new GeoPoint((int) (12.345678 * 1E6), (int) (23.456789 * 1E6)), "Point 1", "some-random-text"));
mapItemizedOverlay.addOverlayItem(new OverlayItem(new GeoPoint((int) (89.012345 * 1E6), (int) (67.890123 * 1E6)), "Point number 2", "more-random-text"));
// Add the overlays to the map
mapView.getOverlays().add(mapItemizedOverlay);
}
If only one of the onTap functions is defined everything works fine - I can either get the address if I click somewhere over the map or I can get a dialog with the place's title and content if I click on the icon over the place.
But I want to have both of the functions working together, the application to detect if the click was over an empty place on the map or over a marker(the drawable set) and show it's information. How can I achieve this?
Found the answer - had to include the:
if(super.onTap(p, mapView)) {
return true;
}
in the beginning of the public boolean onTap (final GeoPoint p, final MapView mapView) function.
you can use onTouch method
#Override
public boolean onTouchEvent(MotionEvent event, final MapView mapView) {
final int action=event.getAction();
final int x=(int)event.getX();
final int y=(int)event.getY();
result = false;
if (action==MotionEvent.ACTION_DOWN) {
downPressed = true;
drag = false;
/* here check for the items is null or not and then after get all the items
so if you click on map and on that place if the item on placed then it will
check for the item hit other it refer the user click on map not on marker
*/
if(items!=null){
for(int i=0;i<items.size();i++){
OverlayItem item = items.get(i);
Point mp=new Point(0,0);
mapView.getProjection().toPixels(item.getPoint(), mp);
xDragTouchOffset=x-mp.x;
yDragTouchOffset=y-mp.y;
if (hitTest(item, marker, x-(mp.x-(xDragImageOffset+xDragTouchOffset*2)), y-(mp.y-((yDragImageOffset/2)+yDragTouchOffset)))) {
result = true;
markerIndex = i;
task_id = Long.parseLong(item.getTitle());
downPressed = false;
markerPressed = true;
break;
}
}
}
}
else if (action==MotionEvent.ACTION_MOVE) {
// here user pressed and drag the downPressed set to false so it will indicate that user want to move the map and drag set to true;
downPressed = false;
drag=true;
}
else if (action==MotionEvent.ACTION_UP) {
// if user not drag then this downPressed is true and it will return the screen and map coordinate
if(downPressed){
tempPoint = mapView.getProjection().fromPixels(x, y);
markerLat = tempPoint.getLatitudeE6()/1e6;
markerLng = tempPoint.getLongitudeE6()/1e6;
mapView.invalidate();
}
drag = false;
downPressed = false;
}
return(result | super.onTouchEvent(event, mapView));
}
here i can do this way hope you get some idea

Categories

Resources