Development issue/problem:

I have a user interface where a MapFragment is placed on a transparent view. The map covers the entire screen, while the display only covers the left part of the screen. This is why the centre of the map is switched off by default. When someone clicks on a marker, I want to center that marker in the fully visible area of the MapFragment (not in the middle of the MapFragment itself).

Since it is difficult to describe it in words, I would like to use a few pictures. Let’s just say this is what my user interface looks like:

When the user clicks on the marker, I want to both center it and zoom in to see it better. Without adjustment, that’s what you get:

I want the marker centered, but in the area to the right of it, like this:

This is very easy to do if you do not change the zoom level with the map projection:

//
Target LatLng = new LatLng (latitude, longitude);
Projection = getMap().getProjection();
Point ScreenLocation = projection.toScreenLocation(target);
ScreenLocation.x += offsetX;
ScreenLocation.y += offsetY;
LatLng offsetTarget = projection.fromScreenLocation(screenLocation) ;

// Animate the calculated lat/lng
getMap().animateCamera(CameraUpdateFactory.newLatLng(offsetTarget)) ;

However, if you change scale levels at the same time, the above calculations will not work (because the lat/lng compensations for the different scale levels change).

Let me go through the list of attempted corrections:

Is it possible to calculate the offsetTarget even if the zoom level is changed?

How can I solve this problem?

Solution 1:

The latest version of the Play Services library adds the setPadding() function to GoogleMap. This block projects all camera movements to the center of the movement map. This document describes the reload behaviour of the card in more detail.

The question originally asked here can now easily be solved by adding a left padding corresponding to the width of the left layout.

mMap.setPadding(leftPx, topPx, rightPx, bottomPx) ;

Solution 2:

I’ve found a solution that really fits the problem.
The solution is to calculate the displacement centre on the desired scale from the initial displacement and then animate the map camera.
To do this, first move the map camera to the desired zoom level, calculate the offset for this zoom level, and then restore the original zoom level. After calculating the new center you can use CameraUpdateFactory.newLatLngZoom to create an animation.

HIH

private void animateLatLngZoom(LatLng latlng, int reqZoom, int offsetX, int offsetY) {

// Save the current scale
float original Zoom = mMap.getCameraPosition().zoom ;

// temporarily move the zoomed-in camera
mMap.moveCamera (CameraUpdateFactory.zoomTo(reqZoom));

PointInScreen = mMap.getProjection().toScreenLocation(latlng) ;

Point newPoint = newPoint();
newPoint.x = pointInScreen.x + offsetX ;
newPoint.y = pointInScreen.y + offsetY ;

LatLng newCenterLatLng = mMap.getProjection().fromScreenLocation(newPoint) ;

// Restore the original scale
mMap.moveCamera (CameraUpdateFactory.zoomTo(OriginalZoom)) ;

// Animate the camera with the new delay center and the desired zoom.
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(newCenterLatLng, reqZoom)) ;

}

Solution 3:

Edit: You can find the code of the old v1 cards below. This OS response describes how similar actions in version 2 can be implemented: How to get the latitude/length in Google Map V2 for Android?

The desired key methods should work in length/length and percentages, not in pixels. Currently, the map view is scaled in the center of the map and then moved to a newer pin in the exposed area. You can get the width in degrees after scaling, take a certain screen deviation into account, and then update the map. Here is an example screenshot of the following code in action after pressing + : for
, fill here the description of the image in
after , fill here the description of the image in
Basic codes :

@Override
public void onZoom(logical zoomIn) {
// TODO Automatically generated stub method
controller.setZoom(zoomIn? map.getZoomLevel()+1: map.getZoomLevel()-1);
int bias = (int) (map.getLatitudeSpan()* 1.0/3.0); // Part of your choice
map.getLongitudeSpan();
controller.animateTo(new GeoPoint(yourLocation.getLatitudeE6(),yourLocation.getLongitudeE6()-bias)))

}

All codes:

Package com.example.testmapview ;

importing com.google.android.maps.GeoPoint;
importing com.google.android.maps.ItemizedOverlay;
importing com.google.android.maps.MapActivity;
importing com.google.android.maps.MapController;
importing com.google.android.maps.MapView ;

import android.os.bundle ; import
android.app.Activity ; import
android.graphics.drawable ; import
android.view.Menu ; import
android.widget.ZoomButtonsController ; import
android.widget.ZoomButtonsController.OnZoomListener ;

The MainActivity public class extends MapActivity {
MapView folder;
GeoPoint yourLocation;
MapController controller;
ZoomButtonController zoomButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
map=(MapView)findViewById(R.id.mapview);
map.setBuiltInZoomControls(true);
controller = map.getController();
zoomButtons = map.getZoomButtonsController() ;

zoomButtons.setOnZoomListener(new OnZoomListener() {

@Override
public void onVisibilityChanged(boolean arg0) {
// TODO Automatically generated method heel

}

@Override
public void onZoom(logical zoomIn) {
// TODO Automatically generated stub method
controller.setZoom(zoomIn? map.getZoomLevel()+1: map.getZoomLevel()-1);
int bias = (int) (map.getLatitudeSpan()* 1.0/3.0); // Part of your choice
map.getLongitudeSpan();
controller.animateTo(new GeoPoint(yourLocation.getLatitudeE6(),yourLocation.getLongitudeE6()-bias)))

}
});

