Monday 27 June 2016

How to Zoom & Scale Google map from center position android

  • Objective :
The main objective of this task is to show two markers (customer & driver) base on the distance with appropriate zoom level. Driver position will change base on location update & zoom level update base on the new distance calculated between two markers.
  • What user can do ?
  1. User can zoom in & zoom out map from center position.(customer will be always in center of the map) 
  2. ?
     
    1
    imgMapTransparent.setOnTouchListener(new MyScaleGestures(TrackingActivity.this, googleMap));
     
     
    
    imgMapTransparent.setOnTouchListener(new MyScaleGestures(TrackingActivity.this, googleMap));
    
    
  • What user can’t do ?
    1. User can’t scale map from X,Y position of the map area.
    2. User can’t move map as default behaviors
    3. All gestures event will be deactivate.
    4. ?
       
      1
      mGoogleMap.getUiSettings().setAllGesturesEnabled(false);
       
       
      
      mGoogleMap.getUiSettings().setAllGesturesEnabled(false);
      
      
  • How driver location will update ?
If a driver changes his/her location from the previous location we will get notify in void onLocationChanged(Location location)this location will help us to calculate the distance between customer & driver.
  • How does it work ?
We will get a frequent update of driver location even app will kill from a background as they will move on the path. Now calculate the distance between customer & driver current location and update driver marker. Now calculate Zoom level base on distance and update map view to be more zoom in.

To achieve this we have used transparent image view over the map to zoom in & zoom out and get touch event of the map. So while user scales it from any X,Y position that will call onCameraChangeListner .Now we will calculate zoom level inside onCameraChangeListner and set customer marker to the center of the screen.
  • Calculate distance between two locations:
?
 
01
02
03
04
05
06
07
08
09
10
public float getDistanceInMeter(double startLat, double startLong, double endLat, double endLong) {
    Location sourceLoc = new Location(“”);
    sourceLoc.setLatitude(startLat);
    sourceLoc.setLongitude(startLong);
    Location destLoc = new Location(“”);
    destLoc.setLatitude(endLat);
    destLoc.setLongitude(endLong);
    float distanceInMeters = sourceLoc.distanceTo(destLoc);
    return distanceInMeters;
}
 
 

public float getDistanceInMeter(double startLat, double startLong, double endLat, double endLong) {
    Location sourceLoc = new Location(“”);
    sourceLoc.setLatitude(startLat);
    sourceLoc.setLongitude(startLong);
    Location destLoc = new Location(“”);
    destLoc.setLatitude(endLat);
    destLoc.setLongitude(endLong);
    float distanceInMeters = sourceLoc.distanceTo(destLoc);
    return distanceInMeters;
}

  • Calculate zoom level from distance
?
 
1
2
3
4
5
public double getZoomForMetersWide(double desiredMeters, double latitude) {
    final double latitudinalAdjustment = Math.cos(Math.PI * latitude / 180);
    final double arg = EQUATOR_LENGTH * getScreenWidth() * latitudinalAdjustment / (desiredMeters * 256);
    return Math.log(arg) / Math.log(2);
}
 
 

public double getZoomForMetersWide(double desiredMeters, double latitude) {
    final double latitudinalAdjustment = Math.cos(Math.PI * latitude / 180);
    final double arg = EQUATOR_LENGTH * getScreenWidth() * latitudinalAdjustment / (desiredMeters * 256);
    return Math.log(arg) / Math.log(2);
}

  • Update customer marker position
?
 
1
mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(CUSTOMER_LOCATION, (float) ZOOM_LEVEL));
 
 

mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(CUSTOMER_LOCATION, (float) ZOOM_LEVEL));

  • Update driver marker with animation
?
 
1
2
LatLngInterpolator latLngInterpolator = new LatLngInterpolator.Spherical();
MarkerAnimation.animateMarkerToGB(driverMarker, currentLatLong, latLngInterpolator);
 
 

LatLngInterpolator latLngInterpolator = new LatLngInterpolator.Spherical();
MarkerAnimation.animateMarkerToGB(driverMarker, currentLatLong, latLngInterpolator);

  • Set Zoom level & Update Map view :
?
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(llTO, mGoogleMap.getCameraPosition().zoom));
mGoogleMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
    @Override
    public void onCameraChange(CameraPosition position) {
        if (!isMapChangeRequired) {
            isMapChangeRequired = true;
            return;
        }
        double meterDistance = objUtils.getDistanceInMeter(llSprenter.latitude, llSprenter.longitude, llTO.latitude, llTO.longitude);
        float Zoomlevel = (float)((float) objUtils.getZoomForMetersWide(meterDistance, llTO.latitude)– 0.3);
        if (Zoomlevel != ZOOM_LEVEL) {
            ZOOM_LEVEL = Zoomlevel;
            mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(llTO, (float) ZOOM_LEVEL));
        } else {
            mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(llTO, mGoogleMap.getCameraPosition().zoom));
        }
        isMapChangeRequired = false;
    }
});
 
 

mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(llTO, mGoogleMap.getCameraPosition().zoom));
mGoogleMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
    @Override
    public void onCameraChange(CameraPosition position) {
        if (!isMapChangeRequired) {
            isMapChangeRequired = true;
            return;
        }
        double meterDistance = objUtils.getDistanceInMeter(llSprenter.latitude, llSprenter.longitude, llTO.latitude, llTO.longitude);
        float Zoomlevel = (float)((float) objUtils.getZoomForMetersWide(meterDistance, llTO.latitude)– 0.3);
        if (Zoomlevel != ZOOM_LEVEL) {
            ZOOM_LEVEL = Zoomlevel;
            mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(llTO, (float) ZOOM_LEVEL));
        } else {
            mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(llTO, mGoogleMap.getCameraPosition().zoom));
        }
        isMapChangeRequired = false;
    }
});

  • Handle custom ScaleGesture & TouchListner over Google Map view
?
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
@Override
public boolean onTouch(View view, MotionEvent event) {
    this.view = view;
    gestureScale.onTouchEvent(event);
    return true;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
    double zoom = googleMap.getCameraPosition().zoom;
    zoom = zoom + Math.log(detector.getScaleFactor()) / Math.log(1.5 d);
    CameraUpdate update = CameraUpdateFactory.newLatLngZoom(googleMap.getCameraPosition().target, (float) zoom);
    googleMap.moveCamera(update);
    return true;
}
 
 

@Override
public boolean onTouch(View view, MotionEvent event) {
    this.view = view;
    gestureScale.onTouchEvent(event);
    return true;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
    double zoom = googleMap.getCameraPosition().zoom;
    zoom = zoom + Math.log(detector.getScaleFactor()) / Math.log(1.5 d);
    CameraUpdate update = CameraUpdateFactory.newLatLngZoom(googleMap.getCameraPosition().target, (float) zoom);
    googleMap.moveCamera(update);
    return true;
}

Full MyScaleGestures.java
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class MyScaleGestures implements View.OnTouchListener, ScaleGestureDetector.OnScaleGestureListener {
    private ScaleGestureDetector gestureScale;
    private GoogleMap googleMap;
    public MyScaleGestures(Context c, GoogleMap googleMap) {
        gestureScale = new ScaleGestureDetector(c, this);
        this.googleMap = googleMap;
    }
    @Override
    public boolean onTouch(View view, MotionEvent event) {
        gestureScale.onTouchEvent(event);
        return true;
    }
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        double zoom = googleMap.getCameraPosition().zoom;
        zoom = zoom + Math.log(detector.getScaleFactor()) / Math.log(1.5 d);
        CameraUpdate update = CameraUpdateFactory.newLatLngZoom(googleMap.getCameraPosition().target, (float) zoom);
        googleMap.moveCamera(update);
        return true;
    }
    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        return true;
    }
    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {}
}
public class MyScaleGestures implements View.OnTouchListener, ScaleGestureDetector.OnScaleGestureListener {
    private ScaleGestureDetector gestureScale;
    private GoogleMap googleMap;
    public MyScaleGestures(Context c, GoogleMap googleMap) {
        gestureScale = new ScaleGestureDetector(c, this);
        this.googleMap = googleMap;
    }
    @Override
    public boolean onTouch(View view, MotionEvent event) {
        gestureScale.onTouchEvent(event);
        return true;
    }
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        double zoom = googleMap.getCameraPosition().zoom;
        zoom = zoom + Math.log(detector.getScaleFactor()) / Math.log(1.5 d);
        CameraUpdate update = CameraUpdateFactory.newLatLngZoom(googleMap.getCameraPosition().target, (float) zoom);
        googleMap.moveCamera(update);
        return true;
    }
    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        return true;
    }
    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {}
}

?
1
2
3
4
<RelativeLayout android:id=”@+id/flMap” android:layout_width=”match_parent” android:layout_height=”match_parent” android:clickable=”true” android:focusable=”true” android:focusableInTouchMode=”true”>
    <fragment android:id=”@+id/layMapContainer” android:name=”com.google.android.gms.maps.SupportMapFragment” android:layout_width=”match_parent” android:layout_height=”match_parent” tools:context=”com.googlemapdemo.MapsActivity” />
    <ImageView android:id=”@+id/imgMapTransparent” android:layout_width=”match_parent” android:layout_height=”match_parent” android:src=”@android:color/transparent” />
</RelativeLayout>

<RelativeLayout android:id=”@+id/flMap” android:layout_width=”match_parent” android:layout_height=”match_parent” android:clickable=”true” android:focusable=”true” android:focusableInTouchMode=”true”>
    <fragment android:id=”@+id/layMapContainer” android:name=”com.google.android.gms.maps.SupportMapFragment” android:layout_width=”match_parent” android:layout_height=”match_parent” tools:context=”com.googlemapdemo.MapsActivity” />
    <ImageView android:id=”@+id/imgMapTransparent” android:layout_width=”match_parent” android:layout_height=”match_parent” android:src=”@android:color/transparent” />
</RelativeLayout>


  • Now while driver change is location marker will move with animation and map will zoom out as per location change. Hope this will help you a lot. You can download sample code.