// draw a pin, center
DrawableMarker=getResources().getDrawable(android.R.drawable.star_big_on);
myItemizedOverlay myItemizedOverlay = new MyItemizedOverlay(marker);
folder.getOverlays().add(myItemizedOverlay);
yourLocation = new GeoPoint(0, 0);
controller.setCenter(yourLocation);
myItemizedOverlay.addItem(yourLocation, myPoint1, myPoint1);
controller.setZoom(5) ;

}

@General Boolean menu
onCreateOptionsMenu(Menu) {
// Inflate the menu; this adds items to the action bar, if any.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}

@Override
protected boolean isRouteDisplayed() {
// TODO auto-generated stub method
gives false;
}

}

and the basic layout:

If that doesn’t fully answer your question, please let me know, because it was a nice experiment to carry out.

Solution 4:

The API projections are not very useful, I encountered the same problem and developed my own.

public class SphericalMercatorProjection {

public static PointD latLngToWorldXY(latLng latLng, double zoomLevel) {
last double worldWidthPixels = toWorldWidthPixels(zoomLevel);
last double x = latLng.longitude / 360 + .5;
last double siny = Math.sin(Math.toRadians(latLng.latitude)));
last double y = 0.5 * Math.log((1 + siny)/ (1 – siny)) / -(2 * Math.PI) + .5;
returns the new PointD(x * worldWidthPixels, y * worldWidthPixels);
}

public static LatLng worldXYToLatLng(PointD point, double zoom level) {
last double worldWidthPixels = toWorldWidthPixels(zoomLevel) ;
last double x = point.x / worldWidthPixels – 0.5 ;
last double lng = x * 360 ;

double y = .5 – (dot.y / worldWidthPixels) ;
last double lat = 90 – Math.toDegrees(Math.atan(Math.exp(-y * 2 * Math.PI)) * 2) ;

return the new LatLng(lat, lng);
}

private static double toWorldWidthPixels (double zoomLevel) {
return 256 * Math.pow(2, zoomLevel);
}
}

This code allows you to transform the card around the new target (i.e. not in the middle). I decided to use an anchor point system where (0.5, 0.5) is the default value and represents the center of the screen. (0.0) – top left and (1, 1) – bottom left.

private LatLng getOffsetLocation(location LatLng, double zoom) {
PointSize = getSize();
PointD anchorOffset = new PointD(size.x * (0.5 – anchor.x), size.y * (0.5 – anchor.y));
PointD screenCenterWorldXY = SphericalMercatorProjection.latLngToWorldXY(location, scale);
PointD newScreenCenterWorldXY = new PointD(screenCenterWorldXY.x + anchorOffset.x, screenCenterWorldXY.y + anchorOffset.y);
newScreenCenterWorldXY.rotate(screenCenterWorldXY, cameraPosition.bearing);
return SphericalMercatorProjection.worldXYToLatLng(newScreenCenterWorldXY, zoom);
}

Basically you use the projection to get the XY coordinate of the world, then you move this point and convert it back to LatLng. You can then transfer it to your card. PointD is a single type that contains x,y as a double and also rotates.

public class PointD {

double public x ;
double public y ;

public PointD(double x, double y) {
this.x = x ;
this.y = y ;
}

public void rotate (PointD origin, float angleDeg) {
double rotationRadians = Math.toRadians(angleDeg);
this.x -= origin.x;
this.y -= origin.y;
double rotationX = this.x * Math.cos(rotationRadians) – this.y * Math.sin(rotationRadians);
double rotationY = this.x * Math.sin(rotationRadians) + this.y * Math.cos(rotationRadians);
this.x = rotationX;
this.y = rotationY;
this.x += origin.x;
this.y += origin.y;
}
}

Note that if you update the bearing and map position at the same time, it is important that you use the new bearing in getOffsetLocation.

Solution No 5:

The easiest way is to animate with a chain camera. First you scale image n°2 normally with animateCamera with 3 parameters, and in CancellableCallback.on Finish your projection calculations and animate with your code to change the center.

I think it would be very good (untested) if you set good animation duration values for both animations. Maybe the second animation should be much faster than the first. Even if the transparent overlay view is not visible when no marker is selected, it would be a good idea to animate it to the left during the second animation.

The most difficult part will be to implement a custom spherical Mercator projection, similar to the projection used by Maps API v2. If all you need is a longitude shift, it shouldn’t be that difficult. If your users can change the bearing and you don’t want to reset it to 0 while scaling, you will also need a latitude correction, which will probably be a little more difficult to implement.

I also think it would be nice to have an API Maps v2 function to get a projection for each camera position, not just the current position. You can submit a job request here: http://code.google.com/p/gmaps-api-issues/issues/list?can=2&q=apitype=Android2. You can just use it in your code to get the desired effect.

Solution No 6:

To repair the marker, we use overlay in Android. In the overlay class==> in the figure of method
we place this thing

Border centre (marker) ;

and the manufacturer mentions it, too:

public xxxxxxOverlay(Drawable marker, ImageView dragImage, MapView map) {
super(boundCenter(marker));
.
.
.
.
.
}

Solution No 7:

I have almost exactly the same problem as you (only my offset is vertical), and I tried a solution that didn’t work as expected, but could help you (us) find a better solution.

I moved the camera on the map to get a projection on the target, and applied the offset to that location.

I put the code on the Utilities class and it looks like this:

public static CameraBuilder moveUsingProjection(GoogleMap folder, CameraBuilder target, int verticalMapOffset) {CameraPosition oldPosition = map.getCameraPosition();map.moveCamera(target.build(map));CameraBuilder result = CameraBuilder.builder().target(moveUsingProjection(map.getProjection(), map.getCameraPosition().target, verticalMapOffset).zoom(map.getCameraPosition().zoom).tilt(map.getCameraPosition().tilt);map.moveCamera(CameraUpdateFactory.newCameraPosition(oldPosition));};return result;.

public static LatLng movingUsingProjection (Projection projection, LatLng latLng, int. verticalMapOffset) {
point screenPoint = projection.toScreenLocation(latLng);
screenPoint.y -= verticalMapOffset;
return projection.fromScreenLocation(screenPoint);
}

You can then use the CameraBuilder returned via these methods for the animation instead of the original (not shifted) purpose.

It works (more or less), but it has two major problems:

  • Sometimes the card wobbles (this is the biggest problem because it makes the hack unusable).
  • This is a hack that may or may not work today or tomorrow (I know that the same algorithm works on iOS, for example).

So I didn’t want to do it, but it got me thinking about the alternative: If you have an identical but hidden map, you can use this map for intermediate calculations and then apply the final result to the visible map.

I think that option would work, but it’s terrible because you would have to have two of the same card, with the corresponding overhead, for you and for the device.

Like I said, I don’t have a definitive solution, but maybe this will help a little.

HIH

Solution No 8:

I solved the problem by calculating a distance vector using the LatLng values of two fixed points on the map, and then creating another LatLng using the position of the marker, this vector, and a predefined factor value. There are many objects involved in this project, but it doesn’t depend on the current zoom level and map rotation and could work well for you!

// Just a help function to get the display size of the unit in px
Point DisplaySize = UIUtil.getDisplaySize(getActivity().getWindowManager().getDefaultDisplay());

// Two fixed points : Bottom of the card and top of the card,
// both horizontally centered (because I want to move the camera vertically …
// If you want to move it to the right, for example, use the left and right ends)
Upper point = new point (displaySize.x / 2, 0) ;
Lower point = new point (displaySize.x / 2, displaySize.y) ;

// Convert to LatLng using the GoogleMap object
LatLng latlngUp = googleMap.getProjection().fromScreenLocation(up) ;
LatLng latlngDown = googleMap.getProjection().fromScreenLocation(down) ;

// Create a vector between the objects latLng screen point
double[] vector = new double[] {latlngUp.latitude – latlngDown.latitude,
latlngUp.longitude – latlngDown.longitude} ;

//
LatLng latlngMarker = marker.getPosition();
double offsetRatio = 1.0/2.5;
LatLng latlngOffset = new LatLng(latlngMarker.latitude + (offsetRatio * vector [0]),
latlngMarker.longitude + (offsetRatio * vector [1]) ;

// Move the camera to the desired position
CameraUpdate CameraUpdate = CameraUpdateFactory.newLatLng(latlngOffset) ;
googleMap.animateCamera(cameraUpdate) ;

Solution No 9:

I had the same problem and I ended up with the same solution as your last option:

Change the x/y position of the screen by the difference in the square zoom level. In theory this should work, but it is still an order of magnitude off (~0.1 latitude/length, which is enough to be far away).

But instead of the cleavage point 2<

Edited by

In the end I was able to solve the problem in a different way (in my opinion the most suitable option). Folder API v2 has fill parameters. Simply configure it to accept all non-hidden parts of MapView. The camera then reflects the center of the soft zone after aimateCamera().

Good luck!

You May Also Like

Shop Disney For PC (Windows 10, 8, 7)

The Disney store is the perfect resource for all fans of Disney…

My Little Princess : Castle Playhouse pretend play for PC – Windows 7, 8, 10 – Free Download

My Little Princess: Castle Free is a fun game for children, similar…

Is Online Gambling More Addictive And Dangerous Than Casino Gambling

Fun is something everyone needs. The main purpose of the party is…

INTRODUCTION TO MATLAB | Matlab Tutorial | Programming in Matlab

INTRODUCTION TO MATLAB – The full Matlab tutorial can be found here.…